// VisitBinaryOperation swaps the right side identifier to left side if left side expression is static. // So it can be used in index plan. func (iev *IdentEvalVisitor) VisitBinaryOperation(binop *expression.BinaryOperation) (expression.Expression, error) { var err error binop.L, err = binop.L.Accept(iev) if err != nil { return binop, errors.Trace(err) } binop.R, err = binop.R.Accept(iev) if err != nil { return binop, errors.Trace(err) } if binop.L.IsStatic() { if _, ok := binop.R.(*expression.Ident); ok { switch binop.Op { case opcode.EQ: case opcode.NE: case opcode.NullEQ: case opcode.LT: binop.Op = opcode.GE case opcode.LE: binop.Op = opcode.GT case opcode.GE: binop.Op = opcode.LT case opcode.GT: binop.Op = opcode.LE default: // unsupported opcode return binop, nil } binop.L, binop.R = binop.R, binop.L } } return binop, nil }
func (r *TableDefaultPlan) filterBinOp(ctx context.Context, x *expression.BinaryOperation) (plan.Plan, bool, error) { ok, name, rval, err := x.IsIdentCompareVal() if err != nil { return r, false, errors.Trace(err) } if !ok { return r, false, nil } if rval == nil { // if nil, any <, <=, >, >=, =, != operator will do nothing // any value compared null returns null // TODO: if we support <=> later, we must handle null return &NullPlan{r.GetFields()}, true, nil } _, tn, cn := field.SplitQualifiedName(name) t := r.T if tn != "" && tn != t.TableName().L { return r, false, nil } c := column.FindCol(t.Cols(), cn) if c == nil { return nil, false, errors.Errorf("No such column: %s", cn) } var seekVal interface{} if seekVal, err = types.Convert(rval, &c.FieldType); err != nil { return nil, false, errors.Trace(err) } spans := toSpans(x.Op, rval, seekVal) if c.IsPKHandleColumn(r.T.Meta()) { if r.rangeScan { spans = filterSpans(r.spans, spans) } return &TableDefaultPlan{ T: r.T, Fields: r.Fields, rangeScan: true, spans: spans, }, true, nil } else if r.rangeScan { // Already filtered on PK handle column, should not switch to index plan. return r, false, nil } ix := t.FindIndexByColName(cn) if ix == nil { // Column cn has no index. return r, false, nil } return &indexPlan{ src: t, col: c, unique: ix.Unique, idxName: ix.Name.O, idx: ix.X, spans: spans, }, true, nil }
func (r *TableDefaultPlan) filterBinOp(ctx context.Context, x *expression.BinaryOperation) (plan.Plan, bool, error) { ok, name, rval, err := x.IsIdentCompareVal() if err != nil { return r, false, err } if !ok { return r, false, nil } if rval == nil { // if nil, any <, <=, >, >=, =, != operator will do nothing // any value compared null returns null // TODO: if we support <=> later, we must handle null return &NullPlan{r.GetFields()}, true, nil } _, tn, cn := field.SplitQualifiedName(name) t := r.T if tn != "" && tn != t.TableName().L { return r, false, nil } c := column.FindCol(t.Cols(), cn) if c == nil { return nil, false, errors.Errorf("No such column: %s", cn) } ix := t.FindIndexByColName(cn) if ix == nil { // Column cn has no index. return r, false, nil } var seekVal interface{} if seekVal, err = types.Convert(rval, &c.FieldType); err != nil { return nil, false, err } return &indexPlan{ src: t, col: c, idxName: ix.Name.O, idx: ix.X, spans: toSpans(x.Op, rval, seekVal), }, true, nil }