Skip to content

[BUG]: CHECK constraints with operator functions generate invalid SQL with parameterized values #4661

@Yuuki-Sakura

Description

@Yuuki-Sakura

Report hasn't been filed before.

  • I have verified that the bug I'm about to report hasn't been filed before.

What version of drizzle-orm are you using?

0.43.1

What version of drizzle-kit are you using?

0.31.1

Other packages

No response

Describe the Bug

What is the undesired behavior?
When using Drizzle operator functions (gt(), gte(), lt(), lte(), etc.) inside check() constraints, the generated SQL migration contains parameterized placeholders ($1, $2, etc.) instead of literal values. This results in invalid CHECK constraints that cannot be executed by the database.

What are the steps to reproduce it?

  1. Create a table schema with CHECK constraints using operator functions:
import { gt, gte } from 'drizzle-orm'
import { check, integer, numeric, pgTable } from 'drizzle-orm/pg-core'

export const ExampleSchema = pgTable('example', {
  id: integer('id').primaryKey(),
  amount: numeric('amount').notNull(),
  count: integer('count').notNull(),
}, t => [
  check('amount_positive', gt(t.amount, '0')),
  check('count_non_negative', gte(t.count, 0)),
])
  1. Run drizzle-kit generate to create migration files
  2. Examine the generated SQL file

What is the desired result?
The generated SQL should contain literal values in CHECK constraints:

CONSTRAINT "amount_positive" CHECK ("example"."amount" > 0)
CONSTRAINT "count_non_negative" CHECK ("example"."count" >= 0)

Actual result:
The generated SQL contains parameterized values:

CONSTRAINT "amount_positive" CHECK ("example"."amount" > $1)
CONSTRAINT "count_non_negative" CHECK ("example"."count" >= $1)

Environment Details

What database engine are you using?
PostgreSQL (tested with PostgreSQL 14+)

Are you using a specific cloud provider?
No, this issue occurs with any PostgreSQL instance (local, cloud, etc.)

Do you think this bug pertains to a specific database driver?
Yes, this appears to be related to the pg driver and how Drizzle handles parameterized queries in DDL statements.

Are you working in a monorepo?
Yes, using pnpm workspaces, but this issue is reproducible in any project structure.

Package Versions:

  • drizzle-orm: 0.43.1
  • drizzle-kit: 0.31.1
  • @types/pg: 8.15.2
  • pg: 8.16.0

TypeScript version:
TypeScript 5.7.3

tsconfig.json (relevant parts):

{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022"],
    "module": "commonjs",
    "moduleResolution": "node",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}

Workaround

The issue can be avoided by using sql template literals instead of operator functions:

import { sql } from 'drizzle-orm'

// Instead of: check('amount_positive', gt(t.amount, '0'))
check('amount_positive', sql`${t.amount} > 0`)

This generates the correct SQL:

CONSTRAINT "amount_positive" CHECK ("example"."amount" > 0)

Additional Context

This issue affects all comparison operators (gt, gte, lt, lte, eq, ne) when used in CHECK constraints. The problem seems to be that Drizzle treats the operator functions as parameterized queries even in DDL contexts where parameterization is not valid.

The issue doesn't occur with:

  • WHERE clauses in SELECT statements
  • Other SQL contexts that support parameterized queries
  • sql template literals in CHECK constraints

This suggests the bug is specifically in how CHECK constraints process operator function expressions during migration generation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions