@sap/cds-compiler 3.4.4 → 3.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +1 -0
- package/bin/cds_update_identifiers.js +5 -5
- package/bin/cdsc.js +12 -12
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +9 -1
- package/doc/CHANGELOG_DEPRECATED.md +2 -0
- package/lib/api/main.js +58 -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 +124 -28
- package/lib/base/messages.js +247 -179
- 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/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 +1 -1
- package/lib/compiler/assert-consistency.js +0 -1
- package/lib/compiler/builtins.js +1 -1
- package/lib/compiler/checks.js +73 -15
- package/lib/compiler/define.js +3 -7
- package/lib/compiler/extend.js +212 -32
- package/lib/compiler/finalize-parse-cdl.js +7 -2
- package/lib/compiler/index.js +17 -14
- package/lib/compiler/populate.js +2 -5
- package/lib/compiler/propagator.js +2 -0
- package/lib/compiler/shared.js +23 -12
- package/lib/compiler/tweak-assocs.js +5 -6
- package/lib/compiler/utils.js +6 -0
- package/lib/edm/annotations/genericTranslation.js +553 -319
- package/lib/edm/annotations/preprocessAnnotations.js +39 -35
- package/lib/edm/csn2edm.js +88 -75
- package/lib/edm/edm.js +17 -3
- package/lib/edm/edmAnnoPreprocessor.js +5 -5
- package/lib/edm/edmPreprocessor.js +106 -76
- package/lib/edm/edmUtils.js +41 -2
- package/lib/gen/Dictionary.json +34 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +66 -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 +14360 -14146
- 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 +82 -40
- package/lib/json/to-csn.js +82 -157
- package/lib/language/.eslintrc.json +1 -4
- package/lib/language/genericAntlrParser.js +59 -38
- package/lib/language/language.g4 +1508 -1490
- package/lib/language/multiLineStringParser.js +1 -1
- package/lib/main.js +3 -3
- package/lib/model/csnUtils.js +130 -122
- package/lib/model/revealInternalProperties.js +1 -1
- package/lib/model/sortViews.js +4 -6
- package/lib/modelCompare/utils/filter.js +4 -3
- package/lib/optionProcessor.js +5 -0
- package/lib/render/DuplicateChecker.js +1 -1
- package/lib/render/manageConstraints.js +12 -12
- package/lib/render/toCdl.js +225 -159
- package/lib/render/toHdbcds.js +63 -63
- package/lib/render/toRename.js +5 -5
- package/lib/render/toSql.js +55 -65
- package/lib/render/utils/common.js +20 -37
- package/lib/render/utils/delta.js +3 -3
- package/lib/render/utils/sql.js +22 -6
- package/lib/render/utils/stringEscapes.js +3 -3
- 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/db.js +10 -10
- package/lib/transform/draft/odata.js +2 -2
- package/lib/transform/forOdataNew.js +12 -40
- package/lib/transform/forRelationalDB.js +17 -7
- package/lib/transform/localized.js +2 -2
- package/lib/transform/odata/toFinalBaseType.js +41 -27
- package/lib/transform/odata/typesExposure.js +106 -62
- package/lib/transform/parseExpr.js +209 -106
- package/lib/transform/transformUtilsNew.js +2 -2
- 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 -7
- 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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
const { setProp } = require('../../base/model');
|
|
10
10
|
const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
|
|
11
|
-
const { cloneCsnNonDict, isBuiltinType, forEachDefinition, forEachMember } = require('../../model/csnUtils');
|
|
11
|
+
const { cloneCsnNonDict, isBuiltinType, forEachDefinition, forEachMember, forEachGeneric } = require('../../model/csnUtils');
|
|
12
12
|
const { copyAnnotations } = require('../../model/csnUtils');
|
|
13
13
|
const { isBetaEnabled } = require('../../base/model.js');
|
|
14
14
|
|
|
@@ -49,6 +49,31 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
49
49
|
}
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
if(isBetaEnabled(options, 'odataTerms')) {
|
|
53
|
+
forEachGeneric(csn, 'vocabularies', (def, defName, _propertyName, path) => {
|
|
54
|
+
// we do expose types only for definition from inside services
|
|
55
|
+
const serviceName = whatsMyServiceName(defName, false);
|
|
56
|
+
// run type exposure only on requested services if not in multi schema mode
|
|
57
|
+
// multi schema mode requires a proper type exposure for all services as a prerequisite
|
|
58
|
+
// for the proxy exposure
|
|
59
|
+
if (serviceName && requestedServiceNames.includes(serviceName)) {
|
|
60
|
+
if(csn.definitions[defName]) {
|
|
61
|
+
// error, duplicate definitions not allowed!
|
|
62
|
+
// TODO: Use path as error location as soon as refs outside definitions are supported
|
|
63
|
+
error('odata-definition-exists', ['vocabularies', defName], { anno: defName, '#':'anno' });
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// link def into definitions for later use
|
|
67
|
+
def.kind = 'annotation';
|
|
68
|
+
csn.definitions[defName] = def;
|
|
69
|
+
const artificialName = `term_${defName.replace(/\./g, '_')}`;//_${paramName}`;
|
|
70
|
+
const newTypeName = getNewTypeName(undefined, undefined, artificialName, serviceName);
|
|
71
|
+
exposeTypeOf(def, false, defName, defName, serviceName, newTypeName, path.concat(['vocabularies', defName]), undefined, true);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
52
77
|
return schemas;
|
|
53
78
|
|
|
54
79
|
/**
|
|
@@ -82,14 +107,14 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
82
107
|
* @param {String} serviceName
|
|
83
108
|
* @param {String} newTypeName
|
|
84
109
|
*/
|
|
85
|
-
function exposeTypeOf(node, isKey, memberName, defName, serviceName, newTypeName, path, parentName) {
|
|
86
|
-
const { isExposable, typeDef, typeName, elements, isAnonymous } = isTypeExposable(
|
|
110
|
+
function exposeTypeOf(node, isKey, memberName, defName, serviceName, newTypeName, path, parentName, isTermDef=false) {
|
|
111
|
+
const { isExposable, typeDef, typeName, elements, isAnonymous } = isTypeExposable();
|
|
87
112
|
if (isExposable) {
|
|
88
113
|
// this is the name used to register the new type in csn.definitions
|
|
89
114
|
let fullQualifiedNewTypeName =
|
|
90
115
|
isMultiSchema
|
|
91
|
-
? (node.type || (node.items
|
|
92
|
-
? getTypeNameInMultiSchema(node.type|| (node.items
|
|
116
|
+
? (node.type || (node.items?.type)
|
|
117
|
+
? getTypeNameInMultiSchema(node.type|| (node.items?.type), serviceName)
|
|
93
118
|
: getAnonymousTypeNameInMultiSchema(newTypeName, parentName || defName))
|
|
94
119
|
: `${serviceName}.${newTypeName}`;
|
|
95
120
|
|
|
@@ -100,7 +125,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
100
125
|
isKey = false;
|
|
101
126
|
// in case this was a named type and if the openess does not match the type definition
|
|
102
127
|
// expose the type as a new one not changing the original definition.
|
|
103
|
-
if((!!node['@open'] !== !!typeDef['@open']) && isBetaEnabled(options, 'odataOpenType'))
|
|
128
|
+
if(elements && (!!node['@open'] !== !!typeDef['@open']) && isBetaEnabled(options, 'odataOpenType'))
|
|
104
129
|
fullQualifiedNewTypeName += node['@open'] ? '_open' : '_closed';
|
|
105
130
|
}
|
|
106
131
|
// check if that type is already defined
|
|
@@ -118,51 +143,62 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
118
143
|
/* Expose new structured type
|
|
119
144
|
* Treat items.elements as ordinary elements for now.
|
|
120
145
|
*/
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
146
|
+
if(elements) {
|
|
147
|
+
newType = createNewStructType(elements);
|
|
148
|
+
// if using node enforces open/closed, set it on type
|
|
149
|
+
if(node['@open'] !== undefined)
|
|
150
|
+
newType['@open'] = node['@open']
|
|
151
|
+
if (node.$location)
|
|
152
|
+
setProp(newType, '$location', node.$location);
|
|
127
153
|
|
|
128
|
-
|
|
129
|
-
|
|
154
|
+
csn.definitions[fullQualifiedNewTypeName] = newType;
|
|
155
|
+
exposedTypes[fullQualifiedNewTypeName] = 1;
|
|
130
156
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
157
|
+
// Recurse into elements of 'type' (if any) and expose them as well (is needed)
|
|
158
|
+
newType.elements && Object.entries(newType.elements).forEach(([elemName, newElem]) => {
|
|
159
|
+
if (node.elements && node.elements[elemName].$location)
|
|
160
|
+
setProp(newElem, '$location', node.elements[elemName].$location);
|
|
161
|
+
defName = typeDef.kind === 'type' ? typeName : defName;
|
|
162
|
+
{
|
|
163
|
+
const { isExposable, typeDef, typeName } = exposeTypeOf(newElem, isKey, elemName, defName, serviceName,
|
|
164
|
+
getNewTypeName(newElem, elemName, newTypeName, serviceName), path, fullQualifiedNewTypeName, isTermDef);
|
|
165
|
+
// if the type for the newElem was not exposed it may be a scalar type def from an external service that hasn't
|
|
166
|
+
// been catched by expandToFinalBaseType() (forODataNew must not modify external imported services)
|
|
167
|
+
if(!isExposable && isBuiltinType(typeName) && !isBuiltinType((newElem.items?.type || newElem.type))) {
|
|
168
|
+
if(typeDef.items) {
|
|
169
|
+
newElem.items = typeDef.items;
|
|
170
|
+
delete newElem.type;
|
|
171
|
+
}
|
|
172
|
+
else if(newElem.items) {
|
|
173
|
+
newElem.items.type = typeName;
|
|
174
|
+
if(typeDef.enum)
|
|
175
|
+
newElem.items.enum = typeDef.enum;
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
newElem.type = typeName;
|
|
179
|
+
if(typeDef.enum)
|
|
180
|
+
newElem.enum = typeDef.enum;
|
|
181
|
+
}
|
|
155
182
|
}
|
|
156
183
|
}
|
|
184
|
+
});
|
|
185
|
+
copyAnnotations(typeDef, newType);
|
|
186
|
+
// if the origin type had items, add items to exposed type
|
|
187
|
+
if(typeDef.kind === 'type') {
|
|
188
|
+
if(typeDef.items) {
|
|
189
|
+
newType.items = { elements: newType.elements };
|
|
190
|
+
delete newType.elements;
|
|
191
|
+
}
|
|
157
192
|
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
newType.items = { elements: newType.elements };
|
|
164
|
-
delete newType.elements;
|
|
193
|
+
}
|
|
194
|
+
else if(isTermDef) {
|
|
195
|
+
newType = Object.create(null);
|
|
196
|
+
for(let n in typeDef) {
|
|
197
|
+
newType[n] = typeDef[n];
|
|
165
198
|
}
|
|
199
|
+
newType.kind = 'type';
|
|
200
|
+
csn.definitions[fullQualifiedNewTypeName] = newType;
|
|
201
|
+
exposedTypes[fullQualifiedNewTypeName] = 1;
|
|
166
202
|
}
|
|
167
203
|
}
|
|
168
204
|
// adjust current node to new type
|
|
@@ -188,36 +224,44 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
188
224
|
* - the elements dictionary that needs to be cloned
|
|
189
225
|
* - the typeDef, either the resolved type def or the node itself
|
|
190
226
|
* - if structured type was anonymously defined
|
|
191
|
-
* @param {object} node
|
|
192
227
|
* @returns {object} { isExposable, typeDef, typeName, elements, isAnonymous }
|
|
193
228
|
*/
|
|
194
|
-
function isTypeExposable(
|
|
229
|
+
function isTypeExposable() {
|
|
195
230
|
let typeName = undefined;
|
|
196
231
|
let typeDef = node;
|
|
197
|
-
let elements = (node.items
|
|
232
|
+
let elements = (node.items?.elements || node.elements)
|
|
198
233
|
// anonymous structured type
|
|
199
234
|
if(elements)
|
|
200
235
|
return { isExposable: true, typeDef, typeName, elements, isAnonymous: true };
|
|
201
236
|
// named type, resolve the type to inspect it
|
|
202
|
-
let type = node.items
|
|
237
|
+
let type = node.items?.type || node.type;
|
|
203
238
|
if(type) {
|
|
204
239
|
typeName = (type.ref && csnUtils.getFinalType(type)) || type;
|
|
205
|
-
typeDef = csnUtils.getFinalTypeDef(typeName);
|
|
206
240
|
const rc = { isExposable: true, typeDef, typeName, isAnonymous: false };
|
|
207
|
-
if(!isBuiltinType(typeName)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
241
|
+
if(!isBuiltinType(typeName)) {
|
|
242
|
+
rc.typeDef = typeDef = csnUtils.getFinalTypeDef(typeName);
|
|
243
|
+
if(!isArtifactInService(typeName, serviceName)) {
|
|
244
|
+
while(!isBuiltinType(typeName)) {
|
|
245
|
+
typeDef = csnUtils.getFinalTypeDef(typeName);
|
|
246
|
+
if(typeDef) {
|
|
247
|
+
if((isTermDef && typeDef.enum) || (rc.elements = (typeDef.items?.elements || typeDef.elements)) !== undefined)
|
|
248
|
+
return rc;
|
|
249
|
+
type = typeDef.items?.type || typeDef.type;
|
|
250
|
+
typeName = (type.ref && csnUtils.getFinalType(type)) || type;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
throw Error(`Please debug me: ${typeName} not found`);
|
|
254
|
+
}
|
|
218
255
|
}
|
|
219
256
|
}
|
|
257
|
+
else {
|
|
258
|
+
rc.isExposable = false;
|
|
259
|
+
return rc;
|
|
260
|
+
}
|
|
220
261
|
}
|
|
262
|
+
// else if(isTermDef && typeDef.enum) {
|
|
263
|
+
// return rc;
|
|
264
|
+
// }
|
|
221
265
|
}
|
|
222
266
|
return { isExposable: false, typeDef, typeName, isAnonymous: false };
|
|
223
267
|
}
|
|
@@ -308,7 +352,7 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
|
|
|
308
352
|
*/
|
|
309
353
|
function getNewTypeName(element, elementName, typeNamePrefix, serviceName) {
|
|
310
354
|
// for the new type name node.type has precedence over node.items.type
|
|
311
|
-
const typeName = (!element
|
|
355
|
+
const typeName = (!element?.elements && element?.type || !element?.items?.elements && element?.items?.type);
|
|
312
356
|
return typeName
|
|
313
357
|
? `${isMultiSchema
|
|
314
358
|
? typeName.split('.').pop() // use leaf element
|
|
@@ -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')
|