forj 0.0.11 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/migrations/blueprint.ts +46 -33
- package/src/migrations/builder.ts +54 -39
- package/src/migrations/column.ts +12 -2
- package/src/migrations/schema.ts +31 -19
- package/src/migrations/types.ts +1 -0
- package/src/utils.ts +45 -0
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Column from './column'
|
|
2
2
|
import ForeignKey from './foreign-key'
|
|
3
|
+
import { tableName } from '../utils'
|
|
3
4
|
import type {
|
|
4
5
|
ColumnDefinition, IndexDefinition, ForeignKeyDefinition,
|
|
5
6
|
} from './types'
|
|
@@ -13,7 +14,7 @@ export class Blueprint {
|
|
|
13
14
|
#renameColumns: Map<string, string> = new Map()
|
|
14
15
|
|
|
15
16
|
constructor(table: string) {
|
|
16
|
-
this.#table = table
|
|
17
|
+
this.#table = tableName(table)
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
#column(definition: ColumnDefinition) {
|
|
@@ -22,96 +23,108 @@ export class Blueprint {
|
|
|
22
23
|
return new Column(column)
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
id(name: string = 'id') {
|
|
26
|
-
return this.#column({ name, type: 'INTEGER',
|
|
26
|
+
id(name: string = 'id') {
|
|
27
|
+
return this.#column({ name, type: 'INTEGER', primary: true, nullable: true })
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
string(name: string, length: number =
|
|
30
|
-
return this.#column({ name, type: 'VARCHAR', length
|
|
30
|
+
string(name: string, length: number = 0) {
|
|
31
|
+
return this.#column({ name, type: 'VARCHAR', length })
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
text(name: string) {
|
|
34
|
-
return this.#column({ name, type: 'TEXT'
|
|
35
|
+
return this.#column({ name, type: 'TEXT' })
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
int(name: string) {
|
|
38
|
-
return this.#column({ name, type: 'INTEGER'
|
|
39
|
+
return this.#column({ name, type: 'INTEGER' })
|
|
39
40
|
}
|
|
40
41
|
integer(name: string) {
|
|
41
42
|
return this.int(name)
|
|
42
43
|
}
|
|
43
44
|
real(name: string) {
|
|
44
|
-
return this.#column({ name, type: 'REAL'
|
|
45
|
+
return this.#column({ name, type: 'REAL' })
|
|
45
46
|
}
|
|
46
47
|
numeric(name: string) {
|
|
47
|
-
return this.#column({ name, type: 'NUMERIC'
|
|
48
|
+
return this.#column({ name, type: 'NUMERIC' })
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
// bigInteger(name: string) {
|
|
51
|
-
// return this.#column({ name, type: 'BIGINT'
|
|
52
|
+
// return this.#column({ name, type: 'BIGINT' })
|
|
52
53
|
// }
|
|
53
54
|
|
|
54
55
|
// tinyInteger(name: string) {
|
|
55
|
-
// return this.#column({ name, type: 'TINYINT'
|
|
56
|
+
// return this.#column({ name, type: 'TINYINT' })
|
|
56
57
|
// }
|
|
57
58
|
|
|
58
59
|
boolean(name: string) {
|
|
59
|
-
return this.#column({ name, type: 'INTEGER'
|
|
60
|
+
return this.#column({ name, type: 'INTEGER' })
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
// decimal(name: string, precision: number = 8, scale: number = 2) {
|
|
63
|
-
// return this.#column({ name, type: `DECIMAL(${precision},${scale})
|
|
64
|
+
// return this.#column({ name, type: `DECIMAL(${precision},${scale})` })
|
|
64
65
|
// }
|
|
65
66
|
|
|
66
67
|
// float(name: string) {
|
|
67
|
-
// return this.#column({ name, type: 'FLOAT'
|
|
68
|
+
// return this.#column({ name, type: 'FLOAT' })
|
|
68
69
|
// }
|
|
69
70
|
|
|
70
71
|
// double(name: string) {
|
|
71
|
-
// return this.#column({ name, type: 'DOUBLE'
|
|
72
|
+
// return this.#column({ name, type: 'DOUBLE' })
|
|
72
73
|
// }
|
|
73
74
|
|
|
74
75
|
// date(name: string) {
|
|
75
|
-
// return this.#column({ name, type: 'DATE'
|
|
76
|
+
// return this.#column({ name, type: 'DATE' })
|
|
76
77
|
// }
|
|
77
78
|
|
|
78
79
|
// dateTime(name: string) {
|
|
79
|
-
// return this.#column({ name, type: 'DATETIME'
|
|
80
|
+
// return this.#column({ name, type: 'DATETIME' })
|
|
80
81
|
// }
|
|
81
82
|
|
|
82
83
|
// timestamp(name: string) {
|
|
83
|
-
// return this.#column({ name, type: 'TIMESTAMP'
|
|
84
|
+
// return this.#column({ name, type: 'TIMESTAMP' })
|
|
84
85
|
// }
|
|
85
86
|
|
|
86
87
|
// time(name: string) {
|
|
87
|
-
// return this.#column({ name, type: 'TIME'
|
|
88
|
+
// return this.#column({ name, type: 'TIME' })
|
|
88
89
|
// }
|
|
89
90
|
|
|
90
|
-
json(name: string) {
|
|
91
|
-
|
|
92
|
-
}
|
|
91
|
+
// json(name: string) {
|
|
92
|
+
// return this.#column({ name, type: 'JSON' })
|
|
93
|
+
// }
|
|
93
94
|
|
|
94
|
-
enum(name: string, values: string[]) {
|
|
95
|
-
return this.#column({
|
|
95
|
+
enum(name: string, values: string[] | number[]) {
|
|
96
|
+
return this.#column({
|
|
97
|
+
name,
|
|
98
|
+
type: values.every(v => typeof v == 'string') ? 'TEXT' : (values.every(v => Number.isInteger(v)) ? 'INTEGER' : 'REAL'),
|
|
99
|
+
// TODO:
|
|
100
|
+
// Checking floating-point numbers can be problematic due to the precision of REAL numbers
|
|
101
|
+
// SQLite might store 0.5 as 0.4999999 or 0.5000001, causing the check to fail
|
|
102
|
+
// Maybe works:
|
|
103
|
+
// rate NUMERIC(3,1) NOT NULL DEFAULT 0 CHECK(
|
|
104
|
+
// rate IN (0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5)
|
|
105
|
+
// )
|
|
106
|
+
raw: `CHECK(${name} IN (${values.map(v => typeof v == 'string' ? `'${v.replace(/'/g, "\\'")}'` : v).join(', ')}))`,
|
|
107
|
+
})
|
|
96
108
|
}
|
|
97
109
|
|
|
98
110
|
blob(name: string) {
|
|
99
|
-
return this.#column({ name, type: 'BLOB'
|
|
111
|
+
return this.#column({ name, type: 'BLOB' })
|
|
100
112
|
}
|
|
101
113
|
|
|
102
|
-
timestamps() {
|
|
103
|
-
|
|
104
|
-
|
|
114
|
+
timestamps(columnType: 'int' | 'date' = 'int') {
|
|
115
|
+
const isInt = columnType == 'int'
|
|
116
|
+
const type = isInt ? 'INTEGER' : 'DATETIME'
|
|
117
|
+
this.#column({ name: 'created_at', type, raw: 'DEFAULT '+ (isInt ? '(unixepoch())' : 'CURRENT_TIMESTAMP') })
|
|
118
|
+
this.#column({ name: 'updated_at', type, nullable: true })
|
|
105
119
|
return this
|
|
106
120
|
}
|
|
107
121
|
|
|
108
|
-
softDelete(name: string = 'deleted_at') {
|
|
109
|
-
this.#column({ name, type: '
|
|
122
|
+
softDelete(columnType: 'int' | 'date' = 'int', name: string = 'deleted_at') {
|
|
123
|
+
this.#column({ name, type: columnType == 'int' ? 'INTEGER' : 'DATETIME', nullable: true })
|
|
110
124
|
return this
|
|
111
125
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return this.softDelete(name)
|
|
126
|
+
softDeletes(columnType: 'int' | 'date' = 'int', name: string = 'deleted_at') {
|
|
127
|
+
return this.softDelete(columnType, name)
|
|
115
128
|
}
|
|
116
129
|
|
|
117
130
|
foreign(column: string) {
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { Blueprint } from './blueprint'
|
|
2
|
+
import { tableName, tableSlug } from '../utils'
|
|
2
3
|
import type { ColumnDefinition, IndexDefinition, ForeignKeyDefinition } from './types'
|
|
3
4
|
|
|
4
5
|
export default class SchemaBuilder {
|
|
5
|
-
static create(blueprint: Blueprint): string {
|
|
6
|
-
const
|
|
6
|
+
static create(blueprint: Blueprint, exist: boolean = false): string {
|
|
7
|
+
const table = blueprint.table
|
|
7
8
|
const columns = blueprint.columns
|
|
8
9
|
const indexes = blueprint.indexes
|
|
9
10
|
const foreignKeys = blueprint.foreignKeys
|
|
10
11
|
|
|
11
12
|
const columnDefinitions = columns.map(col => this.#column(col))
|
|
12
|
-
const indexDefinitions = indexes.map(idx => this.#index(idx,
|
|
13
|
+
const indexDefinitions = indexes.map(idx => this.#index(idx, table))
|
|
13
14
|
const foreignKeyDefinitions = foreignKeys.map(fk => this.#foreignKey(fk))
|
|
14
15
|
|
|
15
16
|
const allDefinitions = [
|
|
@@ -18,31 +19,31 @@ export default class SchemaBuilder {
|
|
|
18
19
|
...foreignKeyDefinitions
|
|
19
20
|
].filter(Boolean)
|
|
20
21
|
|
|
21
|
-
return `CREATE TABLE ${
|
|
22
|
+
return `CREATE TABLE ${exist ? 'IF NOT EXISTS ' : ''}${table} (\n ${allDefinitions.join(',\n ')}\n);`
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
static alter(blueprint: Blueprint): string[] {
|
|
25
|
-
const
|
|
26
|
+
const table = blueprint.table
|
|
26
27
|
const statements: string[] = []
|
|
27
28
|
|
|
28
29
|
const columns = blueprint.columns
|
|
29
30
|
if (columns.length > 0)
|
|
30
|
-
columns.forEach(col => statements.push(`ALTER TABLE ${
|
|
31
|
+
columns.forEach(col => statements.push(`ALTER TABLE ${table} ADD COLUMN ${this.#column(col)};`))
|
|
31
32
|
|
|
32
33
|
const dropColumns = blueprint.dropColumns
|
|
33
|
-
dropColumns.forEach(col => statements.push(`ALTER TABLE ${
|
|
34
|
+
dropColumns.forEach(col => statements.push(`ALTER TABLE ${table} DROP COLUMN ${col};`))
|
|
34
35
|
|
|
35
36
|
const renameColumns = blueprint.renameColumns
|
|
36
|
-
renameColumns.forEach((newName, oldName) => statements.push(`ALTER TABLE ${
|
|
37
|
+
renameColumns.forEach((newName, oldName) => statements.push(`ALTER TABLE ${table} RENAME COLUMN ${oldName} TO ${newName};`))
|
|
37
38
|
|
|
38
39
|
const indexes = blueprint.indexes
|
|
39
40
|
indexes.forEach(idx => {
|
|
40
|
-
const indexSql = this.#indexStatement(idx,
|
|
41
|
+
const indexSql = this.#indexStatement(idx, table)
|
|
41
42
|
if (indexSql) statements.push(indexSql)
|
|
42
43
|
})
|
|
43
44
|
|
|
44
45
|
const foreignKeys = blueprint.foreignKeys
|
|
45
|
-
foreignKeys.forEach(fk => statements.push(`ALTER TABLE ${
|
|
46
|
+
foreignKeys.forEach(fk => statements.push(`ALTER TABLE ${table} ADD ${this.#foreignKey(fk)};`))
|
|
46
47
|
|
|
47
48
|
return statements
|
|
48
49
|
}
|
|
@@ -62,36 +63,41 @@ export default class SchemaBuilder {
|
|
|
62
63
|
// if (column.unsigned)
|
|
63
64
|
// sql += ' UNSIGNED'
|
|
64
65
|
|
|
65
|
-
if (column.nullable)
|
|
66
|
-
sql += ' NULL'
|
|
67
|
-
} else {
|
|
66
|
+
if (!column.nullable)
|
|
68
67
|
sql += ' NOT NULL'
|
|
69
|
-
}
|
|
70
68
|
|
|
71
69
|
if (column.default !== undefined) {
|
|
70
|
+
sql += ' DEFAULT '
|
|
72
71
|
if (column.default === null) {
|
|
73
|
-
sql += '
|
|
74
|
-
} else if (typeof column.default == 'string') {
|
|
75
|
-
sql += ` DEFAULT '${column.default}'`
|
|
76
|
-
} else if (typeof column.default == 'boolean') {
|
|
77
|
-
sql += ` DEFAULT ${column.default ? 1 : 0}`
|
|
72
|
+
sql += 'NULL'
|
|
78
73
|
} else {
|
|
79
|
-
|
|
74
|
+
const type = typeof column.default
|
|
75
|
+
if (type == 'string') {
|
|
76
|
+
sql += `'${column.default.replace(/'/g, "\\'")}'`
|
|
77
|
+
} else if (type == 'boolean') {
|
|
78
|
+
sql += Number(column.default)
|
|
79
|
+
} else {
|
|
80
|
+
sql += `${column.default}`.replace(/''/g, "'\\'")
|
|
81
|
+
}
|
|
80
82
|
}
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
if (column.unique)
|
|
84
86
|
sql += ' UNIQUE'
|
|
85
87
|
|
|
88
|
+
if (column.raw)
|
|
89
|
+
sql += ' '+ column.raw
|
|
90
|
+
|
|
86
91
|
// if (column.comment)
|
|
87
92
|
// sql += ` COMMENT '${column.comment.replace(/'/g, "''")}'`
|
|
88
93
|
|
|
89
94
|
return sql
|
|
90
95
|
}
|
|
91
96
|
|
|
92
|
-
static #index(index: IndexDefinition,
|
|
93
|
-
const indexName = index.name ||
|
|
97
|
+
static #index(index: IndexDefinition, table: string): string {
|
|
98
|
+
const indexName = index.name || tableSlug(table) +`_${index.columns.join('_')}_${index.type}`
|
|
94
99
|
const columns = index.columns.join(', ')
|
|
100
|
+
table = tableName(table)
|
|
95
101
|
|
|
96
102
|
switch (index.type) {
|
|
97
103
|
case 'primary':
|
|
@@ -105,17 +111,18 @@ export default class SchemaBuilder {
|
|
|
105
111
|
}
|
|
106
112
|
}
|
|
107
113
|
|
|
108
|
-
static #indexStatement(index: IndexDefinition,
|
|
109
|
-
const indexName = index.name ||
|
|
114
|
+
static #indexStatement(index: IndexDefinition, table: string): string {
|
|
115
|
+
const indexName = index.name || tableSlug(table) +`_${index.columns.join('_')}_${index.type}`
|
|
110
116
|
const columns = index.columns.join(', ')
|
|
117
|
+
table = tableName(table)
|
|
111
118
|
|
|
112
119
|
switch (index.type) {
|
|
113
120
|
case 'primary':
|
|
114
|
-
return `ALTER TABLE ${
|
|
121
|
+
return `ALTER TABLE ${table} ADD PRIMARY KEY (${columns});`
|
|
115
122
|
case 'unique':
|
|
116
|
-
return `CREATE UNIQUE INDEX ${indexName} ON ${
|
|
123
|
+
return `CREATE UNIQUE INDEX ${indexName} ON ${table} (${columns});`
|
|
117
124
|
case 'index':
|
|
118
|
-
return `CREATE INDEX ${indexName} ON ${
|
|
125
|
+
return `CREATE INDEX ${indexName} ON ${table} (${columns});`
|
|
119
126
|
default:
|
|
120
127
|
return ''
|
|
121
128
|
}
|
|
@@ -133,35 +140,43 @@ export default class SchemaBuilder {
|
|
|
133
140
|
return sql
|
|
134
141
|
}
|
|
135
142
|
|
|
136
|
-
static drop(
|
|
137
|
-
return `DROP TABLE ${tableName};`
|
|
143
|
+
static drop(table: string, exist: boolean = false) {
|
|
144
|
+
return `DROP TABLE ${exist ? 'IF EXISTS ' : ''}${tableName(table)};`
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static dropIfExists(table: string) {
|
|
148
|
+
return this.drop(table, true)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static dropView(view: string, exist: boolean = false) {
|
|
152
|
+
return `DROP VIEW ${exist ? 'IF EXISTS ' : ''}[${tableName(view)}];`
|
|
138
153
|
}
|
|
139
154
|
|
|
140
|
-
static
|
|
141
|
-
return
|
|
155
|
+
static dropViewIfExists(view: string) {
|
|
156
|
+
return this.dropView(view, true)
|
|
142
157
|
}
|
|
143
158
|
|
|
144
159
|
static rename(from: string, to: string) {
|
|
145
|
-
return `ALTER TABLE ${from} RENAME TO ${to};`
|
|
160
|
+
return `ALTER TABLE ${tableName(from)} RENAME TO ${tableName(to)};`
|
|
146
161
|
}
|
|
147
162
|
|
|
148
|
-
static hasTable(
|
|
149
|
-
return `SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName}';`
|
|
163
|
+
static hasTable(table: string) {
|
|
164
|
+
return `SELECT name FROM sqlite_master WHERE type='table' AND name='${tableName(table)}';`
|
|
150
165
|
}
|
|
151
166
|
|
|
152
|
-
static hasColumn(
|
|
153
|
-
return `PRAGMA table_info(${tableName});`
|
|
167
|
+
static hasColumn(table: string, columnName: string) { // TODO refactor..
|
|
168
|
+
return `PRAGMA table_info(${tableName(table)});`
|
|
154
169
|
}
|
|
155
170
|
|
|
156
171
|
static getAllTables() {
|
|
157
172
|
return `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';`
|
|
158
173
|
}
|
|
159
174
|
|
|
160
|
-
static getColumns(
|
|
161
|
-
return `PRAGMA table_info(${tableName});`
|
|
175
|
+
static getColumns(table: string): string {
|
|
176
|
+
return `PRAGMA table_info(${tableName(table)});`
|
|
162
177
|
}
|
|
163
178
|
|
|
164
179
|
static dropAllTables(tables: string[]) {
|
|
165
|
-
return tables.map(table => `DROP TABLE IF EXISTS ${table};`)
|
|
180
|
+
return tables.map(table => `DROP TABLE IF EXISTS ${tableName(table)};`)
|
|
166
181
|
}
|
|
167
182
|
}
|
package/src/migrations/column.ts
CHANGED
|
@@ -13,8 +13,18 @@ export default class Column {
|
|
|
13
13
|
return this
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
default(
|
|
17
|
-
this.column.default =
|
|
16
|
+
default(val: any) {
|
|
17
|
+
this.column.default = val
|
|
18
|
+
return this
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
defaultRaw(val: string) {
|
|
22
|
+
this.column.raw = 'DEFAULT '+ val
|
|
23
|
+
return this
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
raw(raw: string) {
|
|
27
|
+
this.column.raw = raw
|
|
18
28
|
return this
|
|
19
29
|
}
|
|
20
30
|
|
package/src/migrations/schema.ts
CHANGED
|
@@ -41,30 +41,42 @@ export class Schema {
|
|
|
41
41
|
// this.#addStatement(...sql)
|
|
42
42
|
// }
|
|
43
43
|
|
|
44
|
-
static async create(
|
|
45
|
-
const blueprint = new Blueprint(
|
|
44
|
+
static async create(table: string, fn: BlueprintFn, exist: boolean = false): Promise<void> {
|
|
45
|
+
const blueprint = new Blueprint(table)
|
|
46
46
|
fn(blueprint)
|
|
47
|
-
await this.#addStatement(Builder.create(blueprint))
|
|
47
|
+
await this.#addStatement(Builder.create(blueprint, exist))
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
static async table
|
|
51
|
-
|
|
50
|
+
static async createIfNotExists(table: string, fn: BlueprintFn): Promise<void> {
|
|
51
|
+
this.create(table, fn, true)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
static async table(table: string, fn: BlueprintFn): Promise<void> {
|
|
55
|
+
const blueprint = new Blueprint(table)
|
|
52
56
|
fn(blueprint)
|
|
53
57
|
await this.#addStatement(Builder.alter(blueprint))
|
|
54
58
|
}
|
|
55
59
|
|
|
56
|
-
static async drop(
|
|
57
|
-
await this.#addStatement(Builder.drop(
|
|
60
|
+
static async drop(table: string): Promise<void> {
|
|
61
|
+
await this.#addStatement(Builder.drop(table))
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
static async dropIfExists(
|
|
61
|
-
await this.#addStatement(Builder.dropIfExists(
|
|
64
|
+
static async dropIfExists(table: string): Promise<void> {
|
|
65
|
+
await this.#addStatement(Builder.dropIfExists(table))
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
static async rename(from: string, to: string): Promise<void> {
|
|
65
69
|
await this.#addStatement(Builder.rename(from, to))
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
static async dropView(view: string): Promise<void> {
|
|
73
|
+
await this.#addStatement(Builder.dropView(view))
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static async dropViewIfExists(view: string): Promise<void> {
|
|
77
|
+
await this.#addStatement(Builder.dropViewIfExists(view))
|
|
78
|
+
}
|
|
79
|
+
|
|
68
80
|
static async disableForeignKeyConstraints(): Promise<void> {
|
|
69
81
|
this.#foreignKeyConstraintsEnabled = false
|
|
70
82
|
await this.#addStatement('PRAGMA foreign_keys = OFF;')
|
|
@@ -92,29 +104,29 @@ export class Schema {
|
|
|
92
104
|
// await this.#addStatement(sql)
|
|
93
105
|
// }
|
|
94
106
|
|
|
95
|
-
// static async hasTable(
|
|
107
|
+
// static async hasTable(table: string): Promise<boolean> {
|
|
96
108
|
// if (!this.#c)
|
|
97
109
|
// throw new Error('Database connection not set')
|
|
98
110
|
|
|
99
|
-
// const sql = Builder.hasTable(
|
|
111
|
+
// const sql = Builder.hasTable(table)
|
|
100
112
|
// const result = await this.#c.query(sql)
|
|
101
113
|
// return result.length > 0
|
|
102
114
|
// }
|
|
103
115
|
|
|
104
|
-
// static async hasColumn(
|
|
116
|
+
// static async hasColumn(table: string, columnName: string): Promise<boolean> {
|
|
105
117
|
// if (!this.#c)
|
|
106
118
|
// throw new Error('Database connection not set')
|
|
107
119
|
|
|
108
|
-
// const sql = Builder.hasColumn(
|
|
120
|
+
// const sql = Builder.hasColumn(table, columnName)
|
|
109
121
|
// const result = await this.#c.query(sql)
|
|
110
122
|
// return result.some((row: any) => row.name === columnName)
|
|
111
123
|
// }
|
|
112
124
|
|
|
113
|
-
// static async hasColumns(
|
|
125
|
+
// static async hasColumns(table: string, ...columnNames: string[]): Promise<boolean> {
|
|
114
126
|
// if (!this.#c)
|
|
115
127
|
// throw new Error('Database connection not set')
|
|
116
128
|
|
|
117
|
-
// const sql = Builder.hasColumn(
|
|
129
|
+
// const sql = Builder.hasColumn(table, '')
|
|
118
130
|
// const result = await this.#c.query(sql)
|
|
119
131
|
// const existingColumns = result.map((row: any) => row.name)
|
|
120
132
|
|
|
@@ -130,20 +142,20 @@ export class Schema {
|
|
|
130
142
|
// return result.map((row: any) => row.name)
|
|
131
143
|
// }
|
|
132
144
|
|
|
133
|
-
// static async getColumns(
|
|
145
|
+
// static async getColumns(table: string): Promise<any[]> {
|
|
134
146
|
// if (!this.#c)
|
|
135
147
|
// throw new Error('Database connection not set')
|
|
136
148
|
|
|
137
|
-
// const sql = Builder.getColumns(
|
|
149
|
+
// const sql = Builder.getColumns(table)
|
|
138
150
|
// return await this.#c.query(sql)
|
|
139
151
|
// }
|
|
140
152
|
|
|
141
|
-
// static async getColumnType(
|
|
153
|
+
// static async getColumnType(table: string, columnName: string): Promise<string | null> {
|
|
142
154
|
// if (!this.#c) {
|
|
143
155
|
// throw new Error('Database connection not set')
|
|
144
156
|
// }
|
|
145
157
|
|
|
146
|
-
// const sql = Builder.getColumns(
|
|
158
|
+
// const sql = Builder.getColumns(table)
|
|
147
159
|
// const result = await this.#c.query(sql)
|
|
148
160
|
// const column = result.find((row: any) => row.name === columnName)
|
|
149
161
|
// return column ? column.type : null
|
package/src/migrations/types.ts
CHANGED
package/src/utils.ts
CHANGED
|
@@ -132,3 +132,48 @@ export function isJoinCompare(val: any, schema?: DBSchema) {
|
|
|
132
132
|
const keys = zGet(val, schema)
|
|
133
133
|
return keys && keys?.length
|
|
134
134
|
}
|
|
135
|
+
|
|
136
|
+
const reservedWords = new Set([
|
|
137
|
+
'ABORT', 'ACTION', 'ADD', 'AFTER', 'ALL', 'ALTER', 'ANALYZE', 'AND', 'AS',
|
|
138
|
+
'ASC', 'ATTACH', 'AUTOINCREMENT', 'BEFORE', 'BEGIN', 'BETWEEN', 'BY',
|
|
139
|
+
'CASCADE', 'CASE', 'CAST', 'CHECK', 'COLLATE', 'COLUMN', 'COMMIT',
|
|
140
|
+
'CONFLICT', 'CONSTRAINT', 'CREATE', 'CROSS', 'CURRENT', 'CURRENT_DATE',
|
|
141
|
+
'CURRENT_TIME', 'CURRENT_TIMESTAMP', 'DATABASE', 'DEFAULT', 'DEFERRABLE',
|
|
142
|
+
'DEFERRED', 'DELETE', 'DESC', 'DETACH', 'DISTINCT', 'DROP', 'EACH',
|
|
143
|
+
'ELSE', 'END', 'ESCAPE', 'EXCEPT', 'EXCLUSIVE', 'EXISTS', 'EXPLAIN',
|
|
144
|
+
'FAIL', 'FOR', 'FOREIGN', 'FROM', 'FULL', 'GLOB', 'GROUP', 'HAVING',
|
|
145
|
+
'IF', 'IGNORE', 'IMMEDIATE', 'IN', 'INDEX', 'INDEXED', 'INITIALLY',
|
|
146
|
+
'INNER', 'INSERT', 'INSTEAD', 'INTERSECT', 'INTO', 'IS', 'ISNULL',
|
|
147
|
+
'JOIN', 'KEY', 'LEFT', 'LIKE', 'LIMIT', 'MATCH', 'NATURAL', 'NO',
|
|
148
|
+
'NOT', 'NOTNULL', 'NULL', 'OF', 'OFFSET', 'ON', 'OR', 'ORDER',
|
|
149
|
+
'OUTER', 'PLAN', 'PRAGMA', 'PRIMARY', 'QUERY', 'RAISE', 'RECURSIVE',
|
|
150
|
+
'REFERENCES', 'REGEXP', 'REINDEX', 'RELEASE', 'RENAME', 'REPLACE',
|
|
151
|
+
'RESTRICT', 'RIGHT', 'ROLLBACK', 'ROW', 'SAVEPOINT', 'SELECT',
|
|
152
|
+
'SET', 'TABLE', 'TEMP', 'TEMPORARY', 'THEN', 'TO', 'TRANSACTION',
|
|
153
|
+
'TRIGGER', 'UNION', 'UNIQUE', 'UPDATE', 'USING', 'VACUUM', 'VALUES',
|
|
154
|
+
'VIEW', 'VIRTUAL', 'WHEN', 'WHERE', 'WITH', 'WITHOUT',
|
|
155
|
+
])
|
|
156
|
+
export function tableName(name: string) {
|
|
157
|
+
name = name.trim()
|
|
158
|
+
if (!name || name?.includes('.'))
|
|
159
|
+
throw new Error(`Invalid table name "${!name ? 'empty' : name}"`)
|
|
160
|
+
|
|
161
|
+
if (
|
|
162
|
+
/^[0-9]/.test(name)
|
|
163
|
+
|| !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)
|
|
164
|
+
|| reservedWords.has(name.toUpperCase())
|
|
165
|
+
) {
|
|
166
|
+
return `"${name}"`
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return name
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function tableSlug(name: string) {
|
|
173
|
+
return name.trim()
|
|
174
|
+
.replace(/([A-Z])/g, '_$1')
|
|
175
|
+
.replace(/[^a-zA-Z0-9_]/g, '_')
|
|
176
|
+
.toLowerCase()
|
|
177
|
+
.replace(/_+/g, '_')
|
|
178
|
+
.replace(/^_|_$/g, '')
|
|
179
|
+
}
|