Fix autocolumn detection on schema import.
This commit is contained in:
parent
40e886b34d
commit
f23f479eb9
|
@ -864,7 +864,7 @@ describe.each([
|
|||
})
|
||||
|
||||
!isInternal &&
|
||||
it.only("can update a row on an external table with a primary key", async () => {
|
||||
it("can update a row on an external table with a primary key", async () => {
|
||||
const tableName = uuid.v4().substring(0, 10)
|
||||
await client!.schema.createTable(tableName, table => {
|
||||
table.increments("id").primary()
|
||||
|
|
|
@ -104,11 +104,34 @@ export interface OracleColumnsResponse {
|
|||
SEARCH_CONDITION: null | string
|
||||
}
|
||||
|
||||
export enum TriggeringEvent {
|
||||
INSERT = "INSERT",
|
||||
DELETE = "DELETE",
|
||||
UPDATE = "UPDATE",
|
||||
LOGON = "LOGON",
|
||||
LOGOFF = "LOGOFF",
|
||||
STARTUP = "STARTUP",
|
||||
SHUTDOWN = "SHUTDOWN",
|
||||
SERVERERROR = "SERVERERROR",
|
||||
SCHEMA = "SCHEMA",
|
||||
ALTER = "ALTER",
|
||||
DROP = "DROP",
|
||||
}
|
||||
|
||||
export enum TriggerType {
|
||||
BEFORE_EACH_ROW = "BEFORE EACH ROW",
|
||||
AFTER_EACH_ROW = "AFTER EACH ROW",
|
||||
BEFORE_STATEMENT = "BEFORE STATEMENT",
|
||||
AFTER_STATEMENT = "AFTER STATEMENT",
|
||||
INSTEAD_OF = "INSTEAD OF",
|
||||
COMPOUND = "COMPOUND",
|
||||
}
|
||||
|
||||
export interface OracleTriggersResponse {
|
||||
TABLE_NAME: string
|
||||
TRIGGER_NAME: string
|
||||
TRIGGER_TYPE: string
|
||||
TRIGGERING_EVENT: string
|
||||
TRIGGER_TYPE: TriggerType
|
||||
TRIGGERING_EVENT: TriggeringEvent
|
||||
TRIGGER_BODY: string
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,8 @@ import {
|
|||
OracleColumn,
|
||||
OracleColumnsResponse,
|
||||
OracleTriggersResponse,
|
||||
TriggeringEvent,
|
||||
TriggerType,
|
||||
} from "./base/types"
|
||||
import { sql } from "@budibase/backend-core"
|
||||
|
||||
|
@ -146,7 +148,15 @@ class OracleIntegration extends Sql implements DatasourcePlus {
|
|||
`
|
||||
|
||||
private static readonly TRIGGERS_SQL = `
|
||||
SELECT table_name, trigger_name, trigger_type, triggering_event, trigger_body FROM all_triggers WHERE status = 'ENABLED';
|
||||
SELECT
|
||||
table_name,
|
||||
trigger_name,
|
||||
trigger_type,
|
||||
triggering_event,
|
||||
trigger_body
|
||||
FROM
|
||||
all_triggers
|
||||
WHERE status = 'ENABLED'
|
||||
`
|
||||
|
||||
constructor(config: OracleConfig) {
|
||||
|
@ -221,6 +231,75 @@ class OracleIntegration extends Sql implements DatasourcePlus {
|
|||
return oracleTables
|
||||
}
|
||||
|
||||
private getTriggersFor(
|
||||
tableName: string,
|
||||
triggersResponse: Result<OracleTriggersResponse>,
|
||||
opts?: { event?: TriggeringEvent; type?: TriggerType }
|
||||
): OracleTriggersResponse[] {
|
||||
const triggers: OracleTriggersResponse[] = []
|
||||
for (const trigger of triggersResponse.rows || []) {
|
||||
if (trigger.TABLE_NAME !== tableName) {
|
||||
continue
|
||||
}
|
||||
if (opts?.event && opts.event !== trigger.TRIGGERING_EVENT) {
|
||||
continue
|
||||
}
|
||||
if (opts?.type && opts.type !== trigger.TRIGGER_TYPE) {
|
||||
continue
|
||||
}
|
||||
triggers.push(trigger)
|
||||
}
|
||||
return triggers
|
||||
}
|
||||
|
||||
private markAutoIncrementColumns(
|
||||
triggersResponse: Result<OracleTriggersResponse>,
|
||||
tables: Record<string, Table>
|
||||
) {
|
||||
for (const table of Object.values(tables)) {
|
||||
const triggers = this.getTriggersFor(table.name, triggersResponse, {
|
||||
type: TriggerType.BEFORE_EACH_ROW,
|
||||
event: TriggeringEvent.INSERT,
|
||||
})
|
||||
|
||||
// This is the trigger body Knex generates for an auto increment column
|
||||
// called "id" on a table called "foo":
|
||||
//
|
||||
// declare checking number := 1;
|
||||
// begin if (:new. "id" is null) then while checking >= 1 loop
|
||||
// select
|
||||
// "foo_seq".nextval into :new. "id"
|
||||
// from
|
||||
// dual;
|
||||
// select
|
||||
// count("id") into checking
|
||||
// from
|
||||
// "foo"
|
||||
// where
|
||||
// "id" = :new. "id";
|
||||
// end loop;
|
||||
// end if;
|
||||
// end;
|
||||
for (const [columnName, schema] of Object.entries(table.schema)) {
|
||||
const autoIncrementTriggers = triggers.filter(
|
||||
trigger =>
|
||||
// This is a bit heuristic, but I think it's the best we can do with
|
||||
// the information we have. We're looking for triggers that run
|
||||
// before each row is inserted, and that have a body that contains a
|
||||
// call to a function that generates a new value for the column. We
|
||||
// also check that the column name is in the trigger body, to make
|
||||
// sure we're not picking up triggers that don't affect the column.
|
||||
trigger.TRIGGER_BODY.includes(`"${columnName}"`) &&
|
||||
trigger.TRIGGER_BODY.includes(`.nextval`)
|
||||
)
|
||||
|
||||
if (autoIncrementTriggers.length > 0) {
|
||||
schema.autocolumn = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static isSupportedColumn(column: OracleColumn) {
|
||||
return !UNSUPPORTED_TYPES.includes(column.type)
|
||||
}
|
||||
|
@ -331,6 +410,8 @@ class OracleIntegration extends Sql implements DatasourcePlus {
|
|||
})
|
||||
})
|
||||
|
||||
this.markAutoIncrementColumns(triggersResponse, tables)
|
||||
|
||||
let externalTables = finaliseExternalTables(tables, entities)
|
||||
let errors = checkExternalTables(externalTables)
|
||||
return { tables: externalTables, errors }
|
||||
|
|
Loading…
Reference in New Issue