mirror of
				https://github.com/Fabio286/antares.git
				synced 2025-06-05 21:59:22 +02:00 
			
		
		
		
	feat: stored routines edit
This commit is contained in:
		| @@ -2,6 +2,7 @@ import connection from './connection'; | ||||
| import tables from './tables'; | ||||
| import views from './views'; | ||||
| import triggers from './triggers'; | ||||
| import routines from './routines'; | ||||
| import updates from './updates'; | ||||
| import application from './application'; | ||||
| import database from './database'; | ||||
| @@ -14,6 +15,7 @@ export default () => { | ||||
|    tables(connections); | ||||
|    views(connections); | ||||
|    triggers(connections); | ||||
|    routines(connections); | ||||
|    database(connections); | ||||
|    users(connections); | ||||
|    updates(); | ||||
|   | ||||
							
								
								
									
										43
									
								
								src/main/ipc-handlers/routines.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/main/ipc-handlers/routines.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| import { ipcMain } from 'electron'; | ||||
|  | ||||
| export default (connections) => { | ||||
|    ipcMain.handle('get-routine-informations', async (event, params) => { | ||||
|       try { | ||||
|          const result = await connections[params.uid].getRoutineInformations(params); | ||||
|          return { status: 'success', response: result }; | ||||
|       } | ||||
|       catch (err) { | ||||
|          return { status: 'error', response: err.toString() }; | ||||
|       } | ||||
|    }); | ||||
|  | ||||
|    ipcMain.handle('drop-routine', async (event, params) => { | ||||
|       try { | ||||
|          await connections[params.uid].dropRoutine(params); | ||||
|          return { status: 'success' }; | ||||
|       } | ||||
|       catch (err) { | ||||
|          return { status: 'error', response: err.toString() }; | ||||
|       } | ||||
|    }); | ||||
|  | ||||
|    ipcMain.handle('alter-routine', async (event, params) => { | ||||
|       try { | ||||
|          await connections[params.uid].alterRoutine(params); | ||||
|          return { status: 'success' }; | ||||
|       } | ||||
|       catch (err) { | ||||
|          return { status: 'error', response: err.toString() }; | ||||
|       } | ||||
|    }); | ||||
|  | ||||
|    ipcMain.handle('create-routine', async (event, params) => { | ||||
|       try { | ||||
|          await connections[params.uid].createRoutine(params); | ||||
|          return { status: 'success' }; | ||||
|       } | ||||
|       catch (err) { | ||||
|          return { status: 'error', response: err.toString() }; | ||||
|       } | ||||
|    }); | ||||
| }; | ||||
| @@ -104,6 +104,20 @@ export class MySQLClient extends AntaresCore { | ||||
|             }; | ||||
|          }); | ||||
|  | ||||
|          // FUNCTIONS | ||||
|          const remappedFunctions = functions.filter(func => func.Db === db.Database).map(func => { | ||||
|             return { | ||||
|                name: func.Name, | ||||
|                type: func.Type, | ||||
|                definer: func.Definer, | ||||
|                created: func.Created, | ||||
|                updated: func.Modified, | ||||
|                comment: func.Comment, | ||||
|                charset: func.character_set_client, | ||||
|                security: func.Security_type | ||||
|             }; | ||||
|          }); | ||||
|  | ||||
|          // SCHEDULERS | ||||
|          const remappedSchedulers = schedulers.filter(scheduler => scheduler.Db === db.Database).map(scheduler => { | ||||
|             return { | ||||
| @@ -148,7 +162,7 @@ export class MySQLClient extends AntaresCore { | ||||
|          return { | ||||
|             name: db.Database, | ||||
|             tables: remappedTables, | ||||
|             functions: functions.filter(func => func.Db === db.Database), // TODO: remap functions | ||||
|             functions: remappedFunctions, | ||||
|             procedures: remappedProcedures, | ||||
|             triggers: remappedTriggers, | ||||
|             schedulers: remappedSchedulers | ||||
| @@ -355,7 +369,7 @@ export class MySQLClient extends AntaresCore { | ||||
|       return results.rows.map(row => { | ||||
|          return { | ||||
|             definer: row['SQL Original Statement'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0], | ||||
|             sql: row['SQL Original Statement'].match(/BEGIN(.*)END/gs)[0], | ||||
|             sql: row['SQL Original Statement'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0], | ||||
|             name: row.Trigger, | ||||
|             table: row['SQL Original Statement'].match(/(?<=ON `).*?(?=`)/gs)[0], | ||||
|             event1: row['SQL Original Statement'].match(/(BEFORE|AFTER)/gs)[0], | ||||
| @@ -405,9 +419,111 @@ export class MySQLClient extends AntaresCore { | ||||
|     */ | ||||
|    async createTrigger (trigger) { | ||||
|       const sql = `CREATE ${trigger.definer ? `DEFINER=${trigger.definer} ` : ''}TRIGGER \`${trigger.name}\` ${trigger.event1} ${trigger.event2} ON \`${trigger.table}\` FOR EACH ROW ${trigger.sql}`; | ||||
|       return await this.raw(sql, { split: false }); | ||||
|    } | ||||
|  | ||||
|    /** | ||||
|     * SHOW CREATE PROCEDURE | ||||
|     * | ||||
|     * @returns {Array.<Object>} view informations | ||||
|     * @memberof MySQLClient | ||||
|     */ | ||||
|    async getRoutineInformations ({ schema, routine }) { | ||||
|       const sql = `SHOW CREATE PROCEDURE \`${schema}\`.\`${routine}\``; | ||||
|       const results = await this.raw(sql); | ||||
|  | ||||
|       return results.rows.map(row => { | ||||
|          const parameters = row['Create Procedure'] | ||||
|             .match(/(?<=\().*?(?=\))/s)[0] | ||||
|             .replaceAll('\r', '') | ||||
|             .replaceAll('\t', '') | ||||
|             .split(',') | ||||
|             .map(el => { | ||||
|                const param = el.split(' '); | ||||
|                return { | ||||
|                   name: param[1] ? param[1].replaceAll('`', '') : '', | ||||
|                   type: param[2] ? param[2].replace(',', '') : '', | ||||
|                   context: param[0] ? param[0].replace('\n', '') : '' | ||||
|                }; | ||||
|             }).filter(el => el.name); | ||||
|  | ||||
|          let dataAccess = 'CONTAINS SQL'; | ||||
|          if (row['Create Procedure'].includes('NO SQL')) | ||||
|             dataAccess = 'NO SQL'; | ||||
|          if (row['Create Procedure'].includes('READS SQL DATA')) | ||||
|             dataAccess = 'READS SQL DATA'; | ||||
|          if (row['Create Procedure'].includes('MODIFIES SQL DATA')) | ||||
|             dataAccess = 'MODIFIES SQL DATA'; | ||||
|  | ||||
|          return { | ||||
|             definer: row['Create Procedure'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0], | ||||
|             sql: row['Create Procedure'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0], | ||||
|             parameters, | ||||
|             name: row.Procedure, | ||||
|             comment: row['Create Procedure'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Procedure'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '', | ||||
|             security: row['Create Procedure'].includes('SQL SECURITY INVOKER') ? 'INVOKER' : 'DEFINER', | ||||
|             deterministic: row['Create Procedure'].includes('DETERMINISTIC'), | ||||
|             dataAccess | ||||
|          }; | ||||
|       })[0]; | ||||
|    } | ||||
|  | ||||
|    /** | ||||
|     * DROP PROCEDURE | ||||
|     * | ||||
|     * @returns {Array.<Object>} parameters | ||||
|     * @memberof MySQLClient | ||||
|     */ | ||||
|    async dropRoutine (params) { | ||||
|       const sql = `DROP PROCEDURE \`${params.routine}\``; | ||||
|       return await this.raw(sql); | ||||
|    } | ||||
|  | ||||
|    /** | ||||
|     * ALTER PROCEDURE | ||||
|     * | ||||
|     * @returns {Array.<Object>} parameters | ||||
|     * @memberof MySQLClient | ||||
|     */ | ||||
|    async alterRoutine (params) { | ||||
|       const { routine } = params; | ||||
|       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); | ||||
|       } | ||||
|    } | ||||
|  | ||||
|    /** | ||||
|     * CREATE PROCEDURE | ||||
|     * | ||||
|     * @returns {Array.<Object>} parameters | ||||
|     * @memberof MySQLClient | ||||
|     */ | ||||
|    async createRoutine (routine) { | ||||
|       const parameters = routine.parameters.reduce((acc, curr) => { | ||||
|          acc.push(`${curr.context} \`${curr.name}\` ${curr.type}`); | ||||
|          return acc; | ||||
|       }, []).join(','); | ||||
|  | ||||
|       const sql = `CREATE ${routine.definer ? `DEFINER=${routine.definer} ` : ''}PROCEDURE \`${routine.name}\`(${parameters}) | ||||
|          LANGUAGE SQL | ||||
|          ${routine.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'} | ||||
|          ${routine.dataAccess} | ||||
|          SQL SECURITY ${routine.security} | ||||
|          COMMENT '${routine.comment}' | ||||
|          ${routine.sql}`; | ||||
|  | ||||
|       return await this.raw(sql, { split: false }); | ||||
|    } | ||||
|  | ||||
|    /** | ||||
|     * SHOW COLLATION | ||||
|     * | ||||
| @@ -709,6 +825,7 @@ export class MySQLClient extends AntaresCore { | ||||
|     * @param {object} args | ||||
|     * @param {boolean} args.nest | ||||
|     * @param {boolean} args.details | ||||
|     * @param {boolean} args.split | ||||
|     * @returns {Promise} | ||||
|     * @memberof MySQLClient | ||||
|     */ | ||||
| @@ -716,13 +833,14 @@ export class MySQLClient extends AntaresCore { | ||||
|       args = { | ||||
|          nest: false, | ||||
|          details: false, | ||||
|          split: true, | ||||
|          ...args | ||||
|       }; | ||||
|       const nestTables = args.nest ? '.' : false; | ||||
|       const resultsArr = []; | ||||
|       let paramsArr = []; | ||||
|       let selectedFields = []; | ||||
|       const queries = sql.split(';'); | ||||
|       const queries = args.split ? sql.split(';') : [sql]; | ||||
|  | ||||
|       if (process.env.NODE_ENV === 'development') this._logger(sql);// TODO: replace BLOB content with a placeholder | ||||
|  | ||||
|   | ||||
| @@ -89,7 +89,6 @@ | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { mapGetters } from 'vuex'; | ||||
| import ConfirmModal from '@/components/BaseConfirmModal'; | ||||
|  | ||||
| export default { | ||||
| @@ -114,9 +113,6 @@ export default { | ||||
|       }; | ||||
|    }, | ||||
|    computed: { | ||||
|       ...mapGetters({ | ||||
|          selectedWorkspace: 'workspaces/getSelected' | ||||
|       }), | ||||
|       schema () { | ||||
|          return this.workspace.breadcrumbs.schema; | ||||
|       }, | ||||
|   | ||||
| @@ -83,6 +83,12 @@ | ||||
|             :connection="connection" | ||||
|             :trigger="workspace.breadcrumbs.trigger" | ||||
|          /> | ||||
|          <WorkspacePropsTabRoutine | ||||
|             v-show="selectedTab === 'prop' && workspace.breadcrumbs.procedure" | ||||
|             :is-selected="selectedTab === 'prop'" | ||||
|             :connection="connection" | ||||
|             :routine="workspace.breadcrumbs.procedure" | ||||
|          /> | ||||
|          <WorkspaceTableTab | ||||
|             v-show="selectedTab === 'data'" | ||||
|             :connection="connection" | ||||
| @@ -108,6 +114,7 @@ import WorkspaceTableTab from '@/components/WorkspaceTableTab'; | ||||
| import WorkspacePropsTab from '@/components/WorkspacePropsTab'; | ||||
| import WorkspacePropsTabView from '@/components/WorkspacePropsTabView'; | ||||
| import WorkspacePropsTabTrigger from '@/components/WorkspacePropsTabTrigger'; | ||||
| import WorkspacePropsTabRoutine from '@/components/WorkspacePropsTabRoutine'; | ||||
|  | ||||
| export default { | ||||
|    name: 'Workspace', | ||||
| @@ -117,7 +124,8 @@ export default { | ||||
|       WorkspaceTableTab, | ||||
|       WorkspacePropsTab, | ||||
|       WorkspacePropsTabView, | ||||
|       WorkspacePropsTabTrigger | ||||
|       WorkspacePropsTabTrigger, | ||||
|       WorkspacePropsTabRoutine | ||||
|    }, | ||||
|    props: { | ||||
|       connection: Object | ||||
| @@ -144,6 +152,7 @@ export default { | ||||
|             this.workspace.breadcrumbs.view === null && | ||||
|             this.workspace.breadcrumbs.trigger === null && | ||||
|             this.workspace.breadcrumbs.procedure === null && | ||||
|             this.workspace.breadcrumbs.function === null && | ||||
|             this.workspace.breadcrumbs.scheduler === null && | ||||
|             ['data', 'prop'].includes(this.workspace.selected_tab) | ||||
|          ) | ||||
|   | ||||
| @@ -67,7 +67,7 @@ | ||||
|          <div v-if="database.procedures.length" class="database-misc"> | ||||
|             <details class="accordion"> | ||||
|                <summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.procedure}"> | ||||
|                   <i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" /> | ||||
|                   <i class="misc-icon mdi mdi-18px mdi-folder-sync mr-1" /> | ||||
|                   {{ $tc('word.storedRoutine', 2) }} | ||||
|                </summary> | ||||
|                <div class="accordion-body"> | ||||
| @@ -82,7 +82,7 @@ | ||||
|                            @contextmenu.prevent="showMiscContext($event, {...procedure, type: 'procedure'})" | ||||
|                         > | ||||
|                            <a class="table-name"> | ||||
|                               <i class="table-icon mdi mdi-arrow-right-bold-box mdi-18px mr-1" /> | ||||
|                               <i class="table-icon mdi mdi-sync-circle mdi-18px mr-1" /> | ||||
|                               <span>{{ procedure.name }}</span> | ||||
|                            </a> | ||||
|                         </li> | ||||
| @@ -92,6 +92,34 @@ | ||||
|             </details> | ||||
|          </div> | ||||
|  | ||||
|          <div v-if="database.functions.length" class="database-misc"> | ||||
|             <details class="accordion"> | ||||
|                <summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function}"> | ||||
|                   <i class="misc-icon mdi mdi-18px mdi-folder-move mr-1" /> | ||||
|                   {{ $tc('word.function', 2) }} | ||||
|                </summary> | ||||
|                <div class="accordion-body"> | ||||
|                   <div> | ||||
|                      <ul class="menu menu-nav pt-0"> | ||||
|                         <li | ||||
|                            v-for="func of database.functions" | ||||
|                            :key="func.name" | ||||
|                            class="menu-item" | ||||
|                            :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.function === func.name}" | ||||
|                            @click="setBreadcrumbs({schema: database.name, function: func.name})" | ||||
|                            @contextmenu.prevent="showMiscContext($event, {...func, type: 'function'})" | ||||
|                         > | ||||
|                            <a class="table-name"> | ||||
|                               <i class="table-icon mdi mdi-arrow-right-bold-box mdi-18px mr-1" /> | ||||
|                               <span>{{ func.name }}</span> | ||||
|                            </a> | ||||
|                         </li> | ||||
|                      </ul> | ||||
|                   </div> | ||||
|                </div> | ||||
|             </details> | ||||
|          </div> | ||||
|  | ||||
|          <div v-if="database.schedulers.length" class="database-misc"> | ||||
|             <details class="accordion"> | ||||
|                <summary class="accordion-header misc-name" :class="{'text-bold': breadcrumbs.schema === database.name && breadcrumbs.scheduler}"> | ||||
|   | ||||
| @@ -16,10 +16,13 @@ | ||||
|             <div class="context-element" @click="showCreateTriggerModal"> | ||||
|                <span class="d-flex"><i class="mdi mdi-18px mdi-table-cog text-light pr-1" /> {{ $tc('word.trigger', 1) }}</span> | ||||
|             </div> | ||||
|             <div class="context-element d-none" @click="false"> | ||||
|                <span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span> | ||||
|             <div class="context-element" @click="false"> | ||||
|                <span class="d-flex"><i class="mdi mdi-18px mdi-sync-circle pr-1" /> {{ $tc('word.storedRoutine', 1) }}</span> | ||||
|             </div> | ||||
|             <div class="context-element d-none" @click="false"> | ||||
|             <div class="context-element" @click="false"> | ||||
|                <span class="d-flex"><i class="mdi mdi-18px mdi-arrow-right-bold-box pr-1" /> {{ $tc('word.function', 1) }}</span> | ||||
|             </div> | ||||
|             <div class="context-element" @click="false"> | ||||
|                <span class="d-flex"><i class="mdi mdi-18px mdi-calendar-clock text-light pr-1" /> {{ $tc('word.scheduler', 1) }}</span> | ||||
|             </div> | ||||
|          </div> | ||||
|   | ||||
							
								
								
									
										145
									
								
								src/renderer/components/WorkspacePropsRoutineOptionsModal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								src/renderer/components/WorkspacePropsRoutineOptionsModal.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| <template> | ||||
|    <ConfirmModal | ||||
|       :confirm-text="$t('word.confirm')" | ||||
|       size="400" | ||||
|       @confirm="confirmOptionsChange" | ||||
|       @hide="$emit('hide')" | ||||
|    > | ||||
|       <template :slot="'header'"> | ||||
|          <div class="d-flex"> | ||||
|             <i class="mdi mdi-24px mdi-cogs mr-1" /> {{ $t('word.options') }} "{{ localOptions.name }}" | ||||
|          </div> | ||||
|       </template> | ||||
|       <div :slot="'body'"> | ||||
|          <form class="form-horizontal"> | ||||
|             <div class="form-group"> | ||||
|                <label class="form-label col-4"> | ||||
|                   {{ $t('word.name') }} | ||||
|                </label> | ||||
|                <div class="column"> | ||||
|                   <input | ||||
|                      ref="firstInput" | ||||
|                      v-model="optionsProxy.name" | ||||
|                      class="form-input" | ||||
|                      :class="{'is-error': !isTableNameValid}" | ||||
|                      type="text" | ||||
|                   > | ||||
|                </div> | ||||
|             </div> | ||||
|             <div class="form-group"> | ||||
|                <label class="form-label col-4"> | ||||
|                   {{ $t('word.definer') }} | ||||
|                </label> | ||||
|                <div class="column"> | ||||
|                   <select | ||||
|                      v-if="workspace.users.length" | ||||
|                      v-model="optionsProxy.definer" | ||||
|                      class="form-select" | ||||
|                   > | ||||
|                      <option value=""> | ||||
|                         {{ $t('message.currentUser') }} | ||||
|                      </option> | ||||
|                      <option | ||||
|                         v-for="user in workspace.users" | ||||
|                         :key="`${user.name}@${user.host}`" | ||||
|                         :value="`\`${user.name}\`@\`${user.host}\``" | ||||
|                      > | ||||
|                         {{ user.name }}@{{ user.host }} | ||||
|                      </option> | ||||
|                   </select> | ||||
|                   <select v-if="!workspace.users.length" class="form-select"> | ||||
|                      <option value=""> | ||||
|                         {{ $t('message.currentUser') }} | ||||
|                      </option> | ||||
|                   </select> | ||||
|                </div> | ||||
|             </div> | ||||
|             <div class="form-group"> | ||||
|                <label class="form-label col-4"> | ||||
|                   {{ $t('word.comment') }} | ||||
|                </label> | ||||
|                <div class="column"> | ||||
|                   <input | ||||
|                      v-model="optionsProxy.comment" | ||||
|                      class="form-input" | ||||
|                      type="text" | ||||
|                   > | ||||
|                </div> | ||||
|             </div> | ||||
|             <div class="form-group"> | ||||
|                <label class="form-label col-4"> | ||||
|                   {{ $t('message.sqlSecurity') }} | ||||
|                </label> | ||||
|                <div class="column"> | ||||
|                   <select v-model="optionsProxy.security" class="form-select"> | ||||
|                      <option>DEFINER</option> | ||||
|                      <option>INVOKER</option> | ||||
|                   </select> | ||||
|                </div> | ||||
|             </div> | ||||
|             <div class="form-group"> | ||||
|                <label class="form-label col-4"> | ||||
|                   {{ $t('message.dataAccess') }} | ||||
|                </label> | ||||
|                <div class="column"> | ||||
|                   <select v-model="optionsProxy.dataAccess" class="form-select"> | ||||
|                      <option>CONTAINS SQL</option> | ||||
|                      <option>NO SQL</option> | ||||
|                      <option>READS SQL DATA</option> | ||||
|                      <option>MODIFIES SQL DATA</option> | ||||
|                   </select> | ||||
|                </div> | ||||
|             </div> | ||||
|             <div class="form-group"> | ||||
|                <div class="col-4" /> | ||||
|                <div class="column"> | ||||
|                   <label class="form-checkbox form-inline"> | ||||
|                      <input v-model="optionsProxy.deterministic" type="checkbox"><i class="form-icon" /> {{ $t('word.deterministic') }} | ||||
|                   </label> | ||||
|                </div> | ||||
|             </div> | ||||
|          </form> | ||||
|       </div> | ||||
|    </ConfirmModal> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import ConfirmModal from '@/components/BaseConfirmModal'; | ||||
|  | ||||
| export default { | ||||
|    name: 'WorkspacePropsRoutineOptionsModal', | ||||
|    components: { | ||||
|       ConfirmModal | ||||
|    }, | ||||
|    props: { | ||||
|       localOptions: Object, | ||||
|       workspace: Object | ||||
|    }, | ||||
|    data () { | ||||
|       return { | ||||
|          optionsProxy: {}, | ||||
|          isOptionsChanging: false | ||||
|       }; | ||||
|    }, | ||||
|    computed: { | ||||
|       isTableNameValid () { | ||||
|          return this.optionsProxy.name !== ''; | ||||
|       } | ||||
|    }, | ||||
|    created () { | ||||
|       this.optionsProxy = JSON.parse(JSON.stringify(this.localOptions)); | ||||
|  | ||||
|       setTimeout(() => { | ||||
|          this.$refs.firstInput.focus(); | ||||
|       }, 20); | ||||
|    }, | ||||
|    methods: { | ||||
|       confirmOptionsChange () { | ||||
|          if (!this.isTableNameValid) | ||||
|             this.optionsProxy.name = this.localOptions.name; | ||||
|  | ||||
|          this.$emit('options-update', this.optionsProxy); | ||||
|       } | ||||
|    } | ||||
| }; | ||||
| </script> | ||||
							
								
								
									
										287
									
								
								src/renderer/components/WorkspacePropsRoutineParamsModal.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										287
									
								
								src/renderer/components/WorkspacePropsRoutineParamsModal.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,287 @@ | ||||
| <template> | ||||
|    <ConfirmModal | ||||
|       :confirm-text="$t('word.confirm')" | ||||
|       size="medium" | ||||
|       @confirm="confirmIndexesChange" | ||||
|       @hide="$emit('hide')" | ||||
|    > | ||||
|       <template :slot="'header'"> | ||||
|          <div class="d-flex"> | ||||
|             <i class="mdi mdi-24px mdi-dots-horizontal mr-1" /> {{ $t('word.parameters') }} "{{ routine }}" | ||||
|          </div> | ||||
|       </template> | ||||
|       <div :slot="'body'"> | ||||
|          <div class="columns col-gapless"> | ||||
|             <div class="column col-5"> | ||||
|                <div class="panel" :style="{ height: modalInnerHeight + 'px'}"> | ||||
|                   <div class="panel-header pt-0 pl-0"> | ||||
|                      <div class="d-flex"> | ||||
|                         <button class="btn btn-dark btn-sm d-flex" @click="addParameter"> | ||||
|                            <span>{{ $t('word.add') }}</span> | ||||
|                            <i class="mdi mdi-24px mdi-plus ml-1" /> | ||||
|                         </button> | ||||
|                         <button | ||||
|                            class="btn btn-dark btn-sm d-flex ml-2 mr-0" | ||||
|                            :title="$t('message.clearChanges')" | ||||
|                            :disabled="!isChanged" | ||||
|                            @click.prevent="clearChanges" | ||||
|                         > | ||||
|                            <span>{{ $t('word.clear') }}</span> | ||||
|                            <i class="mdi mdi-24px mdi-delete-sweep ml-1" /> | ||||
|                         </button> | ||||
|                      </div> | ||||
|                   </div> | ||||
|                   <div ref="parametersPanel" class="panel-body p-0 pr-1"> | ||||
|                      <div | ||||
|                         v-for="param in parametersProxy" | ||||
|                         :key="param.name" | ||||
|                         class="tile tile-centered c-hand mb-1 p-1" | ||||
|                         :class="{'selected-param': selectedParam === param.name}" | ||||
|                         @click="selectParameter($event, param.name)" | ||||
|                      > | ||||
|                         <div class="tile-icon"> | ||||
|                            <div> | ||||
|                               <i class="mdi mdi-hexagon mdi-24px" :class="`type-${param.type.toLowerCase()}`" /> | ||||
|                            </div> | ||||
|                         </div> | ||||
|                         <div class="tile-content"> | ||||
|                            <div class="tile-title"> | ||||
|                               {{ param.name }} | ||||
|                            </div> | ||||
|                            <small class="tile-subtitle text-gray">{{ param.type }} · {{ param.context }}</small> | ||||
|                         </div> | ||||
|                         <div class="tile-action"> | ||||
|                            <button | ||||
|                               class="btn btn-link remove-field p-0 mr-2" | ||||
|                               :title="$t('word.delete')" | ||||
|                               @click.prevent="removeParameter(param.name)" | ||||
|                            > | ||||
|                               <i class="mdi mdi-close" /> | ||||
|                            </button> | ||||
|                         </div> | ||||
|                      </div> | ||||
|                   </div> | ||||
|                </div> | ||||
|             </div> | ||||
|  | ||||
|             <div class="column col-7 pl-2 editor-col"> | ||||
|                <form | ||||
|                   v-if="selectedParamObj" | ||||
|                   :style="{ height: modalInnerHeight + 'px'}" | ||||
|                   class="form-horizontal" | ||||
|                > | ||||
|                   <div class="form-group"> | ||||
|                      <label class="form-label col-3"> | ||||
|                         {{ $t('word.name') }} | ||||
|                      </label> | ||||
|                      <div class="column"> | ||||
|                         <input | ||||
|                            v-model="selectedParamObj.name" | ||||
|                            class="form-input" | ||||
|                            type="text" | ||||
|                         > | ||||
|                      </div> | ||||
|                   </div> | ||||
|                   <div class="form-group"> | ||||
|                      <label class="form-label col-3"> | ||||
|                         {{ $t('word.type') }} | ||||
|                      </label> | ||||
|                      <div class="column"> | ||||
|                         <select v-model="selectedParamObj.type" class="form-select text-uppercase"> | ||||
|                            <optgroup | ||||
|                               v-for="group in workspace.dataTypes" | ||||
|                               :key="group.group" | ||||
|                               :label="group.group" | ||||
|                            > | ||||
|                               <option | ||||
|                                  v-for="type in group.types" | ||||
|                                  :key="type.name" | ||||
|                                  :selected="selectedParamObj.type.toUpperCase() === type.name" | ||||
|                                  :value="type.name" | ||||
|                               > | ||||
|                                  {{ type.name }} | ||||
|                               </option> | ||||
|                            </optgroup> | ||||
|                         </select> | ||||
|                      </div> | ||||
|                   </div> | ||||
|                   <div 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"> | ||||
|                      <i class="mdi mdi-dots-horizontal mdi-48px" /> | ||||
|                   </div> | ||||
|                   <p class="empty-title h5"> | ||||
|                      {{ $t('message.thereAreNoParameters') }} | ||||
|                   </p> | ||||
|                   <div class="empty-action"> | ||||
|                      <button class="btn btn-primary" @click="addParameter"> | ||||
|                         {{ $t('message.createNewParameter') }} | ||||
|                      </button> | ||||
|                   </div> | ||||
|                </div> | ||||
|             </div> | ||||
|          </div> | ||||
|       </div> | ||||
|    </ConfirmModal> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import ConfirmModal from '@/components/BaseConfirmModal'; | ||||
|  | ||||
| export default { | ||||
|    name: 'WorkspacePropsRoutineParamsModal', | ||||
|    components: { | ||||
|       ConfirmModal | ||||
|    }, | ||||
|    props: { | ||||
|       localParameters: Array, | ||||
|       routine: String, | ||||
|       workspace: Object | ||||
|    }, | ||||
|    data () { | ||||
|       return { | ||||
|          parametersProxy: [], | ||||
|          isOptionsChanging: false, | ||||
|          selectedParam: '', | ||||
|          modalInnerHeight: 400, | ||||
|          i: 1 | ||||
|       }; | ||||
|    }, | ||||
|    computed: { | ||||
|       selectedParamObj () { | ||||
|          return this.parametersProxy.find(param => param.name === this.selectedParam); | ||||
|       }, | ||||
|       isChanged () { | ||||
|          return JSON.stringify(this.localParameters) !== JSON.stringify(this.parametersProxy); | ||||
|       } | ||||
|    }, | ||||
|    mounted () { | ||||
|       this.parametersProxy = JSON.parse(JSON.stringify(this.localParameters)); | ||||
|       this.i = this.parametersProxy.length + 1; | ||||
|  | ||||
|       if (this.parametersProxy.length) | ||||
|          this.resetSelectedID(); | ||||
|  | ||||
|       this.getModalInnerHeight(); | ||||
|       window.addEventListener('resize', this.getModalInnerHeight); | ||||
|    }, | ||||
|    destroyed () { | ||||
|       window.removeEventListener('resize', this.getModalInnerHeight); | ||||
|    }, | ||||
|    methods: { | ||||
|       confirmIndexesChange () { | ||||
|          this.$emit('parameters-update', this.parametersProxy); | ||||
|       }, | ||||
|       selectParameter (event, name) { | ||||
|          if (this.selectedParam !== name && !event.target.classList.contains('remove-field')) | ||||
|             this.selectedParam = name; | ||||
|       }, | ||||
|       getModalInnerHeight () { | ||||
|          const modalBody = document.querySelector('.modal-body'); | ||||
|          if (modalBody) | ||||
|             this.modalInnerHeight = modalBody.clientHeight - (parseFloat(getComputedStyle(modalBody).paddingTop) + parseFloat(getComputedStyle(modalBody).paddingBottom)); | ||||
|       }, | ||||
|       addParameter () { | ||||
|          this.parametersProxy = [...this.parametersProxy, { | ||||
|             name: `Param${this.i++}`, | ||||
|             type: 'INT', | ||||
|             context: 'IN' | ||||
|          }]; | ||||
|  | ||||
|          if (this.parametersProxy.length === 1) | ||||
|             this.resetSelectedID(); | ||||
|  | ||||
|          setTimeout(() => { | ||||
|             this.$refs.parametersPanel.scrollTop = this.$refs.parametersPanel.scrollHeight + 60; | ||||
|          }, 20); | ||||
|       }, | ||||
|       removeParameter (name) { | ||||
|          this.parametersProxy = this.parametersProxy.filter(param => param.name !== name); | ||||
|  | ||||
|          if (this.selectedParam === name && this.parametersProxy.length) | ||||
|             this.resetSelectedID(); | ||||
|       }, | ||||
|       clearChanges () { | ||||
|          this.parametersProxy = JSON.parse(JSON.stringify(this.localParameters)); | ||||
|          this.i = this.parametersProxy.length + 1; | ||||
|  | ||||
|          if (!this.parametersProxy.some(param => param.name === this.selectedParam)) | ||||
|             this.resetSelectedID(); | ||||
|       }, | ||||
|       resetSelectedID () { | ||||
|          this.selectedParam = this.parametersProxy.length ? this.parametersProxy[0].name : ''; | ||||
|       } | ||||
|    } | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .tile { | ||||
|   border-radius: 2px; | ||||
|   opacity: 0.5; | ||||
|   transition: background 0.2s; | ||||
|   transition: opacity 0.2s; | ||||
|  | ||||
|   .tile-action { | ||||
|     opacity: 0; | ||||
|     transition: opacity 0.2s; | ||||
|   } | ||||
|  | ||||
|   &:hover { | ||||
|     background: $bg-color-light; | ||||
|  | ||||
|     .tile-action { | ||||
|       opacity: 1; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   &.selected-param { | ||||
|     background: $bg-color-light; | ||||
|     opacity: 1; | ||||
|   } | ||||
| } | ||||
|  | ||||
| .editor-col { | ||||
|   border-left: 2px solid $bg-color-light; | ||||
| } | ||||
|  | ||||
| .fields-list { | ||||
|   max-height: 300px; | ||||
|   overflow: auto; | ||||
| } | ||||
|  | ||||
| .remove-field .mdi { | ||||
|   pointer-events: none; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										262
									
								
								src/renderer/components/WorkspacePropsTabRoutine.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								src/renderer/components/WorkspacePropsTabRoutine.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,262 @@ | ||||
| <template> | ||||
|    <div class="workspace-query-tab column col-12 columns col-gapless"> | ||||
|       <div class="workspace-query-runner column col-12"> | ||||
|          <div class="workspace-query-runner-footer"> | ||||
|             <div class="workspace-query-buttons"> | ||||
|                <button | ||||
|                   class="btn btn-primary btn-sm" | ||||
|                   :disabled="!isChanged" | ||||
|                   :class="{'loading':isSaving}" | ||||
|                   @click="saveChanges" | ||||
|                > | ||||
|                   <span>{{ $t('word.save') }}</span> | ||||
|                   <i class="mdi mdi-24px mdi-content-save ml-1" /> | ||||
|                </button> | ||||
|                <button | ||||
|                   :disabled="!isChanged" | ||||
|                   class="btn btn-link btn-sm mr-0" | ||||
|                   :title="$t('message.clearChanges')" | ||||
|                   @click="clearChanges" | ||||
|                > | ||||
|                   <span>{{ $t('word.clear') }}</span> | ||||
|                   <i class="mdi mdi-24px mdi-delete-sweep ml-1" /> | ||||
|                </button> | ||||
|  | ||||
|                <div class="divider-vert py-3" /> | ||||
|  | ||||
|                <button | ||||
|                   class="btn btn-dark btn-sm" | ||||
|                   :disabled="isChanged" | ||||
|                   @click="false" | ||||
|                > | ||||
|                   <span>{{ $t('word.run') }}</span> | ||||
|                   <i class="mdi mdi-24px mdi-play ml-1" /> | ||||
|                </button> | ||||
|                <button class="btn btn-dark btn-sm" @click="showParamsModal"> | ||||
|                   <span>{{ $t('word.parameters') }}</span> | ||||
|                   <i class="mdi mdi-24px mdi-dots-horizontal ml-1" /> | ||||
|                </button> | ||||
|                <button class="btn btn-dark btn-sm" @click="showOptionsModal"> | ||||
|                   <span>{{ $t('word.options') }}</span> | ||||
|                   <i class="mdi mdi-24px mdi-cogs ml-1" /> | ||||
|                </button> | ||||
|             </div> | ||||
|          </div> | ||||
|       </div> | ||||
|       <div class="workspace-query-results column col-12 mt-2"> | ||||
|          <label class="form-label ml-2">{{ $t('message.routineBody') }}</label> | ||||
|          <QueryEditor | ||||
|             v-if="isSelected" | ||||
|             ref="queryEditor" | ||||
|             :value.sync="localRoutine.sql" | ||||
|             :workspace="workspace" | ||||
|             :schema="schema" | ||||
|             :height="editorHeight" | ||||
|          /> | ||||
|       </div> | ||||
|       <WorkspacePropsRoutineOptionsModal | ||||
|          v-if="isOptionsModal" | ||||
|          :local-options="localRoutine" | ||||
|          :workspace="workspace" | ||||
|          @hide="hideOptionsModal" | ||||
|          @options-update="optionsUpdate" | ||||
|       /> | ||||
|       <WorkspacePropsRoutineParamsModal | ||||
|          v-if="isParamsModal" | ||||
|          :local-parameters="localRoutine.parameters" | ||||
|          :workspace="workspace" | ||||
|          :routine="localRoutine.name" | ||||
|          @hide="hideParamsModal" | ||||
|          @parameters-update="parametersUpdate" | ||||
|       /> | ||||
|    </div> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import { mapGetters, mapActions } from 'vuex'; | ||||
| import QueryEditor from '@/components/QueryEditor'; | ||||
| import WorkspacePropsRoutineOptionsModal from '@/components/WorkspacePropsRoutineOptionsModal'; | ||||
| import WorkspacePropsRoutineParamsModal from '@/components/WorkspacePropsRoutineParamsModal'; | ||||
| import Routines from '@/ipc-api/Routines'; | ||||
|  | ||||
| export default { | ||||
|    name: 'WorkspacePropsTabRoutine', | ||||
|    components: { | ||||
|       QueryEditor, | ||||
|       WorkspacePropsRoutineOptionsModal, | ||||
|       WorkspacePropsRoutineParamsModal | ||||
|    }, | ||||
|    props: { | ||||
|       connection: Object, | ||||
|       routine: String | ||||
|    }, | ||||
|    data () { | ||||
|       return { | ||||
|          tabUid: 'prop', | ||||
|          isQuering: false, | ||||
|          isSaving: false, | ||||
|          isOptionsModal: false, | ||||
|          isParamsModal: false, | ||||
|          originalRoutine: null, | ||||
|          localRoutine: { sql: '' }, | ||||
|          lastRoutine: null, | ||||
|          sqlProxy: '', | ||||
|          editorHeight: 300 | ||||
|       }; | ||||
|    }, | ||||
|    computed: { | ||||
|       ...mapGetters({ | ||||
|          getWorkspace: 'workspaces/getWorkspace' | ||||
|       }), | ||||
|       workspace () { | ||||
|          return this.getWorkspace(this.connection.uid); | ||||
|       }, | ||||
|       isSelected () { | ||||
|          return this.workspace.selected_tab === 'prop'; | ||||
|       }, | ||||
|       schema () { | ||||
|          return this.workspace.breadcrumbs.schema; | ||||
|       }, | ||||
|       isChanged () { | ||||
|          return JSON.stringify(this.originalRoutine) !== JSON.stringify(this.localRoutine); | ||||
|       }, | ||||
|       isDefinerInUsers () { | ||||
|          return this.originalRoutine ? this.workspace.users.some(user => this.originalRoutine.definer === `\`${user.name}\`@\`${user.host}\``) : true; | ||||
|       }, | ||||
|       schemaTables () { | ||||
|          const schemaTables = this.workspace.structure | ||||
|             .filter(schema => schema.name === this.schema) | ||||
|             .map(schema => schema.tables); | ||||
|  | ||||
|          return schemaTables.length ? schemaTables[0].filter(table => table.type === 'table') : []; | ||||
|       } | ||||
|    }, | ||||
|    watch: { | ||||
|       async routine () { | ||||
|          if (this.isSelected) { | ||||
|             await this.getRoutineData(); | ||||
|             this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql); | ||||
|             this.lastRoutine = this.routine; | ||||
|          } | ||||
|       }, | ||||
|       async isSelected (val) { | ||||
|          if (val && this.lastRoutine !== this.routine) { | ||||
|             await this.getRoutineData(); | ||||
|             this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql); | ||||
|             this.lastRoutine = this.routine; | ||||
|          } | ||||
|       }, | ||||
|       isChanged (val) { | ||||
|          if (this.isSelected && this.lastRoutine === this.routine && this.routine !== null) | ||||
|             this.setUnsavedChanges(val); | ||||
|       } | ||||
|    }, | ||||
|    mounted () { | ||||
|       window.addEventListener('resize', this.resizeQueryEditor); | ||||
|    }, | ||||
|    destroyed () { | ||||
|       window.removeEventListener('resize', this.resizeQueryEditor); | ||||
|    }, | ||||
|    methods: { | ||||
|       ...mapActions({ | ||||
|          addNotification: 'notifications/addNotification', | ||||
|          refreshStructure: 'workspaces/refreshStructure', | ||||
|          setUnsavedChanges: 'workspaces/setUnsavedChanges', | ||||
|          changeBreadcrumbs: 'workspaces/changeBreadcrumbs' | ||||
|       }), | ||||
|       async getRoutineData () { | ||||
|          if (!this.routine) return; | ||||
|          this.isQuering = true; | ||||
|  | ||||
|          const params = { | ||||
|             uid: this.connection.uid, | ||||
|             schema: this.schema, | ||||
|             routine: this.workspace.breadcrumbs.procedure | ||||
|          }; | ||||
|  | ||||
|          try { | ||||
|             const { status, response } = await Routines.getRoutineInformations(params); | ||||
|             if (status === 'success') { | ||||
|                this.originalRoutine = response; | ||||
|                this.localRoutine = JSON.parse(JSON.stringify(this.originalRoutine)); | ||||
|                this.sqlProxy = this.localRoutine.sql; | ||||
|             } | ||||
|             else | ||||
|                this.addNotification({ status: 'error', message: response }); | ||||
|          } | ||||
|          catch (err) { | ||||
|             this.addNotification({ status: 'error', message: err.stack }); | ||||
|          } | ||||
|  | ||||
|          this.resizeQueryEditor(); | ||||
|          this.isQuering = false; | ||||
|       }, | ||||
|       async saveChanges () { | ||||
|          if (this.isSaving) return; | ||||
|          this.isSaving = true; | ||||
|          const params = { | ||||
|             uid: this.connection.uid, | ||||
|             schema: this.schema, | ||||
|             routine: { | ||||
|                ...this.localRoutine, | ||||
|                oldName: this.originalRoutine.name | ||||
|             } | ||||
|          }; | ||||
|  | ||||
|          try { | ||||
|             const { status, response } = await Routines.alterRoutine(params); | ||||
|  | ||||
|             if (status === 'success') { | ||||
|                const oldName = this.originalRoutine.name; | ||||
|  | ||||
|                await this.refreshStructure(this.connection.uid); | ||||
|  | ||||
|                if (oldName !== this.localRoutine.name) { | ||||
|                   this.setUnsavedChanges(false); | ||||
|                   this.changeBreadcrumbs({ schema: this.schema, procedure: this.localRoutine.name }); | ||||
|                } | ||||
|  | ||||
|                this.getRoutineData(); | ||||
|             } | ||||
|             else | ||||
|                this.addNotification({ status: 'error', message: response }); | ||||
|          } | ||||
|          catch (err) { | ||||
|             this.addNotification({ status: 'error', message: err.stack }); | ||||
|          } | ||||
|  | ||||
|          this.isSaving = false; | ||||
|       }, | ||||
|       clearChanges () { | ||||
|          this.localRoutine = JSON.parse(JSON.stringify(this.originalRoutine)); | ||||
|          this.$refs.queryEditor.editor.session.setValue(this.localRoutine.sql); | ||||
|       }, | ||||
|       resizeQueryEditor () { | ||||
|          if (this.$refs.queryEditor) { | ||||
|             const footer = document.getElementById('footer'); | ||||
|             const size = window.innerHeight - this.$refs.queryEditor.$el.getBoundingClientRect().top - footer.offsetHeight; | ||||
|             this.editorHeight = size; | ||||
|             this.$refs.queryEditor.editor.resize(); | ||||
|          } | ||||
|       }, | ||||
|       optionsUpdate (options) { | ||||
|          this.localRoutine = options; | ||||
|       }, | ||||
|       parametersUpdate (parameters) { | ||||
|          this.localRoutine = { ...this.localRoutine, parameters }; | ||||
|       }, | ||||
|       showOptionsModal () { | ||||
|          this.isOptionsModal = true; | ||||
|       }, | ||||
|       hideOptionsModal () { | ||||
|          this.isOptionsModal = false; | ||||
|       }, | ||||
|       showParamsModal () { | ||||
|          this.isParamsModal = true; | ||||
|       }, | ||||
|       hideParamsModal () { | ||||
|          this.isParamsModal = false; | ||||
|       } | ||||
|    } | ||||
| }; | ||||
| </script> | ||||
| @@ -74,7 +74,11 @@ module.exports = { | ||||
|       trigger: 'Trigger | Triggers', | ||||
|       storedRoutine: 'Stored routine | Stored routines', | ||||
|       scheduler: 'Scheduler | Schedulers', | ||||
|       event: 'Event' | ||||
|       event: 'Event', | ||||
|       parameters: 'Parameters', | ||||
|       function: 'Function | Functions', | ||||
|       deterministic: 'Deterministic', | ||||
|       context: 'Context' | ||||
|    }, | ||||
|    message: { | ||||
|       appWelcome: 'Welcome to Antares SQL Client!', | ||||
| @@ -148,7 +152,11 @@ module.exports = { | ||||
|       createNewView: 'Create new view', | ||||
|       deleteTrigger: 'Delete trigger', | ||||
|       createNewTrigger: 'Create new trigger', | ||||
|       currentUser: 'Current user' | ||||
|       currentUser: 'Current user', | ||||
|       routineBody: 'Routine body', | ||||
|       dataAccess: 'Data access', | ||||
|       thereAreNoParameters: 'There are no parameters', | ||||
|       createNewParameter: 'Create new parameter' | ||||
|    }, | ||||
|    // Date and Time | ||||
|    short: { | ||||
|   | ||||
							
								
								
									
										20
									
								
								src/renderer/ipc-api/Routines.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/renderer/ipc-api/Routines.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| 'use strict'; | ||||
| import { ipcRenderer } from 'electron'; | ||||
|  | ||||
| export default class { | ||||
|    static getRoutineInformations (params) { | ||||
|       return ipcRenderer.invoke('get-routine-informations', params); | ||||
|    } | ||||
|  | ||||
|    static dropRoutine (params) { | ||||
|       return ipcRenderer.invoke('drop-routine', params); | ||||
|    } | ||||
|  | ||||
|    static alterRoutine (params) { | ||||
|       return ipcRenderer.invoke('alter-routine', params); | ||||
|    } | ||||
|  | ||||
|    static createRoutine (params) { | ||||
|       return ipcRenderer.invoke('create-routine', params); | ||||
|    } | ||||
| } | ||||
| @@ -345,6 +345,7 @@ export default { | ||||
|             table: null, | ||||
|             trigger: null, | ||||
|             procedure: null, | ||||
|             function: null, | ||||
|             scheduler: null, | ||||
|             view: null | ||||
|          }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user