vitess.io/vitess@v0.16.2/examples/demo/demo.go (about) 1 /* 2 Copyright 2020 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "bufio" 21 "context" 22 "encoding/hex" 23 "encoding/json" 24 "flag" 25 "fmt" 26 "net/http" 27 "os" 28 "os/signal" 29 "path" 30 "strings" 31 "syscall" 32 "time" 33 34 "vitess.io/vitess/go/mysql" 35 "vitess.io/vitess/go/sqltypes" 36 "vitess.io/vitess/go/vt/log" 37 vttestpb "vitess.io/vitess/go/vt/proto/vttest" 38 "vitess.io/vitess/go/vt/vttest" 39 ) 40 41 var cluster *vttest.LocalCluster 42 var querylog <-chan string 43 44 func main() { 45 flag.Parse() 46 47 runCluster() 48 querylog, _ = streamQuerylog(cluster.Env.PortForProtocol("vtcombo", "")) 49 50 mux := http.NewServeMux() 51 mux.Handle("/", http.FileServer(http.Dir("./"))) 52 mux.HandleFunc("/exec", exec) 53 go http.ListenAndServe(":8000", mux) 54 55 wait() 56 cluster.TearDown() 57 } 58 59 func runCluster() { 60 cluster = &vttest.LocalCluster{ 61 Config: vttest.Config{ 62 Topology: &vttestpb.VTTestTopology{ 63 Keyspaces: []*vttestpb.Keyspace{{ 64 Name: "customer", 65 Shards: []*vttestpb.Shard{{ 66 Name: "-80", 67 }, { 68 Name: "80-", 69 }}, 70 }, { 71 Name: "product", 72 Shards: []*vttestpb.Shard{{ 73 Name: "0", 74 }}, 75 }}, 76 }, 77 SchemaDir: path.Join(os.Getenv("VTROOT"), "examples/demo/schema"), 78 MySQLBindHost: "0.0.0.0", 79 // VSchemaDDLAuthorizedUsers allows you to experiment with vschema DDLs. 80 VSchemaDDLAuthorizedUsers: "%", 81 }, 82 } 83 env, err := vttest.NewLocalTestEnv("", 12345) 84 if err != nil { 85 log.Exitf("Error: %v", err) 86 } 87 cluster.Env = env 88 err = cluster.Setup() 89 if err != nil { 90 cluster.TearDown() 91 log.Exitf("Error: %v", err) 92 } 93 } 94 95 func wait() { 96 c := make(chan os.Signal, 2) 97 signal.Notify(c, os.Interrupt, syscall.SIGTERM) 98 <-c 99 } 100 101 func exec(w http.ResponseWriter, req *http.Request) { 102 w.Header().Set("Content-Type", "application/json; charset=utf-8") 103 enc := json.NewEncoder(w) 104 enc.SetEscapeHTML(true) 105 106 cp := &mysql.ConnParams{ 107 Host: "127.0.0.1", 108 Port: cluster.Env.PortForProtocol("vtcombo_mysql_port", ""), 109 } 110 conn, err := mysql.Connect(context.Background(), cp) 111 if err != nil { 112 response := map[string]string{ 113 "error": err.Error(), 114 } 115 enc.Encode(response) 116 return 117 } 118 defer conn.Close() 119 query := req.FormValue("query") 120 response := make(map[string]any) 121 122 var queries []string 123 // Clear existing log. 124 for { 125 select { 126 case <-querylog: 127 continue 128 default: 129 } 130 break 131 } 132 execQuery(conn, "result", query, "", "", response) 133 // Collect 134 time.Sleep(250 * time.Millisecond) 135 for { 136 select { 137 case val := <-querylog: 138 queries = append(queries, val) 139 continue 140 default: 141 } 142 break 143 } 144 response["queries"] = queries 145 146 execQuery(conn, "customer0", "select * from customer", "customer", "-80", response) 147 execQuery(conn, "customer1", "select * from customer", "customer", "80-", response) 148 execQuery(conn, "corder0", "select * from corder", "customer", "-80", response) 149 execQuery(conn, "corder1", "select * from corder", "customer", "80-", response) 150 execQuery(conn, "corder_event0", "select * from corder_event", "customer", "-80", response) 151 execQuery(conn, "corder_event1", "select * from corder_event", "customer", "80-", response) 152 execQuery(conn, "oname_keyspace_idx0", "select * from oname_keyspace_idx", "customer", "-80", response) 153 execQuery(conn, "oname_keyspace_idx1", "select * from oname_keyspace_idx", "customer", "80-", response) 154 execQuery(conn, "product", "select * from product", "product", "0", response) 155 execQuery(conn, "customer_seq", "select * from customer_seq", "product", "0", response) 156 execQuery(conn, "corder_keyspace_idx", "select * from corder_keyspace_idx", "product", "0", response) 157 enc.Encode(response) 158 } 159 160 func execQuery(conn *mysql.Conn, key, query, keyspace, shard string, response map[string]any) { 161 if query == "" || query == "undefined" { 162 return 163 } 164 if keyspace != "" { 165 _, err := conn.ExecuteFetch(fmt.Sprintf("use `%v:%v`", keyspace, shard), 10000, true) 166 if err != nil { 167 response[key] = map[string]any{ 168 "title": key, 169 "error": err.Error(), 170 } 171 return 172 } 173 } 174 title := key 175 switch { 176 case strings.HasSuffix(key, "0"): 177 title = key[:len(key)-1] + ":-80" 178 case strings.HasSuffix(key, "1"): 179 title = key[:len(key)-1] + ":80-" 180 } 181 qr, err := conn.ExecuteFetch(query, 10000, true) 182 if err != nil { 183 if strings.Contains(err.Error(), "doesn't exist") { 184 return 185 } 186 response[key] = map[string]any{ 187 "title": title, 188 "error": err.Error(), 189 } 190 log.Errorf("error: %v", err) 191 return 192 } 193 response[key] = resultToMap(title, qr) 194 } 195 196 func resultToMap(title string, qr *sqltypes.Result) map[string]any { 197 fields := make([]string, 0, len(qr.Fields)) 198 for _, field := range qr.Fields { 199 fields = append(fields, field.Name) 200 } 201 rows := make([][]string, 0, len(qr.Rows)) 202 for _, row := range qr.Rows { 203 srow := make([]string, 0, len(row)) 204 for _, value := range row { 205 if value.Type() == sqltypes.VarBinary { 206 bytes, err := value.ToBytes() 207 if err != nil { 208 log.Errorf("Error converting value to bytes: %v", err) 209 return nil 210 } 211 srow = append(srow, hex.EncodeToString(bytes)) 212 } else { 213 srow = append(srow, value.ToString()) 214 } 215 } 216 rows = append(rows, srow) 217 } 218 return map[string]any{ 219 "title": title, 220 "fields": fields, 221 "rows": rows, 222 "rowsaffected": int64(qr.RowsAffected), 223 "insertid": int64(qr.InsertID), 224 } 225 } 226 227 func streamQuerylog(port int) (<-chan string, error) { 228 request := fmt.Sprintf("http://localhost:%d/debug/querylog", port) 229 resp, err := http.Get(request) 230 if err != nil { 231 log.Errorf("Error reading stream: %v: %v", request, err) 232 return nil, err 233 } 234 ch := make(chan string, 100) 235 go func() { 236 buffered := bufio.NewReader(resp.Body) 237 for { 238 str, err := buffered.ReadString('\n') 239 if err != nil { 240 log.Errorf("Error reading stream: %v: %v", request, err) 241 close(ch) 242 resp.Body.Close() 243 return 244 } 245 splits := strings.Split(str, "\t") 246 if len(splits) < 13 { 247 continue 248 } 249 trimmed := strings.Trim(splits[12], `"`) 250 if trimmed == "" { 251 continue 252 } 253 splitQueries := strings.Split(trimmed, ";") 254 var final []string 255 for _, query := range splitQueries { 256 if strings.Contains(query, "1 != 1") { 257 continue 258 } 259 final = append(final, query) 260 } 261 select { 262 case ch <- strings.Join(final, "; "): 263 default: 264 } 265 } 266 }() 267 return ch, nil 268 }