OmniSciDB  c0231cc57d
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ExtTableFunctionTypeChecker.java
Go to the documentation of this file.
1 package com.mapd.calcite.parser;
2 
5 
6 import org.apache.calcite.linq4j.Ord;
7 import org.apache.calcite.rel.type.RelDataType;
8 import org.apache.calcite.rel.type.RelDataTypeField;
9 import org.apache.calcite.sql.SqlBasicCall;
10 import org.apache.calcite.sql.SqlCall;
11 import org.apache.calcite.sql.SqlCallBinding;
12 import org.apache.calcite.sql.SqlFunctionCategory;
13 import org.apache.calcite.sql.SqlNode;
14 import org.apache.calcite.sql.SqlOperandCountRange;
16 import org.apache.calcite.sql.SqlSyntax;
17 import org.apache.calcite.sql.type.SqlOperandCountRanges;
18 import org.apache.calcite.sql.type.SqlOperandTypeChecker;
19 import org.apache.calcite.sql.type.SqlTypeName;
20 import org.apache.calcite.sql.validate.SqlNameMatchers;
21 
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Set;
28 import java.util.stream.Collectors;
29 
30 public class ExtTableFunctionTypeChecker implements SqlOperandTypeChecker {
32 
34  this.opTable = opTable;
35  }
36 
37  public boolean isOptional(int argIndex) {
38  // We need to mark all arguments as optional, otherwise Calcite may invalidate some
39  // valid calls during the validation process. This is because if it previously bound
40  // the call to a UDTF operator that receives more arguments, it fills up the missing
41  // arguments with DEFAULTs. DEFAULT arguments however will not get to the typechecking
42  // stage if they are not optional for that oeprator.
43  return true;
44  }
45 
47  SqlCallBinding callBinding,
48  SqlNode node,
49  int iFormalOperand) {
50  SqlCall permutedCall = callBinding.permutedCall();
51  SqlNode permutedOperand = permutedCall.operand(iFormalOperand);
52  RelDataType type;
53 
54  // For candidate calls to incompatible operators, type inference of operands may fail.
55  // In that case, we just catch the exception and invalidade the candidate.
56  try {
57  type = callBinding.getValidator().deriveType(
58  callBinding.getScope(), permutedOperand);
59  } catch (Exception e) {
60  return false;
61  }
62  SqlTypeName typeName = type.getSqlTypeName();
63 
64  if (typeName == SqlTypeName.CURSOR) {
65  SqlCall cursorCall = (SqlCall) permutedOperand;
66  RelDataType cursorType = callBinding.getValidator().deriveType(
67  callBinding.getScope(), cursorCall.operand(0));
68  return doesCursorOperandTypeMatch(tf, iFormalOperand, cursorType);
69  } else {
70  return tf.getArgTypes().get(iFormalOperand).getTypeNames().contains(typeName);
71  }
72  }
73 
74  public boolean checkOperandTypes(SqlCallBinding callBinding, boolean throwOnFailure) {
75  Set<ExtTableFunction> candidateOverloads = new HashSet<ExtTableFunction>(
76  getOperatorOverloads(callBinding.getOperator()));
77 
78  // Remove all candidates whose number of formal args doesn't match the
79  // call's number of real args.
80  candidateOverloads.removeIf(
81  tf -> tf.getArgTypes().size() != callBinding.getOperandCount());
82 
83  SqlNode[] operandArray = new SqlNode[callBinding.getCall().getOperandList().size()];
84  for (Ord<SqlNode> arg : Ord.zip(callBinding.getCall().getOperandList())) {
85  operandArray[arg.i] = arg.e;
86  }
87 
88  // Construct a candidate call binding for each overload. We need to do this because
89  // type inference of operands may differ depending on which operator is used. Thus,
90  // typechecking needs to be done on a candidate call-by-call basis.
91  HashMap<ExtTableFunction, SqlCallBinding> candidateBindings =
92  new HashMap<>(candidateOverloads.size());
93  for (ExtTableFunction tf : candidateOverloads) {
94  SqlBasicCall newCall = new SqlBasicCall(
95  tf, operandArray, callBinding.getCall().getParserPosition());
96  SqlCallBinding candidateBinding = new SqlCallBinding(
97  callBinding.getValidator(), callBinding.getScope(), newCall);
98  candidateBindings.put(tf, candidateBinding);
99  }
100 
101  for (int i = 0; i < operandArray.length; i++) {
102  int idx = i;
103  candidateOverloads.removeIf(tf
105  tf, candidateBindings.get(tf), operandArray[idx], idx));
106  }
107 
108  // If there are no candidates left, the call is invalid.
109  if (candidateOverloads.size() == 0) {
110  if (throwOnFailure) {
111  throw(callBinding.newValidationSignatureError());
112  }
113  return false;
114  }
115 
116  // If there are candidates left, and the current bound operator
117  // is not one of them, rewrite the call to use the a better binding.
118  if (!candidateOverloads.isEmpty()
119  && !candidateOverloads.contains(callBinding.getOperator())) {
120  ExtTableFunction optimal = candidateOverloads.iterator().next();
121  ((SqlBasicCall) callBinding.getCall()).setOperator(optimal);
122  }
123 
124  return true;
125  }
126 
128  ExtTableFunction tf, int iFormalOperand, RelDataType actualOperand) {
129  String formalOperandName = tf.getExtendedParamNames().get(iFormalOperand);
130  List<ExtensionFunction.ExtArgumentType> formalFieldTypes =
131  tf.getCursorFieldTypes().get(formalOperandName);
132  List<RelDataTypeField> actualFieldList = actualOperand.getFieldList();
133 
134  // runtime functions may not have CURSOR field type information, so we default
135  // to old behavior of assuming they typecheck
136  if (formalFieldTypes.size() == 0) {
137  System.out.println(
138  "Warning: UDTF has no CURSOR field subtype data. Proceeding assuming CURSOR typechecks.");
139  return true;
140  }
141 
142  int iFormal = 0;
143  int iActual = 0;
144  while (iActual < actualFieldList.size() && iFormal < formalFieldTypes.size()) {
145  ExtensionFunction.ExtArgumentType extType = formalFieldTypes.get(iFormal);
146  SqlTypeName formalType = ExtensionFunction.toSqlTypeName(extType);
147  SqlTypeName actualType = actualFieldList.get(iActual).getValue().getSqlTypeName();
148 
149  if (formalType == SqlTypeName.COLUMN_LIST) {
150  ExtensionFunction.ExtArgumentType colListSubtype =
151  ExtensionFunction.getValueType(extType);
152  SqlTypeName colListType = ExtensionFunction.toSqlTypeName(colListSubtype);
153 
154  if (actualType != colListType) {
155  return false;
156  }
157 
158  int colListSize = 0;
159  int numFormalArgumentsLeft = (formalFieldTypes.size() - 1) - iFormal;
160  while (iActual + colListSize
161  < (actualFieldList.size() - numFormalArgumentsLeft)) {
162  actualType =
163  actualFieldList.get(iActual + colListSize).getValue().getSqlTypeName();
164  if (actualType != colListType) {
165  break;
166  }
167  colListSize++;
168  }
169  iActual += colListSize - 1;
170  } else if (formalType != actualType) {
171  return false;
172  }
173  iFormal++;
174  iActual++;
175  }
176 
177  if (iActual < actualFieldList.size()) {
178  return false;
179  }
180 
181  return true;
182  }
183 
184  public List<ExtTableFunction> getOperatorOverloads(SqlOperator op) {
185  List<SqlOperator> overloads = new ArrayList<>();
186  opTable.lookupOperatorOverloads(op.getNameAsId(),
187  SqlFunctionCategory.USER_DEFINED_TABLE_FUNCTION,
188  SqlSyntax.FUNCTION,
189  overloads,
190  SqlNameMatchers.liberal());
191 
192  return overloads.stream()
193  .filter(p -> p instanceof ExtTableFunction)
194  .map(p -> (ExtTableFunction) p)
195  .collect(Collectors.toList());
196  }
197 
198  public SqlOperandCountRange getOperandCountRange() {
199  return SqlOperandCountRanges.any();
200  }
201 
202  public String getAllowedSignatures(SqlOperator op, String opName) {
203  List<ExtTableFunction> overloads = getOperatorOverloads(op);
204  return String.join(System.lineSeparator() + "\t",
205  overloads.stream()
206  .map(tf -> tf.getExtendedSignature())
207  .collect(Collectors.toList()));
208  }
209 
210  public Consistency getConsistency() {
211  return Consistency.NONE;
212  }
213 }
bool contains(const T &container, const U &element)
Definition: misc.h:195
List< ExtTableFunction > getOperatorOverloads(SqlOperator op)
boolean doesCursorOperandTypeMatch(ExtTableFunction tf, int iFormalOperand, RelDataType actualOperand)
boolean checkOperandTypes(SqlCallBinding callBinding, boolean throwOnFailure)
std::string typeName(const T *v)
Definition: toString.h:103
boolean doesOperandTypeMatch(ExtTableFunction tf, SqlCallBinding callBinding, SqlNode node, int iFormalOperand)