-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
exec: overflow handling for vectorized arithmetic
The overflow checks are done as part of the code generation in overloads.go. The checks are done inline, rather than calling the functions in the arith package for performance reasons. The checks are only done for integer math. float math is already well-defined since overflow will result in +Inf and -Inf as necessary. The operations that these checks are relevant for are the SUM_INT aggregator and projection. In the future, AVG will also benefit from these overflow checks. This changes the error message produced by overflows in the non-vectorized SUM_INT aggregator so that the messages are consistent. This should be fine in terms of postgres-compatibility since SUM_INT is unique to CRDB and eventually we will get rid of it anyway. resolves #38775 Release note: None
- Loading branch information
Showing
10 changed files
with
343 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
pkg/sql/exec/execgen/cmd/execgen/overloads_test_utils_gen.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// Copyright 2019 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 main | ||
|
||
import ( | ||
"io" | ||
"text/template" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/sql/exec/types" | ||
) | ||
|
||
const overloadsTestUtilsTemplate = ` | ||
package exec | ||
import ( | ||
"math" | ||
"github.com/cockroachdb/apd" | ||
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree" | ||
) | ||
{{define "opName"}}perform{{.Name}}{{.LTyp}}{{end}} | ||
{{/* The outer range is a types.T, and the inner is the overloads associated | ||
with that type. */}} | ||
{{range .}} | ||
{{range .}} | ||
func {{template "opName" .}}(a, b {{.LTyp.GoTypeName}}) {{.RetTyp.GoTypeName}} { | ||
{{(.Assign "a" "a" "b")}} | ||
return a | ||
} | ||
{{end}} | ||
{{end}} | ||
` | ||
|
||
// genOverloadsTestUtils creates a file that has a function for each overload | ||
// defined in overloads.go. This is so that we can more easily test each | ||
// overload. | ||
func genOverloadsTestUtils(wr io.Writer) error { | ||
tmpl, err := template.New("overloads_test_utils").Parse(overloadsTestUtilsTemplate) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
typToOverloads := make(map[types.T][]*overload) | ||
for _, overload := range binaryOpOverloads { | ||
typ := overload.LTyp | ||
typToOverloads[typ] = append(typToOverloads[typ], overload) | ||
} | ||
return tmpl.Execute(wr, typToOverloads) | ||
} | ||
|
||
func init() { | ||
registerGenerator(genOverloadsTestUtils, "overloads_test_utils.eg.go") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
// Copyright 2019 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 exec | ||
|
||
import ( | ||
"math" | ||
"testing" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/sql/sem/tree" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestIntegerAddition(t *testing.T) { | ||
// The addition overload is the same for all integer widths, so we only test | ||
// one of them. | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performPlusInt16(1, math.MaxInt16) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performPlusInt16(-1, math.MinInt16) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performPlusInt16(math.MaxInt16, 1) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performPlusInt16(math.MinInt16, -1) }) | ||
|
||
assert.Equal(t, int16(math.MaxInt16), performPlusInt16(1, math.MaxInt16-1)) | ||
assert.Equal(t, int16(math.MinInt16), performPlusInt16(-1, math.MinInt16+1)) | ||
assert.Equal(t, int16(math.MaxInt16-1), performPlusInt16(-1, math.MaxInt16)) | ||
assert.Equal(t, int16(math.MinInt16+1), performPlusInt16(1, math.MinInt16)) | ||
|
||
assert.Equal(t, int16(22), performPlusInt16(10, 12)) | ||
assert.Equal(t, int16(-22), performPlusInt16(-10, -12)) | ||
assert.Equal(t, int16(2), performPlusInt16(-10, 12)) | ||
assert.Equal(t, int16(-2), performPlusInt16(10, -12)) | ||
} | ||
|
||
func TestIntegerSubtraction(t *testing.T) { | ||
// The subtraction overload is the same for all integer widths, so we only | ||
// test one of them. | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMinusInt16(1, -math.MaxInt16) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMinusInt16(-2, math.MaxInt16) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMinusInt16(math.MaxInt16, -1) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMinusInt16(math.MinInt16, 1) }) | ||
|
||
assert.Equal(t, int16(math.MaxInt16), performMinusInt16(1, -math.MaxInt16+1)) | ||
assert.Equal(t, int16(math.MinInt16), performMinusInt16(-1, math.MaxInt16)) | ||
assert.Equal(t, int16(math.MaxInt16-1), performMinusInt16(-1, -math.MaxInt16)) | ||
assert.Equal(t, int16(math.MinInt16+1), performMinusInt16(0, math.MaxInt16)) | ||
|
||
assert.Equal(t, int16(-2), performMinusInt16(10, 12)) | ||
assert.Equal(t, int16(2), performMinusInt16(-10, -12)) | ||
assert.Equal(t, int16(-22), performMinusInt16(-10, 12)) | ||
assert.Equal(t, int16(22), performMinusInt16(10, -12)) | ||
} | ||
|
||
func TestIntegerDivision(t *testing.T) { | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performDivInt8(math.MinInt8, -1) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performDivInt16(math.MinInt16, -1) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performDivInt32(math.MinInt32, -1) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performDivInt64(math.MinInt64, -1) }) | ||
|
||
assert.PanicsWithValue(t, tree.ErrDivByZero, func() { performDivInt8(10, 0) }) | ||
assert.PanicsWithValue(t, tree.ErrDivByZero, func() { performDivInt16(10, 0) }) | ||
assert.PanicsWithValue(t, tree.ErrDivByZero, func() { performDivInt32(10, 0) }) | ||
assert.PanicsWithValue(t, tree.ErrDivByZero, func() { performDivInt64(10, 0) }) | ||
|
||
assert.Equal(t, int8(-math.MaxInt8), performDivInt8(math.MaxInt8, -1)) | ||
assert.Equal(t, int16(-math.MaxInt16), performDivInt16(math.MaxInt16, -1)) | ||
assert.Equal(t, int32(-math.MaxInt32), performDivInt32(math.MaxInt32, -1)) | ||
assert.Equal(t, int64(-math.MaxInt64), performDivInt64(math.MaxInt64, -1)) | ||
|
||
assert.Equal(t, int16(0), performDivInt16(10, 12)) | ||
assert.Equal(t, int16(0), performDivInt16(-10, -12)) | ||
assert.Equal(t, int16(-1), performDivInt16(-12, 10)) | ||
assert.Equal(t, int16(-1), performDivInt16(12, -10)) | ||
} | ||
|
||
func TestIntegerMultiplication(t *testing.T) { | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt8(math.MaxInt8-1, 100) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt8(math.MaxInt8-1, 3) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt8(math.MinInt8+1, 3) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt8(math.MinInt8+1, 100) }) | ||
|
||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt16(math.MaxInt16-1, 100) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt16(math.MaxInt16-1, 3) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt16(math.MinInt16+1, 3) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt16(math.MinInt16+1, 100) }) | ||
|
||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt32(math.MaxInt32-1, 100) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt32(math.MaxInt32-1, 3) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt32(math.MinInt32+1, 3) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt32(math.MinInt32+1, 100) }) | ||
|
||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt64(math.MaxInt64-1, 100) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt64(math.MaxInt64-1, 3) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt64(math.MinInt64+1, 3) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt64(math.MinInt64+1, 100) }) | ||
|
||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt8(math.MinInt8, -1) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt16(math.MinInt16, -1) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt32(math.MinInt32, -1) }) | ||
assert.PanicsWithValue(t, tree.ErrIntOutOfRange, func() { performMultInt64(math.MinInt64, -1) }) | ||
|
||
assert.Equal(t, int8(-math.MaxInt8), performMultInt8(math.MaxInt8, -1)) | ||
assert.Equal(t, int16(-math.MaxInt16), performMultInt16(math.MaxInt16, -1)) | ||
assert.Equal(t, int32(-math.MaxInt32), performMultInt32(math.MaxInt32, -1)) | ||
assert.Equal(t, int64(-math.MaxInt64), performMultInt64(math.MaxInt64, -1)) | ||
|
||
assert.Equal(t, int8(0), performMultInt8(math.MinInt8, 0)) | ||
assert.Equal(t, int16(0), performMultInt16(math.MinInt16, 0)) | ||
assert.Equal(t, int32(0), performMultInt32(math.MinInt32, 0)) | ||
assert.Equal(t, int64(0), performMultInt64(math.MinInt64, 0)) | ||
|
||
assert.Equal(t, int8(120), performMultInt8(10, 12)) | ||
assert.Equal(t, int16(120), performMultInt16(-10, -12)) | ||
assert.Equal(t, int32(-120), performMultInt32(-12, 10)) | ||
assert.Equal(t, int64(-120), performMultInt64(12, -10)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# LogicTest: local-vec local-opt fakedist fakedist-opt fakedist-metadata | ||
|
||
# Test for overflow handling in sum aggregate. | ||
|
||
statement ok | ||
CREATE TABLE large_numbers (a INT8) | ||
|
||
statement ok | ||
INSERT INTO large_numbers VALUES (9223372036854775807),(1) | ||
|
||
statement error integer out of range | ||
SELECT sum_int(a) FROM large_numbers | ||
|
||
statement ok | ||
DELETE FROM large_numbers | ||
|
||
statement ok | ||
INSERT INTO large_numbers VALUES (-9223372036854775808),(-1) | ||
|
||
statement error integer out of range | ||
SELECT sum_int(a) FROM large_numbers |
Oops, something went wrong.