@sap/cds-compiler 6.9.2 → 7.0.1

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 (69) hide show
  1. package/CHANGELOG.md +86 -2
  2. package/bin/cdsc.js +4 -33
  3. package/doc/IncompatibleChanges_v7.md +639 -0
  4. package/lib/api/main.js +4 -56
  5. package/lib/api/options.js +6 -15
  6. package/lib/api/validate.js +1 -0
  7. package/lib/base/builtins.js +1 -2
  8. package/lib/base/csnRefs.js +2 -6
  9. package/lib/base/message-registry.js +82 -76
  10. package/lib/base/messages.js +23 -4
  11. package/lib/base/optionProcessor.js +2 -72
  12. package/lib/base/specialOptions.js +20 -17
  13. package/lib/checks/defaultValues.js +1 -39
  14. package/lib/checks/hasPersistedElements.js +19 -3
  15. package/lib/checks/parameters.js +0 -34
  16. package/lib/checks/selectItems.js +2 -38
  17. package/lib/checks/typeParameters.js +162 -0
  18. package/lib/checks/validator.js +5 -8
  19. package/lib/compiler/assert-consistency.js +19 -5
  20. package/lib/compiler/checks.js +47 -43
  21. package/lib/compiler/define.js +6 -6
  22. package/lib/compiler/extend.js +102 -111
  23. package/lib/compiler/generate.js +4 -8
  24. package/lib/compiler/populate.js +4 -7
  25. package/lib/compiler/propagator.js +9 -9
  26. package/lib/compiler/resolve.js +205 -7
  27. package/lib/compiler/shared.js +76 -82
  28. package/lib/compiler/tweak-assocs.js +102 -22
  29. package/lib/compiler/utils.js +57 -12
  30. package/lib/compiler/xpr-rewrite.js +2 -15
  31. package/lib/edm/annotations/edmJson.js +14 -10
  32. package/lib/edm/annotations/genericTranslation.js +3 -1
  33. package/lib/edm/annotations/preprocessAnnotations.js +9 -26
  34. package/lib/edm/csn2edm.js +27 -20
  35. package/lib/edm/edmUtils.js +25 -0
  36. package/lib/gen/CdlGrammar.checksum +1 -1
  37. package/lib/gen/CdlParser.js +2237 -2241
  38. package/lib/gen/Dictionary.json +17 -2
  39. package/lib/json/from-csn.js +67 -52
  40. package/lib/json/to-csn.js +28 -25
  41. package/lib/language/textUtils.js +0 -13
  42. package/lib/main.d.ts +34 -59
  43. package/lib/main.js +1 -1
  44. package/lib/model/csnUtils.js +9 -8
  45. package/lib/parsers/AstBuildingParser.js +45 -55
  46. package/lib/parsers/Lexer.js +2 -0
  47. package/lib/parsers/identifiers.js +0 -9
  48. package/lib/render/toCdl.js +41 -40
  49. package/lib/render/toSql.js +8 -1
  50. package/lib/render/utils/common.js +1 -1
  51. package/lib/render/utils/sql.js +2 -3
  52. package/lib/tool-lib/enrichCsn.js +1 -2
  53. package/lib/transform/db/applyTransformations.js +7 -5
  54. package/lib/transform/db/assertUnique.js +8 -51
  55. package/lib/transform/db/associations.js +1 -1
  56. package/lib/transform/db/cdsPersistence.js +1 -15
  57. package/lib/transform/db/expansion.js +9 -12
  58. package/lib/transform/db/flattening.js +1 -1
  59. package/lib/transform/db/groupByOrderBy.js +0 -16
  60. package/lib/transform/db/views.js +57 -161
  61. package/lib/transform/draft/db.js +2 -2
  62. package/lib/transform/forOdata.js +25 -14
  63. package/lib/transform/forRelationalDB.js +93 -301
  64. package/lib/transform/localized.js +33 -102
  65. package/lib/transform/odata/flattening.js +11 -2
  66. package/lib/transform/transformUtils.js +25 -3
  67. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -2
  68. package/package.json +2 -2
  69. package/lib/render/toHdbcds.js +0 -1810
@@ -1741,10 +1741,18 @@
1741
1741
  },
1742
1742
  "PersonalData.IsPotentiallySensitive": {
1743
1743
  "AppliesTo": [
1744
- "Property"
1744
+ "Property",
1745
+ "EntityType"
1745
1746
  ],
1746
1747
  "Type": "Core.Tag"
1747
1748
  },
1749
+ "PersonalData.RelatedDataCategoryID": {
1750
+ "AppliesTo": [
1751
+ "Property",
1752
+ "EntityType"
1753
+ ],
1754
+ "Type": "Collection(Edm.String)"
1755
+ },
1748
1756
  "Repeatability.DeleteWithClientIDSupported": {
1749
1757
  "AppliesTo": [
1750
1758
  "EntityContainer"
@@ -3434,7 +3442,6 @@
3434
3442
  "$kind": "ComplexType",
3435
3443
  "Properties": {
3436
3444
  "Discretionary": "Edm.Boolean",
3437
- "EffectTypes": "Common.EffectType",
3438
3445
  "SourceEntities": "Collection(Edm.NavigationPropertyPath)",
3439
3446
  "SourceEvents": "Collection(Edm.String)",
3440
3447
  "SourceProperties": "Collection(Edm.PropertyPath)",
@@ -4235,6 +4242,10 @@
4235
4242
  "Type": "String",
4236
4243
  "Value": "ContractRelatedID"
4237
4244
  },
4245
+ "DataCategoryID": {
4246
+ "Type": "String",
4247
+ "Value": "DataCategoryID"
4248
+ },
4238
4249
  "DataControllerID": {
4239
4250
  "Type": "String",
4240
4251
  "Value": "DataControllerID"
@@ -4255,6 +4266,10 @@
4255
4266
  "Type": "String",
4256
4267
  "Value": "EndOfRetentionDate"
4257
4268
  },
4269
+ "IsBlockedIndicator": {
4270
+ "Type": "String",
4271
+ "Value": "IsBlockedIndicator"
4272
+ },
4258
4273
  "LegalEntityID": {
4259
4274
  "Type": "String",
4260
4275
  "Value": "LegalEntityID"
@@ -117,7 +117,7 @@
117
117
 
118
118
  const { dictAdd } = require('../compiler/dictionaries');
119
119
  const { quotedLiteralPatterns } = require('../compiler/builtins');
120
- const { exprProperties } = require('../base/builtins');
120
+ const { exprProperties, primaryExprProperties } = require('../base/builtins');
121
121
  const { CompilerAssertion } = require('../base/error');
122
122
  const { Location } = require('../base/location');
123
123
  const { XsnSource } = require('../compiler/xsn-model');
@@ -499,9 +499,9 @@ const schema = compileSchema( {
499
499
  '#': {
500
500
  noPrefix: true, // schema spec for '#', not for '#whatever'
501
501
  type: symbol,
502
- // Note: We emit a warning if '#' is used in enums. Because the compiler
503
- // can generate CSN like this, we need to be able to parse it.
504
- inKind: [ '$column', 'enum' ],
502
+ // Note: currently, we allow `#` in enum symbol definitions (→ also via CSN),
503
+ // the compiler just emits a warning ref-unexpected-enum
504
+ inKind: [ '$column', 'enum' ], // TODO v8: delete 'enum'
505
505
  xorException: 'val', // see xorGroup :expr
506
506
  // see also extra handling for 'element' in extension, see definition()
507
507
  },
@@ -656,14 +656,14 @@ const schema = compileSchema( {
656
656
  '-expr': { // '-expr' and '-' must not exist top-level
657
657
  prop: '@‹anno›',
658
658
  type: object,
659
- optional: [
660
- '=', '#', 'xpr', 'ref', 'val', 'list',
661
- 'literal', 'func', 'args', 'param',
662
- 'cast',
659
+ optional: [ // exprProperties + '=' - 'SELECT' - 'SET'
660
+ '=',
661
+ 'ref', 'xpr', 'list', 'val', '#', 'func',
662
+ 'param', 'literal', 'args', 'cast',
663
663
  ],
664
664
  schema: {
665
665
  '=': {
666
- type: renameTo( '$tokenTexts', stringOrBool ),
666
+ type: renameTo( '$tokenTexts', stringOrTrue ),
667
667
  xorGroups: null, // reset xorGroup; allow '=' for all :expr
668
668
  },
669
669
  },
@@ -678,10 +678,6 @@ const schema = compileSchema( {
678
678
  type: boolOrNull,
679
679
  inKind: [ 'element', '$column' ],
680
680
  },
681
- masked: {
682
- type: masked,
683
- inKind: [ 'element' ],
684
- },
685
681
  notNull: {
686
682
  type: boolOrNull,
687
683
  inKind: [ 'element', 'param', 'type' ], // TODO: $column - or if so: in 'cast'?
@@ -703,6 +699,7 @@ const schema = compileSchema( {
703
699
  },
704
700
  includes: {
705
701
  arrayOf: stringRef,
702
+ type: includeRefs,
706
703
  inKind: [ 'entity', 'type', 'aspect', 'event', 'extend' ],
707
704
  },
708
705
  returns: {
@@ -768,6 +765,11 @@ const schema = compileSchema( {
768
765
  $generated: {
769
766
  type: string,
770
767
  },
768
+ $postponeIncludes: { // elements to be added before applying include
769
+ arrayOf: natnum,
770
+ type: postponeIncludes, // values stored with `includes` references
771
+ inKind: [ 'entity', 'type', 'aspect', 'event' ], // not 'extend' yet
772
+ },
771
773
  $: {
772
774
  noPrefix: false, // just '$' is no CSN property
773
775
  type: ignore,
@@ -1132,6 +1134,44 @@ function returnsDefinition( def, spec, xsn, csn ) {
1132
1134
  return definition( def, spec, xsn, csn, '' );
1133
1135
  }
1134
1136
 
1137
+ function includeRefs( def, spec, xsn, csn ) {
1138
+ const array = arrayOf( stringRef )( def, spec, xsn, csn );
1139
+ if (array && Array.isArray( csn.$postponeIncludes )) {
1140
+ array.forEach( ( ref, idx ) => {
1141
+ const postpone = csn.$postponeIncludes[idx];
1142
+ ref.$postponeInclude = (Number.isSafeInteger( postpone ) && postpone >= 0 )
1143
+ ? postpone
1144
+ : 0;
1145
+ } );
1146
+ }
1147
+ return array;
1148
+ }
1149
+
1150
+ function postponeIncludes( array, spec, xsn, csn ) {
1151
+ if (!isArray( array ) || !array.length)
1152
+ return;
1153
+ let noElems = (!csn.elements || typeof csn.elements !== 'object')
1154
+ ? 0
1155
+ : Object.values( csn.elements )
1156
+ .reduce( ( acc, elem ) => (elem?.kind === 'extend' ? acc : acc + 1), 0 );
1157
+ let noIncludes = csn.includes?.length || 0;
1158
+ const { prop } = spec;
1159
+ for (const num of array) {
1160
+ ++virtualLine;
1161
+ if (--noIncludes === -1) {
1162
+ error( 'syntax-invalid-special', location(true), { '#': 'long', prop } );
1163
+ }
1164
+ else if (noIncludes < -1) {
1165
+ ignore( num ); // no further item check after syntax-invalid-special#long
1166
+ }
1167
+ else if (natnum( num, spec ) && (noElems -= num) < 0) {
1168
+ error( 'syntax-invalid-special', location(true), { '#': 'sum', prop } );
1169
+ noIncludes = -1;
1170
+ }
1171
+ }
1172
+ ++virtualLine;
1173
+ }
1174
+
1135
1175
  // Temporary function as long as the message below is not a hard error
1136
1176
  function elementsDict( def, spec, xsn ) {
1137
1177
  const elements = dictionaryOf( definition )( def, spec );
@@ -1319,11 +1359,11 @@ function string( val, spec ) {
1319
1359
  return ignore( val );
1320
1360
  }
1321
1361
 
1322
- function stringOrBool( val, spec ) {
1323
- if (typeof val === 'string' && val || typeof val === 'boolean')
1324
- return val;
1362
+ function stringOrTrue( val, spec ) {
1363
+ if (typeof val === 'string' && val || val === true)
1364
+ return true;
1325
1365
  error( 'syntax-expecting-string', location(true),
1326
- { '#': spec.msgVariant || 'or-bool', prop: spec.msgProp } );
1366
+ { '#': spec.msgVariant || 'or-true', prop: spec.msgProp } );
1327
1367
  return ignore( val );
1328
1368
  }
1329
1369
 
@@ -1331,8 +1371,7 @@ function stringVal( val, spec ) {
1331
1371
  if (typeof val === 'string' && val)
1332
1372
  // XSN TODO: do not require literal
1333
1373
  return { val, literal: 'string', location: location() };
1334
- error( 'syntax-expecting-string', location(true), { prop: spec.msgProp },
1335
- 'Expecting non-empty string for property $(PROP)' );
1374
+ error( 'syntax-expecting-string', location(true), { prop: spec.msgProp } );
1336
1375
  return ignore( val );
1337
1376
  }
1338
1377
 
@@ -1451,33 +1490,11 @@ function annoValue( val, spec ) {
1451
1490
  }
1452
1491
  return retval;
1453
1492
  }
1454
- else if (typeof val['='] === 'string' || val['='] === true) {
1455
- // An object with `=` is an expression if and only if:
1456
- // - there is exactly one property ('=')
1457
- // - there is at least one other expression property (e.g. "xpr")
1458
- const valKeys = Object.keys( val );
1459
- if (valKeys.length === 1 && typeof val['='] === 'string') {
1460
- ++virtualLine;
1461
- const r = refSplit( val['='], '=' ); // i.e. no extra `variant` stuff
1462
- ++virtualLine;
1463
- return r;
1464
- }
1465
- else if (exprProperties.some( prop => val[prop] !== undefined )) {
1466
- const s = schema['@'].schema['-expr'];
1467
- const r = { location: location() };
1468
- Object.assign( r, object( val, s ) );
1469
- return r;
1470
- }
1471
- // fallthrough -> unchecked structure
1472
- }
1473
- if (typeof val['#'] === 'string') {
1474
- if (Object.keys( val ).length === 1) {
1475
- ++virtualLine;
1476
- const xsn = { location: location() };
1477
- symbol( val['#'], schema['#'], xsn );
1478
- ++virtualLine;
1479
- return xsn;
1480
- }
1493
+ else if (primaryExprProperties.some( prop => val[prop] !== undefined )) {
1494
+ const s = schema['@'].schema['-expr'];
1495
+ const r = object( val, s );
1496
+ r.$tokenTexts = true; // property `=` might not be present
1497
+ return r;
1481
1498
  }
1482
1499
  else if (val['...'] !== undefined && Object.keys( val ).length === 1) {
1483
1500
  // TODO: only if not nested - see error above
@@ -1495,8 +1512,11 @@ function annoValue( val, spec ) {
1495
1512
  }
1496
1513
  const r = { struct: Object.create(null), literal: 'struct', location: location() };
1497
1514
  ++virtualLine;
1515
+ // Here, we do not really care whether the anno value is an unchecked reference
1516
+ // or a structure with a `=` property → just check the `=` value to be a string
1498
1517
  for (const name of Object.keys( val )) {
1499
- r.struct[name] = annotation( val[name], schema['@'], null, val, name );
1518
+ if (name !== '=' || string( val[name], schema['='] ))
1519
+ r.struct[name] = annotation( val[name], schema['@'], null, val, name );
1500
1520
  ++virtualLine;
1501
1521
  }
1502
1522
  return r;
@@ -1770,11 +1790,6 @@ function duplicateExcluding( name, loc ) {
1770
1790
  error( 'syntax-duplicate-excluding', loc, { '#': 'csn', name, prop: 'excluding[]' } );
1771
1791
  }
1772
1792
 
1773
- function masked( val, spec ) {
1774
- message( 'syntax-unsupported-masked', location(), { '#': 'csn', prop: 'masked' } );
1775
- return boolOrNull( val, spec );
1776
- }
1777
-
1778
1793
  function setOp( val, spec ) { // UNION, ...
1779
1794
  // similar to string(), but without literal
1780
1795
  return string( val, spec ) && { val, location: location() };
@@ -13,7 +13,7 @@
13
13
 
14
14
  const { locationString } = require('../base/messages');
15
15
  const { isBetaEnabled } = require('../base/specialOptions');
16
- const { pathName } = require('../compiler/utils');
16
+ const { pathName, hasSpecialPathItemProp } = require('../compiler/utils');
17
17
  const { CompilerAssertion } = require('../base/error');
18
18
 
19
19
  const compilerVersion = require('../../package.json').version;
@@ -60,7 +60,6 @@ const transformers = {
60
60
  virtual: value,
61
61
  key: value,
62
62
  unique: value,
63
- masked: value,
64
63
  params,
65
64
  // early expression / query properties -------------------------------------
66
65
  op: o => ((o.val !== 'SELECT' && o.val !== '$query') ? o.val : undefined),
@@ -96,7 +95,7 @@ const transformers = {
96
95
  foreignKeys,
97
96
  enum: enumDict,
98
97
  items,
99
- includes: arrayOf( artifactRef ), // also entities
98
+ includes,
100
99
  // late expressions / query properties -------------------------------------
101
100
  mixin: insertOrderDict, // only in queries with special handling
102
101
  columns,
@@ -275,7 +274,7 @@ function compactModel( model, options = model.options || {} ) {
275
274
  (options.parseCdl ? 'parsed' : 'inferred');
276
275
  if (!options.testMode) {
277
276
  csn.meta = Object.assign( {}, model.meta, meta,
278
- { creator, compilerVersion, compilerCsnFlavor } );
277
+ { creator, compilerCsnFlavor } );
279
278
  csn.$version = csnVersion;
280
279
  }
281
280
  else if (meta !== undefined) {
@@ -723,7 +722,7 @@ function definition( art, csn, _node, prop ) {
723
722
  }
724
723
 
725
724
  const c = standard( art );
726
- // The XSN of actions in extensions do not contain a returns yet - TODO?
725
+ // The XSN of actions in extensions do not contain a returns yet - TODO: still?
727
726
  const elems = c.elements;
728
727
  if (elems && (prop === 'actions' || art.$syntax === 'returns')) {
729
728
  delete c.elements;
@@ -752,12 +751,12 @@ function queryOrigin( xsn ) {
752
751
  /**
753
752
  * Create $origin specification for `includes` of `art`.
754
753
  */
755
- function includesOrigin( includes, art ) {
756
- const $origin = originRef( includes[0]._artifact );
757
- if (includes.length === 1)
754
+ function includesOrigin( refs, art ) {
755
+ const $origin = originRef( refs[0]._artifact );
756
+ if (refs.length === 1)
758
757
  return $origin;
759
758
  const result = { $origin };
760
- for (const incl of includes.slice(1)) {
759
+ for (const incl of refs.slice(1)) {
761
760
  const aspect = incl._artifact;
762
761
  for (const prop in aspect) {
763
762
  if ((prop.charAt(0) === '@' || prop === 'doc') &&
@@ -1104,18 +1103,14 @@ function artifactRef( node, terse ) {
1104
1103
  : ref[0];
1105
1104
  }
1106
1105
 
1106
+ function includes( refs, csn ) {
1107
+ csn.includes = refs.map( r => artifactRef( r, true ) );
1108
+ if (gensrcFlavor && refs.some( r => r.$postponeInclude ))
1109
+ csn.$postponeIncludes = refs.map( r => r.$postponeInclude ?? 0 );
1110
+ }
1111
+
1107
1112
  function pathItem( item ) {
1108
- if (!item.args &&
1109
- !item.where &&
1110
- !item.groupBy &&
1111
- !item.having &&
1112
- !item.limit &&
1113
- !item.orderBy &&
1114
- !item.cardinality &&
1115
- !item.$extra &&
1116
- !item.$syntax)
1117
- return item.id;
1118
- return standard( item );
1113
+ return (hasSpecialPathItemProp( item )) ? standard( item ) : item.id;
1119
1114
  }
1120
1115
 
1121
1116
  function args( node ) {
@@ -1140,9 +1135,7 @@ function anno( node ) {
1140
1135
  }
1141
1136
  if (node.$inferred && gensrcFlavor)
1142
1137
  return undefined;
1143
- if (node.$tokenTexts) // expressions in annotation values
1144
- return Object.assign({ '=': node.$tokenTexts }, expression( node ));
1145
- return value(node);
1138
+ return annoValue( node );
1146
1139
  }
1147
1140
 
1148
1141
  function docComment( doc ) {
@@ -1152,6 +1145,16 @@ function docComment( doc ) {
1152
1145
  return undefined;
1153
1146
  }
1154
1147
 
1148
+ function annoValue( node ) {
1149
+ if (!node.$tokenTexts)
1150
+ return value( node );
1151
+ const csn = expression( node ); // expressions in annotation values
1152
+ return (!csn?.ref || gensrcFlavor || universalCsn ||
1153
+ csn.cast || csn.param || csn.ref.some( id => typeof id !== 'string' ))
1154
+ ? csn
1155
+ : Object.assign({ '=': csn.ref.join( '.' ) }, csn );
1156
+ }
1157
+
1155
1158
  function value( node ) {
1156
1159
  // "Short" value form, e.g. for annotation assignments
1157
1160
  if (!node)
@@ -1175,7 +1178,7 @@ function value( node ) {
1175
1178
  if (node.literal === 'enum')
1176
1179
  return enumValue( node );
1177
1180
  if (node.literal === 'array')
1178
- return node.val.map( value );
1181
+ return node.val.map( annoValue );
1179
1182
  if (node.literal === 'token' && node.val === '...')
1180
1183
  return extra( { '...': !node.upTo || value( node.upTo ) } );
1181
1184
  if (node.literal !== 'struct')
@@ -1183,7 +1186,7 @@ function value( node ) {
1183
1186
  return node.name && !('val' in node) || node.val;
1184
1187
  const r = Object.create( dictionaryPrototype );
1185
1188
  for (const prop in node.struct)
1186
- r[prop] = value( node.struct[prop] );
1189
+ r[prop] = annoValue( node.struct[prop] );
1187
1190
  return r;
1188
1191
  }
1189
1192
 
@@ -38,18 +38,6 @@ function isWhitespaceCharacterNoNewline( char ) {
38
38
  return whitespaceRegEx.test(char);
39
39
  }
40
40
 
41
- /**
42
- * Normalized CDL newlines to LF (\n). CDL newlines also contain PS and other
43
- * characters, see cdlNewLineRegEx.
44
- *
45
- * @param {string} str
46
- * @return {string}
47
- */
48
- function normalizeNewLine( str ) {
49
- // Note: cdlNewLineRegEx does not have `g`.
50
- return str.replace(new RegExp(cdlNewLineRegEx, 'ug'), '\n');
51
- }
52
-
53
41
  /**
54
42
  * Normalizes the given number (as a string):
55
43
  *
@@ -69,6 +57,5 @@ module.exports = {
69
57
  isWhitespaceOrNewLineOnly,
70
58
  isWhitespaceCharacterNoNewline,
71
59
  cdlNewLineRegEx,
72
- normalizeNewLine,
73
60
  normalizeNumberString,
74
61
  };
package/lib/main.d.ts CHANGED
@@ -32,6 +32,18 @@ declare namespace compiler {
32
32
  * during compilation otherwise.
33
33
  */
34
34
  severities?: { [messageId: string]: MessageSeverity}
35
+ /**
36
+ * Downgrade the errors raised for `annotate` statements containing a
37
+ * security-relevant annotation (`@restrict`, `@requires`, `@ams`) but
38
+ * whose target does not exist. Intended as a long-lived migration
39
+ * switch for projects that cannot fix all such statements at once.
40
+ *
41
+ * Explicit per-id entries in `severities` still take precedence over
42
+ * this option.
43
+ *
44
+ * @default false
45
+ */
46
+ noErrorForUnknownAnnotateTarget?: boolean
35
47
  /**
36
48
  * Dictionary of beta flag names. This option allows fine-grained control
37
49
  * over which beta features should be enabled.
@@ -72,20 +84,6 @@ declare namespace compiler {
72
84
  * @default 'client'
73
85
  */
74
86
  csnFlavor?: string | 'client' | 'gensrc' | 'universal'
75
- /**
76
- * If set to false, backends will create localized convenience views for those views,
77
- * that only have an association to a localized entity/view. If set to true, views will
78
- * only get a convenience view, if they themselves contain localized elements (i.e. either
79
- * have simple projection on localized elements and CDL-casts to a localized element).
80
- *
81
- * If true, the OData backend will not set `$localized: true` markers for such cases.
82
- *
83
- * Does not work for backends to.hdi(), to.hdbcds() or to.sql() with `sqlDialect: 'hana'`,
84
- * since in all those dialects, associations still exist in generated artifacts.
85
- *
86
- * @default true
87
- */
88
- fewerLocalizedViews?: boolean
89
87
  }
90
88
 
91
89
  /**
@@ -464,6 +462,26 @@ declare namespace compiler {
464
462
  * @default true (since v5)
465
463
  */
466
464
  betterSqliteSessionVariables?: boolean
465
+ /**
466
+ * @deprecated Use decimal_affinity instead. Will be removed in v7.
467
+ * Controls SQLite type affinity for decimal types (old boolean version).
468
+ * - true: Use REAL_DECIMAL types (real affinity)
469
+ * - false: Use DECIMAL types (numeric affinity)
470
+ *
471
+ * @since Before v3
472
+ */
473
+ sqliteRealAffinityForDecimal?: boolean
474
+ /**
475
+ * Controls SQLite type affinity for decimal types.
476
+ * - 'numeric': Use DECIMAL types (numeric affinity)
477
+ * - 'real': Use REAL_DECIMAL types (real affinity)
478
+ *
479
+ * Only affects SQLite dialect. Values are case-sensitive (must be lowercase).
480
+ *
481
+ * @default 'real'
482
+ * @since v6.9.0
483
+ */
484
+ decimal_affinity?: 'numeric' | 'real'
467
485
  /**
468
486
  * If set, operator `!=` will be treated as a boolean-logic operator,
469
487
  * instead of a three-valued operator, by rendering it as `IS DISTINCT FROM`
@@ -503,38 +521,6 @@ declare namespace compiler {
503
521
  dollarNowAsTimestamp?: boolean
504
522
  }
505
523
 
506
- /**
507
- * Options used by to.hdbcds()
508
- */
509
- export interface HdbcdsOptions extends Options {
510
- /**
511
- * The SQL naming mode decides how names are represented.
512
- * Among others, this includes whether identifiers are quoted or not.
513
- *
514
- * - `plain`:
515
- * In this naming mode, dots are replaced by underscores.
516
- * Identifiers are always uppercased. If "smart-quoting" is used, they are also quoted
517
- if they are also HDBCDS keywords.
518
- * - `quoted`:
519
- * In this mode, all identifiers are quoted. Dots are not replaced in table
520
- * and view names but are still replaced by underscores in element names.
521
- * Identifier casing is kept as specified in the source.
522
- * - `hdbcds`:
523
- * This mode uses names that are compatible to SAP HANA CDS.
524
- * In this mode, all identifiers are quoted. Dots are neither replaced in table
525
- * nor element names: Structured elements persist, contexts are nested.
526
- * Managed associations/compositions are left as-is. No association-to-join-translation
527
- * is done.
528
- *
529
- * @default 'plain'
530
- */
531
- sqlMapping?: string | 'plain' | 'quoted' | 'hdbcds'
532
- /**
533
- * For to.hdbcds(), the SQL dialect is always set to 'hana'.
534
- */
535
- sqlDialect?: 'hana'
536
- }
537
-
538
524
  /**
539
525
  * Options used by to.hdi() and to.hdi.migration()
540
526
  */
@@ -1306,21 +1292,10 @@ declare namespace compiler {
1306
1292
  }
1307
1293
 
1308
1294
  /**
1309
- * Renders the given CSN into HDBCDS artifacts.
1310
- *
1311
- * DEPRECATED! This backend is deprecated and will be removed with cds-compiler v7!
1312
- *
1313
- * @deprecated
1314
- * @returns A map of `{ '<artifactName>.hdbcds|hdbconstraint>': '<content>' }` entries.
1295
+ * @deprecated to.hdbcds() has been removed. Use to.hdi() instead.
1315
1296
  */
1316
- function hdbcds(csn: CSN, options?: HdbcdsOptions): Record<string, string>;
1297
+ function hdbcds(csn: CSN, options?: Options): Record<string, string>;
1317
1298
  namespace hdbcds {
1318
- /**
1319
- * Immutable list of SAP HANA CDS keywords, used for smart quoting in
1320
- * {@link to.hdbcds} with option `sqlMapping: 'plain'`.
1321
- *
1322
- * @deprecated
1323
- */
1324
1299
  const keywords: string[];
1325
1300
  }
1326
1301
 
package/lib/main.js CHANGED
@@ -173,7 +173,7 @@ module.exports = {
173
173
  keywords: Object.freeze([ ...keywords.hana ] ),
174
174
  }),
175
175
  hdbcds: Object.assign((...args) => snapi.hdbcds(...args), {
176
- keywords: Object.freeze([ ...keywords.hdbcds ] ),
176
+ keywords: Object.freeze([ ...keywords.hdbcds ]),
177
177
  }),
178
178
  edm: Object.assign((...args) => snapi.edm(...args), {
179
179
  all: (...args) => snapi.edm.all(...args),
@@ -9,7 +9,7 @@ const {
9
9
  applyTransformationsOnDictionary,
10
10
  mergeTransformers,
11
11
  } = require('../transform/db/applyTransformations');
12
- const { isBuiltinType, isAnnotationExpression } = require('../base/builtins');
12
+ const { isBuiltinType, isAnnotationExpression, primaryExprProperties } = require('../base/builtins');
13
13
  const { ModelError, CompilerAssertion } = require('../base/error');
14
14
  const { typeParameters } = require('../compiler/builtins');
15
15
  const { forEach } = require('../utils/objectUtils');
@@ -1474,13 +1474,14 @@ function pathName( ref ) {
1474
1474
  */
1475
1475
  function findAnnotationExpression( node, prop ) {
1476
1476
  let isExpr = false;
1477
- if (prop[0] === '@') {
1478
- transformExpression(node, prop, {
1479
- '=': (p) => {
1480
- isExpr ||= isAnnotationExpression(p);
1481
- },
1482
- });
1483
- }
1477
+ const updateIsExpr = (p) => {
1478
+ isExpr ||= isAnnotationExpression(p);
1479
+ };
1480
+ const xprTransformers = Object.fromEntries(primaryExprProperties.map( p => [ p, updateIsExpr ]));
1481
+
1482
+ if (prop[0] === '@')
1483
+ transformExpression(node, prop, xprTransformers);
1484
+
1484
1485
  return isExpr;
1485
1486
  }
1486
1487