2021-11-13 23:00:53 +01:00
'use strict' ;
import sqlite from 'better-sqlite3' ;
import { AntaresCore } from '../AntaresCore' ;
import dataTypes from 'common/data-types/mysql' ;
2021-11-18 19:43:08 +01:00
import { NUMBER , FLOAT , TIME , DATETIME } from 'common/fieldTypes' ;
2021-11-13 23:00:53 +01:00
export class SQLiteClient extends AntaresCore {
constructor ( args ) {
super ( args ) ;
this . _schema = null ;
}
_getTypeInfo ( type ) {
return dataTypes
. reduce ( ( acc , group ) => [ ... acc , ... group . types ] , [ ] )
. filter ( _type => _type . name === type . toUpperCase ( ) ) [ 0 ] ;
}
/ * *
* @ memberof SQLiteClient
* /
async connect ( ) {
this . _connection = sqlite ( this . _params . databasePath , {
2021-11-16 13:21:33 +01:00
fileMustExist : true ,
readonly : this . _params . readonly
2021-11-13 23:00:53 +01:00
} ) ;
}
/ * *
* @ memberof SQLiteClient
* /
destroy ( ) { }
/ * *
* Executes an USE query
*
* @ memberof SQLiteClient
* /
use ( ) { }
/ * *
* @ param { Array } schemas list
* @ returns { Array . < Object > } databases scructure
* @ memberof SQLiteClient
* /
async getStructure ( schemas ) {
const { rows : databases } = await this . raw ( 'SELECT * FROM pragma_database_list' ) ;
const filteredDatabases = databases ;
const tablesArr = [ ] ;
const triggersArr = [ ] ;
let schemaSize = 0 ;
for ( const db of filteredDatabases ) {
if ( ! schemas . has ( db . name ) ) continue ;
2021-11-19 13:13:35 +01:00
let { rows : tables } = await this . raw ( `
SELECT *
FROM "${db.name}" . sqlite _master
WHERE type IN ( 'table' , 'view' )
AND name NOT LIKE 'sqlite_%'
ORDER BY name
` );
2021-11-13 23:00:53 +01:00
if ( tables . length ) {
tables = tables . map ( table => {
table . Db = db . name ;
return table ;
} ) ;
tablesArr . push ( ... tables ) ;
}
2021-11-19 15:36:07 +01:00
let { rows : triggers } = await this . raw ( ` SELECT * FROM " ${ db . name } ".sqlite_master WHERE type='trigger' ` ) ;
2021-11-13 23:00:53 +01:00
if ( triggers . length ) {
triggers = triggers . map ( trigger => {
trigger . Db = db . name ;
return trigger ;
} ) ;
triggersArr . push ( ... triggers ) ;
}
}
return filteredDatabases . map ( db => {
if ( schemas . has ( db . name ) ) {
// TABLES
const remappedTables = tablesArr . filter ( table => table . Db === db . name ) . map ( table => {
2021-11-15 18:09:34 +01:00
const tableSize = 0 ;
2021-11-13 23:00:53 +01:00
schemaSize += tableSize ;
return {
name : table . name ,
type : table . type ,
rows : false ,
size : false
} ;
} ) ;
// TRIGGERS
const remappedTriggers = triggersArr . filter ( trigger => trigger . Db === db . name ) . map ( trigger => {
return {
2021-11-19 15:36:07 +01:00
name : trigger . name ,
table : trigger . tbl _name
2021-11-13 23:00:53 +01:00
} ;
} ) ;
return {
name : db . name ,
size : schemaSize ,
tables : remappedTables ,
functions : [ ] ,
procedures : [ ] ,
triggers : remappedTriggers ,
schedulers : [ ]
} ;
}
else {
return {
name : db . name ,
size : 0 ,
tables : [ ] ,
functions : [ ] ,
procedures : [ ] ,
triggers : [ ] ,
schedulers : [ ]
} ;
}
} ) ;
}
/ * *
* @ param { Object } params
* @ param { String } params . schema
* @ param { String } params . table
* @ returns { Object } table scructure
* @ memberof SQLiteClient
* /
async getTableColumns ( { schema , table } ) {
2021-11-18 11:36:46 +01:00
const { rows : fields } = await this . raw ( ` SELECT * FROM " ${ schema } ".pragma_table_info(' ${ table } ') ` ) ;
2021-11-13 23:00:53 +01:00
2021-11-18 11:36:46 +01:00
return fields . map ( field => {
const [ type , length ] = field . type . includes ( '(' )
? field . type . replace ( ')' , '' ) . split ( '(' ) . map ( el => {
if ( ! isNaN ( el ) ) el = + el ;
return el ;
} )
: [ field . type , null ] ;
2021-11-13 23:00:53 +01:00
return {
2021-11-18 11:36:46 +01:00
name : field . name ,
key : null ,
2021-11-18 19:43:08 +01:00
type : type . trim ( ) ,
2021-11-18 11:36:46 +01:00
schema : schema ,
table : table ,
numPrecision : [ ... NUMBER , ... FLOAT ] . includes ( type ) ? length : null ,
datePrecision : null ,
charLength : ! [ ... NUMBER , ... FLOAT ] . includes ( type ) ? length : null ,
nullable : ! field . notnull ,
unsigned : null ,
zerofill : null ,
order : field . cid + 1 ,
default : field . dflt _value ,
charset : null ,
collation : null ,
autoIncrement : false ,
onUpdate : null ,
comment : ''
2021-11-13 23:00:53 +01:00
} ;
} ) ;
}
/ * *
* @ param { Object } params
* @ param { String } params . schema
* @ param { String } params . table
* @ returns { Object } table row count
* @ memberof SQLiteClient
* /
async getTableApproximateCount ( { schema , table } ) {
2021-11-15 18:09:34 +01:00
const { rows } = await this . raw ( ` SELECT COUNT(*) AS count FROM " ${ schema } "." ${ table } " ` ) ;
2021-11-13 23:00:53 +01:00
return rows . length ? rows [ 0 ] . count : 0 ;
}
/ * *
* @ param { Object } params
* @ param { String } params . schema
* @ param { String } params . table
* @ returns { Object } table options
* @ memberof SQLiteClient
* /
async getTableOptions ( { schema , table } ) {
2021-11-18 11:36:46 +01:00
return { name : table } ;
2021-11-13 23:00:53 +01:00
}
/ * *
* @ param { Object } params
* @ param { String } params . schema
* @ param { String } params . table
* @ returns { Object } table indexes
* @ memberof SQLiteClient
* /
async getTableIndexes ( { schema , table } ) {
2021-11-16 12:27:51 +01:00
const remappedIndexes = [ ] ;
const { rows : primaryKeys } = await this . raw ( ` SELECT * FROM " ${ schema } ".pragma_table_info(' ${ table } ') WHERE pk != 0 ` ) ;
for ( const key of primaryKeys ) {
remappedIndexes . push ( {
name : 'PRIMARY' ,
column : key . name ,
indexType : null ,
type : 'PRIMARY' ,
cardinality : null ,
comment : '' ,
indexComment : ''
} ) ;
}
2021-11-13 23:00:53 +01:00
2021-11-16 12:27:51 +01:00
const { rows : indexes } = await this . raw ( ` SELECT * FROM " ${ schema } ".pragma_index_list(' ${ table } '); ` ) ;
for ( const index of indexes ) {
const { rows : details } = await this . raw ( ` SELECT * FROM " ${ schema } ".pragma_index_info(' ${ index . name } '); ` ) ;
for ( const detail of details ) {
remappedIndexes . push ( {
name : index . name ,
column : detail . name ,
indexType : null ,
type : index . unique === 1 ? 'UNIQUE' : 'INDEX' ,
cardinality : null ,
comment : '' ,
indexComment : ''
} ) ;
}
}
return remappedIndexes ;
2021-11-13 23:00:53 +01:00
}
/ * *
* @ param { Object } params
* @ param { String } params . schema
* @ param { String } params . table
* @ returns { Object } table key usage
* @ memberof SQLiteClient
* /
async getKeyUsage ( { schema , table } ) {
2021-11-18 11:36:46 +01:00
const { rows } = await this . raw ( ` SELECT * FROM " ${ schema } ".pragma_foreign_key_list(' ${ table } '); ` ) ;
2021-11-13 23:00:53 +01:00
return rows . map ( field => {
return {
2021-11-18 11:36:46 +01:00
schema : schema ,
table : table ,
field : field . from ,
position : field . id + 1 ,
constraintPosition : null ,
constraintName : field . id ,
refSchema : schema ,
refTable : field . table ,
refField : field . to ,
onUpdate : field . on _update ,
onDelete : field . on _delete
2021-11-13 23:00:53 +01:00
} ;
} ) ;
}
async getUsers ( ) { }
/ * *
* SHOW CREATE VIEW
*
* @ returns { Array . < Object > } view informations
* @ memberof SQLiteClient
* /
async getViewInformations ( { schema , view } ) {
2021-11-19 13:13:35 +01:00
const sql = ` SELECT "sql" FROM " ${ schema } ".sqlite_master WHERE "type"='view' AND name=' ${ view } ' ` ;
2021-11-13 23:00:53 +01:00
const results = await this . raw ( sql ) ;
return results . rows . map ( row => {
return {
2021-11-19 13:13:35 +01:00
sql : row . sql . match ( / ( ? < = A S ) . * ? $ / g s ) [ 0 ] ,
name : view
2021-11-13 23:00:53 +01:00
} ;
} ) [ 0 ] ;
}
/ * *
* DROP VIEW
*
* @ returns { Array . < Object > } parameters
* @ memberof SQLiteClient
* /
async dropView ( params ) {
2021-11-19 13:13:35 +01:00
const sql = ` DROP VIEW " ${ params . schema } "." ${ params . view } " ` ;
2021-11-13 23:00:53 +01:00
return await this . raw ( sql ) ;
}
/ * *
* ALTER VIEW
*
* @ returns { Array . < Object > } parameters
* @ memberof SQLiteClient
* /
async alterView ( params ) {
const { view } = params ;
2021-11-19 15:36:07 +01:00
try {
await this . dropView ( { schema : view . schema , view : view . oldName } ) ;
await this . createView ( view ) ;
}
catch ( err ) {
return Promise . reject ( err ) ;
}
2021-11-13 23:00:53 +01:00
}
/ * *
* CREATE VIEW
*
* @ returns { Array . < Object > } parameters
* @ memberof SQLiteClient
* /
async createView ( params ) {
2021-11-19 13:13:35 +01:00
const sql = ` CREATE VIEW " ${ params . schema } "." ${ params . name } " AS ${ params . sql } ` ;
2021-11-13 23:00:53 +01:00
return await this . raw ( sql ) ;
}
/ * *
* SHOW CREATE TRIGGER
*
* @ returns { Array . < Object > } view informations
* @ memberof SQLiteClient
* /
async getTriggerInformations ( { schema , trigger } ) {
2021-11-19 15:36:07 +01:00
const sql = ` SELECT "sql" FROM " ${ schema } ".sqlite_master WHERE "type"='trigger' AND name=' ${ trigger } ' ` ;
2021-11-13 23:00:53 +01:00
const results = await this . raw ( sql ) ;
return results . rows . map ( row => {
return {
2021-11-19 15:36:07 +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 ] ,
name : trigger ,
table : row . sql . match ( / ( ? < = O N ` ) . * ? ( ? = ` ) / g s ) [ 0 ] ,
activation : row . sql . match ( / ( B E F O R E | A F T E R ) / g s ) [ 0 ] ,
event : row . sql . match ( / ( I N S E R T | U P D A T E | D E L E T E ) / g s ) [ 0 ]
2021-11-13 23:00:53 +01:00
} ;
} ) [ 0 ] ;
}
/ * *
* DROP TRIGGER
*
* @ returns { Array . < Object > } parameters
* @ memberof SQLiteClient
* /
async dropTrigger ( params ) {
const sql = ` DROP TRIGGER \` ${ params . schema } \` . \` ${ params . trigger } \` ` ;
return await this . raw ( sql ) ;
}
/ * *
* ALTER TRIGGER
*
* @ returns { Array . < Object > } parameters
* @ memberof SQLiteClient
* /
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 ( { 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 ) ;
}
}
/ * *
* CREATE TRIGGER
*
* @ returns { Array . < Object > } parameters
* @ memberof SQLiteClient
* /
async createTrigger ( params ) {
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 } ) ;
}
/ * *
* SHOW COLLATION
*
* @ returns { Array . < Object > } collations list
* @ memberof SQLiteClient
* /
async getCollations ( ) {
return [ ] ;
}
/ * *
* SHOW VARIABLES
*
* @ returns { Array . < Object > } variables list
* @ memberof SQLiteClient
* /
async getVariables ( ) {
return [ ] ;
}
/ * *
* SHOW ENGINES
*
* @ returns { Array . < Object > } engines list
* @ memberof SQLiteClient
* /
async getEngines ( ) {
return {
name : 'SQLite' ,
support : 'YES' ,
comment : '' ,
isDefault : true
} ;
}
/ * *
* SHOW VARIABLES LIKE '%vers%'
*
* @ returns { Array . < Object > } version parameters
* @ memberof SQLiteClient
* /
async getVersion ( ) {
const os = require ( 'os' ) ;
const sql = 'SELECT sqlite_version() AS version' ;
const { rows } = await this . raw ( sql ) ;
return {
number : rows [ 0 ] . version ,
name : 'SQLite' ,
arch : process . arch ,
os : ` ${ os . type ( ) } ${ os . release ( ) } `
} ;
}
async getProcesses ( ) { }
async killProcess ( ) { }
/ * *
* CREATE TABLE
*
2021-11-18 11:36:46 +01:00
* @ returns { Promise < null > }
2021-11-13 23:00:53 +01:00
* @ memberof SQLiteClient
* /
async createTable ( params ) {
const {
schema ,
fields ,
foreigns ,
indexes ,
options
} = params ;
const newColumns = [ ] ;
const newIndexes = [ ] ;
2021-11-18 11:36:46 +01:00
const manageIndexes = [ ] ;
2021-11-13 23:00:53 +01:00
const newForeigns = [ ] ;
2021-11-18 11:36:46 +01:00
let sql = ` CREATE TABLE " ${ schema } "." ${ options . name } " ` ;
2021-11-13 23:00:53 +01:00
// ADD FIELDS
fields . forEach ( field => {
const typeInfo = this . _getTypeInfo ( field . type ) ;
2021-11-18 11:36:46 +01:00
const length = typeInfo ? . length ? field . enumValues || field . numLength || field . charLength || field . datePrecision : false ;
2021-11-13 23:00:53 +01:00
2021-11-18 11:36:46 +01:00
newColumns . push ( ` " ${ field . name } "
$ { field . type . toUpperCase ( ) } $ { length && length !== true ? ` ( ${ length } ) ` : '' }
2021-11-13 23:00:53 +01:00
$ { field . unsigned ? 'UNSIGNED' : '' }
$ { field . nullable ? 'NULL' : 'NOT NULL' }
$ { field . autoIncrement ? 'AUTO_INCREMENT' : '' }
$ { field . default ? ` DEFAULT ${ field . default } ` : '' }
$ { field . onUpdate ? ` ON UPDATE ${ field . onUpdate } ` : '' } ` );
} ) ;
// ADD INDEX
indexes . forEach ( index => {
2021-11-18 11:36:46 +01:00
const fields = index . fields . map ( field => ` " ${ field } " ` ) . join ( ',' ) ;
const type = index . type ;
2021-11-13 23:00:53 +01:00
if ( type === 'PRIMARY' )
newIndexes . push ( ` PRIMARY KEY ( ${ fields } ) ` ) ;
2021-11-18 11:36:46 +01:00
else
manageIndexes . push ( ` CREATE ${ type === 'UNIQUE' ? type : '' } INDEX " ${ index . name } " ON " ${ options . name } " ( ${ fields } ) ` ) ;
2021-11-13 23:00:53 +01:00
} ) ;
// ADD FOREIGN KEYS
foreigns . forEach ( foreign => {
2021-11-18 11:36:46 +01:00
newForeigns . push ( ` CONSTRAINT " ${ foreign . constraintName } " FOREIGN KEY (" ${ foreign . field } ") REFERENCES " ${ foreign . refTable } " (" ${ foreign . refField } ") ON UPDATE ${ foreign . onUpdate } ON DELETE ${ foreign . onDelete } ` ) ;
2021-11-13 23:00:53 +01:00
} ) ;
2021-11-18 11:36:46 +01:00
sql = ` ${ sql } ( ${ [ ... newColumns , ... newIndexes , ... newForeigns ] . join ( ', ' ) } ) ` ;
if ( manageIndexes . length ) sql = ` ${ sql } ; ${ manageIndexes . join ( ';' ) } ` ;
2021-11-13 23:00:53 +01:00
return await this . raw ( sql ) ;
}
/ * *
* ALTER TABLE
*
2021-11-18 11:36:46 +01:00
* @ returns { Promise < null > }
2021-11-13 23:00:53 +01:00
* @ memberof SQLiteClient
* /
async alterTable ( params ) {
2021-11-18 11:36:46 +01:00
try {
await this . raw ( 'BEGIN TRANSACTION' ) ;
await this . raw ( 'PRAGMA foreign_keys = 0' ) ;
const tmpName = ` Antares_ ${ params . table } _tmp ` ;
await this . raw ( ` CREATE TABLE " ${ tmpName } " AS SELECT * FROM " ${ params . table } " ` ) ;
await this . dropTable ( params ) ;
const createTableParams = {
schema : params . schema ,
fields : params . tableStructure . fields ,
foreigns : params . tableStructure . foreigns ,
indexes : params . tableStructure . indexes . filter ( index => ! index . name . includes ( 'sqlite_autoindex' ) ) ,
options : { name : params . tableStructure . name }
} ;
await this . createTable ( createTableParams ) ;
const insertFields = createTableParams . fields
. filter ( field => {
return (
params . additions . every ( add => add . name !== field . name ) &&
params . deletions . every ( del => del . name !== field . name )
) ;
} )
. reduce ( ( acc , curr ) => {
acc . push ( ` " ${ curr . name } " ` ) ;
return acc ;
} , [ ] ) ;
2021-11-13 23:00:53 +01:00
2021-11-18 11:36:46 +01:00
const selectFields = insertFields . map ( field => {
const renamedField = params . changes . find ( change => ` " ${ change . name } " ` === field ) ;
if ( renamedField )
return ` " ${ renamedField . orgName } " ` ;
return field ;
} ) ;
2021-11-13 23:00:53 +01:00
2021-11-18 11:36:46 +01:00
await this . raw ( ` INSERT INTO " ${ createTableParams . options . name } " ( ${ insertFields . join ( ',' ) } ) SELECT ${ selectFields . join ( ',' ) } FROM " ${ tmpName } " ` ) ;
2021-11-13 23:00:53 +01:00
2021-11-18 11:36:46 +01:00
await this . dropTable ( { schema : params . schema , table : tmpName } ) ;
await this . raw ( 'PRAGMA foreign_keys = 1' ) ;
await this . raw ( 'COMMIT' ) ;
}
catch ( err ) {
await this . raw ( 'ROLLBACK' ) ;
return Promise . reject ( err ) ;
}
2021-11-13 23:00:53 +01:00
}
/ * *
* DUPLICATE TABLE
*
2021-11-18 11:36:46 +01:00
* @ returns { Promise < null > }
2021-11-13 23:00:53 +01:00
* @ memberof SQLiteClient
* /
2021-11-18 11:36:46 +01:00
async duplicateTable ( params ) { // TODO: retrive table informations and create a copy
const sql = ` CREATE TABLE " ${ params . schema } "." ${ params . table } _copy" AS SELECT * FROM " ${ params . schema } "." ${ params . table } " ` ;
2021-11-13 23:00:53 +01:00
return await this . raw ( sql ) ;
}
/ * *
* TRUNCATE TABLE
*
2021-11-18 11:36:46 +01:00
* @ returns { Promise < null > }
2021-11-13 23:00:53 +01:00
* @ memberof SQLiteClient
* /
async truncateTable ( params ) {
2021-11-18 11:36:46 +01:00
const sql = ` DELETE FROM " ${ params . schema } "." ${ params . table } " ` ;
2021-11-13 23:00:53 +01:00
return await this . raw ( sql ) ;
}
/ * *
* DROP TABLE
*
2021-11-18 11:36:46 +01:00
* @ returns { Promise < null > }
2021-11-13 23:00:53 +01:00
* @ memberof SQLiteClient
* /
async dropTable ( params ) {
2021-11-18 11:36:46 +01:00
const sql = ` DROP TABLE " ${ params . schema } "." ${ params . table } " ` ;
2021-11-13 23:00:53 +01:00
return await this . raw ( sql ) ;
}
/ * *
* @ returns { String } SQL string
* @ memberof SQLiteClient
* /
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' ;
2021-11-15 18:09:34 +01:00
fromRaw += this . _query . from ? ` ${ this . _query . schema ? ` " ${ this . _query . schema } ". ` : '' } " ${ this . _query . from } " ` : '' ;
2021-11-13 23:00:53 +01:00
// WHERE
2021-11-15 18:09:34 +01:00
const whereArray = this . _query . where
. reduce ( this . _reducer , [ ] )
? . map ( clausole => clausole . replace ( '= null' , 'IS NULL' ) ) ;
2021-11-13 23:00:53 +01:00
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 ] ) ;
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
const limitRaw = this . _query . limit . length ? ` LIMIT ${ this . _query . limit . join ( ', ' ) } ` : '' ;
// OFFSET
const offsetRaw = this . _query . offset . length ? ` OFFSET ${ this . _query . offset . join ( ', ' ) } ` : '' ;
return ` ${ selectRaw } ${ updateRaw ? 'UPDATE' : '' } ${ insertRaw ? 'INSERT ' : '' } ${ this . _query . delete ? 'DELETE ' : '' } ${ fromRaw } ${ updateRaw } ${ whereRaw } ${ groupByRaw } ${ orderByRaw } ${ limitRaw } ${ offsetRaw } ${ 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 SQLiteClient
* /
async raw ( sql , args ) {
if ( process . env . NODE _ENV === 'development' ) this . _logger ( sql ) ; // TODO: replace BLOB content with a placeholder
args = {
nest : false ,
details : false ,
split : true ,
comments : true ,
... args
} ;
if ( ! args . comments )
sql = sql . replace ( /(\/\*(.|[\r\n])*?\*\/)|(--(.*|[\r\n]))/gm , '' ) ; // Remove comments
const resultsArr = [ ] ;
2021-11-16 12:27:51 +01:00
let paramsArr = [ ] ;
2021-11-13 23:00:53 +01:00
const queries = args . split
? sql . split ( /((?:[^;'"]*(?:"(?:\\.|[^"])*"|'(?:\\.|[^'])*')[^;'"]*)+)|;/gm )
. filter ( Boolean )
. map ( q => q . trim ( ) )
: [ sql ] ;
const connection = this . _connection ;
for ( const query of queries ) {
if ( ! query ) continue ;
const timeStart = new Date ( ) ;
let timeStop ;
const keysArr = [ ] ;
const { rows , report , fields , keys , duration } = await new Promise ( ( resolve , reject ) => {
2021-11-16 12:27:51 +01:00
( async ( ) => {
let queryResult ;
let affectedRows ;
let fields ;
const detectedTypes = { } ;
2021-11-16 12:56:03 +01:00
try {
const stmt = connection . prepare ( query ) ;
if ( stmt . reader ) {
queryResult = stmt . all ( ) ;
fields = stmt . columns ( ) ;
if ( queryResult . length ) {
fields . forEach ( field => {
detectedTypes [ field . name ] = typeof queryResult [ 0 ] [ field . name ] ;
} ) ;
}
}
else {
const info = queryResult = stmt . run ( ) ;
affectedRows = info . changes ;
2021-11-16 12:27:51 +01:00
}
}
2021-11-16 12:56:03 +01:00
catch ( err ) {
reject ( err ) ;
2021-11-15 18:09:34 +01:00
}
2021-11-13 23:00:53 +01:00
2021-11-16 12:27:51 +01:00
timeStop = new Date ( ) ;
let remappedFields = fields
? fields . map ( field => {
2021-11-18 19:43:08 +01:00
let [ parsedType , length ] = field . type ? . includes ( '(' )
? field . type . replace ( ')' , '' ) . split ( '(' ) . map ( el => {
if ( ! isNaN ( el ) )
el = + el ;
else
el = el . trim ( ) ;
return el ;
} )
: [ field . type , null ] ;
if ( [ ... TIME , ... DATETIME ] . includes ( parsedType ) ) {
const firstNotNull = queryResult . find ( res => res [ field . name ] !== null ) ;
if ( firstNotNull [ field . name ] . includes ( '.' ) )
length = firstNotNull [ field . name ] . split ( '.' ) . pop ( ) . length ;
}
2021-11-18 11:36:46 +01:00
2021-11-16 12:27:51 +01:00
return {
name : field . name ,
alias : field . name ,
orgName : field . column ,
schema : field . database ,
table : field . table ,
tableAlias : field . table ,
orgTable : field . table ,
2021-11-18 19:43:08 +01:00
type : field . type !== null ? parsedType : detectedTypes [ field . name ] ,
length
2021-11-16 12:27:51 +01:00
} ;
} ) . filter ( Boolean )
: [ ] ;
if ( args . details ) {
paramsArr = remappedFields . map ( field => {
return {
table : field . table ,
schema : field . schema
} ;
} ) . 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 {
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 ) {
// if (detailedField) {
// const length = detailedField.numPrecision || detailedField.charLength || detailedField.datePrecision || null;
// field = { ...field, ...detailedField, length };
// }
if ( fieldIndex ) {
const key = fieldIndex . type === 'PRIMARY' ? 'pri' : fieldIndex . type === 'UNIQUE' ? 'uni' : 'mul' ;
field = { ... field , key } ;
} ;
}
return field ;
} ) ;
}
catch ( err ) {
reject ( err ) ;
}
}
}
2021-11-13 23:00:53 +01:00
2021-11-16 12:27:51 +01:00
resolve ( {
duration : timeStop - timeStart ,
rows : Array . isArray ( queryResult ) ? queryResult . some ( el => Array . isArray ( el ) ) ? [ ] : queryResult : false ,
report : affectedRows !== undefined ? { affectedRows } : null ,
fields : remappedFields ,
keys : keysArr
} ) ;
} ) ( ) ;
2021-11-13 23:00:53 +01:00
} ) ;
resultsArr . push ( { rows , report , fields , keys , duration } ) ;
}
return resultsArr . length === 1 ? resultsArr [ 0 ] : resultsArr ;
}
}