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