@sap/cds-compiler 3.1.2 → 3.4.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.
- package/CHANGELOG.md +101 -3
- package/bin/cdsc.js +4 -2
- package/doc/CHANGELOG_BETA.md +35 -0
- package/lib/api/main.js +153 -29
- package/lib/api/validate.js +8 -3
- package/lib/base/dictionaries.js +6 -6
- package/lib/base/error.js +2 -2
- package/lib/base/keywords.js +106 -24
- package/lib/base/message-registry.js +177 -79
- package/lib/base/messages.js +78 -57
- package/lib/base/model.js +2 -1
- package/lib/checks/actionsFunctions.js +1 -1
- package/lib/checks/annotationsOData.js +2 -2
- package/lib/checks/arrayOfs.js +15 -7
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +53 -0
- package/lib/checks/defaultValues.js +4 -2
- package/lib/checks/elements.js +81 -6
- package/lib/checks/foreignKeys.js +12 -13
- package/lib/checks/invalidTarget.js +10 -11
- package/lib/checks/managedInType.js +21 -15
- package/lib/checks/nullableKeys.js +1 -1
- package/lib/checks/onConditions.js +9 -9
- package/lib/checks/parameters.js +23 -0
- package/lib/checks/queryNoDbArtifacts.js +1 -1
- package/lib/checks/selectItems.js +1 -1
- package/lib/checks/sql-snippets.js +12 -10
- package/lib/checks/types.js +2 -2
- package/lib/checks/utils.js +17 -7
- package/lib/checks/validator.js +36 -14
- package/lib/compiler/assert-consistency.js +21 -13
- package/lib/compiler/builtins.js +8 -0
- package/lib/compiler/checks.js +57 -40
- package/lib/compiler/define.js +139 -69
- package/lib/compiler/extend.js +319 -50
- package/lib/compiler/finalize-parse-cdl.js +14 -9
- package/lib/compiler/kick-start.js +2 -35
- package/lib/compiler/populate.js +111 -68
- package/lib/compiler/propagator.js +5 -3
- package/lib/compiler/resolve.js +71 -108
- package/lib/compiler/shared.js +82 -54
- package/lib/compiler/tweak-assocs.js +26 -14
- package/lib/compiler/utils.js +13 -2
- package/lib/edm/annotations/genericTranslation.js +10 -7
- package/lib/edm/csn2edm.js +11 -11
- package/lib/edm/edm.js +17 -9
- package/lib/edm/edmPreprocessor.js +53 -30
- package/lib/edm/edmUtils.js +7 -2
- package/lib/gen/Dictionary.json +14 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -2
- package/lib/gen/languageParser.js +4312 -4186
- package/lib/inspect/inspectModelStatistics.js +1 -1
- package/lib/inspect/inspectPropagation.js +23 -9
- package/lib/json/csnVersion.js +13 -13
- package/lib/json/from-csn.js +161 -172
- package/lib/json/to-csn.js +70 -10
- package/lib/language/.eslintrc.json +4 -0
- package/lib/language/antlrParser.js +8 -11
- package/lib/language/docCommentParser.js +1 -2
- package/lib/language/errorStrategy.js +54 -27
- package/lib/language/genericAntlrParser.js +140 -93
- package/lib/language/language.g4 +57 -33
- package/lib/language/multiLineStringParser.js +75 -63
- package/lib/main.d.ts +3 -6
- package/lib/main.js +1 -0
- package/lib/model/.eslintrc.json +13 -0
- package/lib/model/api.js +4 -2
- package/lib/model/csnRefs.js +78 -50
- package/lib/model/csnUtils.js +272 -222
- package/lib/model/enrichCsn.js +41 -31
- package/lib/model/revealInternalProperties.js +61 -57
- package/lib/model/sortViews.js +35 -31
- package/lib/modelCompare/compare.js +52 -18
- package/lib/modelCompare/filter.js +83 -0
- package/lib/optionProcessor.js +10 -1
- package/lib/render/manageConstraints.js +11 -7
- package/lib/render/toCdl.js +151 -106
- package/lib/render/toHdbcds.js +8 -6
- package/lib/render/toRename.js +4 -4
- package/lib/render/toSql.js +17 -7
- package/lib/render/utils/common.js +27 -9
- package/lib/render/utils/sql.js +5 -5
- package/lib/sql-identifier.js +7 -0
- package/lib/transform/db/applyTransformations.js +32 -3
- package/lib/transform/db/assertUnique.js +27 -38
- package/lib/transform/db/expansion.js +92 -41
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/temporal.js +3 -1
- package/lib/transform/db/transformExists.js +8 -2
- package/lib/transform/db/views.js +42 -13
- package/lib/transform/draft/db.js +2 -2
- package/lib/transform/forOdataNew.js +10 -7
- package/lib/transform/{forHanaNew.js → forRelationalDB.js} +18 -12
- package/lib/transform/localized.js +29 -20
- package/lib/transform/odata/toFinalBaseType.js +8 -11
- package/lib/transform/odata/typesExposure.js +2 -1
- package/lib/transform/parseExpr.js +245 -0
- package/lib/transform/transformUtilsNew.js +122 -51
- package/lib/transform/translateAssocsToJoins.js +17 -16
- package/lib/utils/moduleResolve.js +5 -5
- package/lib/utils/objectUtils.js +3 -3
- package/lib/utils/term.js +5 -5
- package/package.json +2 -2
- package/share/messages/anno-duplicate-unrelated-layer.md +6 -6
- package/share/messages/check-proper-type-of.md +4 -4
- package/share/messages/check-proper-type.md +2 -2
- package/share/messages/duplicate-autoexposed.md +4 -4
- package/share/messages/extend-repeated-intralayer.md +4 -5
- package/share/messages/extend-unrelated-layer.md +4 -4
- package/share/messages/message-explanations.json +3 -1
- package/share/messages/redirected-to-ambiguous.md +7 -6
- package/share/messages/redirected-to-complex.md +63 -0
- package/share/messages/redirected-to-unrelated.md +6 -5
- package/share/messages/rewrite-not-supported.md +4 -4
- package/share/messages/{syntax-expected-integer.md → syntax-expecting-integer.md} +4 -4
- package/share/messages/wildcard-excluding-one.md +37 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,102 @@
|
|
|
7
7
|
Note: `beta` fixes, changes and features are usually not listed in this ChangeLog but [here](doc/CHANGELOG_BETA.md).
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
|
|
10
|
+
## Version 3.4.0 - 2022-10-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- to.sql: Add support for sql dialect `h2`, which renders SQL for H2 2.x
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Properly report an error for bare `$self` references,
|
|
19
|
+
except in the `on` condition of unmanaged associations.
|
|
20
|
+
- Do not dump with references to CDS variables like `$now` in `expand`/`inline`.
|
|
21
|
+
- Properly report an error when trying to `cast` a column to an association.
|
|
22
|
+
- to.cdl: Identifiers that are always keywords in special functions are now escaped.
|
|
23
|
+
- to.edm(x):
|
|
24
|
+
+ Nested annotation was not applied if outer annotation has value zero.
|
|
25
|
+
+ Fix `AppliesTo=ComplexType, TypeDefinition` term definition directive.
|
|
26
|
+
- to.sql/hdi/hdbcds:
|
|
27
|
+
+ Properly report an error for `exists` with `$self.managed-association`
|
|
28
|
+
+ For sql dialect `hana`, add an implicit alias when using `:param` in the select list
|
|
29
|
+
+ Handle `$self` and magic variables during expansion of nested projections
|
|
30
|
+
|
|
31
|
+
## Version 3.3.2 - 2022-09-30
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- to.edm(x): Set `Scale` (V4) or `@sap:variable-scale` (V2) attributes correctly when overwriting `cds.Decimal`
|
|
36
|
+
with `@odata.Scale`.
|
|
37
|
+
- to.sql: For dialect `postgres`, add braces around `$now`, `$at.from` and `$at.to`.
|
|
38
|
+
|
|
39
|
+
## Version 3.3.0 - 2022-09-29
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
|
|
43
|
+
- Nested projections can be used without `beta` option:
|
|
44
|
+
+ Support `expand`: columns can look like `assoc_or_struct_or_tabalias { col_expression1, … }`,
|
|
45
|
+
`longer.ref as name { *, … } excluding { … }`, `{ col_expression1 as sub1, … } as name`, etc.
|
|
46
|
+
+ Support `inline`: columns can look like `assoc_or_struct_or_tabalias.{ col_expression1, … }`,
|
|
47
|
+
`longer.ref[filter = condition].{ *, … } excluding { … }`, `assoc_or_struct_or_tabalias.*`, etc.
|
|
48
|
+
- to.sql/hdi/hdbcds/edm(x)/for.odata: Allow to structure comparison against `is [not] null`.
|
|
49
|
+
- to.sql: Support dialect `postgres` - generates SQL intended for PostgreSQL. Not supported are `cds.hana` data types and views with parameters.
|
|
50
|
+
|
|
51
|
+
### Changed
|
|
52
|
+
|
|
53
|
+
- A valid redirection target does not depend on parameters anymore. This
|
|
54
|
+
change could induce a redirection error, which could easily solved by assigning
|
|
55
|
+
`@cds.redirection.target: false` to the entity with “non-matching” parameters.
|
|
56
|
+
- Properly issue an error when projecting associations with parameter
|
|
57
|
+
references in the `on` condition. Before this change, the compiler dumped
|
|
58
|
+
when projecting such an association in a view on top.
|
|
59
|
+
- Update OData vocabularies 'Capabilities', 'Common', 'UI'.
|
|
60
|
+
- to.cdl:
|
|
61
|
+
+ Extensions are now always put into property `model` of `to.cdl()`s result.
|
|
62
|
+
+ Actions on views and projections are now rendered as part of the definition, instead of an extension.
|
|
63
|
+
- to.edm(x): `@Capabilities` 'pull up' supports all counterpart properties of `@Capabilities.NavigationPropertyRestriction`
|
|
64
|
+
except for properties `NavigationProperty` and `Navigability`.
|
|
65
|
+
- to.hdi: Updated list of `keywords` which must be quoted in naming mode `plain`.
|
|
66
|
+
- to.sql/hdi/hdbcds/edm(x)/for.odata: Reject structure comparison with operators `<,>,<=,>=`. Message id `expr-unexpected-operator`
|
|
67
|
+
is downgradable to a warning.
|
|
68
|
+
|
|
69
|
+
### Fixed
|
|
70
|
+
|
|
71
|
+
- Do not issue a warning anymore when adding elements via multiple `extend` statements in the same file.
|
|
72
|
+
- An info message for annotating builtins through `extend` statements is now reported, similar to `annotate`.
|
|
73
|
+
- Fix auto-redirection for target of new assoc in query entity
|
|
74
|
+
- for.odata: `@readonly/insertonly/mandatory: false` are not expanded.
|
|
75
|
+
|
|
76
|
+
## Version 3.2.0 - 2022-08-30
|
|
77
|
+
|
|
78
|
+
### Added
|
|
79
|
+
|
|
80
|
+
- New Integer types with these mappings:
|
|
81
|
+
|
|
82
|
+
| CDS | OData | SQL | HANA CDS |
|
|
83
|
+
| --------- | --------- | -------- | ------------- |
|
|
84
|
+
| cds.UInt8 | Edm.Byte | TINYINT | hana.TINYINT |
|
|
85
|
+
| cds.Int16 | Edm.Int16 | SMALLINT | hana.SMALLINT |
|
|
86
|
+
| cds.Int32 | Edm.Int32 | INTEGER | cds.Integer |
|
|
87
|
+
| cds.Int64 | Edm.Int64 | BIGINT | cds.Integer64 |
|
|
88
|
+
|
|
89
|
+
- Properties of type definitions and types of direct elements can now be extended,
|
|
90
|
+
e.g. `extend T with (length: 10);`
|
|
91
|
+
|
|
92
|
+
- CDL parser: support SQL function `substr_regexpr` with its special argument syntax.
|
|
93
|
+
|
|
94
|
+
### Fixed
|
|
95
|
+
|
|
96
|
+
- An internal dump could have occurred in certain situations
|
|
97
|
+
for models with cyclic type definitions.
|
|
98
|
+
- Annotations on inferred enum elements of views were lost during recompilation.
|
|
99
|
+
- to.cdl: Annotations on enum value in query elements were lost.
|
|
100
|
+
- for.odata: Allow dynamic shortcut annotation values (`$edmJson`).
|
|
101
|
+
- to.edm(x):
|
|
102
|
+
+ Don't overwrite annotations of input model.
|
|
103
|
+
+ Ignore `null` values in `$edmJson` strings.
|
|
104
|
+
- to.hdi.migration: Don't interpret bound action changes as element changes.
|
|
105
|
+
|
|
10
106
|
## Version 3.1.2 - 2022-08-19
|
|
11
107
|
|
|
12
108
|
### Fixed
|
|
@@ -25,8 +121,8 @@ The compiler behavior concerning `beta` features can change at any time without
|
|
|
25
121
|
`extend SomeEntity with FirstInclude, SecondInclude;`
|
|
26
122
|
- Aspects can now have actions and functions, similar to entities. Aspects can be extended by actions as well.
|
|
27
123
|
- `cdsc`:
|
|
28
|
-
|
|
29
|
-
|
|
124
|
+
+ `toCsn` now supports `--with-locations` which adds a `$location` property to artifacts
|
|
125
|
+
+ `toHana`/`toSql` now supports `--disable-hana-comments`, which disables rendering of doc-comments for HANA.
|
|
30
126
|
- to.hdi/sql/hdbcds: Support FK-access in `ORDER BY` and `GROUP BY`
|
|
31
127
|
- to.hdi.migration: Detect an implicit change from `not null` to `null` and render corresponding `ALTER`
|
|
32
128
|
|
|
@@ -51,8 +147,10 @@ The compiler behavior concerning `beta` features can change at any time without
|
|
|
51
147
|
- compiler:
|
|
52
148
|
+ `cast(elem as EnumType)` crashed the compiler.
|
|
53
149
|
+ Annotations on sub-elements in query entities were lost during re-compilation.
|
|
54
|
-
+ An association's cardinality was lost for associations published in projections.
|
|
150
|
+
+ An association's cardinality was lost for new associations published in projections.
|
|
55
151
|
+ Annotations on indirect action parameters were lost in CSN flavor `gensrc`.
|
|
152
|
+
+ Re-allow `annotate` statements referring to the same element twice,
|
|
153
|
+
even if there are annotation assignments for sub elements.
|
|
56
154
|
+ If a file's content starts with `{` and if neither file extension is known nor
|
|
57
155
|
`fallbackParser` is set, assume the source is CSN.
|
|
58
156
|
- all backends: references in `order by` _expressions_ are correctly resolved.
|
package/bin/cdsc.js
CHANGED
|
@@ -153,7 +153,9 @@ function cdsc_main() {
|
|
|
153
153
|
cmdLine.args.files = [ cmdLine.args.file ];
|
|
154
154
|
}
|
|
155
155
|
else if (cmdLine.command === 'parseOnly') {
|
|
156
|
+
// Remap command and command-specific options.
|
|
156
157
|
cmdLine.command = 'toCsn';
|
|
158
|
+
cmdLine.options.toCsn = cmdLine.options.parseOnly;
|
|
157
159
|
cmdLine.options.parseOnly = true;
|
|
158
160
|
cmdLine.args.files = [ cmdLine.args.file ];
|
|
159
161
|
}
|
|
@@ -409,7 +411,7 @@ function executeCommandLine(command, options, args) {
|
|
|
409
411
|
}
|
|
410
412
|
else {
|
|
411
413
|
const sqlResult = main.to.sql(csn, options);
|
|
412
|
-
writeToFileOrDisplay(options.out, 'model.sql', sqlResult.join('\n'), true);
|
|
414
|
+
writeToFileOrDisplay(options.out, 'model.sql', sqlResult.join('\n\n'), true);
|
|
413
415
|
}
|
|
414
416
|
return model;
|
|
415
417
|
}
|
|
@@ -521,7 +523,7 @@ function executeCommandLine(command, options, args) {
|
|
|
521
523
|
log(); // newline
|
|
522
524
|
});
|
|
523
525
|
if (options.showMessageId && hasAtLeastOneExplanation)
|
|
524
|
-
log(`${colorTerm.
|
|
526
|
+
log(`${colorTerm.asHelp('help')}: Messages marked with '…' have an explanation text. Use \`cdsc explain <message-id>\` for a more detailed error description.`);
|
|
525
527
|
}
|
|
526
528
|
return model;
|
|
527
529
|
}
|
package/doc/CHANGELOG_BETA.md
CHANGED
|
@@ -8,6 +8,41 @@ Note: `beta` fixes, changes and features are listed in this ChangeLog just for i
|
|
|
8
8
|
The compiler behavior concerning `beta` features can change at any time without notice.
|
|
9
9
|
**Don't use `beta` fixes, changes and features in productive mode.**
|
|
10
10
|
|
|
11
|
+
## Version 3.4.0 - 2022-MM-DD
|
|
12
|
+
|
|
13
|
+
### Added `aspectWithoutElements`
|
|
14
|
+
|
|
15
|
+
- Aspects can now be defined without elements, e.g. `aspect A;`. This allows the definition of annotation-only aspects.
|
|
16
|
+
Views can be extended by such an aspect. For example:
|
|
17
|
+
```cds
|
|
18
|
+
entity V as projection on SomeEntity;
|
|
19
|
+
@anno aspect A;
|
|
20
|
+
extend V with A;
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Added `sqlMigration`
|
|
24
|
+
|
|
25
|
+
- to.sql.migration: Offer something similar to to.hdi.migration, but for general SQL. Don't offer a complete out-of-the-box schema evolution, instead only
|
|
26
|
+
allow lossless, easy to revert actions like adding a column or extending a string length.
|
|
27
|
+
|
|
28
|
+
## Version 3.3.0 - 2022-09-29
|
|
29
|
+
|
|
30
|
+
### Removed `nestedProjections`
|
|
31
|
+
|
|
32
|
+
- This is now the default - see CHANGELOG entry for 3.3.0.
|
|
33
|
+
|
|
34
|
+
### Fixed `nestedProjections`
|
|
35
|
+
|
|
36
|
+
- Issue an error for an unexpected `as ‹alias›` for references with `inline`;
|
|
37
|
+
people likely have confused `inline` with `expand`.
|
|
38
|
+
- Resolving references in the user-provided `on` condition of projected or newly-defined
|
|
39
|
+
associations inside `expand` and `inline` now works correctly.
|
|
40
|
+
- Correct `key` propagation for references with `expand` or `inline`.
|
|
41
|
+
- If an element is projected with sibling `expand`, the original data structure is usually
|
|
42
|
+
_not preserved_ (why use an `expand` if it is?). Therefore, the compiler cannot auto-rewrite
|
|
43
|
+
the `on` condition of unmanaged associations if such an element is referred to in the original
|
|
44
|
+
`on` condition. In that case, provide your own via `…: redirected to … on ‹condition›`.
|
|
45
|
+
|
|
11
46
|
## Version 3.1.0 - 2022-08-04
|
|
12
47
|
|
|
13
48
|
### Added `optionalActionFunctionParameters`
|
package/lib/api/main.js
CHANGED
|
@@ -12,10 +12,11 @@ const forOdataNew = lazyload('../transform/forOdataNew.js');
|
|
|
12
12
|
const toSql = lazyload('../render/toSql');
|
|
13
13
|
const toCdl = require('../render/toCdl');
|
|
14
14
|
const modelCompare = lazyload('../modelCompare/compare');
|
|
15
|
+
const diffFilter = lazyload('../modelCompare/filter');
|
|
15
16
|
const sortViews = lazyload('../model/sortViews');
|
|
16
17
|
const csnUtils = lazyload('../model/csnUtils');
|
|
17
18
|
const timetrace = lazyload('../utils/timetrace');
|
|
18
|
-
const
|
|
19
|
+
const forRelationalDB = lazyload('../transform/forRelationalDB');
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Return the artifact name for use for the hdbresult object
|
|
@@ -33,7 +34,7 @@ const { cloneCsnNonDict } = require('../model/csnUtils');
|
|
|
33
34
|
const { toHdbcdsSource } = require('../render/toHdbcds');
|
|
34
35
|
const { ModelError } = require('../base/error');
|
|
35
36
|
const { forEach, forEachKey } = require('../utils/objectUtils');
|
|
36
|
-
const { checkRemovedDeprecatedFlags } = require('../base/model');
|
|
37
|
+
const { checkRemovedDeprecatedFlags, isBetaEnabled } = require('../base/model');
|
|
37
38
|
const { csn2edm, csn2edmAll } = require('../edm/csn2edm');
|
|
38
39
|
|
|
39
40
|
const relevantGeneralOptions = [ /* for future generic options */ ];
|
|
@@ -92,13 +93,17 @@ function checkPreTransformedCsn(csn, options, relevantOptionNames, warnAboutMism
|
|
|
92
93
|
const { error, warning, throwWithAnyError } = messages.makeMessageFunction(csn, options, module);
|
|
93
94
|
|
|
94
95
|
for (const name of relevantOptionNames ) {
|
|
95
|
-
if (options[name] !== csn.meta.options[name])
|
|
96
|
-
error('wrong-pretransformed-csn', null,
|
|
96
|
+
if (options[name] !== csn.meta.options[name]) {
|
|
97
|
+
error('wrong-pretransformed-csn', null, { prop: name, value: options[name], othervalue: csn.meta.options[name] },
|
|
98
|
+
'Expected pre-processed CSN to have option $(PROP) set to $(VALUE). Found: $(OTHERVALUE)');
|
|
99
|
+
}
|
|
97
100
|
}
|
|
98
101
|
|
|
99
102
|
for (const name of warnAboutMismatch ) {
|
|
100
|
-
if (options[name] !== csn.meta.options[name])
|
|
101
|
-
warning('options-mismatch-pretransformed-csn', null,
|
|
103
|
+
if (options[name] !== csn.meta.options[name]) {
|
|
104
|
+
warning('options-mismatch-pretransformed-csn', null, { prop: name, value: options[name], othervalue: csn.meta.options[name] },
|
|
105
|
+
'Expected pre-processed CSN to have option $(PROP) set to $(VALUE). Found: $(OTHERVALUE)');
|
|
106
|
+
}
|
|
102
107
|
}
|
|
103
108
|
|
|
104
109
|
throwWithAnyError();
|
|
@@ -147,7 +152,7 @@ function odata(csn, options = {}) {
|
|
|
147
152
|
*
|
|
148
153
|
* @param {object} csn CSN to process
|
|
149
154
|
* @param {object} [externalOptions={}] Options
|
|
150
|
-
* @returns {object} { model: string,
|
|
155
|
+
* @returns {object} { model: string, namespace: string }
|
|
151
156
|
*/
|
|
152
157
|
function cdl(csn, externalOptions = {}) {
|
|
153
158
|
const internalOptions = prepareOptions.to.cdl(externalOptions);
|
|
@@ -165,7 +170,7 @@ function cdl(csn, externalOptions = {}) {
|
|
|
165
170
|
function forSql(csn, options = {}) {
|
|
166
171
|
const internalOptions = prepareOptions.to.sql(options);
|
|
167
172
|
internalOptions.transformation = 'sql';
|
|
168
|
-
const transformedCsn =
|
|
173
|
+
const transformedCsn = forRelationalDB.transformForRelationalDBWithCsn(csn, internalOptions, 'to.sql');
|
|
169
174
|
|
|
170
175
|
return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
|
|
171
176
|
}
|
|
@@ -180,7 +185,7 @@ function forSql(csn, options = {}) {
|
|
|
180
185
|
function forHdi(csn, options = {}) {
|
|
181
186
|
const internalOptions = prepareOptions.to.hdi(options);
|
|
182
187
|
internalOptions.transformation = 'sql';
|
|
183
|
-
const transformedCsn =
|
|
188
|
+
const transformedCsn = forRelationalDB.transformForRelationalDBWithCsn(csn, internalOptions, 'to.hdi');
|
|
184
189
|
|
|
185
190
|
return internalOptions.testMode ? toCsn.sortCsn(transformedCsn, internalOptions) : transformedCsn;
|
|
186
191
|
}
|
|
@@ -196,7 +201,7 @@ function forHdbcds(csn, options = {}) {
|
|
|
196
201
|
const internalOptions = prepareOptions.to.hdbcds(options);
|
|
197
202
|
internalOptions.transformation = 'hdbcds';
|
|
198
203
|
|
|
199
|
-
const hanaCsn =
|
|
204
|
+
const hanaCsn = forRelationalDB.transformForRelationalDBWithCsn(csn, internalOptions, 'to.hdbcds');
|
|
200
205
|
|
|
201
206
|
return internalOptions.testMode ? toCsn.sortCsn(hanaCsn, internalOptions) : hanaCsn;
|
|
202
207
|
}
|
|
@@ -212,11 +217,6 @@ function sql(csn, options = {}) {
|
|
|
212
217
|
const internalOptions = prepareOptions.to.sql(options);
|
|
213
218
|
internalOptions.transformation = 'sql';
|
|
214
219
|
|
|
215
|
-
const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
|
|
216
|
-
|
|
217
|
-
if (internalOptions.sqlDialect === 'postgres' && !baseModel.isBetaEnabled(internalOptions, 'postgres'))
|
|
218
|
-
error(null, null, 'sqlDialect: \'postgres\' is only supported with beta-flag \'postgres\'');
|
|
219
|
-
|
|
220
220
|
// we need the CSN for view sorting
|
|
221
221
|
const transformedCsn = forSql(csn, options);
|
|
222
222
|
const sqls = toSql.toSqlDdl(transformedCsn, internalOptions);
|
|
@@ -366,6 +366,119 @@ function remapName(key, csn, filter = () => true) {
|
|
|
366
366
|
return key;
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
+
/**
|
|
370
|
+
* Return all changes in artifacts between two given models.
|
|
371
|
+
* Note: Only supports changes in artifacts compiled/rendered as db-CSN/SQL.
|
|
372
|
+
*
|
|
373
|
+
* @param {CSN.Model} csn A clean input CSN representing the desired "after-image"
|
|
374
|
+
* @param {HdiOptions} options Options
|
|
375
|
+
* @param {CSN.Model} beforeImage A db-transformed CSN representing the "before-image", or null in case no such image
|
|
376
|
+
* is known, i.e. for the very first migration step
|
|
377
|
+
* @returns {object} An object with three properties:
|
|
378
|
+
* - afterImage: A db-transformed CSN representing the "after-image"
|
|
379
|
+
* - drops: An array of SQL statements to drop views/tables
|
|
380
|
+
* - createsAndAlters: An array of SQL statements to ALTER/CREATE tables/views
|
|
381
|
+
*/
|
|
382
|
+
function sqlMigration(csn, options, beforeImage) {
|
|
383
|
+
const internalOptions = prepareOptions.to.sql(options);
|
|
384
|
+
const { error, throwWithError } = messages.makeMessageFunction(csn, options, 'to.sql.migration');
|
|
385
|
+
|
|
386
|
+
if (!isBetaEnabled(internalOptions, 'sqlMigration'))
|
|
387
|
+
throw new Error('Function `to.sql.migration` requires beta-flag `sqlMigration`');
|
|
388
|
+
|
|
389
|
+
// Prepare after-image.
|
|
390
|
+
const afterImage = forSql(csn, options);
|
|
391
|
+
// Compare both images.
|
|
392
|
+
const diff = modelCompare.compareModels(beforeImage || afterImage, afterImage, internalOptions);
|
|
393
|
+
const diffFilterObj = diffFilter[options.sqlDialect];
|
|
394
|
+
|
|
395
|
+
if (diffFilterObj) {
|
|
396
|
+
diff.extensions.forEach(ex => diffFilterObj.extension(ex, error));
|
|
397
|
+
diff.migrations.forEach(migration => diffFilterObj.migration(migration, error));
|
|
398
|
+
Object.entries(diff.deletions).forEach(entry => diffFilterObj.deletion(entry, error));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const drops = {
|
|
402
|
+
creates: {},
|
|
403
|
+
final: Object.entries(diff.deletions).reduce((previous, [ name, artifact ]) => {
|
|
404
|
+
previous[name] = `DROP ${ (artifact.query || artifact.projection) ? 'VIEW' : 'TABLE' } ${ artifact['@cds.persistence.name'] };`;
|
|
405
|
+
return previous;
|
|
406
|
+
}, {}),
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
const cleanup = [];
|
|
410
|
+
// Delete artifacts that are already present in csn
|
|
411
|
+
if (beforeImage?.definitions) {
|
|
412
|
+
Object.keys(beforeImage.definitions).forEach((artifactName) => {
|
|
413
|
+
const beforeArtifact = beforeImage.definitions[artifactName];
|
|
414
|
+
const diffArtifact = diff.definitions[artifactName];
|
|
415
|
+
// TODO: exists, abstract? isPersistedOnDb?
|
|
416
|
+
if (diffArtifact && diffArtifact['@cds.persistence.name'] && !diffArtifact['@cds.persistence.skip'] &&
|
|
417
|
+
(diffArtifact.query || diffArtifact.projection) &&
|
|
418
|
+
(diffArtifact[modelCompare.isChanged] === true || // we know it changed because we compared two views
|
|
419
|
+
diffArtifact[modelCompare.isChanged] === undefined)) { // if it was removed in the after, then we don't have the flag
|
|
420
|
+
drops.creates[artifactName] = `DROP VIEW ${ diffArtifact['@cds.persistence.name'] };`;
|
|
421
|
+
} // TODO: What happens with a changed kind -> entity becomes a view?
|
|
422
|
+
else if (diffArtifact &&
|
|
423
|
+
diffArtifact['@cds.persistence.skip'] !== true &&
|
|
424
|
+
diffArtifact.kind === beforeArtifact.kind && // detect action -> entity
|
|
425
|
+
csnUtils.isPersistedAsView(diffArtifact) === csnUtils.isPersistedAsView(beforeArtifact) // detect view -> entity
|
|
426
|
+
) { // don't render again, but need info for primary key extension
|
|
427
|
+
diffArtifact['@cds.persistence.skip'] = true;
|
|
428
|
+
cleanup.push(() => delete diffArtifact['@cds.persistence.skip']);
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Convert the diff to SQL.
|
|
434
|
+
if (!internalOptions.beta)
|
|
435
|
+
internalOptions.beta = {};
|
|
436
|
+
|
|
437
|
+
internalOptions.beta.sqlExtensions = true;
|
|
438
|
+
|
|
439
|
+
// eslint-disable-next-line no-unused-vars
|
|
440
|
+
const { deletions, migrations, ...hdbkinds } = toSql.toSqlDdl(diff, internalOptions);
|
|
441
|
+
|
|
442
|
+
cleanup.forEach(fn => fn());
|
|
443
|
+
// TODO: Handle `ADD CONSTRAINT` etc!
|
|
444
|
+
const sortOrder = sortViews({ sql: {}, csn: afterImage });
|
|
445
|
+
|
|
446
|
+
const dropSqls = [];
|
|
447
|
+
const createAndAlterSqls = [];
|
|
448
|
+
// Turn the structured result into just a flat dictionary of "artifact name": "sql"
|
|
449
|
+
const flatSqlDict = Object.values(hdbkinds).reduce((prev, curr) => {
|
|
450
|
+
forEach(curr, (name, value) => {
|
|
451
|
+
prev[name] = value;
|
|
452
|
+
});
|
|
453
|
+
return prev;
|
|
454
|
+
}, Object.create(null));
|
|
455
|
+
|
|
456
|
+
// Sort all the SQL statements according to the overall order
|
|
457
|
+
for (const { name } of sortOrder) {
|
|
458
|
+
if (drops.final[name])
|
|
459
|
+
dropSqls.push(drops.final[name]);
|
|
460
|
+
else if (drops.creates[name])
|
|
461
|
+
dropSqls.push(drops.creates[name]);
|
|
462
|
+
|
|
463
|
+
// No else-if, since we have drop-creates for views!
|
|
464
|
+
if (flatSqlDict[name])
|
|
465
|
+
createAndAlterSqls.push(flatSqlDict[name]);
|
|
466
|
+
else if (migrations[name])
|
|
467
|
+
createAndAlterSqls.push(...migrations[name].map(m => m.sql));
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// We need to drop the things without dependants first - so inversely sorted
|
|
471
|
+
dropSqls.reverse();
|
|
472
|
+
|
|
473
|
+
throwWithError();
|
|
474
|
+
|
|
475
|
+
return {
|
|
476
|
+
afterImage,
|
|
477
|
+
drops: dropSqls,
|
|
478
|
+
createsAndAlters: createAndAlterSqls,
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
369
482
|
/**
|
|
370
483
|
* Return all changes in artifacts between two given models.
|
|
371
484
|
* Note: Only supports changes in entities (not views etc.) compiled/rendered as HANA-CSN/SQL.
|
|
@@ -374,17 +487,7 @@ function remapName(key, csn, filter = () => true) {
|
|
|
374
487
|
* @param {HdiOptions} options Options
|
|
375
488
|
* @param {CSN.Model} beforeImage A HANA-transformed CSN representing the "before-image", or null in case no such image
|
|
376
489
|
* is known, i.e. for the very first migration step
|
|
377
|
-
* @returns {
|
|
378
|
-
* - definitions: An array of objects with all artifacts in the after-image. Each object specifies
|
|
379
|
-
* the artifact filename, the suffix, and the corresponding SQL statement to create
|
|
380
|
-
* the artifact.
|
|
381
|
-
* - deletions: An array of objects with the deleted artifacts. Each object specifies the artifact
|
|
382
|
-
* filename and the suffix.
|
|
383
|
-
* - migrations: An array of objects with the changed (migrated) artifacts. Each object specifies the
|
|
384
|
-
* artifact filename, the suffix, and the changeset (an array of changes, each specifying
|
|
385
|
-
* whether it incurs potential data loss, and its respective SQL statement(s), with
|
|
386
|
-
* multiple statements concatenated as a multi-line string in case the change e.g.
|
|
387
|
-
* consists of a column drop and add).
|
|
490
|
+
* @returns {migration} The migration result
|
|
388
491
|
*/
|
|
389
492
|
function hdiMigration(csn, options, beforeImage) {
|
|
390
493
|
const internalOptions = prepareOptions.to.hdi(options);
|
|
@@ -392,11 +495,14 @@ function hdiMigration(csn, options, beforeImage) {
|
|
|
392
495
|
// Prepare after-image.
|
|
393
496
|
const afterImage = forHdi(csn, options);
|
|
394
497
|
|
|
395
|
-
// Compare both images.
|
|
396
498
|
const diff = modelCompare.compareModels(beforeImage || afterImage, afterImage, internalOptions);
|
|
397
499
|
|
|
398
500
|
// Convert the diff to SQL.
|
|
399
|
-
internalOptions.
|
|
501
|
+
if (!internalOptions.beta)
|
|
502
|
+
internalOptions.beta = {};
|
|
503
|
+
|
|
504
|
+
internalOptions.beta.sqlExtensions = true;
|
|
505
|
+
|
|
400
506
|
const { deletions, migrations, ...hdbkinds } = toSql.toSqlDdl(diff, internalOptions);
|
|
401
507
|
|
|
402
508
|
return {
|
|
@@ -454,6 +560,8 @@ function createSqlMigrations(migrations, afterImage) {
|
|
|
454
560
|
|
|
455
561
|
hdi.migration = hdiMigration;
|
|
456
562
|
|
|
563
|
+
sql.migration = sqlMigration;
|
|
564
|
+
|
|
457
565
|
/**
|
|
458
566
|
* Process the given CSN into HDBCDS artifacts.
|
|
459
567
|
*
|
|
@@ -514,7 +622,7 @@ function edmall(csn, options = {}) {
|
|
|
514
622
|
const { error } = messages.makeMessageFunction(csn, internalOptions, 'for.odata');
|
|
515
623
|
|
|
516
624
|
if (internalOptions.odataVersion === 'v2')
|
|
517
|
-
error(null, null, 'OData JSON output is not available for OData V2');
|
|
625
|
+
error(null, null, {}, 'OData JSON output is not available for OData V2');
|
|
518
626
|
|
|
519
627
|
const result = {};
|
|
520
628
|
let oDataCsn = csn;
|
|
@@ -927,3 +1035,19 @@ function lazyload(moduleName) {
|
|
|
927
1035
|
*
|
|
928
1036
|
* @typedef {object} edms
|
|
929
1037
|
*/
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* - afterImage: The desired after-image in db-CSN format
|
|
1041
|
+
* - definitions: An array of objects with all artifacts in the after-image. Each object specifies
|
|
1042
|
+
* the artifact filename, the suffix, and the corresponding SQL statement to create
|
|
1043
|
+
* the artifact.
|
|
1044
|
+
* - deletions: An array of objects with the deleted artifacts. Each object specifies the artifact
|
|
1045
|
+
* filename and the suffix.
|
|
1046
|
+
* - migrations: An array of objects with the changed (migrated) artifacts. Each object specifies the
|
|
1047
|
+
* artifact filename, the suffix, and the changeset (an array of changes, each specifying
|
|
1048
|
+
* whether it incurs potential data loss, and its respective SQL statement(s), with
|
|
1049
|
+
* multiple statements concatenated as a multi-line string in case the change e.g.
|
|
1050
|
+
* consists of a column drop and add).
|
|
1051
|
+
*
|
|
1052
|
+
* @typedef {object} migration
|
|
1053
|
+
*/
|
package/lib/api/validate.js
CHANGED
|
@@ -78,7 +78,7 @@ const validators = {
|
|
|
78
78
|
expected: () => 'type array',
|
|
79
79
|
found: val => `type ${ typeof val }`,
|
|
80
80
|
},
|
|
81
|
-
sqlDialect: generateStringValidator([ 'sqlite', 'hana', 'plain', 'postgres' ]),
|
|
81
|
+
sqlDialect: generateStringValidator([ 'sqlite', 'hana', 'plain', 'postgres', 'h2' ]),
|
|
82
82
|
sqlMapping: generateStringValidator([ 'plain', 'quoted', 'hdbcds' ]),
|
|
83
83
|
odataVersion: generateStringValidator([ 'v2', 'v4' ]),
|
|
84
84
|
odataFormat: generateStringValidator([ 'flat', 'structured' ]),
|
|
@@ -161,8 +161,13 @@ function validate(options, moduleName, customValidators = {}, combinationValidat
|
|
|
161
161
|
forEach(options, (optionName, optionValue) => {
|
|
162
162
|
const validator = customValidators[optionName] || validators[optionName] || booleanValidator;
|
|
163
163
|
|
|
164
|
-
if (!validator.validate(optionValue))
|
|
165
|
-
error('invalid-option', null, {
|
|
164
|
+
if (!validator.validate(optionValue)) {
|
|
165
|
+
error('invalid-option', null, {
|
|
166
|
+
prop: optionName,
|
|
167
|
+
value: validator.expected(optionValue),
|
|
168
|
+
othervalue: validator.found(optionValue),
|
|
169
|
+
}, 'Expected option $(PROP) to have $(VALUE). Found: $(OTHERVALUE)');
|
|
170
|
+
}
|
|
166
171
|
});
|
|
167
172
|
throwWithAnyError();
|
|
168
173
|
}
|
package/lib/base/dictionaries.js
CHANGED
|
@@ -19,7 +19,7 @@ function dictAdd( dict, name, entry, duplicateCallback ) {
|
|
|
19
19
|
duplicateCallback( name, found.name.location, found );
|
|
20
20
|
}
|
|
21
21
|
found.$duplicates.push( entry );
|
|
22
|
-
if (Array.isArray(entry.$duplicates))
|
|
22
|
+
if (Array.isArray( entry.$duplicates ))
|
|
23
23
|
found.$duplicates.push( ...entry.$duplicates )
|
|
24
24
|
else if (duplicateCallback && name) // do not complain with empty name ''
|
|
25
25
|
duplicateCallback( name, entry.name.location, entry );
|
|
@@ -31,7 +31,7 @@ function dictForEach( dict, callback ) {
|
|
|
31
31
|
// TODO: probably define an extra dictForEachArray()
|
|
32
32
|
for (const name in dict) {
|
|
33
33
|
const entry = dict[name];
|
|
34
|
-
if (Array.isArray(entry)) {
|
|
34
|
+
if (Array.isArray( entry )) {
|
|
35
35
|
entry.forEach( callback );
|
|
36
36
|
}
|
|
37
37
|
else {
|
|
@@ -53,8 +53,8 @@ function dictAddArray( dict, name, entry, messageCallback ) {
|
|
|
53
53
|
dict[name] = entry; // also ok if array (redefined)
|
|
54
54
|
return entry;
|
|
55
55
|
}
|
|
56
|
-
if (Array.isArray(entry)) {
|
|
57
|
-
if (Array.isArray(found)) {
|
|
56
|
+
if (Array.isArray( entry )) {
|
|
57
|
+
if (Array.isArray( found )) {
|
|
58
58
|
dict[name] = [ ...found, ...entry ];
|
|
59
59
|
}
|
|
60
60
|
else {
|
|
@@ -65,7 +65,7 @@ function dictAddArray( dict, name, entry, messageCallback ) {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
else {
|
|
68
|
-
if (Array.isArray(found)) {
|
|
68
|
+
if (Array.isArray( found )) {
|
|
69
69
|
dict[name] = [ ...found, entry ];
|
|
70
70
|
}
|
|
71
71
|
else {
|
|
@@ -84,7 +84,7 @@ function dictAddArray( dict, name, entry, messageCallback ) {
|
|
|
84
84
|
// Push `entry` to the array value with key `name` in the dictionary `dict`.
|
|
85
85
|
function pushToDict( dict, name, entry ) {
|
|
86
86
|
if (dict[name])
|
|
87
|
-
dict[name].push(entry);
|
|
87
|
+
dict[name].push( entry );
|
|
88
88
|
else
|
|
89
89
|
dict[name] = [entry];
|
|
90
90
|
}
|
package/lib/base/error.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
class CompilerAssertion extends Error {
|
|
8
8
|
constructor(message) {
|
|
9
|
-
super(message);
|
|
9
|
+
super(`cds-compiler assertion failed: ${message}`);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
@@ -16,7 +16,7 @@ class CompilerAssertion extends Error {
|
|
|
16
16
|
*/
|
|
17
17
|
class ModelError extends Error {
|
|
18
18
|
constructor(message) {
|
|
19
|
-
super(message);
|
|
19
|
+
super(`cds-compiler model error: ${message}`);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|