diff --git a/RunQueriesResult.java b/RunQueriesResult.java
new file mode 100644
index 00000000..feac440a
--- /dev/null
+++ b/RunQueriesResult.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 Vaticle
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package com.vaticle.typedb.console;
+
+public class RunQueriesResult {
+ private final boolean success;
+ private final boolean hasChanges;
+
+ public RunQueriesResult(boolean success, boolean hasChanges) {
+ this.success = success;
+ this.hasChanges = hasChanges;
+ }
+
+ public static RunQueriesResult error() {
+ return new RunQueriesResult(false, false);
+ }
+
+ public boolean success() {
+ return success;
+ }
+
+ public boolean hasChanges() {
+ return hasChanges;
+ }
+}
diff --git a/TypeDBConsole.java b/TypeDBConsole.java
index 08ebaf5d..b78a7fd4 100644
--- a/TypeDBConsole.java
+++ b/TypeDBConsole.java
@@ -38,6 +38,7 @@
import com.vaticle.typedb.console.common.Printer;
import com.vaticle.typedb.console.common.exception.TypeDBConsoleException;
import com.vaticle.typeql.lang.TypeQL;
+import com.vaticle.typeql.lang.common.TypeQLArg;
import com.vaticle.typeql.lang.common.exception.TypeQLException;
import com.vaticle.typeql.lang.query.TypeQLCompute;
import com.vaticle.typeql.lang.query.TypeQLDefine;
@@ -80,6 +81,8 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Stream;
@@ -102,6 +105,7 @@ public class TypeDBConsole {
private final Printer printer;
private ExecutorService executorService;
private Terminal terminal;
+ private boolean hasUncommittedChanges = false;
private TypeDBConsole(Printer printer) {
this.printer = printer;
@@ -275,16 +279,18 @@ private boolean transactionREPL(TypeDBClient client, String database, TypeDBSess
.terminal(terminal)
.variable(LineReader.HISTORY_FILE, TRANSACTION_HISTORY_FILE)
.build();
- StringBuilder prompt = new StringBuilder(database + "::" + sessionType.name().toLowerCase() + "::" + transactionType.name().toLowerCase());
- if (options.isCluster() && options.asCluster().readAnyReplica().isPresent() && options.asCluster().readAnyReplica().get())
- prompt.append("[any-replica]");
- prompt.append("> ");
+ StringBuilder promptBuilder = new StringBuilder(database + "::" + sessionType.name().toLowerCase() + "::" + transactionType.name().toLowerCase());
+ if (options.isCluster() && options.asCluster().readAnyReplica().isPresent() && options.asCluster().readAnyReplica().get()) {
+ promptBuilder.append("[any-replica]");
+ }
try (TypeDBSession session = client.session(database, sessionType, options);
TypeDBTransaction tx = session.transaction(transactionType, options)) {
+ hasUncommittedChanges = false;
while (true) {
Either command;
try {
- command = TransactionREPLCommand.readCommand(reader, prompt.toString());
+ String prompt = hasUncommittedChanges ? promptBuilder + "*> " : promptBuilder + "> ";
+ command = TransactionREPLCommand.readCommand(reader, prompt);
} catch (InterruptedException e) {
break;
}
@@ -308,7 +314,8 @@ private boolean transactionREPL(TypeDBClient client, String database, TypeDBSess
runClose(tx);
break;
} else if (replCommand.isSource()) {
- runSource(tx, replCommand.asSource().file(), replCommand.asSource().printAnswers());
+ RunQueriesResult result = runSource(tx, replCommand.asSource().file(), replCommand.asSource().printAnswers());
+ hasUncommittedChanges = result.hasChanges();
} else if (replCommand.isQuery()) {
runQueriesPrintAnswers(tx, replCommand.asQuery().query());
}
@@ -394,10 +401,10 @@ private boolean runInlineCommandMode(CLIOptions options, List inlineComm
break;
} else if (txCommand.first().isSource()) {
TransactionREPLCommand.Source source = txCommand.first().asSource();
- boolean success = runSource(tx, source.file(), source.printAnswers());
+ boolean success = runSource(tx, source.file(), source.printAnswers()).success();
if (!success) return false;
} else if (txCommand.first().isQuery()) {
- boolean success = runQueriesPrintAnswers(tx, txCommand.first().asQuery().query());
+ boolean success = runQueriesPrintAnswers(tx, txCommand.first().asQuery().query()).success();
if (!success) return false;
} else {
printer.error("Command is not available while running console script.");
@@ -583,30 +590,32 @@ private void runClose(TypeDBTransaction tx) {
else printer.info("Transaction closed");
}
- private boolean runSource(TypeDBTransaction tx, String file, boolean printAnswers) {
+ private RunQueriesResult runSource(TypeDBTransaction tx, String file, boolean printAnswers) {
try {
String queryString = new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8);
if (printAnswers) return runQueriesPrintAnswers(tx, queryString);
else return runQueries(tx, queryString);
} catch (IOException e) {
printer.error("Failed to open file '" + file + "'");
- return false;
+ return RunQueriesResult.error();
}
}
- private boolean runQueries(TypeDBTransaction tx, String queryString) {
+ private RunQueriesResult runQueries(TypeDBTransaction tx, String queryString) {
Optional> queries = parseQueries(queryString);
- if (!queries.isPresent()) return false;
+ if (!queries.isPresent()) return RunQueriesResult.error();
CompletableFuture.allOf(queries.get().stream().map(query -> runQuery(tx, query))
.toArray(CompletableFuture[]::new)).join();
- return true;
+ boolean hasChanges = queries.get().stream().anyMatch(query -> query.type() == TypeQLArg.QueryType.WRITE);
+ return new RunQueriesResult(true, hasChanges);
}
- private boolean runQueriesPrintAnswers(TypeDBTransaction tx, String queryString) {
+ private RunQueriesResult runQueriesPrintAnswers(TypeDBTransaction tx, String queryString) {
Optional> queries = parseQueries(queryString);
- if (!queries.isPresent()) return false;
+ if (!queries.isPresent()) return RunQueriesResult.error();
queries.get().forEach(query -> runQueryPrintAnswers(tx, query));
- return true;
+ boolean hasChanges = queries.get().stream().anyMatch(query -> query.type() == TypeQLArg.QueryType.WRITE);
+ return new RunQueriesResult(true, hasChanges);
}
@SuppressWarnings("CheckReturnValue")
@@ -651,18 +660,37 @@ private void runQueryPrintAnswers(TypeDBTransaction tx, TypeQLQuery query) {
if (query instanceof TypeQLDefine) {
tx.query().define(query.asDefine()).get();
printer.info("Concepts have been defined");
+ hasUncommittedChanges = true;
} else if (query instanceof TypeQLUndefine) {
tx.query().undefine(query.asUndefine()).get();
printer.info("Concepts have been undefined");
+ hasUncommittedChanges = true;
} else if (query instanceof TypeQLInsert) {
Stream result = tx.query().insert(query.asInsert());
- printCancellableResult(result, x -> printer.conceptMap(x, tx));
+ AtomicBoolean changed = new AtomicBoolean(false);
+ printCancellableResult(result, x -> {
+ changed.set(true);
+ printer.conceptMap(x, tx);
+ });
+ if (changed.get()) hasUncommittedChanges = true;
} else if (query instanceof TypeQLDelete) {
- tx.query().delete(query.asDelete()).get();
- printer.info("Concepts have been deleted");
+ long limitedCount = tx.query().match(query.asDelete().match()).limit(20).count();
+ if (limitedCount > 0) {
+ tx.query().delete(query.asDelete()).get();
+ if (limitedCount == 20) printer.info("Deleted from 20+ matched answers");
+ else printer.info("Deleted from " + limitedCount + " matched answers");
+ hasUncommittedChanges = true;
+ } else {
+ printer.info("No concepts were matched");
+ }
} else if (query instanceof TypeQLUpdate) {
Stream result = tx.query().update(query.asUpdate());
- printCancellableResult(result, x -> printer.conceptMap(x, tx));
+ AtomicBoolean changed = new AtomicBoolean(false);
+ printCancellableResult(result, x -> {
+ changed.set(true);
+ printer.conceptMap(x, tx);
+ });
+ if (changed.get()) hasUncommittedChanges = true;
} else if (query instanceof TypeQLMatch) {
Stream result = tx.query().match(query.asMatch());
printCancellableResult(result, x -> printer.conceptMap(x, tx));