@sap/cds-compiler 2.5.0 → 2.10.4

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.
Files changed (92) hide show
  1. package/CHANGELOG.md +191 -9
  2. package/bin/cdsc.js +2 -2
  3. package/doc/CHANGELOG_BETA.md +33 -3
  4. package/lib/api/main.js +29 -101
  5. package/lib/api/options.js +15 -11
  6. package/lib/api/validate.js +12 -8
  7. package/lib/backends.js +0 -81
  8. package/lib/base/keywords.js +32 -2
  9. package/lib/base/message-registry.js +63 -9
  10. package/lib/base/messages.js +63 -21
  11. package/lib/base/model.js +2 -3
  12. package/lib/checks/defaultValues.js +27 -2
  13. package/lib/checks/elements.js +1 -6
  14. package/lib/checks/foreignKeys.js +0 -6
  15. package/lib/checks/managedWithoutKeys.js +17 -0
  16. package/lib/checks/nonexpandableStructured.js +38 -0
  17. package/lib/checks/onConditions.js +9 -45
  18. package/lib/checks/queryNoDbArtifacts.js +25 -7
  19. package/lib/checks/selectItems.js +25 -2
  20. package/lib/checks/types.js +26 -2
  21. package/lib/checks/unknownMagic.js +38 -0
  22. package/lib/checks/utils.js +61 -0
  23. package/lib/checks/validator.js +60 -7
  24. package/lib/compiler/assert-consistency.js +16 -7
  25. package/lib/compiler/builtins.js +2 -0
  26. package/lib/compiler/checks.js +6 -4
  27. package/lib/compiler/definer.js +99 -42
  28. package/lib/compiler/index.js +73 -27
  29. package/lib/compiler/resolver.js +288 -157
  30. package/lib/compiler/shared.js +31 -11
  31. package/lib/edm/annotations/genericTranslation.js +182 -186
  32. package/lib/edm/csn2edm.js +103 -108
  33. package/lib/edm/edm.js +18 -21
  34. package/lib/edm/edmPreprocessor.js +361 -114
  35. package/lib/edm/edmUtils.js +103 -33
  36. package/lib/gen/Dictionary.json +22 -0
  37. package/lib/gen/language.checksum +1 -1
  38. package/lib/gen/language.interp +12 -1
  39. package/lib/gen/language.tokens +57 -53
  40. package/lib/gen/languageLexer.interp +10 -1
  41. package/lib/gen/languageLexer.js +770 -744
  42. package/lib/gen/languageLexer.tokens +49 -46
  43. package/lib/gen/languageParser.js +4713 -4279
  44. package/lib/json/from-csn.js +103 -45
  45. package/lib/json/to-csn.js +296 -117
  46. package/lib/language/antlrParser.js +4 -3
  47. package/lib/language/errorStrategy.js +1 -0
  48. package/lib/language/genericAntlrParser.js +21 -12
  49. package/lib/language/language.g4 +99 -31
  50. package/lib/main.d.ts +81 -3
  51. package/lib/main.js +30 -7
  52. package/lib/model/api.js +78 -0
  53. package/lib/model/csnRefs.js +329 -142
  54. package/lib/model/csnUtils.js +235 -58
  55. package/lib/model/enrichCsn.js +18 -1
  56. package/lib/model/revealInternalProperties.js +2 -1
  57. package/lib/modelCompare/compare.js +37 -20
  58. package/lib/optionProcessor.js +9 -3
  59. package/lib/render/.eslintrc.json +4 -1
  60. package/lib/render/DuplicateChecker.js +8 -5
  61. package/lib/render/toCdl.js +112 -33
  62. package/lib/render/toHdbcds.js +134 -64
  63. package/lib/render/toSql.js +91 -38
  64. package/lib/render/utils/common.js +8 -13
  65. package/lib/render/utils/sql.js +3 -3
  66. package/lib/sql-identifier.js +6 -1
  67. package/lib/transform/db/assertUnique.js +5 -6
  68. package/lib/transform/db/constraints.js +29 -13
  69. package/lib/transform/db/draft.js +8 -6
  70. package/lib/transform/db/expansion.js +582 -0
  71. package/lib/transform/db/flattening.js +325 -0
  72. package/lib/transform/db/groupByOrderBy.js +2 -2
  73. package/lib/transform/db/transformExists.js +284 -63
  74. package/lib/transform/forHanaNew.js +98 -381
  75. package/lib/transform/forOdataNew.js +21 -22
  76. package/lib/transform/localized.js +37 -10
  77. package/lib/transform/odata/attachPath.js +19 -4
  78. package/lib/transform/odata/generateForeignKeyElements.js +11 -10
  79. package/lib/transform/odata/referenceFlattener.js +60 -39
  80. package/lib/transform/odata/sortByAssociationDependency.js +2 -2
  81. package/lib/transform/odata/structuralPath.js +72 -0
  82. package/lib/transform/odata/structureFlattener.js +19 -18
  83. package/lib/transform/odata/typesExposure.js +22 -12
  84. package/lib/transform/transformUtilsNew.js +134 -78
  85. package/lib/transform/translateAssocsToJoins.js +17 -14
  86. package/lib/transform/universalCsnEnricher.js +67 -0
  87. package/lib/utils/file.js +0 -11
  88. package/lib/utils/moduleResolve.js +6 -8
  89. package/package.json +1 -1
  90. package/lib/json/walker.js +0 -26
  91. package/lib/transform/sqlite +0 -0
  92. package/lib/utils/string.js +0 -17
@@ -36,7 +36,7 @@ const version = require('../../package.json').version;
36
36
  * @param {CSN.Model} model (Compact) CSN model
37
37
  */
38
38
  function getUtils(model) {
39
- const { artifactRef, inspectRef, effectiveType } = csnRefs(model);
39
+ const { artifactRef, inspectRef, effectiveType, getOrigin } = csnRefs(model);
40
40
 
41
41
  return {
42
42
  getCsnDef,
@@ -58,8 +58,137 @@ function getUtils(model) {
58
58
  inspectRef,
59
59
  artifactRef,
60
60
  effectiveType,
61
+ get$combined,
62
+ getOrigin,
61
63
  };
62
64
 
65
+ /**
66
+ * Compute and return $combined for the given query.
67
+ *
68
+ * @param {CSN.Query} query
69
+ * @returns {object}
70
+ */
71
+ function get$combined(query) {
72
+ const sources = getSources(query);
73
+ return sources;
74
+
75
+ /**
76
+ * Get the union of all elements from the from clause
77
+ * - descend into unions, following the lead query
78
+ * - merge all queries in case of joins
79
+ * - follow subqueries
80
+ *
81
+ * @param {CSN.Query} query Query to check
82
+ * @returns {object} Map of sources
83
+ */
84
+ function getSources(query, isSubquery=false) {
85
+ // Remark CW: better just a while along query.SET.args[0]
86
+ if (query.SET) {
87
+ if (query.SET.args[0].SELECT && query.SET.args[0].SELECT.elements)
88
+ return mergeElementsIntoMap(Object.create(null), query.SET.args[0].SELECT.elements, query.SET.args[0].$location);
89
+
90
+ return getSources(query.SET.args[0], isSubquery);
91
+ }
92
+ else if (query.SELECT) {
93
+ if (query.SELECT.from.args) {
94
+ return walkArgs(query.SELECT.from.args);
95
+ }
96
+ else if (query.SELECT.from.ref) {
97
+ let art = artifactRef(query.SELECT.from);
98
+
99
+ if(art.target)
100
+ art = artifactRef(art.target);
101
+
102
+ if(isSubquery && !query.SELECT.elements)
103
+ throw new Error('Expected subquery to have .elements');
104
+
105
+ return mergeElementsIntoMap(Object.create(null), isSubquery ? query.SELECT.elements : art.elements, art.$location,
106
+ query.SELECT.from.as || query.SELECT.from.ref[query.SELECT.from.ref.length - 1],
107
+ query.SELECT.from.ref[query.SELECT.from.ref.length - 1] || query.SELECT.from.as );
108
+ }
109
+ else if (query.SELECT.from.SET || query.SELECT.from.SELECT) {
110
+ return getSources(query.SELECT.from, true);
111
+ }
112
+ }
113
+
114
+ function walkArgs(args) {
115
+ let elements = Object.create(null);
116
+ for (const arg of args) {
117
+ if (arg.args) {
118
+ elements = mergeElementMaps(elements, walkArgs(arg.args));
119
+ }
120
+ else if (arg.ref) {
121
+ const art = artifactRef(arg);
122
+ elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || arg.ref[arg.ref.length - 1], arg.ref[arg.ref.length - 1] || arg.as);
123
+ }
124
+ else if (arg.SELECT || arg.SET) {
125
+ elements = mergeElementMaps(elements, getSources(arg));
126
+ }
127
+ }
128
+
129
+ return elements;
130
+ }
131
+
132
+ return {};
133
+
134
+ /**
135
+ * Merge two maps of elements together
136
+ *
137
+ * @param {object} mapA Map a - will be returned
138
+ * @param {object} mapB Map b - will not be returned
139
+ * @returns {object} mapA
140
+ */
141
+ function mergeElementMaps(mapA, mapB) {
142
+ for (const elementName in mapB) {
143
+ if (!mapA[elementName])
144
+ mapA[elementName] = [];
145
+
146
+ mapB[elementName].forEach(e => mapA[elementName].push(e));
147
+ }
148
+
149
+ return mapA;
150
+ }
151
+
152
+ /**
153
+ * Merge elements into an existing map
154
+ *
155
+ * @param {any} existingMap map to merge into - will be returned
156
+ * @param {object} elements elements to merge into the map
157
+ * @param {CSN.Location} $location $location of the elements - where they come from
158
+ * @param {any} [parent] Name of the parent of the elements, alias before ref
159
+ * @param {any} [error_parent] Parent name to use for error messages, ref before alias
160
+ * @returns {object} existingMap
161
+ */
162
+ function mergeElementsIntoMap(existingMap, elements, $location, parent, error_parent) {
163
+ for (const elementName in elements) {
164
+ const element = elements[elementName];
165
+ if (!existingMap[elementName])
166
+ existingMap[elementName] = [];
167
+
168
+
169
+ existingMap[elementName].push({
170
+ element, name: elementName, source: $location, parent: getBaseName(parent), error_parent,
171
+ });
172
+ }
173
+
174
+ return existingMap;
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Return the name part of the artifact name - no namespace etc.
180
+ * @param {string|object} name Absolute name of the artifact
181
+ */
182
+ function getBaseName(name) {
183
+ if (!name)
184
+ return name;
185
+
186
+ if (name.id)
187
+ return name.id.substring( name.id.lastIndexOf('.')+1 );
188
+
189
+ return name.substring( name.lastIndexOf('.')+1 )
190
+ }
191
+ }
63
192
 
64
193
 
65
194
  /**
@@ -328,45 +457,54 @@ function getUtils(model) {
328
457
  return resultNode;
329
458
  }
330
459
  }
331
-
332
- // Resolve to the final type of a type, that means follow type chains, references to other types or
333
- // elements a.s.o
334
- // Works for all kinds of types, strings as well as type objects. Strings need to be absolute type names.
335
- // Returns the final type as string (if it has a name, which is not always the case, think of embedded structures),
336
- // else the type object itself is returned. If a type is structured, you can navigate into it by providing a path,
337
- // e.g. given the following model
338
- // type bar: S.foo;
339
- // type s1 {
340
- // s: s2;
341
- // };
342
- // type s2 {
343
- // u: type of S.e:t;
344
- // }
345
- // service S {
346
- // type foo: type of S.e:i.j1;
347
- // entity e {
348
- // key i: { j1: Integer };
349
- // t: bar;
350
- // v: s1;
351
- // x: blutz.s.u;
352
- // };
353
- // type blutz: S.e.v;
354
- // view V as select from e {
355
- // 1+1 as i: bar,
356
- // };
357
- // type tt: type of V:i;
358
- // }
359
- // the following calls will all return 'cds.Integer'
360
- // getFinalBaseType('S.tt')
361
- // getFinalBaseType('S.e',['i','j1'])
362
- // getFinalBaseType('S.e',['t'])
363
- // getFinalBaseType('S.e',['x'])
364
- // getFinalBaseType('S.blutz',['s', 'u'])
365
- // Types are always resolved as far as possible. A type name which has no further definition is simply returned.
366
- // Composed types (structures, entities, vies, ...) are returned as type objects, if not drilled down into
367
- // the elements. Path steps that have no corresponding element lead to 'undefined'. Refs to something that has
368
- // no type (e.g. expr in a view without explicit type) returns 'null'
369
- function getFinalBaseType(type, path = [], cycleCheck = undefined) {
460
+
461
+
462
+ /**
463
+ * Resolve to the final type of a type, that means follow type chains, references to other types or
464
+ * elements a.s.o
465
+ * Works for all kinds of types, strings as well as type objects. Strings need to be absolute type names.
466
+ * Returns the final type as string (if it has a name, which is not always the case, think of embedded structures),
467
+ * else the type object itself is returned. If a type is structured, you can navigate into it by providing a path,
468
+ * e.g. given the following model
469
+ * type bar: S.foo;
470
+ * type s1 {
471
+ * s: s2;
472
+ * };
473
+ * type s2 {
474
+ * u: type of S.e:t;
475
+ * }
476
+ * service S {
477
+ * type foo: type of S.e:i.j1;
478
+ * entity e {
479
+ * key i: { j1: Integer };
480
+ * t: bar;
481
+ * v: s1;
482
+ * x: blutz.s.u;
483
+ * };
484
+ * type blutz: S.e.v;
485
+ * view V as select from e {
486
+ * 1+1 as i: bar,
487
+ * };
488
+ * type tt: type of V:i;
489
+ * }
490
+ * the following calls will all return 'cds.Integer'
491
+ * getFinalBaseType('S.tt')
492
+ * getFinalBaseType('S.e',['i','j1'])
493
+ * getFinalBaseType('S.e',['t'])
494
+ * getFinalBaseType('S.e',['x'])
495
+ * getFinalBaseType('S.blutz',['s', 'u'])
496
+ * Types are always resolved as far as possible. A type name which has no further definition is simply returned.
497
+ * Composed types (structures, entities, views, ...) are returned as type objects, if not drilled down into
498
+ * the elements. Path steps that have no corresponding element lead to 'undefined'. Refs to something that has
499
+ * no type (e.g. expr in a view without explicit type) returns 'null'
500
+ *
501
+ * @param {string|object} type Type - either string or ref
502
+ * @param {CSN.Path} path
503
+ * @param {WeakMap} [resolved=new WeakMap()] WeakMap containing already resolved refs - if a ref is not cached, it will be resolved JIT
504
+ * @param {object} [cycleCheck] Dictionary to remember already resolved types - to be cycle-safe
505
+ * @returns
506
+ */
507
+ function getFinalBaseType(type, path = [], resolved = new WeakMap(), cycleCheck = undefined) {
370
508
  if (!type)
371
509
  return type;
372
510
  if (typeof(type) === 'string') {
@@ -384,24 +522,24 @@ function getUtils(model) {
384
522
  }
385
523
  let definedType = model.definitions[type];
386
524
  if (definedType && definedType.type)
387
- return getFinalBaseType(definedType.type, path, cycleCheck);
525
+ return getFinalBaseType(definedType.type, path, resolved, cycleCheck);
388
526
  else
389
- return getFinalBaseType(definedType, path, cycleCheck);
527
+ return getFinalBaseType(definedType, path, resolved, cycleCheck);
390
528
  }
391
529
  else if (typeof(type) === 'object') {
392
530
  if (type.ref) {
393
531
  // assert type.ref instanceof Array && type.ref.length >= 1
394
- const ref = artifactRef(type);
395
- return getFinalBaseType(ref, path, cycleCheck);
532
+ const ref = resolved.has(type) ? resolved.get(type).art : artifactRef(type);
533
+ return getFinalBaseType(ref, path, resolved, cycleCheck);
396
534
  }
397
535
  else if (type.elements) {
398
536
  if (path.length) {
399
537
  let [e, ...p] = path;
400
- return getFinalBaseType(type.elements[e], p, cycleCheck);
538
+ return getFinalBaseType(type.elements[e], p, resolved, cycleCheck);
401
539
  }
402
540
  }
403
541
  else if (type.type)
404
- return (getFinalBaseType(type.type, path, cycleCheck));
542
+ return (getFinalBaseType(type.type, path, resolved, cycleCheck));
405
543
  else if (type.items)
406
544
  return type;
407
545
  else
@@ -493,12 +631,12 @@ function forEachMember( construct, callback, path=[], ignoreIgnore=true, iterate
493
631
  // Backends rely on the fact that `forEachElement` also goes through all
494
632
  // `elements` of the return type (if structured).
495
633
  // TODO: `returns` should be handled like a parameter just like XSN (maybe with different prop name)
496
- if (construct.returns) {
634
+ if (construct.returns && !iterateOptions.elementsOnly) {
497
635
  forEachMember( construct.returns, callback, [...path, 'returns'], ignoreIgnore, iterateOptions );
498
636
  }
499
637
 
500
638
  path = [...path]; // Copy
501
- const propsWithMembers = ['elements', 'enum', 'foreignKeys', 'actions', 'params'];
639
+ const propsWithMembers = (iterateOptions.elementsOnly ? ['elements'] : ['elements', 'enum', 'foreignKeys', 'actions', 'params']);
502
640
  propsWithMembers.forEach((prop) => forEachGeneric( construct, prop, callback, path, iterateOptions ));
503
641
  }
504
642
 
@@ -922,11 +1060,12 @@ function getElementDatabaseNameOf(elemName, namingConvention) {
922
1060
  *
923
1061
  * @param {object} csn CSN to enrich in-place
924
1062
  * @param {object} customTransformers Map of prop to transform and function to apply
925
- * @param {Function[]} artifactTransformers Transformations to run on the artifacts, like forEachDefinition
926
- * @param {Boolean} skipIgnore Wether to skip _ignore elements or not
1063
+ * @param {Function[]} [artifactTransformers=[]] Transformations to run on the artifacts, like forEachDefinition
1064
+ * @param {Boolean} [skipIgnore=true] Wether to skip _ignore elements or not
1065
+ * @param {object} [options={}] "skipArtifact": (artifact, name) => Boolean to skip certain artifacts, drillRef: boolean - whether to drill into infix/args
927
1066
  * @returns {object} CSN with transformations applied
928
1067
  */
929
- function applyTransformations( csn, customTransformers={}, artifactTransformers=[], skipIgnore = true ) {
1068
+ function applyTransformations( csn, customTransformers={}, artifactTransformers=[], skipIgnore = true, options = {} ) {
930
1069
  const transformers = {
931
1070
  elements: dictionary,
932
1071
  definitions: dictionary,
@@ -959,7 +1098,7 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
959
1098
  for (let name of Object.getOwnPropertyNames( node )) {
960
1099
  const trans = transformers[name] || standard;
961
1100
  if(customTransformers[name])
962
- customTransformers[name](node, name, node[name], csnPath);
1101
+ customTransformers[name](node, name, node[name], csnPath, parent, prop);
963
1102
 
964
1103
  trans( node, name, node[name], csnPath );
965
1104
  }
@@ -968,6 +1107,9 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
968
1107
  }
969
1108
 
970
1109
  function dictionary( node, prop, dict ) {
1110
+ // Allow skipping dicts like actions in forHanaNew
1111
+ if(options.skipDict && options.skipDict[prop])
1112
+ return;
971
1113
  csnPath.push( prop );
972
1114
  for (let name of Object.getOwnPropertyNames( dict )) {
973
1115
  standard( dict, name, dict[name] );
@@ -980,8 +1122,11 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
980
1122
  function definitions( node, prop, dict ) {
981
1123
  csnPath.push( prop );
982
1124
  for (let name of Object.getOwnPropertyNames( dict )) {
983
- artifactTransformers.forEach(fn => fn(dict, name, dict[name]));
984
- standard( dict, name, dict[name] );
1125
+ const skip = options && options.skipArtifact && options.skipArtifact(dict[name], name) || false;
1126
+ if(!skip) {
1127
+ artifactTransformers.forEach(fn => fn(dict, name, dict[name]));
1128
+ standard( dict, name, dict[name] );
1129
+ }
985
1130
  }
986
1131
  if (!Object.prototype.propertyIsEnumerable.call( node, prop ))
987
1132
  setProp(node, '$' + prop, dict);
@@ -994,10 +1139,14 @@ function applyTransformations( csn, customTransformers={}, artifactTransformers=
994
1139
  path.forEach( function step( s, i ) {
995
1140
  if (s && typeof s === 'object') {
996
1141
  csnPath.push( i );
997
- if (s.args)
998
- standard( s, 'args', s.args );
999
- if (s.where)
1000
- standard( s, 'where', s.where );
1142
+ if(options.drillRef) {
1143
+ standard(path, i, s);
1144
+ } else {
1145
+ if (s.args)
1146
+ standard( s, 'args', s.args );
1147
+ if (s.where)
1148
+ standard( s, 'where', s.where );
1149
+ }
1001
1150
  csnPath.pop();
1002
1151
  }
1003
1152
  } );
@@ -1405,6 +1554,32 @@ function getServiceNames(csn) {
1405
1554
  return result;
1406
1555
  }
1407
1556
 
1557
+ /**
1558
+ * Check wether the artifact is @cds.persistence.skip
1559
+ *
1560
+ * @param {CSN.Artifact} artifact
1561
+ * @returns {Boolean}
1562
+ */
1563
+ function isSkipped(artifact) {
1564
+ return hasAnnotationValue(artifact, '@cds.persistence.skip', true)
1565
+ }
1566
+
1567
+ /**
1568
+ * Walk path in the CSN and return the result.
1569
+ *
1570
+ * @param {CSN.Model} csn
1571
+ * @param {CSN.Path} path
1572
+ * @returns {object} Whatever is at the end of path
1573
+ */
1574
+ function walkCsnPath(csn, path) {
1575
+ /** @type {object} */
1576
+ let obj = csn;
1577
+ for(let i = 0; i < path.length; i++){
1578
+ obj = obj[path[i]];
1579
+ }
1580
+
1581
+ return obj;
1582
+ }
1408
1583
 
1409
1584
  module.exports = {
1410
1585
  getUtils,
@@ -1444,4 +1619,6 @@ module.exports = {
1444
1619
  getNamespace,
1445
1620
  sortCsnDefinitionsForTests,
1446
1621
  getServiceNames,
1622
+ isSkipped,
1623
+ walkCsnPath,
1447
1624
  };
@@ -44,11 +44,13 @@ function enrichCsn( csn, options = {} ) {
44
44
  targetAspect: simpleRef,
45
45
  target: simpleRef,
46
46
  includes: simpleRef,
47
+ $origin,
47
48
  // TODO: excluding
48
49
  '@': () => { /* ignore annotations */ },
49
50
  }
50
51
  setLocations( csn, false, null );
51
- const { inspectRef, artifactRef, __getCache_forEnrichCsnDebugging } = csnRefs( csn );
52
+ const { inspectRef, artifactRef, getOrigin, __getCache_forEnrichCsnDebugging } =
53
+ csnRefs( csn );
52
54
  let $$cacheObjectNumber = 0; // for debugging
53
55
  const csnPath = [];
54
56
  if (csn.definitions)
@@ -119,6 +121,21 @@ function enrichCsn( csn, options = {} ) {
119
121
  // parent['_' + prop] = e.toString(); }
120
122
  }
121
123
 
124
+ function $origin( parent, prop, ref ) {
125
+ if (options.testMode) {
126
+ if (Array.isArray( ref )) // $origin: […], not $origin: {…}
127
+ parent._origin = refLocation( getOrigin( parent ) );
128
+ }
129
+ else {
130
+ try {
131
+ if (Array.isArray( ref )) // $origin: […], not $origin: {…}
132
+ parent._origin = refLocation( getOrigin( parent ) );
133
+ } catch (e) {
134
+ parent._origin = e.toString();
135
+ }
136
+ }
137
+ }
138
+
122
139
  function pathRef( parent, prop, path ) {
123
140
  const { links, art, scope, $env } = (() => {
124
141
  if (options.testMode)
@@ -76,6 +76,7 @@ function revealInternalProperties( model, name ) {
76
76
  _annotate: reveal,
77
77
  _deps: dependencyInfo,
78
78
  _status: primOrString, // is a string anyway
79
+ $messageFunctions: () => '‹some functions›',
79
80
  }
80
81
  unique_id = 1;
81
82
  return revealXsnPath(name, model);
@@ -159,7 +160,7 @@ function revealInternalProperties( model, name ) {
159
160
 
160
161
  function columns( nodes, query ) {
161
162
  // If we will have specified elements, we need another test to see columns in --parse-cdl
162
- return nodes && nodes.map( c => (c._parent && c._parent.elements && c.kind !== '$inline')
163
+ return nodes && nodes.map( c => (c._parent && c._parent.elements)
163
164
  ? artifactIdentifier( c, query )
164
165
  : reveal( c, nodes ) );
165
166
  }
@@ -22,17 +22,17 @@ function compareModels(beforeModel, afterModel, options) {
22
22
 
23
23
  const deletedEntities = Object.create(null);
24
24
  const elementAdditions = [];
25
- const elementChanges = [];
25
+ const migrations = []; // element changes/removals or changes of entity properties
26
26
 
27
27
  // There is currently no use in knowing the added entities only. If this changes, hand in `addedEntities` to `getArtifactComparator` below.
28
- forEachDefinition(afterModel, getArtifactComparator(beforeModel, null, null, elementAdditions, elementChanges)); // (, alerts(afterModel))
29
- forEachDefinition(beforeModel, getArtifactComparator(afterModel, null, deletedEntities, null, null)); // (, alerts(beforeModel))
28
+ forEachDefinition(afterModel, getArtifactComparator(beforeModel, null, null, elementAdditions, migrations));
29
+ forEachDefinition(beforeModel, getArtifactComparator(afterModel, null, deletedEntities, null, null));
30
30
 
31
31
  const returnObj = Object.create(null);
32
32
  returnObj.definitions = afterModel.definitions;
33
33
  returnObj.deletions = deletedEntities;
34
34
  returnObj.extensions = elementAdditions;
35
- returnObj.migrations = elementChanges;
35
+ returnObj.migrations = migrations;
36
36
  return returnObj;
37
37
  }
38
38
 
@@ -59,35 +59,45 @@ function validateCsnVersions(beforeModel, afterModel, options) {
59
59
  }
60
60
  }
61
61
 
62
- function getArtifactComparator(otherModel, addedEntities, deletedEntities, elementAdditions, elementChanges) { // (, alerts)
63
- return function compareArtifacts(artifact, name) { // (, topKey, path) topKey == 'definitions'
62
+ function getArtifactComparator(otherModel, addedEntities, deletedEntities, elementAdditions, migrations) {
63
+ return function compareArtifacts(artifact, name) {
64
64
  function addElements() {
65
65
  const elements = {};
66
66
  forEachMember(artifact, getElementComparator(otherArtifact, elements));
67
67
  if (Object.keys(elements).length > 0) {
68
- elementAdditions.push({
69
- extend: name,
70
- elements: elements
71
- });
68
+ elementAdditions.push(addedElements(name, elements));
72
69
  }
73
70
  }
74
- function removeOrChangeElements() {
71
+ function changePropsOrRemoveOrChangeElements() {
72
+ const relevantProperties = ['doc'];
73
+ const changedProperties = {};
74
+
75
75
  const removedElements = {};
76
76
  const changedElements = {};
77
- const modification = { migrate: name };
77
+
78
+ const migration = { migrate: name };
79
+
80
+ relevantProperties.forEach(prop => {
81
+ if (artifact[prop] !== otherArtifact[prop]) {
82
+ changedProperties[prop] = changedElement(artifact[prop], otherArtifact[prop] || null);
83
+ }
84
+ });
85
+ if (Object.keys(changedProperties).length > 0) {
86
+ migration.properties = changedProperties;
87
+ }
78
88
 
79
89
  forEachMember(otherArtifact, getElementComparator(artifact, removedElements));
80
90
  if (Object.keys(removedElements).length > 0) {
81
- modification.remove = removedElements;
91
+ migration.remove = removedElements;
82
92
  }
83
93
 
84
94
  forEachMember(artifact, getElementComparator(otherArtifact, null, changedElements));
85
95
  if (Object.keys(changedElements).length > 0) {
86
- modification.change = changedElements;
96
+ migration.change = changedElements;
87
97
  }
88
98
 
89
- if (modification.remove || modification.change) {
90
- elementChanges.push(modification);
99
+ if (migration.properties || migration.remove || migration.change) {
100
+ migrations.push(migration);
91
101
  }
92
102
  }
93
103
 
@@ -124,8 +134,8 @@ function getArtifactComparator(otherModel, addedEntities, deletedEntities, eleme
124
134
  if (elementAdditions) {
125
135
  addElements();
126
136
  }
127
- if (elementChanges) {
128
- removeOrChangeElements();
137
+ if (migrations) {
138
+ changePropsOrRemoveOrChangeElements();
129
139
  }
130
140
  };
131
141
  }
@@ -139,8 +149,8 @@ function isPersistedAsTable(artifact) {
139
149
  && !hasAnnotationValue(artifact, '@cds.persistence.exists');
140
150
  }
141
151
 
142
- function getElementComparator(otherArtifact, addedElements = null, changedElements = null) { // (, alerts)
143
- return function compareElements(element, name) { // (, topKey, path) topKey == 'elements'
152
+ function getElementComparator(otherArtifact, addedElements = null, changedElements = null) {
153
+ return function compareElements(element, name) {
144
154
  if (element._ignore) {
145
155
  return;
146
156
  }
@@ -209,6 +219,13 @@ function typeParametersChanged(element, otherElement) {
209
219
  return !deepEqual(element, otherElement, (key, depth) => !(depth === 0 && key === 'type'));
210
220
  }
211
221
 
222
+ function addedElements(entity, elements) {
223
+ return {
224
+ extend: entity,
225
+ elements
226
+ };
227
+ }
228
+
212
229
  function changedElement(element, otherElement) {
213
230
  return {
214
231
  old: otherElement,
@@ -37,8 +37,10 @@ optionProcessor
37
37
  .option(' --test-mode')
38
38
  .option(' --test-sort-csn')
39
39
  .option(' --doc-comment')
40
+ .option(' --add-texts-language-assoc')
40
41
  .option(' --localized-without-coalesce')
41
42
  .option(' --defaultStringLength <length>')
43
+ .option(' --no-recompile')
42
44
  .positionalArgument('<files...>')
43
45
  .help(`
44
46
  Usage: cdsc <command> [options] <files...>
@@ -88,12 +90,12 @@ optionProcessor
88
90
  --beta-mode Enable all unsupported, incomplete (beta) features
89
91
  --beta <list> Comma separated list of unsupported, incomplete (beta) features to use.
90
92
  Valid values are:
91
- keylessManagedAssoc
92
93
  foreignKeyConstraints
93
94
  addTextsLanguageAssoc
94
95
  hanaAssocRealCardinality
95
96
  mapAssocToJoinCardinality
96
97
  ignoreAssocPublishingInUnion
98
+ windowFunctions
97
99
  --constraints-not-enforced If this option is supplied, referential constraints are NOT ENFORCED
98
100
  This option is also applied to result of "cdsc manageConstraints"
99
101
  --constraints-not-validated If this option is supplied, referential constraints are NOT VALIDATED
@@ -124,8 +126,11 @@ optionProcessor
124
126
  OData CSN, CDL order and more. When --test-mode is enabled, this
125
127
  option is implicitly enabled as well.
126
128
  --doc-comment Preserve /** */ comments at annotation positions as doc property in CSN
129
+ --add-texts-language-assoc In generated texts entities, add association "language"
130
+ to "sap.common.Languages" if it exists
127
131
  --localized-without-coalesce Omit coalesce in localized convenience views
128
-
132
+ --no-recompile Don't recompile in case of internal errors
133
+
129
134
  Commands
130
135
  H, toHana [options] <files...> Generate HANA CDS source files
131
136
  O, toOdata [options] <files...> Generate ODATA metadata and annotations
@@ -335,7 +340,7 @@ optionProcessor.command('manageConstraints')
335
340
 
336
341
  optionProcessor.command('toCsn')
337
342
  .option('-h, --help')
338
- .option('-f, --flavor <flavor>', ['client', 'gensrc'])
343
+ .option('-f, --flavor <flavor>', ['client', 'gensrc', 'universal'])
339
344
  .option(' --with-localized')
340
345
  .help(`
341
346
  Usage: cdsc toCsn [options] <files...>
@@ -350,6 +355,7 @@ optionProcessor.command('toCsn')
350
355
  combination with additional "extend" or "annotate"
351
356
  statements, but not suitable for consumption by clients or
352
357
  backends
358
+ universal: in development (BETA)
353
359
 
354
360
  Internal options (for testing only, may be changed/removed at any time)
355
361
  --with-localized Add localized convenience views to the CSN output.
@@ -7,8 +7,11 @@
7
7
  "prefer-template": "error",
8
8
  "no-trailing-spaces": "error",
9
9
  "sonarjs/cognitive-complexity": "off",
10
+ "sonarjs/no-duplicate-string": ["off"],
11
+ "sonarjs/no-nested-template-literals": "off",
10
12
  "template-curly-spacing":["error", "never"],
11
- "complexity": ["warn", 30],
13
+ // Who cares - just very whiny and in the way
14
+ "complexity": "off",
12
15
  "max-len": "off",
13
16
  // We should enable this
14
17
  "no-shadow": "off"