2023-08-18 15:57:31 +02:00
import dataTypes from 'common/data-types/firebird' ;
import { FLOAT , NUMBER } from 'common/fieldTypes' ;
2022-11-02 14:18:50 +01:00
import * as antares from 'common/interfaces/antares' ;
import * as firebird from 'node-firebird' ;
2023-08-18 15:57:31 +02:00
import * as path from 'path' ;
2023-10-28 18:25:01 +02:00
import { BaseClient } from './BaseClient' ;
2022-11-02 14:18:50 +01:00
2023-10-28 18:25:01 +02:00
export class FirebirdSQLClient extends BaseClient {
2022-11-02 14:18:50 +01:00
private _schema? : string ;
2022-11-08 14:05:54 +01:00
private _runningConnections : Map < string , number > ;
2022-11-10 15:52:31 +01:00
private _connectionsToCommit : Map < string , firebird.Transaction > ;
2022-11-16 10:12:44 +01:00
protected _connection? : firebird.Database | firebird . ConnectionPool ;
2022-11-02 14:18:50 +01:00
_params : firebird.Options ;
2024-01-19 18:03:20 +01:00
private _types : Record < number , string > = {
2022-11-04 16:31:10 +01:00
452 : 'CHAR' , // Array of char
448 : 'VARCHAR' ,
500 : 'SMALLINT' ,
496 : 'INTEGER' ,
482 : 'FLOAT' ,
480 : 'DOUBLE' ,
530 : 'DOUBLE PRECISION' ,
510 : 'TIMESTAMP' ,
520 : 'BLOB' ,
2022-11-10 15:52:31 +01:00
540 : 'VARCHAR' , // ARRAY ???
2022-11-04 16:31:10 +01:00
550 : 'QUAD' ,
560 : 'TIME' ,
570 : 'DATE' ,
580 : 'BIGINT' ,
32764 : 'BOOLEAN' , // >= 3.0
32766 : 'NULL' // >= 2.5
}
2022-11-02 14:18:50 +01:00
constructor ( args : antares.ClientParams ) {
super ( args ) ;
this . _schema = null ;
this . _connectionsToCommit = new Map ( ) ;
}
2022-11-10 15:52:31 +01:00
private _getType ( type : string , subType? : number ) {
let fieldType = type . trim ( ) ;
if ( [ . . . NUMBER , . . . FLOAT ] . includes ( fieldType ) ) {
if ( subType === 1 )
fieldType = 'NUMERIC' ;
else if ( subType === 2 )
fieldType = 'DECIMAL' ;
}
if ( fieldType === 'BLOB' ) {
if ( subType === 1 )
fieldType = 'BLOB-TEXT' ;
}
if ( fieldType === 'CHAR' ) {
if ( subType === 1 )
fieldType = 'CHAR-BINARY' ;
}
return fieldType ;
}
2022-11-02 14:18:50 +01:00
getTypeInfo ( type : string ) : antares . TypeInformations {
return dataTypes
. reduce ( ( acc , group ) = > [ . . . acc , . . . group . types ] , [ ] )
. filter ( _type = > _type . name === type . toUpperCase ( ) ) [ 0 ] ;
}
2022-11-15 16:46:12 +01:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any
protected _reducer ( acc : string [ ] , curr : any ) {
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-11-02 14:18:50 +01:00
async connect ( ) {
2022-11-07 09:49:36 +01:00
if ( ! this . _poolSize )
this . _connection = await this . getConnection ( ) ;
else
2022-11-16 10:12:44 +01:00
this . _connection = this . getConnectionPool ( ) ;
2022-11-02 14:18:50 +01:00
}
async getConnection ( ) {
return new Promise < firebird.Database > ( ( resolve , reject ) = > {
2022-11-05 10:22:12 +01:00
firebird . attach ( { . . . this . _params , blobAsText : true } , ( err , db ) = > {
2022-11-02 14:18:50 +01:00
if ( err ) reject ( err ) ;
else resolve ( db ) ;
} ) ;
} ) ;
}
2022-11-16 10:12:44 +01:00
getConnectionPool ( ) {
2022-11-17 15:27:39 +01:00
return firebird . pool ( this . _poolSize , { . . . this . _params , blobAsText : true } ) ;
2022-11-07 09:49:36 +01:00
}
2022-11-02 14:18:50 +01:00
destroy ( ) {
2022-11-16 10:12:44 +01:00
if ( this . _poolSize )
return ( this . _connection as firebird . ConnectionPool ) . destroy ( ) ;
2022-11-02 14:18:50 +01:00
}
use ( ) : void {
return null ;
}
2023-11-09 18:49:56 +01:00
getDatabases ( ) : null [ ] {
return [ ] ;
}
2022-11-08 15:53:21 +01:00
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2022-11-05 10:22:12 +01:00
async getStructure ( _schemas : Set < string > ) {
2022-11-17 15:27:39 +01:00
interface TableResult {
2022-11-04 16:31:10 +01:00
FORMAT : number ;
NAME : string ;
TYPE : string ;
DESCRIPTION : string | null ;
2022-11-02 14:18:50 +01:00
}
2022-11-17 15:27:39 +01:00
interface TriggersResult {
2022-11-08 14:05:54 +01:00
NAME : string ;
RELATION : string ;
SOURCE : string ;
}
2022-11-02 14:18:50 +01:00
2022-11-17 15:27:39 +01:00
interface ProcedureResult {
NAME : string ;
COMMENT : string ;
DEFINER : string ;
SOURCE : string ;
}
2022-11-04 16:31:10 +01:00
const { rows : databases } = await this . raw < antares.QueryResult < { NAME : string } > > ( 'SELECT rdb$get_context(\'SYSTEM\', \'DB_NAME\') as name FROM rdb$database' ) ;
2022-11-02 14:18:50 +01:00
2022-11-04 16:31:10 +01:00
const filteredDatabases = databases . map ( db = > {
return { name : path.basename ( db . NAME ) } ;
} ) ;
2022-11-02 14:18:50 +01:00
2022-11-17 15:27:39 +01:00
const tablesArr : TableResult [ ] = [ ] ;
const triggersArr : TriggersResult [ ] = [ ] ;
const proceduresArr : ProcedureResult [ ] = [ ] ;
2022-11-02 14:18:50 +01:00
let schemaSize = 0 ;
2022-11-08 14:05:54 +01:00
// eslint-disable-next-line @typescript-eslint/no-unused-vars
for ( const _db of filteredDatabases ) {
2022-11-04 16:31:10 +01:00
// if (!schemas.has(db.name)) continue;
2022-11-17 15:27:39 +01:00
const { rows : tables } = await this . raw < antares.QueryResult < TableResult > > ( `
2022-11-04 16:31:10 +01:00
SELECT
2023-11-09 18:49:56 +01:00
rdb $relation_name AS NAME ,
rdb $format AS FORMAT ,
rdb $description AS DESCRIPTION ,
'table' AS TYPE
2022-11-04 16:31:10 +01:00
FROM RDB $RELATIONS a
WHERE COALESCE ( RDB $SYSTEM_FLAG , 0 ) = 0
AND RDB $RELATION_TYPE = 0
2022-11-02 14:18:50 +01:00
` );
2022-11-17 15:27:39 +01:00
const { rows : views } = await this . raw < antares.QueryResult < TableResult > > ( `
2022-11-16 10:12:44 +01:00
SELECT
2023-11-09 18:49:56 +01:00
DISTINCT RDB $VIEW_NAME AS NAME ,
'view' AS TYPE
2022-11-16 10:12:44 +01:00
FROM RDB $VIEW_RELATIONS
` );
tablesArr . push ( . . . tables , . . . views ) ;
2022-11-08 14:05:54 +01:00
2022-11-17 15:27:39 +01:00
const { rows : triggers } = await this . raw < antares.QueryResult < TriggersResult > > ( `
2022-11-08 14:05:54 +01:00
SELECT
2023-11-09 18:49:56 +01:00
RDB $TRIGGER_NAME as NAME ,
RDB $RELATION_NAME as RELATION ,
RDB $TRIGGER_SOURCE as SOURCE
2022-11-08 14:05:54 +01:00
FROM RDB $TRIGGERS
2022-11-17 15:27:39 +01:00
WHERE RDB $SYSTEM_FLAG = 0
ORDER BY RDB $TRIGGER_NAME ;
2022-11-08 14:05:54 +01:00
` );
triggersArr . push ( . . . triggers ) ;
2022-11-17 15:27:39 +01:00
const { rows : procedures } = await this . raw ( `
SELECT
RDB $PROCEDURE_NAME AS NAME ,
RDB $DESCRIPTION AS COMMENT ,
RDB $PROCEDURE_SOURCE AS SOURCE ,
RDB $OWNER_NAME AS DEFINER
FROM RDB $PROCEDURES
WHERE RDB $SYSTEM_FLAG = 0
ORDER BY RDB $PROCEDURE_NAME ;
` );
proceduresArr . push ( . . . procedures ) ;
2022-11-02 14:18:50 +01:00
}
return filteredDatabases . map ( db = > {
2022-11-04 16:31:10 +01:00
// TABLES
const remappedTables = tablesArr . map ( table = > {
const tableSize = 0 ;
schemaSize += tableSize ;
2022-11-02 14:18:50 +01:00
return {
2023-11-09 18:49:56 +01:00
name : table.NAME?.trim ( ) ,
type : table . TYPE ? . trim ( ) ,
2022-11-04 16:31:10 +01:00
rows : false ,
size : false
2022-11-02 14:18:50 +01:00
} ;
2022-11-04 16:31:10 +01:00
} ) ;
// TRIGGERS
2022-11-08 14:05:54 +01:00
const remappedTriggers = triggersArr . map ( trigger = > {
return {
2023-11-09 18:49:56 +01:00
name : trigger.NAME?.trim ( ) ,
table : trigger.RELATION?.trim ( ) ,
2022-11-08 14:05:54 +01:00
statement : trigger.SOURCE
} ;
} ) ;
2022-11-04 16:31:10 +01:00
2022-11-17 15:27:39 +01:00
// PROCEDURES
const remappedProcedures = proceduresArr . map ( procedure = > {
return {
2023-11-09 18:49:56 +01:00
name : procedure.NAME?.trim ( ) ,
2022-11-17 15:27:39 +01:00
definer : procedure.DEFINER ,
comment : procedure.COMMENT?.trim ( )
} ;
} ) ;
2022-11-04 16:31:10 +01:00
return {
name : db.name ,
size : schemaSize ,
tables : remappedTables ,
functions : [ ] ,
2022-11-17 15:27:39 +01:00
procedures : remappedProcedures ,
2022-11-08 14:05:54 +01:00
triggers : remappedTriggers ,
2022-11-04 16:31:10 +01:00
schedulers : [ ]
} ;
2022-11-02 14:18:50 +01:00
} ) ;
}
async getTableColumns ( { schema , table } : { schema : string ; table : string } ) {
interface TableColumnsResult {
2022-11-04 16:31:10 +01:00
DESCRIPTION? : string ;
FIELD_NAME : string ;
FIELD_TYPE : string ;
2022-11-09 17:41:31 +01:00
FIELD_POSITION : number ;
2022-11-04 16:31:10 +01:00
NOT_NULL : 0 | 1 ;
2022-11-09 17:41:31 +01:00
DEFAULT_VALUE : Buffer ;
DEFAULT_SOURCE : string ;
2022-11-04 16:31:10 +01:00
FIELD_LENGTH : number ;
FIELD_PRECISION : number ;
FIELD_SCALE : number ;
2022-11-09 17:41:31 +01:00
EXTERNAL_TYPE : number ;
2022-11-10 15:52:31 +01:00
SUBTYPE : number ;
COLLATION? : string ;
2022-11-04 16:31:10 +01:00
CHARSET : string ;
2022-11-02 14:18:50 +01:00
}
2022-11-09 17:41:31 +01:00
/ *
FIELD_SUB_TYPE
BLOB
0 - untyped
1 - text
2 - BLR
3 - access control list
4 - reserved for future use
5 - encoded table metadata description
6 - for storing the details of a cross - database transaction that ends abnormally
CHAR
0 - untyped data
1 - fixed binary data
NUMERIC FIELD
0 or NULL - the data type matches the value in the RDB $FIELD_TYPE field
1 - NUMERIC
2 - DECIMAL
* /
2022-11-04 16:31:10 +01:00
const { rows : fields } = await this . raw < antares.QueryResult < TableColumnsResult > > ( `
SELECT
2022-11-10 15:52:31 +01:00
r . RDB $FIELD_NAME AS FIELD_NAME ,
r . RDB $DESCRIPTION AS DESCRIPTION ,
r . RDB $DEFAULT_VALUE AS DEFAULT_VALUE ,
r . RDB $NULL_FLAG AS NOT_NULL ,
r . RDB $FIELD_POSITION AS FIELD_POSITION ,
f . RDB $FIELD_LENGTH AS FIELD_LENGTH ,
f . RDB $FIELD_PRECISION AS FIELD_PRECISION ,
f . RDB $FIELD_SCALE AS FIELD_SCALE ,
f . RDB $EXTERNAL_TYPE AS EXTERNAL_TYPE ,
r . RDB $DEFAULT_SOURCE AS DEFAULT_SOURCE ,
2022-11-04 16:31:10 +01:00
CASE f . RDB $FIELD_TYPE
WHEN 261 THEN 'BLOB'
WHEN 14 THEN 'CHAR'
WHEN 40 THEN 'CSTRING'
WHEN 11 THEN 'D_FLOAT'
2022-11-09 17:41:31 +01:00
WHEN 27 THEN 'DOUBLE PRECISION'
2022-11-04 16:31:10 +01:00
WHEN 10 THEN 'FLOAT'
2022-11-09 17:41:31 +01:00
WHEN 16 THEN 'BIGINT'
2022-11-04 16:31:10 +01:00
WHEN 8 THEN 'INTEGER'
WHEN 9 THEN 'QUAD'
WHEN 7 THEN 'SMALLINT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN 'VARCHAR'
ELSE 'UNKNOWN'
2022-11-10 15:52:31 +01:00
END AS FIELD_TYPE ,
f . RDB $FIELD_SUB_TYPE AS SUBTYPE ,
-- coll . RDB $COLLATION_NAME AS COLLATION ,
cset . RDB $CHARACTER_SET_NAME AS CHARSET
2022-11-04 16:31:10 +01:00
FROM RDB $RELATION_FIELDS r
LEFT JOIN RDB $FIELDS f ON r . RDB $FIELD_SOURCE = f . RDB $FIELD_NAME
-- LEFT JOIN RDB $COLLATIONS coll ON f . RDB $COLLATION_ID = coll . RDB $COLLATION_ID
LEFT JOIN RDB $CHARACTER_SETS cset ON f . RDB $CHARACTER_SET_ID = cset . RDB $CHARACTER_SET_ID
WHERE r . RDB $RELATION_NAME = '${table}'
ORDER BY r . RDB $FIELD_POSITION ;
` );
2022-11-02 14:18:50 +01:00
2022-11-04 16:31:10 +01:00
return fields . map ( field = > {
2022-11-09 17:41:31 +01:00
const defaultValue = field . DEFAULT_SOURCE ? field . DEFAULT_SOURCE . replace ( 'DEFAULT ' , '' ) : null ;
2022-11-10 15:52:31 +01:00
const fieldType = this . _getType ( field . FIELD_TYPE , field . SUBTYPE ) ;
2022-11-09 17:41:31 +01:00
2022-11-02 14:18:50 +01:00
return {
2022-11-04 16:31:10 +01:00
name : field.FIELD_NAME.trim ( ) ,
2022-11-02 14:18:50 +01:00
key : null ,
2022-11-09 17:41:31 +01:00
type : fieldType ,
2022-11-02 14:18:50 +01:00
schema : schema ,
table : table ,
2022-11-09 17:41:31 +01:00
numPrecision : field.FIELD_PRECISION ? field.FIELD_PRECISION : null ,
numScale : Math.abs ( field . FIELD_SCALE ) ,
datePrecision : field.FIELD_NAME.trim ( ) === 'TIMESTAMP' ? 4 : null ,
charLength : ! [ . . . NUMBER , . . . FLOAT ] . includes ( fieldType ) ? field.FIELD_LENGTH : null ,
2022-11-04 16:31:10 +01:00
nullable : ! field . NOT_NULL ,
2022-11-02 14:18:50 +01:00
unsigned : null ,
zerofill : null ,
2022-11-09 17:41:31 +01:00
order : field.FIELD_POSITION + 1 ,
default : defaultValue ,
2022-11-04 16:31:10 +01:00
charset : field.CHARSET ,
2022-11-02 14:18:50 +01:00
collation : null ,
autoIncrement : false ,
onUpdate : null ,
2022-11-04 16:31:10 +01:00
comment : field.DESCRIPTION?.trim ( )
2022-11-02 14:18:50 +01:00
} ;
} ) ;
}
2022-11-08 14:05:54 +01:00
async getTableApproximateCount ( { table } : { schema : string ; table : string } ) : Promise < number > {
const { rows } = await this . raw ( ` SELECT COUNT(*) AS nRows FROM " ${ table } " ` ) ;
2022-11-02 14:18:50 +01:00
2022-11-08 14:05:54 +01:00
return rows . length ? rows [ 0 ] . NROWS : 0 ;
2022-11-02 14:18:50 +01:00
}
async getTableOptions ( { table } : { table : string } ) {
return { name : table } ;
}
2022-11-08 14:05:54 +01:00
async getTableIndexes ( { table } : { schema : string ; table : string } ) {
2022-11-02 14:18:50 +01:00
interface ShowIndexesResult {
2022-11-08 14:05:54 +01:00
INDEX_NAME : string ;
FIELD_NAME : string ;
TABLE_NAME : string ;
INDEX_TYPE : string ;
INDEX_UNIQUE : number ;
2022-11-02 14:18:50 +01:00
}
const remappedIndexes = [ ] ;
2022-11-08 14:05:54 +01:00
const { rows : indexes } = await this . raw < antares.QueryResult < ShowIndexesResult > > ( `
SELECT
ix . rdb $index_name AS INDEX_NAME ,
sg . rdb $field_name AS FIELD_NAME ,
rc . rdb $relation_name AS TABLE_NAME ,
rc . rdb $constraint_type AS INDEX_TYPE ,
ix . RDB $UNIQUE_FLAG AS INDEX_UNIQUE
FROM
rdb $indices ix
LEFT JOIN rdb $index_segments sg ON ix . rdb $index_name = sg . rdb $index_name
LEFT JOIN rdb $relation_constraints rc ON rc . rdb $index_name = ix . rdb $index_name
WHERE
rc . rdb $relation_name = '${table}'
` );
for ( const index of indexes ) {
2022-11-02 14:18:50 +01:00
remappedIndexes . push ( {
2022-11-08 14:05:54 +01:00
name : index.INDEX_NAME.trim ( ) ,
column : index.FIELD_NAME.trim ( ) ,
2022-11-02 14:18:50 +01:00
indexType : null as never ,
2022-11-15 16:46:12 +01:00
type : index . INDEX_TYPE . trim ( ) === 'PRIMARY KEY' ? 'PRIMARY' : index . INDEX_TYPE . trim ( ) ,
2022-11-02 14:18:50 +01:00
cardinality : null as never ,
comment : '' ,
indexComment : ''
} ) ;
}
return remappedIndexes ;
}
async getKeyUsage ( { schema , table } : { schema : string ; table : string } ) {
/* eslint-disable camelcase */
interface KeyResult {
2022-11-08 14:05:54 +01:00
PKTABLE_NAME : string ;
PKCOLUMN_NAME : string ;
FKTABLE_NAME : string ;
FKCOLUMN_NAME : string ;
KEY_SEQ : number ;
UPDATE_RULE : string ;
DELETE_RULE : string ;
PK_NAME : string ;
FK_NAME : string ;
2022-11-02 14:18:50 +01:00
}
/* eslint-enable camelcase */
2022-11-08 14:05:54 +01:00
const { rows } = await this . raw < antares.QueryResult < KeyResult > > ( `
SELECT
PK . RDB $RELATION_NAME as PKTABLE_NAME ,
ISP . RDB $FIELD_NAME as PKCOLUMN_NAME ,
FK . RDB $RELATION_NAME as FKTABLE_NAME ,
ISF . RDB $FIELD_NAME as FKCOLUMN_NAME ,
( ISP . RDB $FIELD_POSITION + 1 ) as KEY_SEQ ,
RC . RDB $UPDATE_RULE as UPDATE_RULE ,
RC . RDB $DELETE_RULE as DELETE_RULE ,
PK . RDB $CONSTRAINT_NAME as PK_NAME ,
FK . RDB $CONSTRAINT_NAME as FK_NAME
FROM
RDB $RELATION_CONSTRAINTS PK ,
RDB $RELATION_CONSTRAINTS FK ,
RDB $REF_CONSTRAINTS RC ,
RDB $INDEX_SEGMENTS ISP ,
RDB $INDEX_SEGMENTS ISF
WHERE FK . RDB $RELATION_NAME = '${table}'
and FK . RDB $CONSTRAINT_NAME = RC . RDB $CONSTRAINT_NAME
and PK . RDB $CONSTRAINT_NAME = RC . RDB $CONST_NAME_UQ
and ISP . RDB $INDEX_NAME = PK . RDB $INDEX_NAME
and ISF . RDB $INDEX_NAME = FK . RDB $INDEX_NAME
and ISP . RDB $FIELD_POSITION = ISF . RDB $FIELD_POSITION
ORDER BY 1 , 5
` );
2022-11-02 14:18:50 +01:00
return rows . map ( field = > {
return {
schema : schema ,
table : table ,
2022-11-08 14:05:54 +01:00
field : field.FKCOLUMN_NAME.trim ( ) ,
position : field.KEY_SEQ ,
2022-11-02 14:18:50 +01:00
constraintPosition : null ,
2022-11-08 14:05:54 +01:00
constraintName : field.FK_NAME.trim ( ) ,
2022-11-02 14:18:50 +01:00
refSchema : schema ,
2022-11-08 14:05:54 +01:00
refTable : field.PKTABLE_NAME.trim ( ) ,
refField : field.PKCOLUMN_NAME.trim ( ) ,
onUpdate : field.UPDATE_RULE.trim ( ) ,
onDelete : field.DELETE_RULE.trim ( )
2022-11-02 14:18:50 +01:00
} ;
} ) ;
}
async getUsers ( ) : Promise < void > {
return null ;
}
async createTable ( params : antares.CreateTableParams ) {
const {
fields ,
foreigns ,
indexes ,
options
} = params ;
const newColumns : string [ ] = [ ] ;
const newIndexes : string [ ] = [ ] ;
const newForeigns : string [ ] = [ ] ;
2022-11-15 16:46:12 +01:00
let sql = ` CREATE TABLE " ${ options . name } " ` ;
2022-11-02 14:18:50 +01:00
// ADD FIELDS
fields . forEach ( field = > {
const typeInfo = this . getTypeInfo ( field . type ) ;
const length = typeInfo ? . length ? field . enumValues || field . numLength || field . charLength || field.datePrecision : false ;
newColumns . push ( ` " ${ field . name } "
$ { field . type . toUpperCase ( ) } $ { length ? ` ( ${ length } ) ` : '' }
$ { field . default !== null ? ` DEFAULT ${ field . default || '\'\'' } ` : '' }
2022-11-15 16:46:12 +01:00
$ { field . nullable ? '' : 'NOT NULL' } ` );
2022-11-02 14:18:50 +01:00
} ) ;
// ADD INDEX
indexes . forEach ( index = > {
const fields = index . fields . map ( field = > ` " ${ field } " ` ) . join ( ',' ) ;
const type = index . type ;
2022-11-15 16:46:12 +01:00
newIndexes . push ( ` CONSTRAINT " ${ index . name } " ${ type === 'PRIMARY' ? 'PRIMARY KEY' : type } ( ${ fields } ) ` ) ;
2022-11-02 14:18:50 +01:00
} ) ;
// ADD FOREIGN KEYS
foreigns . forEach ( foreign = > {
2022-11-15 16:46:12 +01:00
newForeigns . push ( `
ADD CONSTRAINT "${foreign.constraintName}"
FOREIGN KEY ( "${foreign.field}" ) REFERENCES "${foreign.refTable}" ( "${foreign.refField}" )
$ { foreign . onUpdate !== 'RESTRICT' ? ` ON UPDATE ${ foreign . onUpdate } ` : '' }
$ { foreign . onDelete !== 'RESTRICT' ? ` ON DELETE ${ foreign . onDelete } ` : '' }
` );
2022-11-02 14:18:50 +01:00
} ) ;
2022-11-15 16:46:12 +01:00
sql = ` ${ sql } ( ${ [ . . . newColumns , . . . newIndexes ] . join ( ', ' ) } ) ` ;
if ( newForeigns . length )
sql = ` ${ sql } ; ALTER TABLE " ${ options . name } " ${ newForeigns . join ( ';' ) } ` ;
2022-11-02 14:18:50 +01:00
return await this . raw ( sql ) ;
}
async alterTable ( params : antares.AlterTableParams ) {
const {
table ,
additions ,
deletions ,
changes ,
2022-11-15 16:46:12 +01:00
indexChanges ,
foreignChanges
2022-11-02 14:18:50 +01:00
} = params ;
2022-11-15 16:46:12 +01:00
let sql = ` ALTER TABLE " ${ table } " ` ;
const alterColumns : string [ ] = [ ] ;
const newForeigns : string [ ] = [ ] ;
2022-11-02 14:18:50 +01:00
2022-11-15 16:46:12 +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}'`);
2022-11-02 14:18:50 +01:00
2022-11-15 16:46:12 +01:00
// ADD FIELDS
additions . forEach ( addition = > {
const typeInfo = this . getTypeInfo ( addition . type ) ;
const length = typeInfo . length ? addition . enumValues || addition . numLength || addition . charLength || addition.datePrecision : false ;
alterColumns . push ( ` ADD " ${ addition . name } "
$ { addition . type . toUpperCase ( ) } $ { length ? ` ( ${ length } ) ` : '' }
$ { addition . default !== null ? ` DEFAULT ${ addition . default || '\'\'' } ` : '' }
$ { addition . nullable ? '' : 'NOT NULL' } ` );
} ) ;
2022-11-02 14:18:50 +01:00
2022-11-15 16:46:12 +01:00
// ADD INDEX
indexChanges . additions . forEach ( addition = > {
const fields = addition . fields . map ( field = > ` " ${ field } " ` ) . join ( ',' ) ;
const type = addition . type ;
2022-11-02 14:18:50 +01:00
2022-11-15 16:46:12 +01:00
alterColumns . push ( ` ADD CONSTRAINT " ${ addition . name } " ${ type === 'PRIMARY' ? 'PRIMARY KEY' : type } ( ${ fields } ) ` ) ;
} ) ;
// ADD FOREIGN KEYS
foreignChanges . additions . forEach ( foreign = > {
newForeigns . push ( `
ADD CONSTRAINT "${foreign.constraintName}"
FOREIGN KEY ( "${foreign.field}" ) REFERENCES "${foreign.refTable}" ( "${foreign.refField}" )
$ { foreign . onUpdate !== 'RESTRICT' ? ` ON UPDATE ${ foreign . onUpdate } ` : '' }
$ { foreign . onDelete !== 'RESTRICT' ? ` ON DELETE ${ foreign . onDelete } ` : '' }
` );
} ) ;
// CHANGE FIELDS
changes . forEach ( change = > {
const typeInfo = this . getTypeInfo ( change . type ) ;
const length = typeInfo . length ? change . enumValues || change . numLength || change . charLength || change.datePrecision : false ;
if ( change . orgName !== change . name )
alterColumns . push ( ` ALTER COLUMN " ${ change . orgName } " TO " ${ change . name } " ` ) ;
alterColumns . push ( ` ALTER COLUMN " ${ change . name } " TYPE ${ change . type . toUpperCase ( ) } ${ length ? ` ( ${ length } ${ change . numScale ? ` , ${ change . numScale } ` : '' } ) ` : '' } ` ) ;
if ( change . default !== null )
alterColumns . push ( ` ALTER COLUMN " ${ change . name } " SET DEFAULT ${ change . default || '\'\'' } ` ) ;
alterColumns . push ( ` ALTER COLUMN " ${ change . name } " ${ ! change . nullable ? 'SET ' : 'DROP ' } NOT NULL ` ) ;
// TODO: position
} ) ;
// CHANGE INDEX
indexChanges . changes . forEach ( change = > {
alterColumns . push ( ` DROP CONSTRAINT " ${ change . oldName } " ` ) ;
const fields = change . fields . map ( field = > ` " ${ field } " ` ) . join ( ',' ) ;
const type = change . type ;
alterColumns . push ( ` ADD CONSTRAINT " ${ change . name } " ${ type === 'PRIMARY' ? 'PRIMARY KEY' : type } ( ${ fields } ) ` ) ;
} ) ;
// CHANGE FOREIGN KEYS
foreignChanges . changes . forEach ( change = > {
alterColumns . push ( ` DROP CONSTRAINT " ${ change . oldName } " ` ) ;
alterColumns . push ( `
ADD CONSTRAINT "${change.constraintName}"
FOREIGN KEY ( "${change.field}" ) REFERENCES "${change.refTable}" ( "${change.refField}" )
$ { change . onUpdate !== 'RESTRICT' ? ` ON UPDATE ${ change . onUpdate } ` : '' }
$ { change . onDelete !== 'RESTRICT' ? ` ON DELETE ${ change . onDelete } ` : '' }
` );
} ) ;
// DROP FIELDS
deletions . forEach ( deletion = > {
alterColumns . push ( ` DROP " ${ deletion . name } " ` ) ;
} ) ;
// DROP INDEX
indexChanges . deletions . forEach ( deletion = > {
alterColumns . push ( ` DROP CONSTRAINT " ${ deletion . name } " ` ) ;
} ) ;
// DROP FOREIGN KEYS
foreignChanges . deletions . forEach ( deletion = > {
alterColumns . push ( ` DROP CONSTRAINT " ${ deletion . constraintName } " ` ) ;
} ) ;
if ( alterColumns . length )
sql += alterColumns . join ( ', ' ) ;
if ( newForeigns . length )
sql = ` ${ sql } ; ALTER TABLE " ${ table } " ${ newForeigns . join ( ';' ) } ` ;
return await this . raw ( sql ) ;
2022-11-02 14:18:50 +01:00
}
2022-11-17 15:27:39 +01:00
// eslint-disable-next-line @typescript-eslint/no-unused-vars
2022-11-02 14:18:50 +01:00
async duplicateTable ( params : { schema : string ; table : string } ) { // TODO: retrive table informations and create a copy
2022-11-17 15:27:39 +01:00
// const sql = `CREATE TABLE "${params.table}_copy" AS SELECT * FROM "${params.table}"`;
// return await this.raw(sql);
2022-11-02 14:18:50 +01:00
}
async truncateTable ( params : { schema : string ; table : string } ) {
2022-11-08 15:53:21 +01:00
const sql = ` DELETE FROM " ${ params . table } " ` ;
2022-11-02 14:18:50 +01:00
return await this . raw ( sql ) ;
}
async dropTable ( params : { schema : string ; table : string } ) {
2022-11-08 15:53:21 +01:00
const sql = ` DROP TABLE " ${ params . table } " ` ;
2022-11-02 14:18:50 +01:00
return await this . raw ( sql ) ;
}
2022-11-16 12:00:12 +01:00
async getViewInformations ( { view } : { schema : string ; view : string } ) {
const sql = `
SELECT rdb $view_source as sql
FROM rdb $relations
WHERE rdb $relation_name = '${view}'
` ;
2022-11-02 14:18:50 +01:00
const results = await this . raw ( sql ) ;
return results . rows . map ( row = > {
return {
2022-11-16 12:00:12 +01:00
sql : row.SQL ,
2022-11-02 14:18:50 +01:00
name : view
} ;
} ) [ 0 ] ;
}
async dropView ( params : { schema : string ; view : string } ) {
2022-11-16 12:00:12 +01:00
const sql = ` DROP VIEW " ${ params . view } " ` ;
2022-11-02 14:18:50 +01:00
return await this . raw ( sql ) ;
}
async alterView ( { view } : { view : antares.AlterViewParams } ) {
try {
await this . dropView ( { schema : view.schema , view : view.oldName } ) ;
await this . createView ( view ) ;
}
catch ( err ) {
return Promise . reject ( err ) ;
}
}
async createView ( params : antares.CreateViewParams ) {
2022-11-16 12:00:12 +01:00
const sql = ` CREATE VIEW " ${ params . name } " AS ${ params . sql } ` ;
2022-11-02 14:18:50 +01:00
return await this . raw ( sql ) ;
}
2022-11-16 15:16:12 +01:00
async getTriggerInformations ( { trigger } : { schema : string ; trigger : string } ) {
const sql = `
SELECT
RDB $TRIGGER_NAME as name ,
RDB $RELATION_NAME as relation ,
RDB $TRIGGER_SOURCE as sql ,
RDB $TRIGGER_TYPE as type
FROM RDB $TRIGGERS
WHERE RDB $SYSTEM_FLAG = 0
AND RDB $TRIGGER_NAME = '${trigger}' ;
` ;
2022-11-02 14:18:50 +01:00
const results = await this . raw ( sql ) ;
2022-11-16 15:16:12 +01:00
const eventsMap = new Map ( [
[ 1 , [ 'INSERT' ] ] ,
[ 2 , [ 'INSERT' ] ] ,
[ 3 , [ 'UPDATE' ] ] ,
[ 4 , [ 'UPDATE' ] ] ,
[ 5 , [ 'DELETE' ] ] ,
[ 6 , [ 'DELETE' ] ] ,
[ 17 , [ 'INSERT' , 'UPDATE' ] ] ,
[ 18 , [ 'INSERT' , 'UPDATE' ] ] ,
[ 25 , [ 'INSERT' , 'DELETE' ] ] ,
[ 26 , [ 'INSERT' , 'DELETE' ] ] ,
[ 27 , [ 'UPDATE' , 'DELETE' ] ] ,
[ 28 , [ 'UPDATE' , 'DELETE' ] ] ,
[ 113 , [ 'INSERT' , 'UPDATE' , 'DELETE' ] ] ,
[ 114 , [ 'INSERT' , 'UPDATE' , 'DELETE' ] ]
] ) ;
2022-11-02 14:18:50 +01:00
return results . rows . map ( row = > {
return {
2022-11-16 15:16:12 +01:00
sql : row.SQL.match ( / ( B E G I N | b e g i n ) ( . * ) ( E N D | e n d ) / g s ) [ 0 ] ,
2022-11-02 14:18:50 +01:00
name : trigger ,
2022-11-16 15:16:12 +01:00
table : row.RELATION.trim ( ) ,
activation : row.TYPE % 2 ? 'BEFORE' : 'AFTER' ,
event : eventsMap.get ( row . TYPE )
2022-11-02 14:18:50 +01:00
} ;
} ) [ 0 ] ;
}
async dropTrigger ( params : { schema : string ; trigger : string } ) {
2022-11-16 15:16:12 +01:00
const sql = ` DROP TRIGGER " ${ params . trigger } " ` ;
2022-11-02 14:18:50 +01:00
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 ) {
2022-11-16 15:16:12 +01:00
const eventsString = Array . isArray ( params . event ) ? params . event . join ( ' OR ' ) : params . event ;
const sql = `
CREATE TRIGGER "${params.name}" FOR "${params.table}"
$ { params . activation } $ { eventsString }
AS $ { params . sql }
` ;
2022-11-02 14:18:50 +01:00
return await this . raw ( sql , { split : false } ) ;
}
2022-11-17 15:27:39 +01:00
async getRoutineInformations ( { routine } : { schema : string ; routine : string } ) {
interface ProcedureResult {
NAME : string ;
COMMENT : string ;
DEFINER : string ;
SOURCE : string ;
SECURITY : boolean ;
}
interface ProcedureParamsResult {
PARAMETER_NAME : string ;
FIELD_TYPE : string ;
FIELD_LENGTH : string ;
FIELD_PRECISION : string ;
FIELD_SCALE : string ;
CONTEXT : string ;
}
const { rows : [ procedure ] } = await this . raw < antares.QueryResult < ProcedureResult > > ( `
SELECT
RDB $PROCEDURE_NAME AS NAME ,
RDB $DESCRIPTION AS COMMENT ,
RDB $PROCEDURE_SOURCE AS SOURCE ,
RDB $OWNER_NAME AS DEFINER ,
RDB $SQL_SECURITY AS SECURITY
FROM RDB $PROCEDURES
WHERE RDB $SYSTEM_FLAG = 0
AND RDB $PROCEDURE_NAME = '${routine}' ;
` );
if ( procedure ) {
const { rows : parameters } = await this . raw < antares.QueryResult < ProcedureParamsResult > > ( `
SELECT
p . RDB $PARAMETER_NAME AS PARAMETER_NAME ,
p . RDB $PARAMETER_TYPE AS CONTEXT ,
CASE f . RDB $FIELD_TYPE
WHEN 261 THEN 'BLOB'
WHEN 14 THEN 'CHAR'
WHEN 40 THEN 'CSTRING'
WHEN 11 THEN 'D_FLOAT'
WHEN 27 THEN 'DOUBLE PRECISION'
WHEN 10 THEN 'FLOAT'
WHEN 16 THEN 'BIGINT'
WHEN 8 THEN 'INTEGER'
WHEN 9 THEN 'QUAD'
WHEN 7 THEN 'SMALLINT'
WHEN 12 THEN 'DATE'
WHEN 13 THEN 'TIME'
WHEN 35 THEN 'TIMESTAMP'
WHEN 37 THEN 'VARCHAR'
ELSE 'UNKNOWN'
END AS FIELD_TYPE ,
f . RDB $FIELD_LENGTH AS FIELD_LENGTH ,
f . RDB $FIELD_PRECISION AS FIELD_PRECISION ,
f . RDB $FIELD_SCALE AS FIELD_SCALE
FROM RDB $PROCEDURE_PARAMETERS p
JOIN RDB $FIELDS f ON f . RDB $FIELD_NAME = p . RDB $FIELD_SOURCE
WHERE p . RDB $SYSTEM_FLAG = 0
AND RDB $PROCEDURE_NAME = '${routine}'
ORDER BY p . RDB $PARAMETER_TYPE , p . RDB $PARAMETER_NUMBER
` );
const remappedParams = parameters . map ( param = > {
const length = this . getTypeInfo ( param . FIELD_TYPE . trim ( ) ) . length ? param . FIELD_LENGTH || param.FIELD_PRECISION : null ;
return {
name : param.PARAMETER_NAME.trim ( ) ,
type : param . FIELD_TYPE . trim ( ) ,
length : length ,
context : param.CONTEXT ? 'OUT' : 'IN'
} ;
} ) ;
return {
definer : procedure.DEFINER ,
sql : procedure.SOURCE ,
parameters : remappedParams || [ ] ,
name : procedure.NAME.trim ( ) ,
comment : '' ,
security : procedure.SECURITY === false ? 'INVOKER' : 'DEFINER' ,
deterministic : false ,
dataAccess : 'CONTAINS SQL'
} ;
}
else {
return {
definer : null ,
sql : '' ,
parameters : [ ] ,
name : routine ,
comment : '' ,
security : 'DEFINER' ,
deterministic : false ,
dataAccess : 'CONTAINS SQL'
} ;
}
}
async dropRoutine ( params : { routine : string } ) {
const sql = ` DROP PROCEDURE " ${ params . routine } " ` ;
return await this . raw ( sql ) ;
}
async alterRoutine ( { routine } : { routine : antares.AlterRoutineParams } ) {
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 ) ;
}
}
async createRoutine ( params : antares.CreateRoutineParams ) {
const inParams = 'parameters' in params
? params . parameters
. filter ( param = > param . context === 'IN' )
. reduce ( ( acc : string [ ] , curr ) = > {
acc . push ( ` " ${ curr . name } " ${ curr . type } ${ curr . length ? ` ( ${ curr . length } ) ` : '' } ` ) ;
return acc ;
} , [ ] ) . join ( ',' )
: '' ;
const ourParams = 'parameters' in params
? params . parameters
. filter ( param = > param . context === 'OUT' )
. reduce ( ( acc : string [ ] , curr ) = > {
acc . push ( ` " ${ curr . name } " ${ curr . type } ${ curr . length ? ` ( ${ curr . length } ) ` : '' } ` ) ;
return acc ;
} , [ ] ) . join ( ',' )
: '' ;
const sql = `
CREATE PROCEDURE "${params.name}" ( $ { inParams } )
$ { ourParams ? ` RETURNS ( ${ ourParams } ) ` : '' }
SQL SECURITY $ { params . security }
AS
$ { params . sql }
` ;
return await this . raw ( sql , { split : false } ) ;
}
2022-11-02 14:18:50 +01:00
async getEngines ( ) {
return {
2022-11-16 15:16:12 +01:00
name : 'Firebird' ,
2022-11-02 14:18:50 +01:00
support : 'YES' ,
comment : '' ,
isDefault : true
} ;
}
async getVersion ( ) {
2022-11-04 16:31:10 +01:00
const sql = `
SELECT
rdb $get_context ( 'SYSTEM' , 'ENGINE_VERSION' ) as version ,
rdb $get_context ( 'SYSTEM' , 'NETWORK_PROTOCOL' ) as protocol ,
RDB $GET_CONTEXT ( 'SYSTEM' , 'CLIENT_ADDRESS' ) AS address
FROM rdb $database ` ;
2022-11-02 14:18:50 +01:00
const { rows } = await this . raw ( sql ) ;
return {
2022-11-05 10:22:12 +01:00
number : rows [ 0 ] . VERSION ,
2022-11-04 16:31:10 +01:00
name : 'Firebird SQL' ,
2022-11-05 10:22:12 +01:00
arch : rows [ 0 ] . PROTOCOL ,
os : rows [ 0 ] . ADDRESS
2022-11-02 14:18:50 +01:00
} ;
}
async getProcesses ( ) : Promise < void > {
return null ;
}
async killProcess ( ) : Promise < void > {
return null ;
}
2022-11-08 15:53:21 +01:00
async commitTab ( tabUid : string ) {
2022-11-10 15:52:31 +01:00
const connection = this . _connectionsToCommit . get ( tabUid ) ;
if ( connection ) {
connection . commit ( ) ;
return this . destroyConnectionToCommit ( tabUid ) ;
}
2022-11-08 15:53:21 +01:00
}
async rollbackTab ( tabUid : string ) {
2022-11-10 15:52:31 +01:00
const connection = this . _connectionsToCommit . get ( tabUid ) ;
if ( connection ) {
connection . rollback ( ) ;
return this . destroyConnectionToCommit ( tabUid ) ;
}
2022-11-08 15:53:21 +01:00
}
destroyConnectionToCommit ( tabUid : string ) {
2022-11-10 15:52:31 +01:00
this . _connectionsToCommit . delete ( tabUid ) ;
2022-11-08 15:53:21 +01:00
}
2022-11-02 14:18:50 +01:00
getSQL ( ) {
2022-11-04 16:31:10 +01:00
// LIMIT
2022-11-15 16:46:12 +01:00
const limitRaw = this . _query . limit ? ` FIRST ${ this . _query . limit } ` : '' ;
2022-11-04 16:31:10 +01:00
2022-11-08 14:05:54 +01:00
// OFFSET
2022-11-15 16:46:12 +01:00
const offsetRaw = this . _query . offset ? ` SKIP ${ this . _query . offset } ` : '' ;
2022-11-08 14:05:54 +01:00
2022-11-02 14:18:50 +01:00
// SELECT
const selectArray = this . _query . select . reduce ( this . _reducer , [ ] ) ;
let selectRaw = '' ;
if ( selectArray . length )
2022-11-08 14:05:54 +01:00
selectRaw = selectArray . length ? ` SELECT ${ limitRaw || '' } ${ offsetRaw || '' } ${ selectArray . join ( ', ' ) } ` : ` SELECT ${ limitRaw || '' } ${ offsetRaw || '' } * ` ;
2022-11-02 14:18:50 +01:00
// 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' ;
2022-11-15 16:46:12 +01:00
fromRaw += this . _query . from ? ` " ${ this . _query . from } " ` : '' ;
2022-11-02 14:18:50 +01:00
// WHERE
const whereArray = this . _query . where
. reduce ( this . _reducer , [ ] )
? . map ( clausole = > clausole . replace ( '= null' , 'IS NULL' ) ) ;
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 ) {
2022-11-15 16:46:12 +01:00
const fieldsList = Object . keys ( this . _query . insert [ 0 ] ) . map ( col = > '"' + col + '"' ) ;
2022-11-02 14:18:50 +01:00
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 ( ', ' ) } ` : '' ;
2022-11-15 16:46:12 +01:00
return ` ${ selectRaw } ${ updateRaw ? ` UPDATE ${ ' ' + limitRaw || '' } ` : '' } ${ insertRaw ? 'INSERT ' : '' } ${ this . _query . delete ? 'DELETE ' : '' } ${ fromRaw } ${ updateRaw } ${ whereRaw } ${ groupByRaw } ${ orderByRaw } ${ ' ' + ( this . _query . delete ? ` ROWS ${ this . _query . limit } ` : '' ) || '' } ${ insertRaw } ` ;
2022-11-02 14:18:50 +01:00
}
async raw < T = antares.QueryResult > ( sql : string , args? : antares.QueryParams ) {
2022-11-04 16:31:10 +01:00
interface FieldData {
type : number ;
nullable : boolean ;
subType : number ;
scale : number ;
length : number ;
field : string ;
relation : string ;
alias : string ;
}
2022-11-02 14:18:50 +01:00
this . _logger ( { cUid : this._cUid , sql } ) ;
args = {
nest : false ,
details : false ,
split : true ,
comments : true ,
autocommit : true ,
. . . args
} ;
if ( ! args . comments )
sql = sql . replace ( /(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm , '' ) ; // Remove comments
const resultsArr = [ ] ;
2022-11-08 14:05:54 +01:00
let paramsArr = [ ] ;
2022-11-02 14:18:50 +01:00
const queries = args . split
? sql . split ( /((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm )
. filter ( Boolean )
. map ( q = > q . trim ( ) )
: [ sql ] ;
2022-11-10 15:52:31 +01:00
let connection : firebird.Database | firebird . Transaction ;
2022-11-16 10:12:44 +01:00
const isPool = this . _poolSize ;
2022-11-02 14:18:50 +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 ( ) ;
2022-11-16 10:12:44 +01:00
2022-11-10 15:52:31 +01:00
const transaction = await new Promise < firebird.Transaction > ( ( resolve , reject ) = > {
2022-12-06 12:56:36 +01:00
( connection as firebird . Database ) . transaction ( firebird . ISOLATION_READ_COMMITTED , ( err , transaction ) = > {
2022-11-02 14:18:50 +01:00
if ( err ) reject ( err ) ;
2022-11-10 15:52:31 +01:00
else resolve ( transaction ) ;
2022-11-02 14:18:50 +01:00
} ) ;
} ) ;
2022-11-10 15:52:31 +01:00
connection = transaction ;
this . _connectionsToCommit . set ( args . tabUid , transaction ) ;
2022-11-02 14:18:50 +01:00
}
}
2022-11-16 10:12:44 +01:00
else { // autocommit ON
if ( isPool ) {
const pool = this . _connection as firebird . ConnectionPool ;
connection = await new Promise < firebird.Database > ( ( resolve , reject ) = > {
pool . get ( ( err , db ) = > {
if ( err ) reject ( err ) ;
else resolve ( db ) ;
} ) ;
} ) ;
}
else
connection = this . _connection as firebird . Database ;
}
2022-11-02 14:18:50 +01:00
for ( const query of queries ) {
if ( ! query ) continue ;
const timeStart = new Date ( ) ;
let timeStop ;
2022-11-08 14:05:54 +01:00
let keysArr : antares.QueryForeign [ ] = [ ] ;
2022-11-02 14:18:50 +01:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const { rows , report , fields , keys , duration } : any = await new Promise ( ( resolve , reject ) = > {
( async ( ) = > {
let queryResult ;
2022-11-08 14:05:54 +01:00
let remappedFields : {
name : string ;
alias : string ;
orgName : string ;
schema : string ;
table : string ;
tableAlias : string ;
orgTable : string ;
type : string ;
length : number ;
key? : string ;
} [ ] ;
2022-11-02 14:18:50 +01:00
try {
queryResult = await new Promise < unknown [ ] > ( ( resolve , reject ) = > {
2022-11-04 16:31:10 +01:00
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2022-11-08 14:05:54 +01:00
( connection as any ) . query ( query , [ ] , async ( err : any , res : any , fields : FieldData [ ] ) = > { // <- fields is not natively typed or documented
2022-11-02 14:18:50 +01:00
if ( err ) reject ( err ) ;
else {
const remappedResponse = [ ] ;
2022-11-08 14:05:54 +01:00
if ( res ) {
for ( const row of res ) {
for ( const key in row ) {
if ( Buffer . isBuffer ( row [ key ] ) )
row [ key ] = row [ key ] . toString ( 'binary' ) ;
2022-11-08 15:53:21 +01:00
else if ( typeof row [ key ] === 'function' ) {
const result = await new Promise ( ( resolve , reject ) = > {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
row [ key ] ( ( err : any , _name : string , event : any ) = > {
if ( err )
return reject ( err ) ;
const buffArr : Buffer [ ] = [ ] ;
event . on ( 'data' , ( chunk : Buffer ) = > {
buffArr . push ( chunk ) ;
} ) ;
event . on ( 'end' , ( ) = > {
resolve ( Buffer . concat ( buffArr ) ) ;
} ) ;
} ) ;
} ) ;
row [ key ] = result ;
}
2022-11-08 14:05:54 +01:00
}
2022-11-02 14:18:50 +01:00
2022-11-08 14:05:54 +01:00
remappedResponse . push ( row ) ;
}
2022-11-02 14:18:50 +01:00
}
2022-11-08 14:05:54 +01:00
if ( fields ) {
remappedFields = fields . map ( field = > {
2022-11-10 15:52:31 +01:00
const fieldType = this . _getType ( this . _types [ field . type ] , field . subType ) ;
2022-11-09 17:41:31 +01:00
2022-11-08 14:05:54 +01:00
return {
name : field.alias ,
alias : field.alias ,
orgName : field.field ,
schema : args.schema ,
table : field.relation ,
tableAlias : field.relation ,
orgTable : field.relation ,
2022-11-09 17:41:31 +01:00
type : fieldType ,
length : fieldType === 'TIMESTAMP' ? 4 : field.length ,
2022-11-08 14:05:54 +01:00
key : undefined as string
} ;
} ) ;
}
2022-11-04 16:31:10 +01:00
2022-11-02 14:18:50 +01:00
resolve ( remappedResponse ) ;
}
} ) ;
} ) ;
2022-11-08 14:05:54 +01:00
if ( args . details ) {
2022-11-16 15:16:12 +01:00
if ( remappedFields ? . length ) {
2022-11-08 14:05:54 +01:00
paramsArr = remappedFields . map ( field = > {
return {
table : field.orgTable ,
schema : field.schema
} ;
} ) . filter ( ( val , i , arr ) = > arr . findIndex ( el = > el . table === val . table ) === i ) ;
for ( const paramObj of paramsArr ) {
if ( ! paramObj . table || ! paramObj . schema ) continue ;
try { // Column details
const indexes = await this . getTableIndexes ( paramObj ) ;
remappedFields = remappedFields . map ( field = > {
const fieldIndex = indexes . find ( i = > i . column === field . name ) ;
if ( fieldIndex ) {
const key = fieldIndex . type === 'PRIMARY KEY' ? 'pri' : fieldIndex . type === 'UNIQUE' ? 'uni' : 'fk' ;
field = { . . . field , key } ;
}
return field ;
} ) ;
}
catch ( err ) {
2022-11-16 15:16:12 +01:00
if ( args . autocommit ) {
2022-11-08 14:05:54 +01:00
this . _runningConnections . delete ( args . tabUid ) ;
2022-11-16 15:16:12 +01:00
( connection as firebird . Database ) . detach ( ) ;
}
2022-11-15 16:46:12 +01:00
this . destroy ( ) ;
2022-11-08 14:05:54 +01:00
reject ( err ) ;
}
try { // Key usage (foreign keys)
const response = await this . getKeyUsage ( paramObj ) ;
keysArr = keysArr ? [ . . . keysArr , . . . response ] : response ;
}
catch ( err ) {
2022-11-16 15:16:12 +01:00
if ( args . autocommit ) {
2022-11-08 14:05:54 +01:00
this . _runningConnections . delete ( args . tabUid ) ;
2022-11-16 15:16:12 +01:00
( connection as firebird . Database ) . detach ( ) ;
}
2022-11-15 16:46:12 +01:00
this . destroy ( ) ;
2022-11-08 14:05:54 +01:00
reject ( err ) ;
}
}
}
}
2022-11-02 14:18:50 +01:00
}
catch ( err ) {
reject ( err ) ;
2022-11-15 16:46:12 +01:00
this . destroy ( ) ;
2022-11-16 15:16:12 +01:00
if ( args . autocommit )
( connection as firebird . Database ) . detach ( ) ;
2022-11-02 14:18:50 +01:00
}
timeStop = new Date ( ) ;
resolve ( {
duration : timeStop.getTime ( ) - timeStart . getTime ( ) ,
rows : Array.isArray ( queryResult ) ? queryResult . some ( el = > Array . isArray ( el ) ) ? [ ] : queryResult : false ,
report : null ,
fields : remappedFields ,
keys : keysArr
} ) ;
} ) ( ) ;
} ) ;
resultsArr . push ( { rows , report , fields , keys , duration } ) ;
}
2022-11-16 15:16:12 +01:00
if ( args . autocommit )
( connection as firebird . Database ) . detach ( ) ;
2022-11-16 10:12:44 +01:00
2022-11-02 14:18:50 +01:00
const result = resultsArr . length === 1 ? resultsArr [ 0 ] : resultsArr ;
return result as unknown as T ;
}
getVariables ( ) : null [ ] {
return [ ] ;
}
getCollations ( ) : null [ ] {
return [ ] ;
}
}