2022-04-12 17:08:05 +02:00
import * as antares from 'common/interfaces/antares' ;
2022-04-12 17:59:22 +02:00
import * as mysql from 'mysql2/promise' ;
2020-09-17 17:58:12 +02:00
import { AntaresCore } from '../AntaresCore' ;
2022-05-10 13:22:26 +02:00
import dataTypes from 'common/data-types/mysql' ;
import SSH2Promise = require ( 'ssh2-promise' ) ;
2022-04-12 17:08:05 +02:00
import SSHConfig from 'ssh2-promise/lib/sshConfig' ;
2020-09-17 17:58:12 +02:00
export class MySQLClient extends AntaresCore {
2022-04-12 17:08:05 +02:00
private _schema? : string ;
private _runningConnections : Map < string , number > ;
2022-04-14 09:15:16 +02:00
private _connectionsToCommit : Map < string , mysql.Connection | mysql.PoolConnection > ;
2022-04-15 23:13:23 +02:00
_connection? : mysql.Connection | mysql . Pool ;
_params : mysql.ConnectionOptions & { schema : string ; ssl? : mysql.SslOptions ; ssh? : SSHConfig ; readonly : boolean } ;
2022-04-12 17:08:05 +02:00
private types : { [ key : number ] : string } = {
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'
}
constructor ( args : antares.ClientParams ) {
2021-01-21 18:14:37 +01:00
super ( args ) ;
2021-04-09 19:31:41 +02:00
this . _schema = null ;
2022-02-16 09:14:46 +01:00
this . _runningConnections = new Map ( ) ;
this . _connectionsToCommit = new Map ( ) ;
2021-01-21 18:14:37 +01:00
}
2022-04-12 17:08:05 +02:00
private _getType ( field : mysql.FieldPacket & { columnType? : number ; columnLength? : number } ) {
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 } ;
}
2022-05-07 09:46:07 +02:00
getTypeInfo ( type : string ) : antares . TypeInformations {
2021-01-21 18:14:37 +01:00
return dataTypes
. reduce ( ( acc , group ) = > [ . . . acc , . . . group . types ] , [ ] )
2022-04-12 17:08:05 +02:00
. filter ( ( _type ) = > _type . name === type . toUpperCase ( ) ) [ 0 ] ;
2021-01-21 18:14:37 +01:00
}
2022-04-12 17:08:05 +02:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected _reducer ( acc : string [ ] , curr : any ) {
2022-02-19 12:40:54 +01:00
const type = typeof curr ;
switch ( type ) {
case 'number' :
case 'string' :
return [ . . . acc , curr ] ;
case 'object' :
if ( Array . isArray ( curr ) )
return [ . . . acc , . . . curr ] ;
else {
const clausoles = [ ] ;
for ( const key in curr )
clausoles . push ( ` \` ${ key } \` ${ curr [ key ] } ` ) ;
return clausoles ;
}
}
}
2022-04-12 17:08:05 +02:00
async getDbConfig ( ) : Promise < mysql.ConnectionOptions > {
2021-07-05 09:30:52 +02:00
const dbConfig = {
host : this._params.host ,
port : this._params.port ,
user : this._params.user ,
password : this._params.password ,
2022-04-12 17:08:05 +02:00
database : undefined as string | undefined ,
ssl : null as mysql . SslOptions ,
2022-02-26 10:14:00 +01:00
supportBigNumbers : true ,
bigNumberStrings : true
2021-07-05 09:30:52 +02:00
} ;
2021-07-09 10:26:16 +02:00
if ( this . _params . schema ? . length ) dbConfig . database = this . _params . schema ;
2021-07-05 09:30:52 +02:00
2022-04-12 17:08:05 +02:00
if ( this . _params . ssl ) dbConfig . ssl = this . _params . ssl ;
2021-07-05 09:30:52 +02:00
if ( this . _params . ssh ) {
2021-10-07 14:58:31 +02:00
try {
this . _ssh = new SSH2Promise ( { . . . this . _params . ssh } ) ;
const tunnel = await this . _ssh . addTunnel ( {
remoteAddr : this._params.host ,
remotePort : this._params.port
} ) ;
2022-04-01 09:51:03 +02:00
2022-04-12 17:08:05 +02:00
dbConfig . host = ( this . _ssh . config as SSHConfig [ ] & { host : string } ) . host ;
2021-10-07 14:58:31 +02:00
dbConfig . port = tunnel . localPort ;
}
catch ( err ) {
if ( this . _ssh ) this . _ssh . close ( ) ;
throw err ;
}
2021-07-05 09:30:52 +02:00
}
2022-02-16 09:14:46 +01:00
return dbConfig ;
}
async connect ( ) {
2021-07-09 10:26:16 +02:00
if ( ! this . _poolSize )
2022-02-16 09:14:46 +01:00
this . _connection = await this . getConnection ( ) ;
else
this . _connection = await this . getConnectionPool ( ) ;
2020-09-17 17:58:12 +02:00
}
destroy ( ) {
this . _connection . end ( ) ;
2021-07-05 09:30:52 +02:00
if ( this . _ssh ) this . _ssh . close ( ) ;
2020-09-17 17:58:12 +02:00
}
2022-02-16 09:14:46 +01:00
async getConnection ( ) {
const dbConfig = await this . getDbConfig ( ) ;
const connection = await mysql . createConnection ( {
. . . dbConfig ,
typeCast : ( field , next ) = > {
if ( field . type === 'DATETIME' )
return field . string ( ) ;
else
return next ( ) ;
}
} ) ;
// ANSI_QUOTES check
2022-04-12 17:08:05 +02:00
const [ response ] = await connection . query < mysql.RowDataPacket [ ] > ( 'SHOW GLOBAL VARIABLES LIKE \'%sql_mode%\'' ) ;
2022-05-05 17:18:18 +02:00
const sqlMode = response [ 0 ] ? . Value ? . split ( ',' ) ;
2022-02-16 09:14:46 +01:00
const hasAnsiQuotes = sqlMode . includes ( 'ANSI_QUOTES' ) ;
if ( this . _params . readonly )
await connection . query ( 'SET SESSION TRANSACTION READ ONLY' ) ;
if ( hasAnsiQuotes )
2022-04-12 17:08:05 +02:00
await connection . query ( ` SET SESSION sql_mode = " ${ sqlMode . filter ( ( m : string ) = > m !== 'ANSI_QUOTES' ) . join ( ',' ) } " ` ) ;
2022-02-16 09:14:46 +01:00
return connection ;
}
async getConnectionPool ( ) {
const dbConfig = await this . getDbConfig ( ) ;
const connection = mysql . createPool ( {
. . . dbConfig ,
connectionLimit : this._poolSize ,
typeCast : ( field , next ) = > {
if ( field . type === 'DATETIME' )
return field . string ( ) ;
else
return next ( ) ;
}
} ) ;
// ANSI_QUOTES check
2022-04-12 17:08:05 +02:00
const [ res ] = await connection . query < mysql.RowDataPacket [ ] > ( 'SHOW GLOBAL VARIABLES LIKE \'%sql_mode%\'' ) ;
2022-05-05 17:18:18 +02:00
const sqlMode = res [ 0 ] ? . Value ? . split ( ',' ) ;
2022-02-16 09:14:46 +01:00
const hasAnsiQuotes = sqlMode . includes ( 'ANSI_QUOTES' ) ;
if ( hasAnsiQuotes )
2022-04-12 17:08:05 +02:00
await connection . query ( ` SET SESSION sql_mode = " ${ sqlMode . filter ( ( m : string ) = > m !== 'ANSI_QUOTES' ) . join ( ',' ) } " ` ) ;
2022-02-16 09:14:46 +01:00
connection . on ( 'connection' , conn = > {
if ( this . _params . readonly )
conn . query ( 'SET SESSION TRANSACTION READ ONLY' ) ;
if ( hasAnsiQuotes )
2022-04-12 17:08:05 +02:00
conn . query ( ` SET SESSION sql_mode = " ${ sqlMode . filter ( ( m : string ) = > m !== 'ANSI_QUOTES' ) . join ( ',' ) } " ` ) ;
2022-02-16 09:14:46 +01:00
} ) ;
return connection ;
}
2022-04-12 17:08:05 +02:00
use ( schema : string ) {
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
}
2022-04-12 17:08:05 +02:00
async getStructure ( schemas : Set < string > ) {
/* eslint-disable camelcase */
interface ShowTableResult {
Db? : string ;
Name : string ;
Engine : string ;
Version : number ;
Row_format : string ;
Rows : number ;
Avg_row_length : number ;
Data_length : number ;
Max_data_length : number ;
Index_length : number ;
Data_free : number ;
Auto_increment : number ;
Create_time : Date ;
Update_time : Date ;
Check_time? : number ;
Collation : string ;
Checksum? : number ;
Create_options : string ;
Comment : string ;
}
interface ShowTriggersResult {
Db? : string ;
Trigger : string ;
Event : string ;
Table : string ;
Statement : string ;
Timing : string ;
Created : Date ;
sql_mode : string ;
Definer : string ;
character_set_client : string ;
collation_connection : string ;
'Database Collation' : string ;
}
/* eslint-enable camelcase */
const { rows : databases } = await this . raw < antares.QueryResult < { Database : string } > > ( 'SHOW DATABASES' ) ;
2021-07-09 10:26:16 +02:00
let filteredDatabases = databases ;
if ( this . _params . schema )
filteredDatabases = filteredDatabases . filter ( db = > db . Database === this . _params . schema ) ;
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`' ) ;
2022-04-12 17:08:05 +02:00
const tablesArr : ShowTableResult [ ] = [ ] ;
const triggersArr : ShowTriggersResult [ ] = [ ] ;
2022-02-16 09:14:46 +01:00
let schemaSize = 0 ;
2020-12-28 13:05:30 +01:00
2021-07-09 10:26:16 +02:00
for ( const db of filteredDatabases ) {
2021-01-23 15:50:21 +01:00
if ( ! schemas . has ( db . Database ) ) continue ;
2022-04-12 17:08:05 +02:00
let { rows : tables } = await this . raw < antares.QueryResult < ShowTableResult > > ( ` SHOW TABLE STATUS FROM \` ${ db . Database } \` ` ) ;
2020-12-28 13:05:30 +01:00
if ( tables . length ) {
tables = tables . map ( table = > {
table . Db = db . Database ;
return table ;
} ) ;
tablesArr . push ( . . . tables ) ;
}
2022-04-12 17:08:05 +02:00
let { rows : triggers } = await this . raw < antares.QueryResult < ShowTriggersResult > > ( ` SHOW TRIGGERS FROM \` ${ db . Database } \` ` ) ;
2020-10-03 12:11:42 +02:00
if ( triggers . length ) {
triggers = triggers . map ( trigger = > {
trigger . Db = db . Database ;
return trigger ;
} ) ;
triggersArr . push ( . . . triggers ) ;
}
}
2021-07-09 10:26:16 +02:00
return filteredDatabases . map ( db = > {
2021-01-23 15:50:21 +01:00
if ( schemas . has ( db . Database ) ) {
// TABLES
2022-05-17 19:11:31 +02:00
const remappedTables : antares.TableInfos [ ] = tablesArr . filter ( table = > table . Db === db . Database ) . map ( table = > {
2021-01-23 15:50:21 +01:00
let tableType ;
switch ( table . Comment ) {
case 'VIEW' :
tableType = 'view' ;
break ;
default :
tableType = 'table' ;
break ;
}
2022-02-28 14:19:07 +01:00
const tableSize = Number ( table . Data_length ) + Number ( table . Index_length ) ;
2022-02-16 09:14:46 +01:00
schemaSize += tableSize ;
2021-01-23 15:50:21 +01:00
return {
name : table.Name ,
type : tableType ,
rows : table.Rows ,
created : table.Create_time ,
updated : table.Update_time ,
engine : table.Engine ,
comment : table.Comment ,
2022-02-16 09:14:46 +01:00
size : tableSize ,
2021-01-23 15:50:21 +01:00
autoIncrement : table.Auto_increment ,
collation : table.Collation
} ;
} ) ;
2020-10-12 18:45:15 +02:00
2021-01-23 15:50:21 +01:00
// PROCEDURES
2022-05-17 19:11:31 +02:00
const remappedProcedures : antares.RoutineInfos [ ] = procedures . filter ( procedure = > procedure . Db === db . Database ) . map ( procedure = > {
2021-01-23 15:50:21 +01:00
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
2022-05-17 19:11:31 +02:00
const remappedFunctions : antares.FunctionInfos [ ] = functions . filter ( func = > func . Db === db . Database ) . map ( func = > {
2021-01-23 15:50:21 +01:00
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
2022-05-17 19:11:31 +02:00
const remappedSchedulers : antares.EventInfos [ ] = schedulers . filter ( scheduler = > scheduler . Db === db . Database ) . map ( scheduler = > {
2021-01-23 15:50:21 +01:00
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 ,
2022-02-16 09:14:46 +01:00
enabled : scheduler.STATUS === 'ENABLED' ,
2021-01-23 15:50:21 +01:00
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
2022-05-17 19:11:31 +02:00
const remappedTriggers : antares.TriggerInfos [ ] = triggersArr . filter ( trigger = > trigger . Db === db . Database ) . map ( trigger = > {
2021-01-23 15:50:21 +01:00
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 ,
2022-02-16 09:14:46 +01:00
size : schemaSize ,
2021-01-23 15:50:21 +01:00
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 ,
2022-02-16 09:14:46 +01:00
size : 0 ,
2021-01-23 15:50:21 +01:00
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
} ) ;
}
2022-04-12 17:08:05 +02:00
async getTableColumns ( { schema , table } : { schema : string ; table : string } ) {
interface TableColumnsResult {
COLUMN_TYPE : string ;
2022-04-15 23:13:23 +02:00
NUMERIC_PRECISION : number ;
2022-04-12 17:08:05 +02:00
COLUMN_NAME : string ;
COLUMN_DEFAULT : string ;
COLUMN_KEY : string ;
DATA_TYPE : string ;
TABLE_SCHEMA : string ;
TABLE_NAME : string ;
2022-04-15 23:13:23 +02:00
NUMERIC_SCALE : number ;
DATETIME_PRECISION : number ;
CHARACTER_MAXIMUM_LENGTH : number ;
2022-04-12 17:08:05 +02:00
IS_NULLABLE : string ;
2022-04-15 23:13:23 +02:00
ORDINAL_POSITION : number ;
2022-04-12 17:08:05 +02:00
CHARACTER_SET_NAME : string ;
COLLATION_NAME : string ;
EXTRA : string ;
COLUMN_COMMENT : string ;
}
interface CreateTableResult {
'Create Table' ? : string ;
Table : string ;
}
2020-10-16 17:26:47 +02:00
const { rows } = await this
. select ( '*' )
. schema ( 'information_schema' )
. from ( 'COLUMNS' )
2021-07-13 09:09:25 +02:00
. where ( { TABLE_SCHEMA : ` = ' ${ schema } ' ` , TABLE_NAME : ` = ' ${ table } ' ` } )
2020-10-16 17:26:47 +02:00
. orderBy ( { ORDINAL_POSITION : 'ASC' } )
2022-04-12 17:08:05 +02:00
. run < TableColumnsResult > ( ) ;
2020-10-16 17:26:47 +02:00
2022-04-12 17:08:05 +02:00
const { rows : fields } = await this . raw < antares.QueryResult < CreateTableResult > > ( ` SHOW CREATE TABLE \` ${ schema } \` . \` ${ table } \` ` ) ;
2021-05-22 16:24:19 +02:00
const remappedFields = fields . map ( row = > {
2021-06-03 10:54:59 +02:00
if ( ! row [ 'Create Table' ] ) return false ;
2021-05-22 16:24:19 +02:00
let n = 0 ;
return row [ 'Create Table' ]
. split ( '' )
2022-04-12 17:08:05 +02:00
. reduce ( ( acc : string , curr : string ) = > {
2021-05-22 16:24:19 +02:00
if ( curr === ')' ) n -- ;
if ( n !== 0 ) acc += curr ;
if ( curr === '(' ) n ++ ;
return acc ;
} , '' )
. replaceAll ( '\n' , '' )
2022-02-16 09:14:46 +01:00
. split ( /,\s?(?![^(]*\))/ )
2022-04-12 17:08:05 +02:00
. map ( ( f : string ) = > {
2021-05-23 11:12:09 +02:00
try {
const fieldArr = f . trim ( ) . split ( ' ' ) ;
const nameAndType = fieldArr . slice ( 0 , 2 ) ;
2022-04-12 17:08:05 +02:00
if ( nameAndType [ 0 ] . charAt ( 0 ) !== '`' ) return null ;
2021-05-23 11:12:09 +02:00
const details = fieldArr . slice ( 2 ) . join ( ' ' ) ;
2021-05-26 17:44:33 +02:00
let defaultValue = null ;
2021-07-20 16:59:59 +02:00
if ( details . includes ( 'DEFAULT' ) )
2021-05-26 17:44:33 +02:00
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 ) {
2022-04-12 17:08:05 +02:00
return null ;
2021-05-23 11:12:09 +02:00
}
2021-05-22 16:24:19 +02:00
} )
. filter ( Boolean )
2022-04-12 17:08:05 +02:00
. reduce ( ( acc : { [ key : string ] : { name : string ; type : string ; length : string ; default : string } } , curr ) = > {
2021-05-22 16:24:19 +02:00
acc [ curr . name ] = curr ;
return acc ;
} , { } ) ;
} ) [ 0 ] ;
2021-05-20 12:38:05 +02:00
2022-04-12 17:08:05 +02:00
return rows . map ( ( field ) = > {
const numLengthMatch = field . COLUMN_TYPE . match ( /int\(([^)]+)\)/ ) ;
const numLength = numLengthMatch ? + numLengthMatch . pop ( ) : field . NUMERIC_PRECISION || 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
2022-02-16 09:14:46 +01:00
const defaultValue = ( remappedFields && remappedFields [ field . COLUMN_NAME ] )
? remappedFields [ field . COLUMN_NAME ] . default
: field . COLUMN_DEFAULT ;
2020-10-16 17:26:47 +02:00
return {
name : field.COLUMN_NAME ,
key : field.COLUMN_KEY.toLowerCase ( ) ,
2022-02-16 09:14:46 +01:00
type : ( remappedFields && remappedFields [ field . COLUMN_NAME ] )
? remappedFields [ field . COLUMN_NAME ] . 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 ,
2022-03-17 18:17:59 +01:00
numScale : Number ( field . NUMERIC_SCALE ) ,
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 ,
2022-02-16 09:14:46 +01:00
default : defaultValue ,
2020-10-16 17:26:47 +02:00
charset : field.CHARACTER_SET_NAME ,
collation : field.COLLATION_NAME ,
autoIncrement : field.EXTRA.includes ( 'auto_increment' ) ,
2022-02-19 12:40:54 +01:00
generated : field.EXTRA.toLowerCase ( ) . includes ( 'generated' ) ,
2022-02-16 09:14:46 +01:00
onUpdate : field.EXTRA.toLowerCase ( ) . includes ( 'on update' )
? field . EXTRA . substr ( field . EXTRA . indexOf ( 'on update' ) + 9 , field . EXTRA . length ) . trim ( )
: '' ,
2020-10-16 17:26:47 +02:00
comment : field.COLUMN_COMMENT
} ;
} ) ;
}
2022-04-14 09:15:16 +02:00
async getTableApproximateCount ( { schema , table } : { schema : string ; table : string } ) : Promise < number > {
2021-08-04 15:52:26 +02:00
const { rows } = await this . raw ( ` SELECT table_rows "count" FROM information_schema.tables WHERE table_name = " ${ table } " AND table_schema = " ${ schema } " ` ) ;
return rows . length ? rows [ 0 ] . count : 0 ;
}
2022-04-12 17:08:05 +02:00
async getTableOptions ( { schema , table } : { schema : string ; table : string } ) {
2022-04-14 09:15:16 +02:00
/* eslint-disable camelcase */
interface TableOptionsResult {
Name : string ;
Rows : string ;
Create_time : string ;
Update_time : string ;
Engine : string ;
Data_length : number ;
Index_length : number ;
Auto_increment : string ;
Collation : string ;
Comment : string ;
}
/* eslint-enable camelcase */
const { rows } = await this . raw < antares.QueryResult < TableOptionsResult > > ( ` SHOW TABLE STATUS FROM \` ${ schema } \` WHERE Name = ' ${ table } ' ` ) ;
2021-08-11 16:16:58 +02:00
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
} ;
2022-04-12 17:08:05 +02:00
}
2021-08-11 16:16:58 +02:00
return { } ;
}
2022-04-12 17:08:05 +02:00
async getTableIndexes ( { schema , table } : { schema : string ; table : string } ) {
2022-04-14 09:15:16 +02:00
/* eslint-disable camelcase */
interface ShowIntexesResult {
Non_unique : number ;
Column_name : string ;
Index_type : string ;
Key_name : string ;
Cardinality : number ;
Comment : string ;
Index_comment : string ;
}
/* eslint-enable camelcase */
const { rows } = await this . raw < antares.QueryResult < ShowIntexesResult > > ( ` SHOW INDEXES FROM \` ${ table } \` FROM \` ${ schema } \` ` ) ;
2020-11-20 17:24:02 +01:00
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
} ;
} ) ;
}
2022-04-12 17:08:05 +02:00
async getKeyUsage ( { schema , table } : { schema : string ; table : string } ) {
interface KeyResult {
TABLE_SCHEMA : string ;
TABLE_NAME : string ;
COLUMN_NAME : string ;
ORDINAL_POSITION : number ;
POSITION_IN_UNIQUE_CONSTRAINT : number ;
CONSTRAINT_NAME : string ;
REFERENCED_TABLE_SCHEMA : string ;
REFERENCED_TABLE_NAME : string ;
REFERENCED_COLUMN_NAME : string ;
}
interface KeyExtraResult {
CONSTRAINT_NAME : string ;
UPDATE_RULE : string ;
DELETE_RULE : string ;
}
2020-10-16 17:26:47 +02:00
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' } )
2022-04-12 17:08:05 +02:00
. run < KeyResult > ( ) ;
2020-10-16 17:26:47 +02:00
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' } )
2022-04-12 17:08:05 +02:00
. run < KeyExtraResult > ( ) ;
2020-12-15 17:08:36 +01:00
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
2022-04-12 17:08:05 +02:00
} as antares . QueryForeign ;
2020-10-16 17:26:47 +02:00
} ) ;
}
2020-12-29 10:35:46 +01:00
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
2022-04-14 09:15:16 +02:00
} as { name : string ; host : string ; password : string } ;
2020-12-29 10:35:46 +01:00
} ) ;
}
2022-04-12 17:08:05 +02:00
async createSchema ( params : { name : string ; collation : string } ) {
2021-03-16 18:42:03 +01:00
return await this . raw ( ` CREATE DATABASE \` ${ params . name } \` COLLATE ${ params . collation } ` ) ;
}
2022-04-12 17:08:05 +02:00
async alterSchema ( params : { name : string ; collation : string } ) {
2021-03-16 18:42:03 +01:00
return await this . raw ( ` ALTER DATABASE \` ${ params . name } \` COLLATE ${ params . collation } ` ) ;
}
2022-04-12 17:08:05 +02:00
async dropSchema ( params : { database : string } ) {
2021-03-16 18:42:03 +01:00
return await this . raw ( ` DROP DATABASE \` ${ params . database } \` ` ) ;
}
2022-04-12 17:08:05 +02:00
async getDatabaseCollation ( params : { database : string } ) {
2022-04-15 14:56:13 +02:00
let collation : string ;
const { rows : collaitons } = await this . raw < antares.QueryResult < { DEFAULT_COLLATION_NAME : string } > > ( ` SELECT \` DEFAULT_COLLATION_NAME \` FROM \` information_schema \` . \` SCHEMATA \` WHERE \` SCHEMA_NAME \` =' ${ params . database } ' ` ) ;
if ( collaitons . length )
collation = collaitons [ 0 ] . DEFAULT_COLLATION_NAME ;
return collation ;
2021-03-16 18:42:03 +01:00
}
2022-04-12 17:08:05 +02:00
async createTable ( params : antares.CreateTableParams ) {
const {
schema ,
fields ,
foreigns ,
indexes ,
options
} = params ;
const newColumns : string [ ] = [ ] ;
const newIndexes : string [ ] = [ ] ;
const newForeigns : string [ ] = [ ] ;
2020-12-26 14:47:15 +01:00
2022-04-12 17:08:05 +02:00
let sql = ` CREATE TABLE \` ${ schema } \` . \` ${ options . name } \` ` ;
2020-12-26 14:47:15 +01:00
2022-04-12 17:08:05 +02:00
// ADD FIELDS
fields . forEach ( field = > {
2022-05-07 09:46:07 +02:00
const typeInfo = this . getTypeInfo ( field . type ) ;
2022-04-12 17:08:05 +02:00
const length = typeInfo . length ? field . enumValues || field . numLength || field . charLength || field.datePrecision : false ;
2020-12-26 14:47:15 +01:00
2022-04-12 17:08:05 +02:00
newColumns . push ( ` \` ${ field . name } \`
$ { field . type . toUpperCase ( ) } $ { length ? ` ( ${ length } ${ field . numScale ? ` , ${ field . numScale } ` : '' } ) ` : '' }
$ { field . unsigned ? 'UNSIGNED' : '' }
$ { field . zerofill ? 'ZEROFILL' : '' }
$ { field . nullable ? 'NULL' : 'NOT NULL' }
$ { field . autoIncrement ? 'AUTO_INCREMENT' : '' }
2022-05-05 23:09:10 +02:00
$ { field . default !== null ? ` DEFAULT ${ field . default || '\'\'' } ` : '' }
2022-04-12 17:08:05 +02:00
$ { field . comment ? ` COMMENT ' ${ field . comment } ' ` : '' }
$ { field . collation ? ` COLLATE ${ field . collation } ` : '' }
$ { field . onUpdate ? ` ON UPDATE ${ field . onUpdate } ` : '' } ` );
} ) ;
2020-12-27 13:14:41 +01:00
2022-04-12 17:08:05 +02:00
// ADD INDEX
indexes . forEach ( index = > {
const fields = index . fields . map ( field = > ` \` ${ field } \` ` ) . join ( ',' ) ;
let type = index . type ;
2020-12-27 13:14:41 +01:00
2022-04-12 17:08:05 +02:00
if ( type === 'PRIMARY' )
newIndexes . push ( ` PRIMARY KEY ( ${ fields } ) ` ) ;
else {
if ( type === 'UNIQUE' )
type = 'UNIQUE INDEX' ;
2020-12-26 15:37:34 +01:00
2022-04-12 17:08:05 +02:00
newIndexes . push ( ` ${ type } \` ${ index . name } \` ( ${ fields } ) ` ) ;
}
} ) ;
2020-12-27 16:16:48 +01:00
2022-04-12 17:08:05 +02:00
// 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 } ` ) ;
} ) ;
2020-12-31 19:55:02 +01:00
2022-04-12 17:08:05 +02:00
sql = ` ${ sql } ( ${ [ . . . newColumns , . . . newIndexes , . . . newForeigns ] . join ( ', ' ) } ) COMMENT=' ${ options . comment } ', COLLATE=' ${ options . collation } ', ENGINE= ${ options . engine } ` ;
2020-12-31 19:55:02 +01:00
return await this . raw ( sql ) ;
}
2022-04-12 17:08:05 +02:00
async alterTable ( params : antares.AlterTableParams ) {
const {
table ,
schema ,
additions ,
deletions ,
changes ,
indexChanges ,
foreignChanges ,
options
} = params ;
2020-12-31 19:55:02 +01:00
2022-04-12 17:08:05 +02:00
let sql = ` ALTER TABLE \` ${ schema } \` . \` ${ table } \` ` ;
2022-04-14 09:15:16 +02:00
const alterColumns : string [ ] = [ ] ;
2020-12-31 19:55:02 +01:00
2022-04-12 17:08:05 +02: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 } ' ` ) ;
2021-01-05 17:25:18 +01:00
2022-04-12 17:08:05 +02:00
// ADD FIELDS
additions . forEach ( addition = > {
2022-05-07 09:46:07 +02:00
const typeInfo = this . getTypeInfo ( addition . type ) ;
2022-04-12 17:08:05 +02:00
const length = typeInfo . length ? addition . enumValues || addition . numLength || addition . charLength || addition.datePrecision : false ;
2021-01-05 17:25:18 +01:00
2022-04-12 17:08:05 +02:00
alterColumns . push ( ` ADD COLUMN \` ${ addition . name } \`
$ { addition . type . toUpperCase ( ) } $ { length ? ` ( ${ length } ${ addition . numScale ? ` , ${ addition . numScale } ` : '' } ) ` : '' }
$ { addition . unsigned ? 'UNSIGNED' : '' }
$ { addition . zerofill ? 'ZEROFILL' : '' }
$ { addition . nullable ? 'NULL' : 'NOT NULL' }
$ { addition . autoIncrement ? 'AUTO_INCREMENT' : '' }
2022-05-05 23:09:10 +02:00
$ { addition . default !== null ? ` DEFAULT ${ addition . default || '\'\'' } ` : '' }
2022-04-12 17:08:05 +02:00
$ { addition . comment ? ` COMMENT ' ${ addition . comment } ' ` : '' }
$ { addition . collation ? ` COLLATE ${ addition . collation } ` : '' }
$ { addition . onUpdate ? ` ON UPDATE ${ addition . onUpdate } ` : '' }
$ { addition . after ? ` AFTER \` ${ addition . after } \` ` : 'FIRST' } ` );
} ) ;
2021-01-11 18:56:51 +01:00
2022-04-12 17:08:05 +02:00
// ADD INDEX
indexChanges . additions . forEach ( addition = > {
const fields = addition . fields . map ( field = > ` \` ${ field } \` ` ) . join ( ',' ) ;
let type = addition . type ;
2021-04-14 18:06:20 +02:00
2022-04-12 17:08:05 +02:00
if ( type === 'PRIMARY' )
alterColumns . push ( ` ADD PRIMARY KEY ( ${ fields } ) ` ) ;
else {
if ( type === 'UNIQUE' )
type = 'UNIQUE INDEX' ;
2021-04-14 18:06:20 +02:00
2022-04-12 17:08:05 +02:00
alterColumns . push ( ` ADD ${ type } \` ${ addition . name } \` ( ${ fields } ) ` ) ;
}
} ) ;
2021-01-05 17:25:18 +01:00
2022-04-12 17:08:05 +02: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 } ` ) ;
} ) ;
// CHANGE FIELDS
changes . forEach ( change = > {
2022-05-07 09:46:07 +02:00
const typeInfo = this . getTypeInfo ( change . type ) ;
2022-04-12 17:08:05 +02:00
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 . numScale ? ` , ${ change . numScale } ` : '' } ) ` : '' }
$ { change . unsigned ? 'UNSIGNED' : '' }
$ { change . zerofill ? 'ZEROFILL' : '' }
$ { change . nullable ? 'NULL' : 'NOT NULL' }
$ { change . autoIncrement ? 'AUTO_INCREMENT' : '' }
2022-05-05 23:09:10 +02:00
$ { change . default !== null ? ` DEFAULT ${ change . default || '\'\'' } ` : '' }
2022-04-12 17:08:05 +02:00
$ { 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 ) ;
}
async duplicateTable ( params : { schema : string ; table : string } ) {
const sql = ` CREATE TABLE \` ${ params . schema } \` . \` ${ params . table } _copy \` LIKE \` ${ params . schema } \` . \` ${ params . table } \` ` ;
return await this . raw ( sql ) ;
}
async truncateTable ( params : { schema : string ; table : string } ) {
const sql = ` TRUNCATE TABLE \` ${ params . schema } \` . \` ${ params . table } \` ` ;
return await this . raw ( sql ) ;
}
async dropTable ( params : { schema : string ; table : string } ) {
const sql = ` DROP TABLE \` ${ params . schema } \` . \` ${ params . table } \` ` ;
return await this . raw ( sql ) ;
}
async getViewInformations ( { schema , view } : { schema : string ; view : string } ) {
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 ] ;
}
async dropView ( params : { schema : string ; view : string } ) {
const sql = ` DROP VIEW \` ${ params . schema } \` . \` ${ params . view } \` ` ;
return await this . raw ( sql ) ;
}
2022-04-14 09:15:16 +02:00
async alterView ( { view } : { view : antares.AlterViewParams } ) {
2022-04-12 17:08:05 +02:00
let sql = `
2022-04-14 09:15:16 +02:00
USE \ ` ${ view . schema } \` ;
ALTER ALGORITHM = $ { view . algorithm } $ { view . definer ? ` DEFINER= ${ view . definer } ` : '' }
SQL SECURITY $ { view . security }
params \ ` ${ view . schema } \` . \` ${ view . oldName } \` AS ${ view . sql } ${ view . updateOption ? ` WITH ${ view . updateOption } CHECK OPTION ` : '' }
2022-04-12 17:08:05 +02:00
` ;
2022-04-14 09:15:16 +02:00
if ( view . name !== view . oldName )
sql += ` ; RENAME TABLE \` ${ view . schema } \` . \` ${ view . oldName } \` TO \` ${ view . schema } \` . \` ${ view . name } \` ` ;
2022-04-12 17:08:05 +02:00
return await this . raw ( sql ) ;
}
async createView ( params : antares.CreateViewParams ) {
const sql = ` CREATE ALGORITHM = ${ params . algorithm } ${ params . definer ? ` DEFINER= ${ params . definer } ` : '' } SQL SECURITY ${ params . security } VIEW \` ${ params . schema } \` . \` ${ params . name } \` AS ${ params . sql } ${ params . updateOption ? ` WITH ${ params . updateOption } CHECK OPTION ` : '' } ` ;
return await this . raw ( sql ) ;
}
async getTriggerInformations ( { schema , trigger } : { schema : string ; trigger : string } ) {
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 | b e g i n ) ( . * ) ( E N D | e n d ) / g s ) [ 0 ] ,
name : row.Trigger ,
table : row [ 'SQL Original Statement' ] . match ( / ( ? < = O N ` ) . * ? ( ? = ` ) / g s ) [ 0 ] ,
activation : row [ 'SQL Original Statement' ] . match ( / ( B E F O R E | A F T E R ) / g s ) [ 0 ] ,
event : 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 ] ;
}
async dropTrigger ( params : { schema : string ; trigger : string } ) {
const sql = ` DROP TRIGGER \` ${ params . schema } \` . \` ${ params . trigger } \` ` ;
return await this . raw ( sql ) ;
}
async alterTrigger ( { trigger } : { trigger : antares.AlterTriggerParams } ) {
const tempTrigger = Object . assign ( { } , trigger ) ;
tempTrigger . name = ` Antares_ ${ tempTrigger . name } _tmp ` ;
try {
await this . createTrigger ( tempTrigger ) ;
await this . dropTrigger ( { schema : trigger.schema , trigger : tempTrigger.name } ) ;
await this . dropTrigger ( { schema : trigger.schema , trigger : trigger.oldName } ) ;
await this . createTrigger ( trigger ) ;
}
catch ( err ) {
return Promise . reject ( err ) ;
}
}
async createTrigger ( params : antares.CreateTriggerParams ) {
const sql = ` CREATE ${ params . definer ? ` DEFINER= ${ params . definer } ` : '' } TRIGGER \` ${ params . schema } \` . \` ${ params . name } \` ${ params . activation } ${ params . event } ON \` ${ params . table } \` FOR EACH ROW ${ params . sql } ` ;
return await this . raw ( sql , { split : false } ) ;
}
async getRoutineInformations ( { schema , routine } : { schema : string ; routine : string } ) {
interface CreateProcedureResult {
'Create Procedure' ? : string ;
Procedure : string ;
}
interface ProcedureParamsResult {
PARAMETER_NAME : string ;
DATA_TYPE : string ;
NUMERIC_PRECISION : string ;
DATETIME_PRECISION : string ;
CHARACTER_MAXIMUM_LENGTH : string ;
PARAMETER_MODE : string ;
}
const results = await this . raw < antares.QueryResult < CreateProcedureResult > > ( ` SHOW CREATE PROCEDURE \` ${ schema } \` . \` ${ routine } \` ` ) ;
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 < antares.QueryResult < ProcedureParamsResult > > ( 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' ;
2021-01-05 17:25:18 +01:00
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 ] ;
}
2022-04-12 17:08:05 +02:00
async dropRoutine ( params : { schema : string ; routine : string } ) {
2021-07-21 14:40:29 +02:00
const sql = ` DROP PROCEDURE \` ${ params . schema } \` . \` ${ params . routine } \` ` ;
2020-12-31 19:55:02 +01:00
return await this . raw ( sql ) ;
}
2022-04-12 17:08:05 +02:00
async alterRoutine ( { routine } : { routine : antares.AlterRoutineParams } ) {
2021-01-05 17:25:18 +01:00
const tempProcedure = Object . assign ( { } , routine ) ;
tempProcedure . name = ` Antares_ ${ tempProcedure . name } _tmp ` ;
try {
await this . createRoutine ( tempProcedure ) ;
2021-07-21 14:40:29 +02:00
await this . dropRoutine ( { schema : routine.schema , routine : tempProcedure.name } ) ;
await this . dropRoutine ( { schema : routine.schema , routine : routine.oldName } ) ;
2021-01-05 17:25:18 +01:00
await this . createRoutine ( routine ) ;
}
catch ( err ) {
return Promise . reject ( err ) ;
}
}
2022-04-12 17:08:05 +02:00
async createRoutine ( params : antares.CreateRoutineParams ) {
2021-07-21 14:40:29 +02:00
const parameters = 'parameters' in params
2022-04-12 17:08:05 +02:00
? params . parameters . reduce ( ( acc : string [ ] , curr ) = > {
2021-02-25 17:43:23 +01:00
acc . push ( ` ${ curr . context } \` ${ curr . name } \` ${ curr . type } ${ curr . length ? ` ( ${ curr . length } ) ` : '' } ` ) ;
return acc ;
} , [ ] ) . join ( ',' )
: '' ;
2021-01-05 17:25:18 +01:00
2021-07-21 14:40:29 +02:00
const sql = ` CREATE ${ params . definer ? ` DEFINER= ${ params . definer } ` : '' } PROCEDURE \` ${ params . schema } \` . \` ${ params . name } \` ( ${ parameters } )
2021-01-05 17:25:18 +01:00
LANGUAGE SQL
2021-07-21 14:40:29 +02:00
$ { params . deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC' }
$ { params . dataAccess }
SQL SECURITY $ { params . security }
COMMENT '${params.comment}'
$ { params . sql } ` ;
2021-01-05 17:25:18 +01:00
return await this . raw ( sql , { split : false } ) ;
}
2022-04-12 17:08:05 +02:00
async getFunctionInformations ( { schema , func } : { schema : string ; func : string } ) {
interface CreateFunctionResult {
'Create Function' ? : string ;
Function : string ;
}
interface FunctionParamsResult {
PARAMETER_NAME : string ;
DATA_TYPE : string ;
NUMERIC_PRECISION : string ;
DATETIME_PRECISION : string ;
CHARACTER_MAXIMUM_LENGTH : string ;
PARAMETER_MODE : string ;
}
const results = await this . raw < antares.QueryResult < CreateFunctionResult > > ( ` SHOW CREATE FUNCTION \` ${ schema } \` . \` ${ func } \` ` ) ;
2021-01-10 18:30:56 +01:00
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 : [ ] ,
2022-04-12 17:08:05 +02:00
name : row.Function ,
2021-01-11 18:56:51 +01:00
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
2022-04-12 17:08:05 +02:00
const results = await this . raw < antares.QueryResult < FunctionParamsResult > > ( sql ) ;
2021-04-14 18:06:20 +02:00
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 ] ;
}
2022-04-12 17:08:05 +02:00
async dropFunction ( params : { schema : string ; func : string } ) {
2021-07-21 14:40:29 +02:00
const sql = ` DROP FUNCTION \` ${ params . schema } \` . \` ${ params . func } \` ` ;
2021-01-10 18:30:56 +01:00
return await this . raw ( sql ) ;
}
2022-04-12 17:08:05 +02:00
async alterFunction ( { func } : { func : antares.AlterFunctionParams } ) {
2021-01-10 18:30:56 +01:00
const tempProcedure = Object . assign ( { } , func ) ;
tempProcedure . name = ` Antares_ ${ tempProcedure . name } _tmp ` ;
try {
await this . createFunction ( tempProcedure ) ;
2021-07-21 14:40:29 +02:00
await this . dropFunction ( { schema : func.schema , func : tempProcedure.name } ) ;
await this . dropFunction ( { schema : func.schema , func : func.oldName } ) ;
2021-01-10 18:30:56 +01:00
await this . createFunction ( func ) ;
}
catch ( err ) {
return Promise . reject ( err ) ;
}
}
2022-04-12 17:08:05 +02:00
async createFunction ( params : antares.CreateFunctionParams ) {
2021-09-07 18:20:45 +02:00
const parameters = 'parameters' in params
2022-04-12 17:08:05 +02:00
? params . parameters . reduce ( ( acc : string [ ] , curr ) = > {
2021-09-07 18:20:45 +02:00
acc . push ( ` \` ${ curr . name } \` ${ curr . type } ${ curr . length ? ` ( ${ curr . length } ) ` : '' } ` ) ;
return acc ;
} , [ ] ) . join ( ',' )
: '' ;
2021-01-10 18:30:56 +01:00
2021-07-21 14:40:29 +02:00
const body = params . returns ? params . sql : 'BEGIN\n RETURN 0;\nEND' ;
2021-04-14 18:06:20 +02:00
2021-07-21 14:40:29 +02:00
const sql = ` CREATE ${ params . definer ? ` DEFINER= ${ params . definer } ` : '' } FUNCTION \` ${ params . schema } \` . \` ${ params . name } \` ( ${ parameters } ) RETURNS ${ params . returns || 'SMALLINT' } ${ params . returnsLength ? ` ( ${ params . returnsLength } ) ` : '' }
2021-01-10 18:30:56 +01:00
LANGUAGE SQL
2021-07-21 14:40:29 +02:00
$ { params . deterministic ? 'DETERMINISTIC' : 'NOT DETERMINISTIC' }
$ { params . dataAccess }
SQL SECURITY $ { params . security }
COMMENT '${params.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 } ) ;
}
2022-04-12 17:08:05 +02:00
async getEventInformations ( { schema , scheduler } : { schema : string ; scheduler : string } ) {
interface CreateFunctionResult {
'Create Event' ? : string ;
Event : string ;
}
const results = await this . raw < antares.QueryResult < CreateFunctionResult > > ( ` SHOW CREATE EVENT \` ${ schema } \` . \` ${ scheduler } \` ` ) ;
2021-01-14 18:11:36 +01:00
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 ] ;
}
2022-04-12 17:08:05 +02:00
async dropEvent ( params : { schema : string ; scheduler : string } ) {
2021-07-21 14:40:29 +02:00
const sql = ` DROP EVENT \` ${ params . schema } \` . \` ${ params . scheduler } \` ` ;
2021-01-14 18:11:36 +01:00
return await this . raw ( sql ) ;
}
2022-04-12 17:08:05 +02:00
async alterEvent ( { scheduler } : { scheduler : antares.AlterEventParams } ) {
2021-01-15 19:18:16 +01:00
if ( scheduler . execution === 'EVERY' && scheduler . every [ 0 ] . includes ( '-' ) )
scheduler . every [ 0 ] = ` ' ${ scheduler . every [ 0 ] } ' ` ;
2021-07-21 14:40:29 +02:00
const sql = ` ALTER ${ scheduler . definer ? ` DEFINER= ${ scheduler . definer } ` : '' } EVENT \` ${ scheduler . 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-07-21 14:40:29 +02:00
$ { scheduler . name !== scheduler . oldName ? ` RENAME TO \` ${ scheduler . 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
}
2022-04-12 17:08:05 +02:00
async createEvent ( params : antares.CreateEventParams ) {
2021-07-21 14:40:29 +02:00
const sql = ` CREATE ${ params . definer ? ` DEFINER= ${ params . definer } ` : '' } EVENT \` ${ params . schema } \` . \` ${ params . name } \`
2021-01-16 11:32:42 +01:00
ON SCHEDULE
2021-07-21 14:40:29 +02:00
$ { 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 } ` ;
2021-01-14 18:11:36 +01:00
return await this . raw ( sql , { split : false } ) ;
}
2022-04-12 17:08:05 +02:00
async enableEvent ( { schema , scheduler } : { schema : string ; scheduler : string } ) {
2022-02-16 09:14:46 +01:00
const sql = ` ALTER EVENT \` ${ schema } \` . \` ${ scheduler } \` ENABLE ` ;
return await this . raw ( sql , { split : false } ) ;
}
2022-04-12 17:08:05 +02:00
async disableEvent ( { schema , scheduler } : { schema : string ; scheduler : string } ) {
2022-02-16 09:14:46 +01:00
const sql = ` ALTER EVENT \` ${ schema } \` . \` ${ scheduler } \` DISABLE ` ;
return await this . raw ( sql , { split : false } ) ;
}
2020-09-25 12:39:58 +02:00
async getCollations ( ) {
2022-04-12 17:08:05 +02:00
interface ShowCollationResult {
Charset : string ;
Collation : string ;
Compiled : string ;
Default : string ;
Id : number ;
Sortlen : number ;
}
2020-09-25 12:39:58 +02:00
2022-04-12 17:08:05 +02:00
const { rows } = await this . raw < antares.QueryResult < ShowCollationResult > > ( 'SHOW COLLATION' ) ;
return rows . map ( row = > {
2020-09-25 12:39:58 +02:00
return {
charset : row.Charset ,
collation : row.Collation ,
compiled : row.Compiled.includes ( 'Yes' ) ,
default : row . Default . includes ( 'Yes' ) ,
id : row.Id ,
sortLen : row.Sortlen
} ;
} ) ;
}
async getVariables ( ) {
2022-04-12 17:08:05 +02:00
interface ShowVariablesResult {
// eslint-disable-next-line camelcase
Variable_name : string ;
Value : string ;
}
2020-09-25 12:39:58 +02:00
2022-04-12 17:08:05 +02:00
const { rows } = await this . raw < antares.QueryResult < ShowVariablesResult > > ( 'SHOW VARIABLES' ) ;
return rows . map ( row = > {
2020-09-25 12:39:58 +02:00
return {
name : row.Variable_name ,
value : row.Value
} ;
} ) ;
2020-09-24 13:09:01 +02:00
}
2022-04-12 17:08:05 +02:00
async getVariable ( variable : string , level ? : 'global' | 'session' ) {
2022-02-16 09:14:46 +01:00
const sql = ` SHOW ${ level ? ' ' + level . toUpperCase ( ) : '' } VARIABLES LIKE '% ${ variable } %' ` ;
2022-04-12 17:08:05 +02:00
const { rows } = await this . raw ( sql ) ;
2022-02-16 09:14:46 +01:00
2022-04-12 17:08:05 +02:00
if ( rows . length ) {
2022-02-16 09:14:46 +01:00
return {
2022-04-12 17:08:05 +02:00
name : rows [ 0 ] . Variable_name ,
value : rows [ 0 ] . Value
2022-02-16 09:14:46 +01:00
} ;
}
}
2020-11-16 17:16:39 +01:00
async getEngines ( ) {
const sql = 'SHOW ENGINES' ;
2022-04-12 17:08:05 +02:00
const { rows } = await this . raw ( sql ) ;
2020-11-16 17:16:39 +01:00
2022-04-12 17:08:05 +02:00
return rows . map ( row = > {
2020-11-16 17:16:39 +01:00
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
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
} ;
} ) ;
}
2022-04-12 17:08:05 +02:00
async killProcess ( id : number ) {
2021-09-26 11:19:48 +02:00
return await this . raw ( ` KILL ${ id } ` ) ;
}
2022-04-12 17:08:05 +02:00
async killTabQuery ( tabUid : string ) {
2022-02-16 09:14:46 +01:00
const id = this . _runningConnections . get ( tabUid ) ;
if ( id )
return await this . killProcess ( id ) ;
}
2022-04-12 17:08:05 +02:00
async commitTab ( tabUid : string ) {
2022-02-16 09:14:46 +01:00
const connection = this . _connectionsToCommit . get ( tabUid ) ;
if ( connection )
2022-04-12 17:08:05 +02:00
await connection . query ( 'COMMIT' ) ;
2022-02-16 09:14:46 +01:00
}
2022-04-12 17:08:05 +02:00
async rollbackTab ( tabUid : string ) {
2022-02-16 09:14:46 +01:00
const connection = this . _connectionsToCommit . get ( tabUid ) ;
if ( connection )
2022-04-12 17:08:05 +02:00
await connection . query ( 'ROLLBACK' ) ;
2022-02-16 09:14:46 +01:00
}
2022-04-12 17:08:05 +02:00
destroyConnectionToCommit ( tabUid : string ) {
2022-02-16 09:14:46 +01:00
const connection = this . _connectionsToCommit . get ( tabUid ) ;
if ( connection ) {
2022-04-12 17:08:05 +02:00
( connection as mysql . Connection ) . destroy ( ) ;
2022-02-16 09:14:46 +01:00
this . _connectionsToCommit . delete ( tabUid ) ;
}
}
2020-09-17 17:58:12 +02:00
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 ) {
2022-02-19 12:40:54 +01:00
const fieldsList = Object . keys ( this . _query . insert [ 0 ] ) . map ( col = > '`' + col + '`' ) ;
2021-02-08 11:46:57 +01:00
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
2022-04-15 14:56:13 +02:00
const limitRaw = this . _query . limit ? ` LIMIT ${ this . _query . limit } ` : '' ;
2020-09-17 17:58:12 +02:00
2021-05-27 22:13:59 +02:00
// OFFSET
2022-04-15 14:56:13 +02:00
const offsetRaw = this . _query . offset ? ` OFFSET ${ this . _query . offset } ` : '' ;
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
}
2022-04-12 17:08:05 +02:00
async raw < T = antares.QueryResult > ( sql : string , args? : antares.QueryParams ) {
if ( process . env . NODE_ENV === 'development' ) this . _logger ( sql ) ;
2021-05-15 21:47:30 +02:00
2020-10-27 16:41:00 +01:00
args = {
nest : false ,
details : false ,
2021-01-05 17:25:18 +01:00
split : true ,
2021-07-21 14:40:29 +02:00
comments : true ,
2022-02-16 09:14:46 +01:00
autocommit : true ,
2020-10-27 16:41:00 +01:00
. . . args
} ;
2021-05-14 17:02:27 +02:00
2021-07-21 14:40:29 +02:00
if ( ! args . comments )
sql = sql . replace ( /(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm , '' ) ; // Remove comments
2020-10-27 16:41:00 +01:00
const nestTables = args . nest ? '.' : false ;
2022-04-12 17:08:05 +02:00
const resultsArr : antares.QueryResult [ ] = [ ] ;
2020-10-27 16:41:00 +01:00
let paramsArr = [ ] ;
2021-06-11 14:32:51 +02:00
const queries = args . split
? sql . split ( /((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm )
. filter ( Boolean )
. map ( q = > q . trim ( ) )
: [ sql ] ;
2022-02-16 09:14:46 +01:00
2022-04-14 09:15:16 +02:00
let connection : mysql.Connection | mysql . Pool | mysql . PoolConnection ;
2022-04-12 17:08:05 +02:00
const isPool = 'getConnection' in this . _connection ;
2022-02-16 09:14:46 +01:00
if ( ! args . autocommit && args . tabUid ) { // autocommit OFF
if ( this . _connectionsToCommit . has ( args . tabUid ) )
connection = this . _connectionsToCommit . get ( args . tabUid ) ;
else {
connection = await this . getConnection ( ) ;
await connection . query ( 'SET SESSION autocommit=0' ) ;
this . _connectionsToCommit . set ( args . tabUid , connection ) ;
}
}
else // autocommit ON
2022-04-12 17:08:05 +02:00
connection = isPool ? await ( this . _connection as mysql . Pool ) . getConnection ( ) : this . _connection ;
2022-02-16 09:14:46 +01:00
2022-04-12 17:08:05 +02:00
if ( args . tabUid && isPool ) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this . _runningConnections . set ( args . tabUid , ( connection as any ) . connection . connectionId ) ;
}
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 ( ) ;
2022-04-12 17:08:05 +02:00
let timeStop : Date ;
let keysArr : antares.QueryForeign [ ] = [ ] ;
2020-10-27 16:41:00 +01:00
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 ) )
2022-04-12 17:08:05 +02:00
return undefined ;
2021-05-15 21:47:30 +02:00
const type = this . _getType ( field ) ;
return {
name : field.orgName ,
alias : field.name ,
orgName : field.orgName ,
2022-04-12 17:08:05 +02:00
schema : args.schema || ( field as mysql . FieldPacket & { schema : string } ) . schema ,
2021-05-15 21:47:30 +02:00
table : field.table ,
tableAlias : field.table ,
orgTable : field.orgTable ,
type : type . name ,
length : type.length
} ;
} ) . filter ( Boolean )
: [ ] ;
if ( args . details ) {
2022-04-12 17:08:05 +02:00
let cachedTable : string ;
2021-05-15 21:47:30 +02:00
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 ) {
2022-02-16 09:14:46 +01:00
if ( isPool && args . autocommit ) {
2022-04-14 09:15:16 +02:00
( connection as mysql . PoolConnection ) . release ( ) ;
2022-02-16 09:14:46 +01:00
this . _runningConnections . delete ( args . tabUid ) ;
}
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 ) {
2022-02-16 09:14:46 +01:00
if ( isPool && args . autocommit ) {
2022-04-14 09:15:16 +02:00
( connection as mysql . PoolConnection ) . release ( ) ;
2022-02-16 09:14:46 +01:00
this . _runningConnections . delete ( args . tabUid ) ;
}
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 ( {
2022-04-12 17:08:05 +02:00
duration : timeStop.getTime ( ) - timeStart . getTime ( ) ,
2021-05-15 21:47:30 +02:00
rows : Array.isArray ( queryResult ) ? queryResult . some ( el = > Array . isArray ( el ) ) ? [ ] : queryResult : false ,
report : ! Array . isArray ( queryResult ) ? queryResult : false ,
fields : remappedFields ,
keys : keysArr
} ) ;
2021-06-11 15:02:45 +02:00
} ) . catch ( ( err ) = > {
2022-02-16 09:14:46 +01:00
if ( isPool && args . autocommit ) {
2022-04-14 09:15:16 +02:00
( connection as mysql . PoolConnection ) . release ( ) ;
2022-02-16 09:14:46 +01:00
this . _runningConnections . delete ( args . tabUid ) ;
}
2021-06-11 15:02:45 +02:00
reject ( err ) ;
} ) ;
2020-09-17 17:58:12 +02:00
} ) ;
2020-10-27 16:41:00 +01:00
2022-04-12 17:08:05 +02:00
resultsArr . push ( {
rows ,
report ,
fields ,
keys ,
duration
} ) ;
2020-09-17 17:58:12 +02:00
}
2022-02-16 09:14:46 +01:00
if ( isPool && args . autocommit ) {
2022-04-14 09:15:16 +02:00
( connection as mysql . PoolConnection ) . release ( ) ;
2022-02-16 09:14:46 +01:00
this . _runningConnections . delete ( args . tabUid ) ;
}
2021-05-15 21:47:30 +02:00
2022-04-12 17:08:05 +02:00
const result = resultsArr . length === 1 ? resultsArr [ 0 ] : resultsArr ;
return result as unknown as T ;
2020-09-17 17:58:12 +02:00
}
}