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

Rewrite SHOW TABLES #6615

Merged
merged 5 commits into from
Aug 26, 2020
Merged
Show file tree
Hide file tree
Changes from 4 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
12 changes: 12 additions & 0 deletions go/test/endtoend/vtgate/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,18 @@ func TestXXHash(t *testing.T) {
assertMatches(t, conn, "select phone, keyspace_id from t7_xxhash_idx", `[]`)
}

func TestShowTablesWithWhereClause(t *testing.T) {
defer cluster.PanicHandler(t)
ctx := context.Background()
conn, err := mysql.Connect(ctx, &vtParams)
require.Nil(t, err)
defer conn.Close()

assertMatches(t, conn, "show tables from ks where Tables_in_ks='t6'", `[[VARCHAR("t6")]]`)
exec(t, conn, "begin")
assertMatches(t, conn, "show tables from ks where Tables_in_ks='t3'", `[[VARCHAR("t3")]]`)
}

func assertMatches(t *testing.T, conn *mysql.Conn, query, expected string) {
t.Helper()
qr := exec(t, conn, query)
Expand Down
7 changes: 7 additions & 0 deletions go/vt/sqlparser/ast_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,13 @@ func NewColIdent(str string) ColIdent {
}
}

// NewColName makes a new ColName
func NewColName(str string) *ColName {
return &ColName{
Name: NewColIdent(str),
}
}

//NewSelect is used to create a select statement
func NewSelect(comments Comments, exprs SelectExprs, selectOptions []string, from TableExprs, where *Where, groupBy GroupBy, having *Where) *Select {
var cache *bool
Expand Down
22 changes: 14 additions & 8 deletions go/vt/vtgate/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -630,14 +630,7 @@ func (e *Executor) handleShow(ctx context.Context, safeSession *SafeSession, sql
}
sql = sqlparser.String(show)
case sqlparser.KeywordString(sqlparser.TABLES):
if show.ShowTablesOpt != nil && show.ShowTablesOpt.DbName != "" {
if destKeyspace == "" {
// Change "show tables from <keyspace>" to "show tables" directed to that keyspace.
destKeyspace = show.ShowTablesOpt.DbName
}
show.ShowTablesOpt.DbName = ""
}
sql = sqlparser.String(show)
Copy link
Member

Choose a reason for hiding this comment

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

we can revert this change.

destKeyspace, sql = handleShowTables(show, destKeyspace)
case sqlparser.KeywordString(sqlparser.DATABASES), "vitess_keyspaces", "keyspaces":
keyspaces, err := e.resolver.resolver.GetAllKeyspaces(ctx)
if err != nil {
Expand Down Expand Up @@ -827,6 +820,19 @@ func (e *Executor) handleShow(ctx context.Context, safeSession *SafeSession, sql
return e.handleOther(ctx, safeSession, sql, bindVars, dest, destKeyspace, destTabletType, logStats, ignoreMaxMemoryRows)
}

func handleShowTables(show *sqlparser.Show, destKeyspace string) (string, string) {
if show.ShowTablesOpt != nil && show.ShowTablesOpt.DbName != "" {
if destKeyspace == "" {
// Change "show tables from <keyspace>" to "show tables" directed to that keyspace.
destKeyspace = show.ShowTablesOpt.DbName
}
show.ShowTablesOpt.DbName = ""
}

sql := sqlparser.String(show)
return destKeyspace, sql
}

func (e *Executor) showTablets() (*sqltypes.Result, error) {
var rows [][]sqltypes.Value
if UsingLegacyGateway() {
Expand Down
21 changes: 21 additions & 0 deletions go/vt/vttablet/tabletserver/planbuilder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package planbuilder

import (
"strings"

"vitess.io/vitess/go/vt/sqlparser"
"vitess.io/vitess/go/vt/vterrors"
"vitess.io/vitess/go/vt/vttablet/tabletserver/schema"
Expand Down Expand Up @@ -125,6 +127,25 @@ func analyzeInsert(ins *sqlparser.Insert, tables map[string]*schema.Table) (plan
return plan, nil
}

func analyzeShowTables(show *sqlparser.Show, dbName string) {
// rewrite WHERE clause if it exists
// `where Tables_in_Keyspace` => `where Tables_in_DbName`
if show.ShowTablesOpt != nil && show.ShowTablesOpt.Filter != nil {
filter := show.ShowTablesOpt.Filter.Filter
if filter != nil {
sqlparser.Rewrite(filter, func(cursor *sqlparser.Cursor) bool {
switch n := cursor.Node().(type) {
case *sqlparser.ColName:
if n.Qualifier.IsEmpty() && strings.HasPrefix(n.Name.Lowered(), "tables_in_") {
cursor.Replace(sqlparser.NewColName("Tables_in_" + dbName))
}
}
return true
}, nil)
}
}
}

func analyzeSet(set *sqlparser.Set) (plan *Plan) {
return &Plan{
PlanID: PlanSet,
Expand Down
18 changes: 15 additions & 3 deletions go/vt/vttablet/tabletserver/planbuilder/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,12 @@ const (
PlanSavepoint
PlanRelease
PlanSRollback
PlanShowTables
NumPlans
)

// Must exactly match order of plan constants.
var planName = [NumPlans]string{
var planName = []string{
"Select",
"SelectLock",
"Nextval",
Expand All @@ -88,6 +89,7 @@ var planName = [NumPlans]string{
"Savepoint",
"Release",
"RollbackSavepoint",
"ShowTables",
}

func (pt PlanType) String() string {
Expand Down Expand Up @@ -151,7 +153,7 @@ func (plan *Plan) TableName() sqlparser.TableIdent {
}

// Build builds a plan based on the schema.
func Build(statement sqlparser.Statement, tables map[string]*schema.Table, isReservedConn bool) (plan *Plan, err error) {
func Build(statement sqlparser.Statement, tables map[string]*schema.Table, isReservedConn bool, dbName string) (plan *Plan, err error) {
if !isReservedConn {
err = checkForPoolingUnsafeConstructs(statement)
if err != nil {
Expand Down Expand Up @@ -180,7 +182,17 @@ func Build(statement sqlparser.Statement, tables map[string]*schema.Table, isRes
// DDLs and other statements below don't get fully parsed.
// We have to use the original query at the time of execution.
plan = &Plan{PlanID: PlanDDL}
case *sqlparser.Show, *sqlparser.OtherRead, *sqlparser.Explain:
case *sqlparser.Show:
if stmt.Type == sqlparser.KeywordString(sqlparser.TABLES) {
analyzeShowTables(stmt, dbName)
plan = &Plan{
PlanID: PlanShowTables,
FullQuery: GenerateFullQuery(stmt),
}
} else {
plan, err = &Plan{PlanID: PlanOtherRead}, nil
}
case *sqlparser.OtherRead, *sqlparser.Explain:
plan, err = &Plan{PlanID: PlanOtherRead}, nil
case *sqlparser.OtherAdmin:
plan, err = &Plan{PlanID: PlanOtherAdmin}, nil
Expand Down
10 changes: 5 additions & 5 deletions go/vt/vttablet/tabletserver/planbuilder/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func TestPlan(t *testing.T) {
var err error
statement, err := sqlparser.Parse(tcase.input)
if err == nil {
plan, err = Build(statement, testSchema, false)
plan, err = Build(statement, testSchema, false, "dbName")
}
PassthroughDMLs = false

Expand Down Expand Up @@ -109,7 +109,7 @@ func TestPlanPoolUnsafe(t *testing.T) {
statement, err := sqlparser.Parse(tcase.input)
require.NoError(t, err)
// In Pooled Connection, plan building will fail.
plan, err = Build(statement, testSchema, false /* isReservedConn */)
plan, err = Build(statement, testSchema, false /* isReservedConn */, "dbName")
require.Error(t, err)
out := err.Error()
if out != tcase.output {
Expand All @@ -123,7 +123,7 @@ func TestPlanPoolUnsafe(t *testing.T) {
fmt.Printf("\"%s\"\n%s\n\n", tcase.input, out)
}
// In Reserved Connection, plan will be built.
plan, err = Build(statement, testSchema, true /* isReservedConn */)
plan, err = Build(statement, testSchema, true /* isReservedConn */, "dbName")
require.NoError(t, err)
require.NotEmpty(t, plan)
})
Expand All @@ -141,7 +141,7 @@ func TestPlanInReservedConn(t *testing.T) {
var err error
statement, err := sqlparser.Parse(tcase.input)
if err == nil {
plan, err = Build(statement, testSchema, true)
plan, err = Build(statement, testSchema, true, "dbName")
}
PassthroughDMLs = false

Expand Down Expand Up @@ -192,7 +192,7 @@ func TestCustom(t *testing.T) {
if err != nil {
t.Fatalf("Got error: %v, parsing sql: %v", err.Error(), tcase.input)
}
plan, err := Build(statement, schem, false)
plan, err := Build(statement, schem, false, "dbName")
var out string
if err != nil {
out = err.Error()
Expand Down
16 changes: 16 additions & 0 deletions go/vt/vttablet/tabletserver/planbuilder/testdata/exec_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -787,3 +787,19 @@ options:PassthroughDMLs
# syntax error
"syntax error"
"syntax error at position 7 near 'syntax'"

# show tables #1
"show tables like 'key%'"
{
"PlanID": "ShowTables",
"TableName":"",
"FullQuery": "show tables like 'key%'"
}

# show tables #2
"show tables where Tables_in_keyspace='apa'"
{
"PlanID": "ShowTables",
"TableName":"",
"FullQuery": "show tables where Tables_in_dbName = 'apa'"
}
2 changes: 1 addition & 1 deletion go/vt/vttablet/tabletserver/query_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ func (qe *QueryEngine) GetPlan(ctx context.Context, logStats *tabletenv.LogStats
if err != nil {
return nil, err
}
splan, err := planbuilder.Build(statement, qe.tables, isReservedConn)
splan, err := planbuilder.Build(statement, qe.tables, isReservedConn, qe.env.Config().DB.DBName)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions go/vt/vttablet/tabletserver/query_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (qre *QueryExecutor) Execute() (reply *sqltypes.Result, err error) {
}

switch qre.plan.PlanID {
case planbuilder.PlanSelect, planbuilder.PlanSelectImpossible:
case planbuilder.PlanSelect, planbuilder.PlanSelectImpossible, planbuilder.PlanShowTables:
maxrows := qre.getSelectLimit()
qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1)
qr, err := qre.execSelect()
Expand Down Expand Up @@ -203,7 +203,7 @@ func (qre *QueryExecutor) txConnExec(conn *StatefulConnection) (*sqltypes.Result
return qre.execSQL(conn, qre.query, true)
case planbuilder.PlanSavepoint, planbuilder.PlanRelease, planbuilder.PlanSRollback:
return qre.execSQL(conn, qre.query, true)
case planbuilder.PlanSelect, planbuilder.PlanSelectLock, planbuilder.PlanSelectImpossible:
case planbuilder.PlanSelect, planbuilder.PlanSelectLock, planbuilder.PlanSelectImpossible, planbuilder.PlanShowTables:
maxrows := qre.getSelectLimit()
qre.bindVars["#maxLimit"] = sqltypes.Int64BindVariable(maxrows + 1)
qr, err := qre.txFetch(conn, false)
Expand Down