pipework 0.251.0 → 0.252.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/CHANGELOG.md +11 -0
- package/dist/core/config/load.d.ts +2 -0
- package/dist/core/config/load.d.ts.map +1 -1
- package/dist/core/config/load.js +1 -0
- package/dist/core/config/load.js.map +1 -1
- package/dist/core/config/namespace.d.ts +1 -0
- package/dist/core/config/namespace.d.ts.map +1 -1
- package/dist/core/config/schema.d.ts +2 -0
- package/dist/core/config/schema.d.ts.map +1 -1
- package/dist/core/config/schema.js +1 -0
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/surface/types.d.ts +1 -0
- package/dist/core/surface/types.d.ts.map +1 -1
- package/dist/data/db/namespace.d.ts +3 -0
- package/dist/data/db/namespace.d.ts.map +1 -1
- package/dist/data/db/namespace.js +3 -0
- package/dist/data/db/namespace.js.map +1 -1
- package/dist/data/db/pool.d.ts.map +1 -1
- package/dist/data/db/pool.js +6 -1
- package/dist/data/db/pool.js.map +1 -1
- package/dist/data/db/query-label.d.ts +5 -0
- package/dist/data/db/query-label.d.ts.map +1 -0
- package/dist/data/db/query-label.js +11 -0
- package/dist/data/db/query-label.js.map +1 -0
- package/dist/data/db/query-watch.d.ts +21 -0
- package/dist/data/db/query-watch.d.ts.map +1 -0
- package/dist/data/db/query-watch.js +90 -0
- package/dist/data/db/query-watch.js.map +1 -0
- package/dist/data/query/postgres-js/driver.js +1 -1
- package/dist/data/query/postgres-js/driver.js.map +1 -1
- package/dist/data/query/postgres-js/session.d.ts +5 -1
- package/dist/data/query/postgres-js/session.d.ts.map +1 -1
- package/dist/data/query/postgres-js/session.js +16 -11
- package/dist/data/query/postgres-js/session.js.map +1 -1
- package/dist/data/query/utils.d.ts +2 -0
- package/dist/data/query/utils.d.ts.map +1 -1
- package/dist/data/query/utils.js.map +1 -1
- package/dist/infra/credits/schema.d.ts +1 -1
- package/dist/test/plugin.d.ts.map +1 -1
- package/dist/test/plugin.js +10 -4
- package/dist/test/plugin.js.map +1 -1
- package/package.json +4 -3
- package/src/async/jobs/abortable-delay.ts +18 -0
- package/src/async/jobs/claims.ts +52 -0
- package/src/async/jobs/cron.ts +179 -0
- package/src/async/jobs/errors.ts +56 -0
- package/src/async/jobs/exclusion.ts +117 -0
- package/src/async/jobs/execute.ts +219 -0
- package/src/async/jobs/index.ts +10 -0
- package/src/async/jobs/listener.ts +135 -0
- package/src/async/jobs/namespace.ts +20 -0
- package/src/async/jobs/queue-door.ts +44 -0
- package/src/async/jobs/queue.ts +1012 -0
- package/src/async/jobs/run-script.ts +67 -0
- package/src/async/jobs/scheduler.ts +218 -0
- package/src/async/jobs/schedules-table.ts +37 -0
- package/src/async/jobs/serialize-error.ts +48 -0
- package/src/async/jobs/table.ts +191 -0
- package/src/async/pipeline/define.ts +53 -0
- package/src/async/pipeline/errors.ts +35 -0
- package/src/async/pipeline/execute.ts +53 -0
- package/src/async/pipeline/index.ts +13 -0
- package/src/async/pipeline/namespace.ts +12 -0
- package/src/async/pipeline/resume.ts +67 -0
- package/src/async/pipeline/run-steps.ts +98 -0
- package/src/async/pipeline/types.ts +57 -0
- package/src/async/state-machine/define.ts +64 -0
- package/src/async/state-machine/errors.ts +29 -0
- package/src/async/state-machine/index.ts +3 -0
- package/src/async/state-machine/namespace.ts +12 -0
- package/src/async/state-machine/transition.ts +90 -0
- package/src/async/state-machine/types.ts +37 -0
- package/src/async/steps/errors.ts +31 -0
- package/src/async/steps/graph.ts +1214 -0
- package/src/async/steps/hash.ts +40 -0
- package/src/async/steps/index.ts +7 -0
- package/src/async/steps/namespace.ts +7 -0
- package/src/async/steps/table.ts +97 -0
- package/src/async/steps/types.ts +318 -0
- package/src/audit/chain/index.ts +9 -0
- package/src/audit/chain/install.ts +113 -0
- package/src/audit/chain/retire.ts +52 -0
- package/src/audit/chain/verify.ts +257 -0
- package/src/audit/query/cursor.ts +37 -0
- package/src/audit/query/index.ts +9 -0
- package/src/audit/query/query.ts +223 -0
- package/src/audit/schema/audit-record-fields.ts +68 -0
- package/src/audit/schema/audit-record.ts +23 -0
- package/src/auth/auth/audit.ts +31 -0
- package/src/auth/auth/chain.ts +84 -0
- package/src/auth/auth/cookie.ts +136 -0
- package/src/auth/auth/email-links.ts +311 -0
- package/src/auth/auth/guard.ts +128 -0
- package/src/auth/auth/index.ts +18 -0
- package/src/auth/auth/multi-org.ts +337 -0
- package/src/auth/auth/namespace.ts +34 -0
- package/src/auth/auth/parse-expiry.ts +13 -0
- package/src/auth/auth/passwords.ts +110 -0
- package/src/auth/auth/schema.ts +110 -0
- package/src/auth/auth/sessions.ts +565 -0
- package/src/auth/auth/sign-in.ts +156 -0
- package/src/auth/auth/signing-keys.ts +69 -0
- package/src/auth/auth/types.ts +26 -0
- package/src/auth/auth/uuid.ts +10 -0
- package/src/auth/rbac/cache.ts +55 -0
- package/src/auth/rbac/check.ts +30 -0
- package/src/auth/rbac/create.ts +35 -0
- package/src/auth/rbac/index.ts +5 -0
- package/src/auth/rbac/middleware.ts +20 -0
- package/src/auth/rbac/namespace.ts +15 -0
- package/src/auth/rbac/resolve.ts +31 -0
- package/src/auth/rbac/schema.sql +31 -0
- package/src/auth/rbac/seed.ts +67 -0
- package/src/auth/rbac/tables.ts +32 -0
- package/src/auth/rbac/types.ts +65 -0
- package/src/auth/tenant/extract.ts +33 -0
- package/src/auth/tenant/in-tenant.ts +49 -0
- package/src/auth/tenant/index.ts +7 -0
- package/src/auth/tenant/namespace.ts +23 -0
- package/src/auth/tenant/propagate.ts +20 -0
- package/src/auth/tenant/rls.ts +120 -0
- package/src/auth/tenant/scope.ts +33 -0
- package/src/auth/tenant/scoped-db.ts +581 -0
- package/src/auth/tenant/types.ts +14 -0
- package/src/auth/tenant/validate.ts +27 -0
- package/src/cli/commands/add.ts +158 -0
- package/src/cli/commands/alerts.ts +98 -0
- package/src/cli/commands/check.ts +1418 -0
- package/src/cli/commands/codemod.ts +78 -0
- package/src/cli/commands/db.ts +356 -0
- package/src/cli/commands/dev.ts +101 -0
- package/src/cli/commands/generate.ts +75 -0
- package/src/cli/commands/init.ts +36 -0
- package/src/cli/commands/install.ts +28 -0
- package/src/cli/commands/lint.ts +62 -0
- package/src/cli/commands/migrate.ts +132 -0
- package/src/cli/commands/openapi.ts +63 -0
- package/src/cli/commands/remove.ts +72 -0
- package/src/cli/commands/run.ts +50 -0
- package/src/cli/commands/scope.ts +54 -0
- package/src/cli/commands/serve.ts +68 -0
- package/src/cli/commands/shard.ts +31 -0
- package/src/cli/commands/shared/config-pipework-version.ts +28 -0
- package/src/cli/commands/shared/find-config.ts +22 -0
- package/src/cli/commands/shared/load-config.ts +35 -0
- package/src/cli/commands/shared/pnpm-install.ts +95 -0
- package/src/cli/commands/shared/report-removed.ts +8 -0
- package/src/cli/commands/sync.ts +32 -0
- package/src/cli/commands/test.ts +307 -0
- package/src/cli/commands/upgrade.ts +48 -0
- package/src/cli/dev-bootstrap.ts +24 -0
- package/src/cli/errors.ts +10 -0
- package/src/cli/index.ts +303 -0
- package/src/cli/output.ts +17 -0
- package/src/cli/port.ts +15 -0
- package/src/cli/surface-kinds.ts +20 -0
- package/src/client/index.ts +4 -0
- package/src/client/poll.ts +130 -0
- package/src/client/report-fault.ts +119 -0
- package/src/core/config/app-pool.ts +19 -0
- package/src/core/config/aws-env.ts +29 -0
- package/src/core/config/base-dir.ts +24 -0
- package/src/core/config/discover.ts +64 -0
- package/src/core/config/env-resolve.ts +87 -0
- package/src/core/config/env-types.ts +116 -0
- package/src/core/config/env.ts +55 -0
- package/src/core/config/errors.ts +9 -0
- package/src/core/config/import-config.ts +40 -0
- package/src/core/config/index.ts +8 -0
- package/src/core/config/load.ts +417 -0
- package/src/core/config/namespace.ts +14 -0
- package/src/core/config/resolve.ts +195 -0
- package/src/core/config/schema.ts +488 -0
- package/src/core/config/statement-observer.ts +48 -0
- package/src/core/config/ts-inline-hooks.ts +75 -0
- package/src/core/config/ts-register.ts +4 -0
- package/src/core/config/ts-resolve-hooks.ts +101 -0
- package/src/core/errors/base.ts +49 -0
- package/src/core/errors/http.ts +146 -0
- package/src/core/errors/index.ts +2 -0
- package/src/core/errors/namespace.ts +31 -0
- package/src/core/lifecycle/drain.ts +33 -0
- package/src/core/lifecycle/health.ts +72 -0
- package/src/core/lifecycle/index.ts +4 -0
- package/src/core/lifecycle/namespace.ts +7 -0
- package/src/core/lifecycle/startup.ts +102 -0
- package/src/core/lifecycle/types.ts +36 -0
- package/src/core/logging/context.ts +60 -0
- package/src/core/logging/error-chain.ts +68 -0
- package/src/core/logging/index.ts +5 -0
- package/src/core/logging/logger.ts +65 -0
- package/src/core/logging/namespace.ts +16 -0
- package/src/core/logging/proxy.ts +22 -0
- package/src/core/logging/redact-match.ts +24 -0
- package/src/core/logging/types.ts +47 -0
- package/src/core/pipework.ts +566 -0
- package/src/core/runtime/broker.ts +69 -0
- package/src/core/surface/http.ts +133 -0
- package/src/core/surface/index.ts +11 -0
- package/src/core/surface/job-timeout.ts +40 -0
- package/src/core/surface/namespace.ts +13 -0
- package/src/core/surface/script.ts +33 -0
- package/src/core/surface/start.ts +142 -0
- package/src/core/surface/types.ts +225 -0
- package/src/core/surface/validate.ts +88 -0
- package/src/core/surface/worker.ts +528 -0
- package/src/core/validation/connectivity.ts +49 -0
- package/src/core/validation/domain.ts +99 -0
- package/src/core/validation/index.ts +5 -0
- package/src/core/validation/queue-tables.ts +312 -0
- package/src/core/validation/rls-role.ts +138 -0
- package/src/core/validation/startup.ts +51 -0
- package/src/core/validation/surfaces.ts +65 -0
- package/src/core/validation/types.ts +25 -0
- package/src/data/db/advisory-lock.ts +55 -0
- package/src/data/db/aggregates.ts +172 -0
- package/src/data/db/array-elements.ts +111 -0
- package/src/data/db/bulk-set.ts +53 -0
- package/src/data/db/col/index.ts +14 -0
- package/src/data/db/composed.ts +53 -0
- package/src/data/db/db.ts +276 -0
- package/src/data/db/excluded.ts +16 -0
- package/src/data/db/expressions.ts +473 -0
- package/src/data/db/filter.ts +90 -0
- package/src/data/db/function-source.ts +114 -0
- package/src/data/db/identifiers.ts +16 -0
- package/src/data/db/idx/index.ts +9 -0
- package/src/data/db/in-list.ts +49 -0
- package/src/data/db/index.ts +9 -0
- package/src/data/db/insert-split.ts +137 -0
- package/src/data/db/instrumentation.ts +82 -0
- package/src/data/db/maintenance.ts +28 -0
- package/src/data/db/namespace.ts +107 -0
- package/src/data/db/owner-labels.ts +9 -0
- package/src/data/db/pool.ts +187 -0
- package/src/data/db/query-label.ts +13 -0
- package/src/data/db/query-watch.ts +116 -0
- package/src/data/db/raw-client-warning.ts +37 -0
- package/src/data/db/readonly.ts +23 -0
- package/src/data/db/reservation.ts +151 -0
- package/src/data/db/schema-features.ts +14 -0
- package/src/data/db/serializable.ts +83 -0
- package/src/data/db/sql.ts +1 -0
- package/src/data/db/timeouts.ts +76 -0
- package/src/data/db/types.ts +18 -0
- package/src/data/db/unbound.ts +33 -0
- package/src/data/db/values.ts +29 -0
- package/src/data/domain/brand.ts +15 -0
- package/src/data/domain/build-factory.ts +193 -0
- package/src/data/domain/build-schema.ts +161 -0
- package/src/data/domain/build-table.ts +355 -0
- package/src/data/domain/define.ts +239 -0
- package/src/data/domain/external-registry.ts +34 -0
- package/src/data/domain/external.ts +77 -0
- package/src/data/domain/field.ts +279 -0
- package/src/data/domain/index.ts +13 -0
- package/src/data/domain/naming.ts +6 -0
- package/src/data/domain/projections.ts +57 -0
- package/src/data/domain/trace-safe-projection.ts +31 -0
- package/src/data/domain/traits.ts +108 -0
- package/src/data/domain/types.ts +289 -0
- package/src/data/jsonb/composed.ts +18 -0
- package/src/data/jsonb/index.ts +1 -0
- package/src/data/jsonb/namespace.ts +30 -0
- package/src/data/jsonb/query.ts +86 -0
- package/src/data/migrate/append-only-generate.ts +47 -0
- package/src/data/migrate/applied.ts +47 -0
- package/src/data/migrate/apply.ts +309 -0
- package/src/data/migrate/check.ts +119 -0
- package/src/data/migrate/diff.ts +357 -0
- package/src/data/migrate/door-generate.ts +130 -0
- package/src/data/migrate/framework-rls.ts +58 -0
- package/src/data/migrate/generate.ts +201 -0
- package/src/data/migrate/history.ts +215 -0
- package/src/data/migrate/index.ts +9 -0
- package/src/data/migrate/internal-definitions.ts +175 -0
- package/src/data/migrate/post-process.ts +73 -0
- package/src/data/migrate/pragma.ts +73 -0
- package/src/data/migrate/provision-framework.ts +147 -0
- package/src/data/migrate/rls-generate.ts +61 -0
- package/src/data/migrate/run.ts +124 -0
- package/src/data/migrate/safety.ts +167 -0
- package/src/data/migrate/snapshot.ts +368 -0
- package/src/data/migrate/sql-emit.ts +392 -0
- package/src/data/migrate/statements.ts +312 -0
- package/src/data/query/alias.ts +126 -0
- package/src/data/query/batch.ts +8 -0
- package/src/data/query/cache/core/cache.ts +78 -0
- package/src/data/query/cache/core/index.ts +1 -0
- package/src/data/query/cache/core/types.ts +29 -0
- package/src/data/query/cache/readme.md +230 -0
- package/src/data/query/casing.ts +76 -0
- package/src/data/query/collation.ts +11 -0
- package/src/data/query/column-builder.ts +370 -0
- package/src/data/query/column.ts +150 -0
- package/src/data/query/entity.ts +42 -0
- package/src/data/query/errors.ts +33 -0
- package/src/data/query/index.ts +14 -0
- package/src/data/query/logger.ts +47 -0
- package/src/data/query/operations.ts +45 -0
- package/src/data/query/pg-core/alias.ts +12 -0
- package/src/data/query/pg-core/checks.ts +32 -0
- package/src/data/query/pg-core/columns/all.ts +71 -0
- package/src/data/query/pg-core/columns/bigint.ts +110 -0
- package/src/data/query/pg-core/columns/bigserial.ts +128 -0
- package/src/data/query/pg-core/columns/boolean.ts +43 -0
- package/src/data/query/pg-core/columns/char.ts +85 -0
- package/src/data/query/pg-core/columns/cidr.ts +43 -0
- package/src/data/query/pg-core/columns/common.ts +366 -0
- package/src/data/query/pg-core/columns/custom.ts +232 -0
- package/src/data/query/pg-core/columns/date.common.ts +15 -0
- package/src/data/query/pg-core/columns/date.ts +112 -0
- package/src/data/query/pg-core/columns/double-precision.ts +55 -0
- package/src/data/query/pg-core/columns/enum.ts +201 -0
- package/src/data/query/pg-core/columns/index.ts +35 -0
- package/src/data/query/pg-core/columns/inet.ts +43 -0
- package/src/data/query/pg-core/columns/int.common.ts +57 -0
- package/src/data/query/pg-core/columns/integer.ts +53 -0
- package/src/data/query/pg-core/columns/interval.ts +83 -0
- package/src/data/query/pg-core/columns/json.ts +58 -0
- package/src/data/query/pg-core/columns/jsonb.ts +56 -0
- package/src/data/query/pg-core/columns/line.ts +117 -0
- package/src/data/query/pg-core/columns/macaddr.ts +43 -0
- package/src/data/query/pg-core/columns/macaddr8.ts +43 -0
- package/src/data/query/pg-core/columns/numeric.ts +227 -0
- package/src/data/query/pg-core/columns/point.ts +127 -0
- package/src/data/query/pg-core/columns/postgis_extension/geometry.ts +119 -0
- package/src/data/query/pg-core/columns/postgis_extension/utils.ts +47 -0
- package/src/data/query/pg-core/columns/real.ts +58 -0
- package/src/data/query/pg-core/columns/serial.ts +55 -0
- package/src/data/query/pg-core/columns/smallint.ts +53 -0
- package/src/data/query/pg-core/columns/smallserial.ts +60 -0
- package/src/data/query/pg-core/columns/text.ts +67 -0
- package/src/data/query/pg-core/columns/time.ts +72 -0
- package/src/data/query/pg-core/columns/timestamp.ts +163 -0
- package/src/data/query/pg-core/columns/uuid.ts +51 -0
- package/src/data/query/pg-core/columns/varchar.ts +89 -0
- package/src/data/query/pg-core/columns/vector_extension/bit.ts +68 -0
- package/src/data/query/pg-core/columns/vector_extension/halfvec.ts +80 -0
- package/src/data/query/pg-core/columns/vector_extension/sparsevec.ts +67 -0
- package/src/data/query/pg-core/columns/vector_extension/vector.ts +80 -0
- package/src/data/query/pg-core/db.ts +674 -0
- package/src/data/query/pg-core/dialect.ts +1404 -0
- package/src/data/query/pg-core/expressions.ts +25 -0
- package/src/data/query/pg-core/foreign-keys.ts +130 -0
- package/src/data/query/pg-core/index.ts +21 -0
- package/src/data/query/pg-core/indexes.ts +276 -0
- package/src/data/query/pg-core/policies.ts +56 -0
- package/src/data/query/pg-core/primary-keys.ts +62 -0
- package/src/data/query/pg-core/query-builders/count.ts +86 -0
- package/src/data/query/pg-core/query-builders/delete.ts +293 -0
- package/src/data/query/pg-core/query-builders/index.ts +7 -0
- package/src/data/query/pg-core/query-builders/insert.ts +452 -0
- package/src/data/query/pg-core/query-builders/query-builder.ts +123 -0
- package/src/data/query/pg-core/query-builders/query.ts +157 -0
- package/src/data/query/pg-core/query-builders/raw.ts +49 -0
- package/src/data/query/pg-core/query-builders/refresh-materialized-view.ts +108 -0
- package/src/data/query/pg-core/query-builders/select.ts +1346 -0
- package/src/data/query/pg-core/query-builders/select.types.ts +437 -0
- package/src/data/query/pg-core/query-builders/update.ts +626 -0
- package/src/data/query/pg-core/roles.ts +41 -0
- package/src/data/query/pg-core/schema.ts +72 -0
- package/src/data/query/pg-core/sequence.ts +41 -0
- package/src/data/query/pg-core/session.ts +292 -0
- package/src/data/query/pg-core/subquery.ts +46 -0
- package/src/data/query/pg-core/table.ts +252 -0
- package/src/data/query/pg-core/unique-constraint.ts +73 -0
- package/src/data/query/pg-core/utils/array.ts +95 -0
- package/src/data/query/pg-core/utils/index.ts +1 -0
- package/src/data/query/pg-core/utils.ts +104 -0
- package/src/data/query/pg-core/view-base.ts +14 -0
- package/src/data/query/pg-core/view-common.ts +1 -0
- package/src/data/query/pg-core/view.ts +435 -0
- package/src/data/query/pg-core/with-builder.ts +41 -0
- package/src/data/query/postgres-js/README.md +46 -0
- package/src/data/query/postgres-js/array-parser.ts +45 -0
- package/src/data/query/postgres-js/driver-error-boundary.ts +149 -0
- package/src/data/query/postgres-js/driver.ts +163 -0
- package/src/data/query/postgres-js/index.ts +2 -0
- package/src/data/query/postgres-js/session.ts +234 -0
- package/src/data/query/primary-key.ts +11 -0
- package/src/data/query/query-builders/query-builder.ts +19 -0
- package/src/data/query/query-builders/select.types.ts +175 -0
- package/src/data/query/query-promise.ts +35 -0
- package/src/data/query/relations.ts +725 -0
- package/src/data/query/runnable-query.ts +12 -0
- package/src/data/query/selection-proxy.ts +121 -0
- package/src/data/query/session.ts +8 -0
- package/src/data/query/sql/expressions/conditions.ts +741 -0
- package/src/data/query/sql/expressions/index.ts +2 -0
- package/src/data/query/sql/expressions/select.ts +45 -0
- package/src/data/query/sql/functions/aggregate.ts +129 -0
- package/src/data/query/sql/functions/index.ts +2 -0
- package/src/data/query/sql/functions/vector.ts +178 -0
- package/src/data/query/sql/index.ts +3 -0
- package/src/data/query/sql/sql.ts +794 -0
- package/src/data/query/subquery.ts +53 -0
- package/src/data/query/table.ts +208 -0
- package/src/data/query/table.utils.ts +2 -0
- package/src/data/query/tracing-utils.ts +3 -0
- package/src/data/query/tracing.ts +11 -0
- package/src/data/query/utils.ts +357 -0
- package/src/data/query/version.ts +4 -0
- package/src/data/query/view-common.ts +1 -0
- package/src/data/schema/branded.ts +13 -0
- package/src/data/schema/generated/coerce.ts +5 -0
- package/src/data/schema/generated/combinator.ts +6 -0
- package/src/data/schema/generated/encoding.ts +5 -0
- package/src/data/schema/generated/error.ts +6 -0
- package/src/data/schema/generated/format.ts +5 -0
- package/src/data/schema/generated/type-constructor.ts +10 -0
- package/src/data/schema/generated/type.ts +6 -0
- package/src/data/schema/index.ts +6 -0
- package/src/data/schema/json-schema.ts +33 -0
- package/src/data/schema/namespace.ts +55 -0
- package/src/data/schema/parse.ts +39 -0
- package/src/data/schema/types.ts +39 -0
- package/src/data/temporal/append-only.ts +134 -0
- package/src/data/temporal/close.ts +27 -0
- package/src/data/temporal/columns.ts +10 -0
- package/src/data/temporal/definition-queries.ts +70 -0
- package/src/data/temporal/errors.ts +10 -0
- package/src/data/temporal/get-current.ts +22 -0
- package/src/data/temporal/index.ts +9 -0
- package/src/data/temporal/namespace.ts +36 -0
- package/src/data/temporal/query.ts +25 -0
- package/src/data/temporal/resolve.ts +17 -0
- package/src/data/temporal/revise.ts +67 -0
- package/src/data/temporal/types.ts +22 -0
- package/src/data/valve/artifact.ts +33 -0
- package/src/data/valve/contract-diff.ts +21 -0
- package/src/data/valve/cross.ts +57 -0
- package/src/data/valve/crossing-ledger.ts +50 -0
- package/src/data/valve/decision.ts +44 -0
- package/src/data/valve/durable-crossing.ts +52 -0
- package/src/data/valve/execute-crossing.ts +77 -0
- package/src/data/valve/faucet.ts +56 -0
- package/src/data/valve/gate.ts +114 -0
- package/src/data/valve/graph.ts +77 -0
- package/src/data/valve/index.ts +53 -0
- package/src/data/valve/namespace.ts +57 -0
- package/src/data/valve/registry.ts +50 -0
- package/src/data/valve/schema/valve-crossing-fields.ts +23 -0
- package/src/data/valve/schema/valve-crossing.ts +9 -0
- package/src/data/valve/supply.ts +154 -0
- package/src/data/valve/types.ts +143 -0
- package/src/data/vector/columns.ts +27 -0
- package/src/data/vector/index.ts +4 -0
- package/src/data/vector/namespace.ts +33 -0
- package/src/data/vector/query.ts +85 -0
- package/src/data/vector/validate.ts +16 -0
- package/src/data/write/audit/actor.ts +15 -0
- package/src/data/write/audit/audit-columns.ts +18 -0
- package/src/data/write/audit/audit-select.ts +111 -0
- package/src/data/write/audit/bound-transaction.ts +14 -0
- package/src/data/write/audit/cte-compile.ts +32 -0
- package/src/data/write/audit/decode-returning.ts +47 -0
- package/src/data/write/audit/final-select.ts +43 -0
- package/src/data/write/audit/infra-audit.ts +36 -0
- package/src/data/write/audit/primary-key.ts +65 -0
- package/src/data/write/audit/projection.ts +41 -0
- package/src/data/write/audit/read-touches.ts +86 -0
- package/src/data/write/audit/touch-match.ts +78 -0
- package/src/data/write/audit/verb-delete.ts +62 -0
- package/src/data/write/audit/verb-insert.ts +36 -0
- package/src/data/write/audit/verb-select.ts +47 -0
- package/src/data/write/audit/verb-update.ts +60 -0
- package/src/data/write/audit/verb-upsert.ts +104 -0
- package/src/data/write/conflict.ts +43 -0
- package/src/data/write/delete.ts +38 -0
- package/src/data/write/execute.ts +243 -0
- package/src/data/write/index.ts +11 -0
- package/src/data/write/insert.ts +63 -0
- package/src/data/write/state.ts +43 -0
- package/src/data/write/update.ts +54 -0
- package/src/data/write/upsert.ts +58 -0
- package/src/data/write/values.ts +19 -0
- package/src/index.ts +230 -0
- package/src/infra/access-links/code.ts +70 -0
- package/src/infra/access-links/handle.ts +892 -0
- package/src/infra/access-links/index.ts +9 -0
- package/src/infra/access-links/namespace.ts +30 -0
- package/src/infra/access-links/person-check.ts +177 -0
- package/src/infra/access-links/schema.ts +71 -0
- package/src/infra/alerts/async-sources.ts +231 -0
- package/src/infra/alerts/audit.ts +36 -0
- package/src/infra/alerts/beat.ts +123 -0
- package/src/infra/alerts/client-faults.ts +111 -0
- package/src/infra/alerts/faults.ts +156 -0
- package/src/infra/alerts/handle.ts +778 -0
- package/src/infra/alerts/http.ts +192 -0
- package/src/infra/alerts/index.ts +12 -0
- package/src/infra/alerts/infra-sources.ts +180 -0
- package/src/infra/alerts/namespace.ts +81 -0
- package/src/infra/alerts/redact.ts +21 -0
- package/src/infra/alerts/request-faults.ts +41 -0
- package/src/infra/alerts/schema.ts +124 -0
- package/src/infra/alerts/sinks.ts +125 -0
- package/src/infra/alerts/source-options.ts +26 -0
- package/src/infra/alerts/sources.ts +503 -0
- package/src/infra/alerts/subsystem-sources.ts +137 -0
- package/src/infra/audit/emit.ts +87 -0
- package/src/infra/audit/index.ts +2 -0
- package/src/infra/audit/namespace.ts +22 -0
- package/src/infra/audit/schema.sql +35 -0
- package/src/infra/audit/types.ts +18 -0
- package/src/infra/aws/credentials.ts +147 -0
- package/src/infra/aws/sigv4.ts +140 -0
- package/src/infra/cache/create.ts +58 -0
- package/src/infra/cache/entry.ts +81 -0
- package/src/infra/cache/index.ts +4 -0
- package/src/infra/cache/namespace.ts +10 -0
- package/src/infra/cache/tenant.ts +93 -0
- package/src/infra/cache/types.ts +24 -0
- package/src/infra/crawl/audit.ts +35 -0
- package/src/infra/crawl/errors.ts +25 -0
- package/src/infra/crawl/handle.ts +693 -0
- package/src/infra/crawl/index.ts +9 -0
- package/src/infra/crawl/links.ts +160 -0
- package/src/infra/crawl/namespace.ts +36 -0
- package/src/infra/crawl/robots.ts +100 -0
- package/src/infra/crawl/schema.ts +89 -0
- package/src/infra/credit-line/handle.ts +883 -0
- package/src/infra/credit-line/index.ts +6 -0
- package/src/infra/credit-line/namespace.ts +7 -0
- package/src/infra/credit-line/schema.ts +96 -0
- package/src/infra/credits/handle.ts +706 -0
- package/src/infra/credits/index.ts +5 -0
- package/src/infra/credits/namespace.ts +7 -0
- package/src/infra/credits/schema.ts +83 -0
- package/src/infra/email/audit.ts +36 -0
- package/src/infra/email/inbound.ts +622 -0
- package/src/infra/email/index.ts +11 -0
- package/src/infra/email/mail.ts +517 -0
- package/src/infra/email/mime.ts +447 -0
- package/src/infra/email/namespace.ts +22 -0
- package/src/infra/email/providers.ts +111 -0
- package/src/infra/email/schema.ts +96 -0
- package/src/infra/email/sns.ts +200 -0
- package/src/infra/email/unsubscribe.ts +44 -0
- package/src/infra/fetch/address.ts +88 -0
- package/src/infra/fetch/connect-retry.ts +87 -0
- package/src/infra/fetch/handle.ts +329 -0
- package/src/infra/fetch/index.ts +7 -0
- package/src/infra/fetch/namespace.ts +70 -0
- package/src/infra/idempotency/index.ts +3 -0
- package/src/infra/idempotency/middleware.ts +87 -0
- package/src/infra/idempotency/store.ts +92 -0
- package/src/infra/idempotency/types.ts +30 -0
- package/src/infra/join-requests/handle.ts +312 -0
- package/src/infra/join-requests/index.ts +2 -0
- package/src/infra/join-requests/namespace.ts +7 -0
- package/src/infra/join-requests/schema.ts +33 -0
- package/src/infra/outbox/core.ts +165 -0
- package/src/infra/quota/errors.ts +32 -0
- package/src/infra/quota/handle.ts +215 -0
- package/src/infra/quota/index.ts +5 -0
- package/src/infra/quota/namespace.ts +7 -0
- package/src/infra/quota/schema.ts +15 -0
- package/src/infra/storage/backend.ts +34 -0
- package/src/infra/storage/handle.ts +513 -0
- package/src/infra/storage/index.ts +9 -0
- package/src/infra/storage/local.ts +104 -0
- package/src/infra/storage/namespace.ts +13 -0
- package/src/infra/storage/s3.ts +257 -0
- package/src/infra/storage/schema.ts +24 -0
- package/src/infra/support/handle.ts +854 -0
- package/src/infra/support/http.ts +229 -0
- package/src/infra/support/index.ts +7 -0
- package/src/infra/support/namespace.ts +12 -0
- package/src/infra/support/schema.ts +96 -0
- package/src/infra/webhook/inbound.ts +45 -0
- package/src/infra/webhook/index.ts +5 -0
- package/src/infra/webhook/namespace.ts +24 -0
- package/src/infra/webhook/outbound.ts +315 -0
- package/src/infra/webhook/sign.ts +32 -0
- package/src/infra/webhook/verify.ts +82 -0
- package/src/invariants/assert.ts +26 -0
- package/src/invariants/index.ts +1 -0
- package/src/lint/config.ts +56 -0
- package/src/lint/doctrine.ts +163 -0
- package/src/lint/include.ts +97 -0
- package/src/lint/index.ts +3 -0
- package/src/lint/preset.ts +10 -0
- package/src/lint/resolver.ts +218 -0
- package/src/lint/rules/no-reexports.ts +46 -0
- package/src/request/behavior/audited.ts +85 -0
- package/src/request/behavior/cached.ts +62 -0
- package/src/request/behavior/compose.ts +28 -0
- package/src/request/behavior/index.ts +5 -0
- package/src/request/behavior/namespace.ts +16 -0
- package/src/request/behavior/types.ts +26 -0
- package/src/request/behavior/versioned.ts +54 -0
- package/src/request/context/create.ts +103 -0
- package/src/request/context/guard.ts +49 -0
- package/src/request/context/index.ts +4 -0
- package/src/request/context/run-in-transaction.ts +200 -0
- package/src/request/context/store.ts +28 -0
- package/src/request/context/types.ts +50 -0
- package/src/request/di/builder.ts +535 -0
- package/src/request/di/index.ts +8 -0
- package/src/request/di/resolve.ts +271 -0
- package/src/request/di/types.ts +262 -0
- package/src/request/fitting/namespace.ts +7 -0
- package/src/request/fixture/batch.ts +71 -0
- package/src/request/fixture/builder.ts +26 -0
- package/src/request/fixture/errors.ts +10 -0
- package/src/request/fixture/index.ts +8 -0
- package/src/request/fixture/namespace.ts +23 -0
- package/src/request/fixture/paginate.ts +44 -0
- package/src/request/fixture/to-handlers.ts +260 -0
- package/src/request/fixture/types.ts +79 -0
- package/src/request/flow/act-as.ts +78 -0
- package/src/request/flow/namespace.ts +17 -0
- package/src/request/http/error-step.ts +42 -0
- package/src/request/http/file.ts +141 -0
- package/src/request/http/index.ts +4 -0
- package/src/request/http/middleware.ts +141 -0
- package/src/request/http/namespace.ts +5 -0
- package/src/request/http/rate-limit-classes.ts +170 -0
- package/src/request/http/response.ts +82 -0
- package/src/request/http/server.ts +916 -0
- package/src/request/http/sniff.ts +261 -0
- package/src/request/http/types.ts +250 -0
- package/src/request/http/upload.ts +284 -0
- package/src/request/internal/text.ts +6 -0
- package/src/request/openapi/generate.ts +205 -0
- package/src/request/openapi/index.ts +2 -0
- package/src/request/openapi/namespace.ts +7 -0
- package/src/test/auth.ts +13 -0
- package/src/test/auto-setup.ts +45 -0
- package/src/test/context.ts +91 -0
- package/src/test/crawl-fixture.ts +157 -0
- package/src/test/discover-fork.ts +17 -0
- package/src/test/fingerprint.ts +135 -0
- package/src/test/globals.d.ts +7 -0
- package/src/test/index.ts +19 -0
- package/src/test/namespace.ts +87 -0
- package/src/test/plugin.ts +189 -0
- package/src/test/progress.ts +185 -0
- package/src/test/reap-after-run.ts +28 -0
- package/src/test/reap.ts +286 -0
- package/src/test/reset.ts +253 -0
- package/src/test/savepoints.ts +172 -0
- package/src/test/setup.ts +1422 -0
- package/src/test/system-channel.ts +50 -0
- package/src/test/vitest-plugin.ts +142 -0
- package/src/test/vitest-workspace.ts +507 -0
- package/src/test/vitest.ts +333 -0
- package/src/test/watch.ts +105 -0
- package/src/trace/capture/bounded-serializer.ts +146 -0
- package/src/trace/cardinality/analyze.ts +173 -0
- package/src/trace/cardinality/branch-context.ts +214 -0
- package/src/trace/cardinality/check.ts +33 -0
- package/src/trace/cardinality/classify-edge.ts +64 -0
- package/src/trace/cardinality/structural-max.ts +107 -0
- package/src/trace/check/coverage-check.ts +188 -0
- package/src/trace/check/flow-check.ts +103 -0
- package/src/trace/check/flow-step-site.ts +234 -0
- package/src/trace/check/io-pattern.ts +116 -0
- package/src/trace/codemod/add-cardinality.ts +152 -0
- package/src/trace/codemod/run.ts +80 -0
- package/src/trace/entry/child-trace.ts +21 -0
- package/src/trace/entry/close-trace.ts +87 -0
- package/src/trace/entry/finalize-trace.ts +28 -0
- package/src/trace/entry/open-trace.ts +138 -0
- package/src/trace/entry/parent-trace.ts +20 -0
- package/src/trace/entry/parse-traceparent.ts +39 -0
- package/src/trace/entry/process-start.ts +38 -0
- package/src/trace/entry/trace-context.ts +26 -0
- package/src/trace/entry/tracing-enabled.ts +10 -0
- package/src/trace/external/record.ts +195 -0
- package/src/trace/external/report.ts +66 -0
- package/src/trace/namespace.ts +19 -0
- package/src/trace/partition/day.ts +47 -0
- package/src/trace/partition/index.ts +14 -0
- package/src/trace/partition/partition-name.ts +23 -0
- package/src/trace/partition/plan.ts +82 -0
- package/src/trace/partition/schedule.ts +26 -0
- package/src/trace/partition/startup-hook.ts +39 -0
- package/src/trace/partition/sync.ts +154 -0
- package/src/trace/partition/tables.ts +53 -0
- package/src/trace/read/tree.ts +83 -0
- package/src/trace/retain/copy-on-error.ts +76 -0
- package/src/trace/runtime/expire-stale-traces.ts +54 -0
- package/src/trace/runtime/flow-step.ts +147 -0
- package/src/trace/runtime/flush-steps.ts +31 -0
- package/src/trace/runtime/pending-writes.ts +54 -0
- package/src/trace/runtime/step-buffer.ts +298 -0
- package/src/trace/runtime/step-error.ts +21 -0
- package/src/trace/runtime/step-meta.ts +55 -0
- package/src/trace/runtime/thenable.ts +12 -0
- package/src/trace/runtime/truncate-trace.ts +62 -0
- package/src/trace/schema/trace-fields.ts +52 -0
- package/src/trace/schema/trace-retained.ts +22 -0
- package/src/trace/schema/trace-step-fields.ts +44 -0
- package/src/trace/schema/trace-step-retained.ts +25 -0
- package/src/trace/schema/trace-step.ts +18 -0
- package/src/trace/schema/trace.ts +12 -0
- package/src/trace/transformer/apply.ts +79 -0
- package/src/trace/transformer/build-meta-literal.ts +30 -0
- package/src/trace/transformer/find-flow-step-sites.ts +85 -0
- package/src/trace/transformer/inject-flow-step-import.ts +39 -0
- package/src/trace/transformer/transform-error.ts +14 -0
- package/src/trace/transformer/transform-flow-steps.ts +123 -0
- package/src/trace/transformer/wrap-function-declaration.ts +62 -0
- package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
- package/src/workspace/affected.ts +110 -0
- package/src/workspace/changed-files.ts +163 -0
- package/src/workspace/check-deps.ts +213 -0
- package/src/workspace/checksum.ts +20 -0
- package/src/workspace/dependency-graph.ts +67 -0
- package/src/workspace/detect-pipework-copies.ts +93 -0
- package/src/workspace/discover.ts +206 -0
- package/src/workspace/generate.ts +401 -0
- package/src/workspace/index.ts +7 -0
- package/src/workspace/module-imports-pipework.ts +39 -0
- package/src/workspace/module-package-name.ts +12 -0
- package/src/workspace/scrub-git-env.ts +23 -0
- package/src/workspace/source-paths.ts +36 -0
- package/src/workspace/sync.ts +182 -0
- package/dist/REFERENCE.md +0 -1477
- package/dist/infra/access-links/rate-limit.d.ts +0 -10
- package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
- package/dist/infra/access-links/rate-limit.js +0 -28
- package/dist/infra/access-links/rate-limit.js.map +0 -1
- package/dist/infra/email/sigv4.d.ts +0 -24
- package/dist/infra/email/sigv4.d.ts.map +0 -1
- package/dist/infra/email/sigv4.js +0 -55
- package/dist/infra/email/sigv4.js.map +0 -1
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import postgres from 'postgres'
|
|
2
|
+
|
|
3
|
+
// A worker keeps each per-file database across files and returns it to the
|
|
4
|
+
// state it had right after the clone. The baseline is captured once, before
|
|
5
|
+
// any file runs on it; a reset either restores that state exactly or reports
|
|
6
|
+
// that it cannot, and the caller then takes a fresh clone instead.
|
|
7
|
+
|
|
8
|
+
export const TEST_RESET_LOG_PREFIX = 'pipework:test-reset'
|
|
9
|
+
|
|
10
|
+
export type FreshCloneReason = 'digest' | 'prepared-xact' | 'matview' | 'rls' | 'error'
|
|
11
|
+
|
|
12
|
+
export interface Baseline {
|
|
13
|
+
digest: string
|
|
14
|
+
/** Main-fork bytes of every user table, partition and matview. */
|
|
15
|
+
sizes: Map<string, number>
|
|
16
|
+
/** relfilenode of every matview: a refresh rewrites it. */
|
|
17
|
+
matviews: Map<string, string>
|
|
18
|
+
/** Tables that already had storage at baseline: always cleared and reloaded. */
|
|
19
|
+
stored: Set<string>
|
|
20
|
+
/** COPY text of every table that had rows. */
|
|
21
|
+
rows: Map<string, Buffer>
|
|
22
|
+
/**
|
|
23
|
+
* Tables with storage whose rows row-level security hid from the connection
|
|
24
|
+
* that read the baseline (it neither bypasses RLS nor owns them): what they
|
|
25
|
+
* held is unknown, so a reset cannot restore them.
|
|
26
|
+
*/
|
|
27
|
+
hidden: string[]
|
|
28
|
+
/** last_value (null = not called) and start_value of every sequence. */
|
|
29
|
+
sequences: Map<string, { last: string | null; start: string }>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type ResetOutcome =
|
|
33
|
+
| { ok: true; cleared: number; reloaded: number }
|
|
34
|
+
| { ok: false; reason: FreshCloneReason }
|
|
35
|
+
|
|
36
|
+
const USER_NAMESPACE = `n.nspname !~ '^pg_(toast_)?temp_' AND n.nspname <> 'information_schema' AND n.nspname <> 'pg_catalog'`
|
|
37
|
+
|
|
38
|
+
// One md5 over the user-relevant catalogs. Storage fields (relfilenode,
|
|
39
|
+
// relpages, reltuples, relfrozenxid, ...) are left out: they move without any
|
|
40
|
+
// schema change. Objects below oid 16384 are the cluster's own.
|
|
41
|
+
const DIGEST_QUERY = `
|
|
42
|
+
SELECT md5(concat_ws('|',
|
|
43
|
+
(SELECT md5(coalesce(string_agg(row(n.oid, n.nspname, n.nspowner, n.nspacl)::text, E'\\n' ORDER BY n.oid), ''))
|
|
44
|
+
FROM pg_namespace n WHERE (n.oid >= 16384 OR n.nspname = 'public') AND ${USER_NAMESPACE}),
|
|
45
|
+
(SELECT md5(coalesce(string_agg(row(c.oid, c.relname, c.relnamespace, c.relkind, c.relpersistence, c.relowner, c.relacl, c.relrowsecurity, c.relforcerowsecurity, c.reloptions, c.relispartition, c.relpartbound::text)::text, E'\\n' ORDER BY c.oid), ''))
|
|
46
|
+
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.oid >= 16384 AND ${USER_NAMESPACE}),
|
|
47
|
+
(SELECT md5(coalesce(string_agg(row(a.attrelid, a.attnum, a.attname, a.atttypid, a.attnotnull, a.atthasdef, a.attisdropped, a.attidentity, a.attgenerated, a.attacl, a.atttypmod, a.attcollation)::text, E'\\n' ORDER BY a.attrelid, a.attnum), ''))
|
|
48
|
+
FROM pg_attribute a JOIN pg_class c ON c.oid = a.attrelid JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
49
|
+
WHERE a.attnum > 0 AND c.oid >= 16384 AND ${USER_NAMESPACE}),
|
|
50
|
+
(SELECT md5(coalesce(string_agg(row(d.oid, d.adrelid, d.adnum, pg_get_expr(d.adbin, d.adrelid))::text, E'\\n' ORDER BY d.oid), '')) FROM pg_attrdef d WHERE d.adrelid >= 16384),
|
|
51
|
+
(SELECT md5(coalesce(string_agg(row(k.oid, k.conname, k.contype, k.conrelid, k.convalidated, k.conkey, k.confrelid, k.confkey, k.conbin, k.condeferrable, k.condeferred, k.connoinherit)::text, E'\\n' ORDER BY k.oid), '')) FROM pg_constraint k WHERE k.oid >= 16384),
|
|
52
|
+
(SELECT md5(coalesce(string_agg(row(i.indexrelid, i.indrelid, i.indkey, i.indisunique, i.indisvalid, i.indisready, i.indpred, i.indexprs)::text, E'\\n' ORDER BY i.indexrelid), '')) FROM pg_index i WHERE i.indexrelid >= 16384),
|
|
53
|
+
(SELECT md5(coalesce(string_agg(row(p.oid, p.proname, p.pronamespace, p.proowner, p.proacl, p.prokind, md5(p.prosrc), p.proargtypes, p.prorettype, p.provolatile, p.prosecdef, p.proconfig, p.proisstrict)::text, E'\\n' ORDER BY p.oid), '')) FROM pg_proc p WHERE p.oid >= 16384),
|
|
54
|
+
(SELECT md5(coalesce(string_agg(row(t.oid, t.tgname, t.tgrelid, t.tgenabled, t.tgfoid, t.tgtype, t.tgattr, t.tgqual::text)::text, E'\\n' ORDER BY t.oid), '')) FROM pg_trigger t WHERE t.tgrelid >= 16384),
|
|
55
|
+
(SELECT md5(coalesce(string_agg(row(o.oid, o.polname, o.polrelid, o.polcmd, o.polroles, o.polqual, o.polwithcheck)::text, E'\\n' ORDER BY o.oid), '')) FROM pg_policy o),
|
|
56
|
+
(SELECT md5(coalesce(string_agg(row(r.oid, r.rulename, r.ev_class, r.ev_action)::text, E'\\n' ORDER BY r.oid), '')) FROM pg_rewrite r WHERE r.oid >= 16384),
|
|
57
|
+
(SELECT md5(coalesce(string_agg(row(y.oid, y.typname, y.typnamespace, y.typtype)::text, E'\\n' ORDER BY y.oid), ''))
|
|
58
|
+
FROM pg_type y JOIN pg_namespace n ON n.oid = y.typnamespace WHERE y.oid >= 16384 AND ${USER_NAMESPACE}),
|
|
59
|
+
(SELECT md5(coalesce(string_agg(row(s.seqrelid, s.seqtypid, s.seqstart, s.seqincrement, s.seqmax, s.seqmin, s.seqcache, s.seqcycle)::text, E'\\n' ORDER BY s.seqrelid), '')) FROM pg_sequence s WHERE s.seqrelid >= 16384),
|
|
60
|
+
(SELECT md5(coalesce(string_agg(row(e.oid, e.extname, e.extversion, e.extnamespace)::text, E'\\n' ORDER BY e.oid), '')) FROM pg_extension e),
|
|
61
|
+
(SELECT md5(coalesce(string_agg(row(u.oid, u.pubname)::text, E'\\n' ORDER BY u.oid), '')) FROM pg_publication u),
|
|
62
|
+
(SELECT md5(coalesce(string_agg(row(v.oid, v.evtname, v.evtevent, v.evtfoid, v.evtenabled)::text, E'\\n' ORDER BY v.oid), '')) FROM pg_event_trigger v),
|
|
63
|
+
(SELECT md5(coalesce(string_agg(row(h.inhrelid, h.inhparent, h.inhseqno)::text, E'\\n' ORDER BY h.inhrelid, h.inhparent), '')) FROM pg_inherits h),
|
|
64
|
+
(SELECT md5(coalesce(string_agg(row(m.oid, m.enumtypid, m.enumlabel, m.enumsortorder)::text, E'\\n' ORDER BY m.oid), '')) FROM pg_enum m),
|
|
65
|
+
(SELECT md5(coalesce(string_agg(row(q.oid, q.oprname, q.oprnamespace, q.oprleft, q.oprright, q.oprresult, q.oprcode)::text, E'\\n' ORDER BY q.oid), '')) FROM pg_operator q WHERE q.oid >= 16384),
|
|
66
|
+
(SELECT md5(coalesce(string_agg(row(w.oid, w.castsource, w.casttarget, w.castfunc, w.castcontext, w.castmethod)::text, E'\\n' ORDER BY w.oid), '')) FROM pg_cast w WHERE w.oid >= 16384),
|
|
67
|
+
(SELECT md5(coalesce(string_agg(row(l.oid, l.collname, l.collnamespace, l.collprovider, l.collisdeterministic, l.collcollate, l.collctype)::text, E'\\n' ORDER BY l.oid), '')) FROM pg_collation l WHERE l.oid >= 16384),
|
|
68
|
+
(SELECT md5(coalesce(string_agg(row(z.oid, z.stxrelid, z.stxname, z.stxkeys, z.stxkind)::text, E'\\n' ORDER BY z.oid), '')) FROM pg_statistic_ext z WHERE z.oid >= 16384),
|
|
69
|
+
(SELECT count(*)::text FROM pg_largeobject_metadata),
|
|
70
|
+
(SELECT md5(coalesce(string_agg(row(x.objoid, x.classoid, x.objsubid, x.description)::text, E'\\n' ORDER BY x.objoid, x.classoid, x.objsubid), '')) FROM pg_description x WHERE x.objoid >= 16384),
|
|
71
|
+
(SELECT md5(coalesce(string_agg(row(f.oid, f.defaclrole, f.defaclnamespace, f.defaclobjtype, f.defaclacl)::text, E'\\n' ORDER BY f.oid), '')) FROM pg_default_acl f),
|
|
72
|
+
(SELECT md5(coalesce(string_agg(row(b.setrole, b.setconfig)::text, E'\\n' ORDER BY b.setrole), ''))
|
|
73
|
+
FROM pg_db_role_setting b WHERE b.setdatabase = (SELECT oid FROM pg_database WHERE datname = current_database())),
|
|
74
|
+
(SELECT datacl::text FROM pg_database WHERE datname = current_database())
|
|
75
|
+
)) AS digest`
|
|
76
|
+
|
|
77
|
+
const STORAGE_QUERY = `
|
|
78
|
+
SELECT format('%I.%I', n.nspname, c.relname) AS name, c.relkind::text AS kind,
|
|
79
|
+
c.relfilenode::text AS filenode, pg_relation_size(c.oid, 'main')::text AS size
|
|
80
|
+
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
81
|
+
WHERE c.relkind IN ('r', 'm') AND c.oid >= 16384 AND ${USER_NAMESPACE}`
|
|
82
|
+
|
|
83
|
+
const SEQUENCE_QUERY = `
|
|
84
|
+
SELECT format('%I.%I', schemaname, sequencename) AS name, last_value::text AS last, start_value::text AS start
|
|
85
|
+
FROM pg_sequences WHERE schemaname !~ '^pg_(toast_)?temp_' AND schemaname NOT IN ('information_schema', 'pg_catalog')`
|
|
86
|
+
|
|
87
|
+
// Tables row-level security applies to for the connected role: not a superuser
|
|
88
|
+
// or BYPASSRLS role, and either not the owner or the owner of a FORCEd table.
|
|
89
|
+
const RLS_QUERY = `
|
|
90
|
+
SELECT format('%I.%I', n.nspname, c.relname) AS name,
|
|
91
|
+
pg_has_role(current_user, c.relowner, 'USAGE') AS owner
|
|
92
|
+
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
93
|
+
WHERE c.relkind = 'r' AND c.relrowsecurity AND c.oid >= 16384 AND ${USER_NAMESPACE}
|
|
94
|
+
AND NOT (SELECT rolsuper OR rolbypassrls FROM pg_roles WHERE rolname = current_user)
|
|
95
|
+
AND (c.relforcerowsecurity OR NOT pg_has_role(current_user, c.relowner, 'USAGE'))`
|
|
96
|
+
|
|
97
|
+
type Query = postgres.Sql | postgres.TransactionSql
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Tables among `wanted` that RLS hides from this role but that it owns (FORCEd),
|
|
101
|
+
* and those it does not own, which it cannot read or write at all.
|
|
102
|
+
*/
|
|
103
|
+
async function rlsAccess(q: Query, wanted: ReadonlySet<string>): Promise<{ lift: string[]; hidden: string[] }> {
|
|
104
|
+
const rows = await q.unsafe<{ name: string; owner: boolean }[]>(RLS_QUERY)
|
|
105
|
+
const applies = rows.filter(r => wanted.has(r.name))
|
|
106
|
+
return {
|
|
107
|
+
lift: applies.filter(r => r.owner).map(r => r.name),
|
|
108
|
+
hidden: applies.filter(r => !r.owner).map(r => r.name),
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function connect(url: string): postgres.Sql {
|
|
113
|
+
return postgres(url, { max: 1, onnotice: () => {}, idle_timeout: 0, max_lifetime: 0 })
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function readCopy(sql: Query, table: string): Promise<Buffer> {
|
|
117
|
+
const stream = await sql.unsafe(`COPY ${table} TO STDOUT`).readable()
|
|
118
|
+
const chunks: Buffer[] = []
|
|
119
|
+
for await (const chunk of stream) chunks.push(Buffer.from(chunk as Buffer))
|
|
120
|
+
return Buffer.concat(chunks)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function writeCopy(sql: postgres.Sql | postgres.TransactionSql, table: string, data: Buffer): Promise<void> {
|
|
124
|
+
const sink = await sql.unsafe(`COPY ${table} FROM STDIN`).writable()
|
|
125
|
+
await new Promise<void>((resolve, reject) => {
|
|
126
|
+
sink.once('error', reject)
|
|
127
|
+
sink.once('finish', resolve)
|
|
128
|
+
sink.end(data)
|
|
129
|
+
})
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Run `run` with FORCE ROW LEVEL SECURITY off on `tables` (owned by this role), restored before the transaction commits. */
|
|
133
|
+
async function withForceLifted(sql: postgres.Sql, tables: string[], run: (q: Query) => Promise<void>): Promise<void> {
|
|
134
|
+
if (tables.length === 0) return run(sql)
|
|
135
|
+
await sql.begin(async tx => {
|
|
136
|
+
for (const table of tables) await tx.unsafe(`ALTER TABLE ${table} NO FORCE ROW LEVEL SECURITY`)
|
|
137
|
+
await run(tx)
|
|
138
|
+
for (const table of tables) await tx.unsafe(`ALTER TABLE ${table} FORCE ROW LEVEL SECURITY`)
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** Read the state a reset returns to. `url` is a superuser connection to the database. */
|
|
143
|
+
export async function captureBaseline(url: string): Promise<Baseline> {
|
|
144
|
+
const sql = connect(url)
|
|
145
|
+
try {
|
|
146
|
+
const [digestRow] = await sql.unsafe<{ digest: string }[]>(DIGEST_QUERY)
|
|
147
|
+
const storage = await sql.unsafe<{ name: string; kind: string; filenode: string; size: string }[]>(STORAGE_QUERY)
|
|
148
|
+
const sizes = new Map<string, number>()
|
|
149
|
+
const matviews = new Map<string, string>()
|
|
150
|
+
const stored = new Set<string>()
|
|
151
|
+
for (const row of storage) {
|
|
152
|
+
sizes.set(row.name, Number(row.size))
|
|
153
|
+
if (row.kind === 'm') matviews.set(row.name, row.filenode)
|
|
154
|
+
else if (Number(row.size) > 0) stored.add(row.name)
|
|
155
|
+
}
|
|
156
|
+
// A forced-RLS table the connection owns is read with the force lifted for
|
|
157
|
+
// this transaction; one it does not own shows no rows, whatever it holds.
|
|
158
|
+
const { lift, hidden } = await rlsAccess(sql, stored)
|
|
159
|
+
const rows = new Map<string, Buffer>()
|
|
160
|
+
await withForceLifted(sql, lift, async q => {
|
|
161
|
+
for (const table of stored) {
|
|
162
|
+
if (hidden.includes(table)) continue
|
|
163
|
+
const [has] = await q.unsafe<{ has: boolean }[]>(`SELECT EXISTS (SELECT 1 FROM ${table}) AS has`)
|
|
164
|
+
if (has!.has) rows.set(table, await readCopy(q, table))
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
const sequences = new Map<string, { last: string | null; start: string }>()
|
|
168
|
+
for (const row of await sql.unsafe<{ name: string; last: string | null; start: string }[]>(SEQUENCE_QUERY)) {
|
|
169
|
+
sequences.set(row.name, { last: row.last, start: row.start })
|
|
170
|
+
}
|
|
171
|
+
return { digest: digestRow!.digest, sizes, matviews, stored, rows, hidden, sequences }
|
|
172
|
+
} finally {
|
|
173
|
+
await sql.end()
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Return the database to its baseline. Call after the file's backends are gone.
|
|
179
|
+
* Refuses (ok: false) rather than reset a database whose state a reset cannot
|
|
180
|
+
* undo; nothing is changed in that case.
|
|
181
|
+
*/
|
|
182
|
+
export async function resetDatabase(
|
|
183
|
+
url: string,
|
|
184
|
+
baseline: Baseline,
|
|
185
|
+
superuser = true,
|
|
186
|
+
): Promise<ResetOutcome> {
|
|
187
|
+
let sql: postgres.Sql | null = null
|
|
188
|
+
try {
|
|
189
|
+
sql = connect(url)
|
|
190
|
+
const [prepared] = await sql.unsafe<{ n: number }[]>(
|
|
191
|
+
`SELECT count(*)::int AS n FROM pg_prepared_xacts WHERE database = current_database()`,
|
|
192
|
+
)
|
|
193
|
+
if (prepared!.n > 0) return { ok: false, reason: 'prepared-xact' }
|
|
194
|
+
if (baseline.hidden.length > 0) return { ok: false, reason: 'rls' }
|
|
195
|
+
const [digestRow] = await sql.unsafe<{ digest: string }[]>(DIGEST_QUERY)
|
|
196
|
+
if (digestRow!.digest !== baseline.digest) return { ok: false, reason: 'digest' }
|
|
197
|
+
|
|
198
|
+
const storage = await sql.unsafe<{ name: string; kind: string; filenode: string; size: string }[]>(STORAGE_QUERY)
|
|
199
|
+
const dirty: string[] = []
|
|
200
|
+
for (const row of storage) {
|
|
201
|
+
const base = baseline.sizes.get(row.name)
|
|
202
|
+
if (base === undefined) return { ok: false, reason: 'digest' }
|
|
203
|
+
if (row.kind === 'm') {
|
|
204
|
+
if (Number(row.size) !== base || row.filenode !== baseline.matviews.get(row.name)) {
|
|
205
|
+
return { ok: false, reason: 'matview' }
|
|
206
|
+
}
|
|
207
|
+
} else if (Number(row.size) > base || baseline.stored.has(row.name)) {
|
|
208
|
+
dirty.push(row.name)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (dirty.length > 0) {
|
|
213
|
+
await sql.begin(async tx => {
|
|
214
|
+
// Superuser: reload without firing triggers or checking FKs. Otherwise
|
|
215
|
+
// reload plainly, then refuse if a trigger wrote into a table that
|
|
216
|
+
// should be empty.
|
|
217
|
+
if (superuser) await tx.unsafe(`SET LOCAL session_replication_role = replica`)
|
|
218
|
+
await tx.unsafe(`TRUNCATE ${dirty.join(', ')} CASCADE`)
|
|
219
|
+
// Policies apply to the reload as to any write: lift a forced one on
|
|
220
|
+
// the tables it fills, as the baseline read did.
|
|
221
|
+
const { lift } = await rlsAccess(tx, new Set(baseline.rows.keys()))
|
|
222
|
+
for (const table of lift) await tx.unsafe(`ALTER TABLE ${table} NO FORCE ROW LEVEL SECURITY`)
|
|
223
|
+
for (const [table, data] of baseline.rows) await writeCopy(tx, table, data)
|
|
224
|
+
for (const table of lift) await tx.unsafe(`ALTER TABLE ${table} FORCE ROW LEVEL SECURITY`)
|
|
225
|
+
if (!superuser && baseline.rows.size > 0) {
|
|
226
|
+
for (const row of await tx.unsafe<{ name: string; size: string }[]>(STORAGE_QUERY)) {
|
|
227
|
+
if (baseline.stored.has(row.name) || baseline.matviews.has(row.name)) continue
|
|
228
|
+
if (Number(row.size) > (baseline.sizes.get(row.name) ?? 0)) throw new Error(`reload wrote to ${row.name}`)
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
})
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const current = await sql.unsafe<{ name: string; last: string | null }[]>(SEQUENCE_QUERY)
|
|
235
|
+
const fixes: string[] = []
|
|
236
|
+
for (const row of current) {
|
|
237
|
+
const base = baseline.sequences.get(row.name)
|
|
238
|
+
if (base === undefined) return { ok: false, reason: 'digest' }
|
|
239
|
+
if (row.last === base.last) continue
|
|
240
|
+
const literal = `'${row.name.replace(/'/g, "''")}'::regclass`
|
|
241
|
+
fixes.push(base.last === null
|
|
242
|
+
? `setval(${literal}, ${base.start}, false)`
|
|
243
|
+
: `setval(${literal}, ${base.last}, true)`)
|
|
244
|
+
}
|
|
245
|
+
if (fixes.length > 0) await sql.unsafe(`SELECT ${fixes.join(', ')}`)
|
|
246
|
+
|
|
247
|
+
return { ok: true, cleared: dirty.length, reloaded: baseline.rows.size }
|
|
248
|
+
} catch {
|
|
249
|
+
return { ok: false, reason: 'error' }
|
|
250
|
+
} finally {
|
|
251
|
+
if (sql !== null) await sql.end().catch(() => {})
|
|
252
|
+
}
|
|
253
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks'
|
|
2
|
+
import type postgres from 'postgres'
|
|
3
|
+
import { PostgresJsTransaction, type PostgresJsDatabase } from '../data/query/postgres-js/index.js'
|
|
4
|
+
import { throughErrorBoundary } from '../data/query/postgres-js/driver-error-boundary.js'
|
|
5
|
+
|
|
6
|
+
type Handle = PostgresJsDatabase
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Makes `db.transaction(fn)` — and `transaction` on the handle `fn` receives,
|
|
10
|
+
* to any depth — a SAVEPOINT on the transaction the harness already holds on
|
|
11
|
+
* this connection: `RELEASE` on success, `ROLLBACK TO` and rethrow on error.
|
|
12
|
+
* Without this, drizzle sends `BEGIN … COMMIT` and the `COMMIT` ends the test's
|
|
13
|
+
* transaction.
|
|
14
|
+
*
|
|
15
|
+
* A savepoint inherits the outer transaction's isolation level, so the
|
|
16
|
+
* `config` argument is ignored. Settings changed by statements sent from inside
|
|
17
|
+
* the block (`set_config(..., true)`, `SET LOCAL`) and the role it set are put
|
|
18
|
+
* back to their values before the savepoint when it releases; statements sent
|
|
19
|
+
* on the connection from outside the block (another task) are not undone.
|
|
20
|
+
* Postgres cannot list custom settings (`pipework.*` is not in `pg_settings`),
|
|
21
|
+
* so the names are taken from the statements this connection sends.
|
|
22
|
+
*
|
|
23
|
+
* Returns a function that puts another handle over the same connection on the
|
|
24
|
+
* same savepoint queue.
|
|
25
|
+
*/
|
|
26
|
+
export function collapseTransactionsToSavepoints(db: PostgresJsDatabase, client: postgres.Sql): (other: PostgresJsDatabase) => void {
|
|
27
|
+
let counter = 0
|
|
28
|
+
const known = new Set<string>()
|
|
29
|
+
|
|
30
|
+
const unsafe = client.unsafe.bind(client) as (...args: unknown[]) => unknown
|
|
31
|
+
;(client as { unsafe: unknown }).unsafe = (query: string, params?: unknown[], ...rest: unknown[]): unknown => {
|
|
32
|
+
const names = settingNames(query, params ?? [])
|
|
33
|
+
let owner = inBlock.getStore()
|
|
34
|
+
while (owner !== undefined && !owner.open) owner = owner.parent
|
|
35
|
+
for (const name of names) {
|
|
36
|
+
known.add(name)
|
|
37
|
+
owner?.touched.add(name)
|
|
38
|
+
}
|
|
39
|
+
if (owner !== undefined && ROLE_CHANGE.test(query)) owner.roleTouched = true
|
|
40
|
+
return unsafe(query, params, ...rest)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// One connection runs one savepoint at a time among siblings, as drizzle's
|
|
44
|
+
// reserved `begin` made concurrent transactions do: Postgres savepoints are
|
|
45
|
+
// a stack, so releasing one releases those opened after it. Calls made
|
|
46
|
+
// outside any block queue FIFO on `tail`; calls made from one open block
|
|
47
|
+
// queue FIFO on that block's own `tail`. A call made inside a savepoint's
|
|
48
|
+
// scope, on the root db or on a handle alike, nests in that savepoint at
|
|
49
|
+
// once, or it would wait on the block that is waiting for it. A block
|
|
50
|
+
// releases only after the savepoints started in it settle; a call made from
|
|
51
|
+
// its scope after it ended nests in the nearest block still open, or queues
|
|
52
|
+
// on `tail` when none is.
|
|
53
|
+
let tail: Promise<unknown> = Promise.resolve()
|
|
54
|
+
interface Block {
|
|
55
|
+
open: boolean
|
|
56
|
+
children: Set<Promise<unknown>>
|
|
57
|
+
parent: Block | undefined
|
|
58
|
+
tail: Promise<unknown>
|
|
59
|
+
touched: Set<string>
|
|
60
|
+
roleTouched: boolean
|
|
61
|
+
}
|
|
62
|
+
const inBlock = new AsyncLocalStorage<Block>()
|
|
63
|
+
|
|
64
|
+
// `target` is the handle `fn` receives and its descendants: statements sent
|
|
65
|
+
// through it travel its session, the savepoint statements always travel `db`'s.
|
|
66
|
+
const transactionFor = (target: Handle) => <T>(fn: (tx: Handle) => Promise<T>): Promise<T> => {
|
|
67
|
+
let parent = inBlock.getStore()
|
|
68
|
+
while (parent !== undefined && !parent.open) parent = parent.parent
|
|
69
|
+
const run = (): Promise<T> => {
|
|
70
|
+
const block: Block = { open: true, children: new Set(), parent, tail: Promise.resolve(), touched: new Set(), roleTouched: false }
|
|
71
|
+
return throughErrorBoundary(client, () => inBlock.run(block, () => runSavepoint(fn, block, target)))
|
|
72
|
+
}
|
|
73
|
+
const queued = (parent?.tail ?? tail).then(run)
|
|
74
|
+
const settled = queued.then(() => undefined, () => undefined)
|
|
75
|
+
if (parent !== undefined) {
|
|
76
|
+
parent.tail = settled
|
|
77
|
+
parent.children.add(settled)
|
|
78
|
+
} else {
|
|
79
|
+
tail = settled
|
|
80
|
+
}
|
|
81
|
+
return queued
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const wrap = (target: Handle): void => {
|
|
85
|
+
;(target as { transaction: unknown }).transaction = transactionFor(target)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const settleChildren = async (block: Block): Promise<void> => {
|
|
89
|
+
for (let seen = -1; seen !== block.children.size;) {
|
|
90
|
+
seen = block.children.size
|
|
91
|
+
await Promise.all([...block.children])
|
|
92
|
+
}
|
|
93
|
+
block.open = false
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Each of the three steps below is one simple-protocol round trip on the
|
|
97
|
+
// raw client, so the harness adds no more than two per block.
|
|
98
|
+
const send = unsafe as (query: string) => Promise<unknown>
|
|
99
|
+
|
|
100
|
+
const runSavepoint = async <T>(fn: (tx: Handle) => Promise<T>, block: Block, target: Handle): Promise<T> => {
|
|
101
|
+
const name = `pw_${++counter}`
|
|
102
|
+
const before = await openSavepoint(send, name, known)
|
|
103
|
+
let result: T
|
|
104
|
+
try {
|
|
105
|
+
const handle = new PostgresJsTransaction(target.dialect, target.session as never, undefined) as unknown as Handle
|
|
106
|
+
wrap(handle)
|
|
107
|
+
result = await fn(handle)
|
|
108
|
+
} catch (err) {
|
|
109
|
+
await settleChildren(block)
|
|
110
|
+
await send(`ROLLBACK TO SAVEPOINT ${name}; RELEASE SAVEPOINT ${name}`)
|
|
111
|
+
throw err
|
|
112
|
+
}
|
|
113
|
+
await settleChildren(block)
|
|
114
|
+
await send(releaseAndRestore(name, before, block))
|
|
115
|
+
return result
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
wrap(db)
|
|
119
|
+
// Another handle over the same connection joins this savepoint queue.
|
|
120
|
+
return wrap
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const ROLE_CHANGE = /^\s*(?:set\s+(?:local\s+|session\s+)?role\b|reset\s+role\b)/i
|
|
124
|
+
|
|
125
|
+
const SETTING_NAME = /^[A-Za-z_][\w.]*$/
|
|
126
|
+
|
|
127
|
+
/** Names of the settings a statement changes: `set_config(name, ...)` and `SET [LOCAL] name =`. */
|
|
128
|
+
function settingNames(query: string, params: unknown[]): string[] {
|
|
129
|
+
const names: string[] = []
|
|
130
|
+
for (const m of query.matchAll(/set_config\s*\(\s*(?:'([^']+)'|\$(\d+))/gi)) {
|
|
131
|
+
const name = m[1] ?? params[Number(m[2]) - 1]
|
|
132
|
+
if (typeof name === 'string' && SETTING_NAME.test(name)) names.push(name)
|
|
133
|
+
}
|
|
134
|
+
const set = /^\s*set\s+(?:(?:local|session)\s+)?([A-Za-z_][\w.]*)\s*(?:=|to\b)/i.exec(query)
|
|
135
|
+
if (set?.[1] !== undefined) names.push(set[1])
|
|
136
|
+
return names
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface Snapshot {
|
|
140
|
+
settings: Map<string, string | null>
|
|
141
|
+
role: string
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const literal = (value: string): string => `'${value.replaceAll("'", "''")}'`
|
|
145
|
+
|
|
146
|
+
/** `SAVEPOINT name` and the snapshot of the known settings and the role, in one query. */
|
|
147
|
+
async function openSavepoint(send: (query: string) => Promise<unknown>, name: string, names: ReadonlySet<string>): Promise<Snapshot> {
|
|
148
|
+
const settings = names.size === 0
|
|
149
|
+
? 'NULL'
|
|
150
|
+
: `(SELECT jsonb_object_agg(n, current_setting(n, true)) FROM unnest(ARRAY[${[...names].map(literal).join(', ')}]::text[]) AS n)`
|
|
151
|
+
// postgres.js answers a multi-statement simple query with one result per statement.
|
|
152
|
+
const results = await send(`SAVEPOINT ${name}; SELECT current_user AS role, ${settings} AS settings`) as Array<Array<Record<string, unknown>>>
|
|
153
|
+
const row = results[1]?.[0]
|
|
154
|
+
const values = (row?.['settings'] ?? {}) as Record<string, string | null>
|
|
155
|
+
return { settings: new Map(Object.entries(values)), role: String(row?.['role']) }
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** `RELEASE SAVEPOINT name`, then every touched setting and a touched role set back, in one query. */
|
|
159
|
+
function releaseAndRestore(
|
|
160
|
+
name: string,
|
|
161
|
+
before: Snapshot,
|
|
162
|
+
block: { touched: ReadonlySet<string>; roleTouched: boolean },
|
|
163
|
+
): string {
|
|
164
|
+
let query = `RELEASE SAVEPOINT ${name}`
|
|
165
|
+
if (block.touched.size > 0) {
|
|
166
|
+
// A setting first seen inside the savepoint was unset before it.
|
|
167
|
+
const calls = [...block.touched].map(n => `set_config(${literal(n)}, ${literal(before.settings.get(n) ?? '')}, true)`)
|
|
168
|
+
query += `; SELECT ${calls.join(', ')}`
|
|
169
|
+
}
|
|
170
|
+
if (block.roleTouched) query += `; SET LOCAL ROLE "${before.role.replaceAll('"', '""')}"`
|
|
171
|
+
return query
|
|
172
|
+
}
|