DataHoarder
e2885687b2
All checks were successful
continuous-integration/drone/push Build is passing
175 lines
3 KiB
Go
175 lines
3 KiB
Go
package index
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain"
|
|
)
|
|
|
|
type RowScanInterface interface {
|
|
Scan(dest ...any) error
|
|
}
|
|
|
|
type Scannable interface {
|
|
ScanFromRow(consensus *sidechain.Consensus, row RowScanInterface) error
|
|
}
|
|
|
|
type QueryIterator[V any] interface {
|
|
All(f IterateFunction[int, *V]) (complete bool)
|
|
Next() (int, *V)
|
|
Close()
|
|
Err() error
|
|
}
|
|
|
|
func QueryIterate[V any](i QueryIterator[V], f IterateFunction[int, *V]) {
|
|
if i == nil {
|
|
return
|
|
}
|
|
defer i.Close()
|
|
i.All(f)
|
|
|
|
if i.Err() != nil {
|
|
panic(i.Err())
|
|
}
|
|
}
|
|
|
|
func QueryIterateToSlice[T any](i QueryIterator[T], err ...error) (s []*T) {
|
|
if len(err) > 0 {
|
|
if err[0] != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
QueryIterate(i, func(key int, value *T) (stop bool) {
|
|
s = append(s, value)
|
|
return false
|
|
})
|
|
return s
|
|
}
|
|
|
|
func QueryFirstResult[T any](i QueryIterator[T], err ...error) (v *T) {
|
|
if len(err) > 0 {
|
|
if err[0] != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
QueryIterate(i, func(_ int, value *T) (stop bool) {
|
|
v = value
|
|
return true
|
|
})
|
|
return
|
|
}
|
|
|
|
func QueryHasResults[T any](i QueryIterator[T], err ...error) bool {
|
|
if len(err) > 0 {
|
|
if err[0] != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
var hasValue bool
|
|
QueryIterate(i, func(key int, value *T) (stop bool) {
|
|
if value != nil {
|
|
hasValue = true
|
|
}
|
|
return true
|
|
})
|
|
return hasValue
|
|
}
|
|
|
|
// queryStatement Queries a provided sql.Stmt and returns a QueryIterator
|
|
// After results are read QueryIterator must be closed/freed
|
|
func queryStatement[V any](index *Index, stmt *sql.Stmt, params ...any) (*QueryResult[V], error) {
|
|
var testV *V
|
|
if _, ok := any(testV).(Scannable); !ok {
|
|
return nil, errors.New("unsupported type")
|
|
}
|
|
|
|
if rows, err := stmt.Query(params...); err != nil {
|
|
return nil, err
|
|
} else {
|
|
return &QueryResult[V]{
|
|
consensus: index.consensus,
|
|
rows: rows,
|
|
}, err
|
|
}
|
|
}
|
|
|
|
type FakeQueryResult[V any] struct {
|
|
NextFunction func() (int, *V)
|
|
}
|
|
|
|
func (r *FakeQueryResult[V]) All(f IterateFunction[int, *V]) (complete bool) {
|
|
for {
|
|
if i, v := r.Next(); v == nil {
|
|
return true
|
|
} else {
|
|
if f(i, v) {
|
|
// do not allow resuming
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *FakeQueryResult[V]) Next() (int, *V) {
|
|
return r.NextFunction()
|
|
}
|
|
|
|
func (r *FakeQueryResult[V]) Err() error {
|
|
return nil
|
|
}
|
|
|
|
func (r *FakeQueryResult[V]) Close() {
|
|
|
|
}
|
|
|
|
type QueryResult[V any] struct {
|
|
consensus *sidechain.Consensus
|
|
rows *sql.Rows
|
|
closer func()
|
|
i int
|
|
err error
|
|
}
|
|
|
|
func (r *QueryResult[V]) All(f IterateFunction[int, *V]) (complete bool) {
|
|
for {
|
|
if i, v := r.Next(); v == nil {
|
|
return true
|
|
} else {
|
|
if f(i, v) {
|
|
// do not allow resuming
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *QueryResult[V]) Next() (int, *V) {
|
|
if r.rows.Next() {
|
|
var v V
|
|
if r.err = any(&v).(Scannable).ScanFromRow(r.consensus, r.rows); r.err != nil {
|
|
return 0, nil
|
|
}
|
|
r.i++
|
|
return r.i - 1, &v
|
|
}
|
|
return 0, nil
|
|
}
|
|
|
|
func (r *QueryResult[V]) Err() error {
|
|
return r.err
|
|
}
|
|
|
|
func (r *QueryResult[V]) Close() {
|
|
if r.closer != nil {
|
|
r.closer()
|
|
r.closer = nil
|
|
}
|
|
if r.rows != nil {
|
|
r.rows.Close()
|
|
r.rows = nil
|
|
}
|
|
}
|