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