@sap/cds-compiler 2.11.4 → 2.13.8
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/CHANGELOG.md +159 -1
- package/bin/cds_update_identifiers.js +7 -7
- package/bin/cdsc.js +22 -23
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +25 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +30 -63
- package/lib/api/options.js +5 -5
- package/lib/api/validate.js +0 -5
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +52 -2
- package/lib/base/messages.js +16 -26
- package/lib/base/model.js +2 -62
- package/lib/base/optionProcessorHelper.js +246 -183
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +2 -1
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/foreignKeys.js +4 -4
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedInType.js +4 -4
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/queryNoDbArtifacts.js +1 -3
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +94 -0
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +12 -7
- package/lib/compiler/assert-consistency.js +10 -6
- package/lib/compiler/base.js +0 -1
- package/lib/compiler/builtins.js +8 -6
- package/lib/compiler/checks.js +46 -12
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +33 -14
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +113 -47
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +76 -38
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +204 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -25
- package/lib/edm/annotations/preprocessAnnotations.js +3 -3
- package/lib/edm/csn2edm.js +10 -9
- package/lib/edm/edm.js +19 -20
- package/lib/edm/edmPreprocessor.js +166 -95
- package/lib/edm/edmUtils.js +127 -34
- package/lib/gen/Dictionary.json +92 -43
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -82
- package/lib/gen/languageLexer.interp +18 -1
- package/lib/gen/languageLexer.js +925 -847
- package/lib/gen/languageLexer.tokens +78 -74
- package/lib/gen/languageParser.js +5434 -4298
- package/lib/json/from-csn.js +59 -17
- package/lib/json/to-csn.js +143 -71
- package/lib/language/antlrParser.js +3 -3
- package/lib/language/docCommentParser.js +3 -3
- package/lib/language/genericAntlrParser.js +144 -54
- package/lib/language/language.g4 +424 -203
- package/lib/language/multiLineStringParser.js +536 -0
- package/lib/main.d.ts +472 -61
- package/lib/main.js +38 -11
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +321 -204
- package/lib/model/csnUtils.js +224 -263
- package/lib/model/enrichCsn.js +97 -40
- package/lib/model/revealInternalProperties.js +27 -6
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +7 -6
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +36 -33
- package/lib/render/toCdl.js +174 -275
- package/lib/render/toHdbcds.js +201 -115
- package/lib/render/toRename.js +7 -10
- package/lib/render/toSql.js +149 -75
- package/lib/render/utils/common.js +22 -8
- package/lib/render/utils/sql.js +10 -7
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/braceExpression.js +4 -2
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +187 -0
- package/lib/transform/db/cdsPersistence.js +150 -0
- package/lib/transform/db/constraints.js +61 -56
- package/lib/transform/db/expansion.js +50 -29
- package/lib/transform/db/flattening.js +552 -105
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/temporal.js +236 -0
- package/lib/transform/db/transformExists.js +94 -28
- package/lib/transform/db/views.js +5 -4
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +9 -7
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +94 -801
- package/lib/transform/forOdataNew.js +22 -175
- package/lib/transform/localized.js +36 -32
- package/lib/transform/odata/generateForeignKeyElements.js +3 -3
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +47 -33
- package/lib/transform/translateAssocsToJoins.js +10 -27
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/file.js +2 -1
- package/lib/utils/objectUtils.js +30 -0
- package/lib/utils/timetrace.js +8 -2
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2340
- package/lib/compiler/resolver.js +0 -2988
- package/lib/transform/universalCsnEnricher.js +0 -67
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { forEachDefinition } = require('../../base/model');
|
|
4
|
-
const {
|
|
4
|
+
const { applyTransformations, hasAnnotationValue, getResultingName } = require('../../model/csnUtils');
|
|
5
5
|
const { csnRefs } = require('../../model/csnRefs');
|
|
6
|
+
const { forEach, forEachKey } = require('../../utils/objectUtils');
|
|
6
7
|
|
|
7
8
|
const COMPOSITION = 'cds.Composition';
|
|
8
9
|
const ASSOCIATION = 'cds.Association';
|
|
@@ -28,34 +29,36 @@ function createReferentialConstraints(csn, options) {
|
|
|
28
29
|
// compositions must be processed first, as the <up_> links for them must result in `ON DELETE CASCADE`
|
|
29
30
|
const compositions = [];
|
|
30
31
|
const associations = [];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
32
|
+
applyTransformations(csn, {
|
|
33
|
+
elements: (parent, prop, elements, path) => {
|
|
34
|
+
// Step I: iterate compositions, enrich dependent keys for <up_> association in target entity of composition
|
|
35
|
+
for (const elementName in elements) {
|
|
36
|
+
const element = elements[elementName];
|
|
37
|
+
const ePath = path.concat([ 'elements', elementName ]); // Save a copy in this scope for the late callback
|
|
38
|
+
if (element.type === COMPOSITION && element.$selfOnCondition) {
|
|
39
|
+
compositions.push({
|
|
40
|
+
fn: () => {
|
|
41
|
+
foreignKeyConstraintForUpLinkOfComposition(element, parent, ePath);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Step II: iterate associations, enrich dependent keys (in entity containing the association)
|
|
48
|
+
for (const elementName in elements) {
|
|
49
|
+
const element = elements[elementName];
|
|
50
|
+
const ePath = path.concat([ 'elements', elementName ]); // Save a copy in this scope for the late callback
|
|
51
|
+
if (element.keys && isToOne(element) && element.type === ASSOCIATION || element.type === COMPOSITION && treatCompositionLikeAssociation(element)) {
|
|
52
|
+
associations.push({
|
|
53
|
+
fn: () => {
|
|
54
|
+
foreignKeyConstraintForAssociation(element, ePath );
|
|
55
|
+
},
|
|
56
|
+
});
|
|
55
57
|
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
});
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
}, [], { skipIgnore: false, skipArtifact: a => a.query || a.kind !== 'entity' });
|
|
61
|
+
|
|
59
62
|
// create constraints on foreign keys
|
|
60
63
|
// always process unmanaged first, up_ links must be flagged
|
|
61
64
|
// before they are processed
|
|
@@ -215,8 +218,7 @@ function createReferentialConstraints(csn, options) {
|
|
|
215
218
|
* Skip referential constraint if the parent table (association target, or artifact where composition is defined)
|
|
216
219
|
* of the relation is:
|
|
217
220
|
* - a query
|
|
218
|
-
* -
|
|
219
|
-
* - TODO: Revisit -- annotated with '@cds.persistence.exists:true'
|
|
221
|
+
* - annotated with '@cds.persistence.skip:true'
|
|
220
222
|
*
|
|
221
223
|
* The following decision table reflects the current implementation:
|
|
222
224
|
*
|
|
@@ -299,13 +301,15 @@ function createReferentialConstraints(csn, options) {
|
|
|
299
301
|
|
|
300
302
|
return true;
|
|
301
303
|
}
|
|
304
|
+
const runtimeChecks = options.assertIntegrityType && options.assertIntegrityType.toUpperCase() === RT;
|
|
305
|
+
const compilerChecks = options.assertIntegrityType && options.assertIntegrityType.toUpperCase() === DB;
|
|
302
306
|
|
|
303
307
|
if ((!options.assertIntegrity || options.assertIntegrity === true || options.assertIntegrity === 'true') &&
|
|
304
|
-
(!options.assertIntegrityType ||
|
|
308
|
+
(!options.assertIntegrityType || runtimeChecks))
|
|
305
309
|
return assertForIntegrityTypeRT();
|
|
306
310
|
|
|
307
311
|
if ((!options.assertIntegrity || options.assertIntegrity === true || options.assertIntegrity === 'true') &&
|
|
308
|
-
|
|
312
|
+
compilerChecks)
|
|
309
313
|
return assertForIntegrityTypeDB();
|
|
310
314
|
|
|
311
315
|
if ((options.assertIntegrity === 'individual'))
|
|
@@ -313,7 +317,7 @@ function createReferentialConstraints(csn, options) {
|
|
|
313
317
|
|
|
314
318
|
// The default for the assertIntegrityType is 'RT', no constraints in that case
|
|
315
319
|
if ((!options.assertIntegrity || options.assertIntegrity === true) &&
|
|
316
|
-
(!options.assertIntegrityType ||
|
|
320
|
+
(!options.assertIntegrityType || runtimeChecks))
|
|
317
321
|
return true;
|
|
318
322
|
|
|
319
323
|
if (!element.keys || !isToOne(element))
|
|
@@ -385,7 +389,7 @@ function createReferentialConstraints(csn, options) {
|
|
|
385
389
|
* @returns {boolean}
|
|
386
390
|
*/
|
|
387
391
|
function isAssertIntegrityAnnotationSetTo(value) {
|
|
388
|
-
return hasAnnotationValue(element, '@assert.integrity', value);
|
|
392
|
+
return hasAnnotationValue(element, '@assert.integrity', value, true);
|
|
389
393
|
}
|
|
390
394
|
|
|
391
395
|
/**
|
|
@@ -489,18 +493,20 @@ function createReferentialConstraints(csn, options) {
|
|
|
489
493
|
const dependentKey = [ elementName ];
|
|
490
494
|
const onDeleteRules = new Set();
|
|
491
495
|
onDeleteRules.add($foreignKeyConstraint.onDelete);
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
496
|
+
forEach(artifact.elements, (foreignKeyName, foreignKey) => {
|
|
497
|
+
// find all other `$foreignKeyConstraint`s with same `$sourceAssociation` and same `parentTable`
|
|
498
|
+
const matchingForeignKeyFound = foreignKey.$foreignKeyConstraint &&
|
|
499
|
+
foreignKey.$foreignKeyConstraint.sourceAssociation === $foreignKeyConstraint.sourceAssociation &&
|
|
500
|
+
foreignKey.$foreignKeyConstraint.parentTable === $foreignKeyConstraint.parentTable;
|
|
501
|
+
if (!matchingForeignKeyFound)
|
|
502
|
+
return;
|
|
503
|
+
|
|
504
|
+
const $foreignKeyConstraintCopy = Object.assign({}, foreignKey.$foreignKeyConstraint);
|
|
505
|
+
delete foreignKey.$foreignKeyConstraint;
|
|
506
|
+
parentKey.push($foreignKeyConstraintCopy.parentKey);
|
|
507
|
+
dependentKey.push(foreignKeyName);
|
|
508
|
+
onDeleteRules.add($foreignKeyConstraintCopy.onDelete);
|
|
509
|
+
});
|
|
504
510
|
// onDelete Rule is the "weakest" rule applicable. Precedence: RESTRICT > SET NULL > CASCADE
|
|
505
511
|
const onDelete = onDeleteRules.has('RESTRICT') ? 'RESTRICT' : 'CASCADE';
|
|
506
512
|
let onDeleteRemark = null;
|
|
@@ -508,14 +514,14 @@ function createReferentialConstraints(csn, options) {
|
|
|
508
514
|
if (options.testMode && onDelete === 'CASCADE')
|
|
509
515
|
onDeleteRemark = `Up_ link for Composition "${$foreignKeyConstraint.upLinkFor}" implies existential dependency`;
|
|
510
516
|
referentialConstraints[`${getResultingName(csn, 'quoted', artifactName)}_${$foreignKeyConstraint.sourceAssociation}`] = {
|
|
511
|
-
identifier
|
|
517
|
+
// constraint identifier start with `c__` to avoid name clashes
|
|
518
|
+
identifier: `c__${getResultingName(csn, options.forHana.names, artifactName)}_${$foreignKeyConstraint.sourceAssociation}`,
|
|
512
519
|
foreignKey: dependentKey,
|
|
513
520
|
parentKey,
|
|
514
521
|
dependentTable: artifactName,
|
|
515
522
|
parentTable,
|
|
516
523
|
onDelete,
|
|
517
524
|
onDeleteRemark, // explain why this particular rule is chosen
|
|
518
|
-
// TODO: do we want to switch off validation / enforcement via annotation on association?
|
|
519
525
|
validated: $foreignKeyConstraint.validated,
|
|
520
526
|
enforced: $foreignKeyConstraint.enforced,
|
|
521
527
|
};
|
|
@@ -543,15 +549,14 @@ function assertConstraintIdentifierUniqueness(artifact, artifactName, path, erro
|
|
|
543
549
|
if (!(artifact.$tableConstraints && artifact.$tableConstraints.referential && artifact.$tableConstraints.unique))
|
|
544
550
|
return;
|
|
545
551
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
.
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
});
|
|
552
|
+
forEachKey(artifact.$tableConstraints.unique, (uniqueConstraintKey) => {
|
|
553
|
+
const uniqueConstraintIdentifier = `${artifactName}_${uniqueConstraintKey}`; // final unique constraint identifier will be generated in renderer likewise
|
|
554
|
+
if (artifact.$tableConstraints.referential[uniqueConstraintIdentifier]) {
|
|
555
|
+
error(null, path,
|
|
556
|
+
{ name: uniqueConstraintIdentifier, art: artifactName },
|
|
557
|
+
'Duplicate constraint name $(NAME) in artifact $(ART)');
|
|
558
|
+
}
|
|
559
|
+
});
|
|
555
560
|
}
|
|
556
561
|
|
|
557
562
|
module.exports = { createReferentialConstraints, assertConstraintIdentifierUniqueness };
|
|
@@ -20,8 +20,9 @@ const { setProp, isBetaEnabled } = require('../../base/model');
|
|
|
20
20
|
* @param {Function} messageFunctions.error
|
|
21
21
|
* @param {Function} messageFunctions.info
|
|
22
22
|
* @param {Function} messageFunctions.throwWithError
|
|
23
|
+
* @param {object} iterateOptions
|
|
23
24
|
*/
|
|
24
|
-
function expandStructureReferences(csn, options, pathDelimiter, { error, info, throwWithError }) {
|
|
25
|
+
function expandStructureReferences(csn, options, pathDelimiter, { error, info, throwWithError }, iterateOptions = {}) {
|
|
25
26
|
const {
|
|
26
27
|
isStructured, get$combined, getFinalBaseType, getServiceName,
|
|
27
28
|
} = getUtils(csn);
|
|
@@ -39,7 +40,11 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
39
40
|
const artifact = csn.definitions[path[1]];
|
|
40
41
|
if (!hasAnnotationValue(artifact, '@cds.persistence.table')) {
|
|
41
42
|
const root = get$combined({ SELECT: parent });
|
|
42
|
-
|
|
43
|
+
// TODO: replace with the correct options.transformation?
|
|
44
|
+
// Do not expand the * in OData for a moment, not to introduce changes
|
|
45
|
+
// while the OData CSN is still official
|
|
46
|
+
if (!options.toOdata)
|
|
47
|
+
parent.columns = replaceStar(root, columns, parent.excluding);
|
|
43
48
|
parent.columns = expand(parent.columns, path.concat('columns'), true);
|
|
44
49
|
}
|
|
45
50
|
},
|
|
@@ -49,7 +54,7 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
49
54
|
orderBy: (parent, name, orderBy, path) => {
|
|
50
55
|
parent.orderBy = expand(orderBy, path.concat('orderBy'));
|
|
51
56
|
},
|
|
52
|
-
});
|
|
57
|
+
}, [], iterateOptions);
|
|
53
58
|
|
|
54
59
|
/**
|
|
55
60
|
* Turn .expand/.inline into normal refs. @cds.persistence.skip .expand with to-many (and all transitive views).
|
|
@@ -71,16 +76,25 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
71
76
|
if (!hasAnnotationValue(artifact, '@cds.persistence.table')) {
|
|
72
77
|
const rewritten = rewrite(root, parent.columns, parent.excluding);
|
|
73
78
|
parent.columns = rewritten.columns;
|
|
74
|
-
|
|
79
|
+
/*
|
|
80
|
+
* Do not remove unexpandable many columns in OData
|
|
81
|
+
*/
|
|
82
|
+
if (rewritten.toMany.length > 0 && !options.toOdata) {
|
|
75
83
|
markAsToDummyfy(artifact, path[1]);
|
|
76
84
|
if (getServiceName(path[1]) === null)
|
|
77
85
|
error( null, [ 'definitions', path[1] ], { name: path[1] }, 'Unexpected .expand with to-many association in entity $(NAME), which is outside any service');
|
|
78
86
|
}
|
|
87
|
+
else {
|
|
88
|
+
parent.columns = rewritten.columns;
|
|
89
|
+
}
|
|
79
90
|
}
|
|
80
91
|
},
|
|
81
92
|
});
|
|
82
93
|
|
|
83
|
-
|
|
94
|
+
// OData must keep @cds.persistence.skip definitions
|
|
95
|
+
// to present them in the API (and CSN)
|
|
96
|
+
if (!options.toOdata)
|
|
97
|
+
dummyfy();
|
|
84
98
|
|
|
85
99
|
cleanup.forEach(fn => fn());
|
|
86
100
|
|
|
@@ -88,23 +102,26 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
88
102
|
|
|
89
103
|
|
|
90
104
|
const publishing = [];
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
// OData must allow navigations to @cds.persistence.skip targets
|
|
106
|
+
// as valid navigations in the API
|
|
107
|
+
if (!options.toOdata) {
|
|
108
|
+
applyTransformations(csn, {
|
|
109
|
+
target: (parent, name, target, path) => {
|
|
110
|
+
if (toDummyfy.indexOf(target) !== -1) {
|
|
111
|
+
publishing.push({
|
|
112
|
+
parent, name, target, path: [ ...path ],
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
from: check,
|
|
117
|
+
columns: check,
|
|
118
|
+
where: check,
|
|
119
|
+
groupBy: check,
|
|
120
|
+
orderBy: check,
|
|
121
|
+
having: check,
|
|
122
|
+
limit: check,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
108
125
|
|
|
109
126
|
|
|
110
127
|
/**
|
|
@@ -239,15 +256,14 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
239
256
|
* @param {CSN.Artifact} root All elements visible fromt he query source ($combined)
|
|
240
257
|
* @param {CSN.Column[]} columns
|
|
241
258
|
* @param {string[]} excluding
|
|
242
|
-
* @returns {{columns: Array,
|
|
259
|
+
* @returns {{columns: Array, toMany: Array}} Object with rewritten columns (.expand/.inline) and with any .expand + to-many
|
|
243
260
|
*/
|
|
244
261
|
function rewrite(root, columns, excluding) {
|
|
245
262
|
const allToMany = [];
|
|
246
263
|
const newThing = [];
|
|
247
264
|
// Replace stars - needs to happen here since the .expand/.inline first path step affects the root *
|
|
248
265
|
columns = replaceStar(root, columns, excluding);
|
|
249
|
-
for (
|
|
250
|
-
const col = columns[i];
|
|
266
|
+
for (const col of columns) {
|
|
251
267
|
if (col.expand) {
|
|
252
268
|
// TODO: Can col.ref be empty without an as? Assumption is it cannot - if it has, it's an error, we throw, compiler checks.
|
|
253
269
|
const { expanded, toManys } = expandInline(root, col, col.ref || [], [ dbName(col) ]);
|
|
@@ -270,7 +286,7 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
270
286
|
}
|
|
271
287
|
|
|
272
288
|
/**
|
|
273
|
-
* Check
|
|
289
|
+
* Check whether the given object is a to-many association
|
|
274
290
|
*
|
|
275
291
|
* @param {CSN.Element} obj
|
|
276
292
|
* @returns {boolean}
|
|
@@ -302,6 +318,11 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
302
318
|
while (stack.length > 0) {
|
|
303
319
|
const [ base, current, currentRef, currentAlias ] = stack.pop();
|
|
304
320
|
if (isToMany(current) && current.expand) {
|
|
321
|
+
expanded.push({
|
|
322
|
+
expand: current.expand,
|
|
323
|
+
ref: currentRef,
|
|
324
|
+
as: currentAlias.join(pathDelimiter),
|
|
325
|
+
});
|
|
305
326
|
toManys.push({ art: current, ref: currentRef, as: currentAlias.join(pathDelimiter) });
|
|
306
327
|
}
|
|
307
328
|
else if (current.expand) {
|
|
@@ -436,7 +457,7 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
436
457
|
*
|
|
437
458
|
* @param {Array} thing
|
|
438
459
|
* @param {CSN.Path} path
|
|
439
|
-
* @param {boolean} [withAlias=false]
|
|
460
|
+
* @param {boolean} [withAlias=false] Whether to "expand" the (implicit) alias aswell.
|
|
440
461
|
* @returns {Array} New array - with all structured things expanded
|
|
441
462
|
*/
|
|
442
463
|
function expand(thing, path, withAlias = false) {
|
|
@@ -477,6 +498,7 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
477
498
|
*/
|
|
478
499
|
function expandRef(art, ref, alias, isKey, withAlias) {
|
|
479
500
|
const expanded = [];
|
|
501
|
+
/** @type {Array<[CSN.Element, any[], any[]]>} */
|
|
480
502
|
const stack = [ [ art, ref, [ alias || ref[ref.length - 1] ] ] ];
|
|
481
503
|
while (stack.length > 0) {
|
|
482
504
|
const [ current, currentRef, currentAlias ] = stack.pop();
|
|
@@ -564,8 +586,7 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
564
586
|
}
|
|
565
587
|
}
|
|
566
588
|
// Finally: Replace the stars and leave out the shadowed things
|
|
567
|
-
for (
|
|
568
|
-
const sub = subs[i];
|
|
589
|
+
for (const sub of subs) {
|
|
569
590
|
if (sub !== '*' && !replaced[dbName(sub)])
|
|
570
591
|
final.push(sub);
|
|
571
592
|
else if (sub === '*')
|