okmodel 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -12
- package/dist/contracts/catalog/document.d.ts +20 -0
- package/dist/contracts/driver.d.ts +7 -0
- package/dist/dialects/pg/filter-parse.d.ts +8 -2
- package/dist/dialects/pg/fn/index.js +8 -0
- package/dist/dialects/pg/index.js +8 -2
- package/dist/dialects/pg/introspect.d.ts +6 -0
- package/dist/dialects/pg/ops/or.d.ts +1 -1
- package/dist/dialects/pg/role/sql.d.ts +10 -0
- package/dist/dialects/pg/schema.d.ts +15 -6
- package/dist/dialects/pg/temporal.d.ts +5 -76
- package/dist/dialects/pg/view/index.d.ts +20 -6
- package/dist/dialects/pg/view/index.js +1 -1
- package/dist/dialects/pg/view/scratch.d.ts +2 -2
- package/dist/runtime/client.d.ts +6 -1
- package/dist/runtime/conflict.d.ts +2 -1
- package/dist/runtime/pg/bun.d.ts +12 -7
- package/dist/runtime/pg/bun.js +7 -4
- package/dist/runtime/pg/pg.d.ts +12 -6
- package/dist/runtime/pg/pg.js +7 -4
- package/dist/runtime/pg/pglite.d.ts +11 -3
- package/dist/runtime/pg/pglite.js +7 -4
- package/dist/runtime/pg/postgresjs.d.ts +12 -6
- package/dist/runtime/pg/postgresjs.js +7 -4
- package/dist/runtime/plan.d.ts +16 -1
- package/dist/runtime/tenancy/index.js +1 -1
- package/dist/runtime/topology.d.ts +191 -0
- package/dist/runtime/traits/index.js +7 -7
- package/dist/runtime/types.d.ts +57 -2
- package/dist/runtime/validate/index.js +3 -3
- package/dist/shared/{radd3z4g.js → 0t22wwh1.js} +5 -9
- package/dist/shared/{ady1ms74.js → 2pqx26h8.js} +25 -10
- package/dist/shared/{kyxb62mc.js → 31vm914h.js} +9 -1
- package/dist/shared/{jns2wx4t.js → 32zqc1dw.js} +18 -7
- package/dist/shared/{nxb42q7h.js → 47x8x9ze.js} +2 -2
- package/dist/shared/{zvbjc949.js → 4az561fn.js} +1 -1
- package/dist/shared/{s6gqfn5j.js → 51mvgj8w.js} +27 -3
- package/dist/shared/{k3ted12y.js → 54tqkxja.js} +20 -2
- package/dist/shared/{3p4b1fg0.js → 5enne4ma.js} +1 -1
- package/dist/shared/{6693j9rs.js → 5fqx9yh4.js} +6 -2
- package/dist/shared/{c5g81ngb.js → 6mnp99hx.js} +11 -8
- package/dist/shared/{hqjsxnz7.js → 6qd27mx0.js} +5 -4
- package/dist/shared/{v7tpnhnz.js → 6rk2gen5.js} +5 -3
- package/dist/shared/{2321avmn.js → 7zwwyh0v.js} +46 -16
- package/dist/shared/{fa9vt245.js → 87jvvhnt.js} +2 -2
- package/dist/shared/{8jb4ymh2.js → 8bwbwy8h.js} +12 -1
- package/dist/shared/{c8aghh05.js → 8rsy3hxz.js} +7 -1
- package/dist/shared/{41kv2xdm.js → 8xsbagad.js} +1 -1
- package/dist/shared/{6p5k20sm.js → 96qcj21c.js} +3 -3
- package/dist/shared/{jgxzjzcz.js → aj2qxyhj.js} +2 -2
- package/dist/shared/{5vdm4sgp.js → caw8b9d8.js} +4 -4
- package/dist/shared/{s4mb6a4b.js → cctw17hj.js} +74 -15
- package/dist/shared/{rrstp3kb.js → cgbamx3e.js} +2 -2
- package/dist/shared/{axhk06q1.js → cr74e96e.js} +1 -1
- package/dist/shared/{b066g80h.js → efmy2gcg.js} +2 -2
- package/dist/shared/{gdh9mrtp.js → f5cw5q5r.js} +2 -2
- package/dist/shared/{xghkt7a9.js → fancm5pt.js} +6 -233
- package/dist/shared/{n5y9zyj1.js → ga3sfy93.js} +369 -14
- package/dist/shared/{xrfnq5k8.js → gbtcc2d7.js} +55 -5
- package/dist/shared/{7nk68fj8.js → gde73es6.js} +20 -15
- package/dist/shared/{kkzwp3d2.js → hf22e0wt.js} +55 -19
- package/dist/shared/{tny2t3sm.js → janx09m5.js} +8 -7
- package/dist/shared/{4ef7mmqf.js → jwtxdj06.js} +5 -5
- package/dist/shared/{sf8m6tfy.js → k8v5v5wj.js} +3 -3
- package/dist/shared/{qn0srwwq.js → kfjp0gck.js} +5 -3
- package/dist/shared/{wqem4jed.js → krxx77g0.js} +7 -7
- package/dist/shared/{edwqqryd.js → nkaxvt9q.js} +1 -1
- package/dist/shared/{jsd07prz.js → ntneex2a.js} +1 -1
- package/dist/shared/nxdp2ze3.js +1197 -0
- package/dist/shared/{tbszx8mz.js → pf0d8f77.js} +1 -1
- package/dist/shared/{84ptn35q.js → ps7a6h14.js} +10 -9
- package/dist/shared/{kj680nxv.js → q58y17hy.js} +9 -7
- package/dist/shared/{awrsqrn7.js → qm7cdtvr.js} +22 -9
- package/dist/shared/{q6zsyh31.js → qv2ydck9.js} +156 -205
- package/dist/shared/{kafaa5zv.js → r2sp8hrc.js} +28 -8
- package/dist/shared/{fb00y8fp.js → r8x5qhd7.js} +5 -5
- package/dist/shared/{yjw02enp.js → sdc7r2ww.js} +8 -7
- package/dist/shared/sycnx8gt.js +289 -0
- package/dist/shared/{hcmdtwgh.js → szm0cksa.js} +4 -1
- package/dist/shared/{zg24x0hm.js → tm5ab8we.js} +10 -2
- package/dist/shared/{ygem90rn.js → v36c5g6p.js} +85 -21
- package/dist/shared/wq9n6pwr.js +139 -0
- package/dist/shared/{7p2wbz7f.js → x72jb7ad.js} +1 -1
- package/dist/shared/{q795afwa.js → z2rnngj4.js} +3 -3
- package/dist/shared/{sr5ms7b3.js → zqrnhwsq.js} +3 -3
- package/dist/tooling/cli.js +70 -23
- package/dist/tooling/migrate/apply.d.ts +2 -0
- package/dist/tooling/migrate/commands.d.ts +1 -2
- package/dist/tooling/migrate/drift.d.ts +1 -1
- package/dist/tooling/migrate/estimate.d.ts +14 -1
- package/dist/tooling/migrate/files.d.ts +7 -0
- package/dist/tooling/migrate/index.js +12 -9
- package/dist/tooling/migrate/lint.d.ts +3 -3
- package/dist/tooling/migrate/plan.d.ts +7 -3
- package/dist/tooling/migrate/policy.d.ts +11 -0
- package/dist/tooling/migrate/sql-lex.d.ts +22 -0
- package/dist/tooling/testing/factories.d.ts +10 -0
- package/dist/tooling/testing/facts.d.ts +3 -0
- package/dist/tooling/testing/index.js +3 -3
- package/dist/tooling/testing/isolation.d.ts +4 -3
- package/package.json +6 -5
- package/dist/shared/0v1g424p.js +0 -26
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
creationOrder2,
|
|
3
3
|
renameColumn2,
|
|
4
4
|
renameTable2
|
|
5
|
-
} from "./
|
|
5
|
+
} from "./8bwbwy8h.js";
|
|
6
6
|
import {
|
|
7
7
|
quoteIdent,
|
|
8
8
|
ownedByView,
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
identitySequence,
|
|
16
16
|
createTableSql,
|
|
17
17
|
qualify
|
|
18
|
-
} from "./
|
|
18
|
+
} from "./8xsbagad.js";
|
|
19
19
|
import {
|
|
20
20
|
isExactVersion,
|
|
21
21
|
compareExtensionVersions,
|
|
@@ -122,7 +122,7 @@ function privilegeSql(before, after, schema, recreated = new Set) {
|
|
|
122
122
|
revoke.push({ sql: grantStatement(object, schema, true), kind: "revoke" });
|
|
123
123
|
}
|
|
124
124
|
if (object.kind === "defaultPrivilege") {
|
|
125
|
-
revoke.push({ sql: defaultStatement(object, true), kind: "revoke-default" });
|
|
125
|
+
revoke.push({ sql: defaultStatement(object, schema, true), kind: "revoke-default" });
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
for (const [key, object] of afterBy) {
|
|
@@ -151,7 +151,7 @@ function privilegeSql(before, after, schema, recreated = new Set) {
|
|
|
151
151
|
if (previous !== undefined)
|
|
152
152
|
continue;
|
|
153
153
|
if (object.kind === "defaultPrivilege") {
|
|
154
|
-
defaults.push({ sql: defaultStatement(object, false), kind: "grant-default" });
|
|
154
|
+
defaults.push({ sql: defaultStatement(object, schema, false), kind: "grant-default" });
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
return { revoke, prepare: [...roles, ...defaults], grant };
|
|
@@ -163,6 +163,9 @@ function createdRoleName(sql) {
|
|
|
163
163
|
return quoted.replaceAll('""', '"');
|
|
164
164
|
return match?.[2];
|
|
165
165
|
}
|
|
166
|
+
function createRoleOnce(sql) {
|
|
167
|
+
return `do $okm$ begin ${sql}; exception when duplicate_object or unique_violation then null; end $okm$`;
|
|
168
|
+
}
|
|
166
169
|
function changesRole(sql) {
|
|
167
170
|
return /^\s*(create|alter)\s+role\b/i.test(sql);
|
|
168
171
|
}
|
|
@@ -195,13 +198,12 @@ function grantStatement(object, schema, revoke) {
|
|
|
195
198
|
const direction = revoke ? "from" : "to";
|
|
196
199
|
return `${verb} ${privilege} on ${grantTarget(object.identity.object, schema)} ${direction} ${quoteIdent(object.identity.role)}`;
|
|
197
200
|
}
|
|
198
|
-
function defaultStatement(object, revoke) {
|
|
201
|
+
function defaultStatement(object, schema, revoke) {
|
|
199
202
|
const privilege = object.identity.privilege.toLowerCase();
|
|
200
203
|
assertPrivilege(privilege);
|
|
201
204
|
const verb = revoke ? "revoke" : "grant";
|
|
202
205
|
const direction = revoke ? "from" : "to";
|
|
203
|
-
|
|
204
|
-
return `alter default privileges for role ${quoteIdent(object.identity.forRole)} in schema ${quoteIdent(namespace)} ${verb} ${privilege} on ${objectKindSql(object.identity.objectKind)} ${direction} ${quoteIdent(object.identity.grantee)}`;
|
|
206
|
+
return `alter default privileges for role ${quoteIdent(object.identity.forRole)} in schema ${quoteIdent(schema)} ${verb} ${privilege} on ${objectKindSql(object.identity.objectKind)} ${direction} ${quoteIdent(object.identity.grantee)}`;
|
|
205
207
|
}
|
|
206
208
|
function grantTargetKey(object) {
|
|
207
209
|
if (object.kind === "table" || object.kind === "view" || object.kind === "materializedView" || object.kind === "sequence") {
|
|
@@ -222,14 +224,13 @@ function grantTargetKey(object) {
|
|
|
222
224
|
});
|
|
223
225
|
}
|
|
224
226
|
function grantTarget(object, schema) {
|
|
225
|
-
const name = object.namespace.form === "static" ? object.namespace.name : schema;
|
|
226
227
|
if (object.kind === "sequence")
|
|
227
|
-
return `sequence ${qualify(
|
|
228
|
+
return `sequence ${qualify(schema, object.name)}`;
|
|
228
229
|
if (object.kind === "function")
|
|
229
|
-
return functionTarget(object.name,
|
|
230
|
+
return functionTarget(object.name, schema);
|
|
230
231
|
if (object.kind === "namespace")
|
|
231
232
|
return `schema ${quoteIdent(object.name)}`;
|
|
232
|
-
return `table ${qualify(
|
|
233
|
+
return `table ${qualify(schema, object.name)}`;
|
|
233
234
|
}
|
|
234
235
|
function functionTarget(name, schema) {
|
|
235
236
|
const open = name.lastIndexOf("(");
|
|
@@ -975,6 +976,189 @@ function functionCalls(expression) {
|
|
|
975
976
|
return names;
|
|
976
977
|
}
|
|
977
978
|
|
|
979
|
+
// src/tooling/migrate/sql-lex.ts
|
|
980
|
+
function sqlStatements(sql) {
|
|
981
|
+
return scan(sql).statements;
|
|
982
|
+
}
|
|
983
|
+
function sqlQuoteOpen(sql) {
|
|
984
|
+
return scan(sql).open;
|
|
985
|
+
}
|
|
986
|
+
function scan(sql) {
|
|
987
|
+
const statements = [];
|
|
988
|
+
const parts = [];
|
|
989
|
+
let index = 0;
|
|
990
|
+
let open = false;
|
|
991
|
+
const flush = () => {
|
|
992
|
+
const text = parts.join("").trim();
|
|
993
|
+
parts.length = 0;
|
|
994
|
+
if (text.length > 0)
|
|
995
|
+
statements.push(text);
|
|
996
|
+
};
|
|
997
|
+
while (index < sql.length) {
|
|
998
|
+
if (sql.startsWith("--", index)) {
|
|
999
|
+
parts.push(" ");
|
|
1000
|
+
index = lineCommentEnd(sql, index + 2);
|
|
1001
|
+
continue;
|
|
1002
|
+
}
|
|
1003
|
+
if (sql.startsWith("/*", index)) {
|
|
1004
|
+
parts.push(" ");
|
|
1005
|
+
const block = blockComment(sql, index + 2);
|
|
1006
|
+
index = block.index;
|
|
1007
|
+
if (block.open) {
|
|
1008
|
+
open = true;
|
|
1009
|
+
break;
|
|
1010
|
+
}
|
|
1011
|
+
continue;
|
|
1012
|
+
}
|
|
1013
|
+
const dollar = dollarAt(sql, index);
|
|
1014
|
+
if (dollar !== undefined) {
|
|
1015
|
+
const closed = takeDollar(sql, dollar);
|
|
1016
|
+
parts.push(sql.slice(index, closed.index));
|
|
1017
|
+
index = closed.index;
|
|
1018
|
+
if (closed.open) {
|
|
1019
|
+
open = true;
|
|
1020
|
+
break;
|
|
1021
|
+
}
|
|
1022
|
+
continue;
|
|
1023
|
+
}
|
|
1024
|
+
if (isEscapeString(sql, index)) {
|
|
1025
|
+
const quote = takeQuoted(sql, index + 2, true);
|
|
1026
|
+
parts.push(sql.slice(index, quote.index));
|
|
1027
|
+
index = quote.index;
|
|
1028
|
+
if (quote.open) {
|
|
1029
|
+
open = true;
|
|
1030
|
+
break;
|
|
1031
|
+
}
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
const char = sql[index];
|
|
1035
|
+
if (char === "'") {
|
|
1036
|
+
const quote = takeQuoted(sql, index + 1, false);
|
|
1037
|
+
parts.push(sql.slice(index, quote.index));
|
|
1038
|
+
index = quote.index;
|
|
1039
|
+
if (quote.open) {
|
|
1040
|
+
open = true;
|
|
1041
|
+
break;
|
|
1042
|
+
}
|
|
1043
|
+
continue;
|
|
1044
|
+
}
|
|
1045
|
+
if (char === '"') {
|
|
1046
|
+
const quote = takeIdent(sql, index + 1);
|
|
1047
|
+
parts.push(sql.slice(index, quote.index));
|
|
1048
|
+
index = quote.index;
|
|
1049
|
+
if (quote.open) {
|
|
1050
|
+
open = true;
|
|
1051
|
+
break;
|
|
1052
|
+
}
|
|
1053
|
+
continue;
|
|
1054
|
+
}
|
|
1055
|
+
if (char === ";") {
|
|
1056
|
+
flush();
|
|
1057
|
+
index += 1;
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
parts.push(char ?? "");
|
|
1061
|
+
index += 1;
|
|
1062
|
+
}
|
|
1063
|
+
flush();
|
|
1064
|
+
return { statements, open };
|
|
1065
|
+
}
|
|
1066
|
+
function lineCommentEnd(sql, index) {
|
|
1067
|
+
while (index < sql.length && sql.charCodeAt(index) !== 10)
|
|
1068
|
+
index += 1;
|
|
1069
|
+
return index;
|
|
1070
|
+
}
|
|
1071
|
+
function blockComment(sql, index) {
|
|
1072
|
+
let depth = 1;
|
|
1073
|
+
while (index < sql.length) {
|
|
1074
|
+
if (sql.startsWith("/*", index)) {
|
|
1075
|
+
depth += 1;
|
|
1076
|
+
index += 2;
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
if (sql.startsWith("*/", index)) {
|
|
1080
|
+
depth -= 1;
|
|
1081
|
+
index += 2;
|
|
1082
|
+
if (depth === 0)
|
|
1083
|
+
return { index, open: false };
|
|
1084
|
+
continue;
|
|
1085
|
+
}
|
|
1086
|
+
index += 1;
|
|
1087
|
+
}
|
|
1088
|
+
return { index, open: true };
|
|
1089
|
+
}
|
|
1090
|
+
function dollarAt(sql, index) {
|
|
1091
|
+
if (sql.charCodeAt(index) !== 36)
|
|
1092
|
+
return;
|
|
1093
|
+
const next = index + 1;
|
|
1094
|
+
if (sql.charCodeAt(next) === 36)
|
|
1095
|
+
return { tag: "", end: next + 1 };
|
|
1096
|
+
if (!isIdentStart(sql.charCodeAt(next)))
|
|
1097
|
+
return;
|
|
1098
|
+
let cursor = next + 1;
|
|
1099
|
+
while (isIdentCont(sql.charCodeAt(cursor)))
|
|
1100
|
+
cursor += 1;
|
|
1101
|
+
if (sql.charCodeAt(cursor) !== 36)
|
|
1102
|
+
return;
|
|
1103
|
+
return { tag: sql.slice(next, cursor), end: cursor + 1 };
|
|
1104
|
+
}
|
|
1105
|
+
function takeDollar(sql, dollar) {
|
|
1106
|
+
const closer = `$${dollar.tag}$`;
|
|
1107
|
+
const found = sql.indexOf(closer, dollar.end);
|
|
1108
|
+
if (found === -1)
|
|
1109
|
+
return { index: sql.length, open: true };
|
|
1110
|
+
return { index: found + closer.length, open: false };
|
|
1111
|
+
}
|
|
1112
|
+
function takeQuoted(sql, index, escape) {
|
|
1113
|
+
while (index < sql.length) {
|
|
1114
|
+
const char = sql[index];
|
|
1115
|
+
if (escape && char === "\\") {
|
|
1116
|
+
if (index + 1 >= sql.length)
|
|
1117
|
+
return { index: sql.length, open: true };
|
|
1118
|
+
index += 2;
|
|
1119
|
+
continue;
|
|
1120
|
+
}
|
|
1121
|
+
if (char === "'") {
|
|
1122
|
+
if (sql[index + 1] === "'") {
|
|
1123
|
+
index += 2;
|
|
1124
|
+
continue;
|
|
1125
|
+
}
|
|
1126
|
+
return { index: index + 1, open: false };
|
|
1127
|
+
}
|
|
1128
|
+
index += 1;
|
|
1129
|
+
}
|
|
1130
|
+
return { index, open: true };
|
|
1131
|
+
}
|
|
1132
|
+
function takeIdent(sql, index) {
|
|
1133
|
+
while (index < sql.length) {
|
|
1134
|
+
if (sql[index] === '"') {
|
|
1135
|
+
if (sql[index + 1] === '"') {
|
|
1136
|
+
index += 2;
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
return { index: index + 1, open: false };
|
|
1140
|
+
}
|
|
1141
|
+
index += 1;
|
|
1142
|
+
}
|
|
1143
|
+
return { index, open: true };
|
|
1144
|
+
}
|
|
1145
|
+
function isEscapeString(sql, index) {
|
|
1146
|
+
const code = sql.charCodeAt(index);
|
|
1147
|
+
if (code !== 69 && code !== 101)
|
|
1148
|
+
return false;
|
|
1149
|
+
if (sql.charCodeAt(index + 1) !== 39)
|
|
1150
|
+
return false;
|
|
1151
|
+
if (index > 0 && isIdentCont(sql.charCodeAt(index - 1)))
|
|
1152
|
+
return false;
|
|
1153
|
+
return true;
|
|
1154
|
+
}
|
|
1155
|
+
function isIdentStart(code) {
|
|
1156
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 95;
|
|
1157
|
+
}
|
|
1158
|
+
function isIdentCont(code) {
|
|
1159
|
+
return isIdentStart(code) || code >= 48 && code <= 57;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
978
1162
|
// src/tooling/migrate/values.ts
|
|
979
1163
|
function parseReplace(flag) {
|
|
980
1164
|
const eq = flag.indexOf("=");
|
|
@@ -1376,9 +1560,16 @@ function parsePlan(text) {
|
|
|
1376
1560
|
let behavior;
|
|
1377
1561
|
const allows = [];
|
|
1378
1562
|
const sql = [];
|
|
1379
|
-
|
|
1563
|
+
let quoteOpen = false;
|
|
1564
|
+
while (index < lines.length && (quoteOpen || (lines[index] ?? "") !== "")) {
|
|
1380
1565
|
const line = lines[index] ?? "";
|
|
1381
1566
|
index += 1;
|
|
1567
|
+
if (quoteOpen) {
|
|
1568
|
+
sql.push(line);
|
|
1569
|
+
quoteOpen = sqlQuoteOpen(sql.join(`
|
|
1570
|
+
`));
|
|
1571
|
+
continue;
|
|
1572
|
+
}
|
|
1382
1573
|
if (line.startsWith("-- class: ")) {
|
|
1383
1574
|
stepClass = readClass(line.slice("-- class: ".length));
|
|
1384
1575
|
sawClass = true;
|
|
@@ -1400,8 +1591,11 @@ function parsePlan(text) {
|
|
|
1400
1591
|
behavior = "change";
|
|
1401
1592
|
else if (line.startsWith("-- okm-allow"))
|
|
1402
1593
|
allows.push(readAllow(line));
|
|
1403
|
-
else if (!line.startsWith("--"))
|
|
1594
|
+
else if (!line.startsWith("--")) {
|
|
1404
1595
|
sql.push(line);
|
|
1596
|
+
quoteOpen = sqlQuoteOpen(sql.join(`
|
|
1597
|
+
`));
|
|
1598
|
+
}
|
|
1405
1599
|
}
|
|
1406
1600
|
const statement = sql.join(`
|
|
1407
1601
|
`).replace(/;\s*$/, "");
|
|
@@ -2428,4 +2622,165 @@ function order(value) {
|
|
|
2428
2622
|
return value;
|
|
2429
2623
|
}
|
|
2430
2624
|
|
|
2431
|
-
|
|
2625
|
+
// src/tooling/migrate/policy.ts
|
|
2626
|
+
var READ = new Set([
|
|
2627
|
+
"plan",
|
|
2628
|
+
"status",
|
|
2629
|
+
"check",
|
|
2630
|
+
"drift",
|
|
2631
|
+
"verify",
|
|
2632
|
+
"pull",
|
|
2633
|
+
"catalog-export",
|
|
2634
|
+
"inspect"
|
|
2635
|
+
]);
|
|
2636
|
+
var ALLOWED = new Set(["expand", "reference", "provision"]);
|
|
2637
|
+
var FORWARD_ONLY = new Set(["drop", "rollback"]);
|
|
2638
|
+
function assertTargetPolicy(target, operation, allowProtected = false) {
|
|
2639
|
+
if (FORWARD_ONLY.has(operation)) {
|
|
2640
|
+
throw new OkmError2("invalid", "There is no down migration.", {
|
|
2641
|
+
fix: { summary: "Recovery is a new forward migration." }
|
|
2642
|
+
});
|
|
2643
|
+
}
|
|
2644
|
+
if (!target.protected || allowProtected)
|
|
2645
|
+
return;
|
|
2646
|
+
if (READ.has(operation) || ALLOWED.has(operation))
|
|
2647
|
+
return;
|
|
2648
|
+
throw new OkmError2("OKM1850", `${operation} is blocked on protected target ${target.name}.`, {
|
|
2649
|
+
kind: "forbidden",
|
|
2650
|
+
fix: {
|
|
2651
|
+
summary: "Run a read-only command or an expand migration, or pass --allow-protected for this invocation."
|
|
2652
|
+
}
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
function assertTargetAlias(targets) {
|
|
2656
|
+
const groups = new Map;
|
|
2657
|
+
for (const target of targets) {
|
|
2658
|
+
const key = `${target.host}
|
|
2659
|
+
${target.port}
|
|
2660
|
+
${target.database}`;
|
|
2661
|
+
const group = groups.get(key);
|
|
2662
|
+
if (group === undefined)
|
|
2663
|
+
groups.set(key, [target]);
|
|
2664
|
+
else
|
|
2665
|
+
group.push(target);
|
|
2666
|
+
}
|
|
2667
|
+
for (const group of groups.values()) {
|
|
2668
|
+
const protectedTarget = group.find((target) => target.protected);
|
|
2669
|
+
const open = group.find((target) => !target.protected);
|
|
2670
|
+
if (protectedTarget !== undefined && open !== undefined) {
|
|
2671
|
+
throw new OkmError2("OKM1852", `${open.name} and ${protectedTarget.name} resolve to the same database and disagree about protection.`, {
|
|
2672
|
+
fix: {
|
|
2673
|
+
summary: "Give those targets the same protection flag. okm check and okm doctor both report this."
|
|
2674
|
+
}
|
|
2675
|
+
});
|
|
2676
|
+
}
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
function assertDirectConnection(url, allowPooler) {
|
|
2680
|
+
if (allowPooler)
|
|
2681
|
+
return;
|
|
2682
|
+
const endpoint = endpointOf(url);
|
|
2683
|
+
const parsed = new URL(url);
|
|
2684
|
+
const host = endpoint.host;
|
|
2685
|
+
if (host.includes("pooler") || host.includes("pgbouncer") || host.includes("-pooler") || endpoint.port === "6543" || endpoint.port === "6432" || parsed.searchParams.get("pgbouncer") === "true") {
|
|
2686
|
+
throw new OkmError2("invalid", `Refusing pooler host ${host}. Apply needs a direct connection.`, { fix: { summary: "Use a direct connection, or pass --allow-pooler." } });
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
async function assertStableBackend(readPid, allowPooler) {
|
|
2690
|
+
if (allowPooler)
|
|
2691
|
+
return;
|
|
2692
|
+
const first = await readPid();
|
|
2693
|
+
const second = await readPid();
|
|
2694
|
+
if (first === second)
|
|
2695
|
+
return;
|
|
2696
|
+
throw new OkmError2("OKM1854", `The backend pid changed from ${first} to ${second}. A pooler is handing out a new session between statements.`, { fix: { summary: "Use a direct connection, or pass --allow-pooler." } });
|
|
2697
|
+
}
|
|
2698
|
+
function listTargets(config) {
|
|
2699
|
+
if (config.database !== undefined && config.targets !== undefined) {
|
|
2700
|
+
throw new OkmError2("invalid", "Set database or targets, not both.", {
|
|
2701
|
+
fix: { summary: "database is the one target named default. Use targets for more than one." }
|
|
2702
|
+
});
|
|
2703
|
+
}
|
|
2704
|
+
const entries = [];
|
|
2705
|
+
if (config.database !== undefined)
|
|
2706
|
+
entries.push(["default", config.database]);
|
|
2707
|
+
if (config.targets !== undefined) {
|
|
2708
|
+
for (const name of Object.keys(config.targets).sort()) {
|
|
2709
|
+
const input = config.targets[name];
|
|
2710
|
+
if (input !== undefined)
|
|
2711
|
+
entries.push([name, input]);
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
return entries.map(([name, input]) => resolveTarget(name, input));
|
|
2715
|
+
}
|
|
2716
|
+
function selectTarget(config, name) {
|
|
2717
|
+
const targets = listTargets(config);
|
|
2718
|
+
if (targets.length === 0) {
|
|
2719
|
+
throw new OkmError2("OKM1845", "No target is configured.", {
|
|
2720
|
+
fix: { summary: "Set database or targets in okmodel.config.ts." }
|
|
2721
|
+
});
|
|
2722
|
+
}
|
|
2723
|
+
if (name === undefined) {
|
|
2724
|
+
if (targets.length === 1) {
|
|
2725
|
+
const only = targets[0];
|
|
2726
|
+
if (only !== undefined)
|
|
2727
|
+
return only;
|
|
2728
|
+
}
|
|
2729
|
+
throw new OkmError2("OKM1853", "Several targets are configured and --target was not passed.", {
|
|
2730
|
+
fix: { summary: "Pass --target <name>. The command does not guess." }
|
|
2731
|
+
});
|
|
2732
|
+
}
|
|
2733
|
+
const found = targets.find((target) => target.name === name);
|
|
2734
|
+
if (found === undefined) {
|
|
2735
|
+
throw new OkmError2("OKM1845", `Target ${name} is not configured.`, {
|
|
2736
|
+
fix: { summary: "Pass a name from targets, or database for the target named default." }
|
|
2737
|
+
});
|
|
2738
|
+
}
|
|
2739
|
+
return found;
|
|
2740
|
+
}
|
|
2741
|
+
function resolveTarget(name, input) {
|
|
2742
|
+
if (typeof input !== "string" && isReplicaTarget(input)) {
|
|
2743
|
+
throw new OkmError2("OKM1845", `Target ${name} is a replica. Migrations run on the primary only.`, {
|
|
2744
|
+
fix: {
|
|
2745
|
+
summary: "Set database or targets to the primary URL. A replica is not a migration target."
|
|
2746
|
+
}
|
|
2747
|
+
});
|
|
2748
|
+
}
|
|
2749
|
+
const url = typeof input === "string" ? input : input.url;
|
|
2750
|
+
const protectedTarget = typeof input === "string" ? false : input.protected === true;
|
|
2751
|
+
if (url === undefined || url.length === 0) {
|
|
2752
|
+
throw new OkmError2("OKM1845", `Target ${name} has no url.`, {
|
|
2753
|
+
fix: { summary: "Set the target url. A target is not inferred from the environment." }
|
|
2754
|
+
});
|
|
2755
|
+
}
|
|
2756
|
+
const endpoint = endpointOf(url);
|
|
2757
|
+
return {
|
|
2758
|
+
name,
|
|
2759
|
+
url,
|
|
2760
|
+
protected: protectedTarget,
|
|
2761
|
+
host: endpoint.host,
|
|
2762
|
+
port: endpoint.port,
|
|
2763
|
+
database: endpoint.database
|
|
2764
|
+
};
|
|
2765
|
+
}
|
|
2766
|
+
function isReplicaTarget(input) {
|
|
2767
|
+
return "primary" in input || "replicas" in input || "weight" in input || "pool" in input;
|
|
2768
|
+
}
|
|
2769
|
+
function endpointOf(url) {
|
|
2770
|
+
let parsed;
|
|
2771
|
+
try {
|
|
2772
|
+
parsed = new URL(url);
|
|
2773
|
+
} catch {
|
|
2774
|
+
throw new OkmError2("OKM1845", "The target URL is not a Postgres URL.", {
|
|
2775
|
+
fix: { summary: "Pass a postgres:// URL for the target." }
|
|
2776
|
+
});
|
|
2777
|
+
}
|
|
2778
|
+
const database = decodeURIComponent(parsed.pathname.replace(/^\//, ""));
|
|
2779
|
+
return {
|
|
2780
|
+
host: parsed.hostname.toLowerCase(),
|
|
2781
|
+
port: parsed.port.length > 0 ? parsed.port : "5432",
|
|
2782
|
+
database
|
|
2783
|
+
};
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
export { canonicalTypeName, createdRoleName, createRoleOnce, changesRole, DEFAULT_BACKFILL_PAUSE_MS, DEFAULT_BACKFILL_STATEMENT_TIMEOUT_MS, batchSizeField, aboutBatchCount, boundaryQuery, checkedCast, classOf, strictestClass, assertExtensionsAvailable, omitManagedObjects, sqlStatements, parseReplace, planMigration, formatPlan, parsePlan, staleRenames, assertTargetPolicy, assertTargetAlias, assertDirectConnection, assertStableBackend, listTargets, selectTarget };
|
|
@@ -42,12 +42,21 @@ var FIND_OPTIONS = [
|
|
|
42
42
|
"limit",
|
|
43
43
|
"orderBy",
|
|
44
44
|
"select",
|
|
45
|
+
"route",
|
|
45
46
|
"signal",
|
|
46
47
|
"timeout",
|
|
47
48
|
"where"
|
|
48
49
|
];
|
|
49
|
-
var ONE_OPTIONS = [
|
|
50
|
-
|
|
50
|
+
var ONE_OPTIONS = [
|
|
51
|
+
"include",
|
|
52
|
+
"orderBy",
|
|
53
|
+
"route",
|
|
54
|
+
"select",
|
|
55
|
+
"signal",
|
|
56
|
+
"timeout",
|
|
57
|
+
"where"
|
|
58
|
+
];
|
|
59
|
+
var FILTER_OPTIONS = ["route", "signal", "timeout", "where"];
|
|
51
60
|
function readOptionNames(op) {
|
|
52
61
|
if (op === "find")
|
|
53
62
|
return FIND_OPTIONS;
|
|
@@ -524,7 +533,9 @@ function emitIn(column, ref, name, value, sink) {
|
|
|
524
533
|
sink.text(")");
|
|
525
534
|
}
|
|
526
535
|
function emitOr(schema, table, value, sink, alias, depth) {
|
|
527
|
-
if (!Array.isArray(value)
|
|
536
|
+
if (!Array.isArray(value))
|
|
537
|
+
orFail(false);
|
|
538
|
+
if (value.length === 0) {
|
|
528
539
|
sink.text("false");
|
|
529
540
|
sink.mark(":0");
|
|
530
541
|
return;
|
|
@@ -550,7 +561,7 @@ function emitOr(schema, table, value, sink, alias, depth) {
|
|
|
550
561
|
emitPredicate(schema, table, key, item, sink, alias, depth);
|
|
551
562
|
}
|
|
552
563
|
if (!wrote)
|
|
553
|
-
|
|
564
|
+
orFail(true);
|
|
554
565
|
sink.text(")");
|
|
555
566
|
}
|
|
556
567
|
sink.text(")");
|
|
@@ -835,6 +846,45 @@ function list(names) {
|
|
|
835
846
|
function isRecord(value) {
|
|
836
847
|
return typeof value === "object" && value !== null && !Array.isArray(value) && !isOperator(value);
|
|
837
848
|
}
|
|
849
|
+
function effectivePredicate(where) {
|
|
850
|
+
if (where === undefined)
|
|
851
|
+
return false;
|
|
852
|
+
if (isOperator(where)) {
|
|
853
|
+
const name = operatorName(where);
|
|
854
|
+
const value = operatorValue(where);
|
|
855
|
+
if (name === "or") {
|
|
856
|
+
if (!Array.isArray(value))
|
|
857
|
+
orFail(false);
|
|
858
|
+
if (value.length === 0)
|
|
859
|
+
return true;
|
|
860
|
+
for (const branch of value) {
|
|
861
|
+
if (!effectivePredicate(branch))
|
|
862
|
+
orFail(true);
|
|
863
|
+
}
|
|
864
|
+
return true;
|
|
865
|
+
}
|
|
866
|
+
if (name === "and") {
|
|
867
|
+
return Array.isArray(value) && value.some((part) => effectivePredicate(part));
|
|
868
|
+
}
|
|
869
|
+
if (name === "not")
|
|
870
|
+
return effectivePredicate(value);
|
|
871
|
+
return true;
|
|
872
|
+
}
|
|
873
|
+
if (!isRecord(where))
|
|
874
|
+
return true;
|
|
875
|
+
for (const key of Object.keys(where)) {
|
|
876
|
+
if (where[key] !== undefined)
|
|
877
|
+
return true;
|
|
878
|
+
}
|
|
879
|
+
return false;
|
|
880
|
+
}
|
|
881
|
+
function orFail(empty) {
|
|
882
|
+
throw new OkmError2("OKM1121", empty ? "or() branch is empty." : "Pass an array: or([a, b]).", {
|
|
883
|
+
fix: {
|
|
884
|
+
summary: empty ? "Give every or() branch a predicate. An empty branch matches every row." : "Pass an array: or([a, b])."
|
|
885
|
+
}
|
|
886
|
+
});
|
|
887
|
+
}
|
|
838
888
|
var FAIL_FIX = {
|
|
839
889
|
OKM1121: "Use eq for an object value, or has, none, or every for a relation.",
|
|
840
890
|
OKM1101: "Pass limit, or call .all(reason)."
|
|
@@ -866,4 +916,4 @@ function hasMany(include) {
|
|
|
866
916
|
return Object.keys(include).length > 0;
|
|
867
917
|
}
|
|
868
918
|
|
|
869
|
-
export { withRowFilters, installArchiveInspect, archiveRules, readOptionNames, installOperatorSql, readNeedsOperatorSql, emitRelationFrom, bindCall, compileCall, decodeResult, logicalIntent, appliedRules, emitJoin, emitWhere, emitOrder, emitPrimaryOrder, selectedColumns, parseOrder, rejectKeys, readLimit, takeObject, projectExpr, jsonExpr, decodeRow, cell, indexes, quote, list, isRecord, registerFailFix, fail };
|
|
919
|
+
export { withRowFilters, installArchiveInspect, archiveRules, readOptionNames, installOperatorSql, readNeedsOperatorSql, emitRelationFrom, bindCall, compileCall, decodeResult, logicalIntent, appliedRules, emitJoin, emitWhere, emitOrder, emitPrimaryOrder, selectedColumns, parseOrder, rejectKeys, readLimit, takeObject, projectExpr, jsonExpr, decodeRow, cell, indexes, quote, list, isRecord, effectivePredicate, registerFailFix, fail };
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
2
|
serializeCatalog2,
|
|
3
3
|
catalogHash2,
|
|
4
|
+
startupCatalog2,
|
|
4
5
|
parseCatalog2
|
|
5
|
-
} from "./
|
|
6
|
+
} from "./8bwbwy8h.js";
|
|
6
7
|
import {
|
|
7
8
|
roleExists
|
|
8
|
-
} from "./
|
|
9
|
-
import {
|
|
10
|
-
assertTargetPolicy
|
|
11
|
-
} from "./q6zsyh31.js";
|
|
9
|
+
} from "./szm0cksa.js";
|
|
12
10
|
import {
|
|
13
11
|
canonicalTypeName,
|
|
14
12
|
createdRoleName,
|
|
13
|
+
createRoleOnce,
|
|
15
14
|
DEFAULT_BACKFILL_PAUSE_MS,
|
|
16
15
|
DEFAULT_BACKFILL_STATEMENT_TIMEOUT_MS,
|
|
17
16
|
boundaryQuery,
|
|
18
17
|
checkedCast,
|
|
19
18
|
omitManagedObjects,
|
|
20
19
|
planMigration,
|
|
21
|
-
parsePlan
|
|
22
|
-
|
|
20
|
+
parsePlan,
|
|
21
|
+
assertTargetPolicy
|
|
22
|
+
} from "./ga3sfy93.js";
|
|
23
23
|
import {
|
|
24
24
|
quoteIdent,
|
|
25
25
|
qualify
|
|
26
|
-
} from "./
|
|
26
|
+
} from "./8xsbagad.js";
|
|
27
27
|
import {
|
|
28
28
|
alignExtensionVersions
|
|
29
29
|
} from "./8ktwd3rh.js";
|
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
} from "./g7ez901n.js";
|
|
34
34
|
import {
|
|
35
35
|
snakeCase
|
|
36
|
-
} from "./
|
|
36
|
+
} from "./ntneex2a.js";
|
|
37
37
|
import {
|
|
38
38
|
quoteLiteral
|
|
39
39
|
} from "./94ez8kzh.js";
|
|
@@ -98,8 +98,10 @@ async function runUnit(connection, unit, run, session) {
|
|
|
98
98
|
await rebuildInvalidIndex(connection, item.step.sql);
|
|
99
99
|
const created = createdRoleName(item.step.sql);
|
|
100
100
|
const existed = created !== undefined && await roleExists(connection, created);
|
|
101
|
-
if (created === undefined
|
|
101
|
+
if (created === undefined)
|
|
102
102
|
await connection.execute(item.step.sql);
|
|
103
|
+
else if (!existed)
|
|
104
|
+
await connection.execute(createRoleOnce(item.step.sql));
|
|
103
105
|
if (created !== undefined && created === run.migrationRole) {
|
|
104
106
|
if (!existed) {
|
|
105
107
|
await grantSchema(connection, run.schema ?? "public", created);
|
|
@@ -305,7 +307,7 @@ function failStep(unit, index, error) {
|
|
|
305
307
|
if (error instanceof OkmError2)
|
|
306
308
|
return error;
|
|
307
309
|
const message = error instanceof Error ? error.message : "The step failed.";
|
|
308
|
-
return new OkmError2("invalid", `Migration ${unit.migrationId} failed at step ${String(index)}: ${message}`, {
|
|
310
|
+
return new OkmError2("invalid", `Migration ${unit.migrationId} failed at step ${String(index + 1)}: ${message}`, {
|
|
309
311
|
cause: error,
|
|
310
312
|
fix: { summary: "Fix the step and run apply again. Finished steps are not repeated." }
|
|
311
313
|
});
|
|
@@ -377,8 +379,10 @@ function loadMigrations(directory) {
|
|
|
377
379
|
});
|
|
378
380
|
}
|
|
379
381
|
const plan = parsePlan(readFileSync(join(directory, file), "utf8"));
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
+
const stored = parseCatalog2(readFileSync(catalogPath, "utf8"));
|
|
383
|
+
const startup = startupCatalog2(stored);
|
|
384
|
+
const hash = catalogHash2(startup);
|
|
385
|
+
migrations.push(startup === stored ? { id, catalogHash: hash, steps: plan.steps } : { id, catalogHash: hash, legacyHash: catalogHash2(stored), steps: plan.steps });
|
|
382
386
|
}
|
|
383
387
|
return migrations;
|
|
384
388
|
}
|
|
@@ -601,11 +605,12 @@ async function installSnapshot(connection, head, schema, reference, session, mig
|
|
|
601
605
|
await assumeRole(connection, created, session);
|
|
602
606
|
continue;
|
|
603
607
|
}
|
|
608
|
+
const sql = created === undefined ? step.sql : createRoleOnce(step.sql);
|
|
604
609
|
if (step.transactional && !statementOutsideTransaction(step.sql)) {
|
|
605
|
-
batch.push(
|
|
610
|
+
batch.push(sql);
|
|
606
611
|
} else {
|
|
607
612
|
await flush();
|
|
608
|
-
await connection.execute(
|
|
613
|
+
await connection.execute(sql);
|
|
609
614
|
}
|
|
610
615
|
if (created !== undefined && created === migrationRole) {
|
|
611
616
|
await flush();
|