@linkforty/core 1.41.1 → 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 +18 -1
- package/dist/lib/database.d.ts +15 -0
- package/dist/lib/database.d.ts.map +1 -1
- package/dist/lib/database.js +36 -2
- package/dist/lib/database.js.map +1 -1
- package/dist/lib/database.test.js +73 -0
- package/dist/lib/database.test.js.map +1 -1
- package/dist/lib/link-lookup.d.ts +18 -5
- package/dist/lib/link-lookup.d.ts.map +1 -1
- package/dist/lib/link-lookup.js +13 -1
- package/dist/lib/link-lookup.js.map +1 -1
- package/dist/lib/link-lookup.test.js +9 -1
- package/dist/lib/link-lookup.test.js.map +1 -1
- package/dist/lib/schema-guard.d.ts +46 -0
- package/dist/lib/schema-guard.d.ts.map +1 -0
- package/dist/lib/schema-guard.js +135 -0
- package/dist/lib/schema-guard.js.map +1 -0
- package/dist/lib/schema-guard.test.d.ts +2 -0
- package/dist/lib/schema-guard.test.d.ts.map +1 -0
- package/dist/lib/schema-guard.test.js +83 -0
- package/dist/lib/schema-guard.test.js.map +1 -0
- package/dist/routes/preview.d.ts.map +1 -1
- package/dist/routes/preview.js +7 -4
- package/dist/routes/preview.js.map +1 -1
- package/dist/routes/redirect.d.ts.map +1 -1
- package/dist/routes/redirect.host-scope.integration.test.d.ts +2 -0
- package/dist/routes/redirect.host-scope.integration.test.d.ts.map +1 -0
- package/dist/routes/redirect.host-scope.integration.test.js +105 -0
- package/dist/routes/redirect.host-scope.integration.test.js.map +1 -0
- package/dist/routes/redirect.js +38 -7
- package/dist/routes/redirect.js.map +1 -1
- package/dist/routes/sdk.d.ts.map +1 -1
- package/dist/routes/sdk.js +10 -7
- package/dist/routes/sdk.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -460,7 +460,9 @@ Every short code is unique across the server. Links created through Core's API h
|
|
|
460
460
|
An application that builds on Core and assigns links to organizations (by setting `links.organization_id`) gets one set of codes per organization: two organizations can each have `promo`. Template slugs stay unique across the server, and URLs resolve like this:
|
|
461
461
|
|
|
462
462
|
- `/{templateSlug}/{shortCode}` resolves to exactly one link.
|
|
463
|
-
- `/{shortCode}`
|
|
463
|
+
- `/{shortCode}` on a request that belongs to an organization resolves only that organization's link with that code.
|
|
464
|
+
- A request that belongs to an organization is served only that organization's links, in either form: another organization's link is answered with 404, and the refusal is logged.
|
|
465
|
+
- `/{shortCode}` on any other request prefers a link without a template, then the oldest link with that code.
|
|
464
466
|
|
|
465
467
|
To tell Core which organization a request belongs to, for example by custom domain, register a resolver:
|
|
466
468
|
|
|
@@ -668,6 +670,21 @@ await fastify.register(linkRoutes);
|
|
|
668
670
|
await fastify.listen({ port: 3000 });
|
|
669
671
|
```
|
|
670
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
|
+
|
|
671
688
|
## Deployment
|
|
672
689
|
|
|
673
690
|
LinkForty can be deployed in multiple ways depending on your needs:
|
package/dist/lib/database.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/lib/database.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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.
|
package/dist/lib/database.js.map
CHANGED
|
@@ -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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"}
|
|
@@ -7,10 +7,13 @@ import type { FastifyRequest } from 'fastify';
|
|
|
7
7
|
*
|
|
8
8
|
* - `/{templateSlug}/{shortCode}` names exactly one link: the slug fixes the
|
|
9
9
|
* organization and the code is unique within it.
|
|
10
|
-
* - `/{shortCode}`
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* - `/{shortCode}` on a host that belongs to an organization (see
|
|
11
|
+
* setLinkScopeResolver) matches only that organization's link, so one
|
|
12
|
+
* organization's host never serves another organization's link.
|
|
13
|
+
* - `/{shortCode}` on a shared host can match one link per organization. The
|
|
14
|
+
* lookup prefers a link without a template (whose only URL is the bare one),
|
|
15
|
+
* then the oldest link. While every code is used by one link, that is simply
|
|
16
|
+
* the link.
|
|
14
17
|
*/
|
|
15
18
|
/**
|
|
16
19
|
* Returns the organization a request belongs to, typically from its host (a
|
|
@@ -20,7 +23,9 @@ import type { FastifyRequest } from 'fastify';
|
|
|
20
23
|
export type LinkScopeResolver = (request: FastifyRequest) => Promise<string | null> | string | null;
|
|
21
24
|
/**
|
|
22
25
|
* Set how the redirect, SDK resolve and preview routes decide which
|
|
23
|
-
* organization a request belongs to.
|
|
26
|
+
* organization a request belongs to. A bare code on a request that belongs to
|
|
27
|
+
* an organization resolves only that organization's links. Pass undefined to
|
|
28
|
+
* clear it.
|
|
24
29
|
*/
|
|
25
30
|
export declare function setLinkScopeResolver(resolver: LinkScopeResolver | undefined): void;
|
|
26
31
|
/**
|
|
@@ -28,9 +33,17 @@ export declare function setLinkScopeResolver(resolver: LinkScopeResolver | undef
|
|
|
28
33
|
* treated as null, so it can never take a redirect down.
|
|
29
34
|
*/
|
|
30
35
|
export declare function resolveLinkScope(request: FastifyRequest, onError?: (error: unknown) => void): Promise<string | null>;
|
|
36
|
+
/**
|
|
37
|
+
* WHERE clause fragment for a bare `/{shortCode}` lookup over `links l`:
|
|
38
|
+
* restricts the lookup to the request's organization when there is one.
|
|
39
|
+
* `scopeParam` is the placeholder holding it (e.g. `$2`).
|
|
40
|
+
*/
|
|
41
|
+
export declare function bareCodeScopeFilter(scopeParam?: string): string;
|
|
31
42
|
/**
|
|
32
43
|
* ORDER BY for a bare `/{shortCode}` lookup over `links l`. `scopeParam` is the
|
|
33
44
|
* placeholder holding the request's organization (e.g. `$2`), when there is one.
|
|
45
|
+
* With bareCodeScopeFilter() applied every row is already in scope, so the
|
|
46
|
+
* routes call this without one.
|
|
34
47
|
*/
|
|
35
48
|
export declare function bareCodeOrderBy(scopeParam?: string): string;
|
|
36
49
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link-lookup.d.ts","sourceRoot":"","sources":["../../src/lib/link-lookup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C
|
|
1
|
+
{"version":3,"file":"link-lookup.d.ts","sourceRoot":"","sources":["../../src/lib/link-lookup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C;;;;;;;;;;;;;;;GAeG;AAEH;;;;GAIG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC;AAIpG;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,SAAS,GAAG,IAAI,CAElF;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,cAAc,EACvB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GACjC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQxB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAI3G;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE;IAAE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACzC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,EAC5B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAGT"}
|
package/dist/lib/link-lookup.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
let scopeResolver;
|
|
2
2
|
/**
|
|
3
3
|
* Set how the redirect, SDK resolve and preview routes decide which
|
|
4
|
-
* organization a request belongs to.
|
|
4
|
+
* organization a request belongs to. A bare code on a request that belongs to
|
|
5
|
+
* an organization resolves only that organization's links. Pass undefined to
|
|
6
|
+
* clear it.
|
|
5
7
|
*/
|
|
6
8
|
export function setLinkScopeResolver(resolver) {
|
|
7
9
|
scopeResolver = resolver;
|
|
@@ -21,9 +23,19 @@ export async function resolveLinkScope(request, onError) {
|
|
|
21
23
|
return null;
|
|
22
24
|
}
|
|
23
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* WHERE clause fragment for a bare `/{shortCode}` lookup over `links l`:
|
|
28
|
+
* restricts the lookup to the request's organization when there is one.
|
|
29
|
+
* `scopeParam` is the placeholder holding it (e.g. `$2`).
|
|
30
|
+
*/
|
|
31
|
+
export function bareCodeScopeFilter(scopeParam) {
|
|
32
|
+
return scopeParam ? `AND l.organization_id = ${scopeParam}::uuid` : '';
|
|
33
|
+
}
|
|
24
34
|
/**
|
|
25
35
|
* ORDER BY for a bare `/{shortCode}` lookup over `links l`. `scopeParam` is the
|
|
26
36
|
* placeholder holding the request's organization (e.g. `$2`), when there is one.
|
|
37
|
+
* With bareCodeScopeFilter() applied every row is already in scope, so the
|
|
38
|
+
* routes call this without one.
|
|
27
39
|
*/
|
|
28
40
|
export function bareCodeOrderBy(scopeParam) {
|
|
29
41
|
const preferScope = scopeParam ? `COALESCE(l.organization_id = ${scopeParam}::uuid, false) DESC, ` : '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link-lookup.js","sourceRoot":"","sources":["../../src/lib/link-lookup.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"link-lookup.js","sourceRoot":"","sources":["../../src/lib/link-lookup.ts"],"names":[],"mappings":"AA0BA,IAAI,aAA4C,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,QAAuC;IAC1E,aAAa,GAAG,QAAQ,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,OAAuB,EACvB,OAAkC;IAElC,IAAI,CAAC,aAAa;QAAE,OAAO,IAAI,CAAC;IAChC,IAAI,CAAC;QACH,OAAO,CAAC,MAAM,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,UAAmB;IACrD,OAAO,UAAU,CAAC,CAAC,CAAC,2BAA2B,UAAU,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;AACzE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,UAAmB;IACjD,MAAM,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,gCAAgC,UAAU,uBAAuB,CAAC,CAAC,CAAC,EAAE,CAAC;IACxG,OAAO,YAAY,WAAW,0DAA0D,CAAC;AAC3F,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,SAAiB,EAAE,YAA4B,EAAE,KAAqB;IACjG,IAAI,YAAY;QAAE,OAAO,QAAQ,YAAY,IAAI,SAAS,EAAE,CAAC;IAC7D,IAAI,KAAK;QAAE,OAAO,SAAS,KAAK,IAAI,SAAS,EAAE,CAAC;IAChD,OAAO,QAAQ,SAAS,EAAE,CAAC;AAC7B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAyC,EACzC,YAA4B,EAC5B,KAAqB;IAErB,IAAI,YAAY,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,IAAI,CAAC,eAAe,KAAK,KAAK,CAAC;AACxC,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
-
import { setLinkScopeResolver, resolveLinkScope, bareCodeOrderBy, linkCacheKey, isCacheableResolution, } from './link-lookup.js';
|
|
2
|
+
import { setLinkScopeResolver, resolveLinkScope, bareCodeOrderBy, bareCodeScopeFilter, linkCacheKey, isCacheableResolution, } from './link-lookup.js';
|
|
3
3
|
const request = { headers: { host: 'links.example.com' } };
|
|
4
4
|
afterEach(() => setLinkScopeResolver(undefined));
|
|
5
5
|
describe('resolveLinkScope', () => {
|
|
@@ -27,6 +27,14 @@ describe('bareCodeOrderBy', () => {
|
|
|
27
27
|
expect(bareCodeOrderBy('$2')).toBe('ORDER BY COALESCE(l.organization_id = $2::uuid, false) DESC, (l.template_id IS NULL) DESC, l.created_at ASC, l.id ASC');
|
|
28
28
|
});
|
|
29
29
|
});
|
|
30
|
+
describe('bareCodeScopeFilter', () => {
|
|
31
|
+
it("limits the lookup to the request's organization", () => {
|
|
32
|
+
expect(bareCodeScopeFilter('$2')).toBe('AND l.organization_id = $2::uuid');
|
|
33
|
+
});
|
|
34
|
+
it('adds nothing without an organization', () => {
|
|
35
|
+
expect(bareCodeScopeFilter()).toBe('');
|
|
36
|
+
});
|
|
37
|
+
});
|
|
30
38
|
describe('linkCacheKey', () => {
|
|
31
39
|
it('keys a template URL by slug and code', () => {
|
|
32
40
|
expect(linkCacheKey('promo', 'abc123')).toBe('link:abc123:promo');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link-lookup.test.js","sourceRoot":"","sources":["../../src/lib/link-lookup.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,OAAO,GAAG,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAS,CAAC;AAElE,SAAS,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC;AAEjD,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,CAAC,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,oBAAoB,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,mBAAmB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3F,MAAM,CAAC,MAAM,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8DAA8D,EAAE,KAAK,IAAI,EAAE;QAC5E,MAAM,MAAM,GAAc,EAAE,CAAC;QAC7B,oBAAoB,CAAC,KAAK,IAAI,EAAE;YAC9B,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,gBAAgB,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC1E,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC,IAAI,CAAC,mEAAmE,CAAC,CAAC;IACtG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAChC,uHAAuH,CACxH,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAClE,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,CAAC,qBAAqB,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtF,MAAM,CAAC,qBAAqB,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvF,MAAM,CAAC,qBAAqB,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,qBAAqB,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1F,MAAM,CAAC,qBAAqB,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"link-lookup.test.js","sourceRoot":"","sources":["../../src/lib/link-lookup.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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"}
|