import { Client as PostgresClient , types as PostgresTypes } from 'pg' ; import { csv } from '@mds-core/mds-utils' ; import logger from '@mds-core/mds-logger' ; import schema from './schema' ; export interface PGInfo { user ? : string ; database ? : string ; host ? : string ; password ? : string ; port ? : number ; client_type ? : string ; } export class MDSPostgresClient extends PostgresClient { public client_type : string ; public connected : boolean ; public constructor ( params ) { const { client_type , ... rest_params } = params ; if ( client_type ) { super ( rest_params ) ; this . client_type = client_type ; this . connected = false ; } else { super ( params ) ; this . client_type = 'read_only' ; this . connected = false ; } } public setConnected ( connected ) { this . connected = connected ; } } export function configureClient ( pg_info ) { PostgresTypes . setTypeParser ( 20 , parseInt ) ; const client = new MDSPostgresClient ( { user : pg_info . user , database : pg_info . database , host : pg_info . host || 'localhost' , password : pg_info . password , port : pg_info . port || 5432 , client_type : pg_info . client_type } ) ; client . on ( 'end' , ( ) => { client . setConnected ( false ) ; logger . info ( 'disconnected' , client . client_type , 'client from postgres' ) ; } ) ; client . on ( 'error' , async ( err ) => { logger . error ( 'pg client error event' , err . stack ) ; } ) ; client . on ( 'notice' , async ( msg ) => { logger . warn ( 'notice:' , msg ) ; } ) ; if ( ! client ) { throw Error ( 'no way to connect to PG' ) ; } return client ; } export function vals_sql ( cols ) { return csv ( cols . filter ( col => col !== schema . COLUMN . id ) . map ( ( col , i ) => `$${ i + 1 }` ) ) ; } export function cols_sql ( cols ) { return csv ( cols . filter ( col => col !== schema . COLUMN . id ) ) ; } type DBValueType = null | string | number | boolean | string [ ] ; export function vals_list ( cols , obj ) { return cols . filter ( col => col !== schema . COLUMN . id ) . map ( col => { const value = obj [ col ] ; if ( value === undefined || value === null ) { return null ; } if ( typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' || Array . isArray ( value ) ) { return value ; } return JSON . stringify ( value ) ; } ) ; } export function to_sql ( value ) { if ( value === true || value === false ) { return `${ value }` ; } if ( typeof value === 'number' ) { return `${ value }` ; } if ( typeof value === 'string' ) { return `'${ value }'` ; } if ( Array . isArray ( value ) ) { return `'{${ csv ( value . map ( o => `"${ o }"` ) ) }}'` ; } if ( value === null ) { return 'null' ; } if ( typeof value === 'object' ) { throw new Error ( `can't render object to sql: ${ JSON . stringify ( value ) }` ) ; } return 'null' ; } export async function logSql ( sql , ... values ) { if ( process . env . PG_DEBUG !== 'true' ) { return ; } let out ; if ( typeof values === 'undefined' ) { out = [ ] ; } else if ( typeof values !== 'string' ) { out = values . map ( val => { return String ( val ) ; } ) ; } else { out = values ; } logger . info ( 'sql>' , sql , out ) ; } export class SqlVals { public vals : ( string | number ) [ ] ; private index : number ; public constructor ( ) { this . vals = [ ] ; this . index = 1 ; } public add ( value ) { this . vals . push ( value ) ; const literal = `$${ this . index }` ; this . index += 1 ; return literal ; } public values ( ) { return this . vals . slice ( ) ; } } export const SqlExecuter = ( client ) => async ( command , values = [ ] ) => { await logSql ( command , values ) ; return client . query ( command , values ) ; } ; export type SqlExecuterFunction = ReturnType < typeof SqlExecuter > ; export function arrayToInQueryFormat ( arr ) { return `(${ arr . map ( currElem => `"${ currElem }"` ) })` ; }