Skip to content

[CALCITE-7615] MAP_CONCAT does not accept NULL as an argument#5042

Open
xuzifu666 wants to merge 5 commits into
apache:mainfrom
xuzifu666:calcite-7615
Open

[CALCITE-7615] MAP_CONCAT does not accept NULL as an argument#5042
xuzifu666 wants to merge 5 commits into
apache:mainfrom
xuzifu666:calcite-7615

Conversation

@xuzifu666

Copy link
Copy Markdown
Member

}
return requireNonNull(opBinding.getTypeFactory().leastRestrictive(operandTypes));
}
if (mapTypes.isEmpty()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this legal?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I'm referencing Spark's behavior:

SELECT map_concat(map(1, 'a'), null);

-- result: NULL

SELECT map_concat(null, map(2, 'b'));

-- result: NULL

SELECT map_concat(map(1, 'a'), map(2, 'b'), null);

-- result: NULL

This should be reasonable according to Spark's standards.

+ "but 'INTEGER NOT NULL ARRAY NOT NULL' was found", false);
// test map operand with NULL literal.
f.checkNull("map_concat(map['foo', 1], null)");
f.checkType("map_concat(map['foo', 1], null)",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this type could actually be NULL too.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the main concern here is that the constant map_concat(map['foo', 1], null) might be collapsed into a NULL literal. This would cause the static type to change from MAP to NULL, making checkType unstable or even failing. Therefore, I've removed this test case.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would the type change if you infer it to be NULL?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon closer examination, this should indeed be a NULL type; I have modified the test.

f1.checkFails("^map_concat(map('foo', 1), null)^",
"Function 'MAP_CONCAT' should all be of type map, "
+ "but it is 'NULL'", false);
f1.checkNull("map_concat(map('foo', 1), null)");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to add a test with no arguments, if that's really supported?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I had added a test with no arguments, according to Spark behaivor this should return Map<StringType, StringType>: https://github.com/apache/spark/blob/master/sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/expressions/collectionOperations.scala#L713

@transient override lazy val dataType: MapType = {
    if (children.isEmpty) {
      MapType(StringType, StringType)   // default
    } else {
      super.dataType.asInstanceOf[MapType]
    }
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be documented. Frankly, it looks wrong to me, for empty array the type is inferred to be ANY ARRAY I think

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, this is based on Spark, and I also find it odd. I changed the type here to ANY because I think it's more appropriate that way.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add a test like MAP_CONCAT(MAP_CONCAT(), MAP[1, 2]). I think this should work, and the result should be MAP<INT, INT>.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, had added a test and result is as expected.

return requireNonNull(opBinding.getTypeFactory().leastRestrictive(operandTypes));
}
if (mapTypes.isEmpty() || hasNull) {
// All arguments are NULL literals, or at least one argument is a NULL literal;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at least one null argument.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

f1.checkFails("^map_concat(map('foo', 1), null)^",
"Function 'MAP_CONCAT' should all be of type map, "
+ "but it is 'NULL'", false);
f1.checkNull("map_concat(map('foo', 1), null)");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also add a test like MAP_CONCAT(MAP_CONCAT(), MAP[1, 2]). I think this should work, and the result should be MAP<INT, INT>.

@sonarqubecloud

Copy link
Copy Markdown

@xuzifu666

Copy link
Copy Markdown
Member Author

If you are happy with current changes, I would squash the commits~ @mihaibudiu

@mihaibudiu mihaibudiu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have already approved

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants