Hello,
I reworked a patch to make more stable in different cases. I decided to
use simplify_function instead of eval_const_expression to prevent
inlining of the function. The only possible outputs of the
simplify_function are Const node and NULL.
Also, I block pre-evaluation of functions with types other than
TYPTYPE_BASE, cause there is no special logic for compound (and others)
values yet.
There is still a problem with memory leak in case of simplified
arguments. The only way I see is a creation of temporary memory context,
but it cost some performance. Maybe we can store simplified arguments
in the pointed function itself for later use. But eval_const_expression
and friends doesn't change the content of the nodes inside the tree, it
generates new nodes and returns it as a result.
The last point to mention is a fixed plan for the query in the initial
letter of the thread. As I mentioned before, new versions of the patch
replace var not with a function call, but with a function execution
result. After the patch, the following plan is used instead of Nested
Loop with Sequence Scan:
explain select '|'||subject||'|', ts_rank_cd(body_tsvector,q) from messages,
to_tsquery('english', 'tuple&header&overhead') q where body_tsvector @@ q limit
10;
QUERY PLAN
----------------------------------------------------------------------------------------------------
Limit (cost=224.16..266.11 rows=3 width=36)
-> Nested Loop (cost=224.16..266.11 rows=3 width=36)
-> Function Scan on q (cost=0.00..0.01 rows=1 width=0)
-> Bitmap Heap Scan on messages (cost=224.16..266.04 rows=3
width=275)
Recheck Cond: (body_tsvector @@ '''tupl'' & ''header'' &
''overhead'''::tsquery)
-> Bitmap Index Scan on message_body_idx (cost=0.00..224.16
rows=3 width=0)
Index Cond: (body_tsvector @@ '''tupl'' & ''header'' &
''overhead'''::tsquery)
--
Aleksandr Parfenov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 505ae0af85..2c9983004a 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -3655,6 +3655,59 @@ eval_const_expressions_mutator(Node *node,
context);
}
break;
+ case T_Var:
+ if (context->root && context->root->parse->rtable)
+ {
+ Var *var;
+ Query *query;
+ RangeTblEntry *pointedNode;
+
+ var = (Var *)node;
+ query = context->root->parse;
+
+ if (var->varlevelsup != 0 || var->varattno != 1)
+ break;
+
+ pointedNode = list_nth(query->rtable, var->varno - 1);
+ Assert(IsA(pointedNode, RangeTblEntry));
+
+ if (pointedNode->rtekind == RTE_FUNCTION && list_length(pointedNode->functions) == 1)
+ {
+ Form_pg_type type_form;
+ Node *result;
+ RangeTblFunction *tblFunction = linitial_node(RangeTblFunction, pointedNode->functions);
+ FuncExpr *expr = (FuncExpr *) tblFunction->funcexpr;
+ List *args = expr->args;
+ HeapTuple type_tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(expr->funcresulttype));
+
+ if (!HeapTupleIsValid(type_tuple))
+ elog(ERROR, "cache lookup failed for type %u", expr->funcresulttype);
+
+ type_form = (Form_pg_type) GETSTRUCT(type_tuple);
+
+ if (type_form->typtype != TYPTYPE_BASE)
+ {
+ ReleaseSysCache(type_tuple);
+ break;
+ }
+
+ result = simplify_function(expr->funcid,
+ expr->funcresulttype,
+ exprTypmod(expr),
+ expr->funccollid,
+ expr->inputcollid,
+ &args,
+ expr->funcvariadic,
+ true,
+ false,
+ context);
+
+ ReleaseSysCache(type_tuple);
+
+ if (result) /* successfully simplified it */
+ return (Node *) result;
+ }
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index cbc882d47b..eb92f556d8 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -3021,23 +3021,23 @@ select * from
int4(sin(1)) q1,
int4(sin(0)) q2
where q1 = thousand or q2 = thousand;
- QUERY PLAN
-------------------------------------------------------------------------
- Hash Join
- Hash Cond: (tenk1.twothousand = int4_tbl.f1)
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Nested Loop
-> Nested Loop
- -> Nested Loop
- -> Function Scan on q1
- -> Function Scan on q2
- -> Bitmap Heap Scan on tenk1
- Recheck Cond: ((q1.q1 = thousand) OR (q2.q2 = thousand))
- -> BitmapOr
- -> Bitmap Index Scan on tenk1_thous_tenthous
- Index Cond: (q1.q1 = thousand)
- -> Bitmap Index Scan on tenk1_thous_tenthous
- Index Cond: (q2.q2 = thousand)
- -> Hash
- -> Seq Scan on int4_tbl
+ -> Hash Join
+ Hash Cond: (tenk1.twothousand = int4_tbl.f1)
+ -> Bitmap Heap Scan on tenk1
+ Recheck Cond: ((1 = thousand) OR (0 = thousand))
+ -> BitmapOr
+ -> Bitmap Index Scan on tenk1_thous_tenthous
+ Index Cond: (1 = thousand)
+ -> Bitmap Index Scan on tenk1_thous_tenthous
+ Index Cond: (0 = thousand)
+ -> Hash
+ -> Seq Scan on int4_tbl
+ -> Function Scan on q1
+ -> Function Scan on q2
(15 rows)
explain (costs off)
@@ -3046,20 +3046,20 @@ select * from
int4(sin(1)) q1,
int4(sin(0)) q2
where thousand = (q1 + q2);
- QUERY PLAN
---------------------------------------------------------------
- Hash Join
- Hash Cond: (tenk1.twothousand = int4_tbl.f1)
+ QUERY PLAN
+-------------------------------------------------------------------
+ Nested Loop
-> Nested Loop
- -> Nested Loop
- -> Function Scan on q1
- -> Function Scan on q2
- -> Bitmap Heap Scan on tenk1
- Recheck Cond: (thousand = (q1.q1 + q2.q2))
- -> Bitmap Index Scan on tenk1_thous_tenthous
- Index Cond: (thousand = (q1.q1 + q2.q2))
- -> Hash
- -> Seq Scan on int4_tbl
+ -> Hash Join
+ Hash Cond: (tenk1.twothousand = int4_tbl.f1)
+ -> Bitmap Heap Scan on tenk1
+ Recheck Cond: (thousand = 1)
+ -> Bitmap Index Scan on tenk1_thous_tenthous
+ Index Cond: (thousand = 1)
+ -> Hash
+ -> Seq Scan on int4_tbl
+ -> Function Scan on q1
+ -> Function Scan on q2
(12 rows)
--