Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 27dc7e2

Browse files
committed
Fix handling of collation in SQL-language functions.
Ensure that parameter symbols receive collation from the function's resolved input collation, and fix inlining to behave properly. BTW, this commit lays about 90% of the infrastructure needed to support use of argument names in SQL functions. Parsing of parameters is now done via the parser-hook infrastructure ... we'd just need to supply a column-ref hook ...
1 parent a432e27 commit 27dc7e2

File tree

7 files changed

+303
-76
lines changed

7 files changed

+303
-76
lines changed

src/backend/catalog/pg_proc.c

+9-4
Original file line numberDiff line numberDiff line change
@@ -845,16 +845,21 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
845845
* OK to do full precheck: analyze and rewrite the queries,
846846
* then verify the result type.
847847
*/
848+
SQLFunctionParseInfoPtr pinfo;
849+
850+
/* But first, set up parameter information */
851+
pinfo = prepare_sql_fn_parse_info(tuple, NULL, InvalidOid);
852+
848853
querytree_list = NIL;
849854
foreach(lc, raw_parsetree_list)
850855
{
851856
Node *parsetree = (Node *) lfirst(lc);
852857
List *querytree_sublist;
853858

854-
querytree_sublist = pg_analyze_and_rewrite(parsetree,
855-
prosrc,
856-
proc->proargtypes.values,
857-
proc->pronargs);
859+
querytree_sublist = pg_analyze_and_rewrite_params(parsetree,
860+
prosrc,
861+
(ParserSetupHook) sql_fn_parser_setup,
862+
pinfo);
858863
querytree_list = list_concat(querytree_list,
859864
querytree_sublist);
860865
}

src/backend/executor/functions.c

+135-39
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@ typedef struct
8181
char *fname; /* function name (for error msgs) */
8282
char *src; /* function body text (for error msgs) */
8383

84-
Oid *argtypes; /* resolved types of arguments */
84+
SQLFunctionParseInfoPtr pinfo; /* data for parser callback hooks */
85+
8586
Oid rettype; /* actual return type */
8687
int16 typlen; /* length of the return type */
8788
bool typbyval; /* true if return type is pass by value */
@@ -108,8 +109,21 @@ typedef struct
108109

109110
typedef 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);
113127
static List *init_execution_state(List *queryTree_list,
114128
SQLFunctionCachePtr fcache,
115129
bool lazyEvalOK);
@@ -131,6 +145,112 @@ static void sqlfunction_shutdown(DestReceiver *self);
131145
static 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_ref parser 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,
239359
return eslist;
240360
}
241361

242-
/* Initialize the SQLFunctionCache for a SQL function */
362+
/*
363+
* Initialize the SQLFunctionCache for a SQL function
364+
*/
243365
static void
244366
init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK)
245367
{
@@ -248,8 +370,6 @@ init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK)
248370
HeapTuple procedureTuple;
249371
Form_pg_proc procedureStruct;
250372
SQLFunctionCachePtr fcache;
251-
Oid *argOidVect;
252-
int nargs;
253373
List *raw_parsetree_list;
254374
List *queryTree_list;
255375
List *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)
364460
Node *parsetree = (Node *) lfirst(lc);
365461
List *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);
371467
queryTree_list = lappend(queryTree_list, queryTree_sublist);
372468
flat_query_list = list_concat(flat_query_list,
373469
list_copy(queryTree_sublist));
@@ -583,7 +679,7 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
583679
prm->value = fcinfo->arg[i];
584680
prm->isnull = fcinfo->argnull[i];
585681
prm->pflags = 0;
586-
prm->ptype = fcache->argtypes[i];
682+
prm->ptype = fcache->pinfo->argtypes[i];
587683
}
588684
}
589685
else

0 commit comments

Comments
 (0)