github.com/Azareal/Gosora@v0.0.0-20210729070923-553e66b59003/query_gen/acc_builders.go (about)

     1  package qgen
     2  
     3  import (
     4  	"database/sql"
     5  	"strings"
     6  
     7  	//"fmt"
     8  	"strconv"
     9  )
    10  
    11  type accDeleteBuilder struct {
    12  	table      string
    13  	where      string
    14  	dateCutoff *dateCutoff // We might want to do this in a slightly less hacky way
    15  
    16  	build *Accumulator
    17  }
    18  
    19  func (b *accDeleteBuilder) Where(w string) *accDeleteBuilder {
    20  	if b.where != "" {
    21  		b.where += " AND "
    22  	}
    23  	b.where += w
    24  	return b
    25  }
    26  
    27  func (b *accDeleteBuilder) DateCutoff(col string, quantity int, unit string) *accDeleteBuilder {
    28  	b.dateCutoff = &dateCutoff{col, quantity, unit, 0}
    29  	return b
    30  }
    31  
    32  func (b *accDeleteBuilder) DateOlderThan(col string, quantity int, unit string) *accDeleteBuilder {
    33  	b.dateCutoff = &dateCutoff{col, quantity, unit, 1}
    34  	return b
    35  }
    36  
    37  func (b *accDeleteBuilder) DateOlderThanQ(col, unit string) *accDeleteBuilder {
    38  	b.dateCutoff = &dateCutoff{col, 0, unit, 11}
    39  	return b
    40  }
    41  
    42  /*func (b *accDeleteBuilder) Prepare() *sql.Stmt {
    43  	return b.build.SimpleDelete(b.table, b.where)
    44  }*/
    45  
    46  // TODO: Fix this nasty hack
    47  func (b *accDeleteBuilder) Prepare() *sql.Stmt {
    48  	// 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.
    49  	if b.dateCutoff != nil {
    50  		dBuilder := b.build.GetAdapter().Builder().Delete().FromAcc(b)
    51  		return b.build.prepare(b.build.GetAdapter().ComplexDelete(dBuilder))
    52  	}
    53  	return b.build.SimpleDelete(b.table, b.where)
    54  }
    55  
    56  func (b *accDeleteBuilder) Exec(args ...interface{}) (res sql.Result, e error) {
    57  	stmt := b.Prepare()
    58  	if stmt == nil {
    59  		return res, b.build.FirstError()
    60  	}
    61  	return stmt.Exec(args...)
    62  }
    63  
    64  func (b *accDeleteBuilder) Run(args ...interface{}) (int, error) {
    65  	res, e := b.Exec(args...)
    66  	if e != nil {
    67  		return 0, e
    68  	}
    69  	lastID, e := res.LastInsertId()
    70  	return int(lastID), e
    71  }
    72  
    73  type accUpdateBuilder struct {
    74  	up    *updatePrebuilder
    75  	build *Accumulator
    76  }
    77  
    78  func (u *accUpdateBuilder) Set(set string) *accUpdateBuilder {
    79  	u.up.set = set
    80  	return u
    81  }
    82  
    83  func (u *accUpdateBuilder) Where(where string) *accUpdateBuilder {
    84  	if u.up.where != "" {
    85  		u.up.where += " AND "
    86  	}
    87  	u.up.where += where
    88  	return u
    89  }
    90  
    91  func (b *accUpdateBuilder) DateCutoff(col string, quantity int, unit string) *accUpdateBuilder {
    92  	b.up.dateCutoff = &dateCutoff{col, quantity, unit, 0}
    93  	return b
    94  }
    95  
    96  func (b *accUpdateBuilder) DateOlderThan(col string, quantity int, unit string) *accUpdateBuilder {
    97  	b.up.dateCutoff = &dateCutoff{col, quantity, unit, 1}
    98  	return b
    99  }
   100  
   101  func (b *accUpdateBuilder) DateOlderThanQ(col, unit string) *accUpdateBuilder {
   102  	b.up.dateCutoff = &dateCutoff{col, 0, unit, 11}
   103  	return b
   104  }
   105  
   106  func (b *accUpdateBuilder) WhereQ(sel *selectPrebuilder) *accUpdateBuilder {
   107  	b.up.whereSubQuery = sel
   108  	return b
   109  }
   110  
   111  func (b *accUpdateBuilder) Prepare() *sql.Stmt {
   112  	if b.up.whereSubQuery != nil {
   113  		return b.build.prepare(b.build.adapter.SimpleUpdateSelect(b.up))
   114  	}
   115  	return b.build.prepare(b.build.adapter.SimpleUpdate(b.up))
   116  }
   117  func (b *accUpdateBuilder) Stmt() *sql.Stmt {
   118  	if b.up.whereSubQuery != nil {
   119  		return b.build.prepare(b.build.adapter.SimpleUpdateSelect(b.up))
   120  	}
   121  	return b.build.prepare(b.build.adapter.SimpleUpdate(b.up))
   122  }
   123  
   124  func (b *accUpdateBuilder) Exec(args ...interface{}) (res sql.Result, err error) {
   125  	q, e := b.build.adapter.SimpleUpdate(b.up)
   126  	if err != nil {
   127  		return res, e
   128  	}
   129  	//fmt.Println("q:", q)
   130  	return b.build.exec(q, args...)
   131  }
   132  
   133  type AccBuilder interface {
   134  	Prepare() *sql.Stmt
   135  }
   136  
   137  type AccExec interface {
   138  	Exec(args ...interface{}) (res sql.Result, err error)
   139  }
   140  
   141  type AccSelectBuilder struct {
   142  	table      string
   143  	columns    string
   144  	where      string
   145  	orderby    string
   146  	limit      string
   147  	dateCutoff *dateCutoff // We might want to do this in a slightly less hacky way
   148  	inChain    *AccSelectBuilder
   149  	inColumn   string
   150  
   151  	build *Accumulator
   152  }
   153  
   154  func (b *AccSelectBuilder) Columns(cols string) *AccSelectBuilder {
   155  	b.columns = cols
   156  	return b
   157  }
   158  
   159  func (b *AccSelectBuilder) Cols(cols string) *AccSelectBuilder {
   160  	b.columns = cols
   161  	return b
   162  }
   163  
   164  func (b *AccSelectBuilder) Where(where string) *AccSelectBuilder {
   165  	if b.where != "" {
   166  		b.where += " AND "
   167  	}
   168  	b.where += where
   169  	return b
   170  }
   171  
   172  // TODO: Don't implement the SQL at the accumulator level but the adapter level
   173  func (b *AccSelectBuilder) In(col string, inList []int) *AccSelectBuilder {
   174  	if len(inList) == 0 {
   175  		return b
   176  	}
   177  
   178  	var wsb strings.Builder
   179  	wsb.Grow(len(col) + 5 + 1 + len(b.where) + (len(inList) * 2))
   180  	wsb.WriteString(col)
   181  	wsb.WriteString(" IN(")
   182  	for i, it := range inList {
   183  		if i != 0 {
   184  			wsb.WriteRune(',')
   185  		}
   186  		wsb.WriteString(strconv.Itoa(it))
   187  	}
   188  	if b.where != "" {
   189  		wsb.WriteString(") AND ")
   190  		wsb.WriteString(b.where)
   191  	} else {
   192  		wsb.WriteRune(')')
   193  	}
   194  
   195  	b.where = wsb.String()
   196  	return b
   197  }
   198  
   199  // TODO: Don't implement the SQL at the accumulator level but the adapter level
   200  func (b *AccSelectBuilder) InPQuery(col string, inList []int) (*sql.Rows, error) {
   201  	if len(inList) == 0 {
   202  		return nil, sql.ErrNoRows
   203  	}
   204  	// TODO: Optimise this
   205  	where := col + " IN("
   206  
   207  	idList := make([]interface{}, len(inList))
   208  	for i, id := range inList {
   209  		idList[i] = strconv.Itoa(id)
   210  		where += "?,"
   211  	}
   212  	where = where[0:len(where)-1] + ")"
   213  
   214  	if b.where != "" {
   215  		where += " AND " + b.where
   216  	}
   217  
   218  	b.where = where
   219  	return b.Query(idList...)
   220  }
   221  
   222  func (b *AccSelectBuilder) InQ(col string, sb *AccSelectBuilder) *AccSelectBuilder {
   223  	b.inChain = sb
   224  	b.inColumn = col
   225  	return b
   226  }
   227  
   228  func (b *AccSelectBuilder) DateCutoff(col string, quantity int, unit string) *AccSelectBuilder {
   229  	b.dateCutoff = &dateCutoff{col, quantity, unit, 0}
   230  	return b
   231  }
   232  
   233  func (b *AccSelectBuilder) DateOlderThanQ(col, unit string) *AccSelectBuilder {
   234  	b.dateCutoff = &dateCutoff{col, 0, unit, 11}
   235  	return b
   236  }
   237  
   238  func (b *AccSelectBuilder) Orderby(orderby string) *AccSelectBuilder {
   239  	b.orderby = orderby
   240  	return b
   241  }
   242  
   243  func (b *AccSelectBuilder) Limit(limit string) *AccSelectBuilder {
   244  	b.limit = limit
   245  	return b
   246  }
   247  
   248  func (b *AccSelectBuilder) Prepare() *sql.Stmt {
   249  	// 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.
   250  	if b.dateCutoff != nil || b.inChain != nil {
   251  		selectBuilder := b.build.GetAdapter().Builder().Select().FromAcc(b)
   252  		return b.build.prepare(b.build.GetAdapter().ComplexSelect(selectBuilder))
   253  	}
   254  	return b.build.SimpleSelect(b.table, b.columns, b.where, b.orderby, b.limit)
   255  }
   256  
   257  func (b *AccSelectBuilder) Stmt() *sql.Stmt {
   258  	// 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.
   259  	if b.dateCutoff != nil || b.inChain != nil {
   260  		selectBuilder := b.build.GetAdapter().Builder().Select().FromAcc(b)
   261  		return b.build.prepare(b.build.GetAdapter().ComplexSelect(selectBuilder))
   262  	}
   263  	return b.build.SimpleSelect(b.table, b.columns, b.where, b.orderby, b.limit)
   264  }
   265  
   266  func (b *AccSelectBuilder) ComplexPrepare() *sql.Stmt {
   267  	selectBuilder := b.build.GetAdapter().Builder().Select().FromAcc(b)
   268  	return b.build.prepare(b.build.GetAdapter().ComplexSelect(selectBuilder))
   269  }
   270  
   271  func (b *AccSelectBuilder) query() (string, error) {
   272  	// 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.
   273  	if b.dateCutoff != nil || b.inChain != nil {
   274  		selectBuilder := b.build.GetAdapter().Builder().Select().FromAcc(b)
   275  		return b.build.GetAdapter().ComplexSelect(selectBuilder)
   276  	}
   277  	return b.build.adapter.SimpleSelect("", b.table, b.columns, b.where, b.orderby, b.limit)
   278  }
   279  
   280  func (b *AccSelectBuilder) Query(args ...interface{}) (*sql.Rows, error) {
   281  	stmt := b.Prepare()
   282  	if stmt != nil {
   283  		return stmt.Query(args...)
   284  	}
   285  	return nil, b.build.FirstError()
   286  }
   287  
   288  type AccRowWrap struct {
   289  	row *sql.Row
   290  	err error
   291  }
   292  
   293  func (w *AccRowWrap) Scan(dest ...interface{}) error {
   294  	if w.err != nil {
   295  		return w.err
   296  	}
   297  	return w.row.Scan(dest...)
   298  }
   299  
   300  // TODO: Test to make sure the errors are passed up properly
   301  func (b *AccSelectBuilder) QueryRow(args ...interface{}) *AccRowWrap {
   302  	stmt := b.Prepare()
   303  	if stmt != nil {
   304  		return &AccRowWrap{stmt.QueryRow(args...), nil}
   305  	}
   306  	return &AccRowWrap{nil, b.build.FirstError()}
   307  }
   308  
   309  // Experimental, reduces lines
   310  func (b *AccSelectBuilder) Each(h func(*sql.Rows) error) error {
   311  	query, e := b.query()
   312  	if e != nil {
   313  		return e
   314  	}
   315  	rows, e := b.build.query(query)
   316  	if e != nil {
   317  		return e
   318  	}
   319  	defer rows.Close()
   320  	for rows.Next() {
   321  		if e = h(rows); e != nil {
   322  			return e
   323  		}
   324  	}
   325  	return rows.Err()
   326  }
   327  func (b *AccSelectBuilder) EachP(h func(*sql.Rows) error, p ...interface{}) error {
   328  	query, e := b.query()
   329  	if e != nil {
   330  		return e
   331  	}
   332  	rows, e := b.build.query(query, p)
   333  	if e != nil {
   334  		return e
   335  	}
   336  	defer rows.Close()
   337  	for rows.Next() {
   338  		if e = h(rows); e != nil {
   339  			return e
   340  		}
   341  	}
   342  	return rows.Err()
   343  }
   344  func (b *AccSelectBuilder) EachInt(h func(int) error) error {
   345  	query, e := b.query()
   346  	if e != nil {
   347  		return e
   348  	}
   349  	rows, e := b.build.query(query)
   350  	if e != nil {
   351  		return e
   352  	}
   353  	defer rows.Close()
   354  	for rows.Next() {
   355  		var theInt int
   356  		if e = rows.Scan(&theInt); e != nil {
   357  			return e
   358  		}
   359  		if e = h(theInt); e != nil {
   360  			return e
   361  		}
   362  	}
   363  	return rows.Err()
   364  }
   365  
   366  type accInsertBuilder struct {
   367  	table   string
   368  	columns string
   369  	fields  string
   370  
   371  	build *Accumulator
   372  }
   373  
   374  func (b *accInsertBuilder) Columns(cols string) *accInsertBuilder {
   375  	b.columns = cols
   376  	return b
   377  }
   378  
   379  func (b *accInsertBuilder) Fields(fields string) *accInsertBuilder {
   380  	b.fields = fields
   381  	return b
   382  }
   383  
   384  func (b *accInsertBuilder) Prepare() *sql.Stmt {
   385  	return b.build.SimpleInsert(b.table, b.columns, b.fields)
   386  }
   387  
   388  func (b *accInsertBuilder) Exec(args ...interface{}) (res sql.Result, e error) {
   389  	q, e := b.build.adapter.SimpleInsert("", b.table, b.columns, b.fields)
   390  	if e != nil {
   391  		return res, e
   392  	}
   393  	return b.build.exec(q, args...)
   394  }
   395  
   396  func (b *accInsertBuilder) Run(args ...interface{}) (int, error) {
   397  	res, e := b.Exec(args...)
   398  	if e != nil {
   399  		return 0, e
   400  	}
   401  	lastID, e := res.LastInsertId()
   402  	return int(lastID), e
   403  }
   404  
   405  type accBulkInsertBuilder struct {
   406  	table    string
   407  	columns  string
   408  	fieldSet []string
   409  
   410  	build *Accumulator
   411  }
   412  
   413  func (b *accBulkInsertBuilder) Columns(cols string) *accBulkInsertBuilder {
   414  	b.columns = cols
   415  	return b
   416  }
   417  
   418  func (b *accBulkInsertBuilder) Fields(fieldSet ...string) *accBulkInsertBuilder {
   419  	b.fieldSet = fieldSet
   420  	return b
   421  }
   422  
   423  func (b *accBulkInsertBuilder) Prepare() *sql.Stmt {
   424  	return b.build.SimpleBulkInsert(b.table, b.columns, b.fieldSet)
   425  }
   426  
   427  func (b *accBulkInsertBuilder) Exec(args ...interface{}) (res sql.Result, err error) {
   428  	q, e := b.build.adapter.SimpleBulkInsert("", b.table, b.columns, b.fieldSet)
   429  	if e != nil {
   430  		return res, e
   431  	}
   432  	return b.build.exec(q, args...)
   433  }
   434  
   435  func (b *accBulkInsertBuilder) Run(args ...interface{}) (int, error) {
   436  	res, e := b.Exec(args...)
   437  	if e != nil {
   438  		return 0, e
   439  	}
   440  	lastID, e := res.LastInsertId()
   441  	return int(lastID), e
   442  }
   443  
   444  type accCountBuilder struct {
   445  	table      string
   446  	where      string
   447  	limit      string
   448  	dateCutoff *dateCutoff // We might want to do this in a slightly less hacky way
   449  	inChain    *AccSelectBuilder
   450  	inColumn   string
   451  
   452  	build *Accumulator
   453  }
   454  
   455  func (b *accCountBuilder) Where(w string) *accCountBuilder {
   456  	if b.where != "" {
   457  		b.where += " AND "
   458  	}
   459  	b.where += w
   460  	return b
   461  }
   462  
   463  func (b *accCountBuilder) Limit(limit string) *accCountBuilder {
   464  	b.limit = limit
   465  	return b
   466  }
   467  
   468  func (b *accCountBuilder) DateCutoff(col string, quantity int, unit string) *accCountBuilder {
   469  	b.dateCutoff = &dateCutoff{col, quantity, unit, 0}
   470  	return b
   471  }
   472  
   473  func (b *accCountBuilder) DateOlderThanQ(col, unit string) *accCountBuilder {
   474  	b.dateCutoff = &dateCutoff{col, 0, unit, 11}
   475  	return b
   476  }
   477  
   478  // TODO: Fix this nasty hack
   479  func (b *accCountBuilder) Prepare() *sql.Stmt {
   480  	// 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.
   481  	if b.dateCutoff != nil || b.inChain != nil {
   482  		selBuilder := b.build.GetAdapter().Builder().Count().FromCountAcc(b)
   483  		selBuilder.columns = "COUNT(*)"
   484  		return b.build.prepare(b.build.GetAdapter().ComplexSelect(selBuilder))
   485  	}
   486  	return b.build.SimpleCount(b.table, b.where, b.limit)
   487  }
   488  // TODO: Fix this nasty hack
   489  func (b *accCountBuilder) Stmt() *sql.Stmt {
   490  	// 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.
   491  	if b.dateCutoff != nil || b.inChain != nil {
   492  		selBuilder := b.build.GetAdapter().Builder().Count().FromCountAcc(b)
   493  		selBuilder.columns = "COUNT(*)"
   494  		return b.build.prepare(b.build.GetAdapter().ComplexSelect(selBuilder))
   495  	}
   496  	return b.build.SimpleCount(b.table, b.where, b.limit)
   497  }
   498  
   499  func (b *accCountBuilder) Total() (total int, e error) {
   500  	stmt := b.Prepare()
   501  	if stmt == nil {
   502  		return 0, b.build.FirstError()
   503  	}
   504  	e = stmt.QueryRow().Scan(&total)
   505  	return total, e
   506  }
   507  
   508  func (b *accCountBuilder) TotalP(params ...interface{}) (total int, e error) {
   509  	stmt := b.Prepare()
   510  	if stmt == nil {
   511  		return 0, b.build.FirstError()
   512  	}
   513  	e = stmt.QueryRow(params).Scan(&total)
   514  	return total, e
   515  }
   516  
   517  // TODO: Add a Sum builder for summing viewchunks up into one number for the dashboard?