@sap/cds-compiler 6.7.3 → 6.9.0

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 (116) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +4 -0
  3. package/bin/cdsc.js +5 -5
  4. package/bin/cdshi.js +1 -0
  5. package/bin/cdsse.js +1 -1
  6. package/lib/api/main.js +17 -9
  7. package/lib/api/options.js +5 -2
  8. package/lib/api/validate.js +1 -1
  9. package/lib/base/builtins.js +13 -9
  10. package/lib/{model → base}/csnRefs.js +8 -10
  11. package/lib/base/error.js +2 -0
  12. package/lib/base/message-registry.js +68 -4
  13. package/lib/base/messages.js +4 -2
  14. package/lib/{optionProcessor.js → base/optionProcessor.js} +5 -3
  15. package/lib/base/{model.js → specialOptions.js} +16 -39
  16. package/lib/checks/arrayOfs.js +1 -1
  17. package/lib/checks/elements.js +1 -1
  18. package/lib/checks/enricher.js +2 -2
  19. package/lib/checks/featureFlags.js +54 -24
  20. package/lib/checks/foreignKeys.js +1 -1
  21. package/lib/checks/invalidTarget.js +1 -1
  22. package/lib/checks/managedInType.js +1 -1
  23. package/lib/checks/onConditions.js +1 -1
  24. package/lib/checks/queryNoDbArtifacts.js +1 -1
  25. package/lib/checks/validator.js +10 -14
  26. package/lib/compiler/assert-consistency.js +11 -9
  27. package/lib/compiler/base.js +5 -1
  28. package/lib/compiler/builtins.js +1 -1
  29. package/lib/compiler/checks.js +3 -3
  30. package/lib/compiler/define.js +6 -3
  31. package/lib/{base → compiler}/dictionaries.js +4 -3
  32. package/lib/compiler/extend.js +121 -21
  33. package/lib/compiler/generate.js +2 -2
  34. package/lib/compiler/index.js +11 -3
  35. package/lib/compiler/kick-start.js +1 -1
  36. package/lib/compiler/lsp-api.js +3 -3
  37. package/lib/compiler/populate.js +6 -7
  38. package/lib/compiler/resolve.js +53 -36
  39. package/lib/compiler/shared.js +68 -18
  40. package/lib/compiler/tweak-assocs.js +2 -2
  41. package/lib/compiler/utils.js +28 -27
  42. package/lib/compiler/xpr-rewrite.js +3 -3
  43. package/lib/edm/EdmPrimitiveTypeDefinitions.js +4 -1
  44. package/lib/edm/annotations/edmJson.js +2 -4
  45. package/lib/edm/annotations/genericTranslation.js +51 -7
  46. package/lib/edm/csn2edm.js +3 -2
  47. package/lib/edm/edmAnnoPreprocessor.js +1 -1
  48. package/lib/edm/edmInboundChecks.js +2 -1
  49. package/lib/edm/edmPreprocessor.js +3 -3
  50. package/lib/edm/edmUtils.js +2 -2
  51. package/lib/gen/BaseParser.js +59 -108
  52. package/lib/gen/CdlGrammar.checksum +1 -1
  53. package/lib/gen/CdlParser.js +2052 -1965
  54. package/lib/gen/Dictionary.json +67 -7
  55. package/lib/json/from-csn.js +14 -14
  56. package/lib/json/to-csn.js +77 -38
  57. package/lib/main.js +3 -3
  58. package/lib/model/csnUtils.js +2 -2
  59. package/lib/modelCompare/compare.js +1 -1
  60. package/lib/modelCompare/utils/filter.js +1 -0
  61. package/lib/parsers/AstBuildingParser.js +83 -33
  62. package/lib/parsers/index.js +1 -1
  63. package/lib/render/manageConstraints.js +1 -1
  64. package/lib/render/toCdl.js +49 -30
  65. package/lib/render/toHdbcds.js +2 -2
  66. package/lib/render/toSql.js +16 -7
  67. package/lib/render/utils/common.js +11 -3
  68. package/lib/render/utils/sql.js +14 -5
  69. package/lib/render/utils/standardDatabaseFunctions.js +108 -99
  70. package/lib/sql-identifier.js +9 -1
  71. package/lib/{model → tool-lib}/enrichCsn.js +3 -2
  72. package/lib/{model → tool-lib}/revealInternalProperties.js +2 -1
  73. package/lib/transform/addTenantFields.js +1 -1
  74. package/lib/transform/db/applyTransformations.js +1 -1
  75. package/lib/transform/db/assertUnique.js +1 -1
  76. package/lib/transform/db/assocsToQueries/transformExists.js +1 -1
  77. package/lib/transform/db/backlinks.js +2 -2
  78. package/lib/transform/db/expansion.js +2 -2
  79. package/lib/transform/db/flattening.js +3 -4
  80. package/lib/transform/db/killAnnotations.js +1 -0
  81. package/lib/transform/db/processSqlServices.js +2 -1
  82. package/lib/transform/db/rewriteCalculatedElements.js +2 -2
  83. package/lib/transform/db/temporal.js +30 -5
  84. package/lib/transform/db/views.js +16 -20
  85. package/lib/transform/draft/db.js +1 -2
  86. package/lib/transform/effective/associations.js +1 -1
  87. package/lib/transform/effective/flattening.js +6 -5
  88. package/lib/transform/effective/main.js +24 -4
  89. package/lib/transform/effective/types.js +1 -1
  90. package/lib/transform/{odata/fioriTreeViews.js → fioriTreeViews.js} +48 -25
  91. package/lib/transform/forOdata.js +25 -7
  92. package/lib/transform/forRelationalDB.js +48 -12
  93. package/lib/transform/localized.js +2 -2
  94. package/lib/transform/odata/createForeignKeys.js +1 -1
  95. package/lib/transform/odata/flattening.js +2 -2
  96. package/lib/transform/odata/toFinalBaseType.js +3 -2
  97. package/lib/transform/odata/typesExposure.js +3 -2
  98. package/lib/transform/transformUtils.js +2 -2
  99. package/lib/transform/translateAssocsToJoins.js +2 -1
  100. package/lib/transform/tupleExpansion.js +44 -4
  101. package/lib/transform/universalCsn/universalCsnEnricher.js +7 -3
  102. package/lib/transform/universalCsn/utils.js +1 -1
  103. package/lib/{base → utils}/lazyload.js +9 -0
  104. package/lib/{base → utils}/node-helpers.js +2 -0
  105. package/lib/utils/objectUtils.js +29 -6
  106. package/lib/{base → utils}/optionProcessorHelper.js +16 -6
  107. package/package.json +3 -40
  108. /package/lib/{model → base}/cloneCsn.js +0 -0
  109. /package/lib/{model/api.js → base/model-api.js} +0 -0
  110. /package/lib/{api → base}/trace.js +0 -0
  111. /package/lib/{model → base}/xprAsTree.js +0 -0
  112. /package/lib/{inspect → tool-lib}/index.js +0 -0
  113. /package/lib/{inspect → tool-lib}/inspectModelStatistics.js +0 -0
  114. /package/lib/{inspect → tool-lib}/inspectPropagation.js +0 -0
  115. /package/lib/{inspect → tool-lib}/inspectUtils.js +0 -0
  116. /package/lib/{base → utils}/shuffle.js +0 -0
@@ -3,11 +3,12 @@
3
3
  const BaseParser = require( '../gen/BaseParser' );
4
4
 
5
5
  const { Location } = require( '../base/location' );
6
- const { dictAdd, dictAddArray } = require('../base/dictionaries');
6
+ const { dictAdd, dictAddArray } = require('../compiler/dictionaries');
7
7
  const { functionsWithoutParentheses } = require('./identifiers');
8
8
 
9
9
  const { pathName } = require('../compiler/utils');
10
10
  const { quotedLiteralPatterns, specialFunctions } = require('../compiler/builtins');
11
+ const { primaryExprProperties } = require('../base/builtins');
11
12
 
12
13
  const { parseMultiLineStringLiteral } = require('../language/multiLineStringParser');
13
14
  const { normalizeNewLine, normalizeNumberString } = require('../language/textUtils');
@@ -39,7 +40,7 @@ const valueTokens = {
39
40
  true: true,
40
41
  };
41
42
  const valueTokensLength = Object.values( valueTokens ).length;
42
- // likewise, if expectedArray contains all the following tokens, replace them by 'Literal'
43
+ // if expectedArray contains all the following tokens, replace them by 'Literal'
43
44
  const literalTokens = {
44
45
  Number: true,
45
46
  QuotedLiteral: true,
@@ -51,6 +52,29 @@ const literalTokens = {
51
52
  };
52
53
  const literalTokensLength = Object.values( literalTokens ).length;
53
54
 
55
+ // if expectedArray contains all the following tokens, replace them by 'Comparison'
56
+ const comparisonTokens = {
57
+ '=': true,
58
+ '<>': true,
59
+ '>': true,
60
+ '>=': true,
61
+ '<': true,
62
+ '<=': true,
63
+ '!=': true,
64
+ '==': true,
65
+ };
66
+ const comparisonTokensLength = Object.values( comparisonTokens ).length;
67
+
68
+ // if expectedArray contains all the following tokens, replace them by 'Operator'
69
+ const operatorTokens = {
70
+ '+': true,
71
+ '-': true,
72
+ '*': true,
73
+ '/': true,
74
+ '||': true,
75
+ };
76
+ const operatorTokensLength = Object.values( operatorTokens ).length;
77
+
54
78
  const extensionDicts = {
55
79
  elements: true, enum: true, params: true, returns: true,
56
80
  };
@@ -77,11 +101,6 @@ const extensionsCode = {
77
101
  const PRECEDENCE_OF_EQUAL = 10;
78
102
 
79
103
  class AstBuildingParser extends BaseParser {
80
- leanConditions = {
81
- afterBrace: true,
82
- fail: true,
83
- };
84
-
85
104
  constructor( lexer, keywords, table, options, messageFunctions ) {
86
105
  super( lexer, keywords, table ); // lexer has file
87
106
  this.options = options;
@@ -127,6 +146,19 @@ class AstBuildingParser extends BaseParser {
127
146
  if (withoutLiteralTokens.length + literalTokensLength === array.length)
128
147
  array = [ 'Literal', ...withoutLiteralTokens ];
129
148
  }
149
+
150
+ const withoutComparisonTokens = raw ? array : array.filter(
151
+ tok => comparisonTokens[tok] !== true
152
+ );
153
+ if (withoutComparisonTokens.length + comparisonTokensLength === array.length)
154
+ array = [ 'Comparison', ...withoutComparisonTokens ];
155
+
156
+ const withoutOperatorTokens = raw ? array : array.filter(
157
+ tok => operatorTokens[tok] !== true
158
+ );
159
+ if (withoutOperatorTokens.length + operatorTokensLength === array.length)
160
+ array = [ 'Operator', ...withoutOperatorTokens ];
161
+
130
162
  return array.map( tok => this.antlrName( tok ) )
131
163
  .sort( (a, b) => (tokenPrecedence(a) < tokenPrecedence(b) ? -1 : 1) );
132
164
  }
@@ -165,7 +197,7 @@ class AstBuildingParser extends BaseParser {
165
197
  queryOnLeftSloppyAlias( _arg, mode ) {
166
198
  if (mode === 'M' || this.isNoKeywordInRuleFollow( _arg, mode ))
167
199
  return true;
168
- // TODO TOOL: have a base parser method for the test
200
+ // TODO TOOL: have a base parser method for the test - or extra mode (recovery)
169
201
  if (this.conditionTokenIdx === this.tokenIdx && // tested on same
170
202
  this.conditionStackLength == null) // after error recovery
171
203
  return false;
@@ -202,7 +234,7 @@ class AstBuildingParser extends BaseParser {
202
234
  }
203
235
 
204
236
  prepareSpecialFunction() {
205
- const func = this.tokens[this.tokenIdx - 2].keyword?.toUpperCase();
237
+ const func = this.lb(2).keyword?.toUpperCase();
206
238
  // TODO: use lower-case in specialFunctions
207
239
  const spec = specialFunctions[func];
208
240
  this.dynamic_.call = { func, argPos: 0 };
@@ -225,7 +257,7 @@ class AstBuildingParser extends BaseParser {
225
257
  if (generic !== 'expr')
226
258
  return (generic === 'intro') ? 'GenericIntro' : type;
227
259
  // if both intro and expr: specialFunctions[fn][argPos][token] = 'expr'
228
- const next = this.tokens[this.tokenIdx + 1];
260
+ const next = this.lb(-1);
229
261
  if (next && next.type !== ',' && next.type !== ')' &&
230
262
  this.dynamic_.generic[next.keyword?.toUpperCase?.()] !== 'separator')
231
263
  return 'GenericIntro';
@@ -273,8 +305,7 @@ class AstBuildingParser extends BaseParser {
273
305
  this.dynamic_.inSelectItem ??= [];
274
306
  this.dynamic_.inSelectItem = [ ...this.dynamic_.inSelectItem ];
275
307
  }
276
- this.dynamic_.inSelectItem.push(arg ||
277
- (this.tokens[this.tokenIdx - 2].type === '.' ? 'inline' : 'expand'));
308
+ this.dynamic_.inSelectItem.push(arg || (this.lb(2).type === '.' ? 'inline' : 'expand'));
278
309
  }
279
310
 
280
311
  /**
@@ -285,7 +316,7 @@ class AstBuildingParser extends BaseParser {
285
316
  modifierRestriction() {
286
317
  const top = this.dynamic_.inSelectItem?.at(-1);
287
318
  const insideExpand = this.dynamic_.inSelectItem?.includes('expand');
288
- const next = this.tokens[this.tokenIdx]?.keyword;
319
+ const next = this.la()?.keyword;
289
320
  return insideExpand || (next !== 'key' && top === 'inline');
290
321
  }
291
322
  modifierRestrictionError( args, offending ) {
@@ -304,7 +335,7 @@ class AstBuildingParser extends BaseParser {
304
335
  return false;
305
336
  // TODO: it would be best to set this.dynamic_.inSelectItem to null in filters
306
337
  // (as <prepare>)
307
- const next = this.tokens[this.tokenIdx + 1]?.type;
338
+ const next = this.lb(-1)?.type;
308
339
  return next === '*' || next === '{';
309
340
  }
310
341
 
@@ -321,21 +352,22 @@ class AstBuildingParser extends BaseParser {
321
352
  // <prec=10, postfix=once> + test that the next token is not `null`; TODO: code
322
353
  // completion for `… default 3 not ~;` → currently just `null` but hey
323
354
  isNegatedRelation( prec ) {
324
- return this.tokens[this.tokenIdx + 1]?.keyword === 'null' ||
355
+ return this.lb(-1)?.keyword === 'null' ||
325
356
  this.precNone_( prec );
326
357
  }
327
358
 
328
359
  // TODO: as leanCondition ? `order` should probably appear in the message for
329
360
  // test3/Compiler/GrammarRobustness/InvalidSelectInWhere.err.cds
330
361
  orderByLimitRestriction( _arg, mode ) {
331
- if (mode && (!this.$allowOrderByLimit || this.precPost_( mode, 0 )))
362
+ if (mode &&
363
+ (!this.$allowOrderByLimit && mode !== 'M' || this.precPost_( mode, 0 )))
332
364
  return true;
333
365
  this.$allowOrderByLimit = !mode;
334
366
  return false;
335
367
  }
336
368
 
337
369
  isNamedArg() {
338
- const type = this.tokens[this.tokenIdx + 1]?.type;
370
+ const type = this.lb(-1)?.type;
339
371
  return type !== ':' && type !== '=>';
340
372
  }
341
373
 
@@ -485,13 +517,13 @@ class AstBuildingParser extends BaseParser {
485
517
  }
486
518
 
487
519
  noRepeatedCardinality( _arg, mode ) {
488
- if (this.tokens[this.tokenIdx - 2]?.type !== ']')
520
+ if (this.lb(2)?.type !== ']')
489
521
  return false;
490
522
  if (mode === 'M')
491
523
  return true;
492
524
  // currently just warning if same cardinality provided twice
493
525
  const same = { one: '1', many: '*' }[this.la().keyword];
494
- return this.tokens[this.tokenIdx - 3]?.text !== same;
526
+ return this.lb(3)?.text !== same;
495
527
  }
496
528
  noRepeatedCardinalityError( args ) {
497
529
  let openIdx = this.tokenIdx - 2;
@@ -569,7 +601,7 @@ class AstBuildingParser extends BaseParser {
569
601
  const { arrayAnno } = this.dynamic_;
570
602
  if (!arrayAnno[0])
571
603
  return arrayAnno[0] == null ? 'duplicate' : arg;
572
- arrayAnno[0] = this.tokens[this.tokenIdx + 1]?.keyword;
604
+ arrayAnno[0] = this.lb(-1)?.keyword;
573
605
  }
574
606
  else if (arg === 'bracket') { // syntax-invalid-ellipsis
575
607
  // closing bracket not allowed if last `...` in array is with `up to
@@ -591,7 +623,7 @@ class AstBuildingParser extends BaseParser {
591
623
  }
592
624
 
593
625
  beforeColon() {
594
- return this.tokens[this.tokenIdx + 1]?.text !== ':';
626
+ return this.lb(-1)?.text !== ':';
595
627
  }
596
628
 
597
629
  fail( _arg, mode ) {
@@ -615,13 +647,13 @@ class AstBuildingParser extends BaseParser {
615
647
  return;
616
648
  // assumes no value < -1:
617
649
  const location = (tokenAhead > 0)
618
- ? this.combineLocation( this.la(), this.tokens[this.tokenIdx + tokenAhead] )
619
- : this.tokens[this.tokenIdx + tokenAhead].location;
650
+ ? this.combineLocation( this.la(), this.lb(-tokenAhead) )
651
+ : this.lb(-tokenAhead).location;
620
652
  this.error( msgId, location, textArgs );
621
653
  }
622
654
 
623
655
  warnIfColonFollows( name ) {
624
- if (this.l() === ':') {
656
+ if (this.la().type === ':') {
625
657
  this.warning( 'syntax-missing-parens', name,
626
658
  { code: '@‹anno›', op: ':', newcode: '@(‹anno›…)' },
627
659
  // eslint-disable-next-line @stylistic/max-len
@@ -643,7 +675,7 @@ class AstBuildingParser extends BaseParser {
643
675
 
644
676
  reportDubiousAnnoSpacing() {
645
677
  const at = this.lb();
646
- const before = this.tokens[this.tokenIdx - 2];
678
+ const before = this.lb(2);
647
679
  if (before?.type === 'Id' && before.location.endLine === at.location.line &&
648
680
  before.location.endCol === at.location.col) {
649
681
  this.warning( 'syntax-expecting-anno-space', at.location, { code: '@' },
@@ -851,7 +883,7 @@ class AstBuildingParser extends BaseParser {
851
883
  classifyImplicitName( category, ref ) {
852
884
  if (!ref || ref.path) { // TODO: func
853
885
  const tokenIndex = ref?.path.at(-1)?.location.tokenIndex;
854
- const token = this.prevTokenWithIndex( tokenIndex ) ?? this.tokens[this.tokenIdx - 1];
886
+ const token = this.prevTokenWithIndex( tokenIndex ) ?? this.lb();
855
887
  const { parsedAs } = token;
856
888
  if (parsedAs && parsedAs !== 'token' && parsedAs !== 'keyword')
857
889
  token.parsedAs = category;
@@ -1060,7 +1092,7 @@ class AstBuildingParser extends BaseParser {
1060
1092
 
1061
1093
  // TODO: can we remove `;`/EOF from the expected-set for `annotate Foo with ⎀`?
1062
1094
  checkWith( keyword ) {
1063
- if (this.lb() !== keyword || ![ ';', '}', 'EOF' ].includes( this.l() ))
1095
+ if (this.lb() !== keyword || ![ ';', '}', 'EOF' ].includes( this.la().type ))
1064
1096
  return;
1065
1097
  const tok = this.la();
1066
1098
  const docTokenIndex = this.docCommentIndex &&
@@ -1115,15 +1147,15 @@ class AstBuildingParser extends BaseParser {
1115
1147
  }
1116
1148
 
1117
1149
  // see also <guard=nestedExpand>
1118
- reportExpandInline( column, isInline ) {
1119
- // called before matching `{`
1150
+ reportExpandInline( column, sqlStyle, isInline ) {
1151
+ // called before matching `{` (or `*` with inline)
1120
1152
  if (column.value && !column.value.path) {
1121
1153
  // improve error location when using "inline" `.{…}` after ref (arguments and
1122
1154
  // filters not covered, not worth the effort); after an expression where
1123
1155
  // the last token is an identifier, not the `.` is wrong, but the `{`:
1124
- const token = (isInline && this.tokens[this.tokenIdx - 2].type !== 'Id')
1125
- ? this.lb()
1126
- : this.la();
1156
+ const token = (isInline && this.lb(2)?.type !== 'Id')
1157
+ ? this.lb() // e.g. with `func() .{` → the `.` is wrong
1158
+ : this.la(); // after `current_date` → the `{` is wrong
1127
1159
  this.error( 'syntax-unexpected-nested-proj', token,
1128
1160
  { code: isInline ? '.{ ‹inline› }' : '{ ‹expand› }' },
1129
1161
  'Unexpected $(CODE); nested projections can only be used after a reference' );
@@ -1132,6 +1164,15 @@ class AstBuildingParser extends BaseParser {
1132
1164
  // - no errors for refs inside expand/inline, but for refs in sibling expr
1133
1165
  // - think about: reference to these (sub) elements from other view
1134
1166
  }
1167
+ else if (sqlStyle && this.la().text === '{') {
1168
+ this.message( 'syntax-invalid-nested-proj', this.la(),
1169
+ {
1170
+ code: isInline ? '.{ ‹inline› }' : '{ ‹expand› }',
1171
+ keyword: 'select from … { … }',
1172
+ },
1173
+ // eslint-disable-next-line @stylistic/max-len
1174
+ 'Unexpected $(CODE); nested projections can only be used within a CDL-style $(KEYWORD)' );
1175
+ }
1135
1176
  }
1136
1177
 
1137
1178
  reportDuplicateClause( prop, erroneous, chosen, code ) {
@@ -1161,6 +1202,15 @@ class AstBuildingParser extends BaseParser {
1161
1202
  }
1162
1203
  }
1163
1204
 
1205
+ checkStructProps( dict ) {
1206
+ const special = primaryExprProperties.find( prop => dict[prop] !== undefined );
1207
+ if (special || dict['=']) {
1208
+ const prop = special || '=';
1209
+ this.message( 'syntax-invalid-anno-struct', dict[prop].name.location,
1210
+ { '#': (special ? 'std' : 'ref'), prop } );
1211
+ }
1212
+ }
1213
+
1164
1214
  // TODO: remove the check from the parser; move it to shared.js
1165
1215
  checkTypeArgs( art ) {
1166
1216
  const args = art.$typeArgs;
@@ -1175,7 +1225,7 @@ class AstBuildingParser extends BaseParser {
1175
1225
  locationOfPrevTokens( offset ) {
1176
1226
  // TODO: use combined location of lb() and la() and move actions accordingly
1177
1227
  // (for error recovery)
1178
- const { file, line, col } = this.tokens[this.tokenIdx - offset].location;
1228
+ const { file, line, col } = this.lb( offset ).location;
1179
1229
  const { endLine, endCol } = this.lb().location;
1180
1230
  return {
1181
1231
  file,
@@ -2,7 +2,7 @@
2
2
 
3
3
  'use strict';
4
4
 
5
- const lazyload = require('../base/lazyload')( module );
5
+ const lazyload = require('../utils/lazyload')( module );
6
6
  const { CompilerAssertion } = require( '../base/error' );
7
7
  const { createMessageFunctions } = require( '../base/messages' );
8
8
  const { XsnSource } = require('../compiler/xsn-model');
@@ -11,7 +11,7 @@ const { transformForRelationalDBWithCsn } = require('../transform/forRelationalD
11
11
  const {
12
12
  renderReferentialConstraint, getIdentifierUtils,
13
13
  } = require('./utils/sql');
14
- const { sortCsn } = require('../model/cloneCsn');
14
+ const { sortCsn } = require('../base/cloneCsn');
15
15
 
16
16
  /**
17
17
  * Used only by `cdsc manageConstraints`.
@@ -20,14 +20,14 @@ const { escapeString, hasUnpairedUnicodeSurrogate } = require('./utils/stringEsc
20
20
  const { checkCSNVersion } = require('../json/csnVersion');
21
21
  const { normalizeTypeRef, forEachDefinition } = require('../model/csnUtils');
22
22
  const enrichUniversalCsn = require('../transform/universalCsn/universalCsnEnricher');
23
- const { isBetaEnabled } = require('../base/model');
23
+ const { isBetaEnabled } = require('../base/specialOptions');
24
24
  const { ModelError, CompilerAssertion } = require('../base/error');
25
25
  const { typeParameters, specialFunctions } = require('../compiler/builtins');
26
26
  const { isAnnotationExpression } = require('../base/builtins');
27
27
  const { forEach } = require('../utils/objectUtils');
28
28
  const { isBuiltinType } = require('../base/builtins');
29
- const { cloneFullCsn } = require('../model/cloneCsn');
30
- const { getKeysDict, implicitAs } = require('../model/csnRefs');
29
+ const { cloneFullCsn } = require('../base/cloneCsn');
30
+ const { getKeysDict, implicitAs } = require('../base/csnRefs');
31
31
  const { undelimitedIdentifierRegex } = require('../parsers/identifiers');
32
32
  const { getNormalizedQuery } = require('../model/csnUtils');
33
33
  const {
@@ -675,41 +675,60 @@ class CsnToCdl {
675
675
 
676
676
  // If there is nothing to extend, e.g. only annotations, don't render an
677
677
  // empty element list. This would end up in diffs with parseCdl CSN.
678
- if (!ext.elements && !ext.columns && !ext.actions && !ext.enum) {
678
+ if (!ext.elements && !ext.columns && !ext.actions && !ext.enum &&
679
+ !ext.where && !ext.groupBy && !ext.having && !ext.orderBy && !ext.limit) {
679
680
  result += `${ env.indent }extend ${ extName };\n`;
680
681
  return result;
681
682
  }
682
683
 
683
- // We have the "old-style" prefix syntax and the "new-style" postfix "with <type>" syntax.
684
- // The former one can not only extend (sub-)elements but also actions in the same statement whereas
685
- // the latter cannot.
686
- // If there are actions, check if there are also elements/columns, and if so, use the prefix notation.
687
- const usePrefixNotation = ext.actions && (ext.columns || ext.elements);
688
- if (usePrefixNotation)
689
- result += `${ env.indent }extend ${ this.getExtendPrefixVariant(ext) } ${ extName } with {\n`;
690
- else
691
- result += `${ env.indent }extend ${ extName } with ${ this.getExtendPostfixVariant(ext) }{\n`;
684
+ if (ext.elements || ext.columns || ext.actions || ext.enum) {
685
+ // We have the "old-style" prefix syntax and the "new-style" postfix "with <type>" syntax.
686
+ // The former one can not only extend (sub-)elements but also actions in the same statement whereas
687
+ // the latter cannot.
688
+ // If there are actions, check if there are also elements/columns, and if so, use the prefix notation.
689
+ const usePrefixNotation = ext.actions && (ext.columns || ext.elements);
690
+ if (usePrefixNotation)
691
+ result += `${ env.indent }extend ${ this.getExtendPrefixVariant(ext) } ${ extName } with {\n`;
692
+ else
693
+ result += `${ env.indent }extend ${ extName } with ${ this.getExtendPostfixVariant(ext) }{\n`;
692
694
 
693
- if (ext.columns)
694
- result += this.renderViewColumns(ext, env.withIncreasedIndent());
695
+ if (ext.columns)
696
+ result += this.renderViewColumns(ext, env.withIncreasedIndent());
695
697
 
696
- else if (ext.elements || ext.enum)
697
- result += this.renderExtendStatementElements(ext, env);
698
+ else if (ext.elements || ext.enum)
699
+ result += this.renderExtendStatementElements(ext, env);
698
700
 
699
- // Not part of if/else cascade, because it may be in postfix notation.
700
- if (ext.actions) {
701
- const childEnv = env.withIncreasedIndent();
702
- let actions = '';
703
- forEach(ext.actions, (actionName, action) => {
704
- actions += this.renderActionOrFunction(actionName, action, childEnv.withSubPath([ 'actions', actionName ]), true);
705
- });
706
- if (!usePrefixNotation)
707
- result += actions;
708
- else if (actions !== '')
709
- result += `${ env.indent }} actions {\n${ actions }`;
701
+ // Not part of if/else cascade, because it may be in postfix notation.
702
+ if (ext.actions) {
703
+ const childEnv = env.withIncreasedIndent();
704
+ let actions = '';
705
+ forEach(ext.actions, (actionName, action) => {
706
+ actions += this.renderActionOrFunction(actionName, action, childEnv.withSubPath([ 'actions', actionName ]), true);
707
+ });
708
+ if (!usePrefixNotation)
709
+ result += actions;
710
+ else if (actions !== '')
711
+ result += `${ env.indent }} actions {\n${ actions }`;
712
+ }
713
+
714
+ result += `${ env.indent }}`;
715
+ }
716
+ else {
717
+ result += `${ env.indent }extend ${ extName } with`;
710
718
  }
711
719
 
712
- result += `${ env.indent }};\n`;
720
+ if (ext.where)
721
+ result += ` where ${ this.exprRenderer.renderExpr(ext.where, env.withSubPath([ 'where' ])) }`;
722
+ if (ext.groupBy)
723
+ result += ` group by ${ ext.groupBy.map((expr, i) => this.exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ') }`;
724
+ if (ext.having)
725
+ result += ` having ${ this.exprRenderer.renderExpr(ext.having, env.withSubPath([ 'having' ])) }`;
726
+ if (ext.orderBy)
727
+ result += ` order by ${ ext.orderBy.map((entry, i) => this.renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ') }`;
728
+ if (ext.limit)
729
+ result += ` ${ this.renderLimit(ext.limit, env.withSubPath([ 'limit' ])) }`;
730
+
731
+ result += ';\n';
713
732
  return result;
714
733
  }
715
734
 
@@ -1997,7 +2016,7 @@ class CsnToCdl {
1997
2016
  return `#${ annoValue['#'] }`;
1998
2017
  }
1999
2018
  // Shorthand for absolute path (as string)
2000
- else if (annoValue['='] !== undefined) {
2019
+ else if (typeof annoValue['='] === 'string') {
2001
2020
  if (annoValue['='].startsWith('@'))
2002
2021
  return this.quoteAnnotationPathIfRequired(annoValue['='], env);
2003
2022
  return this.quotePathIfRequired(annoValue['='], env);
@@ -18,13 +18,13 @@ const {
18
18
  renderReferentialConstraint,
19
19
  } = require('./utils/sql');
20
20
  const DuplicateChecker = require('./DuplicateChecker');
21
- const { isDeprecatedEnabled } = require('../base/model');
21
+ const { isDeprecatedEnabled } = require('../base/specialOptions');
22
22
  const { checkCSNVersion } = require('../json/csnVersion');
23
23
  const { timetrace } = require('../utils/timetrace');
24
24
 
25
25
  const { smartId, delimitedId } = require('../sql-identifier');
26
26
  const { ModelError, CompilerAssertion } = require('../base/error');
27
- const { pathId } = require('../model/csnRefs');
27
+ const { pathId } = require('../base/csnRefs');
28
28
 
29
29
  const $PROJECTION = '$projection';
30
30
  const $SELF = '$self';
@@ -24,15 +24,15 @@ const {
24
24
  const DuplicateChecker = require('./DuplicateChecker');
25
25
  const { checkCSNVersion } = require('../json/csnVersion');
26
26
  const { timetrace } = require('../utils/timetrace');
27
- const { isBetaEnabled, isDeprecatedEnabled } = require('../base/model');
27
+ const { isBetaEnabled, isDeprecatedEnabled } = require('../base/specialOptions');
28
28
  const sqlIdentifiers = require('../sql-identifier');
29
- const { sortCsn } = require('../model/cloneCsn');
29
+ const { sortCsn } = require('../base/cloneCsn');
30
30
  const { manageConstraints, manageConstraint } = require('./manageConstraints');
31
31
  const { renderUniqueConstraintString, renderUniqueConstraintDrop, renderUniqueConstraintAdd } = require('./utils/unique');
32
32
  const { ModelError, CompilerAssertion } = require('../base/error');
33
- const { pathId } = require('../model/csnRefs');
33
+ const { pathId } = require('../base/csnRefs');
34
34
  const { transformExprOperators } = require('./utils/operators');
35
- const { exprAsTree, condAsTree } = require('../model/xprAsTree');
35
+ const { exprAsTree, condAsTree } = require('../base/xprAsTree');
36
36
 
37
37
  class SqlRenderEnvironment {
38
38
  indent = '';
@@ -114,7 +114,7 @@ function toSqlDdl( csn, options, messageFunctions ) {
114
114
  const {
115
115
  error, warning, info, throwWithAnyError,
116
116
  } = messageFunctions;
117
- const { quoteSqlId, prepareIdentifier, renderArtifactName } = getIdentifierUtils(csn, options);
117
+ const { quoteSqlId, prepareIdentifier, renderArtifactName } = getIdentifierUtils(csn, options, warning);
118
118
  const reportedMissingReplacements = Object.create(null);
119
119
 
120
120
  const exprRenderer = createExpressionRenderer({
@@ -795,7 +795,7 @@ function toSqlDdl( csn, options, messageFunctions ) {
795
795
  return '';
796
796
  env = env.withSubPath([ 'elements', elementName ]);
797
797
  const isPostgresAlterColumn = env.alterMode && env.changeType === 'migration' && options.sqlDialect === 'postgres';
798
- const quotedElementName = quoteSqlId(elementName);
798
+ const quotedElementName = quoteSqlId(elementName, env.path);
799
799
  if (duplicateChecker)
800
800
  duplicateChecker.addElement(quotedElementName, elm.$location, elementName);
801
801
 
@@ -1422,6 +1422,15 @@ function toSqlDdl( csn, options, messageFunctions ) {
1422
1422
  if (isBuiltinType(elm.type)) {
1423
1423
  // cds.Integer => render as INTEGER (no quotes)
1424
1424
  result += renderBuiltinType(elm.type);
1425
+
1426
+ // On H2 & sqlite the underlying data type for a cds.Vector is a binary, needing a length.
1427
+ // The data is stored as a vector of 4 byte real numbers with dimensions given by length. Additional 4 bytes are used to store the dimensions.
1428
+ if (
1429
+ elm.type === 'cds.Vector' && elm.length &&
1430
+ (options.sqlDialect === 'h2' || options.sqlDialect === 'sqlite')
1431
+ )
1432
+ elm.length = elm.length * 4 + 4;
1433
+
1425
1434
  result += renderTypeParameters(elm);
1426
1435
  }
1427
1436
  else {
@@ -1451,7 +1460,7 @@ function toSqlDdl( csn, options, messageFunctions ) {
1451
1460
  * @returns {string} Rendered type
1452
1461
  */
1453
1462
  function renderBuiltinType( typeName ) {
1454
- const types = cdsToSqlTypes[options.sqlDialect];
1463
+ const types = cdsToSqlTypes[options.sqliteRealAffinityForDecimal && options.sqlDialect === 'sqlite' ? 'sqlitereal' : options.sqlDialect];
1455
1464
  const result = types?.[typeName] || cdsToSqlTypes.standard[typeName];
1456
1465
  if (!result && options.testMode)
1457
1466
  throw new CompilerAssertion(`Expected to find a type mapping for ${ typeName }`);
@@ -21,7 +21,7 @@ const {
21
21
  hasValidSkipOrExists, forEachDefinition, getNamespace, getUnderscoredName,
22
22
  } = require('../../model/csnUtils');
23
23
 
24
- const { implicitAs } = require('../../model/csnRefs');
24
+ const { implicitAs } = require('../../base/csnRefs');
25
25
 
26
26
  /**
27
27
  * Render the given function
@@ -294,7 +294,7 @@ const cdsToSqlTypes = {
294
294
  'cds.Binary': 'BINARY_BLOB',
295
295
  'cds.hana.BINARY': 'BINARY_BLOB',
296
296
  'cds.hana.SMALLDECIMAL': 'SMALLDECIMAL',
297
- 'cds.Vector': 'BINARY_BLOB', // Not supported; see #11725
297
+ 'cds.Vector': 'BLOB',
298
298
  'cds.Map': 'JSON_TEXT', // '_TEXT' suffix required for text affinity
299
299
  },
300
300
  plain: {
@@ -309,6 +309,7 @@ const cdsToSqlTypes = {
309
309
  'cds.DecimalFloat': 'DECFLOAT',
310
310
  'cds.DateTime': 'TIMESTAMP(0)',
311
311
  'cds.Timestamp': 'TIMESTAMP(7)',
312
+ 'cds.Vector': 'VARBINARY',
312
313
  'cds.Map': 'JSON',
313
314
  'cds.UInt8': 'SMALLINT', // See #13870; not equivalent, but smallint can hold >byte
314
315
  },
@@ -320,11 +321,18 @@ const cdsToSqlTypes = {
320
321
  'cds.Binary': 'BYTEA',
321
322
  'cds.Double': 'FLOAT8',
322
323
  'cds.UInt8': 'SMALLINT', // See #13870; not equivalent, but smallint can hold >byte
323
- 'cds.Vector': 'VARCHAR', // Not supported; see #11725
324
+ 'cds.Vector': 'VECTOR', // vector is the datatype for [pgvector](https://github.com/pgvector/pgvector?tab=readme-ov-file)
324
325
  'cds.Map': 'JSONB',
325
326
  },
326
327
  };
327
328
 
329
+ cdsToSqlTypes.sqlitereal = {
330
+ ...cdsToSqlTypes.sqlite,
331
+ 'cds.Decimal': 'REAL_DECIMAL',
332
+ 'cds.DecimalFloat': 'REAL_DECIMAL',
333
+ 'cds.hana.SMALLDECIMAL': 'REAL_SMALLDECIMAL',
334
+ };
335
+
328
336
  // Type mapping from cds type names to HDBCDS type names:
329
337
  // Only those types, that need mapping, are listed.
330
338
  const cdsToHdbcdsTypes = {
@@ -3,9 +3,9 @@
3
3
  'use strict';
4
4
 
5
5
  const { getResultingName } = require('../../model/csnUtils');
6
- const { smartId, delimitedId } = require('../../sql-identifier');
6
+ const { smartId, delimitedId, sqlDialects } = require('../../sql-identifier');
7
7
  const { ModelError } = require('../../base/error');
8
- const { setProp } = require('../../base/model');
8
+ const { setProp } = require('../../utils/objectUtils');
9
9
 
10
10
  /**
11
11
  * Render a given referential constraint as part of a SQL CREATE TABLE statement, or as .hdbconstraint artefact.
@@ -66,10 +66,12 @@ function renderReferentialConstraint( constraint, indent, toUpperCase, csn, opti
66
66
  /**
67
67
  * Get functions which can be used to prepare and quote SQL identifiers based on the options provided.
68
68
  *
69
+ * @param {CSN.Model} csn
69
70
  * @param {CSN.Options} options
71
+ * @param {function} warning
70
72
  * @returns quoteSqlId and prepareIdentifier function
71
73
  */
72
- function getIdentifierUtils( csn, options ) {
74
+ function getIdentifierUtils( csn, options, warning ) {
73
75
  return { quoteSqlId, prepareIdentifier, renderArtifactName };
74
76
  /**
75
77
  * Return 'name' with appropriate "-quotes.
@@ -81,10 +83,17 @@ function getIdentifierUtils( csn, options ) {
81
83
  * Complain about names that collide with known SQL keywords or functions
82
84
  *
83
85
  * @param {string} name Identifier to quote
86
+ * @param {CSN.Path} path Path to the identifier in the CSN model (for warnings)
84
87
  * @returns {string} Quoted identifier
85
88
  */
86
- function quoteSqlId( name ) {
89
+ function quoteSqlId( name, path ) {
87
90
  name = prepareIdentifier(name);
91
+ const s = sqlDialects[options.sqlDialect];
92
+ if ( warning && name.length > s.maxIdentifierLength ) {
93
+ warning( 'query-invalid-identifier',
94
+ path,
95
+ { name: options.sqlDialect, number: s.maxIdentifierLength });
96
+ }
88
97
 
89
98
  switch (options.sqlMapping) {
90
99
  case 'plain':
@@ -139,7 +148,7 @@ function getIdentifierUtils( csn, options ) {
139
148
  * @returns {string} Artifact name
140
149
  */
141
150
  function renderArtifactName( artifactName ) {
142
- return quoteSqlId(getResultingName(csn, options.sqlMapping, artifactName));
151
+ return quoteSqlId(getResultingName(csn, options.sqlMapping, artifactName), [ 'definitions', artifactName ] );
143
152
  }
144
153
  }
145
154