Relevant source files
The following files were used as context for generating this wiki page:
- drizzle-orm/src/sqlite-core/columns/common.ts
- drizzle-kit/src/introspect-pg.ts
- drizzle-orm/type-tests/sqlite/tables.ts
- drizzle-orm/src/sqlite-core/table.ts
- drizzle-kit/src/introspect-gel.ts
- drizzle-orm/src/column-builder.ts
- drizzle-orm/src/pg-core/indexes.ts
- drizzle-kit/src/introspect-singlestore.ts
- drizzle-kit/src/introspect-mysql.ts
- drizzle-kit/src/sqlgenerator.ts
- drizzle-orm/src/gel-core/dialect.ts
- drizzle-orm/src/sqlite-core/dialect.ts
- drizzle-orm/src/singlestore-core/dialect.ts
- drizzle-orm/src/gel-core/columns/common.ts
- drizzle-orm/src/sqlite-core/primary-keys.ts
- drizzle-orm/src/table.ts
- drizzle-orm/src/gel-core/indexes.ts
- drizzle-orm/src/pg-core/columns/common.ts
- drizzle-kit/src/introspect-sqlite.ts
- drizzle-orm/src/sqlite-core/indexes.ts
- drizzle-orm/src/pg-core/table.ts
- drizzle-orm/src/mysql-core/columns/common.ts
- drizzle-orm/src/alias.ts
- drizzle-orm/src/sqlite-core/db.ts
- drizzle-orm/src/sqlite-core/columns/integer.ts
- drizzle-orm/src/mysql-core/table.ts
- drizzle-orm/src/gel-core/table.ts
- drizzle-orm/src/sqlite-core/foreign-keys.ts
- drizzle-orm/src/mysql-core/indexes.ts
- drizzle-orm/src/sqlite-core/utils.ts
Overview
Table and Column Models supply the core structural foundation for defining database entities, column definitions, and schema metadata across all supported dialects. They provide the base abstractions and symbol branding needed to construct typed tables, configure column builders, establish foreign keys, and map driver values.
Sources: drizzle-orm/src/table.ts:42-118
By combining declarative TypeScript types with runtime column transformers and table proxy handlers, this system bridges high-level schema definitions with low-level SQL query generation and relational query execution.
Sources: drizzle-orm/src/alias.ts:10-75, drizzle-orm/src/column-builder.ts:11-26
Core Table Architecture and Invariants
Base Table Class and Symbol Branding
The Table class serves as the root abstraction for all schema entities, implementing SQLWrapper and carrying static metadata alongside internal symbol properties. Each table instance is branded at runtime using IsDrizzleTable and maintains configuration traits through TypeScript mapped types (TableConfig, InferSelectModel, and InferInsertModel).
Internal state tracking relies on a set of exported symbols located on Table.Symbol. These symbols isolate metadata properties from user-defined column names, preserving clean namespace separation for table names, schemas, and alias mapping flags.
export class Table<T extends TableConfig = TableConfig> implements SQLWrapper {
static readonly [entityKind]: string = 'Table';
declare readonly _: {
readonly brand: 'Table';
readonly config: T;
readonly name: T['name'];
readonly schema: T['schema'];
readonly columns: T['columns'];
readonly inferSelect: InferSelectModel<Table<T>>;
readonly inferInsert: InferInsertModel<Table<T>>;
};
...Sources: drizzle-orm/src/table.ts:42-60
| Symbol Name | Runtime Symbol Key | Purpose |
|---|---|---|
Name | drizzle:TableName | Stores the active table name (modifiable during aliasing). |
Schema | drizzle:Schema | Stores the optional database schema identifier. |
OriginalName | drizzle:OriginalName | Preserves the initial unaliased table name. |
Columns | drizzle:Columns | Holds the record mapping column keys to column definitions. |
ExtraConfigColumns | drizzle:ExtraConfigColumns | Stores extra configuration columns during table building. |
BaseName | drizzle:BaseName | Preserves the pre-transformation name before tableCreator hooks. |
IsAlias | drizzle:IsAlias | Boolean flag indicating whether the table instance is an alias. |
ExtraConfigBuilder | drizzle:ExtraConfigBuilder | Callback reference for evaluating extra table constraints and indexes. |
Sources: drizzle-orm/src/table.ts:20-40, drizzle-orm/src/table.ts:65-112
Note
The isTable runtime check validates table instances exclusively by inspecting whether IsDrizzleTable exists on the target object, ensuring fast identification without relying on instanceof checks that can fail across module boundary versions.
Sources: drizzle-orm/src/table.ts
Entity Metadata and Relational Inference
Tables expose inference utilities ($inferSelect and $inferInsert) that evaluate column definitions to construct strict TypeScript models. The InferModelFromColumns type maps column configurations, handling database column name remapping when dbColumnNames is enabled and distinguishing required versus optional insert keys using column operation markers (RequiredKeyOnly and OptionalKeyOnly).
export type InferModelFromColumns<
TColumns extends Record<string, Column>,
TInferMode extends 'select' | 'insert' = 'select',
TConfig extends { dbColumnNames: boolean; override?: boolean } = { dbColumnNames: false; override: false },
> = Simplify<
TInferMode extends 'insert' ?
& {
[
Key in keyof TColumns & string as RequiredKeyOnly<
MapColumnName<Key, TColumns[Key], TConfig['dbColumnNames']>,
TColumns[Key]
>
]: GetColumnData<TColumns[Key], 'query'>;
}
& {
[
Key in keyof TColumns & string as OptionalKeyOnly<
MapColumnName<Key, TColumns[Key], TConfig['dbColumnNames']>,
TColumns[Key],
TConfig['override']
>
]?: GetColumnData<TColumns[Key], 'query'> | undefined;
}
: {
[
Key in keyof TColumns & string as MapColumnName<
Key,
TColumns[Key],
TConfig['dbColumnNames']
>
]: GetColumnData<TColumns[Key], 'query'>;
}
>;Sources: drizzle-orm/src/table.ts:155-187
Functions such as getTableName and getTableUniqueName extract structural metadata directly from table symbols, providing fully qualified namespace strings (schema.tablename) for query builders and the relational query engine.
export function getTableName<T extends Table>(table: T): T['_']['name'] {
return table[TableName];
}
export function getTableUniqueName<T extends Table>(table: T): `${T['_']['schema']}.${T['_']['name']}` {
return `${table[Schema] ?? 'public'}.${table[TableName]}`;
}Sources: drizzle-orm/src/table.ts:143-149
Column Builder Lifecycle and Configuration
Configuration Contracts and Type Mapping
Column builders manage the definition and serialization attributes of database columns before they are materialized into table columns. The core configuration contract relies on ColumnBuilderBaseConfig, which enforces properties such as column name, data type, column type, driver parameters, and enumerated values. The MakeColumnConfig utility type processes these base configurations to determine nullability, default presence, primary key status, autoincrement flags, and generated identity configurations across table columns.
export interface ColumnBuilderBaseConfig<TDataType extends ColumnDataType, TColumnType extends string> {
name: string;
dataType: TDataType;
columnType: TColumnType;
data: unknown;
driverParam: unknown;
enumValues: string[] | undefined;
}
export type MakeColumnConfig<
T extends ColumnBuilderBaseConfig<ColumnDataType, string>,
TTableName extends string,
TData = T extends { $type: infer U } ? U : T['data'],
> = {
name: T['name'];
tableName: TTableName;
dataType: T['dataType'];
columnType: T['columnType'];
data: TData;
driverParam: T['driverParam'];
notNull: T extends { notNull: true } ? true : false;
hasDefault: T extends { hasDefault: true } ? true : false;
isPrimaryKey: T extends { isPrimaryKey: true } ? true : false;
isAutoincrement: T extends { isAutoincrement: true } ? true : false;
hasRuntimeDefault: T extends { hasRuntimeDefault: true } ? true : false;
enumValues: T['enumValues'];
baseColumn: T extends { baseBuilder: infer U extends ColumnBuilderBase } ? BuildColumn<TTableName, U, 'common'>
: never;
identity: T extends { identity: 'always' } ? 'always' : T extends { identity: 'byDefault' } ? 'byDefault' : undefined;
generated: T extends { generated: infer G } ? unknown extends G ? undefined
: G extends undefined ? undefined
: G
: undefined;
} & {};Sources: drizzle-orm/src/column-builder.ts:46-79
Supported primitive data types span string, number, boolean, array, json, date, bigint, custom, buffer, dateDuration, duration, relDuration, localTime, localDate, and localDateTime. Dialect targets are explicitly restricted to pg, mysql, sqlite, singlestore, common, and gel.
export type ColumnDataType =
| 'string'
| 'number'
| 'boolean'
| 'array'
| 'json'
| 'date'
| 'bigint'
| 'custom'
| 'buffer'
| 'dateDuration'
| 'duration'
| 'relDuration'
| 'localTime'
| 'localDate'
| 'localDateTime';
export type Dialect = 'pg' | 'mysql' | 'sqlite' | 'singlestore' | 'common' | 'gel';Sources: drizzle-orm/src/column-builder.ts:11-28
Note
ColumnBuilderRuntimeConfig stores active runtime flags such as keyAsName, notNull, hasDefault, default, defaultFn, and onUpdateFn to evaluate column behaviors during schema building and statement generation.
Sources: drizzle-orm/src/column-builder.ts
SQLite Integer, Timestamp, and Boolean Builders
SQLite columns demonstrate specialized builder lifecycles and driver value transformations through SQLiteBaseIntegerBuilder and its concrete extensions. The integer factory function parses optional names and configurations, branching to construct SQLiteIntegerBuilder, SQLiteTimestampBuilder, or SQLiteBooleanBuilder depending on the selected mode.
export interface IntegerConfig<
TMode extends 'number' | 'timestamp' | 'timestamp_ms' | 'boolean' =
| 'number'
| 'timestamp'
| 'timestamp_ms'
| 'boolean',
> {
mode: TMode;
}
export function integer(): SQLiteIntegerBuilderInitial<''>;
export function integer<TMode extends IntegerConfig['mode']>(
config?: IntegerConfig<TMode>,
): Or<Equal<TMode, 'timestamp'>, Equal<TMode, 'timestamp_ms'>> extends true ? SQLiteTimestampBuilderInitial<''>
: Equal<TMode, 'boolean'> extends true ? SQLiteBooleanBuilderInitial<''>
: SQLiteIntegerBuilderInitial<''>;
export function integer<TName extends string, TMode extends IntegerConfig['mode']>(
name: TName,
config?: IntegerConfig<TMode>,
): Or<Equal<TMode, 'timestamp'>, Equal<TMode, 'timestamp_ms'>> extends true ? SQLiteTimestampBuilderInitial<TName>
: Equal<TMode, 'boolean'> extends true ? SQLiteBooleanBuilderInitial<TName>
: SQLiteIntegerBuilderInitial<TName>;
export function integer(a?: string | IntegerConfig, b?: IntegerConfig) {
const { name, config } = getColumnNameAndConfig<IntegerConfig | undefined>(a, b);
if (config?.mode === 'timestamp' || config?.mode === 'timestamp_ms') {
return new SQLiteTimestampBuilder(name, config.mode);
}
if (config?.mode === 'boolean') {
return new SQLiteBooleanBuilder(name, config.mode);
}
return new SQLiteIntegerBuilder(name);
}
export const int = integer;Sources: drizzle-orm/src/sqlite-core/columns/integer.ts:204-237
| Column Type | Mode Parameter | Driver Parameter Type | Mapped JavaScript Type | Driver Mapping Logic |
|---|---|---|---|---|
SQLiteInteger | number (default) | number | number | Direct integer storage. |
SQLiteTimestamp | timestamp | number | Date | Multiplies or divides by 1000 for epoch seconds. |
SQLiteTimestamp | timestamp_ms | number | Date | Preserves millisecond epoch numbers directly. |
SQLiteBoolean | boolean | number | boolean | Translates integer 1 and 0 to boolean values. |
Sources: drizzle-orm/src/sqlite-core/columns/integer.ts:66-202
Sources: drizzle-orm/src/sqlite-core/columns/integer.ts:143-202, drizzle-orm/src/sqlite-core/columns/integer.ts:228-235
Caution
Calling defaultNow() on a SQLiteTimestampBuilder is deprecated; column defaults should be assigned using .default() with custom SQL expressions instead.
Dialect Table Instantiation and Normalization
Table Creation Functions and Schemas
Table instantiation across database engines relies on dialect-specific table classes that extend the core Table abstraction while managing engine-specific symbols and configuration types. The SQLiteTable class defines InlineForeignKeys symbol branding to track foreign keys declared within column definitions SQLiteTable:
export const InlineForeignKeys = Symbol.for('drizzle:SQLiteInlineForeignKeys');
export class SQLiteTable<T extends TableConfig = TableConfig> extends Table<T> {
static override readonly [entityKind]: string = 'SQLiteTable';
/** @internal */
static override readonly Symbol = Object.assign({}, Table.Symbol, {
InlineForeignKeys: InlineForeignKeys as typeof InlineForeignKeys,
});
/** @internal */
override [Table.Symbol.Columns]!: NonNullable<T['columns']>;
/** @internal */
[InlineForeignKeys]: ForeignKey[] = [];
/** @internal */
override [Table.Symbol.ExtraConfigBuilder]:
| ((self: Record<string, SQLiteColumn>) => SQLiteTableExtraConfig)
| undefined = undefined;
}Sources: drizzle-orm/src/sqlite-core/table.ts:27-47
Similarly, PgTable in PostgreSQL and GelTable in Gel introduce both InlineForeignKeys and EnableRLS symbols to support row-level security policy toggles and inline constraints PgTable, GelTable:
/** @internal */
export const InlineForeignKeys = Symbol.for('drizzle:PgInlineForeignKeys');
/** @internal */
export const EnableRLS = Symbol.for('drizzle:EnableRLS');
export class PgTable<T extends TableConfig = TableConfig> extends Table<T> {
static override readonly [entityKind]: string = 'PgTable';
/** @internal */
static override readonly Symbol = Object.assign({}, Table.Symbol, {
InlineForeignKeys: InlineForeignKeys as typeof InlineForeignKeys,
EnableRLS: EnableRLS as typeof EnableRLS,
});
/**@internal */
[InlineForeignKeys]: ForeignKey[] = [];
/** @internal */
[EnableRLS]: boolean = false;
/** @internal */
override [Table.Symbol.ExtraConfigBuilder]: ((self: Record<string, PgColumn>) => PgTableExtraConfig) | undefined =
undefined;
/** @internal */
override [Table.Symbol.ExtraConfigColumns]: Record<string, ExtraConfigColumn> = {};
}Sources: drizzle-orm/src/pg-core/table.ts:28-54
/** @internal */
export const InlineForeignKeys = Symbol.for('drizzle:GelInlineForeignKeys');
/** @internal */
export const EnableRLS = Symbol.for('drizzle:EnableRLS');
export class GelTable<T extends TableConfig = TableConfig> extends Table<T> {
static override readonly [entityKind]: string = 'GelTable';
/** @internal */
static override readonly Symbol = Object.assign({}, Table.Symbol, {
InlineForeignKeys: InlineForeignKeys as typeof InlineForeignKeys,
EnableRLS: EnableRLS as typeof EnableRLS,
});
/**@internal */
[InlineForeignKeys]: ForeignKey[] = [];
/** @internal */
[EnableRLS]: boolean = false;
/** @internal */
override [Table.Symbol.ExtraConfigBuilder]: ((self: Record<string, GelColumn>) => GelTableExtraConfig) | undefined =
undefined;
/** @internal */
override [Table.Symbol.ExtraConfigColumns]: Record<string, GelExtraConfigColumn> = {};
}Sources: drizzle-orm/src/gel-core/table.ts:28-54
MySqlTable implements equivalent symbol tracking for MySQL environments MySqlTable:
/** @internal */
export const InlineForeignKeys = Symbol.for('drizzle:MySqlInlineForeignKeys');
export class MySqlTable<T extends TableConfig = TableConfig> extends Table<T> {
static override readonly [entityKind]: string = 'MySqlTable';
declare protected $columns: T['columns'];
/** @internal */
static override readonly Symbol = Object.assign({}, Table.Symbol, {
InlineForeignKeys: InlineForeignKeys as typeof InlineForeignKeys,
});
/** @internal */
override [Table.Symbol.Columns]!: NonNullable<T['columns']>;
/** @internal */
[InlineForeignKeys]: ForeignKey[] = [];
/** @internal */
override [Table.Symbol.ExtraConfigBuilder]:
| ((self: Record<string, MySqlColumn>) => MySqlTableExtraConfig)
| undefined = undefined;
}Sources: drizzle-orm/src/mysql-core/table.ts:26-49
Column Builder Parsing and Normalization
During table construction, column definitions can be provided either as a static record map or via a callback function receiving engine-specific column builders (SQLiteColumnBuilders, PgColumnsBuilders, MySqlColumnBuilders, or GelColumnsBuilders). The normalization routine resolves these definitions, sets column names, and instantiates the underlying column objects pgTableWithSchema, mysqlTableWithSchema, gelTableWithSchema:
export function pgTableWithSchema<
TTableName extends string,
TSchemaName extends string | undefined,
TColumnsMap extends Record<string, PgColumnBuilderBase>,
>(
name: TTableName,
columns: TColumnsMap | ((columnTypes: PgColumnsBuilders) => TColumnsMap),
extraConfig:
| ((self: BuildExtraConfigColumns<TTableName, TColumnsMap, 'pg'>) => PgTableExtraConfig | PgTableExtraConfigValue[])
| undefined,
schema: TSchemaName,
baseName = name,
): PgTableWithColumns<{
name: TTableName;
schema: TSchemaName;
columns: BuildColumns<TTableName, TColumnsMap, 'pg'>;
dialect: 'pg';
}> {
const rawTable = new PgTable<{
name: TTableName;
schema: TSchemaName;
columns: BuildColumns<TTableName, TColumnsMap, 'pg'>;
dialect: 'pg';
}>(name, schema, baseName);
const parsedColumns: TColumnsMap = typeof columns === 'function' ? columns(getPgColumnBuilders()) : columns;
const builtColumns = Object.fromEntries(
Object.entries(parsedColumns).map(([name, colBuilderBase]) => {
const colBuilder = colBuilderBase as PgColumnBuilder;
colBuilder.setName(name);
const column = colBuilder.build(rawTable);
rawTable[InlineForeignKeys].push(...colBuilder.buildForeignKeys(column, rawTable));
return [name, column];
}),
) as unknown as BuildColumns<TTableName, TColumnsMap, 'pg'>;
const builtColumnsForExtraConfig = Object.fromEntries(
Object.entries(parsedColumns).map(([name, colBuilderBase]) => {
const colBuilder = colBuilderBase as PgColumnBuilder;
colBuilder.setName(name);
const column = colBuilder.buildExtraConfigColumn(rawTable);
return [name, column];
}),
) as unknown as BuildExtraConfigColumns<TTableName, TColumnsMap, 'pg'>;
const table = Object.assign(rawTable, builtColumns);
table[Table.Symbol.Columns] = builtColumns;
table[Table.Symbol.ExtraConfigColumns] = builtColumnsForExtraConfig;
if (extraConfig) {
table[PgTable.Symbol.ExtraConfigBuilder] = extraConfig as any;
}
return Object.assign(table, {
enableRLS: () => {
table[PgTable.Symbol.EnableRLS] = true;
return table as PgTableWithColumns<{
name: TTableName;
schema: TSchemaName;
columns: BuildColumns<TTableName, TColumnsMap, 'pg'>;
dialect: 'pg';
}>;
},
});
}Sources: drizzle-orm/src/pg-core/table.ts:71-137
export function mysqlTableWithSchema<
TTableName extends string,
TSchemaName extends string | undefined,
TColumnsMap extends Record<string, MySqlColumnBuilderBase>,
>(
name: TTableName,
columns: TColumnsMap | ((columnTypes: MySqlColumnBuilders) => TColumnsMap),
extraConfig:
| ((
self: BuildColumns<TTableName, TColumnsMap, 'mysql'>,
) => MySqlTableExtraConfig | MySqlTableExtraConfigValue[])
| undefined,
schema: TSchemaName,
baseName = name,
): MySqlTableWithColumns<{
name: TTableName;
schema: TSchemaName;
columns: BuildColumns<TTableName, TColumnsMap, 'mysql'>;
dialect: 'mysql';
}> {
const rawTable = new MySqlTable<{
name: TTableName;
schema: TSchemaName;
columns: BuildColumns<TTableName, TColumnsMap, 'mysql'>;
dialect: 'mysql';
}>(name, schema, baseName);
const parsedColumns: TColumnsMap = typeof columns === 'function' ? columns(getMySqlColumnBuilders()) : columns;
const builtColumns = Object.fromEntries(
Object.entries(parsedColumns).map(([name, colBuilderBase]) => {
const colBuilder = colBuilderBase as MySqlColumnBuilder;
colBuilder.setName(name);
const column = colBuilder.build(rawTable);
rawTable[InlineForeignKeys].push(...colBuilder.buildForeignKeys(column, rawTable));
return [name, column];
}),
) as unknown as BuildColumns<TTableName, TColumnsMap, 'mysql'>;
const table = Object.assign(rawTable, builtColumns);
table[Table.Symbol.Columns] = builtColumns;
table[Table.Symbol.ExtraConfigColumns] = builtColumns as unknown as BuildExtraConfigColumns<
TTableName,
TColumnsMap,
'mysql'
>;
if (extraConfig) {
table[MySqlTable.Symbol.ExtraConfigBuilder] = extraConfig as unknown as (
self: Record<string, MySqlColumn>,
) => MySqlTableExtraConfig;
}
return table;
}Sources: drizzle-orm/src/mysql-core/table.ts:61-116
export function gelTableWithSchema<
TTableName extends string,
TSchemaName extends string | undefined,
TColumnsMap extends Record<string, GelColumnBuilderBase>,
>(
name: TTableName,
columns: TColumnsMap | ((columnTypes: GelColumnsBuilders) => TColumnsMap),
extraConfig:
| ((
self: BuildExtraConfigColumns<TTableName, TColumnsMap, 'gel'>,
) => GelTableExtraConfig | GelTableExtraConfigValue[])
| undefined,
schema: TSchemaName,
baseName = name,
): GelTableWithColumns<{
name: TTableName;
schema: TSchemaName;
columns: BuildColumns<TTableName, TColumnsMap, 'gel'>;
dialect: 'gel';
}> {
const rawTable = new GelTable<{
name: TTableName;
schema: TSchemaName;
columns: BuildColumns<TTableName, TColumnsMap, 'gel'>;
dialect: 'gel';
}>(name, schema, baseName);
const parsedColumns: TColumnsMap = typeof columns === 'function' ? columns(getGelColumnBuilders()) : columns;
const builtColumns = Object.fromEntries(
Object.entries(parsedColumns).map(([name, colBuilderBase]) => {
const colBuilder = colBuilderBase as GelColumnBuilder;
colBuilder.setName(name);
const column = colBuilder.build(rawTable);
rawTable[InlineForeignKeys].push(...colBuilder.buildForeignKeys(column, rawTable));
return [name, column];
}),
) as unknown as BuildColumns<TTableName, TColumnsMap, 'gel'>;
const builtColumnsForExtraConfig = Object.fromEntries(
Object.entries(parsedColumns).map(([name, colBuilderBase]) => {
const colBuilder = colBuilderBase as GelColumnBuilder;
colBuilder.setName(name);
const column = colBuilder.buildExtraConfigColumn(rawTable);
return [name, column];
}),
) as unknown as BuildExtraConfigColumns<TTableName, TColumnsMap, 'gel'>;
const table = Object.assign(rawTable, builtColumns);
table[Table.Symbol.Columns] = builtColumns;
table[Table.Symbol.ExtraConfigColumns] = builtColumnsForExtraConfig;
if (extraConfig) {
table[GelTable.Symbol.ExtraConfigBuilder] = extraConfig as any;
}
return Object.assign(table, {
enableRLS: () => {
table[GelTable.Symbol.EnableRLS] = true;
return table as GelTableWithColumns<{
name: TTableName;
schema: TSchemaName;
columns: BuildColumns<TTableName, TColumnsMap, 'gel'>;
dialect: 'gel';
}>;
},
});
}Sources: drizzle-orm/src/gel-core/table.ts:73-141
Note
PostgreSQL and Gel table factories build both standard columns and extra config columns (buildExtraConfigColumn), enabling table-level constraints to reference column metadata prior to schema serialization.
Sources: drizzle-orm/src/pg-core/table.ts, drizzle-orm/src/gel-core/table.ts
Warning
Passing an object instead of an array as the third parameter (extraConfig) to table builder functions is deprecated and will be removed in future releases across SQLite, MySQL, and Gel.
Sources: drizzle-orm/src/sqlite-core/table.ts, drizzle-orm/src/mysql-core/table.ts, drizzle-orm/src/gel-core/table.ts
Table Proxies and Alias Remapping
Proxy Handlers and Identifier Aliasing
Table and column aliasing leverage JavaScript Proxy objects to intercept property access, symbol evaluation, and schema traversal across query builders and dialect runtimes. The TableAliasProxyHandler intercepts core table symbols such as Table.Symbol.IsAlias, Table.Symbol.Name, and Table.Symbol.OriginalName to substitute alias strings while mapping column property accesses through ColumnAliasProxyHandler drizzle-orm/src/alias.ts.
export class TableAliasProxyHandler<T extends Table | View> implements ProxyHandler<T> {
static readonly [entityKind]: string = 'TableAliasProxyHandler';
constructor(private alias: string, private replaceOriginalName: boolean) {}
get(target: T, prop: string | symbol): any {
if (prop === Table.Symbol.IsAlias) {
return true;
}
if (prop === Table.Symbol.Name) {
return this.alias;
}
if (this.replaceOriginalName && prop === Table.Symbol.OriginalName) {
return this.alias;
}
if (prop === ViewBaseConfig) {
return {
...target[ViewBaseConfig as keyof typeof target],
name: this.alias,
isAlias: true,
};
}
if (prop === Table.Symbol.Columns) {
const columns = (target as Table)[Table.Symbol.Columns];
if (!columns) {
return columns;
}
const proxiedColumns: { [key: string]: any } = {};
Object.keys(columns).map((key) => {
proxiedColumns[key] = new Proxy(
columns[key]!,
new ColumnAliasProxyHandler(new Proxy(target, this)),
);
});
return proxiedColumns;
}
const value = target[prop as keyof typeof target];
if (is(value, Column)) {
return new Proxy(value as AnyColumn, new ColumnAliasProxyHandler(new Proxy(target, this)));
}
return value;
}
}Sources: drizzle-orm/src/alias.ts:24-75
The proxy mechanism ensures that references to columns or nested relations inherit the modified table context without mutating the underlying schema definitions. Dialect query builders such as SQLiteDialect incorporate these proxy utilities to handle self-joins, derived tables, and subquery projections drizzle-orm/src/sqlite-core/dialect.ts, drizzle-orm/src/singlestore-core/dialect.ts.
export function aliasedTable<T extends Table | View>(
table: T,
tableAlias: string,
): T {
return new Proxy(table, new TableAliasProxyHandler(tableAlias, false)) as any;
}
export function aliasedRelation<T extends Relation>(relation: T, tableAlias: string): T {
return new Proxy(relation, new RelationTableAliasProxyHandler(tableAlias));
}
export function aliasedTableColumn<T extends AnyColumn>(column: T, tableAlias: string): T {
return new Proxy(
column,
new ColumnAliasProxyHandler(new Proxy(column.table, new TableAliasProxyHandler(tableAlias, false))),
);
}
Sources: drizzle-orm/src/alias.ts:91-108
Note
TableAliasProxyHandler conditionally overrides Table.Symbol.OriginalName based on the boolean flag replaceOriginalName, allowing subqueries to retain or overwrite their underlying table identifier during compilation.
Sources: drizzle-orm/src/alias.ts
SQL Expression Rewrites
When queries contain nested expressions, subqueries, or aliased chunks, column references within the AST must be recursively updated to match the active table alias. The mapColumnsInSQLToAlias function inspects query chunks and wraps columns and subqueries with the designated alias proxy drizzle-orm/src/alias.ts.
export function mapColumnsInSQLToAlias(query: SQL, alias: string): SQL {
return sql.join(query.queryChunks.map((c) => {
if (is(c, Column)) {
return aliasedTableColumn(c, alias);
}
if (is(c, SQL)) {
return mapColumnsInSQLToAlias(c, alias);
}
if (is(c, SQL.Aliased)) {
return mapColumnsInAliasedSQLToAlias(c, alias);
}
return c;
}));
}Sources: drizzle-orm/src/alias.ts:113-126
| Proxy Handler | Target Entity | Intercepted Symbols / Properties | Purpose |
|---|---|---|---|
ColumnAliasProxyHandler | Column | 'table' | Binds column references to the aliased table or view container. |
TableAliasProxyHandler | Table | View | IsAlias, Name, OriginalName, Columns, ViewBaseConfig | Remaps table identifiers, flags, and column maps to use alias names. |
RelationTableAliasProxyHandler | Relation | 'sourceTable' | Recursively proxies relation source tables for relational graph queries. |
Sources: drizzle-orm/src/alias.ts:10-89
Constraint Resolution and Schema Utilities
Runtime Table Configuration and Entity Inspection
Table configurations, schema constraints, and builder metadata are inspected and resolved at runtime via utility functions like getTableConfig. For SQLite tables, this function iterates over column definitions, inline foreign keys, and extra configuration builders, instantiating and categorizing indexes, checks, unique constraints, primary keys, and foreign keys into a unified configuration object drizzle-orm/src/sqlite-core/utils.ts.
export function getTableConfig<TTable extends SQLiteTable>(table: TTable) {
const columns = Object.values(table[SQLiteTable.Symbol.Columns]);
const indexes: Index[] = [];
const checks: Check[] = [];
const primaryKeys: PrimaryKey[] = [];
const uniqueConstraints: UniqueConstraint[] = [];
const foreignKeys: ForeignKey[] = Object.values(table[SQLiteTable.Symbol.InlineForeignKeys]);
const name = table[Table.Symbol.Name];
const extraConfigBuilder = table[SQLiteTable.Symbol.ExtraConfigBuilder];
if (extraConfigBuilder !== undefined) {
const extraConfig = extraConfigBuilder(table[SQLiteTable.Symbol.Columns]);
const extraValues = Array.isArray(extraConfig) ? extraConfig.flat(1) as any[] : Object.values(extraConfig);
for (const builder of Object.values(extraValues)) {
if (is(builder, IndexBuilder)) {
indexes.push(builder.build(table));
} else if (is(builder, CheckBuilder)) {
checks.push(builder.build(table));
} else if (is(builder, UniqueConstraintBuilder)) {
uniqueConstraints.push(builder.build(table));
} else if (is(builder, PrimaryKeyBuilder)) {
primaryKeys.push(builder.build(table));
} else if (is(builder, ForeignKeyBuilder)) {
foreignKeys.push(builder.build(table));
}
}
}
return {
columns,
indexes,
foreignKeys,
checks,
primaryKeys,
uniqueConstraints,
name,
};
}Sources: drizzle-orm/src/sqlite-core/utils.ts:19-57
Entity resolution checks use type guards to differentiate builder instances such as IndexBuilder, CheckBuilder, UniqueConstraintBuilder, PrimaryKeyBuilder, and ForeignKeyBuilder, ensuring each constraint is correctly built against the target table instance prior to AST compilation drizzle-orm/src/sqlite-core/utils.ts.
for (const builder of Object.values(extraValues)) {
if (is(builder, IndexBuilder)) {
indexes.push(builder.build(table));
} else if (is(builder, CheckBuilder)) {
checks.push(builder.build(table));
} else if (is(builder, UniqueConstraintBuilder)) {
uniqueConstraints.push(builder.build(table));
} else if (is(builder, PrimaryKeyBuilder)) {
primaryKeys.push(builder.build(table));
} else if (is(builder, ForeignKeyBuilder)) {
foreignKeys.push(builder.build(table));
}
}Sources: drizzle-orm/src/sqlite-core/utils.ts:33-45
Note
extractUsedTable evaluates table inputs across SQLiteTable, Subquery, SQLiteViewBase, and SQL expression instances to resolve base table names and used table arrays during AST traversal.
Sources: drizzle-orm/src/sqlite-core/utils.ts
Index Builders and Dialect-Specific Options
Index configuration differs across dialects to accommodate specialized storage parameters, concurrency flags, and locking algorithms. PostgreSQL index builders support partial index conditions via where, storage options via with, access methods, and concurrency modifiers via concurrently and only drizzle-orm/src/pg-core/indexes.ts.
interface IndexConfig {
name?: string;
columns: Partial<IndexedColumn | SQL>[];
/**
* If true, the index will be created as `create unique index` instead of `create index`.
*/
unique: boolean;
/**
* If true, the index will be created as `create index concurrently` instead of `create index`.
*/
concurrently?: boolean;
/**
* If true, the index will be created as `create index ... on only <table>` instead of `create index ... on <table>`.
*/
only: boolean;
/**
* Condition for partial index.
*/
where?: SQL;
/**
* The optional WITH clause specifies storage parameters for the index
*/
with?: Record<string, any>;
/**
* The optional WITH clause method for the index
*/
method?: 'btree' | string;
}Sources: drizzle-orm/src/pg-core/indexes.ts:8-42
MySQL index builders manage index construction algorithms and locks directly within their configuration contracts drizzle-orm/src/mysql-core/indexes.ts.
interface IndexConfig {
name: string;
columns: IndexColumn[];
/**
* If true, the index will be created as `create unique index` instead of `create index`.
*/
unique?: boolean;
/**
* If set, the index will be created as `create index ... using { 'btree' | 'hash' }`.
*/
using?: 'btree' | 'hash';
/**
* If set, the index will be created as `create index ... algorithm { 'default' | 'inplace' | 'copy' }`.
*/
algorithm?: 'default' | 'inplace' | 'copy';
/**
* If set, adds locks to the index creation.
*/
lock?: 'default' | 'none' | 'shared' | 'exclusive';
}Sources: drizzle-orm/src/mysql-core/indexes.ts:6-30
| Index Configuration Property | Dialects Supported | Purpose / Values |
|---|---|---|
name | PostgreSQL, MySQL | Optional or required string identifier for the index. |
columns | PostgreSQL, MySQL | Array of indexed columns or raw SQL expressions. |
unique | PostgreSQL, MySQL | Boolean flag creating a unique index. |
concurrently | PostgreSQL | Creates the index without locking concurrent writes (create index concurrently). |
only | PostgreSQL | Restricts index creation to the specified table without inheritance children. |
where | PostgreSQL | Defines a partial index condition using a SQL expression. |
method | PostgreSQL, MySQL | Index access method (e.g., 'btree', 'hash', 'gist', 'gin', 'brin', 'hnsw', 'ivfflat'). |
algorithm | MySQL | Specifies index creation algorithm: 'default', 'inplace', 'copy'. |
lock | MySQL | Adds concurrency locks during index creation: 'default', 'none', 'shared', 'exclusive'. |
Sources: drizzle-orm/src/pg-core/indexes.ts:8-42, drizzle-orm/src/mysql-core/indexes.ts:6-30
Related
On This Page
OverviewCore Table Architecture and InvariantsBase Table Class and Symbol BrandingEntity Metadata and Relational InferenceColumn Builder Lifecycle and ConfigurationConfiguration Contracts and Type MappingSQLite Integer, Timestamp, and Boolean BuildersDialect Table Instantiation and NormalizationTable Creation Functions and SchemasColumn Builder Parsing and NormalizationTable Proxies and Alias RemappingProxy Handlers and Identifier AliasingSQL Expression RewritesConstraint Resolution and Schema UtilitiesRuntime Table Configuration and Entity InspectionIndex Builders and Dialect-Specific OptionsRelated