Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion data/test/tabletserver/exec_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
{
"PlanID": "PASS_SELECT",
"TableName": "a",
"FieldQuery": "select * from a where 1 != 1",
"FieldQuery": "select * from a where 1 != 1 group by b",
"FullQuery": "select * from a group by b limit :#maxLimit"
}

Expand Down
22 changes: 18 additions & 4 deletions data/test/vtgate/postprocess_cases.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# Group by with aggregation
"select col1, count(col2) as col2_count from unsharded group by col1 order by col2_count asc"
{
"Original": "select col1, count(col2) as col2_count from unsharded group by col1 order by col2_count asc",
"Instructions": {
"Opcode": "SelectUnsharded",
"Keyspace": {
"Name": "main",
"Sharded": false
},
"Query": "select col1, count(col2) as col2_count from unsharded group by col1 order by col2_count asc",
"FieldQuery": "select col1, count(col2) as col2_count from unsharded where 1 != 1 group by col1"
}
}
# Group by unsharded
"select col1, col2 from unsharded group by col2"
{
Expand All @@ -9,7 +23,7 @@
"Sharded": false
},
"Query": "select col1, col2 from unsharded group by col2",
"FieldQuery": "select col1, col2 from unsharded where 1 != 1"
"FieldQuery": "select col1, col2 from unsharded where 1 != 1 group by col2"
}
}

Expand All @@ -24,7 +38,7 @@
"Sharded": true
},
"Query": "select col1, col2 from user where id = 1 group by col2",
"FieldQuery": "select col1, col2 from user where 1 != 1",
"FieldQuery": "select col1, col2 from user where 1 != 1 group by col2",
"Vindex": "user_index",
"Values": 1
}
Expand All @@ -41,7 +55,7 @@
"Sharded": true
},
"Query": "select col1, id from user group by id",
"FieldQuery": "select col1, id from user where 1 != 1"
"FieldQuery": "select col1, id from user where 1 != 1 group by id"
}
}

Expand All @@ -56,7 +70,7 @@
"Sharded": true
},
"Query": "select id from user group by id, col",
"FieldQuery": "select id from user where 1 != 1"
"FieldQuery": "select id from user where 1 != 1 group by id, col"
}
}

Expand Down
28 changes: 28 additions & 0 deletions go/vt/sqlparser/impossible_query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package sqlparser

// FormatImpossibleQuery creates an impossible query in a TrackedBuffer.
// An impossible query is a modified version of a query where all selects have where clauses that are
// impossible for mysql to resolve. This is used in the vtgate and vttablet:
//
// - In the vtgate it's used for joins: if the first query returns no result, then vtgate uses the impossible
// query just to fetch field info from vttablet
// - In the vttablet, it's just an optimization: the field info is fetched once form MySQL, cached and reused
// for subsequent queries
func FormatImpossibleQuery(buf *TrackedBuffer, node SQLNode) {
switch node := node.(type) {
case *Select:
buf.Myprintf("select %v from %v where 1 != 1", node.SelectExprs, node.From)
if node.GroupBy != nil {
node.GroupBy.Format(buf)
}
case *JoinTableExpr:
if node.Join == LeftJoinStr || node.Join == RightJoinStr {
// ON clause is requried
buf.Myprintf("%v %s %v on 1 != 1", node.LeftExpr, node.Join, node.RightExpr)
} else {
buf.Myprintf("%v %s %v", node.LeftExpr, node.Join, node.RightExpr)
}
default:
node.Format(buf)
}
}
7 changes: 7 additions & 0 deletions go/vt/sqlparser/tracked_buffer.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ func NewTrackedBuffer(nodeFormatter func(buf *TrackedBuffer, node SQLNode)) *Tra
}
}

// Convenience function, initiates the writing of a single SQLNode tree by passing
// through to Myprintf with a default format string
func (buf *TrackedBuffer) WriteNode(node SQLNode) *TrackedBuffer {
buf.Myprintf("%v", node)
return buf
}

// Myprintf mimics fmt.Fprintf(buf, ...), but limited to Node(%v),
// Node.Value(%s) and string(%s). It also allows a %a for a value argument, in
// which case it adds tracking info for future substitutions.
Expand Down
2 changes: 1 addition & 1 deletion go/vt/tabletserver/endtoend/queries_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func TestNocacheCases(t *testing.T) {
{"1", "3"},
},
Rewritten: []string{
"select eid, sum(id) from vitess_a where 1 != 1",
"select eid, sum(id) from vitess_a where 1 != 1 group by eid",
"select /* group by */ eid, sum(id) from vitess_a group by eid limit 10001",
},
RowsAffected: 1,
Expand Down
25 changes: 3 additions & 22 deletions go/vt/tabletserver/planbuilder/query_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,13 @@ func GenerateFullQuery(statement sqlparser.Statement) *sqlparser.ParsedQuery {
// GenerateFieldQuery generates a query to just fetch the field info
// by adding impossible where clauses as needed.
func GenerateFieldQuery(statement sqlparser.Statement) *sqlparser.ParsedQuery {
buf := sqlparser.NewTrackedBuffer(FormatImpossible)
buf.Myprintf("%v", statement)
buf := sqlparser.NewTrackedBuffer(sqlparser.FormatImpossibleQuery).WriteNode(statement)

if buf.HasBindVars() {
return nil
}
return buf.ParsedQuery()
}

// FormatImpossible is a callback function used by TrackedBuffer
// to generate a modified version of the query where all selects
// have impossible where clauses. It overrides a few node types
// and passes the rest down to the default FormatNode.
func FormatImpossible(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) {
switch node := node.(type) {
case *sqlparser.Select:
buf.Myprintf("select %v from %v where 1 != 1", node.SelectExprs, node.From)
case *sqlparser.JoinTableExpr:
if node.Join == sqlparser.LeftJoinStr || node.Join == sqlparser.RightJoinStr {
// ON clause is requried
buf.Myprintf("%v %s %v on 1 != 1", node.LeftExpr, node.Join, node.RightExpr)
} else {
buf.Myprintf("%v %s %v", node.LeftExpr, node.Join, node.RightExpr)
}
default:
node.Format(buf)
}
return buf.ParsedQuery()
}

// GenerateSelectLimitQuery generates a select query with a limit clause.
Expand Down
18 changes: 3 additions & 15 deletions go/vt/vtgate/planbuilder/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -519,17 +519,6 @@ func (rb *route) isLocal(col *sqlparser.ColName) bool {
func (rb *route) generateFieldQuery(sel *sqlparser.Select, jt *jointab) string {
formatter := func(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) {
switch node := node.(type) {
case *sqlparser.Select:
buf.Myprintf("select %v from %v where 1 != 1", node.SelectExprs, node.From)
return
case *sqlparser.JoinTableExpr:
if node.Join == sqlparser.LeftJoinStr || node.Join == sqlparser.RightJoinStr {
// ON clause is requried
buf.Myprintf("%v %s %v on 1 != 1", node.LeftExpr, node.Join, node.RightExpr)
} else {
buf.Myprintf("%v %s %v", node.LeftExpr, node.Join, node.RightExpr)
}
return
case *sqlparser.ColName:
if !rb.isLocal(node) {
_, joinVar := jt.Lookup(node)
Expand All @@ -540,11 +529,10 @@ func (rb *route) generateFieldQuery(sel *sqlparser.Select, jt *jointab) string {
node.Name.Format(buf)
return
}
node.Format(buf)
sqlparser.FormatImpossibleQuery(buf, node)
}
buf := sqlparser.NewTrackedBuffer(formatter)
formatter(buf, sel)
return buf.ParsedQuery().Query

return sqlparser.NewTrackedBuffer(formatter).WriteNode(sel).ParsedQuery().Query
}

// SupplyVar should be unreachable.
Expand Down