mirror of https://github.com/Fabio286/antares.git
feat(PostgreSQL): functions management
This commit is contained in:
parent
b33199ea59
commit
cd31413256
|
@ -727,7 +727,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||
|
||||
const results = await this.raw(sql);
|
||||
|
||||
const parameters = results.rows.map(row => {
|
||||
const parameters = results.rows.filter(row => row.parameter_mode).map(row => {
|
||||
return {
|
||||
name: row.parameter_name,
|
||||
type: row.data_type.toUpperCase(),
|
||||
|
@ -746,7 +746,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||
deterministic: null,
|
||||
dataAccess: null,
|
||||
language: row.pg_get_functiondef.match(/(?<=LANGUAGE )(.*)(?<=[\S+\n\r\s])/gm)[0],
|
||||
returns: row.pg_get_functiondef.match(/(?<=RETURNS SETOF )(.*)(?<=[\S+\n\r\s])/gm)[0].toUpperCase()
|
||||
returns: row.pg_get_functiondef.match(/(?<=RETURNS )(.*)(?<=[\S+\n\r\s])/gm)[0].replace('SETOF ', '').toUpperCase()
|
||||
};
|
||||
})[0];
|
||||
}
|
||||
|
@ -758,7 +758,7 @@ export class PostgreSQLClient extends AntaresCore {
|
|||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async dropFunction (params) {
|
||||
const sql = `DROP FUNCTION \`${params.func}\``;
|
||||
const sql = `DROP FUNCTION ${this._schema}.${params.func}`;
|
||||
return await this.raw(sql);
|
||||
}
|
||||
|
||||
|
@ -791,18 +791,23 @@ export class PostgreSQLClient extends AntaresCore {
|
|||
* @memberof PostgreSQLClient
|
||||
*/
|
||||
async createFunction (func) {
|
||||
const parameters = func.parameters.reduce((acc, curr) => {
|
||||
acc.push(`\`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
||||
const parameters = 'parameters' in func
|
||||
? func.parameters.reduce((acc, curr) => {
|
||||
acc.push(`${curr.context} ${curr.name} ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
|
||||
return acc;
|
||||
}, []).join(',');
|
||||
}, []).join(',')
|
||||
: '';
|
||||
|
||||
const sql = `CREATE ${func.definer ? `DEFINER=${func.definer} ` : ''}FUNCTION \`${func.name}\`(${parameters}) RETURNS ${func.returns}${func.returnsLength ? `(${func.returnsLength})` : ''}
|
||||
if (this._schema !== 'public')
|
||||
this.use(this._schema);
|
||||
|
||||
const body = func.returns ? func.sql : '$BODY$\n$BODY$';
|
||||
|
||||
const sql = `CREATE FUNCTION ${this._schema}.${func.name}(${parameters})
|
||||
RETURNS ${func.returns || 'void'}
|
||||
LANGUAGE ${func.language}
|
||||
${func.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
|
||||
${func.dataAccess}
|
||||
SQL SECURITY ${func.security}
|
||||
COMMENT '${func.comment}'
|
||||
${func.sql}`;
|
||||
SECURITY ${func.security}
|
||||
AS ${body}`;
|
||||
|
||||
return await this.raw(sql, { split: false });
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<div class="content">
|
||||
<form class="form-horizontal">
|
||||
<div
|
||||
v-for="(parameter, i) in localRoutine.parameters"
|
||||
v-for="(parameter, i) in inParameters"
|
||||
:key="parameter._id"
|
||||
class="form-group"
|
||||
>
|
||||
|
@ -66,6 +66,11 @@ export default {
|
|||
values: {}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
inParameters () {
|
||||
return this.localRoutine.parameters.filter(param => param.context === 'IN');
|
||||
}
|
||||
},
|
||||
created () {
|
||||
window.addEventListener('keydown', this.onKey);
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
>
|
||||
<template :slot="'header'">
|
||||
<div class="d-flex">
|
||||
<i class="mdi mdi-24px mdi-plus mr-1" /> {{ $t('message.createNewRoutine') }}
|
||||
<i class="mdi mdi-24px mdi-plus mr-1" /> {{ $t('message.createNewFunction') }}
|
||||
</div>
|
||||
</template>
|
||||
<div :slot="'body'">
|
||||
|
@ -25,7 +25,19 @@
|
|||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div v-if="customizations.languages" class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.language') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<select v-model="localFunction.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">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.definer') }}
|
||||
</label>
|
||||
|
@ -53,42 +65,7 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.returns') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<div class="input-group">
|
||||
<select
|
||||
v-model="localFunction.returns"
|
||||
class="form-select text-uppercase"
|
||||
style="width: 0;"
|
||||
>
|
||||
<optgroup
|
||||
v-for="group in workspace.dataTypes"
|
||||
:key="group.group"
|
||||
:label="group.group"
|
||||
>
|
||||
<option
|
||||
v-for="type in group.types"
|
||||
:key="type.name"
|
||||
:selected="localFunction.returns === type.name"
|
||||
:value="type.name"
|
||||
>
|
||||
{{ type.name }}
|
||||
</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<input
|
||||
v-model="localFunction.returnsLength"
|
||||
class="form-input"
|
||||
type="number"
|
||||
min="0"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div v-if="customizations.comment" class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.comment') }}
|
||||
</label>
|
||||
|
@ -111,7 +88,7 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div v-if="customizations.functionDataAccess" class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('message.dataAccess') }}
|
||||
</label>
|
||||
|
@ -124,7 +101,7 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div v-if="customizations.functionDeterministic" class="form-group">
|
||||
<div class="col-4" />
|
||||
<div class="column">
|
||||
<label class="form-checkbox form-inline">
|
||||
|
@ -152,11 +129,12 @@ export default {
|
|||
return {
|
||||
localFunction: {
|
||||
definer: '',
|
||||
sql: 'BEGIN\r\n RETURN NULL;\r\nEND',
|
||||
sql: '',
|
||||
parameters: [],
|
||||
name: '',
|
||||
comment: '',
|
||||
returns: 'INT',
|
||||
language: null,
|
||||
returns: null,
|
||||
returnsLength: 10,
|
||||
security: 'DEFINER',
|
||||
deterministic: false,
|
||||
|
@ -168,9 +146,17 @@ export default {
|
|||
computed: {
|
||||
schema () {
|
||||
return this.workspace.breadcrumbs.schema;
|
||||
},
|
||||
customizations () {
|
||||
return this.workspace.customizations;
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.customizations.languages)
|
||||
this.localFunction.language = this.customizations.languages[0];
|
||||
|
||||
if (this.customizations.procedureSql)
|
||||
this.localFunction.sql = this.customizations.procedureSql;
|
||||
setTimeout(() => {
|
||||
this.$refs.firstInput.focus();
|
||||
}, 20);
|
||||
|
|
|
@ -25,6 +25,18 @@
|
|||
>
|
||||
</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="localRoutine.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">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.definer') }}
|
||||
|
@ -121,6 +133,7 @@ export default {
|
|||
parameters: [],
|
||||
name: '',
|
||||
comment: '',
|
||||
language: null,
|
||||
security: 'DEFINER',
|
||||
deterministic: false,
|
||||
dataAccess: 'CONTAINS SQL'
|
||||
|
@ -137,6 +150,9 @@ export default {
|
|||
}
|
||||
},
|
||||
mounted () {
|
||||
if (this.customizations.languages)
|
||||
this.localRoutine.language = this.customizations.languages[0];
|
||||
|
||||
if (this.customizations.procedureSql)
|
||||
this.localRoutine.sql = this.customizations.procedureSql;
|
||||
setTimeout(() => {
|
||||
|
|
|
@ -248,9 +248,11 @@ export default {
|
|||
switch (this.workspace.client) { // TODO: move in a better place
|
||||
case 'maria':
|
||||
case 'mysql':
|
||||
case 'pg':
|
||||
sql = `SELECT \`${this.localElement.name}\` (${params.join(',')})`;
|
||||
break;
|
||||
case 'pg':
|
||||
sql = `SELECT ${this.localElement.name}(${params.join(',')})`;
|
||||
break;
|
||||
case 'mssql':
|
||||
sql = `SELECT ${this.localElement.name} ${params.join(',')}`;
|
||||
break;
|
||||
|
|
|
@ -26,7 +26,19 @@
|
|||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<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">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.definer') }}
|
||||
</label>
|
||||
|
@ -81,6 +93,7 @@
|
|||
</optgroup>
|
||||
</select>
|
||||
<input
|
||||
v-if="customizations.parametersLength"
|
||||
v-model="optionsProxy.returnsLength"
|
||||
class="form-input"
|
||||
type="number"
|
||||
|
@ -89,7 +102,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div v-if="customizations.comment" class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('word.comment') }}
|
||||
</label>
|
||||
|
@ -112,7 +125,7 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div v-if="customizations.functionDataAccess" class="form-group">
|
||||
<label class="form-label col-4">
|
||||
{{ $t('message.dataAccess') }}
|
||||
</label>
|
||||
|
@ -125,7 +138,7 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div v-if="customizations.functionDeterministic" class="form-group">
|
||||
<div class="col-4" />
|
||||
<div class="column">
|
||||
<label class="form-checkbox form-inline">
|
||||
|
@ -159,6 +172,9 @@ export default {
|
|||
computed: {
|
||||
isTableNameValid () {
|
||||
return this.optionsProxy.name !== '';
|
||||
},
|
||||
customizations () {
|
||||
return this.workspace.customizations;
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
<div class="tile-title">
|
||||
{{ param.name }}
|
||||
</div>
|
||||
<small class="tile-subtitle text-gray">{{ param.type }}{{ param.length ? `(${param.length})` : '' }}</small>
|
||||
<small class="tile-subtitle text-gray">{{ param.type }}{{ param.length ? `(${param.length})` : '' }} · {{ param.context }}</small>
|
||||
</div>
|
||||
<div class="tile-action">
|
||||
<button
|
||||
|
@ -106,7 +106,7 @@
|
|||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div v-if="customizations.parametersLength" class="form-group">
|
||||
<label class="form-label col-3">
|
||||
{{ $t('word.length') }}
|
||||
</label>
|
||||
|
@ -119,6 +119,37 @@
|
|||
>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="customizations.functionContext" class="form-group">
|
||||
<label class="form-label col-3">
|
||||
{{ $t('word.context') }}
|
||||
</label>
|
||||
<div class="column">
|
||||
<label 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
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<div v-if="!parametersProxy.length" class="empty">
|
||||
<div class="empty-icon">
|
||||
|
@ -168,6 +199,9 @@ export default {
|
|||
},
|
||||
isChanged () {
|
||||
return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy);
|
||||
},
|
||||
customizations () {
|
||||
return this.workspace.customizations;
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
|
|
@ -281,9 +281,11 @@ export default {
|
|||
switch (this.connection.client) { // TODO: move in a better place
|
||||
case 'maria':
|
||||
case 'mysql':
|
||||
case 'pg':
|
||||
sql = `SELECT \`${this.originalFunction.name}\` (${params.join(',')})`;
|
||||
break;
|
||||
case 'pg':
|
||||
sql = `SELECT ${this.originalFunction.name}(${params.join(',')})`;
|
||||
break;
|
||||
case 'mssql':
|
||||
sql = `SELECT ${this.originalFunction.name} ${params.join(',')}`;
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue