@sequenceholdings/orm 0.1.3 → 2.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/README.md +108 -38
- package/dist/authoring.cjs +128 -16
- package/dist/authoring.cjs.map +1 -1
- package/dist/authoring.d.cts +21 -4
- package/dist/authoring.d.ts +21 -4
- package/dist/authoring.js +128 -16
- package/dist/authoring.js.map +1 -1
- package/dist/canonical.cjs +83 -9
- package/dist/canonical.cjs.map +1 -1
- package/dist/canonical.d.cts +47 -1
- package/dist/canonical.d.ts +47 -1
- package/dist/canonical.js +83 -10
- package/dist/canonical.js.map +1 -1
- package/dist/cli.cjs +816 -134
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +818 -136
- package/dist/cli.js.map +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/compiler.cjs +488 -96
- package/dist/compiler.cjs.map +1 -1
- package/dist/compiler.d.cts +34 -2
- package/dist/compiler.d.ts +34 -2
- package/dist/compiler.js +489 -97
- package/dist/compiler.js.map +1 -1
- package/dist/index.cjs +420 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +266 -9
- package/dist/index.d.ts +266 -9
- package/dist/index.js +410 -73
- package/dist/index.js.map +1 -1
- package/dist/ir.cjs +146 -12
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +161 -5
- package/dist/ir.d.ts +161 -5
- package/dist/ir.js +144 -13
- package/dist/ir.js.map +1 -1
- package/dist/{naming-CTvWSRTf.d.ts → naming-DCn94LJ4.d.ts} +15 -1
- package/dist/{naming-DRyinU4u.d.cts → naming-DPglwzkB.d.cts} +15 -1
- package/dist/predicate-v2.cjs +136 -11
- package/dist/predicate-v2.cjs.map +1 -1
- package/dist/predicate-v2.d.cts +3 -0
- package/dist/predicate-v2.d.ts +3 -0
- package/dist/predicate-v2.js +136 -11
- package/dist/predicate-v2.js.map +1 -1
- package/dist/sdl.cjs +460 -13
- package/dist/sdl.cjs.map +1 -1
- package/dist/sdl.d.cts +1 -1
- package/dist/sdl.d.ts +1 -1
- package/dist/sdl.js +459 -14
- package/dist/sdl.js.map +1 -1
- package/package.json +6 -4
- package/src/codegen-gql/preset.cjs +64 -0
- package/src/codegen-gql/server-fns.cjs +11 -6
- package/templates/orm-namespace/codegen.preset.cjs +17 -0
- package/templates/orm-namespace/codegen.ts +1 -1
- package/templates/orm-namespace/package.json +3 -0
- package/templates/orm-namespace/schema/tables.ts +1 -1
- package/templates/orm-namespace/sequence.config.ts +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { sql, eq, notExists } from 'drizzle-orm';
|
|
1
|
+
import { sql, eq, notExists, is, SQL } from 'drizzle-orm';
|
|
2
2
|
import { pgSchema, uniqueIndex, index, foreignKey, primaryKey, PgDialect, QueryBuilder, getViewConfig, extractUsedTable, jsonb, date, timestamp, boolean, doublePrecision, numeric, bigint, integer, text, uuid } from 'drizzle-orm/pg-core';
|
|
3
3
|
import { createHash } from 'crypto';
|
|
4
4
|
import { z } from 'zod';
|
|
@@ -34,6 +34,7 @@ var ColumnBuilder = class {
|
|
|
34
34
|
hasDefault: false,
|
|
35
35
|
defaultValue: void 0,
|
|
36
36
|
defaultFn: void 0,
|
|
37
|
+
onUpdateFn: void 0,
|
|
37
38
|
description: void 0,
|
|
38
39
|
renamedFrom: void 0,
|
|
39
40
|
cast: void 0,
|
|
@@ -69,6 +70,15 @@ var ColumnBuilder = class {
|
|
|
69
70
|
this.state.defaultFn = void 0;
|
|
70
71
|
return this;
|
|
71
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Stamp this column with Postgres `now()` on every UPDATE. Only valid on
|
|
75
|
+
* timestamptz/date; the compiler drops it from SetInput so callers cannot
|
|
76
|
+
* supply a value.
|
|
77
|
+
*/
|
|
78
|
+
onUpdate(value) {
|
|
79
|
+
this.state.onUpdateFn = value;
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
72
82
|
description(text4) {
|
|
73
83
|
this.state.description = text4;
|
|
74
84
|
return this;
|
|
@@ -112,6 +122,7 @@ var ColumnBuilder = class {
|
|
|
112
122
|
};
|
|
113
123
|
if (this.state.hasDefault) column.default = this.state.defaultValue ?? null;
|
|
114
124
|
if (this.state.defaultFn !== void 0) column.defaultFn = this.state.defaultFn;
|
|
125
|
+
if (this.state.onUpdateFn !== void 0) column.onUpdateFn = this.state.onUpdateFn;
|
|
115
126
|
if (this.state.description !== void 0) column.description = this.state.description;
|
|
116
127
|
if (this.state.renamedFrom !== void 0) column.renamedFrom = this.state.renamedFrom;
|
|
117
128
|
if (this.state.cast !== void 0) column.cast = this.state.cast;
|
|
@@ -139,6 +150,7 @@ var t = {
|
|
|
139
150
|
// src/constants.ts
|
|
140
151
|
var NAMESPACE_PATTERN = /^[a-z][a-z0-9_]{0,40}$/;
|
|
141
152
|
var IDENTIFIER_PATTERN = /^[a-z][a-z0-9_]*$/;
|
|
153
|
+
var COLUMN_IDENTIFIER_PATTERN = /^_?[a-z][a-z0-9_]*$/;
|
|
142
154
|
var MAX_IDENTIFIER_LENGTH = 63;
|
|
143
155
|
var ORM_DATA_TOKEN_HEADER = "x-sequence-data-token";
|
|
144
156
|
var ORM_SERVICE_DATA_TOKEN_HEADER = "x-sequence-data-token-service";
|
|
@@ -154,6 +166,8 @@ var DEFINITION_FILE_EXTENSIONS = [".yml", ".yaml"];
|
|
|
154
166
|
var NAMESPACE_CONFIG_FILENAMES = /* @__PURE__ */ new Set(["namespace.yaml", "namespace.yml"]);
|
|
155
167
|
var HASH_DOMAIN = "sequence-orm-definitions\0";
|
|
156
168
|
var MAX_DEFINITION_FILE_BYTES = 1024 * 1024;
|
|
169
|
+
var MAX_GENERATED_SDL_BYTES = 16 * 1024 * 1024;
|
|
170
|
+
var ORM_REGISTRATION_MAX_BYTES = 64 * 1024 * 1024;
|
|
157
171
|
var RESERVED_TABLE_NAMES = /* @__PURE__ */ new Set(["actions", "query"]);
|
|
158
172
|
var RESERVED_COLUMN_NAMES = /* @__PURE__ */ new Set(["select", "order", "limit", "offset"]);
|
|
159
173
|
|
|
@@ -262,10 +276,10 @@ function isSeqLink(value) {
|
|
|
262
276
|
function isSeqTable(value) {
|
|
263
277
|
return typeof value === "object" && value !== null && value.kind === TABLE_BRAND;
|
|
264
278
|
}
|
|
265
|
-
function assertIdentifier({ value, what, file }) {
|
|
266
|
-
if (!
|
|
279
|
+
function assertIdentifier({ value, what, file, pattern = IDENTIFIER_PATTERN }) {
|
|
280
|
+
if (!pattern.test(value) || value.length > MAX_IDENTIFIER_LENGTH) {
|
|
267
281
|
throw new OrmDefinitionError(
|
|
268
|
-
`${what} "${value}" must match ${
|
|
282
|
+
`${what} "${value}" must match ${pattern} and be at most ${MAX_IDENTIFIER_LENGTH} characters`,
|
|
269
283
|
file
|
|
270
284
|
);
|
|
271
285
|
}
|
|
@@ -286,7 +300,7 @@ function defineTable(name, columns, config = {}) {
|
|
|
286
300
|
);
|
|
287
301
|
}
|
|
288
302
|
const columnName = builder.columnNameFor(propertyName);
|
|
289
|
-
assertIdentifier({ value: columnName, what: `table "${name}": column "${propertyName}" column name`, file });
|
|
303
|
+
assertIdentifier({ value: columnName, what: `table "${name}": column "${propertyName}" column name`, file, pattern: COLUMN_IDENTIFIER_PATTERN });
|
|
290
304
|
const existing = seenColumnNames.get(columnName);
|
|
291
305
|
if (existing !== void 0) {
|
|
292
306
|
throw new OrmDefinitionError(
|
|
@@ -326,7 +340,8 @@ function defineSyncedTable(name, columnsOrConfig, maybeConfig) {
|
|
|
326
340
|
assertIdentifier({
|
|
327
341
|
value: columnName,
|
|
328
342
|
what: `synced table "${name}": column "${propertyName}" column name`,
|
|
329
|
-
file
|
|
343
|
+
file,
|
|
344
|
+
pattern: COLUMN_IDENTIFIER_PATTERN
|
|
330
345
|
});
|
|
331
346
|
const existing = seenColumnNames.get(columnName);
|
|
332
347
|
if (existing !== void 0) {
|
|
@@ -415,7 +430,7 @@ function defineView(name, columns, config) {
|
|
|
415
430
|
);
|
|
416
431
|
}
|
|
417
432
|
const columnName = builder.columnNameFor(propertyName);
|
|
418
|
-
if (!
|
|
433
|
+
if (!COLUMN_IDENTIFIER_PATTERN.test(columnName) || columnName.length > MAX_IDENTIFIER_LENGTH) {
|
|
419
434
|
throw new OrmDefinitionError(`view "${name}": column "${propertyName}" has invalid column name "${columnName}"`, file);
|
|
420
435
|
}
|
|
421
436
|
const existing = seenColumnNames.get(columnName);
|
|
@@ -864,6 +879,7 @@ function scalarColumnType(table, property, helper) {
|
|
|
864
879
|
var CANONICAL_FORMAT = 2;
|
|
865
880
|
var IDENTIFIER_RE = /^[a-z][a-z0-9_]*$/;
|
|
866
881
|
var identifier = z.string().min(1).max(63).regex(IDENTIFIER_RE);
|
|
882
|
+
var columnIdentifier = z.string().min(1).max(63).regex(COLUMN_IDENTIFIER_PATTERN);
|
|
867
883
|
var COLUMN_TYPES = [
|
|
868
884
|
"uuid",
|
|
869
885
|
"text",
|
|
@@ -884,18 +900,33 @@ var canonicalEnumSchema = z.object({
|
|
|
884
900
|
var canonicalColumnSchema = z.object({
|
|
885
901
|
/** Property name in TS (camelCase); columnName is the Postgres name. */
|
|
886
902
|
name: z.string().min(1).max(63),
|
|
887
|
-
columnName:
|
|
903
|
+
columnName: columnIdentifier,
|
|
888
904
|
type: z.union([columnTypeSchema, z.object({ enum: identifier })]),
|
|
889
905
|
nullable: z.boolean().default(true),
|
|
890
906
|
unique: z.boolean().default(false),
|
|
891
907
|
/** Literal default, or one of the allowlisted SQL functions. */
|
|
892
908
|
default: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),
|
|
893
909
|
defaultFn: z.enum(["gen_random_uuid()", "now()"]).optional(),
|
|
910
|
+
/**
|
|
911
|
+
* Stamp this column from the database clock on UPDATE (`now()`). The
|
|
912
|
+
* column stays insertable (defaults fill gaps) but is omitted from
|
|
913
|
+
* SetInput / settable so callers cannot backdate it.
|
|
914
|
+
*/
|
|
915
|
+
onUpdateFn: z.enum(["now()"]).optional(),
|
|
894
916
|
description: z.string().max(500).optional(),
|
|
895
917
|
/** Migration hints — same semantics as v1. */
|
|
896
|
-
renamedFrom:
|
|
918
|
+
renamedFrom: columnIdentifier.optional(),
|
|
897
919
|
cast: z.string().max(200).optional(),
|
|
898
920
|
deprecated: z.boolean().default(false)
|
|
921
|
+
}).superRefine((column, ctx) => {
|
|
922
|
+
if (column.onUpdateFn === void 0) return;
|
|
923
|
+
if (typeof column.type !== "string" || column.type !== "timestamptz" && column.type !== "date") {
|
|
924
|
+
ctx.addIssue({
|
|
925
|
+
code: "custom",
|
|
926
|
+
path: ["onUpdateFn"],
|
|
927
|
+
message: `onUpdateFn '${column.onUpdateFn}' is only valid on timestamptz/date columns`
|
|
928
|
+
});
|
|
929
|
+
}
|
|
899
930
|
});
|
|
900
931
|
var canonicalLinkSchema = z.object({
|
|
901
932
|
name: z.string().min(1).max(63),
|
|
@@ -903,15 +934,15 @@ var canonicalLinkSchema = z.object({
|
|
|
903
934
|
/** Target table name (namespace-local). Absent for toPlatformUser. */
|
|
904
935
|
target: identifier.optional(),
|
|
905
936
|
/** FK column on the owning side (toOne/toPlatformUser: on this table; toMany: on the target). */
|
|
906
|
-
on:
|
|
937
|
+
on: columnIdentifier,
|
|
907
938
|
/** Referenced column on the target (default: target's PK / platform user `sub`). */
|
|
908
|
-
references:
|
|
939
|
+
references: columnIdentifier.optional(),
|
|
909
940
|
/** enforce: true emits a real FK constraint (same-namespace toOne only). */
|
|
910
941
|
enforce: z.boolean().default(false),
|
|
911
942
|
description: z.string().max(500).optional()
|
|
912
943
|
});
|
|
913
944
|
var canonicalIndexSchema = z.object({
|
|
914
|
-
columns: z.array(
|
|
945
|
+
columns: z.array(columnIdentifier).min(1),
|
|
915
946
|
unique: z.boolean().default(false),
|
|
916
947
|
name: identifier.optional()
|
|
917
948
|
});
|
|
@@ -1159,6 +1190,13 @@ var canonicalCowMutationSchema = z.object({
|
|
|
1159
1190
|
}
|
|
1160
1191
|
}
|
|
1161
1192
|
});
|
|
1193
|
+
var canonicalForeignKeySchema = z.object({
|
|
1194
|
+
name: identifier,
|
|
1195
|
+
columns: z.array(columnIdentifier).min(1).max(8),
|
|
1196
|
+
target: identifier,
|
|
1197
|
+
references: z.array(columnIdentifier).min(1).max(8),
|
|
1198
|
+
deferrable: z.enum(["initially-immediate", "initially-deferred"]).optional()
|
|
1199
|
+
}).strict();
|
|
1162
1200
|
var canonicalTableSchema = z.object({
|
|
1163
1201
|
table: identifier,
|
|
1164
1202
|
description: z.string().max(2e3).optional(),
|
|
@@ -1167,7 +1205,7 @@ var canonicalTableSchema = z.object({
|
|
|
1167
1205
|
/** Synced/reflected read-only tables (`source: gold.*`) — v1 semantics. */
|
|
1168
1206
|
source: z.string().regex(/^gold\.[a-z][a-z0-9_]*$/).optional(),
|
|
1169
1207
|
columns: z.array(canonicalColumnSchema).default([]),
|
|
1170
|
-
primaryKey: z.array(
|
|
1208
|
+
primaryKey: z.array(columnIdentifier).min(1).max(8).optional(),
|
|
1171
1209
|
links: z.array(canonicalLinkSchema).default([]),
|
|
1172
1210
|
indexes: z.array(canonicalIndexSchema).default([]),
|
|
1173
1211
|
policies: canonicalPoliciesSchema.default({}),
|
|
@@ -1178,14 +1216,16 @@ var canonicalTableSchema = z.object({
|
|
|
1178
1216
|
*/
|
|
1179
1217
|
actions: z.array(canonicalActionSchema).default([]),
|
|
1180
1218
|
/** Optional (not defaulted) so pre-CoW canonical documents keep byte-identical hashes. */
|
|
1181
|
-
cowMutations: z.array(canonicalCowMutationSchema).optional()
|
|
1219
|
+
cowMutations: z.array(canonicalCowMutationSchema).optional(),
|
|
1220
|
+
/** Omitted by old definitions, preserving their existing content hashes. */
|
|
1221
|
+
foreignKeys: z.array(canonicalForeignKeySchema).max(100).optional()
|
|
1182
1222
|
});
|
|
1183
1223
|
var canonicalViewSchema = z.object({
|
|
1184
1224
|
view: identifier,
|
|
1185
1225
|
description: z.string().max(2e3).optional(),
|
|
1186
1226
|
columns: z.array(canonicalColumnSchema).min(1),
|
|
1187
1227
|
/** Logical identity for GraphQL only; PostgreSQL views have no PK constraint. */
|
|
1188
|
-
primaryKey: z.array(
|
|
1228
|
+
primaryKey: z.array(columnIdentifier).min(1).max(8).optional(),
|
|
1189
1229
|
links: z.array(canonicalLinkSchema).default([]),
|
|
1190
1230
|
policies: z.object({ read: policyExpressionSchema }).strict(),
|
|
1191
1231
|
query: z.string().min(1).max(1e5).refine(isSingleViewQuerySql, {
|
|
@@ -1198,6 +1238,9 @@ var canonicalViewSchema = z.object({
|
|
|
1198
1238
|
if (column.default !== void 0 || column.defaultFn !== void 0) {
|
|
1199
1239
|
ctx.addIssue({ code: "custom", path: ["columns", index3], message: `view column '${column.name}' cannot declare a default` });
|
|
1200
1240
|
}
|
|
1241
|
+
if (column.onUpdateFn !== void 0) {
|
|
1242
|
+
ctx.addIssue({ code: "custom", path: ["columns", index3], message: `view column '${column.name}' cannot declare onUpdateFn` });
|
|
1243
|
+
}
|
|
1201
1244
|
if (column.unique) {
|
|
1202
1245
|
ctx.addIssue({ code: "custom", path: ["columns", index3], message: `view column '${column.name}' cannot declare unique` });
|
|
1203
1246
|
}
|
|
@@ -1231,6 +1274,45 @@ var canonicalDefinitionSchema = z.object({
|
|
|
1231
1274
|
ctx.addIssue({ code: "custom", path: ["tables", index3, "table"], message: `duplicate relation '${table.table}'` });
|
|
1232
1275
|
}
|
|
1233
1276
|
seen.add(table.table);
|
|
1277
|
+
if (table.source !== void 0) {
|
|
1278
|
+
for (const [columnIndex, column] of table.columns.entries()) {
|
|
1279
|
+
if (column.onUpdateFn !== void 0) {
|
|
1280
|
+
ctx.addIssue({
|
|
1281
|
+
code: "custom",
|
|
1282
|
+
path: ["tables", index3, "columns", columnIndex],
|
|
1283
|
+
message: `synced table '${table.table}' column '${column.name}' cannot declare onUpdateFn`
|
|
1284
|
+
});
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
const fkTables = new Map(definition.tables.map((table) => [table.table, table]));
|
|
1290
|
+
for (const [tableIndex, table] of definition.tables.entries()) {
|
|
1291
|
+
const names = /* @__PURE__ */ new Set([
|
|
1292
|
+
...table.primaryKey?.length ? [`${table.table}_pkey`] : [],
|
|
1293
|
+
...table.columns.filter((column) => column.unique && !(table.primaryKey?.length === 1 && table.primaryKey[0] === column.columnName)).map((column) => `${table.table}_${column.columnName}_unique`),
|
|
1294
|
+
...table.links.filter((link) => link.enforce).map((link) => `fk_${table.table}_${link.name}`)
|
|
1295
|
+
]);
|
|
1296
|
+
for (const [index3, fk] of (table.foreignKeys ?? []).entries()) {
|
|
1297
|
+
const fail = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "foreignKeys", index3], message });
|
|
1298
|
+
if (names.has(fk.name)) fail(`duplicate foreign key '${fk.name}'`);
|
|
1299
|
+
names.add(fk.name);
|
|
1300
|
+
const target = fkTables.get(fk.target);
|
|
1301
|
+
if (table.source || !target || target.source) {
|
|
1302
|
+
fail("foreign keys must connect owned tables in the same namespace");
|
|
1303
|
+
continue;
|
|
1304
|
+
}
|
|
1305
|
+
if (fk.columns.length !== fk.references.length) fail("foreign key columns and references must have the same number of columns");
|
|
1306
|
+
if (new Set(fk.columns).size !== fk.columns.length || new Set(fk.references).size !== fk.references.length) fail("foreign key columns must not repeat");
|
|
1307
|
+
for (const [position, name] of fk.columns.entries()) {
|
|
1308
|
+
const sourceColumn = table.columns.find((column) => column.columnName === name);
|
|
1309
|
+
const targetColumn = target.columns.find((column) => column.columnName === fk.references[position]);
|
|
1310
|
+
if (!sourceColumn || !targetColumn) fail("foreign key references an unknown column");
|
|
1311
|
+
else if (JSON.stringify(sourceColumn.type) !== JSON.stringify(targetColumn.type)) fail("foreign key column types must match");
|
|
1312
|
+
}
|
|
1313
|
+
const uniqueKeys = [target.primaryKey ?? [], ...target.indexes.filter((index4) => index4.unique).map((index4) => index4.columns), ...target.columns.filter((column) => column.unique).map((column) => [column.columnName])];
|
|
1314
|
+
if (!uniqueKeys.some((key) => key.length === fk.references.length && key.every((column) => fk.references.includes(column)))) fail("foreign key target columns must form a unique key");
|
|
1315
|
+
}
|
|
1234
1316
|
}
|
|
1235
1317
|
const tableByName = new Map(definition.tables.map((table) => [table.table, table]));
|
|
1236
1318
|
for (const [tableIndex, table] of definition.tables.entries()) {
|
|
@@ -1366,12 +1448,13 @@ function normalizeCanonical(definition) {
|
|
|
1366
1448
|
...definition,
|
|
1367
1449
|
enums: sortByName(definition.enums),
|
|
1368
1450
|
tables: [...definition.tables].sort((a, b) => a.table.localeCompare(b.table)).map((t2) => {
|
|
1369
|
-
const { cowMutations, ...rest } = t2;
|
|
1451
|
+
const { cowMutations, foreignKeys, ...rest } = t2;
|
|
1370
1452
|
return {
|
|
1371
1453
|
...rest,
|
|
1372
1454
|
links: sortByName(t2.links),
|
|
1373
1455
|
indexes: [...t2.indexes].sort((a, b) => (a.name ?? a.columns.join("_")).localeCompare(b.name ?? b.columns.join("_"))),
|
|
1374
1456
|
actions: sortByName(t2.actions),
|
|
1457
|
+
...foreignKeys?.length ? { foreignKeys: sortByName(foreignKeys) } : {},
|
|
1375
1458
|
// Omitted entirely when absent/empty so pre-CoW documents hash unchanged.
|
|
1376
1459
|
...cowMutations !== void 0 && cowMutations.length > 0 ? { cowMutations: sortByName(cowMutations) } : {}
|
|
1377
1460
|
};
|
|
@@ -1645,6 +1728,22 @@ function buildCanonicalTable({ table, relationsByName, enumsByName }) {
|
|
|
1645
1728
|
} else if (columnLevelPk.length > 0) {
|
|
1646
1729
|
candidate.primaryKey = columnLevelPk;
|
|
1647
1730
|
}
|
|
1731
|
+
if (table.config.foreignKeys?.length) {
|
|
1732
|
+
if (table.source) throw new OrmDefinitionError(`synced table "${table.tableName}" cannot enforce foreign keys`, table.file);
|
|
1733
|
+
candidate.foreignKeys = table.config.foreignKeys.map((fk) => {
|
|
1734
|
+
const target = resolveRelationRef({ ref: fk.target, fail: (message) => {
|
|
1735
|
+
throw new OrmDefinitionError(message, table.file);
|
|
1736
|
+
} });
|
|
1737
|
+
if (!isSeqTable(target) || !relationsByName.has(target.tableName)) throw new OrmDefinitionError("foreign key target must be a table in this namespace", table.file);
|
|
1738
|
+
return {
|
|
1739
|
+
name: fk.name,
|
|
1740
|
+
columns: fk.columns.map((ref) => resolveColumnRef({ relation: table, ref, role: `foreign key "${fk.name}" column` })),
|
|
1741
|
+
target: target.tableName,
|
|
1742
|
+
references: fk.references.map((ref) => resolveColumnRef({ relation: target, ref, role: `foreign key "${fk.name}" reference` })),
|
|
1743
|
+
...fk.deferrable ? { deferrable: fk.deferrable } : {}
|
|
1744
|
+
};
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1648
1747
|
for (const index3 of table.config.indexes ?? []) {
|
|
1649
1748
|
const entry = {
|
|
1650
1749
|
columns: index3.columns.map((ref) => resolveColumnRef({ relation: table, ref, role: "index" }))
|
|
@@ -1697,6 +1796,12 @@ function buildCanonicalView({
|
|
|
1697
1796
|
if (column.default !== void 0 || column.defaultFn !== void 0) {
|
|
1698
1797
|
throw new OrmDefinitionError(`view "${view.viewName}": column "${propertyName}" cannot declare a default`, view.file);
|
|
1699
1798
|
}
|
|
1799
|
+
if (column.onUpdateFn !== void 0) {
|
|
1800
|
+
throw new OrmDefinitionError(
|
|
1801
|
+
`view "${view.viewName}": column "${propertyName}" cannot declare onUpdateFn`,
|
|
1802
|
+
view.file
|
|
1803
|
+
);
|
|
1804
|
+
}
|
|
1700
1805
|
if (column.unique) {
|
|
1701
1806
|
throw new OrmDefinitionError(`view "${view.viewName}": column "${propertyName}" cannot declare unique`, view.file);
|
|
1702
1807
|
}
|
|
@@ -1955,7 +2060,16 @@ async function loadNamespaceFromDir(dir, options = {}) {
|
|
|
1955
2060
|
const jiti = createJiti(import.meta.url, {
|
|
1956
2061
|
interopDefault: true,
|
|
1957
2062
|
fsCache: false,
|
|
1958
|
-
...options.ormModulePath !== void 0 ? {
|
|
2063
|
+
...options.ormModulePath !== void 0 ? {
|
|
2064
|
+
alias: {
|
|
2065
|
+
// The public /drizzle barrel re-exports this pinned dependency.
|
|
2066
|
+
// Explicitly map it before the root alias: otherwise jiti appends
|
|
2067
|
+
// "/drizzle" to the overridden module's filename.
|
|
2068
|
+
"@sequenceholdings/orm/drizzle": createRequire(import.meta.url).resolve("drizzle-orm"),
|
|
2069
|
+
"@sequenceholdings/orm": options.ormModulePath,
|
|
2070
|
+
"@sequenceholdings/orm/authoring": options.ormModulePath
|
|
2071
|
+
}
|
|
2072
|
+
} : {}
|
|
1959
2073
|
});
|
|
1960
2074
|
const context = beginAuthoringContext();
|
|
1961
2075
|
try {
|
|
@@ -2514,8 +2628,8 @@ var actionSchema = z.strictObject({
|
|
|
2514
2628
|
}
|
|
2515
2629
|
}
|
|
2516
2630
|
});
|
|
2517
|
-
function extractNamedParams(
|
|
2518
|
-
const withoutStrings =
|
|
2631
|
+
function extractNamedParams(sql5) {
|
|
2632
|
+
const withoutStrings = sql5.replace(QUOTED_STRING_PATTERN, "''");
|
|
2519
2633
|
const names = [...withoutStrings.matchAll(NAMED_PARAM_PATTERN)].map((match) => match[1]);
|
|
2520
2634
|
return [...new Set(names)];
|
|
2521
2635
|
}
|
|
@@ -2672,8 +2786,8 @@ var actionParamArg = (name) => `p_${name}`;
|
|
|
2672
2786
|
function compileActionStatements(action, renderParam = actionParamArg) {
|
|
2673
2787
|
return actionStatementSources(action.executes).map((statement) => compileStatement(statement, renderParam));
|
|
2674
2788
|
}
|
|
2675
|
-
function compileStatement(
|
|
2676
|
-
const compiled =
|
|
2789
|
+
function compileStatement(sql5, renderParam) {
|
|
2790
|
+
const compiled = sql5.replace(TOKEN_PATTERN, (token, claimKey, capabilityKey, paramName) => {
|
|
2677
2791
|
if (claimKey !== void 0) {
|
|
2678
2792
|
if (!CLAIM_KEY_PATTERN.test(claimKey)) throw new OrmDefinitionError(`invalid claim key in executes: ${token}`);
|
|
2679
2793
|
return claimExpression(claimKey);
|
|
@@ -2838,8 +2952,8 @@ function actionPrepareChecks(definition) {
|
|
|
2838
2952
|
const order = actionParamOrder(action);
|
|
2839
2953
|
const signature = order.length > 0 ? ` (${order.map((param) => sqlTypeName(action.params[param].type)).join(", ")})` : "";
|
|
2840
2954
|
const compiled = compileActionStatements(action, (param) => `$${order.indexOf(param) + 1}`);
|
|
2841
|
-
compiled.forEach((
|
|
2842
|
-
const checked = action.returns && index3 === compiled.length - 1 ? returnProjection(action.returns,
|
|
2955
|
+
compiled.forEach((sql5, index3) => {
|
|
2956
|
+
const checked = action.returns && index3 === compiled.length - 1 ? returnProjection(action.returns, sql5) : sql5;
|
|
2843
2957
|
statements.push(`PREPARE ${quoteIdentifier(`orm_check_${name}_${index3}`)}${signature} AS ${checked}`);
|
|
2844
2958
|
});
|
|
2845
2959
|
}
|
|
@@ -3293,7 +3407,8 @@ function syncedViewStatements({
|
|
|
3293
3407
|
readPolicyIrByTable,
|
|
3294
3408
|
columnNamesByTable,
|
|
3295
3409
|
columnTypesByTable,
|
|
3296
|
-
executionOwnerRole = SYNCED_VIEW_EXECUTION_OWNER_ROLE
|
|
3410
|
+
executionOwnerRole = SYNCED_VIEW_EXECUTION_OWNER_ROLE,
|
|
3411
|
+
backingPrivileges = "grant"
|
|
3297
3412
|
}) {
|
|
3298
3413
|
const schema = quoteIdentifier(ormSchemaName(definition.namespace));
|
|
3299
3414
|
const role = quoteIdentifier(consumerRoleName(definition.namespace));
|
|
@@ -3327,8 +3442,10 @@ function syncedViewStatements({
|
|
|
3327
3442
|
columnTypes,
|
|
3328
3443
|
columnNames: columnNamesByTable?.get(table.table)
|
|
3329
3444
|
})}` : readPolicy ? ` WHERE ${compilePredicate(readPolicy.ast, narrowToV1ColumnTypes(columnTypes))}` : " WHERE false";
|
|
3330
|
-
|
|
3331
|
-
|
|
3445
|
+
if (backingPrivileges === "grant") {
|
|
3446
|
+
statements.push(`GRANT USAGE ON SCHEMA ${quoteIdentifier(physical.schema)} TO ${owner}`);
|
|
3447
|
+
statements.push(`GRANT SELECT ON ${physicalTarget} TO ${owner}`);
|
|
3448
|
+
}
|
|
3332
3449
|
statements.push(
|
|
3333
3450
|
`CREATE OR REPLACE VIEW ${viewName} AS SELECT * FROM ${physicalTarget}${whereClause}`
|
|
3334
3451
|
);
|
|
@@ -3617,7 +3734,7 @@ var FACTORIES2 = {
|
|
|
3617
3734
|
var SCHEMA_IMPORT_KEY2 = "$schema";
|
|
3618
3735
|
var enumImportKey = (name) => `$enum_${name}`;
|
|
3619
3736
|
var columnTablesV2 = (definition) => definition.tables.filter((table) => !table.source);
|
|
3620
|
-
function buildDrizzleEntitiesV2(definition) {
|
|
3737
|
+
function buildDrizzleEntitiesV2(definition, options = {}) {
|
|
3621
3738
|
const schemaName = ormSchemaName(definition.namespace);
|
|
3622
3739
|
const schema = pgSchema(schemaName);
|
|
3623
3740
|
const imports = { [SCHEMA_IMPORT_KEY2]: schema };
|
|
@@ -3632,11 +3749,19 @@ function buildDrizzleEntitiesV2(definition) {
|
|
|
3632
3749
|
}
|
|
3633
3750
|
const byName = new Map(definition.tables.map((table) => [table.table, table]));
|
|
3634
3751
|
for (const table of sortByEnforcedLinks2(columnTablesV2(definition))) {
|
|
3635
|
-
const drizzleTable = buildTable2(schema, table, built, enums, byName);
|
|
3752
|
+
const drizzleTable = buildTable2(schema, table, built, enums, byName, options.introspection ?? false);
|
|
3636
3753
|
built.set(table.table, drizzleTable);
|
|
3637
3754
|
imports[table.table] = drizzleTable;
|
|
3638
3755
|
tables[table.table] = drizzleTable;
|
|
3639
3756
|
}
|
|
3757
|
+
if (options.introspection) {
|
|
3758
|
+
for (const table of definition.tables.filter((table2) => table2.source)) {
|
|
3759
|
+
const columns = Object.fromEntries(
|
|
3760
|
+
table.columns.map((column) => [column.columnName, buildViewColumnV2({ view: table.table, column, enums })])
|
|
3761
|
+
);
|
|
3762
|
+
imports[table.table] = schema.view(table.table, columns).existing();
|
|
3763
|
+
}
|
|
3764
|
+
}
|
|
3640
3765
|
for (const view of definition.views ?? []) {
|
|
3641
3766
|
const columns = Object.fromEntries(
|
|
3642
3767
|
view.columns.map((column) => [column.columnName, buildViewColumnV2({ view: view.view, column, enums })])
|
|
@@ -3704,18 +3829,18 @@ function rewriteSyncedSourceRelationsInViewQuery({
|
|
|
3704
3829
|
return rewritten;
|
|
3705
3830
|
});
|
|
3706
3831
|
}
|
|
3707
|
-
function transformSqlOutsideLiterals(
|
|
3832
|
+
function transformSqlOutsideLiterals(sql5, transform) {
|
|
3708
3833
|
const out = [];
|
|
3709
3834
|
let state = { kind: "normal" };
|
|
3710
3835
|
let i = 0;
|
|
3711
3836
|
let codeStart = 0;
|
|
3712
3837
|
const emitCode = (end) => {
|
|
3713
|
-
if (end > codeStart) out.push(transform(
|
|
3838
|
+
if (end > codeStart) out.push(transform(sql5.slice(codeStart, end)));
|
|
3714
3839
|
codeStart = end;
|
|
3715
3840
|
};
|
|
3716
|
-
while (i <
|
|
3717
|
-
const c =
|
|
3718
|
-
const next = i + 1 <
|
|
3841
|
+
while (i < sql5.length) {
|
|
3842
|
+
const c = sql5[i];
|
|
3843
|
+
const next = i + 1 < sql5.length ? sql5[i + 1] : "";
|
|
3719
3844
|
switch (state.kind) {
|
|
3720
3845
|
case "normal": {
|
|
3721
3846
|
if (c === "-" && next === "-") {
|
|
@@ -3735,7 +3860,7 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3735
3860
|
break;
|
|
3736
3861
|
}
|
|
3737
3862
|
if ((c === "E" || c === "e") && next === "'") {
|
|
3738
|
-
const prev = i > 0 ?
|
|
3863
|
+
const prev = i > 0 ? sql5[i - 1] : "";
|
|
3739
3864
|
if (!/[A-Za-z0-9_]/.test(prev)) {
|
|
3740
3865
|
emitCode(i);
|
|
3741
3866
|
state = { kind: "e-single" };
|
|
@@ -3754,9 +3879,9 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3754
3879
|
break;
|
|
3755
3880
|
}
|
|
3756
3881
|
if (c === "$") {
|
|
3757
|
-
const prev = i > 0 ?
|
|
3882
|
+
const prev = i > 0 ? sql5[i - 1] : "";
|
|
3758
3883
|
if (!/[A-Za-z0-9_$]/.test(prev)) {
|
|
3759
|
-
const tagMatch = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/.exec(
|
|
3884
|
+
const tagMatch = /^\$([A-Za-z_][A-Za-z0-9_]*)?\$/.exec(sql5.slice(i));
|
|
3760
3885
|
if (tagMatch) {
|
|
3761
3886
|
emitCode(i);
|
|
3762
3887
|
state = { kind: "dollar", tag: tagMatch[1] ?? "" };
|
|
@@ -3788,8 +3913,8 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3788
3913
|
break;
|
|
3789
3914
|
}
|
|
3790
3915
|
case "e-single": {
|
|
3791
|
-
if (c === "\\" && i + 1 <
|
|
3792
|
-
out.push(c,
|
|
3916
|
+
if (c === "\\" && i + 1 < sql5.length) {
|
|
3917
|
+
out.push(c, sql5[i + 1]);
|
|
3793
3918
|
i += 2;
|
|
3794
3919
|
break;
|
|
3795
3920
|
}
|
|
@@ -3811,7 +3936,7 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3811
3936
|
}
|
|
3812
3937
|
case "dollar": {
|
|
3813
3938
|
const closer = `$${state.tag}$`;
|
|
3814
|
-
if (
|
|
3939
|
+
if (sql5.startsWith(closer, i)) {
|
|
3815
3940
|
out.push(closer);
|
|
3816
3941
|
state = { kind: "normal" };
|
|
3817
3942
|
i += closer.length;
|
|
@@ -3855,15 +3980,15 @@ function transformSqlOutsideLiterals(sql4, transform) {
|
|
|
3855
3980
|
}
|
|
3856
3981
|
}
|
|
3857
3982
|
}
|
|
3858
|
-
emitCode(
|
|
3983
|
+
emitCode(sql5.length);
|
|
3859
3984
|
return out.join("");
|
|
3860
3985
|
}
|
|
3861
3986
|
function escapeRegExp(value) {
|
|
3862
3987
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3863
3988
|
}
|
|
3864
|
-
function sqlReferencesQualifiedRelation(
|
|
3989
|
+
function sqlReferencesQualifiedRelation(sql5, sourceSchema, sourceName) {
|
|
3865
3990
|
let referenced = false;
|
|
3866
|
-
transformSqlOutsideLiterals(
|
|
3991
|
+
transformSqlOutsideLiterals(sql5, (segment) => {
|
|
3867
3992
|
if (qualifiedRelationPattern(sourceSchema, sourceName).test(segment)) referenced = true;
|
|
3868
3993
|
return segment;
|
|
3869
3994
|
});
|
|
@@ -3874,15 +3999,16 @@ function qualifiedRelationPattern(sourceSchema, sourceName) {
|
|
|
3874
3999
|
const namePattern = `(?:"${escapeRegExp(sourceName)}"|${escapeRegExp(sourceName)})`;
|
|
3875
4000
|
return new RegExp(`(?<![\\w."])${schemaPattern}\\.${namePattern}(?![\\w"])`, "g");
|
|
3876
4001
|
}
|
|
3877
|
-
function replaceQualifiedRelation(
|
|
3878
|
-
return
|
|
4002
|
+
function replaceQualifiedRelation(sql5, sourceSchema, sourceName, boundSchema, boundTable) {
|
|
4003
|
+
return sql5.replace(qualifiedRelationPattern(sourceSchema, sourceName), `"${boundSchema}"."${boundTable}"`);
|
|
3879
4004
|
}
|
|
3880
4005
|
function generateSnapshotV2(definition, previousId3) {
|
|
3881
4006
|
const { imports, schemaName } = buildDrizzleEntitiesV2(definition);
|
|
3882
4007
|
const snapshot = generateDrizzleJson(imports, previousId3, [schemaName]);
|
|
3883
|
-
if ((definition.views?.length ?? 0) > 0) {
|
|
4008
|
+
if ((definition.views?.length ?? 0) > 0 || definition.tables.some((table) => table.foreignKeys?.length)) {
|
|
3884
4009
|
const extended = snapshot;
|
|
3885
4010
|
extended._sequenceOrm = {
|
|
4011
|
+
...definition.tables.some((table) => table.foreignKeys?.length) ? { foreignKeys: Object.fromEntries(definition.tables.flatMap((table) => (table.foreignKeys ?? []).map((fk) => [`${table.table}.${fk.name}`, structuredClone(fk)]))) } : {},
|
|
3886
4012
|
viewDependencies: Object.fromEntries(
|
|
3887
4013
|
(definition.views ?? []).map((view) => [view.view, [...view.dependencies].sort()])
|
|
3888
4014
|
)
|
|
@@ -3900,7 +4026,7 @@ function buildViewColumnV2({
|
|
|
3900
4026
|
if (!column.nullable) builder = builder.notNull();
|
|
3901
4027
|
return builder;
|
|
3902
4028
|
}
|
|
3903
|
-
function buildTable2(schema, table, built, enums, byName) {
|
|
4029
|
+
function buildTable2(schema, table, built, enums, byName, introspection) {
|
|
3904
4030
|
const primaryKeyColumns = table.primaryKey ?? [];
|
|
3905
4031
|
if (primaryKeyColumns.length === 0) {
|
|
3906
4032
|
throw new OrmDefinitionError(`table '${table.table}' declares no primaryKey`);
|
|
@@ -3916,13 +4042,22 @@ function buildTable2(schema, table, built, enums, byName) {
|
|
|
3916
4042
|
const columns = Object.fromEntries(
|
|
3917
4043
|
table.columns.map((column) => [
|
|
3918
4044
|
column.columnName,
|
|
3919
|
-
buildColumnV2({ table: table.table, column, singlePrimaryKey, compositeMembers, enums })
|
|
4045
|
+
buildColumnV2({ table: table.table, column, singlePrimaryKey, compositeMembers, enums, introspection })
|
|
3920
4046
|
])
|
|
3921
4047
|
);
|
|
3922
4048
|
const drizzleTable = schema.table(table.table, columns, (self) => [
|
|
3923
4049
|
...buildCompositePrimaryKey(table, primaryKeyColumns, self),
|
|
3924
4050
|
...buildIndexes2(table, self),
|
|
3925
|
-
...buildForeignKeys2(table, self, built, byName)
|
|
4051
|
+
...buildForeignKeys2(table, self, built, byName),
|
|
4052
|
+
...(table.foreignKeys ?? []).map((fk) => {
|
|
4053
|
+
const target = fk.target === table.table ? self : built.get(fk.target);
|
|
4054
|
+
if (!target) throw new OrmDefinitionError(`foreign key '${fk.name}' has no target '${fk.target}'`);
|
|
4055
|
+
return foreignKey({
|
|
4056
|
+
name: fk.name,
|
|
4057
|
+
columns: fk.columns.map((column) => self[column]),
|
|
4058
|
+
foreignColumns: fk.references.map((column) => target[column])
|
|
4059
|
+
});
|
|
4060
|
+
})
|
|
3926
4061
|
]);
|
|
3927
4062
|
return drizzleTable.enableRLS();
|
|
3928
4063
|
}
|
|
@@ -3939,7 +4074,8 @@ function buildColumnV2({
|
|
|
3939
4074
|
column,
|
|
3940
4075
|
singlePrimaryKey,
|
|
3941
4076
|
compositeMembers,
|
|
3942
|
-
enums
|
|
4077
|
+
enums,
|
|
4078
|
+
introspection
|
|
3943
4079
|
}) {
|
|
3944
4080
|
const factory = resolveColumnFactory(table, column, enums);
|
|
3945
4081
|
let builder = factory(column.columnName);
|
|
@@ -3960,7 +4096,7 @@ function buildColumnV2({
|
|
|
3960
4096
|
}
|
|
3961
4097
|
builder = builder.default(sql.raw(column.defaultFn));
|
|
3962
4098
|
} else if (column.default !== void 0 && column.default !== null) {
|
|
3963
|
-
builder = builder.default(column.default);
|
|
4099
|
+
builder = introspection && column.type === "jsonb" && typeof column.default === "string" ? builder.default(sql.raw(`${quoteLiteral(column.default)}::jsonb`)) : builder.default(column.default);
|
|
3964
4100
|
}
|
|
3965
4101
|
return builder;
|
|
3966
4102
|
}
|
|
@@ -4145,7 +4281,8 @@ async function planMigrationV2({
|
|
|
4145
4281
|
assertValidViewDependencies(canonical);
|
|
4146
4282
|
const hints = applyMigrationHintsV2(previous, canonical);
|
|
4147
4283
|
const snapshot = generateSnapshotV2(canonical, previousId(previous));
|
|
4148
|
-
const
|
|
4284
|
+
const generatedBaseDdl = await diffStatements(withoutViews(hints.adjusted), withoutViews(snapshot));
|
|
4285
|
+
const baseDdl = orderForeignKeyAdditions(canonical, generatedBaseDdl);
|
|
4149
4286
|
const previousViews = snapshotViews(hints.adjusted);
|
|
4150
4287
|
const currentViews = snapshotViews(snapshot);
|
|
4151
4288
|
const previousDependencies = snapshotViewDependencies(hints.adjusted);
|
|
@@ -4182,6 +4319,7 @@ async function planMigrationV2({
|
|
|
4182
4319
|
...dropOrder.map((name) => ({ kind: "ddl", sql: requiredViewStatement(dropDdl, name, schema, "DROP") })),
|
|
4183
4320
|
...hints.statements.map((statement) => ({ kind: "hint", sql: statement })),
|
|
4184
4321
|
...baseDdl.map((statement) => ({ kind: "ddl", sql: statement.replace(/;\s*$/, "") })),
|
|
4322
|
+
...foreignKeyTimingStatements(canonical, previous, baseDdl).map((sql5) => ({ kind: "ddl", sql: sql5 })),
|
|
4185
4323
|
...createOrder.map((name) => ({ kind: "ddl", sql: requiredViewStatement(createDdl, name, schema, "CREATE") })),
|
|
4186
4324
|
...createOrder.map((name) => {
|
|
4187
4325
|
const view = currentViewDefinitions.get(name);
|
|
@@ -4191,6 +4329,29 @@ async function planMigrationV2({
|
|
|
4191
4329
|
];
|
|
4192
4330
|
return { namespace: canonical.namespace, schema, statements, snapshot };
|
|
4193
4331
|
}
|
|
4332
|
+
function orderForeignKeyAdditions(canonical, statements) {
|
|
4333
|
+
if (!canonical.tables.some((table) => table.foreignKeys?.length)) return [...statements];
|
|
4334
|
+
const addsForeignKey = (statement) => /^ALTER TABLE[\s\S]* ADD CONSTRAINT[\s\S]* FOREIGN KEY\b/.test(statement);
|
|
4335
|
+
return [...statements.filter((statement) => !addsForeignKey(statement)), ...statements.filter(addsForeignKey)];
|
|
4336
|
+
}
|
|
4337
|
+
function foreignKeyTimingStatements(canonical, previous, baseDdl) {
|
|
4338
|
+
const prior = previous._sequenceOrm?.foreignKeys ?? {};
|
|
4339
|
+
const result = [];
|
|
4340
|
+
for (const table of canonical.tables) {
|
|
4341
|
+
for (const fk of table.foreignKeys ?? []) {
|
|
4342
|
+
const before = prior[`${table.table}.${fk.name}`];
|
|
4343
|
+
const recreated = baseDdl.some((statement) => statement.includes(`ALTER TABLE ${quoteIdentifier(ormSchemaName(canonical.namespace))}.${quoteIdentifier(table.table)} ADD CONSTRAINT ${quoteIdentifier(fk.name)}`));
|
|
4344
|
+
if (fk.deferrable ? !before || before.deferrable !== fk.deferrable || recreated : before?.deferrable && !recreated) {
|
|
4345
|
+
result.push(foreignKeyTimingSql(ormSchemaName(canonical.namespace), table.table, fk.name, fk.deferrable));
|
|
4346
|
+
}
|
|
4347
|
+
}
|
|
4348
|
+
}
|
|
4349
|
+
return result;
|
|
4350
|
+
}
|
|
4351
|
+
function foreignKeyTimingSql(schema, table, name, timing) {
|
|
4352
|
+
const clause = timing === void 0 ? "NOT DEFERRABLE" : timing === "initially-deferred" ? "DEFERRABLE INITIALLY DEFERRED" : "DEFERRABLE INITIALLY IMMEDIATE";
|
|
4353
|
+
return `ALTER TABLE ${quoteIdentifier(schema)}.${quoteIdentifier(table)} ALTER CONSTRAINT ${quoteIdentifier(name)} ${clause}`;
|
|
4354
|
+
}
|
|
4194
4355
|
function validateViewCatalogStatement({
|
|
4195
4356
|
canonical,
|
|
4196
4357
|
view,
|
|
@@ -4445,7 +4606,63 @@ async function pendingKitStatementsV2({
|
|
|
4445
4606
|
canonical,
|
|
4446
4607
|
db
|
|
4447
4608
|
}) {
|
|
4448
|
-
|
|
4609
|
+
const { desired, defaultDrift } = await normalizeJsonDefaultsForIntrospection(canonical, db);
|
|
4610
|
+
const entities = buildDrizzleEntitiesV2(desired, { introspection: true });
|
|
4611
|
+
const pending = await pendingStatements({ entities, db, owned: V2_OWNED_STATEMENT });
|
|
4612
|
+
return [...orderForeignKeyAdditions(canonical, pending), ...defaultDrift, ...await pendingForeignKeyTimingStatements(canonical, db)];
|
|
4613
|
+
}
|
|
4614
|
+
async function normalizeJsonDefaultsForIntrospection(canonical, db) {
|
|
4615
|
+
const defaults = canonical.tables.flatMap((table, tableIndex) => table.source ? [] : table.columns.flatMap((column, columnIndex) => column.type === "jsonb" && typeof column.default === "string" ? [{ tableIndex, columnIndex, table: table.table, column: column.columnName, value: column.default }] : []));
|
|
4616
|
+
if (defaults.length === 0) return { desired: canonical, defaultDrift: [] };
|
|
4617
|
+
const array = (values) => sql`ARRAY[${sql.join(values.map((value) => sql`${value}`), sql`, `)}]::text[]`;
|
|
4618
|
+
const schema = ormSchemaName(canonical.namespace);
|
|
4619
|
+
const result = await shimExecute(db).execute(sql`
|
|
4620
|
+
SELECT desired.ordinal, desired.value::jsonb::text AS normalized,
|
|
4621
|
+
attribute.attnum IS NOT NULL AS column_exists,
|
|
4622
|
+
coalesce(pg_get_expr(ad.adbin, ad.adrelid) = quote_literal(desired.value::jsonb::text) || '::jsonb', false) AS matches
|
|
4623
|
+
FROM unnest(${array(defaults.map((item) => item.table))}, ${array(defaults.map((item) => item.column))}, ${array(defaults.map((item) => item.value))})
|
|
4624
|
+
WITH ORDINALITY AS desired(table_name, column_name, value, ordinal)
|
|
4625
|
+
LEFT JOIN pg_catalog.pg_namespace namespace ON namespace.nspname = ${schema}
|
|
4626
|
+
LEFT JOIN pg_catalog.pg_class relation ON relation.relnamespace = namespace.oid AND relation.relname = desired.table_name
|
|
4627
|
+
LEFT JOIN pg_catalog.pg_attribute attribute ON attribute.attrelid = relation.oid AND attribute.attname = desired.column_name AND NOT attribute.attisdropped
|
|
4628
|
+
LEFT JOIN pg_catalog.pg_attrdef ad ON ad.adrelid = relation.oid AND ad.adnum = attribute.attnum
|
|
4629
|
+
ORDER BY desired.ordinal
|
|
4630
|
+
`);
|
|
4631
|
+
const rows = result.rows;
|
|
4632
|
+
if (!Array.isArray(rows) || rows.length !== defaults.length || rows.some((row, index3) => Number(row.ordinal) !== index3 + 1 || typeof row.normalized !== "string" || typeof row.column_exists !== "boolean" || typeof row.matches !== "boolean")) {
|
|
4633
|
+
throw new Error("invalid JSON default introspection result");
|
|
4634
|
+
}
|
|
4635
|
+
const desired = structuredClone(canonical);
|
|
4636
|
+
const defaultDrift = [];
|
|
4637
|
+
defaults.forEach((item, index3) => {
|
|
4638
|
+
const row = rows[index3];
|
|
4639
|
+
desired.tables[item.tableIndex].columns[item.columnIndex].default = JSON.stringify(JSON.parse(row.normalized));
|
|
4640
|
+
if (row.column_exists && !row.matches) defaultDrift.push(
|
|
4641
|
+
`ALTER TABLE ${quoteIdentifier(schema)}.${quoteIdentifier(item.table)} ALTER COLUMN ${quoteIdentifier(item.column)} SET DEFAULT ${quoteLiteral(item.value)}::jsonb;`
|
|
4642
|
+
);
|
|
4643
|
+
});
|
|
4644
|
+
return { desired, defaultDrift };
|
|
4645
|
+
}
|
|
4646
|
+
async function pendingForeignKeyTimingStatements(canonical, db) {
|
|
4647
|
+
const keys = canonical.tables.flatMap((table) => (table.foreignKeys ?? []).map((fk) => ({ table: table.table, fk })));
|
|
4648
|
+
if (keys.length === 0) return [];
|
|
4649
|
+
const schema = ormSchemaName(canonical.namespace);
|
|
4650
|
+
const result = await shimExecute(db).execute(sql`
|
|
4651
|
+
SELECT relation.relname AS table_name, constraint_row.conname AS name,
|
|
4652
|
+
constraint_row.condeferrable AS deferrable, constraint_row.condeferred AS deferred
|
|
4653
|
+
FROM pg_catalog.pg_constraint AS constraint_row
|
|
4654
|
+
JOIN pg_catalog.pg_class AS relation ON relation.oid = constraint_row.conrelid
|
|
4655
|
+
JOIN pg_catalog.pg_namespace AS namespace ON namespace.oid = relation.relnamespace
|
|
4656
|
+
WHERE namespace.nspname = ${schema} AND constraint_row.contype = 'f'
|
|
4657
|
+
`);
|
|
4658
|
+
const rows = result.rows;
|
|
4659
|
+
return keys.flatMap(({ table, fk }) => {
|
|
4660
|
+
const actual = rows.find((row) => row.table_name === table && row.name === fk.name);
|
|
4661
|
+
const deferrable = fk.deferrable !== void 0;
|
|
4662
|
+
const deferred = fk.deferrable === "initially-deferred";
|
|
4663
|
+
if (actual ? actual.deferrable === deferrable && actual.deferred === deferred : !deferrable) return [];
|
|
4664
|
+
return [foreignKeyTimingSql(schema, table, fk.name, fk.deferrable)];
|
|
4665
|
+
});
|
|
4449
4666
|
}
|
|
4450
4667
|
async function pendingStatements({
|
|
4451
4668
|
entities,
|
|
@@ -4458,11 +4675,31 @@ async function pendingStatements({
|
|
|
4458
4675
|
);
|
|
4459
4676
|
return statementsToExecute.filter((statement) => !owned.test(statement));
|
|
4460
4677
|
}
|
|
4678
|
+
function correctIntrospectionQuery(query) {
|
|
4679
|
+
if (!is(query, SQL)) return query;
|
|
4680
|
+
const compiled = new PgDialect().sqlToQuery(query);
|
|
4681
|
+
let text4 = compiled.sql;
|
|
4682
|
+
const indexJoin = "pg_constraint con ON con.conindid = idx.indexrelid";
|
|
4683
|
+
if (text4.includes("AS generated_by_constraint") && text4.includes(indexJoin)) {
|
|
4684
|
+
text4 = text4.replace(indexJoin, `${indexJoin} AND con.contype IN ('p', 'u', 'x')`);
|
|
4685
|
+
}
|
|
4686
|
+
const sourceJoin = "LEFT JOIN pg_catalog.pg_attribute att ON att.attnum = ANY (con.conkey)";
|
|
4687
|
+
const targetJoin = "LEFT JOIN pg_catalog.pg_attribute fatt ON fatt.attnum = ANY (con.confkey)";
|
|
4688
|
+
if (text4.includes(sourceJoin) && text4.includes(targetJoin) && text4.includes("AND con.contype IN ('f')")) {
|
|
4689
|
+
text4 = text4.replace(
|
|
4690
|
+
sourceJoin,
|
|
4691
|
+
"JOIN LATERAL unnest(con.conkey, con.confkey) WITH ORDINALITY AS fk_key(source_num, target_num, ordinal) ON true LEFT JOIN pg_catalog.pg_attribute att ON att.attnum = fk_key.source_num"
|
|
4692
|
+
).replace(targetJoin, "LEFT JOIN pg_catalog.pg_attribute fatt ON fatt.attnum = fk_key.target_num").replace(/;\s*$/, "") + " ORDER BY con.conname, fk_key.ordinal";
|
|
4693
|
+
}
|
|
4694
|
+
if (text4 === compiled.sql) return query;
|
|
4695
|
+
if (compiled.params.length !== 0) throw new Error("parameterized Drizzle catalog query cannot be normalized");
|
|
4696
|
+
return sql.raw(text4);
|
|
4697
|
+
}
|
|
4461
4698
|
function shimExecute(db) {
|
|
4462
4699
|
const execute = db.execute.bind(db);
|
|
4463
4700
|
return {
|
|
4464
4701
|
execute: async (query) => {
|
|
4465
|
-
const result = await execute(query);
|
|
4702
|
+
const result = await execute(correctIntrospectionQuery(query));
|
|
4466
4703
|
if (result && typeof result === "object" && "rows" in result) return result;
|
|
4467
4704
|
return { rows: result };
|
|
4468
4705
|
}
|
|
@@ -4502,21 +4739,73 @@ async function planMigration({
|
|
|
4502
4739
|
const ddl = await diffStatements(hints.adjusted, snapshot);
|
|
4503
4740
|
const policyDrops = policyDropStatements(schema, snapshotTableNames(previous, schema));
|
|
4504
4741
|
const statements = [
|
|
4505
|
-
...policyDrops.map((
|
|
4506
|
-
...hints.statements.map((
|
|
4507
|
-
...ddl.map((
|
|
4508
|
-
...securityStatements(definition).map((
|
|
4742
|
+
...policyDrops.map((sql5) => ({ kind: "security", sql: sql5 })),
|
|
4743
|
+
...hints.statements.map((sql5) => ({ kind: "hint", sql: sql5 })),
|
|
4744
|
+
...ddl.map((sql5) => ({ kind: "ddl", sql: sql5.replace(/;\s*$/, "") })),
|
|
4745
|
+
...securityStatements(definition).map((sql5) => ({ kind: "security", sql: sql5 })),
|
|
4509
4746
|
// PREPARE each action's executes first (plpgsql bodies don't check columns
|
|
4510
4747
|
// at CREATE), then create the functions. Both reference the tables the DDL
|
|
4511
4748
|
// above creates, so they close the plan.
|
|
4512
|
-
...actionPrepareChecks(definition).map((
|
|
4513
|
-
...actionFunctionStatements(definition).map((
|
|
4749
|
+
...actionPrepareChecks(definition).map((sql5) => ({ kind: "function", sql: sql5 })),
|
|
4750
|
+
...actionFunctionStatements(definition).map((sql5) => ({ kind: "function", sql: sql5 }))
|
|
4514
4751
|
];
|
|
4515
4752
|
return { namespace: definition.namespace, schema, statements, snapshot };
|
|
4516
4753
|
}
|
|
4517
4754
|
var compileNamespace = (definition) => planMigration({ definition });
|
|
4518
4755
|
var previousId2 = (snapshot) => snapshot.id;
|
|
4519
|
-
var
|
|
4756
|
+
var ORM_MIGRATION_EXPORT_FILE = "orm.migration.json";
|
|
4757
|
+
var ORM_MIGRATION_EXPORT_MAX_BYTES = ORM_REGISTRATION_MAX_BYTES;
|
|
4758
|
+
var hash = z.string().regex(/^sha256:[0-9a-f]{64}$/);
|
|
4759
|
+
var ormMigrationPayloadSchema = z.object({
|
|
4760
|
+
canonical_definition: canonicalDefinitionSchema,
|
|
4761
|
+
documents: z.array(z.object({ path: z.string().min(1).max(2048), contents: z.string().max(1e6) }).strict()).max(5e3),
|
|
4762
|
+
client_manifest: z.object({ ops_hash: hash, operations: z.record(z.string(), hash) }).strict(),
|
|
4763
|
+
sdl: z.string().refine((value) => Buffer.byteLength(value, "utf8") <= MAX_GENERATED_SDL_BYTES, {
|
|
4764
|
+
message: `sdl exceeds ${MAX_GENERATED_SDL_BYTES} bytes`
|
|
4765
|
+
}),
|
|
4766
|
+
schema_snapshot: z.json(),
|
|
4767
|
+
// The existing ORM committed-migration validator and replay engine remain
|
|
4768
|
+
// authoritative. This envelope hashes the provenance, never authorizes SQL.
|
|
4769
|
+
migration: z.json().optional()
|
|
4770
|
+
}).strict();
|
|
4771
|
+
var ormMigrationExportSchema = z.object({
|
|
4772
|
+
schema_version: z.literal(1),
|
|
4773
|
+
schema_hash: hash,
|
|
4774
|
+
payload: ormMigrationPayloadSchema,
|
|
4775
|
+
checksum: z.string().regex(/^[0-9a-f]{64}$/)
|
|
4776
|
+
}).strict();
|
|
4777
|
+
function checksum(value) {
|
|
4778
|
+
return createHash("sha256").update(stableJson(value)).digest("hex");
|
|
4779
|
+
}
|
|
4780
|
+
function createOrmMigrationExport({ schemaHash, payload }) {
|
|
4781
|
+
const contents = { schema_version: 1, schema_hash: hash.parse(schemaHash), payload: ormMigrationPayloadSchema.parse(payload) };
|
|
4782
|
+
return { ...contents, checksum: checksum(contents) };
|
|
4783
|
+
}
|
|
4784
|
+
function parseOrmMigrationExport(text4) {
|
|
4785
|
+
if (Buffer.byteLength(text4, "utf8") > ORM_MIGRATION_EXPORT_MAX_BYTES) throw new Error("ORM migration export exceeds the size limit");
|
|
4786
|
+
const exported = ormMigrationExportSchema.parse(JSON.parse(text4));
|
|
4787
|
+
const { checksum: expected, ...contents } = exported;
|
|
4788
|
+
if (checksum(contents) !== expected) throw new Error("ORM migration export checksum mismatch; regenerate and commit it");
|
|
4789
|
+
return exported;
|
|
4790
|
+
}
|
|
4791
|
+
function renderOrmMigrationExport(exported) {
|
|
4792
|
+
const text4 = `${JSON.stringify(exported, null, 2)}
|
|
4793
|
+
`;
|
|
4794
|
+
if (Buffer.byteLength(text4, "utf8") > ORM_MIGRATION_EXPORT_MAX_BYTES) throw new Error("ORM migration export exceeds the size limit");
|
|
4795
|
+
return text4;
|
|
4796
|
+
}
|
|
4797
|
+
var IR_VERSION = 2;
|
|
4798
|
+
var LEGACY_IR_VERSION = 1;
|
|
4799
|
+
var irVersionSchema = z.union([z.literal(LEGACY_IR_VERSION), z.literal(IR_VERSION)]);
|
|
4800
|
+
var operationVersionSchema = z.object({ irVersion: irVersionSchema });
|
|
4801
|
+
function requiredArtifactIrVersion(operations) {
|
|
4802
|
+
let required = LEGACY_IR_VERSION;
|
|
4803
|
+
for (const operation of operations) {
|
|
4804
|
+
const { irVersion } = operationVersionSchema.parse(operation);
|
|
4805
|
+
if (irVersion > required) required = irVersion;
|
|
4806
|
+
}
|
|
4807
|
+
return required;
|
|
4808
|
+
}
|
|
4520
4809
|
var CLAIMS_CONTRACT = {
|
|
4521
4810
|
sub: "string",
|
|
4522
4811
|
org: "string?",
|
|
@@ -4643,6 +4932,33 @@ var aggregateRootSchema = z.object({
|
|
|
4643
4932
|
nodes: selectionIrSchema.optional(),
|
|
4644
4933
|
includeIf: includeIfSchema.optional()
|
|
4645
4934
|
});
|
|
4935
|
+
var DATE_BUCKETS = ["year", "quarter", "month", "week", "day", "hour"];
|
|
4936
|
+
var groupByKeyIrSchema = z.object({
|
|
4937
|
+
column: z.string(),
|
|
4938
|
+
bucket: z.enum(DATE_BUCKETS).optional()
|
|
4939
|
+
});
|
|
4940
|
+
var aggregateGroupsRootSchema = z.object({
|
|
4941
|
+
kind: z.literal("aggregateGroups"),
|
|
4942
|
+
alias: z.string(),
|
|
4943
|
+
table: z.string(),
|
|
4944
|
+
/** Composed at compile time: AND(policy, documentWhere?, dynamicFilter?). */
|
|
4945
|
+
where: predIrSchema,
|
|
4946
|
+
groupBy: z.array(groupByKeyIrSchema).min(1),
|
|
4947
|
+
/**
|
|
4948
|
+
* The `key { … }` projection: which group columns appear in each entry's key
|
|
4949
|
+
* object, in selection order. A subset of `groupBy` (the compiler rejects a
|
|
4950
|
+
* key field that is not a group key — it would be indeterminate within the
|
|
4951
|
+
* group). Empty when the document selects only `aggregate`.
|
|
4952
|
+
*/
|
|
4953
|
+
key: z.array(z.object({ alias: z.string(), column: z.string() })).default([]),
|
|
4954
|
+
count: z.object({ columns: z.array(z.string()).optional(), distinct: z.boolean().optional() }).optional(),
|
|
4955
|
+
sum: z.array(z.string()).optional(),
|
|
4956
|
+
avg: z.array(z.string()).optional(),
|
|
4957
|
+
/** Bounds the GROUP count, not the scanned rows — see MAX_GROUPS. */
|
|
4958
|
+
limit: limitIrSchema,
|
|
4959
|
+
offset: z.object({ variable: z.string().optional(), value: z.number().int().optional() }).optional(),
|
|
4960
|
+
includeIf: includeIfSchema.optional()
|
|
4961
|
+
});
|
|
4646
4962
|
var writeValuesSchema = z.union([
|
|
4647
4963
|
z.object({ slot: z.string() }),
|
|
4648
4964
|
z.object({ entries: z.array(z.object({ column: z.string(), value: valueIrSchema })) })
|
|
@@ -4688,12 +5004,15 @@ var mutationRootSchema = z.object({
|
|
|
4688
5004
|
insertCheck: predIrSchema.optional(),
|
|
4689
5005
|
returning: selectionIrSchema.nullable(),
|
|
4690
5006
|
affectedRows: z.boolean().default(false),
|
|
5007
|
+
/** v2: abort the transaction when the actual write count differs, regardless of selection. */
|
|
5008
|
+
assertAffectedRows: z.number().int().min(0).max(2147483647).optional(),
|
|
4691
5009
|
includeIf: includeIfSchema.optional()
|
|
4692
5010
|
});
|
|
4693
5011
|
var rootIrSchema = z.discriminatedUnion("kind", [
|
|
4694
5012
|
selectRootSchema,
|
|
4695
5013
|
byPkRootSchema,
|
|
4696
5014
|
aggregateRootSchema,
|
|
5015
|
+
aggregateGroupsRootSchema,
|
|
4697
5016
|
mutationRootSchema
|
|
4698
5017
|
]);
|
|
4699
5018
|
var INPUT_REF_KINDS = [
|
|
@@ -4723,7 +5042,7 @@ var variableIrSchema = z.object({
|
|
|
4723
5042
|
defaultValue: variableDefaultIrSchema.optional()
|
|
4724
5043
|
});
|
|
4725
5044
|
var compiledOperationSchema = z.object({
|
|
4726
|
-
irVersion:
|
|
5045
|
+
irVersion: irVersionSchema,
|
|
4727
5046
|
name: z.string(),
|
|
4728
5047
|
opHash: z.string().regex(/^sha256:[0-9a-f]{64}$/),
|
|
4729
5048
|
kind: z.enum(["query", "mutation"]),
|
|
@@ -4735,6 +5054,13 @@ var compiledOperationSchema = z.object({
|
|
|
4735
5054
|
/** Every table the op touches — data-token scoping + audit. */
|
|
4736
5055
|
tableSet: z.array(z.string()).min(1),
|
|
4737
5056
|
roots: z.array(rootIrSchema).min(1)
|
|
5057
|
+
}).superRefine((operation, ctx) => {
|
|
5058
|
+
for (const [index3, root] of operation.roots.entries()) {
|
|
5059
|
+
if (root.kind !== "mutation" || root.assertAffectedRows === void 0) continue;
|
|
5060
|
+
if (operation.irVersion < 2 || operation.kind !== "mutation" || root.includeIf !== void 0) {
|
|
5061
|
+
ctx.addIssue({ code: "custom", path: ["roots", index3], message: "affected-row assertions require unconditional mutation roots and IR v2" });
|
|
5062
|
+
}
|
|
5063
|
+
}
|
|
4738
5064
|
});
|
|
4739
5065
|
var tableMetaIrSchema = z.object({
|
|
4740
5066
|
table: z.string(),
|
|
@@ -4771,18 +5097,29 @@ var tableMetaIrSchema = z.object({
|
|
|
4771
5097
|
insertable: z.array(z.string()),
|
|
4772
5098
|
settable: z.array(z.string()),
|
|
4773
5099
|
appendable: z.array(z.string()),
|
|
5100
|
+
/**
|
|
5101
|
+
* Logical columns stamped with Postgres `now()` on UPDATE (canonical
|
|
5102
|
+
* `onUpdateFn`). Omitted when empty so pre-onUpdate artifacts still parse.
|
|
5103
|
+
*/
|
|
5104
|
+
touchOnUpdate: z.array(z.string()).optional(),
|
|
4774
5105
|
constraints: z.array(z.object({ name: z.string(), columns: z.array(z.string()) })),
|
|
4775
5106
|
/** Depth/node caps for dynamic filter trees on this table. */
|
|
4776
5107
|
dynamicFilterMaxDepth: z.number().int().positive().default(6),
|
|
4777
5108
|
dynamicFilterMaxNodes: z.number().int().positive().default(64)
|
|
4778
5109
|
});
|
|
4779
5110
|
var compiledArtifactSchema = z.object({
|
|
4780
|
-
irVersion:
|
|
5111
|
+
irVersion: irVersionSchema,
|
|
4781
5112
|
namespace: z.string(),
|
|
4782
5113
|
schemaHash: z.string(),
|
|
4783
5114
|
opsHash: z.string(),
|
|
4784
5115
|
tables: z.record(z.string(), tableMetaIrSchema),
|
|
4785
5116
|
operations: z.record(z.string(), compiledOperationSchema)
|
|
5117
|
+
}).superRefine((artifact, ctx) => {
|
|
5118
|
+
for (const [name, operation] of Object.entries(artifact.operations)) {
|
|
5119
|
+
if (operation.irVersion > artifact.irVersion) {
|
|
5120
|
+
ctx.addIssue({ code: "custom", path: ["operations", name, "irVersion"], message: "operation requires a newer IR version than its artifact" });
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
4786
5123
|
});
|
|
4787
5124
|
var IrVersionError = class extends Error {
|
|
4788
5125
|
constructor(found) {
|
|
@@ -4793,7 +5130,7 @@ var IrVersionError = class extends Error {
|
|
|
4793
5130
|
function normalizeIr(raw) {
|
|
4794
5131
|
if (!raw || typeof raw !== "object") throw new IrVersionError(void 0);
|
|
4795
5132
|
const version = raw.irVersion;
|
|
4796
|
-
if (version !== IR_VERSION) throw new IrVersionError(version);
|
|
5133
|
+
if (version !== LEGACY_IR_VERSION && version !== IR_VERSION) throw new IrVersionError(version);
|
|
4797
5134
|
return compiledArtifactSchema.parse(raw);
|
|
4798
5135
|
}
|
|
4799
5136
|
function indexByHash(artifact) {
|
|
@@ -5109,12 +5446,12 @@ async function writeMigration({
|
|
|
5109
5446
|
const tip = workspace.journal.entries.at(-1);
|
|
5110
5447
|
const ordinal = (tip?.ordinal ?? 0) + 1;
|
|
5111
5448
|
const name = migrationName(ordinal, contentHash);
|
|
5112
|
-
const
|
|
5449
|
+
const sql5 = renderMigrationSql({ name, namespace: workspace.journal.namespace, contentHash, plan });
|
|
5113
5450
|
const metaDir = join(dir, MIGRATIONS_DIRNAME, META_DIRNAME);
|
|
5114
5451
|
await mkdir(metaDir, { recursive: true });
|
|
5115
5452
|
const snapshotText = `${JSON.stringify(plan.snapshot, null, 2)}
|
|
5116
5453
|
`;
|
|
5117
|
-
await writeFile(join(dir, MIGRATIONS_DIRNAME, `${name}.sql`),
|
|
5454
|
+
await writeFile(join(dir, MIGRATIONS_DIRNAME, `${name}.sql`), sql5);
|
|
5118
5455
|
await writeFile(join(metaDir, `${pad(ordinal)}.snapshot.json`), snapshotText);
|
|
5119
5456
|
const journal = {
|
|
5120
5457
|
...workspace.journal,
|
|
@@ -5125,7 +5462,7 @@ async function writeMigration({
|
|
|
5125
5462
|
name,
|
|
5126
5463
|
content_hash: contentHash,
|
|
5127
5464
|
previous_content_hash: tip?.content_hash ?? null,
|
|
5128
|
-
checksum: checksumOf(
|
|
5465
|
+
checksum: checksumOf(sql5),
|
|
5129
5466
|
snapshot_checksum: checksumOf(snapshotText),
|
|
5130
5467
|
destructive
|
|
5131
5468
|
}
|
|
@@ -5240,8 +5577,8 @@ async function checkWorkspace({
|
|
|
5240
5577
|
let previousId3;
|
|
5241
5578
|
let previousHash = null;
|
|
5242
5579
|
for (const entry of workspace.journal.entries) {
|
|
5243
|
-
const
|
|
5244
|
-
if (checksumOf(
|
|
5580
|
+
const sql5 = await readMigrationSql(dir, entry);
|
|
5581
|
+
if (checksumOf(sql5) !== entry.checksum) {
|
|
5245
5582
|
log(`${MIGRATIONS_DIRNAME}/${entry.name}.sql does not match its journal checksum \u2014 committed migrations must not be edited`);
|
|
5246
5583
|
return 1;
|
|
5247
5584
|
}
|
|
@@ -5281,7 +5618,7 @@ async function checkWorkspace({
|
|
|
5281
5618
|
log(`migrations in sync \u2014 ${workspace.journal.entries.length} committed, tip ${tip.name}`);
|
|
5282
5619
|
return 0;
|
|
5283
5620
|
}
|
|
5284
|
-
var firstLine = (
|
|
5621
|
+
var firstLine = (sql5) => sql5.split("\n")[0] ?? sql5;
|
|
5285
5622
|
|
|
5286
5623
|
// src/migrations/from-yaml.ts
|
|
5287
5624
|
function convertV1DefinitionToCanonical(definition) {
|
|
@@ -5387,7 +5724,7 @@ function buildRebaseJournalEntry({ namespace, previousHash, newHash, ordinal, sn
|
|
|
5387
5724
|
throw new OrmDefinitionError(`rebase ordinal must be an integer \u2265 2 (a rebase sits on an existing journal tip); got ${ordinal}`);
|
|
5388
5725
|
}
|
|
5389
5726
|
const name = migrationName(ordinal, newHash);
|
|
5390
|
-
const
|
|
5727
|
+
const sql5 = renderRebaseMigrationSql({ name, namespace, previousHash, newHash });
|
|
5391
5728
|
const snapshotText = `${JSON.stringify(snapshot, null, 2)}
|
|
5392
5729
|
`;
|
|
5393
5730
|
const entry = {
|
|
@@ -5395,11 +5732,11 @@ function buildRebaseJournalEntry({ namespace, previousHash, newHash, ordinal, sn
|
|
|
5395
5732
|
name,
|
|
5396
5733
|
content_hash: newHash,
|
|
5397
5734
|
previous_content_hash: previousHash,
|
|
5398
|
-
checksum: checksumOf(
|
|
5735
|
+
checksum: checksumOf(sql5),
|
|
5399
5736
|
snapshot_checksum: checksumOf(snapshotText),
|
|
5400
5737
|
destructive: false
|
|
5401
5738
|
};
|
|
5402
|
-
return { name, entry, sql:
|
|
5739
|
+
return { name, entry, sql: sql5, snapshotText };
|
|
5403
5740
|
}
|
|
5404
5741
|
function renderRebaseMigrationSql({
|
|
5405
5742
|
name,
|
|
@@ -5497,6 +5834,6 @@ function canonicalActions(value) {
|
|
|
5497
5834
|
);
|
|
5498
5835
|
}
|
|
5499
5836
|
|
|
5500
|
-
export { ACTION_REQUIRES_DENIED_SQLSTATE, ACTION_VALIDATES_FAILED_SQLSTATE, AUDIT_ACTIONS, CANONICAL_FORMAT, CAPABILITIES_CLAIM_KEY, CAPABILITY_KEY_PATTERN, CLAIMS_CONTRACT, CLAIMS_SETTING, CLAIM_KEY_PATTERN, CMP_OPS, COLUMN_BRAND, COLUMN_DEFAULT_FNS, COLUMN_TYPES2 as COLUMN_TYPES, COMPARE_OPS, CONSUMER_ROLE_SUFFIX, ColumnBuilder, DEFINITION_FILE_EXTENSIONS, ENUM_BRAND, HASH_DOMAIN, IDENTIFIER_PATTERN, IDENTIFIER_RE, INPUT_REF_KINDS, IR_VERSION, IrVersionError, LINK_BRAND, MAX_DEFINITION_FILE_BYTES, MAX_IDENTIFIER_LENGTH, MIGRATIONS_DIRNAME, NAMESPACE_CONFIG_BRAND, NAMESPACE_CONFIG_FILE, NAMESPACE_CONFIG_FILENAMES, NAMESPACE_PATTERN, ORM_DATA_TOKEN_HEADER, ORM_SCHEMA_PREFIX, ORM_SERVICE_DATA_TOKEN_HEADER, ORM_TO_PIPELINE_TYPE, OrmDefinitionError, PredicateParseError, QUALIFIED_TABLE_PATTERN, RESERVED_COLUMN_NAMES, RESERVED_TABLE_NAMES, ROLES_CLAIM_KEY, SCHEMA_DIR, STATEMENT_KINDS, SYNCED_CONTRACT_HASH_DOMAIN, SYNCED_SOURCE_PATTERN, SYNCED_VIEW_EXECUTION_OWNER_ROLE, TABLE_BRAND, VIEW_BRAND, actionFunctionName, actionFunctionStatements, actionParamArg, actionParamOrder, actionParamSchema, actionPrepareChecks, actionSchema, actionWriteTargets, actionsInterfaceName, activeAuthoringContext, applyMigrationHints, applyMigrationHintsV2, beginAuthoringContext, buildActionParamsSchema, buildDrizzleEntities, buildDrizzleEntitiesV2, buildNamespaceDefinition, buildRebaseJournalEntry, canonicalActionSchema, canonicalColumnSchema, canonicalDefinitionSchema, canonicalEnumSchema, canonicalIndexSchema, canonicalLinkSchema, canonicalPoliciesSchema, canonicalRelations, canonicalTableSchema, canonicalViewSchema, canonicalizeDefinition, capabilityExpression, castParts, checksumOf, claimExpression, classifyDestructive, columnSchema, columnTypeSchema2 as columnTypeSchema, compileActionRequires, compileActionStatements, compileNamespace, compilePredIrToSql, compilePredicate, compiledArtifactSchema, compiledOperationSchema, computeCanonicalHash, computeDefinitionHash, computeSyncedContractHash, consumerRoleName, convertV1DefinitionToCanonical, declaredCapabilityKeys, defineEnum, defineNamespace, defineSyncedTable, defineTable, defineView, deriveColumnName, diffCommand, diffStatements, emptyJournal, emptySnapshot, endAuthoringContext, extractNamedParams, extractOrmSyncedTableContract, generateNamespaceTypes, generateSnapshot, generateSnapshotV2, identifierSchema, indexByHash, isColumnBuilder, isEnumRef, isFunctionDefault, isOwnedStatement, isOwnedStatementV2, isQualifiedReference, isSeqLink, isSeqNamespaceConfig, isSeqTable, isSeqView, limitIrSchema, loadCommittedMigrations, loadCommittedTipMigration, loadNamespaceDefinition, loadNamespaceFromDir, migrationName, namespaceConfigSchema, namespaceSchema, normalizeCanonical, normalizeIr, normalizeSnapshot, orderIrSchema, ormSchemaName, overlayWinsUnion, overrideRowMutation, parseCanonicalDefinition, parseDefinitionSources, parsePredicate, parseTableFile, parsedPredicateSchema, pendingKitStatements, pendingKitStatementsV2, planMigration, planMigrationV2, policyExpressionSchema, policyVariantSchema, predIrSchema, predicateOperands, quoteIdentifier, quoteLiteral, readMigrationSql, readMigrationWorkspace, readSnapshot, rebaseViolations, referencedCapabilities, referencedColumns, renderMigrationSql, revertOverlayMutation, rewriteSyncedSourceRelationsInViewQuery, roleExpression, rootIrSchema, securityStatements, selectionIrSchema, serializeNamespace, sqlTypeName, stableJson, syncedBackingRevokeStatements, syncedContractSurface, syncedTables, syncedViewDropStatements, syncedViewStatements, t, tableFileSchema, tableMetaIrSchema, tableReferenceSchema, toMany, toOne, toPlatformUser, typesInterfaceName, validateViewCatalogStatement, valueIrSchema, variableDefaultIrSchema, variableIrSchema, verifyRebaseIsNoop, walkPredicate, writeMigration };
|
|
5837
|
+
export { ACTION_REQUIRES_DENIED_SQLSTATE, ACTION_VALIDATES_FAILED_SQLSTATE, AUDIT_ACTIONS, CANONICAL_FORMAT, CAPABILITIES_CLAIM_KEY, CAPABILITY_KEY_PATTERN, CLAIMS_CONTRACT, CLAIMS_SETTING, CLAIM_KEY_PATTERN, CMP_OPS, COLUMN_BRAND, COLUMN_DEFAULT_FNS, COLUMN_IDENTIFIER_PATTERN, COLUMN_TYPES2 as COLUMN_TYPES, COMPARE_OPS, CONSUMER_ROLE_SUFFIX, ColumnBuilder, DATE_BUCKETS, DEFINITION_FILE_EXTENSIONS, ENUM_BRAND, HASH_DOMAIN, IDENTIFIER_PATTERN, IDENTIFIER_RE, INPUT_REF_KINDS, IR_VERSION, IrVersionError, LEGACY_IR_VERSION, LINK_BRAND, MAX_DEFINITION_FILE_BYTES, MAX_GENERATED_SDL_BYTES, MAX_IDENTIFIER_LENGTH, MIGRATIONS_DIRNAME, NAMESPACE_CONFIG_BRAND, NAMESPACE_CONFIG_FILE, NAMESPACE_CONFIG_FILENAMES, NAMESPACE_PATTERN, ORM_DATA_TOKEN_HEADER, ORM_MIGRATION_EXPORT_FILE, ORM_MIGRATION_EXPORT_MAX_BYTES, ORM_REGISTRATION_MAX_BYTES, ORM_SCHEMA_PREFIX, ORM_SERVICE_DATA_TOKEN_HEADER, ORM_TO_PIPELINE_TYPE, OrmDefinitionError, PredicateParseError, QUALIFIED_TABLE_PATTERN, RESERVED_COLUMN_NAMES, RESERVED_TABLE_NAMES, ROLES_CLAIM_KEY, SCHEMA_DIR, STATEMENT_KINDS, SYNCED_CONTRACT_HASH_DOMAIN, SYNCED_SOURCE_PATTERN, SYNCED_VIEW_EXECUTION_OWNER_ROLE, TABLE_BRAND, VIEW_BRAND, actionFunctionName, actionFunctionStatements, actionParamArg, actionParamOrder, actionParamSchema, actionPrepareChecks, actionSchema, actionWriteTargets, actionsInterfaceName, activeAuthoringContext, applyMigrationHints, applyMigrationHintsV2, beginAuthoringContext, buildActionParamsSchema, buildDrizzleEntities, buildDrizzleEntitiesV2, buildNamespaceDefinition, buildRebaseJournalEntry, canonicalActionSchema, canonicalColumnSchema, canonicalDefinitionSchema, canonicalEnumSchema, canonicalIndexSchema, canonicalLinkSchema, canonicalPoliciesSchema, canonicalRelations, canonicalTableSchema, canonicalViewSchema, canonicalizeDefinition, capabilityExpression, castParts, checksumOf, claimExpression, classifyDestructive, columnSchema, columnTypeSchema2 as columnTypeSchema, compileActionRequires, compileActionStatements, compileNamespace, compilePredIrToSql, compilePredicate, compiledArtifactSchema, compiledOperationSchema, computeCanonicalHash, computeDefinitionHash, computeSyncedContractHash, consumerRoleName, convertV1DefinitionToCanonical, createOrmMigrationExport, declaredCapabilityKeys, defineEnum, defineNamespace, defineSyncedTable, defineTable, defineView, deriveColumnName, diffCommand, diffStatements, emptyJournal, emptySnapshot, endAuthoringContext, extractNamedParams, extractOrmSyncedTableContract, generateNamespaceTypes, generateSnapshot, generateSnapshotV2, identifierSchema, indexByHash, isColumnBuilder, isEnumRef, isFunctionDefault, isOwnedStatement, isOwnedStatementV2, isQualifiedReference, isSeqLink, isSeqNamespaceConfig, isSeqTable, isSeqView, limitIrSchema, loadCommittedMigrations, loadCommittedTipMigration, loadNamespaceDefinition, loadNamespaceFromDir, migrationName, namespaceConfigSchema, namespaceSchema, normalizeCanonical, normalizeIr, normalizeSnapshot, orderIrSchema, ormMigrationExportSchema, ormSchemaName, overlayWinsUnion, overrideRowMutation, parseCanonicalDefinition, parseDefinitionSources, parseOrmMigrationExport, parsePredicate, parseTableFile, parsedPredicateSchema, pendingKitStatements, pendingKitStatementsV2, planMigration, planMigrationV2, policyExpressionSchema, policyVariantSchema, predIrSchema, predicateOperands, quoteIdentifier, quoteLiteral, readMigrationSql, readMigrationWorkspace, readSnapshot, rebaseViolations, referencedCapabilities, referencedColumns, renderMigrationSql, renderOrmMigrationExport, requiredArtifactIrVersion, revertOverlayMutation, rewriteSyncedSourceRelationsInViewQuery, roleExpression, rootIrSchema, securityStatements, selectionIrSchema, serializeNamespace, sqlTypeName, stableJson, syncedBackingRevokeStatements, syncedContractSurface, syncedTables, syncedViewDropStatements, syncedViewStatements, t, tableFileSchema, tableMetaIrSchema, tableReferenceSchema, toMany, toOne, toPlatformUser, typesInterfaceName, validateViewCatalogStatement, valueIrSchema, variableDefaultIrSchema, variableIrSchema, verifyRebaseIsNoop, walkPredicate, writeMigration };
|
|
5501
5838
|
//# sourceMappingURL=index.js.map
|
|
5502
5839
|
//# sourceMappingURL=index.js.map
|