(opts: RunOpts)
| 540 | // ─── Postgres path ────────────────────────────────────────────────────────── |
| 541 | |
| 542 | async function runPostgres(opts: RunOpts): Promise<void> { |
| 543 | const { default: pg } = await import("postgres"); |
| 544 | const pgSql = pg(opts.url); |
| 545 | try { |
| 546 | let result: |
| 547 | | { |
| 548 | table: string; |
| 549 | column: string; |
| 550 | applied: number; |
| 551 | total: number; |
| 552 | bytesBefore: number; |
| 553 | bytesAfter: number; |
| 554 | results: EditResult[]; |
| 555 | } |
| 556 | | undefined; |
| 557 | |
| 558 | await pgSql.begin(async (tx: any) => { |
| 559 | // Same temp-view scoping db-exec uses — SELECT and UPDATE both go |
| 560 | // through the scoped view. Keep setup and teardown transaction-local |
| 561 | // so pooled Postgres backends never retain the temp views. |
| 562 | const scoping = await buildScopingPostgres(tx); |
| 563 | try { |
| 564 | for (const stmt of scoping.setup) { |
| 565 | await tx.unsafe(stmt); |
| 566 | } |
| 567 | |
| 568 | const selectSql = `SELECT "${opts.column}" AS __val FROM "${opts.table}" WHERE ${opts.where}`; |
| 569 | const selected: any[] = Array.from(await tx.unsafe(selectSql)); |
| 570 | |
| 571 | if (selected.length === 0) { |
| 572 | fail( |
| 573 | `No rows matched: ${opts.table} WHERE ${opts.where}. ` + |
| 574 | `(In production, data scoping filters results to the current user — the row may exist but be owned by someone else.)`, |
| 575 | ); |
| 576 | } |
| 577 | if (selected.length > 1) { |
| 578 | fail( |
| 579 | `WHERE matched ${selected.length} rows in ${opts.table}. db-patch expects exactly one row — narrow the WHERE clause (usually by primary key).`, |
| 580 | ); |
| 581 | } |
| 582 | |
| 583 | const original = (selected[0].__val ?? "") as string; |
| 584 | if (typeof original !== "string") { |
| 585 | fail( |
| 586 | `Column ${opts.table}.${opts.column} is not a text column (got ${typeof original}).`, |
| 587 | ); |
| 588 | } |
| 589 | |
| 590 | const { content, results, applied, total } = applyEither( |
| 591 | original, |
| 592 | opts, |
| 593 | ); |
| 594 | |
| 595 | if (applied > 0) { |
| 596 | await tx.unsafe( |
| 597 | `UPDATE "${opts.table}" SET "${opts.column}" = $1 WHERE ${opts.where}`, |
| 598 | [content], |
| 599 | ); |
no test coverage detected