mirror of https://github.com/Fabio286/antares.git
feat(PostgreSQL): procedure language select
This commit is contained in:
parent
dea5ec7513
commit
b33199ea59
|
@ -52,5 +52,13 @@ module.exports = {
|
||||||
procedureDeterministic: false,
|
procedureDeterministic: false,
|
||||||
procedureDataAccess: false,
|
procedureDataAccess: false,
|
||||||
procedureSql: false,
|
procedureSql: false,
|
||||||
parametersLength: false
|
procedureContext: false,
|
||||||
|
procedureLanguage: false,
|
||||||
|
functionDeterministic: false,
|
||||||
|
functionDataAccess: false,
|
||||||
|
functionSql: false,
|
||||||
|
functionContext: false,
|
||||||
|
functionLanguage: false,
|
||||||
|
parametersLength: false,
|
||||||
|
languages: false
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,5 +50,9 @@ module.exports = {
|
||||||
procedureDeterministic: true,
|
procedureDeterministic: true,
|
||||||
procedureDataAccess: true,
|
procedureDataAccess: true,
|
||||||
procedureSql: 'BEGIN\r\n\r\nEND',
|
procedureSql: 'BEGIN\r\n\r\nEND',
|
||||||
|
procedureContext: true,
|
||||||
|
functionDeterministic: true,
|
||||||
|
functionDataAccess: true,
|
||||||
|
functionSql: 'BEGIN\r\n\r\nEND',
|
||||||
parametersLength: true
|
parametersLength: true
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,25 +15,28 @@ module.exports = {
|
||||||
views: true,
|
views: true,
|
||||||
triggers: false,
|
triggers: false,
|
||||||
routines: true,
|
routines: true,
|
||||||
functions: false,
|
functions: true,
|
||||||
schedulers: false,
|
|
||||||
// Settings
|
// Settings
|
||||||
tableAdd: true,
|
tableAdd: true,
|
||||||
viewAdd: true,
|
viewAdd: true,
|
||||||
triggerAdd: false,
|
triggerAdd: false,
|
||||||
routineAdd: true,
|
routineAdd: true,
|
||||||
functionAdd: false,
|
functionAdd: true,
|
||||||
databaseEdit: false,
|
databaseEdit: false,
|
||||||
tableSettings: true,
|
tableSettings: true,
|
||||||
viewSettings: true,
|
viewSettings: true,
|
||||||
triggerSettings: false,
|
triggerSettings: false,
|
||||||
routineSettings: true,
|
routineSettings: true,
|
||||||
functionSettings: false,
|
functionSettings: true,
|
||||||
schedulerSettings: false,
|
|
||||||
indexes: true,
|
indexes: true,
|
||||||
foreigns: true,
|
foreigns: true,
|
||||||
sortableFields: false,
|
|
||||||
nullable: true,
|
nullable: true,
|
||||||
tableArray: true,
|
tableArray: true,
|
||||||
procedureSql: '$BODY$\r\n\r\n$BODY$'
|
procedureSql: '$BODY$\r\n\r\n$BODY$',
|
||||||
|
procedureContext: true,
|
||||||
|
procedureLanguage: true,
|
||||||
|
functionSql: '$BODY$\r\n\r\n$BODY$',
|
||||||
|
functionContext: true,
|
||||||
|
functionLanguage: true,
|
||||||
|
languages: ['sql', 'plpgsql', 'c', 'internal']
|
||||||
};
|
};
|
||||||
|
|
|
@ -166,18 +166,23 @@ export class PostgreSQLClient extends AntaresCore {
|
||||||
});
|
});
|
||||||
|
|
||||||
// FUNCTIONS
|
// FUNCTIONS
|
||||||
const remappedFunctions = functions.filter(func => func.Db === db.database).map(func => {
|
const remappedFunctions = functions.filter(func => func.routine_schema === db.database && func.data_type !== 'trigger').map(func => {
|
||||||
return {
|
return {
|
||||||
name: func.routine_name,
|
name: func.routine_name,
|
||||||
type: func.routine_type,
|
type: func.routine_type,
|
||||||
definer: null, // func.Definer,
|
|
||||||
created: null, // func.Created,
|
|
||||||
updated: null, // func.Modified,
|
|
||||||
comment: null, // func.Comment,
|
|
||||||
charset: null, // func.character_set_client,
|
|
||||||
security: func.security_type
|
security: func.security_type
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TRIGGER FUNCTIONS
|
||||||
|
const remappedTriggerFunctions = functions.filter(func => func.routine_schema === db.database && func.data_type === 'trigger').map(func => {
|
||||||
|
return {
|
||||||
|
name: func.routine_name,
|
||||||
|
type: func.routine_type,
|
||||||
|
security: func.security_type
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// TRIGGERS
|
// TRIGGERS
|
||||||
const remappedTriggers = triggersArr.filter(trigger => trigger.Db === db.database).map(trigger => {
|
const remappedTriggers = triggersArr.filter(trigger => trigger.Db === db.database).map(trigger => {
|
||||||
return {
|
return {
|
||||||
|
@ -196,6 +201,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||||
functions: remappedFunctions,
|
functions: remappedFunctions,
|
||||||
procedures: remappedProcedures,
|
procedures: remappedProcedures,
|
||||||
triggers: remappedTriggers,
|
triggers: remappedTriggers,
|
||||||
|
triggerFunctions: remappedTriggerFunctions,
|
||||||
schedulers: []
|
schedulers: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -667,7 +673,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||||
this.use(this._schema);
|
this.use(this._schema);
|
||||||
|
|
||||||
const sql = `CREATE PROCEDURE ${this._schema}.${routine.name}(${parameters})
|
const sql = `CREATE PROCEDURE ${this._schema}.${routine.name}(${parameters})
|
||||||
LANGUAGE SQL
|
LANGUAGE ${routine.language}
|
||||||
SECURITY ${routine.security}
|
SECURITY ${routine.security}
|
||||||
AS ${routine.sql}`;
|
AS ${routine.sql}`;
|
||||||
|
|
||||||
|
@ -681,63 +687,66 @@ export class PostgreSQLClient extends AntaresCore {
|
||||||
* @memberof PostgreSQLClient
|
* @memberof PostgreSQLClient
|
||||||
*/
|
*/
|
||||||
async getFunctionInformations ({ schema, func }) {
|
async getFunctionInformations ({ schema, func }) {
|
||||||
const sql = `SHOW CREATE FUNCTION \`${schema}\`.\`${func}\``;
|
const sql = `SELECT pg_get_functiondef((SELECT oid FROM pg_proc WHERE proname = '${func}'));`;
|
||||||
const results = await this.raw(sql);
|
const results = await this.raw(sql);
|
||||||
|
|
||||||
return results.rows.map(row => {
|
return results.rows.map(async row => {
|
||||||
if (!row['Create Function']) {
|
if (!row.pg_get_functiondef) {
|
||||||
return {
|
return {
|
||||||
definer: null,
|
definer: null,
|
||||||
sql: '',
|
sql: '',
|
||||||
parameters: [],
|
parameters: [],
|
||||||
name: row.Procedure,
|
name: func,
|
||||||
comment: '',
|
comment: '',
|
||||||
security: 'DEFINER',
|
security: 'DEFINER',
|
||||||
deterministic: false,
|
deterministic: false,
|
||||||
dataAccess: 'CONTAINS SQL',
|
dataAccess: 'CONTAINS SQL'
|
||||||
returns: 'INT',
|
|
||||||
returnsLength: null
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const parameters = row['Create Function']
|
const sql = `SELECT proc.specific_schema AS procedure_schema,
|
||||||
.match(/(\([^()]*(?:(?:\([^()]*\))[^()]*)*\)\s*)/s)[0]
|
proc.specific_name,
|
||||||
.replaceAll('\r', '')
|
proc.routine_name AS procedure_name,
|
||||||
.replaceAll('\t', '')
|
proc.external_language,
|
||||||
.slice(1, -1)
|
args.parameter_name,
|
||||||
.split(',')
|
args.parameter_mode,
|
||||||
.map(el => {
|
args.data_type
|
||||||
const param = el.split(' ');
|
FROM information_schema.routines proc
|
||||||
const type = param[1] ? param[1].replace(')', '').split('(') : ['', null];
|
LEFT JOIN information_schema.parameters args
|
||||||
|
ON proc.specific_schema = args.specific_schema
|
||||||
|
AND proc.specific_name = args.specific_name
|
||||||
|
WHERE proc.routine_schema not in ('pg_catalog', 'information_schema')
|
||||||
|
AND proc.routine_type = 'FUNCTION'
|
||||||
|
AND proc.routine_name = '${func}'
|
||||||
|
AND proc.specific_schema = '${schema}'
|
||||||
|
ORDER BY procedure_schema,
|
||||||
|
specific_name,
|
||||||
|
procedure_name,
|
||||||
|
args.ordinal_position
|
||||||
|
`;
|
||||||
|
|
||||||
return {
|
const results = await this.raw(sql);
|
||||||
name: param[0] ? param[0].replaceAll('`', '') : '',
|
|
||||||
type: type[0],
|
|
||||||
length: +type[1] ? +type[1].replace(/\D/g, '') : ''
|
|
||||||
};
|
|
||||||
}).filter(el => el.name);
|
|
||||||
|
|
||||||
let dataAccess = 'CONTAINS SQL';
|
const parameters = results.rows.map(row => {
|
||||||
if (row['Create Function'].includes('NO SQL'))
|
return {
|
||||||
dataAccess = 'NO SQL';
|
name: row.parameter_name,
|
||||||
if (row['Create Function'].includes('READS SQL DATA'))
|
type: row.data_type.toUpperCase(),
|
||||||
dataAccess = 'READS SQL DATA';
|
length: '',
|
||||||
if (row['Create Function'].includes('MODIFIES SQL DATA'))
|
context: row.parameter_mode
|
||||||
dataAccess = 'MODIFIES SQL DATA';
|
};
|
||||||
|
});
|
||||||
const output = row['Create Function'].match(/(?<=RETURNS ).*?(?=\s)/gs).length ? row['Create Function'].match(/(?<=RETURNS ).*?(?=\s)/gs)[0].replace(')', '').split('(') : ['', null];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
definer: row['Create Function'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
|
definer: '',
|
||||||
sql: row['Create Function'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
|
sql: row.pg_get_functiondef.match(/(\$(.*)\$)(.*)(\$(.*)\$)/gs)[0],
|
||||||
parameters: parameters || [],
|
parameters: parameters || [],
|
||||||
name: row.Function,
|
name: func,
|
||||||
comment: row['Create Function'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Function'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
|
comment: '',
|
||||||
security: row['Create Function'].includes('SQL SECURITY INVOKER') ? 'INVOKER' : 'DEFINER',
|
security: row.pg_get_functiondef.includes('SECURITY DEFINER') ? 'DEFINER' : 'INVOKER',
|
||||||
deterministic: row['Create Function'].includes('DETERMINISTIC'),
|
deterministic: null,
|
||||||
dataAccess,
|
dataAccess: null,
|
||||||
returns: output[0].toUpperCase(),
|
language: row.pg_get_functiondef.match(/(?<=LANGUAGE )(.*)(?<=[\S+\n\r\s])/gm)[0],
|
||||||
returnsLength: +output[1]
|
returns: row.pg_get_functiondef.match(/(?<=RETURNS SETOF )(.*)(?<=[\S+\n\r\s])/gm)[0].toUpperCase()
|
||||||
};
|
};
|
||||||
})[0];
|
})[0];
|
||||||
}
|
}
|
||||||
|
@ -788,7 +797,7 @@ export class PostgreSQLClient extends AntaresCore {
|
||||||
}, []).join(',');
|
}, []).join(',');
|
||||||
|
|
||||||
const sql = `CREATE ${func.definer ? `DEFINER=${func.definer} ` : ''}FUNCTION \`${func.name}\`(${parameters}) RETURNS ${func.returns}${func.returnsLength ? `(${func.returnsLength})` : ''}
|
const sql = `CREATE ${func.definer ? `DEFINER=${func.definer} ` : ''}FUNCTION \`${func.name}\`(${parameters}) RETURNS ${func.returns}${func.returnsLength ? `(${func.returnsLength})` : ''}
|
||||||
LANGUAGE SQL
|
LANGUAGE ${func.language}
|
||||||
${func.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
${func.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
||||||
${func.dataAccess}
|
${func.dataAccess}
|
||||||
SQL SECURITY ${func.security}
|
SQL SECURITY ${func.security}
|
||||||
|
|
|
@ -38,7 +38,6 @@
|
||||||
<a class="c-hand" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
|
<a class="c-hand" :class="{'badge badge-update': hasUpdates}">{{ $t('word.update') }}</a>
|
||||||
</li>
|
</li>
|
||||||
<li
|
<li
|
||||||
v-if="updateStatus !== 'disabled'"
|
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{'active': selectedTab === 'changelog'}"
|
:class="{'active': selectedTab === 'changelog'}"
|
||||||
@click="selectTab('changelog')"
|
@click="selectTab('changelog')"
|
||||||
|
@ -416,6 +415,7 @@ ORDER BY
|
||||||
|
|
||||||
.panel-body {
|
.panel-body {
|
||||||
min-height: calc(25vh - 70px);
|
min-height: calc(25vh - 70px);
|
||||||
|
max-height: 65vh;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
|
||||||
.theme-block {
|
.theme-block {
|
||||||
|
|
|
@ -75,8 +75,8 @@
|
||||||
<div>
|
<div>
|
||||||
<ul class="menu menu-nav pt-0">
|
<ul class="menu menu-nav pt-0">
|
||||||
<li
|
<li
|
||||||
v-for="procedure of filteredProcedures"
|
v-for="(procedure, i) of filteredProcedures"
|
||||||
:key="procedure.name"
|
:key="`${procedure.name}-${i}`"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure === procedure.name}"
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure === procedure.name}"
|
||||||
@click="setBreadcrumbs({schema: database.name, procedure: procedure.name})"
|
@click="setBreadcrumbs({schema: database.name, procedure: procedure.name})"
|
||||||
|
@ -103,8 +103,8 @@
|
||||||
<div>
|
<div>
|
||||||
<ul class="menu menu-nav pt-0">
|
<ul class="menu menu-nav pt-0">
|
||||||
<li
|
<li
|
||||||
v-for="func of filteredFunctions"
|
v-for="(func, i) of filteredFunctions"
|
||||||
:key="func.name"
|
:key="`${func.name}-${i}`"
|
||||||
class="menu-item"
|
class="menu-item"
|
||||||
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
|
:class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}"
|
||||||
@click="setBreadcrumbs({schema: database.name, function: func.name})"
|
@click="setBreadcrumbs({schema: database.name, function: func.name})"
|
||||||
|
|
|
@ -26,6 +26,18 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="customizations.languages" class="form-group">
|
||||||
|
<label class="form-label col-4">
|
||||||
|
{{ $t('word.language') }}
|
||||||
|
</label>
|
||||||
|
<div class="column">
|
||||||
|
<select v-model="optionsProxy.language" class="form-select">
|
||||||
|
<option v-for="language in customizations.languages" :key="language">
|
||||||
|
{{ language }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-if="customizations.definer" class="form-group">
|
<div v-if="customizations.definer" class="form-group">
|
||||||
<label class="form-label col-4">
|
<label class="form-label col-4">
|
||||||
{{ $t('word.definer') }}
|
{{ $t('word.definer') }}
|
||||||
|
|
|
@ -119,7 +119,7 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div v-if="customizations.procedureContext" class="form-group">
|
||||||
<label class="form-label col-3">
|
<label class="form-label col-3">
|
||||||
{{ $t('word.context') }}
|
{{ $t('word.context') }}
|
||||||
</label>
|
</label>
|
||||||
|
|
Loading…
Reference in New Issue