mihaibudiu commented on code in PR #5211:
URL: https://github.com/apache/calcite/pull/5211#discussion_r3848394356
##########
core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableModifyRule.java:
##########
@@ -44,18 +93,89 @@ protected EnumerableTableModifyRule(Config config) {
@Override public @Nullable RelNode convert(RelNode rel) {
final TableModify modify = (TableModify) rel;
+ final RelOptCluster cluster = modify.getCluster();
final ModifiableTable modifiableTable =
modify.getTable().unwrap(ModifiableTable.class);
if (modifiableTable == null) {
return null;
}
final RelTraitSet traitSet =
modify.getTraitSet().replace(EnumerableConvention.INSTANCE);
+ RelNode input = convert(modify.getInput(), traitSet);
+ if (modify.isInsert() || modify.isUpdate()) {
+ // INSERT assigns stored columns; UPDATE assigns columns in the SET list.
+ RelDataType assignmentType = modify.isInsert()
+ ? RelOptTableImpl.realRowType(modify.getTable())
+ : modify.getCatalogReader().createTypeFromProjection(
+ modify.getTable().getRowType(),
+ requireNonNull(modify.getUpdateColumnList(),
"updateColumnList"));
+ if (modify.isFlattened()) {
+ // TableModify flattens its input, so flatten the target fields too.
+ assignmentType =
+ SqlTypeUtil.flattenRecordType(cluster.getTypeFactory(),
assignmentType, null);
+ }
+
+ final RexBuilder rexBuilder = cluster.getRexBuilder();
+ final List<RexNode> projects =
+ new ArrayList<>(rexBuilder.identityProjects(input.getRowType()));
+ final List<RexNode> checks = new ArrayList<>();
+ // UPDATE appends SET values to the old row; INSERT has only new values.
+ final int assignmentOffset = projects.size() -
assignmentType.getFieldCount();
+ for (RelDataTypeField field : assignmentType.getFieldList()) {
+ final int sourceOrdinal = assignmentOffset + field.getIndex();
+ final RexNode source = projects.get(sourceOrdinal);
+ final RelDataType targetType = field.getType();
+ final SqlTypeName targetName = targetType.getSqlTypeName();
+ if (SqlTypeUtil.inCharOrBinaryFamilies(targetType)) {
+ if (targetType.getPrecision() < 0) {
+ continue;
+ }
+ // Check character and binary lengths because their casts may
truncate.
Review Comment:
I think that a nicer solution would be to have a way for users to plug-in a
function to convert each value.
For example, you may reject dates BC, or you may round timestamps that have
higher precision.
The plug-in should be per type: for each input column type, there's a custom
function to accept the value (perhaps converting it), or throw if the value is
out of range.
I don't really know how you can do this cleanly - this code generator is not
parameterized.
##########
core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableTableModifyRule.java:
##########
@@ -17,17 +17,66 @@
package org.apache.calcite.adapter.enumerable;
import org.apache.calcite.plan.Convention;
+import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.prepare.RelOptTableImpl;
import org.apache.calcite.rel.RelNode;
import org.apache.calcite.rel.convert.ConverterRule;
import org.apache.calcite.rel.core.TableModify;
import org.apache.calcite.rel.logical.LogicalTableModify;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexProgram;
+import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.schema.ModifiableTable;
+import org.apache.calcite.sql.fun.SqlInternalOperators;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.type.SqlTypeUtil;
import org.checkerframework.checker.nullness.qual.Nullable;
-/** Planner rule that converts a {@link LogicalTableModify} to an {@link
EnumerableTableModify}.
- * You may provide a custom config to convert other nodes that extend {@link
TableModify}.
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.List;
+
+import static java.util.Objects.requireNonNull;
+
+/** Planner rule that converts a {@link LogicalTableModify} to an
+ * {@link EnumerableTableModify}.
+ *
+ * <p>For INSERT and UPDATE, the rule adds assignment checks of the following
+ * form:
+ *
+ * <blockquote><pre>{@code
+ * LogicalTableModify
+ * input
+ *
+ * EnumerableTableModify
+ * EnumerableCalc(
+ * condition=[
+ * AND(
+ * $THROW_UNLESS(
Review Comment:
Had no idea that this expression exists
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]