@sap/cds-compiler 3.0.0 → 3.1.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +104 -9
  2. package/bin/.eslintrc.json +2 -1
  3. package/bin/cdsc.js +28 -16
  4. package/doc/API.md +11 -0
  5. package/doc/CHANGELOG_ARCHIVE.md +1 -1
  6. package/doc/CHANGELOG_BETA.md +24 -2
  7. package/doc/CHANGELOG_DEPRECATED.md +21 -1
  8. package/lib/api/main.js +92 -40
  9. package/lib/api/options.js +2 -3
  10. package/lib/base/keywords.js +64 -1
  11. package/lib/base/message-registry.js +33 -5
  12. package/lib/base/messages.js +54 -65
  13. package/lib/base/model.js +2 -0
  14. package/lib/base/optionProcessorHelper.js +53 -21
  15. package/lib/checks/actionsFunctions.js +8 -7
  16. package/lib/checks/selectItems.js +96 -14
  17. package/lib/checks/types.js +5 -8
  18. package/lib/checks/validator.js +1 -2
  19. package/lib/compiler/assert-consistency.js +65 -13
  20. package/lib/compiler/base.js +6 -4
  21. package/lib/compiler/builtins.js +93 -4
  22. package/lib/compiler/checks.js +1 -1
  23. package/lib/compiler/define.js +28 -23
  24. package/lib/compiler/extend.js +20 -11
  25. package/lib/compiler/finalize-parse-cdl.js +5 -9
  26. package/lib/compiler/index.js +2 -0
  27. package/lib/compiler/populate.js +37 -32
  28. package/lib/compiler/propagator.js +11 -6
  29. package/lib/compiler/resolve.js +15 -19
  30. package/lib/compiler/shared.js +54 -18
  31. package/lib/compiler/tweak-assocs.js +5 -11
  32. package/lib/compiler/utils.js +15 -6
  33. package/lib/edm/annotations/genericTranslation.js +12 -2
  34. package/lib/edm/annotations/preprocessAnnotations.js +18 -15
  35. package/lib/edm/csn2edm.js +18 -17
  36. package/lib/edm/edm.js +22 -13
  37. package/lib/edm/edmAnnoPreprocessor.js +349 -0
  38. package/lib/edm/edmInboundChecks.js +85 -0
  39. package/lib/edm/edmPreprocessor.js +336 -665
  40. package/lib/edm/edmUtils.js +86 -45
  41. package/lib/gen/Dictionary.json +29 -9
  42. package/lib/gen/language.checksum +1 -1
  43. package/lib/gen/language.interp +1 -2
  44. package/lib/gen/languageLexer.js +3 -0
  45. package/lib/gen/languageParser.js +4332 -4496
  46. package/lib/inspect/.eslintrc.json +4 -0
  47. package/lib/inspect/index.js +14 -0
  48. package/lib/inspect/inspectModelStatistics.js +81 -0
  49. package/lib/inspect/inspectPropagation.js +189 -0
  50. package/lib/inspect/inspectUtils.js +44 -0
  51. package/lib/json/from-csn.js +19 -20
  52. package/lib/json/to-csn.js +11 -8
  53. package/lib/language/genericAntlrParser.js +150 -92
  54. package/lib/language/language.g4 +47 -74
  55. package/lib/main.d.ts +1 -0
  56. package/lib/model/api.js +1 -1
  57. package/lib/model/csnRefs.js +56 -29
  58. package/lib/model/csnUtils.js +29 -14
  59. package/lib/model/revealInternalProperties.js +6 -4
  60. package/lib/modelCompare/compare.js +3 -0
  61. package/lib/optionProcessor.js +81 -38
  62. package/lib/render/toCdl.js +57 -32
  63. package/lib/render/toHdbcds.js +1 -1
  64. package/lib/render/toSql.js +31 -11
  65. package/lib/render/utils/common.js +3 -4
  66. package/lib/transform/db/associations.js +43 -35
  67. package/lib/transform/db/cdsPersistence.js +0 -1
  68. package/lib/transform/db/flattening.js +3 -4
  69. package/lib/transform/db/transformExists.js +7 -5
  70. package/lib/transform/draft/db.js +1 -1
  71. package/lib/transform/forHanaNew.js +11 -2
  72. package/lib/transform/forOdataNew.js +4 -4
  73. package/lib/transform/localized.js +15 -11
  74. package/lib/transform/odata/typesExposure.js +14 -5
  75. package/lib/utils/file.js +28 -18
  76. package/lib/utils/moduleResolve.js +0 -1
  77. package/package.json +3 -4
  78. package/share/messages/syntax-expected-integer.md +9 -8
  79. package/lib/checks/unknownMagic.js +0 -41
@@ -107,12 +107,12 @@ function attachOnConditions(csn, pathDelimiter) {
107
107
  * @param {string} pathDelimiter
108
108
  * @returns {(artifact: CSN.Artifact, artifactName: string) => void} Callback for forEachDefinition
109
109
  */
110
- function getManagedAssocStepsInOnConditionFinalizer(csn, pathDelimiter) {
110
+ function getFKAccessFinalizer(csn, pathDelimiter) {
111
111
  const {
112
112
  inspectRef,
113
113
  } = getUtils(csn);
114
114
 
115
- return handleManagedAssocStepsInOnCondition;
115
+ return handleManagedAssocSteps;
116
116
 
117
117
  /**
118
118
  * Loop over all elements and for all unmanaged associations translate
@@ -123,45 +123,53 @@ function getManagedAssocStepsInOnConditionFinalizer(csn, pathDelimiter) {
123
123
  * @param {CSN.Artifact} artifact Artifact to check
124
124
  * @param {string} artifactName Name of the artifact
125
125
  */
126
- function handleManagedAssocStepsInOnCondition(artifact, artifactName) {
127
- for (const elemName in artifact.elements) {
128
- const elem = artifact.elements[elemName];
129
- // The association is an unmanaged on
130
- if (!elem.keys && elem.target && elem.on) {
131
- applyTransformationsOnNonDictionary(elem, 'on', {
132
- ref: (refOwner, prop, ref, path) => {
133
- // [<assoc base>.]<managed assoc>.<field>
134
- if (ref.length > 1) {
135
- const { links } = inspectRef(path);
136
- if (links) {
137
- // eslint-disable-next-line for-direction
138
- for (let i = links.length - 1; i >= 0; i--) {
139
- const link = links[i];
140
- // We found the latest managed assoc path step
141
- if (link.art && link.art.target && link.art.keys &&
126
+ function handleManagedAssocSteps(artifact, artifactName) {
127
+ const transformer = {
128
+ ref: (refOwner, prop, ref, path) => {
129
+ // [<assoc base>.]<managed assoc>.<field>
130
+ if (ref.length > 1) {
131
+ const { links } = inspectRef(path);
132
+ if (links) {
133
+ // eslint-disable-next-line for-direction
134
+ for (let i = links.length - 1; i >= 0; i--) {
135
+ const link = links[i];
136
+ // We found the latest managed assoc path step
137
+ if (link.art && link.art.target && link.art.keys &&
142
138
  // Doesn't work when ref-target (filter condition) or similar is used
143
139
  !ref.slice(i).some(refElement => typeof refElement !== 'string')) {
144
- // We join the managed assoc with everything following it
145
- const sourceElementName = ref.slice(i).join(pathDelimiter);
146
- const source = findSource(links, i - 1) || artifact;
147
- // allow specifying managed assoc on the source side
148
- const fks = link.art.keys.filter(fk => ref[i] + pathDelimiter + fk.ref[0] === sourceElementName);
149
- if (fks && fks.length >= 1) {
150
- const fk = fks[0];
151
- const managedAssocStepName = refOwner.ref[i];
152
- const fkName = `${managedAssocStepName}${pathDelimiter}${fk.as}`;
153
- if (source && source.elements[fkName])
154
- refOwner.ref = [ ...ref.slice(0, i), fkName ];
155
- }
156
- }
140
+ // We join the managed assoc with everything following it
141
+ const sourceElementName = ref.slice(i).join(pathDelimiter);
142
+ const source = findSource(links, i - 1) || artifact;
143
+ // allow specifying managed assoc on the source side
144
+ const fks = link.art.keys.filter(fk => ref[i] + pathDelimiter + fk.ref[0] === sourceElementName);
145
+ if (fks && fks.length >= 1) {
146
+ const fk = fks[0];
147
+ const managedAssocStepName = refOwner.ref[i];
148
+ const fkName = `${managedAssocStepName}${pathDelimiter}${fk.as}`;
149
+ if (source && source.elements[fkName])
150
+ refOwner.ref = [ ...ref.slice(0, i), fkName ];
157
151
  }
158
152
  }
159
153
  }
160
- },
161
- }, {}, [ 'definitions', artifactName, 'elements', elemName ]);
162
- }
154
+ }
155
+ }
156
+ },
157
+ };
158
+ for (const elemName in artifact.elements) {
159
+ const elem = artifact.elements[elemName];
160
+ // The association is an unmanaged one
161
+ if (!elem.keys && elem.target && elem.on)
162
+ applyTransformationsOnNonDictionary(elem, 'on', transformer, {}, [ 'definitions', artifactName, 'elements', elemName ]);
163
+ }
164
+
165
+ if (artifact.query || artifact.projection) {
166
+ applyTransformationsOnNonDictionary(artifact, artifact.query ? 'query' : 'projection', {
167
+ orderBy: (parent, prop, thing, path) => applyTransformationsOnNonDictionary(parent, prop, transformer, {}, path),
168
+ groupBy: (parent, prop, thing, path) => applyTransformationsOnNonDictionary(parent, prop, transformer, {}, path),
169
+ }, {}, [ 'definitions', artifactName ]);
163
170
  }
164
171
 
172
+
165
173
  /**
166
174
  * Find out where the managed association is
167
175
  *
@@ -183,5 +191,5 @@ function getManagedAssocStepsInOnConditionFinalizer(csn, pathDelimiter) {
183
191
  }
184
192
  module.exports = {
185
193
  attachOnConditions,
186
- getManagedAssocStepsInOnConditionFinalizer,
194
+ getFKAccessFinalizer,
187
195
  };
@@ -78,7 +78,6 @@ function getAssocToSkippedIgnorer(csn, options, messageFunctions) {
78
78
  * @param {string} memberName
79
79
  * @param {string} prop
80
80
  * @param {CSN.Path} path
81
- * @todo Why do we check for @cds.persistence.exists here if the parent-function only calls this for skip/abstract?
82
81
  */
83
82
  function ignore(member, memberName, prop, path) {
84
83
  if (options.sqlDialect === 'hana' &&
@@ -47,7 +47,6 @@ function removeLeadingSelf(csn) {
47
47
  * @param {object} iterateOptions
48
48
  */
49
49
  function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOptions = {}) {
50
- const typeCache = Object.create(null); // TODO: Argument as well?
51
50
  /**
52
51
  * Remove .localized from the element and any sub-elements
53
52
  *
@@ -83,14 +82,14 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
83
82
  cast: (parent, prop, cast, path) => {
84
83
  // Resolve cast already - we otherwise lose .localized
85
84
  if (cast.type && !isBuiltinType(cast.type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(cast.type, path) && !isODataItems(cast.type)))
86
- toFinalBaseType(parent.cast, resolved, true, typeCache);
85
+ toFinalBaseType(parent.cast, resolved, true);
87
86
  },
88
87
  // @ts-ignore
89
88
  type: (parent, prop, type, path) => {
90
89
  if (options.toOdata && parent.kind && parent.kind in ignoreOdataKinds)
91
90
  return;
92
91
  if (!isBuiltinType(type) && (!options.toOdata || options.toOdata && !isODataV4BuiltinFromService(type, path) && !isODataItems(type))) {
93
- toFinalBaseType(parent, resolved, true, typeCache);
92
+ toFinalBaseType(parent, resolved, true);
94
93
  // structured types might not have the child-types replaced.
95
94
  // Drill down to ensure this.
96
95
  if (parent.elements) {
@@ -99,7 +98,7 @@ function resolveTypeReferences(csn, options, resolved, pathDelimiter, iterateOpt
99
98
  const elements = stack.pop();
100
99
  for (const e of Object.values(elements)) {
101
100
  if (e.type && !isBuiltinType(e.type))
102
- toFinalBaseType(e, resolved, true, typeCache);
101
+ toFinalBaseType(e, resolved, true);
103
102
 
104
103
  if (e.elements)
105
104
  stack.push(e.elements);
@@ -349,7 +349,7 @@ function handleExists(csn, options, error) {
349
349
  const newExpr = [];
350
350
  const query = walkCsnPath(csn, queryPath);
351
351
  const expr = walkCsnPath(csn, exprPath);
352
- const queryBase = query.SELECT.from.ref ? (query.SELECT.from.as || query.SELECT.from.ref[0]) : null;
352
+ const queryBase = query.SELECT.from.ref ? (query.SELECT.from.as || query.SELECT.from.ref) : null;
353
353
  const sources = getQuerySources(query.SELECT);
354
354
 
355
355
  for (let i = 0; i < expr.length; i++) {
@@ -457,7 +457,7 @@ function handleExists(csn, options, error) {
457
457
  *
458
458
  * A valid $self-backlink is handled in translateDollarSelfToWhere.
459
459
  *
460
- * For an ordinary unmanaged association, we do the the following for each part of the on-condition:
460
+ * For an ordinary unmanaged association, we do the following for each part of the on-condition:
461
461
  * - target side: We prefix the real target and cut off the assoc-name from the ref
462
462
  * - source side w/ leading $self: We remove the $self and add the source side entity/query source
463
463
  * - source side w/o leading $self: We simply add the source side entity/query source in front of the ref
@@ -730,15 +730,17 @@ function handleExists(csn, options, error) {
730
730
  /**
731
731
  * Get the name of the source-side query source
732
732
  *
733
- * @param {string|null} queryBase
733
+ * @param {string | Array | null} queryBase
734
734
  * @param {boolean} isPrefixedWithTableAlias
735
735
  * @param {CSN.Column} current
736
736
  * @param {CSN.Path} path
737
737
  * @returns {string}
738
738
  */
739
739
  function getBase(queryBase, isPrefixedWithTableAlias, current, path) {
740
- if (queryBase)
741
- return getRealName(csn, queryBase);
740
+ if (typeof queryBase === 'string') // alias
741
+ return queryBase;
742
+ else if (queryBase) // ref
743
+ return queryBase.length > 1 ? queryBase[queryBase.length - 1] : getRealName(csn, queryBase[0]);
742
744
  else if (isPrefixedWithTableAlias)
743
745
  return current.ref[0];
744
746
  return getParent(current, path);
@@ -19,7 +19,7 @@ const booleanBuiltin = 'cds.Boolean';
19
19
  * @param {object} messageFunctions
20
20
  */
21
21
  function generateDrafts(csn, options, pathDelimiter, messageFunctions) {
22
- const draftSuffix = isDeprecatedEnabled(options, '_generatedEntityNameWithUnderscore') ? '_drafts' : '.drafts';
22
+ const draftSuffix = '.drafts';
23
23
  // All services of the model - needed for drafts
24
24
  const allServices = getServiceNames(csn);
25
25
  const draftRoots = new WeakMap();
@@ -265,10 +265,10 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
265
265
  // To be done after handleAssociations, since then the foreign keys of the managed assocs
266
266
  // are part of the elements
267
267
  if (doA2J)
268
- forEachDefinition(csn, associations.getManagedAssocStepsInOnConditionFinalizer(csn, pathDelimiter));
268
+ forEachDefinition(csn, associations.getFKAccessFinalizer(csn, pathDelimiter));
269
269
 
270
270
  // Create convenience views for localized entities/views.
271
- // To be done after getManagedAssocStepsInOnConditionFinalizer because associations are
271
+ // To be done after getFKAccessFinalizer because associations are
272
272
  // handled and before handleDBChecks which removes the localized attribute.
273
273
  // Association elements of localized convenience views do not have hidden properties
274
274
  // like $managed set, so we cannot do this earlier on.
@@ -392,6 +392,15 @@ function transformForHanaWithCsn(inputModel, options, moduleName) {
392
392
  setProp(SET, 'elements', query.SELECT.elements);
393
393
  }
394
394
  }
395
+ },
396
+
397
+ }
398
+
399
+ if(options.sqlDialect === 'postgres') {
400
+ killers.length = (parent) => {
401
+ if (parent.type === 'cds.Binary' || parent.type === 'cds.hana.BINARY') {
402
+ delete parent.length;
403
+ }
395
404
  }
396
405
  }
397
406
 
@@ -120,7 +120,7 @@ function transform4odataWithCsn(inputModel, options) {
120
120
  return keepLocalizedViews && !isExternalServiceMember(undefined, parent);
121
121
  }
122
122
 
123
- addLocalizationViews(csn, options, acceptLocalizedView);
123
+ addLocalizationViews(csn, options, { acceptLocalizedView, ignoreUnknownExtensions: true });
124
124
 
125
125
  const cleanup = validate.forOdata(csn, {
126
126
  message, error, warning, info, inspectRef, effectiveType, artifactRef, csn, options, csnUtils, services, isAspect, isExternalServiceMember
@@ -178,7 +178,7 @@ function transform4odataWithCsn(inputModel, options) {
178
178
  // To be done after handleManagedAssociationsAndCreateForeignKeys,
179
179
  // since then the foreign keys of the managed assocs are part of the elements
180
180
  if(!structuredOData)
181
- forEachDefinition(csn, associations.getManagedAssocStepsInOnConditionFinalizer(csn, '_'));
181
+ forEachDefinition(csn, associations.getFKAccessFinalizer(csn, '_'));
182
182
 
183
183
  // structure flattener reports errors, further processing is not safe -> throw exception in case of errors
184
184
  throwWithAnyError();
@@ -324,7 +324,7 @@ function transform4odataWithCsn(inputModel, options) {
324
324
  // but '@Core.Immutable' for everything else.
325
325
  if (!(node['@readonly'] && node['@insertonly'])) {
326
326
  if (name === '@readonly' && node[name] !== null) {
327
- if (node.kind === 'entity') {
327
+ if (node.kind === 'entity' || node.kind === 'aspect') {
328
328
  setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
329
329
  setAnnotation(node, '@Capabilities.InsertRestrictions.Insertable', false);
330
330
  setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
@@ -334,7 +334,7 @@ function transform4odataWithCsn(inputModel, options) {
334
334
  }
335
335
  // @insertonly is effective on entities/queries only
336
336
  else if (name === '@insertonly' && node[name] !== null) {
337
- if (node.kind === 'entity') {
337
+ if (node.kind === 'entity' || node.kind === 'aspect') {
338
338
  setAnnotation(node, '@Capabilities.DeleteRestrictions.Deletable', false);
339
339
  setAnnotation(node, '@Capabilities.ReadRestrictions.Readable', false);
340
340
  setAnnotation(node, '@Capabilities.UpdateRestrictions.Updatable', false);
@@ -69,10 +69,9 @@ const _targetFor = Symbol('_targetFor');
69
69
  * @param {CSN.Options} options
70
70
  * @param {boolean} useJoins If true, rewrite the "localized" association to a
71
71
  * join in direct convenience views.
72
- * @param {acceptLocalizedView} [acceptLocalizedView] optional callback function returning true if the localized view
73
- * name and its parent name provided as parameter should be created
72
+ * @param {object} config
74
73
  */
75
- function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = null) {
74
+ function _addLocalizationViews(csn, options, useJoins, config) {
76
75
  // Don't try to create convenience views with errors.
77
76
  if (hasErrors(options.messages))
78
77
  return csn;
@@ -81,6 +80,7 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
81
80
  if (hasExistingLocalizationViews(csn, options, messageFunctions))
82
81
  return csn;
83
82
 
83
+ const { acceptLocalizedView, ignoreUnknownExtensions } = config;
84
84
  const noCoalesce = (options.localizedLanguageFallback === 'none' ||
85
85
  options.localizedWithoutCoalesce);
86
86
 
@@ -91,8 +91,12 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
91
91
 
92
92
  // In case that the user tried to annotate `localized.*` artifacts, apply them.
93
93
  applyAnnotationsFromExtensions(csn, {
94
- overwrite: true,
95
- filter: (name) => name.startsWith('localized.')
94
+ override: true,
95
+ filter: (name) => name.startsWith('localized.'),
96
+ notFound(name, index) {
97
+ if (!ignoreUnknownExtensions)
98
+ messageFunctions.message('anno-undefined-art', [ 'extensions', index ], { name })
99
+ },
96
100
  });
97
101
 
98
102
  sortCsnDefinitionsForTests(csn, options);
@@ -608,10 +612,10 @@ function _addLocalizationViews(csn, options, useJoins, acceptLocalizedView = nul
608
612
  *
609
613
  * @param {CSN.Model} csn
610
614
  * @param {CSN.Options} options
611
- * @param [acceptLocalizedView] optional callback function returning true if the localized view name and its parent name provided as parameter should be created
615
+ * @param [config] config.acceptLocalizedView: optional callback function returning true if the localized view name and its parent name provided as parameter should be created
612
616
  */
613
- function addLocalizationViews(csn, options, acceptLocalizedView = null) {
614
- return _addLocalizationViews(csn, options, false, acceptLocalizedView);
617
+ function addLocalizationViews(csn, options, config = {}) {
618
+ return _addLocalizationViews(csn, options, false, config);
615
619
  }
616
620
 
617
621
  /**
@@ -621,10 +625,10 @@ function addLocalizationViews(csn, options, acceptLocalizedView = null) {
621
625
  *
622
626
  * @param {CSN.Model} csn
623
627
  * @param {CSN.Options} options
624
- * @param [acceptLocalizedView] optional callback function returning true if the localized view name and its parent name provided as parameter should be created
628
+ * @param [config] config.acceptLocalizedView: optional callback function returning true if the localized view name and its parent name provided as parameter should be created
625
629
  */
626
- function addLocalizationViewsWithJoins(csn, options, acceptLocalizedView = null) {
627
- return _addLocalizationViews(csn, options, true, acceptLocalizedView);
630
+ function addLocalizationViewsWithJoins(csn, options, config = {}) {
631
+ return _addLocalizationViews(csn, options, true, config);
628
632
  }
629
633
 
630
634
  /**
@@ -10,6 +10,7 @@ const { setProp } = require('../../base/model');
10
10
  const { defNameWithoutServiceOrContextName, isArtifactInService } = require('./utils');
11
11
  const { cloneCsnNonDict, isBuiltinType, forEachDefinition, forEachMember } = require('../../model/csnUtils');
12
12
  const { copyAnnotations } = require('../../model/csnUtils');
13
+ const { isBetaEnabled } = require('../../base/model.js');
13
14
 
14
15
  function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackSchemaName, options, csnUtils, message) {
15
16
  const { error } = message;
@@ -92,11 +93,16 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
92
93
  : getAnonymousTypeNameInMultiSchema(newTypeName, parentName || defName))
93
94
  : `${serviceName}.${newTypeName}`;
94
95
 
95
- // as soon as we leave of the anonymous world,
96
- // we're no longer in a key def => don't set notNull:true on named types
97
- if (!isAnonymous && isKey)
98
- isKey = false;
99
-
96
+ if (!isAnonymous) {
97
+ // as soon as we leave of the anonymous world,
98
+ // we're no longer in a key def => don't set notNull:true on named types
99
+ if(isKey)
100
+ isKey = false;
101
+ // in case this was a named type and if the openess does not match the type definition
102
+ // expose the type as a new one not changing the original definition.
103
+ if((!!node['@open'] !== !!typeDef['@open']) && isBetaEnabled(options, 'odataOpenType'))
104
+ fullQualifiedNewTypeName += node['@open'] ? '_open' : '_closed';
105
+ }
100
106
  // check if that type is already defined
101
107
  let newType = csn.definitions[fullQualifiedNewTypeName];
102
108
  if (newType) {
@@ -111,6 +117,9 @@ function typesExposure(csn, whatsMyServiceName, requestedServiceNames, fallBackS
111
117
  * Treat items.elements as ordinary elements for now.
112
118
  */
113
119
  newType = createNewStructType(elements);
120
+ // if using node enforces open/closed, set it on type
121
+ if(node['@open'] !== undefined)
122
+ newType['@open'] = node['@open']
114
123
  if (node.$location)
115
124
  setProp(newType, '$location', node.$location);
116
125
 
package/lib/utils/file.js CHANGED
@@ -113,11 +113,16 @@ function cdsFs(fileCache, enableTrace) {
113
113
  traceFS( 'READFILE:start:', filename );
114
114
  // TODO: set cache directly to some "delay" - store error differently?
115
115
  // e.g. an error of callback functions!
116
- reader(filename, enc, ( err, data ) => {
117
- fileCache[filename] = err || data;
118
- traceFS( 'READFILE:data:', filename, err || data );
119
- cb( err, data );
120
- });
116
+ try {
117
+ reader(filename, enc, (err, data) => {
118
+ fileCache[filename] = err || data;
119
+ traceFS('READFILE:data:', filename, err || data);
120
+ cb(err, data);
121
+ });
122
+ }
123
+ catch (err) {
124
+ cb(err); // if filename is not a valid (e.g. contains NUL byte), readFile() may throw
125
+ }
121
126
  }
122
127
  };
123
128
  }
@@ -143,19 +148,24 @@ function cdsFs(fileCache, enableTrace) {
143
148
  // in the future (if we do module resolve ourself with just readFile),
144
149
  // we avoid parallel readFile by storing having an array of `cb`s in
145
150
  // fileCache[ filename ] before starting fs.readFile().
146
- fsStat( filename, ( err, stat ) => {
147
- if (err)
148
- body = (err.code === 'ENOENT' || err.code === 'ENOTDIR') ? false : err;
149
- else
150
- body = !!(stat.isFile() || stat.isFIFO());
151
- if (fileCache[filename] === undefined) // parallel readFile() has been processed
152
- fileCache[filename] = body;
153
- traceFS( 'ISFILE:data:', filename, body );
154
- if (body instanceof Error)
155
- cb( err );
156
- else
157
- cb( null, body );
158
- });
151
+ try {
152
+ fsStat(filename, (err, stat) => {
153
+ if (err)
154
+ body = (err.code === 'ENOENT' || err.code === 'ENOTDIR') ? false : err;
155
+ else
156
+ body = !!(stat.isFile() || stat.isFIFO());
157
+ if (fileCache[filename] === undefined) // parallel readFile() has been processed
158
+ fileCache[filename] = body;
159
+ traceFS('ISFILE:data:', filename, body);
160
+ if (body instanceof Error)
161
+ cb(err);
162
+ else
163
+ cb(null, body);
164
+ });
165
+ }
166
+ catch (err) {
167
+ cb(err); // if filename is not a valid (e.g. contains NUL byte), fsStat() may throw
168
+ }
159
169
  }
160
170
  };
161
171
  }
@@ -25,7 +25,6 @@ const extensions = [ '.cds', '.csn', '.json' ];
25
25
  * @todo Re-think:
26
26
  * - Why can't a JAVA installation set a (symbolic) link?
27
27
  * - Preferred to a local installation? Not the node-way!
28
- * - Why a global? The Umbrella could pass it as an option.
29
28
  *
30
29
  * @param {string} modulePath
31
30
  * @param {CSN.Options} options
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-compiler",
3
- "version": "3.0.0",
3
+ "version": "3.1.2",
4
4
  "description": "CDS (Core Data Services) compiler and backends",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "author": "SAP SE (https://www.sap.com)",
@@ -16,12 +16,11 @@
16
16
  "scripts": {
17
17
  "download": "node scripts/downloadANTLR.js",
18
18
  "gen": "node ./scripts/build.js && node scripts/genGrammarChecksum.js",
19
- "xmakeBeforeInstall": "echo \"Due to binary mirror, use sqlite 5.0.8 explicitly\" && npm install --save --save-exact --no-package-lock sqlite3@5.0.8",
19
+ "xmakeBeforeInstall": "echo \"Due to binary mirror, use sqlite 5.0.11 explicitly\" && npm install --save --save-exact --no-package-lock sqlite3@5.0.11",
20
20
  "xmakeAfterInstall": "npm run gen",
21
21
  "xmakePrepareRelease": "echo \"$(node scripts/stripReadme.js README.md)\" > README.md && node scripts/assertSnapshotVersioning.js && node scripts/assertChangelog.js && node scripts/cleanup.js --remove-dev",
22
- "test": "node scripts/verifyGrammarChecksum.js && mocha --parallel --reporter min --reporter-option maxDiffSize=0 test/ test3/ && mocha scripts/testLazyLoading.js",
22
+ "test": "node scripts/verifyGrammarChecksum.js && mocha --reporter min --reporter-option maxDiffSize=0 scripts/testLazyLoading.js && mocha --parallel --reporter min --reporter-option maxDiffSize=0 test/ test3/",
23
23
  "testverbose": "node scripts/verifyGrammarChecksum.js && mocha --parallel test/ test3/",
24
- "testdot": "node scripts/verifyGrammarChecksum.js && mocha --parallel --reporter dot --reporter-option maxDiffSize=0 --full-trace scripts/linter/lintMessages.js test/ test3/",
25
24
  "test3": "node scripts/verifyGrammarChecksum.js && node scripts/linter/lintTests.js test3/ && mocha --reporter-option maxDiffSize=0 scripts/linter/lintMessages.js test3/",
26
25
  "deployTest3SQL": "deployRefs=true mocha --reporter-option maxDiffSize=0 test3/testHANASQLDeployment.js",
27
26
  "deployTest3": "deployRefs=true mocha --reporter-option maxDiffSize=0 test3/testDeployment.js",
@@ -1,12 +1,12 @@
1
1
  # syntax-expected-integer
2
2
 
3
3
  The compiler expects a safe integer here.
4
- The last safe Integer is `2^53 - 1` or `9007199254740991`.
4
+ The last safe integer is `2^53 - 1` or `9007199254740991`.
5
5
 
6
- A safe integer is an integer that
6
+ A safe integer is an integer that fulfills all of the following:
7
7
 
8
- - can be exactly represented as an IEEE-754 double precision number, and
9
- - whose IEEE-754 representation cannot be the result of rounding any
8
+ - Can be exactly represented as an IEEE-754 double precision number.
9
+ - The IEEE-754 representation cannot be the result of rounding any
10
10
  other integer to fit the IEEE-754 representation.
11
11
 
12
12
  The message's severity is `Error`.
@@ -15,13 +15,14 @@ The message's severity is `Error`.
15
15
 
16
16
  Erroneous code example:
17
17
 
18
+ <!-- cds-mode: ignore -->
18
19
  ```cdl
19
20
  type LengthIsUnsafe : String(9007199254740992);
20
21
  type NotAnInteger : String(42.1);
21
22
  ```
22
23
 
23
- In the above example, the string length for the type `LengthIsUnsafe` is not a
24
- safe Integer. It is too large.
24
+ In the erroneous example, the string length for the type `LengthIsUnsafe` is
25
+ not a safe integer. It is too large.
25
26
  Likewise, the string length for the type `NotAnInteger` is a decimal.
26
27
 
27
28
  ## How to Fix
@@ -33,5 +34,5 @@ type LengthIsSafe : String(9007199254740991);
33
34
  type AnInteger : String(42);
34
35
  ```
35
36
 
36
- If not feasible, a string representation of the number needs to be used,
37
- e.g. in annotation values.
37
+ If it's not feasible to use a safe integer, a string representation of the
38
+ number needs to be used, for example, in annotation values.
@@ -1,41 +0,0 @@
1
- 'use strict';
2
-
3
- const { getVariableReplacement } = require('../model/csnUtils');
4
-
5
- // We only care about the "wild" ones - $at is validated by the compiler
6
- const magicVariables = {
7
- $user: [
8
- 'id', // $user.id
9
- 'locale', // $user.locale
10
- ],
11
- $session: [
12
- // no valid ways for this
13
- ],
14
- };
15
-
16
- /**
17
- * Check that the given ref does not use magic variables for which we don't have
18
- * a valid way of rendering.
19
- *
20
- * Valid ways:
21
- * - We know what to do -> $user.id on HANA
22
- * - The user tells us what to do -> options.variableReplacements
23
- *
24
- * @param {object} parent Object with the ref as a property
25
- * @param {string} name Name of the ref property on parent
26
- * @param {Array} ref to check
27
- */
28
- function unknownMagicVariable(parent, name, ref) {
29
- if (parent.$scope && parent.$scope === '$magic') {
30
- const [ head, ...rest ] = ref;
31
- const tail = rest.join('.');
32
- const magicVariable = magicVariables[head];
33
- if (magicVariable && magicVariable.indexOf(tail) === -1 &&
34
- getVariableReplacement(ref, this.options) === null)
35
- this.error('ref-missing-replacement', parent.$location, { elemref: parent }, 'Missing replacement for variable $(ELEMREF)');
36
- }
37
- }
38
-
39
- module.exports = {
40
- ref: unknownMagicVariable,
41
- };