This is an automated email from the ASF dual-hosted git repository.
apkhmv pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 8f082950b9 IGNITE-22546 Improve multi statement support in CLI (#4348)
8f082950b9 is described below
commit 8f082950b935de526ba19b781adc199eff7a860d
Author: Maksim Myskov <[email protected]>
AuthorDate: Thu Sep 12 17:58:06 2024 +0300
IGNITE-22546 Improve multi statement support in CLI (#4348)
---
.../cli/commands/sql/ItSqlMultistatementTest.java | 154 +++++++++++++++++++++
.../cli/decorators/SqlQueryResultDecorator.java | 8 +-
.../apache/ignite/internal/cli/sql/SqlManager.java | 23 ++-
.../ignite/internal/cli/sql/SqlQueryResult.java | 63 +++++----
.../SqlQueryResultItem.java} | 26 ++--
.../SqlQueryResultMessage.java} | 20 ++-
.../SqlQueryResultTable.java} | 21 ++-
7 files changed, 233 insertions(+), 82 deletions(-)
diff --git
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlMultistatementTest.java
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlMultistatementTest.java
new file mode 100644
index 0000000000..a194cc26c2
--- /dev/null
+++
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlMultistatementTest.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.cli.commands.sql;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for sql multi statement queries.
+ */
+public class ItSqlMultistatementTest extends CliSqlCommandTestBase {
+
+ @BeforeEach
+ void setup() {
+ execute("sql", "create table mytable(id int primary key, name
varchar)", "--jdbc-url", JDBC_URL);
+ }
+
+ /**
+ * Test for multiple insert queries. It ensures that the output contains
the number of updated rows for each query.
+ */
+ @Test
+ void sequentialUpdates() {
+ String testQuery = "insert into mytable(id) values (1);insert into
mytable(id) values (2), (3)";
+ String expectedOutput = "Updated 1 rows.\n"
+ + "Updated 2 rows.";
+
+ execute("sql", testQuery, "--jdbc-url", JDBC_URL);
+
+ assertAll(
+ this::assertExitCodeIsZero,
+ () -> assertOutputContains(expectedOutput),
+ this::assertErrOutputIsEmpty
+ );
+ }
+
+ @Test
+ void sequentialSelects() {
+ String testQuery = "select * from mytable order by id; insert into
mytable(id) values(3); select * from mytable order by id;";
+ String expectedOutput =
+ "╔════╤═══════╗\n"
+ + "║ ID │ NAME ║\n"
+ + "╠════╪═══════╣\n"
+ + "║ 1 │ Name1 ║\n"
+ + "╟────┼───────╢\n"
+ + "║ 2 │ Name2 ║\n"
+ + "╚════╧═══════╝\n"
+ + "Updated 1 rows.\n"
+ + "╔════╤═══════╗\n"
+ + "║ ID │ NAME ║\n"
+ + "╠════╪═══════╣\n"
+ + "║ 1 │ Name1 ║\n"
+ + "╟────┼───────╢\n"
+ + "║ 2 │ Name2 ║\n"
+ + "╟────┼───────╢\n"
+ + "║ 3 │ null ║\n"
+ + "╚════╧═══════╝";
+
+ execute("sql", "insert into mytable(id, name) values (1, 'Name1'), (2,
'Name2')", "--jdbc-url", JDBC_URL);
+
+ execute("sql", testQuery, "--jdbc-url", JDBC_URL);
+
+ assertAll(
+ this::assertExitCodeIsZero,
+ () -> assertOutputContains(expectedOutput),
+ this::assertErrOutputIsEmpty
+ );
+ }
+
+ @Test
+ void deleteAfterInserts() {
+ String testQuery = "insert into mytable(id) values (1);insert into
mytable(id) values (2), (3); delete from mytable;";
+ String expectedOutput = "Updated 1 rows.\n"
+ + "Updated 2 rows.\n"
+ + "Updated 3 rows.";
+
+ execute("sql", testQuery, "--jdbc-url", JDBC_URL);
+
+ assertAll(
+ this::assertExitCodeIsZero,
+ () -> assertOutputContains(expectedOutput),
+ this::assertErrOutputIsEmpty
+ );
+ }
+
+ @Test
+ void updateAfterInserts() {
+ String testQuery = "insert into mytable(id) values (1);insert into
mytable(id) values (2), (3); update mytable set Name = 'Name1';";
+ String expectedOutput = "Updated 1 rows.\n"
+ + "Updated 2 rows.\n"
+ + "Updated 3 rows.";
+
+ execute("sql", testQuery, "--jdbc-url", JDBC_URL);
+
+ assertAll(
+ this::assertExitCodeIsZero,
+ () -> assertOutputContains(expectedOutput),
+ this::assertErrOutputIsEmpty
+ );
+ }
+
+ @Test
+ void sequentialCreateTable() {
+ String testQuery = "create table mytable1(id int primary key); create
table mytable2(id int primary key)";
+ String expectedOutput = "Updated 0 rows.\n"
+ + "Updated 0 rows.";
+
+ execute("sql", testQuery, "--jdbc-url", JDBC_URL);
+
+ assertAll(
+ this::assertExitCodeIsZero,
+ () -> assertOutputContains(expectedOutput),
+ this::assertErrOutputIsEmpty
+ );
+ }
+
+ /**
+ * Test for single select query. It ensures that there is no unnecessary
data in output.
+ */
+ @Test
+ void singleSelect() {
+ String testQuery = "select * from mytable";
+ String expectedOutput =
+ "╔════╤══════╗\n"
+ + "║ ID │ NAME ║\n"
+ + "╠════╧══════╣\n"
+ + "║ (empty) ║\n"
+ + "╚═══════════╝\n" + System.lineSeparator();
+
+ execute("sql", testQuery, "--jdbc-url", JDBC_URL);
+
+ assertAll(
+ this::assertExitCodeIsZero,
+ () -> assertOutputIs(expectedOutput),
+ this::assertErrOutputIsEmpty
+ );
+ }
+}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
index ad2b53f757..77c4aacd00 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
@@ -25,16 +25,14 @@ import org.apache.ignite.internal.cli.sql.SqlQueryResult;
* Composite decorator for {@link SqlQueryResult}.
*/
public class SqlQueryResultDecorator implements Decorator<SqlQueryResult,
TerminalOutput> {
- private final TableDecorator tableDecorator;
-
- private final DefaultDecorator<String> messageDecorator = new
DefaultDecorator<>();
+ private final boolean plain;
public SqlQueryResultDecorator(boolean plain) {
- this.tableDecorator = new TableDecorator(plain);
+ this.plain = plain;
}
@Override
public TerminalOutput decorate(SqlQueryResult data) {
- return data.getResult(tableDecorator, messageDecorator);
+ return data.getResult(plain);
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlManager.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlManager.java
index 88d4557063..372957dcae 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlManager.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlManager.java
@@ -23,6 +23,7 @@ import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
+import org.apache.ignite.internal.cli.sql.SqlQueryResult.SqlQueryResultBuilder;
import org.apache.ignite.internal.cli.sql.table.Table;
/**
@@ -46,13 +47,23 @@ public class SqlManager implements AutoCloseable {
* @throws SQLException in any case when SQL command can't be executed.
*/
public SqlQueryResult execute(String sql) throws SQLException {
+
+ SqlQueryResultBuilder sqlQueryResultBuilder = new
SqlQueryResultBuilder();
+
try (Statement statement = connection.createStatement()) {
- if (statement.execute(sql)) {
- ResultSet resultSet = statement.getResultSet();
- return new SqlQueryResult(Table.fromResultSet(resultSet));
- }
- int updateCount = statement.getUpdateCount();
- return new SqlQueryResult(updateCount >= 0 ? "Updated " +
updateCount + " rows." : "OK!");
+ statement.execute(sql);
+
+ do {
+ ResultSet rs = statement.getResultSet();
+ if (rs != null) {
+ sqlQueryResultBuilder.addTable(Table.fromResultSet(rs));
+ } else {
+ int updateCount = statement.getUpdateCount();
+ sqlQueryResultBuilder.addMessage(updateCount >= 0 ?
"Updated " + updateCount + " rows." : "OK!");
+ }
+ } while (statement.getMoreResults() || statement.getUpdateCount()
!= -1);
+
+ return sqlQueryResultBuilder.build();
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResult.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResult.java
index 0972db073c..4f24efff54 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResult.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResult.java
@@ -17,56 +17,55 @@
package org.apache.ignite.internal.cli.sql;
-import org.apache.ignite.internal.cli.core.decorator.Decorator;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
import org.apache.ignite.internal.cli.core.decorator.TerminalOutput;
-import org.apache.ignite.internal.cli.decorators.TableDecorator;
import org.apache.ignite.internal.cli.sql.table.Table;
-import org.jetbrains.annotations.Nullable;
/**
* Composite object of sql query result.
*/
public class SqlQueryResult {
- @Nullable
- private final Table<String> table;
+ private final List<SqlQueryResultItem> sqlQueryResultItems;
- private final String message;
+ private SqlQueryResult(List<SqlQueryResultItem> sqlQueryResultItems) {
+ this.sqlQueryResultItems = sqlQueryResultItems;
+ }
/**
- * Constructor.
+ * SQL query result provider.
*
- * @param table non null result table.
+ * @return terminal output all items in query result.
*/
- public SqlQueryResult(@Nullable Table<String> table) {
- this(table, null);
+ public TerminalOutput getResult(boolean plain) {
+ return () -> sqlQueryResultItems.stream()
+ .map(x -> x.decorate(plain).toTerminalString())
+ .collect(Collectors.joining(""));
}
/**
- * Constructor.
- *
- * @param message non null result message.
+ * Builder for {@link SqlQueryResult}.
*/
- public SqlQueryResult(String message) {
- this(null, message);
- }
+ static class SqlQueryResultBuilder {
+ private final List<SqlQueryResultItem> sqlQueryResultItems = new
ArrayList<>();
- private SqlQueryResult(@Nullable Table<String> table, String message) {
- this.table = table;
- this.message = message;
- }
+ /**
+ * Add table to query result.
+ */
+ void addTable(Table<String> table) {
+ sqlQueryResultItems.add(new SqlQueryResultTable(table));
+ }
- /**
- * SQL query result provider.
- *
- * @param tableDecorator instance of {@link TableDecorator}.
- * @param messageDecorator decorator of message.
- * @return terminal output of non-null field of class.
- */
- public TerminalOutput getResult(TableDecorator tableDecorator,
- Decorator<String, TerminalOutput>
messageDecorator) {
- if (table != null) {
- return tableDecorator.decorate(table);
+ /**
+ * Add message to query result.
+ */
+ void addMessage(String message) {
+ sqlQueryResultItems.add(new SqlQueryResultMessage(message + "\n"));
+ }
+
+ public SqlQueryResult build() {
+ return new SqlQueryResult(sqlQueryResultItems);
}
- return messageDecorator.decorate(message);
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResultItem.java
similarity index 55%
copy from
modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
copy to
modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResultItem.java
index ad2b53f757..0cebbeb7de 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResultItem.java
@@ -15,26 +15,18 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.cli.decorators;
+package org.apache.ignite.internal.cli.sql;
-import org.apache.ignite.internal.cli.core.decorator.Decorator;
import org.apache.ignite.internal.cli.core.decorator.TerminalOutput;
-import org.apache.ignite.internal.cli.sql.SqlQueryResult;
/**
- * Composite decorator for {@link SqlQueryResult}.
+ * An object that represents a single item of the SQL query result.
*/
-public class SqlQueryResultDecorator implements Decorator<SqlQueryResult,
TerminalOutput> {
- private final TableDecorator tableDecorator;
-
- private final DefaultDecorator<String> messageDecorator = new
DefaultDecorator<>();
-
- public SqlQueryResultDecorator(boolean plain) {
- this.tableDecorator = new TableDecorator(plain);
- }
-
- @Override
- public TerminalOutput decorate(SqlQueryResult data) {
- return data.getResult(tableDecorator, messageDecorator);
- }
+interface SqlQueryResultItem {
+ /**
+ * Decorates the item.
+ *
+ * @param plain Whether to use plain output.
+ */
+ TerminalOutput decorate(boolean plain);
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResultMessage.java
similarity index 57%
copy from
modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
copy to
modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResultMessage.java
index ad2b53f757..ec2e0a876e 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResultMessage.java
@@ -15,26 +15,24 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.cli.decorators;
+package org.apache.ignite.internal.cli.sql;
-import org.apache.ignite.internal.cli.core.decorator.Decorator;
import org.apache.ignite.internal.cli.core.decorator.TerminalOutput;
-import org.apache.ignite.internal.cli.sql.SqlQueryResult;
+import org.apache.ignite.internal.cli.decorators.DefaultDecorator;
/**
- * Composite decorator for {@link SqlQueryResult}.
+ * A message in the SQL query result.
*/
-public class SqlQueryResultDecorator implements Decorator<SqlQueryResult,
TerminalOutput> {
- private final TableDecorator tableDecorator;
+class SqlQueryResultMessage implements SqlQueryResultItem {
- private final DefaultDecorator<String> messageDecorator = new
DefaultDecorator<>();
+ private final String message;
- public SqlQueryResultDecorator(boolean plain) {
- this.tableDecorator = new TableDecorator(plain);
+ SqlQueryResultMessage(String message) {
+ this.message = message;
}
@Override
- public TerminalOutput decorate(SqlQueryResult data) {
- return data.getResult(tableDecorator, messageDecorator);
+ public TerminalOutput decorate(boolean plain) {
+ return new DefaultDecorator<String>().decorate(message);
}
}
diff --git
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResultTable.java
similarity index 57%
copy from
modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
copy to
modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResultTable.java
index ad2b53f757..013054e2cd 100644
---
a/modules/cli/src/main/java/org/apache/ignite/internal/cli/decorators/SqlQueryResultDecorator.java
+++
b/modules/cli/src/main/java/org/apache/ignite/internal/cli/sql/SqlQueryResultTable.java
@@ -15,26 +15,25 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.cli.decorators;
+package org.apache.ignite.internal.cli.sql;
-import org.apache.ignite.internal.cli.core.decorator.Decorator;
import org.apache.ignite.internal.cli.core.decorator.TerminalOutput;
-import org.apache.ignite.internal.cli.sql.SqlQueryResult;
+import org.apache.ignite.internal.cli.decorators.TableDecorator;
+import org.apache.ignite.internal.cli.sql.table.Table;
/**
- * Composite decorator for {@link SqlQueryResult}.
+ * A table in the SQL query result.
*/
-public class SqlQueryResultDecorator implements Decorator<SqlQueryResult,
TerminalOutput> {
- private final TableDecorator tableDecorator;
+class SqlQueryResultTable implements SqlQueryResultItem {
- private final DefaultDecorator<String> messageDecorator = new
DefaultDecorator<>();
+ private final Table<String> table;
- public SqlQueryResultDecorator(boolean plain) {
- this.tableDecorator = new TableDecorator(plain);
+ SqlQueryResultTable(Table<String> table) {
+ this.table = table;
}
@Override
- public TerminalOutput decorate(SqlQueryResult data) {
- return data.getResult(tableDecorator, messageDecorator);
+ public TerminalOutput decorate(boolean plain) {
+ return new TableDecorator(plain).decorate(table);
}
}