Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/fajarnugraha37/drizzle-castor/llms.txt

Use this file to discover all available pages before exploring further.

Drizzle Castor is built to work identically across PostgreSQL, MySQL, and SQLite without any configuration changes. The library detects which database engine you are running at runtime through a duck-typing probe on the Drizzle instance, then automatically selects the correct SQL generation strategy for mutations, JSON operations, and transaction safety. This page explains how that detection works, how each dialect handles atomic mutations, and how JSON paths differ across engines.

Dialect detection

The getDialect() function in src/helper/dialect-helper.ts never reads a config string or environment variable. Instead it probes two behavioral characteristics of the Drizzle dialect object — the parameter placeholder style and the identifier quoting style — to determine which engine is in use:
export function getDialect(db: any): Dialect {
  const dialect = db?.dialect;
  if (!dialect) return "sqlite";

  // 1. Check for PostgreSQL characteristics (uses $1 style parameters)
  if (typeof dialect.escapeParam === 'function') {
    const param = dialect.escapeParam(0, null);
    if (typeof param === 'string' && param.startsWith('$')) {
      return "pg";
    }
  }

  // 2. Check for MySQL characteristics (uses backticks for escaping names)
  if (typeof dialect.escapeName === 'function') {
    const escaped = dialect.escapeName('a');
    if (escaped === '`a`') {
      return "mysql";
    }
  }

  // 3. Fallback to SQLite (uses double quotes for escaping names like PG, but ? for params)
  return "sqlite";
}
The supportsReturning(db) helper is a thin wrapper over getDialect() that returns true for PostgreSQL and SQLite and false for MySQL. All mutation executors call this before choosing a strategy.

Mutation strategies

Different SQL engines expose different capabilities for returning the rows affected by an UPDATE or DELETE. Drizzle Castor bridges this gap with two strategies.

Strategy A: RETURNING clause

PostgreSQL’s RETURNING clause lets an UPDATE or DELETE statement return the affected rows atomically within the same statement. Drizzle Castor uses this to avoid a second SELECT to identify which rows changed.
BEGIN;

-- The mutation happens and immediately returns the ID
UPDATE "users"
SET "status" = 'active'
WHERE "age" > 18
RETURNING "id";

-- If relation hydration is requested, fetch the full tree
SELECT "users"."id", "profiles"."bio"
FROM "users"
LEFT JOIN "profiles" ON "users"."id" = "profiles"."user_id"
WHERE "users"."id" = $1;

COMMIT;
Driver package: pg (node-postgres) or postgres (postgres.js)

JSON path handling

JSON columns require dialect-specific SQL operators for both reading (extraction) and writing (mutation). Drizzle Castor normalizes these differences so you always write the same dot-notation path in your queries.

Extraction — reading nested values

When a projection or filter references a nested JSON path such as "settings.theme", the buildJsonExtractionSql function converts it to the correct dialect syntax:
PostgreSQL uses the #>> operator to extract a nested value as unquoted text. The dot-notation path settings.theme becomes a brace-delimited array literal:
-- Projection: "settings.theme"
"settings"#>>'{theme}'

-- Projection: "metadata.address.city"
"metadata"#>>'{address,city}'

Mutation — updating nested values

When an updateOne or updateMany set payload uses a dot-notation key such as "settings.theme": "light", the parseUpdateSet function generates the correct dialect-specific merge:
PostgreSQL uses jsonb_set() combined with COALESCE to merge the new value into the existing JSON object without overwriting sibling keys:
SET "settings" = jsonb_set(
  COALESCE("settings", '{}'),
  '{theme}',
  '"light"'
)
All JSON paths are validated by validateJsonPath() before they reach the SQL generator. The validator restricts path strings to alphanumerics, dots, and array indices, preventing SQL injection and prototype pollution regardless of dialect.

Driver packages

Each dialect requires a specific database driver alongside Drizzle ORM. Drizzle Castor does not bundle any drivers.

PostgreSQL

Install pg or postgres. Pass the Drizzle instance created with drizzle-orm/node-postgres or drizzle-orm/postgres-js.

MySQL

Install mysql2. Pass the Drizzle instance created with drizzle-orm/mysql2.

SQLite

Install better-sqlite3. Pass the Drizzle instance created with drizzle-orm/better-sqlite3 or drizzle-orm/bun-sqlite.

Safe pagination with CTE split queries

See how the CTE split query strategy handles LIMIT/OFFSET correctly across dialects.

Type system and inference

Explore how TypeScript generics enforce type safety end-to-end regardless of which dialect you use.

Build docs developers (and LLMs) love