aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pl/plpgsql/src/gram.y21
-rw-r--r--src/pl/plpgsql/src/pl_comp.c98
-rw-r--r--src/pl/plpgsql/src/pl_exec.c79
-rw-r--r--src/pl/plpgsql/src/plpgsql.h18
-rw-r--r--src/test/regress/expected/collate.linux.utf8.out82
-rw-r--r--src/test/regress/sql/collate.linux.utf8.sql11
6 files changed, 207 insertions, 102 deletions
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 0ef6b5d48c3..fbd441a1bc9 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -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;
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index a928e2f2f3d..675b91d5306 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -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 */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 1f4d5ac57ad..f793991209d 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -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;
- int fno;
+ Oid collid;
- 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);
- 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);
+ switch (datum->dtype)
+ {
+ case PLPGSQL_DTYPE_VAR:
+ {
+ PLpgSQL_var *var = (PLpgSQL_var *) datum;
- *fieldno = fno;
- return typeid;
+ 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, recfield->fieldname);
+ if (fno == SPI_ERROR_NOATTRIBUTE)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("record \"%s\" has no field \"%s\"",
+ 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;
+ }
+
+ default:
+ elog(ERROR, "unrecognized dtype: %d", datum->dtype);
+ collid = InvalidOid; /* keep compiler quiet */
+ break;
+ }
+
+ return collid;
}
/* ----------
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 7015379842c..25689c78912 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -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;
@@ -634,13 +635,20 @@ typedef struct PLpgSQL_func_hashkey
/*
* For a trigger function, the OID of the relation triggered on is part of
- * the hashkey --- we want to compile the trigger separately for each
+ * the hash key --- we want to compile the trigger separately for each
* relation it is used with, in case the rowtype is different. Zero if
* not called as a trigger.
*/
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
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
index 4680ffd009e..a4ec1e90801 100644
--- a/src/test/regress/expected/collate.linux.utf8.out
+++ b/src/test/regress/expected/collate.linux.utf8.out
@@ -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
diff --git a/src/test/regress/sql/collate.linux.utf8.sql b/src/test/regress/sql/collate.linux.utf8.sql
index 2a1f2113b3d..265db722202 100644
--- a/src/test/regress/sql/collate.linux.utf8.sql
+++ b/src/test/regress/sql/collate.linux.utf8.sql
@@ -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;