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 + + + proisagg bool 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