@sap/cds-compiler 2.12.0 → 2.15.2
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 +221 -15
- package/bin/cdsc.js +125 -50
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +47 -84
- package/lib/api/options.js +5 -6
- package/lib/api/validate.js +6 -11
- 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 +114 -18
- package/lib/base/messages.js +101 -90
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +177 -123
- package/lib/checks/annotationsOData.js +12 -33
- package/lib/checks/arrayOfs.js +1 -34
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +6 -11
- package/lib/compiler/assert-consistency.js +6 -3
- package/lib/compiler/base.js +1 -0
- package/lib/compiler/builtins.js +19 -6
- package/lib/compiler/checks.js +23 -60
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1151 -0
- package/lib/compiler/extend.js +1000 -0
- package/lib/compiler/finalize-parse-cdl.js +237 -0
- package/lib/compiler/index.js +107 -39
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1227 -0
- package/lib/compiler/propagator.js +114 -46
- package/lib/compiler/resolve.js +1521 -0
- package/lib/compiler/shared.js +126 -65
- package/lib/compiler/tweak-assocs.js +535 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +38 -24
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +219 -100
- package/lib/edm/edm.js +302 -230
- package/lib/edm/edmPreprocessor.js +554 -419
- package/lib/edm/edmUtils.js +138 -44
- package/lib/gen/Dictionary.json +100 -19
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +11 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5765 -4480
- package/lib/json/csnVersion.js +10 -11
- package/lib/json/from-csn.js +15 -3
- package/lib/json/to-csn.js +126 -68
- package/lib/language/docCommentParser.js +4 -4
- package/lib/language/genericAntlrParser.js +123 -5
- package/lib/language/language.g4 +355 -156
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +486 -59
- package/lib/main.js +41 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +252 -156
- package/lib/model/csnUtils.js +384 -297
- package/lib/model/enrichCsn.js +71 -29
- package/lib/model/revealInternalProperties.js +29 -8
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +23 -18
- package/lib/optionProcessor.js +63 -26
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +897 -947
- package/lib/render/toHdbcds.js +205 -257
- package/lib/render/toSql.js +264 -225
- package/lib/render/utils/common.js +136 -25
- package/lib/render/utils/sql.js +4 -3
- 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/db/.eslintrc.json +3 -1
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +104 -306
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +58 -53
- package/lib/transform/db/expansion.js +60 -33
- package/lib/transform/db/flattening.js +582 -104
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +66 -13
- package/lib/transform/db/views.js +11 -7
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +109 -208
- package/lib/transform/forOdataNew.js +59 -212
- package/lib/transform/localized.js +46 -26
- package/lib/transform/odata/toFinalBaseType.js +85 -11
- package/lib/transform/odata/typesExposure.js +147 -199
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +44 -33
- package/lib/transform/translateAssocsToJoins.js +3 -20
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +172 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +737 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/moduleResolve.js +13 -6
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/share/messages/message-explanations.json +2 -1
- package/share/messages/syntax-expected-integer.md +37 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- package/lib/transform/odata/attachPath.js +0 -96
- package/lib/transform/odata/expandStructKeysInAssociations.js +0 -59
- package/lib/transform/odata/generateForeignKeyElements.js +0 -261
- package/lib/transform/odata/referenceFlattener.js +0 -290
- package/lib/transform/odata/sortByAssociationDependency.js +0 -105
- package/lib/transform/odata/structuralPath.js +0 -72
- package/lib/transform/odata/structureFlattener.js +0 -171
- package/lib/transform/universalCsnEnricher.js +0 -237
|
@@ -9,12 +9,14 @@ const { setProp } = require('../base/model');
|
|
|
9
9
|
const { csnRefs } = require('../model/csnRefs');
|
|
10
10
|
|
|
11
11
|
const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
|
|
12
|
-
const {
|
|
12
|
+
const { cloneCsnNonDict, cloneCsnDictionary, getUtils, isBuiltinType } = require('../model/csnUtils');
|
|
13
|
+
const { ModelError } = require("../base/error");
|
|
14
|
+
const { forEach } = require('../utils/objectUtils');
|
|
13
15
|
|
|
14
16
|
// Return the public functions of this module, with 'model' captured in a closure (for definitions, options etc).
|
|
15
17
|
// Use 'pathDelimiter' for flattened names (e.g. of struct elements or foreign key elements).
|
|
16
18
|
// 'model' is compacted new style CSN
|
|
17
|
-
// TODO: Error and warnings handling with compacted CSN? - currently just throw new
|
|
19
|
+
// TODO: Error and warnings handling with compacted CSN? - currently just throw new ModelError for everything
|
|
18
20
|
// TODO: check the situation with assocs with values. In compacted CSN such elements have only "@Core.Computed": true
|
|
19
21
|
function getTransformers(model, options, pathDelimiter = '_') {
|
|
20
22
|
const { error, warning, info } = makeMessageFunction(model, options);
|
|
@@ -246,6 +248,8 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
246
248
|
const struct = elemType ? elemType.elements : elem.elements;
|
|
247
249
|
|
|
248
250
|
// Collect all child elements (recursively) into 'result'
|
|
251
|
+
// TODO: Do not report collisions in the generated elements here, but instead
|
|
252
|
+
// leave that work to the receiver of this result
|
|
249
253
|
let result = Object.create(null);
|
|
250
254
|
const addGeneratedFlattenedElement = (e, eName) => {
|
|
251
255
|
if(result[eName]){
|
|
@@ -255,7 +259,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
255
259
|
result[eName] = e;
|
|
256
260
|
}
|
|
257
261
|
}
|
|
258
|
-
|
|
262
|
+
forEach(struct, (childName, childElem) => {
|
|
259
263
|
if (isStructured(childElem)) {
|
|
260
264
|
// Descend recursively into structured children
|
|
261
265
|
let grandChildElems = flattenStructuredElement(childElem, childName, elementPath, pathInCsn.concat('elements',childName));
|
|
@@ -273,7 +277,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
273
277
|
} else {
|
|
274
278
|
// Primitive child - clone it and restore its cross references
|
|
275
279
|
let flatElemName = elemName + pathDelimiter + childName;
|
|
276
|
-
let flatElem =
|
|
280
|
+
let flatElem = cloneCsnNonDict(childElem, options);
|
|
277
281
|
// Don't take over notNull from leaf elements
|
|
278
282
|
delete flatElem.notNull;
|
|
279
283
|
setProp(flatElem, '_flatElementNameWithDots', elementPath.concat(childName).join('.'));
|
|
@@ -282,11 +286,15 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
282
286
|
});
|
|
283
287
|
|
|
284
288
|
// Fix all collected flat elements (names, annotations, properties, origin ..)
|
|
285
|
-
|
|
289
|
+
forEach(result, (name, flatElem) => {
|
|
286
290
|
// Copy annotations from struct (not overwriting, because deep annotations should have precedence)
|
|
287
291
|
copyAnnotations(elem, flatElem, false);
|
|
288
292
|
// Copy selected type properties
|
|
289
|
-
|
|
293
|
+
const props = ['key', 'virtual', 'masked', 'viaAll'];
|
|
294
|
+
// 'localized' is needed for OData
|
|
295
|
+
if(options.toOdata)
|
|
296
|
+
props.push('localized');
|
|
297
|
+
for (let p of props) {
|
|
290
298
|
if (elem[p]) {
|
|
291
299
|
flatElem[p] = elem[p];
|
|
292
300
|
}
|
|
@@ -313,12 +321,14 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
313
321
|
// Refs of length 1 cannot contain steps - no need to check
|
|
314
322
|
if (ref.length < 2) {
|
|
315
323
|
return ref;
|
|
324
|
+
} else if(scope === '$self' && ref.length === 2) {
|
|
325
|
+
return ref;
|
|
316
326
|
}
|
|
317
327
|
|
|
318
328
|
return flatten(ref, path);
|
|
319
329
|
|
|
320
330
|
function flatten(ref, path) {
|
|
321
|
-
let result = [];
|
|
331
|
+
let result = scope === '$self' ? [ref[0]] : [];
|
|
322
332
|
//let stack = []; // IDs of path steps not yet processed or part of a struct traversal
|
|
323
333
|
if(!links && !scope) { // calculate JIT if not supplied
|
|
324
334
|
const res = inspectRef(path);
|
|
@@ -327,23 +337,25 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
327
337
|
}
|
|
328
338
|
if (scope === '$magic')
|
|
329
339
|
return ref;
|
|
340
|
+
// Don't process a leading $self - it will a .art with .elements!
|
|
341
|
+
const startIndex = scope === '$self' ? 1 : 0;
|
|
330
342
|
let flattenStep = false;
|
|
331
|
-
links.
|
|
343
|
+
for(let i = startIndex; i < links.length; i++) {
|
|
344
|
+
const value = links[i];
|
|
332
345
|
if (flattenStep) {
|
|
333
|
-
result[result.length - 1] += pathDelimiter + (ref[
|
|
346
|
+
result[result.length - 1] += pathDelimiter + (ref[i].id ? ref[i].id : ref[i]);
|
|
334
347
|
// if we had a filter or args, we had an assoc so this step is done
|
|
335
348
|
// we then keep along the filter/args by updating the id of the current ref
|
|
336
|
-
if(ref[
|
|
337
|
-
ref[
|
|
338
|
-
result[result.length-1] = ref[
|
|
349
|
+
if(ref[i].id) {
|
|
350
|
+
ref[i].id = result[result.length-1];
|
|
351
|
+
result[result.length-1] = ref[i];
|
|
339
352
|
}
|
|
340
353
|
}
|
|
341
354
|
else {
|
|
342
|
-
result.push(ref[
|
|
355
|
+
result.push(ref[i]);
|
|
343
356
|
}
|
|
344
|
-
|
|
345
357
|
flattenStep = value.art && !value.art.kind && !value.art.SELECT && !value.art.from && (value.art.elements || effectiveType(value.art).elements || (resolvedLinkTypes.get(value)||{}).elements);
|
|
346
|
-
}
|
|
358
|
+
}
|
|
347
359
|
|
|
348
360
|
return result;
|
|
349
361
|
}
|
|
@@ -389,7 +401,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
389
401
|
*
|
|
390
402
|
* @param {CSN.Artifact} node
|
|
391
403
|
* @param {WeakMap} [resolved] WeakMap containing already resolved refs
|
|
392
|
-
* @param {boolean} [keepLocalized=false]
|
|
404
|
+
* @param {boolean} [keepLocalized=false] Whether to clone .localized from a type def
|
|
393
405
|
* @returns {void}
|
|
394
406
|
*/
|
|
395
407
|
function toFinalBaseType(node, resolved, keepLocalized=false) {
|
|
@@ -402,11 +414,11 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
402
414
|
throw Error('Failed to obtain final base type for reference : ' + node.type.ref.join('/'));
|
|
403
415
|
if(finalBaseType.elements) {
|
|
404
416
|
// This changes the order - to be discussed!
|
|
405
|
-
node.elements =
|
|
417
|
+
node.elements = cloneCsnNonDict(finalBaseType, options).elements; // copy elements
|
|
406
418
|
delete node.type; // delete the type reference as edm processing does not expect it
|
|
407
419
|
} else if(finalBaseType.items) {
|
|
408
420
|
// This changes the order - to be discussed!
|
|
409
|
-
node.items =
|
|
421
|
+
node.items = cloneCsnNonDict(finalBaseType.items, options); // copy items
|
|
410
422
|
delete node.type;
|
|
411
423
|
} else {
|
|
412
424
|
node.type=finalBaseType;
|
|
@@ -420,20 +432,19 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
420
432
|
let typeDef = typeof node.type === 'string' ? getCsnDef(node.type) : node.type;
|
|
421
433
|
// Nothing to do if type is an array or a struct type
|
|
422
434
|
if (typeDef.items || typeDef.elements) {
|
|
423
|
-
if(!(options.transformation === 'hdbcds' || options.toSql))
|
|
424
|
-
return;
|
|
425
|
-
|
|
426
435
|
// cloneCsn only works correctly if we start "from the top"
|
|
427
|
-
const cloneTypeDef =
|
|
428
|
-
// With hdbcds-hdbcds, don't resolve structured types - but
|
|
436
|
+
const cloneTypeDef = cloneCsnNonDict(typeDef, options);
|
|
437
|
+
// With hdbcds-hdbcds, don't resolve structured types - but propagate ".items", to turn into LargeString later on.
|
|
429
438
|
if(typeDef.items) {
|
|
430
439
|
delete node.type;
|
|
431
|
-
|
|
440
|
+
if(!node.items)
|
|
441
|
+
Object.assign(node, {items: cloneTypeDef.items});
|
|
432
442
|
}
|
|
433
443
|
if(typeDef.elements && !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds')) {
|
|
434
444
|
if(!typeDef.items)
|
|
435
445
|
delete node.type;
|
|
436
|
-
|
|
446
|
+
if(!node.elements)
|
|
447
|
+
Object.assign(node, {elements: cloneTypeDef.elements});
|
|
437
448
|
}
|
|
438
449
|
|
|
439
450
|
|
|
@@ -593,7 +604,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
593
604
|
// elemName typeName isKey defaultVal notNull
|
|
594
605
|
function createScalarElement(elemName, typeName, isKey = false, defaultVal = undefined, notNull=false) {
|
|
595
606
|
if (!isBuiltinType(typeName) && !model.definitions[typeName]) {
|
|
596
|
-
throw new
|
|
607
|
+
throw new ModelError('Expecting valid type name: ' + typeName);
|
|
597
608
|
}
|
|
598
609
|
let result = {
|
|
599
610
|
[elemName]: {
|
|
@@ -684,7 +695,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
684
695
|
function addForeignKey(foreignKey, elem) {
|
|
685
696
|
// Sanity checks
|
|
686
697
|
if (!elem.target || !elem.keys) {
|
|
687
|
-
throw new
|
|
698
|
+
throw new ModelError('Expecting managed association element with foreign keys');
|
|
688
699
|
}
|
|
689
700
|
|
|
690
701
|
// Add the foreign key
|
|
@@ -703,7 +714,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
703
714
|
function addElement(elem, artifact, artifactName) {
|
|
704
715
|
// Sanity check
|
|
705
716
|
if (!artifact.elements) {
|
|
706
|
-
throw new
|
|
717
|
+
throw new ModelError('Expecting artifact with elements: ' + JSON.stringify(artifact));
|
|
707
718
|
}
|
|
708
719
|
let elemName = Object.keys(elem)[0];
|
|
709
720
|
// Element must not exist
|
|
@@ -734,7 +745,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
734
745
|
*/
|
|
735
746
|
function copyAndAddElement(elem, artifact, artifactName, elementName) {
|
|
736
747
|
if (!artifact.elements) {
|
|
737
|
-
throw new
|
|
748
|
+
throw new ModelError('Expected structured artifact');
|
|
738
749
|
}
|
|
739
750
|
// Must not already have such an element
|
|
740
751
|
if (artifact.elements[elementName]) {
|
|
@@ -765,14 +776,14 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
765
776
|
|
|
766
777
|
if (returnTypeName) {
|
|
767
778
|
if (!isBuiltinType(returnTypeName) && !model.definitions[returnTypeName])
|
|
768
|
-
throw new
|
|
779
|
+
throw new ModelError('Expecting valid return type name: ' + returnTypeName);
|
|
769
780
|
action.returns = { type: returnTypeName };
|
|
770
781
|
}
|
|
771
782
|
|
|
772
783
|
// Add parameter if provided
|
|
773
784
|
if (paramName && paramTypeName) {
|
|
774
785
|
if (!isBuiltinType(paramTypeName) && !model.definitions[paramTypeName])
|
|
775
|
-
throw new
|
|
786
|
+
throw new ModelError('Expecting valid parameter type name: ' + paramTypeName);
|
|
776
787
|
|
|
777
788
|
action.params = Object.create(null);
|
|
778
789
|
action.params[paramName] = {
|
|
@@ -885,7 +896,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
885
896
|
let elements = artifact.elements;
|
|
886
897
|
if (elements) {
|
|
887
898
|
path.push('elements', null);
|
|
888
|
-
|
|
899
|
+
forEach(elements, (name, obj) => {
|
|
889
900
|
path[path.length - 1] = name;
|
|
890
901
|
recurseElements(obj, path, callback);
|
|
891
902
|
});
|
|
@@ -1105,7 +1116,7 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1105
1116
|
'xpr': (parent, name, xpr, path) => {
|
|
1106
1117
|
parent.xpr = expand(parent.xpr, path.concat(name));
|
|
1107
1118
|
}
|
|
1108
|
-
},
|
|
1119
|
+
}, [], options);
|
|
1109
1120
|
|
|
1110
1121
|
/*
|
|
1111
1122
|
flatten structured leaf types and return array of paths
|
|
@@ -44,7 +44,7 @@ function translateAssocsToJoinsCSN(csn, options){
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// If A2J reports error - end! Continuing with a broken CSN makes no sense
|
|
47
|
-
makeMessageFunction(model, options).
|
|
47
|
+
makeMessageFunction(model, options).throwWithAnyError();
|
|
48
48
|
// FIXME: Move this somewhere more appropriate
|
|
49
49
|
const compact = compactModel(model, compileOptions);
|
|
50
50
|
return compact;
|
|
@@ -120,7 +120,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
120
120
|
/*
|
|
121
121
|
Setup QAs for mixins
|
|
122
122
|
Mark all mixin assoc definitions with a pseudo QA that points to the assoc target.
|
|
123
|
-
This QA is required to detect mixin assoc usages to decide
|
|
123
|
+
This QA is required to detect mixin assoc usages to decide whether a minimum or full
|
|
124
124
|
join needs to be done
|
|
125
125
|
*/
|
|
126
126
|
forEachQuery(createQAForMixinAssoc, env);
|
|
@@ -793,25 +793,8 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
793
793
|
If the value is an expression in the select block, return the unmodified
|
|
794
794
|
expression. rewriteGenericPaths will check and rewrite these paths later
|
|
795
795
|
to the correct ON condition expression.
|
|
796
|
-
|
|
797
|
-
Hack alert:
|
|
798
|
-
But if this mixin ON condition path starts with $self no foreign key can
|
|
799
|
-
be generated, raise the error here and set $check to false
|
|
800
|
-
as it is too hard for checkPathDictionary() to find out that this is a
|
|
801
|
-
mixin speciality. Use same mesage as in checkPathDictionary().
|
|
802
796
|
*/
|
|
803
797
|
|
|
804
|
-
path.forEach(ps => { // eslint-disable-line consistent-return
|
|
805
|
-
if(ps._artifact.target) {
|
|
806
|
-
error(null, pathNode.location,
|
|
807
|
-
{ name: env.lead.name.absolute, id: ps.id, alias: pathAsStr(pathNode.path) },
|
|
808
|
-
'$(NAME): $(ID) in path $(ALIAS) must not be an association'
|
|
809
|
-
);
|
|
810
|
-
//pathNode.$check = false;
|
|
811
|
-
return pathNode;
|
|
812
|
-
}
|
|
813
|
-
});
|
|
814
|
-
|
|
815
798
|
if(!value.path)
|
|
816
799
|
return value;
|
|
817
800
|
else {
|
|
@@ -1090,7 +1073,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1090
1073
|
|
|
1091
1074
|
/*
|
|
1092
1075
|
Replace the table alias node in $tableAliases inplace with the newly created JOIN node
|
|
1093
|
-
See
|
|
1076
|
+
See define.js initTableExpression for details where _joinParent and $joinArgsIndex is set.
|
|
1094
1077
|
*/
|
|
1095
1078
|
function replaceTableAliasInPlace( tableAlias, replacementNode ) {
|
|
1096
1079
|
if (tableAlias._joinParent)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"root": true,
|
|
3
|
+
"plugins": ["sonarjs", "jsdoc"],
|
|
4
|
+
"extends": ["../../../.eslintrc-ydkjsi.json", "plugin:sonarjs/recommended", "plugin:jsdoc/recommended"],
|
|
5
|
+
"rules": {
|
|
6
|
+
"prefer-const": "error",
|
|
7
|
+
"quotes": ["error", "single", "avoid-escape"],
|
|
8
|
+
"prefer-template": "error",
|
|
9
|
+
"no-trailing-spaces": "error",
|
|
10
|
+
"template-curly-spacing":["error", "never"],
|
|
11
|
+
"complexity": ["warn", 30],
|
|
12
|
+
"max-len": "off",
|
|
13
|
+
// Don't enforce stupid descriptions
|
|
14
|
+
"jsdoc/require-param-description": "off",
|
|
15
|
+
"jsdoc/require-returns-description": "off",
|
|
16
|
+
// Very whiny and nitpicky
|
|
17
|
+
"sonarjs/cognitive-complexity": "off",
|
|
18
|
+
// Does not recognize TS types
|
|
19
|
+
"jsdoc/no-undefined-types": "off",
|
|
20
|
+
// Just annoying as hell
|
|
21
|
+
"sonarjs/no-duplicate-string": "off"
|
|
22
|
+
},
|
|
23
|
+
"parserOptions": {
|
|
24
|
+
"ecmaVersion": 2018,
|
|
25
|
+
"sourceType": "script"
|
|
26
|
+
},
|
|
27
|
+
"env": {
|
|
28
|
+
"es6": true,
|
|
29
|
+
"node": true
|
|
30
|
+
},
|
|
31
|
+
"settings": {
|
|
32
|
+
"jsdoc": {
|
|
33
|
+
"mode": "typescript"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
getUtils, forEachDefinition, forAllQueries, getNormalizedQuery,
|
|
5
|
+
} = require('../../model/csnUtils');
|
|
6
|
+
const { setAnnotationIfNotDefined } = require('./utils');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Set @Core.Computed on the elements of views (and projections).
|
|
10
|
+
*
|
|
11
|
+
* @param {CSN.Model} csn
|
|
12
|
+
*/
|
|
13
|
+
function setCoreComputedOnViews(csn) {
|
|
14
|
+
const {
|
|
15
|
+
artifactRef, getColumn, getElement,
|
|
16
|
+
} = getUtils(csn, 'init-all');
|
|
17
|
+
|
|
18
|
+
forEachDefinition(csn, (artifact) => {
|
|
19
|
+
if (artifact.query || artifact.projection) {
|
|
20
|
+
forAllQueries(getNormalizedQuery(artifact).query, (query) => {
|
|
21
|
+
if (query.SELECT)
|
|
22
|
+
traverseQueryAndAttachCoreComputed(query, query.SELECT.elements || artifact.elements);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* Attach @Core.Computed to elements resulting from calculated fields
|
|
28
|
+
*
|
|
29
|
+
* To do that, for a given element, we search for its matching column/subquery-element (its ancestor).
|
|
30
|
+
* At the ancestor, we can see if it needs a @CoreComputed - check out {@link needsCoreComputed} for details.
|
|
31
|
+
*
|
|
32
|
+
*
|
|
33
|
+
* @param {CSN.Query} query
|
|
34
|
+
* @param {CSN.Elements} elements
|
|
35
|
+
*/
|
|
36
|
+
function traverseQueryAndAttachCoreComputed(query, elements) {
|
|
37
|
+
for (const [ name, element ] of Object.entries(elements)) {
|
|
38
|
+
const ancestor = getAncestor(element, name, query.SELECT);
|
|
39
|
+
|
|
40
|
+
if (needsCoreComputed(ancestor)) // calculated field, function or virtual
|
|
41
|
+
setAnnotationIfNotDefined(element, '@Core.Computed', true);
|
|
42
|
+
if (ancestor && (ancestor.expand || ancestor.inline))
|
|
43
|
+
traverseExpandInline(ancestor, attachCoreComputed);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get the ancestor of a given element - either a direct column that "caused" it, or an element
|
|
48
|
+
* from some other artifact (table or view/subquery). The later happens via SELECT * and can be found by drilling down into the
|
|
49
|
+
* FROM-clause.
|
|
50
|
+
*
|
|
51
|
+
* @param {CSN.Element} element
|
|
52
|
+
* @param {string} name
|
|
53
|
+
* @param {CSN.QuerySelect} base
|
|
54
|
+
* @returns {CSN.Column|CSN.Element}
|
|
55
|
+
*/
|
|
56
|
+
function getAncestor(element, name, base) {
|
|
57
|
+
const column = getColumn(element);
|
|
58
|
+
if (column)
|
|
59
|
+
return column;
|
|
60
|
+
return getElementFromFrom(name, base.from);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Get the element <name> from the given query-base (from of a select).
|
|
65
|
+
*
|
|
66
|
+
* For a simple ref to a table, resolve the ref and check the elements
|
|
67
|
+
* For a UNION, drill down into the leading query
|
|
68
|
+
* For a JOIN, check each join-argument
|
|
69
|
+
* For a query with subelements, check the subelements
|
|
70
|
+
*
|
|
71
|
+
* @param {string} name
|
|
72
|
+
* @param {object} base
|
|
73
|
+
* @returns {CSN.Element}
|
|
74
|
+
* @todo cleanup throw(s) - but leave in during dev
|
|
75
|
+
*/
|
|
76
|
+
function getElementFromFrom(name, base) {
|
|
77
|
+
if (base.SELECT && base.SELECT.elements) {
|
|
78
|
+
return getAncestor(base.SELECT.elements[name], name, base.SELECT);
|
|
79
|
+
}
|
|
80
|
+
else if (base.ref) {
|
|
81
|
+
let artifact = artifactRef(base);
|
|
82
|
+
if (artifact.target)
|
|
83
|
+
artifact = artifactRef(artifact.target);
|
|
84
|
+
return artifact.elements[name];
|
|
85
|
+
}
|
|
86
|
+
else if (base.SET) {
|
|
87
|
+
return getElementFromFrom(name, base.SET.args[0]);
|
|
88
|
+
}
|
|
89
|
+
else if (base.args && base.join) {
|
|
90
|
+
const result = checkJoinSources(base.args, name);
|
|
91
|
+
if (!result)
|
|
92
|
+
throw new Error(`Could not find ${name} in ${JSON.stringify(base.args)}`);
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
throw new Error(JSON.stringify(base));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* For the given JOIN-args, check if one of the join sources provides an element <name>
|
|
101
|
+
*
|
|
102
|
+
* @param {Array} args
|
|
103
|
+
* @param {string} name
|
|
104
|
+
* @returns {CSN.Element|null} Null if no element was found
|
|
105
|
+
*/
|
|
106
|
+
function checkJoinSources(args, name) {
|
|
107
|
+
for (const arg of args) {
|
|
108
|
+
if (arg.args) { // Join after join - A join B on <..> join C on <..>
|
|
109
|
+
const result = checkJoinSources(arg.args, name);
|
|
110
|
+
if (result)
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
else { // All other cases - normal ref, a subselect, etc. pp
|
|
114
|
+
const result = getElementFromFrom(name, arg);
|
|
115
|
+
if (result)
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* On a given column, attach @Core.Computed if needed
|
|
125
|
+
*
|
|
126
|
+
* @param {CSN.Column} column
|
|
127
|
+
*/
|
|
128
|
+
function attachCoreComputed(column) {
|
|
129
|
+
if (needsCoreComputed(column))
|
|
130
|
+
setAnnotationIfNotDefined(getElement(column), '@Core.Computed', true);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Return whether the given columns element needs to be marked with @Core.Computed.
|
|
135
|
+
*
|
|
136
|
+
* @param {CSN.Column} column
|
|
137
|
+
* @returns {boolean}
|
|
138
|
+
*/
|
|
139
|
+
function needsCoreComputed(column) {
|
|
140
|
+
return column &&
|
|
141
|
+
(
|
|
142
|
+
column.xpr || column.func || column.val !== undefined || column.param || column.SELECT || column.SET ||
|
|
143
|
+
column.ref && [ '$at', '$now', '$user', '$session' ].includes(column.ref[0])
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Call the given callback for all sub-things of a .expand/.inline and drill further down into other .expand/.inline
|
|
149
|
+
*
|
|
150
|
+
* @param {CSN.Column} column
|
|
151
|
+
* @param {Function} callback
|
|
152
|
+
*/
|
|
153
|
+
function traverseExpandInline(column, callback) {
|
|
154
|
+
if (column.expand) {
|
|
155
|
+
column.expand.forEach((col) => {
|
|
156
|
+
callback(col);
|
|
157
|
+
traverseExpandInline(col, callback);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
else if (column.inline) {
|
|
161
|
+
column.inline.forEach((col) => {
|
|
162
|
+
callback(col);
|
|
163
|
+
traverseExpandInline(col, callback);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
module.exports = {
|
|
171
|
+
setCoreComputedOnViews,
|
|
172
|
+
};
|