Skip to content

mssql: accept table constraints and indexes in table variables#391

Merged
rebelice merged 3 commits into
mainfrom
fix-mssql-tablevar-constraint
Jul 20, 2026
Merged

mssql: accept table constraints and indexes in table variables#391
rebelice merged 3 commits into
mainfrom
fix-mssql-tablevar-constraint

Conversation

@rebelice

Copy link
Copy Markdown
Collaborator

Fixes BYT-9909 statement 6's remaining gap (the v3.20.1 lexer error at 45:9 no longer reproduces on main; the surviving failure was DECLARE @t TABLE (..., PRIMARY KEY CLUSTERED (col))).

Problem

Table-variable bodies only accepted column definitions — any table-level constraint rejected at the keyword. Cross-validated minimal repro: DECLARE @t TABLE (p_year INT, PRIMARY KEY (p_year)) rejects while the same constraint passes in CREATE TABLE and the inline column form passes in table variables.

Fix

The element grammar is identical to CREATE TABLE's, so the body loop now dispatches to the existing parseTableConstraint (CONSTRAINT / PRIMARY KEY / UNIQUE / CHECK) and parseInlineTableIndex (INDEX) alongside parseColumnDef. Engine-verified on SQL Server 2019: table-level PK CLUSTERED, named constraints, CHECK, and inline INDEX all execute inside DECLARE @t TABLE. The customer's full function text now parses.

Tests

TestDeclareTableVariableConstraints: 9 shapes — PK bare/CLUSTERED/NONCLUSTERED, named constraint, UNIQUE, CHECK, inline INDEX, the inline-column-PK control, and the customer's exact declarations.

🤖 Generated with Claude Code

DECLARE @t TABLE bodies only accepted column definitions, rejecting
table-level constraints at the constraint keyword — a customer
function declaring PRIMARY KEY CLUSTERED entries in table variables
failed SQL review (BYT-9909 statement 6). The element grammar is the
same as CREATE TABLE: reuse parseTableConstraint (CONSTRAINT / PRIMARY
KEY / UNIQUE / CHECK) and parseInlineTableIndex (INDEX) inside the
table-variable body. Engine-verified on SQL Server 2019: table-level
PK CLUSTERED, named constraints, CHECK, and inline INDEX all execute
in DECLARE @t TABLE.

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: 26462424a2

ℹ️ 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 mssql/parser/declare_set.go Outdated
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: 8679db49d4

ℹ️ 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 mssql/parser/declare_set.go Outdated
Comment thread mssql/parser/declare_set.go
Comment thread mssql/parser/declare_set.go
Comment thread mssql/parser/declare_set.go Outdated
The review flagged the CONSTRAINT name prefix as invalid inside
DECLARE @t TABLE, and the engine confirms (SQL Server 2019: Msg 156,
Incorrect syntax near CONSTRAINT) — my original fence asserted
acceptance without engine-verifying that specific shape. Remove
kwCONSTRAINT from the table-variable dispatch and flip the fence to a
reject with the engine citation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rebelice
rebelice merged commit 726bb7f 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: c624a86781

ℹ️ 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 on lines +142 to +144
if p.cur.Type == kwPRIMARY ||
p.cur.Type == kwUNIQUE || p.cur.Type == kwCHECK {
constraint, err := p.parseTableConstraint()

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 Require KEY after PRIMARY in table constraints

When a table-variable element begins with PRIMARY, this dispatch reuses parseTableConstraint, where p.match(kwKEY) makes KEY optional; therefore DECLARE @t TABLE (a INT, PRIMARY (a)) is accepted. The bundled SQL Server grammar requires the Primary Key token pair in uniqueConstraintHeader (mssql/parser/bnf/TSql170.g:30347-30360), so this table-variable path should reject a PRIMARY constraint without KEY.

Useful? React with 👍 / 👎.

Comment on lines +143 to +144
p.cur.Type == kwUNIQUE || p.cur.Type == kwCHECK {
constraint, err := p.parseTableConstraint()

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 Require a parenthesized predicate for CHECK constraints

For an element such as CHECK, b INT, parseTableConstraint ignores a failed expect('('), returns a CHECK node, and leaves the comma for this loop, so DECLARE @t TABLE (a INT, CHECK, b INT) parses successfully. SQL Server's checkConstraint grammar requires both parentheses and a boolean expression (mssql/parser/bnf/TSql170.g:30679-30699), so the newly accepted CHECK path must reject a missing predicate.

Useful? React with 👍 / 👎.

Comment on lines +151 to +152
} else if p.cur.Type == kwINDEX {
idx, err := p.parseInlineTableIndex()

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 Require key columns for ordinary inline indexes

When an element is INDEX ix_a without a column list or COLUMNSTORE type, parseInlineTableIndex returns a valid-looking node and this loop accepts DECLARE @t TABLE (a INT, INDEX ix_a). The bundled inlineIndexTableDefinition grammar explicitly raises a syntax error when a non-clustered-columnstore index lacks its column list (mssql/parser/bnf/TSql170.g:29330-29341), so this new table-variable path should enforce the same requirement.

Useful? React with 👍 / 👎.

Comment on lines +151 to +152
} else if p.cur.Type == kwINDEX {
idx, err := p.parseInlineTableIndex()

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 Validate inline-index option names

For an inline index with WITH, this dispatch reuses parseInlineTableIndex, whose parseOptionList accepts any identifier as an option name; consequently DECLARE @t TABLE (a INT, INDEX ix_a (a) WITH (BOGUS = 1)) succeeds. The bundled inlineIndexTableDefinition grammar routes this clause through indexOptionList and its allowed-option verification (mssql/parser/bnf/TSql170.g:29345-29354), so table-variable indexes should not approve arbitrary option names.

Useful? React with 👍 / 👎.

Comment on lines +151 to +152
} else if p.cur.Type == kwINDEX {
idx, err := p.parseInlineTableIndex()

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 Require the table-variable closing parenthesis

When the newly supported final element is an index, parseInlineTableIndex consumes the index column list's ) and can return at EOF, after which this body discards the failed expect(')') for the enclosing table definition. As a result, DECLARE @t TABLE (a INT, INDEX ix_a (a) now parses successfully despite missing the table body's closing parenthesis; before this dispatch, INDEX remained unconsumed and caused a syntax error, so the enclosing delimiter error must be propagated.

Useful? React with 👍 / 👎.

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