| /* |
| * 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.Collection; |
| 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<Class<? extends FunctionExpression>> CLIENT_SIDE_BUILT_IN_FUNCTIONS = Arrays.<Class<? extends FunctionExpression>>asList( |
| CurrentDateFunction.class, |
| CurrentTimeFunction.class, |
| AvgAggregateFunction.class |
| ); |
| private static final Map<BuiltInFunctionKey, BuiltInFunctionInfo> BUILT_IN_FUNCTION_MAP = Maps.newHashMap(); |
| private static final Multimap<String, BuiltInFunctionInfo> 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(<col>) from the non aggregate MAX(<col1>,<col2>). |
| * |
| * |
| * @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<? extends FunctionExpression> 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<? extends FunctionExpression> 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<? extends Expression> ec = et.getExpressionClass(); |
| if (FunctionExpression.class.isAssignableFrom(ec)) { |
| @SuppressWarnings("unchecked") |
| Class<? extends FunctionExpression> c = (Class<? extends FunctionExpression>)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<ParseNode> children) { |
| return get(SchemaUtil.normalizeIdentifier(name), children); |
| } |
| |
| public static BuiltInFunctionInfo get(String normalizedName, List<ParseNode> children) { |
| initBuiltInFunctionMap(); |
| BuiltInFunctionInfo info = BUILT_IN_FUNCTION_MAP.get(new BuiltInFunctionKey(normalizedName,children.size())); |
| return info; |
| } |
| |
| public static Multimap<String, BuiltInFunctionInfo> 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<ParseNode> children) { |
| return new AddParseNode(children); |
| } |
| |
| public SubtractParseNode subtract(List<ParseNode> children) { |
| return new SubtractParseNode(children); |
| } |
| |
| public MultiplyParseNode multiply(List<ParseNode> children) { |
| return new MultiplyParseNode(children); |
| } |
| |
| public ModulusParseNode modulus(List<ParseNode> children) { |
| return new ModulusParseNode(children); |
| } |
| |
| public AndParseNode and(List<ParseNode> 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<ParseNode> 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<ColumnDefInPkConstraint> columnDefs) { |
| return new PrimaryKeyConstraint(name, columnDefs); |
| } |
| |
| public IndexKeyConstraint indexKey( List<Pair<ParseNode, SortOrder>> parseNodeAndSortOrder) { |
| return new IndexKeyConstraint(parseNodeAndSortOrder); |
| } |
| |
| public CreateTableStatement createTable(TableName tableName, ListMultimap<String,Pair<String,Object>> props, List<ColumnDef> columns, PrimaryKeyConstraint pkConstraint, List<ParseNode> 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<ColumnName> includeColumns, List<ParseNode> splits, ListMultimap<String,Pair<String,Object>> props, boolean ifNotExists, IndexType indexType,boolean async, int bindCount, Map<String, UDFParseNode> 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<LiteralParseNode> 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<ColumnDef> columnDefs, boolean ifNotExists, ListMultimap<String,Pair<String,Object>> props) { |
| return new AddColumnStatement(table, tableType, columnDefs, ifNotExists, props); |
| } |
| |
| public DropColumnStatement dropColumn(NamedTableNode table, PTableType tableType, List<ColumnName> 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); |
| } |
| |
| 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 async) { |
| return new AlterIndexStatement(indexTableNode, dataTableName, ifExists, state, async); |
| } |
| |
| public AlterIndexStatement alterIndex(NamedTableNode indexTableNode, String dataTableName, boolean ifExists, PIndexState state) { |
| return new AlterIndexStatement(indexTableNode, dataTableName, ifExists, state, false); |
| } |
| |
| public TraceStatement trace(boolean isTraceOn, double samplingRate) { |
| return new TraceStatement(isTraceOn, samplingRate); |
| } |
| |
| public AlterSessionStatement alterSession(Map<String,Object> 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); |
| } |
| |
| public NamedTableNode namedTable(String alias, TableName name) { |
| return new NamedTableNode(alias, name); |
| } |
| |
| public NamedTableNode namedTable(String alias, TableName name ,List<ColumnDef> dyn_columns) { |
| return new NamedTableNode(alias, name,dyn_columns); |
| } |
| |
| public BindTableNode bindTable(String alias, TableName name) { |
| return new BindTableNode(alias, name); |
| } |
| |
| public CaseParseNode caseWhen(List<ParseNode> children) { |
| return new CaseParseNode(children); |
| } |
| |
| public DivideParseNode divide(List<ParseNode> children) { |
| return new DivideParseNode(children); |
| } |
| |
| public UpdateStatisticsStatement updateStatistics(NamedTableNode table, StatisticsCollectionScope scope, Map<String,Object> props) { |
| return new UpdateStatisticsStatement(table, scope, props); |
| } |
| |
| public ExecuteUpgradeStatement executeUpgrade() { |
| return new ExecuteUpgradeStatement(); |
| } |
| |
| |
| public FunctionParseNode functionDistinct(String name, List<ParseNode> 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<ParseNode> args) { |
| return function(ARRAY_ELEM, args); |
| } |
| |
| public FunctionParseNode function(String name, List<ParseNode> args) { |
| BuiltInFunctionInfo info = getInfo(name, args); |
| if (info == null) { |
| return new UDFParseNode(name, args, info); |
| } |
| Constructor<? extends FunctionParseNode> 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<ParseNode> valueNodes, |
| List<ParseNode> columnNodes, boolean isAscending) { |
| |
| List<ParseNode> 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<? extends FunctionParseNode> 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<ParseNode> 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<ParseNode> 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.<ParseNode>asList(rhs, literal(1))))); |
| } |
| |
| public ArrayAllComparisonNode wrapInAll(CompareOp op, ParseNode lhs, ParseNode rhs) { |
| return new ArrayAllComparisonNode(rhs, comparison(op, lhs, elementRef(Arrays.<ParseNode>asList(rhs, literal(1))))); |
| } |
| |
| public ArrayElemRefNode elementRef(List<ParseNode> 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<ParseNode> 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<ParseNode> 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<AliasedNode> select, ParseNode where, |
| List<ParseNode> groupBy, ParseNode having, List<OrderByNode> orderBy, LimitNode limit, OffsetNode offset, int bindCount, boolean isAggregate, |
| boolean hasSequence, List<SelectStatement> selects, Map<String, UDFParseNode> udfParseNodes) { |
| |
| return new SelectStatement(from, hint, isDistinct, select, where, groupBy == null ? Collections.<ParseNode>emptyList() : groupBy, having, |
| orderBy == null ? Collections.<OrderByNode>emptyList() : orderBy, limit, offset, bindCount, isAggregate, hasSequence, selects == null ? Collections.<SelectStatement>emptyList() : selects, udfParseNodes); |
| } |
| |
| public UpsertStatement upsert(NamedTableNode table, HintNode hint, List<ColumnName> columns, List<ParseNode> values, |
| SelectStatement select, int bindCount, |
| Map<String, UDFParseNode> udfParseNodes, |
| List<Pair<ColumnName,ParseNode>> onDupKeyPairs) { |
| return new UpsertStatement(table, hint, columns, values, select, bindCount, udfParseNodes, onDupKeyPairs); |
| } |
| |
| public DeleteStatement delete(NamedTableNode table, HintNode hint, ParseNode node, List<OrderByNode> orderBy, LimitNode limit, int bindCount, Map<String, UDFParseNode> 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<AliasedNode> select, ParseNode where, List<ParseNode> groupBy, ParseNode having, List<OrderByNode> 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<AliasedNode> 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<AliasedNode> 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<AliasedNode> select, ParseNode where, List<ParseNode> 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<OrderByNode> 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<OrderByNode> 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<OrderByNode> 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<SelectStatement> statements, List<OrderByNode> 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<String> aliases = Lists.<String> newArrayList(); |
| Map<String, UDFParseNode> udfParseNodes = new HashMap<String, UDFParseNode>(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<AliasedNode> 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); |
| } |
| } |