@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/dist/index.js +474 -487
- package/dist/index.js.map +4 -4
- package/dist/index.js.meta.json +1 -1
- package/dist/package.json +4 -4
- package/dist/plugins.js.meta.json +1 -1
- package/dist/src/constants/misc.d.ts +2 -1
- package/dist/src/constants/misc.js +1 -0
- package/dist/src/constants/misc.js.map +1 -1
- package/dist/src/environment.d.ts +1 -0
- package/dist/src/environment.js +1 -1
- package/dist/src/environment.js.map +1 -1
- package/dist/src/security/permissions.d.ts +1 -8
- package/dist/src/security/permissions.js +6 -14
- package/dist/src/security/permissions.js.map +1 -1
- package/dist/src/security/roles.d.ts +5 -5
- package/dist/src/security/roles.js +36 -21
- package/dist/src/security/roles.js.map +1 -1
- package/dist/src/sql/sql.js +52 -32
- package/dist/src/sql/sql.js.map +1 -1
- package/dist/tests/core/utilities/mocks/licenses.d.ts +2 -0
- package/dist/tests/core/utilities/mocks/licenses.js +9 -1
- package/dist/tests/core/utilities/mocks/licenses.js.map +1 -1
- package/package.json +4 -4
- package/src/constants/misc.ts +1 -0
- package/src/environment.ts +2 -0
- package/src/security/permissions.ts +5 -9
- package/src/security/roles.ts +36 -15
- package/src/security/tests/permissions.spec.ts +4 -3
- package/src/sql/sql.ts +61 -35
- package/tests/core/utilities/mocks/licenses.ts +8 -0
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
|
|
220
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
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
|
-
|
|
1576
|
-
|
|
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) => {
|