diff --git a/ballerina/types.bal b/ballerina/types.bal index 9142a5b7..4020e5f0 100644 --- a/ballerina/types.bal +++ b/ballerina/types.bal @@ -1321,6 +1321,19 @@ public distinct class XMLOutParameter { } external; } +# Represents the generic OUT Cursor Parameters in `sql:ParameterizedCallQuery`. +public class CursorOutParameter { + + # Parses returned SQL result set values to a ballerina stream value. + # + # + typeDesc - The `typedesc` of the record to which the result needs to be returned + # + return - Stream of records in the `rowType` type + public isolated function get(typedesc rowType = <>) returns stream = @java:Method { + 'class: "io.ballerina.stdlib.sql.nativeimpl.OutParameterProcessor", + name: "getOutCursorValue" + } external; +}; + # Represents SQL InOutParameter in `sql:ParameterizedCallQuery`. public class InOutParameter { Value 'in; @@ -1340,7 +1353,7 @@ public class InOutParameter { } # Generic type that can be passed to `sql:ParameterizedCallQuery` to indicate procedure/function parameters. -public type Parameter Value|InOutParameter|OutParameter; +public type Parameter Value|InOutParameter|OutParameter|CursorOutParameter; # The object constructed through backtick surrounded strings. Dynamic parameters of `sql:Parameter` type can be indicated using `${}` # such as `` `The sql:ParameterizedQuery is ${variable_name}` ``. diff --git a/native/src/main/java/io/ballerina/stdlib/sql/Constants.java b/native/src/main/java/io/ballerina/stdlib/sql/Constants.java index 92dfeaf7..1c7c1b4e 100644 --- a/native/src/main/java/io/ballerina/stdlib/sql/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/sql/Constants.java @@ -297,6 +297,7 @@ private OutParameterTypes() { public static final String BOOLEAN = "BooleanOutParameter"; public static final String BOOLEAN_ARRAY = "BooleanArrayOutParameter"; public static final String REF = "RefOutParameter"; + public static final String REF_CURSOR = "CursorOutParameter"; public static final String STRUCT = "StructOutParameter"; public static final String XML = "XMLOutParameter"; } diff --git a/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/CallProcessor.java b/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/CallProcessor.java index 17d8d70a..b0e5067f 100644 --- a/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/CallProcessor.java +++ b/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/CallProcessor.java @@ -334,6 +334,7 @@ private static void populateOutParameters(CallableStatement statement, BObject p result = resultParameterProcessor.processBoolean(statement, paramIndex); break; case Types.REF: + case Types.REF_CURSOR: result = resultParameterProcessor.processRef(statement, paramIndex); break; case Types.STRUCT: @@ -462,6 +463,9 @@ private static int getOutParameterType(BObject typedValue, case Constants.OutParameterTypes.REF: sqlTypeValue = Types.REF; break; + case Constants.OutParameterTypes.REF_CURSOR: + sqlTypeValue = Types.REF_CURSOR; + break; case Constants.OutParameterTypes.STRUCT: sqlTypeValue = Types.STRUCT; break; diff --git a/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/OutParameterProcessor.java b/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/OutParameterProcessor.java index 9c711302..6871a8a8 100644 --- a/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/OutParameterProcessor.java +++ b/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/OutParameterProcessor.java @@ -19,9 +19,11 @@ package io.ballerina.stdlib.sql.nativeimpl; import io.ballerina.runtime.api.TypeTags; +import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BObject; +import io.ballerina.runtime.api.values.BStream; import io.ballerina.runtime.api.values.BTypedesc; import io.ballerina.stdlib.sql.Constants; import io.ballerina.stdlib.sql.exception.ApplicationError; @@ -35,6 +37,7 @@ import java.sql.Clob; import java.sql.Date; import java.sql.NClob; +import java.sql.ResultSet; import java.sql.RowId; import java.sql.SQLException; import java.sql.SQLXML; @@ -61,10 +64,22 @@ public static Object getOutParameterValue(BObject result, BTypedesc typeDesc) { return get(result, typeDesc, DefaultResultParameterProcessor.getInstance(), "OutParameter"); } + public static BStream getOutCursorValue(BObject result, BTypedesc typeDesc) { + return get(result, typeDesc, DefaultResultParameterProcessor.getInstance()); + } + public static Object getInOutParameterValue(BObject result, BTypedesc typeDesc) { return get(result, typeDesc, DefaultResultParameterProcessor.getInstance(), "InOutParameter"); } + public static BStream get(BObject result, Object recordType, + AbstractResultParameterProcessor resultParameterProcessor) { + Object value = result.getNativeData(Constants.ParameterObject.VALUE_NATIVE_DATA); + RecordType streamConstraint = (RecordType) TypeUtils.getReferredType( + ((BTypedesc) recordType).getDescribingType()); + return resultParameterProcessor.convertCursorValue((ResultSet) value, streamConstraint); + } + public static Object get(BObject result, BTypedesc typeDesc, AbstractResultParameterProcessor resultParameterProcessor, String parameterType) { int sqlType = (int) result.getNativeData(Constants.ParameterObject.SQL_TYPE_NATIVE_DATA); diff --git a/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/QueryProcessor.java b/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/QueryProcessor.java index b3662195..441c943d 100644 --- a/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/QueryProcessor.java +++ b/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/QueryProcessor.java @@ -44,7 +44,6 @@ import io.ballerina.stdlib.sql.parameterprocessor.AbstractStatementParameterProcessor; import io.ballerina.stdlib.sql.utils.ColumnDefinition; import io.ballerina.stdlib.sql.utils.ErrorGenerator; -import io.ballerina.stdlib.sql.utils.ModuleUtils; import io.ballerina.stdlib.sql.utils.PrimitiveTypeColumnDefinition; import io.ballerina.stdlib.sql.utils.Utils; @@ -55,6 +54,7 @@ import java.util.List; import static io.ballerina.stdlib.sql.datasource.SQLWorkerThreadPool.SQL_EXECUTOR_SERVICE; +import static io.ballerina.stdlib.sql.utils.Utils.getErrorStream; /** * This class provides the query processing implementation which executes sql queries. @@ -266,17 +266,6 @@ private static Object getRecordOrPrimitiveTypeBValue( return createValue(resultSet, 1, definition, resultParameterProcessor); } - private static BStream getErrorStream(Object recordType, BError errorValue) { - return ValueCreator.createStreamValue( - TypeCreator.createStreamType(((BTypedesc) recordType).getDescribingType(), - PredefinedTypes.TYPE_NULL), createRecordIterator(errorValue)); - } - - private static BObject createRecordIterator(BError errorValue) { - return ValueCreator.createObjectValue(ModuleUtils.getModule(), Constants.RESULT_ITERATOR_OBJECT, - errorValue, null); - } - public static BMap createRecord(ResultSet resultSet, List columnDefinitions, RecordType recordConstraint, AbstractResultParameterProcessor resultParameterProcessor) diff --git a/native/src/main/java/io/ballerina/stdlib/sql/parameterprocessor/AbstractResultParameterProcessor.java b/native/src/main/java/io/ballerina/stdlib/sql/parameterprocessor/AbstractResultParameterProcessor.java index 1a5cd68c..8edd7307 100644 --- a/native/src/main/java/io/ballerina/stdlib/sql/parameterprocessor/AbstractResultParameterProcessor.java +++ b/native/src/main/java/io/ballerina/stdlib/sql/parameterprocessor/AbstractResultParameterProcessor.java @@ -19,12 +19,14 @@ import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.StructureType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.JsonUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BObject; +import io.ballerina.runtime.api.values.BStream; import io.ballerina.runtime.api.values.BString; import io.ballerina.stdlib.sql.Constants; import io.ballerina.stdlib.sql.exception.ConversionError; @@ -116,6 +118,8 @@ public abstract Object convertBoolean(boolean value, int sqlType, Type type, boo public abstract Object convertStruct(Struct value, int sqlType, Type type) throws DataError, SQLException; + public abstract BStream convertCursorValue(ResultSet value, RecordType recordType); + public abstract Object convertXml(SQLXML value, int sqlType, Type type) throws DataError, SQLException; public abstract Object convertCustomOutParameter(Object value, String outParamObjectName, int sqlType, diff --git a/native/src/main/java/io/ballerina/stdlib/sql/parameterprocessor/DefaultResultParameterProcessor.java b/native/src/main/java/io/ballerina/stdlib/sql/parameterprocessor/DefaultResultParameterProcessor.java index ea1ce022..0220dd30 100644 --- a/native/src/main/java/io/ballerina/stdlib/sql/parameterprocessor/DefaultResultParameterProcessor.java +++ b/native/src/main/java/io/ballerina/stdlib/sql/parameterprocessor/DefaultResultParameterProcessor.java @@ -30,15 +30,21 @@ import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.utils.XmlUtils; import io.ballerina.runtime.api.values.BArray; +import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BObject; +import io.ballerina.runtime.api.values.BStream; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.api.values.BXml; import io.ballerina.stdlib.sql.Constants; +import io.ballerina.stdlib.sql.exception.ApplicationError; import io.ballerina.stdlib.sql.exception.DataError; import io.ballerina.stdlib.sql.exception.FieldMismatchError; import io.ballerina.stdlib.sql.exception.TypeMismatchError; import io.ballerina.stdlib.sql.exception.UnsupportedTypeError; +import io.ballerina.stdlib.sql.utils.ColumnDefinition; +import io.ballerina.stdlib.sql.utils.ErrorGenerator; +import io.ballerina.stdlib.sql.utils.ModuleUtils; import io.ballerina.stdlib.sql.utils.PrimitiveTypeColumnDefinition; import io.ballerina.stdlib.sql.utils.Utils; @@ -62,6 +68,7 @@ import java.util.List; import static io.ballerina.runtime.api.utils.StringUtils.fromString; +import static io.ballerina.stdlib.sql.utils.Utils.getErrorStream; /** * This class implements methods required convert SQL types into ballerina types and @@ -325,6 +332,30 @@ protected BMap createUserDefinedType(Struct structValue, Struct return struct; } + public BStream convertCursorValue(ResultSet resultSet, RecordType streamConstraint) { + if (resultSet == null) { + return null; + } + try { + List columnDefinitions = Utils.getColumnDefinitions(resultSet, streamConstraint); + BObject resultIterator = ValueCreator.createObjectValue(ModuleUtils.getModule(), + Constants.RESULT_ITERATOR_OBJECT, null, getBalStreamResultIterator()); + resultIterator.addNativeData(Constants.RESULT_SET_NATIVE_DATA_FIELD, resultSet); + resultIterator.addNativeData(Constants.COLUMN_DEFINITIONS_DATA_FIELD, columnDefinitions); + resultIterator.addNativeData(Constants.RECORD_TYPE_DATA_FIELD, streamConstraint); + return ValueCreator.createStreamValue(TypeCreator.createStreamType(streamConstraint, + PredefinedTypes.TYPE_NULL), + resultIterator); + } catch (ApplicationError applicationError) { + BError errorValue = ErrorGenerator.getSQLApplicationError(applicationError); + return getErrorStream(streamConstraint, errorValue); + } catch (SQLException sqlException) { + BError errorValue = ErrorGenerator.getSQLDatabaseError(sqlException, + "Error while retrieving column definition from result set."); + return getErrorStream(streamConstraint, errorValue); + } + } + @Override protected void createUserDefinedTypeSubtype(Field internalField, StructureType structType) throws DataError { diff --git a/native/src/main/java/io/ballerina/stdlib/sql/utils/Utils.java b/native/src/main/java/io/ballerina/stdlib/sql/utils/Utils.java index c7ba4d4d..019ce43c 100644 --- a/native/src/main/java/io/ballerina/stdlib/sql/utils/Utils.java +++ b/native/src/main/java/io/ballerina/stdlib/sql/utils/Utils.java @@ -37,7 +37,9 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BMapInitialValueEntry; import io.ballerina.runtime.api.values.BObject; +import io.ballerina.runtime.api.values.BStream; import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.api.values.BTypedesc; import io.ballerina.runtime.api.values.BValue; import io.ballerina.runtime.transactions.TransactionResourceManager; import io.ballerina.stdlib.sql.Constants; @@ -1374,4 +1376,15 @@ public static void disableHikariLogs() { } } } + + public static BStream getErrorStream(Object recordType, BError errorValue) { + return ValueCreator.createStreamValue( + TypeCreator.createStreamType(((BTypedesc) recordType).getDescribingType(), + PredefinedTypes.TYPE_NULL), createRecordIterator(errorValue)); + } + + private static BObject createRecordIterator(BError errorValue) { + return ValueCreator.createObjectValue(ModuleUtils.getModule(), Constants.RESULT_ITERATOR_OBJECT, + errorValue, null); + } }