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
build * Accumulator
}
2018-07-28 12:52:23 +00:00
func ( builder * accDeleteBuilder ) Where ( where string ) * accDeleteBuilder {
if builder . where != "" {
builder . where += " AND "
2018-03-31 05:25:27 +00:00
}
2018-07-28 12:52:23 +00:00
builder . where += where
return builder
2017-11-12 05:25:04 +00:00
}
2018-07-28 12:52:23 +00:00
func ( builder * accDeleteBuilder ) Prepare ( ) * sql . Stmt {
return builder . build . SimpleDelete ( builder . table , builder . where )
}
func ( builder * accDeleteBuilder ) Run ( args ... interface { } ) ( int , error ) {
stmt := builder . Prepare ( )
if stmt == nil {
return 0 , builder . build . FirstError ( )
}
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-05-08 09:50:10 +00:00
func ( u * accUpdateBuilder ) DateCutoff ( column string , quantity int , unit string ) * accUpdateBuilder {
u . up . dateCutoff = & dateCutoff { column , quantity , unit , 0 }
return u
2017-11-12 05:25:04 +00:00
}
2019-05-08 09:50:10 +00:00
func ( u * accUpdateBuilder ) DateOlderThan ( column string , quantity int , unit string ) * accUpdateBuilder {
u . up . dateCutoff = & dateCutoff { column , quantity , unit , 1 }
return u
}
func ( u * accUpdateBuilder ) WhereQ ( sel * selectPrebuilder ) * accUpdateBuilder {
u . up . whereSubQuery = sel
return u
}
func ( u * accUpdateBuilder ) Prepare ( ) * sql . Stmt {
if u . up . whereSubQuery != nil {
return u . build . prepare ( u . build . adapter . SimpleUpdateSelect ( u . up ) )
2018-12-27 05:42:41 +00:00
}
2019-05-08 09:50:10 +00:00
return u . build . prepare ( u . build . adapter . SimpleUpdate ( u . up ) )
2018-12-27 05:42:41 +00:00
}
2019-05-08 09:50:10 +00:00
func ( u * accUpdateBuilder ) Exec ( args ... interface { } ) ( res sql . Result , err error ) {
query , err := u . build . adapter . SimpleUpdate ( u . up )
2018-12-27 05:42:41 +00:00
if err != nil {
return res , err
}
2019-05-08 09:50:10 +00:00
return u . 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
}
2018-12-27 05:42:41 +00:00
func ( builder * AccSelectBuilder ) Columns ( columns string ) * AccSelectBuilder {
builder . columns = columns
return builder
2017-11-12 05:25:04 +00:00
}
2018-12-27 05:42:41 +00:00
func ( builder * AccSelectBuilder ) Cols ( columns string ) * AccSelectBuilder {
builder . columns = columns
return builder
}
func ( builder * AccSelectBuilder ) Where ( where string ) * AccSelectBuilder {
if builder . where != "" {
builder . where += " AND "
2018-03-31 05:25:27 +00:00
}
2018-12-27 05:42:41 +00:00
builder . where += where
return builder
2018-03-31 05:25:27 +00:00
}
// TODO: Don't implement the SQL at the accumulator level but the adapter level
2018-04-22 12:33:56 +00:00
func ( selectItem * AccSelectBuilder ) In ( column string , inList [ ] int ) * AccSelectBuilder {
2018-03-31 05:25:27 +00:00
if len ( inList ) == 0 {
return selectItem
}
var where = column + " IN("
for _ , item := range inList {
where += strconv . Itoa ( item ) + ","
}
where = where [ : len ( where ) - 1 ] + ")"
if selectItem . where != "" {
where += " AND " + selectItem . where
}
2017-11-12 05:25:04 +00:00
selectItem . where = where
return selectItem
}
2018-04-22 12:33:56 +00:00
func ( selectItem * AccSelectBuilder ) InQ ( column string , subBuilder * AccSelectBuilder ) * AccSelectBuilder {
2018-01-21 11:17:43 +00:00
selectItem . inChain = subBuilder
selectItem . inColumn = column
return selectItem
}
2018-12-27 05:42:41 +00:00
func ( builder * AccSelectBuilder ) DateCutoff ( column string , quantity int , unit string ) * AccSelectBuilder {
2019-05-08 09:50:10 +00:00
builder . dateCutoff = & dateCutoff { column , quantity , unit , 0 }
2018-12-27 05:42:41 +00:00
return builder
2018-01-03 07:46:18 +00:00
}
2018-12-27 05:42:41 +00:00
func ( builder * AccSelectBuilder ) Orderby ( orderby string ) * AccSelectBuilder {
builder . orderby = orderby
return builder
2017-11-12 05:25:04 +00:00
}
2018-12-27 05:42:41 +00:00
func ( builder * AccSelectBuilder ) Limit ( limit string ) * AccSelectBuilder {
builder . limit = limit
return builder
2017-11-12 05:25:04 +00:00
}
2018-12-27 05:42:41 +00:00
func ( builder * 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.
2018-12-27 05:42:41 +00:00
if builder . dateCutoff != nil || builder . inChain != nil {
selectBuilder := builder . build . GetAdapter ( ) . Builder ( ) . Select ( ) . FromAcc ( builder )
return builder . build . prepare ( builder . build . GetAdapter ( ) . ComplexSelect ( selectBuilder ) )
2018-01-03 07:46:18 +00:00
}
2018-12-27 05:42:41 +00:00
return builder . build . SimpleSelect ( builder . table , builder . columns , builder . where , builder . orderby , builder . limit )
2017-11-12 05:25:04 +00:00
}
2018-07-29 10:54:12 +00:00
func ( builder * AccSelectBuilder ) query ( ) ( string , error ) {
// 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 builder . dateCutoff != nil || builder . inChain != nil {
selectBuilder := builder . build . GetAdapter ( ) . Builder ( ) . Select ( ) . FromAcc ( builder )
return builder . build . GetAdapter ( ) . ComplexSelect ( selectBuilder )
}
2018-12-27 05:42:41 +00:00
return builder . build . adapter . SimpleSelect ( "" , builder . table , builder . columns , builder . where , builder . orderby , builder . limit )
2018-07-29 10:54:12 +00:00
}
2018-12-27 05:42:41 +00:00
func ( builder * AccSelectBuilder ) Query ( args ... interface { } ) ( * sql . Rows , error ) {
stmt := builder . Prepare ( )
2017-11-12 05:25:04 +00:00
if stmt != nil {
return stmt . Query ( args ... )
}
2018-12-27 05:42:41 +00:00
return nil , builder . 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
2018-07-29 10:54:12 +00:00
func ( builder * AccSelectBuilder ) QueryRow ( args ... interface { } ) * AccRowWrap {
stmt := builder . Prepare ( )
2018-05-14 08:56:56 +00:00
if stmt != nil {
return & AccRowWrap { stmt . QueryRow ( args ... ) , nil }
}
2018-07-29 10:54:12 +00:00
return & AccRowWrap { nil , builder . build . FirstError ( ) }
2018-05-14 08:56:56 +00:00
}
2018-04-22 12:33:56 +00:00
// Experimental, reduces lines
2018-07-29 10:54:12 +00:00
func ( builder * AccSelectBuilder ) Each ( handle func ( * sql . Rows ) error ) error {
query , err := builder . query ( )
if err != nil {
return err
}
rows , err := builder . 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 ( )
}
2018-07-29 10:54:12 +00:00
func ( builder * AccSelectBuilder ) EachInt ( handle func ( int ) error ) error {
query , err := builder . query ( )
if err != nil {
return err
}
rows , err := builder . 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
}
func ( insert * accInsertBuilder ) Columns ( columns string ) * accInsertBuilder {
insert . columns = columns
return insert
}
func ( insert * accInsertBuilder ) Fields ( fields string ) * accInsertBuilder {
insert . fields = fields
return insert
}
func ( insert * accInsertBuilder ) Prepare ( ) * sql . Stmt {
return insert . build . SimpleInsert ( insert . table , insert . columns , insert . fields )
}
2018-07-29 10:54:12 +00:00
func ( builder * accInsertBuilder ) Exec ( args ... interface { } ) ( res sql . Result , err error ) {
2018-12-27 05:42:41 +00:00
query , err := builder . build . adapter . SimpleInsert ( "" , builder . table , builder . columns , builder . fields )
2018-07-29 10:54:12 +00:00
if err != nil {
return res , err
2018-02-03 05:47:14 +00:00
}
2018-07-29 10:54:12 +00:00
return builder . build . exec ( query , args ... )
2018-02-03 05:47:14 +00:00
}
2018-07-28 12:52:23 +00:00
func ( builder * accInsertBuilder ) Run ( args ... interface { } ) ( int , error ) {
2018-12-27 05:42:41 +00:00
query , err := builder . build . adapter . SimpleInsert ( "" , builder . table , builder . columns , builder . fields )
2018-07-29 10:54:12 +00:00
if err != nil {
return 0 , err
2018-07-28 12:52:23 +00:00
}
2018-07-29 10:54:12 +00:00
res , err := builder . 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?