Skip to content

Commit 9468dc3

Browse files
committed
new tests
1 parent 17dfea3 commit 9468dc3

File tree

8 files changed

+124
-3
lines changed

8 files changed

+124
-3
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
exec-ddl
2+
CREATE TABLE sel (a INT, b INT, c INT, d STRING, e STRING) WITH (canary_window = '30s')
3+
----
4+
5+
exec-ddl
6+
ALTER TABLE sel INJECT STATISTICS '[
7+
{
8+
"columns": ["a","b","c","d","e"],
9+
"created_at": "2018-01-01 11:00:20.00000+00:00",
10+
"row_count": 10,
11+
"distinct_count": 10
12+
},
13+
{
14+
"columns": ["a","b","c","d","e"],
15+
"created_at": "2018-01-01 11:00:00.00000+00:00",
16+
"row_count": 20,
17+
"distinct_count": 20
18+
}
19+
]'
20+
----
21+
22+
opt stats-as-of=(2018-01-01 11:00:40.00000+00:00) canary-fraction=0.0
23+
SELECT a FROM sel ORDER BY a
24+
----
25+
sort
26+
├── columns: a:1(int)
27+
├── stats: [rows=20]
28+
├── ordering: +1
29+
└── scan sel
30+
├── columns: a:1(int)
31+
└── stats: [rows=20]
32+
33+
opt stats-as-of=(2018-01-01 11:00:40.00000+00:00) canary-fraction=1.0
34+
SELECT a FROM sel ORDER BY a
35+
----
36+
sort
37+
├── columns: a:1(int)
38+
├── stats: [rows=10]
39+
├── ordering: +1
40+
└── scan sel
41+
├── columns: a:1(int)
42+
└── stats: [rows=10]

pkg/sql/opt/metadata.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,12 @@ var canaryFraction = settings.RegisterFloatSetting(
7171
// should use the "canary path" for statistics.
7272
// This selection is atomic per query.
7373
func canaryRollDice(evalCtx *eval.Context) bool {
74-
threshold := canaryFraction.Get(&evalCtx.Settings.SV)
75-
74+
var threshold float64
75+
if evalCtx.TestingKnobs.CanaryFraction != nil {
76+
threshold = *evalCtx.TestingKnobs.CanaryFraction
77+
} else {
78+
threshold = canaryFraction.Get(&evalCtx.Settings.SV)
79+
}
7680
// If the fraction is 0, never use canary stats. (should we even allow?)
7781
if threshold == 0 {
7882
return false

pkg/sql/opt/testutils/opttester/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ go_library(
4242
"//pkg/sql/parser",
4343
"//pkg/sql/pgwire/pgcode",
4444
"//pkg/sql/pgwire/pgerror",
45+
"//pkg/sql/sem/asof",
4546
"//pkg/sql/sem/eval",
4647
"//pkg/sql/sem/tree",
4748
"//pkg/sql/sem/volatility",

pkg/sql/opt/testutils/opttester/opt_tester.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ import (
5555
"github.com/cockroachdb/cockroach/pkg/sql/parser"
5656
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
5757
"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
58+
"github.com/cockroachdb/cockroach/pkg/sql/sem/asof"
5859
"github.com/cockroachdb/cockroach/pkg/sql/sem/eval"
5960
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
6061
"github.com/cockroachdb/cockroach/pkg/sql/sem/volatility"
@@ -270,6 +271,10 @@ type Flags struct {
270271
// MaxStackBytes specifies the number of bytes to limit the stack size to.
271272
// If it is zero, the stack size has the default Go limit.
272273
MaxStackBytes int
274+
275+
// CanaryFraction determines the fraction of queries that will have use
276+
// canary stats rather than the stable stats.
277+
CanaryFraction *float64
273278
}
274279

275280
// New constructs a new instance of the OptTester for the given SQL statement.
@@ -591,6 +596,7 @@ func (ot *OptTester) RunCommand(tb testing.TB, d *datadriven.TestData) string {
591596

592597
ot.semaCtx.Placeholders = tree.PlaceholderInfo{}
593598

599+
ot.evalCtx.TestingKnobs.CanaryFraction = ot.Flags.CanaryFraction
594600
ot.evalCtx.TestingKnobs.OptimizerCostPerturbation = ot.Flags.PerturbCost
595601
ot.evalCtx.Locality = ot.Flags.Locality
596602
ot.evalCtx.Placeholders = nil
@@ -1180,6 +1186,37 @@ func (f *Flags) Set(arg datadriven.CmdArg) error {
11801186
}
11811187
f.MaxStackBytes = int(bytes)
11821188

1189+
case "canary-fraction":
1190+
if len(arg.Vals) != 1 {
1191+
return fmt.Errorf("require one argument for canary-fraction")
1192+
}
1193+
canaryFrac, err := strconv.ParseFloat(arg.Vals[0], 64)
1194+
if err != nil {
1195+
return errors.Wrapf(err, "parse %s as float for canary fraction", arg.Vals[0])
1196+
}
1197+
f.CanaryFraction = &canaryFrac
1198+
1199+
case "stats-as-of":
1200+
// We set the session variable directly here rather than using the
1201+
// conventional "set" command because stats_as_of only implements
1202+
// SetWithPlanner, not Set. In production, this variable behaves
1203+
// like AS OF SYSTEM TIME, where timestamp evaluation through the
1204+
// planner is more robust. For testing purposes, we can safely
1205+
// bypass this requirement.
1206+
if len(arg.Vals) != 1 {
1207+
return fmt.Errorf("require one argument for stats-as-of")
1208+
}
1209+
input := arg.Vals[0]
1210+
if input == "null" {
1211+
f.evalCtx.SessionData().StatsAsOf = hlc.Timestamp{}
1212+
} else {
1213+
asOfTs, err := asof.Eval(context.Background(), tree.AsOfClause{Expr: tree.NewStrVal(input)}, &tree.SemaContext{}, &f.evalCtx)
1214+
if err != nil {
1215+
return errors.Wrapf(err, "parse %s as timestamp for stats-as-of", input)
1216+
}
1217+
f.evalCtx.SessionData().StatsAsOf = asOfTs.Timestamp
1218+
}
1219+
11831220
default:
11841221
return fmt.Errorf("unknown argument: %s", arg.Key)
11851222
}

pkg/sql/opt/testutils/testcat/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ go_library(
6262
"//pkg/sql/types",
6363
"//pkg/sql/vecindex/vecpb",
6464
"//pkg/sql/vtable",
65+
"//pkg/util/duration",
6566
"//pkg/util/intsets",
6667
"//pkg/util/treeprinter",
6768
"@com_github_cockroachdb_errors//:errors",

pkg/sql/opt/testutils/testcat/alter_table.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ func (tc *Catalog) AlterTable(stmt *tree.AlterTable) {
4747
default:
4848
panic(errors.AssertionFailedf("unsupported constraint type %v", d))
4949
}
50-
50+
case *tree.AlterTableSetStorageParams:
51+
if canaryWindowExpr := t.StorageParams.GetVal(canaryWindowStorageParamKey); canaryWindowExpr != nil {
52+
tab.canaryWindowSize = getDurationFromStrExpr(canaryWindowExpr)
53+
} else {
54+
panic(errors.AssertionFailedf("unsupported AlterTableSetStorageParams stmt with storage params: %v", t.StorageParams))
55+
}
5156
default:
5257
panic(errors.AssertionFailedf("unsupported ALTER TABLE command %T", t))
5358
}

pkg/sql/opt/testutils/testcat/create_table.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"sort"
1414
"strconv"
1515
"strings"
16+
"time"
1617

1718
"github.com/cockroachdb/cockroach/pkg/config/zonepb"
1819
"github.com/cockroachdb/cockroach/pkg/geo/geopb"
@@ -30,7 +31,9 @@ import (
3031
"github.com/cockroachdb/cockroach/pkg/sql/sessiondatapb"
3132
"github.com/cockroachdb/cockroach/pkg/sql/types"
3233
"github.com/cockroachdb/cockroach/pkg/sql/vecindex/vecpb"
34+
"github.com/cockroachdb/cockroach/pkg/util/duration"
3335
"github.com/cockroachdb/cockroach/pkg/util/intsets"
36+
"github.com/cockroachdb/errors"
3437
)
3538

3639
type indexType int
@@ -58,6 +61,8 @@ var uniqueRowIDString = "unique_rowid()"
5861
// referencing side of foreign keys (like it was required before 20.2).
5962
const createFKIndexes = false
6063

64+
const canaryWindowStorageParamKey = "canary_window"
65+
6166
// CreateTable creates a test table from a parsed DDL statement and adds it to
6267
// the catalog. This is intended for testing, and is not a complete (and
6368
// probably not fully correct) implementation. It just has to be "good enough".
@@ -441,6 +446,10 @@ OuterLoop:
441446
}
442447
}
443448

449+
if canaryWindowExpr := stmt.StorageParams.GetVal(canaryWindowStorageParamKey); canaryWindowExpr != nil {
450+
tab.canaryWindowSize = getDurationFromStrExpr(canaryWindowExpr)
451+
}
452+
444453
// Add the new table to the catalog.
445454
tc.AddTable(tab)
446455

@@ -1588,3 +1597,22 @@ func generateDefExprForSerialCol(
15881597
" %s", colName, tableName.String()))
15891598
}
15901599
}
1600+
1601+
func getDurationFromStrExpr(expr tree.Expr) time.Duration {
1602+
if expr == nil {
1603+
return 0
1604+
}
1605+
exprStr, ok := expr.(*tree.StrVal)
1606+
if !ok {
1607+
panic("expr must be of StrVal type")
1608+
}
1609+
d, err := tree.ParseDInterval(duration.IntervalStyle_POSTGRES, exprStr.RawString())
1610+
if err != nil {
1611+
panic(errors.Wrapf(err, "parsing %q", exprStr.RawString()))
1612+
}
1613+
durInSeconds, ok := d.Duration.AsInt64()
1614+
if !ok {
1615+
panic("unable to convert duration to seconds")
1616+
}
1617+
return time.Duration(durInSeconds) * time.Second
1618+
}

pkg/sql/sem/eval/testing_knobs.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ type TestingKnobs struct {
4141
// We use clusterversion.Key rather than a roachpb.Version because it will be used
4242
// to get initial values to use during bootstrap.
4343
TenantLogicalVersionKeyOverride clusterversion.Key
44+
// CanaryFraction determines the portion of queries that would use canary
45+
// stats for planning rather than using the stable stats.
46+
CanaryFraction *float64
4447
}
4548

4649
var _ base.ModuleTestingKnobs = &TestingKnobs{}

0 commit comments

Comments
 (0)