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 statements to Select #68

Merged
merged 11 commits into from
Mar 8, 2016
Prev Previous commit
Next Next commit
Schema for Show to Select conversion
  • Loading branch information
Aaron Raddon committed Mar 3, 2016
commit af1f54dcc1c481b28289f374b2270b7266c06e56
3 changes: 0 additions & 3 deletions datasource/mockcsvtestdata/testdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ hT2impsabc345c,"not_an_email",,"2009-12-11T19:53:31.547Z",12`)

func init() {

u.SetupLogging("debug")
u.SetColorIfTerminal()

LoadTestDataOnce()

builtins.LoadAllBuiltins()
Expand Down
48 changes: 38 additions & 10 deletions datasource/schemadb.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,24 @@ var (
_ schema.Scanner = (*schemaConn)(nil)

// normal tables
defaultSchemaTables = []string{"tables"}
defaultTableColumns = []string{"Table"}
defaultSchemaTables = []string{"tables", "databases", "columns"}
tableColumns = []string{"Table"}
databasesColumns = []string{"Database"}
columnColumns = []string{"Field", "Type", "Null", "Key", "Default", "Extra"}
tableColumnMap = map[string]int{"Table": 0}
columnsColumnMap = map[string]int{"Field": 0, "Type": 1, "Null": 2, "Key": 3, "Default": 4, "Extra": 5}
/*
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)

*/
)

type (
Expand All @@ -44,6 +57,7 @@ type (
db *SchemaDb
tbl *schema.Table
cursor int
rows [][]driver.Value
}
)

Expand All @@ -54,17 +68,19 @@ func NewSchemaDb(s *schema.Schema) *SchemaDb {
func (m *SchemaDb) Close() error { return nil }
func (m *SchemaDb) Tables() []string { return m.tbls }
func (m *SchemaDb) Table(table string) (*schema.Table, error) {
//u.Infof("ask for table %q", table)
u.Infof("ask for table %q", table)
switch table {
case "tables":
return tableForSchema(m.s, m.is)
case "databases":
return databasesForSchema(m.s, m.is)
}
return nil, schema.ErrNotFound
}

// Create a schemaConn specific to schema object (table, database)
func (m *SchemaDb) Open(schemaObjectName string) (schema.SourceConn, error) {
//u.Warnf("SchemaDb.Open(%q)", schemaObjectName)
u.Warnf("SchemaDb.Open(%q)", schemaObjectName)
tbl, err := m.Table(schemaObjectName)
if err == nil && tbl != nil {
return &schemaConn{db: m, tbl: tbl}, nil
Expand All @@ -84,7 +100,9 @@ func (m *schemaConn) Next() schema.Message {
if m.cursor >= len(m.db.s.Tables()) {
return nil
}
u.Infof("%d Next(): %v", m.cursor, m.db.s.Tables())
u.Infof("%d Next():", m.cursor)
vals := make([]driver.Value, 1)

select {
case <-m.db.exit:
return nil
Expand All @@ -96,23 +114,33 @@ func (m *schemaConn) Next() schema.Message {
u.Warnf("wat? %q", tableName)
return nil
}
vals := make([]driver.Value, 1)
vals[0] = tbl.Name
msg := NewSqlDriverMessageMap(uint64(m.cursor-1), vals, tableColumnMap)
u.Infof("msg: %#v", msg)
return msg
}
msg := NewSqlDriverMessageMap(uint64(m.cursor-1), vals, tableColumnMap)
u.Infof("msg: %#v", msg)
return msg
}

func (m *schemaConn) Get(key driver.Value) (schema.Message, error) {
return nil, schema.ErrNotFound
}

func tableForSchema(s, is *schema.Schema) (*schema.Table, error) {
// This table doesn't belong in schema
ss := is.SourceSchemas["schema"]
t := schema.NewTable("tables", ss)
t.AddField(schema.NewFieldBase("Table", value.StringType, 64, "string"))
t.SetColumns(defaultTableColumns)
t.SetColumns(tableColumns)
ss.AddTable(t)
return t, nil
}

func databasesForSchema(s, is *schema.Schema) (*schema.Table, error) {
ss := is.SourceSchemas["schema"]

t := schema.NewTable("databases", ss)
t.AddField(schema.NewFieldBase("Database", value.StringType, 64, "string"))
t.SetColumns(databasesColumns)
ss.AddTable(t)
return t, nil
}
Expand Down
5 changes: 5 additions & 0 deletions datasource/schemadb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ func init() {
var _ = u.EMPTY

func TestSchemaShowStatements(t *testing.T) {
// TODO: this test needs the "databases" ie system-schema not current-info-schema
testutil.TestSelect(t, `show databases;`,
[][]driver.Value{{"users"}},
)
return
// - rewrite show tables -> "use schema; select name from schema.tables;"
testutil.TestSelect(t, `show tables;`,
[][]driver.Value{{"orders"}, {"users"}},
Expand Down
11 changes: 2 additions & 9 deletions exec/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const (
ItemDefaultChannelSize = 50
)

// Base executeable task that implements Task interface, embedded
// into other channel based task runners
type TaskBase struct {
Ctx *plan.Context
Handler MessageHandler
Expand All @@ -43,15 +45,6 @@ func NewTaskBase(ctx *plan.Context) *TaskBase {
}
}

// /// TEMP----------------------------------------------------------
// func (m *TaskBase) IsParallel() bool { return m.parallel }
// func (m *TaskBase) IsSequential() bool { return !m.parallel }
// func (m *TaskBase) SetParallel() { m.parallel = true }
// func (m *TaskBase) SetSequential() { m.parallel = false }
// func (m *TaskBase) Walk(plan.Planner) error { panic("not implemented") }
// func (m *TaskBase) WalkStatus(plan.Planner) (plan.WalkStatus, error) { panic("not implemented") }
// // //------- TEMP

func (m *TaskBase) Children() []Task { return nil }
func (m *TaskBase) Setup(depth int) error {
m.depth = depth
Expand Down
3 changes: 2 additions & 1 deletion plan/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@ func SourceFromPB(pb *PlanPb, ctx *Context) (*Source, error) {

func NewSource(ctx *Context, stmt *rel.SqlSource, isFinal bool) (*Source, error) {
s := &Source{Stmt: stmt, ctx: ctx, SourcePb: &SourcePb{Final: isFinal}, PlanBase: NewPlanBase(false)}
u.Debugf("calling load")
err := s.load()
if err != nil {
return nil, err
Expand Down Expand Up @@ -651,7 +652,7 @@ func (m *Source) load() error {
}
ss, err := m.ctx.Schema.Source(fromName)
if err != nil {
u.Errorf("no schema found for %q ? err=%v", fromName, err)
u.Errorf("no schema found for %T %q ? err=%v", m.ctx.Schema, fromName, err)
return err
}
if ss == nil {
Expand Down
9 changes: 5 additions & 4 deletions plan/planner_select.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func (m *PlannerDefault) WalkSelect(p *Select) error {

if len(p.Stmt.From) == 0 {
if p.Stmt.SystemQry() {
u.Debugf("is System Query")
return m.WalkSelectSystemInfo(p)
}
return m.WalkLiteralQuery(p)
Expand All @@ -36,7 +37,7 @@ func (m *PlannerDefault) WalkSelect(p *Select) error {

p.Stmt.From[0].Source = p.Stmt // TODO: move to a Finalize() in query planner
srcPlan, err := NewSource(m.Ctx, p.Stmt.From[0], true)
//u.Debugf("%p srcPlan", srcPlan)
u.Debugf("%p srcPlan", srcPlan)
if err != nil {
return nil
}
Expand Down Expand Up @@ -306,17 +307,17 @@ func (m *PlannerDefault) WalkSelectSystemInfo(p *Select) error {

// Handle Literal queries such as "SELECT 1, @var;"
func (m *PlannerDefault) WalkLiteralQuery(p *Select) error {
//u.Debugf("WalkLiteralQuery %+v", p.Stmt)
u.Debugf("WalkLiteralQuery %+v", p.Stmt)
return ErrNotImplemented
}

func (m *PlannerDefault) WalkSelectDatabase(p *Select) error {
//u.Debugf("WalkSelectDatabase %+v", p.Stmt)
u.Debugf("WalkSelectDatabase %+v", p.Stmt)
return ErrNotImplemented
}

func (m *PlannerDefault) WalkSysQuery(p *Select) error {
//u.Debugf("WalkSysQuery %+v", p.Stmt)
u.Debugf("WalkSysQuery %+v", p.Stmt)

//u.Debugf("Ctx.Projection: %#v", m.Ctx.Projection)
//u.Debugf("Ctx.Projection.Proj: %#v", m.Ctx.Projection.Proj)
Expand Down
37 changes: 28 additions & 9 deletions plan/sql_rewrite.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,32 @@ var _ = u.EMPTY
func RewriteShowAsSelect(stmt *rel.SqlShow, ctx *Context) (*rel.SqlSelect, error) {

raw := strings.ToLower(stmt.Raw)
u.Debugf("attempting to rewrite %s", raw)

sel := rel.SqlSelect{}
showType := strings.ToLower(stmt.ShowType)
u.Debugf("%s attempting to rewrite %s", showType, raw)
switch {
case showType == "tables" || strings.ToLower(stmt.Identity) == ctx.SchemaName:
// case strings.ToLower(stmt.Identity) == ctx.SchemaName:
// u.Warnf("what? %s == %s", stmt.Identity, ctx.SchemaName)
case showType == "tables":
if stmt.Full {
// SHOW FULL TABLES; = select name, table_type from tables;
// TODO: note the stupid "_in_mysql", assuming i don't have to implement
/*
mysql> show full tables;
+---------------------------+------------+
| Tables_in_mysql | Table_type |
+---------------------------+------------+
| columns_priv | BASE TABLE |

*/
s2, err := rel.ParseSqlSelect("select Table, Table_Type from tables;")
if err != nil {
return nil, err
}
sel = *s2
} else {
// show tables
//sel.From = append(sel.From, &rel.SqlSource{Name: "tables"})
s2, err := rel.ParseSqlSelect("select Table from tables;")
if err != nil {
return nil, err
Expand All @@ -32,8 +48,14 @@ func RewriteShowAsSelect(stmt *rel.SqlShow, ctx *Context) (*rel.SqlSelect, error
}
//case stmt.Create && strings.ToLower(stmt.CreateWhat) == "table":
// SHOW CREATE TABLE
//case strings.ToLower(stmt.Identity) == "databases":
// SHOW databases; -> select name from databases;
case showType == "databases":
// SHOW databases; -> select Database from databases;
s2, err := rel.ParseSqlSelect("select Database from databases;")
if err != nil {
u.Warnf("could not parse: %v", err)
return nil, err
}
sel = *s2
case showType == "variables":
// SHOW [GLOBAL | SESSION] VARIABLES [like_or_where]
default:
Expand All @@ -55,10 +77,7 @@ func RewriteShowAsSelect(stmt *rel.SqlShow, ctx *Context) (*rel.SqlSelect, error
if ctx.Schema == nil {
u.Warnf("WAT? Still nil info schema?")
}
//u.Infof("new info schema: %p replacing: %p", ctx.Schema, originalSchema)
for _, tbl := range ctx.Schema.Tables() {
u.Infof("info schema table: %v", tbl)
}
u.Debugf("schema: %T new stmt: %s", ctx.Schema, sel.String())
return &sel, nil
}
func RewriteDescribeAsSelect(stmt *rel.SqlDescribe, ctx *Context) (*rel.SqlSelect, error) {
Expand Down
30 changes: 27 additions & 3 deletions schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,8 @@ var (
// default schema Refresh Interval
SchemaRefreshInterval = -time.Minute * 5

// Static list of common field names for describe header
// - full columns shows all,
// - "describe table" only shows sub-set
// Static list of common field names for describe header on Show, Describe

DescribeFullCols = []string{"Field", "Type", "Collation", "Null", "Key", "Default", "Extra", "Privileges", "Comment"}
DescribeCols = []string{"Field", "Type", "Null", "Key", "Default", "Extra"}
DescribeFullHeaders = NewDescribeFullHeaders()
Expand Down Expand Up @@ -83,13 +82,15 @@ type (
tblId uint64 // internal tableid, hash of table name + schema?
cols []string // array of column names
lastRefreshed time.Time // Last time we refreshed this schema
rows [][]driver.Value
}

// Field Describes the column info, name, data type, defaults, index, null
// - dialects (mysql, mongo, cassandra) have their own descriptors for these,
// so this is generic meant to be converted to Frontend at runtime
Field struct {
idx uint64 // Positional index in array of fields
row []driver.Value // memoized value of this field
Name string // Column Name
Description string // Comment/Description
Key string // Key info (primary, etc) should be stored in indexes
Expand Down Expand Up @@ -480,6 +481,16 @@ func (m *Table) SetColumns(cols []string) {
}

func (m *Table) Columns() []string { return m.cols }
func (m *Table) AsRows() [][]driver.Value {
if len(m.rows) > 0 {
return m.rows
}
m.rows = make([][]driver.Value, len(m.Fields))
for i, f := range m.Fields {
m.rows[i] = f.AsRow()
}
return m.rows
}

// List of Field Names and ordinal position in Column list
func (m *Table) FieldNamesPositions() map[string]int { return m.FieldPositions }
Expand Down Expand Up @@ -526,6 +537,19 @@ func NewField(name string, valType value.ValueType, size int, allowNulls bool, d

func (m *Field) Id() uint64 { return m.idx }
func (m *Field) Body() interface{} { return m }
func (m *Field) AsRow() []driver.Value {
if len(m.row) > 0 {
return m.row
}
m.row = make([]driver.Value, len(DescribeFullCols))
// []string{"Field", "Type", "Collation", "Null", "Key", "Default", "Extra", "Privileges", "Comment"}
m.row[0] = m.Name
m.row[1] = m.Type.String()
m.row[2] = m.Collation
m.row[6] = m.Extra
m.row[8] = m.Description
return m.row
}

func NewDescribeFullHeaders() []*Field {
fields := make([]*Field, 9)
Expand Down