github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/conn_io_test.go (about) 1 // Copyright 2017 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package sql 12 13 import ( 14 "context" 15 "fmt" 16 "io" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/sql/parser" 20 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 21 ) 22 23 func assertStmt(t *testing.T, cmd Command, exp string) { 24 t.Helper() 25 stmt, ok := cmd.(ExecStmt) 26 if !ok { 27 t.Fatalf("expected ExecStmt, got %T", cmd) 28 } 29 if stmt.AST.String() != exp { 30 t.Fatalf("expected statement %s, got %s", exp, stmt) 31 } 32 } 33 34 func assertPrepareStmt(t *testing.T, cmd Command, expName string) { 35 t.Helper() 36 ps, ok := cmd.(PrepareStmt) 37 if !ok { 38 t.Fatalf("expected PrepareStmt, got %T", cmd) 39 } 40 if ps.Name != expName { 41 t.Fatalf("expected name %s, got %s", expName, ps.Name) 42 } 43 } 44 45 func mustPush(ctx context.Context, t *testing.T, buf *StmtBuf, cmd Command) { 46 t.Helper() 47 if err := buf.Push(ctx, cmd); err != nil { 48 t.Fatalf("%s", err) 49 } 50 } 51 52 func TestStmtBuf(t *testing.T) { 53 defer leaktest.AfterTest(t)() 54 55 ctx := context.Background() 56 s1, err := parser.ParseOne("SELECT 1") 57 if err != nil { 58 t.Fatal(err) 59 } 60 s2, err := parser.ParseOne("SELECT 2") 61 if err != nil { 62 t.Fatal(err) 63 } 64 s3, err := parser.ParseOne("SELECT 3") 65 if err != nil { 66 t.Fatal(err) 67 } 68 s4, err := parser.ParseOne("SELECT 4") 69 if err != nil { 70 t.Fatal(err) 71 } 72 buf := NewStmtBuf() 73 mustPush(ctx, t, buf, ExecStmt{Statement: s1}) 74 mustPush(ctx, t, buf, ExecStmt{Statement: s2}) 75 mustPush(ctx, t, buf, ExecStmt{Statement: s3}) 76 mustPush(ctx, t, buf, ExecStmt{Statement: s4}) 77 78 // Check that, while we don't manually advance the cursor, we keep getting the 79 // same statement. 80 expPos := CmdPos(0) 81 for i := 0; i < 2; i++ { 82 cmd, pos, err := buf.CurCmd() 83 if err != nil { 84 t.Fatal(err) 85 } 86 if pos != expPos { 87 t.Fatalf("expected pos to be %d, got: %d", expPos, pos) 88 } 89 assertStmt(t, cmd, "SELECT 1") 90 } 91 92 buf.AdvanceOne() 93 expPos++ 94 cmd, pos, err := buf.CurCmd() 95 if err != nil { 96 t.Fatal(err) 97 } 98 if pos != expPos { 99 t.Fatalf("expected pos to be %d, got: %d", expPos, pos) 100 } 101 assertStmt(t, cmd, "SELECT 2") 102 103 buf.AdvanceOne() 104 expPos++ 105 cmd, pos, err = buf.CurCmd() 106 if err != nil { 107 t.Fatal(err) 108 } 109 if pos != expPos { 110 t.Fatalf("expected pos to be %d, got: %d", expPos, pos) 111 } 112 assertStmt(t, cmd, "SELECT 3") 113 114 buf.AdvanceOne() 115 expPos++ 116 cmd, pos, err = buf.CurCmd() 117 if err != nil { 118 t.Fatal(err) 119 } 120 if pos != expPos { 121 t.Fatalf("expected pos to be %d, got: %d", expPos, pos) 122 } 123 assertStmt(t, cmd, "SELECT 4") 124 125 // Now rewind. 126 expPos = 1 127 buf.Rewind(ctx, expPos) 128 cmd, pos, err = buf.CurCmd() 129 if err != nil { 130 t.Fatal(err) 131 } 132 if pos != expPos { 133 t.Fatalf("expected pos to be %d, got: %d", expPos, pos) 134 } 135 assertStmt(t, cmd, "SELECT 2") 136 } 137 138 // Test that a reader blocked for an incoming statement is unblocked when that 139 // statement arrives. 140 func TestStmtBufSignal(t *testing.T) { 141 defer leaktest.AfterTest(t)() 142 143 ctx := context.Background() 144 buf := NewStmtBuf() 145 s1, err := parser.ParseOne("SELECT 1") 146 if err != nil { 147 t.Fatal(err) 148 } 149 go func() { 150 _ = buf.Push(ctx, ExecStmt{Statement: s1}) 151 }() 152 153 expPos := CmdPos(0) 154 cmd, pos, err := buf.CurCmd() 155 if err != nil { 156 t.Fatal(err) 157 } 158 if pos != expPos { 159 t.Fatalf("expected pos to be %d, got: %d", expPos, pos) 160 } 161 assertStmt(t, cmd, "SELECT 1") 162 } 163 164 func TestStmtBufLtrim(t *testing.T) { 165 defer leaktest.AfterTest(t)() 166 167 ctx := context.Background() 168 buf := NewStmtBuf() 169 for i := 0; i < 5; i++ { 170 stmt, err := parser.ParseOne( 171 fmt.Sprintf("SELECT %d", i)) 172 if err != nil { 173 t.Fatal(err) 174 } 175 mustPush(ctx, t, buf, ExecStmt{Statement: stmt}) 176 } 177 // Advance the cursor so that we can trim. 178 buf.AdvanceOne() 179 buf.AdvanceOne() 180 trimPos := CmdPos(2) 181 buf.ltrim(ctx, trimPos) 182 if l := buf.mu.data.Len(); l != 3 { 183 t.Fatalf("expected 3 left, got: %d", l) 184 } 185 if s := buf.mu.startPos; s != 2 { 186 t.Fatalf("expected start pos 2, got: %d", s) 187 } 188 } 189 190 // Test that, after Close() is called, buf.CurCmd() returns io.EOF even if 191 // there were commands queued up. 192 func TestStmtBufClose(t *testing.T) { 193 defer leaktest.AfterTest(t)() 194 195 ctx := context.Background() 196 buf := NewStmtBuf() 197 stmt, err := parser.ParseOne("SELECT 1") 198 if err != nil { 199 t.Fatal(err) 200 } 201 mustPush(ctx, t, buf, ExecStmt{Statement: stmt}) 202 buf.Close() 203 204 _, _, err = buf.CurCmd() 205 if err != io.EOF { 206 t.Fatalf("expected EOF, got: %v", err) 207 } 208 } 209 210 // Test that a call to Close() unblocks a CurCmd() call. 211 func TestStmtBufCloseUnblocksReader(t *testing.T) { 212 defer leaktest.AfterTest(t)() 213 214 buf := NewStmtBuf() 215 216 go func() { 217 buf.Close() 218 }() 219 220 _, _, err := buf.CurCmd() 221 if err != io.EOF { 222 t.Fatalf("expected EOF, got: %v", err) 223 } 224 } 225 226 // Test that the buffer can hold and return other kinds of commands intermixed 227 // with ExecStmt. 228 func TestStmtBufPreparedStmt(t *testing.T) { 229 defer leaktest.AfterTest(t)() 230 231 buf := NewStmtBuf() 232 ctx := context.Background() 233 234 s1, err := parser.ParseOne("SELECT 1") 235 if err != nil { 236 t.Fatal(err) 237 } 238 mustPush(ctx, t, buf, ExecStmt{Statement: s1}) 239 mustPush(ctx, t, buf, PrepareStmt{Name: "p1"}) 240 mustPush(ctx, t, buf, PrepareStmt{Name: "p2"}) 241 242 cmd, _, err := buf.CurCmd() 243 if err != nil { 244 t.Fatal(err) 245 } 246 assertStmt(t, cmd, "SELECT 1") 247 248 buf.AdvanceOne() 249 cmd, _, err = buf.CurCmd() 250 if err != nil { 251 t.Fatal(err) 252 } 253 assertPrepareStmt(t, cmd, "p1") 254 255 buf.AdvanceOne() 256 cmd, _, err = buf.CurCmd() 257 if err != nil { 258 t.Fatal(err) 259 } 260 assertPrepareStmt(t, cmd, "p2") 261 262 // Rewind to the first prepared stmt. 263 buf.Rewind(ctx, CmdPos(1)) 264 cmd, _, err = buf.CurCmd() 265 if err != nil { 266 t.Fatal(err) 267 } 268 assertPrepareStmt(t, cmd, "p1") 269 } 270 271 func TestStmtBufBatching(t *testing.T) { 272 defer leaktest.AfterTest(t)() 273 274 buf := NewStmtBuf() 275 ctx := context.Background() 276 277 s1, err := parser.ParseOne("SELECT 1") 278 if err != nil { 279 t.Fatal(err) 280 } 281 282 // Start a new batch. 283 mustPush(ctx, t, buf, Sync{}) 284 285 mustPush(ctx, t, buf, ExecStmt{Statement: s1}) 286 mustPush(ctx, t, buf, ExecStmt{Statement: s1}) 287 288 // Start a new batch. 289 mustPush(ctx, t, buf, Sync{}) 290 291 mustPush(ctx, t, buf, ExecStmt{Statement: s1}) 292 mustPush(ctx, t, buf, ExecStmt{Statement: s1}) 293 mustPush(ctx, t, buf, ExecStmt{Statement: s1}) 294 295 // Start a new batch. 296 mustPush(ctx, t, buf, Sync{}) 297 298 mustPush(ctx, t, buf, ExecStmt{Statement: s1}) 299 300 // Go to 2nd batch. 301 if err := buf.seekToNextBatch(); err != nil { 302 t.Fatal(err) 303 } 304 _, pos, err := buf.CurCmd() 305 if err != nil { 306 t.Fatal(err) 307 } 308 if pos != CmdPos(3) { 309 t.Fatalf("expected pos to be %d, got: %d", 3, pos) 310 } 311 312 // Go to 3rd batch. 313 if err := buf.seekToNextBatch(); err != nil { 314 t.Fatal(err) 315 } 316 _, pos, err = buf.CurCmd() 317 if err != nil { 318 t.Fatal(err) 319 } 320 if pos != CmdPos(7) { 321 t.Fatalf("expected pos to be %d, got: %d", 7, pos) 322 } 323 324 // Async start a 4th batch; that will unblock the seek below. 325 go func() { 326 mustPush(ctx, t, buf, Sync{}) 327 _ = buf.Push(ctx, ExecStmt{Statement: s1}) 328 }() 329 330 // Go to 4th batch. 331 if err := buf.seekToNextBatch(); err != nil { 332 t.Fatal(err) 333 } 334 _, pos, err = buf.CurCmd() 335 if err != nil { 336 t.Fatal(err) 337 } 338 if pos != CmdPos(9) { 339 t.Fatalf("expected pos to be %d, got: %d", 9, pos) 340 } 341 }