Commit 81397c03 authored by cznic's avatar cznic Committed by GitHub

Merge pull request #156 from gernest/issue155

Implement dummy table support, part I.

Some additional tests are probably needed before going into the master branch, but otherwise the implementation seems complete.
parents 5078e40c 88e405ca
......@@ -2802,7 +2802,14 @@ func testMentionedColumns(s stmt) (err error) {
}
}
if w := x.where; w != nil {
mentionedColumns(w.expr)
if e := w.expr; e != nil {
mentionedColumns(w.expr)
}
if s := w.sel; s != nil {
if err := testMentionedColumns(s); err != nil {
return err
}
}
}
case *updateStmt:
for _, v := range x.list {
......
......@@ -35,6 +35,7 @@ const (
var (
_ plan = (*crossJoinDefaultPlan)(nil)
_ plan = (*distinctDefaultPlan)(nil)
_ plan = (*emptyFieldsPlan)(nil)
_ plan = (*explainDefaultPlan)(nil)
_ plan = (*filterDefaultPlan)(nil)
_ plan = (*fullJoinDefaultPlan)(nil)
......@@ -49,6 +50,7 @@ var (
_ plan = (*selectFieldsDefaultPlan)(nil)
_ plan = (*selectFieldsGroupPlan)(nil)
_ plan = (*selectIndexDefaultPlan)(nil)
_ plan = (*selectDummyPlan)(nil)
_ plan = (*sysColumnDefaultPlan)(nil)
_ plan = (*sysIndexDefaultPlan)(nil)
_ plan = (*sysTableDefaultPlan)(nil)
......@@ -2798,3 +2800,37 @@ func (r *fullJoinDefaultPlan) do(ctx *execCtx, f func(id interface{}, data []int
}
}
}
type selectDummyPlan struct {
fields []interface{}
}
func (r *selectDummyPlan) hasID() bool { return true }
func (r *selectDummyPlan) explain(w strutil.Formatter) {
w.Format("┌Selects values from dummy table\n└Output field names %v\n", qnames(r.fieldNames()))
}
func (r *selectDummyPlan) fieldNames() []string { return make([]string, len(r.fields)) }
func (r *selectDummyPlan) filter(expr expression) (plan, []string, error) {
return nil, nil, nil
}
func (r *selectDummyPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) {
_, err = f(nil, r.fields)
return
}
type emptyFieldsPlan struct {
*nullPlan
}
func (r *emptyFieldsPlan) do(ctx *execCtx, f func(id interface{}, data []interface{}) (bool, error)) (err error) {
v := make([]interface{}, len(r.fields))
for i := range v {
v[i] = ""
}
_, err = f(nil, v)
return
}
......@@ -336,6 +336,17 @@ type whereRset struct {
exists bool
}
func (r *whereRset) String() string {
if r.sel != nil {
s := ""
if !r.exists {
s += " NOT "
}
return fmt.Sprintf("%s EXISTS ( %s )", s, strings.TrimSuffix(r.sel.String(), ";"))
}
return r.expr.String()
}
func (r *whereRset) planBinOp(x *binaryOperation) (plan, error) {
p := r.src
ok, cn := isColumnExpression(x.l)
......@@ -516,6 +527,34 @@ func (r *whereRset) planUnaryOp(x *unaryOperation) (plan, error) {
}
func (r *whereRset) plan(ctx *execCtx) (plan, error) {
if r.sel != nil {
var exists bool
p, err := r.sel.plan(ctx)
if err != nil {
return nil, err
}
err = p.do(ctx, func(i interface{}, data []interface{}) (bool, error) {
if len(data) > 0 {
exists = true
}
return false, nil
})
if err != nil {
return nil, err
}
if r.exists && exists {
return p, nil
}
np := &nullPlan{fields: p.fieldNames()}
return &emptyFieldsPlan{nullPlan: np}, nil
}
return r.planExpr(ctx)
}
func (r *whereRset) planExpr(ctx *execCtx) (plan, error) {
if r.expr == nil {
return &nullPlan{}, nil
}
expr, err := r.expr.clone(ctx.arg)
if err != nil {
return nil, err
......@@ -1615,7 +1654,15 @@ type joinRset struct {
on expression
}
func (r *joinRset) isZero() bool {
return len(r.sources) == 0 && r.typ == 0 && r.on == nil
}
func (r *joinRset) String() string {
if r.isZero() {
return ""
}
a := make([]string, len(r.sources))
for i, pair0 := range r.sources {
pair := pair0.([]interface{})
......@@ -1658,6 +1705,9 @@ func (r *joinRset) String() string {
}
func (r *joinRset) plan(ctx *execCtx) (plan, error) {
if r.isZero() {
return nil, nil
}
rsets := make([]plan, len(r.sources))
names := make([]string, len(r.sources))
var err error
......
......@@ -750,11 +750,16 @@ func (s *selectStmt) String() string {
}
b.WriteString(" " + strings.Join(a, ", "))
}
b.WriteString(" FROM ")
b.WriteString(s.from.String())
if s.from != nil {
if !s.from.isZero() {
b.WriteString(" FROM ")
b.WriteString(s.from.String())
}
}
if s.where != nil {
b.WriteString(" WHERE ")
b.WriteString(s.where.expr.String())
b.WriteString(s.where.String())
}
if s.group != nil {
b.WriteString(" GROUP BY ")
......@@ -777,13 +782,25 @@ func (s *selectStmt) String() string {
}
func (s *selectStmt) plan(ctx *execCtx) (plan, error) { //LATER overlapping goroutines/pipelines
r, err := s.from.plan(ctx)
if err != nil {
return nil, err
var r plan
var err error
if s.from != nil {
r, err = s.from.plan(ctx)
if err != nil {
return nil, err
}
}
if r == nil {
var fds []interface{}
for _, v := range s.flds {
if val, ok := v.expr.(value); ok {
fds = append(fds, val)
}
}
r = &selectDummyPlan{fields: fds}
}
if w := s.where; w != nil {
if r, err = (&whereRset{expr: w.expr, src: r}).plan(ctx); err != nil {
if r, err = (&whereRset{expr: w.expr, src: r, sel: w.sel, exists: w.exists}).plan(ctx); err != nil {
return nil, err
}
}
......
......@@ -8396,3 +8396,24 @@ SELECT * FROM t WHERE c1 == 1;
│CREATE INDEX xt_c1 ON t(c1);
└Output field names ["c1" "c2"]
---- 1347
SELECT 42;
┌Selects values from dummy table
└Output field names [""]
┌Evaluate 42 as "",
└Output field names [""]
---- 1348
SELECT * FROM t WHERE EXISTS ( SELECT * FROM t WHERE i == 2 );
┌Iterate all rows of table "t"
└Output field names ["i"]
┌Filter on i == 2
│Possibly useful indices
│CREATE INDEX xt_i ON t(i);
└Output field names ["i"]
---- 1349
SELECT * FROM t WHERE EXISTS ( SELECT * FROM t WHERE i == 2 );
┌Iterate no rows
└Output field names ["i"]
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment