consensus/cmd/index/query.go
DataHoarder e2885687b2
All checks were successful
continuous-integration/drone/push Build is passing
Implement Miner Options page, add webhook notifications
2024-02-25 14:12:33 +01:00

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
}
}