joist-orm 2.3.0-next.50 → 2.3.0-next.52

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.
@@ -23,6 +23,9 @@ postgres_array = require_runtime.__toESM(postgres_array, 1);
23
23
  * avoid issues with ordering issues (cannot bulk insert A w/o knowing the id of B).
24
24
  *
25
25
  * - We use a pg-specific bulk update syntax.
26
+ *
27
+ * Query pipelining is enabled for clients created after driver construction unless the pool was
28
+ * configured with `pipeline: false`.
26
29
  */
27
30
  var PostgresDriver = class {
28
31
  pool;
@@ -35,6 +38,7 @@ var PostgresDriver = class {
35
38
  #databaseBinaryParsers = void 0;
36
39
  constructor(pool, opts) {
37
40
  this.pool = pool;
41
+ pool.options.pipeline ??= true;
38
42
  this.#idAssigner = opts?.idAssigner ?? new joist_core.SequenceIdAssigner(async (sql) => {
39
43
  this.#onQuery?.(sql);
40
44
  return (await pool.query(sql)).rows;
@@ -1 +1 @@
1
- {"version":3,"file":"PostgresDriver.cjs","names":["#idAssigner","#preloadPlugin","#onQuery","SequenceIdAssigner","ensureLazyDataRows","#executeFindRowData","getRuntimeConfig","buildRawQuery","#databaseBinaryParsers","registerDatabaseBinaryParsers","isRowDataCapableClient","PojoRowData","executeRowDataQuery","driverBeforeBegin","driverAfterBegin","driverBeforeCommit","driverAfterCommit","fail","generateOps","cleanSql","kq","kqDot","JoinRowOperation","partition","array","getTypeParser","builtins"],"sources":["../../src/drivers/PostgresDriver.ts"],"sourcesContent":["import {\n type DeleteOp,\n type Driver,\n type EntityManager,\n type IdAssigner,\n type InsertOp,\n JoinRowOperation,\n type JoinRowTodo,\n type OpColumn,\n type ParsedFindQuery,\n PojoRowData,\n type PreloadPlugin,\n type RowData,\n type RuntimeConfig,\n SequenceIdAssigner,\n type Todo,\n type UpdateOp,\n buildRawQuery,\n cleanSql,\n driverAfterBegin,\n driverAfterCommit,\n driverBeforeBegin,\n driverBeforeCommit,\n ensureRectangularArraySizes,\n fail,\n generateOps,\n getRuntimeConfig,\n kq,\n kqDot,\n partition,\n} from \"joist-core\";\nimport pg from \"pg\";\nimport { builtins, getTypeParser } from \"pg-types\";\nimport array from \"postgres-array\";\n\nimport { registerDatabaseBinaryParsers, registerTemporalBinaryParsers } from \"./binaryParsers.ts\";\nimport { ensureLazyDataRows } from \"./patchPgProtocol.ts\";\nimport { executeRowDataQuery, isRowDataCapableClient } from \"./WireRowData.ts\";\n\nexport interface PostgresDriverOpts {\n idAssigner?: IdAssigner;\n /** Sets a default `PreloadPlugin` for any `EntityManager` that uses this driver. */\n preloadPlugin?: PreloadPlugin;\n /** Called after each query is executed, useful for testing/debugging. */\n onQuery?: (sql: string) => void;\n /**\n * Experimental: keeps entity find results as raw wire bytes and decodes each row/column cell\n * lazily on field access, instead of eagerly materializing POJO rows.\n *\n * Applies to unpaginated `em.find`, `em.load`, and o2m/o2o/recursive relation loads against\n * the pure-JS pg client. Other loaders deliberately stay classic: paginated finds (small\n * pages, measured neutral), m2m join-table rows (narrow + fully read, where materialized rows\n * measured faster, and `JoinRows` owns/mutates them), lazy columns, and id/count loaders.\n *\n * Lazy queries also request *binary* result format (prototype), decoding int/bool/float/text\n * cells — and, via the fast paths `setupLatestPgTypes` registers, date/timestamptz cells —\n * wire-bytes -> value with no intermediate strings; numeric/exotic cells render to pg's\n * canonical text and reuse the active text parsers for parity. `JOIST_LAZY_BINARY=0`\n * reverts lazy queries to text-format results. It\n * uses classic rows when the choice is knowable up-front (patching pg-protocol failed, or the\n * client is unsupported, i.e. pg-native — both warn once). If a connection turns out\n * mid-query to use an unpatched pg-protocol copy (a duplicate `pg` install), the query fails\n * with a descriptive error — a misconfiguration CI builds/smoketests will catch immediately.\n * Note deferred decoding also defers custom-parser errors from `await em.find` to first field\n * access. See JS-ROW-STORE-DESIGN.md.\n */\n lazyRows?: boolean;\n}\n\n/**\n * Provides a callback for tests/debugging.\n *\n * We used to get this for-free from knex, but node-pg does not have a `query` event.\n */\ntype OnQuery = ((sql: string) => void) | undefined;\n\n/**\n * Implements the `Driver` interface for Postgres.\n *\n * This is the canonical driver implementation and leverages several aspects of\n * Postgres for the best performance, i.e.:\n *\n * - Deferred foreign key constraints are used to allow bulk inserting/updating\n * all entities without any topographic sorting/ordering issues.\n *\n * - Sequences are used to bulk-assign + then bulk-insert all entities, to again\n * avoid issues with ordering issues (cannot bulk insert A w/o knowing the id of B).\n *\n * - We use a pg-specific bulk update syntax.\n */\nexport class PostgresDriver implements Driver<pg.PoolClient> {\n readonly #idAssigner: IdAssigner;\n readonly #preloadPlugin: PreloadPlugin | undefined;\n readonly #onQuery: OnQuery;\n readonly lazyRows: boolean;\n /** Defined only when `lazyRows` is enabled; its presence is the capability signal the EM checks. */\n readonly executeFindRowData: Driver[\"executeFindRowData\"];\n #databaseBinaryParsers: Promise<void> | undefined = undefined;\n\n constructor(\n readonly pool: pg.Pool,\n opts?: PostgresDriverOpts,\n ) {\n this.#idAssigner =\n opts?.idAssigner ??\n new SequenceIdAssigner(async (sql: string) => {\n this.#onQuery?.(sql);\n return (await pool.query(sql)).rows;\n });\n this.#preloadPlugin = opts?.preloadPlugin;\n this.#onQuery = opts?.onQuery;\n // Lazy rows require pg-protocol to emit lazy DataRows, which we patch in at runtime; if the\n // patch cannot be applied/verified (i.e. future pg-protocol internals changed), stay classic\n this.lazyRows = (opts?.lazyRows ?? false) && ensureLazyDataRows();\n if (opts?.lazyRows && !this.lazyRows) {\n console.warn(\"joist-orm: lazyRows was requested, but patching pg-protocol failed; using classic rows.\");\n }\n this.executeFindRowData = this.lazyRows\n ? (em, parsed, settings) => this.#executeFindRowData(em, parsed, settings)\n : undefined;\n setupLatestPgTypes(getRuntimeConfig().temporal);\n }\n\n async executeFind(\n em: EntityManager,\n parsed: ParsedFindQuery,\n settings: { limit?: number; offset?: number },\n ): Promise<any[]> {\n const { sql, bindings } = buildRawQuery(parsed, { limit: em.entityLimit, ...settings });\n return this.executeQuery(em, sql, bindings);\n }\n\n async #executeFindRowData(\n em: EntityManager,\n parsed: ParsedFindQuery,\n settings: { limit?: number; offset?: number },\n ): Promise<RowData> {\n // Auto-register binary parsers for the db's dynamic-oid text-likes (native enums, citext)\n // once, before our first lazy query; on failure, reset so the next query retries\n await (this.#databaseBinaryParsers ??= registerDatabaseBinaryParsers(this.pool).catch((err) => {\n this.#databaseBinaryParsers = undefined;\n throw err;\n }));\n const { sql, bindings } = buildRawQuery(parsed, { limit: em.entityLimit, ...settings });\n const pgSql = toPgParams(sql);\n this.#onQuery?.(pgSql);\n // Centralize client checkout here (pg-pool rejects Submittables in pool.query), and decide\n // classic-vs-lazy *before* submitting anything, i.e. unsupported clients like pg-native fall\n // back to classic rows rather than failing mid-query\n const txnClient = em.txn as pg.PoolClient | undefined;\n const client = txnClient ?? (await this.pool.connect());\n try {\n if (!isRowDataCapableClient(client)) {\n warnUnsupportedClientOnce();\n return new PojoRowData((await (client as pg.PoolClient).query(pgSql, bindings as any[])).rows);\n }\n return await executeRowDataQuery(client, pgSql, bindings);\n } finally {\n if (!txnClient) client.release();\n }\n }\n\n async executeQuery(em: EntityManager, sql: string, bindings: readonly any[]): Promise<any[]> {\n const pgSql = toPgParams(sql);\n this.#onQuery?.(pgSql);\n const client = this.getMaybeInTxnClient(em);\n return client.query(pgSql, bindings as any[]).then((result) => result.rows);\n }\n\n async transaction<T>(em: EntityManager, fn: (txn: pg.PoolClient) => Promise<T>): Promise<T> {\n if (em.txn) {\n return fn(em.txn as pg.PoolClient);\n }\n const client = await this.pool.connect();\n let result: T;\n try {\n await driverBeforeBegin(em, client);\n this.#onQuery?.(\"BEGIN;\");\n await client.query(\"BEGIN\");\n em.txn = client;\n try {\n await driverAfterBegin(em, client);\n result = await fn(client);\n await driverBeforeCommit(em, client);\n this.#onQuery?.(\"COMMIT;\");\n await client.query(\"COMMIT\");\n } catch (e) {\n await client.query(\"ROLLBACK\");\n throw e;\n } finally {\n em.txn = undefined;\n }\n await driverAfterCommit(em, client);\n } finally {\n client.release();\n }\n return result;\n }\n\n async assignNewIds(_: EntityManager, todos: Record<string, Todo>): Promise<void> {\n return this.#idAssigner.assignNewIds(todos);\n }\n\n async flush(\n em: EntityManager,\n entityTodos: Record<string, Todo>,\n joinRows: Record<string, JoinRowTodo>,\n ): Promise<void> {\n const client = (em.txn ?? fail(\"Expected EntityManager.txn to be set\")) as pg.PoolClient;\n await this.#idAssigner.assignNewIds(entityTodos);\n const ops = generateOps(entityTodos);\n const onQuery = this.#onQuery;\n // Do INSERTs+UPDATEs first so that we avoid DELETE cascades invalidating oplocks\n // See https://github.com/joist-orm/joist-orm/issues/591\n await Promise.all([\n ...ops.inserts.map((op) => batchInsert(client, op, onQuery)),\n ...ops.updates.map((op) => batchUpdate(client, op, onQuery)),\n ...ops.deletes.map((op) => batchDelete(client, op, onQuery)),\n ...Object.entries(joinRows).flatMap(([joinTableName, todo]) => {\n return [\n m2mBatchInsert(client, joinTableName, todo, onQuery),\n m2mBatchDelete(client, joinTableName, todo, onQuery),\n ];\n }),\n ]);\n }\n\n get defaultPlugins() {\n return { preloadPlugin: this.#preloadPlugin };\n }\n\n private getMaybeInTxnClient(em: EntityManager): pg.PoolClient | pg.Pool {\n return (em.txn as pg.PoolClient) || this.pool;\n }\n}\n\nasync function batchInsert(client: pg.PoolClient, op: InsertOp, onQuery: OnQuery): Promise<unknown> {\n const { tableName, columns, columnValues } = op;\n const [cte, bindings] = buildUnnestCte(\"data\", columns, columnValues);\n const sql = cleanSql(`\n ${cte}\n INSERT INTO ${kq(tableName)} (${columns.map((c) => kq(c.columnName)).join(\", \")})\n SELECT * FROM data\n `);\n const pgSql = toPgParams(sql);\n onQuery?.(pgSql);\n return client.query(pgSql, bindings);\n}\n\nasync function batchUpdate(client: pg.PoolClient, op: UpdateOp, onQuery: OnQuery): Promise<void> {\n const { tableName, columns, columnValues, updatedAt } = op;\n\n const [cte, bindings] = buildUnnestCte(\"data\", columns, columnValues);\n\n // JS Dates only have millisecond-level precision, so we may have dropped/lost accuracy when\n // reading Postgres's microsecond-level `timestamptz` values; using `date_trunc` \"downgrades\"\n // the pg data to match what we have in the JS Date.\n //\n // ...but Temporal's types don't have this flaw.\n const truncateToMills = !getRuntimeConfig().temporal;\n const maybeUpdatedAt =\n updatedAt && truncateToMills\n ? ` AND date_trunc('milliseconds', ${kqDot(tableName, updatedAt)}) = data.__original_updated_at`\n : updatedAt\n ? ` AND ${kqDot(tableName, updatedAt)} = data.__original_updated_at`\n : \"\";\n\n const sql = `\n ${cte}\n UPDATE ${kq(tableName)}\n SET ${columns\n .filter((c) => c.columnName !== \"id\" && c.columnName !== \"__original_updated_at\")\n .map((c) => `${kq(c.columnName)} = data.${kq(c.columnName)}`)\n .join(\", \")}\n FROM data\n WHERE ${kq(tableName)}.id = data.id ${maybeUpdatedAt}\n RETURNING ${kq(tableName)}.id\n `;\n\n const pgSql = toPgParams(cleanSql(sql));\n onQuery?.(pgSql);\n const result = await client.query(pgSql, bindings);\n const results = result.rows;\n\n const ids = columnValues[0]; // assume id is the 1st column\n if (results.length !== ids.length) {\n const updated = new Set(results.map((r: any) => r.id));\n const missing = ids.filter((id) => !updated.has(id));\n throw new Error(`Oplock failure for ${tableName} rows ${missing.join(\", \")}`);\n }\n}\n\nasync function batchDelete(client: pg.PoolClient, op: DeleteOp, onQuery: OnQuery): Promise<void> {\n const { tableName, ids } = op;\n const pgSql = toPgParams(`DELETE FROM ${kq(tableName)} WHERE id = ANY(?)`);\n onQuery?.(pgSql);\n await client.query(pgSql, [ids]);\n}\n\n/** Creates a CTE named `tableName` that bulk-injects the `columnValues` into a SQL query. */\nfunction buildUnnestCte(tableName: string, columns: OpColumn[], columnValues: any[][]): [string, any[][]] {\n ensureRectangularArraySizes(columns, columnValues);\n const selects = columns.map((c) => {\n if (c.dbType.endsWith(\"[]\")) {\n if (c.isNullableArray) {\n return `unnest_arrays(?::${c.dbType}[], true) as ${kq(c.columnName)}`;\n } else {\n return `unnest_arrays(?::${c.dbType}[]) as ${kq(c.columnName)}`;\n }\n } else {\n return `unnest(?::${c.dbType}[]) as ${kq(c.columnName)}`;\n }\n });\n const sql = `WITH ${tableName} AS (SELECT ${selects.join(\", \")})`;\n return [sql, columnValues];\n}\n\nasync function m2mBatchInsert(client: pg.PoolClient, joinTableName: string, todo: JoinRowTodo, onQuery: OnQuery) {\n const { m2m, newRows } = todo;\n if (newRows.length === 0) return;\n const col1Values = newRows.map((row) => todo.dbValue(row, m2m.columnName));\n const col2Values = newRows.map((row) => todo.dbValue(row, m2m.otherColumnName));\n if (m2m.hasJoinTableId) {\n const sql = cleanSql(`\n WITH data AS (SELECT unnest(?::int[]) as ${kq(m2m.columnName)}, unnest(?::int[]) as ${kq(m2m.otherColumnName)})\n INSERT INTO ${kq(joinTableName)} (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)})\n SELECT * FROM data\n ON CONFLICT (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) DO UPDATE SET id = ${kq(joinTableName)}.id\n RETURNING id;\n `);\n const pgSql = toPgParams(sql);\n onQuery?.(pgSql);\n const { rows } = await client.query(pgSql, [col1Values, col2Values]);\n for (let i = 0; i < rows.length; i++) {\n newRows[i].id = rows[i].id;\n newRows[i].op = JoinRowOperation.Flushed;\n newRows[i].persisted = true;\n }\n } else {\n // Id-less join tables have no surrogate id to return; the FK pair is the PK, so just\n // insert and let any duplicate be a no-op.\n const sql = cleanSql(`\n WITH data AS (SELECT unnest(?::int[]) as ${kq(m2m.columnName)}, unnest(?::int[]) as ${kq(m2m.otherColumnName)})\n INSERT INTO ${kq(joinTableName)} (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)})\n SELECT * FROM data\n ON CONFLICT (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) DO NOTHING;\n `);\n const pgSql = toPgParams(sql);\n onQuery?.(pgSql);\n await client.query(pgSql, [col1Values, col2Values]);\n for (const row of newRows) {\n row.op = JoinRowOperation.Flushed;\n row.persisted = true;\n }\n }\n}\n\nasync function m2mBatchDelete(client: pg.PoolClient, joinTableName: string, todo: JoinRowTodo, onQuery: OnQuery) {\n const { m2m, deletedRows } = todo;\n if (deletedRows.length === 0) return;\n // Rows with a surrogate id are deleted by id; rows without one — id-less tables, or `remove`s\n // done against an unloaded ManyToManyCollection — are deleted by their (col1, col2) composite.\n const [haveIds, noIds] = partition(deletedRows, (r) => {\n // We used to use `id !== -1` as a marker for \"row is/is-not persisted in the db\" -- but when adding support\n // for id-column-less m2m tables, we couldn't use `id === -1` as this marker anymore, so now rely on a dedicated\n // `persisted` key instead.\n //\n // So, ideally, this `id !== -1` could go away _except_ that our internal verisoning plugin leverages this \"m2m rows\n // with an id=-1 delete by their FK values\" to handle our versioned m2m tables. And for now it's easier to\n // keep/restore this `id !== -1` than refactor that.\n return r.id !== undefined && r.id !== -1;\n });\n if (haveIds.length > 0) {\n const pgSql = toPgParams(`DELETE FROM ${kq(joinTableName)} WHERE id = ANY(?)`);\n onQuery?.(pgSql);\n await client.query(pgSql, [haveIds.map((r) => r.id!)]);\n }\n if (noIds.length > 0) {\n const data = noIds\n // Watch for m2m rows that got added-then-removed to entities that were themselves added-then-removed,\n // as the deTagId will be undefined for those, as we're skipping adding them to the database.\n .filter((row) => !todo.isNew(row, m2m.columnName) && !todo.isNew(row, m2m.otherColumnName))\n .map((row) => [todo.dbValue(row, m2m.columnName), todo.dbValue(row, m2m.otherColumnName)] as any);\n if (data.length > 0) {\n const pgSql = toPgParams(`\n DELETE FROM ${kq(joinTableName)}\n WHERE (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) IN (\n SELECT (data->>0)::int, (data->>1)::int FROM jsonb_array_elements(?) data\n )`);\n onQuery?.(pgSql);\n await client.query(pgSql, [JSON.stringify(data)]);\n }\n }\n deletedRows.forEach((row) => {\n row.id = undefined;\n row.persisted = false;\n row.op = JoinRowOperation.Flushed;\n });\n}\n\n/**\n * Configures the `pg` driver with the best type parsers.\n *\n * In particular, pg's default `TIMESTAMPTZ` parser is very inefficient, and even just\n * pulling in the latest `pg-types` and installing the fixed parser makes a noticable\n * difference.\n */\nexport function setupLatestPgTypes(temporal: RuntimeConfig[\"temporal\"]): void {\n if (temporal) {\n // Don't eagerly parse the strings, instead defer to the serde logic. This only governs\n // classic *text* results (knex, pool.query, non-lazy drivers); binary lazy cells construct\n // Temporal values directly, with no strings involved (see registerTemporalBinaryParsers).\n const noop = (s: string) => s;\n const noopArray = (s: string) => array.parse(s, noop);\n\n const { TIMESTAMP, TIMESTAMPTZ, DATE } = pg.types.builtins;\n pg.types.setTypeParser(DATE, noop);\n pg.types.setTypeParser(TIMESTAMP, noop);\n pg.types.setTypeParser(TIMESTAMPTZ, noop);\n\n // Use `as number` b/c the typings of shadowed pg-types from `pg` and `pg-types` top-level don't line up\n pg.types.setTypeParser(1182 as number, noopArray); // date[]\n pg.types.setTypeParser(1115 as number, noopArray); // timestamp[]\n pg.types.setTypeParser(1185 as number, noopArray); // timestamptz[]\n\n registerTemporalBinaryParsers();\n } else {\n pg.types.setTypeParser(pg.types.builtins.TIMESTAMPTZ, getTypeParser(builtins.TIMESTAMPTZ));\n // The binary registry's date/timestamp/timestamptz builtins already decode to Dates\n }\n}\n\nconst questionMarks = /(?<!@)\\?/g;\n\n/**\n * Converts our knex-style `?` placeholders to pg-native `$1, $2, ...` numbered parameters.\n *\n * We skipping postgres operators like `@?`. */\nfunction toPgParams(sql: string): string {\n let i = 0;\n return sql.replace(questionMarks, () => `$${++i}`);\n}\n\nlet warnedUnsupportedClient = false;\n\n/** Warns once when lazyRows degrades to classic rows for an unsupported client, i.e. pg-native. */\nfunction warnUnsupportedClientOnce(): void {\n if (warnedUnsupportedClient) return;\n warnedUnsupportedClient = true;\n console.warn(\"joist-orm: lazyRows is enabled, but this client does not support it; using classic rows.\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PostgresDriver.cjs","names":["#idAssigner","#preloadPlugin","#onQuery","SequenceIdAssigner","ensureLazyDataRows","#executeFindRowData","getRuntimeConfig","buildRawQuery","#databaseBinaryParsers","registerDatabaseBinaryParsers","isRowDataCapableClient","PojoRowData","executeRowDataQuery","driverBeforeBegin","driverAfterBegin","driverBeforeCommit","driverAfterCommit","fail","generateOps","cleanSql","kq","kqDot","JoinRowOperation","partition","array","getTypeParser","builtins"],"sources":["../../src/drivers/PostgresDriver.ts"],"sourcesContent":["import {\n type DeleteOp,\n type Driver,\n type EntityManager,\n type IdAssigner,\n type InsertOp,\n JoinRowOperation,\n type JoinRowTodo,\n type OpColumn,\n type ParsedFindQuery,\n PojoRowData,\n type PreloadPlugin,\n type RowData,\n type RuntimeConfig,\n SequenceIdAssigner,\n type Todo,\n type UpdateOp,\n buildRawQuery,\n cleanSql,\n driverAfterBegin,\n driverAfterCommit,\n driverBeforeBegin,\n driverBeforeCommit,\n ensureRectangularArraySizes,\n fail,\n generateOps,\n getRuntimeConfig,\n kq,\n kqDot,\n partition,\n} from \"joist-core\";\nimport pg from \"pg\";\nimport { builtins, getTypeParser } from \"pg-types\";\nimport array from \"postgres-array\";\n\nimport { registerDatabaseBinaryParsers, registerTemporalBinaryParsers } from \"./binaryParsers.ts\";\nimport { ensureLazyDataRows } from \"./patchPgProtocol.ts\";\nimport { executeRowDataQuery, isRowDataCapableClient } from \"./WireRowData.ts\";\n\nexport interface PostgresDriverOpts {\n idAssigner?: IdAssigner;\n /** Sets a default `PreloadPlugin` for any `EntityManager` that uses this driver. */\n preloadPlugin?: PreloadPlugin;\n /** Called after each query is executed, useful for testing/debugging. */\n onQuery?: (sql: string) => void;\n /**\n * Experimental: keeps entity find results as raw wire bytes and decodes each row/column cell\n * lazily on field access, instead of eagerly materializing POJO rows.\n *\n * Applies to unpaginated `em.find`, `em.load`, and o2m/o2o/recursive relation loads against\n * the pure-JS pg client. Other loaders deliberately stay classic: paginated finds (small\n * pages, measured neutral), m2m join-table rows (narrow + fully read, where materialized rows\n * measured faster, and `JoinRows` owns/mutates them), lazy columns, and id/count loaders.\n *\n * Lazy queries also request *binary* result format (prototype), decoding int/bool/float/text\n * cells — and, via the fast paths `setupLatestPgTypes` registers, date/timestamptz cells —\n * wire-bytes -> value with no intermediate strings; numeric/exotic cells render to pg's\n * canonical text and reuse the active text parsers for parity. `JOIST_LAZY_BINARY=0`\n * reverts lazy queries to text-format results. It\n * uses classic rows when the choice is knowable up-front (patching pg-protocol failed, or the\n * client is unsupported, i.e. pg-native — both warn once). If a connection turns out\n * mid-query to use an unpatched pg-protocol copy (a duplicate `pg` install), the query fails\n * with a descriptive error — a misconfiguration CI builds/smoketests will catch immediately.\n * Note deferred decoding also defers custom-parser errors from `await em.find` to first field\n * access. See JS-ROW-STORE-DESIGN.md.\n */\n lazyRows?: boolean;\n}\n\n/**\n * Provides a callback for tests/debugging.\n *\n * We used to get this for-free from knex, but node-pg does not have a `query` event.\n */\ntype OnQuery = ((sql: string) => void) | undefined;\n\n/**\n * Implements the `Driver` interface for Postgres.\n *\n * This is the canonical driver implementation and leverages several aspects of\n * Postgres for the best performance, i.e.:\n *\n * - Deferred foreign key constraints are used to allow bulk inserting/updating\n * all entities without any topographic sorting/ordering issues.\n *\n * - Sequences are used to bulk-assign + then bulk-insert all entities, to again\n * avoid issues with ordering issues (cannot bulk insert A w/o knowing the id of B).\n *\n * - We use a pg-specific bulk update syntax.\n *\n * Query pipelining is enabled for clients created after driver construction unless the pool was\n * configured with `pipeline: false`.\n */\nexport class PostgresDriver implements Driver<pg.PoolClient> {\n readonly #idAssigner: IdAssigner;\n readonly #preloadPlugin: PreloadPlugin | undefined;\n readonly #onQuery: OnQuery;\n readonly lazyRows: boolean;\n /** Defined only when `lazyRows` is enabled; its presence is the capability signal the EM checks. */\n readonly executeFindRowData: Driver[\"executeFindRowData\"];\n #databaseBinaryParsers: Promise<void> | undefined = undefined;\n\n constructor(\n readonly pool: pg.Pool,\n opts?: PostgresDriverOpts,\n ) {\n // Set the option before pg creates clients, but preserve an explicit consumer opt-out.\n pool.options.pipeline ??= true;\n this.#idAssigner =\n opts?.idAssigner ??\n new SequenceIdAssigner(async (sql: string) => {\n this.#onQuery?.(sql);\n return (await pool.query(sql)).rows;\n });\n this.#preloadPlugin = opts?.preloadPlugin;\n this.#onQuery = opts?.onQuery;\n // Lazy rows require pg-protocol to emit lazy DataRows, which we patch in at runtime; if the\n // patch cannot be applied/verified (i.e. future pg-protocol internals changed), stay classic\n this.lazyRows = (opts?.lazyRows ?? false) && ensureLazyDataRows();\n if (opts?.lazyRows && !this.lazyRows) {\n console.warn(\"joist-orm: lazyRows was requested, but patching pg-protocol failed; using classic rows.\");\n }\n this.executeFindRowData = this.lazyRows\n ? (em, parsed, settings) => this.#executeFindRowData(em, parsed, settings)\n : undefined;\n setupLatestPgTypes(getRuntimeConfig().temporal);\n }\n\n async executeFind(\n em: EntityManager,\n parsed: ParsedFindQuery,\n settings: { limit?: number; offset?: number },\n ): Promise<any[]> {\n const { sql, bindings } = buildRawQuery(parsed, { limit: em.entityLimit, ...settings });\n return this.executeQuery(em, sql, bindings);\n }\n\n async #executeFindRowData(\n em: EntityManager,\n parsed: ParsedFindQuery,\n settings: { limit?: number; offset?: number },\n ): Promise<RowData> {\n // Auto-register binary parsers for the db's dynamic-oid text-likes (native enums, citext)\n // once, before our first lazy query; on failure, reset so the next query retries\n await (this.#databaseBinaryParsers ??= registerDatabaseBinaryParsers(this.pool).catch((err) => {\n this.#databaseBinaryParsers = undefined;\n throw err;\n }));\n const { sql, bindings } = buildRawQuery(parsed, { limit: em.entityLimit, ...settings });\n const pgSql = toPgParams(sql);\n this.#onQuery?.(pgSql);\n // Centralize client checkout here (pg-pool rejects Submittables in pool.query), and decide\n // classic-vs-lazy *before* submitting anything, i.e. unsupported clients like pg-native fall\n // back to classic rows rather than failing mid-query\n const txnClient = em.txn as pg.PoolClient | undefined;\n const client = txnClient ?? (await this.pool.connect());\n try {\n if (!isRowDataCapableClient(client)) {\n warnUnsupportedClientOnce();\n return new PojoRowData((await (client as pg.PoolClient).query(pgSql, bindings as any[])).rows);\n }\n return await executeRowDataQuery(client, pgSql, bindings);\n } finally {\n if (!txnClient) client.release();\n }\n }\n\n async executeQuery(em: EntityManager, sql: string, bindings: readonly any[]): Promise<any[]> {\n const pgSql = toPgParams(sql);\n this.#onQuery?.(pgSql);\n const client = this.getMaybeInTxnClient(em);\n return client.query(pgSql, bindings as any[]).then((result) => result.rows);\n }\n\n async transaction<T>(em: EntityManager, fn: (txn: pg.PoolClient) => Promise<T>): Promise<T> {\n if (em.txn) {\n return fn(em.txn as pg.PoolClient);\n }\n const client = await this.pool.connect();\n let result: T;\n try {\n await driverBeforeBegin(em, client);\n this.#onQuery?.(\"BEGIN;\");\n await client.query(\"BEGIN\");\n em.txn = client;\n try {\n await driverAfterBegin(em, client);\n result = await fn(client);\n await driverBeforeCommit(em, client);\n this.#onQuery?.(\"COMMIT;\");\n await client.query(\"COMMIT\");\n } catch (e) {\n await client.query(\"ROLLBACK\");\n throw e;\n } finally {\n em.txn = undefined;\n }\n await driverAfterCommit(em, client);\n } finally {\n client.release();\n }\n return result;\n }\n\n async assignNewIds(_: EntityManager, todos: Record<string, Todo>): Promise<void> {\n return this.#idAssigner.assignNewIds(todos);\n }\n\n async flush(\n em: EntityManager,\n entityTodos: Record<string, Todo>,\n joinRows: Record<string, JoinRowTodo>,\n ): Promise<void> {\n const client = (em.txn ?? fail(\"Expected EntityManager.txn to be set\")) as pg.PoolClient;\n await this.#idAssigner.assignNewIds(entityTodos);\n const ops = generateOps(entityTodos);\n const onQuery = this.#onQuery;\n // Do INSERTs+UPDATEs first so that we avoid DELETE cascades invalidating oplocks\n // See https://github.com/joist-orm/joist-orm/issues/591\n await Promise.all([\n ...ops.inserts.map((op) => batchInsert(client, op, onQuery)),\n ...ops.updates.map((op) => batchUpdate(client, op, onQuery)),\n ...ops.deletes.map((op) => batchDelete(client, op, onQuery)),\n ...Object.entries(joinRows).flatMap(([joinTableName, todo]) => {\n return [\n m2mBatchInsert(client, joinTableName, todo, onQuery),\n m2mBatchDelete(client, joinTableName, todo, onQuery),\n ];\n }),\n ]);\n }\n\n get defaultPlugins() {\n return { preloadPlugin: this.#preloadPlugin };\n }\n\n private getMaybeInTxnClient(em: EntityManager): pg.PoolClient | pg.Pool {\n return (em.txn as pg.PoolClient) || this.pool;\n }\n}\n\nasync function batchInsert(client: pg.PoolClient, op: InsertOp, onQuery: OnQuery): Promise<unknown> {\n const { tableName, columns, columnValues } = op;\n const [cte, bindings] = buildUnnestCte(\"data\", columns, columnValues);\n const sql = cleanSql(`\n ${cte}\n INSERT INTO ${kq(tableName)} (${columns.map((c) => kq(c.columnName)).join(\", \")})\n SELECT * FROM data\n `);\n const pgSql = toPgParams(sql);\n onQuery?.(pgSql);\n return client.query(pgSql, bindings);\n}\n\nasync function batchUpdate(client: pg.PoolClient, op: UpdateOp, onQuery: OnQuery): Promise<void> {\n const { tableName, columns, columnValues, updatedAt } = op;\n\n const [cte, bindings] = buildUnnestCte(\"data\", columns, columnValues);\n\n // JS Dates only have millisecond-level precision, so we may have dropped/lost accuracy when\n // reading Postgres's microsecond-level `timestamptz` values; using `date_trunc` \"downgrades\"\n // the pg data to match what we have in the JS Date.\n //\n // ...but Temporal's types don't have this flaw.\n const truncateToMills = !getRuntimeConfig().temporal;\n const maybeUpdatedAt =\n updatedAt && truncateToMills\n ? ` AND date_trunc('milliseconds', ${kqDot(tableName, updatedAt)}) = data.__original_updated_at`\n : updatedAt\n ? ` AND ${kqDot(tableName, updatedAt)} = data.__original_updated_at`\n : \"\";\n\n const sql = `\n ${cte}\n UPDATE ${kq(tableName)}\n SET ${columns\n .filter((c) => c.columnName !== \"id\" && c.columnName !== \"__original_updated_at\")\n .map((c) => `${kq(c.columnName)} = data.${kq(c.columnName)}`)\n .join(\", \")}\n FROM data\n WHERE ${kq(tableName)}.id = data.id ${maybeUpdatedAt}\n RETURNING ${kq(tableName)}.id\n `;\n\n const pgSql = toPgParams(cleanSql(sql));\n onQuery?.(pgSql);\n const result = await client.query(pgSql, bindings);\n const results = result.rows;\n\n const ids = columnValues[0]; // assume id is the 1st column\n if (results.length !== ids.length) {\n const updated = new Set(results.map((r: any) => r.id));\n const missing = ids.filter((id) => !updated.has(id));\n throw new Error(`Oplock failure for ${tableName} rows ${missing.join(\", \")}`);\n }\n}\n\nasync function batchDelete(client: pg.PoolClient, op: DeleteOp, onQuery: OnQuery): Promise<void> {\n const { tableName, ids } = op;\n const pgSql = toPgParams(`DELETE FROM ${kq(tableName)} WHERE id = ANY(?)`);\n onQuery?.(pgSql);\n await client.query(pgSql, [ids]);\n}\n\n/** Creates a CTE named `tableName` that bulk-injects the `columnValues` into a SQL query. */\nfunction buildUnnestCte(tableName: string, columns: OpColumn[], columnValues: any[][]): [string, any[][]] {\n ensureRectangularArraySizes(columns, columnValues);\n const selects = columns.map((c) => {\n if (c.dbType.endsWith(\"[]\")) {\n if (c.isNullableArray) {\n return `unnest_arrays(?::${c.dbType}[], true) as ${kq(c.columnName)}`;\n } else {\n return `unnest_arrays(?::${c.dbType}[]) as ${kq(c.columnName)}`;\n }\n } else {\n return `unnest(?::${c.dbType}[]) as ${kq(c.columnName)}`;\n }\n });\n const sql = `WITH ${tableName} AS (SELECT ${selects.join(\", \")})`;\n return [sql, columnValues];\n}\n\nasync function m2mBatchInsert(client: pg.PoolClient, joinTableName: string, todo: JoinRowTodo, onQuery: OnQuery) {\n const { m2m, newRows } = todo;\n if (newRows.length === 0) return;\n const col1Values = newRows.map((row) => todo.dbValue(row, m2m.columnName));\n const col2Values = newRows.map((row) => todo.dbValue(row, m2m.otherColumnName));\n if (m2m.hasJoinTableId) {\n const sql = cleanSql(`\n WITH data AS (SELECT unnest(?::int[]) as ${kq(m2m.columnName)}, unnest(?::int[]) as ${kq(m2m.otherColumnName)})\n INSERT INTO ${kq(joinTableName)} (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)})\n SELECT * FROM data\n ON CONFLICT (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) DO UPDATE SET id = ${kq(joinTableName)}.id\n RETURNING id;\n `);\n const pgSql = toPgParams(sql);\n onQuery?.(pgSql);\n const { rows } = await client.query(pgSql, [col1Values, col2Values]);\n for (let i = 0; i < rows.length; i++) {\n newRows[i].id = rows[i].id;\n newRows[i].op = JoinRowOperation.Flushed;\n newRows[i].persisted = true;\n }\n } else {\n // Id-less join tables have no surrogate id to return; the FK pair is the PK, so just\n // insert and let any duplicate be a no-op.\n const sql = cleanSql(`\n WITH data AS (SELECT unnest(?::int[]) as ${kq(m2m.columnName)}, unnest(?::int[]) as ${kq(m2m.otherColumnName)})\n INSERT INTO ${kq(joinTableName)} (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)})\n SELECT * FROM data\n ON CONFLICT (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) DO NOTHING;\n `);\n const pgSql = toPgParams(sql);\n onQuery?.(pgSql);\n await client.query(pgSql, [col1Values, col2Values]);\n for (const row of newRows) {\n row.op = JoinRowOperation.Flushed;\n row.persisted = true;\n }\n }\n}\n\nasync function m2mBatchDelete(client: pg.PoolClient, joinTableName: string, todo: JoinRowTodo, onQuery: OnQuery) {\n const { m2m, deletedRows } = todo;\n if (deletedRows.length === 0) return;\n // Rows with a surrogate id are deleted by id; rows without one — id-less tables, or `remove`s\n // done against an unloaded ManyToManyCollection — are deleted by their (col1, col2) composite.\n const [haveIds, noIds] = partition(deletedRows, (r) => {\n // We used to use `id !== -1` as a marker for \"row is/is-not persisted in the db\" -- but when adding support\n // for id-column-less m2m tables, we couldn't use `id === -1` as this marker anymore, so now rely on a dedicated\n // `persisted` key instead.\n //\n // So, ideally, this `id !== -1` could go away _except_ that our internal verisoning plugin leverages this \"m2m rows\n // with an id=-1 delete by their FK values\" to handle our versioned m2m tables. And for now it's easier to\n // keep/restore this `id !== -1` than refactor that.\n return r.id !== undefined && r.id !== -1;\n });\n if (haveIds.length > 0) {\n const pgSql = toPgParams(`DELETE FROM ${kq(joinTableName)} WHERE id = ANY(?)`);\n onQuery?.(pgSql);\n await client.query(pgSql, [haveIds.map((r) => r.id!)]);\n }\n if (noIds.length > 0) {\n const data = noIds\n // Watch for m2m rows that got added-then-removed to entities that were themselves added-then-removed,\n // as the deTagId will be undefined for those, as we're skipping adding them to the database.\n .filter((row) => !todo.isNew(row, m2m.columnName) && !todo.isNew(row, m2m.otherColumnName))\n .map((row) => [todo.dbValue(row, m2m.columnName), todo.dbValue(row, m2m.otherColumnName)] as any);\n if (data.length > 0) {\n const pgSql = toPgParams(`\n DELETE FROM ${kq(joinTableName)}\n WHERE (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) IN (\n SELECT (data->>0)::int, (data->>1)::int FROM jsonb_array_elements(?) data\n )`);\n onQuery?.(pgSql);\n await client.query(pgSql, [JSON.stringify(data)]);\n }\n }\n deletedRows.forEach((row) => {\n row.id = undefined;\n row.persisted = false;\n row.op = JoinRowOperation.Flushed;\n });\n}\n\n/**\n * Configures the `pg` driver with the best type parsers.\n *\n * In particular, pg's default `TIMESTAMPTZ` parser is very inefficient, and even just\n * pulling in the latest `pg-types` and installing the fixed parser makes a noticable\n * difference.\n */\nexport function setupLatestPgTypes(temporal: RuntimeConfig[\"temporal\"]): void {\n if (temporal) {\n // Don't eagerly parse the strings, instead defer to the serde logic. This only governs\n // classic *text* results (knex, pool.query, non-lazy drivers); binary lazy cells construct\n // Temporal values directly, with no strings involved (see registerTemporalBinaryParsers).\n const noop = (s: string) => s;\n const noopArray = (s: string) => array.parse(s, noop);\n\n const { TIMESTAMP, TIMESTAMPTZ, DATE } = pg.types.builtins;\n pg.types.setTypeParser(DATE, noop);\n pg.types.setTypeParser(TIMESTAMP, noop);\n pg.types.setTypeParser(TIMESTAMPTZ, noop);\n\n // Use `as number` b/c the typings of shadowed pg-types from `pg` and `pg-types` top-level don't line up\n pg.types.setTypeParser(1182 as number, noopArray); // date[]\n pg.types.setTypeParser(1115 as number, noopArray); // timestamp[]\n pg.types.setTypeParser(1185 as number, noopArray); // timestamptz[]\n\n registerTemporalBinaryParsers();\n } else {\n pg.types.setTypeParser(pg.types.builtins.TIMESTAMPTZ, getTypeParser(builtins.TIMESTAMPTZ));\n // The binary registry's date/timestamp/timestamptz builtins already decode to Dates\n }\n}\n\nconst questionMarks = /(?<!@)\\?/g;\n\n/**\n * Converts our knex-style `?` placeholders to pg-native `$1, $2, ...` numbered parameters.\n *\n * We skipping postgres operators like `@?`. */\nfunction toPgParams(sql: string): string {\n let i = 0;\n return sql.replace(questionMarks, () => `$${++i}`);\n}\n\nlet warnedUnsupportedClient = false;\n\n/** Warns once when lazyRows degrades to classic rows for an unsupported client, i.e. pg-native. */\nfunction warnUnsupportedClientOnce(): void {\n if (warnedUnsupportedClient) return;\n warnedUnsupportedClient = true;\n console.warn(\"joist-orm: lazyRows is enabled, but this client does not support it; using classic rows.\");\n}\n"],"mappings":"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"}
@@ -43,6 +43,9 @@ interface PostgresDriverOpts {
43
43
  * avoid issues with ordering issues (cannot bulk insert A w/o knowing the id of B).
44
44
  *
45
45
  * - We use a pg-specific bulk update syntax.
46
+ *
47
+ * Query pipelining is enabled for clients created after driver construction unless the pool was
48
+ * configured with `pipeline: false`.
46
49
  */
47
50
  declare class PostgresDriver implements Driver<pg.PoolClient> {
48
51
  #private;
@@ -1 +1 @@
1
- {"version":3,"file":"PostgresDriver.d.cts","names":[],"sources":["../../src/drivers/PostgresDriver.ts"],"mappings":";;;UAuCiB;EACf,aAAa;;EAEb,gBAAgB;;EAEhB,WAAW;;;;;;;;;;;;;;;;;;;;;;EAsBX;;;;;;;;;;;;;;;;cAwBW,0BAA0B,OAAO,GAAG;;WAUpC,MAAM,GAAG;WANX;;WAEA,oBAAoB;EAG7B,YACW,MAAM,GAAG,MAClB,OAAO;EAsBH,YACJ,IAAI,eACJ,QAAQ,iBACR;IAAY;IAAgB;MAC3B;EAmCG,aAAa,IAAI,eAAe,aAAa,2BAA2B;EAOxE,YAAY,GAAG,IAAI,eAAe,KAAK,KAAK,GAAG,eAAe,QAAQ,KAAK,QAAQ;EA8BnF,aAAa,GAAG,eAAe,OAAO,eAAe,QAAQ;EAI7D,MACJ,IAAI,eACJ,aAAa,eAAe,OAC5B,UAAU,eAAe,eACxB;MAoBC;IACO;;UAGH;;;;;;;;;iBAgLM,mBAAmB,UAAU"}
1
+ {"version":3,"file":"PostgresDriver.d.cts","names":[],"sources":["../../src/drivers/PostgresDriver.ts"],"mappings":";;;UAuCiB;EACf,aAAa;;EAEb,gBAAgB;;EAEhB,WAAW;;;;;;;;;;;;;;;;;;;;;;EAsBX;;;;;;;;;;;;;;;;;;;cA2BW,0BAA0B,OAAO,GAAG;;WAUpC,MAAM,GAAG;WANX;;WAEA,oBAAoB;EAG7B,YACW,MAAM,GAAG,MAClB,OAAO;EAwBH,YACJ,IAAI,eACJ,QAAQ,iBACR;IAAY;IAAgB;MAC3B;EAmCG,aAAa,IAAI,eAAe,aAAa,2BAA2B;EAOxE,YAAY,GAAG,IAAI,eAAe,KAAK,KAAK,GAAG,eAAe,QAAQ,KAAK,QAAQ;EA8BnF,aAAa,GAAG,eAAe,OAAO,eAAe,QAAQ;EAI7D,MACJ,IAAI,eACJ,aAAa,eAAe,OAC5B,UAAU,eAAe,eACxB;MAoBC;IACO;;UAGH;;;;;;;;;iBAgLM,mBAAmB,UAAU"}
@@ -43,6 +43,9 @@ interface PostgresDriverOpts {
43
43
  * avoid issues with ordering issues (cannot bulk insert A w/o knowing the id of B).
44
44
  *
45
45
  * - We use a pg-specific bulk update syntax.
46
+ *
47
+ * Query pipelining is enabled for clients created after driver construction unless the pool was
48
+ * configured with `pipeline: false`.
46
49
  */
47
50
  declare class PostgresDriver implements Driver<pg.PoolClient> {
48
51
  #private;
@@ -1 +1 @@
1
- {"version":3,"file":"PostgresDriver.d.mts","names":[],"sources":["../../src/drivers/PostgresDriver.ts"],"mappings":";;;UAuCiB;EACf,aAAa;;EAEb,gBAAgB;;EAEhB,WAAW;;;;;;;;;;;;;;;;;;;;;;EAsBX;;;;;;;;;;;;;;;;cAwBW,0BAA0B,OAAO,GAAG;;WAUpC,MAAM,GAAG;WANX;;WAEA,oBAAoB;EAG7B,YACW,MAAM,GAAG,MAClB,OAAO;EAsBH,YACJ,IAAI,eACJ,QAAQ,iBACR;IAAY;IAAgB;MAC3B;EAmCG,aAAa,IAAI,eAAe,aAAa,2BAA2B;EAOxE,YAAY,GAAG,IAAI,eAAe,KAAK,KAAK,GAAG,eAAe,QAAQ,KAAK,QAAQ;EA8BnF,aAAa,GAAG,eAAe,OAAO,eAAe,QAAQ;EAI7D,MACJ,IAAI,eACJ,aAAa,eAAe,OAC5B,UAAU,eAAe,eACxB;MAoBC;IACO;;UAGH;;;;;;;;;iBAgLM,mBAAmB,UAAU"}
1
+ {"version":3,"file":"PostgresDriver.d.mts","names":[],"sources":["../../src/drivers/PostgresDriver.ts"],"mappings":";;;UAuCiB;EACf,aAAa;;EAEb,gBAAgB;;EAEhB,WAAW;;;;;;;;;;;;;;;;;;;;;;EAsBX;;;;;;;;;;;;;;;;;;;cA2BW,0BAA0B,OAAO,GAAG;;WAUpC,MAAM,GAAG;WANX;;WAEA,oBAAoB;EAG7B,YACW,MAAM,GAAG,MAClB,OAAO;EAwBH,YACJ,IAAI,eACJ,QAAQ,iBACR;IAAY;IAAgB;MAC3B;EAmCG,aAAa,IAAI,eAAe,aAAa,2BAA2B;EAOxE,YAAY,GAAG,IAAI,eAAe,KAAK,KAAK,GAAG,eAAe,QAAQ,KAAK,QAAQ;EA8BnF,aAAa,GAAG,eAAe,OAAO,eAAe,QAAQ;EAI7D,MACJ,IAAI,eACJ,aAAa,eAAe,OAC5B,UAAU,eAAe,eACxB;MAoBC;IACO;;UAGH;;;;;;;;;iBAgLM,mBAAmB,UAAU"}
@@ -19,6 +19,9 @@ import array from "postgres-array";
19
19
  * avoid issues with ordering issues (cannot bulk insert A w/o knowing the id of B).
20
20
  *
21
21
  * - We use a pg-specific bulk update syntax.
22
+ *
23
+ * Query pipelining is enabled for clients created after driver construction unless the pool was
24
+ * configured with `pipeline: false`.
22
25
  */
23
26
  var PostgresDriver = class {
24
27
  pool;
@@ -31,6 +34,7 @@ var PostgresDriver = class {
31
34
  #databaseBinaryParsers = void 0;
32
35
  constructor(pool, opts) {
33
36
  this.pool = pool;
37
+ pool.options.pipeline ??= true;
34
38
  this.#idAssigner = opts?.idAssigner ?? new SequenceIdAssigner(async (sql) => {
35
39
  this.#onQuery?.(sql);
36
40
  return (await pool.query(sql)).rows;
@@ -1 +1 @@
1
- {"version":3,"file":"PostgresDriver.js","names":["#idAssigner","#preloadPlugin","#onQuery","#executeFindRowData","#databaseBinaryParsers"],"sources":["../../src/drivers/PostgresDriver.ts"],"sourcesContent":["import {\n type DeleteOp,\n type Driver,\n type EntityManager,\n type IdAssigner,\n type InsertOp,\n JoinRowOperation,\n type JoinRowTodo,\n type OpColumn,\n type ParsedFindQuery,\n PojoRowData,\n type PreloadPlugin,\n type RowData,\n type RuntimeConfig,\n SequenceIdAssigner,\n type Todo,\n type UpdateOp,\n buildRawQuery,\n cleanSql,\n driverAfterBegin,\n driverAfterCommit,\n driverBeforeBegin,\n driverBeforeCommit,\n ensureRectangularArraySizes,\n fail,\n generateOps,\n getRuntimeConfig,\n kq,\n kqDot,\n partition,\n} from \"joist-core\";\nimport pg from \"pg\";\nimport { builtins, getTypeParser } from \"pg-types\";\nimport array from \"postgres-array\";\n\nimport { registerDatabaseBinaryParsers, registerTemporalBinaryParsers } from \"./binaryParsers.ts\";\nimport { ensureLazyDataRows } from \"./patchPgProtocol.ts\";\nimport { executeRowDataQuery, isRowDataCapableClient } from \"./WireRowData.ts\";\n\nexport interface PostgresDriverOpts {\n idAssigner?: IdAssigner;\n /** Sets a default `PreloadPlugin` for any `EntityManager` that uses this driver. */\n preloadPlugin?: PreloadPlugin;\n /** Called after each query is executed, useful for testing/debugging. */\n onQuery?: (sql: string) => void;\n /**\n * Experimental: keeps entity find results as raw wire bytes and decodes each row/column cell\n * lazily on field access, instead of eagerly materializing POJO rows.\n *\n * Applies to unpaginated `em.find`, `em.load`, and o2m/o2o/recursive relation loads against\n * the pure-JS pg client. Other loaders deliberately stay classic: paginated finds (small\n * pages, measured neutral), m2m join-table rows (narrow + fully read, where materialized rows\n * measured faster, and `JoinRows` owns/mutates them), lazy columns, and id/count loaders.\n *\n * Lazy queries also request *binary* result format (prototype), decoding int/bool/float/text\n * cells — and, via the fast paths `setupLatestPgTypes` registers, date/timestamptz cells —\n * wire-bytes -> value with no intermediate strings; numeric/exotic cells render to pg's\n * canonical text and reuse the active text parsers for parity. `JOIST_LAZY_BINARY=0`\n * reverts lazy queries to text-format results. It\n * uses classic rows when the choice is knowable up-front (patching pg-protocol failed, or the\n * client is unsupported, i.e. pg-native — both warn once). If a connection turns out\n * mid-query to use an unpatched pg-protocol copy (a duplicate `pg` install), the query fails\n * with a descriptive error — a misconfiguration CI builds/smoketests will catch immediately.\n * Note deferred decoding also defers custom-parser errors from `await em.find` to first field\n * access. See JS-ROW-STORE-DESIGN.md.\n */\n lazyRows?: boolean;\n}\n\n/**\n * Provides a callback for tests/debugging.\n *\n * We used to get this for-free from knex, but node-pg does not have a `query` event.\n */\ntype OnQuery = ((sql: string) => void) | undefined;\n\n/**\n * Implements the `Driver` interface for Postgres.\n *\n * This is the canonical driver implementation and leverages several aspects of\n * Postgres for the best performance, i.e.:\n *\n * - Deferred foreign key constraints are used to allow bulk inserting/updating\n * all entities without any topographic sorting/ordering issues.\n *\n * - Sequences are used to bulk-assign + then bulk-insert all entities, to again\n * avoid issues with ordering issues (cannot bulk insert A w/o knowing the id of B).\n *\n * - We use a pg-specific bulk update syntax.\n */\nexport class PostgresDriver implements Driver<pg.PoolClient> {\n readonly #idAssigner: IdAssigner;\n readonly #preloadPlugin: PreloadPlugin | undefined;\n readonly #onQuery: OnQuery;\n readonly lazyRows: boolean;\n /** Defined only when `lazyRows` is enabled; its presence is the capability signal the EM checks. */\n readonly executeFindRowData: Driver[\"executeFindRowData\"];\n #databaseBinaryParsers: Promise<void> | undefined = undefined;\n\n constructor(\n readonly pool: pg.Pool,\n opts?: PostgresDriverOpts,\n ) {\n this.#idAssigner =\n opts?.idAssigner ??\n new SequenceIdAssigner(async (sql: string) => {\n this.#onQuery?.(sql);\n return (await pool.query(sql)).rows;\n });\n this.#preloadPlugin = opts?.preloadPlugin;\n this.#onQuery = opts?.onQuery;\n // Lazy rows require pg-protocol to emit lazy DataRows, which we patch in at runtime; if the\n // patch cannot be applied/verified (i.e. future pg-protocol internals changed), stay classic\n this.lazyRows = (opts?.lazyRows ?? false) && ensureLazyDataRows();\n if (opts?.lazyRows && !this.lazyRows) {\n console.warn(\"joist-orm: lazyRows was requested, but patching pg-protocol failed; using classic rows.\");\n }\n this.executeFindRowData = this.lazyRows\n ? (em, parsed, settings) => this.#executeFindRowData(em, parsed, settings)\n : undefined;\n setupLatestPgTypes(getRuntimeConfig().temporal);\n }\n\n async executeFind(\n em: EntityManager,\n parsed: ParsedFindQuery,\n settings: { limit?: number; offset?: number },\n ): Promise<any[]> {\n const { sql, bindings } = buildRawQuery(parsed, { limit: em.entityLimit, ...settings });\n return this.executeQuery(em, sql, bindings);\n }\n\n async #executeFindRowData(\n em: EntityManager,\n parsed: ParsedFindQuery,\n settings: { limit?: number; offset?: number },\n ): Promise<RowData> {\n // Auto-register binary parsers for the db's dynamic-oid text-likes (native enums, citext)\n // once, before our first lazy query; on failure, reset so the next query retries\n await (this.#databaseBinaryParsers ??= registerDatabaseBinaryParsers(this.pool).catch((err) => {\n this.#databaseBinaryParsers = undefined;\n throw err;\n }));\n const { sql, bindings } = buildRawQuery(parsed, { limit: em.entityLimit, ...settings });\n const pgSql = toPgParams(sql);\n this.#onQuery?.(pgSql);\n // Centralize client checkout here (pg-pool rejects Submittables in pool.query), and decide\n // classic-vs-lazy *before* submitting anything, i.e. unsupported clients like pg-native fall\n // back to classic rows rather than failing mid-query\n const txnClient = em.txn as pg.PoolClient | undefined;\n const client = txnClient ?? (await this.pool.connect());\n try {\n if (!isRowDataCapableClient(client)) {\n warnUnsupportedClientOnce();\n return new PojoRowData((await (client as pg.PoolClient).query(pgSql, bindings as any[])).rows);\n }\n return await executeRowDataQuery(client, pgSql, bindings);\n } finally {\n if (!txnClient) client.release();\n }\n }\n\n async executeQuery(em: EntityManager, sql: string, bindings: readonly any[]): Promise<any[]> {\n const pgSql = toPgParams(sql);\n this.#onQuery?.(pgSql);\n const client = this.getMaybeInTxnClient(em);\n return client.query(pgSql, bindings as any[]).then((result) => result.rows);\n }\n\n async transaction<T>(em: EntityManager, fn: (txn: pg.PoolClient) => Promise<T>): Promise<T> {\n if (em.txn) {\n return fn(em.txn as pg.PoolClient);\n }\n const client = await this.pool.connect();\n let result: T;\n try {\n await driverBeforeBegin(em, client);\n this.#onQuery?.(\"BEGIN;\");\n await client.query(\"BEGIN\");\n em.txn = client;\n try {\n await driverAfterBegin(em, client);\n result = await fn(client);\n await driverBeforeCommit(em, client);\n this.#onQuery?.(\"COMMIT;\");\n await client.query(\"COMMIT\");\n } catch (e) {\n await client.query(\"ROLLBACK\");\n throw e;\n } finally {\n em.txn = undefined;\n }\n await driverAfterCommit(em, client);\n } finally {\n client.release();\n }\n return result;\n }\n\n async assignNewIds(_: EntityManager, todos: Record<string, Todo>): Promise<void> {\n return this.#idAssigner.assignNewIds(todos);\n }\n\n async flush(\n em: EntityManager,\n entityTodos: Record<string, Todo>,\n joinRows: Record<string, JoinRowTodo>,\n ): Promise<void> {\n const client = (em.txn ?? fail(\"Expected EntityManager.txn to be set\")) as pg.PoolClient;\n await this.#idAssigner.assignNewIds(entityTodos);\n const ops = generateOps(entityTodos);\n const onQuery = this.#onQuery;\n // Do INSERTs+UPDATEs first so that we avoid DELETE cascades invalidating oplocks\n // See https://github.com/joist-orm/joist-orm/issues/591\n await Promise.all([\n ...ops.inserts.map((op) => batchInsert(client, op, onQuery)),\n ...ops.updates.map((op) => batchUpdate(client, op, onQuery)),\n ...ops.deletes.map((op) => batchDelete(client, op, onQuery)),\n ...Object.entries(joinRows).flatMap(([joinTableName, todo]) => {\n return [\n m2mBatchInsert(client, joinTableName, todo, onQuery),\n m2mBatchDelete(client, joinTableName, todo, onQuery),\n ];\n }),\n ]);\n }\n\n get defaultPlugins() {\n return { preloadPlugin: this.#preloadPlugin };\n }\n\n private getMaybeInTxnClient(em: EntityManager): pg.PoolClient | pg.Pool {\n return (em.txn as pg.PoolClient) || this.pool;\n }\n}\n\nasync function batchInsert(client: pg.PoolClient, op: InsertOp, onQuery: OnQuery): Promise<unknown> {\n const { tableName, columns, columnValues } = op;\n const [cte, bindings] = buildUnnestCte(\"data\", columns, columnValues);\n const sql = cleanSql(`\n ${cte}\n INSERT INTO ${kq(tableName)} (${columns.map((c) => kq(c.columnName)).join(\", \")})\n SELECT * FROM data\n `);\n const pgSql = toPgParams(sql);\n onQuery?.(pgSql);\n return client.query(pgSql, bindings);\n}\n\nasync function batchUpdate(client: pg.PoolClient, op: UpdateOp, onQuery: OnQuery): Promise<void> {\n const { tableName, columns, columnValues, updatedAt } = op;\n\n const [cte, bindings] = buildUnnestCte(\"data\", columns, columnValues);\n\n // JS Dates only have millisecond-level precision, so we may have dropped/lost accuracy when\n // reading Postgres's microsecond-level `timestamptz` values; using `date_trunc` \"downgrades\"\n // the pg data to match what we have in the JS Date.\n //\n // ...but Temporal's types don't have this flaw.\n const truncateToMills = !getRuntimeConfig().temporal;\n const maybeUpdatedAt =\n updatedAt && truncateToMills\n ? ` AND date_trunc('milliseconds', ${kqDot(tableName, updatedAt)}) = data.__original_updated_at`\n : updatedAt\n ? ` AND ${kqDot(tableName, updatedAt)} = data.__original_updated_at`\n : \"\";\n\n const sql = `\n ${cte}\n UPDATE ${kq(tableName)}\n SET ${columns\n .filter((c) => c.columnName !== \"id\" && c.columnName !== \"__original_updated_at\")\n .map((c) => `${kq(c.columnName)} = data.${kq(c.columnName)}`)\n .join(\", \")}\n FROM data\n WHERE ${kq(tableName)}.id = data.id ${maybeUpdatedAt}\n RETURNING ${kq(tableName)}.id\n `;\n\n const pgSql = toPgParams(cleanSql(sql));\n onQuery?.(pgSql);\n const result = await client.query(pgSql, bindings);\n const results = result.rows;\n\n const ids = columnValues[0]; // assume id is the 1st column\n if (results.length !== ids.length) {\n const updated = new Set(results.map((r: any) => r.id));\n const missing = ids.filter((id) => !updated.has(id));\n throw new Error(`Oplock failure for ${tableName} rows ${missing.join(\", \")}`);\n }\n}\n\nasync function batchDelete(client: pg.PoolClient, op: DeleteOp, onQuery: OnQuery): Promise<void> {\n const { tableName, ids } = op;\n const pgSql = toPgParams(`DELETE FROM ${kq(tableName)} WHERE id = ANY(?)`);\n onQuery?.(pgSql);\n await client.query(pgSql, [ids]);\n}\n\n/** Creates a CTE named `tableName` that bulk-injects the `columnValues` into a SQL query. */\nfunction buildUnnestCte(tableName: string, columns: OpColumn[], columnValues: any[][]): [string, any[][]] {\n ensureRectangularArraySizes(columns, columnValues);\n const selects = columns.map((c) => {\n if (c.dbType.endsWith(\"[]\")) {\n if (c.isNullableArray) {\n return `unnest_arrays(?::${c.dbType}[], true) as ${kq(c.columnName)}`;\n } else {\n return `unnest_arrays(?::${c.dbType}[]) as ${kq(c.columnName)}`;\n }\n } else {\n return `unnest(?::${c.dbType}[]) as ${kq(c.columnName)}`;\n }\n });\n const sql = `WITH ${tableName} AS (SELECT ${selects.join(\", \")})`;\n return [sql, columnValues];\n}\n\nasync function m2mBatchInsert(client: pg.PoolClient, joinTableName: string, todo: JoinRowTodo, onQuery: OnQuery) {\n const { m2m, newRows } = todo;\n if (newRows.length === 0) return;\n const col1Values = newRows.map((row) => todo.dbValue(row, m2m.columnName));\n const col2Values = newRows.map((row) => todo.dbValue(row, m2m.otherColumnName));\n if (m2m.hasJoinTableId) {\n const sql = cleanSql(`\n WITH data AS (SELECT unnest(?::int[]) as ${kq(m2m.columnName)}, unnest(?::int[]) as ${kq(m2m.otherColumnName)})\n INSERT INTO ${kq(joinTableName)} (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)})\n SELECT * FROM data\n ON CONFLICT (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) DO UPDATE SET id = ${kq(joinTableName)}.id\n RETURNING id;\n `);\n const pgSql = toPgParams(sql);\n onQuery?.(pgSql);\n const { rows } = await client.query(pgSql, [col1Values, col2Values]);\n for (let i = 0; i < rows.length; i++) {\n newRows[i].id = rows[i].id;\n newRows[i].op = JoinRowOperation.Flushed;\n newRows[i].persisted = true;\n }\n } else {\n // Id-less join tables have no surrogate id to return; the FK pair is the PK, so just\n // insert and let any duplicate be a no-op.\n const sql = cleanSql(`\n WITH data AS (SELECT unnest(?::int[]) as ${kq(m2m.columnName)}, unnest(?::int[]) as ${kq(m2m.otherColumnName)})\n INSERT INTO ${kq(joinTableName)} (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)})\n SELECT * FROM data\n ON CONFLICT (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) DO NOTHING;\n `);\n const pgSql = toPgParams(sql);\n onQuery?.(pgSql);\n await client.query(pgSql, [col1Values, col2Values]);\n for (const row of newRows) {\n row.op = JoinRowOperation.Flushed;\n row.persisted = true;\n }\n }\n}\n\nasync function m2mBatchDelete(client: pg.PoolClient, joinTableName: string, todo: JoinRowTodo, onQuery: OnQuery) {\n const { m2m, deletedRows } = todo;\n if (deletedRows.length === 0) return;\n // Rows with a surrogate id are deleted by id; rows without one — id-less tables, or `remove`s\n // done against an unloaded ManyToManyCollection — are deleted by their (col1, col2) composite.\n const [haveIds, noIds] = partition(deletedRows, (r) => {\n // We used to use `id !== -1` as a marker for \"row is/is-not persisted in the db\" -- but when adding support\n // for id-column-less m2m tables, we couldn't use `id === -1` as this marker anymore, so now rely on a dedicated\n // `persisted` key instead.\n //\n // So, ideally, this `id !== -1` could go away _except_ that our internal verisoning plugin leverages this \"m2m rows\n // with an id=-1 delete by their FK values\" to handle our versioned m2m tables. And for now it's easier to\n // keep/restore this `id !== -1` than refactor that.\n return r.id !== undefined && r.id !== -1;\n });\n if (haveIds.length > 0) {\n const pgSql = toPgParams(`DELETE FROM ${kq(joinTableName)} WHERE id = ANY(?)`);\n onQuery?.(pgSql);\n await client.query(pgSql, [haveIds.map((r) => r.id!)]);\n }\n if (noIds.length > 0) {\n const data = noIds\n // Watch for m2m rows that got added-then-removed to entities that were themselves added-then-removed,\n // as the deTagId will be undefined for those, as we're skipping adding them to the database.\n .filter((row) => !todo.isNew(row, m2m.columnName) && !todo.isNew(row, m2m.otherColumnName))\n .map((row) => [todo.dbValue(row, m2m.columnName), todo.dbValue(row, m2m.otherColumnName)] as any);\n if (data.length > 0) {\n const pgSql = toPgParams(`\n DELETE FROM ${kq(joinTableName)}\n WHERE (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) IN (\n SELECT (data->>0)::int, (data->>1)::int FROM jsonb_array_elements(?) data\n )`);\n onQuery?.(pgSql);\n await client.query(pgSql, [JSON.stringify(data)]);\n }\n }\n deletedRows.forEach((row) => {\n row.id = undefined;\n row.persisted = false;\n row.op = JoinRowOperation.Flushed;\n });\n}\n\n/**\n * Configures the `pg` driver with the best type parsers.\n *\n * In particular, pg's default `TIMESTAMPTZ` parser is very inefficient, and even just\n * pulling in the latest `pg-types` and installing the fixed parser makes a noticable\n * difference.\n */\nexport function setupLatestPgTypes(temporal: RuntimeConfig[\"temporal\"]): void {\n if (temporal) {\n // Don't eagerly parse the strings, instead defer to the serde logic. This only governs\n // classic *text* results (knex, pool.query, non-lazy drivers); binary lazy cells construct\n // Temporal values directly, with no strings involved (see registerTemporalBinaryParsers).\n const noop = (s: string) => s;\n const noopArray = (s: string) => array.parse(s, noop);\n\n const { TIMESTAMP, TIMESTAMPTZ, DATE } = pg.types.builtins;\n pg.types.setTypeParser(DATE, noop);\n pg.types.setTypeParser(TIMESTAMP, noop);\n pg.types.setTypeParser(TIMESTAMPTZ, noop);\n\n // Use `as number` b/c the typings of shadowed pg-types from `pg` and `pg-types` top-level don't line up\n pg.types.setTypeParser(1182 as number, noopArray); // date[]\n pg.types.setTypeParser(1115 as number, noopArray); // timestamp[]\n pg.types.setTypeParser(1185 as number, noopArray); // timestamptz[]\n\n registerTemporalBinaryParsers();\n } else {\n pg.types.setTypeParser(pg.types.builtins.TIMESTAMPTZ, getTypeParser(builtins.TIMESTAMPTZ));\n // The binary registry's date/timestamp/timestamptz builtins already decode to Dates\n }\n}\n\nconst questionMarks = /(?<!@)\\?/g;\n\n/**\n * Converts our knex-style `?` placeholders to pg-native `$1, $2, ...` numbered parameters.\n *\n * We skipping postgres operators like `@?`. */\nfunction toPgParams(sql: string): string {\n let i = 0;\n return sql.replace(questionMarks, () => `$${++i}`);\n}\n\nlet warnedUnsupportedClient = false;\n\n/** Warns once when lazyRows degrades to classic rows for an unsupported client, i.e. pg-native. */\nfunction warnUnsupportedClientOnce(): void {\n if (warnedUnsupportedClient) return;\n warnedUnsupportedClient = true;\n console.warn(\"joist-orm: lazyRows is enabled, but this client does not support it; using classic rows.\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PostgresDriver.js","names":["#idAssigner","#preloadPlugin","#onQuery","#executeFindRowData","#databaseBinaryParsers"],"sources":["../../src/drivers/PostgresDriver.ts"],"sourcesContent":["import {\n type DeleteOp,\n type Driver,\n type EntityManager,\n type IdAssigner,\n type InsertOp,\n JoinRowOperation,\n type JoinRowTodo,\n type OpColumn,\n type ParsedFindQuery,\n PojoRowData,\n type PreloadPlugin,\n type RowData,\n type RuntimeConfig,\n SequenceIdAssigner,\n type Todo,\n type UpdateOp,\n buildRawQuery,\n cleanSql,\n driverAfterBegin,\n driverAfterCommit,\n driverBeforeBegin,\n driverBeforeCommit,\n ensureRectangularArraySizes,\n fail,\n generateOps,\n getRuntimeConfig,\n kq,\n kqDot,\n partition,\n} from \"joist-core\";\nimport pg from \"pg\";\nimport { builtins, getTypeParser } from \"pg-types\";\nimport array from \"postgres-array\";\n\nimport { registerDatabaseBinaryParsers, registerTemporalBinaryParsers } from \"./binaryParsers.ts\";\nimport { ensureLazyDataRows } from \"./patchPgProtocol.ts\";\nimport { executeRowDataQuery, isRowDataCapableClient } from \"./WireRowData.ts\";\n\nexport interface PostgresDriverOpts {\n idAssigner?: IdAssigner;\n /** Sets a default `PreloadPlugin` for any `EntityManager` that uses this driver. */\n preloadPlugin?: PreloadPlugin;\n /** Called after each query is executed, useful for testing/debugging. */\n onQuery?: (sql: string) => void;\n /**\n * Experimental: keeps entity find results as raw wire bytes and decodes each row/column cell\n * lazily on field access, instead of eagerly materializing POJO rows.\n *\n * Applies to unpaginated `em.find`, `em.load`, and o2m/o2o/recursive relation loads against\n * the pure-JS pg client. Other loaders deliberately stay classic: paginated finds (small\n * pages, measured neutral), m2m join-table rows (narrow + fully read, where materialized rows\n * measured faster, and `JoinRows` owns/mutates them), lazy columns, and id/count loaders.\n *\n * Lazy queries also request *binary* result format (prototype), decoding int/bool/float/text\n * cells — and, via the fast paths `setupLatestPgTypes` registers, date/timestamptz cells —\n * wire-bytes -> value with no intermediate strings; numeric/exotic cells render to pg's\n * canonical text and reuse the active text parsers for parity. `JOIST_LAZY_BINARY=0`\n * reverts lazy queries to text-format results. It\n * uses classic rows when the choice is knowable up-front (patching pg-protocol failed, or the\n * client is unsupported, i.e. pg-native — both warn once). If a connection turns out\n * mid-query to use an unpatched pg-protocol copy (a duplicate `pg` install), the query fails\n * with a descriptive error — a misconfiguration CI builds/smoketests will catch immediately.\n * Note deferred decoding also defers custom-parser errors from `await em.find` to first field\n * access. See JS-ROW-STORE-DESIGN.md.\n */\n lazyRows?: boolean;\n}\n\n/**\n * Provides a callback for tests/debugging.\n *\n * We used to get this for-free from knex, but node-pg does not have a `query` event.\n */\ntype OnQuery = ((sql: string) => void) | undefined;\n\n/**\n * Implements the `Driver` interface for Postgres.\n *\n * This is the canonical driver implementation and leverages several aspects of\n * Postgres for the best performance, i.e.:\n *\n * - Deferred foreign key constraints are used to allow bulk inserting/updating\n * all entities without any topographic sorting/ordering issues.\n *\n * - Sequences are used to bulk-assign + then bulk-insert all entities, to again\n * avoid issues with ordering issues (cannot bulk insert A w/o knowing the id of B).\n *\n * - We use a pg-specific bulk update syntax.\n *\n * Query pipelining is enabled for clients created after driver construction unless the pool was\n * configured with `pipeline: false`.\n */\nexport class PostgresDriver implements Driver<pg.PoolClient> {\n readonly #idAssigner: IdAssigner;\n readonly #preloadPlugin: PreloadPlugin | undefined;\n readonly #onQuery: OnQuery;\n readonly lazyRows: boolean;\n /** Defined only when `lazyRows` is enabled; its presence is the capability signal the EM checks. */\n readonly executeFindRowData: Driver[\"executeFindRowData\"];\n #databaseBinaryParsers: Promise<void> | undefined = undefined;\n\n constructor(\n readonly pool: pg.Pool,\n opts?: PostgresDriverOpts,\n ) {\n // Set the option before pg creates clients, but preserve an explicit consumer opt-out.\n pool.options.pipeline ??= true;\n this.#idAssigner =\n opts?.idAssigner ??\n new SequenceIdAssigner(async (sql: string) => {\n this.#onQuery?.(sql);\n return (await pool.query(sql)).rows;\n });\n this.#preloadPlugin = opts?.preloadPlugin;\n this.#onQuery = opts?.onQuery;\n // Lazy rows require pg-protocol to emit lazy DataRows, which we patch in at runtime; if the\n // patch cannot be applied/verified (i.e. future pg-protocol internals changed), stay classic\n this.lazyRows = (opts?.lazyRows ?? false) && ensureLazyDataRows();\n if (opts?.lazyRows && !this.lazyRows) {\n console.warn(\"joist-orm: lazyRows was requested, but patching pg-protocol failed; using classic rows.\");\n }\n this.executeFindRowData = this.lazyRows\n ? (em, parsed, settings) => this.#executeFindRowData(em, parsed, settings)\n : undefined;\n setupLatestPgTypes(getRuntimeConfig().temporal);\n }\n\n async executeFind(\n em: EntityManager,\n parsed: ParsedFindQuery,\n settings: { limit?: number; offset?: number },\n ): Promise<any[]> {\n const { sql, bindings } = buildRawQuery(parsed, { limit: em.entityLimit, ...settings });\n return this.executeQuery(em, sql, bindings);\n }\n\n async #executeFindRowData(\n em: EntityManager,\n parsed: ParsedFindQuery,\n settings: { limit?: number; offset?: number },\n ): Promise<RowData> {\n // Auto-register binary parsers for the db's dynamic-oid text-likes (native enums, citext)\n // once, before our first lazy query; on failure, reset so the next query retries\n await (this.#databaseBinaryParsers ??= registerDatabaseBinaryParsers(this.pool).catch((err) => {\n this.#databaseBinaryParsers = undefined;\n throw err;\n }));\n const { sql, bindings } = buildRawQuery(parsed, { limit: em.entityLimit, ...settings });\n const pgSql = toPgParams(sql);\n this.#onQuery?.(pgSql);\n // Centralize client checkout here (pg-pool rejects Submittables in pool.query), and decide\n // classic-vs-lazy *before* submitting anything, i.e. unsupported clients like pg-native fall\n // back to classic rows rather than failing mid-query\n const txnClient = em.txn as pg.PoolClient | undefined;\n const client = txnClient ?? (await this.pool.connect());\n try {\n if (!isRowDataCapableClient(client)) {\n warnUnsupportedClientOnce();\n return new PojoRowData((await (client as pg.PoolClient).query(pgSql, bindings as any[])).rows);\n }\n return await executeRowDataQuery(client, pgSql, bindings);\n } finally {\n if (!txnClient) client.release();\n }\n }\n\n async executeQuery(em: EntityManager, sql: string, bindings: readonly any[]): Promise<any[]> {\n const pgSql = toPgParams(sql);\n this.#onQuery?.(pgSql);\n const client = this.getMaybeInTxnClient(em);\n return client.query(pgSql, bindings as any[]).then((result) => result.rows);\n }\n\n async transaction<T>(em: EntityManager, fn: (txn: pg.PoolClient) => Promise<T>): Promise<T> {\n if (em.txn) {\n return fn(em.txn as pg.PoolClient);\n }\n const client = await this.pool.connect();\n let result: T;\n try {\n await driverBeforeBegin(em, client);\n this.#onQuery?.(\"BEGIN;\");\n await client.query(\"BEGIN\");\n em.txn = client;\n try {\n await driverAfterBegin(em, client);\n result = await fn(client);\n await driverBeforeCommit(em, client);\n this.#onQuery?.(\"COMMIT;\");\n await client.query(\"COMMIT\");\n } catch (e) {\n await client.query(\"ROLLBACK\");\n throw e;\n } finally {\n em.txn = undefined;\n }\n await driverAfterCommit(em, client);\n } finally {\n client.release();\n }\n return result;\n }\n\n async assignNewIds(_: EntityManager, todos: Record<string, Todo>): Promise<void> {\n return this.#idAssigner.assignNewIds(todos);\n }\n\n async flush(\n em: EntityManager,\n entityTodos: Record<string, Todo>,\n joinRows: Record<string, JoinRowTodo>,\n ): Promise<void> {\n const client = (em.txn ?? fail(\"Expected EntityManager.txn to be set\")) as pg.PoolClient;\n await this.#idAssigner.assignNewIds(entityTodos);\n const ops = generateOps(entityTodos);\n const onQuery = this.#onQuery;\n // Do INSERTs+UPDATEs first so that we avoid DELETE cascades invalidating oplocks\n // See https://github.com/joist-orm/joist-orm/issues/591\n await Promise.all([\n ...ops.inserts.map((op) => batchInsert(client, op, onQuery)),\n ...ops.updates.map((op) => batchUpdate(client, op, onQuery)),\n ...ops.deletes.map((op) => batchDelete(client, op, onQuery)),\n ...Object.entries(joinRows).flatMap(([joinTableName, todo]) => {\n return [\n m2mBatchInsert(client, joinTableName, todo, onQuery),\n m2mBatchDelete(client, joinTableName, todo, onQuery),\n ];\n }),\n ]);\n }\n\n get defaultPlugins() {\n return { preloadPlugin: this.#preloadPlugin };\n }\n\n private getMaybeInTxnClient(em: EntityManager): pg.PoolClient | pg.Pool {\n return (em.txn as pg.PoolClient) || this.pool;\n }\n}\n\nasync function batchInsert(client: pg.PoolClient, op: InsertOp, onQuery: OnQuery): Promise<unknown> {\n const { tableName, columns, columnValues } = op;\n const [cte, bindings] = buildUnnestCte(\"data\", columns, columnValues);\n const sql = cleanSql(`\n ${cte}\n INSERT INTO ${kq(tableName)} (${columns.map((c) => kq(c.columnName)).join(\", \")})\n SELECT * FROM data\n `);\n const pgSql = toPgParams(sql);\n onQuery?.(pgSql);\n return client.query(pgSql, bindings);\n}\n\nasync function batchUpdate(client: pg.PoolClient, op: UpdateOp, onQuery: OnQuery): Promise<void> {\n const { tableName, columns, columnValues, updatedAt } = op;\n\n const [cte, bindings] = buildUnnestCte(\"data\", columns, columnValues);\n\n // JS Dates only have millisecond-level precision, so we may have dropped/lost accuracy when\n // reading Postgres's microsecond-level `timestamptz` values; using `date_trunc` \"downgrades\"\n // the pg data to match what we have in the JS Date.\n //\n // ...but Temporal's types don't have this flaw.\n const truncateToMills = !getRuntimeConfig().temporal;\n const maybeUpdatedAt =\n updatedAt && truncateToMills\n ? ` AND date_trunc('milliseconds', ${kqDot(tableName, updatedAt)}) = data.__original_updated_at`\n : updatedAt\n ? ` AND ${kqDot(tableName, updatedAt)} = data.__original_updated_at`\n : \"\";\n\n const sql = `\n ${cte}\n UPDATE ${kq(tableName)}\n SET ${columns\n .filter((c) => c.columnName !== \"id\" && c.columnName !== \"__original_updated_at\")\n .map((c) => `${kq(c.columnName)} = data.${kq(c.columnName)}`)\n .join(\", \")}\n FROM data\n WHERE ${kq(tableName)}.id = data.id ${maybeUpdatedAt}\n RETURNING ${kq(tableName)}.id\n `;\n\n const pgSql = toPgParams(cleanSql(sql));\n onQuery?.(pgSql);\n const result = await client.query(pgSql, bindings);\n const results = result.rows;\n\n const ids = columnValues[0]; // assume id is the 1st column\n if (results.length !== ids.length) {\n const updated = new Set(results.map((r: any) => r.id));\n const missing = ids.filter((id) => !updated.has(id));\n throw new Error(`Oplock failure for ${tableName} rows ${missing.join(\", \")}`);\n }\n}\n\nasync function batchDelete(client: pg.PoolClient, op: DeleteOp, onQuery: OnQuery): Promise<void> {\n const { tableName, ids } = op;\n const pgSql = toPgParams(`DELETE FROM ${kq(tableName)} WHERE id = ANY(?)`);\n onQuery?.(pgSql);\n await client.query(pgSql, [ids]);\n}\n\n/** Creates a CTE named `tableName` that bulk-injects the `columnValues` into a SQL query. */\nfunction buildUnnestCte(tableName: string, columns: OpColumn[], columnValues: any[][]): [string, any[][]] {\n ensureRectangularArraySizes(columns, columnValues);\n const selects = columns.map((c) => {\n if (c.dbType.endsWith(\"[]\")) {\n if (c.isNullableArray) {\n return `unnest_arrays(?::${c.dbType}[], true) as ${kq(c.columnName)}`;\n } else {\n return `unnest_arrays(?::${c.dbType}[]) as ${kq(c.columnName)}`;\n }\n } else {\n return `unnest(?::${c.dbType}[]) as ${kq(c.columnName)}`;\n }\n });\n const sql = `WITH ${tableName} AS (SELECT ${selects.join(\", \")})`;\n return [sql, columnValues];\n}\n\nasync function m2mBatchInsert(client: pg.PoolClient, joinTableName: string, todo: JoinRowTodo, onQuery: OnQuery) {\n const { m2m, newRows } = todo;\n if (newRows.length === 0) return;\n const col1Values = newRows.map((row) => todo.dbValue(row, m2m.columnName));\n const col2Values = newRows.map((row) => todo.dbValue(row, m2m.otherColumnName));\n if (m2m.hasJoinTableId) {\n const sql = cleanSql(`\n WITH data AS (SELECT unnest(?::int[]) as ${kq(m2m.columnName)}, unnest(?::int[]) as ${kq(m2m.otherColumnName)})\n INSERT INTO ${kq(joinTableName)} (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)})\n SELECT * FROM data\n ON CONFLICT (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) DO UPDATE SET id = ${kq(joinTableName)}.id\n RETURNING id;\n `);\n const pgSql = toPgParams(sql);\n onQuery?.(pgSql);\n const { rows } = await client.query(pgSql, [col1Values, col2Values]);\n for (let i = 0; i < rows.length; i++) {\n newRows[i].id = rows[i].id;\n newRows[i].op = JoinRowOperation.Flushed;\n newRows[i].persisted = true;\n }\n } else {\n // Id-less join tables have no surrogate id to return; the FK pair is the PK, so just\n // insert and let any duplicate be a no-op.\n const sql = cleanSql(`\n WITH data AS (SELECT unnest(?::int[]) as ${kq(m2m.columnName)}, unnest(?::int[]) as ${kq(m2m.otherColumnName)})\n INSERT INTO ${kq(joinTableName)} (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)})\n SELECT * FROM data\n ON CONFLICT (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) DO NOTHING;\n `);\n const pgSql = toPgParams(sql);\n onQuery?.(pgSql);\n await client.query(pgSql, [col1Values, col2Values]);\n for (const row of newRows) {\n row.op = JoinRowOperation.Flushed;\n row.persisted = true;\n }\n }\n}\n\nasync function m2mBatchDelete(client: pg.PoolClient, joinTableName: string, todo: JoinRowTodo, onQuery: OnQuery) {\n const { m2m, deletedRows } = todo;\n if (deletedRows.length === 0) return;\n // Rows with a surrogate id are deleted by id; rows without one — id-less tables, or `remove`s\n // done against an unloaded ManyToManyCollection — are deleted by their (col1, col2) composite.\n const [haveIds, noIds] = partition(deletedRows, (r) => {\n // We used to use `id !== -1` as a marker for \"row is/is-not persisted in the db\" -- but when adding support\n // for id-column-less m2m tables, we couldn't use `id === -1` as this marker anymore, so now rely on a dedicated\n // `persisted` key instead.\n //\n // So, ideally, this `id !== -1` could go away _except_ that our internal verisoning plugin leverages this \"m2m rows\n // with an id=-1 delete by their FK values\" to handle our versioned m2m tables. And for now it's easier to\n // keep/restore this `id !== -1` than refactor that.\n return r.id !== undefined && r.id !== -1;\n });\n if (haveIds.length > 0) {\n const pgSql = toPgParams(`DELETE FROM ${kq(joinTableName)} WHERE id = ANY(?)`);\n onQuery?.(pgSql);\n await client.query(pgSql, [haveIds.map((r) => r.id!)]);\n }\n if (noIds.length > 0) {\n const data = noIds\n // Watch for m2m rows that got added-then-removed to entities that were themselves added-then-removed,\n // as the deTagId will be undefined for those, as we're skipping adding them to the database.\n .filter((row) => !todo.isNew(row, m2m.columnName) && !todo.isNew(row, m2m.otherColumnName))\n .map((row) => [todo.dbValue(row, m2m.columnName), todo.dbValue(row, m2m.otherColumnName)] as any);\n if (data.length > 0) {\n const pgSql = toPgParams(`\n DELETE FROM ${kq(joinTableName)}\n WHERE (${kq(m2m.columnName)}, ${kq(m2m.otherColumnName)}) IN (\n SELECT (data->>0)::int, (data->>1)::int FROM jsonb_array_elements(?) data\n )`);\n onQuery?.(pgSql);\n await client.query(pgSql, [JSON.stringify(data)]);\n }\n }\n deletedRows.forEach((row) => {\n row.id = undefined;\n row.persisted = false;\n row.op = JoinRowOperation.Flushed;\n });\n}\n\n/**\n * Configures the `pg` driver with the best type parsers.\n *\n * In particular, pg's default `TIMESTAMPTZ` parser is very inefficient, and even just\n * pulling in the latest `pg-types` and installing the fixed parser makes a noticable\n * difference.\n */\nexport function setupLatestPgTypes(temporal: RuntimeConfig[\"temporal\"]): void {\n if (temporal) {\n // Don't eagerly parse the strings, instead defer to the serde logic. This only governs\n // classic *text* results (knex, pool.query, non-lazy drivers); binary lazy cells construct\n // Temporal values directly, with no strings involved (see registerTemporalBinaryParsers).\n const noop = (s: string) => s;\n const noopArray = (s: string) => array.parse(s, noop);\n\n const { TIMESTAMP, TIMESTAMPTZ, DATE } = pg.types.builtins;\n pg.types.setTypeParser(DATE, noop);\n pg.types.setTypeParser(TIMESTAMP, noop);\n pg.types.setTypeParser(TIMESTAMPTZ, noop);\n\n // Use `as number` b/c the typings of shadowed pg-types from `pg` and `pg-types` top-level don't line up\n pg.types.setTypeParser(1182 as number, noopArray); // date[]\n pg.types.setTypeParser(1115 as number, noopArray); // timestamp[]\n pg.types.setTypeParser(1185 as number, noopArray); // timestamptz[]\n\n registerTemporalBinaryParsers();\n } else {\n pg.types.setTypeParser(pg.types.builtins.TIMESTAMPTZ, getTypeParser(builtins.TIMESTAMPTZ));\n // The binary registry's date/timestamp/timestamptz builtins already decode to Dates\n }\n}\n\nconst questionMarks = /(?<!@)\\?/g;\n\n/**\n * Converts our knex-style `?` placeholders to pg-native `$1, $2, ...` numbered parameters.\n *\n * We skipping postgres operators like `@?`. */\nfunction toPgParams(sql: string): string {\n let i = 0;\n return sql.replace(questionMarks, () => `$${++i}`);\n}\n\nlet warnedUnsupportedClient = false;\n\n/** Warns once when lazyRows degrades to classic rows for an unsupported client, i.e. pg-native. */\nfunction warnUnsupportedClientOnce(): void {\n if (warnedUnsupportedClient) return;\n warnedUnsupportedClient = true;\n console.warn(\"joist-orm: lazyRows is enabled, but this client does not support it; using classic rows.\");\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "joist-orm",
3
- "version": "2.3.0-next.50",
3
+ "version": "2.3.0-next.52",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "repository": {
@@ -201,14 +201,14 @@
201
201
  "build"
202
202
  ],
203
203
  "dependencies": {
204
- "joist-codegen": "2.3.0-next.50",
205
- "joist-core": "2.3.0-next.50",
206
- "joist-graphql-codegen": "2.3.0-next.50",
207
- "joist-graphql-resolver-utils": "2.3.0-next.50",
208
- "joist-knex": "2.3.0-next.50",
209
- "joist-migration-utils": "2.3.0-next.50",
210
- "joist-test-utils": "2.3.0-next.50",
211
- "joist-utils": "2.3.0-next.50"
204
+ "joist-codegen": "2.3.0-next.52",
205
+ "joist-core": "2.3.0-next.52",
206
+ "joist-graphql-codegen": "2.3.0-next.52",
207
+ "joist-graphql-resolver-utils": "2.3.0-next.52",
208
+ "joist-knex": "2.3.0-next.52",
209
+ "joist-migration-utils": "2.3.0-next.52",
210
+ "joist-test-utils": "2.3.0-next.52",
211
+ "joist-utils": "2.3.0-next.52"
212
212
  },
213
213
  "peerDependencies": {
214
214
  "knex": "^3.1.0",