blob: 434b1d47bea8a6b8f11f40a72b78384aa9c9fbea [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.jdbc;
import org.apache.calcite.DataContext;
import org.apache.calcite.adapter.java.AbstractQueryableTable;
import org.apache.calcite.adapter.java.JavaTypeFactory;
import org.apache.calcite.avatica.AvaticaParameter;
import org.apache.calcite.avatica.AvaticaStatement;
import org.apache.calcite.avatica.AvaticaUtils;
import org.apache.calcite.avatica.ColumnMetaData;
import org.apache.calcite.avatica.Meta;
import org.apache.calcite.avatica.MetaImpl;
import org.apache.calcite.avatica.NoSuchStatementException;
import org.apache.calcite.avatica.QueryState;
import org.apache.calcite.avatica.remote.TypedValue;
import org.apache.calcite.linq4j.Enumerable;
import org.apache.calcite.linq4j.Enumerator;
import org.apache.calcite.linq4j.Linq4j;
import org.apache.calcite.linq4j.QueryProvider;
import org.apache.calcite.linq4j.Queryable;
import org.apache.calcite.linq4j.function.Function1;
import org.apache.calcite.linq4j.function.Functions;
import org.apache.calcite.linq4j.function.Predicate1;
import org.apache.calcite.rel.RelCollation;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeFactoryImpl;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.runtime.FlatLists;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.schema.Table;
import org.apache.calcite.schema.impl.AbstractTableQueryable;
import org.apache.calcite.server.CalciteServerStatement;
import org.apache.calcite.sql.SqlJdbcFunctionCall;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.util.Util;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
/**
* Helper for implementing the {@code getXxx} methods such as
* {@link org.apache.calcite.avatica.AvaticaDatabaseMetaData#getTables}.
*/
public class CalciteMetaImpl extends MetaImpl {
static final Driver DRIVER = new Driver();
public CalciteMetaImpl(CalciteConnectionImpl connection) {
super(connection);
this.connProps
.setAutoCommit(false)
.setReadOnly(false)
.setTransactionIsolation(Connection.TRANSACTION_NONE);
this.connProps.setDirty(false);
}
static <T extends Named> Predicate1<T> namedMatcher(final Pat pattern) {
if (pattern.s == null || pattern.s.equals("%")) {
return Functions.truePredicate1();
}
final Pattern regex = likeToRegex(pattern);
return new Predicate1<T>() {
public boolean apply(T v1) {
return regex.matcher(v1.getName()).matches();
}
};
}
static Predicate1<String> matcher(final Pat pattern) {
if (pattern.s == null || pattern.s.equals("%")) {
return Functions.truePredicate1();
}
final Pattern regex = likeToRegex(pattern);
return new Predicate1<String>() {
public boolean apply(String v1) {
return regex.matcher(v1).matches();
}
};
}
/** Converts a LIKE-style pattern (where '%' represents a wild-card, escaped
* using '\') to a Java regex. */
public static Pattern likeToRegex(Pat pattern) {
StringBuilder buf = new StringBuilder("^");
char[] charArray = pattern.s.toCharArray();
int slash = -2;
for (int i = 0; i < charArray.length; i++) {
char c = charArray[i];
if (slash == i - 1) {
buf.append('[').append(c).append(']');
} else {
switch (c) {
case '\\':
slash = i;
break;
case '%':
buf.append(".*");
break;
case '[':
buf.append("\\[");
break;
case ']':
buf.append("\\]");
break;
default:
buf.append('[').append(c).append(']');
}
}
}
buf.append("$");
return Pattern.compile(buf.toString());
}
@Override public StatementHandle createStatement(ConnectionHandle ch) {
final StatementHandle h = super.createStatement(ch);
final CalciteConnectionImpl calciteConnection = getConnection();
calciteConnection.server.addStatement(calciteConnection, h);
return h;
}
@Override public void closeStatement(StatementHandle h) {
final CalciteConnectionImpl calciteConnection = getConnection();
final CalciteServerStatement stmt;
try {
stmt = calciteConnection.server.getStatement(h);
} catch (NoSuchStatementException e) {
// statement is not valid; nothing to do
return;
}
// stmt.close(); // TODO: implement
calciteConnection.server.removeStatement(h);
}
private <E> MetaResultSet createResultSet(Enumerable<E> enumerable,
Class clazz, String... names) {
final List<ColumnMetaData> columns = new ArrayList<>();
final List<Field> fields = new ArrayList<>();
final List<String> fieldNames = new ArrayList<>();
for (String name : names) {
final int index = fields.size();
final String fieldName = AvaticaUtils.toCamelCase(name);
final Field field;
try {
field = clazz.getField(fieldName);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
columns.add(columnMetaData(name, index, field.getType()));
fields.add(field);
fieldNames.add(fieldName);
}
//noinspection unchecked
final Iterable<Object> iterable = (Iterable<Object>) (Iterable) enumerable;
return createResultSet(Collections.<String, Object>emptyMap(),
columns, CursorFactory.record(clazz, fields, fieldNames),
new Frame(0, true, iterable));
}
@Override protected <E> MetaResultSet
createEmptyResultSet(final Class<E> clazz) {
final List<ColumnMetaData> columns = fieldMetaData(clazz).columns;
final CursorFactory cursorFactory = CursorFactory.deduce(columns, clazz);
return createResultSet(Collections.<String, Object>emptyMap(), columns,
cursorFactory, Frame.EMPTY);
}
protected MetaResultSet createResultSet(
Map<String, Object> internalParameters, List<ColumnMetaData> columns,
CursorFactory cursorFactory, final Frame firstFrame) {
try {
final CalciteConnectionImpl connection = getConnection();
final AvaticaStatement statement = connection.createStatement();
final CalcitePrepare.CalciteSignature<Object> signature =
new CalcitePrepare.CalciteSignature<Object>("",
ImmutableList.<AvaticaParameter>of(), internalParameters, null,
columns, cursorFactory, ImmutableList.<RelCollation>of(), -1,
null, Meta.StatementType.SELECT) {
@Override public Enumerable<Object> enumerable(
DataContext dataContext) {
return Linq4j.asEnumerable(firstFrame.rows);
}
};
return MetaResultSet.create(connection.id, statement.getId(), true,
signature, firstFrame);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
CalciteConnectionImpl getConnection() {
return (CalciteConnectionImpl) connection;
}
@Override public Map<DatabaseProperty, Object> getDatabaseProperties(ConnectionHandle ch) {
final ImmutableMap.Builder<DatabaseProperty, Object> builder =
ImmutableMap.builder();
for (DatabaseProperty p : DatabaseProperty.values()) {
addProperty(builder, p);
}
return builder.build();
}
private ImmutableMap.Builder<DatabaseProperty, Object> addProperty(
ImmutableMap.Builder<DatabaseProperty, Object> builder,
DatabaseProperty p) {
switch (p) {
case GET_S_Q_L_KEYWORDS:
return builder.put(p,
SqlParser.create("").getMetadata().getJdbcKeywords());
case GET_NUMERIC_FUNCTIONS:
return builder.put(p, SqlJdbcFunctionCall.getNumericFunctions());
case GET_STRING_FUNCTIONS:
return builder.put(p, SqlJdbcFunctionCall.getStringFunctions());
case GET_SYSTEM_FUNCTIONS:
return builder.put(p, SqlJdbcFunctionCall.getSystemFunctions());
case GET_TIME_DATE_FUNCTIONS:
return builder.put(p, SqlJdbcFunctionCall.getTimeDateFunctions());
default:
return builder;
}
}
public MetaResultSet getTables(ConnectionHandle ch,
String catalog,
final Pat schemaPattern,
final Pat tableNamePattern,
final List<String> typeList) {
final Predicate1<MetaTable> typeFilter;
if (typeList == null) {
typeFilter = Functions.truePredicate1();
} else {
typeFilter = new Predicate1<MetaTable>() {
public boolean apply(MetaTable v1) {
return typeList.contains(v1.tableType);
}
};
}
final Predicate1<MetaSchema> schemaMatcher = namedMatcher(schemaPattern);
return createResultSet(schemas(catalog)
.where(schemaMatcher)
.selectMany(
new Function1<MetaSchema, Enumerable<MetaTable>>() {
public Enumerable<MetaTable> apply(MetaSchema schema) {
return tables(schema, matcher(tableNamePattern));
}
})
.where(typeFilter),
MetaTable.class,
"TABLE_CAT",
"TABLE_SCHEM",
"TABLE_NAME",
"TABLE_TYPE",
"REMARKS",
"TYPE_CAT",
"TYPE_SCHEM",
"TYPE_NAME",
"SELF_REFERENCING_COL_NAME",
"REF_GENERATION");
}
public MetaResultSet getTypeInfo(ConnectionHandle ch) {
return createResultSet(allTypeInfo(),
MetaTypeInfo.class,
"TYPE_NAME",
"DATA_TYPE",
"PRECISION",
"LITERAL_PREFIX",
"LITERAL_SUFFIX",
"CREATE_PARAMS",
"NULLABLE",
"CASE_SENSITIVE",
"SEARCHABLE",
"UNSIGNED_ATTRIBUTE",
"FIXED_PREC_SCALE",
"AUTO_INCREMENT",
"LOCAL_TYPE_NAME",
"MINIMUM_SCALE",
"MAXIMUM_SCALE",
"SQL_DATA_TYPE",
"SQL_DATETIME_SUB",
"NUM_PREC_RADIX");
}
public MetaResultSet getColumns(ConnectionHandle ch,
String catalog,
Pat schemaPattern,
Pat tableNamePattern,
Pat columnNamePattern) {
final Predicate1<String> tableNameMatcher = matcher(tableNamePattern);
final Predicate1<MetaSchema> schemaMatcher = namedMatcher(schemaPattern);
final Predicate1<MetaColumn> columnMatcher =
namedMatcher(columnNamePattern);
return createResultSet(schemas(catalog)
.where(schemaMatcher)
.selectMany(
new Function1<MetaSchema, Enumerable<MetaTable>>() {
public Enumerable<MetaTable> apply(MetaSchema schema) {
return tables(schema, tableNameMatcher);
}
})
.selectMany(
new Function1<MetaTable, Enumerable<MetaColumn>>() {
public Enumerable<MetaColumn> apply(MetaTable schema) {
return columns(schema);
}
})
.where(columnMatcher),
MetaColumn.class,
"TABLE_CAT",
"TABLE_SCHEM",
"TABLE_NAME",
"COLUMN_NAME",
"DATA_TYPE",
"TYPE_NAME",
"COLUMN_SIZE",
"BUFFER_LENGTH",
"DECIMAL_DIGITS",
"NUM_PREC_RADIX",
"NULLABLE",
"REMARKS",
"COLUMN_DEF",
"SQL_DATA_TYPE",
"SQL_DATETIME_SUB",
"CHAR_OCTET_LENGTH",
"ORDINAL_POSITION",
"IS_NULLABLE",
"SCOPE_CATALOG",
"SCOPE_SCHEMA",
"SCOPE_TABLE",
"SOURCE_DATA_TYPE",
"IS_AUTOINCREMENT",
"IS_GENERATEDCOLUMN");
}
Enumerable<MetaCatalog> catalogs() {
return Linq4j.asEnumerable(
ImmutableList.of(new MetaCatalog(connection.getCatalog())));
}
Enumerable<MetaTableType> tableTypes() {
return Linq4j.asEnumerable(
ImmutableList.of(
new MetaTableType("TABLE"), new MetaTableType("VIEW")));
}
Enumerable<MetaSchema> schemas(String catalog) {
return Linq4j.asEnumerable(
getConnection().rootSchema.getSubSchemaMap().values())
.select(
new Function1<CalciteSchema, MetaSchema>() {
public MetaSchema apply(CalciteSchema calciteSchema) {
return new CalciteMetaSchema(
calciteSchema,
connection.getCatalog(),
calciteSchema.getName());
}
})
.orderBy(
new Function1<MetaSchema, Comparable>() {
public Comparable apply(MetaSchema metaSchema) {
return (Comparable) FlatLists.of(
Util.first(metaSchema.tableCatalog, ""),
metaSchema.tableSchem);
}
});
}
Enumerable<MetaTable> tables(String catalog) {
return schemas(catalog)
.selectMany(
new Function1<MetaSchema, Enumerable<MetaTable>>() {
public Enumerable<MetaTable> apply(MetaSchema schema) {
return tables(schema, Functions.<String>truePredicate1());
}
});
}
Enumerable<MetaTable> tables(final MetaSchema schema_) {
final CalciteMetaSchema schema = (CalciteMetaSchema) schema_;
return Linq4j.asEnumerable(schema.calciteSchema.getTableNames())
.select(
new Function1<String, MetaTable>() {
public MetaTable apply(String name) {
final Table table =
schema.calciteSchema.getTable(name, true).getTable();
return new CalciteMetaTable(table,
schema.tableCatalog,
schema.tableSchem,
name);
}
})
.concat(
Linq4j.asEnumerable(
schema.calciteSchema.getTablesBasedOnNullaryFunctions()
.entrySet())
.select(
new Function1<Map.Entry<String, Table>, MetaTable>() {
public MetaTable apply(Map.Entry<String, Table> pair) {
final Table table = pair.getValue();
return new CalciteMetaTable(table,
schema.tableCatalog,
schema.tableSchem,
pair.getKey());
}
}));
}
Enumerable<MetaTable> tables(
final MetaSchema schema,
final Predicate1<String> matcher) {
return tables(schema)
.where(
new Predicate1<MetaTable>() {
public boolean apply(MetaTable v1) {
return matcher.apply(v1.getName());
}
});
}
private ImmutableList<MetaTypeInfo> getAllDefaultType() {
final ImmutableList.Builder<MetaTypeInfo> allTypeList =
new ImmutableList.Builder<>();
final CalciteConnectionImpl conn = (CalciteConnectionImpl) connection;
final RelDataTypeSystem typeSystem = conn.typeFactory.getTypeSystem();
for (SqlTypeName sqlTypeName : SqlTypeName.values()) {
allTypeList.add(
new MetaTypeInfo(sqlTypeName.getName(),
sqlTypeName.getJdbcOrdinal(),
typeSystem.getMaxPrecision(sqlTypeName),
typeSystem.getLiteral(sqlTypeName, true),
typeSystem.getLiteral(sqlTypeName, false),
// All types are nullable
DatabaseMetaData.typeNullable,
typeSystem.isCaseSensitive(sqlTypeName),
// Making all type searchable; we may want to
// be specific and declare under SqlTypeName
DatabaseMetaData.typeSearchable,
false,
false,
typeSystem.isAutoincrement(sqlTypeName),
sqlTypeName.getMinScale(),
typeSystem.getMaxScale(sqlTypeName),
typeSystem.getNumTypeRadix(sqlTypeName)));
}
return allTypeList.build();
}
protected Enumerable<MetaTypeInfo> allTypeInfo() {
return Linq4j.asEnumerable(getAllDefaultType());
}
public Enumerable<MetaColumn> columns(final MetaTable table_) {
final CalciteMetaTable table = (CalciteMetaTable) table_;
final RelDataType rowType =
table.calciteTable.getRowType(getConnection().typeFactory);
return Linq4j.asEnumerable(rowType.getFieldList())
.select(
new Function1<RelDataTypeField, MetaColumn>() {
public MetaColumn apply(RelDataTypeField field) {
final int precision =
field.getType().getSqlTypeName().allowsPrec()
&& !(field.getType()
instanceof RelDataTypeFactoryImpl.JavaType)
? field.getType().getPrecision()
: -1;
return new MetaColumn(
table.tableCat,
table.tableSchem,
table.tableName,
field.getName(),
field.getType().getSqlTypeName().getJdbcOrdinal(),
field.getType().getFullTypeString(),
precision,
field.getType().getSqlTypeName().allowsScale()
? field.getType().getScale()
: null,
10,
field.getType().isNullable()
? DatabaseMetaData.columnNullable
: DatabaseMetaData.columnNoNulls,
precision,
field.getIndex() + 1,
field.getType().isNullable() ? "YES" : "NO");
}
});
}
public MetaResultSet getSchemas(ConnectionHandle ch, String catalog, Pat schemaPattern) {
final Predicate1<MetaSchema> schemaMatcher = namedMatcher(schemaPattern);
return createResultSet(schemas(catalog).where(schemaMatcher),
MetaSchema.class,
"TABLE_SCHEM",
"TABLE_CATALOG");
}
public MetaResultSet getCatalogs(ConnectionHandle ch) {
return createResultSet(catalogs(),
MetaCatalog.class,
"TABLE_CAT");
}
public MetaResultSet getTableTypes(ConnectionHandle ch) {
return createResultSet(tableTypes(),
MetaTableType.class,
"TABLE_TYPE");
}
@Override public Iterable<Object> createIterable(StatementHandle handle, QueryState state,
Signature signature, List<TypedValue> parameterValues, Frame firstFrame) {
// Drop QueryState
return _createIterable(handle, signature, parameterValues, firstFrame);
}
Iterable<Object> _createIterable(StatementHandle handle,
Signature signature, List<TypedValue> parameterValues, Frame firstFrame) {
try {
//noinspection unchecked
final CalcitePrepare.CalciteSignature<Object> calciteSignature =
(CalcitePrepare.CalciteSignature<Object>) signature;
return getConnection().enumerable(handle, calciteSignature);
} catch (SQLException e) {
throw new RuntimeException(e.getMessage());
}
}
@Override public StatementHandle prepare(ConnectionHandle ch, String sql,
long maxRowCount) {
final StatementHandle h = createStatement(ch);
final CalciteConnectionImpl calciteConnection = getConnection();
final CalciteServerStatement statement;
try {
statement = calciteConnection.server.getStatement(h);
} catch (NoSuchStatementException e) {
// Not possible. We just created a statement.
throw new AssertionError("missing statement", e);
}
h.signature =
calciteConnection.parseQuery(CalcitePrepare.Query.of(sql),
statement.createPrepareContext(), maxRowCount);
statement.setSignature(h.signature);
return h;
}
@Override public ExecuteResult prepareAndExecute(StatementHandle h,
String sql, long maxRowCount, PrepareCallback callback)
throws NoSuchStatementException {
return prepareAndExecute(h, sql, maxRowCount, -1, callback);
}
@Override public ExecuteResult prepareAndExecute(StatementHandle h,
String sql, long maxRowCount, int maxRowsInFirstFrame,
PrepareCallback callback) throws NoSuchStatementException {
final CalcitePrepare.CalciteSignature<Object> signature;
try {
synchronized (callback.getMonitor()) {
callback.clear();
final CalciteConnectionImpl calciteConnection = getConnection();
CalciteServerStatement statement =
calciteConnection.server.getStatement(h);
signature = calciteConnection.parseQuery(CalcitePrepare.Query.of(sql),
statement.createPrepareContext(), maxRowCount);
statement.setSignature(signature);
callback.assign(signature, null, -1);
}
callback.execute();
final MetaResultSet metaResultSet =
MetaResultSet.create(h.connectionId, h.id, false, signature, null);
return new ExecuteResult(ImmutableList.of(metaResultSet));
} catch (SQLException e) {
throw new RuntimeException(e);
}
// TODO: share code with prepare and createIterable
}
@Override public Frame fetch(StatementHandle h, long offset,
int fetchMaxRowCount) throws NoSuchStatementException {
final CalciteConnectionImpl calciteConnection = getConnection();
CalciteServerStatement stmt = calciteConnection.server.getStatement(h);
final Signature signature = stmt.getSignature();
final Iterator<Object> iterator;
if (stmt.getResultSet() == null) {
final Iterable<Object> iterable =
_createIterable(h, signature, null, null);
iterator = iterable.iterator();
stmt.setResultSet(iterator);
} else {
iterator = stmt.getResultSet();
}
final List rows =
MetaImpl.collect(signature.cursorFactory,
LimitIterator.of(iterator, fetchMaxRowCount),
new ArrayList<List<Object>>());
boolean done = fetchMaxRowCount == 0 || rows.size() < fetchMaxRowCount;
@SuppressWarnings("unchecked") List<Object> rows1 = (List<Object>) rows;
return new Meta.Frame(offset, done, rows1);
}
@Override public ExecuteResult execute(StatementHandle h,
List<TypedValue> parameterValues, long maxRowCount)
throws NoSuchStatementException {
return execute(h, parameterValues, Ints.saturatedCast(maxRowCount));
}
@Override public ExecuteResult execute(StatementHandle h,
List<TypedValue> parameterValues, int maxRowsInFirstFrame)
throws NoSuchStatementException {
final CalciteConnectionImpl calciteConnection = getConnection();
CalciteServerStatement stmt = calciteConnection.server.getStatement(h);
final Signature signature = stmt.getSignature();
MetaResultSet metaResultSet;
if (signature.statementType.canUpdate()) {
final Iterable<Object> iterable =
_createIterable(h, signature, parameterValues, null);
final Iterator<Object> iterator = iterable.iterator();
stmt.setResultSet(iterator);
metaResultSet = MetaResultSet.count(h.connectionId, h.id,
((Number) iterator.next()).intValue());
} else {
// Don't populate the first frame.
// It's not worth saving a round-trip, since we're local.
final Meta.Frame frame =
new Meta.Frame(0, false, Collections.emptyList());
metaResultSet =
MetaResultSet.create(h.connectionId, h.id, false, signature, frame);
}
return new ExecuteResult(ImmutableList.of(metaResultSet));
}
@Override public ExecuteBatchResult executeBatch(StatementHandle h,
List<List<TypedValue>> parameterValueLists) throws NoSuchStatementException {
final List<Long> updateCounts = new ArrayList<>();
for (List<TypedValue> parameterValueList : parameterValueLists) {
ExecuteResult executeResult = execute(h, parameterValueList, -1);
final long updateCount =
executeResult.resultSets.size() == 1
? executeResult.resultSets.get(0).updateCount
: -1L;
updateCounts.add(updateCount);
}
return new ExecuteBatchResult(Longs.toArray(updateCounts));
}
@Override public ExecuteBatchResult prepareAndExecuteBatch(
final StatementHandle h,
List<String> sqlCommands) throws NoSuchStatementException {
final CalciteConnectionImpl calciteConnection = getConnection();
final CalciteServerStatement statement =
calciteConnection.server.getStatement(h);
final List<Long> updateCounts = new ArrayList<>();
final Meta.PrepareCallback callback =
new Meta.PrepareCallback() {
long updateCount;
Signature signature;
public Object getMonitor() {
return statement;
}
public void clear() throws SQLException {}
public void assign(Meta.Signature signature, Meta.Frame firstFrame,
long updateCount) throws SQLException {
this.signature = signature;
this.updateCount = updateCount;
}
public void execute() throws SQLException {
if (signature.statementType.canUpdate()) {
final Iterable<Object> iterable =
_createIterable(h, signature, ImmutableList.<TypedValue>of(),
null);
final Iterator<Object> iterator = iterable.iterator();
updateCount = ((Number) iterator.next()).longValue();
}
updateCounts.add(updateCount);
}
};
for (String sqlCommand : sqlCommands) {
Util.discard(prepareAndExecute(h, sqlCommand, -1L, -1, callback));
}
return new ExecuteBatchResult(Longs.toArray(updateCounts));
}
/** A trojan-horse method, subject to change without notice. */
@VisibleForTesting
public static DataContext createDataContext(CalciteConnection connection) {
return ((CalciteConnectionImpl) connection)
.createDataContext(ImmutableMap.<String, Object>of());
}
/** A trojan-horse method, subject to change without notice. */
@VisibleForTesting
public static CalciteConnection connect(CalciteSchema schema,
JavaTypeFactory typeFactory) {
return DRIVER.connect(schema, typeFactory);
}
public boolean syncResults(StatementHandle h, QueryState state, long offset)
throws NoSuchStatementException {
// Doesn't have application in Calcite itself.
throw new UnsupportedOperationException();
}
@Override public void commit(ConnectionHandle ch) {
throw new UnsupportedOperationException();
}
@Override public void rollback(ConnectionHandle ch) {
throw new UnsupportedOperationException();
}
/** Metadata describing a Calcite table. */
private static class CalciteMetaTable extends MetaTable {
private final Table calciteTable;
public CalciteMetaTable(Table calciteTable, String tableCat,
String tableSchem, String tableName) {
super(tableCat, tableSchem, tableName,
calciteTable.getJdbcTableType().name());
this.calciteTable = Preconditions.checkNotNull(calciteTable);
}
}
/** Metadata describing a Calcite schema. */
private static class CalciteMetaSchema extends MetaSchema {
private final CalciteSchema calciteSchema;
public CalciteMetaSchema(CalciteSchema calciteSchema,
String tableCatalog, String tableSchem) {
super(tableCatalog, tableSchem);
this.calciteSchema = calciteSchema;
}
}
/** Table whose contents are metadata. */
abstract static class MetadataTable<E> extends AbstractQueryableTable {
public MetadataTable(Class<E> clazz) {
super(clazz);
}
public RelDataType getRowType(RelDataTypeFactory typeFactory) {
return ((JavaTypeFactory) typeFactory).createType(elementType);
}
@Override public Schema.TableType getJdbcTableType() {
return Schema.TableType.SYSTEM_TABLE;
}
@SuppressWarnings("unchecked")
@Override public Class<E> getElementType() {
return (Class<E>) elementType;
}
protected abstract Enumerator<E> enumerator(CalciteMetaImpl connection);
public <T> Queryable<T> asQueryable(QueryProvider queryProvider,
SchemaPlus schema, String tableName) {
return new AbstractTableQueryable<T>(queryProvider, schema, this,
tableName) {
@SuppressWarnings("unchecked")
public Enumerator<T> enumerator() {
return (Enumerator<T>) MetadataTable.this.enumerator(
((CalciteConnectionImpl) queryProvider).meta());
}
};
}
}
/** Iterator that returns at most {@code limit} rows from an underlying
* {@link Iterator}. */
private static class LimitIterator<E> implements Iterator<E> {
private final Iterator<E> iterator;
private final long limit;
int i = 0;
private LimitIterator(Iterator<E> iterator, long limit) {
this.iterator = iterator;
this.limit = limit;
}
static <E> Iterator<E> of(Iterator<E> iterator, long limit) {
if (limit <= 0) {
return iterator;
}
return new LimitIterator<>(iterator, limit);
}
public boolean hasNext() {
return iterator.hasNext() && i < limit;
}
public E next() {
++i;
return iterator.next();
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}
// End CalciteMetaImpl.java