import type { Adapter , DatabaseResult , DatabaseValues , } from "./adapters/adapter.ts" ; import { QueryCompiler } from "./querycompiler.ts" ; import { Q } from "./q.ts" ; /**
 * Combine WHERE operators with OR or NOT
 */ export enum WhereType { Or = 1 , Not = 2 , Default = 3 } /**
 * ORDER BY directions
 */ export type OrderDirection = "DESC" | "ASC" ; /**
 * WHERE clause informations
 */ export interface WhereBinding { column : string ; expression : Q ; type : WhereType ; } /**
 * ORDER BY clause informations
 */ interface OrderBinding { column : string ; order : OrderDirection ; } /**
 * Valid query types
 */ export enum QueryType { Select = 1 , Insert = 2 , Delete = 3 , Update = 4 , Replace = 5 } export enum JoinType { Inner = 1 , Full = 2 , Left = 3 , Right = 4 } interface JoinBinding { table : string ; type : JoinType ; columnA : string ; columnB : string ; } export interface CountBinding { columns : string [ ] ; as ? : string ; distinct : boolean ; } /**
 * Query values for INSERT and UPDATE
 */ export type QueryValues = { [ key ] : DatabaseValues ; } ; /**
 * All information about the query
 */ export interface QueryDescription { /** The table which the query is targeting */ tableName : string ; /** Query type */ type : QueryType ; /** Table columns that are going to be fetched */ columns : ( string | [ string , string ] ) [ ] ; /** Query values for INSERT and UPDATE */ values ? : QueryValues | QueryValues [ ] ; /** The where constraints of the query */ wheres : WhereBinding [ ] ; /** The orderings for the query */ orders : OrderBinding [ ] ; /** The maximum number of records to return */ limit ? : number ; /** The number of records to skip */ offset ? : number ; /** Values to be returned by the query */ returning : string [ ] ; /** Tables to be joined */ joins : JoinBinding [ ] ; /** Count records with given conditions */ counts : CountBinding [ ] ; /** Check if the SELECT statement is using DISTINCT keyword */ isDistinct : boolean ; /** The having clauses in the query */ havings : WhereBinding [ ] ; /** Group records by column */ groupBy : string [ ] ; } /**
 * Allows to build complex SQL queries and execute those queries.
 */ export class QueryBuilder { private description : QueryDescription ; constructor ( /** The table which the query is targeting */ tableName , /** The database adapter to perform query */ private adapter ) { this . description = { tableName , type : QueryType . Select , columns : [ ] , wheres : [ ] , orders : [ ] , returning : [ ] , joins : [ ] , counts : [ ] , havings : [ ] , groupBy : [ ] , isDistinct : false } ; } param column param value public where ( column , value ) ; param column param expresion public where ( column , expression ) ; /** Add basic WHERE clause to query */ public where ( column , expression ) { this . description . wheres . push ( { column , expression : expression instanceof Q ? expression : Q . eq ( expression ) , type : WhereType . Default } ) ; return this ; } param column param value public not ( column , value ) ; param column param expresion public not ( column , expression ) ; /** Add WHERE NOT clause to query */ public not ( column , expression ) { this . description . wheres . push ( { column , expression : expression instanceof Q ? expression : Q . eq ( expression ) , type : WhereType . Not } ) ; return this ; } param column param value public or ( column , value ) ; param column param expresion public or ( column , expression ) ; /** Add WHERE ... OR clause to query */ public or ( column , expression ) { this . description . wheres . push ( { column , expression : expression instanceof Q ? expression : Q . eq ( expression ) , type : WhereType . Or } ) ; return this ; } param columns public select ( ... columns ) { this . description . columns = this . description . columns . concat ( columns ) ; return this ; } param limit public limit ( limit ) { if ( limit >= 0 ) { this . description . limit = limit ; } return this ; } param offset public offset ( offset ) { if ( offset > 0 ) { this . description . offset = offset ; } return this ; } /**
     * Get the first record of the query, shortcut for `limit(1)`
     */ public first ( ) { return this . limit ( 1 ) ; } param column param direction public order ( column , direction = "ASC" ) { this . description . orders . push ( { column , order : direction } ) ; return this ; } param column param value public having ( column , value ) ; param column param expresion public having ( column , expression ) ; /** Add SQL HAVING clause to query */ public having ( column , expression ) { this . description . havings . push ( { column , expression : expression instanceof Q ? expression : Q . eq ( expression ) , type : WhereType . Default } ) ; return this ; } param columns public groupBy ( ... columns ) { this . description . groupBy = this . description . groupBy . concat ( columns ) ; return this ; } param columns public returning ( ... columns ) { this . description . returning = this . description . returning . concat ( columns ) ; return this ; } param data public insert ( data ) { this . description . type = QueryType . Insert ; this . description . values = data ; return this ; } param data public replace ( data ) { this . description . type = QueryType . Replace ; this . description . values = data ; return this ; } param data public update ( data ) { this . description . type = QueryType . Update ; this . description . values = data ; return this ; } /**
     * Delete record from the database.
     */ public delete ( ) { this . description . type = QueryType . Delete ; return this ; } param column param as public count ( column , as ? ) { const info = { columns : Array . isArray ( column ) ? column : [ column ] , distinct : false } ; if ( typeof as === "string" && as . length >= 1 ) info . as = as ; this . description . counts . push ( info ) ; return this ; } param columns param as public countDistinct ( column , as ? ) { const info = { columns : Array . isArray ( column ) ? column : [ column ] , distinct : true } ; if ( typeof as === "string" && as . length >= 1 ) info . as = as ; this . description . counts . push ( info ) ; return this ; } /**
     * Force the query to return distinct (unique) results.
     */ public distinct ( ) { this . description . isDistinct = true ; return this ; } /** SQL INNER JOIN */ public innerJoin ( table , a , b ) { this . description . joins . push ( { type : JoinType . Inner , table , columnA : a , columnB : b } ) ; return this ; } /** SQL FULL OUTER JOIN */ public fullJoin ( table , a , b ) { this . description . joins . push ( { type : JoinType . Full , table , columnA : a , columnB : b } ) ; return this ; } /** SQL LEFT OUTER JOIN */ public leftJoin ( table , a , b ) { this . description . joins . push ( { type : JoinType . Left , table , columnA : a , columnB : b } ) ; return this ; } /** SQL RIGHT OUTER JOIN */ public rightJoin ( table , a , b ) { this . description . joins . push ( { type : JoinType . Right , table , columnA : a , columnB : b } ) ; return this ; } param adapter public async execute ( adapter ? ) { let currentAdapter = adapter || this . adapter ; if ( ! currentAdapter ) { throw new Error ( "Cannot run query, adapter is not provided!" ) ; } const { text , values } = this . toSQL ( ) ; return currentAdapter . query ( text , values ) ; } /**
     * Get the actual SQL query string. All the data are replaced by a placeholder.
     * So, you need to also bind the values in order to execute the query.
     */ public toSQL ( ) { const { text , values } = new QueryCompiler ( this . description , this . adapter . dialect ) . compile ( ) ; return { text , values } ; } }