@sap/cds-compiler 3.6.0 → 3.7.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 +3 -0
- package/bin/cdsc.js +9 -5
- package/doc/CHANGELOG_BETA.md +20 -2
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/lib/api/main.js +2 -1
- package/lib/api/options.js +3 -2
- package/lib/base/dictionaries.js +10 -0
- package/lib/base/message-registry.js +56 -12
- package/lib/base/messages.js +39 -20
- package/lib/base/model.js +1 -0
- package/lib/base/shuffle.js +2 -1
- package/lib/checks/elements.js +29 -1
- package/lib/checks/{emptyOrOnlyVirtual.js → hasPersistedElements.js} +9 -5
- package/lib/checks/nonexpandableStructured.js +1 -1
- package/lib/checks/onConditions.js +8 -5
- package/lib/checks/types.js +6 -1
- package/lib/checks/validator.js +7 -3
- package/lib/compiler/assert-consistency.js +20 -23
- package/lib/compiler/base.js +1 -2
- package/lib/compiler/builtins.js +2 -2
- package/lib/compiler/checks.js +237 -242
- package/lib/compiler/define.js +63 -75
- package/lib/compiler/extend.js +325 -22
- package/lib/compiler/finalize-parse-cdl.js +1 -55
- package/lib/compiler/kick-start.js +6 -7
- package/lib/compiler/populate.js +284 -288
- package/lib/compiler/propagator.js +15 -13
- package/lib/compiler/resolve.js +136 -306
- package/lib/compiler/shared.js +42 -44
- package/lib/compiler/tweak-assocs.js +29 -27
- package/lib/compiler/utils.js +29 -3
- package/lib/edm/annotations/genericTranslation.js +7 -13
- package/lib/edm/annotations/preprocessAnnotations.js +3 -0
- package/lib/edm/csn2edm.js +0 -4
- package/lib/edm/edm.js +6 -4
- package/lib/edm/edmAnnoPreprocessor.js +1 -0
- package/lib/edm/edmPreprocessor.js +1 -5
- package/lib/gen/Dictionary.json +34 -2
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +1 -1
- package/lib/gen/languageParser.js +2429 -2401
- package/lib/inspect/inspectPropagation.js +2 -0
- package/lib/json/from-csn.js +87 -41
- package/lib/json/to-csn.js +47 -16
- package/lib/language/errorStrategy.js +1 -0
- package/lib/language/genericAntlrParser.js +109 -28
- package/lib/language/language.g4 +20 -4
- package/lib/model/csnRefs.js +30 -2
- package/lib/model/csnUtils.js +1 -0
- package/lib/model/revealInternalProperties.js +1 -2
- package/lib/modelCompare/compare.js +2 -1
- package/lib/optionProcessor.js +3 -0
- package/lib/render/manageConstraints.js +5 -2
- package/lib/render/toCdl.js +20 -7
- package/lib/render/toHdbcds.js +2 -8
- package/lib/render/toSql.js +6 -5
- package/lib/render/utils/common.js +9 -5
- package/lib/transform/db/assertUnique.js +2 -1
- package/lib/transform/db/expansion.js +2 -0
- package/lib/transform/db/flattening.js +37 -36
- package/lib/transform/db/rewriteCalculatedElements.js +559 -0
- package/lib/transform/db/transformExists.js +15 -6
- package/lib/transform/db/views.js +40 -37
- package/lib/transform/forRelationalDB.js +44 -30
- package/lib/transform/odata/typesExposure.js +50 -15
- package/lib/transform/parseExpr.js +14 -8
- package/lib/transform/transformUtilsNew.js +6 -5
- package/lib/transform/translateAssocsToJoins.js +49 -33
- package/package.json +1 -1
package/lib/language/language.g4
CHANGED
|
@@ -1050,6 +1050,7 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
1050
1050
|
')' { this.finalizeDictOrArray( $art.params ); }
|
|
1051
1051
|
{ this.checkExtensionDict( $art.params ); }
|
|
1052
1052
|
(
|
|
1053
|
+
// TODO: set proper $art.returns
|
|
1053
1054
|
RETURNS { $art['$'+'syntax'] = 'returns'; }
|
|
1054
1055
|
'{' { $art.elements = this.createDict(); }
|
|
1055
1056
|
annotateElement[ $art ]*
|
|
@@ -1060,6 +1061,7 @@ annotateArtifact[ art, outer ] locals[ name = {}, elemName = {} ]
|
|
|
1060
1061
|
requiredSemi
|
|
1061
1062
|
)
|
|
1062
1063
|
|
|
|
1064
|
+
// TODO: set proper $art.returns
|
|
1063
1065
|
RETURNS { $art['$'+'syntax'] = 'returns'; }
|
|
1064
1066
|
'{' { $art.elements = this.createDict(); }
|
|
1065
1067
|
annotateElement[ $art ]*
|
|
@@ -1111,7 +1113,9 @@ annotateAction [ outer ] locals [ art = {} ]
|
|
|
1111
1113
|
{ this.checkExtensionDict( $art.params ); }
|
|
1112
1114
|
)?
|
|
1113
1115
|
(
|
|
1114
|
-
|
|
1116
|
+
// TODO: set proper $art.returns
|
|
1117
|
+
RETURNS { $art['$'+'syntax'] = 'returns'; }
|
|
1118
|
+
'{' { $art.elements = this.createDict(); }
|
|
1115
1119
|
annotateElement[ $art ]*
|
|
1116
1120
|
'}' { this.finalizeDictOrArray( $art.elements ); }
|
|
1117
1121
|
{ this.checkExtensionDict( $art.elements ); }
|
|
@@ -1511,6 +1515,7 @@ typeRefOptArgs[ art ]
|
|
|
1511
1515
|
;
|
|
1512
1516
|
|
|
1513
1517
|
typeRefArgs[ art ]
|
|
1518
|
+
@after { this.checkTypeArgs($art); }
|
|
1514
1519
|
:
|
|
1515
1520
|
paren='(' { $art['$'+'typeArgs'] = this.createArray(); }
|
|
1516
1521
|
(
|
|
@@ -2181,10 +2186,9 @@ expressionTerm returns [ expr ] locals [ args = [] ]
|
|
|
2181
2186
|
END { this.pushXprToken( $args ); }
|
|
2182
2187
|
|
|
|
2183
2188
|
ne=NEW { this.pushXprToken( $args ); } // token rewrite for NEW
|
|
2184
|
-
// please note: there will be no compiler-supported code completion after NEW
|
|
2185
|
-
{ $expr = { op: this.valueWithTokenLocation( 'new', $ne ), args: [] };
|
|
2186
|
-
this.error( 'syntax-unsupported-new', $ne, { keyword: $ne.text }, '$(KEYWORD) is not supported' ); }
|
|
2187
2189
|
nqp=valuePath[ 'ref', null ]
|
|
2190
|
+
{ $args.push( this.valuePathAst( $nqp.qp ) ); }
|
|
2191
|
+
{ this.fixNewKeywordPlacement( $args ); }
|
|
2188
2192
|
|
|
|
2189
2193
|
vp=valuePath[ 'ref', null ] { $args.push( this.valuePathAst( $vp.qp ) ); }
|
|
2190
2194
|
{ this.setLocalTokenIfBefore( 'OVER', 'OVER', /^\($/i ); }
|
|
@@ -2752,6 +2756,18 @@ annoValueBase[ assignment ] locals [ seenEllipsis = false ]
|
|
|
2752
2756
|
|
|
|
2753
2757
|
( plus='+' | min='-' ) num=Number
|
|
2754
2758
|
{ Object.assign( $assignment, this.numberLiteral( $num, $plus||$min ) ); }
|
|
2759
|
+
|
|
|
2760
|
+
'('
|
|
2761
|
+
cond=condition // 'condition' is also used in 'expression' inside '()'.
|
|
2762
|
+
// TODO: (1,2,3) not supported, yet, only ((1,2,3)); we could support it via:
|
|
2763
|
+
// (',' condition)*
|
|
2764
|
+
{ $assignment.value = $cond.cond;
|
|
2765
|
+
if (!this.isBetaEnabled(this.options, 'annotationExpressions')) {
|
|
2766
|
+
this.error( 'syntax-unsupported-expression', [ $cond.cond.location ], {},
|
|
2767
|
+
'Expressions in annotation values are not supported' );
|
|
2768
|
+
}
|
|
2769
|
+
}
|
|
2770
|
+
')'
|
|
2755
2771
|
;
|
|
2756
2772
|
|
|
2757
2773
|
flattenedValue[ assignment ] locals[ val = { name: {} } ]
|
package/lib/model/csnRefs.js
CHANGED
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
// const csnPath = ['definitions','P','projection','columns',0];
|
|
75
75
|
// const subElement = inspectRef( csnPath ); // type T is involved
|
|
76
76
|
// csn.definitions.T.type = 'some.other.type';
|
|
77
|
-
// // ({ inspectRef } = csnRefs( csn )); //
|
|
77
|
+
// // ({ inspectRef } = csnRefs( csn )); // drop caches
|
|
78
78
|
// … = inspectRef( csnPath ); // type T - using the cached or the new?
|
|
79
79
|
//
|
|
80
80
|
// On request, we might add a functions for individual cache invalidations or
|
|
@@ -155,6 +155,8 @@
|
|
|
155
155
|
// which contains cached data. Such data can be a link to a CSN node (like
|
|
156
156
|
// `_effectiveType`/`elements`), scalar (like `$queryNumber`) or link to
|
|
157
157
|
// another cache object (like `$next`).
|
|
158
|
+
// - A cache entry must not link to a cache object of another main definition;
|
|
159
|
+
// otherwise, individual cache invalidation does not work.
|
|
158
160
|
// - Usually, each CSN object has an individual cache object.
|
|
159
161
|
// - For CSN queries nodes, cache objects are _shared_: both the CSN nodes
|
|
160
162
|
// `‹query› = { SELECT: ‹select›, … }` and `‹select›` share the same cache
|
|
@@ -260,6 +262,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
260
262
|
getColumn: elem => getCache( elem, '_column' ),
|
|
261
263
|
getElement: col => getCache( col, '_element' ),
|
|
262
264
|
initDefinition,
|
|
265
|
+
dropDefinitionCache,
|
|
263
266
|
targetAspect,
|
|
264
267
|
__getCache_forEnrichCsnDebugging: obj => cache.get( obj ),
|
|
265
268
|
};
|
|
@@ -486,6 +489,31 @@ function csnRefs( csn, universalReady ) {
|
|
|
486
489
|
setCache( art, '$origin$step', (kind === 'element' ? step : { [kind]: step }) );
|
|
487
490
|
}
|
|
488
491
|
|
|
492
|
+
function dropDefinitionCache( main ) {
|
|
493
|
+
const queries = getCache( main, '$queries' );
|
|
494
|
+
if (!queries) // not yet initialized
|
|
495
|
+
return;
|
|
496
|
+
if (!cache.delete( main )) // not yet initialized
|
|
497
|
+
return;
|
|
498
|
+
for (const qcache of queries || []) {
|
|
499
|
+
const { _select } = qcache;
|
|
500
|
+
for (const n of Object.keys( _select.mixin || {} ))
|
|
501
|
+
cache.delete( _select.mixin[n] );
|
|
502
|
+
dropColumnsCache( _select.columns );
|
|
503
|
+
traverseDef( _select, null, null, null, a => cache.delete( a ) ); // elements
|
|
504
|
+
}
|
|
505
|
+
traverseDef( main, null, null, null, a => cache.delete( a ) );
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function dropColumnsCache( select ) {
|
|
509
|
+
if (!select)
|
|
510
|
+
return;
|
|
511
|
+
for (const col of select.columns || select.expand || select.inline || []) {
|
|
512
|
+
dropColumnsCache( col );
|
|
513
|
+
cache.delete( select );
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
489
517
|
/**
|
|
490
518
|
* @param {CSN.Path} csnPath
|
|
491
519
|
*
|
|
@@ -629,7 +657,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
629
657
|
if (!art) {
|
|
630
658
|
const { env } = links[i - 1];
|
|
631
659
|
const loc = env.name && env.name.$location || env.$location;
|
|
632
|
-
throw new ModelError( `Path item ${ i }=${ pathId( path[i] ) }
|
|
660
|
+
throw new ModelError( `Path item ${ i }=${ pathId( path[i] ) } refers to nothing; in ${ locationString( loc ) }; path=${ JSON.stringify(path) }` );
|
|
633
661
|
}
|
|
634
662
|
links[i].art = art;
|
|
635
663
|
}
|
package/lib/model/csnUtils.js
CHANGED
|
@@ -423,6 +423,7 @@ function getUtils( model, universalReady ) {
|
|
|
423
423
|
// - r({ref: ['\\', '\\', '\\\\'] }) != r({ref: ['\\', '\\\\', '\\'] })
|
|
424
424
|
// - r({ref: ['\\', '\\', '\\\\'] }) != r({ref: ['\\', '\\\\2:\\\\'] })
|
|
425
425
|
const resolvedKey = (typeof type === 'object')
|
|
426
|
+
// eslint-disable-next-line sonarjs/no-nested-template-literals
|
|
426
427
|
? `ref[${ type.ref.length }]:${ type.ref.map((val, i) => `${ i }:${ val }`).join('\\') }`
|
|
427
428
|
: `type:${ type }`;
|
|
428
429
|
|
|
@@ -170,8 +170,7 @@ function revealInternalProperties( model, nameOrPath ) {
|
|
|
170
170
|
if (!Array.isArray(deps))
|
|
171
171
|
return primOrString( deps );
|
|
172
172
|
return deps
|
|
173
|
-
.
|
|
174
|
-
.map( d => artifactIdentifier( d.art ) );
|
|
173
|
+
.map( d => `${ d.location ? '' : '-' }${ artifactIdentifier( d.art ) }`);
|
|
175
174
|
}
|
|
176
175
|
|
|
177
176
|
function layerExtends( dict ) {
|
|
@@ -120,6 +120,7 @@ function getArtifactComparator(otherModel, options, addedEntities, deletedEntiti
|
|
|
120
120
|
// Arguments are interchanged in this case: `artifact` from beforeModel and `otherArtifact` from afterModel.
|
|
121
121
|
if (isPersisted && !isPersistedOther) {
|
|
122
122
|
deletedEntities[name] = artifact;
|
|
123
|
+
// eslint-disable-next-line sonarjs/no-duplicated-branches
|
|
123
124
|
} else if(isPersistedAsView(artifact) && isPersistedOther) { // view turned into table - need to render a drop for the view
|
|
124
125
|
deletedEntities[name] = artifact;
|
|
125
126
|
}
|
|
@@ -254,7 +255,7 @@ module.exports = {
|
|
|
254
255
|
/**
|
|
255
256
|
* A ModelDiff encapsulates the changes between two models ("before" and "after"). It contains information
|
|
256
257
|
* about changes to .elements and removed artifacts.
|
|
257
|
-
*
|
|
258
|
+
*
|
|
258
259
|
* @typedef {object} ModelDiff
|
|
259
260
|
* @property {CSN.Definitions} definitions The artifacts present in the "after" model
|
|
260
261
|
* @property {CSN.Definitions} deletions The artifacts present in the "before", but not in the "after"
|
package/lib/optionProcessor.js
CHANGED
|
@@ -293,6 +293,7 @@ optionProcessor.command('Q, toSql')
|
|
|
293
293
|
.option(' --constraints-in-create-table')
|
|
294
294
|
.option(' --pre2134ReferentialConstraintNames')
|
|
295
295
|
.option(' --disable-hana-comments')
|
|
296
|
+
.option(' --generated-by-comment')
|
|
296
297
|
.help(`
|
|
297
298
|
Usage: cdsc toSql [options] <files...>
|
|
298
299
|
|
|
@@ -343,6 +344,8 @@ optionProcessor.command('Q, toSql')
|
|
|
343
344
|
"ALTER TABLE ADD CONSTRAINT" statements
|
|
344
345
|
--pre2134ReferentialConstraintNames Do not prefix the constraint identifier with "c__"
|
|
345
346
|
--disable-hana-comments Disable rendering of doc comments as SAP HANA comments.
|
|
347
|
+
--generated-by-comment Enable rendering of the initial SQL comment for HDI-based artifacts
|
|
348
|
+
|
|
346
349
|
`);
|
|
347
350
|
|
|
348
351
|
optionProcessor.command('toRename')
|
|
@@ -23,12 +23,15 @@ const { sortCsn } = require('../json/to-csn');
|
|
|
23
23
|
* @param {CSN.Options} options
|
|
24
24
|
*/
|
|
25
25
|
function alterConstraintsWithCsn( csn, options ) {
|
|
26
|
-
const { error } = makeMessageFunction(csn, options, 'manageConstraints');
|
|
26
|
+
const { error, warning } = makeMessageFunction(csn, options, 'manageConstraints');
|
|
27
27
|
|
|
28
28
|
const {
|
|
29
|
-
drop, alter, src, violations,
|
|
29
|
+
drop, alter, src, violations, sqlDialect,
|
|
30
30
|
} = options || {};
|
|
31
31
|
|
|
32
|
+
if (!sqlDialect || sqlDialect === 'h2' || sqlDialect === 'plain')
|
|
33
|
+
warning(null, null, { prop: sqlDialect || 'plain' }, 'Referential Constraints are not available for sql dialect $(PROP)');
|
|
34
|
+
|
|
32
35
|
if (drop && alter)
|
|
33
36
|
error(null, null, 'Option “--drop” can\'t be combined with “--alter”');
|
|
34
37
|
|
package/lib/render/toCdl.js
CHANGED
|
@@ -1380,17 +1380,28 @@ function csnToCdl( csn, options ) {
|
|
|
1380
1380
|
}
|
|
1381
1381
|
|
|
1382
1382
|
/**
|
|
1383
|
-
* Render an annotation value
|
|
1384
|
-
*
|
|
1383
|
+
* Render an annotation value, which is either
|
|
1384
|
+
* - a normal expressions
|
|
1385
|
+
* - a somewhat simplified expression, with slightly different representation
|
|
1385
1386
|
*
|
|
1386
|
-
* @param {any}
|
|
1387
|
+
* @param {any} annoValue
|
|
1387
1388
|
* @param {CdlRenderEnvironment} env
|
|
1388
1389
|
*/
|
|
1389
|
-
function renderAnnotationValue(
|
|
1390
|
+
function renderAnnotationValue( annoValue, env ) {
|
|
1391
|
+
const isXpr = annoValue?.['='] !== undefined && (Object.keys(annoValue).length > 1) &&
|
|
1392
|
+
isBetaEnabled(options, 'annotationExpressions');
|
|
1393
|
+
if (isXpr) {
|
|
1394
|
+
const xpr = exprRenderer.renderExpr(annoValue, env);
|
|
1395
|
+
return `( ${xpr} )`;
|
|
1396
|
+
}
|
|
1397
|
+
return renderSimpleAnnotationValue(annoValue, env);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
function renderSimpleAnnotationValue( x, env ) {
|
|
1390
1401
|
if (Array.isArray(x)) {
|
|
1391
1402
|
// Render array parts as values. Spaces required if last array value is
|
|
1392
1403
|
// a delimited identifier.
|
|
1393
|
-
return `[ ${x.map(item =>
|
|
1404
|
+
return `[ ${x.map(item => renderSimpleAnnotationValue(item, env)).join(', ')} ]`;
|
|
1394
1405
|
}
|
|
1395
1406
|
else if (typeof x === 'object' && x !== null) {
|
|
1396
1407
|
// Enum symbol
|
|
@@ -1405,7 +1416,7 @@ function csnToCdl( csn, options ) {
|
|
|
1405
1416
|
else if (x['...']) {
|
|
1406
1417
|
if (x['...'] === true)
|
|
1407
1418
|
return '...';
|
|
1408
|
-
return `... up to ${
|
|
1419
|
+
return `... up to ${renderSimpleAnnotationValue(x['...'], env)}`;
|
|
1409
1420
|
}
|
|
1410
1421
|
|
|
1411
1422
|
// Struct value (can currently only occur within an array)
|
|
@@ -1413,7 +1424,7 @@ function csnToCdl( csn, options ) {
|
|
|
1413
1424
|
// struct if there are more and use nicer indentation.
|
|
1414
1425
|
const keys = Object.keys(x);
|
|
1415
1426
|
const childEnv = keys.length <= 1 ? env : increaseIndent(env);
|
|
1416
|
-
const values = keys.map(key => `${quoteAnnotationPathIfRequired(key)}: ${
|
|
1427
|
+
const values = keys.map(key => `${quoteAnnotationPathIfRequired(key)}: ${renderSimpleAnnotationValue(x[key], childEnv)}`);
|
|
1417
1428
|
if (values.length <= 1)
|
|
1418
1429
|
return `{ ${values.join(', ')} }`;
|
|
1419
1430
|
const valueList = values.join(`,\n${childEnv.indent}`);
|
|
@@ -1814,6 +1825,8 @@ function csnToCdl( csn, options ) {
|
|
|
1814
1825
|
if (keywords.cdl_functions.includes(x.func.toUpperCase()) && !x.args)
|
|
1815
1826
|
return x.func;
|
|
1816
1827
|
const name = smartFunctionId(x.func);
|
|
1828
|
+
if (!x.args) // e.g. for methods without arguments, `args` is not set at all.
|
|
1829
|
+
return `${name}`;
|
|
1817
1830
|
return `${name}(${renderArguments( x, '=>', this.env )})`;
|
|
1818
1831
|
},
|
|
1819
1832
|
xpr(x) {
|
package/lib/render/toHdbcds.js
CHANGED
|
@@ -1684,25 +1684,19 @@ function toHdbcdsSource( csn, options ) {
|
|
|
1684
1684
|
}
|
|
1685
1685
|
|
|
1686
1686
|
/**
|
|
1687
|
-
* Return an id 'id' with appropriate
|
|
1687
|
+
* Return an id 'id' with appropriate double-quotes
|
|
1688
1688
|
*
|
|
1689
1689
|
* @param {string} id Identifier to quote
|
|
1690
1690
|
* @returns {string} Properly quoted identifier
|
|
1691
1691
|
*/
|
|
1692
1692
|
function quoteId( id ) {
|
|
1693
|
-
// Should only ever be called for real IDs (i.e. no dots inside)
|
|
1694
|
-
if (id.indexOf('.') !== -1)
|
|
1695
|
-
throw new ModelError(`HDBCDS: Tried to quote id with dot: ${id}`);
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
1693
|
switch (options.sqlMapping) {
|
|
1699
1694
|
case 'plain':
|
|
1700
1695
|
return smartId(id, 'hdbcds');
|
|
1701
1696
|
case 'quoted':
|
|
1702
1697
|
case 'hdbcds':
|
|
1703
|
-
return delimitedId(id, 'hdbcds');
|
|
1704
1698
|
default:
|
|
1705
|
-
return
|
|
1699
|
+
return delimitedId(id, 'hdbcds');
|
|
1706
1700
|
}
|
|
1707
1701
|
}
|
|
1708
1702
|
|
package/lib/render/toSql.js
CHANGED
|
@@ -93,10 +93,11 @@ function toSqlDdl( csn, options ) {
|
|
|
93
93
|
return `CAST(${this.renderExpr(withoutCast(x))} AS ${typeRef})`;
|
|
94
94
|
},
|
|
95
95
|
val: renderExpressionLiteral,
|
|
96
|
-
enum
|
|
97
|
-
// TODO:
|
|
96
|
+
enum(x) {
|
|
97
|
+
// TODO: better location; callers should set env.$path
|
|
98
98
|
// FIXME: We can't do enums yet because they are not resolved (and we don't bother finding their value by hand)
|
|
99
|
-
|
|
99
|
+
const loc = x.$location || this.env?._artifact?.$location;
|
|
100
|
+
error('expr-unexpected-enum', [ loc, null ], 'Enum values are not yet supported for conversion to SQL');
|
|
100
101
|
return '';
|
|
101
102
|
},
|
|
102
103
|
ref: renderExpressionRef,
|
|
@@ -230,7 +231,7 @@ function toSqlDdl( csn, options ) {
|
|
|
230
231
|
sourceString = sourceString.slice('COLUMN '.length);
|
|
231
232
|
sql[name] = `${options.testMode ? '' : sqlVersionLine}CREATE ${sourceString};`;
|
|
232
233
|
}
|
|
233
|
-
else if (!options.testMode) {
|
|
234
|
+
else if (!options.testMode && options.generatedByComment) {
|
|
234
235
|
mainResultObj[hdbKind][name] = sqlVersionLine + mainResultObj[hdbKind][name];
|
|
235
236
|
}
|
|
236
237
|
}
|
|
@@ -253,7 +254,7 @@ function toSqlDdl( csn, options ) {
|
|
|
253
254
|
mainResultObj.sql = sql;
|
|
254
255
|
|
|
255
256
|
for (const name in deletions)
|
|
256
|
-
deletions[name] = `${options.testMode ? '' : sqlVersionLine}${deletions[name]}`;
|
|
257
|
+
deletions[name] = `${options.testMode || !options.generatedByComment ? '' : sqlVersionLine}${deletions[name]}`;
|
|
257
258
|
|
|
258
259
|
timetrace.stop('SQL rendering');
|
|
259
260
|
return mainResultObj;
|
|
@@ -54,10 +54,10 @@ function funcWithoutParen( node, dialect ) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* Process already rendered expression parts by joining them nicely
|
|
58
|
-
*
|
|
59
|
-
* @param {Array} tokens Array of expression tokens
|
|
57
|
+
* Process already rendered expression parts by joining them nicely.
|
|
58
|
+
* For example, it adds spaces around operators such as `+`, but not around `.`.
|
|
60
59
|
*
|
|
60
|
+
* @param {any[]} tokens Array of expression tokens
|
|
61
61
|
* @returns {string} The rendered xpr
|
|
62
62
|
*/
|
|
63
63
|
function beautifyExprArray( tokens ) {
|
|
@@ -65,8 +65,12 @@ function beautifyExprArray( tokens ) {
|
|
|
65
65
|
let result = '';
|
|
66
66
|
for (let i = 0; i < tokens.length; i++) {
|
|
67
67
|
result += tokens[i];
|
|
68
|
-
// No space after last token, after opening parentheses, before closing parentheses, before comma
|
|
69
|
-
if (i !== tokens.length - 1 &&
|
|
68
|
+
// No space after last token, after opening parentheses, before closing parentheses, before comma, before and after dot
|
|
69
|
+
if (i !== tokens.length - 1 &&
|
|
70
|
+
// current token
|
|
71
|
+
tokens[i] !== '(' && tokens[i] !== '.' &&
|
|
72
|
+
// next token
|
|
73
|
+
tokens[i + 1] !== ')' && tokens[i + 1] !== '.' && tokens[i + 1] !== ',')
|
|
70
74
|
result += ' ';
|
|
71
75
|
}
|
|
72
76
|
return result;
|
|
@@ -19,11 +19,12 @@ const { pathName } = require('../../compiler/utils');
|
|
|
19
19
|
* @param {CSN.Options} options Options
|
|
20
20
|
* @param {Function} error Message function for errors
|
|
21
21
|
* @param {Function} info Message function for info
|
|
22
|
+
* @returns {Function} forEachDefinition callback
|
|
22
23
|
*/
|
|
23
24
|
function processAssertUnique( csn, options, error, info ) {
|
|
24
25
|
const { resolvePath, flattenPath } = getTransformers(csn, options);
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
return handleAssertUnique;
|
|
27
28
|
/**
|
|
28
29
|
* The detailed processing - see comment above for what is going on here
|
|
29
30
|
*
|
|
@@ -361,6 +361,8 @@ function expandStructureReferences( csn, options, pathDelimiter, { error, info,
|
|
|
361
361
|
expanded.push(Object.assign({}, current, { as: currentAlias.join(pathDelimiter) } ));
|
|
362
362
|
}
|
|
363
363
|
else { // preserve stuff like .cast for redirection
|
|
364
|
+
if (base[currentAlias[currentAlias.length - 1]]?.value)
|
|
365
|
+
error('query-unsupported-calc', col.$path, { '#': 'inside' });
|
|
364
366
|
expanded.push(Object.assign({}, current, { ref: currentRef, as: currentAlias.join(pathDelimiter) } ));
|
|
365
367
|
}
|
|
366
368
|
}
|
|
@@ -293,7 +293,7 @@ function flattenElements( csn, options, pathDelimiter, error, iterateOptions = {
|
|
|
293
293
|
// TODO: use $ignore - _ is for links
|
|
294
294
|
element._ignore = true;
|
|
295
295
|
|
|
296
|
-
const branches = getBranches(element, elementName);
|
|
296
|
+
const branches = getBranches(element, elementName, effectiveType, pathDelimiter);
|
|
297
297
|
const flatElems = flattenStructuredElement(element, elementName, [], path.concat([ 'elements', elementName ]));
|
|
298
298
|
|
|
299
299
|
for (const flatElemName in flatElems) {
|
|
@@ -308,7 +308,7 @@ function flattenElements( csn, options, pathDelimiter, error, iterateOptions = {
|
|
|
308
308
|
const flatElement = flatElems[flatElemName];
|
|
309
309
|
|
|
310
310
|
// Check if we have a valid notNull chain
|
|
311
|
-
const branch = branches[flatElemName];
|
|
311
|
+
const branch = branches[flatElemName].steps;
|
|
312
312
|
if (flatElement.notNull !== false && !branch.some(s => !s.notNull))
|
|
313
313
|
flatElement.notNull = true;
|
|
314
314
|
|
|
@@ -357,49 +357,49 @@ function flattenElements( csn, options, pathDelimiter, error, iterateOptions = {
|
|
|
357
357
|
return elements;
|
|
358
358
|
}, Object.create(null));
|
|
359
359
|
}
|
|
360
|
+
}
|
|
360
361
|
|
|
361
|
-
|
|
362
|
+
/**
|
|
363
|
+
* Get not just the leaves, but all branches of a structured element.
|
|
364
|
+
*
|
|
365
|
+
* @param {object} element Structured element
|
|
366
|
+
* @param {string} elementName Name of the structured element
|
|
367
|
+
* @param {Function} effectiveType
|
|
368
|
+
* @param {string} pathDelimiter
|
|
369
|
+
* @returns {object} Returns a dictionary, where the key is the flat name of the branch and the value is an array of element-steps.
|
|
370
|
+
*/
|
|
371
|
+
function getBranches( element, elementName, effectiveType, pathDelimiter ) {
|
|
372
|
+
const branches = {};
|
|
373
|
+
const subbranchNames = [];
|
|
374
|
+
const subbranchElements = [];
|
|
375
|
+
walkElements(element, elementName);
|
|
362
376
|
/**
|
|
363
|
-
*
|
|
377
|
+
* Walk the element chain
|
|
364
378
|
*
|
|
365
|
-
* @param {object}
|
|
366
|
-
* @param {string}
|
|
367
|
-
* @returns {object} Returns a dictionary, where the key is the flat name of the branch and the value is an array of element-steps.
|
|
379
|
+
* @param {object} e
|
|
380
|
+
* @param {string} name
|
|
368
381
|
*/
|
|
369
|
-
function
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
382
|
+
function walkElements( e, name ) {
|
|
383
|
+
if (isBuiltinType(e.type)) {
|
|
384
|
+
branches[subbranchNames.concat(name).join(pathDelimiter)] = { steps: subbranchElements.concat(e), ref: subbranchNames.concat(name) };
|
|
385
|
+
}
|
|
386
|
+
else {
|
|
387
|
+
const subelements = e.elements || effectiveType(e).elements;
|
|
388
|
+
if (subelements) {
|
|
389
|
+
subbranchElements.push(e);
|
|
390
|
+
subbranchNames.push(name);
|
|
391
|
+
for (const [ subelementName, subelement ] of Object.entries(subelements))
|
|
392
|
+
walkElements(subelement, subelementName);
|
|
393
|
+
|
|
394
|
+
subbranchNames.pop();
|
|
395
|
+
subbranchElements.pop();
|
|
383
396
|
}
|
|
384
397
|
else {
|
|
385
|
-
|
|
386
|
-
const subelements = e.elements || eType.elements;
|
|
387
|
-
if (subelements) {
|
|
388
|
-
subbranchElements.push(e);
|
|
389
|
-
subbranchNames.push(name);
|
|
390
|
-
for (const [ subelementName, subelement ] of Object.entries(subelements))
|
|
391
|
-
walkElements(subelement, subelementName);
|
|
392
|
-
|
|
393
|
-
subbranchNames.pop();
|
|
394
|
-
subbranchElements.pop();
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
branches[subbranchNames.concat(name).join(pathDelimiter)] = subbranchElements.concat(e);
|
|
398
|
-
}
|
|
398
|
+
branches[subbranchNames.concat(name).join(pathDelimiter)] = { steps: subbranchElements.concat(e), ref: subbranchNames.concat(name) };
|
|
399
399
|
}
|
|
400
400
|
}
|
|
401
|
-
return branches;
|
|
402
401
|
}
|
|
402
|
+
return branches;
|
|
403
403
|
}
|
|
404
404
|
|
|
405
405
|
/**
|
|
@@ -792,4 +792,5 @@ module.exports = {
|
|
|
792
792
|
flattenElements,
|
|
793
793
|
removeLeadingSelf,
|
|
794
794
|
handleManagedAssociationsAndCreateForeignKeys,
|
|
795
|
+
getBranches,
|
|
795
796
|
};
|