@linkforty/core 1.41.2 → 1.41.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -670,6 +670,21 @@ await fastify.register(linkRoutes);
670
670
  await fastify.listen({ port: 3000 });
671
671
  ```
672
672
 
673
+ #### Starting while the database is busy
674
+
675
+ `initializeDatabase()` creates or updates the schema on every start. Postgres takes a table's lock *before* it evaluates `IF NOT EXISTS`, so a statement such as `ALTER TABLE links ADD COLUMN IF NOT EXISTS …` for a column that already exists would still request an exclusive lock on `links` — and if a long statement (a retention job, a large export) holds that table, the request waits, and every later query on the table, redirects included, queues behind it until the long statement ends. To keep a routine start from ever doing that, statements whose effect already exists are answered from the catalog and never sent, and you can bound the wait for the rest:
676
+
677
+ ```typescript
678
+ await initializeDatabase({
679
+ url: 'postgresql://localhost/linkforty',
680
+ // A schema change that waits longer than this fails the start with
681
+ // SQLSTATE 55P03 rather than queueing the table. Retry or restart.
682
+ lockTimeoutMs: 5000,
683
+ });
684
+ ```
685
+
686
+ The timeout applies only to the initialisation session and is reset before that connection returns to the pool. When adding schema statements of your own, use the idempotent shapes (`ADD COLUMN IF NOT EXISTS`, `CREATE INDEX IF NOT EXISTS`, `DROP CONSTRAINT IF EXISTS`) or a `DO $$ … IF NOT EXISTS (SELECT … FROM information_schema …)` block, never a bare `ALTER` that asks for a lock on every start.
687
+
673
688
  ## Deployment
674
689
 
675
690
  LinkForty can be deployed in multiple ways depending on your needs:
@@ -5,6 +5,21 @@ export interface DatabaseOptions {
5
5
  min?: number;
6
6
  max?: number;
7
7
  };
8
+ /**
9
+ * How long a schema statement at initialisation may wait for a table lock,
10
+ * in milliseconds. Unset (the default) means it waits as long as Postgres
11
+ * lets it.
12
+ *
13
+ * Postgres takes a table's lock before it evaluates `IF NOT EXISTS`, so an
14
+ * `ALTER TABLE … ADD COLUMN IF NOT EXISTS` for a column that already exists
15
+ * still asks for ACCESS EXCLUSIVE. Statements whose effect already exists
16
+ * are answered from the catalog and never sent, so this only matters for
17
+ * statements that genuinely change the schema. When one of those waits
18
+ * longer than this, `initializeDatabase()` fails with SQLSTATE `55P03`
19
+ * instead of parking a lock request that every later query on the table
20
+ * would queue behind; the host decides whether to retry or restart.
21
+ */
22
+ lockTimeoutMs?: number;
8
23
  }
9
24
  export declare let db: pg.Pool;
10
25
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/lib/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE;QACL,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,eAAO,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC;AA4BvB;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C,yFAAyF;AACzF,eAAO,MAAM,6BAA6B,UAAU,CAAC;AAErD,oGAAoG;AACpG,eAAO,MAAM,2BAA2B,UAAU,CAAC;AAGnD,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,eAAoB,iBAouBrE"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/lib/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAKpB,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE;QACL,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;IACF;;;;;;;;;;;;;OAaG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,eAAO,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC;AA4BvB;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAE5C,yFAAyF;AACzF,eAAO,MAAM,6BAA6B,UAAU,CAAC;AAErD,oGAAoG;AACpG,eAAO,MAAM,2BAA2B,UAAU,CAAC;AAGnD,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,eAAoB,iBAuwBrE"}
@@ -1,4 +1,5 @@
1
1
  import pg from 'pg';
2
+ import { guardSchemaClient, isLockTimeout } from './schema-guard.js';
2
3
  const { Pool } = pg;
3
4
  export let db;
4
5
  // Helper function to wait for a specified time
@@ -54,7 +55,15 @@ export async function initializeDatabase(options = {}) {
54
55
  db.on('error', (error) => {
55
56
  console.error('Database connection lost while idle; it will be replaced:', error.message);
56
57
  });
57
- const client = await connectWithRetry();
58
+ const raw = await connectWithRetry();
59
+ // Idempotent statements whose effect already exists (a column, an index, an
60
+ // absent constraint, a settled NOT NULL) are answered from the catalog and
61
+ // never reach Postgres, so a routine start never asks for a table lock it
62
+ // does not need. See schema-guard.ts for why that matters.
63
+ const client = guardSchemaClient(raw);
64
+ const lockTimeoutMs = Number.isFinite(options.lockTimeoutMs) && options.lockTimeoutMs > 0
65
+ ? Math.trunc(options.lockTimeoutMs)
66
+ : null;
58
67
  // Serialise schema initialisation across processes.
59
68
  //
60
69
  // `CREATE TABLE IF NOT EXISTS` is not atomic: the existence check and the
@@ -79,6 +88,12 @@ export async function initializeDatabase(options = {}) {
79
88
  try {
80
89
  await client.query('SELECT pg_advisory_lock($1)', [SCHEMA_INIT_LOCK_KEY]);
81
90
  lockHeld = true;
91
+ // Set after the advisory lock is held: waiting for another instance's
92
+ // initialisation is wanted, waiting for a table lock is not. Session-level
93
+ // on this client only; RESET below before it goes back to the pool.
94
+ if (lockTimeoutMs !== null) {
95
+ await raw.query(`SET lock_timeout = ${lockTimeoutMs}`);
96
+ }
82
97
  // Organizations table (must be created before links, which references it).
83
98
  //
84
99
  // The redirect path LEFT JOINs this table to read `settings.appConfig`, which
@@ -705,13 +720,32 @@ export async function initializeDatabase(options = {}) {
705
720
  END IF;
706
721
  END $$;
707
722
  `);
708
- console.log('Database schema initialized successfully');
723
+ if (client.skipped.length > 0) {
724
+ console.log(`Database schema initialized successfully (${client.skipped.length} statement(s) already applied, not re-sent)`);
725
+ }
726
+ else {
727
+ console.log('Database schema initialized successfully');
728
+ }
709
729
  }
710
730
  catch (error) {
731
+ if (isLockTimeout(error)) {
732
+ // Say what happened in operator terms and keep the SQLSTATE so a host
733
+ // can recognise it and retry the start with backoff.
734
+ const wrapped = new Error(`Schema initialisation waited ${lockTimeoutMs} ms for a table lock and gave up ` +
735
+ `(another statement is holding the table; retry the start once it finishes): ${error.message}`);
736
+ wrapped.code = error.code;
737
+ console.error('Error initializing database:', wrapped.message);
738
+ throw wrapped;
739
+ }
711
740
  console.error('Error initializing database:', error);
712
741
  throw error;
713
742
  }
714
743
  finally {
744
+ if (lockTimeoutMs !== null) {
745
+ // The client returns to the shared pool: the timeout must not follow it
746
+ // into request handling.
747
+ await raw.query('RESET lock_timeout').catch(() => undefined);
748
+ }
715
749
  if (lockHeld) {
716
750
  // Best effort: a broken connection has already dropped the lock, and
717
751
  // failing to unlock must not mask the error that got us here.
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/lib/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/lib/database.ts"],"names":[],"mappings":"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"}
@@ -77,6 +77,79 @@ describe.skipIf(!ADMIN_URL)('initializeDatabase schema lock', () => {
77
77
  }
78
78
  }, 30000);
79
79
  });
80
+ describe.skipIf(!ADMIN_URL)('initialisation against a table someone is reading', () => {
81
+ // Postgres takes a table's lock before it evaluates IF NOT EXISTS, so a
82
+ // second start used to park an ACCESS EXCLUSIVE request on `links` behind
83
+ // any long reader — and every redirect behind that. With the catalog guard
84
+ // nothing is sent for a column that exists, and with a lock timeout a
85
+ // statement that does need the table gives up instead of queueing it.
86
+ const scratchName = `lf_core_schema_guard_${Date.now()}`;
87
+ let admin;
88
+ let scratchUrl;
89
+ const pools = [];
90
+ beforeAll(async () => {
91
+ admin = new pg.Client({ connectionString: ADMIN_URL });
92
+ await admin.connect();
93
+ await admin.query(`CREATE DATABASE ${scratchName}`);
94
+ const url = new URL(ADMIN_URL);
95
+ url.pathname = `/${scratchName}`;
96
+ scratchUrl = url.toString();
97
+ // First start: every table, column and index exists afterwards.
98
+ await initializeDatabase({ url: scratchUrl, pool: { min: 1, max: 2 } });
99
+ pools.push(db);
100
+ }, 60000);
101
+ afterAll(async () => {
102
+ for (const p of pools)
103
+ await p.end().catch(() => undefined);
104
+ await db?.end().catch(() => undefined);
105
+ await admin.query(`DROP DATABASE IF EXISTS ${scratchName} WITH (FORCE)`);
106
+ await admin.end();
107
+ }, 30000);
108
+ it('a second start completes while a transaction holds ACCESS SHARE on links, and the setting does not leak', async () => {
109
+ const reader = new pg.Client({ connectionString: scratchUrl });
110
+ await reader.connect();
111
+ try {
112
+ await reader.query('BEGIN');
113
+ await reader.query('SELECT 1 FROM links LIMIT 1');
114
+ const started = Date.now();
115
+ await initializeDatabase({ url: scratchUrl, pool: { min: 1, max: 2 }, lockTimeoutMs: 2000 });
116
+ pools.push(db);
117
+ // Nothing waited: well under a single lock timeout.
118
+ expect(Date.now() - started).toBeLessThan(10000);
119
+ // The reader was never blocked either.
120
+ const t = Date.now();
121
+ await reader.query('SELECT 1 FROM links LIMIT 1');
122
+ expect(Date.now() - t).toBeLessThan(1000);
123
+ // The timeout was session-level on the initialisation client and was
124
+ // reset before that client went back to the pool.
125
+ const { rows } = await db.query('SHOW lock_timeout');
126
+ expect(rows[0].lock_timeout).toBe('0');
127
+ }
128
+ finally {
129
+ await reader.query('ROLLBACK').catch(() => undefined);
130
+ await reader.end();
131
+ }
132
+ }, 60000);
133
+ it('a statement that does need the table fails with 55P03 after the timeout instead of queueing it', async () => {
134
+ const reader = new pg.Client({ connectionString: scratchUrl });
135
+ const other = new pg.Client({ connectionString: scratchUrl });
136
+ await reader.connect();
137
+ await other.connect();
138
+ try {
139
+ await reader.query('BEGIN');
140
+ await reader.query('SELECT 1 FROM links LIMIT 1');
141
+ await other.query('SET lock_timeout = 1000');
142
+ const t = Date.now();
143
+ await expect(other.query('ALTER TABLE links ADD COLUMN probe_column_not_guarded INTEGER')).rejects.toMatchObject({ code: '55P03' });
144
+ expect(Date.now() - t).toBeLessThan(5000);
145
+ }
146
+ finally {
147
+ await reader.query('ROLLBACK').catch(() => undefined);
148
+ await reader.end();
149
+ await other.end();
150
+ }
151
+ }, 30000);
152
+ });
80
153
  describe.skipIf(!ADMIN_URL)('idle connection errors', () => {
81
154
  const scratchName = `lf_core_idle_error_${Date.now()}`;
82
155
  let admin;
@@ -1 +1 @@
1
- {"version":3,"file":"database.test.js","sourceRoot":"","sources":["../../src/lib/database.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.test.js","sourceRoot":"","sources":["../../src/lib/database.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type pg from 'pg';
2
+ /**
3
+ * Schema statements that never ask Postgres for a lock they do not need.
4
+ *
5
+ * Postgres acquires a table's lock *before* it evaluates `IF NOT EXISTS`. An
6
+ * `ALTER TABLE … ADD COLUMN IF NOT EXISTS` for a column that has existed for a
7
+ * year still requests ACCESS EXCLUSIVE on the table; `CREATE INDEX IF NOT
8
+ * EXISTS` for an index that exists still requests SHARE. If anything long is
9
+ * reading or writing the table at that moment — a retention job, a large
10
+ * export, an analytics rebuild — the request waits, and Postgres then queues
11
+ * every later request on the table behind the waiting one. A routine start of
12
+ * a second instance becomes a stall of the whole table for as long as the long
13
+ * statement runs; redirects included, since they read `links`.
14
+ *
15
+ * `guardSchemaClient` wraps the initialisation client and answers the
16
+ * idempotent statement shapes from the catalog first: a column that exists, an
17
+ * index that exists, a constraint that is already gone, a NOT NULL that is
18
+ * already set or dropped. Those statements never reach Postgres, so they never
19
+ * ask for a lock. Anything else — a new column, a `CREATE TABLE`, a `DO`
20
+ * block, a parameterised query, a string holding several statements — passes
21
+ * through untouched.
22
+ */
23
+ /** SQLSTATE for "canceling statement due to lock timeout". */
24
+ export declare const LOCK_NOT_AVAILABLE = "55P03";
25
+ export interface Queryable {
26
+ query: (text: string, params?: any[]) => Promise<pg.QueryResult<any>>;
27
+ }
28
+ export interface SchemaClient extends Queryable {
29
+ release: () => void;
30
+ /** Statements answered from the catalog instead of being sent to Postgres. */
31
+ readonly skipped: string[];
32
+ }
33
+ export declare function isLockTimeout(error: unknown): boolean;
34
+ /**
35
+ * Decide whether a statement's effect already exists. Returns the reason to
36
+ * skip it, or null to send it to Postgres.
37
+ */
38
+ export declare function alreadyApplied(client: Queryable, text: string): Promise<string | null>;
39
+ /**
40
+ * A client whose idempotent schema statements are answered from the catalog
41
+ * when their effect already exists, so they never ask Postgres for a lock.
42
+ */
43
+ export declare function guardSchemaClient(client: Queryable & {
44
+ release: () => void;
45
+ }): SchemaClient;
46
+ //# sourceMappingURL=schema-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-guard.d.ts","sourceRoot":"","sources":["../../src/lib/schema-guard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,8DAA8D;AAC9D,eAAO,MAAM,kBAAkB,UAAU,CAAC;AAE1C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;CACvE;AAED,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAErD;AAwDD;;;GAGG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuC5F;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG;IAAE,OAAO,EAAE,MAAM,IAAI,CAAA;CAAE,GAAG,YAAY,CAgB3F"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Schema statements that never ask Postgres for a lock they do not need.
3
+ *
4
+ * Postgres acquires a table's lock *before* it evaluates `IF NOT EXISTS`. An
5
+ * `ALTER TABLE … ADD COLUMN IF NOT EXISTS` for a column that has existed for a
6
+ * year still requests ACCESS EXCLUSIVE on the table; `CREATE INDEX IF NOT
7
+ * EXISTS` for an index that exists still requests SHARE. If anything long is
8
+ * reading or writing the table at that moment — a retention job, a large
9
+ * export, an analytics rebuild — the request waits, and Postgres then queues
10
+ * every later request on the table behind the waiting one. A routine start of
11
+ * a second instance becomes a stall of the whole table for as long as the long
12
+ * statement runs; redirects included, since they read `links`.
13
+ *
14
+ * `guardSchemaClient` wraps the initialisation client and answers the
15
+ * idempotent statement shapes from the catalog first: a column that exists, an
16
+ * index that exists, a constraint that is already gone, a NOT NULL that is
17
+ * already set or dropped. Those statements never reach Postgres, so they never
18
+ * ask for a lock. Anything else — a new column, a `CREATE TABLE`, a `DO`
19
+ * block, a parameterised query, a string holding several statements — passes
20
+ * through untouched.
21
+ */
22
+ /** SQLSTATE for "canceling statement due to lock timeout". */
23
+ export const LOCK_NOT_AVAILABLE = '55P03';
24
+ export function isLockTimeout(error) {
25
+ return typeof error === 'object' && error !== null && error.code === LOCK_NOT_AVAILABLE;
26
+ }
27
+ // One statement only: a trailing semicolon is fine, a second statement is not
28
+ // ours to interpret, so it passes through.
29
+ function single(text) {
30
+ const t = text.trim().replace(/;\s*$/, '');
31
+ return t.includes(';') ? null : t;
32
+ }
33
+ const ident = (raw) => raw.replace(/^"|"$/g, '');
34
+ /** `schema.table` → the table part; the catalog lookups scope to the search path. */
35
+ const tableOf = (raw) => ident(raw.split('.').pop() ?? raw);
36
+ const ALTER_TABLE = /^ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?("?[\w.]+"?)\s+([\s\S]+)$/i;
37
+ const ADD_COLUMN_ACTION = /^\s*ADD\s+COLUMN\s+IF\s+NOT\s+EXISTS\s+("?\w+"?)\b/i;
38
+ // Actions of one ALTER TABLE are split only at a comma that starts another
39
+ // ADD COLUMN IF NOT EXISTS, so commas inside a type (NUMERIC(18,4)), a default
40
+ // or a REFERENCES list stay put.
41
+ const ACTION_SEPARATOR = /,(?=\s*ADD\s+COLUMN\s+IF\s+NOT\s+EXISTS\b)/i;
42
+ const CREATE_INDEX = /^CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:CONCURRENTLY\s+)?IF\s+NOT\s+EXISTS\s+("?\w+"?)\s+ON\b/i;
43
+ const DROP_CONSTRAINT = /^ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?("?[\w.]+"?)\s+DROP\s+CONSTRAINT\s+IF\s+EXISTS\s+("?\w+"?)\s*$/i;
44
+ const SET_NOT_NULL = /^ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?("?[\w.]+"?)\s+ALTER\s+COLUMN\s+("?\w+"?)\s+SET\s+NOT\s+NULL\s*$/i;
45
+ const DROP_NOT_NULL = /^ALTER\s+TABLE\s+(?:IF\s+EXISTS\s+)?("?[\w.]+"?)\s+ALTER\s+COLUMN\s+("?\w+"?)\s+DROP\s+NOT\s+NULL\s*$/i;
46
+ /** True when a comma remains once quoted strings and parenthesised groups are removed: a second action hides in there. */
47
+ function hasTopLevelComma(action) {
48
+ let s = action.replace(/'[^']*'/g, '');
49
+ for (let prev = ''; prev !== s;) {
50
+ prev = s;
51
+ s = s.replace(/\([^()]*\)/g, '');
52
+ }
53
+ return s.includes(',');
54
+ }
55
+ /** The column names when every action of the ALTER is ADD COLUMN IF NOT EXISTS; null otherwise. */
56
+ function addColumnNames(t) {
57
+ const m = t.match(ALTER_TABLE);
58
+ if (!m)
59
+ return null;
60
+ const columns = [];
61
+ for (const action of m[2].split(ACTION_SEPARATOR)) {
62
+ const a = action.match(ADD_COLUMN_ACTION);
63
+ if (!a || hasTopLevelComma(action))
64
+ return null;
65
+ columns.push(ident(a[1]));
66
+ }
67
+ return columns.length > 0 ? { table: tableOf(m[1]), columns } : null;
68
+ }
69
+ async function columnNullable(client, table, column) {
70
+ const r = await client.query(`SELECT is_nullable FROM information_schema.columns
71
+ WHERE table_schema = ANY(current_schemas(false)) AND table_name = $1 AND column_name = $2`, [table, column]);
72
+ return r.rows[0]?.is_nullable ?? null;
73
+ }
74
+ /**
75
+ * Decide whether a statement's effect already exists. Returns the reason to
76
+ * skip it, or null to send it to Postgres.
77
+ */
78
+ export async function alreadyApplied(client, text) {
79
+ const t = single(text);
80
+ if (!t)
81
+ return null;
82
+ const add = addColumnNames(t);
83
+ if (add) {
84
+ // Skip only when every column exists; Postgres handles a mix itself.
85
+ for (const column of add.columns) {
86
+ if ((await columnNullable(client, add.table, column)) === null)
87
+ return null;
88
+ }
89
+ return `column${add.columns.length > 1 ? 's' : ''} ${add.table}.${add.columns.join(', ')} exist${add.columns.length > 1 ? '' : 's'}`;
90
+ }
91
+ let m = t.match(CREATE_INDEX);
92
+ if (m) {
93
+ const r = await client.query(`SELECT 1 FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
94
+ WHERE c.relkind = 'i' AND c.relname = $1 AND n.nspname = ANY(current_schemas(false))`, [ident(m[1])]);
95
+ return r.rows.length > 0 ? `index ${ident(m[1])} exists` : null;
96
+ }
97
+ m = t.match(DROP_CONSTRAINT);
98
+ if (m) {
99
+ const r = await client.query(`SELECT 1 FROM pg_constraint con JOIN pg_class c ON c.oid = con.conrelid
100
+ WHERE con.conname = $1 AND c.relname = $2`, [ident(m[2]), tableOf(m[1])]);
101
+ return r.rows.length === 0 ? `constraint ${ident(m[2])} already absent` : null;
102
+ }
103
+ m = t.match(SET_NOT_NULL);
104
+ if (m) {
105
+ return (await columnNullable(client, tableOf(m[1]), ident(m[2]))) === 'NO' ? `${tableOf(m[1])}.${ident(m[2])} already NOT NULL` : null;
106
+ }
107
+ m = t.match(DROP_NOT_NULL);
108
+ if (m) {
109
+ return (await columnNullable(client, tableOf(m[1]), ident(m[2]))) === 'YES' ? `${tableOf(m[1])}.${ident(m[2])} already nullable` : null;
110
+ }
111
+ return null;
112
+ }
113
+ const SKIPPED = { rows: [], rowCount: 0, command: 'SKIP', oid: 0, fields: [] };
114
+ /**
115
+ * A client whose idempotent schema statements are answered from the catalog
116
+ * when their effect already exists, so they never ask Postgres for a lock.
117
+ */
118
+ export function guardSchemaClient(client) {
119
+ const skipped = [];
120
+ return {
121
+ skipped,
122
+ release: () => client.release(),
123
+ async query(text, params) {
124
+ if (!params || params.length === 0) {
125
+ const why = await alreadyApplied(client, text);
126
+ if (why) {
127
+ skipped.push(why);
128
+ return SKIPPED;
129
+ }
130
+ }
131
+ return client.query(text, params);
132
+ },
133
+ };
134
+ }
135
+ //# sourceMappingURL=schema-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-guard.js","sourceRoot":"","sources":["../../src/lib/schema-guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=schema-guard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-guard.test.d.ts","sourceRoot":"","sources":["../../src/lib/schema-guard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,83 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { LOCK_NOT_AVAILABLE, alreadyApplied, guardSchemaClient, isLockTimeout } from './schema-guard.js';
3
+ function fakeClient(catalog) {
4
+ const query = vi.fn(async (text, params) => {
5
+ if (text.includes('information_schema.columns')) {
6
+ const hit = (catalog.columns ?? []).find(([t, c]) => t === params[0] && c === params[1]);
7
+ return { rows: hit ? [{ is_nullable: hit[2] }] : [], rowCount: hit ? 1 : 0 };
8
+ }
9
+ if (text.includes('pg_class c JOIN pg_namespace')) {
10
+ return { rows: (catalog.indexes ?? []).includes(params[0]) ? [{}] : [], rowCount: 0 };
11
+ }
12
+ if (text.includes('pg_constraint')) {
13
+ const hit = (catalog.constraints ?? []).some(([n, t]) => n === params[0] && t === params[1]);
14
+ return { rows: hit ? [{}] : [], rowCount: 0 };
15
+ }
16
+ return { rows: [], rowCount: 0, command: 'SENT' };
17
+ });
18
+ return { query, release: vi.fn() };
19
+ }
20
+ describe('alreadyApplied', () => {
21
+ it('answers an existing column, index, absent constraint and settled NOT NULL from the catalog', async () => {
22
+ const c = fakeClient({
23
+ columns: [['links', 'append_click_id', 'YES'], ['links', 'warn_at', 'YES'], ['users', 'password_hash', 'YES']],
24
+ indexes: ['idx_clicks_link_id'],
25
+ constraints: [['present_key', 'events']],
26
+ });
27
+ expect(await alreadyApplied(c, 'ALTER TABLE links ADD COLUMN IF NOT EXISTS append_click_id BOOLEAN DEFAULT false')).toMatch(/exists/);
28
+ expect(await alreadyApplied(c, ' ALTER TABLE "links" ADD COLUMN IF NOT EXISTS "append_click_id" BOOLEAN;\n')).toMatch(/exists/);
29
+ expect(await alreadyApplied(c, 'ALTER TABLE public.links ADD COLUMN IF NOT EXISTS warn_at TIMESTAMPTZ')).toMatch(/exists/);
30
+ expect(await alreadyApplied(c, 'ALTER TABLE links\n ADD COLUMN IF NOT EXISTS append_click_id BOOLEAN,\n ADD COLUMN IF NOT EXISTS warn_at TIMESTAMPTZ')).toBe('columns links.append_click_id, warn_at exist');
31
+ expect(await alreadyApplied(c, 'CREATE INDEX IF NOT EXISTS idx_clicks_link_id ON click_events(link_id)')).toMatch(/exists/);
32
+ expect(await alreadyApplied(c, 'CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS idx_clicks_link_id ON click_events(link_id)')).toMatch(/exists/);
33
+ expect(await alreadyApplied(c, 'ALTER TABLE events DROP CONSTRAINT IF EXISTS gone_key')).toMatch(/absent/);
34
+ expect(await alreadyApplied(c, 'ALTER TABLE users ALTER COLUMN password_hash DROP NOT NULL')).toMatch(/already nullable/);
35
+ });
36
+ it('sends statements whose effect is missing, and anything it does not understand', async () => {
37
+ const c = fakeClient({ columns: [['users', 'password_hash', 'NO'], ['links', 'warn_at', 'YES']], constraints: [['present_key', 'events']] });
38
+ expect(await alreadyApplied(c, 'ALTER TABLE links ADD COLUMN IF NOT EXISTS brand_new TEXT')).toBeNull();
39
+ expect(await alreadyApplied(c, 'ALTER TABLE links ADD COLUMN IF NOT EXISTS warn_at TIMESTAMPTZ, ADD COLUMN IF NOT EXISTS brand_new INTEGER NOT NULL DEFAULT 0')).toBeNull();
40
+ expect(await alreadyApplied(c, 'CREATE INDEX IF NOT EXISTS idx_missing ON links(id)')).toBeNull();
41
+ expect(await alreadyApplied(c, 'ALTER TABLE events DROP CONSTRAINT IF EXISTS present_key')).toBeNull();
42
+ expect(await alreadyApplied(c, 'ALTER TABLE users ALTER COLUMN password_hash DROP NOT NULL')).toBeNull();
43
+ expect(await alreadyApplied(c, 'ALTER TABLE users ALTER COLUMN password_hash SET NOT NULL')).toMatch(/already NOT NULL/);
44
+ // A comma inside a type or default does not split the action.
45
+ expect(await alreadyApplied(c, 'ALTER TABLE links ADD COLUMN IF NOT EXISTS warn_at NUMERIC(18,4) DEFAULT 0')).toMatch(/exist/);
46
+ // Shapes that are not idempotent are never guessed at.
47
+ expect(await alreadyApplied(c, 'ALTER TABLE links ADD COLUMN brand_new TEXT')).toBeNull();
48
+ expect(await alreadyApplied(c, 'ALTER TABLE links ADD COLUMN IF NOT EXISTS warn_at TIMESTAMPTZ, ALTER COLUMN warn_at SET DEFAULT NULL')).toBeNull();
49
+ expect(await alreadyApplied(c, 'ALTER TABLE links ALTER COLUMN x SET DEFAULT 1')).toBeNull();
50
+ expect(await alreadyApplied(c, 'CREATE TABLE IF NOT EXISTS t (id int)')).toBeNull();
51
+ expect(await alreadyApplied(c, "DO $$ BEGIN ALTER TABLE links ADD COLUMN IF NOT EXISTS warn_at TEXT; END $$")).toBeNull();
52
+ expect(await alreadyApplied(c, 'ALTER TABLE links ADD COLUMN IF NOT EXISTS a TEXT; ALTER TABLE links ADD COLUMN IF NOT EXISTS b TEXT')).toBeNull();
53
+ });
54
+ });
55
+ describe('guardSchemaClient', () => {
56
+ it('skips what exists, passes the rest through, records why, and releases the real client', async () => {
57
+ const raw = fakeClient({ columns: [['links', 'warn_at', 'YES']] });
58
+ const client = guardSchemaClient(raw);
59
+ expect((await client.query('ALTER TABLE links ADD COLUMN IF NOT EXISTS warn_at TIMESTAMPTZ')).command).toBe('SKIP');
60
+ expect((await client.query('ALTER TABLE links ADD COLUMN IF NOT EXISTS brand_new TEXT')).command).toBe('SENT');
61
+ await client.query('SELECT pg_advisory_lock($1)', [1]);
62
+ expect(client.skipped).toEqual(['column links.warn_at exists']);
63
+ expect(raw.query.mock.calls.map((c) => String(c[0]).slice(0, 30))).toEqual([
64
+ 'SELECT is_nullable FROM inform',
65
+ 'SELECT is_nullable FROM inform',
66
+ 'ALTER TABLE links ADD COLUMN I',
67
+ 'SELECT pg_advisory_lock($1)',
68
+ ]);
69
+ client.release();
70
+ expect(raw.release).toHaveBeenCalledTimes(1);
71
+ });
72
+ it('never inspects parameterised statements', async () => {
73
+ const raw = fakeClient({ columns: [['links', 'x', 'YES']] });
74
+ await guardSchemaClient(raw).query('ALTER TABLE links ADD COLUMN IF NOT EXISTS x TEXT', [1]);
75
+ expect(raw.query).toHaveBeenCalledTimes(1);
76
+ });
77
+ it('recognises a lock timeout by SQLSTATE', () => {
78
+ expect(isLockTimeout(Object.assign(new Error('x'), { code: LOCK_NOT_AVAILABLE }))).toBe(true);
79
+ expect(isLockTimeout(new Error('x'))).toBe(false);
80
+ expect(isLockTimeout(null)).toBe(false);
81
+ });
82
+ });
83
+ //# sourceMappingURL=schema-guard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-guard.test.js","sourceRoot":"","sources":["../../src/lib/schema-guard.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linkforty/core",
3
- "version": "1.41.2",
3
+ "version": "1.41.3",
4
4
  "type": "module",
5
5
  "description": "Open-source deeplink management engine with device detection and analytics",
6
6
  "main": "dist/index.js",