Skip to content

Commit

Permalink
Enhanced output for vexplain keys (#16892)
Browse files Browse the repository at this point in the history
Signed-off-by: Andres Taylor <[email protected]>
  • Loading branch information
systay authored Oct 7, 2024
1 parent 670570d commit 30d63d4
Show file tree
Hide file tree
Showing 5 changed files with 393 additions and 113 deletions.
123 changes: 123 additions & 0 deletions go/vt/sqlparser/ast_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,70 @@ func (node *ComparisonExpr) IsImpossible() bool {
return false
}

func (op ComparisonExprOperator) Inverse() ComparisonExprOperator {
switch op {
case EqualOp:
return NotEqualOp
case LessThanOp:
return GreaterEqualOp
case GreaterThanOp:
return LessEqualOp
case LessEqualOp:
return GreaterThanOp
case GreaterEqualOp:
return LessThanOp
case NotEqualOp:
return EqualOp
case NullSafeEqualOp:
return NotEqualOp
case InOp:
return NotInOp
case NotInOp:
return InOp
case LikeOp:
return NotLikeOp
case NotLikeOp:
return LikeOp
case RegexpOp:
return NotRegexpOp
case NotRegexpOp:
return RegexpOp
}
panic("unreachable")
}

// SwitchSides returns the reversed comparison operator if applicable, along with a boolean indicating success.
// For symmetric operators like '=', '!=', and '<=>', it returns the same operator and true.
// For directional comparison operators ('<', '>', '<=', '>='), it returns the opposite operator and true.
// For operators that imply directionality or cannot be logically reversed (such as 'IN', 'LIKE', 'REGEXP'),
// it returns the original operator and false, indicating that switching sides is not valid.
func (op ComparisonExprOperator) SwitchSides() (ComparisonExprOperator, bool) {
switch op {
case EqualOp, NotEqualOp, NullSafeEqualOp:
// These operators are symmetric, so switching sides has no effect
return op, true
case LessThanOp:
return GreaterThanOp, true
case GreaterThanOp:
return LessThanOp, true
case LessEqualOp:
return GreaterEqualOp, true
case GreaterEqualOp:
return LessEqualOp, true
default:
return op, false
}
}

func (op ComparisonExprOperator) IsCommutative() bool {
switch op {
case EqualOp, NotEqualOp, NullSafeEqualOp:
return true
default:
return false
}
}

// NewStrLiteral builds a new StrVal.
func NewStrLiteral(in string) *Literal {
return &Literal{Type: StrVal, Val: in}
Expand Down Expand Up @@ -1498,6 +1562,65 @@ func (op ComparisonExprOperator) ToString() string {
}
}

func ComparisonExprOperatorFromJson(s string) ComparisonExprOperator {
switch s {
case EqualStr:
return EqualOp
case JsonLessThanStr:
return LessThanOp
case JsonGreaterThanStr:
return GreaterThanOp
case JsonLessThanOrEqualStr:
return LessEqualOp
case JsonGreaterThanOrEqualStr:
return GreaterEqualOp
case NotEqualStr:
return NotEqualOp
case NullSafeEqualStr:
return NullSafeEqualOp
case InStr:
return InOp
case NotInStr:
return NotInOp
case LikeStr:
return LikeOp
case NotLikeStr:
return NotLikeOp
case RegexpStr:
return RegexpOp
case NotRegexpStr:
return NotRegexpOp
default:
return 0
}
}

const (
JsonGreaterThanStr = "gt"
JsonLessThanStr = "lt"
JsonGreaterThanOrEqualStr = "ge"
JsonLessThanOrEqualStr = "le"
)

// JSONString returns a string representation for this operator that does not need escaping in JSON
func (op ComparisonExprOperator) JSONString() string {
switch op {
case EqualOp, NotEqualOp, NullSafeEqualOp, InOp, NotInOp, LikeOp, NotLikeOp, RegexpOp, NotRegexpOp:
// These operators are safe for JSON output, so we delegate to ToString
return op.ToString()
case LessThanOp:
return JsonLessThanStr
case GreaterThanOp:
return JsonGreaterThanStr
case LessEqualOp:
return JsonLessThanOrEqualStr
case GreaterEqualOp:
return JsonGreaterThanOrEqualStr
default:
panic("unreachable")
}
}

// ToString returns the operator as a string
func (op IsExprOperator) ToString() string {
switch op {
Expand Down
41 changes: 0 additions & 41 deletions go/vt/sqlparser/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -692,47 +692,6 @@ const (
All
)

func (op ComparisonExprOperator) Inverse() ComparisonExprOperator {
switch op {
case EqualOp:
return NotEqualOp
case LessThanOp:
return GreaterEqualOp
case GreaterThanOp:
return LessEqualOp
case LessEqualOp:
return GreaterThanOp
case GreaterEqualOp:
return LessThanOp
case NotEqualOp:
return EqualOp
case NullSafeEqualOp:
return NotEqualOp
case InOp:
return NotInOp
case NotInOp:
return InOp
case LikeOp:
return NotLikeOp
case NotLikeOp:
return LikeOp
case RegexpOp:
return NotRegexpOp
case NotRegexpOp:
return RegexpOp
}
panic("unreachable")
}

func (op ComparisonExprOperator) IsCommutative() bool {
switch op {
case EqualOp, NotEqualOp, NullSafeEqualOp:
return true
default:
return false
}
}

// Constant for Enum Type - IsExprOperator
const (
IsNullOp IsExprOperator = iota
Expand Down
84 changes: 54 additions & 30 deletions go/vt/vtgate/executor_vexplain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,54 +122,61 @@ func TestVExplainKeys(t *testing.T) {
{
query: "select count(*), col2 from music group by col2",
expectedRowString: `{
"statementType": "SELECT",
"groupingColumns": [
"music.col2"
],
"statementType": "SELECT"
"selectColumns": [
"music.col2"
]
}`,
}, {
query: "select * from user u join user_extra ue on u.id = ue.user_id where u.col1 > 100 and ue.noLimit = 'foo'",
expectedRowString: `{
"statementType": "SELECT",
"joinColumns": [
"user.id",
"user_extra.user_id"
"user.id =",
"user_extra.user_id ="
],
"filterColumns": [
"user.col1",
"user_extra.noLimit"
],
"statementType": "SELECT"
"user.col1 gt",
"user_extra.noLimit ="
]
}`,
}, {
// same as above, but written differently
query: "select * from user_extra ue, user u where ue.noLimit = 'foo' and u.col1 > 100 and ue.user_id = u.id",
expectedRowString: `{
"statementType": "SELECT",
"joinColumns": [
"user.id",
"user_extra.user_id"
"user.id =",
"user_extra.user_id ="
],
"filterColumns": [
"user.col1",
"user_extra.noLimit"
],
"statementType": "SELECT"
"user.col1 gt",
"user_extra.noLimit ="
]
}`,
},
{
query: "select u.foo, ue.bar, count(*) from user u join user_extra ue on u.id = ue.user_id where u.name = 'John Doe' group by 1, 2",
expectedRowString: `{
"statementType": "SELECT",
"groupingColumns": [
"user.foo",
"user_extra.bar"
],
"joinColumns": [
"user.id",
"user_extra.user_id"
"user.id =",
"user_extra.user_id ="
],
"filterColumns": [
"user.name"
"user.name ="
],
"statementType": "SELECT"
"selectColumns": [
"user.foo",
"user_extra.bar"
]
}`,
},
{
Expand All @@ -181,47 +188,64 @@ func TestVExplainKeys(t *testing.T) {
{
query: "select name, sum(amount) from user group by name",
expectedRowString: `{
"statementType": "SELECT",
"groupingColumns": [
"user.name"
],
"statementType": "SELECT"
"selectColumns": [
"user.amount",
"user.name"
]
}`,
},
{
query: "select name from user where age > 30",
expectedRowString: `{
"statementType": "SELECT",
"filterColumns": [
"user.age"
"user.age gt"
],
"statementType": "SELECT"
"selectColumns": [
"user.name"
]
}`,
},
{
query: "select * from user where name = 'apa' union select * from user_extra where name = 'monkey'",
expectedRowString: `{
"statementType": "SELECT",
"filterColumns": [
"user.name",
"user_extra.name"
],
"statementType": "SELECT"
"user.name =",
"user_extra.name ="
]
}`,
},
{
query: "update user set name = 'Jane Doe' where id = 1",
expectedRowString: `{
"statementType": "UPDATE",
"filterColumns": [
"user.id"
],
"statementType": "UPDATE"
"user.id ="
]
}`,
},
{
query: "delete from user where order_date < '2023-01-01'",
expectedRowString: `{
"statementType": "DELETE",
"filterColumns": [
"user.order_date"
],
"statementType": "DELETE"
"user.order_date lt"
]
}`,
},
{
query: "select * from user where name between 'A' and 'C'",
expectedRowString: `{
"statementType": "SELECT",
"filterColumns": [
"user.name ge",
"user.name le"
]
}`,
},
}
Expand Down
Loading

0 comments on commit 30d63d4

Please sign in to comment.