-
Notifications
You must be signed in to change notification settings - Fork 280
/
sqlite-introspector.ts
99 lines (87 loc) · 2.58 KB
/
sqlite-introspector.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import {
DatabaseIntrospector,
DatabaseMetadata,
DatabaseMetadataOptions,
SchemaMetadata,
TableMetadata,
} from '../database-introspector.js'
import { Kysely } from '../../kysely.js'
import {
DEFAULT_MIGRATION_LOCK_TABLE,
DEFAULT_MIGRATION_TABLE,
} from '../../migration/migrator.js'
import { sql } from '../../raw-builder/sql.js'
export class SqliteIntrospector implements DatabaseIntrospector {
readonly #db: Kysely<any>
constructor(db: Kysely<any>) {
this.#db = db
}
async getSchemas(): Promise<SchemaMetadata[]> {
// Sqlite doesn't support schemas.
return []
}
async getTables(
options: DatabaseMetadataOptions = { withInternalKyselyTables: false }
): Promise<TableMetadata[]> {
let query = this.#db
.selectFrom('sqlite_schema')
.where('type', '=', 'table')
.where('name', 'not like', 'sqlite_%')
.select('name')
.orderBy('name')
.castTo<{ name: string }>()
if (!options.withInternalKyselyTables) {
query = query
.where('name', '!=', DEFAULT_MIGRATION_TABLE)
.where('name', '!=', DEFAULT_MIGRATION_LOCK_TABLE)
}
const tables = await query.execute()
return Promise.all(tables.map(({ name }) => this.#getTableMetadata(name)))
}
async getMetadata(
options?: DatabaseMetadataOptions
): Promise<DatabaseMetadata> {
return {
tables: await this.getTables(options),
}
}
async #getTableMetadata(table: string): Promise<TableMetadata> {
const db = this.#db
// Get the SQL that was used to create the table.
const createSql = await db
.selectFrom('sqlite_master')
.where('name', '=', table)
.select('sql')
.castTo<{ sql: string | undefined }>()
.execute()
// Try to find the name of the column that has `autoincrement` 🤦
const autoIncrementCol = createSql[0]?.sql
?.split(/[\(\),]/)
?.find((it) => it.toLowerCase().includes('autoincrement'))
?.trimStart()
?.split(/\s+/)?.[0]
?.replace(/["`]/g, '')
const columns = await db
.selectFrom(
sql<{
name: string
type: string
notnull: 0 | 1
dflt_value: any
}>`pragma_table_info(${table})`.as('table_info')
)
.select(['name', 'type', 'notnull', 'dflt_value'])
.orderBy('name')
.execute()
return {
name: table,
columns: columns.map((col) => ({
name: col.name,
dataType: col.type,
isNullable: !col.notnull,
isAutoIncrementing: col.name === autoIncrementCol,
hasDefaultValue: col.dflt_value != null,
})),
}
}
}