]> granicus.if.org Git - postgresql/commitdiff
Fix collation handling in plpgsql functions.
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 25 Mar 2011 19:06:36 +0000 (15:06 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 25 Mar 2011 19:06:36 +0000 (15:06 -0400)
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.

src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/plpgsql.h
src/test/regress/expected/collate.linux.utf8.out
src/test/regress/sql/collate.linux.utf8.sql

index 0ef6b5d48c3c0a1ae5e3da19d9f4a8f174e269a1..fbd441a1bc986835d9dc8729ff61ab61ee4d06d4 100644 (file)
@@ -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;
 
index a928e2f2f3d5176c3e6179b26962b6103fc541be..675b91d5306207a97f13c9b2ce77bea97893c29d 100644 (file)
@@ -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 */
index 1f4d5ac57add60591981daeb0a4b10c8c185f065..f793991209d6dac6061a8b6f7a43948eae44ae11 100644 (file)
@@ -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;
 }
 
 /* ----------
index 7015379842c364d9bd99dae3d26ec7466d214c2c..25689c78912bb07bd8aa4a311c85df4d7ac87816 100644 (file)
@@ -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,12 +635,19 @@ 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
index 4680ffd009e52e36ed709fef166729da28c36866..a4ec1e9080146eac82dad30d7084d0817c858886 100644 (file)
@@ -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
index 2a1f2113b3d092f9172f72901d7b41312f9c8efa..265db722202a40481b6f04633046eb9ce7ec37ef 100644 (file)
@@ -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;