uql-orm 0.65.1 → 0.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/querier/httpQuerier.js +1 -8
- package/dist/browser/uql-browser.min.js.map +5 -5
- package/dist/bunSql/bunSql.util.d.ts +2 -6
- package/dist/bunSql/bunSql.util.js +2 -6
- package/dist/bunSql/bunSqlQuerier.d.ts +2 -5
- package/dist/bunSql/bunSqlQuerier.js +2 -5
- package/dist/cockroachdb/cockroachDialect.d.ts +4 -13
- package/dist/cockroachdb/cockroachDialect.js +4 -13
- package/dist/context/context.browser.js +2 -10
- package/dist/context/context.d.ts +4 -17
- package/dist/context/context.js +4 -17
- package/dist/dialect/abstractDialect.d.ts +4 -19
- package/dist/dialect/abstractDialect.js +2 -20
- package/dist/dialect/abstractSqlDialect.d.ts +47 -212
- package/dist/dialect/abstractSqlDialect.js +68 -222
- package/dist/dialect/aliases.d.ts +2 -12
- package/dist/dialect/aliases.js +4 -12
- package/dist/dialect/hydrateColumn.d.ts +2 -6
- package/dist/dialect/hydrateColumn.js +3 -13
- package/dist/dialect/jsonArrayElemMatchUtils.d.ts +1 -7
- package/dist/dialect/jsonArrayElemMatchUtils.js +1 -7
- package/dist/dialect/jsonSql.d.ts +6 -27
- package/dist/dialect/jsonSql.js +6 -27
- package/dist/dialect/mergeSqlDialect.d.ts +4 -22
- package/dist/dialect/mergeSqlDialect.js +4 -22
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +11 -37
- package/dist/dialect/mysqlLikeSqlDialect.js +35 -51
- package/dist/dialect/pgLikeSqlDialect.d.ts +8 -22
- package/dist/dialect/pgLikeSqlDialect.js +36 -39
- package/dist/dialect/queryContext.d.ts +4 -22
- package/dist/dialect/queryContext.js +4 -22
- package/dist/dialect/queryJoins.d.ts +3 -12
- package/dist/dialect/queryJoins.js +3 -12
- package/dist/dialect/vectorCast.d.ts +2 -12
- package/dist/dialect/vectorCast.js +3 -19
- package/dist/dialect/vectorSqlDialect.d.ts +8 -38
- package/dist/dialect/vectorSqlDialect.js +7 -38
- package/dist/entity/decorator/bag.d.ts +6 -19
- package/dist/entity/decorator/bag.js +6 -22
- package/dist/entity/decorator/entity.d.ts +5 -10
- package/dist/entity/decorator/entity.js +2 -7
- package/dist/entity/decorator/members.d.ts +10 -31
- package/dist/entity/decorator/members.js +3 -12
- package/dist/entity/metadata/definition.d.ts +5 -21
- package/dist/entity/metadata/definition.js +69 -91
- package/dist/http/handler.d.ts +2 -14
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/libsql/libsqlDialect.d.ts +1 -8
- package/dist/libsql/libsqlDialect.js +1 -8
- package/dist/maria/mariaDialect.d.ts +3 -5
- package/dist/maria/mariaDialect.js +5 -5
- package/dist/maria/mariadbQuerier.js +2 -2
- package/dist/maria/mariadbQuerierPool.js +1 -6
- package/dist/migrate/builder/migrationBuilder.js +3 -19
- package/dist/migrate/builder/splitSqlStatements.d.ts +1 -14
- package/dist/migrate/builder/splitSqlStatements.js +2 -22
- package/dist/migrate/builder/types.d.ts +2 -15
- package/dist/migrate/cli-config.js +2 -11
- package/dist/migrate/cli.js +2 -7
- package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -15
- package/dist/migrate/codegen/entityCodeGenerator.js +15 -44
- package/dist/migrate/codegen/fieldOptionsSource.d.ts +1 -8
- package/dist/migrate/codegen/fieldOptionsSource.js +3 -22
- package/dist/migrate/ddl/indexDdl.d.ts +2 -5
- package/dist/migrate/ddl/indexDdl.js +2 -5
- package/dist/migrate/ddl/pgIndexDdl.d.ts +3 -13
- package/dist/migrate/ddl/pgIndexDdl.js +3 -13
- package/dist/migrate/generator/definitionToNode.d.ts +2 -9
- package/dist/migrate/generator/definitionToNode.js +3 -17
- package/dist/migrate/generator/indexNodeToSchema.d.ts +2 -3
- package/dist/migrate/generator/indexNodeToSchema.js +2 -3
- package/dist/migrate/generator/mongoCommand.d.ts +1 -8
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +1 -8
- package/dist/migrate/generator/mongoSchemaGenerator.js +1 -8
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +6 -26
- package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +9 -41
- package/dist/migrate/introspection/baseSqlIntrospector.js +0 -1
- package/dist/migrate/introspection/mongoIntrospector.d.ts +3 -1
- package/dist/migrate/introspection/mongoIntrospector.js +48 -46
- package/dist/migrate/introspection/mssqlIntrospector.d.ts +4 -4
- package/dist/migrate/introspection/mssqlIntrospector.js +18 -27
- package/dist/migrate/introspection/mysqlIntrospector.d.ts +7 -2
- package/dist/migrate/introspection/mysqlIntrospector.js +16 -14
- package/dist/migrate/introspection/postgresIntrospector.d.ts +24 -9
- package/dist/migrate/introspection/postgresIntrospector.js +68 -59
- package/dist/migrate/introspection/sqliteIntrospector.d.ts +1 -1
- package/dist/migrate/introspection/sqliteIntrospector.js +8 -10
- package/dist/migrate/migrator.d.ts +9 -53
- package/dist/migrate/migrator.js +32 -65
- package/dist/migrate/schemaGenerator.d.ts +20 -66
- package/dist/migrate/schemaGenerator.js +32 -93
- package/dist/mongo/mongoDialect.d.ts +21 -53
- package/dist/mongo/mongoDialect.js +25 -70
- package/dist/mongo/mongodbQuerier.d.ts +5 -8
- package/dist/mongo/mongodbQuerier.js +31 -65
- package/dist/mssql/mssqlDialect.d.ts +8 -34
- package/dist/mssql/mssqlDialect.js +37 -51
- package/dist/mssql/mssqlQuerier.d.ts +37 -4
- package/dist/mssql/mssqlQuerier.js +2 -2
- package/dist/mssql/mssqlWireTypes.d.ts +2 -14
- package/dist/mssql/mssqlWireTypes.js +2 -14
- package/dist/nestjs/uqlModule.js +2 -7
- package/dist/pglite/pgliteQuerier.d.ts +1 -9
- package/dist/pglite/pgliteQuerierPool.d.ts +4 -26
- package/dist/pglite/pgliteQuerierPool.js +3 -18
- package/dist/postgres/abstractPgQuerierPool.d.ts +1 -8
- package/dist/postgres/abstractPgQuerierPool.js +1 -8
- package/dist/postgres/pgNumericTypes.d.ts +3 -26
- package/dist/postgres/pgNumericTypes.js +3 -26
- package/dist/postgres/postgresDialect.d.ts +4 -10
- package/dist/postgres/postgresDialect.js +4 -10
- package/dist/querier/abstractQuerier.d.ts +35 -103
- package/dist/querier/abstractQuerier.js +105 -201
- package/dist/querier/abstractSharedHandleQuerierPool.d.ts +3 -17
- package/dist/querier/abstractSharedHandleQuerierPool.js +3 -17
- package/dist/querier/abstractSqlQuerier.d.ts +15 -36
- package/dist/querier/abstractSqlQuerier.js +49 -131
- package/dist/schema/canonicalType.d.ts +3 -21
- package/dist/schema/canonicalType.js +22 -67
- package/dist/schema/dependencyGraph.d.ts +2 -8
- package/dist/schema/dependencyGraph.js +2 -32
- package/dist/schema/index.d.ts +1 -25
- package/dist/schema/index.js +0 -26
- package/dist/schema/indexColumns.d.ts +1 -8
- package/dist/schema/indexColumns.js +1 -8
- package/dist/schema/indexDifferences.d.ts +7 -40
- package/dist/schema/indexDifferences.js +6 -31
- package/dist/schema/schemaAST.d.ts +8 -175
- package/dist/schema/schemaAST.js +13 -365
- package/dist/schema/schemaASTBuilder.d.ts +2 -24
- package/dist/schema/schemaASTBuilder.js +6 -41
- package/dist/schema/schemaASTDiffer.d.ts +6 -46
- package/dist/schema/schemaASTDiffer.js +8 -56
- package/dist/schema/types.d.ts +5 -61
- package/dist/schema/types.js +3 -6
- package/dist/sqlite/abstractSqliteQuerier.d.ts +1 -8
- package/dist/sqlite/localSqliteQuerierPool.d.ts +1 -7
- package/dist/sqlite/localSqliteQuerierPool.js +1 -7
- package/dist/sqlite/nodeSqliteQuerierPool.d.ts +2 -7
- package/dist/sqlite/nodeSqliteQuerierPool.js +2 -7
- package/dist/sqlite/sqliteDialect.d.ts +5 -20
- package/dist/sqlite/sqliteDialect.js +29 -35
- package/dist/turso/tursoDialect.d.ts +4 -6
- package/dist/turso/tursoDialect.js +4 -6
- package/dist/turso/tursoLocalQuerierPool.d.ts +1 -7
- package/dist/turso/tursoLocalQuerierPool.js +1 -7
- package/dist/turso/tursoQuerierPool.d.ts +2 -6
- package/dist/turso/tursoQuerierPool.js +2 -6
- package/dist/turso/tursoSessionQuerier.d.ts +1 -7
- package/dist/turso/tursoSessionQuerier.js +1 -7
- package/dist/type/dialect.d.ts +42 -94
- package/dist/type/dialect.js +3 -13
- package/dist/type/entity.d.ts +189 -551
- package/dist/type/entity.js +26 -9
- package/dist/type/logger.d.ts +2 -14
- package/dist/type/migration.d.ts +9 -38
- package/dist/type/querier.d.ts +9 -28
- package/dist/type/querierPool.d.ts +4 -26
- package/dist/type/query.d.ts +28 -78
- package/dist/type/query.js +2 -7
- package/dist/type/queryAggregate.d.ts +18 -98
- package/dist/type/queryRaw.d.ts +1 -8
- package/dist/type/queryRaw.js +1 -8
- package/dist/type/queryWhere.d.ts +13 -61
- package/dist/type/universalQuerier.d.ts +18 -105
- package/dist/type/utility.d.ts +12 -24
- package/dist/type/vector.d.ts +8 -38
- package/dist/type/vector.js +1 -1
- package/dist/type/wire.d.ts +2 -5
- package/dist/util/dialect.util.d.ts +9 -27
- package/dist/util/dialect.util.js +10 -27
- package/dist/util/field.util.d.ts +5 -37
- package/dist/util/field.util.js +7 -50
- package/dist/util/fieldOption.util.d.ts +7 -15
- package/dist/util/fieldOption.util.js +1 -1
- package/dist/util/filters.util.d.ts +2 -5
- package/dist/util/filters.util.js +2 -5
- package/dist/util/logger.d.ts +2 -6
- package/dist/util/logger.js +2 -6
- package/dist/util/object.util.d.ts +2 -6
- package/dist/util/object.util.js +1 -5
- package/dist/util/raw.d.ts +3 -23
- package/dist/util/relationQuery.util.d.ts +3 -14
- package/dist/util/relationQuery.util.js +3 -14
- package/dist/util/rowKey.util.d.ts +2 -10
- package/dist/util/rowKey.util.js +2 -10
- package/dist/util/sql.util.d.ts +6 -37
- package/dist/util/sql.util.js +13 -73
- package/dist/util/sqlLiteral.d.ts +2 -13
- package/dist/util/sqlLiteral.js +8 -13
- package/dist/util/string.util.js +0 -2
- 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,
|
|
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,
|
|
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
|
-
*
|
|
91
|
-
*
|
|
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
|
|
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
|
|
528
|
-
*
|
|
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
|
|
609
|
-
* `
|
|
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
|
|
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
|
|
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
|
-
*
|
|
721
|
-
*
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
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
|
-
|
|
955
|
-
|
|
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
|
|
959
|
-
*
|
|
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
|
-
|
|
970
|
-
|
|
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
|
|
1247
|
-
*
|
|
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 (
|
|
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
|
-
*
|
|
1361
|
-
*
|
|
1362
|
-
*
|
|
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
|
|
1400
|
-
*
|
|
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
|
-
*
|
|
1485
|
-
*
|
|
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.
|
|
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
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
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
|
|
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
|
|
16
|
-
export declare const
|
|
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. */
|
package/dist/dialect/aliases.js
CHANGED
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
|
16
|
-
export const
|
|
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
|
-
*
|
|
11
|
-
*
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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))
|