You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ap...@apache.org on 2023/10/02 12:57:46 UTC

[ignite-3] branch main updated: IGNITE-20494 Fix sql command that does not enter repl (#2632)

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 de28ccedc3 IGNITE-20494 Fix sql command that does not enter repl (#2632)
de28ccedc3 is described below

commit de28ccedc3708e405b156bda4fb69d0c6da1d8c5
Author: Vadim Pakhnushev <86...@users.noreply.github.com>
AuthorDate: Mon Oct 2 15:57:40 2023 +0300

    IGNITE-20494 Fix sql command that does not enter repl (#2632)
---
 .../cli/commands/sql/ItSqlReplCommandTest.java     |  47 ++++++
 .../java/org/apache/ignite/internal/cli/Main.java  |   4 +-
 .../internal/cli/commands/sql/SqlReplCommand.java  |   3 +-
 .../cli/core/repl/executor/ReplExecutor.java       | 181 +--------------------
 .../{ReplExecutor.java => ReplExecutorImpl.java}   |   5 +-
 .../core/repl/executor/ReplExecutorProvider.java   |  28 +---
 ...Provider.java => ReplExecutorProviderImpl.java} |   5 +-
 7 files changed, 63 insertions(+), 210 deletions(-)

diff --git a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java
index 89291c22dc..5e0c17f2a2 100644
--- a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java
+++ b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/sql/ItSqlReplCommandTest.java
@@ -19,7 +19,10 @@ package org.apache.ignite.internal.cli.commands.sql;
 
 import static org.junit.jupiter.api.Assertions.assertAll;
 
+import io.micronaut.context.annotation.Bean;
+import io.micronaut.context.annotation.Replaces;
 import org.apache.ignite.internal.cli.commands.CliCommandTestInitializedIntegrationBase;
+import org.apache.ignite.internal.cli.core.repl.executor.ReplExecutorProvider;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.Test;
 
@@ -41,4 +44,48 @@ class ItSqlReplCommandTest extends CliCommandTestInitializedIntegrationBase {
                 () -> assertErrOutputContains("File with command not found")
         );
     }
+
+    @Test
+    void secondInvocationScript() {
+        execute("CREATE TABLE T(K INT PRIMARY KEY)", "--jdbc-url", JDBC_URL);
+
+        assertAll(
+                () -> assertOutputContains("Updated 0 rows."),
+                this::assertErrOutputIsEmpty
+        );
+
+        resetOutput();
+
+        execute("--jdbc-url", JDBC_URL);
+
+        assertAll(
+                this::assertOutputIsEmpty,
+                this::assertErrOutputIsEmpty
+        );
+    }
+
+    @Test
+    void secondInvocationFile() {
+        execute("-f", "nonexisting", "--jdbc-url", JDBC_URL);
+
+        assertAll(
+                this::assertOutputIsEmpty,
+                () -> assertErrOutputContains("File with command not found")
+        );
+
+        resetOutput();
+
+        execute("--jdbc-url", JDBC_URL);
+
+        assertAll(
+                this::assertOutputIsEmpty,
+                this::assertErrOutputIsEmpty
+        );
+    }
+
+    @Bean
+    @Replaces(ReplExecutorProvider.class)
+    public ReplExecutorProvider replExecutorProvider() {
+        return () -> repl -> {};
+    }
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
index bb05e76083..f198c28060 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/Main.java
@@ -33,7 +33,7 @@ import org.apache.ignite.internal.cli.commands.TopLevelCliCommand;
 import org.apache.ignite.internal.cli.config.ConfigDefaultValueProvider;
 import org.apache.ignite.internal.cli.config.StateFolderProvider;
 import org.apache.ignite.internal.cli.core.exception.handler.PicocliExecutionExceptionHandler;
-import org.apache.ignite.internal.cli.core.repl.executor.ReplExecutorProvider;
+import org.apache.ignite.internal.cli.core.repl.executor.ReplExecutorProviderImpl;
 import org.fusesource.jansi.AnsiConsole;
 import picocli.CommandLine;
 import picocli.CommandLine.Help.Ansi;
@@ -79,7 +79,7 @@ public class Main {
 
     /** Needed for immediate REPL mode and for running a command which will stay in REPL mode so we need to init it once. */
     private static void initReplExecutor(MicronautFactory micronautFactory) throws Exception {
-        ReplExecutorProvider replExecutorProvider = micronautFactory.create(ReplExecutorProvider.class);
+        ReplExecutorProviderImpl replExecutorProvider = micronautFactory.create(ReplExecutorProviderImpl.class);
         replExecutorProvider.injectFactory(micronautFactory);
     }
 
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/sql/SqlReplCommand.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/sql/SqlReplCommand.java
index a2a886488b..1c805fc74a 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/sql/SqlReplCommand.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/commands/sql/SqlReplCommand.java
@@ -78,7 +78,8 @@ public class SqlReplCommand extends BaseCommand implements Runnable {
         @Parameters(index = "0", description = "SQL query to execute", defaultValue = Option.NULL_VALUE)
         private String command;
 
-        @Option(names = {SCRIPT_FILE_OPTION, SCRIPT_FILE_OPTION_SHORT}, description = SCRIPT_FILE_OPTION_SHORT)
+        @Option(names = {SCRIPT_FILE_OPTION, SCRIPT_FILE_OPTION_SHORT}, description = SCRIPT_FILE_OPTION_SHORT,
+                defaultValue = Option.NULL_VALUE)
         private File file;
     }
 
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java
index a22184a356..41147be168 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java
@@ -17,187 +17,12 @@
 
 package org.apache.ignite.internal.cli.core.repl.executor;
 
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.function.Supplier;
-import org.apache.ignite.internal.cli.commands.node.NodeNameOrUrl;
-import org.apache.ignite.internal.cli.config.StateFolderProvider;
-import org.apache.ignite.internal.cli.core.converters.NodeNameOrUrlConverter;
-import org.apache.ignite.internal.cli.core.exception.ExceptionHandlers;
-import org.apache.ignite.internal.cli.core.exception.handler.PicocliExecutionExceptionHandler;
-import org.apache.ignite.internal.cli.core.exception.handler.ReplExceptionHandlers;
-import org.apache.ignite.internal.cli.core.flow.question.JlineQuestionWriterReaderFactory;
-import org.apache.ignite.internal.cli.core.flow.question.QuestionAskerFactory;
 import org.apache.ignite.internal.cli.core.repl.Repl;
-import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleterActivationPoint;
-import org.apache.ignite.internal.cli.core.repl.completer.DynamicCompleterRegistry;
-import org.apache.ignite.internal.cli.core.repl.completer.filter.CompleterFilter;
-import org.apache.ignite.internal.cli.core.repl.completer.filter.DeployUnitsOptionsFilter;
-import org.apache.ignite.internal.cli.core.repl.completer.filter.DynamicCompleterFilter;
-import org.apache.ignite.internal.cli.core.repl.completer.filter.NonRepeatableOptionsFilter;
-import org.apache.ignite.internal.cli.core.repl.completer.filter.ShortOptionsFilter;
-import org.apache.ignite.internal.cli.core.repl.context.CommandLineContextProvider;
-import org.apache.ignite.internal.cli.core.repl.expander.NoopExpander;
-import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
-import org.jline.console.impl.SystemRegistryImpl;
-import org.jline.reader.Completer;
-import org.jline.reader.LineReader;
-import org.jline.reader.LineReader.SuggestionType;
-import org.jline.reader.LineReaderBuilder;
-import org.jline.reader.MaskingCallback;
-import org.jline.reader.Parser;
-import org.jline.reader.impl.DefaultParser;
-import org.jline.terminal.Terminal;
-import org.jline.widget.AutosuggestionWidgets;
-import org.jline.widget.TailTipWidgets;
-import picocli.CommandLine;
-import picocli.CommandLine.IDefaultValueProvider;
-import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
 
 /**
  * Executor of {@link Repl}.
  */
-public class ReplExecutor {
-
-    private final Parser parser = new DefaultParser().escapeChars(null);
-
-    private final Supplier<Path> workDirProvider = () -> Paths.get(System.getProperty("user.dir"));
-
-    private final AtomicBoolean interrupted = new AtomicBoolean();
-
-    private final ExceptionHandlers exceptionHandlers = new ReplExceptionHandlers(interrupted::set);
-
-    private final PicocliCommandsFactory factory;
-
-    private final Terminal terminal;
-
-    private final NodeNameRegistry nodeNameRegistry;
-
-    /**
-     * Constructor.
-     *
-     * @param commandsFactory picocli commands factory.
-     * @param terminal terminal instance.
-     * @param nodeNameRegistry node name registry.
-     */
-    public ReplExecutor(PicocliCommandsFactory commandsFactory, Terminal terminal, NodeNameRegistry nodeNameRegistry) {
-        this.factory = commandsFactory;
-        this.terminal = terminal;
-        this.nodeNameRegistry = nodeNameRegistry;
-    }
-
-    private static void createTailTipWidgets(SystemRegistryImpl registry, LineReader reader) {
-        TailTipWidgets widgets = new TailTipWidgets(reader, registry::commandDescription, 5,
-                TailTipWidgets.TipType.COMPLETER);
-        widgets.enable();
-        // Workaround for the scroll truncation issue in windows terminal
-        // Turn off tailtip widgets before printing to the output
-        CommandLineContextProvider.setPrintWrapper(printer -> {
-            widgets.disable();
-            printer.run();
-            widgets.enable();
-        });
-        // Workaround for jline issue where TailTipWidgets will produce NPE when passed a bracket
-        registry.setScriptDescription(cmdLine -> null);
-    }
-
-    /**
-     * Executor method. This is thread blocking method, until REPL stop executing.
-     *
-     * @param repl data class of executing REPL.
-     */
-    public void execute(Repl repl) {
-        try {
-            repl.customizeTerminal(terminal);
-
-            IgnitePicocliCommands picocliCommands = createPicocliCommands(repl);
-            SystemRegistryImpl registry = new SystemRegistryImpl(parser, terminal, workDirProvider, null);
-            registry.setCommandRegistries(picocliCommands);
-
-            LineReader reader = createReader(
-                    repl.getCompleter() != null
-                            ? repl.getCompleter()
-                            : registry.completer()
-            );
-            if (repl.getHistoryFileName() != null) {
-                reader.variable(LineReader.HISTORY_FILE, StateFolderProvider.getStateFile(repl.getHistoryFileName()));
-            }
-
-            RegistryCommandExecutor executor = new RegistryCommandExecutor(parser, picocliCommands.getCmd());
-
-            setupWidgets(repl, registry, reader);
-
-            repl.getEventListeningActivationPoint().subscribe();
-
-            repl.onStart();
-
-            while (!interrupted.get()) {
-                try {
-                    executor.cleanUp();
-                    String prompt = repl.getPromptProvider().getPrompt();
-                    String line = reader.readLine(prompt, null, (MaskingCallback) null, null);
-                    if (line.isEmpty()) {
-                        continue;
-                    }
-
-                    repl.getPipeline(executor, exceptionHandlers, line).runPipeline();
-                } catch (Throwable t) {
-                    exceptionHandlers.handleException(System.err::println, t);
-                }
-            }
-            reader.getHistory().save();
-        } catch (Throwable t) {
-            exceptionHandlers.handleException(System.err::println, t);
-        }
-    }
-
-    private void setupWidgets(Repl repl, SystemRegistryImpl registry, LineReader reader) {
-        if (repl.isTailTipWidgetsEnabled()) {
-            createTailTipWidgets(registry, reader);
-        } else if (repl.isAutosuggestionsWidgetsEnabled()) {
-            AutosuggestionWidgets widgets = new AutosuggestionWidgets(reader);
-            widgets.enable();
-        }
-
-        QuestionAskerFactory.setWriterReaderFactory(new JlineQuestionWriterReaderFactory(terminal));
-    }
-
-    private LineReader createReader(Completer completer) {
-        LineReader result = LineReaderBuilder.builder()
-                .terminal(terminal)
-                .completer(completer)
-                .parser(parser)
-                .expander(new NoopExpander())
-                .variable(LineReader.LIST_MAX, 50)   // max tab completion candidates
-                .build();
-        result.setAutosuggestion(SuggestionType.COMPLETER);
-        return result;
-    }
-
-    private IgnitePicocliCommands createPicocliCommands(Repl repl) throws Exception {
-        CommandLine cmd = new CommandLine(repl.commandClass(), factory);
-        IDefaultValueProvider defaultValueProvider = repl.defaultValueProvider();
-        if (defaultValueProvider != null) {
-            cmd.setDefaultValueProvider(defaultValueProvider);
-        }
-        CommandLineContextProvider.setCmd(cmd);
-        cmd.setExecutionExceptionHandler(new PicocliExecutionExceptionHandler(exceptionHandlers));
-        cmd.registerConverter(NodeNameOrUrl.class, new NodeNameOrUrlConverter(nodeNameRegistry));
-        cmd.setTrimQuotes(true);
-
-        DynamicCompleterRegistry completerRegistry = factory.create(DynamicCompleterRegistry.class);
-        DynamicCompleterActivationPoint activationPoint = factory.create(DynamicCompleterActivationPoint.class);
-        activationPoint.activateDynamicCompleter(completerRegistry);
-
-        DynamicCompleterFilter dynamicCompleterFilter = factory.create(DynamicCompleterFilter.class);
-        List<CompleterFilter> filters = List.of(dynamicCompleterFilter,
-                new ShortOptionsFilter(),
-                new NonRepeatableOptionsFilter(cmd.getCommandSpec()),
-                new DeployUnitsOptionsFilter()
-        );
-
-        return new IgnitePicocliCommands(cmd, completerRegistry, filters);
-    }
+@FunctionalInterface
+public interface ReplExecutor {
+    void execute(Repl repl);
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
similarity index 97%
copy from modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java
copy to modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
index a22184a356..cf06e43725 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutor.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorImpl.java
@@ -59,7 +59,7 @@ import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
 /**
  * Executor of {@link Repl}.
  */
-public class ReplExecutor {
+public class ReplExecutorImpl implements ReplExecutor {
 
     private final Parser parser = new DefaultParser().escapeChars(null);
 
@@ -82,7 +82,7 @@ public class ReplExecutor {
      * @param terminal terminal instance.
      * @param nodeNameRegistry node name registry.
      */
-    public ReplExecutor(PicocliCommandsFactory commandsFactory, Terminal terminal, NodeNameRegistry nodeNameRegistry) {
+    public ReplExecutorImpl(PicocliCommandsFactory commandsFactory, Terminal terminal, NodeNameRegistry nodeNameRegistry) {
         this.factory = commandsFactory;
         this.terminal = terminal;
         this.nodeNameRegistry = nodeNameRegistry;
@@ -108,6 +108,7 @@ public class ReplExecutor {
      *
      * @param repl data class of executing REPL.
      */
+    @Override
     public void execute(Repl repl) {
         try {
             repl.customizeTerminal(terminal);
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProvider.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProvider.java
index 582a175d61..3eef59870f 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProvider.java
@@ -17,32 +17,10 @@
 
 package org.apache.ignite.internal.cli.core.repl.executor;
 
-import io.micronaut.configuration.picocli.MicronautFactory;
-import jakarta.inject.Inject;
-import jakarta.inject.Singleton;
-import org.apache.ignite.internal.cli.core.repl.registry.NodeNameRegistry;
-import org.jline.terminal.Terminal;
-import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
-
 /**
  * Provider of {@link ReplExecutor}.
  */
-@Singleton
-public class ReplExecutorProvider {
-    private PicocliCommandsFactory factory;
-
-    @Inject
-    private Terminal terminal;
-
-    @Inject
-    private NodeNameRegistry nodeNameRegistry;
-
-    public ReplExecutor get() {
-        return new ReplExecutor(factory, terminal, nodeNameRegistry);
-    }
-
-    public void injectFactory(MicronautFactory micronautFactory) {
-        factory = new PicocliCommandsFactory(micronautFactory);
-        factory.setTerminal(terminal);
-    }
+@FunctionalInterface
+public interface ReplExecutorProvider {
+    ReplExecutor get();
 }
diff --git a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProvider.java b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProviderImpl.java
similarity index 90%
copy from modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProvider.java
copy to modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProviderImpl.java
index 582a175d61..8a66b5a485 100644
--- a/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProvider.java
+++ b/modules/cli/src/main/java/org/apache/ignite/internal/cli/core/repl/executor/ReplExecutorProviderImpl.java
@@ -28,7 +28,7 @@ import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory;
  * Provider of {@link ReplExecutor}.
  */
 @Singleton
-public class ReplExecutorProvider {
+public class ReplExecutorProviderImpl implements ReplExecutorProvider {
     private PicocliCommandsFactory factory;
 
     @Inject
@@ -37,8 +37,9 @@ public class ReplExecutorProvider {
     @Inject
     private NodeNameRegistry nodeNameRegistry;
 
+    @Override
     public ReplExecutor get() {
-        return new ReplExecutor(factory, terminal, nodeNameRegistry);
+        return new ReplExecutorImpl(factory, terminal, nodeNameRegistry);
     }
 
     public void injectFactory(MicronautFactory micronautFactory) {