uql-orm 0.66.0 → 0.67.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.
Files changed (193) hide show
  1. package/dist/browser/querier/httpQuerier.js +1 -8
  2. package/dist/browser/uql-browser.min.js.map +5 -5
  3. package/dist/bunSql/bunSql.util.d.ts +2 -6
  4. package/dist/bunSql/bunSql.util.js +2 -6
  5. package/dist/bunSql/bunSqlQuerier.d.ts +2 -5
  6. package/dist/bunSql/bunSqlQuerier.js +2 -5
  7. package/dist/cockroachdb/cockroachDialect.d.ts +4 -13
  8. package/dist/cockroachdb/cockroachDialect.js +4 -13
  9. package/dist/context/context.browser.js +2 -10
  10. package/dist/context/context.d.ts +4 -17
  11. package/dist/context/context.js +4 -17
  12. package/dist/dialect/abstractDialect.d.ts +4 -19
  13. package/dist/dialect/abstractDialect.js +2 -20
  14. package/dist/dialect/abstractSqlDialect.d.ts +47 -212
  15. package/dist/dialect/abstractSqlDialect.js +68 -222
  16. package/dist/dialect/aliases.d.ts +2 -12
  17. package/dist/dialect/aliases.js +4 -12
  18. package/dist/dialect/hydrateColumn.d.ts +2 -6
  19. package/dist/dialect/hydrateColumn.js +3 -13
  20. package/dist/dialect/jsonArrayElemMatchUtils.d.ts +1 -7
  21. package/dist/dialect/jsonArrayElemMatchUtils.js +1 -7
  22. package/dist/dialect/jsonSql.d.ts +6 -27
  23. package/dist/dialect/jsonSql.js +6 -27
  24. package/dist/dialect/mergeSqlDialect.d.ts +4 -22
  25. package/dist/dialect/mergeSqlDialect.js +4 -22
  26. package/dist/dialect/mysqlLikeSqlDialect.d.ts +11 -37
  27. package/dist/dialect/mysqlLikeSqlDialect.js +35 -51
  28. package/dist/dialect/pgLikeSqlDialect.d.ts +8 -22
  29. package/dist/dialect/pgLikeSqlDialect.js +36 -39
  30. package/dist/dialect/queryContext.d.ts +4 -22
  31. package/dist/dialect/queryContext.js +4 -22
  32. package/dist/dialect/queryJoins.d.ts +3 -12
  33. package/dist/dialect/queryJoins.js +3 -12
  34. package/dist/dialect/vectorCast.d.ts +2 -12
  35. package/dist/dialect/vectorCast.js +3 -19
  36. package/dist/dialect/vectorSqlDialect.d.ts +8 -38
  37. package/dist/dialect/vectorSqlDialect.js +7 -38
  38. package/dist/entity/decorator/bag.d.ts +6 -19
  39. package/dist/entity/decorator/bag.js +6 -22
  40. package/dist/entity/decorator/entity.d.ts +2 -7
  41. package/dist/entity/decorator/entity.js +2 -7
  42. package/dist/entity/decorator/members.d.ts +7 -30
  43. package/dist/entity/decorator/members.js +3 -12
  44. package/dist/entity/metadata/definition.d.ts +2 -18
  45. package/dist/entity/metadata/definition.js +6 -28
  46. package/dist/http/handler.d.ts +2 -14
  47. package/dist/index.d.ts +4 -1
  48. package/dist/index.js +3 -1
  49. package/dist/libsql/libsqlDialect.d.ts +1 -8
  50. package/dist/libsql/libsqlDialect.js +1 -8
  51. package/dist/maria/mariaDialect.d.ts +3 -5
  52. package/dist/maria/mariaDialect.js +5 -5
  53. package/dist/maria/mariadbQuerier.js +2 -2
  54. package/dist/maria/mariadbQuerierPool.js +1 -6
  55. package/dist/migrate/builder/migrationBuilder.js +3 -19
  56. package/dist/migrate/builder/splitSqlStatements.d.ts +1 -14
  57. package/dist/migrate/builder/splitSqlStatements.js +2 -22
  58. package/dist/migrate/builder/types.d.ts +2 -15
  59. package/dist/migrate/cli-config.js +2 -11
  60. package/dist/migrate/cli.js +2 -7
  61. package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -15
  62. package/dist/migrate/codegen/entityCodeGenerator.js +15 -44
  63. package/dist/migrate/codegen/fieldOptionsSource.d.ts +1 -8
  64. package/dist/migrate/codegen/fieldOptionsSource.js +3 -22
  65. package/dist/migrate/ddl/indexDdl.d.ts +2 -5
  66. package/dist/migrate/ddl/indexDdl.js +2 -5
  67. package/dist/migrate/ddl/pgIndexDdl.d.ts +3 -13
  68. package/dist/migrate/ddl/pgIndexDdl.js +3 -13
  69. package/dist/migrate/generator/definitionToNode.d.ts +2 -9
  70. package/dist/migrate/generator/definitionToNode.js +3 -17
  71. package/dist/migrate/generator/indexNodeToSchema.d.ts +2 -3
  72. package/dist/migrate/generator/indexNodeToSchema.js +2 -3
  73. package/dist/migrate/generator/mongoCommand.d.ts +1 -8
  74. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +1 -8
  75. package/dist/migrate/generator/mongoSchemaGenerator.js +1 -8
  76. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +6 -26
  77. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +9 -41
  78. package/dist/migrate/introspection/baseSqlIntrospector.js +0 -1
  79. package/dist/migrate/introspection/mongoIntrospector.d.ts +3 -1
  80. package/dist/migrate/introspection/mongoIntrospector.js +48 -46
  81. package/dist/migrate/introspection/mssqlIntrospector.d.ts +4 -4
  82. package/dist/migrate/introspection/mssqlIntrospector.js +18 -27
  83. package/dist/migrate/introspection/mysqlIntrospector.d.ts +7 -2
  84. package/dist/migrate/introspection/mysqlIntrospector.js +16 -14
  85. package/dist/migrate/introspection/postgresIntrospector.d.ts +24 -9
  86. package/dist/migrate/introspection/postgresIntrospector.js +68 -59
  87. package/dist/migrate/introspection/sqliteIntrospector.d.ts +1 -1
  88. package/dist/migrate/introspection/sqliteIntrospector.js +8 -10
  89. package/dist/migrate/migrator.d.ts +9 -53
  90. package/dist/migrate/migrator.js +32 -65
  91. package/dist/migrate/schemaGenerator.d.ts +16 -66
  92. package/dist/migrate/schemaGenerator.js +21 -74
  93. package/dist/mongo/mongoDialect.d.ts +21 -53
  94. package/dist/mongo/mongoDialect.js +25 -70
  95. package/dist/mongo/mongodbQuerier.d.ts +5 -8
  96. package/dist/mongo/mongodbQuerier.js +31 -65
  97. package/dist/mssql/mssqlDialect.d.ts +8 -34
  98. package/dist/mssql/mssqlDialect.js +37 -51
  99. package/dist/mssql/mssqlQuerier.d.ts +37 -4
  100. package/dist/mssql/mssqlQuerier.js +2 -2
  101. package/dist/mssql/mssqlWireTypes.d.ts +2 -14
  102. package/dist/mssql/mssqlWireTypes.js +2 -14
  103. package/dist/nestjs/uqlModule.js +2 -7
  104. package/dist/pglite/pgliteQuerier.d.ts +1 -9
  105. package/dist/pglite/pgliteQuerierPool.d.ts +4 -26
  106. package/dist/pglite/pgliteQuerierPool.js +3 -18
  107. package/dist/postgres/abstractPgQuerierPool.d.ts +1 -8
  108. package/dist/postgres/abstractPgQuerierPool.js +1 -8
  109. package/dist/postgres/pgNumericTypes.d.ts +3 -26
  110. package/dist/postgres/pgNumericTypes.js +3 -26
  111. package/dist/postgres/postgresDialect.d.ts +4 -10
  112. package/dist/postgres/postgresDialect.js +4 -10
  113. package/dist/querier/abstractQuerier.d.ts +35 -103
  114. package/dist/querier/abstractQuerier.js +105 -201
  115. package/dist/querier/abstractSharedHandleQuerierPool.d.ts +3 -17
  116. package/dist/querier/abstractSharedHandleQuerierPool.js +3 -17
  117. package/dist/querier/abstractSqlQuerier.d.ts +15 -36
  118. package/dist/querier/abstractSqlQuerier.js +49 -131
  119. package/dist/schema/canonicalType.d.ts +3 -21
  120. package/dist/schema/canonicalType.js +22 -67
  121. package/dist/schema/dependencyGraph.d.ts +2 -8
  122. package/dist/schema/dependencyGraph.js +2 -32
  123. package/dist/schema/index.d.ts +1 -25
  124. package/dist/schema/index.js +0 -26
  125. package/dist/schema/indexColumns.d.ts +1 -8
  126. package/dist/schema/indexColumns.js +1 -8
  127. package/dist/schema/indexDifferences.d.ts +7 -40
  128. package/dist/schema/indexDifferences.js +6 -31
  129. package/dist/schema/schemaAST.d.ts +8 -175
  130. package/dist/schema/schemaAST.js +13 -365
  131. package/dist/schema/schemaASTBuilder.d.ts +2 -24
  132. package/dist/schema/schemaASTBuilder.js +2 -30
  133. package/dist/schema/schemaASTDiffer.d.ts +6 -46
  134. package/dist/schema/schemaASTDiffer.js +8 -56
  135. package/dist/schema/types.d.ts +5 -61
  136. package/dist/schema/types.js +3 -6
  137. package/dist/sqlite/abstractSqliteQuerier.d.ts +1 -8
  138. package/dist/sqlite/localSqliteQuerierPool.d.ts +1 -7
  139. package/dist/sqlite/localSqliteQuerierPool.js +1 -7
  140. package/dist/sqlite/nodeSqliteQuerierPool.d.ts +2 -7
  141. package/dist/sqlite/nodeSqliteQuerierPool.js +2 -7
  142. package/dist/sqlite/sqliteDialect.d.ts +5 -20
  143. package/dist/sqlite/sqliteDialect.js +29 -35
  144. package/dist/turso/tursoDialect.d.ts +4 -6
  145. package/dist/turso/tursoDialect.js +4 -6
  146. package/dist/turso/tursoLocalQuerierPool.d.ts +1 -7
  147. package/dist/turso/tursoLocalQuerierPool.js +1 -7
  148. package/dist/turso/tursoQuerierPool.d.ts +2 -6
  149. package/dist/turso/tursoQuerierPool.js +2 -6
  150. package/dist/turso/tursoSessionQuerier.d.ts +1 -7
  151. package/dist/turso/tursoSessionQuerier.js +1 -7
  152. package/dist/type/dialect.d.ts +42 -94
  153. package/dist/type/dialect.js +3 -13
  154. package/dist/type/entity.d.ts +163 -534
  155. package/dist/type/entity.js +26 -9
  156. package/dist/type/logger.d.ts +2 -14
  157. package/dist/type/migration.d.ts +9 -38
  158. package/dist/type/querier.d.ts +9 -28
  159. package/dist/type/querierPool.d.ts +4 -26
  160. package/dist/type/query.d.ts +19 -73
  161. package/dist/type/query.js +2 -7
  162. package/dist/type/queryAggregate.d.ts +18 -98
  163. package/dist/type/queryRaw.d.ts +1 -8
  164. package/dist/type/queryRaw.js +1 -8
  165. package/dist/type/queryWhere.d.ts +13 -61
  166. package/dist/type/universalQuerier.d.ts +18 -105
  167. package/dist/type/utility.d.ts +12 -24
  168. package/dist/type/vector.d.ts +8 -38
  169. package/dist/type/vector.js +1 -1
  170. package/dist/type/wire.d.ts +2 -5
  171. package/dist/util/dialect.util.d.ts +9 -27
  172. package/dist/util/dialect.util.js +10 -27
  173. package/dist/util/field.util.d.ts +2 -31
  174. package/dist/util/field.util.js +3 -43
  175. package/dist/util/fieldOption.util.d.ts +7 -15
  176. package/dist/util/fieldOption.util.js +1 -1
  177. package/dist/util/filters.util.d.ts +2 -5
  178. package/dist/util/filters.util.js +2 -5
  179. package/dist/util/logger.d.ts +2 -6
  180. package/dist/util/logger.js +2 -6
  181. package/dist/util/object.util.d.ts +2 -6
  182. package/dist/util/object.util.js +1 -5
  183. package/dist/util/raw.d.ts +3 -23
  184. package/dist/util/relationQuery.util.d.ts +3 -14
  185. package/dist/util/relationQuery.util.js +3 -14
  186. package/dist/util/rowKey.util.d.ts +2 -10
  187. package/dist/util/rowKey.util.js +2 -10
  188. package/dist/util/sql.util.d.ts +6 -37
  189. package/dist/util/sql.util.js +13 -73
  190. package/dist/util/sqlLiteral.d.ts +2 -13
  191. package/dist/util/sqlLiteral.js +8 -13
  192. package/dist/util/string.util.js +0 -2
  193. package/package.json +4 -4
@@ -1,9 +1,9 @@
1
1
  import { fieldOf, getMeta, relationOf, soleIdOf } from '../entity/index.js';
2
- import { COUNT_RESULT_KEY, parseQueryLock, QueryRaw, RAW_ALIAS, RAW_VALUE, VECTOR_QUERY_KEYS, } from '../type/index.js';
2
+ import { COUNT_RESULT_KEY, parseQueryLock, QueryRaw, RAW_ALIAS, VECTOR_QUERY_KEYS, } from '../type/index.js';
3
3
  import { isInlinedExpression } from '../util/field.util.js';
4
- import { asSelectMap, assertNonNegativeInteger, escapeSqlId, fillOnFields, filterFieldKeys, getInsertFieldKeys, getKeys, getRelationRequestSummary, getSoftDeleteValue, hasKeys, columnFamily, countedRelations, isJsonUpdateOp, isOperatorMap, isOperatorObject, isOperatorOnlyObject, isVectorSearch, normalizeScalarFieldSelection, parentJoins, targetKeyColumns, parseGroupMap, parseRelationAtKey, parseRelationSize, parseSortByCount, populatesRelations, raw, someValue, throwUnknownAggregateColumn, withoutSoftDeleteFilter, } from '../util/index.js';
4
+ import { asSelectMap, assertNonNegativeInteger, escapeSqlId, fillOnFields, filterFieldKeys, getInsertFieldKeys, getKeys, getRelationRequestSummary, getSoftDeleteValue, hasKeys, idOnlyQuery, columnFamily, countedRelations, isJsonUpdateOp, isOperatorMap, isOperatorObject, isOperatorOnlyObject, isVectorSearch, normalizeScalarFieldSelection, parentJoins, targetKeyColumns, parseGroupMap, parseRelationAtKey, parseRelationSize, parseSortByCount, populatesRelations, raw, someValue, throwUnknownAggregateColumn, withoutSoftDeleteFilter, } from '../util/index.js';
5
5
  import { escapeAnsiSqlLiteral, escapeSingleQuotes } from '../util/sqlLiteral.js';
6
- import { COUNT_ALIAS, DISTINCT_DERIVED_ALIAS, JSON_ELEM_ALIAS, relationSortColumn } from './aliases.js';
6
+ import { COUNT_ALIAS, COUNTED_ROWS_ALIAS, JSON_ELEM_ALIAS, relationSortColumn } from './aliases.js';
7
7
  import { buildElemMatchConditions } from './jsonArrayElemMatchUtils.js';
8
8
  import { isJsonbOp, jsonCompareMode, jsonElemExists } from './jsonSql.js';
9
9
  import { SqlQueryContext } from './queryContext.js';
@@ -35,13 +35,6 @@ function inOperands(op, value) {
35
35
  return value;
36
36
  }
37
37
  export class AbstractSqlDialect extends VectorSqlDialect {
38
- /**
39
- * Whether {@link autoIncrementSuffix} states `PRIMARY KEY` itself, so the table must not state it again.
40
- *
41
- * True on SQLite alone, where `AUTOINCREMENT` is legal only in the exact phrase
42
- * `INTEGER PRIMARY KEY AUTOINCREMENT` - the key cannot be lifted out of the column there.
43
- */
44
- serialDeclaresPrimaryKey = false;
45
38
  /**
46
39
  * How this engine declares a namespace, so a generated migration creates the schemas its tables
47
40
  * need before creating them. Only reached where {@link DialectFeatures.schemas} is on. MySQL and
@@ -87,13 +80,8 @@ export class AbstractSqlDialect extends VectorSqlDialect {
87
80
  return new SqlQueryContext(this, [], undefined, options.inlineValues);
88
81
  }
89
82
  /**
90
- * Builds SQL text in isolation via `build`, so the caller can embed it inline (e.g.
91
- * `"col" = <text>`) instead of appending it at the end of `ctx`. The fragment binds any value
92
- * straight into `ctx`'s own values array - shared by reference, not copied - so `addValue` numbers
93
- * its placeholder correctly against the real query from the start; a fresh, empty array would
94
- * instead number from `1` regardless of how many values `ctx` already has, misnumbering every
95
- * bound value on `$n`-placeholder dialects once `ctx` isn't otherwise empty. Generated aliases are
96
- * shared for the same reason - see {@link SqlQueryContext}.
83
+ * The SQL `build` writes, as text to embed rather than appended to `ctx`. It binds into `ctx`'s own values
84
+ * and aliases, so `$n` placeholders number against the whole statement.
97
85
  */
98
86
  buildFragment(ctx, build) {
99
87
  const fragmentCtx = ctx.createFragment();
@@ -105,12 +93,8 @@ export class AbstractSqlDialect extends VectorSqlDialect {
105
93
  return this.buildFragment(ctx, (fragmentCtx) => this.getRawValue(fragmentCtx, { value, prefix, entity }));
106
94
  }
107
95
  /**
108
- * Each operand rendered into its own fragment, keeping only those that emitted SQL.
109
- *
110
- * Nothing reaches `ctx` until every one has rendered, because an operand that emits nothing - an
111
- * empty `$and`, an `{}` entry - must leave behind neither a dangling separator nor a clause with no
112
- * condition after it. How many terms really emit is also what decides the parentheses, which is why
113
- * the caller counts what comes back rather than what it passed in.
96
+ * Each operand rendered on its own, keeping those that emitted SQL: an empty one leaves no dangling
97
+ * separator, and the count of what emitted decides the parentheses.
114
98
  */
115
99
  renderOperands(ctx, operands, render) {
116
100
  return operands
@@ -147,14 +131,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
147
131
  placeholder(_index) {
148
132
  return '?';
149
133
  }
150
- /**
151
- * `RETURNING <id column> AS id`, or nothing at all for a composite key.
152
- *
153
- * The alias names one column, and every column of a composite came from the caller, so there is no
154
- * id the statement could report that the payload does not already carry - the same "no id to give"
155
- * a `firstId` dialect already answers with. Empty rather than a refusal, so an insert and an upsert
156
- * of a composite row both run, and the querier names those rows with `idOf`.
157
- */
134
+ /** `RETURNING <id column> id`, or nothing on a composite key, whose every column the payload already names. */
158
135
  returningId(meta) {
159
136
  const expression = this.returningIdExpression(meta);
160
137
  return expression ? `RETURNING ${expression}` : '';
@@ -524,16 +501,8 @@ export class AbstractSqlDialect extends VectorSqlDialect {
524
501
  [insensitive, { pattern, insensitive: true }],
525
502
  ]));
526
503
  /**
527
- * How this engine matches case-insensitively. One decision, not two: folding the pattern while the
528
- * comparison leaves the column alone matches neither case, which is what `$istartsWith: 'Some'`
529
- * used to do wherever `LIKE` is case-sensitive.
530
- *
531
- * - `ilike`: the engine has a case-insensitive operator (`ILIKE`), so the pattern goes through as written.
532
- * - `native`: plain `LIKE` already ignores case (SQLite, for ASCII). Folding the pattern in JS would
533
- * only break the non-ASCII characters the engine cannot fold anyway - `'É'` would become an `'é'`
534
- * that matches nothing.
535
- * - `fold`: nothing ignores case on its own, so both sides are lowered explicitly. Not indexable as
536
- * such; an expression index over `LOWER(column)` is what makes it so.
504
+ * How the engine matches case-insensitively: `ilike` has the operator, `native` ignores case already
505
+ * (SQLite, where folding in JS would break non-ASCII), and `fold` lowers both sides.
537
506
  */
538
507
  caseInsensitiveMatch = 'fold';
539
508
  /**
@@ -577,6 +546,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
577
546
  const inlined = field && isInlinedExpression(field) ? field.computed : undefined;
578
547
  return inlined ? this.rawFragment(ctx, inlined, prefix, entity) : undefined;
579
548
  }
549
+ /** One operator of a field's condition. Both come from the query as data, so neither is trusted. */
580
550
  compareFieldOperator(ctx, entity, key, op, val, opts = {}) {
581
551
  const field = this.resolveOperandField(ctx, entity, key, opts);
582
552
  if (this.appendOperatorCondition(ctx, field, op, val)) {
@@ -605,17 +575,8 @@ export class AbstractSqlDialect extends VectorSqlDialect {
605
575
  }
606
576
  }
607
577
  /**
608
- * `<operand> <op> <value>` for every operator that needs nothing but its left-hand SQL, or
609
- * `undefined` when `op` is not one of them.
610
- *
611
- * One implementation for three callers that each had their own: a WHERE column, a HAVING aggregate
612
- * expression, and a `$size` count (whose expression is already in the context, so it passes an
613
- * empty operand). They previously disagreed - HAVING carried a second comparison-operator map and
614
- * threw `unsupported HAVING operator` on the `$like` that `QueryHavingMap` accepts, and neither of
615
- * the other two turned `$eq: null` into `IS NULL` the way the WHERE path does.
616
- *
617
- * The operators kept out are the ones that need more than an operand: `$not` recurses through the
618
- * entity, and `$all`/`$size`/`$elemMatch` address a JSON document.
578
+ * `<operand> <op> <value>` for every operator that needs only its left-hand SQL, shared by a column, a
579
+ * `HAVING` expression and a `$size` count; `undefined` for the rest.
619
580
  */
620
581
  operatorCondition(ctx, operand, op, val) {
621
582
  const compareOp = AbstractSqlDialect.COMPARE_OP_MAP.get(op);
@@ -635,7 +596,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
635
596
  return this.regexCondition(operand, this.addValue(ctx, val));
636
597
  case '$in':
637
598
  case '$nin':
638
- return operand + this.formatIn(ctx, inOperands(op, val), op === '$nin');
599
+ return this.formatIn(ctx, operand, inOperands(op, val), op === '$nin');
639
600
  case '$between': {
640
601
  const [min, max] = val;
641
602
  return `${operand} BETWEEN ${this.addValue(ctx, min)} AND ${this.addValue(ctx, max)}`;
@@ -706,7 +667,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
706
667
  const values = inOperands(op, value);
707
668
  const negate = op === '$nin';
708
669
  if (!asJson) {
709
- return `${comparand(values)}${this.formatIn(ctx, values, negate)}`;
670
+ return this.formatIn(ctx, comparand(values), values, negate);
710
671
  }
711
672
  // JSON values have no portable array literal, so the set expands into explicit comparisons.
712
673
  const comparisons = values.map((val) => `${jsonField} ${negate ? '<>' : '='} ${this.jsonScalarParam(ctx, val)}`);
@@ -717,32 +678,15 @@ export class AbstractSqlDialect extends VectorSqlDialect {
717
678
  return asJson ? this.jsonScalarParam(ctx, value) : this.addValue(ctx, value);
718
679
  }
719
680
  /**
720
- * Whether the dialect's array containment ({@link jsonAll}) matches an object element that merely
721
- * *includes* the given keys, as PostgreSQL's `@>` and MySQL's `JSON_CONTAINS` do. SQLite compares
722
- * elements as whole JSON text, so it cannot express a partial match and always expands the
723
- * per-field form below.
724
- */
725
- jsonContainmentIsPartial = true;
726
- /**
727
- * Whether an exploded *scalar* element keeps its SQL type. SQLite's `JSON_EACH` yields JSON
728
- * booleans as `0`/`1` integers and numbers as numbers, so such an element compares directly to a
729
- * bound value; PostgreSQL and MySQL explode scalars to text, losing the type, so a non-string
730
- * operand there has to compare as JSON (see {@link isJsonbOp}).
731
- */
732
- jsonScalarElemKeepsType = false;
733
- /**
734
- * `$elemMatch`: at least one element of the JSON array satisfies `match`. Three shapes, decided
735
- * here so every dialect only supplies {@link jsonElemFrom} / {@link jsonElemRef}:
736
- * - keys are operators (`{ $startsWith: 'ad' }`) - scalar elements, conditions on the element;
737
- * - a plain object with no nested operators - containment, which is the only form an index serves;
738
- * - otherwise - per-field conditions over the exploded objects.
681
+ * `$elemMatch`: an element satisfies `match`. Operator keys test a scalar element; a plain object is
682
+ * containment, which an index can serve; anything else tests each exploded object's fields.
739
683
  */
740
684
  jsonElemMatch(ctx, jsonField, match) {
741
685
  // Conditions on the element itself. One `FROM` serves them all, so the element is read as JSON
742
686
  // only when *every* operand needs it - the same all-operands rule the comparison classifier uses.
743
687
  if (isOperatorOnlyObject(match)) {
744
688
  const entries = Object.entries(match);
745
- const asJson = !this.jsonScalarElemKeepsType && entries.every(([op, val]) => isJsonbOp(op, val));
689
+ const asJson = !this.features.typedJsonElements && entries.every(([op, val]) => isJsonbOp(op, val));
746
690
  const alias = ctx.claimAlias(JSON_ELEM_ALIAS);
747
691
  const conditions = entries.map(([op, val]) => this.buildJsonFieldCondition(ctx, this.elemAccessor(alias, asJson), '', op, val, asJson));
748
692
  return jsonElemExists(this.jsonElemFrom(jsonField, [], alias, asJson), conditions);
@@ -752,7 +696,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
752
696
  }
753
697
  // A plain object with no nested operators is containment, which is also the only form an index
754
698
  // can serve. SQLite compares elements exactly, so it always expands the per-field form below.
755
- if (this.jsonContainmentIsPartial && !someValue(match, isOperatorObject)) {
699
+ if (this.features.partialJsonContainment && !someValue(match, isOperatorObject)) {
756
700
  return this.jsonAll(ctx, jsonField, [match]);
757
701
  }
758
702
  const alias = ctx.claimAlias(JSON_ELEM_ALIAS);
@@ -897,26 +841,16 @@ export class AbstractSqlDialect extends VectorSqlDialect {
897
841
  ctx.append(` OFFSET ${assertNonNegativeInteger(opts.$skip, '$skip')}`);
898
842
  }
899
843
  }
900
- /** Whether this engine has row locks at all. The SQLite family locks the database instead. */
901
- supportsRowLocks = true;
902
- /**
903
- * Whether a `FOR UPDATE` may share a statement with a window function. The MySQL family runs the
904
- * pair; the Postgres family rejects it outright ("FOR UPDATE is not allowed with window functions"),
905
- * which is what a paged read carrying its own `COUNT(*) OVER ()` total becomes under a `$lock`.
906
- */
907
- supportsWindowWithRowLock = true;
908
- /** MariaDB is the one engine here that cannot narrow a lock to one table of a join. */
909
- supportsLockOf = true;
910
844
  /** Validated before the querier checks for a transaction, so the clearer error wins. */
911
845
  assertLockSupported(entity, q, joins) {
912
846
  if (!parseQueryLock(q.$lock)) {
913
847
  return;
914
848
  }
915
- if (!this.supportsRowLocks) {
849
+ if (!this.features.rowLocks) {
916
850
  throw new TypeError(`${this.dialectName} does not support row-level locking ($lock)`);
917
851
  }
918
852
  joins ??= resolveQueryJoins(getMeta(entity), q);
919
- if (!this.supportsLockOf && joins.size > 0) {
853
+ if (!this.features.rowLockOf && joins.size > 0) {
920
854
  throw new TypeError(`${this.dialectName} cannot narrow a row lock to one table, so $lock cannot be combined with a joined relation`);
921
855
  }
922
856
  }
@@ -945,30 +879,36 @@ export class AbstractSqlDialect extends VectorSqlDialect {
945
879
  const suffix = wait === 'skip' ? ' SKIP LOCKED' : wait === 'nowait' ? ' NOWAIT' : '';
946
880
  ctx.append(` FOR UPDATE${target}${suffix}`);
947
881
  }
948
- // `QueryFilter`, not `QueryPage`: a count orders nothing and pages nothing, and an `OFFSET`
949
- // would push its single row out of the result set. The type only stops a statically-checked
950
- // caller though - the HTTP handler hands this `q` straight from the wire - so `$where` is read
951
- // off it explicitly rather than forwarding `q` itself into `search()`, which would honor a
952
- // `$sort`/`$skip`/`$limit` an untyped caller snuck in regardless of what TypeScript allowed them.
882
+ /**
883
+ * `COUNT(*)` over the filter, or over the rows a page settles. The clauses are read off `q` one by one:
884
+ * `/http` hands it over untyped, and a smuggled `$sort` changes no count.
885
+ */
953
886
  count(ctx, entity, q, opts) {
954
- this.select(ctx, entity, { $select: [raw `COUNT(*)`.as(COUNT_ALIAS)] });
955
- this.search(ctx, entity, { $where: q.$where }, opts);
887
+ const { $where, $skip, $limit } = q;
888
+ if ($skip === undefined && $limit === undefined) {
889
+ this.select(ctx, entity, { $select: [raw `COUNT(*)`.as(COUNT_ALIAS)] });
890
+ this.search(ctx, entity, { $where }, opts);
891
+ return;
892
+ }
893
+ const page = idOnlyQuery(getMeta(entity), { $where, $skip, $limit });
894
+ this.countRows(ctx, () => this.find(ctx, entity, page, opts));
956
895
  }
957
896
  /**
958
- * How many rows a `$distinct` read returns, which `COUNT(*)` cannot answer: the deduplication
959
- * happens after it counts, and a window function is no better - it counts before `DISTINCT` too.
960
- * So the deduplicated set is made a derived table and its rows are counted. Every engine here
961
- * supports one; MySQL is the reason it is aliased.
962
- *
963
- * The inner query takes the projection and the filter but never the page: the caller is asking how
964
- * many rows there are beyond the page it already has.
897
+ * How many rows a `$distinct` read returns: the deduplication runs after `COUNT(*)` and a window
898
+ * alike, so the deduplicated set is counted as a derived table, never paged.
965
899
  */
966
900
  countDistinct(ctx, entity, q, opts) {
967
901
  const read = this.readOptions(ctx, getMeta(entity), opts);
902
+ this.countRows(ctx, () => {
903
+ this.select(ctx, entity, q, read);
904
+ this.search(ctx, entity, { $where: q.$where }, read);
905
+ });
906
+ }
907
+ /** `SELECT COUNT(*)` over the rows `rows` appends, as a derived table. */
908
+ countRows(ctx, rows) {
968
909
  ctx.append(`SELECT COUNT(*) ${this.escapeId(COUNT_ALIAS, true)} FROM (`);
969
- this.select(ctx, entity, q, read);
970
- this.search(ctx, entity, { $where: q.$where }, read);
971
- ctx.append(`) ${this.escapeId(DISTINCT_DERIVED_ALIAS, true)}`);
910
+ rows();
911
+ ctx.append(`) ${this.escapeId(COUNTED_ROWS_ALIAS, true)}`);
972
912
  }
973
913
  /**
974
914
  * The statistic the engine already keeps, as a `count` column. Overridden by the dialects that
@@ -1132,21 +1072,8 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1132
1072
  ctx.append(` ${returning}`);
1133
1073
  }
1134
1074
  }
1135
- /**
1136
- * Where the clause reporting an insert's generated ids goes. `suffix` is `RETURNING ...` at the end
1137
- * of the statement, which every engine here but one spells that way; SQL Server's `OUTPUT` has no
1138
- * trailing form and sits between the column list and `VALUES`.
1139
- *
1140
- * A knob rather than a pair of hooks: one concept decides where the string {@link returningId}
1141
- * already built ends up, so the two ends cannot disagree.
1142
- */
1075
+ /** Where an insert's id clause goes: `RETURNING` at the end, or SQL Server's `OUTPUT` before `VALUES`. */
1143
1076
  returningPosition = 'suffix';
1144
- /**
1145
- * Whether a multi-row upsert's `RETURNING` lists its rows in payload order. Where it does not, the
1146
- * ids are read back by the conflict columns instead, since placing them in order would name the
1147
- * wrong rows.
1148
- */
1149
- upsertReturningOrdered = true;
1150
1077
  /**
1151
1078
  * `INSERT INTO ... VALUES (...)` and nothing more. The upsert builders extend this rather than
1152
1079
  * {@link insert}: their own clause has to come before the `RETURNING`, not after it.
@@ -1159,13 +1086,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1159
1086
  ctx.append(`INSERT INTO ${tableName} (${shape.columns.join(', ')})${afterTarget ? ` ${afterTarget}` : ''} VALUES `);
1160
1087
  this.appendValueRows(ctx, shape);
1161
1088
  }
1162
- /**
1163
- * The columns an insert writes and the records it writes them from, resolved once.
1164
- *
1165
- * Split out of {@link appendInsertValues} because a `MERGE` needs the same rows as a `VALUES` row
1166
- * source rather than as an `INSERT`, and both have to apply `onInsert` defaults and the
1167
- * JSON/vector binding rules identically.
1168
- */
1089
+ /** The columns an insert writes and the rows it writes, resolved once, and shared with a `MERGE`'s row source. */
1169
1090
  insertShape(entity, payload) {
1170
1091
  const meta = getMeta(entity);
1171
1092
  const payloads = fillOnFields(meta, payload, 'onInsert');
@@ -1243,15 +1164,8 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1243
1164
  this.search(ctx, entity, q, opts);
1244
1165
  }
1245
1166
  /**
1246
- * `INSERT ... ON CONFLICT (...) DO UPDATE/NOTHING RETURNING ...`, which SQLite adopted from Postgres
1247
- * and which every dialect here speaks except the MySQL family (see {@link MysqlLikeSqlDialect}).
1248
- *
1249
- * Two orderings matter, and they pull in opposite directions. The assignments are computed *before*
1250
- * the insert, because `appendInsertValues` fills `onInsert` fields into the payload and a column that
1251
- * exists only there - `createdAt` - must not join the update set. Their bound values are pushed
1252
- * *after* it, because a `?` placeholder is positional and the clause comes last in the statement.
1253
- * {@link PgLikeSqlDialect} overrides this: `$N` placeholders make array order irrelevant, so it can
1254
- * bind into the main context and skip the second one.
1167
+ * `INSERT ... ON CONFLICT ... DO UPDATE/NOTHING RETURNING`. The assignments are built before the insert
1168
+ * fills `onInsert` columns, which must stay out of them, and their values bound after it, where a `?` reads them.
1255
1169
  */
1256
1170
  upsert(ctx, entity, conflictPaths, payload,
1257
1171
  /** One more `RETURNING` item, as a bare expression: this joins the list and adds the keyword. */
@@ -1270,14 +1184,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1270
1184
  ctx.pushValue(...updateCtx.values);
1271
1185
  }
1272
1186
  }
1273
- /**
1274
- * Whether the upsert's update assignments can bind straight into the statement's own context.
1275
- *
1276
- * They cannot on a `?`-placeholder dialect: the assignments are built before the insert but read
1277
- * after it, so their values have to be pushed afterwards to land in the right positional order.
1278
- * A `$n` placeholder carries its own index, so there is nothing to reorder - but it also cannot use
1279
- * the scratch context, whose numbering would restart at `$1` and collide with the insert's.
1280
- */
1187
+ /** Whether the upsert's assignments bind straight into the statement, as numbered `$n` placeholders can. */
1281
1188
  upsertUpdateBindsInPlace = false;
1282
1189
  /** How an `ON CONFLICT` assignment reads the row that was being inserted. */
1283
1190
  upsertExcluded = (columnName) => `EXCLUDED.${columnName}`;
@@ -1291,7 +1198,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1291
1198
  .map((col) => {
1292
1199
  const field = meta.fields[col];
1293
1200
  const columnName = this.resolveColumnName(col, field);
1294
- if (callback && Object.hasOwn(sample, col)) {
1201
+ if (Object.hasOwn(sample, col)) {
1295
1202
  return `${this.escapeId(columnName)} = ${callback(this.escapeId(columnName))}`;
1296
1203
  }
1297
1204
  const text = this.buildFragment(ctx, (fragmentCtx) => this.formatPersistableValue(fragmentCtx, field, filledPayload[col]));
@@ -1347,35 +1254,15 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1347
1254
  formatPersistableValue(ctx, field, value) {
1348
1255
  this.writePersistableValue(ctx, this.persistKind(field), field, value);
1349
1256
  }
1350
- /**
1351
- * How a column's values are written. A function of the column, not of the value, so a bulk insert
1352
- * classifies each column once instead of re-deciding per row: a 20-row, 6-column insert used to ask
1353
- * 120 times to get the same six answers.
1354
- */
1257
+ /** How a column's values are written: a function of the column, so a bulk insert classifies each once. */
1355
1258
  persistKind(field) {
1356
1259
  const family = columnFamily(field?.type);
1357
1260
  return family === 'json' || family === 'vector' ? family : 'plain';
1358
1261
  }
1359
1262
  /**
1360
- * Which of an entity's columns need decoding on READ, and how: the inverse of {@link persistKind},
1361
- * cached per entity for the same reason it classifies per column. A 1000-row read of a 10-field
1362
- * entity otherwise asks {@link columnFamily} (which lowercases a string on every call) 10,000 times
1363
- * to get the same ten answers. Most entities land here for their numeric columns alone, where the
1364
- * per-row cost is one `typeof` against a value the driver usually decoded already.
1365
- *
1366
- * Dialect-aware exactly like {@link supportedVectorType}, because it has to be: a `sparsevec` field
1367
- * is written as a plain dense vector everywhere but Postgres, so reading it back by the field's own
1368
- * declared cast would look for a sparse literal that was never stored.
1369
- *
1370
- * A type lands here rather than at the driver when the wire type alone cannot decide it, and only
1371
- * the declaration can: `Boolean` is 0/1 in a SQLite INTEGER and a MySQL `TINYINT(1)`, both
1372
- * indistinguishable from a genuine small integer; a decimal is text from pg *and* mysql2, and only
1373
- * the field says it was meant as a number; and `type: BigInt` shares BIGINT with `type: Number`, so
1374
- * the wire decode has to be undone for it. All are no-ops where the driver already decoded.
1375
- *
1376
- * Classified through the same {@link columnFamily} the rest of the library uses, not against the
1377
- * constructors: `type` accepts a string logical type for every one of these (`@Field({ type:
1378
- * 'decimal' })`), and matching `=== Number` alone left those reading back as text.
1263
+ * The columns a read decodes, and how, cached per entity and revision. They are the ones the wire cannot
1264
+ * decide alone: a boolean stored as an integer, a decimal read as text, a `BigInt`, and a related row's
1265
+ * values, which cross JSON as text.
1379
1266
  */
1380
1267
  hydratableFields(entity) {
1381
1268
  const meta = getMeta(entity);
@@ -1396,14 +1283,8 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1396
1283
  return decoded;
1397
1284
  }
1398
1285
  /**
1399
- * The same classification for an aggregate row. Not cached, because these columns are a shape of the
1400
- * query rather than of the entity, and it is computed once per call either way.
1401
- *
1402
- * Mirrors `QueryAggregateFnResult`, which is the contract callers already compile against:
1403
- * `$count`/`$sum`/`$avg` are a number whatever they aggregate, while `$min`/`$max` and every
1404
- * `$group` column keep the aggregated field's own type, so they decode as that field would. Without
1405
- * it a `$sum` over a BIGINT column came back as `'500'` from a result type that says `number`, since
1406
- * Postgres widens that sum to NUMERIC and no driver can know it was meant as a JS number.
1286
+ * The same for an aggregate's row, per query: a count or total is a number however the engine widened
1287
+ * it, and `$min`/`$max` or a grouped column decodes as its field does.
1407
1288
  */
1408
1289
  hydratableAggregates(entity, q) {
1409
1290
  const { fields } = getMeta(entity);
@@ -1423,13 +1304,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1423
1304
  }
1424
1305
  return decoded;
1425
1306
  }
1426
- /**
1427
- * The mirror of {@link persistKind}: what one column decodes as, or nothing if it needs no decode. A
1428
- * date and bytes decode because a related row crosses JSON, which spells both as text.
1429
- *
1430
- * `BigInt` is asked first because it shares the numeric family with `Number`: let the switch answer
1431
- * it and every `type: BigInt` property silently decodes to a JS number again.
1432
- */
1307
+ /** What one column decodes as, the inverse of {@link persistKind}. `BigInt` first, since it shares the numeric family. */
1433
1308
  hydrateKind(field) {
1434
1309
  const type = field?.type;
1435
1310
  if (type === BigInt) {
@@ -1481,17 +1356,8 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1481
1356
  return `CAST(${operand} AS JSON)`;
1482
1357
  }
1483
1358
  /**
1484
- * Generate the full `"col" = <expression>` assignment for a JSON update operator payload.
1485
- * Called from `update()` when a field value is a {@link JsonUpdateOp}.
1486
- *
1487
- * Each operator wraps the expression built so far, innermost-first in the order stated on
1488
- * {@link JsonUpdateOp} (`$pull` -> `$set` -> `$push` -> `$unset`), so dialects only supply the
1489
- * four SQL fragments below. Two invariants keep every dialect consistent and keep bound values in
1490
- * step with their placeholders:
1491
- * - `$pull` is innermost and its subquery reads `escapedCol`, so its value binds exactly once.
1492
- * - Later fragments reference `expr` at most once, so a `$pull` subquery is never duplicated
1493
- * (which would bind its value twice on positional-placeholder dialects). PostgreSQL's `$push`
1494
- * is the one exception, and is safe there because its placeholders are numbered.
1359
+ * `"col" = <expr>` for a JSON update, each operator wrapping the last: `$pull`, `$set`, `$push`, `$unset`.
1360
+ * `$pull` reads the column and every later one its expression once, so no value binds twice.
1495
1361
  */
1496
1362
  formatJsonUpdate(ctx, escapedCol, value, field) {
1497
1363
  // Centralizes the one narrowing cast: the payload's keys are typed against the entity's JSON
@@ -1778,11 +1644,6 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1778
1644
  target: this.escapedColumn(alias, junctionMeta, target),
1779
1645
  };
1780
1646
  }
1781
- /**
1782
- * Whether the engine's JSON aggregate takes an `ORDER BY` of its own. Where it does not, a relation's
1783
- * rows carry no sort term out and keep their own order, which a derived table hands its aggregate.
1784
- */
1785
- orderedAggregates = true;
1786
1647
  /**
1787
1648
  * The rows read as a derived table, their values crossing JSON and, where the aggregate orders, each
1788
1649
  * sort term carried out beside them for it to order by, since a derived table's order is not promised
@@ -1790,7 +1651,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1790
1651
  */
1791
1652
  derivedRelation(ctx, rows) {
1792
1653
  const rowsCtx = ctx.createFragment();
1793
- const readOpts = { alias: rows.alias, json: true, carried: this.orderedAggregates };
1654
+ const readOpts = { alias: rows.alias, json: true, carried: this.features.orderedJsonAggregates };
1794
1655
  const { terms, order = [] } = this.read(rowsCtx, rows.entity, rows.query, readOpts, rows.joins);
1795
1656
  const alias = this.escapeId(rows.alias, true);
1796
1657
  return {
@@ -1861,13 +1722,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1861
1722
  }
1862
1723
  this.buildExprComparison(ctx, sizeExprFn, sizeVal, (op, val) => this.appendSizeOp(ctx, op, val));
1863
1724
  }
1864
- /**
1865
- * `<distance expr> <op> ?` - the `$where` half of vector search, where `$sort` is the ranking half.
1866
- *
1867
- * The bounds are validated here rather than left to the shared renderer, which also knows `$like`
1868
- * and `$in`; and `$eq`/`$ne` are absent on purpose, since a distance is a float. `/http` casts
1869
- * client JSON straight to `Query`, so an unknown key has to be refused rather than ignored.
1870
- */
1725
+ /** `<distance> <op> ?`, the `$where` half of a vector search, its bounds checked here since `/http` input is untyped. */
1871
1726
  compareVectorNear(ctx, meta, key, near) {
1872
1727
  const bounds = {};
1873
1728
  for (const [op, val] of Object.entries(near)) {
@@ -1903,14 +1758,7 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1903
1758
  '$lte',
1904
1759
  '$between',
1905
1760
  ]);
1906
- /**
1907
- * Append a single size comparison operator and value. No operand: the count expression is already
1908
- * in the context, so this contributes only the ` <op> <value>` tail.
1909
- *
1910
- * Gated on {@link SIZE_COMPARE_OPS} rather than on whatever the shared renderer accepts, because
1911
- * that renderer also knows `$like`, `$regex` and `$in`, none of which mean anything against a
1912
- * count. `$size: { $like: 5 }` has to stay the error it always was.
1913
- */
1761
+ /** ` <op> <value>` after a count already written, refusing any operator a count cannot be compared with. */
1914
1762
  appendSizeOp(ctx, op, val) {
1915
1763
  if (!AbstractSqlDialect.SIZE_COMPARE_OPS.has(op)) {
1916
1764
  throw TypeError(`unsupported $size comparison operator: ${op}`);
@@ -1952,15 +1800,13 @@ export class AbstractSqlDialect extends VectorSqlDialect {
1952
1800
  neExpr(field, ph) {
1953
1801
  return `${field} ${this.neOp} ${ph}`;
1954
1802
  }
1955
- /**
1956
- * Formats an IN/NOT IN expression, binding each value individually.
1957
- * Postgres overrides to use `= ANY($1)` / `<> ALL($1)` with a single array parameter.
1958
- */
1959
- formatIn(ctx, values, negate) {
1960
- if (values.length === 0)
1961
- return negate ? ' NOT IN (NULL)' : ' IN (NULL)';
1803
+ /** `operand IN (...)` binding each value, or the constant an empty set reduces to: no value is in it. */
1804
+ formatIn(ctx, operand, values, negate) {
1805
+ if (!values.length) {
1806
+ return negate ? '1 = 1' : '1 = 0';
1807
+ }
1962
1808
  const phs = values.map((v) => this.addValue(ctx, v)).join(', ');
1963
- return ` ${negate ? 'NOT IN' : 'IN'} (${phs})`;
1809
+ return `${operand} ${negate ? 'NOT IN' : 'IN'} (${phs})`;
1964
1810
  }
1965
1811
  toString() {
1966
1812
  return this.dialectName;
@@ -1,19 +1,9 @@
1
- /**
2
- * Every identifier UQL invents for itself: a column a statement answers in, a derived table it wraps
3
- * a set in, a temporary field a pipeline parks a value on.
4
- *
5
- * All of them share the `_uql` prefix, which is what keeps them off a user's own column or field, and
6
- * all of them are declared here rather than beside the code that emits them: the end that writes one
7
- * and the end that reads it back are usually in different modules, and a drift between the two fails
8
- * silently - a count of zero, or an ordering that ranks everything equal. Collected in one file so
9
- * the whole reserved namespace can be read at a glance before a new name is added to it.
10
- */
11
1
  /** The column every internally-built count answers in: `COUNT(*)`, a grouped tally, a `$count` stage. */
12
2
  export declare const COUNT_ALIAS = "_uql_count";
13
3
  /** The column a paged read carries its own unpaged total in, from `COUNT(*) OVER ()`. */
14
4
  export declare const TOTAL_ALIAS = "_uql_total";
15
- /** The derived table a `$distinct` count wraps its deduplicated set in. MySQL requires the alias. */
16
- export declare const DISTINCT_DERIVED_ALIAS = "_uql_distinct";
5
+ /** The derived table a count wraps the rows it counts in: a page, or a `$distinct` set. MySQL requires the alias. */
6
+ export declare const COUNTED_ROWS_ALIAS = "_uql_rows";
17
7
  /** The row a Postgres relation aggregates whole: a LATERAL projection of the columns it answers under. */
18
8
  export declare const RELATION_ROW_ALIAS = "_uql_row";
19
9
  /** The alias an exploded JSON array element is read through, `_uql_elem_2` and on where one nests in another. */
@@ -1,19 +1,11 @@
1
- /**
2
- * Every identifier UQL invents for itself: a column a statement answers in, a derived table it wraps
3
- * a set in, a temporary field a pipeline parks a value on.
4
- *
5
- * All of them share the `_uql` prefix, which is what keeps them off a user's own column or field, and
6
- * all of them are declared here rather than beside the code that emits them: the end that writes one
7
- * and the end that reads it back are usually in different modules, and a drift between the two fails
8
- * silently - a count of zero, or an ordering that ranks everything equal. Collected in one file so
9
- * the whole reserved namespace can be read at a glance before a new name is added to it.
10
- */
1
+ // Every identifier UQL invents, `_uql`-prefixed to stay off a user's own, collected in one place:
2
+ // the ends writing and reading one sit in different modules, and a drift between them fails silently.
11
3
  /** The column every internally-built count answers in: `COUNT(*)`, a grouped tally, a `$count` stage. */
12
4
  export const COUNT_ALIAS = '_uql_count';
13
5
  /** The column a paged read carries its own unpaged total in, from `COUNT(*) OVER ()`. */
14
6
  export const TOTAL_ALIAS = '_uql_total';
15
- /** The derived table a `$distinct` count wraps its deduplicated set in. MySQL requires the alias. */
16
- export const DISTINCT_DERIVED_ALIAS = '_uql_distinct';
7
+ /** The derived table a count wraps the rows it counts in: a page, or a `$distinct` set. MySQL requires the alias. */
8
+ export const COUNTED_ROWS_ALIAS = '_uql_rows';
17
9
  /** The row a Postgres relation aggregates whole: a LATERAL projection of the columns it answers under. */
18
10
  export const RELATION_ROW_ALIAS = '_uql_row';
19
11
  /** The alias an exploded JSON array element is read through, `_uql_elem_2` and on where one nests in another. */
@@ -7,12 +7,8 @@ import { type VectorCast } from './vectorCast.js';
7
7
  */
8
8
  export type HydrateKind = 'json' | 'boolean' | 'number' | 'bigint' | 'date' | 'bytes' | VectorCast;
9
9
  /**
10
- * Decode one non-null cell. Kept beside {@link HydrateKind} rather than inlined into the querier's
11
- * row walk, so classifying a column and decoding it stay one subject in one file.
12
- *
13
- * Every branch is a no-op on a value the driver already decoded, because which types arrive as text
14
- * varies per driver and the entity is the only thing that says what they were meant to be. A value
15
- * that does not match its column's format is returned untouched rather than replaced by a guess.
10
+ * Decodes one non-null cell. A no-op where the driver already decoded it, since that varies per driver,
11
+ * and untouched where it does not match its column's format.
16
12
  */
17
13
  export declare function decodeColumn(value: unknown, kind: HydrateKind): unknown;
18
14
  /**
@@ -1,12 +1,8 @@
1
1
  import { decodeWideNumber } from '../util/wideNumber.js';
2
2
  import { parseVectorLiteral } from './vectorCast.js';
3
3
  /**
4
- * Decode one non-null cell. Kept beside {@link HydrateKind} rather than inlined into the querier's
5
- * row walk, so classifying a column and decoding it stay one subject in one file.
6
- *
7
- * Every branch is a no-op on a value the driver already decoded, because which types arrive as text
8
- * varies per driver and the entity is the only thing that says what they were meant to be. A value
9
- * that does not match its column's format is returned untouched rather than replaced by a guess.
4
+ * Decodes one non-null cell. A no-op where the driver already decoded it, since that varies per driver,
5
+ * and untouched where it does not match its column's format.
10
6
  */
11
7
  export function decodeColumn(value, kind) {
12
8
  if (kind === 'boolean') {
@@ -79,13 +75,7 @@ function hexBytes(hex) {
79
75
  }
80
76
  /** Lazy so a consumer that never reads an encoded column never constructs one. */
81
77
  let decoder;
82
- /**
83
- * The text a driver returned, or `undefined` when it returned something already decoded.
84
- *
85
- * Bytes count as text: `bun:sql` hands a MySQL DECIMAL, and any `SUM` over one, back as a `Buffer`,
86
- * so a string-only check left those as raw bytes. `TextDecoder` rather than `Buffer.toString`, because
87
- * this module is reachable from the browser entry and may not name a Node builtin.
88
- */
78
+ /** The text a driver returned, bytes included (`bun:sql` hands a MySQL decimal as a `Buffer`), or `undefined`. */
89
79
  function asText(value) {
90
80
  if (typeof value === 'string') {
91
81
  return value;
@@ -1,8 +1,2 @@
1
- /**
2
- * Expands an `$elemMatch` object into per-field conditions: a field whose value is an operator
3
- * object contributes one condition per operator, and a plain value contributes an `$eq`.
4
- *
5
- * Treating plain equality as `$eq` is what keeps `{ count: 5 }` and `{ count: { $eq: 5 } }`
6
- * identical - they used to take different code paths and emit different SQL.
7
- */
1
+ /** An `$elemMatch` as per-field conditions, a plain value as `$eq`, so both spellings emit the same SQL. */
8
2
  export declare function buildElemMatchConditions(match: Record<string, unknown>, onCondition: (field: string, op: string, value: unknown) => string): string[];
@@ -1,11 +1,5 @@
1
1
  import { isOperatorObject } from '../util/object.util.js';
2
- /**
3
- * Expands an `$elemMatch` object into per-field conditions: a field whose value is an operator
4
- * object contributes one condition per operator, and a plain value contributes an `$eq`.
5
- *
6
- * Treating plain equality as `$eq` is what keeps `{ count: 5 }` and `{ count: { $eq: 5 } }`
7
- * identical - they used to take different code paths and emit different SQL.
8
- */
2
+ /** An `$elemMatch` as per-field conditions, a plain value as `$eq`, so both spellings emit the same SQL. */
9
3
  export function buildElemMatchConditions(match, onCondition) {
10
4
  return Object.entries(match).flatMap(([field, value]) => isOperatorObject(value)
11
5
  ? Object.entries(value).map(([op, opVal]) => onCondition(field, op, opVal))