or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

examples

edge-cases.mdreal-world-scenarios.md
index.md
tile.json

postgresql.mddocs/reference/

PostgreSQL-Specific Features

This document covers all PostgreSQL-specific features in Drizzle ORM, including column types, extensions, schemas, views, sequences, row-level security, and advanced PostgreSQL functionality.

Table of Contents

PostgreSQL Column Types

Numeric Types

integer

import { integer } from 'drizzle-orm/pg-core';

/**
 * Creates a 32-bit integer column.
 *
 * @param name - Column name (optional if using object syntax)
 * @returns Column builder for integer type
 *
 * @remarks
 * - Stores values from -2,147,483,648 to 2,147,483,647
 * - Maps to JavaScript number type
 * - Can be used with identity columns
 *
 * @example
 * ```typescript
 * const users = pgTable('users', {
 *   age: integer('age'),
 *   count: integer('count').notNull().default(0),
 *   id: integer('id').primaryKey(),
 * });
 * ```
 */
function integer(name?: string): PgIntegerBuilder;

bigint

import { bigint } from 'drizzle-orm/pg-core';

/**
 * Creates a 64-bit integer column.
 *
 * @param name - Column name (optional)
 * @returns Column builder for bigint type
 *
 * @remarks
 * - Stores values from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
 * - Maps to JavaScript string by default (to avoid precision loss)
 * - Use mode: 'number' for JavaScript number (not recommended for large values)
 * - Use mode: 'bigint' for JavaScript BigInt
 *
 * @example
 * ```typescript
 * const analytics = pgTable('analytics', {
 *   views: bigint('views', { mode: 'bigint' }),
 *   timestamp: bigint('timestamp', { mode: 'number' }),
 *   largeId: bigint('large_id', { mode: 'string' }), // default
 * });
 * ```
 */
function bigint(
  name?: string,
  config?: { mode?: 'string' | 'number' | 'bigint' }
): PgBigIntBuilder;

smallint

import { smallint } from 'drizzle-orm/pg-core';

/**
 * Creates a 16-bit integer column.
 *
 * @param name - Column name (optional)
 * @returns Column builder for smallint type
 *
 * @remarks
 * - Stores values from -32,768 to 32,767
 * - Maps to JavaScript number type
 * - Useful for saving storage space when range is limited
 *
 * @example
 * ```typescript
 * const products = pgTable('products', {
 *   stock: smallint('stock').notNull(),
 *   priority: smallint('priority').default(0),
 * });
 * ```
 */
function smallint(name?: string): PgSmallIntBuilder;

serial, bigserial, smallserial

import { serial, bigserial, smallserial } from 'drizzle-orm/pg-core';

/**
 * Creates an auto-incrementing integer column.
 *
 * @param name - Column name (optional)
 * @returns Column builder for serial type
 *
 * @remarks
 * - serial: 32-bit auto-incrementing integer (1 to 2,147,483,647)
 * - bigserial: 64-bit auto-incrementing integer (1 to 9,223,372,036,854,775,807)
 * - smallserial: 16-bit auto-incrementing integer (1 to 32,767)
 * - Automatically creates a sequence
 * - Has NOT NULL constraint by default
 * - Prefer identity columns (generatedAlwaysAsIdentity) in new projects
 *
 * @example
 * ```typescript
 * const users = pgTable('users', {
 *   id: serial('id').primaryKey(),
 *   legacyId: bigserial('legacy_id'),
 *   smallId: smallserial('small_id'),
 * });
 * ```
 */
function serial(name?: string): PgSerialBuilder;
function bigserial(name?: string): PgBigSerialBuilder;
function smallserial(name?: string): PgSmallSerialBuilder;

numeric / decimal

import { numeric, decimal } from 'drizzle-orm/pg-core';

/**
 * Creates a numeric column with arbitrary precision.
 *
 * @param name - Column name (optional)
 * @param config - Numeric configuration
 * @returns Column builder for numeric type
 *
 * @remarks
 * - Stores exact numeric values with user-specified precision
 * - Default mode returns string to avoid precision loss
 * - mode: 'number' returns JavaScript number (may lose precision)
 * - mode: 'bigint' returns JavaScript BigInt (for integers only)
 * - precision: total number of digits (max 1000)
 * - scale: number of digits after decimal point
 * - decimal is an alias for numeric
 *
 * @example
 * ```typescript
 * const products = pgTable('products', {
 *   price: numeric('price', { precision: 10, scale: 2 }), // string
 *   weight: numeric('weight', { precision: 8, scale: 3, mode: 'number' }),
 *   exactValue: numeric('exact_value', { precision: 20, scale: 0, mode: 'bigint' }),
 *   flexible: numeric('flexible'), // no precision limit
 * });
 * ```
 */
function numeric<TMode extends 'string' | 'number' | 'bigint'>(
  name?: string,
  config?: {
    precision?: number;
    scale?: number;
    mode?: TMode;
  }
): PgNumericBuilder<TMode>;

const decimal = numeric; // alias

real

import { real } from 'drizzle-orm/pg-core';

/**
 * Creates a single-precision floating point column.
 *
 * @param name - Column name (optional)
 * @returns Column builder for real type
 *
 * @remarks
 * - 4-byte floating point (6 decimal digits precision)
 * - Maps to JavaScript number
 * - Inexact storage, use numeric for exact values
 *
 * @example
 * ```typescript
 * const sensors = pgTable('sensors', {
 *   temperature: real('temperature'),
 *   humidity: real('humidity'),
 * });
 * ```
 */
function real(name?: string): PgRealBuilder;

doublePrecision

import { doublePrecision } from 'drizzle-orm/pg-core';

/**
 * Creates a double-precision floating point column.
 *
 * @param name - Column name (optional)
 * @returns Column builder for double precision type
 *
 * @remarks
 * - 8-byte floating point (15 decimal digits precision)
 * - Maps to JavaScript number
 * - Inexact storage, use numeric for exact values
 * - Default floating point type in PostgreSQL
 *
 * @example
 * ```typescript
 * const coordinates = pgTable('coordinates', {
 *   latitude: doublePrecision('latitude'),
 *   longitude: doublePrecision('longitude'),
 *   altitude: doublePrecision('altitude'),
 * });
 * ```
 */
function doublePrecision(name?: string): PgDoublePrecisionBuilder;

String Types

text

import { text } from 'drizzle-orm/pg-core';

/**
 * Creates a variable-length text column.
 *
 * @param name - Column name (optional)
 * @param config - Text configuration
 * @returns Column builder for text type
 *
 * @remarks
 * - Unlimited length (up to 1GB)
 * - Maps to JavaScript string
 * - Can specify enum values for validation
 *
 * @example
 * ```typescript
 * const posts = pgTable('posts', {
 *   title: text('title').notNull(),
 *   content: text('content'),
 *   status: text('status', { enum: ['draft', 'published', 'archived'] }),
 * });
 * ```
 */
function text<TEnum extends string[]>(
  name?: string,
  config?: { enum?: TEnum }
): PgTextBuilder<TEnum>;

varchar

import { varchar } from 'drizzle-orm/pg-core';

/**
 * Creates a variable-length character column with length limit.
 *
 * @param name - Column name (optional)
 * @param config - Varchar configuration
 * @returns Column builder for varchar type
 *
 * @remarks
 * - Variable length with optional maximum
 * - Maps to JavaScript string
 * - No performance advantage over text in PostgreSQL
 * - length parameter is optional but recommended for compatibility
 *
 * @example
 * ```typescript
 * const users = pgTable('users', {
 *   username: varchar('username', { length: 50 }).notNull(),
 *   email: varchar('email', { length: 255 }),
 *   shortCode: varchar('short_code', { length: 10 }),
 * });
 * ```
 */
function varchar(
  name?: string,
  config?: {
    length?: number;
    enum?: string[];
  }
): PgVarcharBuilder;

char

import { char } from 'drizzle-orm/pg-core';

/**
 * Creates a fixed-length character column.
 *
 * @param name - Column name (optional)
 * @param config - Char configuration
 * @returns Column builder for char type
 *
 * @remarks
 * - Fixed length, blank-padded to length
 * - Maps to JavaScript string (padding stripped on retrieval)
 * - Use for fixed-length codes or identifiers
 * - length defaults to 1 if not specified
 *
 * @example
 * ```typescript
 * const countries = pgTable('countries', {
 *   code: char('code', { length: 2 }).primaryKey(), // ISO country codes
 *   currencyCode: char('currency_code', { length: 3 }),
 *   flag: char('flag'), // single character, length: 1
 * });
 * ```
 */
function char(
  name?: string,
  config?: {
    length?: number;
    enum?: string[];
  }
): PgCharBuilder;

Date/Time Types

date

import { date } from 'drizzle-orm/pg-core';

/**
 * Creates a date column (calendar date without time).
 *
 * @param name - Column name (optional)
 * @param config - Date configuration
 * @returns Column builder for date type
 *
 * @remarks
 * - Stores calendar date (year, month, day) only
 * - Maps to JavaScript Date object by default
 * - mode: 'string' returns date as string (YYYY-MM-DD)
 *
 * @example
 * ```typescript
 * const events = pgTable('events', {
 *   eventDate: date('event_date'),
 *   birthDate: date('birth_date', { mode: 'string' }),
 *   createdAt: date('created_at').defaultNow(),
 * });
 * ```
 */
function date<TMode extends 'date' | 'string'>(
  name?: string,
  config?: { mode?: TMode }
): PgDateBuilder<TMode>;

timestamp

import { timestamp } from 'drizzle-orm/pg-core';

/**
 * Creates a timestamp column (date and time).
 *
 * @param name - Column name (optional)
 * @param config - Timestamp configuration
 * @returns Column builder for timestamp type
 *
 * @remarks
 * - Stores date and time (with optional timezone)
 * - Maps to JavaScript Date object by default
 * - mode: 'string' returns timestamp as ISO string
 * - withTimezone: true stores with timezone info (recommended)
 * - withTimezone: false stores local time without timezone
 * - precision: fractional seconds precision (0-6 digits)
 *
 * @example
 * ```typescript
 * const posts = pgTable('posts', {
 *   createdAt: timestamp('created_at').defaultNow(),
 *   updatedAt: timestamp('updated_at', { mode: 'date', withTimezone: true }),
 *   publishedAt: timestamp('published_at', {
 *     mode: 'string',
 *     withTimezone: true,
 *     precision: 3
 *   }),
 * });
 * ```
 */
function timestamp<TMode extends 'date' | 'string'>(
  name?: string,
  config?: {
    mode?: TMode;
    withTimezone?: boolean;
    precision?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
  }
): PgTimestampBuilder<TMode>;

time

import { time } from 'drizzle-orm/pg-core';

/**
 * Creates a time column (time of day without date).
 *
 * @param name - Column name (optional)
 * @param config - Time configuration
 * @returns Column builder for time type
 *
 * @remarks
 * - Stores time of day only (no date)
 * - Maps to JavaScript string
 * - withTimezone: true includes timezone offset
 * - precision: fractional seconds precision (0-6 digits)
 *
 * @example
 * ```typescript
 * const schedule = pgTable('schedule', {
 *   openTime: time('open_time'),
 *   closeTime: time('close_time'),
 *   meetingTime: time('meeting_time', {
 *     withTimezone: true,
 *     precision: 0
 *   }),
 * });
 * ```
 */
function time(
  name?: string,
  config?: {
    withTimezone?: boolean;
    precision?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
  }
): PgTimeBuilder;

interval

import { interval } from 'drizzle-orm/pg-core';

/**
 * Creates an interval column (time span/duration).
 *
 * @param name - Column name (optional)
 * @param config - Interval configuration
 * @returns Column builder for interval type
 *
 * @remarks
 * - Stores time intervals/durations
 * - Maps to JavaScript string (PostgreSQL interval format)
 * - Supports various interval fields (year, month, day, hour, etc.)
 * - precision: fractional seconds precision (0-6 digits)
 * - fields: restrict interval to specific fields
 *
 * @example
 * ```typescript
 * const tasks = pgTable('tasks', {
 *   duration: interval('duration'),
 *   timeout: interval('timeout', { precision: 3 }),
 *   yearInterval: interval('year_interval', { fields: 'year' }),
 *   dayToSecond: interval('day_to_second', { fields: 'day to second' }),
 * });
 * ```
 */
function interval(
  name?: string,
  config?: {
    precision?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
    fields?:
      | 'year'
      | 'month'
      | 'day'
      | 'hour'
      | 'minute'
      | 'second'
      | 'year to month'
      | 'day to hour'
      | 'day to minute'
      | 'day to second'
      | 'hour to minute'
      | 'hour to second'
      | 'minute to second';
  }
): PgIntervalBuilder;

Boolean Type

boolean

import { boolean } from 'drizzle-orm/pg-core';

/**
 * Creates a boolean column.
 *
 * @param name - Column name (optional)
 * @returns Column builder for boolean type
 *
 * @remarks
 * - Stores true/false values
 * - Maps to JavaScript boolean
 * - PostgreSQL accepts various input formats: TRUE, 't', 'yes', 'y', '1'
 *
 * @example
 * ```typescript
 * const users = pgTable('users', {
 *   isActive: boolean('is_active').default(true),
 *   emailVerified: boolean('email_verified').notNull().default(false),
 *   isPremium: boolean('is_premium'),
 * });
 * ```
 */
function boolean(name?: string): PgBooleanBuilder;

JSON Types

json

import { json } from 'drizzle-orm/pg-core';

/**
 * Creates a JSON column (stored as text).
 *
 * @param name - Column name (optional)
 * @returns Column builder for json type
 *
 * @remarks
 * - Stores JSON data as text
 * - Maps to any TypeScript type (with type parameter)
 * - Validates JSON on input
 * - Not optimized for querying
 * - Consider jsonb for better performance
 *
 * @example
 * ```typescript
 * interface UserSettings {
 *   theme: 'light' | 'dark';
 *   notifications: boolean;
 * }
 *
 * const users = pgTable('users', {
 *   settings: json('settings').$type<UserSettings>(),
 *   metadata: json('metadata').$type<Record<string, unknown>>(),
 * });
 * ```
 */
function json<TData = unknown>(name?: string): PgJsonBuilder<TData>;

jsonb

import { jsonb } from 'drizzle-orm/pg-core';

/**
 * Creates a JSONB column (binary JSON).
 *
 * @param name - Column name (optional)
 * @returns Column builder for jsonb type
 *
 * @remarks
 * - Stores JSON data in binary format
 * - Maps to any TypeScript type (with type parameter)
 * - Supports efficient querying and indexing
 * - Slightly slower on insert (due to conversion)
 * - Faster for querying compared to json
 * - Removes whitespace and duplicate keys
 * - Preferred over json for most use cases
 *
 * @example
 * ```typescript
 * interface Address {
 *   street: string;
 *   city: string;
 *   country: string;
 * }
 *
 * const users = pgTable('users', {
 *   profile: jsonb('profile').$type<{
 *     bio: string;
 *     website?: string;
 *   }>(),
 *   address: jsonb('address').$type<Address>(),
 * });
 * ```
 */
function jsonb<TData = unknown>(name?: string): PgJsonbBuilder<TData>;

Network Types

inet

import { inet } from 'drizzle-orm/pg-core';

/**
 * Creates an inet column (IPv4 or IPv6 host address).
 *
 * @param name - Column name (optional)
 * @returns Column builder for inet type
 *
 * @remarks
 * - Stores IPv4 or IPv6 host addresses with optional netmask
 * - Maps to JavaScript string
 * - Supports CIDR notation (e.g., '192.168.1.1/24')
 * - Can store host addresses within networks
 *
 * @example
 * ```typescript
 * const connections = pgTable('connections', {
 *   ipAddress: inet('ip_address').notNull(),
 *   proxyIp: inet('proxy_ip'),
 * });
 *
 * // Insert example: '192.168.1.1' or '2001:db8::1'
 * ```
 */
function inet(name?: string): PgInetBuilder;

cidr

import { cidr } from 'drizzle-orm/pg-core';

/**
 * Creates a cidr column (IPv4 or IPv6 network).
 *
 * @param name - Column name (optional)
 * @returns Column builder for cidr type
 *
 * @remarks
 * - Stores IPv4 or IPv6 network addresses
 * - Maps to JavaScript string
 * - Requires CIDR notation (e.g., '192.168.1.0/24')
 * - Stores network addresses, not host addresses
 *
 * @example
 * ```typescript
 * const networks = pgTable('networks', {
 *   subnet: cidr('subnet').notNull(),
 *   vpcRange: cidr('vpc_range'),
 * });
 *
 * // Insert example: '192.168.0.0/16' or '2001:db8::/32'
 * ```
 */
function cidr(name?: string): PgCidrBuilder;

macaddr

import { macaddr } from 'drizzle-orm/pg-core';

/**
 * Creates a macaddr column (MAC address, 6 bytes).
 *
 * @param name - Column name (optional)
 * @returns Column builder for macaddr type
 *
 * @remarks
 * - Stores 6-byte MAC addresses
 * - Maps to JavaScript string
 * - Accepts various formats: 'XX:XX:XX:XX:XX:XX', 'XX-XX-XX-XX-XX-XX'
 *
 * @example
 * ```typescript
 * const devices = pgTable('devices', {
 *   macAddress: macaddr('mac_address').notNull().unique(),
 *   routerMac: macaddr('router_mac'),
 * });
 *
 * // Insert example: '08:00:2b:01:02:03'
 * ```
 */
function macaddr(name?: string): PgMacaddrBuilder;

macaddr8

import { macaddr8 } from 'drizzle-orm/pg-core';

/**
 * Creates a macaddr8 column (MAC address, 8 bytes).
 *
 * @param name - Column name (optional)
 * @returns Column builder for macaddr8 type
 *
 * @remarks
 * - Stores 8-byte MAC addresses (EUI-64 format)
 * - Maps to JavaScript string
 * - Accepts various formats including EUI-48 and EUI-64
 *
 * @example
 * ```typescript
 * const devices = pgTable('devices', {
 *   macAddress64: macaddr8('mac_address_64').notNull(),
 * });
 *
 * // Insert example: '08:00:2b:01:02:03:04:05'
 * ```
 */
function macaddr8(name?: string): PgMacaddr8Builder;

Geometric Types

point

import { point } from 'drizzle-orm/pg-core';

/**
 * Creates a point column (geometric point).
 *
 * @param name - Column name (optional)
 * @param config - Point configuration
 * @returns Column builder for point type
 *
 * @remarks
 * - Stores 2D geometric point (x, y)
 * - mode: 'tuple' returns [x, y] array (default)
 * - mode: 'xy' returns { x, y } object
 * - Maps to JavaScript array or object
 *
 * @example
 * ```typescript
 * const locations = pgTable('locations', {
 *   position: point('position'), // [x, y]
 *   center: point('center', { mode: 'xy' }), // { x, y }
 * });
 *
 * // Insert examples:
 * // tuple mode: [10.5, 20.3]
 * // xy mode: { x: 10.5, y: 20.3 }
 * ```
 */
function point<TMode extends 'tuple' | 'xy'>(
  name?: string,
  config?: { mode?: TMode }
): PgPointBuilder<TMode>;

line

import { line } from 'drizzle-orm/pg-core';

/**
 * Creates a line column (infinite line).
 *
 * @param name - Column name (optional)
 * @returns Column builder for line type
 *
 * @remarks
 * - Stores infinite line in 2D space
 * - Represented as { A, B, C } in equation Ax + By + C = 0
 * - Maps to JavaScript object { a: number, b: number, c: number }
 *
 * @example
 * ```typescript
 * const geometry = pgTable('geometry', {
 *   boundary: line('boundary'),
 * });
 *
 * // Insert example: { a: 1, b: 2, c: 3 }
 * ```
 */
function line(name?: string): PgLineBuilder;

UUID Type

uuid

import { uuid } from 'drizzle-orm/pg-core';

/**
 * Creates a UUID column (universally unique identifier).
 *
 * @param name - Column name (optional)
 * @returns Column builder for uuid type
 *
 * @remarks
 * - Stores 128-bit UUID values
 * - Maps to JavaScript string
 * - Format: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
 * - Can use PostgreSQL functions: gen_random_uuid(), uuid_generate_v4()
 *
 * @example
 * ```typescript
 * import { sql } from 'drizzle-orm';
 *
 * const users = pgTable('users', {
 *   id: uuid('id').defaultRandom().primaryKey(),
 *   externalId: uuid('external_id').default(sql`gen_random_uuid()`),
 *   legacyId: uuid('legacy_id'),
 * });
 * ```
 */
function uuid(name?: string): PgUuidBuilder;

Array Types

All PostgreSQL column types can be converted to array types using the .array() method.

/**
 * Converts any PostgreSQL column to an array type.
 *
 * @param size - Optional array size constraint
 * @returns Array column builder
 *
 * @remarks
 * - Can be called on any column type
 * - Supports multi-dimensional arrays (call .array() multiple times)
 * - Maps to JavaScript array
 * - Can specify array size for constraints
 *
 * @example
 * ```typescript
 * const posts = pgTable('posts', {
 *   // One-dimensional arrays
 *   tags: text('tags').array(),
 *   scores: integer('scores').array(),
 *   prices: numeric('prices', { precision: 10, scale: 2 }).array(),
 *
 *   // Fixed-size array
 *   fixedArray: integer('fixed_array').array(5),
 *
 *   // Multi-dimensional arrays
 *   matrix: integer('matrix').array().array(), // integer[][]
 *   cube: real('cube').array().array().array(), // real[][][]
 *
 *   // Array with enum
 *   statuses: text('statuses', {
 *     enum: ['draft', 'published']
 *   }).array(),
 * });
 *
 * // Insert examples:
 * await db.insert(posts).values({
 *   tags: ['typescript', 'postgresql'],
 *   scores: [1, 2, 3, 4, 5],
 *   matrix: [[1, 2], [3, 4]],
 * });
 *
 * // Query with array operators
 * import { arrayContains } from 'drizzle-orm/pg-core';
 *
 * await db.select()
 *   .from(posts)
 *   .where(arrayContains(posts.tags, ['typescript']));
 * ```
 */
method array<TSize extends number>(size?: TSize): PgArrayBuilder;

Array Operators

import {
  arrayContains,
  arrayContained,
  arrayOverlaps
} from 'drizzle-orm/pg-core';

/**
 * PostgreSQL array comparison operators.
 *
 * @remarks
 * - arrayContains: checks if array contains all values (@> operator)
 * - arrayContained: checks if array is contained by values (<@ operator)
 * - arrayOverlaps: checks if arrays have common elements (&&  operator)
 *
 * @example
 * ```typescript
 * // Array contains
 * await db.select()
 *   .from(posts)
 *   .where(arrayContains(posts.tags, ['typescript', 'orm']));
 *
 * // Array is contained
 * await db.select()
 *   .from(posts)
 *   .where(arrayContained(posts.tags, ['typescript', 'orm', 'database']));
 *
 * // Arrays overlap
 * await db.select()
 *   .from(posts)
 *   .where(arrayOverlaps(posts.tags, ['typescript', 'javascript']));
 * ```
 */
function arrayContains(column: PgColumn, values: unknown[]): SQL;
function arrayContained(column: PgColumn, values: unknown[]): SQL;
function arrayOverlaps(column: PgColumn, values: unknown[]): SQL;

Enum Types

PostgreSQL supports custom enum types for type-safe string values.

pgEnum

import { pgEnum } from 'drizzle-orm/pg-core';

/**
 * Creates a PostgreSQL enum type from an array of values.
 *
 * @param name - Enum type name
 * @param values - Array of enum values
 * @returns Enum column factory
 *
 * @remarks
 * - Creates a reusable enum type in PostgreSQL
 * - Provides type safety in TypeScript
 * - Can be used in multiple tables
 * - Values are immutable after creation (use migrations to alter)
 *
 * @example
 * ```typescript
 * // Define enum
 * export const statusEnum = pgEnum('status', [
 *   'pending',
 *   'processing',
 *   'completed',
 *   'failed'
 * ]);
 *
 * export const roleEnum = pgEnum('role', ['user', 'admin', 'moderator']);
 *
 * // Use in tables
 * const users = pgTable('users', {
 *   id: serial('id').primaryKey(),
 *   role: roleEnum('role').notNull().default('user'),
 * });
 *
 * const tasks = pgTable('tasks', {
 *   id: serial('id').primaryKey(),
 *   status: statusEnum('status').default('pending'),
 * });
 *
 * // Type-safe inserts
 * await db.insert(tasks).values({
 *   status: 'completed', // ✓ type-safe
 *   // status: 'invalid', // ✗ type error
 * });
 * ```
 */
function pgEnum<T extends [string, ...string[]]>(
  name: string,
  values: T
): PgEnum<T>;

pgEnumObject

import { pgEnum } from 'drizzle-orm/pg-core';

/**
 * Creates a PostgreSQL enum type from a TypeScript enum object.
 *
 * @param name - Enum type name
 * @param enumObject - TypeScript enum or object with string values
 * @returns Enum column factory
 *
 * @remarks
 * - Integrates with TypeScript enums
 * - Provides bidirectional type safety
 * - Values extracted from object values (not keys)
 *
 * @example
 * ```typescript
 * // TypeScript enum
 * enum Priority {
 *   Low = 'low',
 *   Medium = 'medium',
 *   High = 'high',
 *   Critical = 'critical'
 * }
 *
 * // Create PostgreSQL enum
 * export const priorityEnum = pgEnum('priority', Priority);
 *
 * // Use in table
 * const tasks = pgTable('tasks', {
 *   id: serial('id').primaryKey(),
 *   priority: priorityEnum('priority').notNull().default(Priority.Medium),
 * });
 *
 * // Use with TypeScript enum
 * await db.insert(tasks).values({
 *   priority: Priority.High, // ✓ type-safe
 * });
 *
 * // Alternative: object literal
 * const Status = {
 *   Draft: 'draft',
 *   Published: 'published',
 * } as const;
 *
 * export const statusEnum = pgEnum('status', Status);
 * ```
 */
function pgEnum<E extends Record<string, string>>(
  name: string,
  enumObject: E
): PgEnumObject<E>;

PostgreSQL Extensions

PostGIS Extension

PostGIS adds support for geographic objects to PostgreSQL.

import { geometry } from 'drizzle-orm/pg-core';

/**
 * Creates a PostGIS geometry column.
 *
 * @param name - Column name (optional)
 * @param config - Geometry configuration
 * @returns Column builder for geometry type
 *
 * @remarks
 * - Requires PostGIS extension: CREATE EXTENSION IF NOT EXISTS postgis;
 * - mode: 'tuple' returns [x, y] (default)
 * - mode: 'xy' returns { x, y }
 * - type: geometry type (point, linestring, polygon, etc.)
 * - srid: Spatial Reference System Identifier (e.g., 4326 for WGS84)
 *
 * @example
 * ```typescript
 * const locations = pgTable('locations', {
 *   // Basic point geometry
 *   position: geometry('position', { type: 'point' }),
 *
 *   // With SRID (WGS84)
 *   gpsCoords: geometry('gps_coords', {
 *     type: 'point',
 *     srid: 4326
 *   }),
 *
 *   // With xy mode
 *   center: geometry('center', {
 *     type: 'point',
 *     mode: 'xy'
 *   }),
 *
 *   // Other geometry types
 *   boundary: geometry('boundary', { type: 'polygon' }),
 *   route: geometry('route', { type: 'linestring' }),
 * });
 *
 * // Insert examples:
 * await db.insert(locations).values({
 *   position: [40.7128, -74.0060], // tuple mode
 *   center: { x: 40.7128, y: -74.0060 }, // xy mode
 * });
 * ```
 */
function geometry<TMode extends 'tuple' | 'xy'>(
  name?: string,
  config?: {
    mode?: TMode;
    type?: 'point' | 'linestring' | 'polygon' | (string & {});
    srid?: number;
  }
): PgGeometryBuilder<TMode>;

pgvector Extension

pgvector adds support for vector similarity search in PostgreSQL.

vector

import { vector } from 'drizzle-orm/pg-core';

/**
 * Creates a pgvector vector column for embeddings.
 *
 * @param name - Column name (optional)
 * @param config - Vector configuration
 * @returns Column builder for vector type
 *
 * @remarks
 * - Requires pgvector extension: CREATE EXTENSION IF NOT EXISTS vector;
 * - Stores high-dimensional vectors for similarity search
 * - dimensions: number of dimensions (required, max 16000)
 * - Maps to JavaScript number array
 * - Use for machine learning embeddings (OpenAI, etc.)
 *
 * @example
 * ```typescript
 * const documents = pgTable('documents', {
 *   id: serial('id').primaryKey(),
 *   content: text('content'),
 *   embedding: vector('embedding', { dimensions: 1536 }), // OpenAI ada-002
 * });
 *
 * // Insert with embeddings
 * await db.insert(documents).values({
 *   content: 'Hello world',
 *   embedding: [0.1, 0.2, 0.3, ...], // 1536 dimensions
 * });
 *
 * // Similarity search
 * import { l2Distance } from 'drizzle-orm';
 *
 * const queryEmbedding = [...]; // your query vector
 * await db.select()
 *   .from(documents)
 *   .orderBy(l2Distance(documents.embedding, queryEmbedding))
 *   .limit(5);
 * ```
 */
function vector<D extends number>(
  name?: string,
  config: { dimensions: D }
): PgVectorBuilder<D>;

halfvec

import { halfvec } from 'drizzle-orm/pg-core';

/**
 * Creates a pgvector halfvec column (half-precision vectors).
 *
 * @param name - Column name (optional)
 * @param config - Halfvec configuration
 * @returns Column builder for halfvec type
 *
 * @remarks
 * - Half-precision (16-bit) floating point vectors
 * - Uses 50% less storage than vector type
 * - Slightly less precise than full vector
 * - dimensions: number of dimensions (required)
 *
 * @example
 * ```typescript
 * const images = pgTable('images', {
 *   id: serial('id').primaryKey(),
 *   embedding: halfvec('embedding', { dimensions: 512 }),
 * });
 * ```
 */
function halfvec<D extends number>(
  name?: string,
  config: { dimensions: D }
): PgHalfvecBuilder<D>;

bit

import { bit } from 'drizzle-orm/pg-core';

/**
 * Creates a bit string column for binary vectors.
 *
 * @param name - Column name (optional)
 * @param config - Bit configuration
 * @returns Column builder for bit type
 *
 * @remarks
 * - Stores binary vectors as bit strings
 * - Used with pgvector for binary embeddings
 * - dimensions: number of bits (required)
 * - Maps to JavaScript string of '0' and '1' characters
 *
 * @example
 * ```typescript
 * const fingerprints = pgTable('fingerprints', {
 *   id: serial('id').primaryKey(),
 *   hash: bit('hash', { dimensions: 256 }),
 * });
 *
 * // Insert binary vector
 * await db.insert(fingerprints).values({
 *   hash: '1010101001010101...', // 256 bits
 * });
 * ```
 */
function bit<D extends number>(
  name?: string,
  config: { dimensions: D }
): PgBitBuilder<D>;

sparsevec

import { sparsevec } from 'drizzle-orm/pg-core';

/**
 * Creates a pgvector sparsevec column (sparse vectors).
 *
 * @param name - Column name (optional)
 * @param config - Sparsevec configuration
 * @returns Column builder for sparsevec type
 *
 * @remarks
 * - Stores sparse vectors efficiently
 * - Only non-zero values are stored
 * - dimensions: maximum number of dimensions (required)
 * - Maps to JavaScript string (pgvector format)
 *
 * @example
 * ```typescript
 * const features = pgTable('features', {
 *   id: serial('id').primaryKey(),
 *   sparseEmbedding: sparsevec('sparse_embedding', { dimensions: 10000 }),
 * });
 *
 * // Format: "{index:value,index:value,...}/dimensions"
 * await db.insert(features).values({
 *   sparseEmbedding: '{1:0.5,100:0.8,500:0.3}/10000',
 * });
 * ```
 */
function sparsevec<D extends number>(
  name?: string,
  config: { dimensions: D }
): PgSparsevecBuilder<D>;

Vector Distance Functions

import {
  l2Distance,
  l1Distance,
  innerProduct,
  cosineDistance,
  hammingDistance,
  jaccardDistance,
} from 'drizzle-orm';

/**
 * Vector distance functions for similarity search.
 *
 * @remarks
 * - l2Distance: Euclidean distance (L2 norm)
 * - l1Distance: Manhattan distance (L1 norm)
 * - innerProduct: Inner product (dot product)
 * - cosineDistance: Cosine distance (1 - cosine similarity)
 * - hammingDistance: Hamming distance (for bit vectors)
 * - jaccardDistance: Jaccard distance (for bit vectors)
 *
 * @example
 * ```typescript
 * const documents = pgTable('documents', {
 *   embedding: vector('embedding', { dimensions: 1536 }),
 * });
 *
 * const queryVector = [...]; // your query embedding
 *
 * // L2 distance (most common)
 * await db.select()
 *   .from(documents)
 *   .orderBy(l2Distance(documents.embedding, queryVector))
 *   .limit(10);
 *
 * // Cosine similarity (for normalized vectors)
 * await db.select()
 *   .from(documents)
 *   .orderBy(cosineDistance(documents.embedding, queryVector))
 *   .limit(10);
 *
 * // Inner product (for maximum similarity)
 * await db.select({
 *   similarity: innerProduct(documents.embedding, queryVector)
 * })
 *   .from(documents)
 *   .orderBy(desc(innerProduct(documents.embedding, queryVector)));
 * ```
 */
function l2Distance(column: PgColumn, vector: number[]): SQL;
function l1Distance(column: PgColumn, vector: number[]): SQL;
function innerProduct(column: PgColumn, vector: number[]): SQL;
function cosineDistance(column: PgColumn, vector: number[]): SQL;
function hammingDistance(column: PgColumn, vector: string): SQL;
function jaccardDistance(column: PgColumn, vector: string): SQL;

Vector Index Methods

/**
 * Creates an index with vector-specific methods.
 *
 * @remarks
 * - hnsw: Hierarchical Navigable Small World (approximate search)
 * - ivfflat: Inverted File Flat (approximate search)
 * - btree: B-tree index (exact search, slower)
 *
 * Use operator classes to specify distance function:
 * - vector_l2_ops: L2 distance
 * - vector_ip_ops: Inner product
 * - vector_cosine_ops: Cosine distance
 * - vector_l1_ops: L1 distance
 * - bit_hamming_ops: Hamming distance (for bit)
 * - bit_jaccard_ops: Jaccard distance (for bit)
 *
 * @example
 * ```typescript
 * import { index } from 'drizzle-orm/pg-core';
 *
 * const documents = pgTable('documents', {
 *   id: serial('id').primaryKey(),
 *   embedding: vector('embedding', { dimensions: 1536 }),
 * }, (table) => ({
 *   // HNSW index (recommended for large datasets)
 *   embeddingIndex: index('embedding_idx')
 *     .using('hnsw', table.embedding.op('vector_l2_ops'))
 *     .with({ m: 16, ef_construction: 64 }),
 *
 *   // IVFFlat index (faster build, slower search)
 *   embeddingIvfIndex: index('embedding_ivf_idx')
 *     .using('ivfflat', table.embedding.op('vector_cosine_ops'))
 *     .with({ lists: 100 }),
 * }));
 *
 * // HNSW parameters:
 * // - m: max connections per layer (higher = better recall, more memory)
 * // - ef_construction: build-time search depth (higher = better index)
 *
 * // IVFFlat parameters:
 * // - lists: number of clusters (sqrt(rows) is a good default)
 * ```
 */

PostgreSQL Schemas

Schemas provide namespacing for database objects.

import { pgSchema } from 'drizzle-orm/pg-core';

/**
 * Creates a PostgreSQL schema namespace.
 *
 * @param name - Schema name
 * @returns Schema object with table/view/enum/sequence builders
 *
 * @remarks
 * - Organizes database objects into namespaces
 * - Cannot use 'public' (that's the default schema)
 * - Provides schema-scoped table, view, enum, and sequence builders
 *
 * @example
 * ```typescript
 * // Create schema
 * export const authSchema = pgSchema('auth');
 *
 * // Define tables in schema
 * export const users = authSchema.table('users', {
 *   id: serial('id').primaryKey(),
 *   email: text('email').notNull(),
 * });
 *
 * export const sessions = authSchema.table('sessions', {
 *   id: uuid('id').primaryKey(),
 *   userId: integer('user_id').references(() => users.id),
 * });
 *
 * // Define enum in schema
 * export const roleEnum = authSchema.enum('role', ['user', 'admin']);
 *
 * // Define view in schema
 * export const activeUsers = authSchema.view('active_users').as((qb) =>
 *   qb.select().from(users).where(eq(users.isActive, true))
 * );
 *
 * // Define sequence in schema
 * export const userIdSeq = authSchema.sequence('user_id_seq');
 *
 * // Multiple schemas
 * export const analyticsSchema = pgSchema('analytics');
 * export const events = analyticsSchema.table('events', {
 *   id: serial('id').primaryKey(),
 *   type: text('type').notNull(),
 * });
 * ```
 */
function pgSchema<TName extends string>(name: TName): PgSchema<TName>;

interface PgSchema<TName extends string> {
  readonly schemaName: TName;

  // Define table in schema
  table<TTableName extends string, TColumns extends Record<string, PgColumnBuilderBase>>(
    name: TTableName,
    columns: TColumns,
    extraConfig?: (self: BuildExtraConfigColumns<TTableName, TColumns>) => PgTableExtraConfig
  ): PgTableWithColumns<{ name: TTableName; schema: TName; columns: BuildColumns<TTableName, TColumns> }>;

  // Define view in schema
  view<TName extends string>(name: TName): ViewBuilder<TName>;
  view<TName extends string, TColumns extends Record<string, PgColumnBuilderBase>>(
    name: TName,
    columns: TColumns
  ): ManualViewBuilder<TName, TColumns>;

  // Define materialized view in schema
  materializedView<TName extends string>(name: TName): MaterializedViewBuilder<TName>;
  materializedView<TName extends string, TColumns extends Record<string, PgColumnBuilderBase>>(
    name: TName,
    columns: TColumns
  ): ManualMaterializedViewBuilder<TName, TColumns>;

  // Define enum in schema
  enum<T extends [string, ...string[]]>(
    name: string,
    values: T
  ): PgEnum<T>;
  enum<E extends Record<string, string>>(
    name: string,
    enumObject: E
  ): PgEnumObject<E>;

  // Define sequence in schema
  sequence(
    name: string,
    options?: PgSequenceOptions
  ): PgSequence;
}

Views and Materialized Views

Regular Views

import { pgView } from 'drizzle-orm/pg-core';

/**
 * Creates a PostgreSQL view definition.
 *
 * @remarks
 * - Views are virtual tables based on queries
 * - Can have configuration options (checkOption, securityBarrier, securityInvoker)
 * - Can be used like tables in SELECT queries
 *
 * @example
 * ```typescript
 * import { sql } from 'drizzle-orm';
 *
 * const users = pgTable('users', {
 *   id: serial('id').primaryKey(),
 *   name: text('name').notNull(),
 *   isActive: boolean('is_active').default(true),
 * });
 *
 * // Inferred columns from query
 * export const activeUsers = pgView('active_users').as((qb) =>
 *   qb.select({
 *     id: users.id,
 *     name: users.name,
 *   })
 *   .from(users)
 *   .where(eq(users.isActive, true))
 * );
 *
 * // With view options
 * export const secureView = pgView('secure_view')
 *   .with({
 *     checkOption: 'cascaded',
 *     securityBarrier: true,
 *     securityInvoker: true,
 *   })
 *   .as((qb) => qb.select().from(users));
 *
 * // Manual column definition
 * export const userView = pgView('user_view', {
 *   id: integer('id'),
 *   name: text('name'),
 * }).as(sql`SELECT id, name FROM users`);
 *
 * // Existing view (not managed by Drizzle)
 * export const legacyView = pgView('legacy_view', {
 *   id: integer('id'),
 *   data: text('data'),
 * }).existing();
 *
 * // Query from view
 * await db.select().from(activeUsers);
 * ```
 */
function pgView<TName extends string>(name: TName): ViewBuilder<TName>;
function pgView<TName extends string, TColumns extends Record<string, PgColumnBuilderBase>>(
  name: TName,
  columns: TColumns
): ManualViewBuilder<TName, TColumns>;

interface ViewBuilder<TName extends string> {
  // Add view options
  with(config: {
    checkOption?: 'local' | 'cascaded';
    securityBarrier?: boolean;
    securityInvoker?: boolean;
  }): this;

  // Define view query
  as<TSelectedFields extends ColumnsSelection>(
    query: TypedQueryBuilder<TSelectedFields> | ((qb: QueryBuilder) => TypedQueryBuilder<TSelectedFields>)
  ): PgView<TName, false, TSelectedFields>;
}

interface ManualViewBuilder<TName extends string, TColumns> {
  with(config: ViewWithConfig): this;
  as(query: SQL): PgView<TName, false, BuildColumns<TName, TColumns>>;
  existing(): PgView<TName, true, BuildColumns<TName, TColumns>>;
}

Materialized Views

import { pgMaterializedView } from 'drizzle-orm/pg-core';

/**
 * Creates a PostgreSQL materialized view definition.
 *
 * @remarks
 * - Materialized views cache query results physically
 * - Must be refreshed to update data
 * - Can have indexes for faster queries
 * - Support many storage parameters
 *
 * @example
 * ```typescript
 * const orders = pgTable('orders', {
 *   id: serial('id').primaryKey(),
 *   total: numeric('total', { precision: 10, scale: 2 }),
 *   createdAt: timestamp('created_at').defaultNow(),
 * });
 *
 * // Basic materialized view
 * export const orderStats = pgMaterializedView('order_stats').as((qb) =>
 *   qb.select({
 *     totalOrders: count(),
 *     totalRevenue: sum(orders.total),
 *   })
 *   .from(orders)
 * );
 *
 * // With storage parameters
 * export const userStats = pgMaterializedView('user_stats')
 *   .with({
 *     fillfactor: 70,
 *     autovacuumEnabled: true,
 *     autovacuumVacuumThreshold: 50,
 *   })
 *   .tablespace('fast_storage')
 *   .using('heap')
 *   .as((qb) => qb.select().from(users));
 *
 * // Create without data initially
 * export const emptyView = pgMaterializedView('empty_view')
 *   .withNoData()
 *   .as((qb) => qb.select().from(orders));
 *
 * // Refresh materialized view
 * await db.refreshMaterializedView(orderStats);
 *
 * // Refresh concurrently (requires unique index)
 * await db.refreshMaterializedView(orderStats).concurrently();
 *
 * // Refresh with no data
 * await db.refreshMaterializedView(orderStats).withNoData();
 * ```
 */
function pgMaterializedView<TName extends string>(
  name: TName
): MaterializedViewBuilder<TName>;
function pgMaterializedView<TName extends string, TColumns extends Record<string, PgColumnBuilderBase>>(
  name: TName,
  columns: TColumns
): ManualMaterializedViewBuilder<TName, TColumns>;

interface MaterializedViewBuilder<TName extends string> {
  // Storage parameters
  with(config: {
    fillfactor?: number;
    toastTupleTarget?: number;
    parallelWorkers?: number;
    autovacuumEnabled?: boolean;
    vacuumIndexCleanup?: 'auto' | 'off' | 'on';
    vacuumTruncate?: boolean;
    autovacuumVacuumThreshold?: number;
    autovacuumVacuumScaleFactor?: number;
    autovacuumVacuumCostDelay?: number;
    autovacuumVacuumCostLimit?: number;
    autovacuumFreezeMinAge?: number;
    autovacuumFreezeMaxAge?: number;
    autovacuumFreezeTableAge?: number;
    autovacuumMultixactFreezeMinAge?: number;
    autovacuumMultixactFreezeMaxAge?: number;
    autovacuumMultixactFreezeTableAge?: number;
    logAutovacuumMinDuration?: number;
    userCatalogTable?: boolean;
  }): this;

  // Access method (default: heap)
  using(method: string): this;

  // Tablespace
  tablespace(name: string): this;

  // Create without data
  withNoData(): this;

  // Define view query
  as<TSelectedFields extends ColumnsSelection>(
    query: TypedQueryBuilder<TSelectedFields> | ((qb: QueryBuilder) => TypedQueryBuilder<TSelectedFields>)
  ): PgMaterializedView<TName, false, TSelectedFields>;
}

// Refresh operations
interface PgRefreshMaterializedView {
  concurrently(): this;
  withNoData(): this;
  execute(): Promise<void>;
}

Sequences

import { pgSequence } from 'drizzle-orm/pg-core';

/**
 * Creates a PostgreSQL sequence.
 *
 * @param name - Sequence name
 * @param options - Sequence configuration
 * @returns Sequence object
 *
 * @remarks
 * - Sequences generate unique numeric identifiers
 * - Used automatically by serial columns
 * - Can be used explicitly with nextval()
 *
 * @example
 * ```typescript
 * import { sql } from 'drizzle-orm';
 *
 * // Define sequence
 * export const orderIdSeq = pgSequence('order_id_seq', {
 *   startWith: 1000,
 *   increment: 1,
 *   minValue: 1000,
 *   maxValue: 999999,
 *   cache: 20,
 *   cycle: false,
 * });
 *
 * // Use in table
 * const orders = pgTable('orders', {
 *   id: integer('id')
 *     .default(sql`nextval('order_id_seq')`)
 *     .primaryKey(),
 *   orderNumber: text('order_number'),
 * });
 *
 * // Use in schema
 * const mySchema = pgSchema('myschema');
 * export const schemaSeq = mySchema.sequence('my_seq', {
 *   startWith: 1,
 *   increment: 1,
 * });
 *
 * // Get next value
 * const result = await db.execute(
 *   sql`SELECT nextval('order_id_seq')`
 * );
 * ```
 */
function pgSequence(
  name: string,
  options?: {
    /**
     * Starting value of the sequence
     * @default 1
     */
    startWith?: number | string;

    /**
     * Increment value (can be negative)
     * @default 1
     */
    increment?: number | string;

    /**
     * Minimum value of the sequence
     */
    minValue?: number | string;

    /**
     * Maximum value of the sequence
     */
    maxValue?: number | string;

    /**
     * Number of sequence values to cache
     * @default 1
     */
    cache?: number | string;

    /**
     * Whether to cycle when reaching min/max
     * @default false
     */
    cycle?: boolean;
  }
): PgSequence;

Row-Level Security

import { pgPolicy, pgRole } from 'drizzle-orm/pg-core';

/**
 * Creates a row-level security policy.
 *
 * @param name - Policy name
 * @param config - Policy configuration
 * @returns Policy object
 *
 * @remarks
 * - Controls which rows users can access
 * - Must enable RLS on table with .enableRLS()
 * - Supports different commands (select, insert, update, delete, all)
 * - Can be permissive (default) or restrictive
 *
 * @example
 * ```typescript
 * import { sql } from 'drizzle-orm';
 *
 * // Define roles
 * export const authenticatedRole = pgRole('authenticated');
 * export const adminRole = pgRole('admin');
 *
 * // Define table with RLS enabled
 * export const posts = pgTable('posts', {
 *   id: serial('id').primaryKey(),
 *   userId: integer('user_id').notNull(),
 *   title: text('title').notNull(),
 *   published: boolean('published').default(false),
 * }, (table) => ({
 *   // Enable RLS
 *   enableRLS: table.enableRLS(),
 *
 *   // Users can only see their own posts
 *   userPolicy: pgPolicy('user_select_own', {
 *     as: 'permissive',
 *     for: 'select',
 *     to: authenticatedRole,
 *     using: sql`user_id = current_user_id()`,
 *   }),
 *
 *   // Users can only insert their own posts
 *   insertPolicy: pgPolicy('user_insert_own', {
 *     for: 'insert',
 *     to: authenticatedRole,
 *     withCheck: sql`user_id = current_user_id()`,
 *   }),
 *
 *   // Users can only update their own unpublished posts
 *   updatePolicy: pgPolicy('user_update_own', {
 *     for: 'update',
 *     to: authenticatedRole,
 *     using: sql`user_id = current_user_id() AND NOT published`,
 *     withCheck: sql`user_id = current_user_id()`,
 *   }),
 *
 *   // Admins can do anything
 *   adminPolicy: pgPolicy('admin_all', {
 *     as: 'permissive',
 *     for: 'all',
 *     to: adminRole,
 *     using: sql`true`,
 *   }),
 *
 *   // Restrictive policy: prevent deleting published posts
 *   restrictDeletePolicy: pgPolicy('restrict_published_delete', {
 *     as: 'restrictive',
 *     for: 'delete',
 *     to: 'public',
 *     using: sql`NOT published`,
 *   }),
 * }));
 *
 * // Enable RLS on table (alternative syntax)
 * export const comments = pgTable('comments', {
 *   id: serial('id').primaryKey(),
 *   postId: integer('post_id').notNull(),
 *   content: text('content').notNull(),
 * }).enableRLS();
 * ```
 */
function pgPolicy(
  name: string,
  config?: {
    /**
     * Policy type: permissive (OR) or restrictive (AND)
     * @default 'permissive'
     */
    as?: 'permissive' | 'restrictive';

    /**
     * SQL command(s) the policy applies to
     * @default 'all'
     */
    for?: 'all' | 'select' | 'insert' | 'update' | 'delete';

    /**
     * Role(s) the policy applies to
     * Can be: 'public', 'current_role', 'current_user', 'session_user', role name, or PgRole
     * @default 'public'
     */
    to?: 'public' | 'current_role' | 'current_user' | 'session_user' | string | PgRole | (string | PgRole)[];

    /**
     * USING expression - determines which rows are visible
     */
    using?: SQL;

    /**
     * WITH CHECK expression - determines which rows can be added/modified
     */
    withCheck?: SQL;
  }
): PgPolicy;

Database Roles

import { pgRole } from 'drizzle-orm/pg-core';

/**
 * Creates a database role reference.
 *
 * @param name - Role name
 * @param config - Role configuration
 * @returns Role object
 *
 * @remarks
 * - References database roles for use in policies
 * - Can mark as existing to reference roles not managed by Drizzle
 * - Configuration options control role privileges (for creation only)
 *
 * @example
 * ```typescript
 * // Define roles
 * export const appUser = pgRole('app_user');
 * export const appAdmin = pgRole('app_admin', {
 *   createDb: true,
 *   createRole: true,
 *   inherit: true,
 * });
 *
 * // Reference existing role
 * export const postgresRole = pgRole('postgres').existing();
 *
 * // Use in policies
 * const posts = pgTable('posts', {
 *   id: serial('id').primaryKey(),
 *   content: text('content'),
 * }, (table) => ({
 *   userPolicy: pgPolicy('user_policy', {
 *     to: appUser,
 *     using: sql`true`,
 *   }),
 *
 *   adminPolicy: pgPolicy('admin_policy', {
 *     to: [appAdmin, postgresRole],
 *     using: sql`true`,
 *   }),
 * }));
 * ```
 */
function pgRole(
  name: string,
  config?: {
    /**
     * Can create databases
     */
    createDb?: boolean;

    /**
     * Can create roles
     */
    createRole?: boolean;

    /**
     * Inherits privileges of roles it is a member of
     */
    inherit?: boolean;
  }
): PgRole;

interface PgRole {
  readonly name: string;

  /**
   * Marks role as existing (not managed by Drizzle)
   */
  existing(): this;
}

Index Methods

PostgreSQL supports multiple index methods for different use cases.

import { index, uniqueIndex } from 'drizzle-orm/pg-core';

/**
 * Creates an index with specific method and operator class.
 *
 * @remarks
 * Available index methods:
 * - btree: B-tree index (default) - sorted data, supports <, <=, =, >=, >
 * - hash: Hash index - only supports = operator
 * - gist: GiST index - generalized search tree, for geometric and full-text
 * - gin: GIN index - inverted index, for arrays, jsonb, full-text
 * - brin: BRIN index - block range index, for very large tables
 * - spgist: SP-GiST index - space-partitioned GiST
 * - hnsw: HNSW index (pgvector) - for vector similarity
 * - ivfflat: IVFFlat index (pgvector) - for vector similarity
 *
 * @example
 * ```typescript
 * const products = pgTable('products', {
 *   id: serial('id').primaryKey(),
 *   name: text('name'),
 *   tags: text('tags').array(),
 *   metadata: jsonb('metadata'),
 *   location: geometry('location', { type: 'point' }),
 *   embedding: vector('embedding', { dimensions: 1536 }),
 *   createdAt: timestamp('created_at').defaultNow(),
 * }, (table) => ({
 *   // B-tree index (default) - for sorting and range queries
 *   nameIndex: index('name_idx').on(table.name),
 *
 *   // Composite B-tree index
 *   nameCreatedIndex: index('name_created_idx')
 *     .on(table.name, table.createdAt),
 *
 *   // B-tree with order and nulls
 *   createdDescIndex: index('created_desc_idx')
 *     .on(table.createdAt.desc().nullsLast()),
 *
 *   // Hash index - only for equality
 *   idHashIndex: index('id_hash_idx')
 *     .using('hash', table.id),
 *
 *   // GIN index for arrays
 *   tagsIndex: index('tags_idx')
 *     .using('gin', table.tags),
 *
 *   // GIN index for JSONB with operator class
 *   metadataIndex: index('metadata_idx')
 *     .using('gin', table.metadata.op('jsonb_path_ops')),
 *
 *   // GiST index for geometric data
 *   locationIndex: index('location_idx')
 *     .using('gist', table.location),
 *
 *   // BRIN index for large sequential data
 *   createdBrinIndex: index('created_brin_idx')
 *     .using('brin', table.createdAt),
 *
 *   // HNSW vector index
 *   embeddingHnswIndex: index('embedding_hnsw_idx')
 *     .using('hnsw', table.embedding.op('vector_l2_ops'))
 *     .with({ m: 16, ef_construction: 64 }),
 *
 *   // IVFFlat vector index
 *   embeddingIvfIndex: index('embedding_ivf_idx')
 *     .using('ivfflat', table.embedding.op('vector_cosine_ops'))
 *     .with({ lists: 100 }),
 *
 *   // Partial index
 *   activeIndex: index('active_idx')
 *     .on(table.name)
 *     .where(sql`active = true`),
 *
 *   // Concurrent index creation
 *   concurrentIndex: index('concurrent_idx')
 *     .on(table.name)
 *     .concurrently(),
 *
 *   // Unique index
 *   uniqueNameIndex: uniqueIndex('unique_name_idx')
 *     .on(table.name),
 * }));
 * ```
 */
interface IndexBuilderOn {
  // Create regular index on columns
  on(...columns: [PgColumn | SQL, ...(PgColumn | SQL)[]]): IndexBuilder;

  // Create index on table only (not partitions)
  onOnly(...columns: [PgColumn | SQL, ...(PgColumn | SQL)[]]): IndexBuilder;

  // Create index with specific method
  using(
    method: 'btree' | 'hash' | 'gist' | 'spgist' | 'gin' | 'brin' | 'hnsw' | 'ivfflat' | (string & {}),
    ...columns: [PgColumn | SQL, ...(PgColumn | SQL)[]]
  ): IndexBuilder;
}

interface IndexBuilder {
  // Create index concurrently (without locking table)
  concurrently(): this;

  // Add storage parameters
  with(params: Record<string, any>): this;

  // Add partial index condition
  where(condition: SQL): this;
}

// Column methods for index customization
interface PgColumn {
  /**
   * Set sort order for index
   */
  asc(): this;
  desc(): this;

  /**
   * Set null sorting for index
   */
  nullsFirst(): this;
  nullsLast(): this;

  /**
   * Set operator class for index
   *
   * Common operator classes:
   * - Numeric: int2_ops, int4_ops, int8_ops, numeric_ops
   * - Text: text_ops, varchar_ops, bpchar_ops
   * - Date/Time: date_ops, timestamp_ops, timestamptz_ops
   * - JSONB: jsonb_ops, jsonb_path_ops
   * - Arrays: array_ops
   * - pgvector: vector_l2_ops, vector_ip_ops, vector_cosine_ops, vector_l1_ops
   * - bit: bit_hamming_ops, bit_jaccard_ops
   */
  op(opClass: string): this;
}

PostgreSQL Query Features

DISTINCT ON

/**
 * SELECT DISTINCT ON - returns first row for each unique value.
 *
 * @remarks
 * - PostgreSQL-specific feature
 * - Returns first row from each group after sorting
 * - Columns in DISTINCT ON must match leftmost ORDER BY columns
 *
 * @example
 * ```typescript
 * const posts = pgTable('posts', {
 *   id: serial('id').primaryKey(),
 *   userId: integer('user_id').notNull(),
 *   title: text('title').notNull(),
 *   createdAt: timestamp('created_at').defaultNow(),
 * });
 *
 * // Get latest post for each user
 * const latestPosts = await db
 *   .selectDistinctOn([posts.userId])
 *   .from(posts)
 *   .orderBy(posts.userId, desc(posts.createdAt));
 *
 * // With additional selections
 * const latestPostsWithDetails = await db
 *   .selectDistinctOn([posts.userId], {
 *     userId: posts.userId,
 *     latestTitle: posts.title,
 *     createdAt: posts.createdAt,
 *   })
 *   .from(posts)
 *   .orderBy(posts.userId, desc(posts.createdAt));
 *
 * // Multiple distinct columns
 * const results = await db
 *   .selectDistinctOn([posts.userId, posts.title])
 *   .from(posts)
 *   .orderBy(posts.userId, posts.title, desc(posts.createdAt));
 * ```
 */
interface PgDatabase {
  selectDistinctOn<TSelection>(
    on: (PgColumn | SQL)[],
    fields?: TSelection
  ): PgSelectBuilder<TSelection>;
}

RETURNING Clause

/**
 * RETURNING clause - returns affected rows from INSERT/UPDATE/DELETE.
 *
 * @remarks
 * - PostgreSQL-specific feature (also available in SQLite)
 * - Returns data from modified rows
 * - Can return specific columns or all columns
 * - Useful for getting generated IDs, timestamps, etc.
 *
 * @example
 * ```typescript
 * const users = pgTable('users', {
 *   id: serial('id').primaryKey(),
 *   name: text('name').notNull(),
 *   email: text('email').notNull(),
 *   createdAt: timestamp('created_at').defaultNow(),
 * });
 *
 * // INSERT with RETURNING
 * const [newUser] = await db.insert(users)
 *   .values({ name: 'John', email: 'john@example.com' })
 *   .returning();
 * // Returns: { id: 1, name: 'John', email: 'john@example.com', createdAt: Date }
 *
 * // Return specific columns
 * const [result] = await db.insert(users)
 *   .values({ name: 'Jane', email: 'jane@example.com' })
 *   .returning({ id: users.id, name: users.name });
 * // Returns: { id: 2, name: 'Jane' }
 *
 * // UPDATE with RETURNING
 * const updated = await db.update(users)
 *   .set({ name: 'John Doe' })
 *   .where(eq(users.id, 1))
 *   .returning();
 *
 * // DELETE with RETURNING
 * const deleted = await db.delete(users)
 *   .where(eq(users.id, 1))
 *   .returning({ id: users.id, name: users.name });
 *
 * // Multiple rows
 * const insertedUsers = await db.insert(users)
 *   .values([
 *     { name: 'User1', email: 'user1@example.com' },
 *     { name: 'User2', email: 'user2@example.com' },
 *   ])
 *   .returning({ id: users.id });
 * // Returns: [{ id: 3 }, { id: 4 }]
 * ```
 */
interface PgInsertBuilder {
  returning(): Promise<TTable[]>;
  returning<TSelection>(fields: TSelection): Promise<TSelection[]>;
}

interface PgUpdateBuilder {
  returning(): Promise<TTable[]>;
  returning<TSelection>(fields: TSelection): Promise<TSelection[]>;
}

interface PgDeleteBuilder {
  returning(): Promise<TTable[]>;
  returning<TSelection>(fields: TSelection): Promise<TSelection[]>;
}

Row Locking

/**
 * Row locking (FOR UPDATE/SHARE) - locks rows for concurrent access control.
 *
 * @remarks
 * - PostgreSQL-specific feature (also available in MySQL)
 * - Prevents concurrent modifications during transactions
 * - Different lock strengths for different use cases
 *
 * Lock strengths:
 * - 'update': FOR UPDATE - exclusive lock, blocks all other locks
 * - 'no key update': FOR NO KEY UPDATE - allows concurrent key updates
 * - 'share': FOR SHARE - shared lock, allows other shares
 * - 'key share': FOR KEY SHARE - weakest lock, allows most concurrency
 *
 * @example
 * ```typescript
 * const accounts = pgTable('accounts', {
 *   id: serial('id').primaryKey(),
 *   balance: numeric('balance', { precision: 10, scale: 2 }),
 * });
 *
 * // FOR UPDATE - exclusive lock
 * await db.transaction(async (tx) => {
 *   const [account] = await tx
 *     .select()
 *     .from(accounts)
 *     .where(eq(accounts.id, 1))
 *     .for('update');
 *
 *   // Update balance safely
 *   await tx.update(accounts)
 *     .set({ balance: String(Number(account.balance) + 100) })
 *     .where(eq(accounts.id, 1));
 * });
 *
 * // FOR UPDATE with NOWAIT - fail immediately if locked
 * const result = await db
 *   .select()
 *   .from(accounts)
 *   .where(eq(accounts.id, 1))
 *   .for('update', { noWait: true });
 *
 * // FOR UPDATE with SKIP LOCKED - skip locked rows
 * const availableAccounts = await db
 *   .select()
 *   .from(accounts)
 *   .where(eq(accounts.balance, '0'))
 *   .for('update', { skipLocked: true })
 *   .limit(10);
 *
 * // FOR SHARE - allows concurrent reads
 * const account = await db
 *   .select()
 *   .from(accounts)
 *   .where(eq(accounts.id, 1))
 *   .for('share');
 *
 * // FOR NO KEY UPDATE - allows key updates
 * const result = await db
 *   .select()
 *   .from(accounts)
 *   .for('no key update');
 *
 * // FOR KEY SHARE - weakest lock
 * const result = await db
 *   .select()
 *   .from(accounts)
 *   .for('key share');
 *
 * // Lock specific tables in join
 * const result = await db
 *   .select()
 *   .from(accounts)
 *   .leftJoin(transactions, eq(accounts.id, transactions.accountId))
 *   .for('update', { of: accounts });
 * ```
 */
interface PgSelect {
  for(
    strength: 'update' | 'no key update' | 'share' | 'key share',
    config?: {
      /**
       * Fail immediately if row is locked (don't wait)
       */
      noWait?: boolean;

      /**
       * Skip locked rows instead of waiting
       */
      skipLocked?: boolean;

      /**
       * Specify which tables to lock in a join
       */
      of?: PgTable;
    }
  ): this;
}

Identity Columns

Identity columns provide SQL standard auto-increment functionality.

/**
 * Identity columns - SQL standard auto-increment.
 *
 * @remarks
 * - Modern alternative to serial columns
 * - GENERATED ALWAYS: value always generated (cannot be overridden)
 * - GENERATED BY DEFAULT: value generated only if not provided
 * - Supports all sequence options
 * - Preferred over serial in new projects
 *
 * @example
 * ```typescript
 * const users = pgTable('users', {
 *   // GENERATED ALWAYS AS IDENTITY
 *   id: integer('id').generatedAlwaysAsIdentity().primaryKey(),
 *
 *   // GENERATED BY DEFAULT AS IDENTITY
 *   legacyId: integer('legacy_id').generatedByDefaultAsIdentity(),
 *
 *   // With sequence options
 *   customId: integer('custom_id').generatedAlwaysAsIdentity({
 *     startWith: 1000,
 *     increment: 1,
 *     minValue: 1000,
 *     maxValue: 999999,
 *     cache: 20,
 *     cycle: false,
 *   }),
 *
 *   // With named sequence
 *   seqId: integer('seq_id').generatedAlwaysAsIdentity({
 *     name: 'user_seq_id',
 *     startWith: 1,
 *     increment: 1,
 *   }),
 *
 *   // Bigint identity
 *   bigId: bigint('big_id', { mode: 'number' })
 *     .generatedAlwaysAsIdentity(),
 *
 *   name: text('name').notNull(),
 * });
 *
 * // GENERATED ALWAYS - cannot override
 * await db.insert(users).values({
 *   name: 'John',
 *   // id: 123, // ✗ Error: cannot override GENERATED ALWAYS
 * });
 *
 * // GENERATED BY DEFAULT - can override
 * await db.insert(users).values({
 *   name: 'Jane',
 *   legacyId: 999, // ✓ Can provide value
 * });
 * ```
 */
interface PgIntColumnBuilder {
  /**
   * GENERATED ALWAYS AS IDENTITY
   * Value is always generated, cannot be overridden
   */
  generatedAlwaysAsIdentity(
    sequence?: PgSequenceOptions & { name?: string }
  ): this;

  /**
   * GENERATED BY DEFAULT AS IDENTITY
   * Value is generated if not provided
   */
  generatedByDefaultAsIdentity(
    sequence?: PgSequenceOptions & { name?: string }
  ): this;
}

Generated Columns

Generated columns compute values from other columns.

/**
 * Generated columns - computed from other columns.
 *
 * @remarks
 * - GENERATED ALWAYS AS (expression) STORED
 * - Computed from other columns in the same row
 * - Automatically updated when dependencies change
 * - Stored physically (STORED is the only option in PostgreSQL)
 * - Can be indexed
 * - Cannot be directly inserted or updated
 *
 * @example
 * ```typescript
 * import { sql } from 'drizzle-orm';
 *
 * const users = pgTable('users', {
 *   id: serial('id').primaryKey(),
 *   firstName: text('first_name').notNull(),
 *   lastName: text('last_name').notNull(),
 *
 *   // Generated from SQL expression
 *   fullName: text('full_name')
 *     .generatedAlwaysAs(
 *       sql`first_name || ' ' || last_name`
 *     ),
 *
 *   email: text('email').notNull(),
 *
 *   // Generated from callback (for type safety)
 *   emailDomain: text('email_domain')
 *     .generatedAlwaysAs((): SQL =>
 *       sql`split_part(${users.email}, '@', 2)`
 *     ),
 * });
 *
 * const products = pgTable('products', {
 *   id: serial('id').primaryKey(),
 *   price: numeric('price', { precision: 10, scale: 2 }).notNull(),
 *   taxRate: numeric('tax_rate', { precision: 4, scale: 2 }).notNull(),
 *
 *   // Calculated price with tax
 *   priceWithTax: numeric('price_with_tax', { precision: 10, scale: 2 })
 *     .generatedAlwaysAs(
 *       sql`price * (1 + tax_rate / 100)`
 *     ),
 * });
 *
 * const rectangles = pgTable('rectangles', {
 *   id: serial('id').primaryKey(),
 *   width: integer('width').notNull(),
 *   height: integer('height').notNull(),
 *
 *   // Computed area
 *   area: integer('area')
 *     .generatedAlwaysAs(
 *       sql`width * height`
 *     ),
 * });
 *
 * // Insert - generated columns are computed automatically
 * await db.insert(users).values({
 *   firstName: 'John',
 *   lastName: 'Doe',
 *   email: 'john@example.com',
 *   // fullName and emailDomain are computed automatically
 * });
 *
 * // Query - can use generated columns
 * const result = await db
 *   .select()
 *   .from(users)
 *   .where(eq(users.fullName, 'John Doe'));
 *
 * // Index generated columns
 * const indexedUsers = pgTable('indexed_users', {
 *   id: serial('id').primaryKey(),
 *   firstName: text('first_name'),
 *   lastName: text('last_name'),
 *   fullName: text('full_name')
 *     .generatedAlwaysAs(sql`first_name || ' ' || last_name`),
 * }, (table) => ({
 *   fullNameIndex: index('full_name_idx').on(table.fullName),
 * }));
 * ```
 */
interface PgColumnBuilder {
  generatedAlwaysAs(
    expression: SQL | (() => SQL)
  ): this;
}

Composite Keys and Advanced Constraints

Composite Primary Keys

import { primaryKey } from 'drizzle-orm/pg-core';

/**
 * Creates a composite primary key constraint.
 *
 * @remarks
 * - Primary key across multiple columns
 * - Commonly used for junction/join tables
 * - Ensures unique combination of columns
 *
 * @example
 * ```typescript
 * // Many-to-many relationship
 * const userGroups = pgTable('user_groups', {
 *   userId: integer('user_id')
 *     .notNull()
 *     .references(() => users.id),
 *   groupId: integer('group_id')
 *     .notNull()
 *     .references(() => groups.id),
 *   joinedAt: timestamp('joined_at').defaultNow(),
 * }, (table) => ({
 *   // Composite primary key
 *   pk: primaryKey({ columns: [table.userId, table.groupId] }),
 *
 *   // Named composite primary key
 *   namedPk: primaryKey({
 *     name: 'user_groups_pk',
 *     columns: [table.userId, table.groupId]
 *   }),
 * }));
 *
 * // Three-way join table
 * const permissions = pgTable('permissions', {
 *   userId: integer('user_id').notNull(),
 *   resourceId: integer('resource_id').notNull(),
 *   action: text('action').notNull(),
 * }, (table) => ({
 *   pk: primaryKey({
 *     columns: [table.userId, table.resourceId, table.action]
 *   }),
 * }));
 * ```
 */
function primaryKey<TColumns extends PgColumn[]>(config: {
  columns: TColumns;
  name?: string;
}): PrimaryKeyBuilder;

Composite Foreign Keys

import { foreignKey } from 'drizzle-orm/pg-core';

/**
 * Creates a composite foreign key constraint.
 *
 * @remarks
 * - Foreign key across multiple columns
 * - References composite primary key in another table
 * - Supports ON UPDATE and ON DELETE actions
 *
 * @example
 * ```typescript
 * const orderItems = pgTable('order_items', {
 *   orderId: integer('order_id').notNull(),
 *   productId: integer('product_id').notNull(),
 *   quantity: integer('quantity').notNull(),
 * }, (table) => ({
 *   // Composite foreign key
 *   orderProductFk: foreignKey({
 *     columns: [table.orderId, table.productId],
 *     foreignColumns: [orders.id, products.id],
 *   }),
 *
 *   // With referential actions
 *   fkWithActions: foreignKey({
 *     columns: [table.orderId, table.productId],
 *     foreignColumns: [orders.id, products.id],
 *   })
 *     .onUpdate('cascade')
 *     .onDelete('restrict'),
 * }));
 *
 * // Single column foreign key alternative
 * const comments = pgTable('comments', {
 *   id: serial('id').primaryKey(),
 *   postId: integer('post_id')
 *     .notNull()
 *     .references(() => posts.id, {
 *       onDelete: 'cascade',
 *       onUpdate: 'cascade'
 *     }),
 * });
 * ```
 */
function foreignKey<
  TColumns extends [PgColumn, ...PgColumn[]],
  TForeignColumns extends [PgColumn, ...PgColumn[]]
>(config: {
  columns: TColumns;
  foreignColumns: TForeignColumns;
  name?: string;
}): ForeignKeyBuilder;

interface ForeignKeyBuilder {
  onUpdate(action: 'cascade' | 'restrict' | 'no action' | 'set null' | 'set default'): this;
  onDelete(action: 'cascade' | 'restrict' | 'no action' | 'set null' | 'set default'): this;
}

Composite Unique Constraints

import { unique } from 'drizzle-orm/pg-core';

/**
 * Creates a composite unique constraint.
 *
 * @remarks
 * - Ensures unique combination of multiple columns
 * - Can specify NULL handling (NULLS DISTINCT/NOT DISTINCT)
 * - PostgreSQL 15+ supports NULLS NOT DISTINCT
 *
 * @example
 * ```typescript
 * const users = pgTable('users', {
 *   id: serial('id').primaryKey(),
 *   email: text('email').notNull(),
 *   provider: text('provider').notNull(),
 *   providerId: text('provider_id').notNull(),
 * }, (table) => ({
 *   // Unique combination
 *   providerUnique: unique()
 *     .on(table.provider, table.providerId),
 *
 *   // Named constraint
 *   namedUnique: unique('provider_id_unique')
 *     .on(table.provider, table.providerId),
 *
 *   // With NULL handling (PostgreSQL 15+)
 *   nullsNotDistinct: unique()
 *     .on(table.email, table.provider)
 *     .nullsNotDistinct(),
 * }));
 *
 * // Single column unique constraint
 * const products = pgTable('products', {
 *   id: serial('id').primaryKey(),
 *   sku: text('sku').notNull().unique(),
 *   name: text('name').notNull(),
 * });
 * ```
 */
function unique(name?: string): UniqueConstraintBuilder;

interface UniqueConstraintBuilder {
  on(...columns: PgColumn[]): this;
  nullsNotDistinct(): this;
}

Check Constraints

import { check } from 'drizzle-orm/pg-core';

/**
 * Creates a check constraint.
 *
 * @remarks
 * - Validates data based on boolean expression
 * - Can reference multiple columns
 * - Evaluated on INSERT and UPDATE
 *
 * @example
 * ```typescript
 * import { sql } from 'drizzle-orm';
 *
 * const products = pgTable('products', {
 *   id: serial('id').primaryKey(),
 *   price: numeric('price', { precision: 10, scale: 2 }).notNull(),
 *   discountPrice: numeric('discount_price', { precision: 10, scale: 2 }),
 *   stock: integer('stock').notNull(),
 * }, (table) => ({
 *   // Price must be positive
 *   positivePrice: check(
 *     'positive_price',
 *     sql`${table.price} > 0`
 *   ),
 *
 *   // Discount price must be less than price
 *   validDiscount: check(
 *     'valid_discount',
 *     sql`${table.discountPrice} < ${table.price}`
 *   ),
 *
 *   // Stock cannot be negative
 *   nonNegativeStock: check(
 *     'non_negative_stock',
 *     sql`${table.stock} >= 0`
 *   ),
 * }));
 *
 * const users = pgTable('users', {
 *   id: serial('id').primaryKey(),
 *   age: integer('age'),
 *   email: text('email'),
 * }, (table) => ({
 *   // Age between 18 and 120
 *   ageRange: check(
 *     'age_range',
 *     sql`${table.age} BETWEEN 18 AND 120`
 *   ),
 *
 *   // Email format
 *   emailFormat: check(
 *     'email_format',
 *     sql`${table.email} LIKE '%@%.%'`
 *   ),
 * }));
 * ```
 */
function check(name: string, condition: SQL): CheckBuilder;

Summary

This document covered all PostgreSQL-specific features in Drizzle ORM:

  1. Column Types: Complete coverage of numeric, string, date/time, boolean, JSON, network, geometric, and UUID types
  2. Array Types: Arrays of any base type with multi-dimensional support
  3. Enum Types: Type-safe enums with pgEnum and pgEnumObject
  4. Extensions: PostGIS for geographic data and pgvector for AI embeddings
  5. Schemas: Namespace organization for database objects
  6. Views: Regular and materialized views with rich configuration
  7. Sequences: Customizable number generators
  8. Row-Level Security: Fine-grained access control with policies and roles
  9. Indexes: Multiple index methods (btree, hash, gist, gin, brin, hnsw, ivfflat)
  10. Query Features: DISTINCT ON, RETURNING, and row locking
  11. Identity Columns: Modern auto-increment with SQL standard syntax
  12. Generated Columns: Computed columns that auto-update
  13. Composite Constraints: Multi-column primary keys, foreign keys, unique constraints, and checks

All features include comprehensive examples demonstrating real-world usage patterns with full type safety.