diff --git a/go.mod b/go.mod index 3d05a537a..6441936c2 100644 --- a/go.mod +++ b/go.mod @@ -1,56 +1,57 @@ module gopkg.in/src-d/go-mysql-server.v0 require ( - github.com/BurntSushi/toml v0.3.1 // indirect - github.com/CAFxX/gcnotifier v0.0.0-20170518020117-39b0596a2da3 // indirect - github.com/DataDog/datadog-go v0.0.0-20180822151419-281ae9f2d895 // indirect - github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f // indirect - github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect - github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/CAFxX/gcnotifier v0.0.0-20170518020117-39b0596a2da3 // indirect + github.com/DataDog/datadog-go v0.0.0-20180822151419-281ae9f2d895 // indirect + github.com/StackExchange/wmi v0.0.0-20180725035823-b12b22c5341f // indirect + github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect + github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect github.com/boltdb/bolt v1.3.1 github.com/cespare/xxhash v1.1.0 // indirect - github.com/circonus-labs/circonus-gometrics v2.2.4+incompatible // indirect - github.com/circonus-labs/circonusllhist v0.0.0-20180430145027-5eb751da55c6 // indirect - github.com/go-ole/go-ole v1.2.1 // indirect + github.com/circonus-labs/circonus-gometrics v2.2.4+incompatible // indirect + github.com/circonus-labs/circonusllhist v0.0.0-20180430145027-5eb751da55c6 // indirect + github.com/go-ole/go-ole v1.2.1 // indirect github.com/gogo/protobuf v1.1.1 // indirect - github.com/google/go-cmp v0.2.0 // indirect - github.com/gorilla/context v1.1.1 // indirect - github.com/gorilla/handlers v1.4.0 // indirect - github.com/gorilla/mux v1.6.2 // indirect - github.com/hashicorp/consul v1.2.3 // indirect - github.com/hashicorp/go-cleanhttp v0.5.0 // indirect - github.com/hashicorp/go-immutable-radix v1.0.0 // indirect - github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c // indirect - github.com/hashicorp/go-multierror v1.0.0 // indirect - github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6 // indirect - github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect - github.com/hashicorp/memberlist v0.1.0 // indirect - github.com/hashicorp/serf v0.8.1 // indirect - github.com/hashicorp/yamux v0.0.0-20180917205041-7221087c3d28 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/miekg/dns v1.0.12 // indirect + github.com/google/go-cmp v0.2.0 // indirect + github.com/gorilla/context v1.1.1 // indirect + github.com/gorilla/handlers v1.4.0 // indirect + github.com/gorilla/mux v1.6.2 // indirect + github.com/hashicorp/consul v1.2.3 // indirect + github.com/hashicorp/go-cleanhttp v0.5.0 // indirect + github.com/hashicorp/go-immutable-radix v1.0.0 // indirect + github.com/hashicorp/go-msgpack v0.0.0-20150518234257-fa3f63826f7c // indirect + github.com/hashicorp/go-multierror v1.0.0 // indirect + github.com/hashicorp/go-retryablehttp v0.0.0-20180718195005-e651d75abec6 // indirect + github.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86 // indirect + github.com/hashicorp/memberlist v0.1.0 // indirect + github.com/hashicorp/serf v0.8.1 // indirect + github.com/hashicorp/yamux v0.0.0-20180917205041-7221087c3d28 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/miekg/dns v1.0.12 // indirect github.com/mitchellh/hashstructure v1.0.0 + github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 github.com/opentracing/opentracing-go v1.0.2 - github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect - github.com/pelletier/go-toml v1.2.0 // indirect + github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c // indirect + github.com/pelletier/go-toml v1.2.0 // indirect github.com/pilosa/pilosa v1.1.0 github.com/pkg/errors v0.8.0 // indirect - github.com/prometheus/client_golang v0.8.0 // indirect - github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect - github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect - github.com/prometheus/procfs v0.0.0-20180920065004-418d78d0b9a7 // indirect + github.com/prometheus/client_golang v0.8.0 // indirect + github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect + github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e // indirect + github.com/prometheus/procfs v0.0.0-20180920065004-418d78d0b9a7 // indirect github.com/satori/go.uuid v1.2.0 // indirect - github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect - github.com/shirou/gopsutil v2.17.12+incompatible // indirect - github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect + github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect + github.com/shirou/gopsutil v2.17.12+incompatible // indirect + github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect github.com/sirupsen/logrus v1.1.0 github.com/spf13/cast v1.2.0 github.com/stretchr/testify v1.2.2 - github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 // indirect - golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3 // indirect + github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 // indirect + golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3 // indirect google.golang.org/grpc v1.15.0 // indirect gopkg.in/src-d/go-errors.v1 v1.0.0 gopkg.in/src-d/go-vitess.v1 v1.1.0 - gopkg.in/vmihailenco/msgpack.v2 v2.9.1 // indirect + gopkg.in/vmihailenco/msgpack.v2 v2.9.1 // indirect gopkg.in/yaml.v2 v2.2.1 ) diff --git a/sql/analyzer/assign_catalog.go b/sql/analyzer/assign_catalog.go index 1f70acf87..45d77def2 100644 --- a/sql/analyzer/assign_catalog.go +++ b/sql/analyzer/assign_catalog.go @@ -34,6 +34,11 @@ func assignCatalog(ctx *sql.Context, a *Analyzer, n sql.Node) (sql.Node, error) nc := *node nc.Catalog = a.Catalog return &nc, nil + case *plan.ShowCreateTable: + nc := *node + nc.Catalog = a.Catalog + nc.CurrentDatabase = a.Catalog.CurrentDatabase() + return &nc, nil case *plan.ShowProcessList: nc := *node nc.Database = a.Catalog.CurrentDatabase() diff --git a/sql/parse/parse.go b/sql/parse/parse.go index d18e7f2c2..b592faebf 100644 --- a/sql/parse/parse.go +++ b/sql/parse/parse.go @@ -9,13 +9,12 @@ import ( "strconv" "strings" - "gopkg.in/src-d/go-mysql-server.v0/sql/expression/function" - - opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go" "github.com/sirupsen/logrus" "gopkg.in/src-d/go-errors.v1" "gopkg.in/src-d/go-mysql-server.v0/sql" "gopkg.in/src-d/go-mysql-server.v0/sql/expression" + "gopkg.in/src-d/go-mysql-server.v0/sql/expression/function" "gopkg.in/src-d/go-mysql-server.v0/sql/plan" "gopkg.in/src-d/go-vitess.v1/vt/sqlparser" ) @@ -39,6 +38,7 @@ var ( createIndexRegex = regexp.MustCompile(`^create\s+index\s+`) dropIndexRegex = regexp.MustCompile(`^drop\s+index\s+`) showIndexRegex = regexp.MustCompile(`^show\s+(index|indexes|keys)\s+(from|in)\s+\S+\s*`) + showCreateRegex = regexp.MustCompile(`^show create\s+\S+\s*`) showVariablesRegex = regexp.MustCompile(`^show\s+(.*)?variables\s*`) describeRegex = regexp.MustCompile(`^(describe|desc|explain)\s+(.*)\s+`) fullProcessListRegex = regexp.MustCompile(`^show\s+(full\s+)?processlist$`) @@ -70,6 +70,8 @@ func Parse(ctx *sql.Context, query string) (sql.Node, error) { return parseDropIndex(s) case showIndexRegex.MatchString(lowerQuery): return parseShowIndex(s) + case showCreateRegex.MatchString(lowerQuery): + return parseShowCreate(s) case showVariablesRegex.MatchString(lowerQuery): return parseShowVariables(ctx, s) case describeRegex.MatchString(lowerQuery): diff --git a/sql/parse/show_create.go b/sql/parse/show_create.go new file mode 100644 index 000000000..cbc9a0d43 --- /dev/null +++ b/sql/parse/show_create.go @@ -0,0 +1,48 @@ +package parse + +import ( + "bufio" + "strings" +) + +import ( + "gopkg.in/src-d/go-errors.v1" + "gopkg.in/src-d/go-mysql-server.v0/sql" + "gopkg.in/src-d/go-mysql-server.v0/sql/plan" +) + +var errUnsupportedShowCreateQuery = errors.NewKind("Unsupported query: SHOW CREATE %s") + +func parseShowCreate(s string) (sql.Node, error) { + r := bufio.NewReader(strings.NewReader(s)) + + var thingToShow string + var name string + steps := []parseFunc{ + expect("show"), + skipSpaces, + expect("create"), + skipSpaces, + readIdent(&thingToShow), + skipSpaces, + readIdent(&name), + skipSpaces, + checkEOF, + } + + for _, step := range steps { + if err := step(r); err != nil { + return nil, err + } + } + + switch strings.ToLower(thingToShow) { + case "table": + return plan.NewShowCreateTable( + sql.UnresolvedDatabase("").Name(), + nil, + name), nil + default: + return nil, errUnsupportedShowCreateQuery.New(name) + } +} diff --git a/sql/parse/show_create_test.go b/sql/parse/show_create_test.go new file mode 100644 index 000000000..032d655c6 --- /dev/null +++ b/sql/parse/show_create_test.go @@ -0,0 +1,56 @@ +package parse + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/src-d/go-errors.v1" + "gopkg.in/src-d/go-mysql-server.v0/sql" + "gopkg.in/src-d/go-mysql-server.v0/sql/plan" +) + +func TestParseShowCreateTableQuery(t *testing.T) { + testCases := []struct { + query string + result sql.Node + err *errors.Kind + }{ + { + "SHOW CREATE", + nil, + errUnsupportedShowCreateQuery, + }, + { + "SHOW CREATE ANYTHING", + nil, + errUnsupportedShowCreateQuery, + }, + { + "SHOW CREATE ASDF foo", + nil, + errUnsupportedShowCreateQuery, + }, + { + "SHOW CREATE TABLE mytable", + plan.NewShowCreateTable(sql.UnresolvedDatabase("").Name(), + nil, + "mytable"), + nil, + }, + } + + for _, tt := range testCases { + t.Run(tt.query, func(t *testing.T) { + require := require.New(t) + + result, err := parseShowCreate(tt.query) + if tt.err != nil { + require.Error(err) + require.True(tt.err.Is(err)) + } else { + require.NoError(err) + require.Equal(tt.result, result) + } + }) + } +} diff --git a/sql/plan/show_create_table.go b/sql/plan/show_create_table.go new file mode 100644 index 000000000..a8a461e21 --- /dev/null +++ b/sql/plan/show_create_table.go @@ -0,0 +1,143 @@ +package plan + +import ( + "fmt" + "io" + "strings" + + "gopkg.in/src-d/go-errors.v1" + "gopkg.in/src-d/go-mysql-server.v0/sql" +) + +var ErrTableNotFound = errors.NewKind("Table `%s` not found") + +// ShowCreateTable is a node that shows the CREATE TABLE statement for a table. +type ShowCreateTable struct { + Catalog *sql.Catalog + CurrentDatabase string + Table string +} + +// Schema implements the Node interface. +func (n *ShowCreateTable) Schema() sql.Schema { + return sql.Schema{ + &sql.Column{Name: "Table", Type: sql.Text, Nullable: false}, + &sql.Column{Name: "Create Table", Type: sql.Text, Nullable: false}, + } +} + +// TransformExpressionsUp implements the Transformable interface. +func (n *ShowCreateTable) TransformExpressionsUp(f sql.TransformExprFunc) (sql.Node, error) { + return n, nil +} + +// TransformUp implements the Transformable interface. +func (n *ShowCreateTable) TransformUp(f sql.TransformNodeFunc) (sql.Node, error) { + return f(NewShowCreateTable(n.CurrentDatabase, n.Catalog, n.Table)) +} + +// RowIter implements the Node interface +func (n *ShowCreateTable) RowIter(*sql.Context) (sql.RowIter, error) { + db, err := n.Catalog.Database(n.CurrentDatabase) + + if err != nil { + return nil, sql.ErrDatabaseNotFound.New(n.CurrentDatabase) + } + + return &showCreateTablesIter{ + db: db, + table: n.Table, + }, nil +} + +// String implements the Stringer interface. +func (n *ShowCreateTable) String() string { + return fmt.Sprintf("SHOW CREATE TABLE %s", n.Table) +} + +type createTableStmt struct { + colName string + colType sql.Type +} + +type showCreateTablesIter struct { + db sql.Database + table string + + createStmt *createTableStmt + didIteration bool +} + +func (i *showCreateTablesIter) Next() (sql.Row, error) { + if i.didIteration { + return nil, io.EOF + } + + i.didIteration = true + + table, found := i.db.Tables()[i.table] + + if !found { + return nil, ErrTableNotFound.New(i.table) + } + + composedCreateTableStatement := produceCreateStatement(table) + + return sql.NewRow( + i.table, // "Table" string + composedCreateTableStatement, // "Create Table" string + ), nil +} + +func produceCreateStatement(table sql.Table) string { + schema := table.Schema() + colCreateStatements := make([]string, len(schema), len(schema)) + + // Statement creation parts for each column + for indx, col := range schema { + createStmtPart := fmt.Sprintf("`%s` %s", col.Name, col.Type.Type()) + + if !col.Nullable { + createStmtPart = fmt.Sprintf("%s NOT NULL", createStmtPart) + } + + switch def := col.Default.(type) { + case string: + if def != "" { + createStmtPart = fmt.Sprintf("%s DEFAULT %s", createStmtPart, def) + } + default: + if def != nil { + createStmtPart = fmt.Sprintf("%s DEFAULT %v", createStmtPart, col.Default) + } + } + + colCreateStatements[indx] = createStmtPart + } + + prettyColCreateStmts := fmt.Sprintf("%s", strings.Join(colCreateStatements, ",\n")) + composedCreateTableStatement := + fmt.Sprintf("CREATE TABLE `%s` (%s) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", table.Name(), prettyColCreateStmts) + + return composedCreateTableStatement +} + +func (i *showCreateTablesIter) Close() error { + return nil +} + +// NewShowCreateTable creates a new ShowCreateTable node. +func NewShowCreateTable(db string, ctl *sql.Catalog, table string) sql.Node { + return &ShowCreateTable{ + CurrentDatabase: db, + Table: table, + Catalog: ctl} +} + +// Resolved implements the Resolvable interface. +func (n *ShowCreateTable) Resolved() bool { + return true +} + +// Children implements the Node interface. +func (n *ShowCreateTable) Children() []sql.Node { return nil } diff --git a/sql/plan/show_create_table_test.go b/sql/plan/show_create_table_test.go new file mode 100644 index 000000000..9f64cccb1 --- /dev/null +++ b/sql/plan/show_create_table_test.go @@ -0,0 +1,46 @@ +package plan + +import ( + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/src-d/go-mysql-server.v0/mem" + "gopkg.in/src-d/go-mysql-server.v0/sql" +) + +func TestShowCreateTable(t *testing.T) { + var require = require.New(t) + + db := mem.NewDatabase("testdb") + + table := mem.NewTable( + "test-table", + sql.Schema{ + &sql.Column{Name: "baz", Type: sql.Text, Default: "", Nullable: false}, + &sql.Column{Name: "zab", Type: sql.Int32, Default: int32(0), Nullable: true}, + &sql.Column{Name: "bza", Type: sql.Int64, Default: int64(0), Nullable: true}, + }) + + db.AddTable(table.Name(), table) + + cat := sql.NewCatalog() + cat.AddDatabase(db) + + showCreateTable := NewShowCreateTable(db.Name(), cat, table.Name()) + + ctx := sql.NewEmptyContext() + rowIter, _ := showCreateTable.RowIter(ctx) + + row, err := rowIter.Next() + + require.Nil(err) + + expected := sql.NewRow( + table.Name(), + "CREATE TABLE `test-table` (`baz` TEXT NOT NULL,\n"+ + "`zab` INT32 DEFAULT 0,\n"+ + "`bza` INT64 DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", + ) + + require.Equal(expected, row) +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy