@sap/cds-compiler 6.7.3 → 6.9.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 (116) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/README.md +4 -0
  3. package/bin/cdsc.js +5 -5
  4. package/bin/cdshi.js +1 -0
  5. package/bin/cdsse.js +1 -1
  6. package/lib/api/main.js +17 -9
  7. package/lib/api/options.js +5 -2
  8. package/lib/api/validate.js +1 -1
  9. package/lib/base/builtins.js +13 -9
  10. package/lib/{model → base}/csnRefs.js +8 -10
  11. package/lib/base/error.js +2 -0
  12. package/lib/base/message-registry.js +68 -4
  13. package/lib/base/messages.js +4 -2
  14. package/lib/{optionProcessor.js → base/optionProcessor.js} +5 -3
  15. package/lib/base/{model.js → specialOptions.js} +16 -39
  16. package/lib/checks/arrayOfs.js +1 -1
  17. package/lib/checks/elements.js +1 -1
  18. package/lib/checks/enricher.js +2 -2
  19. package/lib/checks/featureFlags.js +54 -24
  20. package/lib/checks/foreignKeys.js +1 -1
  21. package/lib/checks/invalidTarget.js +1 -1
  22. package/lib/checks/managedInType.js +1 -1
  23. package/lib/checks/onConditions.js +1 -1
  24. package/lib/checks/queryNoDbArtifacts.js +1 -1
  25. package/lib/checks/validator.js +10 -14
  26. package/lib/compiler/assert-consistency.js +11 -9
  27. package/lib/compiler/base.js +5 -1
  28. package/lib/compiler/builtins.js +1 -1
  29. package/lib/compiler/checks.js +3 -3
  30. package/lib/compiler/define.js +6 -3
  31. package/lib/{base → compiler}/dictionaries.js +4 -3
  32. package/lib/compiler/extend.js +121 -21
  33. package/lib/compiler/generate.js +2 -2
  34. package/lib/compiler/index.js +11 -3
  35. package/lib/compiler/kick-start.js +1 -1
  36. package/lib/compiler/lsp-api.js +3 -3
  37. package/lib/compiler/populate.js +6 -7
  38. package/lib/compiler/resolve.js +53 -36
  39. package/lib/compiler/shared.js +68 -18
  40. package/lib/compiler/tweak-assocs.js +2 -2
  41. package/lib/compiler/utils.js +28 -27
  42. package/lib/compiler/xpr-rewrite.js +3 -3
  43. package/lib/edm/EdmPrimitiveTypeDefinitions.js +4 -1
  44. package/lib/edm/annotations/edmJson.js +2 -4
  45. package/lib/edm/annotations/genericTranslation.js +51 -7
  46. package/lib/edm/csn2edm.js +3 -2
  47. package/lib/edm/edmAnnoPreprocessor.js +1 -1
  48. package/lib/edm/edmInboundChecks.js +2 -1
  49. package/lib/edm/edmPreprocessor.js +3 -3
  50. package/lib/edm/edmUtils.js +2 -2
  51. package/lib/gen/BaseParser.js +59 -108
  52. package/lib/gen/CdlGrammar.checksum +1 -1
  53. package/lib/gen/CdlParser.js +2052 -1965
  54. package/lib/gen/Dictionary.json +67 -7
  55. package/lib/json/from-csn.js +14 -14
  56. package/lib/json/to-csn.js +77 -38
  57. package/lib/main.js +3 -3
  58. package/lib/model/csnUtils.js +2 -2
  59. package/lib/modelCompare/compare.js +1 -1
  60. package/lib/modelCompare/utils/filter.js +1 -0
  61. package/lib/parsers/AstBuildingParser.js +83 -33
  62. package/lib/parsers/index.js +1 -1
  63. package/lib/render/manageConstraints.js +1 -1
  64. package/lib/render/toCdl.js +49 -30
  65. package/lib/render/toHdbcds.js +2 -2
  66. package/lib/render/toSql.js +16 -7
  67. package/lib/render/utils/common.js +11 -3
  68. package/lib/render/utils/sql.js +14 -5
  69. package/lib/render/utils/standardDatabaseFunctions.js +108 -99
  70. package/lib/sql-identifier.js +9 -1
  71. package/lib/{model → tool-lib}/enrichCsn.js +3 -2
  72. package/lib/{model → tool-lib}/revealInternalProperties.js +2 -1
  73. package/lib/transform/addTenantFields.js +1 -1
  74. package/lib/transform/db/applyTransformations.js +1 -1
  75. package/lib/transform/db/assertUnique.js +1 -1
  76. package/lib/transform/db/assocsToQueries/transformExists.js +1 -1
  77. package/lib/transform/db/backlinks.js +2 -2
  78. package/lib/transform/db/expansion.js +2 -2
  79. package/lib/transform/db/flattening.js +3 -4
  80. package/lib/transform/db/killAnnotations.js +1 -0
  81. package/lib/transform/db/processSqlServices.js +2 -1
  82. package/lib/transform/db/rewriteCalculatedElements.js +2 -2
  83. package/lib/transform/db/temporal.js +30 -5
  84. package/lib/transform/db/views.js +16 -20
  85. package/lib/transform/draft/db.js +1 -2
  86. package/lib/transform/effective/associations.js +1 -1
  87. package/lib/transform/effective/flattening.js +6 -5
  88. package/lib/transform/effective/main.js +24 -4
  89. package/lib/transform/effective/types.js +1 -1
  90. package/lib/transform/{odata/fioriTreeViews.js → fioriTreeViews.js} +48 -25
  91. package/lib/transform/forOdata.js +25 -7
  92. package/lib/transform/forRelationalDB.js +48 -12
  93. package/lib/transform/localized.js +2 -2
  94. package/lib/transform/odata/createForeignKeys.js +1 -1
  95. package/lib/transform/odata/flattening.js +2 -2
  96. package/lib/transform/odata/toFinalBaseType.js +3 -2
  97. package/lib/transform/odata/typesExposure.js +3 -2
  98. package/lib/transform/transformUtils.js +2 -2
  99. package/lib/transform/translateAssocsToJoins.js +2 -1
  100. package/lib/transform/tupleExpansion.js +44 -4
  101. package/lib/transform/universalCsn/universalCsnEnricher.js +7 -3
  102. package/lib/transform/universalCsn/utils.js +1 -1
  103. package/lib/{base → utils}/lazyload.js +9 -0
  104. package/lib/{base → utils}/node-helpers.js +2 -0
  105. package/lib/utils/objectUtils.js +29 -6
  106. package/lib/{base → utils}/optionProcessorHelper.js +16 -6
  107. package/package.json +3 -40
  108. /package/lib/{model → base}/cloneCsn.js +0 -0
  109. /package/lib/{model/api.js → base/model-api.js} +0 -0
  110. /package/lib/{api → base}/trace.js +0 -0
  111. /package/lib/{model → base}/xprAsTree.js +0 -0
  112. /package/lib/{inspect → tool-lib}/index.js +0 -0
  113. /package/lib/{inspect → tool-lib}/inspectModelStatistics.js +0 -0
  114. /package/lib/{inspect → tool-lib}/inspectPropagation.js +0 -0
  115. /package/lib/{inspect → tool-lib}/inspectUtils.js +0 -0
  116. /package/lib/{base → utils}/shuffle.js +0 -0
@@ -10,35 +10,35 @@ const oDataFunctions = {
10
10
  // https://www.sqlite.org/lang_corefunc.html
11
11
  sqlite: {
12
12
  contains(signature) {
13
- const { args } = signature;
13
+ const args = getArgs(signature);
14
14
  checkArgs.call(this, 'contains', args, 2);
15
15
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
16
16
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
17
17
  return `(ifnull(instr(${ x }, ${ y }),0) <> 0)`;
18
18
  },
19
19
  startswith(signature) {
20
- const { args } = signature;
20
+ const args = getArgs(signature);
21
21
  checkArgs.call(this, 'startswith', args, 2);
22
22
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
23
23
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
24
24
  return `coalesce((instr(${ x }, ${ y }) = 1), false)`;
25
25
  }, // instr is 1 indexed
26
26
  endswith(signature) {
27
- const { args } = signature;
27
+ const args = getArgs(signature);
28
28
  checkArgs.call(this, 'endswith', args, 2);
29
29
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
30
30
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
31
31
  return `coalesce((substr(${ x }, length(${ x }) + 1 - length(${ y })) = ${ y }), false)`;
32
32
  },
33
33
  indexof(signature) {
34
- const { args } = signature;
34
+ const args = getArgs(signature);
35
35
  checkArgs.call(this, 'indexof', args, 2);
36
36
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
37
37
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
38
38
  return `(instr(${ x }, ${ y }) - 1)`; // instr is 1 indexed
39
39
  },
40
40
  matchespattern(signature) {
41
- const { args } = signature;
41
+ const args = getArgs(signature);
42
42
  checkArgs.call(this, 'matchespattern', args, 2);
43
43
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
44
44
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -48,37 +48,37 @@ const oDataFunctions = {
48
48
  return oDataFunctions.sqlite.matchespattern.call(this, signature);
49
49
  },
50
50
  year(signature) {
51
- const { args } = signature;
51
+ const args = getArgs(signature);
52
52
  checkArgs.call(this, 'year', args, 1);
53
53
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
54
54
  return `cast(strftime('%Y', ${ x }) as Integer)`;
55
55
  },
56
56
  month(signature) {
57
- const { args } = signature;
57
+ const args = getArgs(signature);
58
58
  checkArgs.call(this, 'month', args, 1);
59
59
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
60
60
  return `cast(strftime('%m', ${ x }) as Integer)`;
61
61
  },
62
62
  day(signature) {
63
- const { args } = signature;
63
+ const args = getArgs(signature);
64
64
  checkArgs.call(this, 'day', args, 1);
65
65
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
66
66
  return `cast(strftime('%d', ${ x }) as Integer)`;
67
67
  },
68
68
  hour(signature) {
69
- const { args } = signature;
69
+ const args = getArgs(signature);
70
70
  checkArgs.call(this, 'hour', args, 1);
71
71
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
72
72
  return `cast(strftime('%H', ${ x }) as Integer)`;
73
73
  },
74
74
  minute(signature) {
75
- const { args } = signature;
75
+ const args = getArgs(signature);
76
76
  checkArgs.call(this, 'minute', args, 1);
77
77
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
78
78
  return `cast(strftime('%M', ${ x }) as Integer)`;
79
79
  },
80
80
  second(signature) {
81
- const { args } = signature;
81
+ const args = getArgs(signature);
82
82
  checkArgs.call(this, 'second', args, 1);
83
83
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
84
84
  return `cast(strftime('%S', ${ x }) as Integer)`;
@@ -86,20 +86,20 @@ const oDataFunctions = {
86
86
  // REVISIT: currently runtimes normalize to milliseconds
87
87
  // we could allow this to be more precise
88
88
  fractionalseconds(signature) {
89
- const { args } = signature;
89
+ const args = getArgs(signature);
90
90
  checkArgs.call(this, 'fractionalseconds', args, 1);
91
91
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
92
92
  return `cast(substr(strftime('%f', ${ x }), length(strftime('%f', ${ x })) - 3) as REAL)`;
93
93
  },
94
94
  // The date(), time(), and datetime() functions all return text, and so their strftime() equivalents are exact.
95
95
  time(signature) {
96
- const { args } = signature;
96
+ const args = getArgs(signature);
97
97
  checkArgs.call(this, 'time', args, 1);
98
98
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
99
99
  return `time(${ x })`;
100
100
  },
101
101
  date(signature) {
102
- const { args } = signature;
102
+ const args = getArgs(signature);
103
103
  checkArgs.call(this, 'date', args, 1);
104
104
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
105
105
  return `date(${ x })`;
@@ -111,7 +111,7 @@ const oDataFunctions = {
111
111
  // considering the case where the duration is negative.
112
112
  // --> We do not support this function.
113
113
  // totalseconds(signature) {
114
- // const { args } = signature;
114
+ // const args = getArgs(signature);
115
115
  // checkArgs.call(this, 'totalseconds', args, 1);
116
116
  // let x = this.renderArgs({ ...signature, args: [ args[0] ] });
117
117
  // const isNegative = x.startsWith("'-"); // Check for leading '-'
@@ -124,35 +124,35 @@ const oDataFunctions = {
124
124
  // https://www.postgresql.org/docs/current/functions-math.html
125
125
  postgres: {
126
126
  contains(signature) {
127
- const { args } = signature;
127
+ const args = getArgs(signature);
128
128
  checkArgs.call(this, 'contains', args, 2);
129
129
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
130
130
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
131
131
  return `(coalesce(strpos(${ x }, ${ y }),0) > 0)`;
132
132
  },
133
133
  startswith(signature) {
134
- const { args } = signature;
134
+ const args = getArgs(signature);
135
135
  checkArgs.call(this, 'startswith', args, 2);
136
136
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
137
137
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
138
138
  return `coalesce((strpos(${ x }, ${ y }) = 1), false)`; // strpos is 1 indexed
139
139
  },
140
140
  endswith(signature) {
141
- const { args } = signature;
141
+ const args = getArgs(signature);
142
142
  checkArgs.call(this, 'endswith', args, 2);
143
143
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
144
144
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
145
145
  return `coalesce((substr(${ x }, (length(${ x }) + 1) - length(${ y })) = ${ y }), false)`;
146
146
  },
147
147
  indexof(signature) {
148
- const { args } = signature;
148
+ const args = getArgs(signature);
149
149
  checkArgs.call(this, 'indexof', args, 2);
150
150
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
151
151
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
152
152
  return `(strpos(${ x }, ${ y }) - 1)`; // strpos is 1 indexed
153
153
  },
154
154
  matchespattern(signature) {
155
- const { args } = signature;
155
+ const args = getArgs(signature);
156
156
  checkArgs.call(this, 'matchespattern', args, 2);
157
157
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
158
158
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -164,37 +164,37 @@ const oDataFunctions = {
164
164
  // TODO: PG docu recommends to use the "EXTRACT" function for improved precision
165
165
  // https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-EXTRACT
166
166
  year(signature) {
167
- const { args } = signature;
167
+ const args = getArgs(signature);
168
168
  checkArgs.call(this, 'year', args, 1);
169
169
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
170
170
  return `cast(date_part('year', ${ x }) as Integer)`;
171
171
  },
172
172
  month(signature) {
173
- const { args } = signature;
173
+ const args = getArgs(signature);
174
174
  checkArgs.call(this, 'month', args, 1);
175
175
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
176
176
  return `cast(date_part('month', ${ x }) as Integer)`;
177
177
  },
178
178
  day(signature) {
179
- const { args } = signature;
179
+ const args = getArgs(signature);
180
180
  checkArgs.call(this, 'day', args, 1);
181
181
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
182
182
  return `cast(date_part('day', ${ x }) as Integer)`;
183
183
  },
184
184
  hour(signature) {
185
- const { args } = signature;
185
+ const args = getArgs(signature);
186
186
  checkArgs.call(this, 'hour', args, 1);
187
187
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
188
188
  return `cast(date_part('hour', ${ x }) as Integer)`;
189
189
  },
190
190
  minute(signature) {
191
- const { args } = signature;
191
+ const args = getArgs(signature);
192
192
  checkArgs.call(this, 'minute', args, 1);
193
193
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
194
194
  return `cast(date_part('minute', ${ x }) as Integer)`;
195
195
  },
196
196
  second(signature) {
197
- const { args } = signature;
197
+ const args = getArgs(signature);
198
198
  checkArgs.call(this, 'second', args, 1);
199
199
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
200
200
  return `cast(floor(date_part('second', ${ x })) as Integer)`;
@@ -202,19 +202,19 @@ const oDataFunctions = {
202
202
  // REVISIT: currently runtimes normalize to milliseconds
203
203
  // we could allow this to be more precise
204
204
  fractionalseconds(signature) {
205
- const { args } = signature;
205
+ const args = getArgs(signature);
206
206
  checkArgs.call(this, 'fractionalseconds', args, 1);
207
207
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
208
208
  return `cast(date_part('second', ${ x }) - floor(date_part('second', ${ x })) AS DECIMAL(3,3))`;
209
209
  },
210
210
  time(signature) {
211
- const { args } = signature;
211
+ const args = getArgs(signature);
212
212
  checkArgs.call(this, 'time', args, 1);
213
213
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
214
214
  return `to_char(${ x }, 'HH24:MI:SS')::TIME`;
215
215
  },
216
216
  date(signature) {
217
- const { args } = signature;
217
+ const args = getArgs(signature);
218
218
  checkArgs.call(this, 'date', args, 1);
219
219
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
220
220
  return `${ x }::DATE`;
@@ -223,11 +223,11 @@ const oDataFunctions = {
223
223
  // https://help.sap.com/docs/HANA_SERVICE_CF/7c78579ce9b14a669c1f3295b0d8ca16/f12b86a6284c4aeeb449e57eb5dd3ebd.html?locale=en-US
224
224
  hana: {
225
225
  contains(signature) {
226
- const { args } = signature;
226
+ const args = getArgs(signature);
227
227
  checkArgs.call(this, 'contains', args, 2, 3);
228
228
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
229
229
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
230
- if (signature.args.length > 2) {
230
+ if (args.length > 2) {
231
231
  const z = this.renderArgs({ ...signature, args: [ args[2] ] });
232
232
  // While CONTAINS() looks like a function because of its syntax,
233
233
  // it is classified as a predicate because it is designed to evaluate a condition
@@ -235,24 +235,24 @@ const oDataFunctions = {
235
235
  return `CONTAINS(${ x }, ${ y }, ${ z })`;
236
236
  }
237
237
 
238
- return `(CASE WHEN coalesce(locate(${ this.renderArgs(signature) }),0)>0 THEN TRUE ELSE FALSE END)`;
238
+ return `(CASE WHEN coalesce(locate(${ this.renderArgs({ ...signature, args }) }),0)>0 THEN TRUE ELSE FALSE END)`;
239
239
  },
240
240
  startswith(signature) {
241
- const { args } = signature;
241
+ const args = getArgs(signature);
242
242
  checkArgs.call(this, 'startswith', args, 2);
243
243
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
244
244
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
245
245
  return `(CASE WHEN locate(${ x }, ${ y }) = 1 THEN TRUE ELSE FALSE END)`;
246
246
  }, // locate is 1 indexed
247
247
  endswith(signature) {
248
- const { args } = signature;
248
+ const args = getArgs(signature);
249
249
  checkArgs.call(this, 'endswith', args, 2);
250
250
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
251
251
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
252
252
  return `(CASE WHEN substring(${ x }, (length(${ x }) + 1) - length(${ y })) = ${ y } THEN TRUE ELSE FALSE END)`;
253
253
  },
254
254
  indexof(signature) {
255
- const { args } = signature;
255
+ const args = getArgs(signature);
256
256
  checkArgs.call(this, 'indexof', args, 2);
257
257
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
258
258
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -261,7 +261,7 @@ const oDataFunctions = {
261
261
  matchespattern(signature) {
262
262
  // case … when only works as column expression (not in where)
263
263
  // in the where clause, only "${x} LIKE_REGEXPR ${y}" works
264
- const { args } = signature;
264
+ const args = getArgs(signature);
265
265
  checkArgs.call(this, 'matchespattern', args, 2);
266
266
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
267
267
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -271,37 +271,37 @@ const oDataFunctions = {
271
271
  return oDataFunctions.hana.matchespattern.call(this, signature);
272
272
  },
273
273
  year(signature) {
274
- const { args } = signature;
274
+ const args = getArgs(signature);
275
275
  checkArgs.call(this, 'year', args, 1);
276
276
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
277
277
  return `year(${ x })`;
278
278
  },
279
279
  month(signature) {
280
- const { args } = signature;
280
+ const args = getArgs(signature);
281
281
  checkArgs.call(this, 'month', args, 1);
282
282
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
283
283
  return `month(${ x })`;
284
284
  },
285
285
  day(signature) {
286
- const { args } = signature;
286
+ const args = getArgs(signature);
287
287
  checkArgs.call(this, 'day', args, 1);
288
288
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
289
289
  return `dayofmonth(${ x })`;
290
290
  },
291
291
  hour(signature) {
292
- const { args } = signature;
292
+ const args = getArgs(signature);
293
293
  checkArgs.call(this, 'hour', args, 1);
294
294
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
295
295
  return `hour(${ x })`;
296
296
  },
297
297
  minute(signature) {
298
- const { args } = signature;
298
+ const args = getArgs(signature);
299
299
  checkArgs.call(this, 'minute', args, 1);
300
300
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
301
301
  return `minute(${ x })`;
302
302
  },
303
303
  second(signature) {
304
- const { args } = signature;
304
+ const args = getArgs(signature);
305
305
  checkArgs.call(this, 'second', args, 1);
306
306
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
307
307
  return `to_integer(second(${ x }))`;
@@ -309,19 +309,19 @@ const oDataFunctions = {
309
309
  // REVISIT: currently runtimes normalize to milliseconds
310
310
  // we could allow this to be more precise
311
311
  fractionalseconds(signature) {
312
- const { args } = signature;
312
+ const args = getArgs(signature);
313
313
  checkArgs.call(this, 'fractionalseconds', args, 1);
314
314
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
315
315
  return `(to_decimal(second(${ x }),5,3) - to_integer(second(${ x })))`;
316
316
  },
317
317
  time(signature) {
318
- const { args } = signature;
318
+ const args = getArgs(signature);
319
319
  checkArgs.call(this, 'time', args, 1);
320
320
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
321
321
  return `to_time(${ x })`;
322
322
  },
323
323
  date(signature) {
324
- const { args } = signature;
324
+ const args = getArgs(signature);
325
325
  checkArgs.call(this, 'date', args, 1);
326
326
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
327
327
  return `to_date(${ x })`;
@@ -330,7 +330,7 @@ const oDataFunctions = {
330
330
  // https://www.h2database.com/html/functions.html
331
331
  h2: {
332
332
  contains(signature) {
333
- const args = [ ...signature.args ];
333
+ const args = [ ...getArgs(signature) ];
334
334
  checkArgs.call(this, 'contains', args, 2);
335
335
  // defined as { LOCATE(searchString, string [, startInt]) }
336
336
  args.reverse();
@@ -339,7 +339,7 @@ const oDataFunctions = {
339
339
  return `(coalesce(locate(${ x }, ${ y }),0) > 0)`;
340
340
  },
341
341
  startswith(signature) {
342
- const args = [ ...signature.args ];
342
+ const args = [ ...getArgs(signature) ];
343
343
  checkArgs.call(this, 'startswith', args, 2);
344
344
  // defined as { LOCATE(searchString, string [, startInt]) }
345
345
  args.reverse();
@@ -348,14 +348,14 @@ const oDataFunctions = {
348
348
  return `coalesce((locate(${ x }, ${ y }) = 1), false)`; // locate is 1 indexed
349
349
  },
350
350
  endswith(signature) {
351
- const { args } = signature;
351
+ const args = getArgs(signature);
352
352
  checkArgs.call(this, 'endswith', args, 2);
353
353
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
354
354
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
355
355
  return `coalesce((substring(${ x } FROM (char_length(${ x }) + 1) - char_length(${ y })) = ${ y }), false)`;
356
356
  },
357
357
  substring(signature) {
358
- const { args } = signature;
358
+ const args = getArgs(signature);
359
359
  checkArgs.call(this, 'substring', args, 2, 3);
360
360
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
361
361
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -369,13 +369,13 @@ const oDataFunctions = {
369
369
  // char_length is preferred over length -> REVISIT: returns a BIGINT, is this ok?
370
370
  // https://www.h2database.com/html/functions.html#char_length
371
371
  length(signature) {
372
- const { args } = signature;
372
+ const args = getArgs(signature);
373
373
  checkArgs.call(this, 'length', args, 1);
374
374
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
375
375
  return `cast(char_length(${ x }) as Integer)`;
376
376
  },
377
377
  indexof(signature) {
378
- const args = [ ...signature.args ];
378
+ const args = [ ...getArgs(signature) ];
379
379
  checkArgs.call(this, 'indexof', args, 2);
380
380
  // defined as { LOCATE(searchString, string [, startInt]) }
381
381
  args.reverse();
@@ -384,7 +384,7 @@ const oDataFunctions = {
384
384
  return `(locate(${ x }, ${ y }) - 1)`; // locate is 1 indexed
385
385
  },
386
386
  matchespattern(signature) {
387
- const { args } = signature;
387
+ const args = getArgs(signature);
388
388
  checkArgs.call(this, 'matchespattern', args, 2);
389
389
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
390
390
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -394,37 +394,37 @@ const oDataFunctions = {
394
394
  return oDataFunctions.h2.matchespattern.call(this, signature);
395
395
  },
396
396
  year(signature) {
397
- const { args } = signature;
397
+ const args = getArgs(signature);
398
398
  checkArgs.call(this, 'year', args, 1);
399
399
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
400
400
  return `extract(YEAR FROM ${ x })`;
401
401
  },
402
402
  month(signature) {
403
- const { args } = signature;
403
+ const args = getArgs(signature);
404
404
  checkArgs.call(this, 'month', args, 1);
405
405
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
406
406
  return `extract(MONTH FROM ${ x })`;
407
407
  },
408
408
  day(signature) {
409
- const { args } = signature;
409
+ const args = getArgs(signature);
410
410
  checkArgs.call(this, 'day', args, 1);
411
411
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
412
412
  return `extract(DAY FROM ${ x })`;
413
413
  },
414
414
  hour(signature) {
415
- const { args } = signature;
415
+ const args = getArgs(signature);
416
416
  checkArgs.call(this, 'hour', args, 1);
417
417
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
418
418
  return `extract(HOUR FROM ${ x })`;
419
419
  },
420
420
  minute(signature) {
421
- const { args } = signature;
421
+ const args = getArgs(signature);
422
422
  checkArgs.call(this, 'minute', args, 1);
423
423
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
424
424
  return `extract(MINUTE FROM ${ x })`;
425
425
  },
426
426
  second(signature) {
427
- const { args } = signature;
427
+ const args = getArgs(signature);
428
428
  checkArgs.call(this, 'second', args, 1);
429
429
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
430
430
  return `extract(SECOND FROM ${ x })`;
@@ -432,19 +432,19 @@ const oDataFunctions = {
432
432
  // REVISIT: currently runtimes normalize to milliseconds
433
433
  // we could allow this to be more precise
434
434
  fractionalseconds(signature) {
435
- const { args } = signature;
435
+ const args = getArgs(signature);
436
436
  checkArgs.call(this, 'fractionalseconds', args, 1);
437
437
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
438
438
  return `cast(extract(MILLISECOND FROM ${ x }) / 1000.0 AS NUMERIC(3,3))`;
439
439
  },
440
440
  time(signature) {
441
- const { args } = signature;
441
+ const args = getArgs(signature);
442
442
  checkArgs.call(this, 'time', args, 1);
443
443
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
444
444
  return `cast(${ x } AS TIME)`;
445
445
  },
446
446
  date(signature) {
447
- const { args } = signature;
447
+ const args = getArgs(signature);
448
448
  checkArgs.call(this, 'date', args, 1);
449
449
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
450
450
  return `cast(${ x } AS DATE)`;
@@ -453,7 +453,7 @@ const oDataFunctions = {
453
453
  common: {
454
454
  concat(signature) {
455
455
  const separator = '||';
456
- const args = signature.args.reduce((acc, current, index) => {
456
+ const args = getArgs(signature).reduce((acc, current, index) => {
457
457
  if (index > 0)
458
458
  acc.push(separator);
459
459
 
@@ -464,26 +464,26 @@ const oDataFunctions = {
464
464
  return `(${ res })`;
465
465
  },
466
466
  ceiling(signature) {
467
- const { args } = signature;
467
+ const args = getArgs(signature);
468
468
  checkArgs.call(this, 'ceiling', args, 1);
469
469
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
470
470
  return `ceil(${ x })`;
471
471
  },
472
472
  floor(signature) {
473
- const { args } = signature;
473
+ const args = getArgs(signature);
474
474
  checkArgs.call(this, 'floor', args, 1);
475
475
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
476
476
  return `floor(${ x })`;
477
477
  },
478
478
  trim(signature) {
479
- const { args } = signature;
479
+ const args = getArgs(signature);
480
480
  checkArgs.call(this, 'trim', args, 1);
481
481
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
482
482
  return `trim(${ x })`;
483
483
  },
484
484
  // SAP HANA, sqlite and postgres share the same implementation
485
485
  substring(signature) {
486
- const { args } = signature;
486
+ const args = getArgs(signature);
487
487
  checkArgs.call(this, 'substring', args, 2, 3);
488
488
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
489
489
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -495,52 +495,52 @@ const oDataFunctions = {
495
495
  : `substr(${ x }, CASE WHEN ${ y } < 0 THEN length(${ x }) + ${ y } + 1 ELSE ${ y } + 1 END)`;
496
496
  },
497
497
  min(signature) {
498
- const { args } = signature;
498
+ const args = getArgs(signature);
499
499
  checkArgs.call(this, 'min', args, 1);
500
500
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
501
501
  return `min(${ x })`;
502
502
  },
503
503
  max(signature) {
504
- const { args } = signature;
504
+ const args = getArgs(signature);
505
505
  checkArgs.call(this, 'max', args, 1);
506
506
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
507
507
  return `max(${ x })`;
508
508
  },
509
509
  sum(signature) {
510
- const { args } = signature;
510
+ const args = getArgs(signature);
511
511
  checkArgs.call(this, 'sum', args, 1);
512
512
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
513
513
  return `sum(${ x })`;
514
514
  },
515
515
  count(signature) {
516
- const { args } = signature;
516
+ const args = getArgs(signature);
517
517
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
518
518
  return `count(${ x || '*' })`;
519
519
  },
520
520
  countdistinct(signature) {
521
- const { args } = signature;
522
- return `count(distinct ${ args.length > 0 ? this.renderArgs(signature) : "'*'" })`;
521
+ const args = getArgs(signature);
522
+ return `count(distinct ${ args.length > 0 ? this.renderArgs({ ...signature, args }) : "'*'" })`;
523
523
  },
524
524
  average(signature) {
525
- const { args } = signature;
525
+ const args = getArgs(signature);
526
526
  checkArgs.call(this, 'average', args, 1);
527
527
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
528
528
  return `avg(${ x })`;
529
529
  },
530
530
  length(signature) {
531
- const { args } = signature;
531
+ const args = getArgs(signature);
532
532
  checkArgs.call(this, 'length', args, 1);
533
533
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
534
534
  return `length(${ x })`;
535
535
  },
536
536
  tolower(signature) {
537
- const { args } = signature;
537
+ const args = getArgs(signature);
538
538
  checkArgs.call(this, 'tolower', args, 1);
539
539
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
540
540
  return `lower(${ x })`;
541
541
  },
542
542
  toupper(signature) {
543
- const { args } = signature;
543
+ const args = getArgs(signature);
544
544
  checkArgs.call(this, 'toupper', args, 1);
545
545
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
546
546
  return `upper(${ x })`;
@@ -570,7 +570,7 @@ const hanaFunctions = {
570
570
  * @returns {string} - SQL expression to calculate the nano100 difference in SQLite.
571
571
  */
572
572
  nano100_between(signature) {
573
- const { args } = signature;
573
+ const args = getArgs(signature);
574
574
  checkArgs.call(this, 'nano100_between', args, 2);
575
575
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
576
576
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -578,7 +578,7 @@ const hanaFunctions = {
578
578
  return `CAST(((julianday(${ y }) - julianday(${ x })) * 864000000000) as INTEGER)`;
579
579
  },
580
580
  seconds_between(signature) {
581
- const { args } = signature;
581
+ const args = getArgs(signature);
582
582
  checkArgs.call(this, 'seconds_between', args, 2);
583
583
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
584
584
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -586,7 +586,7 @@ const hanaFunctions = {
586
586
  return `CAST(strftime('%s', ${ y }) - strftime('%s', ${ x }) AS INTEGER)`;
587
587
  },
588
588
  days_between(signature) {
589
- const { args } = signature;
589
+ const args = getArgs(signature);
590
590
  checkArgs.call(this, 'days_between', args, 2);
591
591
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
592
592
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -619,12 +619,13 @@ const hanaFunctions = {
619
619
  * @returns {string} A SQL expression that calculates the adjusted month difference.
620
620
  */
621
621
  months_between(signature) {
622
+ const args = getArgs(signature);
622
623
  // Ensure exactly two arguments (startDate, endDate)
623
- checkArgs.call(this, 'months_between', signature.args, 2);
624
+ checkArgs.call(this, 'months_between', args, 2);
624
625
 
625
626
  // Render the arguments as SQL expressions.
626
- const x = this.renderArgs({ ...signature, args: [ signature.args[0] ] });
627
- const y = this.renderArgs({ ...signature, args: [ signature.args[1] ] });
627
+ const x = this.renderArgs({ ...signature, args: [ args[0] ] });
628
+ const y = this.renderArgs({ ...signature, args: [ args[1] ] });
628
629
 
629
630
  // Construct the SQL expression:
630
631
  // 1. Base month difference from the year and month components.
@@ -654,14 +655,14 @@ const hanaFunctions = {
654
655
  return res.replace(/\s+/g, ' ');
655
656
  },
656
657
  years_between(signature) {
657
- const { args } = signature;
658
+ const args = getArgs(signature);
658
659
  checkArgs.call(this, 'years_between', args, 2);
659
660
  return `floor((${ hanaFunctions.sqlite.months_between.call(this, signature) }) / 12)`;
660
661
  },
661
662
  },
662
663
  postgres: {
663
664
  nano100_between(signature) {
664
- const { args } = signature;
665
+ const args = getArgs(signature);
665
666
  checkArgs.call(this, 'nano100_between', args, 2);
666
667
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
667
668
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -669,7 +670,7 @@ const hanaFunctions = {
669
670
  return `(EXTRACT(EPOCH FROM (${ y })::TIMESTAMP - (${ x })::TIMESTAMP) * 10000000)::BIGINT`;
670
671
  },
671
672
  seconds_between(signature) {
672
- const { args } = signature;
673
+ const args = getArgs(signature);
673
674
  checkArgs.call(this, 'seconds_between', args, 2);
674
675
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
675
676
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -677,14 +678,14 @@ const hanaFunctions = {
677
678
  return `EXTRACT(EPOCH FROM (${ y })::TIMESTAMP - (${ x })::TIMESTAMP)::BIGINT`;
678
679
  },
679
680
  days_between(signature) {
680
- const { args } = signature;
681
+ const args = getArgs(signature);
681
682
  checkArgs.call(this, 'days_between', args, 2);
682
683
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
683
684
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
684
685
  return `EXTRACT(DAY FROM ${ y }::timestamp - ${ x }::timestamp)::integer`;
685
686
  },
686
687
  months_between(signature) {
687
- const { args } = signature;
688
+ const args = getArgs(signature);
688
689
  checkArgs.call(this, 'months_between', args, 2);
689
690
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
690
691
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -692,14 +693,14 @@ const hanaFunctions = {
692
693
  return `(EXTRACT(YEAR FROM AGE(${ y }, ${ x })) * 12 + EXTRACT(MONTH FROM AGE(${ y }, ${ x })))::INTEGER`;
693
694
  },
694
695
  years_between(signature) {
695
- const { args } = signature;
696
+ const args = getArgs(signature);
696
697
  checkArgs.call(this, 'years_between', args, 2);
697
698
  return `floor((${ hanaFunctions.postgres.months_between.call(this, signature) }) / 12)::INTEGER`;
698
699
  },
699
700
  },
700
701
  h2: {
701
702
  nano100_between(signature) {
702
- const { args } = signature;
703
+ const args = getArgs(signature);
703
704
  checkArgs.call(this, 'nano100_between', args, 2);
704
705
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
705
706
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -707,7 +708,7 @@ const hanaFunctions = {
707
708
  return `CAST(DATEDIFF('MICROSECOND', ${ x }, ${ y }) * 10 AS BIGINT)`;
708
709
  },
709
710
  seconds_between(signature) {
710
- const { args } = signature;
711
+ const args = getArgs(signature);
711
712
  checkArgs.call(this, 'seconds_between', args, 2);
712
713
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
713
714
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -715,7 +716,7 @@ const hanaFunctions = {
715
716
  return `CAST(DATEDIFF('SECOND', ${ x }, ${ y }) AS BIGINT)`;
716
717
  },
717
718
  days_between(signature) {
718
- const { args } = signature;
719
+ const args = getArgs(signature);
719
720
  checkArgs.call(this, 'days_between', args, 2);
720
721
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
721
722
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -726,7 +727,7 @@ const hanaFunctions = {
726
727
  * forward and backward (negative) scenarios.
727
728
  */
728
729
  months_between(signature) {
729
- const { args } = signature;
730
+ const args = getArgs(signature);
730
731
  checkArgs.call(this, 'months_between', args, 2);
731
732
 
732
733
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
@@ -752,7 +753,7 @@ const hanaFunctions = {
752
753
  return res.replace(/\s+/g, ' ');
753
754
  },
754
755
  years_between(signature) {
755
- const { args } = signature;
756
+ const args = getArgs(signature);
756
757
  checkArgs.call(this, 'years_between', args, 2);
757
758
  return `floor((${ hanaFunctions.h2.months_between.call(this, signature) }) / 12)`;
758
759
  },
@@ -761,7 +762,7 @@ const hanaFunctions = {
761
762
  // identity functions + argument check
762
763
  hana: {
763
764
  nano100_between(signature) {
764
- const { args } = signature;
765
+ const args = getArgs(signature);
765
766
  checkArgs.call(this, 'nano100_between', args, 2);
766
767
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
767
768
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -769,7 +770,7 @@ const hanaFunctions = {
769
770
  return `nano100_between(${ x }, ${ y })`;
770
771
  },
771
772
  seconds_between(signature) {
772
- const { args } = signature;
773
+ const args = getArgs(signature);
773
774
  checkArgs.call(this, 'seconds_between', args, 2);
774
775
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
775
776
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -777,14 +778,14 @@ const hanaFunctions = {
777
778
  return `seconds_between(${ x }, ${ y })`;
778
779
  },
779
780
  days_between(signature) {
780
- const { args } = signature;
781
+ const args = getArgs(signature);
781
782
  checkArgs.call(this, 'days_between', args, 2);
782
783
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
783
784
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
784
785
  return `days_between(${ x }, ${ y })`;
785
786
  },
786
787
  months_between(signature) {
787
- const { args } = signature;
788
+ const args = getArgs(signature);
788
789
  checkArgs.call(this, 'months_between', args, 2);
789
790
  const x = this.renderArgs({ ...signature, args: [ args[0] ] });
790
791
  const y = this.renderArgs({ ...signature, args: [ args[1] ] });
@@ -792,13 +793,21 @@ const hanaFunctions = {
792
793
  return `months_between(${ x }, ${ y })`;
793
794
  },
794
795
  years_between(signature) {
795
- const { args } = signature;
796
+ const args = getArgs(signature);
796
797
  checkArgs.call(this, 'years_between', args, 2);
797
- return `years_between(${ this.renderArgs(signature) })`;
798
+ return `years_between(${ this.renderArgs({ ...signature, args }) })`;
798
799
  },
799
800
  },
800
801
  };
801
802
 
803
+ /**
804
+ * Normalizes `signature.args` to a positional array, handling both
805
+ * positional (array) and named (object) argument forms from CSN.
806
+ */
807
+ function getArgs( signature ) {
808
+ return Array.isArray(signature.args) ? signature.args : Object.values(signature.args);
809
+ }
810
+
802
811
  function checkArgs( funcName, receivedArgs, expectedLength, alternativeLength = null ) {
803
812
  const expectedMismatch = receivedArgs.length < expectedLength;
804
813
  const alternativeMismatch