2017-11-12 05:25:04 +00:00
package qgen
2018-03-31 05:25:27 +00:00
import (
"database/sql"
"strconv"
)
2017-11-12 05:25:04 +00:00
type accDeleteBuilder struct {
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
}
2019-06-05 04:57:10 +00:00
func ( b * accDeleteBuilder ) Where ( where string ) * accDeleteBuilder {
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
2017-11-12 05:25:04 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * accDeleteBuilder ) DateCutoff ( column string , quantity int , unit string ) * accDeleteBuilder {
b . dateCutoff = & dateCutoff { column , quantity , unit , 0 }
return b
}
func ( b * accDeleteBuilder ) DateOlderThan ( column string , quantity int , unit string ) * accDeleteBuilder {
b . dateCutoff = & dateCutoff { column , quantity , unit , 1 }
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
}
2019-06-05 04:57:10 +00:00
func ( b * accUpdateBuilder ) DateCutoff ( column string , quantity int , unit string ) * accUpdateBuilder {
b . up . dateCutoff = & dateCutoff { column , quantity , unit , 0 }
return b
2017-11-12 05:25:04 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * accUpdateBuilder ) DateOlderThan ( column string , quantity int , unit string ) * accUpdateBuilder {
b . up . dateCutoff = & dateCutoff { column , quantity , unit , 1 }
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
}
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
}
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) Columns ( columns string ) * AccSelectBuilder {
b . columns = columns
return b
2017-11-12 05:25:04 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) Cols ( columns string ) * AccSelectBuilder {
b . columns = columns
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
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) In ( column 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
where := column + " 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
func ( b * AccSelectBuilder ) InPQuery ( column string , inList [ ] int ) ( * sql . Rows , error ) {
if len ( inList ) == 0 {
return nil , sql . ErrNoRows
}
// TODO: Optimise this
where := column + " IN("
idList := make ( [ ] interface { } , len ( inList ) )
for i , id := range inList {
idList [ i ] = strconv . Itoa ( id )
where += "?,"
}
where = where [ 0 : len ( where ) - 1 ] + ")"
if b . where != "" {
where += " AND " + b . where
}
b . where = where
return b . Query ( idList ... )
}
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) InQ ( column string , subBuilder * AccSelectBuilder ) * AccSelectBuilder {
b . inChain = subBuilder
b . inColumn = column
return b
2018-01-21 11:17:43 +00:00
}
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) DateCutoff ( column string , quantity int , unit string ) * AccSelectBuilder {
b . dateCutoff = & dateCutoff { column , quantity , unit , 0 }
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
}
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
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) Each ( handle func ( * sql . Rows ) error ) error {
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 ( ) {
err = handle ( rows )
if err != nil {
return err
}
}
return rows . Err ( )
}
2019-06-05 04:57:10 +00:00
func ( b * AccSelectBuilder ) EachInt ( handle func ( int ) error ) error {
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
}
err = handle ( theInt )
if err != nil {
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
}
2019-06-05 04:57:10 +00:00
func ( b * accInsertBuilder ) Columns ( columns string ) * accInsertBuilder {
b . columns = columns
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
}
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
}
2019-03-12 09:13:57 +00:00
func ( b * accCountBuilder ) Where ( where string ) * accCountBuilder {
if b . where != "" {
b . where += " AND "
2018-03-31 05:25:27 +00:00
}
2019-03-12 09:13:57 +00:00
b . where += where
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?