mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-06 15:24:56 +08: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:
parent
a432e2783b
commit
27dc7e240b
@ -845,16 +845,21 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
|
|||||||
* OK to do full precheck: analyze and rewrite the queries,
|
* OK to do full precheck: analyze and rewrite the queries,
|
||||||
* then verify the result type.
|
* then verify the result type.
|
||||||
*/
|
*/
|
||||||
|
SQLFunctionParseInfoPtr pinfo;
|
||||||
|
|
||||||
|
/* But first, set up parameter information */
|
||||||
|
pinfo = prepare_sql_fn_parse_info(tuple, NULL, InvalidOid);
|
||||||
|
|
||||||
querytree_list = NIL;
|
querytree_list = NIL;
|
||||||
foreach(lc, raw_parsetree_list)
|
foreach(lc, raw_parsetree_list)
|
||||||
{
|
{
|
||||||
Node *parsetree = (Node *) lfirst(lc);
|
Node *parsetree = (Node *) lfirst(lc);
|
||||||
List *querytree_sublist;
|
List *querytree_sublist;
|
||||||
|
|
||||||
querytree_sublist = pg_analyze_and_rewrite(parsetree,
|
querytree_sublist = pg_analyze_and_rewrite_params(parsetree,
|
||||||
prosrc,
|
prosrc,
|
||||||
proc->proargtypes.values,
|
(ParserSetupHook) sql_fn_parser_setup,
|
||||||
proc->pronargs);
|
pinfo);
|
||||||
querytree_list = list_concat(querytree_list,
|
querytree_list = list_concat(querytree_list,
|
||||||
querytree_sublist);
|
querytree_sublist);
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,8 @@ typedef struct
|
|||||||
char *fname; /* function name (for error msgs) */
|
char *fname; /* function name (for error msgs) */
|
||||||
char *src; /* function body text (for error msgs) */
|
char *src; /* function body text (for error msgs) */
|
||||||
|
|
||||||
Oid *argtypes; /* resolved types of arguments */
|
SQLFunctionParseInfoPtr pinfo; /* data for parser callback hooks */
|
||||||
|
|
||||||
Oid rettype; /* actual return type */
|
Oid rettype; /* actual return type */
|
||||||
int16 typlen; /* length of the return type */
|
int16 typlen; /* length of the return type */
|
||||||
bool typbyval; /* true if return type is pass by value */
|
bool typbyval; /* true if return type is pass by value */
|
||||||
@ -108,8 +109,21 @@ typedef struct
|
|||||||
|
|
||||||
typedef SQLFunctionCache *SQLFunctionCachePtr;
|
typedef SQLFunctionCache *SQLFunctionCachePtr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Data structure needed by the parser callback hooks to resolve parameter
|
||||||
|
* references during parsing of a SQL function's body. This is separate from
|
||||||
|
* SQLFunctionCache since we sometimes do parsing separately from execution.
|
||||||
|
*/
|
||||||
|
typedef struct SQLFunctionParseInfo
|
||||||
|
{
|
||||||
|
Oid *argtypes; /* resolved types of input arguments */
|
||||||
|
int nargs; /* number of input arguments */
|
||||||
|
Oid collation; /* function's input collation, if known */
|
||||||
|
} SQLFunctionParseInfo;
|
||||||
|
|
||||||
|
|
||||||
/* non-export function prototypes */
|
/* non-export function prototypes */
|
||||||
|
static Node *sql_fn_param_ref(ParseState *pstate, ParamRef *pref);
|
||||||
static List *init_execution_state(List *queryTree_list,
|
static List *init_execution_state(List *queryTree_list,
|
||||||
SQLFunctionCachePtr fcache,
|
SQLFunctionCachePtr fcache,
|
||||||
bool lazyEvalOK);
|
bool lazyEvalOK);
|
||||||
@ -131,6 +145,112 @@ static void sqlfunction_shutdown(DestReceiver *self);
|
|||||||
static void sqlfunction_destroy(DestReceiver *self);
|
static void sqlfunction_destroy(DestReceiver *self);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare the SQLFunctionParseInfo struct for parsing a SQL function body
|
||||||
|
*
|
||||||
|
* This includes resolving actual types of polymorphic arguments.
|
||||||
|
*
|
||||||
|
* call_expr can be passed as NULL, but then we will fail if there are any
|
||||||
|
* polymorphic arguments.
|
||||||
|
*/
|
||||||
|
SQLFunctionParseInfoPtr
|
||||||
|
prepare_sql_fn_parse_info(HeapTuple procedureTuple,
|
||||||
|
Node *call_expr,
|
||||||
|
Oid inputCollation)
|
||||||
|
{
|
||||||
|
SQLFunctionParseInfoPtr pinfo;
|
||||||
|
Form_pg_proc procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple);
|
||||||
|
int nargs;
|
||||||
|
|
||||||
|
pinfo = (SQLFunctionParseInfoPtr) palloc0(sizeof(SQLFunctionParseInfo));
|
||||||
|
|
||||||
|
/* Save the function's input collation */
|
||||||
|
pinfo->collation = inputCollation;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy input argument types from the pg_proc entry, then resolve any
|
||||||
|
* polymorphic types.
|
||||||
|
*/
|
||||||
|
pinfo->nargs = nargs = procedureStruct->pronargs;
|
||||||
|
if (nargs > 0)
|
||||||
|
{
|
||||||
|
Oid *argOidVect;
|
||||||
|
int argnum;
|
||||||
|
|
||||||
|
argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
|
||||||
|
memcpy(argOidVect,
|
||||||
|
procedureStruct->proargtypes.values,
|
||||||
|
nargs * sizeof(Oid));
|
||||||
|
|
||||||
|
for (argnum = 0; argnum < nargs; argnum++)
|
||||||
|
{
|
||||||
|
Oid argtype = argOidVect[argnum];
|
||||||
|
|
||||||
|
if (IsPolymorphicType(argtype))
|
||||||
|
{
|
||||||
|
argtype = get_call_expr_argtype(call_expr, argnum);
|
||||||
|
if (argtype == InvalidOid)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||||
|
errmsg("could not determine actual type of argument declared %s",
|
||||||
|
format_type_be(argOidVect[argnum]))));
|
||||||
|
argOidVect[argnum] = argtype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pinfo->argtypes = argOidVect;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parser setup hook for parsing a SQL function body.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
sql_fn_parser_setup(struct ParseState *pstate, SQLFunctionParseInfoPtr pinfo)
|
||||||
|
{
|
||||||
|
/* Later we might use these hooks to support parameter names */
|
||||||
|
pstate->p_pre_columnref_hook = NULL;
|
||||||
|
pstate->p_post_columnref_hook = NULL;
|
||||||
|
pstate->p_paramref_hook = sql_fn_param_ref;
|
||||||
|
/* no need to use p_coerce_param_hook */
|
||||||
|
pstate->p_ref_hook_state = (void *) pinfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sql_fn_param_ref parser callback for ParamRefs ($n symbols)
|
||||||
|
*/
|
||||||
|
static Node *
|
||||||
|
sql_fn_param_ref(ParseState *pstate, ParamRef *pref)
|
||||||
|
{
|
||||||
|
SQLFunctionParseInfoPtr pinfo = (SQLFunctionParseInfoPtr) pstate->p_ref_hook_state;
|
||||||
|
int paramno = pref->number;
|
||||||
|
Param *param;
|
||||||
|
|
||||||
|
/* Check parameter number is valid */
|
||||||
|
if (paramno <= 0 || paramno > pinfo->nargs)
|
||||||
|
return NULL; /* unknown parameter number */
|
||||||
|
|
||||||
|
param = makeNode(Param);
|
||||||
|
param->paramkind = PARAM_EXTERN;
|
||||||
|
param->paramid = paramno;
|
||||||
|
param->paramtype = pinfo->argtypes[paramno - 1];
|
||||||
|
param->paramtypmod = -1;
|
||||||
|
param->paramcollid = get_typcollation(param->paramtype);
|
||||||
|
param->location = pref->location;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we have a function input collation, allow it to override the
|
||||||
|
* type-derived collation for parameter symbols. (XXX perhaps this should
|
||||||
|
* not happen if the type collation is not default?)
|
||||||
|
*/
|
||||||
|
if (OidIsValid(pinfo->collation) && OidIsValid(param->paramcollid))
|
||||||
|
param->paramcollid = pinfo->collation;
|
||||||
|
|
||||||
|
return (Node *) param;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up the per-query execution_state records for a SQL function.
|
* Set up the per-query execution_state records for a SQL function.
|
||||||
*
|
*
|
||||||
@ -239,7 +359,9 @@ init_execution_state(List *queryTree_list,
|
|||||||
return eslist;
|
return eslist;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the SQLFunctionCache for a SQL function */
|
/*
|
||||||
|
* Initialize the SQLFunctionCache for a SQL function
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK)
|
init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK)
|
||||||
{
|
{
|
||||||
@ -248,8 +370,6 @@ init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK)
|
|||||||
HeapTuple procedureTuple;
|
HeapTuple procedureTuple;
|
||||||
Form_pg_proc procedureStruct;
|
Form_pg_proc procedureStruct;
|
||||||
SQLFunctionCachePtr fcache;
|
SQLFunctionCachePtr fcache;
|
||||||
Oid *argOidVect;
|
|
||||||
int nargs;
|
|
||||||
List *raw_parsetree_list;
|
List *raw_parsetree_list;
|
||||||
List *queryTree_list;
|
List *queryTree_list;
|
||||||
List *flat_query_list;
|
List *flat_query_list;
|
||||||
@ -302,37 +422,13 @@ init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK)
|
|||||||
(procedureStruct->provolatile != PROVOLATILE_VOLATILE);
|
(procedureStruct->provolatile != PROVOLATILE_VOLATILE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need the actual argument types to pass to the parser.
|
* We need the actual argument types to pass to the parser. Also make
|
||||||
|
* sure that parameter symbols are considered to have the function's
|
||||||
|
* resolved input collation.
|
||||||
*/
|
*/
|
||||||
nargs = procedureStruct->pronargs;
|
fcache->pinfo = prepare_sql_fn_parse_info(procedureTuple,
|
||||||
if (nargs > 0)
|
finfo->fn_expr,
|
||||||
{
|
finfo->fn_collation);
|
||||||
int argnum;
|
|
||||||
|
|
||||||
argOidVect = (Oid *) palloc(nargs * sizeof(Oid));
|
|
||||||
memcpy(argOidVect,
|
|
||||||
procedureStruct->proargtypes.values,
|
|
||||||
nargs * sizeof(Oid));
|
|
||||||
/* Resolve any polymorphic argument types */
|
|
||||||
for (argnum = 0; argnum < nargs; argnum++)
|
|
||||||
{
|
|
||||||
Oid argtype = argOidVect[argnum];
|
|
||||||
|
|
||||||
if (IsPolymorphicType(argtype))
|
|
||||||
{
|
|
||||||
argtype = get_fn_expr_argtype(finfo, argnum);
|
|
||||||
if (argtype == InvalidOid)
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
||||||
errmsg("could not determine actual type of argument declared %s",
|
|
||||||
format_type_be(argOidVect[argnum]))));
|
|
||||||
argOidVect[argnum] = argtype;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
argOidVect = NULL;
|
|
||||||
fcache->argtypes = argOidVect;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* And of course we need the function body text.
|
* And of course we need the function body text.
|
||||||
@ -364,10 +460,10 @@ init_sql_fcache(FmgrInfo *finfo, bool lazyEvalOK)
|
|||||||
Node *parsetree = (Node *) lfirst(lc);
|
Node *parsetree = (Node *) lfirst(lc);
|
||||||
List *queryTree_sublist;
|
List *queryTree_sublist;
|
||||||
|
|
||||||
queryTree_sublist = pg_analyze_and_rewrite(parsetree,
|
queryTree_sublist = pg_analyze_and_rewrite_params(parsetree,
|
||||||
fcache->src,
|
fcache->src,
|
||||||
argOidVect,
|
(ParserSetupHook) sql_fn_parser_setup,
|
||||||
nargs);
|
fcache->pinfo);
|
||||||
queryTree_list = lappend(queryTree_list, queryTree_sublist);
|
queryTree_list = lappend(queryTree_list, queryTree_sublist);
|
||||||
flat_query_list = list_concat(flat_query_list,
|
flat_query_list = list_concat(flat_query_list,
|
||||||
list_copy(queryTree_sublist));
|
list_copy(queryTree_sublist));
|
||||||
@ -583,7 +679,7 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
|
|||||||
prm->value = fcinfo->arg[i];
|
prm->value = fcinfo->arg[i];
|
||||||
prm->isnull = fcinfo->argnull[i];
|
prm->isnull = fcinfo->argnull[i];
|
||||||
prm->pflags = 0;
|
prm->pflags = 0;
|
||||||
prm->ptype = fcache->argtypes[i];
|
prm->ptype = fcache->pinfo->argtypes[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -118,8 +118,8 @@ static Expr *evaluate_function(Oid funcid,
|
|||||||
Oid result_type, int32 result_typmod,
|
Oid result_type, int32 result_typmod,
|
||||||
Oid input_collid, List *args, HeapTuple func_tuple,
|
Oid input_collid, List *args, HeapTuple func_tuple,
|
||||||
eval_const_expressions_context *context);
|
eval_const_expressions_context *context);
|
||||||
static Expr *inline_function(Oid funcid, Oid result_type, List *args,
|
static Expr *inline_function(Oid funcid, Oid result_type, Oid input_collid,
|
||||||
HeapTuple func_tuple,
|
List *args, HeapTuple func_tuple,
|
||||||
eval_const_expressions_context *context);
|
eval_const_expressions_context *context);
|
||||||
static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
|
static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
|
||||||
int *usecounts);
|
int *usecounts);
|
||||||
@ -3431,7 +3431,7 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
|
|||||||
func_tuple, context);
|
func_tuple, context);
|
||||||
|
|
||||||
if (!newexpr && allow_inline)
|
if (!newexpr && allow_inline)
|
||||||
newexpr = inline_function(funcid, result_type, *args,
|
newexpr = inline_function(funcid, result_type, input_collid, *args,
|
||||||
func_tuple, context);
|
func_tuple, context);
|
||||||
|
|
||||||
ReleaseSysCache(func_tuple);
|
ReleaseSysCache(func_tuple);
|
||||||
@ -3798,12 +3798,11 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
|
|||||||
* simplify the function.
|
* simplify the function.
|
||||||
*/
|
*/
|
||||||
static Expr *
|
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,
|
HeapTuple func_tuple,
|
||||||
eval_const_expressions_context *context)
|
eval_const_expressions_context *context)
|
||||||
{
|
{
|
||||||
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
|
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
|
||||||
Oid *argtypes;
|
|
||||||
char *src;
|
char *src;
|
||||||
Datum tmp;
|
Datum tmp;
|
||||||
bool isNull;
|
bool isNull;
|
||||||
@ -3812,6 +3811,9 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
|||||||
MemoryContext mycxt;
|
MemoryContext mycxt;
|
||||||
inline_error_callback_arg callback_arg;
|
inline_error_callback_arg callback_arg;
|
||||||
ErrorContextCallback sqlerrcontext;
|
ErrorContextCallback sqlerrcontext;
|
||||||
|
FuncExpr *fexpr;
|
||||||
|
SQLFunctionParseInfoPtr pinfo;
|
||||||
|
ParseState *pstate;
|
||||||
List *raw_parsetree_list;
|
List *raw_parsetree_list;
|
||||||
Query *querytree;
|
Query *querytree;
|
||||||
Node *newexpr;
|
Node *newexpr;
|
||||||
@ -3875,17 +3877,25 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
|||||||
sqlerrcontext.previous = error_context_stack;
|
sqlerrcontext.previous = error_context_stack;
|
||||||
error_context_stack = &sqlerrcontext;
|
error_context_stack = &sqlerrcontext;
|
||||||
|
|
||||||
/* Check for polymorphic arguments, and substitute actual arg types */
|
/*
|
||||||
argtypes = (Oid *) palloc(funcform->pronargs * sizeof(Oid));
|
* Set up to handle parameters while parsing the function body. We need a
|
||||||
memcpy(argtypes, funcform->proargtypes.values,
|
* dummy FuncExpr node containing the already-simplified arguments to pass
|
||||||
funcform->pronargs * sizeof(Oid));
|
* to prepare_sql_fn_parse_info. (It is really only needed if there are
|
||||||
for (i = 0; i < funcform->pronargs; i++)
|
* some polymorphic arguments, but for simplicity we always build it.)
|
||||||
{
|
*/
|
||||||
if (IsPolymorphicType(argtypes[i]))
|
fexpr = makeNode(FuncExpr);
|
||||||
{
|
fexpr->funcid = funcid;
|
||||||
argtypes[i] = exprType((Node *) list_nth(args, i));
|
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
|
* 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)
|
if (list_length(raw_parsetree_list) != 1)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
querytree = parse_analyze(linitial(raw_parsetree_list), src,
|
pstate = make_parsestate(NULL);
|
||||||
argtypes, funcform->pronargs);
|
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".
|
* The single command must be a simple "SELECT expression".
|
||||||
@ -4030,6 +4045,28 @@ inline_function(Oid funcid, Oid result_type, List *args,
|
|||||||
|
|
||||||
MemoryContextDelete(mycxt);
|
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
|
* Since there is now no trace of the function in the plan tree, we must
|
||||||
* explicitly record the plan's dependency on the function.
|
* explicitly record the plan's dependency on the function.
|
||||||
@ -4219,7 +4256,6 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
|
|||||||
Oid func_oid;
|
Oid func_oid;
|
||||||
HeapTuple func_tuple;
|
HeapTuple func_tuple;
|
||||||
Form_pg_proc funcform;
|
Form_pg_proc funcform;
|
||||||
Oid *argtypes;
|
|
||||||
char *src;
|
char *src;
|
||||||
Datum tmp;
|
Datum tmp;
|
||||||
bool isNull;
|
bool isNull;
|
||||||
@ -4229,10 +4265,10 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
|
|||||||
List *saveInvalItems;
|
List *saveInvalItems;
|
||||||
inline_error_callback_arg callback_arg;
|
inline_error_callback_arg callback_arg;
|
||||||
ErrorContextCallback sqlerrcontext;
|
ErrorContextCallback sqlerrcontext;
|
||||||
|
SQLFunctionParseInfoPtr pinfo;
|
||||||
List *raw_parsetree_list;
|
List *raw_parsetree_list;
|
||||||
List *querytree_list;
|
List *querytree_list;
|
||||||
Query *querytree;
|
Query *querytree;
|
||||||
int i;
|
|
||||||
|
|
||||||
Assert(rte->rtekind == RTE_FUNCTION);
|
Assert(rte->rtekind == RTE_FUNCTION);
|
||||||
|
|
||||||
@ -4366,17 +4402,14 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
|
|||||||
if (list_length(fexpr->args) != funcform->pronargs)
|
if (list_length(fexpr->args) != funcform->pronargs)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
/* Check for polymorphic arguments, and substitute actual arg types */
|
/*
|
||||||
argtypes = (Oid *) palloc(funcform->pronargs * sizeof(Oid));
|
* Set up to handle parameters while parsing the function body. We
|
||||||
memcpy(argtypes, funcform->proargtypes.values,
|
* can use the FuncExpr just created as the input for
|
||||||
funcform->pronargs * sizeof(Oid));
|
* prepare_sql_fn_parse_info.
|
||||||
for (i = 0; i < funcform->pronargs; i++)
|
*/
|
||||||
{
|
pinfo = prepare_sql_fn_parse_info(func_tuple,
|
||||||
if (IsPolymorphicType(argtypes[i]))
|
(Node *) fexpr,
|
||||||
{
|
fexpr->inputcollid);
|
||||||
argtypes[i] = exprType((Node *) list_nth(fexpr->args, i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse, analyze, and rewrite (unlike inline_function(), we can't skip
|
* 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)
|
if (list_length(raw_parsetree_list) != 1)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
querytree_list = pg_analyze_and_rewrite(linitial(raw_parsetree_list), src,
|
querytree_list = pg_analyze_and_rewrite_params(linitial(raw_parsetree_list),
|
||||||
argtypes, funcform->pronargs);
|
src,
|
||||||
|
(ParserSetupHook) sql_fn_parser_setup,
|
||||||
|
pinfo);
|
||||||
if (list_length(querytree_list) != 1)
|
if (list_length(querytree_list) != 1)
|
||||||
goto fail;
|
goto fail;
|
||||||
querytree = linitial(querytree_list);
|
querytree = linitial(querytree_list);
|
||||||
@ -4461,6 +4496,11 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
|
|||||||
error_context_stack = sqlerrcontext.previous;
|
error_context_stack = sqlerrcontext.previous;
|
||||||
ReleaseSysCache(func_tuple);
|
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
|
* Since there is now no trace of the function in the plan tree, we must
|
||||||
* explicitly record the plan's dependency on the function.
|
* explicitly record the plan's dependency on the function.
|
||||||
|
@ -231,6 +231,11 @@ variable_coerce_param_hook(ParseState *pstate, Param *param,
|
|||||||
*/
|
*/
|
||||||
param->paramtypmod = -1;
|
param->paramtypmod = -1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This module always sets a Param's collation to be the default for
|
||||||
|
* its datatype. If that's not what you want, you should be using
|
||||||
|
* the more general parser substitution hooks.
|
||||||
|
*/
|
||||||
param->paramcollid = get_typcollation(param->paramtype);
|
param->paramcollid = get_typcollation(param->paramtype);
|
||||||
|
|
||||||
/* Use the leftmost of the param's and coercion's locations */
|
/* Use the leftmost of the param's and coercion's locations */
|
||||||
|
@ -17,9 +17,18 @@
|
|||||||
#include "nodes/execnodes.h"
|
#include "nodes/execnodes.h"
|
||||||
#include "tcop/dest.h"
|
#include "tcop/dest.h"
|
||||||
|
|
||||||
|
/* This struct is known only within executor/functions.c */
|
||||||
|
typedef struct SQLFunctionParseInfo *SQLFunctionParseInfoPtr;
|
||||||
|
|
||||||
extern Datum fmgr_sql(PG_FUNCTION_ARGS);
|
extern Datum fmgr_sql(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
extern SQLFunctionParseInfoPtr prepare_sql_fn_parse_info(HeapTuple procedureTuple,
|
||||||
|
Node *call_expr,
|
||||||
|
Oid inputCollation);
|
||||||
|
|
||||||
|
extern void sql_fn_parser_setup(struct ParseState *pstate,
|
||||||
|
SQLFunctionParseInfoPtr pinfo);
|
||||||
|
|
||||||
extern bool check_sql_fn_retval(Oid func_id, Oid rettype,
|
extern bool check_sql_fn_retval(Oid func_id, Oid rettype,
|
||||||
List *queryTreeList,
|
List *queryTreeList,
|
||||||
bool *modifyTargetList,
|
bool *modifyTargetList,
|
||||||
|
@ -686,6 +686,59 @@ SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2;
|
|||||||
2 | äbc
|
2 | äbc
|
||||||
(4 rows)
|
(4 rows)
|
||||||
|
|
||||||
|
-- propagation of collation in inlined and non-inlined cases
|
||||||
|
CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql
|
||||||
|
AS $$ select $1 < $2 $$;
|
||||||
|
CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql
|
||||||
|
AS $$ select $1 < $2 limit 1 $$;
|
||||||
|
SELECT a.b AS a, b.b AS b, a.b < b.b AS lt,
|
||||||
|
mylt(a.b, b.b), mylt_noninline(a.b, b.b)
|
||||||
|
FROM collate_test1 a, collate_test1 b
|
||||||
|
ORDER BY a.b, b.b;
|
||||||
|
a | b | lt | mylt | mylt_noninline
|
||||||
|
-----+-----+----+------+----------------
|
||||||
|
abc | abc | f | f | f
|
||||||
|
abc | ABC | t | t | t
|
||||||
|
abc | äbc | t | t | t
|
||||||
|
abc | bbc | t | t | t
|
||||||
|
ABC | abc | f | f | f
|
||||||
|
ABC | ABC | f | f | f
|
||||||
|
ABC | äbc | t | t | t
|
||||||
|
ABC | bbc | t | t | t
|
||||||
|
äbc | abc | f | f | f
|
||||||
|
äbc | ABC | f | f | f
|
||||||
|
äbc | äbc | f | f | f
|
||||||
|
äbc | bbc | t | t | t
|
||||||
|
bbc | abc | f | f | f
|
||||||
|
bbc | ABC | f | f | f
|
||||||
|
bbc | äbc | f | f | f
|
||||||
|
bbc | bbc | f | f | f
|
||||||
|
(16 rows)
|
||||||
|
|
||||||
|
SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt,
|
||||||
|
mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C")
|
||||||
|
FROM collate_test1 a, collate_test1 b
|
||||||
|
ORDER BY a.b, b.b;
|
||||||
|
a | b | lt | mylt | mylt_noninline
|
||||||
|
-----+-----+----+------+----------------
|
||||||
|
abc | abc | f | f | f
|
||||||
|
abc | ABC | f | f | f
|
||||||
|
abc | äbc | t | t | t
|
||||||
|
abc | bbc | t | t | t
|
||||||
|
ABC | abc | t | t | t
|
||||||
|
ABC | ABC | f | f | f
|
||||||
|
ABC | äbc | t | t | t
|
||||||
|
ABC | bbc | t | t | t
|
||||||
|
äbc | abc | f | f | f
|
||||||
|
äbc | ABC | f | f | f
|
||||||
|
äbc | äbc | f | f | f
|
||||||
|
äbc | bbc | f | f | f
|
||||||
|
bbc | abc | f | f | f
|
||||||
|
bbc | ABC | f | f | f
|
||||||
|
bbc | äbc | t | t | t
|
||||||
|
bbc | bbc | f | f | f
|
||||||
|
(16 rows)
|
||||||
|
|
||||||
-- polymorphism
|
-- polymorphism
|
||||||
SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1;
|
SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1;
|
||||||
unnest
|
unnest
|
||||||
|
@ -212,6 +212,25 @@ SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2;
|
|||||||
SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2;
|
SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2;
|
||||||
|
|
||||||
|
|
||||||
|
-- propagation of collation in inlined and non-inlined cases
|
||||||
|
|
||||||
|
CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql
|
||||||
|
AS $$ select $1 < $2 $$;
|
||||||
|
|
||||||
|
CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql
|
||||||
|
AS $$ select $1 < $2 limit 1 $$;
|
||||||
|
|
||||||
|
SELECT a.b AS a, b.b AS b, a.b < b.b AS lt,
|
||||||
|
mylt(a.b, b.b), mylt_noninline(a.b, b.b)
|
||||||
|
FROM collate_test1 a, collate_test1 b
|
||||||
|
ORDER BY a.b, b.b;
|
||||||
|
|
||||||
|
SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt,
|
||||||
|
mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C")
|
||||||
|
FROM collate_test1 a, collate_test1 b
|
||||||
|
ORDER BY a.b, b.b;
|
||||||
|
|
||||||
|
|
||||||
-- polymorphism
|
-- polymorphism
|
||||||
|
|
||||||
SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1;
|
SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user