Skip to content
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

Merged
merged 115 commits into from
Sep 17, 2024
Merged

Initial implementation #2

merged 115 commits into from
Sep 17, 2024

Conversation

RikuVirtanen
Copy link
Collaborator

@RikuVirtanen RikuVirtanen commented Oct 23, 2023

#1

Summary by CodeRabbit

  • New Features

    • Introduced automated workflows for building and testing the Frends.IBMDB2.ExecuteQuery module upon code changes and manual triggers.
    • Added a changelog for version 1.0.0, marking the initial implementation of the module.
  • Enhancements

    • Added a new enum for IBM DB2-specific data types to the Frends.IBMDB2.ExecuteQuery module.
    • Implemented functionality for executing SQL queries against an IBM DB2 database, including support for various execution types and error handling.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Commits

Files that changed from the base of the PR and between 02d4b8c and d7d25bb.

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Commits

Files that changed from the base of the PR and between d7d25bb and dc927b3.

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 ensuring fetch-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

Commits

Files that changed from the base of the PR and between dc927b3 and f4d399e.

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.yml

Length 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.yml

Length 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 to env_var_name_8 are used to set corresponding secret values in the environment.
  • workdir and prebuild_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.yml

Length 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.yml

Length 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.yml

Length of output: 1085


185-251: Review the installation and usage of external tools to ensure security and efficiency.

.github/workflows/localflow.yml Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Commits

Files that changed from the base of the PR and between f4d399e and d5dcef3.

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)

Comment on lines 25 to 198
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}." };
}
Copy link
Contributor

@coderabbitai coderabbitai bot Jun 25, 2024

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.

Suggested change
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}" };
}

Copy link
Contributor

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 :)

Copy link
Contributor

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Commits

Files that changed from the base of the PR and between d5dcef3 and 47566e2.

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 to env_var_name_8, workdir, and prebuild_command are used in the workflow.
  • Secrets env_var_value_1 to env_var_value_8, badge_service_api_key, test_feed_api_key, build_time_nuget_feed_url, and build_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.yml

Length of output: 2421

.github/workflows/localflow.yml Outdated Show resolved Hide resolved
.github/workflows/localflow.yml Outdated Show resolved Hide resolved
.github/workflows/localflow.yml Outdated Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

Commits

Files that changed from the base of the PR and between 47566e2 and cbb9ab4.

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

mateusz.wojtania and others added 2 commits June 26, 2024 08:35
…ons/DataTypes.cs

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 Clearly

The workflow name ExecuteQuery_build_main is clear but could be more descriptive, such as Build and Test on Main Branch.

- name: ExecuteQuery_build_main
+ name: Build and Test on Main Branch

17-17: Clarify the Purpose of Environment Variable

The 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 Clearly

The workflow name ExecuteQuery_build_test is clear but could be more descriptive, such as Build and Test on Push Events.

- name: ExecuteQuery_build_test
+ name: Build and Test on Push Events

17-17: Clarify the Purpose of Environment Variable

The 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

Commits

Files that changed from the base of the PR and between cbb9ab4 and 59de1fe.

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 Command

The 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 Configured

The secrets BADGE_SERVICE_API_KEY and LD_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 Command

The 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 Configured

The secrets BADGE_SERVICE_API_KEY and TASKS_TEST_FEED_API_KEY are used. Ensure these secrets are correctly set in the repository settings to avoid build failures.

@jefim jefim merged commit 0ee739a into main Sep 17, 2024
3 checks passed
@jefim jefim deleted the issue-1 branch September 17, 2024 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Frends.IBMDB2.ExecuteQuery
3 participants