@sap/cds-compiler 6.9.3 → 7.0.1
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 +76 -2
- package/bin/cdsc.js +4 -33
- package/doc/IncompatibleChanges_v7.md +639 -0
- package/lib/api/main.js +4 -56
- package/lib/api/options.js +5 -15
- package/lib/api/validate.js +1 -0
- package/lib/base/builtins.js +1 -2
- package/lib/base/csnRefs.js +2 -6
- package/lib/base/message-registry.js +82 -76
- package/lib/base/messages.js +8 -5
- package/lib/base/optionProcessor.js +2 -72
- package/lib/base/specialOptions.js +20 -17
- package/lib/checks/defaultValues.js +1 -39
- package/lib/checks/hasPersistedElements.js +19 -3
- package/lib/checks/parameters.js +0 -34
- package/lib/checks/selectItems.js +2 -38
- package/lib/checks/typeParameters.js +162 -0
- package/lib/checks/validator.js +5 -8
- package/lib/compiler/assert-consistency.js +19 -5
- package/lib/compiler/checks.js +47 -43
- package/lib/compiler/define.js +6 -6
- package/lib/compiler/extend.js +102 -111
- package/lib/compiler/generate.js +4 -8
- package/lib/compiler/populate.js +4 -7
- package/lib/compiler/propagator.js +9 -9
- package/lib/compiler/resolve.js +205 -7
- package/lib/compiler/shared.js +76 -82
- package/lib/compiler/tweak-assocs.js +102 -22
- package/lib/compiler/utils.js +57 -12
- package/lib/compiler/xpr-rewrite.js +2 -15
- package/lib/edm/annotations/edmJson.js +14 -10
- package/lib/edm/annotations/genericTranslation.js +3 -1
- package/lib/edm/annotations/preprocessAnnotations.js +9 -26
- package/lib/edm/csn2edm.js +27 -20
- package/lib/edm/edmUtils.js +25 -0
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +2237 -2241
- package/lib/gen/Dictionary.json +17 -2
- package/lib/json/from-csn.js +67 -52
- package/lib/json/to-csn.js +28 -25
- package/lib/language/textUtils.js +0 -13
- package/lib/main.d.ts +22 -59
- package/lib/main.js +1 -1
- package/lib/model/csnUtils.js +9 -8
- package/lib/parsers/AstBuildingParser.js +45 -55
- package/lib/parsers/Lexer.js +2 -0
- package/lib/parsers/identifiers.js +0 -9
- package/lib/render/toCdl.js +41 -40
- package/lib/render/toSql.js +8 -1
- package/lib/render/utils/common.js +1 -1
- package/lib/render/utils/sql.js +2 -3
- package/lib/tool-lib/enrichCsn.js +1 -2
- package/lib/transform/db/applyTransformations.js +7 -5
- package/lib/transform/db/assertUnique.js +8 -51
- package/lib/transform/db/associations.js +1 -1
- package/lib/transform/db/cdsPersistence.js +1 -15
- package/lib/transform/db/expansion.js +9 -12
- package/lib/transform/db/flattening.js +1 -1
- package/lib/transform/db/groupByOrderBy.js +0 -16
- package/lib/transform/db/views.js +57 -161
- package/lib/transform/draft/db.js +2 -2
- package/lib/transform/forOdata.js +25 -14
- package/lib/transform/forRelationalDB.js +93 -301
- package/lib/transform/localized.js +33 -102
- package/lib/transform/odata/flattening.js +11 -2
- package/lib/transform/transformUtils.js +25 -3
- package/lib/transform/universalCsn/universalCsnEnricher.js +1 -2
- package/package.json +2 -2
- package/lib/render/toHdbcds.js +0 -1810
package/lib/base/messages.js
CHANGED
|
@@ -100,7 +100,7 @@ function severityChangeMarker( msg, config ) {
|
|
|
100
100
|
// Do not include testMode here, no marker for configurableFor: 'test'
|
|
101
101
|
return '‹↓›';
|
|
102
102
|
}
|
|
103
|
-
else if (centralMessages[msg.messageId]?.errorFor?.includes( '
|
|
103
|
+
else if (centralMessages[msg.messageId]?.errorFor?.includes( 'v8' )) {
|
|
104
104
|
return '‹↑›';
|
|
105
105
|
}
|
|
106
106
|
}
|
|
@@ -223,7 +223,6 @@ const severitySpecs = {
|
|
|
223
223
|
// Message IDs raised for security-relevant annotate statements with non-existing targets.
|
|
224
224
|
// Downgraded to warning when noErrorForUnknownAnnotateTarget option is set.
|
|
225
225
|
const securityAnnotateTargetIds = new Set([
|
|
226
|
-
'ext-undefined-art-sec',
|
|
227
226
|
'ext-undefined-def-sec',
|
|
228
227
|
'ext-undefined-element-sec',
|
|
229
228
|
'ext-undefined-action-sec',
|
|
@@ -262,7 +261,7 @@ function reclassifiedSeverity( msg, options, moduleName ) {
|
|
|
262
261
|
if (errorFor.includes(moduleName))
|
|
263
262
|
return 'Error';
|
|
264
263
|
|
|
265
|
-
if (errorFor.includes('
|
|
264
|
+
if (errorFor.includes('v8') && isBetaEnabled(options, 'v8preview')) {
|
|
266
265
|
severity = 'Error';
|
|
267
266
|
if (!isDowngradable(msg.messageId, moduleName, options))
|
|
268
267
|
return severity;
|
|
@@ -833,7 +832,8 @@ const paramsTransform = {
|
|
|
833
832
|
expecting: transformManyWith( tokenSymbol ),
|
|
834
833
|
// msg: m => m,
|
|
835
834
|
$reviewed: ignoreTextTransform,
|
|
836
|
-
|
|
835
|
+
compiler: quote.direct, // TODO: if VERSION is w/o quotes, use VERSION
|
|
836
|
+
version: quote.single, // TODO: use quote.direct
|
|
837
837
|
};
|
|
838
838
|
|
|
839
839
|
function asDelimitedId( id ) {
|
|
@@ -1461,6 +1461,7 @@ function shortArtName( art ) {
|
|
|
1461
1461
|
}
|
|
1462
1462
|
|
|
1463
1463
|
function artName( art, omit ) {
|
|
1464
|
+
const orig = art;
|
|
1464
1465
|
let suffix = 0;
|
|
1465
1466
|
while (!art.name && art._outer && art.kind !== '$annotation') {
|
|
1466
1467
|
++suffix;
|
|
@@ -1500,7 +1501,9 @@ function artName( art, omit ) {
|
|
|
1500
1501
|
if (art.kind === '$self')
|
|
1501
1502
|
r.push( `alias:${ quoted( name.alias ) }` ); // should be late due to $self in anonymous aspect
|
|
1502
1503
|
|
|
1503
|
-
if (
|
|
1504
|
+
if (orig === art.$backlink)
|
|
1505
|
+
r.push( 'backlink' );
|
|
1506
|
+
else if (suffix && art.targetAspect)
|
|
1504
1507
|
r.push( 'target' );
|
|
1505
1508
|
else if (suffix)
|
|
1506
1509
|
r.push( art.items?.items ? `items:${ suffix }` : 'items' );
|
|
@@ -53,7 +53,7 @@ optionProcessor
|
|
|
53
53
|
.option(' --test-sort-csn')
|
|
54
54
|
.option(' --doc-comment')
|
|
55
55
|
.option(' --propagate-doc-comments')
|
|
56
|
-
.option(' --
|
|
56
|
+
.option(' --v6-key-propagation')
|
|
57
57
|
.option(' --add-texts-language-assoc')
|
|
58
58
|
.option(' --localized-without-coalesce')
|
|
59
59
|
.option(' --tenant-discriminator')
|
|
@@ -157,7 +157,7 @@ optionProcessor
|
|
|
157
157
|
option is implicitly enabled as well.
|
|
158
158
|
--doc-comment Preserve /** */ comments at annotation positions as doc property in CSN
|
|
159
159
|
--propagate-doc-comments Propagate doc comments ('--doc-comment')
|
|
160
|
-
--
|
|
160
|
+
--v6-key-propagation Use compiler-v6 propagation rules for 'key'
|
|
161
161
|
--add-texts-language-assoc In generated texts entities, add association "language"
|
|
162
162
|
to "sap.common.Languages" if it exists
|
|
163
163
|
--localized-without-coalesce Omit coalesce in localized convenience views
|
|
@@ -169,7 +169,6 @@ optionProcessor
|
|
|
169
169
|
--skip-name-check Skip certain name checks, e.g. that there must be no '.' in element names.
|
|
170
170
|
|
|
171
171
|
Commands
|
|
172
|
-
H, toHana [options] <files...> (deprecated) Generate HANA CDS source files
|
|
173
172
|
O, toOdata [options] <files...> Generate ODATA metadata and annotations
|
|
174
173
|
C, toCdl <files...> Generate CDS source files
|
|
175
174
|
Q, toSql [options] <files...> Generate SQL DDL statements
|
|
@@ -196,65 +195,6 @@ optionProcessor
|
|
|
196
195
|
CDSC_TRACE_API If set, additional API calling information is printed to stderr.
|
|
197
196
|
`);
|
|
198
197
|
|
|
199
|
-
// ----------- toHana -----------
|
|
200
|
-
optionProcessor.command('H, toHana')
|
|
201
|
-
.option('-h, --help')
|
|
202
|
-
.option('-n, --sql-mapping <style>', { valid: [ 'plain', 'quoted', 'hdbcds' ], aliases: [ '--names' ] })
|
|
203
|
-
.option(' --render-virtual')
|
|
204
|
-
.option(' --joinfk')
|
|
205
|
-
.option('-u, --user <user>')
|
|
206
|
-
.option('-s, --src')
|
|
207
|
-
.option('-c, --csn')
|
|
208
|
-
.option(' --integrity-not-validated')
|
|
209
|
-
.option(' --integrity-not-enforced')
|
|
210
|
-
.option(' --assert-integrity <mode>', { valid: [ 'true', 'false', 'individual' ] })
|
|
211
|
-
.option(' --assert-integrity-type <type>', { valid: [ 'RT', 'DB' ], ignoreCase: true })
|
|
212
|
-
.option(' --pre2134ReferentialConstraintNames')
|
|
213
|
-
.option(' --disable-hana-comments')
|
|
214
|
-
.option(' --no-standard-database-functions')
|
|
215
|
-
.help(`
|
|
216
|
-
Usage: cdsc toHana [options] <files...>
|
|
217
|
-
|
|
218
|
-
====================================================
|
|
219
|
-
DEPRECATED! Since v5, this backend is deprecated!
|
|
220
|
-
====================================================
|
|
221
|
-
|
|
222
|
-
Generate HANA CDS source files, or CSN.
|
|
223
|
-
|
|
224
|
-
Options
|
|
225
|
-
-h, --help Show this help text
|
|
226
|
-
-n, --sql-mapping <style> Naming style for generated entity and element names:
|
|
227
|
-
plain : (default) Produce HANA entity and element names in
|
|
228
|
-
uppercase and flattened with underscores. Do not generate
|
|
229
|
-
structured types.
|
|
230
|
-
quoted : Produce HANA entity and element names in original case as
|
|
231
|
-
in CDL. Keep nested contexts (resulting in entity names
|
|
232
|
-
with dots), but flatten element names with underscores.
|
|
233
|
-
Generate structured types, too.
|
|
234
|
-
hdbcds : Produce HANA entity end element names as HANA CDS would
|
|
235
|
-
generate them from the same CDS source (like "quoted", but
|
|
236
|
-
using element names with dots).
|
|
237
|
-
--render-virtual Render virtual elements in views and draft tables
|
|
238
|
-
--joinfk Create JOINs for foreign key accesses
|
|
239
|
-
-u, --user <user> Value for the "$user" variable
|
|
240
|
-
-s, --src (default) Generate HANA CDS source files "<artifact>.hdbcds"
|
|
241
|
-
-c, --csn Generate "hana_csn.json" with HANA-preprocessed model
|
|
242
|
-
--integrity-not-enforced If this option is supplied, referential constraints are NOT ENFORCED.
|
|
243
|
-
--integrity-not-validated If this option is supplied, referential constraints are NOT VALIDATED.
|
|
244
|
-
--assert-integrity <mode> Turn DB constraints on/off:
|
|
245
|
-
true : (default) Constraints will be generated for all associations if
|
|
246
|
-
the assert-integrity-type is set to DB
|
|
247
|
-
false : No constraints will be generated
|
|
248
|
-
individual : Constraints will be generated for selected associations
|
|
249
|
-
--assert-integrity-type <type> Specifies how the referential integrity checks should be performed:
|
|
250
|
-
RT : (default) No database constraint for an association
|
|
251
|
-
if not explicitly demanded via annotation
|
|
252
|
-
DB : Create database constraints for associations
|
|
253
|
-
--pre2134ReferentialConstraintNames Do not prefix the constraint identifier with "c__"
|
|
254
|
-
--disable-hana-comments Disable rendering of doc comments as SAP HANA comments.
|
|
255
|
-
--no-standard-database-functions Disable rendering of standard database function mappings.
|
|
256
|
-
`);
|
|
257
|
-
|
|
258
198
|
optionProcessor.command('O, toOdata')
|
|
259
199
|
.option('-h, --help')
|
|
260
200
|
.option('-v, --odata-version <version>', { valid: [ 'v2', 'v4', 'v4x' ], aliases: [ '--version' ] })
|
|
@@ -276,7 +216,6 @@ optionProcessor.command('O, toOdata')
|
|
|
276
216
|
.option('-f, --odata-format <format>', { valid: [ 'flat', 'structured' ] })
|
|
277
217
|
.option('-n, --sql-mapping <style>', { valid: [ 'plain', 'quoted', 'hdbcds' ], aliases: [ '--names' ] })
|
|
278
218
|
.option('-s, --service-names <list>')
|
|
279
|
-
.option(' --transitive-localized-views')
|
|
280
219
|
.help(`
|
|
281
220
|
Usage: cdsc toOdata [options] <files...>
|
|
282
221
|
|
|
@@ -317,8 +256,6 @@ optionProcessor.command('O, toOdata')
|
|
|
317
256
|
source (like "quoted", but using element names with dots)
|
|
318
257
|
-s, --service-names <list> List of comma-separated service names to be rendered
|
|
319
258
|
(default) empty, all services are rendered
|
|
320
|
-
--transitive-localized-views If set, the backends will create localized convenience views for
|
|
321
|
-
those views, that only have an association to a localized entity/view.
|
|
322
259
|
`);
|
|
323
260
|
|
|
324
261
|
optionProcessor.command('J, forJava')
|
|
@@ -369,7 +306,6 @@ optionProcessor.command('Q, toSql')
|
|
|
369
306
|
.option(' --disable-hana-comments')
|
|
370
307
|
.option(' --generated-by-comment')
|
|
371
308
|
.option(' --better-sqlite-session-variables <bool>')
|
|
372
|
-
.option(' --transitive-localized-views')
|
|
373
309
|
.option(' --no-boolean-equality')
|
|
374
310
|
.option(' --with-hana-associations <bool>', { valid: [ 'true', 'false' ] })
|
|
375
311
|
.option(' --no-standard-database-functions')
|
|
@@ -430,8 +366,6 @@ optionProcessor.command('Q, toSql')
|
|
|
430
366
|
active if sqlDialect is \`sqlite\`:
|
|
431
367
|
true : (default) Render better-sqlite session_context(…)
|
|
432
368
|
false : Render session variables as string literals, used e.g. with sqlite3 driver
|
|
433
|
-
--transitive-localized-views If set, the backends will create localized convenience views for
|
|
434
|
-
those views, that only have an association to a localized entity/view.
|
|
435
369
|
--with-hana-associations <bool>
|
|
436
370
|
Enable or disable rendering of "WITH ASSOCIATIONS" for sqlDialect 'hana'.
|
|
437
371
|
true : (default) Render "WITH ASSOCIATIONS"
|
|
@@ -515,7 +449,6 @@ optionProcessor.command('toCsn')
|
|
|
515
449
|
.option('-f, --csn-flavor <flavor>', { valid: [ 'client', 'gensrc', 'universal' ], aliases: [ '--flavor' ] })
|
|
516
450
|
.option(' --with-localized')
|
|
517
451
|
.option(' --with-locations')
|
|
518
|
-
.option(' --transitive-localized-views')
|
|
519
452
|
.help(`
|
|
520
453
|
Usage: cdsc toCsn [options] <files...>
|
|
521
454
|
|
|
@@ -532,9 +465,6 @@ optionProcessor.command('toCsn')
|
|
|
532
465
|
universal: in development (BETA)
|
|
533
466
|
--with-locations Add $location to CSN artifacts. In contrast to \`--enrich-csn\`,
|
|
534
467
|
$location is an object with 'file', 'line' and 'col' properties.
|
|
535
|
-
--transitive-localized-views If --with-locations and this option are set, the backends
|
|
536
|
-
will create localized convenience views for those views,
|
|
537
|
-
that only have an association to a localized entity/view.
|
|
538
468
|
|
|
539
469
|
Internal options (for testing only, may be changed/removed at any time)
|
|
540
470
|
--with-localized Add localized convenience views to the CSN output.
|
|
@@ -22,7 +22,7 @@ const availableBetaFlags = {
|
|
|
22
22
|
tenantVariable: true,
|
|
23
23
|
calcAssoc: true,
|
|
24
24
|
temporalRawProjection: true,
|
|
25
|
-
|
|
25
|
+
v8preview: true,
|
|
26
26
|
rewriteAnnotationExpressionsViaType: true,
|
|
27
27
|
sqlServiceDummies: true,
|
|
28
28
|
// disabled by --beta-mode
|
|
@@ -31,22 +31,26 @@ const availableBetaFlags = {
|
|
|
31
31
|
|
|
32
32
|
// Used by isDeprecatedEnabled() to check if any flag ist set.
|
|
33
33
|
const availableDeprecatedFlags = {
|
|
34
|
-
// the
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
34
|
+
// keep the following for a while:
|
|
35
|
+
downgradableErrors: true, // generally
|
|
36
|
+
ignoreSpecifiedQueryElements: true, // used by stakeholder
|
|
37
|
+
// remove/hide in a next release:
|
|
38
|
+
_noPersistenceJournalForGeneratedEntities: true, // since v6
|
|
39
|
+
_noCompositionIncludes: true, // since v6; was an option with inverted meaning in v5
|
|
38
40
|
noQuasiVirtualAssocs: true, // since v6
|
|
39
|
-
_includesNonShadowedFirst: true,
|
|
40
|
-
_eagerPersistenceForGeneratedEntities: true,
|
|
41
|
-
_noKeyPropagationWithExpansions: true,
|
|
42
|
-
ignoreSpecifiedQueryElements: true,
|
|
43
41
|
};
|
|
44
42
|
|
|
45
|
-
// Deprecated flags that were removed in newer version and are complained
|
|
43
|
+
// Deprecated flags that were removed/hidden in newer version and are complained
|
|
44
|
+
// about with 'api-invalid-deprecated'. Keep all former availableDeprecatedFlags
|
|
45
|
+
// for two majors after they have been hidden (prefix _) or removed.
|
|
46
46
|
const oldDeprecatedFlags = {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
// remove in v9:
|
|
48
|
+
noPersistenceJournalForGeneratedEntities: '7.0', // since v6
|
|
49
|
+
noCompositionIncludes: '7.0', // since v6
|
|
50
|
+
// remove in v8:
|
|
51
|
+
includesNonShadowedFirst: '6.0', // since v4
|
|
52
|
+
noKeyPropagationWithExpansions: '6.0', // since v4
|
|
53
|
+
eagerPersistenceForGeneratedEntities: '6.0', // since v3
|
|
50
54
|
};
|
|
51
55
|
|
|
52
56
|
/**
|
|
@@ -106,10 +110,9 @@ function checkRemovedDeprecatedFlags( { deprecated, messages }, { error } ) {
|
|
|
106
110
|
return;
|
|
107
111
|
|
|
108
112
|
Object.keys( deprecated ).forEach( ( key ) => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
+
const removed = oldDeprecatedFlags[key];
|
|
114
|
+
if (removed && deprecated[key] != null)
|
|
115
|
+
error( 'api-invalid-deprecated', null, { name: key, compiler: removed } );
|
|
113
116
|
});
|
|
114
117
|
}
|
|
115
118
|
|
|
@@ -24,49 +24,11 @@ function validateDefaultValues( member, memberName, prop, path ) {
|
|
|
24
24
|
// The message also needs to be improved.
|
|
25
25
|
if (i > 1)
|
|
26
26
|
// eslint-disable-next-line cds-compiler/message-no-quotes
|
|
27
|
-
this.error(null, path, {}, 'Illegal number of unary
|
|
27
|
+
this.error(null, path, {}, 'Illegal number of unary \'+\'/\'-\' operators');
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
/**
|
|
33
|
-
* For HANA CDS specifically, reject any default parameter values, as these are not supported.
|
|
34
|
-
*
|
|
35
|
-
* @param {CSN.Element} member Member to validate
|
|
36
|
-
* @param {string} memberName Name of the member
|
|
37
|
-
* @param {string} prop Property being looped over
|
|
38
|
-
* @param {CSN.Path} path Path to the member
|
|
39
|
-
*/
|
|
40
|
-
function rejectParamDefaultsInHanaCds( member, memberName, prop, path ) {
|
|
41
|
-
if (member.default && prop === 'params' && this.options.transformation === 'hdbcds') {
|
|
42
|
-
this.error('def-unsupported-param', path, {},
|
|
43
|
-
'Parameter default values are not supported in SAP HANA CDS');
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* For HANA CDS, we render a default for a mixin if the projected entity contains
|
|
49
|
-
* a derived association with a default defined on it. This leads to a deployment error
|
|
50
|
-
* and should be warned about.
|
|
51
|
-
*
|
|
52
|
-
* @param {CSN.Element} member Member to validate
|
|
53
|
-
* @param {string} memberName Name of the member
|
|
54
|
-
* @param {string} prop Property being looped over
|
|
55
|
-
* @param {CSN.Path} path Path to the member
|
|
56
|
-
*/
|
|
57
|
-
function warnAboutDefaultOnAssociationForHanaCds( member, memberName, prop, path ) {
|
|
58
|
-
const art = this.csn.definitions[path[1]];
|
|
59
|
-
if (this.options.transformation === 'hdbcds' && !art.query && !art.projection && member.target && member.default) {
|
|
60
|
-
const type = member._type?.type || member.type || 'cds.Association';
|
|
61
|
-
this.warning('type-invalid-default', path, { '#': type === 'cds.Association' ? 'std' : 'comp' }, {
|
|
62
|
-
std: 'Default on associations is not supported for HDBCDS',
|
|
63
|
-
comp: 'Default on compositions is not supported for HDBCDS',
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
32
|
module.exports = {
|
|
69
33
|
validateDefaultValues,
|
|
70
|
-
rejectParamDefaultsInHanaCds,
|
|
71
|
-
warnAboutDefaultOnAssociationForHanaCds,
|
|
72
34
|
};
|
|
@@ -15,12 +15,28 @@ const { isPersistedOnDatabase } = require('../model/csnUtils.js');
|
|
|
15
15
|
*/
|
|
16
16
|
function validateHasPersistedElements( artifact, artifactName, prop, path ) {
|
|
17
17
|
if (artifact.kind === 'entity' && isPersistedOnDatabase(artifact)) {
|
|
18
|
+
if (artifact.query || artifact.projection)
|
|
19
|
+
return; // handled by validateQueryHasPersistedElements
|
|
18
20
|
if (!artifact.elements || !hasRealElements(artifact.elements))
|
|
19
|
-
|
|
20
|
-
this.error('def-missing-element', path, { '#': ( artifact.query || artifact.projection ) ? 'view' : 'std' });
|
|
21
|
+
this.error('def-missing-element', path, { '#': 'std' });
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Ensure that queries (top-level and subqueries) have at least one non-virtual element.
|
|
27
|
+
*
|
|
28
|
+
* @param {CSN.Query} query Query to validate
|
|
29
|
+
* @param {CSN.Path} path Path to the query
|
|
30
|
+
*/
|
|
31
|
+
function validateQueryHasPersistedElements( query, path ) {
|
|
32
|
+
if (!query.SELECT || this.artifact.kind !== 'entity')
|
|
33
|
+
return;
|
|
34
|
+
|
|
35
|
+
const elements = query.SELECT.elements || this.artifact.elements;
|
|
36
|
+
if (!hasRealElements(elements))
|
|
37
|
+
this.error('def-missing-element', path, { '#': 'view' });
|
|
38
|
+
}
|
|
39
|
+
|
|
24
40
|
/**
|
|
25
41
|
* Check if the provided elements contain elements that will be created on the database.
|
|
26
42
|
* This includes virtual and calculated elements.
|
|
@@ -50,4 +66,4 @@ function hasRealElements( elements ) {
|
|
|
50
66
|
}
|
|
51
67
|
|
|
52
68
|
|
|
53
|
-
module.exports = validateHasPersistedElements;
|
|
69
|
+
module.exports = { validateHasPersistedElements, validateQueryHasPersistedElements };
|
package/lib/checks/parameters.js
CHANGED
|
@@ -39,42 +39,8 @@ function checkForParams( parent, name, params, path ) {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
function checkAssocsWithParams( member, memberName, prop, path ) {
|
|
43
|
-
// Report an error on
|
|
44
|
-
// - view with parameters that has an element of type association/composition
|
|
45
|
-
// - association that points to entity with parameters
|
|
46
|
-
if (member.target && this.csnUtils.isAssocOrComposition(member)) {
|
|
47
|
-
if (this.artifact.params) {
|
|
48
|
-
// HANA does not allow 'WITH ASSOCIATIONS' on something with parameters:
|
|
49
|
-
// SAP DBTech JDBC: [7]: feature not supported: parameterized sql view cannot support association: line 1 col 1 (at pos 0)
|
|
50
|
-
this.message('def-unexpected-paramview-assoc', path, { '#': 'source' });
|
|
51
|
-
}
|
|
52
|
-
else if (this.artifact['@cds.persistence.udf'] || this.artifact['@cds.persistence.calcview']) {
|
|
53
|
-
// UDF/CVs w/o params don't support 'WITH ASSOCIATIONS'
|
|
54
|
-
const anno = this.artifact['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
|
|
55
|
-
this.message('def-unexpected-calcview-assoc', path, { '#': 'source', anno });
|
|
56
|
-
}
|
|
57
|
-
if (this.csn.definitions[member.target].params) {
|
|
58
|
-
// HANA does not allow association targets with parameters or to UDFs/CVs w/o parameters:
|
|
59
|
-
// SAP DBTech JDBC: [7]: feature not supported: cannot support create association to a parameterized view
|
|
60
|
-
this.message('def-unexpected-paramview-assoc', path, { '#': 'target' });
|
|
61
|
-
}
|
|
62
|
-
else if (this.csn.definitions[member.target]['@cds.persistence.udf'] || this.csn.definitions[member.target]['@cds.persistence.calcview']) {
|
|
63
|
-
// HANA won't check the assoc target but when querying an association with target UDF, this is the error:
|
|
64
|
-
// SAP DBTech JDBC: [259]: invalid table name: target object SYSTEM.UDF does not exist: line 3 col 6 (at pos 43)
|
|
65
|
-
// CREATE TABLE F (id INTEGER NOT NULL);
|
|
66
|
-
// CREATE FUNCTION UDF RETURNS TABLE (ID INTEGER) LANGUAGE SQLSCRIPT SQL SECURITY DEFINER AS BEGIN RETURN SELECT ID FROM F; END;
|
|
67
|
-
// CREATE TABLE Y ( id INTEGER NOT NULL, toUDF_id INTEGER) WITH ASSOCIATIONS (MANY TO ONE JOIN UDF AS toUDF ON (toUDF.id = toUDF_id));
|
|
68
|
-
// CREATE VIEW U AS SELECT id, toUDF.a FROM Y;
|
|
69
|
-
const anno = this.csn.definitions[member.target]['@cds.persistence.udf'] ? '@cds.persistence.udf' : '@cds.persistence.calcview';
|
|
70
|
-
this.message('def-unexpected-calcview-assoc', path, { '#': 'target', anno });
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
42
|
module.exports = {
|
|
76
43
|
csnValidator: {
|
|
77
44
|
params: checkForParams,
|
|
78
45
|
},
|
|
79
|
-
memberValidator: checkAssocsWithParams,
|
|
80
46
|
};
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { applyTransformationsOnNonDictionary } = require('../model/csnUtils');
|
|
4
4
|
|
|
5
5
|
// Only to be used with validator.js - a correct this value needs to be provided!
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Validate select items of a query. If a column reference starts with $self or
|
|
9
9
|
* $projection, it must not contain association steps.
|
|
10
|
-
* Furthermore, for to.hdbcds, window functions are not allowed.
|
|
11
|
-
*
|
|
12
|
-
* For to.hdbcds-hdbcds, structures and managed associations are not allowed
|
|
13
|
-
* as they are not flattened - @see rejectManagedAssociationsAndStructuresForHdbcdsNames
|
|
14
10
|
*
|
|
15
11
|
* @param {CSN.Query} query query object
|
|
16
12
|
* @todo Why do we care about this with $self?
|
|
@@ -55,39 +51,7 @@ function validateSelectItems( query ) {
|
|
|
55
51
|
from: aTCB, // $self refs in from clause filters are not allowed
|
|
56
52
|
};
|
|
57
53
|
|
|
58
|
-
if (this.options.transformation === 'hdbcds') {
|
|
59
|
-
transformers.xpr = (parent) => {
|
|
60
|
-
if (parent.func) {
|
|
61
|
-
this.error(null, parent.$path, {},
|
|
62
|
-
'Window functions are not supported by SAP HANA CDS');
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
54
|
applyTransformationsOnNonDictionary(query, 'SELECT', transformers );
|
|
68
|
-
|
|
69
|
-
// .call() with 'this' to ensure we have access to the options
|
|
70
|
-
rejectManagedAssociationsAndStructuresForHdbcdsNames.call(this, SELECT, SELECT.$path);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* For the to.hdbcds transformation with naming mode 'hdbcds', structures and managed associations are not flattened/resolved.
|
|
76
|
-
* It is therefore not possible to publish such elements in a view.
|
|
77
|
-
* This function iterates over all published elements of a query artifact and asserts that no such elements are published.
|
|
78
|
-
*
|
|
79
|
-
* @param {CSN.Artifact} queryArtifact the query artifact which should be checked
|
|
80
|
-
* @param {CSN.Path} artifactPath the path to that artifact
|
|
81
|
-
*/
|
|
82
|
-
function rejectManagedAssociationsAndStructuresForHdbcdsNames( queryArtifact, artifactPath ) {
|
|
83
|
-
if (this.options.transformation === 'hdbcds' && this.options.sqlMapping === 'hdbcds') {
|
|
84
|
-
forEachGeneric(queryArtifact, 'elements', (selectItem, elemName, prop, elementPath) => {
|
|
85
|
-
if (this.csnUtils.isManagedAssociation(selectItem))
|
|
86
|
-
this.error('query-unexpected-assoc-hdbcds', elementPath);
|
|
87
|
-
if (this.csnUtils.isStructured(selectItem))
|
|
88
|
-
this.error('query-unexpected-structure-hdbcds', elementPath);
|
|
89
|
-
}, artifactPath);
|
|
90
|
-
}
|
|
91
55
|
}
|
|
92
56
|
|
|
93
|
-
module.exports = { validateSelectItems
|
|
57
|
+
module.exports = { validateSelectItems };
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { forEachMemberRecursively } = require('../model/csnUtils');
|
|
4
|
+
|
|
5
|
+
// Only to be used with validator.js - a correct this value needs to be provided!
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @typedef {object} ValidatorThis
|
|
9
|
+
* @property {CSN.Model} csn
|
|
10
|
+
* @property {CSN.Options} options
|
|
11
|
+
* @property {CSN.Artifact} artifact
|
|
12
|
+
* @property {object} csnUtils
|
|
13
|
+
* @property {Function} error
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check that required actual parameters on 'node.type' are set, that their values are in the correct range etc.
|
|
18
|
+
*
|
|
19
|
+
* @this {ValidatorThis}
|
|
20
|
+
* @param {CSN.Element} member the element to be checked
|
|
21
|
+
* @param {string} memberName the elements name
|
|
22
|
+
* @param {string} prop which kind of member are we looking at
|
|
23
|
+
* @param {CSN.Path} path the path to the member
|
|
24
|
+
*/
|
|
25
|
+
function checkTypeParameters( member, memberName, prop, path ) {
|
|
26
|
+
// Types don't manifest on the database, only elements of entities/views are of interest
|
|
27
|
+
if (this.artifact.kind !== 'entity')
|
|
28
|
+
return;
|
|
29
|
+
// These are SQL-specific checks; effective CSN is not a SQL output
|
|
30
|
+
if (this.options.transformation === 'effective')
|
|
31
|
+
return;
|
|
32
|
+
if (member.type && !member.virtual) {
|
|
33
|
+
_checkTypeParameters.call(this, member, path);
|
|
34
|
+
// For structured types referenced by name (e.g. `struct: T`), the member has no
|
|
35
|
+
// .elements at this point, so forEachMemberRecursively won't descend into T's elements.
|
|
36
|
+
// We need to look up the type definition and check its elements explicitly.
|
|
37
|
+
if (!member.elements && this.csnUtils.isStructured(member)) {
|
|
38
|
+
const typeDef = this.csn.definitions[member.type];
|
|
39
|
+
if (typeDef?.elements) {
|
|
40
|
+
forEachMemberRecursively(typeDef, (sub, subName, subProp, subPath) => {
|
|
41
|
+
if (sub.type && !sub.virtual)
|
|
42
|
+
_checkTypeParameters.call(this, sub, subPath);
|
|
43
|
+
}, path);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @this {ValidatorThis}
|
|
51
|
+
* @param {object} node
|
|
52
|
+
* @param {CSN.Path} path
|
|
53
|
+
*/
|
|
54
|
+
function _checkTypeParameters( node, path ) {
|
|
55
|
+
const typeInfo = this.csnUtils.getFinalTypeInfo(node.type);
|
|
56
|
+
const absolute = typeInfo?.type;
|
|
57
|
+
if (!absolute)
|
|
58
|
+
return;
|
|
59
|
+
|
|
60
|
+
// Effective parameter value: explicit on node, or inherited from type chain
|
|
61
|
+
const effectiveParam = paramName => node[paramName] ?? typeInfo[paramName];
|
|
62
|
+
|
|
63
|
+
switch (absolute) {
|
|
64
|
+
case 'cds.String':
|
|
65
|
+
case 'cds.Binary':
|
|
66
|
+
case 'cds.hana.VARCHAR': {
|
|
67
|
+
checkTypeParamValue.call(this, node, effectiveParam, absolute, 'length', { min: 1, max: 5000 }, path);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case 'cds.Decimal': {
|
|
71
|
+
const precision = effectiveParam('precision');
|
|
72
|
+
const scale = effectiveParam('scale');
|
|
73
|
+
if (precision || scale) {
|
|
74
|
+
checkTypeParamValue.call(this, node, effectiveParam, absolute, 'precision', { max: 38 }, path);
|
|
75
|
+
checkTypeParamValue.call(this, node, effectiveParam, absolute, 'scale', { max: precision }, path);
|
|
76
|
+
}
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
case 'cds.hana.BINARY':
|
|
81
|
+
case 'cds.hana.NCHAR':
|
|
82
|
+
case 'cds.hana.CHAR': {
|
|
83
|
+
checkTypeParamValue.call(this, node, effectiveParam, absolute, 'length', { min: 1, max: 2000 }, path);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case 'cds.hana.ST_POINT':
|
|
87
|
+
case 'cds.hana.ST_GEOMETRY': {
|
|
88
|
+
checkTypeParamValue.call(this, node, effectiveParam, absolute, 'srid', { max: Number.MAX_SAFE_INTEGER }, path);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
case 'cds.Map': {
|
|
92
|
+
if (this.options.sqlDialect === 'plain')
|
|
93
|
+
this.error('ref-unsupported-type', path, { '#': 'dialect', type: absolute, value: 'plain' });
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
case 'cds.Vector': {
|
|
97
|
+
if (this.options.sqlDialect === 'plain') {
|
|
98
|
+
this.error('ref-unsupported-type', path, {
|
|
99
|
+
'#': 'dialect',
|
|
100
|
+
type: absolute,
|
|
101
|
+
value: 'plain',
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// Technical limitation of SQLite vector extension
|
|
105
|
+
else if (this.options.sqlDialect === 'sqlite' && effectiveParam('length') && effectiveParam('length') % 4 !== 0) {
|
|
106
|
+
this.error('ref-unexpected-args', path, {
|
|
107
|
+
'#': 'vector_length', elemref: path.at(-1),
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
default:
|
|
113
|
+
break; // nothing to check for unknown types
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check that the value of the type property `paramName` (e.g. length, precision, scale ...)
|
|
119
|
+
* is in a given range.
|
|
120
|
+
*
|
|
121
|
+
* @this {ValidatorThis}
|
|
122
|
+
* @param {object} node
|
|
123
|
+
* @param {Function} effectiveParam
|
|
124
|
+
* @param {string} resolvedType
|
|
125
|
+
* @param {string} paramName
|
|
126
|
+
* @param {object} range
|
|
127
|
+
* @param {CSN.Path} path
|
|
128
|
+
* @returns {boolean}
|
|
129
|
+
*/
|
|
130
|
+
function checkTypeParamValue( node, effectiveParam, resolvedType, paramName, range = null, path = null ) {
|
|
131
|
+
const paramValue = effectiveParam(paramName);
|
|
132
|
+
if (paramValue == null)
|
|
133
|
+
return true;
|
|
134
|
+
if (range) {
|
|
135
|
+
if (isMaxParameterLengthRestricted.call(this, resolvedType) && range.max && paramValue > range.max) {
|
|
136
|
+
this.error('type-unexpected-argument', path, {
|
|
137
|
+
'#': 'max', prop: paramName, type: resolvedType, number: range.max, $reviewed: false,
|
|
138
|
+
});
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
if (range.min && paramValue < range.min) {
|
|
142
|
+
this.error('type-unexpected-argument', path, {
|
|
143
|
+
'#': 'min', prop: paramName, type: resolvedType, number: range.min, $reviewed: false,
|
|
144
|
+
});
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if the maximum length of the value of the given type is restricted.
|
|
153
|
+
*
|
|
154
|
+
* @this {ValidatorThis}
|
|
155
|
+
* @param {string} type
|
|
156
|
+
* @returns {boolean}
|
|
157
|
+
*/
|
|
158
|
+
function isMaxParameterLengthRestricted( type ) {
|
|
159
|
+
return !(this.options.toSql && type === 'cds.String' && (this.options.sqlDialect === 'sqlite' || this.options.sqlDialect === 'plain'));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = checkTypeParameters;
|
package/lib/checks/validator.js
CHANGED
|
@@ -9,17 +9,17 @@ const enrichCsn = require('./enricher');
|
|
|
9
9
|
|
|
10
10
|
// forRelationalDB
|
|
11
11
|
const { validateSelectItems } = require('./selectItems');
|
|
12
|
-
const {
|
|
12
|
+
const { validateDefaultValues } = require('./defaultValues');
|
|
13
13
|
const validateCdsPersistenceAnnotation = require('./cdsPersistence');
|
|
14
14
|
const navigationIntoMany = require('./manyNavigations');
|
|
15
15
|
const expandToMany = require('./manyExpand');
|
|
16
16
|
const checkUsedTypesForAnonymousAspectComposition = require('./managedInType');
|
|
17
|
-
const validateHasPersistedElements = require('./hasPersistedElements');
|
|
17
|
+
const { validateHasPersistedElements, validateQueryHasPersistedElements } = require('./hasPersistedElements');
|
|
18
18
|
const checkForHanaTypes = require('./checkForTypes');
|
|
19
19
|
const { checkAnnotationExpression } = require('./structuredAnnoExpressions');
|
|
20
20
|
const checkForParams = require('./parameters');
|
|
21
|
+
const checkTypeParameters = require('./typeParameters');
|
|
21
22
|
// forOdata
|
|
22
|
-
const { validateDefaultValues } = require('./defaultValues');
|
|
23
23
|
const { checkActionOrFunction } = require('./actionsFunctions');
|
|
24
24
|
const {
|
|
25
25
|
checkCoreMediaTypeAllowance, checkAnalytics,
|
|
@@ -57,18 +57,16 @@ const { timetrace } = require('../utils/timetrace');
|
|
|
57
57
|
|
|
58
58
|
const forRelationalDBMemberValidators
|
|
59
59
|
= [
|
|
60
|
-
// For HANA CDS specifically, reject any default parameter values, as these are not supported.
|
|
61
|
-
rejectParamDefaultsInHanaCds,
|
|
62
60
|
checkTypeIsScalar,
|
|
63
61
|
checkDecimalScale,
|
|
64
62
|
checkExplicitlyNullableKeys,
|
|
65
63
|
managedWithoutKeys,
|
|
66
|
-
warnAboutDefaultOnAssociationForHanaCds,
|
|
67
64
|
// sql.prepend/append
|
|
68
65
|
checkSqlAnnotationOnElement,
|
|
69
66
|
// no temporal annotations on calc elements
|
|
70
67
|
rejectAnnotationsOnCalcElement,
|
|
71
68
|
checkElementTypeDefinitionHasType,
|
|
69
|
+
checkTypeParameters,
|
|
72
70
|
];
|
|
73
71
|
|
|
74
72
|
const forRelationalDBArtifactValidators = [
|
|
@@ -100,6 +98,7 @@ const forRelationalDBQueryValidators = [
|
|
|
100
98
|
// TODO reason why this is forRelationalDB exclusive
|
|
101
99
|
validateSelectItems,
|
|
102
100
|
checkQueryForNoDBArtifacts,
|
|
101
|
+
validateQueryHasPersistedElements,
|
|
103
102
|
];
|
|
104
103
|
|
|
105
104
|
const forOdataMemberValidators
|
|
@@ -214,8 +213,6 @@ function getDBCsnValidators( options ) {
|
|
|
214
213
|
*/
|
|
215
214
|
function forRelationalDB( csn, that ) {
|
|
216
215
|
const memberValidators = [ ...forRelationalDBMemberValidators, ...commonMemberValidators ];
|
|
217
|
-
if (that.options.transformation === 'hdbcds')
|
|
218
|
-
memberValidators.push(checkForParams.memberValidator);
|
|
219
216
|
// skip artifacts / elements which are not persisted on the database from being validated
|
|
220
217
|
const iterateOptions = {
|
|
221
218
|
skipArtifact: artifact => artifact.abstract ||
|