Skip to content

fix: EXPLAIN CREATE/DROP TABLE to validate table existence (#27192)#27217

Open
garimauttam wants to merge 6 commits intoprestodb:masterfrom
garimauttam:fix-explain-table-validation-27192
Open

fix: EXPLAIN CREATE/DROP TABLE to validate table existence (#27192)#27217
garimauttam wants to merge 6 commits intoprestodb:masterfrom
garimauttam:fix-explain-table-validation-27192

Conversation

@garimauttam
Copy link
Contributor

@garimauttam garimauttam commented Feb 26, 2026

Summary

Fixes #27192 - EXPLAIN CREATE/DROP TABLE now properly validates table existence.

Problem

  • EXPLAIN CREATE TABLE succeeds even when table already exists
  • EXPLAIN DROP TABLE succeeds even when table doesn't exist
  • Both should respect IF EXISTS/IF NOT EXISTS clauses

Changes

  • Modified ExplainRewrite.java to validate table existence before processing EXPLAIN statements
  • Added validateTableExistence() method that checks table metadata
  • Added instanceof guard to only validate CREATE/DROP TABLE statements (performance optimization)
  • Reused innerStatement variable for better code clarity
  • Throws appropriate errors: TABLE_ALREADY_EXISTS or MISSING_TABLE
  • Respects IF EXISTS/IF NOT EXISTS clauses

Testing

Added comprehensive test coverage in TestExplainCreateDropTable.java with 8 test cases:

CREATE TABLE scenarios:

  • EXPLAIN CREATE TABLE fails when table exists
  • EXPLAIN CREATE TABLE succeeds when table doesn't exist
  • EXPLAIN CREATE TABLE IF NOT EXISTS succeeds when table exists
  • EXPLAIN CREATE TABLE IF NOT EXISTS succeeds when table doesn't exist

DROP TABLE scenarios:

  • EXPLAIN DROP TABLE fails when table doesn't exist
  • EXPLAIN DROP TABLE succeeds when table exists
  • EXPLAIN DROP TABLE IF EXISTS succeeds when table doesn't exist
  • EXPLAIN DROP TABLE IF EXISTS succeeds when table exists

Performance Considerations

  • instanceof guard ensures validation only runs for CREATE/DROP TABLE statements
  • Avoids unnecessary method calls and branching for other EXPLAIN statements (SELECT, INSERT, etc.)

Files Changed

  • presto-main-base/src/main/java/com/facebook/presto/sql/rewrite/ExplainRewrite.java
  • presto-tests/src/test/java/com/facebook/presto/tests/TestExplainCreateDropTable.java (new)

== NO RELEASE NOTE ==

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Feb 26, 2026

Reviewer's Guide

ExplainRewrite now validates metadata for EXPLAIN CREATE/DROP TABLE statements, throwing appropriate semantic errors when table existence constraints are violated, and a new test suite verifies the behavior for IF EXISTS/IF NOT EXISTS combinations.

Sequence diagram for EXPLAIN CREATE/DROP TABLE validation in ExplainRewrite

sequenceDiagram
    actor User
    participant Engine
    participant ExplainRewrite
    participant Visitor
    participant Metadata
    participant MetadataResolver

    User->>Engine: submit EXPLAIN CREATE/DROP TABLE statement
    Engine->>ExplainRewrite: rewrite(session, metadata, parser, queryExplainer, procedureRegistry, warningCollector, query, viewDefinitionReferences)
    ExplainRewrite->>Visitor: new Visitor(session, metadata, parser, queryExplainer, procedureRegistry, warningCollector, query, viewDefinitionReferences)
    ExplainRewrite->>Visitor: process(Explain)
    Visitor->>Visitor: visitExplain(Explain)
    Visitor->>Visitor: validateTableExistence(innerStatement)

    alt CREATE TABLE without IF NOT EXISTS
        Visitor->>Metadata: getMetadataResolver(session)
        Metadata-->>Visitor: MetadataResolver
        Visitor->>MetadataResolver: getTableHandle(tableName)
        MetadataResolver-->>Visitor: Optional<TableHandle>
        alt table exists
            Visitor->>Visitor: throw SemanticException(TABLE_ALREADY_EXISTS)
        else table does not exist
            Visitor->>Visitor: continue processing
        end
    else DROP TABLE without IF EXISTS
        Visitor->>Metadata: getMetadataResolver(session)
        Metadata-->>Visitor: MetadataResolver
        Visitor->>MetadataResolver: getTableHandle(tableName)
        MetadataResolver-->>Visitor: Optional<TableHandle>
        alt table handle absent
            Visitor->>Visitor: throw SemanticException(MISSING_TABLE)
        else table handle present
            Visitor->>Visitor: continue processing
        end
    else IF NOT EXISTS or IF EXISTS specified
        Visitor->>Visitor: skip validation
    end

    Visitor-->>ExplainRewrite: rewritten Explain statement or error
    ExplainRewrite-->>Engine: rewritten statement or error
    Engine-->>User: EXPLAIN plan or error response
Loading

Class diagram for ExplainRewrite Visitor and table existence validation

classDiagram
    class ExplainRewrite {
        +Statement rewrite(Session session, String query, ViewDefinitionReferences viewDefinitionReferences)
    }

    class Visitor {
        -Session session
        -Metadata metadata
        -BuiltInQueryPreparer queryPreparer
        -Optional_QueryExplainer queryExplainer
        -WarningCollector warningCollector
        -String query
        -ViewDefinitionReferences viewDefinitionReferences
        +Visitor(Session session, Metadata metadata, SqlParser parser, Optional_QueryExplainer queryExplainer, ProcedureRegistry procedureRegistry, WarningCollector warningCollector, String query, ViewDefinitionReferences viewDefinitionReferences)
        +Node process(Node node, Void context)
        +Node visitExplain(Explain node, Void context)
        -void validateTableExistence(Statement statement)
    }

    class Session
    class Metadata {
        +MetadataResolver getMetadataResolver(Session session)
    }

    class MetadataResolver {
        +Optional_TableHandle getTableHandle(QualifiedObjectName tableName)
    }

    class Statement
    class Explain {
        +Statement getStatement()
        +boolean isAnalyze()
        +boolean isVerbose()
        +Map_ExplainOption_Expression getOptions()
    }

    class CreateTable {
        +QualifiedName getName()
        +boolean isNotExists()
    }

    class DropTable {
        +QualifiedName getTableName()
        +boolean isExists()
    }

    class QualifiedObjectName
    class TableHandle
    class SemanticException
    class SqlParser
    class BuiltInQueryPreparer
    class QueryExplainer
    class Optional_QueryExplainer
    class Optional_TableHandle
    class ProcedureRegistry
    class WarningCollector
    class ViewDefinitionReferences
    class Node

    ExplainRewrite ..> Visitor : uses
    Visitor ..|> AstVisitor_Node_Void
    class AstVisitor_Node_Void

    Visitor o--> Session
    Visitor o--> Metadata
    Visitor o--> BuiltInQueryPreparer
    Visitor o--> Optional_QueryExplainer
    Visitor o--> WarningCollector
    Visitor o--> ViewDefinitionReferences

    Visitor --> Explain : visitExplain
    Visitor --> Statement : validateTableExistence

    validateTableExistence ..> CreateTable : checks
    validateTableExistence ..> DropTable : checks

    Metadata --> MetadataResolver : returns
    MetadataResolver --> Optional_TableHandle : returns

    SemanticException <.. Visitor : thrown

    Statement <|-- Explain
    Statement <|-- CreateTable
    Statement <|-- DropTable
Loading

File-Level Changes

Change Details Files
Add table-existence validation for EXPLAIN CREATE/DROP TABLE in the explain rewriter.
  • Pass Metadata into ExplainRewrite.Visitor and store it as a field for use during validation.
  • In visitExplain, extract the inner statement and invoke a new validation routine before further EXPLAIN processing.
  • Implement validateTableExistence() to resolve the target table name using createQualifiedObjectName and query Metadata for a TableHandle.
  • For EXPLAIN CREATE TABLE without IF NOT EXISTS, throw TABLE_ALREADY_EXISTS if the table is present; for EXPLAIN DROP TABLE without IF EXISTS, throw MISSING_TABLE if the table is absent.
presto-main-base/src/main/java/com/facebook/presto/sql/rewrite/ExplainRewrite.java
Introduce tests covering EXPLAIN CREATE/DROP TABLE table-existence behavior.
  • Create a new TestExplainCreateDropTable test class extending AbstractTestQueries with a local QueryRunner.
  • In @BeforeClass, create a persistent test table; in @afterclass, drop both the existing and newly-created tables using IF EXISTS.
  • Add tests asserting failure for EXPLAIN CREATE TABLE when the table exists and EXPLAIN DROP TABLE when it does not.
  • Add tests asserting success for EXPLAIN CREATE TABLE IF NOT EXISTS on existing tables, EXPLAIN CREATE TABLE on new tables, EXPLAIN DROP TABLE IF EXISTS on non-existing tables, and EXPLAIN DROP TABLE on existing tables using explain/explain-analyze helpers.
presto-tests/src/test/java/com/facebook/presto/tests/TestExplainCreateDropTable.java

Assessment against linked issues

Issue Objective Addressed Explanation
#27192 Ensure EXPLAIN CREATE TABLE validates table existence: it must fail with an appropriate error when the target table already exists, and succeed when the table does not exist or when IF NOT EXISTS is specified.
#27192 Ensure EXPLAIN DROP TABLE validates table existence: it must fail with an appropriate error when the target table does not exist, and succeed when the table exists or when IF EXISTS is specified.

Possibly linked issues

  • #(not specified): PR changes ExplainRewrite and adds tests so EXPLAIN CREATE/DROP TABLE now errors or succeeds exactly as issue specifies

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • In visitExplain, consider restricting validateTableExistence to only run when the inner statement is a CreateTable or DropTable before calling it (e.g., with an instanceof guard at the call site) to avoid an extra method call and branching on every EXPLAIN statement.
  • In visitExplain, you already bind Statement innerStatement = node.getStatement(); but still call process(node.getStatement(), context) in the analyze branch; using innerStatement there would avoid re-fetching the same value and keep the method slightly clearer.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `visitExplain`, consider restricting `validateTableExistence` to only run when the inner statement is a `CreateTable` or `DropTable` before calling it (e.g., with an `instanceof` guard at the call site) to avoid an extra method call and branching on every EXPLAIN statement.
- In `visitExplain`, you already bind `Statement innerStatement = node.getStatement();` but still call `process(node.getStatement(), context)` in the analyze branch; using `innerStatement` there would avoid re-fetching the same value and keep the method slightly clearer.

## Individual Comments

### Comment 1
<location path="presto-tests/src/test/java/com/facebook/presto/tests/TestExplainCreateDropTable.java" line_range="51-60" />
<code_context>
+        assertUpdate("DROP TABLE IF EXISTS test_new_table");
+    }
+
+    @Test
+    public void testExplainCreateTableAlreadyExists()
+    {
+        // EXPLAIN CREATE TABLE should fail when table already exists
+        assertQueryFails(
+                "EXPLAIN CREATE TABLE test_explain_table (id INTEGER)",
+                ".*Table.*test_explain_table.*already exists.*");
+    }
+
+    @Test
+    public void testExplainCreateTableIfNotExistsAlreadyExists()
+    {
+        // EXPLAIN CREATE TABLE IF NOT EXISTS should succeed even if table exists
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test for `EXPLAIN CREATE TABLE IF NOT EXISTS` when the table does not already exist

We currently only cover the case where the table already exists. Please also add an assertion that `EXPLAIN CREATE TABLE IF NOT EXISTS test_new_table (id INTEGER)` succeeds when `test_new_table` does not exist, to confirm the new existence-checking logic preserves behavior in that scenario.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@garimauttam garimauttam changed the title Fix EXPLAIN CREATE/DROP TABLE to validate table existence (#27192) Fix: EXPLAIN CREATE/DROP TABLE to validate table existence (#27192) Feb 26, 2026
@garimauttam garimauttam changed the title Fix: EXPLAIN CREATE/DROP TABLE to validate table existence (#27192) fix: EXPLAIN CREATE/DROP TABLE to validate table existence (#27192) Feb 26, 2026
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.

EXPLAIN CREATE TABLE should fail for an already existing table name

1 participant