-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
logic.go
3616 lines (3351 loc) · 113 KB
/
logic.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Copyright 2015 The Cockroach Authors.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
package logictest
import (
"bufio"
"bytes"
"context"
"crypto/md5"
gosql "database/sql"
"flag"
"fmt"
gobuild "go/build"
"io"
"math"
"math/rand"
"net/url"
"os"
"path/filepath"
"reflect"
"regexp"
"runtime"
"runtime/debug"
"runtime/trace"
"sort"
"strconv"
"strings"
"testing"
"text/tabwriter"
"time"
"unicode/utf8"
"github.com/cockroachdb/cockroach/pkg/base"
"github.com/cockroachdb/cockroach/pkg/build"
"github.com/cockroachdb/cockroach/pkg/clusterversion"
"github.com/cockroachdb/cockroach/pkg/kv/kvserver"
"github.com/cockroachdb/cockroach/pkg/migration/migrationmanager"
"github.com/cockroachdb/cockroach/pkg/roachpb"
"github.com/cockroachdb/cockroach/pkg/security"
"github.com/cockroachdb/cockroach/pkg/server"
"github.com/cockroachdb/cockroach/pkg/settings/cluster"
"github.com/cockroachdb/cockroach/pkg/sql"
"github.com/cockroachdb/cockroach/pkg/sql/execinfra"
"github.com/cockroachdb/cockroach/pkg/sql/parser"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
"github.com/cockroachdb/cockroach/pkg/sql/randgen"
"github.com/cockroachdb/cockroach/pkg/sql/row"
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
"github.com/cockroachdb/cockroach/pkg/sql/sessiondata"
"github.com/cockroachdb/cockroach/pkg/sql/stats"
"github.com/cockroachdb/cockroach/pkg/testutils"
"github.com/cockroachdb/cockroach/pkg/testutils/floatcmp"
"github.com/cockroachdb/cockroach/pkg/testutils/physicalplanutils"
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
"github.com/cockroachdb/cockroach/pkg/testutils/skip"
"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
"github.com/cockroachdb/cockroach/pkg/util"
"github.com/cockroachdb/cockroach/pkg/util/envutil"
"github.com/cockroachdb/cockroach/pkg/util/log"
"github.com/cockroachdb/cockroach/pkg/util/randutil"
"github.com/cockroachdb/cockroach/pkg/util/syncutil"
"github.com/cockroachdb/cockroach/pkg/util/timeutil"
"github.com/cockroachdb/errors"
"github.com/cockroachdb/errors/oserror"
"github.com/lib/pq"
"github.com/stretchr/testify/require"
)
// This file is home to TestLogic, a general-purpose engine for
// running SQL logic tests.
//
// TestLogic implements the infrastructure that runs end-to-end tests
// against CockroachDB's SQL layer. It is typically used to run
// CockroachDB's own tests (stored in the `testdata` directory) during
// development and CI, and a subset of SQLite's "Sqllogictest" during
// nightly CI runs. However, any test input can be specified via
// command-line flags (see below).
//
// In a nutshell, TestLogic reads one or more test input files
// containing sequences of SQL statements and queries. Each input file
// is meant to test a feature group. The reason why tests can/should
// be split across multiple files is that each test input file gets
// its own fresh, empty database.
//
// Input files for unit testing are stored alongside the source code
// in the `testdata` subdirectory. The input files for the larger
// TestSqlLiteLogic tests are stored in a separate repository.
//
// The test input is expressed using a domain-specific language, called
// Test-Script, defined by SQLite's "Sqllogictest". The official home
// of Sqllogictest and Test-Script is
// https://www.sqlite.org/sqllogictest/
//
// (the TestSqlLiteLogic test uses a fork of the Sqllogictest test files;
// its input files are hosted at https://github.com/cockroachdb/sqllogictest)
//
// Test-Script is line-oriented. It supports both statements which
// generate no result rows, and queries that produce result rows. The
// result of queries can be checked either using an explicit reference
// output in the test file, or using the expected row count and a hash
// of the expected output. A test can also check for expected column
// names for query results, or expected errors.
//
// Logic tests can start with a directive as follows:
//
// # LogicTest: local fakedist
//
// This directive lists configurations; the test is run once in each
// configuration (in separate subtests). The configurations are defined by
// logicTestConfigs. If the directive is missing, the test is run in the
// default configuration.
//
// The directive also supports blocklists, i.e. running all specified
// configurations apart from a blocklisted configuration:
//
// # LogicTest: default-configs !3node-tenant
//
// If a blocklist is specified without an accompanying configuration, the
// default config is assumed. i.e., the following directive is equivalent to the
// one above:
//
// # LogicTest: !3node-tenant
//
// An issue can optionally be specified as the reason for blocking a given
// configuration:
//
// # LogicTest: !3node-tenant(<issue number>)
//
// A link to the issue will be printed out if the -print-blocklist-issues flag
// is specified.
//
// There is a special blocklist directive '!metamorphic' that skips the whole
// test when TAGS=metamorphic is specified for the logic test invocation.
// NOTE: metamorphic directive takes precedence over all other directives.
//
// The Test-Script language is extended here for use with CockroachDB. The
// supported directives are:
//
// - statement ok
// Runs the statement that follows and expects success. For
// example:
// statement ok
// CREATE TABLE kv (k INT PRIMARY KEY, v INT)
//
// - statement notice <regexp>
// Like "statement ok" but expects a notice that matches the given regexp.
//
// - statement count N
// Like "statement ok" but expect a final RowsAffected count of N.
// example:
// statement count 2
// INSERT INTO kv VALUES (1,2), (2,3)
//
// - statement error <regexp>
// Runs the statement that follows and expects an
// error that matches the given regexp.
//
// - query <typestring> <options> <label>
// Runs the query that follows and verifies the results (specified after the
// query and a ---- separator). Example:
// query I
// SELECT 1, 2
// ----
// 1 2
//
// The type string specifies the number of columns and their types:
// - T for text; also used for various types which get converted
// to string (arrays, timestamps, etc.).
// - I for integer
// - F for floating point (matches 15 significant decimal digits,
// https://www.postgresql.org/docs/9.0/datatype-numeric.html)
// - R for decimal
// - B for boolean
// - O for oid
//
// Options are comma separated strings from the following:
// - nosort (default)
// - rowsort: sorts both the returned and the expected rows assuming one
// white-space separated word per column.
// - valuesort: sorts all values on all rows as one big set of
// strings (for both the returned and the expected rows).
// - partialsort(x,y,..): performs a partial sort on both the
// returned and the expected results, preserving the relative
// order of rows that differ on the specified columns
// (1-indexed); for results that are expected to be already
// ordered according to these columns. See partialSort() for
// more information.
// - colnames: column names are verified (the expected column names
// are the first line in the expected results).
// - retry: if the expected results do not match the actual results, the
// test will be retried with exponential backoff up to some maximum
// duration. If the test succeeds at any time during that period, it
// is considered successful. Otherwise, it is a failure. See
// testutils.SucceedsSoon for more information. If run with the
// -rewrite flag, inserts a 500ms sleep before executing the query
// once.
// - kvtrace: runs the query and compares against the results of the
// kv operations trace of the query. kvtrace optionally accepts
// arguments of the form kvtrace(op,op,...). Op is one of
// the accepted k/v arguments such as 'CPut', 'Scan' etc. It
// also accepts arguments of the form 'prefix=...'. For example,
// if kvtrace(CPut,Del,prefix=/Table/54,prefix=/Table/55), the
// results will be filtered to contain messages starting with
// CPut /Table/54, CPut /Table/55, Del /Table/54, Del /Table/55.
// Cannot be combined with noticetrace.
// - noticetrace: runs the query and compares only the notices that
// appear. Cannot be combined with kvtrace.
//
// The label is optional. If specified, the test runner stores a hash
// of the results of the query under the given label. If the label is
// reused, the test runner verifies that the results are the
// same. This can be used to verify that two or more queries in the
// same test script that are logically equivalent always generate the
// same output. If a label is provided, expected results don't need to
// be provided (in which case there should be no ---- separator).
//
// - query error <regexp>
// Runs the query that follows and expects an error
// that matches the given regexp.
//
// - repeat <number>
// It causes the following `statement` or `query` to be repeated the given
// number of times. For example:
// repeat 50
// statement ok
// INSERT INTO T VALUES ((SELECT MAX(k+1) FROM T))
//
// - let $varname
// Executes the query that follows (expecting a single result) and remembers
// the result (as a string) for later use. Any `$varname` occurrences in
// subsequent statements or queries are replaced with the result. The
// variable name must start with a letter, and subsequent characters must be
// letters, digits, or underscores. Example:
// let $foo
// SELECT MAX(v) FROM kv
//
// statement ok
// SELECT * FROM kv WHERE v = $foo
//
// - sleep <duration>
// Introduces a sleep period. Example: sleep 2s
//
// - user <username>
// Changes the user for subsequent statements or queries.
//
// - skipif <mysql/mssql/postgresql/cockroachdb>
// Skips the following `statement` or `query` if the argument is postgresql
// or cockroachdb.
//
// - onlyif <mysql/mssql/postgresql/cockroachdb>
// Skips the following `statement` or query if the argument is not postgresql
// or cockroachdb.
//
// - traceon <file>
// Enables tracing to the given file.
//
// - traceoff
// Stops tracing.
//
// - kv-batch-size <num>
// Limits the kvfetcher batch size; it can be used to trigger certain error
// conditions or corner cases around limited batches.
//
// - subtest <testname>
// Defines the start of a subtest. The subtest is any number of statements
// that occur after this command until the end of file or the next subtest
// command.
//
// The overall architecture of TestLogic is as follows:
//
// - TestLogic() selects the input files and instantiates
// a `logicTest` object for each input file.
//
// - logicTest.run() sets up a new database.
// - logicTest.processTestFile() runs all tests from that input file.
//
// - each individual test in an input file is instantiated either as a
// logicStatement or logicQuery object. These are then processed by
// either logicTest.execStatement() or logicTest.execQuery().
//
// TestLogic has three main parameter groups:
//
// - Which input files are processed.
// - How and when to stop when tests fail.
// - Which results are reported.
//
// The parameters are typically set using the TESTFLAGS `make`
// parameter, as in:
//
// make test PKG=./pkg/sql TESTS=TestLogic TESTFLAGS='....'
//
// Input file selection:
//
// -d <glob> selects all files matching <glob>. This can mix and
// match wildcards (*/?) or groups like {a,b,c}.
//
// -bigtest enable the long-running SqlLiteLogic test, which uses files from
// CockroachDB's fork of Sqllogictest.
//
// Configuration:
//
// -config name[,name2,...] customizes the test cluster configuration for test
// files that lack LogicTest directives; must be one
// of `logicTestConfigs`.
// Example:
// -config local,fakedist
//
// Error mode:
//
// -max-errors N stop testing after N errors have been
// encountered. Default 1. Set to 0 for no limit.
//
// -allow-prepare-fail
// tolerate / ignore errors that occur during query
// preparation. With -allow-prepare-fail you can
// indicate that it is OK as long as the database
// reports early to the client that it does not support
// a given query. Errors are still reported if queries
// fail during execution only or if a statement fails.
//
// -flex-types tolerate when a result column is produced with a
// different numeric type than the one expected by the
// test. This enables reusing tests designed for
// databases with slightly different typing semantics.
//
// Test output:
//
// -v (or -test.v if the test is compiled as a standalone
// binary). Go `testing`'s `verbose` flag.
//
// The output generated by the following flags is suppressed unless
// either -v is given or a test fails.
//
// -show-sql show SQL statements/queries immediately before they
// are tested. This can be useful for example when
// troubleshooting errors that cause the database/test
// to stop before the test completes. When -show-sql
// is set, individual test results are annoted with
// either "OK" (test passed as expected), "XFAIL"
// (expected failure, test failed as expected), or
// "FAIL" to indicate an unexpected/undesired test
// failure.
//
// -error-summary produces a report organized by error message
// of all queries that have caused that error. Useful
// with -allow-prepare-fail and/or -flex-types.
//
// -full-messages by default -error-summary shortens error messages
// and queries so they fit in a moderately large
// terminal screen. With this parameter, the
// full text of errors and queries is printed.
//
// Suggested use:
//
// - For validation testing: just -d or -bigtest.
// - For compatibility testing: add -allow-prepare-fail -flex-types.
// - For troubleshooting / analysis: add -v -show-sql -error-summary.
var (
resultsRE = regexp.MustCompile(`^(\d+)\s+values?\s+hashing\s+to\s+([0-9A-Fa-f]+)$`)
noticeRE = regexp.MustCompile(`^statement\s+notice\s+(.*)$`)
errorRE = regexp.MustCompile(`^(?:statement|query)\s+error\s+(?:pgcode\s+([[:alnum:]]+)\s+)?(.*)$`)
varRE = regexp.MustCompile(`\$[a-zA-Z][a-zA-Z_0-9]*`)
// Input selection
logictestdata = flag.String("d", "", "glob that selects subset of files to run")
bigtest = flag.Bool("bigtest", false, "enable the long-running SqlLiteLogic test")
overrideConfig = flag.String(
"config", "",
"sets the test cluster configuration; comma-separated values",
)
// Testing mode
maxErrs = flag.Int(
"max-errors", 1,
"stop processing input files after this number of errors (set to 0 for no limit)",
)
allowPrepareFail = flag.Bool(
"allow-prepare-fail", false, "tolerate unexpected errors when preparing a query",
)
flexTypes = flag.Bool(
"flex-types", false,
"do not fail when a test expects a column of a numeric type but the query provides another type",
)
// Output parameters
showSQL = flag.Bool("show-sql", false,
"print the individual SQL statement/queries before processing",
)
printErrorSummary = flag.Bool("error-summary", false,
"print a per-error summary of failing queries at the end of testing, "+
"when -allow-prepare-fail is set",
)
fullMessages = flag.Bool("full-messages", false,
"do not shorten the error or SQL strings when printing the summary for -allow-prepare-fail "+
"or -flex-types.",
)
rewriteResultsInTestfiles = flag.Bool(
"rewrite", false,
"ignore the expected results and rewrite the test files with the actual results from this "+
"run. Used to update tests when a change affects many cases; please verify the testfile "+
"diffs carefully!",
)
rewriteSQL = flag.Bool(
"rewrite-sql", false,
"pretty-reformat the SQL queries. Only use this incidentally when importing new tests. "+
"beware! some tests INTEND to use non-formatted SQL queries (e.g. case sensitivity). "+
"do not bluntly apply!",
)
sqlfmtLen = flag.Int("line-length", tree.DefaultPrettyCfg().LineWidth,
"target line length when using -rewrite-sql")
disableOptRuleProbability = flag.Float64(
"disable-opt-rule-probability", 0,
"disable transformation rules in the cost-based optimizer with the given probability.")
optimizerCostPerturbation = flag.Float64(
"optimizer-cost-perturbation", 0,
"randomly perturb the estimated cost of each expression in the query tree by at most the "+
"given fraction for the purpose of creating alternate query plans in the optimizer.")
printBlocklistIssues = flag.Bool(
"print-blocklist-issues", false,
"for any test files that contain a blocklist directive, print a link to the associated issue",
)
)
type testClusterConfig struct {
// name is the name of the config (used for subtest names).
name string
numNodes int
// TODO(asubiotto): The fake span resolver does not currently play well with
// contention events and tracing (see #61438).
useFakeSpanResolver bool
// if non-empty, overrides the default distsql mode.
overrideDistSQLMode string
// if non-empty, overrides the default vectorize mode.
overrideVectorize string
// if non-empty, overrides the default automatic statistics mode.
overrideAutoStats string
// if non-empty, overrides the default experimental DistSQL planning mode.
overrideExperimentalDistSQLPlanning string
// if set, queries using distSQL processors or vectorized operators that can
// fall back to disk do so immediately, using only their disk-based
// implementation.
sqlExecUseDisk bool
// if set, enables DistSQL metadata propagation tests.
distSQLMetadataTestEnabled bool
// if set and the -test.short flag is passed, skip this config.
skipShort bool
// If not empty, bootstrapVersion controls what version the cluster will be
// bootstrapped at.
bootstrapVersion roachpb.Version
// If not empty, binaryVersion is used to set what the Server will consider
// to be the binary version.
binaryVersion roachpb.Version
disableUpgrade bool
// If true, a sql tenant server will be started and pointed at a node in the
// cluster. Connections on behalf of the logic test will go to that tenant.
useTenant bool
// isCCLConfig should be true for any config that can only be run with a CCL
// binary.
isCCLConfig bool
// localities is set if nodes should be set to a particular locality.
// Nodes are 1-indexed.
localities map[int]roachpb.Locality
}
const threeNodeTenantConfigName = "3node-tenant"
var multiregion9node3region3azsLocalities = map[int]roachpb.Locality{
1: {
Tiers: []roachpb.Tier{
{Key: "region", Value: "ap-southeast-2"},
{Key: "availability-zone", Value: "ap-az1"},
},
},
2: {
Tiers: []roachpb.Tier{
{Key: "region", Value: "ap-southeast-2"},
{Key: "availability-zone", Value: "ap-az2"},
},
},
3: {
Tiers: []roachpb.Tier{
{Key: "region", Value: "ap-southeast-2"},
{Key: "availability-zone", Value: "ap-az3"},
},
},
4: {
Tiers: []roachpb.Tier{
{Key: "region", Value: "ca-central-1"},
{Key: "availability-zone", Value: "ca-az1"},
},
},
5: {
Tiers: []roachpb.Tier{
{Key: "region", Value: "ca-central-1"},
{Key: "availability-zone", Value: "ca-az2"},
},
},
6: {
Tiers: []roachpb.Tier{
{Key: "region", Value: "ca-central-1"},
{Key: "availability-zone", Value: "ca-az3"},
},
},
7: {
Tiers: []roachpb.Tier{
{Key: "region", Value: "us-east-1"},
{Key: "availability-zone", Value: "us-az1"},
},
},
8: {
Tiers: []roachpb.Tier{
{Key: "region", Value: "us-east-1"},
{Key: "availability-zone", Value: "us-az2"},
},
},
9: {
Tiers: []roachpb.Tier{
{Key: "region", Value: "us-east-1"},
{Key: "availability-zone", Value: "us-az3"},
},
},
}
// logicTestConfigs contains all possible cluster configs. A test file can
// specify a list of configs they run on in a file-level comment like:
// # LogicTest: default distsql
// The test is run once on each configuration (in different subtests).
// If no configs are indicated, the default one is used (unless overridden
// via -config).
var logicTestConfigs = []testClusterConfig{
{
name: "local",
numNodes: 1,
overrideDistSQLMode: "off",
overrideAutoStats: "false",
},
{
name: "local-vec-off",
numNodes: 1,
overrideDistSQLMode: "off",
overrideAutoStats: "false",
overrideVectorize: "off",
},
{
name: "[email protected]",
numNodes: 1,
overrideDistSQLMode: "off",
overrideAutoStats: "false",
bootstrapVersion: roachpb.Version{Major: 1},
binaryVersion: roachpb.Version{Major: 1, Minor: 1},
disableUpgrade: true,
},
{
name: "local-mixed-20.2-21.1",
numNodes: 1,
overrideDistSQLMode: "off",
overrideAutoStats: "false",
bootstrapVersion: roachpb.Version{Major: 20, Minor: 2},
binaryVersion: roachpb.Version{Major: 21, Minor: 1},
disableUpgrade: true,
},
{
name: "local-spec-planning",
numNodes: 1,
overrideDistSQLMode: "off",
overrideAutoStats: "false",
overrideExperimentalDistSQLPlanning: "on",
},
{
name: "fakedist",
numNodes: 3,
useFakeSpanResolver: true,
overrideDistSQLMode: "on",
overrideAutoStats: "false",
},
{
name: "fakedist-vec-off",
numNodes: 3,
useFakeSpanResolver: true,
overrideDistSQLMode: "on",
overrideAutoStats: "false",
overrideVectorize: "off",
},
{
name: "fakedist-metadata",
numNodes: 3,
useFakeSpanResolver: true,
overrideDistSQLMode: "on",
overrideAutoStats: "false",
distSQLMetadataTestEnabled: true,
skipShort: true,
},
{
name: "fakedist-disk",
numNodes: 3,
useFakeSpanResolver: true,
overrideDistSQLMode: "on",
overrideAutoStats: "false",
sqlExecUseDisk: true,
skipShort: true,
},
{
name: "fakedist-spec-planning",
numNodes: 3,
useFakeSpanResolver: true,
overrideDistSQLMode: "on",
overrideAutoStats: "false",
overrideExperimentalDistSQLPlanning: "on",
},
{
name: "5node",
numNodes: 5,
overrideDistSQLMode: "on",
overrideAutoStats: "false",
},
{
name: "5node-metadata",
numNodes: 5,
overrideDistSQLMode: "on",
overrideAutoStats: "false",
distSQLMetadataTestEnabled: true,
skipShort: true,
},
{
name: "5node-disk",
numNodes: 5,
overrideDistSQLMode: "on",
overrideAutoStats: "false",
sqlExecUseDisk: true,
skipShort: true,
},
{
name: "5node-spec-planning",
numNodes: 5,
overrideDistSQLMode: "on",
overrideAutoStats: "false",
overrideExperimentalDistSQLPlanning: "on",
},
{
// 3node-tenant is a config that runs the test as a SQL tenant. This config
// can only be run with a CCL binary, so is a noop if run through the normal
// logictest command.
// To run a logic test with this config as a directive, run:
// make test PKG=./pkg/ccl/logictestccl TESTS=TestTenantLogic//<test_name>
name: threeNodeTenantConfigName,
numNodes: 3,
// overrideAutoStats will disable automatic stats on the cluster this tenant
// is connected to.
overrideAutoStats: "false",
useTenant: true,
isCCLConfig: true,
},
// Regions and zones below are named deliberately, and contain "-"'s to be reflective
// of the naming convention in public clouds. "-"'s are handled differently in SQL
// (they're double double quoted) so we explicitly test them here to ensure that
// the multi-region code handles them correctly.
{
name: "multiregion-invalid-locality",
numNodes: 3,
overrideAutoStats: "false",
localities: map[int]roachpb.Locality{
1: {
Tiers: []roachpb.Tier{
{Key: "invalid-region-setup", Value: "test1"},
{Key: "availability-zone", Value: "test1-az1"},
},
},
2: {
Tiers: []roachpb.Tier{},
},
3: {
Tiers: []roachpb.Tier{
{Key: "region", Value: "test1"},
{Key: "availability-zone", Value: "test1-az3"},
},
},
},
},
{
name: "multiregion-9node-3region-3azs",
numNodes: 9,
overrideAutoStats: "false",
localities: multiregion9node3region3azsLocalities,
},
{
name: "multiregion-9node-3region-3azs-vec-off",
numNodes: 9,
overrideAutoStats: "false",
localities: multiregion9node3region3azsLocalities,
overrideVectorize: "off",
},
}
// An index in the above slice.
type logicTestConfigIdx int
// A collection of configurations.
type configSet []logicTestConfigIdx
var logicTestConfigIdxToName = make(map[logicTestConfigIdx]string)
func init() {
for i, cfg := range logicTestConfigs {
logicTestConfigIdxToName[logicTestConfigIdx(i)] = cfg.name
}
}
func parseTestConfig(names []string) configSet {
ret := make(configSet, len(names))
for i, name := range names {
idx, ok := findLogicTestConfig(name)
if !ok {
panic(fmt.Errorf("unknown config %s", name))
}
ret[i] = idx
}
return ret
}
var (
// defaultConfigName is a special alias for the default configs.
defaultConfigName = "default-configs"
defaultConfigNames = []string{
"local",
"local-vec-off",
"local-spec-planning",
"fakedist",
"fakedist-vec-off",
"fakedist-metadata",
"fakedist-disk",
"fakedist-spec-planning",
}
// fiveNodeDefaultConfigName is a special alias for all 5 node configs.
fiveNodeDefaultConfigName = "5node-default-configs"
fiveNodeDefaultConfigNames = []string{
"5node",
"5node-metadata",
"5node-disk",
"5node-spec-planning",
}
defaultConfig = parseTestConfig(defaultConfigNames)
fiveNodeDefaultConfig = parseTestConfig(fiveNodeDefaultConfigNames)
)
func findLogicTestConfig(name string) (logicTestConfigIdx, bool) {
for i, cfg := range logicTestConfigs {
if cfg.name == name {
return logicTestConfigIdx(i), true
}
}
return -1, false
}
// lineScanner handles reading from input test files.
type lineScanner struct {
*bufio.Scanner
line int
skip bool
}
func newLineScanner(r io.Reader) *lineScanner {
return &lineScanner{
Scanner: bufio.NewScanner(r),
line: 0,
}
}
func (l *lineScanner) Scan() bool {
ok := l.Scanner.Scan()
if ok {
l.line++
}
return ok
}
func (l *lineScanner) Text() string {
return l.Scanner.Text()
}
// logicStatement represents a single statement test in Test-Script.
type logicStatement struct {
// file and line number of the test.
pos string
// SQL string to be sent to the database.
sql string
// expected notice, if any.
expectNotice string
// expected error, if any. "" indicates the statement should
// succeed.
expectErr string
// expected pgcode for the error, if any. "" indicates the
// test does not check the pgwire error code.
expectErrCode string
// expected rows affected count. -1 to avoid testing this.
expectCount int64
}
// readSQL reads the lines of a SQL statement or query until the first blank
// line or (optionally) a "----" separator, and sets stmt.sql.
//
// If a separator is found, returns separator=true. If a separator is found when
// it is not expected, returns an error.
func (ls *logicStatement) readSQL(
t *logicTest, s *lineScanner, allowSeparator bool,
) (separator bool, _ error) {
var buf bytes.Buffer
hasVars := false
for s.Scan() {
line := s.Text()
if !*rewriteSQL {
t.emit(line)
}
substLine := t.substituteVars(line)
if line != substLine {
hasVars = true
line = substLine
}
if line == "" {
break
}
if line == "----" {
separator = true
if ls.expectNotice != "" {
return false, errors.Errorf(
"%s: invalid ---- separator after a statement expecting a notice: %s",
ls.pos, ls.expectNotice,
)
}
if ls.expectErr != "" {
return false, errors.Errorf(
"%s: invalid ---- separator after a statement or query expecting an error: %s",
ls.pos, ls.expectErr,
)
}
if !allowSeparator {
return false, errors.Errorf("%s: unexpected ---- separator", ls.pos)
}
break
}
fmt.Fprintln(&buf, line)
}
ls.sql = strings.TrimSpace(buf.String())
if *rewriteSQL {
if !hasVars {
newSyntax, err := func(inSql string) (string, error) {
// Can't rewrite the SQL otherwise because the vars make it invalid.
stmtList, err := parser.Parse(inSql)
if err != nil {
if ls.expectErr != "" {
// Maybe a parse error was expected. Simply do not rewrite.
return inSql, nil
}
return "", errors.Wrapf(err, "%s: error while parsing SQL for reformat:\n%s", ls.pos, ls.sql)
}
var newSyntax bytes.Buffer
pcfg := tree.DefaultPrettyCfg()
pcfg.LineWidth = *sqlfmtLen
pcfg.Simplify = false
pcfg.UseTabs = false
for i := range stmtList {
if i > 0 {
fmt.Fprintln(&newSyntax, ";")
}
fmt.Fprint(&newSyntax, pcfg.Pretty(stmtList[i].AST))
}
return newSyntax.String(), nil
}(ls.sql)
if err != nil {
return false, err
}
ls.sql = newSyntax
}
t.emit(ls.sql)
if separator {
t.emit("----")
} else {
t.emit("")
}
}
return separator, nil
}
// logicSorter sorts result rows (or not) depending on Test-Script's
// sorting option for a "query" test. See the implementation of the
// "query" directive below for details.
type logicSorter func(numCols int, values []string)
type rowSorter struct {
numCols int
numRows int
values []string
}
func (r rowSorter) row(i int) []string {
return r.values[i*r.numCols : (i+1)*r.numCols]
}
func (r rowSorter) Len() int {
return r.numRows
}
func (r rowSorter) Less(i, j int) bool {
a := r.row(i)
b := r.row(j)
for k := range a {
if a[k] < b[k] {
return true
}
if a[k] > b[k] {
return false
}
}
return false
}
func (r rowSorter) Swap(i, j int) {
a := r.row(i)
b := r.row(j)
for i := range a {
a[i], b[i] = b[i], a[i]
}
}
func rowSort(numCols int, values []string) {
sort.Sort(rowSorter{
numCols: numCols,
numRows: len(values) / numCols,
values: values,
})
}
func valueSort(numCols int, values []string) {
sort.Strings(values)
}
// partialSort rearranges consecutive rows that have the same values on a
// certain set of columns (orderedCols).
//
// More specifically: rows are partitioned into groups of consecutive rows that
// have the same values for columns orderedCols. Inside each group, the rows are
// sorted. The relative order of any two rows that differ on orderedCols is
// preserved.
//
// This is useful when comparing results for a statement that guarantees a
// partial, but not a total order. Consider:
//
// SELECT a, b FROM ab ORDER BY a
//
// Some possible outputs for the same data:
// 1 2 1 5 1 2
// 1 5 1 4 1 4
// 1 4 or 1 2 or 1 5
// 2 3 2 2 2 3
// 2 2 2 3 2 2
//
// After a partialSort with orderedCols = {0} all become:
// 1 2
// 1 4
// 1 5
// 2 2
// 2 3
//
// An incorrect output like:
// 1 5 1 2
// 1 2 1 5
// 2 3 becomes: 2 2
// 2 2 2 3
// 1 4 1 4
// and it is detected as different.
func partialSort(numCols int, orderedCols []int, values []string) {
// We use rowSorter here only as a container.
c := rowSorter{
numCols: numCols,
numRows: len(values) / numCols,
values: values,
}
// Sort the group of rows [rowStart, rowEnd).
sortGroup := func(rowStart, rowEnd int) {
sort.Sort(rowSorter{
numCols: numCols,
numRows: rowEnd - rowStart,
values: values[rowStart*numCols : rowEnd*numCols],
})
}
groupStart := 0
for rIdx := 1; rIdx < c.numRows; rIdx++ {