diff --git a/.gitignore b/.gitignore index f1028630d..d35a30cb6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ bin/ y.go *.output .idea/ +go.mod +go.sum \ No newline at end of file diff --git a/Makefile b/Makefile index 169820b9a..4ae44861c 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ LINUX:="Linux" all: parser.go test: parser.go - GO111MODULE=on go test ./... + sh test.sh parser.go: parser.y make parser @@ -34,3 +34,4 @@ bin/goyacc: goyacc/main.go clean: go clean -i ./... rm -rf *.out + rm parser.go diff --git a/README.md b/README.md index 495e286f8..badb3c6f7 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,53 @@ # parser + TiDB SQL Parser + +## How to update parser for TiDB + +Assuming that you want to file a PR (pull request) to TiDB, and your PR includes a change in the parser, follow these steps to update the parser in TiDB. + +### Step 1: Make changes in your parser repository + +Fork this repository to your own account and commit the changes to your repository. +> **Note:** +> +> - Don't forget to run `make test` before you commit! +> - Make sure `parser.go` is updated. + +Suppose the forked repository is `https://github.com/your-repo/parser`. + +### Step 2: Make your parser changes take effect in TiDB and run CI + +1. In your TiDB repository, modify the `go.mod` file, remove `github.com/pingcap/parser` from the `require` instruction, and add a new line at the end of the file like this: + +``` +replace github.com/pingcap/parser => github.com/your-repo/parser v0.0.0-20181102150703-4acd198f5092 +``` + +This change tells TiDB to use the modified parser from your repository. + +2. You can get correct version information by running this command in your TiDB directory: + +``` +GO111MODULE=on go get -u github.com/your-repo/parser@master +``` + +If some error is reported, you can ignore it and still edit the `go.mod` file manually. + +3. File a PR to TiDB. + +### Step 3: Merge the PR about the parser to this repository + +File a PR to this repository. **Link the related PR in TiDB in your PR description or comment.** + +This PR will be reviewed, and if everything goes well, it will be merged. + +### Step 4: Update TiDB to use the latest parser + +In your TiDB pull request, modify the `go.mod` file manually or use this command: + +``` +GO111MODULE=on go get -u github.com/pingcap/parser@master +``` + +Make sure the `replace` instruction is changed back to the `require` instruction and the version is the latest. diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000..118a417f3 --- /dev/null +++ b/circle.yml @@ -0,0 +1,12 @@ +version: 2 + +jobs: + build: + docker: + - image: golang:1.11 + working_directory: /go/src/github.com/pingcap/parser + steps: + - checkout + - run: + name: "Build & Test" + command: make test diff --git a/go.mod b/go.mod1 similarity index 91% rename from go.mod rename to go.mod1 index 621567678..964020e71 100644 --- a/go.mod +++ b/go.mod1 @@ -8,7 +8,7 @@ require ( github.com/cznic/y v0.0.0-20170802143616-045f81c6662a github.com/pingcap/check v0.0.0-20171206051426-1c287c953996 github.com/pingcap/errors v0.11.0 - github.com/pingcap/tidb v0.0.0-20181105182855-379ee5b1915a + github.com/pingcap/tidb v0.0.0-20181106092750-bb6d0a935d70 github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323 github.com/sirupsen/logrus v1.2.0 golang.org/x/net v0.0.0-20181029044818-c44066c5c816 diff --git a/go.sum b/go.sum1 similarity index 98% rename from go.sum rename to go.sum1 index 619f276a4..6f3974c27 100644 --- a/go.sum +++ b/go.sum1 @@ -139,6 +139,10 @@ github.com/pingcap/pd v2.1.0-rc.4+incompatible h1:/buwGk04aHO5odk/+O8ZOXGs4qkUjY github.com/pingcap/pd v2.1.0-rc.4+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E= github.com/pingcap/tidb v0.0.0-20181105182855-379ee5b1915a h1:Qd8qbDnsmAIXxefGBgFrWh4y0GDO6froUNFqZYmC568= github.com/pingcap/tidb v0.0.0-20181105182855-379ee5b1915a/go.mod h1:tq1TVnaDUrh46KbB+oJA34Ob3eMbinTopWVzhX5Rj94= +github.com/pingcap/tidb v0.0.0-20181106092750-bb6d0a935d70 h1:a71Zzbf3hautypbfreDgnT+NWtTTJATGGcssArxl/WQ= +github.com/pingcap/tidb v0.0.0-20181106092750-bb6d0a935d70/go.mod h1:tq1TVnaDUrh46KbB+oJA34Ob3eMbinTopWVzhX5Rj94= +github.com/pingcap/tidb v2.0.8+incompatible h1:4G85C71eFTQRJ0Icwul/z3gJfR0u0aWXq1t/f4O8R40= +github.com/pingcap/tidb v2.0.8+incompatible/go.mod h1:I8C6jrPINP2rrVunTRd7C9fRRhQrtR43S1/CL5ix/yQ= github.com/pingcap/tidb-tools v0.0.0-20181101090416-cfac1096162e h1:LKGiK9RwOntq4kniQdGM9q1Cg4AGeIyHBeiFc2OIlpo= github.com/pingcap/tidb-tools v0.0.0-20181101090416-cfac1096162e/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM= github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323 h1:mRKKzRjDNaUNPnAkPAHnRqpNmwNWBX1iA+hxlmvQ93I= diff --git a/mysql/const_test.go b/mysql/const_test.go deleted file mode 100644 index 11c61157b..000000000 --- a/mysql/const_test.go +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright 2017 PingCAP, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// See the License for the specific language governing permissions and -// limitations under the License. - -package mysql_test - -import ( - "flag" - "testing" - - . "github.com/pingcap/check" - "github.com/pingcap/parser" - "github.com/pingcap/parser/mysql" - "github.com/pingcap/tidb/domain" - "github.com/pingcap/tidb/kv" - "github.com/pingcap/tidb/session" - "github.com/pingcap/tidb/store/mockstore" - "github.com/pingcap/tidb/store/mockstore/mocktikv" - "github.com/pingcap/tidb/util/testkit" - "github.com/pingcap/tidb/util/testleak" - "golang.org/x/net/context" -) - -func TestT(t *testing.T) { - CustomVerboseFlag = true - TestingT(t) -} - -var _ = Suite(&testMySQLConstSuite{}) - -type testMySQLConstSuite struct { - cluster *mocktikv.Cluster - mvccStore mocktikv.MVCCStore - store kv.Storage - dom *domain.Domain - *parser.Parser -} - -var mockTikv = flag.Bool("mockTikv", true, "use mock tikv store in executor test") - -func (s *testMySQLConstSuite) SetUpSuite(c *C) { - s.Parser = parser.New() - flag.Lookup("mockTikv") - useMockTikv := *mockTikv - if useMockTikv { - s.cluster = mocktikv.NewCluster() - mocktikv.BootstrapWithSingleStore(s.cluster) - s.mvccStore = mocktikv.MustNewMVCCStore() - store, err := mockstore.NewMockTikvStore( - mockstore.WithCluster(s.cluster), - mockstore.WithMVCCStore(s.mvccStore), - ) - c.Assert(err, IsNil) - s.store = store - session.SetSchemaLease(0) - session.SetStatsLease(0) - } - var err error - s.dom, err = session.BootstrapSession(s.store) - c.Assert(err, IsNil) -} - -func (s *testMySQLConstSuite) TearDownSuite(c *C) { - s.dom.Close() - s.store.Close() - testleak.AfterTest(c)() -} - -func (s *testMySQLConstSuite) TestGetSQLMode(c *C) { - positiveCases := []struct { - arg string - }{ - {"NO_ZERO_DATE"}, - {",,NO_ZERO_DATE"}, - {"NO_ZERO_DATE,NO_ZERO_IN_DATE"}, - {""}, - {", "}, - {","}, - } - - for _, t := range positiveCases { - _, err := mysql.GetSQLMode(mysql.FormatSQLModeStr(t.arg)) - c.Assert(err, IsNil) - } - - negativeCases := []struct { - arg string - }{ - {"NO_ZERO_DATE, NO_ZERO_IN_DATE"}, - {"NO_ZERO_DATE,adfadsdfasdfads"}, - {", ,NO_ZERO_DATE"}, - {" ,"}, - } - - for _, t := range negativeCases { - _, err := mysql.GetSQLMode(mysql.FormatSQLModeStr(t.arg)) - c.Assert(err, NotNil) - } -} - -func (s *testMySQLConstSuite) TestSQLMode(c *C) { - tests := []struct { - arg string - hasNoZeroDateMode bool - hasNoZeroInDateMode bool - hasErrorForDivisionByZeroMode bool - }{ - {"NO_ZERO_DATE", true, false, false}, - {"NO_ZERO_IN_DATE", false, true, false}, - {"ERROR_FOR_DIVISION_BY_ZERO", false, false, true}, - {"NO_ZERO_IN_DATE,NO_ZERO_DATE", true, true, false}, - {"NO_ZERO_DATE,NO_ZERO_IN_DATE", true, true, false}, - {"NO_ZERO_DATE,NO_ZERO_IN_DATE", true, true, false}, - {"NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO", true, true, true}, - {"NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO", false, true, true}, - {"", false, false, false}, - } - - for _, t := range tests { - sqlMode, _ := mysql.GetSQLMode(t.arg) - c.Assert(sqlMode.HasNoZeroDateMode(), Equals, t.hasNoZeroDateMode) - c.Assert(sqlMode.HasNoZeroInDateMode(), Equals, t.hasNoZeroInDateMode) - c.Assert(sqlMode.HasErrorForDivisionByZeroMode(), Equals, t.hasErrorForDivisionByZeroMode) - } -} - -func (s *testMySQLConstSuite) TestRealAsFloatMode(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t;") - tk.MustExec("create table t (a real);") - result := tk.MustQuery("desc t") - c.Check(result.Rows(), HasLen, 1) - row := result.Rows()[0] - c.Assert(row[1], Equals, "double") - - tk.MustExec("drop table if exists t;") - tk.MustExec("set sql_mode='REAL_AS_FLOAT'") - tk.MustExec("create table t (a real)") - result = tk.MustQuery("desc t") - c.Check(result.Rows(), HasLen, 1) - row = result.Rows()[0] - c.Assert(row[1], Equals, "float") -} - -func (s *testMySQLConstSuite) TestPipesAsConcatMode(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("SET sql_mode='PIPES_AS_CONCAT';") - r := tk.MustQuery(`SELECT 'hello' || 'world';`) - r.Check(testkit.Rows("helloworld")) -} - -func (s *testMySQLConstSuite) TestNoUnsignedSubtractionMode(c *C) { - tk := testkit.NewTestKit(c, s.store) - ctx := context.Background() - tk.MustExec("set sql_mode='NO_UNSIGNED_SUBTRACTION'") - r := tk.MustQuery("SELECT CAST(0 as UNSIGNED) - 1;") - r.Check(testkit.Rows("-1")) - rs, _ := tk.Exec("SELECT CAST(18446744073709551615 as UNSIGNED) - 1;") - _, err := session.GetRows4Test(ctx, tk.Se, rs) - c.Assert(err, NotNil) - c.Assert(rs.Close(), IsNil) - rs, _ = tk.Exec("SELECT 1 - CAST(18446744073709551615 as UNSIGNED);") - _, err = session.GetRows4Test(ctx, tk.Se, rs) - c.Assert(err, NotNil) - c.Assert(rs.Close(), IsNil) - rs, _ = tk.Exec("SELECT CAST(-1 as UNSIGNED) - 1") - _, err = session.GetRows4Test(ctx, tk.Se, rs) - c.Assert(err, NotNil) - c.Assert(rs.Close(), IsNil) - rs, _ = tk.Exec("SELECT CAST(9223372036854775808 as UNSIGNED) - 1") - _, err = session.GetRows4Test(ctx, tk.Se, rs) - c.Assert(err, NotNil) - c.Assert(rs.Close(), IsNil) -} - -func (s *testMySQLConstSuite) TestHighNotPrecedenceMode(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (a int);") - tk.MustExec("insert into t1 values (0),(1),(NULL);") - r := tk.MustQuery(`SELECT * FROM t1 WHERE NOT a BETWEEN 2 AND 3;`) - r.Check(testkit.Rows("0", "1")) - r = tk.MustQuery(`SELECT NOT 1 BETWEEN -5 AND 5;`) - r.Check(testkit.Rows("0")) - tk.MustExec("set sql_mode='high_not_precedence';") - r = tk.MustQuery(`SELECT * FROM t1 WHERE NOT a BETWEEN 2 AND 3;`) - r.Check(testkit.Rows()) - r = tk.MustQuery(`SELECT NOT 1 BETWEEN -5 AND 5;`) - r.Check(testkit.Rows("1")) -} - -func (s *testMySQLConstSuite) TestIgnoreSpaceMode(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - tk.MustExec("set sql_mode=''") - tk.MustExec("CREATE TABLE COUNT (a bigint);") - tk.MustExec("DROP TABLE COUNT;") - tk.MustExec("CREATE TABLE `COUNT` (a bigint);") - tk.MustExec("DROP TABLE COUNT;") - _, err := tk.Exec("CREATE TABLE COUNT(a bigint);") - c.Assert(err, NotNil) - tk.MustExec("CREATE TABLE test.COUNT(a bigint);") - tk.MustExec("DROP TABLE COUNT;") - - tk.MustExec("CREATE TABLE BIT_AND (a bigint);") - tk.MustExec("DROP TABLE BIT_AND;") - tk.MustExec("CREATE TABLE `BIT_AND` (a bigint);") - tk.MustExec("DROP TABLE BIT_AND;") - _, err = tk.Exec("CREATE TABLE BIT_AND(a bigint);") - c.Assert(err, NotNil) - tk.MustExec("CREATE TABLE test.BIT_AND(a bigint);") - tk.MustExec("DROP TABLE BIT_AND;") - - tk.MustExec("CREATE TABLE NOW (a bigint);") - tk.MustExec("DROP TABLE NOW;") - tk.MustExec("CREATE TABLE `NOW` (a bigint);") - tk.MustExec("DROP TABLE NOW;") - _, err = tk.Exec("CREATE TABLE NOW(a bigint);") - c.Assert(err, NotNil) - tk.MustExec("CREATE TABLE test.NOW(a bigint);") - tk.MustExec("DROP TABLE NOW;") - - tk.MustExec("set sql_mode='IGNORE_SPACE'") - _, err = tk.Exec("CREATE TABLE COUNT (a bigint);") - c.Assert(err, NotNil) - tk.MustExec("CREATE TABLE `COUNT` (a bigint);") - tk.MustExec("DROP TABLE COUNT;") - _, err = tk.Exec("CREATE TABLE COUNT(a bigint);") - c.Assert(err, NotNil) - tk.MustExec("CREATE TABLE test.COUNT(a bigint);") - tk.MustExec("DROP TABLE COUNT;") - - _, err = tk.Exec("CREATE TABLE BIT_AND (a bigint);") - c.Assert(err, NotNil) - tk.MustExec("CREATE TABLE `BIT_AND` (a bigint);") - tk.MustExec("DROP TABLE BIT_AND;") - _, err = tk.Exec("CREATE TABLE BIT_AND(a bigint);") - c.Assert(err, NotNil) - tk.MustExec("CREATE TABLE test.BIT_AND(a bigint);") - tk.MustExec("DROP TABLE BIT_AND;") - - _, err = tk.Exec("CREATE TABLE NOW (a bigint);") - c.Assert(err, NotNil) - tk.MustExec("CREATE TABLE `NOW` (a bigint);") - tk.MustExec("DROP TABLE NOW;") - _, err = tk.Exec("CREATE TABLE NOW(a bigint);") - c.Assert(err, NotNil) - tk.MustExec("CREATE TABLE test.NOW(a bigint);") - tk.MustExec("DROP TABLE NOW;") - -} - -func (s *testMySQLConstSuite) TestPadCharToFullLengthMode(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("use test") - // test type `CHAR(n)` - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (a char(10));") - tk.MustExec("insert into t1 values ('xy');") - tk.MustExec("set sql_mode='';") - r := tk.MustQuery(`SELECT a='xy ', char_length(a) FROM t1;`) - r.Check(testkit.Rows("0 2")) - r = tk.MustQuery(`SELECT count(*) FROM t1 WHERE a='xy ';`) - r.Check(testkit.Rows("0")) - tk.MustExec("set sql_mode='PAD_CHAR_TO_FULL_LENGTH';") - r = tk.MustQuery(`SELECT a='xy ', char_length(a) FROM t1;`) - r.Check(testkit.Rows("1 10")) - r = tk.MustQuery(`SELECT count(*) FROM t1 WHERE a='xy ';`) - r.Check(testkit.Rows("1")) - - // test type `VARCHAR(n)` - tk.MustExec("drop table if exists t1") - tk.MustExec("create table t1 (a varchar(10));") - tk.MustExec("insert into t1 values ('xy');") - tk.MustExec("set sql_mode='';") - r = tk.MustQuery(`SELECT a='xy ', char_length(a) FROM t1;`) - r.Check(testkit.Rows("0 2")) - r = tk.MustQuery(`SELECT count(*) FROM t1 WHERE a='xy ';`) - r.Check(testkit.Rows("0")) - tk.MustExec("set sql_mode='PAD_CHAR_TO_FULL_LENGTH';") - r = tk.MustQuery(`SELECT a='xy ', char_length(a) FROM t1;`) - r.Check(testkit.Rows("0 2")) -} - -func (s *testMySQLConstSuite) TestNoBackslashEscapesMode(c *C) { - tk := testkit.NewTestKit(c, s.store) - tk.MustExec("set sql_mode=''") - r := tk.MustQuery("SELECT '\\\\'") - r.Check(testkit.Rows("\\")) - tk.MustExec("set sql_mode='NO_BACKSLASH_ESCAPES'") - r = tk.MustQuery("SELECT '\\\\'") - r.Check(testkit.Rows("\\\\")) -} - -func (s *testMySQLConstSuite) TestServerStatus(c *C) { - tests := []struct { - arg uint16 - IsCursorExists bool - }{ - {0, false}, - {mysql.ServerStatusInTrans | mysql.ServerStatusNoBackslashEscaped, false}, - {mysql.ServerStatusCursorExists, true}, - {mysql.ServerStatusCursorExists | mysql.ServerStatusLastRowSend, true}, - } - - for _, t := range tests { - ret := mysql.HasCursorExistsFlag(t.arg) - c.Assert(ret, Equals, t.IsCursorExists) - } -} diff --git a/test.sh b/test.sh new file mode 100644 index 000000000..37733f4c4 --- /dev/null +++ b/test.sh @@ -0,0 +1,11 @@ +{ + mv go.mod1 go.mod + mv go.sum1 go.sum + GO111MODULE=on go test ./... +} || { + mv go.mod go.mod1 + mv go.sum go.sum1 +} + +mv go.mod go.mod1 +mv go.sum go.sum1