@sap/cds-compiler 3.5.4 → 3.6.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 +65 -2
- package/bin/cdsc.js +14 -6
- package/doc/CHANGELOG_ARCHIVE.md +10 -10
- package/doc/CHANGELOG_DEPRECATED.md +2 -2
- package/lib/api/main.js +32 -55
- package/lib/api/options.js +3 -2
- package/lib/api/validate.js +5 -0
- package/lib/base/message-registry.js +104 -32
- package/lib/base/messages.js +277 -212
- package/lib/base/optionProcessorHelper.js +9 -2
- package/lib/base/shuffle.js +50 -0
- package/lib/checks/actionsFunctions.js +37 -20
- package/lib/checks/foreignKeys.js +13 -6
- package/lib/checks/nonexpandableStructured.js +1 -2
- package/lib/checks/onConditions.js +21 -19
- package/lib/checks/parameters.js +1 -1
- package/lib/checks/queryNoDbArtifacts.js +2 -0
- package/lib/checks/types.js +16 -22
- package/lib/compiler/assert-consistency.js +31 -28
- package/lib/compiler/builtins.js +20 -4
- package/lib/compiler/checks.js +72 -63
- package/lib/compiler/define.js +396 -314
- package/lib/compiler/extend.js +55 -49
- package/lib/compiler/index.js +5 -0
- package/lib/compiler/populate.js +28 -11
- package/lib/compiler/propagator.js +2 -1
- package/lib/compiler/resolve.js +28 -13
- package/lib/compiler/shared.js +15 -10
- package/lib/compiler/utils.js +7 -7
- package/lib/edm/annotations/genericTranslation.js +51 -46
- package/lib/edm/annotations/preprocessAnnotations.js +37 -40
- package/lib/edm/csn2edm.js +69 -21
- package/lib/edm/edm.js +2 -2
- package/lib/edm/edmInboundChecks.js +6 -8
- package/lib/edm/edmPreprocessor.js +88 -80
- package/lib/edm/edmUtils.js +6 -15
- package/lib/gen/Dictionary.json +81 -13
- package/lib/gen/language.checksum +1 -1
- package/lib/gen/language.interp +2 -1
- package/lib/gen/languageParser.js +4680 -4484
- package/lib/inspect/inspectModelStatistics.js +2 -1
- package/lib/inspect/inspectPropagation.js +2 -1
- package/lib/json/from-csn.js +131 -78
- package/lib/json/to-csn.js +39 -23
- package/lib/language/antlrParser.js +0 -3
- package/lib/language/docCommentParser.js +7 -3
- package/lib/language/errorStrategy.js +3 -2
- package/lib/language/genericAntlrParser.js +96 -41
- package/lib/language/language.g4 +112 -128
- package/lib/language/multiLineStringParser.js +2 -1
- package/lib/main.d.ts +115 -2
- package/lib/main.js +16 -3
- package/lib/model/csnRefs.js +32 -4
- package/lib/model/csnUtils.js +109 -179
- package/lib/model/enrichCsn.js +13 -8
- package/lib/model/revealInternalProperties.js +4 -3
- package/lib/optionProcessor.js +22 -3
- package/lib/render/manageConstraints.js +11 -15
- package/lib/render/toCdl.js +144 -47
- package/lib/render/toHdbcds.js +22 -22
- package/lib/render/toRename.js +3 -4
- package/lib/render/toSql.js +31 -22
- package/lib/render/utils/delta.js +3 -1
- package/lib/render/utils/sql.js +2 -14
- package/lib/transform/db/associations.js +6 -6
- package/lib/transform/db/cdsPersistence.js +3 -3
- package/lib/transform/db/constraints.js +4 -6
- package/lib/transform/db/expansion.js +4 -4
- package/lib/transform/db/flattening.js +12 -15
- package/lib/transform/db/temporal.js +4 -3
- package/lib/transform/db/transformExists.js +13 -7
- package/lib/transform/draft/db.js +7 -7
- package/lib/transform/forOdataNew.js +15 -4
- package/lib/transform/forRelationalDB.js +59 -41
- package/lib/transform/odata/toFinalBaseType.js +106 -82
- package/lib/transform/odata/typesExposure.js +26 -17
- package/lib/transform/odata/utils.js +1 -1
- package/lib/transform/parseExpr.js +1 -1
- package/lib/transform/transformUtilsNew.js +33 -10
- package/lib/transform/translateAssocsToJoins.js +8 -7
- package/lib/transform/universalCsn/coreComputed.js +7 -5
- package/lib/transform/universalCsn/universalCsnEnricher.js +12 -4
- package/lib/utils/timetrace.js +2 -2
- package/package.json +1 -2
|
@@ -5,6 +5,7 @@ const {
|
|
|
5
5
|
isWhitespaceCharacterNoNewline,
|
|
6
6
|
cdlNewLineRegEx,
|
|
7
7
|
} = require('./textUtils');
|
|
8
|
+
const { CompilerAssertion } = require('../base/error');
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Strips and counts the indentation from the given string.
|
|
@@ -76,7 +77,7 @@ class MultiLineStringParser {
|
|
|
76
77
|
|
|
77
78
|
if (this.str[0] !== '`' || this.str[this.str.length - 1] !== '`')
|
|
78
79
|
// eslint-disable-next-line max-len
|
|
79
|
-
throw new
|
|
80
|
+
throw new CompilerAssertion('Invalid multi-line string sequence: Require string to be surrounded by back-ticks!');
|
|
80
81
|
|
|
81
82
|
this.output = [];
|
|
82
83
|
this.isTextBlock = this.str.startsWith('```');
|
package/lib/main.d.ts
CHANGED
|
@@ -236,7 +236,7 @@ declare namespace compiler {
|
|
|
236
236
|
*
|
|
237
237
|
* Different databases may support different feature sets of SQL.
|
|
238
238
|
* For example, timestamps are handled differently. Furthermore, "smart-quoting"
|
|
239
|
-
* is enabled for
|
|
239
|
+
* is enabled for all flavors except `plain`. This is useful if identifiers
|
|
240
240
|
* collide with reserved keywords.
|
|
241
241
|
*
|
|
242
242
|
* - `plain`:
|
|
@@ -252,10 +252,20 @@ declare namespace compiler {
|
|
|
252
252
|
* - `hana`:
|
|
253
253
|
* Use this SQL dialect for best compatibility with SAP HANA.
|
|
254
254
|
* "smart-quoting" upper-cases and quotes identifiers.
|
|
255
|
+
* - `postgres:
|
|
256
|
+
* This SQL dialect ensures compatibility with PostgreSQL.
|
|
257
|
+
* Does not support `hana.*` types. Requires `sqlMapping: 'plain'`.
|
|
258
|
+
* "smart-quoting" quotes identifiers that are reserved keywords, but does not upper-case them.
|
|
259
|
+
* Since v3.3.0
|
|
260
|
+
* - `h2`
|
|
261
|
+
* This SQL dialect ensures compatibility with H2 v2.
|
|
262
|
+
* Does not support `hana.*` types. Requires `sqlMapping: 'plain'`.
|
|
263
|
+
* "smart-quoting" quotes identifiers that are reserved keywords and upper-cases them.
|
|
264
|
+
* Since v3.4.0
|
|
255
265
|
*
|
|
256
266
|
* @default 'plain'
|
|
257
267
|
*/
|
|
258
|
-
sqlDialect?: string | 'plain' | 'sqlite' | 'hana'
|
|
268
|
+
sqlDialect?: string | 'plain' | 'sqlite' | 'hana' | 'postgres' | 'h2'
|
|
259
269
|
/**
|
|
260
270
|
* Object containing magic variables. These magic variables are
|
|
261
271
|
* used as placeholder values.
|
|
@@ -686,6 +696,59 @@ declare namespace compiler {
|
|
|
686
696
|
* Only relevant for element references of path length 1.
|
|
687
697
|
*/
|
|
688
698
|
const functions: string[];
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* If the given `name` requires brackets in SQL, return an escaped
|
|
702
|
+
* identifier in brackets.
|
|
703
|
+
* Otherwise, return the name without brackets.
|
|
704
|
+
*
|
|
705
|
+
* Example:
|
|
706
|
+
* ```js
|
|
707
|
+
* to.cdl.smartId('with ![brackets]')
|
|
708
|
+
* // '![with ![brackets]]]'
|
|
709
|
+
* to.cdl.smartId('OCCURRENCE', null)
|
|
710
|
+
* // 'OCCURRENCE'
|
|
711
|
+
* to.cdl.smartId('OCCURRENCE', 'REPLACE_REGEXPR')
|
|
712
|
+
* // '![OCCURRENCE]'
|
|
713
|
+
* to.cdl.smartId('myid')
|
|
714
|
+
* // 'myid'
|
|
715
|
+
* ```
|
|
716
|
+
*
|
|
717
|
+
* @param name
|
|
718
|
+
* @param [insideFunction=null]
|
|
719
|
+
*/
|
|
720
|
+
function smartId(name: string, insideFunction?: string|null) : string;
|
|
721
|
+
/**
|
|
722
|
+
* If the given function `name` requires quoting in CDL, return an escaped
|
|
723
|
+
* function identifier in brackets for CDL.
|
|
724
|
+
* Otherwise, return the function name without brackets.
|
|
725
|
+
*
|
|
726
|
+
* Example:
|
|
727
|
+
* ```js
|
|
728
|
+
* to.cdl.smartFunctionId('with ![brackets]')
|
|
729
|
+
* // '![with ![brackets]]]'
|
|
730
|
+
* to.cdl.smartFunctionId('myfunction')
|
|
731
|
+
* // 'myfunction'
|
|
732
|
+
* ```
|
|
733
|
+
*
|
|
734
|
+
* @param name
|
|
735
|
+
*/
|
|
736
|
+
function smartFunctionId(name: string) : string;
|
|
737
|
+
/**
|
|
738
|
+
* Escapes the given name according to the CDL language and puts it
|
|
739
|
+
* into `![` and `]`, properly escaping all `]` in the identifier.
|
|
740
|
+
*
|
|
741
|
+
* Example:
|
|
742
|
+
* ```js
|
|
743
|
+
* to.cdl.delimitedId('with ![brackets]')
|
|
744
|
+
* // '![with ![brackets]]]'
|
|
745
|
+
* to.cdl.delimitedId('myid')
|
|
746
|
+
* // '![myid]'
|
|
747
|
+
* ```
|
|
748
|
+
*
|
|
749
|
+
* @param name
|
|
750
|
+
*/
|
|
751
|
+
function delimitedId(name: string) : string;
|
|
689
752
|
}
|
|
690
753
|
|
|
691
754
|
/**
|
|
@@ -702,8 +765,58 @@ declare namespace compiler {
|
|
|
702
765
|
*/
|
|
703
766
|
const keywords: string[];
|
|
704
767
|
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* If the given `name` requires quoting for SQL dialect `dialect`,
|
|
771
|
+
* returns a quoted and escaped identifier for that SQL dialect.
|
|
772
|
+
* Otherwise, returns the name without quotes.
|
|
773
|
+
*
|
|
774
|
+
* Example:
|
|
775
|
+
* ```js
|
|
776
|
+
* to.sql.smartId('with "quotes"', 'sqlite')
|
|
777
|
+
* // '"with ""quotes"""'
|
|
778
|
+
* to.sql.smartId('SELECT', 'sqlite')
|
|
779
|
+
* // '"SELECT"'
|
|
780
|
+
* to.sql.smartId('myid', 'sqlite')
|
|
781
|
+
* // 'myid'
|
|
782
|
+
* ```
|
|
783
|
+
*
|
|
784
|
+
* @param name
|
|
785
|
+
* @param dialect
|
|
786
|
+
*/
|
|
705
787
|
function smartId(name: string, dialect: string) : string;
|
|
788
|
+
/**
|
|
789
|
+
* If the given function `name` requires quoting for SQL dialect `dialect`,
|
|
790
|
+
* returns a quoted and escaped function identifier for that SQL dialect.
|
|
791
|
+
* Otherwise, returns the function name without quotes.
|
|
792
|
+
*
|
|
793
|
+
* Example:
|
|
794
|
+
* ```js
|
|
795
|
+
* to.sql.smartFunctionId('with "quotes"', 'sqlite')
|
|
796
|
+
* // '"with ""quotes"""'
|
|
797
|
+
* to.sql.smartFunctionId('myfunction', 'sqlite')
|
|
798
|
+
* // 'myfunction'
|
|
799
|
+
* ```
|
|
800
|
+
*
|
|
801
|
+
* @param name
|
|
802
|
+
* @param dialect
|
|
803
|
+
*/
|
|
706
804
|
function smartFunctionId(name: string, dialect: string) : string;
|
|
805
|
+
/**
|
|
806
|
+
* Escapes the given name according to the SQL dialect and puts it
|
|
807
|
+
* into quotes.
|
|
808
|
+
*
|
|
809
|
+
* Example:
|
|
810
|
+
* ```js
|
|
811
|
+
* to.sql.delimitedId('with "quotes"', 'sqlite')
|
|
812
|
+
* // '"with ""quotes"""'
|
|
813
|
+
* to.sql.delimitedId('myid', 'sqlite')
|
|
814
|
+
* // '"myid"'
|
|
815
|
+
* ```
|
|
816
|
+
*
|
|
817
|
+
* @param name
|
|
818
|
+
* @param dialect
|
|
819
|
+
*/
|
|
707
820
|
function delimitedId(name: string, dialect: string) : string;
|
|
708
821
|
}
|
|
709
822
|
|
package/lib/main.js
CHANGED
|
@@ -19,6 +19,7 @@ const model_api = lazyload('./model/api');
|
|
|
19
19
|
const messages = lazyload('./base/messages');
|
|
20
20
|
const sqlIdentifier = lazyload('./sql-identifier');
|
|
21
21
|
const keywords = lazyload( './base/keywords' );
|
|
22
|
+
const toCdl = lazyload('./render/toCdl');
|
|
22
23
|
|
|
23
24
|
const parseLanguage = lazyload('./language/antlrParser');
|
|
24
25
|
const compiler = lazyload('./compiler');
|
|
@@ -106,13 +107,22 @@ module.exports = {
|
|
|
106
107
|
hasErrors: (...args) => messages.hasErrors(...args),
|
|
107
108
|
|
|
108
109
|
// additional API:
|
|
109
|
-
parse: {
|
|
110
|
+
parse: {
|
|
111
|
+
cdl: (...args) => parseCdl(...args),
|
|
112
|
+
cql: (...args) => parseCql(...args),
|
|
113
|
+
expr: (...args) => parseExpr(...args)
|
|
114
|
+
},
|
|
110
115
|
// SNAPI
|
|
111
|
-
for: {
|
|
116
|
+
for: {
|
|
117
|
+
odata: (...args) => snapi.odata(...args)
|
|
118
|
+
},
|
|
112
119
|
to: {
|
|
113
120
|
cdl: Object.assign((...args) => snapi.cdl(...args), {
|
|
114
121
|
keywords: Object.freeze([ ...keywords.cdl ] ),
|
|
115
122
|
functions: Object.freeze([ ...keywords.cdl_functions ] ),
|
|
123
|
+
smartId: (...args) => toCdl.smartId(...args),
|
|
124
|
+
smartFunctionId: (...args) => toCdl.smartFunctionId(...args),
|
|
125
|
+
delimitedId: (...args) => toCdl.delimitedId(...args),
|
|
116
126
|
}),
|
|
117
127
|
sql: Object.assign((...args) => snapi.sql(...args), {
|
|
118
128
|
migration: (...args) => snapi.sql.migration(...args),
|
|
@@ -147,7 +157,10 @@ module.exports = {
|
|
|
147
157
|
// INTERNAL functions for the cds-lsp package and friends - before you use
|
|
148
158
|
// it, you MUST talk with us - there can be potential incompatibilities with
|
|
149
159
|
// new releases (even having the same major version):
|
|
150
|
-
$lsp: { parse: (...args) => compiler.parseX(...args),
|
|
160
|
+
$lsp: { parse: (...args) => compiler.parseX(...args),
|
|
161
|
+
compile: (...args) => compiler.compileX(...args),
|
|
162
|
+
getArtifactName: a => a.name
|
|
163
|
+
},
|
|
151
164
|
|
|
152
165
|
// CSN Model related functionality
|
|
153
166
|
model: {
|
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
|
};
|
|
@@ -334,7 +337,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
334
337
|
return art;
|
|
335
338
|
}
|
|
336
339
|
if (notFound !== undefined && typeof ref === 'string')
|
|
337
|
-
return notFound; // is only meant for builtins
|
|
340
|
+
return notFound; // is only meant for builtins and $self
|
|
338
341
|
// Backend bug workaround, TODO: delete next 2 lines
|
|
339
342
|
if (notFound !== undefined)
|
|
340
343
|
return notFound;
|
|
@@ -396,7 +399,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
396
399
|
const location = $location &&
|
|
397
400
|
(typeof $location === 'string' ? $location : locationString( $location ));
|
|
398
401
|
const def = Object.keys( art ).join('+') + (location ? `:${ location }` : '');
|
|
399
|
-
throw new
|
|
402
|
+
throw new CompilerAssertion( `Inspecting non-initialized CSN node {${ def }}` );
|
|
400
403
|
}
|
|
401
404
|
const step = getCache( art, '$origin$step' );
|
|
402
405
|
if (!step)
|
|
@@ -436,7 +439,7 @@ function csnRefs( csn, universalReady ) {
|
|
|
436
439
|
return effectiveType( art ).items;
|
|
437
440
|
if (step.target)
|
|
438
441
|
return targetAspect( effectiveType( art ) );
|
|
439
|
-
throw
|
|
442
|
+
throw new CompilerAssertion( `Illegal navigation step ${ Object.keys(step)[0] }` );
|
|
440
443
|
}
|
|
441
444
|
|
|
442
445
|
function effectiveArtFor( art, property ) {
|
|
@@ -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
|
*
|