2017-11-06 04:02:35 +00:00
|
|
|
/* WIP: A version of the builder which accumulates errors, we'll see if we can't unify the implementations at some point */
|
|
|
|
package qgen
|
|
|
|
|
2017-11-06 07:23:32 +00:00
|
|
|
import (
|
|
|
|
"database/sql"
|
2017-11-12 11:29:23 +00:00
|
|
|
"log"
|
2019-10-29 01:58:04 +00:00
|
|
|
"strings"
|
2017-11-06 07:23:32 +00:00
|
|
|
)
|
2017-11-06 04:02:35 +00:00
|
|
|
|
2018-06-06 06:13:55 +00:00
|
|
|
var LogPrepares = true
|
|
|
|
|
2018-08-04 11:46:36 +00:00
|
|
|
// So we don't have to do the qgen.Builder.Accumulator() boilerplate all the time
|
|
|
|
func NewAcc() *Accumulator {
|
|
|
|
return Builder.Accumulator()
|
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
type Accumulator struct {
|
2017-11-06 04:02:35 +00:00
|
|
|
conn *sql.DB
|
2017-11-12 05:25:04 +00:00
|
|
|
adapter Adapter
|
2017-11-06 04:02:35 +00:00
|
|
|
firstErr error
|
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SetConn(conn *sql.DB) {
|
2017-11-06 04:02:35 +00:00
|
|
|
build.conn = conn
|
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SetAdapter(name string) error {
|
2017-11-06 04:02:35 +00:00
|
|
|
adap, err := GetAdapter(name)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
build.adapter = adap
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) GetAdapter() Adapter {
|
2017-11-06 04:02:35 +00:00
|
|
|
return build.adapter
|
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) FirstError() error {
|
2017-11-06 04:02:35 +00:00
|
|
|
return build.firstErr
|
|
|
|
}
|
|
|
|
|
2018-08-04 11:46:36 +00:00
|
|
|
func (build *Accumulator) RecordError(err error) {
|
2017-11-06 04:02:35 +00:00
|
|
|
if err == nil {
|
|
|
|
return
|
|
|
|
}
|
2017-11-06 07:23:32 +00:00
|
|
|
if build.firstErr == nil {
|
2017-11-06 04:02:35 +00:00
|
|
|
build.firstErr = err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) prepare(res string, err error) *sql.Stmt {
|
2018-07-28 12:52:23 +00:00
|
|
|
// TODO: Can we make this less noisy on debug mode?
|
2018-06-06 06:13:55 +00:00
|
|
|
if LogPrepares {
|
|
|
|
log.Print("res: ", res)
|
|
|
|
}
|
2017-11-06 04:02:35 +00:00
|
|
|
if err != nil {
|
2018-08-04 11:46:36 +00:00
|
|
|
build.RecordError(err)
|
2017-11-06 04:02:35 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
stmt, err := build.conn.Prepare(res)
|
2018-08-04 11:46:36 +00:00
|
|
|
build.RecordError(err)
|
2017-11-06 04:02:35 +00:00
|
|
|
return stmt
|
|
|
|
}
|
|
|
|
|
2019-02-10 05:52:26 +00:00
|
|
|
func (build *Accumulator) RawPrepare(res string) *sql.Stmt {
|
|
|
|
return build.prepare(res, nil)
|
|
|
|
}
|
|
|
|
|
2018-07-29 10:54:12 +00:00
|
|
|
func (build *Accumulator) query(query string, args ...interface{}) (rows *sql.Rows, err error) {
|
|
|
|
err = build.FirstError()
|
|
|
|
if err != nil {
|
|
|
|
return rows, err
|
|
|
|
}
|
|
|
|
return build.conn.Query(query, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (build *Accumulator) exec(query string, args ...interface{}) (res sql.Result, err error) {
|
|
|
|
err = build.FirstError()
|
|
|
|
if err != nil {
|
|
|
|
return res, err
|
|
|
|
}
|
|
|
|
return build.conn.Exec(query, args...)
|
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) Tx(handler func(*TransactionBuilder) error) {
|
2017-11-06 04:02:35 +00:00
|
|
|
tx, err := build.conn.Begin()
|
|
|
|
if err != nil {
|
2018-08-04 11:46:36 +00:00
|
|
|
build.RecordError(err)
|
2017-11-06 04:02:35 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
err = handler(&TransactionBuilder{tx, build.adapter, nil})
|
|
|
|
if err != nil {
|
|
|
|
tx.Rollback()
|
2018-08-04 11:46:36 +00:00
|
|
|
build.RecordError(err)
|
2017-11-06 04:02:35 +00:00
|
|
|
return
|
|
|
|
}
|
2018-08-04 11:46:36 +00:00
|
|
|
build.RecordError(tx.Commit())
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleSelect(table string, columns string, where string, orderby string, limit string) *sql.Stmt {
|
2018-12-27 05:42:41 +00:00
|
|
|
return build.prepare(build.adapter.SimpleSelect("", table, columns, where, orderby, limit))
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleCount(table string, where string, limit string) *sql.Stmt {
|
2018-12-27 05:42:41 +00:00
|
|
|
return build.prepare(build.adapter.SimpleCount("", table, where, limit))
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleLeftJoin(table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) *sql.Stmt {
|
2018-12-27 05:42:41 +00:00
|
|
|
return build.prepare(build.adapter.SimpleLeftJoin("", table1, table2, columns, joiners, where, orderby, limit))
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleInnerJoin(table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) *sql.Stmt {
|
2018-12-27 05:42:41 +00:00
|
|
|
return build.prepare(build.adapter.SimpleInnerJoin("", table1, table2, columns, joiners, where, orderby, limit))
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) CreateTable(table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) *sql.Stmt {
|
2018-12-27 05:42:41 +00:00
|
|
|
return build.prepare(build.adapter.CreateTable("", table, charset, collation, columns, keys))
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleInsert(table string, columns string, fields string) *sql.Stmt {
|
2018-12-27 05:42:41 +00:00
|
|
|
return build.prepare(build.adapter.SimpleInsert("", table, columns, fields))
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) SimpleInsertSelect(ins DBInsert, sel DBSelect) *sql.Stmt {
|
2018-12-27 05:42:41 +00:00
|
|
|
return build.prepare(build.adapter.SimpleInsertSelect("", ins, sel))
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) SimpleInsertLeftJoin(ins DBInsert, sel DBJoin) *sql.Stmt {
|
2018-12-27 05:42:41 +00:00
|
|
|
return build.prepare(build.adapter.SimpleInsertLeftJoin("", ins, sel))
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) SimpleInsertInnerJoin(ins DBInsert, sel DBJoin) *sql.Stmt {
|
2018-12-27 05:42:41 +00:00
|
|
|
return build.prepare(build.adapter.SimpleInsertInnerJoin("", ins, sel))
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleUpdate(table string, set string, where string) *sql.Stmt {
|
2018-12-27 05:42:41 +00:00
|
|
|
return build.prepare(build.adapter.SimpleUpdate(qUpdate(table, set, where)))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (build *Accumulator) SimpleUpdateSelect(table string, set string, where string) *sql.Stmt {
|
|
|
|
return build.prepare(build.adapter.SimpleUpdateSelect(qUpdate(table, set, where)))
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleDelete(table string, where string) *sql.Stmt {
|
2018-12-27 05:42:41 +00:00
|
|
|
return build.prepare(build.adapter.SimpleDelete("", table, where))
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// I don't know why you need this, but here it is x.x
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) Purge(table string) *sql.Stmt {
|
2018-12-27 05:42:41 +00:00
|
|
|
return build.prepare(build.adapter.Purge("", table))
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) prepareTx(tx *sql.Tx, res string, err error) (stmt *sql.Stmt) {
|
2017-11-06 04:02:35 +00:00
|
|
|
if err != nil {
|
2018-08-04 11:46:36 +00:00
|
|
|
build.RecordError(err)
|
2017-11-06 04:02:35 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
stmt, err = tx.Prepare(res)
|
2018-08-04 11:46:36 +00:00
|
|
|
build.RecordError(err)
|
2017-11-06 04:02:35 +00:00
|
|
|
return stmt
|
|
|
|
}
|
|
|
|
|
2017-11-06 16:24:45 +00:00
|
|
|
// These ones support transactions
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleSelectTx(tx *sql.Tx, table string, columns string, where string, orderby string, limit string) (stmt *sql.Stmt) {
|
2018-12-27 05:42:41 +00:00
|
|
|
res, err := build.adapter.SimpleSelect("", table, columns, where, orderby, limit)
|
2017-11-06 16:24:45 +00:00
|
|
|
return build.prepareTx(tx, res, err)
|
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleCountTx(tx *sql.Tx, table string, where string, limit string) (stmt *sql.Stmt) {
|
2018-12-27 05:42:41 +00:00
|
|
|
res, err := build.adapter.SimpleCount("", table, where, limit)
|
2017-11-06 16:24:45 +00:00
|
|
|
return build.prepareTx(tx, res, err)
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleLeftJoinTx(tx *sql.Tx, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt) {
|
2018-12-27 05:42:41 +00:00
|
|
|
res, err := build.adapter.SimpleLeftJoin("", table1, table2, columns, joiners, where, orderby, limit)
|
2017-11-06 16:24:45 +00:00
|
|
|
return build.prepareTx(tx, res, err)
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleInnerJoinTx(tx *sql.Tx, table1 string, table2 string, columns string, joiners string, where string, orderby string, limit string) (stmt *sql.Stmt) {
|
2018-12-27 05:42:41 +00:00
|
|
|
res, err := build.adapter.SimpleInnerJoin("", table1, table2, columns, joiners, where, orderby, limit)
|
2017-11-06 16:24:45 +00:00
|
|
|
return build.prepareTx(tx, res, err)
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) CreateTableTx(tx *sql.Tx, table string, charset string, collation string, columns []DBTableColumn, keys []DBTableKey) (stmt *sql.Stmt) {
|
2018-12-27 05:42:41 +00:00
|
|
|
res, err := build.adapter.CreateTable("", table, charset, collation, columns, keys)
|
2017-11-06 16:24:45 +00:00
|
|
|
return build.prepareTx(tx, res, err)
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleInsertTx(tx *sql.Tx, table string, columns string, fields string) (stmt *sql.Stmt) {
|
2018-12-27 05:42:41 +00:00
|
|
|
res, err := build.adapter.SimpleInsert("", table, columns, fields)
|
2017-11-06 16:24:45 +00:00
|
|
|
return build.prepareTx(tx, res, err)
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) SimpleInsertSelectTx(tx *sql.Tx, ins DBInsert, sel DBSelect) (stmt *sql.Stmt) {
|
2018-12-27 05:42:41 +00:00
|
|
|
res, err := build.adapter.SimpleInsertSelect("", ins, sel)
|
2017-11-06 16:24:45 +00:00
|
|
|
return build.prepareTx(tx, res, err)
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) SimpleInsertLeftJoinTx(tx *sql.Tx, ins DBInsert, sel DBJoin) (stmt *sql.Stmt) {
|
2018-12-27 05:42:41 +00:00
|
|
|
res, err := build.adapter.SimpleInsertLeftJoin("", ins, sel)
|
2017-11-07 22:38:15 +00:00
|
|
|
return build.prepareTx(tx, res, err)
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) SimpleInsertInnerJoinTx(tx *sql.Tx, ins DBInsert, sel DBJoin) (stmt *sql.Stmt) {
|
2018-12-27 05:42:41 +00:00
|
|
|
res, err := build.adapter.SimpleInsertInnerJoin("", ins, sel)
|
2017-11-06 16:24:45 +00:00
|
|
|
return build.prepareTx(tx, res, err)
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleUpdateTx(tx *sql.Tx, table string, set string, where string) (stmt *sql.Stmt) {
|
2018-12-27 05:42:41 +00:00
|
|
|
res, err := build.adapter.SimpleUpdate(qUpdate(table, set, where))
|
2017-11-06 16:24:45 +00:00
|
|
|
return build.prepareTx(tx, res, err)
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) SimpleDeleteTx(tx *sql.Tx, table string, where string) (stmt *sql.Stmt) {
|
2018-12-27 05:42:41 +00:00
|
|
|
res, err := build.adapter.SimpleDelete("", table, where)
|
2017-11-06 16:24:45 +00:00
|
|
|
return build.prepareTx(tx, res, err)
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// I don't know why you need this, but here it is x.x
|
2017-11-12 03:29:05 +00:00
|
|
|
func (build *Accumulator) PurgeTx(tx *sql.Tx, table string) (stmt *sql.Stmt) {
|
2018-12-27 05:42:41 +00:00
|
|
|
res, err := build.adapter.Purge("", table)
|
2017-11-06 16:24:45 +00:00
|
|
|
return build.prepareTx(tx, res, err)
|
2017-11-06 04:02:35 +00:00
|
|
|
}
|
2017-11-11 08:46:30 +00:00
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) Delete(table string) *accDeleteBuilder {
|
2019-06-05 04:57:10 +00:00
|
|
|
return &accDeleteBuilder{table, "", nil, build}
|
2017-11-11 08:46:30 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) Update(table string) *accUpdateBuilder {
|
2018-12-27 05:42:41 +00:00
|
|
|
return &accUpdateBuilder{qUpdate(table, "", ""), build}
|
2017-11-12 03:29:05 +00:00
|
|
|
}
|
|
|
|
|
2018-04-22 12:33:56 +00:00
|
|
|
func (build *Accumulator) Select(table string) *AccSelectBuilder {
|
|
|
|
return &AccSelectBuilder{table, "", "", "", "", nil, nil, "", build}
|
2017-11-12 03:29:05 +00:00
|
|
|
}
|
|
|
|
|
2019-10-29 01:58:04 +00:00
|
|
|
func (build *Accumulator) Exists(tbl, col string) *AccSelectBuilder {
|
|
|
|
return build.Select(tbl).Columns(col).Where(col + "=?")
|
|
|
|
}
|
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) Insert(table string) *accInsertBuilder {
|
|
|
|
return &accInsertBuilder{table, "", "", build}
|
2017-11-12 03:29:05 +00:00
|
|
|
}
|
|
|
|
|
2017-11-12 05:25:04 +00:00
|
|
|
func (build *Accumulator) Count(table string) *accCountBuilder {
|
2019-03-12 09:13:57 +00:00
|
|
|
return &accCountBuilder{table, "", "", nil, nil, "", build}
|
2017-11-12 03:29:05 +00:00
|
|
|
}
|
2019-10-29 01:58:04 +00:00
|
|
|
|
|
|
|
type SimpleModel struct {
|
|
|
|
delete *sql.Stmt
|
|
|
|
create *sql.Stmt
|
|
|
|
update *sql.Stmt
|
|
|
|
}
|
|
|
|
|
|
|
|
func (build *Accumulator) SimpleModel(tbl, colstr, primary string) SimpleModel {
|
|
|
|
var qlist, uplist string
|
|
|
|
for _, col := range strings.Split(colstr,",") {
|
|
|
|
qlist += "?,"
|
|
|
|
uplist += col + "=?,"
|
|
|
|
}
|
|
|
|
if len(qlist) > 0 {
|
|
|
|
qlist = qlist[0 : len(qlist)-1]
|
|
|
|
uplist = uplist[0 : len(uplist)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
where := primary + "=?"
|
|
|
|
return SimpleModel{
|
|
|
|
delete: build.Delete(tbl).Where(where).Prepare(),
|
|
|
|
create: build.Insert(tbl).Columns(colstr).Fields(qlist).Prepare(),
|
|
|
|
update: build.Update(tbl).Set(uplist).Where(where).Prepare(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m SimpleModel) Delete(keyVal interface{}) error {
|
|
|
|
_, err := m.delete.Exec(keyVal)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m SimpleModel) Update(args ...interface{}) error {
|
|
|
|
_, err := m.update.Exec(args...)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m SimpleModel) Create(args ...interface{}) error {
|
|
|
|
_, err := m.create.Exec(args...)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m SimpleModel) CreateID(args ...interface{}) (int, error) {
|
|
|
|
res, err := m.create.Exec(args...)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
lastID, err := res.LastInsertId()
|
|
|
|
return int(lastID), err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (build *Accumulator) Model(table string) *accModelBuilder {
|
|
|
|
return &accModelBuilder{table,"",build}
|
|
|
|
}
|
|
|
|
|
|
|
|
type accModelBuilder struct {
|
|
|
|
table string
|
|
|
|
primary string
|
|
|
|
|
|
|
|
build *Accumulator
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *accModelBuilder) Primary(col string) *accModelBuilder {
|
|
|
|
b.primary = col
|
|
|
|
return b
|
|
|
|
}
|