Skip to content

Commit a4425e3

Browse files
committed
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.
1 parent f6f0916 commit a4425e3

File tree

6 files changed

+207
-102
lines changed

6 files changed

+207
-102
lines changed

src/pl/plpgsql/src/gram.y

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,8 @@ decl_statement : decl_varname decl_const decl_datatype decl_notnull decl_defval
487487
new = (PLpgSQL_var *)
488488
plpgsql_build_variable($1.name, $1.lineno,
489489
plpgsql_build_datatype(REFCURSOROID,
490-
-1),
490+
-1,
491+
InvalidOid),
491492
true);
492493

493494
curname_def = palloc0(sizeof(PLpgSQL_expr));
@@ -1248,7 +1249,8 @@ for_control : for_variable K_IN
12481249
plpgsql_build_variable($1.name,
12491250
$1.lineno,
12501251
plpgsql_build_datatype(INT4OID,
1251-
-1),
1252+
-1,
1253+
InvalidOid),
12521254
true);
12531255

12541256
new = palloc0(sizeof(PLpgSQL_stmt_fori));
@@ -1932,13 +1934,17 @@ exception_sect :
19321934
PLpgSQL_variable *var;
19331935

19341936
var = plpgsql_build_variable("sqlstate", lineno,
1935-
plpgsql_build_datatype(TEXTOID, -1),
1937+
plpgsql_build_datatype(TEXTOID,
1938+
-1,
1939+
plpgsql_curr_compile->fn_input_collation),
19361940
true);
19371941
((PLpgSQL_var *) var)->isconst = true;
19381942
new->sqlstate_varno = var->dno;
19391943

19401944
var = plpgsql_build_variable("sqlerrm", lineno,
1941-
plpgsql_build_datatype(TEXTOID, -1),
1945+
plpgsql_build_datatype(TEXTOID,
1946+
-1,
1947+
plpgsql_curr_compile->fn_input_collation),
19421948
true);
19431949
((PLpgSQL_var *) var)->isconst = true;
19441950
new->sqlerrm_varno = var->dno;
@@ -3227,7 +3233,8 @@ parse_datatype(const char *string, int location)
32273233
error_context_stack = syntax_errcontext.previous;
32283234

32293235
/* Okay, build a PLpgSQL_type data structure for it */
3230-
return plpgsql_build_datatype(type_id, typmod);
3236+
return plpgsql_build_datatype(type_id, typmod,
3237+
plpgsql_curr_compile->fn_input_collation);
32313238
}
32323239

32333240
/*
@@ -3400,7 +3407,9 @@ make_case(int location, PLpgSQL_expr *t_expr,
34003407
*/
34013408
t_var = (PLpgSQL_var *)
34023409
plpgsql_build_variable(varname, new->lineno,
3403-
plpgsql_build_datatype(INT4OID, -1),
3410+
plpgsql_build_datatype(INT4OID,
3411+
-1,
3412+
InvalidOid),
34043413
true);
34053414
new->t_varno = t_var->dno;
34063415

src/pl/plpgsql/src/pl_comp.c

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ static Node *resolve_column_ref(ParseState *pstate, PLpgSQL_expr *expr,
104104
static Node *make_datum_param(PLpgSQL_expr *expr, int dno, int location);
105105
static PLpgSQL_row *build_row_from_class(Oid classOid);
106106
static PLpgSQL_row *build_row_from_vars(PLpgSQL_variable **vars, int numvars);
107-
static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
107+
static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod, Oid collation);
108108
static void compute_function_hashkey(FunctionCallInfo fcinfo,
109109
Form_pg_proc procStruct,
110110
PLpgSQL_func_hashkey *hashkey,
@@ -348,6 +348,7 @@ do_compile(FunctionCallInfo fcinfo,
348348
function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data);
349349
function->fn_tid = procTup->t_self;
350350
function->fn_is_trigger = is_trigger;
351+
function->fn_input_collation = fcinfo->flinfo->fn_collation;
351352
function->fn_cxt = func_cxt;
352353
function->out_param_varno = -1; /* set up for no OUT param */
353354
function->resolve_option = plpgsql_variable_conflict;
@@ -411,7 +412,9 @@ do_compile(FunctionCallInfo fcinfo,
411412
snprintf(buf, sizeof(buf), "$%d", i + 1);
412413

413414
/* Create datatype info */
414-
argdtype = plpgsql_build_datatype(argtypeid, -1);
415+
argdtype = plpgsql_build_datatype(argtypeid,
416+
-1,
417+
function->fn_input_collation);
415418

416419
/* Disallow pseudotype argument */
417420
/* (note we already replaced polymorphic types) */
@@ -556,7 +559,9 @@ do_compile(FunctionCallInfo fcinfo,
556559
num_out_args == 0)
557560
{
558561
(void) plpgsql_build_variable("$0", 0,
559-
build_datatype(typeTup, -1),
562+
build_datatype(typeTup,
563+
-1,
564+
function->fn_input_collation),
560565
true);
561566
}
562567
}
@@ -587,61 +592,81 @@ do_compile(FunctionCallInfo fcinfo,
587592

588593
/* Add the variable tg_name */
589594
var = plpgsql_build_variable("tg_name", 0,
590-
plpgsql_build_datatype(NAMEOID, -1),
595+
plpgsql_build_datatype(NAMEOID,
596+
-1,
597+
InvalidOid),
591598
true);
592599
function->tg_name_varno = var->dno;
593600

594601
/* Add the variable tg_when */
595602
var = plpgsql_build_variable("tg_when", 0,
596-
plpgsql_build_datatype(TEXTOID, -1),
603+
plpgsql_build_datatype(TEXTOID,
604+
-1,
605+
function->fn_input_collation),
597606
true);
598607
function->tg_when_varno = var->dno;
599608

600609
/* Add the variable tg_level */
601610
var = plpgsql_build_variable("tg_level", 0,
602-
plpgsql_build_datatype(TEXTOID, -1),
611+
plpgsql_build_datatype(TEXTOID,
612+
-1,
613+
function->fn_input_collation),
603614
true);
604615
function->tg_level_varno = var->dno;
605616

606617
/* Add the variable tg_op */
607618
var = plpgsql_build_variable("tg_op", 0,
608-
plpgsql_build_datatype(TEXTOID, -1),
619+
plpgsql_build_datatype(TEXTOID,
620+
-1,
621+
function->fn_input_collation),
609622
true);
610623
function->tg_op_varno = var->dno;
611624

612625
/* Add the variable tg_relid */
613626
var = plpgsql_build_variable("tg_relid", 0,
614-
plpgsql_build_datatype(OIDOID, -1),
627+
plpgsql_build_datatype(OIDOID,
628+
-1,
629+
InvalidOid),
615630
true);
616631
function->tg_relid_varno = var->dno;
617632

618633
/* Add the variable tg_relname */
619634
var = plpgsql_build_variable("tg_relname", 0,
620-
plpgsql_build_datatype(NAMEOID, -1),
635+
plpgsql_build_datatype(NAMEOID,
636+
-1,
637+
InvalidOid),
621638
true);
622639
function->tg_relname_varno = var->dno;
623640

624641
/* tg_table_name is now preferred to tg_relname */
625642
var = plpgsql_build_variable("tg_table_name", 0,
626-
plpgsql_build_datatype(NAMEOID, -1),
643+
plpgsql_build_datatype(NAMEOID,
644+
-1,
645+
InvalidOid),
627646
true);
628647
function->tg_table_name_varno = var->dno;
629648

630649
/* add the variable tg_table_schema */
631650
var = plpgsql_build_variable("tg_table_schema", 0,
632-
plpgsql_build_datatype(NAMEOID, -1),
651+
plpgsql_build_datatype(NAMEOID,
652+
-1,
653+
InvalidOid),
633654
true);
634655
function->tg_table_schema_varno = var->dno;
635656

636657
/* Add the variable tg_nargs */
637658
var = plpgsql_build_variable("tg_nargs", 0,
638-
plpgsql_build_datatype(INT4OID, -1),
659+
plpgsql_build_datatype(INT4OID,
660+
-1,
661+
InvalidOid),
639662
true);
640663
function->tg_nargs_varno = var->dno;
641664

642665
/* Add the variable tg_argv */
643666
var = plpgsql_build_variable("tg_argv", 0,
644-
plpgsql_build_datatype(TEXTARRAYOID, -1),
667+
plpgsql_build_datatype(TEXTARRAYOID,
668+
-1,
669+
function->fn_input_collation),
645670
true);
646671
function->tg_argv_varno = var->dno;
647672

@@ -659,7 +684,9 @@ do_compile(FunctionCallInfo fcinfo,
659684
* Create the magic FOUND variable.
660685
*/
661686
var = plpgsql_build_variable("found", 0,
662-
plpgsql_build_datatype(BOOLOID, -1),
687+
plpgsql_build_datatype(BOOLOID,
688+
-1,
689+
InvalidOid),
663690
true);
664691
function->found_varno = var->dno;
665692

@@ -777,6 +804,7 @@ plpgsql_compile_inline(char *proc_source)
777804

778805
function->fn_name = pstrdup(func_name);
779806
function->fn_is_trigger = false;
807+
function->fn_input_collation = InvalidOid;
780808
function->fn_cxt = func_cxt;
781809
function->out_param_varno = -1; /* set up for no OUT param */
782810
function->resolve_option = plpgsql_variable_conflict;
@@ -810,7 +838,9 @@ plpgsql_compile_inline(char *proc_source)
810838
* Create the magic FOUND variable.
811839
*/
812840
var = plpgsql_build_variable("found", 0,
813-
plpgsql_build_datatype(BOOLOID, -1),
841+
plpgsql_build_datatype(BOOLOID,
842+
-1,
843+
InvalidOid),
814844
true);
815845
function->found_varno = var->dno;
816846

@@ -1218,13 +1248,14 @@ static Node *
12181248
make_datum_param(PLpgSQL_expr *expr, int dno, int location)
12191249
{
12201250
PLpgSQL_execstate *estate;
1251+
PLpgSQL_datum *datum;
12211252
Param *param;
12221253
MemoryContext oldcontext;
12231254

12241255
/* see comment in resolve_column_ref */
12251256
estate = expr->func->cur_estate;
1226-
12271257
Assert(dno >= 0 && dno < estate->ndatums);
1258+
datum = estate->datums[dno];
12281259

12291260
/*
12301261
* Bitmapset must be allocated in function's permanent memory context
@@ -1236,9 +1267,9 @@ make_datum_param(PLpgSQL_expr *expr, int dno, int location)
12361267
param = makeNode(Param);
12371268
param->paramkind = PARAM_EXTERN;
12381269
param->paramid = dno + 1;
1239-
param->paramtype = exec_get_datum_type(estate, estate->datums[dno]);
1270+
param->paramtype = exec_get_datum_type(estate, datum);
12401271
param->paramtypmod = -1;
1241-
param->paramcollid = get_typcollation(param->paramtype);
1272+
param->paramcollid = exec_get_datum_collation(estate, datum);
12421273
param->location = location;
12431274

12441275
return (Node *) param;
@@ -1578,7 +1609,8 @@ plpgsql_parse_wordtype(char *ident)
15781609
return NULL;
15791610
}
15801611

1581-
dtype = build_datatype(typeTup, -1);
1612+
dtype = build_datatype(typeTup, -1,
1613+
plpgsql_curr_compile->fn_input_collation);
15821614

15831615
ReleaseSysCache(typeTup);
15841616
return dtype;
@@ -1687,7 +1719,9 @@ plpgsql_parse_cwordtype(List *idents)
16871719
* return it
16881720
*/
16891721
MemoryContextSwitchTo(oldCxt);
1690-
dtype = build_datatype(typetup, attrStruct->atttypmod);
1722+
dtype = build_datatype(typetup,
1723+
attrStruct->atttypmod,
1724+
attrStruct->attcollation);
16911725
MemoryContextSwitchTo(compile_tmp_cxt);
16921726

16931727
done:
@@ -1720,7 +1754,7 @@ plpgsql_parse_wordrowtype(char *ident)
17201754
errmsg("relation \"%s\" does not exist", ident)));
17211755

17221756
/* Build and return the row type struct */
1723-
return plpgsql_build_datatype(get_rel_type_id(classOid), -1);
1757+
return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid);
17241758
}
17251759

17261760
/* ----------
@@ -1755,7 +1789,7 @@ plpgsql_parse_cwordrowtype(List *idents)
17551789
MemoryContextSwitchTo(oldCxt);
17561790

17571791
/* Build and return the row type struct */
1758-
return plpgsql_build_datatype(get_rel_type_id(classOid), -1);
1792+
return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid);
17591793
}
17601794

17611795
/*
@@ -1935,7 +1969,8 @@ build_row_from_class(Oid classOid)
19351969
*/
19361970
var = plpgsql_build_variable(refname, 0,
19371971
plpgsql_build_datatype(attrStruct->atttypid,
1938-
attrStruct->atttypmod),
1972+
attrStruct->atttypmod,
1973+
attrStruct->attcollation),
19391974
false);
19401975

19411976
/* Add the variable to the row */
@@ -2013,10 +2048,13 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars)
20132048

20142049
/*
20152050
* plpgsql_build_datatype
2016-
* Build PLpgSQL_type struct given type OID and typmod.
2051+
* Build PLpgSQL_type struct given type OID, typmod, and collation.
2052+
*
2053+
* If collation is not InvalidOid then it overrides the type's default
2054+
* collation. But collation is ignored if the datatype is non-collatable.
20172055
*/
20182056
PLpgSQL_type *
2019-
plpgsql_build_datatype(Oid typeOid, int32 typmod)
2057+
plpgsql_build_datatype(Oid typeOid, int32 typmod, Oid collation)
20202058
{
20212059
HeapTuple typeTup;
20222060
PLpgSQL_type *typ;
@@ -2025,7 +2063,7 @@ plpgsql_build_datatype(Oid typeOid, int32 typmod)
20252063
if (!HeapTupleIsValid(typeTup))
20262064
elog(ERROR, "cache lookup failed for type %u", typeOid);
20272065

2028-
typ = build_datatype(typeTup, typmod);
2066+
typ = build_datatype(typeTup, typmod, collation);
20292067

20302068
ReleaseSysCache(typeTup);
20312069

@@ -2036,7 +2074,7 @@ plpgsql_build_datatype(Oid typeOid, int32 typmod)
20362074
* Utility subroutine to make a PLpgSQL_type struct given a pg_type entry
20372075
*/
20382076
static PLpgSQL_type *
2039-
build_datatype(HeapTuple typeTup, int32 typmod)
2077+
build_datatype(HeapTuple typeTup, int32 typmod, Oid collation)
20402078
{
20412079
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
20422080
PLpgSQL_type *typ;
@@ -2077,6 +2115,9 @@ build_datatype(HeapTuple typeTup, int32 typmod)
20772115
typ->typbyval = typeStruct->typbyval;
20782116
typ->typrelid = typeStruct->typrelid;
20792117
typ->typioparam = getTypeIOParam(typeTup);
2118+
typ->collation = typeStruct->typcollation;
2119+
if (OidIsValid(collation) && OidIsValid(typ->collation))
2120+
typ->collation = collation;
20802121
fmgr_info(typeStruct->typinput, &(typ->typinput));
20812122
typ->atttypmod = typmod;
20822123

@@ -2285,6 +2326,9 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
22852326
hashkey->trigrelOid = RelationGetRelid(trigdata->tg_relation);
22862327
}
22872328

2329+
/* get input collation, if known */
2330+
hashkey->inputCollation = fcinfo->flinfo->fn_collation;
2331+
22882332
if (procStruct->pronargs > 0)
22892333
{
22902334
/* get the argument types */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy