@budibase/backend-core 2.33.13 → 3.0.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.
package/src/sql/sql.ts CHANGED
@@ -179,12 +179,6 @@ class InternalBuilder {
179
179
  return this.table.schema[column]
180
180
  }
181
181
 
182
- private supportsILike(): boolean {
183
- return !(
184
- this.client === SqlClient.ORACLE || this.client === SqlClient.SQL_LITE
185
- )
186
- }
187
-
188
182
  private quoteChars(): [string, string] {
189
183
  const wrapped = this.knexClient.wrapIdentifier("foo", {})
190
184
  return [wrapped[0], wrapped[wrapped.length - 1]]
@@ -216,8 +210,30 @@ class InternalBuilder {
216
210
  return formatter.wrap(value, false)
217
211
  }
218
212
 
219
- private rawQuotedValue(value: string): Knex.Raw {
220
- return this.knex.raw(this.quotedValue(value))
213
+ private castIntToString(identifier: string | Knex.Raw): Knex.Raw {
214
+ switch (this.client) {
215
+ case SqlClient.ORACLE: {
216
+ return this.knex.raw("to_char(??)", [identifier])
217
+ }
218
+ case SqlClient.POSTGRES: {
219
+ return this.knex.raw("??::TEXT", [identifier])
220
+ }
221
+ case SqlClient.MY_SQL:
222
+ case SqlClient.MARIADB: {
223
+ return this.knex.raw("CAST(?? AS CHAR)", [identifier])
224
+ }
225
+ case SqlClient.SQL_LITE: {
226
+ // Technically sqlite can actually represent numbers larger than a 64bit
227
+ // int as a string, but it does it using scientific notation (e.g.
228
+ // "1e+20") which is not what we want. Given that the external SQL
229
+ // databases are limited to supporting only 64bit ints, we settle for
230
+ // that here.
231
+ return this.knex.raw("printf('%d', ??)", [identifier])
232
+ }
233
+ case SqlClient.MS_SQL: {
234
+ return this.knex.raw("CONVERT(NVARCHAR, ??)", [identifier])
235
+ }
236
+ }
221
237
  }
222
238
 
223
239
  // Unfortuantely we cannot rely on knex's identifier escaping because it trims
@@ -300,11 +316,9 @@ class InternalBuilder {
300
316
  const columnSchema = schema[column]
301
317
 
302
318
  if (this.SPECIAL_SELECT_CASES.POSTGRES_MONEY(columnSchema)) {
303
- // TODO: figure out how to express this safely without string
304
- // interpolation.
305
- return this.knex.raw(`??::money::numeric as "${field}"`, [
319
+ return this.knex.raw(`??::money::numeric as ??`, [
306
320
  this.rawQuotedIdentifier([table, column].join(".")),
307
- field,
321
+ this.knex.raw(this.quote(field)),
308
322
  ])
309
323
  }
310
324
 
@@ -314,8 +328,9 @@ class InternalBuilder {
314
328
 
315
329
  // TODO: figure out how to express this safely without string
316
330
  // interpolation.
317
- return this.knex.raw(`CONVERT(varchar, ??, 108) as "${field}"`, [
331
+ return this.knex.raw(`CONVERT(varchar, ??, 108) as ??`, [
318
332
  this.rawQuotedIdentifier(field),
333
+ this.knex.raw(this.quote(field)),
319
334
  ])
320
335
  }
321
336
 
@@ -513,7 +528,7 @@ class InternalBuilder {
513
528
  if (!matchesTableName) {
514
529
  updatedKey = filterKey.replace(
515
530
  new RegExp(`^${relationship.column}.`),
516
- `${aliases![relationship.tableName]}.`
531
+ `${aliases?.[relationship.tableName] || relationship.tableName}.`
517
532
  )
518
533
  } else {
519
534
  updatedKey = filterKey
@@ -1075,24 +1090,36 @@ class InternalBuilder {
1075
1090
  )
1076
1091
  }
1077
1092
  } else {
1078
- query = query.count(`* as ${aggregation.name}`)
1093
+ if (this.client === SqlClient.ORACLE) {
1094
+ const field = this.convertClobs(`${tableName}.${aggregation.field}`)
1095
+ query = query.select(
1096
+ this.knex.raw(`COUNT(??) as ??`, [field, aggregation.name])
1097
+ )
1098
+ } else {
1099
+ query = query.count(`${aggregation.field} as ${aggregation.name}`)
1100
+ }
1079
1101
  }
1080
1102
  } else {
1081
- const field = `${tableName}.${aggregation.field} as ${aggregation.name}`
1082
- switch (op) {
1083
- case CalculationType.SUM:
1084
- query = query.sum(field)
1085
- break
1086
- case CalculationType.AVG:
1087
- query = query.avg(field)
1088
- break
1089
- case CalculationType.MIN:
1090
- query = query.min(field)
1091
- break
1092
- case CalculationType.MAX:
1093
- query = query.max(field)
1094
- break
1103
+ const fieldSchema = this.getFieldSchema(aggregation.field)
1104
+ if (!fieldSchema) {
1105
+ // This should not happen in practice.
1106
+ throw new Error(
1107
+ `field schema missing for aggregation target: ${aggregation.field}`
1108
+ )
1109
+ }
1110
+
1111
+ let aggregate = this.knex.raw("??(??)", [
1112
+ this.knex.raw(op),
1113
+ this.rawQuotedIdentifier(`${tableName}.${aggregation.field}`),
1114
+ ])
1115
+
1116
+ if (fieldSchema.type === FieldType.BIGINT) {
1117
+ aggregate = this.castIntToString(aggregate)
1095
1118
  }
1119
+
1120
+ query = query.select(
1121
+ this.knex.raw("?? as ??", [aggregate, aggregation.name])
1122
+ )
1096
1123
  }
1097
1124
  }
1098
1125
  return query
@@ -1435,7 +1462,8 @@ class InternalBuilder {
1435
1462
  schema.constraints?.presence === true ||
1436
1463
  schema.type === FieldType.FORMULA ||
1437
1464
  schema.type === FieldType.AUTO ||
1438
- schema.type === FieldType.LINK
1465
+ schema.type === FieldType.LINK ||
1466
+ schema.type === FieldType.AI
1439
1467
  ) {
1440
1468
  continue
1441
1469
  }
@@ -1557,7 +1585,7 @@ class InternalBuilder {
1557
1585
  query = this.addFilters(query, filters, { relationship: true })
1558
1586
 
1559
1587
  // handle relationships with a CTE for all others
1560
- if (relationships?.length) {
1588
+ if (relationships?.length && aggregations.length === 0) {
1561
1589
  const mainTable =
1562
1590
  this.query.tableAliases?.[this.query.endpoint.entityId] ||
1563
1591
  this.query.endpoint.entityId
@@ -1572,10 +1600,8 @@ class InternalBuilder {
1572
1600
  // add JSON aggregations attached to the CTE
1573
1601
  return this.addJsonRelationships(cte, tableName, relationships)
1574
1602
  }
1575
- // no relationships found - return query
1576
- else {
1577
- return query
1578
- }
1603
+
1604
+ return query
1579
1605
  }
1580
1606
 
1581
1607
  update(opts: QueryOptions): Knex.QueryBuilder {
@@ -102,6 +102,14 @@ export const useAppBuilders = () => {
102
102
  return useFeature(Feature.APP_BUILDERS)
103
103
  }
104
104
 
105
+ export const useBudibaseAI = () => {
106
+ return useFeature(Feature.BUDIBASE_AI)
107
+ }
108
+
109
+ export const useAICustomConfigs = () => {
110
+ return useFeature(Feature.AI_CUSTOM_CONFIGS)
111
+ }
112
+
105
113
  // QUOTAS
106
114
 
107
115
  export const setAutomationLogsQuota = (value: number) => {