Skip to content

oracle: support <<label>> on every PL/SQL statement - #390

Merged
rebelice merged 5 commits into
mainfrom
fix-oracle-plsql-labels
Jul 20, 2026
Merged

oracle: support <<label>> on every PL/SQL statement#390
rebelice merged 5 commits into
mainfrom
fix-oracle-plsql-labels

Conversation

@rebelice

Copy link
Copy Markdown
Collaborator

Fixes the confirmed defect from BYT-9909 statement 3 (customer package failing SQL review at line 498, <<end_check>> followed by a record-field assignment).

Problem

parsePLSQLStatement treated tokLABELOPEN as "labeled block" and dispatched to parsePLSQLBlock unconditionally, so a <<label>> before any non-block statement failed. Systematic probe: labeled assignment / NULL / procedure call / IF / double labels all rejected, while the same statements without labels pass. Engine-verified on Oracle 23ai Free: one or more labels are legal before ANY statement (GOTO targets included, multi-label included).

Fix

Consume all leading <<ident>> labels, recurse into statement parsing, then attach: PLSQLBlock/PLSQLLoop keep their first label on their existing Label field (END-label matching untouched); any other statement — or labels beyond the first — ride on a new PLSQLLabeledStatement wrapper (Labels []string + Statement), serialized with :labels/:statement and wired into the generated walker.

With this fix the customer's package body parses past the failing line (the truncated attachment text now errors only at its Excel truncation point).

Tests

TestPLSQLStatementLabels: 12 accept shapes (every statement kind, GOTO-target flow, loop END-label matching, the customer's package-body shape), unclosed-label reject, and AST-shape assertions (loop keeps Label, wrapper carries multi-labels, :labels serialized).

Consumer note for the next bytebase bump: walkthrough/span switches should treat PLSQLLabeledStatement as transparent (recurse into .Statement); labels carry no data flow.

🤖 Generated with Claude Code

rebelice and others added 2 commits July 20, 2026 11:46
Statement labels were only recognized as block prefixes: the label
branch of parsePLSQLStatement dispatched straight to parsePLSQLBlock,
so a label before any other statement — assignment, NULL, procedure
call, IF, a GOTO target — failed to parse. Engine-verified on Oracle
23ai Free: one or more labels are legal before ANY statement, and a
customer package failed review at exactly this shape
(<<end_check>> followed by a record-field assignment).

Consume all leading labels, parse the following statement, and attach:
blocks and loops keep their first label on their own Label field
(END-label matching unchanged), and any other statement — or labels
beyond the first — ride on a new PLSQLLabeledStatement wrapper node,
serialized with :labels/:statement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
INSERT INTO t VALUES record required a parenthesized tuple, rejecting
the documented record form at the record's name — the error text
matches two statements in the same customer report (BYT-9909), whose
packages fill %ROWTYPE records field by field before inserting them.

When VALUES is not followed by '(', parse an expression as the record
source: bare records, package-qualified records, and FORALL collection
elements (VALUES arr(i)) — all engine-verified on Oracle 23ai Free,
including RETURNING after the record. The value lands on a new
InsertStmt.ValuesRecord field so consumers can tell the record form
from the tuple list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rebelice

Copy link
Copy Markdown
Collaborator Author

Second commit folds in the VALUES-record fix (BYT-9909 statements 4/5 hypothesis, error text byte-identical): INSERT INTO t VALUES record — bare, package-qualified, FORALL arr(i), and RETURNING forms, all engine-verified on 23ai. New InsertStmt.ValuesRecord field keeps the record form distinct from the tuple list. Credit: the construct was isolated by Danny's seven-idiom record probe.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 28a082ced9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread oracle/ast/parsenodes.go
Comment thread oracle/parser/plsql_block.go
Comment thread oracle/parser/plsql_block.go
Comment thread oracle/ast/outfuncs.go Outdated
Comment thread oracle/parser/plsql_block.go
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 89aa57b2f7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread oracle/parser/plsql_block.go
Comment thread oracle/parser/insert.go
rebelice and others added 2 commits July 20, 2026 12:27
- NodeLoc knows PLSQLLabeledStatement, so location consumers keep the
  statement range for labeled non-block statements.
- A labeled block or loop extends its Loc.Start to the label position,
  restoring the pre-change property that the node's range covers its
  own Label field.
- <<>> is rejected (engine: PLS-00103); label names must be nonempty.
- :labels entries are %q-quoted — <<"a b">> is a legal quoted label
  (engine-verified) and was serialized ambiguously.
- Mismatched END LOOP labels stay ACCEPTED, refuting the review
  suggestion with engine evidence: Oracle runs
  <<a>> LOOP ... END LOOP b; successfully. Fenced so it is not
  "fixed" into a rejection later.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rebelice
rebelice merged commit 9772147 into main Jul 20, 2026
23 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1d81724a3d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return p.parsePLSQLBlock()
start := p.pos()
var labels []string
for p.cur.Type == tokLABELOPEN {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Route top-level labels through the multi-label parser

This multi-label path runs only from parsePLSQLStatements; the top-level tokLABELOPEN dispatch in oracle/parser/parser.go still calls parsePLSQLBlock directly, which consumes only one label. Consequently an engine-supported anonymous block such as <<a>> <<b>> BEGIN NULL; END; fails when parsePLSQLBlock encounters the second <<, despite the new one-or-more-label guarantee. Route the top-level label case through equivalent multi-label handling.

Useful? React with 👍 / 👎.

rebelice added a commit to bytebase/bytebase that referenced this pull request Jul 27, 2026
variable constraints

Picks up three engine-verified parser fixes (bytebase/omni#390-#392):

- PL/SQL statement labels (<<label>>) parse as labeled statements or
  attach to blocks and loops; labeled non-block statements are wrapped
  in a PLSQLLabeledStatement node that generic AST walkers traverse
  transparently.
- Local procedure/function declarations in PL/SQL declaration sections
  parse via the package-item parsers, closing both the rejection of
  parameterized forms and the silent mis-parse of parameterless forms
  into variable-declaration fragments.
- MSSQL table variables accept table-level constraints and indexes
  (unnamed, engine-verified; the named-constraint form stays rejected
  like the engine).

No Bytebase-side consumer changes: advisor and span walkers use
generic AST traversal, verified against the new node shapes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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