Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/select
Original file line number Diff line number Diff line change
Expand Up @@ -1013,3 +1013,36 @@ query ITT
SELECT * FROM t146637 WHERE (a = 0 OR a = 100) AND b = 'foo' ORDER BY a DESC LIMIT 1;
----
100 foo bar

# Regression test for #170544. InlineConstVar must cast an inlined constant to
# the variable's type when the two are equivalent but not identical, so that
# type-sensitive expressions such as pg_typeof are not changed.
statement ok
CREATE TABLE t170544_name (n NAME);
INSERT INTO t170544_name VALUES ('hello')

# pg_typeof(n) must still observe NAME (not STRING), so the row is returned.
query T
SELECT n FROM t170544_name WHERE n = 'hello' AND pg_typeof(n)::TEXT = 'name'
----
hello

# The cast added when inlining must not introduce an evaluation error for
# out-of-range or lossy casts, because the constant only reaches dead rows.
# INT8 -> INT2 overflow: the query returns zero rows rather than erroring.
statement ok
CREATE TABLE t170544_int2 (i2 INT2);
INSERT INTO t170544_int2 VALUES (5)

query I
SELECT i2 FROM t170544_int2 WHERE i2 = 40000 AND i2 + 0 = 5
----

# VARCHAR(n) truncation: the query returns zero rows rather than incorrect rows.
statement ok
CREATE TABLE t170544_varchar (s VARCHAR(3));
INSERT INTO t170544_varchar VALUES ('abc')

query T
SELECT s FROM t170544_varchar WHERE s = 'abcdef' AND lower(s) = 'abc'
----
10 changes: 10 additions & 0 deletions pkg/sql/opt/norm/inline_funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,16 @@ func (c *CustomFuncs) InlineConstVar(f memo.FiltersExpr) memo.FiltersExpr {
replace = func(nd opt.Expr) opt.Expr {
if t, ok := nd.(*memo.VariableExpr); ok {
if e, ok := vals[t.Col]; ok {
// The constant was matched against the variable using Equivalent
// (not Identical) above, so its type may differ from the
// variable's (e.g. a STRING constant for a NAME column).
// Substituting the constant directly would change the result of
// type-sensitive expressions such as pg_typeof, so cast the
// constant to the variable's type when the two are not identical.
colType := c.mem.Metadata().ColumnMeta(t.Col).Type
if !e.DataType().Identical(colType) {
return c.f.ConstructCast(e, colType)
}
return e
}
}
Expand Down
60 changes: 60 additions & 0 deletions pkg/sql/opt/norm/testdata/rules/inline
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,66 @@ project
│ └── true [as=column14:14]
└── false

# Regression test for #170544. InlineConstVar must not replace a variable with a
# constant whose type is equivalent but not identical to the variable's type
# without a cast, since that changes the result of type-sensitive expressions
# such as pg_typeof. Here n is NAME and the constant 'hello' is STRING; the
# inlined constant is cast back to NAME so pg_typeof(n) still reports 'name' and
# the row is not incorrectly filtered out.
exec-ddl
CREATE TABLE t_name (n NAME)
----

norm expect=InlineConstVar
SELECT * FROM t_name WHERE n = 'hello' AND pg_typeof(n)::TEXT = 'name'
Comment thread
shafi-VM marked this conversation as resolved.
----
select
├── columns: n:1!null
├── fd: ()-->(1)
├── scan t_name
│ └── columns: n:1
└── filters
└── n:1 = 'hello' [outer=(1), constraints=(/1: [/'hello' - /'hello']; tight), fd=()-->(1)]

# Out-of-range constant (#170544). i2 = 40000 is a contradiction for an INT2
# column, so no row is ever live. The cast that InlineConstVar adds (40000::INT2,
# which would overflow) must not be folded eagerly, or the query would error
# instead of returning zero rows.
exec-ddl
CREATE TABLE t_int2 (i2 INT2)
----

norm
SELECT * FROM t_int2 WHERE i2 = 40000 AND i2 + 0 = 5
----
select
├── columns: i2:1!null
├── immutable
├── fd: ()-->(1)
├── scan t_int2
│ └── columns: i2:1
└── filters
├── i2:1 = 40000 [outer=(1), constraints=(/1: [/40000 - /40000]; tight), fd=()-->(1)]
└── 40000::INT2::INT8 = 5 [immutable]

# Length-limited type (#170544). 'abcdef' truncates to 'abc' when cast to
# VARCHAR(3), but s = 'abcdef' is a contradiction, so the truncated constant is
# only ever inlined into dead rows.
exec-ddl
CREATE TABLE t_varchar (s VARCHAR(3))
----

norm
SELECT * FROM t_varchar WHERE s = 'abcdef' AND lower(s) = 'abc'
----
select
├── columns: s:1!null
├── fd: ()-->(1)
├── scan t_varchar
│ └── columns: s:1
└── filters
└── s:1 = 'abcdef' [outer=(1), constraints=(/1: [/'abcdef' - /'abcdef']; tight), fd=()-->(1)]

# --------------------------------------------------
# InlineProjectConstants
# --------------------------------------------------
Expand Down
Loading