@prairielearn/postgres 5.0.3 → 6.0.1

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,49 +69,6 @@ export const endTransactionAsync = defaultPool.endTransactionAsync.bind(defaultP
69
69
  * will be committed otherwise.
70
70
  */
71
71
  export const runInTransactionAsync = defaultPool.runInTransactionAsync.bind(defaultPool);
72
- /**
73
- * Executes a query with the specified parameters.
74
- *
75
- * @deprecated Use {@link execute} instead.
76
- *
77
- * Using the return value of this function directly is not recommended. Instead, use
78
- * {@link queryRows}, {@link queryRow}, or {@link queryOptionalRow}.
79
- */
80
- export const queryAsync = defaultPool.queryAsync.bind(defaultPool);
81
- /**
82
- * Executes a query with the specified parameters. Errors if the query does
83
- * not return exactly one row.
84
- *
85
- * @deprecated Use {@link executeRow} or {@link queryRow} instead.
86
- */
87
- export const queryOneRowAsync = defaultPool.queryOneRowAsync.bind(defaultPool);
88
- /**
89
- * Executes a query with the specified parameters. Errors if the query
90
- * returns more than one row.
91
- *
92
- * @deprecated Use {@link queryOptionalRow} instead.
93
- */
94
- export const queryZeroOrOneRowAsync = defaultPool.queryZeroOrOneRowAsync.bind(defaultPool);
95
- /**
96
- * Calls the given sproc with the specified parameters.
97
- *
98
- * @deprecated Use {@link callRows} instead.
99
- */
100
- export const callAsync = defaultPool.callAsync.bind(defaultPool);
101
- /**
102
- * Calls the given sproc with the specified parameters. Errors if the
103
- * sproc does not return exactly one row.
104
- *
105
- * @deprecated Use {@link callRow} instead.
106
- */
107
- export const callOneRowAsync = defaultPool.callOneRowAsync.bind(defaultPool);
108
- /**
109
- * Calls the given sproc with the specified parameters. Errors if the
110
- * sproc returns more than one row.
111
- *
112
- * @deprecated Use {@link callOptionalRow} instead.
113
- */
114
- export const callZeroOrOneRowAsync = defaultPool.callZeroOrOneRowAsync.bind(defaultPool);
115
72
  /**
116
73
  * Calls a sproc with the specified parameters using a specific client.
117
74
  */
@@ -130,21 +87,15 @@ export const callWithClientZeroOrOneRowAsync =
130
87
  /**
131
88
  * Executes a query with the specified parameters. Returns an array of rows
132
89
  * that conform to the given Zod schema.
133
- *
134
- * If the query returns a single column, the return value will be a list of column values.
135
90
  */
136
91
  export const queryRows = defaultPool.queryRows.bind(defaultPool);
137
92
  /**
138
93
  * Executes a query with the specified parameters. Returns exactly one row that conforms to the given Zod schema.
139
- *
140
- * If the query returns a single column, the return value will be the column value itself.
141
94
  */
142
95
  export const queryRow = defaultPool.queryRow.bind(defaultPool);
143
96
  /**
144
97
  * Executes a query with the specified parameters. Returns either null or a
145
98
  * single row that conforms to the given Zod schema, and errors otherwise.
146
- *
147
- * If the query returns a single column, the return value will be the column value itself.
148
99
  */
149
100
  export const queryOptionalRow = defaultPool.queryOptionalRow.bind(defaultPool);
150
101
  /**
@@ -162,6 +113,40 @@ export const callRow = defaultPool.callRow.bind(defaultPool);
162
113
  * or a single row that conforms to the given Zod schema.
163
114
  */
164
115
  export const callOptionalRow = defaultPool.callOptionalRow.bind(defaultPool);
116
+ /**
117
+ * Executes a query and returns all values from a single column, validated
118
+ * against the given Zod schema. Errors if the query returns more than one column.
119
+ */
120
+ export const queryScalars = defaultPool.queryScalars.bind(defaultPool);
121
+ /**
122
+ * Executes a query and returns a single value from a single column, validated
123
+ * against the given Zod schema. Errors if the query does not return exactly
124
+ * one row or returns more than one column.
125
+ */
126
+ export const queryScalar = defaultPool.queryScalar.bind(defaultPool);
127
+ /**
128
+ * Executes a query and returns a single value from a single column, or null
129
+ * if no rows are returned. Validated against the given Zod schema. Errors if
130
+ * the query returns more than one row or more than one column.
131
+ */
132
+ export const queryOptionalScalar = defaultPool.queryOptionalScalar.bind(defaultPool);
133
+ /**
134
+ * Calls the given sproc and returns all values from a single column, validated
135
+ * against the given Zod schema. Errors if the sproc returns more than one column.
136
+ */
137
+ export const callScalars = defaultPool.callScalars.bind(defaultPool);
138
+ /**
139
+ * Calls the given sproc and returns a single value from a single column, validated
140
+ * against the given Zod schema. Errors if the sproc does not return exactly
141
+ * one row or returns more than one column.
142
+ */
143
+ export const callScalar = defaultPool.callScalar.bind(defaultPool);
144
+ /**
145
+ * Calls the given sproc and returns a single value from a single column, or
146
+ * null if no rows are returned. Validated against the given Zod schema.
147
+ * Errors if the sproc returns more than one row or more than one column.
148
+ */
149
+ export const callOptionalScalar = defaultPool.callOptionalScalar.bind(defaultPool);
165
150
 
166
151
  /**
167
152
  * Executes a query with the specified parameters. Returns the number of rows affected.
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { type PoolClient } from 'pg';
2
2
 
3
3
  export { loadSql, loadSqlEquiv } from './loader.js';
4
- export { PostgresPool, type PostgresPoolConfig } from './pool.js';
4
+ export { type AnyRowSchema, PostgresPool, type PostgresPoolConfig } from './pool.js';
5
5
 
6
6
  export * from './default-pool.js';
7
7
 
package/src/pool.test.ts CHANGED
@@ -6,14 +6,19 @@ import { ZodError, z } from 'zod';
6
6
 
7
7
  import {
8
8
  callOptionalRow,
9
+ callOptionalScalar,
9
10
  callRow,
10
11
  callRows,
12
+ callScalar,
13
+ callScalars,
11
14
  execute,
12
- queryAsync,
13
15
  queryCursor,
14
16
  queryOptionalRow,
17
+ queryOptionalScalar,
15
18
  queryRow,
16
19
  queryRows,
20
+ queryScalar,
21
+ queryScalars,
17
22
  } from './default-pool.js';
18
23
  import { makePostgresTestUtils } from './test-utils.js';
19
24
 
@@ -59,32 +64,35 @@ describe('@prairielearn/postgres', function () {
59
64
  describe('paramsToArray', () => {
60
65
  it('enforces SQL must be a string', async () => {
61
66
  // @ts-expect-error SQL must be a string
62
- const rows = queryAsync({ invalid: true }, {});
67
+ const rows = execute({ invalid: true }, {});
63
68
  await expect(rows).rejects.toThrow('SQL must be a string');
64
69
  });
65
70
 
66
71
  it('enforces params must be array or object', async () => {
67
72
  // @ts-expect-error params must be an array or object
68
- const rows = queryAsync('SELECT 33;', 33);
73
+ const rows = execute('SELECT 33;', 33);
69
74
  await expect(rows).rejects.toThrow('params must be array or object');
70
75
  });
71
76
 
72
77
  it('rejects missing parameters', async () => {
73
- const rows = queryAsync('SELECT $missing;', {});
78
+ const rows = execute('SELECT $missing;', {});
74
79
  await expect(rows).rejects.toThrow('Missing parameter');
75
80
  });
76
81
 
77
82
  it('rejects unused parameters in testing', async () => {
78
- const rows = queryAsync('SELECT 33;', { unsed_parameter: true });
83
+ const rows = execute('SELECT 33;', { unsed_parameter: true });
79
84
  await expect(rows).rejects.toThrow('Unused parameter');
80
85
  });
81
86
  });
82
87
 
83
88
  describe('queryRows', () => {
84
89
  it('handles single column', async () => {
85
- const rows = await queryRows('SELECT id FROM workspaces WHERE id <= 10;', z.string());
90
+ const rows = await queryRows(
91
+ 'SELECT id FROM workspaces WHERE id <= 10;',
92
+ z.object({ id: z.string() }),
93
+ );
86
94
  assert.lengthOf(rows, 10);
87
- assert.equal(rows[0], '1');
95
+ assert.equal(rows[0].id, '1');
88
96
  });
89
97
 
90
98
  it('handles multiple columns', async () => {
@@ -106,8 +114,11 @@ describe('@prairielearn/postgres', function () {
106
114
 
107
115
  describe('queryRow', () => {
108
116
  it('handles single column', async () => {
109
- const row = await queryRow('SELECT id FROM workspaces WHERE id = 1;', z.string());
110
- assert.equal(row, '1');
117
+ const row = await queryRow(
118
+ 'SELECT id FROM workspaces WHERE id = 1;',
119
+ z.object({ id: z.string() }),
120
+ );
121
+ assert.equal(row.id, '1');
111
122
  });
112
123
 
113
124
  it('handles multiple columns', async () => {
@@ -134,8 +145,11 @@ describe('@prairielearn/postgres', function () {
134
145
 
135
146
  describe('queryOptionalRow', () => {
136
147
  it('handles single column', async () => {
137
- const row = await queryRow('SELECT id FROM workspaces WHERE id = 1;', z.string());
138
- assert.equal(row, '1');
148
+ const row = await queryRow(
149
+ 'SELECT id FROM workspaces WHERE id = 1;',
150
+ z.object({ id: z.string() }),
151
+ );
152
+ assert.equal(row.id, '1');
139
153
  });
140
154
 
141
155
  it('handles multiple columns', async () => {
@@ -171,15 +185,15 @@ describe('@prairielearn/postgres', function () {
171
185
 
172
186
  describe('callRows', () => {
173
187
  it('handles single column', async () => {
174
- const rows = await callRows('test_sproc_one_column_ten_rows', z.string());
188
+ const rows = await callRows('test_sproc_one_column_ten_rows', z.object({ id: z.string() }));
175
189
  assert.lengthOf(rows, 10);
176
- assert.equal(rows[0], '1');
190
+ assert.equal(rows[0].id, '1');
177
191
  });
178
192
 
179
193
  it('handles parameters', async () => {
180
- const rows = await callRows('test_sproc_one_column', [10], z.string());
194
+ const rows = await callRows('test_sproc_one_column', [10], z.object({ id: z.string() }));
181
195
  assert.lengthOf(rows, 10);
182
- assert.equal(rows[0], '1');
196
+ assert.equal(rows[0].id, '1');
183
197
  });
184
198
 
185
199
  it('handles multiple columns', async () => {
@@ -194,13 +208,13 @@ describe('@prairielearn/postgres', function () {
194
208
 
195
209
  describe('callRow', () => {
196
210
  it('handles single column', async () => {
197
- const row = await callRow('test_sproc_one_column_one_row', z.string());
198
- assert.equal(row, '1');
211
+ const row = await callRow('test_sproc_one_column_one_row', z.object({ id: z.string() }));
212
+ assert.equal(row.id, '1');
199
213
  });
200
214
 
201
215
  it('handles parameters', async () => {
202
- const row = await callRow('test_sproc_one_column', [1], z.string());
203
- assert.equal(row, '1');
216
+ const row = await callRow('test_sproc_one_column', [1], z.object({ id: z.string() }));
217
+ assert.equal(row.id, '1');
204
218
  });
205
219
 
206
220
  it('handles multiple columns', async () => {
@@ -222,13 +236,16 @@ describe('@prairielearn/postgres', function () {
222
236
 
223
237
  describe('callOptionalRow', () => {
224
238
  it('handles single column', async () => {
225
- const row = await callOptionalRow('test_sproc_one_column_one_row', z.string());
226
- assert.equal(row, '1');
239
+ const row = await callOptionalRow(
240
+ 'test_sproc_one_column_one_row',
241
+ z.object({ id: z.string() }),
242
+ );
243
+ assert.equal(row?.id, '1');
227
244
  });
228
245
 
229
246
  it('handles parameters', async () => {
230
- const row = await callOptionalRow('test_sproc_one_column', [1], z.string());
231
- assert.equal(row, '1');
247
+ const row = await callOptionalRow('test_sproc_one_column', [1], z.object({ id: z.string() }));
248
+ assert.equal(row?.id, '1');
232
249
  });
233
250
 
234
251
  it('handles multiple columns', async () => {
@@ -249,14 +266,173 @@ describe('@prairielearn/postgres', function () {
249
266
  });
250
267
  });
251
268
 
269
+ describe('queryScalars', () => {
270
+ it('returns all scalar values', async () => {
271
+ const ids = await queryScalars(
272
+ 'SELECT id FROM workspaces WHERE id <= 10 ORDER BY id ASC;',
273
+ z.string(),
274
+ );
275
+ assert.lengthOf(ids, 10);
276
+ assert.equal(ids[0], '1');
277
+ assert.equal(ids[9], '10');
278
+ });
279
+
280
+ it('handles parameters', async () => {
281
+ const ids = await queryScalars(
282
+ 'SELECT id FROM workspaces WHERE id <= $1 ORDER BY id ASC;',
283
+ [5],
284
+ z.string(),
285
+ );
286
+ assert.lengthOf(ids, 5);
287
+ });
288
+
289
+ it('rejects multi-column queries', async () => {
290
+ const result = queryScalars('SELECT * FROM workspaces WHERE id <= 10;', z.string());
291
+ await expect(result).rejects.toThrow('Expected exactly one column');
292
+ });
293
+ });
294
+
295
+ describe('queryScalar', () => {
296
+ it('returns a single scalar value', async () => {
297
+ const id = await queryScalar('SELECT id FROM workspaces WHERE id = 1;', z.string());
298
+ assert.equal(id, '1');
299
+ });
300
+
301
+ it('handles parameters', async () => {
302
+ const id = await queryScalar('SELECT id FROM workspaces WHERE id = $1;', [1], z.string());
303
+ assert.equal(id, '1');
304
+ });
305
+
306
+ it('rejects results with zero rows', async () => {
307
+ const result = queryScalar('SELECT id FROM workspaces WHERE id = -1;', z.string());
308
+ await expect(result).rejects.toThrow('Incorrect rowCount: 0');
309
+ });
310
+
311
+ it('rejects results with multiple rows', async () => {
312
+ const result = queryScalar('SELECT id FROM workspaces;', z.string());
313
+ await expect(result).rejects.toThrow('Incorrect rowCount: 100');
314
+ });
315
+
316
+ it('rejects multi-column queries', async () => {
317
+ const result = queryScalar('SELECT * FROM workspaces WHERE id = 1;', z.string());
318
+ await expect(result).rejects.toThrow('Expected exactly one column');
319
+ });
320
+ });
321
+
322
+ describe('queryOptionalScalar', () => {
323
+ it('returns a scalar value when present', async () => {
324
+ const id = await queryOptionalScalar('SELECT id FROM workspaces WHERE id = 1;', z.string());
325
+ assert.equal(id, '1');
326
+ });
327
+
328
+ it('handles parameters', async () => {
329
+ const id = await queryOptionalScalar(
330
+ 'SELECT id FROM workspaces WHERE id = $1;',
331
+ [1],
332
+ z.string(),
333
+ );
334
+ assert.equal(id, '1');
335
+ });
336
+
337
+ it('returns null for zero rows', async () => {
338
+ const id = await queryOptionalScalar('SELECT id FROM workspaces WHERE id = -1;', z.string());
339
+ assert.isNull(id);
340
+ });
341
+
342
+ it('rejects results with multiple rows', async () => {
343
+ const result = queryOptionalScalar('SELECT id FROM workspaces;', z.string());
344
+ await expect(result).rejects.toThrow('Incorrect rowCount: 100');
345
+ });
346
+
347
+ it('rejects multi-column queries', async () => {
348
+ const result = queryOptionalScalar('SELECT * FROM workspaces WHERE id = 1;', z.string());
349
+ await expect(result).rejects.toThrow('Expected exactly one column');
350
+ });
351
+ });
352
+
353
+ describe('callScalars', () => {
354
+ it('returns all scalar values', async () => {
355
+ const ids = await callScalars('test_sproc_one_column', [10], z.string());
356
+ assert.lengthOf(ids, 10);
357
+ assert.equal(ids[0], '1');
358
+ });
359
+
360
+ it('handles no parameters', async () => {
361
+ const ids = await callScalars('test_sproc_one_column_ten_rows', z.string());
362
+ assert.lengthOf(ids, 10);
363
+ });
364
+
365
+ it('rejects multi-column sprocs', async () => {
366
+ const result = callScalars('test_sproc_two_columns', [10], z.string());
367
+ await expect(result).rejects.toThrow('Expected exactly one column');
368
+ });
369
+ });
370
+
371
+ describe('callScalar', () => {
372
+ it('returns a single scalar value', async () => {
373
+ const id = await callScalar('test_sproc_one_column_one_row', z.string());
374
+ assert.equal(id, '1');
375
+ });
376
+
377
+ it('handles parameters', async () => {
378
+ const id = await callScalar('test_sproc_one_column', [1], z.string());
379
+ assert.equal(id, '1');
380
+ });
381
+
382
+ it('rejects results with zero rows', async () => {
383
+ const result = callScalar('test_sproc_one_column', [0], z.string());
384
+ await expect(result).rejects.toThrow('Incorrect rowCount: 0');
385
+ });
386
+
387
+ it('rejects results with multiple rows', async () => {
388
+ const result = callScalar('test_sproc_one_column', [100], z.string());
389
+ await expect(result).rejects.toThrow('Incorrect rowCount: 100');
390
+ });
391
+
392
+ it('rejects multi-column sprocs', async () => {
393
+ const result = callScalar('test_sproc_two_columns', [1], z.string());
394
+ await expect(result).rejects.toThrow('Expected exactly one column');
395
+ });
396
+ });
397
+
398
+ describe('callOptionalScalar', () => {
399
+ it('returns a scalar value when present', async () => {
400
+ const id = await callOptionalScalar('test_sproc_one_column_one_row', z.string());
401
+ assert.equal(id, '1');
402
+ });
403
+
404
+ it('handles parameters', async () => {
405
+ const id = await callOptionalScalar('test_sproc_one_column', [1], z.string());
406
+ assert.equal(id, '1');
407
+ });
408
+
409
+ it('returns null for zero rows', async () => {
410
+ const id = await callOptionalScalar('test_sproc_one_column', [0], z.string());
411
+ assert.isNull(id);
412
+ });
413
+
414
+ it('rejects results with multiple rows', async () => {
415
+ const result = callOptionalScalar('test_sproc_one_column', [100], z.string());
416
+ await expect(result).rejects.toThrow('Incorrect rowCount: 100');
417
+ });
418
+
419
+ it('rejects multi-column sprocs', async () => {
420
+ const result = callOptionalScalar('test_sproc_two_columns', [1], z.string());
421
+ await expect(result).rejects.toThrow('Expected exactly one column');
422
+ });
423
+ });
424
+
252
425
  describe('queryCursor', () => {
253
426
  it('handles single column', async () => {
254
- const cursor = await queryCursor('SELECT id FROM workspaces WHERE id = 1;', z.string());
255
- const allRows: string[] = [];
427
+ const cursor = await queryCursor(
428
+ 'SELECT id FROM workspaces WHERE id = 1;',
429
+ z.object({ id: z.string() }),
430
+ );
431
+ const allRows: { id: string }[] = [];
256
432
  for await (const rows of cursor.iterate(10)) {
257
433
  allRows.push(...rows);
258
434
  }
259
- assert.equal(allRows[0], '1');
435
+ assert.equal(allRows[0].id, '1');
260
436
  });
261
437
 
262
438
  it('handles multiple columns', async () => {