@supabase/pg-delta 1.0.0-alpha.24 → 1.0.0-alpha.26
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/dist/core/catalog.model.d.ts +2 -2
- package/dist/core/catalog.model.js +28 -21
- package/dist/core/expand-replace-dependencies.js +1 -7
- package/dist/core/integrations/supabase.js +84 -0
- package/dist/core/objects/aggregate/changes/aggregate.privilege.js +21 -9
- package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.alter.js +4 -1
- package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.create.js +6 -3
- package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.d.ts +11 -0
- package/dist/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.js +11 -0
- package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.alter.js +4 -1
- package/dist/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.create.js +6 -3
- package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.d.ts +11 -0
- package/dist/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.js +11 -0
- package/dist/core/objects/foreign-data-wrapper/sensitive-options.d.ts +32 -0
- package/dist/core/objects/foreign-data-wrapper/sensitive-options.js +129 -0
- package/dist/core/objects/foreign-data-wrapper/server/changes/server.alter.js +4 -1
- package/dist/core/objects/foreign-data-wrapper/server/changes/server.create.js +6 -3
- package/dist/core/objects/foreign-data-wrapper/server/server.model.d.ts +10 -0
- package/dist/core/objects/foreign-data-wrapper/server/server.model.js +10 -0
- package/dist/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.alter.js +4 -1
- package/dist/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.create.js +6 -3
- package/dist/core/objects/foreign-data-wrapper/user-mapping/user-mapping.model.d.ts +10 -0
- package/dist/core/objects/foreign-data-wrapper/user-mapping/user-mapping.model.js +10 -0
- package/dist/core/objects/rls-policy/rls-policy.model.d.ts +2 -2
- package/dist/core/objects/table/table.diff.js +53 -30
- package/dist/core/objects/table/table.model.js +7 -2
- package/dist/core/plan/hierarchy.js +4 -4
- package/dist/core/postgres-config.d.ts +7 -0
- package/dist/core/postgres-config.js +19 -5
- package/dist/core/sort/debug-visualization.js +1 -1
- package/dist/core/sort/topological-sort.js +2 -2
- package/package.json +34 -33
- package/src/core/catalog.model.ts +40 -23
- package/src/core/catalog.snapshot.test.ts +1 -0
- package/src/core/expand-replace-dependencies.test.ts +12 -0
- package/src/core/expand-replace-dependencies.ts +1 -12
- package/src/core/integrations/supabase.test.ts +198 -0
- package/src/core/integrations/supabase.ts +84 -0
- package/src/core/objects/aggregate/changes/aggregate.base.ts +1 -1
- package/src/core/objects/aggregate/changes/aggregate.privilege.test.ts +79 -0
- package/src/core/objects/aggregate/changes/aggregate.privilege.ts +22 -9
- package/src/core/objects/collation/changes/collation.base.ts +1 -1
- package/src/core/objects/domain/changes/domain.base.ts +1 -1
- package/src/core/objects/extension/changes/extension.base.ts +1 -1
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.alter.test.ts +34 -4
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.alter.ts +5 -1
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.base.ts +1 -1
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.create.test.ts +34 -0
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/changes/foreign-data-wrapper.create.ts +7 -5
- package/src/core/objects/foreign-data-wrapper/foreign-data-wrapper/foreign-data-wrapper.model.ts +11 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.alter.test.ts +25 -4
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.alter.ts +5 -1
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.base.ts +1 -1
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.create.test.ts +54 -0
- package/src/core/objects/foreign-data-wrapper/foreign-table/changes/foreign-table.create.ts +7 -5
- package/src/core/objects/foreign-data-wrapper/foreign-table/foreign-table.model.ts +11 -0
- package/src/core/objects/foreign-data-wrapper/sensitive-options.test.ts +98 -0
- package/src/core/objects/foreign-data-wrapper/sensitive-options.ts +133 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.alter.test.ts +39 -4
- package/src/core/objects/foreign-data-wrapper/server/changes/server.alter.ts +5 -1
- package/src/core/objects/foreign-data-wrapper/server/changes/server.base.ts +1 -1
- package/src/core/objects/foreign-data-wrapper/server/changes/server.create.test.ts +36 -0
- package/src/core/objects/foreign-data-wrapper/server/changes/server.create.ts +7 -5
- package/src/core/objects/foreign-data-wrapper/server/server.model.ts +10 -0
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.alter.test.ts +39 -6
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.alter.ts +5 -1
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.base.ts +1 -1
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.create.test.ts +38 -2
- package/src/core/objects/foreign-data-wrapper/user-mapping/changes/user-mapping.create.ts +7 -5
- package/src/core/objects/foreign-data-wrapper/user-mapping/user-mapping.model.ts +10 -0
- package/src/core/objects/index/changes/index.base.ts +1 -1
- package/src/core/objects/language/changes/language.base.ts +1 -1
- package/src/core/objects/materialized-view/changes/materialized-view.base.ts +1 -1
- package/src/core/objects/procedure/changes/procedure.base.ts +1 -1
- package/src/core/objects/rls-policy/changes/rls-policy.base.ts +1 -1
- package/src/core/objects/role/changes/role.base.ts +1 -1
- package/src/core/objects/schema/changes/schema.base.ts +1 -1
- package/src/core/objects/sequence/changes/sequence.base.ts +1 -1
- package/src/core/objects/table/changes/table.base.ts +1 -1
- package/src/core/objects/table/changes/table.comment.ts +2 -8
- package/src/core/objects/table/table.diff.test.ts +198 -5
- package/src/core/objects/table/table.diff.ts +63 -34
- package/src/core/objects/table/table.model.ts +7 -2
- package/src/core/objects/trigger/changes/trigger.alter.ts +1 -4
- package/src/core/objects/trigger/changes/trigger.base.ts +1 -1
- package/src/core/objects/type/composite-type/changes/composite-type.base.ts +1 -1
- package/src/core/objects/type/enum/changes/enum.base.ts +1 -1
- package/src/core/objects/type/range/changes/range.base.ts +1 -1
- package/src/core/objects/view/changes/view.base.ts +1 -1
- package/src/core/plan/hierarchy.ts +4 -4
- package/src/core/plan/sql-format/format-off.test.ts +4 -4
- package/src/core/plan/sql-format/format-pretty-lower-leading.test.ts +4 -4
- package/src/core/plan/sql-format/format-pretty-narrow.test.ts +5 -4
- package/src/core/plan/sql-format/format-pretty-preserve.test.ts +4 -4
- package/src/core/plan/sql-format/format-pretty-upper.test.ts +4 -4
- package/src/core/postgres-config.test.ts +39 -1
- package/src/core/postgres-config.ts +32 -16
- package/src/core/sort/debug-visualization.ts +1 -1
- package/src/core/sort/sort-changes.test.ts +1 -0
- package/src/core/sort/topological-sort.ts +2 -2
|
@@ -86,14 +86,6 @@ function createAlterConstraintChange(mainTable: Table, branchTable: Table) {
|
|
|
86
86
|
constraint: c,
|
|
87
87
|
}),
|
|
88
88
|
);
|
|
89
|
-
if (!c.validated) {
|
|
90
|
-
changes.push(
|
|
91
|
-
new AlterTableValidateConstraint({
|
|
92
|
-
table: branchTable,
|
|
93
|
-
constraint: c,
|
|
94
|
-
}),
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
89
|
// Add comment for newly created constraint
|
|
98
90
|
if (c.comment !== null) {
|
|
99
91
|
changes.push(
|
|
@@ -120,7 +112,7 @@ function createAlterConstraintChange(mainTable: Table, branchTable: Table) {
|
|
|
120
112
|
}
|
|
121
113
|
}
|
|
122
114
|
|
|
123
|
-
// Altered constraints -> drop + add
|
|
115
|
+
// Altered constraints -> drop + add (or VALIDATE-only shortcut)
|
|
124
116
|
for (const [name, mainC] of mainByName) {
|
|
125
117
|
const branchC = branchByName.get(name);
|
|
126
118
|
if (!branchC) continue;
|
|
@@ -130,24 +122,68 @@ function createAlterConstraintChange(mainTable: Table, branchTable: Table) {
|
|
|
130
122
|
continue;
|
|
131
123
|
}
|
|
132
124
|
|
|
125
|
+
// Cheap scalar `===` checks first; only fall through to JSON.stringify
|
|
126
|
+
// on the array fields when every scalar has already matched.
|
|
127
|
+
const fieldsEqualExceptValidated =
|
|
128
|
+
mainC.constraint_type === branchC.constraint_type &&
|
|
129
|
+
mainC.deferrable === branchC.deferrable &&
|
|
130
|
+
mainC.initially_deferred === branchC.initially_deferred &&
|
|
131
|
+
mainC.is_local === branchC.is_local &&
|
|
132
|
+
mainC.no_inherit === branchC.no_inherit &&
|
|
133
|
+
mainC.is_temporal === branchC.is_temporal &&
|
|
134
|
+
mainC.foreign_key_table === branchC.foreign_key_table &&
|
|
135
|
+
mainC.foreign_key_schema === branchC.foreign_key_schema &&
|
|
136
|
+
mainC.on_update === branchC.on_update &&
|
|
137
|
+
mainC.on_delete === branchC.on_delete &&
|
|
138
|
+
mainC.match_type === branchC.match_type &&
|
|
139
|
+
mainC.check_expression === branchC.check_expression &&
|
|
140
|
+
JSON.stringify(mainC.key_columns) ===
|
|
141
|
+
JSON.stringify(branchC.key_columns) &&
|
|
142
|
+
JSON.stringify(mainC.foreign_key_columns) ===
|
|
143
|
+
JSON.stringify(branchC.foreign_key_columns);
|
|
144
|
+
|
|
145
|
+
// Safe-migration shortcut: when the only difference is `validated`
|
|
146
|
+
// flipping from false to true, emit a single `ALTER TABLE ... VALIDATE
|
|
147
|
+
// CONSTRAINT` instead of drop+add. VALIDATE CONSTRAINT only takes
|
|
148
|
+
// SHARE UPDATE EXCLUSIVE (concurrent reads/writes proceed), whereas
|
|
149
|
+
// dropping and re-adding takes ACCESS EXCLUSIVE for the entire scan.
|
|
150
|
+
// Postgres has no reverse command, so `true -> false` must still go
|
|
151
|
+
// through drop+add below.
|
|
152
|
+
if (
|
|
153
|
+
fieldsEqualExceptValidated &&
|
|
154
|
+
mainC.validated === false &&
|
|
155
|
+
branchC.validated === true
|
|
156
|
+
) {
|
|
157
|
+
changes.push(
|
|
158
|
+
new AlterTableValidateConstraint({
|
|
159
|
+
table: branchTable,
|
|
160
|
+
constraint: branchC,
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
// VALIDATE preserves the constraint OID, so its comment is preserved
|
|
164
|
+
// too. Only emit a comment change if it actually differs.
|
|
165
|
+
if (mainC.comment !== branchC.comment) {
|
|
166
|
+
if (branchC.comment === null) {
|
|
167
|
+
changes.push(
|
|
168
|
+
new DropCommentOnConstraint({
|
|
169
|
+
table: mainTable,
|
|
170
|
+
constraint: mainC,
|
|
171
|
+
}),
|
|
172
|
+
);
|
|
173
|
+
} else {
|
|
174
|
+
changes.push(
|
|
175
|
+
new CreateCommentOnConstraint({
|
|
176
|
+
table: branchTable,
|
|
177
|
+
constraint: branchC,
|
|
178
|
+
}),
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
133
185
|
const changed =
|
|
134
|
-
mainC.
|
|
135
|
-
mainC.deferrable !== branchC.deferrable ||
|
|
136
|
-
mainC.initially_deferred !== branchC.initially_deferred ||
|
|
137
|
-
mainC.validated !== branchC.validated ||
|
|
138
|
-
mainC.is_local !== branchC.is_local ||
|
|
139
|
-
mainC.no_inherit !== branchC.no_inherit ||
|
|
140
|
-
mainC.is_temporal !== branchC.is_temporal ||
|
|
141
|
-
JSON.stringify(mainC.key_columns) !==
|
|
142
|
-
JSON.stringify(branchC.key_columns) ||
|
|
143
|
-
JSON.stringify(mainC.foreign_key_columns) !==
|
|
144
|
-
JSON.stringify(branchC.foreign_key_columns) ||
|
|
145
|
-
mainC.foreign_key_table !== branchC.foreign_key_table ||
|
|
146
|
-
mainC.foreign_key_schema !== branchC.foreign_key_schema ||
|
|
147
|
-
mainC.on_update !== branchC.on_update ||
|
|
148
|
-
mainC.on_delete !== branchC.on_delete ||
|
|
149
|
-
mainC.match_type !== branchC.match_type ||
|
|
150
|
-
mainC.check_expression !== branchC.check_expression;
|
|
186
|
+
mainC.validated !== branchC.validated || !fieldsEqualExceptValidated;
|
|
151
187
|
if (changed) {
|
|
152
188
|
changes.push(
|
|
153
189
|
new AlterTableDropConstraint({
|
|
@@ -161,14 +197,6 @@ function createAlterConstraintChange(mainTable: Table, branchTable: Table) {
|
|
|
161
197
|
constraint: branchC,
|
|
162
198
|
}),
|
|
163
199
|
);
|
|
164
|
-
if (!branchC.validated) {
|
|
165
|
-
changes.push(
|
|
166
|
-
new AlterTableValidateConstraint({
|
|
167
|
-
table: branchTable,
|
|
168
|
-
constraint: branchC,
|
|
169
|
-
}),
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
200
|
// Ensure constraint comment is applied after re-creation
|
|
173
201
|
if (branchC.comment !== null) {
|
|
174
202
|
changes.push(
|
|
@@ -265,6 +293,7 @@ export function diffTables(
|
|
|
265
293
|
...createAlterConstraintChange(
|
|
266
294
|
// Create a dummy table with no constraints do diff constraints against
|
|
267
295
|
new Table({
|
|
296
|
+
// oxlint-disable-next-line typescript/no-misused-spread
|
|
268
297
|
...branchTable,
|
|
269
298
|
constraints: [],
|
|
270
299
|
}),
|
|
@@ -341,8 +341,13 @@ select
|
|
|
341
341
|
'no_inherit', c.connoinherit,
|
|
342
342
|
'is_temporal', coalesce((to_jsonb(c)->>'conperiod')::boolean, false),
|
|
343
343
|
|
|
344
|
-
--
|
|
345
|
-
|
|
344
|
+
-- Inherited from a parent (partition or classical inheritance).
|
|
345
|
+
-- coninhcount > 0 is the canonical signal across every constraint
|
|
346
|
+
-- kind. We previously used conparentid <> 0, but PostgreSQL only
|
|
347
|
+
-- populates conparentid for PK / UNIQUE / FK on partitions; CHECK
|
|
348
|
+
-- constraints on partitions always have conparentid = 0 and were
|
|
349
|
+
-- being re-emitted on every child, failing apply with 42710.
|
|
350
|
+
'is_partition_clone', (c.coninhcount > 0),
|
|
346
351
|
'parent_constraint_schema', case when c.conparentid <> 0::oid then pc.connamespace::regnamespace::text end,
|
|
347
352
|
'parent_constraint_name', case when c.conparentid <> 0::oid then quote_ident(pc.conname) end,
|
|
348
353
|
'parent_table_schema', case when c.conparentid <> 0::oid then pc_rel.relnamespace::regnamespace::text end,
|
|
@@ -28,10 +28,7 @@ export class ReplaceTrigger extends AlterTriggerChange {
|
|
|
28
28
|
public readonly indexableObject?: TableLikeObject;
|
|
29
29
|
public readonly scope = "object" as const;
|
|
30
30
|
|
|
31
|
-
constructor(props: {
|
|
32
|
-
trigger: Trigger;
|
|
33
|
-
indexableObject?: TableLikeObject;
|
|
34
|
-
}) {
|
|
31
|
+
constructor(props: { trigger: Trigger; indexableObject?: TableLikeObject }) {
|
|
35
32
|
super();
|
|
36
33
|
this.trigger = props.trigger;
|
|
37
34
|
this.indexableObject = props.indexableObject;
|
|
@@ -4,7 +4,7 @@ import type { Trigger } from "../trigger.model.ts";
|
|
|
4
4
|
abstract class BaseTriggerChange extends BaseChange {
|
|
5
5
|
abstract readonly trigger: Trigger;
|
|
6
6
|
abstract readonly scope: "object" | "comment";
|
|
7
|
-
readonly objectType
|
|
7
|
+
readonly objectType = "trigger" as const;
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export abstract class CreateTriggerChange extends BaseTriggerChange {
|
|
@@ -8,7 +8,7 @@ abstract class BaseCompositeTypeChange extends BaseChange {
|
|
|
8
8
|
| "comment"
|
|
9
9
|
| "privilege"
|
|
10
10
|
| "security_label";
|
|
11
|
-
readonly objectType
|
|
11
|
+
readonly objectType = "composite_type" as const;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export abstract class CreateCompositeTypeChange extends BaseCompositeTypeChange {
|
|
@@ -8,7 +8,7 @@ abstract class BaseRangeChange extends BaseChange {
|
|
|
8
8
|
| "comment"
|
|
9
9
|
| "privilege"
|
|
10
10
|
| "security_label";
|
|
11
|
-
readonly objectType
|
|
11
|
+
readonly objectType = "range" as const;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export abstract class CreateRangeChange extends BaseRangeChange {
|
|
@@ -322,7 +322,7 @@ function addClusterChange(cluster: ClusterGroup, change: Change): void {
|
|
|
322
322
|
break;
|
|
323
323
|
default: {
|
|
324
324
|
const _exhaustive: never = objectType;
|
|
325
|
-
throw new Error(`Unhandled object type: ${_exhaustive}`);
|
|
325
|
+
throw new Error(`Unhandled object type: ${JSON.stringify(_exhaustive)}`);
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
}
|
|
@@ -366,7 +366,7 @@ function addChildChange(schema: SchemaGroup, change: Change): void {
|
|
|
366
366
|
break;
|
|
367
367
|
default: {
|
|
368
368
|
const _exhaustive: never = parentType;
|
|
369
|
-
throw new Error(`Unhandled parent type: ${_exhaustive}`);
|
|
369
|
+
throw new Error(`Unhandled parent type: ${JSON.stringify(_exhaustive)}`);
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
372
|
|
|
@@ -416,7 +416,7 @@ function addChildChange(schema: SchemaGroup, change: Change): void {
|
|
|
416
416
|
break;
|
|
417
417
|
default: {
|
|
418
418
|
const _exhaustive: never = objectType;
|
|
419
|
-
throw new Error(`Unhandled object type: ${_exhaustive}`);
|
|
419
|
+
throw new Error(`Unhandled object type: ${JSON.stringify(_exhaustive)}`);
|
|
420
420
|
}
|
|
421
421
|
}
|
|
422
422
|
}
|
|
@@ -568,7 +568,7 @@ function addSchemaLevelChange(
|
|
|
568
568
|
break;
|
|
569
569
|
default: {
|
|
570
570
|
const _exhaustive: never = objectType;
|
|
571
|
-
throw new Error(`Unhandled object type: ${_exhaustive}`);
|
|
571
|
+
throw new Error(`Unhandled object type: ${JSON.stringify(_exhaustive)}`);
|
|
572
572
|
}
|
|
573
573
|
}
|
|
574
574
|
}
|
|
@@ -689,7 +689,7 @@ describe("sql formatting snapshots", () => {
|
|
|
689
689
|
COMMENT ON SUBSCRIPTION sub_replica IS NULL;
|
|
690
690
|
|
|
691
691
|
-- fdw.create
|
|
692
|
-
CREATE FOREIGN DATA WRAPPER postgres_fdw HANDLER postgres_fdw_handler VALIDATOR postgres_fdw_validator OPTIONS (debug '
|
|
692
|
+
CREATE FOREIGN DATA WRAPPER postgres_fdw HANDLER postgres_fdw_handler VALIDATOR postgres_fdw_validator OPTIONS (debug '__OPTION_DEBUG__');
|
|
693
693
|
|
|
694
694
|
-- fdw.drop
|
|
695
695
|
DROP FOREIGN DATA WRAPPER postgres_fdw;
|
|
@@ -698,7 +698,7 @@ describe("sql formatting snapshots", () => {
|
|
|
698
698
|
ALTER FOREIGN DATA WRAPPER postgres_fdw OWNER TO new_owner;
|
|
699
699
|
|
|
700
700
|
-- fdw.alter.set_options
|
|
701
|
-
ALTER FOREIGN DATA WRAPPER postgres_fdw OPTIONS (SET debug '
|
|
701
|
+
ALTER FOREIGN DATA WRAPPER postgres_fdw OPTIONS (SET debug '__OPTION_DEBUG__', ADD use_remote_estimate '');
|
|
702
702
|
|
|
703
703
|
-- fdw.comment
|
|
704
704
|
COMMENT ON FOREIGN DATA WRAPPER postgres_fdw IS 'PostgreSQL foreign data wrapper';
|
|
@@ -794,13 +794,13 @@ describe("sql formatting snapshots", () => {
|
|
|
794
794
|
REVOKE GRANT OPTION FOR ALL ON SERVER remote_server FROM app_user;
|
|
795
795
|
|
|
796
796
|
-- user_mapping.create
|
|
797
|
-
CREATE USER MAPPING FOR app_user SERVER remote_server OPTIONS (user 'remote_app', password '
|
|
797
|
+
CREATE USER MAPPING FOR app_user SERVER remote_server OPTIONS (user 'remote_app', password '__OPTION_PASSWORD__');
|
|
798
798
|
|
|
799
799
|
-- user_mapping.drop
|
|
800
800
|
DROP USER MAPPING FOR app_user SERVER remote_server;
|
|
801
801
|
|
|
802
802
|
-- user_mapping.alter.set_options
|
|
803
|
-
ALTER USER MAPPING FOR app_user SERVER remote_server OPTIONS (SET password '
|
|
803
|
+
ALTER USER MAPPING FOR app_user SERVER remote_server OPTIONS (SET password '__OPTION_PASSWORD__');"
|
|
804
804
|
`);
|
|
805
805
|
});
|
|
806
806
|
});
|
|
@@ -912,7 +912,7 @@ describe("sql formatting snapshots", () => {
|
|
|
912
912
|
create foreign data wrapper postgres_fdw
|
|
913
913
|
handler postgres_fdw_handler
|
|
914
914
|
validator postgres_fdw_validator
|
|
915
|
-
options (debug '
|
|
915
|
+
options (debug '__OPTION_DEBUG__');
|
|
916
916
|
|
|
917
917
|
-- fdw.drop
|
|
918
918
|
drop foreign data wrapper postgres_fdw;
|
|
@@ -924,7 +924,7 @@ describe("sql formatting snapshots", () => {
|
|
|
924
924
|
-- fdw.alter.set_options
|
|
925
925
|
alter foreign data wrapper postgres_fdw
|
|
926
926
|
options (
|
|
927
|
-
SET debug '
|
|
927
|
+
SET debug '__OPTION_DEBUG__'
|
|
928
928
|
, ADD use_remote_estimate ''
|
|
929
929
|
);
|
|
930
930
|
|
|
@@ -1049,13 +1049,13 @@ describe("sql formatting snapshots", () => {
|
|
|
1049
1049
|
|
|
1050
1050
|
-- user_mapping.create
|
|
1051
1051
|
create user mapping for app_user server remote_server
|
|
1052
|
-
options (user 'remote_app', password '
|
|
1052
|
+
options (user 'remote_app', password '__OPTION_PASSWORD__');
|
|
1053
1053
|
|
|
1054
1054
|
-- user_mapping.drop
|
|
1055
1055
|
drop user mapping for app_user server remote_server;
|
|
1056
1056
|
|
|
1057
1057
|
-- user_mapping.alter.set_options
|
|
1058
|
-
alter user mapping for app_user server remote_server options (SET password '
|
|
1058
|
+
alter user mapping for app_user server remote_server options (SET password '__OPTION_PASSWORD__');"
|
|
1059
1059
|
`);
|
|
1060
1060
|
});
|
|
1061
1061
|
});
|
|
@@ -1094,7 +1094,7 @@ describe("sql formatting snapshots", () => {
|
|
|
1094
1094
|
CREATE FOREIGN DATA WRAPPER postgres_fdw
|
|
1095
1095
|
HANDLER postgres_fdw_handler
|
|
1096
1096
|
VALIDATOR postgres_fdw_validator
|
|
1097
|
-
OPTIONS (debug '
|
|
1097
|
+
OPTIONS (debug '__OPTION_DEBUG__');
|
|
1098
1098
|
|
|
1099
1099
|
-- fdw.drop
|
|
1100
1100
|
DROP FOREIGN DATA WRAPPER postgres_fdw;
|
|
@@ -1106,7 +1106,7 @@ describe("sql formatting snapshots", () => {
|
|
|
1106
1106
|
-- fdw.alter.set_options
|
|
1107
1107
|
ALTER FOREIGN DATA WRAPPER postgres_fdw
|
|
1108
1108
|
OPTIONS (
|
|
1109
|
-
SET debug '
|
|
1109
|
+
SET debug '__OPTION_DEBUG__',
|
|
1110
1110
|
ADD use_remote_estimate ''
|
|
1111
1111
|
);
|
|
1112
1112
|
|
|
@@ -1264,7 +1264,7 @@ describe("sql formatting snapshots", () => {
|
|
|
1264
1264
|
remote_server
|
|
1265
1265
|
OPTIONS
|
|
1266
1266
|
(user 'remote_app', password
|
|
1267
|
-
'
|
|
1267
|
+
'__OPTION_PASSWORD__');
|
|
1268
1268
|
|
|
1269
1269
|
-- user_mapping.drop
|
|
1270
1270
|
DROP USER MAPPING FOR app_user SERVER
|
|
@@ -1273,7 +1273,8 @@ describe("sql formatting snapshots", () => {
|
|
|
1273
1273
|
-- user_mapping.alter.set_options
|
|
1274
1274
|
ALTER USER MAPPING FOR app_user SERVER
|
|
1275
1275
|
remote_server
|
|
1276
|
-
OPTIONS
|
|
1276
|
+
OPTIONS
|
|
1277
|
+
(SET password '__OPTION_PASSWORD__');"
|
|
1277
1278
|
`);
|
|
1278
1279
|
});
|
|
1279
1280
|
});
|
|
@@ -908,7 +908,7 @@ describe("sql formatting snapshots", () => {
|
|
|
908
908
|
CREATE FOREIGN DATA WRAPPER postgres_fdw
|
|
909
909
|
HANDLER postgres_fdw_handler
|
|
910
910
|
VALIDATOR postgres_fdw_validator
|
|
911
|
-
OPTIONS (debug '
|
|
911
|
+
OPTIONS (debug '__OPTION_DEBUG__');
|
|
912
912
|
|
|
913
913
|
-- fdw.drop
|
|
914
914
|
DROP FOREIGN DATA WRAPPER postgres_fdw;
|
|
@@ -920,7 +920,7 @@ describe("sql formatting snapshots", () => {
|
|
|
920
920
|
-- fdw.alter.set_options
|
|
921
921
|
ALTER FOREIGN DATA WRAPPER postgres_fdw
|
|
922
922
|
OPTIONS (
|
|
923
|
-
SET debug '
|
|
923
|
+
SET debug '__OPTION_DEBUG__',
|
|
924
924
|
ADD use_remote_estimate ''
|
|
925
925
|
);
|
|
926
926
|
|
|
@@ -1045,13 +1045,13 @@ describe("sql formatting snapshots", () => {
|
|
|
1045
1045
|
|
|
1046
1046
|
-- user_mapping.create
|
|
1047
1047
|
CREATE USER MAPPING FOR app_user SERVER remote_server
|
|
1048
|
-
OPTIONS (user 'remote_app', password '
|
|
1048
|
+
OPTIONS (user 'remote_app', password '__OPTION_PASSWORD__');
|
|
1049
1049
|
|
|
1050
1050
|
-- user_mapping.drop
|
|
1051
1051
|
DROP USER MAPPING FOR app_user SERVER remote_server;
|
|
1052
1052
|
|
|
1053
1053
|
-- user_mapping.alter.set_options
|
|
1054
|
-
ALTER USER MAPPING FOR app_user SERVER remote_server OPTIONS (SET password '
|
|
1054
|
+
ALTER USER MAPPING FOR app_user SERVER remote_server OPTIONS (SET password '__OPTION_PASSWORD__');"
|
|
1055
1055
|
`);
|
|
1056
1056
|
});
|
|
1057
1057
|
});
|
|
@@ -899,7 +899,7 @@ describe("sql formatting snapshots", () => {
|
|
|
899
899
|
CREATE FOREIGN DATA WRAPPER postgres_fdw
|
|
900
900
|
HANDLER postgres_fdw_handler
|
|
901
901
|
VALIDATOR postgres_fdw_validator
|
|
902
|
-
OPTIONS (debug '
|
|
902
|
+
OPTIONS (debug '__OPTION_DEBUG__');
|
|
903
903
|
|
|
904
904
|
-- fdw.drop
|
|
905
905
|
DROP FOREIGN DATA WRAPPER postgres_fdw;
|
|
@@ -911,7 +911,7 @@ describe("sql formatting snapshots", () => {
|
|
|
911
911
|
-- fdw.alter.set_options
|
|
912
912
|
ALTER FOREIGN DATA WRAPPER postgres_fdw
|
|
913
913
|
OPTIONS (
|
|
914
|
-
SET debug '
|
|
914
|
+
SET debug '__OPTION_DEBUG__',
|
|
915
915
|
ADD use_remote_estimate ''
|
|
916
916
|
);
|
|
917
917
|
|
|
@@ -1036,13 +1036,13 @@ describe("sql formatting snapshots", () => {
|
|
|
1036
1036
|
|
|
1037
1037
|
-- user_mapping.create
|
|
1038
1038
|
CREATE USER MAPPING FOR app_user SERVER remote_server
|
|
1039
|
-
OPTIONS (user 'remote_app', password '
|
|
1039
|
+
OPTIONS (user 'remote_app', password '__OPTION_PASSWORD__');
|
|
1040
1040
|
|
|
1041
1041
|
-- user_mapping.drop
|
|
1042
1042
|
DROP USER MAPPING FOR app_user SERVER remote_server;
|
|
1043
1043
|
|
|
1044
1044
|
-- user_mapping.alter.set_options
|
|
1045
|
-
ALTER USER MAPPING FOR app_user SERVER remote_server OPTIONS (SET password '
|
|
1045
|
+
ALTER USER MAPPING FOR app_user SERVER remote_server OPTIONS (SET password '__OPTION_PASSWORD__');"
|
|
1046
1046
|
`);
|
|
1047
1047
|
});
|
|
1048
1048
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
1
|
+
import { describe, expect, spyOn, test } from "bun:test";
|
|
2
2
|
import {
|
|
3
3
|
connectWithRetry,
|
|
4
|
+
connectWithTimeout,
|
|
4
5
|
isRetryableConnectError,
|
|
5
6
|
poolConfigFromUrl,
|
|
6
7
|
} from "./postgres-config.ts";
|
|
@@ -241,6 +242,43 @@ describe("connectWithRetry", () => {
|
|
|
241
242
|
});
|
|
242
243
|
});
|
|
243
244
|
|
|
245
|
+
describe("connectWithTimeout", () => {
|
|
246
|
+
test("clears the timer when connect resolves before it fires", async () => {
|
|
247
|
+
const clearSpy = spyOn(globalThis, "clearTimeout");
|
|
248
|
+
try {
|
|
249
|
+
const sentinel = { client: true };
|
|
250
|
+
const result = await connectWithTimeout(
|
|
251
|
+
() => Promise.resolve(sentinel),
|
|
252
|
+
60_000,
|
|
253
|
+
"source",
|
|
254
|
+
);
|
|
255
|
+
expect(result).toBe(sentinel);
|
|
256
|
+
expect(clearSpy).toHaveBeenCalled();
|
|
257
|
+
} finally {
|
|
258
|
+
clearSpy.mockRestore();
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test("rejects with a timeout error when connect is too slow", async () => {
|
|
263
|
+
await expect(
|
|
264
|
+
connectWithTimeout(() => new Promise<never>(() => {}), 5, "target"),
|
|
265
|
+
).rejects.toThrow(/timed out after 5ms/);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("clears the timer even when connect rejects", async () => {
|
|
269
|
+
const clearSpy = spyOn(globalThis, "clearTimeout");
|
|
270
|
+
try {
|
|
271
|
+
const boom = new Error("connect ECONNREFUSED");
|
|
272
|
+
await expect(
|
|
273
|
+
connectWithTimeout(() => Promise.reject(boom), 60_000, "target"),
|
|
274
|
+
).rejects.toBe(boom);
|
|
275
|
+
expect(clearSpy).toHaveBeenCalled();
|
|
276
|
+
} finally {
|
|
277
|
+
clearSpy.mockRestore();
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
244
282
|
describe("poolConfigFromUrl", () => {
|
|
245
283
|
describe("non-IPv6 URLs pass through as connectionString", () => {
|
|
246
284
|
test("DNS hostname", () => {
|
|
@@ -209,6 +209,37 @@ export async function connectWithRetry<T>(opts: {
|
|
|
209
209
|
throw lastError;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Race `connect()` against a `timeoutMs` rejection and clear the timer when
|
|
214
|
+
* either side wins. If the timer is left running after a fast connect, the
|
|
215
|
+
* pending `setTimeout` keeps the event loop alive and the process hangs for
|
|
216
|
+
* the rest of `timeoutMs`.
|
|
217
|
+
*/
|
|
218
|
+
export function connectWithTimeout<T>(
|
|
219
|
+
connect: () => Promise<T>,
|
|
220
|
+
timeoutMs: number,
|
|
221
|
+
label: "source" | "target",
|
|
222
|
+
): Promise<T> {
|
|
223
|
+
let timer: ReturnType<typeof setTimeout>;
|
|
224
|
+
return Promise.race([
|
|
225
|
+
connect(),
|
|
226
|
+
new Promise<never>((_, reject) => {
|
|
227
|
+
timer = setTimeout(
|
|
228
|
+
() =>
|
|
229
|
+
reject(
|
|
230
|
+
new Error(
|
|
231
|
+
`Connection to ${label} database timed out after ${timeoutMs}ms. ` +
|
|
232
|
+
`The server may require SSL, use an invalid certificate, or be unreachable.`,
|
|
233
|
+
),
|
|
234
|
+
),
|
|
235
|
+
timeoutMs,
|
|
236
|
+
);
|
|
237
|
+
}),
|
|
238
|
+
]).finally(() => {
|
|
239
|
+
clearTimeout(timer);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
212
243
|
/**
|
|
213
244
|
* Options for creating a Pool with event listeners.
|
|
214
245
|
*/
|
|
@@ -412,22 +443,7 @@ export async function createManagedPool(
|
|
|
412
443
|
const timeoutMs = DEFAULT_CONNECT_TIMEOUT_MS;
|
|
413
444
|
try {
|
|
414
445
|
const client = await connectWithRetry({
|
|
415
|
-
connect: () =>
|
|
416
|
-
Promise.race([
|
|
417
|
-
pool.connect(),
|
|
418
|
-
new Promise<never>((_, reject) =>
|
|
419
|
-
setTimeout(
|
|
420
|
-
() =>
|
|
421
|
-
reject(
|
|
422
|
-
new Error(
|
|
423
|
-
`Connection to ${label} database timed out after ${timeoutMs}ms. ` +
|
|
424
|
-
`The server may require SSL, use an invalid certificate, or be unreachable.`,
|
|
425
|
-
),
|
|
426
|
-
),
|
|
427
|
-
timeoutMs,
|
|
428
|
-
),
|
|
429
|
-
),
|
|
430
|
-
]),
|
|
446
|
+
connect: () => connectWithTimeout(() => pool.connect(), timeoutMs, label),
|
|
431
447
|
});
|
|
432
448
|
client.release();
|
|
433
449
|
} catch (err) {
|
|
@@ -144,6 +144,7 @@ function table(
|
|
|
144
144
|
|
|
145
145
|
async function catalogWithDepends(depends: PgDepend[]) {
|
|
146
146
|
const base = await createEmptyCatalog(170000, "postgres");
|
|
147
|
+
// oxlint-disable-next-line typescript/no-misused-spread
|
|
147
148
|
return new Catalog({ ...base, depends });
|
|
148
149
|
}
|
|
149
150
|
|
|
@@ -14,7 +14,7 @@ export function performStableTopologicalSort(
|
|
|
14
14
|
{ length: nodeCount },
|
|
15
15
|
() => new Set<number>(),
|
|
16
16
|
);
|
|
17
|
-
const inDegreeCounts =
|
|
17
|
+
const inDegreeCounts: number[] = Array.from({ length: nodeCount }, () => 0);
|
|
18
18
|
|
|
19
19
|
for (const [sourceIndex, targetIndex] of edges) {
|
|
20
20
|
if (!adjacencyList[sourceIndex].has(targetIndex)) {
|
|
@@ -73,7 +73,7 @@ export function findCycle(
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// 0 = unvisited, 1 = visiting, 2 = completed
|
|
76
|
-
const visitState =
|
|
76
|
+
const visitState: number[] = Array.from({ length: nodeCount }, () => 0);
|
|
77
77
|
const pathStack: number[] = [];
|
|
78
78
|
let cycleNodeIndexes: number[] | null = null;
|
|
79
79
|
|