@sap/cds-compiler 3.6.2 → 3.8.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 +109 -1
- package/README.md +3 -0
- package/bin/cdsc.js +12 -5
- package/doc/CHANGELOG_ARCHIVE.md +6 -6
- package/doc/CHANGELOG_BETA.md +35 -2
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/doc/DeprecatedOptions_v2.md +1 -1
- package/doc/NameResolution.md +1 -1
- package/lib/api/main.js +63 -23
- package/lib/api/options.js +1 -0
- package/lib/api/validate.js +5 -0
- package/lib/base/dictionaries.js +15 -3
- package/lib/base/keywords.js +2 -0
- package/lib/base/message-registry.js +120 -34
- package/lib/base/messages.js +51 -27
- package/lib/base/model.js +4 -2
- package/lib/base/shuffle.js +2 -1
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/defaultValues.js +1 -1
- package/lib/checks/elements.js +29 -1
- package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +10 -6
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/onConditions.js +15 -9
- package/lib/checks/sql-snippets.js +2 -2
- package/lib/checks/types.js +5 -1
- package/lib/checks/validator.js +7 -3
- package/lib/compiler/assert-consistency.js +42 -26
- package/lib/compiler/base.js +50 -4
- package/lib/compiler/builtins.js +17 -8
- package/lib/compiler/checks.js +241 -246
- package/lib/compiler/define.js +113 -146
- package/lib/compiler/extend.js +889 -383
- package/lib/compiler/finalize-parse-cdl.js +5 -58
- package/lib/compiler/index.js +1 -1
- package/lib/compiler/kick-start.js +7 -8
- package/lib/compiler/populate.js +297 -293
- package/lib/compiler/propagator.js +27 -18
- package/lib/compiler/resolve.js +146 -463
- package/lib/compiler/shared.js +36 -79
- package/lib/compiler/tweak-assocs.js +30 -28
- package/lib/compiler/utils.js +31 -5
- package/lib/edm/annotations/genericTranslation.js +131 -59
- package/lib/edm/annotations/preprocessAnnotations.js +3 -0
- package/lib/edm/csn2edm.js +22 -5
- package/lib/edm/edm.js +6 -4
- package/lib/edm/edmAnnoPreprocessor.js +1 -0
- package/lib/edm/edmPreprocessor.js +42 -26
- package/lib/gen/Dictionary.json +38 -2
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +3 -1
- package/lib/gen/languageLexer.js +1 -1
- package/lib/gen/languageParser.js +4828 -4472
- package/lib/inspect/inspectPropagation.js +20 -34
- package/lib/json/from-csn.js +140 -44
- package/lib/json/to-csn.js +114 -122
- package/lib/language/errorStrategy.js +2 -0
- package/lib/language/genericAntlrParser.js +156 -36
- package/lib/language/language.g4 +100 -58
- package/lib/language/textUtils.js +13 -0
- package/lib/main.d.ts +43 -3
- package/lib/main.js +4 -2
- package/lib/model/csnRefs.js +15 -3
- package/lib/model/csnUtils.js +12 -74
- package/lib/model/revealInternalProperties.js +4 -2
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +3 -0
- package/lib/render/manageConstraints.js +5 -2
- package/lib/render/toCdl.js +216 -104
- package/lib/render/toHdbcds.js +2 -9
- package/lib/render/toRename.js +14 -51
- package/lib/render/toSql.js +4 -3
- package/lib/render/utils/common.js +9 -5
- package/lib/transform/braceExpression.js +6 -0
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/expansion.js +2 -0
- package/lib/transform/db/flattening.js +37 -36
- package/lib/transform/db/rewriteCalculatedElements.js +600 -0
- package/lib/transform/db/transformExists.js +4 -0
- package/lib/transform/db/views.js +40 -37
- package/lib/transform/forOdataNew.js +20 -15
- package/lib/transform/forRelationalDB.js +58 -41
- package/lib/transform/odata/typesExposure.js +50 -15
- package/lib/transform/parseExpr.js +16 -8
- package/lib/transform/transformUtilsNew.js +42 -14
- package/lib/transform/translateAssocsToJoins.js +60 -37
- package/lib/transform/universalCsn/coreComputed.js +15 -7
- package/lib/transform/universalCsn/universalCsnEnricher.js +4 -4
- package/package.json +2 -1
package/lib/language/language.g4
CHANGED
|
@@ -581,17 +581,17 @@ elementDefInner[ art, outer, mightBeEnum ]
|
|
|
581
581
|
@after{ this.attachLocation( $art ); }
|
|
582
582
|
:
|
|
583
583
|
// VIRTUAL is keyword, except if before the following tokens texts:
|
|
584
|
-
{ this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^[
|
|
584
|
+
{ this.setLocalToken( 'VIRTUAL', 'VIRTUAL', /^[;:{@=}]$/ ); }
|
|
585
585
|
( virtual=VIRTUAL { $art.virtual = this.valueWithTokenLocation( true, $virtual ); } )?
|
|
586
586
|
( key=KEY { $art.key = this.valueWithTokenLocation( true, $key ); } )?
|
|
587
|
-
{ this.setLocalToken( 'MASKED', 'MASKED', /^[
|
|
587
|
+
{ this.setLocalToken( 'MASKED', 'MASKED', /^[;:{@=}]$/ ); }
|
|
588
588
|
( masked=MASKED
|
|
589
589
|
{
|
|
590
590
|
$art.masked = this.valueWithTokenLocation( true, $masked ) ;
|
|
591
591
|
this.message( 'syntax-unsupported-masked', $masked, { keyword: 'masked' } );
|
|
592
592
|
}
|
|
593
593
|
)?
|
|
594
|
-
{ this.setLocalToken( 'ELEMENT', 'ELEMENT', /^[
|
|
594
|
+
{ this.setLocalToken( 'ELEMENT', 'ELEMENT', /^[;:{@=}]$/ ); }
|
|
595
595
|
( ELEMENT { $mightBeEnum = false; } )? // auto-recognizable at other places
|
|
596
596
|
name=ident['Element']
|
|
597
597
|
{ this.addDef( $art, $outer, 'elements', 'element', $name.id );
|
|
@@ -610,13 +610,15 @@ elementDefInner[ art, outer, mightBeEnum ]
|
|
|
610
610
|
'=' e=expression // SQL has syntax variant using AS - we DO NOT
|
|
611
611
|
{ $art.value = $e.expr;
|
|
612
612
|
// this.setIntroLocation( eq ); -- future
|
|
613
|
-
if ($mightBeEnum && ($e.expr
|
|
613
|
+
if ($mightBeEnum && ($e.expr?.val !== undefined || $e.expr?.sym !== undefined) &&
|
|
614
614
|
!$virtual && !$key && !$masked && !$art.elements && !$art.type)
|
|
615
615
|
$art['$'+'syntax'] = 'enum';
|
|
616
616
|
}
|
|
617
617
|
{ this.docComment( $art ); }
|
|
618
618
|
annotationAssignment_ll1[ $art ]* // for enum symbol def via EXTEND
|
|
619
619
|
requiredSemi
|
|
620
|
+
|
|
|
621
|
+
requiredSemi
|
|
620
622
|
)
|
|
621
623
|
;
|
|
622
624
|
|
|
@@ -716,14 +718,19 @@ elementType[ art ] // TODO: split this monster rule
|
|
|
716
718
|
;
|
|
717
719
|
|
|
718
720
|
elementProperties[ elem ]
|
|
721
|
+
:
|
|
722
|
+
defaultAndNullablity[ $elem ]
|
|
723
|
+
|
|
|
724
|
+
'=' e=expression { $elem.value = $e.expr; }
|
|
725
|
+
;
|
|
726
|
+
|
|
727
|
+
defaultAndNullablity[ elem ]
|
|
719
728
|
:
|
|
720
729
|
defaultValue[ $elem ]
|
|
721
730
|
nullability[ $elem ]? // placement accoring to SQL spec
|
|
722
731
|
|
|
|
723
732
|
nullability[ $elem ]
|
|
724
733
|
defaultValue[ $elem ]?
|
|
725
|
-
|
|
|
726
|
-
'=' e=expression { $elem.value = $e.expr; }
|
|
727
734
|
;
|
|
728
735
|
|
|
729
736
|
defaultValue[ art ] locals[ elem, elements = {} ]
|
|
@@ -761,13 +768,13 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
761
768
|
|
|
|
762
769
|
requiredSemi
|
|
763
770
|
|
|
|
764
|
-
ELEMENTS
|
|
771
|
+
ELEMENTS { $art.elements = this.createDict(); } '{'
|
|
765
772
|
elementDefOrExtend[ $art ]*
|
|
766
773
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
767
774
|
{ this.checkExtensionDict( $art.elements ); }
|
|
768
775
|
optionalSemi
|
|
769
776
|
|
|
|
770
|
-
ENUM
|
|
777
|
+
ENUM { $art.enum = this.createDict(); } '{'
|
|
771
778
|
enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
|
|
772
779
|
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
773
780
|
optionalSemi
|
|
@@ -782,24 +789,22 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
782
789
|
includeRef[ $art ] ( ',' includeRef[ $art ] )*
|
|
783
790
|
requiredSemi
|
|
784
791
|
|
|
|
785
|
-
DEFINITIONS
|
|
786
|
-
'{' { $art.artifacts = this.createDict(); }
|
|
792
|
+
DEFINITIONS { $art.artifacts = this.createDict(); } '{'
|
|
787
793
|
artifactDefOrExtend[ $art, 'definitions' ]*
|
|
788
794
|
'}' { this.finalizeDictOrArray( $art.artifacts ); }
|
|
789
795
|
optionalSemi
|
|
790
796
|
|
|
|
791
|
-
COLUMNS
|
|
792
|
-
'{' { $art.columns = []; }
|
|
797
|
+
COLUMNS { $art.columns = this.createArray(); } '{'
|
|
793
798
|
(
|
|
794
799
|
selectItemDef[ $art.columns ]
|
|
795
800
|
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
796
801
|
selectItemDef[ $art.columns ]
|
|
797
802
|
)*
|
|
798
803
|
)?
|
|
799
|
-
'}'
|
|
804
|
+
'}' { this.finalizeDictOrArray( $art.columns ); }
|
|
800
805
|
optionalSemi
|
|
801
806
|
|
|
|
802
|
-
ACTIONS
|
|
807
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
803
808
|
actionFunctionDef[ $art ]* // TODO: no EXTEND in actions? (ok, would just allow annos)
|
|
804
809
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
805
810
|
optionalSemi
|
|
@@ -811,7 +816,7 @@ extendArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
811
816
|
extendService[ art, outer ] locals[ name = {} ]
|
|
812
817
|
@after { this.attachLocation( $art ); }
|
|
813
818
|
:
|
|
814
|
-
SERVICE { $art.expectedKind =
|
|
819
|
+
SERVICE { $art.expectedKind = this.valueWithTokenLocation(); }
|
|
815
820
|
simplePath[ $name, 'Service' ]
|
|
816
821
|
{ $art.name = $name; this.addItem( $art, $outer, 'extensions', 'extend' ); }
|
|
817
822
|
( WITH { this.noSemicolonHere(); } )?
|
|
@@ -821,7 +826,7 @@ extendService[ art, outer ] locals[ name = {} ]
|
|
|
821
826
|
extendContext[ art, outer ] locals[ name = {} ]
|
|
822
827
|
@after { this.attachLocation( $art ); }
|
|
823
828
|
:
|
|
824
|
-
CONTEXT { $art.expectedKind =
|
|
829
|
+
CONTEXT { $art.expectedKind = this.valueWithTokenLocation(); }
|
|
825
830
|
simplePath[ $name, 'Context' ]
|
|
826
831
|
{ $art.name = $name; this.addItem( $art, $outer, 'extensions', 'extend' ); }
|
|
827
832
|
( WITH { this.noSemicolonHere(); } )?
|
|
@@ -831,8 +836,9 @@ extendContext[ art, outer ] locals[ name = {} ]
|
|
|
831
836
|
extendEntityOrAspect[ art, outer ] locals[ name = {} ]
|
|
832
837
|
@after { /* #ATN 1 */ this.attachLocation( $art ); }
|
|
833
838
|
:
|
|
834
|
-
|
|
835
|
-
|
|
839
|
+
(ASPECT | ENTITY) { $art.expectedKind = this.valueWithTokenLocation(); }
|
|
840
|
+
simplePath[ $name, 'Extend' ]
|
|
841
|
+
{ $art.name = $name;
|
|
836
842
|
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
837
843
|
}
|
|
838
844
|
(
|
|
@@ -858,13 +864,13 @@ extendForEntity[ art ]
|
|
|
858
864
|
elementDefOrExtend[ $art ]*
|
|
859
865
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
860
866
|
(
|
|
861
|
-
ACTIONS
|
|
867
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
862
868
|
actionFunctionDef[ $art ]*
|
|
863
869
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
864
870
|
)?
|
|
865
871
|
optionalSemi
|
|
866
872
|
|
|
|
867
|
-
ACTIONS
|
|
873
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
868
874
|
actionFunctionDef[ $art ]*
|
|
869
875
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
870
876
|
optionalSemi
|
|
@@ -875,30 +881,31 @@ extendForEntity[ art ]
|
|
|
875
881
|
extendProjection[ art, outer ] locals[ name = {} ]
|
|
876
882
|
@after { this.attachLocation( $art ); }
|
|
877
883
|
:
|
|
878
|
-
|
|
879
|
-
|
|
884
|
+
PROJECTION { $art.expectedKind = this.valueWithTokenLocation( 'entity' ); }
|
|
885
|
+
simplePath[ $name, 'Extend' ]
|
|
886
|
+
{ $art.name = $name;
|
|
880
887
|
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
881
888
|
}
|
|
882
889
|
( WITH { this.noSemicolonHere(); } )?
|
|
883
890
|
{ this.docComment( $art ); }
|
|
884
891
|
annotationAssignment_ll1[ $art ]*
|
|
885
892
|
(
|
|
886
|
-
'{' { $art.columns =
|
|
893
|
+
'{' { $art.columns = this.createArray(); }
|
|
887
894
|
(
|
|
888
895
|
selectItemDef[ $art.columns ]
|
|
889
896
|
( ',' { if (this.isStraightBefore("}")) break; } // allow ',' before '}'
|
|
890
897
|
selectItemDef[ $art.columns ]
|
|
891
898
|
)*
|
|
892
899
|
)?
|
|
893
|
-
'}'
|
|
900
|
+
'}' { this.finalizeDictOrArray( $art.columns ); }
|
|
894
901
|
(
|
|
895
|
-
ACTIONS
|
|
902
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
896
903
|
actionFunctionDef[ $art ]*
|
|
897
904
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
898
905
|
)?
|
|
899
906
|
optionalSemi
|
|
900
907
|
|
|
|
901
|
-
ACTIONS
|
|
908
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
902
909
|
actionFunctionDef[ $art ]*
|
|
903
910
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
904
911
|
optionalSemi
|
|
@@ -910,8 +917,9 @@ extendProjection[ art, outer ] locals[ name = {} ]
|
|
|
910
917
|
extendType[ art, outer ] locals[ name = {} ]
|
|
911
918
|
@after { this.attachLocation( $art ); }
|
|
912
919
|
:
|
|
913
|
-
TYPE
|
|
914
|
-
|
|
920
|
+
TYPE { $art.expectedKind = this.valueWithTokenLocation(); }
|
|
921
|
+
simplePath[ $name, 'Extend' ]
|
|
922
|
+
{ $art.name = $name;
|
|
915
923
|
this.addItem( $art, $outer, 'extensions', 'extend' );
|
|
916
924
|
}
|
|
917
925
|
// extendWithOptElementsOrType + includeRef:
|
|
@@ -952,13 +960,13 @@ extendWithOptElementsOrType[ art ]
|
|
|
952
960
|
{ this.checkExtensionDict( $art.elements ); }
|
|
953
961
|
optionalSemi
|
|
954
962
|
|
|
|
955
|
-
ELEMENTS
|
|
963
|
+
ELEMENTS { $art.elements = this.createDict(); } '{'
|
|
956
964
|
elementDefOrExtend[ $art ]*
|
|
957
965
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
958
966
|
{ this.checkExtensionDict( $art.elements ); }
|
|
959
967
|
optionalSemi
|
|
960
968
|
|
|
|
961
|
-
ENUM
|
|
969
|
+
ENUM { $art.enum = this.createDict(); } '{'
|
|
962
970
|
enumSymbolDef[ $art ]* // TODO: no EXTEND in enum? (ok, would just allow annos)
|
|
963
971
|
'}' { this.finalizeDictOrArray( $art.enum ); }
|
|
964
972
|
optionalSemi
|
|
@@ -1006,7 +1014,7 @@ extendElement[ art, outer ]
|
|
|
1006
1014
|
@after{ this.attachLocation( $art ); }
|
|
1007
1015
|
:
|
|
1008
1016
|
{ this.setLocalToken( 'ELEMENT', 'ELEMENT', /^([:{@=}()]|WITH)$/i ); }
|
|
1009
|
-
(
|
|
1017
|
+
( ELEMENT { $art.expectedKind = this.valueWithTokenLocation(); } )?
|
|
1010
1018
|
name=ident['Element']
|
|
1011
1019
|
{ this.addDef( $art, $outer, 'elements', 'extend', $name.id ); }
|
|
1012
1020
|
extendWithOptElementsOrType[ $art, $art ]
|
|
@@ -1016,7 +1024,7 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
1016
1024
|
@after { this.attachLocation( $art ); }
|
|
1017
1025
|
:
|
|
1018
1026
|
simplePath[ $name, 'Annotate' ]
|
|
1019
|
-
( ':' simplePath[ $elemName, 'Element'] )?
|
|
1027
|
+
( ':' simplePath[ $elemName, 'Element'] )? // TODO: do like for extendArtifact !
|
|
1020
1028
|
{ this.addExtension( $art, $outer, 'annotate', $name, $elemName.path ); }
|
|
1021
1029
|
( WITH { this.noSemicolonHere(); } )?
|
|
1022
1030
|
{ this.docComment( $art ); }
|
|
@@ -1027,16 +1035,14 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
1027
1035
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1028
1036
|
{ this.checkExtensionDict( $art.elements ); }
|
|
1029
1037
|
(
|
|
1030
|
-
ACTIONS
|
|
1031
|
-
'{' { $art.actions = this.createDict(); }
|
|
1038
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
1032
1039
|
annotateAction[ $art ]*
|
|
1033
1040
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
1034
1041
|
{ this.checkExtensionDict( $art.actions ); }
|
|
1035
1042
|
)?
|
|
1036
1043
|
optionalSemi
|
|
1037
1044
|
|
|
|
1038
|
-
ACTIONS
|
|
1039
|
-
'{' { $art.actions = this.createDict(); }
|
|
1045
|
+
ACTIONS { $art.actions = this.createDict(); } '{'
|
|
1040
1046
|
annotateAction[ $art ]*
|
|
1041
1047
|
'}' { this.finalizeDictOrArray( $art.actions ); }
|
|
1042
1048
|
{ this.checkExtensionDict( $art.actions ); }
|
|
@@ -1050,8 +1056,8 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
1050
1056
|
')' { this.finalizeDictOrArray( $art.params ); }
|
|
1051
1057
|
{ this.checkExtensionDict( $art.params ); }
|
|
1052
1058
|
(
|
|
1053
|
-
|
|
1054
|
-
|
|
1059
|
+
// TODO: set proper $art.returns
|
|
1060
|
+
RETURNS { $art.elements = this.createDict(); } { $art['$'+'syntax'] = 'returns'; } '{'
|
|
1055
1061
|
annotateElement[ $art ]*
|
|
1056
1062
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1057
1063
|
{ this.checkExtensionDict( $art.elements ); }
|
|
@@ -1060,8 +1066,8 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
1060
1066
|
requiredSemi
|
|
1061
1067
|
)
|
|
1062
1068
|
|
|
|
1063
|
-
|
|
1064
|
-
|
|
1069
|
+
// TODO: set proper $art.returns
|
|
1070
|
+
RETURNS { $art.elements = this.createDict(); } { $art['$'+'syntax'] = 'returns'; } '{'
|
|
1065
1071
|
annotateElement[ $art ]*
|
|
1066
1072
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1067
1073
|
{ this.checkExtensionDict( $art.elements ); }
|
|
@@ -1111,7 +1117,8 @@ annotateAction [ outer ] locals [ art = {} ]
|
|
|
1111
1117
|
{ this.checkExtensionDict( $art.params ); }
|
|
1112
1118
|
)?
|
|
1113
1119
|
(
|
|
1114
|
-
|
|
1120
|
+
// TODO: set proper $art.returns
|
|
1121
|
+
RETURNS { $art.elements = this.createDict(); } { $art['$'+'syntax'] = 'returns'; } '{'
|
|
1115
1122
|
annotateElement[ $art ]*
|
|
1116
1123
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1117
1124
|
{ this.checkExtensionDict( $art.elements ); }
|
|
@@ -1205,7 +1212,10 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1205
1212
|
@after{ /* #ATN 3 */ }
|
|
1206
1213
|
:
|
|
1207
1214
|
typeStruct[ $art ]
|
|
1208
|
-
|
|
1215
|
+
( nullability[ $art ]
|
|
1216
|
+
requiredSemi
|
|
1217
|
+
| optionalSemi
|
|
1218
|
+
)
|
|
1209
1219
|
|
|
|
1210
1220
|
':'
|
|
1211
1221
|
// #ATN: typeRefOptArgs can start with ARRAY or MANY or ASSOCIATION or TYPE or LOCALIZED
|
|
@@ -1213,7 +1223,10 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1213
1223
|
{ this.setLocalToken( 'MANY', 'HelperToken1', /^[^\{]/ ); }
|
|
1214
1224
|
(
|
|
1215
1225
|
typeStruct[ $art ]
|
|
1216
|
-
|
|
1226
|
+
( nullability[ $art ]
|
|
1227
|
+
requiredSemi
|
|
1228
|
+
| optionalSemi
|
|
1229
|
+
)
|
|
1217
1230
|
|
|
|
1218
1231
|
typeAssociationBase[ $art, false ]
|
|
1219
1232
|
// #ATN: path could start with MANY or ONE - make sure a token follows in same rule!
|
|
@@ -1225,7 +1238,7 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1225
1238
|
{ $art.items = { location: this.tokenLocation( $many ) };}
|
|
1226
1239
|
typeStruct[ $art.items ]
|
|
1227
1240
|
nullability[ $art.items ]?
|
|
1228
|
-
optionalSemi
|
|
1241
|
+
optionalSemi // TODO(v4): Should be requiredSemi with nullability
|
|
1229
1242
|
|
|
|
1230
1243
|
(
|
|
1231
1244
|
array=ARRAY of=OF
|
|
@@ -1236,7 +1249,7 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1236
1249
|
// #ATN: typeRefOptArgs can start with TYPE
|
|
1237
1250
|
( typeStruct[ $art.items ]
|
|
1238
1251
|
nullability[ $art.items ]?
|
|
1239
|
-
optionalSemi
|
|
1252
|
+
optionalSemi // TODO(v4): Should be requiredSemi with nullability
|
|
1240
1253
|
| ( typeTypeOf[ $art.items ] | typeRefOptArgs[ $art.items ] )
|
|
1241
1254
|
nullability[ $art.items ]?
|
|
1242
1255
|
{ this.docComment( $art ); }
|
|
@@ -1257,13 +1270,14 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1257
1270
|
)
|
|
1258
1271
|
|
|
|
1259
1272
|
typeTypeOf[ $art ]
|
|
1260
|
-
|
|
1273
|
+
defaultAndNullablity[ $art ]?
|
|
1261
1274
|
{ this.docComment( $art ); }
|
|
1262
|
-
annotationAssignment_ll1[ $art ]*
|
|
1275
|
+
annotationAssignment_ll1[ $art ]*
|
|
1276
|
+
requiredSemi
|
|
1263
1277
|
|
|
|
1264
1278
|
l=LOCALIZED { $art.localized = this.valueWithTokenLocation( true, $l ); }
|
|
1265
1279
|
typeRefOptArgs[ $art ]
|
|
1266
|
-
|
|
1280
|
+
defaultAndNullablity[ $art ]?
|
|
1267
1281
|
{ this.docComment( $art ); }
|
|
1268
1282
|
annotationAssignment_ll1[ $art ]*
|
|
1269
1283
|
requiredSemi
|
|
@@ -1287,11 +1301,11 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1287
1301
|
(
|
|
1288
1302
|
optionalSemi
|
|
1289
1303
|
|
|
|
1290
|
-
|
|
1304
|
+
defaultAndNullablity[ $art ]
|
|
1291
1305
|
requiredSemi
|
|
1292
1306
|
)
|
|
1293
1307
|
|
|
|
1294
|
-
|
|
1308
|
+
defaultAndNullablity[ $art ]?
|
|
1295
1309
|
requiredSemi
|
|
1296
1310
|
)
|
|
1297
1311
|
|
|
|
@@ -1301,7 +1315,10 @@ typeSpecSemi[ art ] // with 'includes', for type and annotation defs
|
|
|
1301
1315
|
includeRef[ $art ]
|
|
1302
1316
|
)*
|
|
1303
1317
|
typeStruct[ $art ]
|
|
1304
|
-
optionalSemi
|
|
1318
|
+
( optionalSemi
|
|
1319
|
+
| nullability[ $art ]
|
|
1320
|
+
requiredSemi
|
|
1321
|
+
)
|
|
1305
1322
|
)
|
|
1306
1323
|
)
|
|
1307
1324
|
;
|
|
@@ -1511,6 +1528,7 @@ typeRefOptArgs[ art ]
|
|
|
1511
1528
|
;
|
|
1512
1529
|
|
|
1513
1530
|
typeRefArgs[ art ]
|
|
1531
|
+
@after { this.checkTypeArgs($art); }
|
|
1514
1532
|
:
|
|
1515
1533
|
paren='(' { $art['$'+'typeArgs'] = this.createArray(); }
|
|
1516
1534
|
(
|
|
@@ -1746,7 +1764,7 @@ tableTerm returns [ table ]
|
|
|
1746
1764
|
( AS a1=ident['FromAlias'] { $table.name = $a1.id } // for defining table aliass
|
|
1747
1765
|
| a2=identNoKeyword['FromAlias'] { $table.name = this.fragileAlias( $a2.id, true ); }
|
|
1748
1766
|
// not using ident` to have a similar behavior to above
|
|
1749
|
-
)
|
|
1767
|
+
)?
|
|
1750
1768
|
|
|
|
1751
1769
|
te=tableExpression close=')'
|
|
1752
1770
|
{ $table = this.surroundByParens( $te.table, $open, $close ); }
|
|
@@ -2040,8 +2058,21 @@ conditionEOF returns [ cond ]
|
|
|
2040
2058
|
c=condition { $cond = $c.cond; } EOF
|
|
2041
2059
|
;
|
|
2042
2060
|
|
|
2043
|
-
condition returns [ cond ] locals[ args = [] ]
|
|
2044
|
-
@after{ $cond = this.argsExpression( $args, true ); }
|
|
2061
|
+
condition returns [ cond ] locals [ args = [], orl = [] ]
|
|
2062
|
+
@after{ $cond = this.argsExpression( $args, $ctx.e2 ? '?:' : true ); }
|
|
2063
|
+
:
|
|
2064
|
+
c1=conditionOr { $args.push($c1.cond); }
|
|
2065
|
+
(
|
|
2066
|
+
// '?' is not mentioned in code completion, see errorStrategy.js#intervalSetToArray
|
|
2067
|
+
q='?' { this.pushXprToken( $args ); }
|
|
2068
|
+
e2=expression { $args.push($e2.expr); }
|
|
2069
|
+
colon=':' { this.pushXprToken( $args ); }
|
|
2070
|
+
e3=expression { $args.push($e3.expr); }
|
|
2071
|
+
)?
|
|
2072
|
+
;
|
|
2073
|
+
|
|
2074
|
+
conditionOr returns [ cond ] locals [ args = [], orl = [] ]
|
|
2075
|
+
@after{ $cond = this.argsExpression( $args, 'or' ); }
|
|
2045
2076
|
:
|
|
2046
2077
|
c1=conditionAnd { $args.push($c1.cond); }
|
|
2047
2078
|
(
|
|
@@ -2181,10 +2212,9 @@ expressionTerm returns [ expr ] locals [ args = [] ]
|
|
|
2181
2212
|
END { this.pushXprToken( $args ); }
|
|
2182
2213
|
|
|
|
2183
2214
|
ne=NEW { this.pushXprToken( $args ); } // token rewrite for NEW
|
|
2184
|
-
// please note: there will be no compiler-supported code completion after NEW
|
|
2185
|
-
{ $expr = { op: this.valueWithTokenLocation( 'new', $ne ), args: [] };
|
|
2186
|
-
this.error( 'syntax-unsupported-new', $ne, { keyword: $ne.text }, '$(KEYWORD) is not supported' ); }
|
|
2187
2215
|
nqp=valuePath[ 'ref', null ]
|
|
2216
|
+
{ $args.push( this.valuePathAst( $nqp.qp ) ); }
|
|
2217
|
+
{ this.fixNewKeywordPlacement( $args ); }
|
|
2188
2218
|
|
|
|
2189
2219
|
vp=valuePath[ 'ref', null ] { $args.push( this.valuePathAst( $vp.qp ) ); }
|
|
2190
2220
|
{ this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
|
|
@@ -2345,7 +2375,7 @@ namedExpression[ pathStep, id ]
|
|
|
2345
2375
|
:
|
|
2346
2376
|
elem=expression
|
|
2347
2377
|
{ if ($pathStep && $id) {
|
|
2348
|
-
this.addDef( ($ctx.elem) ? $elem.expr : { location: $id.location },
|
|
2378
|
+
this.addDef( ($ctx.elem && $elem.expr) ? $elem.expr : { location: $id.location },
|
|
2349
2379
|
$pathStep, 'args', 0, $id );
|
|
2350
2380
|
}
|
|
2351
2381
|
}
|
|
@@ -2752,6 +2782,13 @@ annoValueBase[ assignment ] locals [ seenEllipsis = false ]
|
|
|
2752
2782
|
|
|
|
2753
2783
|
( plus='+' | min='-' ) num=Number
|
|
2754
2784
|
{ Object.assign( $assignment, this.numberLiteral( $num, $plus||$min ) ); }
|
|
2785
|
+
|
|
|
2786
|
+
'('
|
|
2787
|
+
cond=condition // 'condition' is also used in 'expression' inside '()'.
|
|
2788
|
+
// TODO: (1,2,3) not supported, yet, only ((1,2,3)); we could support it via:
|
|
2789
|
+
// (',' condition)*
|
|
2790
|
+
{ this.expressionAsAnnotationValue( $assignment, $ctx.cond ); }
|
|
2791
|
+
')'
|
|
2755
2792
|
;
|
|
2756
2793
|
|
|
2757
2794
|
flattenedValue[ assignment ] locals[ val = { name: {} } ]
|
|
@@ -2807,6 +2844,11 @@ annoSubValue returns[ val = {} ]
|
|
|
2807
2844
|
|
|
|
2808
2845
|
at='@'? annotationPath[ $val, 'ref', $at ]
|
|
2809
2846
|
( annotationPathVariant[ $val ] )?
|
|
2847
|
+
|
|
|
2848
|
+
'('
|
|
2849
|
+
cond=condition
|
|
2850
|
+
{ this.expressionAsAnnotationValue( $val, $ctx.cond ); }
|
|
2851
|
+
')'
|
|
2810
2852
|
;
|
|
2811
2853
|
|
|
2812
2854
|
// Literal values and identifiers -----------------------------------------------
|
|
@@ -37,8 +37,21 @@ function isWhitespaceCharacterNoNewline( char ) {
|
|
|
37
37
|
return whitespaceRegEx.test(char);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Normalized CDL newlines to LF (\n). CDL newlines also contain PS and other
|
|
42
|
+
* characters, see cdlNewLineRegEx.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} str
|
|
45
|
+
* @return {string}
|
|
46
|
+
*/
|
|
47
|
+
function normalizeNewLine( str ) {
|
|
48
|
+
// Note: cdlNewLineRegEx does not have `g` flag and we can't use replaceAll in Node 14.
|
|
49
|
+
return str.replace(new RegExp(cdlNewLineRegEx, 'ug'), '\n');
|
|
50
|
+
}
|
|
51
|
+
|
|
40
52
|
module.exports = {
|
|
41
53
|
isWhitespaceOrNewLineOnly,
|
|
42
54
|
isWhitespaceCharacterNoNewline,
|
|
43
55
|
cdlNewLineRegEx,
|
|
56
|
+
normalizeNewLine,
|
|
44
57
|
};
|
package/lib/main.d.ts
CHANGED
|
@@ -818,6 +818,23 @@ declare namespace compiler {
|
|
|
818
818
|
* @param dialect
|
|
819
819
|
*/
|
|
820
820
|
function delimitedId(name: string, dialect: string) : string;
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Return all non-lossy changes in artifacts between two given models.
|
|
824
|
+
* Note: Only supports changes in artifacts compiled/rendered as db-CSN/SQL.
|
|
825
|
+
*
|
|
826
|
+
* ATTENTION: This function may change without prior notice!
|
|
827
|
+
* It is still considered work-in-progress!
|
|
828
|
+
*
|
|
829
|
+
* @beta
|
|
830
|
+
*
|
|
831
|
+
* @param csn A client/inferred CSN model representing the desired "after-image"
|
|
832
|
+
* @param options SQL specific options
|
|
833
|
+
* @param beforeImage A db-transformed CSN representing the "before-image", or null in case no such image
|
|
834
|
+
* is known, i.e. for the very first migration step.
|
|
835
|
+
* @returns See {@link SqlMigrationResult} for details.
|
|
836
|
+
*/
|
|
837
|
+
function migration(csn: CSN, options: SqlOptions, beforeImage: CSN): SqlMigrationResult;
|
|
821
838
|
}
|
|
822
839
|
|
|
823
840
|
/**
|
|
@@ -884,10 +901,10 @@ declare namespace compiler {
|
|
|
884
901
|
* Return all changes in artifacts between two given models.
|
|
885
902
|
* Note: Only supports changes in entities (not views etc.) compiled/rendered as HANA-CSN/SQL.
|
|
886
903
|
*
|
|
887
|
-
* @param csn A
|
|
904
|
+
* @param csn A client/inferred CSN model representing the desired "after-image"
|
|
888
905
|
* @param options Options
|
|
889
|
-
* @param beforeImage A HANA-transformed CSN representing the "before-image", or null in case
|
|
890
|
-
*
|
|
906
|
+
* @param beforeImage A SAP HANA-transformed CSN representing the "before-image", or null in case
|
|
907
|
+
* no such image is known, i.e. for the very first migration step.
|
|
891
908
|
* @returns See {@link HdiMigrationResult} for details.
|
|
892
909
|
*/
|
|
893
910
|
function migration(csn: CSN, options: HdiOptions, beforeImage: CSN): HdiMigrationResult;
|
|
@@ -942,6 +959,29 @@ declare namespace compiler {
|
|
|
942
959
|
}>,
|
|
943
960
|
}
|
|
944
961
|
|
|
962
|
+
/**
|
|
963
|
+
* Result type of {@link to.sql.migration}.
|
|
964
|
+
*
|
|
965
|
+
* ATTENTION: This structure may change without prior notice!
|
|
966
|
+
* It is still considered work-in-progress!
|
|
967
|
+
*
|
|
968
|
+
* @beta
|
|
969
|
+
*/
|
|
970
|
+
export type SqlMigrationResult = {
|
|
971
|
+
/**
|
|
972
|
+
* The desired after-image in db-transformed CSN format
|
|
973
|
+
*/
|
|
974
|
+
afterImage: CSN
|
|
975
|
+
/**
|
|
976
|
+
* An array of SQL statements to drop views/tables.
|
|
977
|
+
*/
|
|
978
|
+
drops: string[],
|
|
979
|
+
/**
|
|
980
|
+
* An array of SQL statements to ALTER/CREATE tables/views.
|
|
981
|
+
*/
|
|
982
|
+
createsAndAlters: string[],
|
|
983
|
+
}
|
|
984
|
+
|
|
945
985
|
/**
|
|
946
986
|
* Return the resulting database name for (absolute) 'artifactName', depending on the current naming
|
|
947
987
|
* mode.
|
package/lib/main.js
CHANGED
|
@@ -26,6 +26,7 @@ const compiler = lazyload('./compiler');
|
|
|
26
26
|
const shared = lazyload('./compiler/shared');
|
|
27
27
|
const define = lazyload('./compiler/define');
|
|
28
28
|
const builtins = lazyload('./compiler/builtins');
|
|
29
|
+
const base = lazyload('./compiler/base');
|
|
29
30
|
const finalizeParseCdl = lazyload('./compiler/finalize-parse-cdl');
|
|
30
31
|
|
|
31
32
|
// The compiler version (taken from package.json)
|
|
@@ -157,9 +158,10 @@ module.exports = {
|
|
|
157
158
|
// INTERNAL functions for the cds-lsp package and friends - before you use
|
|
158
159
|
// it, you MUST talk with us - there can be potential incompatibilities with
|
|
159
160
|
// new releases (even having the same major version):
|
|
160
|
-
$lsp: {
|
|
161
|
+
$lsp: {
|
|
162
|
+
parse: (...args) => compiler.parseX(...args),
|
|
161
163
|
compile: (...args) => compiler.compileX(...args),
|
|
162
|
-
getArtifactName:
|
|
164
|
+
getArtifactName: (...args) => base.getArtifactName(...args),
|
|
163
165
|
},
|
|
164
166
|
|
|
165
167
|
// CSN Model related functionality
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -637,10 +637,18 @@ function csnRefs( csn, universalReady ) {
|
|
|
637
637
|
/**
|
|
638
638
|
* @param {CSN.Path} path
|
|
639
639
|
* @param {CSN.Artifact} art
|
|
640
|
+
* @param {CSN.Artifact} parent
|
|
640
641
|
* @param {string} [scope]
|
|
641
642
|
* @param [extraInfo]
|
|
642
643
|
*/
|
|
643
644
|
function resolvePath( path, art, parent, scope, extraInfo ) {
|
|
645
|
+
if (!art && path.length > 1) {
|
|
646
|
+
// TODO: For path.length===1, it may be that `art` is undefined, e.g. for CSN paths such
|
|
647
|
+
// as `[…, 'on', 1]` where the path segment refers to `=`.
|
|
648
|
+
// TODO: Check the call-side.
|
|
649
|
+
const loc = locationString(parent?.$location);
|
|
650
|
+
throw new ModelError(`Path item 0='${ pathId(path[0]) }' refers to nothing; in ${ loc }; path=${ JSON.stringify(path) }`);
|
|
651
|
+
}
|
|
644
652
|
const staticAssoc = extraInfo === 'static' && scope === 'global';
|
|
645
653
|
/** @type {{idx, art?, env?}[]} */
|
|
646
654
|
const links = path.map( (_v, idx) => ({ idx }) );
|
|
@@ -657,7 +665,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
657
665
|
if (!art) {
|
|
658
666
|
const { env } = links[i - 1];
|
|
659
667
|
const loc = env.name && env.name.$location || env.$location;
|
|
660
|
-
throw new ModelError( `Path item ${ i }=${ pathId( path[i] ) }
|
|
668
|
+
throw new ModelError( `Path item ${ i }=${ pathId( path[i] ) } refers to nothing; in ${ locationString( loc ) }; path=${ JSON.stringify(path) }` );
|
|
661
669
|
}
|
|
662
670
|
links[i].art = art;
|
|
663
671
|
}
|
|
@@ -706,8 +714,12 @@ function csnRefs( csn, universalReady ) {
|
|
|
706
714
|
if (query !== main)
|
|
707
715
|
cache.set( query, qcache );
|
|
708
716
|
|
|
709
|
-
if (fromSelect)
|
|
710
|
-
|
|
717
|
+
if (fromSelect) {
|
|
718
|
+
const $queryNumber = all.length + 1;
|
|
719
|
+
const alias = query.as || `$_select_${ $queryNumber }__`;
|
|
720
|
+
getCache(fromSelect, '$aliases')[alias] = qcache;
|
|
721
|
+
}
|
|
722
|
+
|
|
711
723
|
const select = query.SELECT || query.projection;
|
|
712
724
|
if (select) {
|
|
713
725
|
cache.set( select, qcache ); // query and query.SELECT have the same cache qcache
|