@sap/cds-compiler 3.9.4 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/CHANGELOG.md +107 -4
  2. package/README.md +0 -1
  3. package/bin/cdsc.js +11 -23
  4. package/bin/cdsse.js +3 -3
  5. package/doc/API.md +5 -0
  6. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  7. package/doc/CHANGELOG_BETA.md +17 -1
  8. package/doc/CHANGELOG_DEPRECATED.md +28 -0
  9. package/lib/api/.eslintrc.json +1 -1
  10. package/lib/api/main.js +55 -9
  11. package/lib/api/options.js +2 -0
  12. package/lib/base/error.js +2 -0
  13. package/lib/base/message-registry.js +143 -64
  14. package/lib/base/messages.js +213 -107
  15. package/lib/base/model.js +11 -11
  16. package/lib/checks/.eslintrc.json +1 -1
  17. package/lib/checks/annotationsOData.js +2 -2
  18. package/lib/checks/elements.js +1 -1
  19. package/lib/checks/enricher.js +26 -3
  20. package/lib/checks/onConditions.js +67 -12
  21. package/lib/checks/queryNoDbArtifacts.js +106 -105
  22. package/lib/checks/sql-snippets.js +2 -0
  23. package/lib/checks/types.js +12 -6
  24. package/lib/checks/validator.js +2 -2
  25. package/lib/compiler/assert-consistency.js +10 -8
  26. package/lib/compiler/builtins.js +8 -2
  27. package/lib/compiler/checks.js +52 -35
  28. package/lib/compiler/define.js +31 -26
  29. package/lib/compiler/extend.js +120 -65
  30. package/lib/compiler/finalize-parse-cdl.js +12 -43
  31. package/lib/compiler/generate.js +16 -5
  32. package/lib/compiler/index.js +8 -5
  33. package/lib/compiler/kick-start.js +4 -3
  34. package/lib/compiler/populate.js +96 -95
  35. package/lib/compiler/propagator.js +7 -8
  36. package/lib/compiler/resolve.js +377 -103
  37. package/lib/compiler/shared.js +794 -517
  38. package/lib/compiler/tweak-assocs.js +8 -6
  39. package/lib/compiler/utils.js +44 -0
  40. package/lib/edm/annotations/genericTranslation.js +41 -5
  41. package/lib/edm/csn2edm.js +34 -32
  42. package/lib/edm/edm.js +34 -31
  43. package/lib/edm/edmAnnoPreprocessor.js +0 -23
  44. package/lib/edm/edmInboundChecks.js +7 -2
  45. package/lib/edm/edmPreprocessor.js +25 -18
  46. package/lib/edm/edmUtils.js +8 -4
  47. package/lib/gen/Dictionary.json +18 -0
  48. package/lib/gen/language.checksum +1 -1
  49. package/lib/gen/language.interp +4 -2
  50. package/lib/gen/languageParser.js +5006 -4582
  51. package/lib/json/from-csn.js +157 -112
  52. package/lib/json/to-csn.js +60 -89
  53. package/lib/language/antlrParser.js +17 -13
  54. package/lib/language/docCommentParser.js +11 -1
  55. package/lib/language/genericAntlrParser.js +13 -10
  56. package/lib/language/language.g4 +168 -97
  57. package/lib/main.d.ts +128 -36
  58. package/lib/main.js +1 -1
  59. package/lib/model/csnRefs.js +24 -5
  60. package/lib/model/csnUtils.js +9 -8
  61. package/lib/model/revealInternalProperties.js +7 -12
  62. package/lib/model/sortViews.js +4 -2
  63. package/lib/modelCompare/compare.js +1 -1
  64. package/lib/modelCompare/utils/filter.js +40 -2
  65. package/lib/optionProcessor.js +0 -3
  66. package/lib/render/toCdl.js +247 -214
  67. package/lib/render/toHdbcds.js +197 -181
  68. package/lib/render/toSql.js +325 -289
  69. package/lib/render/utils/common.js +42 -4
  70. package/lib/render/utils/delta.js +1 -1
  71. package/lib/render/utils/sql.js +3 -3
  72. package/lib/transform/braceExpression.js +2 -2
  73. package/lib/transform/db/.eslintrc.json +1 -1
  74. package/lib/transform/db/applyTransformations.js +3 -3
  75. package/lib/transform/db/associations.js +24 -12
  76. package/lib/transform/db/expansion.js +17 -18
  77. package/lib/transform/db/flattening.js +17 -21
  78. package/lib/transform/db/rewriteCalculatedElements.js +171 -64
  79. package/lib/transform/db/views.js +3 -4
  80. package/lib/transform/draft/db.js +21 -12
  81. package/lib/transform/draft/odata.js +4 -0
  82. package/lib/transform/forOdataNew.js +62 -47
  83. package/lib/transform/forRelationalDB.js +12 -7
  84. package/lib/transform/localized.js +4 -2
  85. package/lib/transform/odata/toFinalBaseType.js +5 -5
  86. package/lib/transform/odata/typesExposure.js +3 -3
  87. package/lib/transform/parseExpr.js +3 -0
  88. package/lib/transform/transformUtilsNew.js +43 -23
  89. package/lib/transform/translateAssocsToJoins.js +7 -6
  90. package/lib/transform/universalCsn/.eslintrc.json +1 -1
  91. package/lib/transform/universalCsn/coreComputed.js +7 -5
  92. package/lib/transform/universalCsn/universalCsnEnricher.js +12 -12
  93. package/package.json +2 -2
  94. package/share/messages/{duplicate-autoexposed.md → def-duplicate-autoexposed.md} +5 -1
  95. package/share/messages/message-explanations.json +1 -1
package/lib/main.d.ts CHANGED
@@ -287,6 +287,23 @@ declare namespace compiler {
287
287
  */
288
288
  $session?: Record<string, string | object>
289
289
  }
290
+ /**
291
+ * If turned on, renders:
292
+ *
293
+ * - `$user.‹id|locale›` as `session_context( '$user.‹id|locale›' )`
294
+ * instead of requiring them to be set in `sqlOptions.variableReplacements`, and
295
+ * - `$at.‹from|to›` and `$valid.‹from|to›` as `session_context( '$valid.‹from|to›' )`
296
+ * instead of using `strftime(…)`.
297
+ *
298
+ * `sqlOptions.variableReplacements` takes precedence for `$user`. If `$user.id` is set,
299
+ * the compiler will not render a `session_context(…)` function, even if this option is set.
300
+ *
301
+ * Only works with sqlDialect 'sqlite'! Otherwise, it has no effect.
302
+ *
303
+ * @since 3.9.0
304
+ * @beta
305
+ */
306
+ betterSqliteSessionVariables?: boolean
290
307
  }
291
308
 
292
309
  /**
@@ -538,7 +555,7 @@ declare namespace compiler {
538
555
 
539
556
  /**
540
557
  * Returns a message string with file- and semantic location if present in compact
541
- * form (i.e. one line)
558
+ * form (i.e. one line).
542
559
  *
543
560
  * Example:
544
561
  * ```
@@ -549,36 +566,113 @@ declare namespace compiler {
549
566
  * @param normalizeFilename If true, the file path will be normalized to use `/` as the path separator.
550
567
  * @param noMessageId If true, the message ID will _not_ be part of the string.
551
568
  * @param noHome If true, the semantic location will _not_ be part of the string.
569
+ *
570
+ * @deprecated Use messageString(msg, config) instead.
552
571
  */
553
572
  export function messageString(msg: CompileMessage, normalizeFilename?: boolean, noMessageId?: boolean, noHome?: boolean): string;
554
573
 
555
574
  /**
556
- * Returns a message string with file- and semantic location if present
557
- * in multiline form.
558
- * The error (+ message id) can colored according to their severity.
575
+ * Returns a message string with file- and semantic location if present in compact
576
+ * form (i.e. one line).
559
577
  *
560
578
  * Example:
561
579
  * ```
562
- * Error[message-id]: Can't find type `nu` in this scope
563
- * |
564
- * <source>.cds:3:11, at entity:“E”/element:“e”
580
+ * <source>.cds:3:11: Error message-id: Can't find type `nu` in this scope (in entity:“E”/element:“e”)
565
581
  * ```
566
582
  *
567
- * @param config.normalizeFilename If true, the file path will be normalized to use `/` as the path separator.
568
- * @param config.noMessageId If true, no messages id (in brackets) will be shown.
569
- * @param config.hintExplanation If true, messages with explanations will get a "…" marker, see {@link hasMessageExplanation}.
570
- * @param config.withLineSpacer If true, an additional line (with `|`) will be inserted between message and location.
571
- * @param config.color If true, ANSI escape codes will be used for coloring the severity. If false, no
572
- * coloring will be used. If 'auto', we will decide based on certain factors such
573
- * as whether the shell is a TTY and whether the environment variable `NO_COLOR` is
574
- * unset.
583
+ * Example Usage:
584
+ * ```js
585
+ * const config = { normalizeFilename: false, noMessageId: true };
586
+ * console.log(messages.map(msg => compiler.messageString(msg, config)));
587
+ * ```
588
+ *
589
+ * @param config.normalizeFilename
590
+ * If true, the file path will be normalized to use `/` as the path separator (instead of `\` on Windows).
591
+ *
592
+ * @param config.noMessageId
593
+ * If true, will _not_ show the message ID (+ explanation hint) in the output.
594
+ *
595
+ * @param config.noHome
596
+ * If true, will _not_ show message's semantic location.
597
+ *
598
+ * @param config.module
599
+ * If set, downgradable error messages will get a '‹↓›' marker, depending on whether
600
+ * the message can be downgraded for the given module.
601
+ */
602
+ export function messageString(msg: CompileMessage, config?: {
603
+ normalizeFilename?: boolean
604
+ noMessageId?: boolean
605
+ noHome?: boolean
606
+ module?: string
607
+ }): string;
608
+
609
+ /**
610
+ * Returns a message string with file- and semantic location if present in multiline form
611
+ * with a source code snippet below that has highlights for the message's location.
612
+ * The message (+ message id) are colored according to their severity.
613
+ *
614
+ * IMPORTANT: Argument `config` should be re-used by subsequent calls to this function,
615
+ * because it caches argument `config.sourceLineMap`.
616
+ *
617
+ * Example Output:
618
+ * ```
619
+ * Error[message-id]: Can't find type `nu` in this scope
620
+ * |
621
+ * <source>.cds:3:10, at entity:“E”/element:“e”
622
+ * |
623
+ * 3 | e : nu;
624
+ * | ^^
625
+ * ```
626
+ *
627
+ * Example Usage:
628
+ * ```js
629
+ * const config = { sourceMap: fileCache, cwd: '' };
630
+ * console.log(messages.map(msg => compiler.messageStringMultiline(msg, config)));
631
+ * ```
632
+ *
633
+ * @param config.normalizeFilename
634
+ * If true, the file path will be normalized to use `/` as the path separator (instead of `\` on Windows).
635
+ *
636
+ * @param config.noMessageId
637
+ * If true, will _not_ show the message ID (+ explanation hint) in the output.
638
+ *
639
+ * @param config.hintExplanation
640
+ * If true, messages with explanations will get a "…" marker, see {@link hasMessageExplanation}.
641
+ *
642
+ * @param config.module
643
+ * If set, downgradable error messages will get a '‹↓›' marker, depending on whether
644
+ * the message can be downgraded for the given module.
645
+ *
646
+ * @param config.sourceMap
647
+ * A dictionary of filename<->source-code entries. You can pass the fileCache that is used
648
+ * by the compiler.
649
+ *
650
+ * @param config.sourceLineMap
651
+ * A dictionary of filename<->source-newline-indices entries. Is used to extract source code
652
+ * snippets for message locations. If not set, will be set and filled by this function on-demand.
653
+ * An entry is an array of character/byte offsets to new-lines, for example sourceLineMap[1] is the
654
+ * end-newline for the second line.
655
+ *
656
+ * @param config.cwd
657
+ * The current working directory (cwd) that was passed to the compiler.
658
+ * This value is only used if a source map is provided and relative paths needs to be
659
+ * resolved to absolute ones.
660
+ *
661
+ * @param config.color
662
+ * If true/'always', ANSI escape codes will be used for coloring the severity. If false/'never',
663
+ * no coloring will be used. If 'auto', we will decide based on certain factors such
664
+ * as whether the shell is a TTY and whether the environment variable `NO_COLOR` is
665
+ * unset.
575
666
  */
576
667
  export function messageStringMultiline(msg: CompileMessage, config?: {
577
668
  normalizeFilename?: boolean
578
669
  noMessageId?: boolean
579
670
  hintExplanation?: boolean
580
- withLineSpacer?: boolean
581
- color?: boolean | 'auto'
671
+ module?: string
672
+ sourceMap?: Record<string, string>
673
+ sourceLineMap?: Record<string, number[]>
674
+ cwd?: string
675
+ color?: boolean | 'auto' | 'always' | 'never'
582
676
  }): string;
583
677
 
584
678
  /**
@@ -604,6 +698,8 @@ declare namespace compiler {
604
698
  * coloring will be used. If 'auto', we will decide based on certain factors such
605
699
  * as whether the shell is a TTY and whether the environment variable `NO_COLOR` is
606
700
  * unset.
701
+ *
702
+ * @deprecated Use {@link messageStringMultiline} with `config.sourceMap` and `config.sourceLineMap` instead!
607
703
  */
608
704
  export function messageContext(sourceLines: string[], msg: CompileMessage, config?: {
609
705
  color?: boolean | 'auto'
@@ -710,8 +806,8 @@ declare namespace compiler {
710
806
  * // 'OCCURRENCE'
711
807
  * to.cdl.smartId('OCCURRENCE', 'REPLACE_REGEXPR')
712
808
  * // '![OCCURRENCE]'
713
- * to.cdl.smartId('myid')
714
- * // 'myid'
809
+ * to.cdl.smartId('myId')
810
+ * // 'myId'
715
811
  * ```
716
812
  *
717
813
  * @param name
@@ -727,8 +823,8 @@ declare namespace compiler {
727
823
  * ```js
728
824
  * to.cdl.smartFunctionId('with ![brackets]')
729
825
  * // '![with ![brackets]]]'
730
- * to.cdl.smartFunctionId('myfunction')
731
- * // 'myfunction'
826
+ * to.cdl.smartFunctionId('myFunction')
827
+ * // 'myFunction'
732
828
  * ```
733
829
  *
734
830
  * @param name
@@ -742,8 +838,8 @@ declare namespace compiler {
742
838
  * ```js
743
839
  * to.cdl.delimitedId('with ![brackets]')
744
840
  * // '![with ![brackets]]]'
745
- * to.cdl.delimitedId('myid')
746
- * // '![myid]'
841
+ * to.cdl.delimitedId('myId')
842
+ * // '![myId]'
747
843
  * ```
748
844
  *
749
845
  * @param name
@@ -777,8 +873,8 @@ declare namespace compiler {
777
873
  * // '"with ""quotes"""'
778
874
  * to.sql.smartId('SELECT', 'sqlite')
779
875
  * // '"SELECT"'
780
- * to.sql.smartId('myid', 'sqlite')
781
- * // 'myid'
876
+ * to.sql.smartId('myId', 'sqlite')
877
+ * // 'myId'
782
878
  * ```
783
879
  *
784
880
  * @param name
@@ -794,8 +890,8 @@ declare namespace compiler {
794
890
  * ```js
795
891
  * to.sql.smartFunctionId('with "quotes"', 'sqlite')
796
892
  * // '"with ""quotes"""'
797
- * to.sql.smartFunctionId('myfunction', 'sqlite')
798
- * // 'myfunction'
893
+ * to.sql.smartFunctionId('myFunction', 'sqlite')
894
+ * // 'myFunction'
799
895
  * ```
800
896
  *
801
897
  * @param name
@@ -810,8 +906,8 @@ declare namespace compiler {
810
906
  * ```js
811
907
  * to.sql.delimitedId('with "quotes"', 'sqlite')
812
908
  * // '"with ""quotes"""'
813
- * to.sql.delimitedId('myid', 'sqlite')
814
- * // '"myid"'
909
+ * to.sql.delimitedId('myId', 'sqlite')
910
+ * // '"myId"'
815
911
  * ```
816
912
  *
817
913
  * @param name
@@ -1096,19 +1192,19 @@ declare namespace compiler {
1096
1192
 
1097
1193
  /**
1098
1194
  * CDS Schema Notation. Not yet specified in this TypeScript declaration file.
1099
- * See <https://pages.github.tools.sap/cap/docs/cds/csn> for more.
1195
+ * See <https://cap.cloud.sap/docs/cds/csn> for more.
1100
1196
  */
1101
1197
  export type CSN = any;
1102
1198
 
1103
1199
  /**
1104
1200
  * CDS Query Notation. Not yet specified in this TypeScript declaration file.
1105
- * See <https://pages.github.tools.sap/cap/docs/cds/cqn> for more.
1201
+ * See <https://cap.cloud.sap/docs/cds/cqn> for more.
1106
1202
  */
1107
1203
  export type CQN = any;
1108
1204
 
1109
1205
  /**
1110
1206
  * CDS Expression Notation. Not yet specified in this TypeScript declaration file.
1111
- * See <https://pages.github.tools.sap/cap/docs/cds/cxn> for more.
1207
+ * See <https://cap.cloud.sap/docs/cds/cxn> for more.
1112
1208
  */
1113
1209
  export type CXN = any;
1114
1210
 
@@ -1123,10 +1219,6 @@ declare namespace compiler {
1123
1219
 
1124
1220
  severity: MessageSeverity
1125
1221
 
1126
- /**
1127
- * @deprecated Use `$location` instead.
1128
- */
1129
- location: Location
1130
1222
  /**
1131
1223
  * Location information like file and line/column of the message.
1132
1224
  */
package/lib/main.js CHANGED
@@ -92,7 +92,7 @@ module.exports = {
92
92
  sortMessagesSeverityAware: (...args) => messages.sortMessagesSeverityAware(...args),
93
93
  deduplicateMessages: (...args) => messages.deduplicateMessages(...args),
94
94
  messageString: (...args) => messages.messageString(...args),
95
- messageStringMultiline: (...args) => messages.messageStringMultiline(...args),
95
+ messageStringMultiline: (err, config) => messages.messageStringMultiline(err, config),
96
96
  messageContext: (...args) => messages.messageContext(...args),
97
97
  explainMessage: (...args) => messages.explainMessage(...args),
98
98
  hasMessageExplanation: (...args) => messages.hasMessageExplanation(...args),
@@ -93,7 +93,7 @@
93
93
  // 1. We search in environments constructed by “defining” names “around” the
94
94
  // lexical position of the reference. In a CSN, these could be the
95
95
  // (explicit and implicit) table alias names and `mixin` definitions of the
96
- // current query and its parent queries (according to the query hiearchy).
96
+ // current query and its parent queries (according to the query hierarchy).
97
97
  // 2. If the search according to (1) was not successful and the name starts
98
98
  // with a `$`, we could consider the name to be a “magic” variable with
99
99
  // `$self` (and `$projection`) being a special magic variable.
@@ -210,7 +210,7 @@ const artifactProperties = [ 'elements', 'columns', 'keys', 'mixin', 'enum',
210
210
  // * other (& not provided) = follow target if not last ref item
211
211
  const referenceSemantics = {
212
212
  $init: { $initOnly: true },
213
- type: { lexical: false, dynamic: 'global' }, // TODO: assoc: 'static', see Issue #8458
213
+ type: { lexical: false, dynamic: 'global', assoc: 'static' },
214
214
  includes: { lexical: false, dynamic: 'global', assoc: 'static' }, // no elem ref anyway
215
215
  target: { lexical: false, dynamic: 'global', assoc: 'static' }, // no elem ref anyway
216
216
  targetAspect: { lexical: false, dynamic: 'global', assoc: 'static' },
@@ -256,6 +256,7 @@ function csnRefs( csn, universalReady ) {
256
256
  return getCache( pathItem, '_env' );
257
257
  } );
258
258
  };
259
+ artifactRef.from = fromArtifactRef;
259
260
  return {
260
261
  effectiveType,
261
262
  artifactRef,
@@ -311,6 +312,8 @@ function csnRefs( csn, universalReady ) {
311
312
  // semantic check)
312
313
  while (env.items)
313
314
  env = effectiveType( env.items );
315
+ if (env.elements) // shortcut
316
+ return env;
314
317
  const target = (staticAssoc ? targetAspect( env ) : env.target);
315
318
  if (typeof target !== 'string')
316
319
  return target || env;
@@ -347,18 +350,26 @@ function csnRefs( csn, universalReady ) {
347
350
  throw new ModelError( `Unknown artifact reference: ${ typeof ref !== 'string' ? JSON.stringify(ref.ref) : ref }` );
348
351
  }
349
352
 
353
+ function fromArtifactRef( ref ) {
354
+ // do not cache while there is second param
355
+ const art = artifactFromRef( ref, true );
356
+ if (art)
357
+ return art;
358
+ throw new ModelError( `Unknown artifact reference: ${ typeof ref !== 'string' ? JSON.stringify(ref.ref) : ref }` );
359
+ }
360
+
350
361
  function artifactPathRef( ref ) {
351
362
  const [ head, ...tail ] = ref.ref;
352
363
  let art = csn.definitions[pathId( head )];
353
364
  initDefinition( art );
354
365
  for (const elem of tail) {
355
- const env = navigationEnv( art ); // TODO: second argument true, see Issue #8458
366
+ const env = navigationEnv( art, true );
356
367
  art = env.elements[pathId( elem )];
357
368
  }
358
369
  return art;
359
370
  }
360
371
 
361
- function artifactFromRef( ref ) {
372
+ function artifactFromRef( ref, noLast ) {
362
373
  const [ head, ...tail ] = ref.ref;
363
374
  let art = csn.definitions[pathId( head )];
364
375
  initDefinition( art );
@@ -366,6 +377,8 @@ function csnRefs( csn, universalReady ) {
366
377
  const env = navigationEnv( art );
367
378
  art = env.elements[pathId( elem )];
368
379
  }
380
+ if (noLast) // TODO: delete that param
381
+ return art;
369
382
  return navigationEnv( art );
370
383
  }
371
384
 
@@ -626,9 +639,10 @@ function csnRefs( csn, universalReady ) {
626
639
  if (!qcache)
627
640
  throw new CompilerAssertion( `Query not in cache at: ${ locationString(query.$location) }` );
628
641
 
629
- if (semantics.dynamic === 'query')
642
+ if (semantics.dynamic === 'query') {
630
643
  // TODO: for ON condition in expand, would need to use cached _element
631
644
  return resolvePath( path, qcache.elements[head], null, 'query' );
645
+ }
632
646
  for (const name in qcache.$aliases) {
633
647
  const alias = qcache.$aliases[name];
634
648
  const found = alias.elements[head];
@@ -667,6 +681,8 @@ function csnRefs( csn, universalReady ) {
667
681
  links[i - 1].env = parent;
668
682
  if (typeof path[i - 1] !== 'string')
669
683
  setCache( path[i - 1], '_env', parent );
684
+ if (!parent.elements)
685
+ throw new ModelError( `${ parent.from ? 'Query ' : '' }elements not available: ${ Object.keys( parent ).join('+') }`);
670
686
  art = parent.elements[pathId( path[i] )];
671
687
  if (!art) {
672
688
  const { env } = links[i - 1];
@@ -743,6 +759,8 @@ function csnRefs( csn, universalReady ) {
743
759
  const { columns } = qcache._select;
744
760
  if (elements && columns)
745
761
  columns.map( c => initColumnElement( c, qcache ) );
762
+ else if (columns && !elements)
763
+ throw new ModelError( `Query elements not available: ${ Object.keys( (index ? qcache._select : main) ).join('+') }`);
746
764
  } );
747
765
  return all;
748
766
  }
@@ -1037,6 +1055,7 @@ function analyseCsnPath( csnPath, csn, resolve ) {
1037
1055
  }
1038
1056
  isName = prop;
1039
1057
  // if we want to allow auto-redirect of user-provided target with renamed keys:
1058
+ // (TODO: no, we do not allow that anymore)
1040
1059
  refCtx = (refCtx === '$origin' && prop === 'keys') ? 'keys_origin' : prop;
1041
1060
  }
1042
1061
  else if (prop === 'items' || prop === 'returns') {
@@ -101,7 +101,7 @@ function getUtils( model, universalReady ) {
101
101
  return walkArgs(query.SELECT.from.args);
102
102
  }
103
103
  else if (query.SELECT.from.ref) {
104
- let art = artifactRef(query.SELECT.from);
104
+ let art = artifactRef.from(query.SELECT.from);
105
105
 
106
106
  if (art.target)
107
107
  art = artifactRef(art.target);
@@ -131,7 +131,7 @@ function getUtils( model, universalReady ) {
131
131
  elements = mergeElementMaps(elements, walkArgs(arg.args));
132
132
  }
133
133
  else if (arg.ref) {
134
- const art = artifactRef(arg);
134
+ const art = artifactRef.from(arg);
135
135
  elements = mergeElementsIntoMap(elements, art.elements, art.$location, arg.as || implicitAs(arg.ref), implicitAs(arg.ref) || arg.as);
136
136
  }
137
137
  else if (arg.SELECT || arg.SET) {
@@ -541,10 +541,10 @@ function forEachMember( construct, callback, path = [], ignoreIgnore = true, ite
541
541
  forEachMember(construct.returns, callback, [ ...path, 'returns' ], ignoreIgnore, iterateOptions, constructCallback);
542
542
  }
543
543
  else if (Array.isArray(callback)) {
544
- callback.forEach(cb => cb(construct.returns, '', 'params', [ ...path, 'returns' ], construct));
544
+ callback.forEach(cb => cb(construct.returns, '', 'returns', [ ...path, 'returns' ], construct));
545
545
  }
546
546
  else {
547
- callback(construct.returns, '', 'params', [ ...path, 'returns' ], construct);
547
+ callback(construct.returns, '', 'returns', [ ...path, 'returns' ], construct);
548
548
  }
549
549
  }
550
550
  }
@@ -906,7 +906,7 @@ function setDependencies( csn, refs = csnRefs(csn) ) {
906
906
  handleArgs(artifact, artifactName, query.SELECT.from.args);
907
907
 
908
908
  else if (typeof query.SELECT.from === 'string' || query.SELECT.from.ref)
909
- handleDependency(artifactRef(query.SELECT.from), artifact, artifactName);
909
+ handleDependency(artifactRef.from(query.SELECT.from), artifact, artifactName);
910
910
  }
911
911
  }, [ 'definitions', artifactName, (artifact.projection ? 'projection' : 'query') ]);
912
912
  }
@@ -921,7 +921,7 @@ function setDependencies( csn, refs = csnRefs(csn) ) {
921
921
  if (arg.args)
922
922
  handleArgs(artifact, artifactName, arg.args);
923
923
  else if (arg.ref)
924
- handleDependency(artifactRef(arg), artifact, artifactName);
924
+ handleDependency(artifactRef.from(arg), artifact, artifactName);
925
925
  }
926
926
  }
927
927
 
@@ -1080,8 +1080,9 @@ function getLastPartOfRef( ref ) {
1080
1080
  * @param {object} fromNode
1081
1081
  * @param {object} toNode
1082
1082
  * @param {boolean} [overwrite]
1083
+ * @param {object} excludes
1083
1084
  */
1084
- function copyAnnotations( fromNode, toNode, overwrite = false ) {
1085
+ function copyAnnotations( fromNode, toNode, overwrite = false, excludes = {} ) {
1085
1086
  // Ignore if no toNode (in case of errors)
1086
1087
  if (!toNode)
1087
1088
  return;
@@ -1089,7 +1090,7 @@ function copyAnnotations( fromNode, toNode, overwrite = false ) {
1089
1090
  const annotations = Object.keys(fromNode).filter(key => key.startsWith('@'));
1090
1091
 
1091
1092
  for (const anno of annotations) {
1092
- if (toNode[anno] === undefined || overwrite)
1093
+ if ((toNode[anno] === undefined || overwrite) && !excludes[anno])
1093
1094
  toNode[anno] = fromNode[anno];
1094
1095
  }
1095
1096
  }
@@ -247,7 +247,7 @@ function revealInternalProperties( model, nameOrPath ) {
247
247
  }
248
248
 
249
249
  function origin( node, parent ) {
250
- if (!node || node.$inferred === 'REDIRECTED')
250
+ if (!node)
251
251
  return reveal( node, parent );
252
252
  return artifactIdentifier( node, parent );
253
253
  }
@@ -311,18 +311,13 @@ function artifactIdentifier( node, parent ) {
311
311
  Object.defineProperty( node, '__unique_id__', { value: ++uniqueId } );
312
312
  let outer = uniqueId ? `##${ node.__unique_id__ }` : '';
313
313
  if (node._outer) {
314
- if (node.$inferred === 'REDIRECTED') {
315
- outer = `/redirected${ outer }`;
316
- }
317
- else {
314
+ // eslint-disable-next-line no-nested-ternary
315
+ outer = (node._outer.items === node) ? `/items${ outer }`
318
316
  // eslint-disable-next-line no-nested-ternary
319
- outer = (node._outer.items === node) ? `/items${ outer }`
320
- // eslint-disable-next-line no-nested-ternary
321
- : (node._outer.returns === node) ? `/returns${ outer }`
322
- // eslint-disable-next-line no-nested-ternary
323
- : (node._outer.targetAspect === node) ? `/target${ outer }`
324
- : `/returns/items${ outer }`;
325
- }
317
+ : (node._outer.returns === node) ? `/returns${ outer }` // TODO returns now normal
318
+ // eslint-disable-next-line no-nested-ternary
319
+ : (node._outer.targetAspect === node) ? `/target${ outer }`
320
+ : `/returns/items${ outer }`;
326
321
  node = node._outer;
327
322
  }
328
323
  if (node === parent)
@@ -93,13 +93,12 @@ function _findWithXPointers( definitionsArray, x, _dependents, _dependencies ) {
93
93
  module.exports = function sortViews({ sql, csn }) {
94
94
  const { cleanup, _dependents, _dependencies } = setDependencies(csn);
95
95
  const { layers, leftover } = sortTopologically(csn, _dependents, _dependencies);
96
- cleanup.forEach(fn => fn());
97
96
  if (leftover.length > 0)
98
97
  throw new ModelError('Unable to build a correct dependency graph! Are there cycles?');
99
98
 
100
99
  const result = [];
101
100
  // keep the "artifact name" - needed for to.hdi sorting
102
- layers.forEach(layer => layer.forEach(objName => result.push({ name: objName, sql: sql[objName] })));
101
+ layers.forEach(layer => layer.forEach(objName => result.push({ name: objName, sql: sql[objName], dependents: csn.definitions[objName][_dependents] })));
103
102
  // attach sql artifacts which are not considered during the view sorting algorithm
104
103
  // --> this is the case for "ALTER TABLE ADD CONSTRAINT" statements,
105
104
  // because their identifiers are not part of the csn.definitions
@@ -107,5 +106,8 @@ module.exports = function sortViews({ sql, csn }) {
107
106
  if (!result.some( o => o.name === name )) // not in result but in incoming sql
108
107
  result.push({ name, sql: sqlString });
109
108
  });
109
+
110
+ cleanup.forEach(fn => fn());
111
+
110
112
  return result;
111
113
  };
@@ -174,7 +174,7 @@ function getElementComparator(otherArtifact, addedElementsDict = null, changedEl
174
174
  if (relevantTypeChange(element.type, otherElement.type) || typeParametersChanged(element, otherElement)) {
175
175
  // Type or parameters, e.g. association target, changed.
176
176
  if(otherElement.notNull && element.notNull === undefined) {
177
- element.$notNull = false; // Explictily set notNull to the implicit default so we render the correct ALTER
177
+ element.$notNull = false; // Explicitly set notNull to the implicit default so we render the correct ALTER
178
178
  }
179
179
  changedElementsDict[name] = changedElement(element, otherElement);
180
180
  }
@@ -3,8 +3,8 @@
3
3
  // Each db has some changes that it can and cannot represent, or that cause problems only on that specific db
4
4
  // In this file, we define rules for each db-dialect to detect and act on these cases.
5
5
 
6
- const { forEach } = require('../../utils/objectUtils');
7
- const { isPersistedAsTable } = require('../../model/csnUtils');
6
+ const { forEach, forEachValue, forEachKey } = require('../../utils/objectUtils');
7
+ const { isPersistedAsTable, applyTransformations } = require('../../model/csnUtils');
8
8
 
9
9
  function isKey( element ) {
10
10
  return element.key;
@@ -32,6 +32,7 @@ module.exports = {
32
32
  postgres: getFilterObject('postgres'),
33
33
  h2: getFilterObject('h2'),
34
34
  hana: getFilterObject('hana'),
35
+ csn: filterCsn,
35
36
  };
36
37
 
37
38
  function getFilterObject( dialect, extensionCallback, migrationCallback ) {
@@ -98,3 +99,40 @@ function typeChangeIsNotCompatible( dialect, before, after ) {
98
99
  }
99
100
  return true;
100
101
  }
102
+
103
+ /**
104
+ * Filter non-diff-relevant properties from a db-expanded CSN.
105
+ * Currently we filter:
106
+ * - annotations
107
+ *
108
+ * @param {CSN.Model} csn CSN to filter
109
+ * @returns {CSN.Model} Filtered input model
110
+ */
111
+ function filterCsn( csn ) {
112
+ const annosToKeep = {
113
+ '@cds.persistence.skip': true,
114
+ '@cds.persistence.exists': true,
115
+ '@cds.persistence.table': true,
116
+ '@cds.persistence.name': true,
117
+ '@sql.append': true,
118
+ '@sql.prepend': true,
119
+ };
120
+
121
+ applyTransformations(csn, {
122
+ elements: (parent, prop, elements) => {
123
+ forEachValue(elements, (element) => {
124
+ forEachKey(element, (key) => {
125
+ if ((key.startsWith('@') && !annosToKeep[key]) || key === 'keys')
126
+ delete element[key];
127
+ });
128
+ });
129
+ },
130
+ }, [ (artifact) => {
131
+ forEachKey(artifact, (key) => {
132
+ if (key.startsWith('@') && !annosToKeep[key])
133
+ delete artifact[key];
134
+ });
135
+ } ]);
136
+
137
+ return csn;
138
+ }
@@ -104,15 +104,12 @@ optionProcessor
104
104
  Valid values are:
105
105
  hanaAssocRealCardinality
106
106
  mapAssocToJoinCardinality
107
- ignoreAssocPublishingInUnion
108
107
  enableUniversalCsn
109
- postgres
110
108
  aspectWithoutElements
111
109
  odataTerms
112
110
  optionalActionFunctionParameters
113
111
  --deprecated <list> Comma separated list of deprecated options.
114
112
  Valid values are:
115
- autoCorrectOrderBySourceRefs
116
113
  eagerPersistenceForGeneratedEntities
117
114
  --fallback-parser <type> If the language cannot be deduced by the file's extensions, use this
118
115
  parser as a fallback. Valid values are: