docs: add architecture research for schema-first multi-dialect TypeBox/Drizzle bridge

This commit is contained in:
2026-04-25 12:14:39 +00:00
parent c82fa8702e
commit 3034e6ebf8
4 changed files with 2238 additions and 0 deletions

View File

@@ -0,0 +1,440 @@
# Drizzle ORM Column Builder Differences: SQLite vs PostgreSQL vs MySQL
Research based on `drizzle-orm@0.38.4` source in `node_modules/drizzle-orm`.
## 1. Column Types by Dialect
### SQLite (`drizzle-orm/sqlite-core/columns/`)
| Function | columnType | dataType | Notes |
|--------------|--------------------|-----------|----------------------------------------------|
| `integer()` | `SQLiteInteger` | `number` | mode: `'number'` (default) |
| `integer()` | `SQLiteTimestamp` | `date` | mode: `'timestamp'` or `'timestamp_ms'` |
| `integer()` | `SQLiteBoolean` | `boolean` | mode: `'boolean'` |
| `text()` | `SQLiteText` | `string` | mode: `'text'` (default), supports `enum`, `length` |
| `text()` | `SQLiteTextJson` | `json` | mode: `'json'` |
| `real()` | `SQLiteReal` | `number` | - |
| `blob()` | `SQLiteBlobJson` | `json` | mode: `'json'` (default) |
| `blob()` | `SQLiteBlobBuffer` | `buffer` | mode: `'buffer'` |
| `blob()` | `SQLiteBigInt` | `bigint` | mode: `'bigint'` |
| `numeric()` | `SQLiteNumeric` | `string` | - |
| `customType()`| `SQLiteCustomColumn`| `custom` | - |
### PostgreSQL (`drizzle-orm/pg-core/columns/`)
| Function | columnType | dataType | Notes |
|------------------|--------------------|-----------|------------------------------|
| `integer()` | `PgInteger` | `number` | - |
| `smallint()` | `PgSmallInt` | `number` | - |
| `bigint()` | `PgBigInt` | `bigint` | |
| `serial()` | `PgSerial` | `number` | notNull + hasDefault built-in|
| `smallserial()` | `PgSmallSerial` | `number` | notNull + hasDefault built-in|
| `bigserial()` | `PgBigSerial` | `bigint` | notNull + hasDefault built-in|
| `boolean()` | `PgBoolean` | `boolean` | - |
| `text()` | `PgText` | `string` | supports `enum` |
| `varchar()` | `PgVarchar` | `string` | supports `enum`, `length` |
| `char()` | `PgChar` | `string` | supports `length`, `enum` |
| `numeric()` | `PgNumeric` | `string` | precision/scale config |
| `real()` | `PgReal` | `number` | - |
| `doublePrecision()`| `PgDoublePrecision`| `number` | - |
| `json()` | `PgJson` | `json` | - |
| `jsonb()` | `PgJsonb` | `json` | PG-specific |
| `uuid()` | `PgUUID` | `string` | has `.defaultRandom()` |
| `date()` | `PgDate`/`PgDateString` | `date`/`string` | mode toggle |
| `timestamp()` | `PgTimestamp`/`PgTimestampString`| `date`/`string` | mode, withTimezone, precision |
| `time()` | `PgTime`/`PgTimeString` | `string` | precision, withTimezone |
| `interval()` | `PgInterval` | `string` | PG-specific |
| `inet()` | `PgInet` | `string` | PG network type |
| `cidr()` | `PgCidr` | `string` | PG network type |
| `macaddr()` | `PgMacaddr` | `string` | PG network type |
| `macaddr8()` | `PgMacaddr8` | `string` | PG network type |
| `point()` | `PgPoint` | `string` | PG geometric type |
| `line()` | `PgLine` | `string` | PG geometric type |
| `pgEnum()` | `PgEnumColumn` | `string` | Requires pre-declared enum |
| `customType()` | `PgCustomColumn` | `custom` | - |
### MySQL (`drizzle-orm/mysql-core/columns/`)
| Function | columnType | dataType | Notes |
|------------------|-------------------------|-----------|--------------------------------------|
| `int()` | `MySqlInt` | `number` | `unsigned` config, `.autoincrement()`|
| `smallint()` | `MySqlSmallInt` | `number` | `unsigned`, `.autoincrement()` |
| `mediumint()` | `MySqlMediumInt` | `number` | `unsigned`, `.autoincrement()` |
| `tinyint()` | `MySqlTinyInt` | `number` | `unsigned`, `.autoincrement()` |
| `bigint()` | `MySqlBigInt` | `bigint` | `unsigned`, mode toggle |
| `serial()` | `MySqlSerial` | `number` | autoIncrement+primaryKey+notNull+default |
| `boolean()` | `MySqlBoolean` | `boolean` | - |
| `float()` | `MySqlFloat` | `number` | precision config |
| `double()` | `MySqlDouble` | `number` | precision config |
| `decimal()` | `MySqlDecimal` | `string` | precision/scale config |
| `real()` | `MySqlReal` | `number` | - |
| `text()` | `MySqlText` | `string` | supports `enum`, textType variants |
| `tinytext()` | `MySqlText` | `string` | textType: 'tinytext' |
| `mediumtext()` | `MySqlText` | `string` | textType: 'mediumtext' |
| `longtext()` | `MySqlText` | `string` | textType: 'longtext' |
| `varchar()` | `MySqlVarChar` | `string` | `length`, `enum` |
| `char()` | `MySqlChar` | `string` | `length`, `enum` |
| `json()` | `MySqlJson` | `json` | - |
| `date()` | `MySqlDate`/`MySqlDateString` | `date`/`string` | mode toggle |
| `datetime()` | `MySqlDateTime`/`MySqlDateTimeString` | `date`/`string` | mode, fsp |
| `timestamp()` | `MySqlTimestamp`/`MySqlTimestampString` | `date`/`string` | mode, fsp |
| `time()` | `MySqlTime`/`MySqlTimeString` | `string` | fsp |
| `binary()` | `MySqlBinary` | `buffer` | length config |
| `varbinary()` | `MySqlVarBinary` | `buffer` | length config |
| `year()` | `MySqlYear` | `number` | - |
| `mysqlEnum()` | `MySqlEnumColumn` | `string` | inline enum values |
| `customType()` | `MySqlCustomColumn` | `custom` | - |
## 2. Builder API Naming Differences
### Factory Function Names
The same concept has **different factory function names** per dialect. A tool like drizzlebox that must handle all three cannot assume a universal name space.
| Concept | SQLite | PostgreSQL | MySQL |
|-----------------|-----------------|---------------|-----------------|
| Integer | `integer` | `integer` | `int` |
| Small int | — | `smallint` | `smallint` |
| Big int | — | `bigint` | `bigint` |
| Auto-increment | `.primaryKey()` on `integer` | `serial`/`bigserial`/`smallserial` | `.autoincrement()` on `int` etc., or `serial` |
| Boolean | `integer({ mode: 'boolean' })` | `boolean` | `boolean` / `tinyint` |
| Text/varchar | `text` | `text` / `varchar` | `text` / `varchar` |
| JSON | `text({ mode: 'json' })` / `blob({ mode: 'json' })` | `json` / `jsonb` | `json` |
| Enum | `text({ enum: [...] })` | `pgEnum` (separate declaration) | `mysqlEnum` |
| Timestamp | `integer({ mode: 'timestamp' })` | `timestamp` | `timestamp` / `datetime` |
| UUID | `text()` (manual) | `uuid` | — |
| Numeric/decimal | `numeric` | `numeric` / `decimal` | `decimal` |
| Real/float | `real` | `real` | `float` / `double` / `real` |
### Builder Class Names
All builder classes are dialect-prefixed:
- **SQLite**: `SQLiteTextBuilder`, `SQLiteIntegerBuilder`, `SQLiteColumnBuilder` (base)
- **PG**: `PgTextBuilder`, `PgIntegerBuilder`, `PgColumnBuilder` (base)
- **MySQL**: `MySqlTextBuilder`, `MySqlIntBuilder`, `MySqlColumnBuilder` (base), `MySqlColumnBuilderWithAutoIncrement`
### Column Class Names (the `columnType` field)
All column classes are also dialect-prefixed:
- **SQLite**: `SQLiteText`, `SQLiteInteger`, `SQLiteTimestamp`, `SQLiteBoolean`, `SQLiteTextJson`, `SQLiteReal`, `SQLiteNumeric`, `SQLiteBlobJson`, `SQLiteBlobBuffer`, `SQLiteBigInt`, `SQLiteCustomColumn`
- **PG**: `PgText`, `PgInteger`, `PgBoolean`, `PgJson`, `PgJsonb`, `PgUUID`, `PgEnumColumn`, `PgNumeric`, `PgVarchar`, `PgSerial`, etc.
- **MySQL**: `MySqlText`, `MySqlInt`, `MySqlBoolean`, `MySqlJson`, `MySqlEnumColumn`, `MySqlDecimal`, `MySqlVarChar`, `MySqlSerial`, etc.
## 3. Shared Column Builder API (from base `ColumnBuilder`)
All three dialect builder hierarchies share a common base class `ColumnBuilder` (in `drizzle-orm/column-builder.d.ts`) that provides these universal methods:
| Method | Description |
|---------------------|-------------|
| `.notNull()` | Makes column not null |
| `.default(value)` | Set a default value |
| `.$defaultFn(fn)` / `.$default` | Dynamic runtime default |
| `.$onUpdateFn(fn)` / `.$onUpdate` | Dynamic runtime update value |
| `.primaryKey()` | Makes column a primary key (implies notNull) |
| `.$type<T>()` | Override the TypeScript type |
| `.generatedAlwaysAs(as, config?)` | Generated column (overridden per-dialect) |
The `ColumnBuilderRuntimeConfig` shared by all dialects:
```ts
{
name: string;
keyAsName: boolean;
notNull: boolean;
default: TData | SQL | undefined;
defaultFn: (() => TData | SQL) | undefined;
onUpdateFn: (() => TData | SQL) | undefined;
hasDefault: boolean;
primaryKey: boolean;
isUnique: boolean;
uniqueName: string | undefined;
uniqueType: string | undefined;
dataType: string;
columnType: string;
generated: GeneratedColumnConfig<TData> | undefined;
generatedIdentity: GeneratedIdentityConfig | undefined;
}
```
The `ColumnBaseConfig` (shared, on the Column side):
```ts
{
name: string;
tableName: string;
dataType: ColumnDataType; // 'string' | 'number' | 'boolean' | 'array' | 'json' | 'date' | 'bigint' | 'custom' | 'buffer'
columnType: string; // Dialect-specific e.g. 'PgText', 'SQLiteInteger'
data: unknown;
driverParam: unknown;
notNull: boolean;
hasDefault: boolean;
isPrimaryKey: boolean;
isAutoincrement: boolean;
hasRuntimeDefault: boolean;
enumValues: string[] | undefined;
}
```
## 4. Dialect-Specific Differences
### 4.1 Dialect Discriminator
Every builder and column carries a `dialect` type tag in its `TTypeConfig`:
- **SQLite**: `{ dialect: 'sqlite' }`
- **PG**: `{ dialect: 'pg' }`
- **MySQL**: `{ dialect: 'mysql' }`
This is used in the `BuildColumn` conditional type in `column-builder.d.ts` to route to the correct column class.
### 4.2 SQLite-Specific
- **`integer()` modes**: The `integer()` function accepts `{ mode: 'number' | 'timestamp' | 'timestamp_ms' | 'boolean' }`. Based on mode, it returns different builder classes (`SQLiteIntegerBuilder`, `SQLiteTimestampBuilder`, `SQLiteBooleanBuilder`). This is a compile-time type-level dispatch, not a runtime polymorphic thing.
- **`text()` modes**: `text()` accepts `{ mode: 'text' | 'json' }`. With `mode: 'json'`, it returns `SQLiteTextJsonBuilder` (dataType `'json'`), which has `mapFromDriverValue`/`mapToDriverValue` for JSON serialization.
- **`blob()` modes**: `blob()` accepts `{ mode: 'buffer' | 'json' | 'bigint' }`. Default is `'json'` (not `'buffer'`!).
- **No native boolean/date types**: SQLite uses `integer` with mode overrides instead of dedicated boolean or date column classes.
- **`primaryKey()` on integer**: SQLite's `primaryKey()` on `integer` implies auto-increment (ROWID). The `SQLiteBaseIntegerBuilder` has a `PrimaryKeyConfig` with `autoIncrement?: boolean` and `onConflict`.
- **`generatedAlwaysAs`**: Accepts `{ mode?: 'virtual' | 'stored' }` config for generated columns.
- **Enum handling**: Enums are just `text({ enum: [...] })` — a constraint on `text`, not a separate type.
### 4.3 PostgreSQL-Specific
- **`pgEnum`**: Enums are a top-level declaration, not a column config option. You call `pgEnum('name', ['val1', 'val2'])` at module scope to create a `PgEnum` object, then use it as a column: `myEnum()`. This is fundamentally different from SQLite/MySQL enum handling.
- **Array support**: `PgColumnBuilder` has an `.array(size?)` method that returns a `PgArrayBuilder`. No other dialect has this.
- **`.unique()` with nulls**: PG's `.unique()` accepts `{ nulls: 'distinct' | 'not distinct' }` — a PG-specific option.
- **`timestamp`/`timestamptz`**: `timestamp()` has `{ withTimezone?: boolean, precision?: number, mode?: 'date' | 'string' }`. With `mode: 'string'`, returns `PgTimestampStringBuilder` (dataType `'string'`).
- **`jsonb`**: PG-specific JSON storage type distinct from `json`.
- **`uuid`**: Has `.defaultRandom()` method shortcut for `defaultRANDOM()`.
- **Identity columns**: PG supports `generatedAlwaysAsIdentity()`/`generatedByDefaultAsIdentity()` (not just `generatedAlwaysAs`), using PG sequences.
- **Index operator classes**: `ExtraConfigColumn` on PG columns supports `.asc()`, `.desc()`, `.nullsFirst()`, `.nullsLast()`, `.op(opClass)` for index definitions.
- **Network/geometry types**: `inet`, `cidr`, `macaddr`, `macaddr8`, `point`, `line` — all PG-only.
- **`interval`**: PG-specific date/time interval type.
- **`date()`**: Has `{ mode: 'date' | 'string' }` similar to timestamp.
### 4.4 MySQL-Specific
- **`.autoincrement()`**: MySQL int types inherit from `MySqlColumnBuilderWithAutoIncrement` instead of plain `MySqlColumnBuilder`. This adds an `.autoincrement()` method. `serial()` is shorthand for `int().primaryKey().notNull().default(autoincrement)`.
- **`unsigned`**: MySQL int types (`int`, `smallint`, `mediumint`, `tinyint`, `bigint`) accept `{ unsigned?: boolean }`.
- **`generatedAlwaysAs`**: Accepts `{ mode?: 'virtual' | 'stored' }` like SQLite.
- **Enum handling**: `mysqlEnum(values)` is a standalone column factory (like PG's `pgEnum`), but takes inline values instead of a pre-declared enum object.
- **Text variants**: `tinytext()`, `mediumtext()`, `longtext()` are MySQL-specific shorthands.
- **Datetime types**: `datetime()` is MySQL-specific (distinct from `timestamp`). Both have `{ mode?: 'date' | 'string', fsp?: number }`.
- **`year()`**: MySQL-specific.
- **Binary types**: `binary()`, `varbinary()` with length — MySQL-specific.
- **No array type**: MySQL has no `.array()` method.
### 4.5 Column Builder Inheritance Differences
```
ColumnBuilder (base, drizzle-orm/column-builder.js)
├── SQLiteColumnBuilder (sqlite-core/columns/common.js)
│ ├── SQLiteTextBuilder
│ ├── SQLiteTextJsonBuilder
│ ├── SQLiteBaseIntegerBuilder (adds .primaryKey() with autoIncrement config)
│ │ ├── SQLiteIntegerBuilder
│ │ ├── SQLiteTimestampBuilder
│ │ └── SQLiteBooleanBuilder
│ ├── SQLiteRealBuilder
│ ├── SQLiteNumericBuilder
│ ├── SQLiteBlobBufferBuilder / SQLiteBlobJsonBuilder / SQLiteBigIntBuilder
│ └── SQLiteCustomColumnBuilder
├── PgColumnBuilder (pg-core/columns/common.js, adds .array(), .unique() with nulls)
│ ├── PgTextBuilder, PgVarcharBuilder, PgCharBuilder
│ ├── PgIntegerBuilder, PgSmallIntBuilder, etc.
│ ├── PgBooleanBuilder
│ ├── PgJsonBuilder, PgJsonbBuilder
│ ├── PgUUIDBuilder (adds .defaultRandom())
│ ├── PgEnumColumnBuilder
│ ├── PgDateColumnBaseBuilder (adds .defaultNow())
│ │ ├── PgTimestampBuilder
│ │ └── PgDateBuilder
│ ├── PgNumericBuilder
│ ├── PgCustomColumnBuilder
│ └── ...network/geometry types
└── MySqlColumnBuilder (mysql-core/columns/common.js)
├── MySqlColumnBuilderWithAutoIncrement (adds .autoincrement())
│ ├── MySqlIntBuilder
│ ├── MySqlSmallIntBuilder
│ ├── MySqlSerialBuilder
│ └── ...other int types
├── MySqlTextBuilder
├── MySqlVarCharBuilder
├── MySqlBooleanBuilder
├── MySqlJsonBuilder
├── MySqlEnumColumnBuilder
├── MySqlDateColumnBaseBuilder (adds .defaultNow())
│ ├── MySqlTimestampBuilder
│ ├── MySqlDateTimeBuilder
│ └── MySqlDateBuilder
├── MySqlDecimalBuilder
├── MySqlCustomColumnBuilder
└── ...MySQL-specific types
```
## 5. Table Creation Functions
### Common Pattern
All three dialects use the same general pattern:
```ts
dialectTable(name, columns, extraConfig?)
dialectTable(name, (columnTypes) => columns, extraConfig?)
```
| Dialect | Table Function | Schema Variant |
|---------|-------------------|----------------------------------|
| SQLite | `sqliteTable` | `sqliteTableCreator(fn)` |
| PG | `pgTable` | `pgTableCreator(fn)` |
| MySQL | `mysqlTable` | `mysqlTableCreator(fn)` |
Key differences:
- **PG**: `pgTable` extra config gets `BuildExtraConfigColumns` (columns get `ExtraConfigColumn` with `.asc()`, `.desc()`, `.nullsFirst()`, `.nullsLast()`, `.op()` for index op classes). Also supports `.enableRLS()`.
- **SQLite/MySQL**: Extra config gets `BuildColumns` without the `ExtraConfigColumn` wrapper.
- **SQLite**: Extra config types include `IndexBuilder | CheckBuilder | ForeignKeyBuilder | PrimaryKeyBuilder | UniqueConstraintBuilder`.
- **PG**: Extra config types include `AnyIndexBuilder | CheckBuilder | ForeignKeyBuilder | PrimaryKeyBuilder | UniqueConstraintBuilder | PgPolicy`.
- **MySQL**: Extra config types include `AnyIndexBuilder | CheckBuilder | ForeignKeyBuilder | PrimaryKeyBuilder | UniqueConstraintBuilder`.
The column builder callback receives dialect-specific column builders:
```ts
// SQLite
sqliteTable('users', (t) => ({ id: t.integer().primaryKey() }))
// PG
pgTable('users', (t) => ({ id: t.serial().primaryKey() }))
// MySQL
mysqlTable('users', (t) => ({ id: t.serial() }))
```
## 6. Key Takeaways for a Transform Registry / DbType Approach
### 6.1 The `columnType` String is the Key Discriminator
Each column has a unique `columnType` string (e.g., `'SQLiteText'`, `'PgJsonb'`, `'MySqlInt'`). This is the most reliable way to identify a column's dialect and specific type at runtime. The `dataType` field (`'string' | 'number' | 'boolean' | 'date' | 'json' | 'bigint' | 'array' | 'buffer' | 'custom'`) is shared across dialects but too coarse for schema generation.
### 6.2 The `dataType` Enum is the Universal Type Categories
The `ColumnDataType` union is:
```ts
'string' | 'number' | 'boolean' | 'array' | 'json' | 'date' | 'bigint' | 'custom' | 'buffer'
```
This can serve as a common "logical type" for cross-dialect schema generation, but you need dialect-specific handling for:
- `'string'` columns that are enums (check `enumValues`)
- `'date'` columns (timestamp vs date, timezone handling)
- `'json'` columns (json vs jsonb, mode-based detection)
- `'number'` columns (integer vs float vs decimal semantics)
### 6.3 Enum Handling is Radically Different
| Aspect | SQLite | PG | MySQL |
|----------------|----------------------------------|--------------------------------|--------------------------------|
| Declaration | `text({ enum: [...] })` | `pgEnum()` at module scope | `mysqlEnum(values)` inline |
| Column builder | `SQLiteTextBuilder` | `PgEnumColumnBuilder` | `MySqlEnumColumnBuilder` |
| columnType | `SQLiteText` | `PgEnumColumn` | `MySqlEnumColumn` |
| Storage | TEXT with enum constraint | Native `CREATE TYPE ... AS ENUM`| ENUM column type |
| `enumValues` | Set on builder config | Set via `PgEnum` instance | Set on builder constructor |
For a schema generator, all three surfaces the `enumValues` field on the built column, so you can reliably extract enum values regardless of dialect. But the *declaration* style varies, and PG requires a pre-declared enum type.
### 6.4 Mode-Based Polymorphism
Several column types use a `mode` config to change the TypeScript type at compile time:
| Dialect | Column | Modes | Effect on dataType |
|---------|-------------|------------------------------------------------|--------------------|
| SQLite | `integer()` | `'number'` / `'timestamp'` / `'timestamp_ms'` / `'boolean'` | `number` / `date` / `boolean` |
| SQLite | `text()` | `'text'` / `'json'` | `string` / `json` |
| SQLite | `blob()` | `'buffer'` / `'json'` / `'bigint'` | `buffer` / `json` / `bigint` |
| PG | `timestamp()` | `'date'` / `'string'` | `date` / `string` |
| PG | `date()` | `'date'` / `'string'` | `date` / `string` |
| PG | `time()` | `'string'` (default) / `'date'`? | `string` / `date` |
| MySQL | `timestamp()` | `'date'` / `'string'` | `date` / `string` |
| MySQL | `datetime()` | `'date'` / `'string'` | `date` / `string` |
| MySQL | `date()` | `'date'` / `'string'` | `date` / `string` |
| MySQL | `bigint()` | `'number'` / `'bigint'` | `number` / `bigint`|
The `mode` is **not** stored in the `dataType` field on the column config directly — it's stored in the dialect-specific runtime config. The `dataType` on the built column does reflect the mode correctly (because each mode produces a different builder class with a different `dataType`).
### 6.5 Runtime Config Differences
Every column builder's `config` object extends `ColumnBuilderRuntimeConfig` with dialect-specific fields:
- **SQLite integer**: `{ autoIncrement: boolean }` (on `SQLiteBaseInteger`)
- **SQLite text**: `{ length?: number, enumValues?: string[] }`
- **PG varchar**: `{ length?: number, enumValues?: string[] }`
- **PG timestamp**: `{ withTimezone: boolean, precision?: number }`
- **PG numeric**: `{ precision?: number, scale?: number }`
- **MySQL int**: `{ unsigned?: boolean }` plus `{ autoIncrement: boolean }`
- **MySQL text**: `{ textType: 'tinytext' | 'text' | 'mediumtext' | 'longtext', enumValues?: string[] }`
- **MySQL timestamp/datetime**: `{ fsp?: number }`
### 6.6 Implications for a DbType / Transform Registry
**Recommended approach:**
1. **Use `dataType` as primary classification** — it's the cross-dialect enum that maps naturally to TypeBox schema types.
2. **Use `columnType` for dialect-specific dispatch** — when you need to handle a column differently based on its SQL type (e.g., `PgJsonb` vs `MySqlJson`).
3. **Check `enumValues`** to detect enum columns regardless of how they were declared.
4. **Don't rely on builder class hierarchy** — the builders are compile-time types that don't exist at runtime in a useful way for introspection. Instead, inspect the built column's properties (`dataType`, `columnType`, `enumValues`, runtime config).
5. **Mode detection**: For columns with mode variants, the `dataType` already reflects the mode (e.g., `integer({ mode: 'boolean' })` produces `dataType: 'boolean'`), so you don't need to check mode separately for most schema generation.
6. **Handle PG arrays specially**: PG's `.array()` wraps any column type into an array type with `dataType: 'array'`. The base column type info is preserved in `config.baseBuilder`.
7. **PG identity columns**: PG has `generatedIdentity` on columns (via `generatedAlwaysAsIdentity()`/`generatedByDefaultAsIdentity()`), which is separate from regular defaults. Check `column.generatedIdentity` or `builder._.identity`.
### 6.7 Column Introspection at Runtime
At runtime, all built columns extend `Column` which has these useful properties:
```ts
column.name // Column name
column.dataType // ColumnDataType: 'string' | 'number' | ...
column.columnType // Dialect-specific string: 'PgJsonb', 'SQLiteText', etc.
column.notNull // boolean
column.hasDefault // boolean
column.primary // boolean (isPrimaryKey)
column.default // Default value or SQL
column.enumValues // string[] | undefined
column.isUnique // boolean
column.uniqueName // string | undefined
column.generated // GeneratedColumnConfig | undefined
```
Plus dialect-specific properties accessible via the column class (e.g., `column.withTimezone` on PgTimestamp, `column.mode` on SQLiteTimestamp, etc.).
### 6.8 The `entityKind` Discriminator
Every builder and column class has a static `[entityKind]` string that uniquely identifies the class. This can be used for runtime type checking:
```
'SQLiteTextBuilder' / 'SQLiteText'
'PgTextBuilder' / 'PgText'
'MySqlTextBuilder' / 'MySqlText'
'PgColumnBuilder' (base)
'SQLiteColumnBuilder' (base)
'MySqlColumnBuilder' (base)
// etc.
```
This is set via `entityKind` symbol from `drizzle-orm/entity.js`.
## 7. Summary Table: Cross-Dialect Type Mapping
| Logical Type | SQLite Factory | PG Factory | MySQL Factory | dataType |
|-------------|------------------------|---------------------|------------------------|----------|
| String | `text()` | `text()` / `varchar()` | `text()` / `varchar()` | `string` |
Enum | `text({ enum: [...] })`| `pgEnum()()` | `mysqlEnum()()` | `string` |
JSON | `text({ mode: 'json' })`| `json()` / `jsonb()`| `json()` | `json` |
Number | `integer()` / `real()` | `integer()` / `real()`| `int()` / `float()` | `number` |
BigInt | `blob({ mode: 'bigint' })`| `bigint()` | `bigint()` | `bigint` |
Boolean | `integer({ mode: 'boolean' })`| `boolean()` | `boolean()` / `tinyint()`| `boolean`|
Date | `integer({ mode: 'timestamp' })`| `timestamp()` | `timestamp()` / `datetime()`| `date` |
UUID | `text()` (manual) | `uuid()` | — | `string` |
Decimal | `numeric()` | `numeric()` / `decimal()`| `decimal()` | `string` |
Buffer | `blob({ mode: 'buffer' })`| — | `binary()` / `varbinary()`| `buffer`|
Array | — | `.array()` on any col| — | `array` |