turbine-orm 0.67.0 → 0.70.0
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/cjs/cli/error-catalog.d.ts +77 -0
- package/dist/cjs/cli/error-catalog.js +388 -0
- package/dist/cjs/cli/index.js +3 -2
- package/dist/cjs/cli/mcp.d.ts +19 -3
- package/dist/cjs/cli/mcp.js +709 -22
- package/dist/cjs/cli/migrate.d.ts +19 -2
- package/dist/cjs/cli/observe.d.ts +2 -2
- package/dist/cjs/cli/observe.js +20 -2
- package/dist/cjs/cli/pii-predicate-guard.d.ts +6 -2
- package/dist/cjs/cli/pii-predicate-guard.js +6 -2
- package/dist/cjs/cli/studio-ui.generated.js +1 -1
- package/dist/cjs/client.d.ts +30 -75
- package/dist/cjs/client.js +31 -11
- package/dist/cjs/introspect.d.ts +113 -17
- package/dist/cjs/introspect.js +229 -33
- package/dist/cjs/pg-types.d.ts +153 -0
- package/dist/cjs/pg-types.js +38 -0
- package/dist/cjs/pipeline.d.ts +3 -3
- package/dist/cjs/query/batched-loader.d.ts +2 -2
- package/dist/cjs/query/builder.d.ts +2 -2
- package/dist/cjs/query/builder.js +10 -1
- package/dist/cjs/query/deferred.d.ts +6 -6
- package/dist/cjs/query/filters.d.ts +13 -7
- package/dist/cjs/query/filters.js +13 -14
- package/dist/cjs/query/where.d.ts +2 -2
- package/dist/cjs/schema-sql.d.ts +18 -0
- package/dist/cjs/schema-sql.js +18 -0
- package/dist/cli/error-catalog.d.ts +77 -0
- package/dist/cli/error-catalog.js +383 -0
- package/dist/cli/index.js +3 -2
- package/dist/cli/mcp.d.ts +19 -3
- package/dist/cli/mcp.js +709 -23
- package/dist/cli/migrate.d.ts +19 -2
- package/dist/cli/observe.d.ts +2 -2
- package/dist/cli/observe.js +20 -2
- package/dist/cli/pii-predicate-guard.d.ts +6 -2
- package/dist/cli/pii-predicate-guard.js +6 -2
- package/dist/cli/studio-ui.generated.js +1 -1
- package/dist/client.d.ts +30 -75
- package/dist/client.js +31 -11
- package/dist/introspect.d.ts +113 -17
- package/dist/introspect.js +227 -33
- package/dist/pg-types.d.ts +153 -0
- package/dist/pg-types.js +37 -0
- package/dist/pipeline.d.ts +3 -3
- package/dist/query/batched-loader.d.ts +2 -2
- package/dist/query/builder.d.ts +2 -2
- package/dist/query/builder.js +10 -1
- package/dist/query/deferred.d.ts +6 -6
- package/dist/query/filters.d.ts +13 -7
- package/dist/query/filters.js +13 -13
- package/dist/query/where-compile.js +1 -1
- package/dist/query/where.d.ts +2 -2
- package/dist/schema-sql.d.ts +18 -0
- package/dist/schema-sql.js +18 -0
- package/package.json +19 -7
package/dist/cjs/client.d.ts
CHANGED
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
* const users = db.table<User>('users');
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
|
-
import pg from 'pg';
|
|
25
24
|
import { type Dialect } from './dialect.js';
|
|
26
25
|
import { type ErrorMessageMode } from './errors.js';
|
|
27
26
|
import { type ObserveConfig, type ObserveHandle } from './observe.js';
|
|
27
|
+
import type { PgCompatPool, PgCompatPoolClient } from './pg-types.js';
|
|
28
28
|
import { type PipelineOptions, type PipelineResults } from './pipeline.js';
|
|
29
29
|
import { type DeferredQuery, type GlobalFilters, type QueryEventListener, QueryInterface, type QueryInterfaceOptions, type RelationLoadStrategy, type TemporalInfinityReading } from './query/index.js';
|
|
30
30
|
import { type NotificationHandler, type Subscription } from './realtime.js';
|
|
@@ -38,75 +38,13 @@ export interface RetryOptions {
|
|
|
38
38
|
}
|
|
39
39
|
export declare function withRetry<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;
|
|
40
40
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
41
|
+
* The pg-compatible driver contract. Declared in `pg-types.ts` (a leaf module
|
|
42
|
+
* with no imports) rather than here, so `query/` can name the same interfaces
|
|
43
|
+
* without an import edge back to the client, and so no `@types/pg` name
|
|
44
|
+
* reaches the published declarations. Re-exported so every existing
|
|
45
|
+
* `from './client.js'` import path keeps working.
|
|
44
46
|
*/
|
|
45
|
-
export
|
|
46
|
-
rows: R[];
|
|
47
|
-
rowCount: number | null;
|
|
48
|
-
fields?: Array<{
|
|
49
|
-
name: string;
|
|
50
|
-
dataTypeID: number;
|
|
51
|
-
}>;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Minimal pg-compatible client used by TurbineClient for transactions.
|
|
55
|
-
* `pg.PoolClient` satisfies this; so do Neon and Vercel's equivalents.
|
|
56
|
-
*/
|
|
57
|
-
export interface PgCompatPoolClient {
|
|
58
|
-
query<R = Record<string, unknown>>(text: string, values?: unknown[]): Promise<PgCompatQueryResult<R>>;
|
|
59
|
-
release(err?: Error | boolean): void;
|
|
60
|
-
/**
|
|
61
|
-
* Optional driver capability: `true` when `query()` may be called again on
|
|
62
|
-
* this connection while earlier calls are still in flight, with replies
|
|
63
|
-
* delivered to callers in FIFO submission order. Drivers that set this let
|
|
64
|
-
* the batch `$transaction([...])` overload dispatch every statement in one
|
|
65
|
-
* write burst (~1 network round trip plus server time) instead of awaiting
|
|
66
|
-
* each reply before sending the next (N round trips). Leave unset for
|
|
67
|
-
* drivers (node-postgres included) whose batch path must stay strictly
|
|
68
|
-
* sequential.
|
|
69
|
-
*/
|
|
70
|
-
readonly supportsPipelining?: boolean;
|
|
71
|
-
/**
|
|
72
|
-
* Optional engine seam: scope a transaction's user callback to its own
|
|
73
|
-
* async subtree. When present, `TurbineClient.transaction` / `$transaction`
|
|
74
|
-
* invoke the callback as `wrapTransactionCallback(() => fn(tx))` instead of
|
|
75
|
-
* `fn(tx)` directly. Single-writer engines (PowDB) implement it with
|
|
76
|
-
* `AsyncLocalStorage.run()` to plant their re-entrancy marker so that it
|
|
77
|
-
* exists ONLY inside the callback's async subtree: a transaction opened
|
|
78
|
-
* from inside the callback is detected as re-entrant (typed E017), while
|
|
79
|
-
* the CALLER's context stays unmarked, so same-tick sibling transactions
|
|
80
|
-
* queue FIFO instead of being falsely flagged. Absent on pg and every other
|
|
81
|
-
* engine, in which case the callback runs unwrapped (zero behavior change).
|
|
82
|
-
*/
|
|
83
|
-
wrapTransactionCallback?<R>(fn: () => Promise<R>): Promise<R>;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Minimal pg-compatible pool. Pass any driver that satisfies this interface
|
|
87
|
-
* via `TurbineConfig.pool`, lets Turbine run on Neon HTTP, Vercel Postgres,
|
|
88
|
-
* Cloudflare Hyperdrive, or any other serverless Postgres driver.
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```ts
|
|
92
|
-
* import { Pool } from '@neondatabase/serverless';
|
|
93
|
-
* import { TurbineClient } from 'turbine-orm';
|
|
94
|
-
*
|
|
95
|
-
* const neonPool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
96
|
-
* const db = new TurbineClient({ pool: neonPool }, schema);
|
|
97
|
-
* ```
|
|
98
|
-
*/
|
|
99
|
-
export interface PgCompatPool {
|
|
100
|
-
query<R = Record<string, unknown>>(text: string, values?: unknown[]): Promise<PgCompatQueryResult<R>>;
|
|
101
|
-
connect(): Promise<PgCompatPoolClient>;
|
|
102
|
-
end(): Promise<void>;
|
|
103
|
-
/** Optional, pools that expose stats (pg.Pool does; Neon HTTP does not) */
|
|
104
|
-
readonly totalCount?: number;
|
|
105
|
-
readonly idleCount?: number;
|
|
106
|
-
readonly waitingCount?: number;
|
|
107
|
-
/** Optional, pg.Pool supports 'error' event; HTTP drivers typically do not */
|
|
108
|
-
on?(event: 'error', listener: (err: Error) => void): this;
|
|
109
|
-
}
|
|
47
|
+
export type { PgCompatPool, PgCompatPoolClient, PgCompatQueryConfig, PgCompatQueryResult, } from './pg-types.js';
|
|
110
48
|
/**
|
|
111
49
|
* Driver-neutral seam. Bundles a pg-compatible connection pool with the SQL
|
|
112
50
|
* {@link Dialect} that owns every piece of SQL text varying across engines -
|
|
@@ -618,7 +556,7 @@ export declare class TransactionClient {
|
|
|
618
556
|
private savepointCounter;
|
|
619
557
|
/** Active SQL dialect, owns savepoint keywords and raw-SQL placeholders. */
|
|
620
558
|
private readonly dialect;
|
|
621
|
-
constructor(client:
|
|
559
|
+
constructor(client: PgCompatPoolClient, schema: SchemaMetadata, middlewares: Middleware[], queryOptions?: QueryInterfaceOptions | undefined,
|
|
622
560
|
/**
|
|
623
561
|
* The parent pool this transaction runs on. Only its `readonly` and
|
|
624
562
|
* `capabilities` are read (both PowDB-only flags), so the transaction-scoped
|
|
@@ -686,8 +624,19 @@ export declare class TransactionClient {
|
|
|
686
624
|
private createTxPool;
|
|
687
625
|
}
|
|
688
626
|
export declare class TurbineClient {
|
|
689
|
-
/**
|
|
690
|
-
|
|
627
|
+
/**
|
|
628
|
+
* The underlying connection pool, exposed for escape hatches.
|
|
629
|
+
*
|
|
630
|
+
* Typed as the driver-neutral {@link PgCompatPool} rather than `pg.Pool`,
|
|
631
|
+
* because it is only a `pg.Pool` when Turbine opened it: supply
|
|
632
|
+
* {@link TurbineConfig.pool} and this is whatever driver you passed (Neon,
|
|
633
|
+
* Vercel Postgres, Hyperdrive), and on the non-Postgres engines it is that
|
|
634
|
+
* engine's pool shim. `query` / `connect` / `end` are the members every
|
|
635
|
+
* driver has. For the pg-only surface (cursors, `copyFrom`/`copyTo`, pool
|
|
636
|
+
* events), import `Pool` from the pg package and cast:
|
|
637
|
+
* `db.pool as unknown as Pool`.
|
|
638
|
+
*/
|
|
639
|
+
readonly pool: PgCompatPool;
|
|
691
640
|
/** The schema metadata this client was built from */
|
|
692
641
|
readonly schema: SchemaMetadata;
|
|
693
642
|
private static int8ParserRegistered;
|
|
@@ -1052,8 +1001,14 @@ export declare class TurbineClient {
|
|
|
1052
1001
|
*/
|
|
1053
1002
|
sql<T extends Record<string, unknown> = Record<string, unknown>>(strings: TemplateStringsArray, ...values: unknown[]): TypedSqlQuery<T>;
|
|
1054
1003
|
/**
|
|
1055
|
-
* Execute a function within a database transaction
|
|
1056
|
-
* For the typed API, use `$transaction()` instead.
|
|
1004
|
+
* Execute a function within a database transaction on the raw driver
|
|
1005
|
+
* connection. For the typed API, use `$transaction()` instead.
|
|
1006
|
+
*
|
|
1007
|
+
* The client is typed as {@link PgCompatPoolClient}, the driver-neutral
|
|
1008
|
+
* contract: `query(text, values)` plus `release()`, which is all any
|
|
1009
|
+
* supported driver guarantees. For the pg-only surface (cursors, COPY
|
|
1010
|
+
* streams, the `QueryConfig` object form), import `PoolClient` from the pg
|
|
1011
|
+
* package and cast: `client as unknown as PoolClient`.
|
|
1057
1012
|
*
|
|
1058
1013
|
* @example
|
|
1059
1014
|
* ```ts
|
|
@@ -1062,7 +1017,7 @@ export declare class TurbineClient {
|
|
|
1062
1017
|
* });
|
|
1063
1018
|
* ```
|
|
1064
1019
|
*/
|
|
1065
|
-
transaction<T>(fn: (client:
|
|
1020
|
+
transaction<T>(fn: (client: PgCompatPoolClient) => Promise<T>): Promise<T>;
|
|
1066
1021
|
/**
|
|
1067
1022
|
* Execute a function within a database transaction with full typed table accessors.
|
|
1068
1023
|
*
|
package/dist/cjs/client.js
CHANGED
|
@@ -585,8 +585,8 @@ class TransactionClient {
|
|
|
585
585
|
return await client.query(textOrConfig, values);
|
|
586
586
|
}
|
|
587
587
|
// Object form for prepared statements: { name, text, values }
|
|
588
|
-
//
|
|
589
|
-
//
|
|
588
|
+
// A driver client accepts it, but the two-argument `query` in the
|
|
589
|
+
// PgCompatPoolClient contract does not describe it, so we cast.
|
|
590
590
|
return await client.query(textOrConfig);
|
|
591
591
|
}
|
|
592
592
|
catch (err) {
|
|
@@ -610,7 +610,18 @@ exports.TransactionClient = TransactionClient;
|
|
|
610
610
|
// TurbineClient
|
|
611
611
|
// ---------------------------------------------------------------------------
|
|
612
612
|
class TurbineClient {
|
|
613
|
-
/**
|
|
613
|
+
/**
|
|
614
|
+
* The underlying connection pool, exposed for escape hatches.
|
|
615
|
+
*
|
|
616
|
+
* Typed as the driver-neutral {@link PgCompatPool} rather than `pg.Pool`,
|
|
617
|
+
* because it is only a `pg.Pool` when Turbine opened it: supply
|
|
618
|
+
* {@link TurbineConfig.pool} and this is whatever driver you passed (Neon,
|
|
619
|
+
* Vercel Postgres, Hyperdrive), and on the non-Postgres engines it is that
|
|
620
|
+
* engine's pool shim. `query` / `connect` / `end` are the members every
|
|
621
|
+
* driver has. For the pg-only surface (cursors, `copyFrom`/`copyTo`, pool
|
|
622
|
+
* events), import `Pool` from the pg package and cast:
|
|
623
|
+
* `db.pool as unknown as Pool`.
|
|
624
|
+
*/
|
|
614
625
|
pool;
|
|
615
626
|
/** The schema metadata this client was built from */
|
|
616
627
|
schema;
|
|
@@ -953,11 +964,15 @@ class TurbineClient {
|
|
|
953
964
|
if (config.ssl !== undefined) {
|
|
954
965
|
poolConfig.ssl = config.ssl;
|
|
955
966
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
967
|
+
// Held as a `pg.Pool` for the length of this block: `PgCompatPool.on` is
|
|
968
|
+
// optional (HTTP drivers have no pool events), and here we know we own a
|
|
969
|
+
// real pg pool that has one. Same shape as the replica loop below.
|
|
970
|
+
const ownPool = new pg_1.default.Pool(TurbineClient.withPlanCacheMode(poolConfig, this.planCacheMode));
|
|
971
|
+
ownPool.on('error', (err) => {
|
|
959
972
|
console.error('[turbine] Unexpected pool error:', err.message);
|
|
960
973
|
});
|
|
974
|
+
this.pool = ownPool;
|
|
975
|
+
this.ownsPool = true;
|
|
961
976
|
if (this.logging) {
|
|
962
977
|
console.log(`[turbine] Pool created, max ${poolConfig.max} connections, ${Object.keys(schema.tables).length} tables`);
|
|
963
978
|
}
|
|
@@ -1413,10 +1428,9 @@ class TurbineClient {
|
|
|
1413
1428
|
}
|
|
1414
1429
|
/** Construct a QueryInterface bound to `pool` (honoring any injected factory). */
|
|
1415
1430
|
buildTableQI(pool, name) {
|
|
1416
|
-
const asPgPool = pool;
|
|
1417
1431
|
return this.queryOptions?.queryInterfaceFactory
|
|
1418
|
-
? this.queryOptions.queryInterfaceFactory(
|
|
1419
|
-
: new index_js_1.QueryInterface(
|
|
1432
|
+
? this.queryOptions.queryInterfaceFactory(pool, name, this.schema, this.middlewares, this.queryOptions)
|
|
1433
|
+
: new index_js_1.QueryInterface(pool, name, this.schema, this.middlewares, this.queryOptions);
|
|
1420
1434
|
}
|
|
1421
1435
|
/**
|
|
1422
1436
|
* Build the read/write routing proxy for a table. The proxy targets the
|
|
@@ -1596,8 +1610,14 @@ class TurbineClient {
|
|
|
1596
1610
|
// Transaction support (raw, legacy)
|
|
1597
1611
|
// -------------------------------------------------------------------------
|
|
1598
1612
|
/**
|
|
1599
|
-
* Execute a function within a database transaction
|
|
1600
|
-
* For the typed API, use `$transaction()` instead.
|
|
1613
|
+
* Execute a function within a database transaction on the raw driver
|
|
1614
|
+
* connection. For the typed API, use `$transaction()` instead.
|
|
1615
|
+
*
|
|
1616
|
+
* The client is typed as {@link PgCompatPoolClient}, the driver-neutral
|
|
1617
|
+
* contract: `query(text, values)` plus `release()`, which is all any
|
|
1618
|
+
* supported driver guarantees. For the pg-only surface (cursors, COPY
|
|
1619
|
+
* streams, the `QueryConfig` object form), import `PoolClient` from the pg
|
|
1620
|
+
* package and cast: `client as unknown as PoolClient`.
|
|
1601
1621
|
*
|
|
1602
1622
|
* @example
|
|
1603
1623
|
* ```ts
|
package/dist/cjs/introspect.d.ts
CHANGED
|
@@ -135,21 +135,98 @@ export declare function applyRelationRenames(schema: SchemaMetadata, renames: Re
|
|
|
135
135
|
*/
|
|
136
136
|
export declare function introspectPostgresCatalog(options: IntrospectOptions): Promise<SchemaMetadata>;
|
|
137
137
|
/**
|
|
138
|
-
*
|
|
138
|
+
* The index-definition key-list SCANNER, and the only one that reads an
|
|
139
|
+
* `indexdef` character by character.
|
|
139
140
|
*
|
|
140
|
-
* `indexdef` always reads `CREATE [UNIQUE] INDEX name ON tbl USING
|
|
141
|
-
* (
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* never mistaken for the column list. The older greedy `/\((.+)\)/` swallowed
|
|
145
|
-
* `) WHERE (` and spliced a raw predicate fragment into the column names, which
|
|
146
|
-
* then leaked into generated compound-unique selector names.
|
|
141
|
+
* `pg_indexes.indexdef` always reads `CREATE [UNIQUE] INDEX name ON tbl USING
|
|
142
|
+
* method (key, ...) [INCLUDE (col, ...)] [WITH (...)] [TABLESPACE ts]
|
|
143
|
+
* [WHERE predicate]`. This returns the raw entries of the KEY LIST only, one per
|
|
144
|
+
* top-level comma, expression entries included and verbatim.
|
|
147
145
|
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
146
|
+
* ## THREE indexdef parsers coexist in this repo. This is one of them.
|
|
147
|
+
*
|
|
148
|
+
* An earlier version of this comment said there was exactly one. There is not,
|
|
149
|
+
* and pretending otherwise is how hand-synced parsers drift here, so each of the
|
|
150
|
+
* three names the other two:
|
|
151
|
+
*
|
|
152
|
+
* 1. THIS scanner (with {@link indexKeyColumn} / {@link parseIndexColumns}).
|
|
153
|
+
* Safe for any `indexdef` pg emits, including expression keys, quoted
|
|
154
|
+
* identifiers holding commas or parens, string literals, INCLUDE lists and
|
|
155
|
+
* partial predicates. Feeds generated metadata, compound-unique selectors,
|
|
156
|
+
* the FK-index advisor and m2m detection, plus `cli/mcp.ts`.
|
|
157
|
+
* 2. {@link parsePlainUniqueIndexColumns}, below. Still the old
|
|
158
|
+
* `USING \w+ \(([^)]*)\)` regex. It answers a NARROWER question ("is this a
|
|
159
|
+
* plain, whole-table unique index over these exact columns") and returns
|
|
160
|
+
* `null` on everything it cannot read, so its regex's known weaknesses cost
|
|
161
|
+
* a missed hasOne flip rather than a wrong answer.
|
|
162
|
+
* 3. `describeIndexDefMismatch` in `schema-sql.ts`. Same old regex, same
|
|
163
|
+
* fail-toward-a-warning posture.
|
|
164
|
+
*
|
|
165
|
+
* Unifying them is a separate change with its own risk: (2) and (3) both treat
|
|
166
|
+
* "cannot read this" as a safe refusal, and swapping in a parser that reads MORE
|
|
167
|
+
* turns some of those refusals into answers. Until then, prefer this scanner for
|
|
168
|
+
* any new caller, and do not assume a fix here reaches the other two.
|
|
169
|
+
*
|
|
170
|
+
* ## Why the mcp.ts copy is gone
|
|
171
|
+
*
|
|
172
|
+
* `cli/mcp.ts` kept its own weaker copy of this, and the two drifted: on
|
|
173
|
+
* `USING btree (id) INCLUDE (email)` the copy answered `['id) INCLUDE (email']`
|
|
174
|
+
* while this one answered `['id']`. Those columns feed `deriveCatalogRelations`,
|
|
175
|
+
* which decides hasOne-vs-hasMany and auto-m2m, so a UNIQUE index with INCLUDE
|
|
176
|
+
* columns was visible to `turbine generate` and invisible to the MCP server, and
|
|
177
|
+
* the two surfaces disagreed about which relations the schema has. The
|
|
178
|
+
* duplication is also what produced the predicate leak that
|
|
179
|
+
* {@link parseIndexColumns}'s own history records. So mcp.ts consumes this
|
|
180
|
+
* function now, and the split between the two callers is expressed as the two
|
|
181
|
+
* exports below rather than as two implementations.
|
|
182
|
+
*
|
|
183
|
+
* ## Why a scanner rather than a regex
|
|
184
|
+
*
|
|
185
|
+
* The regex this replaces (`/USING\s+\w+\s*\(([^)]*)\)/`) stops at the FIRST
|
|
186
|
+
* `)`, which is the wrong paren for any expression key (`lower(email)`), and a
|
|
187
|
+
* plain `.split(',')` cuts `coalesce(a, b)` in half. The scan tracks paren depth
|
|
188
|
+
* and single-quoted literals, so a comma or a paren inside an expression or a
|
|
189
|
+
* literal is not a boundary.
|
|
190
|
+
*
|
|
191
|
+
* The anchor requires `USING <method> (`, which no predicate, INCLUDE list, WITH
|
|
192
|
+
* list or literal can spell, so the key list is found positionally rather than
|
|
193
|
+
* by hoping the first paren is the right one. When the anchor is absent (not a
|
|
194
|
+
* shape pg emits) it falls back to the first parenthesised group, matching the
|
|
195
|
+
* previous behaviour.
|
|
196
|
+
*/
|
|
197
|
+
export declare function parseIndexKeyEntries(indexdef: string): string[];
|
|
198
|
+
/**
|
|
199
|
+
* The plain COLUMN NAME an index key entry indexes, or `null` when the entry is
|
|
200
|
+
* an expression rather than a column.
|
|
201
|
+
*
|
|
202
|
+
* A key entry is `{ column | (expression) } [COLLATE c] [opclass [(params)]]
|
|
203
|
+
* [ASC|DESC] [NULLS FIRST|LAST]`, so the column is the LEADING token and
|
|
204
|
+
* everything after it is a modifier. Reading it that way is what makes
|
|
205
|
+
* `email COLLATE "C" text_pattern_ops` resolve to `email`; the previous
|
|
206
|
+
* suffix-stripping (`ASC`/`DESC` only) returned the whole entry verbatim as a
|
|
207
|
+
* "column name", which matches no real column.
|
|
208
|
+
*
|
|
209
|
+
* SCOPE OF THAT FIX, stated exactly because an earlier version of this comment
|
|
210
|
+
* overstated it: what changes is what {@link parseIndexColumns} reports, and so
|
|
211
|
+
* what its consumers see. Those are the generated `metadata.ts` index lists,
|
|
212
|
+
* the compound-unique selector derivation, the FK-index advisor's leading-column
|
|
213
|
+
* check, and m2m junction detection. Relation CARDINALITY is NOT among them: the
|
|
214
|
+
* `hasMany`/`hasOne` flip reads {@link parsePlainUniqueIndexColumns}, a separate
|
|
215
|
+
* parser this does not feed, and that one still answers `null` for an opclass'd
|
|
216
|
+
* UNIQUE index exactly as it did before.
|
|
217
|
+
*
|
|
218
|
+
* The name is de-quoted (Postgres quotes non-lowercase identifiers such as a
|
|
219
|
+
* Prisma implicit m2m junction's `"A"` / `"B"`) so it matches the unquoted names
|
|
220
|
+
* carried elsewhere in the metadata.
|
|
221
|
+
*/
|
|
222
|
+
export declare function indexKeyColumn(entry: string): string | null;
|
|
223
|
+
/**
|
|
224
|
+
* Parse the indexed COLUMN names out of a `pg_indexes.indexdef` string.
|
|
225
|
+
*
|
|
226
|
+
* Expression keys are dropped, conservatively: a functional index does not name
|
|
227
|
+
* a plain column, and generated metadata has nowhere to say "there was a key
|
|
228
|
+
* here that is not a column". {@link parseIndexKeyEntries} is the variant that
|
|
229
|
+
* keeps them, for the one caller (`cli/mcp.ts`) that reports their presence.
|
|
153
230
|
*/
|
|
154
231
|
export declare function parseIndexColumns(indexdef: string): string[];
|
|
155
232
|
/**
|
|
@@ -198,10 +275,29 @@ export declare function isUnknownTsType(tsType: string): boolean;
|
|
|
198
275
|
* expression, not the raw FK column set.
|
|
199
276
|
*
|
|
200
277
|
* Anchors on the `USING <method> (` clause the same way
|
|
201
|
-
*
|
|
202
|
-
* parentheses are never mistaken for the column list. Every column
|
|
203
|
-
* a bare or double-quoted identifier; anything else (a function
|
|
204
|
-
* operator expression) fails the check and yields `null`.
|
|
278
|
+
* `describeIndexDefMismatch` (schema-sql.ts) does, so a partial index's
|
|
279
|
+
* `WHERE (...)` parentheses are never mistaken for the column list. Every column
|
|
280
|
+
* token must be a bare or double-quoted identifier; anything else (a function
|
|
281
|
+
* call, an operator expression) fails the check and yields `null`.
|
|
282
|
+
*
|
|
283
|
+
* ## Parser 2 of 3, and what it is safe for
|
|
284
|
+
*
|
|
285
|
+
* This is the second of the three indexdef parsers catalogued on
|
|
286
|
+
* {@link parseIndexKeyEntries}; the third is `describeIndexDefMismatch` in
|
|
287
|
+
* schema-sql.ts. It is still the `USING \w+ \(([^)]*)\)` regex that the scanner
|
|
288
|
+
* up there was written to replace, so it inherits that regex's weaknesses: it
|
|
289
|
+
* stops at the FIRST `)`, and it splits on every comma. On an expression key
|
|
290
|
+
* (`lower(email)`), on a quoted identifier containing a comma or a paren, and on
|
|
291
|
+
* an opclass'd key (`email text_pattern_ops`) it therefore reads a token that is
|
|
292
|
+
* not a bare identifier and returns `null`.
|
|
293
|
+
*
|
|
294
|
+
* That is SAFE HERE and only here, because `null` is this function's "I cannot
|
|
295
|
+
* vouch for this index" answer and its single consumer
|
|
296
|
+
* ({@link detectUniqueForeignKeySets}) treats it as "this index does not prove
|
|
297
|
+
* uniqueness". The cost of every misread is a relation left as `hasMany` that
|
|
298
|
+
* could have been `hasOne`, never a uniqueness claim the database does not back.
|
|
299
|
+
* Do NOT reuse it anywhere a wrong-but-plausible column list would be acted on;
|
|
300
|
+
* use the scanner for that.
|
|
205
301
|
*/
|
|
206
302
|
export declare function parsePlainUniqueIndexColumns(indexdef: string): string[] | null;
|
|
207
303
|
/**
|