@mantiq/database 0.0.1
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/README.md +19 -0
- package/package.json +77 -0
- package/src/DatabaseManager.ts +115 -0
- package/src/DatabaseServiceProvider.ts +39 -0
- package/src/contracts/Connection.ts +13 -0
- package/src/contracts/Grammar.ts +16 -0
- package/src/contracts/MongoConnection.ts +122 -0
- package/src/contracts/Paginator.ts +10 -0
- package/src/drivers/BaseGrammar.ts +220 -0
- package/src/drivers/MSSQLConnection.ts +154 -0
- package/src/drivers/MSSQLGrammar.ts +106 -0
- package/src/drivers/MongoConnection.ts +298 -0
- package/src/drivers/MongoQueryBuilderImpl.ts +77 -0
- package/src/drivers/MySQLConnection.ts +120 -0
- package/src/drivers/MySQLGrammar.ts +19 -0
- package/src/drivers/PostgresConnection.ts +125 -0
- package/src/drivers/PostgresGrammar.ts +24 -0
- package/src/drivers/SQLiteConnection.ts +125 -0
- package/src/drivers/SQLiteGrammar.ts +19 -0
- package/src/errors/ConnectionError.ts +10 -0
- package/src/errors/ModelNotFoundError.ts +14 -0
- package/src/errors/QueryError.ts +11 -0
- package/src/events/DatabaseEvents.ts +101 -0
- package/src/factories/Factory.ts +170 -0
- package/src/factories/Faker.ts +382 -0
- package/src/helpers/db.ts +37 -0
- package/src/index.ts +100 -0
- package/src/migrations/Migration.ts +12 -0
- package/src/migrations/MigrationRepository.ts +50 -0
- package/src/migrations/Migrator.ts +201 -0
- package/src/orm/Collection.ts +236 -0
- package/src/orm/Document.ts +202 -0
- package/src/orm/Model.ts +775 -0
- package/src/orm/ModelQueryBuilder.ts +415 -0
- package/src/orm/Scope.ts +39 -0
- package/src/orm/eagerLoad.ts +300 -0
- package/src/query/Builder.ts +456 -0
- package/src/query/Expression.ts +18 -0
- package/src/schema/Blueprint.ts +196 -0
- package/src/schema/ColumnDefinition.ts +93 -0
- package/src/schema/SchemaBuilder.ts +376 -0
- package/src/seeders/Seeder.ts +28 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export class ColumnDefinition {
|
|
2
|
+
private _nullable = false
|
|
3
|
+
private _default: any = undefined
|
|
4
|
+
private _hasDefault = false
|
|
5
|
+
private _unique = false
|
|
6
|
+
private _index = false
|
|
7
|
+
private _unsigned = false
|
|
8
|
+
private _primary = false
|
|
9
|
+
private _references: { table: string; column: string; onDelete?: string; onUpdate?: string } | null = null
|
|
10
|
+
private _comment: string | null = null
|
|
11
|
+
private _after: string | null = null
|
|
12
|
+
|
|
13
|
+
constructor(
|
|
14
|
+
public readonly name: string,
|
|
15
|
+
public readonly type: string,
|
|
16
|
+
public readonly length?: number,
|
|
17
|
+
public readonly precision?: number,
|
|
18
|
+
public readonly scale?: number,
|
|
19
|
+
) {}
|
|
20
|
+
|
|
21
|
+
nullable(): this {
|
|
22
|
+
this._nullable = true
|
|
23
|
+
return this
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
default(value: any): this {
|
|
27
|
+
this._default = value
|
|
28
|
+
this._hasDefault = true
|
|
29
|
+
return this
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
unique(): this {
|
|
33
|
+
this._unique = true
|
|
34
|
+
return this
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
index(): this {
|
|
38
|
+
this._index = true
|
|
39
|
+
return this
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
unsigned(): this {
|
|
43
|
+
this._unsigned = true
|
|
44
|
+
return this
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
primary(): this {
|
|
48
|
+
this._primary = true
|
|
49
|
+
return this
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
references(column: string): this & { on(table: string): this } {
|
|
53
|
+
this._references = { table: '', column }
|
|
54
|
+
const self = this as any
|
|
55
|
+
self.on = (table: string) => {
|
|
56
|
+
this._references!.table = table
|
|
57
|
+
return self
|
|
58
|
+
}
|
|
59
|
+
return self
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
onDelete(action: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION'): this {
|
|
63
|
+
if (this._references) this._references.onDelete = action
|
|
64
|
+
return this
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
onUpdate(action: 'CASCADE' | 'SET NULL' | 'RESTRICT' | 'NO ACTION'): this {
|
|
68
|
+
if (this._references) this._references.onUpdate = action
|
|
69
|
+
return this
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
comment(text: string): this {
|
|
73
|
+
this._comment = text
|
|
74
|
+
return this
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
after(column: string): this {
|
|
78
|
+
this._after = column
|
|
79
|
+
return this
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Getters for the compiler
|
|
83
|
+
isNullable() { return this._nullable }
|
|
84
|
+
hasDefault() { return this._hasDefault }
|
|
85
|
+
getDefault() { return this._default }
|
|
86
|
+
isUnique() { return this._unique }
|
|
87
|
+
hasIndex() { return this._index }
|
|
88
|
+
isUnsigned() { return this._unsigned }
|
|
89
|
+
isPrimary() { return this._primary }
|
|
90
|
+
getReferences() { return this._references }
|
|
91
|
+
getComment() { return this._comment }
|
|
92
|
+
getAfter() { return this._after }
|
|
93
|
+
}
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
import type { DatabaseConnection } from '../contracts/Connection.ts'
|
|
2
|
+
import { Blueprint } from './Blueprint.ts'
|
|
3
|
+
import type { ColumnDefinition } from './ColumnDefinition.ts'
|
|
4
|
+
import type { ForeignKeyDefinition } from './Blueprint.ts'
|
|
5
|
+
|
|
6
|
+
export interface SchemaBuilder {
|
|
7
|
+
/** Create a new table */
|
|
8
|
+
create(table: string, callback: (blueprint: Blueprint) => void): Promise<void>
|
|
9
|
+
/** Modify an existing table */
|
|
10
|
+
table(table: string, callback: (blueprint: Blueprint) => void): Promise<void>
|
|
11
|
+
/** Drop a table if it exists */
|
|
12
|
+
dropIfExists(table: string): Promise<void>
|
|
13
|
+
/** Drop a table */
|
|
14
|
+
drop(table: string): Promise<void>
|
|
15
|
+
/** Check if a table exists */
|
|
16
|
+
hasTable(table: string): Promise<boolean>
|
|
17
|
+
/** Check if a column exists */
|
|
18
|
+
hasColumn(table: string, column: string): Promise<boolean>
|
|
19
|
+
/** Rename a table */
|
|
20
|
+
rename(from: string, to: string): Promise<void>
|
|
21
|
+
/** Disable FK checks */
|
|
22
|
+
disableForeignKeyConstraints(): Promise<void>
|
|
23
|
+
/** Enable FK checks */
|
|
24
|
+
enableForeignKeyConstraints(): Promise<void>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class SchemaBuilderImpl implements SchemaBuilder {
|
|
28
|
+
constructor(private readonly connection: DatabaseConnection) {}
|
|
29
|
+
|
|
30
|
+
async create(table: string, callback: (blueprint: Blueprint) => void): Promise<void> {
|
|
31
|
+
const bp = new Blueprint()
|
|
32
|
+
callback(bp)
|
|
33
|
+
const sql = this.compileCreate(table, bp)
|
|
34
|
+
for (const s of sql) {
|
|
35
|
+
await this.connection.statement(s)
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async table(table: string, callback: (blueprint: Blueprint) => void): Promise<void> {
|
|
40
|
+
const bp = new Blueprint()
|
|
41
|
+
callback(bp)
|
|
42
|
+
const sql = this.compileAlter(table, bp)
|
|
43
|
+
for (const s of sql) {
|
|
44
|
+
await this.connection.statement(s)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async dropIfExists(table: string): Promise<void> {
|
|
49
|
+
await this.connection.statement(this.compileDropIfExists(table))
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async drop(table: string): Promise<void> {
|
|
53
|
+
await this.connection.statement(`DROP TABLE ${this.quoteTable(table)}`)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async hasTable(table: string): Promise<boolean> {
|
|
57
|
+
const driver = this.connection.getDriverName()
|
|
58
|
+
let sql: string
|
|
59
|
+
let bindings: any[]
|
|
60
|
+
|
|
61
|
+
if (driver === 'sqlite') {
|
|
62
|
+
sql = `SELECT name FROM sqlite_master WHERE type='table' AND name=?`
|
|
63
|
+
bindings = [table]
|
|
64
|
+
} else if (driver === 'postgres') {
|
|
65
|
+
sql = `SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_name=$1`
|
|
66
|
+
bindings = [table]
|
|
67
|
+
} else if (driver === 'mssql') {
|
|
68
|
+
sql = `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' AND TABLE_NAME=@p1`
|
|
69
|
+
bindings = [table]
|
|
70
|
+
} else {
|
|
71
|
+
sql = `SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() AND table_name=?`
|
|
72
|
+
bindings = [table]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const rows = await this.connection.select(sql, bindings)
|
|
76
|
+
return rows.length > 0
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async hasColumn(table: string, column: string): Promise<boolean> {
|
|
80
|
+
const driver = this.connection.getDriverName()
|
|
81
|
+
let sql: string
|
|
82
|
+
let bindings: any[]
|
|
83
|
+
|
|
84
|
+
if (driver === 'sqlite') {
|
|
85
|
+
sql = `PRAGMA table_info(${this.quoteTable(table)})`
|
|
86
|
+
const rows = await this.connection.select(sql, [])
|
|
87
|
+
return rows.some((r) => r['name'] === column)
|
|
88
|
+
} else if (driver === 'postgres') {
|
|
89
|
+
sql = `SELECT column_name FROM information_schema.columns WHERE table_name=$1 AND column_name=$2`
|
|
90
|
+
bindings = [table, column]
|
|
91
|
+
} else if (driver === 'mssql') {
|
|
92
|
+
sql = `SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME=@p1 AND COLUMN_NAME=@p2`
|
|
93
|
+
bindings = [table, column]
|
|
94
|
+
} else {
|
|
95
|
+
sql = `SELECT column_name FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name=? AND column_name=?`
|
|
96
|
+
bindings = [table, column]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const rows = await this.connection.select(sql, bindings)
|
|
100
|
+
return rows.length > 0
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async rename(from: string, to: string): Promise<void> {
|
|
104
|
+
const driver = this.connection.getDriverName()
|
|
105
|
+
if (driver === 'mssql') {
|
|
106
|
+
await this.connection.statement(`EXEC sp_rename '${from}', '${to}'`)
|
|
107
|
+
} else {
|
|
108
|
+
await this.connection.statement(`ALTER TABLE ${this.quoteTable(from)} RENAME TO ${this.quoteTable(to)}`)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async disableForeignKeyConstraints(): Promise<void> {
|
|
113
|
+
const driver = this.connection.getDriverName()
|
|
114
|
+
if (driver === 'sqlite') {
|
|
115
|
+
await this.connection.statement('PRAGMA foreign_keys = OFF')
|
|
116
|
+
} else if (driver === 'mysql') {
|
|
117
|
+
await this.connection.statement('SET FOREIGN_KEY_CHECKS=0')
|
|
118
|
+
} else if (driver === 'mssql') {
|
|
119
|
+
await this.connection.statement("EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'")
|
|
120
|
+
} else {
|
|
121
|
+
await this.connection.statement('SET CONSTRAINTS ALL DEFERRED')
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async enableForeignKeyConstraints(): Promise<void> {
|
|
126
|
+
const driver = this.connection.getDriverName()
|
|
127
|
+
if (driver === 'sqlite') {
|
|
128
|
+
await this.connection.statement('PRAGMA foreign_keys = ON')
|
|
129
|
+
} else if (driver === 'mysql') {
|
|
130
|
+
await this.connection.statement('SET FOREIGN_KEY_CHECKS=1')
|
|
131
|
+
} else if (driver === 'mssql') {
|
|
132
|
+
await this.connection.statement("EXEC sp_MSforeachtable 'ALTER TABLE ? WITH CHECK CHECK CONSTRAINT ALL'")
|
|
133
|
+
} else {
|
|
134
|
+
await this.connection.statement('SET CONSTRAINTS ALL IMMEDIATE')
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ── SQL Compilation ────────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
private compileCreate(table: string, bp: Blueprint): string[] {
|
|
141
|
+
const driver = this.connection.getDriverName()
|
|
142
|
+
const columnDefs = bp.columns.map((c) => this.compileColumnDef(c, driver))
|
|
143
|
+
|
|
144
|
+
// Inline primary key from PK index
|
|
145
|
+
const pkIndex = bp.indexes.find((i) => i.type === 'primary')
|
|
146
|
+
if (pkIndex) {
|
|
147
|
+
columnDefs.push(`PRIMARY KEY (${pkIndex.columns.map((c) => this.quoteCol(c)).join(', ')})`)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Foreign keys (not for SQLite inline — use separate PRAGMA)
|
|
151
|
+
if (driver !== 'sqlite') {
|
|
152
|
+
for (const fk of bp.foreignKeys) {
|
|
153
|
+
columnDefs.push(this.compileForeignKey(fk))
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const sql = [`CREATE TABLE ${this.quoteTable(table)} (\n ${columnDefs.join(',\n ')}\n)`]
|
|
158
|
+
|
|
159
|
+
// Column-level unique constraints → separate UNIQUE INDEX statements
|
|
160
|
+
for (const col of bp.columns) {
|
|
161
|
+
if (col.isUnique()) {
|
|
162
|
+
sql.push(this.compileIndex(table, { type: 'unique', columns: [col.name] }))
|
|
163
|
+
}
|
|
164
|
+
if (col.hasIndex()) {
|
|
165
|
+
sql.push(this.compileIndex(table, { type: 'index', columns: [col.name] }))
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Blueprint-level indexes
|
|
170
|
+
for (const idx of bp.indexes.filter((i) => i.type !== 'primary')) {
|
|
171
|
+
sql.push(this.compileIndex(table, idx))
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return sql
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private compileAlter(table: string, bp: Blueprint): string[] {
|
|
178
|
+
const driver = this.connection.getDriverName()
|
|
179
|
+
const statements: string[] = []
|
|
180
|
+
|
|
181
|
+
for (const col of bp.columns) {
|
|
182
|
+
// MSSQL uses ADD without COLUMN keyword
|
|
183
|
+
const addKw = driver === 'mssql' ? 'ADD' : 'ADD COLUMN'
|
|
184
|
+
statements.push(
|
|
185
|
+
`ALTER TABLE ${this.quoteTable(table)} ${addKw} ${this.compileColumnDef(col, driver)}`,
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
for (const col of bp.droppedColumns) {
|
|
190
|
+
statements.push(`ALTER TABLE ${this.quoteTable(table)} DROP COLUMN ${this.quoteCol(col)}`)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
for (const idx of bp.indexes) {
|
|
194
|
+
statements.push(this.compileIndex(table, idx))
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return statements
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private compileDropIfExists(table: string): string {
|
|
201
|
+
const cascade = this.connection.getDriverName() === 'postgres' ? ' CASCADE' : ''
|
|
202
|
+
return `DROP TABLE IF EXISTS ${this.quoteTable(table)}${cascade}`
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private compileColumnDef(col: ColumnDefinition, driver: string): string {
|
|
206
|
+
const isAutoIncrement = col.type === 'bigIncrements' || col.type === 'increments'
|
|
207
|
+
let typeSql = this.mapType(col.type, driver, col.length, col.precision, col.scale)
|
|
208
|
+
|
|
209
|
+
if (col.isUnsigned() && driver !== 'sqlite' && driver !== 'mssql') typeSql += ' UNSIGNED'
|
|
210
|
+
|
|
211
|
+
let def = `${this.quoteCol(col.name)} ${typeSql}`
|
|
212
|
+
|
|
213
|
+
if (isAutoIncrement || col.isPrimary()) {
|
|
214
|
+
if (driver === 'sqlite') {
|
|
215
|
+
def += ' PRIMARY KEY AUTOINCREMENT'
|
|
216
|
+
} else if (driver === 'postgres') {
|
|
217
|
+
// SERIAL/BIGSERIAL already implies sequence; just mark PRIMARY KEY
|
|
218
|
+
def += ' PRIMARY KEY'
|
|
219
|
+
} else if (driver === 'mssql') {
|
|
220
|
+
def += ' IDENTITY(1,1) PRIMARY KEY'
|
|
221
|
+
} else {
|
|
222
|
+
def += ' PRIMARY KEY AUTO_INCREMENT'
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!col.isNullable() && !isAutoIncrement && !col.isPrimary()) def += ' NOT NULL'
|
|
227
|
+
if (col.isNullable()) def += ' NULL'
|
|
228
|
+
|
|
229
|
+
// Enum CHECK constraint for Postgres / MSSQL
|
|
230
|
+
if (col.type.startsWith('enum:') && (driver === 'postgres' || driver === 'mssql')) {
|
|
231
|
+
const values = col.type.slice(5).split(',').map((v) => `'${v}'`).join(', ')
|
|
232
|
+
def += ` CHECK (${this.quoteCol(col.name)} IN (${values}))`
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (col.hasDefault()) {
|
|
236
|
+
const dv = col.getDefault()
|
|
237
|
+
if (dv === null) {
|
|
238
|
+
def += ' DEFAULT NULL'
|
|
239
|
+
} else if (typeof dv === 'string') {
|
|
240
|
+
def += ` DEFAULT '${dv.replace(/'/g, "''")}'`
|
|
241
|
+
} else if (typeof dv === 'boolean') {
|
|
242
|
+
if (driver === 'postgres') {
|
|
243
|
+
def += ` DEFAULT ${dv ? 'TRUE' : 'FALSE'}`
|
|
244
|
+
} else {
|
|
245
|
+
def += ` DEFAULT ${dv ? 1 : 0}`
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
def += ` DEFAULT ${dv}`
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return def
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private compileIndex(table: string, idx: { type: 'index' | 'unique' | 'primary'; columns: string[]; name?: string }): string {
|
|
256
|
+
const cols = idx.columns.map((c) => this.quoteCol(c)).join(', ')
|
|
257
|
+
const idxName = idx.name ?? `${table}_${idx.columns.join('_')}_${idx.type}`
|
|
258
|
+
|
|
259
|
+
if (idx.type === 'unique') {
|
|
260
|
+
return `CREATE UNIQUE INDEX ${this.quoteCol(idxName)} ON ${this.quoteTable(table)} (${cols})`
|
|
261
|
+
}
|
|
262
|
+
return `CREATE INDEX ${this.quoteCol(idxName)} ON ${this.quoteTable(table)} (${cols})`
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private compileForeignKey(fk: ForeignKeyDefinition): string {
|
|
266
|
+
let sql = `FOREIGN KEY (${this.quoteCol(fk.column)}) REFERENCES ${this.quoteTable(fk.on)} (${this.quoteCol(fk.references)})`
|
|
267
|
+
if (fk.onDelete) sql += ` ON DELETE ${fk.onDelete}`
|
|
268
|
+
if (fk.onUpdate) sql += ` ON UPDATE ${fk.onUpdate}`
|
|
269
|
+
return sql
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
private mapType(type: string, driver: string, length?: number, precision?: number, scale?: number): string {
|
|
273
|
+
const len = length ?? 255
|
|
274
|
+
|
|
275
|
+
switch (type) {
|
|
276
|
+
case 'bigIncrements':
|
|
277
|
+
if (driver === 'postgres') return 'BIGSERIAL'
|
|
278
|
+
if (driver === 'mysql') return 'BIGINT UNSIGNED'
|
|
279
|
+
if (driver === 'mssql') return 'BIGINT'
|
|
280
|
+
return 'INTEGER' // SQLite uses INTEGER for all auto-increment
|
|
281
|
+
case 'increments':
|
|
282
|
+
if (driver === 'postgres') return 'SERIAL'
|
|
283
|
+
if (driver === 'mysql') return 'INT UNSIGNED'
|
|
284
|
+
if (driver === 'mssql') return 'INT'
|
|
285
|
+
return 'INTEGER'
|
|
286
|
+
case 'string':
|
|
287
|
+
if (driver === 'mssql') return `NVARCHAR(${len})`
|
|
288
|
+
return `VARCHAR(${len})`
|
|
289
|
+
case 'text':
|
|
290
|
+
if (driver === 'mssql') return 'NVARCHAR(MAX)'
|
|
291
|
+
return 'TEXT'
|
|
292
|
+
case 'mediumText':
|
|
293
|
+
if (driver === 'mssql') return 'NVARCHAR(MAX)'
|
|
294
|
+
return driver === 'mysql' ? 'MEDIUMTEXT' : 'TEXT'
|
|
295
|
+
case 'longText':
|
|
296
|
+
if (driver === 'mssql') return 'NVARCHAR(MAX)'
|
|
297
|
+
return driver === 'mysql' ? 'LONGTEXT' : 'TEXT'
|
|
298
|
+
case 'integer':
|
|
299
|
+
if (driver === 'mssql') return 'INT'
|
|
300
|
+
return 'INTEGER'
|
|
301
|
+
case 'bigInteger':
|
|
302
|
+
return 'BIGINT'
|
|
303
|
+
case 'tinyInteger':
|
|
304
|
+
if (driver === 'mysql' || driver === 'mssql') return 'TINYINT'
|
|
305
|
+
return 'INTEGER'
|
|
306
|
+
case 'smallInteger':
|
|
307
|
+
return 'SMALLINT'
|
|
308
|
+
case 'unsignedInteger':
|
|
309
|
+
if (driver === 'mysql') return 'INT UNSIGNED'
|
|
310
|
+
if (driver === 'mssql') return 'INT'
|
|
311
|
+
return 'INTEGER'
|
|
312
|
+
case 'unsignedBigInteger':
|
|
313
|
+
if (driver === 'mysql') return 'BIGINT UNSIGNED'
|
|
314
|
+
if (driver === 'mssql') return 'BIGINT'
|
|
315
|
+
return 'BIGINT'
|
|
316
|
+
case 'float':
|
|
317
|
+
if (driver === 'postgres' || driver === 'mssql') return 'REAL'
|
|
318
|
+
return `FLOAT(${precision ?? 8}, ${scale ?? 2})`
|
|
319
|
+
case 'double':
|
|
320
|
+
if (driver === 'postgres') return 'DOUBLE PRECISION'
|
|
321
|
+
if (driver === 'mssql') return 'FLOAT'
|
|
322
|
+
return `DOUBLE(${precision ?? 15}, ${scale ?? 8})`
|
|
323
|
+
case 'decimal':
|
|
324
|
+
return `DECIMAL(${precision ?? 8}, ${scale ?? 2})`
|
|
325
|
+
case 'boolean':
|
|
326
|
+
if (driver === 'postgres') return 'BOOLEAN'
|
|
327
|
+
if (driver === 'mssql') return 'BIT'
|
|
328
|
+
return 'TINYINT(1)'
|
|
329
|
+
case 'date':
|
|
330
|
+
return 'DATE'
|
|
331
|
+
case 'dateTime':
|
|
332
|
+
if (driver === 'postgres') return 'TIMESTAMP'
|
|
333
|
+
if (driver === 'mssql') return 'DATETIME2'
|
|
334
|
+
return 'DATETIME'
|
|
335
|
+
case 'timestamp':
|
|
336
|
+
if (driver === 'mssql') return 'DATETIME2'
|
|
337
|
+
return 'TIMESTAMP'
|
|
338
|
+
case 'json':
|
|
339
|
+
if (driver === 'sqlite') return 'TEXT'
|
|
340
|
+
if (driver === 'mssql') return 'NVARCHAR(MAX)'
|
|
341
|
+
return 'JSON'
|
|
342
|
+
case 'jsonb':
|
|
343
|
+
if (driver === 'postgres') return 'JSONB'
|
|
344
|
+
if (driver === 'mssql') return 'NVARCHAR(MAX)'
|
|
345
|
+
return 'JSON'
|
|
346
|
+
case 'uuid':
|
|
347
|
+
if (driver === 'postgres') return 'UUID'
|
|
348
|
+
if (driver === 'mssql') return 'UNIQUEIDENTIFIER'
|
|
349
|
+
return 'VARCHAR(36)'
|
|
350
|
+
case 'binary':
|
|
351
|
+
if (driver === 'postgres') return 'BYTEA'
|
|
352
|
+
if (driver === 'mssql') return 'VARBINARY(MAX)'
|
|
353
|
+
return 'BLOB'
|
|
354
|
+
default:
|
|
355
|
+
// Handle enum:val1,val2 syntax
|
|
356
|
+
if (type.startsWith('enum:')) {
|
|
357
|
+
const values = type.slice(5).split(',').map((v) => `'${v}'`).join(', ')
|
|
358
|
+
if (driver === 'postgres' || driver === 'sqlite') return 'TEXT'
|
|
359
|
+
if (driver === 'mssql') return 'NVARCHAR(255)'
|
|
360
|
+
return `ENUM(${values})`
|
|
361
|
+
}
|
|
362
|
+
return type.toUpperCase()
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
private quoteTable(name: string): string {
|
|
367
|
+
const driver = this.connection.getDriverName()
|
|
368
|
+
if (driver === 'mysql') return `\`${name}\``
|
|
369
|
+
if (driver === 'mssql') return `[${name}]`
|
|
370
|
+
return `"${name}"`
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private quoteCol(name: string): string {
|
|
374
|
+
return this.quoteTable(name)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DatabaseConnection } from '../contracts/Connection.ts'
|
|
2
|
+
|
|
3
|
+
export abstract class Seeder {
|
|
4
|
+
protected connection!: DatabaseConnection
|
|
5
|
+
|
|
6
|
+
abstract run(): Promise<void>
|
|
7
|
+
|
|
8
|
+
setConnection(connection: DatabaseConnection): this {
|
|
9
|
+
this.connection = connection
|
|
10
|
+
return this
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
protected async call(SeederClass: new () => Seeder): Promise<void> {
|
|
14
|
+
const seeder = new SeederClass()
|
|
15
|
+
seeder.setConnection(this.connection)
|
|
16
|
+
await seeder.run()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected async callMany(classes: (new () => Seeder)[]): Promise<void> {
|
|
20
|
+
for (const cls of classes) {
|
|
21
|
+
await this.call(cls)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
table(name: string) {
|
|
26
|
+
return this.connection.table(name)
|
|
27
|
+
}
|
|
28
|
+
}
|