Disabling aliasing on writes (create, update, delete) for MySQL/MS-SQL datasources.

This commit is contained in:
Michael Drury 2024-03-05 16:19:21 +00:00
parent bc8fdeea6a
commit a332c058ce
4 changed files with 74 additions and 15 deletions

View File

@ -1,12 +1,16 @@
import {
QueryJson,
SearchFilters,
Table,
Row,
Datasource,
DatasourcePlusQueryResponse,
Operation,
QueryJson,
Row,
SearchFilters,
} from "@budibase/types"
import { getDatasourceAndQuery } from "../../../sdk/app/rows/utils"
import { getSQLClient } from "../../../sdk/app/rows/utils"
import { cloneDeep } from "lodash"
import sdk from "../../../sdk"
import { makeExternalQuery } from "../../../integrations/base/query"
import { SqlClient } from "../../../integrations/utils"
class CharSequence {
static alphabet = "abcdefghijklmnopqrstuvwxyz"
@ -43,6 +47,32 @@ export default class AliasTables {
this.charSeq = new CharSequence()
}
isAliasingEnabled(json: QueryJson, datasource: Datasource) {
const fieldLength = json.resource?.fields?.length
if (!fieldLength || fieldLength <= 0) {
return false
}
const writeOperations = [
Operation.CREATE,
Operation.UPDATE,
Operation.DELETE,
]
try {
const sqlClient = getSQLClient(datasource)
const isWrite = writeOperations.includes(json.endpoint.operation)
if (
isWrite &&
(sqlClient === SqlClient.MY_SQL || sqlClient === SqlClient.MS_SQL)
) {
return false
}
} catch (err) {
// if we can't get an SQL client, we can't alias
return false
}
return true
}
getAlias(tableName: string) {
if (this.aliases[tableName]) {
return this.aliases[tableName]
@ -111,8 +141,10 @@ export default class AliasTables {
}
async queryWithAliasing(json: QueryJson): DatasourcePlusQueryResponse {
const fieldLength = json.resource?.fields?.length
const aliasingEnabled = fieldLength && fieldLength > 0
const datasourceId = json.endpoint.datasourceId
const datasource = await sdk.datasources.get(datasourceId)
const aliasingEnabled = this.isAliasingEnabled(json, datasource)
if (aliasingEnabled) {
json = cloneDeep(json)
// run through the query json to update anywhere a table may be used
@ -158,7 +190,7 @@ export default class AliasTables {
}
json.tableAliases = invertedTableAliases
}
const response = await getDatasourceAndQuery(json)
const response = await makeExternalQuery(datasource, json)
if (Array.isArray(response) && aliasingEnabled) {
return this.reverse(response)
} else {

View File

@ -435,10 +435,12 @@ class InternalBuilder {
aliases?: QueryJson["tableAliases"]
): Knex.QueryBuilder {
const tableName = endpoint.entityId
const tableAliased = aliases?.[tableName]
? `${tableName} as ${aliases?.[tableName]}`
: tableName
let query = knex(tableAliased)
const tableAlias = aliases?.[tableName]
let table: string | Record<string, string> = tableName
if (tableAlias) {
table = { [tableAlias]: tableName }
}
let query = knex(table)
if (endpoint.schema) {
query = query.withSchema(endpoint.schema)
}

View File

@ -14,13 +14,18 @@ import firebase from "./firebase"
import redis from "./redis"
import snowflake from "./snowflake"
import oracle from "./oracle"
import { SourceName, Integration, PluginType } from "@budibase/types"
import {
SourceName,
Integration,
PluginType,
IntegrationBase,
} from "@budibase/types"
import { getDatasourcePlugin } from "../utilities/fileSystem"
import env from "../environment"
import cloneDeep from "lodash/cloneDeep"
import sdk from "../sdk"
const DEFINITIONS: Record<SourceName, Integration | undefined> = {
const DEFINITIONS: { [key: SourceName]: Integration | undefined } = {
[SourceName.POSTGRES]: postgres.schema,
[SourceName.DYNAMODB]: dynamodb.schema,
[SourceName.MONGODB]: mongodb.schema,
@ -40,7 +45,7 @@ const DEFINITIONS: Record<SourceName, Integration | undefined> = {
[SourceName.BUDIBASE]: undefined,
}
const INTEGRATIONS: Record<SourceName, any> = {
const INTEGRATIONS: { [key: SourceName]: IntegrationBase | undefined } = {
[SourceName.POSTGRES]: postgres.integration,
[SourceName.DYNAMODB]: dynamodb.integration,
[SourceName.MONGODB]: mongodb.integration,

View File

@ -7,11 +7,31 @@ import {
Table,
TableSchema,
DatasourcePlusQueryResponse,
Datasource,
SourceName,
} from "@budibase/types"
import { makeExternalQuery } from "../../../integrations/base/query"
import { Format } from "../../../api/controllers/view/exporters"
import sdk from "../.."
import { isRelationshipColumn } from "../../../db/utils"
import { SqlClient } from "../../../integrations/utils"
export function getSQLClient(datasource: Datasource): SqlClient {
if (!datasource.isSQL) {
throw new Error("Cannot get SQL Client for non-SQL datasource")
}
switch (datasource.source) {
case SourceName.POSTGRES:
return SqlClient.POSTGRES
case SourceName.MYSQL:
return SqlClient.MY_SQL
case SourceName.ORACLE:
return SqlClient.ORACLE
case SourceName.SQL_SERVER:
return SqlClient.MS_SQL
}
throw new Error("Unable to find a valid SQL client")
}
export async function getDatasourceAndQuery(
json: QueryJson