OmniSciDB  72c90bc290
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SqlValidatorImpl.java
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to you under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 // HEAVY.AI modification - begin
19 //
20 // Clang formatting makes it difficult to compare with original Calcite file
21 //
22 // clang-format off
23 //
24 //
25 // Changes for HEAVY.AI in this file are annotated by the string "HEAVY.AI"
26 //
27 // The changes are related to QE-397, specifically:
28 // select LTRIM(name) from heavyai_us_states group by 1;
29 // select LTRIM(name) as n from heavyai_us_states group by n;
30 //
31 // These fail because
32 // The function LTRIM requires 2 arguments, but the second has a default
33 // -> thus allowing "LTRIM(name)" to be used for "LTRIM(name', ' ')
34 // The expansion of the select-list ("TRIM(name)" cannot be done prior
35 // to replacement into the group-by
36 // -> because of timing issues (see comments in pre-existing code)
37 // The substitution of "1" and "n" in the group-by is then replaced by
38 // the not-yet-expanded string "LTRIM(name)"
39 // Once substituted, the code returned, assuming no other expansion was needed
40 // -> which was incorrect
41 //
42 // FIX: expand the replacement expr before/while doing the replacement
43 // -> don't assume it is already expanded and ready to use
44 //
45 // HEAVY.AI modification - end
46 
47 package org.apache.calcite.sql.validate;
48 
49 import org.apache.calcite.linq4j.Ord;
50 import org.apache.calcite.linq4j.function.Function2;
51 import org.apache.calcite.linq4j.function.Functions;
52 import org.apache.calcite.plan.RelOptTable;
53 import org.apache.calcite.plan.RelOptUtil;
54 import org.apache.calcite.prepare.Prepare;
55 import org.apache.calcite.rel.type.DynamicRecordType;
56 import org.apache.calcite.rel.type.RelDataType;
57 import org.apache.calcite.rel.type.RelDataTypeFactory;
58 import org.apache.calcite.rel.type.RelDataTypeField;
59 import org.apache.calcite.rel.type.RelDataTypeSystem;
60 import org.apache.calcite.rel.type.RelRecordType;
61 import org.apache.calcite.rex.RexNode;
62 import org.apache.calcite.rex.RexPatternFieldRef;
63 import org.apache.calcite.rex.RexVisitor;
64 import org.apache.calcite.runtime.CalciteContextException;
65 import org.apache.calcite.runtime.CalciteException;
66 import org.apache.calcite.runtime.Feature;
67 import org.apache.calcite.runtime.Resources;
68 import org.apache.calcite.schema.ColumnStrategy;
69 import org.apache.calcite.schema.Table;
70 import org.apache.calcite.schema.impl.ModifiableViewTable;
71 import org.apache.calcite.sql.JoinConditionType;
73 import org.apache.calcite.sql.SqlAccessEnum;
74 import org.apache.calcite.sql.SqlAccessType;
75 import org.apache.calcite.sql.SqlAggFunction;
76 import org.apache.calcite.sql.SqlBasicCall;
77 import org.apache.calcite.sql.SqlCall;
78 import org.apache.calcite.sql.SqlCallBinding;
79 import org.apache.calcite.sql.SqlDataTypeSpec;
80 import org.apache.calcite.sql.SqlDelete;
81 import org.apache.calcite.sql.SqlDynamicParam;
82 import org.apache.calcite.sql.SqlExplain;
83 import org.apache.calcite.sql.SqlFunction;
84 import org.apache.calcite.sql.SqlFunctionCategory;
85 import org.apache.calcite.sql.SqlIdentifier;
86 import org.apache.calcite.sql.SqlInsert;
87 import org.apache.calcite.sql.SqlIntervalLiteral;
88 import org.apache.calcite.sql.SqlIntervalQualifier;
89 import org.apache.calcite.sql.SqlJoin;
90 import org.apache.calcite.sql.SqlKind;
91 import org.apache.calcite.sql.SqlLiteral;
92 import org.apache.calcite.sql.SqlMatchRecognize;
93 import org.apache.calcite.sql.SqlMerge;
94 import org.apache.calcite.sql.SqlNode;
95 import org.apache.calcite.sql.SqlNodeList;
97 import org.apache.calcite.sql.SqlOperatorTable;
98 import org.apache.calcite.sql.SqlOrderBy;
99 import org.apache.calcite.sql.SqlSampleSpec;
100 import org.apache.calcite.sql.SqlSelect;
101 import org.apache.calcite.sql.SqlSelectKeyword;
102 import org.apache.calcite.sql.SqlSnapshot;
103 import org.apache.calcite.sql.SqlSyntax;
104 import org.apache.calcite.sql.SqlTableFunction;
105 import org.apache.calcite.sql.SqlUnresolvedFunction;
106 import org.apache.calcite.sql.SqlUpdate;
107 import org.apache.calcite.sql.SqlUtil;
108 import org.apache.calcite.sql.SqlWindow;
109 import org.apache.calcite.sql.SqlWindowTableFunction;
110 import org.apache.calcite.sql.SqlWith;
111 import org.apache.calcite.sql.SqlWithItem;
112 import org.apache.calcite.sql.fun.SqlCase;
113 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
114 import org.apache.calcite.sql.parser.SqlParserPos;
115 import org.apache.calcite.sql.type.AssignableOperandTypeChecker;
116 import org.apache.calcite.sql.type.ReturnTypes;
117 import org.apache.calcite.sql.type.SqlOperandTypeInference;
118 import org.apache.calcite.sql.type.SqlTypeCoercionRule;
119 import org.apache.calcite.sql.type.SqlTypeName;
120 import org.apache.calcite.sql.type.SqlTypeUtil;
121 import org.apache.calcite.sql.util.IdPair;
122 import org.apache.calcite.sql.util.SqlBasicVisitor;
123 import org.apache.calcite.sql.util.SqlShuttle;
124 import org.apache.calcite.sql.util.SqlVisitor;
125 import org.apache.calcite.sql.validate.implicit.TypeCoercion;
127 import org.apache.calcite.util.BitString;
128 import org.apache.calcite.util.Bug;
129 import org.apache.calcite.util.ImmutableBitSet;
130 import org.apache.calcite.util.ImmutableIntList;
131 import org.apache.calcite.util.ImmutableNullableList;
132 import org.apache.calcite.util.Litmus;
133 import org.apache.calcite.util.Pair;
134 import org.apache.calcite.util.Static;
135 import org.apache.calcite.util.Util;
136 import org.apache.calcite.util.trace.CalciteTrace;
137 
138 import com.google.common.annotations.VisibleForTesting;
139 import com.google.common.base.Preconditions;
140 import com.google.common.collect.ImmutableList;
141 import com.google.common.collect.ImmutableSet;
142 import com.google.common.collect.Sets;
144 
145 import org.slf4j.Logger;
146 
147 import java.math.BigDecimal;
148 import java.math.BigInteger;
149 import java.util.AbstractList;
150 import java.util.ArrayDeque;
151 import java.util.ArrayList;
152 import java.util.Arrays;
153 import java.util.Calendar;
154 import java.util.Collection;
155 import java.util.Collections;
156 import java.util.Deque;
157 import java.util.GregorianCalendar;
158 import java.util.HashMap;
159 import java.util.HashSet;
160 import java.util.IdentityHashMap;
161 import java.util.List;
162 import java.util.Locale;
163 import java.util.Map;
164 import java.util.Objects;
165 import java.util.Set;
166 import java.util.function.Supplier;
167 import java.util.function.UnaryOperator;
168 import java.util.stream.Collectors;
169 import javax.annotation.Nonnull;
170 import javax.annotation.Nullable;
171 
172 import static org.apache.calcite.sql.SqlUtil.stripAs;
173 import static org.apache.calcite.util.Static.RESOURCE;
174 
178 public class SqlValidatorImpl implements SqlValidatorWithHints {
179  //~ Static fields/initializers ---------------------------------------------
180 
181  public static final Logger TRACER = CalciteTrace.PARSER_LOGGER;
182 
186  public static final String UPDATE_SRC_ALIAS = "SYS$SRC";
187 
192  public static final String UPDATE_TGT_ALIAS = "SYS$TGT";
193 
197  public static final String UPDATE_ANON_PREFIX = "SYS$ANON";
198 
199  //~ Instance fields --------------------------------------------------------
200 
201  private final SqlOperatorTable opTab;
202  final SqlValidatorCatalogReader catalogReader;
203 
208  protected final Map<String, IdInfo> idPositions = new HashMap<>();
209 
214  protected final Map<SqlNode, SqlValidatorScope> scopes =
215  new IdentityHashMap<>();
216 
220  private final Map<IdPair<SqlSelect, Clause>, SqlValidatorScope>
221  clauseScopes = new HashMap<>();
222 
226  private TableScope tableScope = null;
227 
233  protected final Map<SqlNode, SqlValidatorNamespace> namespaces =
234  new IdentityHashMap<>();
235 
241  private final Set<SqlNode> cursorSet = Sets.newIdentityHashSet();
242 
248  protected final Deque<FunctionParamInfo> functionCallStack =
249  new ArrayDeque<>();
250 
251  private int nextGeneratedId;
252  protected final RelDataTypeFactory typeFactory;
253 
255  protected final RelDataType unknownType;
256  private final RelDataType booleanType;
257 
263  private final Map<SqlNode, RelDataType> nodeToTypeMap =
264  new IdentityHashMap<>();
265  private final AggFinder aggFinder;
266  private final AggFinder aggOrOverFinder;
267  private final AggFinder aggOrOverOrGroupFinder;
268  private final AggFinder groupFinder;
269  private final AggFinder overFinder;
270 
271  private Config config;
272 
273  private final Map<SqlNode, SqlNode> originalExprs = new HashMap<>();
274 
275  private SqlNode top;
276 
277  // TODO jvs 11-Dec-2008: make this local to performUnconditionalRewrites
278  // if it's OK to expand the signature of that method.
279  private boolean validatingSqlMerge;
280 
281  private boolean inWindow; // Allow nested aggregates
282 
283  private final SqlValidatorImpl.ValidationErrorFunction validationErrorFunction =
284  new SqlValidatorImpl.ValidationErrorFunction();
285 
286  // TypeCoercion instance used for implicit type coercion.
287  private TypeCoercion typeCoercion;
288 
289  //~ Constructors -----------------------------------------------------------
290 
299  protected SqlValidatorImpl(
300  SqlOperatorTable opTab,
301  SqlValidatorCatalogReader catalogReader,
302  RelDataTypeFactory typeFactory,
303  Config config) {
304  this.opTab = Objects.requireNonNull(opTab);
305  this.catalogReader = Objects.requireNonNull(catalogReader);
306  this.typeFactory = Objects.requireNonNull(typeFactory);
307  this.config = Objects.requireNonNull(config);
308 
309  unknownType = typeFactory.createUnknownType();
310  booleanType = typeFactory.createSqlType(SqlTypeName.BOOLEAN);
311 
312  final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
313  aggFinder = new AggFinder(opTab, false, true, false, null, nameMatcher);
315  new AggFinder(opTab, true, true, false, null, nameMatcher);
316  overFinder = new AggFinder(opTab, true, false, false, aggOrOverFinder,
317  nameMatcher);
318  groupFinder = new AggFinder(opTab, false, false, true, null, nameMatcher);
319  aggOrOverOrGroupFinder = new AggFinder(opTab, true, true, true, null,
320  nameMatcher);
321  // this.typeCoercion = config.typeCoercionFactory().create(typeFactory, this); // HEAVY.AI original
322  this.typeCoercion = TypeCoercions.createHeavyDBTypeCoercion(typeFactory, this); // HEAVY.AI new
323  if (config.typeCoercionRules() != null) {
324  SqlTypeCoercionRule.THREAD_PROVIDERS.set(config.typeCoercionRules());
325  }
326  }
327 
328  //~ Methods ----------------------------------------------------------------
329 
330  public SqlConformance getConformance() {
331  return config.sqlConformance();
332  }
333 
334  public SqlValidatorCatalogReader getCatalogReader() {
335  return catalogReader;
336  }
337 
338  public SqlOperatorTable getOperatorTable() {
339  return opTab;
340  }
341 
342  public RelDataTypeFactory getTypeFactory() {
343  return typeFactory;
344  }
345 
346  public RelDataType getUnknownType() {
347  return unknownType;
348  }
349 
350  public SqlNodeList expandStar(
351  SqlNodeList selectList,
352  SqlSelect select,
353  boolean includeSystemVars) {
354  final List<SqlNode> list = new ArrayList<>();
355  final List<Map.Entry<String, RelDataType>> types = new ArrayList<>();
356  for (int i = 0; i < selectList.size(); i++) {
357  final SqlNode selectItem = selectList.get(i);
358  final RelDataType originalType = getValidatedNodeTypeIfKnown(selectItem);
360  selectItem,
361  select,
362  Util.first(originalType, unknownType),
363  list,
364  catalogReader.nameMatcher().createSet(),
365  types,
366  includeSystemVars);
367  }
368  getRawSelectScope(select).setExpandedSelectList(list);
369  return new SqlNodeList(list, SqlParserPos.ZERO);
370  }
371 
372  // implement SqlValidator
373  public void declareCursor(SqlSelect select, SqlValidatorScope parentScope) {
374  cursorSet.add(select);
375 
376  // add the cursor to a map that maps the cursor to its select based on
377  // the position of the cursor relative to other cursors in that call
378  FunctionParamInfo funcParamInfo = functionCallStack.peek();
379  Map<Integer, SqlSelect> cursorMap = funcParamInfo.cursorPosToSelectMap;
380  int numCursors = cursorMap.size();
381  cursorMap.put(numCursors, select);
382 
383  // create a namespace associated with the result of the select
384  // that is the argument to the cursor constructor; register it
385  // with a scope corresponding to the cursor
386  SelectScope cursorScope = new SelectScope(parentScope, null, select);
387  clauseScopes.put(IdPair.of(select, Clause.CURSOR), cursorScope);
388  final SelectNamespace selectNs = createSelectNamespace(select, select);
389  String alias = deriveAlias(select, nextGeneratedId++);
390  registerNamespace(cursorScope, alias, selectNs, false);
391  }
392 
393  // implement SqlValidator
394  public void pushFunctionCall() {
395  FunctionParamInfo funcInfo = new FunctionParamInfo();
396  functionCallStack.push(funcInfo);
397  }
398 
399  // implement SqlValidator
400  public void popFunctionCall() {
401  functionCallStack.pop();
402  }
403 
404  // implement SqlValidator
405  public String getParentCursor(String columnListParamName) {
406  FunctionParamInfo funcParamInfo = functionCallStack.peek();
407  Map<String, String> parentCursorMap =
408  funcParamInfo.columnListParamToParentCursorMap;
409  return parentCursorMap.get(columnListParamName);
410  }
411 
424  private boolean expandSelectItem(
425  final SqlNode selectItem,
426  SqlSelect select,
427  RelDataType targetType,
428  List<SqlNode> selectItems,
429  Set<String> aliases,
430  List<Map.Entry<String, RelDataType>> fields,
431  final boolean includeSystemVars) {
432  final SelectScope scope = (SelectScope) getWhereScope(select);
433  if (expandStar(selectItems, aliases, fields, includeSystemVars, scope,
434  selectItem)) {
435  return true;
436  }
437 
438  // Expand the select item: fully-qualify columns, and convert
439  // parentheses-free functions such as LOCALTIME into explicit function
440  // calls.
441  SqlNode expanded = expandSelectExpr(selectItem, scope, select);
442  final String alias =
443  deriveAlias(
444  selectItem,
445  aliases.size());
446 
447  // If expansion has altered the natural alias, supply an explicit 'AS'.
448  final SqlValidatorScope selectScope = getSelectScope(select);
449  if (expanded != selectItem) {
450  String newAlias =
451  deriveAlias(
452  expanded,
453  aliases.size());
454  if (!newAlias.equals(alias)) {
455  expanded =
456  SqlStdOperatorTable.AS.createCall(
457  selectItem.getParserPosition(),
458  expanded,
459  new SqlIdentifier(alias, SqlParserPos.ZERO));
460  deriveTypeImpl(selectScope, expanded);
461  }
462  }
463 
464  selectItems.add(expanded);
465  aliases.add(alias);
466 
467  if (expanded != null) {
468  inferUnknownTypes(targetType, scope, expanded);
469  }
470  final RelDataType type = deriveType(selectScope, expanded);
471  setValidatedNodeType(expanded, type);
472  fields.add(Pair.of(alias, type));
473  return false;
474  }
475 
476  private static SqlNode expandExprFromJoin(SqlJoin join, SqlIdentifier identifier,
477  SelectScope scope) {
478  if (join.getConditionType() != JoinConditionType.USING) {
479  return identifier;
480  }
481 
482  for (SqlNode node : (SqlNodeList) join.getCondition()) {
483  final String name = ((SqlIdentifier) node).getSimple();
484  if (identifier.getSimple().equals(name)) {
485  final List<SqlNode> qualifiedNode = new ArrayList<>();
486  for (ScopeChild child : scope.children) {
487  if (child.namespace.getRowType()
488  .getFieldNames().indexOf(name) >= 0) {
489  final SqlIdentifier exp =
490  new SqlIdentifier(
491  ImmutableList.of(child.name, name),
492  identifier.getParserPosition());
493  qualifiedNode.add(exp);
494  }
495  }
496 
497  assert qualifiedNode.size() == 2;
498  final SqlNode finalNode =
499  SqlStdOperatorTable.AS.createCall(SqlParserPos.ZERO,
500  SqlStdOperatorTable.COALESCE.createCall(SqlParserPos.ZERO,
501  qualifiedNode.get(0),
502  qualifiedNode.get(1)),
503  new SqlIdentifier(name, SqlParserPos.ZERO));
504  return finalNode;
505  }
506  }
507 
508  // Only need to try to expand the expr from the left input of join
509  // since it is always left-deep join.
510  final SqlNode node = join.getLeft();
511  if (node instanceof SqlJoin) {
512  return expandExprFromJoin((SqlJoin) node, identifier, scope);
513  } else {
514  return identifier;
515  }
516  }
517 
520  public List<String> usingNames(SqlJoin join) {
521  switch (join.getConditionType()) {
522  case USING:
523  final ImmutableList.Builder<String> list = ImmutableList.builder();
524  final Set<String> names = catalogReader.nameMatcher().createSet();
525  for (SqlNode node : (SqlNodeList) join.getCondition()) {
526  final String name = ((SqlIdentifier) node).getSimple();
527  if (names.add(name)) {
528  list.add(name);
529  }
530  }
531  return list.build();
532  case NONE:
533  if (join.isNatural()) {
534  final RelDataType t0 = getValidatedNodeType(join.getLeft());
535  final RelDataType t1 = getValidatedNodeType(join.getRight());
536  return SqlValidatorUtil.deriveNaturalJoinColumnList(
537  catalogReader.nameMatcher(), t0, t1);
538  }
539  }
540  return null;
541  }
542 
543  private static SqlNode expandCommonColumn(SqlSelect sqlSelect,
544  SqlNode selectItem, SelectScope scope, SqlValidatorImpl validator) {
545  if (!(selectItem instanceof SqlIdentifier)) {
546  return selectItem;
547  }
548 
549  final SqlNode from = sqlSelect.getFrom();
550  if (!(from instanceof SqlJoin)) {
551  return selectItem;
552  }
553 
554  final SqlIdentifier identifier = (SqlIdentifier) selectItem;
555  if (!identifier.isSimple()) {
556  if (!validator.config().sqlConformance().allowQualifyingCommonColumn()) {
557  validateQualifiedCommonColumn((SqlJoin) from, identifier, scope, validator);
558  }
559  return selectItem;
560  }
561 
562  return expandExprFromJoin((SqlJoin) from, identifier, scope);
563  }
564 
565  private static void validateQualifiedCommonColumn(SqlJoin join,
566  SqlIdentifier identifier, SelectScope scope, SqlValidatorImpl validator) {
567  List<String> names = validator.usingNames(join);
568  if (names == null) {
569  // Not USING or NATURAL.
570  return;
571  }
572 
573  // First we should make sure that the first component is the table name.
574  // Then check whether the qualified identifier contains common column.
575  for (ScopeChild child : scope.children) {
576  if (child.name.equals(identifier.getComponent(0).toString())) {
577  if (names.indexOf(identifier.getComponent(1).toString()) >= 0) {
578  throw validator.newValidationError(identifier,
579  RESOURCE.disallowsQualifyingCommonColumn(identifier.toString()));
580  }
581  }
582  }
583 
584  // Only need to try to validate the expr from the left input of join
585  // since it is always left-deep join.
586  final SqlNode node = join.getLeft();
587  if (node instanceof SqlJoin) {
588  validateQualifiedCommonColumn((SqlJoin) node, identifier, scope, validator);
589  }
590  }
591 
592  private boolean expandStar(List<SqlNode> selectItems, Set<String> aliases,
593  List<Map.Entry<String, RelDataType>> fields, boolean includeSystemVars,
594  SelectScope scope, SqlNode node) {
595  if (!(node instanceof SqlIdentifier)) {
596  return false;
597  }
598  final SqlIdentifier identifier = (SqlIdentifier) node;
599  if (!identifier.isStar()) {
600  return false;
601  }
602  final SqlParserPos startPosition = identifier.getParserPosition();
603  switch (identifier.names.size()) {
604  case 1:
605  boolean hasDynamicStruct = false;
606  for (ScopeChild child : scope.children) {
607  final int before = fields.size();
608  if (child.namespace.getRowType().isDynamicStruct()) {
609  hasDynamicStruct = true;
610  // don't expand star if the underneath table is dynamic.
611  // Treat this star as a special field in validation/conversion and
612  // wait until execution time to expand this star.
613  final SqlNode exp =
614  new SqlIdentifier(
615  ImmutableList.of(child.name,
616  DynamicRecordType.DYNAMIC_STAR_PREFIX),
617  startPosition);
619  selectItems,
620  aliases,
621  fields,
622  exp,
623  scope,
624  includeSystemVars);
625  } else {
626  final SqlNode from = child.namespace.getNode();
627  final SqlValidatorNamespace fromNs = getNamespace(from, scope);
628  assert fromNs != null;
629  final RelDataType rowType = fromNs.getRowType();
630  for (RelDataTypeField field : rowType.getFieldList()) {
631  String columnName = field.getName();
632 
633  // TODO: do real implicit collation here
634  final SqlIdentifier exp =
635  new SqlIdentifier(
636  ImmutableList.of(child.name, columnName),
637  startPosition);
638  // Don't add expanded rolled up columns
639  if (!isRolledUpColumn(exp, scope)) {
641  selectItems,
642  aliases,
643  fields,
644  includeSystemVars,
645  scope,
646  exp,
647  field);
648  }
649  }
650  }
651  if (child.nullable) {
652  for (int i = before; i < fields.size(); i++) {
653  final Map.Entry<String, RelDataType> entry = fields.get(i);
654  final RelDataType type = entry.getValue();
655  if (!type.isNullable()) {
656  fields.set(i,
657  Pair.of(entry.getKey(),
658  typeFactory.createTypeWithNullability(type, true)));
659  }
660  }
661  }
662  }
663  // If NATURAL JOIN or USING is present, move key fields to the front of
664  // the list, per standard SQL. Disabled if there are dynamic fields.
665  if (!hasDynamicStruct || Bug.CALCITE_2400_FIXED) {
666  new Permute(scope.getNode().getFrom(), 0).permute(selectItems, fields);
667  }
668  return true;
669 
670  default:
671  final SqlIdentifier prefixId = identifier.skipLast(1);
672  final SqlValidatorScope.ResolvedImpl resolved =
673  new SqlValidatorScope.ResolvedImpl();
674  final SqlNameMatcher nameMatcher =
675  scope.validator.catalogReader.nameMatcher();
676  scope.resolve(prefixId.names, nameMatcher, true, resolved);
677  if (resolved.count() == 0) {
678  // e.g. "select s.t.* from e"
679  // or "select r.* from e"
680  throw newValidationError(prefixId,
681  RESOURCE.unknownIdentifier(prefixId.toString()));
682  }
683  final RelDataType rowType = resolved.only().rowType();
684  if (rowType.isDynamicStruct()) {
685  // don't expand star if the underneath table is dynamic.
687  selectItems,
688  aliases,
689  fields,
690  prefixId.plus(DynamicRecordType.DYNAMIC_STAR_PREFIX, startPosition),
691  scope,
692  includeSystemVars);
693  } else if (rowType.isStruct()) {
694  for (RelDataTypeField field : rowType.getFieldList()) {
695  String columnName = field.getName();
696 
697  // TODO: do real implicit collation here
699  selectItems,
700  aliases,
701  fields,
702  includeSystemVars,
703  scope,
704  prefixId.plus(columnName, startPosition),
705  field);
706  }
707  } else {
708  throw newValidationError(prefixId, RESOURCE.starRequiresRecordType());
709  }
710  return true;
711  }
712  }
713 
714  private SqlNode maybeCast(SqlNode node, RelDataType currentType,
715  RelDataType desiredType) {
716  return SqlTypeUtil.equalSansNullability(typeFactory, currentType, desiredType)
717  ? node
718  : SqlStdOperatorTable.CAST.createCall(SqlParserPos.ZERO,
719  node, SqlTypeUtil.convertTypeToSpec(desiredType));
720  }
721 
722  private boolean addOrExpandField(List<SqlNode> selectItems, Set<String> aliases,
723  List<Map.Entry<String, RelDataType>> fields, boolean includeSystemVars,
724  SelectScope scope, SqlIdentifier id, RelDataTypeField field) {
725  switch (field.getType().getStructKind()) {
726  case PEEK_FIELDS:
727  case PEEK_FIELDS_DEFAULT:
728  final SqlNode starExp = id.plusStar();
729  expandStar(
730  selectItems,
731  aliases,
732  fields,
733  includeSystemVars,
734  scope,
735  starExp);
736  return true;
737 
738  default:
740  selectItems,
741  aliases,
742  fields,
743  id,
744  scope,
745  includeSystemVars);
746  }
747 
748  return false;
749  }
750 
751  public SqlNode validate(SqlNode topNode) {
752  SqlValidatorScope scope = new EmptyScope(this);
753  scope = new CatalogScope(scope, ImmutableList.of("CATALOG"));
754  final SqlNode topNode2 = validateScopedExpression(topNode, scope);
755  final RelDataType type = getValidatedNodeType(topNode2);
756  Util.discard(type);
757  return topNode2;
758  }
759 
760  public List<SqlMoniker> lookupHints(SqlNode topNode, SqlParserPos pos) {
761  SqlValidatorScope scope = new EmptyScope(this);
762  SqlNode outermostNode = performUnconditionalRewrites(topNode, false);
763  cursorSet.add(outermostNode);
764  if (outermostNode.isA(SqlKind.TOP_LEVEL)) {
766  scope,
767  null,
768  outermostNode,
769  outermostNode,
770  null,
771  false);
772  }
773  final SqlValidatorNamespace ns = getNamespace(outermostNode);
774  if (ns == null) {
775  throw new AssertionError("Not a query: " + outermostNode);
776  }
777  Collection<SqlMoniker> hintList = Sets.newTreeSet(SqlMoniker.COMPARATOR);
778  lookupSelectHints(ns, pos, hintList);
779  return ImmutableList.copyOf(hintList);
780  }
781 
782  public SqlMoniker lookupQualifiedName(SqlNode topNode, SqlParserPos pos) {
783  final String posString = pos.toString();
784  IdInfo info = idPositions.get(posString);
785  if (info != null) {
786  final SqlQualified qualified = info.scope.fullyQualify(info.id);
787  return new SqlIdentifierMoniker(qualified.identifier);
788  } else {
789  return null;
790  }
791  }
792 
804  SqlSelect select,
805  SqlParserPos pos,
806  Collection<SqlMoniker> hintList) {
807  IdInfo info = idPositions.get(pos.toString());
808  if ((info == null) || (info.scope == null)) {
809  SqlNode fromNode = select.getFrom();
810  final SqlValidatorScope fromScope = getFromScope(select);
811  lookupFromHints(fromNode, fromScope, pos, hintList);
812  } else {
813  lookupNameCompletionHints(info.scope, info.id.names,
814  info.id.getParserPosition(), hintList);
815  }
816  }
817 
818  private void lookupSelectHints(
819  SqlValidatorNamespace ns,
820  SqlParserPos pos,
821  Collection<SqlMoniker> hintList) {
822  final SqlNode node = ns.getNode();
823  if (node instanceof SqlSelect) {
824  lookupSelectHints((SqlSelect) node, pos, hintList);
825  }
826  }
827 
828  private void lookupFromHints(
829  SqlNode node,
830  SqlValidatorScope scope,
831  SqlParserPos pos,
832  Collection<SqlMoniker> hintList) {
833  if (node == null) {
834  // This can happen in cases like "select * _suggest_", so from clause is absent
835  return;
836  }
837  final SqlValidatorNamespace ns = getNamespace(node);
838  if (ns.isWrapperFor(IdentifierNamespace.class)) {
839  IdentifierNamespace idNs = ns.unwrap(IdentifierNamespace.class);
840  final SqlIdentifier id = idNs.getId();
841  for (int i = 0; i < id.names.size(); i++) {
842  if (pos.toString().equals(
843  id.getComponent(i).getParserPosition().toString())) {
844  final List<SqlMoniker> objNames = new ArrayList<>();
845  SqlValidatorUtil.getSchemaObjectMonikers(
847  id.names.subList(0, i + 1),
848  objNames);
849  for (SqlMoniker objName : objNames) {
850  if (objName.getType() != SqlMonikerType.FUNCTION) {
851  hintList.add(objName);
852  }
853  }
854  return;
855  }
856  }
857  }
858  switch (node.getKind()) {
859  case JOIN:
860  lookupJoinHints((SqlJoin) node, scope, pos, hintList);
861  break;
862  default:
863  lookupSelectHints(ns, pos, hintList);
864  break;
865  }
866  }
867 
868  private void lookupJoinHints(
869  SqlJoin join,
870  SqlValidatorScope scope,
871  SqlParserPos pos,
872  Collection<SqlMoniker> hintList) {
873  SqlNode left = join.getLeft();
874  SqlNode right = join.getRight();
875  SqlNode condition = join.getCondition();
876  lookupFromHints(left, scope, pos, hintList);
877  if (hintList.size() > 0) {
878  return;
879  }
880  lookupFromHints(right, scope, pos, hintList);
881  if (hintList.size() > 0) {
882  return;
883  }
884  final JoinConditionType conditionType = join.getConditionType();
885  final SqlValidatorScope joinScope = scopes.get(join);
886  switch (conditionType) {
887  case ON:
888  condition.findValidOptions(this, joinScope, pos, hintList);
889  return;
890  default:
891 
892  // No suggestions.
893  // Not supporting hints for other types such as 'Using' yet.
894  }
895  }
896 
905  public final void lookupNameCompletionHints(
906  SqlValidatorScope scope,
907  List<String> names,
908  SqlParserPos pos,
909  Collection<SqlMoniker> hintList) {
910  // Remove the last part of name - it is a dummy
911  List<String> subNames = Util.skipLast(names);
912 
913  if (subNames.size() > 0) {
914  // If there's a prefix, resolve it to a namespace.
915  SqlValidatorNamespace ns = null;
916  for (String name : subNames) {
917  if (ns == null) {
918  final SqlValidatorScope.ResolvedImpl resolved =
919  new SqlValidatorScope.ResolvedImpl();
920  final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
921  scope.resolve(ImmutableList.of(name), nameMatcher, false, resolved);
922  if (resolved.count() == 1) {
923  ns = resolved.only().namespace;
924  }
925  } else {
926  ns = ns.lookupChild(name);
927  }
928  if (ns == null) {
929  break;
930  }
931  }
932  if (ns != null) {
933  RelDataType rowType = ns.getRowType();
934  if (rowType.isStruct()) {
935  for (RelDataTypeField field : rowType.getFieldList()) {
936  hintList.add(
937  new SqlMonikerImpl(
938  field.getName(),
939  SqlMonikerType.COLUMN));
940  }
941  }
942  }
943 
944  // builtin function names are valid completion hints when the
945  // identifier has only 1 name part
946  findAllValidFunctionNames(names, this, hintList, pos);
947  } else {
948  // No prefix; use the children of the current scope (that is,
949  // the aliases in the FROM clause)
950  scope.findAliases(hintList);
951 
952  // If there's only one alias, add all child columns
953  SelectScope selectScope =
954  SqlValidatorUtil.getEnclosingSelectScope(scope);
955  if ((selectScope != null)
956  && (selectScope.getChildren().size() == 1)) {
957  RelDataType rowType =
958  selectScope.getChildren().get(0).getRowType();
959  for (RelDataTypeField field : rowType.getFieldList()) {
960  hintList.add(
961  new SqlMonikerImpl(
962  field.getName(),
963  SqlMonikerType.COLUMN));
964  }
965  }
966  }
967 
968  findAllValidUdfNames(names, this, hintList);
969  }
970 
971  private static void findAllValidUdfNames(
972  List<String> names,
973  SqlValidator validator,
974  Collection<SqlMoniker> result) {
975  final List<SqlMoniker> objNames = new ArrayList<>();
976  SqlValidatorUtil.getSchemaObjectMonikers(
977  validator.getCatalogReader(),
978  names,
979  objNames);
980  for (SqlMoniker objName : objNames) {
981  if (objName.getType() == SqlMonikerType.FUNCTION) {
982  result.add(objName);
983  }
984  }
985  }
986 
987  private static void findAllValidFunctionNames(
988  List<String> names,
989  SqlValidator validator,
990  Collection<SqlMoniker> result,
991  SqlParserPos pos) {
992  // a function name can only be 1 part
993  if (names.size() > 1) {
994  return;
995  }
996  for (SqlOperator op : validator.getOperatorTable().getOperatorList()) {
997  SqlIdentifier curOpId =
998  new SqlIdentifier(
999  op.getName(),
1000  pos);
1001 
1002  final SqlCall call = validator.makeNullaryCall(curOpId);
1003  if (call != null) {
1004  result.add(
1005  new SqlMonikerImpl(
1006  op.getName(),
1007  SqlMonikerType.FUNCTION));
1008  } else {
1009  if ((op.getSyntax() == SqlSyntax.FUNCTION)
1010  || (op.getSyntax() == SqlSyntax.PREFIX)) {
1011  if (op.getOperandTypeChecker() != null) {
1012  String sig = op.getAllowedSignatures();
1013  sig = sig.replace("'", "");
1014  result.add(
1015  new SqlMonikerImpl(
1016  sig,
1017  SqlMonikerType.FUNCTION));
1018  continue;
1019  }
1020  result.add(
1021  new SqlMonikerImpl(
1022  op.getName(),
1023  SqlMonikerType.FUNCTION));
1024  }
1025  }
1026  }
1027  }
1028 
1030  SqlNode topNode,
1031  final Map<String, RelDataType> nameToTypeMap) {
1032  SqlValidatorScope scope = new ParameterScope(this, nameToTypeMap);
1033  return validateScopedExpression(topNode, scope);
1034  }
1035 
1036  private SqlNode validateScopedExpression(
1037  SqlNode topNode,
1038  SqlValidatorScope scope) {
1039  SqlNode outermostNode = performUnconditionalRewrites(topNode, false);
1040  cursorSet.add(outermostNode);
1041  top = outermostNode;
1042  TRACER.trace("After unconditional rewrite: {}", outermostNode);
1043  if (outermostNode.isA(SqlKind.TOP_LEVEL)) {
1044  registerQuery(scope, null, outermostNode, outermostNode, null, false);
1045  }
1046  outermostNode.validate(this, scope);
1047  if (!outermostNode.isA(SqlKind.TOP_LEVEL)) {
1048  // force type derivation so that we can provide it to the
1049  // caller later without needing the scope
1050  deriveType(scope, outermostNode);
1051  }
1052  TRACER.trace("After validation: {}", outermostNode);
1053  return outermostNode;
1054  }
1055 
1056  public void validateQuery(SqlNode node, SqlValidatorScope scope,
1057  RelDataType targetRowType) {
1058  final SqlValidatorNamespace ns = getNamespace(node, scope);
1059  if (node.getKind() == SqlKind.TABLESAMPLE) {
1060  List<SqlNode> operands = ((SqlCall) node).getOperandList();
1061  SqlSampleSpec sampleSpec = SqlLiteral.sampleValue(operands.get(1));
1062  if (sampleSpec instanceof SqlSampleSpec.SqlTableSampleSpec) {
1063  validateFeature(RESOURCE.sQLFeature_T613(), node.getParserPosition());
1064  } else if (sampleSpec
1065  instanceof SqlSampleSpec.SqlSubstitutionSampleSpec) {
1066  validateFeature(RESOURCE.sQLFeatureExt_T613_Substitution(),
1067  node.getParserPosition());
1068  }
1069  }
1070 
1071  validateNamespace(ns, targetRowType);
1072  switch (node.getKind()) {
1073  case EXTEND:
1074  // Until we have a dedicated namespace for EXTEND
1075  deriveType(scope, node);
1076  }
1077  if (node == top) {
1078  validateModality(node);
1079  }
1081  node,
1082  ns.getTable(),
1083  SqlAccessEnum.SELECT);
1084 
1085  validateSnapshot(node, scope, ns);
1086  }
1087 
1095  protected void validateNamespace(final SqlValidatorNamespace namespace,
1096  RelDataType targetRowType) {
1097  namespace.validate(targetRowType);
1098  if (namespace.getNode() != null) {
1099  setValidatedNodeType(namespace.getNode(), namespace.getType());
1100  }
1101  }
1102 
1103  @VisibleForTesting
1104  public SqlValidatorScope getEmptyScope() {
1105  return new EmptyScope(this);
1106  }
1107 
1108  public SqlValidatorScope getCursorScope(SqlSelect select) {
1109  return clauseScopes.get(IdPair.of(select, Clause.CURSOR));
1110  }
1111 
1112  public SqlValidatorScope getWhereScope(SqlSelect select) {
1113  return clauseScopes.get(IdPair.of(select, Clause.WHERE));
1114  }
1115 
1116  public SqlValidatorScope getSelectScope(SqlSelect select) {
1117  return clauseScopes.get(IdPair.of(select, Clause.SELECT));
1118  }
1119 
1120  public SelectScope getRawSelectScope(SqlSelect select) {
1121  SqlValidatorScope scope = getSelectScope(select);
1122  if (scope instanceof AggregatingSelectScope) {
1123  scope = ((AggregatingSelectScope) scope).getParent();
1124  }
1125  return (SelectScope) scope;
1126  }
1127 
1128  public SqlValidatorScope getHavingScope(SqlSelect select) {
1129  // Yes, it's the same as getSelectScope
1130  return clauseScopes.get(IdPair.of(select, Clause.SELECT));
1131  }
1132 
1133  public SqlValidatorScope getGroupScope(SqlSelect select) {
1134  // Yes, it's the same as getWhereScope
1135  return clauseScopes.get(IdPair.of(select, Clause.GROUP_BY));
1136  }
1137 
1138  public SqlValidatorScope getFromScope(SqlSelect select) {
1139  return scopes.get(select);
1140  }
1141 
1142  public SqlValidatorScope getOrderScope(SqlSelect select) {
1143  return clauseScopes.get(IdPair.of(select, Clause.ORDER));
1144  }
1145 
1146  public SqlValidatorScope getMatchRecognizeScope(SqlMatchRecognize node) {
1147  return scopes.get(node);
1148  }
1149 
1150  public SqlValidatorScope getJoinScope(SqlNode node) {
1151  return scopes.get(stripAs(node));
1152  }
1153 
1154  public SqlValidatorScope getOverScope(SqlNode node) {
1155  return scopes.get(node);
1156  }
1157 
1158  private SqlValidatorNamespace getNamespace(SqlNode node,
1159  SqlValidatorScope scope) {
1160  if (node instanceof SqlIdentifier && scope instanceof DelegatingScope) {
1161  final SqlIdentifier id = (SqlIdentifier) node;
1162  final DelegatingScope idScope = (DelegatingScope) ((DelegatingScope) scope).getParent();
1163  return getNamespace(id, idScope);
1164  } else if (node instanceof SqlCall) {
1165  // Handle extended identifiers.
1166  final SqlCall call = (SqlCall) node;
1167  switch (call.getOperator().getKind()) {
1168  case TABLE_REF:
1169  return getNamespace(call.operand(0), scope);
1170  case EXTEND:
1171  final SqlNode operand0 = call.getOperandList().get(0);
1172  final SqlIdentifier identifier = operand0.getKind() == SqlKind.TABLE_REF
1173  ? ((SqlCall) operand0).operand(0)
1174  : (SqlIdentifier) operand0;
1175  final DelegatingScope idScope = (DelegatingScope) scope;
1176  return getNamespace(identifier, idScope);
1177  case AS:
1178  final SqlNode nested = call.getOperandList().get(0);
1179  switch (nested.getKind()) {
1180  case TABLE_REF:
1181  case EXTEND:
1182  return getNamespace(nested, scope);
1183  }
1184  break;
1185  }
1186  }
1187  return getNamespace(node);
1188  }
1189 
1190  private SqlValidatorNamespace getNamespace(SqlIdentifier id, DelegatingScope scope) {
1191  if (id.isSimple()) {
1192  final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
1193  final SqlValidatorScope.ResolvedImpl resolved =
1194  new SqlValidatorScope.ResolvedImpl();
1195  scope.resolve(id.names, nameMatcher, false, resolved);
1196  if (resolved.count() == 1) {
1197  return resolved.only().namespace;
1198  }
1199  }
1200  return getNamespace(id);
1201  }
1202 
1203  public SqlValidatorNamespace getNamespace(SqlNode node) {
1204  switch (node.getKind()) {
1205  case AS:
1206 
1207  // AS has a namespace if it has a column list 'AS t (c1, c2, ...)'
1208  final SqlValidatorNamespace ns = namespaces.get(node);
1209  if (ns != null) {
1210  return ns;
1211  }
1212  // fall through
1213  case TABLE_REF:
1214  case SNAPSHOT:
1215  case OVER:
1216  case COLLECTION_TABLE:
1217  case ORDER_BY:
1218  case TABLESAMPLE:
1219  return getNamespace(((SqlCall) node).operand(0));
1220  default:
1221  return namespaces.get(node);
1222  }
1223  }
1224 
1225  private void handleOffsetFetch(SqlNode offset, SqlNode fetch) {
1226  if (offset instanceof SqlDynamicParam) {
1227  setValidatedNodeType(offset,
1228  typeFactory.createSqlType(SqlTypeName.INTEGER));
1229  }
1230  if (fetch instanceof SqlDynamicParam) {
1231  setValidatedNodeType(fetch,
1232  typeFactory.createSqlType(SqlTypeName.INTEGER));
1233  }
1234  }
1235 
1245  protected SqlNode performUnconditionalRewrites(
1246  SqlNode node,
1247  boolean underFrom) {
1248  if (node == null) {
1249  return null;
1250  }
1251 
1252  SqlNode newOperand;
1253 
1254  // first transform operands and invoke generic call rewrite
1255  if (node instanceof SqlCall) {
1256  if (node instanceof SqlMerge) {
1257  validatingSqlMerge = true;
1258  }
1259  SqlCall call = (SqlCall) node;
1260  final SqlKind kind = call.getKind();
1261  final List<SqlNode> operands = call.getOperandList();
1262  for (int i = 0; i < operands.size(); i++) {
1263  SqlNode operand = operands.get(i);
1264  boolean childUnderFrom;
1265  if (kind == SqlKind.SELECT) {
1266  childUnderFrom = i == SqlSelect.FROM_OPERAND;
1267  } else if (kind == SqlKind.AS && (i == 0)) {
1268  // for an aliased expression, it is under FROM if
1269  // the AS expression is under FROM
1270  childUnderFrom = underFrom;
1271  } else {
1272  childUnderFrom = false;
1273  }
1274  newOperand =
1275  performUnconditionalRewrites(operand, childUnderFrom);
1276  if (newOperand != null && newOperand != operand) {
1277  call.setOperand(i, newOperand);
1278  }
1279  }
1280 
1281  if (call.getOperator() instanceof SqlUnresolvedFunction) {
1282  assert call instanceof SqlBasicCall;
1283  final SqlUnresolvedFunction function =
1284  (SqlUnresolvedFunction) call.getOperator();
1285  // This function hasn't been resolved yet. Perform
1286  // a half-hearted resolution now in case it's a
1287  // builtin function requiring special casing. If it's
1288  // not, we'll handle it later during overload resolution.
1289  final List<SqlOperator> overloads = new ArrayList<>();
1290  opTab.lookupOperatorOverloads(function.getNameAsId(),
1291  function.getFunctionType(), SqlSyntax.FUNCTION, overloads,
1292  catalogReader.nameMatcher());
1293  if (overloads.size() == 1) {
1294  ((SqlBasicCall) call).setOperator(overloads.get(0));
1295  }
1296  }
1297  if (config.callRewrite()) {
1298  node = call.getOperator().rewriteCall(this, call);
1299  }
1300  } else if (node instanceof SqlNodeList) {
1301  SqlNodeList list = (SqlNodeList) node;
1302  for (int i = 0, count = list.size(); i < count; i++) {
1303  SqlNode operand = list.get(i);
1304  newOperand =
1306  operand,
1307  false);
1308  if (newOperand != null) {
1309  list.getList().set(i, newOperand);
1310  }
1311  }
1312  }
1313 
1314  // now transform node itself
1315  final SqlKind kind = node.getKind();
1316  switch (kind) {
1317  case VALUES:
1318  // CHECKSTYLE: IGNORE 1
1319  if (underFrom || true) {
1320  // leave FROM (VALUES(...)) [ AS alias ] clauses alone,
1321  // otherwise they grow cancerously if this rewrite is invoked
1322  // over and over
1323  return node;
1324  } else {
1325  final SqlNodeList selectList =
1326  new SqlNodeList(SqlParserPos.ZERO);
1327  selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
1328  return new SqlSelect(node.getParserPosition(), null, selectList, node,
1329  null, null, null, null, null, null, null, null);
1330  }
1331 
1332  case ORDER_BY: {
1333  SqlOrderBy orderBy = (SqlOrderBy) node;
1334  handleOffsetFetch(orderBy.offset, orderBy.fetch);
1335  if (orderBy.query instanceof SqlSelect) {
1336  SqlSelect select = (SqlSelect) orderBy.query;
1337 
1338  // Don't clobber existing ORDER BY. It may be needed for
1339  // an order-sensitive function like RANK.
1340  if (select.getOrderList() == null) {
1341  // push ORDER BY into existing select
1342  select.setOrderBy(orderBy.orderList);
1343  select.setOffset(orderBy.offset);
1344  select.setFetch(orderBy.fetch);
1345  return select;
1346  }
1347  }
1348  if (orderBy.query instanceof SqlWith
1349  && ((SqlWith) orderBy.query).body instanceof SqlSelect) {
1350  SqlWith with = (SqlWith) orderBy.query;
1351  SqlSelect select = (SqlSelect) with.body;
1352 
1353  // Don't clobber existing ORDER BY. It may be needed for
1354  // an order-sensitive function like RANK.
1355  if (select.getOrderList() == null) {
1356  // push ORDER BY into existing select
1357  select.setOrderBy(orderBy.orderList);
1358  select.setOffset(orderBy.offset);
1359  select.setFetch(orderBy.fetch);
1360  return with;
1361  }
1362  }
1363  final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
1364  selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
1365  final SqlNodeList orderList;
1366  if (getInnerSelect(node) != null && isAggregate(getInnerSelect(node))) {
1367  orderList = SqlNode.clone(orderBy.orderList);
1368  // We assume that ORDER BY item does not have ASC etc.
1369  // We assume that ORDER BY item is present in SELECT list.
1370  for (int i = 0; i < orderList.size(); i++) {
1371  SqlNode sqlNode = orderList.get(i);
1372  SqlNodeList selectList2 = getInnerSelect(node).getSelectList();
1373  for (Ord<SqlNode> sel : Ord.zip(selectList2)) {
1374  if (stripAs(sel.e).equalsDeep(sqlNode, Litmus.IGNORE)) {
1375  orderList.set(i,
1376  SqlLiteral.createExactNumeric(Integer.toString(sel.i + 1),
1377  SqlParserPos.ZERO));
1378  }
1379  }
1380  }
1381  } else {
1382  orderList = orderBy.orderList;
1383  }
1384  return new SqlSelect(SqlParserPos.ZERO, null, selectList, orderBy.query,
1385  null, null, null, null, orderList, orderBy.offset,
1386  orderBy.fetch, null);
1387  }
1388 
1389  case EXPLICIT_TABLE: {
1390  // (TABLE t) is equivalent to (SELECT * FROM t)
1391  SqlCall call = (SqlCall) node;
1392  final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
1393  selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
1394  return new SqlSelect(SqlParserPos.ZERO, null, selectList, call.operand(0),
1395  null, null, null, null, null, null, null, null);
1396  }
1397 
1398  case DELETE: {
1399  SqlDelete call = (SqlDelete) node;
1400  SqlSelect select = createSourceSelectForDelete(call);
1401  call.setSourceSelect(select);
1402  break;
1403  }
1404 
1405  case UPDATE: {
1406  SqlUpdate call = (SqlUpdate) node;
1407  SqlSelect select = createSourceSelectForUpdate(call);
1408  call.setSourceSelect(select);
1409 
1410  // See if we're supposed to rewrite UPDATE to MERGE
1411  // (unless this is the UPDATE clause of a MERGE,
1412  // in which case leave it alone).
1413  if (!validatingSqlMerge) {
1414  SqlNode selfJoinSrcExpr =
1416  call.getTargetTable(),
1418  if (selfJoinSrcExpr != null) {
1419  node = rewriteUpdateToMerge(call, selfJoinSrcExpr);
1420  }
1421  }
1422  break;
1423  }
1424 
1425  case MERGE: {
1426  SqlMerge call = (SqlMerge) node;
1427  rewriteMerge(call);
1428  break;
1429  }
1430  }
1431  return node;
1432  }
1433 
1434  private SqlSelect getInnerSelect(SqlNode node) {
1435  for (;;) {
1436  if (node instanceof SqlSelect) {
1437  return (SqlSelect) node;
1438  } else if (node instanceof SqlOrderBy) {
1439  node = ((SqlOrderBy) node).query;
1440  } else if (node instanceof SqlWith) {
1441  node = ((SqlWith) node).body;
1442  } else {
1443  return null;
1444  }
1445  }
1446  }
1447 
1448  private void rewriteMerge(SqlMerge call) {
1449  SqlNodeList selectList;
1450  SqlUpdate updateStmt = call.getUpdateCall();
1451  if (updateStmt != null) {
1452  // if we have an update statement, just clone the select list
1453  // from the update statement's source since it's the same as
1454  // what we want for the select list of the merge source -- '*'
1455  // followed by the update set expressions
1456  selectList = SqlNode.clone(updateStmt.getSourceSelect().getSelectList());
1457  } else {
1458  // otherwise, just use select *
1459  selectList = new SqlNodeList(SqlParserPos.ZERO);
1460  selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
1461  }
1462  SqlNode targetTable = call.getTargetTable();
1463  if (call.getAlias() != null) {
1464  targetTable =
1465  SqlValidatorUtil.addAlias(
1466  targetTable,
1467  call.getAlias().getSimple());
1468  }
1469 
1470  // Provided there is an insert substatement, the source select for
1471  // the merge is a left outer join between the source in the USING
1472  // clause and the target table; otherwise, the join is just an
1473  // inner join. Need to clone the source table reference in order
1474  // for validation to work
1475  SqlNode sourceTableRef = call.getSourceTableRef();
1476  SqlInsert insertCall = call.getInsertCall();
1477  JoinType joinType = (insertCall == null) ? JoinType.INNER : JoinType.LEFT;
1478  final SqlNode leftJoinTerm = SqlNode.clone(sourceTableRef);
1479  SqlNode outerJoin =
1480  new SqlJoin(SqlParserPos.ZERO,
1481  leftJoinTerm,
1482  SqlLiteral.createBoolean(false, SqlParserPos.ZERO),
1483  joinType.symbol(SqlParserPos.ZERO),
1484  targetTable,
1485  JoinConditionType.ON.symbol(SqlParserPos.ZERO),
1486  call.getCondition());
1487  SqlSelect select =
1488  new SqlSelect(SqlParserPos.ZERO, null, selectList, outerJoin, null,
1489  null, null, null, null, null, null, null);
1490  call.setSourceSelect(select);
1491 
1492  // Source for the insert call is a select of the source table
1493  // reference with the select list being the value expressions;
1494  // note that the values clause has already been converted to a
1495  // select on the values row constructor; so we need to extract
1496  // that via the from clause on the select
1497  if (insertCall != null) {
1498  SqlCall valuesCall = (SqlCall) insertCall.getSource();
1499  SqlCall rowCall = valuesCall.operand(0);
1500  selectList =
1501  new SqlNodeList(
1502  rowCall.getOperandList(),
1503  SqlParserPos.ZERO);
1504  final SqlNode insertSource = SqlNode.clone(sourceTableRef);
1505  select =
1506  new SqlSelect(SqlParserPos.ZERO, null, selectList, insertSource, null,
1507  null, null, null, null, null, null, null);
1508  insertCall.setSource(select);
1509  }
1510  }
1511 
1512  private SqlNode rewriteUpdateToMerge(
1513  SqlUpdate updateCall,
1514  SqlNode selfJoinSrcExpr) {
1515  // Make sure target has an alias.
1516  if (updateCall.getAlias() == null) {
1517  updateCall.setAlias(
1518  new SqlIdentifier(UPDATE_TGT_ALIAS, SqlParserPos.ZERO));
1519  }
1520  SqlNode selfJoinTgtExpr =
1522  updateCall.getTargetTable(),
1523  updateCall.getAlias().getSimple());
1524  assert selfJoinTgtExpr != null;
1525 
1526  // Create join condition between source and target exprs,
1527  // creating a conjunction with the user-level WHERE
1528  // clause if one was supplied
1529  SqlNode condition = updateCall.getCondition();
1530  SqlNode selfJoinCond =
1531  SqlStdOperatorTable.EQUALS.createCall(
1532  SqlParserPos.ZERO,
1533  selfJoinSrcExpr,
1534  selfJoinTgtExpr);
1535  if (condition == null) {
1536  condition = selfJoinCond;
1537  } else {
1538  condition =
1539  SqlStdOperatorTable.AND.createCall(
1540  SqlParserPos.ZERO,
1541  selfJoinCond,
1542  condition);
1543  }
1544  SqlNode target =
1545  updateCall.getTargetTable().clone(SqlParserPos.ZERO);
1546 
1547  // For the source, we need to anonymize the fields, so
1548  // that for a statement like UPDATE T SET I = I + 1,
1549  // there's no ambiguity for the "I" in "I + 1";
1550  // this is OK because the source and target have
1551  // identical values due to the self-join.
1552  // Note that we anonymize the source rather than the
1553  // target because downstream, the optimizer rules
1554  // don't want to see any projection on top of the target.
1555  IdentifierNamespace ns =
1556  new IdentifierNamespace(this, target, null, null);
1557  RelDataType rowType = ns.getRowType();
1558  SqlNode source = updateCall.getTargetTable().clone(SqlParserPos.ZERO);
1559  final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
1560  int i = 1;
1561  for (RelDataTypeField field : rowType.getFieldList()) {
1562  SqlIdentifier col =
1563  new SqlIdentifier(
1564  field.getName(),
1565  SqlParserPos.ZERO);
1566  selectList.add(
1567  SqlValidatorUtil.addAlias(col, UPDATE_ANON_PREFIX + i));
1568  ++i;
1569  }
1570  source =
1571  new SqlSelect(SqlParserPos.ZERO, null, selectList, source, null, null,
1572  null, null, null, null, null, null);
1573  source = SqlValidatorUtil.addAlias(source, UPDATE_SRC_ALIAS);
1574  SqlMerge mergeCall =
1575  new SqlMerge(updateCall.getParserPosition(), target, condition, source,
1576  updateCall, null, null, updateCall.getAlias());
1577  rewriteMerge(mergeCall);
1578  return mergeCall;
1579  }
1580 
1595  protected SqlNode getSelfJoinExprForUpdate(
1596  SqlNode table,
1597  String alias) {
1598  return null;
1599  }
1600 
1608  protected SqlSelect createSourceSelectForUpdate(SqlUpdate call) {
1609  final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
1610  selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
1611  int ordinal = 0;
1612  for (SqlNode exp : call.getSourceExpressionList()) {
1613  // Force unique aliases to avoid a duplicate for Y with
1614  // SET X=Y
1615  String alias = SqlUtil.deriveAliasFromOrdinal(ordinal);
1616  selectList.add(SqlValidatorUtil.addAlias(exp, alias));
1617  ++ordinal;
1618  }
1619  SqlNode sourceTable = call.getTargetTable();
1620  if (call.getAlias() != null) {
1621  sourceTable =
1622  SqlValidatorUtil.addAlias(
1623  sourceTable,
1624  call.getAlias().getSimple());
1625  }
1626  return new SqlSelect(SqlParserPos.ZERO, null, selectList, sourceTable,
1627  call.getCondition(), null, null, null, null, null, null, null);
1628  }
1629 
1637  protected SqlSelect createSourceSelectForDelete(SqlDelete call) {
1638  final SqlNodeList selectList = new SqlNodeList(SqlParserPos.ZERO);
1639  selectList.add(SqlIdentifier.star(SqlParserPos.ZERO));
1640  SqlNode sourceTable = call.getTargetTable();
1641  if (call.getAlias() != null) {
1642  sourceTable =
1643  SqlValidatorUtil.addAlias(
1644  sourceTable,
1645  call.getAlias().getSimple());
1646  }
1647  return new SqlSelect(SqlParserPos.ZERO, null, selectList, sourceTable,
1648  call.getCondition(), null, null, null, null, null, null, null);
1649  }
1650 
1656  SqlCall values,
1657  SqlValidatorScope scope) {
1658  final List<SqlNode> rows = values.getOperandList();
1659  assert rows.size() >= 1;
1660  final List<RelDataType> rowTypes = new ArrayList<>();
1661  for (final SqlNode row : rows) {
1662  assert row.getKind() == SqlKind.ROW;
1663  SqlCall rowConstructor = (SqlCall) row;
1664 
1665  // REVIEW jvs 10-Sept-2003: Once we support single-row queries as
1666  // rows, need to infer aliases from there.
1667  final List<String> aliasList = new ArrayList<>();
1668  final List<RelDataType> typeList = new ArrayList<>();
1669  for (Ord<SqlNode> column : Ord.zip(rowConstructor.getOperandList())) {
1670  final String alias = deriveAlias(column.e, column.i);
1671  aliasList.add(alias);
1672  final RelDataType type = deriveType(scope, column.e);
1673  typeList.add(type);
1674  }
1675  rowTypes.add(typeFactory.createStructType(typeList, aliasList));
1676  }
1677  if (rows.size() == 1) {
1678  // TODO jvs 10-Oct-2005: get rid of this workaround once
1679  // leastRestrictive can handle all cases
1680  return rowTypes.get(0);
1681  }
1682  return typeFactory.leastRestrictive(rowTypes);
1683  }
1684 
1685  public RelDataType getValidatedNodeType(SqlNode node) {
1686  RelDataType type = getValidatedNodeTypeIfKnown(node);
1687  if (type == null) {
1688  throw Util.needToImplement(node);
1689  } else {
1690  return type;
1691  }
1692  }
1693 
1694  public RelDataType getValidatedNodeTypeIfKnown(SqlNode node) {
1695  final RelDataType type = nodeToTypeMap.get(node);
1696  if (type != null) {
1697  return type;
1698  }
1699  final SqlValidatorNamespace ns = getNamespace(node);
1700  if (ns != null) {
1701  return ns.getType();
1702  }
1703  final SqlNode original = originalExprs.get(node);
1704  if (original != null && original != node) {
1705  return getValidatedNodeType(original);
1706  }
1707  if (node instanceof SqlIdentifier) {
1708  return getCatalogReader().getNamedType((SqlIdentifier) node);
1709  }
1710  return null;
1711  }
1712 
1722  public final void setValidatedNodeType(SqlNode node, RelDataType type) {
1723  Objects.requireNonNull(type);
1724  Objects.requireNonNull(node);
1725  if (type.equals(unknownType)) {
1726  // don't set anything until we know what it is, and don't overwrite
1727  // a known type with the unknown type
1728  return;
1729  }
1730  nodeToTypeMap.put(node, type);
1731  }
1732 
1733  public void removeValidatedNodeType(SqlNode node) {
1734  nodeToTypeMap.remove(node);
1735  }
1736 
1737  @Nullable public SqlCall makeNullaryCall(SqlIdentifier id) {
1738  if (id.names.size() == 1 && !id.isComponentQuoted(0)) {
1739  final List<SqlOperator> list = new ArrayList<>();
1740  opTab.lookupOperatorOverloads(id, null, SqlSyntax.FUNCTION, list,
1741  catalogReader.nameMatcher());
1742  for (SqlOperator operator : list) {
1743  if (operator.getSyntax() == SqlSyntax.FUNCTION_ID) {
1744  // Even though this looks like an identifier, it is a
1745  // actually a call to a function. Construct a fake
1746  // call to this function, so we can use the regular
1747  // operator validation.
1748  return new SqlBasicCall(operator, SqlNode.EMPTY_ARRAY,
1749  id.getParserPosition(), true, null);
1750  }
1751  }
1752  }
1753  return null;
1754  }
1755 
1756  public RelDataType deriveType(
1757  SqlValidatorScope scope,
1758  SqlNode expr) {
1759  Objects.requireNonNull(scope);
1760  Objects.requireNonNull(expr);
1761 
1762  // if we already know the type, no need to re-derive
1763  RelDataType type = nodeToTypeMap.get(expr);
1764  if (type != null) {
1765  return type;
1766  }
1767  final SqlValidatorNamespace ns = getNamespace(expr);
1768  if (ns != null) {
1769  return ns.getType();
1770  }
1771  type = deriveTypeImpl(scope, expr);
1772  Preconditions.checkArgument(
1773  type != null,
1774  "SqlValidator.deriveTypeInternal returned null");
1775  setValidatedNodeType(expr, type);
1776  return type;
1777  }
1778 
1782  RelDataType deriveTypeImpl(
1783  SqlValidatorScope scope,
1784  SqlNode operand) {
1785  DeriveTypeVisitor v = new DeriveTypeVisitor(scope);
1786  final RelDataType type = operand.accept(v);
1787  return Objects.requireNonNull(scope.nullifyType(operand, type));
1788  }
1789 
1790  public RelDataType deriveConstructorType(
1791  SqlValidatorScope scope,
1792  SqlCall call,
1793  SqlFunction unresolvedConstructor,
1794  SqlFunction resolvedConstructor,
1795  List<RelDataType> argTypes) {
1796  SqlIdentifier sqlIdentifier = unresolvedConstructor.getSqlIdentifier();
1797  assert sqlIdentifier != null;
1798  RelDataType type = catalogReader.getNamedType(sqlIdentifier);
1799  if (type == null) {
1800  // TODO jvs 12-Feb-2005: proper type name formatting
1801  throw newValidationError(sqlIdentifier,
1802  RESOURCE.unknownDatatypeName(sqlIdentifier.toString()));
1803  }
1804 
1805  if (resolvedConstructor == null) {
1806  if (call.operandCount() > 0) {
1807  // This is not a default constructor invocation, and
1808  // no user-defined constructor could be found
1809  throw handleUnresolvedFunction(call, unresolvedConstructor, argTypes,
1810  null);
1811  }
1812  } else {
1813  SqlCall testCall =
1814  resolvedConstructor.createCall(
1815  call.getParserPosition(),
1816  call.getOperandList());
1817  RelDataType returnType =
1818  resolvedConstructor.validateOperands(
1819  this,
1820  scope,
1821  testCall);
1822  assert type == returnType;
1823  }
1824 
1825  if (config.identifierExpansion()) {
1826  if (resolvedConstructor != null) {
1827  ((SqlBasicCall) call).setOperator(resolvedConstructor);
1828  } else {
1829  // fake a fully-qualified call to the default constructor
1830  ((SqlBasicCall) call).setOperator(
1831  new SqlFunction(
1832  type.getSqlIdentifier(),
1833  ReturnTypes.explicit(type),
1834  null,
1835  null,
1836  null,
1837  SqlFunctionCategory.USER_DEFINED_CONSTRUCTOR));
1838  }
1839  }
1840  return type;
1841  }
1842 
1843  public CalciteException handleUnresolvedFunction(SqlCall call,
1844  SqlFunction unresolvedFunction, List<RelDataType> argTypes,
1845  List<String> argNames) {
1846  // For builtins, we can give a better error message
1847  final List<SqlOperator> overloads = new ArrayList<>();
1848  opTab.lookupOperatorOverloads(unresolvedFunction.getNameAsId(), null,
1849  SqlSyntax.FUNCTION, overloads, catalogReader.nameMatcher());
1850  if (overloads.size() == 1) {
1851  SqlFunction fun = (SqlFunction) overloads.get(0);
1852  if ((fun.getSqlIdentifier() == null)
1853  && (fun.getSyntax() != SqlSyntax.FUNCTION_ID)) {
1854  final int expectedArgCount =
1855  fun.getOperandCountRange().getMin();
1856  throw newValidationError(call,
1857  RESOURCE.invalidArgCount(call.getOperator().getName(),
1858  expectedArgCount));
1859  }
1860  }
1861 
1862  AssignableOperandTypeChecker typeChecking =
1863  new AssignableOperandTypeChecker(argTypes, argNames);
1864  String signature =
1865  typeChecking.getAllowedSignatures(
1866  unresolvedFunction,
1867  unresolvedFunction.getName());
1868  throw newValidationError(call,
1869  RESOURCE.validatorUnknownFunction(signature));
1870  }
1871 
1872  protected void inferUnknownTypes(
1873  @Nonnull RelDataType inferredType,
1874  @Nonnull SqlValidatorScope scope,
1875  @Nonnull SqlNode node) {
1876  Objects.requireNonNull(inferredType);
1877  Objects.requireNonNull(scope);
1878  Objects.requireNonNull(node);
1879  final SqlValidatorScope newScope = scopes.get(node);
1880  if (newScope != null) {
1881  scope = newScope;
1882  }
1883  boolean isNullLiteral = SqlUtil.isNullLiteral(node, false);
1884  if ((node instanceof SqlDynamicParam) || isNullLiteral) {
1885  if (inferredType.equals(unknownType)) {
1886  if (isNullLiteral) {
1887  if (config.typeCoercionEnabled()) {
1888  // derive type of null literal
1889  deriveType(scope, node);
1890  return;
1891  } else {
1892  throw newValidationError(node, RESOURCE.nullIllegal());
1893  }
1894  } else {
1895  throw newValidationError(node, RESOURCE.dynamicParamIllegal());
1896  }
1897  }
1898 
1899  // REVIEW: should dynamic parameter types always be nullable?
1900  RelDataType newInferredType =
1901  typeFactory.createTypeWithNullability(inferredType, true);
1902  if (SqlTypeUtil.inCharFamily(inferredType)) {
1903  newInferredType =
1904  typeFactory.createTypeWithCharsetAndCollation(
1905  newInferredType,
1906  inferredType.getCharset(),
1907  inferredType.getCollation());
1908  }
1909  setValidatedNodeType(node, newInferredType);
1910  } else if (node instanceof SqlNodeList) {
1911  SqlNodeList nodeList = (SqlNodeList) node;
1912  if (inferredType.isStruct()) {
1913  if (inferredType.getFieldCount() != nodeList.size()) {
1914  // this can happen when we're validating an INSERT
1915  // where the source and target degrees are different;
1916  // bust out, and the error will be detected higher up
1917  return;
1918  }
1919  }
1920  int i = 0;
1921  for (SqlNode child : nodeList) {
1922  RelDataType type;
1923  if (inferredType.isStruct()) {
1924  type = inferredType.getFieldList().get(i).getType();
1925  ++i;
1926  } else {
1927  type = inferredType;
1928  }
1929  inferUnknownTypes(type, scope, child);
1930  }
1931  } else if (node instanceof SqlCase) {
1932  final SqlCase caseCall = (SqlCase) node;
1933 
1934  final RelDataType whenType =
1935  caseCall.getValueOperand() == null ? booleanType : unknownType;
1936  for (SqlNode sqlNode : caseCall.getWhenOperands().getList()) {
1937  inferUnknownTypes(whenType, scope, sqlNode);
1938  }
1939  RelDataType returnType = deriveType(scope, node);
1940  for (SqlNode sqlNode : caseCall.getThenOperands().getList()) {
1941  inferUnknownTypes(returnType, scope, sqlNode);
1942  }
1943 
1944  if (!SqlUtil.isNullLiteral(caseCall.getElseOperand(), false)) {
1946  returnType,
1947  scope,
1948  caseCall.getElseOperand());
1949  } else {
1950  setValidatedNodeType(caseCall.getElseOperand(), returnType);
1951  }
1952  } else if (node.getKind() == SqlKind.AS) {
1953  // For AS operator, only infer the operand not the alias
1954  inferUnknownTypes(inferredType, scope, ((SqlCall) node).operand(0));
1955  } else if (node instanceof SqlCall) {
1956  final SqlCall call = (SqlCall) node;
1957  final SqlOperandTypeInference operandTypeInference =
1958  call.getOperator().getOperandTypeInference();
1959  final SqlCallBinding callBinding = new SqlCallBinding(this, scope, call);
1960  final List<SqlNode> operands = callBinding.operands();
1961  final RelDataType[] operandTypes = new RelDataType[operands.size()];
1962  Arrays.fill(operandTypes, unknownType);
1963  // TODO: eventually should assert(operandTypeInference != null)
1964  // instead; for now just eat it
1965  if (operandTypeInference != null) {
1966  operandTypeInference.inferOperandTypes(
1967  callBinding,
1968  inferredType,
1969  operandTypes);
1970  }
1971  for (int i = 0; i < operands.size(); ++i) {
1972  final SqlNode operand = operands.get(i);
1973  if (operand != null) {
1974  inferUnknownTypes(operandTypes[i], scope, operand);
1975  }
1976  }
1977  }
1978  }
1979 
1984  protected void addToSelectList(
1985  List<SqlNode> list,
1986  Set<String> aliases,
1987  List<Map.Entry<String, RelDataType>> fieldList,
1988  SqlNode exp,
1989  SelectScope scope,
1990  final boolean includeSystemVars) {
1991  String alias = SqlValidatorUtil.getAlias(exp, -1);
1992  String uniqueAlias =
1993  SqlValidatorUtil.uniquify(
1994  alias, aliases, SqlValidatorUtil.EXPR_SUGGESTER);
1995  if (!Objects.equals(alias, uniqueAlias)) {
1996  exp = SqlValidatorUtil.addAlias(exp, uniqueAlias);
1997  }
1998  fieldList.add(Pair.of(uniqueAlias, deriveType(scope, exp)));
1999  list.add(exp);
2000  }
2001 
2002  public String deriveAlias(
2003  SqlNode node,
2004  int ordinal) {
2005  return SqlValidatorUtil.getAlias(node, ordinal);
2006  }
2007 
2008  protected boolean shouldAllowIntermediateOrderBy() {
2009  return true;
2010  }
2011 
2013  SqlValidatorScope parentScope,
2014  SqlValidatorScope usingScope,
2015  SqlMatchRecognize call,
2016  SqlNode enclosingNode,
2017  String alias,
2018  boolean forceNullable) {
2019 
2020  final MatchRecognizeNamespace matchRecognizeNamespace =
2021  createMatchRecognizeNameSpace(call, enclosingNode);
2022  registerNamespace(usingScope, alias, matchRecognizeNamespace, forceNullable);
2023 
2024  final MatchRecognizeScope matchRecognizeScope =
2025  new MatchRecognizeScope(parentScope, call);
2026  scopes.put(call, matchRecognizeScope);
2027 
2028  // parse input query
2029  SqlNode expr = call.getTableRef();
2030  SqlNode newExpr = registerFrom(usingScope, matchRecognizeScope, true, expr,
2031  expr, null, null, forceNullable, false);
2032  if (expr != newExpr) {
2033  call.setOperand(0, newExpr);
2034  }
2035  }
2036 
2037  protected MatchRecognizeNamespace createMatchRecognizeNameSpace(
2038  SqlMatchRecognize call,
2039  SqlNode enclosingNode) {
2040  return new MatchRecognizeNamespace(this, call, enclosingNode);
2041  }
2042 
2054  protected void registerNamespace(
2055  SqlValidatorScope usingScope,
2056  String alias,
2057  SqlValidatorNamespace ns,
2058  boolean forceNullable) {
2059  namespaces.put(ns.getNode(), ns);
2060  if (usingScope != null) {
2061  usingScope.addChild(ns, alias, forceNullable);
2062  }
2063  }
2064 
2096  private SqlNode registerFrom(
2097  SqlValidatorScope parentScope,
2098  SqlValidatorScope usingScope,
2099  boolean register,
2100  final SqlNode node,
2101  SqlNode enclosingNode,
2102  String alias,
2103  SqlNodeList extendList,
2104  boolean forceNullable,
2105  final boolean lateral) {
2106  final SqlKind kind = node.getKind();
2107 
2108  SqlNode expr;
2109  SqlNode newExpr;
2110 
2111  // Add an alias if necessary.
2112  SqlNode newNode = node;
2113  if (alias == null) {
2114  switch (kind) {
2115  case IDENTIFIER:
2116  case OVER:
2117  alias = deriveAlias(node, -1);
2118  if (alias == null) {
2119  alias = deriveAlias(node, nextGeneratedId++);
2120  }
2121  if (config.identifierExpansion()) {
2122  newNode = SqlValidatorUtil.addAlias(node, alias);
2123  }
2124  break;
2125 
2126  case SELECT:
2127  case UNION:
2128  case INTERSECT:
2129  case EXCEPT:
2130  case VALUES:
2131  case UNNEST:
2132  case OTHER_FUNCTION:
2133  case COLLECTION_TABLE:
2134  case MATCH_RECOGNIZE:
2135 
2136  // give this anonymous construct a name since later
2137  // query processing stages rely on it
2138  alias = deriveAlias(node, nextGeneratedId++);
2139  if (config.identifierExpansion()) {
2140  // Since we're expanding identifiers, we should make the
2141  // aliases explicit too, otherwise the expanded query
2142  // will not be consistent if we convert back to SQL, e.g.
2143  // "select EXPR$1.EXPR$2 from values (1)".
2144  newNode = SqlValidatorUtil.addAlias(node, alias);
2145  }
2146  break;
2147  }
2148  }
2149 
2150  if (lateral) {
2151  SqlValidatorScope s = usingScope;
2152  while (s instanceof JoinScope) {
2153  s = ((JoinScope) s).getUsingScope();
2154  }
2155  final SqlNode node2 = s != null ? s.getNode() : node;
2156  final TableScope tableScope = new TableScope(parentScope, node2);
2157  if (usingScope instanceof ListScope) {
2158  for (ScopeChild child : ((ListScope) usingScope).children) {
2159  tableScope.addChild(child.namespace, child.name, child.nullable);
2160  }
2161  }
2162  parentScope = tableScope;
2163  }
2164 
2165  SqlCall call;
2166  SqlNode operand;
2167  SqlNode newOperand;
2168 
2169  switch (kind) {
2170  case AS:
2171  call = (SqlCall) node;
2172  if (alias == null) {
2173  alias = call.operand(1).toString();
2174  }
2175  final boolean needAlias = call.operandCount() > 2;
2176  expr = call.operand(0);
2177  newExpr =
2178  registerFrom(
2179  parentScope,
2180  usingScope,
2181  !needAlias,
2182  expr,
2183  enclosingNode,
2184  alias,
2185  extendList,
2186  forceNullable,
2187  lateral);
2188  if (newExpr != expr) {
2189  call.setOperand(0, newExpr);
2190  }
2191 
2192  // If alias has a column list, introduce a namespace to translate
2193  // column names. We skipped registering it just now.
2194  if (needAlias) {
2196  usingScope,
2197  alias,
2198  new AliasNamespace(this, call, enclosingNode),
2199  forceNullable);
2200  }
2201  return node;
2202  case MATCH_RECOGNIZE:
2203  registerMatchRecognize(parentScope, usingScope,
2204  (SqlMatchRecognize) node, enclosingNode, alias, forceNullable);
2205  return node;
2206  case TABLESAMPLE:
2207  call = (SqlCall) node;
2208  expr = call.operand(0);
2209  newExpr =
2210  registerFrom(
2211  parentScope,
2212  usingScope,
2213  true,
2214  expr,
2215  enclosingNode,
2216  alias,
2217  extendList,
2218  forceNullable,
2219  lateral);
2220  if (newExpr != expr) {
2221  call.setOperand(0, newExpr);
2222  }
2223  return node;
2224 
2225  case JOIN:
2226  final SqlJoin join = (SqlJoin) node;
2227  final JoinScope joinScope =
2228  new JoinScope(parentScope, usingScope, join);
2229  scopes.put(join, joinScope);
2230  final SqlNode left = join.getLeft();
2231  final SqlNode right = join.getRight();
2232  boolean forceLeftNullable = forceNullable;
2233  boolean forceRightNullable = forceNullable;
2234  switch (join.getJoinType()) {
2235  case LEFT:
2236  forceRightNullable = true;
2237  break;
2238  case RIGHT:
2239  forceLeftNullable = true;
2240  break;
2241  case FULL:
2242  forceLeftNullable = true;
2243  forceRightNullable = true;
2244  break;
2245  }
2246  final SqlNode newLeft =
2247  registerFrom(
2248  parentScope,
2249  joinScope,
2250  true,
2251  left,
2252  left,
2253  null,
2254  null,
2255  forceLeftNullable,
2256  lateral);
2257  if (newLeft != left) {
2258  join.setLeft(newLeft);
2259  }
2260  final SqlNode newRight =
2261  registerFrom(
2262  parentScope,
2263  joinScope,
2264  true,
2265  right,
2266  right,
2267  null,
2268  null,
2269  forceRightNullable,
2270  lateral);
2271  if (newRight != right) {
2272  join.setRight(newRight);
2273  }
2274  registerSubQueries(joinScope, join.getCondition());
2275  final JoinNamespace joinNamespace = new JoinNamespace(this, join);
2276  registerNamespace(null, null, joinNamespace, forceNullable);
2277  return join;
2278 
2279  case IDENTIFIER:
2280  final SqlIdentifier id = (SqlIdentifier) node;
2281  final IdentifierNamespace newNs =
2282  new IdentifierNamespace(
2283  this, id, extendList, enclosingNode,
2284  parentScope);
2285  registerNamespace(register ? usingScope : null, alias, newNs,
2286  forceNullable);
2287  if (tableScope == null) {
2288  tableScope = new TableScope(parentScope, node);
2289  }
2290  tableScope.addChild(newNs, alias, forceNullable);
2291  if (extendList != null && extendList.size() != 0) {
2292  return enclosingNode;
2293  }
2294  return newNode;
2295 
2296  case LATERAL:
2297  return registerFrom(
2298  parentScope,
2299  usingScope,
2300  register,
2301  ((SqlCall) node).operand(0),
2302  enclosingNode,
2303  alias,
2304  extendList,
2305  forceNullable,
2306  true);
2307 
2308  case COLLECTION_TABLE:
2309  call = (SqlCall) node;
2310  operand = call.operand(0);
2311  newOperand =
2312  registerFrom(
2313  parentScope,
2314  usingScope,
2315  register,
2316  operand,
2317  enclosingNode,
2318  alias,
2319  extendList,
2320  forceNullable, lateral);
2321  if (newOperand != operand) {
2322  call.setOperand(0, newOperand);
2323  }
2324  // If the operator is SqlWindowTableFunction, restricts the scope as
2325  // its first operand's (the table) scope.
2326  if (operand instanceof SqlBasicCall) {
2327  final SqlBasicCall call1 = (SqlBasicCall) operand;
2328  final SqlOperator op = call1.getOperator();
2329  if (op instanceof SqlWindowTableFunction
2330  && call1.operand(0).getKind() == SqlKind.SELECT) {
2331  scopes.put(node, getSelectScope(call1.operand(0)));
2332  return newNode;
2333  }
2334  }
2335  // Put the usingScope which can be a JoinScope
2336  // or a SelectScope, in order to see the left items
2337  // of the JOIN tree.
2338  scopes.put(node, usingScope);
2339  return newNode;
2340 
2341  case UNNEST:
2342  if (!lateral) {
2343  return registerFrom(parentScope, usingScope, register, node,
2344  enclosingNode, alias, extendList, forceNullable, true);
2345  }
2346  // fall through
2347  case SELECT:
2348  case UNION:
2349  case INTERSECT:
2350  case EXCEPT:
2351  case VALUES:
2352  case WITH:
2353  case OTHER_FUNCTION:
2354  if (alias == null) {
2355  alias = deriveAlias(node, nextGeneratedId++);
2356  }
2357  registerQuery(
2358  parentScope,
2359  register ? usingScope : null,
2360  node,
2361  enclosingNode,
2362  alias,
2363  forceNullable);
2364  return newNode;
2365 
2366  case OVER:
2367  if (!shouldAllowOverRelation()) {
2368  throw Util.unexpected(kind);
2369  }
2370  call = (SqlCall) node;
2371  final OverScope overScope = new OverScope(usingScope, call);
2372  scopes.put(call, overScope);
2373  operand = call.operand(0);
2374  newOperand =
2375  registerFrom(
2376  parentScope,
2377  overScope,
2378  true,
2379  operand,
2380  enclosingNode,
2381  alias,
2382  extendList,
2383  forceNullable,
2384  lateral);
2385  if (newOperand != operand) {
2386  call.setOperand(0, newOperand);
2387  }
2388 
2389  for (ScopeChild child : overScope.children) {
2390  registerNamespace(register ? usingScope : null, child.name,
2391  child.namespace, forceNullable);
2392  }
2393 
2394  return newNode;
2395 
2396  case TABLE_REF:
2397  call = (SqlCall) node;
2398  registerFrom(parentScope,
2399  usingScope,
2400  register,
2401  call.operand(0),
2402  enclosingNode,
2403  alias,
2404  extendList,
2405  forceNullable,
2406  lateral);
2407  if (extendList != null && extendList.size() != 0) {
2408  return enclosingNode;
2409  }
2410  return newNode;
2411 
2412  case EXTEND:
2413  final SqlCall extend = (SqlCall) node;
2414  return registerFrom(parentScope,
2415  usingScope,
2416  true,
2417  extend.getOperandList().get(0),
2418  extend,
2419  alias,
2420  (SqlNodeList) extend.getOperandList().get(1),
2421  forceNullable,
2422  lateral);
2423 
2424  case SNAPSHOT:
2425  call = (SqlCall) node;
2426  operand = call.operand(0);
2427  newOperand = registerFrom(
2428  parentScope,
2429  usingScope,
2430  register,
2431  operand,
2432  enclosingNode,
2433  alias,
2434  extendList,
2435  forceNullable,
2436  lateral);
2437  if (newOperand != operand) {
2438  call.setOperand(0, newOperand);
2439  }
2440  // Put the usingScope which can be a JoinScope
2441  // or a SelectScope, in order to see the left items
2442  // of the JOIN tree.
2443  scopes.put(node, usingScope);
2444  return newNode;
2445 
2446  default:
2447  throw Util.unexpected(kind);
2448  }
2449  }
2450 
2451  protected boolean shouldAllowOverRelation() {
2452  return false;
2453  }
2454 
2463  protected SelectNamespace createSelectNamespace(
2464  SqlSelect select,
2465  SqlNode enclosingNode) {
2466  return new SelectNamespace(this, select, enclosingNode);
2467  }
2468 
2478  protected SetopNamespace createSetopNamespace(
2479  SqlCall call,
2480  SqlNode enclosingNode) {
2481  return new SetopNamespace(this, call, enclosingNode);
2482  }
2483 
2494  private void registerQuery(
2495  SqlValidatorScope parentScope,
2496  SqlValidatorScope usingScope,
2497  SqlNode node,
2498  SqlNode enclosingNode,
2499  String alias,
2500  boolean forceNullable) {
2501  Preconditions.checkArgument(usingScope == null || alias != null);
2502  registerQuery(
2503  parentScope,
2504  usingScope,
2505  node,
2506  enclosingNode,
2507  alias,
2508  forceNullable,
2509  true);
2510  }
2511 
2524  private void registerQuery(
2525  SqlValidatorScope parentScope,
2526  SqlValidatorScope usingScope,
2527  SqlNode node,
2528  SqlNode enclosingNode,
2529  String alias,
2530  boolean forceNullable,
2531  boolean checkUpdate) {
2532  Objects.requireNonNull(node);
2533  Objects.requireNonNull(enclosingNode);
2534  Preconditions.checkArgument(usingScope == null || alias != null);
2535 
2536  SqlCall call;
2537  List<SqlNode> operands;
2538  switch (node.getKind()) {
2539  case SELECT:
2540  final SqlSelect select = (SqlSelect) node;
2541  final SelectNamespace selectNs =
2542  createSelectNamespace(select, enclosingNode);
2543  registerNamespace(usingScope, alias, selectNs, forceNullable);
2544  final SqlValidatorScope windowParentScope =
2545  (usingScope != null) ? usingScope : parentScope;
2546  SelectScope selectScope =
2547  new SelectScope(parentScope, windowParentScope, select);
2548  scopes.put(select, selectScope);
2549 
2550  // Start by registering the WHERE clause
2551  clauseScopes.put(IdPair.of(select, Clause.WHERE), selectScope);
2553  selectScope,
2554  select,
2555  SqlSelect.WHERE_OPERAND);
2556 
2557  // Register FROM with the inherited scope 'parentScope', not
2558  // 'selectScope', otherwise tables in the FROM clause would be
2559  // able to see each other.
2560  final SqlNode from = select.getFrom();
2561  if (from != null) {
2562  final SqlNode newFrom =
2563  registerFrom(
2564  parentScope,
2565  selectScope,
2566  true,
2567  from,
2568  from,
2569  null,
2570  null,
2571  false,
2572  false);
2573  if (newFrom != from) {
2574  select.setFrom(newFrom);
2575  }
2576  }
2577 
2578  // If this is an aggregating query, the SELECT list and HAVING
2579  // clause use a different scope, where you can only reference
2580  // columns which are in the GROUP BY clause.
2581  SqlValidatorScope aggScope = selectScope;
2582  if (isAggregate(select)) {
2583  aggScope =
2584  new AggregatingSelectScope(selectScope, select, false);
2585  clauseScopes.put(IdPair.of(select, Clause.SELECT), aggScope);
2586  } else {
2587  clauseScopes.put(IdPair.of(select, Clause.SELECT), selectScope);
2588  }
2589  if (select.getGroup() != null) {
2590  GroupByScope groupByScope =
2591  new GroupByScope(selectScope, select.getGroup(), select);
2592  clauseScopes.put(IdPair.of(select, Clause.GROUP_BY), groupByScope);
2593  registerSubQueries(groupByScope, select.getGroup());
2594  }
2596  aggScope,
2597  select,
2598  SqlSelect.HAVING_OPERAND);
2599  registerSubQueries(aggScope, select.getSelectList());
2600  final SqlNodeList orderList = select.getOrderList();
2601  if (orderList != null) {
2602  // If the query is 'SELECT DISTINCT', restrict the columns
2603  // available to the ORDER BY clause.
2604  if (select.isDistinct()) {
2605  aggScope =
2606  new AggregatingSelectScope(selectScope, select, true);
2607  }
2608  OrderByScope orderScope =
2609  new OrderByScope(aggScope, orderList, select);
2610  clauseScopes.put(IdPair.of(select, Clause.ORDER), orderScope);
2611  registerSubQueries(orderScope, orderList);
2612 
2613  if (!isAggregate(select)) {
2614  // Since this is not an aggregating query,
2615  // there cannot be any aggregates in the ORDER BY clause.
2616  SqlNode agg = aggFinder.findAgg(orderList);
2617  if (agg != null) {
2618  throw newValidationError(agg, RESOURCE.aggregateIllegalInOrderBy());
2619  }
2620  }
2621  }
2622  break;
2623 
2624  case INTERSECT:
2625  validateFeature(RESOURCE.sQLFeature_F302(), node.getParserPosition());
2626  registerSetop(
2627  parentScope,
2628  usingScope,
2629  node,
2630  node,
2631  alias,
2632  forceNullable);
2633  break;
2634 
2635  case EXCEPT:
2636  validateFeature(RESOURCE.sQLFeature_E071_03(), node.getParserPosition());
2637  registerSetop(
2638  parentScope,
2639  usingScope,
2640  node,
2641  node,
2642  alias,
2643  forceNullable);
2644  break;
2645 
2646  case UNION:
2647  registerSetop(
2648  parentScope,
2649  usingScope,
2650  node,
2651  node,
2652  alias,
2653  forceNullable);
2654  break;
2655 
2656  case WITH:
2657  registerWith(parentScope, usingScope, (SqlWith) node, enclosingNode,
2658  alias, forceNullable, checkUpdate);
2659  break;
2660 
2661  case VALUES:
2662  call = (SqlCall) node;
2663  scopes.put(call, parentScope);
2664  final TableConstructorNamespace tableConstructorNamespace =
2665  new TableConstructorNamespace(
2666  this,
2667  call,
2668  parentScope,
2669  enclosingNode);
2671  usingScope,
2672  alias,
2673  tableConstructorNamespace,
2674  forceNullable);
2675  operands = call.getOperandList();
2676  for (int i = 0; i < operands.size(); ++i) {
2677  assert operands.get(i).getKind() == SqlKind.ROW;
2678 
2679  // FIXME jvs 9-Feb-2005: Correlation should
2680  // be illegal in these sub-queries. Same goes for
2681  // any non-lateral SELECT in the FROM list.
2682  registerOperandSubQueries(parentScope, call, i);
2683  }
2684  break;
2685 
2686  case INSERT:
2687  SqlInsert insertCall = (SqlInsert) node;
2688  InsertNamespace insertNs =
2689  new InsertNamespace(
2690  this,
2691  insertCall,
2692  enclosingNode,
2693  parentScope);
2694  registerNamespace(usingScope, null, insertNs, forceNullable);
2695  registerQuery(
2696  parentScope,
2697  usingScope,
2698  insertCall.getSource(),
2699  enclosingNode,
2700  null,
2701  false);
2702  break;
2703 
2704  case DELETE:
2705  SqlDelete deleteCall = (SqlDelete) node;
2706  DeleteNamespace deleteNs =
2707  new DeleteNamespace(
2708  this,
2709  deleteCall,
2710  enclosingNode,
2711  parentScope);
2712  registerNamespace(usingScope, null, deleteNs, forceNullable);
2713  registerQuery(
2714  parentScope,
2715  usingScope,
2716  deleteCall.getSourceSelect(),
2717  enclosingNode,
2718  null,
2719  false);
2720  break;
2721 
2722  case UPDATE:
2723  if (checkUpdate) {
2724  validateFeature(RESOURCE.sQLFeature_E101_03(),
2725  node.getParserPosition());
2726  }
2727  SqlUpdate updateCall = (SqlUpdate) node;
2728  UpdateNamespace updateNs =
2729  new UpdateNamespace(
2730  this,
2731  updateCall,
2732  enclosingNode,
2733  parentScope);
2734  registerNamespace(usingScope, null, updateNs, forceNullable);
2735  registerQuery(
2736  parentScope,
2737  usingScope,
2738  updateCall.getSourceSelect(),
2739  enclosingNode,
2740  null,
2741  false);
2742  break;
2743 
2744  case MERGE:
2745  validateFeature(RESOURCE.sQLFeature_F312(), node.getParserPosition());
2746  SqlMerge mergeCall = (SqlMerge) node;
2747  MergeNamespace mergeNs =
2748  new MergeNamespace(
2749  this,
2750  mergeCall,
2751  enclosingNode,
2752  parentScope);
2753  registerNamespace(usingScope, null, mergeNs, forceNullable);
2754  registerQuery(
2755  parentScope,
2756  usingScope,
2757  mergeCall.getSourceSelect(),
2758  enclosingNode,
2759  null,
2760  false);
2761 
2762  // update call can reference either the source table reference
2763  // or the target table, so set its parent scope to the merge's
2764  // source select; when validating the update, skip the feature
2765  // validation check
2766  if (mergeCall.getUpdateCall() != null) {
2767  registerQuery(
2768  clauseScopes.get(IdPair.of(mergeCall.getSourceSelect(), Clause.WHERE)),
2769  null,
2770  mergeCall.getUpdateCall(),
2771  enclosingNode,
2772  null,
2773  false,
2774  false);
2775  }
2776  if (mergeCall.getInsertCall() != null) {
2777  registerQuery(
2778  parentScope,
2779  null,
2780  mergeCall.getInsertCall(),
2781  enclosingNode,
2782  null,
2783  false);
2784  }
2785  break;
2786 
2787  case UNNEST:
2788  call = (SqlCall) node;
2789  final UnnestNamespace unnestNs =
2790  new UnnestNamespace(this, call, parentScope, enclosingNode);
2792  usingScope,
2793  alias,
2794  unnestNs,
2795  forceNullable);
2796  registerOperandSubQueries(parentScope, call, 0);
2797  scopes.put(node, parentScope);
2798  break;
2799  case OTHER_FUNCTION:
2800  call = (SqlCall) node;
2801  ProcedureNamespace procNs =
2802  new ProcedureNamespace(
2803  this,
2804  parentScope,
2805  call,
2806  enclosingNode);
2808  usingScope,
2809  alias,
2810  procNs,
2811  forceNullable);
2812  registerSubQueries(parentScope, call);
2813  break;
2814 
2815  case MULTISET_QUERY_CONSTRUCTOR:
2816  case MULTISET_VALUE_CONSTRUCTOR:
2817  validateFeature(RESOURCE.sQLFeature_S271(), node.getParserPosition());
2818  call = (SqlCall) node;
2819  CollectScope cs = new CollectScope(parentScope, usingScope, call);
2820  final CollectNamespace tableConstructorNs =
2821  new CollectNamespace(call, cs, enclosingNode);
2822  final String alias2 = deriveAlias(node, nextGeneratedId++);
2824  usingScope,
2825  alias2,
2826  tableConstructorNs,
2827  forceNullable);
2828  operands = call.getOperandList();
2829  for (int i = 0; i < operands.size(); i++) {
2830  registerOperandSubQueries(parentScope, call, i);
2831  }
2832  break;
2833 
2834  default:
2835  throw Util.unexpected(node.getKind());
2836  }
2837  }
2838 
2839  private void registerSetop(
2840  SqlValidatorScope parentScope,
2841  SqlValidatorScope usingScope,
2842  SqlNode node,
2843  SqlNode enclosingNode,
2844  String alias,
2845  boolean forceNullable) {
2846  SqlCall call = (SqlCall) node;
2847  final SetopNamespace setopNamespace =
2848  createSetopNamespace(call, enclosingNode);
2849  registerNamespace(usingScope, alias, setopNamespace, forceNullable);
2850 
2851  // A setop is in the same scope as its parent.
2852  scopes.put(call, parentScope);
2853  for (SqlNode operand : call.getOperandList()) {
2854  registerQuery(
2855  parentScope,
2856  null,
2857  operand,
2858  operand,
2859  null,
2860  false);
2861  }
2862  }
2863 
2864  private void registerWith(
2865  SqlValidatorScope parentScope,
2866  SqlValidatorScope usingScope,
2867  SqlWith with,
2868  SqlNode enclosingNode,
2869  String alias,
2870  boolean forceNullable,
2871  boolean checkUpdate) {
2872  final WithNamespace withNamespace =
2873  new WithNamespace(this, with, enclosingNode);
2874  registerNamespace(usingScope, alias, withNamespace, forceNullable);
2875 
2876  SqlValidatorScope scope = parentScope;
2877  for (SqlNode withItem_ : with.withList) {
2878  final SqlWithItem withItem = (SqlWithItem) withItem_;
2879  final WithScope withScope = new WithScope(scope, withItem);
2880  scopes.put(withItem, withScope);
2881 
2882  registerQuery(scope, null, withItem.query, with,
2883  withItem.name.getSimple(), false);
2884  registerNamespace(null, alias,
2885  new WithItemNamespace(this, withItem, enclosingNode),
2886  false);
2887  scope = withScope;
2888  }
2889 
2890  registerQuery(scope, null, with.body, enclosingNode, alias, forceNullable,
2891  checkUpdate);
2892  }
2893 
2894  public boolean isAggregate(SqlSelect select) {
2895  if (getAggregate(select) != null) {
2896  return true;
2897  }
2898  // Also when nested window aggregates are present
2899  for (SqlCall call : overFinder.findAll(select.getSelectList())) {
2900  assert call.getKind() == SqlKind.OVER;
2901  if (isNestedAggregateWindow(call.operand(0))) {
2902  return true;
2903  }
2904  if (isOverAggregateWindow(call.operand(1))) {
2905  return true;
2906  }
2907  }
2908  return false;
2909  }
2910 
2911  protected boolean isNestedAggregateWindow(SqlNode node) {
2912  AggFinder nestedAggFinder =
2913  new AggFinder(opTab, false, false, false, aggFinder,
2914  catalogReader.nameMatcher());
2915  return nestedAggFinder.findAgg(node) != null;
2916  }
2917 
2918  protected boolean isOverAggregateWindow(SqlNode node) {
2919  return aggFinder.findAgg(node) != null;
2920  }
2921 
2928  protected SqlNode getAggregate(SqlSelect select) {
2929  SqlNode node = select.getGroup();
2930  if (node != null) {
2931  return node;
2932  }
2933  node = select.getHaving();
2934  if (node != null) {
2935  return node;
2936  }
2937  return getAgg(select);
2938  }
2939 
2942  private SqlNode getAgg(SqlSelect select) {
2943  final SelectScope selectScope = getRawSelectScope(select);
2944  if (selectScope != null) {
2945  final List<SqlNode> selectList = selectScope.getExpandedSelectList();
2946  if (selectList != null) {
2947  return aggFinder.findAgg(selectList);
2948  }
2949  }
2950  return aggFinder.findAgg(select.getSelectList());
2951  }
2952 
2953  @Deprecated
2954  @Override public boolean isAggregate(SqlNode selectNode) {
2955  return aggFinder.findAgg(selectNode) != null;
2956  }
2957 
2958  private void validateNodeFeature(SqlNode node) {
2959  switch (node.getKind()) {
2960  case MULTISET_VALUE_CONSTRUCTOR:
2961  validateFeature(RESOURCE.sQLFeature_S271(), node.getParserPosition());
2962  break;
2963  }
2964  }
2965 
2966  private void registerSubQueries(
2967  SqlValidatorScope parentScope,
2968  SqlNode node) {
2969  if (node == null) {
2970  return;
2971  }
2972  if (node.getKind().belongsTo(SqlKind.QUERY)
2973  || node.getKind() == SqlKind.MULTISET_QUERY_CONSTRUCTOR
2974  || node.getKind() == SqlKind.MULTISET_VALUE_CONSTRUCTOR) {
2975  registerQuery(parentScope, null, node, node, null, false);
2976  } else if (node instanceof SqlCall) {
2977  validateNodeFeature(node);
2978  SqlCall call = (SqlCall) node;
2979  for (int i = 0; i < call.operandCount(); i++) {
2980  registerOperandSubQueries(parentScope, call, i);
2981  }
2982  } else if (node instanceof SqlNodeList) {
2983  SqlNodeList list = (SqlNodeList) node;
2984  for (int i = 0, count = list.size(); i < count; i++) {
2985  SqlNode listNode = list.get(i);
2986  if (listNode.getKind().belongsTo(SqlKind.QUERY)) {
2987  listNode =
2988  SqlStdOperatorTable.SCALAR_QUERY.createCall(
2989  listNode.getParserPosition(),
2990  listNode);
2991  list.set(i, listNode);
2992  }
2993  registerSubQueries(parentScope, listNode);
2994  }
2995  } else {
2996  // atomic node -- can be ignored
2997  }
2998  }
2999 
3010  SqlValidatorScope parentScope,
3011  SqlCall call,
3012  int operandOrdinal) {
3013  SqlNode operand = call.operand(operandOrdinal);
3014  if (operand == null) {
3015  return;
3016  }
3017  if (operand.getKind().belongsTo(SqlKind.QUERY)
3018  && call.getOperator().argumentMustBeScalar(operandOrdinal)) {
3019  operand =
3020  SqlStdOperatorTable.SCALAR_QUERY.createCall(
3021  operand.getParserPosition(),
3022  operand);
3023  call.setOperand(operandOrdinal, operand);
3024  }
3025  registerSubQueries(parentScope, operand);
3026  }
3027 
3028  public void validateIdentifier(SqlIdentifier id, SqlValidatorScope scope) {
3029  final SqlQualified fqId = scope.fullyQualify(id);
3030  if (this.config.columnReferenceExpansion()) {
3031  // NOTE jvs 9-Apr-2007: this doesn't cover ORDER BY, which has its
3032  // own ideas about qualification.
3033  id.assignNamesFrom(fqId.identifier);
3034  } else {
3035  Util.discard(fqId);
3036  }
3037  }
3038 
3039  public void validateLiteral(SqlLiteral literal) {
3040  switch (literal.getTypeName()) {
3041  case DECIMAL:
3042  // Decimal and long have the same precision (as 64-bit integers), so
3043  // the unscaled value of a decimal must fit into a long.
3044 
3045  // REVIEW jvs 4-Aug-2004: This should probably be calling over to
3046  // the available calculator implementations to see what they
3047  // support. For now use ESP instead.
3048  //
3049  // jhyde 2006/12/21: I think the limits should be baked into the
3050  // type system, not dependent on the calculator implementation.
3051  BigDecimal bd = (BigDecimal) literal.getValue();
3052  BigInteger unscaled = bd.unscaledValue();
3053  long longValue = unscaled.longValue();
3054  if (!BigInteger.valueOf(longValue).equals(unscaled)) {
3055  // overflow
3056  throw newValidationError(literal,
3057  RESOURCE.numberLiteralOutOfRange(bd.toString()));
3058  }
3059  break;
3060 
3061  case DOUBLE:
3062  validateLiteralAsDouble(literal);
3063  break;
3064 
3065  case BINARY:
3066  final BitString bitString = (BitString) literal.getValue();
3067  if ((bitString.getBitCount() % 8) != 0) {
3068  throw newValidationError(literal, RESOURCE.binaryLiteralOdd());
3069  }
3070  break;
3071 
3072  case DATE:
3073  case TIME:
3074  case TIMESTAMP:
3075  Calendar calendar = literal.getValueAs(Calendar.class);
3076  final int year = calendar.get(Calendar.YEAR);
3077  final int era = calendar.get(Calendar.ERA);
3078  if (year < 1 || era == GregorianCalendar.BC || year > 9999) {
3079  throw newValidationError(literal,
3080  RESOURCE.dateLiteralOutOfRange(literal.toString()));
3081  }
3082  break;
3083 
3084  case INTERVAL_YEAR:
3085  case INTERVAL_YEAR_MONTH:
3086  case INTERVAL_MONTH:
3087  case INTERVAL_DAY:
3088  case INTERVAL_DAY_HOUR:
3089  case INTERVAL_DAY_MINUTE:
3090  case INTERVAL_DAY_SECOND:
3091  case INTERVAL_HOUR:
3092  case INTERVAL_HOUR_MINUTE:
3093  case INTERVAL_HOUR_SECOND:
3094  case INTERVAL_MINUTE:
3095  case INTERVAL_MINUTE_SECOND:
3096  case INTERVAL_SECOND:
3097  if (literal instanceof SqlIntervalLiteral) {
3098  SqlIntervalLiteral.IntervalValue interval =
3099  literal.getValueAs(SqlIntervalLiteral.IntervalValue.class);
3100  SqlIntervalQualifier intervalQualifier =
3101  interval.getIntervalQualifier();
3102 
3103  // ensure qualifier is good before attempting to validate literal
3104  validateIntervalQualifier(intervalQualifier);
3105  String intervalStr = interval.getIntervalLiteral();
3106  // throws CalciteContextException if string is invalid
3107  int[] values = intervalQualifier.evaluateIntervalLiteral(intervalStr,
3108  literal.getParserPosition(), typeFactory.getTypeSystem());
3109  Util.discard(values);
3110  }
3111  break;
3112  default:
3113  // default is to do nothing
3114  }
3115  }
3116 
3117  private void validateLiteralAsDouble(SqlLiteral literal) {
3118  BigDecimal bd = (BigDecimal) literal.getValue();
3119  double d = bd.doubleValue();
3120  if (Double.isInfinite(d) || Double.isNaN(d)) {
3121  // overflow
3122  throw newValidationError(literal,
3123  RESOURCE.numberLiteralOutOfRange(Util.toScientificNotation(bd)));
3124  }
3125 
3126  // REVIEW jvs 4-Aug-2004: what about underflow?
3127  }
3128 
3129  public void validateIntervalQualifier(SqlIntervalQualifier qualifier) {
3130  assert qualifier != null;
3131  boolean startPrecisionOutOfRange = false;
3132  boolean fractionalSecondPrecisionOutOfRange = false;
3133  final RelDataTypeSystem typeSystem = typeFactory.getTypeSystem();
3134 
3135  final int startPrecision = qualifier.getStartPrecision(typeSystem);
3136  final int fracPrecision =
3137  qualifier.getFractionalSecondPrecision(typeSystem);
3138  final int maxPrecision = typeSystem.getMaxPrecision(qualifier.typeName());
3139  final int minPrecision = qualifier.typeName().getMinPrecision();
3140  final int minScale = qualifier.typeName().getMinScale();
3141  final int maxScale = typeSystem.getMaxScale(qualifier.typeName());
3142  if (startPrecision < minPrecision || startPrecision > maxPrecision) {
3143  startPrecisionOutOfRange = true;
3144  } else {
3145  if (fracPrecision < minScale || fracPrecision > maxScale) {
3146  fractionalSecondPrecisionOutOfRange = true;
3147  }
3148  }
3149 
3150  if (startPrecisionOutOfRange) {
3151  throw newValidationError(qualifier,
3152  RESOURCE.intervalStartPrecisionOutOfRange(startPrecision,
3153  "INTERVAL " + qualifier));
3154  } else if (fractionalSecondPrecisionOutOfRange) {
3155  throw newValidationError(qualifier,
3156  RESOURCE.intervalFractionalSecondPrecisionOutOfRange(
3157  fracPrecision,
3158  "INTERVAL " + qualifier));
3159  }
3160  }
3161 
3172  protected void validateFrom(
3173  SqlNode node,
3174  RelDataType targetRowType,
3175  SqlValidatorScope scope) {
3176  Objects.requireNonNull(targetRowType);
3177  switch (node.getKind()) {
3178  case AS:
3179  case TABLE_REF:
3180  validateFrom(
3181  ((SqlCall) node).operand(0),
3182  targetRowType,
3183  scope);
3184  break;
3185  case VALUES:
3186  validateValues((SqlCall) node, targetRowType, scope);
3187  break;
3188  case JOIN:
3189  validateJoin((SqlJoin) node, scope);
3190  break;
3191  case OVER:
3192  validateOver((SqlCall) node, scope);
3193  break;
3194  case UNNEST:
3195  validateUnnest((SqlCall) node, scope, targetRowType);
3196  break;
3197  default:
3198  validateQuery(node, scope, targetRowType);
3199  break;
3200  }
3201 
3202  // Validate the namespace representation of the node, just in case the
3203  // validation did not occur implicitly.
3204  getNamespace(node, scope).validate(targetRowType);
3205  }
3206 
3207  protected void validateOver(SqlCall call, SqlValidatorScope scope) {
3208  throw new AssertionError("OVER unexpected in this context");
3209  }
3210 
3211  protected void validateUnnest(SqlCall call, SqlValidatorScope scope, RelDataType targetRowType) {
3212  for (int i = 0; i < call.operandCount(); i++) {
3213  SqlNode expandedItem = expand(call.operand(i), scope);
3214  call.setOperand(i, expandedItem);
3215  }
3216  validateQuery(call, scope, targetRowType);
3217  }
3218 
3219  private void checkRollUpInUsing(SqlIdentifier identifier,
3220  SqlNode leftOrRight, SqlValidatorScope scope) {
3221  SqlValidatorNamespace namespace = getNamespace(leftOrRight, scope);
3222  if (namespace != null) {
3223  SqlValidatorTable sqlValidatorTable = namespace.getTable();
3224  if (sqlValidatorTable != null) {
3225  Table table = sqlValidatorTable.unwrap(Table.class);
3226  String column = Util.last(identifier.names);
3227 
3228  if (table.isRolledUp(column)) {
3229  throw newValidationError(identifier,
3230  RESOURCE.rolledUpNotAllowed(column, "USING"));
3231  }
3232  }
3233  }
3234  }
3235 
3236  protected void validateJoin(SqlJoin join, SqlValidatorScope scope) {
3237  SqlNode left = join.getLeft();
3238  SqlNode right = join.getRight();
3239  SqlNode condition = join.getCondition();
3240  boolean natural = join.isNatural();
3241  final JoinType joinType = join.getJoinType();
3242  final JoinConditionType conditionType = join.getConditionType();
3243  final SqlValidatorScope joinScope = scopes.get(join);
3244  validateFrom(left, unknownType, joinScope);
3245  validateFrom(right, unknownType, joinScope);
3246 
3247  // Validate condition.
3248  switch (conditionType) {
3249  case NONE:
3250  Preconditions.checkArgument(condition == null);
3251  break;
3252  case ON:
3253  Preconditions.checkArgument(condition != null);
3254  SqlNode expandedCondition = expand(condition, joinScope);
3255  join.setOperand(5, expandedCondition);
3256  condition = join.getCondition();
3257  validateWhereOrOn(joinScope, condition, "ON");
3258  checkRollUp(null, join, condition, joinScope, "ON");
3259  break;
3260  case USING:
3261  SqlNodeList list = (SqlNodeList) condition;
3262 
3263  // Parser ensures that using clause is not empty.
3264  Preconditions.checkArgument(list.size() > 0, "Empty USING clause");
3265  for (SqlNode node : list) {
3266  SqlIdentifier id = (SqlIdentifier) node;
3267  final RelDataType leftColType = validateUsingCol(id, left);
3268  final RelDataType rightColType = validateUsingCol(id, right);
3269  if (!SqlTypeUtil.isComparable(leftColType, rightColType)) {
3270  throw newValidationError(id,
3271  RESOURCE.naturalOrUsingColumnNotCompatible(id.getSimple(),
3272  leftColType.toString(), rightColType.toString()));
3273  }
3274  checkRollUpInUsing(id, left, scope);
3275  checkRollUpInUsing(id, right, scope);
3276  }
3277  break;
3278  default:
3279  throw Util.unexpected(conditionType);
3280  }
3281 
3282  // Validate NATURAL.
3283  if (natural) {
3284  if (condition != null) {
3285  throw newValidationError(condition,
3286  RESOURCE.naturalDisallowsOnOrUsing());
3287  }
3288 
3289  // Join on fields that occur exactly once on each side. Ignore
3290  // fields that occur more than once on either side.
3291  final RelDataType leftRowType = getNamespace(left).getRowType();
3292  final RelDataType rightRowType = getNamespace(right).getRowType();
3293  final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
3294  List<String> naturalColumnNames =
3295  SqlValidatorUtil.deriveNaturalJoinColumnList(nameMatcher,
3296  leftRowType, rightRowType);
3297 
3298  // Check compatibility of the chosen columns.
3299  for (String name : naturalColumnNames) {
3300  final RelDataType leftColType =
3301  nameMatcher.field(leftRowType, name).getType();
3302  final RelDataType rightColType =
3303  nameMatcher.field(rightRowType, name).getType();
3304  if (!SqlTypeUtil.isComparable(leftColType, rightColType)) {
3305  throw newValidationError(join,
3306  RESOURCE.naturalOrUsingColumnNotCompatible(name,
3307  leftColType.toString(), rightColType.toString()));
3308  }
3309  }
3310  }
3311 
3312  // Which join types require/allow a ON/USING condition, or allow
3313  // a NATURAL keyword?
3314  switch (joinType) {
3315  case LEFT_SEMI_JOIN:
3316  if (!this.config.sqlConformance().isLiberal()) {
3317  throw newValidationError(join.getJoinTypeNode(),
3318  RESOURCE.dialectDoesNotSupportFeature("LEFT SEMI JOIN"));
3319  }
3320  // fall through
3321  case INNER:
3322  case LEFT:
3323  case RIGHT:
3324  case FULL:
3325  if ((condition == null) && !natural) {
3326  throw newValidationError(join, RESOURCE.joinRequiresCondition());
3327  }
3328  break;
3329  case COMMA:
3330  case CROSS:
3331  if (condition != null) {
3332  throw newValidationError(join.getConditionTypeNode(),
3333  RESOURCE.crossJoinDisallowsCondition());
3334  }
3335  if (natural) {
3336  throw newValidationError(join.getConditionTypeNode(),
3337  RESOURCE.crossJoinDisallowsCondition());
3338  }
3339  break;
3340  default:
3341  throw Util.unexpected(joinType);
3342  }
3343  }
3344 
3353  private void validateNoAggs(AggFinder aggFinder, SqlNode node,
3354  String clause) {
3355  final SqlCall agg = aggFinder.findAgg(node);
3356  if (agg == null) {
3357  return;
3358  }
3359  final SqlOperator op = agg.getOperator();
3360  if (op == SqlStdOperatorTable.OVER) {
3361  throw newValidationError(agg,
3362  RESOURCE.windowedAggregateIllegalInClause(clause));
3363  } else if (op.isGroup() || op.isGroupAuxiliary()) {
3364  throw newValidationError(agg,
3365  RESOURCE.groupFunctionMustAppearInGroupByClause(op.getName()));
3366  } else {
3367  throw newValidationError(agg,
3368  RESOURCE.aggregateIllegalInClause(clause));
3369  }
3370  }
3371 
3372  private RelDataType validateUsingCol(SqlIdentifier id, SqlNode leftOrRight) {
3373  if (id.names.size() == 1) {
3374  String name = id.names.get(0);
3375  final SqlValidatorNamespace namespace = getNamespace(leftOrRight);
3376  final RelDataType rowType = namespace.getRowType();
3377  final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
3378  final RelDataTypeField field = nameMatcher.field(rowType, name);
3379  if (field != null) {
3380  if (nameMatcher.frequency(rowType.getFieldNames(), name) > 1) {
3381  throw newValidationError(id,
3382  RESOURCE.columnInUsingNotUnique(id.toString()));
3383  }
3384  return field.getType();
3385  }
3386  }
3387  throw newValidationError(id, RESOURCE.columnNotFound(id.toString()));
3388  }
3389 
3397  protected void validateSelect(
3398  SqlSelect select,
3399  RelDataType targetRowType) {
3400  assert targetRowType != null;
3401  // Namespace is either a select namespace or a wrapper around one.
3402  final SelectNamespace ns =
3403  getNamespace(select).unwrap(SelectNamespace.class);
3404 
3405  // Its rowtype is null, meaning it hasn't been validated yet.
3406  // This is important, because we need to take the targetRowType into
3407  // account.
3408  assert ns.rowType == null;
3409 
3410  if (select.isDistinct()) {
3411  validateFeature(RESOURCE.sQLFeature_E051_01(),
3412  select.getModifierNode(SqlSelectKeyword.DISTINCT)
3413  .getParserPosition());
3414  }
3415 
3416  final SqlNodeList selectItems = select.getSelectList();
3417  RelDataType fromType = unknownType;
3418  if (selectItems.size() == 1) {
3419  final SqlNode selectItem = selectItems.get(0);
3420  if (selectItem instanceof SqlIdentifier) {
3421  SqlIdentifier id = (SqlIdentifier) selectItem;
3422  if (id.isStar() && (id.names.size() == 1)) {
3423  // Special case: for INSERT ... VALUES(?,?), the SQL
3424  // standard says we're supposed to propagate the target
3425  // types down. So iff the select list is an unqualified
3426  // star (as it will be after an INSERT ... VALUES has been
3427  // expanded), then propagate.
3428  fromType = targetRowType;
3429  }
3430  }
3431  }
3432 
3433  // Make sure that items in FROM clause have distinct aliases.
3434  final SelectScope fromScope = (SelectScope) getFromScope(select);
3435  List<String> names = fromScope.getChildNames();
3436  if (!catalogReader.nameMatcher().isCaseSensitive()) {
3437  names = names.stream()
3438  .map(s -> s.toUpperCase(Locale.ROOT))
3439  .collect(Collectors.toList());
3440  }
3441  final int duplicateAliasOrdinal = Util.firstDuplicate(names);
3442  if (duplicateAliasOrdinal >= 0) {
3443  final ScopeChild child =
3444  fromScope.children.get(duplicateAliasOrdinal);
3445  throw newValidationError(child.namespace.getEnclosingNode(),
3446  RESOURCE.fromAliasDuplicate(child.name));
3447  }
3448 
3449  if (select.getFrom() == null) {
3450  if (this.config.sqlConformance().isFromRequired()) {
3451  throw newValidationError(select, RESOURCE.selectMissingFrom());
3452  }
3453  } else {
3454  validateFrom(select.getFrom(), fromType, fromScope);
3455  }
3456 
3457  validateWhereClause(select);
3458  validateGroupClause(select);
3459  validateHavingClause(select);
3460  validateWindowClause(select);
3461  handleOffsetFetch(select.getOffset(), select.getFetch());
3462 
3463  // Validate the SELECT clause late, because a select item might
3464  // depend on the GROUP BY list, or the window function might reference
3465  // window name in the WINDOW clause etc.
3466  final RelDataType rowType =
3467  validateSelectList(selectItems, select, targetRowType);
3468  ns.setType(rowType);
3469 
3470  // Validate ORDER BY after we have set ns.rowType because in some
3471  // dialects you can refer to columns of the select list, e.g.
3472  // "SELECT empno AS x FROM emp ORDER BY x"
3473  validateOrderList(select);
3474 
3475  if (shouldCheckForRollUp(select.getFrom())) {
3476  checkRollUpInSelectList(select);
3477  checkRollUp(null, select, select.getWhere(), getWhereScope(select));
3478  checkRollUp(null, select, select.getHaving(), getHavingScope(select));
3479  checkRollUpInWindowDecl(select);
3480  checkRollUpInGroupBy(select);
3481  checkRollUpInOrderBy(select);
3482  }
3483  }
3484 
3485  private void checkRollUpInSelectList(SqlSelect select) {
3486  SqlValidatorScope scope = getSelectScope(select);
3487  for (SqlNode item : select.getSelectList()) {
3488  checkRollUp(null, select, item, scope);
3489  }
3490  }
3491 
3492  private void checkRollUpInGroupBy(SqlSelect select) {
3493  SqlNodeList group = select.getGroup();
3494  if (group != null) {
3495  for (SqlNode node : group) {
3496  checkRollUp(null, select, node, getGroupScope(select), "GROUP BY");
3497  }
3498  }
3499  }
3500 
3501  private void checkRollUpInOrderBy(SqlSelect select) {
3502  SqlNodeList orderList = select.getOrderList();
3503  if (orderList != null) {
3504  for (SqlNode node : orderList) {
3505  checkRollUp(null, select, node, getOrderScope(select), "ORDER BY");
3506  }
3507  }
3508  }
3509 
3510  private void checkRollUpInWindow(SqlWindow window, SqlValidatorScope scope) {
3511  if (window != null) {
3512  for (SqlNode node : window.getPartitionList()) {
3513  checkRollUp(null, window, node, scope, "PARTITION BY");
3514  }
3515 
3516  for (SqlNode node : window.getOrderList()) {
3517  checkRollUp(null, window, node, scope, "ORDER BY");
3518  }
3519  }
3520  }
3521 
3522  private void checkRollUpInWindowDecl(SqlSelect select) {
3523  for (SqlNode decl : select.getWindowList()) {
3524  checkRollUpInWindow((SqlWindow) decl, getSelectScope(select));
3525  }
3526  }
3527 
3528  private SqlNode stripDot(SqlNode node) {
3529  if (node != null && node.getKind() == SqlKind.DOT) {
3530  return stripDot(((SqlCall) node).operand(0));
3531  }
3532  return node;
3533  }
3534 
3535  private void checkRollUp(SqlNode grandParent, SqlNode parent,
3536  SqlNode current, SqlValidatorScope scope, String optionalClause) {
3537  current = stripAs(current);
3538  if (current instanceof SqlCall && !(current instanceof SqlSelect)) {
3539  // Validate OVER separately
3540  checkRollUpInWindow(getWindowInOver(current), scope);
3541  current = stripOver(current);
3542 
3543  List<SqlNode> children = ((SqlCall) stripAs(stripDot(current))).getOperandList();
3544  for (SqlNode child : children) {
3545  checkRollUp(parent, current, child, scope, optionalClause);
3546  }
3547  } else if (current instanceof SqlIdentifier) {
3548  SqlIdentifier id = (SqlIdentifier) current;
3549  if (!id.isStar() && isRolledUpColumn(id, scope)) {
3550  if (!isAggregation(parent.getKind())
3551  || !isRolledUpColumnAllowedInAgg(id, scope, (SqlCall) parent, grandParent)) {
3552  String context = optionalClause != null ? optionalClause : parent.getKind().toString();
3553  throw newValidationError(id,
3554  RESOURCE.rolledUpNotAllowed(deriveAlias(id, 0), context));
3555  }
3556  }
3557  }
3558  }
3559 
3560  private void checkRollUp(SqlNode grandParent, SqlNode parent,
3561  SqlNode current, SqlValidatorScope scope) {
3562  checkRollUp(grandParent, parent, current, scope, null);
3563  }
3564 
3565  private SqlWindow getWindowInOver(SqlNode over) {
3566  if (over.getKind() == SqlKind.OVER) {
3567  SqlNode window = ((SqlCall) over).getOperandList().get(1);
3568  if (window instanceof SqlWindow) {
3569  return (SqlWindow) window;
3570  }
3571  // SqlIdentifier, gets validated elsewhere
3572  return null;
3573  }
3574  return null;
3575  }
3576 
3577  private static SqlNode stripOver(SqlNode node) {
3578  switch (node.getKind()) {
3579  case OVER:
3580  return ((SqlCall) node).getOperandList().get(0);
3581  default:
3582  return node;
3583  }
3584  }
3585 
3586  private Pair<String, String> findTableColumnPair(SqlIdentifier identifier,
3587  SqlValidatorScope scope) {
3588  final SqlCall call = makeNullaryCall(identifier);
3589  if (call != null) {
3590  return null;
3591  }
3592  SqlQualified qualified = scope.fullyQualify(identifier);
3593  List<String> names = qualified.identifier.names;
3594 
3595  if (names.size() < 2) {
3596  return null;
3597  }
3598 
3599  return new Pair<>(names.get(names.size() - 2), Util.last(names));
3600  }
3601 
3602  // Returns true iff the given column is valid inside the given aggCall.
3603  private boolean isRolledUpColumnAllowedInAgg(SqlIdentifier identifier, SqlValidatorScope scope,
3604  SqlCall aggCall, SqlNode parent) {
3605  Pair<String, String> pair = findTableColumnPair(identifier, scope);
3606 
3607  if (pair == null) {
3608  return true;
3609  }
3610 
3611  String columnName = pair.right;
3612 
3613  SqlValidatorTable sqlValidatorTable =
3614  scope.fullyQualify(identifier).namespace.getTable();
3615  if (sqlValidatorTable != null) {
3616  Table table = sqlValidatorTable.unwrap(Table.class);
3617  return table.rolledUpColumnValidInsideAgg(columnName, aggCall, parent,
3618  catalogReader.getConfig());
3619  }
3620  return true;
3621  }
3622 
3623 
3624  // Returns true iff the given column is actually rolled up.
3625  private boolean isRolledUpColumn(SqlIdentifier identifier, SqlValidatorScope scope) {
3626  Pair<String, String> pair = findTableColumnPair(identifier, scope);
3627 
3628  if (pair == null) {
3629  return false;
3630  }
3631 
3632  String columnName = pair.right;
3633 
3634  SqlValidatorTable sqlValidatorTable =
3635  scope.fullyQualify(identifier).namespace.getTable();
3636  if (sqlValidatorTable != null) {
3637  Table table = sqlValidatorTable.unwrap(Table.class);
3638  return table.isRolledUp(columnName);
3639  }
3640  return false;
3641  }
3642 
3643  private boolean shouldCheckForRollUp(SqlNode from) {
3644  if (from != null) {
3645  SqlKind kind = stripAs(from).getKind();
3646  return kind != SqlKind.VALUES && kind != SqlKind.SELECT;
3647  }
3648  return false;
3649  }
3650 
3653  private void validateModality(SqlNode query) {
3654  final SqlModality modality = deduceModality(query);
3655  if (query instanceof SqlSelect) {
3656  final SqlSelect select = (SqlSelect) query;
3657  validateModality(select, modality, true);
3658  } else if (query.getKind() == SqlKind.VALUES) {
3659  switch (modality) {
3660  case STREAM:
3661  throw newValidationError(query, Static.RESOURCE.cannotStreamValues());
3662  }
3663  } else {
3664  assert query.isA(SqlKind.SET_QUERY);
3665  final SqlCall call = (SqlCall) query;
3666  for (SqlNode operand : call.getOperandList()) {
3667  if (deduceModality(operand) != modality) {
3668  throw newValidationError(operand,
3669  Static.RESOURCE.streamSetOpInconsistentInputs());
3670  }
3671  validateModality(operand);
3672  }
3673  }
3674  }
3675 
3677  private SqlModality deduceModality(SqlNode query) {
3678  if (query instanceof SqlSelect) {
3679  SqlSelect select = (SqlSelect) query;
3680  return select.getModifierNode(SqlSelectKeyword.STREAM) != null
3681  ? SqlModality.STREAM
3682  : SqlModality.RELATION;
3683  } else if (query.getKind() == SqlKind.VALUES) {
3684  return SqlModality.RELATION;
3685  } else {
3686  assert query.isA(SqlKind.SET_QUERY);
3687  final SqlCall call = (SqlCall) query;
3688  return deduceModality(call.getOperandList().get(0));
3689  }
3690  }
3691 
3692  public boolean validateModality(SqlSelect select, SqlModality modality,
3693  boolean fail) {
3694  final SelectScope scope = getRawSelectScope(select);
3695 
3696  switch (modality) {
3697  case STREAM:
3698  if (scope.children.size() == 1) {
3699  for (ScopeChild child : scope.children) {
3700  if (!child.namespace.supportsModality(modality)) {
3701  if (fail) {
3702  throw newValidationError(child.namespace.getNode(),
3703  Static.RESOURCE.cannotConvertToStream(child.name));
3704  } else {
3705  return false;
3706  }
3707  }
3708  }
3709  } else {
3710  int supportsModalityCount = 0;
3711  for (ScopeChild child : scope.children) {
3712  if (child.namespace.supportsModality(modality)) {
3713  ++supportsModalityCount;
3714  }
3715  }
3716 
3717  if (supportsModalityCount == 0) {
3718  if (fail) {
3719  String inputs = String.join(", ", scope.getChildNames());
3720  throw newValidationError(select,
3721  Static.RESOURCE.cannotStreamResultsForNonStreamingInputs(inputs));
3722  } else {
3723  return false;
3724  }
3725  }
3726  }
3727  break;
3728  default:
3729  for (ScopeChild child : scope.children) {
3730  if (!child.namespace.supportsModality(modality)) {
3731  if (fail) {
3732  throw newValidationError(child.namespace.getNode(),
3733  Static.RESOURCE.cannotConvertToRelation(child.name));
3734  } else {
3735  return false;
3736  }
3737  }
3738  }
3739  }
3740 
3741  // Make sure that aggregation is possible.
3742  final SqlNode aggregateNode = getAggregate(select);
3743  if (aggregateNode != null) {
3744  switch (modality) {
3745  case STREAM:
3746  SqlNodeList groupList = select.getGroup();
3747  if (groupList == null
3748  || !SqlValidatorUtil.containsMonotonic(scope, groupList)) {
3749  if (fail) {
3750  throw newValidationError(aggregateNode,
3751  Static.RESOURCE.streamMustGroupByMonotonic());
3752  } else {
3753  return false;
3754  }
3755  }
3756  }
3757  }
3758 
3759  // Make sure that ORDER BY is possible.
3760  final SqlNodeList orderList = select.getOrderList();
3761  if (orderList != null && orderList.size() > 0) {
3762  switch (modality) {
3763  case STREAM:
3764  if (!hasSortedPrefix(scope, orderList)) {
3765  if (fail) {
3766  throw newValidationError(orderList.get(0),
3767  Static.RESOURCE.streamMustOrderByMonotonic());
3768  } else {
3769  return false;
3770  }
3771  }
3772  }
3773  }
3774  return true;
3775  }
3776 
3778  private boolean hasSortedPrefix(SelectScope scope, SqlNodeList orderList) {
3779  return isSortCompatible(scope, orderList.get(0), false);
3780  }
3781 
3782  private boolean isSortCompatible(SelectScope scope, SqlNode node,
3783  boolean descending) {
3784  switch (node.getKind()) {
3785  case DESCENDING:
3786  return isSortCompatible(scope, ((SqlCall) node).getOperandList().get(0),
3787  true);
3788  }
3789  final SqlMonotonicity monotonicity = scope.getMonotonicity(node);
3790  switch (monotonicity) {
3791  case INCREASING:
3792  case STRICTLY_INCREASING:
3793  return !descending;
3794  case DECREASING:
3795  case STRICTLY_DECREASING:
3796  return descending;
3797  default:
3798  return false;
3799  }
3800  }
3801 
3802  protected void validateWindowClause(SqlSelect select) {
3803  final SqlNodeList windowList = select.getWindowList();
3804  @SuppressWarnings("unchecked") final List<SqlWindow> windows =
3805  (List) windowList.getList();
3806  if (windows.isEmpty()) {
3807  return;
3808  }
3809 
3810  final SelectScope windowScope = (SelectScope) getFromScope(select);
3811  assert windowScope != null;
3812 
3813  // 1. ensure window names are simple
3814  // 2. ensure they are unique within this scope
3815  for (SqlWindow window : windows) {
3816  SqlIdentifier declName = window.getDeclName();
3817  if (!declName.isSimple()) {
3818  throw newValidationError(declName, RESOURCE.windowNameMustBeSimple());
3819  }
3820 
3821  if (windowScope.existingWindowName(declName.toString())) {
3822  throw newValidationError(declName, RESOURCE.duplicateWindowName());
3823  } else {
3824  windowScope.addWindowName(declName.toString());
3825  }
3826  }
3827 
3828  // 7.10 rule 2
3829  // Check for pairs of windows which are equivalent.
3830  for (int i = 0; i < windows.size(); i++) {
3831  SqlNode window1 = windows.get(i);
3832  for (int j = i + 1; j < windows.size(); j++) {
3833  SqlNode window2 = windows.get(j);
3834  if (window1.equalsDeep(window2, Litmus.IGNORE)) {
3835  throw newValidationError(window2, RESOURCE.dupWindowSpec());
3836  }
3837  }
3838  }
3839 
3840  for (SqlWindow window : windows) {
3841  final SqlNodeList expandedOrderList =
3842  (SqlNodeList) expand(window.getOrderList(), windowScope);
3843  window.setOrderList(expandedOrderList);
3844  expandedOrderList.validate(this, windowScope);
3845 
3846  final SqlNodeList expandedPartitionList =
3847  (SqlNodeList) expand(window.getPartitionList(), windowScope);
3848  window.setPartitionList(expandedPartitionList);
3849  expandedPartitionList.validate(this, windowScope);
3850  }
3851 
3852  // Hand off to validate window spec components
3853  windowList.validate(this, windowScope);
3854  }
3855 
3856  public void validateWith(SqlWith with, SqlValidatorScope scope) {
3857  final SqlValidatorNamespace namespace = getNamespace(with);
3858  validateNamespace(namespace, unknownType);
3859  }
3860 
3861  public void validateWithItem(SqlWithItem withItem) {
3862  if (withItem.columnList != null) {
3863  final RelDataType rowType = getValidatedNodeType(withItem.query);
3864  final int fieldCount = rowType.getFieldCount();
3865  if (withItem.columnList.size() != fieldCount) {
3866  throw newValidationError(withItem.columnList,
3867  RESOURCE.columnCountMismatch());
3868  }
3869  SqlValidatorUtil.checkIdentifierListForDuplicates(
3870  withItem.columnList.getList(), validationErrorFunction);
3871  } else {
3872  // Luckily, field names have not been make unique yet.
3873  final List<String> fieldNames =
3874  getValidatedNodeType(withItem.query).getFieldNames();
3875  final int i = Util.firstDuplicate(fieldNames);
3876  if (i >= 0) {
3877  throw newValidationError(withItem.query,
3878  RESOURCE.duplicateColumnAndNoColumnList(fieldNames.get(i)));
3879  }
3880  }
3881  }
3882 
3883  public void validateSequenceValue(SqlValidatorScope scope, SqlIdentifier id) {
3884  // Resolve identifier as a table.
3885  final SqlValidatorScope.ResolvedImpl resolved =
3886  new SqlValidatorScope.ResolvedImpl();
3887  scope.resolveTable(id.names, catalogReader.nameMatcher(),
3888  SqlValidatorScope.Path.EMPTY, resolved);
3889  if (resolved.count() != 1) {
3890  throw newValidationError(id, RESOURCE.tableNameNotFound(id.toString()));
3891  }
3892  // We've found a table. But is it a sequence?
3893  final SqlValidatorNamespace ns = resolved.only().namespace;
3894  if (ns instanceof TableNamespace) {
3895  final Table table = ns.getTable().unwrap(Table.class);
3896  switch (table.getJdbcTableType()) {
3897  case SEQUENCE:
3898  case TEMPORARY_SEQUENCE:
3899  return;
3900  }
3901  }
3902  throw newValidationError(id, RESOURCE.notASequence(id.toString()));
3903  }
3904 
3905  public SqlValidatorScope getWithScope(SqlNode withItem) {
3906  assert withItem.getKind() == SqlKind.WITH_ITEM;
3907  return scopes.get(withItem);
3908  }
3909 
3910  public TypeCoercion getTypeCoercion() {
3911  assert config.typeCoercionEnabled();
3912  return this.typeCoercion;
3913  }
3914 
3915  public Config config() {
3916  return this.config;
3917  }
3918 
3919  public SqlValidator transform(UnaryOperator<Config> transform) {
3920  this.config = transform.apply(this.config);
3921  return this;
3922  }
3923 
3929  protected void validateOrderList(SqlSelect select) {
3930  // ORDER BY is validated in a scope where aliases in the SELECT clause
3931  // are visible. For example, "SELECT empno AS x FROM emp ORDER BY x"
3932  // is valid.
3933  SqlNodeList orderList = select.getOrderList();
3934  if (orderList == null) {
3935  return;
3936  }
3938  if (!cursorSet.contains(select)) {
3939  throw newValidationError(select, RESOURCE.invalidOrderByPos());
3940  }
3941  }
3942  final SqlValidatorScope orderScope = getOrderScope(select);
3943  Objects.requireNonNull(orderScope);
3944 
3945  List<SqlNode> expandList = new ArrayList<>();
3946  for (SqlNode orderItem : orderList) {
3947  SqlNode expandedOrderItem = expand(orderItem, orderScope);
3948  expandList.add(expandedOrderItem);
3949  }
3950 
3951  SqlNodeList expandedOrderList = new SqlNodeList(
3952  expandList,
3953  orderList.getParserPosition());
3954  select.setOrderBy(expandedOrderList);
3955 
3956  for (SqlNode orderItem : expandedOrderList) {
3957  validateOrderItem(select, orderItem);
3958  }
3959  }
3960 
3967  private void validateGroupByItem(SqlSelect select, SqlNode groupByItem) {
3968  final SqlValidatorScope groupByScope = getGroupScope(select);
3969  validateGroupByExpr(groupByItem, groupByScope);
3970  groupByScope.validateExpr(groupByItem);
3971  }
3972 
3973  private void validateGroupByExpr(SqlNode groupByItem,
3974  SqlValidatorScope groupByScope) {
3975  switch (groupByItem.getKind()) {
3976  case GROUPING_SETS:
3977  case ROLLUP:
3978  case CUBE:
3979  final SqlCall call = (SqlCall) groupByItem;
3980  for (SqlNode operand : call.getOperandList()) {
3981  validateExpr(operand, groupByScope);
3982  }
3983  break;
3984  default:
3985  validateExpr(groupByItem, groupByScope);
3986  }
3987  }
3988 
3995  private void validateOrderItem(SqlSelect select, SqlNode orderItem) {
3996  switch (orderItem.getKind()) {
3997  case DESCENDING:
3998  validateFeature(RESOURCE.sQLConformance_OrderByDesc(),
3999  orderItem.getParserPosition());
4000  validateOrderItem(select,
4001  ((SqlCall) orderItem).operand(0));
4002  return;
4003  }
4004 
4005  final SqlValidatorScope orderScope = getOrderScope(select);
4006  validateExpr(orderItem, orderScope);
4007  }
4008 
4009  public SqlNode expandOrderExpr(SqlSelect select, SqlNode orderExpr) {
4010  final SqlNode newSqlNode =
4011  new OrderExpressionExpander(select, orderExpr).go();
4012  if (newSqlNode != orderExpr) {
4013  final SqlValidatorScope scope = getOrderScope(select);
4014  inferUnknownTypes(unknownType, scope, newSqlNode);
4015  final RelDataType type = deriveType(scope, newSqlNode);
4016  setValidatedNodeType(newSqlNode, type);
4017  }
4018  return newSqlNode;
4019  }
4020 
4025  protected void validateGroupClause(SqlSelect select) {
4026  SqlNodeList groupList = select.getGroup();
4027  if (groupList == null) {
4028  return;
4029  }
4030  final String clause = "GROUP BY";
4031  validateNoAggs(aggOrOverFinder, groupList, clause);
4032  final SqlValidatorScope groupScope = getGroupScope(select);
4033  inferUnknownTypes(unknownType, groupScope, groupList);
4034 
4035  // expand the expression in group list.
4036  List<SqlNode> expandedList = new ArrayList<>();
4037  for (SqlNode groupItem : groupList) {
4038  SqlNode expandedItem = expandGroupByOrHavingExpr(groupItem, groupScope, select, false);
4039  expandedList.add(expandedItem);
4040  }
4041  groupList = new SqlNodeList(expandedList, groupList.getParserPosition());
4042  select.setGroupBy(groupList);
4043  for (SqlNode groupItem : expandedList) {
4044  validateGroupByItem(select, groupItem);
4045  }
4046 
4047  // Nodes in the GROUP BY clause are expressions except if they are calls
4048  // to the GROUPING SETS, ROLLUP or CUBE operators; this operators are not
4049  // expressions, because they do not have a type.
4050  for (SqlNode node : groupList) {
4051  switch (node.getKind()) {
4052  case GROUPING_SETS:
4053  case ROLLUP:
4054  case CUBE:
4055  node.validate(this, groupScope);
4056  break;
4057  default:
4058  node.validateExpr(this, groupScope);
4059  }
4060  }
4061 
4062  // Derive the type of each GROUP BY item. We don't need the type, but
4063  // it resolves functions, and that is necessary for deducing
4064  // monotonicity.
4065  final SqlValidatorScope selectScope = getSelectScope(select);
4066  AggregatingSelectScope aggregatingScope = null;
4067  if (selectScope instanceof AggregatingSelectScope) {
4068  aggregatingScope = (AggregatingSelectScope) selectScope;
4069  }
4070  for (SqlNode groupItem : groupList) {
4071  if (groupItem instanceof SqlNodeList
4072  && ((SqlNodeList) groupItem).size() == 0) {
4073  continue;
4074  }
4075  validateGroupItem(groupScope, aggregatingScope, groupItem);
4076  }
4077 
4078  SqlNode agg = aggFinder.findAgg(groupList);
4079  if (agg != null) {
4080  throw newValidationError(agg, RESOURCE.aggregateIllegalInClause(clause));
4081  }
4082  }
4083 
4084  private void validateGroupItem(SqlValidatorScope groupScope,
4085  AggregatingSelectScope aggregatingScope,
4086  SqlNode groupItem) {
4087  switch (groupItem.getKind()) {
4088  case GROUPING_SETS:
4089  case ROLLUP:
4090  case CUBE:
4091  validateGroupingSets(groupScope, aggregatingScope, (SqlCall) groupItem);
4092  break;
4093  default:
4094  if (groupItem instanceof SqlNodeList) {
4095  break;
4096  }
4097  final RelDataType type = deriveType(groupScope, groupItem);
4098  setValidatedNodeType(groupItem, type);
4099  }
4100  }
4101 
4102  private void validateGroupingSets(SqlValidatorScope groupScope,
4103  AggregatingSelectScope aggregatingScope, SqlCall groupItem) {
4104  for (SqlNode node : groupItem.getOperandList()) {
4105  validateGroupItem(groupScope, aggregatingScope, node);
4106  }
4107  }
4108 
4109  protected void validateWhereClause(SqlSelect select) {
4110  // validate WHERE clause
4111  final SqlNode where = select.getWhere();
4112  if (where == null) {
4113  return;
4114  }
4115  final SqlValidatorScope whereScope = getWhereScope(select);
4116  final SqlNode expandedWhere = expand(where, whereScope);
4117  select.setWhere(expandedWhere);
4118  validateWhereOrOn(whereScope, expandedWhere, "WHERE");
4119  }
4120 
4121  protected void validateWhereOrOn(
4122  SqlValidatorScope scope,
4123  SqlNode condition,
4124  String clause) {
4125  validateNoAggs(aggOrOverOrGroupFinder, condition, clause);
4127  booleanType,
4128  scope,
4129  condition);
4130  condition.validate(this, scope);
4131 
4132  final RelDataType type = deriveType(scope, condition);
4133  if (!SqlTypeUtil.inBooleanFamily(type)) {
4134  throw newValidationError(condition, RESOURCE.condMustBeBoolean(clause));
4135  }
4136  }
4137 
4138  protected void validateHavingClause(SqlSelect select) {
4139  // HAVING is validated in the scope after groups have been created.
4140  // For example, in "SELECT empno FROM emp WHERE empno = 10 GROUP BY
4141  // deptno HAVING empno = 10", the reference to 'empno' in the HAVING
4142  // clause is illegal.
4143  SqlNode having = select.getHaving();
4144  if (having == null) {
4145  return;
4146  }
4147  final AggregatingScope havingScope =
4148  (AggregatingScope) getSelectScope(select);
4149  if (config.sqlConformance().isHavingAlias()) {
4150  SqlNode newExpr = expandGroupByOrHavingExpr(having, havingScope, select, true);
4151  if (having != newExpr) {
4152  having = newExpr;
4153  select.setHaving(newExpr);
4154  }
4155  }
4156  havingScope.checkAggregateExpr(having, true);
4158  booleanType,
4159  havingScope,
4160  having);
4161  having.validate(this, havingScope);
4162  final RelDataType type = deriveType(havingScope, having);
4163  if (!SqlTypeUtil.inBooleanFamily(type)) {
4164  throw newValidationError(having, RESOURCE.havingMustBeBoolean());
4165  }
4166  }
4167 
4168  protected RelDataType validateSelectList(
4169  final SqlNodeList selectItems,
4170  SqlSelect select,
4171  RelDataType targetRowType) {
4172  // First pass, ensure that aliases are unique. "*" and "TABLE.*" items
4173  // are ignored.
4174 
4175  // Validate SELECT list. Expand terms of the form "*" or "TABLE.*".
4176  final SqlValidatorScope selectScope = getSelectScope(select);
4177  final List<SqlNode> expandedSelectItems = new ArrayList<>();
4178  final Set<String> aliases = new HashSet<>();
4179  final List<Map.Entry<String, RelDataType>> fieldList = new ArrayList<>();
4180 
4181  for (SqlNode selectItem : selectItems) {
4182  if (selectItem instanceof SqlSelect) {
4184  select,
4185  (SqlSelect) selectItem,
4186  expandedSelectItems,
4187  aliases,
4188  fieldList);
4189  } else {
4190  // Use the field list size to record the field index
4191  // because the select item may be a STAR(*), which could have been expanded.
4192  final int fieldIdx = fieldList.size();
4193  final RelDataType fieldType =
4194  targetRowType.isStruct()
4195  && targetRowType.getFieldCount() > fieldIdx
4196  ? targetRowType.getFieldList().get(fieldIdx).getType()
4197  : unknownType;
4199  selectItem,
4200  select,
4201  fieldType,
4202  expandedSelectItems,
4203  aliases,
4204  fieldList,
4205  false);
4206  }
4207  }
4208 
4209  // Create the new select list with expanded items. Pass through
4210  // the original parser position so that any overall failures can
4211  // still reference the original input text.
4212  SqlNodeList newSelectList =
4213  new SqlNodeList(
4214  expandedSelectItems,
4215  selectItems.getParserPosition());
4216  if (config.identifierExpansion()) {
4217  select.setSelectList(newSelectList);
4218  }
4219  getRawSelectScope(select).setExpandedSelectList(expandedSelectItems);
4220 
4221  // TODO: when SELECT appears as a value sub-query, should be using
4222  // something other than unknownType for targetRowType
4223  inferUnknownTypes(targetRowType, selectScope, newSelectList);
4224 
4225  for (SqlNode selectItem : expandedSelectItems) {
4226  validateNoAggs(groupFinder, selectItem, "SELECT");
4227  validateExpr(selectItem, selectScope);
4228  }
4229 
4230  return typeFactory.createStructType(fieldList);
4231  }
4232 
4239  private void validateExpr(SqlNode expr, SqlValidatorScope scope) {
4240  if (expr instanceof SqlCall) {
4241  final SqlOperator op = ((SqlCall) expr).getOperator();
4242  if (op.isAggregator() && op.requiresOver()) {
4243  throw newValidationError(expr,
4244  RESOURCE.absentOverClause());
4245  }
4246  if (op instanceof SqlTableFunction) {
4247  throw RESOURCE.cannotCallTableFunctionHere(op.getName()).ex();
4248  }
4249  }
4250 
4251  // Call on the expression to validate itself.
4252  expr.validateExpr(this, scope);
4253 
4254  // Perform any validation specific to the scope. For example, an
4255  // aggregating scope requires that expressions are valid aggregations.
4256  scope.validateExpr(expr);
4257  }
4258 
4270  private void handleScalarSubQuery(
4271  SqlSelect parentSelect,
4272  SqlSelect selectItem,
4273  List<SqlNode> expandedSelectItems,
4274  Set<String> aliasList,
4275  List<Map.Entry<String, RelDataType>> fieldList) {
4276  // A scalar sub-query only has one output column.
4277  if (1 != selectItem.getSelectList().size()) {
4278  throw newValidationError(selectItem,
4279  RESOURCE.onlyScalarSubQueryAllowed());
4280  }
4281 
4282  // No expansion in this routine just append to list.
4283  expandedSelectItems.add(selectItem);
4284 
4285  // Get or generate alias and add to list.
4286  final String alias =
4287  deriveAlias(
4288  selectItem,
4289  aliasList.size());
4290  aliasList.add(alias);
4291 
4292  final SelectScope scope = (SelectScope) getWhereScope(parentSelect);
4293  final RelDataType type = deriveType(scope, selectItem);
4294  setValidatedNodeType(selectItem, type);
4295 
4296  // We do not want to pass on the RelRecordType returned
4297  // by the sub-query. Just the type of the single expression
4298  // in the sub-query select list.
4299  assert type instanceof RelRecordType;
4300  RelRecordType rec = (RelRecordType) type;
4301 
4302  RelDataType nodeType = rec.getFieldList().get(0).getType();
4303  nodeType = typeFactory.createTypeWithNullability(nodeType, true);
4304  fieldList.add(Pair.of(alias, nodeType));
4305  }
4306 
4316  protected RelDataType createTargetRowType(
4317  SqlValidatorTable table,
4318  SqlNodeList targetColumnList,
4319  boolean append) {
4320  RelDataType baseRowType = table.getRowType();
4321  if (targetColumnList == null) {
4322  return baseRowType;
4323  }
4324  List<RelDataTypeField> targetFields = baseRowType.getFieldList();
4325  final List<Map.Entry<String, RelDataType>> fields = new ArrayList<>();
4326  if (append) {
4327  for (RelDataTypeField targetField : targetFields) {
4328  fields.add(
4329  Pair.of(SqlUtil.deriveAliasFromOrdinal(fields.size()),
4330  targetField.getType()));
4331  }
4332  }
4333  final Set<Integer> assignedFields = new HashSet<>();
4334  final RelOptTable relOptTable = table instanceof RelOptTable
4335  ? ((RelOptTable) table) : null;
4336  for (SqlNode node : targetColumnList) {
4337  SqlIdentifier id = (SqlIdentifier) node;
4338  RelDataTypeField targetField =
4339  SqlValidatorUtil.getTargetField(
4340  baseRowType, typeFactory, id, catalogReader, relOptTable);
4341  if (targetField == null) {
4342  throw newValidationError(id,
4343  RESOURCE.unknownTargetColumn(id.toString()));
4344  }
4345  if (!assignedFields.add(targetField.getIndex())) {
4346  throw newValidationError(id,
4347  RESOURCE.duplicateTargetColumn(targetField.getName()));
4348  }
4349  fields.add(targetField);
4350  }
4351  return typeFactory.createStructType(fields);
4352  }
4353 
4354  public void validateInsert(SqlInsert insert) {
4355  final SqlValidatorNamespace targetNamespace = getNamespace(insert);
4356  validateNamespace(targetNamespace, unknownType);
4357  final RelOptTable relOptTable = SqlValidatorUtil.getRelOptTable(
4358  targetNamespace, catalogReader.unwrap(Prepare.CatalogReader.class), null, null);
4359  final SqlValidatorTable table = relOptTable == null
4360  ? targetNamespace.getTable()
4361  : relOptTable.unwrap(SqlValidatorTable.class);
4362 
4363  // INSERT has an optional column name list. If present then
4364  // reduce the rowtype to the columns specified. If not present
4365  // then the entire target rowtype is used.
4366  final RelDataType targetRowType =
4368  table,
4369  insert.getTargetColumnList(),
4370  false);
4371 
4372  final SqlNode source = insert.getSource();
4373  if (source instanceof SqlSelect) {
4374  final SqlSelect sqlSelect = (SqlSelect) source;
4375  validateSelect(sqlSelect, targetRowType);
4376  } else {
4377  final SqlValidatorScope scope = scopes.get(source);
4378  validateQuery(source, scope, targetRowType);
4379  }
4380 
4381  // REVIEW jvs 4-Dec-2008: In FRG-365, this namespace row type is
4382  // discarding the type inferred by inferUnknownTypes (which was invoked
4383  // from validateSelect above). It would be better if that information
4384  // were used here so that we never saw any untyped nulls during
4385  // checkTypeAssignment.
4386  final RelDataType sourceRowType = getNamespace(source).getRowType();
4387  final RelDataType logicalTargetRowType =
4388  getLogicalTargetRowType(targetRowType, insert);
4389  setValidatedNodeType(insert, logicalTargetRowType);
4390  final RelDataType logicalSourceRowType =
4391  getLogicalSourceRowType(sourceRowType, insert);
4392 
4393  final List<ColumnStrategy> strategies =
4394  table.unwrap(RelOptTable.class).getColumnStrategies();
4395 
4396  final RelDataType realTargetRowType = typeFactory.createStructType(
4397  logicalTargetRowType.getFieldList()
4398  .stream().filter(f -> strategies.get(f.getIndex()).canInsertInto())
4399  .collect(Collectors.toList()));
4400 
4401  final RelDataType targetRowTypeToValidate =
4402  logicalSourceRowType.getFieldCount() == logicalTargetRowType.getFieldCount()
4403  ? logicalTargetRowType
4404  : realTargetRowType;
4405 
4406  checkFieldCount(insert.getTargetTable(), table, strategies,
4407  targetRowTypeToValidate, realTargetRowType,
4408  source, logicalSourceRowType, logicalTargetRowType);
4409 
4410  checkTypeAssignment(scopes.get(source),
4411  table,
4412  logicalSourceRowType,
4413  targetRowTypeToValidate,
4414  insert);
4415 
4416  checkConstraint(table, source, logicalTargetRowType);
4417 
4418  validateAccess(insert.getTargetTable(), table, SqlAccessEnum.INSERT);
4419 
4420  // Refresh the insert row type to keep sync with source.
4421  setValidatedNodeType(insert, targetRowTypeToValidate);
4422  }
4423 
4431  private void checkConstraint(
4432  SqlValidatorTable validatorTable,
4433  SqlNode source,
4434  RelDataType targetRowType) {
4435  final ModifiableViewTable modifiableViewTable =
4436  validatorTable.unwrap(ModifiableViewTable.class);
4437  if (modifiableViewTable != null && source instanceof SqlCall) {
4438  final Table table = modifiableViewTable.unwrap(Table.class);
4439  final RelDataType tableRowType = table.getRowType(typeFactory);
4440  final List<RelDataTypeField> tableFields = tableRowType.getFieldList();
4441 
4442  // Get the mapping from column indexes of the underlying table
4443  // to the target columns and view constraints.
4444  final Map<Integer, RelDataTypeField> tableIndexToTargetField =
4445  SqlValidatorUtil.getIndexToFieldMap(tableFields, targetRowType);
4446  final Map<Integer, RexNode> projectMap =
4447  RelOptUtil.getColumnConstraints(modifiableViewTable, targetRowType, typeFactory);
4448 
4449  // Determine columns (indexed to the underlying table) that need
4450  // to be validated against the view constraint.
4451  final ImmutableBitSet targetColumns =
4452  ImmutableBitSet.of(tableIndexToTargetField.keySet());
4453  final ImmutableBitSet constrainedColumns =
4454  ImmutableBitSet.of(projectMap.keySet());
4455  final ImmutableBitSet constrainedTargetColumns =
4456  targetColumns.intersect(constrainedColumns);
4457 
4458  // Validate insert values against the view constraint.
4459  final List<SqlNode> values = ((SqlCall) source).getOperandList();
4460  for (final int colIndex : constrainedTargetColumns.asList()) {
4461  final String colName = tableFields.get(colIndex).getName();
4462  final RelDataTypeField targetField = tableIndexToTargetField.get(colIndex);
4463  for (SqlNode row : values) {
4464  final SqlCall call = (SqlCall) row;
4465  final SqlNode sourceValue = call.operand(targetField.getIndex());
4466  final ValidationError validationError =
4467  new ValidationError(sourceValue,
4468  RESOURCE.viewConstraintNotSatisfied(colName,
4469  Util.last(validatorTable.getQualifiedName())));
4470  RelOptUtil.validateValueAgainstConstraint(sourceValue,
4471  projectMap.get(colIndex), validationError);
4472  }
4473  }
4474  }
4475  }
4476 
4485  private void checkConstraint(
4486  SqlValidatorTable validatorTable,
4487  SqlUpdate update,
4488  RelDataType targetRowType) {
4489  final ModifiableViewTable modifiableViewTable =
4490  validatorTable.unwrap(ModifiableViewTable.class);
4491  if (modifiableViewTable != null) {
4492  final Table table = modifiableViewTable.unwrap(Table.class);
4493  final RelDataType tableRowType = table.getRowType(typeFactory);
4494 
4495  final Map<Integer, RexNode> projectMap =
4496  RelOptUtil.getColumnConstraints(modifiableViewTable, targetRowType,
4497  typeFactory);
4498  final Map<String, Integer> nameToIndex =
4499  SqlValidatorUtil.mapNameToIndex(tableRowType.getFieldList());
4500 
4501  // Validate update values against the view constraint.
4502  final List<SqlNode> targets = update.getTargetColumnList().getList();
4503  final List<SqlNode> sources = update.getSourceExpressionList().getList();
4504  for (final Pair<SqlNode, SqlNode> column : Pair.zip(targets, sources)) {
4505  final String columnName = ((SqlIdentifier) column.left).getSimple();
4506  final Integer columnIndex = nameToIndex.get(columnName);
4507  if (projectMap.containsKey(columnIndex)) {
4508  final RexNode columnConstraint = projectMap.get(columnIndex);
4509  final ValidationError validationError =
4510  new ValidationError(column.right,
4511  RESOURCE.viewConstraintNotSatisfied(columnName,
4512  Util.last(validatorTable.getQualifiedName())));
4513  RelOptUtil.validateValueAgainstConstraint(column.right,
4514  columnConstraint, validationError);
4515  }
4516  }
4517  }
4518  }
4519 
4535  private void checkFieldCount(SqlNode node, SqlValidatorTable table,
4536  List<ColumnStrategy> strategies, RelDataType targetRowTypeToValidate,
4537  RelDataType realTargetRowType, SqlNode source,
4538  RelDataType logicalSourceRowType, RelDataType logicalTargetRowType) {
4539  final int sourceFieldCount = logicalSourceRowType.getFieldCount();
4540  final int targetFieldCount = logicalTargetRowType.getFieldCount();
4541  final int targetRealFieldCount = realTargetRowType.getFieldCount();
4542  if (sourceFieldCount != targetFieldCount
4543  && sourceFieldCount != targetRealFieldCount) {
4544  // Allows the source row fields count to be equal with either
4545  // the logical or the real(excludes columns that can not insert into)
4546  // target row fields count.
4547  throw newValidationError(node,
4548  RESOURCE.unmatchInsertColumn(targetFieldCount, sourceFieldCount));
4549  }
4550  // Ensure that non-nullable fields are targeted.
4551  for (final RelDataTypeField field : table.getRowType().getFieldList()) {
4552  final RelDataTypeField targetField =
4553  targetRowTypeToValidate.getField(field.getName(), true, false);
4554  switch (strategies.get(field.getIndex())) {
4555  case NOT_NULLABLE:
4556  assert !field.getType().isNullable();
4557  if (targetField == null) {
4558  throw newValidationError(node,
4559  RESOURCE.columnNotNullable(field.getName()));
4560  }
4561  break;
4562  case NULLABLE:
4563  assert field.getType().isNullable();
4564  break;
4565  case VIRTUAL:
4566  case STORED:
4567  if (targetField != null
4568  && !isValuesWithDefault(source, targetField.getIndex())) {
4569  throw newValidationError(node,
4570  RESOURCE.insertIntoAlwaysGenerated(field.getName()));
4571  }
4572  }
4573  }
4574  }
4575 
4578  private boolean isValuesWithDefault(SqlNode source, int column) {
4579  switch (source.getKind()) {
4580  case VALUES:
4581  for (SqlNode operand : ((SqlCall) source).getOperandList()) {
4582  if (!isRowWithDefault(operand, column)) {
4583  return false;
4584  }
4585  }
4586  return true;
4587  }
4588  return false;
4589  }
4590 
4591  private boolean isRowWithDefault(SqlNode operand, int column) {
4592  switch (operand.getKind()) {
4593  case ROW:
4594  final SqlCall row = (SqlCall) operand;
4595  return row.getOperandList().size() >= column
4596  && row.getOperandList().get(column).getKind() == SqlKind.DEFAULT;
4597  }
4598  return false;
4599  }
4600 
4601  protected RelDataType getLogicalTargetRowType(
4602  RelDataType targetRowType,
4603  SqlInsert insert) {
4604  if (insert.getTargetColumnList() == null
4605  && this.config.sqlConformance().isInsertSubsetColumnsAllowed()) {
4606  // Target an implicit subset of columns.
4607  final SqlNode source = insert.getSource();
4608  final RelDataType sourceRowType = getNamespace(source).getRowType();
4609  final RelDataType logicalSourceRowType =
4610  getLogicalSourceRowType(sourceRowType, insert);
4611  final RelDataType implicitTargetRowType =
4612  typeFactory.createStructType(
4613  targetRowType.getFieldList()
4614  .subList(0, logicalSourceRowType.getFieldCount()));
4615  final SqlValidatorNamespace targetNamespace = getNamespace(insert);
4616  validateNamespace(targetNamespace, implicitTargetRowType);
4617  return implicitTargetRowType;
4618  } else {
4619  // Either the set of columns are explicitly targeted, or target the full
4620  // set of columns.
4621  return targetRowType;
4622  }
4623  }
4624 
4625  protected RelDataType getLogicalSourceRowType(
4626  RelDataType sourceRowType,
4627  SqlInsert insert) {
4628  return sourceRowType;
4629  }
4630 
4645  protected void checkTypeAssignment(
4646  SqlValidatorScope sourceScope,
4647  SqlValidatorTable table,
4648  RelDataType sourceRowType,
4649  RelDataType targetRowType,
4650  final SqlNode query) {
4651  // NOTE jvs 23-Feb-2006: subclasses may allow for extra targets
4652  // representing system-maintained columns, so stop after all sources
4653  // matched
4654  boolean isUpdateModifiableViewTable = false;
4655  if (query instanceof SqlUpdate) {
4656  final SqlNodeList targetColumnList = ((SqlUpdate) query).getTargetColumnList();
4657  if (targetColumnList != null) {
4658  final int targetColumnCnt = targetColumnList.size();
4659  targetRowType = SqlTypeUtil.extractLastNFields(typeFactory, targetRowType,
4660  targetColumnCnt);
4661  sourceRowType = SqlTypeUtil.extractLastNFields(typeFactory, sourceRowType,
4662  targetColumnCnt);
4663  }
4664  isUpdateModifiableViewTable = table.unwrap(ModifiableViewTable.class) != null;
4665  }
4666  if (SqlTypeUtil.equalAsStructSansNullability(typeFactory,
4667  sourceRowType,
4668  targetRowType,
4669  null)) {
4670  // Returns early if source and target row type equals sans nullability.
4671  return;
4672  }
4673  if (config.typeCoercionEnabled() && !isUpdateModifiableViewTable) {
4674  // Try type coercion first if implicit type coercion is allowed.
4675  boolean coerced = typeCoercion.querySourceCoercion(sourceScope,
4676  sourceRowType,
4677  targetRowType,
4678  query);
4679  if (coerced) {
4680  return;
4681  }
4682  }
4683 
4684  // Fall back to default behavior: compare the type families.
4685  List<RelDataTypeField> sourceFields = sourceRowType.getFieldList();
4686  List<RelDataTypeField> targetFields = targetRowType.getFieldList();
4687  final int sourceCount = sourceFields.size();
4688  for (int i = 0; i < sourceCount; ++i) {
4689  RelDataType sourceType = sourceFields.get(i).getType();
4690  RelDataType targetType = targetFields.get(i).getType();
4691  if (!SqlTypeUtil.canAssignFrom(targetType, sourceType)) {
4692  SqlNode node = getNthExpr(query, i, sourceCount);
4693  if (node instanceof SqlDynamicParam) {
4694  continue;
4695  }
4696  String targetTypeString;
4697  String sourceTypeString;
4698  if (SqlTypeUtil.areCharacterSetsMismatched(
4699  sourceType,
4700  targetType)) {
4701  sourceTypeString = sourceType.getFullTypeString();
4702  targetTypeString = targetType.getFullTypeString();
4703  } else {
4704  sourceTypeString = sourceType.toString();
4705  targetTypeString = targetType.toString();
4706  }
4707  throw newValidationError(node,
4708  RESOURCE.typeNotAssignable(
4709  targetFields.get(i).getName(), targetTypeString,
4710  sourceFields.get(i).getName(), sourceTypeString));
4711  }
4712  }
4713  }
4714 
4723  private SqlNode getNthExpr(SqlNode query, int ordinal, int sourceCount) {
4724  if (query instanceof SqlInsert) {
4725  SqlInsert insert = (SqlInsert) query;
4726  if (insert.getTargetColumnList() != null) {
4727  return insert.getTargetColumnList().get(ordinal);
4728  } else {
4729  return getNthExpr(
4730  insert.getSource(),
4731  ordinal,
4732  sourceCount);
4733  }
4734  } else if (query instanceof SqlUpdate) {
4735  SqlUpdate update = (SqlUpdate) query;
4736  if (update.getSourceExpressionList() != null) {
4737  return update.getSourceExpressionList().get(ordinal);
4738  } else {
4739  return getNthExpr(
4740  update.getSourceSelect(),
4741  ordinal,
4742  sourceCount);
4743  }
4744  } else if (query instanceof SqlSelect) {
4745  SqlSelect select = (SqlSelect) query;
4746  if (select.getSelectList().size() == sourceCount) {
4747  return select.getSelectList().get(ordinal);
4748  } else {
4749  return query; // give up
4750  }
4751  } else {
4752  return query; // give up
4753  }
4754  }
4755 
4756  public void validateDelete(SqlDelete call) {
4757  final SqlSelect sqlSelect = call.getSourceSelect();
4758  validateSelect(sqlSelect, unknownType);
4759 
4760  final SqlValidatorNamespace targetNamespace = getNamespace(call);
4761  validateNamespace(targetNamespace, unknownType);
4762  final SqlValidatorTable table = targetNamespace.getTable();
4763 
4764  validateAccess(call.getTargetTable(), table, SqlAccessEnum.DELETE);
4765  }
4766 
4767  public void validateUpdate(SqlUpdate call) {
4768  final SqlValidatorNamespace targetNamespace = getNamespace(call);
4769  validateNamespace(targetNamespace, unknownType);
4770  final RelOptTable relOptTable = SqlValidatorUtil.getRelOptTable(
4771  targetNamespace, catalogReader.unwrap(Prepare.CatalogReader.class), null, null);
4772  final SqlValidatorTable table = relOptTable == null
4773  ? targetNamespace.getTable()
4774  : relOptTable.unwrap(SqlValidatorTable.class);
4775 
4776  final RelDataType targetRowType =
4778  table,
4779  call.getTargetColumnList(),
4780  true);
4781 
4782  final SqlSelect select = call.getSourceSelect();
4783  validateSelect(select, targetRowType);
4784 
4785  final RelDataType sourceRowType = getValidatedNodeType(select);
4786  checkTypeAssignment(scopes.get(select),
4787  table,
4788  sourceRowType,
4789  targetRowType,
4790  call);
4791 
4792  checkConstraint(table, call, targetRowType);
4793 
4794  validateAccess(call.getTargetTable(), table, SqlAccessEnum.UPDATE);
4795  }
4796 
4797  public void validateMerge(SqlMerge call) {
4798  SqlSelect sqlSelect = call.getSourceSelect();
4799  // REVIEW zfong 5/25/06 - Does an actual type have to be passed into
4800  // validateSelect()?
4801 
4802  // REVIEW jvs 6-June-2006: In general, passing unknownType like
4803  // this means we won't be able to correctly infer the types
4804  // for dynamic parameter markers (SET x = ?). But
4805  // maybe validateUpdate and validateInsert below will do
4806  // the job?
4807 
4808  // REVIEW ksecretan 15-July-2011: They didn't get a chance to
4809  // since validateSelect() would bail.
4810  // Let's use the update/insert targetRowType when available.
4811  IdentifierNamespace targetNamespace =
4812  (IdentifierNamespace) getNamespace(call.getTargetTable());
4813  validateNamespace(targetNamespace, unknownType);
4814 
4815  SqlValidatorTable table = targetNamespace.getTable();
4816  validateAccess(call.getTargetTable(), table, SqlAccessEnum.UPDATE);
4817 
4818  RelDataType targetRowType = unknownType;
4819 
4820  if (call.getUpdateCall() != null) {
4821  targetRowType = createTargetRowType(
4822  table,
4823  call.getUpdateCall().getTargetColumnList(),
4824  true);
4825  }
4826  if (call.getInsertCall() != null) {
4827  targetRowType = createTargetRowType(
4828  table,
4829  call.getInsertCall().getTargetColumnList(),
4830  false);
4831  }
4832 
4833  validateSelect(sqlSelect, targetRowType);
4834 
4835  if (call.getUpdateCall() != null) {
4836  validateUpdate(call.getUpdateCall());
4837  }
4838  if (call.getInsertCall() != null) {
4839  validateInsert(call.getInsertCall());
4840  }
4841  }
4842 
4849  private void validateAccess(
4850  SqlNode node,
4851  SqlValidatorTable table,
4852  SqlAccessEnum requiredAccess) {
4853  if (table != null) {
4854  SqlAccessType access = table.getAllowedAccess();
4855  if (!access.allowsAccess(requiredAccess)) {
4856  throw newValidationError(node,
4857  RESOURCE.accessNotAllowed(requiredAccess.name(),
4858  table.getQualifiedName().toString()));
4859  }
4860  }
4861  }
4862 
4870  private void validateSnapshot(
4871  SqlNode node,
4872  SqlValidatorScope scope,
4873  SqlValidatorNamespace ns) {
4874  if (node.getKind() == SqlKind.SNAPSHOT) {
4875  SqlSnapshot snapshot = (SqlSnapshot) node;
4876  SqlNode period = snapshot.getPeriod();
4877  RelDataType dataType = deriveType(scope, period);
4878  if (dataType.getSqlTypeName() != SqlTypeName.TIMESTAMP) {
4879  throw newValidationError(period,
4880  Static.RESOURCE.illegalExpressionForTemporal(dataType.getSqlTypeName().getName()));
4881  }
4882  if (!ns.getTable().isTemporal()) {
4883  List<String> qualifiedName = ns.getTable().getQualifiedName();
4884  String tableName = qualifiedName.get(qualifiedName.size() - 1);
4885  throw newValidationError(snapshot.getTableRef(),
4886  Static.RESOURCE.notTemporalTable(tableName));
4887  }
4888  }
4889  }
4890 
4898  protected void validateValues(
4899  SqlCall node,
4900  RelDataType targetRowType,
4901  final SqlValidatorScope scope) {
4902  assert node.getKind() == SqlKind.VALUES;
4903 
4904  final List<SqlNode> operands = node.getOperandList();
4905  for (SqlNode operand : operands) {
4906  if (!(operand.getKind() == SqlKind.ROW)) {
4907  throw Util.needToImplement(
4908  "Values function where operands are scalars");
4909  }
4910 
4911  SqlCall rowConstructor = (SqlCall) operand;
4912  if (this.config.sqlConformance().isInsertSubsetColumnsAllowed()
4913  && targetRowType.isStruct()
4914  && rowConstructor.operandCount() < targetRowType.getFieldCount()) {
4915  targetRowType =
4916  typeFactory.createStructType(
4917  targetRowType.getFieldList()
4918  .subList(0, rowConstructor.operandCount()));
4919  } else if (targetRowType.isStruct()
4920  && rowConstructor.operandCount() != targetRowType.getFieldCount()) {
4921  return;
4922  }
4923 
4925  targetRowType,
4926  scope,
4927  rowConstructor);
4928 
4929  if (targetRowType.isStruct()) {
4930  for (Pair<SqlNode, RelDataTypeField> pair
4931  : Pair.zip(rowConstructor.getOperandList(),
4932  targetRowType.getFieldList())) {
4933  if (!pair.right.getType().isNullable()
4934  && SqlUtil.isNullLiteral(pair.left, false)) {
4935  throw newValidationError(node,
4936  RESOURCE.columnNotNullable(pair.right.getName()));
4937  }
4938  }
4939  }
4940  }
4941 
4942  for (SqlNode operand : operands) {
4943  operand.validate(this, scope);
4944  }
4945 
4946  // validate that all row types have the same number of columns
4947  // and that expressions in each column are compatible.
4948  // A values expression is turned into something that looks like
4949  // ROW(type00, type01,...), ROW(type11,...),...
4950  final int rowCount = operands.size();
4951  if (rowCount >= 2) {
4952  SqlCall firstRow = (SqlCall) operands.get(0);
4953  final int columnCount = firstRow.operandCount();
4954 
4955  // 1. check that all rows have the same cols length
4956  for (SqlNode operand : operands) {
4957  SqlCall thisRow = (SqlCall) operand;
4958  if (columnCount != thisRow.operandCount()) {
4959  throw newValidationError(node,
4960  RESOURCE.incompatibleValueType(
4961  SqlStdOperatorTable.VALUES.getName()));
4962  }
4963  }
4964 
4965  // 2. check if types at i:th position in each row are compatible
4966  for (int col = 0; col < columnCount; col++) {
4967  final int c = col;
4968  final RelDataType type =
4969  typeFactory.leastRestrictive(
4970  new AbstractList<RelDataType>() {
4971  public RelDataType get(int row) {
4972  SqlCall thisRow = (SqlCall) operands.get(row);
4973  return deriveType(scope, thisRow.operand(c));
4974  }
4975 
4976  public int size() {
4977  return rowCount;
4978  }
4979  });
4980 
4981  if (null == type) {
4982  throw newValidationError(node,
4983  RESOURCE.incompatibleValueType(
4984  SqlStdOperatorTable.VALUES.getName()));
4985  }
4986  }
4987  }
4988  }
4989 
4990  public void validateDataType(SqlDataTypeSpec dataType) {
4991  }
4992 
4993  public void validateDynamicParam(SqlDynamicParam dynamicParam) {
4994  }
4995 
5000  private class ValidationError implements Supplier<CalciteContextException> {
5001  private final SqlNode sqlNode;
5002  private final Resources.ExInst<SqlValidatorException> validatorException;
5003 
5005  Resources.ExInst<SqlValidatorException> validatorException) {
5006  this.sqlNode = sqlNode;
5007  this.validatorException = validatorException;
5008  }
5009 
5010  public CalciteContextException get() {
5012  }
5013  }
5014 
5020  implements Function2<SqlNode, Resources.ExInst<SqlValidatorException>,
5021  CalciteContextException> {
5022  @Override public CalciteContextException apply(
5023  SqlNode v0, Resources.ExInst<SqlValidatorException> v1) {
5024  return newValidationError(v0, v1);
5025  }
5026  }
5027 
5029  return validationErrorFunction;
5030  }
5031 
5032  public CalciteContextException newValidationError(SqlNode node,
5033  Resources.ExInst<SqlValidatorException> e) {
5034  assert node != null;
5035  final SqlParserPos pos = node.getParserPosition();
5036  return SqlUtil.newContextException(pos, e);
5037  }
5038 
5039  protected SqlWindow getWindowByName(
5040  SqlIdentifier id,
5041  SqlValidatorScope scope) {
5042  SqlWindow window = null;
5043  if (id.isSimple()) {
5044  final String name = id.getSimple();
5045  window = scope.lookupWindow(name);
5046  }
5047  if (window == null) {
5048  throw newValidationError(id, RESOURCE.windowNotFound(id.toString()));
5049  }
5050  return window;
5051  }
5052 
5053  public SqlWindow resolveWindow(
5054  SqlNode windowOrRef,
5055  SqlValidatorScope scope) {
5056  SqlWindow window;
5057  if (windowOrRef instanceof SqlIdentifier) {
5058  window = getWindowByName((SqlIdentifier) windowOrRef, scope);
5059  } else {
5060  window = (SqlWindow) windowOrRef;
5061  }
5062  while (true) {
5063  final SqlIdentifier refId = window.getRefName();
5064  if (refId == null) {
5065  break;
5066  }
5067  final String refName = refId.getSimple();
5068  SqlWindow refWindow = scope.lookupWindow(refName);
5069  if (refWindow == null) {
5070  throw newValidationError(refId, RESOURCE.windowNotFound(refName));
5071  }
5072  window = window.overlay(refWindow, this);
5073  }
5074 
5075  return window;
5076  }
5077 
5078  public SqlNode getOriginal(SqlNode expr) {
5079  SqlNode original = originalExprs.get(expr);
5080  if (original == null) {
5081  original = expr;
5082  }
5083  return original;
5084  }
5085 
5086  public void setOriginal(SqlNode expr, SqlNode original) {
5087  // Don't overwrite the original original.
5088  originalExprs.putIfAbsent(expr, original);
5089  }
5090 
5091  SqlValidatorNamespace lookupFieldNamespace(RelDataType rowType, String name) {
5092  final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
5093  final RelDataTypeField field = nameMatcher.field(rowType, name);
5094  if (field == null) {
5095  return null;
5096  }
5097  return new FieldNamespace(this, field.getType());
5098  }
5099 
5100  public void validateWindow(
5101  SqlNode windowOrId,
5102  SqlValidatorScope scope,
5103  SqlCall call) {
5104  // Enable nested aggregates with window aggregates (OVER operator)
5105  inWindow = true;
5106 
5107  final SqlWindow targetWindow;
5108  switch (windowOrId.getKind()) {
5109  case IDENTIFIER:
5110  // Just verify the window exists in this query. It will validate
5111  // when the definition is processed
5112  targetWindow = getWindowByName((SqlIdentifier) windowOrId, scope);
5113  break;
5114  case WINDOW:
5115  targetWindow = (SqlWindow) windowOrId;
5116  break;
5117  default:
5118  throw Util.unexpected(windowOrId.getKind());
5119  }
5120 
5121  assert targetWindow.getWindowCall() == null;
5122  targetWindow.setWindowCall(call);
5123  targetWindow.validate(this, scope);
5124  targetWindow.setWindowCall(null);
5125  call.validate(this, scope);
5126 
5127  validateAggregateParams(call, null, null, scope);
5128 
5129  // Disable nested aggregates post validation
5130  inWindow = false;
5131  }
5132 
5133  @Override public void validateMatchRecognize(SqlCall call) {
5134  final SqlMatchRecognize matchRecognize = (SqlMatchRecognize) call;
5135  final MatchRecognizeScope scope =
5136  (MatchRecognizeScope) getMatchRecognizeScope(matchRecognize);
5137 
5138  final MatchRecognizeNamespace ns =
5139  getNamespace(call).unwrap(MatchRecognizeNamespace.class);
5140  assert ns.rowType == null;
5141 
5142  // rows per match
5143  final SqlLiteral rowsPerMatch = matchRecognize.getRowsPerMatch();
5144  final boolean allRows = rowsPerMatch != null
5145  && rowsPerMatch.getValue()
5146  == SqlMatchRecognize.RowsPerMatchOption.ALL_ROWS;
5147 
5148  final RelDataTypeFactory.Builder typeBuilder = typeFactory.builder();
5149 
5150  // parse PARTITION BY column
5151  SqlNodeList partitionBy = matchRecognize.getPartitionList();
5152  if (partitionBy != null) {
5153  for (SqlNode node : partitionBy) {
5154  SqlIdentifier identifier = (SqlIdentifier) node;
5155  identifier.validate(this, scope);
5156  RelDataType type = deriveType(scope, identifier);
5157  String name = identifier.names.get(1);
5158  typeBuilder.add(name, type);
5159  }
5160  }
5161 
5162  // parse ORDER BY column
5163  SqlNodeList orderBy = matchRecognize.getOrderList();
5164  if (orderBy != null) {
5165  for (SqlNode node : orderBy) {
5166  node.validate(this, scope);
5167  SqlIdentifier identifier;
5168  if (node instanceof SqlBasicCall) {
5169  identifier = (SqlIdentifier) ((SqlBasicCall) node).getOperands()[0];
5170  } else {
5171  identifier = (SqlIdentifier) node;
5172  }
5173 
5174  if (allRows) {
5175  RelDataType type = deriveType(scope, identifier);
5176  String name = identifier.names.get(1);
5177  if (!typeBuilder.nameExists(name)) {
5178  typeBuilder.add(name, type);
5179  }
5180  }
5181  }
5182  }
5183 
5184  if (allRows) {
5185  final SqlValidatorNamespace sqlNs =
5186  getNamespace(matchRecognize.getTableRef());
5187  final RelDataType inputDataType = sqlNs.getRowType();
5188  for (RelDataTypeField fs : inputDataType.getFieldList()) {
5189  if (!typeBuilder.nameExists(fs.getName())) {
5190  typeBuilder.add(fs);
5191  }
5192  }
5193  }
5194 
5195  // retrieve pattern variables used in pattern and subset
5196  SqlNode pattern = matchRecognize.getPattern();
5197  PatternVarVisitor visitor = new PatternVarVisitor(scope);
5198  pattern.accept(visitor);
5199 
5200  SqlLiteral interval = matchRecognize.getInterval();
5201  if (interval != null) {
5202  interval.validate(this, scope);
5203  if (((SqlIntervalLiteral) interval).signum() < 0) {
5204  throw newValidationError(interval,
5205  RESOURCE.intervalMustBeNonNegative(interval.toValue()));
5206  }
5207  if (orderBy == null || orderBy.size() == 0) {
5208  throw newValidationError(interval,
5209  RESOURCE.cannotUseWithinWithoutOrderBy());
5210  }
5211 
5212  SqlNode firstOrderByColumn = orderBy.getList().get(0);
5213  SqlIdentifier identifier;
5214  if (firstOrderByColumn instanceof SqlBasicCall) {
5215  identifier = (SqlIdentifier) ((SqlBasicCall) firstOrderByColumn).getOperands()[0];
5216  } else {
5217  identifier = (SqlIdentifier) firstOrderByColumn;
5218  }
5219  RelDataType firstOrderByColumnType = deriveType(scope, identifier);
5220  if (firstOrderByColumnType.getSqlTypeName() != SqlTypeName.TIMESTAMP) {
5221  throw newValidationError(interval,
5222  RESOURCE.firstColumnOfOrderByMustBeTimestamp());
5223  }
5224 
5225  SqlNode expand = expand(interval, scope);
5226  RelDataType type = deriveType(scope, expand);
5227  setValidatedNodeType(interval, type);
5228  }
5229 
5230  validateDefinitions(matchRecognize, scope);
5231 
5232  SqlNodeList subsets = matchRecognize.getSubsetList();
5233  if (subsets != null && subsets.size() > 0) {
5234  for (SqlNode node : subsets) {
5235  List<SqlNode> operands = ((SqlCall) node).getOperandList();
5236  String leftString = ((SqlIdentifier) operands.get(0)).getSimple();
5237  if (scope.getPatternVars().contains(leftString)) {
5238  throw newValidationError(operands.get(0),
5239  RESOURCE.patternVarAlreadyDefined(leftString));
5240  }
5241  scope.addPatternVar(leftString);
5242  for (SqlNode right : (SqlNodeList) operands.get(1)) {
5243  SqlIdentifier id = (SqlIdentifier) right;
5244  if (!scope.getPatternVars().contains(id.getSimple())) {
5245  throw newValidationError(id,
5246  RESOURCE.unknownPattern(id.getSimple()));
5247  }
5248  scope.addPatternVar(id.getSimple());
5249  }
5250  }
5251  }
5252 
5253  // validate AFTER ... SKIP TO
5254  final SqlNode skipTo = matchRecognize.getAfter();
5255  if (skipTo instanceof SqlCall) {
5256  final SqlCall skipToCall = (SqlCall) skipTo;
5257  final SqlIdentifier id = skipToCall.operand(0);
5258  if (!scope.getPatternVars().contains(id.getSimple())) {
5259  throw newValidationError(id,
5260  RESOURCE.unknownPattern(id.getSimple()));
5261  }
5262  }
5263 
5264  List<Map.Entry<String, RelDataType>> measureColumns =
5265  validateMeasure(matchRecognize, scope, allRows);
5266  for (Map.Entry<String, RelDataType> c : measureColumns) {
5267  if (!typeBuilder.nameExists(c.getKey())) {
5268  typeBuilder.add(c.getKey(), c.getValue());
5269  }
5270  }
5271 
5272  final RelDataType rowType = typeBuilder.build();
5273  if (matchRecognize.getMeasureList().size() == 0) {
5274  ns.setType(getNamespace(matchRecognize.getTableRef()).getRowType());
5275  } else {
5276  ns.setType(rowType);
5277  }
5278  }
5279 
5280  private List<Map.Entry<String, RelDataType>> validateMeasure(SqlMatchRecognize mr,
5281  MatchRecognizeScope scope, boolean allRows) {
5282  final List<String> aliases = new ArrayList<>();
5283  final List<SqlNode> sqlNodes = new ArrayList<>();
5284  final SqlNodeList measures = mr.getMeasureList();
5285  final List<Map.Entry<String, RelDataType>> fields = new ArrayList<>();
5286 
5287  for (SqlNode measure : measures) {
5288  assert measure instanceof SqlCall;
5289  final String alias = deriveAlias(measure, aliases.size());
5290  aliases.add(alias);
5291 
5292  SqlNode expand = expand(measure, scope);
5293  expand = navigationInMeasure(expand, allRows);
5294  setOriginal(expand, measure);
5295 
5296  inferUnknownTypes(unknownType, scope, expand);
5297  final RelDataType type = deriveType(scope, expand);
5299 
5300  fields.add(Pair.of(alias, type));
5301  sqlNodes.add(
5302  SqlStdOperatorTable.AS.createCall(SqlParserPos.ZERO, expand,
5303  new SqlIdentifier(alias, SqlParserPos.ZERO)));
5304  }
5305 
5306  SqlNodeList list = new SqlNodeList(sqlNodes, measures.getParserPosition());
5307  inferUnknownTypes(unknownType, scope, list);
5308 
5309  for (SqlNode node : list) {
5310  validateExpr(node, scope);
5311  }
5312 
5313  mr.setOperand(SqlMatchRecognize.OPERAND_MEASURES, list);
5314 
5315  return fields;
5316  }
5317 
5318  private SqlNode navigationInMeasure(SqlNode node, boolean allRows) {
5319  final Set<String> prefix = node.accept(new PatternValidator(true));
5320  Util.discard(prefix);
5321  final List<SqlNode> ops = ((SqlCall) node).getOperandList();
5322 
5323  final SqlOperator defaultOp =
5324  allRows ? SqlStdOperatorTable.RUNNING : SqlStdOperatorTable.FINAL;
5325  final SqlNode op0 = ops.get(0);
5326  if (!isRunningOrFinal(op0.getKind())
5327  || !allRows && op0.getKind() == SqlKind.RUNNING) {
5328  SqlNode newNode = defaultOp.createCall(SqlParserPos.ZERO, op0);
5329  node = SqlStdOperatorTable.AS.createCall(SqlParserPos.ZERO, newNode, ops.get(1));
5330  }
5331 
5332  node = new NavigationExpander().go(node);
5333  return node;
5334  }
5335 
5336  private void validateDefinitions(SqlMatchRecognize mr,
5337  MatchRecognizeScope scope) {
5338  final Set<String> aliases = catalogReader.nameMatcher().createSet();
5339  for (SqlNode item : mr.getPatternDefList().getList()) {
5340  final String alias = alias(item);
5341  if (!aliases.add(alias)) {
5342  throw newValidationError(item,
5343  Static.RESOURCE.patternVarAlreadyDefined(alias));
5344  }
5345  scope.addPatternVar(alias);
5346  }
5347 
5348  final List<SqlNode> sqlNodes = new ArrayList<>();
5349  for (SqlNode item : mr.getPatternDefList().getList()) {
5350  final String alias = alias(item);
5351  SqlNode expand = expand(item, scope);
5352  expand = navigationInDefine(expand, alias);
5353  setOriginal(expand, item);
5354 
5355  inferUnknownTypes(booleanType, scope, expand);
5356  expand.validate(this, scope);
5357 
5358  // Some extra work need required here.
5359  // In PREV, NEXT, FINAL and LAST, only one pattern variable is allowed.
5360  sqlNodes.add(
5361  SqlStdOperatorTable.AS.createCall(SqlParserPos.ZERO, expand,
5362  new SqlIdentifier(alias, SqlParserPos.ZERO)));
5363 
5364  final RelDataType type = deriveType(scope, expand);
5365  if (!SqlTypeUtil.inBooleanFamily(type)) {
5366  throw newValidationError(expand, RESOURCE.condMustBeBoolean("DEFINE"));
5367  }
5368  setValidatedNodeType(item, type);
5369  }
5370 
5371  SqlNodeList list =
5372  new SqlNodeList(sqlNodes, mr.getPatternDefList().getParserPosition());
5373  inferUnknownTypes(unknownType, scope, list);
5374  for (SqlNode node : list) {
5375  validateExpr(node, scope);
5376  }
5377  mr.setOperand(SqlMatchRecognize.OPERAND_PATTERN_DEFINES, list);
5378  }
5379 
5381  private static String alias(SqlNode item) {
5382  assert item instanceof SqlCall;
5383  assert item.getKind() == SqlKind.AS;
5384  final SqlIdentifier identifier = ((SqlCall) item).operand(1);
5385  return identifier.getSimple();
5386  }
5387 
5391  private SqlNode navigationInDefine(SqlNode node, String alpha) {
5392  Set<String> prefix = node.accept(new PatternValidator(false));
5393  Util.discard(prefix);
5394  node = new NavigationExpander().go(node);
5395  node = new NavigationReplacer(alpha).go(node);
5396  return node;
5397  }
5398 
5399  public void validateAggregateParams(SqlCall aggCall, SqlNode filter,
5400  SqlNodeList orderList, SqlValidatorScope scope) {
5401  // For "agg(expr)", expr cannot itself contain aggregate function
5402  // invocations. For example, "SUM(2 * MAX(x))" is illegal; when
5403  // we see it, we'll report the error for the SUM (not the MAX).
5404  // For more than one level of nesting, the error which results
5405  // depends on the traversal order for validation.
5406  //
5407  // For a windowed aggregate "agg(expr)", expr can contain an aggregate
5408  // function. For example,
5409  // SELECT AVG(2 * MAX(x)) OVER (PARTITION BY y)
5410  // FROM t
5411  // GROUP BY y
5412  // is legal. Only one level of nesting is allowed since non-windowed
5413  // aggregates cannot nest aggregates.
5414 
5415  // Store nesting level of each aggregate. If an aggregate is found at an invalid
5416  // nesting level, throw an assert.
5417  final AggFinder a;
5418  if (inWindow) {
5419  a = overFinder;
5420  } else {
5421  a = aggOrOverFinder;
5422  }
5423 
5424  for (SqlNode param : aggCall.getOperandList()) {
5425  if (a.findAgg(param) != null) {
5426  throw newValidationError(aggCall, RESOURCE.nestedAggIllegal());
5427  }
5428  }
5429  if (filter != null) {
5430  if (a.findAgg(filter) != null) {
5431  throw newValidationError(filter, RESOURCE.aggregateInFilterIllegal());
5432  }
5433  }
5434  if (orderList != null) {
5435  for (SqlNode param : orderList) {
5436  if (a.findAgg(param) != null) {
5437  throw newValidationError(aggCall,
5438  RESOURCE.aggregateInWithinGroupIllegal());
5439  }
5440  }
5441  }
5442 
5443  final SqlAggFunction op = (SqlAggFunction) aggCall.getOperator();
5444  switch (op.requiresGroupOrder()) {
5445  case MANDATORY:
5446  if (orderList == null || orderList.size() == 0) {
5447  throw newValidationError(aggCall,
5448  RESOURCE.aggregateMissingWithinGroupClause(op.getName()));
5449  }
5450  break;
5451  case OPTIONAL:
5452  break;
5453  case IGNORED:
5454  // rewrite the order list to empty
5455  if (orderList != null) {
5456  orderList.getList().clear();
5457  }
5458  break;
5459  case FORBIDDEN:
5460  if (orderList != null && orderList.size() != 0) {
5461  throw newValidationError(aggCall,
5462  RESOURCE.withinGroupClauseIllegalInAggregate(op.getName()));
5463  }
5464  break;
5465  default:
5466  throw new AssertionError(op);
5467  }
5468  }
5469 
5470  public void validateCall(
5471  SqlCall call,
5472  SqlValidatorScope scope) {
5473  final SqlOperator operator = call.getOperator();
5474  if ((call.operandCount() == 0)
5475  && (operator.getSyntax() == SqlSyntax.FUNCTION_ID)
5476  && !call.isExpanded()
5477  && !this.config.sqlConformance().allowNiladicParentheses()) {
5478  // For example, "LOCALTIME()" is illegal. (It should be
5479  // "LOCALTIME", which would have been handled as a
5480  // SqlIdentifier.)
5481  throw handleUnresolvedFunction(call, (SqlFunction) operator,
5482  ImmutableList.of(), null);
5483  }
5484 
5485  SqlValidatorScope operandScope = scope.getOperandScope(call);
5486 
5487  if (operator instanceof SqlFunction
5488  && ((SqlFunction) operator).getFunctionType()
5489  == SqlFunctionCategory.MATCH_RECOGNIZE
5490  && !(operandScope instanceof MatchRecognizeScope)) {
5491  throw newValidationError(call,
5492  Static.RESOURCE.functionMatchRecognizeOnly(call.toString()));
5493  }
5494  // Delegate validation to the operator.
5495  operator.validateCall(call, this, scope, operandScope);
5496  }
5497 
5506  protected void validateFeature(
5507  Feature feature,
5508  SqlParserPos context) {
5509  // By default, do nothing except to verify that the resource
5510  // represents a real feature definition.
5511  assert feature.getProperties().get("FeatureDefinition") != null;
5512  }
5513 
5514  public SqlNode expandSelectExpr(SqlNode expr,
5515  SelectScope scope, SqlSelect select) {
5516  final Expander expander = new SelectExpander(this, scope, select);
5517  final SqlNode newExpr = expr.accept(expander);
5518  if (expr != newExpr) {
5519  setOriginal(newExpr, expr);
5520  }
5521  return newExpr;
5522  }
5523 
5524  public SqlNode expand(SqlNode expr, SqlValidatorScope scope) {
5525  final Expander expander = new Expander(this, scope);
5526  SqlNode newExpr = expr.accept(expander);
5527  if (expr != newExpr) {
5528  setOriginal(newExpr, expr);
5529  }
5530  return newExpr;
5531  }
5532 
5533  public SqlNode expandGroupByOrHavingExpr(SqlNode expr,
5534  SqlValidatorScope scope, SqlSelect select, boolean havingExpression) {
5535  final Expander expander = new ExtendedExpander(this, scope, select, expr,
5536  havingExpression);
5537  SqlNode newExpr = expr.accept(expander);
5538  if (expr != newExpr) {
5539  setOriginal(newExpr, expr);
5540  }
5541  return newExpr;
5542  }
5543 
5544  public boolean isSystemField(RelDataTypeField field) {
5545  return false;
5546  }
5547 
5548  public List<List<String>> getFieldOrigins(SqlNode sqlQuery) {
5549  if (sqlQuery instanceof SqlExplain) {
5550  return Collections.emptyList();
5551  }
5552  final RelDataType rowType = getValidatedNodeType(sqlQuery);
5553  final int fieldCount = rowType.getFieldCount();
5554  if (!sqlQuery.isA(SqlKind.QUERY)) {
5555  return Collections.nCopies(fieldCount, null);
5556  }
5557  final List<List<String>> list = new ArrayList<>();
5558  for (int i = 0; i < fieldCount; i++) {
5559  list.add(getFieldOrigin(sqlQuery, i));
5560  }
5561  return ImmutableNullableList.copyOf(list);
5562  }
5563 
5564  private List<String> getFieldOrigin(SqlNode sqlQuery, int i) {
5565  if (sqlQuery instanceof SqlSelect) {
5566  SqlSelect sqlSelect = (SqlSelect) sqlQuery;
5567  final SelectScope scope = getRawSelectScope(sqlSelect);
5568  final List<SqlNode> selectList = scope.getExpandedSelectList();
5569  final SqlNode selectItem = stripAs(selectList.get(i));
5570  if (selectItem instanceof SqlIdentifier) {
5571  final SqlQualified qualified =
5572  scope.fullyQualify((SqlIdentifier) selectItem);
5573  SqlValidatorNamespace namespace = qualified.namespace;
5574  final SqlValidatorTable table = namespace.getTable();
5575  if (table == null) {
5576  return null;
5577  }
5578  final List<String> origin =
5579  new ArrayList<>(table.getQualifiedName());
5580  for (String name : qualified.suffix()) {
5581  namespace = namespace.lookupChild(name);
5582  if (namespace == null) {
5583  return null;
5584  }
5585  origin.add(name);
5586  }
5587  return origin;
5588  }
5589  return null;
5590  } else if (sqlQuery instanceof SqlOrderBy) {
5591  return getFieldOrigin(((SqlOrderBy) sqlQuery).query, i);
5592  } else {
5593  return null;
5594  }
5595  }
5596 
5597  public RelDataType getParameterRowType(SqlNode sqlQuery) {
5598  // NOTE: We assume that bind variables occur in depth-first tree
5599  // traversal in the same order that they occurred in the SQL text.
5600  final List<RelDataType> types = new ArrayList<>();
5601  // NOTE: but parameters on fetch/offset would be counted twice
5602  // as they are counted in the SqlOrderBy call and the inner SqlSelect call
5603  final Set<SqlNode> alreadyVisited = new HashSet<>();
5604  sqlQuery.accept(
5605  new SqlShuttle() {
5606 
5607  @Override public SqlNode visit(SqlDynamicParam param) {
5608  if (alreadyVisited.add(param)) {
5609  RelDataType type = getValidatedNodeType(param);
5610  types.add(type);
5611  }
5612  return param;
5613  }
5614  });
5615  return typeFactory.createStructType(
5616  types,
5617  new AbstractList<String>() {
5618  @Override public String get(int index) {
5619  return "?" + index;
5620  }
5621 
5622  @Override public int size() {
5623  return types.size();
5624  }
5625  });
5626  }
5627 
5629  SqlFunction function,
5630  List<RelDataType> argTypes,
5631  List<SqlNode> operands) {
5632  throw new UnsupportedOperationException();
5633  }
5634 
5635  private static boolean isPhysicalNavigation(SqlKind kind) {
5636  return kind == SqlKind.PREV || kind == SqlKind.NEXT;
5637  }
5638 
5639  private static boolean isLogicalNavigation(SqlKind kind) {
5640  return kind == SqlKind.FIRST || kind == SqlKind.LAST;
5641  }
5642 
5643  private static boolean isAggregation(SqlKind kind) {
5644  return kind == SqlKind.SUM || kind == SqlKind.SUM0
5645  || kind == SqlKind.AVG || kind == SqlKind.COUNT
5646  || kind == SqlKind.MAX || kind == SqlKind.MIN;
5647  }
5648 
5649  private static boolean isRunningOrFinal(SqlKind kind) {
5650  return kind == SqlKind.RUNNING || kind == SqlKind.FINAL;
5651  }
5652 
5653  private static boolean isSingleVarRequired(SqlKind kind) {
5654  return isPhysicalNavigation(kind)
5655  || isLogicalNavigation(kind)
5656  || isAggregation(kind);
5657  }
5658 
5659  //~ Inner Classes ----------------------------------------------------------
5660 
5664  public static class DmlNamespace extends IdentifierNamespace {
5665  protected DmlNamespace(SqlValidatorImpl validator, SqlNode id,
5666  SqlNode enclosingNode, SqlValidatorScope parentScope) {
5667  super(validator, id, enclosingNode, parentScope);
5668  }
5669  }
5670 
5674  private static class InsertNamespace extends DmlNamespace {
5675  private final SqlInsert node;
5676 
5677  InsertNamespace(SqlValidatorImpl validator, SqlInsert node,
5678  SqlNode enclosingNode, SqlValidatorScope parentScope) {
5679  super(validator, node.getTargetTable(), enclosingNode, parentScope);
5680  this.node = Objects.requireNonNull(node);
5681  }
5682 
5683  public SqlInsert getNode() {
5684  return node;
5685  }
5686  }
5687 
5691  private static class UpdateNamespace extends DmlNamespace {
5692  private final SqlUpdate node;
5693 
5694  UpdateNamespace(SqlValidatorImpl validator, SqlUpdate node,
5695  SqlNode enclosingNode, SqlValidatorScope parentScope) {
5696  super(validator, node.getTargetTable(), enclosingNode, parentScope);
5697  this.node = Objects.requireNonNull(node);
5698  }
5699 
5700  public SqlUpdate getNode() {
5701  return node;
5702  }
5703  }
5704 
5708  private static class DeleteNamespace extends DmlNamespace {
5709  private final SqlDelete node;
5710 
5711  DeleteNamespace(SqlValidatorImpl validator, SqlDelete node,
5712  SqlNode enclosingNode, SqlValidatorScope parentScope) {
5713  super(validator, node.getTargetTable(), enclosingNode, parentScope);
5714  this.node = Objects.requireNonNull(node);
5715  }
5716 
5717  public SqlDelete getNode() {
5718  return node;
5719  }
5720  }
5721 
5725  private static class MergeNamespace extends DmlNamespace {
5726  private final SqlMerge node;
5727 
5728  MergeNamespace(SqlValidatorImpl validator, SqlMerge node,
5729  SqlNode enclosingNode, SqlValidatorScope parentScope) {
5730  super(validator, node.getTargetTable(), enclosingNode, parentScope);
5731  this.node = Objects.requireNonNull(node);
5732  }
5733 
5734  public SqlMerge getNode() {
5735  return node;
5736  }
5737  }
5738 
5740  private static class PatternVarVisitor implements SqlVisitor<Void> {
5741  private MatchRecognizeScope scope;
5742  PatternVarVisitor(MatchRecognizeScope scope) {
5743  this.scope = scope;
5744  }
5745 
5746  @Override public Void visit(SqlLiteral literal) {
5747  return null;
5748  }
5749 
5750  @Override public Void visit(SqlCall call) {
5751  for (int i = 0; i < call.getOperandList().size(); i++) {
5752  call.getOperandList().get(i).accept(this);
5753  }
5754  return null;
5755  }
5756 
5757  @Override public Void visit(SqlNodeList nodeList) {
5758  throw Util.needToImplement(nodeList);
5759  }
5760 
5761  @Override public Void visit(SqlIdentifier id) {
5762  Preconditions.checkArgument(id.isSimple());
5763  scope.addPatternVar(id.getSimple());
5764  return null;
5765  }
5766 
5767  @Override public Void visit(SqlDataTypeSpec type) {
5768  throw Util.needToImplement(type);
5769  }
5770 
5771  @Override public Void visit(SqlDynamicParam param) {
5772  throw Util.needToImplement(param);
5773  }
5774 
5775  @Override public Void visit(SqlIntervalQualifier intervalQualifier) {
5776  throw Util.needToImplement(intervalQualifier);
5777  }
5778  }
5779 
5786  private class DeriveTypeVisitor implements SqlVisitor<RelDataType> {
5787  private final SqlValidatorScope scope;
5788 
5789  DeriveTypeVisitor(SqlValidatorScope scope) {
5790  this.scope = scope;
5791  }
5792 
5793  public RelDataType visit(SqlLiteral literal) {
5794  return literal.createSqlType(typeFactory);
5795  }
5796 
5797  public RelDataType visit(SqlCall call) {
5798  final SqlOperator operator = call.getOperator();
5799  return operator.deriveType(SqlValidatorImpl.this, scope, call);
5800  }
5801 
5802  public RelDataType visit(SqlNodeList nodeList) {
5803  // Operand is of a type that we can't derive a type for. If the
5804  // operand is of a peculiar type, such as a SqlNodeList, then you
5805  // should override the operator's validateCall() method so that it
5806  // doesn't try to validate that operand as an expression.
5807  throw Util.needToImplement(nodeList);
5808  }
5809 
5810  public RelDataType visit(SqlIdentifier id) {
5811  // First check for builtin functions which don't have parentheses,
5812  // like "LOCALTIME".
5813  final SqlCall call = makeNullaryCall(id);
5814  if (call != null) {
5815  return call.getOperator().validateOperands(
5816  SqlValidatorImpl.this,
5817  scope,
5818  call);
5819  }
5820 
5821  RelDataType type = null;
5822  if (!(scope instanceof EmptyScope)) {
5823  id = scope.fullyQualify(id).identifier;
5824  }
5825 
5826  // Resolve the longest prefix of id that we can
5827  int i;
5828  for (i = id.names.size() - 1; i > 0; i--) {
5829  // REVIEW jvs 9-June-2005: The name resolution rules used
5830  // here are supposed to match SQL:2003 Part 2 Section 6.6
5831  // (identifier chain), but we don't currently have enough
5832  // information to get everything right. In particular,
5833  // routine parameters are currently looked up via resolve;
5834  // we could do a better job if they were looked up via
5835  // resolveColumn.
5836 
5837  final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
5838  final SqlValidatorScope.ResolvedImpl resolved =
5839  new SqlValidatorScope.ResolvedImpl();
5840  scope.resolve(id.names.subList(0, i), nameMatcher, false, resolved);
5841  if (resolved.count() == 1) {
5842  // There's a namespace with the name we seek.
5843  final SqlValidatorScope.Resolve resolve = resolved.only();
5844  type = resolve.rowType();
5845  for (SqlValidatorScope.Step p : Util.skip(resolve.path.steps())) {
5846  type = type.getFieldList().get(p.i).getType();
5847  }
5848  break;
5849  }
5850  }
5851 
5852  // Give precedence to namespace found, unless there
5853  // are no more identifier components.
5854  if (type == null || id.names.size() == 1) {
5855  // See if there's a column with the name we seek in
5856  // precisely one of the namespaces in this scope.
5857  RelDataType colType = scope.resolveColumn(id.names.get(0), id);
5858  if (colType != null) {
5859  type = colType;
5860  }
5861  ++i;
5862  }
5863 
5864  if (type == null) {
5865  final SqlIdentifier last = id.getComponent(i - 1, i);
5866  throw newValidationError(last,
5867  RESOURCE.unknownIdentifier(last.toString()));
5868  }
5869 
5870  // Resolve rest of identifier
5871  for (; i < id.names.size(); i++) {
5872  String name = id.names.get(i);
5873  final RelDataTypeField field;
5874  if (name.equals("")) {
5875  // The wildcard "*" is represented as an empty name. It never
5876  // resolves to a field.
5877  name = "*";
5878  field = null;
5879  } else {
5880  final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
5881  field = nameMatcher.field(type, name);
5882  }
5883  if (field == null) {
5884  throw newValidationError(id.getComponent(i),
5885  RESOURCE.unknownField(name));
5886  }
5887  type = field.getType();
5888  }
5889  type =
5890  SqlTypeUtil.addCharsetAndCollation(
5891  type,
5892  getTypeFactory());
5893  return type;
5894  }
5895 
5896  public RelDataType visit(SqlDataTypeSpec dataType) {
5897  // Q. How can a data type have a type?
5898  // A. When it appears in an expression. (Say as the 2nd arg to the
5899  // CAST operator.)
5900  validateDataType(dataType);
5901  return dataType.deriveType(SqlValidatorImpl.this);
5902  }
5903 
5904  public RelDataType visit(SqlDynamicParam param) {
5905  return unknownType;
5906  }
5907 
5908  public RelDataType visit(SqlIntervalQualifier intervalQualifier) {
5909  return typeFactory.createSqlIntervalType(intervalQualifier);
5910  }
5911  }
5912 
5917  private static class Expander extends SqlScopedShuttle {
5918  protected final SqlValidatorImpl validator;
5919 
5920  Expander(SqlValidatorImpl validator, SqlValidatorScope scope) {
5921  super(scope);
5922  this.validator = validator;
5923  }
5924 
5925  @Override public SqlNode visit(SqlIdentifier id) {
5926  // First check for builtin functions which don't have
5927  // parentheses, like "LOCALTIME".
5928  final SqlCall call = validator.makeNullaryCall(id);
5929  if (call != null) {
5930  return call.accept(this);
5931  }
5932  final SqlIdentifier fqId = getScope().fullyQualify(id).identifier;
5933  SqlNode expandedExpr = expandDynamicStar(id, fqId);
5934  validator.setOriginal(expandedExpr, id);
5935  return expandedExpr;
5936  }
5937 
5938  @Override protected SqlNode visitScoped(SqlCall call) {
5939  switch (call.getKind()) {
5940  case SCALAR_QUERY:
5941  case CURRENT_VALUE:
5942  case NEXT_VALUE:
5943  case WITH:
5944  return call;
5945  }
5946  // Only visits arguments which are expressions. We don't want to
5947  // qualify non-expressions such as 'x' in 'empno * 5 AS x'.
5948 
5949  // ArgHandler<SqlNode> argHandler = // HEAVY.AI original
5950  // new CallCopyingArgHandler(call, false); // HEAVY.AI original
5951 
5952  // // HEAVY.AI new
5953  // Allow the operator to decide if it needs to be recreated // HEAVY.AI new
5954  // Useful for UDFs that are supplying default arguements that do // HEAVY.AI new
5955  // not already exist // HEAVY.AI new
5956  // // HEAVY.AI new
5957  // Only Heavy.AI Operators (UDFs) have the fn requiresCreate() overloaded // HEAVY.AI new
5958  // // HEAVY.AI new
5959  boolean bCreate = call.getOperator().requiresCreate(call.getOperandList()); // HEAVY.AI new
5960  ArgHandler<SqlNode> argHandler = // HEAVY.AI new
5961  new CallCopyingArgHandler(call, bCreate); // HEAVY.AI new
5962 
5963 
5964  call.getOperator().acceptCall(this, call, true, argHandler);
5965  final SqlNode result = argHandler.result();
5966  validator.setOriginal(result, call);
5967  return result;
5968  }
5969 
5970  protected SqlNode expandDynamicStar(SqlIdentifier id, SqlIdentifier fqId) {
5971  if (DynamicRecordType.isDynamicStarColName(Util.last(fqId.names))
5972  && !DynamicRecordType.isDynamicStarColName(Util.last(id.names))) {
5973  // Convert a column ref into ITEM(*, 'col_name')
5974  // for a dynamic star field in dynTable's rowType.
5975  SqlNode[] inputs = new SqlNode[2];
5976  inputs[0] = fqId;
5977  inputs[1] = SqlLiteral.createCharString(
5978  Util.last(id.names),
5979  id.getParserPosition());
5980  return new SqlBasicCall(
5981  SqlStdOperatorTable.ITEM,
5982  inputs,
5983  id.getParserPosition());
5984  }
5985  return fqId;
5986  }
5987  }
5988 
5993  class OrderExpressionExpander extends SqlScopedShuttle {
5994  private final List<String> aliasList;
5995  private final SqlSelect select;
5996  private final SqlNode root;
5997 
5998  OrderExpressionExpander(SqlSelect select, SqlNode root) {
5999  super(getOrderScope(select));
6000  this.select = select;
6001  this.root = root;
6002  this.aliasList = getNamespace(select).getRowType().getFieldNames();
6003  }
6004 
6005  public SqlNode go() {
6006  return root.accept(this);
6007  }
6008 
6009  public SqlNode visit(SqlLiteral literal) {
6010  // Ordinal markers, e.g. 'select a, b from t order by 2'.
6011  // Only recognize them if they are the whole expression,
6012  // and if the dialect permits.
6013  if (literal == root && config.sqlConformance().isSortByOrdinal()) {
6014  switch (literal.getTypeName()) {
6015  case DECIMAL:
6016  case DOUBLE:
6017  final int intValue = literal.intValue(false);
6018  if (intValue >= 0) {
6019  if (intValue < 1 || intValue > aliasList.size()) {
6020  throw newValidationError(
6021  literal, RESOURCE.orderByOrdinalOutOfRange());
6022  }
6023 
6024  // SQL ordinals are 1-based, but Sort's are 0-based
6025  int ordinal = intValue - 1;
6026  return nthSelectItem(ordinal, literal.getParserPosition());
6027  }
6028  break;
6029  }
6030  }
6031 
6032  return super.visit(literal);
6033  }
6034 
6038  private SqlNode nthSelectItem(int ordinal, final SqlParserPos pos) {
6039  // TODO: Don't expand the list every time. Maybe keep an expanded
6040  // version of each expression -- select lists and identifiers -- in
6041  // the validator.
6042 
6043  SqlNodeList expandedSelectList =
6044  expandStar(
6045  select.getSelectList(),
6046  select,
6047  false);
6048  SqlNode expr = expandedSelectList.get(ordinal);
6049  expr = stripAs(expr);
6050  if (expr instanceof SqlIdentifier) {
6051  expr = getScope().fullyQualify((SqlIdentifier) expr).identifier;
6052  }
6053 
6054  // Create a copy of the expression with the position of the order
6055  // item.
6056  return expr.clone(pos);
6057  }
6058 
6059  public SqlNode visit(SqlIdentifier id) {
6060  // Aliases, e.g. 'select a as x, b from t order by x'.
6061  if (id.isSimple()
6062  && config.sqlConformance().isSortByAlias()) {
6063  String alias = id.getSimple();
6064  final SqlValidatorNamespace selectNs = getNamespace(select);
6065  final RelDataType rowType =
6066  selectNs.getRowTypeSansSystemColumns();
6067  final SqlNameMatcher nameMatcher = catalogReader.nameMatcher();
6068  RelDataTypeField field = nameMatcher.field(rowType, alias);
6069  if (field != null) {
6070  return nthSelectItem(
6071  field.getIndex(),
6072  id.getParserPosition());
6073  }
6074  }
6075 
6076  // No match. Return identifier unchanged.
6077  return getScope().fullyQualify(id).identifier;
6078  }
6079 
6080  protected SqlNode visitScoped(SqlCall call) {
6081  // Don't attempt to expand sub-queries. We haven't implemented
6082  // these yet.
6083  if (call instanceof SqlSelect) {
6084  return call;
6085  }
6086  return super.visitScoped(call);
6087  }
6088  }
6089 
6095  static class SelectExpander extends Expander {
6096  final SqlSelect select;
6097 
6099  SqlSelect select) {
6100  super(validator, scope);
6101  this.select = select;
6102  }
6103 
6104  @Override public SqlNode visit(SqlIdentifier id) {
6105  final SqlNode node = expandCommonColumn(select, id, (SelectScope) getScope(), validator);
6106  if (node != id) {
6107  return node;
6108  } else {
6109  return super.visit(id);
6110  }
6111  }
6112  }
6113 
6118  static class ExtendedExpander extends Expander {
6119  final SqlSelect select;
6120  final SqlNode root;
6121  final boolean havingExpr;
6122  boolean bNested = false; // HEAVY.AI new
6123 
6124  ExtendedExpander(SqlValidatorImpl validator, SqlValidatorScope scope,
6125  SqlSelect select, SqlNode root, boolean havingExpr) {
6126  super(validator, scope);
6127  this.select = select;
6128  this.root = root;
6129  this.havingExpr = havingExpr;
6130  }
6131 
6132  @Override public SqlNode visit(SqlIdentifier id) {
6133  if (id.isSimple() && getScope() instanceof GroupByScope) { // HEAVY.AI new
6134  RelDataType dataType = getScope().resolveColumn(id.names.get(0), id); // HEAVY.AI new
6135  // Ensure that column names take precedence over aliases in // HEAVY.AI new
6136  // GROUP BY clauses. // HEAVY.AI new
6137  if (dataType != null) { // HEAVY.AI new
6138  return super.visit(id); // HEAVY.AI new
6139  } // HEAVY.AI new
6140  } // HEAVY.AI new
6141  if (id.isSimple()
6142  && (havingExpr
6143  ? validator.config().sqlConformance().isHavingAlias()
6144  : validator.config().sqlConformance().isGroupByAlias())) {
6145  String name = id.getSimple();
6146  SqlNode expr = null;
6147  final SqlNameMatcher nameMatcher =
6148  validator.catalogReader.nameMatcher();
6149  int n = 0;
6150  for (SqlNode s : select.getSelectList()) {
6151  final String alias = SqlValidatorUtil.getAlias(s, -1);
6152  if (alias != null && nameMatcher.matches(alias, name)) {
6153  expr = s;
6154  n++;
6155  }
6156  }
6157  if (n == 0) {
6158  return super.visit(id);
6159  } else if (n > 1) {
6160  // More than one column has this alias.
6161  throw validator.newValidationError(id,
6162  RESOURCE.columnAmbiguous(name));
6163  }
6165  return super.visit(id);
6166  }
6167  expr = stripAs(expr);
6168  if (expr instanceof SqlIdentifier) {
6169  SqlIdentifier sid = (SqlIdentifier) expr;
6170  final SqlIdentifier fqId = getScope().fullyQualify(sid).identifier;
6171  expr = expandDynamicStar(sid, fqId);
6172 
6173  } else { // HEAVY.AI new
6174  // The selectList item has not yet been expanded, // HEAVY.AI new
6175  // so it may need to be expanded *once* more to deal // HEAVY.AI new
6176  // with default arguments to a UDF that are provided // HEAVY.AI new
6177  // when the SqlCall is created. Trigger that process // HEAVY.AI new
6178  // by expanding once more, but *only* on the initial // HEAVY.AI new
6179  // alias replacement // HEAVY.AI new
6180  // HEAVY.AI new
6181  if(!bNested){ // HEAVY.AI new
6182  bNested = true; // HEAVY.AI new
6183  expr = expr.accept(this); // HEAVY.AI new
6184  bNested = false;; // HEAVY.AI new
6185  } // HEAVY.AI new
6186 
6187  }
6188  return expr;
6189  }
6190  if (id.isSimple()) {
6191  final SelectScope scope = validator.getRawSelectScope(select);
6192  SqlNode node = expandCommonColumn(select, id, scope, validator);
6193  if (node != id) {
6194  return node;
6195  }
6196  }
6197  return super.visit(id);
6198  }
6199 
6200  public SqlNode visit(SqlLiteral literal) {
6201  if (havingExpr || !validator.config().sqlConformance().isGroupByOrdinal()) {
6202  return super.visit(literal);
6203  }
6204  boolean isOrdinalLiteral = literal == root;
6205  switch (root.getKind()) {
6206  case GROUPING_SETS:
6207  case ROLLUP:
6208  case CUBE:
6209  if (root instanceof SqlBasicCall) {
6210  List<SqlNode> operandList = ((SqlBasicCall) root).getOperandList();
6211  for (SqlNode node : operandList) {
6212  if (node.equals(literal)) {
6213  isOrdinalLiteral = true;
6214  break;
6215  }
6216  }
6217  }
6218  break;
6219  }
6220  if (isOrdinalLiteral) {
6221  switch (literal.getTypeName()) {
6222  case DECIMAL:
6223  case DOUBLE:
6224  final int intValue = literal.intValue(false);
6225  if (intValue >= 0) {
6226  if (intValue < 1 || intValue > select.getSelectList().size()) {
6227  throw validator.newValidationError(literal,
6228  RESOURCE.orderByOrdinalOutOfRange());
6229  }
6230 
6231  // SQL ordinals are 1-based, but Sort's are 0-based
6232  int ordinal = intValue - 1;
6233 
6234  // return SqlUtil.stripAs(select.getSelectList().get(ordinal)); // HEAVY.AI original
6235 
6236  // The selectList item has not yet been expanded, so it may need to be // HEAVY.AI new
6237  // expanded *once* to deal with default arguments to a UDF that are // HEAVY.AI new
6238  // provided when the SqlCall is created. Trigger that process by // HEAVY.AI new
6239  // expanding once more, but *only* on the initial literal replacement // HEAVY.AI new
6240  // HEAVY.AI new
6241  SqlNode node = SqlUtil.stripAs(select.getSelectList().get(ordinal)); // HEAVY.AI new
6242  // HEAVY.AI new
6243  if(!bNested){ // HEAVY.AI new
6244  bNested = true; // HEAVY.AI new
6245  node = node.accept(this); // HEAVY.AI new
6246  bNested = false; // HEAVY.AI new
6247  } // HEAVY.AI new
6248  return node; // HEAVY.AI new
6249 
6250  }
6251  break;
6252  }
6253  }
6254 
6255  return super.visit(literal);
6256  }
6257  }
6258 
6260  protected static class IdInfo {
6261  public final SqlValidatorScope scope;
6262  public final SqlIdentifier id;
6263 
6264  public IdInfo(SqlValidatorScope scope, SqlIdentifier id) {
6265  this.scope = scope;
6266  this.id = id;
6267  }
6268  }
6269 
6274  protected static class FunctionParamInfo {
6280  public final Map<Integer, SqlSelect> cursorPosToSelectMap;
6281 
6286  public final Map<String, String> columnListParamToParentCursorMap;
6287 
6289  cursorPosToSelectMap = new HashMap<>();
6290  columnListParamToParentCursorMap = new HashMap<>();
6291  }
6292  }
6293 
6298  private static class NavigationModifier extends SqlShuttle {
6299  public SqlNode go(SqlNode node) {
6300  return node.accept(this);
6301  }
6302  }
6303 
6316  private static class NavigationExpander extends NavigationModifier {
6318  final SqlNode offset;
6319 
6321  this(null, null);
6322  }
6323 
6325  this.offset = offset;
6326  this.op = operator;
6327  }
6328 
6329  @Override public SqlNode visit(SqlCall call) {
6330  SqlKind kind = call.getKind();
6331  List<SqlNode> operands = call.getOperandList();
6332  List<SqlNode> newOperands = new ArrayList<>();
6333 
6334  if (call.getFunctionQuantifier() != null
6335  && call.getFunctionQuantifier().getValue() == SqlSelectKeyword.DISTINCT) {
6336  final SqlParserPos pos = call.getParserPosition();
6337  throw SqlUtil.newContextException(pos,
6338  Static.RESOURCE.functionQuantifierNotAllowed(call.toString()));
6339  }
6340 
6341  if (isLogicalNavigation(kind) || isPhysicalNavigation(kind)) {
6342  SqlNode inner = operands.get(0);
6343  SqlNode offset = operands.get(1);
6344 
6345  // merge two straight prev/next, update offset
6346  if (isPhysicalNavigation(kind)) {
6347  SqlKind innerKind = inner.getKind();
6348  if (isPhysicalNavigation(innerKind)) {
6349  List<SqlNode> innerOperands = ((SqlCall) inner).getOperandList();
6350  SqlNode innerOffset = innerOperands.get(1);
6351  SqlOperator newOperator = innerKind == kind
6352  ? SqlStdOperatorTable.PLUS : SqlStdOperatorTable.MINUS;
6353  offset = newOperator.createCall(SqlParserPos.ZERO,
6354  offset, innerOffset);
6355  inner = call.getOperator().createCall(SqlParserPos.ZERO,
6356  innerOperands.get(0), offset);
6357  }
6358  }
6359  SqlNode newInnerNode =
6360  inner.accept(new NavigationExpander(call.getOperator(), offset));
6361  if (op != null) {
6362  newInnerNode = op.createCall(SqlParserPos.ZERO, newInnerNode,
6363  this.offset);
6364  }
6365  return newInnerNode;
6366  }
6367 
6368  if (operands.size() > 0) {
6369  for (SqlNode node : operands) {
6370  if (node != null) {
6371  SqlNode newNode = node.accept(new NavigationExpander());
6372  if (op != null) {
6373  newNode = op.createCall(SqlParserPos.ZERO, newNode, offset);
6374  }
6375  newOperands.add(newNode);
6376  } else {
6377  newOperands.add(null);
6378  }
6379  }
6380  return call.getOperator().createCall(SqlParserPos.ZERO, newOperands);
6381  } else {
6382  if (op == null) {
6383  return call;
6384  } else {
6385  return op.createCall(SqlParserPos.ZERO, call, offset);
6386  }
6387  }
6388  }
6389 
6390  @Override public SqlNode visit(SqlIdentifier id) {
6391  if (op == null) {
6392  return id;
6393  } else {
6394  return op.createCall(SqlParserPos.ZERO, id, offset);
6395  }
6396  }
6397  }
6398 
6411  private static class NavigationReplacer extends NavigationModifier {
6412  private final String alpha;
6413 
6415  this.alpha = alpha;
6416  }
6417 
6418  @Override public SqlNode visit(SqlCall call) {
6419  SqlKind kind = call.getKind();
6420  if (isLogicalNavigation(kind)
6421  || isAggregation(kind)
6422  || isRunningOrFinal(kind)) {
6423  return call;
6424  }
6425 
6426  switch (kind) {
6427  case PREV:
6428  final List<SqlNode> operands = call.getOperandList();
6429  if (operands.get(0) instanceof SqlIdentifier) {
6430  String name = ((SqlIdentifier) operands.get(0)).names.get(0);
6431  return name.equals(alpha) ? call
6432  : SqlStdOperatorTable.LAST.createCall(SqlParserPos.ZERO, operands);
6433  }
6434  }
6435  return super.visit(call);
6436  }
6437 
6438  @Override public SqlNode visit(SqlIdentifier id) {
6439  if (id.isSimple()) {
6440  return id;
6441  }
6442  SqlOperator operator = id.names.get(0).equals(alpha)
6443  ? SqlStdOperatorTable.PREV : SqlStdOperatorTable.LAST;
6444 
6445  return operator.createCall(SqlParserPos.ZERO, id,
6446  SqlLiteral.createExactNumeric("0", SqlParserPos.ZERO));
6447  }
6448  }
6449 
6452  private class PatternValidator extends SqlBasicVisitor<Set<String>> {
6453  private final boolean isMeasure;
6457 
6459  this(<