diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 24d7d98..7a380ce 100644
*** a/doc/src/sgml/catalogs.sgml
--- b/doc/src/sgml/catalogs.sgml
***************
*** 4338,4343 ****
--- 4338,4350 ----
+ protransform
+ regproc
+ pg_proc.oid
+ Calls to function can be simplified by this other function
+
+
+ proisaggbool
diff --git a/src/backend/catalog/index 6250b07..92be0a7 100644
*** a/src/backend/catalog/pg_proc.c
--- b/src/backend/catalog/pg_proc.c
***************
*** 304,309 **** ProcedureCreate(const char *procedureName,
--- 304,310 ----
values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost);
values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows);
values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType);
+ values[Anum_pg_proc_protransform - 1] = ObjectIdGetDatum(InvalidOid);
values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg);
values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(isWindowFunc);
values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer);
diff --git a/src/backend/commands/taindex 912f45c..4dffd64 100644
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
***************
*** 56,61 ****
--- 56,62 ----
#include "nodes/nodeFuncs.h"
#include "nodes/parsenodes.h"
#include "optimizer/clauses.h"
+ #include "optimizer/planner.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
***************
*** 3495,3501 **** ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
{
NewColumnValue *ex = lfirst(l);
! ex->exprstate = ExecPrepareExpr((Expr *) ex->expr, estate);
}
notnull_attrs = NIL;
--- 3496,3503 ----
{
NewColumnValue *ex = lfirst(l);
! /* expr already planned */
! ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
}
notnull_attrs = NIL;
***************
*** 4398,4404 **** ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
newval->attnum = attribute.attnum;
! newval->expr = defval;
tab->newvals = lappend(tab->newvals, newval);
tab->rewrite = true;
--- 4400,4406 ----
newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
newval->attnum = attribute.attnum;
! newval->expr = expression_planner(defval);
tab->newvals = lappend(tab->newvals, newval);
tab->rewrite = true;
***************
*** 6707,6712 **** ATPrepAlterColumnType(List **wqueue,
--- 6709,6717 ----
/* Fix collations after all else */
assign_expr_collations(pstate, transform);
+ /* Plan the expr now so we can accurately assess the need to rewrite. */
+ transform = (Node *) expression_planner((Expr *) transform);
+
/*
* Add a work queue item to make ATRewriteTable update the column
* contents.
diff --git a/src/backend/optimizer/utilindex 2914c39..be0935d 100644
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
***************
*** 106,114 **** static List *simplify_and_arguments(List *args,
eval_const_expressions_context *context,
bool *haveNull, bool *forceFalse);
static Node *simplify_boolean_equality(Oid opno, List *args);
! static Expr *simplify_function(Oid funcid,
! Oid result_type, int32 result_typmod,
! Oid result_collid, Oid input_collid, List **args,
bool has_named_args,
bool allow_inline,
eval_const_expressions_context *context);
--- 106,114 ----
eval_const_expressions_context *context,
bool *haveNull, bool *forceFalse);
static Node *simplify_boolean_equality(Oid opno, List *args);
! static Expr *simplify_function(Expr *oldexpr, Oid funcid,
! Oid result_type, int32 result_typmod, Oid result_collid,
! Oid input_collid, List **args,
bool has_named_args,
bool allow_inline,
eval_const_expressions_context *context);
***************
*** 2223,2229 **** eval_const_expressions_mutator(Node *node,
* FuncExpr, but not when the node is recognizably a length coercion;
* we want to preserve the typmod in the eventual Const if so.
*/
! simple = simplify_function(expr->funcid,
expr->funcresulttype, exprTypmod(node),
expr->funccollid,
expr->inputcollid,
--- 2223,2230 ----
* FuncExpr, but not when the node is recognizably a length coercion;
* we want to preserve the typmod in the eventual Const if so.
*/
! simple = simplify_function((Expr *) expr,
! expr->funcid,
expr->funcresulttype, exprTypmod(node),
expr->funccollid,
expr->inputcollid,
***************
*** 2275,2281 **** eval_const_expressions_mutator(Node *node,
* Code for op/func reduction is pretty bulky, so split it out as a
* separate function.
*/
! simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
expr->opcollid,
expr->inputcollid,
--- 2276,2283 ----
* Code for op/func reduction is pretty bulky, so split it out as a
* separate function.
*/
! simple = simplify_function((Expr *) expr,
! expr->opfuncid,
expr->opresulttype, -1,
expr->opcollid,
expr->inputcollid,
***************
*** 2372,2378 **** eval_const_expressions_mutator(Node *node,
* Code for op/func reduction is pretty bulky, so split it out as
* a separate function.
*/
! simple = simplify_function(expr->opfuncid,
expr->opresulttype, -1,
expr->opcollid,
expr->inputcollid,
--- 2374,2381 ----
* Code for op/func reduction is pretty bulky, so split it out as
* a separate function.
*/
! simple = simplify_function((Expr *) expr,
! expr->opfuncid,
expr->opresulttype, -1,
expr->opcollid,
expr->inputcollid,
***************
*** 2561,2567 **** eval_const_expressions_mutator(Node *node,
getTypeOutputInfo(exprType((Node *) arg), &outfunc, &outtypisvarlena);
getTypeInputInfo(expr->resulttype, &infunc, &intypioparam);
! simple = simplify_function(outfunc,
CSTRINGOID, -1,
InvalidOid,
InvalidOid,
--- 2564,2571 ----
getTypeOutputInfo(exprType((Node *) arg), &outfunc, &outtypisvarlena);
getTypeInputInfo(expr->resulttype, &infunc, &intypioparam);
! simple = simplify_function(NULL,
! outfunc,
CSTRINGOID, -1,
InvalidOid,
InvalidOid,
***************
*** 2581,2587 **** eval_const_expressions_mutator(Node *node,
Int32GetDatum(-1),
false, true));
! simple = simplify_function(infunc,
expr->resulttype, -1,
expr->resultcollid,
InvalidOid,
--- 2585,2592 ----
Int32GetDatum(-1),
false, true));
! simple = simplify_function(NULL,
! infunc,
expr->resulttype, -1,
expr->resultcollid,
InvalidOid,
***************
*** 3417,3427 **** simplify_boolean_equality(Oid opno, List *args)
* Subroutine for eval_const_expressions: try to simplify a function call
* (which might originally have been an operator; we don't care)
*
! * Inputs are the function OID, actual result type OID (which is needed for
! * polymorphic functions), result typmod, result collation,
! * the input collation to use for the function,
! * the pre-simplified argument list, and some flags;
! * also the context data for eval_const_expressions.
*
* Returns a simplified expression if successful, or NULL if cannot
* simplify the function call.
--- 3422,3436 ----
* Subroutine for eval_const_expressions: try to simplify a function call
* (which might originally have been an operator; we don't care)
*
! * Inputs are the original expression (can be NULL), function OID, actual
! * result type OID (which is needed for polymorphic functions), result typmod,
! * result collation, the input collation to use for the function, the
! * pre-simplified argument list, and some flags; also the context data for
! * eval_const_expressions. In common cases, several of the arguments could be
! * derived from the original expression. Sending them separately avoids
! * duplicating NodeTag-specific knowledge, and it's necessary for CoerceViaIO.
! * A NULL original expression disables use of transform functions while
! * retaining all other behaviors.
*
* Returns a simplified expression if successful, or NULL if cannot
* simplify the function call.
***************
*** 3433,3454 **** simplify_boolean_equality(Oid opno, List *args)
* pass-by-reference, and it may get modified even if simplification fails.
*/
static Expr *
! simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
! Oid result_collid, Oid input_collid, List **args,
bool has_named_args,
bool allow_inline,
eval_const_expressions_context *context)
{
HeapTuple func_tuple;
Expr *newexpr;
/*
! * We have two strategies for simplification: either execute the function
! * to deliver a constant result, or expand in-line the body of the
! * function definition (which only works for simple SQL-language
! * functions, but that is a common case). In either case we need access
! * to the function's pg_proc tuple, so fetch it just once to use in both
! * attempts.
*/
func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(func_tuple))
--- 3442,3465 ----
* pass-by-reference, and it may get modified even if simplification fails.
*/
static Expr *
! simplify_function(Expr *oldexpr, Oid funcid,
! Oid result_type, int32 result_typmod, Oid result_collid,
! Oid input_collid, List **args,
bool has_named_args,
bool allow_inline,
eval_const_expressions_context *context)
{
HeapTuple func_tuple;
Expr *newexpr;
+ Oid transform;
/*
! * We have three strategies for simplification: execute the function to
! * deliver a constant result, use a transform function to generate a
! * substitute node tree, or expand in-line the body of the function
! * definition (which only works for simple SQL-language functions, but
! * that is a common case). Each needs access to the function's pg_proc
! * tuple, so fetch it just once.
*/
func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
if (!HeapTupleIsValid(func_tuple))
***************
*** 3468,3473 **** simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
--- 3479,3518 ----
result_collid, input_collid, *args,
func_tuple, context);
+ /*
+ * Some functions calls can be simplified at plan time based on properties
+ * specific to the function. For example, "varchar(s::varchar(4), 8,
+ * true)" simplifies to "s::varchar(4)", and "int4mul(n, 1)" could
+ * simplify to "n". To define such function-specific optimizations, write
+ * a "transform function" and store its OID in the pg_proc.protransform of
+ * the primary function. Give each transform function the signature
+ * "protransform(internal) RETURNS internal". The argument, internally an
+ * Expr *, is the node representing a call to the primary function. If
+ * the transform function's study of that node proves that a simplified
+ * Expr substitutes for all possible concrete calls represented thereby,
+ * return that simplified Expr. Otherwise, return the NULL pointer.
+ *
+ * Currently, the specific Expr nodetag can be FuncExpr, OpExpr or
+ * DistinctExpr. This list may change in the future. The function should
+ * check the nodetag and return the NULL pointer for unexpected inputs.
+ *
+ * We make no guarantee that PostgreSQL will never call the primary
+ * function in cases that the transform function would simplify. Ensure
+ * rigorous equivalence between the simplified expression and an actual
+ * call to the primary function.
+ *
+ * Currently, this facility is undocumented and not exposed to users at
+ * the SQL level. Core length coercion casts use it to avoid calls
+ * guaranteed to return their input unchanged. This in turn allows ALTER
+ * TABLE ALTER TYPE to avoid rewriting tables for some typmod changes. In
+ * the future, this facility may find other applications, like simplifying
+ * x*0, x*1, and x+0.
+ */
+ transform = ((Form_pg_proc) GETSTRUCT(func_tuple))->protransform;
+ if (!newexpr && OidIsValid(transform) && oldexpr)
+ newexpr = (Expr *) DatumGetPointer(OidFunctionCall1(transform,
+ PointerGetDatum(oldexpr)));
+
if (!newexpr && allow_inline)
newexpr = inline_function(funcid, result_type, result_collid,
input_collid, *args,
diff --git a/src/backend/parser/parse_clausindex c5fe6b6..a8549e0 100644
*** a/src/backend/parser/parse_clause.c
--- b/src/backend/parser/parse_clause.c
***************
*** 2278,2280 **** transformFrameOffset(ParseState *pstate, int frameOptions, Node *clause)
--- 2278,2302 ----
return node;
}
+
+ /*
+ * relabel_to_typmod
+ * Add a RelabelType node that changes just the typmod, and remove all
+ * now-superfluous RelabelType nodes beneath it.
+ */
+ Node *
+ relabel_to_typmod(Node *expr, int32 typmod)
+ {
+ Oid type = exprType(expr);
+ Oid coll = exprCollation(expr);
+
+ /*
+ * Strip any existing RelabelType, then add one. This is to preserve the
+ * invariant of no redundant RelabelTypes.
+ */
+ while (IsA(expr, RelabelType))
+ expr = (Node *) ((RelabelType *) expr)->arg;
+
+ return (Node *) makeRelabelType((Expr *) expr, type, typmod, coll,
+ COERCE_DONTCARE);
+ }
diff --git a/src/backend/utils/adt/varchindex 1c0ef92..efa483d 100644
*** a/src/backend/utils/adt/varchar.c
--- b/src/backend/utils/adt/varchar.c
***************
*** 18,23 ****
--- 18,25 ----
#include "access/hash.h"
#include "access/tuptoaster.h"
#include "libpq/pqformat.h"
+ #include "nodes/nodeFuncs.h"
+ #include "parser/parse_clause.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "mb/pg_wchar.h"
***************
*** 549,554 **** varcharsend(PG_FUNCTION_ARGS)
--- 551,588 ----
/*
+ * Flatten calls to our length coercion function that leave the new maximum
+ * length >= the previous maximum length. We ignore the isExplicit argument,
+ * which only affects truncation.
+ */
+ Datum
+ varchar_transform(PG_FUNCTION_ARGS)
+ {
+ FuncExpr *expr = (FuncExpr *) PG_GETARG_POINTER(0);
+ Node *typmod;
+ Node *ret = NULL;
+
+ if (!IsA(expr, FuncExpr))
+ PG_RETURN_POINTER(ret);
+
+ Assert(list_length(expr->args) == 3);
+ typmod = lsecond(expr->args);
+
+ if (IsA(typmod, Const))
+ {
+ Node *source = linitial(expr->args);
+ int32 new_typmod = DatumGetInt32(((Const *) typmod)->constvalue);
+ int32 old_max = exprTypmod(source) - VARHDRSZ;
+ int32 new_max = new_typmod - VARHDRSZ;
+
+ if (new_max < 0 || (old_max >= 0 && old_max <= new_max))
+ ret = relabel_to_typmod(source, new_typmod);
+ }
+
+ PG_RETURN_POINTER(ret);
+ }
+
+ /*
* Converts a VARCHAR type to the specified size.
*
* maxlen is the typmod, ie, declared length plus VARHDRSZ bytes.
diff --git a/src/include/catalog/pg_clindex ffcce3c..002ae6b 100644
*** a/src/include/catalog/pg_class.h
--- b/src/include/catalog/pg_class.h
***************
*** 134,140 **** DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 20 0 f f f f f 3 _null_ _null_ ));
DESCR("");
! DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 25 0 t f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 26 0 t f f f f 3 _null_ _null_ ));
DESCR("");
--- 134,140 ----
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 20 0 f f f f f 3 _null_ _null_ ));
DESCR("");
! DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 26 0 t f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1259 ( pg_class PGNSP 83 0 PGUID 0 0 0 0 0 0 0 f f p r 26 0 t f f f f 3 _null_ _null_ ));
DESCR("");
diff --git a/src/include/catalog/pg_pindex a66ecf1..6980d3e 100644
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 42,47 **** CATALOG(pg_proc,1255) BKI_BOOTSTRAP BKI_ROWTYPE_OID(81) BKI_SCHEMA_MACRO
--- 42,48 ----
float4 procost; /* estimated execution cost */
float4 prorows; /* estimated # of rows out (if proretset) */
Oid provariadic; /* element type of variadic array, or 0 */
+ regproc protransform; /* transforms calls to it during planning */
bool proisagg; /* is it an aggregate? */
bool proiswindow; /* is it a window function? */
bool prosecdef; /* security definer */
***************
*** 76,82 **** typedef FormData_pg_proc *Form_pg_proc;
* compiler constants for pg_proc
* ----------------
*/
! #define Natts_pg_proc 25
#define Anum_pg_proc_proname 1
#define Anum_pg_proc_pronamespace 2
#define Anum_pg_proc_proowner 3
--- 77,83 ----
* compiler constants for pg_proc
* ----------------
*/
! #define Natts_pg_proc 26
#define Anum_pg_proc_proname 1
#define Anum_pg_proc_pronamespace 2
#define Anum_pg_proc_proowner 3
***************
*** 84,107 **** typedef FormData_pg_proc *Form_pg_proc;
#define Anum_pg_proc_procost 5
#define Anum_pg_proc_prorows 6
#define Anum_pg_proc_provariadic 7
! #define Anum_pg_proc_proisagg 8
! #define Anum_pg_proc_proiswindow 9
! #define Anum_pg_proc_prosecdef 10
! #define Anum_pg_proc_proisstrict 11
! #define Anum_pg_proc_proretset 12
! #define Anum_pg_proc_provolatile 13
! #define Anum_pg_proc_pronargs 14
! #define Anum_pg_proc_pronargdefaults 15
! #define Anum_pg_proc_prorettype 16
! #define Anum_pg_proc_proargtypes 17
! #define Anum_pg_proc_proallargtypes 18
! #define Anum_pg_proc_proargmodes 19
! #define Anum_pg_proc_proargnames 20
! #define Anum_pg_proc_proargdefaults 21
! #define Anum_pg_proc_prosrc 22
! #define Anum_pg_proc_probin 23
! #define Anum_pg_proc_proconfig 24
! #define Anum_pg_proc_proacl 25
/* ----------------
* initial contents of pg_proc
--- 85,109 ----
#define Anum_pg_proc_procost 5
#define Anum_pg_proc_prorows 6
#define Anum_pg_proc_provariadic 7
! #define Anum_pg_proc_protransform 8
! #define Anum_pg_proc_proisagg 9
! #define Anum_pg_proc_proiswindow 10
! #define Anum_pg_proc_prosecdef 11
! #define Anum_pg_proc_proisstrict 12
! #define Anum_pg_proc_proretset 13
! #define Anum_pg_proc_provolatile 14
! #define Anum_pg_proc_pronargs 15
! #define Anum_pg_proc_pronargdefaults 16
! #define Anum_pg_proc_prorettype 17
! #define Anum_pg_proc_proargtypes 18
! #define Anum_pg_proc_proallargtypes 19
! #define Anum_pg_proc_proargmodes 20
! #define Anum_pg_proc_proargnames 21
! #define Anum_pg_proc_proargdefaults 22
! #define Anum_pg_proc_prosrc 23
! #define Anum_pg_proc_probin 24
! #define Anum_pg_proc_proconfig 25
! #define Anum_pg_proc_proacl 26
/* ----------------
* initial contents of pg_proc
***************
*** 743,749 **** DATA(insert OID = 659 ( namene PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 16 "
DATA(insert OID = 668 ( bpchar PGNSP PGUID 12 1 0 0 0 f f f t f i 3 0 1042 "1042 23 16" _null_ _null_ _null_ _null_ bpchar _null_ _null_ _null_ ));
DESCR("adjust char() to typmod length");
! DATA(insert OID = 669 ( varchar PGNSP PGUID 12 1 0 0 0 f f f t f i 3 0 1043 "1043 23 16" _null_ _null_ _null_ _null_ varchar _null_ _null_ _null_ ));
DESCR("adjust varchar() to typmod length");
DATA(insert OID = 676 ( mktinterval PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 704 "702 702" _null_ _null_ _null_ _null_ mktinterval _null_ _null_ _null_ ));
--- 745,753 ----
DATA(insert OID = 668 ( bpchar PGNSP PGUID 12 1 0 0 0 f f f t f i 3 0 1042 "1042 23 16" _null_ _null_ _null_ _null_ bpchar _null_ _null_ _null_ ));
DESCR("adjust char() to typmod length");
! DATA(insert OID = 3097 ( varchar_transform PGNSP PGUID 12 1 0 0 0 f f f t f i 1 0 2281 "2281" _null_ _null_ _null_ _null_ varchar_transform _null_ _null_ _null_ ));
! DESCR("transform a varchar length coercion");
! DATA(insert OID = 669 ( varchar PGNSP PGUID 12 1 0 0 3097 f f f t f i 3 0 1043 "1043 23 16" _null_ _null_ _null_ _null_ varchar _null_ _null_ _null_ ));
DESCR("adjust varchar() to typmod length");
DATA(insert OID = 676 ( mktinterval PGNSP PGUID 12 1 0 0 0 f f f t f i 2 0 704 "702 702" _null_ _null_ _null_ _null_ mktinterval _null_ _null_ _null_ ));
diff --git a/src/include/parser/parsindex 09ef450..f5ce2ff 100644
*** a/src/include/parser/parse_clause.h
--- b/src/include/parser/parse_clause.h
***************
*** 44,47 **** extern List *transformDistinctOnClause(ParseState *pstate, List *distinctlist,
--- 44,49 ----
extern Index assignSortGroupRef(TargetEntry *tle, List *tlist);
extern bool targetIsInSortList(TargetEntry *tle, Oid sortop, List *sortList);
+ extern Node *relabel_to_typmod(Node *expr, int32 typmod);
+
#endif /* PARSE_CLAUSE_H */
diff --git a/src/include/utils/builtins.index 14215db..8a1c82e 100644
*** a/src/include/utils/builtins.h
--- b/src/include/utils/builtins.h
***************
*** 684,689 **** extern Datum varcharrecv(PG_FUNCTION_ARGS);
--- 684,690 ----
extern Datum varcharsend(PG_FUNCTION_ARGS);
extern Datum varchartypmodin(PG_FUNCTION_ARGS);
extern Datum varchartypmodout(PG_FUNCTION_ARGS);
+ extern Datum varchar_transform(PG_FUNCTION_ARGS);
extern Datum varchar(PG_FUNCTION_ARGS);
/* varlena.c */
diff --git a/src/test/regress/expecindex 9171f04..8e75b14 100644