@sap/cds-compiler 2.10.4 → 2.11.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/bin/cdsc.js +42 -25
  3. package/bin/cdsse.js +1 -0
  4. package/doc/CHANGELOG_BETA.md +4 -0
  5. package/lib/api/.eslintrc.json +2 -0
  6. package/lib/api/main.js +9 -23
  7. package/lib/api/options.js +12 -4
  8. package/lib/api/validate.js +23 -2
  9. package/lib/backends.js +9 -8
  10. package/lib/base/dictionaries.js +2 -1
  11. package/lib/base/message-registry.js +10 -2
  12. package/lib/base/messages.js +23 -9
  13. package/lib/base/model.js +5 -4
  14. package/lib/base/optionProcessorHelper.js +56 -22
  15. package/lib/checks/selectItems.js +4 -0
  16. package/lib/checks/unknownMagic.js +6 -3
  17. package/lib/compiler/assert-consistency.js +7 -0
  18. package/lib/compiler/base.js +65 -0
  19. package/lib/compiler/builtins.js +28 -1
  20. package/lib/compiler/checks.js +2 -1
  21. package/lib/compiler/definer.js +58 -91
  22. package/lib/compiler/index.js +16 -4
  23. package/lib/compiler/propagator.js +5 -2
  24. package/lib/compiler/resolver.js +93 -34
  25. package/lib/compiler/shared.js +29 -202
  26. package/lib/compiler/utils.js +173 -0
  27. package/lib/edm/annotations/genericTranslation.js +1 -1
  28. package/lib/edm/csn2edm.js +3 -2
  29. package/lib/edm/edmPreprocessor.js +31 -36
  30. package/lib/edm/edmUtils.js +3 -3
  31. package/lib/gen/language.checksum +1 -1
  32. package/lib/gen/language.interp +17 -1
  33. package/lib/gen/language.tokens +79 -73
  34. package/lib/gen/languageLexer.interp +19 -1
  35. package/lib/gen/languageLexer.js +779 -731
  36. package/lib/gen/languageLexer.tokens +71 -65
  37. package/lib/gen/languageParser.js +4668 -4072
  38. package/lib/json/from-csn.js +10 -10
  39. package/lib/json/to-csn.js +169 -34
  40. package/lib/language/antlrParser.js +11 -0
  41. package/lib/language/genericAntlrParser.js +72 -14
  42. package/lib/language/language.g4 +73 -0
  43. package/lib/main.d.ts +136 -17
  44. package/lib/main.js +3 -1
  45. package/lib/model/api.js +2 -2
  46. package/lib/model/csnRefs.js +108 -31
  47. package/lib/model/csnUtils.js +63 -29
  48. package/lib/model/enrichCsn.js +36 -9
  49. package/lib/model/revealInternalProperties.js +20 -4
  50. package/lib/modelCompare/compare.js +2 -1
  51. package/lib/optionProcessor.js +29 -18
  52. package/lib/render/DuplicateChecker.js +1 -1
  53. package/lib/render/toCdl.js +9 -3
  54. package/lib/render/toHdbcds.js +16 -36
  55. package/lib/render/toSql.js +23 -5
  56. package/lib/transform/db/constraints.js +278 -119
  57. package/lib/transform/db/draft.js +3 -2
  58. package/lib/transform/db/expansion.js +6 -4
  59. package/lib/transform/db/flattening.js +17 -1
  60. package/lib/transform/db/transformExists.js +61 -2
  61. package/lib/transform/db/views.js +438 -0
  62. package/lib/transform/forHanaNew.js +56 -435
  63. package/lib/transform/forOdataNew.js +9 -2
  64. package/lib/transform/localized.js +2 -0
  65. package/lib/transform/transformUtilsNew.js +10 -0
  66. package/lib/transform/translateAssocsToJoins.js +5 -13
  67. package/lib/utils/file.js +5 -3
  68. package/lib/utils/term.js +65 -42
  69. package/lib/utils/timetrace.js +48 -26
  70. package/package.json +1 -1
@@ -88,7 +88,7 @@
88
88
  // Sub Phase 2 (initXYZ)
89
89
  // - set _parent, _main (later: _service?) links, and _block links of members
90
90
  // - add _subArtifacts dictionary and "namespace artifacts" for name resolution
91
- // - duplicate checks (TODO - currently via preProcessArtifact in definer)
91
+ // - duplicate checks
92
92
  // - structure checks ?
93
93
  // - annotation assignments
94
94
  // - POST: resolvePath() can be called for artifact references (if complete model)
@@ -101,9 +101,11 @@
101
101
  // to avoid consequential or repeated errors.
102
102
  // - But: The same artifact is added to multiple dictionaries.
103
103
  // - Solution part 1: $duplicates as property of the artifact or member
104
- // for 'definitions', '_artifacts' and member dictionaries.
105
- // - Solution part 2: array value in dictionary for duplicates in CDL 'artifacts'
106
- // dictionary, also used for `_combined` in query search dictionary.
104
+ // for `definitions`, `_artifacts`, member dictionaries, `vocabulary`
105
+ // dictionary of the whole model, `$tableAliases` dictionary of queries.
106
+ // - Solution part 2: array value in dictionary for duplicates in CDL `artifacts`
107
+ // dictionary, `_combined` dictionary for query search, `$tableAliases`
108
+ // of JOIN restrictions, `vocabulary` dictionary of a CDL input source.
107
109
 
108
110
  'use strict';
109
111
 
@@ -111,7 +113,7 @@ const { searchName, weakLocation } = require('../base/messages');
111
113
  const {
112
114
  isDeprecatedEnabled, isBetaEnabled,
113
115
  setProp, forEachGeneric, forEachInOrder,
114
- forEachMember, forEachDefinition,
116
+ forEachDefinition,
115
117
  forEachMemberRecursivelyWithQuery,
116
118
  } = require('../base/model');
117
119
  const {
@@ -120,20 +122,20 @@ const {
120
122
  const {
121
123
  dictLocation,
122
124
  } = require('../base/location');
125
+ const { kindProperties, dictKinds } = require('./base');
123
126
  const {
124
- annotationVal, annotationIsFalse, annotateWith,
125
- } = require('./utils');
126
- const {
127
- dictKinds,
128
- kindProperties,
129
- fns,
127
+ annotationVal,
128
+ annotationIsFalse,
129
+ annotateWith,
130
130
  linkToOrigin,
131
131
  setMemberParent,
132
132
  storeExtension,
133
133
  dependsOnSilent,
134
- } = require('./shared');
134
+ augmentPath,
135
+ splitIntoPath,
136
+ } = require('./utils');
135
137
  const { compareLayer, layer } = require('./moduleLayers');
136
- const { initBuiltins } = require('./builtins');
138
+ const { initBuiltins, isInReservedNamespace } = require('./builtins');
137
139
  const setLink = setProp;
138
140
 
139
141
  /**
@@ -148,7 +150,7 @@ const setLink = setProp;
148
150
  *
149
151
  * @param {XSN.Model} model Model with `sources` property that contain AST-like CSNs.
150
152
  */
151
- function getDefinerFunctions( model ) {
153
+ function define( model ) {
152
154
  const { options } = model;
153
155
  // Get simplified "resolve" functionality and the message function:
154
156
  const {
@@ -160,24 +162,29 @@ function getDefinerFunctions( model ) {
160
162
  resolveTypeArguments,
161
163
  defineAnnotations,
162
164
  attachAndEmitValidNames,
163
- } = fns( model );
164
- const extensionsDict = Object.create(null);
165
- let addTextsLanguageAssoc = false;
166
-
167
- return {
168
- define,
165
+ } = model.$functions;
166
+ Object.assign( model.$functions, {
169
167
  initArtifact,
170
168
  lateExtensions,
171
169
  projectionAncestor,
172
170
  hasTruthyProp,
171
+ } );
172
+ // During the definer, we can only resolve artifact references, i.e,
173
+ // after a `.`, we only search in the `_subArtifacts` dictionary:
174
+ model.$volatileFunctions.environment = function artifactsEnv( art ) {
175
+ return art._subArtifacts || Object.create(null);
173
176
  };
174
177
 
178
+ const extensionsDict = Object.create(null);
179
+ let addTextsLanguageAssoc = false;
180
+ return doDefine();
181
+
175
182
  /**
176
183
  * Main function of the definer.
177
184
  *
178
185
  * @returns {XSN.Model}
179
186
  */
180
- function define() {
187
+ function doDefine() {
181
188
  if (options.deprecated &&
182
189
  messages.every( m => m.messageId !== 'api-deprecated-option' )) {
183
190
  warning( 'api-deprecated-option', {},
@@ -206,8 +213,6 @@ function getDefinerFunctions( model ) {
206
213
 
207
214
  if (options.parseCdl) {
208
215
  initExtensionsWithoutApplying();
209
- // Check for redefinitions
210
- Object.keys( model.definitions ).forEach( preProcessArtifact );
211
216
  // If no extensions shall be applied then we can skip further
212
217
  // artifact processing and return the model with an `extensions` property.
213
218
  return model;
@@ -215,7 +220,6 @@ function getDefinerFunctions( model ) {
215
220
 
216
221
  applyExtensions();
217
222
 
218
- Object.keys( model.definitions ).forEach( preProcessArtifact );
219
223
  const commonLanguagesEntity // TODO: remove beta after a grace period
220
224
  = (options.addTextsLanguageAssoc || isBetaEnabled( options, 'addTextsLanguageAssoc' )) &&
221
225
  model.definitions['sap.common.Languages'];
@@ -238,13 +242,13 @@ function getDefinerFunctions( model ) {
238
242
  * @param {XSN.AST} src
239
243
  */
240
244
  function addSource( src ) {
241
- // handle sub model from CSN parser
245
+ // handle sub model from parser
242
246
  if (!src.kind)
243
247
  src.kind = 'source';
244
248
 
245
249
  let namespace = src.namespace && src.namespace.path;
246
250
  let prefix = namespace ? `${ pathName( namespace ) }.` : '';
247
- if (prefix.startsWith( 'cds.') && !prefix.match(/^cds\.foundation(\.|$)/)) {
251
+ if (isInReservedNamespace(prefix)) {
248
252
  error( 'reserved-namespace-cds', [ src.namespace.location, src.namespace ], {},
249
253
  // TODO: use $(NAME)
250
254
  'The namespace "cds" is reserved for CDS builtins' );
@@ -280,8 +284,7 @@ function getDefinerFunctions( model ) {
280
284
  function addDefinition( art, block ) {
281
285
  const { absolute } = art.name;
282
286
  // TODO: check reserved, see checkName()/checkLocalizedObjects() of checks.js
283
- if (absolute === 'cds' ||
284
- absolute.startsWith( 'cds.') && !absolute.match(/^cds\.foundation(\.|$)/)) {
287
+ if (absolute === 'cds' || isInReservedNamespace(absolute)) {
285
288
  error( 'reserved-namespace-cds', [ art.name.location, art ], {},
286
289
  // TODO: use $(NAME)
287
290
  'The namespace "cds" is reserved for CDS builtins' );
@@ -305,7 +308,7 @@ function getDefinerFunctions( model ) {
305
308
  }
306
309
  else {
307
310
  setLink( art, '_block', block );
308
- // dictAdd might set $duplicates to true if def in other source
311
+ // dictAdd might set $duplicates to true
309
312
  dictAdd( model.definitions, absolute, art );
310
313
  return true;
311
314
  }
@@ -444,6 +447,23 @@ function getDefinerFunctions( model ) {
444
447
 
445
448
  // Phase 2 ("init") --------------------------------------------------------
446
449
 
450
+ function checkRedefinition( art ) {
451
+ if (!art.$duplicates)
452
+ return;
453
+ if (art._main) {
454
+ error( 'duplicate-definition', [ art.name.location, art ], {
455
+ name: art.name.id,
456
+ '#': kindProperties[art.kind].normalized || art.kind,
457
+ } );
458
+ }
459
+ else {
460
+ error( 'duplicate-definition', [ art.name.location, art ], {
461
+ name: art.name.absolute,
462
+ '#': (art.kind === 'annotation' ? 'annotation' : 'absolute' ),
463
+ } );
464
+ }
465
+ }
466
+
447
467
  function initNamespaceAndUsing( src ) {
448
468
  if (src.namespace) {
449
469
  const decl = src.namespace;
@@ -488,6 +508,7 @@ function getDefinerFunctions( model ) {
488
508
  if (!reInit)
489
509
  initParentLink( art, model.definitions );
490
510
  const block = art._block;
511
+ checkRedefinition( art );
491
512
  defineAnnotations( art, art, block );
492
513
  initMembers( art, art, block );
493
514
  initDollarSelf( art ); // $self
@@ -513,6 +534,7 @@ function getDefinerFunctions( model ) {
513
534
 
514
535
  function initVocabulary( art ) {
515
536
  initParentLink( art, model.vocabularies );
537
+ checkRedefinition( art );
516
538
  const block = art._block;
517
539
  defineAnnotations( art, art, block );
518
540
  initMembers( art, art, block );
@@ -554,20 +576,6 @@ function getDefinerFunctions( model ) {
554
576
 
555
577
  // From here til EOF, reexamine code ---------------------------------------
556
578
 
557
- // currently called from preProcessArtifact(), do be called in "init"
558
- function checkRedefinitions( obj, name, prop ) {
559
- forEachMember( obj, checkRedefinitions, obj.targetAspect );
560
- if (!obj.$duplicates)
561
- return;
562
- if (obj.name.location.file === '<built-in>') {
563
- // builtin types like namespace 'cds' or namespace 'localized' shouldn't be printed.
564
- // The error shall only be printed for the user-defined conflicting artifact.
565
- return;
566
- }
567
- error( 'duplicate-definition', [ obj.name.location, obj ],
568
- { name, '#': (obj.kind === 'namespace') ? 'namespace' : dictKinds[prop] } );
569
- }
570
-
571
579
  function initDollarSelf( art ) {
572
580
  const selfname = '$self';
573
581
  // TODO: use setMemberParent() ?
@@ -774,12 +782,13 @@ function getDefinerFunctions( model ) {
774
782
  setMemberParent( table, table.name.id, query );
775
783
  setProp( table, '_block', query._block );
776
784
  dictAdd( query.$tableAliases, table.name.id, table, ( name, loc ) => {
777
- error( 'duplicate-definition', [ loc, table ], { name, '#': '$tableAlias' } );
785
+ error( 'duplicate-definition', [ loc, table ], { name, '#': 'alias' } );
778
786
  } );
779
787
  // also add to JOIN nodes for name restrictions:
780
788
  for (const p of joinParents) {
781
- // console.log('ADD:', query.name.id, parents.length, p)
782
- dictAdd( p.$tableAliases, table.name.id, table );
789
+ // for JOIN alias restriction, we cannot use $duplicates, as it is
790
+ // already used for duplicate aliases of queries:
791
+ dictAddArray( p.$tableAliases, table.name.id, table );
783
792
  }
784
793
  if (table.name.id[0] === '$') {
785
794
  warning( 'syntax-dollar-ident', [ table.name.location, table ], {
@@ -866,7 +875,7 @@ function getDefinerFunctions( model ) {
866
875
  // assignments on the mixin... (also for future mixin definitions
867
876
  // with generated values)
868
877
  dictAdd( query.$tableAliases, name, query.mixin[name], ( dupName, loc ) => {
869
- error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': '$tableAlias' } );
878
+ error( 'duplicate-definition', [ loc, query ], { name: dupName, '#': 'alias' } );
870
879
  } );
871
880
  if (mixin.name.id[0] === '$') {
872
881
  warning( 'syntax-dollar-ident', [ mixin.name.location, mixin ],
@@ -993,6 +1002,7 @@ function getDefinerFunctions( model ) {
993
1002
  setProp( elem, '_block', bl );
994
1003
  setMemberParent( elem, name, parent, construct !== parent && prop );
995
1004
  // console.log(message( null, elem.location, elem, {}, 'Info', 'INIT').toString())
1005
+ checkRedefinition( elem );
996
1006
  defineAnnotations( elem, elem, bl );
997
1007
  initMembers( elem, elem, bl, initExtensions );
998
1008
 
@@ -1958,24 +1968,6 @@ function getDefinerFunctions( model ) {
1958
1968
  }
1959
1969
  }
1960
1970
 
1961
- // TODO: move to "init" phase
1962
- /**
1963
- * Check whether redefinitions of the given artifact name exist and
1964
- * adapt to `targetAspect`.
1965
- *
1966
- * @param {string} name
1967
- */
1968
- function preProcessArtifact( name ) {
1969
- const art = model.definitions[name];
1970
- if (Array.isArray(art.$duplicates)) {
1971
- // A definition name containing a `.` is not invalid (TODO: starting or
1972
- // ending with a dot is invalid and could be checked here)
1973
- for (const a of art.$duplicates)
1974
- checkRedefinitions( a, name, 'definitions' );
1975
- }
1976
- checkRedefinitions( art, name, 'definitions' );
1977
- }
1978
-
1979
1971
  /**
1980
1972
  * Process "composition of" artifacts.
1981
1973
  *
@@ -2326,26 +2318,6 @@ function pathName(path) {
2326
2318
  return path.map( id => id.id ).join('.');
2327
2319
  }
2328
2320
 
2329
- /**
2330
- * Generates an XSN path out of the given name. Path segments are delimited by a dot.
2331
- * Each segment will have the given location assigned.
2332
- *
2333
- * @param {CSN.Location} location
2334
- * @param {string} name
2335
- * @returns {XSN.Path}
2336
- */
2337
- function splitIntoPath( location, name ) {
2338
- return name.split('.').map( id => ({ id, location }) );
2339
- }
2340
-
2341
- /**
2342
- * @param {CSN.Location} location
2343
- * @param {...any} args
2344
- */
2345
- function augmentPath( location, ...args ) {
2346
- return { path: args.map( id => ({ id, location }) ), location };
2347
- }
2348
-
2349
2321
  function augmentEqual( location, assocname, relations, prefix = '' ) {
2350
2322
  const args = relations.map( eq );
2351
2323
  return (args.length === 1)
@@ -2374,9 +2346,4 @@ function augmentEqual( location, assocname, relations, prefix = '' ) {
2374
2346
  // these function could be used to a future lib/compiler/utils.js, but DO NOT
2375
2347
  // SHARE with utility functions for CSN processors
2376
2348
 
2377
- module.exports = {
2378
- define: model => getDefinerFunctions( model ).define(),
2379
- getDefinerFunctions,
2380
- augmentPath,
2381
- splitIntoPath,
2382
- };
2349
+ module.exports = { define };
@@ -1,5 +1,17 @@
1
1
  // Main XSN-based compiler functions
2
2
 
3
+ // ...
4
+
5
+ // How functions are shared across the Core Compiler sub modules:
6
+
7
+ // - Shared XSN-related functions which do not use a context are in utils.js,
8
+ // they are `require`d as usual at the beginning of sub modules.
9
+ // - The XSN is the only context which context-dependent functions can depend on.
10
+ // - Sharing such a function is by adding it to `‹xsn›.$functions`,
11
+ // e.g. `resolvePath` and similar will be attached to the XSN.
12
+ // - Functions which might be overwritten in a next sub module
13
+ // are added to `‹xsn›.$volatileFunctions`, currently just `environment`.
14
+
3
15
  'use strict';
4
16
 
5
17
  const { resolveModule, resolveModuleSync } = require('../utils/moduleResolve');
@@ -8,6 +20,7 @@ const parseCsn = require('../json/from-csn');
8
20
 
9
21
  const assertConsistency = require('./assert-consistency');
10
22
  const moduleLayers = require('./moduleLayers');
23
+ const { fns } = require('./shared');
11
24
  const { define } = require('./definer');
12
25
  const resolve = require('./resolver');
13
26
  const propagator = require('./propagator');
@@ -110,7 +123,6 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
110
123
  // fileCache = Object.assign( Object.create(null), fileCache );
111
124
  dir = path.resolve(dir);
112
125
  const a = processFilenames( filenames, dir );
113
- a.fileContentDict = Object.create(null);
114
126
 
115
127
  const model = { sources: a.sources, options };
116
128
  model.$messageFunctions = createMessageFunctions( options, 'compile', model );
@@ -149,7 +161,6 @@ function compileX( filenames, dir = '', options = {}, fileCache = Object.create(
149
161
  }
150
162
  else {
151
163
  try {
152
- a.fileContentDict[filename] = source;
153
164
  const ast = parseX( source, rel, options, model.$messageFunctions );
154
165
  a.sources[filename] = ast;
155
166
  ast.location = { file: rel };
@@ -220,7 +231,6 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
220
231
  // absolute file names - they start with `/` or `\` or similar
221
232
  dir = path.resolve(dir);
222
233
  const a = processFilenames( filenames, dir );
223
- a.fileContentDict = Object.create(null);
224
234
 
225
235
  const model = { sources: a.sources, options };
226
236
  model.$messageFunctions = createMessageFunctions( options, 'compile', model );
@@ -281,7 +291,6 @@ function compileSyncX( filenames, dir = '', options = {}, fileCache = Object.cre
281
291
  }
282
292
  else {
283
293
  try {
284
- a.fileContentDict[filename] = source;
285
294
  const ast = parseX( source, rel, options, model.$messageFunctions );
286
295
  a.sources[filename] = ast;
287
296
  ast.location = { file: rel };
@@ -416,6 +425,9 @@ function compileDoX( model ) {
416
425
  throwWithError();
417
426
  return model;
418
427
  }
428
+ model.$functions = {};
429
+ model.$volatileFunctions = {};
430
+ fns( model ); // attach (mostly) paths functions
419
431
  define( model );
420
432
  // do not run the resolver in parse-cdl mode or we get duplicate annotations, etc.
421
433
  // TODO: do not use this function for parseCdl anyway…
@@ -8,7 +8,7 @@ const {
8
8
  setProp,
9
9
  isDeprecatedEnabled,
10
10
  } = require( '../base/model');
11
- const { linkToOrigin, withAssociation } = require('./shared');
11
+ const { linkToOrigin, withAssociation } = require('./utils');
12
12
  // const { refString } = require( '../base/messages')
13
13
 
14
14
  function propagate( model ) {
@@ -179,6 +179,8 @@ function propagate( model ) {
179
179
  // accessed at their type being a main artifact
180
180
  function expensive( prop, target, source ) {
181
181
  // console.log(prop,source.name,'->',target.kind,target.name);
182
+ if (source.kind === 'builtin')
183
+ return;
182
184
  if (prop !== 'foreignKeys' && availableAtType( prop, target, source ))
183
185
  // foreignKeys must always be copied with target to avoid any confusion
184
186
  // whether we have to generated implicit keys
@@ -211,7 +213,8 @@ function propagate( model ) {
211
213
  }
212
214
 
213
215
  function onlyViaParent( prop, target, source ) {
214
- if (target.$inferred === 'proxy') // assocs and enums do not have 'include'
216
+ if (target.$inferred === 'proxy' || target.$inferred === 'expand-element')
217
+ // assocs and enums do not have 'include'
215
218
  always( prop, target, source );
216
219
  }
217
220
 
@@ -47,25 +47,28 @@ const {
47
47
  const { dictLocation } = require('../base/location');
48
48
  const { searchName, weakLocation } = require('../base/messages');
49
49
  const { combinedLocation } = require('../base/location');
50
- const { pushLink } = require('./utils');
50
+
51
+ const { kindProperties } = require('./base');
51
52
  const {
52
- getDefinerFunctions,
53
+ pushLink,
54
+ setLink,
53
55
  augmentPath,
54
56
  splitIntoPath,
55
- } = require('./definer');
57
+ linkToOrigin,
58
+ setMemberParent,
59
+ withAssociation,
60
+ storeExtension,
61
+ dependsOn,
62
+ dependsOnSilent,
63
+ } = require('./utils');
56
64
 
57
65
  const detectCycles = require('./cycle-detector');
58
66
  const layers = require('./moduleLayers');
59
67
 
60
- const {
61
- kindProperties, fns, setLink, linkToOrigin, setMemberParent, withAssociation, storeExtension,
62
- dependsOn, dependsOnSilent,
63
- } = require('./shared');
64
-
65
68
  const annotationPriorities = {
66
69
  define: 1, extend: 2, annotate: 2, edmx: 3,
67
70
  };
68
-
71
+ const $inferred = Symbol.for('cds.$inferred');
69
72
 
70
73
  // Export function of this file. Resolve type references in augmented CSN
71
74
  // `model`. If the model has a property argument `messages`, do not throw
@@ -73,21 +76,21 @@ const annotationPriorities = {
73
76
  // that property (should be a vector).
74
77
  function resolve( model ) {
75
78
  const { options } = model;
76
- // Get shared "resolve" functionality and the message function:
79
+ // Get shared functionality and the message function:
80
+ const {
81
+ info, warning, error, message,
82
+ } = model.$messageFunctions;
77
83
  const {
78
84
  resolvePath,
79
85
  resolveTypeArguments,
80
86
  defineAnnotations,
81
87
  attachAndEmitValidNames,
82
- } = fns( model, environment );
83
- const {
84
- info, warning, error, message,
85
- } = model.$messageFunctions;
86
- const {
87
88
  initArtifact,
88
89
  lateExtensions,
89
90
  projectionAncestor,
90
- } = getDefinerFunctions(model);
91
+ } = model.$functions;
92
+ model.$volatileFunctions.environment = environment;
93
+
91
94
  /** @type {any} may also be a boolean */
92
95
  let newAutoExposed = [];
93
96
 
@@ -242,6 +245,7 @@ function resolve( model ) {
242
245
  const chain = [];
243
246
  while (art && !('_effectiveType' in art) &&
244
247
  (art.type || art._origin || art.value && art.value.path) &&
248
+ // TODO: really stop at art.enum?
245
249
  !art.target && !art.enum && !art.elements && !art.items) {
246
250
  chain.push( art );
247
251
  setProp( art, '_effectiveType', 0 ); // initial setting in case of cycles
@@ -273,13 +277,17 @@ function resolve( model ) {
273
277
  // collect the "latest" cardinality (calculate lazyly if necessary)
274
278
  let cardinality = art.cardinality ||
275
279
  art._effectiveType && (() => getCardinality( art._effectiveType ));
280
+ let prev = art;
276
281
  for (const a of chain) {
277
282
  if (a.cardinality)
278
283
  cardinality = a.cardinality;
279
284
  if (a.expand && expandFromColumns( a, art, cardinality ) ||
280
285
  art.target && redirectImplicitly( a, art ) ||
281
- art.elements && expandElements( a, art, eType ))
286
+ art.elements && expandElements( a, art, eType ) ||
287
+ art.items && expandItems( a, art, eType ))
282
288
  art = a;
289
+ else if (art.enum && expandEnum( a, prev ))
290
+ prev = a; // do not set art - effective type is base
283
291
  setProp( a, '_effectiveType', art );
284
292
  }
285
293
  }
@@ -405,7 +413,7 @@ function resolve( model ) {
405
413
  for (const view of resolveChain.reverse()) {
406
414
  if (view._status !== '_query' ) { // not already resolved
407
415
  setProp( view, '_status', '_query' );
408
- traverseQueryPost( view.query, false, populateQuery );
416
+ traverseQueryPost( view.query, null, populateQuery );
409
417
  if (view.elements$) // specified elements
410
418
  mergeSpecifiedElements( view );
411
419
  if (!view.$entity) {
@@ -494,7 +502,8 @@ function resolve( model ) {
494
502
  });
495
503
  }
496
504
  forEachGeneric( { elements: alias.elements }, 'elements', ( elem, name ) => {
497
- dictAddArray( query._combined, name, elem, null ); // not dictAdd()
505
+ if (elem.$duplicates !== true)
506
+ dictAddArray( query._combined, name, elem, null ); // not dictAdd()
498
507
  });
499
508
  }
500
509
  }
@@ -535,6 +544,23 @@ function resolve( model ) {
535
544
  setMemberParent( key, name, elem ); // TODO: set _block here if not present?
536
545
  }
537
546
 
547
+ function expandItems( art, origin, eType ) {
548
+ if (!enableExpandElements || art.items)
549
+ return false;
550
+ if (isInParents( art, eType )) {
551
+ art.items = 0; // circular
552
+ return true;
553
+ }
554
+ const ref = art.type || art.value || art.name;
555
+ const location = ref && ref.location || art.location;
556
+ art.items = { $inferred: 'expand-element', location };
557
+ setProp( art.items, '_outer', art );
558
+ setProp( art.items, '_origin', origin.items );
559
+ if (!art.$expand)
560
+ art.$expand = 'origin'; // if value stays, elements won't appear in CSN
561
+ return true;
562
+ }
563
+
538
564
  function expandElements( art, struct, eType ) {
539
565
  if (!enableExpandElements)
540
566
  return false;
@@ -566,7 +592,28 @@ function resolve( model ) {
566
592
  // member property):
567
593
  if (!art.$expand)
568
594
  art.$expand = 'origin'; // if value stays, elements won't appear in CSN
569
- // TODO: have some art.elements[SYM.$inferred] = 'expand-elements';
595
+ // TODO: have some art.elements[SYM.$inferred] = 'expand-element';
596
+ return true;
597
+ }
598
+
599
+ function expandEnum( art, origin ) {
600
+ if (!enableExpandElements || art.enum)
601
+ return false;
602
+ const ref = art.type || art.value || art.name;
603
+ const location = weakLocation( ref && ref.location || art.location );
604
+ art.enum = Object.create(null);
605
+ for (const name in origin.enum) {
606
+ const orig = origin.enum[name];
607
+ linkToOrigin( orig, name, art, 'enum', location, true )
608
+ // or should we use orig.location? - TODO: try to find test to see message
609
+ .$inferred = 'expand-element';
610
+ }
611
+ // Set elements expansion status (the if condition is always true, as no
612
+ // elements expansion will take place on artifact with existing other
613
+ // member property):
614
+ if (!art.$expand)
615
+ art.$expand = 'origin'; // if value stays, elements won't appear in CSN
616
+ art.enum[$inferred] = 'expand-element';
570
617
  return true;
571
618
  }
572
619
 
@@ -604,7 +651,7 @@ function resolve( model ) {
604
651
  // not counting propagated ones; set up to the definition (main artifact)
605
652
  // (only set with anno on $inferred elem)
606
653
  // Usage according to CSN flavor:
607
- // - gensrc: do not render enferred elements (including expanded elements),
654
+ // - gensrc: do not render inferred elements (including expanded elements),
608
655
  // collect annotate statements with value 'annotate'
609
656
  // - client: do not render expanded sub elements if artifact/member is no type, has a type,
610
657
  // has $expand = 'origin', and all its _origin also have $expand = 'origin'
@@ -1441,8 +1488,9 @@ function resolve( model ) {
1441
1488
  }
1442
1489
  // Resolve projections/views
1443
1490
  // if (art.query)console.log( info( null, [art.query.location,art.query], 'VQ:' ).toString() );
1444
- // TODO: here, any order should be ok, i.e. just loop over $queries
1445
- traverseQueryPost( art.query, false, resolveQuery );
1491
+
1492
+ if (art.$queries)
1493
+ art.$queries.forEach( resolveQuery );
1446
1494
 
1447
1495
  if (obj.type || obj._origin || obj.value && obj.value.path || obj.elements) // typed artifacts
1448
1496
  effectiveType(obj); // set _effectiveType if appropriate, (future?): copy elems if extended
@@ -1658,6 +1706,8 @@ function resolve( model ) {
1658
1706
  location,
1659
1707
  $inferred: 'expand-param',
1660
1708
  };
1709
+ setProp( art.returns, '_parent', art );
1710
+ setProp( art.returns, '_main', art._main || art );
1661
1711
  setProp( art.returns, '_origin', origin.returns );
1662
1712
  }
1663
1713
  }
@@ -1868,7 +1918,7 @@ function resolve( model ) {
1868
1918
  function resolveQuery( query ) {
1869
1919
  if (!query._main) // parse error
1870
1920
  return;
1871
- populateQuery( query );
1921
+ traverseQueryPost( query, null, populateQuery );
1872
1922
  forEachGeneric( query, '$tableAliases', ( alias ) => {
1873
1923
  // console.log( info( null, [alias.location,alias], 'SQA:' ).toString() );
1874
1924
  if (alias.kind === 'mixin')
@@ -2593,6 +2643,8 @@ function resolve( model ) {
2593
2643
  }
2594
2644
 
2595
2645
  function resolveExpr( expr, expected, user, extDict, expandOrInline) {
2646
+ // TODO: when we have rewritten the resolvePath functions,
2647
+ // define a traverseExpr() in ./utils.js
2596
2648
  // TODO: extra "expected" 'expand'/'inline' instead o param `expandOrInline`
2597
2649
  if (!expr || typeof expr === 'string') // parse error or keywords in {xpr:...}
2598
2650
  return;
@@ -2629,7 +2681,7 @@ function resolve( model ) {
2629
2681
  else if (expr.query) {
2630
2682
  const { query } = expr;
2631
2683
  if (query.kind || query._leadingQuery) { // UNION has _leadingQuery
2632
- traverseQueryPost( query, false, resolveQuery );
2684
+ // traverseQueryPost( query, false, resolveQuery );
2633
2685
  }
2634
2686
  else {
2635
2687
  error( 'expr-no-subquery', [ expr.location, user ], {},
@@ -2640,9 +2692,10 @@ function resolve( model ) {
2640
2692
  const args = Array.isArray(expr.args) ? expr.args : Object.values( expr.args );
2641
2693
  args.forEach( e => e && resolveExpr( e, e.$expected || expected, user, extDict ) );
2642
2694
  }
2643
- if (expr.suffix && !isBetaEnabled( options, 'windowFunctions' )) {
2695
+ if (expr.suffix && isDeprecatedEnabled( options )) {
2644
2696
  const { location } = expr.suffix[0] || expr;
2645
- error( null, [ location, user ], 'Window functions are not supported' );
2697
+ error( null, [ location, user ], { prop: 'deprecated' },
2698
+ 'Window functions are not supported if $(PROP) options are set' );
2646
2699
  }
2647
2700
  if (expr.suffix)
2648
2701
  expr.suffix.forEach( s => s && resolveExpr( s, expected, user, extDict ) );
@@ -2830,8 +2883,6 @@ function navProjection( navigation, preferred ) {
2830
2883
  // Query tree post-order traversal - called for everything which makes a query
2831
2884
  // except "real ones": operands of UNION etc, JOIN with ON, and sub queries in FROM
2832
2885
  function traverseQueryPost( query, simpleOnly, callback ) {
2833
- while (Array.isArray(query)) // query in parentheses, TODO: remove
2834
- query = query[0];
2835
2886
  if (!query) // parser error
2836
2887
  return;
2837
2888
  if (!query.op) { // in FROM (not JOIN)
@@ -2851,11 +2902,19 @@ function traverseQueryPost( query, simpleOnly, callback ) {
2851
2902
  // console.log('FE:')
2852
2903
  }
2853
2904
  else if (query.args) { // JOIN, UNION, INTERSECT
2854
- for (const q of query.args)
2855
- traverseQueryPost( q, simpleOnly, callback );
2856
- // The ON condition has to be traversed extra, because it must be evaluated
2857
- // after the complete FROM has been traversed. It is also not necessary to
2858
- // evaluate it in populateQuery().
2905
+ if (!query.join && simpleOnly == null) {
2906
+ // enough for elements: traverse only first args for UNION/INTERSECT
2907
+ // TODO: we might use this also when we do not rewrite associations
2908
+ // in non-referred sub queries
2909
+ traverseQueryPost( query.args[0], simpleOnly, callback );
2910
+ }
2911
+ else {
2912
+ for (const q of query.args)
2913
+ traverseQueryPost( q, simpleOnly, callback );
2914
+ // The ON condition has to be traversed extra, because it must be evaluated
2915
+ // after the complete FROM has been traversed. It is also not necessary to
2916
+ // evaluate it in populateQuery().
2917
+ }
2859
2918
  }
2860
2919
  // else: with parse error (`select from <EOF>`, `select distinct from;`)
2861
2920
  }