@atscript/db 0.1.153 → 0.1.155
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/agg.d.cts +1 -1
- package/dist/agg.d.mts +1 -1
- package/dist/{buckets-jy8BfswO.d.cts → buckets-DcWm3SLt.d.cts} +120 -10
- package/dist/{buckets-CBKL_ZWE.d.mts → buckets-goCoky8v.d.mts} +120 -10
- package/dist/{column-diff-DrE4ho_m.mjs → column-diff-C68V_eYM.mjs} +348 -69
- package/dist/{column-diff-CbdWi_uZ.cjs → column-diff-DMkGYAGM.cjs} +359 -68
- package/dist/{fk-diff-BIJ8l_hF.d.mts → fk-diff-BYLNWoF9.d.mts} +2 -1
- package/dist/{fk-diff-D1mrLMjU.d.cts → fk-diff-DblOPBLS.d.cts} +2 -1
- package/dist/index.cjs +2 -2
- package/dist/index.d.cts +10 -5
- package/dist/index.d.mts +10 -5
- package/dist/index.mjs +2 -2
- package/dist/plugin.cjs +3 -3
- package/dist/plugin.mjs +3 -3
- package/dist/rel.d.cts +2 -2
- package/dist/rel.d.mts +2 -2
- package/dist/{relation-helpers-BUsxNNGU.d.mts → relation-helpers-B9MVBrtx.d.mts} +1 -1
- package/dist/{relation-helpers-D_8bydc2.d.cts → relation-helpers-D9kMFfum.d.cts} +1 -1
- package/dist/shared.cjs +2 -1
- package/dist/shared.d.cts +10 -3
- package/dist/shared.d.mts +10 -3
- package/dist/shared.mjs +2 -2
- package/dist/sync.cjs +288 -30
- package/dist/sync.d.cts +34 -5
- package/dist/sync.d.mts +34 -5
- package/dist/sync.mjs +288 -30
- package/dist/{validation-utils-DqZAH6aT.mjs → validation-utils-DxHIqa36.mjs} +23 -5
- package/dist/{validation-utils-debIYpq0.cjs → validation-utils-_PA7iSLm.cjs} +28 -4
- package/dist/{validator-BACewoRj.mjs → validator-BC3nwFi4.mjs} +178 -10
- package/dist/{validator-BxWwh3wf.cjs → validator-DHwSyBej.cjs} +201 -9
- package/dist/validator.cjs +1 -1
- package/dist/validator.mjs +1 -1
- package/package.json +6 -6
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
const require_db_error = require("./db-error-JcfJqqpk.cjs");
|
|
2
2
|
const require_aggregate_fns = require("./aggregate-fns-DFtKAv7Q.cjs");
|
|
3
3
|
const require_nested_writer = require("./nested-writer-Bl1SZS7x.cjs");
|
|
4
|
+
const require_validator = require("./validator-DHwSyBej.cjs");
|
|
4
5
|
const require_search_fields = require("./search-fields-DwrsJUC1.cjs");
|
|
5
6
|
const require_object = require("./object-Djg28csK.cjs");
|
|
6
7
|
require("./agg.cjs");
|
|
7
8
|
const require_aggregate_rules = require("./aggregate-rules-D_bsCpUI.cjs");
|
|
8
9
|
const require_ops = require("./ops.cjs");
|
|
9
|
-
const require_validator = require("./validator-BxWwh3wf.cjs");
|
|
10
10
|
let _atscript_typescript_utils = require("@atscript/typescript/utils");
|
|
11
11
|
let _uniqu_core = require("@uniqu/core");
|
|
12
12
|
let node_async_hooks = require("node:async_hooks");
|
|
@@ -215,7 +215,7 @@ function sourceIndex(type) {
|
|
|
215
215
|
if (metadata.has("db.ignore") || navFields.has(path)) idx.unstored.add(path);
|
|
216
216
|
if (metadata.has("db.column.derived") && fieldType.ref?.field) idx.derived.set(path, fieldType.ref.field);
|
|
217
217
|
if (metadata.has("db.encrypted")) storage.push([path, false]);
|
|
218
|
-
else if (metadata.has("db.json") || resolveDesignType(fieldType) === "array") storage.push([path, true]);
|
|
218
|
+
else if (metadata.has("db.json") || resolveDesignType(fieldType) === "array" || isStructuredMixedUnion(fieldType)) storage.push([path, true]);
|
|
219
219
|
}
|
|
220
220
|
storage.sort(([a], [b]) => a.split(".").length - b.split(".").length);
|
|
221
221
|
for (const [path, json] of storage) if (require_object.findAncestorInSet(path, idx.jsonRoots) === void 0 && require_object.findAncestorInSet(path, idx.encrypted) === void 0) (json ? idx.jsonRoots : idx.encrypted).add(path);
|
|
@@ -264,8 +264,8 @@ function sourceFieldMetadata(sourceType, logicalPath) {
|
|
|
264
264
|
* aggregate's field, a predicate operand) to where it is physically stored.
|
|
265
265
|
* Internal — `AtscriptDbView.resolveRefSource` is the public entry.
|
|
266
266
|
*
|
|
267
|
-
* Relational rules (`TableMetadata`'s): the outermost `@db.json
|
|
268
|
-
* node with segments remaining is the column and the rest becomes
|
|
267
|
+
* Relational rules (`TableMetadata`'s): the outermost `@db.json`, array or
|
|
268
|
+
* structured mixed union (`isStructuredMixedUnion`) node with segments remaining is the column and the rest becomes
|
|
269
269
|
* {@link TViewSource.jsonPath}; a flattened leaf is its parent segments
|
|
270
270
|
* joined with `__` plus its `@db.column` (or segment); a top-level field is
|
|
271
271
|
* its `@db.column` or name. Nested-object adapters use the document path
|
|
@@ -297,7 +297,8 @@ function resolveViewSource(sourceType, logicalPath, nestedObjects) {
|
|
|
297
297
|
};
|
|
298
298
|
}
|
|
299
299
|
const jsonRoot = require_object.selfOrAncestor(logicalPath, idx.jsonRoots);
|
|
300
|
-
const
|
|
300
|
+
const presence = require_validator.pathPresence(sourceType, logicalPath);
|
|
301
|
+
const optional = (presence === void 0 ? require_object.selfOrAncestor(logicalPath, idx.optional) !== void 0 : presence !== "required") || jsonRoot !== void 0 && jsonRoot !== logicalPath;
|
|
301
302
|
if (!node) return {
|
|
302
303
|
column: logicalPath,
|
|
303
304
|
designType: "unknown",
|
|
@@ -385,6 +386,38 @@ function relationalColumnName(path, override, flattened) {
|
|
|
385
386
|
function isNavRelation(metadata) {
|
|
386
387
|
return metadata.has("db.rel.to") || metadata.has("db.rel.from") || metadata.has("db.rel.via");
|
|
387
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* A union whose value members disagree on their design type and include a
|
|
391
|
+
* structure — `Addr | string`, or the leaf `a` of `{ a: Addr } | { a: string }`.
|
|
392
|
+
* It has no column layout of its own: relational storage keeps it as one
|
|
393
|
+
* JSON column, like `@db.json` (since 0.1.155).
|
|
394
|
+
*/
|
|
395
|
+
function isStructuredMixedUnion(type) {
|
|
396
|
+
if (type.type.kind !== "union" || resolveDesignType(type) !== "union") return false;
|
|
397
|
+
return require_validator.unionValueMembers(type).members.some((member) => {
|
|
398
|
+
const kind = member.type.kind;
|
|
399
|
+
return kind === "object" || kind === "array" || kind === "tuple" || kind === "intersection";
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* The type a column of `type` is described by: the value member of a
|
|
404
|
+
* nullable union (`number.int | null` → `number.int`, so type mappers see
|
|
405
|
+
* its tags) carrying the field's own annotations and optionality, else
|
|
406
|
+
* `type` itself.
|
|
407
|
+
*/
|
|
408
|
+
function columnType(type) {
|
|
409
|
+
const member = require_validator.soleUnionMember(type);
|
|
410
|
+
if (!member) return type;
|
|
411
|
+
return {
|
|
412
|
+
__is_atscript_annotated_type: true,
|
|
413
|
+
type: member.type,
|
|
414
|
+
metadata: type.metadata,
|
|
415
|
+
optional: type.optional,
|
|
416
|
+
id: member.id,
|
|
417
|
+
ref: member.ref,
|
|
418
|
+
validator: (...args) => type.validator(...args)
|
|
419
|
+
};
|
|
420
|
+
}
|
|
388
421
|
/** Returns true if the annotated type IS the `db.geoPoint` primitive (tag-based). */
|
|
389
422
|
function isGeoPointType(fieldType) {
|
|
390
423
|
return fieldType.type.tags?.has("geoPoint") === true;
|
|
@@ -554,6 +587,9 @@ var TableMetadata = class {
|
|
|
554
587
|
_alwaysAddressable;
|
|
555
588
|
_collateMap = /* @__PURE__ */ new Map();
|
|
556
589
|
_columnFromMap = /* @__PURE__ */ new Map();
|
|
590
|
+
/** The annotated type `build()` ran on — the root of {@link presence}. */
|
|
591
|
+
_rootType;
|
|
592
|
+
_presence = /* @__PURE__ */ new Map();
|
|
557
593
|
constructor(nestedObjects) {
|
|
558
594
|
this.nestedObjects = nestedObjects;
|
|
559
595
|
}
|
|
@@ -561,6 +597,72 @@ var TableMetadata = class {
|
|
|
561
597
|
return this._built;
|
|
562
598
|
}
|
|
563
599
|
/**
|
|
600
|
+
* Whether the value of the LOGICAL field `path` is always there, may be
|
|
601
|
+
* NULL / missing, or is declared by only some members of a union of
|
|
602
|
+
* objects on the way ({@link TPathPresence}); `undefined` inside an array.
|
|
603
|
+
* @since 0.1.155
|
|
604
|
+
*/
|
|
605
|
+
presence(path) {
|
|
606
|
+
if (this._presence.has(path)) return this._presence.get(path);
|
|
607
|
+
const root = this._rootType;
|
|
608
|
+
const answer = root ? require_validator.pathPresence(root, path) : void 0;
|
|
609
|
+
this._presence.set(path, answer);
|
|
610
|
+
return answer;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Whether the LOGICAL field `path` can hold NULL (or be missing): it or a
|
|
614
|
+
* parent object is optional or a `| null` union, or only some members of
|
|
615
|
+
* a union of objects declare it. Inside an array: the field or a parent
|
|
616
|
+
* is optional.
|
|
617
|
+
* @since 0.1.155
|
|
618
|
+
*/
|
|
619
|
+
isNullable(path) {
|
|
620
|
+
const presence = this.presence(path);
|
|
621
|
+
if (presence !== void 0) return presence !== "required";
|
|
622
|
+
for (let p = path;;) {
|
|
623
|
+
if (this.flatMap.get(p)?.optional === true) return true;
|
|
624
|
+
const dot = p.lastIndexOf(".");
|
|
625
|
+
if (dot === -1) return false;
|
|
626
|
+
p = p.slice(0, dot);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
_objectLeaves;
|
|
630
|
+
/**
|
|
631
|
+
* The stored fields under the object field `path` that a null test on the
|
|
632
|
+
* object reads (`rewriteObjectNullTests`, since 0.1.155): its leaf columns
|
|
633
|
+
* on relational storage, its scalar / array / JSON fields on document
|
|
634
|
+
* storage (not inside an array or a JSON value). `undefined` when `path`
|
|
635
|
+
* is no stored object (a leaf, a `@db.json` / encrypted / ignored field,
|
|
636
|
+
* a navigation relation, inside a JSON value, unknown).
|
|
637
|
+
*/
|
|
638
|
+
objectLeaves(path) {
|
|
639
|
+
this._objectLeaves ??= /* @__PURE__ */ new Map();
|
|
640
|
+
if (this._objectLeaves.has(path)) return this._objectLeaves.get(path);
|
|
641
|
+
let leaves;
|
|
642
|
+
const fd = this.descriptorByPath.get(path);
|
|
643
|
+
if (require_object.findAncestorInSet(path, this.jsonValueParents) === void 0 && (this.nestedObjects ? fd !== void 0 && fd.designType === "object" && fd.storage !== "json" : this.flattenedParents.has(path) && !this.ignoredFields.has(path))) {
|
|
644
|
+
const prefix = `${path}.`;
|
|
645
|
+
leaves = [];
|
|
646
|
+
for (const [leaf, leafFd] of this.descriptorByPath) {
|
|
647
|
+
if (!leaf.startsWith(prefix) || leafFd.designType === "object") continue;
|
|
648
|
+
if (require_object.findAncestorInSet(leaf, this.jsonValueParents) !== void 0) continue;
|
|
649
|
+
if (this.presence(leaf) === void 0) continue;
|
|
650
|
+
leaves.push(leaf);
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
this._objectLeaves.set(path, leaves);
|
|
654
|
+
return leaves;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Whether the column of `path` is nullable: {@link isNullable}, except
|
|
658
|
+
* inside an array (document storage only), where the field's own
|
|
659
|
+
* optionality counts.
|
|
660
|
+
*/
|
|
661
|
+
_columnNullable(path, type) {
|
|
662
|
+
const presence = this.presence(path);
|
|
663
|
+
return presence === void 0 ? type.optional === true : presence !== "required";
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
564
666
|
* {@link documentPath} over this table's `columnMap`. A `@db.column.derived`
|
|
565
667
|
* field has no stored path of its own: it maps to its source leaf.
|
|
566
668
|
*/
|
|
@@ -604,6 +706,7 @@ var TableMetadata = class {
|
|
|
604
706
|
build(type, adapter, logger) {
|
|
605
707
|
if (this._built) return;
|
|
606
708
|
adapter.onBeforeFlatten?.(type);
|
|
709
|
+
this._rootType = type;
|
|
607
710
|
const collected = [];
|
|
608
711
|
this.flatMap = (0, _atscript_typescript_utils.flattenAnnotatedType)(type, {
|
|
609
712
|
topLevelArrayTag: adapter.getTopLevelArrayTag?.() ?? "db.__topLevelArray",
|
|
@@ -662,10 +765,13 @@ var TableMetadata = class {
|
|
|
662
765
|
* into the synthetic flat entry of the prop's path, so a member's
|
|
663
766
|
* annotations would reach the column (DDL type, size, default). Since
|
|
664
767
|
* atscript 0.1.103 a built-in primitive member carries its built-in
|
|
665
|
-
* annotations (`
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
*
|
|
768
|
+
* annotations (`string.char` → `@expect.maxLength 1`). Rebuild such an
|
|
769
|
+
* entry from the prop's own annotations plus the non-`db.*` annotations of
|
|
770
|
+
* named members (aliases, interfaces), so the column is described as before
|
|
771
|
+
* 0.1.103. Exception: the one non-`null` member of `T | null` is the
|
|
772
|
+
* column's type, so its `db.*` annotations apply too — `createdAt:
|
|
773
|
+
* number.timestamp.created | null` gets `@db.default.now` like
|
|
774
|
+
* `createdAt: number.timestamp.created`.
|
|
669
775
|
*/
|
|
670
776
|
_dropMemberMetadata(collected) {
|
|
671
777
|
const byPath = /* @__PURE__ */ new Map();
|
|
@@ -678,14 +784,16 @@ var TableMetadata = class {
|
|
|
678
784
|
if (!entries.some((e) => e.member)) continue;
|
|
679
785
|
const flat = this.flatMap.get(path);
|
|
680
786
|
if (!flat?.__flat_union) continue;
|
|
681
|
-
const
|
|
682
|
-
|
|
683
|
-
if (entry.member && isInlinePrimitive(entry.type)) continue;
|
|
684
|
-
for (const [key, value] of entry.metadata) if (!entry.member || !key.startsWith("db.")) metadata.set(key, value);
|
|
685
|
-
}
|
|
787
|
+
const prop = entries.find((e) => !e.member);
|
|
788
|
+
const base = prop && require_validator.columnUnionBase(prop.type);
|
|
686
789
|
const target = flat.metadata;
|
|
687
790
|
target.clear();
|
|
688
|
-
for (const
|
|
791
|
+
for (const entry of entries) {
|
|
792
|
+
const inline = entry.member && isInlinePrimitive(entry.type);
|
|
793
|
+
const isBase = entry.member && entry.type === base;
|
|
794
|
+
for (const [key, value] of entry.metadata) if (!entry.member || (key.startsWith("db.") ? isBase : !inline)) target.set(key, value);
|
|
795
|
+
}
|
|
796
|
+
if (base && prop) prop.metadata = target;
|
|
689
797
|
}
|
|
690
798
|
}
|
|
691
799
|
/**
|
|
@@ -708,7 +816,7 @@ var TableMetadata = class {
|
|
|
708
816
|
* Scans `@db.*` and `@meta.id` annotations on a field during flattening.
|
|
709
817
|
*/
|
|
710
818
|
_scanGenericAnnotations(fieldName, fieldType, metadata, logger) {
|
|
711
|
-
if (metadata.has("meta.id")) {
|
|
819
|
+
if (metadata.has("meta.id") && !fieldName.includes(".")) {
|
|
712
820
|
this.primaryKeys.push(fieldName);
|
|
713
821
|
this.originalMetaIdFields.push(fieldName);
|
|
714
822
|
}
|
|
@@ -971,7 +1079,7 @@ var TableMetadata = class {
|
|
|
971
1079
|
const isJson = this.jsonFields.has(path);
|
|
972
1080
|
const isArray = designType === "array";
|
|
973
1081
|
const isObject = designType === "object";
|
|
974
|
-
if (isArray) this.jsonFields.add(path);
|
|
1082
|
+
if (isArray || isStructuredMixedUnion(type)) this.jsonFields.add(path);
|
|
975
1083
|
else if (isObject && isJson) {} else if (isObject && !isJson) this.flattenedParents.add(path);
|
|
976
1084
|
}
|
|
977
1085
|
for (const ignoredField of this.ignoredFields) if (this.flattenedParents.has(ignoredField)) {
|
|
@@ -1073,6 +1181,7 @@ var TableMetadata = class {
|
|
|
1073
1181
|
const isJson = this.jsonFields.has(path);
|
|
1074
1182
|
const isFlattened = !skipFlattening && require_object.findAncestorInSet(path, this.flattenedParents) !== void 0;
|
|
1075
1183
|
const designType = isEncrypted ? "string" : isJson ? "json" : resolveDesignType(type);
|
|
1184
|
+
const colType = columnType(type);
|
|
1076
1185
|
let storage;
|
|
1077
1186
|
if (skipFlattening) storage = "column";
|
|
1078
1187
|
else if (isEncrypted) storage = isFlattened ? "flattened" : "column";
|
|
@@ -1091,10 +1200,10 @@ var TableMetadata = class {
|
|
|
1091
1200
|
if (quantityRef) this.quantityRefByField.set(path, quantityRef);
|
|
1092
1201
|
descriptors.push({
|
|
1093
1202
|
path,
|
|
1094
|
-
type,
|
|
1203
|
+
type: colType,
|
|
1095
1204
|
physicalName,
|
|
1096
1205
|
designType,
|
|
1097
|
-
optional: type.optional === true,
|
|
1206
|
+
optional: skipFlattening ? type.optional === true : this._columnNullable(path, type),
|
|
1098
1207
|
isPrimaryKey: this.primaryKeys.includes(path),
|
|
1099
1208
|
ignored: this.ignoredFields.has(path),
|
|
1100
1209
|
defaultValue: this.defaults.get(path),
|
|
@@ -1108,7 +1217,7 @@ var TableMetadata = class {
|
|
|
1108
1217
|
unitCode,
|
|
1109
1218
|
unitRefField,
|
|
1110
1219
|
encrypted: isEncrypted || underEncrypted || void 0,
|
|
1111
|
-
isGeoPoint: isGeoPointType(
|
|
1220
|
+
isGeoPoint: isGeoPointType(colType) || void 0,
|
|
1112
1221
|
derived: this.derivedFields.get(path),
|
|
1113
1222
|
computed: computedMeta(rootType, path)
|
|
1114
1223
|
});
|
|
@@ -1236,7 +1345,7 @@ var TableMetadata = class {
|
|
|
1236
1345
|
for (const index of this.indexes.values()) for (const field of index.fields) {
|
|
1237
1346
|
const ftype = this.flatMap.get(field.name);
|
|
1238
1347
|
if (ftype) {
|
|
1239
|
-
field.optional =
|
|
1348
|
+
field.optional = this._columnNullable(field.name, ftype);
|
|
1240
1349
|
field.designType = ftype.type.tags?.has("objectId") ? "objectId" : resolveDesignType(ftype);
|
|
1241
1350
|
}
|
|
1242
1351
|
field.name = this.physicalPath(field.name);
|
|
@@ -1649,6 +1758,78 @@ function rewriteIntegerRegex(filter, meta) {
|
|
|
1649
1758
|
return out ?? filter;
|
|
1650
1759
|
}
|
|
1651
1760
|
//#endregion
|
|
1761
|
+
//#region src/query/object-null.ts
|
|
1762
|
+
/**
|
|
1763
|
+
* Whether a filter entry's value only asks if the value is there: `null`,
|
|
1764
|
+
* `{ $eq: null }`, `{ $ne: null }`, `{ $exists: true | false }` (and
|
|
1765
|
+
* combinations of them).
|
|
1766
|
+
*/
|
|
1767
|
+
function isNullTest(value) {
|
|
1768
|
+
if (value === null) return true;
|
|
1769
|
+
if (!require_object.isPlainObject(value)) return false;
|
|
1770
|
+
const ops = Object.entries(value);
|
|
1771
|
+
return ops.length > 0 && ops.every(([op, operand]) => op === "$exists" ? typeof operand === "boolean" : (op === "$eq" || op === "$ne") && operand === null);
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* The leaf form of one null test on an object (see
|
|
1775
|
+
* {@link rewriteObjectNullTests}) as `$and` members: absent — `$exists: false`
|
|
1776
|
+
* on every leaf; present — one `$or` of `$exists: true` per leaf.
|
|
1777
|
+
*/
|
|
1778
|
+
function objectTest(value, leaves) {
|
|
1779
|
+
const ops = value === null ? { $eq: null } : value;
|
|
1780
|
+
const out = [];
|
|
1781
|
+
for (const [op, operand] of Object.entries(ops)) {
|
|
1782
|
+
const present = op === "$exists" ? operand : op === "$ne";
|
|
1783
|
+
const tests = leaves.map((leaf) => ({ [leaf]: { $exists: present } }));
|
|
1784
|
+
if (present) out.push({ $or: tests });
|
|
1785
|
+
else out.push(...tests);
|
|
1786
|
+
}
|
|
1787
|
+
return out;
|
|
1788
|
+
}
|
|
1789
|
+
/**
|
|
1790
|
+
* A null test on a stored object (since 0.1.155) — `{ address: null }`,
|
|
1791
|
+
* `{ address: { $ne: null } }`, `{ address: { $exists: … } }` — on the
|
|
1792
|
+
* object's leaves: the object is null when none of its fields holds a value
|
|
1793
|
+
* (`$exists: false` on every leaf), present when one does. Relational
|
|
1794
|
+
* storage has no column for the object (an all-NULL row reads back as
|
|
1795
|
+
* `null`); document storage answers the same, so an object stored as `{}`
|
|
1796
|
+
* or with only null fields counts as null everywhere. LOGICAL filter;
|
|
1797
|
+
* copy-on-write (the same object comes back when nothing changed).
|
|
1798
|
+
*/
|
|
1799
|
+
function rewriteObjectNullTests(filter, meta) {
|
|
1800
|
+
if (!require_object.isPlainObject(filter)) return filter;
|
|
1801
|
+
let out;
|
|
1802
|
+
const extra = [];
|
|
1803
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
1804
|
+
let next = value;
|
|
1805
|
+
if (key === "$and" || key === "$or") {
|
|
1806
|
+
if (Array.isArray(value)) {
|
|
1807
|
+
const mapped = value.map((f) => rewriteObjectNullTests(f, meta));
|
|
1808
|
+
if (mapped.some((m, i) => m !== value[i])) next = mapped;
|
|
1809
|
+
}
|
|
1810
|
+
} else if (key === "$not") next = rewriteObjectNullTests(value, meta);
|
|
1811
|
+
else if (!key.startsWith("$") && isNullTest(value)) {
|
|
1812
|
+
const leaves = meta.objectLeaves(key);
|
|
1813
|
+
if (leaves && leaves.length > 0) {
|
|
1814
|
+
extra.push(...objectTest(value, leaves));
|
|
1815
|
+
out ??= { ...filter };
|
|
1816
|
+
delete out[key];
|
|
1817
|
+
continue;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
if (next !== value) {
|
|
1821
|
+
out ??= { ...filter };
|
|
1822
|
+
out[key] = next;
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
if (!out) return filter;
|
|
1826
|
+
if (extra.length > 0) {
|
|
1827
|
+
const and = out.$and;
|
|
1828
|
+
out.$and = [...Array.isArray(and) ? and : [], ...extra];
|
|
1829
|
+
}
|
|
1830
|
+
return out;
|
|
1831
|
+
}
|
|
1832
|
+
//#endregion
|
|
1652
1833
|
//#region src/query/aggregate-expr.ts
|
|
1653
1834
|
/**
|
|
1654
1835
|
* Output aliases of the computed `$select` entries of a grouped query —
|
|
@@ -2300,7 +2481,7 @@ var FieldMappingStrategy = class {
|
|
|
2300
2481
|
* translate predicate operands at deeper levels).
|
|
2301
2482
|
*/
|
|
2302
2483
|
translateFilter(filter, meta, depth = 0) {
|
|
2303
|
-
filter = rewriteIntegerRegex(filter, meta);
|
|
2484
|
+
filter = rewriteObjectNullTests(rewriteIntegerRegex(filter, meta), meta);
|
|
2304
2485
|
const has = require_nested_writer.containsRelationFilter(filter);
|
|
2305
2486
|
const resolved = has ? require_nested_writer.resolveRelationFilterTree(filter, meta, depth) : filter;
|
|
2306
2487
|
return this.noteTranslated(filter, this.translateResolvedFilter(resolved, meta), has);
|
|
@@ -2642,13 +2823,13 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
2642
2823
|
current = created;
|
|
2643
2824
|
} else current = next;
|
|
2644
2825
|
}
|
|
2645
|
-
current[col.last] = value;
|
|
2826
|
+
if (!col.partial || value !== null && value !== void 0) current[col.last] = value;
|
|
2646
2827
|
}
|
|
2647
2828
|
for (const parent of plan.parents) collapseNullParent(result, parent);
|
|
2648
2829
|
return result;
|
|
2649
2830
|
}
|
|
2650
2831
|
translateQuery(query, meta) {
|
|
2651
|
-
const logical = rewriteIntegerRegex(query.filter, meta);
|
|
2832
|
+
const logical = rewriteObjectNullTests(rewriteIntegerRegex(query.filter, meta), meta);
|
|
2652
2833
|
const has = require_nested_writer.containsRelationFilter(logical);
|
|
2653
2834
|
const filter = has ? require_nested_writer.resolveRelationFilterTree(logical, meta, 0) : logical;
|
|
2654
2835
|
if (!meta.requiresMappings) {
|
|
@@ -2703,7 +2884,11 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
2703
2884
|
return result;
|
|
2704
2885
|
}
|
|
2705
2886
|
prepareForWrite(payload, meta, adapter) {
|
|
2706
|
-
if (meta.requiresMappings && !meta.onlyColumnRenames)
|
|
2887
|
+
if (meta.requiresMappings && !meta.onlyColumnRenames) {
|
|
2888
|
+
const flat = this.flattenWritePayload(payload, meta, adapter);
|
|
2889
|
+
nullAbsentNested(payload, flat, meta);
|
|
2890
|
+
return this.formatWriteValues(flat, meta);
|
|
2891
|
+
}
|
|
2707
2892
|
const data = { ...payload };
|
|
2708
2893
|
this.prepareCommon(data, meta, adapter);
|
|
2709
2894
|
if (meta.onlyColumnRenames) {
|
|
@@ -2730,7 +2915,7 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
2730
2915
|
const suffix = operatorMatch ? operatorMatch[2] : "";
|
|
2731
2916
|
const fd = meta.leafByLogical.get(basePath);
|
|
2732
2917
|
const finalKey = (fd?.physicalName ?? basePath) + suffix;
|
|
2733
|
-
if (fd?.storage === "json" &&
|
|
2918
|
+
if (fd?.storage === "json" && value !== null && value !== void 0 && !suffix) result[finalKey] = JSON.stringify(value);
|
|
2734
2919
|
else result[finalKey] = value;
|
|
2735
2920
|
}
|
|
2736
2921
|
return this.formatWriteValues(result, meta);
|
|
@@ -2810,6 +2995,49 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
2810
2995
|
return result;
|
|
2811
2996
|
}
|
|
2812
2997
|
};
|
|
2998
|
+
const nestedColumns = /* @__PURE__ */ new WeakMap();
|
|
2999
|
+
function nestedColumnsFor(meta) {
|
|
3000
|
+
let list = nestedColumns.get(meta);
|
|
3001
|
+
if (list === void 0) {
|
|
3002
|
+
list = [];
|
|
3003
|
+
for (const [physical, fd] of meta.leafByPhysical) {
|
|
3004
|
+
if (fd.derived || fd.isPrimaryKey || fd.storage === "column" || !fd.path.includes(".")) continue;
|
|
3005
|
+
const segs = fd.path.split(".");
|
|
3006
|
+
list.push({
|
|
3007
|
+
physical,
|
|
3008
|
+
parents: segs.slice(0, -1),
|
|
3009
|
+
partial: meta.presence(fd.path) === "partial"
|
|
3010
|
+
});
|
|
3011
|
+
}
|
|
3012
|
+
nestedColumns.set(meta, list);
|
|
3013
|
+
}
|
|
3014
|
+
return list;
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* Every write through `prepareForWrite` stores a whole row (insert, replace).
|
|
3018
|
+
* A column the flattened row leaves out would take its column `DEFAULT` —
|
|
3019
|
+
* a `now` default below an absent optional object would make the object
|
|
3020
|
+
* reappear on read, and one of another union member's leaves would add a
|
|
3021
|
+
* field to the stored member. So a column below an absent / `null` parent
|
|
3022
|
+
* object, or one only some union members declare that the written member
|
|
3023
|
+
* leaves out, is written as `NULL` (since 0.1.155), as the document adapters
|
|
3024
|
+
* store such a row.
|
|
3025
|
+
*/
|
|
3026
|
+
function nullAbsentNested(payload, flat, meta) {
|
|
3027
|
+
for (const col of nestedColumnsFor(meta)) {
|
|
3028
|
+
if (col.physical in flat) continue;
|
|
3029
|
+
let absent = false;
|
|
3030
|
+
let current = payload;
|
|
3031
|
+
for (const seg of col.parents) {
|
|
3032
|
+
current = current[seg];
|
|
3033
|
+
if (current === null || current === void 0 || typeof current !== "object") {
|
|
3034
|
+
absent = true;
|
|
3035
|
+
break;
|
|
3036
|
+
}
|
|
3037
|
+
}
|
|
3038
|
+
if (absent || col.partial) flat[col.physical] = null;
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
2813
3041
|
/**
|
|
2814
3042
|
* Per-table read plan of {@link RelationalFieldMapper.reconstructFromRead}:
|
|
2815
3043
|
* the per-column decisions (coercion, JSON, nesting — the dot-path pre-split)
|
|
@@ -2829,12 +3057,14 @@ function compileReadPlan(meta) {
|
|
|
2829
3057
|
const fromFmts = meta.fromStorageFormatters;
|
|
2830
3058
|
const columns = /* @__PURE__ */ new Map();
|
|
2831
3059
|
for (const [physical, fd] of meta.leafByPhysical) {
|
|
2832
|
-
const
|
|
3060
|
+
const nested = (fd.storage === "json" || fd.storage === "flattened") && fd.path.includes(".");
|
|
3061
|
+
const segs = nested ? fd.path.split(".") : void 0;
|
|
2833
3062
|
columns.set(physical, {
|
|
2834
3063
|
path: fd.path,
|
|
2835
3064
|
parents: segs?.slice(0, -1),
|
|
2836
3065
|
last: segs ? segs[segs.length - 1] : fd.path,
|
|
2837
3066
|
json: fd.storage === "json",
|
|
3067
|
+
partial: nested && meta.presence(fd.path) === "partial",
|
|
2838
3068
|
coerce: fd.designType === "boolean" ? toBool : fd.designType === "decimal" ? toDecimalString : void 0,
|
|
2839
3069
|
fromFmt: fromFmts?.get(physical)
|
|
2840
3070
|
});
|
|
@@ -2845,15 +3075,16 @@ function compileReadPlan(meta) {
|
|
|
2845
3075
|
parents.push({
|
|
2846
3076
|
ancestors: segs.slice(0, -1),
|
|
2847
3077
|
last: segs[segs.length - 1],
|
|
2848
|
-
|
|
3078
|
+
presence: meta.presence(parentPath) ?? "required"
|
|
2849
3079
|
});
|
|
2850
3080
|
}
|
|
3081
|
+
parents.sort((a, b) => b.ancestors.length - a.ancestors.length);
|
|
2851
3082
|
return {
|
|
2852
3083
|
columns,
|
|
2853
3084
|
parents
|
|
2854
3085
|
};
|
|
2855
3086
|
}
|
|
2856
|
-
/** If every child of a flattened parent is null / undefined, collapse it (
|
|
3087
|
+
/** If every child of a flattened parent is null / undefined, collapse it (see {@link TReadParent}). */
|
|
2857
3088
|
function collapseNullParent(obj, parent) {
|
|
2858
3089
|
let current = obj;
|
|
2859
3090
|
const ancestors = parent.ancestors;
|
|
@@ -2868,7 +3099,8 @@ function collapseNullParent(obj, parent) {
|
|
|
2868
3099
|
const v = parentObj[k];
|
|
2869
3100
|
if (v !== null && v !== void 0) return;
|
|
2870
3101
|
}
|
|
2871
|
-
|
|
3102
|
+
if (parent.presence === "partial") delete current[parent.last];
|
|
3103
|
+
else current[parent.last] = parent.presence === "nullable" ? null : {};
|
|
2872
3104
|
}
|
|
2873
3105
|
/**
|
|
2874
3106
|
* Per-table write plan of the relational flatten: each logical path's
|
|
@@ -3310,6 +3542,10 @@ function collectQueryPaths(query, aggregate) {
|
|
|
3310
3542
|
path,
|
|
3311
3543
|
predicate,
|
|
3312
3544
|
relation: relationOpsOf(value)
|
|
3545
|
+
} : isNullTest(value) ? {
|
|
3546
|
+
path,
|
|
3547
|
+
predicate,
|
|
3548
|
+
nullTest: true
|
|
3313
3549
|
} : {
|
|
3314
3550
|
path,
|
|
3315
3551
|
predicate
|
|
@@ -3409,10 +3645,11 @@ function pathSourceOf(meta) {
|
|
|
3409
3645
|
* `FieldCapabilityIndex`, with `$with` hints and leaf lists) is what clients
|
|
3410
3646
|
* see and is authoritative — the HTTP gate always answers first.
|
|
3411
3647
|
*/
|
|
3412
|
-
function guardPath(meta, adapter, path, op, predicate = "compare") {
|
|
3648
|
+
function guardPath(meta, adapter, path, op, predicate = "compare", nullTest = false) {
|
|
3413
3649
|
const verb = OP_VERB[op];
|
|
3414
3650
|
const { kind, parent } = classifyQueryPath(pathSourceOf(meta), path);
|
|
3415
3651
|
if (predicate === "relation" && kind !== "nav" && kind !== "unknown") throw pathError(path, `"$some" / "$none" are only valid on a navigation relation — "${path}" is not one`);
|
|
3652
|
+
if (op === "filter" && nullTest && meta.objectLeaves(path)?.length) return;
|
|
3416
3653
|
switch (kind) {
|
|
3417
3654
|
case "nav":
|
|
3418
3655
|
if (op === "filter" && predicate === "relation" && parent === void 0) return;
|
|
@@ -3497,7 +3734,7 @@ function guardPaths(meta, adapter, query, aggregate = false, state) {
|
|
|
3497
3734
|
if (refs.unsupportedOperator !== void 0) throw pathError(refs.unsupportedOperator, unsupportedOperatorMessage(refs.unsupportedOperator));
|
|
3498
3735
|
let relState = state;
|
|
3499
3736
|
for (const ref of refs.filter) {
|
|
3500
|
-
guardPath(meta, adapter, ref.path, "filter", ref.predicate);
|
|
3737
|
+
guardPath(meta, adapter, ref.path, "filter", ref.predicate, ref.nullTest);
|
|
3501
3738
|
if (ref.predicate === "relation") guardRelationRef(meta, adapter, ref, relState ??= require_nested_writer.relGuardState());
|
|
3502
3739
|
}
|
|
3503
3740
|
for (const path of refs.sort) guardPath(meta, adapter, path, "sort");
|
|
@@ -3639,10 +3876,12 @@ var NullsSource = class {
|
|
|
3639
3876
|
}
|
|
3640
3877
|
/**
|
|
3641
3878
|
* Whether ordering by the LOGICAL field `path` can meet NULL (or a missing
|
|
3642
|
-
* value): the field
|
|
3643
|
-
*
|
|
3644
|
-
*
|
|
3645
|
-
*
|
|
3879
|
+
* value): the field can hold NULL (`TableMetadata.isNullable` — it or a
|
|
3880
|
+
* parent object is optional or a `| null` union, or only some members of a
|
|
3881
|
+
* union of objects declare it), the field is a derived column, it is not a
|
|
3882
|
+
* known field, or the source is a view. A `$nulls` entry on any other
|
|
3883
|
+
* field changes nothing and is dropped, so the plain `ORDER BY` keeps
|
|
3884
|
+
* using the index.
|
|
3646
3885
|
*/
|
|
3647
3886
|
nullable(path) {
|
|
3648
3887
|
let answer = this._nullable.get(path);
|
|
@@ -3656,12 +3895,7 @@ var NullsSource = class {
|
|
|
3656
3895
|
const { meta } = this._init;
|
|
3657
3896
|
const fd = meta.descriptorByPath.get(path);
|
|
3658
3897
|
if (!fd || fd.derived) return true;
|
|
3659
|
-
|
|
3660
|
-
if (meta.flatMap.get(p)?.optional === true) return true;
|
|
3661
|
-
const dot = p.lastIndexOf(".");
|
|
3662
|
-
if (dot === -1) return false;
|
|
3663
|
-
p = p.slice(0, dot);
|
|
3664
|
-
}
|
|
3898
|
+
return meta.isNullable(path);
|
|
3665
3899
|
}
|
|
3666
3900
|
/**
|
|
3667
3901
|
* The effective `$nulls` of the `$sort` keys of one read: the request's
|
|
@@ -3776,7 +4010,9 @@ function geoIndexNotFoundMessage(tableName, indexName) {
|
|
|
3776
4010
|
* otherwise trips up every adapter author.
|
|
3777
4011
|
*
|
|
3778
4012
|
* For union types (e.g., from flattened `{...} | {...}` objects):
|
|
3779
|
-
* -
|
|
4013
|
+
* - `null` / `undefined` members are left out (since 0.1.155): `T | null`
|
|
4014
|
+
* resolves to the design type of `T`
|
|
4015
|
+
* - If all other members resolve to the same type → returns that type (strong type)
|
|
3780
4016
|
* - If members disagree → returns `'union'` (out of scope for type management)
|
|
3781
4017
|
*/
|
|
3782
4018
|
function resolveDesignType(fieldType) {
|
|
@@ -3784,9 +4020,9 @@ function resolveDesignType(fieldType) {
|
|
|
3784
4020
|
if (fieldType.type.kind === "object") return "object";
|
|
3785
4021
|
if (fieldType.type.kind === "array") return "array";
|
|
3786
4022
|
if (fieldType.type.kind === "union") {
|
|
3787
|
-
const
|
|
3788
|
-
if (
|
|
3789
|
-
const resolved =
|
|
4023
|
+
const { members } = require_validator.unionValueMembers(fieldType);
|
|
4024
|
+
if (members.length > 0) {
|
|
4025
|
+
const resolved = members.map((item) => resolveDesignType(item));
|
|
3790
4026
|
if (resolved.every((type) => type === resolved[0])) return resolved[0];
|
|
3791
4027
|
}
|
|
3792
4028
|
return "union";
|
|
@@ -4442,21 +4678,13 @@ var AtscriptDbReadable = class {
|
|
|
4442
4678
|
_totalOrderKeys() {
|
|
4443
4679
|
if (this._totalOrderKeysCache) return this._totalOrderKeysCache;
|
|
4444
4680
|
const meta = this._meta;
|
|
4445
|
-
const nullable = (logical) => {
|
|
4446
|
-
for (let path = logical;;) {
|
|
4447
|
-
if (meta.flatMap.get(path)?.optional === true) return true;
|
|
4448
|
-
const dot = path.lastIndexOf(".");
|
|
4449
|
-
if (dot === -1) return false;
|
|
4450
|
-
path = path.slice(0, dot);
|
|
4451
|
-
}
|
|
4452
|
-
};
|
|
4453
4681
|
const sets = [];
|
|
4454
4682
|
if (meta.primaryKeys.length > 0) sets.push(meta.primaryKeys.map((f) => meta.physicalPath(f)));
|
|
4455
4683
|
for (const index of meta.indexes.values()) {
|
|
4456
4684
|
if (index.type !== "unique") continue;
|
|
4457
|
-
if (index.fields.every((f) => f.optional !== true && !
|
|
4685
|
+
if (index.fields.every((f) => f.optional !== true && !meta.isNullable(meta.physicalToPath.get(f.name) ?? f.name))) sets.push(index.fields.map((f) => f.name));
|
|
4458
4686
|
}
|
|
4459
|
-
for (const prop of meta.uniqueProps) if (!
|
|
4687
|
+
for (const prop of meta.uniqueProps) if (!meta.isNullable(prop)) sets.push([meta.physicalPath(prop)]);
|
|
4460
4688
|
this._totalOrderKeysCache = sets;
|
|
4461
4689
|
return sets;
|
|
4462
4690
|
}
|
|
@@ -6077,7 +6305,7 @@ var BaseDbAdapter = class {
|
|
|
6077
6305
|
}
|
|
6078
6306
|
/**
|
|
6079
6307
|
* Whether the table has at least one row. Schema sync uses it in the
|
|
6080
|
-
* pre-flight phase to
|
|
6308
|
+
* pre-flight phase to check a primary-key change on a populated table.
|
|
6081
6309
|
* Override with an EXISTS/LIMIT 1 probe — this default is `count() > 0`,
|
|
6082
6310
|
* a full scan on some engines, and it can only answer for the adapter's
|
|
6083
6311
|
* OWN table: for another `tableName` (or on an administrative adapter
|
|
@@ -6497,6 +6725,10 @@ function flattenPatchPayload(payload, prefix, update, meta, table) {
|
|
|
6497
6725
|
const key = prefix ? `${prefix}.${_key}` : _key;
|
|
6498
6726
|
if (primaryKeys.includes(key)) continue;
|
|
6499
6727
|
if (!(typeof value === "object" && value !== null && !Array.isArray(value))) {
|
|
6728
|
+
if (value === null && meta.flattenedParents.has(key)) {
|
|
6729
|
+
for (const lp of meta.optionalLeavesByLogicalParent.get(key) ?? []) update[lp] = null;
|
|
6730
|
+
continue;
|
|
6731
|
+
}
|
|
6500
6732
|
update[key] = value;
|
|
6501
6733
|
continue;
|
|
6502
6734
|
}
|
|
@@ -7674,26 +7906,56 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
7674
7906
|
* full row). Function defaults (`now` / `uuid` / `increment` / custom) the
|
|
7675
7907
|
* adapter handles natively are NOT filled — the field stays absent so the
|
|
7676
7908
|
* engine's own default applies. The version column is never touched.
|
|
7909
|
+
* A nested field (`audit.createdAt`) is filled inside its object, only when
|
|
7910
|
+
* that object is present (an absent or `null` embedded object stays so), and
|
|
7911
|
+
* in every object of an array on the way; a field inside a JSON value or an
|
|
7912
|
+
* array has no column default, so the SDK fills its function default too.
|
|
7677
7913
|
*/
|
|
7678
7914
|
_applyDefaults(data) {
|
|
7679
7915
|
const nativeFns = this.adapter.nativeDefaultFns();
|
|
7680
7916
|
const versionField = this._meta.versionField;
|
|
7681
7917
|
for (const [field, def] of this._meta.defaults.entries()) {
|
|
7682
7918
|
if (field === versionField) continue;
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
|
|
7687
|
-
|
|
7688
|
-
break;
|
|
7689
|
-
case "uuid":
|
|
7690
|
-
data[field] = crypto.randomUUID();
|
|
7691
|
-
break;
|
|
7692
|
-
}
|
|
7919
|
+
let make;
|
|
7920
|
+
if (def.kind === "value") make = () => this._valueDefault(field, def.value);
|
|
7921
|
+
else if (def.kind === "fn" && (!nativeFns.has(def.fn) || require_object.findAncestorInSet(field, this._meta.jsonParents) !== void 0)) {
|
|
7922
|
+
if (def.fn === "now") make = Date.now;
|
|
7923
|
+
else if (def.fn === "uuid") make = () => crypto.randomUUID();
|
|
7693
7924
|
}
|
|
7925
|
+
if (!make) continue;
|
|
7926
|
+
if (!field.includes(".")) {
|
|
7927
|
+
if (data[field] === void 0) data[field] = make();
|
|
7928
|
+
} else if (this._nestedDefaultFillable(field)) fillNested(data, field.split("."), 0, make);
|
|
7694
7929
|
}
|
|
7695
7930
|
return data;
|
|
7696
7931
|
}
|
|
7932
|
+
/** {@link _nestedDefaultFillable} per nested default path. */
|
|
7933
|
+
_fillableDefaults;
|
|
7934
|
+
/**
|
|
7935
|
+
* Whether every parent of the nested default `field` is one object type —
|
|
7936
|
+
* an object, `T | null` of one, or an array of one. Below a tuple or a
|
|
7937
|
+
* union of several types the default cannot be placed (the item it belongs
|
|
7938
|
+
* to is unknown), so it is not filled.
|
|
7939
|
+
*/
|
|
7940
|
+
_nestedDefaultFillable(field) {
|
|
7941
|
+
const cache = this._fillableDefaults ??= /* @__PURE__ */ new Map();
|
|
7942
|
+
let fillable = cache.get(field);
|
|
7943
|
+
if (fillable === void 0) {
|
|
7944
|
+
fillable = true;
|
|
7945
|
+
for (let dot = field.indexOf("."); dot !== -1; dot = field.indexOf(".", dot + 1)) {
|
|
7946
|
+
let type = this.flatMap.get(field.slice(0, dot));
|
|
7947
|
+
if (type?.type.kind === "union") type = require_validator.soleUnionMember(type);
|
|
7948
|
+
if (type?.type.kind === "array") type = type.type.of;
|
|
7949
|
+
if (type?.type.kind === "union") type = require_validator.soleUnionMember(type);
|
|
7950
|
+
if (type?.type.kind !== "object") {
|
|
7951
|
+
fillable = false;
|
|
7952
|
+
break;
|
|
7953
|
+
}
|
|
7954
|
+
}
|
|
7955
|
+
cache.set(field, fillable);
|
|
7956
|
+
}
|
|
7957
|
+
return fillable;
|
|
7958
|
+
}
|
|
7697
7959
|
/**
|
|
7698
7960
|
* Parsed `@db.default 'literal'` values that are primitives — reused for
|
|
7699
7961
|
* every row (an object / array default is parsed per row: each row gets
|
|
@@ -7859,6 +8121,21 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
7859
8121
|
return this.createValidator({ plugins: adapterPlugins });
|
|
7860
8122
|
}
|
|
7861
8123
|
};
|
|
8124
|
+
/**
|
|
8125
|
+
* Sets `segs[i..]` to `make()` where it is `undefined`, inside present plain
|
|
8126
|
+
* objects only — fanned out over the objects of every array on the way.
|
|
8127
|
+
*/
|
|
8128
|
+
function fillNested(value, segs, i, make) {
|
|
8129
|
+
if (Array.isArray(value)) {
|
|
8130
|
+
for (const item of value) fillNested(item, segs, i, make);
|
|
8131
|
+
return;
|
|
8132
|
+
}
|
|
8133
|
+
if (!require_object.isPlainObject(value)) return;
|
|
8134
|
+
const key = segs[i];
|
|
8135
|
+
if (i === segs.length - 1) {
|
|
8136
|
+
if (value[key] === void 0) value[key] = make();
|
|
8137
|
+
} else fillNested(value[key], segs, i + 1, make);
|
|
8138
|
+
}
|
|
7862
8139
|
//#endregion
|
|
7863
8140
|
//#region src/table/view-read-plan.ts
|
|
7864
8141
|
/** Design types whose `=` compares one scalar to one scalar on every engine. */
|
|
@@ -8971,7 +9248,9 @@ function computeColumnDiff(desired, existing, typeMapper, opts) {
|
|
|
8971
9248
|
});
|
|
8972
9249
|
}
|
|
8973
9250
|
const desiredDefault = serializeDefaultValue(field.defaultValue);
|
|
8974
|
-
|
|
9251
|
+
const fn = field.defaultValue?.kind === "fn" ? field.defaultValue.fn : void 0;
|
|
9252
|
+
const missingNative = fn !== void 0 && fn !== "increment" && opts?.nativeDefaultFns?.has(fn) === true;
|
|
9253
|
+
if ((existingCol.dflt_value !== void 0 || missingNative) && existingCol.dflt_value !== desiredDefault) defaultChanged.push({
|
|
8975
9254
|
field,
|
|
8976
9255
|
oldDefault: existingCol.dflt_value,
|
|
8977
9256
|
newDefault: desiredDefault
|
|
@@ -9368,6 +9647,12 @@ Object.defineProperty(exports, "numericOperandProblem", {
|
|
|
9368
9647
|
return numericOperandProblem;
|
|
9369
9648
|
}
|
|
9370
9649
|
});
|
|
9650
|
+
Object.defineProperty(exports, "relationalColumnName", {
|
|
9651
|
+
enumerable: true,
|
|
9652
|
+
get: function() {
|
|
9653
|
+
return relationalColumnName;
|
|
9654
|
+
}
|
|
9655
|
+
});
|
|
9371
9656
|
Object.defineProperty(exports, "resolveCalendarBuckets", {
|
|
9372
9657
|
enumerable: true,
|
|
9373
9658
|
get: function() {
|
|
@@ -9392,6 +9677,12 @@ Object.defineProperty(exports, "searchTermInteger", {
|
|
|
9392
9677
|
return searchTermInteger;
|
|
9393
9678
|
}
|
|
9394
9679
|
});
|
|
9680
|
+
Object.defineProperty(exports, "serializeDefaultValue", {
|
|
9681
|
+
enumerable: true,
|
|
9682
|
+
get: function() {
|
|
9683
|
+
return serializeDefaultValue;
|
|
9684
|
+
}
|
|
9685
|
+
});
|
|
9395
9686
|
Object.defineProperty(exports, "snapshotToExistingColumns", {
|
|
9396
9687
|
enumerable: true,
|
|
9397
9688
|
get: function() {
|