| /* |
| * 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 |