@@ -81,7 +81,8 @@ typedef struct
8181char * fname ;/* function name (for error msgs) */
8282char * src ;/* function body text (for error msgs) */
8383
84- Oid * argtypes ;/* resolved types of arguments */
84+ SQLFunctionParseInfoPtr pinfo ;/* data for parser callback hooks */
85+
8586Oid rettype ;/* actual return type */
8687int16 typlen ;/* length of the return type */
8788bool typbyval ;/* true if return type is pass by value */
@@ -108,8 +109,21 @@ typedef struct
108109
109110typedef SQLFunctionCache * SQLFunctionCachePtr ;
110111
112+ /*
113+ * Data structure needed by the parser callback hooks to resolve parameter
114+ * references during parsing of a SQL function's body. This is separate from
115+ * SQLFunctionCache since we sometimes do parsing separately from execution.
116+ */
117+ typedef struct SQLFunctionParseInfo
118+ {
119+ Oid * argtypes ;/* resolved types of input arguments */
120+ int nargs ;/* number of input arguments */
121+ Oid collation ;/* function's input collation, if known */
122+ }SQLFunctionParseInfo ;
123+
111124
112125/* non-export function prototypes */
126+ static Node * sql_fn_param_ref (ParseState * pstate ,ParamRef * pref );
113127static List * init_execution_state (List * queryTree_list ,
114128SQLFunctionCachePtr fcache ,
115129bool lazyEvalOK );
@@ -131,6 +145,112 @@ static void sqlfunction_shutdown(DestReceiver *self);
131145static void sqlfunction_destroy (DestReceiver * self );
132146
133147
148+ /*
149+ * Prepare the SQLFunctionParseInfo struct for parsing a SQL function body
150+ *
151+ * This includes resolving actual types of polymorphic arguments.
152+ *
153+ * call_expr can be passed as NULL, but then we will fail if there are any
154+ * polymorphic arguments.
155+ */
156+ SQLFunctionParseInfoPtr
157+ prepare_sql_fn_parse_info (HeapTuple procedureTuple ,
158+ Node * call_expr ,
159+ Oid inputCollation )
160+ {
161+ SQLFunctionParseInfoPtr pinfo ;
162+ Form_pg_proc procedureStruct = (Form_pg_proc )GETSTRUCT (procedureTuple );
163+ int nargs ;
164+
165+ pinfo = (SQLFunctionParseInfoPtr )palloc0 (sizeof (SQLFunctionParseInfo ));
166+
167+ /* Save the function's input collation */
168+ pinfo -> collation = inputCollation ;
169+
170+ /*
171+ * Copy input argument types from the pg_proc entry, then resolve any
172+ * polymorphic types.
173+ */
174+ pinfo -> nargs = nargs = procedureStruct -> pronargs ;
175+ if (nargs > 0 )
176+ {
177+ Oid * argOidVect ;
178+ int argnum ;
179+
180+ argOidVect = (Oid * )palloc (nargs * sizeof (Oid ));
181+ memcpy (argOidVect ,
182+ procedureStruct -> proargtypes .values ,
183+ nargs * sizeof (Oid ));
184+
185+ for (argnum = 0 ;argnum < nargs ;argnum ++ )
186+ {
187+ Oid argtype = argOidVect [argnum ];
188+
189+ if (IsPolymorphicType (argtype ))
190+ {
191+ argtype = get_call_expr_argtype (call_expr ,argnum );
192+ if (argtype == InvalidOid )
193+ ereport (ERROR ,
194+ (errcode (ERRCODE_DATATYPE_MISMATCH ),
195+ errmsg ("could not determine actual type of argument declared %s" ,
196+ format_type_be (argOidVect [argnum ]))));
197+ argOidVect [argnum ]= argtype ;
198+ }
199+ }
200+
201+ pinfo -> argtypes = argOidVect ;
202+ }
203+
204+ return pinfo ;
205+ }
206+
207+ /*
208+ * Parser setup hook for parsing a SQL function body.
209+ */
210+ void
211+ sql_fn_parser_setup (struct ParseState * pstate ,SQLFunctionParseInfoPtr pinfo )
212+ {
213+ /* Later we might use these hooks to support parameter names */
214+ pstate -> p_pre_columnref_hook = NULL ;
215+ pstate -> p_post_columnref_hook = NULL ;
216+ pstate -> p_paramref_hook = sql_fn_param_ref ;
217+ /* no need to use p_coerce_param_hook */
218+ pstate -> p_ref_hook_state = (void * )pinfo ;
219+ }
220+
221+ /*
222+ * sql_fn_param_refparser callback for ParamRefs ($n symbols)
223+ */
224+ static Node *
225+ sql_fn_param_ref (ParseState * pstate ,ParamRef * pref )
226+ {
227+ SQLFunctionParseInfoPtr pinfo = (SQLFunctionParseInfoPtr )pstate -> p_ref_hook_state ;
228+ int paramno = pref -> number ;
229+ Param * param ;
230+
231+ /* Check parameter number is valid */
232+ if (paramno <=0 || paramno > pinfo -> nargs )
233+ return NULL ;/* unknown parameter number */
234+
235+ param = makeNode (Param );
236+ param -> paramkind = PARAM_EXTERN ;
237+ param -> paramid = paramno ;
238+ param -> paramtype = pinfo -> argtypes [paramno - 1 ];
239+ param -> paramtypmod = -1 ;
240+ param -> paramcollid = get_typcollation (param -> paramtype );
241+ param -> location = pref -> location ;
242+
243+ /*
244+ * If we have a function input collation, allow it to override the
245+ * type-derived collation for parameter symbols. (XXX perhaps this should
246+ * not happen if the type collation is not default?)
247+ */
248+ if (OidIsValid (pinfo -> collation )&& OidIsValid (param -> paramcollid ))
249+ param -> paramcollid = pinfo -> collation ;
250+
251+ return (Node * )param ;
252+ }
253+
134254/*
135255 * Set up the per-query execution_state records for a SQL function.
136256 *
@@ -239,7 +359,9 @@ init_execution_state(List *queryTree_list,
239359return eslist ;
240360}
241361
242- /* Initialize the SQLFunctionCache for a SQL function */
362+ /*
363+ * Initialize the SQLFunctionCache for a SQL function
364+ */
243365static void
244366init_sql_fcache (FmgrInfo * finfo ,bool lazyEvalOK )
245367{
@@ -248,8 +370,6 @@ init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK)
248370HeapTuple procedureTuple ;
249371Form_pg_proc procedureStruct ;
250372SQLFunctionCachePtr fcache ;
251- Oid * argOidVect ;
252- int nargs ;
253373List * raw_parsetree_list ;
254374List * queryTree_list ;
255375List * flat_query_list ;
@@ -302,37 +422,13 @@ init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK)
302422(procedureStruct -> provolatile != PROVOLATILE_VOLATILE );
303423
304424/*
305- * We need the actual argument types to pass to the parser.
425+ * We need the actual argument types to pass to the parser. Also make
426+ * sure that parameter symbols are considered to have the function's
427+ * resolved input collation.
306428 */
307- nargs = procedureStruct -> pronargs ;
308- if (nargs > 0 )
309- {
310- int argnum ;
311-
312- argOidVect = (Oid * )palloc (nargs * sizeof (Oid ));
313- memcpy (argOidVect ,
314- procedureStruct -> proargtypes .values ,
315- nargs * sizeof (Oid ));
316- /* Resolve any polymorphic argument types */
317- for (argnum = 0 ;argnum < nargs ;argnum ++ )
318- {
319- Oid argtype = argOidVect [argnum ];
320-
321- if (IsPolymorphicType (argtype ))
322- {
323- argtype = get_fn_expr_argtype (finfo ,argnum );
324- if (argtype == InvalidOid )
325- ereport (ERROR ,
326- (errcode (ERRCODE_DATATYPE_MISMATCH ),
327- errmsg ("could not determine actual type of argument declared %s" ,
328- format_type_be (argOidVect [argnum ]))));
329- argOidVect [argnum ]= argtype ;
330- }
331- }
332- }
333- else
334- argOidVect = NULL ;
335- fcache -> argtypes = argOidVect ;
429+ fcache -> pinfo = prepare_sql_fn_parse_info (procedureTuple ,
430+ finfo -> fn_expr ,
431+ finfo -> fn_collation );
336432
337433/*
338434 * And of course we need the function body text.
@@ -364,10 +460,10 @@ init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK)
364460Node * parsetree = (Node * )lfirst (lc );
365461List * queryTree_sublist ;
366462
367- queryTree_sublist = pg_analyze_and_rewrite (parsetree ,
368- fcache -> src ,
369- argOidVect ,
370- nargs );
463+ queryTree_sublist = pg_analyze_and_rewrite_params (parsetree ,
464+ fcache -> src ,
465+ ( ParserSetupHook ) sql_fn_parser_setup ,
466+ fcache -> pinfo );
371467queryTree_list = lappend (queryTree_list ,queryTree_sublist );
372468flat_query_list = list_concat (flat_query_list ,
373469list_copy (queryTree_sublist ));
@@ -583,7 +679,7 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
583679prm -> value = fcinfo -> arg [i ];
584680prm -> isnull = fcinfo -> argnull [i ];
585681prm -> pflags = 0 ;
586- prm -> ptype = fcache -> argtypes [i ];
682+ prm -> ptype = fcache -> pinfo -> argtypes [i ];
587683}
588684}
589685else