/*
// Licensed to Julian Hyde under one or more contributor license
// agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership.
//
// Julian Hyde 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 net.hydromatic.optiq.jdbc;
import net.hydromatic.avatica.*;
import net.hydromatic.linq4j.*;
import net.hydromatic.linq4j.expressions.*;
import net.hydromatic.linq4j.function.*;
import net.hydromatic.optiq.*;
import net.hydromatic.optiq.Table;
import net.hydromatic.optiq.impl.AbstractTableQueryable;
import net.hydromatic.optiq.impl.java.AbstractQueryableTable;
import net.hydromatic.optiq.impl.java.JavaTypeFactory;
import net.hydromatic.optiq.runtime.*;
import org.eigenbase.reltype.*;
import org.eigenbase.sql.SqlJdbcFunctionCall;
import org.eigenbase.sql.parser.SqlParser;
import org.eigenbase.util.Pair;
import org.eigenbase.util.Util;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.*;
import java.sql.Types;
import java.util.*;
import java.util.regex.Pattern;
/**
* Helper for implementing the {@code getXxx} methods such as
* {@link net.hydromatic.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 OptiqConnectionImpl connection;
public MetaImpl(OptiqConnectionImpl 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(Util.camelToUpper(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(
OptiqConnectionImpl connection,
final Class<E> clazz) {
return createResultSet(
connection,
fieldMetaData(clazz),
new RecordEnumeratorCursor<E>(Linq4j.<E>emptyEnumerator(), clazz));
}
private static <E> ResultSet createResultSet(OptiqConnectionImpl connection,
final ColumnMetaData.StructType structType,
final Cursor cursor) {
try {
final AvaticaResultSet resultSet = connection.getFactory().newResultSet(
connection.createStatement(),
new OptiqPrepare.PrepareResult<E>("",
ImmutableList.<AvaticaParameter>of(), null,
structType, -1, null, Object.class) {
@Override
public Cursor createCursor(DataContext dataContext) {
return cursor;
}
},
connection.getTimeZone());
return OptiqConnectionImpl.TROJAN.execute(resultSet);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
private static ResultSet createResultSet(
OptiqConnectionImpl connection,
final Enumerable<?> enumerable,
final NamedFieldGetter columnGetter) {
//noinspection unchecked
return createResultSet(connection, 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<OptiqSchema, MetaSchema>() {
public MetaSchema apply(OptiqSchema optiqSchema) {
return new MetaSchema(
optiqSchema,
connection.getCatalog(),
optiqSchema.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.optiqSchema.getTableNames())
.select(
new Function1<String, MetaTable>() {
public MetaTable apply(String name) {
final Table table =
schema.optiqSchema.getTable(name, true).getValue();
return new MetaTable(table,
schema.tableCatalog,
schema.tableSchem,
name);
}
})
.concat(
Linq4j.asEnumerable(
schema.optiqSchema.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.optiqTable.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_) {
OptiqResultSet resultSet = (OptiqResultSet) resultSet_;
final DataContext dataContext =
connection.createDataContext(
OptiqConnectionImpl.TROJAN.getParameterValues(
resultSet.getStatement()));
OptiqPrepare.PrepareResult prepareResult = resultSet.getPrepareResult();
return prepareResult.createCursor(dataContext);
}
public AvaticaPrepareResult prepare(AvaticaStatement statement_, String sql) {
OptiqStatement statement = (OptiqStatement) 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(OptiqConnection connection) {
return ((OptiqConnectionImpl) connection)
.createDataContext(ImmutableList.of());
}
/** A trojan-horse method, subject to change without notice. */
@VisibleForTesting
public static OptiqConnection connect(OptiqRootSchema 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 optiqTable;
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 optiqTable, String tableCat, String tableSchem,
String tableName) {
this.optiqTable = optiqTable;
assert optiqTable != null;
this.tableCat = tableCat;
this.tableSchem = tableSchem;
this.tableName = tableName;
this.tableType = optiqTable.getJdbcTableType().name();
}
public String getName() {
return tableName;
}
}
/** Metadata describing a schema. */
public static class MetaSchema implements Named {
private final OptiqSchema optiqSchema;
public final String tableCatalog;
public final String tableSchem;
public MetaSchema(
OptiqSchema optiqSchema,
String tableCatalog,
String tableSchem) {
this.optiqSchema = optiqSchema;
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 = Util.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);
}
}
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(
((OptiqConnectionImpl) queryProvider).meta());
}
};
}
}
}
// End MetaImpl.java