@atscript/db-postgres 0.1.149 → 0.1.151

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/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TFieldOps, TPrimaryKeyChange, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair, TViewCapability } from "@atscript/db";
1
+ import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateOptions, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TFieldOps, TPrimaryKeyChange, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair, TViewCapability } from "@atscript/db";
2
2
  import { TMetadataMap } from "@atscript/typescript/utils";
3
3
  import { TFilterVisitorOptions, TSqlFragment } from "@atscript/db-sql-tools";
4
4
 
@@ -79,8 +79,42 @@ declare class PostgresAdapter extends BaseDbAdapter {
79
79
  private _nocaseColumns;
80
80
  /** Whether citext extension has been provisioned (avoids redundant round-trips). */
81
81
  private _citextProvisioned;
82
- /** Whether the connected PostgreSQL instance has the PostGIS extension. */
82
+ /**
83
+ * Whether the connected PostgreSQL instance has the PostGIS extension —
84
+ * `undefined` until known: set by schema sync ({@link prepareTypeMapper},
85
+ * which installs it), or by the read-only geo probe ({@link _probeGeo}).
86
+ * Decides DDL and geo search; how a geo VALUE binds is decided per column
87
+ * ({@link _geoNative}).
88
+ */
83
89
  private _supportsGeo;
90
+ /** Whether {@link _detectGeoSupport} (the installing detection) already ran. */
91
+ private _geoInstallTried;
92
+ /** Memo of {@link _hasGeoPointFields}. */
93
+ private _geoFields?;
94
+ /** Physical names of the unencrypted `db.geoPoint` columns (set with the metadata). */
95
+ private _geoColumnNames;
96
+ /**
97
+ * Physical geo column → whether it is a PostGIS column (`geography` /
98
+ * `geometry`: EWKT) or not (JSONB: the JSON form). Learned from the catalog
99
+ * — after schema sync ({@link afterSyncTable}) or before the first statement
100
+ * binding a geo value — never from the extension alone: a JSONB column
101
+ * created before PostGIS was installed stays JSONB until a sync migrates
102
+ * it. Cleared by every DDL path ({@link _geoSchemaChanged}).
103
+ */
104
+ private _geoNative;
105
+ /** The in-flight shared geo probe and the executor it runs on. */
106
+ private _geoProbe?;
107
+ /** Bumped by {@link _geoSchemaChanged}: a probe started before a DDL caches nothing. */
108
+ private _geoSchemaGen;
109
+ /** The pool executor with geo-marker resolution (built once). */
110
+ private _poolExec?;
111
+ /** The last transaction connection's wrapped executor. */
112
+ private _txExec?;
113
+ private _pkColumnsMemo?;
114
+ private _replaceColumnsMemo?;
115
+ private _ignorePlanMemo?;
116
+ private _fulltextMemo?;
117
+ private _searchIndexesMemo?;
84
118
  /** Whether the connected PostgreSQL instance has the pgvector extension. */
85
119
  private _supportsVector;
86
120
  /** Vector fields: physical field name → { dimensions, similarity, indexName }. */
@@ -108,6 +142,19 @@ declare class PostgresAdapter extends BaseDbAdapter {
108
142
  * otherwise the pool-based driver.
109
143
  */
110
144
  private _exec;
145
+ /**
146
+ * `exec` resolving `PendingGeoPoint` params before the statement runs. Every
147
+ * adapter wraps — not only geo tables: a relational filter on another
148
+ * table's geo column carries that table's markers into this one's
149
+ * statement. A statement without markers runs as is (one param scan).
150
+ */
151
+ private _geoResolvingExec;
152
+ /**
153
+ * `$geoWithin` passed the core guard while PostGIS presence was unknown
154
+ * ({@link isGeoSearchable} answers optimistically then): learn it, and
155
+ * refuse like the guard would have when it is absent.
156
+ */
157
+ private _assertGeoWithin;
111
158
  /**
112
159
  * Relational predicates (`$some` / `$none`) render as correlated
113
160
  * `[NOT] EXISTS` subqueries — in reads and in mutation filters alike.
@@ -163,12 +210,21 @@ declare class PostgresAdapter extends BaseDbAdapter {
163
210
  * unique key columns>`: one statement per chunk (a conflict never raises, so
164
211
  * the surrounding transaction survives). Skipped rows are the ones missing
165
212
  * from RETURNING — mapped back by key values, see {@link mapIgnoredBatch}.
166
- * Each chunk runs inside a SAVEPOINT: when the mapping is ambiguous (a key
167
- * the server returns in another form than it was sent) the chunk is rolled
168
- * back to the savepoint and redone row by row with the same
169
- * `ON CONFLICT DO NOTHING`, so the result is always exact.
213
+ * A multi-row chunk of a keyed table runs inside a SAVEPOINT: when the
214
+ * mapping is ambiguous (a key the server returns in another form than it
215
+ * was sent) the chunk is rolled back to the savepoint and redone row by row
216
+ * with the same `ON CONFLICT DO NOTHING`, so the result is always exact. A
217
+ * one-row chunk (or a table without keys) maps exactly by construction, so
218
+ * it needs no savepoint — and a single such chunk no transaction either.
170
219
  */
171
220
  insertManyIgnore(data: Array<Record<string, unknown>>): Promise<TDbInsertIgnoreSlot[]>;
221
+ /**
222
+ * The key columns of a conflict-ignoring insert (built once per table):
223
+ * every primary / unique key set, their RETURNING list, and the key columns
224
+ * whose physical type is not text (see {@link mapIgnoredBatch}). Keyed on the
225
+ * detected extension state the type mapper reads.
226
+ */
227
+ private _ignorePlan;
172
228
  findOne(query: DbQuery): Promise<Record<string, unknown> | null>;
173
229
  findMany(query: DbQuery): Promise<Array<Record<string, unknown>>>;
174
230
  /**
@@ -178,8 +234,19 @@ declare class PostgresAdapter extends BaseDbAdapter {
178
234
  findManyPerPartition(query: DbQuery, partitionBy: readonly string[]): Promise<Array<Record<string, unknown>>>;
179
235
  count(query: DbQuery): Promise<number>;
180
236
  aggregate(query: DbQuery): Promise<Array<Record<string, unknown>>>;
181
- /** Physical primary-key columns (`@db.column` renames applied). */
182
- private _pkColumns;
237
+ /**
238
+ * Physical primary-key columns (`@db.column` renames applied) and their
239
+ * ` RETURNING …` clause (empty without a PK) — built once per table.
240
+ */
241
+ private _pk;
242
+ /**
243
+ * Whether `filter` pins at most one row by itself: a plain object holding
244
+ * exactly the primary-key columns, each equal to a scalar (`{ id: 5 }` or
245
+ * `{ id: { $eq: 5 } }`) — what the core sends for a by-id write. Such a
246
+ * filter needs no `LIMIT 1` re-keying subquery: the outer predicate is the
247
+ * PK equality either way.
248
+ */
249
+ private _isExactPkFilter;
183
250
  /**
184
251
  * The predicate that narrows a single-row UPDATE/DELETE to the first row
185
252
  * matching `whereSql`: `<key> <op> (SELECT <cols> … LIMIT 1)`. Single-col
@@ -189,14 +256,21 @@ declare class PostgresAdapter extends BaseDbAdapter {
189
256
  * existing behavior rather than guess.
190
257
  */
191
258
  private _limitOnePredicate;
192
- updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number): Promise<TDbUpdateResult>;
193
- updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps): Promise<TDbUpdateResult>;
259
+ updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number, opts?: TDbUpdateOptions): Promise<TDbUpdateResult>;
260
+ updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, opts?: TDbUpdateOptions): Promise<TDbUpdateResult>;
194
261
  replaceOne(filter: FilterExpr, data: Record<string, unknown>, expectedVersion?: number): Promise<TDbUpdateResult>;
262
+ /** The columns a full replace assigns (`replaceColumnsFor`), built once per table. */
263
+ private _replaceColumns;
195
264
  replaceMany(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
196
265
  deleteOne(filter: FilterExpr): Promise<TDbDeleteResult>;
197
266
  deleteMany(filter: FilterExpr): Promise<TDbDeleteResult>;
267
+ private _deleteWhere;
198
268
  prepareTypeMapper(): Promise<void>;
199
- /** Whether the table declares any `db.geoPoint` fields (unencrypted). */
269
+ /**
270
+ * Whether the table declares any `db.geoPoint` fields (unencrypted) — set
271
+ * when the metadata is built (`false` before, and on an administrative
272
+ * adapter: no geo value can be formatted without built metadata).
273
+ */
200
274
  private _hasGeoPointFields;
201
275
  ensureTable(opts?: TEnsureTableOptions): Promise<void>;
202
276
  /** `schema.name` when the adapter targets a named schema. */
@@ -267,6 +341,7 @@ declare class PostgresAdapter extends BaseDbAdapter {
267
341
  /** Queries information_schema for existing FK constraints. */
268
342
  private _getExistingFkConstraints;
269
343
  getSearchIndexes(): TSearchIndexInfo[];
344
+ private _buildSearchIndexes;
270
345
  search(text: string, query: DbQuery, indexName?: string): Promise<Array<Record<string, unknown>>>;
271
346
  searchWithCount(text: string, query: DbQuery, indexName?: string): Promise<{
272
347
  data: Array<Record<string, unknown>>;
@@ -276,6 +351,8 @@ declare class PostgresAdapter extends BaseDbAdapter {
276
351
  /** Builds the tsvector SQL expression for a fulltext index's fields. Must match between index DDL and queries. */
277
352
  private _buildTsvectorExpr;
278
353
  private _getFulltextIndex;
354
+ /** The table's fulltext indexes and the default one, built once per table. */
355
+ private _fulltextIndexes;
279
356
  /**
280
357
  * Detects pgvector support by attempting to enable the extension.
281
358
  * Idempotent — safe to call multiple times.
@@ -296,11 +373,47 @@ declare class PostgresAdapter extends BaseDbAdapter {
296
373
  /** Resolves threshold: query-time $threshold > schema-level @db.search.vector.threshold. */
297
374
  private _resolveVectorThreshold;
298
375
  /**
299
- * Detects PostGIS support by attempting to enable the extension.
300
- * Idempotent — safe to call multiple times.
376
+ * Detects PostGIS support by attempting to enable the extension — schema
377
+ * sync's detection ({@link prepareTypeMapper}). Runs once; a probe that
378
+ * already found PostGIS skips it.
301
379
  */
302
380
  private _detectGeoSupport;
381
+ /**
382
+ * Whether geo column `column` is a PostGIS column (EWKT) — synchronously
383
+ * when known, else after {@link _probeGeo} on `exec` (the statement's own
384
+ * executor). A column the catalog does not show (table not created yet)
385
+ * follows PostGIS presence — what sync would create — and is probed again
386
+ * next time.
387
+ */
388
+ private _geoColumnNative;
389
+ /**
390
+ * The read-only geo probe ({@link GEO_PROBE_SQL}) of this table on `exec`:
391
+ * records PostGIS presence when not known yet, and caches the type of every
392
+ * geo column the catalog shows (unless a DDL path ran meanwhile).
393
+ *
394
+ * Concurrent callers on the SAME executor share one probe; a caller on
395
+ * another executor runs its own — waiting on a probe queued for a pool
396
+ * connection while holding a transaction connection could deadlock a
397
+ * small pool, and a transaction's failure (an aborted one) never reaches
398
+ * callers outside it. A failed probe is not cached, and a caller that
399
+ * joined it retries once (a pool probe may have hit a broken connection).
400
+ */
401
+ private _probeGeo;
402
+ private _runGeoProbe;
403
+ /**
404
+ * A DDL path ran (create / alter / recreate / rename / drop): the geo
405
+ * column types are learned anew, and a probe in flight caches nothing.
406
+ */
407
+ private _geoSchemaChanged;
408
+ /**
409
+ * PostGIS presence when known; while unknown (no sync, no statement yet),
410
+ * a table with geo columns answers `true` — the geo paths learn presence
411
+ * before they run and refuse with `GEO_NOT_SUPPORTED` then, so the first
412
+ * geo search of a process that never syncs is not refused up front.
413
+ */
303
414
  isGeoSearchable(): boolean;
415
+ /** PostGIS presence, probed when not known yet (geo search entry points). */
416
+ private _ensureGeoKnown;
304
417
  geoSearch(point: [number, number], query: DbQuery, indexName?: string): Promise<Array<Record<string, unknown>>>;
305
418
  geoSearchWithCount(point: [number, number], query: DbQuery, indexName?: string): Promise<{
306
419
  data: Array<Record<string, unknown>>;
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TFieldOps, TPrimaryKeyChange, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair, TViewCapability } from "@atscript/db";
1
+ import { AggregateFn, BaseDbAdapter, BucketUnit, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbUpdateOptions, TDbUpdateResult, TEnsureTableOptions, TExistingColumn, TFieldOps, TPrimaryKeyChange, TReferencingForeignKey, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair, TViewCapability } from "@atscript/db";
2
2
  import { TFilterVisitorOptions, TSqlFragment } from "@atscript/db-sql-tools";
3
3
  import { TMetadataMap } from "@atscript/typescript/utils";
4
4
 
@@ -79,8 +79,42 @@ declare class PostgresAdapter extends BaseDbAdapter {
79
79
  private _nocaseColumns;
80
80
  /** Whether citext extension has been provisioned (avoids redundant round-trips). */
81
81
  private _citextProvisioned;
82
- /** Whether the connected PostgreSQL instance has the PostGIS extension. */
82
+ /**
83
+ * Whether the connected PostgreSQL instance has the PostGIS extension —
84
+ * `undefined` until known: set by schema sync ({@link prepareTypeMapper},
85
+ * which installs it), or by the read-only geo probe ({@link _probeGeo}).
86
+ * Decides DDL and geo search; how a geo VALUE binds is decided per column
87
+ * ({@link _geoNative}).
88
+ */
83
89
  private _supportsGeo;
90
+ /** Whether {@link _detectGeoSupport} (the installing detection) already ran. */
91
+ private _geoInstallTried;
92
+ /** Memo of {@link _hasGeoPointFields}. */
93
+ private _geoFields?;
94
+ /** Physical names of the unencrypted `db.geoPoint` columns (set with the metadata). */
95
+ private _geoColumnNames;
96
+ /**
97
+ * Physical geo column → whether it is a PostGIS column (`geography` /
98
+ * `geometry`: EWKT) or not (JSONB: the JSON form). Learned from the catalog
99
+ * — after schema sync ({@link afterSyncTable}) or before the first statement
100
+ * binding a geo value — never from the extension alone: a JSONB column
101
+ * created before PostGIS was installed stays JSONB until a sync migrates
102
+ * it. Cleared by every DDL path ({@link _geoSchemaChanged}).
103
+ */
104
+ private _geoNative;
105
+ /** The in-flight shared geo probe and the executor it runs on. */
106
+ private _geoProbe?;
107
+ /** Bumped by {@link _geoSchemaChanged}: a probe started before a DDL caches nothing. */
108
+ private _geoSchemaGen;
109
+ /** The pool executor with geo-marker resolution (built once). */
110
+ private _poolExec?;
111
+ /** The last transaction connection's wrapped executor. */
112
+ private _txExec?;
113
+ private _pkColumnsMemo?;
114
+ private _replaceColumnsMemo?;
115
+ private _ignorePlanMemo?;
116
+ private _fulltextMemo?;
117
+ private _searchIndexesMemo?;
84
118
  /** Whether the connected PostgreSQL instance has the pgvector extension. */
85
119
  private _supportsVector;
86
120
  /** Vector fields: physical field name → { dimensions, similarity, indexName }. */
@@ -108,6 +142,19 @@ declare class PostgresAdapter extends BaseDbAdapter {
108
142
  * otherwise the pool-based driver.
109
143
  */
110
144
  private _exec;
145
+ /**
146
+ * `exec` resolving `PendingGeoPoint` params before the statement runs. Every
147
+ * adapter wraps — not only geo tables: a relational filter on another
148
+ * table's geo column carries that table's markers into this one's
149
+ * statement. A statement without markers runs as is (one param scan).
150
+ */
151
+ private _geoResolvingExec;
152
+ /**
153
+ * `$geoWithin` passed the core guard while PostGIS presence was unknown
154
+ * ({@link isGeoSearchable} answers optimistically then): learn it, and
155
+ * refuse like the guard would have when it is absent.
156
+ */
157
+ private _assertGeoWithin;
111
158
  /**
112
159
  * Relational predicates (`$some` / `$none`) render as correlated
113
160
  * `[NOT] EXISTS` subqueries — in reads and in mutation filters alike.
@@ -163,12 +210,21 @@ declare class PostgresAdapter extends BaseDbAdapter {
163
210
  * unique key columns>`: one statement per chunk (a conflict never raises, so
164
211
  * the surrounding transaction survives). Skipped rows are the ones missing
165
212
  * from RETURNING — mapped back by key values, see {@link mapIgnoredBatch}.
166
- * Each chunk runs inside a SAVEPOINT: when the mapping is ambiguous (a key
167
- * the server returns in another form than it was sent) the chunk is rolled
168
- * back to the savepoint and redone row by row with the same
169
- * `ON CONFLICT DO NOTHING`, so the result is always exact.
213
+ * A multi-row chunk of a keyed table runs inside a SAVEPOINT: when the
214
+ * mapping is ambiguous (a key the server returns in another form than it
215
+ * was sent) the chunk is rolled back to the savepoint and redone row by row
216
+ * with the same `ON CONFLICT DO NOTHING`, so the result is always exact. A
217
+ * one-row chunk (or a table without keys) maps exactly by construction, so
218
+ * it needs no savepoint — and a single such chunk no transaction either.
170
219
  */
171
220
  insertManyIgnore(data: Array<Record<string, unknown>>): Promise<TDbInsertIgnoreSlot[]>;
221
+ /**
222
+ * The key columns of a conflict-ignoring insert (built once per table):
223
+ * every primary / unique key set, their RETURNING list, and the key columns
224
+ * whose physical type is not text (see {@link mapIgnoredBatch}). Keyed on the
225
+ * detected extension state the type mapper reads.
226
+ */
227
+ private _ignorePlan;
172
228
  findOne(query: DbQuery): Promise<Record<string, unknown> | null>;
173
229
  findMany(query: DbQuery): Promise<Array<Record<string, unknown>>>;
174
230
  /**
@@ -178,8 +234,19 @@ declare class PostgresAdapter extends BaseDbAdapter {
178
234
  findManyPerPartition(query: DbQuery, partitionBy: readonly string[]): Promise<Array<Record<string, unknown>>>;
179
235
  count(query: DbQuery): Promise<number>;
180
236
  aggregate(query: DbQuery): Promise<Array<Record<string, unknown>>>;
181
- /** Physical primary-key columns (`@db.column` renames applied). */
182
- private _pkColumns;
237
+ /**
238
+ * Physical primary-key columns (`@db.column` renames applied) and their
239
+ * ` RETURNING …` clause (empty without a PK) — built once per table.
240
+ */
241
+ private _pk;
242
+ /**
243
+ * Whether `filter` pins at most one row by itself: a plain object holding
244
+ * exactly the primary-key columns, each equal to a scalar (`{ id: 5 }` or
245
+ * `{ id: { $eq: 5 } }`) — what the core sends for a by-id write. Such a
246
+ * filter needs no `LIMIT 1` re-keying subquery: the outer predicate is the
247
+ * PK equality either way.
248
+ */
249
+ private _isExactPkFilter;
183
250
  /**
184
251
  * The predicate that narrows a single-row UPDATE/DELETE to the first row
185
252
  * matching `whereSql`: `<key> <op> (SELECT <cols> … LIMIT 1)`. Single-col
@@ -189,14 +256,21 @@ declare class PostgresAdapter extends BaseDbAdapter {
189
256
  * existing behavior rather than guess.
190
257
  */
191
258
  private _limitOnePredicate;
192
- updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number): Promise<TDbUpdateResult>;
193
- updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps): Promise<TDbUpdateResult>;
259
+ updateOne(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, expectedVersion?: number, opts?: TDbUpdateOptions): Promise<TDbUpdateResult>;
260
+ updateMany(filter: FilterExpr, data: Record<string, unknown>, ops?: TFieldOps, opts?: TDbUpdateOptions): Promise<TDbUpdateResult>;
194
261
  replaceOne(filter: FilterExpr, data: Record<string, unknown>, expectedVersion?: number): Promise<TDbUpdateResult>;
262
+ /** The columns a full replace assigns (`replaceColumnsFor`), built once per table. */
263
+ private _replaceColumns;
195
264
  replaceMany(filter: FilterExpr, data: Record<string, unknown>): Promise<TDbUpdateResult>;
196
265
  deleteOne(filter: FilterExpr): Promise<TDbDeleteResult>;
197
266
  deleteMany(filter: FilterExpr): Promise<TDbDeleteResult>;
267
+ private _deleteWhere;
198
268
  prepareTypeMapper(): Promise<void>;
199
- /** Whether the table declares any `db.geoPoint` fields (unencrypted). */
269
+ /**
270
+ * Whether the table declares any `db.geoPoint` fields (unencrypted) — set
271
+ * when the metadata is built (`false` before, and on an administrative
272
+ * adapter: no geo value can be formatted without built metadata).
273
+ */
200
274
  private _hasGeoPointFields;
201
275
  ensureTable(opts?: TEnsureTableOptions): Promise<void>;
202
276
  /** `schema.name` when the adapter targets a named schema. */
@@ -267,6 +341,7 @@ declare class PostgresAdapter extends BaseDbAdapter {
267
341
  /** Queries information_schema for existing FK constraints. */
268
342
  private _getExistingFkConstraints;
269
343
  getSearchIndexes(): TSearchIndexInfo[];
344
+ private _buildSearchIndexes;
270
345
  search(text: string, query: DbQuery, indexName?: string): Promise<Array<Record<string, unknown>>>;
271
346
  searchWithCount(text: string, query: DbQuery, indexName?: string): Promise<{
272
347
  data: Array<Record<string, unknown>>;
@@ -276,6 +351,8 @@ declare class PostgresAdapter extends BaseDbAdapter {
276
351
  /** Builds the tsvector SQL expression for a fulltext index's fields. Must match between index DDL and queries. */
277
352
  private _buildTsvectorExpr;
278
353
  private _getFulltextIndex;
354
+ /** The table's fulltext indexes and the default one, built once per table. */
355
+ private _fulltextIndexes;
279
356
  /**
280
357
  * Detects pgvector support by attempting to enable the extension.
281
358
  * Idempotent — safe to call multiple times.
@@ -296,11 +373,47 @@ declare class PostgresAdapter extends BaseDbAdapter {
296
373
  /** Resolves threshold: query-time $threshold > schema-level @db.search.vector.threshold. */
297
374
  private _resolveVectorThreshold;
298
375
  /**
299
- * Detects PostGIS support by attempting to enable the extension.
300
- * Idempotent — safe to call multiple times.
376
+ * Detects PostGIS support by attempting to enable the extension — schema
377
+ * sync's detection ({@link prepareTypeMapper}). Runs once; a probe that
378
+ * already found PostGIS skips it.
301
379
  */
302
380
  private _detectGeoSupport;
381
+ /**
382
+ * Whether geo column `column` is a PostGIS column (EWKT) — synchronously
383
+ * when known, else after {@link _probeGeo} on `exec` (the statement's own
384
+ * executor). A column the catalog does not show (table not created yet)
385
+ * follows PostGIS presence — what sync would create — and is probed again
386
+ * next time.
387
+ */
388
+ private _geoColumnNative;
389
+ /**
390
+ * The read-only geo probe ({@link GEO_PROBE_SQL}) of this table on `exec`:
391
+ * records PostGIS presence when not known yet, and caches the type of every
392
+ * geo column the catalog shows (unless a DDL path ran meanwhile).
393
+ *
394
+ * Concurrent callers on the SAME executor share one probe; a caller on
395
+ * another executor runs its own — waiting on a probe queued for a pool
396
+ * connection while holding a transaction connection could deadlock a
397
+ * small pool, and a transaction's failure (an aborted one) never reaches
398
+ * callers outside it. A failed probe is not cached, and a caller that
399
+ * joined it retries once (a pool probe may have hit a broken connection).
400
+ */
401
+ private _probeGeo;
402
+ private _runGeoProbe;
403
+ /**
404
+ * A DDL path ran (create / alter / recreate / rename / drop): the geo
405
+ * column types are learned anew, and a probe in flight caches nothing.
406
+ */
407
+ private _geoSchemaChanged;
408
+ /**
409
+ * PostGIS presence when known; while unknown (no sync, no statement yet),
410
+ * a table with geo columns answers `true` — the geo paths learn presence
411
+ * before they run and refuse with `GEO_NOT_SUPPORTED` then, so the first
412
+ * geo search of a process that never syncs is not refused up front.
413
+ */
303
414
  isGeoSearchable(): boolean;
415
+ /** PostGIS presence, probed when not known yet (geo search entry points). */
416
+ private _ensureGeoKnown;
304
417
  geoSearch(point: [number, number], query: DbQuery, indexName?: string): Promise<Array<Record<string, unknown>>>;
305
418
  geoSearchWithCount(point: [number, number], query: DbQuery, indexName?: string): Promise<{
306
419
  data: Array<Record<string, unknown>>;