@sap/cds-compiler 3.9.4 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +92 -4
- package/README.md +0 -1
- package/bin/cdsc.js +11 -23
- package/bin/cdsse.js +3 -3
- package/doc/API.md +5 -0
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +17 -1
- package/doc/CHANGELOG_DEPRECATED.md +28 -0
- package/lib/api/.eslintrc.json +1 -1
- package/lib/api/main.js +26 -8
- package/lib/api/options.js +2 -0
- package/lib/base/error.js +2 -0
- package/lib/base/message-registry.js +143 -64
- package/lib/base/messages.js +213 -107
- package/lib/base/model.js +11 -11
- package/lib/checks/.eslintrc.json +1 -1
- package/lib/checks/annotationsOData.js +2 -2
- package/lib/checks/elements.js +1 -1
- package/lib/checks/enricher.js +26 -3
- package/lib/checks/onConditions.js +67 -12
- package/lib/checks/queryNoDbArtifacts.js +106 -105
- package/lib/checks/sql-snippets.js +2 -0
- package/lib/checks/types.js +12 -6
- package/lib/checks/validator.js +2 -2
- package/lib/compiler/assert-consistency.js +10 -8
- package/lib/compiler/builtins.js +8 -2
- package/lib/compiler/checks.js +52 -35
- package/lib/compiler/define.js +31 -26
- package/lib/compiler/extend.js +120 -65
- package/lib/compiler/finalize-parse-cdl.js +12 -43
- package/lib/compiler/generate.js +16 -5
- package/lib/compiler/index.js +8 -5
- package/lib/compiler/kick-start.js +4 -3
- package/lib/compiler/populate.js +96 -95
- package/lib/compiler/propagator.js +7 -8
- package/lib/compiler/resolve.js +377 -103
- package/lib/compiler/shared.js +794 -517
- package/lib/compiler/tweak-assocs.js +8 -6
- package/lib/compiler/utils.js +44 -0
- package/lib/edm/annotations/genericTranslation.js +12 -4
- package/lib/edm/csn2edm.js +34 -32
- package/lib/edm/edm.js +34 -31
- package/lib/edm/edmAnnoPreprocessor.js +0 -23
- package/lib/edm/edmInboundChecks.js +7 -2
- package/lib/edm/edmPreprocessor.js +18 -17
- package/lib/edm/edmUtils.js +8 -4
- package/lib/gen/Dictionary.json +18 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +4 -2
- package/lib/gen/languageParser.js +5006 -4582
- package/lib/json/from-csn.js +157 -112
- package/lib/json/to-csn.js +60 -89
- package/lib/language/antlrParser.js +17 -13
- package/lib/language/docCommentParser.js +11 -1
- package/lib/language/genericAntlrParser.js +13 -10
- package/lib/language/language.g4 +168 -97
- package/lib/main.d.ts +128 -36
- package/lib/main.js +1 -1
- package/lib/model/csnRefs.js +24 -5
- package/lib/model/csnUtils.js +9 -8
- package/lib/model/revealInternalProperties.js +7 -12
- package/lib/modelCompare/compare.js +1 -1
- package/lib/modelCompare/utils/filter.js +40 -2
- package/lib/optionProcessor.js +0 -3
- package/lib/render/toCdl.js +247 -214
- package/lib/render/toHdbcds.js +197 -181
- package/lib/render/toSql.js +325 -289
- package/lib/render/utils/common.js +42 -4
- package/lib/render/utils/delta.js +1 -1
- package/lib/render/utils/sql.js +3 -3
- package/lib/transform/braceExpression.js +2 -2
- package/lib/transform/db/.eslintrc.json +1 -1
- package/lib/transform/db/applyTransformations.js +3 -3
- package/lib/transform/db/associations.js +24 -12
- package/lib/transform/db/expansion.js +17 -18
- package/lib/transform/db/flattening.js +17 -21
- package/lib/transform/db/rewriteCalculatedElements.js +171 -64
- package/lib/transform/db/views.js +3 -4
- package/lib/transform/draft/db.js +21 -12
- package/lib/transform/draft/odata.js +4 -0
- package/lib/transform/forOdataNew.js +11 -10
- package/lib/transform/forRelationalDB.js +12 -7
- package/lib/transform/localized.js +4 -2
- package/lib/transform/odata/toFinalBaseType.js +5 -5
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/parseExpr.js +3 -0
- package/lib/transform/transformUtilsNew.js +43 -23
- package/lib/transform/translateAssocsToJoins.js +7 -6
- package/lib/transform/universalCsn/.eslintrc.json +1 -1
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -12
- package/package.json +2 -2
- package/share/messages/{duplicate-autoexposed.md → def-duplicate-autoexposed.md} +5 -1
- 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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
568
|
-
*
|
|
569
|
-
*
|
|
570
|
-
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
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
|
-
|
|
581
|
-
|
|
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('
|
|
714
|
-
* // '
|
|
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('
|
|
731
|
-
* // '
|
|
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('
|
|
746
|
-
* // '![
|
|
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('
|
|
781
|
-
* // '
|
|
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('
|
|
798
|
-
* // '
|
|
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('
|
|
814
|
-
* // '"
|
|
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://
|
|
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://
|
|
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://
|
|
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: (
|
|
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),
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -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
|
|
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'
|
|
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 );
|
|
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') {
|
package/lib/model/csnUtils.js
CHANGED
|
@@ -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, '', '
|
|
544
|
+
callback.forEach(cb => cb(construct.returns, '', 'returns', [ ...path, 'returns' ], construct));
|
|
545
545
|
}
|
|
546
546
|
else {
|
|
547
|
-
callback(construct.returns, '', '
|
|
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
|
|
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
|
-
|
|
315
|
-
|
|
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
|
-
|
|
320
|
-
|
|
321
|
-
: (node._outer.
|
|
322
|
-
|
|
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)
|
|
@@ -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; //
|
|
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
|
+
}
|
package/lib/optionProcessor.js
CHANGED
|
@@ -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:
|