All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.phoenix.parse.ParseNodeFactory Maven / Gradle / Ivy

The newest version!
/*
 * 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.phoenix.parse;

import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

import com.google.common.collect.ArrayListMultimap;
import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.phoenix.expression.Expression;
import org.apache.phoenix.expression.ExpressionType;
import org.apache.phoenix.expression.function.AvgAggregateFunction;
import org.apache.phoenix.expression.function.CountAggregateFunction;
import org.apache.phoenix.expression.function.CurrentDateFunction;
import org.apache.phoenix.expression.function.CurrentTimeFunction;
import org.apache.phoenix.expression.function.DistinctCountAggregateFunction;
import org.apache.phoenix.expression.function.FunctionExpression;
import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunction;
import org.apache.phoenix.parse.FunctionParseNode.BuiltInFunctionInfo;
import org.apache.phoenix.parse.JoinTableNode.JoinType;
import org.apache.phoenix.parse.LikeParseNode.LikeType;
import org.apache.phoenix.schema.PIndexState;
import org.apache.phoenix.schema.PTable.IndexType;
import org.apache.phoenix.schema.PTableType;
import org.apache.phoenix.schema.SortOrder;
import org.apache.phoenix.schema.TypeMismatchException;
import org.apache.phoenix.schema.stats.StatisticsCollectionScope;
import org.apache.phoenix.schema.types.PDataType;
import org.apache.phoenix.schema.types.PLong;
import org.apache.phoenix.schema.types.PTimestamp;
import org.apache.phoenix.util.SchemaUtil;

import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;

/**
 * 
 * Factory used by parser to construct object model while parsing a SQL statement
 * 
 * 
 * @since 0.1
 */
public class ParseNodeFactory {
	private static final String ARRAY_ELEM = "ARRAY_ELEM";
	// TODO: Use Google's Reflection library instead to find aggregate functions
    @SuppressWarnings("unchecked")
    private static final List> CLIENT_SIDE_BUILT_IN_FUNCTIONS = Arrays.>asList(
        CurrentDateFunction.class,
        CurrentTimeFunction.class,
        AvgAggregateFunction.class
        );
    private static final Map BUILT_IN_FUNCTION_MAP = Maps.newHashMap();
    private static final Multimap BUILT_IN_FUNCTION_MULTIMAP = ArrayListMultimap.create();
    private static final BigDecimal MAX_LONG = BigDecimal.valueOf(Long.MAX_VALUE);


    /**
     *
     * Key used to look up a built-in function using the combination of
     * the lowercase name and the number of arguments. This disambiguates
     * the aggregate MAX() from the non aggregate MAX(,).
     *
     * 
     * @since 0.1
     */
    public static class BuiltInFunctionKey {
        private final String upperName;
        private final int argCount;

        public BuiltInFunctionKey(String lowerName, int argCount) {
            this.upperName = lowerName;
            this.argCount = argCount;
        }

        @Override
        public String toString() {
            return upperName;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + argCount;
            result = prime * result + ((upperName == null) ? 0 : upperName.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj == null) return false;
            if (getClass() != obj.getClass()) return false;
            BuiltInFunctionKey other = (BuiltInFunctionKey)obj;
            if (argCount != other.argCount) return false;
            if (!upperName.equals(other.upperName)) return false;
            return true;
        }
    }

    private static void addBuiltInFunction(Class f) throws Exception {
        BuiltInFunction d = f.getAnnotation(BuiltInFunction.class);
        if (d == null) {
            return;
        }
        int nArgs = d.args().length;
        BuiltInFunctionInfo value = new BuiltInFunctionInfo(f, d);
        if (d.classType() != FunctionParseNode.FunctionClassType.ABSTRACT) {
            BUILT_IN_FUNCTION_MULTIMAP.put(value.getName(), value);
        }
        if (d.classType() != FunctionParseNode.FunctionClassType.DERIVED) {
            do {
                // Add function to function map, throwing if conflicts found
                // Add entry for each possible version of function based on arguments that are not required to be present (i.e. arg with default value)
                BuiltInFunctionKey key = new BuiltInFunctionKey(value.getName(), nArgs);
                if (BUILT_IN_FUNCTION_MAP.put(key, value) != null) {
                    throw new IllegalStateException("Multiple " + value.getName() + " functions with " + nArgs + " arguments");
                }
            } while (--nArgs >= 0 && d.args()[nArgs].defaultValue().length() > 0);

            // Look for default values that aren't at the end and throw
            while (--nArgs >= 0) {
                if (d.args()[nArgs].defaultValue().length() > 0) {
                    throw new IllegalStateException("Function " + value.getName() + " has non trailing default value of '" + d.args()[nArgs].defaultValue() + "'. Only trailing arguments may have default values");
                }
            }
        }
    }
    /**
     * Reflect this class and populate static structures from it.
     * Don't initialize in static block because we have a circular dependency
     */
    private synchronized static void initBuiltInFunctionMap() {
        if (!BUILT_IN_FUNCTION_MAP.isEmpty()) {
            return;
        }
        Class f = null;
        try {
            // Reflection based parsing which yields direct explicit function evaluation at runtime
            for (int i = 0; i < CLIENT_SIDE_BUILT_IN_FUNCTIONS.size(); i++) {
                f = CLIENT_SIDE_BUILT_IN_FUNCTIONS.get(i);
                addBuiltInFunction(f);
            }
            for (ExpressionType et : ExpressionType.values()) {
                Class ec = et.getExpressionClass();
                if (FunctionExpression.class.isAssignableFrom(ec)) {
                    @SuppressWarnings("unchecked")
                    Class c = (Class)ec;
                    addBuiltInFunction(f = c);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("Failed initialization of built-in functions at class '" + f + "'", e);
        }
    }

    private static BuiltInFunctionInfo getInfo(String name, List children) {
        return get(SchemaUtil.normalizeIdentifier(name), children);
    }

    public static BuiltInFunctionInfo get(String normalizedName, List children) {
        initBuiltInFunctionMap();
        BuiltInFunctionInfo info = BUILT_IN_FUNCTION_MAP.get(new BuiltInFunctionKey(normalizedName,children.size()));
        return info;
    }

    public static Multimap getBuiltInFunctionMultimap(){
        initBuiltInFunctionMap();
        return BUILT_IN_FUNCTION_MULTIMAP;
    }

    public ParseNodeFactory() {
    }
    
    private static AtomicInteger tempAliasCounter = new AtomicInteger(0);
    
    public static String createTempAlias() {
        return "$" + tempAliasCounter.incrementAndGet();
    }

    public ExplainStatement explain(BindableStatement statement) {
        return new ExplainStatement(statement);
    }

    public AliasedNode aliasedNode(String alias, ParseNode expression) {
    	return new AliasedNode(alias, expression);
    }

    public AddParseNode add(List children) {
        return new AddParseNode(children);
    }

    public SubtractParseNode subtract(List children) {
        return new SubtractParseNode(children);
    }

    public MultiplyParseNode multiply(List children) {
        return new MultiplyParseNode(children);
    }

    public ModulusParseNode modulus(List children) {
        return new ModulusParseNode(children);
    }

    public AndParseNode and(List children) {
        return new AndParseNode(children);
    }

    public FamilyWildcardParseNode family(String familyName){
    	    return new FamilyWildcardParseNode(familyName, false);
    }

    public TableWildcardParseNode tableWildcard(TableName tableName) {
        return new TableWildcardParseNode(tableName, false);
    }

    public WildcardParseNode wildcard() {
        return WildcardParseNode.INSTANCE;
    }

    public BetweenParseNode between(ParseNode l, ParseNode r1, ParseNode r2, boolean negate) {
        return new BetweenParseNode(l, r1, r2, negate);
    }

    public BindParseNode bind(String bind) {
        return new BindParseNode(bind);
    }

    public StringConcatParseNode concat(List children) {
        return new StringConcatParseNode(children);
    }

    public ColumnParseNode column(TableName tableName, String columnName, String alias) {
        return new ColumnParseNode(tableName, columnName, alias);
    }
    
    public ColumnName columnName(String columnName) {
        return new ColumnName(columnName);
    }

    public ColumnName columnName(String familyName, String columnName) {
        return new ColumnName(familyName, columnName);
    }

    public PropertyName propertyName(String propertyName) {
        return new PropertyName(propertyName);
    }

    public PropertyName propertyName(String familyName, String propertyName) {
        return new PropertyName(familyName, propertyName);
    }

    public ColumnDef columnDef(ColumnName columnDefName, String sqlTypeName, boolean isNull, Integer maxLength, Integer scale, boolean isPK, SortOrder sortOrder, String expressionStr, boolean isRowTimestamp) {
        return new ColumnDef(columnDefName, sqlTypeName, isNull, maxLength, scale, isPK, sortOrder, expressionStr, isRowTimestamp);
    }

    public ColumnDef columnDef(ColumnName columnDefName, String sqlTypeName,
            boolean isArray, Integer arrSize, Boolean isNull,
            Integer maxLength, Integer scale, boolean isPK,
        	SortOrder sortOrder, String expressionStr, boolean isRowTimestamp) {
        return new ColumnDef(columnDefName, sqlTypeName,
                isArray, arrSize, isNull,
                maxLength, scale, isPK,
                sortOrder, expressionStr, isRowTimestamp);
    }

    public ColumnDef columnDef(ColumnName columnDefName, String sqlTypeName, boolean isArray, Integer arrSize, Boolean isNull, Integer maxLength, Integer scale, boolean isPK,
            SortOrder sortOrder, boolean isRowTimestamp) {
        return new ColumnDef(columnDefName, sqlTypeName, isArray, arrSize, isNull, maxLength, scale, isPK, sortOrder, null, isRowTimestamp);
    }
    
    public ColumnDefInPkConstraint columnDefInPkConstraint(ColumnName columnDefName, SortOrder sortOrder, boolean isRowTimestamp) {
        return new ColumnDefInPkConstraint(columnDefName, sortOrder, isRowTimestamp);
    }
    
    public PrimaryKeyConstraint primaryKey(String name, List columnDefs) {
        return new PrimaryKeyConstraint(name, columnDefs);
    }
    
    public IndexKeyConstraint indexKey( List> parseNodeAndSortOrder) {
        return new IndexKeyConstraint(parseNodeAndSortOrder);
    }

    public CreateTableStatement createTable(TableName tableName, ListMultimap> props, List columns, PrimaryKeyConstraint pkConstraint, List splits, PTableType tableType, boolean ifNotExists, TableName baseTableName, ParseNode tableTypeIdNode, int bindCount, Boolean immutableRows) {
        return new CreateTableStatement(tableName, props, columns, pkConstraint, splits, tableType, ifNotExists, baseTableName, tableTypeIdNode, bindCount, immutableRows);
    }

    public CreateSchemaStatement createSchema(String schemaName, boolean ifNotExists) {
        return new CreateSchemaStatement(schemaName, ifNotExists);
    }

    public CreateIndexStatement createIndex(NamedNode indexName, NamedTableNode dataTable, IndexKeyConstraint ikConstraint, List includeColumns, List splits, ListMultimap> props, boolean ifNotExists, IndexType indexType,boolean async, int bindCount, Map udfParseNodes) {
        return new CreateIndexStatement(indexName, dataTable, ikConstraint, includeColumns, splits, props, ifNotExists, indexType, async, bindCount, udfParseNodes);
    }

    public CreateSequenceStatement createSequence(TableName tableName, ParseNode startsWith,
            ParseNode incrementBy, ParseNode cacheSize, ParseNode minValue, ParseNode maxValue,
            boolean cycle, boolean ifNotExits, int bindCount) {
        return new CreateSequenceStatement(tableName, startsWith, incrementBy, cacheSize, minValue,
                maxValue, cycle, ifNotExits, bindCount);
    }

    public CreateFunctionStatement createFunction(PFunction functionInfo, boolean temporary, boolean isReplace) {
        return new CreateFunctionStatement(functionInfo, temporary, isReplace);
    }

    public AddJarsStatement addJars(List jarPaths) {
        return new AddJarsStatement(jarPaths);
    }

    public ListJarsStatement listJars() {
        return new ListJarsStatement();
    }

    public DeleteJarStatement deleteJar(LiteralParseNode jarPath) {
        return new DeleteJarStatement(jarPath);
    }

    public DropFunctionStatement dropFunction(String functionName, boolean ifExists) {
        return new DropFunctionStatement(functionName, ifExists);
    }

    public DropSequenceStatement dropSequence(TableName tableName, boolean ifExits, int bindCount){
        return new DropSequenceStatement(tableName, ifExits, bindCount);
    }

    public SequenceValueParseNode currentValueFor(TableName tableName) {
        return new SequenceValueParseNode(tableName, SequenceValueParseNode.Op.CURRENT_VALUE, null);
    }

    public SequenceValueParseNode nextValueFor(TableName tableName, ParseNode numToAllocateNode) {
        return new SequenceValueParseNode(tableName, SequenceValueParseNode.Op.NEXT_VALUE, numToAllocateNode);
    }

    public AddColumnStatement addColumn(NamedTableNode table,  PTableType tableType, List columnDefs, boolean ifNotExists, ListMultimap> props) {
        return new AddColumnStatement(table, tableType, columnDefs, ifNotExists, props);
    }

    public DropColumnStatement dropColumn(NamedTableNode table,  PTableType tableType, List columnNodes, boolean ifExists) {
        return new DropColumnStatement(table, tableType, columnNodes, ifExists);
    }

    public DropTableStatement dropTable(TableName tableName, PTableType tableType, boolean ifExists, boolean cascade) {
        return new DropTableStatement(tableName, tableType, ifExists, cascade, false);
    }

    public DropIndexStatement dropIndex(NamedNode indexName, TableName tableName, boolean ifExists) {
        return new DropIndexStatement(indexName, tableName, ifExists);
    }

    public AlterIndexStatement alterIndex(NamedTableNode indexTableNode, String dataTableName, boolean ifExists, PIndexState state, boolean isRebuildAll, boolean async, ListMultimap> props) {
        return new AlterIndexStatement(indexTableNode, dataTableName, ifExists, state, isRebuildAll, async, props);
    }

    public AlterIndexStatement alterIndex(NamedTableNode indexTableNode, String dataTableName, boolean ifExists, PIndexState state) {
        return new AlterIndexStatement(indexTableNode, dataTableName, ifExists, state, false, false);
    }

    public TraceStatement trace(boolean isTraceOn, double samplingRate) {
        return new TraceStatement(isTraceOn, samplingRate);
    }

    public AlterSessionStatement alterSession(Map props) {
        return new AlterSessionStatement(props);
    }

    public TableName table(String schemaName, String tableName) {
        return TableName.createNormalized(schemaName,tableName);
    }

    public NamedNode indexName(String name) {
        return new NamedNode(name);
    }

    @Deprecated
    public NamedTableNode namedTable(String alias, TableName name) {
        return new NamedTableNode(alias, name);
    }
    
    @Deprecated
    public NamedTableNode namedTable(String alias, TableName name, List dyn_columns) {
        return new NamedTableNode(alias, name,dyn_columns);
    }
    
    public NamedTableNode namedTable(String alias, TableName name, Double tableSamplingRate) {
        return new NamedTableNode(alias, name, tableSamplingRate);
    }
    
    public NamedTableNode namedTable(String alias, TableName name, List dyn_columns, Double tableSamplingRate) {
    	return new NamedTableNode(alias, name,dyn_columns, tableSamplingRate);
    }
    
    public NamedTableNode namedTable(String alias, TableName name, List dyn_columns, LiteralParseNode tableSampleNode) {
    	Double tableSamplingRate;
    	if(tableSampleNode==null||tableSampleNode.getValue()==null){
    		tableSamplingRate=ConcreteTableNode.DEFAULT_TABLE_SAMPLING_RATE;
    	}else if(tableSampleNode.getValue() instanceof Integer){
    		tableSamplingRate=(double)((int)tableSampleNode.getValue());
    	}else{
    		tableSamplingRate=((BigDecimal) tableSampleNode.getValue()).doubleValue();
    	}
    	return new NamedTableNode(alias, name, dyn_columns, tableSamplingRate);
    }

    public BindTableNode bindTable(String alias, TableName name) {
        return new BindTableNode(alias, name);
    }

    public CaseParseNode caseWhen(List children) {
        return new CaseParseNode(children);
    }

    public DivideParseNode divide(List children) {
        return new DivideParseNode(children);
    }

    public UpdateStatisticsStatement updateStatistics(NamedTableNode table, StatisticsCollectionScope scope, Map props) {
      return new UpdateStatisticsStatement(table, scope, props);
    }
    
    public ExecuteUpgradeStatement executeUpgrade() {
        return new ExecuteUpgradeStatement();
    }


    public FunctionParseNode functionDistinct(String name, List args) {
        if (CountAggregateFunction.NAME.equals(SchemaUtil.normalizeIdentifier(name))) {
            BuiltInFunctionInfo info = getInfo(
                    SchemaUtil.normalizeIdentifier(DistinctCountAggregateFunction.NAME), args);
            return new DistinctCountParseNode(DistinctCountAggregateFunction.NAME, args, info);
        } else {
            throw new UnsupportedOperationException("DISTINCT not supported with " + name);
        }
    }

    public FunctionParseNode arrayElemRef(List args) {
    	return function(ARRAY_ELEM, args);
    }

    public FunctionParseNode function(String name, List args) {
        BuiltInFunctionInfo info = getInfo(name, args);
        if (info == null) {
            return new UDFParseNode(name, args, info);
        }
        Constructor ctor = info.getNodeCtor();
        if (ctor == null) {
            return info.isAggregate()
            ? new AggregateFunctionParseNode(name, args, info)
            : new FunctionParseNode(name, args, info);
        } else {
            try {
                return ctor.newInstance(name, args, info);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public FunctionParseNode function(String name, List valueNodes,
            List columnNodes, boolean isAscending) {

        List args = Lists.newArrayListWithExpectedSize(columnNodes.size() + valueNodes.size() + 1);
        args.addAll(columnNodes);
        args.add(new LiteralParseNode(Boolean.valueOf(isAscending)));
        args.addAll(valueNodes);

        BuiltInFunctionInfo info = getInfo(name, args);
        if(info==null) {
            return new UDFParseNode(name,args,info);
        }
        Constructor ctor = info.getNodeCtor();
        if (ctor == null) {
            return new AggregateFunctionWithinGroupParseNode(name, args, info);
        } else {
            try {
                return ctor.newInstance(name, args, info);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public HintNode hint(String hint) {
        return new HintNode(hint);
    }

    public InListParseNode inList(List children, boolean negate) {
        return new InListParseNode(children, negate);
    }

    public ExistsParseNode exists(ParseNode child, boolean negate) {
        return new ExistsParseNode(child, negate);
    }

    public InParseNode in(ParseNode l, ParseNode r, boolean negate, boolean isSubqueryDistinct) {
        return new InParseNode(l, r, negate, isSubqueryDistinct);
    }

    public IsNullParseNode isNull(ParseNode child, boolean negate) {
        return new IsNullParseNode(child, negate);
    }

    public JoinTableNode join(JoinType type, TableNode lhs, TableNode rhs, ParseNode on, boolean singleValueOnly) {
        return new JoinTableNode(type, lhs, rhs, on, singleValueOnly);
    }

    public DerivedTableNode derivedTable (String alias, SelectStatement select) {
        return new DerivedTableNode(alias, select);
    }

    public LikeParseNode like(ParseNode lhs, ParseNode rhs, boolean negate, LikeType likeType) {
        return new LikeParseNode(lhs, rhs, negate, likeType);
    }

    public LiteralParseNode literal(Object value) {
        return new LiteralParseNode(value);
    }

    public LiteralParseNode realNumber(String text) {
        return new LiteralParseNode(new BigDecimal(text, PDataType.DEFAULT_MATH_CONTEXT));
    }
    
    public LiteralParseNode wholeNumber(String text) {
        int length = text.length();
        // We know it'll fit into long, might still fit into int
        if (length <= PDataType.LONG_PRECISION-1) {
            long l = Long.parseLong(text);
            if (l <= Integer.MAX_VALUE) {
                // Fits into int
                return new LiteralParseNode((int)l);
            }
            return new LiteralParseNode(l);
        }
        // Might still fit into long
        BigDecimal d = new BigDecimal(text, PDataType.DEFAULT_MATH_CONTEXT);
        if (d.compareTo(MAX_LONG) <= 0) {
            return new LiteralParseNode(d.longValueExact());
        }
        // Doesn't fit into long
        return new LiteralParseNode(d);
    }

    public LiteralParseNode intOrLong(String text) {
        long l = Long.parseLong(text);
        if (l <= Integer.MAX_VALUE) {
            // Fits into int
            return new LiteralParseNode((int)l);
        }
        return new LiteralParseNode(l);
    }

    public CastParseNode cast(ParseNode expression, String dataType, Integer maxLength, Integer scale) {
        return new CastParseNode(expression, dataType, maxLength, scale, false);
    }

    public CastParseNode cast(ParseNode expression, PDataType dataType, Integer maxLength, Integer scale) {
        return new CastParseNode(expression, dataType, maxLength, scale, false);
    }

    public CastParseNode cast(ParseNode expression, PDataType dataType, Integer maxLength, Integer scale, boolean arr) {
        return new CastParseNode(expression, dataType, maxLength, scale, arr);
    }

    public CastParseNode cast(ParseNode expression, String dataType, Integer maxLength, Integer scale, boolean arr) {
        return new CastParseNode(expression, dataType, maxLength, scale, arr);
    }

    public ParseNode rowValueConstructor(List l) {
        return new RowValueConstructorParseNode(l);
    }

    private void checkTypeMatch (PDataType expectedType, PDataType actualType) throws SQLException {
        if (!expectedType.isCoercibleTo(actualType)) {
            throw TypeMismatchException.newException(expectedType, actualType);
        }
    }

    public LiteralParseNode literal(Object value, PDataType expectedType) throws SQLException {
        PDataType actualType = PDataType.fromLiteral(value);
        if (actualType != null && actualType != expectedType) {
            checkTypeMatch(expectedType, actualType);
            value = expectedType.toObject(value, actualType);
        }
        return new LiteralParseNode(value);
        /*
        Object typedValue = expectedType.toObject(value.toString());
        return new LiteralParseNode(typedValue);
        */
    }

    public LiteralParseNode literal(String value, String sqlTypeName) throws SQLException {
        PDataType expectedType = sqlTypeName == null ? null : PDataType.fromSqlTypeName(SchemaUtil.normalizeIdentifier(sqlTypeName));
        if (expectedType == null || !expectedType.isCoercibleTo(PTimestamp.INSTANCE)) {
            throw TypeMismatchException.newException(expectedType, PTimestamp.INSTANCE);
        }
        Object typedValue = expectedType.toObject(value);
        return new LiteralParseNode(typedValue);
    }

    public LiteralParseNode coerce(LiteralParseNode literalNode, PDataType expectedType) throws SQLException {
        PDataType actualType = literalNode.getType();
        if (actualType != null) {
            Object before = literalNode.getValue();
            checkTypeMatch(expectedType, actualType);
            Object after = expectedType.toObject(before, actualType);
            if (before != after) {
                literalNode = literal(after);
            }
        }
        return literalNode;
    }

    public ComparisonParseNode comparison(CompareOp op, ParseNode lhs, ParseNode rhs) {
        switch (op){
        case LESS:
            return lt(lhs,rhs);
        case LESS_OR_EQUAL:
            return lte(lhs,rhs);
        case EQUAL:
            return equal(lhs,rhs);
        case NOT_EQUAL:
            return notEqual(lhs,rhs);
        case GREATER_OR_EQUAL:
            return gte(lhs,rhs);
        case GREATER:
            return gt(lhs,rhs);
        default:
            throw new IllegalArgumentException("Unexpcted CompareOp of " + op);
        }
    }
    
    public ArrayAnyComparisonNode arrayAny(ParseNode rhs, ComparisonParseNode compareNode) {
        return new ArrayAnyComparisonNode(rhs, compareNode);
    }
    
    public ArrayAllComparisonNode arrayAll(ParseNode rhs, ComparisonParseNode compareNode) {
        return new ArrayAllComparisonNode(rhs, compareNode);
    }

    public ArrayAnyComparisonNode wrapInAny(CompareOp op, ParseNode lhs, ParseNode rhs) {
        return new ArrayAnyComparisonNode(rhs, comparison(op, lhs, elementRef(Arrays.asList(rhs, literal(1)))));
    }

    public ArrayAllComparisonNode wrapInAll(CompareOp op, ParseNode lhs, ParseNode rhs) {
        return new ArrayAllComparisonNode(rhs, comparison(op, lhs, elementRef(Arrays.asList(rhs, literal(1)))));
    }

    public ArrayElemRefNode elementRef(List parseNode) {
        return new ArrayElemRefNode(parseNode);
    }

    public GreaterThanParseNode gt(ParseNode lhs, ParseNode rhs) {
        return new GreaterThanParseNode(lhs, rhs);
    }


    public GreaterThanOrEqualParseNode gte(ParseNode lhs, ParseNode rhs) {
        return new GreaterThanOrEqualParseNode(lhs, rhs);
    }

    public LessThanParseNode lt(ParseNode lhs, ParseNode rhs) {
        return new LessThanParseNode(lhs, rhs);
    }


    public LessThanOrEqualParseNode lte(ParseNode lhs, ParseNode rhs) {
        return new LessThanOrEqualParseNode(lhs, rhs);
    }

    public EqualParseNode equal(ParseNode lhs, ParseNode rhs) {
        return new EqualParseNode(lhs, rhs);
    }

    public ArrayConstructorNode upsertStmtArrayNode(List upsertStmtArray) {
    	return new ArrayConstructorNode(upsertStmtArray);
    }

    public ParseNode negate(ParseNode child) {
        // Prevents reparsing of -1 from becoming 1*-1 and 1*1*-1 with each re-parsing
        if (LiteralParseNode.ONE.equals(child) && ((LiteralParseNode)child).getType().isCoercibleTo(
                PLong.INSTANCE)) {
            return LiteralParseNode.MINUS_ONE;
        }
        // Special case to convert Long.MIN_VALUE back to a Long. We can't initially represent it
        // as a Long in the parser because we only represent positive values as constants in the
        // parser, and ABS(Long.MIN_VALUE) is too big to fit into a Long. So we convert it back here.
        if (LiteralParseNode.MIN_LONG_AS_BIG_DECIMAL.equals(child)) {
            return LiteralParseNode.MIN_LONG;
        }
        return new MultiplyParseNode(Arrays.asList(child,LiteralParseNode.MINUS_ONE));
    }

    public NotEqualParseNode notEqual(ParseNode lhs, ParseNode rhs) {
        return new NotEqualParseNode(lhs, rhs);
    }

    public ParseNode not(ParseNode child) {
        if (child instanceof ExistsParseNode) {
            return exists(child.getChildren().get(0), !((ExistsParseNode) child).isNegate());
        }
        
        return new NotParseNode(child);
    }


    public OrParseNode or(List children) {
        return new OrParseNode(children);
    }


    public OrderByNode orderBy(ParseNode expression, boolean nullsLast, boolean orderAscending) {
        return new OrderByNode(expression, nullsLast, orderAscending);
    }

    public SelectStatement select(TableNode from, HintNode hint, boolean isDistinct, List select, ParseNode where,
            List groupBy, ParseNode having, List orderBy, LimitNode limit, OffsetNode offset, int bindCount, boolean isAggregate, 
            boolean hasSequence, List selects, Map udfParseNodes) {

        return new SelectStatement(from, hint, isDistinct, select, where, groupBy == null ? Collections.emptyList() : groupBy, having,
                orderBy == null ? Collections.emptyList() : orderBy, limit, offset, bindCount, isAggregate, hasSequence, selects == null ? Collections.emptyList() : selects, udfParseNodes);
    } 
    
    public UpsertStatement upsert(NamedTableNode table, HintNode hint, List columns, List values,
            SelectStatement select, int bindCount, 
            Map udfParseNodes,
            List> onDupKeyPairs) {
        return new UpsertStatement(table, hint, columns, values, select, bindCount, udfParseNodes, onDupKeyPairs);
    }

    public CursorName cursorName(String name){
        return new CursorName(name);
    }

    public DeclareCursorStatement declareCursor(CursorName cursor, SelectStatement select){
        return new DeclareCursorStatement(cursor, select);
    }

    public FetchStatement fetch(CursorName cursor, boolean isNext, int fetchLimit){
        return new FetchStatement(cursor, isNext, fetchLimit);
    }

    public OpenStatement open(CursorName cursor){
        return new OpenStatement(cursor);
    }

    public CloseStatement close(CursorName cursor){
        return new CloseStatement(cursor);
    }

    public DeleteStatement delete(NamedTableNode table, HintNode hint, ParseNode node, List orderBy, LimitNode limit, int bindCount, Map udfParseNodes) {
        return new DeleteStatement(table, hint, node, orderBy, limit, bindCount, udfParseNodes);
    }

    public SelectStatement select(SelectStatement statement, ParseNode where) {
        return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(), where, statement.getGroupBy(), statement.getHaving(),
                statement.getOrderBy(), statement.getLimit(), statement.getOffset(), statement.getBindCount(), statement.isAggregate(), statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());
    }

    public SelectStatement select(SelectStatement statement, ParseNode where, ParseNode having) {
        return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(), where, statement.getGroupBy(), having,
                statement.getOrderBy(), statement.getLimit(), statement.getOffset(), statement.getBindCount(), statement.isAggregate(), statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());
    }
    
    public SelectStatement select(SelectStatement statement, List select, ParseNode where, List groupBy, ParseNode having, List orderBy) {
        return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), 
                select, where, groupBy, having, orderBy, statement.getLimit(), statement.getOffset(), statement.getBindCount(), statement.isAggregate(), statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());
    }
    
    public SelectStatement select(SelectStatement statement, TableNode table) {
        return select(table, statement.getHint(), statement.isDistinct(), statement.getSelect(), statement.getWhere(), statement.getGroupBy(),
                statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getOffset(), statement.getBindCount(), statement.isAggregate(),
                statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());
    }

    public SelectStatement select(SelectStatement statement, TableNode table, ParseNode where) {
        return select(table, statement.getHint(), statement.isDistinct(), statement.getSelect(), where, statement.getGroupBy(),
                statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getOffset(), statement.getBindCount(), statement.isAggregate(),
                statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());
    }

    public SelectStatement select(SelectStatement statement, boolean isDistinct, List select) {
        return select(statement.getFrom(), statement.getHint(), isDistinct, select, statement.getWhere(), statement.getGroupBy(),
                statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getOffset(), statement.getBindCount(), statement.isAggregate(),
                statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());
    }

    public SelectStatement select(SelectStatement statement, boolean isDistinct, List select, ParseNode where) {
        return select(statement.getFrom(), statement.getHint(), isDistinct, select, where, statement.getGroupBy(),
                statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getOffset(), statement.getBindCount(), statement.isAggregate(),
                statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());
    }

    public SelectStatement select(SelectStatement statement, boolean isDistinct, List select, ParseNode where, List groupBy, boolean isAggregate) {
        return select(statement.getFrom(), statement.getHint(), isDistinct, select, where, groupBy,
                statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getOffset(), statement.getBindCount(), isAggregate,
                statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());
    }

    public SelectStatement select(SelectStatement statement, List orderBy) {
        return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(),
                statement.getWhere(), statement.getGroupBy(), statement.getHaving(), orderBy, statement.getLimit(),
                statement.getOffset(), statement.getBindCount(), statement.isAggregate(), statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());
    }

    public SelectStatement select(SelectStatement statement, HintNode hint) {
        return hint == null || hint.isEmpty() ? statement : select(statement.getFrom(), hint, statement.isDistinct(), statement.getSelect(),
                statement.getWhere(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getOffset(), 
                statement.getBindCount(), statement.isAggregate(), statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());
    }

    public SelectStatement select(SelectStatement statement, HintNode hint, ParseNode where) {
        return select(statement.getFrom(), hint, statement.isDistinct(), statement.getSelect(), where, statement.getGroupBy(),
                statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getOffset(), statement.getBindCount(), statement.isAggregate(),
                statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());
    }

    public SelectStatement select(SelectStatement statement, List orderBy, LimitNode limit, OffsetNode offset, int bindCount, boolean isAggregate) {
        return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(),
            statement.getWhere(), statement.getGroupBy(), statement.getHaving(), orderBy, limit, offset,
            bindCount, isAggregate || statement.isAggregate(), statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());

    }

    public SelectStatement select(SelectStatement statement, LimitNode limit) {
        return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(),
                statement.getWhere(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), limit,
                statement.getOffset(), statement.getBindCount(), statement.isAggregate(), statement.hasSequence(),
                statement.getSelects(), statement.getUdfParseNodes());
    }

    public SelectStatement select(SelectStatement statement, List orderBy, LimitNode limit, OffsetNode offset) {
        return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(),
            statement.getWhere(), statement.getGroupBy(), statement.getHaving(), orderBy, limit,offset,
            statement.getBindCount(), statement.isAggregate(), statement.hasSequence(), statement.getSelects(), statement.getUdfParseNodes());
    }

    public SelectStatement select(List statements, List orderBy, LimitNode limit,
            OffsetNode offset, int bindCount, boolean isAggregate) {
        if (statements.size() == 1) return select(statements.get(0), orderBy, limit, offset, bindCount, isAggregate);        

        // Get a list of adjusted aliases from a non-wildcard sub-select if any. 
        // We do not check the number of select nodes among all sub-selects, as 
        // it will be done later at compile stage. Empty or different aliases 
        // are ignored, since they cannot be referred by outer queries.
        List aliases = Lists. newArrayList();
        Map udfParseNodes = new HashMap(1);
        for (int i = 0; i < statements.size() && aliases.isEmpty(); i++) {
            SelectStatement subselect = statements.get(i);
            udfParseNodes.putAll(subselect.getUdfParseNodes());
            if (!subselect.hasWildcard()) {
                for (AliasedNode aliasedNode : subselect.getSelect()) {
                    String alias = aliasedNode.getAlias();
                    if (alias == null) {
                        alias = SchemaUtil.normalizeIdentifier(aliasedNode.getNode().getAlias());
                    }
                    aliases.add(alias == null ? createTempAlias() : alias);
                }
            }
        }

        List aliasedNodes;
        if (aliases.isEmpty()) {
            aliasedNodes = Lists.newArrayList(aliasedNode(null, wildcard()));
        } else {
            aliasedNodes = Lists.newArrayListWithExpectedSize(aliases.size());
            for (String alias : aliases) {
                aliasedNodes.add(aliasedNode(alias, column(null, alias, alias)));
            }
        }
        
        return select(null, HintNode.EMPTY_HINT_NODE, false, aliasedNodes, 
                null, null, null, orderBy, limit,offset, bindCount, false, false, statements, udfParseNodes);
    }

    public SubqueryParseNode subquery(SelectStatement select, boolean expectSingleRow) {
        return new SubqueryParseNode(select, expectSingleRow);
    }

    public LimitNode limit(BindParseNode b) {
        return new LimitNode(b);
    }

    public LimitNode limit(LiteralParseNode l) {
        return new LimitNode(l);
    }

    public OffsetNode offset(BindParseNode b) {
        return new OffsetNode(b);
    }

    public OffsetNode offset(LiteralParseNode l) {
        return new OffsetNode(l);
    }

    public DropSchemaStatement dropSchema(String schemaName, boolean ifExists, boolean cascade) {
        return new DropSchemaStatement(schemaName, ifExists, cascade);
    }

    public UseSchemaStatement useSchema(String schemaName) {
        return new UseSchemaStatement(schemaName);
    }

    public ChangePermsStatement changePermsStatement(String permsString, boolean isSchemaName, TableName tableName
            , String schemaName, boolean isGroupName, LiteralParseNode userOrGroup, boolean isGrantStatement) {
        return new ChangePermsStatement(permsString, isSchemaName, tableName, schemaName, isGroupName, userOrGroup, isGrantStatement);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy