Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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;