|
21 | 21 | import lombok.Setter;
|
22 | 22 | import org.apache.calcite.rel.type.RelDataType;
|
23 | 23 | import org.apache.calcite.rel.type.RelDataTypeFactory;
|
| 24 | +import org.apache.calcite.rel.type.RelDataTypeField; |
24 | 25 | import org.apache.calcite.sql.SqlBasicCall;
|
25 | 26 | import org.apache.calcite.sql.SqlCall;
|
| 27 | +import org.apache.calcite.sql.SqlKind; |
26 | 28 | import org.apache.calcite.sql.SqlNode;
|
| 29 | +import org.apache.calcite.sql.SqlUtil; |
27 | 30 | import org.apache.calcite.sql.fun.SqlMapValueConstructor;
|
28 | 31 | import org.apache.calcite.sql.fun.SqlStdOperatorTable;
|
29 | 32 | import org.apache.calcite.sql.util.SqlOperatorTables;
|
|
38 | 41 | import org.apache.calcite.sql2rel.SqlFunctionScanOperator;
|
39 | 42 | import org.apache.calcite.sql2rel.SqlHybridSearchOperator;
|
40 | 43 | import org.apache.calcite.sql2rel.SqlVectorOperator;
|
| 44 | +import org.apache.calcite.util.Pair; |
| 45 | +import org.apache.calcite.util.Util; |
41 | 46 | import org.checkerframework.checker.nullness.qual.Nullable;
|
42 | 47 |
|
| 48 | +import java.util.AbstractList; |
| 49 | +import java.util.List; |
43 | 50 | import java.util.Map;
|
44 | 51 | import java.util.concurrent.ConcurrentHashMap;
|
45 | 52 |
|
| 53 | +import static org.apache.calcite.util.Static.RESOURCE; |
| 54 | + |
46 | 55 | public class DingoSqlValidator extends SqlValidatorImpl {
|
47 | 56 |
|
48 | 57 | @Getter
|
@@ -148,4 +157,96 @@ protected void inferUnknownTypes(RelDataType inferredType, SqlValidatorScope sco
|
148 | 157 | super.inferUnknownTypes(inferredType, scope, node);
|
149 | 158 | }
|
150 | 159 |
|
| 160 | + protected void validateValues( |
| 161 | + SqlCall node, |
| 162 | + RelDataType targetRowType, |
| 163 | + final SqlValidatorScope scope) { |
| 164 | + assert node.getKind() == SqlKind.VALUES; |
| 165 | + |
| 166 | + final List<SqlNode> operands = node.getOperandList(); |
| 167 | + for (SqlNode operand : operands) { |
| 168 | + if (!(operand.getKind() == SqlKind.ROW)) { |
| 169 | + throw Util.needToImplement( |
| 170 | + "Values function where operands are scalars"); |
| 171 | + } |
| 172 | + |
| 173 | + SqlCall rowConstructor = (SqlCall) operand; |
| 174 | + if (false |
| 175 | + && targetRowType.isStruct() |
| 176 | + && rowConstructor.operandCount() < targetRowType.getFieldCount()) { |
| 177 | + targetRowType = |
| 178 | + typeFactory.createStructType( |
| 179 | + targetRowType.getFieldList() |
| 180 | + .subList(0, rowConstructor.operandCount())); |
| 181 | + } else if (targetRowType.isStruct() |
| 182 | + && rowConstructor.operandCount() != targetRowType.getFieldCount()) { |
| 183 | + return; |
| 184 | + } |
| 185 | + |
| 186 | + inferUnknownTypes( |
| 187 | + targetRowType, |
| 188 | + scope, |
| 189 | + rowConstructor); |
| 190 | + |
| 191 | + if (targetRowType.isStruct()) { |
| 192 | + for (Pair<SqlNode, RelDataTypeField> pair |
| 193 | + : Pair.zip(rowConstructor.getOperandList(), |
| 194 | + targetRowType.getFieldList())) { |
| 195 | + if (!pair.right.getType().isNullable() |
| 196 | + && SqlUtil.isNullLiteral(pair.left, false)) { |
| 197 | + throw newValidationError(node, |
| 198 | + RESOURCE.columnNotNullable(pair.right.getName())); |
| 199 | + } |
| 200 | + } |
| 201 | + } |
| 202 | + } |
| 203 | + |
| 204 | + for (SqlNode operand : operands) { |
| 205 | + operand.validate(this, scope); |
| 206 | + } |
| 207 | + |
| 208 | + // validate that all row types have the same number of columns |
| 209 | + // and that expressions in each column are compatible. |
| 210 | + // A values expression is turned into something that looks like |
| 211 | + // ROW(type00, type01,...), ROW(type11,...),... |
| 212 | + final int rowCount = operands.size(); |
| 213 | + if (rowCount >= 2) { |
| 214 | + SqlCall firstRow = (SqlCall) operands.get(0); |
| 215 | + final int columnCount = firstRow.operandCount(); |
| 216 | + |
| 217 | + // 1. check that all rows have the same cols length |
| 218 | + for (SqlNode operand : operands) { |
| 219 | + SqlCall thisRow = (SqlCall) operand; |
| 220 | + if (columnCount != thisRow.operandCount()) { |
| 221 | + throw newValidationError(node, |
| 222 | + RESOURCE.incompatibleValueType( |
| 223 | + SqlStdOperatorTable.VALUES.getName())); |
| 224 | + } |
| 225 | + } |
| 226 | + |
| 227 | + // 2. check if types at i:th position in each row are compatible |
| 228 | + for (int col = 0; col < columnCount; col++) { |
| 229 | + final int c = col; |
| 230 | + final RelDataType type = |
| 231 | + typeFactory.leastRestrictive( |
| 232 | + new AbstractList<RelDataType>() { |
| 233 | + @Override public RelDataType get(int row) { |
| 234 | + SqlCall thisRow = (SqlCall) operands.get(row); |
| 235 | + return deriveType(scope, thisRow.operand(c)); |
| 236 | + } |
| 237 | + |
| 238 | + @Override public int size() { |
| 239 | + return rowCount; |
| 240 | + } |
| 241 | + }); |
| 242 | + |
| 243 | + if (null == type) { |
| 244 | + throw newValidationError(node, |
| 245 | + RESOURCE.incompatibleValueType( |
| 246 | + SqlStdOperatorTable.VALUES.getName())); |
| 247 | + } |
| 248 | + } |
| 249 | + } |
| 250 | + } |
| 251 | + |
151 | 252 | }
|
0 commit comments