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

Lines changed: 9 additions & 4 deletions
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

Lines changed: 135 additions & 39 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy