@sap/cds-compiler 3.0.0 → 3.1.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +104 -9
  2. package/bin/.eslintrc.json +2 -1
  3. package/bin/cdsc.js +28 -16
  4. package/doc/API.md +11 -0
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +24 -2
  7. package/doc/CHANGELOG_DEPRECATED.md +21 -1
  8. package/lib/api/main.js +92 -40
  9. package/lib/api/options.js +2 -3
  10. package/lib/base/keywords.js +64 -1
  11. package/lib/base/message-registry.js +33 -5
  12. package/lib/base/messages.js +54 -65
  13. package/lib/base/model.js +2 -0
  14. package/lib/base/optionProcessorHelper.js +53 -21
  15. package/lib/checks/actionsFunctions.js +8 -7
  16. package/lib/checks/selectItems.js +96 -14
  17. package/lib/checks/types.js +5 -8
  18. package/lib/checks/validator.js +1 -2
  19. package/lib/compiler/assert-consistency.js +65 -13
  20. package/lib/compiler/base.js +6 -4
  21. package/lib/compiler/builtins.js +93 -4
  22. package/lib/compiler/checks.js +1 -1
  23. package/lib/compiler/define.js +28 -23
  24. package/lib/compiler/extend.js +20 -11
  25. package/lib/compiler/finalize-parse-cdl.js +5 -9
  26. package/lib/compiler/index.js +2 -0
  27. package/lib/compiler/populate.js +37 -32
  28. package/lib/compiler/propagator.js +11 -6
  29. package/lib/compiler/resolve.js +15 -19
  30. package/lib/compiler/shared.js +54 -18
  31. package/lib/compiler/tweak-assocs.js +5 -11
  32. package/lib/compiler/utils.js +15 -6
  33. package/lib/edm/annotations/genericTranslation.js +12 -2
  34. package/lib/edm/annotations/preprocessAnnotations.js +18 -15
  35. package/lib/edm/csn2edm.js +18 -17
  36. package/lib/edm/edm.js +22 -13
  37. package/lib/edm/edmAnnoPreprocessor.js +349 -0
  38. package/lib/edm/edmInboundChecks.js +85 -0
  39. package/lib/edm/edmPreprocessor.js +336 -665
  40. package/lib/edm/edmUtils.js +86 -45
  41. package/lib/gen/Dictionary.json +29 -9
  42. package/lib/gen/language.checksum +1 -1
  43. package/lib/gen/language.interp +1 -2
  44. package/lib/gen/languageLexer.js +3 -0
  45. package/lib/gen/languageParser.js +4332 -4496
  46. package/lib/inspect/.eslintrc.json +4 -0
  47. package/lib/inspect/index.js +14 -0
  48. package/lib/inspect/inspectModelStatistics.js +81 -0
  49. package/lib/inspect/inspectPropagation.js +189 -0
  50. package/lib/inspect/inspectUtils.js +44 -0
  51. package/lib/json/from-csn.js +19 -20
  52. package/lib/json/to-csn.js +11 -8
  53. package/lib/language/genericAntlrParser.js +150 -92
  54. package/lib/language/language.g4 +47 -74
  55. package/lib/main.d.ts +1 -0
  56. package/lib/model/api.js +1 -1
  57. package/lib/model/csnRefs.js +56 -29
  58. package/lib/model/csnUtils.js +29 -14
  59. package/lib/model/revealInternalProperties.js +6 -4
  60. package/lib/modelCompare/compare.js +3 -0
  61. package/lib/optionProcessor.js +81 -38
  62. package/lib/render/toCdl.js +57 -32
  63. package/lib/render/toHdbcds.js +1 -1
  64. package/lib/render/toSql.js +31 -11
  65. package/lib/render/utils/common.js +3 -4
  66. package/lib/transform/db/associations.js +43 -35
  67. package/lib/transform/db/cdsPersistence.js +0 -1
  68. package/lib/transform/db/flattening.js +3 -4
  69. package/lib/transform/db/transformExists.js +7 -5
  70. package/lib/transform/draft/db.js +1 -1
  71. package/lib/transform/forHanaNew.js +11 -2
  72. package/lib/transform/forOdataNew.js +4 -4
  73. package/lib/transform/localized.js +15 -11
  74. package/lib/transform/odata/typesExposure.js +14 -5
  75. package/lib/utils/file.js +28 -18
  76. package/lib/utils/moduleResolve.js +0 -1
  77. package/package.json +3 -4
  78. package/share/messages/syntax-expected-integer.md +9 -8
  79. package/lib/checks/unknownMagic.js +0 -41
@@ -19,10 +19,7 @@ optionProcessor
19
19
  .option(' --color <mode>', ['auto', 'always', 'never'])
20
20
  .option('-o, --out <dir>')
21
21
  .option(' --cds-home <dir>')
22
- .option(' --lint-mode')
23
22
  .option(' --fuzzy-csn-error')
24
- .option(' --trace-parser')
25
- .option(' --trace-parser-amb')
26
23
  .option(' --trace-fs')
27
24
  .option(' --error <id-list>')
28
25
  .option(' --warn <id-list>')
@@ -35,7 +32,6 @@ optionProcessor
35
32
  .option(' --beta <list>')
36
33
  .option(' --deprecated <list>')
37
34
  .option(' --direct-backend')
38
- .option(' --parse-only')
39
35
  .option(' --fallback-parser <type>', ['cdl', 'csn', 'csn!'])
40
36
  .option(' --test-mode')
41
37
  .option(' --test-sort-csn')
@@ -78,7 +74,6 @@ optionProcessor
78
74
  never:
79
75
  -o, --out <dir> Place generated files in directory <dir>, default is "-" for <stdout>
80
76
  --cds-home <dir> When set, modules starting with '@sap/cds/' are searched in <dir>
81
- --lint-mode Generate nothing, just produce messages if any (for use by editors)
82
77
  --fuzzy-csn-error Report free-style CSN properties as errors
83
78
  -- Indicate the end of options (helpful if source names start with "-")
84
79
 
@@ -87,8 +82,6 @@ optionProcessor
87
82
  --default-string-length <length> Default 'length' for 'cds.String'
88
83
 
89
84
  Diagnostic options
90
- --trace-parser Trace parser
91
- --trace-parser-amb Trace parser ambiguities
92
85
  --trace-fs Trace file system access caused by "using from"
93
86
 
94
87
  Severity options
@@ -109,10 +102,12 @@ optionProcessor
109
102
  hanaAssocRealCardinality
110
103
  mapAssocToJoinCardinality
111
104
  ignoreAssocPublishingInUnion
105
+ odataOpenType
106
+ optionalActionFunctionParameters
112
107
  --deprecated <list> Comma separated list of deprecated options.
113
108
  Valid values are:
109
+ autoCorrectOrderBySourceRefs
114
110
  eagerPersistenceForGeneratedEntities
115
- --parse-only Stop compilation after parsing and write result to <stdout>
116
111
  --fallback-parser <type> If the language cannot be deduced by the file's extensions, use this
117
112
  parser as a fallback. Valid values are:
118
113
  cdl : Use CDL parser
@@ -141,15 +136,18 @@ optionProcessor
141
136
  toCsn [options] <files...> (default) Generate original model as CSN
142
137
  parseCdl [options] <file> Generate a CSN that is close to the CDL source.
143
138
  explain <message-id> Explain a compiler message.
139
+ parseOnly [options] <files...> (internal) Stop compilation after parsing, write messages to <stderr>,
140
+ per default no output.
144
141
  toRename [options] <files...> (internal) Generate SQL DDL rename statements
145
142
  manageConstraints [options] <files...> (internal) Generate ALTER TABLE statements to
146
143
  add / modify referential constraints.
144
+ inspect [options] <files...> (internal) Inspect the given CDS files.
147
145
  `);
148
146
 
149
147
  // ----------- toHana -----------
150
148
  optionProcessor.command('H, toHana')
151
149
  .option('-h, --help')
152
- .option('-n, --names <style>', ['plain', 'quoted', 'hdbcds'])
150
+ .option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'], { aliases: ['--names'] })
153
151
  .option(' --render-virtual')
154
152
  .option(' --joinfk')
155
153
  .option('-u, --user <user>')
@@ -159,6 +157,7 @@ optionProcessor.command('H, toHana')
159
157
  .option(' --integrity-not-enforced')
160
158
  .option(' --assert-integrity <mode>', ['true', 'false', 'individual'])
161
159
  .option(' --assert-integrity-type <type>', ['RT', 'DB'], { ignoreCase: true })
160
+ .option(' --disable-hana-comments')
162
161
  .help(`
163
162
  Usage: cdsc toHana [options] <files...>
164
163
 
@@ -166,7 +165,7 @@ optionProcessor.command('H, toHana')
166
165
 
167
166
  Options
168
167
  -h, --help Show this help text
169
- -n, --names <style> Naming style for generated entity and element names:
168
+ -n, --sql-mapping <style> Naming style for generated entity and element names:
170
169
  plain : (default) Produce HANA entity and element names in
171
170
  uppercase and flattened with underscores. Do not generate
172
171
  structured types.
@@ -193,11 +192,12 @@ optionProcessor.command('H, toHana')
193
192
  RT : (default) No database constraint for an association
194
193
  if not explicitly demanded via annotation
195
194
  DB : Create database constraints for associations
195
+ --disable-hana-comments Disable rendering of doc comments as SAP HANA comments.
196
196
  `);
197
197
 
198
198
  optionProcessor.command('O, toOdata')
199
199
  .option('-h, --help')
200
- .option('-v, --odata-version <version>', ['v2', 'v4', 'v4x'])
200
+ .option('-v, --odata-version <version>', ['v2', 'v4', 'v4x'], { aliases: ['--version'] })
201
201
  .option('-x, --xml')
202
202
  .option('-j, --json')
203
203
  .option(' --odata-containment')
@@ -207,7 +207,7 @@ optionProcessor.command('O, toOdata')
207
207
  .option(' --odata-v2-partial-constr')
208
208
  .option('-c, --csn')
209
209
  .option('-f, --odata-format <format>', ['flat', 'structured'])
210
- .option('-n, --names <style>', ['plain', 'quoted', 'hdbcds'])
210
+ .option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'], { aliases: [ '--names' ] })
211
211
  .option('-s, --service-names <list>')
212
212
  .help(`
213
213
  Usage: cdsc toOdata [options] <files...>
@@ -232,13 +232,13 @@ optionProcessor.command('O, toOdata')
232
232
  --odata-foreign-keys Render foreign keys in structured format (V4 only)
233
233
  --odata-v2-partial-constr Render referential constraints also for partial principal key tuple
234
234
  (Not spec compliant and V2 only)
235
- -n, --names <style> Annotate artifacts and elements with "@cds.persistence.name", which is
236
- the corresponding database name (see "--names" for "toHana or "toSql")
237
- plain : (default) Names in uppercase and flattened with underscores
238
- quoted : Names in original case as in CDL. Entity names with dots,
239
- but element names flattened with underscores
240
- hdbcds : Names as HANA CDS would generate them from the same CDS
241
- source (like "quoted", but using element names with dots)
235
+ -n, --sql-mapping <style> Annotate artifacts and elements with "@cds.persistence.name", which is
236
+ the corresponding database name (see "--sql-mapping" for "toHana or "toSql")
237
+ plain : (default) Names in uppercase and flattened with underscores
238
+ quoted : Names in original case as in CDL. Entity names with dots,
239
+ but element names flattened with underscores
240
+ hdbcds : Names as HANA CDS would generate them from the same CDS
241
+ source (like "quoted", but using element names with dots)
242
242
  -s, --service-names <list> List of comma-separated service names to be rendered
243
243
  (default) empty, all services are rendered
244
244
  `);
@@ -256,10 +256,10 @@ optionProcessor.command('C, toCdl')
256
256
 
257
257
  optionProcessor.command('Q, toSql')
258
258
  .option('-h, --help')
259
- .option('-n, --names <style>', ['plain', 'quoted', 'hdbcds'])
259
+ .option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'], { aliases: [ '--names' ] })
260
+ .option('-d, --sql-dialect <dialect>', ['hana', 'sqlite', 'plain', 'postgres'], { aliases: [ '--dialect' ] })
260
261
  .option(' --render-virtual')
261
262
  .option(' --joinfk')
262
- .option('-d, --dialect <dialect>', ['hana', 'sqlite', 'plain', 'postgres'])
263
263
  .option('-u, --user <user>')
264
264
  .option('-l, --locale <locale>')
265
265
  .option('-s, --src <style>', ['sql', 'hdi'])
@@ -269,6 +269,7 @@ optionProcessor.command('Q, toSql')
269
269
  .option(' --assert-integrity <mode>', ['true', 'false', 'individual'])
270
270
  .option(' --assert-integrity-type <type>', ['RT', 'DB'], { ignoreCase: true })
271
271
  .option(' --constraints-in-create-table')
272
+ .option(' --disable-hana-comments')
272
273
  .help(`
273
274
  Usage: cdsc toSql [options] <files...>
274
275
 
@@ -276,7 +277,7 @@ optionProcessor.command('Q, toSql')
276
277
 
277
278
  Options
278
279
  -h, --help Show this help text
279
- -n, --names <style> Naming style for generated entity and element names:
280
+ -n, --sql-mapping <style> Naming style for generated entity and element names:
280
281
  plain : (default) Produce SQL table and view names in
281
282
  flattened with underscores format (no quotes required)
282
283
  quoted : Produce SQL table and view names in original case as in
@@ -289,7 +290,7 @@ optionProcessor.command('Q, toSql')
289
290
  combination with "hana" dialect.
290
291
  --render-virtual Render virtual elements in views and draft tables
291
292
  --joinfk Create JOINs for foreign key accesses
292
- -d, --dialect <dialect> SQL dialect to be generated:
293
+ -d, --sql-dialect <dialect> SQL dialect to be generated:
293
294
  plain : (default) Common SQL - no assumptions about DB restrictions
294
295
  hana : SQL with HANA specific language features
295
296
  sqlite : Common SQL for sqlite
@@ -316,32 +317,34 @@ optionProcessor.command('Q, toSql')
316
317
  --constraints-in-create-table If set, the foreign key constraints will be rendered as
317
318
  part of the "CREATE TABLE" statements rather than as separate
318
319
  "ALTER TABLE ADD CONSTRAINT" statements
320
+ --disable-hana-comments Disable rendering of doc comments as SAP HANA comments.
319
321
  `);
320
322
 
321
323
  optionProcessor.command('toRename')
322
324
  .option('-h, --help')
323
- .option('-n, --names <style>', ['quoted', 'hdbcds'])
325
+ .option('-n, --sql-mapping <style>', ['quoted', 'hdbcds'], { aliases: ['--names'] })
324
326
  .help(`
325
327
  Usage: cdsc toRename [options] <files...>
326
328
 
327
329
  (internal, subject to change): Generate SQL stored procedure containing DDL statements to
328
330
  "storedProcedure.sql" that allows to rename existing tables and their columns so that they
329
- match the result of "toHana" or "toSql" with the "--names plain" option.
331
+ match the result of "toHana" or "toSql" with the "--sql-mapping plain" option.
330
332
 
331
333
  Options
332
334
  -h, --help Display this help text
333
- -n, --names <style> Assume existing tables were generated with "--names <style>":
335
+ -n, --sql-mapping <style>
336
+ Assume existing tables were generated with "--sql-mapping <style>":
334
337
  quoted : Assume existing SQL tables and views were named in original
335
338
  case as in CDL (with dots), but column names were flattened
336
- with underscores (e.g. resulting from "toHana --names quoted")
339
+ with underscores (e.g. resulting from "toHana --sql-mapping quoted")
337
340
  hdbcds : (default) Assume existing SQL tables, views and columns were
338
341
  generated by HANA CDS from the same CDS source (or resulting
339
- from "toHana --names hdbcds")
342
+ from "toHana --sql-mapping hdbcds")
340
343
  `);
341
344
 
342
345
  optionProcessor.command('manageConstraints')
343
346
  .option('-h, --help')
344
- .option('-n, --names <style>', ['plain', 'quoted', 'hdbcds'])
347
+ .option('-n, --sql-mapping <style>', ['plain', 'quoted', 'hdbcds'], { aliases: ['--names'] })
345
348
  .option('-s, --src <style>', ['sql', 'hdi'])
346
349
  .option(' --drop')
347
350
  .option(' --alter')
@@ -357,7 +360,8 @@ optionProcessor.command('manageConstraints')
357
360
 
358
361
  Options
359
362
  -h, --help Display this help text
360
- -n, --names <style> Assume existing tables were generated with "--names <style>":
363
+ -n, --sql-mapping <style>
364
+ Assume existing tables were generated with "--sql-mapping <style>":
361
365
  plain : (default) Assume SQL tables were flattened and dots were
362
366
  replaced by underscores
363
367
  quoted : Assume existing SQL tables and views were named in original
@@ -379,8 +383,9 @@ optionProcessor.command('manageConstraints')
379
383
 
380
384
  optionProcessor.command('toCsn')
381
385
  .option('-h, --help')
382
- .option('-f, --flavor <flavor>', ['client', 'gensrc', 'universal'])
386
+ .option('-f, --csn-flavor <flavor>', ['client', 'gensrc', 'universal'], { aliases: ['--flavor'] })
383
387
  .option(' --with-localized')
388
+ .option(' --with-locations')
384
389
  .help(`
385
390
  Usage: cdsc toCsn [options] <files...>
386
391
 
@@ -388,13 +393,15 @@ optionProcessor.command('toCsn')
388
393
 
389
394
  Options
390
395
  -h, --help Show this help text
391
- -f, --flavor <flavor> Generate CSN in one of two flavors:
392
- client : (default) Standard CSN consumable by clients and backends
393
- gensrc : CSN specifically for use as a source, e.g. for
394
- combination with additional "extend" or "annotate"
395
- statements, but not suitable for consumption by clients or
396
- backends
397
- universal: in development (BETA)
396
+ -f, --csn-flavor <flavor> Generate CSN in one of two flavors:
397
+ client : (default) Standard CSN consumable by clients and backends
398
+ gensrc : CSN specifically for use as a source, e.g. for
399
+ combination with additional "extend" or "annotate"
400
+ statements, but not suitable for consumption by clients or
401
+ backends
402
+ universal: in development (BETA)
403
+ --with-locations Add $location to CSN artifacts. In contrast to \`--enrich-csn\`,
404
+ $location is an object with 'file', 'line' and 'col' properties.
398
405
 
399
406
  Internal options (for testing only, may be changed/removed at any time)
400
407
  --with-localized Add localized convenience views to the CSN output.
@@ -413,6 +420,26 @@ optionProcessor.command('parseCdl')
413
420
  -h, --help Show this help text
414
421
  `);
415
422
 
423
+ optionProcessor.command('parseOnly')
424
+ .option('-h, --help')
425
+ .option(' --trace-parser')
426
+ .option(' --trace-parser-amb')
427
+ .positionalArgument('<file>')
428
+ .help(`
429
+ Usage: cdsc parseOnly [options] <files...>
430
+
431
+ (internal): Stop compilation after parsing and write messages to <stderr>.
432
+ Per default, nothing is printed. With \`--raw-output +\`, XSN is printed
433
+ to <stdout>.
434
+
435
+ Options
436
+ -h, --help Show this help text
437
+
438
+ Diagnostic options
439
+ --trace-parser Trace parser
440
+ --trace-parser-amb Trace parser ambiguities
441
+ `);
442
+
416
443
  optionProcessor.command('explain')
417
444
  .option('-h, --help')
418
445
  .positionalArgument('<message-id>')
@@ -428,6 +455,22 @@ optionProcessor.command('explain')
428
455
  -h, --help Show this help text
429
456
  `);
430
457
 
458
+ optionProcessor.command('inspect')
459
+ .option('-h, --help')
460
+ .option(' --statistics')
461
+ .option(' --propagation <art>')
462
+ .positionalArgument('<files...>')
463
+ .help(`
464
+ Usage: cdsc inspect [options] <files...>
465
+
466
+ (internal): Inspect the CSN model compiled from the provided CDS files.
467
+
468
+ Options
469
+ -h, --help Show this help text
470
+ --statistics Print model statistics
471
+ --propagation <art> Show propagation sources for <art>
472
+ `);
473
+
431
474
  module.exports = {
432
475
  optionProcessor
433
476
  };
@@ -142,11 +142,10 @@ function csnToCdl(csn, options) {
142
142
  let result = renderAnnotationAssignmentsAndDocComment(ext, env);
143
143
 
144
144
  if (ext.includes && ext.includes.length > 0) {
145
- // Includes can't be combined with anything in braces {}. Multiple includes
146
- // are possible through CSN, but in CDL, only one include at once is possible.
145
+ // Includes can't be combined with anything in braces {}.
147
146
  const affix = isElementExtend ? 'element ' : '';
148
- for (const id of ext.includes)
149
- result += `${env.indent}extend ${affix}${extName} with ${quotePathIfRequired(id)};\n`;
147
+ const includes = ext.includes.map(inc => quotePathIfRequired(inc)).join(', ');
148
+ result += `${env.indent}extend ${affix}${extName} with ${includes};\n`;
150
149
  return result;
151
150
  }
152
151
 
@@ -358,17 +357,21 @@ function csnToCdl(csn, options) {
358
357
  case 'context':
359
358
  case 'service':
360
359
  return renderContextOrService(artifactName, art, env);
360
+
361
361
  case 'type':
362
362
  case 'aspect':
363
363
  case 'annotation': // annotation in 'csn.definitions' for compiler v1 compatibility
364
364
  return renderTypeOrAnnotation(artifactName, art, env);
365
+
365
366
  case 'action':
366
367
  case 'function':
367
368
  return renderActionOrFunction(artifactName, art, env);
369
+
368
370
  case 'event':
369
371
  return renderEvent(artifactName, art, env);
372
+
370
373
  default:
371
- throw new ModelError(`Unknown artifact kind: ${art.kind}`);
374
+ throw new ModelError(`to.cdl: Unknown artifact kind: ${art.kind}`);
372
375
  }
373
376
  }
374
377
 
@@ -426,14 +429,16 @@ function csnToCdl(csn, options) {
426
429
  */
427
430
  function renderEntity(artifactName, art, env) {
428
431
  let result = renderAnnotationAssignmentsAndDocComment(art, env);
429
- const childEnv = increaseIndent(env);
430
- const normalizedArtifactName = renderArtifactName(artifactName);
431
- result += `${env.indent + (art.abstract ? 'abstract ' : '')}entity ${normalizedArtifactName}`;
432
- const parameters = Object.keys(art.params || []).map(name => renderParameter(name, art.params[name], childEnv)).join(',\n');
433
- result += (parameters === '') ? '' : ` (\n${parameters}\n${env.indent})`;
432
+ result += env.indent + (art.abstract ? 'abstract ' : '');
433
+ result += `entity ${renderArtifactName(artifactName)}`;
434
+
435
+ if (art.params)
436
+ result += renderParameters(art, env);
437
+
434
438
  if (art.includes)
435
439
  result += renderIncludes(art.includes);
436
440
  result += ' {\n';
441
+ const childEnv = increaseIndent(env);
437
442
  for (const name in art.elements) {
438
443
  const element = art.elements[name];
439
444
  result += renderElement(name, element, childEnv);
@@ -664,8 +669,11 @@ function csnToCdl(csn, options) {
664
669
  if (path.ref[0].args)
665
670
  result += `(${renderArgs(path.ref[0], ':', env)})`;
666
671
 
667
- if (path.ref[0].where)
668
- result += `[${path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : ''}${renderExpr(path.ref[0].where, env, true, true)}]`;
672
+ if (path.ref[0].where) {
673
+ const cardinality = path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : '';
674
+ const expr = renderExpr(path.ref[0].where, env, true, true);
675
+ result += `[${cardinality}${expr}]`;
676
+ }
669
677
 
670
678
  // Add any path steps (possibly with parameters and filters) that may follow after that
671
679
  if (path.ref.length > 1)
@@ -841,14 +849,9 @@ function csnToCdl(csn, options) {
841
849
  const syntax = (art.projection) ? 'projection' : 'entity';
842
850
  let result = renderAnnotationAssignmentsAndDocComment(art, env);
843
851
  result += `${env.indent}${art.abstract ? 'abstract ' : ''}${syntax === 'projection' ? 'entity' : syntax} ${renderArtifactName(artifactName)}`;
844
- if (art.params) {
845
- const childEnv = increaseIndent(env);
846
- const parameters = Object.keys(art.params).map(name => renderParameter(name, art.params[name], childEnv)).join(',\n');
847
- result += `(\n${parameters}\n${env.indent}) as `;
848
- }
849
- else {
850
- result += ' as ';
851
- }
852
+ if (art.params)
853
+ result += renderParameters(art, env);
854
+ result += ' as ';
852
855
  result += renderQuery(getNormalizedQuery(art).query, true, syntax, env, [ 'definitions', artifactName, 'query' ], art.elements);
853
856
  result += ';\n';
854
857
  result += renderQueryElementAnnotations(artifactName, art, env);
@@ -959,9 +962,10 @@ function csnToCdl(csn, options) {
959
962
  if (limit.rows !== undefined)
960
963
  limitStr += `limit ${renderExpr(limit.rows, limitEnv)}`;
961
964
 
962
- if (limit.offset !== undefined)
963
- limitStr += `${limitStr !== '' ? `\n${increaseIndent(limitEnv).indent}` : ''}offset ${renderExpr(limit.offset, limitEnv)}`;
964
-
965
+ if (limit.offset !== undefined) {
966
+ const offsetIndent = (limitStr === '') ? '' : `\n${increaseIndent(limitEnv).indent}`;
967
+ limitStr += `${offsetIndent}offset ${renderExpr(limit.offset, limitEnv)}`;
968
+ }
965
969
  return limitStr;
966
970
  }
967
971
 
@@ -1040,10 +1044,9 @@ function csnToCdl(csn, options) {
1040
1044
  * @return {string}
1041
1045
  */
1042
1046
  function renderActionOrFunction(actionName, act, env) {
1043
- let result = `${renderAnnotationAssignmentsAndDocComment(act, env) + env.indent + act.kind} ${renderArtifactName(actionName)}`;
1044
- const childEnv = increaseIndent(env);
1045
- const parameters = Object.keys(act.params || []).map(name => renderParameter(name, act.params[name], childEnv)).join(',\n');
1046
- result += (parameters === '') ? '()' : `(\n${parameters}\n${env.indent})`;
1047
+ let result = renderAnnotationAssignmentsAndDocComment(act, env) + env.indent + act.kind;
1048
+ result += ` ${renderArtifactName(actionName)}`;
1049
+ result += renderParameters(act, env);
1047
1050
  if (act.returns) {
1048
1051
  const actEnv = envAddPath(env, [ 'returns' ]);
1049
1052
  result += ` returns ${renderTypeReferenceAndProps(act.returns, actEnv)}`;
@@ -1053,6 +1056,23 @@ function csnToCdl(csn, options) {
1053
1056
  return result;
1054
1057
  }
1055
1058
 
1059
+ /**
1060
+ * Render art.params, i.e. list of parameter in parentheses. If there is only one
1061
+ * parameter, a single line is used, otherwise an indented list is used.
1062
+ * If there are no params, an empty list `()` is returned.
1063
+ *
1064
+ * @param {CSN.Artifact} art
1065
+ * @param {CdlRenderEnvironment} env
1066
+ * @returns {string}
1067
+ */
1068
+ function renderParameters(art, env) {
1069
+ const childEnv = increaseIndent(env);
1070
+ const parameters = Object.keys(art.params || {}).map(name => renderParameter(name, art.params[name], childEnv));
1071
+ if (parameters.length === 0)
1072
+ return '()';
1073
+ return `(\n${parameters.join(',\n')}\n${env.indent})`;
1074
+ }
1075
+
1056
1076
  /**
1057
1077
  * Render an action or function parameter 'par' with name 'parName'. Return the resulting source string (no trailing LF).
1058
1078
  *
@@ -1075,7 +1095,7 @@ function csnToCdl(csn, options) {
1075
1095
  * @param {string} artifactName
1076
1096
  * @param {CSN.Artifact} art
1077
1097
  * @param {CdlRenderEnvironment} env
1078
- * @param {String} [artType] - used for rendering csn.vocabularies, as the annotations there do not have a kind. Only in toCdl mode
1098
+ * @param {String} [artType] - used for rendering csn.vocabularies, as the annotations there do not have a kind.
1079
1099
  * @return {string}
1080
1100
  */
1081
1101
  function renderTypeOrAnnotation(artifactName, art, env, artType) {
@@ -1085,9 +1105,11 @@ function csnToCdl(csn, options) {
1085
1105
  result += renderIncludes(art.includes);
1086
1106
 
1087
1107
  if (!art.type && art.elements) // For nicer output, no colon if unnamed structure is used.
1088
- result += ` ${renderTypeReferenceAndProps(art, env)};\n`;
1108
+ result += ` ${renderTypeReferenceAndProps(art, env)}`;
1089
1109
  else
1090
- result += ` : ${renderTypeReferenceAndProps(art, env)};\n`;
1110
+ result += ` : ${renderTypeReferenceAndProps(art, env)}`;
1111
+ // for aspects, but since types don't have `actions` this does not hurt
1112
+ result += `${renderActionsAndFunctions(art, env)};\n`;
1091
1113
  return result;
1092
1114
  }
1093
1115
 
@@ -1373,7 +1395,9 @@ function csnToCdl(csn, options) {
1373
1395
  }
1374
1396
  if (s.where) {
1375
1397
  // Filter, possibly with cardinality
1376
- result += `[${s.cardinality ? (`${s.cardinality.max}: `) : ''}${renderExpr(s.where, env, inline, true)}]`;
1398
+ const cardinality = s.cardinality ? (`${s.cardinality.max}: `) : '';
1399
+ const expr = renderExpr(s.where, env, inline, true);
1400
+ result += `[${cardinality}${expr}]`;
1377
1401
  }
1378
1402
 
1379
1403
  return result;
@@ -1516,7 +1540,8 @@ function csnToCdl(csn, options) {
1516
1540
  * @return {string}
1517
1541
  */
1518
1542
  function renderForeignKey(fKey, env) {
1519
- return `${renderExpr(fKey, env)}${fKey.as ? (` as ${fKey.as}`) : ''}`;
1543
+ const alias = fKey.as ? (` as ${fKey.as}`) : '';
1544
+ return renderExpr(fKey, env) + alias;
1520
1545
  }
1521
1546
 
1522
1547
  /**
@@ -839,7 +839,7 @@ function toHdbcdsSource(csn, options) {
839
839
  * @param {object} [elements] For leading query, the elements of the artifact
840
840
  * @returns {string} The rendered query
841
841
  */
842
- function renderQuery(query, isLeadingQuery, env, path = [], elements) {
842
+ function renderQuery(query, isLeadingQuery, env, path = [], elements = null) {
843
843
  let result = '';
844
844
  env.skipKeys = !isLeadingQuery;
845
845
  // Set operator, like UNION, INTERSECT, ...
@@ -117,14 +117,18 @@ function toSqlDdl(csn, options) {
117
117
  */
118
118
  addColumns: {
119
119
  fromElementStrings(tableName, eltStrings) {
120
+ if (options.sqlDialect === 'sqlite') // SQLite can only alter one column at a time
121
+ return eltStrings.map(eltString => `ALTER TABLE ${tableName} ADD ${eltString};`);
122
+
120
123
  const elts = options.sqlDialect === 'hana' ? `(${eltStrings.join(', ')})` : `${eltStrings.join(', ')}`;
121
124
  return [ `ALTER TABLE ${tableName} ADD ${elts};` ];
122
125
  },
123
126
  fromElementsObj(artifactName, tableName, elementsObj, env, duplicateChecker) {
124
127
  // Only extend with 'ADD' for elements/associations
125
128
  // TODO: May also include 'RENAME' at a later stage
129
+ const alterEnv = activateAlterMode(env);
126
130
  const elements = Object.entries(elementsObj)
127
- .map(([ name, elt ]) => renderElement(artifactName, name, elt, duplicateChecker, null, env))
131
+ .map(([ name, elt ]) => renderElement(artifactName, name, elt, duplicateChecker, null, alterEnv))
128
132
  .filter(s => s !== '');
129
133
 
130
134
  if (elements.length)
@@ -138,10 +142,10 @@ function toSqlDdl(csn, options) {
138
142
  TODO duplicity check
139
143
  */
140
144
  addAssociations(artifactName, tableName, elementsObj, env) {
141
- return Object.entries(elementsObj)
145
+ return options.sqlDialect === 'hana' ? Object.entries(elementsObj)
142
146
  .map(([ name, elt ]) => renderAssociationElement(name, elt, env))
143
147
  .filter(s => s !== '')
144
- .map(eltStr => `ALTER TABLE ${tableName} ADD ASSOCIATION (${eltStr});`);
148
+ .map(eltStr => `ALTER TABLE ${tableName} ADD ASSOCIATION (${eltStr});`) : [];
145
149
  },
146
150
  /*
147
151
  Render key addition as HANA SQL.
@@ -159,7 +163,7 @@ function toSqlDdl(csn, options) {
159
163
  Render association removals as HANA SQL.
160
164
  */
161
165
  dropAssociation(tableName, sqlId) {
162
- return [ `ALTER TABLE ${tableName} DROP ASSOCIATION ${sqlId};` ];
166
+ return options.sqlDialect === 'hana' ? [ `ALTER TABLE ${tableName} DROP ASSOCIATION ${sqlId};` ] : [];
163
167
  },
164
168
  /*
165
169
  Render primary-key removals as HANA SQL.
@@ -200,7 +204,7 @@ function toSqlDdl(csn, options) {
200
204
  Render comment string.
201
205
  */
202
206
  comment(comment) {
203
- return comment && renderStringForSql(comment, options.sqlDialect) || 'NULL';
207
+ return comment && renderStringForSql(getHanaComment({ doc: comment }), options.sqlDialect) || 'NULL';
204
208
  },
205
209
  /*
206
210
  Alter SQL snippet for entity.
@@ -434,7 +438,7 @@ function toSqlDdl(csn, options) {
434
438
  function getEltStr(defVariant, eltName) {
435
439
  return defVariant.target
436
440
  ? renderAssociationElement(eltName, defVariant, env)
437
- : renderElement(artifactName, eltName, defVariant, null, null, env);
441
+ : renderElement(artifactName, eltName, defVariant, null, null, activateAlterMode(env));
438
442
  }
439
443
  function getEltStrNoProps(defVariant, eltName, ...props) {
440
444
  const defNoProps = Object.assign({}, defVariant);
@@ -461,7 +465,7 @@ function toSqlDdl(csn, options) {
461
465
  // Change entity properties
462
466
  if (migration.properties) {
463
467
  for (const [ prop, def ] of Object.entries(migration.properties)) {
464
- if (prop === 'doc') {
468
+ if (prop === 'doc' && !options.disableHanaComments) { // def.new may be `null`
465
469
  const alterComment = render.alterEntityComment(tableName, def.new);
466
470
  addMigration(resultObj, artifactName, false, alterComment);
467
471
  }
@@ -527,7 +531,7 @@ function toSqlDdl(csn, options) {
527
531
  }
528
532
  }
529
533
 
530
- if (def.old.doc !== def.new.doc) {
534
+ if (!options.disableHanaComments && def.old.doc !== def.new.doc) {
531
535
  const eltStrOldNoDoc = getEltStrNoProps(def.old, eltName, 'doc');
532
536
  const eltStrNewNoDoc = getEltStrNoProps(def.new, eltName, 'doc');
533
537
  if (eltStrOldNoDoc === eltStrNewNoDoc) { // only `doc` changed
@@ -753,7 +757,7 @@ function toSqlDdl(csn, options) {
753
757
  duplicateChecker.addElement(quotedElementName, elm.$location, elementName);
754
758
 
755
759
  let result = `${env.indent + quotedElementName} ${renderTypeReference(artifactName, elementName, elm)
756
- }${renderNullability(elm, true)}`;
760
+ }${renderNullability(elm, true, env.alterMode)}`;
757
761
  if (elm.default)
758
762
  result += ` DEFAULT ${renderExpr(elm.default, env)}`;
759
763
 
@@ -1395,9 +1399,15 @@ function toSqlDdl(csn, options) {
1395
1399
  *
1396
1400
  * @param {object} obj Object to render for
1397
1401
  * @param {boolean} treatKeyAsNotNull Whether to render KEY as not null
1402
+ * @param {boolean} deltaMode Look for a $notNull and use that with precedence over notNull
1398
1403
  * @returns {string} NULL/NOT NULL or ''
1399
1404
  */
1400
- function renderNullability(obj, treatKeyAsNotNull = false) {
1405
+ function renderNullability(obj, treatKeyAsNotNull = false, deltaMode = false) {
1406
+ if (deltaMode && obj.$notNull !== undefined) { // can be set via compare.js if it goes from "not null" to implicit "null"
1407
+ return obj.$notNull ? ' NOT NULL' : ' NULL';
1408
+ }
1409
+
1410
+
1401
1411
  if (obj.notNull === undefined && !(obj.key && treatKeyAsNotNull)) {
1402
1412
  // Attribute not set at all
1403
1413
  return '';
@@ -1492,6 +1502,7 @@ function toSqlDdl(csn, options) {
1492
1502
  }
1493
1503
  else if (x.ref[0] === '$now') { // TODO: Can there be cases where $now is followed by something?
1494
1504
  switch (options.sqlDialect) {
1505
+ case 'plain':
1495
1506
  case 'sqlite':
1496
1507
  case 'hana':
1497
1508
  return 'CURRENT_TIMESTAMP';
@@ -1531,7 +1542,7 @@ function toSqlDdl(csn, options) {
1531
1542
  return 'SESSION_CONTEXT(\'LOCALE\')';
1532
1543
  return '\'en\''; // default language
1533
1544
  }
1534
- // Basically: Second path step was invalid, do nothing - should not happen, see 'unknownMagic.js'
1545
+ // Basically: Second path step was invalid, do nothing - should not happen.
1535
1546
  return null;
1536
1547
  }
1537
1548
  /**
@@ -1654,6 +1665,15 @@ function toSqlDdl(csn, options) {
1654
1665
  function increaseIndent(env) {
1655
1666
  return Object.assign({}, env, { indent: `${env.indent} ` });
1656
1667
  }
1668
+ /**
1669
+ * Returns a copy of 'env' with alterMode set to true
1670
+ *
1671
+ * @param {object} env Render environment
1672
+ * @returns {object} Render environment with alterMode
1673
+ */
1674
+ function activateAlterMode(env) {
1675
+ return Object.assign({ alterMode: true }, env);
1676
+ }
1657
1677
  }
1658
1678
 
1659
1679
  /**
@@ -286,8 +286,8 @@ const cdsToSqlTypes = {
286
286
  'cds.LargeString': 'text',
287
287
  'cds.hana.CLOB': 'text',
288
288
  'cds.LargeBinary': 'bytea',
289
- 'cds.Binary': 'VARCHAR',
290
- 'cds.hana.BINARY': 'VARCHAR',
289
+ 'cds.Binary': 'bytea',
290
+ 'cds.hana.BINARY': 'bytea',
291
291
  'cds.Double': 'double precision',
292
292
  'cds.hana.TINYINT': 'INTEGER',
293
293
  },
@@ -358,11 +358,10 @@ function hasHanaComment(obj, options) {
358
358
  * Return the comment of the given artifact or element.
359
359
  * Uses the first block (everything up to the first empty line (double \n)).
360
360
  * Remove leading/trailing whitespace.
361
- * Does not escape any characters.
361
+ * Does not escape any characters, use e.g. `getEscapedHanaComment()` for HDBCDS.
362
362
  *
363
363
  * @param {CSN.Artifact|CSN.Element} obj
364
364
  * @returns {string}
365
- * @todo Warning/info to user?
366
365
  */
367
366
  function getHanaComment(obj) {
368
367
  return obj.doc.split('\n\n')[0].trim();