import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDefaultFn, TDbDeleteResult, TDbFieldMeta, TDbInsertManyResult, TDbInsertResult, TDbUpdateResult, TExistingColumn, TFieldOps, TSearchIndexInfo, TSyncColumnResult, TValueFormatterPair } from "@atscript/db"; import { TSqlFragment } from "@atscript/db-sql-tools"; import { TMetadataMap } from "@atscript/typescript/utils"; //#region src/types.d.ts /** * Result of a PostgreSQL statement that modifies data (INSERT, UPDATE, DELETE). */ interface TPgRunResult { /** Number of rows affected by the statement. */ affectedRows: number; /** Rows returned by a RETURNING clause (empty array when not applicable). */ rows: Record[]; } /** * Async driver interface for PostgreSQL engines. * * The driver has two modes of operation: * 1. **Pool mode** (default) — each call acquires/releases a connection automatically * 2. **Connection mode** (for transactions) — a dedicated connection is acquired * and all operations run on it until released * * Implementations: {@link PgDriver} */ interface TPgDriver { /** * Execute a SQL statement that modifies data (INSERT, UPDATE, DELETE, DDL). * Uses parameterized queries (`$1, $2, ...` placeholders). */ run(sql: string, params?: unknown[]): Promise; /** * Execute a query and return all matching rows. */ all>(sql: string, params?: unknown[]): Promise; /** * Execute a query and return the first matching row, or null. */ get>(sql: string, params?: unknown[]): Promise; /** * Execute raw SQL without returning results. * Used for DDL, SET statements, etc. */ exec(sql: string): Promise; /** * Acquire a dedicated connection for transaction use. * Returns a {@link TPgConnection} that must be released after use. */ getConnection(): Promise; /** * Close the pool / end all connections. */ close(): Promise; } /** * A dedicated connection acquired from the pool. * Used for transactions where all operations must run on the same connection. */ interface TPgConnection { /** Execute a statement that modifies data. */ run(sql: string, params?: unknown[]): Promise; /** Execute a query and return all matching rows. */ all>(sql: string, params?: unknown[]): Promise; /** Execute a query and return the first matching row, or null. */ get>(sql: string, params?: unknown[]): Promise; /** Execute raw SQL without returning results. */ exec(sql: string): Promise; /** Release this connection back to the pool. */ release(): void; } //#endregion //#region src/postgres-adapter.d.ts /** * PostgreSQL adapter for {@link AtscriptDbTable}. * * Accepts any {@link TPgDriver} implementation — the actual PostgreSQL driver * is fully swappable (pg Pool, custom implementations, etc.). * * Usage: * ```typescript * import { PgDriver, PostgresAdapter } from '@atscript/db-postgres' * import { DbSpace } from '@atscript/db' * * const driver = new PgDriver('postgresql://user@localhost:5432/mydb') * const space = new DbSpace(() => new PostgresAdapter(driver)) * const users = space.getTable(UsersType) * ``` */ declare class PostgresAdapter extends BaseDbAdapter { protected readonly driver: TPgDriver; supportsColumnModify: boolean; private static readonly NATIVE_DEFAULT_FNS; private _incrementFields; private _autoIncrementStart?; /** Physical column names with @db.collate 'nocase'. Used to trigger CITEXT extension. */ private _nocaseColumns; /** Whether citext extension has been provisioned (avoids redundant round-trips). */ private _citextProvisioned; /** Whether the connected PostgreSQL instance has the PostGIS extension. */ private _supportsGeo; /** Whether the connected PostgreSQL instance has the pgvector extension. */ private _supportsVector; /** Vector fields: physical field name → { dimensions, similarity, indexName }. */ private _vectorFields; /** Default similarity thresholds per vector field (from @db.search.vector.threshold). */ private _vectorThresholds; /** Schema name for queries (null falls through to 'public'). */ private get _schema(); constructor(driver: TPgDriver); protected _beginTransaction(): Promise; protected _commitTransaction(state: unknown): Promise; protected _rollbackTransaction(state: unknown): Promise; /** * Returns the active executor: dedicated connection if inside a transaction, * otherwise the pool-based driver. */ private _exec; /** PostgreSQL enforces FK constraints natively. */ supportsNativeForeignKeys(): boolean; prepareId(id: unknown, _fieldType: unknown): unknown; supportsNativeValueDefaults(): boolean; nativeDefaultFns(): ReadonlySet; onBeforeFlatten(_type: unknown): void; onAfterFlatten(): void; onFieldScanned(field: string, _type: unknown, metadata: TMetadataMap): void; getDesiredTableOptions(): never[]; getExistingTableOptions(): Promise; /** * Converts vector fields between JavaScript `number[]` and pgvector text format `[1,2,3]`. * The pg driver serializes JS arrays as PostgreSQL array literals `{1,2,3}` which is * invalid for the pgvector `vector` type — it expects bracket-delimited `[1,2,3]`. */ formatValue(field: TDbFieldMeta): TValueFormatterPair | undefined; /** * Wraps an async write operation to catch PostgreSQL constraint errors * and rethrow as structured `DbError`. * * PostgreSQL uses SQLSTATE codes: * - 23505 = unique_violation * - 23503 = foreign_key_violation */ private _wrapConstraintError; private _extractFieldFromConstraint; private _mapFkError; insertOne(data: Record): Promise; insertMany(data: Array>): Promise; findOne(query: DbQuery): Promise | null>; findMany(query: DbQuery): Promise>>; count(query: DbQuery): Promise; aggregate(query: DbQuery): Promise>>; updateOne(filter: FilterExpr, data: Record, ops?: TFieldOps, expectedVersion?: number): Promise; updateMany(filter: FilterExpr, data: Record, ops?: TFieldOps): Promise; replaceOne(filter: FilterExpr, data: Record, expectedVersion?: number): Promise; replaceMany(filter: FilterExpr, data: Record): Promise; deleteOne(filter: FilterExpr): Promise; deleteMany(filter: FilterExpr): Promise; prepareTypeMapper(): Promise; /** Whether the table declares any `db.geoPoint` fields (unencrypted). */ private _hasGeoPointFields; ensureTable(): Promise; private _ensureView; getExistingColumns(): Promise; getExistingColumnsForTable(tableName: string): Promise; syncColumns(diff: TColumnDiff): Promise; recreateTable(): Promise; afterSyncTable(): Promise; /** * Resets IDENTITY sequences to MAX(column) so that the next auto-generated * value doesn't conflict with existing data. PostgreSQL's GENERATED BY DEFAULT * AS IDENTITY does not advance the sequence when rows are inserted with explicit * values, so this is needed after data seeding, bulk imports, or recreateTable(). */ private _resetIdentitySequences; tableExists(): Promise; dropTable(): Promise; dropColumns(columns: string[]): Promise; dropIndexesForColumns(columns: string[]): Promise; dropTableByName(tableName: string): Promise; dropViewByName(viewName: string): Promise; renameTable(oldName: string): Promise; typeMapper(field: TDbFieldMeta): string; syncIndexes(): Promise; syncForeignKeys(): Promise; dropForeignKeys(fkFieldKeys: string[]): Promise; /** Queries information_schema for existing FK constraints. */ private _getExistingFkConstraints; getSearchIndexes(): TSearchIndexInfo[]; search(text: string, query: DbQuery, indexName?: string): Promise>>; searchWithCount(text: string, query: DbQuery, indexName?: string): Promise<{ data: Array>; count: number; }>; private _buildSearchWhere; /** Builds the tsvector SQL expression for a fulltext index's fields. Must match between index DDL and queries. */ private _buildTsvectorExpr; private _getFulltextIndex; /** * Detects pgvector support by attempting to enable the extension. * Idempotent — safe to call multiple times. */ private _detectVectorSupport; isVectorSearchable(): boolean; vectorSearch(vector: number[], query: DbQuery, indexName?: string): Promise>>; vectorSearchWithCount(vector: number[], query: DbQuery, indexName?: string): Promise<{ data: Array>; count: number; }>; /** Resolves vector field and computes shared context for vector search SQL builders. */ private _prepareVectorSearch; private _buildVectorSearchQuery; private _buildVectorSearchCountQuery; /** Resolves threshold: query-time $threshold > schema-level @db.search.vector.threshold. */ private _resolveVectorThreshold; /** * Detects PostGIS support by attempting to enable the extension. * Idempotent — safe to call multiple times. */ private _detectGeoSupport; isGeoSearchable(): boolean; geoSearch(point: [number, number], query: DbQuery, indexName?: string): Promise>>; geoSearchWithCount(point: [number, number], query: DbQuery, indexName?: string): Promise<{ data: Array>; count: number; }>; /** Resolves the shared parts of a geo search once (column, filter, distance, window). */ private _prepareGeoSearch; private _buildGeoSearchSelect; } //#endregion //#region src/pg-driver.d.ts /** * {@link TPgDriver} implementation backed by `pg` (node-postgres). * * Accepts a connection URI string, a `pg.PoolConfig` object, or a pre-created * `pg.Pool` instance. * * ```typescript * import { PgDriver } from '@atscript/db-postgres' * * // Connection URI * const driver = new PgDriver('postgresql://user:pass@localhost:5432/mydb') * * // Pool options * const driver = new PgDriver({ * host: 'localhost', * user: 'postgres', * database: 'mydb', * max: 10, * }) * * // Pre-created pool * import pg from 'pg' * const pool = new pg.Pool({ connectionString: '...' }) * const driver = new PgDriver(pool) * ``` * * Requires `pg` to be installed: * ```bash * pnpm add pg * ``` */ declare class PgDriver implements TPgDriver { private pool; private poolInit; constructor(poolOrConfig: string | import("pg").Pool | import("pg").PoolConfig); private getPool; run(sql: string, params?: unknown[]): Promise; all>(sql: string, params?: unknown[]): Promise; get>(sql: string, params?: unknown[]): Promise; exec(sql: string): Promise; getConnection(): Promise; close(): Promise; } //#endregion //#region src/filter-builder.d.ts /** * Translates a filter expression into a parameterized PostgreSQL WHERE clause. * * Note: The returned fragment uses `?` placeholders (not `$N`). * Finalization to `$N` happens when the fragment is consumed by a DML builder * (buildSelect, buildUpdate, etc.) via the dialect's `paramPlaceholder`. * * Case-insensitive columns (`@db.collate 'nocase'`) are handled by CITEXT * column type at the storage level — no query-side wrapping needed. */ declare function buildWhere(filter: FilterExpr): TSqlFragment; //#endregion //#region src/index.d.ts /** * Creates a {@link DbSpace} backed by a PostgreSQL connection pool. * * @param uri - PostgreSQL connection URI (e.g., `postgresql://user@localhost:5432/mydb`) * @param options - Additional pool options passed to pg. * @returns A `DbSpace` that creates `PostgresAdapter` instances per table. */ declare function createAdapter(uri: string, options?: Record): DbSpace; //#endregion export { PgDriver, PostgresAdapter, type TPgConnection, type TPgDriver, type TPgRunResult, type TSqlFragment, buildWhere, createAdapter };