diff --git a/server/http_status.go b/server/http_status.go index 5e73f21943891..8ed50cf7f6609 100644 --- a/server/http_status.go +++ b/server/http_status.go @@ -54,6 +54,9 @@ func (s *Server) startHTTPServer() { router.Handle("/mvcc/txn/{startTS}/{db}/{table}", mvccTxnHandler{tikvHandler, opMvccGetByTxn}) router.Handle("/mvcc/txn/{startTS}", mvccTxnHandler{tikvHandler, opMvccGetByTxn}) router.Handle("/mvcc/hex/{hexKey}", mvccTxnHandler{tikvHandler, opMvccGetByHex}) + router.Handle("/schema", schemaHandler{tikvHandler}) + router.Handle("/schema/{db}", schemaHandler{tikvHandler}) + router.Handle("/schema/{db}/{table}", schemaHandler{tikvHandler}) } addr := fmt.Sprintf(":%d", s.cfg.Status.StatusPort) if s.cfg.Status.StatusPort == 0 { diff --git a/server/region_handler.go b/server/region_handler.go index 79a5de958f849..855d49ee6699d 100644 --- a/server/region_handler.go +++ b/server/region_handler.go @@ -43,13 +43,16 @@ import ( const ( pDBName = "db" - pTableName = "table" - pRegionID = "regionID" + pHexKey = "hexKey" pRecordID = "recordID" + pRegionID = "regionID" pStartTS = "startTS" - pHexKey = "hexKey" + pTableName = "table" ) +// For query string +const qTableID = "table_id" + const ( headerContentType = "Content-Type" contentTypeJSON = "application/json" @@ -72,6 +75,11 @@ type regionHandler struct { *regionHandlerTool } +// schemaHandler is the handler for list database or table schemas. +type schemaHandler struct { + *regionHandler +} + // tableRegionsHandler is the handler for list table's regions. type tableRegionsHandler struct { *regionHandler @@ -227,6 +235,68 @@ func (t *regionHandlerTool) getRegionsMeta(regionIDs []uint64) ([]RegionMeta, er return regions, nil } +// ServeHTTP handles request of list a database or table's schemas. +func (rh schemaHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + schema, err := rh.schema() + if err != nil { + rh.writeError(w, err) + return + } + + // parse params + params := mux.Vars(req) + + if dbName, ok := params[pDBName]; ok { + cDBName := model.NewCIStr(dbName) + if tableName, ok := params[pTableName]; ok { + // table schema of a specified table name + cTableName := model.NewCIStr(tableName) + data, err := schema.TableByName(cDBName, cTableName) + if err != nil { + rh.writeError(w, err) + return + } + rh.writeData(w, data.Meta()) + return + } + // all table schemas in a specified database + if schema.SchemaExists(cDBName) { + tbs := schema.SchemaTables(cDBName) + tbsInfo := make([]*model.TableInfo, len(tbs)) + for i := range tbsInfo { + tbsInfo[i] = tbs[i].Meta() + } + rh.writeData(w, tbsInfo) + return + } + rh.writeError(w, infoschema.ErrDatabaseNotExists.GenByArgs(dbName)) + return + } + + if tableID := req.FormValue(qTableID); len(tableID) > 0 { + // table schema of a specified tableID + tid, err := strconv.Atoi(tableID) + if err != nil { + rh.writeError(w, err) + return + } + if tid < 0 { + rh.writeError(w, infoschema.ErrTableNotExists.Gen("Table which ID = %s does not exist.", tableID)) + return + } + if data, ok := schema.TableByID(int64(tid)); ok { + rh.writeData(w, data.Meta()) + return + } + rh.writeError(w, infoschema.ErrTableNotExists.Gen("Table which ID = %s does not exist.", tableID)) + return + } + + // all databases' schemas + rh.writeData(w, schema.AllSchemas()) + return +} + // ServeHTTP handles request of list a table's regions. func (rh tableRegionsHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // parse params diff --git a/server/region_handler_test.go b/server/region_handler_test.go index 153f15ef853d4..e17e966b74c22 100644 --- a/server/region_handler_test.go +++ b/server/region_handler_test.go @@ -21,11 +21,13 @@ import ( "fmt" "math" "net/http" + "sort" . "github.com/pingcap/check" "github.com/pingcap/kvproto/pkg/kvrpcpb" "github.com/pingcap/tidb" "github.com/pingcap/tidb/config" + "github.com/pingcap/tidb/model" "github.com/pingcap/tidb/store/tikv" "github.com/pingcap/tidb/store/tikv/mock-tikv" "github.com/pingcap/tidb/tablecodec" @@ -302,3 +304,60 @@ func (ts *TidbRegionHandlerTestSuite) TestGetMvccNotFound(c *C) { c.Assert(err, IsNil) c.Assert(p.Info, IsNil) } + +func (ts *TidbRegionHandlerTestSuite) TestGetSchema(c *C) { + ts.startServer(c) + ts.prepareData(c) + defer ts.stopServer(c) + resp, err := http.Get(fmt.Sprintf("http://127.0.0.1:10090/schema")) + c.Assert(err, IsNil) + decoder := json.NewDecoder(resp.Body) + var dbs []*model.DBInfo + err = decoder.Decode(&dbs) + c.Assert(err, IsNil) + expects := []string{"information_schema", "mysql", "performance_schema", "test", "tidb"} + names := make([]string, len(dbs)) + for i, v := range dbs { + names[i] = v.Name.L + } + sort.Strings(names) + c.Assert(names, DeepEquals, expects) + + resp, err = http.Get(fmt.Sprintf("http://127.0.0.1:10090/schema?table_id=5")) + c.Assert(err, IsNil) + var t *model.TableInfo + decoder = json.NewDecoder(resp.Body) + err = decoder.Decode(&t) + c.Assert(err, IsNil) + c.Assert(t.Name.L, Equals, "user") + + _, err = http.Get(fmt.Sprintf("http://127.0.0.1:10090/schema?table_id=a")) + c.Assert(err, IsNil) + + _, err = http.Get(fmt.Sprintf("http://127.0.0.1:10090/schema?table_id=1")) + c.Assert(err, IsNil) + + _, err = http.Get(fmt.Sprintf("http://127.0.0.1:10090/schema?table_id=-1")) + c.Assert(err, IsNil) + + resp, err = http.Get(fmt.Sprintf("http://127.0.0.1:10090/schema/tidb")) + c.Assert(err, IsNil) + var lt []*model.TableInfo + decoder = json.NewDecoder(resp.Body) + err = decoder.Decode(<) + c.Assert(err, IsNil) + c.Assert(lt[0].Name.L, Equals, "test") + + _, err = http.Get(fmt.Sprintf("http://127.0.0.1:10090/schema/abc")) + c.Assert(err, IsNil) + + resp, err = http.Get(fmt.Sprintf("http://127.0.0.1:10090/schema/tidb/test")) + c.Assert(err, IsNil) + decoder = json.NewDecoder(resp.Body) + err = decoder.Decode(&t) + c.Assert(err, IsNil) + c.Assert(t.Name.L, Equals, "test") + + _, err = http.Get(fmt.Sprintf("http://127.0.0.1:10090/schema/tidb/abc")) + c.Assert(err, IsNil) +}