Fix collation handling in plpgsql functions.
Make plpgsql treat the input collation as a polymorphism variable, so that we cache separate plans for each input collation that's used in a particular session, as per recent discussion. Propagate the input collation to all collatable input parameters. I chose to also propagate the input collation to all declared variables of collatable types, which is a bit more debatable but seems to be necessary for non-astonishing behavior. (Copying a parameter into a separate local variable shouldn't result in a change of behavior, for example.) There is enough infrastructure here to support declaring a collation for each local variable to override that default, but I thought we should wait to see what the field demand is before adding such a feature. In passing, remove exec_get_rec_fieldtype(), which wasn't used anywhere. Documentation patch to follow.
This commit is contained in:
parent
f6f0916dbd
commit
a4425e3200
@ -487,7 +487,8 @@ decl_statement : decl_varname decl_const decl_datatype decl_notnull decl_defval
|
||||
new = (PLpgSQL_var *)
|
||||
plpgsql_build_variable($1.name, $1.lineno,
|
||||
plpgsql_build_datatype(REFCURSOROID,
|
||||
-1),
|
||||
-1,
|
||||
InvalidOid),
|
||||
true);
|
||||
|
||||
curname_def = palloc0(sizeof(PLpgSQL_expr));
|
||||
@ -1248,7 +1249,8 @@ for_control : for_variable K_IN
|
||||
plpgsql_build_variable($1.name,
|
||||
$1.lineno,
|
||||
plpgsql_build_datatype(INT4OID,
|
||||
-1),
|
||||
-1,
|
||||
InvalidOid),
|
||||
true);
|
||||
|
||||
new = palloc0(sizeof(PLpgSQL_stmt_fori));
|
||||
@ -1932,13 +1934,17 @@ exception_sect :
|
||||
PLpgSQL_variable *var;
|
||||
|
||||
var = plpgsql_build_variable("sqlstate", lineno,
|
||||
plpgsql_build_datatype(TEXTOID, -1),
|
||||
plpgsql_build_datatype(TEXTOID,
|
||||
-1,
|
||||
plpgsql_curr_compile->fn_input_collation),
|
||||
true);
|
||||
((PLpgSQL_var *) var)->isconst = true;
|
||||
new->sqlstate_varno = var->dno;
|
||||
|
||||
var = plpgsql_build_variable("sqlerrm", lineno,
|
||||
plpgsql_build_datatype(TEXTOID, -1),
|
||||
plpgsql_build_datatype(TEXTOID,
|
||||
-1,
|
||||
plpgsql_curr_compile->fn_input_collation),
|
||||
true);
|
||||
((PLpgSQL_var *) var)->isconst = true;
|
||||
new->sqlerrm_varno = var->dno;
|
||||
@ -3227,7 +3233,8 @@ parse_datatype(const char *string, int location)
|
||||
error_context_stack = syntax_errcontext.previous;
|
||||
|
||||
/* Okay, build a PLpgSQL_type data structure for it */
|
||||
return plpgsql_build_datatype(type_id, typmod);
|
||||
return plpgsql_build_datatype(type_id, typmod,
|
||||
plpgsql_curr_compile->fn_input_collation);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3400,7 +3407,9 @@ make_case(int location, PLpgSQL_expr *t_expr,
|
||||
*/
|
||||
t_var = (PLpgSQL_var *)
|
||||
plpgsql_build_variable(varname, new->lineno,
|
||||
plpgsql_build_datatype(INT4OID, -1),
|
||||
plpgsql_build_datatype(INT4OID,
|
||||
-1,
|
||||
InvalidOid),
|
||||
true);
|
||||
new->t_varno = t_var->dno;
|
||||
|
||||
|
@ -104,7 +104,7 @@ static Node *resolve_column_ref(ParseState *pstate, PLpgSQL_expr *expr,
|
||||
static Node *make_datum_param(PLpgSQL_expr *expr, int dno, int location);
|
||||
static PLpgSQL_row *build_row_from_class(Oid classOid);
|
||||
static PLpgSQL_row *build_row_from_vars(PLpgSQL_variable **vars, int numvars);
|
||||
static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
|
||||
static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod, Oid collation);
|
||||
static void compute_function_hashkey(FunctionCallInfo fcinfo,
|
||||
Form_pg_proc procStruct,
|
||||
PLpgSQL_func_hashkey *hashkey,
|
||||
@ -348,6 +348,7 @@ do_compile(FunctionCallInfo fcinfo,
|
||||
function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
|
||||
function->fn_tid = procTup->t_self;
|
||||
function->fn_is_trigger = is_trigger;
|
||||
function->fn_input_collation = fcinfo->flinfo->fn_collation;
|
||||
function->fn_cxt = func_cxt;
|
||||
function->out_param_varno = -1; /* set up for no OUT param */
|
||||
function->resolve_option = plpgsql_variable_conflict;
|
||||
@ -411,7 +412,9 @@ do_compile(FunctionCallInfo fcinfo,
|
||||
snprintf(buf, sizeof(buf), "$%d", i + 1);
|
||||
|
||||
/* Create datatype info */
|
||||
argdtype = plpgsql_build_datatype(argtypeid, -1);
|
||||
argdtype = plpgsql_build_datatype(argtypeid,
|
||||
-1,
|
||||
function->fn_input_collation);
|
||||
|
||||
/* Disallow pseudotype argument */
|
||||
/* (note we already replaced polymorphic types) */
|
||||
@ -556,7 +559,9 @@ do_compile(FunctionCallInfo fcinfo,
|
||||
num_out_args == 0)
|
||||
{
|
||||
(void) plpgsql_build_variable("$0", 0,
|
||||
build_datatype(typeTup, -1),
|
||||
build_datatype(typeTup,
|
||||
-1,
|
||||
function->fn_input_collation),
|
||||
true);
|
||||
}
|
||||
}
|
||||
@ -587,61 +592,81 @@ do_compile(FunctionCallInfo fcinfo,
|
||||
|
||||
/* Add the variable tg_name */
|
||||
var = plpgsql_build_variable("tg_name", 0,
|
||||
plpgsql_build_datatype(NAMEOID, -1),
|
||||
plpgsql_build_datatype(NAMEOID,
|
||||
-1,
|
||||
InvalidOid),
|
||||
true);
|
||||
function->tg_name_varno = var->dno;
|
||||
|
||||
/* Add the variable tg_when */
|
||||
var = plpgsql_build_variable("tg_when", 0,
|
||||
plpgsql_build_datatype(TEXTOID, -1),
|
||||
plpgsql_build_datatype(TEXTOID,
|
||||
-1,
|
||||
function->fn_input_collation),
|
||||
true);
|
||||
function->tg_when_varno = var->dno;
|
||||
|
||||
/* Add the variable tg_level */
|
||||
var = plpgsql_build_variable("tg_level", 0,
|
||||
plpgsql_build_datatype(TEXTOID, -1),
|
||||
plpgsql_build_datatype(TEXTOID,
|
||||
-1,
|
||||
function->fn_input_collation),
|
||||
true);
|
||||
function->tg_level_varno = var->dno;
|
||||
|
||||
/* Add the variable tg_op */
|
||||
var = plpgsql_build_variable("tg_op", 0,
|
||||
plpgsql_build_datatype(TEXTOID, -1),
|
||||
plpgsql_build_datatype(TEXTOID,
|
||||
-1,
|
||||
function->fn_input_collation),
|
||||
true);
|
||||
function->tg_op_varno = var->dno;
|
||||
|
||||
/* Add the variable tg_relid */
|
||||
var = plpgsql_build_variable("tg_relid", 0,
|
||||
plpgsql_build_datatype(OIDOID, -1),
|
||||
plpgsql_build_datatype(OIDOID,
|
||||
-1,
|
||||
InvalidOid),
|
||||
true);
|
||||
function->tg_relid_varno = var->dno;
|
||||
|
||||
/* Add the variable tg_relname */
|
||||
var = plpgsql_build_variable("tg_relname", 0,
|
||||
plpgsql_build_datatype(NAMEOID, -1),
|
||||
plpgsql_build_datatype(NAMEOID,
|
||||
-1,
|
||||
InvalidOid),
|
||||
true);
|
||||
function->tg_relname_varno = var->dno;
|
||||
|
||||
/* tg_table_name is now preferred to tg_relname */
|
||||
var = plpgsql_build_variable("tg_table_name", 0,
|
||||
plpgsql_build_datatype(NAMEOID, -1),
|
||||
plpgsql_build_datatype(NAMEOID,
|
||||
-1,
|
||||
InvalidOid),
|
||||
true);
|
||||
function->tg_table_name_varno = var->dno;
|
||||
|
||||
/* add the variable tg_table_schema */
|
||||
var = plpgsql_build_variable("tg_table_schema", 0,
|
||||
plpgsql_build_datatype(NAMEOID, -1),
|
||||
plpgsql_build_datatype(NAMEOID,
|
||||
-1,
|
||||
InvalidOid),
|
||||
true);
|
||||
function->tg_table_schema_varno = var->dno;
|
||||
|
||||
/* Add the variable tg_nargs */
|
||||
var = plpgsql_build_variable("tg_nargs", 0,
|
||||
plpgsql_build_datatype(INT4OID, -1),
|
||||
plpgsql_build_datatype(INT4OID,
|
||||
-1,
|
||||
InvalidOid),
|
||||
true);
|
||||
function->tg_nargs_varno = var->dno;
|
||||
|
||||
/* Add the variable tg_argv */
|
||||
var = plpgsql_build_variable("tg_argv", 0,
|
||||
plpgsql_build_datatype(TEXTARRAYOID, -1),
|
||||
plpgsql_build_datatype(TEXTARRAYOID,
|
||||
-1,
|
||||
function->fn_input_collation),
|
||||
true);
|
||||
function->tg_argv_varno = var->dno;
|
||||
|
||||
@ -659,7 +684,9 @@ do_compile(FunctionCallInfo fcinfo,
|
||||
* Create the magic FOUND variable.
|
||||
*/
|
||||
var = plpgsql_build_variable("found", 0,
|
||||
plpgsql_build_datatype(BOOLOID, -1),
|
||||
plpgsql_build_datatype(BOOLOID,
|
||||
-1,
|
||||
InvalidOid),
|
||||
true);
|
||||
function->found_varno = var->dno;
|
||||
|
||||
@ -777,6 +804,7 @@ plpgsql_compile_inline(char *proc_source)
|
||||
|
||||
function->fn_name = pstrdup(func_name);
|
||||
function->fn_is_trigger = false;
|
||||
function->fn_input_collation = InvalidOid;
|
||||
function->fn_cxt = func_cxt;
|
||||
function->out_param_varno = -1; /* set up for no OUT param */
|
||||
function->resolve_option = plpgsql_variable_conflict;
|
||||
@ -810,7 +838,9 @@ plpgsql_compile_inline(char *proc_source)
|
||||
* Create the magic FOUND variable.
|
||||
*/
|
||||
var = plpgsql_build_variable("found", 0,
|
||||
plpgsql_build_datatype(BOOLOID, -1),
|
||||
plpgsql_build_datatype(BOOLOID,
|
||||
-1,
|
||||
InvalidOid),
|
||||
true);
|
||||
function->found_varno = var->dno;
|
||||
|
||||
@ -1218,13 +1248,14 @@ static Node *
|
||||
make_datum_param(PLpgSQL_expr *expr, int dno, int location)
|
||||
{
|
||||
PLpgSQL_execstate *estate;
|
||||
PLpgSQL_datum *datum;
|
||||
Param *param;
|
||||
MemoryContext oldcontext;
|
||||
|
||||
/* see comment in resolve_column_ref */
|
||||
estate = expr->func->cur_estate;
|
||||
|
||||
Assert(dno >= 0 && dno < estate->ndatums);
|
||||
datum = estate->datums[dno];
|
||||
|
||||
/*
|
||||
* Bitmapset must be allocated in function's permanent memory context
|
||||
@ -1236,9 +1267,9 @@ make_datum_param(PLpgSQL_expr *expr, int dno, int location)
|
||||
param = makeNode(Param);
|
||||
param->paramkind = PARAM_EXTERN;
|
||||
param->paramid = dno + 1;
|
||||
param->paramtype = exec_get_datum_type(estate, estate->datums[dno]);
|
||||
param->paramtype = exec_get_datum_type(estate, datum);
|
||||
param->paramtypmod = -1;
|
||||
param->paramcollid = get_typcollation(param->paramtype);
|
||||
param->paramcollid = exec_get_datum_collation(estate, datum);
|
||||
param->location = location;
|
||||
|
||||
return (Node *) param;
|
||||
@ -1578,7 +1609,8 @@ plpgsql_parse_wordtype(char *ident)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dtype = build_datatype(typeTup, -1);
|
||||
dtype = build_datatype(typeTup, -1,
|
||||
plpgsql_curr_compile->fn_input_collation);
|
||||
|
||||
ReleaseSysCache(typeTup);
|
||||
return dtype;
|
||||
@ -1687,7 +1719,9 @@ plpgsql_parse_cwordtype(List *idents)
|
||||
* return it
|
||||
*/
|
||||
MemoryContextSwitchTo(oldCxt);
|
||||
dtype = build_datatype(typetup, attrStruct->atttypmod);
|
||||
dtype = build_datatype(typetup,
|
||||
attrStruct->atttypmod,
|
||||
attrStruct->attcollation);
|
||||
MemoryContextSwitchTo(compile_tmp_cxt);
|
||||
|
||||
done:
|
||||
@ -1720,7 +1754,7 @@ plpgsql_parse_wordrowtype(char *ident)
|
||||
errmsg("relation \"%s\" does not exist", ident)));
|
||||
|
||||
/* Build and return the row type struct */
|
||||
return plpgsql_build_datatype(get_rel_type_id(classOid), -1);
|
||||
return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid);
|
||||
}
|
||||
|
||||
/* ----------
|
||||
@ -1755,7 +1789,7 @@ plpgsql_parse_cwordrowtype(List *idents)
|
||||
MemoryContextSwitchTo(oldCxt);
|
||||
|
||||
/* Build and return the row type struct */
|
||||
return plpgsql_build_datatype(get_rel_type_id(classOid), -1);
|
||||
return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1935,7 +1969,8 @@ build_row_from_class(Oid classOid)
|
||||
*/
|
||||
var = plpgsql_build_variable(refname, 0,
|
||||
plpgsql_build_datatype(attrStruct->atttypid,
|
||||
attrStruct->atttypmod),
|
||||
attrStruct->atttypmod,
|
||||
attrStruct->attcollation),
|
||||
false);
|
||||
|
||||
/* Add the variable to the row */
|
||||
@ -2013,10 +2048,13 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars)
|
||||
|
||||
/*
|
||||
* plpgsql_build_datatype
|
||||
* Build PLpgSQL_type struct given type OID and typmod.
|
||||
* Build PLpgSQL_type struct given type OID, typmod, and collation.
|
||||
*
|
||||
* If collation is not InvalidOid then it overrides the type's default
|
||||
* collation. But collation is ignored if the datatype is non-collatable.
|
||||
*/
|
||||
PLpgSQL_type *
|
||||
plpgsql_build_datatype(Oid typeOid, int32 typmod)
|
||||
plpgsql_build_datatype(Oid typeOid, int32 typmod, Oid collation)
|
||||
{
|
||||
HeapTuple typeTup;
|
||||
PLpgSQL_type *typ;
|
||||
@ -2025,7 +2063,7 @@ plpgsql_build_datatype(Oid typeOid, int32 typmod)
|
||||
if (!HeapTupleIsValid(typeTup))
|
||||
elog(ERROR, "cache lookup failed for type %u", typeOid);
|
||||
|
||||
typ = build_datatype(typeTup, typmod);
|
||||
typ = build_datatype(typeTup, typmod, collation);
|
||||
|
||||
ReleaseSysCache(typeTup);
|
||||
|
||||
@ -2036,7 +2074,7 @@ plpgsql_build_datatype(Oid typeOid, int32 typmod)
|
||||
* Utility subroutine to make a PLpgSQL_type struct given a pg_type entry
|
||||
*/
|
||||
static PLpgSQL_type *
|
||||
build_datatype(HeapTuple typeTup, int32 typmod)
|
||||
build_datatype(HeapTuple typeTup, int32 typmod, Oid collation)
|
||||
{
|
||||
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
|
||||
PLpgSQL_type *typ;
|
||||
@ -2077,6 +2115,9 @@ build_datatype(HeapTuple typeTup, int32 typmod)
|
||||
typ->typbyval = typeStruct->typbyval;
|
||||
typ->typrelid = typeStruct->typrelid;
|
||||
typ->typioparam = getTypeIOParam(typeTup);
|
||||
typ->collation = typeStruct->typcollation;
|
||||
if (OidIsValid(collation) && OidIsValid(typ->collation))
|
||||
typ->collation = collation;
|
||||
fmgr_info(typeStruct->typinput, &(typ->typinput));
|
||||
typ->atttypmod = typmod;
|
||||
|
||||
@ -2285,6 +2326,9 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
|
||||
hashkey->trigrelOid = RelationGetRelid(trigdata->tg_relation);
|
||||
}
|
||||
|
||||
/* get input collation, if known */
|
||||
hashkey->inputCollation = fcinfo->flinfo->fn_collation;
|
||||
|
||||
if (procStruct->pronargs > 0)
|
||||
{
|
||||
/* get the argument types */
|
||||
|
@ -1516,7 +1516,9 @@ exec_stmt_case(PLpgSQL_execstate *estate, PLpgSQL_stmt_case *stmt)
|
||||
* this doesn't affect the originally stored function parse tree.
|
||||
*/
|
||||
if (t_var->datatype->typoid != t_oid)
|
||||
t_var->datatype = plpgsql_build_datatype(t_oid, -1);
|
||||
t_var->datatype = plpgsql_build_datatype(t_oid,
|
||||
-1,
|
||||
estate->func->fn_input_collation);
|
||||
|
||||
/* now we can assign to the variable */
|
||||
exec_assign_value(estate,
|
||||
@ -4307,33 +4309,64 @@ exec_get_datum_type(PLpgSQL_execstate *estate,
|
||||
}
|
||||
|
||||
/*
|
||||
* exec_get_rec_fieldtype Get datatype of a PLpgSQL record field
|
||||
*
|
||||
* Also returns the field number to *fieldno.
|
||||
* exec_get_datum_collation Get collation of a PLpgSQL_datum
|
||||
*/
|
||||
Oid
|
||||
exec_get_rec_fieldtype(PLpgSQL_rec *rec, const char *fieldname,
|
||||
int *fieldno)
|
||||
exec_get_datum_collation(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_datum *datum)
|
||||
{
|
||||
Oid typeid;
|
||||
Oid collid;
|
||||
|
||||
switch (datum->dtype)
|
||||
{
|
||||
case PLPGSQL_DTYPE_VAR:
|
||||
{
|
||||
PLpgSQL_var *var = (PLpgSQL_var *) datum;
|
||||
|
||||
collid = var->datatype->collation;
|
||||
break;
|
||||
}
|
||||
|
||||
case PLPGSQL_DTYPE_ROW:
|
||||
case PLPGSQL_DTYPE_REC:
|
||||
/* composite types are never collatable */
|
||||
collid = InvalidOid;
|
||||
break;
|
||||
|
||||
case PLPGSQL_DTYPE_RECFIELD:
|
||||
{
|
||||
PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) datum;
|
||||
PLpgSQL_rec *rec;
|
||||
int fno;
|
||||
|
||||
rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]);
|
||||
if (rec->tupdesc == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("record \"%s\" is not assigned yet",
|
||||
rec->refname),
|
||||
errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
|
||||
fno = SPI_fnumber(rec->tupdesc, fieldname);
|
||||
fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
|
||||
if (fno == SPI_ERROR_NOATTRIBUTE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_COLUMN),
|
||||
errmsg("record \"%s\" has no field \"%s\"",
|
||||
rec->refname, fieldname)));
|
||||
typeid = SPI_gettypeid(rec->tupdesc, fno);
|
||||
rec->refname, recfield->fieldname)));
|
||||
/* XXX there's no SPI_getcollid, as yet */
|
||||
if (fno > 0)
|
||||
collid = rec->tupdesc->attrs[fno - 1]->attcollation;
|
||||
else /* no system column types have collation */
|
||||
collid = InvalidOid;
|
||||
break;
|
||||
}
|
||||
|
||||
*fieldno = fno;
|
||||
return typeid;
|
||||
default:
|
||||
elog(ERROR, "unrecognized dtype: %d", datum->dtype);
|
||||
collid = InvalidOid; /* keep compiler quiet */
|
||||
break;
|
||||
}
|
||||
|
||||
return collid;
|
||||
}
|
||||
|
||||
/* ----------
|
||||
|
@ -167,6 +167,7 @@ typedef struct
|
||||
bool typbyval;
|
||||
Oid typrelid;
|
||||
Oid typioparam;
|
||||
Oid collation; /* from pg_type, but can be overridden */
|
||||
FmgrInfo typinput; /* lookup info for typinput function */
|
||||
int32 atttypmod; /* typmod (taken from someplace else) */
|
||||
} PLpgSQL_type;
|
||||
@ -640,6 +641,13 @@ typedef struct PLpgSQL_func_hashkey
|
||||
*/
|
||||
Oid trigrelOid;
|
||||
|
||||
/*
|
||||
* We must include the input collation as part of the hash key too,
|
||||
* because we have to generate different plans (with different Param
|
||||
* collations) for different collation settings.
|
||||
*/
|
||||
Oid inputCollation;
|
||||
|
||||
/*
|
||||
* We include actual argument types in the hash key to support polymorphic
|
||||
* PLpgSQL functions. Be careful that extra positions are zeroed!
|
||||
@ -655,6 +663,7 @@ typedef struct PLpgSQL_function
|
||||
TransactionId fn_xmin;
|
||||
ItemPointerData fn_tid;
|
||||
bool fn_is_trigger;
|
||||
Oid fn_input_collation;
|
||||
PLpgSQL_func_hashkey *fn_hashkey; /* back-link to hashtable key */
|
||||
MemoryContext fn_cxt;
|
||||
|
||||
@ -860,7 +869,8 @@ extern PLpgSQL_type *plpgsql_parse_wordtype(char *ident);
|
||||
extern PLpgSQL_type *plpgsql_parse_cwordtype(List *idents);
|
||||
extern PLpgSQL_type *plpgsql_parse_wordrowtype(char *ident);
|
||||
extern PLpgSQL_type *plpgsql_parse_cwordrowtype(List *idents);
|
||||
extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod);
|
||||
extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod,
|
||||
Oid collation);
|
||||
extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
|
||||
PLpgSQL_type *dtype,
|
||||
bool add2namespace);
|
||||
@ -895,8 +905,8 @@ extern void plpgsql_subxact_cb(SubXactEvent event, SubTransactionId mySubid,
|
||||
SubTransactionId parentSubid, void *arg);
|
||||
extern Oid exec_get_datum_type(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_datum *datum);
|
||||
extern Oid exec_get_rec_fieldtype(PLpgSQL_rec *rec, const char *fieldname,
|
||||
int *fieldno);
|
||||
extern Oid exec_get_datum_collation(PLpgSQL_execstate *estate,
|
||||
PLpgSQL_datum *datum);
|
||||
|
||||
/* ----------
|
||||
* Functions for namespace handling in pl_funcs.c
|
||||
|
@ -686,57 +686,61 @@ SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2;
|
||||
2 | äbc
|
||||
(4 rows)
|
||||
|
||||
-- propagation of collation in inlined and non-inlined cases
|
||||
-- propagation of collation in SQL functions (inlined and non-inlined cases)
|
||||
-- and plpgsql functions too
|
||||
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 $$;
|
||||
CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql
|
||||
AS $$ begin return $1 < $2; end $$;
|
||||
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)
|
||||
mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(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
|
||||
a | b | lt | mylt | mylt_noninline | mylt_plpgsql
|
||||
-----+-----+----+------+----------------+--------------
|
||||
abc | abc | f | f | f | f
|
||||
abc | ABC | t | t | t | t
|
||||
abc | äbc | t | t | t | t
|
||||
abc | bbc | t | t | t | t
|
||||
ABC | abc | f | f | f | f
|
||||
ABC | ABC | f | f | f | f
|
||||
ABC | äbc | t | t | t | t
|
||||
ABC | bbc | t | t | t | t
|
||||
äbc | abc | f | f | f | f
|
||||
äbc | ABC | f | f | f | f
|
||||
äbc | äbc | f | f | f | f
|
||||
äbc | bbc | t | t | t | t
|
||||
bbc | abc | f | f | f | f
|
||||
bbc | ABC | f | f | f | f
|
||||
bbc | äbc | f | f | f | f
|
||||
bbc | bbc | f | 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")
|
||||
mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"),
|
||||
mylt_plpgsql(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
|
||||
a | b | lt | mylt | mylt_noninline | mylt_plpgsql
|
||||
-----+-----+----+------+----------------+--------------
|
||||
abc | abc | f | f | f | f
|
||||
abc | ABC | f | f | f | f
|
||||
abc | äbc | t | t | t | t
|
||||
abc | bbc | t | t | t | t
|
||||
ABC | abc | t | t | t | t
|
||||
ABC | ABC | f | f | f | f
|
||||
ABC | äbc | t | t | t | t
|
||||
ABC | bbc | t | t | t | t
|
||||
äbc | abc | f | f | f | f
|
||||
äbc | ABC | f | f | f | f
|
||||
äbc | äbc | f | f | f | f
|
||||
äbc | bbc | f | f | f | f
|
||||
bbc | abc | f | f | f | f
|
||||
bbc | ABC | f | f | f | f
|
||||
bbc | äbc | t | t | t | t
|
||||
bbc | bbc | f | f | f | f
|
||||
(16 rows)
|
||||
|
||||
-- polymorphism
|
||||
|
@ -212,7 +212,8 @@ SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2;
|
||||
SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2;
|
||||
|
||||
|
||||
-- propagation of collation in inlined and non-inlined cases
|
||||
-- propagation of collation in SQL functions (inlined and non-inlined cases)
|
||||
-- and plpgsql functions too
|
||||
|
||||
CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql
|
||||
AS $$ select $1 < $2 $$;
|
||||
@ -220,13 +221,17 @@ CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql
|
||||
CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql
|
||||
AS $$ select $1 < $2 limit 1 $$;
|
||||
|
||||
CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql
|
||||
AS $$ begin return $1 < $2; end $$;
|
||||
|
||||
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)
|
||||
mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(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")
|
||||
mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"),
|
||||
mylt_plpgsql(a.b, b.b COLLATE "C")
|
||||
FROM collate_test1 a, collate_test1 b
|
||||
ORDER BY a.b, b.b;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user