Alessandro Solimando created CALCITE-7537:
---------------------------------------------

             Summary: Invalid Postgres SQL generated for right-deep comma join 
trees
                 Key: CALCITE-7537
                 URL: https://issues.apache.org/jira/browse/CALCITE-7537
             Project: Calcite
          Issue Type: Bug
          Components: core
    Affects Versions: 1.41.0
            Reporter: Alessandro Solimando
            Assignee: Alessandro Solimando
             Fix For: 1.42.0


When RelToSqlConverter encounters a right-deep join tree where all joins are 
cross joins (INNER JOIN with TRUE condition), it correctly identifies the tree 
as a comma join but generates invalid SQL with extra parentheses around the 
right side.

For example, given a right-deep tree Join(A, Join(B, C)) with all-true 
conditions, the converter generates:
{noformat}
SELECT *
FROM "A",
("B", "C"){noformat}
while it could generate a flat form like:
{noformat}
SELECT *
FROM "A",
"B",
"C"{noformat}
Since the parenthesized form FROM "A", ("B", "C") is invalid in PostgreSQL 
(syntax error: *Query Error:* syntax error at or near ","), and the flat 
version seems to be supported more broadly.

Left-deep trees (which the SQL parser produces) are unaffected, so this only 
manifests when the relational plan comes from a non-SQL source (e.g. Substrait, 
or manual RelBuilder construction) that produces right-deep join trees.

*Root cause*

_SqlJoin.SqlJoinOperator.unparse()_ passes _getRightPrec()_ (19) as the 
_leftPrec_ when unparsing the right child. Since the join operator is 
left-associative with precedence 18, 19 > 18 triggers parenthesization. Comma 
join is associative, so parentheses on the right child are never really needed.

*Fix*
For comma joins, pass _getLeftPrec()_ instead of _getRightPrec()_ when 
unparsing the right child.

*Minimal reproducer*

This can be run by adding it to {_}RelToSqlConverterTest{_}:
{code:java}
@Test void testCommaCrossJoin3wayRightDeep() {
  final Function<RelBuilder, RelNode> relFn = b ->
      b.scan("EMP")
          .scan("DEPT")
          .scan("BONUS")
          .join(JoinRelType.INNER)
          .join(JoinRelType.INNER)
          .build();
  final String expectedPostgresql = "SELECT *\n"
      + "FROM \"scott\".\"EMP\",\n"
      + "\"scott\".\"DEPT\",\n"
      + "\"scott\".\"BONUS\"";
  relFn(relFn)
      .withPostgresql().ok(expectedPostgresql);
}{code}
Extra (DB Fiddle to see this shape passing with MySQL and failing with 
Postgres):
 * [MySQL|https://www.db-fiddle.com/#&togetherjs=1rQH0sckmf]
 * [Postgres|https://www.db-fiddle.com/#&togetherjs=chn5bQc4vl]



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

Reply via email to