2017-11-12 05:25:04 +00:00
package qgen
2018-03-31 05:25:27 +00:00
import (
"database/sql"
2020-02-24 11:15:17 +00:00
//"fmt"
2018-03-31 05:25:27 +00:00
"strconv"
)
2017-11-12 05:25:04 +00:00
type accDeleteBuilder struct {
2020-02-24 11:15:17 +00:00
table string
where string
2019-06-05 04:57:10 +00:00
dateCutoff * dateCutoff // We might want to do this in a slightly less hacky way
2017-11-12 05:25:04 +00:00
build * Accumulator
}
2020-02-24 11:15:17 +00:00
func ( b * accDeleteBuilder ) Where ( w string ) * accDeleteBuilder {
2019-06-05 04:57:10 +00:00
if b . where != "" {
b . where += " AND "
2018-03-31 05:25:27 +00:00
}
2020-02-24 11:15:17 +00:00
b . where += w
2019-06-05 04:57:10 +00:00
return b
2017-11-12 05:25:04 +00:00
}
2020-02-24 11:15:17 +00:00
func ( b * accDeleteBuilder ) DateCutoff ( col string , quantity int , unit string ) * accDeleteBuilder {
b . dateCutoff = & dateCutoff { col , quantity , unit , 0 }
2019-06-05 04:57:10 +00:00
return b
}
2020-02-24 11:15:17 +00:00
func ( b * accDeleteBuilder ) DateOlderThan ( col string , quantity int , unit string ) * accDeleteBuilder {
b . dateCutoff = & dateCutoff { col , quantity , unit , 1 }
2019-06-05 04:57:10 +00:00
return b
2018-07-28 12:52:23 +00:00
}
2019-06-05 04:57:10 +00:00
/ * func ( b * accDeleteBuilder ) Prepare ( ) * sql . Stmt {
return b . build . SimpleDelete ( b . table , b . where )
} * /
// TODO: Fix this nasty hack
func ( b * accDeleteBuilder ) Prepare ( ) * sql . Stmt {
// TODO: Phase out the procedural API and use the adapter's OO API? The OO API might need a bit more work before we do that and it needs to be rolled out to MSSQL.
if b . dateCutoff != nil {
dBuilder := b . build . GetAdapter ( ) . Builder ( ) . Delete ( ) . FromAcc ( b )
return b . build . prepare ( b . build . GetAdapter ( ) . ComplexDelete ( dBuilder ) )
}
return b . build . SimpleDelete ( b . table , b . where )
}
func ( b * accDeleteBuilder ) Run ( args ... interface { } ) ( int , error ) {
stmt := b . Prepare ( )
2018-07-28 12:52:23 +00:00
if stmt == nil {
2019-06-05 04:57:10 +00:00
return 0 , b . build . FirstError ( )
2018-07-28 12:52:23 +00:00
}
res , err := stmt . Exec ( args ... )
if err != nil {
return 0 , err
}
lastID , err := res . LastInsertId ( )
return int ( lastID ) , err
2017-11-12 05:25:04 +00:00
}
type accUpdateBuilder struct {
2018-12-27 05:42:41 +00:00
up * updatePrebuilder
2017-11-12 05:25:04 +00:00
build * Accumulator
}
2019-05-08 09:50:10 +00:00
func ( u * accUpdateBuilder ) Set ( set string ) * accUpdateBuilder {
u . up . set = set
return u
2017-11-12 05:25:04 +00:00
}
2019-05-08 09:50:10 +00:00
func ( u * accUpdateBuilder ) Where ( where string ) * accUpdateBuilder {
if u . up . where != "" {
u . up . where += " AND "
2018-03-31 05:25:27 +00:00
}
2019-05-08 09:50:10 +00:00
u . up . where += where
return u
2018-12-27 05:42:41 +00:00
}
2020-02-24 11:15:17 +00:00
func ( b * accUpdateBuilder ) DateCutoff ( col string , quantity int , unit string ) * accUpdateBuilder {
b . up . dateCutoff = & dateCutoff { col , quantity , unit , 0 }
2019-06-05 04:57:10 +00:00
return b
2017-11-12 05:25:04 +00:00
}
2020-02-24 11:15:17 +00:00
func ( b * accUpdateBuilder ) DateOlderThan ( col string , quantity int , unit string ) * accUpdateBuilder {
b . up . dateCutoff = & dateCutoff { col , quantity , unit , 1 }
2019-06-05 04:57:10 +00:00
return b
2019-05-08 09:50:10 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * accUpdateBuilder ) WhereQ ( sel * selectPrebuilder ) * accUpdateBuilder {
b . up . whereSubQuery = sel
return b
2019-05-08 09:50:10 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * accUpdateBuilder ) Prepare ( ) * sql . Stmt {
if b . up . whereSubQuery != nil {
return b . build . prepare ( b . build . adapter . SimpleUpdateSelect ( b . up ) )
2018-12-27 05:42:41 +00:00
}
2019-06-05 04:57:10 +00:00
return b . build . prepare ( b . build . adapter . SimpleUpdate ( b . up ) )
2018-12-27 05:42:41 +00:00
}
2019-08-28 06:47:54 +00:00
func ( b * accUpdateBuilder ) Exec ( args ... interface { } ) ( res sql . Result , err error ) {
query , err := b . build . adapter . SimpleUpdate ( b . up )
2018-12-27 05:42:41 +00:00
if err != nil {
return res , err
}
2020-02-24 11:15:17 +00:00
//fmt.Println("query:", query)
2019-08-28 06:47:54 +00:00
return b . build . exec ( query , args ... )
2017-11-12 05:25:04 +00:00
}
2018-04-22 12:33:56 +00:00
type AccSelectBuilder struct {
2018-01-03 07:46:18 +00:00
table string
columns string
where string
orderby string
limit string
dateCutoff * dateCutoff // We might want to do this in a slightly less hacky way
2018-04-22 12:33:56 +00:00
inChain * AccSelectBuilder
2018-01-21 11:17:43 +00:00
inColumn string
2017-11-12 05:25:04 +00:00
build * Accumulator
}
2020-02-24 11:15:17 +00:00
func ( b * AccSelectBuilder ) Columns ( cols string ) * AccSelectBuilder {
b . columns = cols
2019-06-05 04:57:10 +00:00
return b
2017-11-12 05:25:04 +00:00
}
2020-02-24 11:15:17 +00:00
func ( b * AccSelectBuilder ) Cols ( cols string ) * AccSelectBuilder {
b . columns = cols
2019-06-05 04:57:10 +00:00
return b
2018-12-27 05:42:41 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) Where ( where string ) * AccSelectBuilder {
if b . where != "" {
b . where += " AND "
2018-03-31 05:25:27 +00:00
}
2019-06-05 04:57:10 +00:00
b . where += where
return b
2018-03-31 05:25:27 +00:00
}
// TODO: Don't implement the SQL at the accumulator level but the adapter level
2020-02-24 11:15:17 +00:00
func ( b * AccSelectBuilder ) In ( col string , inList [ ] int ) * AccSelectBuilder {
2018-03-31 05:25:27 +00:00
if len ( inList ) == 0 {
2019-06-05 04:57:10 +00:00
return b
2018-03-31 05:25:27 +00:00
}
2019-08-28 06:47:54 +00:00
// TODO: Optimise this
2020-02-24 11:15:17 +00:00
where := col + " IN("
2018-03-31 05:25:27 +00:00
for _ , item := range inList {
where += strconv . Itoa ( item ) + ","
}
where = where [ : len ( where ) - 1 ] + ")"
2019-06-05 04:57:10 +00:00
if b . where != "" {
where += " AND " + b . where
2018-03-31 05:25:27 +00:00
}
2019-06-05 04:57:10 +00:00
b . where = where
return b
2017-11-12 05:25:04 +00:00
}
2019-08-28 06:47:54 +00:00
// TODO: Don't implement the SQL at the accumulator level but the adapter level
2020-02-24 11:15:17 +00:00
func ( b * AccSelectBuilder ) InPQuery ( col string , inList [ ] int ) ( * sql . Rows , error ) {
2019-08-28 06:47:54 +00:00
if len ( inList ) == 0 {
return nil , sql . ErrNoRows
}
// TODO: Optimise this
2020-02-24 11:15:17 +00:00
where := col + " IN("
2019-08-28 06:47:54 +00:00
2020-02-24 11:15:17 +00:00
idList := make ( [ ] interface { } , len ( inList ) )
2019-08-28 06:47:54 +00:00
for i , id := range inList {
idList [ i ] = strconv . Itoa ( id )
where += "?,"
}
2020-02-24 11:15:17 +00:00
where = where [ 0 : len ( where ) - 1 ] + ")"
2019-08-28 06:47:54 +00:00
if b . where != "" {
where += " AND " + b . where
}
b . where = where
return b . Query ( idList ... )
}
2020-02-24 11:15:17 +00:00
func ( b * AccSelectBuilder ) InQ ( col string , sb * AccSelectBuilder ) * AccSelectBuilder {
b . inChain = sb
b . inColumn = col
return b
}
func ( b * AccSelectBuilder ) DateCutoff ( col string , quantity int , unit string ) * AccSelectBuilder {
b . dateCutoff = & dateCutoff { col , quantity , unit , 0 }
2019-06-05 04:57:10 +00:00
return b
2018-01-21 11:17:43 +00:00
}
2020-02-24 11:15:17 +00:00
func ( b * AccSelectBuilder ) DateOlderThanQ ( col , unit string ) * AccSelectBuilder {
b . dateCutoff = & dateCutoff { col , 0 , unit , 11 }
2019-06-05 04:57:10 +00:00
return b
2018-01-03 07:46:18 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) Orderby ( orderby string ) * AccSelectBuilder {
b . orderby = orderby
return b
2017-11-12 05:25:04 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) Limit ( limit string ) * AccSelectBuilder {
b . limit = limit
return b
2017-11-12 05:25:04 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) Prepare ( ) * sql . Stmt {
2018-01-03 07:46:18 +00:00
// TODO: Phase out the procedural API and use the adapter's OO API? The OO API might need a bit more work before we do that and it needs to be rolled out to MSSQL.
2019-06-05 04:57:10 +00:00
if b . dateCutoff != nil || b . inChain != nil {
selectBuilder := b . build . GetAdapter ( ) . Builder ( ) . Select ( ) . FromAcc ( b )
return b . build . prepare ( b . build . GetAdapter ( ) . ComplexSelect ( selectBuilder ) )
2018-01-03 07:46:18 +00:00
}
2019-06-05 04:57:10 +00:00
return b . build . SimpleSelect ( b . table , b . columns , b . where , b . orderby , b . limit )
2017-11-12 05:25:04 +00:00
}
2020-12-18 00:53:12 +00:00
func ( b * AccSelectBuilder ) ComplexPrepare ( ) * sql . Stmt {
selectBuilder := b . build . GetAdapter ( ) . Builder ( ) . Select ( ) . FromAcc ( b )
return b . build . prepare ( b . build . GetAdapter ( ) . ComplexSelect ( selectBuilder ) )
}
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) query ( ) ( string , error ) {
2018-07-29 10:54:12 +00:00
// TODO: Phase out the procedural API and use the adapter's OO API? The OO API might need a bit more work before we do that and it needs to be rolled out to MSSQL.
2019-06-05 04:57:10 +00:00
if b . dateCutoff != nil || b . inChain != nil {
selectBuilder := b . build . GetAdapter ( ) . Builder ( ) . Select ( ) . FromAcc ( b )
return b . build . GetAdapter ( ) . ComplexSelect ( selectBuilder )
2018-07-29 10:54:12 +00:00
}
2019-06-05 04:57:10 +00:00
return b . build . adapter . SimpleSelect ( "" , b . table , b . columns , b . where , b . orderby , b . limit )
2018-07-29 10:54:12 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) Query ( args ... interface { } ) ( * sql . Rows , error ) {
stmt := b . Prepare ( )
2017-11-12 05:25:04 +00:00
if stmt != nil {
return stmt . Query ( args ... )
}
2019-06-05 04:57:10 +00:00
return nil , b . build . FirstError ( )
2017-11-12 05:25:04 +00:00
}
2018-05-14 08:56:56 +00:00
type AccRowWrap struct {
row * sql . Row
err error
}
func ( wrap * AccRowWrap ) Scan ( dest ... interface { } ) error {
if wrap . err != nil {
return wrap . err
}
return wrap . row . Scan ( dest ... )
}
// TODO: Test to make sure the errors are passed up properly
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) QueryRow ( args ... interface { } ) * AccRowWrap {
stmt := b . Prepare ( )
2018-05-14 08:56:56 +00:00
if stmt != nil {
return & AccRowWrap { stmt . QueryRow ( args ... ) , nil }
}
2019-06-05 04:57:10 +00:00
return & AccRowWrap { nil , b . build . FirstError ( ) }
2018-05-14 08:56:56 +00:00
}
2018-04-22 12:33:56 +00:00
// Experimental, reduces lines
2020-02-24 11:15:17 +00:00
func ( b * AccSelectBuilder ) Each ( h func ( * sql . Rows ) error ) error {
2019-06-05 04:57:10 +00:00
query , err := b . query ( )
2018-07-29 10:54:12 +00:00
if err != nil {
return err
}
2019-06-05 04:57:10 +00:00
rows , err := b . build . query ( query )
2018-04-22 12:33:56 +00:00
if err != nil {
return err
}
defer rows . Close ( )
for rows . Next ( ) {
2020-02-24 11:15:17 +00:00
if err = h ( rows ) ; err != nil {
2018-04-22 12:33:56 +00:00
return err
}
}
return rows . Err ( )
}
2020-02-24 11:15:17 +00:00
func ( b * AccSelectBuilder ) EachInt ( h func ( int ) error ) error {
2019-06-05 04:57:10 +00:00
query , err := b . query ( )
2018-07-29 10:54:12 +00:00
if err != nil {
return err
}
2019-06-05 04:57:10 +00:00
rows , err := b . build . query ( query )
2018-07-28 12:52:23 +00:00
if err != nil {
return err
}
defer rows . Close ( )
for rows . Next ( ) {
var theInt int
err = rows . Scan ( & theInt )
if err != nil {
return err
}
2020-02-24 11:15:17 +00:00
if err = h ( theInt ) ; err != nil {
2018-07-28 12:52:23 +00:00
return err
}
}
return rows . Err ( )
}
2018-04-22 12:33:56 +00:00
2017-11-12 05:25:04 +00:00
type accInsertBuilder struct {
table string
columns string
fields string
build * Accumulator
}
2020-02-24 11:15:17 +00:00
func ( b * accInsertBuilder ) Columns ( cols string ) * accInsertBuilder {
b . columns = cols
2019-06-05 04:57:10 +00:00
return b
2017-11-12 05:25:04 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * accInsertBuilder ) Fields ( fields string ) * accInsertBuilder {
b . fields = fields
return b
2017-11-12 05:25:04 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * accInsertBuilder ) Prepare ( ) * sql . Stmt {
return b . build . SimpleInsert ( b . table , b . columns , b . fields )
2017-11-12 05:25:04 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * accInsertBuilder ) Exec ( args ... interface { } ) ( res sql . Result , err error ) {
query , err := b . build . adapter . SimpleInsert ( "" , b . table , b . columns , b . fields )
2018-07-29 10:54:12 +00:00
if err != nil {
return res , err
2018-02-03 05:47:14 +00:00
}
2019-06-05 04:57:10 +00:00
return b . build . exec ( query , args ... )
2018-02-03 05:47:14 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * accInsertBuilder ) Run ( args ... interface { } ) ( int , error ) {
query , err := b . build . adapter . SimpleInsert ( "" , b . table , b . columns , b . fields )
2018-07-29 10:54:12 +00:00
if err != nil {
return 0 , err
2018-07-28 12:52:23 +00:00
}
2019-06-05 04:57:10 +00:00
res , err := b . build . exec ( query , args ... )
2018-07-28 12:52:23 +00:00
if err != nil {
return 0 , err
}
2020-02-24 11:15:17 +00:00
lastID , err := res . LastInsertId ( )
return int ( lastID ) , err
}
type accBulkInsertBuilder struct {
table string
columns string
fieldSet [ ] string
build * Accumulator
}
func ( b * accBulkInsertBuilder ) Columns ( cols string ) * accBulkInsertBuilder {
b . columns = cols
return b
}
func ( b * accBulkInsertBuilder ) Fields ( fieldSet ... string ) * accBulkInsertBuilder {
b . fieldSet = fieldSet
return b
}
func ( b * accBulkInsertBuilder ) Prepare ( ) * sql . Stmt {
return b . build . SimpleBulkInsert ( b . table , b . columns , b . fieldSet )
}
func ( b * accBulkInsertBuilder ) Exec ( args ... interface { } ) ( res sql . Result , err error ) {
query , err := b . build . adapter . SimpleBulkInsert ( "" , b . table , b . columns , b . fieldSet )
if err != nil {
return res , err
}
return b . build . exec ( query , args ... )
}
2018-07-28 12:52:23 +00:00
2020-02-24 11:15:17 +00:00
func ( b * accBulkInsertBuilder ) Run ( args ... interface { } ) ( int , error ) {
query , err := b . build . adapter . SimpleBulkInsert ( "" , b . table , b . columns , b . fieldSet )
if err != nil {
return 0 , err
}
res , err := b . build . exec ( query , args ... )
if err != nil {
return 0 , err
}
2018-07-28 12:52:23 +00:00
lastID , err := res . LastInsertId ( )
return int ( lastID ) , err
}
2017-11-12 05:25:04 +00:00
type accCountBuilder struct {
2019-03-12 09:13:57 +00:00
table string
where string
limit string
dateCutoff * dateCutoff // We might want to do this in a slightly less hacky way
inChain * AccSelectBuilder
inColumn string
2017-11-12 05:25:04 +00:00
build * Accumulator
}
2020-02-24 11:15:17 +00:00
func ( b * accCountBuilder ) Where ( w string ) * accCountBuilder {
2019-03-12 09:13:57 +00:00
if b . where != "" {
b . where += " AND "
2018-03-31 05:25:27 +00:00
}
2020-02-24 11:15:17 +00:00
b . where += w
2019-03-12 09:13:57 +00:00
return b
}
func ( b * accCountBuilder ) Limit ( limit string ) * accCountBuilder {
b . limit = limit
return b
2017-11-12 05:25:04 +00:00
}
2019-03-12 09:13:57 +00:00
func ( b * accCountBuilder ) DateCutoff ( column string , quantity int , unit string ) * accCountBuilder {
2019-05-08 09:50:10 +00:00
b . dateCutoff = & dateCutoff { column , quantity , unit , 0 }
2019-03-12 09:13:57 +00:00
return b
2017-11-12 05:25:04 +00:00
}
2019-03-12 09:13:57 +00:00
// TODO: Fix this nasty hack
func ( b * accCountBuilder ) Prepare ( ) * sql . Stmt {
// TODO: Phase out the procedural API and use the adapter's OO API? The OO API might need a bit more work before we do that and it needs to be rolled out to MSSQL.
if b . dateCutoff != nil || b . inChain != nil {
selBuilder := b . build . GetAdapter ( ) . Builder ( ) . Count ( ) . FromCountAcc ( b )
selBuilder . columns = "COUNT(*)"
return b . build . prepare ( b . build . GetAdapter ( ) . ComplexSelect ( selBuilder ) )
}
return b . build . SimpleCount ( b . table , b . where , b . limit )
2017-11-12 05:25:04 +00:00
}
2017-12-30 05:47:46 +00:00
2019-03-12 09:13:57 +00:00
func ( b * accCountBuilder ) Total ( ) ( total int , err error ) {
stmt := b . Prepare ( )
2018-12-27 05:42:41 +00:00
if stmt == nil {
2019-03-12 09:13:57 +00:00
return 0 , b . build . FirstError ( )
2018-12-27 05:42:41 +00:00
}
err = stmt . QueryRow ( ) . Scan ( & total )
return total , err
}
2017-12-30 05:47:46 +00:00
// TODO: Add a Sum builder for summing viewchunks up into one number for the dashboard?