danny0405 commented on a change in pull request #1338: [CALCITE-3210] Fix the
bug that RelToSqlConverter converts "cast(null as $type)" just as null
URL: https://github.com/apache/calcite/pull/1338#discussion_r313357915
##########
File path:
core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
##########
@@ -195,15 +198,81 @@ public Result visit(Project e) {
final Builder builder =
x.builder(e, Clause.SELECT);
final List<SqlNode> selectList = new ArrayList<>();
+
+ final boolean checkNull = isCastNullNeeded(stack);
for (RexNode ref : e.getChildExps()) {
SqlNode sqlExpr = builder.context.toSql(null, ref);
+ if (checkNull && SqlUtil.isNullLiteral(sqlExpr, false)) {
+ sqlExpr = castNullType(sqlExpr,
e.getRowType().getFieldList().get(selectList.size()));
+ }
addSelect(selectList, sqlExpr, e.getRowType());
}
builder.setSelect(new SqlNodeList(selectList, POS));
return builder.result();
}
+ /**
+ * Returns whether a NULL literal in the SELECT clause needs to be wrapped
in a CAST.
+ * @param stack stack of visit
+ * @return If null needs to be casted then return true, otherwise return
false.
+ * There are several cases to handle :
+ * 1. If a Project is under a Join or other operator which not instance of
SingleRel,
+ * and there may be some SingleRel which is not Project between the
project and none SingleRel,
+ * NULL needs to be wrapped in a CAST.
+ * Here is an example,
+ * "SELECT * FROM (SELECT CAST(NULL AS INT) AS c1, c2 FROM t1) AS t1
+ * JOIN (SELECT c3, c4 FROM t2) AS t2 ON c2 = c3"
+ *
+ * 2. If a Project is under another Project,
+ * and there may be some SingleRel which is not Project between these two
Project,
+ * NULL needs to be wrapped in a CAST.
+ * Here is an example,
+ * "SELECT * FROM (SELECT CAST(NULL AS INT) AS c1, c2 FROM t1) as t1
WHERE c2 = 10"
+ *
+ * 3. If a Project is under a TableModify,
+ * and there may be some SingleRel which is not Project between the
Project and TableModify,
+ * because that the type of NULL can be inferred by the target table of
TableModify,
+ * so NULL does not need to be wrapped in a CAST.
+ * Here is some examples,
+ * 3.1 "INSERT INTO t SELECT CAST(NULL AS INT) AS c1, c2 FROM t1"
+ * 3.2 "INSERT INTO t SELECT COUNT(CAST(NULL AS INT))"
+ * In case3.2, there is an LogicalAggregate which is instance of
SingleRel between
+ * TableModify and Project.
+ *
+ * Please note that in the example mentioned above,
+ * the Project converted by the select statement with "CAST(NULL AS INT)"
+ * is with no CAST call but a NULL expression and a data type described in
RowType.
+ */
+ private static boolean isCastNullNeeded(Deque<Frame> stack) {
+ final int stackSize = stack.size();
+ for (int i = 1; i < stackSize; i++) {
+ RelNode currentNode = Iterables.get(stack, i).r;
+ if (!(currentNode instanceof SingleRel)) {
+ return true;
+ } else if (currentNode instanceof Project) {
+ // direct or indirect input of Project need to cast null type
Review comment:
As i have said, for table modify (insert or update), we can handle them
separately here.
----------------------------------------------------------------
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.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services