@sap/cds-compiler 5.9.4 → 6.0.12

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 (114) hide show
  1. package/CHANGELOG.md +117 -319
  2. package/README.md +1 -1
  3. package/bin/cds_update_identifiers.js +3 -5
  4. package/bin/cdsc.js +24 -9
  5. package/bin/cdshi.js +1 -1
  6. package/bin/cdsse.js +4 -4
  7. package/doc/CHANGELOG_BETA.md +11 -0
  8. package/doc/CHANGELOG_DEPRECATED.md +29 -0
  9. package/lib/api/main.js +8 -5
  10. package/lib/api/options.js +12 -10
  11. package/lib/base/builtins.js +1 -0
  12. package/lib/base/message-registry.js +191 -99
  13. package/lib/base/messages.js +35 -21
  14. package/lib/base/model.js +14 -24
  15. package/lib/checks/actionsFunctions.js +10 -20
  16. package/lib/checks/annotationsOData.js +1 -1
  17. package/lib/checks/elements.js +35 -10
  18. package/lib/checks/enums.js +31 -0
  19. package/lib/checks/foreignKeys.js +2 -2
  20. package/lib/checks/hasPersistedElements.js +5 -0
  21. package/lib/checks/invalidTarget.js +1 -1
  22. package/lib/checks/managedWithoutKeys.js +5 -4
  23. package/lib/checks/queryNoDbArtifacts.js +10 -8
  24. package/lib/checks/types.js +5 -5
  25. package/lib/checks/validator.js +6 -4
  26. package/lib/compiler/assert-consistency.js +13 -9
  27. package/lib/compiler/checks.js +20 -52
  28. package/lib/compiler/define.js +31 -6
  29. package/lib/compiler/extend.js +5 -1
  30. package/lib/compiler/generate.js +14 -17
  31. package/lib/compiler/populate.js +8 -31
  32. package/lib/compiler/propagator.js +21 -35
  33. package/lib/compiler/resolve.js +64 -29
  34. package/lib/compiler/shared.js +16 -4
  35. package/lib/compiler/tweak-assocs.js +1 -1
  36. package/lib/compiler/utils.js +1 -1
  37. package/lib/edm/annotations/edmJson.js +23 -20
  38. package/lib/edm/annotations/genericTranslation.js +12 -10
  39. package/lib/edm/csn2edm.js +50 -56
  40. package/lib/edm/edm.js +33 -28
  41. package/lib/edm/edmInboundChecks.js +2 -2
  42. package/lib/edm/edmPreprocessor.js +54 -88
  43. package/lib/edm/edmUtils.js +9 -12
  44. package/lib/gen/BaseParser.js +63 -52
  45. package/lib/gen/CdlGrammar.checksum +1 -1
  46. package/lib/gen/CdlParser.js +1153 -1165
  47. package/lib/gen/Dictionary.json +21 -1
  48. package/lib/json/from-csn.js +70 -43
  49. package/lib/json/to-csn.js +6 -8
  50. package/lib/language/multiLineStringParser.js +3 -2
  51. package/lib/main.d.ts +58 -24
  52. package/lib/model/cloneCsn.js +3 -0
  53. package/lib/model/csnUtils.js +28 -39
  54. package/lib/model/xprAsTree.js +23 -9
  55. package/lib/modelCompare/compare.js +5 -4
  56. package/lib/optionProcessor.js +24 -17
  57. package/lib/parsers/AstBuildingParser.js +81 -25
  58. package/lib/parsers/XprTree.js +57 -3
  59. package/lib/parsers/identifiers.js +1 -1
  60. package/lib/parsers/index.js +0 -3
  61. package/lib/render/manageConstraints.js +25 -25
  62. package/lib/render/toCdl.js +173 -170
  63. package/lib/render/toHdbcds.js +126 -128
  64. package/lib/render/toRename.js +7 -7
  65. package/lib/render/toSql.js +128 -125
  66. package/lib/render/utils/common.js +47 -22
  67. package/lib/render/utils/delta.js +25 -25
  68. package/lib/render/utils/operators.js +2 -2
  69. package/lib/render/utils/pretty.js +5 -5
  70. package/lib/render/utils/sql.js +13 -13
  71. package/lib/render/utils/standardDatabaseFunctions.js +115 -103
  72. package/lib/render/utils/unique.js +4 -4
  73. package/lib/transform/db/applyTransformations.js +1 -1
  74. package/lib/transform/db/assertUnique.js +2 -2
  75. package/lib/transform/db/associations.js +6 -7
  76. package/lib/transform/db/assocsToQueries/utils.js +4 -5
  77. package/lib/transform/db/backlinks.js +12 -9
  78. package/lib/transform/db/cdsPersistence.js +8 -7
  79. package/lib/transform/db/constraints.js +13 -10
  80. package/lib/transform/db/expansion.js +7 -3
  81. package/lib/transform/db/flattening.js +4 -14
  82. package/lib/transform/db/processSqlServices.js +2 -1
  83. package/lib/transform/db/temporal.js +5 -7
  84. package/lib/transform/db/views.js +2 -4
  85. package/lib/transform/draft/db.js +8 -8
  86. package/lib/transform/draft/odata.js +10 -7
  87. package/lib/transform/forOdata.js +10 -5
  88. package/lib/transform/forRelationalDB.js +5 -75
  89. package/lib/transform/localized.js +1 -1
  90. package/lib/transform/odata/createForeignKeys.js +11 -10
  91. package/lib/transform/odata/flattening.js +8 -4
  92. package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +96 -0
  93. package/lib/transform/odata/typesExposure.js +3 -3
  94. package/lib/transform/transformUtils.js +4 -8
  95. package/lib/transform/translateAssocsToJoins.js +14 -7
  96. package/lib/transform/universalCsn/universalCsnEnricher.js +10 -4
  97. package/lib/utils/objectUtils.js +0 -17
  98. package/package.json +10 -13
  99. package/share/messages/def-upcoming-virtual-change.md +1 -1
  100. package/LICENSE +0 -37
  101. package/bin/cds_remove_invalid_whitespace.js +0 -138
  102. package/doc/CHANGELOG_ARCHIVE.md +0 -3604
  103. package/lib/gen/genericAntlrParser.js +0 -3
  104. package/lib/gen/language.checksum +0 -1
  105. package/lib/gen/language.interp +0 -456
  106. package/lib/gen/language.tokens +0 -180
  107. package/lib/gen/languageLexer.interp +0 -439
  108. package/lib/gen/languageLexer.js +0 -1483
  109. package/lib/gen/languageLexer.tokens +0 -167
  110. package/lib/gen/languageParser.js +0 -24941
  111. package/lib/language/antlrParser.js +0 -205
  112. package/lib/language/errorStrategy.js +0 -646
  113. package/lib/language/genericAntlrParser.js +0 -1572
  114. package/lib/parsers/CdlGrammar.g4 +0 -2070
@@ -63,7 +63,6 @@ function getUtils( model, universalReady ) {
63
63
  getContextOfArtifact,
64
64
  addStringAnnotationTo,
65
65
  getServiceName,
66
- hasAnnotationValue,
67
66
  getFinalTypeInfo,
68
67
  get$combined,
69
68
  getQueryPrimarySource,
@@ -238,7 +237,8 @@ function getUtils( model, universalReady ) {
238
237
  // Return true if 'node' is a managed association element
239
238
  // TODO: what about elements having a type, which (finally) is an assoc?
240
239
  function isManagedAssociation( node ) {
241
- return node.target !== undefined && node.on === undefined && node.keys;
240
+ // Since v6, managed to-many associations don't get any keys, hence don't require it.
241
+ return node.target !== undefined && node.on === undefined;
242
242
  }
243
243
 
244
244
  /**
@@ -631,34 +631,15 @@ function traverseFrom( from, queryCallback, path = [] ) {
631
631
  }
632
632
  }
633
633
 
634
-
635
634
  /**
636
- * Compare a given annotation value with an expectation value and return
637
- *
638
- * | Expected
639
- * | true | false | null | arb val
640
- * Anno Val |-------|-------|-------|--------
641
- * true | true | false | false | false
642
- * false | false | true | false | false
643
- * null | false | true | true | false
644
- * arb val | false | false | false | true/false
635
+ * Returns true if the artifact should be skipped during persistence.
636
+ * Respects special value `if-unused` by Node runtime.
645
637
  *
646
- * If the annotation value is 'null', 'true' is returned for the expectation values
647
- * 'null' and 'false'. Expecting 'null' for an annotation value 'false' returns
648
- * 'false'.
649
- *
650
- * @param {CSN.Artifact} artifact
651
- * @param {string} annotationName Name of the annotation (including the at-sign)
652
- * @param {any} expected
653
- * @param {boolean} caseInsensitive
638
+ * @param {CSN.Artifact} art
654
639
  * @returns {boolean}
655
640
  */
656
- function hasAnnotationValue( artifact, annotationName, expected = true, caseInsensitive = false ) {
657
- if (expected === false)
658
- return artifact[annotationName] === expected || artifact[annotationName] === null;
659
- else if (typeof artifact[annotationName] === 'string' && caseInsensitive === true)
660
- return artifact[annotationName].toLowerCase() === expected.toLowerCase();
661
- return artifact[annotationName] === expected;
641
+ function hasPersistenceSkipAnnotation( art ) {
642
+ return art['@cds.persistence.skip'] && art['@cds.persistence.skip'] !== 'if-unused';
662
643
  }
663
644
 
664
645
  /**
@@ -682,8 +663,7 @@ function isEdmPropertyRendered( elementCsn, options ) {
682
663
  const isNotIgnored = !elementCsn.target ? !elementCsn['@cds.api.ignore'] : true;
683
664
  const isNavigable = elementCsn.target
684
665
  ? (elementCsn['@odata.navigable'] === undefined ||
685
- elementCsn['@odata.navigable'] !== undefined &&
686
- (elementCsn['@odata.navigable'] === null || elementCsn['@odata.navigable'] === true)) : true;
666
+ (elementCsn['@odata.navigable'] === null || !!elementCsn['@odata.navigable'])) : true;
687
667
  // Foreign Keys can be ignored
688
668
  if (elementCsn['@odata.foreignKey4'])
689
669
  return isNotIgnored && renderForeignKey;
@@ -920,7 +900,7 @@ function setDependencies( csn, refs = csnRefs(csn) ) {
920
900
  * @returns {boolean}
921
901
  */
922
902
  function isPersistedOnDatabase( art ) {
923
- return !(art.kind === 'entity' && (art.abstract || hasAnnotationValue(art, '@cds.persistence.skip')));
903
+ return !(art.kind === 'entity' && (art.abstract || hasPersistenceSkipAnnotation(art)));
924
904
  }
925
905
  /**
926
906
  * Check if the given artifact will be persisted on the database via `CREATE VIEW`
@@ -932,9 +912,9 @@ function isPersistedAsView( artifact ) {
932
912
  return artifact && artifact.kind === 'entity' &&
933
913
  !artifact.$ignore &&
934
914
  !artifact.abstract &&
935
- ((artifact.query || artifact.projection) && !hasAnnotationValue(artifact, '@cds.persistence.table')) &&
936
- !hasAnnotationValue(artifact, '@cds.persistence.skip') &&
937
- !hasAnnotationValue(artifact, '@cds.persistence.exists');
915
+ ((artifact.query || artifact.projection) && !artifact['@cds.persistence.table']) &&
916
+ !hasPersistenceSkipAnnotation(artifact) &&
917
+ !artifact['@cds.persistence.exists'];
938
918
  }
939
919
  /**
940
920
  * Check if the given artifact will be persisted on the database via `CREATE TABLE`
@@ -946,9 +926,9 @@ function isPersistedAsTable( artifact ) {
946
926
  return artifact.kind === 'entity' &&
947
927
  !artifact.$ignore &&
948
928
  !artifact.abstract &&
949
- (!artifact.query && !artifact.projection || hasAnnotationValue(artifact, '@cds.persistence.table')) &&
950
- !hasAnnotationValue(artifact, '@cds.persistence.skip') &&
951
- !hasAnnotationValue(artifact, '@cds.persistence.exists');
929
+ (!artifact.query && !artifact.projection || artifact['@cds.persistence.table']) &&
930
+ !hasPersistenceSkipAnnotation(artifact) &&
931
+ !artifact['@cds.persistence.exists'];
952
932
  }
953
933
 
954
934
  /**
@@ -1127,8 +1107,9 @@ function moveAnnotationsAndDoc( sourceNode, targetNode, overwrite = false ) {
1127
1107
  * filter: Positive filter. If it returns true, annotations for the referenced artifact
1128
1108
  * will be applied.
1129
1109
  * applyToElements: Whether to apply annotations to elements or only to artifacts
1110
+ * @param {Function} error The error function
1130
1111
  */
1131
- function applyAnnotationsFromExtensions( csn, config ) {
1112
+ function applyAnnotationsFromExtensions( csn, config, error ) {
1132
1113
  if (!csn.extensions)
1133
1114
  return;
1134
1115
 
@@ -1137,6 +1118,8 @@ function applyAnnotationsFromExtensions( csn, config ) {
1137
1118
  for (let i = 0; i < csn.extensions.length; ++i) {
1138
1119
  const ext = csn.extensions[i];
1139
1120
  const name = ext.annotate || ext.extend;
1121
+ if (isAnnotateDraftAdminDataWithTenantIndependent(ext))
1122
+ error(null, [ 'extensions', i ], { name }, '$(NAME) can\'t be tenant independent');
1140
1123
  if (name && filter(name)) {
1141
1124
  const def = csn.definitions[name];
1142
1125
  if (def) {
@@ -1153,6 +1136,8 @@ function applyAnnotationsFromExtensions( csn, config ) {
1153
1136
  }
1154
1137
 
1155
1138
  csn.extensions = csn.extensions.filter(ext => ext);
1139
+ if (!csn.extensions.length)
1140
+ delete csn.extensions;
1156
1141
 
1157
1142
  function applyAnnotationsToElements( ext, def ) {
1158
1143
  // Only the definition is arrayed but the extension is not since
@@ -1177,6 +1162,10 @@ function applyAnnotationsFromExtensions( csn, config ) {
1177
1162
  if (Object.keys(ext.elements).length === 0)
1178
1163
  delete ext.elements;
1179
1164
  }
1165
+
1166
+ function isAnnotateDraftAdminDataWithTenantIndependent( ext ) {
1167
+ return ext.annotate && ext.annotate.endsWith('.DraftAdministrativeData') && ext['@cds.tenant.independent'];
1168
+ }
1180
1169
  }
1181
1170
 
1182
1171
  /**
@@ -1187,8 +1176,8 @@ function applyAnnotationsFromExtensions( csn, config ) {
1187
1176
  */
1188
1177
  function hasValidSkipOrExists( artifact ) {
1189
1178
  return artifact.kind === 'entity' &&
1190
- (hasAnnotationValue(artifact, '@cds.persistence.exists', true) ||
1191
- hasAnnotationValue(artifact, '@cds.persistence.skip', true));
1179
+ (artifact['@cds.persistence.exists'] ||
1180
+ hasPersistenceSkipAnnotation(artifact));
1192
1181
  }
1193
1182
 
1194
1183
  /**
@@ -1419,7 +1408,7 @@ module.exports = {
1419
1408
  forEachMember,
1420
1409
  forEachMemberRecursively,
1421
1410
  forAllQueries,
1422
- hasAnnotationValue,
1411
+ hasPersistenceSkipAnnotation,
1423
1412
  isEdmPropertyRendered,
1424
1413
  getArtifactDatabaseNameOf,
1425
1414
  getResultingName,
@@ -7,19 +7,26 @@
7
7
  //
8
8
  // Function/assoc arguments and filter conditions are
9
9
  // - traversed with expressionAsTree() and conditionAsTree(),
10
- // - not traversed with exprAsTree() and condAsTree()
10
+ // - not traversed with exprAsTree() and condAsTree(), you might need to call
11
+ // splitClauses() to cover `order by` in the last function argument
11
12
 
12
13
  'use strict';
13
14
 
14
- const xprParser = require( '../parsers/XprTree' );
15
+ const { csnAsTree, splitClauses } = require( '../parsers/XprTree' );
15
16
 
16
17
  function conditionAsTree( args ) {
17
18
  args.forEach( expressionAsTree );
18
- return xprParser.xprAsTree( args, args, true );
19
+ return asTree( args );
19
20
  }
20
21
  function condAsTree( args ) {
21
22
  args.forEach( exprAsTree );
22
- return xprParser.xprAsTree( args, args, true );
23
+ return asTree( args );
24
+ }
25
+
26
+ function asTree( args ) {
27
+ return (args.length === 2 && args[0] === 'over' && args[1]?.xpr && !args[1].func)
28
+ ? [ 'over', { xpr: splitClauses( args[1].xpr, true ) } ]
29
+ : csnAsTree( args );
23
30
  }
24
31
 
25
32
  function expressionAsTree( expr ) {
@@ -31,11 +38,17 @@ function expressionAsTree( expr ) {
31
38
  }
32
39
  if (expr.list) // expression, ref
33
40
  expr.list.forEach( expressionAsTree );
34
- if (expr.args) { // expression, ref
35
- if (Array.isArray( expr.args ) )
36
- expr.args.forEach(expressionAsTree);
37
- else
38
- Object.values(expr.args).forEach(expressionAsTree);
41
+ const { args } = expr;
42
+ if (args) { // expression, ref
43
+ if (!Array.isArray( args )) {
44
+ Object.values( args ).forEach( expressionAsTree );
45
+ }
46
+ else if (args.length) {
47
+ args.forEach( expressionAsTree );
48
+ const last = args.at( -1 );
49
+ if (last?.xpr && last.xpr.length > 4) // order by in last argument
50
+ last.xpr = splitClauses( last.xpr, true );
51
+ }
39
52
  }
40
53
  if (expr.ref) // expression
41
54
  expr.ref.forEach( expressionAsTree );
@@ -68,4 +81,5 @@ module.exports = {
68
81
  condAsTree,
69
82
  expressionAsTree,
70
83
  exprAsTree,
84
+ splitClauses: nodes => splitClauses( nodes, true ),
71
85
  };
@@ -57,20 +57,21 @@ function validateCsnVersions(beforeModel, afterModel, options) {
57
57
 
58
58
  if (!beforeVersionParts || beforeVersionParts.length < 2) {
59
59
  const { error, throwWithAnyError } = makeMessageFunction(beforeModel, options, 'modelCompare');
60
- error(null, null, { version: beforeVersion || 'undefined' }, 'Invalid CSN version: $(VERSION)');
60
+ error('api-invalid-version', null, { version: beforeVersion || 'unknown' });
61
61
  throwWithAnyError();
62
62
  }
63
63
  if (!afterVersionParts || afterVersionParts.length < 2) {
64
64
  const { error, throwWithAnyError } = makeMessageFunction(afterModel, options, 'modelCompare');
65
- error(null, null, { version: afterVersion || 'undefined' }, 'Invalid CSN version: $(VERSION)');
65
+ error('api-invalid-version', null, { version: afterVersion || 'unknown' });
66
66
  throwWithAnyError();
67
67
  }
68
68
  if (beforeVersionParts[0] > afterVersionParts[0] && !(options && options.allowCsnDowngrade)) {
69
69
  const { error, throwWithAnyError } = makeMessageFunction(afterModel, options, 'modelCompare');
70
70
 
71
71
  const { version } = require('../../package.json');
72
- error(null, null, { value: afterVersion, othervalue: beforeVersion, version },
73
- 'Incompatible CSN versions: $(VALUE) is a major downgrade from $(OTHERVALUE). Is @sap/cds-compiler version $(VERSION) outdated?');
72
+ error('api-invalid-version', null, {
73
+ '#': 'migrationComparison', value: afterVersion, othervalue: beforeVersion, version,
74
+ });
74
75
  throwWithAnyError();
75
76
  }
76
77
  }
@@ -18,7 +18,6 @@ const { availableBetaFlags } = require('./base/model');
18
18
  const optionProcessor = createOptionProcessor();
19
19
 
20
20
  // General options
21
- // FIXME: Since they mainly affect the compiler, they could also live near main.compile
22
21
  optionProcessor
23
22
  .option('-h, --help')
24
23
  .option('-v, --version')
@@ -40,17 +39,18 @@ optionProcessor
40
39
  .option(' --debug <id-list>')
41
40
  .option('-E, --enrich-csn')
42
41
  .option('-R, --raw-output <name>')
43
- .option(' --new-parser')
44
42
  .option(' --internal-msg')
45
43
  .option(' --beta-mode')
46
44
  .option(' --beta <list>')
47
45
  .option(' --deprecated <list>')
48
46
  .option(' --direct-backend')
47
+ .option(' --direct-messages')
49
48
  .option(' --fallback-parser <type>', { valid: [ 'auto!', 'cdl', 'csn', 'csn!' ] })
50
49
  .option(' --shuffle <seed>') // 0 | 1..4294967296
51
50
  .option(' --test-mode')
52
51
  .option(' --test-sort-csn')
53
52
  .option(' --doc-comment')
53
+ .option(' --propagate-doc-comments')
54
54
  .option(' --add-texts-language-assoc')
55
55
  .option(' --localized-without-coalesce')
56
56
  .option(' --tenant-discriminator')
@@ -119,7 +119,6 @@ optionProcessor
119
119
  with name = "+", write complete XSN, long!
120
120
  --tenant-discriminator Add tenant fields to entities
121
121
  --internal-msg Write raw messages with call stack to <stdout>/<stderr>
122
- --new-parser Use the new CDL parser
123
122
  --beta-mode Enable all unsupported, incomplete (beta) features
124
123
  --beta <list> Comma separated list of unsupported, incomplete (beta) features to use.
125
124
  Valid values are:
@@ -130,8 +129,6 @@ optionProcessor
130
129
  .join(`\n${ ' '.repeat(30) }`)
131
130
  }
132
131
  --deprecated <list> Comma separated list of deprecated options.
133
- Valid values are:
134
- eagerPersistenceForGeneratedEntities
135
132
  --fallback-parser <type> If the language cannot be deduced by the file's extensions, use this
136
133
  parser as a fallback. Valid values are:
137
134
  cdl : Use CDL parser
@@ -142,6 +139,8 @@ optionProcessor
142
139
  Can only be used with certain new CSN based backends. Combination with
143
140
  other flags is limited, e.g. --test-mode will not run a consistency check.
144
141
  No recompilation is triggered in case of errors. cdsc will dump.
142
+ --direct-messages Messages are written directly to console.error when an error/warning/… is detected.
143
+ Useful to see some messages before a compiler dump.
145
144
  --shuffle <seed> If provided, some internal processing sequences are changed, most notably by
146
145
  using a shuffled version of ‹model›.definitions. <seed> should be a number
147
146
  between 1 and 4294967296, the compiler uses a random number in that range if the
@@ -153,6 +152,7 @@ optionProcessor
153
152
  OData CSN, CDL order and more. When --test-mode is enabled, this
154
153
  option is implicitly enabled as well.
155
154
  --doc-comment Preserve /** */ comments at annotation positions as doc property in CSN
155
+ --propagate-doc-comments Propagate doc comments ('--doc-comment')
156
156
  --add-texts-language-assoc In generated texts entities, add association "language"
157
157
  to "sap.common.Languages" if it exists
158
158
  --localized-without-coalesce Omit coalesce in localized convenience views
@@ -163,7 +163,7 @@ optionProcessor
163
163
  --skip-name-check Skip certain name checks, e.g. that there must be no '.' in element names.
164
164
 
165
165
  Commands
166
- H, toHana [options] <files...> Generate HANA CDS source files
166
+ H, toHana [options] <files...> (deprecated) Generate HANA CDS source files
167
167
  O, toOdata [options] <files...> Generate ODATA metadata and annotations
168
168
  C, toCdl <files...> Generate CDS source files
169
169
  Q, toSql [options] <files...> Generate SQL DDL statements
@@ -204,10 +204,14 @@ optionProcessor.command('H, toHana')
204
204
  .option(' --assert-integrity-type <type>', { valid: [ 'RT', 'DB' ], ignoreCase: true })
205
205
  .option(' --pre2134ReferentialConstraintNames')
206
206
  .option(' --disable-hana-comments')
207
- .option(' --standard-database-functions')
207
+ .option(' --no-standard-database-functions')
208
208
  .help(`
209
209
  Usage: cdsc toHana [options] <files...>
210
210
 
211
+ ====================================================
212
+ DEPRECATED! Since v5, this backend is deprecated!
213
+ ====================================================
214
+
211
215
  Generate HANA CDS source files, or CSN.
212
216
 
213
217
  Options
@@ -241,7 +245,7 @@ optionProcessor.command('H, toHana')
241
245
  DB : Create database constraints for associations
242
246
  --pre2134ReferentialConstraintNames Do not prefix the constraint identifier with "c__"
243
247
  --disable-hana-comments Disable rendering of doc comments as SAP HANA comments.
244
- --standard-database-functions Enable rendering of standard database function mappings.
248
+ --no-standard-database-functions Disable rendering of standard database function mappings.
245
249
  `);
246
250
 
247
251
  optionProcessor.command('O, toOdata')
@@ -294,7 +298,7 @@ optionProcessor.command('O, toOdata')
294
298
  { prefix: { alias, ns, uri }, ... }
295
299
  --odata-no-creator Omit creator identification in API
296
300
  -n, --sql-mapping <style> Annotate artifacts and elements with "@cds.persistence.name", which is
297
- the corresponding database name (see "--sql-mapping" for "toHana or "toSql")
301
+ the corresponding database name (see "--sql-mapping" for "toSql")
298
302
  plain : (default) Names in uppercase and flattened with underscores
299
303
  quoted : Names in original case as in CDL. Entity names with dots,
300
304
  but element names flattened with underscores
@@ -319,8 +323,8 @@ optionProcessor.command('J, forJava')
319
323
 
320
324
  optionProcessor.command('C, toCdl')
321
325
  .option('-h, --help')
322
- .option(' --render-cdl-definition-nesting')
323
- .option(' --render-cdl-common-namespace')
326
+ .option(' --no-render-cdl-definition-nesting')
327
+ .option(' --no-render-cdl-common-namespace')
324
328
  .help(`
325
329
  Usage: cdsc toCdl [options] <files...>
326
330
 
@@ -328,10 +332,10 @@ optionProcessor.command('C, toCdl')
328
332
 
329
333
  Options
330
334
  -h, --help Show this help text
331
- --render-cdl-definition-nesting If set, definitions will be nested
335
+ --no-render-cdl-definition-nesting If set, definitions will be nested
332
336
  inside services/contexts instead of having only top-level
333
337
  definitions.
334
- --render-cdl-common-namespace If true and render-cdl-definition-nesting
338
+ --no-render-cdl-common-namespace If true and render-cdl-definition-nesting
335
339
  is set, a common namespace will be extracted and rendered.
336
340
  `);
337
341
 
@@ -355,9 +359,10 @@ optionProcessor.command('Q, toSql')
355
359
  .option(' --generated-by-comment')
356
360
  .option(' --better-sqlite-session-variables <bool>')
357
361
  .option(' --transitive-localized-views')
358
- .option(' --boolean-equality')
362
+ .option(' --no-boolean-equality')
359
363
  .option(' --with-hana-associations <bool>', { valid: [ 'true', 'false' ] })
360
- .option(' --standard-database-functions')
364
+ .option(' --no-standard-database-functions')
365
+ .option(' --dollar-now-as-timestamp')
361
366
  .help(`
362
367
  Usage: cdsc toSql [options] <files...>
363
368
 
@@ -420,8 +425,10 @@ optionProcessor.command('Q, toSql')
420
425
  Enable or disable rendering of "WITH ASSOCIATIONS" for sqlDialect 'hana'.
421
426
  true : (default) Render "WITH ASSOCIATIONS"
422
427
  false : Do not render "WITH ASSOCIATIONS"
423
- --standard-database-functions Enable rendering of standard database function mappings.
424
- --boolean-equality Enable support for boolean logic '!=' operator.
428
+ --no-standard-database-functions Disable rendering of standard database function mappings.
429
+ --no-boolean-equality Enable support for boolean logic '!=' operator.
430
+ --dollar-now-as-timestamp Render '$now' variable as 'CURRENT_TIMESTAMP' instead of 'SESSION_CONTEXT(…)'
431
+ for SQL dialect 'hana'.
425
432
  `);
426
433
 
427
434
  optionProcessor.command('toRename')
@@ -44,7 +44,6 @@ const extensionsCode = {
44
44
  service: 'extend service',
45
45
  };
46
46
 
47
- const PRECEDENCE_OF_IN_PREDICATE = 10;
48
47
  const PRECEDENCE_OF_EQUAL = 10;
49
48
 
50
49
  class AstBuildingParser extends BaseParser {
@@ -114,6 +113,8 @@ class AstBuildingParser extends BaseParser {
114
113
  err.expectedTokens = this.expectingArray();
115
114
  }
116
115
 
116
+ // Guards, Prepare Commands and Lookahead Methods -----------------------------
117
+
117
118
  tableWithoutAs() { // not used in <guard=…>, only called by other guard
118
119
  // TODO TOOL: if the tool properly creates `default: this.giR()`, this
119
120
  // condition method is most likely not necessary
@@ -136,7 +137,7 @@ class AstBuildingParser extends BaseParser {
136
137
  * - <guard=queryOnLeft, arg=tableWithoutAs>: …after having checked
137
138
  * whether the next token is no (reserved or unreserved) keyword
138
139
  */
139
- queryOnLeft( test, arg ) {
140
+ queryOnLeft( arg, test ) {
140
141
  if (arg === 'tableWithoutAs') {
141
142
  if (this.tableWithoutAs())
142
143
  return true;
@@ -177,8 +178,6 @@ class AstBuildingParser extends BaseParser {
177
178
  const text = typeof keyword === 'string' ? keyword.toUpperCase() : type;
178
179
  const generic = this.dynamic_.generic?.[text];
179
180
  if (tryGenericIntro) {
180
- if (this.dynamic_.generic?.IN === 'separator')
181
- this.prec_ = PRECEDENCE_OF_IN_PREDICATE; // only expressions if `in` is separator
182
181
  if (generic !== 'expr')
183
182
  return (generic === 'intro') ? 'GenericIntro' : type;
184
183
  // if both intro and expr: specialFunctions[fn][argPos][token] = 'expr'
@@ -220,7 +219,7 @@ class AstBuildingParser extends BaseParser {
220
219
  }
221
220
  }
222
221
 
223
- inSelectItem( _test, arg ) { // only as action
222
+ inSelectItem( arg ) { // <prepare=…>
224
223
  this.dynamic_.inSelectItem = arg ||
225
224
  (this.tokens[this.tokenIdx - 2].type === '.' ? 'inline' : 'expand');
226
225
  }
@@ -251,7 +250,7 @@ class AstBuildingParser extends BaseParser {
251
250
  return next === '*' || next === '{';
252
251
  }
253
252
 
254
- notAfterEntityArgOrFilter( mode ) { // TODO: for <hide>
253
+ notAfterEntityArgOrFilter( _arg, mode ) { // TODO: for <hide>
255
254
  if (mode !== 'M')
256
255
  return false;
257
256
  const { type } = this.lb();
@@ -263,9 +262,18 @@ class AstBuildingParser extends BaseParser {
263
262
 
264
263
  // <prec=10, postfix=once> + test that the next token is not `null`; TODO: code
265
264
  // completion for `… default 3 not ~;` → currently just `null` but hey
266
- isNegatedRelation( _test, prec ) {
265
+ isNegatedRelation( prec ) {
267
266
  return this.tokens[this.tokenIdx + 1]?.keyword === 'null' ||
268
- this.precNone_( _test, prec );
267
+ this.precNone_( prec );
268
+ }
269
+
270
+ // TODO: as leanCondition ? `order` should probably appear in the message for
271
+ // test3/Compiler/GrammarRobustness/InvalidSelectInWhere.err.cds
272
+ orderByLimitRestriction( _arg, mode ) {
273
+ if (mode && (!this.$allowOrderByLimit || this.precPost_( mode, 0 )))
274
+ return true;
275
+ this.$allowOrderByLimit = !mode;
276
+ return false;
269
277
  }
270
278
 
271
279
  isNamedArg() {
@@ -303,7 +311,7 @@ class AstBuildingParser extends BaseParser {
303
311
  /**
304
312
  * `annotation` def is only allowed top-level. TODO: combine with `extensionRestriction`
305
313
  */
306
- vocabularyRestriction( test ) {
314
+ vocabularyRestriction( _arg, test ) {
307
315
  if (!test)
308
316
  this.dynamic_.inBlock = this.tokenIdx;
309
317
  return this.dynamic_.inBlock ?? this.dynamic_.inExtension;
@@ -319,7 +327,7 @@ class AstBuildingParser extends BaseParser {
319
327
  * Currently only to restrict it to a single `Id` for published associations.
320
328
  * No extra syntax-unexpected-assoc for failure.
321
329
  */
322
- columnExpr( mode, arg ) {
330
+ columnExpr( arg, mode ) {
323
331
  if (mode)
324
332
  return !this.columnExpr$;
325
333
  if (arg)
@@ -331,7 +339,7 @@ class AstBuildingParser extends BaseParser {
331
339
  return true;
332
340
  }
333
341
 
334
- nestedExpand( mode ) {
342
+ nestedExpand( _arg, mode ) {
335
343
  if (!mode)
336
344
  this.nestedExpand$ = this.tokenIdx;
337
345
  return this.nestedExpand$ !== this.tokenIdx;
@@ -375,7 +383,7 @@ class AstBuildingParser extends BaseParser {
375
383
  * - DEFAULT: true if `default` had been provided
376
384
  * - NOTNULL: true if `null` or `not null` had been provided
377
385
  */
378
- elementRestriction( test, arg ) {
386
+ elementRestriction( arg, test ) {
379
387
  let { elementCtx } = this.dynamic_;
380
388
  if (test) {
381
389
  if (elementCtx?.[0] === arg)
@@ -418,7 +426,7 @@ class AstBuildingParser extends BaseParser {
418
426
  'Unexpected $(OFFENDING) after $(KEYWORD) clause, expecting $(EXPECTING)' );
419
427
  }
420
428
 
421
- noRepeatedCardinality( mode ) {
429
+ noRepeatedCardinality( _arg, mode ) {
422
430
  if (this.tokens[this.tokenIdx - 2]?.type !== ']')
423
431
  return false;
424
432
  if (mode === 'M')
@@ -443,7 +451,7 @@ class AstBuildingParser extends BaseParser {
443
451
  *
444
452
  * Beware: mentioned in leanConditions, i.e. executed in predictions!
445
453
  */
446
- afterBrace( test, arg ) {
454
+ afterBrace( arg, test ) {
447
455
  if (!test) {
448
456
  if (arg === 'normal' && this.lb().type !== '}') {
449
457
  const { type, keyword } = this.la();
@@ -487,7 +495,7 @@ class AstBuildingParser extends BaseParser {
487
495
  /**
488
496
  * For annotations at the beginning of columns outside parentheses
489
497
  */
490
- annoInSameLine( test ) {
498
+ annoInSameLine( _arg, test ) {
491
499
  if (!test)
492
500
  this.dynamic_.safeAnno = true;
493
501
  return !this.dynamic_.safeAnno &&
@@ -499,7 +507,7 @@ class AstBuildingParser extends BaseParser {
499
507
  * - `...` can appear in the top-level array value only and not after `...`
500
508
  * without `up to`.
501
509
  */
502
- arrayAnno( test, arg ) {
510
+ arrayAnno( arg, test ) {
503
511
  if (!test) {
504
512
  this.dynamic_.arrayAnno = [ !this.dynamic_.arrayAnno ];
505
513
  }
@@ -532,7 +540,7 @@ class AstBuildingParser extends BaseParser {
532
540
  return this.tokens[this.tokenIdx + 1]?.text !== ':';
533
541
  }
534
542
 
535
- fail( mode ) {
543
+ fail( _arg, mode ) {
536
544
  // TODO TOOL: the following test belongs to the BaseParser.js:
537
545
  if (this.conditionTokenIdx === this.tokenIdx && // tested on same
538
546
  this.conditionStackLength == null && // after error recover
@@ -569,13 +577,27 @@ class AstBuildingParser extends BaseParser {
569
577
 
570
578
  noAssignmentInSameLine() {
571
579
  const next = this.la();
572
- if (next.text === '@' && next.line <= this.lb().endLine) {
580
+ if (next.text === '@' && next.location.line <= this.lb().location.endLine &&
581
+ // TODO Tool Runtime: it is probably better to skip tokens directly
582
+ // do not report error if the '@' is not correct:
583
+ this.s !== null && this.tokenIdx > this.recoverTokenIdx) {
573
584
  this.warning( 'syntax-missing-semicolon', next, { code: ';' },
574
585
  // eslint-disable-next-line @stylistic/js/max-len
575
586
  'Add a $(CODE) and/or newline before the annotation assignment to indicate that it belongs to the next statement' );
576
587
  }
577
588
  }
578
589
 
590
+ reportDubiousAnnoSpacing() {
591
+ const at = this.lb();
592
+ const before = this.tokens[this.tokenIdx - 2];
593
+ if (before?.type === 'Id' && before.location.endLine === at.location.line &&
594
+ before.location.endCol === at.location.col) {
595
+ this.warning( 'syntax-expecting-anno-space', at.location, { code: '@' },
596
+ 'Expecting a space before the $(CODE) starting an annotation assignment' );
597
+ }
598
+ this.reportUnexpectedSpace( at );
599
+ }
600
+
579
601
  // For :param, #variant, #symbol, @(…) and @Begin and `@` inside annotation paths,
580
602
  // inside `.*` and `.{`
581
603
  reportUnexpectedSpace( prefix = this.lb(),
@@ -604,6 +626,8 @@ class AstBuildingParser extends BaseParser {
604
626
  return prefixLoc;
605
627
  }
606
628
 
629
+ // Locations for ASTs ---------------------------------------------------------
630
+
607
631
  startLocation( { location } = this.lr() ) {
608
632
  return {
609
633
  __proto__: Location.prototype,
@@ -749,8 +773,23 @@ class AstBuildingParser extends BaseParser {
749
773
  return ast;
750
774
  }
751
775
 
776
+ virtualOrImplicit( art ) {
777
+ const token = this.lb();
778
+ const ref = art.value.func || art.value;
779
+ if (!art.virtual ||
780
+ ref.path[0].location.tokenIndex < token.location.tokenIndex ||
781
+ this.la().text === '{') {
782
+ this.classifyImplicitName( 'ItemImplicit', ref );
783
+ }
784
+ else {
785
+ token.parsedAs = 'ItemAlias';
786
+ art.name = ref.path[0];
787
+ art.value = undefined;
788
+ }
789
+ }
790
+
752
791
  classifyImplicitName( category, ref ) {
753
- if (!ref || ref.path) {
792
+ if (!ref || ref.path) { // TODO: func
754
793
  const tokenIndex = ref?.path.at(-1)?.location.tokenIndex;
755
794
  const token = this.prevTokenWithIndex( tokenIndex ) ?? this.tokens[this.tokenIdx - 1];
756
795
  const { parsedAs } = token;
@@ -1043,10 +1082,8 @@ class AstBuildingParser extends BaseParser {
1043
1082
  line: chosen.location.line,
1044
1083
  col: chosen.location.col,
1045
1084
  };
1046
- if (erroneous.val === chosen.val) {
1047
- // TODO v6: duplicate clause = error, independently whether it is the same
1048
- this.warning( 'syntax-duplicate-equal-clause', erroneous.location, args );
1049
- }
1085
+ if (erroneous.val === chosen.val)
1086
+ this.message( 'syntax-duplicate-equal-clause', erroneous.location, args );
1050
1087
  // TODO extra msg text 'syntax-duplicate-clause' for noRepeatedCardinality()
1051
1088
  }
1052
1089
 
@@ -1169,6 +1206,21 @@ class AstBuildingParser extends BaseParser {
1169
1206
  };
1170
1207
  }
1171
1208
 
1209
+ valuePathAstWithNew( expr, path ) {
1210
+ path = this.valuePathAst( path );
1211
+ if (path.op?.val !== 'ixpr') {
1212
+ expr.args.push( path );
1213
+ }
1214
+ else {
1215
+ const ref = path.args[0];
1216
+ const op = { val: 'ixpr', location: expr.args[0].location };
1217
+ const location = this.combineLocation( expr.args[0], ref );
1218
+ path.args[0] = { op, args: [ expr.args[0], ref ], location };
1219
+ expr.args = path.args;
1220
+ }
1221
+ this.attachLocation( expr );
1222
+ }
1223
+
1172
1224
  valuePathAst( ref ) {
1173
1225
  // TODO: XSN representation of functions is a bit strange - rework
1174
1226
  // TODO: rework this function
@@ -1189,8 +1241,12 @@ class AstBuildingParser extends BaseParser {
1189
1241
  if (filter) // TODO v7: make this be reported via guard, as error
1190
1242
  this.message( 'syntax-unexpected-filter', filter.location, {} );
1191
1243
  // TODO: XSN representation of functions is a bit strange - rework
1192
- const op = { location, val: 'call' };
1193
- return this.attachLocation( { op, func: ref, args } );
1244
+ return this.attachLocation( {
1245
+ op: { location, val: 'call' },
1246
+ func: ref,
1247
+ args,
1248
+ location: ref.location,
1249
+ } );
1194
1250
  }
1195
1251
 
1196
1252
  // $syntax === ':' => path(P: 1)