2020-09-17 17:58:12 +02:00
'use strict' ;
2021-05-15 21:47:30 +02:00
import mysql from 'mysql2/promise' ;
2020-09-17 17:58:12 +02:00
import { AntaresCore } from '../AntaresCore' ;
2021-01-21 18:14:37 +01:00
import dataTypes from 'common/data-types/mysql' ;
2020-09-17 17:58:12 +02:00
export class MySQLClient extends AntaresCore {
2021-01-21 18:14:37 +01:00
constructor ( args ) {
super ( args ) ;
2021-04-09 19:31:41 +02:00
this . _schema = null ;
2021-01-21 18:14:37 +01:00
this . types = {
0 : 'DECIMAL' ,
1 : 'TINYINT' ,
2 : 'SMALLINT' ,
3 : 'INT' ,
4 : 'FLOAT' ,
5 : 'DOUBLE' ,
6 : 'NULL' ,
7 : 'TIMESTAMP' ,
8 : 'BIGINT' ,
9 : 'MEDIUMINT' ,
10 : 'DATE' ,
11 : 'TIME' ,
12 : 'DATETIME' ,
13 : 'YEAR' ,
14 : 'NEWDATE' ,
15 : 'VARCHAR' ,
16 : 'BIT' ,
17 : 'TIMESTAMP2' ,
18 : 'DATETIME2' ,
19 : 'TIME2' ,
245 : 'JSON' ,
246 : 'NEWDECIMAL' ,
247 : 'ENUM' ,
248 : 'SET' ,
249 : 'TINY_BLOB' ,
250 : 'MEDIUM_BLOB' ,
251 : 'LONG_BLOB' ,
252 : 'BLOB' ,
253 : 'VARCHAR' ,
254 : 'CHAR' ,
255 : 'GEOMETRY'
} ;
}
_getType ( field ) {
2021-03-02 12:03:01 +01:00
let name = this . types [ field . columnType ] ;
let length = field . columnLength ;
2021-01-21 18:14:37 +01:00
if ( [ 'DATE' , 'TIME' , 'YEAR' , 'DATETIME' ] . includes ( name ) )
length = field . decimals ;
2021-02-27 18:30:34 +01:00
if ( name === 'TIMESTAMP' )
length = 0 ;
2021-03-02 12:03:01 +01:00
if ( field . charsetNr === 63 ) { // if binary
if ( name === 'CHAR' )
name = 'BINARY' ;
else if ( name === 'VARCHAR' )
name = 'VARBINARY' ;
}
2021-01-21 18:14:37 +01:00
if ( name === 'BLOB' ) {
switch ( length ) {
case 765 :
name = 'TYNITEXT' ;
break ;
case 196605 :
name = 'TEXT' ;
break ;
case 50331645 :
name = 'MEDIUMTEXT' ;
break ;
case 4294967295 :
name = field . charsetNr === 63 ? 'LONGBLOB' : 'LONGTEXT' ;
break ;
case 255 :
name = 'TINYBLOB' ;
break ;
case 65535 :
name = 'BLOB' ;
break ;
case 16777215 :
name = 'MEDIUMBLOB' ;
break ;
2021-01-23 15:50:21 +01:00
default :
name = field . charsetNr === 63 ? 'BLOB' : 'TEXT' ;
2021-01-21 18:14:37 +01:00
}
}
return { name , length } ;
}
_getTypeInfo ( type ) {
return dataTypes
. reduce ( ( acc , group ) => [ ... acc , ... group . types ] , [ ] )
. filter ( _type => _type . name === type . toUpperCase ( ) ) [ 0 ] ;
}
2020-09-17 17:58:12 +02:00
/ * *
* @ memberof MySQLClient
* /
async connect ( ) {
2021-05-15 21:47:30 +02:00
delete this . _params . application _name ;
2020-09-17 17:58:12 +02:00
if ( ! this . _poolSize )
2021-05-15 21:47:30 +02:00
this . _connection = await mysql . createConnection ( this . _params ) ;
2021-04-16 18:48:56 +02:00
else {
this . _connection = mysql . createPool ( {
... this . _params ,
connectionLimit : this . _poolSize ,
typeCast : ( field , next ) => {
if ( field . type === 'DATETIME' )
return field . string ( ) ;
else
return next ( ) ;
}
} ) ;
}
2020-09-17 17:58:12 +02:00
}
/ * *
* @ memberof MySQLClient
* /
destroy ( ) {
this . _connection . end ( ) ;
}
/ * *
* Executes an USE query
*
2020-09-27 19:06:13 +02:00
* @ param { String } schema
2020-09-17 17:58:12 +02:00
* @ memberof MySQLClient
* /
use ( schema ) {
2021-04-09 19:31:41 +02:00
this . _schema = schema ;
2020-09-29 16:43:20 +02:00
return this . raw ( ` USE \` ${ schema } \` ` ) ;
2020-09-17 17:58:12 +02:00
}
2020-09-27 19:06:13 +02:00
/ * *
2021-01-23 15:50:21 +01:00
* @ param { Array } schemas list
2020-09-27 19:06:13 +02:00
* @ returns { Array . < Object > } databases scructure
* @ memberof MySQLClient
* /
2021-01-23 15:50:21 +01:00
async getStructure ( schemas ) {
2020-09-27 19:06:13 +02:00
const { rows : databases } = await this . raw ( 'SHOW DATABASES' ) ;
2020-10-03 12:11:42 +02:00
const { rows : functions } = await this . raw ( 'SHOW FUNCTION STATUS' ) ;
const { rows : procedures } = await this . raw ( 'SHOW PROCEDURE STATUS' ) ;
const { rows : schedulers } = await this . raw ( 'SELECT *, EVENT_SCHEMA AS `Db`, EVENT_NAME AS `Name` FROM information_schema.`EVENTS`' ) ;
2020-12-28 13:05:30 +01:00
const tablesArr = [ ] ;
2020-10-03 12:11:42 +02:00
const triggersArr = [ ] ;
2020-12-28 13:05:30 +01:00
2020-10-03 12:11:42 +02:00
for ( const db of databases ) {
2021-01-23 15:50:21 +01:00
if ( ! schemas . has ( db . Database ) ) continue ;
2020-12-28 13:05:30 +01:00
let { rows : tables } = await this . raw ( ` SHOW TABLE STATUS FROM \` ${ db . Database } \` ` ) ;
if ( tables . length ) {
tables = tables . map ( table => {
table . Db = db . Database ;
return table ;
} ) ;
tablesArr . push ( ... tables ) ;
}
2020-10-03 12:11:42 +02:00
let { rows : triggers } = await this . raw ( ` SHOW TRIGGERS FROM \` ${ db . Database } \` ` ) ;
if ( triggers . length ) {
triggers = triggers . map ( trigger => {
trigger . Db = db . Database ;
return trigger ;
} ) ;
triggersArr . push ( ... triggers ) ;
}
}
2020-10-14 19:00:13 +02:00
return databases . map ( db => {
2021-01-23 15:50:21 +01:00
if ( schemas . has ( db . Database ) ) {
// TABLES
const remappedTables = tablesArr . filter ( table => table . Db === db . Database ) . map ( table => {
let tableType ;
switch ( table . Comment ) {
case 'VIEW' :
tableType = 'view' ;
break ;
default :
tableType = 'table' ;
break ;
}
return {
name : table . Name ,
type : tableType ,
rows : table . Rows ,
created : table . Create _time ,
updated : table . Update _time ,
engine : table . Engine ,
comment : table . Comment ,
size : table . Data _length + table . Index _length ,
autoIncrement : table . Auto _increment ,
collation : table . Collation
} ;
} ) ;
2020-10-12 18:45:15 +02:00
2021-01-23 15:50:21 +01:00
// PROCEDURES
const remappedProcedures = procedures . filter ( procedure => procedure . Db === db . Database ) . map ( procedure => {
return {
name : procedure . Name ,
type : procedure . Type ,
definer : procedure . Definer ,
created : procedure . Created ,
updated : procedure . Modified ,
comment : procedure . Comment ,
charset : procedure . character _set _client ,
security : procedure . Security _type
} ;
} ) ;
2020-10-12 18:45:15 +02:00
2021-01-23 15:50:21 +01:00
// 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
} ;
} ) ;
2020-10-14 19:00:13 +02:00
2021-01-23 15:50:21 +01:00
// SCHEDULERS
const remappedSchedulers = schedulers . filter ( scheduler => scheduler . Db === db . Database ) . map ( scheduler => {
return {
name : scheduler . EVENT _NAME ,
definition : scheduler . EVENT _DEFINITION ,
type : scheduler . EVENT _TYPE ,
definer : scheduler . DEFINER ,
body : scheduler . EVENT _BODY ,
starts : scheduler . STARTS ,
ends : scheduler . ENDS ,
status : scheduler . STATUS ,
executeAt : scheduler . EXECUTE _AT ,
intervalField : scheduler . INTERVAL _FIELD ,
intervalValue : scheduler . INTERVAL _VALUE ,
onCompletion : scheduler . ON _COMPLETION ,
originator : scheduler . ORIGINATOR ,
sqlMode : scheduler . SQL _MODE ,
created : scheduler . CREATED ,
updated : scheduler . LAST _ALTERED ,
lastExecuted : scheduler . LAST _EXECUTED ,
comment : scheduler . EVENT _COMMENT ,
charset : scheduler . CHARACTER _SET _CLIENT ,
timezone : scheduler . TIME _ZONE
} ;
} ) ;
// TRIGGERS
const remappedTriggers = triggersArr . filter ( trigger => trigger . Db === db . Database ) . map ( trigger => {
return {
name : trigger . Trigger ,
statement : trigger . Statement ,
timing : trigger . Timing ,
definer : trigger . Definer ,
event : trigger . Event ,
table : trigger . Table ,
sqlMode : trigger . sql _mode ,
created : trigger . Created ,
charset : trigger . character _set _client
} ;
} ) ;
2021-01-05 17:25:18 +01:00
2020-10-14 19:00:13 +02:00
return {
2021-01-23 15:50:21 +01:00
name : db . Database ,
tables : remappedTables ,
functions : remappedFunctions ,
procedures : remappedProcedures ,
triggers : remappedTriggers ,
schedulers : remappedSchedulers
2020-10-14 19:00:13 +02:00
} ;
2021-01-23 15:50:21 +01:00
}
else {
2020-10-14 19:00:13 +02:00
return {
2021-01-23 15:50:21 +01:00
name : db . Database ,
tables : [ ] ,
functions : [ ] ,
procedures : [ ] ,
triggers : [ ] ,
schedulers : [ ]
2020-10-14 19:00:13 +02:00
} ;
2021-01-23 15:50:21 +01:00
}
2020-09-27 19:06:13 +02:00
} ) ;
}
2020-10-16 17:26:47 +02:00
/ * *
* @ param { Object } params
* @ param { String } params . schema
* @ param { String } params . table
* @ returns { Object } table scructure
* @ memberof MySQLClient
* /
async getTableColumns ( { schema , table } ) {
const { rows } = await this
. select ( '*' )
. schema ( 'information_schema' )
. from ( 'COLUMNS' )
2021-05-27 22:13:59 +02:00
. where ( { TABLE _SCHEMA : ` = ' ${ this . _schema || schema } ' ` , TABLE _NAME : ` = ' ${ table } ' ` } )
2020-10-16 17:26:47 +02:00
. orderBy ( { ORDINAL _POSITION : 'ASC' } )
. run ( ) ;
2021-05-27 22:13:59 +02:00
const { rows : fields } = await this . raw ( ` SHOW CREATE TABLE \` ${ this . _schema || schema } \` . \` ${ table } \` ` ) ;
2021-05-22 16:24:19 +02:00
const remappedFields = fields . map ( row => {
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 => {
2021-05-23 11:12:09 +02:00
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 ( ' ' ) ;
2021-05-26 17:44:33 +02:00
let defaultValue = null ;
if ( details . includes ( 'DEFAULT' ) )
defaultValue = details . match ( / ( ? < = D E F A U L T ) . * ? $ / g s ) [ 0 ] . s p l i t ( ' C O M M E N T ' ) [ 0 ] ;
2021-05-23 11:12:09 +02:00
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 ;
}
2021-05-22 16:24:19 +02:00
} )
. filter ( Boolean )
. reduce ( ( acc , curr ) => {
acc [ curr . name ] = curr ;
return acc ;
} , { } ) ;
} ) [ 0 ] ;
2021-05-20 12:38:05 +02:00
2020-10-16 17:26:47 +02:00
return rows . map ( field => {
2020-10-17 10:12:40 +02:00
let numLength = field . COLUMN _TYPE . match ( /int\(([^)]+)\)/ ) ;
numLength = numLength ? + numLength . pop ( ) : null ;
2021-05-04 21:50:41 +02:00
const enumValues = /(enum|set)/ . test ( field . COLUMN _TYPE )
? field . COLUMN _TYPE . match ( /\(([^)]+)\)/ ) [ 0 ] . slice ( 1 , - 1 )
: null ;
2020-10-17 10:12:40 +02:00
2020-10-16 17:26:47 +02:00
return {
name : field . COLUMN _NAME ,
key : field . COLUMN _KEY . toLowerCase ( ) ,
2021-05-22 16:24:19 +02:00
type : remappedFields [ field . COLUMN _NAME ] . type ,
2020-10-16 17:26:47 +02:00
schema : field . TABLE _SCHEMA ,
table : field . TABLE _NAME ,
numPrecision : field . NUMERIC _PRECISION ,
2020-10-17 10:12:40 +02:00
numLength ,
2021-05-04 21:50:41 +02:00
enumValues ,
2020-10-16 17:26:47 +02:00
datePrecision : field . DATETIME _PRECISION ,
charLength : field . CHARACTER _MAXIMUM _LENGTH ,
2020-10-17 10:12:40 +02:00
nullable : field . IS _NULLABLE . includes ( 'YES' ) ,
unsigned : field . COLUMN _TYPE . includes ( 'unsigned' ) ,
zerofill : field . COLUMN _TYPE . includes ( 'zerofill' ) ,
order : field . ORDINAL _POSITION ,
2021-05-22 16:24:19 +02:00
default : remappedFields [ field . COLUMN _NAME ] . default ,
2020-10-16 17:26:47 +02:00
charset : field . CHARACTER _SET _NAME ,
collation : field . COLLATION _NAME ,
autoIncrement : field . EXTRA . includes ( 'auto_increment' ) ,
2020-11-13 12:39:40 +01:00
onUpdate : field . EXTRA . toLowerCase ( ) . includes ( 'on update' ) ? field . EXTRA . replace ( 'on update' , '' ) : '' ,
2020-10-16 17:26:47 +02:00
comment : field . COLUMN _COMMENT
} ;
} ) ;
}
2020-11-20 17:24:02 +01:00
/ * *
* @ param { Object } params
* @ param { String } params . schema
* @ param { String } params . table
* @ returns { Object } table indexes
* @ memberof MySQLClient
* /
async getTableIndexes ( { schema , table } ) {
const { rows } = await this . raw ( ` SHOW INDEXES FROM \` ${ table } \` FROM \` ${ schema } \` ` ) ;
return rows . map ( row => {
return {
unique : ! row . Non _unique ,
name : row . Key _name ,
column : row . Column _name ,
indexType : row . Index _type ,
type : row . Key _name === 'PRIMARY' ? 'PRIMARY' : ! row . Non _unique ? 'UNIQUE' : row . Index _type === 'FULLTEXT' ? 'FULLTEXT' : 'INDEX' ,
cardinality : row . Cardinality ,
comment : row . Comment ,
indexComment : row . Index _comment
} ;
} ) ;
}
2020-10-16 17:26:47 +02:00
/ * *
* @ param { Object } params
* @ param { String } params . schema
* @ param { String } params . table
* @ returns { Object } table key usage
* @ memberof MySQLClient
* /
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 ( ) ;
2020-12-15 17:08:36 +01:00
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 ( ) ;
2020-10-16 17:26:47 +02:00
return rows . map ( field => {
2020-12-15 17:08:36 +01:00
const extra = extras . find ( x => x . CONSTRAINT _NAME === field . CONSTRAINT _NAME ) ;
2020-10-16 17:26:47 +02:00
return {
schema : field . TABLE _SCHEMA ,
table : field . TABLE _NAME ,
2020-12-15 17:08:36 +01:00
field : field . COLUMN _NAME ,
2020-10-16 17:26:47 +02:00
position : field . ORDINAL _POSITION ,
constraintPosition : field . POSITION _IN _UNIQUE _CONSTRAINT ,
constraintName : field . CONSTRAINT _NAME ,
refSchema : field . REFERENCED _TABLE _SCHEMA ,
refTable : field . REFERENCED _TABLE _NAME ,
2020-12-15 17:08:36 +01:00
refField : field . REFERENCED _COLUMN _NAME ,
onUpdate : extra . UPDATE _RULE ,
onDelete : extra . DELETE _RULE
2020-10-16 17:26:47 +02:00
} ;
} ) ;
}
2020-12-29 10:35:46 +01:00
/ * *
2021-01-23 15:50:21 +01:00
* SELECT ` user ` , ` host ` , authentication _string ) AS ` password ` FROM ` mysql ` . ` user `
2020-12-29 10:35:46 +01:00
*
* @ returns { Array . < Object > } users list
* @ memberof MySQLClient
* /
async getUsers ( ) {
2021-01-23 15:50:21 +01:00
const { rows } = await this . raw ( 'SELECT `user`, `host`, authentication_string AS `password` FROM `mysql`.`user`' ) ;
2020-12-29 10:35:46 +01:00
return rows . map ( row => {
return {
name : row . user ,
host : row . host ,
password : row . password
} ;
} ) ;
}
2021-03-16 18:42:03 +01:00
/ * *
* CREATE DATABASE
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
2021-03-17 11:15:14 +01:00
async createSchema ( params ) {
2021-03-16 18:42:03 +01:00
return await this . raw ( ` CREATE DATABASE \` ${ params . name } \` COLLATE ${ params . collation } ` ) ;
}
/ * *
* ALTER DATABASE
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
2021-03-17 11:15:14 +01:00
async alterSchema ( params ) {
2021-03-16 18:42:03 +01:00
return await this . raw ( ` ALTER DATABASE \` ${ params . name } \` COLLATE ${ params . collation } ` ) ;
}
/ * *
* DROP DATABASE
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
2021-03-17 11:15:14 +01:00
async dropSchema ( params ) {
2021-03-16 18:42:03 +01:00
return await this . raw ( ` DROP DATABASE \` ${ params . database } \` ` ) ;
}
/ * *
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async getDatabaseCollation ( params ) {
return await this . raw ( ` SELECT \` DEFAULT_COLLATION_NAME \` FROM \` information_schema \` . \` SCHEMATA \` WHERE \` SCHEMA_NAME \` =' ${ params . database } ' ` ) ;
}
2020-12-26 14:47:15 +01:00
/ * *
* SHOW CREATE VIEW
*
* @ returns { Array . < Object > } view informations
* @ memberof MySQLClient
* /
async getViewInformations ( { schema , view } ) {
const sql = ` SHOW CREATE VIEW \` ${ schema } \` . \` ${ view } \` ` ;
const results = await this . raw ( sql ) ;
return results . rows . map ( row => {
return {
algorithm : row [ 'Create View' ] . match ( / ( ? < = C R E A T E A L G O R I T H M = ) . * ? ( ? = \ s ) / g s ) [ 0 ] ,
definer : row [ 'Create View' ] . match ( / ( ? < = D E F I N E R = ) . * ? ( ? = \ s ) / g s ) [ 0 ] ,
security : row [ 'Create View' ] . match ( / ( ? < = S Q L S E C U R I T Y ) . * ? ( ? = \ s ) / g s ) [ 0 ] ,
updateOption : row [ 'Create View' ] . match ( / ( ? < = W I T H ) . * ? ( ? = \ s ) / g s ) ? r o w [ ' C r e a t e V i e w ' ] . m a t c h ( / ( ? < = W I T H ) . * ? ( ? = \ s ) / g s ) [ 0 ] : ' ' ,
sql : row [ 'Create View' ] . match ( / ( ? < = A S ) . * ? $ / g s ) [ 0 ] ,
name : row . View
} ;
} ) [ 0 ] ;
}
/ * *
* DROP VIEW
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async dropView ( params ) {
2021-05-14 17:02:27 +02:00
const sql = ` DROP VIEW \` ${ this . _schema } \` . \` ${ params . view } \` ` ;
2020-12-26 14:47:15 +01:00
return await this . raw ( sql ) ;
}
2020-12-26 15:37:34 +01:00
/ * *
* ALTER VIEW
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async alterView ( params ) {
const { view } = params ;
2021-05-14 17:02:27 +02:00
let sql = ` ALTER ALGORITHM = ${ view . algorithm } ${ view . definer ? ` DEFINER= ${ view . definer } ` : '' } SQL SECURITY ${ view . security } VIEW \` ${ this . _schema } \` . \` ${ view . oldName } \` AS ${ view . sql } ${ view . updateOption ? ` WITH ${ view . updateOption } CHECK OPTION ` : '' } ` ;
2020-12-27 13:14:41 +01:00
if ( view . name !== view . oldName )
2021-05-14 17:02:27 +02:00
sql += ` ; RENAME TABLE \` ${ this . _schema } \` . \` ${ view . oldName } \` TO \` ${ this . _schema } \` . \` ${ view . name } \` ` ;
2020-12-27 13:14:41 +01:00
2020-12-26 15:37:34 +01:00
return await this . raw ( sql ) ;
}
2020-12-27 16:16:48 +01:00
/ * *
* CREATE VIEW
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async createView ( view ) {
2021-05-14 17:02:27 +02:00
const sql = ` CREATE ALGORITHM = ${ view . algorithm } ${ view . definer ? ` DEFINER= ${ view . definer } ` : '' } SQL SECURITY ${ view . security } VIEW \` ${ this . _schema } \` . \` ${ view . name } \` AS ${ view . sql } ${ view . updateOption ? ` WITH ${ view . updateOption } CHECK OPTION ` : '' } ` ;
2020-12-27 16:16:48 +01:00
return await this . raw ( sql ) ;
}
2020-12-31 19:55:02 +01:00
/ * *
* SHOW CREATE TRIGGER
*
* @ returns { Array . < Object > } view informations
* @ memberof MySQLClient
* /
async getTriggerInformations ( { schema , trigger } ) {
const sql = ` SHOW CREATE TRIGGER \` ${ schema } \` . \` ${ trigger } \` ` ;
const results = await this . raw ( sql ) ;
return results . rows . map ( row => {
return {
definer : row [ 'SQL Original Statement' ] . match ( / ( ? < = D E F I N E R = ) . * ? ( ? = \ s ) / g s ) [ 0 ] ,
2021-01-05 17:25:18 +01:00
sql : row [ 'SQL Original Statement' ] . match ( / ( B E G I N | b e g i n ) ( . * ) ( E N D | e n d ) / g s ) [ 0 ] ,
2020-12-31 19:55:02 +01:00
name : row . Trigger ,
table : row [ 'SQL Original Statement' ] . match ( / ( ? < = O N ` ) . * ? ( ? = ` ) / g s ) [ 0 ] ,
event1 : row [ 'SQL Original Statement' ] . match ( / ( B E F O R E | A F T E R ) / g s ) [ 0 ] ,
event2 : row [ 'SQL Original Statement' ] . match ( / ( I N S E R T | U P D A T E | D E L E T E ) / g s ) [ 0 ]
} ;
} ) [ 0 ] ;
}
/ * *
* DROP TRIGGER
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async dropTrigger ( params ) {
2021-05-14 17:02:27 +02:00
const sql = ` DROP TRIGGER \` ${ this . _schema } \` . \` ${ params . trigger } \` ` ;
2020-12-31 19:55:02 +01:00
return await this . raw ( sql ) ;
}
/ * *
* ALTER TRIGGER
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async alterTrigger ( params ) {
const { trigger } = params ;
const tempTrigger = Object . assign ( { } , trigger ) ;
tempTrigger . name = ` Antares_ ${ tempTrigger . name } _tmp ` ;
try {
await this . createTrigger ( tempTrigger ) ;
await this . dropTrigger ( { trigger : tempTrigger . name } ) ;
await this . dropTrigger ( { trigger : trigger . oldName } ) ;
await this . createTrigger ( trigger ) ;
}
catch ( err ) {
return Promise . reject ( err ) ;
}
}
/ * *
* CREATE TRIGGER
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async createTrigger ( trigger ) {
2021-05-14 17:02:27 +02:00
const sql = ` CREATE ${ trigger . definer ? ` DEFINER= ${ trigger . definer } ` : '' } TRIGGER \` ${ this . _schema } \` . \` ${ trigger . name } \` ${ trigger . event1 } ${ trigger . event2 } ON \` ${ trigger . table } \` FOR EACH ROW ${ trigger . sql } ` ;
2021-01-05 17:25:18 +01:00
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 ) ;
2021-04-14 18:06:20 +02:00
return results . rows . map ( async row => {
2021-01-11 18:56:51 +01:00
if ( ! row [ 'Create Procedure' ] ) {
return {
definer : null ,
sql : '' ,
parameters : [ ] ,
name : row . Procedure ,
comment : '' ,
security : 'DEFINER' ,
deterministic : false ,
dataAccess : 'CONTAINS SQL'
} ;
}
2021-04-14 18:06:20 +02:00
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
} ;
} ) ;
2021-01-05 17:25:18 +01:00
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 ( / ( ? < = D E F I N E R = ) . * ? ( ? = \ s ) / g s ) [ 0 ] ,
sql : row [ 'Create Procedure' ] . match ( / ( B E G I N | b e g i n ) ( . * ) ( E N D | e n d ) / g s ) [ 0 ] ,
2021-02-25 17:43:23 +01:00
parameters : parameters || [ ] ,
2021-01-05 17:25:18 +01:00
name : row . Procedure ,
comment : row [ 'Create Procedure' ] . match ( / ( ? < = C O M M E N T ' ) . * ? ( ? = ' ) / g s ) ? r o w [ ' C r e a t e P r o c e d u r e ' ] . m a t c h ( / ( ? < = C O M M E N T ' ) . * ? ( ? = ' ) / g s ) [ 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 ) {
2021-05-14 17:02:27 +02:00
const sql = ` DROP PROCEDURE \` ${ this . _schema } \` . \` ${ params . routine } \` ` ;
2020-12-31 19:55:02 +01:00
return await this . raw ( sql ) ;
}
2021-01-05 17:25:18 +01:00
/ * *
* 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 ) {
2021-02-25 17:43:23 +01:00
const parameters = 'parameters' in routine
? routine . parameters . reduce ( ( acc , curr ) => {
acc . push ( ` ${ curr . context } \` ${ curr . name } \` ${ curr . type } ${ curr . length ? ` ( ${ curr . length } ) ` : '' } ` ) ;
return acc ;
} , [ ] ) . join ( ',' )
: '' ;
2021-01-05 17:25:18 +01:00
2021-05-06 22:21:42 +02:00
const sql = ` CREATE ${ routine . definer ? ` DEFINER= ${ routine . definer } ` : '' } PROCEDURE \` ${ this . _schema } \` . \` ${ routine . name } \` ( ${ parameters } )
2021-01-05 17:25:18 +01:00
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 } ) ;
}
2021-01-10 18:30:56 +01:00
/ * *
* SHOW CREATE FUNCTION
*
* @ returns { Array . < Object > } view informations
* @ memberof MySQLClient
* /
async getFunctionInformations ( { schema , func } ) {
const sql = ` SHOW CREATE FUNCTION \` ${ schema } \` . \` ${ func } \` ` ;
const results = await this . raw ( sql ) ;
2021-04-14 18:06:20 +02:00
return results . rows . map ( async row => {
2021-01-11 18:56:51 +01:00
if ( ! row [ 'Create Function' ] ) {
return {
definer : null ,
sql : '' ,
parameters : [ ] ,
name : row . Procedure ,
comment : '' ,
security : 'DEFINER' ,
deterministic : false ,
dataAccess : 'CONTAINS SQL' ,
returns : 'INT' ,
returnsLength : null
} ;
}
2021-04-14 18:06:20 +02:00
const sql = ` SELECT *
FROM information _schema . parameters
WHERE SPECIFIC _NAME = '${func}'
AND SPECIFIC _SCHEMA = '${schema}'
ORDER BY ORDINAL _POSITION
` ;
2021-01-10 18:30:56 +01:00
2021-04-14 18:06:20 +02:00
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
} ;
} ) ;
2021-01-10 18:30:56 +01:00
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 ( / ( ? < = R E T U R N S ) . * ? ( ? = \ s ) / g s ) . l e n g t h ? r o w [ ' C r e a t e F u n c t i o n ' ] . m a t c h ( / ( ? < = R E T U R N S ) . * ? ( ? = \ s ) / g s ) [ 0 ] . r e p l a c e ( ' ) ' , ' ' ) . s p l i t ( ' ( ' ) : [ ' ' , n u l l ] ;
return {
definer : row [ 'Create Function' ] . match ( / ( ? < = D E F I N E R = ) . * ? ( ? = \ s ) / g s ) [ 0 ] ,
sql : row [ 'Create Function' ] . match ( / ( B E G I N | b e g i n ) ( . * ) ( E N D | e n d ) / g s ) [ 0 ] ,
2021-02-25 17:43:23 +01:00
parameters : parameters || [ ] ,
2021-01-10 18:30:56 +01:00
name : row . Function ,
comment : row [ 'Create Function' ] . match ( / ( ? < = C O M M E N T ' ) . * ? ( ? = ' ) / g s ) ? r o w [ ' C r e a t e F u n c t i o n ' ] . m a t c h ( / ( ? < = C O M M E N T ' ) . * ? ( ? = ' ) / g s ) [ 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 MySQLClient
* /
async dropFunction ( params ) {
2021-05-14 17:02:27 +02:00
const sql = ` DROP FUNCTION \` ${ this . _schema } \` . \` ${ params . func } \` ` ;
2021-01-10 18:30:56 +01:00
return await this . raw ( sql ) ;
}
/ * *
* ALTER FUNCTION
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
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 ( { func : tempProcedure . name } ) ;
await this . dropFunction ( { func : func . oldName } ) ;
await this . createFunction ( func ) ;
}
catch ( err ) {
return Promise . reject ( err ) ;
}
}
/ * *
* CREATE FUNCTION
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async createFunction ( func ) {
const parameters = func . parameters . reduce ( ( acc , curr ) => {
acc . push ( ` \` ${ curr . name } \` ${ curr . type } ${ curr . length ? ` ( ${ curr . length } ) ` : '' } ` ) ;
return acc ;
} , [ ] ) . join ( ',' ) ;
2021-04-14 18:06:20 +02:00
const body = func . returns ? func . sql : 'BEGIN\n RETURN 0;\nEND' ;
2021-05-14 17:02:27 +02:00
const sql = ` CREATE ${ func . definer ? ` DEFINER= ${ func . definer } ` : '' } FUNCTION \` ${ this . _schema } \` . \` ${ func . name } \` ( ${ parameters } ) RETURNS ${ func . returns || 'SMALLINT' } ${ func . returnsLength ? ` ( ${ func . returnsLength } ) ` : '' }
2021-01-10 18:30:56 +01:00
LANGUAGE SQL
$ { func . deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC' }
$ { func . dataAccess }
SQL SECURITY $ { func . security }
COMMENT '${func.comment}'
2021-04-14 18:06:20 +02:00
$ { body } ` ;
2021-01-10 18:30:56 +01:00
return await this . raw ( sql , { split : false } ) ;
}
2021-01-14 18:11:36 +01:00
/ * *
* SHOW CREATE EVENT
*
* @ returns { Array . < Object > } view informations
* @ memberof MySQLClient
* /
async getEventInformations ( { schema , scheduler } ) {
const sql = ` SHOW CREATE EVENT \` ${ schema } \` . \` ${ scheduler } \` ` ;
const results = await this . raw ( sql ) ;
return results . rows . map ( row => {
2021-01-22 18:46:33 +01:00
const schedule = row [ 'Create Event' ] ;
2021-01-14 18:11:36 +01:00
const execution = schedule . includes ( 'EVERY' ) ? 'EVERY' : 'ONCE' ;
2021-01-15 19:18:16 +01:00
const every = execution === 'EVERY' ? row [ 'Create Event' ] . match ( / ( ? < = E V E R Y ) ( \ s * ( [ ^ \ s ] + ) ) { 0 , 2 } / g s ) [ 0 ] . r e p l a c e A l l ( ' \ ' ' , ' ' ) . s p l i t ( ' ' ) : [ ] ;
2021-01-14 18:11:36 +01:00
const starts = execution === 'EVERY' && schedule . includes ( 'STARTS' ) ? schedule . match ( / ( ? < = S T A R T S ' ) . * ? ( ? = ' \ s ) / g s ) [ 0 ] : ' ' ;
const ends = execution === 'EVERY' && schedule . includes ( 'ENDS' ) ? schedule . match ( / ( ? < = E N D S ' ) . * ? ( ? = ' \ s ) / g s ) [ 0 ] : ' ' ;
const at = execution === 'ONCE' && schedule . includes ( 'AT' ) ? schedule . match ( / ( ? < = A T ' ) . * ? ( ? = ' \ s ) / g s ) [ 0 ] : ' ' ;
return {
definer : row [ 'Create Event' ] . match ( / ( ? < = D E F I N E R = ) . * ? ( ? = \ s ) / g s ) [ 0 ] ,
2021-01-22 18:46:33 +01:00
sql : row [ 'Create Event' ] . match ( / ( ? < = D O ) ( . * ) / g s ) [ 0 ] ,
2021-01-14 18:11:36 +01:00
name : row . Event ,
comment : row [ 'Create Event' ] . match ( / ( ? < = C O M M E N T ' ) . * ? ( ? = ' ) / g s ) ? r o w [ ' C r e a t e E v e n t ' ] . m a t c h ( / ( ? < = C O M M E N T ' ) . * ? ( ? = ' ) / g s ) [ 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 MySQLClient
* /
async dropEvent ( params ) {
2021-05-14 17:02:27 +02:00
const sql = ` DROP EVENT \` ${ this . _schema } \` . \` ${ params . scheduler } \` ` ;
2021-01-14 18:11:36 +01:00
return await this . raw ( sql ) ;
}
/ * *
* ALTER EVENT
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async alterEvent ( params ) {
const { scheduler } = params ;
2021-01-15 19:18:16 +01:00
if ( scheduler . execution === 'EVERY' && scheduler . every [ 0 ] . includes ( '-' ) )
scheduler . every [ 0 ] = ` ' ${ scheduler . every [ 0 ] } ' ` ;
2021-05-14 17:02:27 +02:00
const sql = ` ALTER ${ scheduler . definer ? ` DEFINER= ${ scheduler . definer } ` : '' } EVENT \` ${ this . _schema } \` . \` ${ scheduler . oldName } \`
2021-01-15 19:18:16 +01:00
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
2021-05-14 17:02:27 +02:00
$ { scheduler . name !== scheduler . oldName ? ` RENAME TO \` ${ this . _schema } \` . \` ${ scheduler . name } \` ` : '' }
2021-01-15 19:18:16 +01:00
$ { scheduler . state }
COMMENT '${scheduler.comment}'
DO $ { scheduler . sql } ` ;
2021-01-16 11:32:42 +01:00
return await this . raw ( sql , { split : false } ) ;
2021-01-14 18:11:36 +01:00
}
/ * *
* CREATE EVENT
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async createEvent ( scheduler ) {
2021-05-14 17:02:27 +02:00
const sql = ` CREATE ${ scheduler . definer ? ` DEFINER= ${ scheduler . definer } ` : '' } EVENT \` ${ this . _schema } \` . \` ${ scheduler . name } \`
2021-01-16 11:32:42 +01:00
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 . state }
COMMENT '${scheduler.comment}'
DO $ { scheduler . sql } ` ;
2021-01-14 18:11:36 +01:00
return await this . raw ( sql , { split : false } ) ;
}
2020-09-25 12:39:58 +02:00
/ * *
* SHOW COLLATION
*
2020-09-27 19:06:13 +02:00
* @ returns { Array . < Object > } collations list
2020-09-25 12:39:58 +02:00
* @ memberof MySQLClient
* /
async getCollations ( ) {
2020-09-27 19:06:13 +02:00
const results = await this . raw ( 'SHOW COLLATION' ) ;
2020-09-25 12:39:58 +02:00
return results . rows . map ( row => {
return {
charset : row . Charset ,
collation : row . Collation ,
compiled : row . Compiled . includes ( 'Yes' ) ,
default : row . Default . includes ( 'Yes' ) ,
id : row . Id ,
sortLen : row . Sortlen
} ;
} ) ;
}
/ * *
* SHOW VARIABLES
*
2020-09-27 19:06:13 +02:00
* @ returns { Array . < Object > } variables list
2020-09-25 12:39:58 +02:00
* @ memberof MySQLClient
* /
async getVariables ( ) {
const sql = 'SHOW VARIABLES' ;
const results = await this . raw ( sql ) ;
return results . rows . map ( row => {
return {
name : row . Variable _name ,
value : row . Value
} ;
} ) ;
2020-09-24 13:09:01 +02:00
}
2020-11-16 17:16:39 +01:00
/ * *
* SHOW ENGINES
*
* @ returns { Array . < Object > } engines list
* @ memberof MySQLClient
* /
async getEngines ( ) {
const sql = 'SHOW ENGINES' ;
const results = await this . raw ( sql ) ;
return results . rows . map ( row => {
return {
name : row . Engine ,
support : row . Support ,
comment : row . Comment ,
2020-12-03 13:00:54 +01:00
transactions : row . Transactions ,
2020-11-16 17:16:39 +01:00
xa : row . XA ,
2020-12-03 13:00:54 +01:00
savepoints : row . Savepoints ,
isDefault : row . Support . includes ( 'DEFAULT' )
2020-11-16 17:16:39 +01:00
} ;
} ) ;
}
2021-02-01 16:31:48 +01:00
/ * *
* SHOW VARIABLES LIKE '%vers%'
*
* @ returns { Array . < Object > } version parameters
* @ memberof MySQLClient
* /
async getVersion ( ) {
const sql = 'SHOW VARIABLES LIKE "%vers%"' ;
const { rows } = await this . raw ( sql ) ;
return rows . reduce ( ( acc , curr ) => {
switch ( curr . Variable _name ) {
case 'version' :
acc . number = curr . Value . split ( '-' ) [ 0 ] ;
break ;
case 'version_comment' :
acc . name = curr . Value . replace ( '(GPL)' , '' ) ;
break ;
case 'version_compile_machine' :
acc . arch = curr . Value ;
break ;
case 'version_compile_os' :
acc . os = curr . Value ;
break ;
}
return acc ;
} , { } ) ;
}
2021-03-03 19:31:05 +01:00
async getProcesses ( ) {
const sql = 'SELECT `ID`, `USER`, `HOST`, `DB`, `COMMAND`, `TIME`, `STATE`, LEFT(`INFO`, 51200) AS `INFO` FROM `information_schema`.`PROCESSLIST`' ;
const { rows } = await this . raw ( sql ) ;
return rows . map ( row => {
return {
id : row . ID ,
user : row . USER ,
host : row . HOST ,
db : row . DB ,
command : row . COMMAND ,
time : row . TIME ,
state : row . STATE ,
info : row . INFO
} ;
} ) ;
}
2020-12-03 13:00:54 +01:00
/ * *
* CREATE TABLE
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async createTable ( params ) {
const {
name ,
collation ,
comment ,
engine
} = params ;
2021-05-14 17:02:27 +02:00
const sql = ` CREATE TABLE \` ${ this . _schema } \` . \` ${ name } \` ( \` ${ name } _ID \` INT NULL) COMMENT=' ${ comment } ', COLLATE=' ${ collation } ', ENGINE= ${ engine } ` ;
2020-12-03 13:00:54 +01:00
return await this . raw ( sql ) ;
}
2020-11-13 12:39:40 +01:00
/ * *
* ALTER TABLE
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async alterTable ( params ) {
const {
table ,
2020-11-13 15:04:51 +01:00
additions ,
deletions ,
2020-11-16 17:16:39 +01:00
changes ,
2020-12-01 16:48:20 +01:00
indexChanges ,
2020-12-15 17:08:36 +01:00
foreignChanges ,
2020-11-16 17:16:39 +01:00
options
2020-11-13 12:39:40 +01:00
} = params ;
2021-04-09 19:31:41 +02:00
let sql = ` ALTER TABLE \` ${ this . _schema } \` . \` ${ table } \` ` ;
2020-11-13 12:39:40 +01:00
const alterColumns = [ ] ;
2020-11-16 17:16:39 +01:00
// 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 } ' ` ) ;
2020-12-01 16:48:20 +01:00
// ADD FIELDS
2020-11-13 15:04:51 +01:00
additions . forEach ( addition => {
2021-01-21 18:14:37 +01:00
const typeInfo = this . _getTypeInfo ( addition . type ) ;
2021-05-20 12:38:05 +02:00
const length = typeInfo . length ? addition . enumValues || addition . numLength || addition . charLength || addition . datePrecision : false ;
2020-11-13 15:04:51 +01:00
alterColumns . push ( ` ADD COLUMN \` ${ addition . name } \`
$ { addition . type . toUpperCase ( ) } $ { length ? ` ( ${ length } ) ` : '' }
$ { addition . unsigned ? 'UNSIGNED' : '' }
2020-11-13 16:37:52 +01:00
$ { addition . zerofill ? 'ZEROFILL' : '' }
2020-11-13 15:04:51 +01:00
$ { addition . nullable ? 'NULL' : 'NOT NULL' }
$ { addition . autoIncrement ? 'AUTO_INCREMENT' : '' }
2021-05-06 22:21:42 +02:00
$ { addition . default ? ` DEFAULT ${ addition . default } ` : '' }
2020-11-13 15:04:51 +01:00
$ { addition . comment ? ` COMMENT ' ${ addition . comment } ' ` : '' }
$ { addition . collation ? ` COLLATE ${ addition . collation } ` : '' }
$ { addition . onUpdate ? ` ON UPDATE ${ addition . onUpdate } ` : '' }
$ { addition . after ? ` AFTER \` ${ addition . after } \` ` : 'FIRST' } ` );
} ) ;
2020-12-01 16:48:20 +01:00
// 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 } ) ` ) ;
}
} ) ;
2020-12-15 17:08:36 +01:00
// 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 } ` ) ;
} ) ;
2020-12-01 16:48:20 +01:00
// CHANGE FIELDS
2020-11-13 12:39:40 +01:00
changes . forEach ( change => {
2021-01-21 18:14:37 +01:00
const typeInfo = this . _getTypeInfo ( change . type ) ;
2021-05-04 21:50:41 +02:00
const length = typeInfo . length ? change . enumValues || change . numLength || change . charLength || change . datePrecision : false ;
2020-11-13 12:39:40 +01:00
alterColumns . push ( ` CHANGE COLUMN \` ${ change . orgName } \` \` ${ change . name } \`
$ { change . type . toUpperCase ( ) } $ { length ? ` ( ${ length } ) ` : '' }
$ { change . unsigned ? 'UNSIGNED' : '' }
2020-11-13 16:37:52 +01:00
$ { change . zerofill ? 'ZEROFILL' : '' }
2020-11-13 12:39:40 +01:00
$ { change . nullable ? 'NULL' : 'NOT NULL' }
$ { change . autoIncrement ? 'AUTO_INCREMENT' : '' }
2021-05-06 22:21:42 +02:00
$ { change . default ? ` DEFAULT ${ change . default } ` : '' }
2020-11-13 12:39:40 +01:00
$ { change . comment ? ` COMMENT ' ${ change . comment } ' ` : '' }
$ { change . collation ? ` COLLATE ${ change . collation } ` : '' }
$ { change . onUpdate ? ` ON UPDATE ${ change . onUpdate } ` : '' }
$ { change . after ? ` AFTER \` ${ change . after } \` ` : 'FIRST' } ` );
} ) ;
2020-12-01 16:48:20 +01:00
// 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 } ) ` ) ;
}
} ) ;
2020-12-15 17:08:36 +01:00
// 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 } ` ) ;
} ) ;
2020-12-01 16:48:20 +01:00
// DROP FIELDS
2020-11-13 15:04:51 +01:00
deletions . forEach ( deletion => {
alterColumns . push ( ` DROP COLUMN \` ${ deletion . name } \` ` ) ;
} ) ;
2020-12-01 16:48:20 +01:00
// DROP INDEX
indexChanges . deletions . forEach ( deletion => {
if ( deletion . type === 'PRIMARY' )
alterColumns . push ( 'DROP PRIMARY KEY' ) ;
else
alterColumns . push ( ` DROP INDEX \` ${ deletion . name } \` ` ) ;
} ) ;
2020-12-15 17:08:36 +01:00
// DROP FOREIGN KEYS
foreignChanges . deletions . forEach ( deletion => {
alterColumns . push ( ` DROP FOREIGN KEY \` ${ deletion . constraintName } \` ` ) ;
} ) ;
2020-11-13 12:39:40 +01:00
sql += alterColumns . join ( ', ' ) ;
2020-11-16 17:16:39 +01:00
// RENAME
2021-05-14 17:02:27 +02:00
if ( options . name ) sql += ` ; RENAME TABLE \` ${ this . _schema } \` . \` ${ table } \` TO \` ${ this . _schema } \` . \` ${ options . name } \` ` ;
2020-11-16 17:16:39 +01:00
2020-11-13 12:39:40 +01:00
return await this . raw ( sql ) ;
}
2020-12-03 16:15:10 +01:00
/ * *
* TRUNCATE TABLE
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async truncateTable ( params ) {
2021-05-04 21:50:41 +02:00
const sql = ` TRUNCATE TABLE \` ${ this . _schema } \` . \` ${ params . table } \` ` ;
2020-12-03 16:15:10 +01:00
return await this . raw ( sql ) ;
}
/ * *
* DROP TABLE
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async dropTable ( params ) {
2021-05-04 21:50:41 +02:00
const sql = ` DROP TABLE \` ${ this . _schema } \` . \` ${ params . table } \` ` ;
2020-12-03 16:15:10 +01:00
return await this . raw ( sql ) ;
}
2020-09-17 17:58:12 +02:00
/ * *
2020-09-27 19:06:13 +02:00
* @ returns { String } SQL string
2020-09-17 17:58:12 +02:00
* @ memberof MySQLClient
* /
getSQL ( ) {
// SELECT
const selectArray = this . _query . select . reduce ( this . _reducer , [ ] ) ;
let selectRaw = '' ;
if ( selectArray . length )
selectRaw = selectArray . length ? ` SELECT ${ selectArray . join ( ', ' ) } ` : 'SELECT * ' ;
// FROM
let fromRaw = '' ;
if ( ! this . _query . update . length && ! Object . keys ( this . _query . insert ) . length && ! ! this . _query . from )
fromRaw = 'FROM' ;
else if ( Object . keys ( this . _query . insert ) . length )
fromRaw = 'INTO' ;
fromRaw += this . _query . from ? ` ${ this . _query . schema ? ` \` ${ this . _query . schema } \` . ` : '' } \` ${ this . _query . from } \` ` : '' ;
// WHERE
const whereArray = this . _query . where . reduce ( this . _reducer , [ ] ) ;
const whereRaw = whereArray . length ? ` WHERE ${ whereArray . join ( ' AND ' ) } ` : '' ;
// UPDATE
const updateArray = this . _query . update . reduce ( this . _reducer , [ ] ) ;
const updateRaw = updateArray . length ? ` SET ${ updateArray . join ( ', ' ) } ` : '' ;
// INSERT
let insertRaw = '' ;
2021-02-08 11:46:57 +01:00
if ( this . _query . insert . length ) {
const fieldsList = Object . keys ( this . _query . insert [ 0 ] ) ;
const rowsList = this . _query . insert . map ( el => ` ( ${ Object . values ( el ) . join ( ', ' ) } ) ` ) ;
2020-09-17 17:58:12 +02:00
2021-02-08 11:46:57 +01:00
insertRaw = ` ( ${ fieldsList . join ( ', ' ) } ) VALUES ${ rowsList . join ( ', ' ) } ` ;
2020-09-17 17:58:12 +02:00
}
// GROUP BY
const groupByArray = this . _query . groupBy . reduce ( this . _reducer , [ ] ) ;
const groupByRaw = groupByArray . length ? ` GROUP BY ${ groupByArray . join ( ', ' ) } ` : '' ;
// ORDER BY
const orderByArray = this . _query . orderBy . reduce ( this . _reducer , [ ] ) ;
const orderByRaw = orderByArray . length ? ` ORDER BY ${ orderByArray . join ( ', ' ) } ` : '' ;
// LIMIT
const limitRaw = this . _query . limit . length ? ` LIMIT ${ this . _query . limit . join ( ', ' ) } ` : '' ;
2021-05-27 22:13:59 +02:00
// OFFSET
2021-05-31 14:27:02 +02:00
const offsetRaw = this . _query . offset . length ? ` OFFSET ${ this . _query . offset . join ( ', ' ) } ` : '' ;
2021-05-27 22:13:59 +02:00
return ` ${ selectRaw } ${ updateRaw ? 'UPDATE' : '' } ${ insertRaw ? 'INSERT ' : '' } ${ this . _query . delete ? 'DELETE ' : '' } ${ fromRaw } ${ updateRaw } ${ whereRaw } ${ groupByRaw } ${ orderByRaw } ${ limitRaw } ${ offsetRaw } ${ insertRaw } ` ;
2020-09-17 17:58:12 +02:00
}
/ * *
* @ param { string } sql raw SQL query
2020-10-27 16:41:00 +01:00
* @ param { object } args
* @ param { boolean } args . nest
* @ param { boolean } args . details
2021-01-05 17:25:18 +01:00
* @ param { boolean } args . split
2020-09-17 17:58:12 +02:00
* @ returns { Promise }
* @ memberof MySQLClient
* /
2020-10-27 16:41:00 +01:00
async raw ( sql , args ) {
2021-05-15 21:47:30 +02:00
if ( process . env . NODE _ENV === 'development' ) this . _logger ( sql ) ; // TODO: replace BLOB content with a placeholder
2020-10-27 16:41:00 +01:00
args = {
nest : false ,
details : false ,
2021-01-05 17:25:18 +01:00
split : true ,
2020-10-27 16:41:00 +01:00
... args
} ;
2021-05-14 17:02:27 +02:00
2020-10-27 16:41:00 +01:00
const nestTables = args . nest ? '.' : false ;
2020-09-17 17:58:12 +02:00
const resultsArr = [ ] ;
2020-10-27 16:41:00 +01:00
let paramsArr = [ ] ;
2021-04-29 21:03:32 +02:00
const queries = args . split ? sql . split ( /((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm ) : [ sql ] ;
2021-05-15 21:47:30 +02:00
const isPool = typeof this . _connection . getConnection === 'function' ;
const connection = isPool ? await this . _connection . getConnection ( ) : this . _connection ;
2020-09-17 17:58:12 +02:00
2021-05-15 21:47:30 +02:00
if ( args . schema )
await connection . query ( ` USE \` ${ args . schema } \` ` ) ;
2020-09-17 17:58:12 +02:00
for ( const query of queries ) {
if ( ! query ) continue ;
2021-02-26 18:45:00 +01:00
const timeStart = new Date ( ) ;
let timeStop ;
2020-10-27 16:41:00 +01:00
let keysArr = [ ] ;
2021-02-26 18:45:00 +01:00
const { rows , report , fields , keys , duration } = await new Promise ( ( resolve , reject ) => {
2021-05-15 21:47:30 +02:00
connection . query ( { sql : query , nestTables } ) . then ( async ( [ response , fields ] ) => {
2021-02-26 18:45:00 +01:00
timeStop = new Date ( ) ;
2020-10-27 16:41:00 +01:00
const queryResult = response ;
2020-09-17 17:58:12 +02:00
2021-05-15 21:47:30 +02:00
let remappedFields = fields
? fields . map ( field => {
if ( ! field || Array . isArray ( field ) )
return false ;
const type = this . _getType ( field ) ;
return {
name : field . orgName ,
alias : field . name ,
orgName : field . orgName ,
schema : field . schema ,
table : field . table ,
tableAlias : field . table ,
orgTable : field . orgTable ,
type : type . name ,
length : type . length
} ;
} ) . filter ( Boolean )
: [ ] ;
if ( args . details ) {
let cachedTable ;
if ( remappedFields . length ) {
paramsArr = remappedFields . map ( field => {
if ( field . orgTable ) cachedTable = field . orgTable ; // Needed for some queries on information_schema
2020-11-13 12:39:40 +01:00
return {
2021-05-15 21:47:30 +02:00
table : field . orgTable || cachedTable ,
schema : field . schema || 'INFORMATION_SCHEMA'
2020-11-13 12:39:40 +01:00
} ;
2021-05-15 21:47:30 +02:00
} ) . filter ( ( val , i , arr ) => arr . findIndex ( el => el . schema === val . schema && el . table === val . table ) === i ) ;
for ( const paramObj of paramsArr ) {
if ( ! paramObj . table || ! paramObj . schema ) continue ;
try { // Column details
const response = await this . getTableColumns ( paramObj ) ;
remappedFields = remappedFields . map ( field => {
const detailedField = response . find ( f => f . name === field . name ) ;
if ( detailedField && field . orgTable === paramObj . table && field . schema === paramObj . schema )
field = { ... field , ... detailedField } ;
return field ;
} ) ;
}
catch ( err ) {
2021-05-27 22:13:59 +02:00
if ( isPool ) connection . release ( ) ;
2021-05-15 21:47:30 +02:00
reject ( err ) ;
}
try { // Key usage (foreign keys)
const response = await this . getKeyUsage ( paramObj ) ;
keysArr = keysArr ? [ ... keysArr , ... response ] : response ;
}
catch ( err ) {
2021-05-27 22:13:59 +02:00
if ( isPool ) connection . release ( ) ;
2021-05-15 21:47:30 +02:00
reject ( err ) ;
2020-10-27 16:41:00 +01:00
}
}
}
2020-09-17 17:58:12 +02:00
}
2021-05-15 21:47:30 +02:00
resolve ( {
duration : timeStop - timeStart ,
rows : Array . isArray ( queryResult ) ? queryResult . some ( el => Array . isArray ( el ) ) ? [ ] : queryResult : false ,
report : ! Array . isArray ( queryResult ) ? queryResult : false ,
fields : remappedFields ,
keys : keysArr
} ) ;
} ) . catch ( reject ) ;
2020-09-17 17:58:12 +02:00
} ) ;
2020-10-27 16:41:00 +01:00
2021-02-26 18:45:00 +01:00
resultsArr . push ( { rows , report , fields , keys , duration } ) ;
2020-09-17 17:58:12 +02:00
}
2021-05-15 21:47:30 +02:00
if ( isPool ) connection . release ( ) ;
2020-09-17 17:58:12 +02:00
return resultsArr . length === 1 ? resultsArr [ 0 ] : resultsArr ;
}
}