Skip to content

feat(schema): support table-level constraints with array syntax in Drizzle v1#4065

Open
kobayashiyabako16g wants to merge 3 commits intoliam-hq:mainfrom
kobayashiyabako16g:feat/drizzle-v1-foreign-keys
Open

feat(schema): support table-level constraints with array syntax in Drizzle v1#4065
kobayashiyabako16g wants to merge 3 commits intoliam-hq:mainfrom
kobayashiyabako16g:feat/drizzle-v1-foreign-keys

Conversation

@kobayashiyabako16g
Copy link
Copy Markdown

@kobayashiyabako16g kobayashiyabako16g commented Mar 18, 2026

Issue

Why is this change needed?

Drizzle ORM v1 uses (table) => [...] array syntax for table-level constraints,
but this form was not parsed, causing foreign key relationships to be missing from the ERD.

Added

check() and unique() parsing logic for PostgreSQL is ported directly from the MySQL implementation, as both dialects share the same Drizzle ORM API for these constraints.

Changed

  • Extended tableParser to handle ArrayExpression return type in the third argument of table definitions
    (previously only ObjectExpression was supported)
  • Added extractColumnNames / extractTableAndColumns AST utilities for parsing column references from
    array expressions
  • Added DrizzleForeignKeyDefinition type and foreignKeys field to table definition types
  • Extended converter to map DrizzleForeignKeyDefinitionForeignKeyConstraint

Background

Although there was a comment on the issue indicating that the implementation was already in progress, I have been waiting for some time and decided to submit this PR to move things forward.
If a PR from the original commenter is submitted during the review, I am happy to withdraw this one.


Open with Devin

Summary by CodeRabbit

  • New Features

    • Table-level foreign key support with custom constraint names and onDelete/onUpdate actions
    • Optional naming for composite primary keys
    • Check and table-level unique constraints support
    • Better resolution for composite foreign keys and cross-table references
  • Tests

    • Expanded parser tests covering array-style table definitions, FK/index/unique/check/primaryKey scenarios

…izzle v1

Add support for Drizzle ORM v1's `(table) => [...]` array syntax for
table-level constraints in both PostgreSQL and MySQL parsers:

- foreignKey(): supports custom name, composite columns, onDelete/onUpdate
- check(): ported from MySQL implementation to PostgreSQL
- unique(): ported from MySQL implementation to PostgreSQL
- index() / uniqueIndex(): array syntax parity with object syntax
- primaryKey() (composite): supports optional custom name via `name` option
@kobayashiyabako16g kobayashiyabako16g requested a review from a team as a code owner March 18, 2026 15:21
@kobayashiyabako16g kobayashiyabako16g requested review from NoritakaIkeda, Copilot and sasamuku and removed request for a team March 18, 2026 15:21
@giselles-ai
Copy link
Copy Markdown

giselles-ai bot commented Mar 18, 2026

Unexpected error on running flow

Step 1
🟢
On Pull Request OpenedStatus: Success Updated: Mar 18, 2026 3:21pm
Step 2
gpt-5Status: In progress Updated: Not started
Step 3
Create Pull Request CommentStatus: Pending Updated: Not started

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 18, 2026

📝 Walkthrough

Walkthrough

Adds parsing, AST helpers, types, converters, and tests to support Drizzle v1 table-level constraints (foreign keys, composite primary keys, indexes, unique/check) for MySQL and PostgreSQL; converts parsed definitions into internal constraint objects.

Changes

Cohort / File(s) Summary
Tests
frontend/packages/schema/src/parser/drizzle/__tests__/unified.test.ts
Added parameterized tests for v1 array/table-level syntax: foreignKey() (single/composite, custom/default names, onDelete/onUpdate), index()/uniqueIndex(), composite primaryKey(), check() and table-level unique().
AST Utilities (MySQL)
frontend/packages/schema/src/parser/drizzle/mysql/astUtils.ts
Added helpers: isCallToFunction, extractColumnNames, extractTableAndColumns, reconstructSqlTemplate, and extractConfigObject to parse arrays, templates, and call configs.
AST Utilities (Postgres)
frontend/packages/schema/src/parser/drizzle/postgres/astUtils.ts
Added same helper set as MySQL: isCallToFunction, extractColumnNames, extractTableAndColumns, reconstructSqlTemplate, and extractConfigObject.
Types (MySQL)
frontend/packages/schema/src/parser/drizzle/mysql/types.ts
Added DrizzleForeignKeyDefinition, made CompositePrimaryKeyDefinition.name optional, and added optional foreignKeys?: DrizzleForeignKeyDefinition[] on DrizzleTableDefinition.
Types (Postgres)
frontend/packages/schema/src/parser/drizzle/postgres/types.ts
Added DrizzleForeignKeyDefinition, DrizzleCheckConstraintDefinition, made composite PK name optional, and added foreignKeys?: DrizzleForeignKeyDefinition[] and constraints?: Record<string, Constraint> to DrizzleTableDefinition.
Table Parser (MySQL)
frontend/packages/schema/src/parser/drizzle/mysql/tableParser.ts
Parse arrow-function array returns to extract foreignKey(...) calls, check/unique constraints, indexes, and composite PK name; added parseForeignKeyDefinition and related helpers; uses reconstructSqlTemplate for check conditions.
Table Parser (Postgres)
frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts
Added comprehensive parsing for table-level foreignKey(), check(), unique() constraints, and optional named composite primaryKey(); introduced helpers to parse chained call expressions and configs; collects foreignKeys, indexes, and constraints.
Converter (MySQL)
frontend/packages/schema/src/parser/drizzle/mysql/converter.ts
Consume tableDef.foreignKeys: resolve target table names, map JS→DB columns, derive constraint names (use provided or generated composite name), map onDelete/onUpdate (default NO_ACTION), and attach ForeignKeyConstraint; use composite PK name when provided.
Converter (Postgres)
frontend/packages/schema/src/parser/drizzle/postgres/converter.ts
Added conversion for tableDef.foreignKeys and tableDef.constraints: resolve target names, map columns, derive constraint names, convert reference actions, and merge constraint objects; composite PK naming uses provided name when present.

Sequence Diagram

sequenceDiagram
    participant Parser as Table Parser
    participant AST as AST Utils
    participant Converter as Converter
    participant Store as Constraints Map

    Parser->>AST: detect foreignKey() / other calls
    AST-->>Parser: config object, column names, table+columns, sql template
    Parser-->>Parser: build DrizzleForeignKeyDefinition / Constraint defs
    Parser->>Converter: pass DrizzleTableDefinition with foreignKeys/constraints
    Converter->>Converter: resolve target table names, map columns to DB names
    Converter->>Converter: compute constraint names and actions
    Converter->>Store: add ForeignKeyConstraint / Constraint to constraints collection
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

Review effort 4/5

Suggested reviewers

  • FunamaYukina
  • junkisai
  • NoritakaIkeda
  • MH4GF

Poem

🐇 I nibble AST leaves all day,
I find the keys that hide away,
Tables linked in tidy rows,
Names and checks in tidy prose,
Hooray — constraints now come out to play!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and specifically describes the main change: adding support for table-level constraints with array syntax in Drizzle v1, which is the core objective of this changeset.
Description check ✅ Passed The PR description provides comprehensive coverage of changes, linked issues, implementation details, and references to Drizzle documentation, following the required template structure.
Linked Issues check ✅ Passed The PR successfully implements parsing for Drizzle v1 array-syntax constraints (foreignKey, primaryKey, index, unique, check) to resolve the missing relationships and composite keys issues (#4037, #4045), addressing the core coding requirements.
Out of Scope Changes check ✅ Passed All changes are scoped to parsing Drizzle v1 array-syntax constraints and related AST utilities; no unrelated or extraneous modifications are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 18, 2026

@kobayashiyabako16g is attempting to deploy a commit to the Liam Team on Vercel.

A member of the Team first needs to authorize it.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds support to the Drizzle schema parsers (PostgreSQL + MySQL) for Drizzle v1 table-level constraints written using the (table) => [...] array-return syntax, so ERDs include the missing relationships/constraints.

Changes:

  • Extend Postgres/MySQL table parsers to handle ArrayExpression returns for table-level definitions (FKs, indexes, composite PKs, plus check/unique constraints).
  • Add AST helpers for parsing column/table references from array expressions.
  • Extend converters and table definition types to carry table-level foreign keys (and, for Postgres, table-level check/unique constraints) into internal Constraint objects.
  • Add unified tests covering the new v1 array syntax parsing.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
frontend/packages/schema/src/parser/drizzle/postgres/types.ts Adds table-level FK + constraints fields to Postgres table definition types.
frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts Implements array-syntax parsing for FKs/indexes/PKs/check/unique on Postgres.
frontend/packages/schema/src/parser/drizzle/postgres/converter.ts Converts table-level foreign keys + constraints into internal constraint objects.
frontend/packages/schema/src/parser/drizzle/postgres/astUtils.ts Adds new shared AST utilities used by the Postgres parser.
frontend/packages/schema/src/parser/drizzle/mysql/types.ts Adds table-level FK + named composite PK support to MySQL table definition types.
frontend/packages/schema/src/parser/drizzle/mysql/tableParser.ts Implements array-syntax parsing for FKs/indexes/PKs/check/unique on MySQL.
frontend/packages/schema/src/parser/drizzle/mysql/converter.ts Converts table-level foreign keys into internal constraint objects.
frontend/packages/schema/src/parser/drizzle/mysql/astUtils.ts Adds new shared AST utilities used by the MySQL parser.
frontend/packages/schema/src/parser/drizzle/tests/unified.test.ts Adds test coverage for Drizzle v1 array-syntax table-level constraints.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +206 to +210
return (
isIdentifier(callExpr.callee) &&
callExpr.callee.value === functionName &&
callExpr.arguments.length > 0
)
Comment on lines +165 to +166
callExpr.callee.value === functionName &&
callExpr.arguments.length > 0
Comment on lines +543 to +545
// Extract the condition from the second argument (sql template literal)
let condition = 'true' // Default condition
if (callExpr.arguments.length > 1) {
Comment on lines +561 to +576
const firstQuasi = conditionExpr.template.quasis[0]
if (firstQuasi && firstQuasi.type === 'TemplateElement') {
// SWC TemplateElement has different structure than TypeScript's
// We need to access the raw string from the SWC AST structure
// Use property access with type checking to avoid type assertions
const hasRaw =
'raw' in firstQuasi && typeof firstQuasi.raw === 'string'
const hasCooked =
'cooked' in firstQuasi && typeof firstQuasi.cooked === 'string'

if (hasRaw) {
condition = firstQuasi.raw || ''
} else if (hasCooked) {
condition = firstQuasi.cooked || ''
}
}
Comment on lines +266 to +276
} else if (returnExpr.type === 'ArrayExpression') {
for (const element of returnExpr.elements) {
if (!element) continue
const elemExpr = getArgumentExpression(element)
if (elemExpr?.type === 'CallExpression') {
const fkDef = parseForeignKeyDefinition(elemExpr)
if (fkDef) {
table.foreignKeys = table.foreignKeys ?? []
table.foreignKeys.push(fkDef)
}
const indexDef = parseIndexDefinition(elemExpr, 'auto')
coderabbitai[bot]

This comment was marked as resolved.

…nt conditions in Drizzle

Previously, only the first quasi (static string part) was extracted from
sql
  template literals, so column interpolations like `${table.age}` were
dropped.
  Adds `reconstructSqlTemplate()` to properly join all quasis and
MemberExpression
  identifiers (falling back to "?") for both MySQL and PostgreSQL
parsers.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts (1)

319-417: Consider extracting shared parsing logic between MySQL and PostgreSQL.

The parseForeignKeyDefinition, parseForeignKeyConfig, parseForeignKeyProp, isValidForeignKeyDefinition, parseCheckConstraint, and parseUniqueConstraint functions are nearly identical between the MySQL and PostgreSQL parsers. Consider extracting these to a shared common/ or shared/ module to reduce duplication and ensure consistency.

This is optional given the current structure works correctly.

Also applies to: 549-689

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts` around
lines 319 - 417, The MySQL and PostgreSQL parsers duplicate parsing functions
(parseForeignKeyDefinition, parseForeignKeyConfig, parseForeignKeyProp,
isValidForeignKeyDefinition, parseCheckConstraint, parseUniqueConstraint);
extract these into a shared module (e.g., shared/parserConstraints.ts), move the
implementations there, export the functions and any shared types (e.g.,
DrizzleForeignKeyDefinition), then update both
frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts and the
MySQL parser to import and use the shared functions (ensure function signatures
and any helper utilities like extractTableAndColumns, extractColumnNames,
isStringLiteral are either moved too or remain accessible via imports), run type
checks and adjust any names so callers (parseForeignKeyDefinition,
parseForeignKeyConfig, parseForeignKeyProp, isValidForeignKeyDefinition,
parseCheckConstraint, parseUniqueConstraint) keep the same API.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts`:
- Around line 319-417: The MySQL and PostgreSQL parsers duplicate parsing
functions (parseForeignKeyDefinition, parseForeignKeyConfig,
parseForeignKeyProp, isValidForeignKeyDefinition, parseCheckConstraint,
parseUniqueConstraint); extract these into a shared module (e.g.,
shared/parserConstraints.ts), move the implementations there, export the
functions and any shared types (e.g., DrizzleForeignKeyDefinition), then update
both frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts and the
MySQL parser to import and use the shared functions (ensure function signatures
and any helper utilities like extractTableAndColumns, extractColumnNames,
isStringLiteral are either moved too or remain accessible via imports), run type
checks and adjust any names so callers (parseForeignKeyDefinition,
parseForeignKeyConfig, parseForeignKeyProp, isValidForeignKeyDefinition,
parseCheckConstraint, parseUniqueConstraint) keep the same API.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 37135ce8-16e3-4de5-8f11-d8ac521a6934

📥 Commits

Reviewing files that changed from the base of the PR and between 19931ec and c4f4754.

📒 Files selected for processing (5)
  • frontend/packages/schema/src/parser/drizzle/__tests__/unified.test.ts
  • frontend/packages/schema/src/parser/drizzle/mysql/astUtils.ts
  • frontend/packages/schema/src/parser/drizzle/mysql/tableParser.ts
  • frontend/packages/schema/src/parser/drizzle/postgres/astUtils.ts
  • frontend/packages/schema/src/parser/drizzle/postgres/tableParser.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/packages/schema/src/parser/drizzle/postgres/astUtils.ts
  • frontend/packages/schema/src/parser/drizzle/mysql/astUtils.ts

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.

No support for Drizzle ORM multi-column foreign keys Liam does not support drizzle v1 version.

2 participants