mirror of
https://github.com/postgres/postgres.git
synced 2025-09-03 15:22:11 +03:00
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 ...
This commit is contained in:
@@ -118,8 +118,8 @@ static Expr *evaluate_function(Oid funcid,
|
||||
Oid result_type, int32 result_typmod,
|
||||
Oid input_collid, List *args, HeapTuple func_tuple,
|
||||
eval_const_expressions_context *context);
|
||||
static Expr *inline_function(Oid funcid, Oid result_type, List *args,
|
||||
HeapTuple func_tuple,
|
||||
static Expr *inline_function(Oid funcid, Oid result_type, Oid input_collid,
|
||||
List *args, HeapTuple func_tuple,
|
||||
eval_const_expressions_context *context);
|
||||
static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
|
||||
int *usecounts);
|
||||
@@ -3431,7 +3431,7 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
|
||||
func_tuple, context);
|
||||
|
||||
if (!newexpr && allow_inline)
|
||||
newexpr = inline_function(funcid, result_type, *args,
|
||||
newexpr = inline_function(funcid, result_type, input_collid, *args,
|
||||
func_tuple, context);
|
||||
|
||||
ReleaseSysCache(func_tuple);
|
||||
@@ -3798,12 +3798,11 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
|
||||
* simplify the function.
|
||||
*/
|
||||
static Expr *
|
||||
inline_function(Oid funcid, Oid result_type, List *args,
|
||||
inline_function(Oid funcid, Oid result_type, Oid input_collid, List *args,
|
||||
HeapTuple func_tuple,
|
||||
eval_const_expressions_context *context)
|
||||
{
|
||||
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
|
||||
Oid *argtypes;
|
||||
char *src;
|
||||
Datum tmp;
|
||||
bool isNull;
|
||||
@@ -3812,6 +3811,9 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
MemoryContext mycxt;
|
||||
inline_error_callback_arg callback_arg;
|
||||
ErrorContextCallback sqlerrcontext;
|
||||
FuncExpr *fexpr;
|
||||
SQLFunctionParseInfoPtr pinfo;
|
||||
ParseState *pstate;
|
||||
List *raw_parsetree_list;
|
||||
Query *querytree;
|
||||
Node *newexpr;
|
||||
@@ -3875,17 +3877,25 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
sqlerrcontext.previous = error_context_stack;
|
||||
error_context_stack = &sqlerrcontext;
|
||||
|
||||
/* Check for polymorphic arguments, and substitute actual arg types */
|
||||
argtypes = (Oid *) palloc(funcform->pronargs * sizeof(Oid));
|
||||
memcpy(argtypes, funcform->proargtypes.values,
|
||||
funcform->pronargs * sizeof(Oid));
|
||||
for (i = 0; i < funcform->pronargs; i++)
|
||||
{
|
||||
if (IsPolymorphicType(argtypes[i]))
|
||||
{
|
||||
argtypes[i] = exprType((Node *) list_nth(args, i));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Set up to handle parameters while parsing the function body. We need a
|
||||
* dummy FuncExpr node containing the already-simplified arguments to pass
|
||||
* to prepare_sql_fn_parse_info. (It is really only needed if there are
|
||||
* some polymorphic arguments, but for simplicity we always build it.)
|
||||
*/
|
||||
fexpr = makeNode(FuncExpr);
|
||||
fexpr->funcid = funcid;
|
||||
fexpr->funcresulttype = result_type;
|
||||
fexpr->funcretset = false;
|
||||
fexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */
|
||||
fexpr->funccollid = InvalidOid; /* doesn't matter */
|
||||
fexpr->inputcollid = input_collid;
|
||||
fexpr->args = args;
|
||||
fexpr->location = -1;
|
||||
|
||||
pinfo = prepare_sql_fn_parse_info(func_tuple,
|
||||
(Node *) fexpr,
|
||||
input_collid);
|
||||
|
||||
/*
|
||||
* We just do parsing and parse analysis, not rewriting, because rewriting
|
||||
@@ -3897,8 +3907,13 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
if (list_length(raw_parsetree_list) != 1)
|
||||
goto fail;
|
||||
|
||||
querytree = parse_analyze(linitial(raw_parsetree_list), src,
|
||||
argtypes, funcform->pronargs);
|
||||
pstate = make_parsestate(NULL);
|
||||
pstate->p_sourcetext = src;
|
||||
sql_fn_parser_setup(pstate, pinfo);
|
||||
|
||||
querytree = transformStmt(pstate, linitial(raw_parsetree_list));
|
||||
|
||||
free_parsestate(pstate);
|
||||
|
||||
/*
|
||||
* The single command must be a simple "SELECT expression".
|
||||
@@ -4030,6 +4045,28 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
||||
|
||||
MemoryContextDelete(mycxt);
|
||||
|
||||
/*
|
||||
* If the result is of a collatable type, force the result to expose
|
||||
* the correct collation. In most cases this does not matter, but
|
||||
* it's possible that the function result is used directly as a sort key
|
||||
* or in other places where we expect exprCollation() to tell the truth.
|
||||
*/
|
||||
if (OidIsValid(input_collid))
|
||||
{
|
||||
Oid exprcoll = exprCollation(newexpr);
|
||||
|
||||
if (OidIsValid(exprcoll) && exprcoll != input_collid)
|
||||
{
|
||||
CollateExpr *newnode = makeNode(CollateExpr);
|
||||
|
||||
newnode->arg = (Expr *) newexpr;
|
||||
newnode->collOid = input_collid;
|
||||
newnode->location = -1;
|
||||
|
||||
newexpr = (Node *) newnode;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Since there is now no trace of the function in the plan tree, we must
|
||||
* explicitly record the plan's dependency on the function.
|
||||
@@ -4219,7 +4256,6 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
|
||||
Oid func_oid;
|
||||
HeapTuple func_tuple;
|
||||
Form_pg_proc funcform;
|
||||
Oid *argtypes;
|
||||
char *src;
|
||||
Datum tmp;
|
||||
bool isNull;
|
||||
@@ -4229,10 +4265,10 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
|
||||
List *saveInvalItems;
|
||||
inline_error_callback_arg callback_arg;
|
||||
ErrorContextCallback sqlerrcontext;
|
||||
SQLFunctionParseInfoPtr pinfo;
|
||||
List *raw_parsetree_list;
|
||||
List *querytree_list;
|
||||
Query *querytree;
|
||||
int i;
|
||||
|
||||
Assert(rte->rtekind == RTE_FUNCTION);
|
||||
|
||||
@@ -4366,17 +4402,14 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
|
||||
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,
|
||||
funcform->pronargs * sizeof(Oid));
|
||||
for (i = 0; i < funcform->pronargs; i++)
|
||||
{
|
||||
if (IsPolymorphicType(argtypes[i]))
|
||||
{
|
||||
argtypes[i] = exprType((Node *) list_nth(fexpr->args, i));
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Set up to handle parameters while parsing the function body. We
|
||||
* can use the FuncExpr just created as the input for
|
||||
* prepare_sql_fn_parse_info.
|
||||
*/
|
||||
pinfo = prepare_sql_fn_parse_info(func_tuple,
|
||||
(Node *) fexpr,
|
||||
fexpr->inputcollid);
|
||||
|
||||
/*
|
||||
* Parse, analyze, and rewrite (unlike inline_function(), we can't skip
|
||||
@@ -4387,8 +4420,10 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
|
||||
if (list_length(raw_parsetree_list) != 1)
|
||||
goto fail;
|
||||
|
||||
querytree_list = pg_analyze_and_rewrite(linitial(raw_parsetree_list), src,
|
||||
argtypes, funcform->pronargs);
|
||||
querytree_list = pg_analyze_and_rewrite_params(linitial(raw_parsetree_list),
|
||||
src,
|
||||
(ParserSetupHook) sql_fn_parser_setup,
|
||||
pinfo);
|
||||
if (list_length(querytree_list) != 1)
|
||||
goto fail;
|
||||
querytree = linitial(querytree_list);
|
||||
@@ -4461,6 +4496,11 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
|
||||
error_context_stack = sqlerrcontext.previous;
|
||||
ReleaseSysCache(func_tuple);
|
||||
|
||||
/*
|
||||
* We don't have to fix collations here because the upper query is
|
||||
* already parsed, ie, the collations in the RTE are what count.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Since there is now no trace of the function in the plan tree, we must
|
||||
* explicitly record the plan's dependency on the function.
|
||||
|
Reference in New Issue
Block a user