@sap/cds-compiler 3.4.2 → 3.5.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 +80 -0
- package/README.md +1 -0
- package/bin/cds_update_identifiers.js +5 -5
- package/bin/cdsc.js +15 -16
- package/bin/cdshi.js +19 -6
- package/doc/CHANGELOG_ARCHIVE.md +2 -2
- package/doc/CHANGELOG_BETA.md +9 -1
- package/doc/CHANGELOG_DEPRECATED.md +2 -0
- package/lib/api/main.js +61 -59
- package/lib/api/options.js +4 -2
- package/lib/api/validate.js +2 -2
- package/lib/base/cleanSymbols.js +2 -3
- package/lib/base/dictionaries.js +6 -6
- package/lib/base/error.js +2 -2
- package/lib/base/keywords.js +6 -6
- package/lib/base/location.js +11 -12
- package/lib/base/message-registry.js +177 -58
- package/lib/base/messages.js +252 -180
- package/lib/base/model.js +14 -11
- package/lib/base/node-helpers.js +9 -10
- package/lib/base/optionProcessorHelper.js +138 -129
- package/lib/checks/.eslintrc.json +2 -0
- package/lib/checks/actionsFunctions.js +5 -5
- package/lib/checks/annotationsOData.js +4 -4
- package/lib/checks/arrayOfs.js +1 -1
- package/lib/checks/cdsPersistence.js +1 -1
- package/lib/checks/checkForTypes.js +3 -3
- package/lib/checks/defaultValues.js +3 -3
- package/lib/checks/elements.js +7 -7
- package/lib/checks/emptyOrOnlyVirtual.js +2 -2
- package/lib/checks/foreignKeys.js +1 -1
- package/lib/checks/invalidTarget.js +4 -4
- package/lib/checks/managedInType.js +1 -1
- package/lib/checks/managedWithoutKeys.js +1 -1
- package/lib/checks/nonexpandableStructured.js +5 -3
- package/lib/checks/nullableKeys.js +1 -1
- package/lib/checks/onConditions.js +5 -6
- package/lib/checks/parameters.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -2
- package/lib/checks/selectItems.js +4 -4
- package/lib/checks/sql-snippets.js +4 -4
- package/lib/checks/types.js +7 -7
- package/lib/checks/utils.js +4 -4
- package/lib/checks/validator.js +16 -13
- package/lib/compiler/.eslintrc.json +4 -1
- package/lib/compiler/assert-consistency.js +8 -7
- package/lib/compiler/builtins.js +14 -14
- package/lib/compiler/checks.js +123 -48
- package/lib/compiler/define.js +12 -13
- package/lib/compiler/extend.js +266 -60
- package/lib/compiler/finalize-parse-cdl.js +10 -5
- package/lib/compiler/index.js +17 -14
- package/lib/compiler/populate.js +14 -6
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/resolve.js +2 -15
- package/lib/compiler/shared.js +27 -16
- package/lib/compiler/tweak-assocs.js +5 -6
- package/lib/compiler/utils.js +20 -0
- package/lib/edm/annotations/genericTranslation.js +604 -358
- package/lib/edm/annotations/preprocessAnnotations.js +39 -35
- package/lib/edm/csn2edm.js +275 -222
- package/lib/edm/edm.js +17 -3
- package/lib/edm/edmAnnoPreprocessor.js +6 -6
- package/lib/edm/edmInboundChecks.js +2 -2
- package/lib/edm/edmPreprocessor.js +107 -77
- package/lib/edm/edmUtils.js +44 -5
- package/lib/gen/Dictionary.json +210 -8
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +67 -63
- package/lib/gen/language.tokens +81 -81
- package/lib/gen/languageLexer.interp +4 -10
- package/lib/gen/languageLexer.js +854 -869
- package/lib/gen/languageLexer.tokens +79 -81
- package/lib/gen/languageParser.js +14309 -13832
- package/lib/inspect/inspectModelStatistics.js +2 -2
- package/lib/inspect/inspectPropagation.js +6 -6
- package/lib/inspect/inspectUtils.js +2 -2
- package/lib/json/from-csn.js +102 -55
- package/lib/json/to-csn.js +119 -198
- package/lib/language/antlrParser.js +5 -2
- package/lib/language/docCommentParser.js +6 -6
- package/lib/language/errorStrategy.js +43 -23
- package/lib/language/genericAntlrParser.js +113 -133
- package/lib/language/language.g4 +1550 -1506
- package/lib/language/multiLineStringParser.js +3 -3
- package/lib/language/textUtils.js +2 -2
- package/lib/main.js +3 -3
- package/lib/model/csnRefs.js +5 -0
- package/lib/model/csnUtils.js +130 -122
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/model/sortViews.js +4 -6
- package/lib/modelCompare/compare.js +2 -2
- package/lib/modelCompare/utils/.eslintrc.json +22 -0
- package/lib/modelCompare/utils/filter.js +100 -0
- package/lib/optionProcessor.js +5 -0
- package/lib/render/.eslintrc.json +1 -0
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +12 -12
- package/lib/render/toCdl.js +311 -276
- package/lib/render/toHdbcds.js +97 -94
- package/lib/render/toRename.js +5 -5
- package/lib/render/toSql.js +127 -223
- package/lib/render/utils/common.js +141 -108
- package/lib/render/utils/delta.js +227 -0
- package/lib/render/utils/sql.js +22 -6
- package/lib/render/utils/stringEscapes.js +3 -3
- package/lib/transform/db/.eslintrc.json +2 -0
- package/lib/transform/db/applyTransformations.js +3 -3
- package/lib/transform/db/assertUnique.js +13 -12
- package/lib/transform/db/associations.js +5 -5
- package/lib/transform/db/cdsPersistence.js +10 -8
- package/lib/transform/db/constraints.js +14 -14
- package/lib/transform/db/expansion.js +20 -22
- package/lib/transform/db/flattening.js +24 -42
- package/lib/transform/db/groupByOrderBy.js +3 -3
- package/lib/transform/db/temporal.js +6 -6
- package/lib/transform/db/transformExists.js +23 -23
- package/lib/transform/db/views.js +16 -16
- package/lib/transform/draft/.eslintrc.json +1 -35
- package/lib/transform/draft/db.js +10 -10
- package/lib/transform/draft/odata.js +2 -2
- package/lib/transform/forOdataNew.js +8 -29
- package/lib/transform/forRelationalDB.js +16 -6
- package/lib/transform/localized.js +11 -10
- package/lib/transform/odata/toFinalBaseType.js +41 -27
- package/lib/transform/odata/typesExposure.js +113 -47
- package/lib/transform/parseExpr.js +209 -106
- package/lib/transform/transformUtilsNew.js +17 -10
- package/lib/transform/translateAssocsToJoins.js +24 -19
- package/lib/transform/universalCsn/coreComputed.js +10 -10
- package/lib/transform/universalCsn/universalCsnEnricher.js +26 -26
- package/lib/transform/universalCsn/utils.js +3 -3
- package/lib/utils/file.js +5 -5
- package/lib/utils/moduleResolve.js +13 -13
- package/lib/utils/objectUtils.js +6 -6
- package/lib/utils/term.js +5 -2
- package/lib/utils/timetrace.js +51 -24
- package/package.json +5 -8
- package/share/messages/check-proper-type-of.md +1 -1
- package/share/messages/message-explanations.json +1 -1
- package/share/messages/redirected-to-complex.md +4 -4
- package/share/messages/{syntax-expecting-integer.md → syntax-expecting-unsigned-int.md} +7 -4
- package/lib/modelCompare/filter.js +0 -83
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
1
3
|
'use strict'
|
|
2
4
|
|
|
5
|
+
// const funkyfuncs = Object.keys(require('../compiler/builtins.js').
|
|
6
|
+
// specialFunctions).filter(n => n.length).map(n=>n.toLowerCase());
|
|
7
|
+
|
|
3
8
|
/**
|
|
4
|
-
* parseExpr accepts any JSON object and tries to convert a token stream expression
|
|
9
|
+
* parseExpr() accepts any JSON object and tries to convert a token stream expression
|
|
5
10
|
* array into an AST like expression with CDL operator precedence.
|
|
6
11
|
*
|
|
7
12
|
* The following operators are supported:
|
|
8
13
|
*
|
|
14
|
+
* Unary: +/-
|
|
9
15
|
* Multiplication/Division: '*', '/'
|
|
10
16
|
* Addition/Subtraction: '+', '-'
|
|
11
17
|
* Concatenation: '||'
|
|
@@ -13,7 +19,7 @@
|
|
|
13
19
|
* Unary: 'is [not] null', 'not'
|
|
14
20
|
* Conditional: 'case [when then]+ [else]? end', 'and', 'or'
|
|
15
21
|
*
|
|
16
|
-
* Not yet
|
|
22
|
+
* Not yet implemented: 'new'
|
|
17
23
|
*
|
|
18
24
|
* This is not an optimized LL(1) parser but a token 'sniffer'. A stream is
|
|
19
25
|
* cracked up in sub streams and passed down to the next higher function.
|
|
@@ -27,95 +33,122 @@
|
|
|
27
33
|
* This parser intentionally does no error handling. If a clause is malformed, it is accepted as is.
|
|
28
34
|
*
|
|
29
35
|
* @param {any} xpr A JSON object.
|
|
30
|
-
* @param {
|
|
36
|
+
* @param {Object} state Objet
|
|
37
|
+
* anno: Don't eliminate arrays with single entry in statetations as they are collections
|
|
38
|
+
* array: Bias AST representation.
|
|
39
|
+
* nary: return n-ary or binary tree
|
|
31
40
|
*/
|
|
32
41
|
|
|
33
|
-
function parseExpr(xpr, array
|
|
34
|
-
|
|
42
|
+
function parseExpr(xpr, state = { anno: 0, array: true, nary: true }) {
|
|
43
|
+
// Notes:
|
|
44
|
+
// - Variables `s` and `e` are used as index variables into `xpr`s for start and end.
|
|
45
|
+
// - xpr's are our CSN expressions, see <https://pages.github.tools.sap/cap/docs/cds/cxn>
|
|
46
|
+
|
|
47
|
+
return parseExprInt(xpr, state);
|
|
35
48
|
|
|
36
|
-
function parseExprInt(xpr) {
|
|
37
|
-
return conditionOR(...CaseWhen(xpr));
|
|
49
|
+
function parseExprInt(xpr, state) {
|
|
50
|
+
return conditionOR(...CaseWhen(xpr), state);
|
|
38
51
|
}
|
|
39
52
|
|
|
40
53
|
function CaseWhen(xpr) {
|
|
41
|
-
if(Array.isArray(xpr))
|
|
42
|
-
|
|
54
|
+
if(Array.isArray(xpr)) {
|
|
55
|
+
recurseIntoCases();
|
|
56
|
+
}
|
|
43
57
|
return [xpr, 0, Array.isArray(xpr) ? xpr.length : 1];
|
|
44
58
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
let e = findLastIndex(pxpr, 'end');
|
|
50
|
-
pxpr = pxpr.slice(s+1, e);
|
|
51
|
-
const dist = inner(pxpr, lvl+1);
|
|
52
|
-
e -= dist;
|
|
53
|
-
if(dist > 0)
|
|
54
|
-
pxpr = xpr.slice(s+1, e+1);
|
|
55
|
-
const caseTree = array ? [ 'case' ] : { 'case': [] };
|
|
56
|
-
let i = pxpr.findIndex(t => t === 'else');
|
|
57
|
-
let elseCond = undefined;
|
|
58
|
-
if(i >= 0) {
|
|
59
|
-
elseCond = pxpr.slice(i+1);
|
|
60
|
-
pxpr = pxpr.slice(0, i);
|
|
59
|
+
function recurseIntoCases(casePos=-1, lvl=-1) {
|
|
60
|
+
for(let c = casePos+1; c < xpr.length; c++) {
|
|
61
|
+
if(xpr[c] === 'case') {
|
|
62
|
+
recurseIntoCases(c, lvl+1)
|
|
61
63
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
caseTree
|
|
70
|
-
i = pxpr.findIndex(t => t === 'then');
|
|
71
|
-
if(i >= 0) {
|
|
72
|
-
const arg = pxpr.slice(0, i);
|
|
73
|
-
if(array)
|
|
74
|
-
caseTree.push(arg);
|
|
75
|
-
else
|
|
76
|
-
when.when.push(arg.length === 1 ? arg[0] : arg);
|
|
77
|
-
}
|
|
78
|
-
pxpr = pxpr.slice(i+1);
|
|
79
|
-
i = pxpr.findIndex(t => t === 'when');
|
|
80
|
-
const arg = ((i >= 0) ? pxpr.slice(0, i) : pxpr);
|
|
81
|
-
if(array)
|
|
82
|
-
caseTree.push('then', arg);
|
|
64
|
+
}
|
|
65
|
+
if(lvl > -1) {
|
|
66
|
+
let endPos = casePos;
|
|
67
|
+
while(xpr[endPos] !== 'end' && endPos < xpr.length) endPos++;
|
|
68
|
+
if(xpr[endPos] === 'end') {
|
|
69
|
+
const caseTree = rewriteCaseBlock(casePos, endPos);
|
|
70
|
+
if(casePos === 0 && endPos === xpr.length-1)
|
|
71
|
+
xpr = caseTree;
|
|
83
72
|
else
|
|
84
|
-
|
|
73
|
+
xpr.splice(casePos, endPos-casePos+1, caseTree);
|
|
85
74
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @param {number} casePos
|
|
80
|
+
* @param {number} endPos
|
|
81
|
+
* @return {Array|object}
|
|
82
|
+
*/
|
|
83
|
+
function rewriteCaseBlock(casePos, endPos) {
|
|
84
|
+
const caseTree = state.array ? [ 'case' ] : { 'case': [] };
|
|
85
|
+
|
|
86
|
+
let elsePos = endPos;
|
|
87
|
+
let whenPos = casePos;
|
|
88
|
+
|
|
89
|
+
while(xpr[elsePos] !== 'else' && elsePos > casePos) elsePos--;
|
|
90
|
+
let elseCond = undefined;
|
|
91
|
+
if(xpr[elsePos] === 'else') {
|
|
92
|
+
elseCond = xpr.slice(elsePos+1, endPos);
|
|
93
|
+
endPos = elsePos;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
while(xpr[whenPos] !== 'when' && whenPos < endPos) whenPos++;
|
|
97
|
+
if(xpr[whenPos] === 'when' && whenPos - (casePos+1) >= 1) {
|
|
98
|
+
const arg = xpr.slice(casePos+1, whenPos)
|
|
99
|
+
if(state.array)
|
|
100
|
+
caseTree.push(arg);
|
|
101
|
+
else
|
|
102
|
+
caseTree.case.push(arg);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
while(xpr[whenPos] === 'when') {
|
|
106
|
+
const when = { 'when': [] };
|
|
107
|
+
if(state.array)
|
|
108
|
+
caseTree.push('when');
|
|
109
|
+
else
|
|
110
|
+
caseTree.case.push(when);
|
|
111
|
+
|
|
112
|
+
let thenPos = whenPos+1;
|
|
113
|
+
while(xpr[thenPos] !== 'then' && thenPos < endPos) thenPos++;
|
|
114
|
+
if(xpr[thenPos] === 'then') {
|
|
115
|
+
const when = xpr.slice(whenPos+1, thenPos);
|
|
116
|
+
if(state.array)
|
|
117
|
+
caseTree.push(when);
|
|
89
118
|
else
|
|
90
|
-
|
|
119
|
+
when.when.push(when.length === 1 ? when[0] : when);
|
|
91
120
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
121
|
+
|
|
122
|
+
whenPos = thenPos+1;
|
|
123
|
+
while(xpr[whenPos] !== 'when' && whenPos < endPos) whenPos++;
|
|
124
|
+
if(xpr[whenPos] === 'when' || whenPos === endPos) {
|
|
125
|
+
const then = xpr.slice(thenPos+1, whenPos);
|
|
126
|
+
if(state.array)
|
|
127
|
+
caseTree.push('then', then);
|
|
128
|
+
else
|
|
129
|
+
when.when.push(then.length === 1 ? then[0] : then);
|
|
98
130
|
}
|
|
99
|
-
return e-s+1;
|
|
100
131
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
132
|
+
if(elseCond) {
|
|
133
|
+
if(state.array)
|
|
134
|
+
caseTree.push('else', elseCond);
|
|
135
|
+
else
|
|
136
|
+
caseTree.case.push(elseCond.length === 1 ? elseCond[0] : elseCond);
|
|
137
|
+
}
|
|
138
|
+
if(state.array)
|
|
139
|
+
caseTree.push('end');
|
|
140
|
+
return caseTree;
|
|
108
141
|
}
|
|
109
142
|
}
|
|
110
143
|
|
|
111
|
-
function conditionOR(xpr, s, e) {
|
|
112
|
-
return binaryExpr(xpr, ['or'], conditionAnd, s, e);
|
|
144
|
+
function conditionOR(xpr, s, e, state) {
|
|
145
|
+
return binaryExpr(xpr, ['or'], conditionAnd, s, e, state);
|
|
113
146
|
}
|
|
114
|
-
|
|
147
|
+
|
|
148
|
+
function conditionAnd(xpr, s, e, state) {
|
|
115
149
|
return binaryExpr(xpr, (xpr, s, e) => {
|
|
116
150
|
let a = s-1;
|
|
117
151
|
let b;
|
|
118
|
-
// scan for 'and', skip 'between/and'
|
|
119
152
|
do {
|
|
120
153
|
b = false;
|
|
121
154
|
for(a++; xpr[a] !== 'and' && a < e; a++) {
|
|
@@ -128,26 +161,31 @@ function parseExpr(xpr, array=true) {
|
|
|
128
161
|
return [1, a]
|
|
129
162
|
else
|
|
130
163
|
return [1, -1];
|
|
131
|
-
}, conditionTerm, s, e);
|
|
164
|
+
}, conditionTerm, s, e, state);
|
|
132
165
|
}
|
|
133
166
|
|
|
134
|
-
function conditionTerm(xpr, s, e) {
|
|
167
|
+
function conditionTerm(xpr, s, e, state) {
|
|
135
168
|
if(Array.isArray(xpr)) {
|
|
136
169
|
if(xpr.length >= 3 && xpr[s+1] === 'is') {
|
|
170
|
+
const isnull = conditionOR(xpr[s], state);
|
|
137
171
|
if(xpr[s+2] === 'null')
|
|
138
|
-
return array ? [
|
|
172
|
+
return state.array ? [ isnull, 'is', 'null' ] : { 'isNull': isnull };
|
|
139
173
|
else if(xpr[s+2] === 'not' && xpr[s+3] === 'null')
|
|
140
|
-
return array ? [
|
|
174
|
+
return state.array ? [ isnull, 'is', 'not', 'null' ] : { 'isNotNull': isnull };
|
|
175
|
+
}
|
|
176
|
+
if(xpr[s] === 'not') {
|
|
177
|
+
const not = conditionTerm(xpr, s+1, e, state)
|
|
178
|
+
return state.array ? [ 'not', not ] : { 'not': not };
|
|
179
|
+
}
|
|
180
|
+
if(xpr[s] === 'exists') {
|
|
181
|
+
const exists = conditionTerm(xpr, s+1, e, state)
|
|
182
|
+
return state.array ? [ 'exists', exists ] : { 'exists': exists };
|
|
141
183
|
}
|
|
142
|
-
if(xpr[s] === 'not')
|
|
143
|
-
return array ? [ 'not', conditionTerm(xpr, s+1, e) ] : { 'not': conditionTerm(xpr, s+1, e) };
|
|
144
|
-
if(xpr[s] === 'exists')
|
|
145
|
-
return array ? [ 'exists', conditionOR(xpr[s+1]) ] : { 'exists': conditionOR(xpr[s+1]) };
|
|
146
184
|
}
|
|
147
|
-
return compareTerm(xpr, s, e);
|
|
185
|
+
return compareTerm(xpr, s, e, state);
|
|
148
186
|
}
|
|
149
187
|
|
|
150
|
-
function compareTerm(xpr, s, e) {
|
|
188
|
+
function compareTerm(xpr, s, e, state) {
|
|
151
189
|
if(Array.isArray(xpr)) {
|
|
152
190
|
let i = s;
|
|
153
191
|
while(i < e && xpr[i] !== 'between') i++;
|
|
@@ -155,20 +193,26 @@ function parseExpr(xpr, array=true) {
|
|
|
155
193
|
while(i < e && xpr[i] !== 'and') i++;
|
|
156
194
|
const a = i < e ? i : -1;
|
|
157
195
|
if(b >= 0) {
|
|
158
|
-
|
|
159
|
-
const
|
|
196
|
+
let token = [ 'between' ];
|
|
197
|
+
const not = (xpr[b-1] === 'not');
|
|
198
|
+
if(not)
|
|
199
|
+
token.splice(0,0, 'not');
|
|
200
|
+
const expr = expression(xpr, s, not ? b-1 : b, state);
|
|
201
|
+
const between = state.array
|
|
202
|
+
? [ expr, ...token ]
|
|
203
|
+
: { 'between': [ expr ] };
|
|
160
204
|
if(a >= 0) {
|
|
161
|
-
const lower = expression(xpr, b+1, a);
|
|
162
|
-
const upper = expression(xpr, a+1, e);
|
|
163
|
-
if(array)
|
|
205
|
+
const lower = expression(xpr, b+1, a, state);
|
|
206
|
+
const upper = expression(xpr, a+1, e, state);
|
|
207
|
+
if(state.array)
|
|
164
208
|
between.push(lower, 'and', upper);
|
|
165
209
|
else {
|
|
166
210
|
between.between.push(lower, upper);
|
|
167
211
|
}
|
|
168
212
|
}
|
|
169
213
|
else {
|
|
170
|
-
const unspec = expression(xpr, b+1, e);
|
|
171
|
-
if(array)
|
|
214
|
+
const unspec = expression(xpr, b+1, e, state);
|
|
215
|
+
if(state.array)
|
|
172
216
|
between.push(unspec);
|
|
173
217
|
else
|
|
174
218
|
between.between.push(unspec);
|
|
@@ -176,55 +220,114 @@ function parseExpr(xpr, array=true) {
|
|
|
176
220
|
return between;
|
|
177
221
|
}
|
|
178
222
|
}
|
|
179
|
-
return binaryExpr(xpr,
|
|
223
|
+
return binaryExpr(xpr, (xpr, s, e) => {
|
|
224
|
+
const token = ['=', '<>', '>', '>=', '<', '<=', '!=', 'like', 'in'];
|
|
225
|
+
while(s < e && !token.includes(xpr[s])) s++;
|
|
226
|
+
if(s < e) {
|
|
227
|
+
if(xpr[s-1] === 'not' && (xpr[s] === 'in' || xpr[s] === 'like'))
|
|
228
|
+
return [2, s-1];
|
|
229
|
+
else
|
|
230
|
+
return [1, s];
|
|
231
|
+
}
|
|
232
|
+
return [1, -1];
|
|
233
|
+
}, expression, s, e, state);
|
|
180
234
|
}
|
|
181
235
|
|
|
182
|
-
function expression(xpr, s, e) {
|
|
183
|
-
return binaryExpr(xpr, ['||'], exprAddSub, s, e);
|
|
236
|
+
function expression(xpr, s, e, state) {
|
|
237
|
+
return binaryExpr(xpr, ['||'], exprAddSub, s, e, state);
|
|
184
238
|
}
|
|
185
239
|
|
|
186
|
-
function exprAddSub(xpr, s, e) {
|
|
187
|
-
return binaryExpr(xpr,
|
|
240
|
+
function exprAddSub(xpr, s, e, state) {
|
|
241
|
+
return binaryExpr(xpr, (xpr, s, e) => {
|
|
242
|
+
const skips = [ '+', '-', '*', '/' ];
|
|
243
|
+
let found = false;
|
|
244
|
+
let p=s;
|
|
245
|
+
while(!found && p < e) {
|
|
246
|
+
found = ((xpr[p] === '+' || xpr[p] === '-') && p > s && !skips.includes(xpr[p-1]) && p < e);
|
|
247
|
+
if(!found) p++;
|
|
248
|
+
}
|
|
249
|
+
if(found)
|
|
250
|
+
return [1, p];
|
|
251
|
+
return [1, -1];
|
|
252
|
+
}, exprMulDiv, s, e, state);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function exprMulDiv(xpr, s, e, state) {
|
|
256
|
+
return binaryExpr(xpr, ['*', '/'], unary, s, e, state);
|
|
188
257
|
}
|
|
189
258
|
|
|
190
|
-
function
|
|
191
|
-
|
|
259
|
+
function unary(xpr, s, e, state) {
|
|
260
|
+
if(Array.isArray(xpr)) {
|
|
261
|
+
if(xpr[s] === '+' || xpr[s] === '-') {
|
|
262
|
+
return [ xpr[s], unary(xpr, s+1, e, state) ];
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return terminal(xpr, s, e, state);
|
|
192
266
|
}
|
|
267
|
+
function terminal(xpr, s, e, state) {
|
|
268
|
+
const csnarray = [
|
|
269
|
+
'ref', 'args', 'columns', 'keys', 'expand', 'inline',
|
|
270
|
+
'requires', 'extensions', 'includes', 'excluding'
|
|
271
|
+
];
|
|
272
|
+
const xprarray = [
|
|
273
|
+
'xpr', 'on', 'where', 'orderBy', 'groupBy', 'having' ];
|
|
193
274
|
|
|
194
|
-
function terminal(xpr, s, e) {
|
|
195
275
|
if(Array.isArray(xpr) && xpr.length > 0) {
|
|
196
|
-
if(e-s <= 1)
|
|
197
|
-
return parseExprInt(xpr[e-1]);
|
|
276
|
+
if(e-s <= 1 && state.anno === 0)
|
|
277
|
+
return parseExprInt(xpr[e-1], state);
|
|
198
278
|
else
|
|
199
|
-
return xpr.slice(s, e).map(parseExprInt);
|
|
279
|
+
return xpr.slice(s, e).map(ix => parseExprInt(ix, state));
|
|
200
280
|
}
|
|
201
281
|
if (typeof xpr === 'object') {
|
|
282
|
+
// if(xpr?.func && funkyfuncs.includes(xpr?.func))
|
|
283
|
+
// return xpr;
|
|
202
284
|
for(let n in xpr) {
|
|
203
|
-
|
|
285
|
+
const x = xpr[n];
|
|
286
|
+
if(n[0] === '@')
|
|
287
|
+
state.anno++;
|
|
288
|
+
if(Array.isArray(x)) {
|
|
289
|
+
if(csnarray.includes(n) || state.anno !== 0)
|
|
290
|
+
xpr[n] = x.map(ix => parseExprInt(ix, state));
|
|
291
|
+
else if(xprarray.includes(n) && x.length === 1)
|
|
292
|
+
xpr[n] = x.map(ix => parseExprInt(ix, state));
|
|
293
|
+
else
|
|
294
|
+
xpr[n] = parseExprInt(x, state);
|
|
295
|
+
}
|
|
296
|
+
else
|
|
297
|
+
xpr[n] = parseExprInt(x, state);
|
|
298
|
+
if(n[0] === '@')
|
|
299
|
+
state.anno--;
|
|
204
300
|
}
|
|
205
301
|
}
|
|
206
302
|
return xpr;
|
|
207
303
|
}
|
|
208
304
|
|
|
209
|
-
function binaryExpr(xpr, token, next, s, e) {
|
|
305
|
+
function binaryExpr(xpr, token, next, s, e, state) {
|
|
306
|
+
const expr = [];
|
|
210
307
|
if (Array.isArray(xpr)) {
|
|
211
308
|
let [tl, p] = findToken(s, e);
|
|
212
309
|
if (p >= 0) {
|
|
213
|
-
let lhs = next(xpr, s, p);
|
|
214
|
-
|
|
310
|
+
let lhs = next(xpr, s, p, state);
|
|
311
|
+
expr.push(lhs);
|
|
312
|
+
let op = xpr.slice(p, p+tl);
|
|
215
313
|
s = p+tl;
|
|
216
314
|
[tl, p] = findToken(s, e);
|
|
217
315
|
while(p >= 0) {
|
|
218
|
-
let rhs = next(xpr, s, p);
|
|
219
|
-
|
|
220
|
-
|
|
316
|
+
let rhs = next(xpr, s, p, state);
|
|
317
|
+
expr.push(...op, rhs);
|
|
318
|
+
lhs = state.array ? [ lhs, ...op, rhs ] : { [op.join('')]: [lhs, rhs] };
|
|
319
|
+
op = xpr.slice(p, p+tl);
|
|
221
320
|
s = p+tl;
|
|
222
321
|
[tl, p] = findToken(s, e);
|
|
223
322
|
}
|
|
224
|
-
|
|
323
|
+
expr.push(...op, next(xpr, s, e, state));
|
|
324
|
+
if (state.array)
|
|
325
|
+
return (state.nary ? expr : [ lhs, ...op, next(xpr, s, e, state) ])
|
|
326
|
+
else
|
|
327
|
+
return { [op.join('')]: [lhs, next(xpr, s, e, state)] };
|
|
225
328
|
}
|
|
226
329
|
}
|
|
227
|
-
return next(xpr, s, e);
|
|
330
|
+
return next(xpr, s, e, state);
|
|
228
331
|
|
|
229
332
|
function findToken(s, e) {
|
|
230
333
|
if(typeof token === 'function')
|
|
@@ -10,9 +10,9 @@ const { setProp } = require('../base/model');
|
|
|
10
10
|
const { copyAnnotations, applyTransformations } = require('../model/csnUtils');
|
|
11
11
|
const { cloneCsnNonDict, cloneCsnDictionary, getUtils } = require('../model/csnUtils');
|
|
12
12
|
const { typeParameters, isBuiltinType } = require('../compiler/builtins');
|
|
13
|
-
const { ModelError } = require(
|
|
13
|
+
const { ModelError } = require('../base/error');
|
|
14
14
|
const { forEach } = require('../utils/objectUtils');
|
|
15
|
-
const { pathName } = require(
|
|
15
|
+
const { pathName } = require('../compiler/utils');
|
|
16
16
|
|
|
17
17
|
const RestrictedOperators = ['<', '>', '>=', '<='];
|
|
18
18
|
const RelationalOperators = ['=', '!=', '<>', 'is' /*, 'like'*/,...RestrictedOperators];
|
|
@@ -1220,17 +1220,24 @@ function getTransformers(model, options, pathDelimiter = '_') {
|
|
|
1220
1220
|
}
|
|
1221
1221
|
// t_0 OR ... OR t_n with t = (a <not equal> b)
|
|
1222
1222
|
const bop = (op === 'is' && not) || op === '!=' || op === '<>' ? 'or' : 'and';
|
|
1223
|
-
|
|
1223
|
+
const xpr = { xpr: [] };
|
|
1224
1224
|
xrefvalues.filter(x => x.lhs && x.rhs).forEach((x,i) => {
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1225
|
+
xpr.i = i;
|
|
1226
|
+
if(i>0) {
|
|
1227
|
+
xpr.xpr.push(bop);
|
|
1228
|
+
}
|
|
1229
|
+
xpr.xpr.push(x.lhs);
|
|
1230
|
+
xpr.xpr.push(op);
|
|
1229
1231
|
if(not)
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
+
xpr.xpr.push('not')
|
|
1233
|
+
xpr.xpr.push(x.rhs);
|
|
1232
1234
|
});
|
|
1233
|
-
|
|
1235
|
+
if(xpr.i > 0) {
|
|
1236
|
+
delete xpr.i;
|
|
1237
|
+
rc.push(xpr);
|
|
1238
|
+
}
|
|
1239
|
+
else
|
|
1240
|
+
rc.push(...xpr.xpr);
|
|
1234
1241
|
i += not ? 3 : 2;
|
|
1235
1242
|
}
|
|
1236
1243
|
else
|
|
@@ -12,16 +12,16 @@ const { timetrace } = require('../utils/timetrace');
|
|
|
12
12
|
const internalArtifactKinds = ['builtin', '$parameters', 'param'];
|
|
13
13
|
|
|
14
14
|
function translateAssocsToJoinsCSN(csn, options){
|
|
15
|
-
timetrace.start('Recompiling model');
|
|
15
|
+
timetrace.start('A2J: Recompiling model');
|
|
16
16
|
// Do not re-complain about localized
|
|
17
17
|
const compileOptions = { ...options, $skipNameCheck: true };
|
|
18
18
|
delete compileOptions.csnFlavor;
|
|
19
19
|
const model = recompileX(csn, compileOptions);
|
|
20
|
-
timetrace.stop();
|
|
21
|
-
timetrace.start('Translating associations to joins');
|
|
20
|
+
timetrace.stop('A2J: Recompiling model');
|
|
21
|
+
timetrace.start('A2J: Translating associations to joins');
|
|
22
22
|
translateAssocsToJoins(model, options);
|
|
23
|
-
timetrace.stop();
|
|
24
|
-
timetrace.start('Post-processing columns');
|
|
23
|
+
timetrace.stop('A2J: Translating associations to joins');
|
|
24
|
+
timetrace.start('A2J: Post-processing columns');
|
|
25
25
|
// Use the effective elements list as columns
|
|
26
26
|
forEachDefinition(model, art => {
|
|
27
27
|
if (art.$queries) {
|
|
@@ -36,7 +36,7 @@ function translateAssocsToJoinsCSN(csn, options){
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
|
-
timetrace.stop();
|
|
39
|
+
timetrace.stop('A2J: Post-processing columns');
|
|
40
40
|
|
|
41
41
|
if (options.messages) {
|
|
42
42
|
// Make sure that we don't complain twice about the same things
|
|
@@ -46,8 +46,7 @@ function translateAssocsToJoinsCSN(csn, options){
|
|
|
46
46
|
// If A2J reports error - end! Continuing with a broken CSN makes no sense
|
|
47
47
|
makeMessageFunction(model, options).throwWithAnyError();
|
|
48
48
|
// FIXME: Move this somewhere more appropriate
|
|
49
|
-
|
|
50
|
-
return compact;
|
|
49
|
+
return compactModel(model, compileOptions);
|
|
51
50
|
}
|
|
52
51
|
|
|
53
52
|
function translateAssocsToJoins(model, inputOptions = {})
|
|
@@ -329,9 +328,10 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
329
328
|
|
|
330
329
|
for(let i = 0; i < tail.length-1; i++) {
|
|
331
330
|
if(tail[i]._navigation && tail[i]._navigation.$njr) {
|
|
332
|
-
// the correct flattened foreign key must match the leaf artifact of this path
|
|
331
|
+
// the correct flattened foreign key must match the leaf artifact and access path prefix of this path
|
|
333
332
|
const leafArt = tail[tail.length-1]._artifact;
|
|
334
|
-
const
|
|
333
|
+
const tailPath = tail.map(p=>p.id).join(pathDelimiter);
|
|
334
|
+
const fk = tail[i]._artifact.$flatSrcFKs.find(f => f._artifact === leafArt && f.acc.startsWith(tailPath));
|
|
335
335
|
if(!fk) {
|
|
336
336
|
// const revealInternalProperties = require('../model/revealInternalProperties.js');
|
|
337
337
|
// console.log('++++++++ Path tail: ', revealInternalProperties(tail[tail.length-1]._artifact));
|
|
@@ -565,7 +565,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
565
565
|
QATs until a QA has been found).
|
|
566
566
|
*/
|
|
567
567
|
if(!assoc.$flatSrcFKs)
|
|
568
|
-
setProp(assoc, '$flatSrcFKs', flattenElement(assoc, true, assoc.name.id));
|
|
568
|
+
setProp(assoc, '$flatSrcFKs', flattenElement(assoc, true, assoc.name.id, assoc.name.id));
|
|
569
569
|
if(!assoc.$flatTgtFKs)
|
|
570
570
|
setProp(assoc, '$flatTgtFKs', flattenElement(assoc, false));
|
|
571
571
|
|
|
@@ -664,7 +664,8 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
664
664
|
}
|
|
665
665
|
// If this is a backlink condition, produce the
|
|
666
666
|
// ON cond of the forward assoc with swapped src/tgt aliases
|
|
667
|
-
else if(i < args.length-2 && args[i].path &&
|
|
667
|
+
else if(i < args.length-2 && args[i].path &&
|
|
668
|
+
args[i+1]?.literal === 'token' && args[i+1]?.val === '=' && args[i+2].path)
|
|
668
669
|
{
|
|
669
670
|
let fwdAssoc = getForwardAssociation(args[i].path, args[i+2].path);
|
|
670
671
|
if(fwdAssoc)
|
|
@@ -1088,11 +1089,11 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1088
1089
|
respecting the src or the target side of the ON condition.
|
|
1089
1090
|
Return an array of column names and it's leaf element.
|
|
1090
1091
|
*/
|
|
1091
|
-
function flattenElement(element, srcSide, prefix)
|
|
1092
|
+
function flattenElement(element, srcSide, prefix, acc)
|
|
1092
1093
|
{
|
|
1093
1094
|
// terminate if element is unstructured
|
|
1094
1095
|
if(!element.foreignKeys && !element.elements)
|
|
1095
|
-
return [ { id: prefix, _artifact: element } ];
|
|
1096
|
+
return [ { id: prefix, _artifact: element, acc } ];
|
|
1096
1097
|
|
|
1097
1098
|
let paths = [];
|
|
1098
1099
|
// get paths of managed assocs (unmanaged assocs are not allowed in FK paths)
|
|
@@ -1103,7 +1104,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1103
1104
|
let fk = element.foreignKeys[fkn];
|
|
1104
1105
|
// once a fk is to be followed, treat all sub patsh as srcSide, this will add fk.name.id only
|
|
1105
1106
|
if(srcSide)
|
|
1106
|
-
paths = paths.concat(flattenElement(fk.targetElement._artifact, true, fk.name.id));
|
|
1107
|
+
paths = paths.concat(flattenElement(fk.targetElement._artifact, true, fk.name.id, fk.targetElement.path.map(ps => ps.id).join(pathDelimiter)));
|
|
1107
1108
|
else
|
|
1108
1109
|
{
|
|
1109
1110
|
// consume path segments until the next assoc and substitute against fk alias until path is eaten up
|
|
@@ -1113,7 +1114,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1113
1114
|
[tail, fkPrefix] = substituteFKAliasForPath(assocStep, tail, fkPrefix);
|
|
1114
1115
|
[assocStep, tail, fkPrefix] = pathAsStringUpToAssoc(tail, fkPrefix);
|
|
1115
1116
|
}
|
|
1116
|
-
paths = paths.concat(flattenElement(fk.targetElement._artifact, true, fkPrefix));
|
|
1117
|
+
paths = paths.concat(flattenElement(fk.targetElement._artifact, true, fkPrefix, fk.targetElement.path.map(ps => ps.id).join(pathDelimiter)));
|
|
1117
1118
|
}
|
|
1118
1119
|
}
|
|
1119
1120
|
}
|
|
@@ -1123,11 +1124,15 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1123
1124
|
for(let n in element.elements)
|
|
1124
1125
|
{
|
|
1125
1126
|
let elt = element.elements[n];
|
|
1126
|
-
paths = paths.concat(flattenElement(elt, true, elt.name.id));
|
|
1127
|
+
paths = paths.concat(flattenElement(elt, true, elt.name.id, elt.name.id));
|
|
1127
1128
|
}
|
|
1128
1129
|
}
|
|
1129
1130
|
return paths.map(p => {
|
|
1130
|
-
return {
|
|
1131
|
+
return {
|
|
1132
|
+
id: (prefix ? prefix + pathDelimiter : '' ) + p.id,
|
|
1133
|
+
acc: (acc ? acc + pathDelimiter : '') + p.acc,
|
|
1134
|
+
_artifact: p._artifact
|
|
1135
|
+
}
|
|
1131
1136
|
} );
|
|
1132
1137
|
}
|
|
1133
1138
|
|
|
@@ -1556,7 +1561,7 @@ function translateAssocsToJoins(model, inputOptions = {})
|
|
|
1556
1561
|
if(!qat[qatChildrenName]) {
|
|
1557
1562
|
setProp(qat, '$njr', true);
|
|
1558
1563
|
// flatten left hand side ON condition paths ( => foreign keys to the source side)
|
|
1559
|
-
setProp(art, '$flatSrcFKs', flattenElement(art, true, art.name.id));
|
|
1564
|
+
setProp(art, '$flatSrcFKs', flattenElement(art, true, art.name.id, art.name.id));
|
|
1560
1565
|
}
|
|
1561
1566
|
}
|
|
1562
1567
|
else {
|