Skip to content
This repository was archived by the owner on Jan 28, 2021. It is now read-only.

sql: plan: Add SHOW CREATE TABLE [Fix #406] #435

Merged
merged 15 commits into from
Oct 17, 2018
Merged
Show file tree
Hide file tree
Changes from 2 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
33 changes: 30 additions & 3 deletions sql/parse/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,8 @@ import (
"regexp"
"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"
Expand Down Expand Up @@ -39,6 +37,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*`)
showCreateTableRegex = regexp.MustCompile(`^show create table\s+\S+\s*`)
describeRegex = regexp.MustCompile(`^(describe|desc|explain)\s+(.*)\s+`)
fullProcessListRegex = regexp.MustCompile(`^show\s+(full\s+)?processlist$`)
)
Expand Down Expand Up @@ -69,6 +68,8 @@ func Parse(ctx *sql.Context, query string) (sql.Node, error) {
return parseDropIndex(s)
case showIndexRegex.MatchString(lowerQuery):
return parseShowIndex(s)
case showCreateTableRegex.MatchString(lowerQuery):
return parseShowCreateTable(s)
case describeRegex.MatchString(lowerQuery):
return parseDescribeQuery(ctx, s)
case fullProcessListRegex.MatchString(lowerQuery):
Expand All @@ -83,6 +84,32 @@ func Parse(ctx *sql.Context, query string) (sql.Node, error) {
return convert(ctx, stmt, s)
}

//TODO: Move this to somewhere else perhaps?
func parseShowCreateTable(s string) (sql.Node, error) {
r := bufio.NewReader(strings.NewReader(s))

var table string
steps := []parseFunc{
expect("show create table"),
skipSpaces,
readIdent(&table),
skipSpaces,
checkEOF,
}

for _, step := range steps {
if err := step(r); err != nil {
return nil, err
}
}

return plan.NewShowCreateTable(
&sql.UnresolvedDatabase{},
table,
nil,
), nil
}

func parseDescribeTables(s string) (sql.Node, error) {
t := describeTablesRegex.FindStringSubmatch(s)
if len(t) == 2 && t[1] != "" {
Expand Down
124 changes: 124 additions & 0 deletions sql/plan/show_create_table.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package plan

import (
"fmt"
"strings"
)

import (
"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 {
Database sql.Database
Table string
Registry *sql.IndexRegistry
}

// 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.Database, n.Table, n.Registry))
}

// RowIter implements the Node interface.
func (n *ShowCreateTable) RowIter(*sql.Context) (sql.RowIter, error) {
return &showCreateTablesIter{
db: n.Database,
table: n.Table,
}, nil
}

// String implements the Stringer interface.
func (n *ShowCreateTable) String() string {
return fmt.Sprintf("ShowCreateTable(%s)", n.Table)
}

type createTableStmt struct {
colName string
colType sql.Type
}

type showCreateTablesIter struct {
db sql.Database
table string

createStmt *createTableStmt
}

func (i *showCreateTablesIter) Next() (sql.Row, error) {
table := i.db.Tables()[i.table]

if table == nil {
return nil, ErrTableNotFound.New(table)
}

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.Default != nil {
createStmtPart = fmt.Sprintf("%s DEFAULT %v", createStmtPart, col.Default)
}

if !col.Nullable {
createStmtPart = fmt.Sprintf("%sNOT NULL", createStmtPart)
}

if indx != len(schema)-1 {
colCreateStatements[indx] = createStmtPart + ",\n"
continue
}

colCreateStatements[indx] = createStmtPart
}

prettyColCreateStmts := fmt.Sprintf("%s", stripBrackets(colCreateStatements))

composedCreateTableStatement :=
fmt.Sprintf("CREATE TABLE `%s` (%s) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", i.table, prettyColCreateStmts)

return sql.NewRow(
i.table, // "Table" string
composedCreateTableStatement, // "Create Table" string
), nil
}

func stripBrackets(val interface{}) string {
return strings.Trim(fmt.Sprintf("%s", val), "[]")
}

func (i *showCreateTablesIter) Close() error {
return nil
}

// NewShowCreateTable creates a new ShowCreateTable node.
func NewShowCreateTable(db sql.Database, table string, registry *sql.IndexRegistry) sql.Node {
return &ShowCreateTable{db, table, registry}
}

// Resolved implements the Resolvable interface.
func (n *ShowCreateTable) Resolved() bool {
_, ok := n.Database.(*sql.UnresolvedDatabase)
return !ok
}

// Children implements the Node interface.
func (n *ShowCreateTable) Children() []sql.Node { return nil }
42 changes: 42 additions & 0 deletions sql/plan/show_create_table_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package plan

import (
"github.com/stretchr/testify/require"
"gopkg.in/src-d/go-mysql-server.v0/mem"
"gopkg.in/src-d/go-mysql-server.v0/sql"
"testing"
)

func TestShowCreateTable(t *testing.T) {
var require = require.New(t)

db := mem.NewDatabase("test")

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)

showCreateTable := NewShowCreateTable(db, table.Name(), sql.NewIndexRegistry())

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 DEFAULT 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