@sap/cds-compiler 5.6.0 → 5.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/bin/cdsse.js +1 -0
  3. package/bin/cdsv2m.js +2 -1
  4. package/doc/Versioning.md +4 -4
  5. package/lib/api/options.js +1 -0
  6. package/lib/base/builtins.js +2 -2
  7. package/lib/base/dictionaries.js +1 -2
  8. package/lib/base/keywords.js +3 -1
  9. package/lib/base/lazyload.js +1 -1
  10. package/lib/base/message-registry.js +169 -144
  11. package/lib/base/messages.js +69 -59
  12. package/lib/base/model.js +3 -3
  13. package/lib/base/node-helpers.js +17 -16
  14. package/lib/base/optionProcessorHelper.js +13 -14
  15. package/lib/base/shuffle.js +4 -1
  16. package/lib/checks/structuredAnnoExpressions.js +1 -1
  17. package/lib/compiler/assert-consistency.js +1 -1
  18. package/lib/compiler/builtins.js +2 -1
  19. package/lib/compiler/extend.js +20 -5
  20. package/lib/compiler/resolve.js +45 -9
  21. package/lib/compiler/shared.js +1 -0
  22. package/lib/edm/annotations/edmJson.js +3 -3
  23. package/lib/edm/annotations/genericTranslation.js +5 -1
  24. package/lib/edm/annotations/vocabularyDefinitions.js +2 -2
  25. package/lib/edm/edmUtils.js +2 -1
  26. package/lib/gen/BaseParser.js +32 -32
  27. package/lib/gen/CdlParser.js +1526 -1488
  28. package/lib/json/from-csn.js +2 -0
  29. package/lib/json/to-csn.js +13 -4
  30. package/lib/language/docCommentParser.js +11 -5
  31. package/lib/language/errorStrategy.js +3 -3
  32. package/lib/language/genericAntlrParser.js +2 -0
  33. package/lib/model/csnUtils.js +6 -1
  34. package/lib/optionProcessor.js +5 -1
  35. package/lib/parsers/AstBuildingParser.js +161 -73
  36. package/lib/parsers/CdlGrammar.g4 +129 -85
  37. package/lib/parsers/Lexer.js +5 -3
  38. package/lib/parsers/index.js +1 -1
  39. package/lib/render/toCdl.js +6 -5
  40. package/lib/render/toHdbcds.js +1 -1
  41. package/lib/render/toSql.js +5 -3
  42. package/lib/render/utils/common.js +19 -6
  43. package/lib/render/utils/delta.js +1 -3
  44. package/lib/render/utils/standardDatabaseFunctions.js +576 -0
  45. package/lib/transform/addTenantFields.js +2 -1
  46. package/lib/transform/db/flattening.js +18 -77
  47. package/lib/transform/db/groupByOrderBy.js +2 -2
  48. package/lib/transform/db/rewriteCalculatedElements.js +14 -19
  49. package/lib/transform/db/temporal.js +2 -1
  50. package/lib/transform/odata/adaptAnnotationRefs.js +79 -0
  51. package/lib/transform/odata/createForeignKeys.js +4 -71
  52. package/lib/transform/odata/flattening.js +11 -1
  53. package/lib/transform/transformUtils.js +20 -85
  54. package/package.json +2 -1
  55. package/bin/cds_update_annotations.js +0 -180
@@ -0,0 +1,576 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * List of functions for which we provide a mapping to the respective SQL dialect.
5
+ * All functions are lowercase, the caller may treat the function name case-insensitive.
6
+ *
7
+ * The `this` context within the functions hold the `renderArgs` function.
8
+ */
9
+ const oDataFunctions = {
10
+ // https://www.sqlite.org/lang_corefunc.html
11
+ sqlite: {
12
+ contains(signature) {
13
+ const { args } = signature;
14
+ checkArgs.call(this, 'contains', args, 2);
15
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
16
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
17
+ return `(ifnull(instr(${x}, ${y}),0) <> 0)`;
18
+ },
19
+ startswith(signature) {
20
+ const { args } = signature;
21
+ checkArgs.call(this, 'startswith', args, 2);
22
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
23
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
24
+ return `coalesce((instr(${x}, ${y}) = 1), false)`;
25
+ }, // instr is 1 indexed
26
+ endswith(signature) {
27
+ const { args } = signature;
28
+ checkArgs.call(this, 'endswith', args, 2);
29
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
30
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
31
+ return `coalesce((substr(${x}, length(${x}) + 1 - length(${y})) = ${y}), false)`;
32
+ },
33
+ indexof(signature) {
34
+ const { args } = signature;
35
+ checkArgs.call(this, 'indexof', args, 2);
36
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
37
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
38
+ return `(instr(${x}, ${y}) - 1)`; // instr is 1 indexed
39
+ },
40
+ matchespattern(signature) {
41
+ const { args } = signature;
42
+ checkArgs.call(this, 'matchespattern', args, 2);
43
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
44
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
45
+ return `cast((${x} regexp ${y}) as INTEGER)`; // this is a udf, sqlite always returns a REAL w/o the cast
46
+ },
47
+ year(signature) {
48
+ const { args } = signature;
49
+ checkArgs.call(this, 'year', args, 1);
50
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
51
+ return `cast(strftime('%Y', ${x}) as Integer)`;
52
+ },
53
+ month(signature) {
54
+ const { args } = signature;
55
+ checkArgs.call(this, 'month', args, 1);
56
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
57
+ return `cast(strftime('%m', ${x}) as Integer)`;
58
+ },
59
+ day(signature) {
60
+ const { args } = signature;
61
+ checkArgs.call(this, 'day', args, 1);
62
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
63
+ return `cast(strftime('%d', ${x}) as Integer)`;
64
+ },
65
+ hour(signature) {
66
+ const { args } = signature;
67
+ checkArgs.call(this, 'hour', args, 1);
68
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
69
+ return `cast(strftime('%H', ${x}) as Integer)`;
70
+ },
71
+ minute(signature) {
72
+ const { args } = signature;
73
+ checkArgs.call(this, 'minute', args, 1);
74
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
75
+ return `cast(strftime('%M', ${x}) as Integer)`;
76
+ },
77
+ second(signature) {
78
+ const { args } = signature;
79
+ checkArgs.call(this, 'second', args, 1);
80
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
81
+ return `cast(strftime('%S', ${x}) as Integer)`;
82
+ },
83
+ // REVISIT: currently runtimes normalize to milliseconds
84
+ // we could allow this to be more precise
85
+ fractionalseconds(signature) {
86
+ const { args } = signature;
87
+ checkArgs.call(this, 'fractionalseconds', args, 1);
88
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
89
+ return `cast(substr(strftime('%f', ${x}), length(strftime('%f', ${x})) - 3) as REAL)`;
90
+ },
91
+ // The date(), time(), and datetime() functions all return text, and so their strftime() equivalents are exact.
92
+ time(signature) {
93
+ const { args } = signature;
94
+ checkArgs.call(this, 'time', args, 1);
95
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
96
+ return `time(${x})`;
97
+ },
98
+ date(signature) {
99
+ const { args } = signature;
100
+ checkArgs.call(this, 'date', args, 1);
101
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
102
+ return `date(${x}) `;
103
+ },
104
+ // this could also be a negative number
105
+ // also, parts of the EDM.duration are optional which complicates
106
+ // the implementation on SQL level. As the parameter may be an element
107
+ // reference, we must do splitting and casting in the SQL as well as
108
+ // considering the case where the duration is negative.
109
+ // --> We do not support this function.
110
+ // totalseconds(signature) {
111
+ // const { args } = signature;
112
+ // checkArgs.call(this, 'totalseconds', args, 1);
113
+ // let x = this.renderArgs({ ...signature, args: [ args[0] ] });
114
+ // const isNegative = x.startsWith("'-"); // Check for leading '-'
115
+ // x = isNegative ? x.replace('-', '') : x; // remove for easier processing
116
+ // const sql = `((cast(substr(${x},2,instr(${x},'DT') - 2) as Integer) + (julianday('-4713-11-25T' || replace(replace(replace(substr(${x},instr(${x},'DT') + 2),'H',':'),'M',':'),'S','Z')) - 0.5)) * 86400)`;
117
+ // return isNegative ? `-(${sql})` : sql;
118
+ // },
119
+ },
120
+ // https://www.postgresql.org/docs/current/functions-string.html
121
+ // https://www.postgresql.org/docs/current/functions-math.html
122
+ postgres: {
123
+ contains(signature) {
124
+ const { args } = signature;
125
+ checkArgs.call(this, 'contains', args, 2);
126
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
127
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
128
+ return `(coalesce(strpos(${x}, ${y}),0) > 0)`;
129
+ },
130
+ startswith(signature) {
131
+ const { args } = signature;
132
+ checkArgs.call(this, 'startswith', args, 2);
133
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
134
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
135
+ return `coalesce((strpos(${x}, ${y}) = 1), false)`; // strpos is 1 indexed
136
+ },
137
+ endswith(signature) {
138
+ const { args } = signature;
139
+ checkArgs.call(this, 'endswith', args, 2);
140
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
141
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
142
+ return `coalesce((substr(${x}, (length(${x}) + 1) - length(${y})) = ${y}), false)`;
143
+ },
144
+ indexof(signature) {
145
+ const { args } = signature;
146
+ checkArgs.call(this, 'indexof', args, 2);
147
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
148
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
149
+ return `(strpos(${x}, ${y}) - 1)`; // strpos is 1 indexed
150
+ },
151
+ matchespattern(signature) {
152
+ const { args } = signature;
153
+ checkArgs.call(this, 'matchespattern', args, 2);
154
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
155
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
156
+ return `regexp_like(${x}, ${y})`;
157
+ },
158
+ // TODO: PG docu recommends to use the "EXTRACT" function for improved precision
159
+ // https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
160
+ year(signature) {
161
+ const { args } = signature;
162
+ checkArgs.call(this, 'year', args, 1);
163
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
164
+ return `cast(date_part('year', ${x}) as Integer)`;
165
+ },
166
+ month(signature) {
167
+ const { args } = signature;
168
+ checkArgs.call(this, 'month', args, 1);
169
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
170
+ return `cast(date_part('month', ${x}) as Integer)`;
171
+ },
172
+ day(signature) {
173
+ const { args } = signature;
174
+ checkArgs.call(this, 'day', args, 1);
175
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
176
+ return `cast(date_part('day', ${x}) as Integer)`;
177
+ },
178
+ hour(signature) {
179
+ const { args } = signature;
180
+ checkArgs.call(this, 'hour', args, 1);
181
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
182
+ return `cast(date_part('hour', ${x}) as Integer)`;
183
+ },
184
+ minute(signature) {
185
+ const { args } = signature;
186
+ checkArgs.call(this, 'minute', args, 1);
187
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
188
+ return `cast(date_part('minute', ${x}) as Integer)`;
189
+ },
190
+ second(signature) {
191
+ const { args } = signature;
192
+ checkArgs.call(this, 'second', args, 1);
193
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
194
+ return `cast(floor(date_part('second', ${x})) as Integer)`;
195
+ },
196
+ // REVISIT: currently runtimes normalize to milliseconds
197
+ // we could allow this to be more precise
198
+ fractionalseconds(signature) {
199
+ const { args } = signature;
200
+ checkArgs.call(this, 'fractionalseconds', args, 1);
201
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
202
+ return `CAST(date_part('second', ${x}) - floor(date_part('second', ${x})) AS DECIMAL(3,3))`;
203
+ },
204
+ time(signature) {
205
+ const { args } = signature;
206
+ checkArgs.call(this, 'time', args, 1);
207
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
208
+ return `to_char(${x}, 'HH24:MI:SS')`;
209
+ },
210
+ date(signature) {
211
+ const { args } = signature;
212
+ checkArgs.call(this, 'date', args, 1);
213
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
214
+ return `to_char(${x}, 'YYYY-MM-DD')`;
215
+ },
216
+ },
217
+ // https://help.sap.com/docs/HANA_SERVICE_CF/7c78579ce9b14a669c1f3295b0d8ca16/f12b86a6284c4aeeb449e57eb5dd3ebd.html?locale=en-US
218
+ hana: {
219
+ contains(signature) {
220
+ const { args } = signature;
221
+ checkArgs.call(this, 'contains', args, 2, 3);
222
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
223
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
224
+ if (signature.args.length > 2) {
225
+ const z = this.renderArgs({ ...signature, args: [ args[2] ] });
226
+ // While CONTAINS() looks like a function because of its syntax,
227
+ // it is classified as a predicate because it is designed to evaluate a condition
228
+ // and return a Boolean result.
229
+ return `CONTAINS(${x}, ${y}, ${z})`;
230
+ }
231
+
232
+ return `(CASE WHEN coalesce(locate(${this.renderArgs(signature)}),0)>0 THEN TRUE ELSE FALSE END)`;
233
+ },
234
+ startswith(signature) {
235
+ const { args } = signature;
236
+ checkArgs.call(this, 'startswith', args, 2);
237
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
238
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
239
+ return `(CASE WHEN locate(${x}, ${y}) = 1 THEN TRUE ELSE FALSE END)`;
240
+ }, // locate is 1 indexed
241
+ endswith(signature) {
242
+ const { args } = signature;
243
+ checkArgs.call(this, 'endswith', args, 2);
244
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
245
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
246
+ return `(CASE WHEN substring(${x}, (length(${x}) + 1) - length(${y})) = ${y} THEN TRUE ELSE FALSE END)`;
247
+ },
248
+ indexof(signature) {
249
+ const { args } = signature;
250
+ checkArgs.call(this, 'indexof', args, 2);
251
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
252
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
253
+ return `(locate(${x}, ${y}) - 1)`; // locate is 1 indexed
254
+ },
255
+ matchespattern(signature) {
256
+ // case … when only works as column expression (not in where)
257
+ // in the where clause, only "${x} LIKE_REGEXPR ${y}" works
258
+ const { args } = signature;
259
+ checkArgs.call(this, 'matchespattern', args, 2);
260
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
261
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
262
+ return `(CASE WHEN ${x} LIKE_REGEXPR ${y} THEN TRUE ELSE FALSE END)`;
263
+ },
264
+ year(signature) {
265
+ const { args } = signature;
266
+ checkArgs.call(this, 'year', args, 1);
267
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
268
+ return `year(${x})`;
269
+ },
270
+ month(signature) {
271
+ const { args } = signature;
272
+ checkArgs.call(this, 'month', args, 1);
273
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
274
+ return `month(${x})`;
275
+ },
276
+ day(signature) {
277
+ const { args } = signature;
278
+ checkArgs.call(this, 'day', args, 1);
279
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
280
+ return `dayofmonth(${x})`;
281
+ },
282
+ hour(signature) {
283
+ const { args } = signature;
284
+ checkArgs.call(this, 'hour', args, 1);
285
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
286
+ return `hour(${x})`;
287
+ },
288
+ minute(signature) {
289
+ const { args } = signature;
290
+ checkArgs.call(this, 'minute', args, 1);
291
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
292
+ return `minute(${x})`;
293
+ },
294
+ second(signature) {
295
+ const { args } = signature;
296
+ checkArgs.call(this, 'second', args, 1);
297
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
298
+ return `to_integer(second(${x}))`;
299
+ },
300
+ // REVISIT: currently runtimes normalize to milliseconds
301
+ // we could allow this to be more precise
302
+ fractionalseconds(signature) {
303
+ const { args } = signature;
304
+ checkArgs.call(this, 'fractionalseconds', args, 1);
305
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
306
+ return `(to_decimal(second(${x}),5,3) - to_integer(second(${x})))`;
307
+ },
308
+ time(signature) {
309
+ const { args } = signature;
310
+ checkArgs.call(this, 'time', args, 1);
311
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
312
+ return `cast(to_time(${x}) AS NVARCHAR)`;
313
+ },
314
+ date(signature) {
315
+ const { args } = signature;
316
+ checkArgs.call(this, 'date', args, 1);
317
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
318
+ return `cast(to_date(${x}) AS NVARCHAR)`;
319
+ },
320
+ },
321
+ // https://www.h2database.com/html/functions.html
322
+ h2: {
323
+ contains(signature) {
324
+ const args = [ ...signature.args ];
325
+ checkArgs.call(this, 'contains', args, 2);
326
+ // defined as { LOCATE(searchString, string [, startInt]) }
327
+ args.reverse();
328
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
329
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
330
+ return `(coalesce(locate(${x}, ${y}),0) > 0)`;
331
+ },
332
+ startswith(signature) {
333
+ const args = [ ...signature.args ];
334
+ checkArgs.call(this, 'startswith', args, 2);
335
+ // defined as { LOCATE(searchString, string [, startInt]) }
336
+ args.reverse();
337
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
338
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
339
+ return `coalesce((locate(${x}, ${y}) = 1), false)`; // locate is 1 indexed
340
+ },
341
+ endswith(signature) {
342
+ const { args } = signature;
343
+ checkArgs.call(this, 'endswith', args, 2);
344
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
345
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
346
+ return `coalesce((substring(${x} FROM (char_length(${x}) + 1) - char_length(${y})) = ${y}), false)`;
347
+ },
348
+ substring(signature) {
349
+ const { args } = signature;
350
+ checkArgs.call(this, 'substring', args, 2, 3);
351
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
352
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
353
+ const z = args[2]
354
+ ? this.renderArgs({ ...signature, args: [ args[2] ] })
355
+ : null;
356
+ return z
357
+ ? `substring(${x} FROM CASE WHEN ${y} < 0 THEN char_length(${x}) + ${y} + 1 ELSE ${y} + 1 END FOR ${z})`
358
+ : `substring(${x} FROM CASE WHEN ${y} < 0 THEN char_length(${x}) + ${y} + 1 ELSE ${y} + 1 END)`;
359
+ },
360
+ // char_length is preferred over length -> REVISIT: returns a BIGINT, is this ok?
361
+ // https://www.h2database.com/html/functions.html#char_length
362
+ length(signature) {
363
+ const { args } = signature;
364
+ checkArgs.call(this, 'length', args, 1);
365
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
366
+ return `cast(char_length(${x}) as Integer)`;
367
+ },
368
+ indexof(signature) {
369
+ const args = [ ...signature.args ];
370
+ checkArgs.call(this, 'indexof', args, 2);
371
+ // defined as { LOCATE(searchString, string [, startInt]) }
372
+ args.reverse();
373
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
374
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
375
+ return `(locate(${x}, ${y}) - 1)`; // locate is 1 indexed
376
+ },
377
+ matchespattern(signature) {
378
+ const { args } = signature;
379
+ checkArgs.call(this, 'matchespattern', args, 2);
380
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
381
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
382
+ return `regexp_like(${x}, ${y})`;
383
+ },
384
+ year(signature) {
385
+ const { args } = signature;
386
+ checkArgs.call(this, 'year', args, 1);
387
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
388
+ return `extract(YEAR FROM ${x})`;
389
+ },
390
+ month(signature) {
391
+ const { args } = signature;
392
+ checkArgs.call(this, 'month', args, 1);
393
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
394
+ return `extract(MONTH FROM ${x})`;
395
+ },
396
+ day(signature) {
397
+ const { args } = signature;
398
+ checkArgs.call(this, 'day', args, 1);
399
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
400
+ return `extract(DAY FROM ${x})`;
401
+ },
402
+ hour(signature) {
403
+ const { args } = signature;
404
+ checkArgs.call(this, 'hour', args, 1);
405
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
406
+ return `extract(HOUR FROM ${x})`;
407
+ },
408
+ minute(signature) {
409
+ const { args } = signature;
410
+ checkArgs.call(this, 'minute', args, 1);
411
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
412
+ return `extract(MINUTE FROM ${x})`;
413
+ },
414
+ second(signature) {
415
+ const { args } = signature;
416
+ checkArgs.call(this, 'second', args, 1);
417
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
418
+ return `extract(SECOND FROM ${x})`;
419
+ },
420
+ // REVISIT: currently runtimes normalize to milliseconds
421
+ // we could allow this to be more precise
422
+ fractionalseconds(signature) {
423
+ const { args } = signature;
424
+ checkArgs.call(this, 'fractionalseconds', args, 1);
425
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
426
+ return `cast(extract(MILLISECOND FROM ${x}) / 1000.0 AS NUMERIC(3,3))`;
427
+ },
428
+ time(signature) {
429
+ const { args } = signature;
430
+ checkArgs.call(this, 'time', args, 1);
431
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
432
+ return `cast(cast(${x} AS TIME) AS VARCHAR)`;
433
+ },
434
+ date(signature) {
435
+ const { args } = signature;
436
+ checkArgs.call(this, 'date', args, 1);
437
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
438
+ return `cast(cast(${x} AS DATE) AS VARCHAR)`;
439
+ },
440
+ },
441
+ common: {
442
+ concat(signature) {
443
+ const separator = '||';
444
+ const args = signature.args.reduce((acc, current, index) => {
445
+ if (index > 0)
446
+ acc.push(separator);
447
+
448
+ acc.push(current);
449
+ return acc;
450
+ }, []);
451
+ const res = this.renderArgs({ signature, ...{ args: [ args ] } });
452
+ return `(${res})`;
453
+ },
454
+ ceiling(signature) {
455
+ const { args } = signature;
456
+ checkArgs.call(this, 'ceiling', args, 1);
457
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
458
+ return `ceil(${x})`;
459
+ },
460
+ floor(signature) {
461
+ const { args } = signature;
462
+ checkArgs.call(this, 'floor', args, 1);
463
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
464
+ return `floor(${x})`;
465
+ },
466
+ trim(signature) {
467
+ const { args } = signature;
468
+ checkArgs.call(this, 'trim', args, 1);
469
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
470
+ return `trim(${x})`;
471
+ },
472
+ // SAP HANA, sqlite and postgres share the same implementation
473
+ substring(signature) {
474
+ const { args } = signature;
475
+ checkArgs.call(this, 'substring', args, 2, 3);
476
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
477
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
478
+ const z = args[2]
479
+ ? this.renderArgs({ ...signature, args: [ args[2] ] })
480
+ : null;
481
+ return z
482
+ ? `substr(${x}, CASE WHEN ${y} < 0 THEN length(${x}) + ${y} + 1 ELSE ${y} + 1 END, ${z})`
483
+ : `substr(${x}, CASE WHEN ${y} < 0 THEN length(${x}) + ${y} + 1 ELSE ${y} + 1 END)`;
484
+ },
485
+ min(signature) {
486
+ const { args } = signature;
487
+ checkArgs.call(this, 'min', args, 1);
488
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
489
+ return `min(${x})`;
490
+ },
491
+ max(signature) {
492
+ const { args } = signature;
493
+ checkArgs.call(this, 'max', args, 1);
494
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
495
+ return `max(${x})`;
496
+ },
497
+ sum(signature) {
498
+ const { args } = signature;
499
+ checkArgs.call(this, 'sum', args, 1);
500
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
501
+ return `sum(${x})`;
502
+ },
503
+ count(signature) {
504
+ const { args } = signature;
505
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
506
+ return `count(${x || '*'})`;
507
+ },
508
+ countdistinct(signature) {
509
+ const { args } = signature;
510
+ return `count(distinct ${args.length > 0 ? this.renderArgs(signature) : "'*'"})`;
511
+ },
512
+ average(signature) {
513
+ const { args } = signature;
514
+ checkArgs.call(this, 'average', args, 1);
515
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
516
+ return `avg(${x})`;
517
+ },
518
+ length(signature) {
519
+ const { args } = signature;
520
+ checkArgs.call(this, 'length', args, 1);
521
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
522
+ return `length(${x})`;
523
+ },
524
+ tolower(signature) {
525
+ const { args } = signature;
526
+ checkArgs.call(this, 'tolower', args, 1);
527
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
528
+ return `lower(${x})`;
529
+ },
530
+ toupper(signature) {
531
+ const { args } = signature;
532
+ checkArgs.call(this, 'toupper', args, 1);
533
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
534
+ return `upper(${x})`;
535
+ },
536
+ // eslint-disable-next-line no-unused-vars
537
+ maxdatetime(signature) {
538
+ return "'9999-12-31T23:59:59.999Z'";
539
+ },
540
+ // eslint-disable-next-line no-unused-vars
541
+ mindatetime(signature) {
542
+ return "'0001-01-01T00:00:00.000Z'";
543
+ },
544
+ },
545
+ };
546
+
547
+ // TODO: add support for the common SAP HANA Functions
548
+ const hanaFunctions = {
549
+ sqlite: {},
550
+ postgres: {},
551
+ hana: { /* no-op */ },
552
+ h2: {},
553
+ common: {},
554
+ };
555
+
556
+ function checkArgs( funcName, receivedArgs, expectedLength, alternativeLength = null ) {
557
+ const expectedMismatch = receivedArgs.length < expectedLength;
558
+ const alternativeMismatch = expectedMismatch && (!alternativeLength || alternativeLength && receivedArgs.length < alternativeLength);
559
+ if (expectedMismatch && alternativeMismatch) {
560
+ this.error('def-missing-argument', [ ...this.path, 'args' ], {
561
+ '#': alternativeLength ? 'alternative' : 'std',
562
+ n: expectedLength,
563
+ m: alternativeLength,
564
+ literal: receivedArgs.length,
565
+ name: funcName,
566
+ });
567
+ }
568
+ };
569
+
570
+ module.exports.standardDatabaseFunctions = {
571
+ sqlite: { ...oDataFunctions.sqlite, ...hanaFunctions.sqlite },
572
+ postgres: { ...oDataFunctions.postgres, ...hanaFunctions.postgres },
573
+ hana: { ...oDataFunctions.hana, ...hanaFunctions.hana },
574
+ h2: { ...oDataFunctions.h2, ...hanaFunctions.h2 },
575
+ common: { ...oDataFunctions.common },
576
+ };
@@ -331,7 +331,8 @@ function addTenantFields( csn, options, messageFunctions ) {
331
331
  * Type references are followed, but only without sibling `elements` or `items`.
332
332
  */
333
333
  function typeDependency( assoc ) {
334
- if (!assoc || !(assoc = effectiveType( assoc )))
334
+ assoc = assoc ? effectiveType( assoc ) : assoc;
335
+ if (!assoc)
335
336
  return '';
336
337
  const assocDep = typeCache.get( assoc );
337
338
  if (assocDep != null)