@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.
- package/CHANGELOG.md +110 -15
- package/bin/cdsc.js +13 -13
- package/bin/cdsse.js +2 -2
- package/doc/CHANGELOG_BETA.md +13 -6
- package/doc/CHANGELOG_DEPRECATED.md +22 -6
- package/doc/NameResolution.md +21 -16
- package/lib/api/main.js +28 -63
- package/lib/api/options.js +3 -3
- package/lib/api/validate.js +0 -5
- package/lib/backends.js +15 -23
- package/lib/base/dictionaries.js +0 -8
- package/lib/base/error.js +26 -0
- package/lib/base/keywords.js +7 -17
- package/lib/base/location.js +9 -4
- package/lib/base/message-registry.js +25 -4
- package/lib/base/messages.js +16 -26
- package/lib/base/model.js +2 -63
- package/lib/base/optionProcessorHelper.js +158 -123
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +2 -1
- package/lib/checks/enricher.js +17 -1
- package/lib/checks/invalidTarget.js +3 -1
- package/lib/checks/managedWithoutKeys.js +3 -1
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +27 -26
- package/lib/checks/types.js +1 -1
- package/lib/checks/validator.js +4 -7
- package/lib/compiler/assert-consistency.js +5 -3
- package/lib/compiler/builtins.js +8 -6
- package/lib/compiler/checks.js +14 -3
- package/lib/compiler/cycle-detector.js +1 -1
- package/lib/compiler/define.js +1103 -0
- package/lib/compiler/extend.js +983 -0
- package/lib/compiler/finalize-parse-cdl.js +231 -0
- package/lib/compiler/index.js +32 -13
- package/lib/compiler/kick-start.js +190 -0
- package/lib/compiler/moduleLayers.js +4 -4
- package/lib/compiler/populate.js +1226 -0
- package/lib/compiler/propagator.js +111 -46
- package/lib/compiler/resolve.js +1433 -0
- package/lib/compiler/shared.js +64 -37
- package/lib/compiler/tweak-assocs.js +529 -0
- package/lib/compiler/utils.js +197 -33
- package/lib/edm/.eslintrc.json +5 -0
- package/lib/edm/annotations/genericTranslation.js +5 -9
- package/lib/edm/annotations/preprocessAnnotations.js +2 -2
- package/lib/edm/csn2edm.js +9 -8
- package/lib/edm/edm.js +11 -12
- package/lib/edm/edmPreprocessor.js +137 -73
- package/lib/edm/edmUtils.js +116 -22
- package/lib/gen/Dictionary.json +10 -3
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +9 -1
- package/lib/gen/language.tokens +86 -83
- package/lib/gen/languageLexer.interp +10 -1
- package/lib/gen/languageLexer.js +860 -833
- package/lib/gen/languageLexer.tokens +78 -75
- package/lib/gen/languageParser.js +5282 -4265
- package/lib/json/from-csn.js +12 -1
- package/lib/json/to-csn.js +126 -66
- package/lib/language/docCommentParser.js +2 -2
- package/lib/language/genericAntlrParser.js +76 -3
- package/lib/language/language.g4 +297 -130
- package/lib/language/multiLineStringParser.js +5 -5
- package/lib/main.d.ts +468 -59
- package/lib/main.js +35 -9
- package/lib/model/api.js +3 -1
- package/lib/model/csnRefs.js +225 -156
- package/lib/model/csnUtils.js +192 -223
- package/lib/model/enrichCsn.js +70 -29
- package/lib/model/revealInternalProperties.js +27 -6
- package/lib/model/sortViews.js +2 -1
- package/lib/modelCompare/compare.js +17 -12
- package/lib/optionProcessor.js +5 -4
- package/lib/render/manageConstraints.js +35 -32
- package/lib/render/toCdl.js +73 -288
- package/lib/render/toHdbcds.js +25 -23
- package/lib/render/toSql.js +98 -41
- package/lib/render/utils/common.js +5 -10
- package/lib/render/utils/sql.js +4 -3
- package/lib/render/utils/stringEscapes.js +111 -0
- package/lib/sql-identifier.js +1 -1
- package/lib/transform/.eslintrc.json +5 -0
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +35 -12
- package/lib/transform/db/assertUnique.js +1 -1
- package/lib/transform/db/associations.js +103 -305
- package/lib/transform/db/cdsPersistence.js +2 -2
- package/lib/transform/db/constraints.js +55 -52
- package/lib/transform/db/expansion.js +46 -24
- package/lib/transform/db/flattening.js +553 -102
- package/lib/transform/db/groupByOrderBy.js +3 -1
- package/lib/transform/db/transformExists.js +59 -6
- package/lib/transform/db/views.js +5 -4
- package/lib/transform/draft/.eslintrc.json +38 -0
- package/lib/transform/{db/draft.js → draft/db.js} +6 -5
- package/lib/transform/draft/odata.js +227 -0
- package/lib/transform/forHanaNew.js +67 -183
- package/lib/transform/forOdataNew.js +17 -171
- package/lib/transform/localized.js +34 -19
- package/lib/transform/odata/generateForeignKeyElements.js +1 -1
- package/lib/transform/odata/referenceFlattener.js +95 -89
- package/lib/transform/odata/structureFlattener.js +1 -1
- package/lib/transform/odata/toFinalBaseType.js +86 -12
- package/lib/transform/odata/typesExposure.js +5 -5
- package/lib/transform/odata/utils.js +2 -2
- package/lib/transform/transformUtilsNew.js +36 -22
- package/lib/transform/translateAssocsToJoins.js +2 -19
- package/lib/transform/universalCsn/.eslintrc.json +36 -0
- package/lib/transform/universalCsn/coreComputed.js +170 -0
- package/lib/transform/universalCsn/universalCsnEnricher.js +715 -0
- package/lib/transform/universalCsn/utils.js +63 -0
- package/lib/utils/objectUtils.js +30 -0
- package/package.json +1 -1
- package/share/messages/README.md +26 -0
- package/lib/compiler/definer.js +0 -2361
- package/lib/compiler/resolver.js +0 -3079
- 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
|
|
77
|
+
const cmd = {
|
|
78
78
|
options: {},
|
|
79
79
|
positionalArguments: [],
|
|
80
|
-
option,
|
|
80
|
+
option: commandOption,
|
|
81
81
|
positionalArgument: (argumentDefinition) => {
|
|
82
|
-
_setPositionalArguments(argumentDefinition,
|
|
83
|
-
return
|
|
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[
|
|
89
|
-
throw new Error(`Duplicate assignment for long command ${
|
|
88
|
+
if (optionProcessor.commands[cmd.longName]) {
|
|
89
|
+
throw new Error(`Duplicate assignment for long command ${cmd.longName}`);
|
|
90
90
|
}
|
|
91
|
-
optionProcessor.commands[
|
|
91
|
+
optionProcessor.commands[cmd.longName] = cmd;
|
|
92
92
|
|
|
93
|
-
if (
|
|
94
|
-
if (optionProcessor.commands[
|
|
95
|
-
throw new Error(`Duplicate assignment for short command ${
|
|
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[
|
|
97
|
+
optionProcessor.commands[cmd.shortName] = cmd;
|
|
98
98
|
}
|
|
99
|
-
return
|
|
99
|
+
return cmd;
|
|
100
100
|
|
|
101
101
|
// Command API: Define a command option
|
|
102
|
-
function
|
|
103
|
-
return _addOption(
|
|
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
|
|
108
|
-
|
|
109
|
-
return
|
|
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(
|
|
159
|
+
function _addOption(cmd, optString, validValues, options) {
|
|
160
160
|
const opt = _parseOptionString(optString, validValues);
|
|
161
161
|
Object.assign(opt, options);
|
|
162
162
|
|
|
163
|
-
if (
|
|
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 '${
|
|
169
|
+
description: `Command '${cmd.longName}' has option clash with general options for: ${opt.longName}`
|
|
170
170
|
});
|
|
171
171
|
}
|
|
172
|
-
|
|
172
|
+
cmd.options[opt.longName] = opt;
|
|
173
173
|
if (opt.shortName) {
|
|
174
|
-
if (
|
|
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 '${
|
|
180
|
+
description: `Command '${cmd.longName}' has option clash with general options for: ${opt.shortName}`
|
|
181
181
|
});
|
|
182
182
|
}
|
|
183
|
-
|
|
183
|
+
cmd.options[opt.shortName] = opt;
|
|
184
184
|
}
|
|
185
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
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
|
-
|
|
471
|
-
|
|
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
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
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,
|
|
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 (
|
|
532
|
-
const cmd = optionProcessor.commands[
|
|
566
|
+
if (commandName) {
|
|
567
|
+
const cmd = optionProcessor.commands[commandName];
|
|
533
568
|
if (!cmd) {
|
|
534
|
-
throw new Error(`Expected existing command: "${
|
|
569
|
+
throw new Error(`Expected existing command: "${cmd}"`);
|
|
535
570
|
}
|
|
536
571
|
opts = cmd.options;
|
|
537
|
-
options = options[
|
|
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 ((
|
|
552
|
-
error = `Unknown option "${
|
|
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,
|
|
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(
|
|
600
|
-
if (!
|
|
634
|
+
function camelOptionsForCommand(cmd) {
|
|
635
|
+
if (!cmd || !optionProcessor.commands[cmd])
|
|
601
636
|
return []
|
|
602
|
-
|
|
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
|
|
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
|
|
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
|
}
|
package/lib/checks/enricher.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
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.
|
|
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,
|
|
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 (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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 (
|
|
58
|
-
if (artifact['@sql.prepend'])
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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.
|
package/lib/checks/types.js
CHANGED
|
@@ -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 &&
|
|
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
|
|
package/lib/checks/validator.js
CHANGED
|
@@ -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), [],
|
|
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 ||
|
|
208
|
-
|
|
209
|
-
|
|
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
|
|