2022-10-10 22:35:21 +08:00
import crypto from 'crypto' ;
2023-02-16 23:56:00 +08:00
import Database from './database' ;
2022-10-14 14:51:19 +08:00
import { IdentifierError } from './errors/identifier-error' ;
2022-11-30 10:57:41 +08:00
import { Model } from './model' ;
2023-03-05 14:45:56 +08:00
import lodash from 'lodash' ;
2022-10-10 15:40:12 +08:00
type HandleAppendsQueryOptions = {
templateModel : any ;
queryPromises : Array < any > ;
} ;
export async function handleAppendsQuery ( options : HandleAppendsQueryOptions ) {
const { templateModel , queryPromises } = options ;
2022-11-30 10:57:41 +08:00
if ( ! templateModel ) {
return [ ] ;
}
2022-10-14 17:29:14 +08:00
const primaryKey = templateModel . constructor . primaryKeyAttribute ;
2022-10-10 15:40:12 +08:00
const results = await Promise . all ( queryPromises ) ;
let rows : Array < Model > ;
for ( const appendedResult of results ) {
if ( ! rows ) {
rows = appendedResult . rows ;
if ( rows . length == 0 ) {
return [ ] ;
}
const modelOptions = templateModel [ '_options' ] ;
for ( const row of rows ) {
row [ '_options' ] = {
. . . row [ '_options' ] ,
include : modelOptions [ 'include' ] ,
includeNames : modelOptions [ 'includeNames' ] ,
includeMap : modelOptions [ 'includeMap' ] ,
} ;
}
continue ;
}
for ( let i = 0 ; i < appendedResult . rows . length ; i ++ ) {
2022-10-14 17:29:14 +08:00
const appendingRow = appendedResult . rows [ i ] ;
2022-10-10 15:40:12 +08:00
const key = appendedResult . include . association ;
2022-10-14 17:29:14 +08:00
const val = appendingRow . get ( key ) ;
const rowKey = appendingRow . get ( primaryKey ) ;
const targetIndex = rows . findIndex ( ( row ) = > row . get ( primaryKey ) === rowKey ) ;
if ( targetIndex === - 1 ) {
throw new Error ( 'target row not found' ) ;
}
2022-10-10 15:40:12 +08:00
2022-10-14 17:29:14 +08:00
rows [ targetIndex ] . set ( key , val , {
2022-10-13 18:44:12 +08:00
raw : true ,
} ) ;
2022-10-10 15:40:12 +08:00
}
}
return rows ;
}
2022-10-13 11:15:27 +08:00
export function md5 ( value : string ) {
return crypto . createHash ( 'md5' ) . update ( value ) . digest ( 'hex' ) ;
}
2022-10-14 14:51:19 +08:00
const MAX_IDENTIFIER_LENGTH = 63 ;
export function checkIdentifier ( value : string ) {
if ( value . length > MAX_IDENTIFIER_LENGTH ) {
throw new IdentifierError ( ` Identifier ${ value } is too long ` ) ;
}
}
2023-02-13 21:38:47 +08:00
export function getTableName ( collectionName : string , options ) {
return options . underscored ? snakeCase ( collectionName ) : collectionName ;
}
export function snakeCase ( name : string ) {
return require ( 'sequelize' ) . Utils . underscore ( name ) ;
}
2023-02-16 10:53:04 +08:00
2023-02-23 14:13:32 +08:00
function patchShowConstraintsQuery ( queryGenerator , db ) {
queryGenerator . showConstraintsQuery = ( tableName , constraintName ) = > {
const lines = [
'SELECT constraint_catalog AS "constraintCatalog",' ,
'constraint_schema AS "constraintSchema",' ,
'constraint_name AS "constraintName",' ,
'table_catalog AS "tableCatalog",' ,
'table_schema AS "tableSchema",' ,
'table_name AS "tableName",' ,
'constraint_type AS "constraintType",' ,
'is_deferrable AS "isDeferrable",' ,
'initially_deferred AS "initiallyDeferred"' ,
'from INFORMATION_SCHEMA.table_constraints' ,
2023-03-05 14:45:56 +08:00
` WHERE table_name=' ${ lodash . isPlainObject ( tableName ) ? tableName.tableName : tableName } ' ` ,
2023-02-23 14:13:32 +08:00
] ;
if ( ! constraintName ) {
lines . push ( ` AND constraint_name=' ${ constraintName } ' ` ) ;
}
2023-03-05 14:45:56 +08:00
if ( lodash . isPlainObject ( tableName ) && tableName . schema ) {
lines . push ( ` AND table_schema=' ${ tableName . schema } ' ` ) ;
2023-02-23 14:13:32 +08:00
}
return lines . join ( ' ' ) ;
} ;
}
function patchDescribeTableQuery ( queryGenerator ) {
const describeTableQuery = function ( tableName , schema ) {
schema = schema || this . options . schema || 'public' ;
return (
'SELECT ' +
'pk.constraint_type as "Constraint",' +
'c.column_name as "Field", ' +
'c.column_default as "Default",' +
'c.is_nullable as "Null", ' +
"(CASE WHEN c.udt_name = 'hstore' THEN c.udt_name ELSE c.data_type END) || (CASE WHEN c.character_maximum_length IS NOT NULL THEN '(' || c.character_maximum_length || ')' ELSE '' END) as \"Type\", " +
'(SELECT array_agg(e.enumlabel) FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON t.oid=e.enumtypid WHERE t.typname=c.udt_name) AS "special", ' +
'(SELECT pgd.description FROM pg_catalog.pg_statio_all_tables AS st INNER JOIN pg_catalog.pg_description pgd on (pgd.objoid=st.relid) WHERE c.ordinal_position=pgd.objsubid AND c.table_name=st.relname AND st.schemaname = c.table_schema) AS "Comment" ' +
'FROM information_schema.columns c ' +
'LEFT JOIN (SELECT tc.table_schema, tc.table_name, ' +
'cu.column_name, tc.constraint_type ' +
'FROM information_schema.TABLE_CONSTRAINTS tc ' +
'JOIN information_schema.KEY_COLUMN_USAGE cu ' +
'ON tc.table_schema=cu.table_schema and tc.table_name=cu.table_name ' +
'and tc.constraint_name=cu.constraint_name ' +
"and tc.constraint_type='PRIMARY KEY') pk " +
'ON pk.table_schema=c.table_schema ' +
'AND pk.table_name=c.table_name ' +
'AND pk.column_name=c.column_name ' +
` WHERE c.table_name = ${ this . escape ( tableName ) } AND c.table_schema = ${ this . escape ( schema ) } `
) ;
} ;
queryGenerator . describeTableQuery = describeTableQuery . bind ( queryGenerator ) ;
}
2023-02-16 10:53:04 +08:00
export function patchSequelizeQueryInterface ( db : Database ) {
if ( db . inDialect ( 'postgres' ) ) {
//@ts-ignore
const queryGenerator = db . sequelize . dialect . queryGenerator ;
2023-02-23 14:13:32 +08:00
patchShowConstraintsQuery ( queryGenerator , db ) ;
patchDescribeTableQuery ( queryGenerator ) ;
2023-02-16 10:53:04 +08:00
}
}
2023-02-16 23:56:00 +08:00
export function percent2float ( value : string ) {
2023-02-17 18:15:35 +08:00
if ( ! value . endsWith ( '%' ) ) {
return NaN ;
}
let val = value . substring ( 0 , value . length - 1 ) ;
if ( isNaN ( + val ) ) {
return NaN ;
}
2023-02-16 23:56:00 +08:00
const index = value . indexOf ( '.' ) ;
if ( index === - 1 ) {
return parseFloat ( value ) / 100 ;
}
const repeat = value . length - index - 2 ;
const v = parseInt ( '1' + '0' . repeat ( repeat ) ) ;
return ( parseFloat ( value ) * v ) / ( 100 * v ) ;
}