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_r309006898
 
 

 ##########
 File path: 
core/src/main/java/org/apache/calcite/rel/rel2sql/RelToSqlConverter.java
 ##########
 @@ -195,15 +198,78 @@ 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 an example,
+   *    "INSERT INTO t SELECT CAST(NULL AS INT) AS c1, c2 FROM t1"
+   *
+   * 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)) {
+        break;
+      } else if (currentNode instanceof Project) {
+        // direct or indirect input of Project need to cast null type
+        break;
+      } else if (currentNode instanceof TableModify) {
+        // direct or indirect input of TableModify do not need to cast null 
type
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  /**
+   * cast null with type
+   * @param sqlNodeNull null SqlNode
+   * @param field field description of sqlNodeNull
+   * @return SqlNode with cast to type.
+   */
+  private SqlNode castNullType(SqlNode sqlNodeNull, RelDataTypeField field) {
 
 Review comment:
     /**
      * Wrap the {code sqlNodeNull} in a CAST operator with target type as 
{code field}.
      * @param sqlNodeNull null literal
      * @param field field description of sqlNodeNull
      * @return null literal wrapped in CAST call.
      */

----------------------------------------------------------------
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

Reply via email to