diff --git a/jdbc/src/main/java/tech/ydb/jdbc/query/QueryStatement.java b/jdbc/src/main/java/tech/ydb/jdbc/query/QueryStatement.java index 65af37a..8d31012 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/query/QueryStatement.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/query/QueryStatement.java @@ -1,9 +1,11 @@ package tech.ydb.jdbc.query; + import java.util.ArrayList; import java.util.List; -import tech.ydb.jdbc.common.TypeDescription; +import tech.ydb.jdbc.query.params.JdbcPrm; + /** * @@ -12,7 +14,7 @@ public class QueryStatement { private final QueryType queryType; private final QueryCmd command; - private final List parameters = new ArrayList<>(); + private final List parameters = new ArrayList<>(); private boolean hasReturinng = false; public QueryStatement(QueryType custom, QueryType baseType, QueryCmd command) { @@ -28,12 +30,16 @@ public QueryCmd getCmd() { return command; } - public List getParams() { + public boolean hasJdbcParameters() { + return !parameters.isEmpty(); + } + + public List getJdbcPrmFactories() { return parameters; } - public void addParameter(String name, TypeDescription type) { - this.parameters.add(new ParamDescription(name, type)); + public void addJdbcPrmFactory(JdbcPrm.Factory prm) { + this.parameters.add(prm); } public void setHasReturning(boolean hasReturning) { diff --git a/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQuery.java b/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQuery.java index ccc0fb8..35384f6 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQuery.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQuery.java @@ -27,11 +27,11 @@ public class YdbQuery { this.type = type; this.batcher = batcher; - boolean hasJdbcParamters = false; + boolean hasJdbcParameters = false; for (QueryStatement st: statements) { - hasJdbcParamters = hasJdbcParamters || !st.getParams().isEmpty(); + hasJdbcParameters = hasJdbcParameters || st.hasJdbcParameters(); } - this.isPlainYQL = !hasJdbcParamters; + this.isPlainYQL = !hasJdbcParameters; } public QueryType getType() { @@ -59,8 +59,10 @@ public List getStatements() { } public static YdbQuery parseQuery(String query, YdbQueryProperties opts) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(opts.isDetectQueryType(), opts.isDetectJdbcParameters()); - String preparedYQL = parser.parseSQL(query); + YdbQueryParser parser = new YdbQueryParser( + query, opts.isDetectQueryType(), opts.isDetectJdbcParameters(), opts.isReplaceJdbcInByYqlList() + ); + String preparedYQL = parser.parseSQL(); QueryType type = null; YqlBatcher batcher = parser.getYqlBatcher(); diff --git a/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQueryParser.java b/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQueryParser.java index ba5c215..8cde00c 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQueryParser.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/query/YdbQueryParser.java @@ -7,8 +7,7 @@ import java.util.List; import tech.ydb.jdbc.YdbConst; -import tech.ydb.jdbc.common.TypeDescription; -import tech.ydb.table.values.PrimitiveType; +import tech.ydb.jdbc.query.params.JdbcPrm; /** @@ -18,13 +17,21 @@ public class YdbQueryParser { private final boolean isDetectQueryType; private final boolean isDetectJdbcParameters; + private final boolean isConvertJdbcInToList; private final List statements = new ArrayList<>(); private final YqlBatcher batcher = new YqlBatcher(); + private final String origin; + private final StringBuilder parsed; - public YdbQueryParser(boolean isDetectQueryType, boolean isDetectJdbcParameters) { + private int jdbcPrmIndex = 0; + + public YdbQueryParser(String origin, boolean isDetectQueryType, boolean isDetectParameters, boolean isConvertIn) { this.isDetectQueryType = isDetectQueryType; - this.isDetectJdbcParameters = isDetectJdbcParameters; + this.isDetectJdbcParameters = isDetectParameters; + this.isConvertJdbcInToList = isConvertIn; + this.origin = origin; + this.parsed = new StringBuilder(origin.length() + 10); } public List getStatements() { @@ -56,7 +63,7 @@ public QueryType detectQueryType() throws SQLException { } @SuppressWarnings("MethodLength") - public String parseSQL(String origin) throws SQLException { + public String parseSQL() throws SQLException { int fragmentStart = 0; boolean detectJdbcArgs = false; @@ -65,16 +72,13 @@ public String parseSQL(String origin) throws SQLException { int parenLevel = 0; int keywordStart = -1; - boolean lastKeywordIsOffsetLimit = false; char[] chars = origin.toCharArray(); - StringBuilder parsed = new StringBuilder(origin.length() + 10); - ArgNameGenerator argNameGenerator = new ArgNameGenerator(); - for (int i = 0; i < chars.length; ++i) { char ch = chars[i]; boolean isInsideKeyword = false; + int keywordEnd = i; // parseSingleQuotes, parseDoubleQuotes, etc move index so we keep old value switch (ch) { case '\'': // single-quotes @@ -102,6 +106,7 @@ public String parseSQL(String origin) throws SQLException { case '/': // possibly /* */ style comment i = parseBlockComment(chars, i); break; + case '?': if (detectJdbcArgs && statement != null) { parsed.append(chars, fragmentStart, i - fragmentStart); @@ -110,22 +115,15 @@ public String parseSQL(String origin) throws SQLException { batcher.readIdentifier(chars, i, 1); i++; // make sure the coming ? is not treated as a bind } else { - String binded = argNameGenerator.createArgName(origin); - // force type UInt64 for OFFSET and LIMIT parameters - TypeDescription forcedType = lastKeywordIsOffsetLimit - ? TypeDescription.of(PrimitiveType.Uint64) - : null; - statement.addParameter(binded, forcedType); - parsed.append(binded); - + String name = nextJdbcPrmName(); + statement.addJdbcPrmFactory(JdbcPrm.simplePrm(name)); + parsed.append(name); batcher.readParameter(); } fragmentStart = i + 1; } break; default: - lastKeywordIsOffsetLimit = lastKeywordIsOffsetLimit && Character.isWhitespace(ch); - if (keywordStart >= 0) { isInsideKeyword = Character.isJavaIdentifierPart(ch); break; @@ -140,7 +138,6 @@ public String parseSQL(String origin) throws SQLException { if (keywordStart >= 0 && (!isInsideKeyword || (i == chars.length - 1))) { - lastKeywordIsOffsetLimit = false; int keywordLength = (isInsideKeyword ? i + 1 : keywordEnd) - keywordStart; if (statement != null) { @@ -151,9 +148,23 @@ public String parseSQL(String origin) throws SQLException { statement.setHasReturning(true); } - if (parseOffsetKeyword(chars, keywordStart, keywordLength) - || parseLimitKeyword(chars, keywordStart, keywordLength)) { - lastKeywordIsOffsetLimit = Character.isWhitespace(ch); + // Process ? after OFFSET and LIMIT + if (i < chars.length && detectJdbcArgs && Character.isWhitespace(ch)) { + if (parseOffsetKeyword(chars, keywordStart, keywordLength) + || parseLimitKeyword(chars, keywordStart, keywordLength)) { + parsed.append(chars, fragmentStart, i - fragmentStart); + i = parseOffsetLimitParameter(chars, i, statement); + fragmentStart = i; + } + } + + // Process IN (?, ?, ... ) + if (i < chars.length && detectJdbcArgs && isConvertJdbcInToList) { + if (parseInKeyword(chars, keywordStart, keywordLength)) { + parsed.append(chars, fragmentStart, i - fragmentStart); + i = parseInListParameters(chars, i, statement); + fragmentStart = i; + } } } else { boolean skipped = false; @@ -272,18 +283,102 @@ public String parseSQL(String origin) throws SQLException { return parsed.toString(); } - private static class ArgNameGenerator { - private int index = 0; + private String nextJdbcPrmName() { + while (true) { + jdbcPrmIndex += 1; + String name = YdbConst.AUTO_GENERATED_PARAMETER_PREFIX + jdbcPrmIndex; + if (!origin.contains(name)) { + return name; + } + } + } - public String createArgName(String origin) { - while (true) { - index += 1; - String name = YdbConst.AUTO_GENERATED_PARAMETER_PREFIX + index; - if (!origin.contains(name)) { - return name; - } + private int parseOffsetLimitParameter(char[] query, int offset, QueryStatement st) { + int start = offset; + while (++offset < query.length) { + char ch = query[offset]; + switch (ch) { + case '?' : + if (offset + 1 < query.length && query[offset + 1] == '?') { + return start; + } + String name = nextJdbcPrmName(); + parsed.append(query, start, offset - start); + parsed.append(name); + st.addJdbcPrmFactory(JdbcPrm.uint64Prm(name)); + return offset + 1; + case '-': // possibly -- style comment + offset = parseLineComment(query, offset); + break; + case '/': // possibly /* */ style comment + offset = parseBlockComment(query, offset); + break; + default: + if (!Character.isWhitespace(query[offset])) { + return start; + } + break; + } + } + + return start; + } + + private int parseInListParameters(char[] query, int offset, QueryStatement st) { + int start = offset; + int listStartedAt = -1; + int listSize = 0; + boolean waitPrm = false; + while (offset < query.length) { + char ch = query[offset]; + switch (ch) { + case '(': // start of list + if (listStartedAt >= 0) { + return start; + } + listStartedAt = offset; + waitPrm = true; + break; + case ',': + if (listStartedAt < 0 || waitPrm) { + return start; + } + waitPrm = true; + break; + case '?' : + if (!waitPrm || (offset + 1 < query.length && query[offset + 1] == '?')) { + return start; + } + listSize++; + waitPrm = false; + break; + case ')': + if (waitPrm || listSize == 0 || listStartedAt < 0) { + return start; + } + + String name = nextJdbcPrmName(); + parsed.append(query, start, listStartedAt - start); + parsed.append(' '); // add extra space to avoid IN$jpN + parsed.append(name); + st.addJdbcPrmFactory(JdbcPrm.inListOrm(name, listSize)); + return offset + 1; + case '-': // possibly -- style comment + offset = parseLineComment(query, offset); + break; + case '/': // possibly /* */ style comment + offset = parseBlockComment(query, offset); + break; + default: + if (!Character.isWhitespace(query[offset])) { + return start; + } + break; } + offset++; } + + return start; } private static int parseSingleQuotes(final char[] query, int offset) { @@ -553,4 +648,13 @@ private static boolean parseLimitKeyword(char[] query, int offset, int length) { && (query[offset + 3] | 32) == 'i' && (query[offset + 4] | 32) == 't'; } + + private static boolean parseInKeyword(char[] query, int offset, int length) { + if (length != 2) { + return false; + } + + return (query[offset] | 32) == 'i' + && (query[offset + 1] | 32) == 'n'; + } } diff --git a/jdbc/src/main/java/tech/ydb/jdbc/query/params/InListJdbcPrm.java b/jdbc/src/main/java/tech/ydb/jdbc/query/params/InListJdbcPrm.java new file mode 100644 index 0000000..15d7c04 --- /dev/null +++ b/jdbc/src/main/java/tech/ydb/jdbc/query/params/InListJdbcPrm.java @@ -0,0 +1,124 @@ +package tech.ydb.jdbc.query.params; + +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +import tech.ydb.jdbc.YdbConst; +import tech.ydb.jdbc.common.TypeDescription; +import tech.ydb.jdbc.impl.YdbTypes; +import tech.ydb.table.query.Params; +import tech.ydb.table.values.ListType; +import tech.ydb.table.values.Type; +import tech.ydb.table.values.Value; +import tech.ydb.table.values.VoidValue; + +/** + * + * @author Aleksandr Gorshenin + */ +public class InListJdbcPrm { + private static final Value NULL = VoidValue.of(); + private final String listName; + private final List items = new ArrayList<>(); + private TypeDescription type; + + public InListJdbcPrm(String listName, int listSize) { + this.listName = listName; + for (int idx = 0; idx < listSize; idx++) { + items.add(new Item(listName, idx)); + } + } + + public List toJdbcPrmList() { + return items; + } + + private Value buildList() throws SQLException { + if (type == null) { + throw new SQLException(YdbConst.PARAMETER_TYPE_UNKNOWN); + } + + boolean hasNull = false; + for (Item item: items) { + if (item.value == null) { + throw new SQLException(YdbConst.MISSING_VALUE_FOR_PARAMETER + item.name); + } + hasNull = hasNull || item.value == NULL; + } + + List> values = new ArrayList<>(); + if (!hasNull) { + for (Item item: items) { + values.add(item.value); + } + return ListType.of(type.ydbType()).newValue(values); + } + + for (Item item: items) { + if (item.value == NULL) { + values.add(type.nullValue()); + } else { + values.add(item.value.makeOptional()); + } + } + + return ListType.of(type.ydbType().makeOptional()).newValue(values); + + } + + private class Item implements JdbcPrm { + private final String name; + private final int index; + private Value value = null; + + Item(String listName, int index) { + this.name = listName + "[" + index + "]"; + this.index = index; + } + + @Override + public String getName() { + return name; + } + + @Override + public TypeDescription getType() { + return type; + } + + @Override + public void setValue(Object obj, int sqlType) throws SQLException { + if (type == null) { + Type ydbType = YdbTypes.findType(obj, sqlType); + if (ydbType == null) { + if (obj == null) { + value = NULL; + return; + } else { + throw new SQLException(String.format(YdbConst.PARAMETER_TYPE_UNKNOWN, sqlType, obj)); + } + } + + type = TypeDescription.of(ydbType); + } + + value = type.setters().toValue(obj); + } + + @Override + public void copyToParams(Params params) throws SQLException { + if (index == 0) { // first prm + params.put(listName, buildList()); + } + } + + @Override + public void reset() { + value = null; + if (index == items.size() - 1) { // last prm reset type + type = null; + } + } + } +} diff --git a/jdbc/src/main/java/tech/ydb/jdbc/query/params/InMemoryQuery.java b/jdbc/src/main/java/tech/ydb/jdbc/query/params/InMemoryQuery.java index c0aac7c..3a9e385 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/query/params/InMemoryQuery.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/query/params/InMemoryQuery.java @@ -2,24 +2,18 @@ -import java.sql.SQLDataException; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Function; -import java.util.stream.Collectors; import tech.ydb.jdbc.YdbConst; import tech.ydb.jdbc.common.TypeDescription; -import tech.ydb.jdbc.impl.YdbTypes; -import tech.ydb.jdbc.query.ParamDescription; +import tech.ydb.jdbc.query.QueryStatement; import tech.ydb.jdbc.query.YdbPreparedQuery; import tech.ydb.jdbc.query.YdbQuery; import tech.ydb.table.query.Params; -import tech.ydb.table.values.Type; -import tech.ydb.table.values.Value; /** @@ -29,23 +23,22 @@ public class InMemoryQuery implements YdbPreparedQuery { private final String yql; private final boolean isAutoDeclare; - private final ParamDescription[] parameters; - private final Map parametersByName; - private final Map> paramValues; - private final List batchList; + private final List parameters = new ArrayList<>(); + private final Map parametersByName = new HashMap<>(); + private final List batchList = new ArrayList<>(); public InMemoryQuery(YdbQuery query, boolean isAutoDeclare) { this.yql = query.getPreparedYql(); this.isAutoDeclare = isAutoDeclare; - this.parameters = query.getStatements().stream() - .flatMap(s -> s.getParams().stream()) - .toArray(ParamDescription[]::new); - this.parametersByName = query.getStatements().stream() - .flatMap(s -> s.getParams().stream()) - .collect(Collectors.toMap(ParamDescription::name, Function.identity())); - - this.paramValues = new HashMap<>(); - this.batchList = new ArrayList<>(); + + for (QueryStatement st: query.getStatements()) { + for (JdbcPrm.Factory factory: st.getJdbcPrmFactories()) { + for (JdbcPrm prm: factory.create()) { + parameters.add(prm); + parametersByName.put(prm.getName(), prm); + } + } + } } @Override @@ -55,19 +48,13 @@ public String getQueryText(Params prms) throws SQLException { } StringBuilder query = new StringBuilder(); - Map> values = prms.values(); - for (ParamDescription prm: parameters) { - if (!values.containsKey(prm.name())) { - throw new SQLDataException(YdbConst.MISSING_VALUE_FOR_PARAMETER + prm.name()); - } - - String prmType = values.get(prm.name()).getType().toString(); - query.append("DECLARE ") - .append(prm.name()) - .append(" AS ") - .append(prmType) - .append(";\n"); - } + prms.values().forEach((name, value) -> query + .append("DECLARE ") + .append(name) + .append(" AS ") + .append(value.getType().toString()) + .append(";\n") + ); query.append(yql); return query.toString(); @@ -75,7 +62,7 @@ public String getQueryText(Params prms) throws SQLException { @Override public int parametersCount() { - return paramValues.size(); + return parameters.size(); } @Override @@ -84,9 +71,13 @@ public int batchSize() { } @Override - public void addBatch() { - batchList.add(Params.copyOf(paramValues)); - paramValues.clear(); + public void addBatch() throws SQLException { + Params batch = Params.create(); + for (JdbcPrm prm: parameters) { + prm.copyToParams(batch); + prm.reset(); + } + batchList.add(batch); } @Override @@ -96,7 +87,9 @@ public void clearBatch() { @Override public void clearParameters() { - paramValues.clear(); + for (JdbcPrm prm: parameters) { + prm.reset(); + } } @Override @@ -105,69 +98,47 @@ public List getBatchParams() { } @Override - public Params getCurrentParams() { - return Params.copyOf(paramValues); + public Params getCurrentParams() throws SQLException { + Params current = Params.create(); + for (JdbcPrm prm: parameters) { + prm.copyToParams(current); + } + return current; } @Override public String getNameByIndex(int index) throws SQLException { - if (index <= 0 || index > parameters.length) { + if (index <= 0 || index > parameters.size()) { throw new SQLException(YdbConst.PARAMETER_NUMBER_NOT_FOUND + index); } - return parameters[index - 1].name(); + return parameters.get(index - 1).getName(); } @Override public TypeDescription getDescription(int index) throws SQLException { - if (index <= 0 || index > parameters.length) { + if (index <= 0 || index > parameters.size()) { throw new SQLException(YdbConst.PARAMETER_NUMBER_NOT_FOUND + index); } - ParamDescription p = parameters[index - 1]; - if (p.type() != null) { - return p.type(); - } - Value arg = paramValues.get(p.name()); - if (arg != null) { - return TypeDescription.of(arg.getType()); - } - - return null; + JdbcPrm p = parameters.get(index - 1); + return p.getType(); } @Override public void setParam(int index, Object obj, int sqlType) throws SQLException { - if (index <= 0 || index > parameters.length) { + if (index <= 0 || index > parameters.size()) { throw new SQLException(YdbConst.PARAMETER_NUMBER_NOT_FOUND + index); } - setParam(parameters[index - 1], obj, sqlType); + parameters.get(index - 1).setValue(obj, sqlType); } @Override public void setParam(String name, Object obj, int sqlType) throws SQLException { - ParamDescription param = parametersByName.get(name); + JdbcPrm param = parametersByName.get(name); if (param == null) { - param = new ParamDescription(name, null); - } - setParam(param, obj, sqlType); - } - - private void setParam(ParamDescription param, Object obj, int sqlType) throws SQLException { - if (obj instanceof Value) { - paramValues.put(param.name(), (Value) obj); - return; + throw new SQLException(YdbConst.PARAMETER_NUMBER_NOT_FOUND + name); } - - TypeDescription description = param.type(); - if (description == null) { - Type type = YdbTypes.findType(obj, sqlType); - if (type == null) { - throw new SQLException(String.format(YdbConst.PARAMETER_TYPE_UNKNOWN, sqlType, obj)); - } - description = TypeDescription.of(type); - } - - paramValues.put(param.name(), ValueFactory.readValue(param.name(), obj, description)); + param.setValue(obj, sqlType); } } diff --git a/jdbc/src/main/java/tech/ydb/jdbc/query/params/JdbcPrm.java b/jdbc/src/main/java/tech/ydb/jdbc/query/params/JdbcPrm.java new file mode 100644 index 0000000..7d10a53 --- /dev/null +++ b/jdbc/src/main/java/tech/ydb/jdbc/query/params/JdbcPrm.java @@ -0,0 +1,39 @@ +package tech.ydb.jdbc.query.params; + + +import java.sql.SQLException; +import java.util.Collections; +import java.util.List; + +import tech.ydb.jdbc.common.TypeDescription; +import tech.ydb.table.query.Params; + +/** + * + * @author Aleksandr Gorshenin + */ +public interface JdbcPrm { + String getName(); + TypeDescription getType(); + + void setValue(Object obj, int sqlType) throws SQLException; + void copyToParams(Params params) throws SQLException; + + void reset(); + + interface Factory { + List create(); + } + + static Factory simplePrm(String name) { + return () -> Collections.singletonList(new SimpleJdbcPrm(name)); + } + + static Factory uint64Prm(String name) { + return () -> Collections.singletonList(new UInt64JdbcPrm(name)); + } + + static Factory inListOrm(String name, int count) { + return () -> new InListJdbcPrm(name, count).toJdbcPrmList(); + } +} diff --git a/jdbc/src/main/java/tech/ydb/jdbc/query/params/SimpleJdbcPrm.java b/jdbc/src/main/java/tech/ydb/jdbc/query/params/SimpleJdbcPrm.java new file mode 100644 index 0000000..afd28d3 --- /dev/null +++ b/jdbc/src/main/java/tech/ydb/jdbc/query/params/SimpleJdbcPrm.java @@ -0,0 +1,69 @@ +package tech.ydb.jdbc.query.params; + +import java.sql.SQLException; + +import tech.ydb.jdbc.YdbConst; +import tech.ydb.jdbc.common.TypeDescription; +import tech.ydb.jdbc.impl.YdbTypes; +import tech.ydb.table.query.Params; +import tech.ydb.table.values.Type; +import tech.ydb.table.values.Value; + +/** + * + * @author Aleksandr Gorshenin + */ +class SimpleJdbcPrm implements JdbcPrm { + private final String name; + private Value value; + + SimpleJdbcPrm(String name) { + this.name = name; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void reset() { + value = null; + } + + @Override + public void copyToParams(Params params) throws SQLException { + if (value == null) { + throw new SQLException(YdbConst.MISSING_VALUE_FOR_PARAMETER + name); + } + params.put(name, value); + } + + @Override + public TypeDescription getType() { + if (value == null) { + return null; + } + return TypeDescription.of(value.getType()); + } + + @Override + public void setValue(Object obj, int sqlType) throws SQLException { + if (obj instanceof Value) { + value = (Value) obj; + return; + } + + Type type = YdbTypes.findType(obj, sqlType); + if (type == null) { + throw new SQLException(String.format(YdbConst.PARAMETER_TYPE_UNKNOWN, sqlType, obj)); + } + + if (obj == null) { + value = type.makeOptional().emptyValue(); + return; + } + + value = TypeDescription.of(type).setters().toValue(obj); + } +} diff --git a/jdbc/src/main/java/tech/ydb/jdbc/query/params/UInt64JdbcPrm.java b/jdbc/src/main/java/tech/ydb/jdbc/query/params/UInt64JdbcPrm.java new file mode 100644 index 0000000..628ba54 --- /dev/null +++ b/jdbc/src/main/java/tech/ydb/jdbc/query/params/UInt64JdbcPrm.java @@ -0,0 +1,52 @@ +package tech.ydb.jdbc.query.params; + +import java.sql.SQLException; + +import tech.ydb.jdbc.YdbConst; +import tech.ydb.jdbc.common.TypeDescription; +import tech.ydb.table.query.Params; +import tech.ydb.table.values.PrimitiveType; +import tech.ydb.table.values.Value; + +/** + * + * @author Aleksandr Gorshenin + */ +class UInt64JdbcPrm implements JdbcPrm { + private static final TypeDescription DESC = TypeDescription.of(PrimitiveType.Uint64); + private final String name; + private Value value; + + UInt64JdbcPrm(String name) { + this.name = name; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void reset() { + value = null; + } + + @Override + public void copyToParams(Params params) throws SQLException { + if (value == null) { + throw new SQLException(YdbConst.MISSING_VALUE_FOR_PARAMETER + name); + } + + params.put(name, value); + } + + @Override + public TypeDescription getType() { + return DESC; + } + + @Override + public void setValue(Object obj, int sqlType) throws SQLException { + value = DESC.setters().toValue(obj); + } +} diff --git a/jdbc/src/main/java/tech/ydb/jdbc/settings/YdbConfig.java b/jdbc/src/main/java/tech/ydb/jdbc/settings/YdbConfig.java index 5643233..ee7ae8f 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/settings/YdbConfig.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/settings/YdbConfig.java @@ -227,6 +227,7 @@ public DriverPropertyInfo[] toPropertyInfo() throws SQLException { YdbQueryProperties.DISABLE_DETECT_SQL_OPERATIONS.toInfo(properties), YdbQueryProperties.DISABLE_JDBC_PARAMETERS.toInfo(properties), YdbQueryProperties.DISABLE_JDBC_PARAMETERS_DECLARE.toInfo(properties), + YdbQueryProperties.REPLACE_JDBC_IN_BY_YQL_LIST.toInfo(properties), YdbQueryProperties.REPLACE_INSERT_TO_UPSERT.toInfo(properties), YdbQueryProperties.FORCE_BULK_UPSERT.toInfo(properties), diff --git a/jdbc/src/main/java/tech/ydb/jdbc/settings/YdbQueryProperties.java b/jdbc/src/main/java/tech/ydb/jdbc/settings/YdbQueryProperties.java index 78d5107..496f5be 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/settings/YdbQueryProperties.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/settings/YdbQueryProperties.java @@ -28,9 +28,13 @@ public class YdbQueryProperties { static final YdbProperty DISABLE_JDBC_PARAMETERS = YdbProperty.bool("disableJdbcParameters", "Disable auto detect JDBC standart parameters '?'", false); + static final YdbProperty REPLACE_JDBC_IN_BY_YQL_LIST = YdbProperty.bool("replaceJdbcInByYqlList", + "Convert SQL operation IN (?, ?, ... ,?) to YQL operation IN $list", true); + static final YdbProperty DISABLE_JDBC_PARAMETERS_DECLARE = YdbProperty.bool("disableJdbcParameterDeclare", "Disable enforce DECLARE section for JDBC parameters '?'", false); + @Deprecated private static final YdbProperty FORCE_QUERY_MODE = YdbProperty.enums("forceQueryMode", QueryType.class, "Force usage one of query modes (DATA_QUERY, SCAN_QUERY, SCHEME_QUERY or EXPLAIN_QUERY) for all statements" @@ -50,6 +54,7 @@ public class YdbQueryProperties { private final boolean isDetectQueryType; private final boolean isDetectJdbcParameters; + private final boolean isReplaceJdbcInToYqlList; private final boolean isDeclareJdbcParameters; private final boolean isPrepareDataQueries; @@ -68,6 +73,7 @@ public YdbQueryProperties(YdbConfig config) throws SQLException { this.isPrepareDataQueries = !disablePrepareDataQueries; this.isDetectBatchQueries = !disablePrepareDataQueries && !disableAutoPreparedBatches; + boolean replaceJdbcInByYqlList = REPLACE_JDBC_IN_BY_YQL_LIST.readValue(props).getValue(); boolean disableJdbcParametersDeclare = DISABLE_JDBC_PARAMETERS_DECLARE.readValue(props).getValue(); boolean disableJdbcParametersParse = DISABLE_JDBC_PARAMETERS.readValue(props).getValue(); boolean disableSqlOperationsDetect = DISABLE_DETECT_SQL_OPERATIONS.readValue(props).getValue(); @@ -76,6 +82,8 @@ public YdbQueryProperties(YdbConfig config) throws SQLException { this.isDetectJdbcParameters = !disableSqlOperationsDetect && !disableJdbcParametersParse; this.isDeclareJdbcParameters = !disableSqlOperationsDetect && !disableJdbcParametersParse && !disableJdbcParametersDeclare; + this.isReplaceJdbcInToYqlList = !disableSqlOperationsDetect && !disableJdbcParametersParse + && replaceJdbcInByYqlList; YdbValue forcedType = FORCE_QUERY_MODE.readValue(props); @@ -109,6 +117,10 @@ public boolean isDeclareJdbcParameters() { return isDeclareJdbcParameters; } + public boolean isReplaceJdbcInByYqlList() { + return isReplaceJdbcInToYqlList; + } + public boolean isPrepareDataQueries() { return isPrepareDataQueries; } diff --git a/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbPreparedStatementTest.java b/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbPreparedStatementTest.java index 6423c74..74d5faf 100644 --- a/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbPreparedStatementTest.java +++ b/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbPreparedStatementTest.java @@ -2,6 +2,7 @@ import java.math.BigDecimal; import java.math.BigInteger; +import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -29,6 +30,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; +import org.junit.jupiter.params.provider.ValueSource; import tech.ydb.jdbc.YdbConst; import tech.ydb.jdbc.impl.helper.ExceptionAssert; @@ -519,6 +521,91 @@ public void tableRowTest() throws SQLException { } }; + private void assertResultSetCount(ResultSet rs, int count) throws SQLException { + Assertions.assertTrue(rs.next()); + Assertions.assertEquals(count, rs.getInt(1)); + Assertions.assertFalse(rs.next()); + rs.close(); + } + + @ParameterizedTest + @ValueSource(strings = { "true", "false" }) + public void inListTest(boolean convertInToList) throws SQLException { + String option = String.valueOf(convertInToList); + String arg2Name = convertInToList ? "$jp1[1]" : "$jp2"; + try (Connection conn = jdbc.createCustomConnection("replaceJdbcInByYqlList", option)) { + String upsert = TEST_TABLE.upsertOne(SqlQueries.JdbcQuery.STANDARD, "c_Text", "Text"); + String selectByIds = TEST_TABLE.withTableName("select count(*) from #tableName where key in (?, ?)"); + String selectByValue = TEST_TABLE.withTableName("select count(*) from #tableName where c_Text in (?, ?)"); + + try (PreparedStatement ps = conn.prepareStatement(upsert)) { + ps.setInt(1, 1); + ps.setString(2, "1"); + ps.addBatch(); + + ps.setInt(1, 2); + ps.setString(2, null); + ps.addBatch(); + + ps.setInt(1, 3); + ps.setString(2, "3"); + ps.addBatch(); + + ps.executeBatch(); + } + + try (PreparedStatement ps = conn.prepareStatement(selectByIds)) { + ps.setInt(1, 1); + ExceptionAssert.sqlException("Missing value for parameter: " + arg2Name, ps::executeQuery); + + ps.setInt(1, 1); + ps.setInt(2, 2); + assertResultSetCount(ps.executeQuery(), 2); + + ps.setInt(1, 1); + ps.setInt(2, 4); + assertResultSetCount(ps.executeQuery(), 1); + + ps.setInt(1, 1); + if (convertInToList) { + ExceptionAssert.sqlException("Cannot cast [class java.lang.String: text] to [Int32]", () -> { + ps.setString(2, "text"); + }); + } else { + ExceptionAssert.ydbException("Can't compare Optional with Utf8 (S_ERROR)", () -> { + ps.setString(2, "text"); + ps.executeQuery(); + }); + } + } + + try (PreparedStatement ps = conn.prepareStatement(selectByValue)) { + ps.setString(1, null); + ExceptionAssert.sqlException("Missing value for parameter: " + arg2Name, ps::executeQuery); + + ps.setString(1, null); + ps.setString(2, null); + assertResultSetCount(ps.executeQuery(), 0); + + ps.setString(1, "1"); + ps.setString(2, null); + assertResultSetCount(ps.executeQuery(), 1); + + ps.setString(1, "1"); + ps.setString(2, "1"); + assertResultSetCount(ps.executeQuery(), 1); + + ps.setString(1, "1"); + ps.setString(2, "2"); + assertResultSetCount(ps.executeQuery(), 1); + + ps.setString(1, "1"); + ps.setString(2, "3"); + assertResultSetCount(ps.executeQuery(), 2); + } + } + } + @ParameterizedTest(name = "with {0}") @EnumSource(SqlQueries.JdbcQuery.class) public void int32Test(SqlQueries.JdbcQuery query) throws SQLException { diff --git a/jdbc/src/test/java/tech/ydb/jdbc/query/YdbQueryParserTest.java b/jdbc/src/test/java/tech/ydb/jdbc/query/YdbQueryParserTest.java index 8b48c8f..c0cf533 100644 --- a/jdbc/src/test/java/tech/ydb/jdbc/query/YdbQueryParserTest.java +++ b/jdbc/src/test/java/tech/ydb/jdbc/query/YdbQueryParserTest.java @@ -9,6 +9,7 @@ import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; +import tech.ydb.jdbc.query.params.JdbcPrm; import tech.ydb.table.values.PrimitiveType; @@ -25,9 +26,9 @@ public class YdbQueryParserTest { "SCAN;~;", "BULK;~;", }, delimiter = '~') - public void emptyQueryTest(String sql, String prepared) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - String parsed = parser.parseSQL(sql); + public void emptyQueryTest(String query, String prepared) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + String parsed = parser.parseSQL(); Assertions.assertEquals(prepared, parsed); Assertions.assertEquals(0, parser.getStatements().size()); } @@ -40,9 +41,9 @@ public void emptyQueryTest(String sql, String prepared) throws SQLException { "'\texPlain\nupsert to', '\t\nupsert to', INSERT_UPSERT", "'EXPLAIN/*comment*/UPSERT INTO', '/*comment*/UPSERT INTO', INSERT_UPSERT", }) - public void explainQueryTest(String sql, String prepared, String cmd) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - String parsed = parser.parseSQL(sql); + public void explainQueryTest(String query, String prepared, String cmd) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + String parsed = parser.parseSQL(); Assertions.assertEquals(prepared, parsed); @@ -59,11 +60,11 @@ public void explainQueryTest(String sql, String prepared, String cmd) throws SQL "drOp table 'test'", "-- comment \nCreate;", }) - public void schemeQueryTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - String parsed = parser.parseSQL(sql); + public void schemeQueryTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + String parsed = parser.parseSQL(); - Assertions.assertEquals(sql, parsed); + Assertions.assertEquals(query, parsed); Assertions.assertEquals(1, parser.getStatements().size()); QueryStatement statement = parser.getStatements().get(0); @@ -79,11 +80,11 @@ public void schemeQueryTest(String sql) throws SQLException { "\ndrops;", "BuLK_INSERT;", }) - public void unknownQueryTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - String parsed = parser.parseSQL(sql); + public void unknownQueryTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + String parsed = parser.parseSQL(); - Assertions.assertEquals(sql, parsed); + Assertions.assertEquals(query, parsed); Assertions.assertEquals(1, parser.getStatements().size()); QueryStatement statement = parser.getStatements().get(0); @@ -94,8 +95,8 @@ public void unknownQueryTest(String sql) throws SQLException { @Test public void wrongSqlCommandTest() throws SQLException { String query = "SC;"; - YdbQueryParser parser = new YdbQueryParser(true, true); - String parsed = parser.parseSQL(query); + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + String parsed = parser.parseSQL(); Assertions.assertEquals(query, parsed); Assertions.assertEquals(1, parser.getStatements().size()); @@ -109,8 +110,8 @@ public void yqlUpsertTest() throws SQLException { + "declare $p2 as Text;\n" + "upsert into tableName (key, c_Text) values ($p1, $p2)"; - YdbQueryParser parser = new YdbQueryParser(true, true); - String parsed = parser.parseSQL(query); + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + String parsed = parser.parseSQL(); Assertions.assertEquals(query, parsed); Assertions.assertEquals(3, parser.getStatements().size()); @@ -125,8 +126,8 @@ public void yqlSelectWithKeyTest() throws SQLException { + "declare $key as Optional;\n" + "select key, column from tableName where key=$key"; - YdbQueryParser parser = new YdbQueryParser(true, true); - String parsed = parser.parseSQL(query); + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + String parsed = parser.parseSQL(); Assertions.assertEquals(query, parsed); Assertions.assertEquals(2, parser.getStatements().size()); @@ -142,8 +143,8 @@ public void scanSelectWithKeyTest() throws SQLException { + "declare $key as Optional;\n" + "scan select key, column from tableName where key=$key"; - YdbQueryParser parser = new YdbQueryParser(true, true); - String parsed = parser.parseSQL(query); + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + String parsed = parser.parseSQL(); Assertions.assertEquals("declare $key as Optional;\n" + " select key, column from tableName where key=$key", parsed); @@ -154,28 +155,57 @@ public void scanSelectWithKeyTest() throws SQLException { Assertions.assertEquals(QueryType.SCAN_QUERY, parser.detectQueryType()); } + @ParameterizedTest(name = "[{index}] {0} jdbc arguments") + @CsvSource(value = { + "select * from test_table where col = ?" + + "@select * from test_table where col = $jp1", + "select * from test_table where col = ? and c1 = \'$jp1\'" + + "@select * from test_table where col = $jp2 and c1 = \'$jp1\'", + "select * from test_table where col = ? and c1 = \'\\\\$jp1" + + "@select * from test_table where col = $jp2 and c1 = \'\\\\$jp1", + }, delimiter = '@') + public void simpleJdbcArgTest(String query, String parsed) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + Assertions.assertEquals(parsed, parser.parseSQL()); + Assertions.assertEquals(1, parser.getStatements().size()); + QueryStatement statement = parser.getStatements().get(0); + Assertions.assertEquals(QueryType.DATA_QUERY, statement.getType()); + Assertions.assertTrue(statement.hasJdbcParameters()); + + int prmCount = 0; + for (JdbcPrm.Factory factory : statement.getJdbcPrmFactories()) { + prmCount += factory.create().size(); + } + Assertions.assertEquals(1, prmCount); + } + @ParameterizedTest(name = "[{index}] {0} has offset or limit parameter") - @ValueSource(strings = { - "select * from test_table where true=true -- test request\noffset /* comment */ ? limit 20", - "select * from test_table where true=true /*comm*/offset ?\tlimit\t\n?;", - "select offset, limit from test_table offset 20 limit -- comment\n?;", - }) - public void offsetParameterTest(String query) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(query); + @CsvSource(value = { + "'select * from test_table where true=true -- test request\noffset /* comment */ ? limit 20'" + + "@'select * from test_table where true=true -- test request\noffset /* comment */ $jp1 limit 20'", + "'select * from test_table where true=true /*comm*/offset ?\tlimit\t\n?'" + + "@'select * from test_table where true=true /*comm*/offset $jp1\tlimit\t\n$jp2'", + "'select offset, limit from test_table offset 20 limit -- comment\n?;'" + + "@'select offset, limit from test_table offset 20 limit -- comment\n$jp1;'", + }, delimiter = '@') + public void offsetParameterTest(String query, String parsed) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + Assertions.assertEquals(parsed, parser.parseSQL()); Assertions.assertEquals(1, parser.getStatements().size()); QueryStatement statement = parser.getStatements().get(0); Assertions.assertEquals(QueryType.DATA_QUERY, statement.getType()); - Assertions.assertFalse(statement.getParams().isEmpty()); + Assertions.assertTrue(statement.hasJdbcParameters()); int idx = 0; - for (ParamDescription prm : statement.getParams()) { - idx++; - Assertions.assertEquals("$jp" + idx, prm.name()); - Assertions.assertNotNull(prm.type()); // forced UInt64 type - Assertions.assertEquals(PrimitiveType.Uint64, prm.type().ydbType()); // forced UInt64 type + for (JdbcPrm.Factory factory : statement.getJdbcPrmFactories()) { + for (JdbcPrm prm: factory.create()) { + idx++; + Assertions.assertEquals("$jp" + idx, prm.getName()); + Assertions.assertNotNull(prm.getType()); // forced UInt64 type + Assertions.assertEquals(PrimitiveType.Uint64, prm.getType().ydbType()); // forced UInt64 type + } } } @@ -184,24 +214,123 @@ public void offsetParameterTest(String query) throws SQLException { "select * from test_table where limit = ? or offset = ?", "update test_table set limit = ?, offset = ? where id = ?", "select * from test_table where limit=? or offset=?", + "select * from test_table where limit ??", + "select * from test_table where limit", + "select * from test_table where limit ", "update test_table set limit=?, offset=? where id=?", "select * from test_table where limit? or offset?", + "select * from test_table where limit-?", + "select * from test_table where limit/?", }) public void noOffsetParameterTest(String query) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(query); + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); + + Assertions.assertEquals(1, parser.getStatements().size()); + + QueryStatement statement = parser.getStatements().get(0); + Assertions.assertEquals(QueryType.DATA_QUERY, statement.getType()); + + int idx = 0; + for (JdbcPrm.Factory factory : statement.getJdbcPrmFactories()) { + for (JdbcPrm prm: factory.create()) { + idx++; + Assertions.assertEquals("$jp" + idx, prm.getName()); + Assertions.assertNull(prm.getType()); // uknown type + } + } + } + + @ParameterizedTest(name = "[{index}] {0} has in list parameter") + @CsvSource(value = { + "'select * from test_table where id in (?)'" + + "@'select * from test_table where id in $jp1'", + "'select * from test_table where id in (?,\n?, ?, \t?)'" + + "@'select * from test_table where id in $jp1'", + "'select * from test_table where id In(?--comment\n,?,?/**other /** inner */ comment*/)'" + + "@'select * from test_table where id In $jp1'", + }, delimiter = '@') + public void inListParameterTest(String query, String parsed) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + Assertions.assertEquals(parsed, parser.parseSQL()); + + Assertions.assertEquals(1, parser.getStatements().size()); + + QueryStatement statement = parser.getStatements().get(0); + Assertions.assertEquals(QueryType.DATA_QUERY, statement.getType()); + + Assertions.assertTrue(statement.hasJdbcParameters()); + int idx = 0; + for (JdbcPrm.Factory factory : statement.getJdbcPrmFactories()) { + for (JdbcPrm prm: factory.create()) { + Assertions.assertEquals("$jp1[" + idx + "]", prm.getName()); + idx++; + } + } + } + + @ParameterizedTest(name = "[{index}] {0} has in list parameter") + @CsvSource(value = { + "'select * from test_table where id in (?)'" + + "@'select * from test_table where id in ($jp1)'", + "'select * from test_table where id in (?,\n?, ?, \t?)'" + + "@'select * from test_table where id in ($jp1,\n$jp2, $jp3, \t$jp4)'", + "'select * from test_table where id In(?--comment\n,?,?/**other /** inner */ comment*/)'" + + "@'select * from test_table where id In($jp1--comment\n,$jp2,$jp3/**other /** inner */ comment*/)'", + }, delimiter = '@') + public void disabledInListParameterTest(String query, String parsed) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, false); + Assertions.assertEquals(parsed, parser.parseSQL()); + + Assertions.assertEquals(1, parser.getStatements().size()); + + QueryStatement statement = parser.getStatements().get(0); + Assertions.assertEquals(QueryType.DATA_QUERY, statement.getType()); + + Assertions.assertTrue(statement.hasJdbcParameters()); + int idx = 0; + for (JdbcPrm.Factory factory : statement.getJdbcPrmFactories()) { + for (JdbcPrm prm: factory.create()) { + idx++; + Assertions.assertEquals("$jp" + idx, prm.getName()); + } + } + } + + @ParameterizedTest(name = "[{index}] {0} has in list parameter") + @CsvSource(value = { + "'select * from test_table where id in (?, 1, ?)'" + + "@'select * from test_table where id in ($jp1, 1, $jp2)'", + "'select * from test_table where id in (??, ?)'" + + "@'select * from test_table where id in (?, $jp1)'", + "'select * from test_table where id in()'" + + "@'select * from test_table where id in()'", + "'select * from test_table where id in(?, ?, ?,)'" + + "@'select * from test_table where id in($jp1, $jp2, $jp3,)'", + "'select * from test_table where id in(?, ?, ?'" + + "@'select * from test_table where id in($jp1, $jp2, $jp3'", + "'select * from test_table where id in ?, ?, ?'" + + "@'select * from test_table where id in $jp1, $jp2, $jp3'", + "'select * from test_table where id in ((?))'" + + "@'select * from test_table where id in (($jp1))'", + "'select * from test_table where id in ,?)'" + + "@'select * from test_table where id in ,$jp1)'", + }, delimiter = '@') + public void wrongInListParameterTest(String query, String parsed) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + Assertions.assertEquals(parsed, parser.parseSQL()); Assertions.assertEquals(1, parser.getStatements().size()); QueryStatement statement = parser.getStatements().get(0); Assertions.assertEquals(QueryType.DATA_QUERY, statement.getType()); - Assertions.assertFalse(statement.getParams().isEmpty()); int idx = 0; - for (ParamDescription prm : statement.getParams()) { - idx++; - Assertions.assertEquals("$jp" + idx, prm.name()); - Assertions.assertNull(prm.type()); // uknown type + for (JdbcPrm.Factory factory : statement.getJdbcPrmFactories()) { + for (JdbcPrm prm: factory.create()) { + idx++; + Assertions.assertEquals("$jp" + idx, prm.getName()); + } } } @@ -212,9 +341,9 @@ public void noOffsetParameterTest(String query) throws SQLException { "/* comment */ Insert into `table_name` (`c1`, /* commect */ c2, c3)values(?, ? , ?);\n-- post comment", ";;Insert into table_name (`c1`, /* comment */ c2, c3 ) values(?, ? , ?);;", }) - public void batchedInsertTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void batchedInsertTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); Assertions.assertEquals(1, parser.getStatements().size()); Assertions.assertEquals(QueryType.DATA_QUERY, parser.getStatements().get(0).getType()); @@ -238,9 +367,9 @@ public void batchedInsertTest(String sql) throws SQLException { "/* comment */ Insert into `one_column` (`c1`/* commect */)values(?);\n-- post comment", ";;Insert\tinto\tone_column(`c1`) values(\t\n?);;;", }) - public void batchedInsertOneColumnTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void batchedInsertOneColumnTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); Assertions.assertEquals(1, parser.getStatements().size()); Assertions.assertEquals(QueryType.DATA_QUERY, parser.getStatements().get(0).getType()); @@ -264,9 +393,9 @@ public void batchedInsertOneColumnTest(String sql) throws SQLException { "/* comment */ Upsert into `table_name` (`c1`, /* commect */ c2, c3)values(?, ? , ?);\n-- post comment", ";;Upsert/* comment */into table_name (`c1`, /* comment */ c2, c3 ) values(?, ? , ?);", }) - public void batchedUpsertTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void batchedUpsertTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); Assertions.assertEquals(1, parser.getStatements().size()); Assertions.assertEquals(QueryType.DATA_QUERY, parser.getStatements().get(0).getType()); @@ -290,9 +419,9 @@ public void batchedUpsertTest(String sql) throws SQLException { "/* comment */ Upsert into `one_column` (`c1`/* commect */)values(?);\n-- post comment", ";;Upsert\tinto\tone_column(`c1`) values(\t\n?);;;", }) - public void batchedUpsertOneColumnTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void batchedUpsertOneColumnTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); Assertions.assertEquals(1, parser.getStatements().size()); Assertions.assertEquals(QueryType.DATA_QUERY, parser.getStatements().get(0).getType()); @@ -316,9 +445,9 @@ public void batchedUpsertOneColumnTest(String sql) throws SQLException { "/* comment */ Replace into `table_name` (`c1`, /* commect */ c2, c3)values(?, ? , ?);\n-- post comment", ";;Replace/* comment */into table_name (`c1`, /* comment */ c2, c3 ) values(?, ? , ?);", }) - public void batchedReplaceTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void batchedReplaceTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); Assertions.assertEquals(1, parser.getStatements().size()); Assertions.assertEquals(QueryType.DATA_QUERY, parser.getStatements().get(0).getType()); @@ -342,9 +471,9 @@ public void batchedReplaceTest(String sql) throws SQLException { "/* comment */ Replace into `one_column` (`c1`/* commect */)values(?);\n-- post comment", ";;Replace\tinto\tone_column(`c1`) values(\t\n?);;;", }) - public void batchedReplaceOneColumnTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void batchedReplaceOneColumnTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); Assertions.assertEquals(1, parser.getStatements().size()); Assertions.assertEquals(QueryType.DATA_QUERY, parser.getStatements().get(0).getType()); @@ -368,9 +497,9 @@ public void batchedReplaceOneColumnTest(String sql) throws SQLException { "/* comment */ upDaTe `table_name` set `c1` /* commect */ = ?, c2 = \n?, c3 = ? WHERE k1=? AND k2=?;;\n-- com", ";;UPDATE/* comment */table_name set `c1`= ?, c2 = ?, c3 = ? WHERE k1\n=\t?--comment\nAND k2=?", }) - public void batchedUpdateTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void batchedUpdateTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); Assertions.assertEquals(1, parser.getStatements().size()); Assertions.assertEquals(QueryType.DATA_QUERY, parser.getStatements().get(0).getType()); @@ -394,9 +523,9 @@ public void batchedUpdateTest(String sql) throws SQLException { "/* comment */ upDaTe `one_column` set `c1` /* commect */ = ? WHERE k1=?;;\n-- com", ";;UPDATE/* comment */one_column set `c1`= ? WHERE k1=--comment\n?", }) - public void batchedUpdateOneColumnTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void batchedUpdateOneColumnTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); Assertions.assertEquals(1, parser.getStatements().size()); Assertions.assertEquals(QueryType.DATA_QUERY, parser.getStatements().get(0).getType()); @@ -420,9 +549,9 @@ public void batchedUpdateOneColumnTest(String sql) throws SQLException { "/* comment */ deLete from `table_name` WHERE k1=? AND k2=?;;\n-- com", ";;DELETE/* comment */FRom table_name WHERE k1\n=\t?--comment\nAND k2=?", }) - public void batchedDeleteTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void batchedDeleteTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); Assertions.assertEquals(1, parser.getStatements().size()); Assertions.assertEquals(QueryType.DATA_QUERY, parser.getStatements().get(0).getType()); @@ -445,9 +574,9 @@ public void batchedDeleteTest(String sql) throws SQLException { "/* comment */ deLeTe FrOm `one_column` WHERE k1=?;;\n-- com", ";;DELETE/* comment */FROM--\none_column WHERE k1=--comment\n?", }) - public void batchedDeleteOneColumnTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void batchedDeleteOneColumnTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); Assertions.assertEquals(1, parser.getStatements().size()); Assertions.assertEquals(QueryType.DATA_QUERY, parser.getStatements().get(0).getType()); @@ -488,9 +617,9 @@ public void batchedDeleteOneColumnTest(String sql) throws SQLException { "upsert into table_name (c1, c2, c3) values (?, ?, ?) returning c1, c2, c3;", "upsert into table_name (c1, c2, c3) values (?, ?, ?); select 1;", }) - public void notBatchedTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void notBatchedTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); YqlBatcher batch = parser.getYqlBatcher(); Assertions.assertFalse(batch.isValidBatch()); @@ -503,9 +632,9 @@ public void notBatchedTest(String sql) throws SQLException { "--comment1\nbulk/* comment */Insert into `table_name` (`c1`, /* commect */ c2, c3)values(?, ? , ?);", ";;BUlk Insert into table_name (`c1`, /* comment */ c2, c3 ) values(?, ? , ?);", }) - public void validBulkInsertTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void validBulkInsertTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); Assertions.assertEquals(1, parser.getStatements().size()); Assertions.assertEquals(QueryType.BULK_QUERY, parser.getStatements().get(0).getType()); @@ -527,9 +656,9 @@ public void validBulkInsertTest(String sql) throws SQLException { "/* test */Bulk/* comment */ Upsert into `table_name` (`c1`, c2, c3)values(?, ? , ?);\n-- post comment", ";;Bulk Upsert/* comment */into table_name (`c1`, /* comment */ c2, c3 ) values(?, ? , ?);", }) - public void validBulkUpsertTest(String sql) throws SQLException { - YdbQueryParser parser = new YdbQueryParser(true, true); - parser.parseSQL(sql); + public void validBulkUpsertTest(String query) throws SQLException { + YdbQueryParser parser = new YdbQueryParser(query, true, true, true); + parser.parseSQL(); Assertions.assertEquals(1, parser.getStatements().size()); Assertions.assertEquals(QueryType.BULK_QUERY, parser.getStatements().get(0).getType()); diff --git a/jdbc/src/test/java/tech/ydb/jdbc/settings/YdbDriverProperitesTest.java b/jdbc/src/test/java/tech/ydb/jdbc/settings/YdbDriverProperitesTest.java index 464f5d6..8de9f26 100644 --- a/jdbc/src/test/java/tech/ydb/jdbc/settings/YdbDriverProperitesTest.java +++ b/jdbc/src/test/java/tech/ydb/jdbc/settings/YdbDriverProperitesTest.java @@ -338,6 +338,7 @@ static DriverPropertyInfo[] defaultPropertyInfo(@Nullable String localDatacenter new DriverPropertyInfo("disableDetectSqlOperations", "false"), new DriverPropertyInfo("disableJdbcParameters", "false"), new DriverPropertyInfo("disableJdbcParameterDeclare", "false"), + new DriverPropertyInfo("replaceJdbcInByYqlList", "true"), new DriverPropertyInfo("replaceInsertByUpsert", "false"), new DriverPropertyInfo("forceBulkUpsert", "false"), new DriverPropertyInfo("forceScanSelect", "false"), @@ -381,6 +382,7 @@ static DriverPropertyInfo[] customizedPropertyInfo() { new DriverPropertyInfo("disableDetectSqlOperations", "true"), new DriverPropertyInfo("disableJdbcParameters", "true"), new DriverPropertyInfo("disableJdbcParameterDeclare", "true"), + new DriverPropertyInfo("replaceJdbcInByYqlList", "false"), new DriverPropertyInfo("replaceInsertByUpsert", "true"), new DriverPropertyInfo("forceBulkUpsert", "true"), new DriverPropertyInfo("forceScanSelect", "true"),