Skip to content

Commit

Permalink
Merge pull request #739 from MohamedSabthar/2201.8.x
Browse files Browse the repository at this point in the history
[2201.8.x] Fix stored procedure call having output parameter failing with error
  • Loading branch information
MohamedSabthar authored Oct 13, 2024
2 parents aa7f746 + 1b6c29c commit d7a1969
Show file tree
Hide file tree
Showing 10 changed files with 286 additions and 180 deletions.
8 changes: 4 additions & 4 deletions ballerina/Ballerina.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
org = "ballerina"
name = "sql"
version = "1.12.2"
version = "1.13.0"
authors = ["Ballerina"]
keywords = ["database", "client", "network", "SQL", "RDBMS"]
repository = "https://github.com/ballerina-platform/module-ballerina-sql"
Expand All @@ -15,11 +15,11 @@ graalvmCompatible = true
[[platform.java17.dependency]]
groupId = "io.ballerina.stdlib"
artifactId = "sql-native"
version = "1.12.2"
path = "../native/build/libs/sql-native-1.12.2.jar"
version = "1.13.0"
path = "../native/build/libs/sql-native-1.13.0-SNAPSHOT.jar"

[[platform.java17.dependency]]
path = "../test-utils/build/libs/sql-test-utils-1.12.2.jar"
path = "../test-utils/build/libs/sql-test-utils-1.13.0-SNAPSHOT.jar"
scope = "testOnly"

[[platform.java17.dependency]]
Expand Down
2 changes: 1 addition & 1 deletion ballerina/CompilerPlugin.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ id = "sql-compiler-plugin"
class = "io.ballerina.stdlib.sql.compiler.SQLCompilerPlugin"

[[dependency]]
path = "../compiler-plugin/build/libs/sql-compiler-plugin-1.12.2.jar"
path = "../compiler-plugin/build/libs/sql-compiler-plugin-1.13.0-SNAPSHOT.jar"
11 changes: 6 additions & 5 deletions ballerina/Dependencies.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "cache"
version = "3.7.0"
version = "3.7.1"
scope = "testOnly"
dependencies = [
{org = "ballerina", name = "constraint"},
Expand Down Expand Up @@ -69,7 +69,7 @@ modules = [
[[package]]
org = "ballerina"
name = "http"
version = "2.10.0"
version = "2.10.16"
scope = "testOnly"
dependencies = [
{org = "ballerina", name = "auth"},
Expand Down Expand Up @@ -98,7 +98,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "io"
version = "1.6.0"
version = "1.6.1"
dependencies = [
{org = "ballerina", name = "jballerina.java"},
{org = "ballerina", name = "lang.value"}
Expand Down Expand Up @@ -281,7 +281,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "observe"
version = "1.2.0"
version = "1.2.3"
scope = "testOnly"
dependencies = [
{org = "ballerina", name = "jballerina.java"}
Expand All @@ -300,7 +300,7 @@ dependencies = [
[[package]]
org = "ballerina"
name = "sql"
version = "1.12.2"
version = "1.13.0"
dependencies = [
{org = "ballerina", name = "file"},
{org = "ballerina", name = "io"},
Expand Down Expand Up @@ -344,6 +344,7 @@ modules = [
org = "ballerina"
name = "time"
version = "2.4.0"
scope = "testOnly"
dependencies = [
{org = "ballerina", name = "jballerina.java"}
]
Expand Down
161 changes: 109 additions & 52 deletions ballerina/tests/call-procedures-test.bal

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed
- [Fix stored procedure call having output parameter failing with result set closed error](https://github.com/ballerina-platform/ballerina-library/issues/7255)

## [1.12.0] - 2024-02-25

### Added
- Support for Cursor based result set retrieval in procedure calls

Expand Down
2 changes: 2 additions & 0 deletions docs/spec/spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ In addition to the above parameters, it has `CursorOutParameter` to retrieve the
stream<record{}, sql:Error?> resultStream = cursor.get();
```

> **_Note:_** In the case of a stored procedure query that returns a result set along with the mentioned parameters, the `get()` method of these parameters should only be invoked after consuming the result set. Otherwise, consuming the result set fails with a 'Error when iterating the SQL result. The result set is closed.' error.
## 3.3. Query concatenation

`sql:ParameterizedQuery` can be concatenated using util methods such as `sql:queryConcat()` and
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group=io.ballerina.stdlib
version=1.12.3-SNAPSHOT
version=1.13.0-SNAPSHOT

puppycrawlCheckstyleVersion=10.12.1
hikkariLibVersion=3.3.1
Expand Down
3 changes: 3 additions & 0 deletions native/src/main/java/io/ballerina/stdlib/sql/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ private Constants() {
public static final String READ_BYTE_CHANNEL_STRUCT = "ReadableByteChannel";
public static final String READ_CHAR_CHANNEL_STRUCT = "ReadableCharacterChannel";

public static final String RESULT_PARAMETER_PROCESSOR = "ResultParameterProcessor";
public static final String PARAMETER_INDEX_META_DATA = "parameterIndex";

public static final String USERNAME = "user";
public static final String PASSWORD = "password";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@

import static io.ballerina.stdlib.sql.Constants.CONNECTION_NATIVE_DATA_FIELD;
import static io.ballerina.stdlib.sql.Constants.DATABASE_CLIENT;
import static io.ballerina.stdlib.sql.Constants.PARAMETER_INDEX_META_DATA;
import static io.ballerina.stdlib.sql.Constants.PROCEDURE_CALL_RESULT;
import static io.ballerina.stdlib.sql.Constants.QUERY_RESULT_FIELD;
import static io.ballerina.stdlib.sql.Constants.RESULT_PARAMETER_PROCESSOR;
import static io.ballerina.stdlib.sql.Constants.RESULT_SET_COUNT_NATIVE_DATA_FIELD;
import static io.ballerina.stdlib.sql.Constants.RESULT_SET_TOTAL_NATIVE_DATA_FIELD;
import static io.ballerina.stdlib.sql.Constants.STATEMENT_NATIVE_DATA_FIELD;
Expand Down Expand Up @@ -159,7 +161,7 @@ private static Object nativeCallExecutable(BObject client, BObject paramSQLStrin
updateProcedureCallExecutionResult(statement, procedureCallResult);
}

populateOutParameters(statement, paramSQLString, outputParamTypes,
populateOutParametersMetaData(statement, paramSQLString, outputParamTypes,
resultParameterProcessor, procedureCallResult);

procedureCallResult.addNativeData(STATEMENT_NATIVE_DATA_FIELD, statement);
Expand Down Expand Up @@ -229,132 +231,25 @@ private static void setCallParameters(Connection connection, CallableStatement s
}
}

private static void populateOutParameters(CallableStatement statement, BObject paramSQLString,
HashMap<Integer, Integer> outputParamTypes,
AbstractResultParameterProcessor resultParameterProcessor,
BObject procedureCallResult)
private static void populateOutParametersMetaData(CallableStatement statement, BObject paramSQLString,
HashMap<Integer, Integer> outputParamTypes,
AbstractResultParameterProcessor resultParameterProcessor,
BObject procedureCallResult)
throws SQLException, ApplicationError {
if (outputParamTypes.size() == 0) {
return;
}
BArray arrayValue = paramSQLString.getArrayValue(Constants.ParameterizedQueryFields.INSERTIONS);

for (Map.Entry<Integer, Integer> entry : outputParamTypes.entrySet()) {
int paramIndex = entry.getKey();
int sqlType = entry.getValue();

BObject parameter = (BObject) arrayValue.get(paramIndex - 1);
parameter.addNativeData(PARAMETER_INDEX_META_DATA, paramIndex);
parameter.addNativeData(Constants.ParameterObject.SQL_TYPE_NATIVE_DATA, sqlType);

Object result;
switch (sqlType) {
case Types.CHAR:
result = resultParameterProcessor.processChar(statement, paramIndex);
break;
case Types.VARCHAR:
result = resultParameterProcessor.processVarchar(statement, paramIndex);
break;
case Types.LONGVARCHAR:
result = resultParameterProcessor.processLongVarchar(statement, paramIndex);
break;
case Types.NCHAR:
result = resultParameterProcessor.processNChar(statement, paramIndex);
break;
case Types.NVARCHAR:
result = resultParameterProcessor.processNVarchar(statement, paramIndex);
break;
case Types.LONGNVARCHAR:
result = resultParameterProcessor.processLongNVarchar(statement, paramIndex);
break;
case Types.BINARY:
result = resultParameterProcessor.processBinary(statement, paramIndex);
break;
case Types.VARBINARY:
result = resultParameterProcessor.processVarBinary(statement, paramIndex);
break;
case Types.LONGVARBINARY:
result = resultParameterProcessor.processLongVarBinary(statement, paramIndex);
break;
case Types.BLOB:
result = resultParameterProcessor.processBlob(statement, paramIndex);
break;
case Types.CLOB:
result = resultParameterProcessor.processClob(statement, paramIndex);
break;
case Types.NCLOB:
result = resultParameterProcessor.processNClob(statement, paramIndex);
break;
case Types.DATE:
result = resultParameterProcessor.processDate(statement, paramIndex);
break;
case Types.TIME:
result = resultParameterProcessor.processTime(statement, paramIndex);
break;
case Types.TIME_WITH_TIMEZONE:
result = resultParameterProcessor.processTimeWithTimeZone(statement, paramIndex);
break;
case Types.TIMESTAMP:
result = resultParameterProcessor.processTimestamp(statement, paramIndex);
break;
case Types.TIMESTAMP_WITH_TIMEZONE:
result = resultParameterProcessor.processTimestampWithTimeZone(statement, paramIndex);
break;
case Types.ARRAY:
result = resultParameterProcessor.processArray(statement, paramIndex);
break;
case Types.ROWID:
result = resultParameterProcessor.processRowID(statement, paramIndex);
break;
case Types.TINYINT:
result = resultParameterProcessor.processTinyInt(statement, paramIndex);
break;
case Types.SMALLINT:
result = resultParameterProcessor.processSmallInt(statement, paramIndex);
break;
case Types.INTEGER:
result = resultParameterProcessor.processInteger(statement, paramIndex);
break;
case Types.BIGINT:
result = resultParameterProcessor.processBigInt(statement, paramIndex);
break;
case Types.REAL:
result = resultParameterProcessor.processReal(statement, paramIndex);
break;
case Types.FLOAT:
result = resultParameterProcessor.processFloat(statement, paramIndex);
break;
case Types.DOUBLE:
result = resultParameterProcessor.processDouble(statement, paramIndex);
break;
case Types.NUMERIC:
result = resultParameterProcessor.processNumeric(statement, paramIndex);
break;
case Types.DECIMAL:
result = resultParameterProcessor.processDecimal(statement, paramIndex);
break;
case Types.BIT:
result = resultParameterProcessor.processBit(statement, paramIndex);
break;
case Types.BOOLEAN:
result = resultParameterProcessor.processBoolean(statement, paramIndex);
break;
case Types.REF:
case Types.REF_CURSOR:
result = resultParameterProcessor.processRef(statement, paramIndex);
// This is to clean up the result set attached to the ref cursor out parameter
// when procedure call result is closed.
procedureCallResult.addNativeData(Constants.REF_CURSOR_VALUE_NATIVE_DATA, result);
break;
case Types.STRUCT:
result = resultParameterProcessor.processStruct(statement, paramIndex);
break;
case Types.SQLXML:
result = resultParameterProcessor.processXML(statement, paramIndex);
break;
default:
result = resultParameterProcessor.processCustomOutParameters(statement, paramIndex, sqlType);
}
parameter.addNativeData(Constants.ParameterObject.VALUE_NATIVE_DATA, result);
parameter.addNativeData(RESULT_PARAMETER_PROCESSOR, resultParameterProcessor);
parameter.addNativeData(STATEMENT_NATIVE_DATA_FIELD, statement);
parameter.addNativeData(PROCEDURE_CALL_RESULT, procedureCallResult);
}
}

Expand Down
Loading

0 comments on commit d7a1969

Please sign in to comment.