-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial implementation #2
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (3)
- .github/workflows/ExecuteQuery_build_and_test_on_push.yml (1 hunks)
- .gitignore (1 hunks)
- Frends.IBMDB2.ExecuteQuery/_build/setup.sh (1 hunks)
Files skipped from review due to trivial changes (1)
- .gitignore
Files skipped from review as they are similar to previous changes (2)
- .github/workflows/ExecuteQuery_build_and_test_on_push.yml
- Frends.IBMDB2.ExecuteQuery/_build/setup.sh
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (1)
- Frends.IBMDB2.ExecuteQuery/_build/setup.sh (1 hunks)
Files skipped from review as they are similar to previous changes (1)
- Frends.IBMDB2.ExecuteQuery/_build/setup.sh
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Outside diff range and nitpick comments (1)
.github/workflows/localflow.yml (1)
65-84
: Optimize checkout action usage by ensuringfetch-depth: 0
is necessary for all jobs.Consider whether a full fetch (fetch-depth: 0) is necessary, as this can slow down the checkout process. If a shallow clone suffices, adjust accordingly.
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (5)
- .github/workflows/ExecuteQuery_build_and_test_on_push.yml (1 hunks)
- .github/workflows/localflow.yml (1 hunks)
- .gitignore (1 hunks)
- Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery.Tests/Frends.IBMDB2.ExecuteQuery.Tests.csproj (1 hunks)
- Frends.IBMDB2.ExecuteQuery/_build/setup.sh (1 hunks)
Files skipped from review due to trivial changes (1)
- Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery.Tests/Frends.IBMDB2.ExecuteQuery.Tests.csproj
Files skipped from review as they are similar to previous changes (3)
- .github/workflows/ExecuteQuery_build_and_test_on_push.yml
- .gitignore
- Frends.IBMDB2.ExecuteQuery/_build/setup.sh
Additional comments not posted (5)
.github/workflows/localflow.yml (5)
36-60
: Review the necessity and security of all secrets.Verification successful
All secrets listed in the
.github/workflows/localflow.yml
file are being used in the workflow and are necessary. Additionally, they are being masked using the::add-mask::
command, ensuring they are handled securely.
.github/workflows/localflow.yml
: All secrets are necessary and used securely.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that all secrets are necessary and used securely in the workflow. # Test: Search for secret variable usage in the workflow. rg --type yaml $'secrets' .github/workflows/localflow.ymlLength of output: 1760
124-184
: Ensure environment variables are set securely and used appropriately.Verification successful
Environment variables are set securely and used appropriately in the workflow.
- Secrets are masked using
::add-mask::
.- Environment variables are appended to
$GITHUB_ENV
securely.- Working directory is set based on the provided or default value.
Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that all environment variables are set securely and used appropriately in the workflow. # Test: Search for environment variable setup and usage in the workflow. rg --type yaml $'env' .github/workflows/localflow.ymlLength of output: 3026
3-35
: Ensure all inputs are necessary and securely handled.Verification successful
All input variables in the
.github/workflows/localflow.yml
file are utilized appropriately and securely handled.
env_var_name_1
toenv_var_name_8
are used to set corresponding secret values in the environment.workdir
andprebuild_command
are conditionally checked and used as needed.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that all input variables are used in the workflow and secured properly. # Test: Search for input variable usage in the workflow. rg --type yaml $'inputs' .github/workflows/localflow.ymlLength of output: 1427
252-264
: Validate the final steps of the workflow to ensure they align with the project's deployment and testing strategies.Verification successful
The final steps of the workflow include setting up dotnet with a test feed and pushing the NuGet package to the test feed. These steps appear to align with a deployment and testing strategy that involves validating packages in a test environment before potentially deploying them to a production environment.
.github/workflows/localflow.yml
: The final steps involve setting up dotnet with a test feed and pushing the NuGet package to the test feed.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Verify that the final steps of the workflow align with the project's deployment and testing strategies. # Test: Search for final steps in the workflow. rg --type yaml $'name: Push NuGet package(s) to the test feed' .github/workflows/localflow.ymlLength of output: 95
Script:
#!/bin/bash # Description: Gather the final steps of the workflow to understand its alignment with the project's deployment and testing strategies. # Test: Fetch the last 20 lines of the workflow file to analyze the final steps. tail -n 20 .github/workflows/localflow.ymlLength of output: 1085
185-251
: Review the installation and usage of external tools to ensure security and efficiency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (4)
- Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery.Tests/TestsBase.cs (1 hunks)
- Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery.Tests/UnitTests.cs (1 hunks)
- Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery/Definitions/DataTypes.cs (1 hunks)
- Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery/IBMDB2.cs (1 hunks)
Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery/Definitions/DataTypes.cs
Outdated
Show resolved
Hide resolved
Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery.Tests/TestsBase.cs
Outdated
Show resolved
Hide resolved
result = new Result | ||
{ | ||
Success = true, | ||
RecordsAffected = (int)dataObject, | ||
Data = JToken.FromObject(new { AffectedRows = dataObject }), | ||
}; | ||
break; | ||
case ExecuteType.ExecuteReader: | ||
dataReader = command.ExecuteReader(); | ||
table.Load(dataReader); | ||
result = new Result | ||
{ | ||
Success = true, | ||
RecordsAffected = dataReader.RecordsAffected, | ||
Data = JToken.FromObject(table), | ||
}; | ||
dataReader.Close(); | ||
break; | ||
case ExecuteType.NonQuery: | ||
dataObject = command.ExecuteNonQuery(); | ||
result = new Result | ||
{ | ||
Success = true, | ||
RecordsAffected = (int)dataObject, | ||
Data = JToken.FromObject(new { AffectedRows = dataObject }), | ||
}; | ||
break; | ||
case ExecuteType.Scalar: | ||
dataObject = command.ExecuteScalar(); | ||
result = new Result | ||
{ | ||
Success = true, | ||
RecordsAffected = (int)dataObject, | ||
Data = JToken.FromObject(new { AffectedRows = dataObject }), | ||
}; | ||
break; | ||
default: | ||
throw new NotSupportedException(); | ||
} | ||
|
||
if (command.Transaction != null) | ||
await command.Transaction.CommitAsync(cancellationToken); | ||
|
||
return result; | ||
} | ||
catch (Exception ex) | ||
{ | ||
if (dataReader != null && !dataReader.IsClosed) | ||
await dataReader.CloseAsync(); | ||
|
||
if (command.Transaction is null) | ||
{ | ||
return HandleError( | ||
ex, | ||
"ExecuteHandler exception: 'Options.TransactionIsolationLevel = None', so there was no transaction rollback.", | ||
options.ThrowErrorOnFailure); | ||
} | ||
else | ||
{ | ||
try | ||
{ | ||
await command.Transaction.RollbackAsync(cancellationToken); | ||
} | ||
catch (Exception rollbackEx) | ||
{ | ||
return HandleError( | ||
rollbackEx, | ||
$"ExecuteHandler exception: An exception occurred on transaction rollback. Exception leading to rollback: {ex}.", | ||
options.ThrowErrorOnFailure); | ||
} | ||
|
||
return HandleError( | ||
ex, | ||
"ExecuteHandler exception: (If required) transaction rollback completed without exception.", | ||
options.ThrowErrorOnFailure); | ||
} | ||
} | ||
} | ||
|
||
private static IsolationLevel GetIsolationLevel(Options options) | ||
{ | ||
return options.SqlTransactionIsolationLevel switch | ||
{ | ||
TransactionIsolationLevel.Unspecified => IsolationLevel.Unspecified, | ||
TransactionIsolationLevel.ReadUncommitted => IsolationLevel.ReadUncommitted, | ||
TransactionIsolationLevel.ReadCommitted => IsolationLevel.ReadCommitted, | ||
TransactionIsolationLevel.RepeatableRead => IsolationLevel.RepeatableRead, | ||
TransactionIsolationLevel.Serializable => IsolationLevel.Serializable, | ||
TransactionIsolationLevel.Snapshot => IsolationLevel.Snapshot, | ||
_ => IsolationLevel.ReadCommitted, | ||
}; | ||
} | ||
|
||
private static Result HandleError(Exception ex, string message, bool throwErrorOnFailure) | ||
{ | ||
if (throwErrorOnFailure) | ||
throw new Exception(message, ex); | ||
else | ||
return new Result { Success = false, ErrorMessage = $"{message} {ex}." }; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Robust Query Execution and Error Handling
The IBMDB2
class provides comprehensive methods for executing different types of database operations. The use of async/await pattern is appropriate for database operations. The method ExecuteQuery
handles different execution types and manages transactions effectively.
However, there is a potential improvement in error handling:
- The method
HandleError
could provide more specific error messages based on the exception type rather than a generic message. This would aid in debugging and maintenance.
- return new Result { Success = false, ErrorMessage = $"{message} {ex}." };
+ return new Result { Success = false, ErrorMessage = $"{message} Specific error: {ex.Message}" };
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
public static async Task<Result> ExecuteQuery( | |
[PropertyTab] Input input, | |
[PropertyTab] Options options, | |
CancellationToken cancellationToken) | |
{ | |
Result result; | |
using var connection = new DB2Connection(input.ConnectionString); | |
connection.Open(); | |
var command = connection.CreateCommand(); | |
command.CommandText = input.Query; | |
if (input.Parameters != null) | |
{ | |
foreach (var parameter in input.Parameters) | |
{ | |
if (parameter.DataType is DataTypes.Auto) | |
{ | |
command.Parameters.Add( | |
parameterName: parameter.Name, | |
value: parameter.Value); | |
} | |
else | |
{ | |
var dbType = (DB2Type)Enum.Parse(typeof(DB2Type), parameter.DataType.ToString()); | |
var commandParameter = command.Parameters.Add(parameter.Name, dbType); | |
commandParameter.Value = parameter.Value; | |
} | |
} | |
} | |
if (options.SqlTransactionIsolationLevel is TransactionIsolationLevel.None) | |
result = await ExecuteHandler(input, options, command, cancellationToken); | |
else | |
{ | |
using var transaction = connection.BeginTransaction(GetIsolationLevel(options)); | |
command.Transaction = transaction; | |
result = await ExecuteHandler(input, options, command, cancellationToken); | |
} | |
return result; | |
} | |
private static async Task<Result> ExecuteHandler( | |
Input input, | |
Options options, | |
DB2Command command, | |
CancellationToken cancellationToken) | |
{ | |
Result result; | |
object dataObject; | |
dynamic dataReader = null; | |
var table = new DataTable(); | |
try | |
{ | |
switch (input.ExecuteType) | |
{ | |
case ExecuteType.Auto: | |
if (input.Query.ToLower().StartsWith("select")) | |
{ | |
dataReader = command.ExecuteReader(); | |
table.Load(dataReader); | |
result = new Result | |
{ | |
Success = true, | |
RecordsAffected = dataReader.RecordsAffected, | |
Data = JToken.FromObject(table), | |
}; | |
dataReader.Close(); | |
break; | |
} | |
dataObject = command.ExecuteNonQuery(); | |
result = new Result | |
{ | |
Success = true, | |
RecordsAffected = (int)dataObject, | |
Data = JToken.FromObject(new { AffectedRows = dataObject }), | |
}; | |
break; | |
case ExecuteType.ExecuteReader: | |
dataReader = command.ExecuteReader(); | |
table.Load(dataReader); | |
result = new Result | |
{ | |
Success = true, | |
RecordsAffected = dataReader.RecordsAffected, | |
Data = JToken.FromObject(table), | |
}; | |
dataReader.Close(); | |
break; | |
case ExecuteType.NonQuery: | |
dataObject = command.ExecuteNonQuery(); | |
result = new Result | |
{ | |
Success = true, | |
RecordsAffected = (int)dataObject, | |
Data = JToken.FromObject(new { AffectedRows = dataObject }), | |
}; | |
break; | |
case ExecuteType.Scalar: | |
dataObject = command.ExecuteScalar(); | |
result = new Result | |
{ | |
Success = true, | |
RecordsAffected = (int)dataObject, | |
Data = JToken.FromObject(new { AffectedRows = dataObject }), | |
}; | |
break; | |
default: | |
throw new NotSupportedException(); | |
} | |
if (command.Transaction != null) | |
await command.Transaction.CommitAsync(cancellationToken); | |
return result; | |
} | |
catch (Exception ex) | |
{ | |
if (dataReader != null && !dataReader.IsClosed) | |
await dataReader.CloseAsync(); | |
if (command.Transaction is null) | |
{ | |
return HandleError( | |
ex, | |
"ExecuteHandler exception: 'Options.TransactionIsolationLevel = None', so there was no transaction rollback.", | |
options.ThrowErrorOnFailure); | |
} | |
else | |
{ | |
try | |
{ | |
await command.Transaction.RollbackAsync(cancellationToken); | |
} | |
catch (Exception rollbackEx) | |
{ | |
return HandleError( | |
rollbackEx, | |
$"ExecuteHandler exception: An exception occurred on transaction rollback. Exception leading to rollback: {ex}.", | |
options.ThrowErrorOnFailure); | |
} | |
return HandleError( | |
ex, | |
"ExecuteHandler exception: (If required) transaction rollback completed without exception.", | |
options.ThrowErrorOnFailure); | |
} | |
} | |
} | |
private static IsolationLevel GetIsolationLevel(Options options) | |
{ | |
return options.SqlTransactionIsolationLevel switch | |
{ | |
TransactionIsolationLevel.Unspecified => IsolationLevel.Unspecified, | |
TransactionIsolationLevel.ReadUncommitted => IsolationLevel.ReadUncommitted, | |
TransactionIsolationLevel.ReadCommitted => IsolationLevel.ReadCommitted, | |
TransactionIsolationLevel.RepeatableRead => IsolationLevel.RepeatableRead, | |
TransactionIsolationLevel.Serializable => IsolationLevel.Serializable, | |
TransactionIsolationLevel.Snapshot => IsolationLevel.Snapshot, | |
_ => IsolationLevel.ReadCommitted, | |
}; | |
} | |
private static Result HandleError(Exception ex, string message, bool throwErrorOnFailure) | |
{ | |
if (throwErrorOnFailure) | |
throw new Exception(message, ex); | |
else | |
return new Result { Success = false, ErrorMessage = $"{message} {ex}." }; | |
} | |
private static Result HandleError(Exception ex, string message, bool throwErrorOnFailure) | |
{ | |
if (throwErrorOnFailure) | |
throw new Exception(message, ex); | |
else | |
return new Result { Success = false, ErrorMessage = $"{message} Specific error: {ex.Message}" }; | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually valid and good suggestion, but CodeRabbit suggests to delete too much :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jefim, thank you for your feedback. I understand the concern about the suggestion removing too much code. Here's a refined version that only updates the error message formatting without removing any other parts of the method:
private static Result HandleError(Exception ex, string message, bool throwErrorOnFailure)
{
if (throwErrorOnFailure)
throw new Exception(message, ex);
else
return new Result { Success = false, ErrorMessage = $"{message} Specific error: {ex.Message}" };
}
This change focuses solely on enhancing the error message specificity, which should aid in debugging and maintenance.
Would you like to proceed with this refined suggestion?
Learnings added
Learnt from: jefim
PR: FrendsPlatform/Frends.IBMDB2#2
File: Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery/IBMDB2.cs:25-198
Timestamp: 2024-07-09T05:54:59.520Z
Learning: When suggesting improvements to error handling in the `HandleError` method, ensure that only the error message formatting is updated without removing other parts of the method.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery.Tests/UnitTests.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (4)
- .github/workflows/ExecuteQuery_build_and_test_on_push.yml (1 hunks)
- .github/workflows/localflow.yml (1 hunks)
- Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery.Tests/TestsBase.cs (1 hunks)
- Frends.IBMDB2.ExecuteQuery/_build/setup.sh (1 hunks)
Files skipped from review due to trivial changes (2)
- .github/workflows/ExecuteQuery_build_and_test_on_push.yml
- Frends.IBMDB2.ExecuteQuery/_build/setup.sh
Files skipped from review as they are similar to previous changes (1)
- Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery.Tests/TestsBase.cs
Additional comments not posted (1)
.github/workflows/localflow.yml (1)
3-60
: Verify the configuration and usage of workflow inputs and secrets.Ensure that all required inputs and secrets are appropriately defined and used throughout the workflow. This helps in maintaining security and configurability.
Verification successful
Verify the configuration and usage of workflow inputs and secrets.
Ensure that all defined inputs and secrets in the workflow configuration are appropriately used in the workflow steps. This helps in maintaining security and configurability.
- Inputs
env_var_name_1
toenv_var_name_8
,workdir
, andprebuild_command
are used in the workflow.- Secrets
env_var_value_1
toenv_var_value_8
,badge_service_api_key
,test_feed_api_key
,build_time_nuget_feed_url
, andbuild_time_nuget_feed_api_key
are used in the workflow.Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Description: Check for the usage of workflow inputs and secrets. # Test: Search for the usage of inputs and secrets in the workflow file. rg --type yaml 'inputs|secrets' .github/workflows/localflow.ymlLength of output: 2421
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- .github/workflows/ExecuteQuery_build_and_test_on_push.yml (1 hunks)
- Frends.IBMDB2.ExecuteQuery/_build/setup.sh (1 hunks)
Files skipped from review as they are similar to previous changes (2)
- .github/workflows/ExecuteQuery_build_and_test_on_push.yml
- Frends.IBMDB2.ExecuteQuery/_build/setup.sh
…ons/DataTypes.cs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Outside diff range and nitpick comments (4)
.github/workflows/ExecuteQuery_build_and_test_on_main.yml (2)
1-1
: Specify the Workflow Name ClearlyThe workflow name
ExecuteQuery_build_main
is clear but could be more descriptive, such asBuild and Test on Main Branch
.- name: ExecuteQuery_build_main + name: Build and Test on Main Branch
17-17
: Clarify the Purpose of Environment VariableThe environment variable
LD_LIBRARY_PATH
is set but not clearly documented. Consider adding a comment to explain why this variable is needed.- env_var_name_1: LD_LIBRARY_PATH + env_var_name_1: LD_LIBRARY_PATH # Path to DB2 libraries.github/workflows/ExecuteQuery_build_and_test_on_push.yml (2)
1-1
: Specify the Workflow Name ClearlyThe workflow name
ExecuteQuery_build_test
is clear but could be more descriptive, such asBuild and Test on Push Events
.- name: ExecuteQuery_build_test + name: Build and Test on Push Events
17-17
: Clarify the Purpose of Environment VariableThe environment variable
LD_LIBRARY_PATH
is set but not clearly documented. Consider adding a comment to explain why this variable is needed.- env_var_name_1: LD_LIBRARY_PATH + env_var_name_1: LD_LIBRARY_PATH # Path to DB2 libraries
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (3)
- .github/workflows/ExecuteQuery_build_and_test_on_main.yml (1 hunks)
- .github/workflows/ExecuteQuery_build_and_test_on_push.yml (1 hunks)
- Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery/Definitions/DataTypes.cs (1 hunks)
Files skipped from review due to trivial changes (1)
- Frends.IBMDB2.ExecuteQuery/Frends.IBMDB2.ExecuteQuery/Definitions/DataTypes.cs
Additional comments not posted (4)
.github/workflows/ExecuteQuery_build_and_test_on_main.yml (2)
16-16
: Verify the Prebuild CommandThe prebuild command runs a Docker container. Ensure that the Docker image
icr.io/db2_community/db2
and the environment file./Frends.IBMDB2.ExecuteQuery.Tests/lib/env_list.txt
are correctly configured and accessible in the CI environment.
19-20
: Ensure Secrets are Properly ConfiguredThe secrets
BADGE_SERVICE_API_KEY
andLD_LIBRARY_PATH
are used. Ensure these secrets are correctly set in the repository settings to avoid build failures..github/workflows/ExecuteQuery_build_and_test_on_push.yml (2)
16-16
: Verify the Prebuild CommandThe prebuild command runs a Docker container. Ensure that the Docker image
icr.io/db2_community/db2
and the environment file./Frends.IBMDB2.ExecuteQuery.Tests/lib/env_list.txt
are correctly configured and accessible in the CI environment.
20-20
: Ensure Secrets are Properly ConfiguredThe secrets
BADGE_SERVICE_API_KEY
andTASKS_TEST_FEED_API_KEY
are used. Ensure these secrets are correctly set in the repository settings to avoid build failures.
#1
Summary by CodeRabbit
New Features
Frends.IBMDB2.ExecuteQuery
module upon code changes and manual triggers.1.0.0
, marking the initial implementation of the module.Enhancements
Frends.IBMDB2.ExecuteQuery
module.