diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 21635a75..99aaa09a 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -5,6 +5,7 @@ on: branches: - master - 2201.[0-9]+.x + - v1.8.x jobs: ubuntu-build: diff --git a/build-config/spotbugs-exclude.xml b/build-config/spotbugs-exclude.xml index a16e871b..9e424c12 100644 --- a/build-config/spotbugs-exclude.xml +++ b/build-config/spotbugs-exclude.xml @@ -39,13 +39,52 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + diff --git a/changelog.md b/changelog.md index 1f4340f6..ac0eb54c 100644 --- a/changelog.md +++ b/changelog.md @@ -8,9 +8,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +### Changed +- [Revert Accept escaped backtick as insertions in parameterised query](https://github.com/ballerina-platform/ballerina-standard-library/issues/2056) + +## [1.8.0] - 2023-04-10 + ### Changed - [Improve documentation regard `sql:Column` annotation](https://github.com/ballerina-platform/ballerina-standard-library/issues/4134) - [Handle null error messages from underlying drivers](https://github.com/ballerina-platform/ballerina-standard-library/issues/4200) +- [Make `sql:Client` isolated](https://github.com/ballerina-platform/ballerina-standard-library/issues/4455) ## [1.7.1] - 2023-03-09 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 5705d35b..26d615d8 100644 --- a/native/src/main/java/io/ballerina/stdlib/sql/Constants.java +++ b/native/src/main/java/io/ballerina/stdlib/sql/Constants.java @@ -77,7 +77,6 @@ private Constants() { public static final String PASSWORD = "password"; public static final String ANNON_RECORD_TYPE_NAME = "record {"; - public static final String BACKTICK = "`"; public static final String DEFAULT_STREAM_CONSTRAINT_NAME = "$stream$anon$constraint$"; public static final String INHERENT_TYPE_VIOLATION = "{ballerina/lang.map}InherentTypeViolation"; /** diff --git a/native/src/main/java/io/ballerina/stdlib/sql/ParameterizedQuery.java b/native/src/main/java/io/ballerina/stdlib/sql/ParameterizedQuery.java deleted file mode 100644 index eaddbd39..00000000 --- a/native/src/main/java/io/ballerina/stdlib/sql/ParameterizedQuery.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.stdlib.sql; - -/** - * Processed Details from sql:ParameterizedQuery. - */ -public class ParameterizedQuery { - private final String sqlQuery; - private final Object[] insertions; - - public ParameterizedQuery(String sqlQuery, Object[] insertions) { - this.sqlQuery = sqlQuery; - this.insertions = insertions; - } - - public String getSqlQuery() { - return sqlQuery; - } - - public Object[] getInsertions() { - return insertions; - } -} 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 71b5f74f..190fc14d 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 @@ -32,7 +32,6 @@ import io.ballerina.runtime.api.values.BTypedesc; import io.ballerina.runtime.transactions.TransactionResourceManager; import io.ballerina.stdlib.sql.Constants; -import io.ballerina.stdlib.sql.ParameterizedQuery; import io.ballerina.stdlib.sql.datasource.SQLDatasource; import io.ballerina.stdlib.sql.exception.ApplicationError; import io.ballerina.stdlib.sql.parameterprocessor.AbstractResultParameterProcessor; @@ -62,6 +61,7 @@ import static io.ballerina.stdlib.sql.datasource.SQLWorkerThreadPool.SQL_EXECUTOR_SERVICE; import static io.ballerina.stdlib.sql.utils.Utils.getColumnDefinitions; import static io.ballerina.stdlib.sql.utils.Utils.getDefaultStreamConstraint; +import static io.ballerina.stdlib.sql.utils.Utils.getSqlQuery; import static io.ballerina.stdlib.sql.utils.Utils.updateProcedureCallExecutionResult; /** @@ -121,13 +121,12 @@ private static Object nativeCallExecutable(BObject client, BObject paramSQLStrin ResultSet resultSet; String sqlQuery = null; try { - ParameterizedQuery parameterizedQuery = Utils.getParameterizedSQLQuery(paramSQLString); - sqlQuery = parameterizedQuery.getSqlQuery(); + sqlQuery = getSqlQuery(paramSQLString); connection = SQLDatasource.getConnection(isWithinTrxBlock, trxResourceManager, client, sqlDatasource); statement = connection.prepareCall(sqlQuery); HashMap outputParamTypes = new HashMap<>(); - setCallParameters(connection, statement, parameterizedQuery.getInsertions(), outputParamTypes, + setCallParameters(connection, statement, paramSQLString, outputParamTypes, statementParameterProcessor); boolean resultType = statement.execute(); @@ -159,7 +158,7 @@ private static Object nativeCallExecutable(BObject client, BObject paramSQLStrin updateProcedureCallExecutionResult(statement, procedureCallResult); } - populateOutParameters(statement, parameterizedQuery.getInsertions(), outputParamTypes, + populateOutParameters(statement, paramSQLString, outputParamTypes, resultParameterProcessor); procedureCallResult.addNativeData(STATEMENT_NATIVE_DATA_FIELD, statement); @@ -182,11 +181,12 @@ private static Object nativeCallExecutable(BObject client, BObject paramSQLStrin } private static void setCallParameters(Connection connection, CallableStatement statement, - Object[] insertions, HashMap outputParamTypes, + BObject paramString, HashMap outputParamTypes, AbstractStatementParameterProcessor statementParameterProcessor) throws SQLException, ApplicationError { - for (int i = 0; i < insertions.length; i++) { - Object object = insertions[i]; + BArray arrayValue = paramString.getArrayValue(Constants.ParameterizedQueryFields.INSERTIONS); + for (int i = 0; i < arrayValue.size(); i++) { + Object object = arrayValue.get(i); int index = i + 1; if (object instanceof BObject) { BObject objectValue = (BObject) object; @@ -225,19 +225,20 @@ private static void setCallParameters(Connection connection, CallableStatement s } } - private static void populateOutParameters(CallableStatement statement, Object[] insertions, + private static void populateOutParameters(CallableStatement statement, BObject paramSQLString, HashMap outputParamTypes, AbstractResultParameterProcessor resultParameterProcessor) throws SQLException, ApplicationError { if (outputParamTypes.size() == 0) { return; } + BArray arrayValue = paramSQLString.getArrayValue(Constants.ParameterizedQueryFields.INSERTIONS); for (Map.Entry entry : outputParamTypes.entrySet()) { int paramIndex = entry.getKey(); int sqlType = entry.getValue(); - BObject parameter = (BObject) insertions[paramIndex - 1]; + BObject parameter = (BObject) arrayValue.get(paramIndex - 1); parameter.addNativeData(Constants.ParameterObject.SQL_TYPE_NATIVE_DATA, sqlType); Object result; diff --git a/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/ExecuteProcessor.java b/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/ExecuteProcessor.java index 30ff43d2..a09aacc6 100644 --- a/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/ExecuteProcessor.java +++ b/native/src/main/java/io/ballerina/stdlib/sql/nativeimpl/ExecuteProcessor.java @@ -28,7 +28,6 @@ import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.transactions.TransactionResourceManager; import io.ballerina.stdlib.sql.Constants; -import io.ballerina.stdlib.sql.ParameterizedQuery; import io.ballerina.stdlib.sql.datasource.SQLDatasource; import io.ballerina.stdlib.sql.exception.ApplicationError; import io.ballerina.stdlib.sql.parameterprocessor.AbstractStatementParameterProcessor; @@ -52,6 +51,7 @@ import static io.ballerina.stdlib.sql.datasource.SQLWorkerThreadPool.SQL_EXECUTOR_SERVICE; import static io.ballerina.stdlib.sql.utils.Utils.closeResources; import static io.ballerina.stdlib.sql.utils.Utils.getGeneratedKeys; +import static io.ballerina.stdlib.sql.utils.Utils.getSqlQuery; /** @@ -104,8 +104,7 @@ private static Object nativeExecuteExecutable(BObject client, BObject paramSQLSt ResultSet resultSet = null; String sqlQuery = null; try { - ParameterizedQuery parameterizedQuery = Utils.getParameterizedSQLQuery(paramSQLString); - sqlQuery = parameterizedQuery.getSqlQuery(); + sqlQuery = getSqlQuery(paramSQLString); connection = SQLDatasource.getConnection(isWithInTrxBlock, trxResourceManager, client, sqlDatasource); if (sqlDatasource.getExecuteGKFlag()) { @@ -114,7 +113,7 @@ private static Object nativeExecuteExecutable(BObject client, BObject paramSQLSt statement = connection.prepareStatement(sqlQuery); } - statementParameterProcessor.setParams(connection, statement, parameterizedQuery.getInsertions()); + statementParameterProcessor.setParams(connection, statement, paramSQLString); int count = statement.executeUpdate(); Object lastInsertedId = null; @@ -184,19 +183,20 @@ private static Object nativeBatchExecuteExecutable(BObject client, BArray paramS Connection connection = null; PreparedStatement statement = null; String sqlQuery = null; - List parameters = new ArrayList<>(); + List parameters = new ArrayList<>(); List> executionResults = new ArrayList<>(); boolean processResultSet = false; int batchSize = 1000; try { Object[] paramSQLObjects = paramSQLStrings.getValues(); - ParameterizedQuery parameterizedQuery = Utils.getParameterizedSQLQuery(((BObject) paramSQLObjects[0])); - sqlQuery = parameterizedQuery.getSqlQuery(); - parameters.add(parameterizedQuery.getInsertions()); + BObject parameterizedQuery = (BObject) paramSQLObjects[0]; + sqlQuery = getSqlQuery(parameterizedQuery); + parameters.add(parameterizedQuery); for (int paramIndex = 1; paramIndex < paramSQLStrings.size(); paramIndex++) { - parameterizedQuery = Utils.getParameterizedSQLQuery(((BObject) paramSQLObjects[paramIndex])); - if (sqlQuery.equals(parameterizedQuery.getSqlQuery())) { - parameters.add(parameterizedQuery.getInsertions()); + parameterizedQuery = (BObject) paramSQLObjects[paramIndex]; + String paramSQLQuery = getSqlQuery(parameterizedQuery); + if (sqlQuery.equals(paramSQLQuery)) { + parameters.add(parameterizedQuery); } else { return ErrorGenerator.getSQLApplicationError("Batch Execute cannot contain different SQL " + "commands. These has to be executed in different function calls"); 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 4f31e0d6..b3662195 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 @@ -36,7 +36,6 @@ import io.ballerina.runtime.api.values.BTypedesc; import io.ballerina.runtime.transactions.TransactionResourceManager; import io.ballerina.stdlib.sql.Constants; -import io.ballerina.stdlib.sql.ParameterizedQuery; import io.ballerina.stdlib.sql.datasource.SQLDatasource; import io.ballerina.stdlib.sql.exception.ApplicationError; import io.ballerina.stdlib.sql.exception.DataError; @@ -115,11 +114,10 @@ private static BStream nativeQueryExecutable( ResultSet resultSet = null; String sqlQuery = null; try { - ParameterizedQuery parameterizedQuery = Utils.getParameterizedSQLQuery(paramSQLString); - sqlQuery = parameterizedQuery.getSqlQuery(); + sqlQuery = Utils.getSqlQuery(paramSQLString); connection = SQLDatasource.getConnection(isWithInTrxBlock, trxResourceManager, client, sqlDatasource); statement = connection.prepareStatement(sqlQuery); - statementParameterProcessor.setParams(connection, statement, parameterizedQuery.getInsertions()); + statementParameterProcessor.setParams(connection, statement, paramSQLString); resultSet = statement.executeQuery(); RecordType streamConstraint = (RecordType) TypeUtils.getReferredType( ((BTypedesc) recordType).getDescribingType()); @@ -172,7 +170,8 @@ public static Object nativeQueryRow(Environment env, BObject client, BObject par } private static Object nativeQueryRowExecutable( - BObject client, BObject paramSQLString, BTypedesc ballerinaType, + BObject client, BObject paramSQLString, + BTypedesc ballerinaType, AbstractStatementParameterProcessor statementParameterProcessor, AbstractResultParameterProcessor resultParameterProcessor, boolean isWithInTrxBlock, TransactionResourceManager trxResourceManager) { @@ -189,11 +188,10 @@ private static Object nativeQueryRowExecutable( ResultSet resultSet = null; String sqlQuery = null; try { - ParameterizedQuery parameterizedQuery = Utils.getParameterizedSQLQuery(paramSQLString); - sqlQuery = parameterizedQuery.getSqlQuery(); + sqlQuery = Utils.getSqlQuery(paramSQLString); connection = SQLDatasource.getConnection(isWithInTrxBlock, trxResourceManager, client, sqlDatasource); statement = connection.prepareStatement(sqlQuery); - statementParameterProcessor.setParams(connection, statement, parameterizedQuery.getInsertions()); + statementParameterProcessor.setParams(connection, statement, paramSQLString); resultSet = statement.executeQuery(); if (!resultSet.next()) { return ErrorGenerator.getNoRowsError("Query did not retrieve any rows."); diff --git a/native/src/main/java/io/ballerina/stdlib/sql/parameterprocessor/AbstractStatementParameterProcessor.java b/native/src/main/java/io/ballerina/stdlib/sql/parameterprocessor/AbstractStatementParameterProcessor.java index fcc7ba55..9ed75496 100644 --- a/native/src/main/java/io/ballerina/stdlib/sql/parameterprocessor/AbstractStatementParameterProcessor.java +++ b/native/src/main/java/io/ballerina/stdlib/sql/parameterprocessor/AbstractStatementParameterProcessor.java @@ -196,10 +196,11 @@ protected abstract void setXml(Connection connection, PreparedStatement prepared protected abstract int setCustomBOpenRecord(Connection connection, PreparedStatement preparedStatement, int index, Object value, boolean returnType) throws DataError, SQLException; - public void setParams(Connection connection, PreparedStatement preparedStatement, - Object[] insertions) throws DataError, SQLException { - for (int i = 0; i < insertions.length; i++) { - Object object = insertions[i]; + public void setParams(Connection connection, PreparedStatement preparedStatement, BObject paramString) + throws DataError, SQLException { + BArray arrayValue = paramString.getArrayValue(Constants.ParameterizedQueryFields.INSERTIONS); + for (int i = 0; i < arrayValue.size(); i++) { + Object object = arrayValue.get(i); int index = i + 1; setSQLValueParam(connection, preparedStatement, index, object, false); } 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 0f829263..3101d79c 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 @@ -41,7 +41,6 @@ import io.ballerina.runtime.api.values.BValue; import io.ballerina.runtime.transactions.TransactionResourceManager; import io.ballerina.stdlib.sql.Constants; -import io.ballerina.stdlib.sql.ParameterizedQuery; import io.ballerina.stdlib.sql.exception.ApplicationError; import io.ballerina.stdlib.sql.exception.ConversionError; import io.ballerina.stdlib.sql.exception.DataError; @@ -85,7 +84,6 @@ import static io.ballerina.stdlib.sql.Constants.AFFECTED_ROW_COUNT_FIELD; import static io.ballerina.stdlib.sql.Constants.ANNON_RECORD_TYPE_NAME; import static io.ballerina.stdlib.sql.Constants.ANN_COLUMN_NAME_FIELD; -import static io.ballerina.stdlib.sql.Constants.BACKTICK; import static io.ballerina.stdlib.sql.Constants.COLUMN_ANN_NAME; import static io.ballerina.stdlib.sql.Constants.DEFAULT_STREAM_CONSTRAINT_NAME; import static io.ballerina.stdlib.sql.Constants.EXECUTION_RESULT_FIELD; @@ -155,23 +153,16 @@ public static void closeResources(boolean isWithinTrxBlock, ResultSet resultSet, } } - public static ParameterizedQuery getParameterizedSQLQuery(BObject paramString) { + public static String getSqlQuery(BObject paramString) { + BArray stringsArray = paramString.getArrayValue(Constants.ParameterizedQueryFields.STRINGS); StringBuilder sqlQuery = new StringBuilder(); - List insertions = new ArrayList<>(); - - BArray bStringsArray = paramString.getArrayValue(Constants.ParameterizedQueryFields.STRINGS); - BArray bInsertions = paramString.getArrayValue(Constants.ParameterizedQueryFields.INSERTIONS); - for (int i = 0; i < bInsertions.size(); i++) { - if (bInsertions.get(i) instanceof BString && bInsertions.getBString(i).getValue().equals(BACKTICK)) { - sqlQuery.append(bStringsArray.getBString(i).getValue()).append(BACKTICK); - } else { - insertions.add(bInsertions.get(i)); - sqlQuery.append(bStringsArray.getBString(i).getValue()).append(" ? "); + for (int i = 0; i < stringsArray.size(); i++) { + if (i > 0) { + sqlQuery.append(" ? "); } + sqlQuery.append(stringsArray.get(i).toString()); } - sqlQuery.append(bStringsArray.getBString(bInsertions.size())); - - return new ParameterizedQuery(sqlQuery.toString(), insertions.toArray()); + return sqlQuery.toString(); } public static DataError throwInvalidParameterError(Object value, String sqlType) { diff --git a/native/src/test/java/io/ballerina/stdlib/sql/TestUtils.java b/native/src/test/java/io/ballerina/stdlib/sql/TestUtils.java index 1eedca47..4ed8ded2 100644 --- a/native/src/test/java/io/ballerina/stdlib/sql/TestUtils.java +++ b/native/src/test/java/io/ballerina/stdlib/sql/TestUtils.java @@ -145,7 +145,7 @@ public BObject getObjectValue(BString bString) { @Override public BArray getArrayValue(BString bString) { - return ((BArray) nativeData.get(bString.getValue())); + return null; } @Override diff --git a/native/src/test/java/io/ballerina/stdlib/sql/utils/UtilsTest.java b/native/src/test/java/io/ballerina/stdlib/sql/utils/UtilsTest.java index a8d3cfec..dae405b3 100644 --- a/native/src/test/java/io/ballerina/stdlib/sql/utils/UtilsTest.java +++ b/native/src/test/java/io/ballerina/stdlib/sql/utils/UtilsTest.java @@ -18,20 +18,14 @@ package io.ballerina.stdlib.sql.utils; -import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.StructureType; import io.ballerina.runtime.api.utils.TypeUtils; -import io.ballerina.runtime.api.values.BObject; -import io.ballerina.runtime.api.values.BString; -import io.ballerina.stdlib.sql.Constants; -import io.ballerina.stdlib.sql.ParameterizedQuery; import io.ballerina.stdlib.sql.TestUtils; import org.testng.annotations.Test; import java.util.ArrayList; import java.util.List; -import static io.ballerina.runtime.api.utils.StringUtils.fromString; import static org.testng.Assert.assertEquals; /** @@ -62,23 +56,4 @@ void getDefaultRecordTypeTest() { StructureType structureType = Utils.getDefaultRecordType(list); assertEquals(structureType.getFlags(), 0); } - - @Test - void backTickEscapeTest() { - // HSQLDB does not support backtick quotes - BObject bParameterizedQuery = TestUtils.getMockObject("parameterizedQuery"); - - BString[] bStrings = {fromString("x"), fromString("y"), fromString("z"), fromString("")}; - - BString[] insertions = {fromString("`"), fromString("abc"), fromString("`") }; - - - bParameterizedQuery.addNativeData(Constants.ParameterizedQueryFields.STRINGS.getValue(), - ValueCreator.createArrayValue(bStrings)); - bParameterizedQuery.addNativeData(Constants.ParameterizedQueryFields.INSERTIONS.getValue(), - ValueCreator.createArrayValue(insertions)); - - ParameterizedQuery parameterizedSQLQuery = Utils.getParameterizedSQLQuery(bParameterizedQuery); - assertEquals(parameterizedSQLQuery.getSqlQuery(), "x`y ? z`"); - } }