okmodel 0.9.0 → 0.9.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.
Files changed (175) hide show
  1. package/README.md +9 -1
  2. package/dist/adapters/pg/bunsql-extra.d.ts +5 -5
  3. package/dist/adapters/pg/bunsql.d.ts +6 -2
  4. package/dist/adapters/pg/nodepostgres.d.ts +16 -1
  5. package/dist/adapters/pg/postgres-extra.d.ts +3 -17
  6. package/dist/adapters/pg/postgresjs.d.ts +2 -1
  7. package/dist/contracts/driver.d.ts +3 -2
  8. package/dist/contracts/error.d.ts +1 -1
  9. package/dist/dialects/pg/errors.d.ts +2 -0
  10. package/dist/dialects/pg/ext/fuzzystrmatch.js +1 -1
  11. package/dist/dialects/pg/ext/hstore.js +1 -1
  12. package/dist/dialects/pg/ext/ltree.js +1 -1
  13. package/dist/dialects/pg/ext/pgcrypto.js +1 -1
  14. package/dist/dialects/pg/ext/unaccent.js +1 -1
  15. package/dist/dialects/pg/filters.d.ts +2 -2
  16. package/dist/dialects/pg/fn/index.js +1 -1
  17. package/dist/dialects/pg/index.js +3 -3
  18. package/dist/dialects/pg/introspect.d.ts +12 -0
  19. package/dist/dialects/pg/view/index.js +2 -2
  20. package/dist/runtime/param-limit.d.ts +20 -0
  21. package/dist/runtime/pg/bun.js +3 -3
  22. package/dist/runtime/pg/neon.js +3 -3
  23. package/dist/runtime/pg/pg.js +3 -3
  24. package/dist/runtime/pg/pglite.js +2 -2
  25. package/dist/runtime/pg/postgresjs.js +10 -10
  26. package/dist/runtime/tenancy/index.js +1 -1
  27. package/dist/runtime/traits/index.js +2 -2
  28. package/dist/runtime/validate/index.js +2 -2
  29. package/dist/runtime/write.d.ts +1 -1
  30. package/dist/shared/{5kyb3tqx.js → 1h5h1rf5.js} +1 -1
  31. package/dist/shared/{4vbf83xx.js → 2vcnpf0n.js} +1 -1
  32. package/dist/shared/{8n688yjg.js → 3ap5hxvj.js} +2 -1
  33. package/dist/shared/{wvtjg5m3.js → 3d7a80fq.js} +1 -1
  34. package/dist/shared/{bphrgwz9.js → 3dr444ny.js} +57 -78
  35. package/dist/shared/{yvabyg9k.js → 3vvpcphd.js} +7 -6
  36. package/dist/shared/{760ppr76.js → 4a3mmhhe.js} +2 -2
  37. package/dist/shared/{90sg0w5a.js → 4pkz5nvw.js} +65 -10
  38. package/dist/shared/{mazpbcdj.js → 4te76xc5.js} +1 -1
  39. package/dist/shared/{xwhdcrm4.js → 60y4ahmw.js} +1 -1
  40. package/dist/shared/{9dtgn1z2.js → 63qjmhnp.js} +100 -17
  41. package/dist/shared/{s7he5e5d.js → 67w3kn71.js} +1 -1
  42. package/dist/shared/{n306gqkd.js → 6vfvsdkt.js} +9 -7
  43. package/dist/shared/{jpybhf98.js → 6xqvesa5.js} +1 -1
  44. package/dist/shared/{n5whk3mt.js → 8gdbty5v.js} +8 -8
  45. package/dist/shared/{ccsmhv9r.js → 8ma6drzr.js} +194 -31
  46. package/dist/shared/{3456242y.js → 8p2bgye0.js} +110 -18
  47. package/dist/shared/{ebhz8vh6.js → 8qwqrxed.js} +11 -3
  48. package/dist/shared/{ss2kv2rs.js → 8v7vzpgt.js} +10 -7
  49. package/dist/shared/{8qzkd9f3.js → ak61ye1f.js} +3 -3
  50. package/dist/shared/{6nae0spm.js → bdekbvmt.js} +69 -7
  51. package/dist/shared/{tw1nvvfj.js → bxjq6xqv.js} +2 -2
  52. package/dist/shared/{rbxt795q.js → c7dqd3gk.js} +81 -41
  53. package/dist/shared/ddh72ym5.js +20 -0
  54. package/dist/shared/e1c55v04.js +10 -0
  55. package/dist/shared/{8jesqsrk.js → ekhq3ytz.js} +1 -1
  56. package/dist/shared/{7ba5h6pb.js → esy4dyxe.js} +5 -5
  57. package/dist/shared/{gcefca6p.js → f9t9g54z.js} +22 -9
  58. package/dist/shared/fkjne6gx.js +8 -0
  59. package/dist/shared/{ds229x4r.js → fne90dvz.js} +4 -1
  60. package/dist/shared/{cexb7z6b.js → gr9sn4za.js} +1 -1
  61. package/dist/shared/{mvmm3xpb.js → gzgfsspx.js} +1 -1
  62. package/dist/shared/{fdsz3w80.js → hgt69zjf.js} +2 -2
  63. package/dist/shared/{dd5pmnky.js → jvxcp1qk.js} +70 -86
  64. package/dist/shared/{yybhre6n.js → k73e8n9d.js} +3 -3
  65. package/dist/shared/{mgaq2sd3.js → kzgrcy8s.js} +8 -7
  66. package/dist/shared/{myk8fswx.js → mz48rs74.js} +14 -3
  67. package/dist/shared/{62b603gr.js → n6je5b28.js} +10 -23
  68. package/dist/shared/{2szgv6yj.js → ns35jqz0.js} +71 -45
  69. package/dist/shared/{xtsydxjg.js → nstwgzn8.js} +1 -1
  70. package/dist/shared/{stfa9xcw.js → py2zrrm6.js} +7 -6
  71. package/dist/shared/{9dr2qnx7.js → qqm3ze6q.js} +1 -1
  72. package/dist/shared/{cf8zdz5p.js → rcjm0yn7.js} +27 -34
  73. package/dist/shared/{ckz05z15.js → rged92v6.js} +7 -4
  74. package/dist/shared/{qvwwp1mp.js → rw4tpcd0.js} +30 -21
  75. package/dist/shared/{bsek72fg.js → s5qe47a6.js} +3 -0
  76. package/dist/shared/{1d9q9cv2.js → smxzjz60.js} +2 -2
  77. package/dist/shared/{c3mvnq7a.js → th5rnaxw.js} +1 -1
  78. package/dist/shared/{1mxm04fy.js → tyqapcgk.js} +71 -8
  79. package/dist/shared/{zdqf8k5d.js → vsna85qf.js} +1 -1
  80. package/dist/shared/{63qtkrca.js → vy47ryqa.js} +55 -5
  81. package/dist/shared/{xv8qj97g.js → wbdh9qbg.js} +3 -1
  82. package/dist/shared/{2zdxkt7b.js → wp7y8q3y.js} +41 -9
  83. package/dist/shared/{pg4ezgyf.js → xbd9xs6p.js} +2 -2
  84. package/dist/shared/{npdftanb.js → xbvz446k.js} +16 -13
  85. package/dist/shared/{aksb89mn.js → y6tfjk9q.js} +2 -2
  86. package/dist/shared/{3bb7wsqt.js → ycnft340.js} +1 -1
  87. package/dist/shared/{z7s6fpxv.js → ymenf259.js} +1 -1
  88. package/dist/shared/{71n0jmhc.js → ysdw2fg3.js} +10 -2
  89. package/dist/shared/{xqacq37x.js → zecrwdk9.js} +1 -1
  90. package/dist/shared/{3t6kw83m.js → zm2rzqba.js} +2 -1
  91. package/dist/tooling/cli.js +122 -44
  92. package/dist/tooling/driver-run.js +107 -24
  93. package/dist/tooling/github/comment.d.ts +8 -2
  94. package/dist/tooling/migrate/apply.d.ts +17 -2
  95. package/dist/tooling/migrate/drift.d.ts +12 -0
  96. package/dist/tooling/migrate/driver-url.d.ts +17 -0
  97. package/dist/tooling/migrate/index.js +9 -7
  98. package/dist/tooling/migrate/policy.d.ts +17 -5
  99. package/dist/tooling/migrate/pooler-probe.d.ts +23 -0
  100. package/dist/tooling/migrate/pull.d.ts +3 -0
  101. package/dist/tooling/migrate/runner.d.ts +24 -1
  102. package/dist/tooling/shared/{0j0jtse1.js → 09a6vsys.js} +1 -1
  103. package/dist/tooling/shared/{68tf18v4.js → 0v9ptnxp.js} +8 -8
  104. package/dist/tooling/shared/{qartgazh.js → 0wtzps9p.js} +6 -6
  105. package/dist/tooling/shared/{1800eymw.js → 0yhf1db4.js} +6 -6
  106. package/dist/tooling/shared/{s6qha57m.js → 19msgrsc.js} +2 -2
  107. package/dist/tooling/shared/{3yn79q5m.js → 1fwfm5m3.js} +1 -1
  108. package/dist/tooling/shared/{hfq3n3bd.js → 2jdn09e2.js} +55 -5
  109. package/dist/tooling/shared/{v6j1ep6w.js → 2qrte2ct.js} +5 -5
  110. package/dist/tooling/shared/{yb4kd19s.js → 2v4nwnkb.js} +1 -1
  111. package/dist/tooling/shared/{ezqn88nj.js → 33z39trp.js} +106 -23
  112. package/dist/tooling/shared/{0r05zhyq.js → 353vnw9z.js} +5 -5
  113. package/dist/tooling/shared/{m4hhb8aa.js → 390kry9g.js} +2 -2
  114. package/dist/tooling/shared/{rcywj2w8.js → 3f6etb9z.js} +18 -31
  115. package/dist/tooling/shared/{9wsnsvbf.js → 3t2cqmsx.js} +1 -1
  116. package/dist/tooling/shared/{5zrzffnk.js → 4dzvjtpy.js} +1 -1
  117. package/dist/tooling/shared/{am04v8na.js → 4ec0nxvw.js} +10 -10
  118. package/dist/tooling/shared/{04svnxxj.js → 4tg2bn41.js} +59 -78
  119. package/dist/tooling/shared/{829fjxyw.js → 5rah55z0.js} +2 -2
  120. package/dist/tooling/shared/{c0xevn1h.js → 804w1t69.js} +4 -4
  121. package/dist/tooling/shared/{dhatassp.js → 8g0m0rvd.js} +6 -6
  122. package/dist/tooling/shared/{zx3v9phv.js → 8msbtw5s.js} +1 -1
  123. package/dist/tooling/shared/{7pxncnzs.js → 8t22h3h7.js} +2 -2
  124. package/dist/tooling/shared/{wbwq1jsa.js → 91c59b7b.js} +2 -2
  125. package/dist/tooling/shared/{a2xe04bm.js → adk9k6va.js} +1 -1
  126. package/dist/tooling/shared/{w17sj05d.js → ajjd5cwz.js} +2 -2
  127. package/dist/tooling/shared/{35zemrmf.js → angjck01.js} +1 -1
  128. package/dist/tooling/shared/{jsjjz875.js → asnnc3v5.js} +1 -1
  129. package/dist/tooling/shared/{5bmksndh.js → bj1kyqc8.js} +2 -2
  130. package/dist/tooling/shared/{kbqnrs1x.js → bt3tjemb.js} +11 -3
  131. package/dist/tooling/shared/{vwr2y833.js → chk2bh4f.js} +2 -2
  132. package/dist/tooling/shared/{ybdew6j7.js → ckq4z5ax.js} +5 -2
  133. package/dist/tooling/shared/{3rmaz3pq.js → d3rhb24q.js} +1 -1
  134. package/dist/tooling/shared/{mw5npye0.js → d8q00vxg.js} +4 -4
  135. package/dist/tooling/shared/{j664qjrt.js → dx188rxs.js} +2 -2
  136. package/dist/tooling/shared/{8yr7ges1.js → efq6p9df.js} +35 -42
  137. package/dist/tooling/shared/{yhp829bx.js → en6rwzq0.js} +3 -3
  138. package/dist/tooling/shared/{f77nnfqy.js → f2r5g0fs.js} +2 -2
  139. package/dist/tooling/shared/{zs9aymcp.js → f4gep0nk.js} +1 -1
  140. package/dist/tooling/shared/{kz715295.js → f8a010fm.js} +13 -11
  141. package/dist/tooling/shared/{w5d89rb4.js → fdsek6pa.js} +1 -1
  142. package/dist/tooling/shared/fvmrxsd1.js +8 -0
  143. package/dist/tooling/shared/{jpgmjsx3.js → g3xybeqf.js} +4 -3
  144. package/dist/tooling/shared/{wezxf3h8.js → gefmpwsa.js} +1 -1
  145. package/dist/tooling/shared/{jr2nmr81.js → gjj71hfh.js} +23 -20
  146. package/dist/tooling/shared/{9sfbtwsk.js → jgzt69pa.js} +5 -5
  147. package/dist/tooling/shared/{a4pzghha.js → jm30k2ca.js} +10 -8
  148. package/dist/tooling/shared/{shjs0379.js → kayqzpbk.js} +6 -3
  149. package/dist/tooling/shared/{tcwpjvxe.js → kvw1672d.js} +1 -1
  150. package/dist/tooling/shared/{stebqyyd.js → mr1kcgpw.js} +3 -3
  151. package/dist/tooling/shared/{tpssp7ac.js → mwbdf409.js} +1 -1
  152. package/dist/tooling/shared/{fh07z1dt.js → ns9207v3.js} +1 -1
  153. package/dist/tooling/shared/{5p8km5jb.js → p6tjv97k.js} +3 -3
  154. package/dist/tooling/shared/{ytk1tjs0.js → p73evwf0.js} +1 -1
  155. package/dist/tooling/shared/{8m5znpdt.js → phc1nc4g.js} +1 -1
  156. package/dist/tooling/shared/{asgr07mp.js → ppybjw50.js} +79 -16
  157. package/dist/tooling/shared/{t4xveemq.js → qdrn8ghn.js} +5 -4
  158. package/dist/tooling/shared/rd8h01ta.js +20 -0
  159. package/dist/tooling/shared/{7aews1zg.js → rwg8y90m.js} +3 -3
  160. package/dist/tooling/shared/{5x3pf400.js → ry2f3w8p.js} +3 -3
  161. package/dist/tooling/shared/{m3xt8b1h.js → ryc5sa58.js} +8 -8
  162. package/dist/tooling/shared/{57btdkgm.js → ss87qxwv.js} +2 -2
  163. package/dist/tooling/shared/{6vk28rjp.js → vg68k11z.js} +3 -3
  164. package/dist/tooling/shared/{62fxpc1p.js → vms9dta5.js} +73 -89
  165. package/dist/tooling/shared/{gqwevek4.js → xchpnbdv.js} +1 -1
  166. package/dist/tooling/shared/{t6zjeake.js → xn6p01fh.js} +5 -5
  167. package/dist/tooling/shared/{7b3xp9gc.js → xwr6q618.js} +3 -3
  168. package/dist/tooling/shared/{yfedr1zb.js → xx3gmq94.js} +1 -1
  169. package/dist/tooling/shared/{9qr9rkbh.js → y2bd7yfx.js} +21 -13
  170. package/dist/tooling/shared/{nxq95eb6.js → y4gq9rbe.js} +11 -11
  171. package/dist/tooling/shared/{76pv6qff.js → y4vwft92.js} +2 -2
  172. package/dist/tooling/shared/{sjwkt1pg.js → yz68xcmc.js} +1 -1
  173. package/dist/tooling/testing/factories.d.ts +1 -3
  174. package/dist/tooling/testing/index.js +1 -1
  175. package/package.json +2 -1
@@ -1,3 +1,6 @@
1
+ import {
2
+ assertParamCount2
3
+ } from "./ddh72ym5.js";
1
4
  import {
2
5
  stack2
3
6
  } from "./mss6ctgv.js";
@@ -29,6 +32,7 @@ import {
29
32
  decodeRow,
30
33
  indexes,
31
34
  quote,
35
+ list,
32
36
  isRecord,
33
37
  registerFailFix,
34
38
  fail2
@@ -163,7 +167,7 @@ async function prepareWrite(host, op, table, input, options, mods, scope) {
163
167
  return {
164
168
  statements: planned.statements,
165
169
  options: callOptions(options),
166
- ...planned.parentGuard === true ? { checked: "parent" } : planned.expect !== undefined ? { checked: "expect" } : {},
170
+ ...planned.parentGuard === true ? { checked: "parent" } : planned.treeGuard === true ? { checked: "tree" } : planned.expect !== undefined ? { checked: "expect" } : {},
167
171
  ...planned.recover === undefined ? {} : {
168
172
  follow: (results) => planned.recover?.(decodePlanned(withoutQuiet(results, planned.quiet), planned.outputs)) ?? []
169
173
  },
@@ -198,7 +202,7 @@ async function planWrite(schema, generators, op, tableName, input, options, mods
198
202
  }
199
203
  if (op === "update") {
200
204
  if (traitNamed(table, "versioned")) {
201
- const { planVersioned } = await import("./3t6kw83m.js");
205
+ const { planVersioned } = await import("./zm2rzqba.js");
202
206
  return withRowFilters(scope, mods.archive, () => {
203
207
  noteArchive(table, mods.archive);
204
208
  const planned = planVersioned(schema, table, input, record, mods);
@@ -244,7 +248,7 @@ async function planInsert(table, input, options, mods, generators, scope, tenanc
244
248
  keyColumns: undefined
245
249
  };
246
250
  }
247
- const conflictMod = options.onConflict === undefined || options.onConflict === "error" ? undefined : await import("./xwhdcrm4.js");
251
+ const conflictMod = options.onConflict === undefined || options.onConflict === "error" ? undefined : await import("./60y4ahmw.js");
248
252
  const conflict = conflictMod?.readConflict(table, options.onConflict, allow, tenantKeyList(tenancy));
249
253
  if (conflict?.kind === "update" && traitNamed(table, "versioned")) {
250
254
  fail2("OKM1120", `onConflict update on ${table.model.name} does not check the version you read.`, "Read the row and update. ignore and return do not change the existing row.");
@@ -308,7 +312,7 @@ function planUpdate(schema, table, input, options, mods, scope) {
308
312
  rejectDuplicateTargets2(table, rows);
309
313
  rejectMixedSets2(rows);
310
314
  for (const row of rows)
311
- refuseTreeCycle(table, row.where, row.set, schema.tenancy?.key);
315
+ refuseTreeCycle(table, row.where, row.set, tenantKeys(schema.tenancy));
312
316
  const moves = rows.some((row) => pathField(schema.tenancy, table.model.name, row.set) !== undefined);
313
317
  if (moves) {
314
318
  return {
@@ -338,7 +342,8 @@ function planUpdate(schema, table, input, options, mods, scope) {
338
342
  requireFilter2(table, input.where, mods, "update");
339
343
  if (Object.keys(set).length === 0)
340
344
  fail2("OKM1120", "update set is empty.");
341
- refuseTreeCycle(table, input.where, set, schema.tenancy?.key);
345
+ const keys = tenantKeys(schema.tenancy);
346
+ const cycle = refuseTreeCycle(table, input.where, set, keys);
342
347
  const where = stack2(input.where, mods.presets);
343
348
  const moved = pathAssignment(schema.tenancy, table.model.name, set, scope);
344
349
  if (moved !== undefined && claim !== undefined) {
@@ -365,6 +370,17 @@ function planUpdate(schema, table, input, options, mods, scope) {
365
370
  keyColumns: undefined
366
371
  };
367
372
  }
373
+ if (cycle !== undefined) {
374
+ return {
375
+ statements: [treeSetUpdate(schema, table, where, set, cycle, returning)],
376
+ outputs: parentOutputs(returning),
377
+ shape: returning === undefined ? "count" : "rows",
378
+ expect,
379
+ keys: undefined,
380
+ keyColumns: undefined,
381
+ treeGuard: true
382
+ };
383
+ }
368
384
  const sql = new Sql2;
369
385
  sql.text("update ");
370
386
  sql.text(quote(table.model.sql));
@@ -411,6 +427,8 @@ function finish(results, planned, table) {
411
427
  return finishVersioned(results, planned, table);
412
428
  if (planned.parentGuard === true)
413
429
  return finishParent(results, planned, table);
430
+ if (planned.treeGuard === true)
431
+ return finishTree(results, planned, table);
414
432
  if (planned.shape === "count") {
415
433
  let count = 0;
416
434
  for (const result of results)
@@ -439,6 +457,18 @@ function finishParent(results, planned, table) {
439
457
  fix: { summary: "Point the foreign key at a parent in this tenant." }
440
458
  });
441
459
  }
460
+ return finishGuarded(results, planned, table, updated);
461
+ }
462
+ function finishTree(results, planned, table) {
463
+ const row = results[0]?.rows[0];
464
+ const matched = countCell(row?.[0]);
465
+ const updated = countCell(row?.[1]);
466
+ if (updated < matched) {
467
+ fail2("OKM1120", `update on ${table} cannot point a tree row at itself or its descendant.`, "Set the parent to another row that is not under this one. A filter that is not the primary key is not checked.");
468
+ }
469
+ return finishGuarded(results, planned, table, updated);
470
+ }
471
+ function finishGuarded(results, planned, table, updated) {
442
472
  if (planned.shape === "count") {
443
473
  checkExpect(updated, planned.expect, table, "write");
444
474
  return { count: updated };
@@ -1074,6 +1104,7 @@ class Sql2 {
1074
1104
  };
1075
1105
  }
1076
1106
  statement() {
1107
+ assertParamCount2(this.params.length);
1077
1108
  return this.params.length === 0 ? { text: this.parts.join("") } : { text: this.parts.join(""), params: this.params };
1078
1109
  }
1079
1110
  }
@@ -1084,7 +1115,7 @@ function insertRow(table, value, allow, tenancy) {
1084
1115
  for (const key of Object.keys(value)) {
1085
1116
  const column = table.columns.get(key);
1086
1117
  if (column === undefined)
1087
- continue;
1118
+ refuseUnknownField(table, key);
1088
1119
  tenancy?.guard(table.model.name, key, "insert");
1089
1120
  if (!writable(table, column, allow, "insert"))
1090
1121
  refuse(table, column);
@@ -1094,6 +1125,9 @@ function insertRow(table, value, allow, tenancy) {
1094
1125
  refuseHalfMorph(table, row);
1095
1126
  return row;
1096
1127
  }
1128
+ function refuseUnknownField(table, key) {
1129
+ throwNamed2("OKM1120", key, table.names, `Field ${key} is not on ${table.model.name}. Accepted names: ${list(table.names)}.`);
1130
+ }
1097
1131
  function cellsFor(columns, row) {
1098
1132
  return columns.map((column) => {
1099
1133
  if (!Object.hasOwn(row, column.field))
@@ -1200,10 +1234,10 @@ function writeSet2(table, value, allow, tenancy) {
1200
1234
  }
1201
1235
  const set = {};
1202
1236
  for (const key of Object.keys(value)) {
1203
- if (value[key] === undefined)
1204
- continue;
1205
1237
  const column = table.columns.get(key);
1206
1238
  if (column === undefined)
1239
+ refuseUnknownField(table, key);
1240
+ if (value[key] === undefined)
1207
1241
  continue;
1208
1242
  tenancy?.guard(table.model.name, key, "update");
1209
1243
  if (column.guardUpdate || !writable(table, column, allow, "update"))
@@ -1213,10 +1247,11 @@ function writeSet2(table, value, allow, tenancy) {
1213
1247
  refuseHalfMorph(table, set);
1214
1248
  return set;
1215
1249
  }
1216
- function refuseTreeCycle(table, where, set, scopeKey) {
1217
- const own = keyedId(table, where, scopeKey);
1218
- if (own === undefined)
1250
+ function refuseTreeCycle(table, where, set, scopeKeys) {
1251
+ const keyed = keyedId(table, where, scopeKeys);
1252
+ if (keyed === undefined)
1219
1253
  return;
1254
+ let found;
1220
1255
  for (const relation of table.model.relations) {
1221
1256
  if (relation.kind !== "many" || relation.through !== undefined || relation.morph !== undefined) {
1222
1257
  continue;
@@ -1231,17 +1266,20 @@ function refuseTreeCycle(table, where, set, scopeKey) {
1231
1266
  if (typeof value !== "string" && typeof value !== "number" && typeof value !== "bigint") {
1232
1267
  continue;
1233
1268
  }
1234
- if ((typeof value === "string" ? value : value.toString()) !== own)
1235
- continue;
1236
- fail2("OKM1120", `update on ${table.model.name}.${column.field} cannot point a tree row at itself.`, "Set the parent to another row. A filter that is not the primary key is not checked.");
1269
+ const parent = typeof value === "string" ? value : value.toString();
1270
+ if (parent === keyed.own) {
1271
+ fail2("OKM1120", `update on ${table.model.name}.${column.field} cannot point a tree row at itself.`, "Set the parent to another row. A filter that is not the primary key is not checked.");
1272
+ }
1273
+ found = { own: keyed.own, parent, column, identity: keyed.identity };
1237
1274
  }
1238
1275
  }
1276
+ return found;
1239
1277
  }
1240
- function keyedId(table, where, scopeKey) {
1278
+ function keyedId(table, where, scopeKeys) {
1241
1279
  if (!isRecord(where))
1242
1280
  return;
1243
1281
  const primary = table.model.primary;
1244
- const required = primary.filter((field) => field !== scopeKey);
1282
+ const required = primary.filter((field) => !scopeKeys.includes(field));
1245
1283
  const field = required.length === 1 ? required[0] : undefined;
1246
1284
  if (field === undefined || !Object.hasOwn(where, field))
1247
1285
  return;
@@ -1253,7 +1291,51 @@ function keyedId(table, where, scopeKey) {
1253
1291
  if (typeof value !== "string" && typeof value !== "number" && typeof value !== "bigint") {
1254
1292
  return;
1255
1293
  }
1256
- return String(value);
1294
+ const identity = table.columns.get(field);
1295
+ if (identity === undefined)
1296
+ return;
1297
+ return { own: String(value), identity };
1298
+ }
1299
+ function tenantKeys(tenancy) {
1300
+ if (tenancy === undefined)
1301
+ return [];
1302
+ if (tenancy.keys !== undefined && tenancy.keys.length > 0)
1303
+ return tenancy.keys;
1304
+ return [tenancy.key];
1305
+ }
1306
+ function treeSetUpdate(schema, table, where, set, cycle, returning) {
1307
+ const sql = new Sql2;
1308
+ sql.text("with matched as (select ");
1309
+ sql.text(rowId("t"));
1310
+ sql.text(" from ");
1311
+ sql.text(quote(table.model.sql));
1312
+ sql.text(" as t");
1313
+ emitFilter(schema, table, where, sql, "t");
1314
+ sql.text("), updated as (update ");
1315
+ sql.text(quote(table.model.sql));
1316
+ sql.text(" as t set ");
1317
+ emitSet2(sql, table, set, "t");
1318
+ sql.text(" where (");
1319
+ sql.text(rowId("t"));
1320
+ sql.text(") in (select tableoid, ctid from matched) and not exists (with recursive d as (select ");
1321
+ sql.text(quote(cycle.identity.sql));
1322
+ sql.text(" as k from ");
1323
+ sql.text(quote(table.model.sql));
1324
+ sql.text(" where ");
1325
+ sql.text(quote(cycle.column.sql));
1326
+ sql.text(" = ");
1327
+ sql.param(cycle.own, cycle.identity.dataType);
1328
+ sql.text(" union all select c.");
1329
+ sql.text(quote(cycle.identity.sql));
1330
+ sql.text(" from ");
1331
+ sql.text(quote(table.model.sql));
1332
+ sql.text(" c join d on c.");
1333
+ sql.text(quote(cycle.column.sql));
1334
+ sql.text(" = d.k) select 1 from d where d.k = ");
1335
+ sql.param(cycle.parent, cycle.identity.dataType);
1336
+ sql.text(")");
1337
+ endParent(sql, returning);
1338
+ return sql.statement();
1257
1339
  }
1258
1340
  function columnBySql(table, sql) {
1259
1341
  for (const column of table.model.columns) {
@@ -1362,6 +1444,7 @@ function chunkRows(rows) {
1362
1444
  function withParams(text, params) {
1363
1445
  if (params === undefined || params.length === 0)
1364
1446
  return { text };
1447
+ assertParamCount2(params.length);
1365
1448
  return { text, params };
1366
1449
  }
1367
1450
  function keyWire(columns, row) {
@@ -2,7 +2,7 @@ import {
2
2
  readHandle,
3
3
  readCall,
4
4
  attachHttp
5
- } from "./npdftanb.js";
5
+ } from "./xbvz446k.js";
6
6
  import {
7
7
  decodeResult,
8
8
  emitWhere,
@@ -4,16 +4,18 @@ import {
4
4
  applyProject2,
5
5
  pushProject2,
6
6
  backfillTiming2
7
- } from "./ccsmhv9r.js";
8
- import"./3456242y.js";
9
- import"./jpybhf98.js";
10
- import"./myk8fswx.js";
11
- import"./2zdxkt7b.js";
12
- import"./6nae0spm.js";
7
+ } from "./8ma6drzr.js";
8
+ import"./8p2bgye0.js";
9
+ import"./6xqvesa5.js";
10
+ import"./4pkz5nvw.js";
11
+ import"./e1c55v04.js";
12
+ import"./mz48rs74.js";
13
+ import"./wp7y8q3y.js";
14
+ import"./bdekbvmt.js";
13
15
  import"./n442h54c.js";
14
16
  import"./fk9vef30.js";
15
17
  import"./12z7aznj.js";
16
- import"./dd5pmnky.js";
18
+ import"./jvxcp1qk.js";
17
19
  export {
18
20
  applyProject2 as applyProject,
19
21
  applyTarget2 as applyTarget,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  FUNCTION_ARG_TYPES
3
- } from "./myk8fswx.js";
3
+ } from "./mz48rs74.js";
4
4
  import {
5
5
  OkmError2
6
6
  } from "./52szzakm.js";
@@ -1,18 +1,18 @@
1
1
  import"./12z7aznj.js";
2
2
  import"./fk9vef30.js";
3
3
  import"./n442h54c.js";
4
- import"./dd5pmnky.js";
4
+ import"./jvxcp1qk.js";
5
5
  import {
6
6
  emitPull,
7
7
  formatPullReport,
8
8
  pullLayout2,
9
9
  commitPullFiles2
10
- } from "./rbxt795q.js";
11
- import"./myk8fswx.js";
10
+ } from "./c7dqd3gk.js";
11
+ import"./mz48rs74.js";
12
12
  import {
13
13
  loadConfig
14
- } from "./2zdxkt7b.js";
15
- import"./6nae0spm.js";
14
+ } from "./wp7y8q3y.js";
15
+ import"./bdekbvmt.js";
16
16
  import {
17
17
  catalog,
18
18
  enumType
@@ -452,14 +452,14 @@ function columnOf(parent, property, value, primaryColumns, enums, objects, names
452
452
  }));
453
453
  }
454
454
  const defaultExpression = serial || identity !== undefined || generated !== undefined ? undefined : readDefault(value, extras, `${tableName}.${sqlName}`);
455
- const nullable = flag(value, "notNull") ? false : true;
456
455
  const dataType = note !== undefined ? note.name : mapped.dataType;
457
- const solePrimary = identity?.always === true && dataType === "bigint" && !nullable && primaryColumns.length === 1 && primaryColumns[0] === sqlName;
456
+ const inPrimary = primaryColumns.includes(sqlName);
457
+ const nullable = inPrimary || flag(value, "notNull") ? false : true;
458
458
  return column2({
459
459
  parent,
460
460
  name: sqlName,
461
461
  dataType,
462
- nullable: solePrimary ? false : nullable,
462
+ nullable,
463
463
  provenance: provenanceOf(`field:${property}`),
464
464
  ...defaultExpression !== undefined ? { defaultExpression } : {},
465
465
  ...identity !== undefined ? { identity } : {},
@@ -1,12 +1,18 @@
1
1
  import {
2
2
  open2
3
- } from "./dd5pmnky.js";
3
+ } from "./jvxcp1qk.js";
4
4
  import {
5
5
  startupHash2
6
6
  } from "./12z7aznj.js";
7
7
  import {
8
8
  quoteIdent2
9
9
  } from "./fk9vef30.js";
10
+ import {
11
+ sealViews,
12
+ sealDomainChecks2,
13
+ catalogQuery2,
14
+ managedRoleOptions2
15
+ } from "./4pkz5nvw.js";
10
16
  import {
11
17
  runTarget,
12
18
  assumeRole,
@@ -17,14 +23,21 @@ import {
17
23
  provisionMark,
18
24
  expandProvisioned,
19
25
  installSnapshot
20
- } from "./3456242y.js";
26
+ } from "./8p2bgye0.js";
21
27
  import {
22
28
  assertCreateRole,
23
29
  currentUser,
24
30
  roleExists
25
- } from "./jpybhf98.js";
31
+ } from "./6xqvesa5.js";
32
+ import {
33
+ probeStableBackend
34
+ } from "./e1c55v04.js";
35
+ import {
36
+ introspectSchema
37
+ } from "./mz48rs74.js";
26
38
  import {
27
39
  projectPath,
40
+ assertSnapshots,
28
41
  loadMigrations,
29
42
  loadHeadSnapshot,
30
43
  lintMigrationDirectory,
@@ -33,8 +46,9 @@ import {
33
46
  projectHead,
34
47
  loadBuiltSchema,
35
48
  loadConfig
36
- } from "./2zdxkt7b.js";
49
+ } from "./wp7y8q3y.js";
37
50
  import {
51
+ driverUrl,
38
52
  changesRole,
39
53
  batchSizeField,
40
54
  assertExtensionsAvailable,
@@ -42,9 +56,8 @@ import {
42
56
  finishQuiet,
43
57
  assertTargetPolicy,
44
58
  assertDirectConnection,
45
- assertStableBackend,
46
59
  selectTarget
47
- } from "./6nae0spm.js";
60
+ } from "./bdekbvmt.js";
48
61
  import {
49
62
  UUIDV7_POSTGRES,
50
63
  uuidv7NeedsPostgres
@@ -99,6 +112,7 @@ function unquote(name) {
99
112
  }
100
113
 
101
114
  // src/tooling/migrate/apply.ts
115
+ import postgres from "postgres";
102
116
  var DEFAULT_LOCK_MS = 5000;
103
117
  var DEFAULT_STATEMENT_MS = 30000;
104
118
  var DEFAULT_RETRIES = 3;
@@ -173,8 +187,9 @@ function applyUnits(migration, done) {
173
187
  }
174
188
  async function applyTarget2(request) {
175
189
  assertDirectConnection(request.url, request.allowPooler === true);
190
+ const url = driverUrl(request.url);
176
191
  const pool = open2({
177
- url: request.url,
192
+ url,
178
193
  max: 1,
179
194
  ...request.searchPath !== undefined ? { searchPath: request.searchPath } : {}
180
195
  });
@@ -188,7 +203,7 @@ async function applyTarget2(request) {
188
203
  const connection = reserved;
189
204
  let locked = false;
190
205
  try {
191
- await assertStableBackend(() => backendPid(connection), request.allowPooler === true);
206
+ await probeStableBackend(request.url, () => backendPid(connection), request.allowPooler === true);
192
207
  await assertStandardStrings(connection);
193
208
  await setTimeouts(connection, request);
194
209
  await lockTarget(connection, request.target);
@@ -234,8 +249,11 @@ async function applyTarget2(request) {
234
249
  if (request.snapshot !== undefined) {
235
250
  await insertReference(connection, request);
236
251
  }
252
+ const desiredHash = request.headHash;
253
+ const headStamp = desiredHash === undefined || request.restampHead !== true ? [] : await restampHeadHash(connection, request, nextSequenceId("restamp", history.ids), desiredHash);
237
254
  await grantAppMeta(connection, request.appRole, session);
238
- return restamped.length === 0 ? report : { ...report, restamped };
255
+ const stamped = [...restamped, ...headStamp];
256
+ return stamped.length === 0 ? report : { ...report, restamped: stamped };
239
257
  } finally {
240
258
  if (locked) {
241
259
  await finishQuiet(() => connection.execute("select pg_advisory_unlock(hashtext($1))", [lockKey(request.target)]));
@@ -259,7 +277,10 @@ nothing to apply
259
277
  return;
260
278
  throw error;
261
279
  });
280
+ if (built !== undefined)
281
+ assertSnapshots(cwd, config, built.catalog);
262
282
  const progress = [];
283
+ const externalConfigured = (config.external ?? []).length > 0;
263
284
  const report = await applyTarget2({
264
285
  url: target.url,
265
286
  target: target.name,
@@ -276,6 +297,8 @@ nothing to apply
276
297
  catalogHash: head.catalogHash,
277
298
  reference: built === undefined ? [] : readReference(built.tables, built.casing, head.catalog)
278
299
  },
300
+ ...built !== undefined ? { headHash: startupHash2(built.catalog) } : {},
301
+ ...externalConfigured ? { restampHead: true } : {},
279
302
  ...config.roles !== undefined ? { migrationRole: config.roles.migration, appRole: config.roles.app } : {},
280
303
  ...catalogSchema(config.namespace),
281
304
  lintDirectory: directory,
@@ -299,37 +322,176 @@ async function pushProject2(cwd, flags) {
299
322
  const target = selectTarget(config, flags.target);
300
323
  assertTargetPolicy(target, "push", flags.allowProtected);
301
324
  const head = await projectHead(cwd);
302
- const plan = planMigration({
303
- before: head.previous,
325
+ assertSnapshots(cwd, config, head.catalog);
326
+ const catalogHash = startupHash2(head.catalog);
327
+ const report = await pushTarget({
328
+ url: target.url,
329
+ target: target.name,
330
+ protected: target.protected,
331
+ allowProtected: flags.allowProtected,
332
+ allowPooler: flags.allowPooler || config.allowPooler === true,
333
+ ...backfillTiming2(config.backfill),
304
334
  after: head.catalog,
305
335
  renames: head.renames,
306
- name: "push",
336
+ catalogHash,
337
+ ...config.roles !== undefined ? { migrationRole: config.roles.migration, appRole: config.roles.app, roles: config.roles } : {},
307
338
  ...catalogSchema(config.namespace),
308
339
  ...batchSizeField(config.backfill?.batchSize)
309
340
  });
310
- if (plan.steps.length === 0)
341
+ if (report.applied.length === 0 && (report.restamped ?? []).length === 0) {
311
342
  return `target ${target.name}
312
343
  no changes
313
344
  `;
314
- const report = await applyTarget2({
315
- url: target.url,
316
- target: target.name,
317
- protected: target.protected,
318
- allowProtected: flags.allowProtected,
319
- allowPooler: flags.allowPooler || config.allowPooler === true,
320
- ...backfillTiming2(config.backfill),
321
- migrations: [
322
- {
323
- id: "push",
324
- catalogHash: startupHash2(head.catalog),
325
- steps: plan.steps
326
- }
327
- ],
328
- ...config.roles !== undefined ? { migrationRole: config.roles.migration, appRole: config.roles.app } : {},
329
- ...catalogSchema(config.namespace)
330
- });
345
+ }
331
346
  return formatReport(report);
332
347
  }
348
+ async function pushTarget(request) {
349
+ assertDirectConnection(request.url, request.allowPooler === true);
350
+ const url = driverUrl(request.url);
351
+ const pool = open2({
352
+ url,
353
+ max: 1,
354
+ ...request.searchPath !== undefined ? { searchPath: request.searchPath } : {}
355
+ });
356
+ if (pool.reserve === undefined) {
357
+ await pool.close();
358
+ throw new OkmError2("invalid", "The driver cannot reserve a connection.", {
359
+ fix: { summary: "Push needs a driver with interactive transactions." }
360
+ });
361
+ }
362
+ const connection = await pool.reserve();
363
+ let locked = false;
364
+ try {
365
+ await probeStableBackend(request.url, () => backendPid(connection), request.allowPooler === true);
366
+ await assertStandardStrings(connection);
367
+ await setTimeouts(connection, {
368
+ url: request.url,
369
+ target: request.target,
370
+ protected: request.protected,
371
+ migrations: []
372
+ });
373
+ await lockTarget(connection, request.target);
374
+ locked = true;
375
+ const session = {};
376
+ const schema = request.schema ?? "public";
377
+ await enterCatalogSchema(connection, schema);
378
+ await ensureMigrationTables2((sql) => connection.execute(sql));
379
+ const history = await readHistory(connection);
380
+ const pushId = nextSequenceId("push", history.ids);
381
+ const sql = postgres(url, {
382
+ max: 1,
383
+ connect_timeout: 5,
384
+ idle_timeout: 1,
385
+ onnotice: () => {},
386
+ ...schema !== "public" ? { connection: { search_path: schema } } : {}
387
+ });
388
+ let planSteps = [];
389
+ try {
390
+ const runner = catalogQuery2(sql);
391
+ if (schema !== "public") {
392
+ await sql.unsafe(`create schema if not exists ${quoteIdent2(schema)}`);
393
+ await sql.unsafe(`set search_path to ${quoteIdent2(schema)}`);
394
+ }
395
+ const authorViews = await sealViews(runner, request.after);
396
+ const author = await sealDomainChecks2(runner, authorViews);
397
+ const live = await introspectSchema(runner, schema, schema, managedRoleOptions2(request.roles));
398
+ const plan = planMigration({
399
+ before: live,
400
+ after: author,
401
+ renames: request.renames,
402
+ name: pushId,
403
+ ...request.schema !== undefined ? { schema: request.schema } : {},
404
+ ...request.batchSize !== undefined ? { batchSize: request.batchSize } : {}
405
+ });
406
+ planSteps = plan.steps;
407
+ } finally {
408
+ await finishQuiet(() => sql.end({ timeout: 5 }));
409
+ }
410
+ if (planSteps.length === 0) {
411
+ const stamped = await restampHeadHash(connection, request, pushId, request.catalogHash);
412
+ await grantAppMeta(connection, request.appRole, session);
413
+ if (stamped.length === 0) {
414
+ return { target: request.target, applied: [] };
415
+ }
416
+ return { target: request.target, applied: [], restamped: stamped };
417
+ }
418
+ const migration = {
419
+ id: pushId,
420
+ catalogHash: request.catalogHash,
421
+ steps: planSteps
422
+ };
423
+ await prepareMigrationRole(connection, {
424
+ url: request.url,
425
+ target: request.target,
426
+ protected: request.protected,
427
+ migrations: [migration],
428
+ ...request.migrationRole !== undefined ? { migrationRole: request.migrationRole } : {},
429
+ ...request.schema !== undefined ? { schema: request.schema } : {}
430
+ }, session);
431
+ await assertExtensionsAvailable(connection, [migration]);
432
+ await assertUuidV7Available(connection, [migration]);
433
+ const units = applyUnits(migration, new Set);
434
+ const report = await runTarget(connection, {
435
+ policy: {
436
+ target: request.target,
437
+ protected: request.protected,
438
+ allowProtected: request.allowProtected === true
439
+ },
440
+ units,
441
+ retries: DEFAULT_RETRIES,
442
+ lockTimeoutMs: DEFAULT_LOCK_MS,
443
+ statementTimeoutMs: DEFAULT_STATEMENT_MS,
444
+ backfillStatementTimeoutMs: request.backfillStatementTimeoutMs ?? DEFAULT_STATEMENT_MS,
445
+ ...request.backfillPauseMs !== undefined ? { backfillPauseMs: request.backfillPauseMs } : {},
446
+ ...request.migrationRole !== undefined ? { migrationRole: request.migrationRole } : {},
447
+ ...request.schema !== undefined ? { schema: request.schema } : {}
448
+ }, session);
449
+ await grantAppMeta(connection, request.appRole, session);
450
+ return report;
451
+ } finally {
452
+ if (locked) {
453
+ await finishQuiet(() => connection.execute("select pg_advisory_unlock(hashtext($1))", [lockKey(request.target)]));
454
+ }
455
+ await finishQuiet(() => connection.release());
456
+ await finishQuiet(() => pool.close());
457
+ }
458
+ }
459
+ async function restampHeadHash(connection, request, migrationId, catalogHash) {
460
+ const present = await connection.execute("select to_regclass('okm_meta') is not null");
461
+ if (!wireTrue(present.rows[0]?.[0]))
462
+ return [];
463
+ const current = await connection.execute("select catalog_hash from okm_meta where id = 'head'");
464
+ const stored = current.rows[0]?.[0];
465
+ if (typeof stored === "string" && stored === catalogHash)
466
+ return [];
467
+ assertTargetPolicy({ name: request.target, protected: request.protected }, "history-repair", request.allowProtected === true);
468
+ await connection.execute("begin");
469
+ try {
470
+ await connection.execute("insert into okm_history (migration_id, step_index, class, catalog_hash) values ($1, 0, 'expand', $2)", [migrationId, catalogHash]);
471
+ await connection.execute(`insert into okm_meta (id, catalog_hash, migration_id) values ('head', $1, $2)
472
+ on conflict (id) do update set catalog_hash = excluded.catalog_hash, migration_id = excluded.migration_id`, [catalogHash, migrationId]);
473
+ await connection.execute("commit");
474
+ } catch (error) {
475
+ await connection.execute("rollback").catch(() => {
476
+ return;
477
+ });
478
+ throw error;
479
+ }
480
+ return [migrationId];
481
+ }
482
+ function nextSequenceId(prefix, ids) {
483
+ const pattern = new RegExp(`^${prefix}-(\\d+)$`);
484
+ let max = 0;
485
+ for (const id of ids) {
486
+ const match = pattern.exec(id);
487
+ if (match === null)
488
+ continue;
489
+ const value = Number(match[1]);
490
+ if (Number.isFinite(value) && value > max)
491
+ max = value;
492
+ }
493
+ return `${prefix}-${String(max + 1)}`;
494
+ }
333
495
  function backfillTiming2(backfill) {
334
496
  return {
335
497
  ...backfill?.pauseMs !== undefined ? { backfillPauseMs: backfill.pauseMs } : {},
@@ -341,10 +503,11 @@ function formatReport(report) {
341
503
  ` : "";
342
504
  const restamped = (report.restamped ?? []).map((id) => `restamped ${id}
343
505
  `).join("");
344
- if (report.applied.length === 0)
506
+ if (report.applied.length === 0) {
345
507
  return `target ${report.target}
346
508
  ${role}${restamped}nothing to apply
347
509
  `;
510
+ }
348
511
  return `target ${report.target}
349
512
  ${role}${restamped}${report.applied.map((id) => `applied ${id}`).join(`
350
513
  `)}