mirror of
https://github.com/Fabio286/antares.git
synced 2025-03-04 03:17:40 +01:00
feat(Firebird SQL): procedure add/edit/delete support
This commit is contained in:
parent
8e422e3f07
commit
ae312efbbc
@ -51,6 +51,7 @@ export const defaults: Customizations = {
|
||||
tableSettings: false,
|
||||
tableArray: false,
|
||||
tableRealCount: false,
|
||||
tableDuplicate: false,
|
||||
viewSettings: false,
|
||||
triggerSettings: false,
|
||||
triggerFunctionSettings: false,
|
||||
@ -76,6 +77,7 @@ export const defaults: Customizations = {
|
||||
procedureDataAccess: false,
|
||||
procedureSql: null,
|
||||
procedureContext: false,
|
||||
procedureContextValues: [],
|
||||
procedureLanguage: false,
|
||||
functionDeterministic: false,
|
||||
functionDataAccess: false,
|
||||
|
@ -39,18 +39,24 @@ export const customizations: Customizations = {
|
||||
tables: true,
|
||||
views: true,
|
||||
triggers: true,
|
||||
routines: false,
|
||||
routines: true,
|
||||
functions: false,
|
||||
// Settings
|
||||
elementsWrapper: '"',
|
||||
stringsWrapper: '\'',
|
||||
tableAdd: true,
|
||||
tableSettings: true,
|
||||
tableRealCount: true,
|
||||
viewAdd: true,
|
||||
viewSettings: true,
|
||||
triggerAdd: true,
|
||||
triggerMultipleEvents: true,
|
||||
triggerSql: 'BEGIN\r\n\r\nEND',
|
||||
routineAdd: true,
|
||||
procedureContext: true,
|
||||
procedureContextValues: ['IN', 'OUT'],
|
||||
procedureSql: 'BEGIN\r\n\r\nEND',
|
||||
parametersLength: true,
|
||||
indexes: true,
|
||||
foreigns: true,
|
||||
nullable: true
|
||||
|
@ -43,6 +43,7 @@ export const customizations: Customizations = {
|
||||
stringsWrapper: '"',
|
||||
tableAdd: true,
|
||||
tableTruncateDisableFKCheck: true,
|
||||
tableDuplicate: true,
|
||||
viewAdd: true,
|
||||
triggerAdd: true,
|
||||
routineAdd: true,
|
||||
@ -77,6 +78,7 @@ export const customizations: Customizations = {
|
||||
procedureDataAccess: true,
|
||||
procedureSql: 'BEGIN\r\n\r\nEND',
|
||||
procedureContext: true,
|
||||
procedureContextValues: ['IN', 'OUT', 'INOUT'],
|
||||
triggerSql: 'BEGIN\r\n\r\nEND',
|
||||
functionDeterministic: true,
|
||||
functionDataAccess: true,
|
||||
|
@ -39,6 +39,7 @@ export const customizations: Customizations = {
|
||||
elementsWrapper: '"',
|
||||
stringsWrapper: '\'',
|
||||
tableAdd: true,
|
||||
tableDuplicate: true,
|
||||
viewAdd: true,
|
||||
triggerAdd: true,
|
||||
triggerFunctionAdd: true,
|
||||
@ -60,6 +61,7 @@ export const customizations: Customizations = {
|
||||
tableArray: true,
|
||||
procedureSql: '$procedure$\r\n\r\n$procedure$',
|
||||
procedureContext: true,
|
||||
procedureContextValues: ['IN', 'OUT', 'INOUT'],
|
||||
procedureLanguage: true,
|
||||
functionSql: '$function$\r\n\r\n$function$',
|
||||
triggerFunctionSql: '$function$\r\nBEGIN\r\n\r\nEND\r\n$function$',
|
||||
|
@ -27,6 +27,7 @@ export const customizations: Customizations = {
|
||||
elementsWrapper: '"',
|
||||
stringsWrapper: '\'',
|
||||
tableAdd: true,
|
||||
tableDuplicate: true,
|
||||
viewAdd: true,
|
||||
triggerAdd: true,
|
||||
schemaEdit: false,
|
||||
|
@ -35,6 +35,7 @@ export interface Customizations {
|
||||
stringsWrapper: string;
|
||||
tableAdd?: boolean;
|
||||
tableSettings?: boolean;
|
||||
tableDuplicate?: boolean;
|
||||
tableArray?: boolean;
|
||||
tableRealCount?: boolean;
|
||||
tableTruncateDisableFKCheck?: boolean;
|
||||
@ -75,6 +76,7 @@ export interface Customizations {
|
||||
procedureDataAccess?: boolean;
|
||||
procedureSql?: string;
|
||||
procedureContext?: boolean;
|
||||
procedureContextValues?: string[];
|
||||
procedureLanguage?: boolean;
|
||||
functionDeterministic?: boolean;
|
||||
functionDataAccess?: boolean;
|
||||
|
@ -16,7 +16,7 @@ const queryLogger = ({ sql, cUid }: {sql: string; cUid: string}) => {
|
||||
/**
|
||||
* As Simple As Possible Query Builder Core
|
||||
*/
|
||||
export class AntaresCore {
|
||||
export abstract class AntaresCore {
|
||||
_client: antares.ClientCode;
|
||||
protected _cUid: string
|
||||
protected _params: mysql.ConnectionOptions | pg.ClientConfig | { databasePath: string; readonly: boolean};
|
||||
|
@ -105,15 +105,7 @@ export class FirebirdSQLClient extends AntaresCore {
|
||||
}
|
||||
|
||||
getConnectionPool () {
|
||||
const pool = firebird.pool(this._poolSize, { ...this._params, blobAsText: true });
|
||||
// return new Promise<firebird.Database>((resolve, reject) => {
|
||||
// pool.get((err, db) => {
|
||||
// if (err) reject(err);
|
||||
// else resolve(db);
|
||||
// });
|
||||
// });
|
||||
|
||||
return pool;
|
||||
return firebird.pool(this._poolSize, { ...this._params, blobAsText: true });
|
||||
}
|
||||
|
||||
destroy () {
|
||||
@ -127,34 +119,42 @@ export class FirebirdSQLClient extends AntaresCore {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async getStructure (_schemas: Set<string>) {
|
||||
interface ShowTableResult {
|
||||
interface TableResult {
|
||||
FORMAT: number;
|
||||
NAME: string;
|
||||
TYPE: string;
|
||||
DESCRIPTION: string | null;
|
||||
}
|
||||
|
||||
interface ShowTriggersResult {
|
||||
interface TriggersResult {
|
||||
NAME: string;
|
||||
RELATION: string;
|
||||
SOURCE: string;
|
||||
}
|
||||
|
||||
interface ProcedureResult {
|
||||
NAME: string;
|
||||
COMMENT: string;
|
||||
DEFINER: string;
|
||||
SOURCE: string;
|
||||
}
|
||||
|
||||
const { rows: databases } = await this.raw<antares.QueryResult<{ NAME: string}>>('SELECT rdb$get_context(\'SYSTEM\', \'DB_NAME\') as name FROM rdb$database');
|
||||
|
||||
const filteredDatabases = databases.map(db => {
|
||||
return { name: path.basename(db.NAME) };
|
||||
});
|
||||
|
||||
const tablesArr: ShowTableResult[] = [];
|
||||
const triggersArr: ShowTriggersResult[] = [];
|
||||
const tablesArr: TableResult[] = [];
|
||||
const triggersArr: TriggersResult[] = [];
|
||||
const proceduresArr: ProcedureResult[] = [];
|
||||
let schemaSize = 0;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
for (const _db of filteredDatabases) {
|
||||
// if (!schemas.has(db.name)) continue;
|
||||
|
||||
const { rows: tables } = await this.raw<antares.QueryResult<ShowTableResult>>(`
|
||||
const { rows: tables } = await this.raw<antares.QueryResult<TableResult>>(`
|
||||
SELECT
|
||||
rdb$relation_name AS name,
|
||||
rdb$format AS format,
|
||||
@ -165,7 +165,7 @@ export class FirebirdSQLClient extends AntaresCore {
|
||||
AND RDB$RELATION_TYPE = 0
|
||||
`);
|
||||
|
||||
const { rows: views } = await this.raw<antares.QueryResult<ShowTableResult>>(`
|
||||
const { rows: views } = await this.raw<antares.QueryResult<TableResult>>(`
|
||||
SELECT
|
||||
DISTINCT RDB$VIEW_NAME AS name,
|
||||
'view' AS type
|
||||
@ -174,16 +174,30 @@ export class FirebirdSQLClient extends AntaresCore {
|
||||
|
||||
tablesArr.push(...tables, ...views);
|
||||
|
||||
const { rows: triggers } = await this.raw<antares.QueryResult<ShowTriggersResult>>(`
|
||||
const { rows: triggers } = await this.raw<antares.QueryResult<TriggersResult>>(`
|
||||
SELECT
|
||||
RDB$TRIGGER_NAME as name,
|
||||
RDB$RELATION_NAME as relation,
|
||||
RDB$TRIGGER_SOURCE as source
|
||||
FROM RDB$TRIGGERS
|
||||
WHERE RDB$SYSTEM_FLAG=0;
|
||||
WHERE RDB$SYSTEM_FLAG=0
|
||||
ORDER BY RDB$TRIGGER_NAME;
|
||||
`);
|
||||
|
||||
triggersArr.push(...triggers);
|
||||
|
||||
const { rows: procedures } = await this.raw(`
|
||||
SELECT
|
||||
RDB$PROCEDURE_NAME AS NAME,
|
||||
RDB$DESCRIPTION AS COMMENT,
|
||||
RDB$PROCEDURE_SOURCE AS SOURCE,
|
||||
RDB$OWNER_NAME AS DEFINER
|
||||
FROM RDB$PROCEDURES
|
||||
WHERE RDB$SYSTEM_FLAG=0
|
||||
ORDER BY RDB$PROCEDURE_NAME;
|
||||
`);
|
||||
|
||||
proceduresArr.push(...procedures);
|
||||
}
|
||||
|
||||
return filteredDatabases.map(db => {
|
||||
@ -209,12 +223,21 @@ export class FirebirdSQLClient extends AntaresCore {
|
||||
};
|
||||
});
|
||||
|
||||
// PROCEDURES
|
||||
const remappedProcedures = proceduresArr.map(procedure => {
|
||||
return {
|
||||
name: procedure.NAME.trim(),
|
||||
definer: procedure.DEFINER,
|
||||
comment: procedure.COMMENT?.trim()
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
name: db.name,
|
||||
size: schemaSize,
|
||||
tables: remappedTables,
|
||||
functions: [],
|
||||
procedures: [],
|
||||
procedures: remappedProcedures,
|
||||
triggers: remappedTriggers,
|
||||
schedulers: []
|
||||
};
|
||||
@ -600,9 +623,10 @@ export class FirebirdSQLClient extends AntaresCore {
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async duplicateTable (params: { schema: string; table: string }) { // TODO: retrive table informations and create a copy
|
||||
const sql = `CREATE TABLE "${params.table}_copy" AS SELECT * FROM "${params.table}"`;
|
||||
return await this.raw(sql);
|
||||
// const sql = `CREATE TABLE "${params.table}_copy" AS SELECT * FROM "${params.table}"`;
|
||||
// return await this.raw(sql);
|
||||
}
|
||||
|
||||
async truncateTable (params: { schema: string; table: string }) {
|
||||
@ -723,6 +747,153 @@ export class FirebirdSQLClient extends AntaresCore {
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
async getRoutineInformations ({ routine }: { schema: string; routine: string }) {
|
||||
interface ProcedureResult {
|
||||
NAME: string;
|
||||
COMMENT: string;
|
||||
DEFINER: string;
|
||||
SOURCE: string;
|
||||
SECURITY: boolean;
|
||||
}
|
||||
|
||||
interface ProcedureParamsResult {
|
||||
PARAMETER_NAME: string;
|
||||
FIELD_TYPE: string;
|
||||
FIELD_LENGTH: string;
|
||||
FIELD_PRECISION: string;
|
||||
FIELD_SCALE: string;
|
||||
CONTEXT: string;
|
||||
}
|
||||
|
||||
const { rows: [procedure] } = await this.raw<antares.QueryResult<ProcedureResult>>(`
|
||||
SELECT
|
||||
RDB$PROCEDURE_NAME AS NAME,
|
||||
RDB$DESCRIPTION AS COMMENT,
|
||||
RDB$PROCEDURE_SOURCE AS SOURCE,
|
||||
RDB$OWNER_NAME AS DEFINER,
|
||||
RDB$SQL_SECURITY AS SECURITY
|
||||
FROM RDB$PROCEDURES
|
||||
WHERE RDB$SYSTEM_FLAG = 0
|
||||
AND RDB$PROCEDURE_NAME = '${routine}';
|
||||
`);
|
||||
|
||||
if (procedure) {
|
||||
const { rows: parameters } = await this.raw<antares.QueryResult<ProcedureParamsResult>>(`
|
||||
SELECT
|
||||
p.RDB$PARAMETER_NAME AS PARAMETER_NAME,
|
||||
p.RDB$PARAMETER_TYPE AS CONTEXT,
|
||||
CASE f.RDB$FIELD_TYPE
|
||||
WHEN 261 THEN 'BLOB'
|
||||
WHEN 14 THEN 'CHAR'
|
||||
WHEN 40 THEN 'CSTRING'
|
||||
WHEN 11 THEN 'D_FLOAT'
|
||||
WHEN 27 THEN 'DOUBLE PRECISION'
|
||||
WHEN 10 THEN 'FLOAT'
|
||||
WHEN 16 THEN 'BIGINT'
|
||||
WHEN 8 THEN 'INTEGER'
|
||||
WHEN 9 THEN 'QUAD'
|
||||
WHEN 7 THEN 'SMALLINT'
|
||||
WHEN 12 THEN 'DATE'
|
||||
WHEN 13 THEN 'TIME'
|
||||
WHEN 35 THEN 'TIMESTAMP'
|
||||
WHEN 37 THEN 'VARCHAR'
|
||||
ELSE 'UNKNOWN'
|
||||
END AS FIELD_TYPE,
|
||||
f.RDB$FIELD_LENGTH AS FIELD_LENGTH,
|
||||
f.RDB$FIELD_PRECISION AS FIELD_PRECISION,
|
||||
f.RDB$FIELD_SCALE AS FIELD_SCALE
|
||||
FROM RDB$PROCEDURE_PARAMETERS p
|
||||
JOIN RDB$FIELDS f ON f.RDB$FIELD_NAME = p.RDB$FIELD_SOURCE
|
||||
WHERE p.RDB$SYSTEM_FLAG = 0
|
||||
AND RDB$PROCEDURE_NAME = '${routine}'
|
||||
ORDER BY p.RDB$PARAMETER_TYPE, p.RDB$PARAMETER_NUMBER
|
||||
`);
|
||||
|
||||
const remappedParams = parameters.map(param => {
|
||||
const length = this.getTypeInfo(param.FIELD_TYPE.trim()).length ? param.FIELD_LENGTH || param.FIELD_PRECISION : null;
|
||||
return {
|
||||
name: param.PARAMETER_NAME.trim(),
|
||||
type: param.FIELD_TYPE.trim(),
|
||||
length: length,
|
||||
context: param.CONTEXT ? 'OUT' : 'IN'
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
definer: procedure.DEFINER,
|
||||
sql: procedure.SOURCE,
|
||||
parameters: remappedParams || [],
|
||||
name: procedure.NAME.trim(),
|
||||
comment: '',
|
||||
security: procedure.SECURITY === false ? 'INVOKER' : 'DEFINER',
|
||||
deterministic: false,
|
||||
dataAccess: 'CONTAINS SQL'
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
definer: null,
|
||||
sql: '',
|
||||
parameters: [],
|
||||
name: routine,
|
||||
comment: '',
|
||||
security: 'DEFINER',
|
||||
deterministic: false,
|
||||
dataAccess: 'CONTAINS SQL'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async dropRoutine (params: { routine: string }) {
|
||||
const sql = `DROP PROCEDURE "${params.routine}"`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
async alterRoutine ({ routine }: {routine: antares.AlterRoutineParams}) {
|
||||
const tempProcedure = Object.assign({}, routine);
|
||||
tempProcedure.name = `Antares_${tempProcedure.name}_tmp`;
|
||||
|
||||
try {
|
||||
await this.createRoutine(tempProcedure);
|
||||
await this.dropRoutine({ routine: tempProcedure.name });
|
||||
await this.dropRoutine({ routine: routine.oldName });
|
||||
await this.createRoutine(routine);
|
||||
}
|
||||
catch (err) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
}
|
||||
|
||||
async createRoutine (params: antares.CreateRoutineParams) {
|
||||
const inParams = 'parameters' in params
|
||||
? params.parameters
|
||||
.filter(param => param.context === 'IN')
|
||||
.reduce((acc: string[], curr) => {
|
||||
acc.push(`"${curr.name}" ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
||||
return acc;
|
||||
}, []).join(',')
|
||||
: '';
|
||||
|
||||
const ourParams = 'parameters' in params
|
||||
? params.parameters
|
||||
.filter(param => param.context === 'OUT')
|
||||
.reduce((acc: string[], curr) => {
|
||||
acc.push(`"${curr.name}" ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
||||
return acc;
|
||||
}, []).join(',')
|
||||
: '';
|
||||
|
||||
const sql = `
|
||||
CREATE PROCEDURE "${params.name}"(${inParams})
|
||||
${ourParams ? `RETURNS (${ourParams})` : ''}
|
||||
SQL SECURITY ${params.security}
|
||||
AS
|
||||
${params.sql}
|
||||
`;
|
||||
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
||||
async getEngines () {
|
||||
return {
|
||||
name: 'Firebird',
|
||||
|
@ -258,6 +258,9 @@ const runRoutine = (params?: string[]) => {
|
||||
case 'pg':
|
||||
sql = `CALL ${localElement.value.name}(${params.join(',')})`;
|
||||
break;
|
||||
case 'firebird':
|
||||
sql = `EXECUTE PROCEDURE "${localElement.value.name}"(${params.join(',')})`;
|
||||
break;
|
||||
// case 'mssql':
|
||||
// sql = `EXEC ${localElement.value.name} ${params.join(',')}`;
|
||||
// break;
|
||||
|
@ -18,7 +18,7 @@
|
||||
<span class="d-flex"><i class="mdi mdi-18px mdi-tune-vertical-variant text-light pr-1" /> {{ t('word.settings') }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedTable && selectedTable.type === 'table'"
|
||||
v-if="selectedTable && selectedTable.type === 'table' && customizations.tableDuplicate"
|
||||
class="context-element"
|
||||
@click="duplicateTable"
|
||||
>
|
||||
|
@ -291,7 +291,7 @@ watch(consoleHeight, () => {
|
||||
});
|
||||
|
||||
originalRoutine.value = {
|
||||
sql: customizations.value.functionSql,
|
||||
sql: customizations.value.procedureSql,
|
||||
language: customizations.value.languages ? customizations.value.languages[0] : null,
|
||||
name: '',
|
||||
definer: '',
|
||||
|
@ -351,6 +351,9 @@ const runRoutine = (params?: string[]) => {
|
||||
case 'pg':
|
||||
sql = `CALL ${originalRoutine.value.name}(${params.join(',')})`;
|
||||
break;
|
||||
case 'firebird':
|
||||
sql = `EXECUTE PROCEDURE "${originalRoutine.value.name}"(${params.join(',')})`;
|
||||
break;
|
||||
case 'mssql':
|
||||
sql = `EXEC ${originalRoutine.value.name} ${params.join(',')}`;
|
||||
break;
|
||||
|
@ -118,29 +118,17 @@
|
||||
{{ t('word.context') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<label class="form-radio">
|
||||
<label
|
||||
v-for="condext in customizations.procedureContextValues"
|
||||
:key="condext"
|
||||
class="form-radio"
|
||||
>
|
||||
<input
|
||||
v-model="selectedParamObj.context"
|
||||
type="radio"
|
||||
name="context"
|
||||
value="IN"
|
||||
> <i class="form-icon" /> IN
|
||||
</label>
|
||||
<label class="form-radio">
|
||||
<input
|
||||
v-model="selectedParamObj.context"
|
||||
type="radio"
|
||||
value="OUT"
|
||||
name="context"
|
||||
> <i class="form-icon" /> OUT
|
||||
</label>
|
||||
<label class="form-radio">
|
||||
<input
|
||||
v-model="selectedParamObj.context"
|
||||
type="radio"
|
||||
value="INOUT"
|
||||
name="context"
|
||||
> <i class="form-icon" /> INOUT
|
||||
:value="condext"
|
||||
> <i class="form-icon" /> {{ condext }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user