net.hydromatic.optiq.Schemas.java Source code

Java tutorial

Introduction

Here is the source code for net.hydromatic.optiq.Schemas.java

Source

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

import net.hydromatic.linq4j.QueryProvider;
import net.hydromatic.linq4j.Queryable;
import net.hydromatic.linq4j.expressions.*;

import net.hydromatic.optiq.impl.java.JavaTypeFactory;
import net.hydromatic.optiq.jdbc.*;

import org.eigenbase.reltype.RelDataType;
import org.eigenbase.reltype.RelDataTypeFactory;
import org.eigenbase.reltype.RelProtoDataType;
import org.eigenbase.sql.type.SqlTypeUtil;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.Connection;
import java.util.*;

/**
 * Utility functions for schemas.
 */
public final class Schemas {
    private Schemas() {
        throw new AssertionError("no instances!");
    }

    public static OptiqSchema.TableFunctionEntry resolve(RelDataTypeFactory typeFactory, String name,
            Collection<OptiqSchema.TableFunctionEntry> tableFunctions, List<RelDataType> argumentTypes) {
        final List<OptiqSchema.TableFunctionEntry> matches = new ArrayList<OptiqSchema.TableFunctionEntry>();
        for (OptiqSchema.TableFunctionEntry member : tableFunctions) {
            if (matches(typeFactory, member.getTableFunction(), argumentTypes)) {
                matches.add(member);
            }
        }
        switch (matches.size()) {
        case 0:
            return null;
        case 1:
            return matches.get(0);
        default:
            throw new RuntimeException("More than one match for " + name + " with arguments " + argumentTypes);
        }
    }

    private static boolean matches(RelDataTypeFactory typeFactory, TableFunction member,
            List<RelDataType> argumentTypes) {
        List<Parameter> parameters = member.getParameters();
        if (parameters.size() != argumentTypes.size()) {
            return false;
        }
        for (int i = 0; i < argumentTypes.size(); i++) {
            RelDataType argumentType = argumentTypes.get(i);
            Parameter parameter = parameters.get(i);
            if (!canConvert(argumentType, parameter.getType(typeFactory))) {
                return false;
            }
        }
        return true;
    }

    private static boolean canConvert(RelDataType fromType, RelDataType toType) {
        return SqlTypeUtil.canAssignFrom(toType, fromType);
    }

    public static TableFunction methodMember(final Method method, final JavaTypeFactory typeFactory) {
        final List<Parameter> parameters = new ArrayList<Parameter>();
        for (final Class<?> parameterType : method.getParameterTypes()) {
            parameters.add(new Parameter() {
                final int ordinal = parameters.size();
                final RelDataType type = typeFactory.createType(parameterType);

                public int getOrdinal() {
                    return ordinal;
                }

                public String getName() {
                    return "a" + ordinal;
                }

                public RelDataType getType(RelDataTypeFactory typeFactory) {
                    return type;
                }
            });
        }
        return new TableFunction() {
            public List<Parameter> getParameters() {
                return parameters;
            }

            public Table apply(List<Object> arguments) {
                try {
                    //noinspection unchecked
                    return (Table) method.invoke(null, arguments.toArray());
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }

            public RelDataType getRowType(RelDataTypeFactory typeFactory) {
                final Class<?> returnType = method.getReturnType();
                return ((JavaTypeFactory) typeFactory).createType(returnType);
            }
        };
    }

    /** Returns the path of a schema, appending the name of a table if not
     * null. */
    public static List<String> path(Schema schema, String name) {
        final List<String> list = new ArrayList<String>();
        if (name != null) {
            list.add(name);
        }
        for (Schema s = schema; s != null; s = s.getParentSchema()) {
            if (s.getParentSchema() != null || !s.getName().equals("")) {
                // Omit the root schema's name from the path if it's the empty string,
                // which it usually is.
                list.add(s.getName());
            }
        }
        return ImmutableList.copyOf(Lists.reverse(list));
    }

    /** Returns the root schema. */
    public static Schema root(Schema schema) {
        for (Schema s = schema;;) {
            Schema previous = s;
            s = s.getParentSchema();
            if (s == null) {
                return previous;
            }
        }
    }

    /** Returns the expression for a sub-schema. */
    public static Expression subSchemaExpression(Schema schema, String name, Class type) {
        // (Type) schemaExpression.getSubSchema("name")
        Expression call = Expressions.call(schema.getExpression(), BuiltinMethod.SCHEMA_GET_SUB_SCHEMA.method,
                Expressions.constant(name));
        //CHECKSTYLE: IGNORE 2
        //noinspection unchecked
        if (false && type != null && !type.isAssignableFrom(Schema.class)) {
            return unwrap(call, type);
        }
        return call;
    }

    /** Converts a schema expression to a given type by calling the
     * {@link net.hydromatic.optiq.SchemaPlus#unwrap(Class)} method. */
    public static Expression unwrap(Expression call, Class type) {
        return Expressions.convert_(
                Expressions.call(call, BuiltinMethod.SCHEMA_PLUS_UNWRAP.method, Expressions.constant(type)), type);
    }

    /** Returns the expression to access a table within a schema. */
    public static Expression tableExpression(Schema schema, Type elementType, String tableName, Class clazz) {
        final MethodCallExpression expression;
        if (Table.class.isAssignableFrom(clazz)) {
            expression = Expressions.call(schema.getExpression(), BuiltinMethod.SCHEMA_GET_TABLE.method,
                    Expressions.constant(tableName));
        } else {
            expression = Expressions.call(BuiltinMethod.SCHEMAS_QUERYABLE.method, DataContext.ROOT,
                    schema.getExpression(), Expressions.constant(elementType), Expressions.constant(tableName));
        }
        return Types.castIfNecessary(clazz, expression);
    }

    public static DataContext createDataContext(Connection connection) {
        return new DummyDataContext((OptiqConnection) connection);
    }

    /** Returns a {@link Queryable}, given a fully-qualified table name. */
    public static <E> Queryable<E> queryable(DataContext root, Class<E> clazz, String... names) {
        SchemaPlus schema = root.getRootSchema();
        for (int i = 0; i < names.length - 1; i++) {
            String name = names[i];
            schema = schema.getSubSchema(name);
        }
        final String tableName = names[names.length - 1];
        return queryable(root, schema, clazz, tableName);
    }

    /** Returns a {@link Queryable}, given a schema and table name. */
    public static <E> Queryable<E> queryable(DataContext root, SchemaPlus schema, Class<E> clazz,
            String tableName) {
        QueryableTable table = (QueryableTable) schema.getTable(tableName);
        return table.asQueryable(root.getQueryProvider(), schema, tableName);
    }

    /** Parses and validates a SQL query. For use within Optiq only. */
    public static OptiqPrepare.ParseResult parse(final OptiqConnection connection, final OptiqSchema schema,
            final List<String> schemaPath, final String sql) {
        final OptiqPrepare prepare = OptiqPrepare.DEFAULT_FACTORY.apply();
        final OptiqPrepare.Context context = makeContext(connection, schema, schemaPath);
        OptiqPrepare.Dummy.push(context);
        try {
            return prepare.parse(context, sql);
        } finally {
            OptiqPrepare.Dummy.pop(context);
        }
    }

    /** Prepares a SQL query for execution. For use within Optiq only. */
    public static OptiqPrepare.PrepareResult<Object> prepare(final OptiqConnection connection,
            final OptiqSchema schema, final List<String> schemaPath, final String sql) {
        final OptiqPrepare prepare = OptiqPrepare.DEFAULT_FACTORY.apply();
        return prepare.prepareSql(makeContext(connection, schema, schemaPath), sql, null, Object[].class, -1);
    }

    private static OptiqPrepare.Context makeContext(final OptiqConnection connection, final OptiqSchema schema,
            final List<String> schemaPath) {
        if (connection == null) {
            final OptiqPrepare.Context context0 = OptiqPrepare.Dummy.peek();
            return makeContext(context0.config(), context0.getTypeFactory(), context0.getDataContext(), schema,
                    schemaPath);
        } else {
            return makeContext(connection.config(), connection.getTypeFactory(), createDataContext(connection),
                    schema, schemaPath);
        }
    }

    private static OptiqPrepare.Context makeContext(final ConnectionConfig connectionConfig,
            final JavaTypeFactory typeFactory, final DataContext dataContext, final OptiqSchema schema,
            final List<String> schemaPath) {
        return new OptiqPrepare.Context() {
            public JavaTypeFactory getTypeFactory() {
                return typeFactory;
            }

            public OptiqRootSchema getRootSchema() {
                return schema.root();
            }

            public List<String> getDefaultSchemaPath() {
                // schemaPath is usually null. If specified, it overrides schema
                // as the context within which the SQL is validated.
                if (schemaPath == null) {
                    return path(schema.schema, null);
                }
                return schemaPath;
            }

            public ConnectionConfig config() {
                return connectionConfig;
            }

            public DataContext getDataContext() {
                return dataContext;
            }

            public OptiqPrepare.SparkHandler spark() {
                return OptiqPrepare.Dummy.getSparkHandler();
            }
        };
    }

    /** Returns an implementation of
     * {@link RelProtoDataType}
     * that asks a given table for its row type with a given type factory. */
    public static RelProtoDataType proto(final Table table) {
        return new RelProtoDataType() {
            public RelDataType apply(RelDataTypeFactory typeFactory) {
                return table.getRowType(typeFactory);
            }
        };
    }

    /** Dummy data context that has no variables. */
    private static class DummyDataContext implements DataContext {
        private final OptiqConnection connection;
        private final ImmutableMap<String, Object> map;

        public DummyDataContext(OptiqConnection connection) {
            this.connection = connection;
            this.map = ImmutableMap.<String, Object>of("timeZone", TimeZone.getDefault());
        }

        public SchemaPlus getRootSchema() {
            return connection.getRootSchema();
        }

        public JavaTypeFactory getTypeFactory() {
            return connection.getTypeFactory();
        }

        public QueryProvider getQueryProvider() {
            return connection;
        }

        public Object get(String name) {
            return map.get(name);
        }
    }
}

// End Schemas.java