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.
- package/README.md +9 -1
- package/dist/adapters/pg/bunsql-extra.d.ts +5 -5
- package/dist/adapters/pg/bunsql.d.ts +6 -2
- package/dist/adapters/pg/nodepostgres.d.ts +16 -1
- package/dist/adapters/pg/postgres-extra.d.ts +3 -17
- package/dist/adapters/pg/postgresjs.d.ts +2 -1
- package/dist/contracts/driver.d.ts +3 -2
- package/dist/contracts/error.d.ts +1 -1
- package/dist/dialects/pg/errors.d.ts +2 -0
- package/dist/dialects/pg/ext/fuzzystrmatch.js +1 -1
- package/dist/dialects/pg/ext/hstore.js +1 -1
- package/dist/dialects/pg/ext/ltree.js +1 -1
- package/dist/dialects/pg/ext/pgcrypto.js +1 -1
- package/dist/dialects/pg/ext/unaccent.js +1 -1
- package/dist/dialects/pg/filters.d.ts +2 -2
- package/dist/dialects/pg/fn/index.js +1 -1
- package/dist/dialects/pg/index.js +3 -3
- package/dist/dialects/pg/introspect.d.ts +12 -0
- package/dist/dialects/pg/view/index.js +2 -2
- package/dist/runtime/param-limit.d.ts +20 -0
- package/dist/runtime/pg/bun.js +3 -3
- package/dist/runtime/pg/neon.js +3 -3
- package/dist/runtime/pg/pg.js +3 -3
- package/dist/runtime/pg/pglite.js +2 -2
- package/dist/runtime/pg/postgresjs.js +10 -10
- package/dist/runtime/tenancy/index.js +1 -1
- package/dist/runtime/traits/index.js +2 -2
- package/dist/runtime/validate/index.js +2 -2
- package/dist/runtime/write.d.ts +1 -1
- package/dist/shared/{5kyb3tqx.js → 1h5h1rf5.js} +1 -1
- package/dist/shared/{4vbf83xx.js → 2vcnpf0n.js} +1 -1
- package/dist/shared/{8n688yjg.js → 3ap5hxvj.js} +2 -1
- package/dist/shared/{wvtjg5m3.js → 3d7a80fq.js} +1 -1
- package/dist/shared/{bphrgwz9.js → 3dr444ny.js} +57 -78
- package/dist/shared/{yvabyg9k.js → 3vvpcphd.js} +7 -6
- package/dist/shared/{760ppr76.js → 4a3mmhhe.js} +2 -2
- package/dist/shared/{90sg0w5a.js → 4pkz5nvw.js} +65 -10
- package/dist/shared/{mazpbcdj.js → 4te76xc5.js} +1 -1
- package/dist/shared/{xwhdcrm4.js → 60y4ahmw.js} +1 -1
- package/dist/shared/{9dtgn1z2.js → 63qjmhnp.js} +100 -17
- package/dist/shared/{s7he5e5d.js → 67w3kn71.js} +1 -1
- package/dist/shared/{n306gqkd.js → 6vfvsdkt.js} +9 -7
- package/dist/shared/{jpybhf98.js → 6xqvesa5.js} +1 -1
- package/dist/shared/{n5whk3mt.js → 8gdbty5v.js} +8 -8
- package/dist/shared/{ccsmhv9r.js → 8ma6drzr.js} +194 -31
- package/dist/shared/{3456242y.js → 8p2bgye0.js} +110 -18
- package/dist/shared/{ebhz8vh6.js → 8qwqrxed.js} +11 -3
- package/dist/shared/{ss2kv2rs.js → 8v7vzpgt.js} +10 -7
- package/dist/shared/{8qzkd9f3.js → ak61ye1f.js} +3 -3
- package/dist/shared/{6nae0spm.js → bdekbvmt.js} +69 -7
- package/dist/shared/{tw1nvvfj.js → bxjq6xqv.js} +2 -2
- package/dist/shared/{rbxt795q.js → c7dqd3gk.js} +81 -41
- package/dist/shared/ddh72ym5.js +20 -0
- package/dist/shared/e1c55v04.js +10 -0
- package/dist/shared/{8jesqsrk.js → ekhq3ytz.js} +1 -1
- package/dist/shared/{7ba5h6pb.js → esy4dyxe.js} +5 -5
- package/dist/shared/{gcefca6p.js → f9t9g54z.js} +22 -9
- package/dist/shared/fkjne6gx.js +8 -0
- package/dist/shared/{ds229x4r.js → fne90dvz.js} +4 -1
- package/dist/shared/{cexb7z6b.js → gr9sn4za.js} +1 -1
- package/dist/shared/{mvmm3xpb.js → gzgfsspx.js} +1 -1
- package/dist/shared/{fdsz3w80.js → hgt69zjf.js} +2 -2
- package/dist/shared/{dd5pmnky.js → jvxcp1qk.js} +70 -86
- package/dist/shared/{yybhre6n.js → k73e8n9d.js} +3 -3
- package/dist/shared/{mgaq2sd3.js → kzgrcy8s.js} +8 -7
- package/dist/shared/{myk8fswx.js → mz48rs74.js} +14 -3
- package/dist/shared/{62b603gr.js → n6je5b28.js} +10 -23
- package/dist/shared/{2szgv6yj.js → ns35jqz0.js} +71 -45
- package/dist/shared/{xtsydxjg.js → nstwgzn8.js} +1 -1
- package/dist/shared/{stfa9xcw.js → py2zrrm6.js} +7 -6
- package/dist/shared/{9dr2qnx7.js → qqm3ze6q.js} +1 -1
- package/dist/shared/{cf8zdz5p.js → rcjm0yn7.js} +27 -34
- package/dist/shared/{ckz05z15.js → rged92v6.js} +7 -4
- package/dist/shared/{qvwwp1mp.js → rw4tpcd0.js} +30 -21
- package/dist/shared/{bsek72fg.js → s5qe47a6.js} +3 -0
- package/dist/shared/{1d9q9cv2.js → smxzjz60.js} +2 -2
- package/dist/shared/{c3mvnq7a.js → th5rnaxw.js} +1 -1
- package/dist/shared/{1mxm04fy.js → tyqapcgk.js} +71 -8
- package/dist/shared/{zdqf8k5d.js → vsna85qf.js} +1 -1
- package/dist/shared/{63qtkrca.js → vy47ryqa.js} +55 -5
- package/dist/shared/{xv8qj97g.js → wbdh9qbg.js} +3 -1
- package/dist/shared/{2zdxkt7b.js → wp7y8q3y.js} +41 -9
- package/dist/shared/{pg4ezgyf.js → xbd9xs6p.js} +2 -2
- package/dist/shared/{npdftanb.js → xbvz446k.js} +16 -13
- package/dist/shared/{aksb89mn.js → y6tfjk9q.js} +2 -2
- package/dist/shared/{3bb7wsqt.js → ycnft340.js} +1 -1
- package/dist/shared/{z7s6fpxv.js → ymenf259.js} +1 -1
- package/dist/shared/{71n0jmhc.js → ysdw2fg3.js} +10 -2
- package/dist/shared/{xqacq37x.js → zecrwdk9.js} +1 -1
- package/dist/shared/{3t6kw83m.js → zm2rzqba.js} +2 -1
- package/dist/tooling/cli.js +122 -44
- package/dist/tooling/driver-run.js +107 -24
- package/dist/tooling/github/comment.d.ts +8 -2
- package/dist/tooling/migrate/apply.d.ts +17 -2
- package/dist/tooling/migrate/drift.d.ts +12 -0
- package/dist/tooling/migrate/driver-url.d.ts +17 -0
- package/dist/tooling/migrate/index.js +9 -7
- package/dist/tooling/migrate/policy.d.ts +17 -5
- package/dist/tooling/migrate/pooler-probe.d.ts +23 -0
- package/dist/tooling/migrate/pull.d.ts +3 -0
- package/dist/tooling/migrate/runner.d.ts +24 -1
- package/dist/tooling/shared/{0j0jtse1.js → 09a6vsys.js} +1 -1
- package/dist/tooling/shared/{68tf18v4.js → 0v9ptnxp.js} +8 -8
- package/dist/tooling/shared/{qartgazh.js → 0wtzps9p.js} +6 -6
- package/dist/tooling/shared/{1800eymw.js → 0yhf1db4.js} +6 -6
- package/dist/tooling/shared/{s6qha57m.js → 19msgrsc.js} +2 -2
- package/dist/tooling/shared/{3yn79q5m.js → 1fwfm5m3.js} +1 -1
- package/dist/tooling/shared/{hfq3n3bd.js → 2jdn09e2.js} +55 -5
- package/dist/tooling/shared/{v6j1ep6w.js → 2qrte2ct.js} +5 -5
- package/dist/tooling/shared/{yb4kd19s.js → 2v4nwnkb.js} +1 -1
- package/dist/tooling/shared/{ezqn88nj.js → 33z39trp.js} +106 -23
- package/dist/tooling/shared/{0r05zhyq.js → 353vnw9z.js} +5 -5
- package/dist/tooling/shared/{m4hhb8aa.js → 390kry9g.js} +2 -2
- package/dist/tooling/shared/{rcywj2w8.js → 3f6etb9z.js} +18 -31
- package/dist/tooling/shared/{9wsnsvbf.js → 3t2cqmsx.js} +1 -1
- package/dist/tooling/shared/{5zrzffnk.js → 4dzvjtpy.js} +1 -1
- package/dist/tooling/shared/{am04v8na.js → 4ec0nxvw.js} +10 -10
- package/dist/tooling/shared/{04svnxxj.js → 4tg2bn41.js} +59 -78
- package/dist/tooling/shared/{829fjxyw.js → 5rah55z0.js} +2 -2
- package/dist/tooling/shared/{c0xevn1h.js → 804w1t69.js} +4 -4
- package/dist/tooling/shared/{dhatassp.js → 8g0m0rvd.js} +6 -6
- package/dist/tooling/shared/{zx3v9phv.js → 8msbtw5s.js} +1 -1
- package/dist/tooling/shared/{7pxncnzs.js → 8t22h3h7.js} +2 -2
- package/dist/tooling/shared/{wbwq1jsa.js → 91c59b7b.js} +2 -2
- package/dist/tooling/shared/{a2xe04bm.js → adk9k6va.js} +1 -1
- package/dist/tooling/shared/{w17sj05d.js → ajjd5cwz.js} +2 -2
- package/dist/tooling/shared/{35zemrmf.js → angjck01.js} +1 -1
- package/dist/tooling/shared/{jsjjz875.js → asnnc3v5.js} +1 -1
- package/dist/tooling/shared/{5bmksndh.js → bj1kyqc8.js} +2 -2
- package/dist/tooling/shared/{kbqnrs1x.js → bt3tjemb.js} +11 -3
- package/dist/tooling/shared/{vwr2y833.js → chk2bh4f.js} +2 -2
- package/dist/tooling/shared/{ybdew6j7.js → ckq4z5ax.js} +5 -2
- package/dist/tooling/shared/{3rmaz3pq.js → d3rhb24q.js} +1 -1
- package/dist/tooling/shared/{mw5npye0.js → d8q00vxg.js} +4 -4
- package/dist/tooling/shared/{j664qjrt.js → dx188rxs.js} +2 -2
- package/dist/tooling/shared/{8yr7ges1.js → efq6p9df.js} +35 -42
- package/dist/tooling/shared/{yhp829bx.js → en6rwzq0.js} +3 -3
- package/dist/tooling/shared/{f77nnfqy.js → f2r5g0fs.js} +2 -2
- package/dist/tooling/shared/{zs9aymcp.js → f4gep0nk.js} +1 -1
- package/dist/tooling/shared/{kz715295.js → f8a010fm.js} +13 -11
- package/dist/tooling/shared/{w5d89rb4.js → fdsek6pa.js} +1 -1
- package/dist/tooling/shared/fvmrxsd1.js +8 -0
- package/dist/tooling/shared/{jpgmjsx3.js → g3xybeqf.js} +4 -3
- package/dist/tooling/shared/{wezxf3h8.js → gefmpwsa.js} +1 -1
- package/dist/tooling/shared/{jr2nmr81.js → gjj71hfh.js} +23 -20
- package/dist/tooling/shared/{9sfbtwsk.js → jgzt69pa.js} +5 -5
- package/dist/tooling/shared/{a4pzghha.js → jm30k2ca.js} +10 -8
- package/dist/tooling/shared/{shjs0379.js → kayqzpbk.js} +6 -3
- package/dist/tooling/shared/{tcwpjvxe.js → kvw1672d.js} +1 -1
- package/dist/tooling/shared/{stebqyyd.js → mr1kcgpw.js} +3 -3
- package/dist/tooling/shared/{tpssp7ac.js → mwbdf409.js} +1 -1
- package/dist/tooling/shared/{fh07z1dt.js → ns9207v3.js} +1 -1
- package/dist/tooling/shared/{5p8km5jb.js → p6tjv97k.js} +3 -3
- package/dist/tooling/shared/{ytk1tjs0.js → p73evwf0.js} +1 -1
- package/dist/tooling/shared/{8m5znpdt.js → phc1nc4g.js} +1 -1
- package/dist/tooling/shared/{asgr07mp.js → ppybjw50.js} +79 -16
- package/dist/tooling/shared/{t4xveemq.js → qdrn8ghn.js} +5 -4
- package/dist/tooling/shared/rd8h01ta.js +20 -0
- package/dist/tooling/shared/{7aews1zg.js → rwg8y90m.js} +3 -3
- package/dist/tooling/shared/{5x3pf400.js → ry2f3w8p.js} +3 -3
- package/dist/tooling/shared/{m3xt8b1h.js → ryc5sa58.js} +8 -8
- package/dist/tooling/shared/{57btdkgm.js → ss87qxwv.js} +2 -2
- package/dist/tooling/shared/{6vk28rjp.js → vg68k11z.js} +3 -3
- package/dist/tooling/shared/{62fxpc1p.js → vms9dta5.js} +73 -89
- package/dist/tooling/shared/{gqwevek4.js → xchpnbdv.js} +1 -1
- package/dist/tooling/shared/{t6zjeake.js → xn6p01fh.js} +5 -5
- package/dist/tooling/shared/{7b3xp9gc.js → xwr6q618.js} +3 -3
- package/dist/tooling/shared/{yfedr1zb.js → xx3gmq94.js} +1 -1
- package/dist/tooling/shared/{9qr9rkbh.js → y2bd7yfx.js} +21 -13
- package/dist/tooling/shared/{nxq95eb6.js → y4gq9rbe.js} +11 -11
- package/dist/tooling/shared/{76pv6qff.js → y4vwft92.js} +2 -2
- package/dist/tooling/shared/{sjwkt1pg.js → yz68xcmc.js} +1 -1
- package/dist/tooling/testing/factories.d.ts +1 -3
- package/dist/tooling/testing/index.js +1 -1
- package/package.json +2 -1
|
@@ -7,11 +7,12 @@ import {
|
|
|
7
7
|
} from "./fk9vef30.js";
|
|
8
8
|
import {
|
|
9
9
|
roleExists
|
|
10
|
-
} from "./
|
|
10
|
+
} from "./6xqvesa5.js";
|
|
11
11
|
import {
|
|
12
12
|
canonicalTypeName,
|
|
13
13
|
createdRoleName,
|
|
14
14
|
createRoleOnce,
|
|
15
|
+
changesRole,
|
|
15
16
|
DEFAULT_BACKFILL_PAUSE_MS,
|
|
16
17
|
DEFAULT_BACKFILL_STATEMENT_TIMEOUT_MS,
|
|
17
18
|
boundaryQuery,
|
|
@@ -20,10 +21,10 @@ import {
|
|
|
20
21
|
planMigration,
|
|
21
22
|
sessionFailure,
|
|
22
23
|
assertTargetPolicy
|
|
23
|
-
} from "./
|
|
24
|
+
} from "./bdekbvmt.js";
|
|
24
25
|
import {
|
|
25
26
|
snakeCase
|
|
26
|
-
} from "./
|
|
27
|
+
} from "./f9t9g54z.js";
|
|
27
28
|
import {
|
|
28
29
|
catalog,
|
|
29
30
|
isDomain
|
|
@@ -70,9 +71,68 @@ async function runTarget(connection, run, session) {
|
|
|
70
71
|
async function assumeRole(connection, role, session) {
|
|
71
72
|
if (session.setRole === role)
|
|
72
73
|
return;
|
|
74
|
+
await ensureSetOption(connection, role);
|
|
73
75
|
await connection.execute(`set role ${quoteIdent2(role)}`);
|
|
74
76
|
session.setRole = role;
|
|
75
77
|
}
|
|
78
|
+
async function ensureSetOption(connection, role) {
|
|
79
|
+
const who = await connection.execute("select current_user, rolsuper from pg_roles where rolname = current_user");
|
|
80
|
+
const current = who.rows[0]?.[0];
|
|
81
|
+
if (typeof current !== "string" || current === role)
|
|
82
|
+
return;
|
|
83
|
+
if (roleFlag(who.rows[0]?.[1]))
|
|
84
|
+
return;
|
|
85
|
+
const version = await connection.execute("show server_version_num");
|
|
86
|
+
const versionNum = Number(version.rows[0]?.[0] ?? 0);
|
|
87
|
+
if (Number.isFinite(versionNum) && versionNum >= 160000) {
|
|
88
|
+
const option = await connection.execute(`select am.set_option
|
|
89
|
+
from pg_auth_members am
|
|
90
|
+
join pg_roles r on r.oid = am.roleid
|
|
91
|
+
join pg_roles m on m.oid = am.member
|
|
92
|
+
where r.rolname = $1 and m.rolname = $2`, [role, current]);
|
|
93
|
+
if (roleFlag(option.rows[0]?.[0]))
|
|
94
|
+
return;
|
|
95
|
+
await connection.execute(`grant ${quoteIdent2(role)} to ${quoteIdent2(current)} with set true`);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const member = await connection.execute("select pg_has_role($1, $2, 'MEMBER')", [current, role]);
|
|
99
|
+
if (roleFlag(member.rows[0]?.[0]))
|
|
100
|
+
return;
|
|
101
|
+
await connection.execute(`grant ${quoteIdent2(role)} to ${quoteIdent2(current)}`);
|
|
102
|
+
}
|
|
103
|
+
function rolePrivilegeError(error, role) {
|
|
104
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
105
|
+
if (/permission denied to create role/i.test(message)) {
|
|
106
|
+
return new OkmError2("forbidden", "CREATEROLE is required to create roles. Nothing was changed.", {
|
|
107
|
+
kind: "forbidden",
|
|
108
|
+
cause: error,
|
|
109
|
+
fix: {
|
|
110
|
+
summary: "Grant CREATEROLE to the connecting role, or connect as a superuser."
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (/permission denied to set role/i.test(message)) {
|
|
115
|
+
const name = role ?? "the migration role";
|
|
116
|
+
return new OkmError2("forbidden", `Cannot SET ROLE ${name}: the connecting role lacks the SET option on that membership.`, {
|
|
117
|
+
kind: "forbidden",
|
|
118
|
+
cause: error,
|
|
119
|
+
fix: {
|
|
120
|
+
summary: `Grant ${name} to the connecting role (WITH SET TRUE on Postgres 16+), or connect as a superuser.`
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (/must be able to SET ROLE|not a member/i.test(message)) {
|
|
125
|
+
const name = role ?? "the migration role";
|
|
126
|
+
return new OkmError2("forbidden", `Cannot SET ROLE ${name}: the connecting role is not a member with the SET option.`, {
|
|
127
|
+
kind: "forbidden",
|
|
128
|
+
cause: error,
|
|
129
|
+
fix: {
|
|
130
|
+
summary: `Grant ${name} to the connecting role (WITH SET TRUE on Postgres 16+), or connect as a superuser.`
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
76
136
|
async function grantAppMeta(connection, role, session) {
|
|
77
137
|
if (role === undefined)
|
|
78
138
|
return;
|
|
@@ -86,6 +146,23 @@ async function grantAppMeta(connection, role, session) {
|
|
|
86
146
|
async function grantSchema(connection, schema, role) {
|
|
87
147
|
await connection.execute(`grant usage, create on schema ${quoteIdent2(schema)} to ${quoteIdent2(role)}`);
|
|
88
148
|
}
|
|
149
|
+
async function ownSchema(connection, schema, role) {
|
|
150
|
+
await ensureSetOption(connection, role);
|
|
151
|
+
await connection.execute("savepoint okm_own_schema");
|
|
152
|
+
try {
|
|
153
|
+
const database = await databaseName(connection);
|
|
154
|
+
await connection.execute(`grant create on database ${quoteIdent2(database)} to ${quoteIdent2(role)}`);
|
|
155
|
+
await connection.execute(`alter schema ${quoteIdent2(schema)} owner to ${quoteIdent2(role)}`);
|
|
156
|
+
await connection.execute("release savepoint okm_own_schema");
|
|
157
|
+
} catch {
|
|
158
|
+
await connection.execute("rollback to savepoint okm_own_schema");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async function databaseName(connection) {
|
|
162
|
+
const result = await connection.execute("select current_database()");
|
|
163
|
+
const name = result.rows[0]?.[0];
|
|
164
|
+
return typeof name === "string" ? name : "postgres";
|
|
165
|
+
}
|
|
89
166
|
async function runUnit(connection, unit, run, session) {
|
|
90
167
|
const only = unit.steps.length === 1 ? unit.steps[0] : undefined;
|
|
91
168
|
if (only?.step.backfill !== undefined) {
|
|
@@ -102,6 +179,9 @@ async function runUnit(connection, unit, run, session) {
|
|
|
102
179
|
}
|
|
103
180
|
for (const item of unit.steps) {
|
|
104
181
|
current = item.index;
|
|
182
|
+
if (run.migrationRole !== undefined && !changesRole(item.step.sql) && session.setRole !== run.migrationRole && await roleExists(connection, run.migrationRole)) {
|
|
183
|
+
await assumeRole(connection, run.migrationRole, session);
|
|
184
|
+
}
|
|
105
185
|
await rebuildInvalidIndex(connection, item.step.sql);
|
|
106
186
|
const created = createdRoleName(item.step.sql);
|
|
107
187
|
const existed = created !== undefined && await roleExists(connection, created);
|
|
@@ -110,12 +190,10 @@ async function runUnit(connection, unit, run, session) {
|
|
|
110
190
|
await assertSchemaUsageLanded(connection, item.step.sql);
|
|
111
191
|
} else if (!existed)
|
|
112
192
|
await connection.execute(createRoleOnce(item.step.sql));
|
|
113
|
-
if (created !== undefined && created === run.migrationRole) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
await assumeRole(connection, created, session);
|
|
193
|
+
if (created !== undefined && created === run.migrationRole && !existed) {
|
|
194
|
+
await grantSchema(connection, run.schema ?? "public", created);
|
|
195
|
+
await ownSchema(connection, run.schema ?? "public", created);
|
|
196
|
+
await connection.execute(`grant select, insert, update on okm_meta, okm_history, okm_backfill to ${quoteIdent2(created)}`);
|
|
119
197
|
}
|
|
120
198
|
if (!unit.transactional) {
|
|
121
199
|
await connection.execute("begin");
|
|
@@ -127,6 +205,9 @@ async function runUnit(connection, unit, run, session) {
|
|
|
127
205
|
began = false;
|
|
128
206
|
}
|
|
129
207
|
}
|
|
208
|
+
if (run.migrationRole !== undefined && session.setRole !== run.migrationRole && await roleExists(connection, run.migrationRole)) {
|
|
209
|
+
await assumeRole(connection, run.migrationRole, session);
|
|
210
|
+
}
|
|
130
211
|
if (unit.transactional) {
|
|
131
212
|
for (const item of unit.steps)
|
|
132
213
|
await writeStep(connection, unit, item);
|
|
@@ -182,7 +263,7 @@ async function runBackfill(connection, unit, item, spec, run) {
|
|
|
182
263
|
rows,
|
|
183
264
|
lastKey: committed.lastKey
|
|
184
265
|
};
|
|
185
|
-
run.onProgress?.(`backfill ${unit.migrationId} step ${String(item.index)} batch ${String(batches)} rows ${String(rows)} key ${committed.lastKey ?? "-"}`);
|
|
266
|
+
run.onProgress?.(`backfill ${unit.migrationId} step ${String(item.index + 1)} batch ${String(batches)} rows ${String(rows)} key ${committed.lastKey ?? "-"}`);
|
|
186
267
|
await run.afterBatch?.(note);
|
|
187
268
|
if (committed.final)
|
|
188
269
|
return;
|
|
@@ -325,8 +406,7 @@ async function assertSchemaUsageLanded(connection, sql) {
|
|
|
325
406
|
join pg_roles r on r.oid = a.grantee
|
|
326
407
|
where n.nspname = $1 and r.rolname = $2 and a.privilege_type = 'USAGE'
|
|
327
408
|
)`, [schema, role]);
|
|
328
|
-
|
|
329
|
-
if (landed === "t" || landed === "true")
|
|
409
|
+
if (roleFlag(found.rows[0]?.[0]))
|
|
330
410
|
return;
|
|
331
411
|
throw new OkmError2("OKM1825", `Grant usage on schema ${schema} to ${role} did not take effect.`, {
|
|
332
412
|
kind: "forbidden",
|
|
@@ -340,6 +420,10 @@ function failStep(unit, index, error) {
|
|
|
340
420
|
if (lost !== undefined) {
|
|
341
421
|
return new OkmError2(lost.code, `Migration ${unit.migrationId} failed at step ${String(index + 1)}: ${lost.message}`, { kind: lost.kind, cause: error, fix: lost.fix });
|
|
342
422
|
}
|
|
423
|
+
const privilege = rolePrivilegeError(error, undefined);
|
|
424
|
+
if (privilege !== undefined) {
|
|
425
|
+
return new OkmError2(privilege.code, `Migration ${unit.migrationId} failed at step ${String(index + 1)}: ${privilege.message}`, { kind: privilege.kind, cause: error, fix: privilege.fix });
|
|
426
|
+
}
|
|
343
427
|
if (error instanceof OkmError2)
|
|
344
428
|
return error;
|
|
345
429
|
const message = error instanceof Error ? error.message : "The step failed.";
|
|
@@ -359,6 +443,9 @@ function isLockTimeout(error) {
|
|
|
359
443
|
function wireTrue(value) {
|
|
360
444
|
return value === "t" || value === "true";
|
|
361
445
|
}
|
|
446
|
+
function roleFlag(value) {
|
|
447
|
+
return value === true || value === "t" || value === "true";
|
|
448
|
+
}
|
|
362
449
|
function unquote(token) {
|
|
363
450
|
if (token.startsWith('"') && token.endsWith('"')) {
|
|
364
451
|
return token.slice(1, -1).replaceAll('""', '"');
|
|
@@ -598,13 +685,14 @@ async function installSnapshot(connection, head, schema, reference, session, mig
|
|
|
598
685
|
for (const step of plan.steps) {
|
|
599
686
|
if (step.backfill !== undefined)
|
|
600
687
|
continue;
|
|
688
|
+
if (migrationRole !== undefined && !changesRole(step.sql) && session.setRole !== migrationRole && await roleExists(connection, migrationRole)) {
|
|
689
|
+
await flush();
|
|
690
|
+
await assumeRole(connection, migrationRole, session);
|
|
691
|
+
}
|
|
601
692
|
const created = createdRoleName(step.sql);
|
|
602
693
|
const existed = created !== undefined && await roleExists(connection, created);
|
|
603
|
-
if (created !== undefined && existed)
|
|
604
|
-
if (created === migrationRole)
|
|
605
|
-
await assumeRole(connection, created, session);
|
|
694
|
+
if (created !== undefined && existed)
|
|
606
695
|
continue;
|
|
607
|
-
}
|
|
608
696
|
const sql = created === undefined ? step.sql : createRoleOnce(step.sql);
|
|
609
697
|
if (step.transactional && !statementOutsideTransaction(step.sql)) {
|
|
610
698
|
batch.push(sql);
|
|
@@ -616,10 +704,14 @@ async function installSnapshot(connection, head, schema, reference, session, mig
|
|
|
616
704
|
if (created !== undefined && created === migrationRole) {
|
|
617
705
|
await flush();
|
|
618
706
|
await grantSchema(connection, schema, created);
|
|
707
|
+
await ownSchema(connection, schema, created);
|
|
619
708
|
await connection.execute(`grant select, insert, update on okm_meta, okm_history, okm_backfill to ${quoteIdent2(created)}`);
|
|
620
|
-
await assumeRole(connection, created, session);
|
|
621
709
|
}
|
|
622
710
|
}
|
|
711
|
+
if (migrationRole !== undefined && session.setRole !== migrationRole && await roleExists(connection, migrationRole)) {
|
|
712
|
+
await flush();
|
|
713
|
+
await assumeRole(connection, migrationRole, session);
|
|
714
|
+
}
|
|
623
715
|
await flush();
|
|
624
716
|
for (const sql of referenceInserts(reference, schema)) {
|
|
625
717
|
if (!began) {
|
|
@@ -754,4 +846,4 @@ function normaliseFunction(object) {
|
|
|
754
846
|
};
|
|
755
847
|
}
|
|
756
848
|
|
|
757
|
-
export { runTarget, assumeRole, grantAppMeta, grantSchema, assertSchemaUsageLanded, statementOutsideTransaction, readReference, referenceInserts, catalogsEqual, provisionMark, expandProvisioned, provisionHistoryIsKnown, installSnapshot, snapshotDifference };
|
|
849
|
+
export { runTarget, assumeRole, grantAppMeta, grantSchema, ownSchema, assertSchemaUsageLanded, statementOutsideTransaction, readReference, referenceInserts, catalogsEqual, provisionMark, expandProvisioned, provisionHistoryIsKnown, installSnapshot, snapshotDifference };
|
|
@@ -3,9 +3,10 @@ import {
|
|
|
3
3
|
} from "./cm3w0ywp.js";
|
|
4
4
|
import {
|
|
5
5
|
armDriverError,
|
|
6
|
+
logClientError,
|
|
6
7
|
send,
|
|
7
8
|
connectClient
|
|
8
|
-
} from "./
|
|
9
|
+
} from "./tyqapcgk.js";
|
|
9
10
|
import {
|
|
10
11
|
resetSession2
|
|
11
12
|
} from "./99qp9vtg.js";
|
|
@@ -31,8 +32,10 @@ async function checkout(held) {
|
|
|
31
32
|
const client = await connectClient(pool, held.acquireMs, counters);
|
|
32
33
|
counters.reserved += 1;
|
|
33
34
|
let lost = false;
|
|
34
|
-
const onError = () => {
|
|
35
|
+
const onError = (error) => {
|
|
35
36
|
lost = true;
|
|
37
|
+
if (error !== undefined)
|
|
38
|
+
logClientError(held.notices, error);
|
|
36
39
|
};
|
|
37
40
|
const events = client;
|
|
38
41
|
events.on("error", onError);
|
|
@@ -40,7 +43,7 @@ async function checkout(held) {
|
|
|
40
43
|
const session = new WireSession((text, params) => {
|
|
41
44
|
const pending = send(client, text, params, named, client);
|
|
42
45
|
const guarded = pending.catch((error) => {
|
|
43
|
-
if (isConnectionLost(error) || lost) {
|
|
46
|
+
if (isConnectionLost(error) || lost || pgClosed(error)) {
|
|
44
47
|
lost = true;
|
|
45
48
|
if (isConnectionLost(error))
|
|
46
49
|
throw error;
|
|
@@ -77,6 +80,8 @@ async function checkout(held) {
|
|
|
77
80
|
try {
|
|
78
81
|
if (!lost)
|
|
79
82
|
await reset(session, client);
|
|
83
|
+
} catch {
|
|
84
|
+
lost = true;
|
|
80
85
|
} finally {
|
|
81
86
|
counters.reserved = Math.max(0, counters.reserved - 1);
|
|
82
87
|
events.removeListener("error", onError);
|
|
@@ -85,6 +90,9 @@ async function checkout(held) {
|
|
|
85
90
|
}
|
|
86
91
|
};
|
|
87
92
|
}
|
|
93
|
+
function pgClosed(error) {
|
|
94
|
+
return error instanceof Error && error.message === "Client has encountered a connection error and is not queryable";
|
|
95
|
+
}
|
|
88
96
|
async function reset(session, client) {
|
|
89
97
|
await resetSession2(session.depth(), (text) => client.query(text));
|
|
90
98
|
}
|
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
import {
|
|
2
2
|
open2
|
|
3
|
-
} from "./
|
|
3
|
+
} from "./jvxcp1qk.js";
|
|
4
4
|
import"./12z7aznj.js";
|
|
5
5
|
import"./fk9vef30.js";
|
|
6
6
|
import {
|
|
7
7
|
createdOf,
|
|
8
8
|
testing2
|
|
9
|
-
} from "./
|
|
9
|
+
} from "./ns35jqz0.js";
|
|
10
10
|
import"./n442h54c.js";
|
|
11
|
+
import {
|
|
12
|
+
probeStableBackend
|
|
13
|
+
} from "./e1c55v04.js";
|
|
11
14
|
import {
|
|
12
15
|
projectPath,
|
|
13
16
|
loadConfig,
|
|
14
17
|
loadSchema
|
|
15
|
-
} from "./
|
|
18
|
+
} from "./wp7y8q3y.js";
|
|
16
19
|
import {
|
|
20
|
+
driverUrl,
|
|
17
21
|
sessionFailure,
|
|
18
22
|
finishQuiet,
|
|
19
23
|
assertTargetPolicy,
|
|
20
24
|
assertDirectConnection,
|
|
21
|
-
assertStableBackend,
|
|
22
25
|
selectTarget
|
|
23
|
-
} from "./
|
|
26
|
+
} from "./bdekbvmt.js";
|
|
24
27
|
import {
|
|
25
28
|
OkmError2
|
|
26
29
|
} from "./52szzakm.js";
|
|
@@ -41,13 +44,13 @@ async function seedProject(cwd, file, flags) {
|
|
|
41
44
|
const allowPooler = flags.allowPooler || config.allowPooler === true;
|
|
42
45
|
assertDirectConnection(target.url, allowPooler);
|
|
43
46
|
const schema = await loadSchema(cwd, config.schema);
|
|
44
|
-
const pool = open2({ url: target.url, max: 1 });
|
|
47
|
+
const pool = open2({ url: driverUrl(target.url), max: 1 });
|
|
45
48
|
let harness;
|
|
46
49
|
try {
|
|
47
50
|
const held = await pool.reserve?.();
|
|
48
51
|
if (held !== undefined) {
|
|
49
52
|
try {
|
|
50
|
-
await
|
|
53
|
+
await probeStableBackend(target.url, async () => {
|
|
51
54
|
const result = await held.execute("select pg_backend_pid()::text");
|
|
52
55
|
const cell = result.rows[0]?.[0];
|
|
53
56
|
return cell === null || cell === undefined ? "" : String(cell);
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import"./12z7aznj.js";
|
|
2
2
|
import"./n442h54c.js";
|
|
3
3
|
import"./fk9vef30.js";
|
|
4
|
-
import"./
|
|
4
|
+
import"./jvxcp1qk.js";
|
|
5
5
|
import {
|
|
6
6
|
projectPath,
|
|
7
7
|
loadConfig
|
|
8
|
-
} from "./
|
|
8
|
+
} from "./wp7y8q3y.js";
|
|
9
9
|
import {
|
|
10
10
|
listTargets
|
|
11
|
-
} from "./
|
|
11
|
+
} from "./bdekbvmt.js";
|
|
12
12
|
|
|
13
13
|
// src/tooling/migrate/dev.ts
|
|
14
14
|
import { mkdirSync } from "node:fs";
|
|
@@ -47,6 +47,20 @@ import {
|
|
|
47
47
|
OkmError2
|
|
48
48
|
} from "./52szzakm.js";
|
|
49
49
|
|
|
50
|
+
// src/tooling/migrate/driver-url.ts
|
|
51
|
+
function driverUrl(url) {
|
|
52
|
+
let parsed;
|
|
53
|
+
try {
|
|
54
|
+
parsed = new URL(url);
|
|
55
|
+
} catch {
|
|
56
|
+
return url;
|
|
57
|
+
}
|
|
58
|
+
if (!parsed.searchParams.has("pgbouncer"))
|
|
59
|
+
return url;
|
|
60
|
+
parsed.searchParams.delete("pgbouncer");
|
|
61
|
+
return parsed.toString();
|
|
62
|
+
}
|
|
63
|
+
|
|
50
64
|
// src/tooling/migrate/sql-lex.ts
|
|
51
65
|
function sqlStatements(sql) {
|
|
52
66
|
return scan(sql).statements;
|
|
@@ -545,6 +559,9 @@ function privilegeSql(before, after, schema, recreated = new Set) {
|
|
|
545
559
|
const tighten = tightenColumnSelect(object, after, schema);
|
|
546
560
|
if (tighten !== undefined)
|
|
547
561
|
grant.push(tighten);
|
|
562
|
+
const fromPublic = revokePublicExecute(object, schema);
|
|
563
|
+
if (fromPublic !== undefined)
|
|
564
|
+
grant.push(fromPublic);
|
|
548
565
|
grant.push({ sql: grantStatement(object, schema, false), kind: "grant" });
|
|
549
566
|
const writes = revokeMatviewWrites(object, after, schema);
|
|
550
567
|
if (writes !== undefined)
|
|
@@ -554,6 +571,9 @@ function privilegeSql(before, after, schema, recreated = new Set) {
|
|
|
554
571
|
if (previous !== undefined)
|
|
555
572
|
continue;
|
|
556
573
|
if (object.kind === "defaultPrivilege") {
|
|
574
|
+
const fromPublic = revokePublicDefaultExecute(object, schema);
|
|
575
|
+
if (fromPublic !== undefined)
|
|
576
|
+
defaults.push(fromPublic);
|
|
557
577
|
defaults.push({ sql: defaultStatement(object, schema, false), kind: "grant-default" });
|
|
558
578
|
}
|
|
559
579
|
}
|
|
@@ -606,6 +626,26 @@ function tightenColumnSelect(object, after, schema) {
|
|
|
606
626
|
kind: "revoke"
|
|
607
627
|
};
|
|
608
628
|
}
|
|
629
|
+
function revokePublicExecute(object, schema) {
|
|
630
|
+
if (object.identity.privilege.toUpperCase() !== "EXECUTE")
|
|
631
|
+
return;
|
|
632
|
+
if (object.identity.object.kind !== "function")
|
|
633
|
+
return;
|
|
634
|
+
return {
|
|
635
|
+
sql: `revoke execute on ${grantTarget(object.identity.object, schema)} from public`,
|
|
636
|
+
kind: "revoke"
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
function revokePublicDefaultExecute(object, schema) {
|
|
640
|
+
if (object.identity.objectKind !== "function")
|
|
641
|
+
return;
|
|
642
|
+
if (object.identity.privilege.toUpperCase() !== "EXECUTE")
|
|
643
|
+
return;
|
|
644
|
+
return {
|
|
645
|
+
sql: `alter default privileges for role ${quoteIdent2(object.identity.forRole)} in schema ${quoteIdent2(schema)} revoke execute on functions from public`,
|
|
646
|
+
kind: "grant-default"
|
|
647
|
+
};
|
|
648
|
+
}
|
|
609
649
|
function revokeMatviewWrites(object, after, schema) {
|
|
610
650
|
if (object.identity.privilege.toUpperCase() !== "SELECT")
|
|
611
651
|
return;
|
|
@@ -3082,14 +3122,36 @@ function assertDirectConnection(url, allowPooler) {
|
|
|
3082
3122
|
throw new OkmError2("invalid", `Refusing pooler host ${host}. Apply needs a direct connection.`, { fix: { summary: "Use a direct connection, or pass --allow-pooler." } });
|
|
3083
3123
|
}
|
|
3084
3124
|
}
|
|
3085
|
-
async function assertStableBackend(readPid, allowPooler) {
|
|
3125
|
+
async function assertStableBackend(readPid, allowPooler, options) {
|
|
3086
3126
|
if (allowPooler)
|
|
3087
3127
|
return;
|
|
3088
|
-
const
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3128
|
+
const seen = new Set;
|
|
3129
|
+
let first = "";
|
|
3130
|
+
let release;
|
|
3131
|
+
try {
|
|
3132
|
+
for (let i = 0;i < 8; i += 1) {
|
|
3133
|
+
const pid = await readPid();
|
|
3134
|
+
if (first.length === 0)
|
|
3135
|
+
first = pid;
|
|
3136
|
+
seen.add(pid);
|
|
3137
|
+
if (seen.size >= 2) {
|
|
3138
|
+
const second = [...seen].find((value) => value !== first) ?? pid;
|
|
3139
|
+
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." } });
|
|
3140
|
+
}
|
|
3141
|
+
if (release !== undefined) {
|
|
3142
|
+
await release();
|
|
3143
|
+
release = undefined;
|
|
3144
|
+
}
|
|
3145
|
+
if (i < 7 && options?.holdBackend !== undefined) {
|
|
3146
|
+
release = await options.holdBackend();
|
|
3147
|
+
}
|
|
3148
|
+
}
|
|
3149
|
+
} finally {
|
|
3150
|
+
if (release !== undefined)
|
|
3151
|
+
await release().catch(() => {
|
|
3152
|
+
return;
|
|
3153
|
+
});
|
|
3154
|
+
}
|
|
3093
3155
|
}
|
|
3094
3156
|
function listTargets(config) {
|
|
3095
3157
|
if (config.database !== undefined && config.targets !== undefined) {
|
|
@@ -3179,4 +3241,4 @@ function endpointOf(url) {
|
|
|
3179
3241
|
};
|
|
3180
3242
|
}
|
|
3181
3243
|
|
|
3182
|
-
export { sqlStatements, transactionControl, sqlIdentifiers, 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 };
|
|
3244
|
+
export { driverUrl, sqlStatements, transactionControl, sqlIdentifiers, 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 };
|