mirror of
https://github.com/octoleo/syncthing.git
synced 2024-11-14 09:14:10 +00:00
2801 lines
58 KiB
Go
2801 lines
58 KiB
Go
// Copyright 2015 The ql Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package ql
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/cznic/b"
|
|
"github.com/cznic/strutil"
|
|
)
|
|
|
|
const (
|
|
_ = iota
|
|
indexEq // [L]
|
|
indexFalse // [false]
|
|
indexGe // [L, ...)
|
|
indexGt // (L, ...)
|
|
indexIsNotNull // (NULL, ...)
|
|
indexIsNull // [NULL]
|
|
indexIntervalCC // [L, H]
|
|
indexIntervalCO // [L, H)
|
|
indexIntervalOC // (L, H]
|
|
indexIntervalOO // (L, H)
|
|
indexLe // (..., H]
|
|
indexLt // (..., H)
|
|
indexNe // (L)
|
|
indexTrue // [true]
|
|
)
|
|
|
|
// Note: All plans must have a pointer receiver. Enables planA == planB operation.
|
|
var (
|
|
_ plan = (*crossJoinDefaultPlan)(nil)
|
|
_ plan = (*distinctDefaultPlan)(nil)
|
|
_ plan = (*explainDefaultPlan)(nil)
|
|
_ plan = (*filterDefaultPlan)(nil)
|
|
_ plan = (*fullJoinDefaultPlan)(nil)
|
|
_ plan = (*groupByDefaultPlan)(nil)
|
|
_ plan = (*indexPlan)(nil)
|
|
_ plan = (*leftJoinDefaultPlan)(nil)
|
|
_ plan = (*limitDefaultPlan)(nil)
|
|
_ plan = (*nullPlan)(nil)
|
|
_ plan = (*offsetDefaultPlan)(nil)
|
|
_ plan = (*orderByDefaultPlan)(nil)
|
|
_ plan = (*rightJoinDefaultPlan)(nil)
|
|
_ plan = (*selectFieldsDefaultPlan)(nil)
|
|
_ plan = (*selectFieldsGroupPlan)(nil)
|
|
_ plan = (*selectIndexDefaultPlan)(nil)
|
|
_ plan = (*sysColumnDefaultPlan)(nil)
|
|
_ plan = (*sysIndexDefaultPlan)(nil)
|
|
_ plan = (*sysTableDefaultPlan)(nil)
|
|
_ plan = (*tableDefaultPlan)(nil)
|
|
_ plan = (*tableNilPlan)(nil)
|
|
)
|
|
|
|
type plan interface {
|
|
do(ctx *execCtx, f func(id interface{}, data []interface{}) (more bool, err error)) error
|
|
explain(w strutil.Formatter)
|
|
fieldNames() []string
|
|
filter(expr expression) (p plan, indicesSought []string, err error)
|
|
hasID() bool
|
|
}
|
|
|
|
func isTableOrIndex(p plan) bool {
|
|
switch p.(type) {
|
|
case
|
|
*indexPlan,
|
|
*sysColumnDefaultPlan,
|
|
*sysIndexDefaultPlan,
|
|
*sysTableDefaultPlan,
|
|
*tableDefaultPlan:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Invariants
|
|
// - All interval plans produce rows in ascending index value collating order.
|
|
// - L <= H
|
|
type indexPlan struct {
|
|
src *table
|
|
cname string
|
|
xname string
|
|
x btreeIndex
|
|
kind int // See interval* consts.
|
|
lval interface{} // L, H: Ordered and comparable (lldb perspective).
|
|
hval interface{}
|
|
}
|
|
|
|
func (r *indexPlan) doGe(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
// nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ...
|
|
// --- --- --- --- --- + + +++ +++ +++
|
|
t := r.src
|
|
it, _, err := r.x.Seek([]interface{}{r.lval})
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
for {
|
|
_, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doGt(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
// nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ...
|
|
// --- --- --- --- --- - - +++ +++ +++
|
|
t := r.src
|
|
it, _, err := r.x.Seek([]interface{}{r.lval})
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
var ok bool
|
|
for {
|
|
k, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
if !ok {
|
|
val, err := expand1(k[0], nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if collate1(val, r.lval) == 0 {
|
|
continue
|
|
}
|
|
|
|
ok = true
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doInterval00(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
// nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ..., H-1, H-1, H, H, H+1, H+1, ...
|
|
// --- --- --- --- --- - - +++ +++ +++ +++ +++ - - --- --- ---
|
|
t := r.src
|
|
it, _, err := r.x.Seek([]interface{}{r.lval})
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
var ok bool
|
|
for {
|
|
k, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
if !ok {
|
|
val, err := expand1(k[0], nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if collate1(val, r.lval) == 0 {
|
|
continue
|
|
}
|
|
|
|
ok = true
|
|
}
|
|
|
|
val, err := expand1(k[0], nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if collate1(val, r.hval) == 0 {
|
|
return nil
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doIntervalOC(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
// nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ..., H-1, H-1, H, H, H+1, H+1, ...
|
|
// --- --- --- --- --- - - +++ +++ +++ +++ +++ + + --- --- ---
|
|
t := r.src
|
|
it, _, err := r.x.Seek([]interface{}{r.lval})
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
var ok bool
|
|
for {
|
|
k, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
if !ok {
|
|
val, err := expand1(k[0], nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if collate1(val, r.lval) == 0 {
|
|
continue
|
|
}
|
|
|
|
ok = true
|
|
}
|
|
|
|
val, err := expand1(k[0], nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if collate1(val, r.hval) > 0 {
|
|
return nil
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doIntervalCC(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
// nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ..., H-1, H-1, H, H, H+1, H+1, ...
|
|
// --- --- --- --- --- + + +++ +++ +++ +++ +++ + + --- --- ---
|
|
t := r.src
|
|
it, _, err := r.x.Seek([]interface{}{r.lval})
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
for {
|
|
k, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
val, err := expand1(k[0], nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if collate1(val, r.hval) > 0 {
|
|
return nil
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doIntervalCO(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
// nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ..., H-1, H-1, H, H, H+1, H+1, ...
|
|
// --- --- --- --- --- + + +++ +++ +++ +++ +++ - - --- --- ---
|
|
t := r.src
|
|
it, _, err := r.x.Seek([]interface{}{r.lval})
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
for {
|
|
k, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
val, err := expand1(k[0], nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if collate1(val, r.hval) >= 0 {
|
|
return nil
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doLe(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
// nil, nil, ..., H-1, H-1, H, H, H+1, H+1, ...
|
|
// --- --- +++ +++ +++ + + --- ---
|
|
t := r.src
|
|
it, err := r.x.SeekFirst()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
for {
|
|
k, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
if k == nil || k[0] == nil {
|
|
continue
|
|
}
|
|
|
|
val, err := expand1(k[0], nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if collate1(val, r.hval) > 0 {
|
|
return nil
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doLt(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
// nil, nil, ..., H-1, H-1, H, H, H+1, H+1, ...
|
|
// --- --- +++ +++ +++ - - --- ---
|
|
t := r.src
|
|
it, err := r.x.SeekFirst()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
for {
|
|
k, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
if k == nil || k[0] == nil {
|
|
continue
|
|
}
|
|
|
|
val, err := expand1(k[0], nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if collate1(val, r.hval) >= 0 {
|
|
return nil
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doEq(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
// nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ...
|
|
// --- --- --- --- --- + + --- --- ---
|
|
t := r.src
|
|
it, _, err := r.x.Seek([]interface{}{r.lval})
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
for {
|
|
k, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
val, err := expand1(k[0], nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if collate1(val, r.lval) != 0 {
|
|
return nil
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doNe(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
// nil, nil, ..., L-1, L-1, L, L, L+1, L+1, ...
|
|
// --- --- +++ +++ +++ - - +++ +++ +++
|
|
t := r.src
|
|
it, err := r.x.SeekFirst()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
for {
|
|
k, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
if k == nil || k[0] == nil {
|
|
continue
|
|
}
|
|
|
|
val, err := expand1(k[0], nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if collate1(val, r.hval) == 0 {
|
|
continue
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doIsNull(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
// nil, nil, ...
|
|
// +++ +++ ---
|
|
t := r.src
|
|
it, err := r.x.SeekFirst()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
for {
|
|
k, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
if k != nil && k[0] != nil {
|
|
return nil
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doIsNotNull(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
// nil, nil, ...
|
|
// --- --- +++
|
|
t := r.src
|
|
it, _, err := r.x.Seek([]interface{}{false}) // lldb collates false right after NULL.
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
for {
|
|
_, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doFalse(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
t := r.src
|
|
it, _, err := r.x.Seek([]interface{}{false})
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
for {
|
|
k, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
b, ok := k[0].(bool)
|
|
if !ok || b {
|
|
return nil
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) doTrue(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
t := r.src
|
|
it, _, err := r.x.Seek([]interface{}{true})
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
for {
|
|
k, h, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
if _, ok := k[0].(bool); !ok {
|
|
return nil
|
|
}
|
|
|
|
id, data, err := t.row(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if more, err := f(id, data); err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) do(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) error {
|
|
switch r.kind {
|
|
case indexEq:
|
|
return r.doEq(ctx, f)
|
|
case indexGe:
|
|
return r.doGe(ctx, f)
|
|
case indexGt:
|
|
return r.doGt(ctx, f)
|
|
case indexLe:
|
|
return r.doLe(ctx, f)
|
|
case indexLt:
|
|
return r.doLt(ctx, f)
|
|
case indexNe:
|
|
return r.doNe(ctx, f)
|
|
case indexIsNull:
|
|
return r.doIsNull(ctx, f)
|
|
case indexIsNotNull:
|
|
return r.doIsNotNull(ctx, f)
|
|
case indexFalse:
|
|
return r.doFalse(ctx, f)
|
|
case indexTrue:
|
|
return r.doTrue(ctx, f)
|
|
case indexIntervalOO:
|
|
return r.doInterval00(ctx, f)
|
|
case indexIntervalCC:
|
|
return r.doIntervalCC(ctx, f)
|
|
case indexIntervalOC:
|
|
return r.doIntervalOC(ctx, f)
|
|
case indexIntervalCO:
|
|
return r.doIntervalCO(ctx, f)
|
|
default:
|
|
//dbg("", r.kind)
|
|
panic("internal error 072")
|
|
}
|
|
}
|
|
|
|
func (r *indexPlan) explain(w strutil.Formatter) {
|
|
s := ""
|
|
if r.kind == indexFalse {
|
|
s = "!"
|
|
}
|
|
w.Format("┌Iterate all rows of table %q using index %q where %s%s", r.src.name, r.xname, s, r.cname)
|
|
switch r.kind {
|
|
case indexEq:
|
|
w.Format(" == %v", value{r.lval})
|
|
case indexGe:
|
|
w.Format(" >= %v", value{r.lval})
|
|
case indexGt:
|
|
w.Format(" > %v", value{r.lval})
|
|
case indexLe:
|
|
w.Format(" <= %v", value{r.hval})
|
|
case indexLt:
|
|
w.Format(" < %v", value{r.hval})
|
|
case indexNe:
|
|
w.Format(" != %v", value{r.lval})
|
|
case indexIsNull:
|
|
w.Format(" IS NULL")
|
|
case indexIsNotNull:
|
|
w.Format(" IS NOT NULL")
|
|
case indexFalse, indexTrue:
|
|
// nop
|
|
case indexIntervalOO:
|
|
w.Format(" > %v && %s < %v", value{r.lval}, r.cname, value{r.hval})
|
|
case indexIntervalCC:
|
|
w.Format(" >= %v && %s <= %v", value{r.lval}, r.cname, value{r.hval})
|
|
case indexIntervalCO:
|
|
w.Format(" >= %v && %s < %v", value{r.lval}, r.cname, value{r.hval})
|
|
case indexIntervalOC:
|
|
w.Format(" > %v && %s <= %v", value{r.lval}, r.cname, value{r.hval})
|
|
default:
|
|
//dbg("", r.kind)
|
|
panic("internal error 073")
|
|
}
|
|
w.Format("\n└Output field names %v\n", qnames(r.fieldNames()))
|
|
}
|
|
|
|
func (r *indexPlan) fieldNames() []string { return r.src.fieldNames() }
|
|
|
|
func (r *indexPlan) filterEq(binOp2 int, val interface{}) (plan, []string, error) {
|
|
switch binOp2 {
|
|
case eq:
|
|
if collate1(r.lval, val) == 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case ge:
|
|
if collate1(r.lval, val) >= 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case '>':
|
|
if collate1(r.lval, val) > 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case le:
|
|
if collate1(r.lval, val) <= 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case '<':
|
|
if collate1(r.lval, val) < 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case neq:
|
|
if collate1(r.lval, val) != 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *indexPlan) filterGe(binOp2 int, val interface{}) (plan, []string, error) {
|
|
switch binOp2 {
|
|
case eq:
|
|
if collate1(r.lval, val) <= 0 {
|
|
r.lval = val
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case ge:
|
|
if collate1(r.lval, val) < 0 {
|
|
r.lval = val
|
|
}
|
|
return r, nil, nil
|
|
case '>':
|
|
if collate1(r.lval, val) <= 0 {
|
|
r.lval = val
|
|
r.kind = indexGt
|
|
}
|
|
return r, nil, nil
|
|
case le:
|
|
switch c := collate1(r.lval, val); {
|
|
case c < 0:
|
|
r.hval = val
|
|
r.kind = indexIntervalCC
|
|
return r, nil, nil
|
|
case c == 0:
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
default: // c > 0
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
case '<':
|
|
if collate1(r.lval, val) < 0 {
|
|
r.hval = val
|
|
r.kind = indexIntervalCO
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case neq:
|
|
switch c := collate1(r.lval, val); {
|
|
case c < 0:
|
|
//MAYBE ORed intervals
|
|
case c == 0:
|
|
r.kind = indexGt
|
|
return r, nil, nil
|
|
default: // c > 0
|
|
return r, nil, nil
|
|
}
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *indexPlan) filterGt(binOp2 int, val interface{}) (plan, []string, error) {
|
|
switch binOp2 {
|
|
case eq:
|
|
if collate1(r.lval, val) < 0 {
|
|
r.lval = val
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case ge:
|
|
if collate1(r.lval, val) < 0 {
|
|
r.lval = val
|
|
r.kind = indexGe
|
|
}
|
|
return r, nil, nil
|
|
case '>':
|
|
if collate1(r.lval, val) < 0 {
|
|
r.lval = val
|
|
}
|
|
return r, nil, nil
|
|
case le:
|
|
if collate1(r.lval, val) < 0 {
|
|
r.hval = val
|
|
r.kind = indexIntervalOC
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case '<':
|
|
if collate1(r.lval, val) < 0 {
|
|
r.hval = val
|
|
r.kind = indexIntervalOO
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case neq:
|
|
if collate1(r.lval, val) >= 0 {
|
|
return r, nil, nil
|
|
}
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *indexPlan) filterLe(binOp2 int, val interface{}) (plan, []string, error) {
|
|
switch binOp2 {
|
|
case eq:
|
|
if collate1(r.hval, val) >= 0 {
|
|
r.lval = val
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case ge:
|
|
switch c := collate1(r.hval, val); {
|
|
case c < 0:
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case c == 0:
|
|
r.lval = val
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
default: // c > 0
|
|
r.lval = val
|
|
r.kind = indexIntervalCC
|
|
return r, nil, nil
|
|
}
|
|
case '>':
|
|
if collate1(r.hval, val) > 0 {
|
|
r.lval = val
|
|
r.kind = indexIntervalOC
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case le:
|
|
if collate1(r.hval, val) > 0 {
|
|
r.hval = val
|
|
}
|
|
return r, nil, nil
|
|
case '<':
|
|
if collate1(r.hval, val) >= 0 {
|
|
r.hval = val
|
|
r.kind = indexLt
|
|
}
|
|
return r, nil, nil
|
|
case neq:
|
|
switch c := collate1(r.hval, val); {
|
|
case c < 0:
|
|
return r, nil, nil
|
|
case c == 0:
|
|
r.kind = indexLt
|
|
return r, nil, nil
|
|
default: // c > 0
|
|
//bop
|
|
}
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *indexPlan) filterLt(binOp2 int, val interface{}) (plan, []string, error) {
|
|
switch binOp2 {
|
|
case eq:
|
|
if collate1(r.hval, val) > 0 {
|
|
r.lval = val
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case ge:
|
|
if collate1(r.hval, val) > 0 {
|
|
r.lval = val
|
|
r.kind = indexIntervalCO
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case '>':
|
|
if collate1(r.hval, val) > 0 {
|
|
r.lval = val
|
|
r.kind = indexIntervalOO
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case le:
|
|
if collate1(r.hval, val) > 0 {
|
|
r.hval = val
|
|
r.kind = indexLe
|
|
}
|
|
return r, nil, nil
|
|
case '<':
|
|
if collate1(r.hval, val) > 0 {
|
|
r.hval = val
|
|
}
|
|
return r, nil, nil
|
|
case neq:
|
|
if collate1(r.hval, val) > 0 {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
return r, nil, nil
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *indexPlan) filterCC(binOp2 int, val interface{}) (plan, []string, error) {
|
|
switch binOp2 {
|
|
case eq:
|
|
if collate1(val, r.lval) < 0 || collate1(val, r.hval) > 0 {
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
|
|
r.lval = val
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
case ge:
|
|
if collate1(val, r.lval) <= 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
switch c := collate1(val, r.hval); {
|
|
case c < 0:
|
|
r.lval = val
|
|
return r, nil, nil
|
|
case c == 0:
|
|
r.lval = val
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
default:
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
case '>':
|
|
switch c := collate1(val, r.lval); {
|
|
case c < 0:
|
|
return r, nil, nil
|
|
case c == 0:
|
|
r.kind = indexIntervalOC
|
|
return r, nil, nil
|
|
default:
|
|
if collate1(val, r.hval) < 0 {
|
|
r.lval = val
|
|
r.kind = indexIntervalOC
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
case le:
|
|
switch c := collate1(val, r.lval); {
|
|
case c < 0:
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case c == 0:
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
default:
|
|
if collate1(val, r.hval) < 0 {
|
|
r.hval = val
|
|
}
|
|
return r, nil, nil
|
|
}
|
|
case '<':
|
|
if collate1(val, r.lval) <= 0 {
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
|
|
if collate1(val, r.hval) <= 0 {
|
|
r.hval = val
|
|
r.kind = indexIntervalCO
|
|
}
|
|
return r, nil, nil
|
|
case neq:
|
|
switch c := collate1(val, r.lval); {
|
|
case c < 0:
|
|
return r, nil, nil
|
|
case c == 0:
|
|
r.kind = indexIntervalOC
|
|
return r, nil, nil
|
|
default:
|
|
switch c := collate1(val, r.hval); {
|
|
case c == 0:
|
|
r.kind = indexIntervalCO
|
|
return r, nil, nil
|
|
case c > 0:
|
|
return r, nil, nil
|
|
default:
|
|
return nil, nil, nil
|
|
}
|
|
}
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *indexPlan) filterOC(binOp2 int, val interface{}) (plan, []string, error) {
|
|
switch binOp2 {
|
|
case eq:
|
|
if collate1(val, r.lval) <= 0 || collate1(val, r.hval) > 0 {
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
|
|
r.lval = val
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
case ge:
|
|
if collate1(val, r.lval) <= 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
switch c := collate1(val, r.hval); {
|
|
case c < 0:
|
|
r.lval = val
|
|
r.kind = indexIntervalCC
|
|
return r, nil, nil
|
|
case c == 0:
|
|
r.lval = val
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
default:
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
case '>':
|
|
if collate1(val, r.lval) <= 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
if collate1(val, r.hval) < 0 {
|
|
r.lval = val
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case le:
|
|
if collate1(val, r.lval) <= 0 {
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
|
|
if collate1(val, r.hval) < 0 {
|
|
r.hval = val
|
|
}
|
|
return r, nil, nil
|
|
case '<':
|
|
if collate1(val, r.lval) <= 0 {
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
|
|
switch c := collate1(val, r.hval); {
|
|
case c < 0:
|
|
r.hval = val
|
|
r.kind = indexIntervalOO
|
|
case c == 0:
|
|
r.kind = indexIntervalOO
|
|
}
|
|
return r, nil, nil
|
|
case neq:
|
|
if collate1(val, r.lval) <= 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
switch c := collate1(val, r.hval); {
|
|
case c < 0:
|
|
return nil, nil, nil
|
|
case c == 0:
|
|
r.kind = indexIntervalOO
|
|
return r, nil, nil
|
|
default:
|
|
return r, nil, nil
|
|
}
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *indexPlan) filterOO(binOp2 int, val interface{}) (plan, []string, error) {
|
|
switch binOp2 {
|
|
case eq:
|
|
if collate1(val, r.lval) <= 0 || collate1(val, r.hval) >= 0 {
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
|
|
r.lval = val
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
case ge:
|
|
if collate1(val, r.lval) <= 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
if collate1(val, r.hval) < 0 {
|
|
r.lval = val
|
|
r.kind = indexIntervalCO
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case '>':
|
|
if collate1(val, r.lval) <= 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
if collate1(val, r.hval) < 0 {
|
|
r.lval = val
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case le:
|
|
if collate1(val, r.lval) <= 0 {
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
|
|
if collate1(val, r.hval) < 0 {
|
|
r.hval = val
|
|
r.kind = indexIntervalOC
|
|
}
|
|
return r, nil, nil
|
|
case '<':
|
|
if collate1(val, r.lval) <= 0 {
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
|
|
if collate1(val, r.hval) < 0 {
|
|
r.hval = val
|
|
}
|
|
return r, nil, nil
|
|
case neq:
|
|
if collate1(val, r.lval) <= 0 || collate1(val, r.hval) >= 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
return nil, nil, nil
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *indexPlan) filterCO(binOp2 int, val interface{}) (plan, []string, error) {
|
|
switch binOp2 {
|
|
case eq:
|
|
if collate1(val, r.lval) < 0 || collate1(val, r.hval) >= 0 {
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
|
|
r.lval = val
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
case ge:
|
|
if collate1(val, r.lval) <= 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
if collate1(val, r.hval) < 0 {
|
|
r.lval = val
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case '>':
|
|
switch c := collate1(val, r.lval); {
|
|
case c < 0:
|
|
return r, nil, nil
|
|
case c == 0:
|
|
r.kind = indexIntervalOO
|
|
return r, nil, nil
|
|
default:
|
|
if collate1(val, r.hval) < 0 {
|
|
r.lval = val
|
|
r.kind = indexIntervalOO
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
case le:
|
|
switch c := collate1(val, r.lval); {
|
|
case c < 0:
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case c == 0:
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
default:
|
|
if collate1(val, r.hval) < 0 {
|
|
r.hval = val
|
|
r.kind = indexIntervalCC
|
|
}
|
|
return r, nil, nil
|
|
}
|
|
case '<':
|
|
if collate1(val, r.lval) <= 0 {
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
|
|
if collate1(val, r.hval) < 0 {
|
|
r.hval = val
|
|
}
|
|
return r, nil, nil
|
|
case neq:
|
|
switch c := collate1(val, r.lval); {
|
|
case c < 0:
|
|
return r, nil, nil
|
|
case c == 0:
|
|
r.kind = indexIntervalOO
|
|
return r, nil, nil
|
|
default:
|
|
if collate1(val, r.hval) < 0 {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
return r, nil, nil
|
|
}
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *indexPlan) filterNe(binOp2 int, val interface{}) (plan, []string, error) {
|
|
switch binOp2 {
|
|
case eq:
|
|
if collate1(val, r.lval) != 0 {
|
|
r.lval = val
|
|
r.kind = indexEq
|
|
return r, nil, nil
|
|
}
|
|
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case ge:
|
|
switch c := collate1(val, r.lval); {
|
|
case c < 0:
|
|
return nil, nil, nil //TODO
|
|
case c == 0:
|
|
r.kind = indexGt
|
|
return r, nil, nil
|
|
default:
|
|
r.lval = val
|
|
r.kind = indexGe
|
|
return r, nil, nil
|
|
}
|
|
case '>':
|
|
if collate1(val, r.lval) < 0 {
|
|
return nil, nil, nil //TODO
|
|
}
|
|
|
|
r.lval = val
|
|
r.kind = indexGt
|
|
return r, nil, nil
|
|
case le:
|
|
switch c := collate1(val, r.lval); {
|
|
case c < 0:
|
|
r.hval = val
|
|
r.kind = indexLe
|
|
return r, nil, nil
|
|
case c == 0:
|
|
r.kind = indexLt
|
|
return r, nil, nil
|
|
default:
|
|
return nil, nil, nil //TODO
|
|
}
|
|
case '<':
|
|
if collate1(val, r.lval) <= 0 {
|
|
r.hval = val
|
|
r.kind = indexLt
|
|
return r, nil, nil
|
|
}
|
|
|
|
return nil, nil, nil //TODO
|
|
case neq:
|
|
if collate1(val, r.lval) == 0 {
|
|
return r, nil, nil
|
|
}
|
|
|
|
return nil, nil, nil
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *indexPlan) filter(expr expression) (plan, []string, error) {
|
|
switch x := expr.(type) {
|
|
case *binaryOperation:
|
|
ok, cname, val, err := x.isIdentRelOpVal()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if !ok || r.cname != cname {
|
|
break
|
|
}
|
|
|
|
if val, err = typeCheck1(val, findCol(r.src.cols, cname)); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
switch r.kind {
|
|
case indexEq: // [L]
|
|
return r.filterEq(x.op, val)
|
|
case indexGe: // [L, ...)
|
|
return r.filterGe(x.op, val)
|
|
case indexGt: // (L, ...)
|
|
return r.filterGt(x.op, val)
|
|
case indexIntervalCC: // [L, H]
|
|
return r.filterCC(x.op, val)
|
|
case indexIntervalCO: // [L, H)
|
|
return r.filterCO(x.op, val)
|
|
case indexIntervalOC: // (L, H]
|
|
return r.filterOC(x.op, val)
|
|
case indexIntervalOO: // (L, H)
|
|
return r.filterOO(x.op, val)
|
|
case indexLe: // (..., H]
|
|
return r.filterLe(x.op, val)
|
|
case indexLt: // (..., H)
|
|
return r.filterLt(x.op, val)
|
|
case indexNe: // (L)
|
|
return r.filterNe(x.op, val)
|
|
}
|
|
case *ident:
|
|
cname := x.s
|
|
if r.cname != cname {
|
|
break
|
|
}
|
|
|
|
switch r.kind {
|
|
case indexFalse: // [false]
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
case indexTrue: // [true]
|
|
return r, nil, nil
|
|
}
|
|
case *unaryOperation:
|
|
if x.op != '!' {
|
|
break
|
|
}
|
|
|
|
operand, ok := x.v.(*ident)
|
|
if !ok {
|
|
break
|
|
}
|
|
|
|
cname := operand.s
|
|
if r.cname != cname {
|
|
break
|
|
}
|
|
|
|
switch r.kind {
|
|
case indexFalse: // [false]
|
|
return r, nil, nil
|
|
case indexTrue: // [true]
|
|
return &nullPlan{r.fieldNames()}, nil, nil
|
|
}
|
|
}
|
|
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *indexPlan) hasID() bool { return true }
|
|
|
|
type explainDefaultPlan struct {
|
|
s stmt
|
|
}
|
|
|
|
func (r *explainDefaultPlan) hasID() bool { return false }
|
|
|
|
func (r *explainDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (more bool, err error)) error {
|
|
var buf bytes.Buffer
|
|
switch x := r.s.(type) {
|
|
default:
|
|
w := strutil.IndentFormatter(&buf, "│ ")
|
|
x.explain(ctx, w)
|
|
}
|
|
|
|
a := bytes.Split(buf.Bytes(), []byte{'\n'})
|
|
for _, v := range a[:len(a)-1] {
|
|
if more, err := f(nil, []interface{}{string(v)}); !more || err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *explainDefaultPlan) explain(w strutil.Formatter) {
|
|
return
|
|
}
|
|
|
|
func (r *explainDefaultPlan) fieldNames() []string {
|
|
return []string{""}
|
|
}
|
|
|
|
func (r *explainDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
type filterDefaultPlan struct {
|
|
plan
|
|
expr expression
|
|
is []string
|
|
}
|
|
|
|
func (r *filterDefaultPlan) hasID() bool { return r.plan.hasID() }
|
|
|
|
func (r *filterDefaultPlan) explain(w strutil.Formatter) {
|
|
r.plan.explain(w)
|
|
w.Format("┌Filter on %v\n", r.expr)
|
|
if len(r.is) != 0 {
|
|
w.Format("│Possibly useful indices\n")
|
|
m := map[string]bool{}
|
|
for _, v := range r.is {
|
|
if !m[v] {
|
|
m[v] = true
|
|
n := ""
|
|
for _, r := range v {
|
|
if r >= '0' && r <= '9' || r >= 'a' && r <= 'z' || r >= 'A' && r <= 'Z' || r == '_' {
|
|
n += string(r)
|
|
continue
|
|
}
|
|
|
|
n += "_"
|
|
}
|
|
for strings.Contains(n, "__") {
|
|
n = strings.Replace(n, "__", "_", -1)
|
|
}
|
|
for strings.HasSuffix(n, "_") {
|
|
n = n[:len(n)-1]
|
|
}
|
|
w.Format("│CREATE INDEX x%s ON %s;\n", n, v)
|
|
}
|
|
}
|
|
}
|
|
w.Format("└Output field names %v\n", qnames(r.plan.fieldNames()))
|
|
}
|
|
|
|
func (r *filterDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) {
|
|
m := map[interface{}]interface{}{}
|
|
fields := r.plan.fieldNames()
|
|
return r.plan.do(ctx, func(rid interface{}, data []interface{}) (bool, error) {
|
|
for i, v := range fields {
|
|
m[v] = data[i]
|
|
}
|
|
m["$id"] = rid
|
|
val, err := r.expr.eval(ctx, m)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if val == nil {
|
|
return true, nil
|
|
}
|
|
|
|
x, ok := val.(bool)
|
|
if !ok {
|
|
return false, fmt.Errorf("invalid boolean expression %s (value of type %T)", val, val)
|
|
}
|
|
|
|
if !x {
|
|
return true, nil
|
|
}
|
|
|
|
return f(rid, data)
|
|
})
|
|
}
|
|
|
|
type crossJoinDefaultPlan struct {
|
|
rsets []plan
|
|
names []string
|
|
fields []string
|
|
}
|
|
|
|
func (r *crossJoinDefaultPlan) hasID() bool { return false }
|
|
|
|
func (r *crossJoinDefaultPlan) explain(w strutil.Formatter) {
|
|
w.Format("┌Compute Cartesian product of%i\n")
|
|
for i, v := range r.rsets {
|
|
sel := !isTableOrIndex(v)
|
|
if sel {
|
|
w.Format("┌Iterate all rows of virtual table %q%i\n", r.names[i])
|
|
}
|
|
v.explain(w)
|
|
if sel {
|
|
w.Format("%u└Output field names %v\n", qnames(v.fieldNames()))
|
|
}
|
|
}
|
|
w.Format("%u└Output field names %v\n", qnames(r.fields))
|
|
}
|
|
|
|
func (r *crossJoinDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
var is []string
|
|
for i, v := range r.names {
|
|
e2, err := expr.clone(nil, v)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
p2, is2, err := r.rsets[i].filter(e2)
|
|
is = append(is, is2...)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if p2 != nil {
|
|
r.rsets[i] = p2
|
|
return r, is, nil
|
|
}
|
|
}
|
|
return nil, is, nil
|
|
}
|
|
|
|
func (r *crossJoinDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error {
|
|
if len(r.rsets) == 1 {
|
|
return r.rsets[0].do(ctx, f)
|
|
}
|
|
|
|
ids := map[string]interface{}{}
|
|
var g func([]interface{}, []plan, int) error
|
|
g = func(prefix []interface{}, rsets []plan, x int) (err error) {
|
|
return rsets[0].do(ctx, func(id interface{}, in []interface{}) (bool, error) {
|
|
ids[r.names[x]] = id
|
|
if len(rsets) > 1 {
|
|
return true, g(append(prefix, in...), rsets[1:], x+1)
|
|
}
|
|
|
|
return f(ids, append(prefix, in...))
|
|
})
|
|
}
|
|
return g(nil, r.rsets, 0)
|
|
}
|
|
|
|
func (r *crossJoinDefaultPlan) fieldNames() []string { return r.fields }
|
|
|
|
type distinctDefaultPlan struct {
|
|
src plan
|
|
fields []string
|
|
}
|
|
|
|
func (r *distinctDefaultPlan) hasID() bool { return false }
|
|
|
|
func (r *distinctDefaultPlan) explain(w strutil.Formatter) {
|
|
r.src.explain(w)
|
|
w.Format("┌Compute distinct rows\n└Output field names %v\n", r.fields)
|
|
}
|
|
|
|
func (r *distinctDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *distinctDefaultPlan) fieldNames() []string { return r.fields }
|
|
|
|
func (r *distinctDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) {
|
|
t, err := ctx.db.store.CreateTemp(true)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
defer func() {
|
|
if derr := t.Drop(); derr != nil && err == nil {
|
|
err = derr
|
|
}
|
|
}()
|
|
|
|
if err = r.src.do(ctx, func(id interface{}, in []interface{}) (bool, error) {
|
|
if err = t.Set(in, nil); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}); err != nil {
|
|
return
|
|
}
|
|
|
|
var data []interface{}
|
|
more := true
|
|
it, err := t.SeekFirst()
|
|
for more && err == nil {
|
|
data, _, err = it.Next()
|
|
if err != nil {
|
|
break
|
|
}
|
|
|
|
more, err = f(nil, data)
|
|
}
|
|
return noEOF(err)
|
|
}
|
|
|
|
type groupByDefaultPlan struct {
|
|
colNames []string
|
|
src plan
|
|
fields []string
|
|
}
|
|
|
|
func (r *groupByDefaultPlan) hasID() bool { return false }
|
|
|
|
func (r *groupByDefaultPlan) explain(w strutil.Formatter) {
|
|
r.src.explain(w)
|
|
switch {
|
|
case len(r.colNames) == 0: //TODO this case should not exist for this plan, should become tableDefaultPlan
|
|
w.Format("┌Group by distinct rows")
|
|
default:
|
|
w.Format("┌Group by")
|
|
for _, v := range r.colNames {
|
|
w.Format(" %s,", v)
|
|
}
|
|
}
|
|
w.Format("\n└Output field names %v\n", qnames(r.fields))
|
|
}
|
|
|
|
func (r *groupByDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *groupByDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) {
|
|
t, err := ctx.db.store.CreateTemp(true)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
defer func() {
|
|
if derr := t.Drop(); derr != nil && err == nil {
|
|
err = derr
|
|
}
|
|
}()
|
|
|
|
var gcols []*col
|
|
var cols []*col
|
|
m := map[string]int{}
|
|
for i, v := range r.src.fieldNames() {
|
|
m[v] = i
|
|
}
|
|
for _, c := range r.colNames {
|
|
i, ok := m[c]
|
|
if !ok {
|
|
return fmt.Errorf("unknown field %s", c)
|
|
}
|
|
|
|
gcols = append(gcols, &col{name: c, index: i})
|
|
}
|
|
k := make([]interface{}, len(r.colNames)) //TODO optimize when len(r.cols) == 0, should become tableDefaultPlan
|
|
if err = r.src.do(ctx, func(rid interface{}, in []interface{}) (more bool, err error) {
|
|
infer(in, &cols)
|
|
for i, c := range gcols {
|
|
k[i] = in[c.index]
|
|
}
|
|
h0, err := t.Get(k)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
var h int64
|
|
if len(h0) != 0 {
|
|
h, _ = h0[0].(int64)
|
|
}
|
|
nh, err := t.Create(append([]interface{}{h, nil}, in...)...)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
for i, c := range gcols {
|
|
k[i] = in[c.index]
|
|
}
|
|
err = t.Set(k, []interface{}{nh})
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}); err != nil {
|
|
return
|
|
}
|
|
|
|
for i, v := range r.src.fieldNames()[:len(cols)] {
|
|
cols[i].name = v
|
|
cols[i].index = i
|
|
}
|
|
if more, err := f(nil, []interface{}{t, cols}); !more || err != nil {
|
|
return err
|
|
}
|
|
|
|
it, err := t.SeekFirst()
|
|
more := true
|
|
var data []interface{}
|
|
for more && err == nil {
|
|
if _, data, err = it.Next(); err != nil {
|
|
break
|
|
}
|
|
|
|
more, err = f(nil, data)
|
|
}
|
|
return noEOF(err)
|
|
}
|
|
|
|
func (r *groupByDefaultPlan) fieldNames() []string { return r.fields }
|
|
|
|
type selectIndexDefaultPlan struct {
|
|
nm string
|
|
x interface{}
|
|
}
|
|
|
|
func (r *selectIndexDefaultPlan) hasID() bool { return false }
|
|
|
|
func (r *selectIndexDefaultPlan) explain(w strutil.Formatter) {
|
|
w.Format("┌Iterate all values of index %q\n└Output field names N/A\n", r.nm)
|
|
}
|
|
|
|
func (r *selectIndexDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *selectIndexDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) {
|
|
var x btreeIndex
|
|
switch ix := r.x.(type) {
|
|
case *indexedCol:
|
|
x = ix.x
|
|
case *index2:
|
|
x = ix.x
|
|
default:
|
|
panic("internal error 007")
|
|
}
|
|
|
|
en, err := x.SeekFirst()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
var id int64
|
|
for {
|
|
k, _, err := en.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
id++
|
|
if more, err := f(id, k); !more || err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
func (r *selectIndexDefaultPlan) fieldNames() []string {
|
|
return []string{r.nm}
|
|
}
|
|
|
|
type limitDefaultPlan struct {
|
|
expr expression
|
|
src plan
|
|
fields []string
|
|
}
|
|
|
|
func (r *limitDefaultPlan) hasID() bool { return r.src.hasID() }
|
|
|
|
func (r *limitDefaultPlan) explain(w strutil.Formatter) {
|
|
r.src.explain(w)
|
|
w.Format("┌Pass first %v records\n└Output field names %v\n", r.expr, r.fields)
|
|
}
|
|
|
|
func (r *limitDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *limitDefaultPlan) fieldNames() []string { return r.fields }
|
|
|
|
func (r *limitDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (more bool, err error)) (err error) {
|
|
m := map[interface{}]interface{}{}
|
|
var eval bool
|
|
var lim uint64
|
|
return r.src.do(ctx, func(rid interface{}, in []interface{}) (more bool, err error) {
|
|
if !eval {
|
|
for i, fld := range r.fields {
|
|
if fld != "" {
|
|
m[fld] = in[i]
|
|
}
|
|
}
|
|
m["$id"] = rid
|
|
val, err := r.expr.eval(ctx, m)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if val == nil {
|
|
return true, nil
|
|
}
|
|
|
|
if lim, err = limOffExpr(val); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
eval = true
|
|
}
|
|
switch lim {
|
|
case 0:
|
|
return false, nil
|
|
default:
|
|
lim--
|
|
return f(rid, in)
|
|
}
|
|
})
|
|
}
|
|
|
|
type offsetDefaultPlan struct {
|
|
expr expression
|
|
src plan
|
|
fields []string
|
|
}
|
|
|
|
func (r *offsetDefaultPlan) hasID() bool { return r.src.hasID() }
|
|
|
|
func (r *offsetDefaultPlan) explain(w strutil.Formatter) {
|
|
r.src.explain(w)
|
|
w.Format("┌Skip first %v records\n└Output field names %v\n", r.expr, qnames(r.fields))
|
|
}
|
|
|
|
func (r *offsetDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *offsetDefaultPlan) fieldNames() []string { return r.fields }
|
|
|
|
func (r *offsetDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error {
|
|
m := map[interface{}]interface{}{}
|
|
var eval bool
|
|
var off uint64
|
|
return r.src.do(ctx, func(rid interface{}, in []interface{}) (bool, error) {
|
|
if !eval {
|
|
for i, fld := range r.fields {
|
|
if fld != "" {
|
|
m[fld] = in[i]
|
|
}
|
|
}
|
|
m["$id"] = rid
|
|
val, err := r.expr.eval(ctx, m)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if val == nil {
|
|
return true, nil
|
|
}
|
|
|
|
if off, err = limOffExpr(val); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
eval = true
|
|
}
|
|
if off > 0 {
|
|
off--
|
|
return true, nil
|
|
}
|
|
|
|
return f(rid, in)
|
|
})
|
|
}
|
|
|
|
type orderByDefaultPlan struct {
|
|
asc bool
|
|
by []expression
|
|
src plan
|
|
fields []string
|
|
}
|
|
|
|
func (r *orderByDefaultPlan) hasID() bool { return r.src.hasID() }
|
|
|
|
func (r *orderByDefaultPlan) explain(w strutil.Formatter) {
|
|
r.src.explain(w)
|
|
w.Format("┌Order%s by", map[bool]string{false: " descending"}[r.asc])
|
|
for _, v := range r.by {
|
|
w.Format(" %s,", v)
|
|
}
|
|
w.Format("\n└Output field names %v\n", qnames(r.fields))
|
|
}
|
|
|
|
func (r *orderByDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *orderByDefaultPlan) fieldNames() []string { return r.fields }
|
|
|
|
func (r *orderByDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) {
|
|
t, err := ctx.db.store.CreateTemp(r.asc)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
defer func() {
|
|
if derr := t.Drop(); derr != nil && err == nil {
|
|
err = derr
|
|
}
|
|
}()
|
|
|
|
m := map[interface{}]interface{}{}
|
|
flds := r.fields
|
|
k := make([]interface{}, len(r.by)+1)
|
|
id := int64(-1)
|
|
if err = r.src.do(ctx, func(rid interface{}, in []interface{}) (bool, error) {
|
|
id++
|
|
for i, fld := range flds {
|
|
if fld != "" {
|
|
m[fld] = in[i]
|
|
}
|
|
}
|
|
m["$id"] = rid
|
|
for i, expr := range r.by {
|
|
val, err := expr.eval(ctx, m)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if val != nil {
|
|
val, ordered, err := isOrderedType(val)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if !ordered {
|
|
return false, fmt.Errorf("cannot order by %v (type %T)", val, val)
|
|
|
|
}
|
|
}
|
|
|
|
k[i] = val
|
|
}
|
|
k[len(r.by)] = id
|
|
if err = t.Set(k, in); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return true, nil
|
|
}); err != nil {
|
|
return
|
|
}
|
|
|
|
it, err := t.SeekFirst()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
var data []interface{}
|
|
more := true
|
|
for more && err == nil {
|
|
if _, data, err = it.Next(); err != nil {
|
|
break
|
|
}
|
|
|
|
more, err = f(nil, data)
|
|
}
|
|
return noEOF(err)
|
|
}
|
|
|
|
type selectFieldsDefaultPlan struct {
|
|
flds []*fld
|
|
src plan
|
|
fields []string
|
|
}
|
|
|
|
func (r *selectFieldsDefaultPlan) hasID() bool { return r.src.hasID() }
|
|
|
|
func (r *selectFieldsDefaultPlan) explain(w strutil.Formatter) {
|
|
//TODO check for non existing fields
|
|
r.src.explain(w)
|
|
w.Format("┌Evaluate")
|
|
for _, v := range r.flds {
|
|
w.Format(" %s as %s,", v.expr, fmt.Sprintf("%q", v.name))
|
|
}
|
|
w.Format("\n└Output field names %v\n", qnames(r.fields))
|
|
}
|
|
|
|
func (r *selectFieldsDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *selectFieldsDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error {
|
|
fields := r.src.fieldNames()
|
|
m := map[interface{}]interface{}{}
|
|
return r.src.do(ctx, func(rid interface{}, in []interface{}) (bool, error) {
|
|
for i, nm := range fields {
|
|
if nm != "" {
|
|
m[nm] = in[i]
|
|
}
|
|
}
|
|
m["$id"] = rid
|
|
out := make([]interface{}, len(r.flds))
|
|
for i, fld := range r.flds {
|
|
var err error
|
|
if out[i], err = fld.expr.eval(ctx, m); err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
return f(rid, out)
|
|
})
|
|
}
|
|
|
|
func (r *selectFieldsDefaultPlan) fieldNames() []string { return r.fields }
|
|
|
|
type selectFieldsGroupPlan struct {
|
|
flds []*fld
|
|
src *groupByDefaultPlan
|
|
fields []string
|
|
}
|
|
|
|
func (r *selectFieldsGroupPlan) hasID() bool { return false }
|
|
|
|
func (r *selectFieldsGroupPlan) explain(w strutil.Formatter) {
|
|
//TODO check for non existing fields
|
|
r.src.explain(w)
|
|
w.Format("┌Evaluate")
|
|
for _, v := range r.flds {
|
|
w.Format(" %s as %s,", v.expr, fmt.Sprintf("%q", v.name))
|
|
}
|
|
w.Format("\n└Output field names %v\n", qnames(r.fields))
|
|
}
|
|
|
|
func (r *selectFieldsGroupPlan) fieldNames() []string { return r.fields }
|
|
|
|
func (r *selectFieldsGroupPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *selectFieldsGroupPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error {
|
|
var t temp
|
|
var cols []*col
|
|
var err error
|
|
out := make([]interface{}, len(r.flds))
|
|
ok := false
|
|
rows := false
|
|
if err = r.src.do(ctx, func(rid interface{}, in []interface{}) (bool, error) {
|
|
if ok {
|
|
h := in[0].(int64)
|
|
m := map[interface{}]interface{}{}
|
|
for h != 0 {
|
|
in, err = t.Read(nil, h, cols...)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
rec := in[2:]
|
|
for i, c := range cols {
|
|
if nm := c.name; nm != "" {
|
|
m[nm] = rec[i]
|
|
}
|
|
}
|
|
m["$id"] = rid
|
|
for _, fld := range r.flds {
|
|
if _, err = fld.expr.eval(ctx, m); err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
|
|
h = in[0].(int64)
|
|
}
|
|
m["$agg"] = true
|
|
for i, fld := range r.flds {
|
|
if out[i], err = fld.expr.eval(ctx, m); err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
rows = true
|
|
return f(nil, out)
|
|
}
|
|
|
|
ok = true
|
|
t = in[0].(temp)
|
|
cols = in[1].([]*col)
|
|
if len(r.flds) == 0 { // SELECT *
|
|
r.flds = make([]*fld, len(cols))
|
|
for i, v := range cols {
|
|
r.flds[i] = &fld{expr: &ident{v.name}, name: v.name}
|
|
}
|
|
out = make([]interface{}, len(r.flds))
|
|
}
|
|
return true, nil
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
if rows {
|
|
return nil
|
|
}
|
|
|
|
m := map[interface{}]interface{}{"$agg0": true} // aggregate empty record set
|
|
for i, fld := range r.flds {
|
|
if out[i], err = fld.expr.eval(ctx, m); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
_, err = f(nil, out)
|
|
|
|
return err
|
|
}
|
|
|
|
type sysColumnDefaultPlan struct{}
|
|
|
|
func (r *sysColumnDefaultPlan) hasID() bool { return false }
|
|
|
|
func (r *sysColumnDefaultPlan) explain(w strutil.Formatter) {
|
|
w.Format("┌Iterate all rows of table \"__Column\"\n└Output field names %v\n", qnames(r.fieldNames()))
|
|
}
|
|
|
|
func (r *sysColumnDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *sysColumnDefaultPlan) fieldNames() []string {
|
|
return []string{"TableName", "Ordinal", "Name", "Type"}
|
|
}
|
|
|
|
func (r *sysColumnDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error {
|
|
rec := make([]interface{}, 4)
|
|
di, err := ctx.db.info()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var id int64
|
|
for _, ti := range di.Tables {
|
|
rec[0] = ti.Name
|
|
var ix int64
|
|
for _, ci := range ti.Columns {
|
|
ix++
|
|
rec[1] = ix
|
|
rec[2] = ci.Name
|
|
rec[3] = ci.Type.String()
|
|
id++
|
|
if more, err := f(id, rec); !more || err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type sysIndexDefaultPlan struct{}
|
|
|
|
func (r *sysIndexDefaultPlan) hasID() bool { return false }
|
|
|
|
func (r *sysIndexDefaultPlan) explain(w strutil.Formatter) {
|
|
w.Format("┌Iterate all rows of table \"__Index\"\n└Output field names %v\n", qnames(r.fieldNames()))
|
|
}
|
|
|
|
func (r *sysIndexDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *sysIndexDefaultPlan) fieldNames() []string {
|
|
return []string{"TableName", "ColumnName", "Name", "IsUnique"}
|
|
}
|
|
|
|
func (r *sysIndexDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error {
|
|
rec := make([]interface{}, 4)
|
|
di, err := ctx.db.info()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var id int64
|
|
for _, xi := range di.Indices {
|
|
rec[0] = xi.Table
|
|
rec[1] = xi.Column
|
|
rec[2] = xi.Name
|
|
rec[3] = xi.Unique
|
|
id++
|
|
if more, err := f(id, rec); !more || err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type sysTableDefaultPlan struct{}
|
|
|
|
func (r *sysTableDefaultPlan) hasID() bool { return false }
|
|
|
|
func (r *sysTableDefaultPlan) explain(w strutil.Formatter) {
|
|
w.Format("┌Iterate all rows of table \"__Table\"\n└Output field names %v\n", qnames(r.fieldNames()))
|
|
}
|
|
|
|
func (r *sysTableDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *sysTableDefaultPlan) fieldNames() []string { return []string{"Name", "Schema"} }
|
|
|
|
func (r *sysTableDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) error {
|
|
rec := make([]interface{}, 2)
|
|
di, err := ctx.db.info()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var id int64
|
|
for _, ti := range di.Tables {
|
|
rec[0] = ti.Name
|
|
a := []string{}
|
|
for _, ci := range ti.Columns {
|
|
s := ""
|
|
if ci.NotNull {
|
|
s += " NOT NULL"
|
|
}
|
|
if c := ci.Constraint; c != "" {
|
|
s += " " + c
|
|
}
|
|
if d := ci.Default; d != "" {
|
|
s += " DEFAULT " + d
|
|
}
|
|
a = append(a, fmt.Sprintf("%s %s%s", ci.Name, ci.Type, s))
|
|
}
|
|
rec[1] = fmt.Sprintf("CREATE TABLE %s (%s);", ti.Name, strings.Join(a, ", "))
|
|
id++
|
|
if more, err := f(id, rec); !more || err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type tableNilPlan struct {
|
|
t *table
|
|
}
|
|
|
|
func (r *tableNilPlan) hasID() bool { return true }
|
|
|
|
func (r *tableNilPlan) explain(w strutil.Formatter) {
|
|
w.Format("┌Iterate all rows of table %q\n└Output field names %v\n", r.t.name, qnames(r.fieldNames()))
|
|
}
|
|
|
|
func (r *tableNilPlan) fieldNames() []string { return []string{} }
|
|
|
|
func (r *tableNilPlan) filter(expr expression) (plan, []string, error) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *tableNilPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) {
|
|
t := r.t
|
|
h := t.head
|
|
cols := t.cols
|
|
for h > 0 {
|
|
rec, err := t.store.Read(nil, h, cols...) // 0:next, 1:id, 2...: data
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if m, err := f(rec[1], nil); !m || err != nil {
|
|
return err
|
|
}
|
|
|
|
h = rec[0].(int64) // next
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type tableDefaultPlan struct {
|
|
t *table
|
|
fields []string
|
|
}
|
|
|
|
func (r *tableDefaultPlan) hasID() bool { return true }
|
|
|
|
func (r *tableDefaultPlan) explain(w strutil.Formatter) {
|
|
w.Format("┌Iterate all rows of table %q\n└Output field names %v\n", r.t.name, qnames(r.fields))
|
|
}
|
|
|
|
func (r *tableDefaultPlan) filterBinOp(x *binaryOperation) (plan, []string, error) {
|
|
ok, cn, rval, err := x.isIdentRelOpVal()
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if !ok {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
t := r.t
|
|
c, ix := t.findIndexByColName(cn)
|
|
if ix == nil { // Column cn has no index.
|
|
return nil, []string{fmt.Sprintf("%s(%s)", t.name, cn)}, nil
|
|
}
|
|
|
|
if rval, err = typeCheck1(rval, c); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
switch x.op {
|
|
case eq:
|
|
return &indexPlan{t, cn, ix.name, ix.x, indexEq, rval, rval}, nil, nil
|
|
case '<':
|
|
return &indexPlan{t, cn, ix.name, ix.x, indexLt, nil, rval}, nil, nil
|
|
case le:
|
|
return &indexPlan{t, cn, ix.name, ix.x, indexLe, nil, rval}, nil, nil
|
|
case ge:
|
|
return &indexPlan{t, cn, ix.name, ix.x, indexGe, rval, nil}, nil, nil
|
|
case '>':
|
|
return &indexPlan{t, cn, ix.name, ix.x, indexGt, rval, nil}, nil, nil
|
|
case neq:
|
|
return &indexPlan{t, cn, ix.name, ix.x, indexNe, rval, rval}, nil, nil
|
|
default:
|
|
panic("internal error 069")
|
|
}
|
|
}
|
|
|
|
func (r *tableDefaultPlan) filterIdent(x *ident, trueValue bool) (plan, []string, error) {
|
|
cn := x.s
|
|
t := r.t
|
|
for _, v := range t.cols {
|
|
if v.name != cn {
|
|
continue
|
|
}
|
|
|
|
if v.typ != qBool {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
xi := v.index + 1 // 0: id()
|
|
if xi >= len(t.indices) {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
ix := t.indices[xi]
|
|
if ix == nil { // Column cn has no index.
|
|
return nil, []string{fmt.Sprintf("%s(%s)", t.name, cn)}, nil
|
|
}
|
|
|
|
kind := indexFalse
|
|
if trueValue {
|
|
kind = indexTrue
|
|
}
|
|
return &indexPlan{t, cn, ix.name, ix.x, kind, nil, nil}, nil, nil
|
|
}
|
|
return nil, nil, nil
|
|
}
|
|
|
|
func (r *tableDefaultPlan) filterIsNull(x *isNull) (plan, []string, error) {
|
|
ok, cn := isColumnExpression(x.expr)
|
|
if !ok {
|
|
return nil, nil, nil
|
|
}
|
|
|
|
t := r.t
|
|
_, ix := t.findIndexByColName(cn)
|
|
if ix == nil { // Column cn has no index.
|
|
return nil, []string{fmt.Sprintf("%s(%s)", t.name, cn)}, nil
|
|
}
|
|
|
|
switch {
|
|
case x.not:
|
|
return &indexPlan{t, cn, ix.name, ix.x, indexIsNotNull, nil, nil}, nil, nil
|
|
default:
|
|
return &indexPlan{t, cn, ix.name, ix.x, indexIsNull, nil, nil}, nil, nil
|
|
}
|
|
}
|
|
|
|
func (r *tableDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
cols := mentionedColumns(expr)
|
|
for _, v := range r.fields {
|
|
delete(cols, v)
|
|
}
|
|
for k := range cols {
|
|
return nil, nil, fmt.Errorf("unknown field %s", k)
|
|
}
|
|
|
|
var is []string
|
|
|
|
//TODO var sexpr string
|
|
//TODO for _, ix := range t.indices2 {
|
|
//TODO if len(ix.exprList) != 1 {
|
|
//TODO continue
|
|
//TODO }
|
|
|
|
//TODO if sexpr == "" {
|
|
//TODO sexpr = expr.String()
|
|
//TODO }
|
|
//TODO if ix.sources[0] != sexpr {
|
|
//TODO continue
|
|
//TODO }
|
|
|
|
//TODO }
|
|
|
|
switch x := expr.(type) {
|
|
case *binaryOperation:
|
|
return r.filterBinOp(x)
|
|
case *ident:
|
|
return r.filterIdent(x, true)
|
|
case *isNull:
|
|
return r.filterIsNull(x)
|
|
case *unaryOperation:
|
|
if x.op != '!' {
|
|
break
|
|
}
|
|
|
|
if operand, ok := x.v.(*ident); ok {
|
|
return r.filterIdent(operand, false)
|
|
}
|
|
default:
|
|
//dbg("", expr)
|
|
return nil, is, nil //TODO
|
|
}
|
|
|
|
return nil, is, nil
|
|
}
|
|
|
|
func (r *tableDefaultPlan) do(ctx *execCtx, f func(interface{}, []interface{}) (bool, error)) (err error) {
|
|
t := r.t
|
|
cols := t.cols
|
|
h := t.head
|
|
for h > 0 {
|
|
rec, err := t.row0(ctx, h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
h = rec[0].(int64)
|
|
id := rec[1].(int64)
|
|
for i, c := range cols {
|
|
rec[i] = rec[c.index+2]
|
|
}
|
|
if m, err := f(id, rec[:len(cols)]); !m || err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *tableDefaultPlan) fieldNames() []string { return r.fields }
|
|
|
|
type nullPlan struct {
|
|
fields []string
|
|
}
|
|
|
|
func (r *nullPlan) hasID() bool { return false }
|
|
|
|
func (r *nullPlan) fieldNames() []string { return r.fields }
|
|
|
|
func (r *nullPlan) explain(w strutil.Formatter) {
|
|
w.Format("┌Iterate no rows\n└Output field names %v\n", qnames(r.fields))
|
|
}
|
|
|
|
func (r *nullPlan) do(*execCtx, func(interface{}, []interface{}) (bool, error)) error {
|
|
return nil
|
|
}
|
|
|
|
func (r *nullPlan) filter(expr expression) (plan, []string, error) {
|
|
return r, nil, nil
|
|
}
|
|
|
|
type leftJoinDefaultPlan struct {
|
|
on expression
|
|
rsets []plan
|
|
names []string
|
|
right int
|
|
fields []string
|
|
}
|
|
|
|
func (r *leftJoinDefaultPlan) hasID() bool { return false }
|
|
|
|
func (r *leftJoinDefaultPlan) explain(w strutil.Formatter) {
|
|
w.Format("┌Compute Cartesian product of%i\n")
|
|
for i, v := range r.rsets {
|
|
sel := !isTableOrIndex(v)
|
|
if sel {
|
|
w.Format("┌Iterate all rows of virtual table %q%i\n", r.names[i])
|
|
}
|
|
v.explain(w)
|
|
if sel {
|
|
w.Format("%u└Output field names %v\n", qnames(v.fieldNames()))
|
|
}
|
|
}
|
|
w.Format("Extend the product with all NULL rows of %q when no match for %v%u\n", r.names[len(r.names)-1], r.on)
|
|
w.Format("└Output field names %v\n", qnames(r.fields))
|
|
}
|
|
|
|
func (r *leftJoinDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
var is []string
|
|
for i, v := range r.names {
|
|
e2, err := expr.clone(nil, v)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
p2, is2, err := r.rsets[i].filter(e2)
|
|
is = append(is, is2...)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if p2 != nil {
|
|
r.rsets[i] = p2
|
|
return r, is, nil
|
|
}
|
|
}
|
|
return nil, is, nil
|
|
}
|
|
|
|
type rightJoinDefaultPlan struct {
|
|
leftJoinDefaultPlan
|
|
}
|
|
|
|
func (r *rightJoinDefaultPlan) hasID() bool { return false }
|
|
|
|
func (r *rightJoinDefaultPlan) explain(w strutil.Formatter) {
|
|
w.Format("┌Compute Cartesian product of%i\n")
|
|
for i, v := range r.rsets {
|
|
sel := !isTableOrIndex(v)
|
|
if sel {
|
|
w.Format("┌Iterate all rows of virtual table %q%i\n", r.names[i])
|
|
}
|
|
v.explain(w)
|
|
if sel {
|
|
w.Format("%u└Output field names %v\n", qnames(v.fieldNames()))
|
|
}
|
|
}
|
|
w.Format("Extend the product with all NULL rows of all but %q when no match for %v%u\n", r.names[len(r.names)-1], r.on)
|
|
w.Format("└Output field names %v\n", qnames(r.fields))
|
|
}
|
|
|
|
func (r *rightJoinDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
var is []string
|
|
for i, v := range r.names {
|
|
e2, err := expr.clone(nil, v)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
p2, is2, err := r.rsets[i].filter(e2)
|
|
is = append(is, is2...)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if p2 != nil {
|
|
r.rsets[i] = p2
|
|
return r, is, nil
|
|
}
|
|
}
|
|
return nil, is, nil
|
|
}
|
|
|
|
type fullJoinDefaultPlan struct {
|
|
leftJoinDefaultPlan
|
|
}
|
|
|
|
func (r *fullJoinDefaultPlan) hasID() bool { return false }
|
|
|
|
func (r *fullJoinDefaultPlan) explain(w strutil.Formatter) {
|
|
w.Format("┌Compute Cartesian product of%i\n")
|
|
for i, v := range r.rsets {
|
|
sel := !isTableOrIndex(v)
|
|
if sel {
|
|
w.Format("┌Iterate all rows of virtual table %q%i\n", r.names[i])
|
|
}
|
|
v.explain(w)
|
|
if sel {
|
|
w.Format("%u└Output field names %v\n", qnames(v.fieldNames()))
|
|
}
|
|
}
|
|
w.Format("Extend the product with all NULL rows of %q when no match for %v\n", r.names[len(r.names)-1], r.on)
|
|
w.Format("Extend the product with all NULL rows of all but %q when no match for %v%u\n", r.names[len(r.names)-1], r.on)
|
|
w.Format("└Output field names %v\n", qnames(r.fields))
|
|
}
|
|
|
|
func (r *fullJoinDefaultPlan) filter(expr expression) (plan, []string, error) {
|
|
var is []string
|
|
for i, v := range r.names {
|
|
e2, err := expr.clone(nil, v)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
p2, is2, err := r.rsets[i].filter(e2)
|
|
is = append(is, is2...)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
if p2 != nil {
|
|
r.rsets[i] = p2
|
|
return r, is, nil
|
|
}
|
|
}
|
|
return nil, is, nil
|
|
}
|
|
|
|
func (r *leftJoinDefaultPlan) fieldNames() []string { return r.fields }
|
|
|
|
func (r *leftJoinDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (more bool, err error)) error {
|
|
m := map[interface{}]interface{}{}
|
|
ids := map[string]interface{}{}
|
|
var g func([]interface{}, []plan, int) error
|
|
var match bool
|
|
g = func(prefix []interface{}, rsets []plan, x int) (err error) {
|
|
return rsets[0].do(ctx, func(id interface{}, in []interface{}) (bool, error) {
|
|
ids[r.names[x]] = id
|
|
row := append(prefix, in...)
|
|
if len(rsets) > 1 {
|
|
if len(rsets) == 2 {
|
|
match = false
|
|
}
|
|
if err = g(row, rsets[1:], x+1); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if len(rsets) != 2 || match {
|
|
return true, nil
|
|
}
|
|
|
|
ids[r.names[x+1]] = nil
|
|
return f(ids, append(row, make([]interface{}, r.right)...))
|
|
}
|
|
|
|
for i, fld := range r.fields {
|
|
if fld != "" {
|
|
m[fld] = row[i]
|
|
}
|
|
}
|
|
|
|
val, err := r.on.eval(ctx, m)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if val == nil {
|
|
return true, nil
|
|
}
|
|
|
|
x, ok := val.(bool)
|
|
if !ok {
|
|
return false, fmt.Errorf("invalid ON expression %s (value of type %T)", val, val)
|
|
}
|
|
|
|
if !x {
|
|
return true, nil
|
|
}
|
|
|
|
match = true
|
|
return f(ids, row)
|
|
})
|
|
}
|
|
return g(nil, r.rsets, 0)
|
|
}
|
|
|
|
func (r *rightJoinDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (more bool, err error)) error {
|
|
right := r.right
|
|
left := len(r.fields) - right
|
|
n := len(r.rsets)
|
|
m := map[interface{}]interface{}{}
|
|
ids := map[string]interface{}{}
|
|
var g func([]interface{}, []plan, int) error
|
|
var match bool
|
|
nf := len(r.fields)
|
|
fields := append(append([]string(nil), r.fields[nf-right:]...), r.fields[:nf-right]...)
|
|
g = func(prefix []interface{}, rsets []plan, x int) (err error) {
|
|
return rsets[0].do(ctx, func(id interface{}, in []interface{}) (bool, error) {
|
|
ids[r.names[x]] = id
|
|
row := append(prefix, in...)
|
|
if len(rsets) > 1 {
|
|
if len(rsets) == n {
|
|
match = false
|
|
}
|
|
if err = g(row, rsets[1:], x+1); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if len(rsets) != n || match {
|
|
return true, nil
|
|
}
|
|
|
|
for i := 0; i < n-1; i++ {
|
|
ids[r.names[i]] = nil
|
|
}
|
|
|
|
// rigth, left -> left, right
|
|
return f(ids, append(make([]interface{}, left), row[:right]...))
|
|
}
|
|
|
|
for i, fld := range fields {
|
|
if fld != "" {
|
|
m[fld] = row[i]
|
|
}
|
|
}
|
|
|
|
val, err := r.on.eval(ctx, m)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if val == nil {
|
|
return true, nil
|
|
}
|
|
|
|
x, ok := val.(bool)
|
|
if !ok {
|
|
return false, fmt.Errorf("invalid ON expression %s (value of type %T)", val, val)
|
|
}
|
|
|
|
if !x {
|
|
return true, nil
|
|
}
|
|
|
|
match = true
|
|
// rigth, left -> left, right
|
|
return f(ids, append(append([]interface{}(nil), row[right:]...), row[:right]...))
|
|
})
|
|
}
|
|
return g(nil, append([]plan{r.rsets[n-1]}, r.rsets[:n-1]...), 0)
|
|
}
|
|
|
|
func (r *fullJoinDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (more bool, err error)) error {
|
|
b3 := b.TreeNew(func(a, b interface{}) int {
|
|
x := a.(int64)
|
|
y := b.(int64)
|
|
if x < y {
|
|
return -1
|
|
}
|
|
|
|
if x == y {
|
|
return 0
|
|
}
|
|
|
|
return 1
|
|
})
|
|
m := map[interface{}]interface{}{}
|
|
ids := map[string]interface{}{}
|
|
var g func([]interface{}, []plan, int) error
|
|
var match bool
|
|
var rid int64
|
|
firstR := true
|
|
g = func(prefix []interface{}, rsets []plan, x int) (err error) {
|
|
return rsets[0].do(ctx, func(id interface{}, in []interface{}) (bool, error) {
|
|
ids[r.names[x]] = id
|
|
row := append(prefix, in...)
|
|
if len(rsets) > 1 {
|
|
if len(rsets) == 2 {
|
|
match = false
|
|
rid = 0
|
|
}
|
|
if err = g(row, rsets[1:], x+1); err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if len(rsets) == 2 {
|
|
firstR = false
|
|
}
|
|
if len(rsets) != 2 || match {
|
|
return true, nil
|
|
}
|
|
|
|
ids[r.names[x+1]] = nil
|
|
return f(ids, append(row, make([]interface{}, r.right)...))
|
|
}
|
|
|
|
rid++
|
|
if firstR {
|
|
b3.Set(rid, in)
|
|
}
|
|
for i, fld := range r.fields {
|
|
if fld != "" {
|
|
m[fld] = row[i]
|
|
}
|
|
}
|
|
|
|
val, err := r.on.eval(ctx, m)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if val == nil {
|
|
return true, nil
|
|
}
|
|
|
|
x, ok := val.(bool)
|
|
if !ok {
|
|
return false, fmt.Errorf("invalid ON expression %s (value of type %T)", val, val)
|
|
}
|
|
|
|
if !x {
|
|
return true, nil
|
|
}
|
|
|
|
match = true
|
|
b3.Delete(rid)
|
|
return f(ids, row)
|
|
})
|
|
}
|
|
if err := g(nil, r.rsets, 0); err != nil {
|
|
return err
|
|
}
|
|
|
|
it, err := b3.SeekFirst()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
pref := make([]interface{}, len(r.fields)-r.right)
|
|
for {
|
|
_, v, err := it.Next()
|
|
if err != nil {
|
|
return noEOF(err)
|
|
}
|
|
|
|
more, err := f(nil, append(pref, v.([]interface{})...))
|
|
if err != nil || !more {
|
|
return err
|
|
}
|
|
}
|
|
}
|