Java tutorial
/* * 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.AvaticaPrepareResult; import org.apache.calcite.avatica.AvaticaResultSet; import org.apache.calcite.avatica.AvaticaStatement; import org.apache.calcite.avatica.ColumnMetaData; import org.apache.calcite.avatica.Cursor; import org.apache.calcite.avatica.Meta; import org.apache.calcite.linq4j.Enumerable; import org.apache.calcite.linq4j.Enumerator; import org.apache.calcite.linq4j.Linq4j; import org.apache.calcite.linq4j.Ord; 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.linq4j.tree.Primitive; 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.runtime.EnumeratorCursor; import org.apache.calcite.runtime.FlatLists; import org.apache.calcite.runtime.RecordEnumeratorCursor; 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.sql.SqlJdbcFunctionCall; import org.apache.calcite.sql.parser.SqlParser; import org.apache.calcite.util.Pair; import org.apache.calcite.util.Util; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.CaseFormat; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; import java.util.ArrayList; 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 MetaImpl implements Meta { private static final Map<Class, Pair<Integer, String>> MAP = ImmutableMap .<Class, Pair<Integer, String>>builder().put(boolean.class, Pair.of(Types.BOOLEAN, "BOOLEAN")) .put(Boolean.class, Pair.of(Types.BOOLEAN, "BOOLEAN")) .put(byte.class, Pair.of(Types.TINYINT, "TINYINT")).put(Byte.class, Pair.of(Types.TINYINT, "TINYINT")) .put(short.class, Pair.of(Types.SMALLINT, "SMALLINT")) .put(Short.class, Pair.of(Types.SMALLINT, "SMALLINT")).put(int.class, Pair.of(Types.INTEGER, "INTEGER")) .put(Integer.class, Pair.of(Types.INTEGER, "INTEGER")).put(long.class, Pair.of(Types.BIGINT, "BIGINT")) .put(Long.class, Pair.of(Types.BIGINT, "BIGINT")).put(float.class, Pair.of(Types.FLOAT, "FLOAT")) .put(Float.class, Pair.of(Types.FLOAT, "FLOAT")).put(double.class, Pair.of(Types.DOUBLE, "DOUBLE")) .put(Double.class, Pair.of(Types.DOUBLE, "DOUBLE")).put(String.class, Pair.of(Types.VARCHAR, "VARCHAR")) .put(java.sql.Date.class, Pair.of(Types.DATE, "DATE")).put(Time.class, Pair.of(Types.TIME, "TIME")) .put(Timestamp.class, Pair.of(Types.TIMESTAMP, "TIMESTAMP")).build(); static final Driver DRIVER = new Driver(); final CalciteConnectionImpl connection; public MetaImpl(CalciteConnectionImpl connection) { this.connection = connection; } 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()); } static ColumnMetaData columnMetaData(String name, int index, Class<?> type) { Pair<Integer, String> pair = MAP.get(type); ColumnMetaData.Rep rep = ColumnMetaData.Rep.VALUE_MAP.get(type); ColumnMetaData.AvaticaType scalarType = ColumnMetaData.scalar(pair.left, pair.right, rep); return new ColumnMetaData(index, false, true, false, false, Primitive.is(type) ? DatabaseMetaData.columnNullable : DatabaseMetaData.columnNoNulls, true, -1, name, name, null, 0, 0, null, null, scalarType, true, false, false, null); } static ColumnMetaData.StructType fieldMetaData(Class clazz) { final List<ColumnMetaData> list = new ArrayList<ColumnMetaData>(); for (Field field : clazz.getFields()) { if (Modifier.isPublic(field.getModifiers()) && !Modifier.isStatic(field.getModifiers())) { list.add(columnMetaData(CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, field.getName()), list.size() + 1, field.getType())); } } return ColumnMetaData.struct(list); } /** Creates an empty result set. Useful for JDBC metadata methods that are * not implemented or which query entities that are not supported (e.g. * triggers in Lingual). */ public static <E> ResultSet createEmptyResultSet(CalciteConnectionImpl connection, final Class<E> clazz) { return createResultSet(connection, ImmutableMap.<String, Object>of(), fieldMetaData(clazz), new RecordEnumeratorCursor<E>(Linq4j.<E>emptyEnumerator(), clazz)); } private static <E> ResultSet createResultSet(CalciteConnectionImpl connection, final Map<String, Object> internalParameters, final ColumnMetaData.StructType structType, final Cursor cursor) { try { final AvaticaResultSet resultSet = connection.getFactory().newResultSet(connection.createStatement(), new CalcitePrepare.PrepareResult<E>("", ImmutableList.<AvaticaParameter>of(), internalParameters, null, structType, -1, null, Object.class) { @Override public Cursor createCursor(DataContext dataContext) { return cursor; } }, connection.getTimeZone()); return CalciteConnectionImpl.TROJAN.execute(resultSet); } catch (SQLException e) { throw new RuntimeException(e); } } private static ResultSet createResultSet(CalciteConnectionImpl connection, final Enumerable<?> enumerable, final NamedFieldGetter columnGetter) { //noinspection unchecked return createResultSet(connection, ImmutableMap.<String, Object>of(), columnGetter.structType, columnGetter.cursor(((Enumerable) enumerable).enumerator())); } public String getSqlKeywords() { return SqlParser.create("").getMetadata().getJdbcKeywords(); } public String getNumericFunctions() { return SqlJdbcFunctionCall.getNumericFunctions(); } public String getStringFunctions() { return SqlJdbcFunctionCall.getStringFunctions(); } public String getSystemFunctions() { return SqlJdbcFunctionCall.getSystemFunctions(); } public String getTimeDateFunctions() { return SqlJdbcFunctionCall.getTimeDateFunctions(); } public ResultSet getTables(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(connection, schemas(catalog).where(schemaMatcher) .selectMany(new Function1<MetaSchema, Enumerable<MetaTable>>() { public Enumerable<MetaTable> apply(MetaSchema schema) { return tables(schema, matcher(tableNamePattern)); } }).where(typeFilter), new NamedFieldGetter(MetaTable.class, "TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "TABLE_TYPE", "REMARKS", "TYPE_CAT", "TYPE_SCHEM", "TYPE_NAME", "SELF_REFERENCING_COL_NAME", "REF_GENERATION")); } public ResultSet getColumns(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(connection, 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), new NamedFieldGetter(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_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(connection.rootSchema.getSubSchemaMap().values()) .select(new Function1<CalciteSchema, MetaSchema>() { public MetaSchema apply(CalciteSchema calciteSchema) { return new MetaSchema(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) { return Linq4j.asEnumerable(schema.calciteSchema.getTableNames()).select(new Function1<String, MetaTable>() { public MetaTable apply(String name) { final Table table = schema.calciteSchema.getTable(name, true).getValue(); return new MetaTable(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 MetaTable(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()); } }); } public Enumerable<MetaColumn> columns(final MetaTable table) { final RelDataType rowType = table.calciteTable.getRowType(connection.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 ResultSet getSchemas(String catalog, Pat schemaPattern) { final Predicate1<MetaSchema> schemaMatcher = namedMatcher(schemaPattern); return createResultSet(connection, schemas(catalog).where(schemaMatcher), new NamedFieldGetter(MetaSchema.class, "TABLE_SCHEM", "TABLE_CATALOG")); } public ResultSet getCatalogs() { return createResultSet(connection, catalogs(), new NamedFieldGetter(MetaCatalog.class, "TABLE_CATALOG")); } public ResultSet getTableTypes() { return createResultSet(connection, tableTypes(), new NamedFieldGetter(MetaTableType.class, "TABLE_TYPE")); } public ResultSet getProcedures(String catalog, Pat schemaPattern, Pat procedureNamePattern) { return createEmptyResultSet(connection, MetaProcedure.class); } public ResultSet getProcedureColumns(String catalog, Pat schemaPattern, Pat procedureNamePattern, Pat columnNamePattern) { return createEmptyResultSet(connection, MetaProcedureColumn.class); } public ResultSet getColumnPrivileges(String catalog, String schema, String table, Pat columnNamePattern) { return createEmptyResultSet(connection, MetaColumnPrivilege.class); } public ResultSet getTablePrivileges(String catalog, Pat schemaPattern, Pat tableNamePattern) { return createEmptyResultSet(connection, MetaTablePrivilege.class); } public ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) { return createEmptyResultSet(connection, MetaBestRowIdentifier.class); } public ResultSet getVersionColumns(String catalog, String schema, String table) { return createEmptyResultSet(connection, MetaVersionColumn.class); } public ResultSet getPrimaryKeys(String catalog, String schema, String table) { return createEmptyResultSet(connection, MetaPrimaryKey.class); } public ResultSet getImportedKeys(String catalog, String schema, String table) { return createEmptyResultSet(connection, MetaImportedKey.class); } public ResultSet getExportedKeys(String catalog, String schema, String table) { return createEmptyResultSet(connection, MetaExportedKey.class); } public ResultSet getCrossReference(String parentCatalog, String parentSchema, String parentTable, String foreignCatalog, String foreignSchema, String foreignTable) { return createEmptyResultSet(connection, MetaCrossReference.class); } public ResultSet getTypeInfo() { return createEmptyResultSet(connection, MetaTypeInfo.class); } public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) { return createEmptyResultSet(connection, MetaIndexInfo.class); } public ResultSet getUDTs(String catalog, Pat schemaPattern, Pat typeNamePattern, int[] types) { return createEmptyResultSet(connection, MetaUdt.class); } public ResultSet getSuperTypes(String catalog, Pat schemaPattern, Pat typeNamePattern) { return createEmptyResultSet(connection, MetaSuperType.class); } public ResultSet getSuperTables(String catalog, Pat schemaPattern, Pat tableNamePattern) { return createEmptyResultSet(connection, MetaSuperTable.class); } public ResultSet getAttributes(String catalog, Pat schemaPattern, Pat typeNamePattern, Pat attributeNamePattern) { return createEmptyResultSet(connection, MetaAttribute.class); } public ResultSet getClientInfoProperties() { return createEmptyResultSet(connection, MetaClientInfoProperty.class); } public ResultSet getFunctions(String catalog, Pat schemaPattern, Pat functionNamePattern) { return createEmptyResultSet(connection, MetaFunction.class); } public ResultSet getFunctionColumns(String catalog, Pat schemaPattern, Pat functionNamePattern, Pat columnNamePattern) { return createEmptyResultSet(connection, MetaFunctionColumn.class); } public ResultSet getPseudoColumns(String catalog, Pat schemaPattern, Pat tableNamePattern, Pat columnNamePattern) { return createEmptyResultSet(connection, MetaPseudoColumn.class); } public Cursor createCursor(AvaticaResultSet resultSet_) { CalciteResultSet resultSet = (CalciteResultSet) resultSet_; Map<String, Object> map = Maps.newLinkedHashMap(); final List<Object> parameterValues = CalciteConnectionImpl.TROJAN .getParameterValues(resultSet.getStatement()); for (Ord<Object> o : Ord.zip(parameterValues)) { map.put("?" + o.i, o.e); } map.putAll(resultSet.getPrepareResult().getInternalParameters()); final DataContext dataContext = connection.createDataContext(map); CalcitePrepare.PrepareResult prepareResult = resultSet.getPrepareResult(); return prepareResult.createCursor(dataContext); } public AvaticaPrepareResult prepare(AvaticaStatement statement_, String sql) { CalciteStatement statement = (CalciteStatement) statement_; int maxRowCount = statement.getMaxRows(); return connection.parseQuery(sql, statement.createPrepareContext(), maxRowCount <= 0 ? -1 : maxRowCount); } /** 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(CalciteRootSchema schema, JavaTypeFactory typeFactory) { return DRIVER.connect(schema, typeFactory); } /** An object that has a name. */ interface Named { String getName(); } /** Metadata describing a column. */ public static class MetaColumn implements Named { public final String tableCat; public final String tableSchem; public final String tableName; public final String columnName; public final int dataType; public final String typeName; public final int columnSize; public final String bufferLength = null; public final Integer decimalDigits; public final int numPrecRadix; public final int nullable; public final String remarks = null; public final String columnDef = null; public final String sqlDataType = null; public final String sqlDatetimeSub = null; public final int charOctetLength; public final int ordinalPosition; public final String isNullable; public final String scopeCatalog = null; public final String scopeTable = null; public final String sourceDataType = null; public final String isAutoincrement = null; public final String isGeneratedcolumn = null; MetaColumn(String tableCat, String tableSchem, String tableName, String columnName, int dataType, String typeName, int columnSize, Integer decimalDigits, int numPrecRadix, int nullable, int charOctetLength, int ordinalPosition, String isNullable) { this.tableCat = tableCat; this.tableSchem = tableSchem; this.tableName = tableName; this.columnName = columnName; this.dataType = dataType; this.typeName = typeName; this.columnSize = columnSize; this.decimalDigits = decimalDigits; this.numPrecRadix = numPrecRadix; this.nullable = nullable; this.charOctetLength = charOctetLength; this.ordinalPosition = ordinalPosition; this.isNullable = isNullable; } public String getName() { return columnName; } } /** Metadata describing a table. */ public static class MetaTable implements Named { private final Table calciteTable; public final String tableCat; public final String tableSchem; public final String tableName; public final String tableType; public final String remarks = null; public final String typeCat = null; public final String typeSchem = null; public final String typeName = null; public final String selfReferencingColName = null; public final String refGeneration = null; public MetaTable(Table calciteTable, String tableCat, String tableSchem, String tableName) { this.calciteTable = calciteTable; assert calciteTable != null; this.tableCat = tableCat; this.tableSchem = tableSchem; this.tableName = tableName; this.tableType = calciteTable.getJdbcTableType().name(); } public String getName() { return tableName; } } /** Metadata describing a schema. */ public static class MetaSchema implements Named { private final CalciteSchema calciteSchema; public final String tableCatalog; public final String tableSchem; public MetaSchema(CalciteSchema calciteSchema, String tableCatalog, String tableSchem) { this.calciteSchema = calciteSchema; this.tableCatalog = tableCatalog; this.tableSchem = tableSchem; } public String getName() { return tableSchem; } } /** Metadata describing a catalog. */ public static class MetaCatalog implements Named { public final String tableCatalog; public MetaCatalog(String tableCatalog) { this.tableCatalog = tableCatalog; } public String getName() { return tableCatalog; } } /** Metadata describing a table type. */ public static class MetaTableType { public final String tableType; public MetaTableType(String tableType) { this.tableType = tableType; } } /** Metadata describing a procedure. */ public static class MetaProcedure { } /** Metadata describing a procedure column. */ public static class MetaProcedureColumn { } /** Metadata describing a column privilege. */ public static class MetaColumnPrivilege { } /** Metadata describing a table privilege. */ public static class MetaTablePrivilege { } /** Metadata describing the best identifier for a row. */ public static class MetaBestRowIdentifier { } /** Metadata describing a version column. */ public static class MetaVersionColumn { public final short scope; public final String columnName; public final int dataType; public final String typeName; public final int columnSize; public final int bufferLength; public final short decimalDigits; public final short pseudoColumn; MetaVersionColumn(short scope, String columnName, int dataType, String typeName, int columnSize, int bufferLength, short decimalDigits, short pseudoColumn) { this.scope = scope; this.columnName = columnName; this.dataType = dataType; this.typeName = typeName; this.columnSize = columnSize; this.bufferLength = bufferLength; this.decimalDigits = decimalDigits; this.pseudoColumn = pseudoColumn; } } /** Metadata describing a primary key. */ public static class MetaPrimaryKey { public final String tableCat; public final String tableSchem; public final String tableName; public final String columnName; public final short keySeq; public final String pkName; MetaPrimaryKey(String tableCat, String tableSchem, String tableName, String columnName, short keySeq, String pkName) { this.tableCat = tableCat; this.tableSchem = tableSchem; this.tableName = tableName; this.columnName = columnName; this.keySeq = keySeq; this.pkName = pkName; } } /** Metadata describing an imported key. */ public static class MetaImportedKey { } /** Metadata describing an exported key. */ public static class MetaExportedKey { } /** Metadata describing a cross reference. */ public static class MetaCrossReference { } /** Metadata describing type info. */ public static class MetaTypeInfo { } /** Metadata describing index info. */ public static class MetaIndexInfo { } /** Metadata describing a user-defined type. */ public static class MetaUdt { } /** Metadata describing a super-type. */ public static class MetaSuperType { } /** Metadata describing an attribute. */ public static class MetaAttribute { } /** Metadata describing a client info property. */ public static class MetaClientInfoProperty { } /** Metadata describing a function. */ public static class MetaFunction { } /** Metadata describing a function column. */ public static class MetaFunctionColumn { } /** Metadata describing a pseudo column. */ public static class MetaPseudoColumn { } /** Metadata describing a super-table. */ public static class MetaSuperTable { } /** Accesses fields by name. */ private static class NamedFieldGetter { private final List<Field> fields = new ArrayList<Field>(); private final ColumnMetaData.StructType structType; public NamedFieldGetter(Class clazz, String... names) { final List<ColumnMetaData> columns = new ArrayList<ColumnMetaData>(); init(clazz, names, columns, fields); structType = ColumnMetaData.struct(columns); } private static void init(Class clazz, String[] names, List<ColumnMetaData> columns, List<Field> fields) { for (String name : names) { final int index = fields.size(); final String fieldName = CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, 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); } } Object get(Object o, int columnIndex) { try { return fields.get(columnIndex).get(o); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } public Cursor cursor(Enumerator<Object> enumerator) { //noinspection unchecked return new EnumeratorCursor(enumerator) { protected Getter createGetter(final int ordinal) { return new Getter() { public Object getObject() { return get(current(), ordinal); } public boolean wasNull() { return getObject() == null; } }; } }; } } /** 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(MetaImpl 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()); } }; } } } // End MetaImpl.java