blob: 4ca9ce11142626685473508c871e0d4ec9aa8191 [file] [log] [blame]
/*
* 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.calcite.avatica;
import org.apache.calcite.avatica.proto.Common;
import org.apache.calcite.avatica.proto.Common.MetaDataOperationArgument;
import org.apache.calcite.avatica.proto.Common.MetaDataOperationArgument.ArgumentType;
import org.apache.calcite.avatica.remote.MetaDataOperation;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.Objects;
/**
* A struct used to encapsulate the necessary information to reconstitute a ResultSet in the
* Avatica server.
*/
public class QueryState {
/**
* An enumeration that represents how a ResultSet was created.
*/
public enum StateType {
SQL,
METADATA;
public Common.StateType toProto() {
switch (this) {
case SQL:
return Common.StateType.SQL;
case METADATA:
return Common.StateType.METADATA;
default:
return Common.StateType.UNRECOGNIZED;
}
}
public static StateType fromProto(Common.StateType protoType) {
switch (protoType) {
case SQL:
return StateType.SQL;
case METADATA:
return StateType.METADATA;
default:
throw new IllegalArgumentException("Unhandled StateType " + protoType);
}
}
}
@JsonProperty("type")
public final StateType type;
@JsonProperty("sql")
public final String sql;
@JsonProperty("metaDataOperation")
public final MetaDataOperation metaDataOperation;
@JsonProperty("operationArgs")
public final Object[] operationArgs;
/**
* Constructor encapsulating a SQL query used to create a result set.
*
* @param sql The SQL query.
*/
public QueryState(String sql) {
// This doesn't to be non-null
this.sql = sql;
this.type = StateType.SQL;
// Null out the members we don't use.
this.metaDataOperation = null;
this.operationArgs = null;
}
/**
* Constructor encapsulating a metadata operation's result set.
*
* @param op A pointer to the {@link DatabaseMetaData} operation being invoked.
* @param args The arguments to the method being invoked.
*/
public QueryState(MetaDataOperation op, Object... args) {
this.metaDataOperation = Objects.requireNonNull(op);
this.operationArgs = Arrays.copyOf(Objects.requireNonNull(args), args.length);
this.type = StateType.METADATA;
// Null out the members we won't use
this.sql = null;
}
/**
* Not intended for external use. For Jackson-databind only.
*/
public QueryState(StateType type, String sql, MetaDataOperation op, Object... args) {
this.type = Objects.requireNonNull(type);
switch (type) {
case SQL:
this.sql = Objects.requireNonNull(sql);
if (null != op) {
throw new IllegalArgumentException("Expected null MetaDataOperation, but got " + op);
}
this.metaDataOperation = null;
if (null != args) {
throw new IllegalArgumentException("Expected null arguments, but got "
+ Arrays.toString(args));
}
this.operationArgs = null;
break;
case METADATA:
this.metaDataOperation = Objects.requireNonNull(op);
this.operationArgs = Objects.requireNonNull(args);
if (null != sql) {
throw new IllegalArgumentException("Expected null SQl but got " + sql);
}
this.sql = null;
break;
default:
throw new IllegalArgumentException("Unable to handle StateType " + type);
}
}
/**
* Not intended for external use. For Jackson-databind only.
*/
public QueryState() {
this.sql = null;
this.metaDataOperation = null;
this.type = null;
this.operationArgs = null;
}
/**
* @return The {@link StateType} for this encapsulated state.
*/
public StateType getType() {
return type;
}
/**
* @return The SQL expression to invoke.
*/
public String getSql() {
assert type == StateType.SQL;
return sql;
}
/**
* @return The metadata operation to invoke.
*/
public MetaDataOperation getMetaDataOperation() {
assert type == StateType.METADATA;
return metaDataOperation;
}
/**
* @return The Arguments for the given metadata operation.
*/
public Object[] getOperationArgs() {
assert type == StateType.METADATA;
return operationArgs;
}
public ResultSet invoke(Connection conn, Statement statement) throws SQLException {
switch (type) {
case SQL:
boolean ret = Objects.requireNonNull(statement).execute(sql);
ResultSet results = statement.getResultSet();
// Either execute(sql) returned true or the resultSet was null
assert ret || null == results;
return results;
case METADATA:
DatabaseMetaData metadata = Objects.requireNonNull(conn).getMetaData();
switch (metaDataOperation) {
case GET_ATTRIBUTES:
verifyOpArgs(4);
return metadata.getAttributes((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2],
(String) operationArgs[3]);
case GET_BEST_ROW_IDENTIFIER:
verifyOpArgs(5);
return metadata.getBestRowIdentifier((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2],
(int) operationArgs[3],
(boolean) operationArgs[4]);
case GET_CATALOGS:
verifyOpArgs(0);
return metadata.getCatalogs();
case GET_COLUMNS:
verifyOpArgs(4);
return metadata.getColumns((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2],
(String) operationArgs[3]);
case GET_COLUMN_PRIVILEGES:
verifyOpArgs(4);
return metadata.getColumnPrivileges((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2],
(String) operationArgs[3]);
case GET_CROSS_REFERENCE:
verifyOpArgs(6);
return metadata.getCrossReference((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2],
(String) operationArgs[3],
(String) operationArgs[4],
(String) operationArgs[5]);
case GET_EXPORTED_KEYS:
verifyOpArgs(3);
return metadata.getExportedKeys((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2]);
case GET_FUNCTIONS:
verifyOpArgs(3);
return metadata.getFunctions((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2]);
case GET_FUNCTION_COLUMNS:
verifyOpArgs(4);
return metadata.getFunctionColumns((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2],
(String) operationArgs[3]);
case GET_IMPORTED_KEYS:
verifyOpArgs(3);
return metadata.getImportedKeys((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2]);
case GET_INDEX_INFO:
verifyOpArgs(5);
return metadata.getIndexInfo((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2],
(boolean) operationArgs[3],
(boolean) operationArgs[4]);
case GET_PRIMARY_KEYS:
verifyOpArgs(3);
return metadata.getPrimaryKeys((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2]);
case GET_PROCEDURES:
verifyOpArgs(3);
return metadata.getProcedures((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2]);
case GET_PROCEDURE_COLUMNS:
verifyOpArgs(4);
return metadata.getProcedureColumns((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2],
(String) operationArgs[3]);
case GET_PSEUDO_COLUMNS:
verifyOpArgs(4);
return metadata.getPseudoColumns((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2],
(String) operationArgs[3]);
case GET_SCHEMAS:
verifyOpArgs(0);
return metadata.getSchemas();
case GET_SCHEMAS_WITH_ARGS:
verifyOpArgs(2);
return metadata.getSchemas((String) operationArgs[0],
(String) operationArgs[1]);
case GET_SUPER_TABLES:
verifyOpArgs(3);
return metadata.getSuperTables((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2]);
case GET_SUPER_TYPES:
verifyOpArgs(3);
return metadata.getSuperTypes((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2]);
case GET_TABLES:
verifyOpArgs(4);
return metadata.getTables((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2],
(String[]) operationArgs[3]);
case GET_TABLE_PRIVILEGES:
verifyOpArgs(3);
return metadata.getTablePrivileges((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2]);
case GET_TABLE_TYPES:
verifyOpArgs(0);
return metadata.getTableTypes();
case GET_TYPE_INFO:
verifyOpArgs(0);
return metadata.getTypeInfo();
case GET_UDTS:
verifyOpArgs(4);
return metadata.getUDTs((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2],
(int[]) operationArgs[3]);
case GET_VERSION_COLUMNS:
verifyOpArgs(3);
return metadata.getVersionColumns((String) operationArgs[0],
(String) operationArgs[1],
(String) operationArgs[2]);
default:
throw new IllegalArgumentException("Unhandled Metadata operation: " + metaDataOperation);
}
default:
throw new IllegalArgumentException("Unable to process QueryState of type " + type);
}
}
private void verifyOpArgs(int expectedArgs) {
if (expectedArgs != operationArgs.length) {
throw new RuntimeException("Expected " + expectedArgs + " arguments, but got "
+ Arrays.toString(operationArgs));
}
}
public Common.QueryState toProto() {
Common.QueryState.Builder builder = Common.QueryState.newBuilder();
// Required
switch (type) {
case SQL:
builder.setType(Common.StateType.SQL);
break;
case METADATA:
builder.setType(Common.StateType.METADATA);
break;
default:
throw new IllegalStateException("Unhandled type: " + type);
}
// Optional SQL
if (null != sql) {
builder.setSql(sql).setHasSql(true);
}
// Optional metaDataOperation
if (null != metaDataOperation) {
builder.setOp(metaDataOperation.toProto()).setHasOp(true);
}
// Optional operationArgs
if (null != operationArgs) {
builder.setHasArgs(true);
for (Object arg : operationArgs) {
MetaDataOperationArgument.Builder argBuilder = MetaDataOperationArgument.newBuilder();
if (null == arg) {
builder.addArgs(argBuilder.setType(ArgumentType.NULL).build());
} else if (arg instanceof String) {
builder.addArgs(argBuilder.setType(ArgumentType.STRING)
.setStringValue((String) arg).build());
} else if (arg instanceof Integer) {
builder.addArgs(argBuilder.setType(ArgumentType.INT).setIntValue((int) arg).build());
} else if (arg instanceof Boolean) {
builder.addArgs(
argBuilder.setType(ArgumentType.BOOL).setBoolValue((boolean) arg).build());
} else if (arg instanceof String[]) {
argBuilder.setType(ArgumentType.REPEATED_STRING);
for (String strArg : (String[]) arg) {
argBuilder.addStringArrayValues(strArg);
}
builder.addArgs(argBuilder.build());
} else if (arg instanceof int[]) {
argBuilder.setType(ArgumentType.REPEATED_INT);
for (int intArg : (int[]) arg) {
argBuilder.addIntArrayValues(intArg);
}
builder.addArgs(argBuilder.build());
} else {
throw new RuntimeException("Unexpected operation argument: " + arg.getClass());
}
}
} else {
builder.setHasArgs(false);
}
return builder.build();
}
public static QueryState fromProto(Common.QueryState protoState) {
StateType type = StateType.fromProto(protoState.getType());
String sql = protoState.getHasSql() ? protoState.getSql() : null;
MetaDataOperation op = protoState.getHasOp()
? MetaDataOperation.fromProto(protoState.getOp()) : null;
Object[] opArgs = null;
if (protoState.getHasArgs()) {
opArgs = new Object[protoState.getArgsCount()];
int i = 0;
for (Common.MetaDataOperationArgument arg : protoState.getArgsList()) {
switch (arg.getType()) {
case STRING:
opArgs[i] = arg.getStringValue();
break;
case BOOL:
opArgs[i] = arg.getBoolValue();
break;
case INT:
opArgs[i] = arg.getIntValue();
break;
case REPEATED_STRING:
opArgs[i] = arg.getStringArrayValuesList().toArray(
new String[arg.getStringArrayValuesCount()]);
break;
case REPEATED_INT:
int[] arr = new int[arg.getIntArrayValuesCount()];
int offset = 0;
for (Integer val : arg.getIntArrayValuesList()) {
arr[offset] = val;
offset++;
}
opArgs[i] = arr;
break;
case NULL:
opArgs[i] = null;
break;
default:
throw new RuntimeException("Could not interpret " + arg.getType());
}
i++;
}
}
return new QueryState(type, sql, op, opArgs);
}
@Override public int hashCode() {
return Objects.hash(metaDataOperation, Arrays.hashCode(operationArgs), sql);
}
@Override public boolean equals(Object o) {
return o == this
|| o instanceof QueryState
&& metaDataOperation == ((QueryState) o).metaDataOperation
&& Arrays.deepEquals(operationArgs, ((QueryState) o).operationArgs)
&& Objects.equals(sql, ((QueryState) o).sql);
}
}
// End QueryState.java