feat(SQLite): tables management

This commit is contained in:
Fabio Di Stasio 2021-11-18 11:36:46 +01:00
parent 3fc227d2de
commit 3efeb45c46
14 changed files with 312 additions and 722 deletions

View File

@ -36,6 +36,7 @@ module.exports = {
schedulerAdd: false,
databaseEdit: false,
schemaEdit: false,
schemaDrop: false,
tableSettings: false,
tableOptions: false,
tableArray: false,
@ -51,6 +52,7 @@ module.exports = {
sortableFields: false,
unsigned: false,
nullable: false,
nullablePrimary: false,
zerofill: false,
autoIncrement: false,
comment: false,

View File

@ -32,6 +32,7 @@ module.exports = {
functionAdd: true,
schedulerAdd: true,
schemaEdit: true,
schemaDrop: true,
tableSettings: true,
viewSettings: true,
triggerSettings: true,

View File

@ -28,6 +28,7 @@ module.exports = {
triggerFunctionAdd: true,
routineAdd: true,
functionAdd: true,
schemaDrop: true,
databaseEdit: false,
tableSettings: true,
viewSettings: true,

View File

@ -25,7 +25,7 @@ module.exports = {
// Settings
elementsWrapper: '',
stringsWrapper: '"',
tableAdd: false,
tableAdd: true,
viewAdd: false,
triggerAdd: false,
triggerFunctionAdd: false,
@ -34,7 +34,7 @@ module.exports = {
schedulerAdd: false,
databaseEdit: false,
schemaEdit: false,
tableSettings: false,
tableSettings: true,
tableArray: false,
tableRealCount: true,
viewSettings: false,
@ -43,11 +43,12 @@ module.exports = {
routineSettings: false,
functionSettings: false,
schedulerSettings: false,
indexes: false,
foreigns: false,
sortableFields: false,
indexes: true,
foreigns: true,
sortableFields: true,
unsigned: false,
nullable: false,
nullable: true,
nullablePrimary: true,
zerofill: false,
tableOptions: false,
autoIncrement: false,

View File

@ -0,0 +1,137 @@
module.exports = [
{
group: 'integer',
types: [
{
name: 'INT',
length: true,
collation: false,
unsigned: true,
zerofill: true
},
{
name: 'INTEGER',
length: true,
collation: false,
unsigned: true,
zerofill: true
},
{
name: 'BIGINT',
length: true,
collation: false,
unsigned: true,
zerofill: true
},
{
name: 'NUMERIC',
length: true,
collation: false,
unsigned: true,
zerofill: true
},
{
name: 'BOOLEAN',
length: false,
collation: false,
unsigned: true,
zerofill: true
}
]
},
{
group: 'float',
types: [
{
name: 'FLOAT',
length: true,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'REAL',
length: true,
collation: false,
unsigned: false,
zerofill: false
}
]
},
{
group: 'string',
types: [
{
name: 'CHAR',
length: true,
collation: true,
unsigned: false,
zerofill: false
},
{
name: 'VARCHAR',
length: true,
collation: true,
unsigned: false,
zerofill: false
},
{
name: 'TEXT',
length: true,
collation: true,
unsigned: false,
zerofill: false
}
]
},
{
group: 'binary',
types: [
{
name: 'BLOB',
length: true,
collation: false,
unsigned: false,
zerofill: false
}
]
},
{
group: 'time',
types: [
{
name: 'DATE',
length: true,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'TIME',
length: true,
collation: false,
unsigned: false,
zerofill: false
},
{
name: 'DATETIME',
length: true,
collation: false,
unsigned: false,
zerofill: false
}
]
},
{
group: 'other',
types: [
{
name: 'NONE',
length: false,
collation: false,
unsigned: false,
zerofill: false
}
]
}
];

View File

@ -0,0 +1,5 @@
module.exports = [
'PRIMARY',
'INDEX',
'UNIQUE'
];

View File

@ -571,7 +571,7 @@ export class MySQLClient extends AntaresCore {
/**
* CREATE DATABASE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async createSchema (params) {
@ -581,7 +581,7 @@ export class MySQLClient extends AntaresCore {
/**
* ALTER DATABASE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async alterSchema (params) {
@ -591,7 +591,7 @@ export class MySQLClient extends AntaresCore {
/**
* DROP DATABASE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async dropSchema (params) {
@ -631,7 +631,7 @@ export class MySQLClient extends AntaresCore {
/**
* DROP VIEW
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async dropView (params) {
@ -642,7 +642,7 @@ export class MySQLClient extends AntaresCore {
/**
* ALTER VIEW
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async alterView (params) {
@ -663,7 +663,7 @@ export class MySQLClient extends AntaresCore {
/**
* CREATE VIEW
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async createView (params) {
@ -696,7 +696,7 @@ export class MySQLClient extends AntaresCore {
/**
* DROP TRIGGER
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async dropTrigger (params) {
@ -707,7 +707,7 @@ export class MySQLClient extends AntaresCore {
/**
* ALTER TRIGGER
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async alterTrigger (params) {
@ -729,7 +729,7 @@ export class MySQLClient extends AntaresCore {
/**
* CREATE TRIGGER
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async createTrigger (params) {
@ -803,7 +803,7 @@ export class MySQLClient extends AntaresCore {
/**
* DROP PROCEDURE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async dropRoutine (params) {
@ -814,7 +814,7 @@ export class MySQLClient extends AntaresCore {
/**
* ALTER PROCEDURE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async alterRoutine (params) {
@ -836,7 +836,7 @@ export class MySQLClient extends AntaresCore {
/**
* CREATE PROCEDURE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async createRoutine (params) {
@ -930,7 +930,7 @@ export class MySQLClient extends AntaresCore {
/**
* DROP FUNCTION
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async dropFunction (params) {
@ -941,7 +941,7 @@ export class MySQLClient extends AntaresCore {
/**
* ALTER FUNCTION
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async alterFunction (params) {
@ -963,7 +963,7 @@ export class MySQLClient extends AntaresCore {
/**
* CREATE FUNCTION
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async createFunction (params) {
@ -1024,7 +1024,7 @@ export class MySQLClient extends AntaresCore {
/**
* DROP EVENT
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async dropEvent (params) {
@ -1035,7 +1035,7 @@ export class MySQLClient extends AntaresCore {
/**
* ALTER EVENT
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async alterEvent (params) {
@ -1061,7 +1061,7 @@ export class MySQLClient extends AntaresCore {
/**
* CREATE EVENT
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async createEvent (params) {
@ -1205,7 +1205,7 @@ export class MySQLClient extends AntaresCore {
/**
* CREATE TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async createTable (params) {
@ -1267,7 +1267,7 @@ export class MySQLClient extends AntaresCore {
/**
* ALTER TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async alterTable (params) {
@ -1402,7 +1402,7 @@ export class MySQLClient extends AntaresCore {
/**
* DUPLICATE TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async duplicateTable (params) {
@ -1413,7 +1413,7 @@ export class MySQLClient extends AntaresCore {
/**
* TRUNCATE TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async truncateTable (params) {
@ -1424,7 +1424,7 @@ export class MySQLClient extends AntaresCore {
/**
* DROP TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async dropTable (params) {

View File

@ -506,7 +506,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* CREATE SCHEMA
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async createSchema (params) {
@ -516,7 +516,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* ALTER DATABASE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async alterSchema (params) {
@ -526,7 +526,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* DROP DATABASE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof MySQLClient
*/
async dropSchema (params) {
@ -558,7 +558,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* DROP VIEW
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof PostgreSQLClient
*/
async dropView (params) {
@ -569,7 +569,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* ALTER VIEW
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof PostgreSQLClient
*/
async alterView (params) {
@ -585,7 +585,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* CREATE VIEW
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof PostgreSQLClient
*/
async createView (params) {
@ -640,7 +640,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* DROP TRIGGER
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof PostgreSQLClient
*/
async dropTrigger (params) {
@ -652,7 +652,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* ALTER TRIGGER
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof PostgreSQLClient
*/
async alterTrigger (params) {
@ -674,7 +674,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* CREATE TRIGGER
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof PostgreSQLClient
*/
async createTrigger (params) {
@ -1086,7 +1086,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* CREATE TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof PostgreSQLClient
*/
async createTable (params) {
@ -1144,7 +1144,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* ALTER TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof PostgreSQLClient
*/
async alterTable (params) {
@ -1290,7 +1290,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* DUPLICATE TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof PostgreSQLClient
*/
async duplicateTable (params) {
@ -1301,7 +1301,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* TRUNCATE TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof PostgreSQLClient
*/
async truncateTable (params) {
@ -1312,7 +1312,7 @@ export class PostgreSQLClient extends AntaresCore {
/**
* DROP TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof PostgreSQLClient
*/
async dropTable (params) {

View File

@ -2,6 +2,7 @@
import sqlite from 'better-sqlite3';
import { AntaresCore } from '../AntaresCore';
import dataTypes from 'common/data-types/mysql';
import { NUMBER, FLOAT } from 'common/fieldTypes';
export class SQLiteClient extends AntaresCore {
constructor (args) {
@ -136,95 +137,35 @@ export class SQLiteClient extends AntaresCore {
* @memberof SQLiteClient
*/
async getTableColumns ({ schema, table }) {
const { rows } = await this
.select('*')
.schema('information_schema')
.from('COLUMNS')
.where({ TABLE_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'` })
.orderBy({ ORDINAL_POSITION: 'ASC' })
.run();
const { rows: fields } = await this.raw(`SELECT * FROM "${schema}".pragma_table_info('${table}')`);
const { rows: fields } = await this.raw(`SHOW CREATE TABLE \`${schema}\`.\`${table}\``);
const remappedFields = fields.map(row => {
if (!row['Create Table']) return false;
let n = 0;
return row['Create Table']
.split('')
.reduce((acc, curr) => {
if (curr === ')') n--;
if (n !== 0) acc += curr;
if (curr === '(') n++;
return acc;
}, '')
.replaceAll('\n', '')
.split(',')
.map(f => {
try {
const fieldArr = f.trim().split(' ');
const nameAndType = fieldArr.slice(0, 2);
if (nameAndType[0].charAt(0) !== '`') return false;
const details = fieldArr.slice(2).join(' ');
let defaultValue = null;
if (details.includes('DEFAULT'))
defaultValue = details.match(/(?<=DEFAULT ).*?$/gs)[0].split(' COMMENT')[0];
// const defaultValueArr = defaultValue.split('');
// if (defaultValueArr[0] === '\'') {
// defaultValueArr.shift();
// defaultValueArr.pop();
// defaultValue = defaultValueArr.join('');
// }
const typeAndLength = nameAndType[1].replace(')', '').split('(');
return {
name: nameAndType[0].replaceAll('`', ''),
type: typeAndLength[0].toUpperCase(),
length: typeAndLength[1] ? typeAndLength[1] : null,
default: defaultValue
};
}
catch (err) {
return false;
}
return fields.map(field => {
const [type, length] = field.type.includes('(')
? field.type.replace(')', '').split('(').map(el => {
if (!isNaN(el)) el = +el;
return el;
})
.filter(Boolean)
.reduce((acc, curr) => {
acc[curr.name] = curr;
return acc;
}, {});
})[0];
return rows.map(field => {
let numLength = field.COLUMN_TYPE.match(/int\(([^)]+)\)/);
numLength = numLength ? +numLength.pop() : null;
const enumValues = /(enum|set)/.test(field.COLUMN_TYPE)
? field.COLUMN_TYPE.match(/\(([^)]+)\)/)[0].slice(1, -1)
: null;
: [field.type, null];
return {
name: field.COLUMN_NAME,
key: field.COLUMN_KEY.toLowerCase(),
type: remappedFields ? remappedFields[field.COLUMN_NAME].type : field.DATA_TYPE,
schema: field.TABLE_SCHEMA,
table: field.TABLE_NAME,
numPrecision: field.NUMERIC_PRECISION,
numLength,
enumValues,
datePrecision: field.DATETIME_PRECISION,
charLength: field.CHARACTER_MAXIMUM_LENGTH,
nullable: field.IS_NULLABLE.includes('YES'),
unsigned: field.COLUMN_TYPE.includes('unsigned'),
zerofill: field.COLUMN_TYPE.includes('zerofill'),
order: field.ORDINAL_POSITION,
default: remappedFields ? remappedFields[field.COLUMN_NAME].default : field.COLUMN_DEFAULT,
charset: field.CHARACTER_SET_NAME,
collation: field.COLLATION_NAME,
autoIncrement: field.EXTRA.includes('auto_increment'),
onUpdate: field.EXTRA.toLowerCase().includes('on update') ? field.EXTRA.replace('on update', '') : '',
comment: field.COLUMN_COMMENT
name: field.name,
key: null,
type,
schema: schema,
table: table,
numPrecision: [...NUMBER, ...FLOAT].includes(type) ? length : null,
datePrecision: null,
charLength: ![...NUMBER, ...FLOAT].includes(type) ? length : null,
nullable: !field.notnull,
unsigned: null,
zerofill: null,
order: field.cid + 1,
default: field.dflt_value,
charset: null,
collation: null,
autoIncrement: false,
onUpdate: null,
comment: ''
};
});
}
@ -250,33 +191,7 @@ export class SQLiteClient extends AntaresCore {
* @memberof SQLiteClient
*/
async getTableOptions ({ schema, table }) {
const { rows } = await this.raw(`SHOW TABLE STATUS FROM \`${schema}\` WHERE Name = '${table}'`);
if (rows.length) {
let tableType;
switch (rows[0].Comment) {
case 'VIEW':
tableType = 'view';
break;
default:
tableType = 'table';
break;
}
return {
name: rows[0].Name,
type: tableType,
rows: rows[0].Rows,
created: rows[0].Create_time,
updated: rows[0].Update_time,
engine: rows[0].Engine,
comment: rows[0].Comment,
size: rows[0].Data_length + rows[0].Index_length,
autoIncrement: rows[0].Auto_increment,
collation: rows[0].Collation
};
};
return {};
return { name: table };
}
/**
@ -331,78 +246,27 @@ export class SQLiteClient extends AntaresCore {
* @memberof SQLiteClient
*/
async getKeyUsage ({ schema, table }) {
const { rows } = await this
.select('*')
.schema('information_schema')
.from('KEY_COLUMN_USAGE')
.where({ TABLE_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'`, REFERENCED_TABLE_NAME: 'IS NOT NULL' })
.run();
const { rows: extras } = await this
.select('*')
.schema('information_schema')
.from('REFERENTIAL_CONSTRAINTS')
.where({ CONSTRAINT_SCHEMA: `= '${schema}'`, TABLE_NAME: `= '${table}'`, REFERENCED_TABLE_NAME: 'IS NOT NULL' })
.run();
const { rows } = await this.raw(`SELECT * FROM "${schema}".pragma_foreign_key_list('${table}');`);
return rows.map(field => {
const extra = extras.find(x => x.CONSTRAINT_NAME === field.CONSTRAINT_NAME);
return {
schema: field.TABLE_SCHEMA,
table: field.TABLE_NAME,
field: field.COLUMN_NAME,
position: field.ORDINAL_POSITION,
constraintPosition: field.POSITION_IN_UNIQUE_CONSTRAINT,
constraintName: field.CONSTRAINT_NAME,
refSchema: field.REFERENCED_TABLE_SCHEMA,
refTable: field.REFERENCED_TABLE_NAME,
refField: field.REFERENCED_COLUMN_NAME,
onUpdate: extra.UPDATE_RULE,
onDelete: extra.DELETE_RULE
schema: schema,
table: table,
field: field.from,
position: field.id + 1,
constraintPosition: null,
constraintName: field.id,
refSchema: schema,
refTable: field.table,
refField: field.to,
onUpdate: field.on_update,
onDelete: field.on_delete
};
});
}
async getUsers () {}
/**
* CREATE DATABASE
*
* @returns {Array.<Object>} parameters
* @memberof SQLiteClient
*/
async createSchema (params) {
return await this.raw(`CREATE DATABASE \`${params.name}\` COLLATE ${params.collation}`);
}
/**
* ALTER DATABASE
*
* @returns {Array.<Object>} parameters
* @memberof SQLiteClient
*/
async alterSchema (params) {
return await this.raw(`ALTER DATABASE \`${params.name}\` COLLATE ${params.collation}`);
}
/**
* DROP DATABASE
*
* @returns {Array.<Object>} parameters
* @memberof SQLiteClient
*/
async dropSchema (params) {
return await this.raw(`DROP DATABASE \`${params.database}\``);
}
/**
* @returns {Array.<Object>} parameters
* @memberof SQLiteClient
*/
async getDatabaseCollation (params) {
return await this.raw(`SELECT \`DEFAULT_COLLATION_NAME\` FROM \`information_schema\`.\`SCHEMATA\` WHERE \`SCHEMA_NAME\`='${params.database}'`);
}
/**
* SHOW CREATE VIEW
*
@ -534,357 +398,6 @@ export class SQLiteClient extends AntaresCore {
return await this.raw(sql, { split: false });
}
/**
* SHOW CREATE PROCEDURE
*
* @returns {Array.<Object>} view informations
* @memberof SQLiteClient
*/
async getRoutineInformations ({ schema, routine }) {
const sql = `SHOW CREATE PROCEDURE \`${schema}\`.\`${routine}\``;
const results = await this.raw(sql);
return results.rows.map(async row => {
if (!row['Create Procedure']) {
return {
definer: null,
sql: '',
parameters: [],
name: row.Procedure,
comment: '',
security: 'DEFINER',
deterministic: false,
dataAccess: 'CONTAINS SQL'
};
}
const sql = `SELECT *
FROM information_schema.parameters
WHERE SPECIFIC_NAME = '${routine}'
AND SPECIFIC_SCHEMA = '${schema}'
ORDER BY ORDINAL_POSITION
`;
const results = await this.raw(sql);
const parameters = results.rows.map(row => {
return {
name: row.PARAMETER_NAME,
type: row.DATA_TYPE.toUpperCase(),
length: row.NUMERIC_PRECISION || row.DATETIME_PRECISION || row.CHARACTER_MAXIMUM_LENGTH || '',
context: row.PARAMETER_MODE
};
});
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: 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 SQLiteClient
*/
async dropRoutine (params) {
const sql = `DROP PROCEDURE \`${params.schema}\`.\`${params.routine}\``;
return await this.raw(sql);
}
/**
* ALTER PROCEDURE
*
* @returns {Array.<Object>} parameters
* @memberof SQLiteClient
*/
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({ schema: routine.schema, routine: tempProcedure.name });
await this.dropRoutine({ schema: routine.schema, routine: routine.oldName });
await this.createRoutine(routine);
}
catch (err) {
return Promise.reject(err);
}
}
/**
* CREATE PROCEDURE
*
* @returns {Array.<Object>} parameters
* @memberof SQLiteClient
*/
async createRoutine (params) {
const parameters = 'parameters' in params
? params.parameters.reduce((acc, curr) => {
acc.push(`${curr.context} \`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
return acc;
}, []).join(',')
: '';
const sql = `CREATE ${params.definer ? `DEFINER=${params.definer} ` : ''}PROCEDURE \`${params.schema}\`.\`${params.name}\`(${parameters})
LANGUAGE SQL
${params.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
${params.dataAccess}
SQL SECURITY ${params.security}
COMMENT '${params.comment}'
${params.sql}`;
return await this.raw(sql, { split: false });
}
/**
* SHOW CREATE FUNCTION
*
* @returns {Array.<Object>} view informations
* @memberof SQLiteClient
*/
async getFunctionInformations ({ schema, func }) {
const sql = `SHOW CREATE FUNCTION \`${schema}\`.\`${func}\``;
const results = await this.raw(sql);
return results.rows.map(async row => {
if (!row['Create Function']) {
return {
definer: null,
sql: '',
parameters: [],
name: row.Procedure,
comment: '',
security: 'DEFINER',
deterministic: false,
dataAccess: 'CONTAINS SQL',
returns: 'INT',
returnsLength: null
};
}
const sql = `SELECT *
FROM information_schema.parameters
WHERE SPECIFIC_NAME = '${func}'
AND SPECIFIC_SCHEMA = '${schema}'
ORDER BY ORDINAL_POSITION
`;
const results = await this.raw(sql);
const parameters = results.rows.filter(row => row.PARAMETER_MODE).map(row => {
return {
name: row.PARAMETER_NAME,
type: row.DATA_TYPE.toUpperCase(),
length: row.NUMERIC_PRECISION || row.DATETIME_PRECISION || row.CHARACTER_MAXIMUM_LENGTH || '',
context: row.PARAMETER_MODE
};
});
let dataAccess = 'CONTAINS SQL';
if (row['Create Function'].includes('NO SQL'))
dataAccess = 'NO SQL';
if (row['Create Function'].includes('READS SQL DATA'))
dataAccess = 'READS SQL DATA';
if (row['Create Function'].includes('MODIFIES SQL DATA'))
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 {
definer: row['Create Function'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
sql: row['Create Function'].match(/(BEGIN|begin)(.*)(END|end)/gs)[0],
parameters: parameters || [],
name: row.Function,
comment: row['Create Function'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Function'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
security: row['Create Function'].includes('SQL SECURITY INVOKER') ? 'INVOKER' : 'DEFINER',
deterministic: row['Create Function'].includes('DETERMINISTIC'),
dataAccess,
returns: output[0].toUpperCase(),
returnsLength: +output[1]
};
})[0];
}
/**
* DROP FUNCTION
*
* @returns {Array.<Object>} parameters
* @memberof SQLiteClient
*/
async dropFunction (params) {
const sql = `DROP FUNCTION \`${params.schema}\`.\`${params.func}\``;
return await this.raw(sql);
}
/**
* ALTER FUNCTION
*
* @returns {Array.<Object>} parameters
* @memberof SQLiteClient
*/
async alterFunction (params) {
const { func } = params;
const tempProcedure = Object.assign({}, func);
tempProcedure.name = `Antares_${tempProcedure.name}_tmp`;
try {
await this.createFunction(tempProcedure);
await this.dropFunction({ schema: func.schema, func: tempProcedure.name });
await this.dropFunction({ schema: func.schema, func: func.oldName });
await this.createFunction(func);
}
catch (err) {
return Promise.reject(err);
}
}
/**
* CREATE FUNCTION
*
* @returns {Array.<Object>} parameters
* @memberof SQLiteClient
*/
async createFunction (params) {
const parameters = 'parameters' in params
? params.parameters.reduce((acc, curr) => {
acc.push(`\`${curr.name}\` ${curr.type}${curr.length ? `(${curr.length})` : ''}`);
return acc;
}, []).join(',')
: '';
const body = params.returns ? params.sql : 'BEGIN\n RETURN 0;\nEND';
const sql = `CREATE ${params.definer ? `DEFINER=${params.definer} ` : ''}FUNCTION \`${params.schema}\`.\`${params.name}\`(${parameters}) RETURNS ${params.returns || 'SMALLINT'}${params.returnsLength ? `(${params.returnsLength})` : ''}
LANGUAGE SQL
${params.deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC'}
${params.dataAccess}
SQL SECURITY ${params.security}
COMMENT '${params.comment}'
${body}`;
return await this.raw(sql, { split: false });
}
/**
* SHOW CREATE EVENT
*
* @returns {Array.<Object>} view informations
* @memberof SQLiteClient
*/
async getEventInformations ({ schema, scheduler }) {
const sql = `SHOW CREATE EVENT \`${schema}\`.\`${scheduler}\``;
const results = await this.raw(sql);
return results.rows.map(row => {
const schedule = row['Create Event'];
const execution = schedule.includes('EVERY') ? 'EVERY' : 'ONCE';
const every = execution === 'EVERY' ? row['Create Event'].match(/(?<=EVERY )(\s*([^\s]+)){0,2}/gs)[0].replaceAll('\'', '').split(' ') : [];
const starts = execution === 'EVERY' && schedule.includes('STARTS') ? schedule.match(/(?<=STARTS ').*?(?='\s)/gs)[0] : '';
const ends = execution === 'EVERY' && schedule.includes('ENDS') ? schedule.match(/(?<=ENDS ').*?(?='\s)/gs)[0] : '';
const at = execution === 'ONCE' && schedule.includes('AT') ? schedule.match(/(?<=AT ').*?(?='\s)/gs)[0] : '';
return {
definer: row['Create Event'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
sql: row['Create Event'].match(/(?<=DO )(.*)/gs)[0],
name: row.Event,
comment: row['Create Event'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Event'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
state: row['Create Event'].includes('ENABLE') ? 'ENABLE' : row['Create Event'].includes('DISABLE ON SLAVE') ? 'DISABLE ON SLAVE' : 'DISABLE',
preserve: row['Create Event'].includes('ON COMPLETION PRESERVE'),
execution,
every,
starts,
ends,
at
};
})[0];
}
/**
* DROP EVENT
*
* @returns {Array.<Object>} parameters
* @memberof SQLiteClient
*/
async dropEvent (params) {
const sql = `DROP EVENT \`${params.schema}\`.\`${params.scheduler}\``;
return await this.raw(sql);
}
/**
* ALTER EVENT
*
* @returns {Array.<Object>} parameters
* @memberof SQLiteClient
*/
async alterEvent (params) {
const { scheduler } = params;
if (scheduler.execution === 'EVERY' && scheduler.every[0].includes('-'))
scheduler.every[0] = `'${scheduler.every[0]}'`;
const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.schema}\`.\`${scheduler.oldName}\`
ON SCHEDULE
${scheduler.execution === 'EVERY'
? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
: `AT '${scheduler.at}'`}
ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
${scheduler.name !== scheduler.oldName ? `RENAME TO \`${scheduler.schema}\`.\`${scheduler.name}\`` : ''}
${scheduler.state}
COMMENT '${scheduler.comment}'
DO ${scheduler.sql}`;
return await this.raw(sql, { split: false });
}
/**
* CREATE EVENT
*
* @returns {Array.<Object>} parameters
* @memberof SQLiteClient
*/
async createEvent (params) {
const sql = `CREATE ${params.definer ? ` DEFINER=${params.definer}` : ''} EVENT \`${params.schema}\`.\`${params.name}\`
ON SCHEDULE
${params.execution === 'EVERY'
? `EVERY ${params.every.join(' ')}${params.starts ? ` STARTS '${params.starts}'` : ''}${params.ends ? ` ENDS '${params.ends}'` : ''}`
: `AT '${params.at}'`}
ON COMPLETION${!params.preserve ? ' NOT' : ''} PRESERVE
${params.state}
COMMENT '${params.comment}'
DO ${params.sql}`;
return await this.raw(sql, { split: false });
}
async enableEvent ({ schema, scheduler }) {
const sql = `ALTER EVENT \`${schema}\`.\`${scheduler}\` ENABLE`;
return await this.raw(sql, { split: false });
}
async disableEvent ({ schema, scheduler }) {
const sql = `ALTER EVENT \`${schema}\`.\`${scheduler}\` DISABLE`;
return await this.raw(sql, { split: false });
}
/**
* SHOW COLLATION
*
@ -946,7 +459,7 @@ export class SQLiteClient extends AntaresCore {
/**
* CREATE TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof SQLiteClient
*/
async createTable (params) {
@ -959,48 +472,43 @@ export class SQLiteClient extends AntaresCore {
} = params;
const newColumns = [];
const newIndexes = [];
const manageIndexes = [];
const newForeigns = [];
let sql = `CREATE TABLE \`${schema}\`.\`${options.name}\``;
let sql = `CREATE TABLE "${schema}"."${options.name}"`;
// ADD FIELDS
fields.forEach(field => {
const typeInfo = this._getTypeInfo(field.type);
const length = typeInfo.length ? field.enumValues || field.numLength || field.charLength || field.datePrecision : false;
const length = typeInfo?.length ? field.enumValues || field.numLength || field.charLength || field.datePrecision : false;
newColumns.push(`\`${field.name}\`
${field.type.toUpperCase()}${length ? `(${length})` : ''}
newColumns.push(`"${field.name}"
${field.type.toUpperCase()}${length && length !== true ? `(${length})` : ''}
${field.unsigned ? 'UNSIGNED' : ''}
${field.zerofill ? 'ZEROFILL' : ''}
${field.nullable ? 'NULL' : 'NOT NULL'}
${field.autoIncrement ? 'AUTO_INCREMENT' : ''}
${field.default ? `DEFAULT ${field.default}` : ''}
${field.comment ? `COMMENT '${field.comment}'` : ''}
${field.collation ? `COLLATE ${field.collation}` : ''}
${field.onUpdate ? `ON UPDATE ${field.onUpdate}` : ''}`);
});
// ADD INDEX
indexes.forEach(index => {
const fields = index.fields.map(field => `\`${field}\``).join(',');
let type = index.type;
const fields = index.fields.map(field => `"${field}"`).join(',');
const type = index.type;
if (type === 'PRIMARY')
newIndexes.push(`PRIMARY KEY (${fields})`);
else {
if (type === 'UNIQUE')
type = 'UNIQUE INDEX';
newIndexes.push(`${type} \`${index.name}\` (${fields})`);
}
else
manageIndexes.push(`CREATE ${type === 'UNIQUE' ? type : ''} INDEX "${index.name}" ON "${options.name}" (${fields})`);
});
// ADD FOREIGN KEYS
foreigns.forEach(foreign => {
newForeigns.push(`CONSTRAINT \`${foreign.constraintName}\` FOREIGN KEY (\`${foreign.field}\`) REFERENCES \`${foreign.refTable}\` (\`${foreign.refField}\`) ON UPDATE ${foreign.onUpdate} ON DELETE ${foreign.onDelete}`);
newForeigns.push(`CONSTRAINT "${foreign.constraintName}" FOREIGN KEY ("${foreign.field}") REFERENCES "${foreign.refTable}" ("${foreign.refField}") ON UPDATE ${foreign.onUpdate} ON DELETE ${foreign.onDelete}`);
});
sql = `${sql} (${[...newColumns, ...newIndexes, ...newForeigns].join(', ')}) COMMENT='${options.comment}', COLLATE='${options.collation}', ENGINE=${options.engine}`;
sql = `${sql} (${[...newColumns, ...newIndexes, ...newForeigns].join(', ')})`;
if (manageIndexes.length) sql = `${sql}; ${manageIndexes.join(';')}`;
return await this.raw(sql);
}
@ -1008,168 +516,87 @@ export class SQLiteClient extends AntaresCore {
/**
* ALTER TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof SQLiteClient
*/
async alterTable (params) {
const {
table,
schema,
additions,
deletions,
changes,
indexChanges,
foreignChanges,
options
} = params;
try {
await this.raw('BEGIN TRANSACTION');
await this.raw('PRAGMA foreign_keys = 0');
let sql = `ALTER TABLE \`${schema}\`.\`${table}\` `;
const alterColumns = [];
const tmpName = `Antares_${params.table}_tmp`;
await this.raw(`CREATE TABLE "${tmpName}" AS SELECT * FROM "${params.table}"`);
await this.dropTable(params);
// OPTIONS
if ('comment' in options) alterColumns.push(`COMMENT='${options.comment}'`);
if ('engine' in options) alterColumns.push(`ENGINE=${options.engine}`);
if ('autoIncrement' in options) alterColumns.push(`AUTO_INCREMENT=${+options.autoIncrement}`);
if ('collation' in options) alterColumns.push(`COLLATE='${options.collation}'`);
const createTableParams = {
schema: params.schema,
fields: params.tableStructure.fields,
foreigns: params.tableStructure.foreigns,
indexes: params.tableStructure.indexes.filter(index => !index.name.includes('sqlite_autoindex')),
options: { name: params.tableStructure.name }
};
await this.createTable(createTableParams);
const insertFields = createTableParams.fields
.filter(field => {
return (
params.additions.every(add => add.name !== field.name) &&
params.deletions.every(del => del.name !== field.name)
);
})
.reduce((acc, curr) => {
acc.push(`"${curr.name}"`);
return acc;
}, []);
// ADD FIELDS
additions.forEach(addition => {
const typeInfo = this._getTypeInfo(addition.type);
const length = typeInfo.length ? addition.enumValues || addition.numLength || addition.charLength || addition.datePrecision : false;
const selectFields = insertFields.map(field => {
const renamedField = params.changes.find(change => `"${change.name}"` === field);
if (renamedField)
return `"${renamedField.orgName}"`;
return field;
});
alterColumns.push(`ADD COLUMN \`${addition.name}\`
${addition.type.toUpperCase()}${length ? `(${length})` : ''}
${addition.unsigned ? 'UNSIGNED' : ''}
${addition.zerofill ? 'ZEROFILL' : ''}
${addition.nullable ? 'NULL' : 'NOT NULL'}
${addition.autoIncrement ? 'AUTO_INCREMENT' : ''}
${addition.default ? `DEFAULT ${addition.default}` : ''}
${addition.comment ? `COMMENT '${addition.comment}'` : ''}
${addition.collation ? `COLLATE ${addition.collation}` : ''}
${addition.onUpdate ? `ON UPDATE ${addition.onUpdate}` : ''}
${addition.after ? `AFTER \`${addition.after}\`` : 'FIRST'}`);
});
await this.raw(`INSERT INTO "${createTableParams.options.name}" (${insertFields.join(',')}) SELECT ${selectFields.join(',')} FROM "${tmpName}"`);
// ADD INDEX
indexChanges.additions.forEach(addition => {
const fields = addition.fields.map(field => `\`${field}\``).join(',');
let type = addition.type;
if (type === 'PRIMARY')
alterColumns.push(`ADD PRIMARY KEY (${fields})`);
else {
if (type === 'UNIQUE')
type = 'UNIQUE INDEX';
alterColumns.push(`ADD ${type} \`${addition.name}\` (${fields})`);
}
});
// ADD FOREIGN KEYS
foreignChanges.additions.forEach(addition => {
alterColumns.push(`ADD CONSTRAINT \`${addition.constraintName}\` FOREIGN KEY (\`${addition.field}\`) REFERENCES \`${addition.refTable}\` (\`${addition.refField}\`) ON UPDATE ${addition.onUpdate} ON DELETE ${addition.onDelete}`);
});
// CHANGE FIELDS
changes.forEach(change => {
const typeInfo = this._getTypeInfo(change.type);
const length = typeInfo.length ? change.enumValues || change.numLength || change.charLength || change.datePrecision : false;
alterColumns.push(`CHANGE COLUMN \`${change.orgName}\` \`${change.name}\`
${change.type.toUpperCase()}${length ? `(${length})` : ''}
${change.unsigned ? 'UNSIGNED' : ''}
${change.zerofill ? 'ZEROFILL' : ''}
${change.nullable ? 'NULL' : 'NOT NULL'}
${change.autoIncrement ? 'AUTO_INCREMENT' : ''}
${change.default ? `DEFAULT ${change.default}` : ''}
${change.comment ? `COMMENT '${change.comment}'` : ''}
${change.collation ? `COLLATE ${change.collation}` : ''}
${change.onUpdate ? `ON UPDATE ${change.onUpdate}` : ''}
${change.after ? `AFTER \`${change.after}\`` : 'FIRST'}`);
});
// CHANGE INDEX
indexChanges.changes.forEach(change => {
if (change.oldType === 'PRIMARY')
alterColumns.push('DROP PRIMARY KEY');
else
alterColumns.push(`DROP INDEX \`${change.oldName}\``);
const fields = change.fields.map(field => `\`${field}\``).join(',');
let type = change.type;
if (type === 'PRIMARY')
alterColumns.push(`ADD PRIMARY KEY (${fields})`);
else {
if (type === 'UNIQUE')
type = 'UNIQUE INDEX';
alterColumns.push(`ADD ${type} \`${change.name}\` (${fields})`);
}
});
// CHANGE FOREIGN KEYS
foreignChanges.changes.forEach(change => {
alterColumns.push(`DROP FOREIGN KEY \`${change.oldName}\``);
alterColumns.push(`ADD CONSTRAINT \`${change.constraintName}\` FOREIGN KEY (\`${change.field}\`) REFERENCES \`${change.refTable}\` (\`${change.refField}\`) ON UPDATE ${change.onUpdate} ON DELETE ${change.onDelete}`);
});
// DROP FIELDS
deletions.forEach(deletion => {
alterColumns.push(`DROP COLUMN \`${deletion.name}\``);
});
// DROP INDEX
indexChanges.deletions.forEach(deletion => {
if (deletion.type === 'PRIMARY')
alterColumns.push('DROP PRIMARY KEY');
else
alterColumns.push(`DROP INDEX \`${deletion.name}\``);
});
// DROP FOREIGN KEYS
foreignChanges.deletions.forEach(deletion => {
alterColumns.push(`DROP FOREIGN KEY \`${deletion.constraintName}\``);
});
sql += alterColumns.join(', ');
// RENAME
if (options.name) sql += `; RENAME TABLE \`${schema}\`.\`${table}\` TO \`${schema}\`.\`${options.name}\``;
return await this.raw(sql);
await this.dropTable({ schema: params.schema, table: tmpName });
await this.raw('PRAGMA foreign_keys = 1');
await this.raw('COMMIT');
}
catch (err) {
await this.raw('ROLLBACK');
return Promise.reject(err);
}
}
/**
* DUPLICATE TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof SQLiteClient
*/
async duplicateTable (params) {
const sql = `CREATE TABLE \`${params.schema}\`.\`${params.table}_copy\` LIKE \`${params.schema}\`.\`${params.table}\``;
async duplicateTable (params) { // TODO: retrive table informations and create a copy
const sql = `CREATE TABLE "${params.schema}"."${params.table}_copy" AS SELECT * FROM "${params.schema}"."${params.table}"`;
return await this.raw(sql);
}
/**
* TRUNCATE TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof SQLiteClient
*/
async truncateTable (params) {
const sql = `TRUNCATE TABLE \`${params.schema}\`.\`${params.table}\``;
const sql = `DELETE FROM "${params.schema}"."${params.table}"`;
return await this.raw(sql);
}
/**
* DROP TABLE
*
* @returns {Array.<Object>} parameters
* @returns {Promise<null>}
* @memberof SQLiteClient
*/
async dropTable (params) {
const sql = `DROP TABLE \`${params.schema}\`.\`${params.table}\``;
const sql = `DROP TABLE "${params.schema}"."${params.table}"`;
return await this.raw(sql);
}
@ -1303,6 +730,10 @@ export class SQLiteClient extends AntaresCore {
let remappedFields = fields
? fields.map(field => {
const parsedType = field.type?.includes('(')
? field.type.split('(')[0]
: field.type;
return {
name: field.name,
alias: field.name,
@ -1311,7 +742,7 @@ export class SQLiteClient extends AntaresCore {
table: field.table,
tableAlias: field.table,
orgTable: field.table,
type: field.type !== null ? field.type : detectedTypes[field.name]
type: field.type !== null ? parsedType : detectedTypes[field.name]
};
}).filter(Boolean)
: [];

View File

@ -13,6 +13,7 @@
<span class="workspace-explorebar-title">{{ connectionName }}</span>
<span v-if="workspace.connectionStatus === 'connected'" class="workspace-explorebar-tools">
<i
v-if="customizations.schemas"
class="mdi mdi-18px mdi-database-plus c-hand mr-2"
:title="$t('message.createNewSchema')"
@click="showNewDBModal"

View File

@ -65,7 +65,11 @@
>
<span class="d-flex"><i class="mdi mdi-18px mdi-database-edit text-light pr-1" /> {{ $t('word.edit') }}</span>
</div>
<div class="context-element" @click="showDeleteModal">
<div
v-if="workspace.customizations.schemaDrop"
class="context-element"
@click="showDeleteModal"
>
<span class="d-flex"><i class="mdi mdi-18px mdi-database-remove text-light pr-1" /> {{ $t('word.delete') }}</span>
</div>

View File

@ -506,6 +506,12 @@ export default {
uid: this.connection.uid,
schema: this.schema,
table: this.table,
tableStructure: {
name: this.localOptions.name,
fields: this.localFields,
foreigns: this.localKeyUsage,
indexes: this.localIndexes
},
additions,
changes,
deletions,

View File

@ -367,7 +367,8 @@ export default {
getWorkspace: 'workspaces/getWorkspace'
}),
localLength () {
return this.localRow.numLength || this.localRow.charLength || this.localRow.datePrecision || this.localRow.numPrecision || 0;
const localLength = this.localRow.numLength || this.localRow.charLength || this.localRow.datePrecision || this.localRow.numPrecision || 0;
return localLength === true ? null : localLength;
},
fieldType () {
const fieldType = this.dataTypes.reduce((acc, group) => [...acc, ...group.types], []).filter(type =>
@ -391,7 +392,7 @@ export default {
return this.indexes.some(index => ['PRIMARY', 'UNIQUE'].includes(index.type));
},
isNullable () {
return !this.indexes.some(index => ['PRIMARY'].includes(index.type));
return this.customizations.nullablePrimary || !this.indexes.some(index => ['PRIMARY'].includes(index.type));
},
isInDataTypes () {
let typeNames = [];

View File

@ -413,8 +413,8 @@ export default {
customizations = require('common/customizations/postgresql');
break;
case 'sqlite':
// dataTypes = require('common/data-types/sqlite');
// indexTypes = require('common/index-types/sqlite');
dataTypes = require('common/data-types/sqlite');
indexTypes = require('common/index-types/sqlite');
customizations = require('common/customizations/sqlite');
break;
}