github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/cli/cli_test.go (about) 1 // Copyright 2015 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 cli 12 13 import ( 14 "bufio" 15 "bytes" 16 "context" 17 "fmt" 18 "io" 19 "io/ioutil" 20 "net" 21 "os" 22 "path/filepath" 23 "reflect" 24 "regexp" 25 "strconv" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/cockroachdb/cockroach/pkg/base" 31 "github.com/cockroachdb/cockroach/pkg/build" 32 "github.com/cockroachdb/cockroach/pkg/roachpb" 33 "github.com/cockroachdb/cockroach/pkg/security" 34 "github.com/cockroachdb/cockroach/pkg/security/securitytest" 35 "github.com/cockroachdb/cockroach/pkg/server" 36 "github.com/cockroachdb/cockroach/pkg/sql/lex" 37 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 38 "github.com/cockroachdb/cockroach/pkg/testutils" 39 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils" 40 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 41 "github.com/cockroachdb/cockroach/pkg/util/log" 42 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 43 // register some workloads for TestWorkload 44 _ "github.com/cockroachdb/cockroach/pkg/workload/examples" 45 "github.com/cockroachdb/errors" 46 "github.com/stretchr/testify/assert" 47 ) 48 49 type cliTest struct { 50 *server.TestServer 51 certsDir string 52 cleanupFunc func() error 53 54 // t is the testing.T instance used for this test. 55 // Example_xxx tests may have this set to nil. 56 t *testing.T 57 // logScope binds the lifetime of the log files to this test, when t 58 // is not nil 59 logScope *log.TestLogScope 60 // if true, doesn't print args during RunWithArgs 61 omitArgs bool 62 } 63 64 type cliTestParams struct { 65 t *testing.T 66 insecure bool 67 noServer bool 68 storeSpecs []base.StoreSpec 69 locality roachpb.Locality 70 noNodelocal bool 71 } 72 73 func (c *cliTest) fail(err interface{}) { 74 if c.t != nil { 75 defer c.logScope.Close(c.t) 76 c.t.Fatal(err) 77 } else { 78 panic(err) 79 } 80 } 81 82 func createTestCerts(certsDir string) (cleanup func() error) { 83 // Copy these assets to disk from embedded strings, so this test can 84 // run from a standalone binary. 85 // Disable embedded certs, or the security library will try to load 86 // our real files as embedded assets. 87 security.ResetAssetLoader() 88 89 assets := []string{ 90 filepath.Join(security.EmbeddedCertsDir, security.EmbeddedCACert), 91 filepath.Join(security.EmbeddedCertsDir, security.EmbeddedCAKey), 92 filepath.Join(security.EmbeddedCertsDir, security.EmbeddedNodeCert), 93 filepath.Join(security.EmbeddedCertsDir, security.EmbeddedNodeKey), 94 filepath.Join(security.EmbeddedCertsDir, security.EmbeddedRootCert), 95 filepath.Join(security.EmbeddedCertsDir, security.EmbeddedRootKey), 96 } 97 98 for _, a := range assets { 99 securitytest.RestrictedCopy(nil, a, certsDir, filepath.Base(a)) 100 } 101 102 return func() error { 103 security.SetAssetLoader(securitytest.EmbeddedAssets) 104 return os.RemoveAll(certsDir) 105 } 106 } 107 108 func newCLITest(params cliTestParams) cliTest { 109 c := cliTest{t: params.t} 110 111 certsDir, err := ioutil.TempDir("", "cli-test") 112 if err != nil { 113 c.fail(err) 114 } 115 c.certsDir = certsDir 116 117 if c.t != nil { 118 c.logScope = log.Scope(c.t) 119 } 120 121 c.cleanupFunc = func() error { return nil } 122 123 if !params.noServer { 124 if !params.insecure { 125 c.cleanupFunc = createTestCerts(certsDir) 126 baseCfg.SSLCertsDir = certsDir 127 } 128 129 args := base.TestServerArgs{ 130 Insecure: params.insecure, 131 SSLCertsDir: c.certsDir, 132 StoreSpecs: params.storeSpecs, 133 Locality: params.locality, 134 ExternalIODir: filepath.Join(certsDir, "extern"), 135 } 136 if params.noNodelocal { 137 args.ExternalIODir = "" 138 } 139 s, err := serverutils.StartServerRaw(args) 140 if err != nil { 141 c.fail(err) 142 } 143 c.TestServer = s.(*server.TestServer) 144 145 log.Infof(context.Background(), "server started at %s", c.ServingRPCAddr()) 146 log.Infof(context.Background(), "SQL listener at %s", c.ServingSQLAddr()) 147 } 148 149 baseCfg.User = security.NodeUser 150 151 // Ensure that CLI error messages and anything meant for the 152 // original stderr is redirected to stdout, where it can be 153 // captured. 154 stderr = os.Stdout 155 156 return c 157 } 158 159 // setCLIDefaultsForTests invokes initCLIDefaults but pretends the 160 // output is not a terminal, even if it happens to be. This ensures 161 // e.g. that tests ran with -v have the same output as those without. 162 func setCLIDefaultsForTests() { 163 initCLIDefaults() 164 cliCtx.terminalOutput = false 165 sqlCtx.showTimes = false 166 // Even though we pretend there is no terminal, most tests want 167 // pretty tables. 168 cliCtx.tableDisplayFormat = tableDisplayTable 169 } 170 171 // stopServer stops the test server. 172 func (c *cliTest) stopServer() { 173 if c.TestServer != nil { 174 log.Infof(context.Background(), "stopping server at %s / %s", 175 c.ServingRPCAddr(), c.ServingSQLAddr()) 176 select { 177 case <-c.Stopper().ShouldStop(): 178 // If ShouldStop() doesn't block, that means someone has already 179 // called Stop(). We just need to wait. 180 <-c.Stopper().IsStopped() 181 default: 182 c.Stopper().Stop(context.Background()) 183 } 184 } 185 } 186 187 // restartServer stops and restarts the test server. The ServingRPCAddr() may 188 // have changed after this method returns. 189 func (c *cliTest) restartServer(params cliTestParams) { 190 c.stopServer() 191 log.Info(context.Background(), "restarting server") 192 s, err := serverutils.StartServerRaw(base.TestServerArgs{ 193 Insecure: params.insecure, 194 SSLCertsDir: c.certsDir, 195 StoreSpecs: params.storeSpecs, 196 }) 197 if err != nil { 198 c.fail(err) 199 } 200 c.TestServer = s.(*server.TestServer) 201 log.Infof(context.Background(), "restarted server at %s / %s", 202 c.ServingRPCAddr(), c.ServingSQLAddr()) 203 } 204 205 // cleanup cleans up after the test, stopping the server if necessary. 206 // The log files are removed if the test has succeeded. 207 func (c *cliTest) cleanup() { 208 if c.t != nil { 209 defer c.logScope.Close(c.t) 210 } 211 212 // Restore stderr. 213 stderr = log.OrigStderr 214 215 log.Info(context.Background(), "stopping server and cleaning up CLI test") 216 217 c.stopServer() 218 219 if err := c.cleanupFunc(); err != nil { 220 panic(err) 221 } 222 } 223 224 func (c cliTest) Run(line string) { 225 a := strings.Fields(line) 226 c.RunWithArgs(a) 227 } 228 229 // RunWithCapture runs c and returns a string containing the output of c 230 // and any error that may have occurred capturing the output. We do not propagate 231 // errors in executing c, because those will be caught when the test verifies 232 // the output of c. 233 func (c cliTest) RunWithCapture(line string) (out string, err error) { 234 return captureOutput(func() { 235 c.Run(line) 236 }) 237 } 238 239 func (c cliTest) RunWithCaptureArgs(args []string) (string, error) { 240 return captureOutput(func() { 241 c.RunWithArgs(args) 242 }) 243 } 244 245 // captureOutput runs f and returns a string containing the output and any 246 // error that may have occurred capturing the output. 247 func captureOutput(f func()) (out string, err error) { 248 // Heavily inspired by Go's testing/example.go:runExample(). 249 250 // Funnel stdout into a pipe. 251 stdoutSave, stderrRedirSave := os.Stdout, stderr 252 r, w, err := os.Pipe() 253 if err != nil { 254 return "", err 255 } 256 os.Stdout = w 257 stderr = w 258 259 // Send all bytes from piped stdout through the output channel. 260 type captureResult struct { 261 out string 262 err error 263 } 264 outC := make(chan captureResult) 265 go func() { 266 var buf bytes.Buffer 267 _, err := io.Copy(&buf, r) 268 r.Close() 269 outC <- captureResult{buf.String(), err} 270 }() 271 272 // Clean up and record output in separate function to handle panics. 273 defer func() { 274 // Close pipe and restore normal stdout. 275 w.Close() 276 os.Stdout = stdoutSave 277 stderr = stderrRedirSave 278 outResult := <-outC 279 out, err = outResult.out, outResult.err 280 if x := recover(); x != nil { 281 err = errors.Errorf("panic: %v", x) 282 } 283 }() 284 285 // Run the command. The output will be returned in the defer block. 286 f() 287 return 288 } 289 290 func isSQLCommand(args []string) bool { 291 if len(args) == 0 { 292 return false 293 } 294 switch args[0] { 295 case "sql", "dump", "workload", "nodelocal": 296 return true 297 case "node": 298 if len(args) == 0 { 299 return false 300 } 301 switch args[1] { 302 case "status", "ls": 303 return true 304 default: 305 return false 306 } 307 default: 308 return false 309 } 310 } 311 312 func (c cliTest) RunWithArgs(origArgs []string) { 313 TestingReset() 314 315 if err := func() error { 316 args := append([]string(nil), origArgs[:1]...) 317 if c.TestServer != nil { 318 addr := c.ServingRPCAddr() 319 if isSQLCommand(origArgs) { 320 addr = c.ServingSQLAddr() 321 } 322 h, p, err := net.SplitHostPort(addr) 323 if err != nil { 324 return err 325 } 326 args = append(args, fmt.Sprintf("--host=%s", net.JoinHostPort(h, p))) 327 if c.Cfg.Insecure { 328 args = append(args, "--insecure=true") 329 } else { 330 args = append(args, "--insecure=false") 331 args = append(args, fmt.Sprintf("--certs-dir=%s", c.certsDir)) 332 } 333 } 334 args = append(args, origArgs[1:]...) 335 336 if !c.omitArgs { 337 fmt.Fprintf(os.Stderr, "%s\n", args) 338 fmt.Println(strings.Join(origArgs, " ")) 339 } 340 341 return Run(args) 342 }(); err != nil { 343 cliOutputError(os.Stdout, err, true /*showSeverity*/, false /*verbose*/) 344 } 345 } 346 347 func (c cliTest) RunWithCAArgs(origArgs []string) { 348 TestingReset() 349 350 if err := func() error { 351 args := append([]string(nil), origArgs[:1]...) 352 if c.TestServer != nil { 353 args = append(args, fmt.Sprintf("--ca-key=%s", filepath.Join(c.certsDir, security.EmbeddedCAKey))) 354 args = append(args, fmt.Sprintf("--certs-dir=%s", c.certsDir)) 355 } 356 args = append(args, origArgs[1:]...) 357 358 fmt.Fprintf(os.Stderr, "%s\n", args) 359 fmt.Println(strings.Join(origArgs, " ")) 360 361 return Run(args) 362 }(); err != nil { 363 fmt.Println(err) 364 } 365 } 366 367 func TestQuit(t *testing.T) { 368 defer leaktest.AfterTest(t)() 369 370 if testing.Short() { 371 t.Skip("short flag") 372 } 373 374 c := newCLITest(cliTestParams{t: t}) 375 defer c.cleanup() 376 377 c.Run("quit") 378 // Wait until this async command cleanups the server. 379 <-c.Stopper().IsStopped() 380 } 381 382 func Example_logging() { 383 c := newCLITest(cliTestParams{}) 384 defer c.cleanup() 385 386 c.RunWithArgs([]string{`sql`, `--logtostderr=false`, `-e`, `select 1 as "1"`}) 387 c.RunWithArgs([]string{`sql`, `--logtostderr=true`, `-e`, `select 1 as "1"`}) 388 c.RunWithArgs([]string{`sql`, `--vmodule=foo=1`, `-e`, `select 1 as "1"`}) 389 390 // Output: 391 // sql --logtostderr=false -e select 1 as "1" 392 // 1 393 // 1 394 // sql --logtostderr=true -e select 1 as "1" 395 // 1 396 // 1 397 // sql --vmodule=foo=1 -e select 1 as "1" 398 // 1 399 // 1 400 } 401 402 func Example_demo() { 403 c := newCLITest(cliTestParams{noServer: true}) 404 defer c.cleanup() 405 406 testData := [][]string{ 407 {`demo`, `-e`, `show database`}, 408 {`demo`, `-e`, `show database`, `--empty`}, 409 {`demo`, `-e`, `show application_name`}, 410 {`demo`, `--format=table`, `-e`, `show database`}, 411 {`demo`, `-e`, `select 1 as "1"`, `-e`, `select 3 as "3"`}, 412 {`demo`, `--echo-sql`, `-e`, `select 1 as "1"`}, 413 {`demo`, `--set=errexit=0`, `-e`, `select nonexistent`, `-e`, `select 123 as "123"`}, 414 {`demo`, `startrek`, `-e`, `show databases`}, 415 {`demo`, `startrek`, `-e`, `show databases`, `--format=table`}, 416 // Test that if we start with --insecure we cannot perform 417 // commands that require a secure cluster. 418 {`demo`, `-e`, `CREATE USER test WITH PASSWORD 'testpass'`}, 419 {`demo`, `--insecure`, `-e`, `CREATE USER test WITH PASSWORD 'testpass'`}, 420 {`demo`, `--geo-partitioned-replicas`, `--disable-demo-license`}, 421 } 422 setCLIDefaultsForTests() 423 // We must reset the security asset loader here, otherwise the dummy 424 // asset loader that is set by default in tests will not be able to 425 // find the certs that demo sets up. 426 security.ResetAssetLoader() 427 for _, cmd := range testData { 428 c.RunWithArgs(cmd) 429 } 430 431 // Output: 432 // demo -e show database 433 // database 434 // movr 435 // demo -e show database --empty 436 // database 437 // defaultdb 438 // demo -e show application_name 439 // application_name 440 // $ cockroach demo 441 // demo --format=table -e show database 442 // database 443 // ------------ 444 // movr 445 // (1 row) 446 // demo -e select 1 as "1" -e select 3 as "3" 447 // 1 448 // 1 449 // 3 450 // 3 451 // demo --echo-sql -e select 1 as "1" 452 // > select 1 as "1" 453 // 1 454 // 1 455 // demo --set=errexit=0 -e select nonexistent -e select 123 as "123" 456 // ERROR: column "nonexistent" does not exist 457 // SQLSTATE: 42703 458 // 123 459 // 123 460 // demo startrek -e show databases 461 // database_name 462 // defaultdb 463 // postgres 464 // startrek 465 // system 466 // demo startrek -e show databases --format=table 467 // database_name 468 // ----------------- 469 // defaultdb 470 // postgres 471 // startrek 472 // system 473 // (4 rows) 474 // demo -e CREATE USER test WITH PASSWORD 'testpass' 475 // CREATE ROLE 476 // demo --insecure -e CREATE USER test WITH PASSWORD 'testpass' 477 // ERROR: setting or updating a password is not supported in insecure mode 478 // SQLSTATE: 28P01 479 // demo --geo-partitioned-replicas --disable-demo-license 480 // ERROR: enterprise features are needed for this demo (--geo-partitioned-replicas) 481 } 482 483 func Example_sql() { 484 c := newCLITest(cliTestParams{}) 485 defer c.cleanup() 486 487 c.RunWithArgs([]string{`sql`, `-e`, `show application_name`}) 488 c.RunWithArgs([]string{`sql`, `-e`, `create database t; create table t.f (x int, y int); insert into t.f values (42, 69)`}) 489 c.RunWithArgs([]string{`sql`, `-e`, `select 3 as "3"`, `-e`, `select * from t.f`}) 490 c.RunWithArgs([]string{`sql`, `-e`, `begin`, `-e`, `select 3 as "3"`, `-e`, `commit`}) 491 c.RunWithArgs([]string{`sql`, `-e`, `select * from t.f`}) 492 c.RunWithArgs([]string{`sql`, `--execute=show databases`}) 493 c.RunWithArgs([]string{`sql`, `-e`, `select 1 as "1"; select 2 as "2"`}) 494 c.RunWithArgs([]string{`sql`, `-e`, `select 1 as "1"; select 2 as "@" where false`}) 495 // CREATE TABLE AS returns a SELECT tag with a row count, check this. 496 c.RunWithArgs([]string{`sql`, `-e`, `create table t.g1 (x int)`}) 497 c.RunWithArgs([]string{`sql`, `-e`, `create table t.g2 as select * from generate_series(1,10)`}) 498 // It must be possible to access pre-defined/virtual tables even if the current database 499 // does not exist yet. 500 c.RunWithArgs([]string{`sql`, `-d`, `nonexistent`, `-e`, `select count(*) from "".information_schema.tables limit 0`}) 501 // It must be possible to create the current database after the 502 // connection was established. 503 c.RunWithArgs([]string{`sql`, `-d`, `nonexistent`, `-e`, `create database nonexistent; create table foo(x int); select * from foo`}) 504 // COPY should return an intelligible error message. 505 c.RunWithArgs([]string{`sql`, `-e`, `copy t.f from stdin`}) 506 // --set changes client-side variables before executing commands. 507 c.RunWithArgs([]string{`sql`, `--set=errexit=0`, `-e`, `select nonexistent`, `-e`, `select 123 as "123"`}) 508 c.RunWithArgs([]string{`sql`, `--set`, `echo=true`, `-e`, `select 123 as "123"`}) 509 c.RunWithArgs([]string{`sql`, `--set`, `unknownoption`, `-e`, `select 123 as "123"`}) 510 // Check that partial results + error get reported together. 511 c.RunWithArgs([]string{`sql`, `-e`, `select 1/(@1-3) from generate_series(1,4)`}) 512 513 // Output: 514 // sql -e show application_name 515 // application_name 516 // $ cockroach sql 517 // sql -e create database t; create table t.f (x int, y int); insert into t.f values (42, 69) 518 // INSERT 1 519 // sql -e select 3 as "3" -e select * from t.f 520 // 3 521 // 3 522 // x y 523 // 42 69 524 // sql -e begin -e select 3 as "3" -e commit 525 // BEGIN 526 // 3 527 // 3 528 // COMMIT 529 // sql -e select * from t.f 530 // x y 531 // 42 69 532 // sql --execute=show databases 533 // database_name 534 // defaultdb 535 // postgres 536 // system 537 // t 538 // sql -e select 1 as "1"; select 2 as "2" 539 // 1 540 // 1 541 // 2 542 // 2 543 // sql -e select 1 as "1"; select 2 as "@" where false 544 // 1 545 // 1 546 // @ 547 // sql -e create table t.g1 (x int) 548 // CREATE TABLE 549 // sql -e create table t.g2 as select * from generate_series(1,10) 550 // CREATE TABLE AS 551 // sql -d nonexistent -e select count(*) from "".information_schema.tables limit 0 552 // count 553 // sql -d nonexistent -e create database nonexistent; create table foo(x int); select * from foo 554 // x 555 // sql -e copy t.f from stdin 556 // ERROR: woops! COPY has confused this client! Suggestion: use 'psql' for COPY 557 // sql --set=errexit=0 -e select nonexistent -e select 123 as "123" 558 // ERROR: column "nonexistent" does not exist 559 // SQLSTATE: 42703 560 // 123 561 // 123 562 // sql --set echo=true -e select 123 as "123" 563 // > select 123 as "123" 564 // 123 565 // 123 566 // sql --set unknownoption -e select 123 as "123" 567 // invalid syntax: \set unknownoption. Try \? for help. 568 // ERROR: invalid syntax 569 // sql -e select 1/(@1-3) from generate_series(1,4) 570 // ?column? 571 // -0.5 572 // -1 573 // (error encountered after some results were delivered) 574 // ERROR: division by zero 575 // SQLSTATE: 22012 576 } 577 578 func Example_sql_watch() { 579 c := newCLITest(cliTestParams{}) 580 defer c.cleanup() 581 582 c.RunWithArgs([]string{`sql`, `-e`, `create table d(x int); insert into d values(3)`}) 583 c.RunWithArgs([]string{`sql`, `--watch`, `.1s`, `-e`, `update d set x=x-1 returning 1/x as dec`}) 584 585 // Output: 586 // sql -e create table d(x int); insert into d values(3) 587 // INSERT 1 588 // sql --watch .1s -e update d set x=x-1 returning 1/x as dec 589 // dec 590 // 0.5 591 // dec 592 // 1 593 // ERROR: division by zero 594 // SQLSTATE: 22012 595 } 596 597 func Example_sql_format() { 598 c := newCLITest(cliTestParams{}) 599 defer c.cleanup() 600 601 c.RunWithArgs([]string{"sql", "-e", "create database t; create table t.times (bare timestamp, withtz timestamptz)"}) 602 c.RunWithArgs([]string{"sql", "-e", "insert into t.times values ('2016-01-25 10:10:10', '2016-01-25 10:10:10-05:00')"}) 603 c.RunWithArgs([]string{"sql", "-e", "select * from t.times"}) 604 605 // Output: 606 // sql -e create database t; create table t.times (bare timestamp, withtz timestamptz) 607 // CREATE TABLE 608 // sql -e insert into t.times values ('2016-01-25 10:10:10', '2016-01-25 10:10:10-05:00') 609 // INSERT 1 610 // sql -e select * from t.times 611 // bare withtz 612 // 2016-01-25 10:10:10+00:00 2016-01-25 15:10:10+00:00 613 } 614 615 func Example_sql_column_labels() { 616 c := newCLITest(cliTestParams{}) 617 defer c.cleanup() 618 619 testData := []string{ 620 `f"oo`, 621 `f'oo`, 622 `f\oo`, 623 `short 624 very very long 625 not much`, 626 `very very long 627 thenshort`, 628 `κόσμε`, 629 `a|b`, 630 `܈85`, 631 } 632 633 tdef := make([]string, len(testData)) 634 var vals bytes.Buffer 635 for i, col := range testData { 636 tdef[i] = tree.NameString(col) + " int" 637 if i > 0 { 638 vals.WriteString(", ") 639 } 640 vals.WriteByte('0') 641 } 642 c.RunWithArgs([]string{"sql", "-e", "create database t; create table t.u (" + strings.Join(tdef, ", ") + ")"}) 643 c.RunWithArgs([]string{"sql", "-e", "insert into t.u values (" + vals.String() + ")"}) 644 c.RunWithArgs([]string{"sql", "-e", "show columns from t.u"}) 645 c.RunWithArgs([]string{"sql", "-e", "select * from t.u"}) 646 c.RunWithArgs([]string{"sql", "--format=table", "-e", "show columns from t.u"}) 647 for i := tableDisplayFormat(0); i < tableDisplayLastFormat; i++ { 648 c.RunWithArgs([]string{"sql", "--format=" + i.String(), "-e", "select * from t.u"}) 649 } 650 651 // Output 652 // sql -e create database t; create table t.u ("f""oo" int, "f'oo" int, "f\oo" int, "short 653 // very very long 654 // not much" int, "very very long 655 // thenshort" int, "κόσμε" int, "a|b" int, ܈85 int) 656 // CREATE TABLE 657 // sql -e insert into t.u values (0, 0, 0, 0, 0, 0, 0, 0) 658 // INSERT 1 659 // sql -e show columns from t.u 660 // column_name data_type is_nullable column_default generation_expression indices 661 // "f""oo" INT true NULL {} 662 // f'oo INT true NULL {} 663 // f\oo INT true NULL {} 664 // "short 665 // very very long 666 // not much" INT true NULL {} 667 // "very very long 668 // thenshort" INT true NULL {} 669 // κόσμε INT true NULL {} 670 // a|b INT true NULL {} 671 // ܈85 INT true NULL {} 672 // sql -e select * from t.u 673 // "f""oo" f'oo f\oo "short 674 // very very long 675 // not much" "very very long 676 // thenshort" κόσμε a|b ܈85 677 // 0 0 0 0 0 0 0 0 678 // sql --format=table -e show columns from t.u 679 // column_name | data_type | is_nullable | column_default | generation_expression | indices 680 // +----------------+-----------+-------------+----------------+-----------------------+---------+ 681 // f"oo | INT | true | NULL | | {} 682 // f'oo | INT | true | NULL | | {} 683 // f\oo | INT | true | NULL | | {} 684 // short | INT | true | NULL | | {} 685 // very very long | | | | | 686 // not much | | | | | 687 // very very long | INT | true | NULL | | {} 688 // thenshort | | | | | 689 // κόσμε | INT | true | NULL | | {} 690 // a|b | INT | true | NULL | | {} 691 // ܈85 | INT | true | NULL | | {} 692 // (8 rows) 693 // sql --format=tsv -e select * from t.u 694 // "f""oo" f'oo f\oo "short 695 // very very long 696 // not much" "very very long 697 // thenshort" κόσμε a|b ܈85 698 // 0 0 0 0 0 0 0 0 699 // sql --format=csv -e select * from t.u 700 // "f""oo",f'oo,f\oo,"short 701 // very very long 702 // not much","very very long 703 // thenshort",κόσμε,a|b,܈85 704 // 0,0,0,0,0,0,0,0 705 // sql --format=table -e select * from t.u 706 // f"oo | f'oo | f\oo | short | very very long | κόσμε | a|b | ܈85 707 // | | | very very long | thenshort | | | 708 // | | | not much | | | | 709 // +------+------+------+----------------+----------------+-------+-----+-----+ 710 // 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 711 // (1 row) 712 // sql --format=records -e select * from t.u 713 // -[ RECORD 1 ] 714 // f"oo | 0 715 // f'oo | 0 716 // f\oo | 0 717 // short +| 0 718 // very very long+| 719 // not much | 720 // very very long+| 0 721 // thenshort | 722 // κόσμε | 0 723 // a|b | 0 724 // ܈85 | 0 725 // sql --format=sql -e select * from t.u 726 // CREATE TABLE results ( 727 // "f""oo" STRING, 728 // "f'oo" STRING, 729 // "f\oo" STRING, 730 // "short 731 // very very long 732 // not much" STRING, 733 // "very very long 734 // thenshort" STRING, 735 // "κόσμε" STRING, 736 // "a|b" STRING, 737 // ܈85 STRING 738 // ); 739 // 740 // INSERT INTO results VALUES ('0', '0', '0', '0', '0', '0', '0', '0'); 741 // -- 1 row 742 // sql --format=html -e select * from t.u 743 // <table> 744 // <thead><tr><th>row</th><th>f"oo</th><th>f'oo</th><th>f\oo</th><th>short<br/>very very long<br/>not much</th><th>very very long<br/>thenshort</th><th>κόσμε</th><th>a|b</th><th>܈85</th></tr></thead> 745 // <tbody> 746 // <tr><td>1</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td></tr> 747 // </tbody> 748 // <tfoot><tr><td colspan=9>1 row</td></tr></tfoot></table> 749 // sql --format=raw -e select * from t.u 750 // # 8 columns 751 // # row 1 752 // ## 1 753 // 0 754 // ## 1 755 // 0 756 // ## 1 757 // 0 758 // ## 1 759 // 0 760 // ## 1 761 // 0 762 // ## 1 763 // 0 764 // ## 1 765 // 0 766 // ## 1 767 // 0 768 // # 1 row 769 } 770 771 func Example_sql_empty_table() { 772 c := newCLITest(cliTestParams{}) 773 defer c.cleanup() 774 775 c.RunWithArgs([]string{"sql", "-e", "create database t;" + 776 "create table t.norows(x int);" + 777 "create table t.nocolsnorows();" + 778 "create table t.nocols(); insert into t.nocols(rowid) values (1),(2),(3);"}) 779 for _, table := range []string{"norows", "nocols", "nocolsnorows"} { 780 for format := tableDisplayFormat(0); format < tableDisplayLastFormat; format++ { 781 c.RunWithArgs([]string{"sql", "--format=" + format.String(), "-e", "select * from t." + table}) 782 } 783 } 784 785 // Output: 786 // sql -e create database t;create table t.norows(x int);create table t.nocolsnorows();create table t.nocols(); insert into t.nocols(rowid) values (1),(2),(3); 787 // INSERT 3 788 // sql --format=tsv -e select * from t.norows 789 // x 790 // sql --format=csv -e select * from t.norows 791 // x 792 // sql --format=table -e select * from t.norows 793 // x 794 // ----- 795 // (0 rows) 796 // sql --format=records -e select * from t.norows 797 // sql --format=sql -e select * from t.norows 798 // CREATE TABLE results ( 799 // x STRING 800 // ); 801 // 802 // -- 0 rows 803 // sql --format=html -e select * from t.norows 804 // <table> 805 // <thead><tr><th>row</th><th>x</th></tr></thead> 806 // </tbody> 807 // <tfoot><tr><td colspan=2>0 rows</td></tr></tfoot></table> 808 // sql --format=raw -e select * from t.norows 809 // # 1 column 810 // # 0 rows 811 // sql --format=tsv -e select * from t.nocols 812 // # no columns 813 // # empty 814 // # empty 815 // # empty 816 // sql --format=csv -e select * from t.nocols 817 // # no columns 818 // # empty 819 // # empty 820 // # empty 821 // sql --format=table -e select * from t.nocols 822 // -- 823 // (3 rows) 824 // sql --format=records -e select * from t.nocols 825 // (3 rows) 826 // sql --format=sql -e select * from t.nocols 827 // CREATE TABLE results ( 828 // ); 829 // 830 // INSERT INTO results(rowid) VALUES (DEFAULT); 831 // INSERT INTO results(rowid) VALUES (DEFAULT); 832 // INSERT INTO results(rowid) VALUES (DEFAULT); 833 // -- 3 rows 834 // sql --format=html -e select * from t.nocols 835 // <table> 836 // <thead><tr><th>row</th></tr></thead> 837 // <tbody> 838 // <tr><td>1</td></tr> 839 // <tr><td>2</td></tr> 840 // <tr><td>3</td></tr> 841 // </tbody> 842 // <tfoot><tr><td colspan=1>3 rows</td></tr></tfoot></table> 843 // sql --format=raw -e select * from t.nocols 844 // # 0 columns 845 // # row 1 846 // # row 2 847 // # row 3 848 // # 3 rows 849 // sql --format=tsv -e select * from t.nocolsnorows 850 // # no columns 851 // sql --format=csv -e select * from t.nocolsnorows 852 // # no columns 853 // sql --format=table -e select * from t.nocolsnorows 854 // -- 855 // (0 rows) 856 // sql --format=records -e select * from t.nocolsnorows 857 // (0 rows) 858 // sql --format=sql -e select * from t.nocolsnorows 859 // CREATE TABLE results ( 860 // ); 861 // 862 // -- 0 rows 863 // sql --format=html -e select * from t.nocolsnorows 864 // <table> 865 // <thead><tr><th>row</th></tr></thead> 866 // </tbody> 867 // <tfoot><tr><td colspan=1>0 rows</td></tr></tfoot></table> 868 // sql --format=raw -e select * from t.nocolsnorows 869 // # 0 columns 870 // # 0 rows 871 } 872 873 func Example_csv_tsv_quoting() { 874 c := newCLITest(cliTestParams{}) 875 defer c.cleanup() 876 877 testData := []string{ 878 `ab`, 879 `a b`, 880 `a 881 bc 882 def`, 883 `a, b`, 884 `"a", "b"`, 885 `'a', 'b'`, 886 `a\,b`, 887 `a b`, 888 } 889 890 for _, sqlStr := range testData { 891 escaped := lex.EscapeSQLString(sqlStr) 892 sql := "select " + escaped + " as s, " + escaped + " as t" 893 c.RunWithArgs([]string{"sql", "--format=csv", "-e", sql}) 894 c.RunWithArgs([]string{"sql", "--format=tsv", "-e", sql}) 895 } 896 897 for _, identStr := range testData { 898 escaped1 := tree.NameString(identStr + "1") 899 escaped2 := tree.NameString(identStr + "2") 900 sql := "select 1 as " + escaped1 + ", 2 as " + escaped2 901 c.RunWithArgs([]string{"sql", "--format=csv", "-e", sql}) 902 c.RunWithArgs([]string{"sql", "--format=tsv", "-e", sql}) 903 } 904 905 // Output: 906 // sql --format=csv -e select 'ab' as s, 'ab' as t 907 // s,t 908 // ab,ab 909 // sql --format=tsv -e select 'ab' as s, 'ab' as t 910 // s t 911 // ab ab 912 // sql --format=csv -e select 'a b' as s, 'a b' as t 913 // s,t 914 // a b,a b 915 // sql --format=tsv -e select 'a b' as s, 'a b' as t 916 // s t 917 // a b a b 918 // sql --format=csv -e select e'a\nbc\ndef' as s, e'a\nbc\ndef' as t 919 // s,t 920 // "a 921 // bc 922 // def","a 923 // bc 924 // def" 925 // sql --format=tsv -e select e'a\nbc\ndef' as s, e'a\nbc\ndef' as t 926 // s t 927 // "a 928 // bc 929 // def" "a 930 // bc 931 // def" 932 // sql --format=csv -e select 'a, b' as s, 'a, b' as t 933 // s,t 934 // "a, b","a, b" 935 // sql --format=tsv -e select 'a, b' as s, 'a, b' as t 936 // s t 937 // a, b a, b 938 // sql --format=csv -e select '"a", "b"' as s, '"a", "b"' as t 939 // s,t 940 // """a"", ""b""","""a"", ""b""" 941 // sql --format=tsv -e select '"a", "b"' as s, '"a", "b"' as t 942 // s t 943 // """a"", ""b""" """a"", ""b""" 944 // sql --format=csv -e select e'\'a\', \'b\'' as s, e'\'a\', \'b\'' as t 945 // s,t 946 // "'a', 'b'","'a', 'b'" 947 // sql --format=tsv -e select e'\'a\', \'b\'' as s, e'\'a\', \'b\'' as t 948 // s t 949 // 'a', 'b' 'a', 'b' 950 // sql --format=csv -e select e'a\\,b' as s, e'a\\,b' as t 951 // s,t 952 // "a\,b","a\,b" 953 // sql --format=tsv -e select e'a\\,b' as s, e'a\\,b' as t 954 // s t 955 // a\,b a\,b 956 // sql --format=csv -e select e'a\tb' as s, e'a\tb' as t 957 // s,t 958 // a b,a b 959 // sql --format=tsv -e select e'a\tb' as s, e'a\tb' as t 960 // s t 961 // "a b" "a b" 962 // sql --format=csv -e select 1 as ab1, 2 as ab2 963 // ab1,ab2 964 // 1,2 965 // sql --format=tsv -e select 1 as ab1, 2 as ab2 966 // ab1 ab2 967 // 1 2 968 // sql --format=csv -e select 1 as "a b1", 2 as "a b2" 969 // a b1,a b2 970 // 1,2 971 // sql --format=tsv -e select 1 as "a b1", 2 as "a b2" 972 // a b1 a b2 973 // 1 2 974 // sql --format=csv -e select 1 as "a 975 // bc 976 // def1", 2 as "a 977 // bc 978 // def2" 979 // "a 980 // bc 981 // def1","a 982 // bc 983 // def2" 984 // 1,2 985 // sql --format=tsv -e select 1 as "a 986 // bc 987 // def1", 2 as "a 988 // bc 989 // def2" 990 // "a 991 // bc 992 // def1" "a 993 // bc 994 // def2" 995 // 1 2 996 // sql --format=csv -e select 1 as "a, b1", 2 as "a, b2" 997 // "a, b1","a, b2" 998 // 1,2 999 // sql --format=tsv -e select 1 as "a, b1", 2 as "a, b2" 1000 // a, b1 a, b2 1001 // 1 2 1002 // sql --format=csv -e select 1 as """a"", ""b""1", 2 as """a"", ""b""2" 1003 // """a"", ""b""1","""a"", ""b""2" 1004 // 1,2 1005 // sql --format=tsv -e select 1 as """a"", ""b""1", 2 as """a"", ""b""2" 1006 // """a"", ""b""1" """a"", ""b""2" 1007 // 1 2 1008 // sql --format=csv -e select 1 as "'a', 'b'1", 2 as "'a', 'b'2" 1009 // "'a', 'b'1","'a', 'b'2" 1010 // 1,2 1011 // sql --format=tsv -e select 1 as "'a', 'b'1", 2 as "'a', 'b'2" 1012 // 'a', 'b'1 'a', 'b'2 1013 // 1 2 1014 // sql --format=csv -e select 1 as "a\,b1", 2 as "a\,b2" 1015 // "a\,b1","a\,b2" 1016 // 1,2 1017 // sql --format=tsv -e select 1 as "a\,b1", 2 as "a\,b2" 1018 // a\,b1 a\,b2 1019 // 1 2 1020 // sql --format=csv -e select 1 as "a b1", 2 as "a b2" 1021 // a b1,a b2 1022 // 1,2 1023 // sql --format=tsv -e select 1 as "a b1", 2 as "a b2" 1024 // "a b1" "a b2" 1025 // 1 2 1026 } 1027 1028 func Example_sql_table() { 1029 c := newCLITest(cliTestParams{}) 1030 defer c.cleanup() 1031 1032 testData := []struct { 1033 str, desc string 1034 }{ 1035 {"e'foo'", "printable ASCII"}, 1036 {"e'\"foo'", "printable ASCII with quotes"}, 1037 {"e'\\\\foo'", "printable ASCII with backslash"}, 1038 {"e'foo\\x0abar'", "non-printable ASCII"}, 1039 {"'κόσμε'", "printable UTF8"}, 1040 {"e'\\xc3\\xb1'", "printable UTF8 using escapes"}, 1041 {"e'\\x01'", "non-printable UTF8 string"}, 1042 {"e'\\xdc\\x88\\x38\\x35'", "UTF8 string with RTL char"}, 1043 {"e'a\\tb\\tc\\n12\\t123123213\\t12313'", "tabs"}, 1044 {"e'\\xc3\\x28'", "non-UTF8 string"}, // This expects an insert error. 1045 } 1046 1047 c.RunWithArgs([]string{"sql", "-e", "create database t; create table t.t (s string, d string);"}) 1048 for _, t := range testData { 1049 c.RunWithArgs([]string{"sql", "-e", "insert into t.t values (" + t.str + ", '" + t.desc + "')"}) 1050 } 1051 c.RunWithArgs([]string{"sql", "-e", "select * from t.t"}) 1052 for format := tableDisplayFormat(0); format < tableDisplayLastFormat; format++ { 1053 c.RunWithArgs([]string{"sql", "--format=" + format.String(), "-e", "select * from t.t"}) 1054 } 1055 1056 // Output: 1057 // sql -e create database t; create table t.t (s string, d string); 1058 // CREATE TABLE 1059 // sql -e insert into t.t values (e'foo', 'printable ASCII') 1060 // INSERT 1 1061 // sql -e insert into t.t values (e'"foo', 'printable ASCII with quotes') 1062 // INSERT 1 1063 // sql -e insert into t.t values (e'\\foo', 'printable ASCII with backslash') 1064 // INSERT 1 1065 // sql -e insert into t.t values (e'foo\x0abar', 'non-printable ASCII') 1066 // INSERT 1 1067 // sql -e insert into t.t values ('κόσμε', 'printable UTF8') 1068 // INSERT 1 1069 // sql -e insert into t.t values (e'\xc3\xb1', 'printable UTF8 using escapes') 1070 // INSERT 1 1071 // sql -e insert into t.t values (e'\x01', 'non-printable UTF8 string') 1072 // INSERT 1 1073 // sql -e insert into t.t values (e'\xdc\x88\x38\x35', 'UTF8 string with RTL char') 1074 // INSERT 1 1075 // sql -e insert into t.t values (e'a\tb\tc\n12\t123123213\t12313', 'tabs') 1076 // INSERT 1 1077 // sql -e insert into t.t values (e'\xc3\x28', 'non-UTF8 string') 1078 // ERROR: lexical error: invalid UTF-8 byte sequence 1079 // SQLSTATE: 42601 1080 // DETAIL: source SQL: 1081 // insert into t.t values (e'\xc3\x28', 'non-UTF8 string') 1082 // ^ 1083 // HINT: try \h VALUES 1084 // sql -e select * from t.t 1085 // s d 1086 // foo printable ASCII 1087 // """foo" printable ASCII with quotes 1088 // \foo printable ASCII with backslash 1089 // "foo 1090 // bar" non-printable ASCII 1091 // κόσμε printable UTF8 1092 // ñ printable UTF8 using escapes 1093 // \x01 non-printable UTF8 string 1094 // ܈85 UTF8 string with RTL char 1095 // "a b c 1096 // 12 123123213 12313" tabs 1097 // sql --format=tsv -e select * from t.t 1098 // s d 1099 // foo printable ASCII 1100 // """foo" printable ASCII with quotes 1101 // \foo printable ASCII with backslash 1102 // "foo 1103 // bar" non-printable ASCII 1104 // κόσμε printable UTF8 1105 // ñ printable UTF8 using escapes 1106 // \x01 non-printable UTF8 string 1107 // ܈85 UTF8 string with RTL char 1108 // "a b c 1109 // 12 123123213 12313" tabs 1110 // sql --format=csv -e select * from t.t 1111 // s,d 1112 // foo,printable ASCII 1113 // """foo",printable ASCII with quotes 1114 // \foo,printable ASCII with backslash 1115 // "foo 1116 // bar",non-printable ASCII 1117 // κόσμε,printable UTF8 1118 // ñ,printable UTF8 using escapes 1119 // \x01,non-printable UTF8 string 1120 // ܈85,UTF8 string with RTL char 1121 // "a b c 1122 // 12 123123213 12313",tabs 1123 // sql --format=table -e select * from t.t 1124 // s | d 1125 // ----------------------+--------------------------------- 1126 // foo | printable ASCII 1127 // "foo | printable ASCII with quotes 1128 // \foo | printable ASCII with backslash 1129 // foo | non-printable ASCII 1130 // bar | 1131 // κόσμε | printable UTF8 1132 // ñ | printable UTF8 using escapes 1133 // \x01 | non-printable UTF8 string 1134 // ܈85 | UTF8 string with RTL char 1135 // a b c | tabs 1136 // 12 123123213 12313 | 1137 // (9 rows) 1138 // sql --format=records -e select * from t.t 1139 // -[ RECORD 1 ] 1140 // s | foo 1141 // d | printable ASCII 1142 // -[ RECORD 2 ] 1143 // s | "foo 1144 // d | printable ASCII with quotes 1145 // -[ RECORD 3 ] 1146 // s | \foo 1147 // d | printable ASCII with backslash 1148 // -[ RECORD 4 ] 1149 // s | foo+ 1150 // | bar 1151 // d | non-printable ASCII 1152 // -[ RECORD 5 ] 1153 // s | κόσμε 1154 // d | printable UTF8 1155 // -[ RECORD 6 ] 1156 // s | ñ 1157 // d | printable UTF8 using escapes 1158 // -[ RECORD 7 ] 1159 // s | \x01 1160 // d | non-printable UTF8 string 1161 // -[ RECORD 8 ] 1162 // s | ܈85 1163 // d | UTF8 string with RTL char 1164 // -[ RECORD 9 ] 1165 // s | a b c+ 1166 // | 12 123123213 12313 1167 // d | tabs 1168 // sql --format=sql -e select * from t.t 1169 // CREATE TABLE results ( 1170 // s STRING, 1171 // d STRING 1172 // ); 1173 // 1174 // INSERT INTO results VALUES ('foo', 'printable ASCII'); 1175 // INSERT INTO results VALUES ('"foo', 'printable ASCII with quotes'); 1176 // INSERT INTO results VALUES (e'\\foo', 'printable ASCII with backslash'); 1177 // INSERT INTO results VALUES (e'foo\nbar', 'non-printable ASCII'); 1178 // INSERT INTO results VALUES (e'\u03BA\U00001F79\u03C3\u03BC\u03B5', 'printable UTF8'); 1179 // INSERT INTO results VALUES (e'\u00F1', 'printable UTF8 using escapes'); 1180 // INSERT INTO results VALUES (e'\\x01', 'non-printable UTF8 string'); 1181 // INSERT INTO results VALUES (e'\u070885', 'UTF8 string with RTL char'); 1182 // INSERT INTO results VALUES (e'a\tb\tc\n12\t123123213\t12313', 'tabs'); 1183 // -- 9 rows 1184 // sql --format=html -e select * from t.t 1185 // <table> 1186 // <thead><tr><th>row</th><th>s</th><th>d</th></tr></thead> 1187 // <tbody> 1188 // <tr><td>1</td><td>foo</td><td>printable ASCII</td></tr> 1189 // <tr><td>2</td><td>"foo</td><td>printable ASCII with quotes</td></tr> 1190 // <tr><td>3</td><td>\foo</td><td>printable ASCII with backslash</td></tr> 1191 // <tr><td>4</td><td>foo<br/>bar</td><td>non-printable ASCII</td></tr> 1192 // <tr><td>5</td><td>κόσμε</td><td>printable UTF8</td></tr> 1193 // <tr><td>6</td><td>ñ</td><td>printable UTF8 using escapes</td></tr> 1194 // <tr><td>7</td><td>\x01</td><td>non-printable UTF8 string</td></tr> 1195 // <tr><td>8</td><td>܈85</td><td>UTF8 string with RTL char</td></tr> 1196 // <tr><td>9</td><td>a b c<br/>12 123123213 12313</td><td>tabs</td></tr> 1197 // </tbody> 1198 // <tfoot><tr><td colspan=3>9 rows</td></tr></tfoot></table> 1199 // sql --format=raw -e select * from t.t 1200 // # 2 columns 1201 // # row 1 1202 // ## 3 1203 // foo 1204 // ## 15 1205 // printable ASCII 1206 // # row 2 1207 // ## 4 1208 // "foo 1209 // ## 27 1210 // printable ASCII with quotes 1211 // # row 3 1212 // ## 4 1213 // \foo 1214 // ## 30 1215 // printable ASCII with backslash 1216 // # row 4 1217 // ## 7 1218 // foo 1219 // bar 1220 // ## 19 1221 // non-printable ASCII 1222 // # row 5 1223 // ## 11 1224 // κόσμε 1225 // ## 14 1226 // printable UTF8 1227 // # row 6 1228 // ## 2 1229 // ñ 1230 // ## 28 1231 // printable UTF8 using escapes 1232 // # row 7 1233 // ## 4 1234 // \x01 1235 // ## 25 1236 // non-printable UTF8 string 1237 // # row 8 1238 // ## 4 1239 // ܈85 1240 // ## 25 1241 // UTF8 string with RTL char 1242 // # row 9 1243 // ## 24 1244 // a b c 1245 // 12 123123213 12313 1246 // ## 4 1247 // tabs 1248 // # 9 rows 1249 } 1250 1251 func TestRenderHTML(t *testing.T) { 1252 defer leaktest.AfterTest(t)() 1253 1254 cols := []string{"colname"} 1255 align := "d" 1256 rows := [][]string{ 1257 {"<b>foo</b>"}, 1258 {"bar"}, 1259 } 1260 1261 type testCase struct { 1262 reporter htmlReporter 1263 out string 1264 } 1265 1266 testCases := []testCase{ 1267 { 1268 reporter: htmlReporter{}, 1269 out: `<table> 1270 <thead><tr><th>colname</th></tr></thead> 1271 <tbody> 1272 <tr><td><b>foo</b></td></tr> 1273 <tr><td>bar</td></tr> 1274 </tbody> 1275 </table> 1276 `, 1277 }, 1278 { 1279 reporter: htmlReporter{escape: true}, 1280 out: `<table> 1281 <thead><tr><th>colname</th></tr></thead> 1282 <tbody> 1283 <tr><td><b>foo</b></td></tr> 1284 <tr><td>bar</td></tr> 1285 </tbody> 1286 </table> 1287 `, 1288 }, 1289 { 1290 reporter: htmlReporter{rowStats: true}, 1291 out: `<table> 1292 <thead><tr><th>row</th><th>colname</th></tr></thead> 1293 <tbody> 1294 <tr><td>1</td><td><b>foo</b></td></tr> 1295 <tr><td>2</td><td>bar</td></tr> 1296 </tbody> 1297 <tfoot><tr><td colspan=2>2 rows</td></tr></tfoot></table> 1298 `, 1299 }, 1300 { 1301 reporter: htmlReporter{escape: true, rowStats: true}, 1302 out: `<table> 1303 <thead><tr><th>row</th><th>colname</th></tr></thead> 1304 <tbody> 1305 <tr><td>1</td><td><b>foo</b></td></tr> 1306 <tr><td>2</td><td>bar</td></tr> 1307 </tbody> 1308 <tfoot><tr><td colspan=2>2 rows</td></tr></tfoot></table> 1309 `, 1310 }, 1311 } 1312 1313 for _, tc := range testCases { 1314 name := fmt.Sprintf("escape=%v/rowStats=%v", tc.reporter.escape, tc.reporter.rowStats) 1315 t.Run(name, func(t *testing.T) { 1316 var buf bytes.Buffer 1317 err := render(&tc.reporter, &buf, 1318 cols, newRowSliceIter(rows, align), 1319 nil /* completedHook */, nil /* noRowsHook */) 1320 if err != nil { 1321 t.Fatal(err) 1322 } 1323 if tc.out != buf.String() { 1324 t.Errorf("expected:\n%s\ngot:\n%s", tc.out, buf.String()) 1325 } 1326 }) 1327 } 1328 } 1329 1330 func Example_misc_table() { 1331 c := newCLITest(cliTestParams{}) 1332 defer c.cleanup() 1333 1334 c.RunWithArgs([]string{"sql", "-e", "create database t; create table t.t (s string, d string);"}) 1335 c.RunWithArgs([]string{"sql", "--format=table", "-e", "select ' hai' as x"}) 1336 c.RunWithArgs([]string{"sql", "--format=table", "-e", "explain select s, 'foo' from t.t"}) 1337 1338 // Output: 1339 // sql -e create database t; create table t.t (s string, d string); 1340 // CREATE TABLE 1341 // sql --format=table -e select ' hai' as x 1342 // x 1343 // --------- 1344 // hai 1345 // (1 row) 1346 // sql --format=table -e explain select s, 'foo' from t.t 1347 // tree | field | description 1348 // ------------+-------------+-------------- 1349 // | distributed | true 1350 // | vectorized | false 1351 // render | | 1352 // └── scan | | 1353 // | table | t@primary 1354 // | spans | FULL SCAN 1355 // (6 rows) 1356 } 1357 1358 func Example_cert() { 1359 c := newCLITest(cliTestParams{}) 1360 defer c.cleanup() 1361 1362 c.RunWithCAArgs([]string{"cert", "create-client", "foo"}) 1363 c.RunWithCAArgs([]string{"cert", "create-client", "Ομηρος"}) 1364 c.RunWithCAArgs([]string{"cert", "create-client", "0foo"}) 1365 c.RunWithCAArgs([]string{"cert", "create-client", ",foo"}) 1366 1367 // Output: 1368 // cert create-client foo 1369 // cert create-client Ομηρος 1370 // cert create-client 0foo 1371 // cert create-client ,foo 1372 // ERROR: failed to generate client certificate and key: username ",foo" invalid 1373 // SQLSTATE: 42602 1374 // HINT: Usernames are case insensitive, must start with a letter, digit or underscore, may contain letters, digits, dashes, periods, or underscores, and must not exceed 63 characters. 1375 } 1376 1377 // TestFlagUsage is a basic test to make sure the fragile 1378 // help template does not break. 1379 func TestFlagUsage(t *testing.T) { 1380 defer leaktest.AfterTest(t)() 1381 1382 expUsage := `Usage: 1383 cockroach [command] 1384 1385 Available Commands: 1386 start start a node in a multi-node cluster 1387 start-single-node start a single-node cluster 1388 init initialize a cluster 1389 cert create ca, node, and client certs 1390 quit drain and shut down a node 1391 1392 sql open a sql shell 1393 auth-session log in and out of HTTP sessions 1394 node list, inspect, drain or remove nodes 1395 dump dump sql tables 1396 1397 nodelocal upload and delete nodelocal files 1398 demo open a demo sql shell 1399 gen generate auxiliary files 1400 version output version information 1401 debug debugging commands 1402 sqlfmt format SQL statements 1403 workload [experimental] generators for data and query loads 1404 systembench Run systembench 1405 help Help about any command 1406 1407 Flags: 1408 -h, --help help for cockroach 1409 --logtostderr Severity[=DEFAULT] logs at or above this threshold go to stderr (default NONE) 1410 --no-color disable standard error log colorization 1411 1412 Use "cockroach [command] --help" for more information about a command. 1413 ` 1414 helpExpected := fmt.Sprintf("CockroachDB command-line interface and server.\n\n%s", expUsage) 1415 badFlagExpected := fmt.Sprintf("%s\nError: unknown flag: --foo\n", expUsage) 1416 1417 testCases := []struct { 1418 flags []string 1419 expErr bool 1420 expected string 1421 }{ 1422 {[]string{"help"}, false, helpExpected}, // request help specifically 1423 {[]string{"--foo"}, true, badFlagExpected}, // unknown flag 1424 } 1425 for _, test := range testCases { 1426 t.Run(strings.Join(test.flags, ","), func(t *testing.T) { 1427 // Override os.Stdout or os.Stderr with our own. 1428 r, w, err := os.Pipe() 1429 if err != nil { 1430 t.Fatal(err) 1431 } 1432 cockroachCmd.SetOutput(w) 1433 defer cockroachCmd.SetOutput(nil) 1434 1435 done := make(chan error) 1436 var buf bytes.Buffer 1437 // copy the output in a separate goroutine so printing can't block indefinitely. 1438 go func() { 1439 // Copy reads 'r' until EOF is reached. 1440 _, err := io.Copy(&buf, r) 1441 done <- err 1442 }() 1443 1444 if err := Run(test.flags); err != nil { 1445 fmt.Fprintln(w, "Error:", err) 1446 if !test.expErr { 1447 t.Error(err) 1448 } 1449 } 1450 1451 // back to normal state 1452 w.Close() 1453 if err := <-done; err != nil { 1454 t.Fatal(err) 1455 } 1456 1457 // Filter out all test flags. 1458 testFlagRE := regexp.MustCompile(`--(test\.|vmodule|rewrite)`) 1459 lines := strings.Split(buf.String(), "\n") 1460 final := []string{} 1461 for _, l := range lines { 1462 if testFlagRE.MatchString(l) { 1463 continue 1464 } 1465 final = append(final, l) 1466 } 1467 got := strings.Join(final, "\n") 1468 1469 assert.Equal(t, test.expected, got) 1470 }) 1471 } 1472 } 1473 1474 func Example_node() { 1475 c := newCLITest(cliTestParams{}) 1476 defer c.cleanup() 1477 1478 // Refresh time series data, which is required to retrieve stats. 1479 if err := c.WriteSummaries(); err != nil { 1480 log.Fatalf(context.Background(), "Couldn't write stats summaries: %s", err) 1481 } 1482 1483 c.Run("node ls") 1484 c.Run("node ls --format=table") 1485 c.Run("node status 10000") 1486 c.RunWithArgs([]string{"sql", "-e", "drop database defaultdb"}) 1487 c.Run("node ls") 1488 1489 // Output: 1490 // node ls 1491 // id 1492 // 1 1493 // node ls --format=table 1494 // id 1495 // ------ 1496 // 1 1497 // (1 row) 1498 // node status 10000 1499 // ERROR: node 10000 doesn't exist 1500 // sql -e drop database defaultdb 1501 // DROP DATABASE 1502 // node ls 1503 // id 1504 // 1 1505 } 1506 1507 func TestCLITimeout(t *testing.T) { 1508 defer leaktest.AfterTest(t)() 1509 1510 c := newCLITest(cliTestParams{}) 1511 defer c.cleanup() 1512 1513 // Wrap the meat of the test in a retry loop. Setting a timeout like this is 1514 // racy as the operation may have succeeded by the time the scheduler gives 1515 // the timeout a chance to have an effect. We specify --all to include some 1516 // slower to access virtual tables in the query. 1517 testutils.SucceedsSoon(t, func() error { 1518 out, err := c.RunWithCapture("node status 1 --all --timeout 1ns") 1519 if err != nil { 1520 t.Fatal(err) 1521 } 1522 1523 const exp = `node status 1 --all --timeout 1ns 1524 ERROR: query execution canceled due to statement timeout 1525 SQLSTATE: 57014 1526 ` 1527 if out != exp { 1528 err := errors.Errorf("unexpected output:\n%q\nwanted:\n%q", out, exp) 1529 t.Log(err) 1530 return err 1531 } 1532 return nil 1533 }) 1534 } 1535 1536 func TestNodeStatus(t *testing.T) { 1537 defer leaktest.AfterTest(t)() 1538 1539 t.Skip("currently flaky: #38151") 1540 1541 start := timeutil.Now() 1542 c := newCLITest(cliTestParams{}) 1543 defer c.cleanup() 1544 1545 // Refresh time series data, which is required to retrieve stats. 1546 if err := c.WriteSummaries(); err != nil { 1547 t.Fatalf("couldn't write stats summaries: %s", err) 1548 } 1549 1550 out, err := c.RunWithCapture("node status 1 --format=table") 1551 if err != nil { 1552 t.Fatal(err) 1553 } 1554 checkNodeStatus(t, c, out, start) 1555 1556 out, err = c.RunWithCapture("node status --ranges --format=table") 1557 if err != nil { 1558 t.Fatal(err) 1559 } 1560 checkNodeStatus(t, c, out, start) 1561 1562 out, err = c.RunWithCapture("node status --stats --format=table") 1563 if err != nil { 1564 t.Fatal(err) 1565 } 1566 checkNodeStatus(t, c, out, start) 1567 1568 out, err = c.RunWithCapture("node status --ranges --stats --format=table") 1569 if err != nil { 1570 t.Fatal(err) 1571 } 1572 checkNodeStatus(t, c, out, start) 1573 1574 out, err = c.RunWithCapture("node status --decommission --format=table") 1575 if err != nil { 1576 t.Fatal(err) 1577 } 1578 checkNodeStatus(t, c, out, start) 1579 1580 out, err = c.RunWithCapture("node status --ranges --stats --decommission --format=table") 1581 if err != nil { 1582 t.Fatal(err) 1583 } 1584 checkNodeStatus(t, c, out, start) 1585 1586 out, err = c.RunWithCapture("node status --all --format=table") 1587 if err != nil { 1588 t.Fatal(err) 1589 } 1590 checkNodeStatus(t, c, out, start) 1591 1592 out, err = c.RunWithCapture("node status --format=table") 1593 if err != nil { 1594 t.Fatal(err) 1595 } 1596 checkNodeStatus(t, c, out, start) 1597 } 1598 1599 func checkNodeStatus(t *testing.T, c cliTest, output string, start time.Time) { 1600 buf := bytes.NewBufferString(output) 1601 s := bufio.NewScanner(buf) 1602 1603 type testCase struct { 1604 name string 1605 idx int 1606 maxval int64 1607 } 1608 1609 // Skip command line. 1610 if !s.Scan() { 1611 t.Fatalf("Couldn't skip command line: %s", s.Err()) 1612 } 1613 1614 // check column names. 1615 if !s.Scan() { 1616 t.Fatalf("Error reading column names: %s", s.Err()) 1617 } 1618 cols, err := extractFields(s.Text()) 1619 if err != nil { 1620 t.Fatalf("%s", err) 1621 } 1622 if !reflect.DeepEqual(cols, getStatusNodeHeaders()) { 1623 t.Fatalf("columns (%s) don't match expected (%s)", cols, getStatusNodeHeaders()) 1624 } 1625 1626 checkSeparatorLine(t, s) 1627 1628 // Check node status. 1629 if !s.Scan() { 1630 t.Fatalf("error reading node status: %s", s.Err()) 1631 } 1632 fields, err := extractFields(s.Text()) 1633 if err != nil { 1634 t.Fatalf("%s", err) 1635 } 1636 1637 nodeID := c.Gossip().NodeID.Get() 1638 nodeIDStr := strconv.FormatInt(int64(nodeID), 10) 1639 if a, e := fields[0], nodeIDStr; a != e { 1640 t.Errorf("node id (%s) != expected (%s)", a, e) 1641 } 1642 1643 nodeAddr, err := c.Gossip().GetNodeIDAddress(nodeID) 1644 if err != nil { 1645 t.Fatal(err) 1646 } 1647 if a, e := fields[1], nodeAddr.String(); a != e { 1648 t.Errorf("node address (%s) != expected (%s)", a, e) 1649 } 1650 1651 nodeSQLAddr, err := c.Gossip().GetNodeIDSQLAddress(nodeID) 1652 if err != nil { 1653 t.Fatal(err) 1654 } 1655 if a, e := fields[2], nodeSQLAddr.String(); a != e { 1656 t.Errorf("node SQL address (%s) != expected (%s)", a, e) 1657 } 1658 1659 // Verify Build Tag. 1660 if a, e := fields[3], build.GetInfo().Tag; a != e { 1661 t.Errorf("build tag (%s) != expected (%s)", a, e) 1662 } 1663 1664 // Verify that updated_at and started_at are reasonably recent. 1665 // CircleCI can be very slow. This was flaky at 5s. 1666 checkTimeElapsed(t, fields[4], 15*time.Second, start) 1667 checkTimeElapsed(t, fields[5], 15*time.Second, start) 1668 1669 testcases := []testCase{} 1670 1671 // We're skipping over the first 5 default fields such as node id and 1672 // address. They don't need closer checks. 1673 baseIdx := len(baseNodeColumnHeaders) 1674 1675 // Adding fields that need verification for --ranges flag. 1676 // We have to allow up to 1 unavailable/underreplicated range because 1677 // sometimes we run the `node status` command before the server has fully 1678 // initialized itself and it doesn't consider itself live yet. In such cases, 1679 // there will only be one range covering the entire keyspace because it won't 1680 // have been able to do any splits yet. 1681 if nodeCtx.statusShowRanges || nodeCtx.statusShowAll { 1682 testcases = append(testcases, 1683 testCase{"leader_ranges", baseIdx, 22}, 1684 testCase{"leaseholder_ranges", baseIdx + 1, 22}, 1685 testCase{"ranges", baseIdx + 2, 22}, 1686 testCase{"unavailable_ranges", baseIdx + 3, 1}, 1687 testCase{"underreplicated_ranges", baseIdx + 4, 1}, 1688 ) 1689 baseIdx += len(statusNodesColumnHeadersForRanges) 1690 } 1691 1692 // Adding fields that need verification for --stats flag. 1693 if nodeCtx.statusShowStats || nodeCtx.statusShowAll { 1694 testcases = append(testcases, 1695 testCase{"live_bytes", baseIdx, 100000}, 1696 testCase{"key_bytes", baseIdx + 1, 50000}, 1697 testCase{"value_bytes", baseIdx + 2, 100000}, 1698 testCase{"intent_bytes", baseIdx + 3, 50000}, 1699 testCase{"system_bytes", baseIdx + 4, 50000}, 1700 ) 1701 baseIdx += len(statusNodesColumnHeadersForStats) 1702 } 1703 1704 if nodeCtx.statusShowDecommission || nodeCtx.statusShowAll { 1705 testcases = append(testcases, 1706 testCase{"gossiped_replicas", baseIdx, 30}, 1707 ) 1708 baseIdx++ 1709 } 1710 1711 for _, tc := range testcases { 1712 val, err := strconv.ParseInt(fields[tc.idx], 10, 64) 1713 if err != nil { 1714 t.Errorf("couldn't parse %s '%s': %v", tc.name, fields[tc.idx], err) 1715 continue 1716 } 1717 if val < 0 { 1718 t.Errorf("value for %s (%d) cannot be less than 0", tc.name, val) 1719 continue 1720 } 1721 if val > tc.maxval { 1722 t.Errorf("value for %s (%d) greater than max (%d)", tc.name, val, tc.maxval) 1723 } 1724 } 1725 1726 if nodeCtx.statusShowDecommission || nodeCtx.statusShowAll { 1727 names := []string{"is_decommissioning", "is_draining"} 1728 for i := range names { 1729 if fields[baseIdx] != "false" { 1730 t.Errorf("value for %s (%s) should be false", names[i], fields[baseIdx]) 1731 } 1732 baseIdx++ 1733 } 1734 } 1735 } 1736 1737 var separatorLineExp = regexp.MustCompile(`[\+-]+$`) 1738 1739 func checkSeparatorLine(t *testing.T, s *bufio.Scanner) { 1740 if !s.Scan() { 1741 t.Fatalf("error reading separator line: %s", s.Err()) 1742 } 1743 if !separatorLineExp.MatchString(s.Text()) { 1744 t.Fatalf("separator line not found: %s", s.Text()) 1745 } 1746 } 1747 1748 // checkRecentTime produces a test error if the time is not within the specified number 1749 // of seconds of the given start time. 1750 func checkTimeElapsed(t *testing.T, timeStr string, elapsed time.Duration, start time.Time) { 1751 // Truncate start time, because the CLI currently outputs times with a second-level 1752 // granularity. 1753 start = start.Truncate(time.Second) 1754 tm, err := time.ParseInLocation(localTimeFormat, timeStr, start.Location()) 1755 if err != nil { 1756 t.Errorf("couldn't parse time '%s': %s", timeStr, err) 1757 return 1758 } 1759 end := start.Add(elapsed) 1760 if tm.Before(start) || tm.After(end) { 1761 t.Errorf("time (%s) not within range [%s,%s]", tm, start, end) 1762 } 1763 } 1764 1765 // extractFields extracts the fields from a pretty-printed row of SQL output, 1766 // discarding excess whitespace and column separators. 1767 func extractFields(line string) ([]string, error) { 1768 fields := strings.Split(line, "|") 1769 // fields has two extra entries, one for the empty token to the left of the first 1770 // |, and another empty one to the right of the final |. So, we need to take those 1771 // out. 1772 if a, e := len(fields), len(getStatusNodeHeaders()); a != e { 1773 return nil, errors.Errorf("can't extract fields: # of fields (%d) != expected (%d)", a, e) 1774 } 1775 var r []string 1776 for _, f := range fields { 1777 r = append(r, strings.TrimSpace(f)) 1778 } 1779 return r, nil 1780 } 1781 1782 func TestGenMan(t *testing.T) { 1783 defer leaktest.AfterTest(t)() 1784 1785 // Generate man pages in a temp directory. 1786 manpath, err := ioutil.TempDir("", "TestGenMan") 1787 if err != nil { 1788 t.Fatal(err) 1789 } 1790 defer func() { 1791 if err := os.RemoveAll(manpath); err != nil { 1792 t.Errorf("couldn't remove temporary directory %s: %s", manpath, err) 1793 } 1794 }() 1795 if err := Run([]string{"gen", "man", "--path=" + manpath}); err != nil { 1796 t.Fatal(err) 1797 } 1798 1799 // Ensure we have a sane number of man pages. 1800 count := 0 1801 err = filepath.Walk(manpath, func(path string, info os.FileInfo, err error) error { 1802 if strings.HasSuffix(path, ".1") && !info.IsDir() { 1803 count++ 1804 } 1805 return nil 1806 }) 1807 if err != nil { 1808 t.Fatal(err) 1809 } 1810 if min := 20; count < min { 1811 t.Errorf("number of man pages (%d) < minimum (%d)", count, min) 1812 } 1813 } 1814 1815 func TestGenAutocomplete(t *testing.T) { 1816 defer leaktest.AfterTest(t)() 1817 1818 // Get a unique path to which we can write our autocomplete files. 1819 acdir, err := ioutil.TempDir("", "TestGenAutoComplete") 1820 if err != nil { 1821 t.Fatal(err) 1822 } 1823 defer func() { 1824 if err := os.RemoveAll(acdir); err != nil { 1825 t.Errorf("couldn't remove temporary directory %s: %s", acdir, err) 1826 } 1827 }() 1828 1829 for _, tc := range []struct { 1830 shell string 1831 expErr string 1832 }{ 1833 {shell: ""}, 1834 {shell: "bash"}, 1835 {shell: "zsh"}, 1836 {shell: "bad", expErr: `invalid argument "bad" for "cockroach gen autocomplete"`}, 1837 } { 1838 t.Run("shell="+tc.shell, func(t *testing.T) { 1839 const minsize = 1000 1840 acpath := filepath.Join(acdir, "output-"+tc.shell) 1841 1842 args := []string{"gen", "autocomplete", "--out=" + acpath} 1843 if len(tc.shell) > 0 { 1844 args = append(args, tc.shell) 1845 } 1846 err := Run(args) 1847 if tc.expErr == "" { 1848 if err != nil { 1849 t.Fatal(err) 1850 } 1851 } else { 1852 if !testutils.IsError(err, tc.expErr) { 1853 t.Fatalf("expected error %s, found %v", tc.expErr, err) 1854 } 1855 return 1856 } 1857 1858 info, err := os.Stat(acpath) 1859 if err != nil { 1860 t.Fatal(err) 1861 } 1862 if size := info.Size(); size < minsize { 1863 t.Fatalf("autocomplete file size (%d) < minimum (%d)", size, minsize) 1864 } 1865 }) 1866 } 1867 } 1868 1869 func TestJunkPositionalArguments(t *testing.T) { 1870 defer leaktest.AfterTest(t)() 1871 1872 c := newCLITest(cliTestParams{t: t, noServer: true}) 1873 defer c.cleanup() 1874 1875 for i, test := range []string{ 1876 "start", 1877 "sql", 1878 "gen man", 1879 "gen example-data intro", 1880 } { 1881 const junk = "junk" 1882 line := test + " " + junk 1883 out, err := c.RunWithCapture(line) 1884 if err != nil { 1885 t.Fatalf("%d: %v", i, err) 1886 } 1887 exp := fmt.Sprintf("%s\nERROR: unknown command %q for \"cockroach %s\"\n", line, junk, test) 1888 if exp != out { 1889 t.Errorf("%d: expected:\n%s\ngot:\n%s", i, exp, out) 1890 } 1891 } 1892 } 1893 1894 func TestWorkload(t *testing.T) { 1895 defer leaktest.AfterTest(t)() 1896 1897 c := newCLITest(cliTestParams{noServer: true}) 1898 defer c.cleanup() 1899 1900 out, err := c.RunWithCapture("workload init --help") 1901 if err != nil { 1902 t.Fatal(err) 1903 } 1904 1905 if !strings.Contains(out, `startrek`) { 1906 t.Fatalf(`startrek workload failed to register got: %s`, out) 1907 } 1908 } 1909 1910 func Example_in_memory() { 1911 spec, err := base.NewStoreSpec("type=mem,size=1GiB") 1912 if err != nil { 1913 panic(err) 1914 } 1915 c := newCLITest(cliTestParams{ 1916 storeSpecs: []base.StoreSpec{spec}, 1917 }) 1918 defer c.cleanup() 1919 1920 // Test some sql to ensure that the in memory store is working. 1921 c.RunWithArgs([]string{"sql", "-e", "create database t; create table t.f (x int, y int); insert into t.f values (42, 69)"}) 1922 c.RunWithArgs([]string{"node", "ls"}) 1923 1924 // Output: 1925 // sql -e create database t; create table t.f (x int, y int); insert into t.f values (42, 69) 1926 // INSERT 1 1927 // node ls 1928 // id 1929 // 1 1930 // 1931 } 1932 1933 func Example_pretty_print_numerical_strings() { 1934 c := newCLITest(cliTestParams{}) 1935 defer c.cleanup() 1936 1937 // All strings in pretty-print output should be aligned to left regardless of their contents 1938 c.RunWithArgs([]string{"sql", "-e", "create database t; create table t.t (s string, d string);"}) 1939 c.RunWithArgs([]string{"sql", "-e", "insert into t.t values (e'0', 'positive numerical string')"}) 1940 c.RunWithArgs([]string{"sql", "-e", "insert into t.t values (e'-1', 'negative numerical string')"}) 1941 c.RunWithArgs([]string{"sql", "-e", "insert into t.t values (e'1.0', 'decimal numerical string')"}) 1942 c.RunWithArgs([]string{"sql", "-e", "insert into t.t values (e'aaaaa', 'non-numerical string')"}) 1943 c.RunWithArgs([]string{"sql", "--format=table", "-e", "select * from t.t"}) 1944 1945 // Output: 1946 // sql -e create database t; create table t.t (s string, d string); 1947 // CREATE TABLE 1948 // sql -e insert into t.t values (e'0', 'positive numerical string') 1949 // INSERT 1 1950 // sql -e insert into t.t values (e'-1', 'negative numerical string') 1951 // INSERT 1 1952 // sql -e insert into t.t values (e'1.0', 'decimal numerical string') 1953 // INSERT 1 1954 // sql -e insert into t.t values (e'aaaaa', 'non-numerical string') 1955 // INSERT 1 1956 // sql --format=table -e select * from t.t 1957 // s | d 1958 // --------+---------------------------- 1959 // 0 | positive numerical string 1960 // -1 | negative numerical string 1961 // 1.0 | decimal numerical string 1962 // aaaaa | non-numerical string 1963 // (4 rows) 1964 } 1965 1966 func Example_sqlfmt() { 1967 c := newCLITest(cliTestParams{noServer: true}) 1968 defer c.cleanup() 1969 1970 c.RunWithArgs([]string{"sqlfmt", "-e", ";"}) 1971 c.RunWithArgs([]string{"sqlfmt", "-e", "delete from t"}) 1972 c.RunWithArgs([]string{"sqlfmt", "-e", "delete from t", "-e", "update t set a = 1"}) 1973 c.RunWithArgs([]string{"sqlfmt", "--print-width=10", "-e", "select 1,2,3 from a,b,c;;;select 4"}) 1974 c.RunWithArgs([]string{"sqlfmt", "--print-width=10", "--align", "-e", "select 1,2,3 from a,b,c;;;select 4"}) 1975 c.RunWithArgs([]string{"sqlfmt", "--print-width=10", "--tab-width=2", "--use-spaces", "-e", "select 1,2,3 from a,b,c;;;select 4"}) 1976 c.RunWithArgs([]string{"sqlfmt", "-e", "select (1+2)+3"}) 1977 c.RunWithArgs([]string{"sqlfmt", "--no-simplify", "-e", "select (1+2)+3"}) 1978 1979 // Output: 1980 // sqlfmt -e ; 1981 // sqlfmt -e delete from t 1982 // DELETE FROM t 1983 // sqlfmt -e delete from t -e update t set a = 1 1984 // DELETE FROM t; 1985 // UPDATE t SET a = 1; 1986 // sqlfmt --print-width=10 -e select 1,2,3 from a,b,c;;;select 4 1987 // SELECT 1988 // 1, 1989 // 2, 1990 // 3 1991 // FROM 1992 // a, 1993 // b, 1994 // c; 1995 // SELECT 4; 1996 // sqlfmt --print-width=10 --align -e select 1,2,3 from a,b,c;;;select 4 1997 // SELECT 1, 1998 // 2, 1999 // 3 2000 // FROM a, 2001 // b, 2002 // c; 2003 // SELECT 4; 2004 // sqlfmt --print-width=10 --tab-width=2 --use-spaces -e select 1,2,3 from a,b,c;;;select 4 2005 // SELECT 2006 // 1, 2, 3 2007 // FROM 2008 // a, b, c; 2009 // SELECT 4; 2010 // sqlfmt -e select (1+2)+3 2011 // SELECT 1 + 2 + 3 2012 // sqlfmt --no-simplify -e select (1+2)+3 2013 // SELECT (1 + 2) + 3 2014 } 2015 2016 func Example_dump_no_visible_columns() { 2017 c := newCLITest(cliTestParams{}) 2018 defer c.cleanup() 2019 2020 c.RunWithArgs([]string{"sql", "-e", "create table t(x int); set sql_safe_updates=false; alter table t drop x"}) 2021 c.RunWithArgs([]string{"dump", "defaultdb"}) 2022 2023 // Output: 2024 // sql -e create table t(x int); set sql_safe_updates=false; alter table t drop x 2025 // ALTER TABLE 2026 // dump defaultdb 2027 // CREATE TABLE t (FAMILY "primary" (rowid) 2028 // ); 2029 }