@prairielearn/postgres 6.0.4 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -69,55 +69,55 @@ export declare const callWithClientZeroOrOneRowAsync: (client: import("pg").defa
69
69
  * that conform to the given Zod schema.
70
70
  */
71
71
  export declare const queryRows: {
72
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, model: Model): Promise<import("zod").TypeOf<Model>[]>;
73
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, params: QueryParams, model: Model): Promise<import("zod").TypeOf<Model>[]>;
72
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<import("zod").output<Model>[]>;
73
+ <Model extends import("zod").ZodType>(sql: string, params: QueryParams, model: Model): Promise<import("zod").output<Model>[]>;
74
74
  };
75
75
  /**
76
76
  * Executes a query with the specified parameters. Returns exactly one row that conforms to the given Zod schema.
77
77
  */
78
78
  export declare const queryRow: {
79
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, model: Model): Promise<import("zod").TypeOf<Model>>;
80
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, params: QueryParams, model: Model): Promise<import("zod").TypeOf<Model>>;
79
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<import("zod").output<Model>>;
80
+ <Model extends import("zod").ZodType>(sql: string, params: QueryParams, model: Model): Promise<import("zod").output<Model>>;
81
81
  };
82
82
  /**
83
83
  * Executes a query with the specified parameters. Returns either null or a
84
84
  * single row that conforms to the given Zod schema, and errors otherwise.
85
85
  */
86
86
  export declare const queryOptionalRow: {
87
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, model: Model): Promise<import("zod").TypeOf<Model> | null>;
88
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, params: QueryParams, model: Model): Promise<import("zod").TypeOf<Model> | null>;
87
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<import("zod").output<Model> | null>;
88
+ <Model extends import("zod").ZodType>(sql: string, params: QueryParams, model: Model): Promise<import("zod").output<Model> | null>;
89
89
  };
90
90
  /**
91
91
  * Calls the given sproc with the specified parameters.
92
92
  * Errors if the sproc does not return anything.
93
93
  */
94
94
  export declare const callRows: {
95
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, model: Model): Promise<import("zod").TypeOf<Model>[]>;
96
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, params: any[], model: Model): Promise<import("zod").TypeOf<Model>[]>;
95
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<import("zod").output<Model>[]>;
96
+ <Model extends import("zod").ZodType>(sql: string, params: any[], model: Model): Promise<import("zod").output<Model>[]>;
97
97
  };
98
98
  /**
99
99
  * Calls the given sproc with the specified parameters.
100
100
  * Returns exactly one row from the sproc that conforms to the given Zod schema.
101
101
  */
102
102
  export declare const callRow: {
103
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, model: Model): Promise<import("zod").TypeOf<Model>>;
104
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, params: any[], model: Model): Promise<import("zod").TypeOf<Model>>;
103
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<import("zod").output<Model>>;
104
+ <Model extends import("zod").ZodType>(sql: string, params: any[], model: Model): Promise<import("zod").output<Model>>;
105
105
  };
106
106
  /**
107
107
  * Calls the given sproc with the specified parameters. Returns either null
108
108
  * or a single row that conforms to the given Zod schema.
109
109
  */
110
110
  export declare const callOptionalRow: {
111
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, model: Model): Promise<import("zod").TypeOf<Model> | null>;
112
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, params: any[], model: Model): Promise<import("zod").TypeOf<Model> | null>;
111
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<import("zod").output<Model> | null>;
112
+ <Model extends import("zod").ZodType>(sql: string, params: any[], model: Model): Promise<import("zod").output<Model> | null>;
113
113
  };
114
114
  /**
115
115
  * Executes a query and returns all values from a single column, validated
116
116
  * against the given Zod schema. Errors if the query returns more than one column.
117
117
  */
118
118
  export declare const queryScalars: {
119
- <Model extends import("zod").ZodTypeAny>(sql: string, model: Model): Promise<import("zod").TypeOf<Model>[]>;
120
- <Model extends import("zod").ZodTypeAny>(sql: string, params: QueryParams, model: Model): Promise<import("zod").TypeOf<Model>[]>;
119
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<import("zod").output<Model>[]>;
120
+ <Model extends import("zod").ZodType>(sql: string, params: QueryParams, model: Model): Promise<import("zod").output<Model>[]>;
121
121
  };
122
122
  /**
123
123
  * Executes a query and returns a single value from a single column, validated
@@ -125,8 +125,8 @@ export declare const queryScalars: {
125
125
  * one row or returns more than one column.
126
126
  */
127
127
  export declare const queryScalar: {
128
- <Model extends import("zod").ZodTypeAny>(sql: string, model: Model): Promise<import("zod").TypeOf<Model>>;
129
- <Model extends import("zod").ZodTypeAny>(sql: string, params: QueryParams, model: Model): Promise<import("zod").TypeOf<Model>>;
128
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<import("zod").output<Model>>;
129
+ <Model extends import("zod").ZodType>(sql: string, params: QueryParams, model: Model): Promise<import("zod").output<Model>>;
130
130
  };
131
131
  /**
132
132
  * Executes a query and returns a single value from a single column, or null
@@ -134,16 +134,16 @@ export declare const queryScalar: {
134
134
  * the query returns more than one row or more than one column.
135
135
  */
136
136
  export declare const queryOptionalScalar: {
137
- <Model extends import("zod").ZodTypeAny>(sql: string, model: Model): Promise<import("zod").TypeOf<Model> | null>;
138
- <Model extends import("zod").ZodTypeAny>(sql: string, params: QueryParams, model: Model): Promise<import("zod").TypeOf<Model> | null>;
137
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<import("zod").output<Model> | null>;
138
+ <Model extends import("zod").ZodType>(sql: string, params: QueryParams, model: Model): Promise<import("zod").output<Model> | null>;
139
139
  };
140
140
  /**
141
141
  * Calls the given sproc and returns all values from a single column, validated
142
142
  * against the given Zod schema. Errors if the sproc returns more than one column.
143
143
  */
144
144
  export declare const callScalars: {
145
- <Model extends import("zod").ZodTypeAny>(sql: string, model: Model): Promise<import("zod").TypeOf<Model>[]>;
146
- <Model extends import("zod").ZodTypeAny>(sql: string, params: any[], model: Model): Promise<import("zod").TypeOf<Model>[]>;
145
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<import("zod").output<Model>[]>;
146
+ <Model extends import("zod").ZodType>(sql: string, params: any[], model: Model): Promise<import("zod").output<Model>[]>;
147
147
  };
148
148
  /**
149
149
  * Calls the given sproc and returns a single value from a single column, validated
@@ -151,8 +151,8 @@ export declare const callScalars: {
151
151
  * one row or returns more than one column.
152
152
  */
153
153
  export declare const callScalar: {
154
- <Model extends import("zod").ZodTypeAny>(sql: string, model: Model): Promise<import("zod").TypeOf<Model>>;
155
- <Model extends import("zod").ZodTypeAny>(sql: string, params: any[], model: Model): Promise<import("zod").TypeOf<Model>>;
154
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<import("zod").output<Model>>;
155
+ <Model extends import("zod").ZodType>(sql: string, params: any[], model: Model): Promise<import("zod").output<Model>>;
156
156
  };
157
157
  /**
158
158
  * Calls the given sproc and returns a single value from a single column, or
@@ -160,8 +160,8 @@ export declare const callScalar: {
160
160
  * Errors if the sproc returns more than one row or more than one column.
161
161
  */
162
162
  export declare const callOptionalScalar: {
163
- <Model extends import("zod").ZodTypeAny>(sql: string, model: Model): Promise<import("zod").TypeOf<Model> | null>;
164
- <Model extends import("zod").ZodTypeAny>(sql: string, params: any[], model: Model): Promise<import("zod").TypeOf<Model> | null>;
163
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<import("zod").output<Model> | null>;
164
+ <Model extends import("zod").ZodType>(sql: string, params: any[], model: Model): Promise<import("zod").output<Model> | null>;
165
165
  };
166
166
  /**
167
167
  * Executes a query with the specified parameters. Returns the number of rows affected.
@@ -177,8 +177,8 @@ export declare const executeRow: (sql: string, params?: QueryParams) => Promise<
177
177
  * Each row will be parsed by the given Zod schema.
178
178
  */
179
179
  export declare const queryCursor: {
180
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, model: Model): Promise<CursorIterator<import("zod").TypeOf<Model>>>;
181
- <Model extends import("./pool.js").AnyRowSchema>(sql: string, params: QueryParams, model: Model): Promise<CursorIterator<import("zod").TypeOf<Model>>>;
180
+ <Model extends import("zod").ZodType>(sql: string, model: Model): Promise<CursorIterator<import("zod").output<Model>>>;
181
+ <Model extends import("zod").ZodType>(sql: string, params: QueryParams, model: Model): Promise<CursorIterator<import("zod").output<Model>>>;
182
182
  };
183
183
  /**
184
184
  * Set the schema to use for the search path.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { type PoolClient } from 'pg';
2
2
  export { loadSql, loadSqlEquiv } from './loader.js';
3
- export { type AnyRowSchema, PostgresPool, type PostgresPoolConfig } from './pool.js';
3
+ export { PostgresPool, type PostgresPoolConfig } from './pool.js';
4
4
  export * from './default-pool.js';
5
5
  export { formatQueryWithErrorPosition } from './error.js';
6
6
  export { makePostgresTestUtils, type PostgresTestUtils, type PostgresTestUtilsOptions, } from './test-utils.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,IAAI,CAAC;AAErC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,KAAK,YAAY,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAErF,cAAc,mBAAmB,CAAC;AAElC,OAAO,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAE1D,OAAO,EACL,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,GAC9B,MAAM,iBAAiB,CAAC","sourcesContent":["export { type PoolClient } from 'pg';\n\nexport { loadSql, loadSqlEquiv } from './loader.js';\nexport { type AnyRowSchema, PostgresPool, type PostgresPoolConfig } from './pool.js';\n\nexport * from './default-pool.js';\n\nexport { formatQueryWithErrorPosition } from './error.js';\n\nexport {\n makePostgresTestUtils,\n type PostgresTestUtils,\n type PostgresTestUtilsOptions,\n} from './test-utils.js';\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,IAAI,CAAC;AAErC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAElE,cAAc,mBAAmB,CAAC;AAElC,OAAO,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAE1D,OAAO,EACL,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,GAC9B,MAAM,iBAAiB,CAAC","sourcesContent":["export { type PoolClient } from 'pg';\n\nexport { loadSql, loadSqlEquiv } from './loader.js';\nexport { PostgresPool, type PostgresPoolConfig } from './pool.js';\n\nexport * from './default-pool.js';\n\nexport { formatQueryWithErrorPosition } from './error.js';\n\nexport {\n makePostgresTestUtils,\n type PostgresTestUtils,\n type PostgresTestUtilsOptions,\n} from './test-utils.js';\n"]}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,MAAM,IAAI,CAAC;AAErC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAqB,YAAY,EAA2B,MAAM,WAAW,CAAC;AAErF,cAAc,mBAAmB,CAAC;AAElC,OAAO,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAE1D,OAAO,EACL,qBAAqB,GAGtB,MAAM,iBAAiB,CAAC","sourcesContent":["export { type PoolClient } from 'pg';\n\nexport { loadSql, loadSqlEquiv } from './loader.js';\nexport { type AnyRowSchema, PostgresPool, type PostgresPoolConfig } from './pool.js';\n\nexport * from './default-pool.js';\n\nexport { formatQueryWithErrorPosition } from './error.js';\n\nexport {\n makePostgresTestUtils,\n type PostgresTestUtils,\n type PostgresTestUtilsOptions,\n} from './test-utils.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,MAAM,IAAI,CAAC;AAErC,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,EAAE,YAAY,EAA2B,MAAM,WAAW,CAAC;AAElE,cAAc,mBAAmB,CAAC;AAElC,OAAO,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAE1D,OAAO,EACL,qBAAqB,GAGtB,MAAM,iBAAiB,CAAC","sourcesContent":["export { type PoolClient } from 'pg';\n\nexport { loadSql, loadSqlEquiv } from './loader.js';\nexport { PostgresPool, type PostgresPoolConfig } from './pool.js';\n\nexport * from './default-pool.js';\n\nexport { formatQueryWithErrorPosition } from './error.js';\n\nexport {\n makePostgresTestUtils,\n type PostgresTestUtils,\n type PostgresTestUtilsOptions,\n} from './test-utils.js';\n"]}
package/dist/pool.d.ts CHANGED
@@ -1,14 +1,6 @@
1
1
  import pg, { type QueryResult } from 'pg';
2
2
  import { z } from 'zod';
3
3
  export type QueryParams = Record<string, any> | any[];
4
- /**
5
- * Type constraint for row schemas accepted by query functions.
6
- * Accepts `z.object(...)`, unions/intersections/discriminated unions of objects,
7
- * transforms/refinements of any of those, branded variants, and `z.unknown()`
8
- * as an escape hatch.
9
- */
10
- type AnyObjectLikeSchema = z.AnyZodObject | z.ZodEffects<AnyObjectLikeSchema, any, any> | z.ZodIntersection<AnyObjectLikeSchema, AnyObjectLikeSchema> | z.ZodUnion<Readonly<[AnyObjectLikeSchema, ...AnyObjectLikeSchema[]]>> | z.ZodDiscriminatedUnion<string, z.AnyZodObject[]>;
11
- export type AnyRowSchema = AnyObjectLikeSchema | z.ZodBranded<AnyObjectLikeSchema, any> | z.ZodUnknown;
12
4
  export interface CursorIterator<T> {
13
5
  iterate: (batchSize: number) => AsyncGenerator<T[]>;
14
6
  stream: (batchSize: number) => NodeJS.ReadWriteStream;
@@ -136,30 +128,30 @@ export declare class PostgresPool {
136
128
  * Errors if the sproc returns more than one row.
137
129
  */
138
130
  callWithClientZeroOrOneRowAsync(client: pg.PoolClient, functionName: string, params: any[]): Promise<pg.QueryResult>;
139
- queryRows<Model extends AnyRowSchema>(sql: string, model: Model): Promise<z.infer<Model>[]>;
140
- queryRows<Model extends AnyRowSchema>(sql: string, params: QueryParams, model: Model): Promise<z.infer<Model>[]>;
141
- queryRow<Model extends AnyRowSchema>(sql: string, model: Model): Promise<z.infer<Model>>;
142
- queryRow<Model extends AnyRowSchema>(sql: string, params: QueryParams, model: Model): Promise<z.infer<Model>>;
143
- queryOptionalRow<Model extends AnyRowSchema>(sql: string, model: Model): Promise<z.infer<Model> | null>;
144
- queryOptionalRow<Model extends AnyRowSchema>(sql: string, params: QueryParams, model: Model): Promise<z.infer<Model> | null>;
145
- callRows<Model extends AnyRowSchema>(sql: string, model: Model): Promise<z.infer<Model>[]>;
146
- callRows<Model extends AnyRowSchema>(sql: string, params: any[], model: Model): Promise<z.infer<Model>[]>;
147
- callRow<Model extends AnyRowSchema>(sql: string, model: Model): Promise<z.infer<Model>>;
148
- callRow<Model extends AnyRowSchema>(sql: string, params: any[], model: Model): Promise<z.infer<Model>>;
149
- callOptionalRow<Model extends AnyRowSchema>(sql: string, model: Model): Promise<z.infer<Model> | null>;
150
- callOptionalRow<Model extends AnyRowSchema>(sql: string, params: any[], model: Model): Promise<z.infer<Model> | null>;
151
- queryScalars<Model extends z.ZodTypeAny>(sql: string, model: Model): Promise<z.infer<Model>[]>;
152
- queryScalars<Model extends z.ZodTypeAny>(sql: string, params: QueryParams, model: Model): Promise<z.infer<Model>[]>;
153
- queryScalar<Model extends z.ZodTypeAny>(sql: string, model: Model): Promise<z.infer<Model>>;
154
- queryScalar<Model extends z.ZodTypeAny>(sql: string, params: QueryParams, model: Model): Promise<z.infer<Model>>;
155
- queryOptionalScalar<Model extends z.ZodTypeAny>(sql: string, model: Model): Promise<z.infer<Model> | null>;
156
- queryOptionalScalar<Model extends z.ZodTypeAny>(sql: string, params: QueryParams, model: Model): Promise<z.infer<Model> | null>;
157
- callScalars<Model extends z.ZodTypeAny>(sql: string, model: Model): Promise<z.infer<Model>[]>;
158
- callScalars<Model extends z.ZodTypeAny>(sql: string, params: any[], model: Model): Promise<z.infer<Model>[]>;
159
- callScalar<Model extends z.ZodTypeAny>(sql: string, model: Model): Promise<z.infer<Model>>;
160
- callScalar<Model extends z.ZodTypeAny>(sql: string, params: any[], model: Model): Promise<z.infer<Model>>;
161
- callOptionalScalar<Model extends z.ZodTypeAny>(sql: string, model: Model): Promise<z.infer<Model> | null>;
162
- callOptionalScalar<Model extends z.ZodTypeAny>(sql: string, params: any[], model: Model): Promise<z.infer<Model> | null>;
131
+ queryRows<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>[]>;
132
+ queryRows<Model extends z.ZodType>(sql: string, params: QueryParams, model: Model): Promise<z.infer<Model>[]>;
133
+ queryRow<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>>;
134
+ queryRow<Model extends z.ZodType>(sql: string, params: QueryParams, model: Model): Promise<z.infer<Model>>;
135
+ queryOptionalRow<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model> | null>;
136
+ queryOptionalRow<Model extends z.ZodType>(sql: string, params: QueryParams, model: Model): Promise<z.infer<Model> | null>;
137
+ callRows<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>[]>;
138
+ callRows<Model extends z.ZodType>(sql: string, params: any[], model: Model): Promise<z.infer<Model>[]>;
139
+ callRow<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>>;
140
+ callRow<Model extends z.ZodType>(sql: string, params: any[], model: Model): Promise<z.infer<Model>>;
141
+ callOptionalRow<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model> | null>;
142
+ callOptionalRow<Model extends z.ZodType>(sql: string, params: any[], model: Model): Promise<z.infer<Model> | null>;
143
+ queryScalars<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>[]>;
144
+ queryScalars<Model extends z.ZodType>(sql: string, params: QueryParams, model: Model): Promise<z.infer<Model>[]>;
145
+ queryScalar<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>>;
146
+ queryScalar<Model extends z.ZodType>(sql: string, params: QueryParams, model: Model): Promise<z.infer<Model>>;
147
+ queryOptionalScalar<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model> | null>;
148
+ queryOptionalScalar<Model extends z.ZodType>(sql: string, params: QueryParams, model: Model): Promise<z.infer<Model> | null>;
149
+ callScalars<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>[]>;
150
+ callScalars<Model extends z.ZodType>(sql: string, params: any[], model: Model): Promise<z.infer<Model>[]>;
151
+ callScalar<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model>>;
152
+ callScalar<Model extends z.ZodType>(sql: string, params: any[], model: Model): Promise<z.infer<Model>>;
153
+ callOptionalScalar<Model extends z.ZodType>(sql: string, model: Model): Promise<z.infer<Model> | null>;
154
+ callOptionalScalar<Model extends z.ZodType>(sql: string, params: any[], model: Model): Promise<z.infer<Model> | null>;
163
155
  /**
164
156
  * Executes a query with the specified parameters. Returns the number of rows affected.
165
157
  */
@@ -169,8 +161,8 @@ export declare class PostgresPool {
169
161
  */
170
162
  executeRow(sql: string, params?: QueryParams): Promise<void>;
171
163
  private queryCursorWithClient;
172
- queryCursor<Model extends AnyRowSchema>(sql: string, model: Model): Promise<CursorIterator<z.infer<Model>>>;
173
- queryCursor<Model extends AnyRowSchema>(sql: string, params: QueryParams, model: Model): Promise<CursorIterator<z.infer<Model>>>;
164
+ queryCursor<Model extends z.ZodType>(sql: string, model: Model): Promise<CursorIterator<z.infer<Model>>>;
165
+ queryCursor<Model extends z.ZodType>(sql: string, params: QueryParams, model: Model): Promise<CursorIterator<z.infer<Model>>>;
174
166
  private queryCursorInternal;
175
167
  /**
176
168
  * Set the schema to use for the search path.
@@ -206,5 +198,4 @@ export declare class PostgresPool {
206
198
  /** The total number of queries that have been executed by this pool. */
207
199
  get queryCount(): number;
208
200
  }
209
- export {};
210
201
  //# sourceMappingURL=pool.d.ts.map
@@ -1 +1 @@
1
- {"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\n/**\n * Type constraint for row schemas accepted by query functions.\n * Accepts `z.object(...)`, unions/intersections/discriminated unions of objects,\n * transforms/refinements of any of those, branded variants, and `z.unknown()`\n * as an escape hatch.\n */\ntype AnyObjectLikeSchema =\n | z.AnyZodObject\n | z.ZodEffects<AnyObjectLikeSchema, any, any>\n | z.ZodIntersection<AnyObjectLikeSchema, AnyObjectLikeSchema>\n | z.ZodUnion<Readonly<[AnyObjectLikeSchema, ...AnyObjectLikeSchema[]]>>\n | z.ZodDiscriminatedUnion<string, z.AnyZodObject[]>;\nexport type AnyRowSchema =\n | AnyObjectLikeSchema\n | z.ZodBranded<AnyObjectLikeSchema, any>\n | z.ZodUnknown;\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 */\nfunction escapeIdentifier(identifier: string): string {\n // Note that as of 2021-06-29 escapeIdentifier() is undocumented. See:\n // https://github.com/brianc/node-postgres/pull/396\n // https://github.com/brianc/node-postgres/issues/1978\n // https://www.postgresql.org/docs/current/sql-syntax-lexical.html\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 AnyRowSchema>(sql: string, model: Model): Promise<z.infer<Model>[]>;\n async queryRows<Model extends AnyRowSchema>(\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 AnyRowSchema>(\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 AnyRowSchema>(sql: string, model: Model): Promise<z.infer<Model>>;\n async queryRow<Model extends AnyRowSchema>(\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 AnyRowSchema>(\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 AnyRowSchema>(\n sql: string,\n model: Model,\n ): Promise<z.infer<Model> | null>;\n async queryOptionalRow<Model extends AnyRowSchema>(\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 AnyRowSchema>(\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 AnyRowSchema>(sql: string, model: Model): Promise<z.infer<Model>[]>;\n async callRows<Model extends AnyRowSchema>(\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 AnyRowSchema>(\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 AnyRowSchema>(sql: string, model: Model): Promise<z.infer<Model>>;\n async callRow<Model extends AnyRowSchema>(\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 AnyRowSchema>(\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 AnyRowSchema>(\n sql: string,\n model: Model,\n ): Promise<z.infer<Model> | null>;\n async callOptionalRow<Model extends AnyRowSchema>(\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 AnyRowSchema>(\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.ZodTypeAny>(\n sql: string,\n model: Model,\n ): Promise<z.infer<Model>[]>;\n async queryScalars<Model extends z.ZodTypeAny>(\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.ZodTypeAny>(\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.ZodTypeAny>(sql: string, model: Model): Promise<z.infer<Model>>;\n async queryScalar<Model extends z.ZodTypeAny>(\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.ZodTypeAny>(\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.ZodTypeAny>(\n sql: string,\n model: Model,\n ): Promise<z.infer<Model> | null>;\n async queryOptionalScalar<Model extends z.ZodTypeAny>(\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.ZodTypeAny>(\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.ZodTypeAny>(\n sql: string,\n model: Model,\n ): Promise<z.infer<Model>[]>;\n async callScalars<Model extends z.ZodTypeAny>(\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.ZodTypeAny>(\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.ZodTypeAny>(sql: string, model: Model): Promise<z.infer<Model>>;\n async callScalar<Model extends z.ZodTypeAny>(\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.ZodTypeAny>(\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.ZodTypeAny>(\n sql: string,\n model: Model,\n ): Promise<z.infer<Model> | null>;\n async callOptionalScalar<Model extends z.ZodTypeAny>(\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.ZodTypeAny>(\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 AnyRowSchema>(\n sql: string,\n model: Model,\n ): Promise<CursorIterator<z.infer<Model>>>;\n\n async queryCursor<Model extends AnyRowSchema>(\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 AnyRowSchema>(\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 AnyRowSchema>(\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"]}
1
+ {"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 */\nfunction escapeIdentifier(identifier: string): string {\n // Note that as of 2021-06-29 escapeIdentifier() is undocumented. See:\n // https://github.com/brianc/node-postgres/pull/396\n // https://github.com/brianc/node-postgres/issues/1978\n // https://www.postgresql.org/docs/current/sql-syntax-lexical.html\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"]}