Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
25297: build: add roachtests to be run on every PR r=benesch,petermattis a=danhhz

Release note: None

25434: opt: Change how Any operator is represented and flattened r=andy-kimball a=andy-kimball

The Any operator currently takes a single input rowset, and expects
it to return a single boolean column. The hoister flattens the Any
operator by testing and projecting that boolean column. The problem
is that this representation cannot easily be decorrelated. Example:

   z = ANY(SELECT x FROM xy)

This is currently represented as:

  (Any (Project xy [ z=x ]))

The z=x projection field cannot easily be hoisted over a left join.
This commit uses an alternate representation:

  (Any xy z EqOp)

The new representation keeps the input, scalar, and comparison op
components separate, so they can be combined in ways that it easier
to decorrelate.

25456: storage: fix deadlock in consistency queue r=bdarnell,a-robinson a=tschottdorf

When `CheckConsistency` returns an error, the queue checks whether the
store is draining to decide whether the error is worth logging.

Unfortunately this check was incorrect and would block until the store
actually started draining.

A toy example of this problem is below (this will deadlock). The dual
return form of chan receive isn't non-blocking -- the second parameter
indicates whether the received value corresponds to a closing of the
channel.

Switch to a `select` instead.

```go
package main

import (
	"fmt"
)

func main() {
	ch := make(chan struct{})
	_, ok := <-ch
	fmt.Println(ok)
}
```

Touches #21824.

Release note (bug fix): Prevent the consistency checker from
deadlocking. This would previously manifest itself as a steady number of
replicas queued for consistency checking on one or more nodes and would
resolve by restarting the affected nodes.

Co-authored-by: Daniel Harrison <[email protected]>
Co-authored-by: Andrew Kimball <[email protected]>
Co-authored-by: Tobias Schottdorf <[email protected]>
  • Loading branch information
4 people committed May 14, 2018
4 parents 66daa4e + 6ad8fe4 + ace2736 + 7055cc8 commit 17ed382
Show file tree
Hide file tree
Showing 43 changed files with 1,935 additions and 1,100 deletions.
34 changes: 34 additions & 0 deletions build/teamcity-local-roachtest.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash

set -euo pipefail

source "$(dirname "${0}")/teamcity-support.sh"

tc_start_block "Prepare environment"
run mkdir -p artifacts
maybe_ccache
tc_end_block "Prepare environment"

tc_start_block "Install roachprod"
run build/builder.sh go get -u -v github.com/cockroachdb/roachprod
tc_end_block "Install roachprod"

tc_start_block "Compile CockroachDB"
run build/builder.sh make build
tc_end_block "Compile CockroachDB"

tc_start_block "Compile workload/roachtest"
run build/builder.sh make bin/workload bin/roachtest
tc_end_block "Compile workload/roachtest"

tc_start_block "Run local roachtests"
# TODO(dan): Run kv/splits as a proof of concept of running roachtest on every
# PR. After we're sure this is stable, curate a suite of the tests that work
# locally.
run build/builder.sh ./bin/roachtest run kv/splits \
--local \
--cockroach "cockroach" \
--workload "bin/workload" \
--artifacts artifacts \
--teamcity
tc_end_block "Run local roachtests"
35 changes: 23 additions & 12 deletions pkg/sql/opt/memo/logical_props_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,27 @@ func (b *logicalPropsBuilder) buildProjectProps(ev ExprView) props.Logical {

inputProps := ev.childGroup(0).logical.Relational
projectionProps := ev.childGroup(1).logical.Scalar
projections := ev.Child(1)
projectionColList := projections.Private().(opt.ColList)

// Use output columns from projection list.
logical.Relational.OutputCols = opt.ColListToSet(ev.Child(1).Private().(opt.ColList))
logical.Relational.OutputCols = opt.ColListToSet(projectionColList)

// Inherit not null columns from input, but only use those that are also
// output columns.
logical.Relational.NotNullCols = inputProps.NotNullCols
filterNullCols(logical.Relational)
logical.Relational.NotNullCols = inputProps.NotNullCols.Copy()
logical.Relational.NotNullCols.IntersectionWith(logical.Relational.OutputCols)

// Also add any column that projects a constant value, since the optimizer
// sometimes constructs these in order to guarantee a not-null column.
for i := 0; i < projections.ChildCount(); i++ {
child := projections.Child(i)
if child.IsConstValue() {
if ExtractConstDatum(child) != tree.DNull {
logical.Relational.NotNullCols.Add(int(projectionColList[i]))
}
}
}

// Any outer columns from the projection list that are not bound by the input
// columns are outer columns from the Project expression, in addition to any
Expand Down Expand Up @@ -532,6 +545,13 @@ func (b *logicalPropsBuilder) buildScalarProps(ev ExprView) props.Logical {
case opt.SubqueryOp, opt.ExistsOp, opt.AnyOp:
// Inherit outer columns from input query.
logical.Scalar.OuterCols = ev.childGroup(0).logical.Relational.OuterCols

// Any has additional scalar value that can contain outer references.
if ev.Operator() == opt.AnyOp {
cols := ev.childGroup(1).logical.Scalar.OuterCols
logical.Scalar.OuterCols = logical.Scalar.OuterCols.Union(cols)
}

if !logical.Scalar.OuterCols.Empty() {
logical.Scalar.HasCorrelatedSubquery = true
}
Expand All @@ -556,15 +576,6 @@ func (b *logicalPropsBuilder) buildScalarProps(ev ExprView) props.Logical {
return logical
}

// filterNullCols ensures that the set of null columns is a subset of the output
// columns. It respects immutability by making a copy of the null columns if
// they need to be updated.
func filterNullCols(relational *props.Relational) {
if !relational.NotNullCols.SubsetOf(relational.OutputCols) {
relational.NotNullCols = relational.NotNullCols.Intersection(relational.OutputCols)
}
}

// filterWeakKeys ensures that each weak key is a subset of the output columns.
// It respects immutability by making a copy of the weak keys if they need to be
// updated.
Expand Down
16 changes: 16 additions & 0 deletions pkg/sql/opt/memo/private_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,22 @@ func (ps *privateStorage) internColList(colList opt.ColList) PrivateID {
return ps.addValue(privateKey{iface: typ, str: ps.keyBuf.String()}, colList)
}

// internOperator adds the given value to storage and returns an id that can
// later be used to retrieve the value by calling the lookup method. If the
// value has been previously added to storage, then internOperator always
// returns the same private id that was returned from the previous call.
func (ps *privateStorage) internOperator(op opt.Operator) PrivateID {
// The below code is carefully constructed to not allocate in the case where
// the value is already in the map. Be careful when modifying.
ps.keyBuf.Reset()
ps.keyBuf.writeUvarint(uint64(op))
typ := (*opt.Operator)(nil)
if id, ok := ps.privatesMap[privateKey{iface: typ, str: ps.keyBuf.String()}]; ok {
return id
}
return ps.addValue(privateKey{iface: typ, str: ps.keyBuf.String()}, op)
}

// internOrdering adds the given value to storage and returns an id that can
// later be used to retrieve the value by calling the lookup method. If the
// value has been previously added to storage, then internOrdering always
Expand Down
22 changes: 22 additions & 0 deletions pkg/sql/opt/memo/private_storage_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,24 @@ func TestInternSetOpColMap(t *testing.T) {
test(&SetOpColMap{list1, list2, list3}, &SetOpColMap{list1, list3, list2}, false)
}

func TestInternOperator(t *testing.T) {
var ps privateStorage
ps.init()

test := func(left, right opt.Operator, expected bool) {
t.Helper()
leftID := ps.internOperator(left)
rightID := ps.internOperator(right)
if (leftID == rightID) != expected {
t.Errorf("%v == %v, expected %v, got %v", left, right, expected, !expected)
}
}

test(opt.PlusOp, opt.PlusOp, true)
test(opt.PlusOp, opt.MinusOp, false)
test(opt.MinusOp, opt.PlusOp, false)
}

func TestInternOrdering(t *testing.T) {
var ps privateStorage
ps.init()
Expand Down Expand Up @@ -263,6 +281,7 @@ func TestPrivateStorageAllocations(t *testing.T) {
colSet := util.MakeFastIntSet(1, 2, 3)
colList := opt.ColList{3, 2, 1}
ordering := props.Ordering{1, -2, 3}
op := opt.PlusOp
funcOpDef := &FuncOpDef{
Name: "concat",
Type: types.String,
Expand All @@ -277,6 +296,7 @@ func TestPrivateStorageAllocations(t *testing.T) {
ps.internColumnID(colID)
ps.internColSet(colSet)
ps.internColList(colList)
ps.internOperator(op)
ps.internOrdering(ordering)
ps.internFuncOpDef(funcOpDef)
ps.internScanOpDef(scanOpDef)
Expand All @@ -295,6 +315,7 @@ func BenchmarkPrivateStorage(b *testing.B) {
colSet := util.MakeFastIntSet(1, 2, 3)
colList := opt.ColList{3, 2, 1}
ordering := props.Ordering{1, -2, 3}
op := opt.PlusOp
funcOpDef := &FuncOpDef{
Name: "concat",
Type: types.String,
Expand All @@ -311,6 +332,7 @@ func BenchmarkPrivateStorage(b *testing.B) {
ps.internColumnID(colID)
ps.internColSet(colSet)
ps.internColList(colList)
ps.internOperator(op)
ps.internOrdering(ordering)
ps.internFuncOpDef(funcOpDef)
ps.internScanOpDef(scanOpDef)
Expand Down
2 changes: 1 addition & 1 deletion pkg/sql/opt/memo/testdata/logprops/groupby
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ build
SELECT y, SUM(z), x, False FROM xyzs GROUP BY x, y
----
project
├── columns: y:2(int) column5:5(float) x:1(int!null) column6:6(bool)
├── columns: y:2(int) column5:5(float) x:1(int!null) column6:6(bool!null)
├── stats: [rows=1000]
├── keys: (1)
├── group-by
Expand Down
4 changes: 2 additions & 2 deletions pkg/sql/opt/memo/testdata/logprops/limit
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ limit
│ └── keys: (1) weak(3,4)
└── subquery [type=int]
└── max1-row
├── columns: column5:5(int)
├── columns: column5:5(int!null)
├── cardinality: [1 - 1]
├── stats: [rows=1]
└── project
├── columns: column5:5(int)
├── columns: column5:5(int!null)
├── cardinality: [1 - 1]
├── stats: [rows=1]
├── values
Expand Down
4 changes: 2 additions & 2 deletions pkg/sql/opt/memo/testdata/logprops/offset
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ offset
│ └── keys: (1) weak(3,4)
└── subquery [type=int]
└── max1-row
├── columns: column5:5(int)
├── columns: column5:5(int!null)
├── cardinality: [1 - 1]
├── stats: [rows=1]
└── project
├── columns: column5:5(int)
├── columns: column5:5(int!null)
├── cardinality: [1 - 1]
├── stats: [rows=1]
├── values
Expand Down
20 changes: 19 additions & 1 deletion pkg/sql/opt/memo/testdata/logprops/project
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ build
SELECT y, x+1, 1, x FROM xysd
----
project
├── columns: y:2(int) column5:5(int) column6:6(int) x:1(int!null)
├── columns: y:2(int) column5:5(int) column6:6(int!null) x:1(int!null)
├── stats: [rows=1000]
├── keys: (1)
├── scan xysd
Expand Down Expand Up @@ -132,3 +132,21 @@ project
└── projections [outer=(1,2)]
├── variable: xysd.x [type=int, outer=(1)]
└── variable: xysd.y [type=int, outer=(2)]

# Constant null and not-null columns.
build
SELECT 1, 'foo', null, 1::decimal + null, null::string FROM xysd
----
project
├── columns: column5:5(int!null) column6:6(string!null) column7:7(unknown) column7:7(unknown) column8:8(string)
├── stats: [rows=1000]
├── scan xysd
│ ├── columns: xysd.x:1(int!null) xysd.y:2(int) xysd.s:3(string) xysd.d:4(decimal!null)
│ ├── stats: [rows=1000]
│ └── keys: (1) weak(3,4)
└── projections
├── const: 1 [type=int]
├── const: 'foo' [type=string]
├── null [type=unknown]
└── cast: string [type=string]
└── null [type=unknown]
104 changes: 84 additions & 20 deletions pkg/sql/opt/memo/testdata/logprops/scalar
Original file line number Diff line number Diff line change
@@ -1,64 +1,128 @@
exec-ddl
CREATE TABLE a (x INT PRIMARY KEY, y INT)
CREATE TABLE xy (x INT PRIMARY KEY, y INT)
----
TABLE a
TABLE xy
├── x int not null
├── y int
└── INDEX primary
└── x int not null

exec-ddl
CREATE TABLE b (x INT, z INT NOT NULL)
CREATE TABLE uv (u INT, v INT NOT NULL)
----
TABLE b
├── x int
├── z int not null
TABLE uv
├── u int
├── v int not null
├── rowid int not null (hidden)
└── INDEX primary
└── rowid int not null (hidden)

build
SELECT * FROM a WHERE x < 5
SELECT * FROM xy WHERE x < 5
----
select
├── columns: x:1(int!null) y:2(int)
├── stats: [rows=333]
├── keys: (1)
├── scan a
│ ├── columns: a.x:1(int!null) a.y:2(int)
├── scan xy
│ ├── columns: xy.x:1(int!null) xy.y:2(int)
│ ├── stats: [rows=1000]
│ └── keys: (1)
└── lt [type=bool, outer=(1), constraints=(/1: (/NULL - /4]; tight)]
├── variable: a.x [type=int, outer=(1)]
├── variable: xy.x [type=int, outer=(1)]
└── const: 5 [type=int]

build
SELECT a.x + 1 = length('foo') + a.y, b.rowid * a.x FROM a, b
SELECT xy.x + 1 = length('foo') + xy.y, uv.rowid * xy.x FROM xy, uv
----
project
├── columns: column6:6(bool) column7:7(int)
├── stats: [rows=1000000]
├── inner-join
│ ├── columns: a.x:1(int!null) a.y:2(int) b.x:3(int) b.z:4(int!null) b.rowid:5(int!null)
│ ├── columns: xy.x:1(int!null) xy.y:2(int) uv.u:3(int) uv.v:4(int!null) uv.rowid:5(int!null)
│ ├── stats: [rows=1000000]
│ ├── scan a
│ │ ├── columns: a.x:1(int!null) a.y:2(int)
│ ├── scan xy
│ │ ├── columns: xy.x:1(int!null) xy.y:2(int)
│ │ ├── stats: [rows=1000]
│ │ └── keys: (1)
│ ├── scan b
│ │ ├── columns: b.x:3(int) b.z:4(int!null) b.rowid:5(int!null)
│ ├── scan uv
│ │ ├── columns: uv.u:3(int) uv.v:4(int!null) uv.rowid:5(int!null)
│ │ ├── stats: [rows=1000]
│ │ └── keys: (5)
│ └── true [type=bool]
└── projections [outer=(1,2,5)]
├── eq [type=bool, outer=(1,2)]
│ ├── plus [type=int, outer=(1)]
│ │ ├── variable: a.x [type=int, outer=(1)]
│ │ ├── variable: xy.x [type=int, outer=(1)]
│ │ └── const: 1 [type=int]
│ └── plus [type=int, outer=(2)]
│ ├── function: length [type=int]
│ │ └── const: 'foo' [type=string]
│ └── variable: a.y [type=int, outer=(2)]
│ └── variable: xy.y [type=int, outer=(2)]
└── mult [type=int, outer=(1,5)]
├── variable: b.rowid [type=int, outer=(5)]
└── variable: a.x [type=int, outer=(1)]
├── variable: uv.rowid [type=int, outer=(5)]
└── variable: xy.x [type=int, outer=(1)]

build
SELECT * FROM xy WHERE EXISTS(SELECT * FROM uv WHERE u=x)
----
select
├── columns: x:1(int!null) y:2(int)
├── stats: [rows=333]
├── keys: (1)
├── scan xy
│ ├── columns: xy.x:1(int!null) xy.y:2(int)
│ ├── stats: [rows=1000]
│ └── keys: (1)
└── exists [type=bool, outer=(1)]
└── project
├── columns: uv.u:3(int) uv.v:4(int!null)
├── outer: (1)
├── stats: [rows=333]
├── select
│ ├── columns: uv.u:3(int) uv.v:4(int!null) uv.rowid:5(int!null)
│ ├── outer: (1)
│ ├── stats: [rows=333]
│ ├── keys: (5)
│ ├── scan uv
│ │ ├── columns: uv.u:3(int) uv.v:4(int!null) uv.rowid:5(int!null)
│ │ ├── stats: [rows=1000]
│ │ └── keys: (5)
│ └── eq [type=bool, outer=(1,3)]
│ ├── variable: uv.u [type=int, outer=(3)]
│ └── variable: xy.x [type=int, outer=(1)]
└── projections [outer=(3,4)]
├── variable: uv.u [type=int, outer=(3)]
└── variable: uv.v [type=int, outer=(4)]

build
SELECT * FROM xy WHERE y IN (SELECT v FROM uv WHERE u=x)
----
select
├── columns: x:1(int!null) y:2(int)
├── stats: [rows=333]
├── keys: (1)
├── scan xy
│ ├── columns: xy.x:1(int!null) xy.y:2(int)
│ ├── stats: [rows=1000]
│ └── keys: (1)
└── any: eq [type=bool, outer=(1,2)]
├── project
│ ├── columns: uv.v:4(int!null)
│ ├── outer: (1)
│ ├── stats: [rows=333]
│ ├── select
│ │ ├── columns: uv.u:3(int) uv.v:4(int!null) uv.rowid:5(int!null)
│ │ ├── outer: (1)
│ │ ├── stats: [rows=333]
│ │ ├── keys: (5)
│ │ ├── scan uv
│ │ │ ├── columns: uv.u:3(int) uv.v:4(int!null) uv.rowid:5(int!null)
│ │ │ ├── stats: [rows=1000]
│ │ │ └── keys: (5)
│ │ └── eq [type=bool, outer=(1,3)]
│ │ ├── variable: uv.u [type=int, outer=(3)]
│ │ └── variable: xy.x [type=int, outer=(1)]
│ └── projections [outer=(4)]
│ └── variable: uv.v [type=int, outer=(4)]
└── variable: xy.y [type=int, outer=(2)]
Loading

0 comments on commit 17ed382

Please sign in to comment.