Skip to content

Commit 1d6bbf2

Browse files
committed
feat(builder): extract runCreateTable parseTableMap createTimeTrigger from builder
1 parent 14e89fb commit 1d6bbf2

File tree

3 files changed

+226
-97
lines changed

3 files changed

+226
-97
lines changed

packages/sqlite-builder/src/builder.ts

Lines changed: 91 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { Compilable, CompiledQuery, KyselyPlugin, LogEvent, QueryResult, RawBuilder, Sql, Transaction } from 'kysely'
22
import { Kysely, sql } from 'kysely'
3-
import type { DataTypeExpression } from 'kysely/dist/cjs/parser/data-type-parser'
43
import { SqliteSerializePlugin } from 'kysely-plugin-serialize'
5-
import { isBoolean, isString } from './util'
4+
import { parseTableMap, runCreateTable } from './util'
65
import type { ITable, SqliteBuilderOption, TriggerEvent } from './types'
76

87
const enum DBStatus {
@@ -30,14 +29,7 @@ export class SqliteBuilder<DB extends Record<string, any>> {
3029
this.#status = truncateBeforeInit
3130
? DBStatus.needDrop
3231
: DBStatus.noNeedDrop
33-
this.#tableMap = new Map()
34-
for (const tableName in tables) {
35-
if (!Object.prototype.hasOwnProperty.call(tables, tableName)) {
36-
continue
37-
}
38-
const table = tables[tableName]
39-
this.#tableMap.set(tableName, table)
40-
}
32+
this.#tableMap = parseTableMap(tables)
4133
}
4234

4335
private async createTimeTrigger(
@@ -63,93 +55,95 @@ export class SqliteBuilder<DB extends Record<string, any>> {
6355
}
6456

6557
public async init(dropTableBeforeInit = false): Promise<SqliteBuilder<DB>> {
66-
for (const [tableName, table] of this.#tableMap) {
67-
const { columns: columnList, property: tableProperty } = table
68-
if (dropTableBeforeInit || this.#status === DBStatus.needDrop) {
69-
await this.kysely.schema.dropTable(tableName).ifExists().execute().catch()
70-
}
71-
let tableSql = this.kysely.schema.createTable(tableName)
72-
let _triggerKey = 'rowid'
73-
let _haveAutoKey = false
74-
let _insertColumnName = 'createAt'
75-
let _updateColumnName = 'updateAt'
76-
if (tableProperty?.timestamp && !isBoolean(tableProperty.timestamp)) {
77-
const { create, update } = tableProperty.timestamp as { create?: string; update?: string }
78-
_insertColumnName = create ?? 'createAt'
79-
_updateColumnName = update ?? 'updateAt'
80-
}
81-
for (const columnName in columnList) {
82-
if (!Object.prototype.hasOwnProperty.call(columnList, columnName)) {
83-
continue
84-
}
85-
const columnOption = columnList[columnName]
86-
let dataType: DataTypeExpression
87-
const { type, notNull, defaultTo } = columnOption
88-
switch (type) {
89-
case 'boolean':
90-
case 'date':
91-
case 'object':
92-
case 'string':
93-
dataType = 'text'
94-
break
95-
case 'increments':
96-
_triggerKey = columnName
97-
// eslint-disable-next-line no-fallthrough
98-
case 'number':
99-
dataType = 'integer'
100-
break
101-
case 'blob':
102-
dataType = 'blob'
103-
}
104-
if ([_insertColumnName, _updateColumnName].includes(columnName)) {
105-
continue
106-
}
107-
tableSql = tableSql.addColumn(columnName, dataType, (builder) => {
108-
if (type === 'increments') {
109-
_haveAutoKey = true
110-
return builder.autoIncrement().primaryKey()
111-
}
112-
notNull && (builder = builder.notNull())
113-
defaultTo !== undefined && (builder = builder.defaultTo(defaultTo))
114-
return builder
115-
})
116-
}
117-
if (tableProperty) {
118-
const _primary = tableProperty.primary as string | string[] | undefined
119-
const _unique = tableProperty.unique as string[] | (string[])[] | undefined
120-
if (tableProperty.timestamp) {
121-
if (_insertColumnName) {
122-
tableSql = tableSql.addColumn(_insertColumnName, 'text')
123-
}
124-
if (_updateColumnName) {
125-
tableSql = tableSql.addColumn(_updateColumnName, 'text')
126-
}
127-
}
128-
if (!_haveAutoKey && _primary) {
129-
const is = isString(_primary)
130-
_triggerKey = is ? _primary : _primary[0]
131-
tableSql = tableSql.addPrimaryKeyConstraint(`pk_${is ? _primary : _primary.join('_')}`, (is ? [_primary] : _primary) as any)
132-
}
133-
_unique?.forEach((u: string | string[]) => {
134-
const is = isString(u)
135-
_triggerKey = (!_primary && !_haveAutoKey) ? is ? u : u[0] : _triggerKey
136-
tableSql = tableSql.addUniqueConstraint(`un_${is ? u : u.join('_')}`, (is ? [u] : u) as any)
137-
})
138-
}
139-
await tableSql.ifNotExists().execute()
140-
if (tableProperty?.index) {
141-
for (const i of tableProperty.index) {
142-
const is = isString(i)
143-
let _idx = this.kysely.schema.createIndex(`idx_${is ? i : (i as []).join('_')}`).on(tableName)
144-
_idx = is ? _idx.column(i) : _idx.columns(i as [])
145-
await _idx.ifNotExists().execute()
146-
}
147-
}
148-
if (tableProperty?.timestamp) {
149-
_insertColumnName && await this.createTimeTrigger(tableName, 'insert', _insertColumnName, _triggerKey)
150-
_updateColumnName && await this.createTimeTrigger(tableName, 'update', _updateColumnName, _triggerKey)
151-
}
152-
}
58+
const drop = dropTableBeforeInit || this.#status === DBStatus.needDrop
59+
await runCreateTable(this.kysely, this.#tableMap, drop)
60+
// for (const [tableName, table] of this.#tableMap) {
61+
// const { columns: columnList, property: tableProperty } = table
62+
// if (dropTableBeforeInit || this.#status === DBStatus.needDrop) {
63+
// await this.kysely.schema.dropTable(tableName).ifExists().execute().catch()
64+
// }
65+
// let tableSql = this.kysely.schema.createTable(tableName)
66+
// let _triggerKey = 'rowid'
67+
// let _haveAutoKey = false
68+
// let _insertColumnName = 'createAt'
69+
// let _updateColumnName = 'updateAt'
70+
// if (tableProperty?.timestamp && !isBoolean(tableProperty.timestamp)) {
71+
// const { create, update } = tableProperty.timestamp as { create?: string; update?: string }
72+
// _insertColumnName = create ?? 'createAt'
73+
// _updateColumnName = update ?? 'updateAt'
74+
// }
75+
// for (const columnName in columnList) {
76+
// if (!Object.prototype.hasOwnProperty.call(columnList, columnName)) {
77+
// continue
78+
// }
79+
// const columnOption = columnList[columnName]
80+
// let dataType: DataTypeExpression
81+
// const { type, notNull, defaultTo } = columnOption
82+
// switch (type) {
83+
// case 'boolean':
84+
// case 'date':
85+
// case 'object':
86+
// case 'string':
87+
// dataType = 'text'
88+
// break
89+
// case 'increments':
90+
// _triggerKey = columnName
91+
// // eslint-disable-next-line no-fallthrough
92+
// case 'number':
93+
// dataType = 'integer'
94+
// break
95+
// case 'blob':
96+
// dataType = 'blob'
97+
// }
98+
// if ([_insertColumnName, _updateColumnName].includes(columnName)) {
99+
// continue
100+
// }
101+
// tableSql = tableSql.addColumn(columnName, dataType, (builder) => {
102+
// if (type === 'increments') {
103+
// _haveAutoKey = true
104+
// return builder.autoIncrement().primaryKey()
105+
// }
106+
// notNull && (builder = builder.notNull())
107+
// defaultTo !== undefined && (builder = builder.defaultTo(defaultTo))
108+
// return builder
109+
// })
110+
// }
111+
// if (tableProperty) {
112+
// const _primary = tableProperty.primary as string | string[] | undefined
113+
// const _unique = tableProperty.unique as string[] | (string[])[] | undefined
114+
// if (tableProperty.timestamp) {
115+
// if (_insertColumnName) {
116+
// tableSql = tableSql.addColumn(_insertColumnName, 'text')
117+
// }
118+
// if (_updateColumnName) {
119+
// tableSql = tableSql.addColumn(_updateColumnName, 'text')
120+
// }
121+
// }
122+
// if (!_haveAutoKey && _primary) {
123+
// const is = isString(_primary)
124+
// _triggerKey = is ? _primary : _primary[0]
125+
// tableSql = tableSql.addPrimaryKeyConstraint(`pk_${is ? _primary : _primary.join('_')}`, (is ? [_primary] : _primary) as any)
126+
// }
127+
// _unique?.forEach((u: string | string[]) => {
128+
// const is = isString(u)
129+
// _triggerKey = (!_primary && !_haveAutoKey) ? is ? u : u[0] : _triggerKey
130+
// tableSql = tableSql.addUniqueConstraint(`un_${is ? u : u.join('_')}`, (is ? [u] : u) as any)
131+
// })
132+
// }
133+
// await tableSql.ifNotExists().execute()
134+
// if (tableProperty?.index) {
135+
// for (const i of tableProperty.index) {
136+
// const is = isString(i)
137+
// let _idx = this.kysely.schema.createIndex(`idx_${is ? i : (i as []).join('_')}`).on(tableName)
138+
// _idx = is ? _idx.column(i) : _idx.columns(i as [])
139+
// await _idx.ifNotExists().execute()
140+
// }
141+
// }
142+
// if (tableProperty?.timestamp) {
143+
// _insertColumnName && await this.createTimeTrigger(tableName, 'insert', _insertColumnName, _triggerKey)
144+
// _updateColumnName && await this.createTimeTrigger(tableName, 'update', _updateColumnName, _triggerKey)
145+
// }
146+
// }
153147
this.#status = DBStatus.ready
154148
return this
155149
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './builder'
22
export * from './types'
3+
export { createTimeTrigger, parseTableMap, runCreateTable } from './util'
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,140 @@
1+
import type { Kysely } from 'kysely'
2+
import { sql } from 'kysely'
3+
import type { DataTypeExpression } from 'kysely/dist/cjs/parser/data-type-parser'
4+
import type { ITable, Tables, TriggerEvent } from './types'
5+
16
export function isString(value: any): value is string {
27
return typeof value === 'string'
38
}
49
export function isBoolean(value: any): value is boolean {
510
return typeof value === 'boolean'
611
}
12+
13+
export async function createTimeTrigger<T>(
14+
kysely: Kysely<T>,
15+
table: keyof T,
16+
event: TriggerEvent,
17+
column: string,
18+
key = 'rowid',
19+
): Promise<void> {
20+
// datetime('now') will return UTC Time
21+
await sql`
22+
create trigger if not exists ${sql.raw(table as string)}_${sql.raw(column)}
23+
after ${sql.raw(event)}
24+
on ${sql.table(table as string)}
25+
begin
26+
update ${sql.table(table as string)}
27+
set ${sql.ref(column)} = datetime('now','localtime')
28+
where ${sql.ref(key)} = NEW.${sql.ref(key)};
29+
end
30+
`.execute(kysely).catch((err) => {
31+
console.error(err)
32+
return undefined
33+
})
34+
}
35+
36+
export function parseTableMap<T>(tables: Tables<T>): Map<string, ITable<T[Extract<keyof T, string>]>> {
37+
const map = new Map()
38+
for (const tableName in tables) {
39+
if (!Object.prototype.hasOwnProperty.call(tables, tableName)) {
40+
continue
41+
}
42+
const table = tables[tableName]
43+
map.set(tableName, table)
44+
}
45+
return map
46+
}
47+
48+
export async function runCreateTable<T>(
49+
kysely: Kysely<T>,
50+
tableMap: Map<string, ITable<T[Extract<keyof T, string>]>>,
51+
dropTableBeforeInit = false,
52+
) {
53+
for (const [tableName, table] of tableMap) {
54+
const { columns: columnList, property: tableProperty } = table
55+
if (dropTableBeforeInit) {
56+
await kysely.schema.dropTable(tableName).ifExists().execute().catch()
57+
}
58+
let tableSql = kysely.schema.createTable(tableName)
59+
let _triggerKey = 'rowid'
60+
let _haveAutoKey = false
61+
let _insertColumnName = 'createAt'
62+
let _updateColumnName = 'updateAt'
63+
if (tableProperty?.timestamp && !isBoolean(tableProperty.timestamp)) {
64+
const { create, update } = tableProperty.timestamp as { create?: string; update?: string }
65+
_insertColumnName = create ?? 'createAt'
66+
_updateColumnName = update ?? 'updateAt'
67+
}
68+
for (const columnName in columnList) {
69+
if (!Object.prototype.hasOwnProperty.call(columnList, columnName)) {
70+
continue
71+
}
72+
const columnOption = columnList[columnName]
73+
let dataType: DataTypeExpression = 'text'
74+
const { type, notNull, defaultTo } = columnOption
75+
switch (type) {
76+
case 'boolean':
77+
case 'date':
78+
case 'object':
79+
case 'string':
80+
dataType = 'text'
81+
break
82+
case 'increments':
83+
_triggerKey = columnName
84+
// eslint-disable-next-line no-fallthrough
85+
case 'number':
86+
dataType = 'integer'
87+
break
88+
case 'blob':
89+
dataType = 'blob'
90+
}
91+
if ([_insertColumnName, _updateColumnName].includes(columnName)) {
92+
continue
93+
}
94+
tableSql = tableSql.addColumn(columnName, dataType, (builder) => {
95+
if (type === 'increments') {
96+
_haveAutoKey = true
97+
return builder.autoIncrement().primaryKey()
98+
}
99+
notNull && (builder = builder.notNull())
100+
defaultTo !== undefined && (builder = builder.defaultTo(defaultTo))
101+
return builder
102+
})
103+
}
104+
if (tableProperty) {
105+
const _primary = tableProperty.primary as string | string[] | undefined
106+
const _unique = tableProperty.unique as string[] | (string[])[] | undefined
107+
if (tableProperty.timestamp) {
108+
if (_insertColumnName) {
109+
tableSql = tableSql.addColumn(_insertColumnName, 'text')
110+
}
111+
if (_updateColumnName) {
112+
tableSql = tableSql.addColumn(_updateColumnName, 'text')
113+
}
114+
}
115+
if (!_haveAutoKey && _primary) {
116+
const is = isString(_primary)
117+
_triggerKey = is ? _primary : _primary[0]
118+
tableSql = tableSql.addPrimaryKeyConstraint(`pk_${is ? _primary : _primary.join('_')}`, (is ? [_primary] : _primary) as any)
119+
}
120+
_unique?.forEach((u: string | string[]) => {
121+
const is = isString(u)
122+
_triggerKey = (!_primary && !_haveAutoKey) ? is ? u : u[0] : _triggerKey
123+
tableSql = tableSql.addUniqueConstraint(`un_${is ? u : u.join('_')}`, (is ? [u] : u) as any)
124+
})
125+
}
126+
await tableSql.ifNotExists().execute()
127+
if (tableProperty?.index) {
128+
for (const i of tableProperty.index) {
129+
const is = isString(i)
130+
let _idx = kysely.schema.createIndex(`idx_${is ? i : (i as []).join('_')}`).on(tableName)
131+
_idx = is ? _idx.column(i) : _idx.columns(i as [])
132+
await _idx.ifNotExists().execute()
133+
}
134+
}
135+
if (tableProperty?.timestamp) {
136+
_insertColumnName && await createTimeTrigger(kysely, tableName as keyof T, 'insert', _insertColumnName, _triggerKey)
137+
_updateColumnName && await createTimeTrigger(kysely, tableName as keyof T, 'update', _updateColumnName, _triggerKey)
138+
}
139+
}
140+
}

0 commit comments

Comments
 (0)