Relevant source files
The following files were used as context for generating this wiki page:
- drizzle-kit/src/introspect-singlestore.ts
- drizzle-kit/src/introspect-mysql.ts
- drizzle-kit/src/cli/commands/introspect.ts
- drizzle-kit/src/api.ts
- drizzle-kit/src/cli/schema.ts
- drizzle-kit/src/serializer/singlestoreSerializer.ts
- drizzle-kit/src/serializer/gelSerializer.ts
- drizzle-kit/src/serializer/studio.ts
- drizzle-kit/src/introspect-pg.ts
- drizzle-kit/src/cli/commands/sqliteIntrospect.ts
- drizzle-kit/src/introspect-gel.ts
- drizzle-kit/src/serializer/mysqlSerializer.ts
- drizzle-kit/src/introspect-sqlite.ts
- drizzle-kit/src/serializer/pgSerializer.ts
- drizzle-kit/src/cli/commands/mysqlIntrospect.ts
- drizzle-kit/src/sqlgenerator.ts
- drizzle-kit/src/cli/commands/singlestoreIntrospect.ts
- drizzle-kit/src/serializer/sqliteSerializer.ts
- drizzle-kit/src/serializer/mysqlSchema.ts
- integration-tests/drizzle2/planetscale/meta/0000_snapshot.json
- integration-tests/drizzle2/mysql/meta/0000_snapshot.json
- integration-tests/drizzle2/singlestore/meta/0000_snapshot.json
- drizzle-kit/src/cli/commands/pgIntrospect.ts
- integration-tests/drizzle2/pg/meta/0000_snapshot.json
- integration-tests/drizzle2/mysql-proxy/first/meta/0000_snapshot.json
Overview
Database introspection in Drizzle Kit recovers relational database schemas from live database instances across multiple supported dialects, normalizing catalog metadata into structured JSON migration snapshots and translating them into idiomatic Drizzle TypeScript schema definitions.
Sources: drizzle-kit/src/cli/schema.ts:538-623, drizzle-kit/src/serializer/singlestoreSerializer.ts:400-411, and drizzle-kit/src/serializer/mysqlSerializer.ts:564-573
CLI Introspection Orchestration
Dialect Dispatch and Driver Checks
The CLI command handler for introspection ensures output directories are created recursively via mkdirSync(out, { recursive: true }) and evaluates dialect branches to dynamically import and invoke dialect-specific introspection routines. For PostgreSQL credentials carrying a specific driver option, version compatibility checks against drizzle-orm are enforced before proceeding.
| Dialect | Credential Type | Introspection Function | Target Module File |
|---|---|---|---|
postgresql | PostgresCredentials | introspectPostgres | drizzle-kit/src/cli/commands/introspect.ts |
mysql | MysqlCredentials | introspectMysql | drizzle-kit/src/cli/commands/introspect.ts |
sqlite | SqliteCredentials | introspectSqlite | drizzle-kit/src/cli/commands/introspect.ts |
turso | LibSQLCredentials | introspectLibSQL | drizzle-kit/src/cli/commands/introspect.ts |
singlestore | SingleStoreCredentials | introspectSingleStore | drizzle-kit/src/cli/commands/introspect.ts |
gel | GelCredentials | introspectGel | drizzle-kit/src/cli/commands/introspect.ts |
Warning
When executing PostgreSQL introspection with the aws-data-api driver, drizzle-orm version 0.30.10 or higher is strictly required; using pglite requires version 0.30.6 or higher. Failing this check terminates the process with exit code 1.
Sources: drizzle-kit/src/cli/schema.ts:524-625
Filtering and Progress Reporting
Table filtering is implemented across database dialects by mapping each provided table filter string into a Minimatch instance. The filter function evaluates table names against all matchers, taking negation patterns into account and enforcing that all match criteria pass via flags.every(Boolean).
const matchers = tablesFilter.map((it) => {
return new Minimatch(it);
});
const filter = (tableName: string) => {
if (matchers.length === 0) return true;
let flags: boolean[] = [];
for (let matcher of matchers) {
if (matcher.negate) {
if (!matcher.match(tableName)) {
flags.push(false);
}
}
if (matcher.match(tableName)) {
flags.push(true);
}
}
if (flags.length > 0) {
return flags.every(Boolean);
}
return false;
};Progress reporting runs concurrently with database extraction by instantiating an IntrospectProgress reporter and wrapping the database fetch task with renderWithTask, where status updates propagate stage, count, and status arguments down to progress.update(stage, count, status).
Sources: drizzle-kit/src/cli/commands/introspect.ts:184-234, drizzle-kit/src/cli/commands/introspect.ts:318-351
Snapshot Diffing and Output Generation
Once schema objects are retrieved from the database and converted into TypeScript files (schema.ts and relations.ts), migration folders are inspected via prepareOutFolder(out, dialect). If no existing snapshots are detected (snapshots.length === 0), dialect-specific snapshot diff functions compute SQL migration statements and metadata.
const { snapshots, journal } = prepareOutFolder(out, 'mysql');
if (snapshots.length === 0) {
const { sqlStatements, _meta } = await applyMysqlSnapshotsDiff(
squashMysqlScheme(dryMySql),
squashMysqlScheme(schema),
tablesResolver,
columnsResolver,
mySqlViewsResolver,
dryMySql,
schema,
);
writeResult({
cur: schema,
sqlStatements,
journal,
_meta,
outFolder: out,
breakpoints,
type: 'introspect',
prefixMode: prefix,
});
} else {
render(
`[${
chalk.blue(
'i',
)
}] No SQL generated, you already have migrations in project`,
);
}Note
If snapshot files already exist inside the project output folder, introspection skips generating new migration SQL and outputs an informational message indicating that migrations are already present.
Sources: drizzle-kit/src/cli/commands/introspect.ts:364-395
PostgreSQL Catalog Inspection
PostgreSQL Introspection and Push Execution
PostgreSQL schema introspection is driven through pgPushIntrospect, which processes user-supplied table filter strings into Minimatch instances to evaluate and filter table names against positive and negative match criteria.
const matchers = filters.map((it) => {
return new Minimatch(it);
});
const filter = (tableName: string) => {
if (matchers.length === 0) return true;
let flags: boolean[] = [];
for (let matcher of matchers) {
if (matcher.negate) {
if (!matcher.match(tableName)) {
flags.push(false);
}
}
if (matcher.match(tableName)) {
flags.push(true);
}
}
if (flags.length > 0) {
return flags.every(Boolean);
}
return false;
};Sources: drizzle-kit/src/cli/commands/pgIntrospect.ts:17-42
The introspection execution wraps schema pulling within a terminal progress view using renderWithTask before appending internal identifiers to construct the final serialized PgSchema payload.
const progress = new ProgressView(
'Pulling schema from database...',
'Pulling schema from database...',
);
const res = await renderWithTask(
progress,
fromDatabase(db, filter, schemaFilters, entities, undefined, tsSchema),
);
const schema = { id: originUUID, prevId: '', ...res } as PgSchema;
const { internal, ...schemaWithoutInternals } = schema;
return { schema: schemaWithoutInternals };Sources: drizzle-kit/src/cli/commands/pgIntrospect.ts:43-54
Catalog Transformation and TypeScript Generation
Catalog reconstruction inspects database entities, mapping identities, sequences, columns, roles, and schema definitions into corresponding TypeScript constructs. When reconstructing column identities, parameters such as maxValue, cache, and cycle are serialized into .generatedAlwaysAsIdentity() or .generatedByDefaultAsIdentity() calls.
if (identity?.maxValue) {
paramsObj += `, maxValue: ${identity.maxValue}`;
}
if (identity?.cache) {
paramsObj += `, cache: ${identity.cache}`;
}
if (identity?.cycle) {
paramsObj += `, cycle: true`;
}
paramsObj += ' }';
if (identity?.type === 'always') {
return `.generatedAlwaysAsIdentity(${paramsObj})`;
}
return `.generatedByDefaultAsIdentity(${paramsObj})`;Sources: drizzle-kit/src/introspect-pg.ts:289-302
Note
Parameter name generation uses paramNameFor, appending a capitalised schema suffix when a non-public schema is present to avoid name collisions across distinct PostgreSQL schemas.
Sources: drizzle-kit/src/introspect-pg.ts:304-307
MySQL and SingleStore Schema Recovery
Introspection Commands and Table Filtering
MySQL and SingleStore schema introspection processes database metadata by executing database pulls through mysqlPushIntrospect and singlestorePushIntrospect. Both routines accept database connection handles, target database names, and filter arrays to construct Minimatch instances that selectively include or exclude tables based on pattern matching rules and negated match flags.
const matchers = filters.map((it) => {
return new Minimatch(it);
});
const filter = (tableName: string) => {
if (matchers.length === 0) return true;
let flags: boolean[] = [];
for (let matcher of matchers) {
if (matcher.negate) {
if (!matcher.match(tableName)) {
flags.push(false);
}
}
if (matcher.match(tableName)) {
flags.push(true);
}
}
if (flags.length > 0) {
return flags.every(Boolean);
}
return false;
};Sources: drizzle-kit/src/cli/commands/mysqlIntrospect.ts:14-39, drizzle-kit/src/cli/commands/singlestoreIntrospect.ts:14-39
Note
During push introspection, the ProgressView utility renders a terminal task indicator labeled 'Pulling schema from database...' while fromDatabase queries catalog tables.
Sources: drizzle-kit/src/cli/commands/mysqlIntrospect.ts:41-48, drizzle-kit/src/cli/commands/singlestoreIntrospect.ts:41-48
Casing Transformation and Schema Squashing
Field names and identifiers undergo casing transformation determined by the active casing configuration. The prepareCasing helper checks whether casing is set to 'preserve' or 'camel', routing column keys through escapeColumnKey or string transformation methods.
const escapeColumnKey = (value: string) => {
if (/^(?![a-zA-Z_$][a-zA-Z0-9_$]*$).+$/.test(value)) {
return `"${value}"`;
}
return value;
};
const prepareCasing = (casing?: Casing) => (value: string) => {
if (casing === 'preserve') {
return escapeColumnKey(value);
}
if (casing === 'camel') {
return escapeColumnKey(value.camelCase());
}
assertUnreachable(casing);
};Sources: drizzle-kit/src/introspect-mysql.ts:104-120, drizzle-kit/src/introspect-singlestore.ts:110-126
Catalog serialization and migration diffing rely on squasher functions defined in mysqlSchema.ts. The squasher flattens indices, foreign keys, primary keys, unique constraints, check constraints, and views into delimited string representations for snapshot comparison.
| Squasher Method | Serialized Components | Source Reference |
|---|---|---|
squashIdx | name, columns, isUnique, using, algorithm, lock | drizzle-kit/src/serializer/mysqlSchema.ts:223-228 |
squashPK | name, columns | drizzle-kit/src/serializer/mysqlSchema.ts:241-243 |
squashUnique | name, columns | drizzle-kit/src/serializer/mysqlSchema.ts:248-250 |
squashFK | name, tableFrom, columnsFrom, tableTo, columnsTo, onUpdate, onDelete | drizzle-kit/src/serializer/mysqlSchema.ts:255-259 |
squashCheck | name, value | drizzle-kit/src/serializer/mysqlSchema.ts:282-284 |
squashView | algorithm, sqlSecurity, withCheckOption | drizzle-kit/src/serializer/mysqlSchema.ts:290-292 |
Sources: drizzle-kit/src/serializer/mysqlSchema.ts:223-292
Warning
In MySQL schemas, text and tinytext columns cannot have default values assigned; introspection guards against unsupported defaults when mapping column properties.
Sources: drizzle-kit/src/introspect-mysql.ts:602-609
SQLite Catalog Recovery
Table Filtering and Introspection Execution
The SQLite introspection pipeline connects to live SQLite and LibSQL databases using configured credentials, applying glob table filters via Minimatch before delegating schema serialization to fromDatabase. The sqliteIntrospect and sqlitePushIntrospect functions process filter matchers supporting negation rules, evaluating table names through an array accumulator and returning boolean conditions.
export const sqliteIntrospect = async (
credentials: SqliteCredentials,
filters: string[],
casing: Casing,
) => {
const { connectToSQLite } = await import('../connections');
const db = await connectToSQLite(credentials);
const matchers = filters.map((it) => {
return new Minimatch(it);
});
const filter = (tableName: string) => {
if (matchers.length === 0) return true;
let flags: boolean[] = [];
for (let matcher of matchers) {
if (matcher.negate) {
if (!matcher.match(tableName)) {
flags.push(false);
}
}
if (matcher.match(tableName)) {
flags.push(true);
}
}
if (flags.length > 0) {
return flags.every(Boolean);
}
return false;
};
const progress = new IntrospectProgress();
const res = await renderWithTask(
progress,
fromDatabase(db, filter, (stage, count, status) => {
progress.update(stage, count, status);
}),
);
const schema = { id: originUUID, prevId: '', ...res } as SQLiteSchema;
const ts = schemaToTypeScript(schema, casing);
return { schema, ts };
};Sources: drizzle-kit/src/cli/commands/sqliteIntrospect.ts:12-58
Note
When executing sqlitePushIntrospect, the CLI renders a ProgressView terminal task instead of interactive progress bars while querying catalog tables from the target database instance.
Sources: drizzle-kit/src/cli/commands/sqliteIntrospect.ts:88-92
Column Mapping and Default Value Parsing
Recovered SQLite column types map database definitions to TypeScript declarations while processing default values, column flags, and self-referential or cyclic foreign key constraints. The mapColumnDefault utility handles SQL expressions enclosed in parentheses, stringified NULL constants, and quoted string defaults by unescaping interior quotes.
const mapColumnDefault = (defaultValue: any) => {
if (
typeof defaultValue === 'string'
&& defaultValue.startsWith('(')
&& defaultValue.endsWith(')')
) {
return `sql\`${defaultValue}\``;
}
if (defaultValue === 'NULL') {
return `sql\`NULL\``;
}
if (
typeof defaultValue === 'string'
) {
return defaultValue.substring(1, defaultValue.length - 1).replaceAll('"', '\\"').replaceAll("''", "'");
}
return defaultValue;
};Sources: drizzle-kit/src/introspect-sqlite.ts:258-278
Warning
SQLite stores literal null values as the string 'NULL' or wrapped representations in system catalogs; introspection catches these specifically to emit explicit sql\NULL`` expressions.
Sources: drizzle-kit/src/introspect-sqlite.ts:266-269
Foreign key relationships are analyzed for self-referential structures and cyclic dependencies by checking key pairs against registered relation sets.
| Relation Helper | Condition Evaluated | Source Reference |
|---|---|---|
isCyclic | Checks whether both tableFrom-tableTo and reverse tableTo-tableFrom exist in relations | drizzle-kit/src/introspect-sqlite.ts:248-252 |
isSelf | Verifies if tableFrom matches tableTo on the foreign key definition | drizzle-kit/src/introspect-sqlite.ts:254-256 |
Sources: drizzle-kit/src/introspect-sqlite.ts:248-256
Gel Schema Extraction
Default Value Mapping Rules
Gel introspection maps database types, array flags, and default expression constraints into structured TypeScript declarations Migration Toolkit Schema. The mapDefault function inspects column metadata, handling type-specific formatting for integers, floating-point numbers, UUIDs, dates, durations, JSON, and byte arrays.
const isCyclic = (fk: ForeignKey) => {
const key = `${fk.tableFrom}-${fk.tableTo}`;
const reverse = `${fk.tableTo}-${fk.tableFrom}`;
return relations.has(key) && relations.has(reverse);
};
const isSelf = (fk: ForeignKey) => {
return fk.tableFrom === fk.tableTo;
};Sources: drizzle-kit/src/introspect-gel.ts:522-530
Array defaults undergo sanitization through buildArrayDefault, checking whether the default value string begins with ARRAY[ or _nullif_array_nulls(ARRAY[ before wrapping expressions in SQL template literals.
const buildArrayDefault = (defaultValue: string, typeName: string): string => {
if (
typeof defaultValue === 'string'
&& !(defaultValue.startsWith('_nullif_array_nulls(ARRAY[') || defaultValue.startsWith('ARRAY['))
) {
return `sql\`${defaultValue}\``;
}
const regex = /ARRAY\[(.*)\]/;
const match = defaultValue.match(regex);
if (!match) {
return `sql\`${defaultValue}\``;
}
defaultValue = match[1];
return `sql\`[${defaultValue}]\``;
};Sources: drizzle-kit/src/introspect-gel.ts:532-549
Note
Gel column mapping distinguishes between standard SQL types and prefixed extension types such as edgedbt.bigint_t, edgedbt.date_duration_t, edgedbt.timestamptz_t, edgedbt.duration_t, edgedbt.date_t, and edgedbt.relative_duration_t.
Sources: drizzle-kit/src/introspect-gel.ts:596-673
| Data Type Prefix / Condition | Default Evaluation Behavior | Source Reference |
|---|---|---|
name === 'id' | Returns .default(sql\uuid_generate_v4()`)` | drizzle-kit/src/introspect-gel.ts:564-566 |
isArray | Delegates to buildArrayDefault when defaultValue is defined | drizzle-kit/src/introspect-gel.ts:568-570 |
edgedbt.bigint_t | Wraps parsed column default inside BigInt(...) | drizzle-kit/src/introspect-gel.ts:596-600 |
uuid (gen_random_uuid()) | Emits .defaultRandom() | drizzle-kit/src/introspect-gel.ts:618-620 |
edgedbt.timestamptz_t (now()) | Emits .defaultNow() | drizzle-kit/src/introspect-gel.ts:637-639 |
edgedbt.date_t (now()) | Emits .defaultNow() | drizzle-kit/src/introspect-gel.ts:661-663 |
Sources: drizzle-kit/src/introspect-gel.ts:564-663
Warning
Integer, smallint, and bigint default values strip enclosing parentheses via .replaceAll('(', '').replaceAll(')', '') before being passed to mapColumnDefault.
Sources: drizzle-kit/src/introspect-gel.ts:578-594
TypeScript Declaration Code Generation
Schema to TypeScript Mapping and Imports
The code generation phase inspects recovered schema structures and aggregates column types, constraints, and relational entities to emit standard Drizzle ORM TypeScript declarations. Across PostgreSQL, MySQL, SingleStore, SQLite, and Gel introspectors, builders verify dependency names and compile distinct import lists. For instance, SingleStore and MySQL generation evaluate table column types, composite primary keys, unique constraints, and indexes to construct the required core importsMigration Toolkit Schema. Casing configurations (preserve or camel) determine whether identifiers undergo key escaping via prepareCasing and escapeColumnKey.
const singlestoreImportsList = new Set([
'singlestoreTable',
'singlestoreEnum',
'bigint',
'binary',
'boolean',
'char',
'date',
'datetime',
'decimal',
'double',
'float',
'int',
'json',
// TODO: add new type BSON
// TODO: add new type Blob
// TODO: add new type UUID
// TODO: add new type GUID
// TODO: add new type Vector
// TODO: add new type GeoPoint
'mediumint',
'real',
'serial',
'smallint',
'text',
'tinytext',
'mediumtext',
'longtext',
'time',
'timestamp',
'tinyint',
'varbinary',
'varchar',
'vector',
'year',
'enum',
]);Sources: drizzle-kit/src/introspect-singlestore.ts:19-55
Note
When table names or column identifiers collide with reserved import tokens or require safe property syntax, escapeColumnKey wraps them in double quotes if they fail valid identifier regex checks.
Sources: drizzle-kit/src/introspect-singlestore.ts:110-115, drizzle-kit/src/introspect-mysql.ts:104-109
Column Mapping and Default Value Formatting
Column generators translate raw data types and constraint flags into chainable Drizzle builder methods. Unsigned integer modifiers, auto-increment configurations, and default expressions are formatted through dialect-specific helper functions such as mapColumnDefault, mapColumnDefaultForJson, and binaryConfig.
const mapColumnDefault = (defaultValue: any, isExpression?: boolean) => {
if (isExpression) {
return `sql\`${defaultValue}\``;
}
return defaultValue;
};
const mapColumnDefaultForJson = (defaultValue: any) => {
if (
typeof defaultValue === 'string'
&& defaultValue.startsWith("('")
&& defaultValue.endsWith("')")
) {
return defaultValue.substring(2, defaultValue.length - 2);
}
return defaultValue;
};Sources: drizzle-kit/src/introspect-singlestore.ts:341-359
| Column Type / Dialect Helper | Behavior and Transformation | Source Reference |
|---|---|---|
serial | Emits serial(...) without explicit type parameters | drizzle-kit/src/introspect-singlestore.ts:376-378 |
int (Unsigned) | Appends { unsigned: true } configuration object if lowered type includes unsigned | drizzle-kit/src/introspect-singlestore.ts:380-388 |
| Expression Defaults | Wraps expressions inside sql\...`viamapColumnDefault` | drizzle-kit/src/introspect-singlestore.ts:341-347 |
| JSON Defaults | Strips wrapping parentheses and quotes from JSON default strings via mapColumnDefaultForJson | drizzle-kit/src/introspect-singlestore.ts:349-359 |
Sources: drizzle-kit/src/introspect-singlestore.ts:341-388
Warning
Cyclic foreign key relationships between tables trigger the inclusion of utility type imports like AnyPgColumn or specialized constraint checks to prevent circular type inference failures in generated schemas.
Sources: drizzle-kit/src/introspect-pg.ts:333-335, drizzle-kit/src/introspect-mysql.ts:156-160
Related
On This Page
OverviewCLI Introspection OrchestrationDialect Dispatch and Driver ChecksFiltering and Progress ReportingSnapshot Diffing and Output GenerationPostgreSQL Catalog InspectionPostgreSQL Introspection and Push ExecutionCatalog Transformation and TypeScript GenerationMySQL and SingleStore Schema RecoveryIntrospection Commands and Table FilteringCasing Transformation and Schema SquashingSQLite Catalog RecoveryTable Filtering and Introspection ExecutionColumn Mapping and Default Value ParsingGel Schema ExtractionDefault Value Mapping RulesTypeScript Declaration Code GenerationSchema to TypeScript Mapping and ImportsColumn Mapping and Default Value FormattingRelated