tempest-db-js 0.4.0 → 0.5.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/README.md +8 -2
- package/dist/bin.cjs +159 -56
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +2 -2
- package/dist/{chunk-JR4MLFQN.js → chunk-5QQMVTS5.js} +881 -257
- package/dist/chunk-5QQMVTS5.js.map +1 -0
- package/dist/{chunk-43XL66JG.js → chunk-EPMLFNFK.js} +103 -30
- package/dist/chunk-EPMLFNFK.js.map +1 -0
- package/dist/index.cjs +884 -254
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +532 -29
- package/dist/index.d.ts +532 -29
- package/dist/index.js +1 -1
- package/dist/migrations/index.cjs +214 -62
- package/dist/migrations/index.cjs.map +1 -1
- package/dist/migrations/index.d.cts +12 -2
- package/dist/migrations/index.d.ts +12 -2
- package/dist/migrations/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-43XL66JG.js.map +0 -1
- package/dist/chunk-JR4MLFQN.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { ActiveRecord, Agg, AsyncEngine, AsyncResult, AsyncSession, BaseDialect, BaseRepository, Column, DeleteBuilder, InsertBuilder, InvalidDatabaseUrl, JoinBuilder, Model, MysqlDialect, NoResultError, NodeSqliteDriver, OPERATORS, PostgresDialect, QueryExecutionError, RecordNotFound, SelectBuilder, SqliteDialect, SyncEngine, SyncResult, SyncSession, UpdateBuilder, ValidationError, activeRecord, and, avg, belongsTo, column, columnsOf, count, createEngine, createSyncEngine, del, detectDialect, foreignKey, fromDict, getDialect, hasMany, insert, isCondition, join, loadRelations, max, min, not, or, parse, parseDatabaseUrl, select, sql, stringify, sum, toCondNode, toDict, toJSON, unique, update } from './chunk-
|
|
1
|
+
export { ActiveRecord, Agg, AsyncEngine, AsyncResult, AsyncSession, BaseDialect, BaseRepository, Column, DeleteBuilder, InsertBuilder, InvalidDatabaseUrl, JoinBuilder, Model, MysqlDialect, NoResultError, NodeSqliteDriver, OPERATORS, Params, PostgresDialect, QueryExecutionError, RecordNotFound, SelectBuilder, SqliteDialect, SyncEngine, SyncResult, SyncSession, UpdateBuilder, ValidationError, activeRecord, and, avg, belongsTo, column, columnNamesOf, columnPropsOf, columnsOf, count, createEngine, createSyncEngine, dbColumn, del, detectDialect, foreignKey, fromDict, getDialect, hasMany, insert, isCondition, isSqlExpression, join, loadRelations, max, min, not, or, parse, parseDatabaseUrl, select, sql, stringify, sum, toCondNode, toDict, toJSON, toSnakeCase, unique, update } from './chunk-5QQMVTS5.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// src/expressions.ts
|
|
4
|
+
function renderPortableToken(token, dialect) {
|
|
5
|
+
switch (token) {
|
|
6
|
+
case "now":
|
|
7
|
+
return dialect === "postgresql" ? "now()" : "CURRENT_TIMESTAMP";
|
|
8
|
+
case "current_date":
|
|
9
|
+
return "CURRENT_DATE";
|
|
10
|
+
case "current_time":
|
|
11
|
+
return "CURRENT_TIME";
|
|
12
|
+
case "uuidv4":
|
|
13
|
+
if (dialect === "postgresql") return "gen_random_uuid()";
|
|
14
|
+
if (dialect === "mysql") return "(UUID())";
|
|
15
|
+
return "(lower(hex(randomblob(16))))";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
3
19
|
// src/index.ts
|
|
4
20
|
function isDefaultValue(value) {
|
|
5
21
|
return typeof value === "object" && value !== null && "kind" in value && (value.kind === "literal" || value.kind === "expression");
|
|
6
22
|
}
|
|
23
|
+
function bindsParameters(value) {
|
|
24
|
+
return value.kind === "expression" && typeof value.expression === "object" && "parts" in value.expression;
|
|
25
|
+
}
|
|
7
26
|
function parseReference(ref, options) {
|
|
8
27
|
const dot = ref.lastIndexOf(".");
|
|
9
28
|
if (dot <= 0 || dot === ref.length - 1) {
|
|
@@ -17,48 +36,71 @@ function parseReference(ref, options) {
|
|
|
17
36
|
};
|
|
18
37
|
}
|
|
19
38
|
var Column = class _Column {
|
|
20
|
-
constructor(type, flags, defaultValue = null, onUpdateValue = null, reference = null) {
|
|
39
|
+
constructor(type, flags, defaultValue = null, onUpdateValue = null, reference = null, dbName = null) {
|
|
21
40
|
this.type = type;
|
|
22
41
|
this.flags = flags;
|
|
23
42
|
this.defaultValue = defaultValue;
|
|
24
43
|
this.onUpdateValue = onUpdateValue;
|
|
25
44
|
this.reference = reference;
|
|
45
|
+
this.dbName = dbName;
|
|
26
46
|
}
|
|
27
47
|
type;
|
|
28
48
|
flags;
|
|
29
49
|
defaultValue;
|
|
30
50
|
onUpdateValue;
|
|
31
51
|
reference;
|
|
32
|
-
|
|
52
|
+
dbName;
|
|
53
|
+
/** Clone this column with one facet replaced, carrying every other over. */
|
|
54
|
+
derive(patch) {
|
|
33
55
|
return new _Column(
|
|
34
56
|
this.type,
|
|
35
|
-
|
|
36
|
-
this.defaultValue,
|
|
37
|
-
this.onUpdateValue,
|
|
38
|
-
this.reference
|
|
57
|
+
patch.flags ?? this.flags,
|
|
58
|
+
patch.defaultValue !== void 0 ? patch.defaultValue : this.defaultValue,
|
|
59
|
+
patch.onUpdateValue !== void 0 ? patch.onUpdateValue : this.onUpdateValue,
|
|
60
|
+
patch.reference !== void 0 ? patch.reference : this.reference,
|
|
61
|
+
patch.dbName !== void 0 ? patch.dbName : this.dbName
|
|
39
62
|
);
|
|
40
63
|
}
|
|
64
|
+
primaryKey() {
|
|
65
|
+
return this.derive({ flags: { ...this.flags, primaryKey: true, hasDefault: true } });
|
|
66
|
+
}
|
|
41
67
|
notNull() {
|
|
42
|
-
return
|
|
43
|
-
this.type,
|
|
44
|
-
{ ...this.flags, notNull: true },
|
|
45
|
-
this.defaultValue,
|
|
46
|
-
this.onUpdateValue,
|
|
47
|
-
this.reference
|
|
48
|
-
);
|
|
68
|
+
return this.derive({ flags: { ...this.flags, notNull: true } });
|
|
49
69
|
}
|
|
50
70
|
/**
|
|
51
71
|
* Add a `UNIQUE` constraint to the column (mirrors SQLAlchemy's
|
|
52
72
|
* `mapped_column(unique=True)`). DDL-only — does not change the inferred type.
|
|
53
73
|
*/
|
|
54
74
|
unique() {
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
75
|
+
return this.derive({ flags: { ...this.flags, unique: true } });
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Map this property to a differently-named database column, à la SQLAlchemy's
|
|
79
|
+
* `mapped_column("consumer_name")` (Django's `db_column`, Prisma's `@map`).
|
|
80
|
+
*
|
|
81
|
+
* The override applies everywhere the name reaches SQL — select, insert,
|
|
82
|
+
* update, delete, where, order by, group by, returning, conflict targets, the
|
|
83
|
+
* migration IR and the drift check — while the TypeScript row keeps the
|
|
84
|
+
* property name. Use it to keep a `snake_case` schema behind a `camelCase`
|
|
85
|
+
* model; {@link Model.naming} does the same for a whole table at once.
|
|
86
|
+
*
|
|
87
|
+
* @param dbName The real column name in the database.
|
|
88
|
+
* @returns A new column bound to that name.
|
|
89
|
+
* @throws Error When `dbName` is empty.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```ts
|
|
93
|
+
* class ApiKey extends Model {
|
|
94
|
+
* static tablename = "api_keys";
|
|
95
|
+
* consumerName = column.text().name("consumer_name").notNull();
|
|
96
|
+
* }
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
name(dbName) {
|
|
100
|
+
if (dbName.length === 0) {
|
|
101
|
+
throw new Error("column.name() requires a non-empty database column name.");
|
|
102
|
+
}
|
|
103
|
+
return this.derive({ dbName });
|
|
62
104
|
}
|
|
63
105
|
/**
|
|
64
106
|
* Declare a foreign-key reference to another table's column, à la SQLAlchemy's
|
|
@@ -71,37 +113,50 @@ var Column = class _Column {
|
|
|
71
113
|
* @throws Error When `ref` is not a valid `"table.column"` string.
|
|
72
114
|
*/
|
|
73
115
|
references(ref, options) {
|
|
74
|
-
return
|
|
75
|
-
this.type,
|
|
76
|
-
this.flags,
|
|
77
|
-
this.defaultValue,
|
|
78
|
-
this.onUpdateValue,
|
|
79
|
-
parseReference(ref, options)
|
|
80
|
-
);
|
|
116
|
+
return this.derive({ reference: parseReference(ref, options) });
|
|
81
117
|
}
|
|
82
118
|
/**
|
|
83
119
|
* Set the insert-time default: a constant value of type `T`, or a portable
|
|
84
120
|
* server-side expression from {@link sql} (e.g. `sql.now()`, `sql.uuidv4()`).
|
|
121
|
+
*
|
|
122
|
+
* @param value The literal default, or a {@link sql} expression.
|
|
123
|
+
* @returns A new column carrying the default.
|
|
124
|
+
* @throws Error When given a `sql.expr` fragment — a `DEFAULT` clause has
|
|
125
|
+
* nowhere to bind parameters; use `sql.raw()` for a verbatim expression.
|
|
85
126
|
*/
|
|
86
127
|
default(value) {
|
|
87
128
|
const resolved = isDefaultValue(value) ? value : { kind: "literal", value };
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
129
|
+
if (bindsParameters(resolved)) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
"sql.expr`...` binds parameters and cannot be a column default \u2014 use sql.raw() for a verbatim DEFAULT expression."
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return this.derive({
|
|
135
|
+
flags: { ...this.flags, hasDefault: true },
|
|
136
|
+
defaultValue: resolved
|
|
137
|
+
});
|
|
95
138
|
}
|
|
96
139
|
/**
|
|
97
140
|
* Re-apply a value whenever the row is updated (e.g. an `updated_at` column
|
|
98
141
|
* with `sql.now()`). Mirrors SQLAlchemy's `onupdate`.
|
|
142
|
+
*
|
|
143
|
+
* @param value The literal value, or a {@link sql} expression.
|
|
144
|
+
* @returns A new column carrying the on-update value.
|
|
145
|
+
* @throws Error When given a `sql.expr` fragment (see {@link Column.default}).
|
|
99
146
|
*/
|
|
100
147
|
onUpdate(value) {
|
|
101
148
|
const resolved = isDefaultValue(value) ? value : { kind: "literal", value };
|
|
102
|
-
|
|
149
|
+
if (bindsParameters(resolved)) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
"sql.expr`...` binds parameters and cannot be an onUpdate default \u2014 use sql.raw() for a verbatim expression."
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
return this.derive({ onUpdateValue: resolved });
|
|
103
155
|
}
|
|
104
156
|
};
|
|
157
|
+
function toSnakeCase(name) {
|
|
158
|
+
return name.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
159
|
+
}
|
|
105
160
|
var columnsCache = /* @__PURE__ */ new WeakMap();
|
|
106
161
|
function columnsOf(model) {
|
|
107
162
|
const cached = columnsCache.get(model);
|
|
@@ -116,16 +171,43 @@ function columnsOf(model) {
|
|
|
116
171
|
columnsCache.set(model, out);
|
|
117
172
|
return out;
|
|
118
173
|
}
|
|
174
|
+
var nameMapCache = /* @__PURE__ */ new WeakMap();
|
|
175
|
+
function columnNamesOf(model) {
|
|
176
|
+
const cached = nameMapCache.get(model);
|
|
177
|
+
if (cached !== void 0) return cached;
|
|
178
|
+
const strategy = model.naming ?? "preserve";
|
|
179
|
+
const map = {};
|
|
180
|
+
const seen = /* @__PURE__ */ new Map();
|
|
181
|
+
let renamed = false;
|
|
182
|
+
for (const [prop, col] of Object.entries(columnsOf(model))) {
|
|
183
|
+
const dbName = col.dbName ?? (strategy === "snake_case" ? toSnakeCase(prop) : prop);
|
|
184
|
+
const collision = seen.get(dbName);
|
|
185
|
+
if (collision !== void 0) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`${model.tablename}: properties "${collision}" and "${prop}" both map to column "${dbName}".`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
seen.set(dbName, prop);
|
|
191
|
+
map[prop] = dbName;
|
|
192
|
+
if (dbName !== prop) renamed = true;
|
|
193
|
+
}
|
|
194
|
+
const result = renamed ? map : null;
|
|
195
|
+
nameMapCache.set(model, result);
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
119
198
|
|
|
120
199
|
// src/migrations/ir.ts
|
|
121
200
|
function constraintName(prefix, table, columns) {
|
|
122
201
|
return `${prefix}_${table}_${columns.join("_")}`;
|
|
123
202
|
}
|
|
124
203
|
function reflectTable(model) {
|
|
204
|
+
const names = columnNamesOf(model);
|
|
205
|
+
const toColumn = (prop) => names?.[prop] ?? prop;
|
|
125
206
|
const columns = {};
|
|
126
207
|
const primaryKey = [];
|
|
127
|
-
for (const [
|
|
208
|
+
for (const [prop, col] of Object.entries(columnsOf(model))) {
|
|
128
209
|
const isPk = col.flags.primaryKey;
|
|
210
|
+
const name = toColumn(prop);
|
|
129
211
|
columns[name] = {
|
|
130
212
|
name,
|
|
131
213
|
type: col.type,
|
|
@@ -140,15 +222,16 @@ function reflectTable(model) {
|
|
|
140
222
|
const uniqueConstraints = [];
|
|
141
223
|
const foreignKeys = [];
|
|
142
224
|
for (const c of model.tableArgs?.() ?? []) {
|
|
225
|
+
const cols = c.columns.map(toColumn);
|
|
143
226
|
if (c.kind === "unique") {
|
|
144
227
|
uniqueConstraints.push({
|
|
145
|
-
name: c.name ?? constraintName("uq", model.tablename,
|
|
146
|
-
columns:
|
|
228
|
+
name: c.name ?? constraintName("uq", model.tablename, cols),
|
|
229
|
+
columns: cols
|
|
147
230
|
});
|
|
148
231
|
} else {
|
|
149
232
|
foreignKeys.push({
|
|
150
|
-
name: c.name ?? constraintName("fk", model.tablename,
|
|
151
|
-
columns:
|
|
233
|
+
name: c.name ?? constraintName("fk", model.tablename, cols),
|
|
234
|
+
columns: cols,
|
|
152
235
|
refTable: c.refTable,
|
|
153
236
|
refColumns: c.refColumns,
|
|
154
237
|
onDelete: c.onDelete,
|
|
@@ -239,6 +322,8 @@ function renderColumnType(type, dialect) {
|
|
|
239
322
|
return "NUMERIC";
|
|
240
323
|
case "blob":
|
|
241
324
|
return "BLOB";
|
|
325
|
+
case "array":
|
|
326
|
+
throw new Error(unsupportedArray("sqlite"));
|
|
242
327
|
default:
|
|
243
328
|
return "TEXT";
|
|
244
329
|
}
|
|
@@ -280,6 +365,8 @@ function renderColumnType(type, dialect) {
|
|
|
280
365
|
return "CHAR(36)";
|
|
281
366
|
case "enum":
|
|
282
367
|
return `ENUM(${(meta.values ?? []).map(quoteLiteral).join(", ")})`;
|
|
368
|
+
case "array":
|
|
369
|
+
throw new Error(unsupportedArray("mysql"));
|
|
283
370
|
}
|
|
284
371
|
}
|
|
285
372
|
switch (kind) {
|
|
@@ -318,27 +405,39 @@ function renderColumnType(type, dialect) {
|
|
|
318
405
|
return "UUID";
|
|
319
406
|
case "enum":
|
|
320
407
|
return "TEXT";
|
|
408
|
+
case "array":
|
|
409
|
+
return `${renderColumnType(arrayElement(meta.element), dialect)}[]`;
|
|
321
410
|
}
|
|
322
411
|
}
|
|
323
|
-
function
|
|
412
|
+
function unsupportedArray(dialect) {
|
|
413
|
+
return `column.array() is PostgreSQL-only \u2014 ${dialect} has no native array type. Model the column as JSON there, accepting that array operators will not work.`;
|
|
414
|
+
}
|
|
415
|
+
function arrayElement(element) {
|
|
416
|
+
if (!element) {
|
|
417
|
+
throw new Error(
|
|
418
|
+
"An array column has no element type \u2014 build it with column.array()."
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
return element;
|
|
422
|
+
}
|
|
423
|
+
function renderDefault(def, dialect, type) {
|
|
324
424
|
if (def.kind === "expression") {
|
|
325
425
|
const expr = def.expression;
|
|
326
|
-
if (typeof expr === "object")
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
return "CURRENT_DATE";
|
|
332
|
-
case "current_time":
|
|
333
|
-
return "CURRENT_TIME";
|
|
334
|
-
case "uuidv4":
|
|
335
|
-
if (dialect === "postgresql") return "gen_random_uuid()";
|
|
336
|
-
if (dialect === "mysql") return "(UUID())";
|
|
337
|
-
return "(lower(hex(randomblob(16))))";
|
|
426
|
+
if (typeof expr === "object") {
|
|
427
|
+
if ("raw" in expr) return expr.raw;
|
|
428
|
+
throw new Error(
|
|
429
|
+
"sql.expr`...` binds parameters and cannot be rendered as a DEFAULT \u2014 use sql.raw()."
|
|
430
|
+
);
|
|
338
431
|
}
|
|
432
|
+
return renderPortableToken(expr, dialect);
|
|
339
433
|
}
|
|
340
434
|
const value = def.value;
|
|
341
435
|
if (value === null) return "NULL";
|
|
436
|
+
if (Array.isArray(value) && type?.kind === "array") {
|
|
437
|
+
const elementType = renderColumnType(arrayElement(type.meta.element), dialect);
|
|
438
|
+
const items = value.map((v) => renderDefault({ kind: "literal", value: v }, dialect));
|
|
439
|
+
return `ARRAY[${items.join(", ")}]::${elementType}[]`;
|
|
440
|
+
}
|
|
342
441
|
if (typeof value === "boolean") {
|
|
343
442
|
return dialect === "postgresql" ? value ? "TRUE" : "FALSE" : value ? "1" : "0";
|
|
344
443
|
}
|
|
@@ -384,7 +483,9 @@ function tableConstraintClauses(table, dialect) {
|
|
|
384
483
|
function renderColumnDef(col, dialect) {
|
|
385
484
|
let sql = `${quoteId(col.name, dialect)} ${renderColumnType(col.type, dialect)}`;
|
|
386
485
|
if (col.notNull) sql += " NOT NULL";
|
|
387
|
-
if (col.default !== null)
|
|
486
|
+
if (col.default !== null) {
|
|
487
|
+
sql += ` DEFAULT ${renderDefault(col.default, dialect, col.type)}`;
|
|
488
|
+
}
|
|
388
489
|
sql += columnConstraintSuffix(col, dialect);
|
|
389
490
|
return sql;
|
|
390
491
|
}
|
|
@@ -408,7 +509,9 @@ function renderCreateTable(table, dialect) {
|
|
|
408
509
|
typeStmts.push(`CREATE TYPE ${quoteId(typeName, dialect)} AS ENUM (${values})`);
|
|
409
510
|
let def = `${quoteId(c.name, dialect)} ${quoteId(typeName, dialect)}`;
|
|
410
511
|
if (c.notNull) def += " NOT NULL";
|
|
411
|
-
if (c.default !== null)
|
|
512
|
+
if (c.default !== null) {
|
|
513
|
+
def += ` DEFAULT ${renderDefault(c.default, dialect, c.type)}`;
|
|
514
|
+
}
|
|
412
515
|
return def + columnConstraintSuffix(c, dialect);
|
|
413
516
|
}
|
|
414
517
|
if (dialect === "postgresql" && isAutoIncrementPk(table, c)) {
|
|
@@ -552,7 +655,7 @@ function renderAlterColumn(table, to, dialect) {
|
|
|
552
655
|
to.notNull ? `ALTER TABLE ${t} ALTER COLUMN ${c} SET NOT NULL` : `ALTER TABLE ${t} ALTER COLUMN ${c} DROP NOT NULL`
|
|
553
656
|
);
|
|
554
657
|
stmts.push(
|
|
555
|
-
to.default !== null ? `ALTER TABLE ${t} ALTER COLUMN ${c} SET DEFAULT ${renderDefault(to.default, dialect)}` : `ALTER TABLE ${t} ALTER COLUMN ${c} DROP DEFAULT`
|
|
658
|
+
to.default !== null ? `ALTER TABLE ${t} ALTER COLUMN ${c} SET DEFAULT ${renderDefault(to.default, dialect, to.type)}` : `ALTER TABLE ${t} ALTER COLUMN ${c} DROP DEFAULT`
|
|
556
659
|
);
|
|
557
660
|
return stmts;
|
|
558
661
|
}
|
|
@@ -1197,6 +1300,54 @@ function checkDrift(driver, models) {
|
|
|
1197
1300
|
}
|
|
1198
1301
|
return issues;
|
|
1199
1302
|
}
|
|
1303
|
+
function pgTypeToColumnType(dataType, udtName) {
|
|
1304
|
+
if (dataType.toLowerCase() === "array") {
|
|
1305
|
+
return {
|
|
1306
|
+
kind: "array",
|
|
1307
|
+
meta: { element: { kind: pgUdtToKind(udtName.replace(/^_/, "")), meta: {} } }
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
return { kind: pgTypeToKind(dataType, udtName), meta: {} };
|
|
1311
|
+
}
|
|
1312
|
+
function pgUdtToKind(udtName) {
|
|
1313
|
+
switch (udtName) {
|
|
1314
|
+
case "int2":
|
|
1315
|
+
return "smallint";
|
|
1316
|
+
case "int4":
|
|
1317
|
+
return "integer";
|
|
1318
|
+
case "int8":
|
|
1319
|
+
return "bigint";
|
|
1320
|
+
case "float4":
|
|
1321
|
+
return "real";
|
|
1322
|
+
case "float8":
|
|
1323
|
+
return "double";
|
|
1324
|
+
case "numeric":
|
|
1325
|
+
return "numeric";
|
|
1326
|
+
case "varchar":
|
|
1327
|
+
return "varchar";
|
|
1328
|
+
case "bpchar":
|
|
1329
|
+
return "char";
|
|
1330
|
+
case "bool":
|
|
1331
|
+
return "boolean";
|
|
1332
|
+
case "date":
|
|
1333
|
+
return "date";
|
|
1334
|
+
case "time":
|
|
1335
|
+
case "timetz":
|
|
1336
|
+
return "time";
|
|
1337
|
+
case "timestamp":
|
|
1338
|
+
case "timestamptz":
|
|
1339
|
+
return "timestamp";
|
|
1340
|
+
case "bytea":
|
|
1341
|
+
return "blob";
|
|
1342
|
+
case "json":
|
|
1343
|
+
case "jsonb":
|
|
1344
|
+
return "json";
|
|
1345
|
+
case "uuid":
|
|
1346
|
+
return "uuid";
|
|
1347
|
+
default:
|
|
1348
|
+
return "text";
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1200
1351
|
function pgTypeToKind(dataType, udtName) {
|
|
1201
1352
|
const t = dataType.toLowerCase();
|
|
1202
1353
|
if (t === "user-defined") return "enum";
|
|
@@ -1244,10 +1395,7 @@ async function introspectPostgres(driver) {
|
|
|
1244
1395
|
const isPk = pkSet.has(name);
|
|
1245
1396
|
columns[name] = {
|
|
1246
1397
|
name,
|
|
1247
|
-
type:
|
|
1248
|
-
kind: pgTypeToKind(String(col.data_type), String(col.udt_name)),
|
|
1249
|
-
meta: {}
|
|
1250
|
-
},
|
|
1398
|
+
type: pgTypeToColumnType(String(col.data_type), String(col.udt_name)),
|
|
1251
1399
|
notNull: col.is_nullable === "NO" || isPk,
|
|
1252
1400
|
primaryKey: isPk,
|
|
1253
1401
|
default: null,
|
|
@@ -1303,6 +1451,10 @@ async function postgresUniques(driver, table) {
|
|
|
1303
1451
|
columns: r.cols ?? []
|
|
1304
1452
|
}));
|
|
1305
1453
|
}
|
|
1454
|
+
function describeKind(type) {
|
|
1455
|
+
if (type.kind !== "array") return type.kind;
|
|
1456
|
+
return `${type.meta.element ? describeKind(type.meta.element) : "unknown"}[]`;
|
|
1457
|
+
}
|
|
1306
1458
|
async function checkDriftPostgres(driver, models) {
|
|
1307
1459
|
const actual = await introspectPostgres(driver);
|
|
1308
1460
|
const expected = reflectSchema(models);
|
|
@@ -1319,9 +1471,9 @@ async function checkDriftPostgres(driver, models) {
|
|
|
1319
1471
|
issues.push(`column "${tableName}.${colName}" is missing from the database`);
|
|
1320
1472
|
continue;
|
|
1321
1473
|
}
|
|
1322
|
-
if (expectedCol.type
|
|
1474
|
+
if (describeKind(expectedCol.type) !== describeKind(actualCol.type)) {
|
|
1323
1475
|
issues.push(
|
|
1324
|
-
`column "${tableName}.${colName}" type differs: model ${expectedCol.type
|
|
1476
|
+
`column "${tableName}.${colName}" type differs: model ${describeKind(expectedCol.type)}, db ${describeKind(actualCol.type)}`
|
|
1325
1477
|
);
|
|
1326
1478
|
}
|
|
1327
1479
|
if (expectedCol.notNull !== actualCol.notNull) {
|