import { AnyColumn , Column } from './column' ; import { AnyTable , Table } from './TableType' ; import { CollectionToken , EmptyToken , GroupToken , ParameterToken , SeparatorToken , StringToken , Token , createQueryState , } from './tokens' ; import { DbConfig , GetResultType } from './config' ; import { GetReturning , PickByValue , QueryExecutorFn , ResultType } from './types' ; import { SelectFn , makeSelect } from './select' ; import { DeleteQuery } from './delete' ; import { Expression } from './expression' ; import { Query } from './query' ; import { ResultSet } from './result-set' ; import { TableDefinition } from './table' ; import { UpdateQuery } from './update' ; import { wrapQuotes } from './naming' ; export class InsertQuery < Config extends DbConfig , T extends Table < any , any , any > , Returning = number , TableColumns = T extends Table < Config , any , infer Columns > ? Columns : never > extends Query < Returning > { private _insertQueryBrand ! : [ 'insert' , Config , T , Returning ] ; internal getReturningKeys ( ) { return this . returningKeys ; } constructor ( private readonly queryExecutor , private readonly returningKeys , private readonly table , private readonly resultType , private readonly tokens ) { super ( ) ; } then < Result1 , Result2 = never > ( onFulfilled ? , onRejected ? ) { const queryState = createQueryState ( this . tokens ) ; return this . queryExecutor ( queryState . text . join ( ` ` ) , queryState . parameters ) . then ( ( result ) => onFulfilled ? onFulfilled ( this . resultType === `AFFECTED_COUNT` ? result . affectedCount : ( result . rows as any ) ) : result ) . catch ( onRejected ) as any ; } returning < C1 extends keyof TableColumns > ( column1 ) ; returning < C1 extends keyof TableColumns , C2 extends keyof TableColumns > ( column1 , column2 ) ; returning < C1 extends keyof TableColumns , C2 extends keyof TableColumns , C3 extends keyof TableColumns > ( column1 , column2 , column3 ) ; returning < C1 extends keyof TableColumns , C2 extends keyof TableColumns , C3 extends keyof TableColumns , C4 extends keyof TableColumns > ( column1 , column2 , column3 , column4 ) ; returning < C1 extends keyof TableColumns , C2 extends keyof TableColumns , C3 extends keyof TableColumns , C4 extends keyof TableColumns , C5 extends keyof TableColumns > ( column1 , column2 , column3 , column4 , column5 ) ; returning < C1 extends keyof TableColumns , C2 extends keyof TableColumns , C3 extends keyof TableColumns , C4 extends keyof TableColumns , C5 extends keyof TableColumns , C6 extends keyof TableColumns > ( column1 , column2 , column3 , column4 , column5 , column6 ) ; returning < C1 extends keyof TableColumns , C2 extends keyof TableColumns , C3 extends keyof TableColumns , C4 extends keyof TableColumns , C5 extends keyof TableColumns , C6 extends keyof TableColumns , C7 extends keyof TableColumns > ( column1 , column2 , column3 , column4 , column5 , column6 , column7 ) ; returning < C1 extends keyof TableColumns , C2 extends keyof TableColumns , C3 extends keyof TableColumns , C4 extends keyof TableColumns , C5 extends keyof TableColumns , C6 extends keyof TableColumns , C7 extends keyof TableColumns , C8 extends keyof TableColumns > ( column1 , column2 , column3 , column4 , column5 , column6 , column7 , column8 ) ; returning < C1 extends keyof TableColumns , C2 extends keyof TableColumns , C3 extends keyof TableColumns , C4 extends keyof TableColumns , C5 extends keyof TableColumns , C6 extends keyof TableColumns , C7 extends keyof TableColumns , C8 extends keyof TableColumns , C9 extends keyof TableColumns > ( column1 , column2 , column3 , column4 , column5 , column6 , column7 , column8 , column9 ) ; returning < C1 extends keyof TableColumns , C2 extends keyof TableColumns , C3 extends keyof TableColumns , C4 extends keyof TableColumns , C5 extends keyof TableColumns , C6 extends keyof TableColumns , C7 extends keyof TableColumns , C8 extends keyof TableColumns , C9 extends keyof TableColumns , C10 extends keyof TableColumns > ( column1 , column2 , column3 , column4 , column5 , column6 , column7 , column8 , column9 , column10 ) ; returning ( ... columnNames ) { return new InsertQuery ( this . queryExecutor , columnNames , this . table , 'ROWS' , [ ... this . tokens , new StringToken ( `RETURNING` ) , new SeparatorToken ( `,` , columnNames . map ( ( alias ) => { const column = ( this . table as any ) [ alias ] as AnyColumn ; if ( alias !== column . getSnakeCaseName ( ) ) { return new StringToken ( `${ column . getSnakeCaseName ( ) } ${ wrapQuotes ( alias ) }` ) ; } else { return new StringToken ( column . getSnakeCaseName ( ) ) ; } } ) ) , ] ) as any ; } where ( expression ) { return new InsertQuery ( this . queryExecutor , this . returningKeys , this . table , this . resultType , [ ... this . tokens , new StringToken ( `WHERE` ) , ... expression . toTokens ( ) , ] ) ; } onConflictOnConstraint ( constraintName ) { const self = this ; return { doNothing ( ) { return new InsertQuery ( self . queryExecutor , self . returningKeys , self . table , self . resultType , [ ... self . tokens , new StringToken ( `ON CONFLICT ON CONSTRAINT` ) , new StringToken ( constraintName ) , new StringToken ( `DO NOTHING` ) , ] ) ; } , doUpdateSet ( values ) { return new InsertQuery ( self . queryExecutor , self . returningKeys , self . table , self . resultType , [ ... self . tokens , new StringToken ( `ON CONFLICT ON CONSTRAINT` ) , new StringToken ( constraintName ) , new StringToken ( `DO UPDATE SET` ) , new SeparatorToken ( `,` , Object . keys ( values ) . map ( ( columnName ) => { const column = ( self . table as any ) [ columnName ] as AnyColumn ; const value = ( values as any ) [ columnName ] ; if ( value && typeof value === `object` && 'toTokens' in value && typeof value . toTokens === `function` ) { return new CollectionToken ( [ new StringToken ( column . getSnakeCaseName ( ) ) , new StringToken ( `=` ) , ... value . toTokens ( ) , ] ) ; } else { return new CollectionToken ( [ new StringToken ( column . getSnakeCaseName ( ) ) , new StringToken ( `=` ) , new ParameterToken ( value ) , ] ) ; } } ) ) , ] ) ; } , } ; } private getConflictTargetToken < ColumnNames extends T extends Table < Config , any , infer Columns > ? ( keyof Columns ) [ ] : never > ( columnNames ) { if ( columnNames . length === 0 ) return new EmptyToken ( ) ; return new GroupToken ( [ new SeparatorToken ( ',' , columnNames . map ( ( columnName ) => { const column = ( this . table as any ) [ columnName ] as AnyColumn ; return new StringToken ( column . getSnakeCaseName ( ) ) ; } ) ) , ] ) ; } onConflict < ColumnNames extends T extends Table < Config , any , infer Columns > ? ( keyof Columns ) [ ] : never > ( ... columnNames ) { const self = this ; return { doNothing ( ) { return new InsertQuery ( self . queryExecutor , self . returningKeys , self . table , self . resultType , [ ... self . tokens , new StringToken ( `ON CONFLICT` ) , self . getConflictTargetToken ( columnNames ) , new StringToken ( `DO NOTHING` ) , ] ) ; } , doUpdateSet ( values ) { return new InsertQuery ( self . queryExecutor , self . returningKeys , self . table , self . resultType , [ ... self . tokens , new StringToken ( `ON CONFLICT` ) , self . getConflictTargetToken ( columnNames ) , new StringToken ( `DO UPDATE SET` ) , new SeparatorToken ( `,` , Object . keys ( values ) . map ( ( columnName ) => { const column = ( self . table as any ) [ columnName ] as AnyColumn ; const value = ( values as any ) [ columnName ] ; if ( value && typeof value === `object` && 'toTokens' in value && typeof value . toTokens === `function` ) { return new CollectionToken ( [ new StringToken ( column . getSnakeCaseName ( ) ) , new StringToken ( `=` ) , ... value . toTokens ( ) , ] ) ; } else { return new CollectionToken ( [ new StringToken ( column . getSnakeCaseName ( ) ) , new StringToken ( `=` ) , new ParameterToken ( value ) , ] ) ; } } ) ) , ] ) ; } , } ; } internal toTokens ( ) { return this . tokens ; } } export interface InsertIntoResult < Config extends DbConfig , T extends AnyTable , Row = T extends Table < Config , any , infer Columns > ? { [ K in keyof PickByValue < { [ K in keyof Columns ] : Columns [ K ] extends Column < Config , any , any , any , infer IsNotNull , infer HasDefault , any > ? HasDefault extends true ? false : IsNotNull : never ; } , true > ] : Columns [ K ] extends Column < Config , any , any , infer DataType , boolean , any , any > ? GetResultType < Config , DataType > | Query < { [ key ] : GetResultType < Config , DataType > | Expression < Config , DataType , boolean , string > | Expression < Config , GetResultType < Config , DataType > , boolean , string > ; } > | Expression < Config , DataType , boolean , string > | Expression < Config , GetResultType < Config , DataType > , boolean , string > : never ; } & { [ K in keyof PickByValue < { [ K in keyof Columns ] : Columns [ K ] extends Column < Config , any , any , any , infer IsNotNull , infer HasDefault , any > ? HasDefault extends true ? false : IsNotNull : never ; } , false > ] ? : Columns [ K ] extends Column < Config , any , any , infer DataType , boolean , any , any > ? GetResultType < Config , DataType > | Query < { [ key ] : GetResultType < Config , DataType > | Expression < Config , DataType , boolean , string > | Expression < Config , GetResultType < Config , DataType > , boolean , string > ; } > | Expression < Config , DataType , boolean , string > | Expression < Config , GetResultType < Config , DataType > , boolean , string > | GetResultType < Config , 'Null' > : never ; } : never > { select : SelectFn < Config > ; deleteFrom < DeleteTable extends AnyTable > ( deleteTable ) : DeleteQuery < Config , DeleteTable , number > ; update < UpdateTable extends AnyTable > ( updateTable ) : { set ( values ) : UpdateQuery < Config , UpdateTable , number > ; } ; defaultValues ( ) : InsertQuery < Config , T , number > ; values ( values ) : InsertQuery < Config , T , number > ; } export const makeInsertInto = < Config extends DbConfig > ( queryExecutor ) => < T extends AnyTable > ( table , columnNames ? ) : T extends TableDefinition < any > ? never : InsertIntoResult < Config , T > => { return { select : makeSelect ( queryExecutor , [ new StringToken ( `INSERT INTO` ) , new StringToken ( ( table as AnyTable ) . getName ( ) ) , new GroupToken ( [ new SeparatorToken ( `,` , columnNames ?. map ( ( columnName ) => { const column = ( table as any ) [ columnName ] as AnyColumn ; return new StringToken ( column . getSnakeCaseName ( ) ) ; } ) || [ ] ) , ] ) , ] ) , deleteFrom < DeleteTable extends AnyTable > ( deleteTable ) { return new DeleteQuery < Config , DeleteTable , number > ( queryExecutor , [ ] , deleteTable , 'AFFECTED_COUNT' , [ new StringToken ( `INSERT INTO` ) , new StringToken ( ( table as AnyTable ) . getName ( ) ) , new GroupToken ( [ new SeparatorToken ( `,` , columnNames ! . map ( ( columnName ) => { const column = ( table as any ) [ columnName ] as AnyColumn ; return new StringToken ( column . getSnakeCaseName ( ) ) ; } ) ) , ] ) , new StringToken ( `DELETE FROM` ) , new StringToken ( ( deleteTable as AnyTable ) . getName ( ) ) , ] ) ; } , update < UpdateTable extends AnyTable > ( updateTable ) { return { set ( values ) { const keys = Object . keys ( values ) ; return new UpdateQuery ( queryExecutor , [ ] , table , 'AFFECTED_COUNT' , [ new StringToken ( `INSERT INTO` ) , new StringToken ( ( table as AnyTable ) . getName ( ) ) , new GroupToken ( [ new SeparatorToken ( `,` , columnNames ! . map ( ( columnName ) => { const column = ( table as any ) [ columnName ] as AnyColumn ; return new StringToken ( column . getSnakeCaseName ( ) ) ; } ) ) , ] ) , new StringToken ( `UPDATE` ) , new StringToken ( ( updateTable as AnyTable ) . getName ( ) ) , new StringToken ( `SET` ) , new SeparatorToken ( `,` , keys . map ( ( key ) => { const column = ( table as any ) [ key ] as AnyColumn ; const value = ( values as any ) [ key ] ; return new CollectionToken ( [ new StringToken ( column . getSnakeCaseName ( ) ) , new StringToken ( `=` ) , value && typeof value === `object` && 'toTokens' in value ? value . toTokens ( ) : new ParameterToken ( value ) , ] ) ; } ) ) , ] ) ; } , } ; } , defaultValues ( ) { return new InsertQuery ( queryExecutor , [ ] , table , 'AFFECTED_COUNT' , [ new StringToken ( `INSERT INTO` ) , new StringToken ( ( table as AnyTable ) . getName ( ) ) , new StringToken ( `DEFAULT VALUES` ) , ] ) ; } , values ( listOrItem ) { const list = Array . isArray ( listOrItem ) ? listOrItem : [ listOrItem ] ; const [ firstItem ] = list ; return new InsertQuery ( queryExecutor , [ ] , table , 'AFFECTED_COUNT' , [ new StringToken ( `INSERT INTO` ) , new StringToken ( ( table as AnyTable ) . getName ( ) ) , new GroupToken ( [ new SeparatorToken ( `,` , Object . keys ( firstItem ) . map ( ( columnName ) => { const column = ( table as any ) [ columnName ] as AnyColumn ; return new StringToken ( column . getSnakeCaseName ( ) ) ; } ) ) , ] ) , new StringToken ( `VALUES` ) , new SeparatorToken ( ',' , list . map ( ( values ) => { return new GroupToken ( [ new SeparatorToken ( `,` , Object . keys ( values ) . map ( ( columnName ) => { const value = ( values as any ) [ columnName ] ; if ( value && typeof value === `object` && 'toTokens' in value && typeof value . toTokens === `function` ) { return new GroupToken ( [ new CollectionToken ( value . toTokens ( ) ) ] ) ; } else { return new ParameterToken ( value ) ; } } ) ) , ] ) ; } ) ) , ] ) ; } , } as any ; } ;