From 00bab7d1c1bf41df23e4200da189ee9c659a54ec Mon Sep 17 00:00:00 2001 From: chengluo Date: Wed, 8 Feb 2023 15:56:59 +0800 Subject: [PATCH] feat: implement GUi mode for SQL server --- .../openblocks-plugins/mssqlPlugin/pom.xml | 5 + .../plugin/mssql/MssqlQueryExecutor.java | 238 ++++++++++++------ .../plugin/mssql/gui/GuiConstants.java | 8 + .../mssql/gui/MssqlBulkInsertCommand.java | 24 ++ .../mssql/gui/MssqlBulkUpdateCommand.java | 27 ++ .../plugin/mssql/gui/MssqlDeleteCommand.java | 44 ++++ .../plugin/mssql/gui}/MssqlInsertCommand.java | 8 +- .../plugin/mssql/gui/MssqlUpdateCommand.java | 41 +++ .../plugin/mssql/model/MssqlQueryConfig.java | 16 +- .../plugin/mysql/MysqlQueryExecutor.java | 17 +- .../plugin/postgres/PostgresExecutor.java | 8 +- .../plugin/common/QueryExecutionUtils.java | 4 +- .../sqlcommand/command/DeleteCommand.java | 16 +- .../openblocks/sdk/util/MustacheHelper.java | 31 --- .../command/mssql/MssqlInsertCommandTest.java | 41 --- .../command/postgres/PostgresCommandTest.java | 126 +++++----- 16 files changed, 410 insertions(+), 244 deletions(-) create mode 100644 server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/GuiConstants.java create mode 100644 server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlBulkInsertCommand.java create mode 100644 server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlBulkUpdateCommand.java create mode 100644 server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlDeleteCommand.java rename server/api-service/{openblocks-sdk/src/main/java/com/openblocks/sdk/plugin/sqlcommand/command/mssql => openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui}/MssqlInsertCommand.java (77%) create mode 100644 server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlUpdateCommand.java delete mode 100644 server/api-service/openblocks-sdk/src/test/java/com/openblocks/sdk/plugin/sqlcommand/command/mssql/MssqlInsertCommandTest.java diff --git a/server/api-service/openblocks-plugins/mssqlPlugin/pom.xml b/server/api-service/openblocks-plugins/mssqlPlugin/pom.xml index 9e0512df..4dcd3163 100644 --- a/server/api-service/openblocks-plugins/mssqlPlugin/pom.xml +++ b/server/api-service/openblocks-plugins/mssqlPlugin/pom.xml @@ -58,6 +58,11 @@ + + org.assertj + assertj-core + test + diff --git a/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/MssqlQueryExecutor.java b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/MssqlQueryExecutor.java index 2a78262c..ef4f58a9 100644 --- a/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/MssqlQueryExecutor.java +++ b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/MssqlQueryExecutor.java @@ -1,17 +1,19 @@ package com.openblocks.plugin.mssql; +import static com.google.common.collect.Lists.newArrayList; import static com.openblocks.plugin.mssql.util.MssqlStructureParser.parseTableAndColumns; import static com.openblocks.sdk.exception.PluginCommonError.CONNECTION_ERROR; import static com.openblocks.sdk.exception.PluginCommonError.DATASOURCE_GET_STRUCTURE_ERROR; import static com.openblocks.sdk.exception.PluginCommonError.PREPARED_STATEMENT_BIND_PARAMETERS_ERROR; import static com.openblocks.sdk.exception.PluginCommonError.QUERY_ARGUMENT_ERROR; import static com.openblocks.sdk.exception.PluginCommonError.QUERY_EXECUTION_ERROR; -import static com.openblocks.sdk.plugin.common.QueryExecutionUtils.getIdenticalColumns; import static com.openblocks.sdk.plugin.common.QueryExecutionUtils.querySharedScheduler; +import static com.openblocks.sdk.util.ExceptionUtils.wrapException; import static com.openblocks.sdk.util.JsonUtils.toJson; import static com.openblocks.sdk.util.MustacheHelper.doPrepareStatement; import static com.openblocks.sdk.util.MustacheHelper.extractMustacheKeysInOrder; import static com.openblocks.sdk.util.MustacheHelper.renderMustacheString; +import static org.apache.commons.lang3.ObjectUtils.firstNonNull; import java.math.BigDecimal; import java.sql.Connection; @@ -30,9 +32,15 @@ import java.util.Map; import java.util.function.Supplier; +import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.pf4j.Extension; +import com.openblocks.plugin.mssql.gui.MssqlBulkInsertCommand; +import com.openblocks.plugin.mssql.gui.MssqlBulkUpdateCommand; +import com.openblocks.plugin.mssql.gui.MssqlDeleteCommand; +import com.openblocks.plugin.mssql.gui.MssqlInsertCommand; +import com.openblocks.plugin.mssql.gui.MssqlUpdateCommand; import com.openblocks.plugin.mssql.model.MssqlDatasourceConfig; import com.openblocks.plugin.mssql.model.MssqlQueryConfig; import com.openblocks.plugin.mssql.util.MssqlResultParser; @@ -41,11 +49,12 @@ import com.openblocks.sdk.exception.PluginException; import com.openblocks.sdk.models.DatasourceStructure; import com.openblocks.sdk.models.DatasourceStructure.Table; -import com.openblocks.sdk.models.LocaleMessage; import com.openblocks.sdk.models.QueryExecutionResult; import com.openblocks.sdk.plugin.common.QueryExecutor; -import com.openblocks.sdk.plugin.common.sql.ResultSetParser; +import com.openblocks.sdk.plugin.common.SqlQueryUtils; import com.openblocks.sdk.plugin.common.sql.SqlBasedQueryExecutionContext; +import com.openblocks.sdk.plugin.sqlcommand.GuiSqlCommand; +import com.openblocks.sdk.plugin.sqlcommand.GuiSqlCommand.GuiSqlCommandRenderResult; import com.openblocks.sdk.query.QueryVisitorContext; import com.zaxxer.hikari.HikariDataSource; @@ -69,11 +78,15 @@ public SqlBasedQueryExecutionContext buildQueryExecutionContext(MssqlDatasourceC Map requestParams, QueryVisitorContext queryVisitorContext) { MssqlQueryConfig mssqlQueryConfig = MssqlQueryConfig.from(queryConfig); - String query = mssqlQueryConfig.getSql().trim(); - if (StringUtils.isBlank(query)) { - throw new PluginException(QUERY_ARGUMENT_ERROR, "SQL_EMPTY"); + if (mssqlQueryConfig.isGuiMode()) { + GuiSqlCommand sqlCommand = getGuiSqlCommand(mssqlQueryConfig); + return SqlBasedQueryExecutionContext.builder() + .guiSqlCommand(sqlCommand) + .requestParams(requestParams) + .build(); } + String query = SqlQueryUtils.removeQueryComments(mssqlQueryConfig.getSql().trim()); return SqlBasedQueryExecutionContext.builder() .query(query) .requestParams(requestParams) @@ -86,10 +99,16 @@ public SqlBasedQueryExecutionContext buildQueryExecutionContext(MssqlDatasourceC public Mono executeQuery(HikariDataSource hikariDataSource, SqlBasedQueryExecutionContext context) { String query = context.getQuery(); + GuiSqlCommand guiSqlCommand = context.getGuiSqlCommand(); + boolean isGuiMode = guiSqlCommand != null; Map requestParams = context.getRequestParams(); - boolean preparedStatement = !context.isDisablePreparedStatement(); + if (StringUtils.isBlank(query) && !isGuiMode) { + throw new PluginException(QUERY_ARGUMENT_ERROR, "SQL_EMPTY"); + } + + boolean isPreparedStatement = isGuiMode || !context.isDisablePreparedStatement(); - return Mono.fromSupplier(() -> executeQuery0(hikariDataSource, query, requestParams, preparedStatement)) + return Mono.fromSupplier(() -> executeQuery0(hikariDataSource, query, requestParams, guiSqlCommand, isPreparedStatement)) .onErrorMap(e -> { if (e instanceof PluginException) { return e; @@ -125,60 +144,103 @@ public Mono getStructure(HikariDataSource hikariDataSource) } private QueryExecutionResult executeQuery0(HikariDataSource hikariDataSource, String query, Map requestParams, - boolean isPreparedStatement) { - - List mustacheKeysInOrder = extractMustacheKeysInOrder(query); + GuiSqlCommand guiSqlCommand, boolean isPreparedStatement) { Statement statement = null; - ResultSet resultSet = null; PreparedStatement preparedQuery = null; boolean isResultSet; - + ResultSet generatedKeys = null; Connection connection = getConnection(hikariDataSource); try { - if (isPreparedStatement) { - String preparedSql = doPrepareStatement(query, mustacheKeysInOrder, requestParams); - - preparedQuery = connection.prepareStatement(preparedSql, Statement.RETURN_GENERATED_KEYS); - bindPreparedStatementParams(preparedQuery, - mustacheKeysInOrder, - requestParams - ); + if (guiSqlCommand != null) { + GuiSqlCommandRenderResult renderResult = guiSqlCommand.render(requestParams); + String sql = renderResult.sql(); + List bindParams = renderResult.bindParams(); + boolean isInsertQuery = guiSqlCommand.isInsertCommand(); + preparedQuery = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + bindPreparedStatementForGuiMode(preparedQuery, bindParams); isResultSet = preparedQuery.execute(); - resultSet = preparedQuery.getResultSet(); + if (isInsertQuery) { + generatedKeys = preparedQuery.getGeneratedKeys(); + } } else { - statement = connection.createStatement(); - isResultSet = statement.execute(renderMustacheString(query, requestParams), Statement.RETURN_GENERATED_KEYS); - resultSet = statement.getResultSet(); + if (isPreparedStatement) { + List mustacheKeysInOrder = extractMustacheKeysInOrder(query); + String preparedSql = doPrepareStatement(query, mustacheKeysInOrder, requestParams); + + preparedQuery = connection.prepareStatement(preparedSql, Statement.RETURN_GENERATED_KEYS); + bindPreparedStatementParams(preparedQuery, mustacheKeysInOrder, requestParams); + + isResultSet = preparedQuery.execute(); + } else { + statement = connection.createStatement(); + isResultSet = statement.execute(renderMustacheString(query, requestParams), Statement.RETURN_GENERATED_KEYS); + } } - return parseExecuteResult(isPreparedStatement, statement, resultSet, preparedQuery, isResultSet); + return parseExecuteResult(firstNonNull(preparedQuery, statement), isResultSet); } catch (SQLException e) { throw new PluginException(QUERY_EXECUTION_ERROR, "QUERY_EXECUTION_ERROR", e.getMessage()); } finally { - releaseResources(connection, statement, resultSet, preparedQuery); + // Note:When a Statement object is closed, its current ResultSet object, if one exists, is also closed. + // https://docs.oracle.com/javase/6/docs/api/java/sql/Statement.html + releaseResources(connection, statement, generatedKeys, preparedQuery); } } - private QueryExecutionResult parseExecuteResult(boolean preparedStatement, Statement statement, - ResultSet resultSet, PreparedStatement preparedQuery, boolean isResultSet) throws SQLException { + private void bindPreparedStatementForGuiMode(PreparedStatement preparedQuery, List bindParams) { + try { + for (int index = 0; index < bindParams.size(); index++) { + Object value = bindParams.get(index); + bindParam(index + 1, value, preparedQuery, ""); + } + } catch (Exception e) { + throw wrapException(PREPARED_STATEMENT_BIND_PARAMETERS_ERROR, "PREPARED_STATEMENT_BIND_PARAMETERS_ERROR", e); + } + } + private QueryExecutionResult parseExecuteResult(Statement statement, boolean isResultSet) + throws SQLException { + + List result = newArrayList(); + int updateCount = statement.getUpdateCount(); + do { + if (isResultSet) { + ResultSet rs = statement.getResultSet(); + ResultSetMetaData metaData = rs.getMetaData(); + int colCount = metaData.getColumnCount(); + List> dataRows = parseDataRows(rs, metaData, colCount); + if (!containsNullGeneratedKeys(dataRows)) { + result.add(dataRows); + } + } else { + Object affectedRows = Math.max(updateCount, 0); + result.add(Map.of("affectedRows", affectedRows)); + } - if (isResultSet) { - ResultSetMetaData metaData = resultSet.getMetaData(); - int colCount = metaData.getColumnCount(); - List> dataRows = parseDataRows(resultSet, metaData, colCount); + isResultSet = statement.getMoreResults(); + updateCount = statement.getUpdateCount(); + } while (isResultSet || updateCount != -1); - List columnLabels = ResultSetParser.parseColumns(metaData); - return QueryExecutionResult.success(dataRows, populateHintMessages(columnLabels)); + if (result.size() == 1) { + return QueryExecutionResult.success(result.get(0)); } - Object affectedRows = preparedStatement ? Math.max(preparedQuery.getUpdateCount(), 0) // might return -1 here - : Math.max(statement.getUpdateCount(), 0); - return QueryExecutionResult.success(Map.of("affectedRows", affectedRows)); + return QueryExecutionResult.success(result); + } + + private static boolean containsNullGeneratedKeys(List> dataRows) { + if (dataRows.size() != 1) { + return false; + } + Map map = dataRows.get(0); + if (map.size() == 1) { + return map.containsKey("GENERATED_KEYS"); + } + return false; } private List> parseDataRows(ResultSet resultSet, ResultSetMetaData metaData, int colCount) throws SQLException { @@ -190,55 +252,15 @@ private List> parseDataRows(ResultSet resultSet, ResultSetMe return result; } - private List populateHintMessages(List columnNames) { - List messages = new ArrayList<>(); - List identicalColumns = getIdenticalColumns(columnNames); - if (!org.springframework.util.CollectionUtils.isEmpty(identicalColumns)) { - messages.add(new LocaleMessage("DUPLICATE_COLUMN", String.join("/", identicalColumns))); - } - return messages; - } - private void bindPreparedStatementParams(PreparedStatement preparedStatement, List mustacheKeysInOrder, Map requestParams) { try { for (int index = 0; index < mustacheKeysInOrder.size(); index++) { - String mustacheKey = mustacheKeysInOrder.get(index); Object value = requestParams.get(mustacheKey); - int bindIndex = index + 1; - if (value == null) { - preparedStatement.setNull(bindIndex, Types.NULL); - continue; - } - if (value instanceof Integer intValue) { - preparedStatement.setInt(bindIndex, intValue); - continue; - } - if (value instanceof Long longValue) { - preparedStatement.setLong(bindIndex, longValue); - continue; - } - if (value instanceof Float || value instanceof Double) { - preparedStatement.setBigDecimal(bindIndex, new BigDecimal(String.valueOf(value))); - continue; - } - if (value instanceof Boolean boolValue) { - preparedStatement.setBoolean(bindIndex, boolValue); - continue; - } - if (value instanceof Map || value instanceof Collection) { - preparedStatement.setString(bindIndex, toJson(value)); - continue; - } - if (value instanceof String strValue) { - preparedStatement.setString(bindIndex, strValue); - continue; - } - throw new PluginException(PREPARED_STATEMENT_BIND_PARAMETERS_ERROR, "PS_BIND_ERROR", mustacheKey, - value.getClass().getSimpleName()); + bindParam(bindIndex, value, preparedStatement, mustacheKey); } } catch (Exception e) { if (e instanceof PluginException pluginException) { @@ -248,6 +270,40 @@ private void bindPreparedStatementParams(PreparedStatement preparedStatement, Li } } + private static void bindParam(int bindIndex, Object value, PreparedStatement preparedStatement, String mustacheKey) throws SQLException { + if (value == null) { + preparedStatement.setNull(bindIndex, Types.NULL); + return; + } + if (value instanceof Integer intValue) { + preparedStatement.setInt(bindIndex, intValue); + return; + } + if (value instanceof Long longValue) { + preparedStatement.setLong(bindIndex, longValue); + return; + } + if (value instanceof Float || value instanceof Double) { + preparedStatement.setBigDecimal(bindIndex, new BigDecimal(String.valueOf(value))); + return; + } + if (value instanceof Boolean boolValue) { + preparedStatement.setBoolean(bindIndex, boolValue); + return; + } + if (value instanceof Map || value instanceof Collection) { + preparedStatement.setString(bindIndex, toJson(value)); + return; + } + if (value instanceof String strValue) { + preparedStatement.setString(bindIndex, strValue); + return; + } + throw new PluginException(PREPARED_STATEMENT_BIND_PARAMETERS_ERROR, "PS_BIND_ERROR", + StringUtils.isBlank(mustacheKey) ? String.valueOf(value) : mustacheKey, + value.getClass().getSimpleName()); + } + private void releaseResources(AutoCloseable... autoCloseables) { for (AutoCloseable closeable : autoCloseables) { if (closeable != null) { @@ -270,4 +326,28 @@ private Connection getConnection(HikariDataSource hikariDataSource) { throw new PluginException(CONNECTION_ERROR, "CONNECTION_ERROR", e.getMessage()); } } + + private GuiSqlCommand getGuiSqlCommand(MssqlQueryConfig queryConfig) { + String guiStatementType = queryConfig.getGuiStatementType(); + if (StringUtils.isBlank(guiStatementType)) { + throw new PluginException(QUERY_ARGUMENT_ERROR, "GUI_COMMAND_TYPE_EMPTY"); + } + Map guiStatementDetail = queryConfig.getGuiStatementDetail(); + if (MapUtils.isEmpty(guiStatementDetail)) { + throw new PluginException(QUERY_ARGUMENT_ERROR, "INVALID_GUI_PARAM"); + } + + return parseSqlCommand(guiStatementType, guiStatementDetail); + } + + private GuiSqlCommand parseSqlCommand(String guiStatementType, Map detail) { + return switch (guiStatementType.toUpperCase()) { + case "INSERT" -> MssqlInsertCommand.from(detail); + case "UPDATE" -> MssqlUpdateCommand.from(detail); + case "DELETE" -> MssqlDeleteCommand.from(detail); + case "BULK_INSERT" -> MssqlBulkInsertCommand.from(detail); + case "BULK_UPDATE" -> MssqlBulkUpdateCommand.from(detail); + default -> throw new PluginException(QUERY_ARGUMENT_ERROR, "INVALID_GUI_COMMAND_TYPE", guiStatementType); + }; + } } diff --git a/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/GuiConstants.java b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/GuiConstants.java new file mode 100644 index 00000000..e9f07be4 --- /dev/null +++ b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/GuiConstants.java @@ -0,0 +1,8 @@ +package com.openblocks.plugin.mssql.gui; + +public final class GuiConstants { + + public static final String MSSQL_COLUMN_DELIMITER_FRONT = "["; + public static final String MSSQL_COLUMN_DELIMITER_BACK = "]"; + +} diff --git a/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlBulkInsertCommand.java b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlBulkInsertCommand.java new file mode 100644 index 00000000..6a8300b6 --- /dev/null +++ b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlBulkInsertCommand.java @@ -0,0 +1,24 @@ +package com.openblocks.plugin.mssql.gui; + +import static com.openblocks.plugin.mssql.gui.GuiConstants.MSSQL_COLUMN_DELIMITER_BACK; +import static com.openblocks.plugin.mssql.gui.GuiConstants.MSSQL_COLUMN_DELIMITER_FRONT; +import static com.openblocks.sdk.plugin.sqlcommand.changeset.BulkObjectChangeSet.parseBulkRecords; + +import java.util.Map; + +import com.openblocks.sdk.plugin.sqlcommand.GuiSqlCommand; +import com.openblocks.sdk.plugin.sqlcommand.changeset.BulkObjectChangeSet; +import com.openblocks.sdk.plugin.sqlcommand.command.BulkInsertCommand; + +public class MssqlBulkInsertCommand extends BulkInsertCommand { + protected MssqlBulkInsertCommand(String table, BulkObjectChangeSet bulkObjectChangeSet) { + super(table, bulkObjectChangeSet, MSSQL_COLUMN_DELIMITER_FRONT, MSSQL_COLUMN_DELIMITER_BACK); + } + + public static BulkInsertCommand from(Map commandDetail) { + String table = GuiSqlCommand.parseTable(commandDetail); + String recordStr = parseBulkRecords(commandDetail); + BulkObjectChangeSet bulkObjectChangeSet = new BulkObjectChangeSet(recordStr); + return new MssqlBulkInsertCommand(table, bulkObjectChangeSet); + } +} diff --git a/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlBulkUpdateCommand.java b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlBulkUpdateCommand.java new file mode 100644 index 00000000..43f85300 --- /dev/null +++ b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlBulkUpdateCommand.java @@ -0,0 +1,27 @@ +package com.openblocks.plugin.mssql.gui; + +import static com.openblocks.plugin.mssql.gui.GuiConstants.MSSQL_COLUMN_DELIMITER_BACK; +import static com.openblocks.plugin.mssql.gui.GuiConstants.MSSQL_COLUMN_DELIMITER_FRONT; +import static com.openblocks.sdk.plugin.sqlcommand.GuiSqlCommand.parseTable; +import static com.openblocks.sdk.plugin.sqlcommand.changeset.BulkObjectChangeSet.parseBulkRecords; +import static com.openblocks.sdk.plugin.sqlcommand.changeset.BulkObjectChangeSet.parsePrimaryKey; + +import java.util.Map; + +import com.openblocks.sdk.plugin.sqlcommand.changeset.BulkObjectChangeSet; +import com.openblocks.sdk.plugin.sqlcommand.command.BulkUpdateCommand; + +public class MssqlBulkUpdateCommand extends BulkUpdateCommand { + + protected MssqlBulkUpdateCommand(String table, BulkObjectChangeSet bulkObjectChangeSet, String primaryKey) { + super(table, bulkObjectChangeSet, primaryKey, MSSQL_COLUMN_DELIMITER_FRONT, MSSQL_COLUMN_DELIMITER_BACK); + } + + public static MssqlBulkUpdateCommand from(Map commandDetail) { + String table = parseTable(commandDetail); + String recordStr = parseBulkRecords(commandDetail); + BulkObjectChangeSet bulkObjectChangeSet = new BulkObjectChangeSet(recordStr); + return new MssqlBulkUpdateCommand(table, bulkObjectChangeSet, parsePrimaryKey(commandDetail)); + } + +} diff --git a/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlDeleteCommand.java b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlDeleteCommand.java new file mode 100644 index 00000000..da9a267c --- /dev/null +++ b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlDeleteCommand.java @@ -0,0 +1,44 @@ +package com.openblocks.plugin.mssql.gui; + +import static com.openblocks.plugin.mssql.gui.GuiConstants.MSSQL_COLUMN_DELIMITER_BACK; +import static com.openblocks.plugin.mssql.gui.GuiConstants.MSSQL_COLUMN_DELIMITER_FRONT; +import static com.openblocks.sdk.plugin.sqlcommand.filter.FilterSet.parseFilterSet; + +import java.util.Map; + +import com.openblocks.sdk.plugin.sqlcommand.GuiSqlCommand; +import com.openblocks.sdk.plugin.sqlcommand.command.DeleteCommand; +import com.openblocks.sdk.plugin.sqlcommand.filter.FilterSet; + +public class MssqlDeleteCommand extends DeleteCommand { + + protected MssqlDeleteCommand(String table, FilterSet filterSet, boolean allowMultiModify) { + super(table, filterSet, allowMultiModify, MSSQL_COLUMN_DELIMITER_FRONT, MSSQL_COLUMN_DELIMITER_BACK); + } + + public static DeleteCommand from(Map commandDetail) { + String table = GuiSqlCommand.parseTable(commandDetail); + FilterSet filterSet = parseFilterSet(commandDetail); + boolean allowMultiModify = GuiSqlCommand.parseAllowMultiModify(commandDetail); + return new MssqlDeleteCommand(table, filterSet, allowMultiModify); + } + + @Override + public GuiSqlCommandRenderResult render(Map requestMap) { + return super.render(requestMap); + } + + @Override + protected void renderTable(String renderedTable, StringBuilder sb) { + sb.append("delete "); + if (!allowMultiModify) { + sb.append("top (1) "); + } + sb.append("from ").append(renderedTable); + } + + @Override + protected void renderLimit(StringBuilder sb) { + // do nothing + } +} diff --git a/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/plugin/sqlcommand/command/mssql/MssqlInsertCommand.java b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlInsertCommand.java similarity index 77% rename from server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/plugin/sqlcommand/command/mssql/MssqlInsertCommand.java rename to server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlInsertCommand.java index 51022840..030744c9 100644 --- a/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/plugin/sqlcommand/command/mssql/MssqlInsertCommand.java +++ b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlInsertCommand.java @@ -1,4 +1,7 @@ -package com.openblocks.sdk.plugin.sqlcommand.command.mssql; +package com.openblocks.plugin.mssql.gui; + +import static com.openblocks.plugin.mssql.gui.GuiConstants.MSSQL_COLUMN_DELIMITER_BACK; +import static com.openblocks.plugin.mssql.gui.GuiConstants.MSSQL_COLUMN_DELIMITER_FRONT; import java.util.Map; @@ -8,9 +11,6 @@ public class MssqlInsertCommand extends InsertCommand { - private static final String MSSQL_COLUMN_DELIMITER_FRONT = "["; - private static final String MSSQL_COLUMN_DELIMITER_BACK = "]"; - private MssqlInsertCommand(Map commandDetail) { super(commandDetail, MSSQL_COLUMN_DELIMITER_FRONT, MSSQL_COLUMN_DELIMITER_BACK); } diff --git a/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlUpdateCommand.java b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlUpdateCommand.java new file mode 100644 index 00000000..1ac62c66 --- /dev/null +++ b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/gui/MssqlUpdateCommand.java @@ -0,0 +1,41 @@ +package com.openblocks.plugin.mssql.gui; + +import static com.openblocks.plugin.mssql.gui.GuiConstants.MSSQL_COLUMN_DELIMITER_BACK; +import static com.openblocks.plugin.mssql.gui.GuiConstants.MSSQL_COLUMN_DELIMITER_FRONT; + +import java.util.Map; + +import com.google.common.annotations.VisibleForTesting; +import com.openblocks.sdk.plugin.sqlcommand.changeset.ChangeSet; +import com.openblocks.sdk.plugin.sqlcommand.command.UpdateCommand; +import com.openblocks.sdk.plugin.sqlcommand.filter.FilterSet; + +public class MssqlUpdateCommand extends UpdateCommand { + + private MssqlUpdateCommand(Map commandDetail) { + super(commandDetail, MSSQL_COLUMN_DELIMITER_FRONT, MSSQL_COLUMN_DELIMITER_BACK); + } + + @VisibleForTesting + protected MssqlUpdateCommand(String table, ChangeSet changeSet, FilterSet filterSet, boolean allowMultiModify) { + super(table, changeSet, filterSet, allowMultiModify, MSSQL_COLUMN_DELIMITER_FRONT, MSSQL_COLUMN_DELIMITER_BACK); + } + + @Override + protected void appendTable(String renderedTable, StringBuilder sb) { + sb.append("update "); + if (!allowMultiModify) { + sb.append(" top (1) "); + } + sb.append(renderedTable); + } + + @Override + protected void appendLimit(StringBuilder sb) { + // do nothing + } + + public static MssqlUpdateCommand from(Map commandDetail) { + return new MssqlUpdateCommand(commandDetail); + } +} diff --git a/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/model/MssqlQueryConfig.java b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/model/MssqlQueryConfig.java index 5d9b7f9e..e6d2f0e3 100644 --- a/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/model/MssqlQueryConfig.java +++ b/server/api-service/openblocks-plugins/mssqlPlugin/src/main/java/com/openblocks/plugin/mssql/model/MssqlQueryConfig.java @@ -9,6 +9,7 @@ import org.apache.commons.collections4.MapUtils; import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; import com.openblocks.sdk.exception.PluginException; import lombok.Getter; @@ -17,11 +18,20 @@ public class MssqlQueryConfig { private final String sql; private final boolean disablePreparedStatement; + private final String mode; + private final String guiStatementType; + private final Map guiStatementDetail; @JsonCreator - private MssqlQueryConfig(String sql, boolean disablePreparedStatement) { + private MssqlQueryConfig(String sql, boolean disablePreparedStatement, + String mode, + @JsonProperty("commandType") String guiStatementType, + @JsonProperty("command") Map guiStatementDetail) { this.sql = sql; this.disablePreparedStatement = disablePreparedStatement; + this.mode = mode; + this.guiStatementType = guiStatementType; + this.guiStatementDetail = guiStatementDetail; } public static MssqlQueryConfig from(Map queryConfigs) { @@ -41,4 +51,8 @@ public String getSql() { return sql.trim(); } + public boolean isGuiMode() { + return "GUI".equalsIgnoreCase(mode); + } + } diff --git a/server/api-service/openblocks-plugins/mysqlPlugin/src/main/java/com/openblocks/plugin/mysql/MysqlQueryExecutor.java b/server/api-service/openblocks-plugins/mysqlPlugin/src/main/java/com/openblocks/plugin/mysql/MysqlQueryExecutor.java index 36b8748e..2c2ca3df 100644 --- a/server/api-service/openblocks-plugins/mysqlPlugin/src/main/java/com/openblocks/plugin/mysql/MysqlQueryExecutor.java +++ b/server/api-service/openblocks-plugins/mysqlPlugin/src/main/java/com/openblocks/plugin/mysql/MysqlQueryExecutor.java @@ -11,6 +11,7 @@ import static com.openblocks.sdk.exception.PluginCommonError.QUERY_EXECUTION_ERROR; import static com.openblocks.sdk.plugin.common.QueryExecutionUtils.getIdenticalColumns; import static com.openblocks.sdk.plugin.common.SqlQueryUtils.isInsertQuery; +import static com.openblocks.sdk.util.ExceptionUtils.wrapException; import static com.openblocks.sdk.util.JsonUtils.toJson; import static com.openblocks.sdk.util.MustacheHelper.doPrepareStatement; import static com.openblocks.sdk.util.MustacheHelper.extractMustacheKeysInOrder; @@ -36,6 +37,7 @@ import javax.annotation.Nonnull; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.MapUtils; import org.apache.commons.lang3.StringUtils; import org.pf4j.Extension; @@ -243,10 +245,7 @@ private void bindPreparedStatementForGuiMode(PreparedStatement preparedQuery, Li bindParam(index + 1, value, preparedQuery, ""); } } catch (Exception e) { - if (e instanceof PluginException pluginException) { - throw pluginException; - } - throw new PluginException(PREPARED_STATEMENT_BIND_PARAMETERS_ERROR, "PREPARED_STATEMENT_BIND_PARAMETERS_ERROR", e.getMessage()); + throw wrapException(PREPARED_STATEMENT_BIND_PARAMETERS_ERROR, "PREPARED_STATEMENT_BIND_PARAMETERS_ERROR", e); } } @@ -298,7 +297,7 @@ private List getGeneratedIds(ResultSet generatedKeys) throws SQLException private List populateHintMessages(List columnNames) { List messages = new ArrayList<>(); List identicalColumns = getIdenticalColumns(columnNames); - if (!org.springframework.util.CollectionUtils.isEmpty(identicalColumns)) { + if (!CollectionUtils.isEmpty(identicalColumns)) { messages.add(new LocaleMessage("DUPLICATE_COLUMN", String.join("/", identicalColumns))); } return messages; @@ -316,10 +315,7 @@ private void bindPreparedStatementParamsWithMustacheKeyValues(PreparedStatement bindParam(index + 1, value, preparedStatement, mustacheKey); } } catch (Exception e) { - if (e instanceof PluginException pluginException) { - throw pluginException; - } - throw new PluginException(PREPARED_STATEMENT_BIND_PARAMETERS_ERROR, "PREPARED_STATEMENT_BIND_PARAMETERS_ERROR", e.getMessage()); + throw wrapException(PREPARED_STATEMENT_BIND_PARAMETERS_ERROR, "PREPARED_STATEMENT_BIND_PARAMETERS_ERROR", e); } } @@ -353,7 +349,8 @@ private void bindParam(int bindIndex, Object value, PreparedStatement preparedSt return; } throw new PluginException(PREPARED_STATEMENT_BIND_PARAMETERS_ERROR, "PS_BIND_ERROR", - bindKeyName, value.getClass().getSimpleName()); + StringUtils.isBlank(bindKeyName) ? String.valueOf(value) : bindKeyName, + value.getClass().getSimpleName()); } private void releaseResources(AutoCloseable... autoCloseables) { diff --git a/server/api-service/openblocks-plugins/postgresPlugin/src/main/java/com/openblocks/plugin/postgres/PostgresExecutor.java b/server/api-service/openblocks-plugins/postgresPlugin/src/main/java/com/openblocks/plugin/postgres/PostgresExecutor.java index 596cd2c9..01e6cb4f 100644 --- a/server/api-service/openblocks-plugins/postgresPlugin/src/main/java/com/openblocks/plugin/postgres/PostgresExecutor.java +++ b/server/api-service/openblocks-plugins/postgresPlugin/src/main/java/com/openblocks/plugin/postgres/PostgresExecutor.java @@ -10,6 +10,7 @@ import static com.openblocks.sdk.plugin.common.QueryExecutionUtils.getIdenticalColumns; import static com.openblocks.sdk.util.JsonUtils.fromJsonList; import static com.openblocks.sdk.util.JsonUtils.toJson; +import static com.openblocks.sdk.util.MustacheHelper.doPrepareStatement; import static com.openblocks.sdk.util.MustacheHelper.renderMustacheString; import java.io.IOException; @@ -147,7 +148,7 @@ private QueryExecutionResult executeCommon(HikariDataSource hikariDataSource, bindPreparedStatementForGuiMode(preparedQuery, renderResult.bindParams()); } else { List mustacheValuesInOrder = MustacheHelper.extractMustacheKeysInOrder(rawQuery); - String updatedQuery = MustacheHelper.replaceMustacheWithQuestionMark(rawQuery, mustacheValuesInOrder); + var updatedQuery = doPrepareStatement(rawQuery, mustacheValuesInOrder, requestParams); List explicitCastDataTypes = PostgresDataTypeUtils.extractExplicitCasting(updatedQuery); preparedQuery = connection.prepareStatement(updatedQuery); @@ -297,7 +298,8 @@ private void bindParam(int bindIndex, Object value, PreparedStatement preparedSt return; } throw new PluginException(PREPARED_STATEMENT_BIND_PARAMETERS_ERROR, "PS_BIND_ERROR", - bindKeyName, value.getClass().getSimpleName()); + StringUtils.isBlank(bindKeyName) ? String.valueOf(value) : bindKeyName, + value.getClass().getSimpleName()); } private void releaseResources(Connection connectionFromPool, Statement statement, @@ -339,7 +341,7 @@ private void releaseResources(Connection connectionFromPool, Statement statement private List populateHintMessages(List columnNames) { List messages = new ArrayList<>(); List identicalColumns = getIdenticalColumns(columnNames); - if (!org.springframework.util.CollectionUtils.isEmpty(identicalColumns)) { + if (!CollectionUtils.isEmpty(identicalColumns)) { messages.add(new LocaleMessage("DUPLICATE_COLUMN", String.join("/", identicalColumns))); } return messages; diff --git a/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/plugin/common/QueryExecutionUtils.java b/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/plugin/common/QueryExecutionUtils.java index 2e1f263f..e07c5d89 100644 --- a/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/plugin/common/QueryExecutionUtils.java +++ b/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/plugin/common/QueryExecutionUtils.java @@ -8,7 +8,7 @@ import java.util.function.Function; import java.util.stream.Collectors; -import org.springframework.util.CollectionUtils; +import org.apache.commons.collections4.MapUtils; import reactor.core.scheduler.Scheduler; import reactor.core.scheduler.Schedulers; @@ -57,7 +57,7 @@ public static String getStringValueSafelyFromFormData(Map formDa @SuppressWarnings("unchecked") public static Object getValueSafelyFromFormData(Map formData, String field) { - if (CollectionUtils.isEmpty(formData)) { + if (MapUtils.isEmpty(formData)) { return null; } diff --git a/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/plugin/sqlcommand/command/DeleteCommand.java b/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/plugin/sqlcommand/command/DeleteCommand.java index 8e89ba88..526b1acd 100644 --- a/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/plugin/sqlcommand/command/DeleteCommand.java +++ b/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/plugin/sqlcommand/command/DeleteCommand.java @@ -31,21 +31,27 @@ public GuiSqlCommandRenderResult render(Map requestMap) { String renderedTable = MustacheHelper.renderMustacheString(table, requestMap); StringBuilder sb = new StringBuilder(); - sb.append("delete from ").append(renderedTable); + renderTable(renderedTable, sb); if (filterSet.isEmpty()) { - if (!allowMultiModify) { - sb.append(" limit 1"); - } + renderLimit(sb); return new GuiSqlCommandRenderResult(sb.toString(), Collections.emptyList()); } GuiSqlCommandRenderResult render = filterSet.render(requestMap, columnFrontDelimiter, columnBackDelimiter, isRenderWithRawSql(), escapeStrFunc()); sb.append(render.sql()); + renderLimit(sb); + return new GuiSqlCommandRenderResult(sb.toString(), render.bindParams()); + } + + protected void renderTable(String renderedTable, StringBuilder sb) { + sb.append("delete from ").append(renderedTable); + } + + protected void renderLimit(StringBuilder sb) { if (!allowMultiModify) { sb.append(" limit 1"); } - return new GuiSqlCommandRenderResult(sb.toString(), render.bindParams()); } @Override diff --git a/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/util/MustacheHelper.java b/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/util/MustacheHelper.java index 2cbbcf3b..a5dc28ae 100644 --- a/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/util/MustacheHelper.java +++ b/server/api-service/openblocks-sdk/src/main/java/com/openblocks/sdk/util/MustacheHelper.java @@ -38,7 +38,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -65,14 +64,6 @@ public final class MustacheHelper { private MustacheHelper() { } - /** - * find ? with quotes around '?' or "?" - */ - private static final String REGEX_QUOTES_TRIMMING = "([\"']\\?[\"'])"; - private static final Pattern QUOTE_QUESTION_PATTERN = Pattern.compile(REGEX_QUOTES_TRIMMING); - // The final replacement string of ? for replacing '?' or "?" - private static final String POST_QUOTE_TRIMMING_QUESTION_MARK = "\\?"; - private static final char SPECIAL_CHAR_4_PREPARED_STATEMENT = 16; private static final String SPECIAL_STRING_4_PREPARED_STATEMENT = SPECIAL_CHAR_4_PREPARED_STATEMENT + ""; @@ -338,28 +329,6 @@ public static String renderMustacheTokens(List tokens, Map pa } - public static String replaceMustacheWithQuestionMark(String query, List mustacheBindings) { - Map replaceParamsMap = mustacheBindings - .stream() - .collect(Collectors.toMap(Function.identity(), v -> "?", (a, b) -> b)); - String result = renderMustacheString(query, replaceParamsMap); - result = QUOTE_QUESTION_PATTERN.matcher(result).replaceAll(POST_QUOTE_TRIMMING_QUESTION_MARK); - return result; - } - - public static String replaceQuestionMarkWithDollarIndex(String query) { - AtomicInteger counter = new AtomicInteger(); - return query.chars() - .mapToObj(c -> { - if (c == '?') { - return "$" + counter.incrementAndGet(); - } - - return Character.toString(c); - }) - .collect(Collectors.joining()); - } - public static String replaceMustacheWithQuestionMarkMore(String query, List mustacheBindings, Map param) { Map replaceParamsMap = mustacheBindings.stream().collect(Collectors.toMap(Function.identity(), v -> SPECIAL_STRING_4_PREPARED_STATEMENT, (a, b) -> b)); diff --git a/server/api-service/openblocks-sdk/src/test/java/com/openblocks/sdk/plugin/sqlcommand/command/mssql/MssqlInsertCommandTest.java b/server/api-service/openblocks-sdk/src/test/java/com/openblocks/sdk/plugin/sqlcommand/command/mssql/MssqlInsertCommandTest.java deleted file mode 100644 index 44ddbc07..00000000 --- a/server/api-service/openblocks-sdk/src/test/java/com/openblocks/sdk/plugin/sqlcommand/command/mssql/MssqlInsertCommandTest.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.openblocks.sdk.plugin.sqlcommand.command.mssql; - -import static com.openblocks.sdk.util.JsonUtils.toJson; - -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.assertj.core.api.Assertions; -import org.junit.Assert; -import org.junit.Test; - -import com.openblocks.sdk.plugin.sqlcommand.GuiSqlCommand.GuiSqlCommandRenderResult; -import com.openblocks.sdk.plugin.sqlcommand.changeset.KeyValuePairChangeSet; - -public class MssqlInsertCommandTest { - @Test - public void testInsertCommand() { - - String id = "12312"; - String name = "jack"; - String email = "jack@gmail.com"; - Map infoMap = Map.of("age", 35, - "job", "sales"); - - LinkedHashMap treeMap = new LinkedHashMap<>(); - treeMap.put("id", id); - treeMap.put("name", name); - treeMap.put("email", "{{ email }}"); - treeMap.put("info", " {{ info }}"); - KeyValuePairChangeSet changeSet = KeyValuePairChangeSet.buildForTest(treeMap); - - MssqlInsertCommand insertCommand = new MssqlInsertCommand("user", changeSet); - - GuiSqlCommandRenderResult render = insertCommand.render(Map.of("email", email, "info", infoMap)); - - Assert.assertEquals("insert into user ([id],[name],[email],[info]) values (?,?,?,?);", render.sql()); - Assertions.assertThat(render.bindParams()).isEqualTo(List.of(Integer.parseInt(id), name, email, toJson(infoMap))); - - } -} \ No newline at end of file diff --git a/server/api-service/openblocks-sdk/src/test/java/com/openblocks/sdk/plugin/sqlcommand/command/postgres/PostgresCommandTest.java b/server/api-service/openblocks-sdk/src/test/java/com/openblocks/sdk/plugin/sqlcommand/command/postgres/PostgresCommandTest.java index 951310dd..e40404ca 100644 --- a/server/api-service/openblocks-sdk/src/test/java/com/openblocks/sdk/plugin/sqlcommand/command/postgres/PostgresCommandTest.java +++ b/server/api-service/openblocks-sdk/src/test/java/com/openblocks/sdk/plugin/sqlcommand/command/postgres/PostgresCommandTest.java @@ -2,7 +2,6 @@ import static com.openblocks.sdk.util.JsonUtils.toJson; -import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -11,22 +10,25 @@ import org.junit.Assert; import org.junit.Test; +import com.google.common.collect.ImmutableMap; import com.openblocks.sdk.plugin.sqlcommand.GuiSqlCommand.GuiSqlCommandRenderResult; import com.openblocks.sdk.plugin.sqlcommand.changeset.BulkObjectChangeSet; import com.openblocks.sdk.plugin.sqlcommand.changeset.KeyValuePairChangeSet; import com.openblocks.sdk.plugin.sqlcommand.command.UpdateOrDeleteSingleCommandResult; import com.openblocks.sdk.plugin.sqlcommand.filter.FilterSet; +import com.openblocks.sdk.util.SqlGuiUtils.GuiSqlValue.EscapeSql; public class PostgresCommandTest { + private final EscapeSql testEscaper = s -> "$$" + s + "$$"; + @Test public void testInsertCommand() { String id = "12312"; String name = "jack"; String email = "jack@gmail.com"; - Map infoMap = Map.of("age", 35, - "job", "sales"); + Map infoMap = ImmutableMap.of("age", 35, "job", "sales"); LinkedHashMap treeMap = new LinkedHashMap<>(); treeMap.put("id", id); @@ -35,13 +37,19 @@ public void testInsertCommand() { treeMap.put("info", " {{ info }}"); KeyValuePairChangeSet changeSet = KeyValuePairChangeSet.buildForTest(treeMap); - PostgresInsertCommand insertCommand = new PostgresInsertCommand("user", changeSet); + PostgresInsertCommand insertCommand = new PostgresInsertCommand("user", changeSet) { + @Override + public EscapeSql escapeStrFunc() { + return testEscaper; + } + }; GuiSqlCommandRenderResult render = insertCommand.render(Map.of("email", email, "info", infoMap)); Assert.assertEquals(""" - insert into user ("id","name","email","info") values (?,?,?,?);""", render.sql()); - Assertions.assertThat(render.bindParams()).isEqualTo(List.of(Integer.parseInt(id), name, email, toJson(infoMap))); + insert into user ("id","name","email","info") values (12312,$$jack$$,$$jack@gmail.com$$,$${"age":35,"job":"sales"}$$);""", + render.sql()); + Assertions.assertThat(render.bindParams()).isEqualTo(List.of()); } @@ -51,8 +59,7 @@ public void testUpdateCommand() { String id = "12312"; String name = "jack"; String email = "jack@gmail.com"; - Map infoMap = Map.of("age", 35, - "job", "sales"); + Map infoMap = ImmutableMap.of("age", 35, "job", "sales"); Map treeMap = new LinkedHashMap<>(); treeMap.put("id", id); @@ -67,23 +74,21 @@ public void testUpdateCommand() { filterSet.addCondition("id", "IN", "[1,2, 5]"); filterSet.addCondition("phone", "IS", "null"); - var command = new PostgresUpdateCommand("user", changeSet, filterSet, false); + var command = new PostgresUpdateCommand("user", changeSet, filterSet, false) { + @Override + public EscapeSql escapeStrFunc() { + return testEscaper; + } + }; var renderResult = command.render(Map.of("email", email, "info", infoMap)); String updateSql = renderResult.sql(); List updateBindParams = renderResult.bindParams(); Assert.assertEquals( """ - update user set "id"=?,"name"=?,"email"=?,"info"=? where "name" = ? and "status" > ? and "id" IN (1,2,5) and "phone" IS null\s""", + update user set "id"=12312,"name"=$$jack$$,"email"=$$jack@gmail.com$$,"info"=$${"age":35,"job":"sales"}$$ where "name" = $$jack$$ and "status" > 1 and "id" IN (1,2,5) and "phone" IS null\s""", updateSql); - List expectedUpdateBindParams = new ArrayList<>(); - expectedUpdateBindParams.add(Integer.parseInt(id)); - expectedUpdateBindParams.add(name); - expectedUpdateBindParams.add(email); - expectedUpdateBindParams.add(toJson(infoMap)); - expectedUpdateBindParams.add(name); - expectedUpdateBindParams.add(1); - Assertions.assertThat(updateBindParams).isEqualTo(expectedUpdateBindParams); + Assertions.assertThat(updateBindParams).isEqualTo(List.of()); Assert.assertTrue(renderResult instanceof UpdateOrDeleteSingleCommandResult); var pgUpdateRenderResult = (UpdateOrDeleteSingleCommandResult) renderResult; @@ -91,11 +96,9 @@ public void testUpdateCommand() { List selectBindParams = pgUpdateRenderResult.getSelectBindParams(); Assert.assertEquals(""" - select count(1) as count from user where "name" = ? and "status" > ? and "id" IN (1,2,5) and "phone" IS null\s""", selectQuery); - List expectedSelectBindParams = new ArrayList<>(); - expectedSelectBindParams.add(name); - expectedSelectBindParams.add(1); - Assertions.assertThat(selectBindParams).isEqualTo(expectedSelectBindParams); + select count(1) as count from user where "name" = $$jack$$ and "status" > 1 and "id" IN (1,2,5) and "phone" IS null\s""", + selectQuery); + Assertions.assertThat(selectBindParams).isEqualTo(List.of()); } @@ -110,13 +113,18 @@ public void testDeleteCommand() { filterSet.addCondition("id", "IN", "{{list}}"); filterSet.addCondition("phone", "IS", "null"); - var command = new PostgresDeleteCommand("user", filterSet, false); + var command = new PostgresDeleteCommand("user", filterSet, false) { + @Override + public EscapeSql escapeStrFunc() { + return testEscaper; + } + }; var renderResult = command.render(Map.of("list", List.of(1, 2, 3))); String sql = renderResult.sql(); Assert.assertEquals( """ - delete from user where "name" = ? and "status" > ? and "id" IN (1,2,3) and "phone" IS null\s""", + delete from user where "name" = $$jack$$ and "status" > 1 and "id" IN (1,2,3) and "phone" IS null\s""", sql); Assert.assertTrue(renderResult instanceof UpdateOrDeleteSingleCommandResult); @@ -125,11 +133,9 @@ public void testDeleteCommand() { List selectBindParams = pgUpdateRenderResult.getSelectBindParams(); Assert.assertEquals(""" - select count(1) as count from user where "name" = ? and "status" > ? and "id" IN (1,2,3) and "phone" IS null\s""", selectQuery); - List expectedSelectBindParams = new ArrayList<>(); - expectedSelectBindParams.add(name); - expectedSelectBindParams.add(1); - Assertions.assertThat(selectBindParams).isEqualTo(expectedSelectBindParams); + select count(1) as count from user where "name" = $$jack$$ and "status" > 1 and "id" IN (1,2,3) and "phone" IS null\s""", + selectQuery); + Assertions.assertThat(selectBindParams).isEqualTo(List.of()); } @@ -139,8 +145,7 @@ public void testBulkInsertCommand() { int id = 12312; String name = "jack"; String email = "jack@gmail.com"; - Map infoMap = Map.of("age", 35, - "job", "sales"); + Map infoMap = ImmutableMap.of("age", 35, "job", "sales"); Map treeMap = new LinkedHashMap<>(); treeMap.put("id", id); @@ -150,25 +155,20 @@ public void testBulkInsertCommand() { List> insertList = List.of(treeMap, treeMap); ; - var command = new PostgresBulkInsertCommand("user", new BulkObjectChangeSet(toJson(insertList))); + var command = new PostgresBulkInsertCommand("user", new BulkObjectChangeSet(toJson(insertList))) { + @Override + public EscapeSql escapeStrFunc() { + return testEscaper; + } + }; var render = command.render(Map.of("email", email, "info", infoMap)); Assert.assertEquals( - "insert into user (\"id\",\"name\",\"email\",\"info\") values (?,?,?,?),(?,?,?,?);", + """ + insert into user ("id","name","email","info") values (12312,$$jack$$,$$jack@gmail.com$$,$${"age":35,"job":"sales"}$$),(12312,$$jack$$,$$jack@gmail.com$$,$${"age":35,"job":"sales"}$$);""", render.sql()); - List expectedParams = new ArrayList<>(); - // insert - expectedParams.add(id); - expectedParams.add(name); - expectedParams.add(email); - expectedParams.add(toJson(infoMap)); - // update - expectedParams.add(id); - expectedParams.add(name); - expectedParams.add(email); - expectedParams.add(toJson(infoMap)); - Assertions.assertThat(render.bindParams()).isEqualTo(expectedParams); + Assertions.assertThat(render.bindParams()).isEqualTo(List.of()); } @@ -181,38 +181,28 @@ public void testBulkUpdateCommand() { ), Map.of("id", 2, "name", "rose", - "info", Map.of("age", 35, "job", "sales") + "info", ImmutableMap.of("age", 35, "job", "sales") ) ); var command = new PostgresBulkUpdateCommand("user", - new BulkObjectChangeSet(toJson(updateList)), "id"); + new BulkObjectChangeSet(toJson(updateList)), "id") { + @Override + public EscapeSql escapeStrFunc() { + return testEscaper; + } + }; var render = command.render(Map.of()); Assert.assertEquals( """ UPDATE user set - "name" = CASE WHEN "id" = ? THEN ? WHEN "id" = ? THEN ? END, - "email" = CASE WHEN "id" = ? THEN ? END, - "info" = CASE WHEN "id" = ? THEN ? END - where id in (?,?)""", + "name" = CASE WHEN "id" = 1 THEN $$jack$$ WHEN "id" = 2 THEN $$rose$$ ELSE "name" END, + "email" = CASE WHEN "id" = 1 THEN $$jack@jack.com$$ ELSE "email" END, + "info" = CASE WHEN "id" = 2 THEN $${"age":35,"job":"sales"}$$ ELSE "info" END + where id in (1,2)""", render.sql()); - - /* - [1, "jack", 2, "rose", 1, "jack@jack.com", 2, "{"job":"sales","age":35}", 1, 2] - */ - List expectedParams = new ArrayList<>(); - expectedParams.add(1); - expectedParams.add("jack"); - expectedParams.add(2); - expectedParams.add("rose"); - expectedParams.add(1); - expectedParams.add("jack@jack.com"); - expectedParams.add(2); - expectedParams.add(toJson(Map.of("age", 35, "job", "sales"))); - expectedParams.add(1); - expectedParams.add(2); - Assertions.assertThat(render.bindParams()).isEqualTo(expectedParams); + Assertions.assertThat(render.bindParams()).isEqualTo(List.of()); }