Skip to content

Commit 8a99a96

Browse files
committed
feat: primary key checks and fix migration
1 parent cf2d34a commit 8a99a96

File tree

3 files changed

+37
-24
lines changed

3 files changed

+37
-24
lines changed

src/schema/core.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
} from './parse-exist'
2222
import {
2323
addColumn,
24+
asArray,
2425
createIndex,
2526
createTableWithIndexAndTrigger,
2627
createTimeTrigger,
@@ -324,6 +325,7 @@ function updateTable(
324325
isChanged
325326
|| isPrimaryKeyChanged(existTable.primary, targetTable.primary || autoIncrementColumn)
326327
|| isUniqueChanged(existTable.unique, targetTable.unique)
328+
|| targetTable.withoutRowId
327329
) {
328330
return migrateWholeTable(trx, tableName, updateColumnList, targetTable)
329331
}
@@ -349,7 +351,7 @@ function updateTable(
349351
result.splice(0, 0, dropTrigger(existTrigger))
350352
}
351353
if (updateTimeColumn) {
352-
result.push(createTimeTrigger(tableName, updateTimeColumn)!)
354+
result.push(createTimeTrigger(tableName, updateTimeColumn, asArray(targetTable.primary)[0] || autoIncrementColumn)!)
353355
}
354356
}
355357

src/schema/run.ts

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,24 @@ export function parseColumnType(type: DataTypeValue): [type: ParsedColumnType, i
2929
return [dataType, isIncrements]
3030
}
3131

32+
export function asArray<T>(arr: Arrayable<T>): T[] {
33+
return Array.isArray(arr) ? arr : [arr]
34+
}
35+
3236
/**
3337
* Merge array with `_` and prepend `_`
3438
*
3539
* Return merged string and parsed array
3640
*/
37-
function parseArray(arr: Arrayable<any>): [columnListStr: string, key: string] {
38-
const columns = Array.isArray(arr) ? arr : [arr]
41+
function parseArray(arr: Arrayable<any>): [columnListStr: string, key: string, first: string] {
42+
const columns = asArray(arr)
3943
let key = ''
4044
let columnList = ''
4145
for (const c of columns) {
4246
key += `_${c}`
4347
columnList += `"${c}",`
4448
}
45-
return [columnList.slice(0, -1), key]
49+
return [columnList.slice(0, -1), key, columns[0]]
4650
}
4751

4852
/**
@@ -79,9 +83,9 @@ export function createTableWithIndexAndTrigger(
7983
): string[] {
8084
const { index, ...props } = table
8185
const result: string[] = []
82-
const { updateColumn, sql } = createTable(trx, tableName, props)
86+
const [sql, updateColumn, triggerColumn] = createTable(trx, tableName, props)
8387
result.push(sql, ...createTableIndex(tableName, index))
84-
const triggerSql = createTimeTrigger(tableName, updateColumn)
88+
const triggerSql = createTimeTrigger(tableName, updateColumn, triggerColumn)
8589
if (triggerSql) {
8690
result.push(triggerSql)
8791
}
@@ -102,9 +106,9 @@ export function createTable(
102106
trx: Kysely<any> | Transaction<any>,
103107
tableName: string,
104108
{ columns, primary, unique, withoutRowId }: Omit<Table, 'index'>,
105-
): { updateColumn?: string, sql: string } {
109+
): [sql: string, updateColumn?: string, triggerColumn?: string] {
106110
let updateColumn
107-
let autoIncrementColumn
111+
let triggerColumn
108112

109113
const columnList: string[] = []
110114

@@ -114,10 +118,13 @@ export function createTable(
114118
const [dataType, isIncrements] = parseColumnType(type)
115119

116120
if (isIncrements) {
117-
if (autoIncrementColumn) {
118-
throw new Error(`Multiple AUTOINCREMENT columns (${autoIncrementColumn}, ${columnName}) in table ${tableName}`)
121+
if (withoutRowId) {
122+
throw new Error(`Cannot setup AUTOINCREMENT column "${columnName}" in table "${tableName}" without rowid `)
119123
}
120-
autoIncrementColumn = columnName
124+
if (triggerColumn) {
125+
throw new Error(`Multiple AUTOINCREMENT columns (${triggerColumn}, ${columnName}) in table ${tableName}`)
126+
}
127+
triggerColumn = columnName
121128
columnList.push(`"${columnName}" ${dataType} PRIMARY KEY AUTOINCREMENT`)
122129
} else {
123130
// update trigger column is not null
@@ -131,12 +138,15 @@ export function createTable(
131138

132139
// primary/unique key is jointable, so can not be set as trigger key
133140
if (primary) {
134-
const [targetColumns, key] = parseArray(primary)
135-
if (!autoIncrementColumn) {
141+
const [targetColumns, key, first] = parseArray(primary)
142+
if (!triggerColumn) {
136143
columnList.push(`PRIMARY KEY (${targetColumns})`)
137-
} else if (autoIncrementColumn !== key.substring(1)) {
138-
throw new Error(`Exists AUTOINCREMENT column "${autoIncrementColumn}" in table "${tableName}", cannot setup extra primary key (${targetColumns})`)
144+
triggerColumn = first
145+
} else if (triggerColumn !== key.substring(1)) {
146+
throw new Error(`Exists AUTOINCREMENT column "${triggerColumn}" in table "${tableName}", cannot setup extra primary key (${targetColumns})`)
139147
}
148+
} else if (withoutRowId) {
149+
throw new Error(`No primary key in table "${tableName}" and "withoutRowId" setup`)
140150
}
141151

142152
if (unique) {
@@ -147,18 +157,19 @@ export function createTable(
147157

148158
const rowIdClause = withoutRowId ? ' WITHOUT ROWID' : ''
149159

150-
return {
151-
sql: `CREATE TABLE IF NOT EXISTS "${tableName}" (${columnList})${rowIdClause};`,
160+
return [
161+
`CREATE TABLE IF NOT EXISTS "${tableName}" (${columnList})${rowIdClause};`,
152162
updateColumn,
153-
}
163+
triggerColumn || 'rowid',
164+
]
154165
}
155166

156-
export function createTimeTrigger(tableName: string, updateColumn?: string): string | undefined {
157-
if (!updateColumn) {
167+
export function createTimeTrigger(tableName: string, updateColumn: string | undefined, triggerColumn: string | undefined): string | undefined {
168+
if (!updateColumn || !triggerColumn) {
158169
return
159170
}
160171
const triggerName = `tgr_${tableName}_${updateColumn}`
161-
return `CREATE TRIGGER IF NOT EXISTS "${triggerName}" AFTER UPDATE ON "${tableName}" BEGIN UPDATE "${tableName}" SET "${updateColumn}" = CURRENT_TIMESTAMP WHERE "rowid" = NEW."rowid"; END;`
172+
return `CREATE TRIGGER IF NOT EXISTS "${triggerName}" AFTER UPDATE ON "${tableName}" BEGIN UPDATE "${tableName}" SET "${updateColumn}" = CURRENT_TIMESTAMP WHERE "${triggerColumn}" = NEW."${triggerColumn}"; END;`
162173
}
163174

164175
export function renameTable(tableName: string, newTableName: string): string {
@@ -213,7 +224,7 @@ export function migrateWholeTable(
213224
const tempTableName = `_temp_${tableName}`
214225

215226
// 1. create target table with temp name
216-
const { updateColumn, sql } = createTable(trx, tempTableName, targetTable)
227+
const [sql, updateColumn, triggerColumn] = createTable(trx, tempTableName, targetTable)
217228
result.push(sql)
218229

219230
// 2. diff and restore data from source table to target table
@@ -235,7 +246,7 @@ export function migrateWholeTable(
235246

236247
// 5. restore indexes and triggers
237248
result.push(...createTableIndex(tableName, targetTable.index))
238-
const triggerSql = createTimeTrigger(tableName, updateColumn)
249+
const triggerSql = createTimeTrigger(tableName, updateColumn, triggerColumn)
239250
if (triggerSql) {
240251
result.push(triggerSql)
241252
}

tests/sync-sql.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe('test sync sql', async () => {
3434
'CREATE TABLE IF NOT EXISTS "test" ("id" INTEGER PRIMARY KEY AUTOINCREMENT,"person" TEXT DEFAULT \'{"name":"test"}\',"gender" INTEGER NOT NULL,"createAt" TEXT DEFAULT CURRENT_TIMESTAMP,"updateAt" TEXT DEFAULT CURRENT_TIMESTAMP);',
3535
'CREATE INDEX IF NOT EXISTS idx_test_person on "test" ("person");',
3636
'CREATE INDEX IF NOT EXISTS idx_test_id_gender on "test" ("id","gender");',
37-
'CREATE TRIGGER IF NOT EXISTS "tgr_test_updateAt" AFTER UPDATE ON "test" BEGIN UPDATE "test" SET "updateAt" = CURRENT_TIMESTAMP WHERE "rowid" = NEW."rowid"; END;',
37+
'CREATE TRIGGER IF NOT EXISTS "tgr_test_updateAt" AFTER UPDATE ON "test" BEGIN UPDATE "test" SET "updateAt" = CURRENT_TIMESTAMP WHERE "id" = NEW."id"; END;',
3838
],
3939
)
4040

0 commit comments

Comments
 (0)