new = (PLpgSQL_var *)
plpgsql_build_variable($1.name, $1.lineno,
plpgsql_build_datatype(REFCURSOROID,
- -1),
+ -1,
+ InvalidOid),
true);
curname_def = palloc0(sizeof(PLpgSQL_expr));
plpgsql_build_variable($1.name,
$1.lineno,
plpgsql_build_datatype(INT4OID,
- -1),
+ -1,
+ InvalidOid),
true);
new = palloc0(sizeof(PLpgSQL_stmt_fori));
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;
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);
}
/*
*/
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;
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,
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;
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) */
num_out_args == 0)
{
(void) plpgsql_build_variable("$0", 0,
- build_datatype(typeTup, -1),
+ build_datatype(typeTup,
+ -1,
+ function->fn_input_collation),
true);
}
}
/* 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;
* 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;
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;
* 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;
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
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;
return NULL;
}
- dtype = build_datatype(typeTup, -1);
+ dtype = build_datatype(typeTup, -1,
+ plpgsql_curr_compile->fn_input_collation);
ReleaseSysCache(typeTup);
return dtype;
* return it
*/
MemoryContextSwitchTo(oldCxt);
- dtype = build_datatype(typetup, attrStruct->atttypmod);
+ dtype = build_datatype(typetup,
+ attrStruct->atttypmod,
+ attrStruct->attcollation);
MemoryContextSwitchTo(compile_tmp_cxt);
done:
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);
}
/* ----------
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);
}
/*
*/
var = plpgsql_build_variable(refname, 0,
plpgsql_build_datatype(attrStruct->atttypid,
- attrStruct->atttypmod),
+ attrStruct->atttypmod,
+ attrStruct->attcollation),
false);
/* Add the variable to the row */
/*
* 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;
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);
* 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;
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;
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 */
* 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,
}
/*
- * 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;
}
/* ----------
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;
/*
* 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!
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;
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);
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
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
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 $$;
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;