Repository: src-d/go-mysql-server (go)

Issue:
USE command to change database
Implement use command to change the database in use.

More info: https://github.com/src-d/gitbase/issues/361#issuecomment-427285397

Pull request:
sql: implement USE <table> statement
Closes #411 

- Implement USE
- Change implementation of UnresolvedDatabase
- Catalog is not thread-safe
- CurrentDatabase is now on the Catalog

Patch:
diff --git a/engine.go b/engine.go
index c73ba3e..88cc007 100644
--- a/engine.go
+++ b/engine.go
@@ -90,11 +90,10 @@ func (e *Engine) Query(
 
 // AddDatabase adds the given database to the catalog.
 func (e *Engine) AddDatabase(db sql.Database) {
-	e.Catalog.Databases = append(e.Catalog.Databases, db)
-	e.Analyzer.CurrentDatabase = db.Name()
+	e.Catalog.AddDatabase(db)
 }
 
 // Init performs all the initialization requirements for the engine to work.
 func (e *Engine) Init() error {
-	return e.Catalog.LoadIndexes(e.Catalog.Databases)
+	return e.Catalog.LoadIndexes(e.Catalog.AllDatabases())
 }
diff --git a/mem/table.go b/mem/table.go
index 116c103..a77ff1b 100644
--- a/mem/table.go
+++ b/mem/table.go
@@ -418,14 +418,17 @@ func (t *Table) IndexKeyValues(
 	}, nil
 }
 
+// Projection implements the sql.ProjectedTable interface.
 func (t *Table) Projection() []string {
 	return t.projection
 }
 
+// Filters implements the sql.FilteredTable interface.
 func (t *Table) Filters() []sql.Expression {
 	return t.filters
 }
 
+// IndexLookup implements the sql.IndexableTable interface.
 func (t *Table) IndexLookup() sql.IndexLookup {
 	return t.lookup
 }
diff --git a/sql/analyzer/analyzer.go b/sql/analyzer/analyzer.go
index d81b9c2..389a44b 100644
--- a/sql/analyzer/analyzer.go
+++ b/sql/analyzer/analyzer.go
@@ -147,8 +147,6 @@ type Analyzer struct {
 	Batches []*Batch
 	// Catalog of databases and registered functions.
 	Catalog *sql.Catalog
-	// CurrentDatabase in use.
-	CurrentDatabase string
 }
 
 // NewDefault creates a default Analyzer instance with all default Rules and configuration.
diff --git a/sql/analyzer/assign_catalog.go b/sql/analyzer/assign_catalog.go
index c9e7ee8..1f70acf 100644
--- a/sql/analyzer/assign_catalog.go
+++ b/sql/analyzer/assign_catalog.go
@@ -19,12 +19,12 @@ func assignCatalog(ctx *sql.Context, a *Analyzer, n sql.Node) (sql.Node, error)
 		case *plan.CreateIndex:
 			nc := *node
 			nc.Catalog = a.Catalog
-			nc.CurrentDatabase = a.CurrentDatabase
+			nc.CurrentDatabase = a.Catalog.CurrentDatabase()
 			return &nc, nil
 		case *plan.DropIndex:
 			nc := *node
 			nc.Catalog = a.Catalog
-			nc.CurrentDatabase = a.CurrentDatabase
+			nc.CurrentDatabase = a.Catalog.CurrentDatabase()
 			return &nc, nil
 		case *plan.ShowIndexes:
 			nc := *node
@@ -36,13 +36,17 @@ func assignCatalog(ctx *sql.Context, a *Analyzer, n sql.Node) (sql.Node, error)
 			return &nc, nil
 		case *plan.ShowProcessList:
 			nc := *node
-			nc.Database = a.CurrentDatabase
+			nc.Database = a.Catalog.CurrentDatabase()
 			nc.ProcessList = a.Catalog.ProcessList
 			return &nc, nil
 		case *plan.ShowTableStatus:
 			nc := *node
 			nc.Catalog = a.Catalog
 			return &nc, nil
+		case *plan.Use:
+			nc := *node
+			nc.Catalog = a.Catalog
+			return &nc, nil
 		default:
 			return n, nil
 		}
diff --git a/sql/analyzer/assign_indexes.go b/sql/analyzer/assign_indexes.go
index 4bd5288..b8fb608 100644
--- a/sql/analyzer/assign_indexes.go
+++ b/sql/analyzer/assign_indexes.go
@@ -101,7 +101,7 @@ func getIndexes(e sql.Expression, a *Analyzer) (map[string]*indexLookup, error)
 		// the right branch is evaluable and the indexlookup supports set
 		// operations.
 		if !isEvaluable(c.Left()) && isEvaluable(c.Right()) {
-			idx := a.Catalog.IndexByExpression(a.CurrentDatabase, c.Left())
+			idx := a.Catalog.IndexByExpression(a.Catalog.CurrentDatabase(), c.Left())
 			if idx != nil {
 				var nidx sql.NegateIndex
 				if negate {
@@ -198,7 +198,7 @@ func getIndexes(e sql.Expression, a *Analyzer) (map[string]*indexLookup, error)
 		}
 	case *expression.Between:
 		if !isEvaluable(e.Val) && isEvaluable(e.Upper) && isEvaluable(e.Lower) {
-			idx := a.Catalog.IndexByExpression(a.CurrentDatabase, e.Val)
+			idx := a.Catalog.IndexByExpression(a.Catalog.CurrentDatabase(), e.Val)
 			if idx != nil {
 				// release the index if it was not used
 				defer func() {
@@ -301,7 +301,7 @@ func getComparisonIndex(
 	}
 
 	if !isEvaluable(left) && isEvaluable(right) {
-		idx := a.Catalog.IndexByExpression(a.CurrentDatabase, left)
+		idx := a.Catalog.IndexByExpression(a.Catalog.CurrentDatabase(), left)
 		if idx != nil {
 			value, err := right.Eval(sql.NewEmptyContext(), nil)
 			if err != nil {
@@ -378,7 +378,7 @@ func getNegatedIndexes(a *Analyzer, not *expression.Not) (map[string]*indexLooku
 			return nil, nil
 		}
 
-		idx := a.Catalog.IndexByExpression(a.CurrentDatabase, left)
+		idx := a.Catalog.IndexByExpression(a.Catalog.CurrentDatabase(), left)
 		if idx == nil {
 			return nil, nil
 		}
@@ -492,7 +492,7 @@ func getMultiColumnIndexes(
 				cols[i] = e.col
 			}
 
-			exprList := a.Catalog.ExpressionsWithIndexes(a.CurrentDatabase, cols...)
+			exprList := a.Catalog.ExpressionsWithIndexes(a.Catalog.CurrentDatabase(), cols...)
 
 			var selected []sql.Expression
 			for _, l := range exprList {
@@ -535,7 +535,7 @@ func getMultiColumnIndexForExpressions(
 	exprs []columnExpr,
 	used map[sql.Expression]struct{},
 ) (index sql.Index, lookup sql.IndexLookup, err error) {
-	index = a.Catalog.IndexByExpression(a.CurrentDatabase, selected...)
+	index = a.Catalog.IndexByExpression(a.Catalog.CurrentDatabase(), selected...)
 	if index != nil {
 		var first sql.Expression
 		for _, e := range exprs {
diff --git a/sql/analyzer/resolve_database.go b/sql/analyzer/resolve_database.go
index 3cce8d3..1722084 100644
--- a/sql/analyzer/resolve_database.go
+++ b/sql/analyzer/resolve_database.go
@@ -11,31 +11,44 @@ func resolveDatabase(ctx *sql.Context, a *Analyzer, n sql.Node) (sql.Node, error
 
 	a.Log("resolve database, node of type: %T", n)
 
-	// TODO Database should implement node,
-	// and ShowTables and CreateTable nodes should be binaryNodes
 	switch v := n.(type) {
 	case *plan.ShowIndexes:
-		db, err := a.Catalog.Database(a.CurrentDatabase)
+		db, err := a.Catalog.Database(a.Catalog.CurrentDatabase())
 		if err != nil {
 			return nil, err
 		}
 
-		v.Database = db
+		nc := *v
+		nc.Database = db
+		return &nc, nil
 	case *plan.ShowTables:
-		db, err := a.Catalog.Database(a.CurrentDatabase)
+		db, err := a.Catalog.Database(a.Catalog.CurrentDatabase())
 		if err != nil {
-			return n, err
+			return nil, err
 		}
 
-		v.Database = db
+		nc := *v
+		nc.Database = db
+		return &nc, nil
 	case *plan.CreateTable:
-		db, err := a.Catalog.Database(a.CurrentDatabase)
+		db, err := a.Catalog.Database(a.Catalog.CurrentDatabase())
 		if err != nil {
-			return n, err
+			return nil, err
 		}
 
-		v.Database = db
-	}
+		nc := *v
+		nc.Database = db
+		return &nc, nil
+	case *plan.Use:
+		db, err := a.Catalog.Database(v.Database.Name())
+		if err != nil {
+			return nil, err
+		}
 
-	return n, nil
+		nc := *v
+		nc.Database = db
+		return &nc, nil
+	default:
+		return n, nil
+	}
 }
diff --git a/sql/analyzer/resolve_tables.go b/sql/analyzer/resolve_tables.go
index 987e22f..b9b01be 100644
--- a/sql/analyzer/resolve_tables.go
+++ b/sql/analyzer/resolve_tables.go
@@ -33,7 +33,7 @@ func resolveTables(ctx *sql.Context, a *Analyzer, n sql.Node) (sql.Node, error)
 		}
 
 		name := t.Name()
-		rt, err := a.Catalog.Table(a.CurrentDatabase, name)
+		rt, err := a.Catalog.Table(a.Catalog.CurrentDatabase(), name)
 		if err != nil {
 			if sql.ErrTableNotFound.Is(err) && name == dualTableName {
 				rt = dualTable
diff --git a/sql/catalog.go b/sql/catalog.go
index 649b206..909e212 100644
--- a/sql/catalog.go
+++ b/sql/catalog.go
@@ -2,6 +2,7 @@ package sql
 
 import (
 	"strings"
+	"sync"
 
 	"gopkg.in/src-d/go-errors.v1"
 )
@@ -11,22 +12,73 @@ var ErrDatabaseNotFound = errors.NewKind("database not found: %s")
 
 // Catalog holds databases, tables and functions.
 type Catalog struct {
-	Databases
 	FunctionRegistry
 	*IndexRegistry
 	*ProcessList
+
+	mu              sync.RWMutex
+	currentDatabase string
+	dbs             Databases
 }
 
 // NewCatalog returns a new empty Catalog.
 func NewCatalog() *Catalog {
 	return &Catalog{
-		Databases:        Databases{},
 		FunctionRegistry: NewFunctionRegistry(),
 		IndexRegistry:    NewIndexRegistry(),
 		ProcessList:      NewProcessList(),
 	}
 }
 
+// CurrentDatabase returns the current database.
+func (c *Catalog) CurrentDatabase() string {
+	c.mu.RLock()
+	defer c.mu.RUnlock()
+	return c.currentDatabase
+}
+
+// SetCurrentDatabase changes the current database.
+func (c *Catalog) SetCurrentDatabase(db string) {
+	c.mu.Lock()
+	c.currentDatabase = db
+	c.mu.Unlock()
+}
+
+// AllDatabases returns all databases in the catalog.
+func (c *Catalog) AllDatabases() Databases {
+	c.mu.RLock()
+	defer c.mu.RUnlock()
+
+	var result = make(Databases, len(c.dbs))
+	copy(result, c.dbs)
+	return result
+}
+
+// AddDatabase adds a new database to the catalog.
+func (c *Catalog) AddDatabase(db Database) {
+	c.mu.Lock()
+	if c.currentDatabase == "" {
+		c.currentDatabase = db.Name()
+	}
+
+	c.dbs.Add(db)
+	c.mu.Unlock()
+}
+
+// Database returns the database with the given name.
+func (c *Catalog) Database(db string) (Database, error) {
+	c.mu.RLock()
+	defer c.mu.RUnlock()
+	return c.dbs.Database(db)
+}
+
+// Table returns the table in the given database with the given name.
+func (c *Catalog) Table(db, table string) (Table, error) {
+	c.mu.RLock()
+	defer c.mu.RUnlock()
+	return c.dbs.Table(db, table)
+}
+
 // Databases is a collection of Database.
 type Databases []Database
 
@@ -41,8 +93,8 @@ func (d Databases) Database(name string) (Database, error) {
 	return nil, ErrDatabaseNotFound.New(name)
 }
 
-// AddDatabase adds a new database.
-func (d *Databases) AddDatabase(db Database) {
+// Add adds a new database.
+func (d *Databases) Add(db Database) {
 	*d = append(*d, db)
 }
 
diff --git a/sql/parse/indexes.go b/sql/parse/indexes.go
index 0f45470..ae1ef95 100644
--- a/sql/parse/indexes.go
+++ b/sql/parse/indexes.go
@@ -35,7 +35,7 @@ func parseShowIndex(s string) (sql.Node, error) {
 	}
 
 	return plan.NewShowIndexes(
-		&sql.UnresolvedDatabase{},
+		sql.UnresolvedDatabase(""),
 		table,
 		nil,
 	), nil
diff --git a/sql/parse/parse.go b/sql/parse/parse.go
index f10b62c..01bd3b4 100644
--- a/sql/parse/parse.go
+++ b/sql/parse/parse.go
@@ -106,9 +106,16 @@ func convert(ctx *sql.Context, stmt sqlparser.Statement, query string) (sql.Node
 		return convertDDL(n)
 	case *sqlparser.Set:
 		return convertSet(ctx, n)
+	case *sqlparser.Use:
+		return convertUse(n)
 	}
 }
 
+func convertUse(n *sqlparser.Use) (sql.Node, error) {
+	name := n.DBName.String()
+	return plan.NewUse(sql.UnresolvedDatabase(name)), nil
+}
+
 func convertSet(ctx *sql.Context, n *sqlparser.Set) (sql.Node, error) {
 	if n.Scope == sqlparser.GlobalStr {
 		return nil, ErrUnsupportedFeature.New("SET global variables")
@@ -165,7 +172,7 @@ func convertSet(ctx *sql.Context, n *sqlparser.Set) (sql.Node, error) {
 func convertShow(s *sqlparser.Show, query string) (sql.Node, error) {
 	switch s.Type {
 	case sqlparser.KeywordString(sqlparser.TABLES):
-		return plan.NewShowTables(&sql.UnresolvedDatabase{}), nil
+		return plan.NewShowTables(sql.UnresolvedDatabase("")), nil
 	case sqlparser.KeywordString(sqlparser.DATABASES):
 		return plan.NewShowDatabases(), nil
 	case sqlparser.KeywordString(sqlparser.FIELDS), sqlparser.KeywordString(sqlparser.COLUMNS):
@@ -273,7 +280,7 @@ func convertCreateTable(c *sqlparser.DDL) (sql.Node, error) {
 	}
 
 	return plan.NewCreateTable(
-		&sql.UnresolvedDatabase{},
+		sql.UnresolvedDatabase(""),
 		c.NewName.Name.String(),
 		schema,
 	), nil
diff --git a/sql/plan/ddl.go b/sql/plan/ddl.go
index 8eda849..48c3c87 100644
--- a/sql/plan/ddl.go
+++ b/sql/plan/ddl.go
@@ -30,7 +30,7 @@ func NewCreateTable(db sql.Database, name string, schema sql.Schema) *CreateTabl
 
 // Resolved implements the Resolvable interface.
 func (c *CreateTable) Resolved() bool {
-	_, ok := c.Database.(*sql.UnresolvedDatabase)
+	_, ok := c.Database.(sql.UnresolvedDatabase)
 	return !ok
 }
 
diff --git a/sql/plan/show_indexes.go b/sql/plan/show_indexes.go
index ee50a1d..0c484c4 100644
--- a/sql/plan/show_indexes.go
+++ b/sql/plan/show_indexes.go
@@ -21,7 +21,7 @@ func NewShowIndexes(db sql.Database, table string, registry *sql.IndexRegistry)
 
 // Resolved implements the Resolvable interface.
 func (n *ShowIndexes) Resolved() bool {
-	_, ok := n.Database.(*sql.UnresolvedDatabase)
+	_, ok := n.Database.(sql.UnresolvedDatabase)
 	return !ok
 }
 
diff --git a/sql/plan/show_tables.go b/sql/plan/show_tables.go
index 0abbe38..3ac26dc 100644
--- a/sql/plan/show_tables.go
+++ b/sql/plan/show_tables.go
@@ -21,7 +21,7 @@ func NewShowTables(database sql.Database) *ShowTables {
 
 // Resolved implements the Resolvable interface.
 func (p *ShowTables) Resolved() bool {
-	_, ok := p.Database.(*sql.UnresolvedDatabase)
+	_, ok := p.Database.(sql.UnresolvedDatabase)
 	return !ok
 }
 
diff --git a/sql/plan/showdatabases.go b/sql/plan/showdatabases.go
index a7a6efe..db9d58e 100644
--- a/sql/plan/showdatabases.go
+++ b/sql/plan/showdatabases.go
@@ -38,8 +38,9 @@ func (*ShowDatabases) Schema() sql.Schema {
 
 // RowIter implements the Node interface.
 func (p *ShowDatabases) RowIter(ctx *sql.Context) (sql.RowIter, error) {
-	var rows = make([]sql.Row, len(p.Catalog.Databases))
-	for i, db := range p.Catalog.Databases {
+	dbs := p.Catalog.AllDatabases()
+	var rows = make([]sql.Row, len(dbs))
+	for i, db := range dbs {
 		rows[i] = sql.Row{db.Name()}
 	}
 
diff --git a/sql/plan/showtablestatus.go b/sql/plan/showtablestatus.go
index 8617eb0..ec40e21 100644
--- a/sql/plan/showtablestatus.go
+++ b/sql/plan/showtablestatus.go
@@ -53,7 +53,7 @@ func (s *ShowTableStatus) Schema() sql.Schema { return showTableStatusSchema }
 func (s *ShowTableStatus) RowIter(ctx *sql.Context) (sql.RowIter, error) {
 	var rows []sql.Row
 	var tables []string
-	for _, db := range s.Catalog.Databases {
+	for _, db := range s.Catalog.AllDatabases() {
 		if len(s.Databases) > 0 && !stringContains(s.Databases, db.Name()) {
 			continue
 		}
diff --git a/sql/plan/use.go b/sql/plan/use.go
new file mode 100644
index 0000000..74e5ac2
--- /dev/null
+++ b/sql/plan/use.go
@@ -0,0 +1,53 @@
+package plan
+
+import (
+	"fmt"
+
+	"gopkg.in/src-d/go-mysql-server.v0/sql"
+)
+
+// Use changes the current database.
+type Use struct {
+	Database sql.Database
+	Catalog  *sql.Catalog
+}
+
+// NewUse creates a new Use node.
+func NewUse(db sql.Database) *Use {
+	return &Use{Database: db}
+}
+
+var _ sql.Node = (*Use)(nil)
+
+// Children implements the sql.Node interface.
+func (Use) Children() []sql.Node { return nil }
+
+// Resolved implements the sql.Node interface.
+func (u *Use) Resolved() bool {
+	_, ok := u.Database.(sql.UnresolvedDatabase)
+	return !ok
+}
+
+// Schema implements the sql.Node interface.
+func (Use) Schema() sql.Schema { return nil }
+
+// RowIter implements the sql.Node interface.
+func (u *Use) RowIter(ctx *sql.Context) (sql.RowIter, error) {
+	u.Catalog.SetCurrentDatabase(u.Database.Name())
+	return sql.RowsToRowIter(), nil
+}
+
+// TransformUp implements the sql.Node interface.
+func (u *Use) TransformUp(f sql.TransformNodeFunc) (sql.Node, error) {
+	return f(u)
+}
+
+// TransformExpressionsUp implements the sql.Node interface.
+func (u *Use) TransformExpressionsUp(f sql.TransformExprFunc) (sql.Node, error) {
+	return u, nil
+}
+
+// String implements the sql.Node interface.
+func (u *Use) String() string {
+	return fmt.Sprintf("USE(%s)", u.Database.Name())
+}
diff --git a/sql/unresolved_database.go b/sql/unresolved_database.go
index 62556be..86c59b6 100644
--- a/sql/unresolved_database.go
+++ b/sql/unresolved_database.go
@@ -1,14 +1,14 @@
 package sql
 
 // UnresolvedDatabase is a database which has not been resolved yet.
-type UnresolvedDatabase struct{}
+type UnresolvedDatabase string
 
-// Name returns the database name, which is always "unresolved_database".
-func (d *UnresolvedDatabase) Name() string {
-	return "unresolved_database"
+// Name returns the database name.
+func (d UnresolvedDatabase) Name() string {
+	return string(d)
 }
 
 // Tables returns the tables in the database.
-func (d *UnresolvedDatabase) Tables() map[string]Table {
+func (UnresolvedDatabase) Tables() map[string]Table {
 	return make(map[string]Table)
 }


Test patch:
diff --git a/engine_test.go b/engine_test.go
index 233c989..3ae93f8 100644
--- a/engine_test.go
+++ b/engine_test.go
@@ -339,7 +339,7 @@ var queries = []struct {
 	},
 	{
 		`SHOW DATABASES`,
-		[]sql.Row{{"mydb"}},
+		[]sql.Row{{"mydb"}, {"foo"}},
 	},
 	{
 		`SELECT s FROM mytable WHERE s LIKE '%d row'`,
@@ -856,6 +856,7 @@ func newEngineWithParallelism(t *testing.T, parallelism int) *sqle.Engine {
 
 	catalog := sql.NewCatalog()
 	catalog.AddDatabase(db)
+	catalog.AddDatabase(mem.NewDatabase("foo"))
 
 	var a *analyzer.Analyzer
 	if parallelism > 1 {
@@ -864,8 +865,6 @@ func newEngineWithParallelism(t *testing.T, parallelism int) *sqle.Engine {
 		a = analyzer.NewDefault(catalog)
 	}
 
-	a.CurrentDatabase = "mydb"
-
 	return sqle.New(catalog, a, new(sqle.Config))
 }
 
@@ -1151,7 +1150,6 @@ func TestReadOnly(t *testing.T) {
 	catalog.AddDatabase(db)
 
 	a := analyzer.NewBuilder(catalog).ReadOnly().Build()
-	a.CurrentDatabase = "mydb"
 	e := sqle.New(catalog, a, nil)
 
 	_, _, err := e.Query(newCtx(), `SELECT i FROM mytable`)
@@ -1224,6 +1222,23 @@ func TestNestedAliases(t *testing.T) {
 	require.True(analyzer.ErrMisusedAlias.Is(err))
 }
 
+func TestUse(t *testing.T) {
+	require := require.New(t)
+	e := newEngine(t)
+
+	_, _, err := e.Query(newCtx(), "USE bar")
+	require.Error(err)
+
+	_, iter, err := e.Query(newCtx(), "USE foo")
+	require.NoError(err)
+
+	rows, err := sql.RowIterToRows(iter)
+	require.NoError(err)
+	require.Len(rows, 0)
+
+	require.Equal("foo", e.Catalog.CurrentDatabase())
+}
+
 func insertRows(t *testing.T, table sql.Inserter, rows ...sql.Row) {
 	t.Helper()
 
diff --git a/mem/table_test.go b/mem/table_test.go
index c2f3936..412dafd 100644
--- a/mem/table_test.go
+++ b/mem/table_test.go
@@ -21,7 +21,7 @@ func TestTablePartitionsCount(t *testing.T) {
 func TestTableName(t *testing.T) {
 	require := require.New(t)
 	s := sql.Schema{
-		{"col1", sql.Text, nil, true, ""},
+		{Name: "col1", Type: sql.Text, Nullable: true},
 	}
 
 	table := NewTable("test", s)
@@ -36,8 +36,8 @@ const expectedString = `Table(foo)
 func TestTableString(t *testing.T) {
 	require := require.New(t)
 	table := NewTable("foo", sql.Schema{
-		{"col1", sql.Text, nil, true, ""},
-		{"col2", sql.Int64, nil, false, ""},
+		{Name: "col1", Type: sql.Text, Nullable: true},
+		{Name: "col2", Type: sql.Int64, Nullable: false},
 	})
 	require.Equal(expectedString, table.String())
 }
diff --git a/sql/analyzer/analyzer_test.go b/sql/analyzer/analyzer_test.go
index 591f459..673012c 100644
--- a/sql/analyzer/analyzer_test.go
+++ b/sql/analyzer/analyzer_test.go
@@ -30,7 +30,6 @@ func TestAnalyzer_Analyze(t *testing.T) {
 	catalog := sql.NewCatalog()
 	catalog.AddDatabase(db)
 	a := withoutProcessTracking(NewDefault(catalog))
-	a.CurrentDatabase = "mydb"
 
 	var notAnalyzed sql.Node = plan.NewUnresolvedTable("mytable")
 	analyzed, err := a.Analyze(sql.NewEmptyContext(), notAnalyzed)
@@ -216,8 +215,6 @@ func TestMaxIterations(t *testing.T) {
 			return n, nil
 		}).Build())
 
-	a.CurrentDatabase = "mydb"
-
 	notAnalyzed := plan.NewUnresolvedTable(tName)
 	analyzed, err := a.Analyze(sql.NewEmptyContext(), notAnalyzed)
 	require.NoError(err)
@@ -301,7 +298,6 @@ func TestMixInnerAndNaturalJoins(t *testing.T) {
 	catalog := sql.NewCatalog()
 	catalog.AddDatabase(db)
 	a := withoutProcessTracking(NewDefault(catalog))
-	a.CurrentDatabase = "mydb"
 
 	node := plan.NewProject(
 		[]sql.Expression{
diff --git a/sql/analyzer/assign_catalog_test.go b/sql/analyzer/assign_catalog_test.go
index 913eabb..245aeae 100644
--- a/sql/analyzer/assign_catalog_test.go
+++ b/sql/analyzer/assign_catalog_test.go
@@ -18,7 +18,6 @@ func TestAssignCatalog(t *testing.T) {
 	c.AddDatabase(db)
 
 	a := NewDefault(c)
-	a.CurrentDatabase = "foo"
 	a.Catalog.IndexRegistry = sql.NewIndexRegistry()
 
 	tbl := mem.NewTable("foo", nil)
diff --git a/sql/analyzer/pushdown_test.go b/sql/analyzer/pushdown_test.go
index 87d8aec..ba2ffa8 100644
--- a/sql/analyzer/pushdown_test.go
+++ b/sql/analyzer/pushdown_test.go
@@ -31,9 +31,8 @@ func TestPushdownProjectionAndFilters(t *testing.T) {
 	db.AddTable("mytable2", table2)
 
 	catalog := sql.NewCatalog()
-	catalog.Databases = []sql.Database{db}
+	catalog.AddDatabase(db)
 	a := NewDefault(catalog)
-	a.CurrentDatabase = "mydb"
 
 	node := plan.NewProject(
 		[]sql.Expression{
@@ -104,7 +103,7 @@ func TestPushdownIndexable(t *testing.T) {
 	db.AddTable("mytable2", table2)
 
 	catalog := sql.NewCatalog()
-	catalog.Databases = []sql.Database{db}
+	catalog.AddDatabase(db)
 
 	idx1 := &dummyIndex{
 		"mytable",
@@ -141,7 +140,6 @@ func TestPushdownIndexable(t *testing.T) {
 	<-ready
 
 	a := withoutProcessTracking(NewDefault(catalog))
-	a.CurrentDatabase = ""
 
 	node := plan.NewProject(
 		[]sql.Expression{
diff --git a/sql/analyzer/resolve_subqueries_test.go b/sql/analyzer/resolve_subqueries_test.go
index 01ba2f2..6e336e9 100644
--- a/sql/analyzer/resolve_subqueries_test.go
+++ b/sql/analyzer/resolve_subqueries_test.go
@@ -27,7 +27,6 @@ func TestResolveSubqueries(t *testing.T) {
 	catalog := sql.NewCatalog()
 	catalog.AddDatabase(db)
 	a := withoutProcessTracking(NewDefault(catalog))
-	a.CurrentDatabase = "mydb"
 
 	// SELECT * FROM
 	// 	(SELECT a FROM foo) t1,
diff --git a/sql/analyzer/resolve_tables_test.go b/sql/analyzer/resolve_tables_test.go
index cea96d0..83b0a93 100644
--- a/sql/analyzer/resolve_tables_test.go
+++ b/sql/analyzer/resolve_tables_test.go
@@ -19,11 +19,11 @@ func TestResolveTables(t *testing.T) {
 	db := mem.NewDatabase("mydb")
 	db.AddTable("mytable", table)
 
-	catalog := &sql.Catalog{Databases: []sql.Database{db}}
+	catalog := sql.NewCatalog()
+	catalog.AddDatabase(db)
 
 	a := NewBuilder(catalog).AddPostAnalyzeRule(f.Name, f.Apply).Build()
 
-	a.CurrentDatabase = "mydb"
 	var notAnalyzed sql.Node = plan.NewUnresolvedTable("mytable")
 	analyzed, err := f.Apply(sql.NewEmptyContext(), a, notAnalyzed)
 	require.NoError(err)
@@ -57,13 +57,11 @@ func TestResolveTablesNested(t *testing.T) {
 	table := mem.NewTable("mytable", sql.Schema{{Name: "i", Type: sql.Int32}})
 	db := mem.NewDatabase("mydb")
 	db.AddTable("mytable", table)
-
-	catalog := &sql.Catalog{Databases: []sql.Database{db}}
+	catalog := sql.NewCatalog()
+	catalog.AddDatabase(db)
 
 	a := NewBuilder(catalog).AddPostAnalyzeRule(f.Name, f.Apply).Build()
 
-	a.CurrentDatabase = "mydb"
-
 	notAnalyzed := plan.NewProject(
 		[]sql.Expression{expression.NewGetField(0, sql.Int32, "i", true)},
 		plan.NewUnresolvedTable("mytable"),
diff --git a/sql/catalog_test.go b/sql/catalog_test.go
index c70c258..2377c58 100644
--- a/sql/catalog_test.go
+++ b/sql/catalog_test.go
@@ -8,6 +8,36 @@ import (
 	"gopkg.in/src-d/go-mysql-server.v0/sql"
 )
 
+func TestCatalogCurrentDatabase(t *testing.T) {
+	require := require.New(t)
+
+	c := sql.NewCatalog()
+	require.Equal("", c.CurrentDatabase())
+
+	c.AddDatabase(mem.NewDatabase("foo"))
+	require.Equal("foo", c.CurrentDatabase())
+
+	c.SetCurrentDatabase("bar")
+	require.Equal("bar", c.CurrentDatabase())
+}
+
+func TestAllDatabases(t *testing.T) {
+	require := require.New(t)
+
+	var dbs = sql.Databases{
+		mem.NewDatabase("a"),
+		mem.NewDatabase("b"),
+		mem.NewDatabase("c"),
+	}
+
+	c := sql.NewCatalog()
+	for _, db := range dbs {
+		c.AddDatabase(db)
+	}
+
+	require.Equal(dbs, c.AllDatabases())
+}
+
 func TestCatalogDatabase(t *testing.T) {
 	require := require.New(t)
 
@@ -17,7 +47,7 @@ func TestCatalogDatabase(t *testing.T) {
 	require.Nil(db)
 
 	mydb := mem.NewDatabase("foo")
-	c.Databases = append(c.Databases, mydb)
+	c.AddDatabase(mydb)
 
 	db, err = c.Database("foo")
 	require.NoError(err)
@@ -34,7 +64,7 @@ func TestCatalogTable(t *testing.T) {
 	require.Nil(table)
 
 	db := mem.NewDatabase("foo")
-	c.Databases = append(c.Databases, db)
+	c.AddDatabase(db)
 
 	table, err = c.Table("foo", "bar")
 	require.EqualError(err, "table not found: bar")
diff --git a/sql/parse/parse_test.go b/sql/parse/parse_test.go
index 422beb1..fae40ca 100644
--- a/sql/parse/parse_test.go
+++ b/sql/parse/parse_test.go
@@ -12,7 +12,7 @@ import (
 
 var fixtures = map[string]sql.Node{
 	`CREATE TABLE t1(a INTEGER, b TEXT, c DATE, d TIMESTAMP, e VARCHAR(20), f BLOB NOT NULL)`: plan.NewCreateTable(
-		&sql.UnresolvedDatabase{},
+		sql.UnresolvedDatabase(""),
 		"t1",
 		sql.Schema{{
 			Name:     "a",
@@ -231,7 +231,7 @@ var fixtures = map[string]sql.Node{
 		}}),
 		[]string{"col1", "col2"},
 	),
-	`SHOW TABLES`: plan.NewShowTables(&sql.UnresolvedDatabase{}),
+	`SHOW TABLES`: plan.NewShowTables(sql.UnresolvedDatabase("")),
 	`SELECT DISTINCT foo, bar FROM foo;`: plan.NewDistinct(
 		plan.NewProject(
 			[]sql.Expression{
@@ -624,12 +624,12 @@ var fixtures = map[string]sql.Node{
 		[]sql.Expression{},
 		plan.NewUnresolvedTable("foo"),
 	),
-	`SHOW INDEXES FROM foo`: plan.NewShowIndexes(&sql.UnresolvedDatabase{}, "foo", nil),
-	`SHOW INDEX FROM foo`:   plan.NewShowIndexes(&sql.UnresolvedDatabase{}, "foo", nil),
-	`SHOW KEYS FROM foo`:    plan.NewShowIndexes(&sql.UnresolvedDatabase{}, "foo", nil),
-	`SHOW INDEXES IN foo`:   plan.NewShowIndexes(&sql.UnresolvedDatabase{}, "foo", nil),
-	`SHOW INDEX IN foo`:     plan.NewShowIndexes(&sql.UnresolvedDatabase{}, "foo", nil),
-	`SHOW KEYS IN foo`:      plan.NewShowIndexes(&sql.UnresolvedDatabase{}, "foo", nil),
+	`SHOW INDEXES FROM foo`: plan.NewShowIndexes(sql.UnresolvedDatabase(""), "foo", nil),
+	`SHOW INDEX FROM foo`:   plan.NewShowIndexes(sql.UnresolvedDatabase(""), "foo", nil),
+	`SHOW KEYS FROM foo`:    plan.NewShowIndexes(sql.UnresolvedDatabase(""), "foo", nil),
+	`SHOW INDEXES IN foo`:   plan.NewShowIndexes(sql.UnresolvedDatabase(""), "foo", nil),
+	`SHOW INDEX IN foo`:     plan.NewShowIndexes(sql.UnresolvedDatabase(""), "foo", nil),
+	`SHOW KEYS IN foo`:      plan.NewShowIndexes(sql.UnresolvedDatabase(""), "foo", nil),
 	`create index foo on bar using qux (baz)`: plan.NewCreateIndex(
 		"foo",
 		plan.NewUnresolvedTable("bar"),
@@ -778,6 +778,7 @@ var fixtures = map[string]sql.Node{
 		),
 		plan.NewShowTableStatus(),
 	),
+	`USE foo`: plan.NewUse(sql.UnresolvedDatabase("foo")),
 }
 
 func TestParse(t *testing.T) {
diff --git a/sql/plan/create_index_test.go b/sql/plan/create_index_test.go
index 52beca8..00ef852 100644
--- a/sql/plan/create_index_test.go
+++ b/sql/plan/create_index_test.go
@@ -29,7 +29,7 @@ func TestCreateIndexAsync(t *testing.T) {
 	catalog.RegisterIndexDriver(driver)
 	db := mem.NewDatabase("foo")
 	db.AddTable("foo", table)
-	catalog.Databases = append(catalog.Databases, db)
+	catalog.AddDatabase(db)
 
 	exprs := []sql.Expression{
 		expression.NewGetFieldWithTable(2, sql.Int64, "foo", "c", true),
@@ -83,7 +83,7 @@ func TestCreateIndexNotIndexableExprs(t *testing.T) {
 	catalog.RegisterIndexDriver(driver)
 	db := mem.NewDatabase("foo")
 	db.AddTable("foo", table)
-	catalog.Databases = append(catalog.Databases, db)
+	catalog.AddDatabase(db)
 
 	ci := NewCreateIndex(
 		"idx",
@@ -132,7 +132,7 @@ func TestCreateIndexSync(t *testing.T) {
 	catalog.RegisterIndexDriver(driver)
 	db := mem.NewDatabase("foo")
 	db.AddTable("foo", table)
-	catalog.Databases = append(catalog.Databases, db)
+	catalog.AddDatabase(db)
 
 	exprs := []sql.Expression{
 		expression.NewGetFieldWithTable(2, sql.Int64, "foo", "c", true),
@@ -199,7 +199,7 @@ func TestCreateIndexWithIter(t *testing.T) {
 	catalog.RegisterIndexDriver(driver)
 	db := mem.NewDatabase("foo")
 	db.AddTable("foo", foo)
-	catalog.Databases = append(catalog.Databases, db)
+	catalog.AddDatabase(db)
 
 	ci := NewCreateIndex("idx", NewResolvedTable(foo), exprs, "mock", make(map[string]string))
 	ci.Catalog = catalog
diff --git a/sql/plan/drop_index_test.go b/sql/plan/drop_index_test.go
index f5746b5..7c1c810 100644
--- a/sql/plan/drop_index_test.go
+++ b/sql/plan/drop_index_test.go
@@ -24,7 +24,7 @@ func TestDeleteIndex(t *testing.T) {
 	catalog.RegisterIndexDriver(driver)
 	db := mem.NewDatabase("foo")
 	db.AddTable("foo", table)
-	catalog.Databases = append(catalog.Databases, db)
+	catalog.AddDatabase(db)
 
 	var expressions = []sql.Expression{
 		expression.NewGetFieldWithTable(0, sql.Int64, "foo", "c", true),
@@ -67,7 +67,7 @@ func TestDeleteIndexNotReady(t *testing.T) {
 	catalog.RegisterIndexDriver(driver)
 	db := mem.NewDatabase("foo")
 	db.AddTable("foo", table)
-	catalog.Databases = append(catalog.Databases, db)
+	catalog.AddDatabase(db)
 
 	var expressions = []sql.Expression{
 		expression.NewGetFieldWithTable(0, sql.Int64, "foo", "c", true),
diff --git a/sql/plan/show_indexes_test.go b/sql/plan/show_indexes_test.go
index 3640f57..e5e3dd8 100644
--- a/sql/plan/show_indexes_test.go
+++ b/sql/plan/show_indexes_test.go
@@ -12,7 +12,7 @@ import (
 func TestShowIndexes(t *testing.T) {
 	var require = require.New(t)
 
-	unresolved := NewShowIndexes(&sql.UnresolvedDatabase{}, "table-test", nil)
+	unresolved := NewShowIndexes(sql.UnresolvedDatabase(""), "table-test", nil)
 	require.False(unresolved.Resolved())
 	require.Nil(unresolved.Children())
 
diff --git a/sql/plan/show_tables_test.go b/sql/plan/show_tables_test.go
index 2bee9af..04fb604 100644
--- a/sql/plan/show_tables_test.go
+++ b/sql/plan/show_tables_test.go
@@ -13,7 +13,7 @@ func TestShowTables(t *testing.T) {
 	require := require.New(t)
 	ctx := sql.NewEmptyContext()
 
-	unresolvedShowTables := NewShowTables(&sql.UnresolvedDatabase{})
+	unresolvedShowTables := NewShowTables(sql.UnresolvedDatabase(""))
 
 	require.False(unresolvedShowTables.Resolved())
 	require.Nil(unresolvedShowTables.Children())