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?