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);
     }
 }

Reply via email to