okmodel 0.5.0 → 0.6.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 +36 -18
- package/dist/adapters/pg/no-inflight-timeout.d.ts +17 -0
- package/dist/adapters/pg/postgres-extra.d.ts +22 -0
- package/dist/contracts/catalog/document.d.ts +19 -5
- package/dist/contracts/catalog/object.d.ts +2 -1
- package/dist/contracts/catalog/policy.d.ts +48 -0
- package/dist/contracts/catalog/types.d.ts +25 -1
- package/dist/contracts/catalog/view.d.ts +2 -0
- package/dist/contracts/internal.js +1 -1
- package/dist/contracts/location.d.ts +10 -0
- package/dist/dialects/pg/column.d.ts +13 -2
- package/dist/dialects/pg/ddl.d.ts +37 -1
- package/dist/dialects/pg/emit.d.ts +19 -1
- package/dist/dialects/pg/errors.d.ts +32 -1
- package/dist/dialects/pg/ext/citext.js +1 -1
- package/dist/dialects/pg/ext/index.js +1 -1
- package/dist/dialects/pg/ext/pg-trgm.js +1 -1
- package/dist/dialects/pg/filter-parse.d.ts +10 -3
- package/dist/dialects/pg/fn/index.js +10 -2
- package/dist/dialects/pg/index.d.ts +2 -1
- package/dist/dialects/pg/index.js +29 -17
- package/dist/dialects/pg/introspect.d.ts +7 -0
- package/dist/dialects/pg/model.d.ts +7 -2
- package/dist/dialects/pg/ops/or.d.ts +1 -1
- package/dist/dialects/pg/owner.d.ts +42 -0
- package/dist/dialects/pg/path-endpoint.d.ts +20 -0
- package/dist/dialects/pg/role/check.d.ts +3 -0
- package/dist/dialects/pg/role/index.d.ts +71 -6
- package/dist/dialects/pg/role/sql.d.ts +11 -0
- package/dist/dialects/pg/schema.d.ts +2 -0
- package/dist/dialects/pg/table.d.ts +25 -3
- package/dist/dialects/pg/temporal.d.ts +5 -76
- package/dist/dialects/pg/tenancy.d.ts +45 -3
- package/dist/dialects/pg/view/client.d.ts +19 -1
- package/dist/dialects/pg/view/index.d.ts +4 -0
- package/dist/dialects/pg/view/index.js +21 -178
- package/dist/dialects/pg/view/refresh.d.ts +24 -0
- package/dist/runtime/batch.d.ts +1 -1
- package/dist/runtime/client.d.ts +6 -1
- package/dist/runtime/conflict.d.ts +2 -1
- package/dist/runtime/drift.d.ts +9 -0
- package/dist/runtime/inflight.d.ts +14 -0
- package/dist/runtime/pg/bun.js +6 -5
- package/dist/runtime/pg/pg.js +5 -5
- package/dist/runtime/pg/pglite.js +6 -5
- package/dist/runtime/pg/postgresjs.js +5 -5
- package/dist/runtime/plan.d.ts +22 -1
- package/dist/runtime/predicate.d.ts +19 -0
- package/dist/runtime/safety/index.js +1 -1
- package/dist/runtime/tenancy/composite.d.ts +26 -0
- package/dist/runtime/tenancy/context.d.ts +16 -0
- package/dist/runtime/tenancy/index.d.ts +9 -5
- package/dist/runtime/tenancy/index.js +1508 -37
- package/dist/runtime/tenancy/path.d.ts +87 -0
- package/dist/runtime/tenancy/rls.d.ts +67 -0
- package/dist/runtime/tenancy/via.d.ts +22 -0
- package/dist/runtime/traits/index.js +17 -11
- package/dist/runtime/types.d.ts +13 -1
- package/dist/runtime/validate/index.js +4 -4
- package/dist/runtime/write.d.ts +1 -1
- package/dist/shared/09p049xa.js +702 -0
- package/dist/shared/{ykv7wh5b.js → 1f0xa3cb.js} +5 -5
- package/dist/shared/1nw1f3vs.js +23 -0
- package/dist/shared/{6p5k20sm.js → 1z04fa85.js} +21 -11
- package/dist/shared/{p7pfx624.js → 2cg3wnxc.js} +314 -65
- package/dist/shared/{dyj86cmj.js → 2jd1sn0e.js} +2 -2
- package/dist/shared/{qgvt8nx1.js → 2z7wv5tj.js} +21 -5
- package/dist/shared/{kyxb62mc.js → 31vm914h.js} +9 -1
- package/dist/shared/{sf8m6tfy.js → 32cnm7kn.js} +8 -3
- package/dist/shared/{t4tbsy9h.js → 358zsv4r.js} +1 -1
- package/dist/shared/{jdgqqx31.js → 3fqwmr09.js} +38 -14
- package/dist/shared/{922qbgnw.js → 3qnhc7w5.js} +16 -9
- package/dist/shared/{zvbjc949.js → 4az561fn.js} +1 -1
- package/dist/shared/{k3ted12y.js → 54tqkxja.js} +20 -2
- package/dist/shared/{k7cc9nzk.js → 5d8t84hg.js} +15 -3
- package/dist/shared/{fa9vt245.js → 5j36z8dn.js} +17 -6
- package/dist/shared/{1g0drakv.js → 5yv4k6a6.js} +1 -1
- package/dist/shared/8dqhth8c.js +12 -0
- package/dist/shared/8etn7d8w.js +62 -0
- package/dist/shared/{g516zdsb.js → 8x72s86k.js} +476 -30
- package/dist/shared/{nkc9sfqb.js → 929h19xp.js} +145 -7
- package/dist/shared/{gjthds9j.js → 9628a9xt.js} +1 -1
- package/dist/shared/{8xsbagad.js → 9s0ypp87.js} +88 -7
- package/dist/shared/{b066g80h.js → ag4c7516.js} +2 -2
- package/dist/shared/b3awxn7h.js +45 -0
- package/dist/shared/b4v57v3x.js +9 -0
- package/dist/shared/bdk4sty6.js +58 -0
- package/dist/shared/{s6gqfn5j.js → c58secgj.js} +125 -8
- package/dist/shared/{rrstp3kb.js → cgbamx3e.js} +2 -2
- package/dist/shared/d5azex6s.js +6 -0
- package/dist/shared/{5fqx9yh4.js → dk8v79p3.js} +3 -1
- package/dist/shared/{4m9fn52z.js → ed0b8k4s.js} +1 -1
- package/dist/shared/{z2rnngj4.js → ek2wt2dz.js} +8 -5
- package/dist/shared/{wxn4b7pp.js → f0f1n281.js} +59 -12
- package/dist/shared/{gdh9mrtp.js → g90m9b0g.js} +3 -2
- package/dist/shared/{tbszx8mz.js → gwn6v667.js} +2 -2
- package/dist/shared/{c8aghh05.js → h15jx9bc.js} +8 -2
- package/dist/shared/{3p4b1fg0.js → hazqqecq.js} +17 -93
- package/dist/shared/{4ef7mmqf.js → j3rkk835.js} +34 -11
- package/dist/shared/{bt9sfq0z.js → j60w0dvr.js} +56 -21
- package/dist/shared/{ycatkxmq.js → jgqj11w4.js} +1 -1
- package/dist/shared/k68eedmy.js +85 -0
- package/dist/shared/{szm0cksa.js → knmes2kh.js} +34 -6
- package/dist/shared/{ckjbyrt9.js → mb2ycs1p.js} +7 -9
- package/dist/shared/{tq9afp8d.js → n076ydsc.js} +1 -1
- package/dist/shared/{xhh1srw3.js → nx9hh281.js} +50 -32
- package/dist/shared/{kdkxh3kb.js → p248q79y.js} +58 -59
- package/dist/shared/{990a291j.js → p6bjzq9q.js} +14 -9
- package/dist/shared/{h4nqvjkp.js → pjr6xrm5.js} +2 -2
- package/dist/shared/pz6rj8ay.js +212 -0
- package/dist/shared/{5vdm4sgp.js → q3j1tq7a.js} +36 -23
- package/dist/shared/{8bwbwy8h.js → qq3899ht.js} +128 -18
- package/dist/shared/{dxjp14rk.js → r11n8grd.js} +2 -2
- package/dist/shared/r1fdd0f6.js +19 -0
- package/dist/shared/{8ktwd3rh.js → r3j8s5y0.js} +1 -1
- package/dist/shared/rhs3xe4k.js +7 -0
- package/dist/shared/{jsd07prz.js → rpycarcr.js} +6 -6
- package/dist/shared/{xsq7v6wp.js → sbdme3vy.js} +14 -2
- package/dist/shared/{xdpj5kkm.js → t3g0k88x.js} +775 -264
- package/dist/shared/{ayz64vzj.js → ttmg09vd.js} +708 -19
- package/dist/shared/{r6pj4fq2.js → venqrjna.js} +8 -4
- package/dist/shared/{fancm5pt.js → vjpnzwjz.js} +138 -15
- package/dist/shared/{9tgmr1gh.js → vmfkhszw.js} +44 -9
- package/dist/shared/{wqem4jed.js → wkme6tep.js} +11 -9
- package/dist/shared/{qy9rdq0g.js → wt5rzv1h.js} +15 -11
- package/dist/shared/wxr8cwns.js +6 -0
- package/dist/shared/{bzsjtd7c.js → wynzeggs.js} +19 -14
- package/dist/shared/x3jc53kg.js +220 -0
- package/dist/shared/{7p2wbz7f.js → x72jb7ad.js} +1 -1
- package/dist/shared/{fyqkk1m0.js → xq0mvhts.js} +10 -9
- package/dist/shared/xqve9598.js +53 -0
- package/dist/shared/{47xzb9fc.js → y2t3jktd.js} +2 -2
- package/dist/shared/{9x25fxhc.js → y34a8714.js} +1 -1
- package/dist/shared/{k7dwkhvq.js → y6jdds7g.js} +4 -1
- package/dist/shared/{h8ne16fq.js → ys0xzyaj.js} +1 -1
- package/dist/shared/{c9z5g0fm.js → ywr8hmey.js} +5 -2
- package/dist/shared/{g7ez901n.js → ywspezzx.js} +1 -1
- package/dist/shared/{9m80mkac.js → ze4hxsmy.js} +1 -1
- package/dist/shared/{fb00y8fp.js → zm0mafk4.js} +12 -6
- package/dist/shared/{y005b761.js → zy8r7qvz.js} +6 -2
- package/dist/tooling/cli.js +97 -24
- package/dist/tooling/migrate/apply.d.ts +14 -0
- package/dist/tooling/migrate/check.d.ts +3 -4
- package/dist/tooling/migrate/classify.d.ts +4 -0
- package/dist/tooling/migrate/commands.d.ts +1 -2
- package/dist/tooling/migrate/config.d.ts +3 -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 +11 -1
- package/dist/tooling/migrate/index.js +17 -16
- package/dist/tooling/migrate/lint.d.ts +4 -4
- package/dist/tooling/migrate/lost.d.ts +28 -0
- package/dist/tooling/migrate/paths.d.ts +14 -0
- package/dist/tooling/migrate/plan.d.ts +10 -3
- package/dist/tooling/migrate/policy.d.ts +11 -0
- package/dist/tooling/migrate/runner.d.ts +25 -0
- package/dist/tooling/migrate/sandbox.d.ts +57 -0
- package/dist/tooling/migrate/sql-lex.d.ts +39 -0
- package/dist/tooling/migrate/text.d.ts +42 -0
- package/dist/tooling/node-version.d.ts +21 -0
- package/dist/tooling/testing/factories.d.ts +10 -0
- package/dist/tooling/testing/facts.d.ts +33 -5
- package/dist/tooling/testing/index.d.ts +1 -1
- package/dist/tooling/testing/index.js +3 -3
- package/dist/tooling/testing/isolation.d.ts +15 -8
- package/package.json +10 -6
- package/dist/shared/0v1g424p.js +0 -26
- package/dist/shared/r73pt594.js +0 -426
- package/dist/shared/rasvvzgz.js +0 -21
|
@@ -2,29 +2,35 @@ import {
|
|
|
2
2
|
creationOrder2,
|
|
3
3
|
renameColumn2,
|
|
4
4
|
renameTable2
|
|
5
|
-
} from "./
|
|
5
|
+
} from "./qq3899ht.js";
|
|
6
|
+
import {
|
|
7
|
+
isConnectionLost
|
|
8
|
+
} from "./gwn6v667.js";
|
|
6
9
|
import {
|
|
7
10
|
quoteIdent,
|
|
11
|
+
uniqueBeforeForeignKey,
|
|
8
12
|
ownedByView,
|
|
9
13
|
dropObjectSql,
|
|
10
14
|
createObjectSql,
|
|
11
15
|
functionSql,
|
|
12
16
|
viewSql,
|
|
17
|
+
policyCreatePlan,
|
|
18
|
+
policyDropPlan,
|
|
13
19
|
refreshMaterializedViewSql,
|
|
14
20
|
alterColumnSql,
|
|
15
21
|
identitySequence,
|
|
16
22
|
createTableSql,
|
|
17
23
|
qualify
|
|
18
|
-
} from "./
|
|
24
|
+
} from "./9s0ypp87.js";
|
|
19
25
|
import {
|
|
20
26
|
isExactVersion,
|
|
21
27
|
compareExtensionVersions,
|
|
22
28
|
sameExtensionDefinition
|
|
23
|
-
} from "./
|
|
29
|
+
} from "./r3j8s5y0.js";
|
|
24
30
|
import {
|
|
25
31
|
isDomain,
|
|
26
32
|
sameEnumLabels
|
|
27
|
-
} from "./
|
|
33
|
+
} from "./ywspezzx.js";
|
|
28
34
|
import {
|
|
29
35
|
quoteLiteral
|
|
30
36
|
} from "./94ez8kzh.js";
|
|
@@ -33,7 +39,7 @@ import {
|
|
|
33
39
|
identityKey2,
|
|
34
40
|
identityLabel2,
|
|
35
41
|
compareText
|
|
36
|
-
} from "./
|
|
42
|
+
} from "./358zsv4r.js";
|
|
37
43
|
import {
|
|
38
44
|
fitIdentifier2
|
|
39
45
|
} from "./nfzc5z0w.js";
|
|
@@ -41,6 +47,332 @@ import {
|
|
|
41
47
|
OkmError2
|
|
42
48
|
} from "./52szzakm.js";
|
|
43
49
|
|
|
50
|
+
// src/tooling/migrate/sql-lex.ts
|
|
51
|
+
function sqlStatements(sql) {
|
|
52
|
+
return scan(sql).statements;
|
|
53
|
+
}
|
|
54
|
+
function sqlQuoteOpen(sql) {
|
|
55
|
+
return scan(sql).open;
|
|
56
|
+
}
|
|
57
|
+
function scan(sql) {
|
|
58
|
+
const statements = [];
|
|
59
|
+
const parts = [];
|
|
60
|
+
let index = 0;
|
|
61
|
+
let open = false;
|
|
62
|
+
let atomic = 0;
|
|
63
|
+
const flush = () => {
|
|
64
|
+
const text = parts.join("").trim();
|
|
65
|
+
parts.length = 0;
|
|
66
|
+
if (text.length > 0)
|
|
67
|
+
statements.push(text);
|
|
68
|
+
};
|
|
69
|
+
while (index < sql.length) {
|
|
70
|
+
if (sql.startsWith("--", index)) {
|
|
71
|
+
parts.push(" ");
|
|
72
|
+
index = lineCommentEnd(sql, index + 2);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (sql.startsWith("/*", index)) {
|
|
76
|
+
parts.push(" ");
|
|
77
|
+
const block = blockComment(sql, index + 2);
|
|
78
|
+
index = block.index;
|
|
79
|
+
if (block.open) {
|
|
80
|
+
open = true;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const dollar = dollarAt(sql, index);
|
|
86
|
+
if (dollar !== undefined) {
|
|
87
|
+
const closed = takeDollar(sql, dollar);
|
|
88
|
+
parts.push(sql.slice(index, closed.index));
|
|
89
|
+
index = closed.index;
|
|
90
|
+
if (closed.open) {
|
|
91
|
+
open = true;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (isEscapeString(sql, index)) {
|
|
97
|
+
const quote = takeQuoted(sql, index + 2, true);
|
|
98
|
+
parts.push(sql.slice(index, quote.index));
|
|
99
|
+
index = quote.index;
|
|
100
|
+
if (quote.open) {
|
|
101
|
+
open = true;
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const char = sql[index];
|
|
107
|
+
if (char === "'") {
|
|
108
|
+
const quote = takeQuoted(sql, index + 1, false);
|
|
109
|
+
parts.push(sql.slice(index, quote.index));
|
|
110
|
+
index = quote.index;
|
|
111
|
+
if (quote.open) {
|
|
112
|
+
open = true;
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (char === '"') {
|
|
118
|
+
const quote = takeIdent(sql, index + 1);
|
|
119
|
+
parts.push(sql.slice(index, quote.index));
|
|
120
|
+
index = quote.index;
|
|
121
|
+
if (quote.open) {
|
|
122
|
+
open = true;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (char === ";") {
|
|
128
|
+
if (atomic === 0)
|
|
129
|
+
flush();
|
|
130
|
+
else
|
|
131
|
+
parts.push(";");
|
|
132
|
+
index += 1;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (isIdentStart(sql.charCodeAt(index))) {
|
|
136
|
+
const word = readWord(sql, index);
|
|
137
|
+
const lower = word.toLowerCase();
|
|
138
|
+
if (atomic > 0 && lower === "case")
|
|
139
|
+
atomic += 1;
|
|
140
|
+
else if (atomic > 0 && lower === "end")
|
|
141
|
+
atomic -= 1;
|
|
142
|
+
else if (lower === "begin" && nextWordIs(sql, index + word.length, "atomic")) {
|
|
143
|
+
if (atomic > 0 || isRoutineHead(parts.join("")))
|
|
144
|
+
atomic += 1;
|
|
145
|
+
}
|
|
146
|
+
parts.push(word);
|
|
147
|
+
index += word.length;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
parts.push(char ?? "");
|
|
151
|
+
index += 1;
|
|
152
|
+
}
|
|
153
|
+
flush();
|
|
154
|
+
return { statements, open: open || atomic > 0 };
|
|
155
|
+
}
|
|
156
|
+
function transactionControl(sql) {
|
|
157
|
+
for (const statement of sqlStatements(sql)) {
|
|
158
|
+
const top = controlWord(statement, false);
|
|
159
|
+
if (top !== undefined)
|
|
160
|
+
return top;
|
|
161
|
+
if (!isDo(statement))
|
|
162
|
+
continue;
|
|
163
|
+
for (const body of dollarBodies(statement)) {
|
|
164
|
+
for (const inner of sqlStatements(body)) {
|
|
165
|
+
const hit = controlWord(inner, true);
|
|
166
|
+
if (hit !== undefined)
|
|
167
|
+
return hit;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
function isDo(statement) {
|
|
174
|
+
return /^do(?:\s|$)/i.test(statement.trim());
|
|
175
|
+
}
|
|
176
|
+
function controlWord(statement, insideBody) {
|
|
177
|
+
let text = statement.trim().replace(/\s+/g, " ").toLowerCase();
|
|
178
|
+
if (insideBody && /^begin(?:\s|$)/.test(text) && !/^begin\s+(?:work|transaction)\b/.test(text)) {
|
|
179
|
+
text = text.replace(/^begin\s+/, "").trim();
|
|
180
|
+
if (text.length === 0)
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (/^commit(?:\s+(?:work|transaction|prepared\b.*))?$/.test(text))
|
|
184
|
+
return "commit";
|
|
185
|
+
if (text === "abort")
|
|
186
|
+
return "abort";
|
|
187
|
+
if (/^rollback(?:\s+(?:work|transaction|prepared\b.*))?$/.test(text))
|
|
188
|
+
return "rollback";
|
|
189
|
+
if (/^prepare\s+transaction\b/.test(text))
|
|
190
|
+
return "prepare transaction";
|
|
191
|
+
if (/^start\s+transaction\b/.test(text))
|
|
192
|
+
return "start transaction";
|
|
193
|
+
if (/^begin(?:\s+(?:work|transaction)\b.*)?$/.test(text) || /^begin\s+\S/.test(text)) {
|
|
194
|
+
if (insideBody && !/^begin\s+(?:work|transaction)\b/.test(text))
|
|
195
|
+
return;
|
|
196
|
+
return "begin";
|
|
197
|
+
}
|
|
198
|
+
if (/^end(?:\s+(?:work|transaction))?$/.test(text)) {
|
|
199
|
+
if (insideBody && text === "end")
|
|
200
|
+
return;
|
|
201
|
+
return "end";
|
|
202
|
+
}
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
function dollarBodies(sql) {
|
|
206
|
+
const bodies = [];
|
|
207
|
+
let index = 0;
|
|
208
|
+
while (index < sql.length) {
|
|
209
|
+
if (sql.startsWith("--", index)) {
|
|
210
|
+
index = lineCommentEnd(sql, index + 2);
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (sql.startsWith("/*", index)) {
|
|
214
|
+
index = blockComment(sql, index + 2).index;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (isEscapeString(sql, index)) {
|
|
218
|
+
index = takeQuoted(sql, index + 2, true).index;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (sql[index] === "'") {
|
|
222
|
+
index = takeQuoted(sql, index + 1, false).index;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (sql[index] === '"') {
|
|
226
|
+
index = takeIdent(sql, index + 1).index;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
const dollar = dollarAt(sql, index);
|
|
230
|
+
if (dollar !== undefined) {
|
|
231
|
+
const closed = takeDollar(sql, dollar);
|
|
232
|
+
if (!closed.open) {
|
|
233
|
+
const closer = `$${dollar.tag}$`.length;
|
|
234
|
+
bodies.push(sql.slice(dollar.end, closed.index - closer));
|
|
235
|
+
}
|
|
236
|
+
index = closed.index;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
index += 1;
|
|
240
|
+
}
|
|
241
|
+
return bodies;
|
|
242
|
+
}
|
|
243
|
+
function readWord(sql, index) {
|
|
244
|
+
let cursor = index + 1;
|
|
245
|
+
while (isIdentCont(sql.charCodeAt(cursor)))
|
|
246
|
+
cursor += 1;
|
|
247
|
+
return sql.slice(index, cursor);
|
|
248
|
+
}
|
|
249
|
+
function nextWordIs(sql, index, expected) {
|
|
250
|
+
const at = skipTrivia(sql, index);
|
|
251
|
+
if (!isIdentStart(sql.charCodeAt(at)))
|
|
252
|
+
return false;
|
|
253
|
+
return readWord(sql, at).toLowerCase() === expected;
|
|
254
|
+
}
|
|
255
|
+
function skipTrivia(sql, index) {
|
|
256
|
+
while (index < sql.length) {
|
|
257
|
+
const code = sql.charCodeAt(index);
|
|
258
|
+
if (code === 32 || code === 9 || code === 10 || code === 13) {
|
|
259
|
+
index += 1;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (sql.startsWith("--", index)) {
|
|
263
|
+
index = lineCommentEnd(sql, index + 2);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (sql.startsWith("/*", index)) {
|
|
267
|
+
const block = blockComment(sql, index + 2);
|
|
268
|
+
if (block.open)
|
|
269
|
+
return sql.length;
|
|
270
|
+
index = block.index;
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
return index;
|
|
274
|
+
}
|
|
275
|
+
return index;
|
|
276
|
+
}
|
|
277
|
+
function isRoutineHead(text) {
|
|
278
|
+
return /^create(?:\s+or\s+replace)?\s+(?:function|procedure)\b/i.test(text.replace(/\s+/g, " ").trim());
|
|
279
|
+
}
|
|
280
|
+
function lineCommentEnd(sql, index) {
|
|
281
|
+
while (index < sql.length && sql.charCodeAt(index) !== 10)
|
|
282
|
+
index += 1;
|
|
283
|
+
return index;
|
|
284
|
+
}
|
|
285
|
+
function blockComment(sql, index) {
|
|
286
|
+
let depth = 1;
|
|
287
|
+
while (index < sql.length) {
|
|
288
|
+
if (sql.startsWith("/*", index)) {
|
|
289
|
+
depth += 1;
|
|
290
|
+
index += 2;
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (sql.startsWith("*/", index)) {
|
|
294
|
+
depth -= 1;
|
|
295
|
+
index += 2;
|
|
296
|
+
if (depth === 0)
|
|
297
|
+
return { index, open: false };
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
index += 1;
|
|
301
|
+
}
|
|
302
|
+
return { index, open: true };
|
|
303
|
+
}
|
|
304
|
+
function dollarAt(sql, index) {
|
|
305
|
+
if (sql.charCodeAt(index) !== 36)
|
|
306
|
+
return;
|
|
307
|
+
const next = index + 1;
|
|
308
|
+
if (sql.charCodeAt(next) === 36)
|
|
309
|
+
return { tag: "", end: next + 1 };
|
|
310
|
+
if (!isIdentStart(sql.charCodeAt(next)))
|
|
311
|
+
return;
|
|
312
|
+
let cursor = next + 1;
|
|
313
|
+
while (isIdentCont(sql.charCodeAt(cursor)))
|
|
314
|
+
cursor += 1;
|
|
315
|
+
if (sql.charCodeAt(cursor) !== 36)
|
|
316
|
+
return;
|
|
317
|
+
return { tag: sql.slice(next, cursor), end: cursor + 1 };
|
|
318
|
+
}
|
|
319
|
+
function takeDollar(sql, dollar) {
|
|
320
|
+
const closer = `$${dollar.tag}$`;
|
|
321
|
+
const found = sql.indexOf(closer, dollar.end);
|
|
322
|
+
if (found === -1)
|
|
323
|
+
return { index: sql.length, open: true };
|
|
324
|
+
return { index: found + closer.length, open: false };
|
|
325
|
+
}
|
|
326
|
+
function takeQuoted(sql, index, escape) {
|
|
327
|
+
while (index < sql.length) {
|
|
328
|
+
const char = sql[index];
|
|
329
|
+
if (escape && char === "\\") {
|
|
330
|
+
if (index + 1 >= sql.length)
|
|
331
|
+
return { index: sql.length, open: true };
|
|
332
|
+
index += 2;
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
if (char === "'") {
|
|
336
|
+
if (sql[index + 1] === "'") {
|
|
337
|
+
index += 2;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
return { index: index + 1, open: false };
|
|
341
|
+
}
|
|
342
|
+
index += 1;
|
|
343
|
+
}
|
|
344
|
+
return { index, open: true };
|
|
345
|
+
}
|
|
346
|
+
function takeIdent(sql, index) {
|
|
347
|
+
while (index < sql.length) {
|
|
348
|
+
if (sql[index] === '"') {
|
|
349
|
+
if (sql[index + 1] === '"') {
|
|
350
|
+
index += 2;
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
return { index: index + 1, open: false };
|
|
354
|
+
}
|
|
355
|
+
index += 1;
|
|
356
|
+
}
|
|
357
|
+
return { index, open: true };
|
|
358
|
+
}
|
|
359
|
+
function isEscapeString(sql, index) {
|
|
360
|
+
const code = sql.charCodeAt(index);
|
|
361
|
+
if (code !== 69 && code !== 101)
|
|
362
|
+
return false;
|
|
363
|
+
if (sql.charCodeAt(index + 1) !== 39)
|
|
364
|
+
return false;
|
|
365
|
+
if (index > 0 && isIdentCont(sql.charCodeAt(index - 1)))
|
|
366
|
+
return false;
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
function isIdentStart(code) {
|
|
370
|
+
return code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 95;
|
|
371
|
+
}
|
|
372
|
+
function isIdentCont(code) {
|
|
373
|
+
return isIdentStart(code) || code >= 48 && code <= 57;
|
|
374
|
+
}
|
|
375
|
+
|
|
44
376
|
// src/tooling/migrate/type-name.ts
|
|
45
377
|
function canonicalTypeName(typeName) {
|
|
46
378
|
const suffix = arraySuffix(typeName);
|
|
@@ -108,6 +440,10 @@ function canonicalTime(name, precision) {
|
|
|
108
440
|
|
|
109
441
|
// src/dialects/pg/role/sql.ts
|
|
110
442
|
var PRIVILEGE = /^(select|insert|update|delete|execute|usage)$/;
|
|
443
|
+
var COLUMN_PRIVILEGE = /^(select|update)\(([^)]*)\)$/i;
|
|
444
|
+
function columnPrivilege(name, columns) {
|
|
445
|
+
return `${name.toUpperCase()}(${[...columns].sort().join(",")})`;
|
|
446
|
+
}
|
|
111
447
|
function privilegeSql(before, after, schema, recreated = new Set) {
|
|
112
448
|
const beforeBy = index(before);
|
|
113
449
|
const afterBy = index(after);
|
|
@@ -145,7 +481,13 @@ function privilegeSql(before, after, schema, recreated = new Set) {
|
|
|
145
481
|
const target = grantTargetKey(object.identity.object);
|
|
146
482
|
if (previous !== undefined && (target === undefined || !recreated.has(target)))
|
|
147
483
|
continue;
|
|
484
|
+
const tighten = tightenColumnSelect(object, after, schema);
|
|
485
|
+
if (tighten !== undefined)
|
|
486
|
+
grant.push(tighten);
|
|
148
487
|
grant.push({ sql: grantStatement(object, schema, false), kind: "grant" });
|
|
488
|
+
const writes = revokeMatviewWrites(object, after, schema);
|
|
489
|
+
if (writes !== undefined)
|
|
490
|
+
grant.push(writes);
|
|
149
491
|
continue;
|
|
150
492
|
}
|
|
151
493
|
if (previous !== undefined)
|
|
@@ -191,12 +533,48 @@ function login(object) {
|
|
|
191
533
|
function inherit(object) {
|
|
192
534
|
return object.definition.inherit ? "inherit" : "noinherit";
|
|
193
535
|
}
|
|
536
|
+
function tightenColumnSelect(object, after, schema) {
|
|
537
|
+
if (!/^select\([^)]*\)$/.test(object.identity.privilege.toLowerCase()))
|
|
538
|
+
return;
|
|
539
|
+
const target = object.identity.object;
|
|
540
|
+
const covered = after.some((item) => item.kind === "grant" && item.identity.role === object.identity.role && item.identity.privilege.toUpperCase() === "SELECT" && item.identity.object.kind === target.kind && item.identity.object.name === target.name);
|
|
541
|
+
if (covered)
|
|
542
|
+
return;
|
|
543
|
+
return {
|
|
544
|
+
sql: `revoke select on ${grantTarget(target, schema)} from ${quoteIdent(object.identity.role)}`,
|
|
545
|
+
kind: "revoke"
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
function revokeMatviewWrites(object, after, schema) {
|
|
549
|
+
if (object.identity.privilege.toUpperCase() !== "SELECT")
|
|
550
|
+
return;
|
|
551
|
+
const target = object.identity.object;
|
|
552
|
+
if (target.kind !== "materializedView")
|
|
553
|
+
return;
|
|
554
|
+
const inheritsWrites = after.some((item) => item.kind === "defaultPrivilege" && item.identity.grantee === object.identity.role && item.identity.objectKind === "table" && (item.identity.privilege === "INSERT" || item.identity.privilege === "UPDATE" || item.identity.privilege === "DELETE"));
|
|
555
|
+
if (!inheritsWrites)
|
|
556
|
+
return;
|
|
557
|
+
return {
|
|
558
|
+
sql: `revoke insert, update, delete on ${grantTarget(target, schema)} from ${quoteIdent(object.identity.role)}`,
|
|
559
|
+
kind: "revoke"
|
|
560
|
+
};
|
|
561
|
+
}
|
|
194
562
|
function grantStatement(object, schema, revoke) {
|
|
195
|
-
const privilege = object.identity.privilege
|
|
196
|
-
|
|
563
|
+
const privilege = object.identity.privilege;
|
|
564
|
+
const column = COLUMN_PRIVILEGE.exec(privilege);
|
|
197
565
|
const verb = revoke ? "revoke" : "grant";
|
|
198
566
|
const direction = revoke ? "from" : "to";
|
|
199
|
-
|
|
567
|
+
const target = grantTarget(object.identity.object, schema);
|
|
568
|
+
const role = quoteIdent(object.identity.role);
|
|
569
|
+
const listed = column?.[2];
|
|
570
|
+
const base = column?.[1];
|
|
571
|
+
if (listed !== undefined && base !== undefined) {
|
|
572
|
+
const names = listed.split(",").filter((name) => name.length > 0).map((name) => quoteIdent(name)).join(", ");
|
|
573
|
+
return `${verb} ${base.toLowerCase()} (${names}) on ${target} ${direction} ${role}`;
|
|
574
|
+
}
|
|
575
|
+
const keyword = privilege.toLowerCase();
|
|
576
|
+
assertPrivilege(keyword);
|
|
577
|
+
return `${verb} ${keyword} on ${target} ${direction} ${role}`;
|
|
200
578
|
}
|
|
201
579
|
function defaultStatement(object, schema, revoke) {
|
|
202
580
|
const privilege = object.identity.privilege.toLowerCase();
|
|
@@ -229,7 +607,7 @@ function grantTarget(object, schema) {
|
|
|
229
607
|
if (object.kind === "function")
|
|
230
608
|
return functionTarget(object.name, schema);
|
|
231
609
|
if (object.kind === "namespace")
|
|
232
|
-
return `schema ${quoteIdent(
|
|
610
|
+
return `schema ${quoteIdent(schema)}`;
|
|
233
611
|
return `table ${qualify(schema, object.name)}`;
|
|
234
612
|
}
|
|
235
613
|
function functionTarget(name, schema) {
|
|
@@ -481,6 +859,10 @@ var STEP_CLASS = {
|
|
|
481
859
|
"rename-sequence": "contract",
|
|
482
860
|
"backfill-expand": "expand",
|
|
483
861
|
"backfill-contract": "contract",
|
|
862
|
+
"enable-rls": "expand",
|
|
863
|
+
"create-policy": "expand",
|
|
864
|
+
"drop-policy": "contract",
|
|
865
|
+
"disable-rls": "contract",
|
|
484
866
|
"raw-sql": "unclassified"
|
|
485
867
|
};
|
|
486
868
|
var STEP_KINDS = Object.keys(STEP_CLASS);
|
|
@@ -690,6 +1072,43 @@ function managedSequence(name) {
|
|
|
690
1072
|
return name === "okm_meta" || name === "okm_history" || name === "okm_backfill" || name.startsWith("okm_meta_") || name.startsWith("okm_history_") || name.startsWith("okm_backfill_");
|
|
691
1073
|
}
|
|
692
1074
|
|
|
1075
|
+
// src/tooling/migrate/text.ts
|
|
1076
|
+
var FIX = {
|
|
1077
|
+
"no-steps": "Add -- no steps when the migration is intentionally empty. Otherwise the file needs a statement okm can read.",
|
|
1078
|
+
utf16: "Save the file as UTF-8. okm does not read UTF-16."
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
class MigrationTextError extends OkmError2 {
|
|
1082
|
+
reason;
|
|
1083
|
+
constructor(reason, message) {
|
|
1084
|
+
super("invalid", message, {
|
|
1085
|
+
kind: "invalid",
|
|
1086
|
+
fieldReason: reason,
|
|
1087
|
+
fix: { summary: FIX[reason] }
|
|
1088
|
+
});
|
|
1089
|
+
this.name = "OkmError";
|
|
1090
|
+
this.reason = reason;
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
function prepareMigrationText(text, label = "Migration SQL") {
|
|
1094
|
+
if (text.includes("\x00")) {
|
|
1095
|
+
throw new MigrationTextError("utf16", `${label} is UTF-16. Save the file as UTF-8.`);
|
|
1096
|
+
}
|
|
1097
|
+
const withoutBom = text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
1098
|
+
return withoutBom.replaceAll(`\r
|
|
1099
|
+
`, `
|
|
1100
|
+
`).replaceAll("\r", `
|
|
1101
|
+
`);
|
|
1102
|
+
}
|
|
1103
|
+
function assertMigrationSteps(text, stepCount, label) {
|
|
1104
|
+
if (stepCount > 0 || text.trim().length === 0)
|
|
1105
|
+
return;
|
|
1106
|
+
if (text.split(`
|
|
1107
|
+
`).includes("-- no steps"))
|
|
1108
|
+
return;
|
|
1109
|
+
throw new MigrationTextError("no-steps", `${label} has text but no steps. okm did not apply it.`);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
693
1112
|
// src/tooling/migrate/safe.ts
|
|
694
1113
|
var ACCESS = "ACCESS EXCLUSIVE";
|
|
695
1114
|
var SHARE_UPDATE = "SHARE UPDATE EXCLUSIVE";
|
|
@@ -1047,17 +1466,24 @@ function planMigration(input) {
|
|
|
1047
1466
|
if (!afterBy.has(item.key)) {
|
|
1048
1467
|
if (item.object.kind === "role")
|
|
1049
1468
|
continue;
|
|
1469
|
+
if (handsOff(item.object) || childOfHandsOff(item.object, afterBy))
|
|
1470
|
+
continue;
|
|
1050
1471
|
dropKeys.add(item.key);
|
|
1051
1472
|
}
|
|
1052
1473
|
}
|
|
1053
1474
|
for (const item of afterList) {
|
|
1054
|
-
if (!beforeBy.has(item.key))
|
|
1475
|
+
if (!beforeBy.has(item.key)) {
|
|
1476
|
+
if (handsOff(item.object) || childOfHandsOff(item.object, afterBy))
|
|
1477
|
+
continue;
|
|
1055
1478
|
createKeys.add(item.key);
|
|
1479
|
+
}
|
|
1056
1480
|
}
|
|
1057
1481
|
for (const item of afterList) {
|
|
1058
1482
|
const previous = beforeBy.get(item.key);
|
|
1059
1483
|
if (previous === undefined || sameDefinition(previous, item.object))
|
|
1060
1484
|
continue;
|
|
1485
|
+
if (handsOff(item.object) || childOfHandsOff(item.object, afterBy))
|
|
1486
|
+
continue;
|
|
1061
1487
|
if (previous.kind === "extension" && item.object.kind === "extension")
|
|
1062
1488
|
continue;
|
|
1063
1489
|
if (previous.kind === "role" && item.object.kind === "role")
|
|
@@ -1093,6 +1519,8 @@ function planMigration(input) {
|
|
|
1093
1519
|
refuseRoutineDrops(beforeBy, afterBy, dropKeys);
|
|
1094
1520
|
omitOwnedSequences(createKeys, afterBy, request.after);
|
|
1095
1521
|
omitOwnedSequences(dropKeys, beforeBy, renamed);
|
|
1522
|
+
releaseHandsOff(dropKeys, beforeBy, afterBy);
|
|
1523
|
+
releaseHandsOff(createKeys, afterBy, afterBy);
|
|
1096
1524
|
const recreated = new Set;
|
|
1097
1525
|
for (const key of createKeys) {
|
|
1098
1526
|
if (dropKeys.has(key))
|
|
@@ -1111,7 +1539,7 @@ function planMigration(input) {
|
|
|
1111
1539
|
const deferred = new Set(picklists.map((change) => identityKey2(change.before.identity)));
|
|
1112
1540
|
const typeDrops = [];
|
|
1113
1541
|
const extensionDrops = [];
|
|
1114
|
-
const reverse = [...creationOrder2(renamed)].reverse();
|
|
1542
|
+
const reverse = [...uniqueBeforeForeignKey(creationOrder2(renamed))].reverse();
|
|
1115
1543
|
const emitDrop = (object) => {
|
|
1116
1544
|
const key = identityKey2(object.identity);
|
|
1117
1545
|
if (!dropKeys.has(key) || deferred.has(key) || covered(object, droppedTables))
|
|
@@ -1145,7 +1573,17 @@ function planMigration(input) {
|
|
|
1145
1573
|
emitDrop(object);
|
|
1146
1574
|
}
|
|
1147
1575
|
for (const object of reverse) {
|
|
1148
|
-
if (object.kind
|
|
1576
|
+
if (object.kind !== "policy")
|
|
1577
|
+
continue;
|
|
1578
|
+
const key = identityKey2(object.identity);
|
|
1579
|
+
if (!dropKeys.has(key) || deferred.has(key))
|
|
1580
|
+
continue;
|
|
1581
|
+
for (const item of policyDropPlan(object, schema)) {
|
|
1582
|
+
steps.push(step(item.sql, item.kind, "ddl", ACCESS2, true, [object.identity.parent.name]));
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
for (const object of reverse) {
|
|
1586
|
+
if (object.kind === "trigger" || object.kind === "function" || object.kind === "view" || object.kind === "materializedView" || object.kind === "policy" || ownedByView(object)) {
|
|
1149
1587
|
continue;
|
|
1150
1588
|
}
|
|
1151
1589
|
const key = identityKey2(object.identity);
|
|
@@ -1217,8 +1655,8 @@ function planMigration(input) {
|
|
|
1217
1655
|
continue;
|
|
1218
1656
|
steps.push(step(sql, kind, "ddl", ACCESS2));
|
|
1219
1657
|
}
|
|
1220
|
-
for (const object of creationOrder2(request.after)) {
|
|
1221
|
-
if (object.kind === "type" || object.kind === "extension" || object.kind === "function" || object.kind === "trigger" || object.kind === "view" || object.kind === "materializedView" || ownedByView(object)) {
|
|
1658
|
+
for (const object of uniqueBeforeForeignKey(creationOrder2(request.after))) {
|
|
1659
|
+
if (object.kind === "type" || object.kind === "extension" || object.kind === "function" || object.kind === "trigger" || object.kind === "view" || object.kind === "materializedView" || object.kind === "policy" || ownedByView(object)) {
|
|
1222
1660
|
continue;
|
|
1223
1661
|
}
|
|
1224
1662
|
const key = identityKey2(object.identity);
|
|
@@ -1249,6 +1687,17 @@ function planMigration(input) {
|
|
|
1249
1687
|
}
|
|
1250
1688
|
steps.push(step(sql, kind, "ddl", object.kind === "index" ? SHARE : ACCESS2));
|
|
1251
1689
|
}
|
|
1690
|
+
for (const object of creationOrder2(request.after)) {
|
|
1691
|
+
if (object.kind !== "policy")
|
|
1692
|
+
continue;
|
|
1693
|
+
const key = identityKey2(object.identity);
|
|
1694
|
+
if (!createKeys.has(key) || emitted.has(key))
|
|
1695
|
+
continue;
|
|
1696
|
+
emitted.add(key);
|
|
1697
|
+
for (const item of policyCreatePlan(object, schema)) {
|
|
1698
|
+
steps.push(step(item.sql, item.kind, "ddl", ACCESS2, true, [object.identity.parent.name]));
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1252
1701
|
for (const change of replaces) {
|
|
1253
1702
|
if (sameDefinition(change.before, change.after))
|
|
1254
1703
|
continue;
|
|
@@ -1345,7 +1794,7 @@ function formatPlan(plan, lockText) {
|
|
|
1345
1794
|
`);
|
|
1346
1795
|
}
|
|
1347
1796
|
function parsePlan(text) {
|
|
1348
|
-
const lines = text.split(`
|
|
1797
|
+
const lines = prepareMigrationText(text).split(`
|
|
1349
1798
|
`);
|
|
1350
1799
|
let name = "migration";
|
|
1351
1800
|
let planClass = "expand";
|
|
@@ -1377,9 +1826,16 @@ function parsePlan(text) {
|
|
|
1377
1826
|
let behavior;
|
|
1378
1827
|
const allows = [];
|
|
1379
1828
|
const sql = [];
|
|
1380
|
-
|
|
1829
|
+
let quoteOpen = false;
|
|
1830
|
+
while (index < lines.length && (quoteOpen || (lines[index] ?? "") !== "")) {
|
|
1381
1831
|
const line = lines[index] ?? "";
|
|
1382
1832
|
index += 1;
|
|
1833
|
+
if (quoteOpen) {
|
|
1834
|
+
sql.push(line);
|
|
1835
|
+
quoteOpen = sqlQuoteOpen(sql.join(`
|
|
1836
|
+
`));
|
|
1837
|
+
continue;
|
|
1838
|
+
}
|
|
1383
1839
|
if (line.startsWith("-- class: ")) {
|
|
1384
1840
|
stepClass = readClass(line.slice("-- class: ".length));
|
|
1385
1841
|
sawClass = true;
|
|
@@ -1401,8 +1857,11 @@ function parsePlan(text) {
|
|
|
1401
1857
|
behavior = "change";
|
|
1402
1858
|
else if (line.startsWith("-- okm-allow"))
|
|
1403
1859
|
allows.push(readAllow(line));
|
|
1404
|
-
else if (!line.startsWith("--"))
|
|
1860
|
+
else if (!line.startsWith("--")) {
|
|
1405
1861
|
sql.push(line);
|
|
1862
|
+
quoteOpen = sqlQuoteOpen(sql.join(`
|
|
1863
|
+
`));
|
|
1864
|
+
}
|
|
1406
1865
|
}
|
|
1407
1866
|
const statement = sql.join(`
|
|
1408
1867
|
`).replace(/;\s*$/, "");
|
|
@@ -1559,7 +2018,7 @@ function withoutNameKey(value) {
|
|
|
1559
2018
|
}
|
|
1560
2019
|
function renameShapeStep(pair, schema) {
|
|
1561
2020
|
if (pair.from.kind === "index" && pair.to.kind === "index") {
|
|
1562
|
-
return step(`alter index ${qualify(schema, pair.from.identity.name)} rename to ${quoteIdent(pair.to.identity.name)}`, "rename-index", "ddl",
|
|
2021
|
+
return step(`alter index ${qualify(schema, pair.from.identity.name)} rename to ${quoteIdent(pair.to.identity.name)}`, "rename-index", "ddl", SHARE_UPDATE2);
|
|
1563
2022
|
}
|
|
1564
2023
|
if (pair.from.kind !== "constraint" || pair.to.kind !== "constraint") {
|
|
1565
2024
|
throw new OkmError2("invalid", "A same-shape rename is an index or a constraint.", {
|
|
@@ -2132,6 +2591,25 @@ function reprintedDefaultBase(typeName) {
|
|
|
2132
2591
|
return `${time[1] ?? ""} ${time[2] ?? ""}`;
|
|
2133
2592
|
return;
|
|
2134
2593
|
}
|
|
2594
|
+
function handsOff(object) {
|
|
2595
|
+
return object.owner === "external" || object.owner === "ignored";
|
|
2596
|
+
}
|
|
2597
|
+
function childOfHandsOff(object, catalog) {
|
|
2598
|
+
if (object.kind !== "column" && object.kind !== "index" && object.kind !== "constraint" && object.kind !== "trigger" && object.kind !== "policy") {
|
|
2599
|
+
return false;
|
|
2600
|
+
}
|
|
2601
|
+
const parent = object.identity.parent;
|
|
2602
|
+
const table = catalog.get(identityKey2({ kind: "table", namespace: parent.namespace, name: parent.name }));
|
|
2603
|
+
return table !== undefined && handsOff(table);
|
|
2604
|
+
}
|
|
2605
|
+
function releaseHandsOff(keys, objects, parents) {
|
|
2606
|
+
for (const key of keys) {
|
|
2607
|
+
const object = objects.get(key);
|
|
2608
|
+
if (object !== undefined && (handsOff(object) || childOfHandsOff(object, parents))) {
|
|
2609
|
+
keys.delete(key);
|
|
2610
|
+
}
|
|
2611
|
+
}
|
|
2612
|
+
}
|
|
2135
2613
|
function sameDefinition(left, right) {
|
|
2136
2614
|
if (left.kind === "materializedView" && right.kind === "materializedView") {
|
|
2137
2615
|
return stable(canonicalColumns(left.definition.columns)) === stable(canonicalColumns(right.definition.columns)) && left.definition.query === right.definition.query;
|
|
@@ -2171,6 +2649,9 @@ function forComparison(object) {
|
|
|
2171
2649
|
if (!isDomain(object.definition))
|
|
2172
2650
|
return object.definition;
|
|
2173
2651
|
return { ...object.definition, base: canonicalTypeName(object.definition.base) };
|
|
2652
|
+
case "constraint":
|
|
2653
|
+
case "index":
|
|
2654
|
+
return withoutNameKey(object.definition);
|
|
2174
2655
|
default:
|
|
2175
2656
|
return object.definition;
|
|
2176
2657
|
}
|
|
@@ -2429,4 +2910,212 @@ function order(value) {
|
|
|
2429
2910
|
return value;
|
|
2430
2911
|
}
|
|
2431
2912
|
|
|
2432
|
-
|
|
2913
|
+
// src/tooling/migrate/lost.ts
|
|
2914
|
+
var UNAVAILABLE_FIX = {
|
|
2915
|
+
summary: "The connection to Postgres closed. Run the command again when the database is reachable."
|
|
2916
|
+
};
|
|
2917
|
+
function sessionFailure(error) {
|
|
2918
|
+
if (error instanceof OkmError2) {
|
|
2919
|
+
if (error.kind === "outcome_unknown" || error.kind === "unavailable")
|
|
2920
|
+
return error;
|
|
2921
|
+
const cause = error.cause;
|
|
2922
|
+
if (cause === undefined || cause === error || error.kind !== "invalid")
|
|
2923
|
+
return;
|
|
2924
|
+
const inner = sessionFailure(cause);
|
|
2925
|
+
if (inner === undefined)
|
|
2926
|
+
return;
|
|
2927
|
+
return new OkmError2(inner.code, error.message, {
|
|
2928
|
+
kind: inner.kind,
|
|
2929
|
+
cause: error,
|
|
2930
|
+
fix: inner.fix
|
|
2931
|
+
});
|
|
2932
|
+
}
|
|
2933
|
+
if (isOutcomeUnknown(error)) {
|
|
2934
|
+
return new OkmError2("OKM1401", "The commit outcome is unknown. The result never arrived.", {
|
|
2935
|
+
kind: "outcome_unknown",
|
|
2936
|
+
cause: error,
|
|
2937
|
+
fix: {
|
|
2938
|
+
summary: "Check whether the write landed before retrying. This error is never retried automatically."
|
|
2939
|
+
}
|
|
2940
|
+
});
|
|
2941
|
+
}
|
|
2942
|
+
if (!isConnectionLost(error))
|
|
2943
|
+
return;
|
|
2944
|
+
const message = error instanceof Error && error.message.length > 0 ? error.message : "The database is unavailable.";
|
|
2945
|
+
return new OkmError2("unavailable", message, {
|
|
2946
|
+
kind: "unavailable",
|
|
2947
|
+
cause: error,
|
|
2948
|
+
fix: UNAVAILABLE_FIX
|
|
2949
|
+
});
|
|
2950
|
+
}
|
|
2951
|
+
async function finishQuiet(run) {
|
|
2952
|
+
await run().catch(() => {
|
|
2953
|
+
return;
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2956
|
+
function isOutcomeUnknown(error) {
|
|
2957
|
+
return typeof error === "object" && error !== null && Reflect.get(error, "kind") === "outcome_unknown";
|
|
2958
|
+
}
|
|
2959
|
+
|
|
2960
|
+
// src/tooling/migrate/policy.ts
|
|
2961
|
+
var READ = new Set([
|
|
2962
|
+
"plan",
|
|
2963
|
+
"status",
|
|
2964
|
+
"check",
|
|
2965
|
+
"drift",
|
|
2966
|
+
"verify",
|
|
2967
|
+
"pull",
|
|
2968
|
+
"catalog-export",
|
|
2969
|
+
"inspect"
|
|
2970
|
+
]);
|
|
2971
|
+
var ALLOWED = new Set(["expand", "reference", "provision"]);
|
|
2972
|
+
var FORWARD_ONLY = new Set(["drop", "rollback"]);
|
|
2973
|
+
function assertTargetPolicy(target, operation, allowProtected = false) {
|
|
2974
|
+
if (FORWARD_ONLY.has(operation)) {
|
|
2975
|
+
throw new OkmError2("invalid", "There is no down migration.", {
|
|
2976
|
+
fix: { summary: "Recovery is a new forward migration." }
|
|
2977
|
+
});
|
|
2978
|
+
}
|
|
2979
|
+
if (!target.protected || allowProtected)
|
|
2980
|
+
return;
|
|
2981
|
+
if (READ.has(operation) || ALLOWED.has(operation))
|
|
2982
|
+
return;
|
|
2983
|
+
throw new OkmError2("OKM1850", `${operation} is blocked on protected target ${target.name}.`, {
|
|
2984
|
+
kind: "forbidden",
|
|
2985
|
+
fix: {
|
|
2986
|
+
summary: "Run a read-only command or an expand migration, or pass --allow-protected for this invocation."
|
|
2987
|
+
}
|
|
2988
|
+
});
|
|
2989
|
+
}
|
|
2990
|
+
function assertTargetAlias(targets) {
|
|
2991
|
+
const groups = new Map;
|
|
2992
|
+
for (const target of targets) {
|
|
2993
|
+
const key = `${target.host}
|
|
2994
|
+
${target.port}
|
|
2995
|
+
${target.database}`;
|
|
2996
|
+
const group = groups.get(key);
|
|
2997
|
+
if (group === undefined)
|
|
2998
|
+
groups.set(key, [target]);
|
|
2999
|
+
else
|
|
3000
|
+
group.push(target);
|
|
3001
|
+
}
|
|
3002
|
+
for (const group of groups.values()) {
|
|
3003
|
+
const protectedTarget = group.find((target) => target.protected);
|
|
3004
|
+
const open = group.find((target) => !target.protected);
|
|
3005
|
+
if (protectedTarget !== undefined && open !== undefined) {
|
|
3006
|
+
throw new OkmError2("OKM1852", `${open.name} and ${protectedTarget.name} resolve to the same database and disagree about protection.`, {
|
|
3007
|
+
fix: {
|
|
3008
|
+
summary: "Give those targets the same protection flag. okm check and okm doctor both report this."
|
|
3009
|
+
}
|
|
3010
|
+
});
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
3014
|
+
function assertDirectConnection(url, allowPooler) {
|
|
3015
|
+
if (allowPooler)
|
|
3016
|
+
return;
|
|
3017
|
+
const endpoint = endpointOf(url);
|
|
3018
|
+
const parsed = new URL(url);
|
|
3019
|
+
const host = endpoint.host;
|
|
3020
|
+
if (host.includes("pooler") || host.includes("pgbouncer") || host.includes("-pooler") || endpoint.port === "6543" || endpoint.port === "6432" || parsed.searchParams.get("pgbouncer") === "true") {
|
|
3021
|
+
throw new OkmError2("invalid", `Refusing pooler host ${host}. Apply needs a direct connection.`, { fix: { summary: "Use a direct connection, or pass --allow-pooler." } });
|
|
3022
|
+
}
|
|
3023
|
+
}
|
|
3024
|
+
async function assertStableBackend(readPid, allowPooler) {
|
|
3025
|
+
if (allowPooler)
|
|
3026
|
+
return;
|
|
3027
|
+
const first = await readPid();
|
|
3028
|
+
const second = await readPid();
|
|
3029
|
+
if (first === second)
|
|
3030
|
+
return;
|
|
3031
|
+
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." } });
|
|
3032
|
+
}
|
|
3033
|
+
function listTargets(config) {
|
|
3034
|
+
if (config.database !== undefined && config.targets !== undefined) {
|
|
3035
|
+
throw new OkmError2("invalid", "Set database or targets, not both.", {
|
|
3036
|
+
fix: { summary: "database is the one target named default. Use targets for more than one." }
|
|
3037
|
+
});
|
|
3038
|
+
}
|
|
3039
|
+
const entries = [];
|
|
3040
|
+
if (config.database !== undefined)
|
|
3041
|
+
entries.push(["default", config.database]);
|
|
3042
|
+
if (config.targets !== undefined) {
|
|
3043
|
+
for (const name of Object.keys(config.targets).sort()) {
|
|
3044
|
+
const input = config.targets[name];
|
|
3045
|
+
if (input !== undefined)
|
|
3046
|
+
entries.push([name, input]);
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
return entries.map(([name, input]) => resolveTarget(name, input));
|
|
3050
|
+
}
|
|
3051
|
+
function selectTarget(config, name) {
|
|
3052
|
+
const targets = listTargets(config);
|
|
3053
|
+
if (targets.length === 0) {
|
|
3054
|
+
throw new OkmError2("OKM1845", "No target is configured.", {
|
|
3055
|
+
fix: { summary: "Set database or targets in okmodel.config.ts." }
|
|
3056
|
+
});
|
|
3057
|
+
}
|
|
3058
|
+
if (name === undefined) {
|
|
3059
|
+
if (targets.length === 1) {
|
|
3060
|
+
const only = targets[0];
|
|
3061
|
+
if (only !== undefined)
|
|
3062
|
+
return only;
|
|
3063
|
+
}
|
|
3064
|
+
throw new OkmError2("OKM1853", "Several targets are configured and --target was not passed.", {
|
|
3065
|
+
fix: { summary: "Pass --target <name>. The command does not guess." }
|
|
3066
|
+
});
|
|
3067
|
+
}
|
|
3068
|
+
const found = targets.find((target) => target.name === name);
|
|
3069
|
+
if (found === undefined) {
|
|
3070
|
+
throw new OkmError2("OKM1845", `Target ${name} is not configured.`, {
|
|
3071
|
+
fix: { summary: "Pass a name from targets, or database for the target named default." }
|
|
3072
|
+
});
|
|
3073
|
+
}
|
|
3074
|
+
return found;
|
|
3075
|
+
}
|
|
3076
|
+
function resolveTarget(name, input) {
|
|
3077
|
+
if (typeof input !== "string" && isReplicaTarget(input)) {
|
|
3078
|
+
throw new OkmError2("OKM1845", `Target ${name} is a replica. Migrations run on the primary only.`, {
|
|
3079
|
+
fix: {
|
|
3080
|
+
summary: "Set database or targets to the primary URL. A replica is not a migration target."
|
|
3081
|
+
}
|
|
3082
|
+
});
|
|
3083
|
+
}
|
|
3084
|
+
const url = typeof input === "string" ? input : input.url;
|
|
3085
|
+
const protectedTarget = typeof input === "string" ? false : input.protected === true;
|
|
3086
|
+
if (url === undefined || url.length === 0) {
|
|
3087
|
+
throw new OkmError2("OKM1845", `Target ${name} has no url.`, {
|
|
3088
|
+
fix: { summary: "Set the target url. A target is not inferred from the environment." }
|
|
3089
|
+
});
|
|
3090
|
+
}
|
|
3091
|
+
const endpoint = endpointOf(url);
|
|
3092
|
+
return {
|
|
3093
|
+
name,
|
|
3094
|
+
url,
|
|
3095
|
+
protected: protectedTarget,
|
|
3096
|
+
host: endpoint.host,
|
|
3097
|
+
port: endpoint.port,
|
|
3098
|
+
database: endpoint.database
|
|
3099
|
+
};
|
|
3100
|
+
}
|
|
3101
|
+
function isReplicaTarget(input) {
|
|
3102
|
+
return "primary" in input || "replicas" in input || "weight" in input || "pool" in input;
|
|
3103
|
+
}
|
|
3104
|
+
function endpointOf(url) {
|
|
3105
|
+
let parsed;
|
|
3106
|
+
try {
|
|
3107
|
+
parsed = new URL(url);
|
|
3108
|
+
} catch {
|
|
3109
|
+
throw new OkmError2("OKM1845", "The target URL is not a Postgres URL.", {
|
|
3110
|
+
fix: { summary: "Pass a postgres:// URL for the target." }
|
|
3111
|
+
});
|
|
3112
|
+
}
|
|
3113
|
+
const database = decodeURIComponent(parsed.pathname.replace(/^\//, ""));
|
|
3114
|
+
return {
|
|
3115
|
+
host: parsed.hostname.toLowerCase(),
|
|
3116
|
+
port: parsed.port.length > 0 ? parsed.port : "5432",
|
|
3117
|
+
database
|
|
3118
|
+
};
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
export { sqlStatements, transactionControl, canonicalTypeName, prepareMigrationText, assertMigrationSteps, columnPrivilege, createdRoleName, createRoleOnce, changesRole, DEFAULT_BACKFILL_PAUSE_MS, DEFAULT_BACKFILL_STATEMENT_TIMEOUT_MS, batchSizeField, aboutBatchCount, boundaryQuery, checkedCast, classOf, strictestClass, assertExtensionsAvailable, omitManagedObjects, parseReplace, planMigration, formatPlan, parsePlan, staleRenames, sessionFailure, finishQuiet, assertTargetPolicy, assertTargetAlias, assertDirectConnection, assertStableBackend, listTargets, selectTarget };
|