org.apache.calcite.jdbc.MetaImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.calcite.jdbc.MetaImpl.java

Source

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