org.apache.calcite.sql.SqlUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.calcite.sql.SqlUtil.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.sql;

import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.linq4j.function.Function1;
import org.apache.calcite.linq4j.function.Functions;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypePrecedenceList;
import org.apache.calcite.runtime.CalciteContextException;
import org.apache.calcite.runtime.CalciteException;
import org.apache.calcite.runtime.PredicateImpl;
import org.apache.calcite.runtime.Resources;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.sql.util.SqlBasicVisitor;
import org.apache.calcite.util.BarfingInvocationHandler;
import org.apache.calcite.util.ConversionUtil;
import org.apache.calcite.util.Glossary;
import org.apache.calcite.util.NlsString;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;

import java.nio.charset.Charset;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import static org.apache.calcite.util.Static.RESOURCE;

/**
 * Contains utility functions related to SQL parsing, all static.
 */
public abstract class SqlUtil {
    //~ Methods ----------------------------------------------------------------

    static SqlNode andExpressions(SqlNode node1, SqlNode node2) {
        if (node1 == null) {
            return node2;
        }
        ArrayList<SqlNode> list = new ArrayList<SqlNode>();
        if (node1.getKind() == SqlKind.AND) {
            list.addAll(((SqlCall) node1).getOperandList());
        } else {
            list.add(node1);
        }
        if (node2.getKind() == SqlKind.AND) {
            list.addAll(((SqlCall) node2).getOperandList());
        } else {
            list.add(node2);
        }
        return SqlStdOperatorTable.AND.createCall(SqlParserPos.ZERO, list);
    }

    static ArrayList<SqlNode> flatten(SqlNode node) {
        ArrayList<SqlNode> list = new ArrayList<SqlNode>();
        flatten(node, list);
        return list;
    }

    /**
     * Returns the <code>n</code>th (0-based) input to a join expression.
     */
    public static SqlNode getFromNode(SqlSelect query, int ordinal) {
        ArrayList<SqlNode> list = flatten(query.getFrom());
        return list.get(ordinal);
    }

    private static void flatten(SqlNode node, ArrayList<SqlNode> list) {
        switch (node.getKind()) {
        case JOIN:
            SqlJoin join = (SqlJoin) node;
            flatten(join.getLeft(), list);
            flatten(join.getRight(), list);
            return;
        case AS:
            SqlCall call = (SqlCall) node;
            flatten(call.operand(0), list);
            return;
        default:
            list.add(node);
            return;
        }
    }

    /**
     * Converts an SqlNode array to a SqlNodeList
     */
    public static SqlNodeList toNodeList(SqlNode[] operands) {
        SqlNodeList ret = new SqlNodeList(SqlParserPos.ZERO);
        for (SqlNode node : operands) {
            ret.add(node);
        }
        return ret;
    }

    /**
     * Returns whether a node represents the NULL value.
     *
     * <p>Examples:
     *
     * <ul>
     * <li>For {@link SqlLiteral} Unknown, returns false.
     * <li>For <code>CAST(NULL AS <i>type</i>)</code>, returns true if <code>
     * allowCast</code> is true, false otherwise.
     * <li>For <code>CAST(CAST(NULL AS <i>type</i>) AS <i>type</i>))</code>,
     * returns false.
     * </ul>
     */
    public static boolean isNullLiteral(SqlNode node, boolean allowCast) {
        if (node instanceof SqlLiteral) {
            SqlLiteral literal = (SqlLiteral) node;
            if (literal.getTypeName() == SqlTypeName.NULL) {
                assert null == literal.getValue();
                return true;
            } else {
                // We don't regard UNKNOWN -- SqlLiteral(null,Boolean) -- as
                // NULL.
                return false;
            }
        }
        if (allowCast) {
            if (node.getKind() == SqlKind.CAST) {
                SqlCall call = (SqlCall) node;
                if (isNullLiteral(call.operand(0), false)) {
                    // node is "CAST(NULL as type)"
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Returns whether a node represents the NULL value or a series of nested
     * <code>CAST(NULL AS type)</code> calls. For example:
     * <code>isNull(CAST(CAST(NULL as INTEGER) AS VARCHAR(1)))</code>
     * returns {@code true}.
     */
    public static boolean isNull(SqlNode node) {
        return isNullLiteral(node, false) || node.getKind() == SqlKind.CAST && isNull(((SqlCall) node).operand(0));
    }

    /**
     * Returns whether a node is a literal.
     *
     * <p>Examples:
     *
     * <ul>
     * <li>For <code>CAST(literal AS <i>type</i>)</code>, returns true if <code>
     * allowCast</code> is true, false otherwise.
     * <li>For <code>CAST(CAST(literal AS <i>type</i>) AS <i>type</i>))</code>,
     * returns false.
     * </ul>
     *
     * @param node The node, never null.
     * @param allowCast whether to regard CAST(literal) as a literal
     * @return Whether the node is a literal
     */
    public static boolean isLiteral(SqlNode node, boolean allowCast) {
        assert node != null;
        if (node instanceof SqlLiteral) {
            return true;
        }
        if (allowCast) {
            if (node.getKind() == SqlKind.CAST) {
                SqlCall call = (SqlCall) node;
                if (isLiteral(call.operand(0), false)) {
                    // node is "CAST(literal as type)"
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Returns whether a node is a literal.
     *
     * <p>Many constructs which require literals also accept <code>CAST(NULL AS
     * <i>type</i>)</code>. This method does not accept casts, so you should
     * call {@link #isNullLiteral} first.
     *
     * @param node The node, never null.
     * @return Whether the node is a literal
     */
    public static boolean isLiteral(SqlNode node) {
        return isLiteral(node, false);
    }

    /**
     * Returns whether a node is a literal chain which is used to represent a
     * continued string literal.
     *
     * @param node The node, never null.
     * @return Whether the node is a literal chain
     */
    public static boolean isLiteralChain(SqlNode node) {
        assert node != null;
        if (node instanceof SqlCall) {
            SqlCall call = (SqlCall) node;
            return call.getKind() == SqlKind.LITERAL_CHAIN;
        } else {
            return false;
        }
    }

    /**
     * Unparses a call to an operator which has function syntax.
     *
     * @param operator    The operator
     * @param writer      Writer
     * @param call    List of 0 or more operands
     */
    public static void unparseFunctionSyntax(SqlOperator operator, SqlWriter writer, SqlCall call) {
        if (operator instanceof SqlFunction) {
            SqlFunction function = (SqlFunction) operator;

            if (function.getFunctionType().isSpecific()) {
                writer.keyword("SPECIFIC");
            }
            SqlIdentifier id = function.getSqlIdentifier();
            if (id == null) {
                writer.keyword(operator.getName());
            } else {
                id.unparse(writer, 0, 0);
            }
        } else {
            writer.print(operator.getName());
        }
        if (call.operandCount() == 0) {
            switch (call.getOperator().getSyntax()) {
            case FUNCTION_ID:
                // For example, the "LOCALTIME" function appears as "LOCALTIME"
                // when it has 0 args, not "LOCALTIME()".
                return;
            case FUNCTION_STAR: // E.g. "COUNT(*)"
            case FUNCTION: // E.g. "RANK()"
                // fall through - dealt with below
            }
        }
        final SqlWriter.Frame frame = writer.startList(SqlWriter.FrameTypeEnum.FUN_CALL, "(", ")");
        final SqlLiteral quantifier = call.getFunctionQuantifier();
        if (quantifier != null) {
            quantifier.unparse(writer, 0, 0);
        }
        if (call.operandCount() == 0) {
            switch (call.getOperator().getSyntax()) {
            case FUNCTION_STAR:
                writer.sep("*");
            }
        }
        for (SqlNode operand : call.getOperandList()) {
            writer.sep(",");
            operand.unparse(writer, 0, 0);
        }
        writer.endList(frame);
    }

    public static void unparseBinarySyntax(SqlOperator operator, SqlCall call, SqlWriter writer, int leftPrec,
            int rightPrec) {
        assert call.operandCount() == 2;
        final SqlWriter.Frame frame = writer
                .startList((operator instanceof SqlSetOperator) ? SqlWriter.FrameTypeEnum.SETOP
                        : SqlWriter.FrameTypeEnum.SIMPLE);
        call.operand(0).unparse(writer, leftPrec, operator.getLeftPrec());
        final boolean needsSpace = operator.needsSpace();
        writer.setNeedWhitespace(needsSpace);
        writer.sep(operator.getName());
        writer.setNeedWhitespace(needsSpace);
        call.operand(1).unparse(writer, operator.getRightPrec(), rightPrec);
        writer.endList(frame);
    }

    /**
     * Concatenates string literals.
     *
     * <p>This method takes an array of arguments, since pairwise concatenation
     * means too much string copying.
     *
     * @param lits an array of {@link SqlLiteral}, not empty, all of the same
     *             class
     * @return a new {@link SqlLiteral}, of that same class, whose value is the
     * string concatenation of the values of the literals
     * @throws ClassCastException             if the lits are not homogeneous.
     * @throws ArrayIndexOutOfBoundsException if lits is an empty array.
     */
    public static SqlLiteral concatenateLiterals(List<SqlLiteral> lits) {
        if (lits.size() == 1) {
            return lits.get(0); // nothing to do
        }
        return ((SqlAbstractStringLiteral) lits.get(0)).concat1(lits);
    }

    /**
     * Looks up a (possibly overloaded) routine based on name and argument
     * types.
     *
     * @param opTab    operator table to search
     * @param funcName name of function being invoked
     * @param argTypes argument types
     * @param argNames argument names, or null if call by position
     * @param category whether a function or a procedure. (If a procedure is
     *                 being invoked, the overload rules are simpler.)
     * @return matching routine, or null if none found
     *
     * @see Glossary#SQL99 SQL:1999 Part 2 Section 10.4
     */
    public static SqlOperator lookupRoutine(SqlOperatorTable opTab, SqlIdentifier funcName,
            List<RelDataType> argTypes, List<String> argNames, SqlFunctionCategory category, SqlSyntax syntax,
            SqlKind sqlKind) {
        Iterator<SqlOperator> list = lookupSubjectRoutines(opTab, funcName, argTypes, argNames, syntax, sqlKind,
                category);
        if (list.hasNext()) {
            // return first on schema path
            return list.next();
        }
        return null;
    }

    private static Iterator<SqlOperator> filterOperatorRoutinesByKind(Iterator<SqlOperator> routines,
            final SqlKind sqlKind) {
        return Iterators.filter(routines, new PredicateImpl<SqlOperator>() {
            public boolean test(SqlOperator input) {
                return input.getKind() == sqlKind;
            }
        });
    }

    /**
     * Looks up all subject routines matching the given name and argument types.
     *
     * @param opTab     operator table to search
     * @param funcName  name of function being invoked
     * @param argTypes  argument types
     * @param argNames  argument names, or null if call by position
     * @param sqlSyntax the SqlSyntax of the SqlOperator being looked up
     * @param sqlKind   the SqlKind of the SqlOperator being looked up
     * @param category category of routine to look up
     * @return list of matching routines
     * @see Glossary#SQL99 SQL:1999 Part 2 Section 10.4
     */
    public static Iterator<SqlOperator> lookupSubjectRoutines(SqlOperatorTable opTab, SqlIdentifier funcName,
            List<RelDataType> argTypes, List<String> argNames, SqlSyntax sqlSyntax, SqlKind sqlKind,
            SqlFunctionCategory category) {
        // start with all routines matching by name
        Iterator<SqlOperator> routines = lookupSubjectRoutinesByName(opTab, funcName, sqlSyntax, category);

        // first pass:  eliminate routines which don't accept the given
        // number of arguments
        routines = filterRoutinesByParameterCount(routines, argTypes);

        // NOTE: according to SQL99, procedures are NOT overloaded on type,
        // only on number of arguments.
        if (category == SqlFunctionCategory.USER_DEFINED_PROCEDURE) {
            return routines;
        }

        // second pass:  eliminate routines which don't accept the given
        // argument types
        routines = filterRoutinesByParameterType(sqlSyntax, routines, argTypes, argNames);

        // see if we can stop now; this is necessary for the case
        // of builtin functions where we don't have param type info
        final List<SqlOperator> list = Lists.newArrayList(routines);
        routines = list.iterator();
        if (list.size() < 2) {
            return routines;
        }

        // third pass:  for each parameter from left to right, eliminate
        // all routines except those with the best precedence match for
        // the given arguments
        routines = filterRoutinesByTypePrecedence(sqlSyntax, routines, argTypes);

        // fourth pass: eliminate routines which do not have the same
        // SqlKind as requested
        return filterOperatorRoutinesByKind(routines, sqlKind);
    }

    /**
     * Determines whether there is a routine matching the given name and number
     * of arguments.
     *
     * @param opTab    operator table to search
     * @param funcName name of function being invoked
     * @param argTypes argument types
     * @param category category of routine to look up
     * @return true if match found
     */
    public static boolean matchRoutinesByParameterCount(SqlOperatorTable opTab, SqlIdentifier funcName,
            List<RelDataType> argTypes, SqlFunctionCategory category) {
        // start with all routines matching by name
        Iterator<SqlOperator> routines = lookupSubjectRoutinesByName(opTab, funcName, SqlSyntax.FUNCTION, category);

        // first pass:  eliminate routines which don't accept the given
        // number of arguments
        routines = filterRoutinesByParameterCount(routines, argTypes);

        return routines.hasNext();
    }

    private static Iterator<SqlOperator> lookupSubjectRoutinesByName(SqlOperatorTable opTab, SqlIdentifier funcName,
            final SqlSyntax syntax, SqlFunctionCategory category) {
        final List<SqlOperator> sqlOperators = new ArrayList<>();
        opTab.lookupOperatorOverloads(funcName, category, syntax, sqlOperators);
        switch (syntax) {
        case FUNCTION:
            return Iterators.filter(sqlOperators.iterator(), Predicates.instanceOf(SqlFunction.class));
        default:
            return Iterators.filter(sqlOperators.iterator(), new PredicateImpl<SqlOperator>() {
                public boolean test(SqlOperator operator) {
                    return operator.getSyntax() == syntax;
                }
            });
        }
    }

    private static Iterator<SqlOperator> filterRoutinesByParameterCount(Iterator<SqlOperator> routines,
            final List<RelDataType> argTypes) {
        return Iterators.filter(routines, new PredicateImpl<SqlOperator>() {
            public boolean test(SqlOperator operator) {
                SqlOperandCountRange od = operator.getOperandCountRange();
                return od.isValidCount(argTypes.size());
            }
        });
    }

    /**
     * @see Glossary#SQL99 SQL:1999 Part 2 Section 10.4 Syntax Rule 6.b.iii.2.B
     */
    private static Iterator<SqlOperator> filterRoutinesByParameterType(SqlSyntax syntax,
            final Iterator<SqlOperator> routines, final List<RelDataType> argTypes, final List<String> argNames) {
        if (syntax != SqlSyntax.FUNCTION) {
            return routines;
        }

        //noinspection unchecked
        return (Iterator) Iterators.filter(Iterators.filter(routines, SqlFunction.class),
                new PredicateImpl<SqlFunction>() {
                    public boolean test(SqlFunction function) {
                        List<RelDataType> paramTypes = function.getParamTypes();
                        if (paramTypes == null) {
                            // no parameter information for builtins; keep for now
                            return true;
                        }
                        final List<RelDataType> permutedArgTypes;
                        if (argNames != null) {
                            // Arguments passed by name. Make sure that the function has
                            // parameters of all of these names.
                            final Map<Integer, Integer> map = new HashMap<>();
                            for (Ord<String> argName : Ord.zip(argNames)) {
                                final int i = function.getParamNames().indexOf(argName.e);
                                if (i < 0) {
                                    return false;
                                }
                                map.put(i, argName.i);
                            }
                            permutedArgTypes = Functions.generate(paramTypes.size(),
                                    new Function1<Integer, RelDataType>() {
                                        public RelDataType apply(Integer a0) {
                                            if (map.containsKey(a0)) {
                                                return argTypes.get(map.get(a0));
                                            } else {
                                                return null;
                                            }
                                        }
                                    });
                        } else {
                            permutedArgTypes = Lists.newArrayList(argTypes);
                            while (permutedArgTypes.size() < argTypes.size()) {
                                paramTypes.add(null);
                            }
                        }
                        for (Pair<RelDataType, RelDataType> p : Pair.zip(paramTypes, permutedArgTypes)) {
                            final RelDataType argType = p.right;
                            final RelDataType paramType = p.left;
                            if (argType != null && !SqlTypeUtil.canCastFrom(paramType, argType, false)) {
                                return false;
                            }
                        }
                        return true;
                    }
                });
    }

    /**
     * @see Glossary#SQL99 SQL:1999 Part 2 Section 9.4
     */
    private static Iterator<SqlOperator> filterRoutinesByTypePrecedence(SqlSyntax sqlSyntax,
            Iterator<SqlOperator> routines, List<RelDataType> argTypes) {
        if (sqlSyntax != SqlSyntax.FUNCTION) {
            return routines;
        }

        List<SqlFunction> sqlFunctions = Lists.newArrayList(Iterators.filter(routines, SqlFunction.class));

        for (final Ord<RelDataType> argType : Ord.zip(argTypes)) {
            final RelDataTypePrecedenceList precList = argType.e.getPrecedenceList();
            final RelDataType bestMatch = bestMatch(sqlFunctions, argType.i, precList);
            if (bestMatch != null) {
                sqlFunctions = Lists.newArrayList(Iterables.filter(sqlFunctions, new PredicateImpl<SqlFunction>() {
                    public boolean test(SqlFunction function) {
                        final List<RelDataType> paramTypes = function.getParamTypes();
                        if (paramTypes == null) {
                            return false;
                        }
                        final RelDataType paramType = paramTypes.get(argType.i);
                        return precList.compareTypePrecedence(paramType, bestMatch) >= 0;
                    }
                }));
            }
        }
        //noinspection unchecked
        return (Iterator) sqlFunctions.iterator();
    }

    private static RelDataType bestMatch(List<SqlFunction> sqlFunctions, int i,
            RelDataTypePrecedenceList precList) {
        RelDataType bestMatch = null;
        for (SqlFunction function : sqlFunctions) {
            List<RelDataType> paramTypes = function.getParamTypes();
            if (paramTypes == null) {
                continue;
            }
            final RelDataType paramType = paramTypes.get(i);
            if (bestMatch == null) {
                bestMatch = paramType;
            } else {
                int c = precList.compareTypePrecedence(bestMatch, paramType);
                if (c < 0) {
                    bestMatch = paramType;
                }
            }
        }
        return bestMatch;
    }

    /**
     * Returns the <code>i</code>th select-list item of a query.
     */
    public static SqlNode getSelectListItem(SqlNode query, int i) {
        switch (query.getKind()) {
        case SELECT:
            SqlSelect select = (SqlSelect) query;
            final SqlNode from = stripAs(select.getFrom());
            if (from.getKind() == SqlKind.VALUES) {
                // They wrote "VALUES (x, y)", but the validator has
                // converted this into "SELECT * FROM VALUES (x, y)".
                return getSelectListItem(from, i);
            }
            final SqlNodeList fields = select.getSelectList();

            // Range check the index to avoid index out of range.  This
            // could be expanded to actually check to see if the select
            // list is a "*"
            if (i >= fields.size()) {
                i = 0;
            }
            return fields.get(i);

        case VALUES:
            SqlCall call = (SqlCall) query;
            assert call.operandCount() > 0 : "VALUES must have at least one operand";
            final SqlCall row = call.operand(0);
            assert row.operandCount() > i : "VALUES has too few columns";
            return row.operand(i);

        default:
            // Unexpected type of query.
            throw Util.needToImplement(query);
        }
    }

    /**
     * If an identifier is a legitimate call to a function which has no
     * arguments and requires no parentheses (for example "CURRENT_USER"),
     * returns a call to that function, otherwise returns null.
     */
    public static SqlCall makeCall(SqlOperatorTable opTab, SqlIdentifier id) {
        if (id.names.size() == 1) {
            final List<SqlOperator> list = Lists.newArrayList();
            opTab.lookupOperatorOverloads(id, null, SqlSyntax.FUNCTION, list);
            for (SqlOperator operator : list) {
                if (operator.getSyntax() == SqlSyntax.FUNCTION_ID) {
                    // Even though this looks like an identifier, it is a
                    // actually a call to a function. Construct a fake
                    // call to this function, so we can use the regular
                    // operator validation.
                    return new SqlBasicCall(operator, SqlNode.EMPTY_ARRAY, id.getParserPosition(), true, null);
                }
            }
        }
        return null;
    }

    public static String deriveAliasFromOrdinal(int ordinal) {
        // Use a '$' so that queries can't easily reference the
        // generated name.
        return "EXPR$" + ordinal;
    }

    /**
     * Constructs an operator signature from a type list.
     *
     * @param op       operator
     * @param typeList list of types to use for operands. Types may be
     *                 represented as {@link String}, {@link SqlTypeFamily}, or
     *                 any object with a valid {@link Object#toString()} method.
     * @return constructed signature
     */
    public static String getOperatorSignature(SqlOperator op, List<?> typeList) {
        return getAliasedSignature(op, op.getName(), typeList);
    }

    /**
     * Constructs an operator signature from a type list, substituting an alias
     * for the operator name.
     *
     * @param op       operator
     * @param opName   name to use for operator
     * @param typeList list of {@link SqlTypeName} or {@link String} to use for
     *                 operands
     * @return constructed signature
     */
    public static String getAliasedSignature(SqlOperator op, String opName, List<?> typeList) {
        StringBuilder ret = new StringBuilder();
        String template = op.getSignatureTemplate(typeList.size());
        if (null == template) {
            ret.append("'");
            ret.append(opName);
            ret.append("(");
            for (int i = 0; i < typeList.size(); i++) {
                if (i > 0) {
                    ret.append(", ");
                }
                final String t = typeList.get(i).toString().toUpperCase(Locale.ROOT);
                ret.append("<").append(t).append(">");
            }
            ret.append(")'");
        } else {
            Object[] values = new Object[typeList.size() + 1];
            values[0] = opName;
            ret.append("'");
            for (int i = 0; i < typeList.size(); i++) {
                final String t = typeList.get(i).toString().toUpperCase(Locale.ROOT);
                values[i + 1] = "<" + t + ">";
            }
            ret.append(new MessageFormat(template, Locale.ROOT).format(values));
            ret.append("'");
            assert (typeList.size() + 1) == values.length;
        }

        return ret.toString();
    }

    /**
     * Wraps an exception with context.
     */
    public static CalciteException newContextException(final SqlParserPos pos, Resources.ExInst<?> e,
            String inputText) {
        CalciteContextException ex = newContextException(pos, e);
        ex.setOriginalStatement(inputText);
        return ex;
    }

    /**
     * Wraps an exception with context.
     */
    public static CalciteContextException newContextException(final SqlParserPos pos, Resources.ExInst<?> e) {
        int line = pos.getLineNum();
        int col = pos.getColumnNum();
        int endLine = pos.getEndLineNum();
        int endCol = pos.getEndColumnNum();
        return newContextException(line, col, endLine, endCol, e);
    }

    /**
     * Wraps an exception with context.
     */
    public static CalciteContextException newContextException(int line, int col, int endLine, int endCol,
            Resources.ExInst<?> e) {
        CalciteContextException contextExcn = (line == endLine && col == endCol
                ? RESOURCE.validatorContextPoint(line, col)
                : RESOURCE.validatorContext(line, col, endLine, endCol)).ex(e.ex());
        contextExcn.setPosition(line, col, endLine, endCol);
        return contextExcn;
    }

    /**
     * Returns whether a {@link SqlNode node} is a {@link SqlCall call} to a
     * given {@link SqlOperator operator}.
     */
    public static boolean isCallTo(SqlNode node, SqlOperator operator) {
        return (node instanceof SqlCall) && (((SqlCall) node).getOperator() == operator);
    }

    /**
     * Creates the type of an {@link org.apache.calcite.util.NlsString}.
     *
     * <p>The type inherits the The NlsString's {@link Charset} and
     * {@link SqlCollation}, if they are set, otherwise it gets the system
     * defaults.
     *
     * @param typeFactory Type factory
     * @param str         String
     * @return Type, including collation and charset
     */
    public static RelDataType createNlsStringType(RelDataTypeFactory typeFactory, NlsString str) {
        Charset charset = str.getCharset();
        if (null == charset) {
            charset = typeFactory.getDefaultCharset();
        }
        SqlCollation collation = str.getCollation();
        if (null == collation) {
            collation = SqlCollation.COERCIBLE;
        }
        RelDataType type = typeFactory.createSqlType(SqlTypeName.CHAR, str.getValue().length());
        type = typeFactory.createTypeWithCharsetAndCollation(type, charset, collation);
        return type;
    }

    /**
     * Translates a character set name from a SQL-level name into a Java-level
     * name.
     *
     * @param name SQL-level name
     * @return Java-level name, or null if SQL-level name is unknown
     */
    public static String translateCharacterSetName(String name) {
        if (name.equals("LATIN1")) {
            return "ISO-8859-1";
        } else if (name.equals("UTF16")) {
            return ConversionUtil.NATIVE_UTF16_CHARSET_NAME;
        } else if (name.equals(ConversionUtil.NATIVE_UTF16_CHARSET_NAME)) {
            // no translation needed
            return name;
        } else if (name.equals("ISO-8859-1")) {
            // no translation needed
            return name;
        }
        return null;
    }

    /** If a node is "AS", returns the underlying expression; otherwise returns
     * the node. */
    public static SqlNode stripAs(SqlNode node) {
        switch (node.getKind()) {
        case AS:
            return ((SqlCall) node).operand(0);
        default:
            return node;
        }
    }

    /** Returns a list of ancestors of {@code predicate} within a given
     * {@code SqlNode} tree.
     *
     * <p>The first element of the list is {@code root}, and the last is
     * the node that matched {@code predicate}. Throws if no node matches.
     */
    public static ImmutableList<SqlNode> getAncestry(SqlNode root, Predicate<SqlNode> predicate,
            Predicate<SqlNode> postPredicate) {
        try {
            new Genealogist(predicate, postPredicate).visitChild(root);
            throw new AssertionError("not found: " + predicate + " in " + root);
        } catch (Util.FoundOne e) {
            //noinspection unchecked
            return (ImmutableList<SqlNode>) e.getNode();
        }
    }

    //~ Inner Classes ----------------------------------------------------------

    /**
     * Handles particular {@link DatabaseMetaData} methods; invocations of other
     * methods will fall through to the base class,
     * {@link org.apache.calcite.util.BarfingInvocationHandler}, which will throw
     * an error.
     */
    public static class DatabaseMetaDataInvocationHandler extends BarfingInvocationHandler {
        private final String databaseProductName;
        private final String identifierQuoteString;

        public DatabaseMetaDataInvocationHandler(String databaseProductName, String identifierQuoteString) {
            this.databaseProductName = databaseProductName;
            this.identifierQuoteString = identifierQuoteString;
        }

        public String getDatabaseProductName() throws SQLException {
            return databaseProductName;
        }

        public String getIdentifierQuoteString() throws SQLException {
            return identifierQuoteString;
        }
    }

    /** Walks over a {@link org.apache.calcite.sql.SqlNode} tree and returns the
     * ancestry stack when it finds a given node. */
    private static class Genealogist extends SqlBasicVisitor<Void> {
        private final List<SqlNode> ancestors = Lists.newArrayList();
        private final Predicate<SqlNode> predicate;
        private final Predicate<SqlNode> postPredicate;

        Genealogist(Predicate<SqlNode> predicate, Predicate<SqlNode> postPredicate) {
            this.predicate = predicate;
            this.postPredicate = postPredicate;
        }

        private Void check(SqlNode node) {
            preCheck(node);
            postCheck(node);
            return null;
        }

        private Void preCheck(SqlNode node) {
            if (predicate.apply(node)) {
                throw new Util.FoundOne(ImmutableList.copyOf(ancestors));
            }
            return null;
        }

        private Void postCheck(SqlNode node) {
            if (postPredicate.apply(node)) {
                throw new Util.FoundOne(ImmutableList.copyOf(ancestors));
            }
            return null;
        }

        private void visitChild(SqlNode node) {
            if (node == null) {
                return;
            }
            ancestors.add(node);
            node.accept(this);
            ancestors.remove(ancestors.size() - 1);
        }

        @Override
        public Void visit(SqlIdentifier id) {
            return check(id);
        }

        @Override
        public Void visit(SqlCall call) {
            preCheck(call);
            for (SqlNode node : call.getOperandList()) {
                visitChild(node);
            }
            return postCheck(call);
        }

        @Override
        public Void visit(SqlIntervalQualifier intervalQualifier) {
            return check(intervalQualifier);
        }

        @Override
        public Void visit(SqlLiteral literal) {
            return check(literal);
        }

        @Override
        public Void visit(SqlNodeList nodeList) {
            preCheck(nodeList);
            for (SqlNode node : nodeList) {
                visitChild(node);
            }
            return postCheck(nodeList);
        }

        @Override
        public Void visit(SqlDynamicParam param) {
            return check(param);
        }

        @Override
        public Void visit(SqlDataTypeSpec type) {
            return check(type);
        }
    }
}

// End SqlUtil.java