@sap/cds-compiler 5.9.2 → 6.0.10
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 +109 -319
- package/README.md +1 -1
- package/bin/cds_update_identifiers.js +3 -5
- package/bin/cdsc.js +22 -8
- package/bin/cdshi.js +1 -1
- package/bin/cdsse.js +4 -4
- package/doc/CHANGELOG_BETA.md +11 -0
- package/doc/CHANGELOG_DEPRECATED.md +29 -0
- package/lib/api/main.js +8 -5
- package/lib/api/options.js +12 -10
- package/lib/base/builtins.js +1 -0
- package/lib/base/message-registry.js +190 -96
- package/lib/base/messages.js +29 -20
- package/lib/base/model.js +14 -24
- package/lib/checks/actionsFunctions.js +10 -20
- package/lib/checks/annotationsOData.js +1 -1
- package/lib/checks/elements.js +30 -10
- package/lib/checks/enums.js +31 -0
- package/lib/checks/foreignKeys.js +2 -2
- package/lib/checks/hasPersistedElements.js +5 -0
- package/lib/checks/invalidTarget.js +1 -1
- package/lib/checks/managedWithoutKeys.js +5 -4
- package/lib/checks/queryNoDbArtifacts.js +10 -8
- package/lib/checks/types.js +5 -5
- package/lib/checks/validator.js +6 -4
- package/lib/compiler/assert-consistency.js +12 -9
- package/lib/compiler/checks.js +18 -50
- package/lib/compiler/define.js +6 -6
- package/lib/compiler/extend.js +2 -1
- package/lib/compiler/generate.js +14 -17
- package/lib/compiler/populate.js +8 -31
- package/lib/compiler/propagator.js +21 -35
- package/lib/compiler/resolve.js +35 -22
- package/lib/compiler/shared.js +7 -1
- package/lib/compiler/tweak-assocs.js +1 -1
- package/lib/compiler/utils.js +1 -1
- package/lib/edm/annotations/edmJson.js +20 -15
- package/lib/edm/annotations/genericTranslation.js +7 -8
- package/lib/edm/csn2edm.js +46 -50
- package/lib/edm/edm.js +8 -7
- package/lib/edm/edmPreprocessor.js +37 -85
- package/lib/edm/edmUtils.js +2 -2
- package/lib/gen/BaseParser.js +55 -44
- package/lib/gen/CdlGrammar.checksum +1 -1
- package/lib/gen/CdlParser.js +1133 -1150
- package/lib/json/from-csn.js +70 -43
- package/lib/json/to-csn.js +6 -8
- package/lib/language/multiLineStringParser.js +3 -2
- package/lib/main.d.ts +58 -24
- package/lib/model/csnUtils.js +28 -39
- package/lib/model/xprAsTree.js +23 -9
- package/lib/modelCompare/compare.js +5 -4
- package/lib/optionProcessor.js +21 -17
- package/lib/parsers/AstBuildingParser.js +63 -11
- package/lib/parsers/XprTree.js +57 -3
- package/lib/parsers/identifiers.js +1 -1
- package/lib/parsers/index.js +0 -3
- package/lib/render/manageConstraints.js +25 -25
- package/lib/render/toCdl.js +173 -170
- package/lib/render/toHdbcds.js +126 -128
- package/lib/render/toRename.js +7 -7
- package/lib/render/toSql.js +128 -125
- package/lib/render/utils/common.js +47 -22
- package/lib/render/utils/delta.js +25 -25
- package/lib/render/utils/operators.js +2 -2
- package/lib/render/utils/pretty.js +5 -5
- package/lib/render/utils/sql.js +13 -13
- package/lib/render/utils/standardDatabaseFunctions.js +115 -103
- package/lib/render/utils/unique.js +4 -4
- package/lib/transform/db/applyTransformations.js +1 -1
- package/lib/transform/db/assertUnique.js +2 -2
- package/lib/transform/db/associations.js +6 -7
- package/lib/transform/db/assocsToQueries/utils.js +4 -5
- package/lib/transform/db/backlinks.js +12 -9
- package/lib/transform/db/cdsPersistence.js +8 -7
- package/lib/transform/db/constraints.js +13 -10
- package/lib/transform/db/expansion.js +7 -3
- package/lib/transform/db/flattening.js +4 -14
- package/lib/transform/db/processSqlServices.js +2 -1
- package/lib/transform/db/temporal.js +5 -7
- package/lib/transform/db/views.js +2 -4
- package/lib/transform/draft/db.js +8 -8
- package/lib/transform/draft/odata.js +10 -7
- package/lib/transform/forOdata.js +10 -5
- package/lib/transform/forRelationalDB.js +5 -75
- package/lib/transform/localized.js +1 -1
- package/lib/transform/odata/createForeignKeys.js +11 -10
- package/lib/transform/odata/flattening.js +8 -4
- package/lib/transform/odata/foreignKeyRefsInXprAnnos.js +96 -0
- package/lib/transform/odata/typesExposure.js +3 -3
- package/lib/transform/transformUtils.js +4 -8
- package/lib/transform/translateAssocsToJoins.js +14 -7
- package/lib/transform/universalCsn/universalCsnEnricher.js +10 -4
- package/lib/utils/objectUtils.js +0 -17
- package/package.json +10 -13
- package/share/messages/def-upcoming-virtual-change.md +1 -1
- package/LICENSE +0 -37
- package/bin/cds_remove_invalid_whitespace.js +0 -138
- package/doc/CHANGELOG_ARCHIVE.md +0 -3604
- package/lib/gen/genericAntlrParser.js +0 -3
- package/lib/gen/language.checksum +0 -1
- package/lib/gen/language.interp +0 -456
- package/lib/gen/language.tokens +0 -180
- package/lib/gen/languageLexer.interp +0 -439
- package/lib/gen/languageLexer.js +0 -1483
- package/lib/gen/languageLexer.tokens +0 -167
- package/lib/gen/languageParser.js +0 -24941
- package/lib/language/antlrParser.js +0 -205
- package/lib/language/errorStrategy.js +0 -646
- package/lib/language/genericAntlrParser.js +0 -1572
- package/lib/parsers/CdlGrammar.g4 +0 -2070
package/lib/json/from-csn.js
CHANGED
|
@@ -89,9 +89,6 @@
|
|
|
89
89
|
* of the xorGroup may be set, e.g. if target is set, elements may not.
|
|
90
90
|
* If you are looking for a `notWith` (which should be symmetric), this is your property.
|
|
91
91
|
*
|
|
92
|
-
* @property {string} [xsnOp]
|
|
93
|
-
* Defines the operator to be used for XSN. Used for SET and SELECT. See queryTerm().
|
|
94
|
-
*
|
|
95
92
|
* @property {string} [vZeroFor]
|
|
96
93
|
* Marks the property as a CSN 0.1.0 property. It is replaced by this CSN 1.0
|
|
97
94
|
* property (value of vZeroFor).
|
|
@@ -124,7 +121,7 @@ const { isAnnotationExpression } = require('../base/builtins');
|
|
|
124
121
|
const { CompilerAssertion } = require('../base/error');
|
|
125
122
|
const { Location } = require('../base/location');
|
|
126
123
|
const { XsnSource } = require('../compiler/xsn-model');
|
|
127
|
-
const {
|
|
124
|
+
const { xsnAsTree, splitClauses } = require('../parsers/XprTree');
|
|
128
125
|
|
|
129
126
|
const $location = Symbol.for('cds.$location');
|
|
130
127
|
|
|
@@ -202,7 +199,8 @@ const schemaClasses = {
|
|
|
202
199
|
defaultKind: '$column',
|
|
203
200
|
validKinds: [], // pseudo kind '$column'
|
|
204
201
|
// A column with only as+cast.type is a new association
|
|
205
|
-
requires: [ 'ref', 'cast', 'xpr', 'val', '#', 'func', 'list',
|
|
202
|
+
requires: [ 'ref', 'cast', 'xpr', 'val', '#', 'func', 'list',
|
|
203
|
+
'SELECT', 'SET', 'expand', 'virtual' ],
|
|
206
204
|
schema: {
|
|
207
205
|
xpr: {
|
|
208
206
|
class: 'condition',
|
|
@@ -211,6 +209,13 @@ const schemaClasses = {
|
|
|
211
209
|
inKind: [ '$column' ],
|
|
212
210
|
inValue: true,
|
|
213
211
|
},
|
|
212
|
+
cast: { // CDL-style type cast
|
|
213
|
+
// see “global” `cast` schema for SQL `cast` function
|
|
214
|
+
type: embed,
|
|
215
|
+
inValue: false,
|
|
216
|
+
optional: typeProperties,
|
|
217
|
+
inKind: [ '$column' ],
|
|
218
|
+
},
|
|
214
219
|
'=': {
|
|
215
220
|
// by not setting `vZeroFor`, we disallow `=` in `columns`.
|
|
216
221
|
// CSN v0.1 didn't have columns, so this isn't breaking v0.1 compatibility.
|
|
@@ -524,7 +529,6 @@ const schema = compileSchema( {
|
|
|
524
529
|
},
|
|
525
530
|
projection: {
|
|
526
531
|
type: queryTerm,
|
|
527
|
-
xsnOp: 'SELECT',
|
|
528
532
|
requires: 'from',
|
|
529
533
|
optional: [
|
|
530
534
|
'from', 'all', 'distinct', 'columns', 'excluding', // no 'mixin'
|
|
@@ -534,7 +538,6 @@ const schema = compileSchema( {
|
|
|
534
538
|
},
|
|
535
539
|
SELECT: {
|
|
536
540
|
type: queryTerm,
|
|
537
|
-
xsnOp: 'SELECT',
|
|
538
541
|
requires: 'from',
|
|
539
542
|
optional: [
|
|
540
543
|
'from', 'mixin', 'all', 'distinct', 'columns', 'excluding',
|
|
@@ -553,15 +556,14 @@ const schema = compileSchema( {
|
|
|
553
556
|
},
|
|
554
557
|
},
|
|
555
558
|
SET: {
|
|
556
|
-
type:
|
|
557
|
-
xsnOp: '$query', // might be overwritten by 'op'
|
|
559
|
+
type: querySet,
|
|
558
560
|
requires: 'args',
|
|
559
561
|
optional: [ 'op', 'all', 'distinct', 'args', 'orderBy', 'limit' ],
|
|
560
562
|
schema: {
|
|
561
563
|
args: {
|
|
562
564
|
arrayOf: embed, // like query
|
|
563
565
|
type: queryArgs,
|
|
564
|
-
minLength: 1,
|
|
566
|
+
minLength: csn => (csn.op ? 2 : 1),
|
|
565
567
|
optional: [ 'SELECT', 'SET' ],
|
|
566
568
|
},
|
|
567
569
|
},
|
|
@@ -692,16 +694,12 @@ const schema = compileSchema( {
|
|
|
692
694
|
type: boolOrNull,
|
|
693
695
|
inKind: [ 'element', '$column' ],
|
|
694
696
|
},
|
|
695
|
-
cast: {
|
|
697
|
+
cast: { // SQL `cast` function
|
|
698
|
+
// see `cast` sub schema inside `columns` for CDL-style type cast
|
|
696
699
|
type: embed,
|
|
697
|
-
//
|
|
698
|
-
//
|
|
699
|
-
|
|
700
|
-
// Because of (1) we have to set this property to false.
|
|
701
|
-
inValue: false,
|
|
702
|
-
optional: typeProperties,
|
|
703
|
-
// TODO v6: only in CDL-style cast, otherwise just length,…
|
|
704
|
-
inKind: [ '$column' ],
|
|
700
|
+
// see also call of eventualCast() for every (expression) object
|
|
701
|
+
inValue: false, // should have no relevance
|
|
702
|
+
optional: [ 'type', 'length', 'precision', 'scale', 'srid' ],
|
|
705
703
|
},
|
|
706
704
|
default: {
|
|
707
705
|
class: 'expression',
|
|
@@ -888,13 +886,15 @@ function arrayOf( fn, filter = undefined ) {
|
|
|
888
886
|
++virtualLine;
|
|
889
887
|
return fn( v, spec, xsn, csn ) || { location: location() };
|
|
890
888
|
} );
|
|
891
|
-
|
|
889
|
+
if (val.length)
|
|
890
|
+
++virtualLine; // [] in one JSON line
|
|
891
|
+
const minLength = (typeof spec.minLength === 'function')
|
|
892
|
+
? spec.minLength( csn )
|
|
893
|
+
: spec.minLength || 0;
|
|
892
894
|
if (minLength > val.length) {
|
|
893
895
|
error( 'syntax-incomplete-array', location(true),
|
|
894
896
|
{ prop: spec.prop, n: minLength, '#': minLength === 1 ? 'one' : 'std' });
|
|
895
897
|
}
|
|
896
|
-
if (val.length)
|
|
897
|
-
++virtualLine; // [] in one JSON line
|
|
898
898
|
if (filter)
|
|
899
899
|
return r.filter(filter);
|
|
900
900
|
return r;
|
|
@@ -1581,7 +1581,9 @@ function xpr( exprs, spec, xsn, csn ) {
|
|
|
1581
1581
|
}
|
|
1582
1582
|
xsn.suffix = exprArgs( exprs, spec );
|
|
1583
1583
|
if (exprs.length > 2)
|
|
1584
|
-
xsn.suffix =
|
|
1584
|
+
xsn.suffix = xsnAsTree( exprs, xsn.suffix, xsn.op.location ).args;
|
|
1585
|
+
else if (exprs.length === 2 && exprs[0] === 'over' && exprs[1]?.xpr && !exprs[1].func)
|
|
1586
|
+
xsn.suffix[1].args = splitClauses( xsn.suffix[1].args );
|
|
1585
1587
|
}
|
|
1586
1588
|
else {
|
|
1587
1589
|
// setting $parens here would not always be correct; thus, keep distinction
|
|
@@ -1589,7 +1591,7 @@ function xpr( exprs, spec, xsn, csn ) {
|
|
|
1589
1591
|
xsn.op = { val: 'xpr', location: location() };
|
|
1590
1592
|
xsn.args = exprArgs( exprs, spec );
|
|
1591
1593
|
if (exprs.length > 2)
|
|
1592
|
-
xsn.args =
|
|
1594
|
+
xsn.args = xsnAsTree( exprs, xsn.args, xsn.op.location ).args;
|
|
1593
1595
|
}
|
|
1594
1596
|
}
|
|
1595
1597
|
|
|
@@ -1612,7 +1614,13 @@ function xprInValue( exprs, spec, xsn, csn ) {
|
|
|
1612
1614
|
|
|
1613
1615
|
function args( exprs, spec ) {
|
|
1614
1616
|
if (Array.isArray( exprs )) {
|
|
1615
|
-
|
|
1617
|
+
const xsn = arrayOf( exprOrString )( exprs, spec );
|
|
1618
|
+
if (xsn.length) {
|
|
1619
|
+
const last = xsn.at( -1 );
|
|
1620
|
+
if (last?.op?.val === 'xpr' && last.args.length > 4) // `order by` in last arg?
|
|
1621
|
+
last.args = splitClauses( last.args );
|
|
1622
|
+
}
|
|
1623
|
+
return xsn;
|
|
1616
1624
|
}
|
|
1617
1625
|
else if (!exprs || typeof exprs !== 'object') {
|
|
1618
1626
|
error( 'syntax-expecting-args', location(true),
|
|
@@ -1641,7 +1649,7 @@ function expr( e, spec ) {
|
|
|
1641
1649
|
const xsn = exprArgs( e, spec );
|
|
1642
1650
|
return (e.length < 3) // optimization
|
|
1643
1651
|
? { op: { val: 'ixpr', location: loc }, args: xsn, location: loc }
|
|
1644
|
-
:
|
|
1652
|
+
: xsnAsTree( e, xsn, loc );
|
|
1645
1653
|
}
|
|
1646
1654
|
else if (e.length === 1) { // CSN v.0.1.0 way for parentheses
|
|
1647
1655
|
const loc = location();
|
|
@@ -1693,33 +1701,54 @@ function condition( cond, spec ) {
|
|
|
1693
1701
|
const xsn = exprArgs( cond, spec );
|
|
1694
1702
|
// TODO sql-like backends: with the commented `return`, test3 generated sql
|
|
1695
1703
|
// files will not have the unnecessary `(…)` around `on` anymore → extra PR
|
|
1696
|
-
const tree = (cond.length < 3) ? xsn :
|
|
1704
|
+
const tree = (cond.length < 3) ? xsn : xsnAsTree( cond, xsn, loc ).args;
|
|
1697
1705
|
return { op: { val: 'xpr', location: loc }, args: tree, location: loc };
|
|
1698
1706
|
// return (cond.length < 3) // optimization
|
|
1699
1707
|
// ? { op: { val: 'ixpr', location: loc }, args: xsn, location: loc }
|
|
1700
|
-
// :
|
|
1708
|
+
// : xsnAsTree( cond, xsn, loc );
|
|
1701
1709
|
}
|
|
1702
1710
|
|
|
1703
1711
|
// Queries (std signature) ---------------------------------------------------
|
|
1704
1712
|
|
|
1705
|
-
function queryTerm( term, spec, xsn ) { // for CSN properties 'SELECT' and '
|
|
1713
|
+
function queryTerm( term, spec, xsn ) { // for CSN properties 'SELECT' and 'projection'
|
|
1706
1714
|
// TODO: re-check $location: pushLocation( term ) / popLocation( term )
|
|
1707
1715
|
xsn.query = object( term, spec );
|
|
1708
1716
|
if (!xsn.query)
|
|
1709
1717
|
return;
|
|
1710
|
-
// XSN TODO:
|
|
1711
|
-
if (!xsn.query.
|
|
1712
|
-
xsn.query.
|
|
1713
|
-
val: (spec.prop !== 'SET' ? 'SELECT' : '$query'),
|
|
1714
|
-
location: location(), // XSN TODO: work without location everywhere
|
|
1715
|
-
};
|
|
1716
|
-
}
|
|
1717
|
-
if (spec.prop !== 'SET' && !xsn.query.from)
|
|
1718
|
-
xsn.query.from = null; // make it clear that SELECT is used with parse error
|
|
1718
|
+
xsn.query.op = { val: 'SELECT', location: location() }; // XSN TODO: without location
|
|
1719
|
+
if (!xsn.query.from)
|
|
1720
|
+
xsn.query.from = null; // make it clear that SELECT is used with parse error
|
|
1719
1721
|
if (spec.prop === 'projection')
|
|
1720
1722
|
xsn.$syntax = 'projection';
|
|
1721
1723
|
}
|
|
1722
1724
|
|
|
1725
|
+
function querySet( term, spec, xsn ) { // for CSN property 'SET'
|
|
1726
|
+
// TODO: re-check $location: pushLocation( term ) / popLocation( term )
|
|
1727
|
+
xsn.query = object( term, spec );
|
|
1728
|
+
if (!xsn.query || xsn.query.op) // TODO: check for args.length === 1 ?
|
|
1729
|
+
return;
|
|
1730
|
+
const setArgs = xsn.query.args;
|
|
1731
|
+
if (setArgs?.length !== 1)
|
|
1732
|
+
return;
|
|
1733
|
+
const { orderBy, limit } = xsn.query;
|
|
1734
|
+
xsn.query = setArgs[0];
|
|
1735
|
+
if (!orderBy && !limit)
|
|
1736
|
+
return;
|
|
1737
|
+
if (xsn.query.orderBy || xsn.query.limit) {
|
|
1738
|
+
// giving this error at the exact location would be far more work → not worth
|
|
1739
|
+
error( 'syntax-duplicate-clause', location(true), {
|
|
1740
|
+
'#': 'setForCsn',
|
|
1741
|
+
prop: 'orderBy',
|
|
1742
|
+
siblingprop: 'limit',
|
|
1743
|
+
parentprop: 'args[]',
|
|
1744
|
+
} );
|
|
1745
|
+
}
|
|
1746
|
+
else {
|
|
1747
|
+
xsn.query.orderBy = orderBy;
|
|
1748
|
+
xsn.query.limit = limit;
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1723
1752
|
function asQuantifier( quantifier, spec, xsn ) {
|
|
1724
1753
|
if (quantifier)
|
|
1725
1754
|
xsn.quantifier = { val: spec.prop, location: location() };
|
|
@@ -1766,13 +1795,11 @@ function join( val, spec, xsn ) {
|
|
|
1766
1795
|
|
|
1767
1796
|
function queryArgs( val, spec, xsn, csn ) {
|
|
1768
1797
|
if (Array.isArray( val ) && val.length > 1 && !csn.op) {
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
{ siblingprop: 'args', prop: 'op' },
|
|
1772
|
-
'Object with property $(SIBLINGPROP) must also have a property $(PROP)' );
|
|
1798
|
+
error( 'syntax-missing-property', location(true),
|
|
1799
|
+
{ '#': 'sibling', siblingprop: 'args', prop: 'op' } );
|
|
1773
1800
|
xsn.op = { val: 'union', location: location() };
|
|
1774
1801
|
}
|
|
1775
|
-
return arrayOf( object )( val, spec ).map( q => q.query );
|
|
1802
|
+
return arrayOf( object )( val, spec, xsn, csn ).map( q => q.query );
|
|
1776
1803
|
}
|
|
1777
1804
|
|
|
1778
1805
|
// i18n ------------------------------
|
package/lib/json/to-csn.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
'use strict';
|
|
13
13
|
|
|
14
14
|
const { locationString } = require('../base/messages');
|
|
15
|
-
const { isBetaEnabled
|
|
15
|
+
const { isBetaEnabled } = require('../base/model');
|
|
16
16
|
const { pathName } = require('../compiler/utils');
|
|
17
17
|
const { CompilerAssertion } = require('../base/error');
|
|
18
18
|
|
|
@@ -31,7 +31,6 @@ const normalizedKind = {
|
|
|
31
31
|
let gensrcFlavor = true; // good enough here...
|
|
32
32
|
let universalCsn = false;
|
|
33
33
|
let strictMode = false; // whether to dump with unknown properties (in standard)
|
|
34
|
-
let projectionAsQuery = false;
|
|
35
34
|
let withLocations = false;
|
|
36
35
|
let withDocComments = false;
|
|
37
36
|
let structXpr = false;
|
|
@@ -580,8 +579,6 @@ function dollarSyntax( node, csn ) {
|
|
|
580
579
|
// eslint-disable-next-line no-prototype-builtins
|
|
581
580
|
if (specialDollarValues.hasOwnProperty( node ))
|
|
582
581
|
return specialDollarValues[node];
|
|
583
|
-
if (projectionAsQuery)
|
|
584
|
-
return node;
|
|
585
582
|
setHidden( csn, '$syntax', node );
|
|
586
583
|
return undefined;
|
|
587
584
|
}
|
|
@@ -650,7 +647,9 @@ function dictionary( dict, keys, prop ) {
|
|
|
650
647
|
}
|
|
651
648
|
|
|
652
649
|
function foreignKeys( dict, csn, node ) {
|
|
653
|
-
if (
|
|
650
|
+
if (!dict || // `Association to many Target` without specified keys
|
|
651
|
+
universalCsn && dict[$inferred] === 'keys' ||
|
|
652
|
+
!target( node.target, csn, node ) )
|
|
654
653
|
return;
|
|
655
654
|
|
|
656
655
|
if (gensrcFlavor) {
|
|
@@ -1286,7 +1285,7 @@ function exprInternal( node, xprParens ) {
|
|
|
1286
1285
|
|
|
1287
1286
|
const naryOperators = {
|
|
1288
1287
|
__proto__: null,
|
|
1289
|
-
|
|
1288
|
+
'.': true,
|
|
1290
1289
|
'*': true,
|
|
1291
1290
|
'/': true,
|
|
1292
1291
|
'+': true,
|
|
@@ -1334,7 +1333,7 @@ function ternaryOperator( node ) {
|
|
|
1334
1333
|
function query( node, csn, xsn, _prop, expectedParens = 0 ) {
|
|
1335
1334
|
if (node.op.val === 'SELECT') {
|
|
1336
1335
|
if (xsn && xsn.query === node && xsn.$syntax === 'projection' &&
|
|
1337
|
-
node.from && node.from.path
|
|
1336
|
+
node.from && node.from.path) {
|
|
1338
1337
|
csn.projection = addLocation( node.location, standard( node ) );
|
|
1339
1338
|
return undefined;
|
|
1340
1339
|
}
|
|
@@ -1560,7 +1559,6 @@ function initModuleVars( options = { csnFlavor: 'gensrc' } ) {
|
|
|
1560
1559
|
withLocations = options.withLocations;
|
|
1561
1560
|
withDocComments = options.docComment !== false;
|
|
1562
1561
|
structXpr = options.structXpr;
|
|
1563
|
-
projectionAsQuery = isDeprecatedEnabled( options, '_projectionAsQuery' );
|
|
1564
1562
|
}
|
|
1565
1563
|
|
|
1566
1564
|
module.exports = {
|
|
@@ -71,8 +71,8 @@ function stripIndentation( str ) {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
class MultiLineStringParser {
|
|
74
|
-
constructor(
|
|
75
|
-
this.parser =
|
|
74
|
+
constructor(cdsParser, token) {
|
|
75
|
+
this.parser = cdsParser; // for message functions
|
|
76
76
|
this.token = token;
|
|
77
77
|
this.str = token.text; // Copy because .text is a getter
|
|
78
78
|
|
|
@@ -377,6 +377,7 @@ class MultiLineStringParser {
|
|
|
377
377
|
*/
|
|
378
378
|
_matchLineBreakAtCurrentChar() {
|
|
379
379
|
// Only increase line number for \n, because ANTLR does the same
|
|
380
|
+
// TODO: Is this still the case with redepage?
|
|
380
381
|
switch (this._current()) {
|
|
381
382
|
case '\r':
|
|
382
383
|
if (this._lookahead() === '\n') {
|
package/lib/main.d.ts
CHANGED
|
@@ -126,6 +126,18 @@ declare namespace compiler {
|
|
|
126
126
|
* The CDL equivalent of the CSN value `doc: null`, is an empty doc comment.
|
|
127
127
|
*/
|
|
128
128
|
docComment?: boolean
|
|
129
|
+
/**
|
|
130
|
+
* If this option is `true`, doc comments are propagated just like annotations.
|
|
131
|
+
* If `false`, they won't be propagated at all.
|
|
132
|
+
*
|
|
133
|
+
* See option 'docComment'.
|
|
134
|
+
*
|
|
135
|
+
* Until @sap/cds-compiler v6.0, doc comments were always propagated.
|
|
136
|
+
*
|
|
137
|
+
* @default `false`
|
|
138
|
+
* @since v6.0
|
|
139
|
+
*/
|
|
140
|
+
propagateDocComments?: boolean
|
|
129
141
|
/**
|
|
130
142
|
* When set to `true`, and the model contains an entity `sap.common.Languages`
|
|
131
143
|
* with an element `code`, all generated texts entities additionally contain
|
|
@@ -135,15 +147,6 @@ declare namespace compiler {
|
|
|
135
147
|
* @since v2.8.0
|
|
136
148
|
*/
|
|
137
149
|
addTextsLanguageAssoc?: boolean
|
|
138
|
-
/**
|
|
139
|
-
* If set to `true`, add named aspects to the list of 'includes' of the generated entity.
|
|
140
|
-
* This will cause propagation of actions from aspect to generated entity.
|
|
141
|
-
*
|
|
142
|
-
* Will become default in v6.
|
|
143
|
-
*
|
|
144
|
-
* @since v5.9.0
|
|
145
|
-
*/
|
|
146
|
-
compositionIncludes?: boolean
|
|
147
150
|
/**
|
|
148
151
|
* An array of directory names that are used for CDS module lookups.
|
|
149
152
|
* Lookup directory `node_modules/` is appended if not set explicitly.
|
|
@@ -179,12 +182,6 @@ declare namespace compiler {
|
|
|
179
182
|
* of the _name_ of the artifact.
|
|
180
183
|
*/
|
|
181
184
|
withLocations?: boolean|string
|
|
182
|
-
/**
|
|
183
|
-
* Use the new non-ANTLR based parser for compilation.
|
|
184
|
-
*
|
|
185
|
-
* @since v5.2.0
|
|
186
|
-
*/
|
|
187
|
-
newParser?: boolean
|
|
188
185
|
/**
|
|
189
186
|
* Internal option for LSP only!
|
|
190
187
|
* If set, each AST gets a `tokenStream` property containing all lexed tokens.
|
|
@@ -441,7 +438,7 @@ declare namespace compiler {
|
|
|
441
438
|
* Only works with sqlDialect 'sqlite'! Otherwise, it has no effect.
|
|
442
439
|
*
|
|
443
440
|
* @since 3.9.0
|
|
444
|
-
* @
|
|
441
|
+
* @default true (since v5)
|
|
445
442
|
*/
|
|
446
443
|
betterSqliteSessionVariables?: boolean
|
|
447
444
|
/**
|
|
@@ -454,12 +451,33 @@ declare namespace compiler {
|
|
|
454
451
|
* three-valued logic, on databases that support it.
|
|
455
452
|
* - `==` is still treated as two-valued logic operator.
|
|
456
453
|
*
|
|
457
|
-
* This option
|
|
454
|
+
* This option is the default since cds-compiler v6.
|
|
458
455
|
*
|
|
459
456
|
* @since 5.9.0
|
|
460
|
-
* @default
|
|
457
|
+
* @default true (since v6.0)
|
|
461
458
|
*/
|
|
462
459
|
booleanEquality?: boolean
|
|
460
|
+
/**
|
|
461
|
+
* If set to true, a specified set of functions - inspired by OData and SAP HANA -
|
|
462
|
+
* are translated to database-specific variants.
|
|
463
|
+
*
|
|
464
|
+
* See <https://cap.cloud.sap/docs/guides/databases>
|
|
465
|
+
*
|
|
466
|
+
* @since 5.9.2
|
|
467
|
+
* @default true (since v6.0)
|
|
468
|
+
*/
|
|
469
|
+
standardDatabaseFunctions?: boolean
|
|
470
|
+
/**
|
|
471
|
+
* If set to true, variable `$now` will be rendered as 'CURRENT_TIMESTAMP' in
|
|
472
|
+
* SQL, instead of `SESSION_CONTEXT('$now')` (or an equivalent clause in other SQL dialects).
|
|
473
|
+
*
|
|
474
|
+
* In cds-compiler <6, `$now` was always rendered as `CURRENT_TIMESTAMP`.
|
|
475
|
+
* Since v6, it is a session variable, except for DEFAULT clauses.
|
|
476
|
+
*
|
|
477
|
+
* @since 6.0
|
|
478
|
+
* @default false
|
|
479
|
+
*/
|
|
480
|
+
dollarNowAsTimestamp?: boolean
|
|
463
481
|
}
|
|
464
482
|
|
|
465
483
|
/**
|
|
@@ -536,6 +554,17 @@ declare namespace compiler {
|
|
|
536
554
|
* @default false
|
|
537
555
|
*/
|
|
538
556
|
allowCsnDowngrade?: boolean
|
|
557
|
+
/**
|
|
558
|
+
* If set to true, variable `$now` will be rendered as 'CURRENT_TIMESTAMP'
|
|
559
|
+
* instead of `SESSION_CONTEXT('$now')`.
|
|
560
|
+
*
|
|
561
|
+
* In cds-compiler <6, `$now` was always rendered as `CURRENT_TIMESTAMP`.
|
|
562
|
+
* Since v6, it is a session variable, except for DEFAULT clauses.
|
|
563
|
+
*
|
|
564
|
+
* @since 6.0
|
|
565
|
+
* @default false
|
|
566
|
+
*/
|
|
567
|
+
dollarNowAsTimestamp?: boolean
|
|
539
568
|
}
|
|
540
569
|
|
|
541
570
|
/**
|
|
@@ -579,7 +608,7 @@ declare namespace compiler {
|
|
|
579
608
|
* entity S.S.Base as projection on S.Base as Base;
|
|
580
609
|
* ```
|
|
581
610
|
*
|
|
582
|
-
* @default
|
|
611
|
+
* @default true (since v6)
|
|
583
612
|
*/
|
|
584
613
|
renderCdlDefinitionNesting?: boolean
|
|
585
614
|
/**
|
|
@@ -587,6 +616,8 @@ declare namespace compiler {
|
|
|
587
616
|
* as a `namespace` statement.
|
|
588
617
|
*
|
|
589
618
|
* Only usable in combination with `renderCdlDefinitionNesting: true`.
|
|
619
|
+
*
|
|
620
|
+
* @default true (since v6)
|
|
590
621
|
*/
|
|
591
622
|
renderCdlCommonNamespace?: boolean
|
|
592
623
|
}
|
|
@@ -1257,6 +1288,9 @@ declare namespace compiler {
|
|
|
1257
1288
|
/**
|
|
1258
1289
|
* Renders the given CSN into HDBCDS artifacts.
|
|
1259
1290
|
*
|
|
1291
|
+
* DEPRECATED! This backend is deprecated and will be removed with cds-compiler v7!
|
|
1292
|
+
*
|
|
1293
|
+
* @deprecated
|
|
1260
1294
|
* @returns A map of `{ '<artifactName>.hdbcds|hdbconstraint>': '<content>' }` entries.
|
|
1261
1295
|
*/
|
|
1262
1296
|
function hdbcds(csn: CSN, options?: HdbcdsOptions): Record<string, string>;
|
|
@@ -1264,6 +1298,8 @@ declare namespace compiler {
|
|
|
1264
1298
|
/**
|
|
1265
1299
|
* Immutable list of SAP HANA CDS keywords, used for smart quoting in
|
|
1266
1300
|
* {@link to.hdbcds} with option `sqlMapping: 'plain'`.
|
|
1301
|
+
*
|
|
1302
|
+
* @deprecated
|
|
1267
1303
|
*/
|
|
1268
1304
|
const keywords: string[];
|
|
1269
1305
|
}
|
|
@@ -1304,11 +1340,6 @@ declare namespace compiler {
|
|
|
1304
1340
|
* Rendered model, including extensions.
|
|
1305
1341
|
*/
|
|
1306
1342
|
model?: string
|
|
1307
|
-
/**
|
|
1308
|
-
* Rendered `csn.namespace` property + using directives.
|
|
1309
|
-
* Useful to keep the `csn.namespace` property when re-compiling the to.cdl() result.
|
|
1310
|
-
*/
|
|
1311
|
-
namespace?: string
|
|
1312
1343
|
}
|
|
1313
1344
|
|
|
1314
1345
|
/**
|
|
@@ -1581,6 +1612,9 @@ declare namespace compiler {
|
|
|
1581
1612
|
* Columns and lines are 1-based, i.e. value `0` is an invalid value and
|
|
1582
1613
|
* indicates absence of the property.
|
|
1583
1614
|
*
|
|
1615
|
+
* Note that the columns are UTF-16 _code unit_ offsets in a line.
|
|
1616
|
+
* A column is neither the number of _graphemes_ nor _code points_!
|
|
1617
|
+
*
|
|
1584
1618
|
* All properties are optional, even `file`.
|
|
1585
1619
|
*/
|
|
1586
1620
|
export type Location = {
|
package/lib/model/csnUtils.js
CHANGED
|
@@ -63,7 +63,6 @@ function getUtils( model, universalReady ) {
|
|
|
63
63
|
getContextOfArtifact,
|
|
64
64
|
addStringAnnotationTo,
|
|
65
65
|
getServiceName,
|
|
66
|
-
hasAnnotationValue,
|
|
67
66
|
getFinalTypeInfo,
|
|
68
67
|
get$combined,
|
|
69
68
|
getQueryPrimarySource,
|
|
@@ -238,7 +237,8 @@ function getUtils( model, universalReady ) {
|
|
|
238
237
|
// Return true if 'node' is a managed association element
|
|
239
238
|
// TODO: what about elements having a type, which (finally) is an assoc?
|
|
240
239
|
function isManagedAssociation( node ) {
|
|
241
|
-
|
|
240
|
+
// Since v6, managed to-many associations don't get any keys, hence don't require it.
|
|
241
|
+
return node.target !== undefined && node.on === undefined;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
/**
|
|
@@ -631,34 +631,15 @@ function traverseFrom( from, queryCallback, path = [] ) {
|
|
|
631
631
|
}
|
|
632
632
|
}
|
|
633
633
|
|
|
634
|
-
|
|
635
634
|
/**
|
|
636
|
-
*
|
|
637
|
-
*
|
|
638
|
-
* | Expected
|
|
639
|
-
* | true | false | null | arb val
|
|
640
|
-
* Anno Val |-------|-------|-------|--------
|
|
641
|
-
* true | true | false | false | false
|
|
642
|
-
* false | false | true | false | false
|
|
643
|
-
* null | false | true | true | false
|
|
644
|
-
* arb val | false | false | false | true/false
|
|
635
|
+
* Returns true if the artifact should be skipped during persistence.
|
|
636
|
+
* Respects special value `if-unused` by Node runtime.
|
|
645
637
|
*
|
|
646
|
-
*
|
|
647
|
-
* 'null' and 'false'. Expecting 'null' for an annotation value 'false' returns
|
|
648
|
-
* 'false'.
|
|
649
|
-
*
|
|
650
|
-
* @param {CSN.Artifact} artifact
|
|
651
|
-
* @param {string} annotationName Name of the annotation (including the at-sign)
|
|
652
|
-
* @param {any} expected
|
|
653
|
-
* @param {boolean} caseInsensitive
|
|
638
|
+
* @param {CSN.Artifact} art
|
|
654
639
|
* @returns {boolean}
|
|
655
640
|
*/
|
|
656
|
-
function
|
|
657
|
-
|
|
658
|
-
return artifact[annotationName] === expected || artifact[annotationName] === null;
|
|
659
|
-
else if (typeof artifact[annotationName] === 'string' && caseInsensitive === true)
|
|
660
|
-
return artifact[annotationName].toLowerCase() === expected.toLowerCase();
|
|
661
|
-
return artifact[annotationName] === expected;
|
|
641
|
+
function hasPersistenceSkipAnnotation( art ) {
|
|
642
|
+
return art['@cds.persistence.skip'] && art['@cds.persistence.skip'] !== 'if-unused';
|
|
662
643
|
}
|
|
663
644
|
|
|
664
645
|
/**
|
|
@@ -682,8 +663,7 @@ function isEdmPropertyRendered( elementCsn, options ) {
|
|
|
682
663
|
const isNotIgnored = !elementCsn.target ? !elementCsn['@cds.api.ignore'] : true;
|
|
683
664
|
const isNavigable = elementCsn.target
|
|
684
665
|
? (elementCsn['@odata.navigable'] === undefined ||
|
|
685
|
-
|
|
686
|
-
(elementCsn['@odata.navigable'] === null || elementCsn['@odata.navigable'] === true)) : true;
|
|
666
|
+
(elementCsn['@odata.navigable'] === null || !!elementCsn['@odata.navigable'])) : true;
|
|
687
667
|
// Foreign Keys can be ignored
|
|
688
668
|
if (elementCsn['@odata.foreignKey4'])
|
|
689
669
|
return isNotIgnored && renderForeignKey;
|
|
@@ -920,7 +900,7 @@ function setDependencies( csn, refs = csnRefs(csn) ) {
|
|
|
920
900
|
* @returns {boolean}
|
|
921
901
|
*/
|
|
922
902
|
function isPersistedOnDatabase( art ) {
|
|
923
|
-
return !(art.kind === 'entity' && (art.abstract ||
|
|
903
|
+
return !(art.kind === 'entity' && (art.abstract || hasPersistenceSkipAnnotation(art)));
|
|
924
904
|
}
|
|
925
905
|
/**
|
|
926
906
|
* Check if the given artifact will be persisted on the database via `CREATE VIEW`
|
|
@@ -932,9 +912,9 @@ function isPersistedAsView( artifact ) {
|
|
|
932
912
|
return artifact && artifact.kind === 'entity' &&
|
|
933
913
|
!artifact.$ignore &&
|
|
934
914
|
!artifact.abstract &&
|
|
935
|
-
((artifact.query || artifact.projection) && !
|
|
936
|
-
!
|
|
937
|
-
!
|
|
915
|
+
((artifact.query || artifact.projection) && !artifact['@cds.persistence.table']) &&
|
|
916
|
+
!hasPersistenceSkipAnnotation(artifact) &&
|
|
917
|
+
!artifact['@cds.persistence.exists'];
|
|
938
918
|
}
|
|
939
919
|
/**
|
|
940
920
|
* Check if the given artifact will be persisted on the database via `CREATE TABLE`
|
|
@@ -946,9 +926,9 @@ function isPersistedAsTable( artifact ) {
|
|
|
946
926
|
return artifact.kind === 'entity' &&
|
|
947
927
|
!artifact.$ignore &&
|
|
948
928
|
!artifact.abstract &&
|
|
949
|
-
(!artifact.query && !artifact.projection ||
|
|
950
|
-
!
|
|
951
|
-
!
|
|
929
|
+
(!artifact.query && !artifact.projection || artifact['@cds.persistence.table']) &&
|
|
930
|
+
!hasPersistenceSkipAnnotation(artifact) &&
|
|
931
|
+
!artifact['@cds.persistence.exists'];
|
|
952
932
|
}
|
|
953
933
|
|
|
954
934
|
/**
|
|
@@ -1127,8 +1107,9 @@ function moveAnnotationsAndDoc( sourceNode, targetNode, overwrite = false ) {
|
|
|
1127
1107
|
* filter: Positive filter. If it returns true, annotations for the referenced artifact
|
|
1128
1108
|
* will be applied.
|
|
1129
1109
|
* applyToElements: Whether to apply annotations to elements or only to artifacts
|
|
1110
|
+
* @param {Function} error The error function
|
|
1130
1111
|
*/
|
|
1131
|
-
function applyAnnotationsFromExtensions( csn, config ) {
|
|
1112
|
+
function applyAnnotationsFromExtensions( csn, config, error ) {
|
|
1132
1113
|
if (!csn.extensions)
|
|
1133
1114
|
return;
|
|
1134
1115
|
|
|
@@ -1137,6 +1118,8 @@ function applyAnnotationsFromExtensions( csn, config ) {
|
|
|
1137
1118
|
for (let i = 0; i < csn.extensions.length; ++i) {
|
|
1138
1119
|
const ext = csn.extensions[i];
|
|
1139
1120
|
const name = ext.annotate || ext.extend;
|
|
1121
|
+
if (isAnnotateDraftAdminDataWithTenantIndependent(ext))
|
|
1122
|
+
error(null, [ 'extensions', i ], { name }, '$(NAME) can\'t be tenant independent');
|
|
1140
1123
|
if (name && filter(name)) {
|
|
1141
1124
|
const def = csn.definitions[name];
|
|
1142
1125
|
if (def) {
|
|
@@ -1153,6 +1136,8 @@ function applyAnnotationsFromExtensions( csn, config ) {
|
|
|
1153
1136
|
}
|
|
1154
1137
|
|
|
1155
1138
|
csn.extensions = csn.extensions.filter(ext => ext);
|
|
1139
|
+
if (!csn.extensions.length)
|
|
1140
|
+
delete csn.extensions;
|
|
1156
1141
|
|
|
1157
1142
|
function applyAnnotationsToElements( ext, def ) {
|
|
1158
1143
|
// Only the definition is arrayed but the extension is not since
|
|
@@ -1177,6 +1162,10 @@ function applyAnnotationsFromExtensions( csn, config ) {
|
|
|
1177
1162
|
if (Object.keys(ext.elements).length === 0)
|
|
1178
1163
|
delete ext.elements;
|
|
1179
1164
|
}
|
|
1165
|
+
|
|
1166
|
+
function isAnnotateDraftAdminDataWithTenantIndependent( ext ) {
|
|
1167
|
+
return ext.annotate && ext.annotate.endsWith('.DraftAdministrativeData') && ext['@cds.tenant.independent'];
|
|
1168
|
+
}
|
|
1180
1169
|
}
|
|
1181
1170
|
|
|
1182
1171
|
/**
|
|
@@ -1187,8 +1176,8 @@ function applyAnnotationsFromExtensions( csn, config ) {
|
|
|
1187
1176
|
*/
|
|
1188
1177
|
function hasValidSkipOrExists( artifact ) {
|
|
1189
1178
|
return artifact.kind === 'entity' &&
|
|
1190
|
-
(
|
|
1191
|
-
|
|
1179
|
+
(artifact['@cds.persistence.exists'] ||
|
|
1180
|
+
hasPersistenceSkipAnnotation(artifact));
|
|
1192
1181
|
}
|
|
1193
1182
|
|
|
1194
1183
|
/**
|
|
@@ -1419,7 +1408,7 @@ module.exports = {
|
|
|
1419
1408
|
forEachMember,
|
|
1420
1409
|
forEachMemberRecursively,
|
|
1421
1410
|
forAllQueries,
|
|
1422
|
-
|
|
1411
|
+
hasPersistenceSkipAnnotation,
|
|
1423
1412
|
isEdmPropertyRendered,
|
|
1424
1413
|
getArtifactDatabaseNameOf,
|
|
1425
1414
|
getResultingName,
|