uql-orm 0.53.0 → 0.55.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.
Files changed (158) hide show
  1. package/README.md +2 -2
  2. package/dist/browser/uql-browser.min.js.map +2 -2
  3. package/dist/bunSql/bunSql.util.d.ts +3 -14
  4. package/dist/bunSql/bunSql.util.js +33 -56
  5. package/dist/bunSql/bunSqlQuerier.d.ts +3 -6
  6. package/dist/bunSql/bunSqlQuerier.js +7 -13
  7. package/dist/bunSql/bunSqlQuerierPool.d.ts +10 -5
  8. package/dist/bunSql/bunSqlQuerierPool.js +25 -10
  9. package/dist/cockroachdb/cockroachDialect.d.ts +2 -5
  10. package/dist/cockroachdb/cockroachDialect.js +2 -5
  11. package/dist/cockroachdb/crdbQuerierPool.d.ts +1 -3
  12. package/dist/cockroachdb/crdbQuerierPool.js +0 -4
  13. package/dist/cockroachdb/index.d.ts +0 -1
  14. package/dist/cockroachdb/index.js +0 -1
  15. package/dist/d1/d1SqliteDialect.d.ts +5 -0
  16. package/dist/d1/d1SqliteDialect.js +7 -0
  17. package/dist/dialect/abstractSqlDialect.d.ts +15 -9
  18. package/dist/dialect/abstractSqlDialect.js +47 -54
  19. package/dist/dialect/hydrateColumn.js +2 -2
  20. package/dist/dialect/mergeSqlDialect.d.ts +2 -2
  21. package/dist/dialect/mergeSqlDialect.js +0 -4
  22. package/dist/dialect/mysqlLikeSqlDialect.d.ts +0 -3
  23. package/dist/dialect/mysqlLikeSqlDialect.js +0 -3
  24. package/dist/dialect/pgLikeSqlDialect.d.ts +5 -0
  25. package/dist/dialect/pgLikeSqlDialect.js +12 -2
  26. package/dist/entity/decorator/members.d.ts +3 -10
  27. package/dist/entity/index.d.ts +1 -1
  28. package/dist/entity/index.js +1 -1
  29. package/dist/entity/metadata/definition.d.ts +3 -1
  30. package/dist/entity/metadata/definition.js +8 -4
  31. package/dist/libsql/index.d.ts +0 -1
  32. package/dist/libsql/index.js +0 -1
  33. package/dist/libsql/libsqlQuerierPool.d.ts +3 -5
  34. package/dist/libsql/libsqlQuerierPool.js +2 -5
  35. package/dist/maria/mariadbQuerier.d.ts +0 -3
  36. package/dist/maria/mariadbQuerier.js +6 -7
  37. package/dist/maria/mariadbQuerierPool.js +4 -7
  38. package/dist/migrate/builder/migrationBuilder.js +0 -4
  39. package/dist/migrate/ddl/index.d.ts +3 -3
  40. package/dist/migrate/ddl/index.js +3 -3
  41. package/dist/migrate/migrator.d.ts +3 -7
  42. package/dist/migrate/migrator.js +3 -7
  43. package/dist/migrate/schemaGenerator.d.ts +0 -13
  44. package/dist/migrate/schemaGenerator.js +0 -13
  45. package/dist/migrate/storage/databaseStorage.d.ts +2 -2
  46. package/dist/migrate/storage/databaseStorage.js +2 -2
  47. package/dist/mongo/index.d.ts +0 -1
  48. package/dist/mongo/index.js +0 -1
  49. package/dist/mongo/mongoDialect.d.ts +0 -1
  50. package/dist/mongo/mongoDialect.js +3 -14
  51. package/dist/mongo/mongodbQuerier.d.ts +3 -5
  52. package/dist/mongo/mongodbQuerier.js +23 -23
  53. package/dist/mongo/mongodbQuerierPool.d.ts +2 -2
  54. package/dist/mongo/mongodbQuerierPool.js +2 -2
  55. package/dist/mssql/mssqlDialect.d.ts +5 -5
  56. package/dist/mssql/mssqlDialect.js +11 -8
  57. package/dist/mssql/mssqlQuerier.d.ts +13 -6
  58. package/dist/mssql/mssqlQuerier.js +30 -75
  59. package/dist/mssql/mssqlQuerierPool.js +2 -0
  60. package/dist/mssql/mssqlWireTypes.d.ts +3 -4
  61. package/dist/mssql/mssqlWireTypes.js +5 -8
  62. package/dist/mysql/index.d.ts +0 -1
  63. package/dist/mysql/index.js +0 -1
  64. package/dist/mysql/mysql2Querier.d.ts +1 -4
  65. package/dist/mysql/mysql2Querier.js +0 -3
  66. package/dist/mysql/mysql2QuerierPool.d.ts +2 -2
  67. package/dist/mysql/mysql2QuerierPool.js +5 -3
  68. package/dist/neon/index.d.ts +0 -2
  69. package/dist/neon/index.js +0 -2
  70. package/dist/neon/neonQuerierPool.d.ts +2 -4
  71. package/dist/neon/neonQuerierPool.js +2 -6
  72. package/dist/pglite/index.d.ts +0 -1
  73. package/dist/pglite/index.js +0 -1
  74. package/dist/pglite/pgliteQuerier.d.ts +3 -3
  75. package/dist/pglite/pgliteQuerier.js +1 -1
  76. package/dist/pglite/pgliteQuerierPool.d.ts +7 -2
  77. package/dist/pglite/pgliteQuerierPool.js +16 -6
  78. package/dist/postgres/abstractPgQuerierPool.d.ts +8 -12
  79. package/dist/postgres/abstractPgQuerierPool.js +8 -6
  80. package/dist/postgres/index.d.ts +0 -1
  81. package/dist/postgres/index.js +0 -1
  82. package/dist/postgres/pgNumericTypes.d.ts +5 -5
  83. package/dist/postgres/pgNumericTypes.js +11 -7
  84. package/dist/postgres/pgQuerier.d.ts +22 -4
  85. package/dist/postgres/pgQuerier.js +29 -2
  86. package/dist/postgres/pgQuerierPool.d.ts +2 -4
  87. package/dist/postgres/pgQuerierPool.js +2 -6
  88. package/dist/postgres/postgresDialect.d.ts +5 -5
  89. package/dist/postgres/postgresDialect.js +5 -5
  90. package/dist/postgres/postgresWireDriverCapabilities.d.ts +2 -2
  91. package/dist/postgres/postgresWireDriverCapabilities.js +2 -2
  92. package/dist/querier/abstractPoolQuerier.d.ts +1 -1
  93. package/dist/querier/abstractPoolQuerier.js +1 -1
  94. package/dist/querier/abstractQuerier.d.ts +17 -22
  95. package/dist/querier/abstractQuerier.js +80 -57
  96. package/dist/querier/abstractSqlQuerier.d.ts +20 -13
  97. package/dist/querier/abstractSqlQuerier.js +96 -100
  98. package/dist/schema/schemaASTBuilder.js +7 -7
  99. package/dist/sqlite/hranaQuerier.d.ts +6 -8
  100. package/dist/sqlite/hranaQuerier.js +13 -30
  101. package/dist/sqlite/hranaQuerierPool.d.ts +3 -4
  102. package/dist/sqlite/hranaQuerierPool.js +2 -1
  103. package/dist/sqlite/localSqliteQuerierPool.d.ts +3 -3
  104. package/dist/sqlite/localSqliteQuerierPool.js +4 -2
  105. package/dist/sqlite/nodeSqliteAdapter.d.ts +0 -1
  106. package/dist/sqlite/nodeSqliteQuerierPool.js +0 -2
  107. package/dist/sqlite/sqlitePragmas.d.ts +12 -0
  108. package/dist/sqlite/sqlitePragmas.js +15 -0
  109. package/dist/sqlite/sqliteQuerierPool.d.ts +0 -5
  110. package/dist/sqlite/sqliteQuerierPool.js +2 -13
  111. package/dist/turso/index.d.ts +0 -1
  112. package/dist/turso/index.js +0 -1
  113. package/dist/turso/tursoLocalQuerier.d.ts +0 -1
  114. package/dist/turso/tursoLocalQuerierPool.js +2 -2
  115. package/dist/turso/tursoQuerierPool.d.ts +1 -3
  116. package/dist/turso/tursoQuerierPool.js +0 -4
  117. package/dist/type/dialect.d.ts +1 -1
  118. package/dist/type/entity.d.ts +6 -11
  119. package/dist/type/migration.d.ts +0 -3
  120. package/dist/type/query.d.ts +12 -12
  121. package/dist/type/query.js +0 -6
  122. package/dist/type/universalQuerier.d.ts +3 -3
  123. package/dist/util/dialect.util.d.ts +4 -3
  124. package/dist/util/dialect.util.js +2 -1
  125. package/dist/util/field.util.d.ts +4 -16
  126. package/dist/util/field.util.js +6 -19
  127. package/dist/util/fieldOption.util.d.ts +1 -4
  128. package/dist/util/fieldOption.util.js +0 -2
  129. package/dist/util/logger.d.ts +10 -11
  130. package/dist/util/logger.js +21 -11
  131. package/dist/util/raw.d.ts +3 -10
  132. package/dist/util/raw.js +3 -3
  133. package/dist/util/sql.util.js +2 -2
  134. package/dist/util/sqlLiteral.js +3 -8
  135. package/dist/util/string.util.js +2 -6
  136. package/dist/util/wideNumber.d.ts +14 -0
  137. package/dist/util/wideNumber.js +24 -0
  138. package/package.json +1 -1
  139. package/dist/cockroachdb/crdbQuerier.d.ts +0 -8
  140. package/dist/cockroachdb/crdbQuerier.js +0 -6
  141. package/dist/libsql/libsqlQuerier.d.ts +0 -10
  142. package/dist/libsql/libsqlQuerier.js +0 -10
  143. package/dist/mongo/mongodbNativeDialect.d.ts +0 -9
  144. package/dist/mongo/mongodbNativeDialect.js +0 -9
  145. package/dist/mysql/mysql2Dialect.d.ts +0 -9
  146. package/dist/mysql/mysql2Dialect.js +0 -9
  147. package/dist/neon/neonDialect.d.ts +0 -10
  148. package/dist/neon/neonDialect.js +0 -10
  149. package/dist/neon/neonQuerier.d.ts +0 -5
  150. package/dist/neon/neonQuerier.js +0 -3
  151. package/dist/pglite/pgliteDialect.d.ts +0 -14
  152. package/dist/pglite/pgliteDialect.js +0 -14
  153. package/dist/postgres/abstractPgQuerier.d.ts +0 -24
  154. package/dist/postgres/abstractPgQuerier.js +0 -32
  155. package/dist/postgres/pgDialect.d.ts +0 -10
  156. package/dist/postgres/pgDialect.js +0 -10
  157. package/dist/turso/tursoQuerier.d.ts +0 -10
  158. package/dist/turso/tursoQuerier.js +0 -10
@@ -6,6 +6,11 @@ import { SqliteDialect } from '../sqlite/sqliteDialect.js';
6
6
  */
7
7
  export declare class D1SqliteDialect extends SqliteDialect {
8
8
  readonly maxBindValues: number;
9
+ /**
10
+ * D1's Worker API refuses a `bigint` bind ([workerd#4195](https://github.com/cloudflare/workerd/issues/4195)),
11
+ * so one goes as its exact text, which SQLite's INTEGER affinity stores as the same integer.
12
+ */
13
+ normalizeValue(value: unknown): unknown;
9
14
  /**
10
15
  * D1 loads no extensions (its allowlist is FTS5, JSON and the math functions) and has no vector
11
16
  * functions of its own, so the sqlite-vec names inherited from {@link SqliteDialect} would compile
@@ -7,6 +7,13 @@ import { SqliteDialect } from '../sqlite/sqliteDialect.js';
7
7
  export class D1SqliteDialect extends SqliteDialect {
8
8
  // Cloudflare D1 caps bound parameters at 100 per query.
9
9
  maxBindValues = 100;
10
+ /**
11
+ * D1's Worker API refuses a `bigint` bind ([workerd#4195](https://github.com/cloudflare/workerd/issues/4195)),
12
+ * so one goes as its exact text, which SQLite's INTEGER affinity stores as the same integer.
13
+ */
14
+ normalizeValue(value) {
15
+ return typeof value === 'bigint' ? String(value) : super.normalizeValue(value);
16
+ }
10
17
  /**
11
18
  * D1 loads no extensions (its allowlist is FTS5, JSON and the math functions) and has no vector
12
19
  * functions of its own, so the sqlite-vec names inherited from {@link SqliteDialect} would compile
@@ -84,6 +84,8 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
84
84
  * shared for the same reason - see {@link SqlQueryContext}.
85
85
  */
86
86
  protected buildFragment(ctx: QueryContext, build: QueryBuildFn): string;
87
+ /** A `raw()` operand, rendered in place: bound, it would reach the driver as the object itself. */
88
+ protected rawFragment(ctx: QueryContext, value: QueryRaw): string;
87
89
  /**
88
90
  * Each operand rendered into its own fragment, keeping only those that emitted SQL.
89
91
  *
@@ -95,10 +97,9 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
95
97
  protected renderOperands<T>(ctx: QueryContext, operands: readonly T[], render: (ctx: QueryContext, operand: T) => void): string[];
96
98
  addValue(values: unknown[], value: unknown): string;
97
99
  /**
98
- * Normalizes a parameter value for the database driver.
99
- * Handles bigint, boolean, and serializes plain objects/arrays to JSON strings.
100
- * Date values are preserved so SQL drivers can apply native date/time binding.
101
- * Postgres overrides to pass objects through to its native JSONB driver.
100
+ * A parameter value as this engine's driver takes it: a boolean as the integer an engine with no
101
+ * boolean type stores, and everything else as it is - a `bigint` included, which every driver here
102
+ * binds exactly, where a number would round it past 2^53. A driver that refuses one (D1) overrides.
102
103
  */
103
104
  normalizeValue(value: unknown): unknown;
104
105
  /**
@@ -354,10 +355,8 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
354
355
  * caller reached for this to avoid, and only say so by taking a long time.
355
356
  */
356
357
  estimatedCount<E>(_ctx: QueryContext, _entity: Type<E>): void;
357
- /** `$group` aggregate operator → SQL function name. An allowlist, not a formatter: the op key
358
- * comes from query data, so anything outside this map must be rejected rather than passed
359
- * through as a raw SQL function name. */
360
- private static readonly AGGREGATE_FN_MAP;
358
+ /** `$group` aggregate operator to SQL function name, over ops `resolveAggregateOp` has already allowlisted. */
359
+ private static readonly AGGREGATE_FN;
361
360
  aggregate<E, G extends QueryGroupMap<E>, A extends QueryAggMap<E>>(ctx: QueryContext, entity: Type<E>, q: QueryAggregate<E, G, A>, opts?: QueryOptions): void;
362
361
  /**
363
362
  * ORDER BY for aggregate queries - handles both entity-field and alias references. A grouped
@@ -388,6 +387,12 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
388
387
  * already built ends up, so the two ends cannot disagree.
389
388
  */
390
389
  readonly returningPosition: 'suffix' | 'after-target';
390
+ /**
391
+ * Whether a multi-row upsert's `RETURNING` lists its rows in payload order. Where it does not, the
392
+ * ids are read back by the conflict columns instead, since placing them in order would name the
393
+ * wrong rows.
394
+ */
395
+ readonly upsertReturningOrdered: boolean;
391
396
  /**
392
397
  * `INSERT INTO ... VALUES (...)` and nothing more. The upsert builders extend this rather than
393
398
  * {@link insert}: their own clause has to come before the `RETURNING`, not after it.
@@ -652,6 +657,7 @@ export declare abstract class AbstractSqlDialect extends VectorSqlDialect implem
652
657
  * Postgres overrides to use `= ANY($1)` / `<> ALL($1)` with a single array parameter.
653
658
  */
654
659
  protected formatIn(ctx: QueryContext, values: unknown[], negate: boolean): string;
655
- protected numericCast(expr: string): string;
660
+ /** Reads extracted JSON text as a number, which every engine spells its own way. */
661
+ protected abstract numericCast(expr: string): string;
656
662
  toString(): string;
657
663
  }
@@ -1,6 +1,6 @@
1
- import { getMeta, soleIdOf } from '../entity/index.js';
1
+ import { fieldOf, getMeta, soleIdOf } from '../entity/index.js';
2
2
  import { parseQueryLock, QueryRaw, RAW_ALIAS, RAW_VALUE, VECTOR_QUERY_KEYS, } from '../type/index.js';
3
- import { computedExpression, isInlinedExpression } from '../util/field.util.js';
3
+ import { isInlinedExpression } from '../util/field.util.js';
4
4
  import { asSelectMap, assertNonNegativeInteger, escapeSqlId, fillOnFields, filterFieldKeys, getInsertFieldKeys, getKeys, getSoftDeleteValue, hasKeys, columnFamily, isJsonUpdateOp, isOperatorMap, isOperatorObject, isOperatorOnlyObject, isVectorSearch, normalizeScalarFieldSelection, parentJoins, targetKeyColumns, parseGroupMap, parseRelationSize, parseSortByCount, populatesRelations, queryChildrenOf, raw, someValue, throwUnknownAggregateColumn, withoutSoftDeleteFilter, } from '../util/index.js';
5
5
  import { escapeAnsiSqlLiteral, escapeSingleQuotes } from '../util/sqlLiteral.js';
6
6
  import { COUNT_ALIAS, DISTINCT_DERIVED_ALIAS, JSON_ELEM_ALIAS_PREFIX, PER_PARENT_BRANCH_ALIAS } from './aliases.js';
@@ -10,6 +10,13 @@ import { SqlQueryContext } from './queryContext.js';
10
10
  import { NO_JOINS, resolveQueryJoins, resolveSortableJoin, } from './queryJoins.js';
11
11
  import { resolveVectorCast } from './vectorCast.js';
12
12
  import { VectorSqlDialect } from './vectorSqlDialect.js';
13
+ /** An `$in`/`$nin` operand, which the types require to be an array but `/http` hands over untyped. */
14
+ function inOperands(op, value) {
15
+ if (!Array.isArray(value)) {
16
+ throw TypeError(`${op} expects an array, got ${value === null ? 'null' : typeof value}`);
17
+ }
18
+ return value;
19
+ }
13
20
  export class AbstractSqlDialect extends VectorSqlDialect {
14
21
  /**
15
22
  * Whether {@link autoIncrementSuffix} states `PRIMARY KEY` itself, so the table must not state it again.
@@ -99,6 +106,10 @@ export class AbstractSqlDialect extends VectorSqlDialect {
99
106
  build(fragmentCtx);
100
107
  return fragmentCtx.sql;
101
108
  }
109
+ /** A `raw()` operand, rendered in place: bound, it would reach the driver as the object itself. */
110
+ rawFragment(ctx, value) {
111
+ return this.buildFragment(ctx, (fragmentCtx) => this.getRawValue(fragmentCtx, { value }));
112
+ }
102
113
  /**
103
114
  * Each operand rendered into its own fragment, keeping only those that emitted SQL.
104
115
  *
@@ -117,20 +128,13 @@ export class AbstractSqlDialect extends VectorSqlDialect {
117
128
  return this.placeholder(values.length);
118
129
  }
119
130
  /**
120
- * Normalizes a parameter value for the database driver.
121
- * Handles bigint, boolean, and serializes plain objects/arrays to JSON strings.
122
- * Date values are preserved so SQL drivers can apply native date/time binding.
123
- * Postgres overrides to pass objects through to its native JSONB driver.
131
+ * A parameter value as this engine's driver takes it: a boolean as the integer an engine with no
132
+ * boolean type stores, and everything else as it is - a `bigint` included, which every driver here
133
+ * binds exactly, where a number would round it past 2^53. A driver that refuses one (D1) overrides.
124
134
  */
125
135
  normalizeValue(value) {
126
- if (value == null || value instanceof Date || value instanceof Uint8Array || value instanceof QueryRaw) {
127
- return value;
128
- }
129
- if (typeof value === 'bigint') {
130
- return Number(value);
131
- }
132
- if (typeof value === 'boolean') {
133
- return this.booleanLiteral === 'native' ? value : value ? 1 : 0;
136
+ if (typeof value === 'boolean' && this.booleanLiteral !== 'native') {
137
+ return value ? 1 : 0;
134
138
  }
135
139
  return value;
136
140
  }
@@ -198,9 +202,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
198
202
  });
199
203
  }
200
204
  else {
201
- const field = meta.fields[key];
202
- if (!field)
203
- return;
205
+ const field = fieldOf(meta, key);
204
206
  if (isInlinedExpression(field)) {
205
207
  // Qualified even when nothing else in this statement is: the expression is spliced in, and
206
208
  // one that opens a correlated subquery has the inner table's columns in scope, so a bare
@@ -208,7 +210,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
208
210
  // is valid on every engine, so naming the table costs nothing where it is not needed.
209
211
  const qualified = opts.prefix ?? this.resolveTableAlias(meta);
210
212
  this.getRawValue(ctx, {
211
- value: computedExpression(field).as(key),
213
+ value: field.computed.as(key),
212
214
  prefix: qualified,
213
215
  escapedPrefix: this.escapeId(qualified, true, true),
214
216
  autoPrefixAlias: opts.autoPrefixAlias,
@@ -427,7 +429,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
427
429
  if (entry instanceof QueryRaw) {
428
430
  this.getRawValue(fragmentCtx, { value: entry });
429
431
  }
430
- else if (entry) {
432
+ else {
431
433
  this.renderWhere(fragmentCtx, entity, entry, { prefix: opts.prefix, operand: childOperand, clause: false });
432
434
  }
433
435
  });
@@ -523,7 +525,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
523
525
  * alias exists only when the field was also selected, which `$where` and `$sort` cannot assume.
524
526
  */
525
527
  inlinedOperand(ctx, field, prefix) {
526
- const inlined = field && isInlinedExpression(field) ? computedExpression(field) : undefined;
528
+ const inlined = field && isInlinedExpression(field) ? field.computed : undefined;
527
529
  return inlined
528
530
  ? this.buildFragment(ctx, (fragmentCtx) => this.getRawValue(fragmentCtx, {
529
531
  value: inlined,
@@ -589,13 +591,8 @@ export class AbstractSqlDialect extends VectorSqlDialect {
589
591
  case '$regex':
590
592
  return this.regexCondition(operand, this.addValue(ctx.values, val));
591
593
  case '$in':
592
- case '$nin': {
593
- if (!Array.isArray(val)) {
594
- // Not covered by the types: `/http` casts client JSON straight to `Query`, so this arrives untyped.
595
- throw TypeError(`${op} expects an array, got ${val === null ? 'null' : typeof val}`);
596
- }
597
- return operand + this.formatIn(ctx, val, op === '$nin');
598
- }
594
+ case '$nin':
595
+ return operand + this.formatIn(ctx, inOperands(op, val), op === '$nin');
599
596
  case '$between': {
600
597
  const [min, max] = val;
601
598
  return `${operand} BETWEEN ${this.addValue(ctx.values, min)} AND ${this.addValue(ctx.values, max)}`;
@@ -663,7 +660,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
663
660
  }
664
661
  }
665
662
  jsonInNin(ctx, jsonField, comparand, op, value, asJson) {
666
- const values = Array.isArray(value) ? value : [];
663
+ const values = inOperands(op, value);
667
664
  const negate = op === '$nin';
668
665
  if (!asJson) {
669
666
  return `${comparand(values)}${this.formatIn(ctx, values, negate)}`;
@@ -739,7 +736,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
739
736
  */
740
737
  jsonScalarParam(ctx, value) {
741
738
  if (value instanceof QueryRaw) {
742
- return this.addValue(ctx.values, value);
739
+ return this.rawFragment(ctx, value);
743
740
  }
744
741
  ctx.pushValue(JSON.stringify(value));
745
742
  // The placeholder for the value just pushed, so a named or numbered one is spelled correctly.
@@ -917,16 +914,14 @@ export class AbstractSqlDialect extends VectorSqlDialect {
917
914
  estimatedCount(_ctx, _entity) {
918
915
  throw new TypeError(`${this.dialectName} does not support estimatedCount`);
919
916
  }
920
- /** `$group` aggregate operator → SQL function name. An allowlist, not a formatter: the op key
921
- * comes from query data, so anything outside this map must be rejected rather than passed
922
- * through as a raw SQL function name. */
923
- static AGGREGATE_FN_MAP = new Map([
924
- ['$count', 'COUNT'],
925
- ['$sum', 'SUM'],
926
- ['$avg', 'AVG'],
927
- ['$min', 'MIN'],
928
- ['$max', 'MAX'],
929
- ]);
917
+ /** `$group` aggregate operator to SQL function name, over ops `resolveAggregateOp` has already allowlisted. */
918
+ static AGGREGATE_FN = {
919
+ $count: 'COUNT',
920
+ $sum: 'SUM',
921
+ $avg: 'AVG',
922
+ $min: 'MIN',
923
+ $max: 'MAX',
924
+ };
930
925
  aggregate(ctx, entity, q, opts = {}) {
931
926
  const meta = getMeta(entity);
932
927
  const tableName = this.escapedTableName(meta);
@@ -946,10 +941,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
946
941
  selectParts.push(columnName !== entry.alias ? `${escaped} ${this.escapeId(entry.alias)}` : escaped);
947
942
  }
948
943
  else {
949
- const sqlFn = AbstractSqlDialect.AGGREGATE_FN_MAP.get(entry.op);
950
- if (!sqlFn) {
951
- throw TypeError(`unsupported aggregate operator: ${entry.op}`);
952
- }
944
+ const sqlFn = AbstractSqlDialect.AGGREGATE_FN[entry.op];
953
945
  const sqlArg = entry.fieldRef === '*' ? '*' : this.escapeId(this.columnOf(meta, entry.fieldRef));
954
946
  const expr = `${sqlFn}(${entry.distinct ? 'DISTINCT ' : ''}${sqlArg})`;
955
947
  emittedColumns[entry.alias] = expr;
@@ -1065,6 +1057,12 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1065
1057
  * already built ends up, so the two ends cannot disagree.
1066
1058
  */
1067
1059
  returningPosition = 'suffix';
1060
+ /**
1061
+ * Whether a multi-row upsert's `RETURNING` lists its rows in payload order. Where it does not, the
1062
+ * ids are read back by the conflict columns instead, since placing them in order would name the
1063
+ * wrong rows.
1064
+ */
1065
+ upsertReturningOrdered = true;
1068
1066
  /**
1069
1067
  * `INSERT INTO ... VALUES (...)` and nothing more. The upsert builders extend this rather than
1070
1068
  * {@link insert}: their own clause has to come before the `RETURNING`, not after it.
@@ -1232,14 +1230,12 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1232
1230
  // Soft-delete (stamp only live rows) unless `hardDelete` is requested or the entity has no
1233
1231
  // soft-delete field (e.g. a cascade onto a non-soft-deletable child).
1234
1232
  if (!opts.hardDelete && meta.softDelete) {
1235
- const field = meta.fields[meta.softDelete];
1236
- if (field) {
1237
- const columnName = this.resolveColumnName(meta.softDelete, field);
1238
- ctx.append(`UPDATE ${tableName} SET ${this.escapeId(columnName)} = `);
1239
- this.formatPersistableValue(ctx, field, getSoftDeleteValue(field));
1240
- this.search(ctx, entity, q, opts);
1241
- return;
1242
- }
1233
+ const field = fieldOf(meta, meta.softDelete);
1234
+ const columnName = this.resolveColumnName(meta.softDelete, field);
1235
+ ctx.append(`UPDATE ${tableName} SET ${this.escapeId(columnName)} = `);
1236
+ this.formatPersistableValue(ctx, field, getSoftDeleteValue(field));
1237
+ this.search(ctx, entity, q, opts);
1238
+ return;
1243
1239
  }
1244
1240
  // Hard delete removes matching rows regardless of soft-delete state (keeps other filters, e.g. tenant).
1245
1241
  // Only rewrite the filters when there is a soft-delete filter to disable.
@@ -1758,9 +1754,6 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1758
1754
  const phs = values.map((v) => this.addValue(ctx.values, v)).join(', ');
1759
1755
  return ` ${negate ? 'NOT IN' : 'IN'} (${phs})`;
1760
1756
  }
1761
- numericCast(expr) {
1762
- return expr;
1763
- }
1764
1757
  toString() {
1765
1758
  return this.dialectName;
1766
1759
  }
@@ -1,3 +1,4 @@
1
+ import { decodeWideNumber } from '../util/wideNumber.js';
1
2
  import { parseVectorLiteral } from './vectorCast.js';
2
3
  /**
3
4
  * Decode one non-null cell. Kept beside {@link HydrateKind} rather than inlined into the querier's
@@ -31,8 +32,7 @@ export function decodeColumn(value, kind) {
31
32
  return value;
32
33
  }
33
34
  if (kind === 'number') {
34
- const decoded = Number(text);
35
- return Number.isNaN(decoded) ? value : decoded;
35
+ return Number.isNaN(Number(text)) ? value : decodeWideNumber(text);
36
36
  }
37
37
  if (kind === 'json') {
38
38
  try {
@@ -38,8 +38,8 @@ export declare abstract class MergeSqlDialect extends AbstractSqlDialect {
38
38
  * holds it across both. Empty on Oracle, which does not have the hint and does not need it.
39
39
  */
40
40
  protected readonly mergeTargetHint: string;
41
- /** How the merge reports the row it wrote. Oracle has no such clause on a `MERGE` and omits it. */
42
- protected mergeReturning(expression: string): string;
41
+ /** How the merge reports the row it wrote: SQL Server's `OUTPUT`. Oracle has no such clause on a `MERGE`. */
42
+ protected abstract mergeReturning(expression: string): string;
43
43
  /** `MERGE` must be terminated on SQL Server; nothing else here cares. */
44
44
  protected readonly statementTerminator: string;
45
45
  }
@@ -80,10 +80,6 @@ export class MergeSqlDialect extends AbstractSqlDialect {
80
80
  * holds it across both. Empty on Oracle, which does not have the hint and does not need it.
81
81
  */
82
82
  mergeTargetHint = '';
83
- /** How the merge reports the row it wrote. Oracle has no such clause on a `MERGE` and omits it. */
84
- mergeReturning(expression) {
85
- return `RETURNING ${expression}`;
86
- }
87
83
  /** `MERGE` must be terminated on SQL Server; nothing else here cares. */
88
84
  statementTerminator = '';
89
85
  }
@@ -9,9 +9,6 @@ import { AbstractSqlDialect } from './abstractSqlDialect.js';
9
9
  * - `$elemMatch` (JSON_TABLE, or fast JSON_CONTAINS for the simple case)
10
10
  * - the update operators `$set` (JSON_SET), `$unset` (JSON_REMOVE), `$push` (JSON_MERGE_PRESERVE)
11
11
  * and `$pull` (JSON_REPLACE over JSON_TABLE)
12
- *
13
- * Neither has `FOR NO KEY UPDATE`/`FOR KEY SHARE`, PostgreSQL's weaker pair, so asking for one is
14
- * rejected rather than served a stronger lock.
15
12
  */
16
13
  export declare abstract class MysqlLikeSqlDialect extends AbstractSqlDialect {
17
14
  /** Default {@link DialectFeatures} for MySQL-compatible SQL dialects. */
@@ -15,9 +15,6 @@ const MAX_LIMIT = BigInt.asUintN(64, -1n);
15
15
  * - `$elemMatch` (JSON_TABLE, or fast JSON_CONTAINS for the simple case)
16
16
  * - the update operators `$set` (JSON_SET), `$unset` (JSON_REMOVE), `$push` (JSON_MERGE_PRESERVE)
17
17
  * and `$pull` (JSON_REPLACE over JSON_TABLE)
18
- *
19
- * Neither has `FOR NO KEY UPDATE`/`FOR KEY SHARE`, PostgreSQL's weaker pair, so asking for one is
20
- * rejected rather than served a stronger lock.
21
18
  */
22
19
  export class MysqlLikeSqlDialect extends AbstractSqlDialect {
23
20
  /** Default {@link DialectFeatures} for MySQL-compatible SQL dialects. */
@@ -93,6 +93,11 @@ export declare abstract class PgLikeSqlDialect extends AbstractSqlDialect {
93
93
  * ORDINALITY` keeps the surviving elements in their original order.
94
94
  */
95
95
  protected jsonPullKey(ctx: QueryContext, expr: string, escapedCol: string, key: string, value: unknown): string;
96
+ /**
97
+ * The plain values merge as one bound object; a `raw()` one is an SQL expression rather than JSON,
98
+ * so it merges through `JSONB_BUILD_OBJECT` and is evaluated in place - stringified with the rest,
99
+ * it would land as `{}`.
100
+ */
96
101
  protected jsonSet(ctx: QueryContext, expr: string, set: Record<string, unknown>, field?: FieldOptions): string;
97
102
  /** The only fragment that references `expr` twice - safe here because placeholders are numbered. */
98
103
  protected jsonPush(ctx: QueryContext, expr: string, push: Record<string, unknown>): string;
@@ -213,8 +213,18 @@ export class PgLikeSqlDialect extends AbstractSqlDialect {
213
213
  const kept = `SELECT JSONB_AGG(${JSON_PULL_ALIAS}.val ORDER BY ${JSON_PULL_ALIAS}.ord) FROM JSONB_ARRAY_ELEMENTS(${escapedCol}->'${escapedKey}') WITH ORDINALITY AS ${JSON_PULL_ALIAS}(val, ord) WHERE ${JSON_PULL_ALIAS}.val <> ${this.jsonVal(ctx, value)}`;
214
214
  return `JSONB_SET(${expr}, '{${escapedKey}}', COALESCE((${kept}), '[]'::jsonb), false)`;
215
215
  }
216
+ /**
217
+ * The plain values merge as one bound object; a `raw()` one is an SQL expression rather than JSON,
218
+ * so it merges through `JSONB_BUILD_OBJECT` and is evaluated in place - stringified with the rest,
219
+ * it would land as `{}`.
220
+ */
216
221
  jsonSet(ctx, expr, set, field) {
217
- return `${jsonSetTarget(expr, field, `'{}'::jsonb`)} || ${this.jsonVal(ctx, set)}`;
222
+ const entries = Object.entries(set);
223
+ const merged = this.jsonVal(ctx, Object.fromEntries(entries.filter(([, value]) => !(value instanceof QueryRaw))));
224
+ const raws = entries.flatMap(([key, value]) => value instanceof QueryRaw
225
+ ? [` || JSONB_BUILD_OBJECT('${escapeSingleQuotes(key)}', ${this.rawFragment(ctx, value)})`]
226
+ : []);
227
+ return `${jsonSetTarget(expr, field, `'{}'::jsonb`)} || ${merged}${raws.join('')}`;
218
228
  }
219
229
  /** The only fragment that references `expr` twice - safe here because placeholders are numbered. */
220
230
  jsonPush(ctx, expr, push) {
@@ -236,7 +246,7 @@ export class PgLikeSqlDialect extends AbstractSqlDialect {
236
246
  */
237
247
  jsonVal(ctx, value, type = 'jsonb') {
238
248
  if (value instanceof QueryRaw)
239
- return this.addValue(ctx.values, value);
249
+ return this.rawFragment(ctx, value);
240
250
  if (value == null)
241
251
  return `${this.addValue(ctx.values, null)}::${type}`;
242
252
  const json = JSON.stringify(value);
@@ -2,12 +2,6 @@ import type { EntityGetter, FieldOptions, FieldType, IdValue, NamedIdKey, Relati
2
2
  import type { RejectIncompatible } from '../../util/index.js';
3
3
  /** A member decorator that also constrains the property it may be applied to. */
4
4
  type MemberDecorator<V> = (value: undefined, context: ClassFieldDecoratorContext<unknown, V>) => void;
5
- /**
6
- * The property type a set of field options describes, in the same order schema generation resolves the
7
- * column: a declared `type` wins, and otherwise the column - and so the property - is the referenced
8
- * primary key's own type. Which is what makes `@Field({ references: () => User })` on a `number`, where
9
- * `User.id` is a `uuid`, a compile error rather than a column that disagrees with its property.
10
- */
11
5
  /**
12
6
  * Maps any option the type does not declare to `never`, turning a typo into a compile error.
13
7
  *
@@ -17,10 +11,9 @@ type MemberDecorator<V> = (value: undefined, context: ClassFieldDecoratorContext
17
11
  */
18
12
  type RejectUnknown<O, Known> = [Exclude<keyof O, keyof Known>] extends [never] ? unknown : Record<Exclude<keyof O, keyof Known> & string, never>;
19
13
  /**
20
- * The value type the options declare, which the decorated property is then checked against.
21
- *
22
- * `enum` narrows it to its own values, so the property must spell out the same set. Only the values
23
- * the declared `type` admits count, which is what keeps `enum: [2]` off a `String` field.
14
+ * The property type a set of field options describes: the declared `type`, narrowed by `enum` to the
15
+ * values that type admits (so `enum: [2]` stays off a `String`), or else the referenced key's own type,
16
+ * which makes `@Field({ references: () => User })` on a `number` an error when `User.id` is a `uuid`.
24
17
  */
25
18
  type DeclaredValue<O> = O extends {
26
19
  readonly type: infer T extends FieldType;
@@ -1,3 +1,3 @@
1
1
  export * from './decorator/entity.js';
2
2
  export * from './decorator/members.js';
3
- export { defineEntity, defineField, defineFilter, defineHook, defineId, defineIndex, defineRelation, getEntities, getMeta, removeEntity, assertSoleId, idOf, namesKey, soleIdOf, } from './metadata/definition.js';
3
+ export { defineEntity, defineField, defineFilter, defineHook, defineId, defineIndex, defineRelation, getEntities, getMeta, removeEntity, assertSoleId, fieldOf, idOf, namesKey, soleIdOf, } from './metadata/definition.js';
@@ -1,3 +1,3 @@
1
1
  export * from './decorator/entity.js';
2
2
  export * from './decorator/members.js';
3
- export { defineEntity, defineField, defineFilter, defineHook, defineId, defineIndex, defineRelation, getEntities, getMeta, removeEntity, assertSoleId, idOf, namesKey, soleIdOf, } from './metadata/definition.js';
3
+ export { defineEntity, defineField, defineFilter, defineHook, defineId, defineIndex, defineRelation, getEntities, getMeta, removeEntity, assertSoleId, fieldOf, idOf, namesKey, soleIdOf, } from './metadata/definition.js';
@@ -1,4 +1,4 @@
1
- import type { EntityData, EntityIndexInput, EntityMembers, EntityMeta, EntityOptions, FieldKey, FieldOptions, FilterOptions, HookEvent, IdKey, RelationOptions, Type, WrittenId } from '../../type/index.js';
1
+ import type { EntityData, EntityIndexInput, EntityMembers, EntityMeta, EntityOptions, FieldKey, FieldMeta, FieldOptions, FilterOptions, HookEvent, IdKey, RelationOptions, Type, WrittenId } from '../../type/index.js';
2
2
  export declare function defineField<E>(entity: Type<E>, key: string, opts?: FieldOptions): EntityMeta<E>;
3
3
  export declare function defineId<E>(entity: Type<E>, key: string, opts: FieldOptions): EntityMeta<E>;
4
4
  export declare function defineRelation<E>(entity: Type<E>, key: string, opts: RelationOptions): EntityMeta<E>;
@@ -30,6 +30,8 @@ export declare function defineEntity<E>(entity: Type<E>, opts?: EntityOptions<E>
30
30
  export declare function assertSoleId<E>(meta: EntityMeta<E>, what: string): void;
31
31
  /** The entity's one primary key, for a path that cannot express a composite. See {@link assertSoleId}. */
32
32
  export declare function soleIdOf<E>(meta: EntityMeta<E>, what: string): IdKey<E>;
33
+ /** The field `key` names, for a caller that took `key` from the metadata itself. */
34
+ export declare function fieldOf<E>(meta: EntityMeta<E>, key: string): FieldMeta;
33
35
  /**
34
36
  * Whether the caller named every column of the row's primary key, so {@link idOf} can name the row.
35
37
  *
@@ -17,10 +17,6 @@ function globalMap(key) {
17
17
  const metas = globalMap('uql-orm/entity/metadata/v1');
18
18
  export function defineField(entity, key, opts = {}) {
19
19
  const meta = ensureWritableMeta(entity);
20
- if (opts.virtual !== undefined && opts.computed !== undefined) {
21
- throw new TypeError(`'${entity.name}.${key}' gives both 'virtual' and 'computed'. They are one option under two names - ` +
22
- "keep 'computed'; 'npx uql-codemod' rewrites the other.");
23
- }
24
20
  // A stored computed column is a real column and still needs a type; only an inlined one is exempt,
25
21
  // its expression being spliced in rather than declared.
26
22
  if (!opts.type && !opts.references && !isInlinedExpression(opts)) {
@@ -223,6 +219,14 @@ export function soleIdOf(meta, what) {
223
219
  assertSoleId(meta, what);
224
220
  return meta.ids[0];
225
221
  }
222
+ /** The field `key` names, for a caller that took `key` from the metadata itself. */
223
+ export function fieldOf(meta, key) {
224
+ const field = meta.fields[key];
225
+ if (!field) {
226
+ throw new TypeError(`'${meta.entity.name}' has no field '${key}'`);
227
+ }
228
+ return field;
229
+ }
226
230
  /**
227
231
  * Whether the caller named every column of the row's primary key, so {@link idOf} can name the row.
228
232
  *
@@ -1,3 +1,2 @@
1
1
  export * from './libsqlDialect.js';
2
- export * from './libsqlQuerier.js';
3
2
  export * from './libsqlQuerierPool.js';
@@ -1,3 +1,2 @@
1
1
  export * from './libsqlDialect.js';
2
- export * from './libsqlQuerier.js';
3
2
  export * from './libsqlQuerierPool.js';
@@ -1,21 +1,19 @@
1
1
  import type { Config } from '@libsql/client';
2
- import type { HranaClient, HranaQuerierConnectionOptions } from '../sqlite/hranaQuerier.js';
2
+ import { type HranaClient, HranaQuerier } from '../sqlite/hranaQuerier.js';
3
3
  import { AbstractHranaQuerierPool } from '../sqlite/hranaQuerierPool.js';
4
4
  import type { ExtraOptions } from '../type/index.js';
5
5
  import { LibsqlDialect } from './libsqlDialect.js';
6
- import { LibsqlQuerier } from './libsqlQuerier.js';
7
6
  /** Embedded replica: local `file:` DB + `syncUrl` remote - DDL should run on the remote (sqld). */
8
7
  export declare function libsqlUseRemoteForMigrations(config: Pick<Config, 'url' | 'syncUrl'>): boolean;
9
- export declare class LibsqlQuerierPool extends AbstractHranaQuerierPool<LibsqlQuerier, LibsqlDialect> {
8
+ export declare class LibsqlQuerierPool extends AbstractHranaQuerierPool<LibsqlDialect> {
10
9
  private readonly conf;
11
10
  constructor(conf: Config, extra?: ExtraOptions);
12
11
  protected openClient(): Promise<HranaClient>;
13
- protected buildQuerier(client: HranaClient, connection?: HranaQuerierConnectionOptions): LibsqlQuerier;
14
12
  /**
15
13
  * For embedded replicas (`file:` + `syncUrl`), returns a querier connected to `syncUrl` so migrations hit sqld.
16
14
  * Otherwise same as `getQuerier`. The migrator calls this for `up`/`down`, `syncForce`, and `autoSync` DDL.
17
15
  */
18
- getMigrationQuerier(): Promise<LibsqlQuerier>;
16
+ getMigrationQuerier(): Promise<HranaQuerier>;
19
17
  /** Imported on use, so `uql-orm/libsql` loads without the optional `@libsql/client` peer installed. */
20
18
  private createClient;
21
19
  }
@@ -1,7 +1,7 @@
1
1
  import { dialectOptionsFrom } from '../dialect/abstractDialect.js';
2
+ import { HranaQuerier } from '../sqlite/hranaQuerier.js';
2
3
  import { AbstractHranaQuerierPool } from '../sqlite/hranaQuerierPool.js';
3
4
  import { LibsqlDialect } from './libsqlDialect.js';
4
- import { LibsqlQuerier } from './libsqlQuerier.js';
5
5
  /** Embedded replica: local `file:` DB + `syncUrl` remote - DDL should run on the remote (sqld). */
6
6
  export function libsqlUseRemoteForMigrations(config) {
7
7
  return Boolean(config.syncUrl && config.url.startsWith('file:'));
@@ -20,9 +20,6 @@ export class LibsqlQuerierPool extends AbstractHranaQuerierPool {
20
20
  openClient() {
21
21
  return this.createClient(this.conf);
22
22
  }
23
- buildQuerier(client, connection) {
24
- return new LibsqlQuerier(client, this.dialect, this.extra, connection);
25
- }
26
23
  /**
27
24
  * For embedded replicas (`file:` + `syncUrl`), returns a querier connected to `syncUrl` so migrations hit sqld.
28
25
  * Otherwise same as `getQuerier`. The migrator calls this for `up`/`down`, `syncForce`, and `autoSync` DDL.
@@ -32,7 +29,7 @@ export class LibsqlQuerierPool extends AbstractHranaQuerierPool {
32
29
  return this.getQuerier();
33
30
  }
34
31
  const remote = await this.createClient(remoteMigrationClientConfig(this.conf));
35
- return this.buildQuerier(remote, { closeClientOnRelease: true });
32
+ return new HranaQuerier(remote, this.dialect, this.extra, { closeClientOnRelease: true });
36
33
  }
37
34
  /** Imported on use, so `uql-orm/libsql` loads without the optional `@libsql/client` peer installed. */
38
35
  async createClient(conf) {
@@ -1,9 +1,6 @@
1
1
  import type { PoolConnection } from 'mariadb';
2
2
  import { AbstractPoolQuerier } from '../querier/abstractPoolQuerier.js';
3
- import type { ExtraOptions } from '../type/index.js';
4
- import type { MariaDialect } from './mariaDialect.js';
5
3
  export declare class MariadbQuerier extends AbstractPoolQuerier<PoolConnection> {
6
- constructor(connect: () => Promise<PoolConnection>, dialect: MariaDialect, extra?: ExtraOptions);
7
4
  internalAll<T>(query: string, values?: unknown[]): Promise<T[]>;
8
5
  internalRun(query: string, values?: unknown[]): Promise<import("../type/query.js").QueryUpdateResult>;
9
6
  internalStream<T>(query: string, values?: unknown[]): AsyncGenerator<Awaited<T>, void, unknown>;
@@ -1,23 +1,22 @@
1
1
  import { AbstractPoolQuerier } from '../querier/abstractPoolQuerier.js';
2
+ import { decodeBigInts } from '../util/wideNumber.js';
2
3
  export class MariadbQuerier extends AbstractPoolQuerier {
3
- constructor(connect, dialect, extra) {
4
- super(dialect, connect, extra);
5
- }
6
4
  async internalAll(query, values) {
7
- const res = await this.getConn().query(query, values);
8
- return res;
5
+ const rows = await this.getConn().query(query, values);
6
+ return Array.from(rows, decodeBigInts);
9
7
  }
10
8
  async internalRun(query, values) {
11
9
  const res = await this.getConn().query(query, values);
12
10
  // MariaDB may not set `affectedRows` when RETURNING is used; fall back to row count.
13
11
  const changes = res.affectedRows ?? res.length ?? 0;
14
- return this.buildUpdateResult({ rows: res.length ? res : [], changes, upsertStatus: res.affectedRows });
12
+ const rows = res.length ? Array.from(res, decodeBigInts) : [];
13
+ return this.buildUpdateResult({ rows, changes, upsertStatus: res.affectedRows });
15
14
  }
16
15
  async *internalStream(query, values) {
17
16
  const stream = this.getConn().queryStream(query, values);
18
17
  try {
19
18
  for await (const row of stream) {
20
- yield row;
19
+ yield decodeBigInts(row);
21
20
  }
22
21
  }
23
22
  finally {
@@ -8,19 +8,16 @@ export class MariadbQuerierPool extends AbstractSqlQuerierPool {
8
8
  pool;
9
9
  constructor(opts, extra) {
10
10
  super(new MariaDialect(dialectOptionsFrom(extra)), extra);
11
- // `mariadb` defaults to handing BIGINT back as a BigInt, and uql maps `type: Number` to BIGINT
12
- // (see `schema/canonicalType.ts`), so every auto-increment id reached a field declared `number`
13
- // as `9n` without this. Same trade as the pg pools: exact to 2^53, and `...opts` wins for a
14
- // caller who needs more. This belongs to the pool, not to the suites - it lived in
15
- // `mariadbQuerier.test.ts`, which meant the tests passed on behaviour the library never shipped.
16
- this.pool = createPool({ bigIntAsNumber: true, ...opts });
11
+ // BIGINT stays the driver's `bigint`, which `MariadbQuerier` decodes by the rule every driver here
12
+ // shares (`decodeWideNumber`) - not `bigIntAsNumber`, which rounds past 2^53 without a word.
13
+ this.pool = createPool(opts);
17
14
  // `mariadb`'s own `createPool` already attaches a silent no-op 'error'
18
15
  // listener (so a dropped connection can't crash the process), but its
19
16
  // `Pool` type only declares `on` for 'acquire' | 'connection' | 'enqueue'
20
17
  // | 'release' - 'error' genuinely fires at runtime (see `lib/pool.js`)
21
18
  // but isn't in the declaration, hence the cast. Re-attaching our own
22
19
  // listener here just makes the error visible instead of a silent no-op.
23
- attachPoolErrorHandler(this.pool, 'Idle MariaDB pool connection encountered an error');
20
+ attachPoolErrorHandler(this.pool, 'Idle MariaDB pool connection encountered an error', extra?.logger);
24
21
  }
25
22
  async getQuerier() {
26
23
  return new MariadbQuerier(() => this.pool.getConnection(), this.dialect, this.extra);