Skip to content

feat: parse PostgreSQL IMPORT FOREIGN SCHEMA - #2909

Open
minleejae wants to merge 1 commit into
JSQLParser:masterfrom
minleejae:feat/postgresql-import-foreign-schema-20261110
Open

minleejae wants to merge 1 commit into
JSQLParser:masterfrom
minleejae:feat/postgresql-import-foreign-schema-20261110

Conversation

@minleejae

Copy link
Copy Markdown
Contributor

Purpose

PostgreSQL's IMPORT FOREIGN SCHEMA failed to parse at IMPORT:

IMPORT FOREIGN SCHEMA remote FROM SERVER srv INTO local;
IMPORT FOREIGN SCHEMA remote LIMIT TO (t1, t2) FROM SERVER srv INTO local
    OPTIONS (import_default 'true');
IMPORT FOREIGN SCHEMA remote EXCEPT (t3) FROM SERVER srv INTO local;

It is the one statement of PostgreSQL's foreign-data DDL that had no model: CREATE/ALTER/DROP
of FOREIGN DATA WRAPPER, SERVER, USER MAPPING and FOREIGN TABLE already parse.

Current design (As-is)

  • IMPORT starts a statement only with the EXASOL dialect (Import, and (IMPORT …) as a
    sub-select source). Elsewhere it is used inside MySQL's ALTER TABLE … IMPORT TABLESPACE /
    IMPORT PARTITION.
  • With the PostgreSQL dialect or without a dialect, IMPORT FOREIGN SCHEMA … is a parse error,
    or an UnsupportedStatement when unsupported statements are allowed.
  • The foreign-data statements share ForeignDataStatement (the OPTIONS (…) list of
    ForeignDataOption values, its rendering and its expression traversal) and the
    ForeignDataOptions production.
  • LOCK and TRUNCATE share TableReference and the RelationTarget production for
    PostgreSQL's relation_expr (name, name *, ONLY name, ONLY (name)).

Target design (To-be)

  • Model. ImportForeignSchema in net.sf.jsqlparser.statement.foreign extends
    ForeignDataStatement:

    • getRemoteSchema(), getServer(), getLocalSchema(): the three names;
    • getQualification(): LIMIT_TO, EXCEPT, or null when the whole schema is imported;
    • getTables(): the listed tables as TableReference values (a read-only snapshot);
      setTables(qualification, tables) changes the kind and the list together and rejects a kind
      without tables or tables without a kind;
    • getOptions(): the inherited ForeignDataOption list.
  • Grammar. One production follows PostgreSQL's ImportForeignSchemaStmt:

    IMPORT FOREIGN SCHEMA name [ { LIMIT TO | EXCEPT } ( relation_expr [, ...] ) ]
        FROM SERVER name INTO name [ OPTIONS ( option 'value' [, ...] ) ]
    
    • The table list reuses RelationTarget, because PostgreSQL's grammar reads a
      relation_expr_list there: LIMIT TO (ONLY t1, s.t2 *) is accepted by the server and by the
      parser, LIMIT TO (ONLY t1 *) and LIMIT TO ((t1)) by neither.
    • OPTIONS reuses ForeignDataOptions in its CREATE mode, so ADD, SET and DROP are
      rejected, as are an empty list and a value that is not a string constant.
    • No keyword is added: IMPORT, FOREIGN, SCHEMA, LIMIT, TO, EXCEPT, FROM and INTO
      are existing tokens, and SERVER and OPTIONS are read as contextual words, so server,
      options, import and schema stay usable as names
      (IMPORT FOREIGN SCHEMA server FROM SERVER server INTO server).
  • Dialects. The statement is read with the PostgreSQL dialect or without one, when FOREIGN
    follows IMPORT. The EXASOL dialect keeps IMPORT for its own statement, which FOREIGN
    cannot continue; MySQL, MariaDB, Oracle, SQL Server and EXASOL keep rejecting
    IMPORT FOREIGN SCHEMA. ALTER TABLE … IMPORT TABLESPACE is unchanged.

  • Wiring. StatementVisitor default method, StatementVisitorAdapter (visits the option
    values like the other foreign-data statements), StatementDeParser (through the shared
    appendTo), TablesNamesFinder (no tables: the listed names belong to the remote schema),
    and feature analysis (MODIFIES_SCHEMA). Validation gets the feature importForeignSchema,
    enabled by the PostgreSQL capabilities and by FeaturesAllowed.DDL.

Reference: PostgreSQL IMPORT FOREIGN SCHEMA.

Not included:

  • A reserved word as an option name (OPTIONS (select 'y')). PostgreSQL reads option names as
    column labels; the shared ForeignDataOptions production reads them as object names, for this
    statement as for CREATE SERVER, CREATE USER MAPPING and CREATE FOREIGN TABLE. That
    belongs in a change to the shared production.

Validation

  • New ImportForeignSchemaTest with 59 test cases. They cover parsing with the PostgreSQL
    dialect and without one, toString, StatementDeParser, reparse, AST fields, the AST node,
    rejected forms, other dialects and other uses of IMPORT keeping their readings, statement
    boundaries with and without semicolons (including after an unsupported statement), visitors,
    TablesNamesFinder, feature analysis, validation and the model API.

  • Updated tests:

    • ASTNodeCoverageTest walks one of the new forms.
    • Six tests (DiscardStatementTest, ReindexTest, CheckpointStatementTest,
      ClusterStatementTest, VacuumStatementTest, SecurityLabelTest) used
      IMPORT FOREIGN SCHEMA … as their example of an unsupported statement; they now use
      EXASOL's IMPORT INTO t FROM LOCAL CSV FILE 'f.csv', which stays unsupported there.
  • These tests fail on master, where the statement does not parse.

  • ./gradlew spotlessApply check jar: 13392 tests, 25 skipped, 0 failures. Grammar ambiguity check, Checkstyle, PMD,
    SpotBugs and JaCoCo pass.

  • Compared with master (e27929f76) on the DDL/DCL and non-DDL survey corpora, nothing
    regressed or changed. The corpora do not contain IMPORT FOREIGN SCHEMA; a separate list of 24
    accepted forms (PostgreSQL dialect and no dialect) parses and round-trips, and 35 forms that
    PostgreSQL rejects are rejected.

  • PostgreSQL 18.6 accepts all 19 IMPORT FOREIGN SCHEMA statements the tests accept, all of which master rejects; they fail only when run (the foreign server cannot be reached). This includes LIMIT TO (ONLY t1, t2 *), EXCEPT (ONLY (t1)), quoted names, dollar-quoted and escape-string option values, and server, options, import and schema used as names. It reports a syntax error for all 35 statements the tests reject, including an empty table list, ONLY t1 *, ((t1)), LIMIT TO together with EXCEPT, OPTIONS (), ADD/SET/DROP in the option list and a qualified schema or server name. One form PostgreSQL accepts is not parsed: a reserved word as an option name (OPTIONS (select 'y')), which the shared option production rejects for CREATE SERVER on master as well.

AI assistance: prepared with Claude Code.

Add ImportForeignSchema for IMPORT FOREIGN SCHEMA remote [{LIMIT TO | EXCEPT} (table, ...)] FROM SERVER server INTO local [OPTIONS (...)], read with the PostgreSQL dialect or without one. The table list reuses the relation targets of LOCK and TRUNCATE, and OPTIONS the generic options of the other foreign-data statements. The EXASOL IMPORT statement and other dialects are unchanged.

Signed-off-by: minleejae <mmj9808@gmail.com>

This branch has not been deployed

No deployments
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.

1 participant