[ 
https://issues.apache.org/jira/browse/CALCITE-5767?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Will Noble updated CALCITE-5767:
--------------------------------
    Description: 
In {{{}RelBuilder.collation{}}}, we use the "default null direction" to insert 
rex nodes as sorting expressions, but this is only the default null direction 
for NULLS-high dialects, i.e. *not* MSSQL. This is a problem because MSSQL has 
special-case logic for emulating null direction of GROUPING calls, whereby it 
effectively duplicates the expression. Really, 
{{MssqlSqlDialect.emulateNullDirection}} probably should've been returning 
{{null}} instead, signalling to callers that no null-direction emulation is 
necessary because {{GROUPING}} never returns {{{}NULL{}}}, but this causes 
another problem when the null direction is non-default as is caused simply by 
using {{RelBuilder.collation}} as described above (it should be noted that this 
method takes rex nodes instead of {{RelFieldCollation}} object, so there is no 
way to specify null direction) because the non-default null direction is not 
expanded into a {{CASE}} expression (MSSQL does not support {{NULLS FIRST}} or 
{{LAST}} syntax).

Here's a test illustrating the problem:

Input SQL (default dialect)
{code:xml}
select "product_class_id", "brand_name", GROUPING("brand_name")
from "product"
group by GROUPING SETS (("product_class_id", "brand_name"), 
("product_class_id"))
order by 3, 2, 1
{code}
Current behavior for unparsing as MSSQL (incorrect because it orders by the 
same column twice; {{GROUPING([brand_name])}} and {{3}}, which will fail if you 
try to actually run this against a real MSSQL database, even though it seems 
like it shouldn't):
{code:xml}
SELECT [product_class_id], [brand_name], GROUPING([brand_name])
FROM [foodmart].[product]
GROUP BY GROUPING SETS(([product_class_id], [brand_name]), [product_class_id])
ORDER BY
         GROUPING([brand_name]),
         3,
         CASE WHEN [brand_name] IS NULL THEN 1 ELSE 0 END,
         [brand_name],
         CASE WHEN [product_class_id] IS NULL THEN 1 ELSE 0 END,
         [product_class_id]
{code}
Behavior where {{MssqlSqlDialect.emulateNullDirection}} simply returns {{null}} 
for {{GROUPING}} expressions (incorrect because it uses {{NULLS LAST}} syntax):
{code:xml}
...
ORDER BY
         3 NULLS LAST,
         3,
         CASE WHEN [brand_name] IS NULL THEN 1 ELSE 0 END,
         [brand_name],
         CASE WHEN [product_class_id] IS NULL THEN 1 ELSE 0 END,
         [product_class_id]
{code}
Acceptable behavior (although the first {{ORDER BY}}-clause is effectively 
ordering by a constant, this will at least run and produce the correct results):
{code:xml}
...
ORDER BY
         CASE WHEN GROUPING([brand_name]) IS NULL THEN 0 ELSE 1 END,
         3,
         CASE WHEN [brand_name] IS NULL THEN 1 ELSE 0 END,
         [brand_name],
         CASE WHEN [product_class_id] IS NULL THEN 1 ELSE 0 END,
         [product_class_id]
{code}

  was:
In {{{}RelBuilder.collation{}}}, we use the "default null direction" to insert 
rex nodes as sorting expressions, but this is only the default null direction 
for NULLS-high dialects, i.e. *not* MSSQL. This is a problem because MSSQL has 
special-case logic for emulating null direction of GROUPING calls, whereby it 
effectively duplicates the expression. Really, 
{{MssqlSqlDialect.emulateNullDirection}} probably should've been returning 
{{null}} instead, signalling to callers that no null-direction emulation is 
necessary because {{GROUPING}} never returns {{{}NULL{}}}, but this causes 
another problem when the null direction is non-default as is caused simply by 
using {{RelBuilder.collation}} as described above (it should be noted that this 
method takes rex nodes instead of {{RelFieldCollation}} object, so there is no 
way to specify null direction) because the non-default null direction is not 
expanded into a {{CASE}} expression (MSSQL does not support {{NULLS FIRST}} or 
{{LAST}} syntax).

Here's a test illustrating the problem:

Input SQL (default dialect)
{code:xml}
select "product_class_id", "brand_name", GROUPING("brand_name")
from "product"
group by GROUPING SETS (("product_class_id", "brand_name"), 
("product_class_id"))
order by 3, 2, 1
{code}
Current behavior for unparsing as MSSQL (incorrect because it orders by the 
same column twice; {{GROUPING([brand_name])}} and {{3}}):
{code:xml}
SELECT [product_class_id], [brand_name], GROUPING([brand_name])
FROM [foodmart].[product]
GROUP BY GROUPING SETS(([product_class_id], [brand_name]), [product_class_id])
ORDER BY
         GROUPING([brand_name]),
         3,
         CASE WHEN [brand_name] IS NULL THEN 1 ELSE 0 END,
         [brand_name],
         CASE WHEN [product_class_id] IS NULL THEN 1 ELSE 0 END,
         [product_class_id]
{code}
Behavior where {{MssqlSqlDialect.emulateNullDirection}} simply returns {{null}} 
for {{GROUPING}} expressions (incorrect because it uses {{NULLS LAST}} syntax):
{code:xml}
...
ORDER BY
         3 NULLS LAST,
         3,
         CASE WHEN [brand_name] IS NULL THEN 1 ELSE 0 END,
         [brand_name],
         CASE WHEN [product_class_id] IS NULL THEN 1 ELSE 0 END,
         [product_class_id]
{code}
Acceptable behavior:
{code:xml}
...
ORDER BY
         CASE WHEN GROUPING([brand_name]) IS NULL THEN 0 ELSE 1 END,
         3,
         CASE WHEN [brand_name] IS NULL THEN 1 ELSE 0 END,
         [brand_name],
         CASE WHEN [product_class_id] IS NULL THEN 1 ELSE 0 END,
         [product_class_id]
{code}


> MSSQL fails to unparse properly when sorting by GROUPING expression
> -------------------------------------------------------------------
>
>                 Key: CALCITE-5767
>                 URL: https://issues.apache.org/jira/browse/CALCITE-5767
>             Project: Calcite
>          Issue Type: Bug
>          Components: core
>            Reporter: Will Noble
>            Assignee: Will Noble
>            Priority: Minor
>
> In {{{}RelBuilder.collation{}}}, we use the "default null direction" to 
> insert rex nodes as sorting expressions, but this is only the default null 
> direction for NULLS-high dialects, i.e. *not* MSSQL. This is a problem 
> because MSSQL has special-case logic for emulating null direction of GROUPING 
> calls, whereby it effectively duplicates the expression. Really, 
> {{MssqlSqlDialect.emulateNullDirection}} probably should've been returning 
> {{null}} instead, signalling to callers that no null-direction emulation is 
> necessary because {{GROUPING}} never returns {{{}NULL{}}}, but this causes 
> another problem when the null direction is non-default as is caused simply by 
> using {{RelBuilder.collation}} as described above (it should be noted that 
> this method takes rex nodes instead of {{RelFieldCollation}} object, so there 
> is no way to specify null direction) because the non-default null direction 
> is not expanded into a {{CASE}} expression (MSSQL does not support {{NULLS 
> FIRST}} or {{LAST}} syntax).
> Here's a test illustrating the problem:
> Input SQL (default dialect)
> {code:xml}
> select "product_class_id", "brand_name", GROUPING("brand_name")
> from "product"
> group by GROUPING SETS (("product_class_id", "brand_name"), 
> ("product_class_id"))
> order by 3, 2, 1
> {code}
> Current behavior for unparsing as MSSQL (incorrect because it orders by the 
> same column twice; {{GROUPING([brand_name])}} and {{3}}, which will fail if 
> you try to actually run this against a real MSSQL database, even though it 
> seems like it shouldn't):
> {code:xml}
> SELECT [product_class_id], [brand_name], GROUPING([brand_name])
> FROM [foodmart].[product]
> GROUP BY GROUPING SETS(([product_class_id], [brand_name]), [product_class_id])
> ORDER BY
>          GROUPING([brand_name]),
>          3,
>          CASE WHEN [brand_name] IS NULL THEN 1 ELSE 0 END,
>          [brand_name],
>          CASE WHEN [product_class_id] IS NULL THEN 1 ELSE 0 END,
>          [product_class_id]
> {code}
> Behavior where {{MssqlSqlDialect.emulateNullDirection}} simply returns 
> {{null}} for {{GROUPING}} expressions (incorrect because it uses {{NULLS 
> LAST}} syntax):
> {code:xml}
> ...
> ORDER BY
>          3 NULLS LAST,
>          3,
>          CASE WHEN [brand_name] IS NULL THEN 1 ELSE 0 END,
>          [brand_name],
>          CASE WHEN [product_class_id] IS NULL THEN 1 ELSE 0 END,
>          [product_class_id]
> {code}
> Acceptable behavior (although the first {{ORDER BY}}-clause is effectively 
> ordering by a constant, this will at least run and produce the correct 
> results):
> {code:xml}
> ...
> ORDER BY
>          CASE WHEN GROUPING([brand_name]) IS NULL THEN 0 ELSE 1 END,
>          3,
>          CASE WHEN [brand_name] IS NULL THEN 1 ELSE 0 END,
>          [brand_name],
>          CASE WHEN [product_class_id] IS NULL THEN 1 ELSE 0 END,
>          [product_class_id]
> {code}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to