You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@calcite.apache.org by "Abhishek Dasgupta (Jira)" <ji...@apache.org> on 2022/03/26 16:08:00 UTC

[jira] [Comment Edited] (CALCITE-4401) SqlJoin toString throws RuntimeException

    [ https://issues.apache.org/jira/browse/CALCITE-4401?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17512750#comment-17512750 ] 

Abhishek Dasgupta edited comment on CALCITE-4401 at 3/26/22, 4:07 PM:
----------------------------------------------------------------------

I'm trying to solve this ticket. As [~julianhyde] wrote in the last comment on CALCITE-3526, I've implemented this idea. I want to discuss it first before raising a PR (Also I don't have access to create a PR)

The challenge is "SELECT *" wrapper is not needed for all types of Sqls like UPDATE, DELETE etc{*}.{*} Hence in order to figure out when to *wrap a SqlNode before calling unparse method* or *stripping the buffer in SqlPrettryWriter#toString* . I've introduced a new property in SqlWriterConfig#needExtraSelectWrap which is a boolean.

After introducing the above property, the SqlPrettyWriter#toString will look like: 
{code:java}
@Override public String toString() {
    return config.needExtraSelectWrap() ? stripSelectWrapper() : buf.toString();
}{code}
 

and SqlNode#toString method of  will look like:
{code:java}
public SqlString toSqlString(UnaryOperator<SqlWriterConfig> transform) {
 final SqlWriterConfig config = transform.apply(SqlPrettyWriter.config());
 SqlPrettyWriter writer = new SqlPrettyWriter(config);
 if (config.needExtraSelectWrap()) {
     SqlNode selectWrapper = new SqlSelect(SqlParserPos.ZERO, SqlNodeList.EMPTY,
     SqlNodeList.SINGLETON_STAR, this, null, null, null,
     SqlNodeList.EMPTY, null, null, null, SqlNodeList.EMPTY);
     selectWrapper.unparse(writer, 0, 0);
 } else {
     this.unparse(writer, 0, 0);
 }
 return writer.toSqlString();
}{code}
 

In order to set SqlWriterConfig#needExtraSelectWrap, I've introduced a static method in SqlPrettyWriter (didn't know where to put it, maybe SqlUtils)  called SqlPrettyWriter#needSelectWrapper(SqlNode node)

 
{code:java}
public static boolean needSelectWrapper(SqlNode node) {
   return node instanceof SqlSelect;
}{code}
 

I've introduced this property in SqlWriterConfig because otherwise every SQL were wrapped in "SELECT *" and some SQL present in the buffer were wrong. Also they all need different type of string manipulation during the phase of stripping the buffer.

 

Let me know if you want further understanding. I can also raise a PR so that anyone can take a quick glance but first I need access for that.

 

Thanks!


was (Author: abhishek.dasgupta):
I'm trying to solve this ticket. As [~julianhyde] wrote in the last comment on [CALCITE-3526|https://issues.apache.org/jira/browse/CALCITE-3526], I've implemented this idea. I want to discuss it first before raising a PR (Also I don't have access to create a PR)

The challenge is "SELECT *" wrapper is not needed for all types of Sqls like UPDATE, DELETE etc. Hence in order to figure out when to *wrap a SqlNode before calling unparse method* or *stripping the buffer in SqlPrettryWriter#toString* . I've introduced a new property in SqlWriterConfig#needExtraSelectWrap which is a boolean.

After introducing the above property, the SqlPrettyWriter#toString will look like: 
{code:java}
@Override public String toString() {
    return config.needExtraSelectWrap() ? stripSelectWrapper() : buf.toString();
}{code}
 

and SqlNode#toString method of  will look like:
{code:java}
public SqlString toSqlString(UnaryOperator<SqlWriterConfig> transform) {
 final SqlWriterConfig config = transform.apply(SqlPrettyWriter.config());
 SqlPrettyWriter writer = new SqlPrettyWriter(config);
 if (config.needExtraSelectWrap()) {
     SqlNode selectWrapper = new SqlSelect(SqlParserPos.ZERO, SqlNodeList.EMPTY,
     SqlNodeList.SINGLETON_STAR, this, null, null, null,
     SqlNodeList.EMPTY, null, null, null, SqlNodeList.EMPTY);
     selectWrapper.unparse(writer, 0, 0);
 } else {
     this.unparse(writer, 0, 0);
 }
 return writer.toSqlString();
}{code}
 

In order to set SqlWriterConfig#needExtraSelectWrap, I've introduced a static method in SqlPrettyWriter (didn't know where to put it, maybe SqlUtils)  called SqlPrettyWriter#needSelectWrapper(SqlNode node)

 
{code:java}
public static boolean needSelectWrapper(SqlNode node) {
   return node instanceof SqlSelect;
}{code}
 

I've introduced this property in SqlWriterConfig because otherwise every SQL were wrapped in "SELECT *" and some SQL present in the buffer were wrong. Also they all need different type of string manipulation during the phase of stripping the buffer.

 

Let me know if you want further understanding. I can also raise a PR so that anyone can take a quick glance but first I need access for that.

 

Thanks!

> SqlJoin toString throws RuntimeException
> ----------------------------------------
>
>                 Key: CALCITE-4401
>                 URL: https://issues.apache.org/jira/browse/CALCITE-4401
>             Project: Calcite
>          Issue Type: Bug
>          Components: core
>    Affects Versions: 1.25.0, 1.26.0
>            Reporter: Dominik Labuda
>            Priority: Minor
>
> Hi,
> In our project we use Kotlin along with [Strikt assertion library|https://strikt.io/] in tests. The thing is that Strikt calls `.toString()` method on failing asserted objects to provide users with a nicely formatted output.
> Lets say the test looks like this: 
> {code:java}
>     @Test
>     fun `test correct type of JOIN in the root of generated SQL without neighboring JOINs`() {
>         // Generates SqlSelect that fails one of the conditions below
>         val rootSelect: SqlSelect = generateRootSql()
>         
>         expectThat(rootSelect.from) {
>             // Passes - from is a SqlJoin
>             val join = isA<SqlJoin>()
>             // Passes - joinType is FULL
>             join.get { joinType }.isEqualTo(JoinType.FULL)
>             // Fails - left is a SqlJoin, calls .toString() on rootSelect.from to provide output info
>             join.get { left }.isNotA<SqlJoin>()
>             join.get { right }.isNotA<SqlJoin>()
>         }
>     }
> {code}
> This can be inherently reduced to this example:
> {code:java}
> fun main() {
>     val frameworkConfig = initSchema()
>     val relBuilder: RelBuilder = RelBuilder.create(frameworkConfig)
>     val rootRelationalNode = relBuilder
>         .scan("tpch", "out_tpch_vw__customer")
>         .project(
>             relBuilder.field("c_custkey"),
>             relBuilder.field("region_name")
>         )
>         .scan("tpch", "out_tpch_vw__orders")
>         .project(
>             relBuilder.field("o_custkey"),
>             relBuilder.field("o_totalprice")
>         )
>         .join(
>             JoinRelType.INNER,
>             relBuilder.call(
>                 SqlStdOperatorTable.EQUALS,
>                 relBuilder.field(2, 0, "c_custkey"),
>                 relBuilder.field(2, 1, "o_custkey")
>             )
>         )
>         .build()
>     val sqlNode = RelToSqlConverter(PostgresqlSqlDialect.DEFAULT)
>         .visitRoot(rootRelationalNode)
>         .asStatement()
>     println("SQL:")
>     println(sqlNode)
>     println()
>     println("sqlNode root is SqlSelect: ${sqlNode is SqlSelect}")
>     // Treat sqlNode as SqlSelect
>     sqlNode as SqlSelect
>     println("sqlNode.from field is a SqlJoin: ${sqlNode.from is SqlJoin}")
>     println("Printing out the SqlJoin: ${sqlNode.from}")
> }
> {code}
> Which results in:
> {code:java}
> SQL:
> SELECT *
> FROM (SELECT `c_custkey`, `region_name`
> FROM `tpch`.`out_tpch_vw__customer`) AS `t`
> INNER JOIN (SELECT `o_custkey`, `o_totalprice`
> FROM `tpch`.`out_tpch_vw__orders`) AS `t0` ON `t`.`c_custkey` = `t0`.`o_custkey`
> sqlNode root is SqlSelect: true
> sqlNode.from field is a SqlJoin: true
> Exception in thread "main" java.lang.RuntimeException: No list started
>   at org.apache.calcite.sql.pretty.SqlPrettyWriter.sep(SqlPrettyWriter.java:1079)
>   at org.apache.calcite.sql.pretty.SqlPrettyWriter.sep(SqlPrettyWriter.java:1074)
>   at org.apache.calcite.sql.SqlJoin$SqlJoinOperator.unparse(SqlJoin.java:214)
>   at org.apache.calcite.sql.SqlDialect.unparseCall(SqlDialect.java:453)
>   at org.apache.calcite.sql.SqlCall.unparse(SqlCall.java:104)
>   at org.apache.calcite.sql.SqlNode.toSqlString(SqlNode.java:154)
>   at org.apache.calcite.sql.SqlNode.toString(SqlNode.java:129)
>   at java.base/java.lang.String.valueOf(String.java:2951)
>   at java.base/java.lang.StringBuilder.append(StringBuilder.java:168)
>   ...{code}
>  



--
This message was sent by Atlassian Jira
(v8.20.1#820001)