/*
* Copyright © 2012-2014 Cask Data, Inc.
*
* Licensed 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 co.cask.cdap.cli.command;
import co.cask.cdap.cli.ArgumentName;
import co.cask.cdap.cli.CLIConfig;
import co.cask.cdap.cli.Categorized;
import co.cask.cdap.cli.CommandCategory;
import co.cask.cdap.cli.ElementType;
import co.cask.cdap.cli.english.Article;
import co.cask.cdap.cli.english.Fragment;
import co.cask.cdap.cli.util.AbstractAuthCommand;
import co.cask.cdap.cli.util.RowMaker;
import co.cask.cdap.cli.util.table.Table;
import co.cask.cdap.client.QueryClient;
import co.cask.cdap.explore.client.ExploreExecutionResult;
import co.cask.cdap.explore.service.HandleNotFoundException;
import co.cask.cdap.explore.service.UnexpectedQueryStatusException;
import co.cask.cdap.proto.ColumnDesc;
import co.cask.cdap.proto.QueryResult;
import co.cask.cdap.proto.QueryStatus;
import co.cask.common.cli.Arguments;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Inject;
import java.io.PrintStream;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Executes a dataset query.
*/
public class ExecuteQueryCommand extends AbstractAuthCommand implements Categorized {
private static final long DEFAULT_TIMEOUT_MIN = Long.MAX_VALUE;
private final QueryClient queryClient;
@Inject
public ExecuteQueryCommand(QueryClient queryClient, CLIConfig cliConfig) {
super(cliConfig);
this.queryClient = queryClient;
}
@Override
public void perform(Arguments arguments, PrintStream output) throws Exception {
String query = arguments.get(ArgumentName.QUERY.toString());
long timeOutMins = arguments.getLong(ArgumentName.TIMEOUT.toString(), DEFAULT_TIMEOUT_MIN);
ListenableFuture<ExploreExecutionResult> future = queryClient.execute(cliConfig.getCurrentNamespace(), query);
try {
ExploreExecutionResult executionResult = future.get(timeOutMins, TimeUnit.MINUTES);
if (!executionResult.canContainResults()) {
output.println("SQL statement does not output any result.");
executionResult.close();
return;
}
final List<ColumnDesc> schema = executionResult.getResultSchema();
String[] header = new String[schema.size()];
for (int i = 0; i < header.length; i++) {
ColumnDesc column = schema.get(i);
// Hive columns start at 1
int index = column.getPosition() - 1;
header[index] = column.getName() + ": " + column.getType();
}
List<QueryResult> rows = Lists.newArrayList(executionResult);
executionResult.close();
QueryStatus.OpStatus opStatus = executionResult.getStatus().getStatus();
if (opStatus != QueryStatus.OpStatus.FINISHED) {
throw new SQLException(String.format("Query '%s' execution did not finish successfully. " +
"Got final state - %s", query, opStatus));
}
Table table = Table.builder()
.setHeader(header)
.setRows(rows, new RowMaker<QueryResult>() {
@Override
public List<?> makeRow(QueryResult object) {
return object.getColumns();
}
}).build();
cliConfig.getTableRenderer().render(cliConfig, output, table);
output.printf("Fetched %d rows", rows.size()).println();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
Throwable t = Throwables.getRootCause(e);
if (t instanceof HandleNotFoundException) {
throw Throwables.propagate(t);
} else if (t instanceof UnexpectedQueryStatusException) {
UnexpectedQueryStatusException sE = (UnexpectedQueryStatusException) t;
throw new SQLException(String.format("Query '%s' execution did not finish successfully. " +
"Got final state - %s", query, sE.getStatus().toString()));
}
throw new SQLException(Throwables.getRootCause(e));
} catch (CancellationException e) {
throw new RuntimeException("Query has been cancelled on ListenableFuture object.");
} catch (TimeoutException e) {
output.println("Couldn't obtain results after " + timeOutMins + "mins.");
}
}
@Override
public String getPattern() {
return String.format("execute <%s> [<%s>]", ArgumentName.QUERY, ArgumentName.TIMEOUT);
}
@Override
public String getDescription() {
return String.format("Executes %s with optional <%s> in minutes (default is no timeout).",
Fragment.of(Article.A, ElementType.QUERY.getName()), ArgumentName.TIMEOUT);
}
@Override
public String getCategory() {
return CommandCategory.EXPLORE.getName();
}
}