@sap/cds-compiler 2.15.2 → 3.0.2
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 +66 -1590
- package/bin/cdsc.js +42 -46
- package/doc/CHANGELOG_ARCHIVE.md +1592 -0
- package/doc/CHANGELOG_BETA.md +3 -4
- package/doc/CHANGELOG_DEPRECATED.md +35 -1
- package/doc/{DeprecatedOptions.md → DeprecatedOptions_v2.md} +3 -1
- package/doc/Versioning.md +20 -1
- package/lib/api/.eslintrc.json +2 -2
- package/lib/api/main.js +312 -143
- package/lib/api/options.js +15 -85
- package/lib/api/validate.js +6 -10
- package/lib/base/keywords.js +280 -110
- package/lib/base/message-registry.js +80 -24
- package/lib/base/messages.js +103 -52
- package/lib/base/model.js +44 -2
- package/lib/base/optionProcessorHelper.js +53 -21
- package/lib/checks/actionsFunctions.js +7 -5
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -0
- package/lib/checks/elements.js +6 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -1
- package/lib/checks/selectItems.js +5 -1
- package/lib/checks/types.js +4 -2
- package/lib/checks/utils.js +2 -2
- package/lib/checks/validator.js +2 -1
- package/lib/compiler/assert-consistency.js +15 -10
- package/lib/compiler/builtins.js +127 -10
- package/lib/compiler/define.js +6 -4
- package/lib/compiler/extend.js +63 -12
- package/lib/compiler/finalize-parse-cdl.js +20 -9
- package/lib/compiler/index.js +25 -11
- package/lib/compiler/moduleLayers.js +7 -0
- package/lib/compiler/populate.js +16 -14
- package/lib/compiler/propagator.js +3 -3
- package/lib/compiler/resolve.js +194 -222
- package/lib/compiler/shared.js +56 -76
- package/lib/compiler/tweak-assocs.js +9 -10
- package/lib/compiler/utils.js +7 -2
- package/lib/edm/annotations/genericTranslation.js +60 -6
- package/lib/edm/annotations/preprocessAnnotations.js +10 -11
- package/lib/edm/csn2edm.js +39 -41
- package/lib/edm/edm.js +22 -15
- package/lib/edm/edmPreprocessor.js +66 -69
- package/lib/edm/edmUtils.js +12 -62
- package/lib/gen/Dictionary.json +8 -6
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +8 -30
- package/lib/gen/language.tokens +105 -114
- package/lib/gen/languageLexer.interp +1 -34
- package/lib/gen/languageLexer.js +889 -1007
- package/lib/gen/languageLexer.tokens +95 -106
- package/lib/gen/languageParser.js +20717 -22376
- package/lib/json/from-csn.js +73 -68
- package/lib/json/to-csn.js +13 -10
- package/lib/language/antlrParser.js +2 -2
- package/lib/language/docCommentParser.js +61 -38
- package/lib/language/errorStrategy.js +52 -40
- package/lib/language/genericAntlrParser.js +333 -259
- package/lib/language/language.g4 +600 -645
- package/lib/language/multiLineStringParser.js +14 -42
- package/lib/language/textUtils.js +44 -0
- package/lib/main.d.ts +27 -42
- package/lib/main.js +104 -81
- package/lib/model/csnRefs.js +2 -1
- package/lib/model/csnUtils.js +183 -285
- package/lib/model/revealInternalProperties.js +32 -9
- package/lib/model/sortViews.js +32 -31
- package/lib/optionProcessor.js +64 -57
- package/lib/render/.eslintrc.json +1 -1
- package/lib/render/DuplicateChecker.js +4 -7
- package/lib/render/manageConstraints.js +70 -2
- package/lib/render/toCdl.js +334 -339
- package/lib/render/toHdbcds.js +20 -16
- package/lib/render/toRename.js +44 -22
- package/lib/render/toSql.js +60 -54
- package/lib/render/utils/common.js +15 -1
- package/lib/render/utils/sql.js +20 -19
- package/lib/sql-identifier.js +6 -0
- package/lib/transform/db/.eslintrc.json +3 -2
- package/lib/transform/db/cdsPersistence.js +5 -15
- package/lib/transform/db/constraints.js +1 -1
- package/lib/transform/db/expansion.js +7 -6
- package/lib/transform/db/flattening.js +18 -19
- package/lib/transform/db/views.js +3 -3
- package/lib/transform/draft/.eslintrc.json +2 -2
- package/lib/transform/draft/db.js +6 -6
- package/lib/transform/draft/odata.js +6 -7
- package/lib/transform/forHanaNew.js +19 -22
- package/lib/transform/forOdataNew.js +13 -15
- package/lib/transform/localized.js +35 -25
- package/lib/transform/odata/toFinalBaseType.js +11 -9
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/odata/utils.js +1 -38
- package/lib/transform/transformUtilsNew.js +63 -77
- package/lib/transform/translateAssocsToJoins.js +6 -2
- package/lib/transform/universalCsn/.eslintrc.json +2 -2
- package/lib/transform/universalCsn/coreComputed.js +11 -6
- package/lib/transform/universalCsn/universalCsnEnricher.js +33 -5
- package/lib/utils/file.js +31 -21
- package/lib/utils/timetrace.js +20 -21
- package/package.json +34 -4
- package/share/messages/syntax-expected-integer.md +9 -8
- package/doc/ApiMigration.md +0 -237
- package/doc/CommandLineMigration.md +0 -58
- package/doc/ErrorMessages.md +0 -175
- package/doc/FioriAnnotations.md +0 -94
- package/doc/ODataTransformation.md +0 -273
- package/lib/backends.js +0 -529
- package/lib/fix_antlr4-8_warning.js +0 -56
package/lib/render/utils/sql.js
CHANGED
|
@@ -40,18 +40,18 @@ function renderReferentialConstraint(constraint, indent, toUpperCase, csn, optio
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
const renderAsHdbconstraint = options.transformation === 'hdbcds' ||
|
|
43
|
-
|
|
43
|
+
options.src === 'hdi' ||
|
|
44
44
|
(options.manageConstraints && options.manageConstraints.src === 'hdi');
|
|
45
45
|
|
|
46
|
-
const {
|
|
47
|
-
const forSqlite = options.
|
|
46
|
+
const { sqlMapping } = options;
|
|
47
|
+
const forSqlite = options.sqlDialect === 'sqlite';
|
|
48
48
|
let result = '';
|
|
49
49
|
result += `${indent}CONSTRAINT ${quoteId(constraint.identifier)}\n`;
|
|
50
50
|
if (renderAsHdbconstraint)
|
|
51
|
-
result += `${indent}ON ${quoteId(getResultingName(csn,
|
|
51
|
+
result += `${indent}ON ${quoteId(getResultingName(csn, sqlMapping, constraint.dependentTable))}\n`;
|
|
52
52
|
if (!alterConstraint) {
|
|
53
53
|
result += `${indent}FOREIGN KEY(${constraint.foreignKey.map(quoteId).join(', ')})\n`;
|
|
54
|
-
result += `${indent}REFERENCES ${quoteId(getResultingName(csn,
|
|
54
|
+
result += `${indent}REFERENCES ${quoteId(getResultingName(csn, sqlMapping, constraint.parentTable))}(${constraint.parentKey.map(quoteId).join(', ')})\n`;
|
|
55
55
|
const onDeleteRemark = constraint.onDeleteRemark ? ` -- ${constraint.onDeleteRemark}` : '';
|
|
56
56
|
|
|
57
57
|
// omit 'RESTRICT' action for ON UPDATE / ON DELETE, because it interferes with deferred constraint check
|
|
@@ -65,12 +65,13 @@ function renderReferentialConstraint(constraint, indent, toUpperCase, csn, optio
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
// constraint enforcement / validation must be switched off using sqlite pragma statement
|
|
68
|
-
|
|
68
|
+
// Does not include HDBCDS.
|
|
69
|
+
if (options.toSql && !forSqlite) {
|
|
69
70
|
result += `${indent}${!constraint.validated ? 'NOT ' : ''}VALIDATED\n`;
|
|
70
71
|
result += `${indent}${!constraint.enforced ? 'NOT ' : ''}ENFORCED\n`;
|
|
71
72
|
}
|
|
72
73
|
// for sqlite, the DEFERRABLE keyword is required
|
|
73
|
-
result += `${indent}${
|
|
74
|
+
result += `${indent}${forSqlite ? 'DEFERRABLE ' : ''}INITIALLY DEFERRED`;
|
|
74
75
|
return result;
|
|
75
76
|
}
|
|
76
77
|
|
|
@@ -85,9 +86,9 @@ function getIdentifierUtils(options) {
|
|
|
85
86
|
/**
|
|
86
87
|
* Return 'name' with appropriate "-quotes.
|
|
87
88
|
* Additionally perform the following conversions on 'name'
|
|
88
|
-
* If 'options.
|
|
89
|
+
* If 'options.sqlMapping' is 'plain'
|
|
89
90
|
* - replace '.' or '::' by '_'
|
|
90
|
-
* else if 'options.
|
|
91
|
+
* else if 'options.sqlMapping' is 'quoted'
|
|
91
92
|
* - replace '::' by '.'
|
|
92
93
|
* Complain about names that collide with known SQL keywords or functions
|
|
93
94
|
*
|
|
@@ -97,13 +98,13 @@ function getIdentifierUtils(options) {
|
|
|
97
98
|
function quoteSqlId(name) {
|
|
98
99
|
name = prepareIdentifier(name);
|
|
99
100
|
|
|
100
|
-
switch (options.
|
|
101
|
+
switch (options.sqlMapping) {
|
|
101
102
|
case 'plain':
|
|
102
|
-
return smartId(name, options.
|
|
103
|
+
return smartId(name, options.sqlDialect);
|
|
103
104
|
case 'quoted':
|
|
104
|
-
return delimitedId(name, options.
|
|
105
|
+
return delimitedId(name, options.sqlDialect);
|
|
105
106
|
case 'hdbcds':
|
|
106
|
-
return delimitedId(name, options.
|
|
107
|
+
return delimitedId(name, options.sqlDialect);
|
|
107
108
|
default:
|
|
108
109
|
return undefined;
|
|
109
110
|
}
|
|
@@ -111,9 +112,9 @@ function getIdentifierUtils(options) {
|
|
|
111
112
|
|
|
112
113
|
/**
|
|
113
114
|
* Prepare an identifier:
|
|
114
|
-
* If 'options.
|
|
115
|
+
* If 'options.sqlMapping' is 'plain'
|
|
115
116
|
* - replace '.' or '::' by '_'
|
|
116
|
-
* else if 'options.
|
|
117
|
+
* else if 'options.sqlMapping' is 'quoted'
|
|
117
118
|
* - replace '::' by '.'
|
|
118
119
|
*
|
|
119
120
|
* @param {string} name Identifier to prepare
|
|
@@ -121,11 +122,11 @@ function getIdentifierUtils(options) {
|
|
|
121
122
|
*/
|
|
122
123
|
function prepareIdentifier(name) {
|
|
123
124
|
// Sanity check
|
|
124
|
-
if (options.
|
|
125
|
-
throw new ModelError(`Not expecting ${options.
|
|
125
|
+
if (options.sqlDialect === 'sqlite' && options.sqlMapping !== 'plain')
|
|
126
|
+
throw new ModelError(`Not expecting ${options.sqlMapping} names for 'sqlite' dialect`);
|
|
126
127
|
|
|
127
128
|
|
|
128
|
-
switch (options.
|
|
129
|
+
switch (options.sqlMapping) {
|
|
129
130
|
case 'plain':
|
|
130
131
|
return name.replace(/(\.|::)/g, '_');
|
|
131
132
|
case 'quoted':
|
|
@@ -133,7 +134,7 @@ function getIdentifierUtils(options) {
|
|
|
133
134
|
case 'hdbcds':
|
|
134
135
|
return name;
|
|
135
136
|
default:
|
|
136
|
-
throw new ModelError(`No matching rendering found for naming mode ${options.
|
|
137
|
+
throw new ModelError(`No matching rendering found for naming mode ${options.sqlMapping}`);
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
}
|
package/lib/sql-identifier.js
CHANGED
|
@@ -45,6 +45,12 @@ const sqlDialects = {
|
|
|
45
45
|
effectiveName: name => name,
|
|
46
46
|
asDelimitedId: name => `"${ name.replace(/"/g, '""')}"`,
|
|
47
47
|
},
|
|
48
|
+
postgres: {
|
|
49
|
+
regularRegex: /^[A-Za-z_][A-Za-z_$0-9]*$/,
|
|
50
|
+
reservedWords: keywords.postgres,
|
|
51
|
+
effectiveName: name => name.toLowerCase(),
|
|
52
|
+
asDelimitedId: name => `"${ name.replace(/"/g, '""')}"`,
|
|
53
|
+
},
|
|
48
54
|
hana: {
|
|
49
55
|
regularRegex: /^[A-Za-z_][A-Za-z_$#0-9]*$/,
|
|
50
56
|
reservedWords: keywords.hana,
|
|
@@ -17,15 +17,16 @@
|
|
|
17
17
|
"sonarjs/prefer-single-boolean-return": "off",
|
|
18
18
|
// Very whiny and nitpicky
|
|
19
19
|
"sonarjs/cognitive-complexity": "off",
|
|
20
|
+
"sonarjs/no-duplicate-string": "off",
|
|
20
21
|
// Does not recognize TS types
|
|
21
22
|
"jsdoc/no-undefined-types": "off"
|
|
22
23
|
},
|
|
23
24
|
"parserOptions": {
|
|
24
|
-
"ecmaVersion":
|
|
25
|
+
"ecmaVersion": 2020,
|
|
25
26
|
"sourceType": "script"
|
|
26
27
|
},
|
|
27
28
|
"env": {
|
|
28
|
-
"
|
|
29
|
+
"es2020": true,
|
|
29
30
|
"node": true
|
|
30
31
|
},
|
|
31
32
|
"settings": {
|
|
@@ -81,26 +81,16 @@ function getAssocToSkippedIgnorer(csn, options, messageFunctions) {
|
|
|
81
81
|
* @todo Why do we check for @cds.persistence.exists here if the parent-function only calls this for skip/abstract?
|
|
82
82
|
*/
|
|
83
83
|
function ignore(member, memberName, prop, path) {
|
|
84
|
-
if (options.sqlDialect === 'hana' &&
|
|
85
|
-
|
|
84
|
+
if (options.sqlDialect === 'hana' &&
|
|
85
|
+
!member._ignore && member.target &&
|
|
86
|
+
isAssocOrComposition(member.type) &&
|
|
87
|
+
!isPersistedOnDatabase(csn.definitions[member.target])) {
|
|
86
88
|
info(null, path,
|
|
87
|
-
{ target: member.target, anno:
|
|
89
|
+
{ target: member.target, anno: '@cds.persistence.skip' },
|
|
88
90
|
'Association has been removed as it\'s target $(TARGET) is annotated with $(ANNO)');
|
|
89
91
|
member._ignore = true;
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Check whether the given artifact is an unreachable association target because it will not "realy" hit the database:
|
|
95
|
-
* - @cds.persistence.skip/exists
|
|
96
|
-
* - abstract
|
|
97
|
-
*
|
|
98
|
-
* @param {CSN.Artifact} art
|
|
99
|
-
* @returns {boolean}
|
|
100
|
-
*/
|
|
101
|
-
function isUnreachableAssociationTarget(art) {
|
|
102
|
-
return !isPersistedOnDatabase(art) || hasAnnotationValue(art, exists);
|
|
103
|
-
}
|
|
104
94
|
}
|
|
105
95
|
|
|
106
96
|
/**
|
|
@@ -517,7 +517,7 @@ function createReferentialConstraints(csn, options) {
|
|
|
517
517
|
onDeleteRemark = `Up_ link for Composition "${$foreignKeyConstraint.upLinkFor}" implies existential dependency`;
|
|
518
518
|
referentialConstraints[`${getResultingName(csn, 'quoted', artifactName)}_${$foreignKeyConstraint.sourceAssociation}`] = {
|
|
519
519
|
// constraint identifier start with `c__` to avoid name clashes
|
|
520
|
-
identifier: `c__${getResultingName(csn, options.
|
|
520
|
+
identifier: `c__${getResultingName(csn, options.sqlMapping, artifactName)}_${$foreignKeyConstraint.sourceAssociation}`,
|
|
521
521
|
foreignKey: dependentKey,
|
|
522
522
|
parentKey,
|
|
523
523
|
dependentTable: artifactName,
|
|
@@ -24,10 +24,11 @@ const { forEach } = require('../../utils/objectUtils');
|
|
|
24
24
|
* @param {object} iterateOptions
|
|
25
25
|
*/
|
|
26
26
|
function expandStructureReferences(csn, options, pathDelimiter, { error, info, throwWithAnyError }, iterateOptions = {}) {
|
|
27
|
+
const csnUtils = getUtils(csn);
|
|
27
28
|
const {
|
|
28
|
-
isStructured, get$combined,
|
|
29
|
-
} =
|
|
30
|
-
let { effectiveType, inspectRef } =
|
|
29
|
+
isStructured, get$combined, getFinalBaseTypeWithProps, getServiceName,
|
|
30
|
+
} = csnUtils;
|
|
31
|
+
let { effectiveType, inspectRef } = csnUtils;
|
|
31
32
|
|
|
32
33
|
if (isBetaEnabled(options, 'nestedProjections'))
|
|
33
34
|
rewriteExpandInline();
|
|
@@ -239,13 +240,13 @@ function expandStructureReferences(csn, options, pathDelimiter, { error, info, t
|
|
|
239
240
|
*/
|
|
240
241
|
function nextBase(parent, base) {
|
|
241
242
|
if (parent.ref) {
|
|
242
|
-
const finalBaseType =
|
|
243
|
+
const finalBaseType = getFinalBaseTypeWithProps(parent._art.type);
|
|
243
244
|
const art = parent._art;
|
|
244
245
|
|
|
245
|
-
if (finalBaseType === 'cds.Association' || finalBaseType === 'cds.Composition')
|
|
246
|
+
if (finalBaseType && (finalBaseType.type === 'cds.Association' || finalBaseType.type === 'cds.Composition'))
|
|
246
247
|
return csn.definitions[art.target].elements;
|
|
247
248
|
|
|
248
|
-
return art.elements || finalBaseType
|
|
249
|
+
return art.elements || finalBaseType?.elements;
|
|
249
250
|
}
|
|
250
251
|
|
|
251
252
|
return base;
|
|
@@ -47,6 +47,7 @@ function removeLeadingSelf(csn) {
|
|
|
47
47
|
* @param {object} iterateOptions
|
|
48
48
|
*/
|
|
49
49
|
function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOptions = {}) {
|
|
50
|
+
const typeCache = Object.create(null); // TODO: Argument as well?
|
|
50
51
|
/**
|
|
51
52
|
* Remove .localized from the element and any sub-elements
|
|
52
53
|
*
|
|
@@ -67,8 +68,8 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
|
|
|
67
68
|
stack.push(...Object.values(current.elements));
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
|
-
const { toFinalBaseType } = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
71
|
-
const { getServiceName,
|
|
71
|
+
const { toFinalBaseType, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
72
|
+
const { getServiceName, getFinalBaseTypeWithProps } = csnUtils;
|
|
72
73
|
|
|
73
74
|
// We don't want to iterate over actions
|
|
74
75
|
if (iterateOptions.skipDict && !iterateOptions.skipDict.actions)
|
|
@@ -82,14 +83,14 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
|
|
|
82
83
|
cast: (parent, prop, cast, path) => {
|
|
83
84
|
// Resolve cast already - we otherwise lose .localized
|
|
84
85
|
if (cast.type && !isBuiltinType(cast.type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(cast.type, path) && !isODataItems(cast.type)))
|
|
85
|
-
toFinalBaseType(parent.cast, resolved, true);
|
|
86
|
+
toFinalBaseType(parent.cast, resolved, true, typeCache);
|
|
86
87
|
},
|
|
87
88
|
// @ts-ignore
|
|
88
89
|
type: (parent, prop, type, path) => {
|
|
89
90
|
if (options.toOdata && parent.kind && parent.kind in ignoreOdataKinds)
|
|
90
91
|
return;
|
|
91
92
|
if (!isBuiltinType(type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(type, path) && !isODataItems(type))) {
|
|
92
|
-
toFinalBaseType(parent, resolved);
|
|
93
|
+
toFinalBaseType(parent, resolved, true, typeCache);
|
|
93
94
|
// structured types might not have the child-types replaced.
|
|
94
95
|
// Drill down to ensure this.
|
|
95
96
|
if (parent.elements) {
|
|
@@ -98,7 +99,7 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
|
|
|
98
99
|
const elements = stack.pop();
|
|
99
100
|
for (const e of Object.values(elements)) {
|
|
100
101
|
if (e.type && !isBuiltinType(e.type))
|
|
101
|
-
toFinalBaseType(e, resolved);
|
|
102
|
+
toFinalBaseType(e, resolved, true, typeCache);
|
|
102
103
|
|
|
103
104
|
if (e.elements)
|
|
104
105
|
stack.push(e.elements);
|
|
@@ -152,11 +153,11 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
|
|
|
152
153
|
* @returns {boolean}
|
|
153
154
|
*/
|
|
154
155
|
function isODataV4BuiltinFromService(typeName, path) {
|
|
155
|
-
if (!options.toOdata || (options.
|
|
156
|
+
if (!options.toOdata || (options.odataVersion === 'v2') || typeof typeName !== 'string')
|
|
156
157
|
return false;
|
|
157
158
|
|
|
158
159
|
const typeServiceName = getServiceName(typeName);
|
|
159
|
-
const finalBaseType =
|
|
160
|
+
const finalBaseType = getFinalBaseTypeWithProps(typeName)?.type;
|
|
160
161
|
// we need the service of the current definition
|
|
161
162
|
const currDefServiceName = getServiceName(path[1]);
|
|
162
163
|
|
|
@@ -266,9 +267,8 @@ function flattenAllStructStepsInRefs(csn, options, resolved, pathDelimiter, iter
|
|
|
266
267
|
* @param {object} iterateOptions
|
|
267
268
|
*/
|
|
268
269
|
function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}) {
|
|
269
|
-
const {
|
|
270
|
-
const {
|
|
271
|
-
const { effectiveType } = csnRefs(csn);
|
|
270
|
+
const { flattenStructuredElement, csnUtils } = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
271
|
+
const { isAssocOrComposition, effectiveType } = csnUtils;
|
|
272
272
|
const transformers = {
|
|
273
273
|
elements: flatten,
|
|
274
274
|
};
|
|
@@ -299,11 +299,13 @@ function flattenElements(csn, options, pathDelimiter, error, iterateOptions = {}
|
|
|
299
299
|
const flatElems = flattenStructuredElement(element, elementName, [], path.concat([ 'elements', elementName ]));
|
|
300
300
|
|
|
301
301
|
for (const flatElemName in flatElems) {
|
|
302
|
-
if (parent[prop][flatElemName])
|
|
302
|
+
if (parent[prop][flatElemName]) {
|
|
303
303
|
// TODO: combine message ID with generated FK duplicate
|
|
304
|
-
// do the duplicate check in the
|
|
304
|
+
// do the duplicate check in the construct callback, requires to mark generated flat elements,
|
|
305
305
|
// check: Error location should be the existing element like @odata.foreignKey4
|
|
306
|
-
error(
|
|
306
|
+
error('name-duplicate-element', path.concat([ 'elements', elementName ]),
|
|
307
|
+
{ '#': 'flatten-element-exist', name: flatElemName });
|
|
308
|
+
}
|
|
307
309
|
|
|
308
310
|
const flatElement = flatElems[flatElemName];
|
|
309
311
|
|
|
@@ -609,8 +611,7 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
|
|
|
609
611
|
((options.toOdata && isDeepEqual(element, parent[prop][fk[0]], true)) ||
|
|
610
612
|
!options.toOdata)) {
|
|
611
613
|
// error location is the colliding element
|
|
612
|
-
error(
|
|
613
|
-
'Generated foreign key element $(NAME) for association $(ART) conflicts with existing element');
|
|
614
|
+
error('name-duplicate-element', eltPath, { '#': 'flatten-fkey-exists', name: fk[0], art: elementName });
|
|
614
615
|
}
|
|
615
616
|
// attach a proper $path
|
|
616
617
|
setProp(element, '$path', eltPath);
|
|
@@ -619,10 +620,8 @@ function handleManagedAssociationsAndCreateForeignKeys(csn, options, error, path
|
|
|
619
620
|
|
|
620
621
|
// check for duplicate foreign keys
|
|
621
622
|
Object.entries(refCount).forEach(([ name, occ ]) => {
|
|
622
|
-
if (occ > 1)
|
|
623
|
-
error(
|
|
624
|
-
'Duplicate definition of foreign key element $(NAME)');
|
|
625
|
-
}
|
|
623
|
+
if (occ > 1)
|
|
624
|
+
error('name-duplicate-element', eltPath, { '#': 'flatten-fkey-gen', name, art: elementName });
|
|
626
625
|
});
|
|
627
626
|
if (element.keys) {
|
|
628
627
|
element.keys.forEach((key, i) => {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const {
|
|
4
4
|
getUtils, cloneCsnNonDict, applyTransformationsOnNonDictionary,
|
|
5
5
|
} = require('../../model/csnUtils');
|
|
6
|
-
const { implicitAs
|
|
6
|
+
const { implicitAs } = require('../../model/csnRefs');
|
|
7
7
|
const { isBetaEnabled } = require('../../base/model');
|
|
8
8
|
const { ModelError } = require('../../base/error');
|
|
9
9
|
|
|
@@ -69,9 +69,9 @@ function usesMixinAssociation(query, association, associationName) {
|
|
|
69
69
|
function getViewTransformer(csn, options, messageFunctions, transformCommon) {
|
|
70
70
|
const {
|
|
71
71
|
get$combined, isAssocOrComposition,
|
|
72
|
+
inspectRef, queryOrMain, // csnRefs
|
|
72
73
|
} = getUtils(csn);
|
|
73
|
-
const
|
|
74
|
-
const pathDelimiter = (options.forHana.names === 'hdbcds') ? '.' : '_';
|
|
74
|
+
const pathDelimiter = options.forHana && (options.sqlMapping === 'hdbcds') ? '.' : '_';
|
|
75
75
|
const { error, info } = messageFunctions;
|
|
76
76
|
const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
|
|
77
77
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const {
|
|
4
|
-
hasAnnotationValue,
|
|
4
|
+
hasAnnotationValue, getServiceNames, forEachDefinition,
|
|
5
5
|
getResultingName, forEachMemberRecursively,
|
|
6
6
|
} = require('../../model/csnUtils');
|
|
7
7
|
const { setProp, isDeprecatedEnabled } = require('../../base/model');
|
|
@@ -19,15 +19,15 @@ const booleanBuiltin = 'cds.Boolean';
|
|
|
19
19
|
* @param {object} messageFunctions
|
|
20
20
|
*/
|
|
21
21
|
function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
22
|
-
const draftSuffix = isDeprecatedEnabled(options, '
|
|
22
|
+
const draftSuffix = isDeprecatedEnabled(options, '_generatedEntityNameWithUnderscore') ? '_drafts' : '.drafts';
|
|
23
23
|
// All services of the model - needed for drafts
|
|
24
24
|
const allServices = getServiceNames(csn);
|
|
25
25
|
const draftRoots = new WeakMap();
|
|
26
26
|
const {
|
|
27
27
|
createForeignKeyElement, createAndAddDraftAdminDataProjection, createScalarElement, createAssociationElement,
|
|
28
|
-
addElement, copyAndAddElement, createAssociationPathComparison,
|
|
28
|
+
addElement, copyAndAddElement, createAssociationPathComparison, csnUtils,
|
|
29
29
|
} = getTransformers(csn, options, pathDelimiter);
|
|
30
|
-
const { getCsnDef, isComposition } =
|
|
30
|
+
const { getCsnDef, isComposition } = csnUtils;
|
|
31
31
|
const { error, warning } = messageFunctions;
|
|
32
32
|
|
|
33
33
|
forEachDefinition(csn, generateDraft);
|
|
@@ -155,7 +155,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
155
155
|
'Generated entity $(NAME) conflicts with existing artifact');
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
const persistenceName = getResultingName(csn, options.
|
|
158
|
+
const persistenceName = getResultingName(csn, options.sqlMapping, draftsArtifactName);
|
|
159
159
|
// Duplicate the artifact as a draft shadow entity
|
|
160
160
|
if (csn.definitions[persistenceName]) {
|
|
161
161
|
const definingDraftRoot = draftRoots.get(csn.definitions[persistenceName]);
|
|
@@ -187,7 +187,7 @@ function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
|
|
|
187
187
|
for (const elemName in artifact.elements) {
|
|
188
188
|
const origElem = artifact.elements[elemName];
|
|
189
189
|
let elem;
|
|
190
|
-
if ((isDeprecatedEnabled(options, '
|
|
190
|
+
if ((isDeprecatedEnabled(options, '_renderVirtualElements') && origElem.virtual) || !origElem.virtual)
|
|
191
191
|
elem = copyAndAddElement(origElem, draftsArtifact, draftsArtifactName, elemName)[elemName];
|
|
192
192
|
if (elem) {
|
|
193
193
|
// Remove "virtual" - cap/issues 4956
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { forEachDefinition,
|
|
3
|
+
const { forEachDefinition, getServiceNames } = require('../../model/csnUtils');
|
|
4
4
|
const { forEach } = require('../../utils/objectUtils');
|
|
5
5
|
const { isArtifactInSomeService, getServiceOfArtifact } = require('../odata/utils');
|
|
6
6
|
const { getTransformers } = require('../transformUtilsNew');
|
|
@@ -30,15 +30,14 @@ function generateDrafts(csn, options, services) {
|
|
|
30
30
|
createAssociationElement, createAssociationPathComparison,
|
|
31
31
|
addElement, createAction, assignAction,
|
|
32
32
|
resetAnnotation,
|
|
33
|
+
csnUtils,
|
|
33
34
|
} = getTransformers(csn, options);
|
|
34
|
-
|
|
35
35
|
const {
|
|
36
36
|
getFinalType,
|
|
37
37
|
getServiceName,
|
|
38
38
|
hasAnnotationValue,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
} = getUtils(csn);
|
|
39
|
+
getFinalBaseTypeWithProps,
|
|
40
|
+
} = csnUtils;
|
|
42
41
|
|
|
43
42
|
const { error, info } = makeMessageFunction(csn, options, 'for.odata');
|
|
44
43
|
|
|
@@ -197,8 +196,8 @@ function generateDrafts(csn, options, services) {
|
|
|
197
196
|
stack.push(elem);
|
|
198
197
|
}
|
|
199
198
|
else if (elem.type) { // types - possibly structured
|
|
200
|
-
const typeDef =
|
|
201
|
-
if (typeDef
|
|
199
|
+
const typeDef = getFinalBaseTypeWithProps(elem.type);
|
|
200
|
+
if (typeDef?.elements)
|
|
202
201
|
stack.push(typeDef);
|
|
203
202
|
}
|
|
204
203
|
});
|
|
@@ -41,9 +41,7 @@ function forEachDefinition(csn, cb) {
|
|
|
41
41
|
* in HANA CDS style, used by 'toHana', toSql' and 'toRename'.
|
|
42
42
|
* The behavior is controlled by the following options:
|
|
43
43
|
* options = {
|
|
44
|
-
*
|
|
45
|
-
* forHana.alwaysResolveDerivedTypes // Always resolve derived type chains (by default, this is only
|
|
46
|
-
* // done for 'quoted' names). FIXME: Should always be done in general.
|
|
44
|
+
* sqlMapping // See the behavior of 'sqlMapping' in toHana, toSql and toRename
|
|
47
45
|
* }
|
|
48
46
|
* The result model will always have 'options.forHana' set, to indicate that these transformations have happened.
|
|
49
47
|
* The following transformations are made:
|
|
@@ -69,9 +67,9 @@ function forEachDefinition(csn, cb) {
|
|
|
69
67
|
* - (110) Actions and functions (bound or unbound) are ignored.
|
|
70
68
|
* - (120) (a) Services become contexts.
|
|
71
69
|
* - (130) (not for to.hdbcds with hdbcds names): Elements having structured types are flattened into
|
|
72
|
-
* multiple elements (using '_' or '.' as name separator, depending on '
|
|
70
|
+
* multiple elements (using '_' or '.' as name separator, depending on 'sqlMapping').
|
|
73
71
|
* - (140) (not for to.hdbcds with hdbcds names): Managed associations get explicit ON-conditions, with
|
|
74
|
-
* generated foreign key elements (also using '_' or '.' as name separator, depending on '
|
|
72
|
+
* generated foreign key elements (also using '_' or '.' as name separator, depending on 'sqlMapping').
|
|
75
73
|
* - (150) (a) Elements from inherited (included) entities are copied into the receiving entity
|
|
76
74
|
* (b) The 'include' property is removed from entities.
|
|
77
75
|
* - (160) Projections become views, with MIXINs for association elements (adding $projection where
|
|
@@ -94,7 +92,7 @@ function forEachDefinition(csn, cb) {
|
|
|
94
92
|
* (d) Managed association entries in ORDER BY
|
|
95
93
|
* - (240) All artifacts (a), elements, foreign keys, parameters (b) that have a DB representation are annotated
|
|
96
94
|
* with their database name (as '@cds.persistence.name') according to the naming convention chosen
|
|
97
|
-
* in 'options.
|
|
95
|
+
* in 'options.sqlMapping'.
|
|
98
96
|
* - (250) Remove name space definitions again (only in forHanaNew). Maybe we can omit inserting namespace definitions
|
|
99
97
|
* completely (TODO)
|
|
100
98
|
*
|
|
@@ -108,17 +106,14 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
108
106
|
/** @type {CSN.Model} */
|
|
109
107
|
let csn = cloneCsnNonDict(inputModel, options);
|
|
110
108
|
|
|
111
|
-
|
|
112
|
-
|
|
113
109
|
checkCSNVersion(csn, options);
|
|
114
110
|
|
|
115
|
-
const pathDelimiter = (options.
|
|
111
|
+
const pathDelimiter = (options.sqlMapping === 'hdbcds') ? '.' : '_';
|
|
116
112
|
|
|
117
113
|
let message, error, warning, info; // message functions
|
|
118
114
|
/** @type {() => void} */
|
|
119
115
|
let throwWithAnyError;
|
|
120
116
|
let artifactRef, inspectRef, effectiveType, get$combined,
|
|
121
|
-
getFinalBaseType, // csnUtils (csnRefs)
|
|
122
117
|
addDefaultTypeFacets, expandStructsInExpression; // transformUtils
|
|
123
118
|
|
|
124
119
|
bindCsnReference();
|
|
@@ -130,14 +125,14 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
130
125
|
bindCsnReference();
|
|
131
126
|
}
|
|
132
127
|
|
|
133
|
-
const dialect = options.
|
|
128
|
+
const dialect = options.sqlDialect;
|
|
134
129
|
const doA2J = !(options.transformation === 'hdbcds' && options.sqlMapping === 'hdbcds');
|
|
135
130
|
if (!doA2J)
|
|
136
131
|
forEachDefinition(csn, handleMixinOnConditions);
|
|
137
132
|
|
|
138
133
|
// Run validations on CSN - each validator function has access to the message functions and the inspect ref via this
|
|
139
134
|
const cleanup = validate.forHana(csn, {
|
|
140
|
-
message, error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options,
|
|
135
|
+
message, error, warning, info, inspectRef, effectiveType, artifactRef, csnUtils: getUtils(csn), csn, options, isAspect
|
|
141
136
|
});
|
|
142
137
|
|
|
143
138
|
// Needs to happen before tuple expansion, so the newly generated WHERE-conditions have it applied
|
|
@@ -222,6 +217,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
222
217
|
flattenStructuredElement,
|
|
223
218
|
flattenStructStepsInRef,
|
|
224
219
|
isAssociationOperand, isDollarSelfOrProjectionOperand,
|
|
220
|
+
csnUtils,
|
|
225
221
|
} = transformUtils.getTransformers(csn, options, pathDelimiter);
|
|
226
222
|
|
|
227
223
|
const {
|
|
@@ -229,7 +225,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
229
225
|
isAssocOrComposition,
|
|
230
226
|
addStringAnnotationTo,
|
|
231
227
|
cloneWithTransformations,
|
|
232
|
-
} =
|
|
228
|
+
} = csnUtils;
|
|
233
229
|
|
|
234
230
|
// (000) Rename primitive types, make UUID a String
|
|
235
231
|
transformCsn(csn, {
|
|
@@ -409,8 +405,9 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
409
405
|
|
|
410
406
|
function bindCsnReference(){
|
|
411
407
|
({ error, warning, info, message, throwWithAnyError } = makeMessageFunction(csn, options, moduleName));
|
|
412
|
-
({ artifactRef, inspectRef, effectiveType, getFinalBaseType, get$combined } = getUtils(csn));
|
|
413
408
|
({ addDefaultTypeFacets, expandStructsInExpression } = transformUtils.getTransformers(csn, options, pathDelimiter));
|
|
409
|
+
// TODO: Can we use csnUtils of the call above (transformUtils.getTransformers)?
|
|
410
|
+
({ artifactRef, inspectRef, effectiveType, get$combined } = getUtils(csn));
|
|
414
411
|
}
|
|
415
412
|
|
|
416
413
|
function bindCsnReferenceOnly(){
|
|
@@ -509,7 +506,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
509
506
|
function recursivelyApplyCommon(artifact, artifactName) {
|
|
510
507
|
if (!artifact._ignore) {
|
|
511
508
|
if (artifact.kind !== 'service' && artifact.kind !== 'context')
|
|
512
|
-
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.
|
|
509
|
+
addStringAnnotationTo('@cds.persistence.name', getArtifactDatabaseNameOf(artifactName, options.sqlMapping, csn, options.sqlDialect), artifact);
|
|
513
510
|
|
|
514
511
|
forEachMemberRecursively(artifact, (member, memberName, property, path) => {
|
|
515
512
|
transformCommon(member, memberName, path);
|
|
@@ -517,7 +514,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
517
514
|
// Virtual elements in entities and types are not annotated, as they have no DB representation.
|
|
518
515
|
// In views they are, as we generate a null expression for them (null as <colname>)
|
|
519
516
|
if ((!member.virtual || artifact.query))
|
|
520
|
-
addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.
|
|
517
|
+
addStringAnnotationTo('@cds.persistence.name', getElementDatabaseNameOf(memberName, options.sqlMapping, options.sqlDialect), member);
|
|
521
518
|
}, [ 'definitions', artifactName ]);
|
|
522
519
|
}
|
|
523
520
|
}
|
|
@@ -543,7 +540,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
543
540
|
function transformSelfInBacklinks(artifact, artifactName, dummy, path) {
|
|
544
541
|
// Fixme: For toHana mixins must be transformed, for toSql -d hana
|
|
545
542
|
// mixin elements must be transformed, why can't toSql also use mixins?
|
|
546
|
-
if(artifact.kind === 'entity' || artifact.query || (options.
|
|
543
|
+
if(artifact.kind === 'entity' || artifact.query || (options.forHana && options.sqlMapping === 'hdbcds' && artifact.kind === 'type'))
|
|
547
544
|
doit(artifact.elements, path.concat([ 'elements' ]));
|
|
548
545
|
if (artifact.query && artifact.query.SELECT && artifact.query.SELECT.mixin)
|
|
549
546
|
doit(artifact.query.SELECT.mixin, path.concat([ 'query', 'SELECT', 'mixin' ]));
|
|
@@ -574,7 +571,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
574
571
|
// For HANA: Report an error on
|
|
575
572
|
// - view with parameters that has an element of type association/composition
|
|
576
573
|
// - association that points to entity with parameters
|
|
577
|
-
if (options.
|
|
574
|
+
if (options.sqlDialect === 'hana' && member.target && isAssocOrComposition(member.type) && !isBetaEnabled(options, 'assocsWithParams')) {
|
|
578
575
|
if (artifact.params) {
|
|
579
576
|
// HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
|
|
580
577
|
// SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
|
|
@@ -617,7 +614,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
617
614
|
sql: 'Table-like entities with parameters are not supported for conversion to SQL',
|
|
618
615
|
});
|
|
619
616
|
}
|
|
620
|
-
else if (options.
|
|
617
|
+
else if (options.sqlDialect === 'sqlite') { // view with params
|
|
621
618
|
// Allow with plain
|
|
622
619
|
error(null, [ 'definitions', artifactName ], `SQLite does not support entities with parameters`);
|
|
623
620
|
}
|
|
@@ -626,8 +623,8 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
626
623
|
if (pname.match(/\W/g) || pname.match(/^\d/) || pname.match(/^_/)) { // parameter name must be regular SQL identifier
|
|
627
624
|
warning(null, [ 'definitions', artifactName, 'params', pname ], `Expecting regular SQL-Identifier`);
|
|
628
625
|
}
|
|
629
|
-
else if (options.
|
|
630
|
-
warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.
|
|
626
|
+
else if (options.sqlMapping !== 'plain' && pname.toUpperCase() !== pname) { // not plain mode: param name must be all upper
|
|
627
|
+
warning(null, [ 'definitions', artifactName, 'params', pname ], { name: options.sqlMapping },
|
|
631
628
|
'Expecting parameter to be uppercase in naming mode $(NAME)');
|
|
632
629
|
}
|
|
633
630
|
}
|
|
@@ -1048,7 +1045,7 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
|
|
|
1048
1045
|
* @returns {boolean}
|
|
1049
1046
|
*/
|
|
1050
1047
|
function isMaxParameterLengthRestricted(type) {
|
|
1051
|
-
return !(options.toSql && type === 'cds.String' && (options.
|
|
1048
|
+
return !(options.toSql && type === 'cds.String' && (options.sqlDialect === 'sqlite' || options.sqlDialect === 'plain'));
|
|
1052
1049
|
}
|
|
1053
1050
|
|
|
1054
1051
|
/**
|