@atscript/db 0.1.154 → 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-DAIAq6XR.mjs → column-diff-C68V_eYM.mjs} +346 -65
- package/dist/{column-diff-qnzx1o3c.cjs → column-diff-DMkGYAGM.cjs} +357 -64
- 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,12 +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;
|
|
787
|
+
const prop = entries.find((e) => !e.member);
|
|
788
|
+
const base = prop && require_validator.columnUnionBase(prop.type);
|
|
681
789
|
const target = flat.metadata;
|
|
682
790
|
target.clear();
|
|
683
791
|
for (const entry of entries) {
|
|
684
|
-
|
|
685
|
-
|
|
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);
|
|
686
795
|
}
|
|
796
|
+
if (base && prop) prop.metadata = target;
|
|
687
797
|
}
|
|
688
798
|
}
|
|
689
799
|
/**
|
|
@@ -706,7 +816,7 @@ var TableMetadata = class {
|
|
|
706
816
|
* Scans `@db.*` and `@meta.id` annotations on a field during flattening.
|
|
707
817
|
*/
|
|
708
818
|
_scanGenericAnnotations(fieldName, fieldType, metadata, logger) {
|
|
709
|
-
if (metadata.has("meta.id")) {
|
|
819
|
+
if (metadata.has("meta.id") && !fieldName.includes(".")) {
|
|
710
820
|
this.primaryKeys.push(fieldName);
|
|
711
821
|
this.originalMetaIdFields.push(fieldName);
|
|
712
822
|
}
|
|
@@ -969,7 +1079,7 @@ var TableMetadata = class {
|
|
|
969
1079
|
const isJson = this.jsonFields.has(path);
|
|
970
1080
|
const isArray = designType === "array";
|
|
971
1081
|
const isObject = designType === "object";
|
|
972
|
-
if (isArray) this.jsonFields.add(path);
|
|
1082
|
+
if (isArray || isStructuredMixedUnion(type)) this.jsonFields.add(path);
|
|
973
1083
|
else if (isObject && isJson) {} else if (isObject && !isJson) this.flattenedParents.add(path);
|
|
974
1084
|
}
|
|
975
1085
|
for (const ignoredField of this.ignoredFields) if (this.flattenedParents.has(ignoredField)) {
|
|
@@ -1071,6 +1181,7 @@ var TableMetadata = class {
|
|
|
1071
1181
|
const isJson = this.jsonFields.has(path);
|
|
1072
1182
|
const isFlattened = !skipFlattening && require_object.findAncestorInSet(path, this.flattenedParents) !== void 0;
|
|
1073
1183
|
const designType = isEncrypted ? "string" : isJson ? "json" : resolveDesignType(type);
|
|
1184
|
+
const colType = columnType(type);
|
|
1074
1185
|
let storage;
|
|
1075
1186
|
if (skipFlattening) storage = "column";
|
|
1076
1187
|
else if (isEncrypted) storage = isFlattened ? "flattened" : "column";
|
|
@@ -1089,10 +1200,10 @@ var TableMetadata = class {
|
|
|
1089
1200
|
if (quantityRef) this.quantityRefByField.set(path, quantityRef);
|
|
1090
1201
|
descriptors.push({
|
|
1091
1202
|
path,
|
|
1092
|
-
type,
|
|
1203
|
+
type: colType,
|
|
1093
1204
|
physicalName,
|
|
1094
1205
|
designType,
|
|
1095
|
-
optional: type.optional === true,
|
|
1206
|
+
optional: skipFlattening ? type.optional === true : this._columnNullable(path, type),
|
|
1096
1207
|
isPrimaryKey: this.primaryKeys.includes(path),
|
|
1097
1208
|
ignored: this.ignoredFields.has(path),
|
|
1098
1209
|
defaultValue: this.defaults.get(path),
|
|
@@ -1106,7 +1217,7 @@ var TableMetadata = class {
|
|
|
1106
1217
|
unitCode,
|
|
1107
1218
|
unitRefField,
|
|
1108
1219
|
encrypted: isEncrypted || underEncrypted || void 0,
|
|
1109
|
-
isGeoPoint: isGeoPointType(
|
|
1220
|
+
isGeoPoint: isGeoPointType(colType) || void 0,
|
|
1110
1221
|
derived: this.derivedFields.get(path),
|
|
1111
1222
|
computed: computedMeta(rootType, path)
|
|
1112
1223
|
});
|
|
@@ -1234,7 +1345,7 @@ var TableMetadata = class {
|
|
|
1234
1345
|
for (const index of this.indexes.values()) for (const field of index.fields) {
|
|
1235
1346
|
const ftype = this.flatMap.get(field.name);
|
|
1236
1347
|
if (ftype) {
|
|
1237
|
-
field.optional =
|
|
1348
|
+
field.optional = this._columnNullable(field.name, ftype);
|
|
1238
1349
|
field.designType = ftype.type.tags?.has("objectId") ? "objectId" : resolveDesignType(ftype);
|
|
1239
1350
|
}
|
|
1240
1351
|
field.name = this.physicalPath(field.name);
|
|
@@ -1647,6 +1758,78 @@ function rewriteIntegerRegex(filter, meta) {
|
|
|
1647
1758
|
return out ?? filter;
|
|
1648
1759
|
}
|
|
1649
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
|
|
1650
1833
|
//#region src/query/aggregate-expr.ts
|
|
1651
1834
|
/**
|
|
1652
1835
|
* Output aliases of the computed `$select` entries of a grouped query —
|
|
@@ -2298,7 +2481,7 @@ var FieldMappingStrategy = class {
|
|
|
2298
2481
|
* translate predicate operands at deeper levels).
|
|
2299
2482
|
*/
|
|
2300
2483
|
translateFilter(filter, meta, depth = 0) {
|
|
2301
|
-
filter = rewriteIntegerRegex(filter, meta);
|
|
2484
|
+
filter = rewriteObjectNullTests(rewriteIntegerRegex(filter, meta), meta);
|
|
2302
2485
|
const has = require_nested_writer.containsRelationFilter(filter);
|
|
2303
2486
|
const resolved = has ? require_nested_writer.resolveRelationFilterTree(filter, meta, depth) : filter;
|
|
2304
2487
|
return this.noteTranslated(filter, this.translateResolvedFilter(resolved, meta), has);
|
|
@@ -2640,13 +2823,13 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
2640
2823
|
current = created;
|
|
2641
2824
|
} else current = next;
|
|
2642
2825
|
}
|
|
2643
|
-
current[col.last] = value;
|
|
2826
|
+
if (!col.partial || value !== null && value !== void 0) current[col.last] = value;
|
|
2644
2827
|
}
|
|
2645
2828
|
for (const parent of plan.parents) collapseNullParent(result, parent);
|
|
2646
2829
|
return result;
|
|
2647
2830
|
}
|
|
2648
2831
|
translateQuery(query, meta) {
|
|
2649
|
-
const logical = rewriteIntegerRegex(query.filter, meta);
|
|
2832
|
+
const logical = rewriteObjectNullTests(rewriteIntegerRegex(query.filter, meta), meta);
|
|
2650
2833
|
const has = require_nested_writer.containsRelationFilter(logical);
|
|
2651
2834
|
const filter = has ? require_nested_writer.resolveRelationFilterTree(logical, meta, 0) : logical;
|
|
2652
2835
|
if (!meta.requiresMappings) {
|
|
@@ -2701,7 +2884,11 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
2701
2884
|
return result;
|
|
2702
2885
|
}
|
|
2703
2886
|
prepareForWrite(payload, meta, adapter) {
|
|
2704
|
-
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
|
+
}
|
|
2705
2892
|
const data = { ...payload };
|
|
2706
2893
|
this.prepareCommon(data, meta, adapter);
|
|
2707
2894
|
if (meta.onlyColumnRenames) {
|
|
@@ -2728,7 +2915,7 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
2728
2915
|
const suffix = operatorMatch ? operatorMatch[2] : "";
|
|
2729
2916
|
const fd = meta.leafByLogical.get(basePath);
|
|
2730
2917
|
const finalKey = (fd?.physicalName ?? basePath) + suffix;
|
|
2731
|
-
if (fd?.storage === "json" &&
|
|
2918
|
+
if (fd?.storage === "json" && value !== null && value !== void 0 && !suffix) result[finalKey] = JSON.stringify(value);
|
|
2732
2919
|
else result[finalKey] = value;
|
|
2733
2920
|
}
|
|
2734
2921
|
return this.formatWriteValues(result, meta);
|
|
@@ -2808,6 +2995,49 @@ var RelationalFieldMapper = class extends FieldMappingStrategy {
|
|
|
2808
2995
|
return result;
|
|
2809
2996
|
}
|
|
2810
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
|
+
}
|
|
2811
3041
|
/**
|
|
2812
3042
|
* Per-table read plan of {@link RelationalFieldMapper.reconstructFromRead}:
|
|
2813
3043
|
* the per-column decisions (coercion, JSON, nesting — the dot-path pre-split)
|
|
@@ -2827,12 +3057,14 @@ function compileReadPlan(meta) {
|
|
|
2827
3057
|
const fromFmts = meta.fromStorageFormatters;
|
|
2828
3058
|
const columns = /* @__PURE__ */ new Map();
|
|
2829
3059
|
for (const [physical, fd] of meta.leafByPhysical) {
|
|
2830
|
-
const
|
|
3060
|
+
const nested = (fd.storage === "json" || fd.storage === "flattened") && fd.path.includes(".");
|
|
3061
|
+
const segs = nested ? fd.path.split(".") : void 0;
|
|
2831
3062
|
columns.set(physical, {
|
|
2832
3063
|
path: fd.path,
|
|
2833
3064
|
parents: segs?.slice(0, -1),
|
|
2834
3065
|
last: segs ? segs[segs.length - 1] : fd.path,
|
|
2835
3066
|
json: fd.storage === "json",
|
|
3067
|
+
partial: nested && meta.presence(fd.path) === "partial",
|
|
2836
3068
|
coerce: fd.designType === "boolean" ? toBool : fd.designType === "decimal" ? toDecimalString : void 0,
|
|
2837
3069
|
fromFmt: fromFmts?.get(physical)
|
|
2838
3070
|
});
|
|
@@ -2843,15 +3075,16 @@ function compileReadPlan(meta) {
|
|
|
2843
3075
|
parents.push({
|
|
2844
3076
|
ancestors: segs.slice(0, -1),
|
|
2845
3077
|
last: segs[segs.length - 1],
|
|
2846
|
-
|
|
3078
|
+
presence: meta.presence(parentPath) ?? "required"
|
|
2847
3079
|
});
|
|
2848
3080
|
}
|
|
3081
|
+
parents.sort((a, b) => b.ancestors.length - a.ancestors.length);
|
|
2849
3082
|
return {
|
|
2850
3083
|
columns,
|
|
2851
3084
|
parents
|
|
2852
3085
|
};
|
|
2853
3086
|
}
|
|
2854
|
-
/** 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}). */
|
|
2855
3088
|
function collapseNullParent(obj, parent) {
|
|
2856
3089
|
let current = obj;
|
|
2857
3090
|
const ancestors = parent.ancestors;
|
|
@@ -2866,7 +3099,8 @@ function collapseNullParent(obj, parent) {
|
|
|
2866
3099
|
const v = parentObj[k];
|
|
2867
3100
|
if (v !== null && v !== void 0) return;
|
|
2868
3101
|
}
|
|
2869
|
-
|
|
3102
|
+
if (parent.presence === "partial") delete current[parent.last];
|
|
3103
|
+
else current[parent.last] = parent.presence === "nullable" ? null : {};
|
|
2870
3104
|
}
|
|
2871
3105
|
/**
|
|
2872
3106
|
* Per-table write plan of the relational flatten: each logical path's
|
|
@@ -3308,6 +3542,10 @@ function collectQueryPaths(query, aggregate) {
|
|
|
3308
3542
|
path,
|
|
3309
3543
|
predicate,
|
|
3310
3544
|
relation: relationOpsOf(value)
|
|
3545
|
+
} : isNullTest(value) ? {
|
|
3546
|
+
path,
|
|
3547
|
+
predicate,
|
|
3548
|
+
nullTest: true
|
|
3311
3549
|
} : {
|
|
3312
3550
|
path,
|
|
3313
3551
|
predicate
|
|
@@ -3407,10 +3645,11 @@ function pathSourceOf(meta) {
|
|
|
3407
3645
|
* `FieldCapabilityIndex`, with `$with` hints and leaf lists) is what clients
|
|
3408
3646
|
* see and is authoritative — the HTTP gate always answers first.
|
|
3409
3647
|
*/
|
|
3410
|
-
function guardPath(meta, adapter, path, op, predicate = "compare") {
|
|
3648
|
+
function guardPath(meta, adapter, path, op, predicate = "compare", nullTest = false) {
|
|
3411
3649
|
const verb = OP_VERB[op];
|
|
3412
3650
|
const { kind, parent } = classifyQueryPath(pathSourceOf(meta), path);
|
|
3413
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;
|
|
3414
3653
|
switch (kind) {
|
|
3415
3654
|
case "nav":
|
|
3416
3655
|
if (op === "filter" && predicate === "relation" && parent === void 0) return;
|
|
@@ -3495,7 +3734,7 @@ function guardPaths(meta, adapter, query, aggregate = false, state) {
|
|
|
3495
3734
|
if (refs.unsupportedOperator !== void 0) throw pathError(refs.unsupportedOperator, unsupportedOperatorMessage(refs.unsupportedOperator));
|
|
3496
3735
|
let relState = state;
|
|
3497
3736
|
for (const ref of refs.filter) {
|
|
3498
|
-
guardPath(meta, adapter, ref.path, "filter", ref.predicate);
|
|
3737
|
+
guardPath(meta, adapter, ref.path, "filter", ref.predicate, ref.nullTest);
|
|
3499
3738
|
if (ref.predicate === "relation") guardRelationRef(meta, adapter, ref, relState ??= require_nested_writer.relGuardState());
|
|
3500
3739
|
}
|
|
3501
3740
|
for (const path of refs.sort) guardPath(meta, adapter, path, "sort");
|
|
@@ -3637,10 +3876,12 @@ var NullsSource = class {
|
|
|
3637
3876
|
}
|
|
3638
3877
|
/**
|
|
3639
3878
|
* Whether ordering by the LOGICAL field `path` can meet NULL (or a missing
|
|
3640
|
-
* value): the field
|
|
3641
|
-
*
|
|
3642
|
-
*
|
|
3643
|
-
*
|
|
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.
|
|
3644
3885
|
*/
|
|
3645
3886
|
nullable(path) {
|
|
3646
3887
|
let answer = this._nullable.get(path);
|
|
@@ -3654,12 +3895,7 @@ var NullsSource = class {
|
|
|
3654
3895
|
const { meta } = this._init;
|
|
3655
3896
|
const fd = meta.descriptorByPath.get(path);
|
|
3656
3897
|
if (!fd || fd.derived) return true;
|
|
3657
|
-
|
|
3658
|
-
if (meta.flatMap.get(p)?.optional === true) return true;
|
|
3659
|
-
const dot = p.lastIndexOf(".");
|
|
3660
|
-
if (dot === -1) return false;
|
|
3661
|
-
p = p.slice(0, dot);
|
|
3662
|
-
}
|
|
3898
|
+
return meta.isNullable(path);
|
|
3663
3899
|
}
|
|
3664
3900
|
/**
|
|
3665
3901
|
* The effective `$nulls` of the `$sort` keys of one read: the request's
|
|
@@ -3774,7 +4010,9 @@ function geoIndexNotFoundMessage(tableName, indexName) {
|
|
|
3774
4010
|
* otherwise trips up every adapter author.
|
|
3775
4011
|
*
|
|
3776
4012
|
* For union types (e.g., from flattened `{...} | {...}` objects):
|
|
3777
|
-
* -
|
|
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)
|
|
3778
4016
|
* - If members disagree → returns `'union'` (out of scope for type management)
|
|
3779
4017
|
*/
|
|
3780
4018
|
function resolveDesignType(fieldType) {
|
|
@@ -3782,9 +4020,9 @@ function resolveDesignType(fieldType) {
|
|
|
3782
4020
|
if (fieldType.type.kind === "object") return "object";
|
|
3783
4021
|
if (fieldType.type.kind === "array") return "array";
|
|
3784
4022
|
if (fieldType.type.kind === "union") {
|
|
3785
|
-
const
|
|
3786
|
-
if (
|
|
3787
|
-
const resolved =
|
|
4023
|
+
const { members } = require_validator.unionValueMembers(fieldType);
|
|
4024
|
+
if (members.length > 0) {
|
|
4025
|
+
const resolved = members.map((item) => resolveDesignType(item));
|
|
3788
4026
|
if (resolved.every((type) => type === resolved[0])) return resolved[0];
|
|
3789
4027
|
}
|
|
3790
4028
|
return "union";
|
|
@@ -4440,21 +4678,13 @@ var AtscriptDbReadable = class {
|
|
|
4440
4678
|
_totalOrderKeys() {
|
|
4441
4679
|
if (this._totalOrderKeysCache) return this._totalOrderKeysCache;
|
|
4442
4680
|
const meta = this._meta;
|
|
4443
|
-
const nullable = (logical) => {
|
|
4444
|
-
for (let path = logical;;) {
|
|
4445
|
-
if (meta.flatMap.get(path)?.optional === true) return true;
|
|
4446
|
-
const dot = path.lastIndexOf(".");
|
|
4447
|
-
if (dot === -1) return false;
|
|
4448
|
-
path = path.slice(0, dot);
|
|
4449
|
-
}
|
|
4450
|
-
};
|
|
4451
4681
|
const sets = [];
|
|
4452
4682
|
if (meta.primaryKeys.length > 0) sets.push(meta.primaryKeys.map((f) => meta.physicalPath(f)));
|
|
4453
4683
|
for (const index of meta.indexes.values()) {
|
|
4454
4684
|
if (index.type !== "unique") continue;
|
|
4455
|
-
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));
|
|
4456
4686
|
}
|
|
4457
|
-
for (const prop of meta.uniqueProps) if (!
|
|
4687
|
+
for (const prop of meta.uniqueProps) if (!meta.isNullable(prop)) sets.push([meta.physicalPath(prop)]);
|
|
4458
4688
|
this._totalOrderKeysCache = sets;
|
|
4459
4689
|
return sets;
|
|
4460
4690
|
}
|
|
@@ -6075,7 +6305,7 @@ var BaseDbAdapter = class {
|
|
|
6075
6305
|
}
|
|
6076
6306
|
/**
|
|
6077
6307
|
* Whether the table has at least one row. Schema sync uses it in the
|
|
6078
|
-
* pre-flight phase to
|
|
6308
|
+
* pre-flight phase to check a primary-key change on a populated table.
|
|
6079
6309
|
* Override with an EXISTS/LIMIT 1 probe — this default is `count() > 0`,
|
|
6080
6310
|
* a full scan on some engines, and it can only answer for the adapter's
|
|
6081
6311
|
* OWN table: for another `tableName` (or on an administrative adapter
|
|
@@ -6495,6 +6725,10 @@ function flattenPatchPayload(payload, prefix, update, meta, table) {
|
|
|
6495
6725
|
const key = prefix ? `${prefix}.${_key}` : _key;
|
|
6496
6726
|
if (primaryKeys.includes(key)) continue;
|
|
6497
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
|
+
}
|
|
6498
6732
|
update[key] = value;
|
|
6499
6733
|
continue;
|
|
6500
6734
|
}
|
|
@@ -7672,26 +7906,56 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
7672
7906
|
* full row). Function defaults (`now` / `uuid` / `increment` / custom) the
|
|
7673
7907
|
* adapter handles natively are NOT filled — the field stays absent so the
|
|
7674
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.
|
|
7675
7913
|
*/
|
|
7676
7914
|
_applyDefaults(data) {
|
|
7677
7915
|
const nativeFns = this.adapter.nativeDefaultFns();
|
|
7678
7916
|
const versionField = this._meta.versionField;
|
|
7679
7917
|
for (const [field, def] of this._meta.defaults.entries()) {
|
|
7680
7918
|
if (field === versionField) continue;
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
break;
|
|
7687
|
-
case "uuid":
|
|
7688
|
-
data[field] = crypto.randomUUID();
|
|
7689
|
-
break;
|
|
7690
|
-
}
|
|
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();
|
|
7691
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);
|
|
7692
7929
|
}
|
|
7693
7930
|
return data;
|
|
7694
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
|
+
}
|
|
7695
7959
|
/**
|
|
7696
7960
|
* Parsed `@db.default 'literal'` values that are primitives — reused for
|
|
7697
7961
|
* every row (an object / array default is parsed per row: each row gets
|
|
@@ -7857,6 +8121,21 @@ var AtscriptDbTable = class extends AtscriptDbReadable {
|
|
|
7857
8121
|
return this.createValidator({ plugins: adapterPlugins });
|
|
7858
8122
|
}
|
|
7859
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
|
+
}
|
|
7860
8139
|
//#endregion
|
|
7861
8140
|
//#region src/table/view-read-plan.ts
|
|
7862
8141
|
/** Design types whose `=` compares one scalar to one scalar on every engine. */
|
|
@@ -8969,7 +9248,9 @@ function computeColumnDiff(desired, existing, typeMapper, opts) {
|
|
|
8969
9248
|
});
|
|
8970
9249
|
}
|
|
8971
9250
|
const desiredDefault = serializeDefaultValue(field.defaultValue);
|
|
8972
|
-
|
|
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({
|
|
8973
9254
|
field,
|
|
8974
9255
|
oldDefault: existingCol.dflt_value,
|
|
8975
9256
|
newDefault: desiredDefault
|
|
@@ -9366,6 +9647,12 @@ Object.defineProperty(exports, "numericOperandProblem", {
|
|
|
9366
9647
|
return numericOperandProblem;
|
|
9367
9648
|
}
|
|
9368
9649
|
});
|
|
9650
|
+
Object.defineProperty(exports, "relationalColumnName", {
|
|
9651
|
+
enumerable: true,
|
|
9652
|
+
get: function() {
|
|
9653
|
+
return relationalColumnName;
|
|
9654
|
+
}
|
|
9655
|
+
});
|
|
9369
9656
|
Object.defineProperty(exports, "resolveCalendarBuckets", {
|
|
9370
9657
|
enumerable: true,
|
|
9371
9658
|
get: function() {
|
|
@@ -9390,6 +9677,12 @@ Object.defineProperty(exports, "searchTermInteger", {
|
|
|
9390
9677
|
return searchTermInteger;
|
|
9391
9678
|
}
|
|
9392
9679
|
});
|
|
9680
|
+
Object.defineProperty(exports, "serializeDefaultValue", {
|
|
9681
|
+
enumerable: true,
|
|
9682
|
+
get: function() {
|
|
9683
|
+
return serializeDefaultValue;
|
|
9684
|
+
}
|
|
9685
|
+
});
|
|
9393
9686
|
Object.defineProperty(exports, "snapshotToExistingColumns", {
|
|
9394
9687
|
enumerable: true,
|
|
9395
9688
|
get: function() {
|