@sap/cds-compiler 2.12.0 → 2.13.6

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 (118) hide show
  1. package/CHANGELOG.md +110 -15
  2. package/bin/cdsc.js +13 -13
  3. package/bin/cdsse.js +2 -2
  4. package/doc/CHANGELOG_BETA.md +13 -6
  5. package/doc/CHANGELOG_DEPRECATED.md +22 -6
  6. package/doc/NameResolution.md +21 -16
  7. package/lib/api/main.js +28 -63
  8. package/lib/api/options.js +3 -3
  9. package/lib/api/validate.js +0 -5
  10. package/lib/backends.js +15 -23
  11. package/lib/base/dictionaries.js +0 -8
  12. package/lib/base/error.js +26 -0
  13. package/lib/base/keywords.js +7 -17
  14. package/lib/base/location.js +9 -4
  15. package/lib/base/message-registry.js +25 -4
  16. package/lib/base/messages.js +16 -26
  17. package/lib/base/model.js +2 -63
  18. package/lib/base/optionProcessorHelper.js +158 -123
  19. package/lib/checks/annotationsOData.js +1 -1
  20. package/lib/checks/cdsPersistence.js +2 -1
  21. package/lib/checks/enricher.js +17 -1
  22. package/lib/checks/invalidTarget.js +3 -1
  23. package/lib/checks/managedWithoutKeys.js +3 -1
  24. package/lib/checks/selectItems.js +4 -4
  25. package/lib/checks/sql-snippets.js +27 -26
  26. package/lib/checks/types.js +1 -1
  27. package/lib/checks/validator.js +4 -7
  28. package/lib/compiler/assert-consistency.js +5 -3
  29. package/lib/compiler/builtins.js +8 -6
  30. package/lib/compiler/checks.js +14 -3
  31. package/lib/compiler/cycle-detector.js +1 -1
  32. package/lib/compiler/define.js +1103 -0
  33. package/lib/compiler/extend.js +983 -0
  34. package/lib/compiler/finalize-parse-cdl.js +231 -0
  35. package/lib/compiler/index.js +32 -13
  36. package/lib/compiler/kick-start.js +190 -0
  37. package/lib/compiler/moduleLayers.js +4 -4
  38. package/lib/compiler/populate.js +1226 -0
  39. package/lib/compiler/propagator.js +111 -46
  40. package/lib/compiler/resolve.js +1433 -0
  41. package/lib/compiler/shared.js +64 -37
  42. package/lib/compiler/tweak-assocs.js +529 -0
  43. package/lib/compiler/utils.js +197 -33
  44. package/lib/edm/.eslintrc.json +5 -0
  45. package/lib/edm/annotations/genericTranslation.js +5 -9
  46. package/lib/edm/annotations/preprocessAnnotations.js +2 -2
  47. package/lib/edm/csn2edm.js +9 -8
  48. package/lib/edm/edm.js +11 -12
  49. package/lib/edm/edmPreprocessor.js +137 -73
  50. package/lib/edm/edmUtils.js +116 -22
  51. package/lib/gen/Dictionary.json +10 -3
  52. package/lib/gen/language.checksum +1 -1
  53. package/lib/gen/language.interp +9 -1
  54. package/lib/gen/language.tokens +86 -83
  55. package/lib/gen/languageLexer.interp +10 -1
  56. package/lib/gen/languageLexer.js +860 -833
  57. package/lib/gen/languageLexer.tokens +78 -75
  58. package/lib/gen/languageParser.js +5282 -4265
  59. package/lib/json/from-csn.js +12 -1
  60. package/lib/json/to-csn.js +126 -66
  61. package/lib/language/docCommentParser.js +2 -2
  62. package/lib/language/genericAntlrParser.js +76 -3
  63. package/lib/language/language.g4 +297 -130
  64. package/lib/language/multiLineStringParser.js +5 -5
  65. package/lib/main.d.ts +468 -59
  66. package/lib/main.js +35 -9
  67. package/lib/model/api.js +3 -1
  68. package/lib/model/csnRefs.js +225 -156
  69. package/lib/model/csnUtils.js +192 -223
  70. package/lib/model/enrichCsn.js +70 -29
  71. package/lib/model/revealInternalProperties.js +27 -6
  72. package/lib/model/sortViews.js +2 -1
  73. package/lib/modelCompare/compare.js +17 -12
  74. package/lib/optionProcessor.js +5 -4
  75. package/lib/render/manageConstraints.js +35 -32
  76. package/lib/render/toCdl.js +73 -288
  77. package/lib/render/toHdbcds.js +25 -23
  78. package/lib/render/toSql.js +98 -41
  79. package/lib/render/utils/common.js +5 -10
  80. package/lib/render/utils/sql.js +4 -3
  81. package/lib/render/utils/stringEscapes.js +111 -0
  82. package/lib/sql-identifier.js +1 -1
  83. package/lib/transform/.eslintrc.json +5 -0
  84. package/lib/transform/db/.eslintrc.json +2 -0
  85. package/lib/transform/db/applyTransformations.js +35 -12
  86. package/lib/transform/db/assertUnique.js +1 -1
  87. package/lib/transform/db/associations.js +103 -305
  88. package/lib/transform/db/cdsPersistence.js +2 -2
  89. package/lib/transform/db/constraints.js +55 -52
  90. package/lib/transform/db/expansion.js +46 -24
  91. package/lib/transform/db/flattening.js +553 -102
  92. package/lib/transform/db/groupByOrderBy.js +3 -1
  93. package/lib/transform/db/transformExists.js +59 -6
  94. package/lib/transform/db/views.js +5 -4
  95. package/lib/transform/draft/.eslintrc.json +38 -0
  96. package/lib/transform/{db/draft.js → draft/db.js} +6 -5
  97. package/lib/transform/draft/odata.js +227 -0
  98. package/lib/transform/forHanaNew.js +67 -183
  99. package/lib/transform/forOdataNew.js +17 -171
  100. package/lib/transform/localized.js +34 -19
  101. package/lib/transform/odata/generateForeignKeyElements.js +1 -1
  102. package/lib/transform/odata/referenceFlattener.js +95 -89
  103. package/lib/transform/odata/structureFlattener.js +1 -1
  104. package/lib/transform/odata/toFinalBaseType.js +86 -12
  105. package/lib/transform/odata/typesExposure.js +5 -5
  106. package/lib/transform/odata/utils.js +2 -2
  107. package/lib/transform/transformUtilsNew.js +36 -22
  108. package/lib/transform/translateAssocsToJoins.js +2 -19
  109. package/lib/transform/universalCsn/.eslintrc.json +36 -0
  110. package/lib/transform/universalCsn/coreComputed.js +170 -0
  111. package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
  112. package/lib/transform/universalCsn/utils.js +63 -0
  113. package/lib/utils/objectUtils.js +30 -0
  114. package/package.json +1 -1
  115. package/share/messages/README.md +26 -0
  116. package/lib/compiler/definer.js +0 -2361
  117. package/lib/compiler/resolver.js +0 -3079
  118. package/lib/transform/universalCsnEnricher.js +0 -237
@@ -74,39 +74,39 @@ function createOptionProcessor() {
74
74
  */
75
75
  function command(cmdString) {
76
76
  /** @type {object} */
77
- const command = {
77
+ const cmd = {
78
78
  options: {},
79
79
  positionalArguments: [],
80
- option,
80
+ option: commandOption,
81
81
  positionalArgument: (argumentDefinition) => {
82
- _setPositionalArguments(argumentDefinition, command.positionalArguments);
83
- return command;
82
+ _setPositionalArguments(argumentDefinition, cmd.positionalArguments);
83
+ return cmd;
84
84
  },
85
- help,
85
+ help: commandHelp,
86
86
  ..._parseCommandString(cmdString)
87
87
  };
88
- if (optionProcessor.commands[command.longName]) {
89
- throw new Error(`Duplicate assignment for long command ${command.longName}`);
88
+ if (optionProcessor.commands[cmd.longName]) {
89
+ throw new Error(`Duplicate assignment for long command ${cmd.longName}`);
90
90
  }
91
- optionProcessor.commands[command.longName] = command;
91
+ optionProcessor.commands[cmd.longName] = cmd;
92
92
 
93
- if (command.shortName) {
94
- if (optionProcessor.commands[command.shortName]) {
95
- throw new Error(`Duplicate assignment for short command ${command.shortName}`);
93
+ if (cmd.shortName) {
94
+ if (optionProcessor.commands[cmd.shortName]) {
95
+ throw new Error(`Duplicate assignment for short command ${cmd.shortName}`);
96
96
  }
97
- optionProcessor.commands[command.shortName] = command;
97
+ optionProcessor.commands[cmd.shortName] = cmd;
98
98
  }
99
- return command;
99
+ return cmd;
100
100
 
101
101
  // Command API: Define a command option
102
- function option(optString, validValues, options) {
103
- return _addOption(command, optString, validValues, options);
102
+ function commandOption(optString, validValues, options) {
103
+ return _addOption(cmd, optString, validValues, options);
104
104
  }
105
105
 
106
106
  // Command API: Define the command help text
107
- function help(text) {
108
- command.helpText = text;
109
- return command;
107
+ function commandHelp(text) {
108
+ cmd.helpText = text;
109
+ return cmd;
110
110
  }
111
111
  }
112
112
 
@@ -156,33 +156,33 @@ function createOptionProcessor() {
156
156
  * @private
157
157
  * @see option()
158
158
  */
159
- function _addOption(command, optString, validValues, options) {
159
+ function _addOption(cmd, optString, validValues, options) {
160
160
  const opt = _parseOptionString(optString, validValues);
161
161
  Object.assign(opt, options);
162
162
 
163
- if (command.options[opt.longName]) {
163
+ if (cmd.options[opt.longName]) {
164
164
  throw new Error(`Duplicate assignment for long option ${opt.longName}`);
165
165
  } else if (optionProcessor.options[opt.longName]) {
166
166
  // This path is only taken if optString is for commands
167
167
  optionProcessor.optionClashes.push({
168
168
  option: opt.longName,
169
- description: `Command '${command.longName}' has option clash with general options for: ${opt.longName}`
169
+ description: `Command '${cmd.longName}' has option clash with general options for: ${opt.longName}`
170
170
  });
171
171
  }
172
- command.options[opt.longName] = opt;
172
+ cmd.options[opt.longName] = opt;
173
173
  if (opt.shortName) {
174
- if (command.options[opt.shortName]) {
174
+ if (cmd.options[opt.shortName]) {
175
175
  throw new Error(`Duplicate assignment for short option ${opt.shortName}`);
176
176
  } else if (optionProcessor.options[opt.shortName]) {
177
177
  // This path is only taken if optString is for commands
178
178
  optionProcessor.optionClashes.push({
179
179
  option: opt.shortName,
180
- description: `Command '${command.longName}' has option clash with general options for: ${opt.shortName}`
180
+ description: `Command '${cmd.longName}' has option clash with general options for: ${opt.shortName}`
181
181
  });
182
182
  }
183
- command.options[opt.shortName] = opt;
183
+ cmd.options[opt.shortName] = opt;
184
184
  }
185
- return command;
185
+ return cmd;
186
186
  }
187
187
 
188
188
  // Internal: Parse one command string like "F, toFoo". Return an object like this
@@ -340,46 +340,7 @@ function createOptionProcessor() {
340
340
  seenDashDash = true;
341
341
  }
342
342
  else if (!seenDashDash && arg.startsWith('-')) {
343
- if (result.command) {
344
- // We already have a command
345
- const opt = optionProcessor.commands[result.command].options[arg];
346
- if (opt) {
347
- // Found as a command option
348
- i += processOption(i, opt, result.command);
349
- } else {
350
- // No command option, try general options as fallback
351
- const opt = optionProcessor.options[arg];
352
- if (opt) {
353
- i += processOption(i, opt, false);
354
- } else {
355
- // Not found at all, put into unknownOptions if it is an option
356
- // for another cdsc command.
357
- // We dig into the other cdsc commands in order to check if
358
- // the option expects a parameter and if so to take the next argument as a value
359
- if (Object.keys(optionProcessor.commands).some(cmd => optionProcessor.commands[cmd].options[arg])) {
360
- const cmd = optionProcessor.commands[
361
- Object.keys(optionProcessor.commands).find(cmd => optionProcessor.commands[cmd].options[arg])
362
- ];
363
- i += processOption(i, cmd.options[arg], optionProcessor.commands[result.command], true);
364
- } else { // still add it to the unknownOptions
365
- result.unknownOptions.push(`Unknown option "${arg}" for the command "${result.command}"`);
366
- // if the next argument looks like an argument for this unknown option => skip it
367
- if ((i + 1) < argv.length && !argv[i + 1].match('(^[.-])|[.](csn|cds|json)$'))
368
- i++;
369
- }
370
- }
371
- }
372
- } else {
373
- // We don't have a command
374
- const opt = optionProcessor.options[arg];
375
- if (opt) {
376
- // Found as a general option
377
- i += processOption(i, opt, false);
378
- } else {
379
- // Not found, complain
380
- result.unknownOptions.push(`Unknown option "${arg}"`);
381
- }
382
- }
343
+ i += processOption(i);
383
344
  }
384
345
  else {
385
346
  // Command or arg
@@ -407,7 +368,11 @@ function createOptionProcessor() {
407
368
  const missingArg = getCurrentPositionArguments().find((arg) => arg.required && !result.args[arg.name]);
408
369
  if (missingArg) {
409
370
  const forCommand = result.command ? ` for '${ result.command }'` : '';
410
- result.errors.push(`Missing positional argument${forCommand}: <${missingArg.name}${missingArg.isDynamic ? '...' : ''}>`)
371
+ const errorMsg = `Missing positional argument${forCommand}: <${missingArg.name}${missingArg.isDynamic ? '...' : ''}>`;
372
+ if (forCommand)
373
+ result.cmdErrors.push(errorMsg)
374
+ else
375
+ result.errors.push(errorMsg)
411
376
  }
412
377
 
413
378
  return result;
@@ -448,57 +413,127 @@ function createOptionProcessor() {
448
413
 
449
414
  // (Note that this works on 'argv' and 'result' from above).
450
415
  // Process 'argv[i]' as an option.
451
- // Check the option definition in 'opt' to see if a parameter is expected.
416
+ // Check the option definition to see if a parameter is expected.
452
417
  // If so, take it (complain if one is found in 'argv').
453
418
  // Populate 'result.options' with the result. Return the number params found (0 or 1).
454
- function processOption(i, opt, command, unknownOption = false) {
455
- // Does this option expect a parameter?
456
- if (opt.param) {
457
- if (i + 1 >= argv.length || argv[i + 1].startsWith('-')) {
458
- // There should be a param but isn't - complain
459
- let error = `Missing param "${opt.param}" for option "${opt.shortName ? opt.shortName + ', ': ''}${opt.longName}"`;
460
- if (command && unknownOption) {
461
- result.unknownOptions.push(`Unknown option "${argv[i]}" for the command "${command.longName}"`);
462
- } else if (command) {
463
- error = `${error} of command "${command}"`;
464
- result.cmdErrors.push(error);
465
- } else {
466
- result.errors.push(error);
419
+ function processOption(i) {
420
+ const arg = argv[i];
421
+ let currentCommand = result.command;
422
+
423
+ // First check top-level options
424
+ let currentOption = optionProcessor.options[arg];
425
+ if (currentCommand) {
426
+ // If there is a command and it has an option that overrides it, use it instead.
427
+ const cmdOpt = optionProcessor.commands[currentCommand].options[arg];
428
+ if (cmdOpt)
429
+ currentOption = cmdOpt;
430
+ else if (currentOption)
431
+ // Otherwise, if there exist a top-level option, set 'command' to null.
432
+ currentCommand = null;
433
+ }
434
+
435
+ if (!currentOption)
436
+ return reportUnknown();
437
+
438
+ if (!currentOption.param) {
439
+ setCurrentOption(true);
440
+ return 0;
441
+ }
442
+
443
+ const param = paramForOption(currentOption);
444
+ if (param === null)
445
+ return 0;
446
+ setCurrentOption(param);
447
+ return 1;
448
+
449
+ /**
450
+ * Report that an option is unknown. If the option exists for other
451
+ * commands or if the next argument looks like a param, return 1,
452
+ * otherwise 0, indicating how many argv fields have been consumed.
453
+ *
454
+ * @returns {number}
455
+ */
456
+ function reportUnknown() {
457
+ if (currentCommand)
458
+ result.unknownOptions.push(`Unknown option "${arg}" for the command "${currentCommand}"`);
459
+ else
460
+ result.unknownOptions.push(`Unknown option "${arg}"`);
461
+
462
+ if (currentCommand) {
463
+ // Not found at all. We dig into the other cdsc commands in order to check if
464
+ // the option expects a parameter and if so to take the next argument as a value
465
+ const otherCmd = Object.keys(optionProcessor.commands).find(cmd => optionProcessor.commands[cmd].options[arg]);
466
+ const otherCmdOpt = otherCmd && optionProcessor.commands[otherCmd].options[arg];
467
+ if (otherCmdOpt && hasParamForUnknown(otherCmdOpt)) {
468
+ return 1
467
469
  }
468
- return 0;
469
470
  }
470
- else {
471
- // Take the option with the parameter
472
- const value = argv[i + 1];
473
- const shortOption = opt.shortName ? `${opt.shortName}, ` : ''
474
- if (command) {
475
- // if an unknown option for a command => add it to the array and warn about
476
- if (unknownOption) {
477
- result.unknownOptions.push(`Unknown option "${argv[i]}" for the command "${command.longName}"`);
478
- } else {
479
- result.options[command][opt.camelName] = value;
480
- if (!isValidOptionValue(opt, value)) {
481
- result.cmdErrors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
482
- }
483
- }
484
- } else {
485
- result.options[opt.camelName] = value;
486
- if (!isValidOptionValue(opt, value)) {
487
- result.errors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
488
- }
489
- }
471
+
472
+ if (hasParamForUnknown(null))
490
473
  return 1;
474
+
475
+ return 0;
476
+ }
477
+
478
+ function setCurrentOption(val) {
479
+ if (currentCommand) {
480
+ if (!result.options[currentCommand])
481
+ result.options[currentCommand] = {};
482
+ result.options[currentCommand][currentOption.camelName] = val;
483
+ }
484
+ else {
485
+ result.options[currentOption.camelName] = val;
491
486
  }
492
487
  }
493
- // No parameter, take option as bool
494
- if (command) {
495
- unknownOption
496
- ? result.unknownOptions.push(`Unknown option "${argv[i]}" for the command "${command.longName}"`)
497
- : result.options[command][opt.camelName] = true;
498
- } else {
499
- result.options[opt.camelName] = true;
488
+
489
+ function reportMissingParam(opt) {
490
+ let error = `Missing param "${opt.param}" for option "${opt.shortName ? opt.shortName + ', ': ''}${opt.longName}"`;
491
+ if (currentCommand) {
492
+ error = `${error} of command "${currentCommand}"`;
493
+ result.cmdErrors.push(error);
494
+ } else {
495
+ result.errors.push(error);
496
+ }
497
+ }
498
+
499
+ function reportInvalidValue(opt, value) {
500
+ const shortOption = opt.shortName ? `${opt.shortName}, ` : ''
501
+ const errors = currentCommand ? result.cmdErrors : result.errors;
502
+ errors.push(`Invalid value "${value}" for option "${shortOption}${opt.longName}" - use one of [${opt.validValues}]`);
503
+ }
504
+
505
+ /**
506
+ * Get the value for the option's parameter. If the option does not require one,
507
+ * returns `null`. Reports missing parameters and invalid values.
508
+ *
509
+ * @returns {null|*}
510
+ */
511
+ function paramForOption(opt, reportMissing = true) {
512
+ if (i + 1 >= argv.length || argv[i + 1].startsWith('-')) {
513
+ if (reportMissing)
514
+ reportMissingParam(opt)
515
+ return null;
516
+ }
517
+
518
+ const value = argv[i + 1];
519
+ if (!isValidOptionValue(opt, value) && reportMissing) {
520
+ reportInvalidValue(opt, value);
521
+ }
522
+ return value;
523
+ }
524
+
525
+ /**
526
+ * Returns true if:
527
+ * - we didn't find an option (opt === null) _or_
528
+ * - we found an option and it requires a param
529
+ * _and_ if the next arg looks like an argument.
530
+ *
531
+ * @param {object|null} opt
532
+ * @returns {boolean}
533
+ */
534
+ function hasParamForUnknown(opt) {
535
+ return ((!opt || opt.param) && (i + 1) < argv.length && !argv[i + 1].match('(^[.-])|[.](csn|cdl|cds|json)$'));
500
536
  }
501
- return 0;
502
537
  }
503
538
  }
504
539
 
@@ -506,16 +541,16 @@ function createOptionProcessor() {
506
541
  // If 'command' is supplied, check only 'options.command', otherwise check
507
542
  // only top-level options
508
543
  // Return an array of complaints (possibly empty)
509
- function verifyOptions(options, command = undefined, silent = false) {
544
+ function verifyOptions(options, commandName = undefined, silent = false) {
510
545
  const result = [];
511
546
  let opts;
512
547
 
513
- if((options.betaMode || options.beta) && !options.testMode && !silent) {
548
+ if ((options.betaMode || options.beta) && !options.testMode && !silent) {
514
549
  const mode = options.beta ? 'beta' : 'beta-mode';
515
550
  result.push(`Option --${mode} was used. This option should not be used in productive scenarios!`)
516
551
  }
517
552
 
518
- if(options) {
553
+ if (options) {
519
554
  [
520
555
  'defaultBinaryLength', 'defaultStringLength',
521
556
  /*'length', 'precision', 'scale'*/
@@ -528,13 +563,13 @@ function createOptionProcessor() {
528
563
  });
529
564
  }
530
565
 
531
- if (command) {
532
- const cmd = optionProcessor.commands[command];
566
+ if (commandName) {
567
+ const cmd = optionProcessor.commands[commandName];
533
568
  if (!cmd) {
534
- throw new Error(`Expected existing command: "${command}"`);
569
+ throw new Error(`Expected existing command: "${cmd}"`);
535
570
  }
536
571
  opts = cmd.options;
537
- options = options[command] || {};
572
+ options = options[cmd] || {};
538
573
  if (typeof options === 'boolean') {
539
574
  // Special case: command without options
540
575
  options = {};
@@ -548,12 +583,12 @@ function createOptionProcessor() {
548
583
  let error;
549
584
  if (!opt) {
550
585
  // Don't report commands in top-level options
551
- if ((command || !optionProcessor.commands[camelName]) && !silent) {
552
- error = `Unknown option "${command ? command + '.' : ''}${camelName}"`;
586
+ if ((commandName || !optionProcessor.commands[camelName]) && !silent) {
587
+ error = `Unknown option "${commandName ? commandName + '.' : ''}${camelName}"`;
553
588
  }
554
589
  } else {
555
590
  const param = options[camelName];
556
- error = verifyOptionParam(param, opt, command ? command + '.' : '');
591
+ error = verifyOptionParam(param, opt, commandName ? commandName + '.' : '');
557
592
  }
558
593
  if (error) {
559
594
  result.push(error);
@@ -596,10 +631,10 @@ function createOptionProcessor() {
596
631
 
597
632
  // Return an array of unique camelNames of the options for the specified command
598
633
  // If invalid command -> an empty array
599
- function camelOptionsForCommand(command) {
600
- if (!command || !optionProcessor.commands[command])
634
+ function camelOptionsForCommand(cmd) {
635
+ if (!cmd || !optionProcessor.commands[cmd])
601
636
  return []
602
- const cmd = optionProcessor.commands[command];
637
+ cmd = optionProcessor.commands[cmd];
603
638
  const names = Object.keys(cmd.options).map(name => cmd.options[name].camelName);
604
639
  return [...new Set(names)];
605
640
  }
@@ -82,7 +82,7 @@ function checkAtSapAnnotations(node) {
82
82
  function checkReadOnlyAndInsertOnly(artifact, artifactName) {
83
83
  if (!this.csnUtils.getServiceName(artifactName))
84
84
  return;
85
- if (artifact.kind && [ 'entity', 'view' ].includes(artifact.kind) && artifact['@readonly'] && artifact['@insertonly'])
85
+ if (artifact.kind === 'entity' && artifact['@readonly'] && artifact['@insertonly'])
86
86
  this.warning(null, artifact.$path, 'Annotations “@readonly” and “@insertonly” can\'t be assigned in combination');
87
87
  }
88
88
 
@@ -13,7 +13,8 @@
13
13
  function validateCdsPersistenceAnnotation(artifact, artifactName, prop, path) {
14
14
  if (artifact.kind === 'entity') {
15
15
  // filter for 'table', 'udf', 'calcview' === true
16
- const TableUdfCv = Object.keys(artifact).filter(p => [ '@cds.persistence.table', '@cds.persistence.udf', '@cds.persistence.calcview' ].includes(p) && artifact[p]);
16
+ const persistenceAnnos = [ '@cds.persistence.table', '@cds.persistence.udf', '@cds.persistence.calcview' ];
17
+ const TableUdfCv = Object.keys(artifact).filter(p => persistenceAnnos.includes(p) && artifact[p]);
17
18
  if (TableUdfCv.length > 1)
18
19
  this.error(null, path, `Annotations ${ TableUdfCv.join(', ') } can't be used in combination`);
19
20
  }
@@ -30,6 +30,7 @@ function enrichCsn( csn ) {
30
30
  type: simpleRef,
31
31
  target: simpleRef,
32
32
  includes: simpleRef,
33
+ columns,
33
34
  // Annotations are ignored.
34
35
  '@': () => { /* ignore annotations */ },
35
36
  };
@@ -40,7 +41,7 @@ function enrichCsn( csn ) {
40
41
  cleanupCallbacks = [];
41
42
  };
42
43
 
43
- const { inspectRef, artifactRef } = csnRefs( csn );
44
+ const { inspectRef, artifactRef, getElement } = csnRefs( csn );
44
45
  const csnPath = [];
45
46
  if (csn.definitions)
46
47
  dictionary( csn, 'definitions', csn.definitions );
@@ -82,6 +83,21 @@ function enrichCsn( csn ) {
82
83
  csnPath.pop();
83
84
  }
84
85
 
86
+ // eslint-disable-next-line jsdoc/require-jsdoc
87
+ function columns( parent, prop, node ) {
88
+ // Establish the link relationships
89
+ parent[prop].forEach((column) => {
90
+ const element = getElement(column);
91
+ if (element) {
92
+ setProp(column, '_element', element);
93
+ cleanupCallbacks.push(() => delete column._element);
94
+ setProp(element, '_column', column);
95
+ cleanupCallbacks.push(() => delete element._column);
96
+ }
97
+ });
98
+ standard(parent, prop, node);
99
+ }
100
+
85
101
  // eslint-disable-next-line jsdoc/require-jsdoc
86
102
  function simpleRef( node, prop ) {
87
103
  setProp(node, '$path', [ ...csnPath ]);
@@ -2,6 +2,8 @@
2
2
 
3
3
  // Only to be used with validator.js - a correct this value needs to be provided!
4
4
 
5
+ const { ModelError } = require('../base/error');
6
+
5
7
  /**
6
8
  * Assert that targets of associations and compositions are entities.
7
9
  *
@@ -22,7 +24,7 @@ function invalidTarget(member) {
22
24
  if (mem.target) {
23
25
  const target = this.csn.definitions[mem.target];
24
26
  if (!target)
25
- throw new Error(`Expected target ${ mem.target }`);
27
+ throw new ModelError(`Expected target ${ mem.target }`);
26
28
  if (target.kind !== 'entity') {
27
29
  const isAssoc = this.csnUtils.getFinalBaseType(member.type) !== 'cds.Composition';
28
30
  this.error(
@@ -1,6 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
 
4
+ const { ModelError } = require('../base/error');
5
+
4
6
  /**
5
7
  * Trigger a recompilation in case of an association without .keys and without .on
6
8
  *
@@ -10,7 +12,7 @@
10
12
  */
11
13
  function managedWithoutKeys(member, memberName, prop) {
12
14
  if (prop === 'elements' && member.target && !member.keys && !member.on) { // trigger recompilation
13
- throw new Error('Expected association to have either an on-condition or foreign keys.');
15
+ throw new ModelError('Expected association to have either an on-condition or foreign keys.');
14
16
  }
15
17
  }
16
18
 
@@ -29,7 +29,7 @@ function validateSelectItems(query) {
29
29
  }
30
30
  });
31
31
  // .call() with 'this' to ensure we have access to the options
32
- rejectManagedAssociationsAndStructuresForHdbcsNames.call(this, SELECT, SELECT.$path);
32
+ rejectManagedAssociationsAndStructuresForHdbcdsNames.call(this, SELECT, SELECT.$path);
33
33
  }
34
34
 
35
35
 
@@ -41,10 +41,10 @@ function validateSelectItems(query) {
41
41
  * @param {CSN.Artifact} queryArtifact the query artifact which should be checked
42
42
  * @param {CSN.Path} artifactPath the path to that artifact
43
43
  */
44
- function rejectManagedAssociationsAndStructuresForHdbcsNames(queryArtifact, artifactPath) {
44
+ function rejectManagedAssociationsAndStructuresForHdbcdsNames(queryArtifact, artifactPath) {
45
45
  if (this.options.transformation === 'hdbcds' && this.options.sqlMapping === 'hdbcds') {
46
46
  forEachGeneric(queryArtifact, 'elements', (selectItem, elemName, prop, elementPath) => {
47
- if (this.csnUtils.isManagedAssociationElement(selectItem))
47
+ if (this.csnUtils.isManagedAssociation(selectItem))
48
48
  this.error('query-unexpected-assoc-hdbcds', elementPath);
49
49
  if (this.csnUtils.isStructured(selectItem))
50
50
  this.error('query-unexpected-structure-hdbcds', elementPath);
@@ -52,4 +52,4 @@ function rejectManagedAssociationsAndStructuresForHdbcsNames(queryArtifact, arti
52
52
  }
53
53
  }
54
54
 
55
- module.exports = { validateSelectItems, rejectManagedAssociationsAndStructuresForHdbcsNames };
55
+ module.exports = { validateSelectItems, rejectManagedAssociationsAndStructuresForHdbcdsNames };
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const { isBetaEnabled } = require('../base/model');
4
-
5
3
  // Only to be used with validator.js - a correct this value needs to be provided!
6
4
 
7
5
  /**
@@ -14,20 +12,18 @@ const { isBetaEnabled } = require('../base/model');
14
12
  * @returns {void}
15
13
  */
16
14
  function checkSqlAnnotationOnElement(member, memberName, prop, path) {
17
- if (isBetaEnabled(this.options, 'sqlSnippets')) {
18
- if (member['@sql.replace'])
19
- this.error(null, path, { anno: 'sql.replace' }, `Annotation $(ANNO) is reserved and must not be used`);
20
- if (member['@sql.prepend'])
21
- this.error('anno-invalid-sql-element', path, { anno: 'sql.prepend' }, `Annotation $(ANNO) can't be used on elements` );
15
+ if (member['@sql.replace'])
16
+ this.error(null, path, { anno: 'sql.replace' }, `Annotation $(ANNO) is reserved and must not be used`);
17
+ if (member['@sql.prepend'])
18
+ this.message('anno-invalid-sql-element', path, { anno: 'sql.prepend' }, `Annotation $(ANNO) can't be used on elements` );
22
19
 
23
- if (member['@sql.append']) {
24
- if (this.artifact.query)
25
- this.error('anno-invalid-sql-view-element', path, { anno: 'sql.append' }, `Annotation $(ANNO) can't be used on elements in views` );
26
- else if (this.csnUtils.isStructured(member))
27
- this.error('anno-invalid-sql-struct', path, { anno: 'sql.append' }, `Annotation $(ANNO) can't be used on structured elements` );
28
- else
29
- checkValidAnnoValue(member, '@sql.append', path, this.error, this.options);
30
- }
20
+ if (member['@sql.append']) {
21
+ if (this.artifact.query)
22
+ this.message('anno-invalid-sql-view-element', path, { anno: 'sql.append' }, `Annotation $(ANNO) can't be used on elements in views` );
23
+ else if (this.csnUtils.isStructured(member))
24
+ this.message('anno-invalid-sql-struct', path, { anno: 'sql.append' }, `Annotation $(ANNO) can't be used on structured elements` );
25
+ else
26
+ checkValidAnnoValue(member, '@sql.append', path, this.error, this.options);
31
27
  }
32
28
  }
33
29
 
@@ -54,19 +50,24 @@ function checkValidAnnoValue(carrier, annotation, path, error, options) {
54
50
  * @param {string} artifactName
55
51
  */
56
52
  function checkSqlAnnotationOnArtifact(artifact, artifactName) {
57
- if (isBetaEnabled(this.options, 'sqlSnippets')) {
58
- if (artifact['@sql.prepend']) {
59
- if (artifact.query)
60
- this.error('anno-invalid-sql-view', [ 'definitions', artifactName ], { name: '@sql.prepend' }, `Annotation $(NAME) can't be used on views` );
61
- else
62
- checkValidAnnoValue(artifact, '@sql.prepend', [ 'definitions', artifactName ], this.error, this.options);
63
- }
53
+ if (artifact.kind !== 'entity') {
54
+ if (artifact['@sql.prepend'])
55
+ this.message('anno-invalid-sql-kind', [ 'definitions', artifactName ], { name: '@sql.prepend', kind: artifact.kind }, `Annotation $(NAME) can't be used on an artifact of kind $(KIND)` );
56
+ if (artifact['@sql.append'])
57
+ this.message('anno-invalid-sql-kind', [ 'definitions', artifactName ], { name: '@sql.append', kind: artifact.kind }, `Annotation $(NAME) can't be used on an artifact of kind $(KIND)` );
58
+ }
59
+ else if (artifact['@sql.prepend']) {
60
+ if (artifact.query)
61
+ this.message('anno-invalid-sql-view', [ 'definitions', artifactName ], { name: '@sql.prepend' }, `Annotation $(NAME) can't be used on views` );
62
+ else
63
+ checkValidAnnoValue(artifact, '@sql.prepend', [ 'definitions', artifactName ], this.error, this.options);
64
+ }
64
65
 
65
- if (artifact['@sql.replace'])
66
- this.error(null, [ 'definitions', artifactName ], { anno: 'sql.replace' }, `Annotation $(ANNO) is reserved and must not be used`);
67
66
 
68
- checkValidAnnoValue(artifact, '@sql.append', [ 'definitions', artifactName ], this.error, this.options);
69
- }
67
+ if (artifact['@sql.replace'])
68
+ this.error(null, [ 'definitions', artifactName ], { anno: 'sql.replace' }, `Annotation $(ANNO) is reserved and must not be used`);
69
+
70
+ checkValidAnnoValue(artifact, '@sql.append', [ 'definitions', artifactName ], this.error, this.options);
70
71
  }
71
72
 
72
73
  // Anything that could terminate the "old" statement and start a new one basically.
@@ -19,7 +19,7 @@ function checkDecimalScale(member, memberName, prop, path) {
19
19
  // skip is already filtered in validator, here for completeness
20
20
  hasAnnotationValue(this.artifact, '@cds.persistence.skip'))
21
21
  return;
22
- if (member.scale && [ 'variable', 'floating' ].includes(member.scale))
22
+ if (member.scale && (member.scale === 'variable' || member.scale === 'floating'))
23
23
  this.error(null, path, { name: member.scale }, 'Unexpected scale $(NAME)');
24
24
  }
25
25
 
@@ -129,7 +129,7 @@ function _validate(csn, that,
129
129
  iterateOptions = {}) {
130
130
  const { cleanup } = enrich(csn);
131
131
 
132
- applyTransformations(csn, mergeCsnValidators(csnValidators, that), [], true, { drillRef: true });
132
+ applyTransformations(csn, mergeCsnValidators(csnValidators, that), [], { drillRef: true });
133
133
 
134
134
  forEachDefinition(csn, (artifact, artifactName, prop, path) => {
135
135
  artifactValidators.forEach((artifactValidator) => {
@@ -204,12 +204,9 @@ function forHana(csn, that) {
204
204
  ),
205
205
  forHanaQueryValidators.concat(commonQueryValidators),
206
206
  {
207
- skipArtifact: artifact => artifact.abstract || hasAnnotationValue(artifact, '@cds.persistence.skip'),
208
- skip: [
209
- 'action',
210
- 'function',
211
- 'event',
212
- ],
207
+ skipArtifact: artifact => artifact.abstract ||
208
+ hasAnnotationValue(artifact, '@cds.persistence.skip') ||
209
+ [ 'action', 'function', 'event' ].includes(artifact.kind),
213
210
  });
214
211
  }
215
212