diff --git a/expression/column.go b/expression/column.go index 18051f786701f..1c4ec9ab49bda 100644 --- a/expression/column.go +++ b/expression/column.go @@ -123,6 +123,11 @@ func (col *CorrelatedColumn) IsCorrelated() bool { return true } +// ConstItem implements Expression interface. +func (col *CorrelatedColumn) ConstItem() bool { + return false +} + // Decorrelate implements Expression interface. func (col *CorrelatedColumn) Decorrelate(schema *Schema) Expression { if !schema.Contains(&col.Column) { @@ -297,6 +302,11 @@ func (col *Column) IsCorrelated() bool { return false } +// ConstItem implements Expression interface. +func (col *Column) ConstItem() bool { + return false +} + // Decorrelate implements Expression interface. func (col *Column) Decorrelate(_ *Schema) Expression { return col diff --git a/expression/column_test.go b/expression/column_test.go index 8f11509783055..5f0ffdb2c9b46 100644 --- a/expression/column_test.go +++ b/expression/column_test.go @@ -43,6 +43,7 @@ func (s *testEvaluatorSuite) TestColumn(c *C) { c.Assert(corCol.Equal(nil, corCol), IsTrue) c.Assert(corCol.Equal(nil, invalidCorCol), IsFalse) c.Assert(corCol.IsCorrelated(), IsTrue) + c.Assert(corCol.ConstItem(), IsFalse) c.Assert(corCol.Decorrelate(schema).Equal(nil, col), IsTrue) c.Assert(invalidCorCol.Decorrelate(schema).Equal(nil, invalidCorCol), IsTrue) diff --git a/expression/constant.go b/expression/constant.go index 75dbf85ce99e8..c678bc40b8d78 100644 --- a/expression/constant.go +++ b/expression/constant.go @@ -311,6 +311,11 @@ func (c *Constant) IsCorrelated() bool { return false } +// ConstItem implements Expression interface. +func (c *Constant) ConstItem() bool { + return true +} + // Decorrelate implements Expression interface. func (c *Constant) Decorrelate(_ *Schema) Expression { return c diff --git a/expression/expression.go b/expression/expression.go index cf6691de7329a..162cacb015474 100644 --- a/expression/expression.go +++ b/expression/expression.go @@ -79,6 +79,14 @@ type Expression interface { // IsCorrelated checks if this expression has correlated key. IsCorrelated() bool + // ConstItem checks if this expression is constant item, regardless of query evaluation state. + // An expression is constant item if it: + // refers no tables. + // refers no subqueries that refers any tables. + // refers no non-deterministic functions. + // refers no statement parameters. + ConstItem() bool + // Decorrelate try to decorrelate the expression by schema. Decorrelate(schema *Schema) Expression diff --git a/expression/expression_test.go b/expression/expression_test.go index d96135617bc7f..f090725364a19 100644 --- a/expression/expression_test.go +++ b/expression/expression_test.go @@ -61,6 +61,7 @@ func (s *testEvaluatorSuite) TestConstant(c *C) { sc := &stmtctx.StatementContext{TimeZone: time.Local} c.Assert(Zero.IsCorrelated(), IsFalse) + c.Assert(Zero.ConstItem(), IsTrue) c.Assert(Zero.Decorrelate(nil).Equal(s.ctx, Zero), IsTrue) c.Assert(Zero.HashCode(sc), DeepEquals, []byte{0x0, 0x8, 0x0}) c.Assert(Zero.Equal(s.ctx, One), IsFalse) @@ -85,6 +86,19 @@ func (s *testEvaluatorSuite) TestIsBinaryLiteral(c *C) { c.Assert(IsBinaryLiteral(con), IsFalse) } +func (s *testEvaluatorSuite) TestConstItem(c *C) { + defer testleak.AfterTest(c)() + + sf := newFunction(ast.Rand) + c.Assert(sf.ConstItem(), Equals, false) + sf = newFunction(ast.UUID) + c.Assert(sf.ConstItem(), Equals, false) + sf = newFunction(ast.GetParam, One) + c.Assert(sf.ConstItem(), Equals, false) + sf = newFunction(ast.Abs, One) + c.Assert(sf.ConstItem(), Equals, true) +} + type testTableBuilder struct { tableName string columnNames []string diff --git a/expression/scalar_function.go b/expression/scalar_function.go index 6ab007daef7b3..77ce863a39406 100644 --- a/expression/scalar_function.go +++ b/expression/scalar_function.go @@ -165,6 +165,20 @@ func (sf *ScalarFunction) IsCorrelated() bool { return false } +// ConstItem implements Expression interface. +func (sf *ScalarFunction) ConstItem() bool { + // Note: some unfoldable functions are deterministic, we use unFoldableFunctions here for simplification. + if _, ok := unFoldableFunctions[sf.FuncName.L]; ok { + return false + } + for _, arg := range sf.GetArgs() { + if !arg.ConstItem() { + return false + } + } + return true +} + // Decorrelate implements Expression interface. func (sf *ScalarFunction) Decorrelate(schema *Schema) Expression { for i, arg := range sf.GetArgs() { diff --git a/expression/scalar_function_test.go b/expression/scalar_function_test.go index ef466355c1f53..ae8d562788e71 100644 --- a/expression/scalar_function_test.go +++ b/expression/scalar_function_test.go @@ -40,6 +40,7 @@ func (s *testEvaluatorSuite) TestScalarFunction(c *C) { c.Assert(err, IsNil) c.Assert(res, DeepEquals, []byte{0x22, 0x6c, 0x74, 0x28, 0x66, 0x65, 0x69, 0x2e, 0x68, 0x61, 0x6e, 0x2c, 0x20, 0x31, 0x29, 0x22}) c.Assert(sf.IsCorrelated(), IsFalse) + c.Assert(sf.ConstItem(), IsFalse) c.Assert(sf.Decorrelate(nil).Equal(s.ctx, sf), IsTrue) c.Assert(sf.HashCode(sc), DeepEquals, []byte{0x3, 0x4, 0x6c, 0x74, 0x1, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x5, 0xbf, 0xf0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0})