diff --git a/jdbc/src/main/java/tech/ydb/jdbc/YdbConnection.java b/jdbc/src/main/java/tech/ydb/jdbc/YdbConnection.java index c4eb182..7ee1610 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/YdbConnection.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/YdbConnection.java @@ -9,6 +9,7 @@ import tech.ydb.jdbc.context.YdbContext; import tech.ydb.jdbc.context.YdbValidator; import tech.ydb.jdbc.query.ExplainedQuery; +import tech.ydb.jdbc.query.YdbQuery; import tech.ydb.table.query.Params; import tech.ydb.table.result.ResultSetReader; @@ -36,7 +37,8 @@ public interface YdbConnection extends Connection { /** * Explicitly execute query as a data query * - * @param yql query to execute + * @param query query to execute + * @param yql YQL text to execute * @param params parameters for query * @param timeout timeout of operation * @param keepInCache flag to store query in server-side cache @@ -44,19 +46,21 @@ public interface YdbConnection extends Connection { * @return list of result set * @throws SQLException if query cannot be executed */ - List executeDataQuery(String yql, YdbValidator validator, + List executeDataQuery(YdbQuery query, String yql, YdbValidator validator, int timeout, boolean keepInCache, Params params) throws SQLException; /** * Explicitly execute query as a scan query * - * @param yql query to execute + * @param query query to execute + * @param yql YQL text to execute * @param params parameters for query * @param validator handler for logging and warnings * @return single result set with rows * @throws SQLException if query cannot be executed */ - ResultSetReader executeScanQuery(String yql, YdbValidator validator, Params params) throws SQLException; + ResultSetReader executeScanQuery(YdbQuery query, String yql, YdbValidator validator, Params params) + throws SQLException; /** * Explicitly explain this query diff --git a/jdbc/src/main/java/tech/ydb/jdbc/context/BaseYdbExecutor.java b/jdbc/src/main/java/tech/ydb/jdbc/context/BaseYdbExecutor.java index e4a6c34..25a15eb 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/context/BaseYdbExecutor.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/context/BaseYdbExecutor.java @@ -9,6 +9,7 @@ import tech.ydb.core.UnexpectedResultException; import tech.ydb.jdbc.exception.ExceptionFactory; import tech.ydb.jdbc.query.QueryType; +import tech.ydb.jdbc.query.YdbQuery; import tech.ydb.table.Session; import tech.ydb.table.TableClient; import tech.ydb.table.query.Params; @@ -50,8 +51,9 @@ public void executeSchemeQuery(YdbContext ctx, YdbValidator validator, String yq } @Override - public ResultSetReader executeScanQuery(YdbContext ctx, YdbValidator validator, String yql, Params params) - throws SQLException { + public ResultSetReader executeScanQuery( + YdbContext ctx, YdbValidator validator, YdbQuery query, String yql, Params params + ) throws SQLException { ensureOpened(); Collection resultSets = new LinkedBlockingQueue<>(); @@ -59,6 +61,8 @@ public ResultSetReader executeScanQuery(YdbContext ctx, YdbValidator validator, ExecuteScanQuerySettings settings = ExecuteScanQuerySettings.newBuilder() .withRequestTimeout(scanQueryTimeout) .build(); + + ctx.traceQuery(query, yql); try (Session session = createNewTableSession(validator)) { validator.execute(QueryType.SCAN_QUERY + " >>\n" + yql, () -> session.executeScanQuery(yql, params, settings).start(resultSets::add)); diff --git a/jdbc/src/main/java/tech/ydb/jdbc/context/QueryServiceExecutor.java b/jdbc/src/main/java/tech/ydb/jdbc/context/QueryServiceExecutor.java index 6e88996..9da1cb4 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/context/QueryServiceExecutor.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/context/QueryServiceExecutor.java @@ -17,6 +17,7 @@ import tech.ydb.jdbc.exception.ExceptionFactory; import tech.ydb.jdbc.query.ExplainedQuery; import tech.ydb.jdbc.query.QueryType; +import tech.ydb.jdbc.query.YdbQuery; import tech.ydb.query.QueryClient; import tech.ydb.query.QuerySession; import tech.ydb.query.QueryStream; @@ -196,7 +197,8 @@ public void rollback(YdbContext ctx, YdbValidator validator) throws SQLException @Override public List executeDataQuery( - YdbContext ctx, YdbValidator validator, String yql, long timeout, boolean keepInCache, Params params + YdbContext ctx, YdbValidator validator, YdbQuery query, + String yql, long timeout, boolean keepInCache, Params params ) throws SQLException { ensureOpened(); diff --git a/jdbc/src/main/java/tech/ydb/jdbc/context/QueryStat.java b/jdbc/src/main/java/tech/ydb/jdbc/context/QueryStat.java index 235286f..ccdc15e 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/context/QueryStat.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/context/QueryStat.java @@ -5,7 +5,6 @@ import tech.ydb.core.Status; import tech.ydb.jdbc.common.FixedResultSetFactory; -import tech.ydb.jdbc.query.YdbQuery; import tech.ydb.table.result.ResultSetReader; /** @@ -13,11 +12,13 @@ * @author Aleksandr Gorshenin */ public class QueryStat { - public static final String QUERY = "print_jdbc_stats();"; + private static final String PRINT_QUERY = "print_jdbc_stats();"; + private static final String RESET_QUERY = "reset_jdbc_stats();"; private static final FixedResultSetFactory STATS_RS_FACTORY = FixedResultSetFactory.newBuilder() .addTextColumn("sql") .addBooleanColumn("is_fullscan") + .addBooleanColumn("is_error") .addLongColumn("executed") .addTextColumn("yql") .addTextColumn("ast") @@ -31,23 +32,26 @@ public class QueryStat { private final String plan; private final LongAdder usage; private final boolean isFullScan; + private final boolean isError; - public QueryStat(YdbQuery query, String ast, String plan) { - this.originSQL = query.getOriginQuery(); - this.preparedYQL = query.getPreparedYql(); + public QueryStat(String sql, String yql, String ast, String plan) { + this.originSQL = sql; + this.preparedYQL = yql; this.ast = ast; this.plan = plan; this.usage = new LongAdder(); this.isFullScan = plan.contains("\"Node Type\":\"TableFullScan\""); + this.isError = false; } - public QueryStat(YdbQuery query, Status error) { - this.originSQL = query.getOriginQuery(); - this.preparedYQL = query.getPreparedYql(); - this.ast = error.toString(); + public QueryStat(String sql, String yql, Status error) { + this.originSQL = sql; + this.preparedYQL = yql; + this.ast = null; this.plan = error.toString(); this.usage = new LongAdder(); this.isFullScan = false; + this.isError = true; } public long getUsageCounter() { @@ -74,6 +78,10 @@ public boolean isFullScan() { return isFullScan; } + public boolean isError() { + return isError; + } + public void incrementUsage() { this.usage.increment(); } @@ -84,6 +92,7 @@ public static ResultSetReader toResultSetReader(Collection stats) { builder.newRow() .withTextValue("sql", stat.originSQL) .withBoolValue("is_fullscan", stat.isFullScan) + .withBoolValue("is_error", stat.isError) .withLongValue("executed", stat.usage.longValue()) .withTextValue("yql", stat.preparedYQL) .withTextValue("ast", stat.ast) @@ -92,4 +101,12 @@ public static ResultSetReader toResultSetReader(Collection stats) { } return builder.build(); } + + public static boolean isPrint(String sql) { + return sql != null && PRINT_QUERY.equalsIgnoreCase(sql.trim()); + } + + public static boolean isReset(String sql) { + return sql != null && RESET_QUERY.equalsIgnoreCase(sql.trim()); + } } diff --git a/jdbc/src/main/java/tech/ydb/jdbc/context/TableServiceExecutor.java b/jdbc/src/main/java/tech/ydb/jdbc/context/TableServiceExecutor.java index 258a73a..616045d 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/context/TableServiceExecutor.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/context/TableServiceExecutor.java @@ -10,6 +10,7 @@ import tech.ydb.jdbc.YdbConst; import tech.ydb.jdbc.query.ExplainedQuery; import tech.ydb.jdbc.query.QueryType; +import tech.ydb.jdbc.query.YdbQuery; import tech.ydb.table.Session; import tech.ydb.table.query.DataQueryResult; import tech.ydb.table.query.ExplainDataQueryResult; @@ -168,7 +169,8 @@ public ExplainedQuery executeExplainQuery(YdbContext ctx, YdbValidator validator @Override public List executeDataQuery( - YdbContext ctx, YdbValidator validator, String yql, long timeout, boolean keepInCache, Params params + YdbContext ctx, YdbValidator validator, YdbQuery query, + String yql, long timeout, boolean keepInCache, Params params ) throws SQLException { ensureOpened(); diff --git a/jdbc/src/main/java/tech/ydb/jdbc/context/YdbContext.java b/jdbc/src/main/java/tech/ydb/jdbc/context/YdbContext.java index 8293f11..ad5c9fb 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/context/YdbContext.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/context/YdbContext.java @@ -3,12 +3,12 @@ import java.sql.SQLDataException; import java.sql.SQLException; import java.time.Duration; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.TreeSet; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; @@ -75,7 +75,7 @@ public class YdbContext implements AutoCloseable { private final SessionRetryContext retryCtx; private final Cache queriesCache; - private final Cache queryStatesCache; + private final Cache statsCache; private final Cache> queryParamsCache; private final boolean autoResizeSessionPool; @@ -107,13 +107,13 @@ private YdbContext( queriesCache = CacheBuilder.newBuilder().maximumSize(cacheSize).build(); queryParamsCache = CacheBuilder.newBuilder().maximumSize(cacheSize).build(); if (config.isFullScanDetectorEnabled()) { - queryStatesCache = CacheBuilder.newBuilder().maximumSize(cacheSize).build(); + statsCache = CacheBuilder.newBuilder().maximumSize(cacheSize).build(); } else { - queryStatesCache = null; + statsCache = null; } } else { queriesCache = null; - queryStatesCache = null; + statsCache = null; queryParamsCache = null; } } @@ -184,25 +184,26 @@ public boolean hasConnections() { } public boolean queryStatsEnabled() { - return queryStatesCache != null; + return statsCache != null; } - public Collection getQueryStats() { - if (queryStatesCache == null) { - return Collections.emptyList(); + public void resetQueryStats() { + if (statsCache != null) { + statsCache.invalidateAll(); } - Set sortedByUsage = new TreeSet<>(Comparator.comparingLong(QueryStat::getUsageCounter).reversed()); - sortedByUsage.addAll(queryStatesCache.asMap().values()); - return sortedByUsage; } - public void traceQueryExecution(YdbQuery query) { - if (queryStatesCache != null) { - QueryStat stat = queryStatesCache.getIfPresent(query.getOriginQuery()); - if (stat != null) { - stat.incrementUsage(); - } + public Collection getQueryStats() { + if (statsCache == null) { + return Collections.emptyList(); } + List sorted = new ArrayList<>(statsCache.asMap().values()); + Collections.sort(sorted, + Comparator + .comparingLong(QueryStat::getUsageCounter).reversed() + .thenComparing(QueryStat::getPreparedYQL) + ); + return sorted; } public void register() { @@ -308,29 +309,42 @@ public YdbQuery findOrParseYdbQuery(String sql) throws SQLException { queriesCache.put(sql, cached); } - if (queryStatesCache != null) { - QueryStat stat = queryStatesCache.getIfPresent(sql); - if (stat == null) { - final String preparedYQL = cached.getPreparedYql(); - final ExplainDataQuerySettings settings = withDefaultTimeout(new ExplainDataQuerySettings()); - Result res = retryCtx.supplyResult( - session -> session.explainDataQuery(preparedYQL, settings) - ).join(); - if (res.isSuccess()) { - ExplainDataQueryResult exp = res.getValue(); - stat = new QueryStat(cached, exp.getQueryAst(), exp.getQueryPlan()); - } else { - stat = new QueryStat(cached, res.getStatus()); - } - queryStatesCache.put(sql, stat); + return cached; + } + + public void traceQuery(YdbQuery query, String yql) { + if (statsCache == null) { + return; + } + + QueryStat stat = statsCache.getIfPresent(yql); + if (stat == null) { + final ExplainDataQuerySettings settings = withDefaultTimeout(new ExplainDataQuerySettings()); + Result res = retryCtx.supplyResult( + session -> session.explainDataQuery(yql, settings) + ).join(); + + if (res.isSuccess()) { + ExplainDataQueryResult exp = res.getValue(); + stat = new QueryStat(query.getOriginQuery(), yql, exp.getQueryAst(), exp.getQueryPlan()); + } else { + stat = new QueryStat(query.getOriginQuery(), yql, res.getStatus()); } + + statsCache.put(yql, stat); } - return cached; + stat.incrementUsage(); } public YdbPreparedQuery findOrPrepareParams(YdbQuery query, YdbPrepareMode mode) throws SQLException { + if (statsCache != null) { + if (QueryStat.isPrint(query.getOriginQuery()) || QueryStat.isReset(query.getOriginQuery())) { + return new InMemoryQuery(query, queryOptions.isDeclareJdbcParameters()); + } + } + if (query.getYqlBatcher() != null && mode == YdbPrepareMode.AUTO) { Map types = queryParamsCache.getIfPresent(query.getOriginQuery()); if (types == null) { diff --git a/jdbc/src/main/java/tech/ydb/jdbc/context/YdbExecutor.java b/jdbc/src/main/java/tech/ydb/jdbc/context/YdbExecutor.java index cf7e87a..65f2b54 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/context/YdbExecutor.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/context/YdbExecutor.java @@ -5,6 +5,7 @@ import tech.ydb.jdbc.YdbConst; import tech.ydb.jdbc.query.ExplainedQuery; +import tech.ydb.jdbc.query.YdbQuery; import tech.ydb.table.query.Params; import tech.ydb.table.result.ResultSetReader; @@ -34,10 +35,10 @@ default void ensureOpened() throws SQLException { void executeSchemeQuery(YdbContext ctx, YdbValidator validator, String yql) throws SQLException; - List executeDataQuery(YdbContext ctx, YdbValidator validator, String yql, + List executeDataQuery(YdbContext ctx, YdbValidator validator, YdbQuery query, String yql, long timeout, boolean poolable, Params params) throws SQLException; - ResultSetReader executeScanQuery(YdbContext ctx, YdbValidator validator, String yql, Params params) + ResultSetReader executeScanQuery(YdbContext ctx, YdbValidator validator, YdbQuery query, String yql, Params params) throws SQLException; ExplainedQuery executeExplainQuery(YdbContext ctx, YdbValidator validator, String yql) diff --git a/jdbc/src/main/java/tech/ydb/jdbc/impl/BaseYdbStatement.java b/jdbc/src/main/java/tech/ydb/jdbc/impl/BaseYdbStatement.java index 39d4f86..0f91cd3 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/impl/BaseYdbStatement.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/impl/BaseYdbStatement.java @@ -16,6 +16,8 @@ import tech.ydb.jdbc.YdbResultSet; import tech.ydb.jdbc.YdbStatement; import tech.ydb.jdbc.common.FixedResultSetFactory; +import tech.ydb.jdbc.context.QueryStat; +import tech.ydb.jdbc.context.YdbContext; import tech.ydb.jdbc.context.YdbValidator; import tech.ydb.jdbc.query.ExplainedQuery; import tech.ydb.jdbc.query.QueryStatement; @@ -189,14 +191,29 @@ protected List executeExplainQuery(YdbQuery query) throws SQLExceptio return Collections.singletonList(new YdbResult(new YdbResultSetImpl(this, result))); } - protected List executeScanQuery(String yql, Params params) throws SQLException { - ResultSetReader result = connection.executeScanQuery(yql, validator, params); + protected List executeScanQuery(YdbQuery query, String yql, Params params) throws SQLException { + connection.getCtx().traceQuery(query, yql); + ResultSetReader result = connection.executeScanQuery(query, yql, validator, params); return Collections.singletonList(new YdbResult(new YdbResultSetImpl(this, result))); } protected List executeDataQuery(YdbQuery query, String yql, Params params) throws SQLException { + YdbContext ctx = connection.getCtx(); + + if (ctx.queryStatsEnabled()) { + if (QueryStat.isPrint(yql)) { + YdbResultSet rs = new YdbResultSetImpl(this, QueryStat.toResultSetReader(ctx.getQueryStats())); + return Collections.singletonList(new YdbResult(rs)); + } + if (QueryStat.isReset(yql)) { + getConnection().getCtx().resetQueryStats(); + return null; + } + } + + ctx.traceQuery(query, yql); List resultSets = connection - .executeDataQuery(yql, validator, getQueryTimeout(), isPoolable(), params); + .executeDataQuery(query, yql, validator, getQueryTimeout(), isPoolable(), params); List results = new ArrayList<>(); int idx = 0; diff --git a/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbConnectionImpl.java b/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbConnectionImpl.java index 8c09eb3..fd755e7 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbConnectionImpl.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbConnectionImpl.java @@ -205,13 +205,14 @@ public void executeSchemeQuery(String yql, YdbValidator validator) throws SQLExc } @Override - public List executeDataQuery(String yql, YdbValidator validator, + public List executeDataQuery(YdbQuery query, String yql, YdbValidator validator, int timeout, boolean poolable, Params params) throws SQLException { - return executor.executeDataQuery(ctx, validator, yql, timeout, poolable, params); + return executor.executeDataQuery(ctx, validator, query, yql, timeout, poolable, params); } @Override - public ResultSetReader executeScanQuery(String yql, YdbValidator validator, Params params) throws SQLException { + public ResultSetReader executeScanQuery(YdbQuery query, String yql, YdbValidator validator, Params params) + throws SQLException { executor.ensureOpened(); if (executor.isInsideTransaction()) { @@ -227,7 +228,7 @@ public ResultSetReader executeScanQuery(String yql, YdbValidator validator, Para } } - return executor.executeScanQuery(ctx, validator, yql, params); + return executor.executeScanQuery(ctx, validator, query, yql, params); } @Override diff --git a/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbPreparedStatementImpl.java b/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbPreparedStatementImpl.java index 591be46..63dab64 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbPreparedStatementImpl.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbPreparedStatementImpl.java @@ -91,7 +91,6 @@ public int[] executeBatch() throws SQLException { try { for (Params prm: prepared.getBatchParams()) { - getConnection().getCtx().traceQueryExecution(query); executeDataQuery(query, prepared.getQueryText(prm), prm); } } finally { @@ -126,13 +125,12 @@ public boolean execute() throws SQLException { List newState = null; Params prms = prepared.getCurrentParams(); - getConnection().getCtx().traceQueryExecution(query); switch (query.getType()) { case DATA_QUERY: newState = executeDataQuery(query, prepared.getQueryText(prms), prms); break; case SCAN_QUERY: - newState = executeScanQuery(prepared.getQueryText(prms), prms); + newState = executeScanQuery(query, prepared.getQueryText(prms), prms); break; default: throw new IllegalStateException("Internal error. Unsupported query type " + query.getType()); @@ -146,7 +144,7 @@ public boolean execute() throws SQLException { public YdbResultSet executeScanQuery() throws SQLException { cleanState(); Params prms = prepared.getCurrentParams(); - List state = executeScanQuery(prepared.getQueryText(prms), prms); + List state = executeScanQuery(query, prepared.getQueryText(prms), prms); prepared.clearParameters(); updateState(state); return getResultSet(); diff --git a/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbStatementImpl.java b/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbStatementImpl.java index ca0af3b..0e5003e 100644 --- a/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbStatementImpl.java +++ b/jdbc/src/main/java/tech/ydb/jdbc/impl/YdbStatementImpl.java @@ -5,7 +5,6 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -13,8 +12,6 @@ import tech.ydb.jdbc.YdbConnection; import tech.ydb.jdbc.YdbConst; import tech.ydb.jdbc.YdbResultSet; -import tech.ydb.jdbc.context.QueryStat; -import tech.ydb.jdbc.context.YdbContext; import tech.ydb.jdbc.query.YdbQuery; import tech.ydb.table.query.Params; @@ -42,7 +39,7 @@ public YdbResultSet executeScanQuery(String sql) throws SQLException { clearBatch(); YdbQuery query = getConnection().getCtx().parseYdbQuery(sql); - List results = executeScanQuery(query.getPreparedYql(), Params.empty()); + List results = executeScanQuery(query, query.getPreparedYql(), Params.empty()); if (!updateState(results)) { throw new SQLException(YdbConst.QUERY_EXPECT_RESULT_SET); } @@ -82,20 +79,7 @@ public int executeUpdate(String sql) throws SQLException { public boolean execute(String sql) throws SQLException { cleanState(); - YdbContext ctx = getConnection().getCtx(); - YdbQuery query; - - if (ctx.queryStatsEnabled()) { - if (sql != null && QueryStat.QUERY.equalsIgnoreCase(sql.trim())) { - YdbResultSet rs = new YdbResultSetImpl(this, QueryStat.toResultSetReader(ctx.getQueryStats())); - return updateState(Collections.singletonList(new YdbResult(rs))); - } - query = ctx.findOrParseYdbQuery(sql); - ctx.traceQueryExecution(query); - } else { - query = ctx.parseYdbQuery(sql); - } - + YdbQuery query = getConnection().getCtx().parseYdbQuery(sql); List newState = null; switch (query.getType()) { case SCHEME_QUERY: @@ -105,7 +89,7 @@ public boolean execute(String sql) throws SQLException { newState = executeDataQuery(query, query.getPreparedYql(), Params.empty()); break; case SCAN_QUERY: - newState = executeScanQuery(query.getPreparedYql(), Params.empty()); + newState = executeScanQuery(query, query.getPreparedYql(), Params.empty()); break; case EXPLAIN_QUERY: newState = executeExplainQuery(query); diff --git a/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbConnectionImplTest.java b/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbConnectionImplTest.java index 578da30..bff0680 100644 --- a/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbConnectionImplTest.java +++ b/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbConnectionImplTest.java @@ -28,6 +28,7 @@ import tech.ydb.jdbc.impl.helper.ExceptionAssert; import tech.ydb.jdbc.impl.helper.JdbcConnectionExtention; import tech.ydb.jdbc.impl.helper.SqlQueries; +import tech.ydb.jdbc.impl.helper.StatsAssert; import tech.ydb.jdbc.impl.helper.TableAssert; import tech.ydb.test.junit5.YdbHelperExtension; @@ -927,84 +928,233 @@ public void testUnsupportedComplexTypes(String type) throws SQLException { } @Test - public void testFullScanAnalyzer() throws SQLException { - try (Connection connection = jdbc.createCustomConnection("jdbcFullScanDetector", "true")) { - String selectAll = QUERIES.selectAllSQL(); - String selectByKey = QUERIES.selectAllByKey("1"); - String preparedSelect = QUERIES.selectAllByKey("?"); + public void fullScanAnalyzerSchemeQueriesTest() throws SQLException { + StatsAssert sa = new StatsAssert(); + + String createTable = QUERIES.withTableName("CREATE TABLE #tableName_1(id Int32, value Int32, PRIMARY KEY(id))"); + String dropTable = QUERIES.withTableName("DROP TABLE #tableName_1;"); + try (Connection connection = jdbc.createCustomConnection("jdbcFullScanDetector", "true")) { try (Statement st = connection.createStatement()) { try (ResultSet rs = st.executeQuery(" print_JDBC_stats(); ")) { - Assertions.assertFalse(rs.next()); // not stats + sa.check(rs) + .assertMetaColumns() + .assertNoRows(); } - try (ResultSet rs = st.executeQuery(selectAll)) { - Assertions.assertFalse(rs.next()); + // scheme queries don't collect stats + Assertions.assertFalse(st.execute(createTable)); + Assertions.assertFalse(st.execute(dropTable)); + + try (ResultSet rs = st.executeQuery("Print_JDBC_stats();\n")) { + sa.check(rs) + .assertMetaColumns() + .assertNoRows(); } + } + } + } + + @Test + public void fullScanAnalyzerSchemeWrongQueryTest() throws SQLException { + StatsAssert sa = new StatsAssert(); + + String wrongQuery = QUERIES.withTableName("select * from wrong_table;"); + + try (Connection connection = jdbc.createCustomConnection("jdbcFullScanDetector", "true")) { + try (Statement st = connection.createStatement()) { + try (ResultSet rs = st.executeQuery("print_JDBC_stats();")) { + sa.check(rs) + .assertMetaColumns() + .assertNoRows(); + } + + ExceptionAssert.ydbException("Cannot find table", () -> st.execute(wrongQuery)); try (ResultSet rs = st.executeQuery("Print_JDBC_stats();\n")) { - Assertions.assertTrue(rs.next()); - Assertions.assertEquals(selectAll, rs.getString("sql")); - Assertions.assertEquals(true, rs.getBoolean("is_fullscan")); - Assertions.assertEquals(1l, rs.getLong("executed")); + TableAssert.ResultSetAssert check = sa.check(rs).assertMetaColumns(); - Assertions.assertFalse(rs.next()); + check.nextRow( + sa.sql("select * from wrong_table;"), + sa.yql("select * from wrong_table;"), + sa.isNotFullScan(), sa.isError(), sa.executed(1), sa.hasNoAst(), sa.hasPlan() + ).assertAll(); + + check.assertNoRows(); + } + + Assertions.assertFalse(st.execute("reset_jdbc_stats();\n")); + try (ResultSet rs = st.executeQuery("print_JDBC_stats();")) { + sa.check(rs) + .assertMetaColumns() + .assertNoRows(); } + } + } + } + + @Test + public void fullScanAnalyzerStatementTest() throws SQLException { + StatsAssert sa = new StatsAssert(); + String selectAll = QUERIES.selectAllSQL(); + String selectByKey = QUERIES.selectAllByKey("1"); + + try (Connection connection = jdbc.createCustomConnection("jdbcFullScanDetector", "true")) { + try (Statement st = connection.createStatement()) { + try (ResultSet rs = st.executeQuery(" print_JDBC_stats(); ")) { + sa.check(rs) + .assertMetaColumns() + .assertNoRows(); + } + + // full scan query try (ResultSet rs = st.executeQuery(selectAll)) { Assertions.assertFalse(rs.next()); } + + try (ResultSet rs = st.executeQuery("\tPrint_JDBC_staTs();")) { + TableAssert.ResultSetAssert check = sa.check(rs).assertMetaColumns(); + + check.nextRow( + sa.sql("select * from ydb_connection_test"), + sa.yql("select * from ydb_connection_test"), + sa.isFullScan(), sa.isNotError(), sa.executed(1), sa.hasAst(), sa.hasPlan() + ).assertAll(); + + check.assertNoRows(); + } + + // key read query try (ResultSet rs = st.executeQuery(selectByKey)) { Assertions.assertFalse(rs.next()); } - try (ResultSet rs = st.executeQuery("Print_JDBC_stats();\n")) { - Assertions.assertTrue(rs.next()); - Assertions.assertEquals(selectAll, rs.getString("sql")); - Assertions.assertEquals(true, rs.getBoolean("is_fullscan")); - Assertions.assertEquals(2l, rs.getLong("executed")); + try (ResultSet rs = st.executeQuery("print_JDBC_staTs();")) { + TableAssert.ResultSetAssert check = sa.check(rs).assertMetaColumns(); - Assertions.assertTrue(rs.next()); - Assertions.assertEquals(selectByKey, rs.getString("sql")); - Assertions.assertEquals(false, rs.getBoolean("is_fullscan")); - Assertions.assertEquals(1l, rs.getLong("executed")); + check.nextRow( + sa.sql("select * from ydb_connection_test"), + sa.yql("select * from ydb_connection_test"), + sa.isFullScan(), sa.isNotError(), sa.executed(1), sa.hasAst(), sa.hasPlan() + ).assertAll(); - Assertions.assertFalse(rs.next()); + check.nextRow( + sa.sql("select * from ydb_connection_test where key = 1"), + sa.yql("select * from ydb_connection_test where key = 1"), + sa.isNotFullScan(), sa.isNotError(), sa.executed(1), sa.hasAst(), sa.hasPlan() + ).assertAll(); + + check.assertNoRows(); } - try (PreparedStatement ps = connection.prepareStatement(preparedSelect)) { - ps.setLong(1, 1); - try (ResultSet rs = ps.executeQuery()) { - Assertions.assertFalse(rs.next()); - } - ps.setLong(1, 2); - try (ResultSet rs = ps.executeQuery()) { - Assertions.assertFalse(rs.next()); - } - ps.setLong(1, 3); - try (ResultSet rs = ps.executeQuery()) { - Assertions.assertFalse(rs.next()); - } + // key read query + try (ResultSet rs = st.executeQuery(selectByKey)) { + Assertions.assertFalse(rs.next()); } - try (ResultSet rs = st.executeQuery("Print_JDBC_stats();\n")) { - Assertions.assertTrue(rs.next()); - Assertions.assertEquals(preparedSelect, rs.getString("sql")); - Assertions.assertEquals(false, rs.getBoolean("is_fullscan")); - Assertions.assertEquals(3l, rs.getLong("executed")); + try (ResultSet rs = st.executeQuery("print_JDBC_staTs();")) { + TableAssert.ResultSetAssert check = sa.check(rs).assertMetaColumns(); - Assertions.assertTrue(rs.next()); - Assertions.assertEquals(selectAll, rs.getString("sql")); - Assertions.assertEquals(true, rs.getBoolean("is_fullscan")); - Assertions.assertEquals(2l, rs.getLong("executed")); + check.nextRow( + sa.sql("select * from ydb_connection_test where key = 1"), + sa.yql("select * from ydb_connection_test where key = 1"), + sa.isNotFullScan(), sa.isNotError(), sa.executed(2), sa.hasAst(), sa.hasPlan() + ).assertAll(); - Assertions.assertTrue(rs.next()); - Assertions.assertEquals(selectByKey, rs.getString("sql")); - Assertions.assertEquals(false, rs.getBoolean("is_fullscan")); - Assertions.assertEquals(1l, rs.getLong("executed")); + check.nextRow( + sa.sql("select * from ydb_connection_test"), + sa.yql("select * from ydb_connection_test"), + sa.isFullScan(), sa.isNotError(), sa.executed(1), sa.hasAst(), sa.hasPlan() + ).assertAll(); - Assertions.assertFalse(rs.next()); + check.assertNoRows(); } + + Assertions.assertFalse(st.execute("\t\treSet_jdbc_statS();")); + try (ResultSet rs = st.executeQuery("print_JDBC_stats();")) { + sa.check(rs) + .assertMetaColumns() + .assertNoRows(); + } + } + } + } + + @Test + public void fullScanAnalyzerPreparedStatementTest() throws SQLException { + StatsAssert sa = new StatsAssert(); + + String preparedSelectByKey = QUERIES.selectAllByKey("?"); + String preparedSelectByColumn = QUERIES.selectAllByColumnValue("c_Text", "?"); + + try (Connection connection = jdbc.createCustomConnection("jdbcFullScanDetector", "true")) { + try (PreparedStatement ps = connection.prepareStatement("print_JDBC_stats();")) { + sa.check(ps.executeQuery()) + .assertMetaColumns() + .assertNoRows(); + } + + try (PreparedStatement ps = connection.prepareStatement(preparedSelectByKey)) { + ps.setInt(1, 1); + ps.execute(); + + ps.setInt(1, 2); + ps.execute(); + } + + try (PreparedStatement ps = connection.prepareStatement("print_JDBC_stats();")) { + TableAssert.ResultSetAssert check = sa.check(ps.executeQuery()).assertMetaColumns(); + + check.nextRow( + sa.sql("select * from ydb_connection_test where key = ?"), + sa.yql("DECLARE $jp1 AS Int32;\nselect * from ydb_connection_test where key = $jp1"), + sa.isNotFullScan(), sa.isNotError(), sa.executed(2), sa.hasAst(), sa.hasPlan() + ).assertAll(); + + check.assertNoRows(); + } + + try (PreparedStatement ps = connection.prepareStatement(preparedSelectByColumn)) { + ps.setString(1, "v1"); + ps.execute(); + + ps.setString(1, null); + ps.execute(); + } + + try (PreparedStatement ps = connection.prepareStatement("print_JDBC_stats();")) { + TableAssert.ResultSetAssert check = sa.check(ps.executeQuery()).assertMetaColumns(); + + check.nextRow( + sa.sql("select * from ydb_connection_test where key = ?"), + sa.yql("DECLARE $jp1 AS Int32;\nselect * from ydb_connection_test where key = $jp1"), + sa.isNotFullScan(), sa.isNotError(), sa.executed(2), sa.hasAst(), sa.hasPlan() + ).assertAll(); + + check.nextRow( + sa.sql("select * from ydb_connection_test where c_Text = ?"), + sa.yql("DECLARE $jp1 AS Text;\nselect * from ydb_connection_test where c_Text = $jp1"), + sa.isFullScan(), sa.isNotError(), sa.executed(1), sa.hasAst(), sa.hasPlan() + ).assertAll(); + + check.nextRow( + sa.sql("select * from ydb_connection_test where c_Text = ?"), + sa.yql("DECLARE $jp1 AS Text?;\nselect * from ydb_connection_test where c_Text = $jp1"), + sa.isFullScan(), sa.isNotError(), sa.executed(1), sa.hasAst(), sa.hasPlan() + ).assertAll(); + + check.assertNoRows(); + } + + try (PreparedStatement ps = connection.prepareStatement("reset_JDBC_stats();")) { + Assertions.assertFalse(ps.execute()); + } + + try (PreparedStatement ps = connection.prepareStatement("print_JDBC_stats();")) { + sa.check(ps.executeQuery()) + .assertMetaColumns() + .assertNoRows(); } } } diff --git a/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbQueryConnectionImplTest.java b/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbQueryConnectionImplTest.java index 13a0cc8..fc2f9b9 100644 --- a/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbQueryConnectionImplTest.java +++ b/jdbc/src/test/java/tech/ydb/jdbc/impl/YdbQueryConnectionImplTest.java @@ -29,6 +29,7 @@ import tech.ydb.jdbc.impl.helper.ExceptionAssert; import tech.ydb.jdbc.impl.helper.JdbcConnectionExtention; import tech.ydb.jdbc.impl.helper.SqlQueries; +import tech.ydb.jdbc.impl.helper.StatsAssert; import tech.ydb.jdbc.impl.helper.TableAssert; import tech.ydb.test.junit5.YdbHelperExtension; @@ -930,84 +931,233 @@ public void testUnsupportedComplexTypes(String type) throws SQLException { } @Test - public void testFullScanAnalyzer() throws SQLException { - try (Connection connection = jdbc.createCustomConnection("jdbcFullScanDetector", "true")) { - String selectAll = QUERIES.selectAllSQL(); - String selectByKey = QUERIES.selectAllByKey("1"); - String preparedSelect = QUERIES.selectAllByKey("?"); + public void fullScanAnalyzerSchemeQueriesTest() throws SQLException { + StatsAssert sa = new StatsAssert(); + + String createTable = QUERIES.withTableName("CREATE TABLE #tableName_1(id Int32, value Int32, PRIMARY KEY(id))"); + String dropTable = QUERIES.withTableName("DROP TABLE #tableName_1;"); + try (Connection connection = jdbc.createCustomConnection("jdbcFullScanDetector", "true")) { try (Statement st = connection.createStatement()) { try (ResultSet rs = st.executeQuery(" print_JDBC_stats(); ")) { - Assertions.assertFalse(rs.next()); // not stats + sa.check(rs) + .assertMetaColumns() + .assertNoRows(); } - try (ResultSet rs = st.executeQuery(selectAll)) { - Assertions.assertFalse(rs.next()); + // scheme queries don't collect stats + Assertions.assertFalse(st.execute(createTable)); + Assertions.assertFalse(st.execute(dropTable)); + + try (ResultSet rs = st.executeQuery("Print_JDBC_stats();\n")) { + sa.check(rs) + .assertMetaColumns() + .assertNoRows(); } + } + } + } + + @Test + public void fullScanAnalyzerSchemeWrongQueryTest() throws SQLException { + StatsAssert sa = new StatsAssert(); + + String wrongQuery = QUERIES.withTableName("select * from wrong_table;"); + + try (Connection connection = jdbc.createCustomConnection("jdbcFullScanDetector", "true")) { + try (Statement st = connection.createStatement()) { + try (ResultSet rs = st.executeQuery("print_JDBC_stats();")) { + sa.check(rs) + .assertMetaColumns() + .assertNoRows(); + } + + ExceptionAssert.ydbException("Cannot find table", () -> st.execute(wrongQuery)); try (ResultSet rs = st.executeQuery("Print_JDBC_stats();\n")) { - Assertions.assertTrue(rs.next()); - Assertions.assertEquals(selectAll, rs.getString("sql")); - Assertions.assertEquals(true, rs.getBoolean("is_fullscan")); - Assertions.assertEquals(1l, rs.getLong("executed")); + TableAssert.ResultSetAssert check = sa.check(rs).assertMetaColumns(); - Assertions.assertFalse(rs.next()); + check.nextRow( + sa.sql("select * from wrong_table;"), + sa.yql("select * from wrong_table;"), + sa.isNotFullScan(), sa.isError(), sa.executed(1), sa.hasNoAst(), sa.hasPlan() + ).assertAll(); + + check.assertNoRows(); + } + + Assertions.assertFalse(st.execute("reset_jdbc_stats();\n")); + try (ResultSet rs = st.executeQuery("print_JDBC_stats();")) { + sa.check(rs) + .assertMetaColumns() + .assertNoRows(); } + } + } + } + + @Test + public void fullScanAnalyzerStatementTest() throws SQLException { + StatsAssert sa = new StatsAssert(); + String selectAll = QUERIES.selectAllSQL(); + String selectByKey = QUERIES.selectAllByKey("1"); + + try (Connection connection = jdbc.createCustomConnection("jdbcFullScanDetector", "true")) { + try (Statement st = connection.createStatement()) { + try (ResultSet rs = st.executeQuery(" print_JDBC_stats(); ")) { + sa.check(rs) + .assertMetaColumns() + .assertNoRows(); + } + + // full scan query try (ResultSet rs = st.executeQuery(selectAll)) { Assertions.assertFalse(rs.next()); } + + try (ResultSet rs = st.executeQuery("\tPrint_JDBC_staTs();")) { + TableAssert.ResultSetAssert check = sa.check(rs).assertMetaColumns(); + + check.nextRow( + sa.sql("select * from ydb_connection_test"), + sa.yql("select * from ydb_connection_test"), + sa.isFullScan(), sa.isNotError(), sa.executed(1), sa.hasAst(), sa.hasPlan() + ).assertAll(); + + check.assertNoRows(); + } + + // key read query try (ResultSet rs = st.executeQuery(selectByKey)) { Assertions.assertFalse(rs.next()); } - try (ResultSet rs = st.executeQuery("Print_JDBC_stats();\n")) { - Assertions.assertTrue(rs.next()); - Assertions.assertEquals(selectAll, rs.getString("sql")); - Assertions.assertEquals(true, rs.getBoolean("is_fullscan")); - Assertions.assertEquals(2l, rs.getLong("executed")); + try (ResultSet rs = st.executeQuery("print_JDBC_staTs();")) { + TableAssert.ResultSetAssert check = sa.check(rs).assertMetaColumns(); - Assertions.assertTrue(rs.next()); - Assertions.assertEquals(selectByKey, rs.getString("sql")); - Assertions.assertEquals(false, rs.getBoolean("is_fullscan")); - Assertions.assertEquals(1l, rs.getLong("executed")); + check.nextRow( + sa.sql("select * from ydb_connection_test"), + sa.yql("select * from ydb_connection_test"), + sa.isFullScan(), sa.isNotError(), sa.executed(1), sa.hasAst(), sa.hasPlan() + ).assertAll(); - Assertions.assertFalse(rs.next()); + check.nextRow( + sa.sql("select * from ydb_connection_test where key = 1"), + sa.yql("select * from ydb_connection_test where key = 1"), + sa.isNotFullScan(), sa.isNotError(), sa.executed(1), sa.hasAst(), sa.hasPlan() + ).assertAll(); + + check.assertNoRows(); } - try (PreparedStatement ps = connection.prepareStatement(preparedSelect)) { - ps.setLong(1, 1); - try (ResultSet rs = ps.executeQuery()) { - Assertions.assertFalse(rs.next()); - } - ps.setLong(1, 2); - try (ResultSet rs = ps.executeQuery()) { - Assertions.assertFalse(rs.next()); - } - ps.setLong(1, 3); - try (ResultSet rs = ps.executeQuery()) { - Assertions.assertFalse(rs.next()); - } + // key read query + try (ResultSet rs = st.executeQuery(selectByKey)) { + Assertions.assertFalse(rs.next()); } - try (ResultSet rs = st.executeQuery("Print_JDBC_stats();\n")) { - Assertions.assertTrue(rs.next()); - Assertions.assertEquals(preparedSelect, rs.getString("sql")); - Assertions.assertEquals(false, rs.getBoolean("is_fullscan")); - Assertions.assertEquals(3l, rs.getLong("executed")); + try (ResultSet rs = st.executeQuery("print_JDBC_staTs();")) { + TableAssert.ResultSetAssert check = sa.check(rs).assertMetaColumns(); - Assertions.assertTrue(rs.next()); - Assertions.assertEquals(selectAll, rs.getString("sql")); - Assertions.assertEquals(true, rs.getBoolean("is_fullscan")); - Assertions.assertEquals(2l, rs.getLong("executed")); + check.nextRow( + sa.sql("select * from ydb_connection_test where key = 1"), + sa.yql("select * from ydb_connection_test where key = 1"), + sa.isNotFullScan(), sa.isNotError(), sa.executed(2), sa.hasAst(), sa.hasPlan() + ).assertAll(); - Assertions.assertTrue(rs.next()); - Assertions.assertEquals(selectByKey, rs.getString("sql")); - Assertions.assertEquals(false, rs.getBoolean("is_fullscan")); - Assertions.assertEquals(1l, rs.getLong("executed")); + check.nextRow( + sa.sql("select * from ydb_connection_test"), + sa.yql("select * from ydb_connection_test"), + sa.isFullScan(), sa.isNotError(), sa.executed(1), sa.hasAst(), sa.hasPlan() + ).assertAll(); - Assertions.assertFalse(rs.next()); + check.assertNoRows(); } + + Assertions.assertFalse(st.execute("\t\treSet_jdbc_statS();")); + try (ResultSet rs = st.executeQuery("print_JDBC_stats();")) { + sa.check(rs) + .assertMetaColumns() + .assertNoRows(); + } + } + } + } + + @Test + public void fullScanAnalyzerPreparedStatementTest() throws SQLException { + StatsAssert sa = new StatsAssert(); + + String preparedSelectByKey = QUERIES.selectAllByKey("?"); + String preparedSelectByColumn = QUERIES.selectAllByColumnValue("c_Text", "?"); + + try (Connection connection = jdbc.createCustomConnection("jdbcFullScanDetector", "true")) { + try (PreparedStatement ps = connection.prepareStatement("print_JDBC_stats();")) { + sa.check(ps.executeQuery()) + .assertMetaColumns() + .assertNoRows(); + } + + try (PreparedStatement ps = connection.prepareStatement(preparedSelectByKey)) { + ps.setInt(1, 1); + ps.execute(); + + ps.setInt(1, 2); + ps.execute(); + } + + try (PreparedStatement ps = connection.prepareStatement("print_JDBC_stats();")) { + TableAssert.ResultSetAssert check = sa.check(ps.executeQuery()).assertMetaColumns(); + + check.nextRow( + sa.sql("select * from ydb_connection_test where key = ?"), + sa.yql("DECLARE $jp1 AS Int32;\nselect * from ydb_connection_test where key = $jp1"), + sa.isNotFullScan(), sa.isNotError(), sa.executed(2), sa.hasAst(), sa.hasPlan() + ).assertAll(); + + check.assertNoRows(); + } + + try (PreparedStatement ps = connection.prepareStatement(preparedSelectByColumn)) { + ps.setString(1, "v1"); + ps.execute(); + + ps.setString(1, null); + ps.execute(); + } + + try (PreparedStatement ps = connection.prepareStatement("print_JDBC_stats();")) { + TableAssert.ResultSetAssert check = sa.check(ps.executeQuery()).assertMetaColumns(); + + check.nextRow( + sa.sql("select * from ydb_connection_test where key = ?"), + sa.yql("DECLARE $jp1 AS Int32;\nselect * from ydb_connection_test where key = $jp1"), + sa.isNotFullScan(), sa.isNotError(), sa.executed(2), sa.hasAst(), sa.hasPlan() + ).assertAll(); + + check.nextRow( + sa.sql("select * from ydb_connection_test where c_Text = ?"), + sa.yql("DECLARE $jp1 AS Text;\nselect * from ydb_connection_test where c_Text = $jp1"), + sa.isFullScan(), sa.isNotError(), sa.executed(1), sa.hasAst(), sa.hasPlan() + ).assertAll(); + + check.nextRow( + sa.sql("select * from ydb_connection_test where c_Text = ?"), + sa.yql("DECLARE $jp1 AS Text?;\nselect * from ydb_connection_test where c_Text = $jp1"), + sa.isFullScan(), sa.isNotError(), sa.executed(1), sa.hasAst(), sa.hasPlan() + ).assertAll(); + + check.assertNoRows(); + } + + try (PreparedStatement ps = connection.prepareStatement("reset_JDBC_stats();")) { + Assertions.assertFalse(ps.execute()); + } + + try (PreparedStatement ps = connection.prepareStatement("print_JDBC_stats();")) { + sa.check(ps.executeQuery()) + .assertMetaColumns() + .assertNoRows(); } } } diff --git a/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/SqlQueries.java b/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/SqlQueries.java index f5c718d..cf68236 100644 --- a/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/SqlQueries.java +++ b/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/SqlQueries.java @@ -39,6 +39,7 @@ public enum YqlQuery { private static final String SELECT_ALL = "select * from #tableName"; private static final String SELECT_BY_KEY = "select * from #tableName where key = #value"; + private static final String SELECT_BY_COLUMN = "select * from #tableName where #column = #value"; private static final String DELETE_ALL = "delete from #tableName"; private static final String SELECT_COLUMN = "select key, #column from #tableName"; private static final String WRONG_SELECT = "select key2 from #tableName"; @@ -98,11 +99,23 @@ public String selectAllSQL() { return withTableName(SELECT_ALL); } - /** @return select * from #tableName where key = #value */ + /** + * @param value key value + * @return select * from #tableName where key = #value + */ public String selectAllByKey(String value) { return withTableName(SELECT_BY_KEY).replaceAll("#value", value); } + /** + * @param column column name + * @param value column value + * @return select * from #tableName where #column = #value + */ + public String selectAllByColumnValue(String column, String value) { + return withTableName(SELECT_BY_COLUMN).replaceAll("#column", column).replaceAll("#value", value); + } + /** @return select key, c_Bool, c_Int8, ... , from #tableName */ public String selectSQL() { return withTableName(SELECT); diff --git a/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/StatsAssert.java b/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/StatsAssert.java new file mode 100644 index 0000000..ecc2a2a --- /dev/null +++ b/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/StatsAssert.java @@ -0,0 +1,55 @@ +package tech.ydb.jdbc.impl.helper; + +/** + * + * @author Aleksandr Gorshenin + */ +public final class StatsAssert extends TableAssert { + private final TextColumn querySql = addTextColumn("sql", "Text");; + private final BoolColumn isFullScan = addBoolColumn("is_fullscan", "Bool");; + private final BoolColumn isError = addBoolColumn("is_error", "Bool"); + private final LongColumn executed = addLongColumn("executed", "Int64"); + private final TextColumn queryYql= addTextColumn("yql", "Text"); + private final TextColumn queryAst = addTextColumn("ast", "Text"); + private final TextColumn queryPlan = addTextColumn("plan", "Text"); + + public ValueAssert sql(String sql) { + return querySql.eq(sql); + } + + public ValueAssert yql(String yql) { + return queryYql.eq(yql); + } + + public ValueAssert hasAst() { + return queryAst.isNotEmpty(); + } + + public ValueAssert hasNoAst() { + return queryAst.isNull(); + } + + public ValueAssert hasPlan() { + return queryPlan.isNotEmpty(); + } + + public ValueAssert executed(long count) { + return executed.eq(count); + } + + public ValueAssert isFullScan() { + return isFullScan.eq(true); + } + + public ValueAssert isNotFullScan() { + return isFullScan.eq(false); + } + + public ValueAssert isError() { + return isError.eq(true); + } + + public ValueAssert isNotError() { + return isError.eq(false); + } +} diff --git a/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/TableAssert.java b/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/TableAssert.java index d0a6a66..e9449d9 100644 --- a/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/TableAssert.java +++ b/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/TableAssert.java @@ -233,6 +233,36 @@ public void assertValue(ResultSet rs) throws SQLException { } }; } + + public ValueAssert isNull() { + return new ValueAssert(this) { + @Override + public void assertValue(ResultSet rs) throws SQLException { + Assertions.assertNull(rs.getString(column.name), + "Not null text value for column label " + column.name); + Assertions.assertTrue(rs.wasNull(), "Not null value for column label " + column.name); + + Assertions.assertNull(rs.getString(column.index), + "Not null value for column index " + column.index); + Assertions.assertTrue(rs.wasNull(), "Not null text value of column index " + column.index); + } + }; + } + + public ValueAssert isNotEmpty() { + return new ValueAssert(this) { + @Override + public void assertValue(ResultSet rs) throws SQLException { + String v1 = rs.getString(column.name); + Assertions.assertFalse(rs.wasNull(), "Null value for column label " + column.name); + Assertions.assertFalse(v1.isEmpty(), "Empty text value for column label " + column.name); + + String v2 = rs.getString(column.index); + Assertions.assertFalse(rs.wasNull(), "Null value for column index " + column.index); + Assertions.assertFalse(v2.isEmpty(), "Empty text value of column index " + column.index); + } + }; + } } public class IntColumn extends Column { diff --git a/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/TestHelper.java b/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/TestHelper.java deleted file mode 100644 index d64e09a..0000000 --- a/jdbc/src/test/java/tech/ydb/jdbc/impl/helper/TestHelper.java +++ /dev/null @@ -1,87 +0,0 @@ -package tech.ydb.jdbc.impl.helper; - -import java.sql.SQLException; -import java.util.function.Consumer; - -import javax.annotation.Nullable; - -import org.junit.jupiter.api.function.Executable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import tech.ydb.jdbc.YdbConnection; -import tech.ydb.jdbc.YdbStatement; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class TestHelper { - private static final Logger LOGGER = LoggerFactory.getLogger(TestHelper.class); - - // Significantly reduce total time spending in tests - private static final boolean IGNORE_EXISTING_TABLE = - Boolean.parseBoolean(System.getProperty("IGNORE_EXISTING_TABLE", "false")); - - private TestHelper() { } - - public static void assertThrowsMsg(Class type, Executable exec, String expectMessage) { - assertThrowsMsg(type, exec, e -> assertEquals(expectMessage, e.getMessage(), "Error message"), null); - } - - public static void assertThrowsMsgLike(Class type, Executable exec, String expectLike) { - assertThrowsMsgLike(type, exec, expectLike, null); - } - - public static void assertThrowsMsgLike(Class type, Executable exec, String expectLike, - String description) { - assertThrowsMsg(type, exec, e -> { - String errorMessage = e.getMessage(); - assertTrue(errorMessage.contains(expectLike), - String.format("Error message [%s] must contains [%s]", errorMessage, expectLike)); - }, description); - } - - - public static void assertThrowsMsg(Class type, - Executable exec, - Consumer check, - @Nullable String description) { - Throwable throwable = assertThrows(Throwable.class, exec, description); - assertTrue(type.isAssignableFrom(throwable.getClass()), - () -> "Unexpected exception type thrown, expected " + type + ", got " + throwable.getClass()); - LOGGER.trace("Catch exception", throwable); - check.accept(type.cast(throwable)); - } - - public static void initTable(YdbConnection connection, String tableName, String expression) throws SQLException { - try (YdbStatement statement = connection.createStatement()) { - if (IGNORE_EXISTING_TABLE) { - try { - statement.execute(String.format("select * from %s limit 1", tableName)); - } catch (Exception e) { - statement.executeSchemeQuery(withTableName(tableName, expression)); - } - } else { - try { - statement.executeSchemeQuery(String.format("drop table %s", tableName)); - } catch (Exception e) { - // do nothing - } - statement.executeSchemeQuery(withTableName(tableName, expression)); - } - } - } - - public static String withTableName(String tableName, String sql) { - return sql.replace("${tableName}", tableName); - } - - public interface SQLRun { - void run(YdbConnection connection) throws SQLException; - } - - public interface SQLSimpleRun { - void run() throws SQLException; - } -}