@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.
Files changed (69) hide show
  1. package/CHANGELOG.md +76 -2
  2. package/bin/cdsc.js +4 -33
  3. package/doc/IncompatibleChanges_v7.md +639 -0
  4. package/lib/api/main.js +4 -56
  5. package/lib/api/options.js +5 -15
  6. package/lib/api/validate.js +1 -0
  7. package/lib/base/builtins.js +1 -2
  8. package/lib/base/csnRefs.js +2 -6
  9. package/lib/base/message-registry.js +82 -76
  10. package/lib/base/messages.js +8 -5
  11. package/lib/base/optionProcessor.js +2 -72
  12. package/lib/base/specialOptions.js +20 -17
  13. package/lib/checks/defaultValues.js +1 -39
  14. package/lib/checks/hasPersistedElements.js +19 -3
  15. package/lib/checks/parameters.js +0 -34
  16. package/lib/checks/selectItems.js +2 -38
  17. package/lib/checks/typeParameters.js +162 -0
  18. package/lib/checks/validator.js +5 -8
  19. package/lib/compiler/assert-consistency.js +19 -5
  20. package/lib/compiler/checks.js +47 -43
  21. package/lib/compiler/define.js +6 -6
  22. package/lib/compiler/extend.js +102 -111
  23. package/lib/compiler/generate.js +4 -8
  24. package/lib/compiler/populate.js +4 -7
  25. package/lib/compiler/propagator.js +9 -9
  26. package/lib/compiler/resolve.js +205 -7
  27. package/lib/compiler/shared.js +76 -82
  28. package/lib/compiler/tweak-assocs.js +102 -22
  29. package/lib/compiler/utils.js +57 -12
  30. package/lib/compiler/xpr-rewrite.js +2 -15
  31. package/lib/edm/annotations/edmJson.js +14 -10
  32. package/lib/edm/annotations/genericTranslation.js +3 -1
  33. package/lib/edm/annotations/preprocessAnnotations.js +9 -26
  34. package/lib/edm/csn2edm.js +27 -20
  35. package/lib/edm/edmUtils.js +25 -0
  36. package/lib/gen/CdlGrammar.checksum +1 -1
  37. package/lib/gen/CdlParser.js +2237 -2241
  38. package/lib/gen/Dictionary.json +17 -2
  39. package/lib/json/from-csn.js +67 -52
  40. package/lib/json/to-csn.js +28 -25
  41. package/lib/language/textUtils.js +0 -13
  42. package/lib/main.d.ts +22 -59
  43. package/lib/main.js +1 -1
  44. package/lib/model/csnUtils.js +9 -8
  45. package/lib/parsers/AstBuildingParser.js +45 -55
  46. package/lib/parsers/Lexer.js +2 -0
  47. package/lib/parsers/identifiers.js +0 -9
  48. package/lib/render/toCdl.js +41 -40
  49. package/lib/render/toSql.js +8 -1
  50. package/lib/render/utils/common.js +1 -1
  51. package/lib/render/utils/sql.js +2 -3
  52. package/lib/tool-lib/enrichCsn.js +1 -2
  53. package/lib/transform/db/applyTransformations.js +7 -5
  54. package/lib/transform/db/assertUnique.js +8 -51
  55. package/lib/transform/db/associations.js +1 -1
  56. package/lib/transform/db/cdsPersistence.js +1 -15
  57. package/lib/transform/db/expansion.js +9 -12
  58. package/lib/transform/db/flattening.js +1 -1
  59. package/lib/transform/db/groupByOrderBy.js +0 -16
  60. package/lib/transform/db/views.js +57 -161
  61. package/lib/transform/draft/db.js +2 -2
  62. package/lib/transform/forOdata.js +25 -14
  63. package/lib/transform/forRelationalDB.js +93 -301
  64. package/lib/transform/localized.js +33 -102
  65. package/lib/transform/odata/flattening.js +11 -2
  66. package/lib/transform/transformUtils.js +25 -3
  67. package/lib/transform/universalCsn/universalCsnEnricher.js +1 -2
  68. package/package.json +2 -2
  69. package/lib/render/toHdbcds.js +0 -1810
@@ -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( 'v7' )) {
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('v7') && isBetaEnabled(options, 'v7preview')) {
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
- version: quote.single, // TODO delete: just use for OData $(VERSION), with version: 2.0
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 (suffix && art.targetAspect)
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(' --v7-key-propagation')
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
- --v7-key-propagation Use simplified propagation of 'key' in query entities
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
- v7preview: true,
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 old ones starting with _, : false
35
- noPersistenceJournalForGeneratedEntities: true, // since v6
36
- downgradableErrors: true,
37
- noCompositionIncludes: true, // since v6; was an option with inverted meaning in v5
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 about.
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
- includesNonShadowedFirst: true,
48
- eagerPersistenceForGeneratedEntities: true,
49
- noKeyPropagationWithExpansions: true,
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
- if (deprecated[key] && oldDeprecatedFlags[key]) { // why testing the value of the option?
110
- error( 'api-invalid-deprecated', null, { name: key },
111
- 'Deprecated flag $(NAME) has been removed in CDS compiler v6' );
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 ‘+’/‘-’ operators');
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
- // TODO: Maybe check if there are only calc elements and adapt the message?
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 };
@@ -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 { forEachGeneric, applyTransformationsOnNonDictionary } = require('../model/csnUtils');
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, rejectManagedAssociationsAndStructuresForHdbcdsNames };
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;
@@ -9,17 +9,17 @@ const enrichCsn = require('./enricher');
9
9
 
10
10
  // forRelationalDB
11
11
  const { validateSelectItems } = require('./selectItems');
12
- const { rejectParamDefaultsInHanaCds, warnAboutDefaultOnAssociationForHanaCds } = require('./defaultValues');
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 ||