2021-03-16 18:42:03 +01:00
'use strict' ;
2021-03-25 18:33:29 +01:00
import { Pool , Client , types } from 'pg' ;
2021-03-19 18:49:26 +01:00
import { parse } from 'pgsql-ast-parser' ;
2021-03-16 18:42:03 +01:00
import { AntaresCore } from '../AntaresCore' ;
import dataTypes from 'common/data-types/postgresql' ;
2021-03-18 15:56:52 +01:00
function pgToString ( value ) {
return value . toString ( ) ;
}
2021-03-25 18:33:29 +01:00
types . setTypeParser ( 1082 , pgToString ) ; // date
types . setTypeParser ( 1083 , pgToString ) ; // time
types . setTypeParser ( 1114 , pgToString ) ; // timestamp
types . setTypeParser ( 1184 , pgToString ) ; // timestamptz
types . setTypeParser ( 1266 , pgToString ) ; // timetz
2021-03-18 15:56:52 +01:00
2021-03-16 18:42:03 +01:00
export class PostgreSQLClient extends AntaresCore {
constructor ( args ) {
super ( args ) ;
this . _schema = null ;
this . types = { } ;
for ( const key in types . builtins )
this . types [ types . builtins [ key ] ] = key ;
2021-04-09 19:31:41 +02:00
this . _arrayTypes = {
_int2 : 'SMALLINT' ,
_int4 : 'INTEGER' ,
_int8 : 'BIGINT' ,
_float4 : 'REAL' ,
_float8 : 'DOUBLE PRECISION' ,
_char : '"CHAR"' ,
_varchar : 'CHARACTER VARYING'
} ;
2021-03-16 18:42:03 +01:00
}
_getTypeInfo ( type ) {
return dataTypes
. reduce ( ( acc , group ) => [ ... acc , ... group . types ] , [ ] )
. filter ( _type => _type . name === type . toUpperCase ( ) ) [ 0 ] ;
}
2021-04-09 19:31:41 +02:00
_getArrayType ( type ) {
if ( Object . keys ( this . _arrayTypes ) . includes ( type ) )
return this . _arrayTypes [ type ] ;
return type . replace ( '_' , '' ) ;
}
2021-03-16 18:42:03 +01:00
/ * *
* @ memberof PostgreSQLClient
* /
async connect ( ) {
if ( ! this . _poolSize ) {
const client = new Client ( this . _params ) ;
2021-03-17 16:51:26 +01:00
await client . connect ( ) ;
this . _connection = client ;
2021-03-16 18:42:03 +01:00
}
else {
const pool = new Pool ( { ... this . _params , max : this . _poolSize } ) ;
this . _connection = pool ;
}
}
/ * *
* @ memberof PostgreSQLClient
* /
destroy ( ) {
this . _connection . end ( ) ;
}
/ * *
* Executes an USE query
*
* @ param { String } schema
* @ memberof PostgreSQLClient
* /
use ( schema ) {
this . _schema = schema ;
2021-04-19 19:15:06 +02:00
if ( schema )
return this . raw ( ` SET search_path TO ${ schema } ` ) ;
2021-03-16 18:42:03 +01:00
}
/ * *
* @ param { Array } schemas list
* @ returns { Array . < Object > } databases scructure
* @ memberof PostgreSQLClient
* /
async getStructure ( schemas ) {
const { rows : databases } = await this . raw ( 'SELECT schema_name AS database FROM information_schema.schemata ORDER BY schema_name' ) ;
const { rows : functions } = await this . raw ( 'SELECT * FROM information_schema.routines WHERE routine_type = \'FUNCTION\'' ) ;
const { rows : procedures } = await this . raw ( 'SELECT * FROM information_schema.routines WHERE routine_type = \'PROCEDURE\'' ) ;
const tablesArr = [ ] ;
const triggersArr = [ ] ;
for ( const db of databases ) {
if ( ! schemas . has ( db . database ) ) continue ;
let { rows : tables } = await this . raw ( `
SELECT * ,
pg _table _size ( QUOTE _IDENT ( t . TABLE _SCHEMA ) || '.' || QUOTE _IDENT ( t . TABLE _NAME ) ) : : bigint AS data _length ,
pg _relation _size ( QUOTE _IDENT ( t . TABLE _SCHEMA ) || '.' || QUOTE _IDENT ( t . TABLE _NAME ) ) : : bigint AS index _length ,
c . reltuples , obj _description ( c . oid ) AS comment
FROM "information_schema" . "tables" AS t
LEFT JOIN "pg_namespace" n ON t . table _schema = n . nspname
LEFT JOIN "pg_class" c ON n . oid = c . relnamespace AND c . relname = t . table _name
WHERE t . "table_schema" = '${db.database}'
ORDER BY table _name
` );
if ( tables . length ) {
tables = tables . map ( table => {
table . Db = db . database ;
return table ;
} ) ;
tablesArr . push ( ... tables ) ;
}
let { rows : triggers } = await this . raw ( `
SELECT event _object _schema AS table _schema ,
event _object _table AS table _name ,
trigger _schema ,
trigger _name ,
string _agg ( event _manipulation , ',' ) AS event ,
action _timing AS activation ,
action _condition AS condition ,
action _statement AS definition
FROM information _schema . triggers
WHERE trigger _schema = '${db.database}'
GROUP BY 1 , 2 , 3 , 4 , 6 , 7 , 8
ORDER BY table _schema ,
table _name
` );
if ( triggers . length ) {
triggers = triggers . map ( trigger => {
trigger . Db = db . database ;
return trigger ;
} ) ;
triggersArr . push ( ... triggers ) ;
}
}
return databases . map ( db => {
if ( schemas . has ( db . database ) ) {
// TABLES
const remappedTables = tablesArr . filter ( table => table . Db === db . database ) . map ( table => {
return {
name : table . table _name ,
type : table . table _type === 'VIEW' ? 'view' : 'table' ,
rows : table . reltuples ,
size : + table . data _length + + table . index _length ,
collation : table . Collation ,
comment : table . comment ,
engine : ''
} ;
} ) ;
// PROCEDURES
2021-04-09 19:31:41 +02:00
const remappedProcedures = procedures . filter ( procedure => procedure . routine _schema === db . database ) . map ( procedure => {
2021-03-16 18:42:03 +01:00
return {
2021-04-09 19:31:41 +02:00
name : procedure . routine _name ,
type : procedure . routine _type ,
security : procedure . security _type
2021-03-16 18:42:03 +01:00
} ;
} ) ;
// FUNCTIONS
2021-04-12 18:46:35 +02:00
const remappedFunctions = functions . filter ( func => func . routine _schema === db . database && func . data _type !== 'trigger' ) . map ( func => {
2021-03-16 18:42:03 +01:00
return {
name : func . routine _name ,
type : func . routine _type ,
security : func . security _type
} ;
} ) ;
2021-04-12 18:46:35 +02:00
// TRIGGER FUNCTIONS
const remappedTriggerFunctions = functions . filter ( func => func . routine _schema === db . database && func . data _type === 'trigger' ) . map ( func => {
return {
name : func . routine _name ,
type : func . routine _type ,
security : func . security _type
} ;
} ) ;
2021-03-16 18:42:03 +01:00
// TRIGGERS
const remappedTriggers = triggersArr . filter ( trigger => trigger . Db === db . database ) . map ( trigger => {
return {
2021-04-22 15:15:08 +02:00
name : ` ${ trigger . table _name } . ${ trigger . trigger _name } ` ,
orgName : trigger . trigger _name ,
2021-03-16 18:42:03 +01:00
timing : trigger . activation ,
2021-04-22 15:15:08 +02:00
definer : '' ,
definition : trigger . definition ,
2021-03-16 18:42:03 +01:00
event : trigger . event ,
2021-04-22 15:15:08 +02:00
table : trigger . table _name ,
sqlMode : ''
2021-03-16 18:42:03 +01:00
} ;
} ) ;
return {
name : db . database ,
tables : remappedTables ,
functions : remappedFunctions ,
procedures : remappedProcedures ,
triggers : remappedTriggers ,
2021-04-12 18:46:35 +02:00
triggerFunctions : remappedTriggerFunctions ,
2021-03-16 18:42:03 +01:00
schedulers : [ ]
} ;
}
else {
return {
name : db . database ,
tables : [ ] ,
functions : [ ] ,
procedures : [ ] ,
triggers : [ ] ,
schedulers : [ ]
} ;
}
} ) ;
}
/ * *
* @ param { Object } params
* @ param { String } params . schema
* @ param { String } params . table
* @ returns { Object } table scructure
* @ memberof PostgreSQLClient
* /
2021-04-09 19:31:41 +02:00
async getTableColumns ( { schema , table } , arrayRemap = true ) {
2021-03-16 18:42:03 +01:00
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 => {
2021-04-09 19:31:41 +02:00
let type = field . data _type ;
const isArray = type === 'ARRAY' ;
if ( isArray && arrayRemap )
type = this . _getArrayType ( field . udt _name ) ;
2021-03-16 18:42:03 +01:00
return {
name : field . column _name ,
key : null ,
2021-04-09 19:31:41 +02:00
type : type . toUpperCase ( ) ,
isArray ,
2021-03-16 18:42:03 +01:00
schema : field . table _schema ,
table : field . table _name ,
numPrecision : field . numeric _precision ,
datePrecision : field . datetime _precision ,
charLength : field . character _maximum _length ,
nullable : field . is _nullable . includes ( 'YES' ) ,
unsigned : null ,
zerofill : null ,
order : field . ordinal _position ,
default : field . column _default ,
charset : field . character _set _name ,
collation : field . collation _name ,
2021-03-25 18:33:29 +01:00
autoIncrement : false ,
2021-03-16 18:42:03 +01:00
onUpdate : null ,
comment : ''
} ;
} ) ;
}
/ * *
* @ param { Object } params
* @ param { String } params . schema
* @ param { String } params . table
* @ returns { Object } table indexes
* @ memberof PostgreSQLClient
* /
async getTableIndexes ( { schema , table } ) {
2021-04-06 12:48:40 +02:00
if ( schema !== 'public' )
2021-04-19 19:15:06 +02:00
await this . use ( schema ) ;
2021-04-06 12:48:40 +02:00
2021-03-16 18:42:03 +01:00
const { rows } = await this . raw ( ` WITH ndx_list AS (
SELECT pg _index . indexrelid , pg _class . oid
FROM pg _index , pg _class
WHERE pg _class . relname = '${table}' AND pg _class . oid = pg _index . indrelid ) , ndx _cols AS (
2021-03-29 20:18:44 +02:00
SELECT pg _class . relname , UNNEST ( i . indkey ) AS col _ndx , CASE i . indisprimary WHEN TRUE THEN 'PRIMARY' ELSE CASE i . indisunique WHEN TRUE THEN 'UNIQUE' ELSE 'INDEX' END END AS CONSTRAINT _TYPE , pg _class . oid
2021-03-16 18:42:03 +01:00
FROM pg _class
JOIN pg _index i ON ( pg _class . oid = i . indexrelid )
JOIN ndx _list ON ( pg _class . oid = ndx _list . indexrelid )
WHERE pg _table _is _visible ( pg _class . oid ) )
SELECT ndx _cols . relname AS CONSTRAINT _NAME , ndx _cols . CONSTRAINT _TYPE , a . attname AS COLUMN _NAME
FROM pg _attribute a
JOIN ndx _cols ON ( a . attnum = ndx _cols . col _ndx )
JOIN ndx _list ON ( ndx _list . oid = a . attrelid AND ndx _list . indexrelid = ndx _cols . oid )
` );
return rows . map ( row => {
return {
name : row . constraint _name ,
column : row . column _name ,
indexType : null ,
2021-03-25 18:33:29 +01:00
type : row . constraint _type ,
cardinality : null ,
comment : '' ,
indexComment : ''
2021-03-16 18:42:03 +01:00
} ;
} ) ;
}
2021-03-19 18:49:26 +01:00
/ * *
*
* @ param { Number } id
* @ returns { Array }
* /
async getTableByIDs ( ids ) {
2021-03-21 13:00:27 +01:00
if ( ! ids ) return ;
2021-03-19 18:49:26 +01:00
const { rows } = await this . raw ( `
SELECT relid AS tableid , relname , schemaname FROM pg _statio _all _tables WHERE relid IN ( $ { ids } )
UNION
SELECT pg _class . oid AS tableid , relname , nspname AS schemaname FROM pg _class JOIN pg _namespace ON pg _namespace . oid = pg _class . relnamespace WHERE pg _class . oid IN ( $ { ids } )
` );
return rows . reduce ( ( acc , curr ) => {
acc [ curr . tableid ] = {
table : curr . relname ,
schema : curr . schemaname
} ;
return acc ;
} , { } ) ;
}
2021-03-16 18:42:03 +01:00
/ * *
* @ param { Object } params
* @ param { String } params . schema
* @ param { String } params . table
* @ returns { Object } table key usage
* @ memberof PostgreSQLClient
* /
async getKeyUsage ( { schema , table } ) {
2021-03-19 18:49:26 +01:00
const { rows } = await this . raw ( `
2021-03-21 11:51:22 +01:00
SELECT
tc . table _schema ,
tc . constraint _name ,
tc . table _name ,
kcu . column _name ,
2021-03-25 18:33:29 +01:00
kcu . position _in _unique _constraint ,
kcu . ordinal _position ,
2021-03-21 11:51:22 +01:00
ccu . table _schema AS foreign _table _schema ,
ccu . table _name AS foreign _table _name ,
ccu . column _name AS foreign _column _name ,
rc . update _rule ,
rc . delete _rule
FROM information _schema . table _constraints AS tc
JOIN information _schema . key _column _usage AS kcu
ON tc . constraint _name = kcu . constraint _name
AND tc . table _schema = kcu . table _schema
JOIN information _schema . constraint _column _usage AS ccu
ON ccu . constraint _name = tc . constraint _name
AND ccu . table _schema = tc . table _schema
JOIN information _schema . referential _constraints AS rc
ON rc . constraint _name = kcu . constraint _name
WHERE tc . constraint _type = 'FOREIGN KEY' AND tc . table _schema = '${schema}'
AND tc . table _name = '${table}'
2021-03-19 18:49:26 +01:00
` );
2021-03-16 18:42:03 +01:00
return rows . map ( field => {
return {
2021-03-19 18:49:26 +01:00
schema : field . table _schema ,
table : field . table _name ,
field : field . column _name ,
position : field . ordinal _position ,
2021-03-21 11:51:22 +01:00
constraintPosition : field . position _in _unique _constraint ,
2021-03-19 18:49:26 +01:00
constraintName : field . constraint _name ,
2021-03-21 11:51:22 +01:00
refSchema : field . foreign _table _schema ,
refTable : field . foreign _table _name ,
refField : field . foreign _column _name ,
2021-03-19 18:49:26 +01:00
onUpdate : field . update _rule ,
onDelete : field . delete _rule
2021-03-16 18:42:03 +01:00
} ;
} ) ;
}
/ * *
* SELECT * FROM pg _catalog . pg _user
*
* @ returns { Array . < Object > } users list
* @ memberof PostgreSQLClient
* /
async getUsers ( ) {
const { rows } = await this . raw ( 'SELECT * FROM pg_catalog.pg_user' ) ;
return rows . map ( row => {
return {
name : row . username ,
host : row . host ,
password : row . passwd
} ;
} ) ;
}
/ * *
* CREATE SCHEMA
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
2021-03-17 11:15:14 +01:00
async createSchema ( params ) {
2021-03-16 18:42:03 +01:00
return await this . raw ( ` CREATE SCHEMA " ${ params . name } " ` ) ;
}
/ * *
* ALTER DATABASE
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
2021-03-17 11:15:14 +01:00
async alterSchema ( params ) {
2021-03-16 18:42:03 +01:00
return await this . raw ( ` ALTER SCHEMA " ${ params . name } " ` ) ;
}
/ * *
* DROP DATABASE
*
* @ returns { Array . < Object > } parameters
* @ memberof MySQLClient
* /
2021-03-17 11:15:14 +01:00
async dropSchema ( params ) {
2021-03-16 18:42:03 +01:00
return await this . raw ( ` DROP SCHEMA " ${ params . database } " ` ) ;
}
/ * *
* SHOW CREATE VIEW
*
* @ returns { Array . < Object > } view informations
* @ memberof PostgreSQLClient
* /
async getViewInformations ( { schema , view } ) {
2021-03-31 16:54:06 +02:00
const sql = ` SELECT "definition" FROM "pg_views" WHERE "viewname"=' ${ view } ' AND "schemaname"=' ${ schema } ' ` ;
2021-03-16 18:42:03 +01:00
const results = await this . raw ( sql ) ;
return results . rows . map ( row => {
return {
2021-03-31 16:54:06 +02:00
algorithm : '' ,
definer : '' ,
security : '' ,
updateOption : '' ,
sql : row . definition ,
name : view
2021-03-16 18:42:03 +01:00
} ;
} ) [ 0 ] ;
}
/ * *
* DROP VIEW
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async dropView ( params ) {
2021-04-06 12:48:40 +02:00
const sql = ` DROP VIEW ${ this . _schema } . ${ params . view } ` ;
2021-03-16 18:42:03 +01:00
return await this . raw ( sql ) ;
}
/ * *
* ALTER VIEW
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async alterView ( params ) {
const { view } = params ;
2021-04-06 12:48:40 +02:00
let sql = ` CREATE OR REPLACE VIEW ${ this . _schema } . ${ view . oldName } AS ${ view . sql } ` ;
2021-03-16 18:42:03 +01:00
if ( view . name !== view . oldName )
2021-04-06 12:48:40 +02:00
sql += ` ; ALTER VIEW ${ this . _schema } . ${ view . oldName } RENAME TO ${ view . name } ` ;
2021-03-16 18:42:03 +01:00
return await this . raw ( sql ) ;
}
/ * *
* CREATE VIEW
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async createView ( view ) {
2021-04-06 12:48:40 +02:00
const sql = ` CREATE VIEW ${ this . _schema } . ${ view . name } AS ${ view . sql } ` ;
2021-03-16 18:42:03 +01:00
return await this . raw ( sql ) ;
}
/ * *
* SHOW CREATE TRIGGER
*
* @ returns { Array . < Object > } view informations
* @ memberof PostgreSQLClient
* /
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 | 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 ] ,
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 PostgreSQLClient
* /
async dropTrigger ( params ) {
2021-04-22 15:15:08 +02:00
const triggerParts = params . trigger . split ( '.' ) ;
const sql = ` DROP TRIGGER ${ triggerParts [ 1 ] } ON ${ triggerParts [ 0 ] } ` ;
2021-03-16 18:42:03 +01:00
return await this . raw ( sql ) ;
}
/ * *
* ALTER TRIGGER
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
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 PostgreSQLClient
* /
async createTrigger ( trigger ) {
const sql = ` CREATE ${ trigger . definer ? ` DEFINER= ${ trigger . definer } ` : '' } TRIGGER \` ${ trigger . name } \` ${ trigger . event1 } ${ trigger . event2 } ON \` ${ trigger . table } \` FOR EACH ROW ${ trigger . sql } ` ;
return await this . raw ( sql , { split : false } ) ;
}
/ * *
* SHOW CREATE PROCEDURE
*
* @ returns { Array . < Object > } view informations
* @ memberof PostgreSQLClient
* /
async getRoutineInformations ( { schema , routine } ) {
2021-04-09 19:31:41 +02:00
const sql = ` SELECT pg_get_functiondef((SELECT oid FROM pg_proc WHERE proname = ' ${ routine } ')); ` ;
2021-03-16 18:42:03 +01:00
const results = await this . raw ( sql ) ;
2021-04-10 20:38:46 +02:00
return results . rows . map ( async row => {
2021-04-09 19:31:41 +02:00
if ( ! row . pg _get _functiondef ) {
2021-03-16 18:42:03 +01:00
return {
definer : null ,
sql : '' ,
parameters : [ ] ,
2021-04-09 19:31:41 +02:00
name : routine ,
2021-03-16 18:42:03 +01:00
comment : '' ,
security : 'DEFINER' ,
deterministic : false ,
dataAccess : 'CONTAINS SQL'
} ;
}
2021-04-10 20:38:46 +02:00
const sql = ` SELECT proc.specific_schema AS procedure_schema,
2021-04-14 18:06:20 +02:00
proc . specific _name ,
proc . routine _name AS procedure _name ,
proc . external _language ,
args . parameter _name ,
args . parameter _mode ,
args . data _type
FROM information _schema . routines proc
LEFT JOIN information _schema . parameters args
ON proc . specific _schema = args . specific _schema
AND proc . specific _name = args . specific _name
WHERE proc . routine _schema not in ( 'pg_catalog' , 'information_schema' )
AND proc . routine _type = 'PROCEDURE'
AND proc . routine _name = '${routine}'
AND proc . specific _schema = '${schema}'
ORDER BY procedure _schema ,
specific _name ,
procedure _name ,
args . ordinal _position
` ;
2021-04-10 20:38:46 +02:00
const results = await this . raw ( sql ) ;
const parameters = results . rows . map ( row => {
return {
name : row . parameter _name ,
type : row . data _type . toUpperCase ( ) ,
length : '' ,
context : row . parameter _mode
} ;
} ) ;
2021-03-16 18:42:03 +01:00
return {
2021-04-09 19:31:41 +02:00
definer : '' ,
sql : row . pg _get _functiondef . match ( / ( \ $ ( . * ) \ $ ) ( . * ) ( \ $ ( . * ) \ $ ) / g s ) [ 0 ] ,
2021-03-16 18:42:03 +01:00
parameters : parameters || [ ] ,
2021-04-09 19:31:41 +02:00
name : routine ,
comment : '' ,
2021-04-10 20:38:46 +02:00
security : row . pg _get _functiondef . includes ( 'SECURITY DEFINER' ) ? 'DEFINER' : 'INVOKER' ,
2021-04-09 19:31:41 +02:00
deterministic : null ,
2021-04-10 20:38:46 +02:00
dataAccess : null ,
2021-04-09 19:31:41 +02:00
language : row . pg _get _functiondef . match ( /(?<=LANGUAGE )(.*)(?<=[\S+\n\r\s])/gm ) [ 0 ]
2021-03-16 18:42:03 +01:00
} ;
} ) [ 0 ] ;
}
/ * *
* DROP PROCEDURE
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async dropRoutine ( params ) {
2021-04-09 19:31:41 +02:00
const sql = ` DROP PROCEDURE ${ this . _schema } . ${ params . routine } ` ;
2021-03-16 18:42:03 +01:00
return await this . raw ( sql ) ;
}
/ * *
* ALTER PROCEDURE
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async alterRoutine ( params ) {
const { routine } = params ;
const tempProcedure = Object . assign ( { } , routine ) ;
tempProcedure . name = ` Antares_ ${ tempProcedure . name } _tmp ` ;
try {
await this . createRoutine ( tempProcedure ) ;
await this . dropRoutine ( { routine : tempProcedure . name } ) ;
await this . dropRoutine ( { routine : routine . oldName } ) ;
await this . createRoutine ( routine ) ;
}
catch ( err ) {
return Promise . reject ( err ) ;
}
}
/ * *
* CREATE PROCEDURE
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async createRoutine ( routine ) {
const parameters = 'parameters' in routine
? routine . parameters . reduce ( ( acc , curr ) => {
2021-04-09 19:31:41 +02:00
acc . push ( ` ${ curr . context } ${ curr . name } ${ curr . type } ${ curr . length ? ` ( ${ curr . length } ) ` : '' } ` ) ;
2021-03-16 18:42:03 +01:00
return acc ;
} , [ ] ) . join ( ',' )
: '' ;
2021-04-10 20:38:46 +02:00
if ( this . _schema !== 'public' )
2021-04-19 19:15:06 +02:00
await this . use ( this . _schema ) ;
2021-04-10 20:38:46 +02:00
2021-04-09 19:31:41 +02:00
const sql = ` CREATE PROCEDURE ${ this . _schema } . ${ routine . name } ( ${ parameters } )
2021-04-12 18:46:35 +02:00
LANGUAGE $ { routine . language }
2021-04-09 19:31:41 +02:00
SECURITY $ { routine . security }
AS $ { routine . sql } ` ;
2021-03-16 18:42:03 +01:00
return await this . raw ( sql , { split : false } ) ;
}
/ * *
* SHOW CREATE FUNCTION
*
* @ returns { Array . < Object > } view informations
* @ memberof PostgreSQLClient
* /
async getFunctionInformations ( { schema , func } ) {
2021-04-12 18:46:35 +02:00
const sql = ` SELECT pg_get_functiondef((SELECT oid FROM pg_proc WHERE proname = ' ${ func } ')); ` ;
2021-03-16 18:42:03 +01:00
const results = await this . raw ( sql ) ;
2021-04-12 18:46:35 +02:00
return results . rows . map ( async row => {
if ( ! row . pg _get _functiondef ) {
2021-03-16 18:42:03 +01:00
return {
definer : null ,
sql : '' ,
parameters : [ ] ,
2021-04-12 18:46:35 +02:00
name : func ,
2021-03-16 18:42:03 +01:00
comment : '' ,
security : 'DEFINER' ,
deterministic : false ,
2021-04-12 18:46:35 +02:00
dataAccess : 'CONTAINS SQL'
2021-03-16 18:42:03 +01:00
} ;
}
2021-04-12 18:46:35 +02:00
const sql = ` SELECT proc.specific_schema AS procedure_schema,
2021-04-14 18:06:20 +02:00
proc . specific _name ,
proc . routine _name AS procedure _name ,
proc . external _language ,
args . parameter _name ,
args . parameter _mode ,
args . data _type
FROM information _schema . routines proc
LEFT JOIN information _schema . parameters args
ON proc . specific _schema = args . specific _schema
AND proc . specific _name = args . specific _name
WHERE proc . routine _schema not in ( 'pg_catalog' , 'information_schema' )
AND proc . routine _type = 'FUNCTION'
AND proc . routine _name = '${func}'
AND proc . specific _schema = '${schema}'
ORDER BY procedure _schema ,
specific _name ,
procedure _name ,
args . ordinal _position
` ;
2021-03-16 18:42:03 +01:00
2021-04-12 18:46:35 +02:00
const results = await this . raw ( sql ) ;
2021-03-16 18:42:03 +01:00
2021-04-13 18:05:03 +02:00
const parameters = results . rows . filter ( row => row . parameter _mode ) . map ( row => {
2021-04-12 18:46:35 +02:00
return {
name : row . parameter _name ,
type : row . data _type . toUpperCase ( ) ,
length : '' ,
context : row . parameter _mode
} ;
} ) ;
2021-03-16 18:42:03 +01:00
return {
2021-04-12 18:46:35 +02:00
definer : '' ,
sql : row . pg _get _functiondef . match ( / ( \ $ ( . * ) \ $ ) ( . * ) ( \ $ ( . * ) \ $ ) / g s ) [ 0 ] ,
2021-03-16 18:42:03 +01:00
parameters : parameters || [ ] ,
2021-04-12 18:46:35 +02:00
name : func ,
comment : '' ,
security : row . pg _get _functiondef . includes ( 'SECURITY DEFINER' ) ? 'DEFINER' : 'INVOKER' ,
deterministic : null ,
dataAccess : null ,
language : row . pg _get _functiondef . match ( /(?<=LANGUAGE )(.*)(?<=[\S+\n\r\s])/gm ) [ 0 ] ,
2021-04-13 18:05:03 +02:00
returns : row . pg _get _functiondef . match ( /(?<=RETURNS )(.*)(?<=[\S+\n\r\s])/gm ) [ 0 ] . replace ( 'SETOF ' , '' ) . toUpperCase ( )
2021-03-16 18:42:03 +01:00
} ;
} ) [ 0 ] ;
}
/ * *
* DROP FUNCTION
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async dropFunction ( params ) {
2021-04-13 18:05:03 +02:00
const sql = ` DROP FUNCTION ${ this . _schema } . ${ params . func } ` ;
2021-03-16 18:42:03 +01:00
return await this . raw ( sql ) ;
}
/ * *
* ALTER FUNCTION
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async alterFunction ( params ) {
const { func } = params ;
const tempProcedure = Object . assign ( { } , func ) ;
tempProcedure . name = ` Antares_ ${ tempProcedure . name } _tmp ` ;
try {
await this . createFunction ( tempProcedure ) ;
await this . dropFunction ( { func : tempProcedure . name } ) ;
await this . dropFunction ( { func : func . oldName } ) ;
await this . createFunction ( func ) ;
}
catch ( err ) {
return Promise . reject ( err ) ;
}
}
/ * *
* CREATE FUNCTION
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async createFunction ( func ) {
2021-04-13 18:05:03 +02:00
const parameters = 'parameters' in func
? func . parameters . reduce ( ( acc , curr ) => {
acc . push ( ` ${ curr . context } ${ curr . name } ${ curr . type } ${ curr . length ? ` ( ${ curr . length } ) ` : '' } ` ) ;
return acc ;
} , [ ] ) . join ( ',' )
: '' ;
if ( this . _schema !== 'public' )
2021-04-19 19:15:06 +02:00
await this . use ( this . _schema ) ;
2021-04-13 18:05:03 +02:00
const body = func . returns ? func . sql : '$BODY$\n$BODY$' ;
2021-03-16 18:42:03 +01:00
2021-04-13 18:05:03 +02:00
const sql = ` CREATE FUNCTION ${ this . _schema } . ${ func . name } ( ${ parameters } )
RETURNS $ { func . returns || 'void' }
2021-04-12 18:46:35 +02:00
LANGUAGE $ { func . language }
2021-04-13 18:05:03 +02:00
SECURITY $ { func . security }
AS $ { body } ` ;
2021-03-16 18:42:03 +01:00
return await this . raw ( sql , { split : false } ) ;
}
/ * *
* SHOW CREATE EVENT
*
* @ returns { Array . < Object > } view informations
* @ memberof PostgreSQLClient
* /
2021-03-31 16:54:06 +02:00
// async getEventInformations ({ schema, scheduler }) {
// const sql = `SHOW CREATE EVENT \`${schema}\`.\`${scheduler}\``;
// const results = await this.raw(sql);
// return results.rows.map(row => {
// const schedule = row['Create Event'];
// const execution = schedule.includes('EVERY') ? 'EVERY' : 'ONCE';
// const every = execution === 'EVERY' ? row['Create Event'].match(/(?<=EVERY )(\s*([^\s]+)){0,2}/gs)[0].replaceAll('\'', '').split(' ') : [];
// const starts = execution === 'EVERY' && schedule.includes('STARTS') ? schedule.match(/(?<=STARTS ').*?(?='\s)/gs)[0] : '';
// const ends = execution === 'EVERY' && schedule.includes('ENDS') ? schedule.match(/(?<=ENDS ').*?(?='\s)/gs)[0] : '';
// const at = execution === 'ONCE' && schedule.includes('AT') ? schedule.match(/(?<=AT ').*?(?='\s)/gs)[0] : '';
// return {
// definer: row['Create Event'].match(/(?<=DEFINER=).*?(?=\s)/gs)[0],
// sql: row['Create Event'].match(/(?<=DO )(.*)/gs)[0],
// name: row.Event,
// comment: row['Create Event'].match(/(?<=COMMENT ').*?(?=')/gs) ? row['Create Event'].match(/(?<=COMMENT ').*?(?=')/gs)[0] : '',
// state: row['Create Event'].includes('ENABLE') ? 'ENABLE' : row['Create Event'].includes('DISABLE ON SLAVE') ? 'DISABLE ON SLAVE' : 'DISABLE',
// preserve: row['Create Event'].includes('ON COMPLETION PRESERVE'),
// execution,
// every,
// starts,
// ends,
// at
// };
// })[0];
// }
2021-03-16 18:42:03 +01:00
/ * *
* DROP EVENT
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
2021-03-31 16:54:06 +02:00
// async dropEvent (params) {
// const sql = `DROP EVENT \`${params.scheduler}\``;
// return await this.raw(sql);
// }
2021-03-16 18:42:03 +01:00
/ * *
* ALTER EVENT
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
2021-03-31 16:54:06 +02:00
// async alterEvent (params) {
// const { scheduler } = params;
2021-03-16 18:42:03 +01:00
2021-03-31 16:54:06 +02:00
// if (scheduler.execution === 'EVERY' && scheduler.every[0].includes('-'))
// scheduler.every[0] = `'${scheduler.every[0]}'`;
2021-03-16 18:42:03 +01:00
2021-03-31 16:54:06 +02:00
// const sql = `ALTER ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.oldName}\`
// ON SCHEDULE
// ${scheduler.execution === 'EVERY'
// ? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
// : `AT '${scheduler.at}'`}
// ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
// ${scheduler.name !== scheduler.oldName ? `RENAME TO \`${scheduler.name}\`` : ''}
// ${scheduler.state}
// COMMENT '${scheduler.comment}'
// DO ${scheduler.sql}`;
2021-03-16 18:42:03 +01:00
2021-03-31 16:54:06 +02:00
// return await this.raw(sql, { split: false });
// }
2021-03-16 18:42:03 +01:00
/ * *
* CREATE EVENT
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
2021-03-31 16:54:06 +02:00
// async createEvent (scheduler) {
// const sql = `CREATE ${scheduler.definer ? ` DEFINER=${scheduler.definer}` : ''} EVENT \`${scheduler.name}\`
// ON SCHEDULE
// ${scheduler.execution === 'EVERY'
// ? `EVERY ${scheduler.every.join(' ')}${scheduler.starts ? ` STARTS '${scheduler.starts}'` : ''}${scheduler.ends ? ` ENDS '${scheduler.ends}'` : ''}`
// : `AT '${scheduler.at}'`}
// ON COMPLETION${!scheduler.preserve ? ' NOT' : ''} PRESERVE
// ${scheduler.state}
// COMMENT '${scheduler.comment}'
// DO ${scheduler.sql}`;
// return await this.raw(sql, { split: false });
// }
2021-03-16 18:42:03 +01:00
/ * *
* SELECT * FROM pg _collation
*
* @ returns { Array . < Object > } collations list
* @ memberof PostgreSQLClient
* /
async getCollations ( ) {
return [ ] ;
}
/ * *
* SHOW ALL
*
* @ returns { Array . < Object > } variables list
* @ memberof PostgreSQLClient
* /
async getVariables ( ) {
const sql = 'SHOW ALL' ;
const results = await this . raw ( sql ) ;
return results . rows . map ( row => {
return {
name : row . name ,
value : row . setting
} ;
} ) ;
}
/ * *
* SHOW ENGINES
*
* @ returns { Array . < Object > } engines list
* @ memberof PostgreSQLClient
* /
async getEngines ( ) {
return {
name : 'PostgreSQL' ,
support : 'YES' ,
comment : '' ,
isDefault : true
} ;
}
/ * *
* SHOW VARIABLES LIKE '%vers%'
*
* @ returns { Array . < Object > } version parameters
* @ memberof PostgreSQLClient
* /
async getVersion ( ) {
const sql = 'SELECT version()' ;
const { rows } = await this . raw ( sql ) ;
const infos = rows [ 0 ] . version . split ( ',' ) ;
return {
number : infos [ 0 ] . split ( ' ' ) [ 1 ] ,
name : infos [ 0 ] . split ( ' ' ) [ 0 ] ,
arch : infos [ 1 ] ,
os : infos [ 2 ]
} ;
}
async getProcesses ( ) {
const sql = 'SELECT "pid", "usename", "client_addr", "datname", application_name , EXTRACT(EPOCH FROM CURRENT_TIMESTAMP - "query_start")::INTEGER, "state", "query" FROM "pg_stat_activity"' ;
const { rows } = await this . raw ( sql ) ;
return rows . map ( row => {
return {
id : row . pid ,
user : row . usename ,
host : row . client _addr ,
database : row . datname ,
application : row . application _name ,
time : row . date _part ,
state : row . state ,
info : row . query
} ;
} ) ;
}
/ * *
* CREATE TABLE
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async createTable ( params ) {
const {
2021-03-28 11:55:15 +02:00
name
2021-03-16 18:42:03 +01:00
} = params ;
2021-04-06 12:48:40 +02:00
const sql = ` CREATE TABLE ${ this . _schema } . ${ name } ( ${ name } _id INTEGER NULL); ALTER TABLE ${ this . _schema } . ${ name } DROP COLUMN ${ name } _id ` ;
2021-03-16 18:42:03 +01:00
return await this . raw ( sql ) ;
}
/ * *
* ALTER TABLE
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async alterTable ( params ) {
const {
table ,
additions ,
deletions ,
changes ,
indexChanges ,
foreignChanges ,
options
} = params ;
2021-04-16 17:42:16 +02:00
if ( this . _schema !== 'public' )
2021-04-19 19:15:06 +02:00
await this . use ( this . _schema ) ;
2021-04-16 17:42:16 +02:00
2021-03-25 18:33:29 +01:00
let sql = '' ;
2021-03-16 18:42:03 +01:00
const alterColumns = [ ] ;
2021-03-25 18:33:29 +01:00
const renameColumns = [ ] ;
const createSequences = [ ] ;
2021-03-30 19:07:04 +02:00
const manageIndexes = [ ] ;
2021-03-16 18:42:03 +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 } ' ` ) ;
// ADD FIELDS
additions . forEach ( addition => {
const typeInfo = this . _getTypeInfo ( addition . type ) ;
const length = typeInfo . length ? addition . numLength || addition . charLength || addition . datePrecision : false ;
2021-03-25 18:33:29 +01:00
alterColumns . push ( ` ADD COLUMN ${ addition . name }
2021-04-09 19:31:41 +02:00
$ { addition . type . toUpperCase ( ) } $ { length ? ` ( ${ length } ) ` : '' } $ { addition . isArray ? '[]' : '' }
2021-03-16 18:42:03 +01:00
$ { addition . unsigned ? 'UNSIGNED' : '' }
$ { addition . zerofill ? 'ZEROFILL' : '' }
$ { addition . nullable ? 'NULL' : 'NOT NULL' }
$ { addition . autoIncrement ? 'AUTO_INCREMENT' : '' }
$ { addition . default ? ` DEFAULT ${ addition . default } ` : '' }
$ { addition . comment ? ` COMMENT ' ${ addition . comment } ' ` : '' }
$ { addition . collation ? ` COLLATE ${ addition . collation } ` : '' }
2021-03-25 18:33:29 +01:00
$ { addition . onUpdate ? ` ON UPDATE ${ addition . onUpdate } ` : '' } ` );
2021-03-16 18:42:03 +01:00
} ) ;
// ADD INDEX
indexChanges . additions . forEach ( addition => {
2021-03-29 20:18:44 +02:00
const fields = addition . fields . map ( field => ` ${ field } ` ) . join ( ',' ) ;
const type = addition . type ;
2021-03-16 18:42:03 +01:00
if ( type === 'PRIMARY' )
alterColumns . push ( ` ADD PRIMARY KEY ( ${ fields } ) ` ) ;
2021-03-29 20:18:44 +02:00
else if ( type === 'UNIQUE' )
alterColumns . push ( ` ADD CONSTRAINT ${ addition . name } UNIQUE ( ${ fields } ) ` ) ;
2021-03-30 19:07:04 +02:00
else
2021-04-16 17:42:16 +02:00
manageIndexes . push ( ` CREATE INDEX ${ addition . name } ON ${ this . _schema } . ${ table } ( ${ fields } ) ` ) ;
2021-03-16 18:42:03 +01:00
} ) ;
// ADD FOREIGN KEYS
foreignChanges . additions . forEach ( addition => {
2021-03-31 15:57:23 +02:00
alterColumns . push ( ` ADD CONSTRAINT ${ addition . constraintName } FOREIGN KEY ( ${ addition . field } ) REFERENCES ${ addition . refTable } ( ${ addition . refField } ) ON UPDATE ${ addition . onUpdate } ON DELETE ${ addition . onDelete } ` ) ;
2021-03-16 18:42:03 +01:00
} ) ;
// CHANGE FIELDS
changes . forEach ( change => {
const typeInfo = this . _getTypeInfo ( change . type ) ;
const length = typeInfo . length ? change . numLength || change . charLength || change . datePrecision : false ;
2021-03-25 18:33:29 +01:00
let localType ;
2021-03-16 18:42:03 +01:00
2021-03-25 18:33:29 +01:00
switch ( change . type ) {
case 'SERIAL' :
localType = 'integer' ;
break ;
case 'SMALLSERIAL' :
localType = 'smallint' ;
break ;
case 'BIGSERIAL' :
localType = 'bigint' ;
break ;
default :
localType = change . type . toLowerCase ( ) ;
}
2021-04-16 17:42:16 +02:00
alterColumns . push ( ` ALTER COLUMN " ${ change . name } " TYPE ${ localType } ${ length ? ` ( ${ length } ) ` : '' } ${ change . isArray ? '[]' : '' } USING " ${ change . name } ":: ${ localType } ` ) ;
alterColumns . push ( ` ALTER COLUMN " ${ change . name } " ${ change . nullable ? 'DROP NOT NULL' : 'SET NOT NULL' } ` ) ;
alterColumns . push ( ` ALTER COLUMN " ${ change . name } " ${ change . default ? ` SET DEFAULT ${ change . default } ` : 'DROP DEFAULT' } ` ) ;
2021-03-25 18:33:29 +01:00
if ( [ 'SERIAL' , 'SMALLSERIAL' , 'BIGSERIAL' ] . includes ( change . type ) ) {
const sequenceName = ` ${ table } _ ${ change . name } _seq ` . replace ( ' ' , '_' ) ;
2021-04-16 17:42:16 +02:00
createSequences . push ( ` CREATE SEQUENCE IF NOT EXISTS ${ sequenceName } OWNED BY " ${ table } "." ${ change . name } " ` ) ;
alterColumns . push ( ` ALTER COLUMN " ${ change . name } " SET DEFAULT nextval(' ${ sequenceName } ') ` ) ;
2021-03-25 18:33:29 +01:00
}
if ( change . orgName !== change . name )
2021-04-06 12:48:40 +02:00
renameColumns . push ( ` ALTER TABLE " ${ this . _schema } "." ${ table } " RENAME COLUMN " ${ change . orgName } " TO " ${ change . name } " ` ) ;
2021-03-16 18:42:03 +01:00
} ) ;
// CHANGE INDEX
indexChanges . changes . forEach ( change => {
2021-04-16 17:42:16 +02:00
if ( [ 'PRIMARY' , 'UNIQUE' ] . includes ( change . oldType ) )
2021-03-29 20:18:44 +02:00
alterColumns . push ( ` DROP CONSTRAINT ${ change . oldName } ` ) ;
2021-03-30 19:07:04 +02:00
else
manageIndexes . push ( ` DROP INDEX ${ change . oldName } ` ) ;
2021-03-16 18:42:03 +01:00
2021-03-29 20:18:44 +02:00
const fields = change . fields . map ( field => ` ${ field } ` ) . join ( ',' ) ;
const type = change . type ;
2021-03-16 18:42:03 +01:00
if ( type === 'PRIMARY' )
alterColumns . push ( ` ADD PRIMARY KEY ( ${ fields } ) ` ) ;
2021-03-29 20:18:44 +02:00
else if ( type === 'UNIQUE' )
alterColumns . push ( ` ADD CONSTRAINT ${ change . name } UNIQUE ( ${ fields } ) ` ) ;
2021-03-30 19:07:04 +02:00
else
2021-04-16 17:42:16 +02:00
manageIndexes . push ( ` CREATE INDEX ${ change . name } ON ${ this . _schema } . ${ table } ( ${ fields } ) ` ) ;
2021-03-16 18:42:03 +01:00
} ) ;
// CHANGE FOREIGN KEYS
foreignChanges . changes . forEach ( change => {
2021-03-31 15:57:23 +02:00
alterColumns . push ( ` DROP CONSTRAINT ${ 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 } ` ) ;
2021-03-16 18:42:03 +01:00
} ) ;
// DROP FIELDS
deletions . forEach ( deletion => {
2021-03-25 18:33:29 +01:00
alterColumns . push ( ` DROP COLUMN ${ deletion . name } ` ) ;
2021-03-16 18:42:03 +01:00
} ) ;
// DROP INDEX
indexChanges . deletions . forEach ( deletion => {
2021-03-30 19:07:04 +02:00
if ( [ 'PRIMARY' , 'UNIQUE' ] . includes ( deletion . type ) )
2021-03-29 20:18:44 +02:00
alterColumns . push ( ` DROP CONSTRAINT ${ deletion . name } ` ) ;
2021-03-30 19:07:04 +02:00
else
manageIndexes . push ( ` DROP INDEX ${ deletion . name } ` ) ;
2021-03-16 18:42:03 +01:00
} ) ;
// DROP FOREIGN KEYS
foreignChanges . deletions . forEach ( deletion => {
2021-03-31 15:57:23 +02:00
alterColumns . push ( ` DROP CONSTRAINT ${ deletion . constraintName } ` ) ;
2021-03-16 18:42:03 +01:00
} ) ;
2021-04-06 12:48:40 +02:00
if ( alterColumns . length ) sql += ` ALTER TABLE " ${ this . _schema } "." ${ table } " ${ alterColumns . join ( ', ' ) } ; ` ;
2021-03-25 18:33:29 +01:00
if ( createSequences . length ) sql = ` ${ createSequences . join ( ';' ) } ; ${ sql } ` ;
2021-03-30 19:07:04 +02:00
if ( manageIndexes . length ) sql = ` ${ manageIndexes . join ( ';' ) } ; ${ sql } ` ;
2021-04-06 12:48:40 +02:00
if ( options . name ) sql += ` ALTER TABLE " ${ this . _schema } "." ${ table } " RENAME TO " ${ options . name } "; ` ;
2021-03-16 18:42:03 +01:00
2021-04-16 17:42:16 +02:00
// RENAME
if ( renameColumns . length ) sql = ` ${ renameColumns . join ( ';' ) } ; ${ sql } ` ;
2021-03-16 18:42:03 +01:00
return await this . raw ( sql ) ;
}
/ * *
* TRUNCATE TABLE
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async truncateTable ( params ) {
2021-04-06 12:48:40 +02:00
const sql = ` TRUNCATE TABLE ${ this . _schema } . ${ params . table } ` ;
2021-03-16 18:42:03 +01:00
return await this . raw ( sql ) ;
}
/ * *
* DROP TABLE
*
* @ returns { Array . < Object > } parameters
* @ memberof PostgreSQLClient
* /
async dropTable ( params ) {
2021-04-06 12:48:40 +02:00
const sql = ` DROP TABLE ${ this . _schema } . ${ params . table } ` ;
2021-03-16 18:42:03 +01:00
return await this . raw ( sql ) ;
}
/ * *
* @ returns { String } SQL string
* @ memberof PostgreSQLClient
* /
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 ( this . _query . insert . length ) {
const fieldsList = Object . keys ( this . _query . insert [ 0 ] ) . map ( f => ` " ${ f } " ` ) ;
const rowsList = this . _query . insert . map ( el => ` ( ${ Object . values ( el ) . join ( ', ' ) } ) ` ) ;
insertRaw = ` ( ${ fieldsList . join ( ', ' ) } ) VALUES ${ rowsList . 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
2021-03-21 13:00:27 +01:00
const limitRaw = selectArray . length && this . _query . limit . length ? ` LIMIT ${ this . _query . limit . join ( ', ' ) } ` : '' ;
2021-03-16 18:42:03 +01:00
return ` ${ selectRaw } ${ updateRaw ? 'UPDATE' : '' } ${ insertRaw ? 'INSERT ' : '' } ${ this . _query . delete ? 'DELETE ' : '' } ${ fromRaw } ${ updateRaw } ${ whereRaw } ${ groupByRaw } ${ orderByRaw } ${ limitRaw } ${ insertRaw } ` ;
}
/ * *
* @ param { string } sql raw SQL query
* @ param { object } args
* @ param { boolean } args . nest
* @ param { boolean } args . details
* @ param { boolean } args . split
* @ returns { Promise }
* @ memberof PostgreSQLClient
* /
async raw ( sql , args ) {
args = {
nest : false ,
details : false ,
split : true ,
... args
} ;
2021-04-06 12:48:40 +02:00
if ( args . nest && this . _schema !== 'public' )
2021-04-19 19:15:06 +02:00
await this . use ( this . _schema ) ;
2021-04-06 12:48:40 +02:00
2021-03-16 18:42:03 +01:00
const resultsArr = [ ] ;
let paramsArr = [ ] ;
2021-04-28 11:50:07 +02:00
const queries = args . split ? sql . split ( /(?!\B'[^']*);(?![^']*'\B)/gm ) : [ sql ] ;
2021-03-16 18:42:03 +01:00
if ( process . env . NODE _ENV === 'development' ) this . _logger ( sql ) ; // TODO: replace BLOB content with a placeholder
for ( const query of queries ) {
if ( ! query ) continue ;
const timeStart = new Date ( ) ;
let timeStop ;
let keysArr = [ ] ;
const { rows , report , fields , keys , duration } = await new Promise ( ( resolve , reject ) => {
2021-03-19 18:49:26 +01:00
this . _connection . query ( {
rowMode : args . nest ? 'array' : null ,
text : query
} , async ( err , res ) => {
2021-03-16 18:42:03 +01:00
timeStop = new Date ( ) ;
if ( err )
reject ( err ) ;
else {
let ast ;
2021-03-19 18:49:26 +01:00
2021-03-16 18:42:03 +01:00
try {
2021-03-19 18:49:26 +01:00
[ ast ] = parse ( query ) ;
2021-03-16 18:42:03 +01:00
}
2021-03-19 18:49:26 +01:00
catch ( err ) { }
2021-03-16 18:42:03 +01:00
2021-03-19 18:49:26 +01:00
const { rows , fields } = res ;
let queryResult ;
2021-03-21 11:51:22 +01:00
let tablesInfo ;
2021-03-19 18:49:26 +01:00
if ( args . nest ) {
const tablesID = [ ... new Set ( fields . map ( field => field . tableID ) ) ] . toString ( ) ;
2021-03-21 11:51:22 +01:00
tablesInfo = await this . getTableByIDs ( tablesID ) ;
2021-03-19 18:49:26 +01:00
queryResult = rows . map ( row => {
return row . reduce ( ( acc , curr , i ) => {
2021-03-20 16:29:56 +01:00
const table = tablesInfo [ fields [ i ] . tableID ] ? tablesInfo [ fields [ i ] . tableID ] . table : '' ;
acc [ ` ${ table ? ` ${ table } . ` : '' } ${ fields [ i ] . name } ` ] = curr ;
2021-03-19 18:49:26 +01:00
return acc ;
} , { } ) ;
} ) ;
2021-03-16 18:42:03 +01:00
}
2021-03-19 18:49:26 +01:00
else
queryResult = rows ;
2021-03-16 18:42:03 +01:00
let remappedFields = fields
? fields . map ( field => {
if ( ! field || Array . isArray ( field ) )
return false ;
2021-03-21 11:51:22 +01:00
let schema = ast && ast . from && 'schema' in ast . from [ 0 ] ? ast . from [ 0 ] . schema : this . _schema ;
let table = ast && ast . from ? ast . from [ 0 ] . name : null ;
if ( args . nest ) {
schema = tablesInfo [ field . tableID ] ? tablesInfo [ field . tableID ] . schema : this . _schema ;
table = tablesInfo [ field . tableID ] ? tablesInfo [ field . tableID ] . table : null ;
}
2021-03-16 18:42:03 +01:00
return {
2021-03-19 18:49:26 +01:00
... field ,
2021-03-16 18:42:03 +01:00
name : field . name ,
alias : field . name ,
2021-03-21 11:51:22 +01:00
schema ,
table ,
// TODO: pick ast.from index if multiple
2021-03-16 18:42:03 +01:00
tableAlias : ast && ast . from ? ast . from [ 0 ] . as : null ,
2021-03-19 18:49:26 +01:00
orgTable : ast && ast . from ? ast . from [ 0 ] . name : null ,
type : this . types [ field . dataTypeID ] || field . format
2021-03-16 18:42:03 +01:00
} ;
} ) . filter ( Boolean )
: [ ] ;
if ( args . details ) {
if ( remappedFields . length ) {
paramsArr = remappedFields . map ( field => {
return {
2021-03-19 18:49:26 +01:00
table : field . table ,
schema : field . schema
2021-03-16 18:42:03 +01: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
2021-04-09 19:31:41 +02:00
const columns = await this . getTableColumns ( paramObj , false ) ;
2021-03-16 18:42:03 +01:00
const indexes = await this . getTableIndexes ( paramObj ) ;
remappedFields = remappedFields . map ( field => {
const detailedField = columns . find ( f => f . name === field . name ) ;
const fieldIndex = indexes . find ( i => i . column === field . name ) ;
if ( field . table === paramObj . table && field . schema === paramObj . schema ) {
2021-03-18 15:56:52 +01:00
if ( detailedField ) {
const length = detailedField . numPrecision || detailedField . charLength || detailedField . datePrecision || null ;
field = { ... field , ... detailedField , length } ;
}
2021-03-16 18:42:03 +01:00
if ( fieldIndex ) {
const key = fieldIndex . type === 'PRIMARY' ? 'pri' : fieldIndex . type === 'UNIQUE' ? 'uni' : 'mul' ;
field = { ... field , key } ;
} ;
}
return field ;
} ) ;
}
catch ( err ) {
reject ( err ) ;
}
try { // Key usage (foreign keys)
const response = await this . getKeyUsage ( paramObj ) ;
keysArr = keysArr ? [ ... keysArr , ... response ] : response ;
}
catch ( err ) {
reject ( err ) ;
}
}
}
}
resolve ( {
duration : timeStop - timeStart ,
rows : Array . isArray ( queryResult ) ? queryResult . some ( el => Array . isArray ( el ) ) ? [ ] : queryResult : false ,
report : ! Array . isArray ( queryResult ) ? queryResult : false ,
fields : remappedFields ,
keys : keysArr
} ) ;
}
} ) ;
} ) ;
resultsArr . push ( { rows , report , fields , keys , duration } ) ;
}
return resultsArr . length === 1 ? resultsArr [ 0 ] : resultsArr ;
}
}