2020-09-17 17:58:12 +02:00
'use strict' ;
import mysql from 'mysql' ;
import { AntaresCore } from '../AntaresCore' ;
export class MySQLClient extends AntaresCore {
/ * *
* @ memberof MySQLClient
* /
async connect ( ) {
if ( ! this . _poolSize )
this . _connection = mysql . createConnection ( this . _params ) ;
else
this . _connection = mysql . createPool ( { ... this . _params , connectionLimit : this . _poolSize } ) ;
}
/ * *
* @ 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 ) {
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
/ * *
* @ returns { Array . < Object > } databases scructure
* @ memberof MySQLClient
* /
async getStructure ( ) {
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 ) {
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 => {
// TABLES
2020-12-28 13:05:30 +01:00
const remappedTables = tablesArr . filter ( table => table . Db === db . Database ) . map ( table => {
2020-10-12 18:45:15 +02:00
let tableType ;
2020-12-28 13:05:30 +01:00
switch ( table . Comment ) {
2020-10-12 18:45:15 +02:00
case 'VIEW' :
tableType = 'view' ;
break ;
default :
tableType = 'table' ;
break ;
}
return {
2020-12-28 13:05:30 +01:00
name : table . Name ,
2020-10-12 18:45:15 +02:00
type : tableType ,
2020-12-28 13:05:30 +01:00
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
} ;
} ) ;
2020-10-14 19:00:13 +02: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
} ;
} ) ;
// 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
} ;
} ) ;
2020-09-27 19:06:13 +02:00
return {
name : db . Database ,
2020-10-14 19:00:13 +02:00
tables : remappedTables ,
functions : functions . filter ( func => func . Db === db . Database ) , // TODO: remap functions
procedures : remappedProcedures ,
triggers : remappedTriggers ,
schedulers : remappedSchedulers
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' )
. where ( { TABLE _SCHEMA : ` = ' ${ schema } ' ` , TABLE _NAME : ` = ' ${ table } ' ` } )
. orderBy ( { ORDINAL _POSITION : 'ASC' } )
. run ( ) ;
return rows . map ( field => {
2020-10-17 10:12:40 +02:00
let numLength = field . COLUMN _TYPE . match ( /int\(([^)]+)\)/ ) ;
numLength = numLength ? + numLength . pop ( ) : null ;
2020-10-16 17:26:47 +02:00
return {
name : field . COLUMN _NAME ,
key : field . COLUMN _KEY . toLowerCase ( ) ,
2020-12-07 19:11:29 +01:00
type : field . DATA _TYPE . toUpperCase ( ) ,
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 ,
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 ,
2020-10-16 17:26:47 +02:00
default : field . COLUMN _DEFAULT ,
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
/ * *
* SELECT ` user ` , ` host ` , IF ( LENGTH ( password ) > 0 , password , authentication _string ) AS ` password ` FROM ` mysql ` . ` user `
*
* @ returns { Array . < Object > } users list
* @ memberof MySQLClient
* /
async getUsers ( ) {
const { rows } = await this . raw ( 'SELECT `user`, `host`, IF(LENGTH(password)>0, password, authentication_string) AS `password` FROM `mysql`.`user`' ) ;
return rows . map ( row => {
return {
name : row . user ,
host : row . host ,
password : row . password
} ;
} ) ;
}
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 ) {
2020-12-31 19:55:02 +01:00
const sql = ` DROP VIEW \` ${ 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 ;
2020-12-29 10:35:46 +01:00
let sql = ` ALTER ALGORITHM = ${ view . algorithm } ${ view . definer ? ` DEFINER= ${ view . definer } ` : '' } SQL SECURITY ${ view . security } VIEW \` ${ 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 )
sql += ` ; RENAME TABLE \` ${ view . oldName } \` TO \` ${ view . name } \` ` ;
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 ) {
const sql = ` CREATE ALGORITHM = ${ view . algorithm } ${ view . definer ? ` DEFINER= ${ view . definer } ` : '' } SQL SECURITY ${ view . security } VIEW \` ${ view . name } \` AS ${ view . sql } ${ view . updateOption ? ` WITH ${ view . updateOption } CHECK OPTION ` : '' } ` ;
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 ] ,
sql : row [ 'SQL Original Statement' ] . match ( / B E G I N ( . * ) E N D / g s ) [ 0 ] ,
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 ) {
const sql = ` DROP TRIGGER \` ${ params . trigger } \` ` ;
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 ) {
const sql = ` CREATE ${ trigger . definer ? ` DEFINER= ${ trigger . definer } ` : '' } TRIGGER \` ${ trigger . name } \` ${ trigger . event1 } ${ trigger . event2 } ON \` ${ trigger . table } \` FOR EACH ROW ${ trigger . sql } ` ;
return await this . raw ( sql ) ;
}
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
} ;
} ) ;
}
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 ;
const sql = ` CREATE TABLE \` ${ name } \` ( \` ${ name } _ID \` INT NULL) COMMENT=' ${ comment } ', COLLATE=' ${ collation } ', ENGINE= ${ engine } ` ;
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 ;
let sql = ` ALTER TABLE \` ${ table } \` ` ;
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 => {
const length = addition . numLength || addition . charLength || addition . datePrecision ;
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' : '' }
$ { 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' } ` );
} ) ;
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 => {
const length = change . numLength || change . charLength || change . datePrecision ;
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' : '' }
$ { 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' } ` );
} ) ;
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
if ( options . name ) sql += ` ; RENAME TABLE \` ${ table } \` TO \` ${ options . name } \` ` ;
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 ) {
const sql = ` TRUNCATE TABLE \` ${ params . table } \` ` ;
return await this . raw ( sql ) ;
}
/ * *
* DROP TABLE
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
async dropTable ( params ) {
2020-12-31 19:55:02 +01:00
const sql = ` DROP TABLE \` ${ 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 = '' ;
if ( Object . keys ( this . _query . insert ) . length ) {
const fieldsList = [ ] ;
const valueList = [ ] ;
const fields = this . _query . insert ;
for ( const key in fields ) {
if ( fields [ key ] === null ) continue ;
fieldsList . push ( key ) ;
valueList . push ( fields [ key ] ) ;
}
insertRaw = ` ( ${ fieldsList . join ( ', ' ) } ) VALUES ( ${ valueList . join ( ', ' ) } ) ` ;
}
// 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 ( ', ' ) } ` : '' ;
return ` ${ selectRaw } ${ updateRaw ? 'UPDATE' : '' } ${ insertRaw ? 'INSERT ' : '' } ${ this . _query . delete ? 'DELETE ' : '' } ${ fromRaw } ${ updateRaw } ${ whereRaw } ${ groupByRaw } ${ orderByRaw } ${ limitRaw } ${ insertRaw } ` ;
}
/ * *
* @ 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
2020-09-17 17:58:12 +02:00
* @ returns { Promise }
* @ memberof MySQLClient
* /
2020-10-27 16:41:00 +01:00
async raw ( sql , args ) {
args = {
nest : false ,
details : false ,
... args
} ;
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 = [ ] ;
let selectedFields = [ ] ;
2020-09-17 17:58:12 +02:00
const queries = sql . split ( ';' ) ;
if ( process . env . NODE _ENV === 'development' ) this . _logger ( sql ) ; // TODO: replace BLOB content with a placeholder
for ( const query of queries ) {
if ( ! query ) continue ;
2020-10-27 16:41:00 +01:00
let fieldsArr = [ ] ;
let keysArr = [ ] ;
const { rows , report , fields , keys } = await new Promise ( ( resolve , reject ) => {
this . _connection . query ( { sql : query , nestTables } , async ( err , response , fields ) => {
const queryResult = response ;
2020-09-17 17:58:12 +02:00
if ( err )
reject ( err ) ;
else {
2020-11-13 12:39:40 +01:00
const remappedFields = fields
? fields . map ( field => {
return {
name : field . name ,
orgName : field . orgName ,
schema : field . db ,
table : field . table ,
orgTable : field . orgTable ,
2020-12-28 17:46:23 +01:00
type : 'VARCHAR'
2020-11-13 12:39:40 +01:00
} ;
} )
: [ ] ;
2020-10-09 22:44:05 +02:00
2020-10-27 16:41:00 +01:00
if ( args . details ) {
let cachedTable ;
if ( remappedFields . length ) {
selectedFields = remappedFields . map ( field => {
return {
name : field . orgName || field . name ,
table : field . orgTable || field . table
} ;
} ) ;
paramsArr = remappedFields . map ( field => {
if ( field . table ) cachedTable = field . table ; // Needed for some queries on information_schema
return {
table : field . orgTable || cachedTable ,
schema : field . schema || 'INFORMATION_SCHEMA'
} ;
} ) . filter ( ( val , i , arr ) => arr . findIndex ( el => el . schema === val . schema && el . table === val . table ) === i ) ;
for ( const paramObj of paramsArr ) {
try { // Table data
const response = await this . getTableColumns ( paramObj ) ;
2020-11-13 12:39:40 +01:00
let detailedFields = response . length
? selectedFields . map ( selField => {
2020-12-28 17:46:23 +01:00
return response . find ( field => field . name . toLowerCase ( ) === selField . name . toLowerCase ( ) && field . table === selField . table ) ;
2020-11-13 12:39:40 +01:00
} ) . filter ( el => ! ! el )
: [ ] ;
2020-10-27 16:41:00 +01:00
if ( selectedFields . length ) {
detailedFields = detailedFields . map ( field => {
2020-12-28 17:46:23 +01:00
const aliasObj = remappedFields . find ( resField => resField . orgName === field . name && resField . orgTable === field . table ) ;
2020-10-27 16:41:00 +01:00
return {
... field ,
alias : aliasObj . name || field . name ,
tableAlias : aliasObj . table || field . table
} ;
} ) ;
}
if ( ! detailedFields . length ) {
detailedFields = remappedFields . map ( field => {
2020-12-28 17:46:23 +01:00
const isInFields = fieldsArr . some ( f => field . name . toLowerCase ( ) === f . name . toLowerCase ( ) && field . table === f . table ) ;
if ( ! isInFields ) {
return {
... field ,
alias : field . name ,
tableAlias : field . table
} ;
}
else
return false ;
} ) . filter ( Boolean ) ;
2020-10-27 16:41:00 +01:00
}
fieldsArr = fieldsArr ? [ ... fieldsArr , ... detailedFields ] : detailedFields ;
}
catch ( err ) {
reject ( err ) ;
}
try { // Key usage (foreign keys)
const response = await this . getKeyUsage ( paramObj ) ;
keysArr = keysArr ? [ ... keysArr , ... response ] : response ;
}
catch ( err ) {
reject ( err ) ;
}
}
}
}
2020-09-17 17:58:12 +02:00
resolve ( {
2020-10-27 16:41:00 +01:00
rows : Array . isArray ( queryResult ) ? queryResult : false ,
report : ! Array . isArray ( queryResult ) ? queryResult : false ,
fields : fieldsArr . length ? fieldsArr : remappedFields ,
keys : keysArr
2020-09-17 17:58:12 +02:00
} ) ;
}
} ) ;
} ) ;
2020-10-27 16:41:00 +01:00
resultsArr . push ( { rows , report , fields , keys } ) ;
2020-09-17 17:58:12 +02:00
}
return resultsArr . length === 1 ? resultsArr [ 0 ] : resultsArr ;
}
}