@sap/cds-compiler 3.9.4 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +92 -4
- package/README.md +0 -1
- package/bin/cdsc.js +11 -23
- package/bin/cdsse.js +3 -3
- package/doc/API.md +5 -0
- package/doc/CHANGELOG_ARCHIVE.md +1 -1
- package/doc/CHANGELOG_BETA.md +17 -1
- package/doc/CHANGELOG_DEPRECATED.md +28 -0
- package/lib/api/.eslintrc.json +1 -1
- package/lib/api/main.js +26 -8
- package/lib/api/options.js +2 -0
- package/lib/base/error.js +2 -0
- package/lib/base/message-registry.js +143 -64
- package/lib/base/messages.js +213 -107
- package/lib/base/model.js +11 -11
- package/lib/checks/.eslintrc.json +1 -1
- package/lib/checks/annotationsOData.js +2 -2
- package/lib/checks/elements.js +1 -1
- package/lib/checks/enricher.js +26 -3
- package/lib/checks/onConditions.js +67 -12
- package/lib/checks/queryNoDbArtifacts.js +106 -105
- package/lib/checks/sql-snippets.js +2 -0
- package/lib/checks/types.js +12 -6
- package/lib/checks/validator.js +2 -2
- package/lib/compiler/assert-consistency.js +10 -8
- package/lib/compiler/builtins.js +8 -2
- package/lib/compiler/checks.js +52 -35
- package/lib/compiler/define.js +31 -26
- package/lib/compiler/extend.js +120 -65
- package/lib/compiler/finalize-parse-cdl.js +12 -43
- package/lib/compiler/generate.js +16 -5
- package/lib/compiler/index.js +8 -5
- package/lib/compiler/kick-start.js +4 -3
- package/lib/compiler/populate.js +96 -95
- package/lib/compiler/propagator.js +7 -8
- package/lib/compiler/resolve.js +377 -103
- package/lib/compiler/shared.js +794 -517
- package/lib/compiler/tweak-assocs.js +8 -6
- package/lib/compiler/utils.js +44 -0
- package/lib/edm/annotations/genericTranslation.js +12 -4
- package/lib/edm/csn2edm.js +34 -32
- package/lib/edm/edm.js +34 -31
- package/lib/edm/edmAnnoPreprocessor.js +0 -23
- package/lib/edm/edmInboundChecks.js +7 -2
- package/lib/edm/edmPreprocessor.js +18 -17
- package/lib/edm/edmUtils.js +8 -4
- package/lib/gen/Dictionary.json +18 -0
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +4 -2
- package/lib/gen/languageParser.js +5006 -4582
- package/lib/json/from-csn.js +157 -112
- package/lib/json/to-csn.js +60 -89
- package/lib/language/antlrParser.js +17 -13
- package/lib/language/docCommentParser.js +11 -1
- package/lib/language/genericAntlrParser.js +13 -10
- package/lib/language/language.g4 +168 -97
- package/lib/main.d.ts +128 -36
- package/lib/main.js +1 -1
- package/lib/model/csnRefs.js +24 -5
- package/lib/model/csnUtils.js +9 -8
- package/lib/model/revealInternalProperties.js +7 -12
- package/lib/modelCompare/compare.js +1 -1
- package/lib/modelCompare/utils/filter.js +40 -2
- package/lib/optionProcessor.js +0 -3
- package/lib/render/toCdl.js +247 -214
- package/lib/render/toHdbcds.js +197 -181
- package/lib/render/toSql.js +325 -289
- package/lib/render/utils/common.js +42 -4
- package/lib/render/utils/delta.js +1 -1
- package/lib/render/utils/sql.js +3 -3
- package/lib/transform/braceExpression.js +2 -2
- package/lib/transform/db/.eslintrc.json +1 -1
- package/lib/transform/db/applyTransformations.js +3 -3
- package/lib/transform/db/associations.js +24 -12
- package/lib/transform/db/expansion.js +17 -18
- package/lib/transform/db/flattening.js +17 -21
- package/lib/transform/db/rewriteCalculatedElements.js +171 -64
- package/lib/transform/db/views.js +3 -4
- package/lib/transform/draft/db.js +21 -12
- package/lib/transform/draft/odata.js +4 -0
- package/lib/transform/forOdataNew.js +11 -10
- package/lib/transform/forRelationalDB.js +12 -7
- package/lib/transform/localized.js +4 -2
- package/lib/transform/odata/toFinalBaseType.js +5 -5
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/parseExpr.js +3 -0
- package/lib/transform/transformUtilsNew.js +43 -23
- package/lib/transform/translateAssocsToJoins.js +7 -6
- package/lib/transform/universalCsn/.eslintrc.json +1 -1
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -12
- package/package.json +2 -2
- package/share/messages/{duplicate-autoexposed.md → def-duplicate-autoexposed.md} +5 -1
- package/share/messages/message-explanations.json +1 -1
package/lib/render/toCdl.js
CHANGED
|
@@ -92,6 +92,8 @@ function csnToCdl( csn, options ) {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
timetrace.stop('CDL rendering');
|
|
95
|
+
|
|
96
|
+
msg.throwWithError();
|
|
95
97
|
return cdlResult;
|
|
96
98
|
|
|
97
99
|
/**
|
|
@@ -104,7 +106,7 @@ function csnToCdl( csn, options ) {
|
|
|
104
106
|
let result = '';
|
|
105
107
|
const env = createEnv();
|
|
106
108
|
forEachDefinition(csn, (artifact, artifactName) => {
|
|
107
|
-
const sourceStr =
|
|
109
|
+
const sourceStr = renderDefinition(artifactName, artifact, env);
|
|
108
110
|
if (sourceStr !== '')
|
|
109
111
|
result += `${sourceStr}\n`;
|
|
110
112
|
});
|
|
@@ -127,7 +129,7 @@ function csnToCdl( csn, options ) {
|
|
|
127
129
|
if (!anno._ignore) {
|
|
128
130
|
// This environment is passed down the call hierarchy, for dealing with
|
|
129
131
|
// indentation and name resolution issues
|
|
130
|
-
const env =
|
|
132
|
+
const env = createEnv({ path: [ 'vocabularies', name ] });
|
|
131
133
|
const sourceStr = renderTypeOrAnnotation(name, anno, env, 'annotation');
|
|
132
134
|
result += `${sourceStr}\n`;
|
|
133
135
|
}
|
|
@@ -145,8 +147,8 @@ function csnToCdl( csn, options ) {
|
|
|
145
147
|
*/
|
|
146
148
|
function renderExtensions( extensions, env ) {
|
|
147
149
|
if (!env.path)
|
|
148
|
-
env =
|
|
149
|
-
return extensions.map((ext, index) => renderExtension(ext,
|
|
150
|
+
env = env.cloneWith({ path: [ 'extensions' ] });
|
|
151
|
+
return extensions.map((ext, index) => renderExtension(ext, env.withSubPath([ index ]))).join('\n');
|
|
150
152
|
}
|
|
151
153
|
|
|
152
154
|
/**
|
|
@@ -181,7 +183,7 @@ function csnToCdl( csn, options ) {
|
|
|
181
183
|
if (ext.includes && ext.includes.length > 0) {
|
|
182
184
|
// Includes can't be combined with anything in braces {}.
|
|
183
185
|
const affix = isElementExtend ? 'element ' : '';
|
|
184
|
-
const includes = ext.includes.map(inc => renderDefinitionReference(inc, env)).join(', ');
|
|
186
|
+
const includes = ext.includes.map((inc, i) => renderDefinitionReference(inc, env.withSubPath([ 'includes', i ]))).join(', ');
|
|
185
187
|
result += `${env.indent}extend ${affix}${extName} with ${includes};\n`;
|
|
186
188
|
return result;
|
|
187
189
|
}
|
|
@@ -210,18 +212,17 @@ function csnToCdl( csn, options ) {
|
|
|
210
212
|
result += `${env.indent}extend ${extName} with ${getExtendPostfixVariant(ext)}{\n`;
|
|
211
213
|
|
|
212
214
|
if (ext.columns)
|
|
213
|
-
result += renderViewColumns(ext
|
|
215
|
+
result += renderViewColumns(ext, env.withIncreasedIndent());
|
|
214
216
|
|
|
215
217
|
else if (ext.elements || ext.enum)
|
|
216
218
|
result += renderExtendStatementElements(ext, env);
|
|
217
219
|
|
|
218
|
-
|
|
219
220
|
// Not part of if/else cascade, because it may be in postfix notation.
|
|
220
221
|
if (ext.actions) {
|
|
221
|
-
const childEnv =
|
|
222
|
+
const childEnv = env.withIncreasedIndent();
|
|
222
223
|
let actions = '';
|
|
223
224
|
forEach(ext.actions, (actionName, action) => {
|
|
224
|
-
actions += renderActionOrFunction(actionName, action, childEnv);
|
|
225
|
+
actions += renderActionOrFunction(actionName, action, childEnv.withSubPath([ 'actions', actionName ]));
|
|
225
226
|
});
|
|
226
227
|
if (!usePrefixNotation)
|
|
227
228
|
result += actions;
|
|
@@ -281,13 +282,15 @@ function csnToCdl( csn, options ) {
|
|
|
281
282
|
*/
|
|
282
283
|
function renderExtendStatementElements( ext, env ) {
|
|
283
284
|
let result = '';
|
|
284
|
-
|
|
285
|
+
const prop = ext.elements ? 'elements' : 'enum';
|
|
286
|
+
forEach(ext[prop] || {}, (elemName, element) => {
|
|
287
|
+
const childEnv = env.withIncreasedIndent().withSubPath([ 'elements', elemName ]);
|
|
285
288
|
if (element.kind === 'extend')
|
|
286
|
-
result += renderExtendStatement(elemName, element,
|
|
289
|
+
result += renderExtendStatement(elemName, element, childEnv);
|
|
287
290
|
else
|
|
288
291
|
// As soon as we are inside an element, nested `extend` are not possible,
|
|
289
292
|
// since we can't extend an existing element of a new one.
|
|
290
|
-
result += renderElement(elemName, element,
|
|
293
|
+
result += renderElement(elemName, element, childEnv.withSubPath([ prop, elemName ]));
|
|
291
294
|
});
|
|
292
295
|
return result;
|
|
293
296
|
}
|
|
@@ -307,42 +310,36 @@ function csnToCdl( csn, options ) {
|
|
|
307
310
|
result += `${env.indent}annotate ${renderArtifactName(ext.annotate, env)}`;
|
|
308
311
|
|
|
309
312
|
if (ext.params)
|
|
310
|
-
result += renderAnnotateParamsInParentheses(ext
|
|
313
|
+
result += renderAnnotateParamsInParentheses(ext, env);
|
|
311
314
|
|
|
312
315
|
// Element extensions and annotations (possibly nested)
|
|
316
|
+
// TODO: Deduplicate coding, see renderAnnotateStatementElements()
|
|
313
317
|
if (ext.elements)
|
|
314
|
-
result += renderAnnotateStatementElements(ext.elements, env)
|
|
315
|
-
if (ext.enum)
|
|
316
|
-
result += renderAnnotateStatementElements(ext.enum, env)
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
if (ext.returns) {
|
|
320
|
-
const childEnv = increaseIndent(env);
|
|
321
|
-
result += ` returns${renderAnnotateStatementElements(ext.returns.elements, childEnv)}`;
|
|
322
|
-
}
|
|
318
|
+
result += ` ${renderAnnotateStatementElements(ext.elements, env.withSubPath([ 'elements' ]))}`;
|
|
319
|
+
else if (ext.enum)
|
|
320
|
+
result += ` ${renderAnnotateStatementElements(ext.enum, env.withSubPath([ 'enum' ]))}`;
|
|
321
|
+
else if (ext.returns)
|
|
322
|
+
result += renderAnnotateReturns(ext, env);
|
|
323
323
|
|
|
324
|
-
//
|
|
325
|
-
if (ext.actions) {
|
|
324
|
+
if (ext.actions) { // Bound action annotations
|
|
326
325
|
result += ' actions {\n';
|
|
327
|
-
const childEnv = increaseIndent(env);
|
|
328
326
|
for (const name in ext.actions) {
|
|
327
|
+
const childEnv = env.withIncreasedIndent().withSubPath([ 'actions', name ]);
|
|
329
328
|
const action = ext.actions[name];
|
|
330
|
-
result += renderAnnotationAssignmentsAndDocComment(action, childEnv) + childEnv.indent + quoteNonIdentifierOrKeyword(name,
|
|
329
|
+
result += renderAnnotationAssignmentsAndDocComment(action, childEnv) + childEnv.indent + quoteNonIdentifierOrKeyword(name, childEnv);
|
|
331
330
|
// Action parameter annotations
|
|
332
331
|
if (action.params)
|
|
333
|
-
result += renderAnnotateParamsInParentheses(action
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
if (action.returns && action.returns.elements) {
|
|
337
|
-
const grandChildEnv = increaseIndent(childEnv);
|
|
338
|
-
result += ` returns${renderAnnotateStatementElements(action.returns.elements, grandChildEnv)}`;
|
|
339
|
-
}
|
|
332
|
+
result += renderAnnotateParamsInParentheses(action, childEnv);
|
|
333
|
+
if (action.returns)
|
|
334
|
+
result += renderAnnotateReturns(action, childEnv);
|
|
340
335
|
|
|
336
|
+
result = removeTrailingNewline(result);
|
|
341
337
|
result += ';\n';
|
|
342
338
|
}
|
|
343
339
|
result += `${env.indent}}`;
|
|
344
340
|
}
|
|
345
341
|
|
|
342
|
+
result = removeTrailingNewline(result);
|
|
346
343
|
result += ';\n';
|
|
347
344
|
return result;
|
|
348
345
|
}
|
|
@@ -357,16 +354,16 @@ function csnToCdl( csn, options ) {
|
|
|
357
354
|
* @return {string}
|
|
358
355
|
*/
|
|
359
356
|
function renderAnnotateStatementElements( elements, env ) {
|
|
360
|
-
let result = '
|
|
361
|
-
const childEnv = increaseIndent(env);
|
|
357
|
+
let result = '{\n';
|
|
362
358
|
for (const name in elements) {
|
|
359
|
+
const childEnv = env.withIncreasedIndent().withSubPath([ name ]);
|
|
363
360
|
const elem = elements[name];
|
|
364
361
|
result += renderAnnotationAssignmentsAndDocComment(elem, childEnv);
|
|
365
362
|
result += childEnv.indent + quoteNonIdentifierOrKeyword(name, env);
|
|
366
363
|
if (elem.elements)
|
|
367
|
-
result += renderAnnotateStatementElements(elem.elements, childEnv)
|
|
368
|
-
if (elem.enum)
|
|
369
|
-
result += renderAnnotateStatementElements(elem.enum, childEnv)
|
|
364
|
+
result += ` ${renderAnnotateStatementElements(elem.elements, childEnv.withSubPath([ 'elements' ]))}`;
|
|
365
|
+
else if (elem.enum)
|
|
366
|
+
result += ` ${renderAnnotateStatementElements(elem.enum, childEnv.withSubPath([ 'enum' ]))}`;
|
|
370
367
|
|
|
371
368
|
result += ';\n';
|
|
372
369
|
}
|
|
@@ -374,19 +371,45 @@ function csnToCdl( csn, options ) {
|
|
|
374
371
|
return result;
|
|
375
372
|
}
|
|
376
373
|
|
|
374
|
+
/**
|
|
375
|
+
* Renders the `returns` part of an `annotate` statement for (bound) actions.
|
|
376
|
+
* `ext` must be an object with a `returns` property.
|
|
377
|
+
*
|
|
378
|
+
* @param {CSN.Extension} ext
|
|
379
|
+
* @param {CdlRenderEnvironment} env
|
|
380
|
+
* @return {string}
|
|
381
|
+
*/
|
|
382
|
+
function renderAnnotateReturns( ext, env ) {
|
|
383
|
+
env = env.withSubPath([ 'returns', 'elements' ]);
|
|
384
|
+
let result = ' returns';
|
|
385
|
+
|
|
386
|
+
const returnAnnos = renderAnnotationAssignmentsAndDocComment(ext.returns, env.withIncreasedIndent());
|
|
387
|
+
if (returnAnnos)
|
|
388
|
+
result += `\n${returnAnnos}`;
|
|
389
|
+
|
|
390
|
+
if (ext.returns.elements) {
|
|
391
|
+
// Annotations are on separate lines: Have it aligned nicely
|
|
392
|
+
result += returnAnnos ? `${env.indent}` : ' ';
|
|
393
|
+
result += renderAnnotateStatementElements(ext.returns.elements, env);
|
|
394
|
+
}
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
|
|
377
398
|
/**
|
|
378
399
|
* Render a parameter list for `annotate` statements, in parentheses `()`.
|
|
379
400
|
*
|
|
380
|
-
* @param {
|
|
401
|
+
* @param {CSN.Artifact} art
|
|
381
402
|
* @param {CdlRenderEnvironment} env
|
|
382
403
|
* @return {string}
|
|
383
404
|
*/
|
|
384
|
-
function renderAnnotateParamsInParentheses(
|
|
385
|
-
const childEnv =
|
|
405
|
+
function renderAnnotateParamsInParentheses( art, env ) {
|
|
406
|
+
const childEnv = env.withIncreasedIndent();
|
|
386
407
|
let result = '(\n';
|
|
387
408
|
const paramAnnotations = [];
|
|
388
|
-
forEach(params, (paramName, param) => {
|
|
389
|
-
|
|
409
|
+
forEach(art.params, (paramName, param) => {
|
|
410
|
+
const annos = renderAnnotationAssignmentsAndDocComment(param, childEnv);
|
|
411
|
+
const name = quoteNonIdentifierOrKeyword(paramName, env.withSubPath([ 'params', paramName ]));
|
|
412
|
+
paramAnnotations.push( annos + childEnv.indent + name );
|
|
390
413
|
});
|
|
391
414
|
result += `${paramAnnotations.join(',\n')}\n${env.indent})`;
|
|
392
415
|
return result;
|
|
@@ -399,11 +422,11 @@ function csnToCdl( csn, options ) {
|
|
|
399
422
|
* @param {CSN.Artifact} art
|
|
400
423
|
* @param {CdlRenderEnvironment} env
|
|
401
424
|
*/
|
|
402
|
-
function
|
|
403
|
-
env =
|
|
404
|
-
env.artifactName = artifactName;
|
|
425
|
+
function renderDefinition( artifactName, art, env ) {
|
|
426
|
+
env = env.cloneWith({ path: [ 'definitions', artifactName ] });
|
|
405
427
|
|
|
406
|
-
|
|
428
|
+
const kind = art.kind || 'type'; // the default kind is "type".
|
|
429
|
+
switch (kind) {
|
|
407
430
|
case 'entity':
|
|
408
431
|
if (art.query || art.projection)
|
|
409
432
|
return renderView(artifactName, art, env);
|
|
@@ -427,6 +450,7 @@ function csnToCdl( csn, options ) {
|
|
|
427
450
|
return renderEvent(artifactName, art, env);
|
|
428
451
|
|
|
429
452
|
default:
|
|
453
|
+
// TODO: Make it a error message.
|
|
430
454
|
throw new ModelError(`to.cdl: Unknown artifact kind: ${art.kind}`);
|
|
431
455
|
}
|
|
432
456
|
}
|
|
@@ -444,8 +468,9 @@ function csnToCdl( csn, options ) {
|
|
|
444
468
|
result += renderIncludes(art.includes, env);
|
|
445
469
|
if (art.query || art.projection) {
|
|
446
470
|
result += ' : ';
|
|
447
|
-
|
|
448
|
-
|
|
471
|
+
// events (should) only support "projections"
|
|
472
|
+
result += renderQuery(getNormalizedQuery(art).query, true, 'projection',
|
|
473
|
+
env.withSubPath([ art.projection ? 'projection' : 'query' ]));
|
|
449
474
|
result += ';\n';
|
|
450
475
|
}
|
|
451
476
|
else if (art.type) {
|
|
@@ -527,13 +552,11 @@ function csnToCdl( csn, options ) {
|
|
|
527
552
|
*/
|
|
528
553
|
function renderElements( artifact, env ) {
|
|
529
554
|
let elements = '';
|
|
530
|
-
const childEnv =
|
|
555
|
+
const childEnv = env.withIncreasedIndent();
|
|
531
556
|
for (const name in artifact.elements)
|
|
532
|
-
elements += renderElement(name, artifact.elements[name], childEnv);
|
|
557
|
+
elements += renderElement(name, artifact.elements[name], childEnv.withSubPath([ 'elements', name ]));
|
|
533
558
|
|
|
534
|
-
|
|
535
|
-
return '{ }';
|
|
536
|
-
return `{\n${elements}${env.indent}}`;
|
|
559
|
+
return (elements === '') ? '{ }' : `{\n${elements}${env.indent}}`;
|
|
537
560
|
}
|
|
538
561
|
|
|
539
562
|
/**
|
|
@@ -546,7 +569,6 @@ function csnToCdl( csn, options ) {
|
|
|
546
569
|
* @param {CdlRenderEnvironment} env
|
|
547
570
|
*/
|
|
548
571
|
function renderElement( elementName, element, env ) {
|
|
549
|
-
env = envAddPath(env, [ 'elements', elementName ]);
|
|
550
572
|
let result = renderAnnotationAssignmentsAndDocComment(element, env);
|
|
551
573
|
result += env.indent;
|
|
552
574
|
result += element.virtual ? 'virtual ' : '';
|
|
@@ -569,9 +591,9 @@ function csnToCdl( csn, options ) {
|
|
|
569
591
|
if (element.value !== undefined) { // calculated element // @ts-ignore
|
|
570
592
|
result += ' = ';
|
|
571
593
|
if (element.value.xpr && xprContainsCondition(element.value.xpr))
|
|
572
|
-
result += exprRenderer.renderSubExpr(element.value, env);
|
|
594
|
+
result += exprRenderer.renderSubExpr(element.value, env.withSubPath([ 'value' ]));
|
|
573
595
|
else
|
|
574
|
-
result += exprRenderer.renderExpr(element.value, env);
|
|
596
|
+
result += exprRenderer.renderExpr(element.value, env.withSubPath([ 'value' ]));
|
|
575
597
|
if (element.value.stored === true)
|
|
576
598
|
result += ' stored';
|
|
577
599
|
}
|
|
@@ -589,13 +611,12 @@ function csnToCdl( csn, options ) {
|
|
|
589
611
|
* however, in client CSN, annotations are not part of the column and in parseCdl CSN,
|
|
590
612
|
* no `elements` exist.
|
|
591
613
|
*
|
|
592
|
-
* @param {string} artifactName
|
|
593
614
|
* @param {CSN.Artifact} art
|
|
594
615
|
* @param {CdlRenderEnvironment} env
|
|
595
616
|
* @return {string}
|
|
596
617
|
*/
|
|
597
|
-
function renderQueryElementAndEnumAnnotations(
|
|
598
|
-
const annotate = collectAnnotationsOfElementsAndEnum(art,
|
|
618
|
+
function renderQueryElementAndEnumAnnotations( art, env ) {
|
|
619
|
+
const annotate = collectAnnotationsOfElementsAndEnum(art, env);
|
|
599
620
|
if (annotate)
|
|
600
621
|
return renderExtensions([ annotate ], env);
|
|
601
622
|
return '';
|
|
@@ -612,7 +633,7 @@ function csnToCdl( csn, options ) {
|
|
|
612
633
|
function collectAnnotationsOfElementsAndEnum( artifact, env ) {
|
|
613
634
|
// Array, which may be annotated as well.
|
|
614
635
|
if (artifact.items) {
|
|
615
|
-
env =
|
|
636
|
+
env = env.withSubPath([ 'items' ]);
|
|
616
637
|
artifact = artifact.items;
|
|
617
638
|
}
|
|
618
639
|
|
|
@@ -699,7 +720,7 @@ function csnToCdl( csn, options ) {
|
|
|
699
720
|
function renderViewSource( source, env ) {
|
|
700
721
|
// Sub-SELECT
|
|
701
722
|
if (source.SELECT || source.SET) {
|
|
702
|
-
const subEnv =
|
|
723
|
+
const subEnv = env.withIncreasedIndent();
|
|
703
724
|
let result = `(\n${subEnv.indent}${renderQuery(source, false, 'view', subEnv)}\n${env.indent})`;
|
|
704
725
|
if (source.as)
|
|
705
726
|
result += renderAlias(source.as, env);
|
|
@@ -709,13 +730,13 @@ function csnToCdl( csn, options ) {
|
|
|
709
730
|
// JOIN
|
|
710
731
|
else if (source.join) {
|
|
711
732
|
// One join operation, possibly with ON-condition
|
|
712
|
-
let result = `(${renderViewSource(source.args[0], env)}`;
|
|
733
|
+
let result = `(${renderViewSource(source.args[0], env.withSubPath([ 'args', 0 ]))}`;
|
|
713
734
|
for (let i = 1; i < source.args.length; i++) {
|
|
714
735
|
result += ` ${source.join} `;
|
|
715
736
|
result += renderJoinCardinality(source.cardinality);
|
|
716
|
-
result += `join ${renderViewSource(source.args[i], env)}`;
|
|
737
|
+
result += `join ${renderViewSource(source.args[i], env.withSubPath([ 'args', i ]))}`;
|
|
717
738
|
if (source.on)
|
|
718
|
-
result += ` on ${exprRenderer.renderExpr(source.on, env)}`;
|
|
739
|
+
result += ` on ${exprRenderer.renderExpr(source.on, env.withSubPath([ 'on' ]))}`;
|
|
719
740
|
}
|
|
720
741
|
result += ')';
|
|
721
742
|
return result;
|
|
@@ -762,12 +783,12 @@ function csnToCdl( csn, options ) {
|
|
|
762
783
|
|
|
763
784
|
// Even the first step might have parameters and/or a filter
|
|
764
785
|
if (path.ref[0].args)
|
|
765
|
-
result += `(${renderArguments(path.ref[0], ':', env)})`;
|
|
786
|
+
result += `(${renderArguments(path.ref[0], ':', env.withSubPath([ 'ref', 0 ]))})`;
|
|
766
787
|
|
|
767
788
|
if (path.ref[0].where) {
|
|
768
789
|
// TODO: Unify with other filter rendering
|
|
769
790
|
const cardinality = path.ref[0].cardinality ? (`${path.ref[0].cardinality.max}: `) : '';
|
|
770
|
-
const expr = exprRenderer.renderExpr(path.ref[0].where, env);
|
|
791
|
+
const expr = exprRenderer.renderExpr(path.ref[0].where, env.withSubPath([ 'ref', 0, 'where' ]));
|
|
771
792
|
if (expr.endsWith(']')) // for cases such as [… ![id] ]
|
|
772
793
|
result += `[ ${cardinality}${expr} ]`;
|
|
773
794
|
else
|
|
@@ -804,14 +825,13 @@ function csnToCdl( csn, options ) {
|
|
|
804
825
|
/**
|
|
805
826
|
* Render the given columns.
|
|
806
827
|
*
|
|
807
|
-
* @param {
|
|
828
|
+
* @param {CSN.Artifact} art
|
|
808
829
|
* @param {object} elements
|
|
809
830
|
* @param {CdlRenderEnvironment} env
|
|
810
831
|
* @return {string}
|
|
811
832
|
*/
|
|
812
|
-
function renderViewColumns(
|
|
813
|
-
const result = columns.map(col => renderViewColumn(col, env, findElement(elements, col)))
|
|
814
|
-
.filter(s => s !== '')
|
|
833
|
+
function renderViewColumns( art, env, elements = Object.create(null) ) {
|
|
834
|
+
const result = art.columns.map((col, i) => renderViewColumn(col, env.withSubPath([ 'columns', i ]), findElement(elements, col)))
|
|
815
835
|
.join(',\n');
|
|
816
836
|
return `${result}\n`;
|
|
817
837
|
}
|
|
@@ -859,9 +879,9 @@ function csnToCdl( csn, options ) {
|
|
|
859
879
|
if (col.cast) {
|
|
860
880
|
// Special case: Explicit association type is actually a redirect
|
|
861
881
|
if (col.cast.target && !col.cast.type)
|
|
862
|
-
result += ` : ${renderRedirectedTo(col.cast, env)}`;
|
|
882
|
+
result += ` : ${renderRedirectedTo(col.cast, env.withSubPath([ 'cast' ]))}`;
|
|
863
883
|
else
|
|
864
|
-
result += ` : ${renderTypeReferenceAndProps(col.cast, env, { typeRefOnly: true, noAnnoCollect: true })}`;
|
|
884
|
+
result += ` : ${renderTypeReferenceAndProps(col.cast, env.withSubPath([ 'cast' ]), { typeRefOnly: true, noAnnoCollect: true })}`;
|
|
865
885
|
}
|
|
866
886
|
return result;
|
|
867
887
|
}
|
|
@@ -885,9 +905,9 @@ function csnToCdl( csn, options ) {
|
|
|
885
905
|
// We found a leaf - no further drilling
|
|
886
906
|
if (!obj.inline && !obj.expand) {
|
|
887
907
|
if (obj.cast && obj.cast.type)
|
|
888
|
-
result += ` : ${renderTypeReferenceAndProps(obj.cast,
|
|
908
|
+
result += ` : ${renderTypeReferenceAndProps(obj.cast, env.withSubPath([ 'cast' ]), { noAnnoCollect: true })}`;
|
|
889
909
|
else if (obj.cast && obj.cast.target) // test tbd
|
|
890
|
-
result += ` : ${renderRedirectedTo(obj.cast, env)}`;
|
|
910
|
+
result += ` : ${renderRedirectedTo(obj.cast, env.withSubPath([ 'cast' ]))}`;
|
|
891
911
|
return result;
|
|
892
912
|
}
|
|
893
913
|
|
|
@@ -897,7 +917,7 @@ function csnToCdl( csn, options ) {
|
|
|
897
917
|
result += result !== '' ? ' {\n' : '{\n';
|
|
898
918
|
|
|
899
919
|
// Drill down and render children of the expand/inline
|
|
900
|
-
const childEnv =
|
|
920
|
+
const childEnv = env.withIncreasedIndent();
|
|
901
921
|
const expandInline = obj.expand || obj.inline;
|
|
902
922
|
result += expandInline //
|
|
903
923
|
.map(elm => renderAnnotationAssignmentsAndDocComment(elm, childEnv) + childEnv.indent + renderInlineExpand(elm, childEnv))
|
|
@@ -929,8 +949,8 @@ function csnToCdl( csn, options ) {
|
|
|
929
949
|
return `\n${env.indent}/** */\n`;
|
|
930
950
|
|
|
931
951
|
let { doc } = obj;
|
|
932
|
-
if (/[
|
|
933
|
-
doc = doc.replace(/
|
|
952
|
+
if (/[*]\//.test(doc)) // only escape sequence allowed in CDL for doc comments
|
|
953
|
+
doc = doc.replace(/[*]\//g, '*\\/');
|
|
934
954
|
|
|
935
955
|
// Smaller comment for single-line comments. If the comment starts or ends with whitespace
|
|
936
956
|
// we must use a block comment, or it will be lost when compiling the source again.
|
|
@@ -953,11 +973,11 @@ function csnToCdl( csn, options ) {
|
|
|
953
973
|
if (art.params)
|
|
954
974
|
result += renderParameters(art, env);
|
|
955
975
|
result += ' as ';
|
|
956
|
-
result += renderQuery(getNormalizedQuery(art).query, true, syntax, env
|
|
976
|
+
result += renderQuery(getNormalizedQuery(art).query, true, syntax, env.withSubPath([ art.projection ? 'projection' : 'query' ]), art.elements);
|
|
957
977
|
if (art.actions) // Views/Projections also allow actions. Just the VIEW keyword variant did not.
|
|
958
978
|
result += renderActionsAndFunctions(art, env);
|
|
959
979
|
result += ';\n';
|
|
960
|
-
result += renderQueryElementAndEnumAnnotations(
|
|
980
|
+
result += renderQueryElementAndEnumAnnotations(art, env);
|
|
961
981
|
if (art.includes)
|
|
962
982
|
result += renderExtension({ extend: artifactName, includes: art.includes }, env);
|
|
963
983
|
return result;
|
|
@@ -973,10 +993,9 @@ function csnToCdl( csn, options ) {
|
|
|
973
993
|
* @param {boolean} isLeadingQuery
|
|
974
994
|
* @param {string} syntax The query syntax, either "projection", "entity" or "view"
|
|
975
995
|
* @param {CdlRenderEnvironment} env
|
|
976
|
-
* @param {CSN.Path} [path=[]]
|
|
977
996
|
* @param {object} [elements]
|
|
978
997
|
*/
|
|
979
|
-
function renderQuery( query, isLeadingQuery, syntax, env,
|
|
998
|
+
function renderQuery( query, isLeadingQuery, syntax, env, elements = query.elements || Object.create(null) ) {
|
|
980
999
|
if (query.SET) {
|
|
981
1000
|
// Set operator, such as UNION, INTERSECT, or EXCEPT...
|
|
982
1001
|
return renderQuerySet();
|
|
@@ -988,16 +1007,16 @@ function csnToCdl( csn, options ) {
|
|
|
988
1007
|
|
|
989
1008
|
let result = '';
|
|
990
1009
|
const select = query.SELECT;
|
|
991
|
-
const childEnv =
|
|
1010
|
+
const childEnv = env.withIncreasedIndent();
|
|
992
1011
|
|
|
993
1012
|
// If not a projection, must be view/entity.
|
|
994
1013
|
result += (syntax === 'projection') ? 'projection on ' : 'select from ';
|
|
995
|
-
result += renderViewSource(select.from, env);
|
|
1014
|
+
result += renderViewSource(select.from, env.withSubPath([ 'from' ]));
|
|
996
1015
|
|
|
997
1016
|
if (select.mixin) {
|
|
998
1017
|
let elems = '';
|
|
999
1018
|
forEach(select.mixin, (name, mixin) => {
|
|
1000
|
-
elems += renderElement(name, mixin, childEnv);
|
|
1019
|
+
elems += renderElement(name, mixin, childEnv.withSubPath([ 'mixin', name ]));
|
|
1001
1020
|
});
|
|
1002
1021
|
if (elems) {
|
|
1003
1022
|
result += ' mixin {\n';
|
|
@@ -1008,7 +1027,7 @@ function csnToCdl( csn, options ) {
|
|
|
1008
1027
|
result += select.distinct ? ' distinct' : '';
|
|
1009
1028
|
if (select.columns) {
|
|
1010
1029
|
result += ' {\n';
|
|
1011
|
-
result += renderViewColumns(select
|
|
1030
|
+
result += renderViewColumns(select, env.withIncreasedIndent(), elements);
|
|
1012
1031
|
result += `${env.indent}}`;
|
|
1013
1032
|
}
|
|
1014
1033
|
|
|
@@ -1022,19 +1041,19 @@ function csnToCdl( csn, options ) {
|
|
|
1022
1041
|
result += renderActionsAndFunctions(query, env);
|
|
1023
1042
|
|
|
1024
1043
|
if (select.where)
|
|
1025
|
-
result += `${continueIndent(result, env)}where ${exprRenderer.renderExpr(select.where, env)}`;
|
|
1044
|
+
result += `${continueIndent(result, env)}where ${exprRenderer.renderExpr(select.where, env.withSubPath([ 'where' ]))}`;
|
|
1026
1045
|
|
|
1027
1046
|
if (select.groupBy)
|
|
1028
|
-
result += `${continueIndent(result, env)}group by ${select.groupBy.map(expr => exprRenderer.renderExpr(expr, env)).join(', ')}`;
|
|
1047
|
+
result += `${continueIndent(result, env)}group by ${select.groupBy.map((expr, i) => exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ')}`;
|
|
1029
1048
|
|
|
1030
1049
|
if (select.having)
|
|
1031
|
-
result += `${continueIndent(result, env)}having ${exprRenderer.renderExpr(select.having, env)}`;
|
|
1050
|
+
result += `${continueIndent(result, env)}having ${exprRenderer.renderExpr(select.having, env.withSubPath([ 'having' ]))}`;
|
|
1032
1051
|
|
|
1033
1052
|
if (select.orderBy)
|
|
1034
|
-
result += `${continueIndent(result, env)}order by ${select.orderBy.map(entry => renderOrderByEntry(entry, env)).join(', ')}`;
|
|
1053
|
+
result += `${continueIndent(result, env)}order by ${select.orderBy.map((entry, i) => renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ')}`;
|
|
1035
1054
|
|
|
1036
1055
|
if (select.limit)
|
|
1037
|
-
result += `${continueIndent(result, env)}${renderLimit(select.limit, env)}`;
|
|
1056
|
+
result += `${continueIndent(result, env)}${renderLimit(select.limit, env.withSubPath([ 'limit' ]))}`;
|
|
1038
1057
|
|
|
1039
1058
|
return result;
|
|
1040
1059
|
|
|
@@ -1051,26 +1070,7 @@ function csnToCdl( csn, options ) {
|
|
|
1051
1070
|
return ' ';
|
|
1052
1071
|
}
|
|
1053
1072
|
// Otherwise, start new line and indent normally
|
|
1054
|
-
return `\n${
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
/**
|
|
1058
|
-
* Render a query's LIMIT clause, which may also have OFFSET.
|
|
1059
|
-
*
|
|
1060
|
-
* @param {CSN.QueryLimit} limit
|
|
1061
|
-
* @param {CdlRenderEnvironment} limitEnv
|
|
1062
|
-
* @return {string}
|
|
1063
|
-
*/
|
|
1064
|
-
function renderLimit( limit, limitEnv ) {
|
|
1065
|
-
let limitStr = '';
|
|
1066
|
-
if (limit.rows !== undefined)
|
|
1067
|
-
limitStr += `limit ${exprRenderer.renderExpr(limit.rows, limitEnv)}`;
|
|
1068
|
-
|
|
1069
|
-
if (limit.offset !== undefined) {
|
|
1070
|
-
const offsetIndent = (limitStr === '') ? '' : `\n${increaseIndent(limitEnv).indent}`;
|
|
1071
|
-
limitStr += `${offsetIndent}offset ${exprRenderer.renderExpr(limit.offset, limitEnv)}`;
|
|
1072
|
-
}
|
|
1073
|
-
return limitStr;
|
|
1073
|
+
return `\n${indentEnv.withIncreasedIndent().indent}`;
|
|
1074
1074
|
}
|
|
1075
1075
|
|
|
1076
1076
|
/**
|
|
@@ -1081,7 +1081,8 @@ function csnToCdl( csn, options ) {
|
|
|
1081
1081
|
function renderQuerySet() {
|
|
1082
1082
|
const subQueries = query.SET.args.map((arg, i) => {
|
|
1083
1083
|
// First arg may be leading query
|
|
1084
|
-
const
|
|
1084
|
+
const subEnv = env.withSubPath([ 'SET', 'args', i ]);
|
|
1085
|
+
const subQuery = renderQuery(arg, isLeadingQuery && (i === 0), 'view', subEnv, elements);
|
|
1085
1086
|
return `(${subQuery})`;
|
|
1086
1087
|
});
|
|
1087
1088
|
|
|
@@ -1089,10 +1090,10 @@ function csnToCdl( csn, options ) {
|
|
|
1089
1090
|
// Set operation may also have an ORDER BY and LIMIT/OFFSET (in contrast to the ones belonging to
|
|
1090
1091
|
// each SELECT)
|
|
1091
1092
|
if (query.SET.orderBy)
|
|
1092
|
-
setResult += `${continueIndent(setResult, env)}order by ${query.SET.orderBy.map(entry => renderOrderByEntry(entry, env)).join(', ')}`;
|
|
1093
|
+
setResult += `${continueIndent(setResult, env)}order by ${query.SET.orderBy.map((entry, i) => renderOrderByEntry(entry, env.withSubPath([ 'SET', 'orderBy', i ]))).join(', ')}`;
|
|
1093
1094
|
|
|
1094
1095
|
if (query.SET.limit)
|
|
1095
|
-
setResult += `${continueIndent(setResult, env)}${renderLimit(query.SET.limit, env)}`;
|
|
1096
|
+
setResult += `${continueIndent(setResult, env)}${renderLimit(query.SET.limit, env.withSubPath([ 'SET', 'limit' ]))}`;
|
|
1096
1097
|
return setResult;
|
|
1097
1098
|
}
|
|
1098
1099
|
}
|
|
@@ -1116,6 +1117,25 @@ function csnToCdl( csn, options ) {
|
|
|
1116
1117
|
return result;
|
|
1117
1118
|
}
|
|
1118
1119
|
|
|
1120
|
+
/**
|
|
1121
|
+
* Render a query's LIMIT clause, which may also have OFFSET.
|
|
1122
|
+
*
|
|
1123
|
+
* @param {CSN.QueryLimit} limit
|
|
1124
|
+
* @param {CdlRenderEnvironment} limitEnv
|
|
1125
|
+
* @return {string}
|
|
1126
|
+
*/
|
|
1127
|
+
function renderLimit( limit, limitEnv ) {
|
|
1128
|
+
let limitStr = '';
|
|
1129
|
+
if (limit.rows !== undefined)
|
|
1130
|
+
limitStr += `limit ${exprRenderer.renderExpr(limit.rows, limitEnv.withSubPath([ 'rows' ]))}`;
|
|
1131
|
+
|
|
1132
|
+
if (limit.offset !== undefined) {
|
|
1133
|
+
const offsetIndent = (limitStr === '') ? '' : `\n${limitEnv.withIncreasedIndent().indent}`;
|
|
1134
|
+
limitStr += `${offsetIndent}offset ${exprRenderer.renderExpr(limit.offset, limitEnv.withSubPath([ 'offset' ]))}`;
|
|
1135
|
+
}
|
|
1136
|
+
return limitStr;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1119
1139
|
/**
|
|
1120
1140
|
* Render an entity's actions and functions (if any)
|
|
1121
1141
|
* (expect an entity with trailing '}' or an 'extend' statement ending with 'with'
|
|
@@ -1128,9 +1148,9 @@ function csnToCdl( csn, options ) {
|
|
|
1128
1148
|
*/
|
|
1129
1149
|
function renderActionsAndFunctions( art, env ) {
|
|
1130
1150
|
let result = '';
|
|
1131
|
-
const childEnv =
|
|
1151
|
+
const childEnv = env.withIncreasedIndent();
|
|
1132
1152
|
for (const name in art.actions)
|
|
1133
|
-
result += renderActionOrFunction(name, art.actions[name],
|
|
1153
|
+
result += renderActionOrFunction(name, art.actions[name], childEnv.withSubPath([ 'actions', name ]));
|
|
1134
1154
|
|
|
1135
1155
|
// Even if we have seen actions/functions, they might all have been ignored
|
|
1136
1156
|
if (result !== '')
|
|
@@ -1152,8 +1172,12 @@ function csnToCdl( csn, options ) {
|
|
|
1152
1172
|
result += ` ${renderArtifactName(actionName, env)}`;
|
|
1153
1173
|
result += renderParameters(act, env);
|
|
1154
1174
|
if (act.returns) {
|
|
1155
|
-
|
|
1156
|
-
|
|
1175
|
+
let actEnv = env.withSubPath([ 'returns' ]);
|
|
1176
|
+
const annos = renderAnnotationAssignmentsAndDocComment(act.returns, actEnv.withIncreasedIndent());
|
|
1177
|
+
if (annos) // if `returns` has annotations, increase indent for nicer aligned output
|
|
1178
|
+
actEnv = actEnv.withIncreasedIndent();
|
|
1179
|
+
const type = renderTypeReferenceAndProps(act.returns, actEnv);
|
|
1180
|
+
result += ` returns${annos ? '\n' : ' '}${annos}${annos ? actEnv.indent : ''}${type}`;
|
|
1157
1181
|
}
|
|
1158
1182
|
|
|
1159
1183
|
result += ';\n';
|
|
@@ -1170,8 +1194,8 @@ function csnToCdl( csn, options ) {
|
|
|
1170
1194
|
* @returns {string}
|
|
1171
1195
|
*/
|
|
1172
1196
|
function renderParameters( art, env ) {
|
|
1173
|
-
const childEnv =
|
|
1174
|
-
const parameters = Object.keys(art.params || {}).map(name => renderParameter(name, art.params[name], childEnv));
|
|
1197
|
+
const childEnv = env.withIncreasedIndent();
|
|
1198
|
+
const parameters = Object.keys(art.params || {}).map(name => renderParameter(name, art.params[name], childEnv.withSubPath([ 'params', name ])));
|
|
1175
1199
|
if (parameters.length === 0)
|
|
1176
1200
|
return '()';
|
|
1177
1201
|
return `(\n${parameters.join(',\n')}\n${env.indent})`;
|
|
@@ -1186,7 +1210,7 @@ function csnToCdl( csn, options ) {
|
|
|
1186
1210
|
* @return {string}
|
|
1187
1211
|
*/
|
|
1188
1212
|
function renderParameter( parName, par, env ) {
|
|
1189
|
-
env =
|
|
1213
|
+
env = env.withSubPath( [ 'params', parName ]);
|
|
1190
1214
|
let result = `${renderAnnotationAssignmentsAndDocComment(par, env)}${env.indent}`;
|
|
1191
1215
|
result += `${quoteNonIdentifierOrKeyword(parName, env)} : ${renderTypeReferenceAndProps(par, env)}`;
|
|
1192
1216
|
return result;
|
|
@@ -1199,7 +1223,7 @@ function csnToCdl( csn, options ) {
|
|
|
1199
1223
|
* @param {string} artifactName
|
|
1200
1224
|
* @param {CSN.Artifact} art
|
|
1201
1225
|
* @param {CdlRenderEnvironment} env
|
|
1202
|
-
* @param {String} [artType]
|
|
1226
|
+
* @param {String} [artType] Used for rendering `csn.vocabularies`, as the annotations there do not have a kind.
|
|
1203
1227
|
* @return {string}
|
|
1204
1228
|
*/
|
|
1205
1229
|
function renderTypeOrAnnotation( artifactName, art, env, artType ) {
|
|
@@ -1232,7 +1256,7 @@ function csnToCdl( csn, options ) {
|
|
|
1232
1256
|
const { typeRefOnly, noAnnoCollect } = config;
|
|
1233
1257
|
|
|
1234
1258
|
if (typeRefOnly && !artifact.type)
|
|
1235
|
-
throw new ModelError(`Expected artifact to have a type; in: ${env.
|
|
1259
|
+
throw new ModelError(`Expected artifact to have a type; in: ${JSON.stringify(env.path)}`);
|
|
1236
1260
|
|
|
1237
1261
|
if (artifact.localized) // works even for type definitions
|
|
1238
1262
|
result += 'localized ';
|
|
@@ -1240,7 +1264,8 @@ function csnToCdl( csn, options ) {
|
|
|
1240
1264
|
if (!artifact.type && artifact.items) {
|
|
1241
1265
|
checkArrayedArtifact(artifact, env);
|
|
1242
1266
|
result += 'many '; // alternative: 'array of'; but not used
|
|
1243
|
-
|
|
1267
|
+
artifact = artifact.items;
|
|
1268
|
+
env = env.withSubPath([ 'items' ]);
|
|
1244
1269
|
}
|
|
1245
1270
|
|
|
1246
1271
|
const type = normalizeTypeRef(artifact.type);
|
|
@@ -1263,13 +1288,13 @@ function csnToCdl( csn, options ) {
|
|
|
1263
1288
|
// In parseCdl, `target` may still be an object containing elements. This would be replaced
|
|
1264
1289
|
// by targetAspect in client CSN, but we can't rely on that.
|
|
1265
1290
|
// If a name exists (either in target or targetAspect), prefer it over rendering elements.
|
|
1266
|
-
const elements = artifact.target
|
|
1291
|
+
const elements = artifact.target?.elements || artifact.targetAspect?.elements;
|
|
1267
1292
|
if (typeof artifact.target === 'string' || typeof artifact.targetAspect === 'string') {
|
|
1268
1293
|
result += renderAbsolutePath({ ref: [ artifact.target || artifact.targetAspect ] }, env);
|
|
1269
1294
|
}
|
|
1270
1295
|
else if (elements) {
|
|
1271
1296
|
// anonymous aspect, either parseCdl or client CSN.
|
|
1272
|
-
result += renderElements({ elements }, env);
|
|
1297
|
+
result += renderElements({ elements }, env.withSubPath([ artifact.target?.elements ? 'target' : 'targetAspect' ]));
|
|
1273
1298
|
}
|
|
1274
1299
|
else {
|
|
1275
1300
|
throw new ModelError('Association/Composition is missing its target! Throwing exception to trigger recompilation.');
|
|
@@ -1277,11 +1302,11 @@ function csnToCdl( csn, options ) {
|
|
|
1277
1302
|
|
|
1278
1303
|
// ON-condition (if any)
|
|
1279
1304
|
if (artifact.on)
|
|
1280
|
-
result += ` on ${exprRenderer.renderExpr(artifact.on, env)}`;
|
|
1305
|
+
result += ` on ${exprRenderer.renderExpr(artifact.on, env.withSubPath([ 'on' ]))}`;
|
|
1281
1306
|
|
|
1282
1307
|
// Foreign keys (if any, unless we also have an ON_condition (which means we have been transformed from managed to unmanaged)
|
|
1283
1308
|
if (artifact.keys && !artifact.on)
|
|
1284
|
-
result += ` { ${Object.keys(artifact.keys).map(name => renderForeignKey(artifact.keys[name], env)).join(', ')} }`;
|
|
1309
|
+
result += ` { ${Object.keys(artifact.keys).map(name => renderForeignKey(artifact.keys[name], env.withSubPath([ 'keys', name ]))).join(', ')} }`;
|
|
1285
1310
|
|
|
1286
1311
|
if (artifact.notNull !== undefined && !artifact.on) // unmanaged associations can't be followed by "not null"
|
|
1287
1312
|
result += renderNullability(artifact);
|
|
@@ -1315,8 +1340,11 @@ function csnToCdl( csn, options ) {
|
|
|
1315
1340
|
result += renderEnum(artifact.enum, env);
|
|
1316
1341
|
if (artifact.notNull !== undefined)
|
|
1317
1342
|
result += renderNullability(artifact);
|
|
1318
|
-
|
|
1319
|
-
|
|
1343
|
+
|
|
1344
|
+
// If there is a default value, and it's a calculated element, do not
|
|
1345
|
+
// render the default (because it's not supported for calc elements).
|
|
1346
|
+
if (artifact.default !== undefined && !artifact.value)
|
|
1347
|
+
result += ` default ${exprRenderer.renderExpr(artifact.default, env.withSubPath([ 'default' ]))}`;
|
|
1320
1348
|
|
|
1321
1349
|
return result;
|
|
1322
1350
|
}
|
|
@@ -1331,9 +1359,9 @@ function csnToCdl( csn, options ) {
|
|
|
1331
1359
|
function renderRedirectedTo( art, env ) {
|
|
1332
1360
|
let result = `redirected to ${renderDefinitionReference(art.target, env)}`;
|
|
1333
1361
|
if (art.on)
|
|
1334
|
-
result += ` on ${exprRenderer.renderExpr(art.on, env)}`;
|
|
1362
|
+
result += ` on ${exprRenderer.renderExpr(art.on, env.withSubPath([ 'on' ]))}`;
|
|
1335
1363
|
else if (art.keys)
|
|
1336
|
-
result += ` { ${Object.keys(art.keys).map(name => renderForeignKey(art.keys[name], env)).join(', ')} }`;
|
|
1364
|
+
result += ` { ${Object.keys(art.keys).map(name => renderForeignKey(art.keys[name], env.withSubPath([ 'keys', name ]))).join(', ')} }`;
|
|
1337
1365
|
return result;
|
|
1338
1366
|
}
|
|
1339
1367
|
|
|
@@ -1377,9 +1405,9 @@ function csnToCdl( csn, options ) {
|
|
|
1377
1405
|
*/
|
|
1378
1406
|
function renderEnum( enumPart, env ) {
|
|
1379
1407
|
let result = ' enum {\n';
|
|
1380
|
-
const childEnv =
|
|
1408
|
+
const childEnv = env.withIncreasedIndent();
|
|
1381
1409
|
for (const name in enumPart)
|
|
1382
|
-
result += renderElement(name, enumPart[name], childEnv);
|
|
1410
|
+
result += renderElement(name, enumPart[name], childEnv.withSubPath([ 'enum', name ]));
|
|
1383
1411
|
result += `${env.indent}}`;
|
|
1384
1412
|
return result;
|
|
1385
1413
|
}
|
|
@@ -1407,17 +1435,17 @@ function csnToCdl( csn, options ) {
|
|
|
1407
1435
|
}
|
|
1408
1436
|
else if (typeof annoValue === 'object' && annoValue !== null) {
|
|
1409
1437
|
// Enum symbol
|
|
1410
|
-
if (annoValue['#']) {
|
|
1438
|
+
if (annoValue['#'] !== undefined) {
|
|
1411
1439
|
return `#${annoValue['#']}`;
|
|
1412
1440
|
}
|
|
1413
1441
|
// Shorthand for absolute path (as string)
|
|
1414
|
-
else if (annoValue['=']) {
|
|
1442
|
+
else if (annoValue['='] !== undefined) {
|
|
1415
1443
|
if (annoValue['='].startsWith('@'))
|
|
1416
1444
|
return quoteAnnotationPathIfRequired(annoValue['='], env);
|
|
1417
1445
|
return quotePathIfRequired(annoValue['='], env);
|
|
1418
1446
|
}
|
|
1419
1447
|
// Shorthand for ellipsis: `... up to <val>`
|
|
1420
|
-
else if (annoValue['...']) {
|
|
1448
|
+
else if (annoValue['...'] !== undefined) {
|
|
1421
1449
|
if (annoValue['...'] === true)
|
|
1422
1450
|
return '...';
|
|
1423
1451
|
return `... up to ${renderAnnotationValue(annoValue['...'], env)}`;
|
|
@@ -1427,8 +1455,8 @@ function csnToCdl( csn, options ) {
|
|
|
1427
1455
|
// Render as one-liner if there is at most one key. Render as multi-line
|
|
1428
1456
|
// struct if there are more and use nicer indentation.
|
|
1429
1457
|
const keys = Object.keys(annoValue);
|
|
1430
|
-
const childEnv = keys.length <= 1 ? env :
|
|
1431
|
-
const values = keys.map(key => `${quoteAnnotationPathIfRequired(key, env)}: ${renderAnnotationValue(annoValue[key], childEnv)}`);
|
|
1458
|
+
const childEnv = keys.length <= 1 ? env : env.withIncreasedIndent();
|
|
1459
|
+
const values = keys.map(key => `${quoteAnnotationPathIfRequired(key, env)}: ${renderAnnotationValue(annoValue[key], childEnv.withSubPath([ key ]))}`);
|
|
1432
1460
|
if (values.length <= 1)
|
|
1433
1461
|
return `{ ${values.join(', ')} }`;
|
|
1434
1462
|
const valueList = values.join(`,\n${childEnv.indent}`);
|
|
@@ -1455,12 +1483,12 @@ function csnToCdl( csn, options ) {
|
|
|
1455
1483
|
* @return {string}
|
|
1456
1484
|
*/
|
|
1457
1485
|
function renderAnnotationArrayValue( annoValue, env ) {
|
|
1458
|
-
const childEnv =
|
|
1486
|
+
const childEnv = env.withIncreasedIndent();
|
|
1459
1487
|
// Render array parts as values.
|
|
1460
1488
|
let length = 0;
|
|
1461
1489
|
let hasLineBreak = false;
|
|
1462
|
-
const items = annoValue.map((item) => {
|
|
1463
|
-
const result = renderAnnotationValue(item, childEnv);
|
|
1490
|
+
const items = annoValue.map((item, i) => {
|
|
1491
|
+
const result = renderAnnotationValue(item, childEnv.withSubPath([ i ]));
|
|
1464
1492
|
length += result.length + 2; // just a heuristic; add 2 for `, `.
|
|
1465
1493
|
if (!hasLineBreak && result.includes('\n'))
|
|
1466
1494
|
hasLineBreak = true;
|
|
@@ -1508,14 +1536,35 @@ function csnToCdl( csn, options ) {
|
|
|
1508
1536
|
// View parameters
|
|
1509
1537
|
result += `(${renderArguments(s, ':', env)})`;
|
|
1510
1538
|
}
|
|
1539
|
+
|
|
1540
|
+
const cardinality = s.cardinality ? (`${s.cardinality.max}: `) : '';
|
|
1541
|
+
let filter = '';
|
|
1542
|
+
|
|
1543
|
+
// TODO: Unify with other filter rendering for SELECT
|
|
1544
|
+
if (s.groupBy)
|
|
1545
|
+
filter += ` group by ${s.groupBy.map((expr, i) => exprRenderer.renderExpr(expr, env.withSubPath([ 'groupBy', i ]))).join(', ')}`;
|
|
1546
|
+
if (s.having)
|
|
1547
|
+
filter += ` having ${exprRenderer.renderExpr(s.having, env.withSubPath([ 'having' ]))}`;
|
|
1548
|
+
if (s.orderBy)
|
|
1549
|
+
filter += ` order by ${s.orderBy.map((entry, i) => renderOrderByEntry(entry, env.withSubPath([ 'orderBy', i ]))).join(', ')}`;
|
|
1550
|
+
if (s.limit)
|
|
1551
|
+
filter += ` ${renderLimit(s.limit, env.withSubPath([ 'limit' ]))}`;
|
|
1552
|
+
|
|
1511
1553
|
if (s.where) {
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1554
|
+
let where = exprRenderer.renderExpr(s.where, env.withSubPath([ 'where' ]));
|
|
1555
|
+
// Special rules in CDS parser: If filter starts with one of these SQL keywords, WHERE is mandatory.
|
|
1556
|
+
if (filter || /^(?:group|having|order|limit)\s/i.test(where))
|
|
1557
|
+
where = ` where ${where}`;
|
|
1558
|
+
filter = `${where} ${filter}`;
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
filter = filter.trim();
|
|
1562
|
+
|
|
1563
|
+
if (cardinality || filter) {
|
|
1564
|
+
if (filter.endsWith(']')) // for cases such as [… ![id] ]
|
|
1565
|
+
result += `[ ${cardinality}${filter} ]`;
|
|
1517
1566
|
else
|
|
1518
|
-
result += `[${cardinality}${
|
|
1567
|
+
result += `[${cardinality}${filter}]`;
|
|
1519
1568
|
}
|
|
1520
1569
|
|
|
1521
1570
|
return result;
|
|
@@ -1554,7 +1603,7 @@ function csnToCdl( csn, options ) {
|
|
|
1554
1603
|
*/
|
|
1555
1604
|
function renderNamedArguments( node, separator, env ) {
|
|
1556
1605
|
return Object.keys(node.args).map(function renderNamedArgument(key) {
|
|
1557
|
-
return `${quoteNonIdentifierOrKeyword(key, env)} ${separator} ${renderArgument(node.args[key], env)}`;
|
|
1606
|
+
return `${quoteNonIdentifierOrKeyword(key, env)} ${separator} ${renderArgument(node.args[key], env.withSubPath([ 'args', key ]))}`;
|
|
1558
1607
|
}).join(', ');
|
|
1559
1608
|
}
|
|
1560
1609
|
|
|
@@ -1571,10 +1620,10 @@ function csnToCdl( csn, options ) {
|
|
|
1571
1620
|
const func = node.func?.toUpperCase();
|
|
1572
1621
|
if (func) {
|
|
1573
1622
|
return node.args.map(function renderFunctionArg(arg, i) {
|
|
1574
|
-
return renderArgument(arg, env, getKeywordsForSpecialFunctionArgument(func, i));
|
|
1623
|
+
return renderArgument(arg, env.withSubPath([ 'args', i ]), getKeywordsForSpecialFunctionArgument(func, i));
|
|
1575
1624
|
}).join(', ');
|
|
1576
1625
|
}
|
|
1577
|
-
return node.args.map(arg => renderArgument(arg, env)).join(', ');
|
|
1626
|
+
return node.args.map((arg, i) => renderArgument(arg, env.withSubPath([ 'args', i ]))).join(', ');
|
|
1578
1627
|
}
|
|
1579
1628
|
|
|
1580
1629
|
/**
|
|
@@ -1589,7 +1638,7 @@ function csnToCdl( csn, options ) {
|
|
|
1589
1638
|
function renderArgument( arg, env, additionalKeywords = [] ) {
|
|
1590
1639
|
// If the argument is a xpr with e.g. `=`, it may require parentheses.
|
|
1591
1640
|
// For nested xpr, `exprRenderer.renderExpr()` will already add parentheses.
|
|
1592
|
-
env = {
|
|
1641
|
+
env = env.cloneWith({ additionalKeywords });
|
|
1593
1642
|
if (isSimpleFunctionExpression(arg && arg.xpr, additionalKeywords))
|
|
1594
1643
|
return exprRenderer.renderExpr(arg, env);
|
|
1595
1644
|
return exprRenderer.renderSubExpr(arg, env);
|
|
@@ -1744,7 +1793,7 @@ function csnToCdl( csn, options ) {
|
|
|
1744
1793
|
let result = renderDocComment(obj, env);
|
|
1745
1794
|
for (const name in obj) {
|
|
1746
1795
|
if (name.startsWith('@'))
|
|
1747
|
-
result += renderAnnotationAssignment(obj[name], name, env, config);
|
|
1796
|
+
result += renderAnnotationAssignment(obj[name], name, env.withSubPath([ name ]), config);
|
|
1748
1797
|
}
|
|
1749
1798
|
return result;
|
|
1750
1799
|
}
|
|
@@ -1814,14 +1863,14 @@ function csnToCdl( csn, options ) {
|
|
|
1814
1863
|
* @return {string}
|
|
1815
1864
|
*/
|
|
1816
1865
|
function renderIncludes( includes, env ) {
|
|
1817
|
-
return ` : ${includes.map(name => renderDefinitionReference(name, env)).join(', ')}`;
|
|
1866
|
+
return ` : ${includes.map((name, i) => renderDefinitionReference(name, env.withSubPath([ 'includes', i ]))).join(', ')}`;
|
|
1818
1867
|
}
|
|
1819
1868
|
|
|
1820
1869
|
function createCdlExpressionRenderer() {
|
|
1821
1870
|
return createExpressionRenderer({
|
|
1822
1871
|
finalize: x => x,
|
|
1823
1872
|
typeCast(x) {
|
|
1824
|
-
const typeRef = renderTypeReferenceAndProps(x.cast, this.env, { typeRefOnly: true, noAnnoCollect: true });
|
|
1873
|
+
const typeRef = renderTypeReferenceAndProps(x.cast, this.env.withSubPath([ 'cast' ]), { typeRefOnly: true, noAnnoCollect: true });
|
|
1825
1874
|
const arg = { ...x, cast: null }; // "arg" without cast to avoid recursion.
|
|
1826
1875
|
return `cast(${renderArgument(arg, this.env)} as ${typeRef})`;
|
|
1827
1876
|
},
|
|
@@ -1850,11 +1899,11 @@ function csnToCdl( csn, options ) {
|
|
|
1850
1899
|
aliasOnly: x => x.as,
|
|
1851
1900
|
enum: x => `#${x['#']}`,
|
|
1852
1901
|
ref(x) {
|
|
1853
|
-
return `${(x.param || x.global) ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, this.env)).join('.')}`;
|
|
1902
|
+
return `${(x.param || x.global) ? ':' : ''}${x.ref.map((step, index) => renderPathStep(step, index, this.env.withSubPath([ 'ref', index ]))).join('.')}`;
|
|
1854
1903
|
},
|
|
1855
1904
|
windowFunction(x) {
|
|
1856
1905
|
const funcDef = this.func(x);
|
|
1857
|
-
return `${funcDef} ${this.renderExpr(x.xpr)}`; // xpr[0] is 'over'
|
|
1906
|
+
return `${funcDef} ${this.renderExpr(x.xpr, this.env.withSubPath([ 'xpr' ]))}`; // xpr[0] is 'over'
|
|
1858
1907
|
},
|
|
1859
1908
|
func(x) {
|
|
1860
1909
|
if (keywords.cdl_functions.includes(x.func.toUpperCase()) && !x.args)
|
|
@@ -1865,67 +1914,36 @@ function csnToCdl( csn, options ) {
|
|
|
1865
1914
|
return `${name}(${renderArguments( x, '=>', this.env )})`;
|
|
1866
1915
|
},
|
|
1867
1916
|
xpr(x) {
|
|
1917
|
+
const xprEnv = this.env.withSubPath([ 'xpr' ]);
|
|
1868
1918
|
if (this.isNestedXpr && !x.cast)
|
|
1869
|
-
return `(${this.renderExpr(x.xpr)})`;
|
|
1870
|
-
return this.renderExpr(x.xpr);
|
|
1919
|
+
return `(${this.renderExpr(x.xpr, xprEnv)})`;
|
|
1920
|
+
return this.renderExpr(x.xpr, xprEnv);
|
|
1871
1921
|
},
|
|
1872
1922
|
// Sub-queries in expressions need to be in parentheses, otherwise
|
|
1873
1923
|
// left-associativity of UNIONS may result in different results.
|
|
1874
1924
|
// For example: `select from E where id in (select from E union select from E);`:
|
|
1875
1925
|
// Without parentheses, it would be different query.
|
|
1876
1926
|
SET(x) {
|
|
1877
|
-
return `(${renderQuery(x, false, 'view',
|
|
1927
|
+
return `(${renderQuery(x, false, 'view', this.env.withIncreasedIndent())})`;
|
|
1878
1928
|
},
|
|
1879
1929
|
SELECT(x) {
|
|
1880
|
-
return `(${renderQuery(x, false, 'view',
|
|
1930
|
+
return `(${renderQuery(x, false, 'view', this.env.withIncreasedIndent())})`;
|
|
1881
1931
|
},
|
|
1882
|
-
});
|
|
1932
|
+
}, true);
|
|
1883
1933
|
}
|
|
1884
1934
|
|
|
1885
1935
|
// checks -------------------------------------------------------------------
|
|
1886
1936
|
// The CDL backend has very few checks, but we need to tell the user if
|
|
1887
1937
|
// something can't be rendered.
|
|
1888
1938
|
|
|
1889
|
-
/**
|
|
1890
|
-
* to.cdl() can only render one nesting level of `items`. `items` inside `items, etc.
|
|
1891
|
-
* can't be represented in CDL, hence can't be rendered.
|
|
1892
|
-
* However, it's possible that due to CSN expansion because of type-ofs, we have
|
|
1893
|
-
* nested `.items` with a `.type` next to it. In that case, return the node with `.type`.
|
|
1894
|
-
*
|
|
1895
|
-
* Returns the most deeply nested `.items`. Upper bound are 100 nesting levels.
|
|
1896
|
-
*
|
|
1897
|
-
* @param {CSN.Artifact} art
|
|
1898
|
-
* @param {CdlRenderEnvironment} env
|
|
1899
|
-
* @return {{art: CSN.Artifact, env: CdlRenderEnvironment}} `art` and new `env` with adapted `env.path`.
|
|
1900
|
-
*/
|
|
1901
|
-
function checkInnerMostArray( art, env ) {
|
|
1902
|
-
env = envNewPath(env, env.path); // copy path, so we can modify it directly
|
|
1903
|
-
|
|
1904
|
-
let nesting = 0;
|
|
1905
|
-
while (art.items && nesting < 100) {
|
|
1906
|
-
art = art.items;
|
|
1907
|
-
env.path.push( 'items');
|
|
1908
|
-
++nesting;
|
|
1909
|
-
if (art.type)
|
|
1910
|
-
break; // after first `.items`, break at nesting level that has a type.
|
|
1911
|
-
}
|
|
1912
|
-
|
|
1913
|
-
if (nesting >= 100) {
|
|
1914
|
-
msg.error('def-invalid-nesting', env.path, { count: nesting, prop: 'items' },
|
|
1915
|
-
'Property $(PROP) is nested more than $(COUNT) levels and can\'t be rendered');
|
|
1916
|
-
}
|
|
1917
|
-
else if (nesting > 1) {
|
|
1918
|
-
msg.warning('def-unexpected-nesting', env.path, { prop: 'items' },
|
|
1919
|
-
'Property $(PROP) is nested more than one level; only rendering deepest level');
|
|
1920
|
-
}
|
|
1921
|
-
return { art, env };
|
|
1922
|
-
}
|
|
1923
|
-
|
|
1924
1939
|
/**
|
|
1925
1940
|
* If an artifact is an array via `.items`, some properties on `art` can't be rendered,
|
|
1926
1941
|
* for example "not null", because there is no CDL representation for it. Only "not null"
|
|
1927
1942
|
* on `.items` can be rendered.
|
|
1928
1943
|
*
|
|
1944
|
+
* Furthermore, to.cdl() can only render one nesting level of `items`. `items` inside
|
|
1945
|
+
* `items, etc. can't be represented in CDL, hence can't be rendered.
|
|
1946
|
+
*
|
|
1929
1947
|
* @param {CSN.Artifact} art
|
|
1930
1948
|
* @param {CdlRenderEnvironment} env
|
|
1931
1949
|
*/
|
|
@@ -1936,6 +1954,16 @@ function csnToCdl( csn, options ) {
|
|
|
1936
1954
|
msg.warning('def-unexpected-nullability', env.path, { prop: 'not null', otherprop: 'items' },
|
|
1937
1955
|
'Property $(PROP) not rendered, because it can only be rendered inside $(OTHERPROP) for arrayed artifacts');
|
|
1938
1956
|
}
|
|
1957
|
+
|
|
1958
|
+
if (art.items.items && !art.items.type)
|
|
1959
|
+
msg.message('type-invalid-items', [ ...env.path, 'items' ], { '#': 'nested', prop: 'items' } );
|
|
1960
|
+
|
|
1961
|
+
const type = art.items.type && normalizeTypeRef(art.items.type);
|
|
1962
|
+
if (type === 'cds.Association' || type === 'cds.Composition') {
|
|
1963
|
+
// check for `art.items.target` not sufficient; could be indirect type reference
|
|
1964
|
+
const isComp = type === 'cds.Composition';
|
|
1965
|
+
msg.message('type-invalid-items', [ ...env.path, 'items' ], { '#': isComp ? 'comp' : 'assoc', prop: 'items' });
|
|
1966
|
+
}
|
|
1939
1967
|
}
|
|
1940
1968
|
|
|
1941
1969
|
/**
|
|
@@ -2049,19 +2077,28 @@ function csnToCdl( csn, options ) {
|
|
|
2049
2077
|
}
|
|
2050
2078
|
}
|
|
2051
2079
|
|
|
2052
|
-
|
|
2053
2080
|
class CdlRenderEnvironment {
|
|
2054
2081
|
indent = '';
|
|
2055
2082
|
path = null;
|
|
2056
|
-
artifactName = null;
|
|
2057
2083
|
elementName = null;
|
|
2058
2084
|
additionalKeywords = [];
|
|
2059
2085
|
|
|
2060
2086
|
constructor(values) {
|
|
2061
2087
|
Object.assign(this, values);
|
|
2062
2088
|
}
|
|
2089
|
+
|
|
2090
|
+
withIncreasedIndent() {
|
|
2091
|
+
return new CdlRenderEnvironment({ ...this, indent: ` ${this.indent}` });
|
|
2092
|
+
}
|
|
2093
|
+
withSubPath(path) {
|
|
2094
|
+
return new CdlRenderEnvironment({ ...this, path: [ ...this.path, ...path ] });
|
|
2095
|
+
}
|
|
2096
|
+
cloneWith(values) {
|
|
2097
|
+
return Object.assign(new CdlRenderEnvironment(this), values);
|
|
2098
|
+
}
|
|
2063
2099
|
}
|
|
2064
2100
|
|
|
2101
|
+
|
|
2065
2102
|
/**
|
|
2066
2103
|
* Returns a newly created default environment (which keeps track of indentation, required USING
|
|
2067
2104
|
* declarations and name prefixes.
|
|
@@ -2073,21 +2110,17 @@ function createEnv( values = {} ) {
|
|
|
2073
2110
|
return new CdlRenderEnvironment( values );
|
|
2074
2111
|
}
|
|
2075
2112
|
|
|
2076
|
-
function envAddPath( env, path ) {
|
|
2077
|
-
return Object.assign(new CdlRenderEnvironment(env), { path: [ ...env.path, ...path ] } );
|
|
2078
|
-
}
|
|
2079
|
-
function envNewPath( env, path ) {
|
|
2080
|
-
return Object.assign(new CdlRenderEnvironment(env), { path: [ ...path ] } );
|
|
2081
|
-
}
|
|
2082
|
-
|
|
2083
2113
|
/**
|
|
2084
|
-
*
|
|
2114
|
+
* Remove a trailing `\n`/newline/LF from `str` and return the modified string.
|
|
2115
|
+
* Useful if you want to append a `;` to the string, but not on a separate line.
|
|
2085
2116
|
*
|
|
2086
|
-
* @param {
|
|
2087
|
-
* @
|
|
2117
|
+
* @param {string} str
|
|
2118
|
+
* @return {string}
|
|
2088
2119
|
*/
|
|
2089
|
-
function
|
|
2090
|
-
|
|
2120
|
+
function removeTrailingNewline( str ) {
|
|
2121
|
+
if (str[str.length - 1] === '\n')
|
|
2122
|
+
str = str.substring(0, str.length - 1);
|
|
2123
|
+
return str;
|
|
2091
2124
|
}
|
|
2092
2125
|
|
|
2093
2126
|
/**
|
|
@@ -2152,7 +2185,7 @@ function isSimpleFunctionExpression( xpr, additionalAllowedKeywords = [] ) {
|
|
|
2152
2185
|
|
|
2153
2186
|
/**
|
|
2154
2187
|
* If `xpr` contains tokens that are used in conditions, it may be required to put the
|
|
2155
|
-
* rendered expression in parentheses. This function checks if any
|
|
2188
|
+
* rendered expression in parentheses. This function checks if any direct entry in
|
|
2156
2189
|
* `xpr` is a condition token such as `AND`.
|
|
2157
2190
|
*
|
|
2158
2191
|
* May report false positives for e.g. `CASE WHEN 1>1 THEN …`.
|
|
@@ -2219,8 +2252,8 @@ function getAllKeywordsForSpecialFunction( funcName ) {
|
|
|
2219
2252
|
* Render the given string. Uses back-tick strings.
|
|
2220
2253
|
* env is used for indentation of three-back-tick strings.
|
|
2221
2254
|
*
|
|
2222
|
-
* @param str
|
|
2223
|
-
* @param env
|
|
2255
|
+
* @param {string} str
|
|
2256
|
+
* @param {CdlRenderEnvironment} env
|
|
2224
2257
|
* @returns {string}
|
|
2225
2258
|
*/
|
|
2226
2259
|
function renderString( str, env ) {
|