{"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"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","sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\nimport { Readable, Transform } from 'node:stream';\n\nimport debugfn from 'debug';\nimport { difference, sampleSize } from 'es-toolkit';\nimport multipipe from 'multipipe';\nimport pg, { DatabaseError, type QueryResult, escapeLiteral } from 'pg';\nimport Cursor from 'pg-cursor';\nimport { z } from 'zod';\n\nexport type QueryParams = Record<string, any> | any[];\n\nexport interface CursorIterator<T> {\n  iterate: (batchSize: number) => AsyncGenerator<T[]>;\n  stream: (batchSize: number) => NodeJS.ReadWriteStream;\n}\n\nexport interface PostgresPoolConfig extends pg.PoolConfig {\n  errorOnUnusedParameters?: boolean;\n}\n\nconst debug = debugfn('@prairielearn/postgres');\nconst lastQueryMap = new WeakMap<pg.PoolClient, string>();\nconst searchSchemaMap = new WeakMap<pg.PoolClient, string>();\n\nfunction addDataToError(err: Error, data: Record<string, any>): Error {\n  (err as any).data = {\n    ...(err as any).data,\n    ...data,\n  };\n  return err;\n}\n\n/** @knipignore */\nexport class PostgresError extends Error {\n  public data: Record<string, any>;\n\n  constructor(message: string, data: Record<string, any>) {\n    super(message);\n    this.data = data;\n    this.name = 'PostgresError';\n  }\n}\n\n/**\n * Formats a string for debugging.\n */\nfunction debugString(s: string): string {\n  if (typeof s !== 'string') return 'NOT A STRING';\n  s = s.replaceAll('\\n', '\\\\n');\n  if (s.length > 78) s = s.slice(0, 75) + '...';\n  s = '\"' + s + '\"';\n  return s;\n}\n\n/**\n * Formats a set of params for debugging.\n */\nfunction debugParams(params: QueryParams): string {\n  let s;\n  try {\n    s = JSON.stringify(params);\n  } catch {\n    s = 'CANNOT JSON STRINGIFY';\n  }\n  return debugString(s);\n}\n\n/**\n * Given an SQL string and params, creates an array of params and an SQL string\n * with any named dollar-sign placeholders replaced with parameters.\n */\nfunction paramsToArray(\n  sql: string,\n  params: QueryParams,\n  errorOnUnusedParameters: boolean,\n): { processedSql: string; paramsArray: any } {\n  if (typeof sql !== 'string') throw new Error('SQL must be a string');\n  if (Array.isArray(params)) {\n    return {\n      processedSql: sql,\n      paramsArray: params,\n    };\n  }\n  if (params == null || typeof params !== 'object') {\n    throw new Error('params must be array or object');\n  }\n\n  const re = /\\$([-_a-zA-Z0-9]+)/;\n  let result;\n  let processedSql = '';\n  let remainingSql = sql;\n  let nParams = 0;\n  const map: Record<string, string> = {};\n  let paramsArray: any[] = [];\n  while ((result = re.exec(remainingSql)) !== null) {\n    const v = result[1];\n    if (!(v in map)) {\n      if (!(v in params)) throw new Error(`Missing parameter: ${v}`);\n      if (Array.isArray(params[v])) {\n        map[v] = 'ARRAY[' + params[v].map((_, n) => '$' + (n + nParams + 1)).join(',') + ']';\n        nParams += params[v].length;\n        paramsArray = paramsArray.concat(params[v]);\n      } else {\n        nParams++;\n        map[v] = '$' + nParams;\n        paramsArray.push(params[v]);\n      }\n    }\n    processedSql += remainingSql.slice(0, result.index) + map[v];\n    remainingSql = remainingSql.slice(result.index + result[0].length);\n  }\n  processedSql += remainingSql;\n  remainingSql = '';\n  if (errorOnUnusedParameters) {\n    const unusedKeys = difference(Object.keys(params), Object.keys(map));\n    if (unusedKeys.length > 0) {\n      throw new Error(`Unused parameters in SQL query: ${JSON.stringify(unusedKeys)}`);\n    }\n  }\n  return { processedSql, paramsArray };\n}\n\n/**\n * Escapes the given identifier for use in an SQL query. Useful for preventing\n * SQL injection.\n */\nexport function escapeIdentifier(identifier: string): string {\n  return pg.Client.prototype.escapeIdentifier(identifier);\n}\n\nfunction assertSingleColumn(result: pg.QueryResult, context: Record<string, any>): string {\n  if (result.fields.length !== 1) {\n    throw new PostgresError(\n      `Expected exactly one column, but found ${result.fields.length}`,\n      context,\n    );\n  }\n  return result.fields[0].name;\n}\n\nfunction enhanceError(err: Error, sql: string, params: QueryParams): Error {\n  // Copy the error so we don't end up with a circular reference in the\n  // final error.\n  const sqlError = {\n    ...err,\n    // `message` is a non-enumerable property, so we need to copy it manually to\n    // the error object.\n    message: err.message,\n  };\n\n  const errorHasPosition = err instanceof DatabaseError && err.position != null;\n\n  return addDataToError(err, {\n    sqlError,\n    // If the error has a `position` field, we need to use the processed source\n    // (where e.g. `$foobar` has been replaced with `$1`) so that the position\n    // is accurate.\n    sql: errorHasPosition ? paramsToArray(sql, params, false).processedSql : sql,\n    sqlParams: params,\n  });\n}\n\nexport class PostgresPool {\n  /** The pool from which clients will be acquired. */\n  private pool: pg.Pool | null = null;\n  /**\n   * We use this to propagate the client associated with the current transaction\n   * to any nested queries. In the past, we had some nasty bugs associated with\n   * the fact that we tried to acquire new clients inside of transactions, which\n   * ultimately lead to a deadlock.\n   */\n  private alsClient = new AsyncLocalStorage<pg.PoolClient>();\n  private searchSchema: string | null = null;\n  /** Tracks the total number of queries executed by this pool. */\n  private _queryCount = 0;\n  private errorOnUnusedParameters = false;\n\n  /**\n   * Creates a new connection pool and attempts to connect to the database.\n   */\n  async initAsync(\n    pgConfig: PostgresPoolConfig,\n    idleErrorHandler: (error: Error, client: pg.PoolClient) => void,\n  ): Promise<void> {\n    if (this.pool != null) {\n      throw new Error('Postgres pool already initialized');\n    }\n    this.errorOnUnusedParameters = pgConfig.errorOnUnusedParameters ?? false;\n    this.pool = new pg.Pool(pgConfig);\n    this.pool.on('error', function (err, client) {\n      const lastQuery = lastQueryMap.get(client);\n      idleErrorHandler(addDataToError(err, { lastQuery }), client);\n    });\n    this.pool.on('connect', (client) => {\n      client.on('error', (err) => {\n        const lastQuery = lastQueryMap.get(client);\n        idleErrorHandler(addDataToError(err, { lastQuery }), client);\n      });\n    });\n    this.pool.on('remove', (client) => {\n      // This shouldn't be necessary, as `pg` currently allows clients to be\n      // garbage collected after they're removed. However, if `pg` someday\n      // starts reusing client objects across difference connections, this\n      // will ensure that we re-set the search path when the client reconnects.\n      searchSchemaMap.delete(client);\n    });\n\n    // Attempt to connect to the database so that we can fail quickly if\n    // something isn't configured correctly.\n    let retryCount = 0;\n    const retryTimeouts = [500, 1000, 2000, 5000, 10000];\n    while (retryCount <= retryTimeouts.length) {\n      try {\n        const client = await this.pool.connect();\n        client.release();\n        return;\n      } catch (err: any) {\n        if (retryCount === retryTimeouts.length) {\n          throw new Error(`Could not connect to Postgres after ${retryTimeouts.length} attempts`, {\n            cause: err,\n          });\n        }\n\n        const timeout = retryTimeouts[retryCount];\n        retryCount++;\n        await new Promise((resolve) => setTimeout(resolve, timeout));\n      }\n    }\n  }\n\n  /**\n   * Closes the connection pool.\n   */\n  async closeAsync(): Promise<void> {\n    if (!this.pool) return;\n    await this.pool.end();\n    this.pool = null;\n  }\n\n  /**\n   * Gets a new client from the connection pool. The caller MUST call `release()` to\n   * release the client, whether or not errors occurred while using\n   * `client`. The client can call `done(truthy_value)` to force\n   * destruction of the client, but this should not be used except in\n   * unusual circumstances.\n   */\n  async getClientAsync(): Promise<pg.PoolClient> {\n    if (!this.pool) {\n      throw new Error('Connection pool is not open');\n    }\n\n    // If we're inside a transaction, we'll reuse the same client to avoid a\n    // potential deadlock.\n    const client = this.alsClient.getStore() ?? (await this.pool.connect());\n\n    // If we're configured to use a particular schema, we'll store whether or\n    // not the search path has already been configured for this particular\n    // client. If we acquire a client and it's already had its search path\n    // set, we can avoid setting it again since the search path will persist\n    // for the life of the client.\n    //\n    // We do this check for each call to `getClient` instead of on\n    // `pool.connect` so that we don't have to be really careful about\n    // destroying old clients that were created before `setSearchSchema` was\n    // called. Instead, we'll just check if the search path matches the\n    // currently-desired schema, and if it's a mismatch (or doesn't exist\n    // at all), we re-set it for the current client.\n    //\n    // Note that this accidentally supports changing the search_path on the fly,\n    // although that's not something we currently do (or would be likely to do).\n    // It does NOT support clearing the existing search schema - e.g.,\n    // `setSearchSchema(null)` would not work as you expect. This is fine, as\n    // that's not something we ever do in practice.\n    const clientSearchSchema = searchSchemaMap.get(client);\n    if (this.searchSchema != null && clientSearchSchema !== this.searchSchema) {\n      const setSearchPathSql = `SET search_path TO ${escapeIdentifier(this.searchSchema)},public`;\n      try {\n        await this.queryWithClientAsync(client, setSearchPathSql, {});\n      } catch (err) {\n        client.release();\n        throw err;\n      }\n      searchSchemaMap.set(client, this.searchSchema);\n    }\n\n    return client;\n  }\n\n  /**\n   * Performs a query with the given client.\n   */\n  async queryWithClientAsync(\n    client: pg.PoolClient,\n    sql: string,\n    params: QueryParams,\n  ): Promise<pg.QueryResult> {\n    this._queryCount += 1;\n    debug('queryWithClient()', 'sql:', debugString(sql));\n    debug('queryWithClient()', 'params:', debugParams(params));\n    const { processedSql, paramsArray } = paramsToArray(sql, params, this.errorOnUnusedParameters);\n    try {\n      lastQueryMap.set(client, processedSql);\n      const result = await client.query(processedSql, paramsArray);\n      debug('queryWithClient() success', 'rowCount:', result.rowCount);\n      return result;\n    } catch (err: any) {\n      throw enhanceError(err, sql, params);\n    }\n  }\n\n  /**\n   * Performs a query with the given client. Errors if the query returns more\n   * than one row.\n   */\n  async queryWithClientOneRowAsync(\n    client: pg.PoolClient,\n    sql: string,\n    params: QueryParams,\n  ): Promise<pg.QueryResult> {\n    debug('queryWithClientOneRow()', 'sql:', debugString(sql));\n    debug('queryWithClientOneRow()', 'params:', debugParams(params));\n    const result = await this.queryWithClientAsync(client, sql, params);\n    if (result.rowCount !== 1) {\n      throw new PostgresError(`Incorrect rowCount: ${result.rowCount}`, {\n        sql,\n        sqlParams: params,\n        result,\n      });\n    }\n    debug('queryWithClientOneRow() success', 'rowCount:', result.rowCount);\n    return result;\n  }\n\n  /**\n   * Performs a query with the given client. Errors if the query returns more\n   * than one row.\n   */\n  async queryWithClientZeroOrOneRowAsync(\n    client: pg.PoolClient,\n    sql: string,\n    params: QueryParams,\n  ): Promise<QueryResult> {\n    debug('queryWithClientZeroOrOneRow()', 'sql:', debugString(sql));\n    debug('queryWithClientZeroOrOneRow()', 'params:', debugParams(params));\n    const result = await this.queryWithClientAsync(client, sql, params);\n    if (result.rowCount == null || result.rowCount > 1) {\n      throw new PostgresError(`Incorrect rowCount: ${result.rowCount}`, {\n        sql,\n        sqlParams: params,\n        result,\n      });\n    }\n    debug('queryWithClientZeroOrOneRow() success', 'rowCount:', result.rowCount);\n    return result;\n  }\n\n  /**\n   * Rolls back the current transaction for the given client.\n   */\n  async rollbackWithClientAsync(client: pg.PoolClient) {\n    debug('rollbackWithClient()');\n    // From https://node-postgres.com/features/transactions\n    try {\n      await client.query('ROLLBACK');\n      // Only release the client if we weren't already inside a transaction.\n      if (this.alsClient.getStore() === undefined) {\n        client.release();\n      }\n    } catch (err: any) {\n      // If there was a problem rolling back the query, something is\n      // seriously messed up. Return the error to the release() function to\n      // close & remove this client from the pool. If you leave a client in\n      // the pool with an unaborted transaction, weird and hard to diagnose\n      // problems might happen.\n      client.release(err);\n    }\n  }\n\n  /**\n   * Begins a new transaction.\n   */\n  async beginTransactionAsync(): Promise<pg.PoolClient> {\n    debug('beginTransaction()');\n    const client = await this.getClientAsync();\n    try {\n      await this.queryWithClientAsync(client, 'START TRANSACTION;', {});\n      return client;\n    } catch (err) {\n      await this.rollbackWithClientAsync(client);\n      throw err;\n    }\n  }\n\n  /**\n   * Commits the transaction if err is null, otherwise rollbacks the transaction.\n   * Also releases the client.\n   */\n  async endTransactionAsync(client: pg.PoolClient, err: Error | null | undefined) {\n    debug('endTransaction()');\n    if (err) {\n      try {\n        await this.rollbackWithClientAsync(client);\n      } catch (rollbackErr: any) {\n        throw addDataToError(rollbackErr, { prevErr: err, rollback: 'fail' });\n      }\n\n      // Even though we successfully rolled back the transaction, there was\n      // still an error in the first place that necessitated a rollback. Re-throw\n      // that error here so that everything downstream of here will know about it.\n      throw addDataToError(err, { rollback: 'success' });\n    } else {\n      try {\n        await this.queryWithClientAsync(client, 'COMMIT', {});\n      } finally {\n        // Only release the client if we aren't nested inside another transaction.\n        if (this.alsClient.getStore() === undefined) {\n          client.release();\n        }\n      }\n    }\n  }\n\n  /**\n   * Runs the specified function inside of a transaction. The function will\n   * receive a database client as an argument, but it can also make queries\n   * as usual, and the correct client will be used automatically.\n   *\n   * The transaction will be rolled back if the function throws an error, and\n   * will be committed otherwise.\n   */\n  async runInTransactionAsync<T>(fn: (client: pg.PoolClient) => Promise<T>): Promise<T> {\n    // Check if we're already inside a transaction. If so, we won't start another one,\n    // as Postgres doesn't support nested transactions.\n    const client = this.alsClient.getStore();\n    const isNestedTransaction = client !== undefined;\n    const transactionClient = client ?? (await this.beginTransactionAsync());\n\n    let result: T;\n    try {\n      result = await this.alsClient.run(transactionClient, () => fn(transactionClient));\n    } catch (err: any) {\n      if (!isNestedTransaction) {\n        // If we're inside another transaction, we assume that the root transaction\n        // will catch this error and roll back the transaction.\n        await this.endTransactionAsync(transactionClient, err);\n      }\n      throw err;\n    }\n\n    if (!isNestedTransaction) {\n      // If we're inside another transaction; don't commit it prematurely. Allow\n      // the root transaction to commit it instead.\n      //\n      // Note that we don't invoke `endTransactionAsync` inside the `try` block\n      // because we don't want an error thrown by it to trigger *another* call\n      // to `endTransactionAsync` in the `catch` block.\n      await this.endTransactionAsync(transactionClient, null);\n    }\n\n    return result;\n  }\n\n  /**\n   * Executes a query with the specified parameters.\n   *\n   * @deprecated Use {@link execute} instead.\n   *\n   * Using the return value of this function directly is not recommended. Instead, use\n   * {@link queryRows}, {@link queryRow}, or {@link queryOptionalRow}.\n   */\n  async queryAsync(sql: string, params: QueryParams): Promise<QueryResult> {\n    debug('query()', 'sql:', debugString(sql));\n    debug('query()', 'params:', debugParams(params));\n    const client = await this.getClientAsync();\n    try {\n      return await this.queryWithClientAsync(client, sql, params);\n    } finally {\n      // Only release if we aren't nested in a transaction.\n      if (this.alsClient.getStore() === undefined) {\n        client.release();\n      }\n    }\n  }\n\n  /**\n   * Executes a query with the specified parameters. Errors if the query does\n   * not return exactly one row.\n   *\n   * @deprecated Use {@link executeRow} or {@link queryRow} instead.\n   */\n  async queryOneRowAsync(sql: string, params: QueryParams): Promise<pg.QueryResult> {\n    debug('queryOneRow()', 'sql:', debugString(sql));\n    debug('queryOneRow()', 'params:', debugParams(params));\n    const result = await this.queryAsync(sql, params);\n    if (result.rowCount !== 1) {\n      throw new PostgresError(`Incorrect rowCount: ${result.rowCount}`, {\n        sql,\n        sqlParams: params,\n      });\n    }\n    debug('queryOneRow() success', 'rowCount:', result.rowCount);\n    return result;\n  }\n\n  /**\n   * Executes a query with the specified parameters. Errors if the query\n   * returns more than one row.\n   */\n  async queryZeroOrOneRowAsync(sql: string, params: QueryParams): Promise<pg.QueryResult> {\n    debug('queryZeroOrOneRow()', 'sql:', debugString(sql));\n    debug('queryZeroOrOneRow()', 'params:', debugParams(params));\n    const result = await this.queryAsync(sql, params);\n    if (result.rowCount == null || result.rowCount > 1) {\n      throw new PostgresError(`Incorrect rowCount: ${result.rowCount}`, {\n        sql,\n        sqlParams: params,\n      });\n    }\n    debug('queryZeroOrOneRow() success', 'rowCount:', result.rowCount);\n    return result;\n  }\n\n  /**\n   * Calls the given sproc with the specified parameters.\n   */\n  async callAsync(functionName: string, params: any[]): Promise<pg.QueryResult> {\n    debug('call()', 'function:', functionName);\n    debug('call()', 'params:', debugParams(params));\n    const placeholders = params.map((_, v) => '$' + (v + 1)).join(',');\n    const sql = `SELECT * FROM ${escapeIdentifier(functionName)}(${placeholders});`;\n    const result = await this.queryAsync(sql, params);\n    debug('call() success', 'rowCount:', result.rowCount);\n    return result;\n  }\n\n  /**\n   * Calls the given sproc with the specified parameters. Errors if the\n   * sproc does not return exactly one row.\n   */\n  async callOneRowAsync(functionName: string, params: any[]): Promise<pg.QueryResult> {\n    debug('callOneRow()', 'function:', functionName);\n    debug('callOneRow()', 'params:', debugParams(params));\n    const result = await this.callAsync(functionName, params);\n    if (result.rowCount !== 1) {\n      throw new PostgresError('Incorrect rowCount: ' + result.rowCount, {\n        functionName,\n        sqlParams: params,\n      });\n    }\n    debug('callOneRow() success', 'rowCount:', result.rowCount);\n    return result;\n  }\n\n  /**\n   * Calls the given sproc with the specified parameters. Errors if the\n   * sproc returns more than one row.\n   */\n  async callZeroOrOneRowAsync(functionName: string, params: any[]): Promise<pg.QueryResult> {\n    debug('callZeroOrOneRow()', 'function:', functionName);\n    debug('callZeroOrOneRow()', 'params:', debugParams(params));\n    const result = await this.callAsync(functionName, params);\n    if (result.rowCount == null || result.rowCount > 1) {\n      throw new PostgresError('Incorrect rowCount: ' + result.rowCount, {\n        functionName,\n        sqlParams: params,\n      });\n    }\n    debug('callZeroOrOneRow() success', 'rowCount:', result.rowCount);\n    return result;\n  }\n\n  /**\n   * Calls a sproc with the specified parameters using a specific client.\n   */\n  async callWithClientAsync(\n    client: pg.PoolClient,\n    functionName: string,\n    params: any[],\n  ): Promise<pg.QueryResult> {\n    debug('callWithClient()', 'function:', functionName);\n    debug('callWithClient()', 'params:', debugParams(params));\n    const placeholders = params.map((_, v) => '$' + (v + 1)).join(',');\n    const sql = `SELECT * FROM ${escapeIdentifier(functionName)}(${placeholders})`;\n    const result = await this.queryWithClientAsync(client, sql, params);\n    debug('callWithClient() success', 'rowCount:', result.rowCount);\n    return result;\n  }\n\n  /**\n   * Calls a sproc with the specified parameters using a specific client.\n   * Errors if the sproc does not return exactly one row.\n   */\n  async callWithClientOneRowAsync(\n    client: pg.PoolClient,\n    functionName: string,\n    params: any[],\n  ): Promise<pg.QueryResult> {\n    debug('callWithClientOneRow()', 'function:', functionName);\n    debug('callWithClientOneRow()', 'params:', debugParams(params));\n    const result = await this.callWithClientAsync(client, functionName, params);\n    if (result.rowCount !== 1) {\n      throw new PostgresError('Incorrect rowCount: ' + result.rowCount, {\n        functionName,\n        sqlParams: params,\n      });\n    }\n    debug('callWithClientOneRow() success', 'rowCount:', result.rowCount);\n    return result;\n  }\n\n  /**\n   * Calls a function with the specified parameters using a specific client.\n   * Errors if the sproc returns more than one row.\n   */\n  async callWithClientZeroOrOneRowAsync(\n    client: pg.PoolClient,\n    functionName: string,\n    params: any[],\n  ): Promise<pg.QueryResult> {\n    debug('callWithClientZeroOrOneRow()', 'function:', functionName);\n    debug('callWithClientZeroOrOneRow()', 'params:', debugParams(params));\n    const result = await this.callWithClientAsync(client, functionName, params);\n    if (result.rowCount == null || result.rowCount > 1) {\n      throw new PostgresError('Incorrect rowCount: ' + result.rowCount, {\n        functionName,\n        sqlParams: params,\n      });\n    }\n    debug('callWithClientZeroOrOneRow() success', 'rowCount:', result.rowCount);\n    return result;\n  }\n\n  async queryRows<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>[]>;\n  async queryRows<Model extends z.ZodType>(\n    sql: string,\n    params: QueryParams,\n    model: Model,\n  ): Promise<z.infer<Model>[]>;\n  /**\n   * Executes a query with the specified parameters. Returns an array of rows\n   * that conform to the given Zod schema.\n   */\n  async queryRows<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: QueryParams | Model,\n    maybeModel?: Model,\n  ) {\n    const params = maybeModel === undefined ? {} : (paramsOrSchema as QueryParams);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    const results = await this.queryAsync(sql, params);\n    return z.array(model).parse(results.rows);\n  }\n\n  async queryRow<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>>;\n  async queryRow<Model extends z.ZodType>(\n    sql: string,\n    params: QueryParams,\n    model: Model,\n  ): Promise<z.infer<Model>>;\n  /**\n   * Executes a query with the specified parameters. Returns exactly one row that conforms to the given Zod schema.\n   */\n  async queryRow<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: QueryParams | Model,\n    maybeModel?: Model,\n  ) {\n    const params = maybeModel === undefined ? {} : (paramsOrSchema as QueryParams);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    const results = await this.queryOneRowAsync(sql, params);\n    return model.parse(results.rows[0]);\n  }\n\n  async queryOptionalRow<Model extends z.ZodType>(\n    sql: string,\n    model: Model,\n  ): Promise<z.infer<Model> | null>;\n  async queryOptionalRow<Model extends z.ZodType>(\n    sql: string,\n    params: QueryParams,\n    model: Model,\n  ): Promise<z.infer<Model> | null>;\n  /**\n   * Executes a query with the specified parameters. Returns either null or a\n   * single row that conforms to the given Zod schema, and errors otherwise.\n   */\n  async queryOptionalRow<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: QueryParams | Model,\n    maybeModel?: Model,\n  ) {\n    const params = maybeModel === undefined ? {} : (paramsOrSchema as QueryParams);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    const results = await this.queryZeroOrOneRowAsync(sql, params);\n    if (results.rows.length === 0) {\n      return null;\n    }\n    return model.parse(results.rows[0]);\n  }\n\n  async callRows<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>[]>;\n  async callRows<Model extends z.ZodType>(\n    sql: string,\n    params: any[],\n    model: Model,\n  ): Promise<z.infer<Model>[]>;\n  /**\n   * Calls the given sproc with the specified parameters.\n   * Errors if the sproc does not return anything.\n   */\n  async callRows<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: any[] | Model,\n    maybeModel?: Model,\n  ) {\n    const params = maybeModel === undefined ? [] : (paramsOrSchema as any[]);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    const results = await this.callAsync(sql, params);\n    return z.array(model).parse(results.rows);\n  }\n\n  async callRow<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>>;\n  async callRow<Model extends z.ZodType>(\n    sql: string,\n    params: any[],\n    model: Model,\n  ): Promise<z.infer<Model>>;\n  /**\n   * Calls the given sproc with the specified parameters.\n   * Returns exactly one row from the sproc that conforms to the given Zod schema.\n   */\n  async callRow<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: any[] | Model,\n    maybeModel?: Model,\n  ) {\n    const params = maybeModel === undefined ? [] : (paramsOrSchema as any[]);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    const results = await this.callOneRowAsync(sql, params);\n    return model.parse(results.rows[0]);\n  }\n\n  async callOptionalRow<Model extends z.ZodType>(\n    sql: string,\n    model: Model,\n  ): Promise<z.infer<Model> | null>;\n  async callOptionalRow<Model extends z.ZodType>(\n    sql: string,\n    params: any[],\n    model: Model,\n  ): Promise<z.infer<Model> | null>;\n  /**\n   * Calls the given sproc with the specified parameters. Returns either null\n   * or a single row that conforms to the given Zod schema.\n   */\n  async callOptionalRow<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: any[] | Model,\n    maybeModel?: Model,\n  ) {\n    const params = maybeModel === undefined ? [] : (paramsOrSchema as any[]);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    const results = await this.callZeroOrOneRowAsync(sql, params);\n    if (results.rows.length === 0) {\n      return null;\n    }\n    return model.parse(results.rows[0]);\n  }\n\n  async queryScalars<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>[]>;\n  async queryScalars<Model extends z.ZodType>(\n    sql: string,\n    params: QueryParams,\n    model: Model,\n  ): Promise<z.infer<Model>[]>;\n  /**\n   * Executes a query and returns all values from a single column, validated\n   * against the given Zod schema. Errors if the query returns more than one column.\n   */\n  async queryScalars<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: QueryParams | Model,\n    maybeModel?: Model,\n  ) {\n    const params = maybeModel === undefined ? {} : (paramsOrSchema as QueryParams);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    const results = await this.queryAsync(sql, params);\n    const columnName = assertSingleColumn(results, { sql, sqlParams: params });\n    return z.array(model).parse(results.rows.map((row) => row[columnName]));\n  }\n\n  async queryScalar<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>>;\n  async queryScalar<Model extends z.ZodType>(\n    sql: string,\n    params: QueryParams,\n    model: Model,\n  ): Promise<z.infer<Model>>;\n  /**\n   * Executes a query and returns a single value from a single column, validated\n   * against the given Zod schema. Errors if the query does not return exactly\n   * one row or returns more than one column.\n   */\n  async queryScalar<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: QueryParams | Model,\n    maybeModel?: Model,\n  ) {\n    const params = maybeModel === undefined ? {} : (paramsOrSchema as QueryParams);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    const results = await this.queryOneRowAsync(sql, params);\n    const columnName = assertSingleColumn(results, { sql, sqlParams: params });\n    return model.parse(results.rows[0][columnName]);\n  }\n\n  async queryOptionalScalar<Model extends z.ZodType>(\n    sql: string,\n    model: Model,\n  ): Promise<z.infer<Model> | null>;\n  async queryOptionalScalar<Model extends z.ZodType>(\n    sql: string,\n    params: QueryParams,\n    model: Model,\n  ): Promise<z.infer<Model> | null>;\n  /**\n   * Executes a query and returns a single value from a single column, or null\n   * if no rows are returned. Validated against the given Zod schema. Errors if\n   * the query returns more than one row or more than one column.\n   */\n  async queryOptionalScalar<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: QueryParams | Model,\n    maybeModel?: Model,\n  ) {\n    const params = maybeModel === undefined ? {} : (paramsOrSchema as QueryParams);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    const results = await this.queryZeroOrOneRowAsync(sql, params);\n    const columnName = assertSingleColumn(results, { sql, sqlParams: params });\n    if (results.rows.length === 0) {\n      return null;\n    }\n    return model.parse(results.rows[0][columnName]);\n  }\n\n  async callScalars<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>[]>;\n  async callScalars<Model extends z.ZodType>(\n    sql: string,\n    params: any[],\n    model: Model,\n  ): Promise<z.infer<Model>[]>;\n  /**\n   * Calls the given sproc and returns all values from a single column, validated\n   * against the given Zod schema. Errors if the sproc returns more than one column.\n   */\n  async callScalars<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: any[] | Model,\n    maybeModel?: Model,\n  ) {\n    const params = maybeModel === undefined ? [] : (paramsOrSchema as any[]);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    const results = await this.callAsync(sql, params);\n    const columnName = assertSingleColumn(results, { functionName: sql, sqlParams: params });\n    return z.array(model).parse(results.rows.map((row) => row[columnName]));\n  }\n\n  async callScalar<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>>;\n  async callScalar<Model extends z.ZodType>(\n    sql: string,\n    params: any[],\n    model: Model,\n  ): Promise<z.infer<Model>>;\n  /**\n   * Calls the given sproc and returns a single value from a single column, validated\n   * against the given Zod schema. Errors if the sproc does not return exactly\n   * one row or returns more than one column.\n   */\n  async callScalar<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: any[] | Model,\n    maybeModel?: Model,\n  ) {\n    const params = maybeModel === undefined ? [] : (paramsOrSchema as any[]);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    const results = await this.callOneRowAsync(sql, params);\n    const columnName = assertSingleColumn(results, { functionName: sql, sqlParams: params });\n    return model.parse(results.rows[0][columnName]);\n  }\n\n  async callOptionalScalar<Model extends z.ZodType>(\n    sql: string,\n    model: Model,\n  ): Promise<z.infer<Model> | null>;\n  async callOptionalScalar<Model extends z.ZodType>(\n    sql: string,\n    params: any[],\n    model: Model,\n  ): Promise<z.infer<Model> | null>;\n  /**\n   * Calls the given sproc and returns a single value from a single column, or\n   * null if no rows are returned. Validated against the given Zod schema.\n   * Errors if the sproc returns more than one row or more than one column.\n   */\n  async callOptionalScalar<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: any[] | Model,\n    maybeModel?: Model,\n  ) {\n    const params = maybeModel === undefined ? [] : (paramsOrSchema as any[]);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    const results = await this.callZeroOrOneRowAsync(sql, params);\n    const columnName = assertSingleColumn(results, { functionName: sql, sqlParams: params });\n    if (results.rows.length === 0) {\n      return null;\n    }\n    return model.parse(results.rows[0][columnName]);\n  }\n\n  /**\n   * Executes a query with the specified parameters. Returns the number of rows affected.\n   */\n  async execute(sql: string, params: QueryParams = {}): Promise<number> {\n    const result = await this.queryAsync(sql, params);\n    return result.rowCount ?? 0;\n  }\n\n  /**\n   * Executes a query with the specified parameter, and errors if the query doesn't return exactly one row.\n   */\n  async executeRow(sql: string, params: QueryParams = {}) {\n    const rowCount = await this.execute(sql, params);\n    if (rowCount !== 1) {\n      throw new PostgresError('Incorrect rowCount: ' + rowCount, {\n        sql,\n        sqlParams: params,\n      });\n    }\n  }\n\n  /**\n   * Returns a {@link Cursor} for the given query. The cursor can be used to\n   * read results in batches, which is useful for large result sets.\n   */\n  private async queryCursorWithClient(\n    client: pg.PoolClient,\n    sql: string,\n    params: QueryParams,\n  ): Promise<Cursor> {\n    this._queryCount += 1;\n    debug('queryCursorWithClient()', 'sql:', debugString(sql));\n    debug('queryCursorWithClient()', 'params:', debugParams(params));\n    const { processedSql, paramsArray } = paramsToArray(sql, params, this.errorOnUnusedParameters);\n    lastQueryMap.set(client, processedSql);\n    return client.query(new Cursor(processedSql, paramsArray));\n  }\n\n  async queryCursor<Model extends z.ZodType>(\n    sql: string,\n    model: Model,\n  ): Promise<CursorIterator<z.infer<Model>>>;\n\n  async queryCursor<Model extends z.ZodType>(\n    sql: string,\n    params: QueryParams,\n    model: Model,\n  ): Promise<CursorIterator<z.infer<Model>>>;\n\n  /**\n   * Returns an {@link CursorIterator} that can be used to iterate over the\n   * results of the query in batches, which is useful for large result sets.\n   * Each row will be parsed by the given Zod schema.\n   */\n  async queryCursor<Model extends z.ZodType>(\n    sql: string,\n    paramsOrSchema: Model | QueryParams,\n    maybeModel?: Model,\n  ): Promise<CursorIterator<z.infer<Model>>> {\n    const params = maybeModel === undefined ? {} : (paramsOrSchema as QueryParams);\n    const model = maybeModel === undefined ? (paramsOrSchema as Model) : maybeModel;\n    return this.queryCursorInternal(sql, params, model);\n  }\n\n  private async queryCursorInternal<Model extends z.ZodType>(\n    sql: string,\n    params: QueryParams,\n    model?: Model,\n  ): Promise<CursorIterator<z.infer<Model>>> {\n    const client = await this.getClientAsync();\n    const cursor = await this.queryCursorWithClient(client, sql, params);\n\n    let iterateCalled = false;\n    const iterator: CursorIterator<z.infer<Model>> = {\n      async *iterate(batchSize: number) {\n        // Safety check: if someone calls iterate multiple times, they're\n        // definitely doing something wrong.\n        if (iterateCalled) {\n          throw new Error('iterate() called multiple times');\n        }\n        iterateCalled = true;\n\n        try {\n          while (true) {\n            const rows = await cursor.read(batchSize);\n            if (rows.length === 0) {\n              break;\n            }\n\n            if (model) {\n              yield z.array(model).parse(rows);\n            } else {\n              yield rows;\n            }\n          }\n        } catch (err: any) {\n          throw enhanceError(err, sql, params);\n        } finally {\n          try {\n            await cursor.close();\n          } finally {\n            client.release();\n          }\n        }\n      },\n      stream(batchSize: number) {\n        const transform = new Transform({\n          readableObjectMode: true,\n          writableObjectMode: true,\n          transform(chunk, _encoding, callback) {\n            for (const row of chunk) {\n              this.push(row);\n            }\n            callback();\n          },\n        });\n\n        // TODO: use native `node:stream#compose` once it's stable.\n        const generator = iterator.iterate(batchSize);\n        const pipe = multipipe(Readable.from(generator), transform);\n\n        // When the underlying stream is closed, we need to make sure that the\n        // cursor is also closed. We do this by calling `return()` on the generator,\n        // which will trigger its `finally` block, which will in turn release\n        // the client and close the cursor. The fact that the stream is already\n        // closed by this point means that someone reading from the stream will\n        // never actually see the `null` value that's returned.\n        pipe.once('close', () => {\n          generator.return(null);\n        });\n\n        return pipe;\n      },\n    };\n    return iterator;\n  }\n\n  /**\n   * Set the schema to use for the search path.\n   *\n   * @param schema The schema name to use (can be \"null\" to unset the search path)\n   */\n  async setSearchSchema(schema: string | null) {\n    if (schema == null) {\n      this.searchSchema = schema;\n      return;\n    }\n\n    await this.queryAsync(`CREATE SCHEMA IF NOT EXISTS ${escapeIdentifier(schema)}`, {});\n    // We only set searchSchema after CREATE to avoid the above query() call using searchSchema.\n    this.searchSchema = schema;\n  }\n\n  /**\n   * Get the schema that is currently used for the search path.\n   *\n   * @returns schema in use (may be `null` to indicate no schema)\n   */\n  getSearchSchema(): string | null {\n    return this.searchSchema;\n  }\n\n  /**\n   * Generate, set, and return a random schema name.\n   *\n   * @param prefix The prefix of the new schema, only the first 28 characters will be used (after lowercasing).\n   * @returns The randomly-generated search schema.\n   */\n  async setRandomSearchSchemaAsync(prefix: string): Promise<string> {\n    // truncated prefix (max 28 characters)\n    const truncPrefix = prefix.slice(0, 28);\n    // timestamp in format YYYY-MM-DDTHH:MM:SS.SSSZ (guaranteed to not exceed 27 characters in the spec)\n    const timestamp = new Date().toISOString();\n    // random 6-character suffix to avoid clashes (approx 1.4 billion possible values)\n    const suffix = sampleSize([...'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'], 6).join('');\n\n    // Schema is guaranteed to have length at most 63 (= 28 + 1 + 27 + 1 + 6),\n    // which is the default PostgreSQL identifier limit.\n    // Note that this schema name will need quoting because of characters like ':', '-', etc\n    const schema = `${truncPrefix}_${timestamp}_${suffix}`;\n    await this.setSearchSchema(schema);\n    return schema;\n  }\n\n  /**\n   * Deletes all schemas starting with the given prefix.\n   *\n   * @param prefix The prefix of the schemas to delete.\n   */\n  async clearSchemasStartingWith(prefix: string): Promise<void> {\n    // Sanity check against deleting public, pg_, information_schema, etc.\n    if (prefix === 'public' || prefix.startsWith('pg_') || prefix === 'information_schema') {\n      throw new Error(`Cannot clear schema starting with ${prefix}`);\n    }\n    // Sanity check against a bad prefix.\n    if (prefix.length < 4) {\n      throw new Error(`Prefix is too short: ${prefix}`);\n    }\n\n    await this.queryAsync(\n      `DO $$\n    DECLARE\n      r RECORD;\n    BEGIN\n      FOR r IN\n        SELECT nspname\n        FROM pg_namespace\n        WHERE nspname LIKE ${escapeLiteral(prefix + '%')}\n          AND nspname NOT LIKE 'pg_temp_%'\n      LOOP\n        EXECUTE format('DROP SCHEMA IF EXISTS %I CASCADE;', r.nspname);\n        COMMIT;  -- avoid shared memory exhaustion\n      END LOOP;\n    END $$;`,\n      {},\n    );\n  }\n\n  /** The number of established connections. */\n  get totalCount() {\n    return this.pool?.totalCount ?? 0;\n  }\n\n  /** The number of idle connections. */\n  get idleCount() {\n    return this.pool?.idleCount ?? 0;\n  }\n\n  /** The number of queries waiting for a connection to become available. */\n  get waitingCount() {\n    return this.pool?.waitingCount ?? 0;\n  }\n\n  /** The total number of queries that have been executed by this pool. */\n  get queryCount() {\n    return this._queryCount;\n  }\n}\n"]}