{"action":"created","comment":{"url":"https://api.github.com/repos/pingcap/tidb-tools/pulls/comments/192722370","pull_request_review_id":125547138,"id":192722370,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDE5MjcyMjM3MA==","diff_hunk":"@@ -0,0 +1,534 @@\n+// Copyright 2018 PingCAP, Inc.\n+//\n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+//\n+//     http://www.apache.org/licenses/LICENSE-2.0\n+//\n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+package main\n+\n+import (\n+\t\"database/sql\"\n+\t\"fmt\"\n+\t\"math/rand\"\n+\t\"os\"\n+\t\"strconv\"\n+\t\"strings\"\n+\t\"sync\"\n+\n+\t\"github.com/juju/errors\"\n+\t\"github.com/ngaut/log\"\n+\t\"github.com/pingcap/tidb-tools/pkg/db\"\n+\t\"github.com/pingcap/tidb-tools/diff/util\"\n+\t\"github.com/pingcap/tidb/model\"\n+\t\"github.com/pingcap/tidb/types\"\n+)\n+\n+// Diff contains two sql DB, used for comparing.\n+type Diff struct {\n+\tdb1              *sql.DB\n+\tdb2              *sql.DB\n+\tdbName           string\n+\tchunkSize        int\n+\tsample           int\n+\tcheckThreadCount int\n+\tuseRowID         bool\n+\ttables           []*TableCheckCfg\n+\tfixSQLFile       *os.File\n+\tsqlCh            chan string\n+\twg               sync.WaitGroup\n+}\n+\n+// NewDiff returns a Diff instance.\n+func NewDiff(db1, db2 *sql.DB, cfg *Config) (diff *Diff, err error) {\n+\tdiff = &Diff{\n+\t\tdb1:              db1,\n+\t\tdb2:              db2,\n+\t\tdbName:           cfg.SourceDBCfg.Name,\n+\t\tchunkSize:        cfg.ChunkSize,\n+\t\tsample:           cfg.Sample,\n+\t\tcheckThreadCount: cfg.CheckThreadCount,\n+\t\tuseRowID:         cfg.UseRowID,\n+\t\ttables:           cfg.Tables,\n+\t\tsqlCh:            make(chan string),\n+\t}\n+\tfor _, table := range diff.tables {\n+\t\ttable.Info, err = pkgdb.GetTableInfoWithRowID(diff.db1, diff.dbName, table.Name, cfg.UseRowID)\n+\t\tif err != nil {\n+\t\t\treturn nil, errors.Trace(err)\n+\t\t}\n+\t\ttable.Schema = diff.dbName\n+\t}\n+\tdiff.fixSQLFile, err = os.Create(cfg.FixSQLFile)\n+\tif err != nil {\n+\t\treturn nil, errors.Trace(err)\n+\t}\n+\n+\treturn diff, nil\n+}\n+\n+// Equal tests whether two database have same data and schema.\n+func (df *Diff) Equal() (equal bool, err error) {\n+\tdefer func() {\n+\t\tdf.fixSQLFile.Close()\n+\t\tdf.db1.Close()\n+\t\tdf.db2.Close()\n+\t}()\n+\n+\tdf.wg.Add(1)\n+\tgo df.WriteSqls()\n+\n+\tequal = true\n+\ttbls1, err := pkgdb.GetTables(df.db1, df.dbName)\n+\tif err != nil {\n+\t\terr = errors.Trace(err)\n+\t\treturn\n+\t}\n+\n+\ttbls2, err := pkgdb.GetTables(df.db2, df.dbName)\n+\tif err != nil {\n+\t\terr = errors.Trace(err)\n+\t\treturn\n+\t}\n+\n+\teq := equalStrings(tbls1, tbls2)\n+\t// len(df.tables) == 0 means check all tables\n+\tif !eq && len(df.tables) == 0 {\n+\t\tlog.Errorf(\"show tables get different table. [source db tables] %v [target db tables] %v\", tbls1, tbls2)\n+\t\tequal = false\n+\t}\n+\n+\tif len(df.tables) == 0 {\n+\t\tdf.tables = make([]*TableCheckCfg, 0, len(tbls1))\n+\t\tfor _, name := range tbls1 {\n+\t\t\ttable := &TableCheckCfg{Name: name, Schema: df.dbName}\n+\t\t\ttable.Info, err = pkgdb.GetTableInfoWithRowID(df.db1, df.dbName, name, df.useRowID)\n+\t\t\tif err != nil {\n+\t\t\t\treturn false, errors.Trace(err)\n+\t\t\t}\n+\n+\t\t\tdf.tables = append(df.tables, table)\n+\t\t}\n+\t}\n+\n+\tfor _, table := range df.tables {\n+\t\ttableInfo1 := table.Info\n+\t\ttableInfo2, err := pkgdb.GetTableInfoWithRowID(df.db2, df.dbName, table.Name, df.useRowID)\n+\t\tif err != nil {\n+\t\t\treturn false, errors.Trace(err)\n+\t\t}\n+\t\teq, err = df.EqualTableStruct(tableInfo1, tableInfo2)\n+\t\tif err != nil {\n+\t\t\treturn equal, errors.Trace(err)\n+\t\t}\n+\t\tif !eq {\n+\t\t\tlog.Errorf(\"table have different index: %s\\n\", table.Name)\n+\t\t\tequal = false\n+\t\t}\n+\n+\t\teq, err = df.EqualTableData(table)\n+\t\tif err != nil {\n+\t\t\tlog.Errorf(\"equal table error %v\", err)\n+\t\t}\n+\t\tif !eq {\n+\t\t\tequal = false\n+\t\t\tlog.Errorf(\"table %s's data is not equal\", table.Name)\n+\t\t}\n+\t}\n+\n+\tdf.sqlCh <- \"end\"\n+\tdf.wg.Wait()\n+\treturn\n+}\n+\n+// EqualTableStruct tests whether two table's struct are same.\n+func (df *Diff) EqualTableStruct(tableInfo1, tableInfo2 *model.TableInfo) (bool, error) {\n+\t// check columns\n+\tif len(tableInfo1.Columns) != len(tableInfo2.Columns) {\n+\t\treturn false, nil\n+\t}\n+\n+\tfor j, col := range tableInfo1.Columns {\n+\t\tif col.Name.O != tableInfo2.Columns[j].Name.O {\n+\t\t\treturn false, nil\n+\t\t}\n+\t\tif col.Tp != tableInfo2.Columns[j].Tp {\n+\t\t\treturn false, nil\n+\t\t}\n+\t}\n+\n+\t// check index\n+\tif len(tableInfo1.Indices) != len(tableInfo2.Indices) {\n+\t\treturn false, nil\n+\t}\n+\n+\tfor i, index := range tableInfo1.Indices {\n+\t\tindex2 := tableInfo2.Indices[i]\n+\t\tif index.Name.O != index2.Name.O {\n+\t\t\treturn false, nil\n+\t\t}\n+\t\tif len(index.Columns) != len(index2.Columns) {\n+\t\t\treturn false, nil\n+\t\t}\n+\t\tfor j, col := range index.Columns {\n+\t\t\tif col.Name.O != index2.Columns[j].Name.O {\n+\t\t\t\treturn false, nil\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn true, nil\n+}\n+\n+// EqualTableData checks data is equal or not.\n+func (df *Diff) EqualTableData(table *TableCheckCfg) (bool, error) {\n+\t// TODO: now only check data between source data's min and max, need check data less than min and greater than max.\n+\tdumpJobs, err := util.GenerateDumpJob(df.db1, df.dbName, table.Info, table.Field, table.Range, df.chunkSize, df.sample, df.useRowID)\n+\tif err != nil {\n+\t\treturn false, errors.Trace(err)\n+\t}\n+\n+\tcheckNums := len(dumpJobs)*df.sample/100 + 1\n+\tcheckNumArr := getRandomN(len(dumpJobs), checkNums)\n+\tlog.Infof(\"total has %d check jobs, check %+v\", len(dumpJobs), checkNumArr)\n+\n+\tcheckResultCh := make(chan bool, df.checkThreadCount)\n+\tdefer close(checkResultCh)\n+\n+\tfor i := 0; i < df.checkThreadCount; i++ {\n+\t\tcheckJobs := make([]*util.DumpJob, 0, len(checkNumArr))\n+\t\tfor j := len(checkNumArr) * i / df.checkThreadCount; j < len(checkNumArr)*(i+1)/df.checkThreadCount && j < len(dumpJobs); j++ {\n+\t\t\tcheckJobs = append(checkJobs, dumpJobs[j])\n+\t\t}\n+\t\tgo func() {\n+\t\t\teq, err := df.checkChunkDataEqual(checkJobs, table)\n+\t\t\tif err != nil {\n+\t\t\t\tlog.Errorf(\"check chunk data equal failed, error %v\", err)\n+\t\t\t}\n+\t\t\tcheckResultCh <- eq\n+\t\t}()\n+\t}\n+\n+\tnum := 0\n+\tequal := true\n+\n+CheckResult:\n+\tfor {\n+\t\tselect {\n+\t\tcase eq := <-checkResultCh:\n+\t\t\tnum++\n+\t\t\tif !eq {\n+\t\t\t\tequal = false\n+\t\t\t}\n+\t\t\tif num == df.checkThreadCount {\n+\t\t\t\tbreak CheckResult\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn equal, nil\n+}\n+\n+func (df *Diff) checkChunkDataEqual(dumpJobs []*util.DumpJob, table *TableCheckCfg) (bool, error) {\n+\tequal := true\n+\tif len(dumpJobs) == 0 {\n+\t\treturn true, nil\n+\t}\n+\n+\tfor _, job := range dumpJobs {\n+\t\t// first check the checksum is equal or not\n+\t\tchecksum1, err := pkgdb.GetCRC32Checksum(df.db1, df.dbName, table.Info, job.Where)\n+\t\tif err != nil {\n+\t\t\treturn false, errors.Trace(err)\n+\t\t}\n+\n+\t\tchecksum2, err := pkgdb.GetCRC32Checksum(df.db2, df.dbName, table.Info, job.Where)\n+\t\tif err != nil {\n+\t\t\treturn false, errors.Trace(err)\n+\t\t}\n+\t\tif checksum1 == checksum2 {\n+\t\t\tlog.Infof(\"table: %s, range: %s checksum is equal, checksum: %s\", job.Table, job.Where, checksum1)\n+\t\t\tcontinue\n+\t\t}\n+\n+\t\t// if checksum is not equal, compare the data\n+\t\tlog.Errorf(\"table: %s, range: %s checksum is not equal\", job.Table, job.Where)\n+\t\trows1, orderKeyCols, err := getChunkRows(df.db1, df.dbName, table, job.Where, df.useRowID)\n+\t\tif err != nil {\n+\t\t\treturn false, errors.Trace(err)\n+\t\t}\n+\t\tdefer rows1.Close()\n+\n+\t\trows2, _, err := getChunkRows(df.db2, df.dbName, table, job.Where, df.useRowID)\n+\t\tif err != nil {\n+\t\t\treturn false, errors.Trace(err)\n+\t\t}\n+\t\tdefer rows2.Close()\n+\n+\t\teq, err := df.compareRows(rows1, rows2, orderKeyCols, table)\n+\t\tif err != nil {\n+\t\t\treturn false, errors.Trace(err)\n+\t\t}\n+\t\tif !eq {\n+\t\t\tequal = false\n+\t\t}\n+\t}\n+\n+\treturn equal, nil\n+}\n+\n+func (df *Diff) compareRows(rows1, rows2 *sql.Rows, orderKeyCols []*model.ColumnInfo, table *TableCheckCfg) (bool, error) {\n+\tequal := true\n+\trowsData1 := make([]map[string][]byte, 0, 100)\n+\trowsData2 := make([]map[string][]byte, 0, 100)\n+\n+\tfor rows1.Next() {\n+\t\tdata1, err := pkgdb.ScanRow(rows1)\n+\t\tif err != nil {\n+\t\t\treturn false, errors.Trace(err)\n+\t\t}\n+\t\trowsData1 = append(rowsData1, data1)\n+\t}\n+\tfor rows2.Next() {\n+\t\tdata2, err := pkgdb.ScanRow(rows2)\n+\t\tif err != nil {\n+\t\t\treturn false, errors.Trace(err)\n+\t\t}\n+\t\trowsData2 = append(rowsData2, data2)\n+\t}\n+\n+\tvar index1, index2 int\n+\tfor {\n+\t\tif index1 == len(rowsData1) {\n+\t\t\t// all the rowsData2's data should be deleted\n+\t\t\tfor ; index2 < len(rowsData2); index2++ {\n+\t\t\t\tsql := generateDML(\"delete\", rowsData2[index2], orderKeyCols, table.Info, table.Schema)\n+\t\t\t\tlog.Infof(\"[delete] sql: %v\", sql)\n+\t\t\t\tdf.wg.Add(1)\n+\t\t\t\tdf.sqlCh <- sql\n+\t\t\t\tequal = false\n+\t\t\t}\n+\t\t\tbreak\n+\t\t}\n+\t\tif index2 == len(rowsData2) {\n+\t\t\t// rosData2 lack some data, should insert them\n+\t\t\tfor ; index1 < len(rowsData1); index1++ {\n+\t\t\t\tsql := generateDML(\"replace\", rowsData1[index1], orderKeyCols, table.Info, table.Schema)\n+\t\t\t\tlog.Infof(\"[insert] sql: %v\", sql)\n+\t\t\t\tdf.wg.Add(1)\n+\t\t\t\tdf.sqlCh <- sql\n+\t\t\t\tequal = false\n+\t\t\t}\n+\t\t\tbreak\n+\t\t}\n+\t\teq, cmp, err := compareData(rowsData1[index1], rowsData2[index2], orderKeyCols)\n+\t\tif err != nil {\n+\t\t\treturn false, errors.Trace(err)\n+\t\t}\n+\t\tif eq {\n+\t\t\tindex1++\n+\t\t\tindex2++\n+\t\t\tcontinue\n+\t\t}\n+\t\tequal = false\n+\t\tswitch cmp {\n+\t\tcase 1:\n+\t\t\t// delete\n+\t\t\tsql := generateDML(\"delete\", rowsData2[index2], orderKeyCols, table.Info, table.Schema)\n+\t\t\tlog.Infof(\"[delete] sql: %v\", sql)\n+\t\t\tdf.wg.Add(1)\n+\t\t\tdf.sqlCh <- sql\n+\t\t\tindex2++\n+\t\tcase -1:\n+\t\t\t// insert\n+\t\t\tsql := generateDML(\"replace\", rowsData1[index1], orderKeyCols, table.Info, table.Schema)\n+\t\t\tlog.Infof(\"[insert] sql: %v\", sql)\n+\t\t\tdf.wg.Add(1)\n+\t\t\tdf.sqlCh <- sql\n+\t\t\tindex1++\n+\t\tcase 0:\n+\t\t\t// update\n+\t\t\tdeleteSQL := generateDML(\"delete\", rowsData2[index2], orderKeyCols, table.Info, table.Schema)\n+\t\t\treplaceSQL := generateDML(\"replace\", rowsData1[index1], orderKeyCols, table.Info, table.Schema)\n+\t\t\tlog.Infof(\"[update] delete: %s,\\n replace: %s\", deleteSQL, replaceSQL)\n+\t\t\tdf.wg.Add(2)\n+\t\t\tdf.sqlCh <- deleteSQL\n+\t\t\tdf.sqlCh <- replaceSQL\n+\t\t\tindex1++\n+\t\t\tindex2++\n+\t\t}\n+\t}\n+\n+\treturn equal, nil\n+}\n+\n+// WriteSqls write sqls to file\n+func (df *Diff) WriteSqls() {\n+\tdefer df.wg.Done()\n+\tfor {\n+\t\tselect {\n+\t\tcase dml, ok := <-df.sqlCh:\n+\t\t\tif !ok || dml == \"end\" {\n+\t\t\t\treturn\n+\t\t\t}\n+\n+\t\t\t_, err := df.fixSQLFile.WriteString(fmt.Sprintf(\"%s\\n\", dml))\n+\t\t\tif err != nil {\n+\t\t\t\tlog.Errorf(\"write sql: %s failed, error: %v\", dml, err)\n+\t\t\t}\n+\t\t\tdf.wg.Done()\n+\t\tdefault:\n+\t\t}\n+\t}\n+}\n+\n+func generateDML(tp string, data map[string][]byte, keys []*model.ColumnInfo, table *model.TableInfo, dbName string) (sql string) {\n+\t// TODO: can't distinguish NULL between \"\"\n+\tswitch tp {\n+\tcase \"replace\":\n+\t\tcolNames := make([]string, 0, len(table.Columns))\n+\t\tvalues := make([]string, 0, len(table.Columns))\n+\t\tfor _, col := range table.Columns {\n+\t\t\tcolNames = append(colNames, col.Name.O)\n+\t\t\tif needQuotes(col.FieldType) {\n+\t\t\t\tvalues = append(values, fmt.Sprintf(\"\\\"%s\\\"\", string(data[col.Name.O])))\n+\t\t\t} else {\n+\t\t\t\tvalues = append(values, string(data[col.Name.O]))\n+\t\t\t}\n+\t\t}\n+\n+\t\tsql = fmt.Sprintf(\"REPLACE INTO `%s`.`%s`(%s) VALUES (%s);\", dbName, table.Name, strings.Join(colNames, \",\"), strings.Join(values, \",\"))\n+\tcase \"delete\":\n+\t\tkvs := make([]string, 0, len(keys))\n+\t\tfor _, col := range keys {\n+\t\t\tif needQuotes(col.FieldType) {\n+\t\t\t\tkvs = append(kvs, fmt.Sprintf(\"%s = \\\"%s\\\"\", col.Name.O, string(data[col.Name.O])))\n+\t\t\t} else {\n+\t\t\t\tkvs = append(kvs, fmt.Sprintf(\"%s = %s\", col.Name.O, string(data[col.Name.O])))\n+\t\t\t}\n+\t\t}\n+\t\tsql = fmt.Sprintf(\"DELETE FROM `%s`.`%s` where %s;\", dbName, table.Name, strings.Join(kvs, \" AND \"))\n+\tdefault:\n+\t\tlog.Errorf(\"unknow sql type %s\", tp)\n+\t}\n+\n+\treturn\n+}\n+\n+func needQuotes(ft types.FieldType) bool {\n+\treturn !(pkgdb.IsNumberType(ft.Tp) || pkgdb.IsFloatType(ft.Tp))\n+}\n+\n+func compareData(map1 map[string][]byte, map2 map[string][]byte, orderKeyCols []*model.ColumnInfo) (bool, int32, error) {\n+\tvar (\n+\t\tequal        = true\n+\t\tdata1, data2 []byte\n+\t\tkey          string\n+\t\tok           bool\n+\t\tcmp          int32\n+\t)\n+\n+\tfor key, data1 = range map1 {\n+\t\tif data2, ok = map2[key]; !ok {\n+\t\t\treturn false, 0, errors.Errorf(\"don't have key %s\", key)\n+\t\t}\n+\t\tif string(data1) == string(data2) {\n+\t\t\tcontinue\n+\t\t}\n+\t\tequal = false\n+\t\tlog.Errorf(\"find difference data, data1: %s, data2: %s\", map1, map2)\n+\t\tbreak\n+\t}\n+\tif equal {\n+\t\treturn true, 0, nil\n+\t}\n+\n+\tfor _, col := range orderKeyCols {\n+\t\tif data1, ok = map1[col.Name.O]; !ok {\n+\t\t\treturn false, 0, errors.Errorf(\"don't have key %s\", col.Name.O)\n+\t\t}\n+\t\tif data2, ok = map2[col.Name.O]; !ok {\n+\t\t\treturn false, 0, errors.Errorf(\"don't have key %s\", col.Name.O)\n+\t\t}\n+\t\tif needQuotes(col.FieldType) {\n+\t\t\tif string(data1) > string(data2) {","path":"diff/diff.go","position":459,"original_position":459,"commit_id":"85f7ab78b7426a2d35b51574394f9cb98ce906c9","original_commit_id":"85f7ab78b7426a2d35b51574394f9cb98ce906c9","user":{"login":"csuzhangxc","id":3183039,"node_id":"MDQ6VXNlcjMxODMwMzk=","avatar_url":"https://avatars0.githubusercontent.com/u/3183039?v=4","gravatar_id":"","url":"https://api.github.com/users/csuzhangxc","html_url":"https://github.com/csuzhangxc","followers_url":"https://api.github.com/users/csuzhangxc/followers","following_url":"https://api.github.com/users/csuzhangxc/following{/other_user}","gists_url":"https://api.github.com/users/csuzhangxc/gists{/gist_id}","starred_url":"https://api.github.com/users/csuzhangxc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/csuzhangxc/subscriptions","organizations_url":"https://api.github.com/users/csuzhangxc/orgs","repos_url":"https://api.github.com/users/csuzhangxc/repos","events_url":"https://api.github.com/users/csuzhangxc/events{/privacy}","received_events_url":"https://api.github.com/users/csuzhangxc/received_events","type":"User","site_admin":false},"body":"use `bytes.Compare` as above","created_at":"2018-06-04T12:25:00Z","updated_at":"2018-06-04T12:30:32Z","html_url":"https://github.com/pingcap/tidb-tools/pull/17#discussion_r192722370","pull_request_url":"https://api.github.com/repos/pingcap/tidb-tools/pulls/17","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/pingcap/tidb-tools/pulls/comments/192722370"},"html":{"href":"https://github.com/pingcap/tidb-tools/pull/17#discussion_r192722370"},"pull_request":{"href":"https://api.github.com/repos/pingcap/tidb-tools/pulls/17"}}},"pull_request":{"url":"https://api.github.com/repos/pingcap/tidb-tools/pulls/17","id":189216243,"node_id":"MDExOlB1bGxSZXF1ZXN0MTg5MjE2MjQz","html_url":"https://github.com/pingcap/tidb-tools/pull/17","diff_url":"https://github.com/pingcap/tidb-tools/pull/17.diff","patch_url":"https://github.com/pingcap/tidb-tools/pull/17.patch","issue_url":"https://api.github.com/repos/pingcap/tidb-tools/issues/17","number":17,"state":"open","locked":false,"title":"sync_check will generate sql for fix different data","user":{"login":"WangXiangUSTC","id":5793595,"node_id":"MDQ6VXNlcjU3OTM1OTU=","avatar_url":"https://avatars3.githubusercontent.com/u/5793595?v=4","gravatar_id":"","url":"https://api.github.com/users/WangXiangUSTC","html_url":"https://github.com/WangXiangUSTC","followers_url":"https://api.github.com/users/WangXiangUSTC/followers","following_url":"https://api.github.com/users/WangXiangUSTC/following{/other_user}","gists_url":"https://api.github.com/users/WangXiangUSTC/gists{/gist_id}","starred_url":"https://api.github.com/users/WangXiangUSTC/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/WangXiangUSTC/subscriptions","organizations_url":"https://api.github.com/users/WangXiangUSTC/orgs","repos_url":"https://api.github.com/users/WangXiangUSTC/repos","events_url":"https://api.github.com/users/WangXiangUSTC/events{/privacy}","received_events_url":"https://api.github.com/users/WangXiangUSTC/received_events","type":"User","site_admin":false},"body":"","created_at":"2018-05-20T02:46:32Z","updated_at":"2018-06-04T12:30:32Z","closed_at":null,"merged_at":null,"merge_commit_sha":"30b23ae53f8c58ab5b7c7b043df769c40ce91e5b","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/pingcap/tidb-tools/pulls/17/commits","review_comments_url":"https://api.github.com/repos/pingcap/tidb-tools/pulls/17/comments","review_comment_url":"https://api.github.com/repos/pingcap/tidb-tools/pulls/comments{/number}","comments_url":"https://api.github.com/repos/pingcap/tidb-tools/issues/17/comments","statuses_url":"https://api.github.com/repos/pingcap/tidb-tools/statuses/85f7ab78b7426a2d35b51574394f9cb98ce906c9","head":{"label":"WangXiangUSTC:xiang/sync_fix","ref":"xiang/sync_fix","sha":"85f7ab78b7426a2d35b51574394f9cb98ce906c9","user":{"login":"WangXiangUSTC","id":5793595,"node_id":"MDQ6VXNlcjU3OTM1OTU=","avatar_url":"https://avatars3.githubusercontent.com/u/5793595?v=4","gravatar_id":"","url":"https://api.github.com/users/WangXiangUSTC","html_url":"https://github.com/WangXiangUSTC","followers_url":"https://api.github.com/users/WangXiangUSTC/followers","following_url":"https://api.github.com/users/WangXiangUSTC/following{/other_user}","gists_url":"https://api.github.com/users/WangXiangUSTC/gists{/gist_id}","starred_url":"https://api.github.com/users/WangXiangUSTC/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/WangXiangUSTC/subscriptions","organizations_url":"https://api.github.com/users/WangXiangUSTC/orgs","repos_url":"https://api.github.com/users/WangXiangUSTC/repos","events_url":"https://api.github.com/users/WangXiangUSTC/events{/privacy}","received_events_url":"https://api.github.com/users/WangXiangUSTC/received_events","type":"User","site_admin":false},"repo":{"id":110508079,"node_id":"MDEwOlJlcG9zaXRvcnkxMTA1MDgwNzk=","name":"tidb-tools","full_name":"WangXiangUSTC/tidb-tools","owner":{"login":"WangXiangUSTC","id":5793595,"node_id":"MDQ6VXNlcjU3OTM1OTU=","avatar_url":"https://avatars3.githubusercontent.com/u/5793595?v=4","gravatar_id":"","url":"https://api.github.com/users/WangXiangUSTC","html_url":"https://github.com/WangXiangUSTC","followers_url":"https://api.github.com/users/WangXiangUSTC/followers","following_url":"https://api.github.com/users/WangXiangUSTC/following{/other_user}","gists_url":"https://api.github.com/users/WangXiangUSTC/gists{/gist_id}","starred_url":"https://api.github.com/users/WangXiangUSTC/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/WangXiangUSTC/subscriptions","organizations_url":"https://api.github.com/users/WangXiangUSTC/orgs","repos_url":"https://api.github.com/users/WangXiangUSTC/repos","events_url":"https://api.github.com/users/WangXiangUSTC/events{/privacy}","received_events_url":"https://api.github.com/users/WangXiangUSTC/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/WangXiangUSTC/tidb-tools","description":"tidb-tools are some useful tool collections for TiDB.","fork":true,"url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools","forks_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/forks","keys_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/keys{/key_id}","collaborators_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/teams","hooks_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/hooks","issue_events_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/issues/events{/number}","events_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/events","assignees_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/assignees{/user}","branches_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/branches{/branch}","tags_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/tags","blobs_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/git/refs{/sha}","trees_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/git/trees{/sha}","statuses_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/statuses/{sha}","languages_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/languages","stargazers_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/stargazers","contributors_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/contributors","subscribers_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/subscribers","subscription_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/subscription","commits_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/commits{/sha}","git_commits_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/git/commits{/sha}","comments_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/comments{/number}","issue_comment_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/issues/comments{/number}","contents_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/contents/{+path}","compare_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/compare/{base}...{head}","merges_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/merges","archive_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/downloads","issues_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/issues{/number}","pulls_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/pulls{/number}","milestones_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/milestones{/number}","notifications_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/labels{/name}","releases_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/releases{/id}","deployments_url":"https://api.github.com/repos/WangXiangUSTC/tidb-tools/deployments","created_at":"2017-11-13T06:19:17Z","updated_at":"2017-11-13T06:19:19Z","pushed_at":"2018-06-04T08:00:20Z","git_url":"git://github.com/WangXiangUSTC/tidb-tools.git","ssh_url":"git@github.com:WangXiangUSTC/tidb-tools.git","clone_url":"https://github.com/WangXiangUSTC/tidb-tools.git","svn_url":"https://github.com/WangXiangUSTC/tidb-tools","homepage":"","size":5619,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"pingcap:master","ref":"master","sha":"68bb75a37bb9ee471d453dd6b4a738e747f7c747","user":{"login":"pingcap","id":11855343,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExODU1MzQz","avatar_url":"https://avatars3.githubusercontent.com/u/11855343?v=4","gravatar_id":"","url":"https://api.github.com/users/pingcap","html_url":"https://github.com/pingcap","followers_url":"https://api.github.com/users/pingcap/followers","following_url":"https://api.github.com/users/pingcap/following{/other_user}","gists_url":"https://api.github.com/users/pingcap/gists{/gist_id}","starred_url":"https://api.github.com/users/pingcap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pingcap/subscriptions","organizations_url":"https://api.github.com/users/pingcap/orgs","repos_url":"https://api.github.com/users/pingcap/repos","events_url":"https://api.github.com/users/pingcap/events{/privacy}","received_events_url":"https://api.github.com/users/pingcap/received_events","type":"Organization","site_admin":false},"repo":{"id":93518223,"node_id":"MDEwOlJlcG9zaXRvcnk5MzUxODIyMw==","name":"tidb-tools","full_name":"pingcap/tidb-tools","owner":{"login":"pingcap","id":11855343,"node_id":"MDEyOk9yZ2FuaXphdGlvbjExODU1MzQz","avatar_url":"https://avatars3.githubusercontent.com/u/11855343?v=4","gravatar_id":"","url":"https://api.github.com/users/pingcap","html_url":"https://github.com/pingcap","followers_url":"https://api.github.com/users/pingcap/followers","following_url":"https://api.github.com/users/pingcap/following{/other_user}","gists_url":"https://api.github.com/users/pingcap/gists{/gist_id}","starred_url":"https://api.github.com/users/pingcap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pingcap/subscriptions","organizations_url":"https://api.github.com/users/pingcap/orgs","repos_url":"https://api.github.com/users/pingcap/repos","events_url":"https://api.github.com/users/pingcap/events{/privacy}","received_events_url":"https://api.github.com/users/pingcap/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/pingcap/tidb-tools","description":"tidb-tools are some useful tool collections for TiDB.","fork":false,"url":"https://api.github.com/repos/pingcap/tidb-tools","forks_url":"https://api.github.com/repos/pingcap/tidb-tools/forks","keys_url":"https://api.github.com/repos/pingcap/tidb-tools/keys{/key_id}","collaborators_url":"https://api.github.com/repos/pingcap/tidb-tools/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/pingcap/tidb-tools/teams","hooks_url":"https://api.github.com/repos/pingcap/tidb-tools/hooks","issue_events_url":"https://api.github.com/repos/pingcap/tidb-tools/issues/events{/number}","events_url":"https://api.github.com/repos/pingcap/tidb-tools/events","assignees_url":"https://api.github.com/repos/pingcap/tidb-tools/assignees{/user}","branches_url":"https://api.github.com/repos/pingcap/tidb-tools/branches{/branch}","tags_url":"https://api.github.com/repos/pingcap/tidb-tools/tags","blobs_url":"https://api.github.com/repos/pingcap/tidb-tools/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/pingcap/tidb-tools/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/pingcap/tidb-tools/git/refs{/sha}","trees_url":"https://api.github.com/repos/pingcap/tidb-tools/git/trees{/sha}","statuses_url":"https://api.github.com/repos/pingcap/tidb-tools/statuses/{sha}","languages_url":"https://api.github.com/repos/pingcap/tidb-tools/languages","stargazers_url":"https://api.github.com/repos/pingcap/tidb-tools/stargazers","contributors_url":"https://api.github.com/repos/pingcap/tidb-tools/contributors","subscribers_url":"https://api.github.com/repos/pingcap/tidb-tools/subscribers","subscription_url":"https://api.github.com/repos/pingcap/tidb-tools/subscription","commits_url":"https://api.github.com/repos/pingcap/tidb-tools/commits{/sha}","git_commits_url":"https://api.github.com/repos/pingcap/tidb-tools/git/commits{/sha}","comments_url":"https://api.github.com/repos/pingcap/tidb-tools/comments{/number}","issue_comment_url":"https://api.github.com/repos/pingcap/tidb-tools/issues/comments{/number}","contents_url":"https://api.github.com/repos/pingcap/tidb-tools/contents/{+path}","compare_url":"https://api.github.com/repos/pingcap/tidb-tools/compare/{base}...{head}","merges_url":"https://api.github.com/repos/pingcap/tidb-tools/merges","archive_url":"https://api.github.com/repos/pingcap/tidb-tools/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/pingcap/tidb-tools/downloads","issues_url":"https://api.github.com/repos/pingcap/tidb-tools/issues{/number}","pulls_url":"https://api.github.com/repos/pingcap/tidb-tools/pulls{/number}","milestones_url":"https://api.github.com/repos/pingcap/tidb-tools/milestones{/number}","notifications_url":"https://api.github.com/repos/pingcap/tidb-tools/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/pingcap/tidb-tools/labels{/name}","releases_url":"https://api.github.com/repos/pingcap/tidb-tools/releases{/id}","deployments_url":"https://api.github.com/repos/pingcap/tidb-tools/deployments","created_at":"2017-06-06T12:53:46Z","updated_at":"2018-06-01T10:01:56Z","pushed_at":"2018-06-04T10:08:44Z","git_url":"git://github.com/pingcap/tidb-tools.git","ssh_url":"git@github.com:pingcap/tidb-tools.git","clone_url":"https://github.com/pingcap/tidb-tools.git","svn_url":"https://github.com/pingcap/tidb-tools","homepage":"","size":4894,"stargazers_count":26,"watchers_count":26,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":15,"mirror_url":null,"archived":false,"open_issues_count":5,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":15,"open_issues":5,"watchers":26,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/pingcap/tidb-tools/pulls/17"},"html":{"href":"https://github.com/pingcap/tidb-tools/pull/17"},"issue":{"href":"https://api.github.com/repos/pingcap/tidb-tools/issues/17"},"comments":{"href":"https://api.github.com/repos/pingcap/tidb-tools/issues/17/comments"},"review_comments":{"href":"https://api.github.com/repos/pingcap/tidb-tools/pulls/17/comments"},"review_comment":{"href":"https://api.github.com/repos/pingcap/tidb-tools/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/pingcap/tidb-tools/pulls/17/commits"},"statuses":{"href":"https://api.github.com/repos/pingcap/tidb-tools/statuses/85f7ab78b7426a2d35b51574394f9cb98ce906c9"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"csuzhangxc"}}