// Send forwards the call for further processing. func (s *Server) exec(call sqlwire.Call) { req := call.Args resp := call.Reply stmt, err := parser.Parse(req.Sql) if err != nil { resp.SetGoError(err) return } switch p := stmt.(type) { case *parser.CreateDatabase: s.CreateDatabase(p, req.Params, resp) case *parser.CreateTable: s.CreateTable(p, req.Params, resp) case *parser.Delete: s.Delete(p, req.Params, resp) case *parser.Insert: s.Insert(p, req.Params, resp) case *parser.Select: s.Select(p, req.Params, resp) case *parser.ShowColumns: s.ShowColumns(p, req.Params, resp) case *parser.ShowDatabases: s.ShowDatabases(p, req.Params, resp) case *parser.ShowIndex: s.ShowIndex(p, req.Params, resp) case *parser.ShowTables: s.ShowTables(p, req.Params, resp) case *parser.Update: s.Update(p, req.Params, resp) case *parser.Use: s.Use(p, req.Params, resp) default: resp.SetGoError(fmt.Errorf("unknown statement type: %T", stmt)) } }
func (c *conn) Exec(query string, args []driver.Value) (driver.Result, error) { stmt, err := parser.Parse(query) if err != nil { return nil, err } return c.exec(stmt, args) }
func (c *conn) Prepare(query string) (driver.Stmt, error) { s, err := parser.Parse(query) if err != nil { return nil, err } return &stmt{conn: c, stmt: s}, nil }
func (c *conn) Query(query string, args []driver.Value) (driver.Rows, error) { stmt, err := parser.Parse(query) if err != nil { return nil, err } return c.query(stmt, args) }
// exec executes the request. Any error encountered is returned; it is // the caller's responsibility to update the response. func (s *Server) exec(req driver.Request) (driver.Response, error) { var resp driver.Response // Pick up current session state. planner := planner{db: s.db} if req.Session != nil { // TODO(tschottdorf) will have to validate the Session information (for // instance, whether access to the stored database is permitted). if err := gogoproto.Unmarshal(req.Session, &planner.session); err != nil { return resp, err } } stmts, err := parser.Parse(req.Sql) if err != nil { return resp, err } for _, stmt := range stmts { // Bind all the placeholder variables in the stmt to actual values. if err := parser.FillArgs(stmt, parameters(req.Params)); err != nil { return resp, err } var plan planNode if plan, err = planner.makePlan(stmt); err != nil { return resp, err } result := driver.Result{ Columns: plan.Columns(), } for plan.Next() { values := plan.Values() row := driver.Result_Row{} row.Values = make([]driver.Datum, 0, len(values)) for _, val := range values { switch vt := val.(type) { case parser.DBool: row.Values = append(row.Values, driver.Datum{BoolVal: (*bool)(&vt)}) case parser.DInt: row.Values = append(row.Values, driver.Datum{IntVal: (*int64)(&vt)}) case parser.DFloat: row.Values = append(row.Values, driver.Datum{FloatVal: (*float64)(&vt)}) case parser.DString: row.Values = append(row.Values, driver.Datum{StringVal: (*string)(&vt)}) case parser.DNull: row.Values = append(row.Values, driver.Datum{}) default: return resp, util.Errorf("unsupported datum: %T", val) } } result.Rows = append(result.Rows, row) } resp.Results = append(resp.Results, result) } // Update session state. resp.Session, err = gogoproto.Marshal(&planner.session) return resp, err }
func fuzzSingle(stmt parser.Statement) (interestingness int) { var lastExpr parser.Expr rcvr := func() { if r := recover(); r != nil { if !expected(fmt.Sprintf("%v", r)) { fmt.Printf("Stmt: %s\n%s", stmt, spew.Sdump(stmt)) if lastExpr != nil { fmt.Printf("Expr: %s", spew.Sdump(lastExpr)) } panic(r) } // Anything that has expected errors in it is fine, but not as // interesting as things that go through. interestingness = 1 } } defer rcvr() data0 := stmt.String() // TODO(tschottdorf): again, this is since we're ignoring stuff in the // grammar instead of erroring out on unsupported language. See: // https://github.com/cockroachdb/cockroach/issues/1949 if strings.Contains(data0, "%!s(<nil>)") { return 0 } stmt1, err := parser.Parse(data0) if err != nil { fmt.Printf("AST: %s", spew.Sdump(stmt)) fmt.Printf("data0: %q\n", data0) panic(err) } interestingness = 2 data1 := stmt1.String() // TODO(tschottdorf): due to the ignoring issue again. // if !reflect.DeepEqual(stmt, stmt1) { if data1 != data0 { fmt.Printf("data0: %q\n", data0) fmt.Printf("AST: %s", spew.Sdump(stmt)) fmt.Printf("data1: %q\n", data1) fmt.Printf("AST: %s", spew.Sdump(stmt1)) panic("not equal") } var v visitorFunc = func(e parser.Expr) parser.Expr { lastExpr = e if _, err := parser.EvalExpr(e); err != nil { panic(err) } return e } parser.WalkStmt(v, stmt) return }
// Send forwards the call for further processing. func (s *Server) exec(call sqlwire.Call) { req := call.Args resp := call.Reply // Pick up current session state. var session Session if req.Session != nil { if err := gogoproto.Unmarshal(req.Session, &session); err != nil { resp.SetGoError(err) return } } stmt, err := parser.Parse(req.Sql) if err != nil { resp.SetGoError(err) return } switch p := stmt.(type) { case *parser.CreateDatabase: s.CreateDatabase(&session, p, req.Params, resp) case *parser.CreateTable: s.CreateTable(&session, p, req.Params, resp) case *parser.Delete: s.Delete(&session, p, req.Params, resp) case *parser.Insert: s.Insert(&session, p, req.Params, resp) case *parser.Select: s.Select(&session, p, req.Params, resp) case *parser.ShowColumns: s.ShowColumns(&session, p, req.Params, resp) case *parser.ShowDatabases: s.ShowDatabases(&session, p, req.Params, resp) case *parser.ShowIndex: s.ShowIndex(&session, p, req.Params, resp) case *parser.ShowTables: s.ShowTables(&session, p, req.Params, resp) case *parser.Update: s.Update(&session, p, req.Params, resp) case *parser.Use: s.Use(&session, p, req.Params, resp) default: resp.SetGoError(fmt.Errorf("unknown statement type: %T", stmt)) } // Update session state. if resp.Error != nil { return } payload, err := gogoproto.Marshal(&session) if err != nil { resp.SetGoError(util.Errorf("unable to marshal %+v to protobuf: %s", session, err)) return } resp.Session = payload }
// Send forwards the call for further processing. func (s *Server) exec(call sqlwire.Call) error { req := call.Args resp := call.Reply // Pick up current session state. var session Session if req.Session != nil { // TODO(tschottdorf) will have to validate the Session information (for // instance, whether access to the stored database is permitted). if err := gogoproto.Unmarshal(req.Session, &session); err != nil { return err } } stmts, err := parser.Parse(req.Sql) if err != nil { return err } for _, stmt := range stmts { switch p := stmt.(type) { case *parser.CreateDatabase: err = s.CreateDatabase(&session, p, req.Params, resp) case *parser.CreateTable: err = s.CreateTable(&session, p, req.Params, resp) case *parser.Delete: err = s.Delete(&session, p, req.Params, resp) case *parser.Insert: err = s.Insert(&session, p, req.Params, resp) case *parser.Select: err = s.Select(&session, p, req.Params, resp) case *parser.Set: err = s.Set(&session, p, req.Params, resp) case *parser.ShowColumns: err = s.ShowColumns(&session, p, req.Params, resp) case *parser.ShowDatabases: err = s.ShowDatabases(&session, p, req.Params, resp) case *parser.ShowIndex: err = s.ShowIndex(&session, p, req.Params, resp) case *parser.ShowTables: err = s.ShowTables(&session, p, req.Params, resp) case *parser.Update: err = s.Update(&session, p, req.Params, resp) default: err = fmt.Errorf("unknown statement type: %T", stmt) } if err != nil { return err } } // Update session state. resp.Session, err = gogoproto.Marshal(&session) return err }
// Fuzz is the entry point for go-fuzz. Run it via // go-fuzz-build github.com/cockroachdb/go-fuzz/examples/parser && \ // go-fuzz -bin=./parser-fuzz.zip -workdir=. func Fuzz(data []byte) (interestingness int) { sql := string(data) stmts, err := parser.Parse(sql) if err != nil || stmts == nil { if stmts != nil { panic("stmt is not nil on error") } return } for _, stmt := range stmts { interestingness = fuzzSingle(stmt) } return }
func TestPrimaryKeyUnspecified(t *testing.T) { defer leaktest.AfterTest(t) stmt, err := parser.Parse("CREATE TABLE test (a INT, b INT, CONSTRAINT c UNIQUE (b))") if err != nil { t.Fatal(err) } desc, err := makeTableDesc(stmt[0].(*parser.CreateTable)) if err != nil { t.Fatal(err) } if err := desc.AllocateIDs(); err != structured.ErrMissingPrimaryKey { t.Fatal(err) } }
// Send forwards the call for further processing. func (s *Server) exec(call sqlwire.Call) error { req := call.Args resp := call.Reply // Pick up current session state. var session Session if req.Session != nil { if err := gogoproto.Unmarshal(req.Session, &session); err != nil { return err } } stmt, err := parser.Parse(req.Sql) if err != nil { return err } switch p := stmt.(type) { case *parser.CreateDatabase: err = s.CreateDatabase(&session, p, req.Params, resp) case *parser.CreateTable: err = s.CreateTable(&session, p, req.Params, resp) case *parser.Delete: err = s.Delete(&session, p, req.Params, resp) case *parser.Insert: err = s.Insert(&session, p, req.Params, resp) case *parser.Select: err = s.Select(&session, p, req.Params, resp) case *parser.ShowColumns: err = s.ShowColumns(&session, p, req.Params, resp) case *parser.ShowDatabases: err = s.ShowDatabases(&session, p, req.Params, resp) case *parser.ShowIndex: err = s.ShowIndex(&session, p, req.Params, resp) case *parser.ShowTables: err = s.ShowTables(&session, p, req.Params, resp) case *parser.Update: err = s.Update(&session, p, req.Params, resp) case *parser.Use: s.Use(&session, p, req.Params, resp) default: err = fmt.Errorf("unknown statement type: %T", stmt) } if err != nil { return err } // Update session state. resp.Session, err = gogoproto.Marshal(&session) return err }
// exec executes the request. Any error encountered is returned; it is // the caller's responsibility to update the response. func (s server) execStmts(sql string, params parameters, planMaker *planner) driver.Response { var resp driver.Response stmts, err := parser.Parse(sql, parser.Syntax(planMaker.session.Syntax)) if err != nil { // A parse error occured: we can't determine if there were multiple // statements or only one, so just pretend there was one. resp.Results = append(resp.Results, rollbackTxnAndReturnResultWithError(planMaker, err)) return resp } for _, stmt := range stmts { result, err := s.execStmt(stmt, params, planMaker) if err != nil { result = rollbackTxnAndReturnResultWithError(planMaker, err) } resp.Results = append(resp.Results, result) } return resp }
func TestPrimaryKeyUnspecified(t *testing.T) { defer leaktest.AfterTest(t) stmt, err := parser.Parse("CREATE TABLE foo.test (a INT, b INT, CONSTRAINT c UNIQUE (b))") if err != nil { t.Fatal(err) } create := stmt[0].(*parser.CreateTable) if err := create.Table.NormalizeTableName(""); err != nil { t.Fatal(err) } desc, err := makeTableDesc(create) if err != nil { t.Fatal(err) } if err := desc.AllocateIDs(); err != ErrMissingPrimaryKey { t.Fatal(err) } }
func TestMakeDatabaseDesc(t *testing.T) { defer leaktest.AfterTest(t) stmt, err := parser.Parse("CREATE DATABASE test") if err != nil { t.Fatal(err) } desc := makeDatabaseDesc(stmt[0].(*parser.CreateDatabase)) if desc.Name != "test" { t.Fatalf("expected Name == test, got %s", desc.Name) } // ID is not set yet. if desc.ID != 0 { t.Fatalf("expected ID == 0, got %d", desc.ID) } if len(desc.GetPrivileges().Users) != 1 { t.Fatalf("wrong number of privilege users, expected 1, got: %d", len(desc.GetPrivileges().Users)) } }
func init() { const sql = ` CREATE TABLE system.namespace ( "parentID" INT, "name" CHAR, "id" INT, PRIMARY KEY (parentID, name) ); CREATE TABLE system.descriptor ( "id" INT PRIMARY KEY, "desc" BLOB ); ` stmts, err := parser.Parse(sql) if err != nil { log.Fatal(err) } NamespaceTable, err = makeTableDesc(stmts[0].(*parser.CreateTable)) if err != nil { log.Fatal(err) } NamespaceTable.Privileges = SystemDB.Privileges NamespaceTable.ID = namespaceTableID if err := NamespaceTable.AllocateIDs(); err != nil { log.Fatal(err) } DescriptorTable, err = makeTableDesc(stmts[1].(*parser.CreateTable)) if err != nil { log.Fatal(err) } DescriptorTable.Privileges = SystemDB.Privileges DescriptorTable.ID = descriptorTableID if err := DescriptorTable.AllocateIDs(); err != nil { log.Fatal(err) } }
func TestMakeDatabaseDesc(t *testing.T) { defer leaktest.AfterTest(t) stmt, err := parser.Parse("CREATE DATABASE test") if err != nil { t.Fatal(err) } desc := makeDatabaseDesc(stmt[0].(*parser.CreateDatabase)) if desc.Name != "test" { t.Fatalf("expected Name == test, got %s", desc.Name) } // ID is not set yet. if desc.ID != 0 { t.Fatalf("expected ID == 0, got %d", desc.ID) } if len(desc.Read) != 1 || desc.Read[0] != security.RootUser { t.Fatalf("expected read == [root], got: %v", desc.Read) } if len(desc.Write) != 1 || desc.Write[0] != security.RootUser { t.Fatalf("expected write == [root], got: %v", desc.Write) } }
func TestEvalExprError(t *testing.T) { testData := []struct { expr string expected string }{ {`'a' + 0`, `parsing \"a\": invalid syntax`}, {`'0a' + 0`, `parsing \"0a\": invalid syntax`}, {`a`, `column \"a\" not found`}, // TODO(pmattis): Check for overflow. // {`~0 + 1`, `0`, nil}, } for i, d := range testData { q, err := parser.Parse("SELECT " + d.expr) if err != nil { t.Fatalf("%d: %v: %s", i, err, d.expr) } expr := q.(*parser.Select).Exprs[0].(*parser.NonStarExpr).Expr if _, err := EvalExpr(expr, mapEnv{}); !testutils.IsError(err, d.expected) { t.Errorf("%d: expected %s, but found %v", i, d.expected, err) } } }
// Send forwards the call for further processing. func (s *Server) send(call sqlwire.Call) { req := call.Args resp := call.Reply stmt, err := parser.Parse(req.Sql) if err != nil { echo(req.Sql, resp) return } switch p := stmt.(type) { case *parser.ShowColumns: s.ShowColumns(p, req.Params, resp) case *parser.ShowDatabases: s.ShowDatabases(p, req.Params, resp) case *parser.ShowIndex: s.ShowIndex(p, req.Params, resp) case *parser.ShowTables: s.ShowTables(p, req.Params, resp) case *parser.Use: s.Use(p, req.Params, resp) default: echo(req.Sql, resp) } }
// exec executes the request. Any error encountered is returned; it is // the caller's responsibility to update the response. func (e *Executor) execStmts(sql string, params parameters, planMaker *planner) driver.Response { var resp driver.Response stmts, err := parser.Parse(sql, parser.Syntax(planMaker.session.Syntax)) if err != nil { // A parse error occurred: we can't determine if there were multiple // statements or only one, so just pretend there was one. resp.Results = append(resp.Results, makeResultFromError(planMaker, err)) return resp } for _, stmt := range stmts { result, err := e.execStmt(stmt, params, planMaker) if err != nil { result = makeResultFromError(planMaker, err) } resp.Results = append(resp.Results, result) // TODO(pmattis): Is this the correct time to be releasing leases acquired // during execution of the statement? // // TODO(pmattis): Need to record the leases used by a transaction within // the transaction state and restore it when the transaction is restored. planMaker.releaseLeases(e.db) } return resp }
func TestEvalExpr(t *testing.T) { testData := []struct { expr string expected string env Env }{ // Bitwise operators. {`1 & 3`, `1`, nil}, {`1 | 3`, `3`, nil}, {`1 ^ 3`, `2`, nil}, // Bitwise operators convert their arguments to ints. // TODO(pmattis): Should this be an error instead? {`1.1 ^ 3.1`, `2`, nil}, // Arithmetic operators. {`1 + 1`, `2`, nil}, {`1 - 2`, `-1`, nil}, {`3 * 4`, `12`, nil}, {`3.1 % 2.0`, `1.1`, nil}, {`5 % 3`, `2`, nil}, // Division is always done on floats. {`4 / 5`, `0.8`, nil}, // Grouping {`1 + 2 + (3 * 4)`, `15`, nil}, // Unary operators. {`-3`, `-3`, nil}, {`-4.1`, `-4.1`, nil}, // Ones complement operates on unsigned integers. {`~0`, `18446744073709551615`, nil}, {`~0.1`, `18446744073709551615`, nil}, {`~0 - 1`, `18446744073709551614`, nil}, // Hexadecimal numbers. {`0xa`, `10`, nil}, // Octal numbers. {`0755`, `493`, nil}, // String conversion {`'1' + '2'`, `3`, nil}, // Strings convert to floats. {`'18446744073709551614' + 1`, `1.8446744073709552e+19`, nil}, // String concatenation. {`'a' || 'b'`, `ab`, nil}, {`'a' || (1 + 2)`, `a3`, nil}, // Column lookup. {`a`, `1`, mapEnv{"a": dInt(1)}}, {`a`, `2`, mapEnv{"a": dUint(2)}}, {`a`, `3.1`, mapEnv{"a": dFloat(3.1)}}, {`a`, `b`, mapEnv{"a": dBytes([]byte("b"))}}, {`a`, `c`, mapEnv{"a": dString("c")}}, {`a.b + 1`, `2`, mapEnv{"a.b": dInt(1)}}, // Boolean expressions. {`false AND true`, `false`, nil}, {`true AND true`, `true`, nil}, {`true AND false`, `false`, nil}, {`false AND false`, `false`, nil}, {`false OR true`, `true`, nil}, {`true OR true`, `true`, nil}, {`true OR false`, `true`, nil}, {`false OR false`, `false`, nil}, {`NOT false`, `true`, nil}, {`NOT true`, `false`, nil}, // Boolean expressions short-circuit the evaluation. {`false AND (a = 1)`, `false`, nil}, {`true OR (a = 1)`, `true`, nil}, // Comparisons. {`0 = 1`, `false`, nil}, {`0 != 1`, `true`, nil}, {`0 < 1`, `true`, nil}, {`0 <= 1`, `true`, nil}, {`0 > 1`, `false`, nil}, {`0 >= 1`, `false`, nil}, {`true = false`, `false`, nil}, {`true != false`, `true`, nil}, {`true < false`, `false`, nil}, {`true <= false`, `false`, nil}, {`true > false`, `true`, nil}, {`true >= false`, `true`, nil}, {`true <=> false`, `false`, nil}, {`'a' = 'b'`, `false`, nil}, {`'a' != 'b'`, `true`, nil}, {`'a' < 'b'`, `true`, nil}, {`'a' <= 'b'`, `true`, nil}, {`'a' > 'b'`, `false`, nil}, {`'a' >= 'b'`, `false`, nil}, {`'a' >= 'b'`, `false`, nil}, // Comparison of a string against a number compares using floating point. {`'10' > '2'`, `false`, nil}, {`'10' > 2`, `true`, nil}, // Comparisons against NULL result in NULL, except for the null-safe equal. {`0 = NULL`, `NULL`, nil}, {`NULL = NULL`, `NULL`, nil}, {`NULL <=> NULL`, `true`, nil}, // NULL checks. {`0 IS NULL`, `false`, nil}, {`0 IS NOT NULL`, `true`, nil}, {`NULL IS NULL`, `true`, nil}, {`NULL IS NOT NULL`, `false`, nil}, // Range conditions. {`2 BETWEEN 1 AND 3`, `true`, nil}, {`1 NOT BETWEEN 2 AND 3`, `true`, nil}, {`'foo' BETWEEN 'a' AND 'z'`, `true`, nil}, // Case operator. {`CASE WHEN true THEN 1 END`, `1`, nil}, {`CASE WHEN false THEN 1 END`, `NULL`, nil}, {`CASE WHEN false THEN 1 ELSE 2 END`, `2`, nil}, {`CASE WHEN false THEN 1 WHEN false THEN 2 END`, `NULL`, nil}, } for i, d := range testData { q, err := parser.Parse("SELECT " + d.expr) if err != nil { t.Fatalf("%d: %v: %s", i, err, d.expr) } expr := q.(*parser.Select).Exprs[0].(*parser.NonStarExpr).Expr r, err := EvalExpr(expr, d.env) if err != nil { t.Fatalf("%d: %v", i, err) } if s := r.String(); d.expected != s { t.Errorf("%d: expected %s, but found %s: %s", i, d.expected, s, d.expr) } } }
// exec executes the request. Any error encountered is returned; it is // the caller's responsibility to update the response. func (s *Server) exec(req driver.Request) (resp driver.Response, err error) { // Pick up current session state. // The request user is validated in ServeHTTP. Even in insecure mode, // it is guaranteed not to be empty. planMaker := planner{user: req.GetUser()} defer func() { // Update session state even if an error occurs. if bytes, err := gogoproto.Marshal(&planMaker.session); err != nil { // Marshaling a `Session` never errors (known from reading the code). panic(err) } else { resp.Session = bytes } }() if req.Session != nil { // TODO(tschottdorf) will have to validate the Session information (for // instance, whether access to the stored database is permitted). if err = gogoproto.Unmarshal(req.Session, &planMaker.session); err != nil { return } } var stmts parser.StatementList if stmts, err = parser.Parse(req.Sql); err != nil { return } for _, stmt := range stmts { // Bind all the placeholder variables in the stmt to actual values. if err = parser.FillArgs(stmt, parameters(req.Params)); err != nil { return } var plan planNode if err = s.db.Txn(func(txn *client.Txn) error { planMaker.txn = txn plan, err = planMaker.makePlan(stmt) planMaker.txn = nil return err }); err != nil { return } result := driver.Result{ Columns: plan.Columns(), } for plan.Next() { values := plan.Values() row := driver.Result_Row{} row.Values = make([]driver.Datum, 0, len(values)) for _, val := range values { if val == parser.DNull { row.Values = append(row.Values, driver.Datum{}) } else { switch vt := val.(type) { case parser.DBool: row.Values = append(row.Values, driver.Datum{BoolVal: (*bool)(&vt)}) case parser.DInt: row.Values = append(row.Values, driver.Datum{IntVal: (*int64)(&vt)}) case parser.DFloat: row.Values = append(row.Values, driver.Datum{FloatVal: (*float64)(&vt)}) case parser.DString: row.Values = append(row.Values, driver.Datum{StringVal: (*string)(&vt)}) default: err = util.Errorf("unsupported datum: %T", val) return } } } result.Rows = append(result.Rows, row) } if err = plan.Err(); err != nil { return } resp.Results = append(resp.Results, result) } return }
func TestMakeTableDescIndexes(t *testing.T) { defer leaktest.AfterTest(t) testData := []struct { sql string primary structured.IndexDescriptor indexes []structured.IndexDescriptor }{ { "a INT PRIMARY KEY", structured.IndexDescriptor{ Name: structured.PrimaryKeyIndexName, Unique: true, ColumnNames: []string{"a"}, }, []structured.IndexDescriptor{}, }, { "a INT UNIQUE, b INT PRIMARY KEY", structured.IndexDescriptor{ Name: "primary", Unique: true, ColumnNames: []string{"b"}, }, []structured.IndexDescriptor{ { Name: "", Unique: true, ColumnNames: []string{"a"}, }, }, }, { "a INT, b INT, CONSTRAINT c PRIMARY KEY (a, b)", structured.IndexDescriptor{ Name: "c", Unique: true, ColumnNames: []string{"a", "b"}, }, []structured.IndexDescriptor{}, }, { "a INT, b INT, CONSTRAINT c UNIQUE (b), PRIMARY KEY (a, b)", structured.IndexDescriptor{ Name: "primary", Unique: true, ColumnNames: []string{"a", "b"}, }, []structured.IndexDescriptor{ { Name: "c", Unique: true, ColumnNames: []string{"b"}, }, }, }, { "a INT, b INT, PRIMARY KEY (a, b)", structured.IndexDescriptor{ Name: structured.PrimaryKeyIndexName, Unique: true, ColumnNames: []string{"a", "b"}, }, []structured.IndexDescriptor{}, }, } for i, d := range testData { stmt, err := parser.Parse("CREATE TABLE foo.test (" + d.sql + ")") if err != nil { t.Fatalf("%d: %v", i, err) } create := stmt[0].(*parser.CreateTable) if err := create.Table.NormalizeTableName(""); err != nil { t.Fatalf("%d: %v", i, err) } schema, err := makeTableDesc(create) if err != nil { t.Fatalf("%d: %v", i, err) } if !reflect.DeepEqual(d.primary, schema.PrimaryIndex) { t.Fatalf("%d: expected %+v, but got %+v", i, d.primary, schema.PrimaryIndex) } if !reflect.DeepEqual(d.indexes, append([]structured.IndexDescriptor{}, schema.Indexes...)) { t.Fatalf("%d: expected %+v, but got %+v", i, d.indexes, schema.Indexes) } } }
func TestMakeTableDescColumns(t *testing.T) { defer leaktest.AfterTest(t) testData := []struct { sqlType string colType structured.ColumnType nullable bool }{ { "BIT(1)", structured.ColumnType{Kind: structured.ColumnType_BIT, Width: 1}, true, }, { "BOOLEAN", structured.ColumnType{Kind: structured.ColumnType_BOOL}, true, }, { "INT", structured.ColumnType{Kind: structured.ColumnType_INT}, true, }, { "FLOAT(3)", structured.ColumnType{Kind: structured.ColumnType_FLOAT, Precision: 3}, true, }, { "DECIMAL(5,6)", structured.ColumnType{Kind: structured.ColumnType_DECIMAL, Precision: 5, Width: 6}, true, }, { "DATE", structured.ColumnType{Kind: structured.ColumnType_DATE}, true, }, { "TIME", structured.ColumnType{Kind: structured.ColumnType_TIME}, true, }, { "TIMESTAMP", structured.ColumnType{Kind: structured.ColumnType_TIMESTAMP}, true, }, { "CHAR", structured.ColumnType{Kind: structured.ColumnType_CHAR}, true, }, { "TEXT", structured.ColumnType{Kind: structured.ColumnType_TEXT}, true, }, { "BLOB", structured.ColumnType{Kind: structured.ColumnType_BLOB}, true, }, { "INT NOT NULL", structured.ColumnType{Kind: structured.ColumnType_INT}, false, }, { "INT NULL", structured.ColumnType{Kind: structured.ColumnType_INT}, true, }, } for i, d := range testData { stmt, err := parser.Parse("CREATE TABLE foo.test (a " + d.sqlType + " PRIMARY KEY)") if err != nil { t.Fatalf("%d: %v", i, err) } create := stmt[0].(*parser.CreateTable) if err := create.Table.NormalizeTableName(""); err != nil { t.Fatalf("%d: %v", i, err) } schema, err := makeTableDesc(create) if err != nil { t.Fatalf("%d: %v", i, err) } if !reflect.DeepEqual(d.colType, schema.Columns[0].Type) { t.Fatalf("%d: expected %+v, but got %+v", i, d.colType, schema.Columns[0]) } if d.nullable != schema.Columns[0].Nullable { t.Fatalf("%d: expected %+v, but got %+v", i, d.nullable, schema.Columns[0].Nullable) } } }
func TestMakeTableDescIndexes(t *testing.T) { defer leaktest.AfterTest(t) testData := []struct { sql string index structured.IndexDescriptor }{ { "a INT PRIMARY KEY", structured.IndexDescriptor{ Name: "primary", Unique: true, ColumnNames: []string{"a"}, }, }, { "a INT UNIQUE", structured.IndexDescriptor{ Name: "", Unique: true, ColumnNames: []string{"a"}, }, }, { "a INT, b INT, INDEX c (a, b)", structured.IndexDescriptor{ Name: "c", Unique: false, ColumnNames: []string{"a", "b"}, }, }, { "a INT, b INT, UNIQUE INDEX c (a, b)", structured.IndexDescriptor{ Name: "c", Unique: true, ColumnNames: []string{"a", "b"}, }, }, { "a INT, b INT, PRIMARY KEY (a, b)", structured.IndexDescriptor{ Name: "primary", Unique: true, ColumnNames: []string{"a", "b"}, }, }, } for i, d := range testData { stmt, err := parser.Parse("CREATE TABLE test (" + d.sql + ")") if err != nil { t.Fatalf("%d: %v", i, err) } schema, err := makeTableDesc(stmt.(*parser.CreateTable)) if err != nil { t.Fatalf("%d: %v", i, err) } if !reflect.DeepEqual(d.index, schema.Indexes[0]) { t.Fatalf("%d: expected %+v, but got %+v", i, d.index, schema.Indexes[0]) } } }
func TestMakeTableDescColumns(t *testing.T) { defer leaktest.AfterTest(t) testData := []struct { sqlType string colType structured.ColumnType nullable bool }{ { "BIT(1)", structured.ColumnType{Kind: structured.ColumnType_BIT, Width: 1}, true, }, { "INT(2)", structured.ColumnType{Kind: structured.ColumnType_INT, Width: 2}, true, }, { "FLOAT(3,4)", structured.ColumnType{Kind: structured.ColumnType_FLOAT, Width: 3, Precision: 4}, true, }, { "DECIMAL(5,6)", structured.ColumnType{Kind: structured.ColumnType_DECIMAL, Width: 5, Precision: 6}, true, }, { "DATE", structured.ColumnType{Kind: structured.ColumnType_DATE}, true, }, { "TIME", structured.ColumnType{Kind: structured.ColumnType_TIME}, true, }, { "DATETIME", structured.ColumnType{Kind: structured.ColumnType_DATETIME}, true, }, { "TIMESTAMP", structured.ColumnType{Kind: structured.ColumnType_TIMESTAMP}, true, }, { "CHAR", structured.ColumnType{Kind: structured.ColumnType_CHAR}, true, }, { "TEXT", structured.ColumnType{Kind: structured.ColumnType_TEXT}, true, }, { "BLOB", structured.ColumnType{Kind: structured.ColumnType_BLOB}, true, }, { "ENUM(a)", structured.ColumnType{Kind: structured.ColumnType_ENUM, Vals: []string{"a"}}, true, }, { "SET(b)", structured.ColumnType{Kind: structured.ColumnType_SET, Vals: []string{"b"}}, true, }, { "INT NOT NULL", structured.ColumnType{Kind: structured.ColumnType_INT}, false, }, { "INT NULL", structured.ColumnType{Kind: structured.ColumnType_INT}, true, }, } for i, d := range testData { stmt, err := parser.Parse("CREATE TABLE test (a " + d.sqlType + ")") if err != nil { t.Fatalf("%d: %v", i, err) } schema, err := makeTableDesc(stmt.(*parser.CreateTable)) if err != nil { t.Fatalf("%d: %v", i, err) } if !reflect.DeepEqual(d.colType, schema.Columns[0].Type) { t.Fatalf("%d: expected %+v, but got %+v", i, d.colType, schema.Columns[0]) } if d.nullable != schema.Columns[0].Nullable { t.Fatalf("%d: expected %+v, but got %+v", i, d.nullable, schema.Columns[0].Nullable) } } }