kern-orm 0.7.0 → 0.9.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.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { B as noOrder, C as toNode, D as isType, E as defineType, F as createNode, L as falseNode, N as defineGenericOperator, P as createList, S as toFunctionOperandNode, T as getBooleanType, U as trueNode, _ as isOperandSource, b as toCastValueNode, c as textOperators, d as orderingOperators, f as compare, g as isExpression, h as getType, k as equalityOperators, v as operandNode, x as toExpression, y as toBooleanExpression } from "./text-CIUxPWPl.mjs";
2
- import { A as not, C as countType, D as literal, E as toAggregateType, M as combineNodes, O as and, S as countProjection, T as createCountCall, _ as createTableFactory, a as defineSchema, b as RequiredRelationError, c as requireDerived, d as desc, f as orderBy, g as createStatement, h as one, i as sql, j as or, k as isColumnValues, l as addTerms, m as many, n as identifier, o as SelectQuery, p as matchColumn, r as param, s as from, t as createDatabase, u as asc, v as defineTable, w as createAggregateCall, x as ResultDecodeError, y as NotFoundError } from "./database-B7Kf5Q9X.mjs";
2
+ import { A as toAggregateType, C as NotFoundError, D as countType, E as countProjection, F as or, I as combineNodes, M as and, N as isColumnValues, O as createAggregateCall, P as not, S as defineTable, T as ResultDecodeError, _ as matchColumn, a as toDriverError, b as createStatement, c as sql, d as from, f as requireDerived, g as orderBy, h as desc, i as QueryError, j as literal, k as createCountCall, l as defineSchema, m as asc, n as TransactionConnectionError, o as identifier, p as addTerms, r as ConnectionError, s as param, t as createDatabase, u as SelectQuery, v as many, w as RequiredRelationError, x as createTableFactory, y as one } from "./database-t66EaIGE.mjs";
3
3
  // src/expression/constants.ts
4
4
  /** Condition that always holds, such as for a branch of `and` built in code: `and(check, isAdmin ? TRUE : other)`. */
5
5
  const TRUE = /* @__PURE__ */ toExpression(trueNode, /* @__PURE__ */ getBooleanType());
@@ -127,6 +127,277 @@ function readFilter(options, method) {
127
127
  if (!isOperandSource(filter)) throw new TypeError(`The filter of ${method} must be an expression or a query of one Boolean value`);
128
128
  return filter[operandNode]();
129
129
  }
130
+ // src/execution/error-condition-codes.ts
131
+ /**
132
+ * Condition names of the SQLSTATE codes of PostgreSQL errors, by code: the codes of type `E` with a condition name, which PL/pgSQL takes in `EXCEPTION WHEN`, under the headings of their classes.
133
+ *
134
+ * A code that ends in `000` is the condition of its class.
135
+ * A few names have two codes, such as `null_value_not_allowed`.
136
+ */
137
+ const conditionsByCode = {
138
+ "03000": "sql_statement_not_yet_complete",
139
+ "08000": "connection_exception",
140
+ "08003": "connection_does_not_exist",
141
+ "08006": "connection_failure",
142
+ "08001": "sqlclient_unable_to_establish_sqlconnection",
143
+ "08004": "sqlserver_rejected_establishment_of_sqlconnection",
144
+ "08007": "transaction_resolution_unknown",
145
+ "08P01": "protocol_violation",
146
+ "09000": "triggered_action_exception",
147
+ "0A000": "feature_not_supported",
148
+ "0B000": "invalid_transaction_initiation",
149
+ "0F000": "locator_exception",
150
+ "0F001": "invalid_locator_specification",
151
+ "0L000": "invalid_grantor",
152
+ "0LP01": "invalid_grant_operation",
153
+ "0P000": "invalid_role_specification",
154
+ "0Z000": "diagnostics_exception",
155
+ "0Z002": "stacked_diagnostics_accessed_without_active_handler",
156
+ "10608": "invalid_argument_for_xquery",
157
+ "20000": "case_not_found",
158
+ "21000": "cardinality_violation",
159
+ "22000": "data_exception",
160
+ "2202E": "array_subscript_error",
161
+ "22021": "character_not_in_repertoire",
162
+ "22008": "datetime_field_overflow",
163
+ "22012": "division_by_zero",
164
+ "22005": "error_in_assignment",
165
+ "2200B": "escape_character_conflict",
166
+ "22022": "indicator_overflow",
167
+ "22015": "interval_field_overflow",
168
+ "2201E": "invalid_argument_for_logarithm",
169
+ "22014": "invalid_argument_for_ntile_function",
170
+ "22016": "invalid_argument_for_nth_value_function",
171
+ "2201F": "invalid_argument_for_power_function",
172
+ "2201G": "invalid_argument_for_width_bucket_function",
173
+ "22018": "invalid_character_value_for_cast",
174
+ "22007": "invalid_datetime_format",
175
+ "22019": "invalid_escape_character",
176
+ "2200D": "invalid_escape_octet",
177
+ "22025": "invalid_escape_sequence",
178
+ "22P06": "nonstandard_use_of_escape_character",
179
+ "22010": "invalid_indicator_parameter_value",
180
+ "22023": "invalid_parameter_value",
181
+ "22013": "invalid_preceding_or_following_size",
182
+ "2201B": "invalid_regular_expression",
183
+ "2201W": "invalid_row_count_in_limit_clause",
184
+ "2201X": "invalid_row_count_in_result_offset_clause",
185
+ "2202H": "invalid_tablesample_argument",
186
+ "2202G": "invalid_tablesample_repeat",
187
+ "22009": "invalid_time_zone_displacement_value",
188
+ "2200C": "invalid_use_of_escape_character",
189
+ "2200G": "most_specific_type_mismatch",
190
+ "22004": "null_value_not_allowed",
191
+ "22002": "null_value_no_indicator_parameter",
192
+ "22003": "numeric_value_out_of_range",
193
+ "2200H": "sequence_generator_limit_exceeded",
194
+ "22026": "string_data_length_mismatch",
195
+ "22001": "string_data_right_truncation",
196
+ "22011": "substring_error",
197
+ "22027": "trim_error",
198
+ "22024": "unterminated_c_string",
199
+ "2200F": "zero_length_character_string",
200
+ "22P01": "floating_point_exception",
201
+ "22P02": "invalid_text_representation",
202
+ "22P03": "invalid_binary_representation",
203
+ "22P04": "bad_copy_file_format",
204
+ "22P05": "untranslatable_character",
205
+ "2200L": "not_an_xml_document",
206
+ "2200M": "invalid_xml_document",
207
+ "2200N": "invalid_xml_content",
208
+ "2200S": "invalid_xml_comment",
209
+ "2200T": "invalid_xml_processing_instruction",
210
+ "22030": "duplicate_json_object_key_value",
211
+ "22031": "invalid_argument_for_sql_json_datetime_function",
212
+ "22032": "invalid_json_text",
213
+ "22033": "invalid_sql_json_subscript",
214
+ "22034": "more_than_one_sql_json_item",
215
+ "22035": "no_sql_json_item",
216
+ "22036": "non_numeric_sql_json_item",
217
+ "22037": "non_unique_keys_in_a_json_object",
218
+ "22038": "singleton_sql_json_item_required",
219
+ "22039": "sql_json_array_not_found",
220
+ "2203A": "sql_json_member_not_found",
221
+ "2203B": "sql_json_number_not_found",
222
+ "2203C": "sql_json_object_not_found",
223
+ "2203D": "too_many_json_array_elements",
224
+ "2203E": "too_many_json_object_members",
225
+ "2203F": "sql_json_scalar_required",
226
+ "2203G": "sql_json_item_cannot_be_cast_to_target_type",
227
+ "23000": "integrity_constraint_violation",
228
+ "23001": "restrict_violation",
229
+ "23502": "not_null_violation",
230
+ "23503": "foreign_key_violation",
231
+ "23505": "unique_violation",
232
+ "23514": "check_violation",
233
+ "23P01": "exclusion_violation",
234
+ "24000": "invalid_cursor_state",
235
+ "25000": "invalid_transaction_state",
236
+ "25001": "active_sql_transaction",
237
+ "25002": "branch_transaction_already_active",
238
+ "25008": "held_cursor_requires_same_isolation_level",
239
+ "25003": "inappropriate_access_mode_for_branch_transaction",
240
+ "25004": "inappropriate_isolation_level_for_branch_transaction",
241
+ "25005": "no_active_sql_transaction_for_branch_transaction",
242
+ "25006": "read_only_sql_transaction",
243
+ "25007": "schema_and_data_statement_mixing_not_supported",
244
+ "25P01": "no_active_sql_transaction",
245
+ "25P02": "in_failed_sql_transaction",
246
+ "25P03": "idle_in_transaction_session_timeout",
247
+ "25P04": "transaction_timeout",
248
+ "26000": "invalid_sql_statement_name",
249
+ "27000": "triggered_data_change_violation",
250
+ "28000": "invalid_authorization_specification",
251
+ "28P01": "invalid_password",
252
+ "2B000": "dependent_privilege_descriptors_still_exist",
253
+ "2BP01": "dependent_objects_still_exist",
254
+ "2D000": "invalid_transaction_termination",
255
+ "2F000": "sql_routine_exception",
256
+ "2F005": "function_executed_no_return_statement",
257
+ "2F002": "modifying_sql_data_not_permitted",
258
+ "2F003": "prohibited_sql_statement_attempted",
259
+ "2F004": "reading_sql_data_not_permitted",
260
+ "34000": "invalid_cursor_name",
261
+ "38000": "external_routine_exception",
262
+ "38001": "containing_sql_not_permitted",
263
+ "38002": "modifying_sql_data_not_permitted",
264
+ "38003": "prohibited_sql_statement_attempted",
265
+ "38004": "reading_sql_data_not_permitted",
266
+ "39000": "external_routine_invocation_exception",
267
+ "39001": "invalid_sqlstate_returned",
268
+ "39004": "null_value_not_allowed",
269
+ "39P01": "trigger_protocol_violated",
270
+ "39P02": "srf_protocol_violated",
271
+ "39P03": "event_trigger_protocol_violated",
272
+ "3B000": "savepoint_exception",
273
+ "3B001": "invalid_savepoint_specification",
274
+ "3D000": "invalid_catalog_name",
275
+ "3F000": "invalid_schema_name",
276
+ "40000": "transaction_rollback",
277
+ "40002": "transaction_integrity_constraint_violation",
278
+ "40001": "serialization_failure",
279
+ "40003": "statement_completion_unknown",
280
+ "40P01": "deadlock_detected",
281
+ "42000": "syntax_error_or_access_rule_violation",
282
+ "42601": "syntax_error",
283
+ "42501": "insufficient_privilege",
284
+ "42846": "cannot_coerce",
285
+ "42803": "grouping_error",
286
+ "42P20": "windowing_error",
287
+ "42P19": "invalid_recursion",
288
+ "42830": "invalid_foreign_key",
289
+ "42602": "invalid_name",
290
+ "42622": "name_too_long",
291
+ "42939": "reserved_name",
292
+ "42804": "datatype_mismatch",
293
+ "42P18": "indeterminate_datatype",
294
+ "42P21": "collation_mismatch",
295
+ "42P22": "indeterminate_collation",
296
+ "42809": "wrong_object_type",
297
+ "428C9": "generated_always",
298
+ "42703": "undefined_column",
299
+ "42883": "undefined_function",
300
+ "42P01": "undefined_table",
301
+ "42P02": "undefined_parameter",
302
+ "42704": "undefined_object",
303
+ "42701": "duplicate_column",
304
+ "42P03": "duplicate_cursor",
305
+ "42P04": "duplicate_database",
306
+ "42723": "duplicate_function",
307
+ "42P05": "duplicate_prepared_statement",
308
+ "42P06": "duplicate_schema",
309
+ "42P07": "duplicate_table",
310
+ "42712": "duplicate_alias",
311
+ "42710": "duplicate_object",
312
+ "42702": "ambiguous_column",
313
+ "42725": "ambiguous_function",
314
+ "42P08": "ambiguous_parameter",
315
+ "42P09": "ambiguous_alias",
316
+ "42P10": "invalid_column_reference",
317
+ "42611": "invalid_column_definition",
318
+ "42P11": "invalid_cursor_definition",
319
+ "42P12": "invalid_database_definition",
320
+ "42P13": "invalid_function_definition",
321
+ "42P14": "invalid_prepared_statement_definition",
322
+ "42P15": "invalid_schema_definition",
323
+ "42P16": "invalid_table_definition",
324
+ "42P17": "invalid_object_definition",
325
+ "44000": "with_check_option_violation",
326
+ "53000": "insufficient_resources",
327
+ "53100": "disk_full",
328
+ "53200": "out_of_memory",
329
+ "53300": "too_many_connections",
330
+ "53400": "configuration_limit_exceeded",
331
+ "54000": "program_limit_exceeded",
332
+ "54001": "statement_too_complex",
333
+ "54011": "too_many_columns",
334
+ "54023": "too_many_arguments",
335
+ "55000": "object_not_in_prerequisite_state",
336
+ "55006": "object_in_use",
337
+ "55P02": "cant_change_runtime_param",
338
+ "55P03": "lock_not_available",
339
+ "55P04": "unsafe_new_enum_value_usage",
340
+ "57000": "operator_intervention",
341
+ "57014": "query_canceled",
342
+ "57P01": "admin_shutdown",
343
+ "57P02": "crash_shutdown",
344
+ "57P03": "cannot_connect_now",
345
+ "57P04": "database_dropped",
346
+ "57P05": "idle_session_timeout",
347
+ "58000": "system_error",
348
+ "58030": "io_error",
349
+ "58P01": "undefined_file",
350
+ "58P02": "duplicate_file",
351
+ "58P03": "file_name_too_long",
352
+ F0000: "config_file_error",
353
+ F0001: "lock_file_exists",
354
+ HV000: "fdw_error",
355
+ HV005: "fdw_column_name_not_found",
356
+ HV002: "fdw_dynamic_parameter_value_needed",
357
+ HV010: "fdw_function_sequence_error",
358
+ HV021: "fdw_inconsistent_descriptor_information",
359
+ HV024: "fdw_invalid_attribute_value",
360
+ HV007: "fdw_invalid_column_name",
361
+ HV008: "fdw_invalid_column_number",
362
+ HV004: "fdw_invalid_data_type",
363
+ HV006: "fdw_invalid_data_type_descriptors",
364
+ HV091: "fdw_invalid_descriptor_field_identifier",
365
+ HV00B: "fdw_invalid_handle",
366
+ HV00C: "fdw_invalid_option_index",
367
+ HV00D: "fdw_invalid_option_name",
368
+ HV090: "fdw_invalid_string_length_or_buffer_length",
369
+ HV00A: "fdw_invalid_string_format",
370
+ HV009: "fdw_invalid_use_of_null_pointer",
371
+ HV014: "fdw_too_many_handles",
372
+ HV001: "fdw_out_of_memory",
373
+ HV00P: "fdw_no_schemas",
374
+ HV00J: "fdw_option_name_not_found",
375
+ HV00K: "fdw_reply_handle",
376
+ HV00Q: "fdw_schema_not_found",
377
+ HV00R: "fdw_table_not_found",
378
+ HV00L: "fdw_unable_to_create_execution",
379
+ HV00M: "fdw_unable_to_create_reply",
380
+ HV00N: "fdw_unable_to_establish_connection",
381
+ P0000: "plpgsql_error",
382
+ P0001: "raise_exception",
383
+ P0002: "no_data_found",
384
+ P0003: "too_many_rows",
385
+ P0004: "assert_failure",
386
+ XX000: "internal_error",
387
+ XX001: "data_corrupted",
388
+ XX002: "index_corrupted"
389
+ };
390
+ // src/execution/error-condition.ts
391
+ /**
392
+ * Tells whether `error` is a `QueryError` of `condition`, including a `QueryError` of another copy of kern-orm.
393
+ *
394
+ * The condition of a class, such as `integrity_constraint_violation`, matches every error of its class, as in PL/pgSQL.
395
+ */
396
+ function isQueryError(error, condition) {
397
+ if (!(error instanceof QueryError)) return false;
398
+ const conditions = conditionsByCode;
399
+ return conditions[error.code] === condition || conditions[`${error.code.slice(0, 2)}000`] === condition;
400
+ }
130
401
  // src/query/reusable-expression.ts
131
402
  function defineExpression(_table, expression) {
132
403
  return expression;
@@ -157,4 +428,4 @@ const wordStart = /(?<=[\p{Ll}\p{N}])(?=\p{Lu})|(?<=\p{Lu})(?=\p{Lu}(?!s$)\p{Ll}
157
428
  function toSnakeCase(key) {
158
429
  return key.replace(wordStart, "_").toLowerCase();
159
430
  }
160
- export { FALSE, NotFoundError, RequiredRelationError, ResultDecodeError, TRUE, and, asc, avg, caseWhen, coalesce, compare, count, countDistinct, createDatabase, createStatement, createTableFactory, defineExpression, defineGenericOperator, defineHelper, defineSchema, defineTable, defineType, desc, equalityOperators, every, from, greatest, identifier, least, literal, many, matches, max, min, not, one, or, orderBy, orderingOperators, param, some, sql, stringAgg, sum, textOperators, toSnakeCase };
431
+ export { ConnectionError, FALSE, NotFoundError, QueryError, RequiredRelationError, ResultDecodeError, TRUE, TransactionConnectionError, and, asc, avg, caseWhen, coalesce, compare, count, countDistinct, createDatabase, createStatement, createTableFactory, defineExpression, defineGenericOperator, defineHelper, defineSchema, defineTable, defineType, desc, equalityOperators, every, from, greatest, identifier, isQueryError, least, literal, many, matches, max, min, not, one, or, orderBy, orderingOperators, param, some, sql, stringAgg, sum, textOperators, toDriverError, toSnakeCase };
@@ -1,4 +1,4 @@
1
- import { B as PendingTestFixture, G as createTestHelpers, H as TestHelpers, Ni as Driver, U as TestHooks, V as TestFixture, W as TestTransactionOptions, z as OpenedTestFixture } from "./index-CLKi9gZL.mjs";
1
+ import { $ as TestHooks, J as OpenedTestFixture, Q as TestHelpers, Ui as Driver, X as TestFixture, Y as PendingTestFixture, Z as TestFixtureOptions, et as TestTransactionOptions, tt as createTestHelpers } from "./index-2z5iTzXf.mjs";
2
2
  /** Options of `describeDriverContract`: how to create the driver under test. */
3
3
  interface DriverContractOptions {
4
4
  /** Creates a driver over a new client of the test database with `max` connections. */
@@ -7,8 +7,6 @@ interface DriverContractOptions {
7
7
  }): Driver;
8
8
  /** Creates a driver over a new client of the test database whose parsers of result values throw; for a client without parsers, the same as `createDriver`. */
9
9
  createDriverWithFailingParsers(): Driver;
10
- /** Whether the client can close a reserved connection, so that the driver gets rid of a connection left in a transaction or lost; `false` leaves out the tests of it. */
11
- closesReservedConnections: boolean;
12
10
  }
13
11
  /**
14
12
  * Functions of a test runner that declare the tests of `describeDriverContract`.
@@ -25,4 +23,4 @@ interface DriverContractRunner extends TestHooks {
25
23
  * They run in the empty database of its clients and leave it unchanged.
26
24
  */
27
25
  export declare function describeDriverContract(options: DriverContractOptions, runner: DriverContractRunner): void;
28
- export { type DriverContractOptions, type DriverContractRunner, type OpenedTestFixture, type PendingTestFixture, type TestFixture, type TestHelpers, type TestHooks, type TestTransactionOptions, createTestHelpers };
26
+ export { type DriverContractOptions, type DriverContractRunner, type OpenedTestFixture, type PendingTestFixture, type TestFixture, type TestFixtureOptions, type TestHelpers, type TestHooks, type TestTransactionOptions, createTestHelpers };
package/dist/testing.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as text, t as char } from "./text-CIUxPWPl.mjs";
2
- import { O as and, a as defineSchema, g as createStatement, i as sql, r as param, t as createDatabase, v as defineTable } from "./database-B7Kf5Q9X.mjs";
2
+ import { M as and, S as defineTable, b as createStatement, c as sql, i as QueryError, l as defineSchema, n as TransactionConnectionError, r as ConnectionError, s as param, t as createDatabase } from "./database-t66EaIGE.mjs";
3
3
  import { _ as timestamptz, a as numeric, b as array, d as jsonb, f as enumOf, g as timestamp, h as time, i as integer, l as inet, m as interval, n as bigint, p as date, r as doublePrecision, t as uuid, v as boolean, y as bytea } from "./types-D3d35qR2.mjs";
4
4
  import assert from "node:assert/strict";
5
5
  // src/execution/testing.ts
@@ -9,11 +9,11 @@ function createTestHelpers(database, hooks) {
9
9
  if (!coordinator.test) throw new Error("createTestHelpers requires a database created with test: true");
10
10
  return {
11
11
  testTransaction: (callback, options) => coordinator.testTransaction(callback, options),
12
- useTestFixture(seed) {
12
+ useTestFixture(seed, options) {
13
13
  let pending;
14
14
  let fixture;
15
15
  hooks.before(async () => {
16
- const current = coordinator.startTestFixture(seed);
16
+ const current = coordinator.startTestFixture(seed, options);
17
17
  pending = current;
18
18
  try {
19
19
  const opened = await current.fixture;
@@ -36,14 +36,14 @@ function createTestHelpers(database, hooks) {
36
36
  };
37
37
  }
38
38
  // src/execution/driver-contract.ts
39
- /** Creates helpers that run raw statements on `database`. */
40
- function createStatementRunners(database) {
39
+ /** Creates helpers that run raw statements on `target`: a database, a driver, or a reserved connection. */
40
+ function createStatementRunners(target) {
41
41
  return {
42
42
  async runStatement(text) {
43
- await database.execute(createStatement(text));
43
+ await target.execute(createStatement(text));
44
44
  },
45
45
  async selectValue(text, parameters = []) {
46
- const { rows } = await database.execute(createStatement(text, parameters));
46
+ const { rows } = await target.execute(createStatement(text, parameters));
47
47
  return rows[0][0];
48
48
  }
49
49
  };
@@ -54,12 +54,14 @@ function createStatementRunners(database) {
54
54
  * They run in the empty database of its clients and leave it unchanged.
55
55
  */
56
56
  function describeDriverContract(options, runner) {
57
- const { createDriver, createDriverWithFailingParsers, closesReservedConnections } = options;
57
+ const { createDriver, createDriverWithFailingParsers } = options;
58
58
  const { describe, it, before, after } = runner;
59
59
  describe("driver contract", () => {
60
- const database = createDatabase({ driver: createDriver({ max: 2 }) });
60
+ const driver = createDriver({ max: 2 });
61
+ const database = createDatabase({ driver });
61
62
  after(() => database.close());
62
63
  const { runStatement, selectValue } = createStatementRunners(database);
64
+ const { selectValue: selectValueOnDriver } = createStatementRunners(driver);
63
65
  it("executes a query in a transaction", async () => {
64
66
  const tag = defineTable("pg_tag_test", {
65
67
  id: integer,
@@ -91,6 +93,18 @@ function describeDriverContract(options, runner) {
91
93
  await database.script("SET LOCAL client_min_messages = warning; DROP TABLE IF EXISTS pg_statement_test");
92
94
  }
93
95
  });
96
+ it("returns every value as text", async () => {
97
+ const { rows } = await driver.execute(createStatement(`SELECT 42::integer, 12345678901234::bigint, true, '{"a":1}'::jsonb, NULL::text, ARRAY['a', 'b'], '2030-01-01 12:00'::timestamp`));
98
+ assert.deepEqual(rows, [[
99
+ "42",
100
+ "12345678901234",
101
+ "t",
102
+ "{\"a\": 1}",
103
+ null,
104
+ "{a,b}",
105
+ "2030-01-01 12:00:00"
106
+ ]]);
107
+ });
94
108
  it("keeps the values of a statement as text without the parsers of the client", async () => {
95
109
  const parsed = createDatabase({ driver: createDriverWithFailingParsers() });
96
110
  try {
@@ -110,7 +124,7 @@ function describeDriverContract(options, runner) {
110
124
  });
111
125
  it("executes a script on the pool in one implicit transaction", async () => {
112
126
  try {
113
- await assert.rejects(database.script("CREATE TABLE pg_script_pool_test (id integer); SELECT 1/0"), /division by zero/);
127
+ await assert.rejects(database.script("CREATE TABLE pg_script_pool_test (id integer); SELECT 1/0"), (error) => error instanceof QueryError && error.code === "22012");
114
128
  assert.equal(await selectValue("SELECT to_regclass('pg_script_pool_test')::text"), null);
115
129
  } finally {
116
130
  await database.script("SET LOCAL client_min_messages = warning; DROP TABLE IF EXISTS pg_script_pool_test");
@@ -156,6 +170,10 @@ function describeDriverContract(options, runner) {
156
170
  assert.equal(await selectValue("SELECT $1", ["text"]), "text");
157
171
  assert.equal(await selectValue("SELECT $1::text IS NULL", [null]), "t");
158
172
  });
173
+ it("sends a parameter as it is, whatever type PostgreSQL describes for it", async () => {
174
+ assert.equal(await selectValueOnDriver("SELECT $1::boolean", ["true"]), "t");
175
+ assert.equal(await selectValueOnDriver("SELECT $1::jsonb ->> 'a'", ["{\"a\":\"x\"}"]), "x");
176
+ });
159
177
  it("runs concurrent statements of a transaction in the order of calls", async () => {
160
178
  const values = await database.transaction(() => Promise.all([
161
179
  selectValue("SELECT set_config('kern.test', '1', true) FROM pg_sleep(0.05)"),
@@ -170,6 +188,28 @@ function describeDriverContract(options, runner) {
170
188
  "2"
171
189
  ]);
172
190
  });
191
+ it("runs the statements sent on a reserved connection before ROLLBACK inside its transaction", async () => {
192
+ const own = createDriver({ max: 1 });
193
+ try {
194
+ const connection = await own.acquire();
195
+ const { runStatement, selectValue } = createStatementRunners(connection);
196
+ try {
197
+ await runStatement("CREATE TEMP TABLE pg_order_test (id integer)");
198
+ await runStatement("BEGIN");
199
+ await Promise.all([
200
+ runStatement("INSERT INTO pg_order_test VALUES (1)"),
201
+ runStatement("SELECT pg_sleep(0.05)"),
202
+ runStatement("INSERT INTO pg_order_test VALUES (2)"),
203
+ runStatement("ROLLBACK")
204
+ ]);
205
+ assert.equal(await selectValue("SELECT count(*) FROM pg_order_test"), "0");
206
+ } finally {
207
+ await connection.release();
208
+ }
209
+ } finally {
210
+ await own.close();
211
+ }
212
+ });
173
213
  it("decodes selected expressions with their types", async () => {
174
214
  const tag = defineTable("pg_tag_test", {
175
215
  id: integer,
@@ -201,6 +241,33 @@ function describeDriverContract(options, runner) {
201
241
  length: null
202
242
  }]);
203
243
  });
244
+ it("rejects a statement that PostgreSQL rejected with QueryError and keeps its connection, on the pool and on a reserved connection", async () => {
245
+ const own = createDriver({ max: 1 });
246
+ try {
247
+ const pool = createStatementRunners(own);
248
+ await pool.runStatement("CREATE TEMP TABLE pg_unique_test (id integer CONSTRAINT pg_unique_test_key UNIQUE)");
249
+ await pool.runStatement("INSERT INTO pg_unique_test VALUES (1)");
250
+ const pid = await pool.selectValue("SELECT pg_backend_pid()");
251
+ /** Asserts that inserting a duplicate on `target` rejects with the violated constraint, and that its connection stays the same. */
252
+ async function assertRejected(target) {
253
+ const runners = createStatementRunners(target);
254
+ const error = await runners.runStatement("INSERT INTO pg_unique_test VALUES (1)").catch((error) => error);
255
+ assert.ok(error instanceof QueryError, `Expected QueryError, got ${String(error)}`);
256
+ assert.equal(error.code, "23505");
257
+ assert.equal(error.constraint, "pg_unique_test_key");
258
+ assert.equal(await runners.selectValue("SELECT pg_backend_pid()"), pid);
259
+ }
260
+ await assertRejected(own);
261
+ const connection = await own.acquire();
262
+ try {
263
+ await assertRejected(connection);
264
+ } finally {
265
+ await connection.release();
266
+ }
267
+ } finally {
268
+ await own.close();
269
+ }
270
+ });
204
271
  it("rejects a transaction whose failed statement the callback caught", async () => {
205
272
  const transaction = database.transaction(async () => {
206
273
  await runStatement("SELECT 1/0").catch(() => {});
@@ -221,47 +288,119 @@ function describeDriverContract(options, runner) {
221
288
  await single.close();
222
289
  }
223
290
  });
224
- it("leaves no failed transaction on the pool after a release with its error", async () => {
225
- const driver = createDriver({ max: 1 });
291
+ it("returns a connection to the pool after PostgreSQL rejected its last statement outside a transaction", async () => {
292
+ const own = createDriver({ max: 1 });
226
293
  try {
227
- const connection = await driver.acquire();
228
- await connection.execute(createStatement("BEGIN"));
229
- const error = await connection.execute(createStatement("SELECT 1/0")).catch((error) => error);
230
- await connection.release({ error });
231
- await assert.rejects(driver.execute(createStatement("SAVEPOINT probe")), /transaction blocks/);
294
+ const connection = await own.acquire();
295
+ let pid;
296
+ try {
297
+ pid = await createStatementRunners(connection).selectValue("SELECT pg_backend_pid()");
298
+ await assert.rejects(connection.execute(createStatement("SELECT 1/0")), QueryError);
299
+ } finally {
300
+ await connection.release();
301
+ }
302
+ assert.equal(await createStatementRunners(own).selectValue("SELECT pg_backend_pid()"), pid);
232
303
  } finally {
233
- await driver.close();
304
+ await own.close();
234
305
  }
235
306
  });
236
- if (closesReservedConnections) {
237
- for (const [state, text] of [["an open", "SELECT 1"], ["a failed", "SELECT 1/0"]]) it(`does not return a connection in ${state} transaction to the pool`, async () => {
238
- const driver = createDriver({ max: 1 });
307
+ for (const [state, text] of [["an open", "SELECT 1"], ["a failed", "SELECT 1/0"]]) it(`does not return a connection in ${state} transaction to the pool`, async () => {
308
+ const own = createDriver({ max: 1 });
309
+ try {
310
+ const connection = await own.acquire();
239
311
  try {
240
- const connection = await driver.acquire();
241
312
  await connection.execute(createStatement("BEGIN"));
242
313
  await connection.execute(createStatement(text)).catch(() => {});
243
- await connection.release({ error: /* @__PURE__ */ new Error("failure") });
244
- await assert.rejects(driver.execute(createStatement("SAVEPOINT probe")), /transaction blocks/);
245
314
  } finally {
246
- await driver.close();
315
+ await connection.release();
247
316
  }
248
- });
249
- it("rejects a transaction whose connection is lost and keeps the pool usable", async () => {
250
- const single = createDatabase({ driver: createDriver({ max: 1 }) });
251
- const { selectValue: selectValueOnSingle } = createStatementRunners(single);
317
+ await assert.rejects(own.execute(createStatement("SAVEPOINT probe")), /transaction blocks/);
318
+ } finally {
319
+ await own.close();
320
+ }
321
+ });
322
+ describe("a statement on the pool that leaves a transaction open", () => {
323
+ const cases = [
324
+ [
325
+ "a statement",
326
+ (own) => own.execute(createStatement("BEGIN")),
327
+ ConnectionError
328
+ ],
329
+ [
330
+ "a script",
331
+ (own) => own.executeScript("BEGIN; SELECT 1"),
332
+ ConnectionError
333
+ ],
334
+ [
335
+ "a script that fails in the transaction",
336
+ (own) => own.executeScript("BEGIN; SELECT 1/0"),
337
+ Error
338
+ ]
339
+ ];
340
+ for (const [name, run, rejection] of cases) it(`rejects ${name} and leaves no transaction on the pool`, async () => {
341
+ const own = createDriver({ max: 2 });
342
+ const held = [];
252
343
  try {
253
- const transaction = single.transaction(async () => {
254
- const pid = await selectValueOnSingle("SELECT pg_backend_pid()");
255
- await selectValue("SELECT pg_terminate_backend($1)", [pid]);
256
- await selectValueOnSingle("SELECT 1");
257
- });
258
- await assert.rejects(transaction, /Transaction rollback failed/);
259
- assert.equal(await single.transaction(() => selectValueOnSingle("SELECT 'usable'")), "usable");
344
+ await assert.rejects(run(own), rejection);
345
+ for (let count = 0; count < 2; count++) held.push(await own.acquire());
346
+ for (const connection of held) await assert.rejects(connection.execute(createStatement("SAVEPOINT probe")), /transaction blocks/);
260
347
  } finally {
261
- await single.close();
348
+ await Promise.allSettled(held.map(async (connection) => connection.release()));
349
+ await own.close();
262
350
  }
263
351
  });
264
- }
352
+ });
353
+ it("rejects a transaction whose connection is lost and keeps the pool usable", async () => {
354
+ const single = createDatabase({ driver: createDriver({ max: 1 }) });
355
+ const { selectValue: selectValueOnSingle } = createStatementRunners(single);
356
+ try {
357
+ const transaction = single.transaction(async () => {
358
+ const pid = await selectValueOnSingle("SELECT pg_backend_pid()");
359
+ await selectValue("SELECT pg_terminate_backend($1)", [pid]);
360
+ await selectValueOnSingle("SELECT 1");
361
+ });
362
+ await assert.rejects(transaction, (error) => error instanceof TransactionConnectionError && error.phase === "transaction");
363
+ assert.equal(await single.transaction(() => selectValueOnSingle("SELECT 'usable'")), "usable");
364
+ } finally {
365
+ await single.close();
366
+ }
367
+ });
368
+ it("rejects a statement of a transaction whose connection is lost with ConnectionError", async () => {
369
+ const own = createDriver({ max: 1 });
370
+ try {
371
+ const connection = await own.acquire();
372
+ const { runStatement, selectValue: selectValueOnConnection } = createStatementRunners(connection);
373
+ try {
374
+ await runStatement("BEGIN");
375
+ await runStatement("CREATE TEMP TABLE pg_lost_test (id integer) ON COMMIT DROP");
376
+ const pid = await selectValueOnConnection("SELECT pg_backend_pid()");
377
+ assert.equal(await selectValue("SELECT pg_terminate_backend($1, 5000)", [pid]), "t");
378
+ await new Promise((resolve) => setTimeout(resolve, 50));
379
+ const error = await runStatement("INSERT INTO pg_lost_test VALUES (1)").catch((error) => error);
380
+ assert.ok(error instanceof ConnectionError, `Expected ConnectionError, got ${String(error)}`);
381
+ } finally {
382
+ await connection.release();
383
+ }
384
+ } finally {
385
+ await own.close();
386
+ }
387
+ });
388
+ it("replaces an idle connection whose backend exited", async () => {
389
+ const own = createDriver({ max: 2 });
390
+ const held = [];
391
+ try {
392
+ for (let count = 0; count < 2; count++) held.push(await own.acquire());
393
+ const pids = [];
394
+ for (const connection of held) pids.push(await createStatementRunners(connection).selectValue("SELECT pg_backend_pid()"));
395
+ while (held.length > 0) await held.shift().release();
396
+ for (const pid of pids) assert.equal(await selectValue("SELECT pg_terminate_backend($1, 5000)", [pid]), "t");
397
+ await new Promise((resolve) => setTimeout(resolve, 50));
398
+ assert.equal(await createStatementRunners(own).selectValue("SELECT 'usable'"), "usable");
399
+ } finally {
400
+ await Promise.allSettled(held.map(async (connection) => connection.release()));
401
+ await own.close();
402
+ }
403
+ });
265
404
  it("isolates concurrent transactions on their own connections", async () => {
266
405
  const bothStarted = Promise.withResolvers();
267
406
  let startedCount = 0;
@@ -353,15 +492,15 @@ function describeDriverContract(options, runner) {
353
492
  assert.equal(await query, "slept");
354
493
  assert.deepEqual(settled, ["query", "database"]);
355
494
  });
356
- it("rejects a statement, a script, and a connection requested right after it starts", async () => {
495
+ it("rejects a statement, a script, and a connection requested right after it starts with ConnectionError", async () => {
357
496
  const driver = createDriver({ max: 1 });
358
497
  const held = await driver.acquire();
359
498
  const closing = driver.close();
360
499
  const acquiring = driver.acquire();
361
500
  const rejections = [
362
- assert.rejects(driver.execute(createStatement("SELECT 1"))),
363
- assert.rejects(driver.executeScript("SELECT 1")),
364
- assert.rejects(acquiring)
501
+ assert.rejects(driver.execute(createStatement("SELECT 1")), ConnectionError),
502
+ assert.rejects(driver.executeScript("SELECT 1"), ConnectionError),
503
+ assert.rejects(acquiring, ConnectionError)
365
504
  ];
366
505
  acquiring.then((connection) => connection.release()).catch(() => {});
367
506
  await held.release();
package/dist/types.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { Gr as InSignature, Hr as EqualityOperator, Jr as ComparisonSignature, S as AnyExpression, Ur as EqualityOperators, Vi as Type, Wr as HasEquality, _ as TextOperators, b as NumberTypeOperators, ea as MergeOperators, na as OperatorDefinitions, ra as GenericOperator, ta as NoOperators, v as TextTypeOperators, zi as BaseType } from "./index-CLKi9gZL.mjs";
1
+ import { $r as EqualityOperators, A as AnyExpression, E as TextTypeOperators, Ji as BaseType, O as NumberTypeOperators, Qr as EqualityOperator, T as TextOperators, Xi as Type, da as OperatorDefinitions, ei as HasEquality, fa as GenericOperator, ii as ComparisonSignature, la as MergeOperators, ti as InSignature, ua as NoOperators } from "./index-2z5iTzXf.mjs";
2
2
  /**
3
3
  * Creates a one-dimensional array type of `element` values.
4
4
  *