Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

slack-15.0: backport vitessio/vitess#16154 #420

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions go/test/endtoend/utils/cmp.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,17 @@ import (
"vitess.io/vitess/go/test/utils"
)

type TestingT interface {
require.TestingT
Helper()
}

type MySQLCompare struct {
t *testing.T
t TestingT
MySQLConn, VtConn *mysql.Conn
}

func NewMySQLCompare(t *testing.T, vtParams, mysqlParams mysql.ConnParams) (MySQLCompare, error) {
func NewMySQLCompare(t TestingT, vtParams, mysqlParams mysql.ConnParams) (MySQLCompare, error) {
ctx := context.Background()
vtConn, err := mysql.Connect(ctx, &vtParams)
if err != nil {
Expand All @@ -54,6 +59,10 @@ func NewMySQLCompare(t *testing.T, vtParams, mysqlParams mysql.ConnParams) (MySQ
}, nil
}

func (mcmp *MySQLCompare) AsT() *testing.T {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think anything calls this, but in the upstream change something does. I'll just leave it here

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used by https://github.com/vitessio/vitess-tester, AFAIK

return mcmp.t.(*testing.T)
}

func (mcmp *MySQLCompare) Close() {
mcmp.VtConn.Close()
mcmp.MySQLConn.Close()
Expand Down
16 changes: 7 additions & 9 deletions go/test/endtoend/utils/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ import (
"fmt"
"os"
"path"
"testing"

"github.com/stretchr/testify/assert"

Expand Down Expand Up @@ -132,16 +131,16 @@ func prepareMySQLWithSchema(params mysql.ConnParams, sql string) error {
return nil
}

func compareVitessAndMySQLResults(t *testing.T, query string, vtQr, mysqlQr *sqltypes.Result, compareColumns bool) {
func compareVitessAndMySQLResults(t TestingT, query string, vtQr, mysqlQr *sqltypes.Result, compareColumns bool) {
if vtQr == nil && mysqlQr == nil {
return
}
if vtQr == nil {
t.Error("Vitess result is 'nil' while MySQL's is not.")
t.Errorf("Vitess result is 'nil' while MySQL's is not.")
return
}
if mysqlQr == nil {
t.Error("MySQL result is 'nil' while Vitess' is not.")
t.Errorf("MySQL result is 'nil' while Vitess' is not.")
return
}
if compareColumns {
Expand All @@ -163,7 +162,7 @@ func compareVitessAndMySQLResults(t *testing.T, query string, vtQr, mysqlQr *sql
}
stmt, err := sqlparser.Parse(query)
if err != nil {
t.Error(err)
t.Errorf(err.Error())
return
}
orderBy := false
Expand All @@ -185,13 +184,12 @@ func compareVitessAndMySQLResults(t *testing.T, query string, vtQr, mysqlQr *sql
for _, row := range mysqlQr.Rows {
errStr += fmt.Sprintf("%s\n", row)
}
t.Error(errStr)
t.Errorf(errStr)
}

func compareVitessAndMySQLErrors(t *testing.T, vtErr, mysqlErr error) {
func compareVitessAndMySQLErrors(t TestingT, vtErr, mysqlErr error) {
if vtErr != nil && mysqlErr != nil || vtErr == nil && mysqlErr == nil {
return
}
out := fmt.Sprintf("Vitess and MySQL are not erroring the same way.\nVitess error: %v\nMySQL error: %v", vtErr, mysqlErr)
t.Error(out)
t.Errorf("Vitess and MySQL are not erroring the same way.\nVitess error: %v\nMySQL error: %v", vtErr, mysqlErr)
}
2 changes: 1 addition & 1 deletion go/test/endtoend/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func ExecCompareMySQL(t *testing.T, vtConn, mysqlConn *mysql.Conn, query string)

// ExecAllowError executes the given query without failing the test if it produces
// an error. The error is returned to the client, along with the result set.
func ExecAllowError(t *testing.T, conn *mysql.Conn, query string) (*sqltypes.Result, error) {
func ExecAllowError(t TestingT, conn *mysql.Conn, query string) (*sqltypes.Result, error) {
t.Helper()
return conn.ExecuteFetch(query, 1000, true)
}
Expand Down
12 changes: 12 additions & 0 deletions go/test/endtoend/vtgate/gen4/gen4_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -495,3 +495,15 @@ func TestFilterOnLeftOuterJoin(t *testing.T) {

mcmp.AssertMatches(query, "[[INT32(22)] [INT32(33)]]")
}

func BenchmarkCountStar(b *testing.B) {
mcmp, closer := start(b)
defer closer()

// insert some data.
mcmp.Exec(`insert into t2(id, tcol1, tcol2) values (1, 'A%B', 'A%B'),(2, 'C_D', 'E'),(3, 'AB', 'C1D'),(4, 'E', 'A%B'),(5, 'A%B', 'AB'),(6, 'C1D', 'E'),(7, 'C_D', 'A%B'),(8, 'E', 'C_D')`)

for i := 0; i < b.N; i++ {
mcmp.Exec(`select Count(*) from t2`)
}
}
2 changes: 1 addition & 1 deletion go/test/endtoend/vtgate/gen4/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func TestMain(m *testing.M) {
os.Exit(exitCode)
}

func start(t *testing.T) (utils.MySQLCompare, func()) {
func start(t testing.TB) (utils.MySQLCompare, func()) {
mcmp, err := utils.NewMySQLCompare(t, vtParams, mysqlParams)
require.NoError(t, err)
deleteAll := func() {
Expand Down
28 changes: 28 additions & 0 deletions go/vt/sqlparser/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -2707,6 +2707,34 @@ type (
}

CountStar struct {
Name string
Copy link
Member Author

@timvaillancourt timvaillancourt Jun 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GuptaManan100 would this reduce the cost of this change? Enough to matter?

type CountStarName int

var (
        CountStarLower CountStarName = iota
        CountStarCapital
        CountStarUpper

        CountStarNameStr map[CountStarName]string{
                CountStarLower:   "count(*)",
                CountStarCapital: "Count(*)",
                CountStarUpper:   "COUNT(*)",
        }
)

func (csn CountStarName) String() string {
        return CountStarNameStr[csn]
}

or a more generic:

var (
        AggregatorLower AggregatorName = iota
        AggregatorCapital
        AggregatorUpper
)

func (an AggregatorName) Name(name string) string {
        switch an {
        case AggregatorLower:
                return strings.ToLower(name)
        case AggregatorCapital:
                return strings.Title(name)
        case AggregatorUpper:
                return strings.ToUpper(name)
        }
}

(count, Count and COUNT only)

Name string would become Name CountStarName in CountStar struct

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could matter. It would definitely reduce the size of the struct, but how meaningful that overall change is going to be to runtime, we'll have to test. I don't think it would be very significant either way, because we do need to print the string eventually during formatting.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GuptaManan100 makes sense. I think we aren't too worried about something this finite, so I suppose I'm just thinking aloud how a potential fix to main could be done without adding overhead

_ bool
// TL;DR; This makes sure that reference equality checks works as expected
//
// You're correct that this might seem a bit strange at first glance.
// It's a quirk of Go's handling of empty structs. In Go, two instances of an empty struct are considered
// identical, which can be problematic when using these as keys in maps.
// They would be treated as the same key and potentially lead to incorrect map behavior.
//
// Here's a brief example:
//
// ```golang
// func TestWeirdGo(t *testing.T) {
// type CountStar struct{}
//
// cs1 := &CountStar{}
// cs2 := &CountStar{}
// if cs1 == cs2 {
// panic("what the what!?")
// }
// }
// ```
//
// In the above code, cs1 and cs2, despite being distinct variables, would be treated as the same object.
//
// The solution we employed was to add a dummy field `_ bool` to the otherwise empty struct `CountStar`.
// This ensures that each instance of `CountStar` is treated as a separate object,
// even in the context of out semantic state which uses these objects as map keys.
}
Copy link
Member Author

@timvaillancourt timvaillancourt Jun 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting that I didn't include the OverClause field from the upstream change - v15 doesn't have the OverClause support

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context on OverClause: vitessio#13444


Avg struct {
Expand Down
2 changes: 1 addition & 1 deletion go/vt/sqlparser/ast_equals.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions go/vt/sqlparser/ast_format.go
Original file line number Diff line number Diff line change
Expand Up @@ -2656,8 +2656,7 @@ func (node *Count) Format(buf *TrackedBuffer) {
}

func (node *CountStar) Format(buf *TrackedBuffer) {
buf.astPrintf(node, "%s(", node.AggrName())
buf.WriteString("*)")
buf.astPrintf(node, "%s(*)", node.Name)
}

func (node *Avg) Format(buf *TrackedBuffer) {
Expand Down
5 changes: 2 additions & 3 deletions go/vt/sqlparser/ast_format_fast.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions go/vt/sqlparser/cached_size.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions go/vt/sqlparser/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5294,8 +5294,8 @@ func TestOne(t *testing.T) {
testOne := struct {
input, output string
}{
input: "",
output: "",
input: "Select Count(*) from t",
output: "select Count(*) from t",
}
if testOne.input == "" {
return
Expand Down
2 changes: 1 addition & 1 deletion go/vt/sqlparser/sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion go/vt/sqlparser/sql.y
Original file line number Diff line number Diff line change
Expand Up @@ -5755,7 +5755,7 @@ UTC_DATE func_paren_opt
}
| COUNT openb '*' closeb
{
$$ = &CountStar{}
$$ = &CountStar{Name: $1}
}
| COUNT openb distinct_opt expression_list closeb
{
Expand Down
2 changes: 1 addition & 1 deletion go/vt/vtgate/planbuilder/select.go
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ func buildSQLCalcFoundRowsPlan(
sel2.Limit = nil

countStartExpr := []sqlparser.SelectExpr{&sqlparser.AliasedExpr{
Expr: &sqlparser.CountStar{},
Expr: &sqlparser.CountStar{Name: "count"},
}}
if sel2.GroupBy == nil && sel2.Having == nil {
// if there is no grouping, we can use the same query and
Expand Down
63 changes: 61 additions & 2 deletions go/vt/vtgate/planbuilder/testdata/onecase.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,68 @@
[
{
"comment": "Add your test case here for debugging and run go test -run=One.",
"query": "",
"query": "select Count(*) from user join user_extra where user.intcol + user_extra.incol < 5 limit 5",
"plan": {

"QueryType": "SELECT",
"Original": "select Count(*) from user join user_extra where user.intcol + user_extra.incol < 5 limit 5",
"Instructions": {
"OperatorType": "Limit",
"Count": "5",
"Inputs": [
{
"OperatorType": "Aggregate",
"Variant": "Scalar",
"Aggregates": "sum_count_star(0) AS Count(*)",
"Inputs": [
{
"OperatorType": "Projection",
"Expressions": [
"count(*) * count(*) as Count(*)"
],
"Inputs": [
{
"OperatorType": "Join",
"Variant": "Join",
"JoinColumnIndexes": "L:0,R:0",
"JoinVars": {
"user_intcol": 1
},
"TableName": "`user`_user_extra",
"Inputs": [
{
"OperatorType": "Route",
"Variant": "Scatter",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select count(*), `user`.intcol from `user` where 1 != 1 group by `user`.intcol",
"Query": "select count(*), `user`.intcol from `user` group by `user`.intcol",
"Table": "`user`"
},
{
"OperatorType": "Route",
"Variant": "Scatter",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select count(*) from user_extra where 1 != 1 group by .0",
"Query": "select count(*) from user_extra where :user_intcol + user_extra.incol < 5 group by .0",
"Table": "user_extra"
}
]
}
]
}
]
}
]
},
"TablesUsed": [
"user.user",
"user.user_extra"
]
}
}
]
Loading