org.apache.flink.table.client.cli.CliClient Maven / Gradle / Ivy
/*
* 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.flink.table.client.cli;
import org.apache.flink.table.client.SqlClientException;
import org.apache.flink.table.client.cli.SqlCommandParser.SqlCommandCall;
import org.apache.flink.table.client.gateway.DatabaseDesc;
import org.apache.flink.table.client.gateway.Executor;
import org.apache.flink.table.client.gateway.ProgramTargetDescriptor;
import org.apache.flink.table.client.gateway.ResultDescriptor;
import org.apache.flink.table.client.gateway.SessionContext;
import org.apache.flink.table.client.gateway.SqlExecutionException;
import org.apache.flink.table.client.gateway.TableDesc;
import org.apache.flink.table.client.gateway.TypedResult;
import org.apache.flink.table.client.gateway.local.result.DynamicResult;
import org.apache.flink.types.Row;
import org.apache.flink.util.Preconditions;
import org.jline.reader.EndOfFileException;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.MaskingCallback;
import org.jline.reader.UserInterruptException;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
import org.jline.utils.AttributedString;
import org.jline.utils.AttributedStringBuilder;
import org.jline.utils.AttributedStyle;
import org.jline.utils.InfoCmp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOError;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* SQL CLI client.
*/
public class CliClient {
private static final Logger LOG = LoggerFactory.getLogger(CliClient.class);
private final Executor executor;
private final SessionContext context;
private final Terminal terminal;
private final LineReader lineReader;
private final String prompt;
private boolean isRunning;
private static final int PLAIN_TERMINAL_WIDTH = 80;
private static final int PLAIN_TERMINAL_HEIGHT = 30;
private static final int SOURCE_MAX_SIZE = 50_000;
public CliClient(SessionContext context, Executor executor) {
this.context = context;
this.executor = executor;
try {
// initialize terminal
terminal = TerminalBuilder.builder()
.name(CliStrings.CLI_NAME)
.build();
// make space from previous output and test the writer
terminal.writer().println();
terminal.writer().flush();
} catch (IOException e) {
throw new SqlClientException("Error opening command line interface.", e);
}
// initialize line lineReader
lineReader = LineReaderBuilder.builder()
.terminal(terminal)
.appName(CliStrings.CLI_NAME)
.parser(new SqlMultiLineParser())
.completer(new SqlCompleter(context, executor))
.build();
// this option is disabled for now for correct backslash escaping
// a "SELECT '\'" query should return a string with a backslash
lineReader.option(LineReader.Option.DISABLE_EVENT_EXPANSION, true);
// set strict "typo" distance between words when doing code completion
lineReader.setVariable(LineReader.ERRORS, 1);
// perform code completion case insensitive
lineReader.option(LineReader.Option.CASE_INSENSITIVE, true);
// create prompt
prompt = new AttributedStringBuilder()
.style(AttributedStyle.DEFAULT.foreground(AttributedStyle.GREEN))
.append("Flink SQL")
.style(AttributedStyle.DEFAULT)
.append("> ")
.toAnsi();
}
public Terminal getTerminal() {
return terminal;
}
public SessionContext getContext() {
return context;
}
public void clearTerminal() {
if (isPlainTerminal()) {
for (int i = 0; i < 200; i++) { // large number of empty lines
terminal.writer().println();
}
} else {
terminal.puts(InfoCmp.Capability.clear_screen);
}
}
public boolean isPlainTerminal() {
// check if terminal width can be determined
// e.g. IntelliJ IDEA terminal supports only a plain terminal
return terminal.getWidth() == 0 && terminal.getHeight() == 0;
}
public int getWidth() {
if (isPlainTerminal()) {
return PLAIN_TERMINAL_WIDTH;
}
return terminal.getWidth();
}
public int getHeight() {
if (isPlainTerminal()) {
return PLAIN_TERMINAL_HEIGHT;
}
return terminal.getHeight();
}
public Executor getExecutor() {
return executor;
}
/**
* Opens the interactive CLI shell.
*/
public void open() {
isRunning = true;
// print welcome
terminal.writer().append(CliStrings.MESSAGE_WELCOME);
// begin reading loop
while (isRunning) {
// make some space to previous command
terminal.writer().append("\n");
terminal.flush();
final String line;
try {
line = lineReader.readLine(prompt, null, (MaskingCallback) null, null);
} catch (UserInterruptException e) {
// user cancelled line with Ctrl+C
continue;
} catch (EndOfFileException | IOError e) {
// user cancelled application with Ctrl+D or kill
break;
} catch (Throwable t) {
throw new SqlClientException("Could not read from command line.", t);
}
if (line == null) {
continue;
}
final List statements = splitSemiColon(line);
for (String statement : statements) {
final Optional cmdCall = parseCommand(statement);
if (!cmdCall.isPresent()) {
// if we found a invalid command, issue error message and break
break;
}
cmdCall.ifPresent(this::callCommand);
}
}
}
public void submitSQLFile(URL sqlFile) {
isRunning = true;
final String content;
try {
final Path path = Paths.get(sqlFile.toURI());
byte[] encoded = Files.readAllBytes(path);
content = new String(encoded, Charset.defaultCharset());
} catch (IOException | URISyntaxException e) {
printExecutionException(e);
return;
}
final List statements = splitSemiColon(content);
for (String statement : statements) {
final Optional cmdCall = parseCommand(statement);
if (!cmdCall.isPresent()) {
break;
}
cmdCall.ifPresent(this::callCommand);
}
}
/**
* Submits a SQL update statement and prints status information and/or errors on the terminal.
*
* @param statement SQL update statement
* @return flag to indicate if the submission was successful or not
*/
public boolean submitUpdate(String statement) {
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_WILL_EXECUTE).toAnsi());
terminal.writer().println(new AttributedString(statement).toString());
terminal.flush();
final Optional parsedStatement = parseCommand(statement);
// only support INSERT INTO
return parsedStatement.map(cmdCall -> {
switch (cmdCall.command) {
case INSERT_INTO:
case DELETE_FROM:
return callSqlUpdate(cmdCall);
default:
printError(CliStrings.MESSAGE_UNSUPPORTED_SQL);
return false;
}
}).orElse(false);
}
// --------------------------------------------------------------------------------------------
private Optional parseCommand(String line) {
final Optional parsedLine = SqlCommandParser.parse(line);
if (!parsedLine.isPresent()) {
printError(CliStrings.MESSAGE_UNKNOWN_SQL);
}
return parsedLine;
}
private void callCommand(SqlCommandCall cmdCall) {
switch (cmdCall.command) {
case QUIT:
callQuit();
break;
case CLEAR:
callClear();
break;
case RESET:
callReset();
break;
case SET:
callSet(cmdCall);
break;
case HELP:
callHelp();
break;
case SHOW_CATALOGS:
callShowCatalogs();
break;
case SHOW_DATABASES:
callShowDatabases();
break;
case SHOW_TABLES:
callShowTables();
break;
case SHOW_VIEWS:
callShowViews();
break;
case SHOW_FUNCTIONS:
callShowFunctions();
break;
case SHOW_PARTITIONS:
callShowPartitons(cmdCall);
break;
case SHOW_CREATE_TABLE:
callShowCreateTable(cmdCall);
break;
case USE:
callUseDatabase(cmdCall);
break;
case USE_CATALOG:
callUseCatalog(cmdCall);
break;
case DESCRIBE:
case DESC:
callDescribe(cmdCall);
break;
case DESCRIBE_DATABASE:
case DESC_DATABASE:
callDescribeDatabase(cmdCall);
break;
case EXPLAIN:
callExplain(cmdCall);
break;
case SELECT:
callSelect(cmdCall);
break;
case INSERT_INTO:
case INSERT_OVERWRITE:
case DELETE_FROM:
callSqlUpdate(cmdCall);
break;
case CREATE_TABLE:
callCreateTable(cmdCall);
break;
case DROP_TABLE:
callDropTable(cmdCall);
break;
case CREATE_VIEW:
callCreateView(cmdCall);
break;
case DROP_VIEW:
callDropView(cmdCall);
break;
case CREATE_FUNCTION:
callCreateFunction(cmdCall);
break;
case DROP_FUNCTION:
callDropFunction(cmdCall);
break;
case CREATE_DATABASE:
callCreateDatabase(cmdCall);
break;
case DROP_DATABASE:
callDropDatabase(cmdCall);
break;
case SOURCE:
callSource(cmdCall);
break;
case ALTER_DATABASE:
callAlterDatabase(cmdCall);
break;
case ALTER_TABLE:
callAlterTable(cmdCall);
break;
case BOND:
callBond(cmdCall);
break;
case ANALYZE:
callAnalyze(cmdCall);
break;
case IMPORT:
callImport(cmdCall);
break;
default:
throw new SqlClientException("Unsupported command: " + cmdCall.command);
}
}
private Optional> callBondCommand(SqlCommandCall cmdCall) {
switch (cmdCall.command) {
case SELECT:
return callBondSelect(cmdCall);
default:
throw new SqlClientException("Unsupported command: " + cmdCall.command);
}
}
private void callQuit() {
printInfo(CliStrings.MESSAGE_QUIT);
isRunning = false;
}
private void callClear() {
clearTerminal();
}
private void callReset() {
context.resetSessionProperties();
printInfo(CliStrings.MESSAGE_RESET);
}
private void callSet(SqlCommandCall cmdCall) {
// show all properties
if (cmdCall.operands.length == 0) {
final Map properties;
try {
properties = executor.getSessionProperties(context);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
if (properties.isEmpty()) {
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_EMPTY).toAnsi());
} else {
properties
.entrySet()
.stream()
.map((e) -> e.getKey() + "=" + e.getValue())
.sorted()
.forEach((p) -> terminal.writer().println(p));
}
}
// set a property
else {
context.setSessionProperty(cmdCall.operands[0], cmdCall.operands[1]);
// if we change execution type (batch -> streaming or streaming -> batch
// we will use a new TableEnvironment for the following queries.
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_SET).toAnsi());
}
terminal.flush();
}
private void callHelp() {
terminal.writer().println(CliStrings.MESSAGE_HELP);
terminal.flush();
}
private void callShowCatalogs() {
final List catalogs;
try {
catalogs = executor.listCatalogs(context);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
if (catalogs.isEmpty()) {
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_EMPTY).toAnsi());
} else {
catalogs.forEach((v) -> terminal.writer().println(v));
}
terminal.flush();
}
private void callShowDatabases() {
final List dbs;
try {
dbs = executor.listDatabases(context);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
if (dbs.isEmpty()) {
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_EMPTY).toAnsi());
} else {
dbs.forEach((v) -> terminal.writer().println(v));
}
terminal.flush();
}
private void callShowTables() {
final List tables;
try {
tables = executor.listTables(context);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
if (tables.isEmpty()) {
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_EMPTY).toAnsi());
} else {
tables.forEach((v) -> terminal.writer().println(v));
}
terminal.flush();
}
private void callShowViews() {
final List views;
try {
views = executor.listViews(context);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
if (views.isEmpty()) {
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_EMPTY).toAnsi());
} else {
views.forEach((v) -> terminal.writer().println(v));
}
terminal.flush();
}
private void callShowFunctions() {
final List functions;
try {
functions = executor.listUserDefinedFunctions(context);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
if (functions.isEmpty()) {
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_EMPTY).toAnsi());
} else {
functions.forEach((v) -> terminal.writer().println(v));
}
terminal.flush();
}
private void callShowPartitons(SqlCommandCall cmdCall) {
try {
List partitions = executor.listPartitions(context, cmdCall.operands[0]);
if (null == partitions || partitions.isEmpty()) {
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_EMPTY).toAnsi());
} else {
partitions.forEach(v -> terminal.writer().println(v));
}
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
terminal.flush();
}
private void callShowCreateTable(SqlCommandCall cmdCall) {
try {
String createTableDDL = executor.showCreateTable(context, cmdCall.operands[0]);
if (null == createTableDDL || createTableDDL.isEmpty()) {
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_EMPTY).toAnsi());
} else {
terminal.writer().println(createTableDDL);
}
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
terminal.flush();
}
private void callUseDatabase(SqlCommandCall cmdCall) {
try {
executor.setDefaultDatabase(context, cmdCall.operands[0]);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
terminal.flush();
}
private void callUseCatalog(SqlCommandCall cmdCall) {
try {
executor.setDefaultCatalog(context, cmdCall.operands[0]);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
terminal.flush();
}
private void callDescribe(SqlCommandCall cmdCall) {
final TableDesc tableDesc;
try {
tableDesc = executor.describeTable(context, cmdCall.operands[0]);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
terminal.writer().println(tableDesc.toString());
terminal.flush();
}
private void callDescribeDatabase(SqlCommandCall cmdCall) {
try {
DatabaseDesc desc = executor.describeDatabase(context, cmdCall.operands[0]);
terminal.writer().println(desc.toString());
terminal.flush();
} catch (SqlExecutionException e) {
printExecutionException(e);
}
}
private void callExplain(SqlCommandCall cmdCall) {
final String explanation;
try {
explanation = executor.explainStatement(context, cmdCall.operands[0]);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
terminal.writer().println(explanation);
terminal.flush();
}
private void callSelect(SqlCommandCall cmdCall) {
final long start = System.currentTimeMillis();
final ResultDescriptor resultDesc;
try {
resultDesc = executor.executeQuery(context, cmdCall.operands[0]);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
final long end = System.currentTimeMillis();
// support a traditional and scrolling non-interactive view
if (executor.getOrCreateExecutionContext(context).getMergedEnvironment().getExecution().isNonInteractiveViewMode()) {
printRows(resultDesc, start, end);
this.executor.cancelQuery(context, resultDesc.getResultId(), resultDesc.getStatement());
} else {
final CliResultView view;
if (resultDesc.isMaterialized()) {
view = new CliTableResultView(this, resultDesc);
} else {
view = new CliChangelogResultView(this, resultDesc);
}
// enter view
try {
view.open();
// view left
printInfo(CliStrings.MESSAGE_RESULT_QUIT);
} catch (SqlExecutionException e) {
printExecutionException(e);
}
}
}
private boolean callSqlUpdate(SqlCommandCall cmdCall) {
printInfo(CliStrings.MESSAGE_SUBMITTING_STATEMENT);
try {
final ProgramTargetDescriptor programTarget = executor.executeUpdate(context, cmdCall.operands[0]);
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_STATEMENT_SUBMITTED).toAnsi());
terminal.writer().println(programTarget.toString());
terminal.flush();
} catch (SqlExecutionException e) {
printExecutionException(e);
return false;
}
return true;
}
private void callCreateDatabase(SqlCommandCall cmdCall) {
try {
executor.createDatabase(context, cmdCall.operands[0]);
printInfo(CliStrings.MESSAGE_DATABASE_CREATED);
} catch (SqlExecutionException e) {
printExecutionException(e);
}
}
private void callCreateTable(SqlCommandCall cmdCall) {
try {
executor.createTable(context, cmdCall.operands[0]);
printInfo(CliStrings.MESSAGE_TABLE_CREATE);
} catch (SqlExecutionException e) {
printExecutionException(e);
}
}
private void callAlterTable(SqlCommandCall cmdCall) {
try {
executor.alterTable(context, cmdCall.operands[0]);
} catch (SqlExecutionException e) {
printExecutionException(e);
}
}
private void callDropTable(SqlCommandCall cmdCall) {
try {
// perform and validate change
executor.dropTable(context, cmdCall.operands[0]);
printInfo(CliStrings.MESSAGE_TABLE_REMOVED);
} catch (SqlExecutionException e) {
printExecutionException(CliStrings.MESSAGE_TABLE_NOT_REMOVED, e);
}
}
private void callCreateView(SqlCommandCall cmdCall) {
try {
executor.createView(context, cmdCall.operands[0]);
printInfo(CliStrings.MESSAGE_VIEW_CREATED);
} catch (SqlExecutionException e) {
printExecutionException(e);
}
}
private void callDropView(SqlCommandCall cmdCall) {
try {
// perform and validate change
executor.dropView(context, cmdCall.operands[0]);
printInfo(CliStrings.MESSAGE_VIEW_REMOVED);
} catch (SqlExecutionException e) {
printExecutionException(CliStrings.MESSAGE_VIEW_NOT_REMOVED, e);
}
}
private void callCreateFunction(SqlCommandCall cmdCall) {
try {
executor.createFunction(context, cmdCall.operands[0]);
printInfo(CliStrings.MESSAGE_FUNCTION_CREATE);
} catch (SqlExecutionException e) {
printExecutionException(e);
}
}
private void callDropDatabase(SqlCommandCall cmdCall) {
try {
executor.dropDatabase(context, cmdCall.operands[0]);
printInfo(CliStrings.MESSAGE_DATABASE_DROP);
} catch (SqlExecutionException e) {
printExecutionException(e);
}
}
private void callDropFunction(SqlCommandCall cmdCall) {
try {
// perform and validate change
executor.dropFunction(context, cmdCall.operands[0]);
printInfo(CliStrings.MESSAGE_FUNCTION_REMOVED);
} catch (SqlExecutionException e) {
printExecutionException(e);
}
}
private void callSource(SqlCommandCall cmdCall) {
final String pathString = cmdCall.operands[0];
// load file
final String stmt;
try {
final Path path = Paths.get(pathString);
byte[] encoded = Files.readAllBytes(path);
stmt = new String(encoded, Charset.defaultCharset());
} catch (IOException e) {
printExecutionException(e);
return;
}
// limit the output a bit
if (stmt.length() > SOURCE_MAX_SIZE) {
printExecutionError(CliStrings.MESSAGE_MAX_SIZE_EXCEEDED);
return;
}
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_WILL_EXECUTE).toAnsi());
terminal.writer().println(new AttributedString(stmt).toString());
terminal.flush();
// try to run it
final List statements = splitSemiColon(stmt);
for (String statement : statements) {
final Optional call = parseCommand(statement);
if (!call.isPresent()) {
break;
}
call.ifPresent(this::callCommand);
}
}
private void callBond(SqlCommandCall cmdCall) {
final long start = System.currentTimeMillis();
// bond only support table mode:
if (this.getContext().getEnvironment().getExecution().isChangelogMode()) {
printExecutionError("BOND only support table mode, please change result-mode to 'table'.");
return;
}
final String pathString = cmdCall.operands[0];
final String jobName;
// load file
final String stmt;
try {
final Path path = Paths.get(pathString);
byte[] encoded = Files.readAllBytes(path);
stmt = new String(encoded, Charset.defaultCharset());
jobName = path.getFileName().toString();
} catch (IOException e) {
printExecutionException(e);
return;
}
// limit the output a bit
if (stmt.length() > SOURCE_MAX_SIZE) {
printExecutionError(CliStrings.MESSAGE_MAX_SIZE_EXCEEDED);
return;
}
// currently ONLY support SqlKind.QUERY
terminal.writer().println(CliStrings.messageInfo(CliStrings.MESSAGE_WILL_EXECUTE).toAnsi());
terminal.writer().println("Bond only support SQL statements of type SELECT. Other statements would be ignored");
terminal.writer().println(new AttributedString(stmt).toString());
terminal.flush();
final List statements = splitSemiColon(stmt);
final List calls = new ArrayList<>();
for (String statement : statements) {
final Optional call = parseCommand(statement);
if (!call.isPresent()) {
// if we found invalid command, issue error message and return
return;
}
if (call.get().command == SqlCommandParser.SqlCommand.SELECT) {
calls.add(call.get());
}
}
// prepare plan
List> results =
calls
.stream()
.map(call -> callBondCommand(call))
.filter((result) -> result.isPresent())
.map(result -> result.get())
.collect(Collectors.toList());
// submit plan
final List resultDescList;
try {
resultDescList = executor.executeBondQuery(context, results, jobName);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
final long end = System.currentTimeMillis();
if (executor.getOrCreateExecutionContext(context).getMergedEnvironment().getExecution().isNonInteractiveViewMode()) {
resultDescList.forEach((desc) -> {
printRows(desc, start, end);
this.executor.cancelQuery(context, desc.getResultId(), desc.getStatement());
});
} else {
resultDescList.forEach((desc) -> {
final CliResultView view;
if (desc.isMaterialized()) {
view = new CliTableResultView(this, desc);
} else {
view = new CliChangelogResultView(this, desc);
}
// enter view
try {
view.open();
// view left
printInfo(CliStrings.MESSAGE_RESULT_QUIT);
} catch (SqlExecutionException e) {
printExecutionException(e);
}
});
}
}
private Optional> callBondSelect(SqlCommandCall cmdCall) {
final DynamicResult> result;
try {
result = executor.applyBondQuery(context, cmdCall.operands[0]);
} catch (SqlExecutionException e) {
printExecutionException(e);
return Optional.empty();
}
return Optional.of(result);
}
private void callAlterDatabase(SqlCommandCall cmdCall) {
try {
executor.alterDatabase(context, cmdCall.operands[0]);
} catch (SqlExecutionException e) {
printExecutionException(e);
}
}
private void callAnalyze(SqlCommandCall cmdCall) {
final long start = System.currentTimeMillis();
try {
executor.analyzeTable(context, cmdCall.operands[0]);
} catch (SqlExecutionException e) {
printExecutionException(e);
}
final long end = System.currentTimeMillis();
terminal.writer().println(String.format("Time token %.2f seconds.", (end - start) / 1000.0));
terminal.writer().flush();
}
private void callImport(SqlCommandCall cmdCall) {
final long start = System.currentTimeMillis();
final String srcTableName = cmdCall.operands[0];
final String descTAbleName = cmdCall.operands[1];
terminal.writer().println("Import command would scan data from src and write to desc");
terminal.writer().println("src and desc should have the same Schema or a error will be issued.");
terminal.writer().flush();
final String convertSqlString = String.format("INSERT INTO %s SELECT * FROM %s", descTAbleName, srcTableName);
final String statsSqlString = executor.generateAnalyzeSQL(context, srcTableName);
// prepare plan
try {
executor.applyBondUpdate(context, convertSqlString);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
final List> results = new ArrayList<>();
try {
results.add(executor.applyBondQuery(context, statsSqlString));
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
// submit plan
final List resultDescList;
try {
resultDescList = executor.executeBondQuery(context, results, "import " + srcTableName + " to " + descTAbleName);
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
final long end = System.currentTimeMillis();
Preconditions.checkArgument(resultDescList.size() == 1);
final ResultDescriptor desc = resultDescList.get(0);
final String resultId = desc.getResultId();
final String statement = desc.getStatement();
final List actualResults = new ArrayList<>();
if (executor.getOrCreateExecutionContext(context).getMergedEnvironment().getExecution().isNonInteractiveViewMode()) {
TypedResult> result = this.executor.retrieveResults(resultId, statement);
if (TypedResult.ResultType.PAYLOAD == result.getType()) {
actualResults.addAll(result.getPayload());
}
} else {
do {
final TypedResult result = executor.snapshotResult(this.getContext(), resultId, statement, 1);
if (result.getType() == TypedResult.ResultType.PAYLOAD) {
actualResults.clear();
IntStream.rangeClosed(1, result.getPayload()).forEach((page) -> {
for (Row row : executor.retrieveResultPage(resultId, statement, page)) {
actualResults.add(row);
}
});
} else if (result.getType() == TypedResult.ResultType.EOS) {
// Job finished or cancelled
break;
}
try {
Thread.sleep(10);
} catch (Exception e) {
printExecutionException(e);
return;
}
} while (true);
}
this.executor.cancelQuery(context, resultId, statement);
Preconditions.checkArgument(actualResults.size() == 1);
try {
executor.applyTableStats(context, srcTableName, actualResults.get(0));
executor.applyTableStats(context, descTAbleName, actualResults.get(0));
} catch (SqlExecutionException e) {
printExecutionException(e);
return;
}
terminal.writer().println(String.format("Time token: %.2f seconds.", (end - start) / 1000.0));
terminal.writer().flush();
}
// --------------------------------------------------------------------------------------------
private void printExecutionException(Throwable t) {
printExecutionException(null, t);
}
private void printExecutionException(String message, Throwable t) {
final String finalMessage;
if (message == null) {
finalMessage = CliStrings.MESSAGE_SQL_EXECUTION_ERROR;
} else {
finalMessage = CliStrings.MESSAGE_SQL_EXECUTION_ERROR + ' ' + message;
}
printException(finalMessage, t);
}
private void printExecutionError(String message) {
terminal.writer().println(CliStrings.messageError(CliStrings.MESSAGE_SQL_EXECUTION_ERROR, message).toAnsi());
terminal.flush();
}
private void printException(String message, Throwable t) {
LOG.warn(message, t);
terminal.writer().println(CliStrings.messageError(message, t).toAnsi());
terminal.flush();
}
private void printError(String message) {
terminal.writer().println(CliStrings.messageError(message).toAnsi());
terminal.flush();
}
private void printInfo(String message) {
terminal.writer().println(CliStrings.messageInfo(message).toAnsi());
terminal.flush();
}
/**
* Split a SemiColon-separated String, but ignore SemiColons in quotes.
* @param line
* @return
*/
private static List splitSemiColon(String line) {
boolean inSingleQuotes = false;
boolean inDoubleQuotes = false;
boolean escape = false;
// normalize
line = line.replaceAll("--[^\r\n]*", ""); // remove single-line comments
line = line.replaceAll("/\\*[\\w\\W]*?(?=\\*/)\\*/", ""); // remove double-line comments
line = line.trim();
List ret = new ArrayList<>();
int beginIdx = 0;
for (int idx = 0; idx < line.length(); idx++) {
char c = line.charAt(idx);
switch (c) {
case ';':
if (!inSingleQuotes && !inDoubleQuotes) {
ret.add(line.substring(beginIdx, idx));
beginIdx = idx + 1;
}
break;
case '"':
if (!escape) {
inDoubleQuotes = !inDoubleQuotes;
}
break;
case '\'':
if (!escape) {
inSingleQuotes = !inSingleQuotes;
}
break;
default:
break;
}
if (escape) {
escape = false;
} else if (c == '\\') {
escape = true;
}
}
if (beginIdx < line.length()) {
ret.add(line.substring(beginIdx));
}
return ret;
}
/**
* Output Results in non-interactive way.
*/
void printRows(final ResultDescriptor desc, long start, long end) {
final String resultId = desc.getResultId();
final String statement = desc.getStatement();
TypedResult> result = this.executor.retrieveResults(resultId, statement);
if (TypedResult.ResultType.PAYLOAD == result.getType()) {
final List rows = result.getPayload();
terminal.writer().println("Fetched result is: ");
rows.forEach((row) -> terminal.writer().println(row.toString()));
terminal.writer().println(String.format("Time token: %.2f seconds, Fetched %d row(s).", (end - start) / 1000.0, rows.size()));
terminal.writer().flush();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy