@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $ PostgreSQL : pgsql / src / backend / optimizer / util / clauses . c , v 1.278 2009 / 07 / 20 00 : 24 : 30 tgl Exp $
* $ PostgreSQL : pgsql / src / backend / optimizer / util / clauses . c , v 1.279 2009 / 10 / 08 02 : 39 : 21 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -95,11 +95,18 @@ static List *simplify_and_arguments(List *args,
static Expr * simplify_boolean_equality ( Oid opno , List * args ) ;
static Expr * simplify_function ( Oid funcid ,
Oid result_type , int32 result_typmod , List * * args ,
bool has_named_args ,
bool allow_inline ,
eval_const_expressions_context * context ) ;
static List * reorder_function_arguments ( List * args , Oid result_type ,
HeapTuple func_tuple ,
eval_const_expressions_context * context ) ;
static List * add_function_defaults ( List * args , Oid result_type ,
HeapTuple func_tuple ,
eval_const_expressions_context * context ) ;
static List * fetch_function_defaults ( HeapTuple func_tuple ) ;
static void recheck_cast_function_args ( List * args , Oid result_type ,
HeapTuple func_tuple ) ;
static Expr * evaluate_function ( Oid funcid ,
Oid result_type , int32 result_typmod , List * args ,
HeapTuple func_tuple ,
@ -2003,7 +2010,8 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
* OR clauses into N - argument form . See comments in prepqual . c .
*
* NOTE : another critical effect is that any function calls that require
* default arguments will be expanded .
* default arguments will be expanded , and named - argument calls will be
* converted to positional notation . The executor won ' t handle either .
* - - - - - - - - - - - - - - - - - - - -
*/
Node *
@ -2113,17 +2121,26 @@ eval_const_expressions_mutator(Node *node,
{
FuncExpr * expr = ( FuncExpr * ) node ;
List * args ;
bool has_named_args ;
Expr * simple ;
FuncExpr * newexpr ;
ListCell * lc ;
/*
* Reduce constants in the FuncExpr ' s arguments . We know args is
* either NIL or a List node , so we can call expression_tree_mutator
* directly rather than recursing to self .
* Reduce constants in the FuncExpr ' s arguments , and check to see
* if there are any named args .
*/
args = ( List * ) expression_tree_mutator ( ( Node * ) expr - > args ,
eval_const_expressions_mutator ,
( void * ) context ) ;
args = NIL ;
has_named_args = false ;
foreach ( lc , expr - > args )
{
Node * arg = ( Node * ) lfirst ( lc ) ;
arg = eval_const_expressions_mutator ( arg , context ) ;
if ( IsA ( arg , NamedArgExpr ) )
has_named_args = true ;
args = lappend ( args , arg ) ;
}
/*
* Code for op / func reduction is pretty bulky , so split it out as a
@ -2134,14 +2151,15 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function ( expr - > funcid ,
expr - > funcresulttype , exprTypmod ( node ) ,
& args ,
true , context ) ;
has_named_args , true , context ) ;
if ( simple ) /* successfully simplified it */
return ( Node * ) simple ;
/*
* The expression cannot be simplified any further , so build and
* return a replacement FuncExpr node using the possibly - simplified
* arguments .
* arguments . Note that we have also converted the argument list
* to positional notation .
*/
newexpr = makeNode ( FuncExpr ) ;
newexpr - > funcid = expr - > funcid ;
@ -2181,7 +2199,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function ( expr - > opfuncid ,
expr - > opresulttype , - 1 ,
& args ,
true , context ) ;
false , true , context ) ;
if ( simple ) /* successfully simplified it */
return ( Node * ) simple ;
@ -2274,7 +2292,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function ( expr - > opfuncid ,
expr - > opresulttype , - 1 ,
& args ,
false , context ) ;
false , false , context ) ;
if ( simple ) /* successfully simplified it */
{
/*
@ -2466,7 +2484,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function ( outfunc ,
CSTRINGOID , - 1 ,
& args ,
true , context ) ;
false , true , context ) ;
if ( simple ) /* successfully simplified output fn */
{
/*
@ -2484,7 +2502,7 @@ eval_const_expressions_mutator(Node *node,
simple = simplify_function ( infunc ,
expr - > resulttype , - 1 ,
& args ,
true , context ) ;
false , true , context ) ;
if ( simple ) /* successfully simplified input fn */
return ( Node * ) simple ;
}
@ -3241,15 +3259,16 @@ simplify_boolean_equality(Oid opno, List *args)
* Returns a simplified expression if successful , or NULL if cannot
* simplify the function call .
*
* This function is also responsible for adding any default argument
* expressions onto the function argument list ; which is a bit grotty ,
* but it avoids an extra fetch of the function ' s pg_proc tuple . For this
* reason , the args list is pass - by - reference , and it may get modified
* even if simplification fails .
* This function is also responsible for converting named - notation argument
* lists into positional notation and / or adding any needed default argument
* expressions ; which is a bit grotty , but it avoids an extra fetch of the
* function ' s pg_proc tuple . For this reason , the args list is
* pass - by - reference , and it may get modified even if simplification fails .
*/
static Expr *
simplify_function ( Oid funcid , Oid result_type , int32 result_typmod ,
List * * args ,
bool has_named_args ,
bool allow_inline ,
eval_const_expressions_context * context )
{
@ -3270,8 +3289,14 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
if ( ! HeapTupleIsValid ( func_tuple ) )
elog ( ERROR , " cache lookup failed for function %u " , funcid ) ;
/* While we have the tuple, check if we need to add defaults */
if ( ( ( Form_pg_proc ) GETSTRUCT ( func_tuple ) ) - > pronargs > list_length ( * args ) )
/*
* While we have the tuple , reorder named arguments and add default
* arguments if needed .
*/
if ( has_named_args )
* args = reorder_function_arguments ( * args , result_type , func_tuple ,
context ) ;
else if ( ( ( Form_pg_proc ) GETSTRUCT ( func_tuple ) ) - > pronargs > list_length ( * args ) )
* args = add_function_defaults ( * args , result_type , func_tuple , context ) ;
newexpr = evaluate_function ( funcid , result_type , result_typmod , * args ,
@ -3286,13 +3311,113 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
return newexpr ;
}
/*
* reorder_function_arguments : convert named - notation args to positional args
*
* This function also inserts default argument values as needed , since it ' s
* impossible to form a truly valid positional call without that .
*/
static List *
reorder_function_arguments ( List * args , Oid result_type , HeapTuple func_tuple ,
eval_const_expressions_context * context )
{
Form_pg_proc funcform = ( Form_pg_proc ) GETSTRUCT ( func_tuple ) ;
int pronargs = funcform - > pronargs ;
int nargsprovided = list_length ( args ) ;
Node * argarray [ FUNC_MAX_ARGS ] ;
Bitmapset * defargnumbers ;
ListCell * lc ;
int i ;
Assert ( nargsprovided < = pronargs ) ;
if ( pronargs > FUNC_MAX_ARGS )
elog ( ERROR , " too many function arguments " ) ;
MemSet ( argarray , 0 , pronargs * sizeof ( Node * ) ) ;
/* Deconstruct the argument list into an array indexed by argnumber */
i = 0 ;
foreach ( lc , args )
{
Node * arg = ( Node * ) lfirst ( lc ) ;
if ( ! IsA ( arg , NamedArgExpr ) )
{
/* positional argument, assumed to precede all named args */
Assert ( argarray [ i ] = = NULL ) ;
argarray [ i + + ] = arg ;
}
else
{
NamedArgExpr * na = ( NamedArgExpr * ) arg ;
Assert ( argarray [ na - > argnumber ] = = NULL ) ;
argarray [ na - > argnumber ] = ( Node * ) na - > arg ;
}
}
/*
* Fetch default expressions , if needed , and insert into array at
* proper locations ( they aren ' t necessarily consecutive or all used )
*/
defargnumbers = NULL ;
if ( nargsprovided < pronargs )
{
List * defaults = fetch_function_defaults ( func_tuple ) ;
i = pronargs - funcform - > pronargdefaults ;
foreach ( lc , defaults )
{
if ( argarray [ i ] = = NULL )
{
argarray [ i ] = ( Node * ) lfirst ( lc ) ;
defargnumbers = bms_add_member ( defargnumbers , i ) ;
}
i + + ;
}
}
/* Now reconstruct the args list in proper order */
args = NIL ;
for ( i = 0 ; i < pronargs ; i + + )
{
Assert ( argarray [ i ] ! = NULL ) ;
args = lappend ( args , argarray [ i ] ) ;
}
/* Recheck argument types and add casts if needed */
recheck_cast_function_args ( args , result_type , func_tuple ) ;
/*
* Lastly , we have to recursively simplify the defaults we just added
* ( but don ' t recurse on the args passed in , as we already did those ) .
* This isn ' t merely an optimization , it ' s * necessary * since there could
* be functions with named or defaulted arguments down in there .
*
* Note that we do this last in hopes of simplifying any typecasts that
* were added by recheck_cast_function_args - - - there shouldn ' t be any new
* casts added to the explicit arguments , but casts on the defaults are
* possible .
*/
if ( defargnumbers ! = NULL )
{
i = 0 ;
foreach ( lc , args )
{
if ( bms_is_member ( i , defargnumbers ) )
lfirst ( lc ) = eval_const_expressions_mutator ( ( Node * ) lfirst ( lc ) ,
context ) ;
i + + ;
}
}
return args ;
}
/*
* add_function_defaults : add missing function arguments from its defaults
*
* It is possible for some of the defaulted arguments to be polymorphic ;
* therefore we can ' t assume that the default expressions have the correct
* data types already . We have to re - resolve polymorphics and do coercion
* just like the parser did .
* This is used only when the argument list was positional to begin with ,
* and so we know we just need to add defaults at the end .
*/
static List *
add_function_defaults ( List * args , Oid result_type , HeapTuple func_tuple ,
@ -3300,41 +3425,96 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
{
Form_pg_proc funcform = ( Form_pg_proc ) GETSTRUCT ( func_tuple ) ;
int nargsprovided = list_length ( args ) ;
Datum proargdefaults ;
bool isnull ;
char * str ;
List * defaults ;
int ndelete ;
int nargs ;
Oid actual_arg_types [ FUNC_MAX_ARGS ] ;
Oid declared_arg_types [ FUNC_MAX_ARGS ] ;
Oid rettype ;
ListCell * lc ;
/* The error cases here shouldn't happen, but check anyway */
proargdefaults = SysCacheGetAttr ( PROCOID , func_tuple ,
Anum_pg_proc_proargdefaults ,
& isnull ) ;
if ( isnull )
elog ( ERROR , " not enough default arguments " ) ;
str = TextDatumGetCString ( proargdefaults ) ;
defaults = ( List * ) stringToNode ( str ) ;
Assert ( IsA ( defaults , List ) ) ;
pfree ( str ) ;
/* Get all the default expressions from the pg_proc tuple */
defaults = fetch_function_defaults ( func_tuple ) ;
/* Delete any unused defaults from the list */
ndelete = nargsprovided + list_length ( defaults ) - funcform - > pronargs ;
if ( ndelete < 0 )
elog ( ERROR , " not enough default arguments " ) ;
while ( ndelete - - > 0 )
defaults = list_delete_first ( defaults ) ;
/* And form the combined argument list */
args = list_concat ( args , defaults ) ;
Assert ( list_length ( args ) = = funcform - > pronargs ) ;
/* Recheck argument types and add casts if needed */
recheck_cast_function_args ( args , result_type , func_tuple ) ;
/*
* The next part should be a no - op if there are no polymorphic arguments ,
* but we do it anyway to be sure .
* Lastly , we have to recursively simplify the defaults we just added
* ( but don ' t recurse on the args passed in , as we already did those ) .
* This isn ' t merely an optimization , it ' s * necessary * since there could
* be functions with named or defaulted arguments down in there .
*
* Note that we do this last in hopes of simplifying any typecasts that
* were added by recheck_cast_function_args - - - there shouldn ' t be any new
* casts added to the explicit arguments , but casts on the defaults are
* possible .
*/
foreach ( lc , args )
{
if ( nargsprovided - - > 0 )
continue ; /* skip original arg positions */
lfirst ( lc ) = eval_const_expressions_mutator ( ( Node * ) lfirst ( lc ) ,
context ) ;
}
return args ;
}
/*
* fetch_function_defaults : get function ' s default arguments as expression list
*/
static List *
fetch_function_defaults ( HeapTuple func_tuple )
{
List * defaults ;
Datum proargdefaults ;
bool isnull ;
char * str ;
/* The error cases here shouldn't happen, but check anyway */
proargdefaults = SysCacheGetAttr ( PROCOID , func_tuple ,
Anum_pg_proc_proargdefaults ,
& isnull ) ;
if ( isnull )
elog ( ERROR , " not enough default arguments " ) ;
str = TextDatumGetCString ( proargdefaults ) ;
defaults = ( List * ) stringToNode ( str ) ;
Assert ( IsA ( defaults , List ) ) ;
pfree ( str ) ;
return defaults ;
}
/*
* recheck_cast_function_args : recheck function args and typecast as needed
* after adding defaults .
*
* It is possible for some of the defaulted arguments to be polymorphic ;
* therefore we can ' t assume that the default expressions have the correct
* data types already . We have to re - resolve polymorphics and do coercion
* just like the parser did .
*
* This should be a no - op if there are no polymorphic arguments ,
* but we do it anyway to be sure .
*
* Note : if any casts are needed , the args list is modified in - place .
*/
static void
recheck_cast_function_args ( List * args , Oid result_type , HeapTuple func_tuple )
{
Form_pg_proc funcform = ( Form_pg_proc ) GETSTRUCT ( func_tuple ) ;
int nargs ;
Oid actual_arg_types [ FUNC_MAX_ARGS ] ;
Oid declared_arg_types [ FUNC_MAX_ARGS ] ;
Oid rettype ;
ListCell * lc ;
if ( list_length ( args ) > FUNC_MAX_ARGS )
elog ( ERROR , " too many function arguments " ) ;
nargs = 0 ;
@ -3342,6 +3522,7 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
{
actual_arg_types [ nargs + + ] = exprType ( ( Node * ) lfirst ( lc ) ) ;
}
Assert ( nargs = = funcform - > pronargs ) ;
memcpy ( declared_arg_types , funcform - > proargtypes . values ,
funcform - > pronargs * sizeof ( Oid ) ) ;
rettype = enforce_generic_type_consistency ( actual_arg_types ,
@ -3355,22 +3536,6 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
/* perform any necessary typecasting of arguments */
make_fn_arguments ( NULL , args , actual_arg_types , declared_arg_types ) ;
/*
* Lastly , we have to recursively simplify the arguments we just added
* ( but don ' t recurse on the ones passed in , as we already did those ) .
* This isn ' t merely an optimization , it ' s * necessary * since there could
* be functions with defaulted arguments down in there .
*/
foreach ( lc , args )
{
if ( nargsprovided - - > 0 )
continue ; /* skip original arg positions */
lfirst ( lc ) = eval_const_expressions_mutator ( ( Node * ) lfirst ( lc ) ,
context ) ;
}
return args ;
}
/*
@ -3916,6 +4081,7 @@ Query *
inline_set_returning_function ( PlannerInfo * root , RangeTblEntry * rte )
{
FuncExpr * fexpr ;
Oid func_oid ;
HeapTuple func_tuple ;
Form_pg_proc funcform ;
Oid * argtypes ;
@ -3944,6 +4110,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
fexpr = ( FuncExpr * ) rte - > funcexpr ;
if ( fexpr = = NULL | | ! IsA ( fexpr , FuncExpr ) )
return NULL ;
func_oid = fexpr - > funcid ;
/*
* The function must be declared to return a set , else inlining would
@ -3967,17 +4134,17 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
return NULL ;
/* Check permission to call function (fail later, if not) */
if ( pg_proc_aclcheck ( fexpr - > func id , GetUserId ( ) , ACL_EXECUTE ) ! = ACLCHECK_OK )
if ( pg_proc_aclcheck ( func_o id , GetUserId ( ) , ACL_EXECUTE ) ! = ACLCHECK_OK )
return NULL ;
/*
* OK , let ' s take a look at the function ' s pg_proc entry .
*/
func_tuple = SearchSysCache ( PROCOID ,
ObjectIdGetDatum ( fexpr - > func id ) ,
ObjectIdGetDatum ( func_o id ) ,
0 , 0 , 0 ) ;
if ( ! HeapTupleIsValid ( func_tuple ) )
elog ( ERROR , " cache lookup failed for function %u " , fexpr - > func id ) ;
elog ( ERROR , " cache lookup failed for function %u " , func_o id ) ;
funcform = ( Form_pg_proc ) GETSTRUCT ( func_tuple ) ;
/*
@ -3985,16 +4152,15 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* properties . In particular it mustn ' t be declared STRICT , since we
* couldn ' t enforce that . It also mustn ' t be VOLATILE , because that is
* supposed to cause it to be executed with its own snapshot , rather than
* sharing the snapshot of the calling query . ( The nargs check is just
* paranoia , ditto rechecking proretset . )
* sharing the snapshot of the calling query . ( Rechecking proretset is
* just paranoia . )
*/
if ( funcform - > prolang ! = SQLlanguageId | |
funcform - > proisstrict | |
funcform - > provolatile = = PROVOLATILE_VOLATILE | |
funcform - > prosecdef | |
! funcform - > proretset | |
! heap_attisnull ( func_tuple , Anum_pg_proc_proconfig ) | |
funcform - > pronargs ! = list_length ( fexpr - > args ) )
! heap_attisnull ( func_tuple , Anum_pg_proc_proconfig ) )
{
ReleaseSysCache ( func_tuple ) ;
return NULL ;
@ -4020,6 +4186,24 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
ALLOCSET_DEFAULT_MAXSIZE ) ;
oldcxt = MemoryContextSwitchTo ( mycxt ) ;
/*
* Run eval_const_expressions on the function call . This is necessary
* to ensure that named - argument notation is converted to positional
* notation and any default arguments are inserted . It ' s a bit of
* overkill for the arguments , since they ' ll get processed again later ,
* but no harm will be done .
*/
fexpr = ( FuncExpr * ) eval_const_expressions ( root , ( Node * ) fexpr ) ;
/* It should still be a call of the same function, but let's check */
if ( ! IsA ( fexpr , FuncExpr ) | |
fexpr - > funcid ! = func_oid )
goto fail ;
/* Arg list length should now match the function */
if ( list_length ( fexpr - > args ) ! = funcform - > pronargs )
goto fail ;
/* Check for polymorphic arguments, and substitute actual arg types */
argtypes = ( Oid * ) palloc ( funcform - > pronargs * sizeof ( Oid ) ) ;
memcpy ( argtypes , funcform - > proargtypes . values ,
@ -4038,7 +4222,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
Anum_pg_proc_prosrc ,
& isNull ) ;
if ( isNull )
elog ( ERROR , " null prosrc for function %u " , fexpr - > func id ) ;
elog ( ERROR , " null prosrc for function %u " , func_o id ) ;
src = TextDatumGetCString ( tmp ) ;
/*
@ -4076,7 +4260,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* shows it ' s returning a whole tuple result ; otherwise what it ' s
* returning is a single composite column which is not what we need .
*/
if ( ! check_sql_fn_retval ( fexpr - > func id , fexpr - > funcresulttype ,
if ( ! check_sql_fn_retval ( func_o id , fexpr - > funcresulttype ,
querytree_list ,
true , NULL ) & &
( get_typtype ( fexpr - > funcresulttype ) = = TYPTYPE_COMPOSITE | |
@ -4116,7 +4300,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
* Since there is now no trace of the function in the plan tree , we must
* explicitly record the plan ' s dependency on the function .
*/
record_plan_function_dependency ( root - > glob , fexpr - > func id ) ;
record_plan_function_dependency ( root - > glob , func_o id ) ;
return querytree ;