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

Pavel Pereslegin updated IGNITE-24560:
--------------------------------------
    Description: 
Reproducer

{code:java}
   @Test
    public void testScriptSingleStatement() {
        sql("create table test(id int primary key, val int)");
        igniteSql().executeScript("select * from test;");
        sql("alter table test drop column val");
        igniteSql().executeScript("select * from test;"); // Column 'VAL' not 
found in table 'TEST
    }
{code}

Error output

{noformat}
Caused by: org.apache.calcite.runtime.CalciteContextException: At line 1, 
column 8: Column 'VAL' not found in table 'TEST'
        at 
java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        at 
org.apache.calcite.runtime.Resources$ExInstWithCause.ex(Resources.java:511)
        at org.apache.calcite.sql.SqlUtil.newContextException(SqlUtil.java:952)
        at org.apache.calcite.sql.SqlUtil.newContextException(SqlUtil.java:937)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.newValidationError(SqlValidatorImpl.java:5899)
        at 
org.apache.ignite.internal.sql.engine.prepare.IgniteSqlValidator.newValidationError(IgniteSqlValidator.java:291)
        at 
org.apache.calcite.sql.validate.DelegatingScope.fullyQualify(DelegatingScope.java:473)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl$Expander.visit(SqlValidatorImpl.java:7105)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl$SelectExpander.visit(SqlValidatorImpl.java:7276)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl$SelectExpander.visit(SqlValidatorImpl.java:7261)
        at org.apache.calcite.sql.SqlIdentifier.accept(SqlIdentifier.java:324)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl$Expander.go(SqlValidatorImpl.java:7094)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.expandSelectExpr(SqlValidatorImpl.java:6665)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.expandSelectItem(SqlValidatorImpl.java:481)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.validateSelectList(SqlValidatorImpl.java:5015)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.validateSelect(SqlValidatorImpl.java:4096)
        at 
org.apache.ignite.internal.sql.engine.prepare.IgniteSqlValidator.validateSelect(IgniteSqlValidator.java:684)
        at 
org.apache.calcite.sql.validate.SelectNamespace.validateImpl(SelectNamespace.java:62)
        at 
org.apache.calcite.sql.validate.AbstractNamespace.validate(AbstractNamespace.java:95)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.validateNamespace(SqlValidatorImpl.java:1206)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.validateQuery(SqlValidatorImpl.java:1177)
        at org.apache.calcite.sql.SqlSelect.validate(SqlSelect.java:282)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.validateScopedExpression(SqlValidatorImpl.java:1143)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.validate(SqlValidatorImpl.java:849)
        at 
org.apache.ignite.internal.sql.engine.prepare.IgniteSqlValidator.validate(IgniteSqlValidator.java:194)
        at 
org.apache.ignite.internal.sql.engine.prepare.IgnitePlanner.validateAndGetTypeMetadata(IgnitePlanner.java:303)
        at 
org.apache.ignite.internal.sql.engine.prepare.PrepareServiceImpl.lambda$prepareQuery$3(PrepareServiceImpl.java:389)
        at 
java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run$$$capture(CompletableFuture.java:1700)
{noformat}

This happened because we redirect the multi-statement execution with single 
statement to single-statement execution workflow, but the AST tree in the cache 
remains modified by validator.

E.g. 

for single statement execution we generating AST each time.

{code:Java}
ParsedResult result = new ParsedResultImpl(
                queryType,
                query,
                normalizedQuery,
                parsedStatement.dynamicParamsCount(),
                () -> {
                    // Descendants of SqlNode class are mutable, thus we must 
use every
                    // syntax node only once to avoid problem. But we already 
parsed the
                    // query once to get normalized result. An `unparse` 
operation is known
                    // to be safe, so let's reuse result of parsing for the 
first invocation
                    // of `parsedTree` method to avoid double-parsing for one 
time queries.
                    SqlNode ast = holder.getAndSet(null);

                    if (ast != null) {
                        return ast;
                    }

                    return IgniteSqlParser.parse(query, 
StatementParseResult.MODE).statement();
                }
{code}

but for multi-statement we use the same AST.

{code:Java}
            results.add(new ParsedResultImpl(
                    queryType,
                    normalizedQuery,
                    normalizedQuery,
                    result.dynamicParamsCount(),
                    () -> parsedTree
            ));
{code}



*NOTE*: This issue is critical for Ignite CLI (see attachment), we need to add 
CLI test for this case

  was:
Reproducer

{code:java}
   @Test
    public void testScriptSingleStatement() {
        sql("create table test(id int primary key, val int)");
        igniteSql().executeScript("select * from test;");
        sql("alter table test drop column val");
        igniteSql().executeScript("select * from test;"); // Column 'VAL' not 
found in table 'TEST
    }
{code}

Error output

{noformat}
Caused by: org.apache.calcite.runtime.CalciteContextException: At line 1, 
column 8: Column 'VAL' not found in table 'TEST'
        at 
java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
        at 
org.apache.calcite.runtime.Resources$ExInstWithCause.ex(Resources.java:511)
        at org.apache.calcite.sql.SqlUtil.newContextException(SqlUtil.java:952)
        at org.apache.calcite.sql.SqlUtil.newContextException(SqlUtil.java:937)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.newValidationError(SqlValidatorImpl.java:5899)
        at 
org.apache.ignite.internal.sql.engine.prepare.IgniteSqlValidator.newValidationError(IgniteSqlValidator.java:291)
        at 
org.apache.calcite.sql.validate.DelegatingScope.fullyQualify(DelegatingScope.java:473)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl$Expander.visit(SqlValidatorImpl.java:7105)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl$SelectExpander.visit(SqlValidatorImpl.java:7276)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl$SelectExpander.visit(SqlValidatorImpl.java:7261)
        at org.apache.calcite.sql.SqlIdentifier.accept(SqlIdentifier.java:324)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl$Expander.go(SqlValidatorImpl.java:7094)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.expandSelectExpr(SqlValidatorImpl.java:6665)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.expandSelectItem(SqlValidatorImpl.java:481)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.validateSelectList(SqlValidatorImpl.java:5015)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.validateSelect(SqlValidatorImpl.java:4096)
        at 
org.apache.ignite.internal.sql.engine.prepare.IgniteSqlValidator.validateSelect(IgniteSqlValidator.java:684)
        at 
org.apache.calcite.sql.validate.SelectNamespace.validateImpl(SelectNamespace.java:62)
        at 
org.apache.calcite.sql.validate.AbstractNamespace.validate(AbstractNamespace.java:95)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.validateNamespace(SqlValidatorImpl.java:1206)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.validateQuery(SqlValidatorImpl.java:1177)
        at org.apache.calcite.sql.SqlSelect.validate(SqlSelect.java:282)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.validateScopedExpression(SqlValidatorImpl.java:1143)
        at 
org.apache.calcite.sql.validate.SqlValidatorImpl.validate(SqlValidatorImpl.java:849)
        at 
org.apache.ignite.internal.sql.engine.prepare.IgniteSqlValidator.validate(IgniteSqlValidator.java:194)
        at 
org.apache.ignite.internal.sql.engine.prepare.IgnitePlanner.validateAndGetTypeMetadata(IgnitePlanner.java:303)
        at 
org.apache.ignite.internal.sql.engine.prepare.PrepareServiceImpl.lambda$prepareQuery$3(PrepareServiceImpl.java:389)
        at 
java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run$$$capture(CompletableFuture.java:1700)
{noformat}

This happened because we redirect the multi-statement execution with single 
statement to single-statement execution workflow, but the AST tree in the cache 
remains modified by validator.

E.g. 

for single statement execution we generating AST each time.

{code:Java}
ParsedResult result = new ParsedResultImpl(
                queryType,
                query,
                normalizedQuery,
                parsedStatement.dynamicParamsCount(),
                () -> {
                    // Descendants of SqlNode class are mutable, thus we must 
use every
                    // syntax node only once to avoid problem. But we already 
parsed the
                    // query once to get normalized result. An `unparse` 
operation is known
                    // to be safe, so let's reuse result of parsing for the 
first invocation
                    // of `parsedTree` method to avoid double-parsing for one 
time queries.
                    SqlNode ast = holder.getAndSet(null);

                    if (ast != null) {
                        return ast;
                    }

                    return IgniteSqlParser.parse(query, 
StatementParseResult.MODE).statement();
                }
{code}

but for multi-statement we use the same AST.

{code:Java}
            results.add(new ParsedResultImpl(
                    queryType,
                    normalizedQuery,
                    normalizedQuery,
                    result.dynamicParamsCount(),
                    () -> parsedTree
            ));
{code}

NOTE: This issue is critical for Ignite CLI (see attachment), we need to add 
CLI test for this case


> Sql. Result of script parsing with single statement is cached incorrectly
> -------------------------------------------------------------------------
>
>                 Key: IGNITE-24560
>                 URL: https://issues.apache.org/jira/browse/IGNITE-24560
>             Project: Ignite
>          Issue Type: Bug
>          Components: sql
>    Affects Versions: 3.0
>            Reporter: Pavel Pereslegin
>            Priority: Major
>              Labels: ignite-3
>             Fix For: 3.1
>
>
> Reproducer
> {code:java}
>    @Test
>     public void testScriptSingleStatement() {
>         sql("create table test(id int primary key, val int)");
>         igniteSql().executeScript("select * from test;");
>         sql("alter table test drop column val");
>         igniteSql().executeScript("select * from test;"); // Column 'VAL' not 
> found in table 'TEST
>     }
> {code}
> Error output
> {noformat}
> Caused by: org.apache.calcite.runtime.CalciteContextException: At line 1, 
> column 8: Column 'VAL' not found in table 'TEST'
>       at 
> java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
>       at 
> org.apache.calcite.runtime.Resources$ExInstWithCause.ex(Resources.java:511)
>       at org.apache.calcite.sql.SqlUtil.newContextException(SqlUtil.java:952)
>       at org.apache.calcite.sql.SqlUtil.newContextException(SqlUtil.java:937)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.newValidationError(SqlValidatorImpl.java:5899)
>       at 
> org.apache.ignite.internal.sql.engine.prepare.IgniteSqlValidator.newValidationError(IgniteSqlValidator.java:291)
>       at 
> org.apache.calcite.sql.validate.DelegatingScope.fullyQualify(DelegatingScope.java:473)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl$Expander.visit(SqlValidatorImpl.java:7105)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl$SelectExpander.visit(SqlValidatorImpl.java:7276)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl$SelectExpander.visit(SqlValidatorImpl.java:7261)
>       at org.apache.calcite.sql.SqlIdentifier.accept(SqlIdentifier.java:324)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl$Expander.go(SqlValidatorImpl.java:7094)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.expandSelectExpr(SqlValidatorImpl.java:6665)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.expandSelectItem(SqlValidatorImpl.java:481)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validateSelectList(SqlValidatorImpl.java:5015)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validateSelect(SqlValidatorImpl.java:4096)
>       at 
> org.apache.ignite.internal.sql.engine.prepare.IgniteSqlValidator.validateSelect(IgniteSqlValidator.java:684)
>       at 
> org.apache.calcite.sql.validate.SelectNamespace.validateImpl(SelectNamespace.java:62)
>       at 
> org.apache.calcite.sql.validate.AbstractNamespace.validate(AbstractNamespace.java:95)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validateNamespace(SqlValidatorImpl.java:1206)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validateQuery(SqlValidatorImpl.java:1177)
>       at org.apache.calcite.sql.SqlSelect.validate(SqlSelect.java:282)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validateScopedExpression(SqlValidatorImpl.java:1143)
>       at 
> org.apache.calcite.sql.validate.SqlValidatorImpl.validate(SqlValidatorImpl.java:849)
>       at 
> org.apache.ignite.internal.sql.engine.prepare.IgniteSqlValidator.validate(IgniteSqlValidator.java:194)
>       at 
> org.apache.ignite.internal.sql.engine.prepare.IgnitePlanner.validateAndGetTypeMetadata(IgnitePlanner.java:303)
>       at 
> org.apache.ignite.internal.sql.engine.prepare.PrepareServiceImpl.lambda$prepareQuery$3(PrepareServiceImpl.java:389)
>       at 
> java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run$$$capture(CompletableFuture.java:1700)
> {noformat}
> This happened because we redirect the multi-statement execution with single 
> statement to single-statement execution workflow, but the AST tree in the 
> cache remains modified by validator.
> E.g. 
> for single statement execution we generating AST each time.
> {code:Java}
> ParsedResult result = new ParsedResultImpl(
>                 queryType,
>                 query,
>                 normalizedQuery,
>                 parsedStatement.dynamicParamsCount(),
>                 () -> {
>                     // Descendants of SqlNode class are mutable, thus we must 
> use every
>                     // syntax node only once to avoid problem. But we already 
> parsed the
>                     // query once to get normalized result. An `unparse` 
> operation is known
>                     // to be safe, so let's reuse result of parsing for the 
> first invocation
>                     // of `parsedTree` method to avoid double-parsing for one 
> time queries.
>                     SqlNode ast = holder.getAndSet(null);
>                     if (ast != null) {
>                         return ast;
>                     }
>                     return IgniteSqlParser.parse(query, 
> StatementParseResult.MODE).statement();
>                 }
> {code}
> but for multi-statement we use the same AST.
> {code:Java}
>             results.add(new ParsedResultImpl(
>                     queryType,
>                     normalizedQuery,
>                     normalizedQuery,
>                     result.dynamicParamsCount(),
>                     () -> parsedTree
>             ));
> {code}
> *NOTE*: This issue is critical for Ignite CLI (see attachment), we need to 
> add CLI test for this case



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

Reply via email to