diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 0ef6b5d48c..fbd441a1bc 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -487,7 +487,8 @@ decl_statement : decl_varname decl_const decl_datatype decl_notnull decl_defval new = (PLpgSQL_var *) plpgsql_build_variable($1.name, $1.lineno, plpgsql_build_datatype(REFCURSOROID, - -1), + -1, + InvalidOid), true); curname_def = palloc0(sizeof(PLpgSQL_expr)); @@ -1248,7 +1249,8 @@ for_control : for_variable K_IN plpgsql_build_variable($1.name, $1.lineno, plpgsql_build_datatype(INT4OID, - -1), + -1, + InvalidOid), true); new = palloc0(sizeof(PLpgSQL_stmt_fori)); @@ -1932,13 +1934,17 @@ exception_sect : PLpgSQL_variable *var; var = plpgsql_build_variable("sqlstate", lineno, - plpgsql_build_datatype(TEXTOID, -1), + plpgsql_build_datatype(TEXTOID, + -1, + plpgsql_curr_compile->fn_input_collation), true); ((PLpgSQL_var *) var)->isconst = true; new->sqlstate_varno = var->dno; var = plpgsql_build_variable("sqlerrm", lineno, - plpgsql_build_datatype(TEXTOID, -1), + plpgsql_build_datatype(TEXTOID, + -1, + plpgsql_curr_compile->fn_input_collation), true); ((PLpgSQL_var *) var)->isconst = true; new->sqlerrm_varno = var->dno; @@ -3227,7 +3233,8 @@ parse_datatype(const char *string, int location) error_context_stack = syntax_errcontext.previous; /* Okay, build a PLpgSQL_type data structure for it */ - return plpgsql_build_datatype(type_id, typmod); + return plpgsql_build_datatype(type_id, typmod, + plpgsql_curr_compile->fn_input_collation); } /* @@ -3400,7 +3407,9 @@ make_case(int location, PLpgSQL_expr *t_expr, */ t_var = (PLpgSQL_var *) plpgsql_build_variable(varname, new->lineno, - plpgsql_build_datatype(INT4OID, -1), + plpgsql_build_datatype(INT4OID, + -1, + InvalidOid), true); new->t_varno = t_var->dno; diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index a928e2f2f3..675b91d530 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -104,7 +104,7 @@ static Node *resolve_column_ref(ParseState *pstate, PLpgSQL_expr *expr, static Node *make_datum_param(PLpgSQL_expr *expr, int dno, int location); static PLpgSQL_row *build_row_from_class(Oid classOid); static PLpgSQL_row *build_row_from_vars(PLpgSQL_variable **vars, int numvars); -static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod); +static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod, Oid collation); static void compute_function_hashkey(FunctionCallInfo fcinfo, Form_pg_proc procStruct, PLpgSQL_func_hashkey *hashkey, @@ -348,6 +348,7 @@ do_compile(FunctionCallInfo fcinfo, function->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); function->fn_tid = procTup->t_self; function->fn_is_trigger = is_trigger; + function->fn_input_collation = fcinfo->flinfo->fn_collation; function->fn_cxt = func_cxt; function->out_param_varno = -1; /* set up for no OUT param */ function->resolve_option = plpgsql_variable_conflict; @@ -411,7 +412,9 @@ do_compile(FunctionCallInfo fcinfo, snprintf(buf, sizeof(buf), "$%d", i + 1); /* Create datatype info */ - argdtype = plpgsql_build_datatype(argtypeid, -1); + argdtype = plpgsql_build_datatype(argtypeid, + -1, + function->fn_input_collation); /* Disallow pseudotype argument */ /* (note we already replaced polymorphic types) */ @@ -556,7 +559,9 @@ do_compile(FunctionCallInfo fcinfo, num_out_args == 0) { (void) plpgsql_build_variable("$0", 0, - build_datatype(typeTup, -1), + build_datatype(typeTup, + -1, + function->fn_input_collation), true); } } @@ -587,61 +592,81 @@ do_compile(FunctionCallInfo fcinfo, /* Add the variable tg_name */ var = plpgsql_build_variable("tg_name", 0, - plpgsql_build_datatype(NAMEOID, -1), + plpgsql_build_datatype(NAMEOID, + -1, + InvalidOid), true); function->tg_name_varno = var->dno; /* Add the variable tg_when */ var = plpgsql_build_variable("tg_when", 0, - plpgsql_build_datatype(TEXTOID, -1), + plpgsql_build_datatype(TEXTOID, + -1, + function->fn_input_collation), true); function->tg_when_varno = var->dno; /* Add the variable tg_level */ var = plpgsql_build_variable("tg_level", 0, - plpgsql_build_datatype(TEXTOID, -1), + plpgsql_build_datatype(TEXTOID, + -1, + function->fn_input_collation), true); function->tg_level_varno = var->dno; /* Add the variable tg_op */ var = plpgsql_build_variable("tg_op", 0, - plpgsql_build_datatype(TEXTOID, -1), + plpgsql_build_datatype(TEXTOID, + -1, + function->fn_input_collation), true); function->tg_op_varno = var->dno; /* Add the variable tg_relid */ var = plpgsql_build_variable("tg_relid", 0, - plpgsql_build_datatype(OIDOID, -1), + plpgsql_build_datatype(OIDOID, + -1, + InvalidOid), true); function->tg_relid_varno = var->dno; /* Add the variable tg_relname */ var = plpgsql_build_variable("tg_relname", 0, - plpgsql_build_datatype(NAMEOID, -1), + plpgsql_build_datatype(NAMEOID, + -1, + InvalidOid), true); function->tg_relname_varno = var->dno; /* tg_table_name is now preferred to tg_relname */ var = plpgsql_build_variable("tg_table_name", 0, - plpgsql_build_datatype(NAMEOID, -1), + plpgsql_build_datatype(NAMEOID, + -1, + InvalidOid), true); function->tg_table_name_varno = var->dno; /* add the variable tg_table_schema */ var = plpgsql_build_variable("tg_table_schema", 0, - plpgsql_build_datatype(NAMEOID, -1), + plpgsql_build_datatype(NAMEOID, + -1, + InvalidOid), true); function->tg_table_schema_varno = var->dno; /* Add the variable tg_nargs */ var = plpgsql_build_variable("tg_nargs", 0, - plpgsql_build_datatype(INT4OID, -1), + plpgsql_build_datatype(INT4OID, + -1, + InvalidOid), true); function->tg_nargs_varno = var->dno; /* Add the variable tg_argv */ var = plpgsql_build_variable("tg_argv", 0, - plpgsql_build_datatype(TEXTARRAYOID, -1), + plpgsql_build_datatype(TEXTARRAYOID, + -1, + function->fn_input_collation), true); function->tg_argv_varno = var->dno; @@ -659,7 +684,9 @@ do_compile(FunctionCallInfo fcinfo, * Create the magic FOUND variable. */ var = plpgsql_build_variable("found", 0, - plpgsql_build_datatype(BOOLOID, -1), + plpgsql_build_datatype(BOOLOID, + -1, + InvalidOid), true); function->found_varno = var->dno; @@ -777,6 +804,7 @@ plpgsql_compile_inline(char *proc_source) function->fn_name = pstrdup(func_name); function->fn_is_trigger = false; + function->fn_input_collation = InvalidOid; function->fn_cxt = func_cxt; function->out_param_varno = -1; /* set up for no OUT param */ function->resolve_option = plpgsql_variable_conflict; @@ -810,7 +838,9 @@ plpgsql_compile_inline(char *proc_source) * Create the magic FOUND variable. */ var = plpgsql_build_variable("found", 0, - plpgsql_build_datatype(BOOLOID, -1), + plpgsql_build_datatype(BOOLOID, + -1, + InvalidOid), true); function->found_varno = var->dno; @@ -1218,13 +1248,14 @@ static Node * make_datum_param(PLpgSQL_expr *expr, int dno, int location) { PLpgSQL_execstate *estate; + PLpgSQL_datum *datum; Param *param; MemoryContext oldcontext; /* see comment in resolve_column_ref */ estate = expr->func->cur_estate; - Assert(dno >= 0 && dno < estate->ndatums); + datum = estate->datums[dno]; /* * Bitmapset must be allocated in function's permanent memory context @@ -1236,9 +1267,9 @@ make_datum_param(PLpgSQL_expr *expr, int dno, int location) param = makeNode(Param); param->paramkind = PARAM_EXTERN; param->paramid = dno + 1; - param->paramtype = exec_get_datum_type(estate, estate->datums[dno]); + param->paramtype = exec_get_datum_type(estate, datum); param->paramtypmod = -1; - param->paramcollid = get_typcollation(param->paramtype); + param->paramcollid = exec_get_datum_collation(estate, datum); param->location = location; return (Node *) param; @@ -1578,7 +1609,8 @@ plpgsql_parse_wordtype(char *ident) return NULL; } - dtype = build_datatype(typeTup, -1); + dtype = build_datatype(typeTup, -1, + plpgsql_curr_compile->fn_input_collation); ReleaseSysCache(typeTup); return dtype; @@ -1687,7 +1719,9 @@ plpgsql_parse_cwordtype(List *idents) * return it */ MemoryContextSwitchTo(oldCxt); - dtype = build_datatype(typetup, attrStruct->atttypmod); + dtype = build_datatype(typetup, + attrStruct->atttypmod, + attrStruct->attcollation); MemoryContextSwitchTo(compile_tmp_cxt); done: @@ -1720,7 +1754,7 @@ plpgsql_parse_wordrowtype(char *ident) errmsg("relation \"%s\" does not exist", ident))); /* Build and return the row type struct */ - return plpgsql_build_datatype(get_rel_type_id(classOid), -1); + return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid); } /* ---------- @@ -1755,7 +1789,7 @@ plpgsql_parse_cwordrowtype(List *idents) MemoryContextSwitchTo(oldCxt); /* Build and return the row type struct */ - return plpgsql_build_datatype(get_rel_type_id(classOid), -1); + return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid); } /* @@ -1935,7 +1969,8 @@ build_row_from_class(Oid classOid) */ var = plpgsql_build_variable(refname, 0, plpgsql_build_datatype(attrStruct->atttypid, - attrStruct->atttypmod), + attrStruct->atttypmod, + attrStruct->attcollation), false); /* Add the variable to the row */ @@ -2013,10 +2048,13 @@ build_row_from_vars(PLpgSQL_variable **vars, int numvars) /* * plpgsql_build_datatype - * Build PLpgSQL_type struct given type OID and typmod. + * Build PLpgSQL_type struct given type OID, typmod, and collation. + * + * If collation is not InvalidOid then it overrides the type's default + * collation. But collation is ignored if the datatype is non-collatable. */ PLpgSQL_type * -plpgsql_build_datatype(Oid typeOid, int32 typmod) +plpgsql_build_datatype(Oid typeOid, int32 typmod, Oid collation) { HeapTuple typeTup; PLpgSQL_type *typ; @@ -2025,7 +2063,7 @@ plpgsql_build_datatype(Oid typeOid, int32 typmod) if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", typeOid); - typ = build_datatype(typeTup, typmod); + typ = build_datatype(typeTup, typmod, collation); ReleaseSysCache(typeTup); @@ -2036,7 +2074,7 @@ plpgsql_build_datatype(Oid typeOid, int32 typmod) * Utility subroutine to make a PLpgSQL_type struct given a pg_type entry */ static PLpgSQL_type * -build_datatype(HeapTuple typeTup, int32 typmod) +build_datatype(HeapTuple typeTup, int32 typmod, Oid collation) { Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup); PLpgSQL_type *typ; @@ -2077,6 +2115,9 @@ build_datatype(HeapTuple typeTup, int32 typmod) typ->typbyval = typeStruct->typbyval; typ->typrelid = typeStruct->typrelid; typ->typioparam = getTypeIOParam(typeTup); + typ->collation = typeStruct->typcollation; + if (OidIsValid(collation) && OidIsValid(typ->collation)) + typ->collation = collation; fmgr_info(typeStruct->typinput, &(typ->typinput)); typ->atttypmod = typmod; @@ -2285,6 +2326,9 @@ compute_function_hashkey(FunctionCallInfo fcinfo, hashkey->trigrelOid = RelationGetRelid(trigdata->tg_relation); } + /* get input collation, if known */ + hashkey->inputCollation = fcinfo->flinfo->fn_collation; + if (procStruct->pronargs > 0) { /* get the argument types */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 1f4d5ac57a..f793991209 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -1516,7 +1516,9 @@ exec_stmt_case(PLpgSQL_execstate *estate, PLpgSQL_stmt_case *stmt) * this doesn't affect the originally stored function parse tree. */ if (t_var->datatype->typoid != t_oid) - t_var->datatype = plpgsql_build_datatype(t_oid, -1); + t_var->datatype = plpgsql_build_datatype(t_oid, + -1, + estate->func->fn_input_collation); /* now we can assign to the variable */ exec_assign_value(estate, @@ -4307,33 +4309,64 @@ exec_get_datum_type(PLpgSQL_execstate *estate, } /* - * exec_get_rec_fieldtype Get datatype of a PLpgSQL record field - * - * Also returns the field number to *fieldno. + * exec_get_datum_collation Get collation of a PLpgSQL_datum */ Oid -exec_get_rec_fieldtype(PLpgSQL_rec *rec, const char *fieldname, - int *fieldno) +exec_get_datum_collation(PLpgSQL_execstate *estate, + PLpgSQL_datum *datum) { - Oid typeid; - int fno; + Oid collid; - if (rec->tupdesc == NULL) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), - errmsg("record \"%s\" is not assigned yet", - rec->refname), - errdetail("The tuple structure of a not-yet-assigned record is indeterminate."))); - fno = SPI_fnumber(rec->tupdesc, fieldname); - if (fno == SPI_ERROR_NOATTRIBUTE) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_COLUMN), - errmsg("record \"%s\" has no field \"%s\"", - rec->refname, fieldname))); - typeid = SPI_gettypeid(rec->tupdesc, fno); + switch (datum->dtype) + { + case PLPGSQL_DTYPE_VAR: + { + PLpgSQL_var *var = (PLpgSQL_var *) datum; - *fieldno = fno; - return typeid; + collid = var->datatype->collation; + break; + } + + case PLPGSQL_DTYPE_ROW: + case PLPGSQL_DTYPE_REC: + /* composite types are never collatable */ + collid = InvalidOid; + break; + + case PLPGSQL_DTYPE_RECFIELD: + { + PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) datum; + PLpgSQL_rec *rec; + int fno; + + rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]); + if (rec->tupdesc == NULL) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("record \"%s\" is not assigned yet", + rec->refname), + errdetail("The tuple structure of a not-yet-assigned record is indeterminate."))); + fno = SPI_fnumber(rec->tupdesc, recfield->fieldname); + if (fno == SPI_ERROR_NOATTRIBUTE) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("record \"%s\" has no field \"%s\"", + rec->refname, recfield->fieldname))); + /* XXX there's no SPI_getcollid, as yet */ + if (fno > 0) + collid = rec->tupdesc->attrs[fno - 1]->attcollation; + else /* no system column types have collation */ + collid = InvalidOid; + break; + } + + default: + elog(ERROR, "unrecognized dtype: %d", datum->dtype); + collid = InvalidOid; /* keep compiler quiet */ + break; + } + + return collid; } /* ---------- diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 7015379842..25689c7891 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -167,6 +167,7 @@ typedef struct bool typbyval; Oid typrelid; Oid typioparam; + Oid collation; /* from pg_type, but can be overridden */ FmgrInfo typinput; /* lookup info for typinput function */ int32 atttypmod; /* typmod (taken from someplace else) */ } PLpgSQL_type; @@ -634,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 diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out index 4680ffd009..a4ec1e9080 100644 --- a/src/test/regress/expected/collate.linux.utf8.out +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -686,57 +686,61 @@ SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; 2 | äbc (4 rows) --- propagation of collation in inlined and non-inlined cases +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql AS $$ select $1 < $2 $$; CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, - mylt(a.b, b.b), mylt_noninline(a.b, b.b) + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b; - a | b | lt | mylt | mylt_noninline ------+-----+----+------+---------------- - abc | abc | f | f | f - abc | ABC | t | t | t - abc | äbc | t | t | t - abc | bbc | t | t | t - ABC | abc | f | f | f - ABC | ABC | f | f | f - ABC | äbc | t | t | t - ABC | bbc | t | t | t - äbc | abc | f | f | f - äbc | ABC | f | f | f - äbc | äbc | f | f | f - äbc | bbc | t | t | t - bbc | abc | f | f | f - bbc | ABC | f | f | f - bbc | äbc | f | f | f - bbc | bbc | f | f | f + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | t | t | t | t + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | f | f | f | f + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | t | t | t | t + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | f | f | f | f + bbc | bbc | f | f | f | f (16 rows) SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, - mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C") + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b; - a | b | lt | mylt | mylt_noninline ------+-----+----+------+---------------- - abc | abc | f | f | f - abc | ABC | f | f | f - abc | äbc | t | t | t - abc | bbc | t | t | t - ABC | abc | t | t | t - ABC | ABC | f | f | f - ABC | äbc | t | t | t - ABC | bbc | t | t | t - äbc | abc | f | f | f - äbc | ABC | f | f | f - äbc | äbc | f | f | f - äbc | bbc | f | f | f - bbc | abc | f | f | f - bbc | ABC | f | f | f - bbc | äbc | t | t | t - bbc | bbc | f | f | f + a | b | lt | mylt | mylt_noninline | mylt_plpgsql +-----+-----+----+------+----------------+-------------- + abc | abc | f | f | f | f + abc | ABC | f | f | f | f + abc | äbc | t | t | t | t + abc | bbc | t | t | t | t + ABC | abc | t | t | t | t + ABC | ABC | f | f | f | f + ABC | äbc | t | t | t | t + ABC | bbc | t | t | t | t + äbc | abc | f | f | f | f + äbc | ABC | f | f | f | f + äbc | äbc | f | f | f | f + äbc | bbc | f | f | f | f + bbc | abc | f | f | f | f + bbc | ABC | f | f | f | f + bbc | äbc | t | t | t | t + bbc | bbc | f | f | f | f (16 rows) -- polymorphism diff --git a/src/test/regress/sql/collate.linux.utf8.sql b/src/test/regress/sql/collate.linux.utf8.sql index 2a1f2113b3..265db72220 100644 --- a/src/test/regress/sql/collate.linux.utf8.sql +++ b/src/test/regress/sql/collate.linux.utf8.sql @@ -212,7 +212,8 @@ SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; --- propagation of collation in inlined and non-inlined cases +-- propagation of collation in SQL functions (inlined and non-inlined cases) +-- and plpgsql functions too CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql AS $$ select $1 < $2 $$; @@ -220,13 +221,17 @@ CREATE FUNCTION mylt (text, text) RETURNS boolean LANGUAGE sql CREATE FUNCTION mylt_noninline (text, text) RETURNS boolean LANGUAGE sql AS $$ select $1 < $2 limit 1 $$; +CREATE FUNCTION mylt_plpgsql (text, text) RETURNS boolean LANGUAGE plpgsql + AS $$ begin return $1 < $2; end $$; + SELECT a.b AS a, b.b AS b, a.b < b.b AS lt, - mylt(a.b, b.b), mylt_noninline(a.b, b.b) + mylt(a.b, b.b), mylt_noninline(a.b, b.b), mylt_plpgsql(a.b, b.b) FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b; SELECT a.b AS a, b.b AS b, a.b < b.b COLLATE "C" AS lt, - mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C") + mylt(a.b, b.b COLLATE "C"), mylt_noninline(a.b, b.b COLLATE "C"), + mylt_plpgsql(a.b, b.b COLLATE "C") FROM collate_test1 a, collate_test1 b ORDER BY a.b, b.b;