diff options
author | Peter Eisentraut <peter_e@gmx.net> | 2011-02-08 23:04:18 +0200 |
---|---|---|
committer | Peter Eisentraut <peter_e@gmx.net> | 2011-02-08 23:04:18 +0200 |
commit | 414c5a2ea65cbd38d79ffdf9b1fde7cc75c134e0 (patch) | |
tree | 016efd0c7108f659ea4f3c52ea54d78e1e5449e1 /src/backend/parser | |
parent | 1703f0e8da2e8e3eccb6e12879c011ba106f8a62 (diff) | |
download | postgresql-414c5a2ea65cbd38d79ffdf9b1fde7cc75c134e0.tar.gz postgresql-414c5a2ea65cbd38d79ffdf9b1fde7cc75c134e0.zip |
Per-column collation support
This adds collation support for columns and domains, a COLLATE clause
to override it per expression, and B-tree index support.
Peter Eisentraut
reviewed by Pavel Stehule, Itagaki Takahiro, Robert Haas, Noah Misch
Diffstat (limited to 'src/backend/parser')
-rw-r--r-- | src/backend/parser/analyze.c | 17 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 50 | ||||
-rw-r--r-- | src/backend/parser/parse_agg.c | 6 | ||||
-rw-r--r-- | src/backend/parser/parse_clause.c | 10 | ||||
-rw-r--r-- | src/backend/parser/parse_coerce.c | 132 | ||||
-rw-r--r-- | src/backend/parser/parse_cte.c | 26 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 53 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 15 | ||||
-rw-r--r-- | src/backend/parser/parse_node.c | 8 | ||||
-rw-r--r-- | src/backend/parser/parse_oper.c | 16 | ||||
-rw-r--r-- | src/backend/parser/parse_param.c | 3 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 33 | ||||
-rw-r--r-- | src/backend/parser/parse_target.c | 9 | ||||
-rw-r--r-- | src/backend/parser/parse_type.c | 97 | ||||
-rw-r--r-- | src/backend/parser/parse_utilcmd.c | 9 |
15 files changed, 443 insertions, 41 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 070e4c177a6..22447f92a2e 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -1203,6 +1203,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ListCell *left_tlist, *lct, *lcm, + *lcc, *l; List *targetvars, *targetnames, @@ -1296,10 +1297,11 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) targetnames = NIL; left_tlist = list_head(leftmostQuery->targetList); - forboth(lct, sostmt->colTypes, lcm, sostmt->colTypmods) + forthree(lct, sostmt->colTypes, lcm, sostmt->colTypmods, lcc, sostmt->colCollations) { Oid colType = lfirst_oid(lct); int32 colTypmod = lfirst_int(lcm); + Oid colCollation = lfirst_oid(lcc); TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist); char *colName; TargetEntry *tle; @@ -1311,6 +1313,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) lefttle->resno, colType, colTypmod, + colCollation, 0); var->location = exprLocation((Node *) lefttle->expr); tle = makeTargetEntry((Expr *) var, @@ -1418,7 +1421,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) * Recursively transform leaves and internal nodes of a set-op tree * * In addition to returning the transformed node, we return a list of - * expression nodes showing the type, typmod, and location (for error messages) + * expression nodes showing the type, typmod, collation, and location (for error messages) * of each output column of the set-op node. This is used only during the * internal recursion of this function. At the upper levels we use * SetToDefault nodes for this purpose, since they carry exactly the fields @@ -1591,6 +1594,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, *colInfo = NIL; op->colTypes = NIL; op->colTypmods = NIL; + op->colCollations = NIL; op->groupClauses = NIL; forboth(lci, lcolinfo, rci, rcolinfo) { @@ -1604,6 +1608,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, SetToDefault *rescolnode; Oid rescoltype; int32 rescoltypmod; + Oid rescolcoll; /* select common type, same as CASE et al */ rescoltype = select_common_type(pstate, @@ -1615,6 +1620,12 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, rescoltypmod = lcoltypmod; else rescoltypmod = -1; + /* Select common collation. A common collation is + * required for all set operators except UNION ALL; see + * SQL:2008-2 7.13 SR 15c. */ + rescolcoll = select_common_collation(pstate, + list_make2(lcolnode, rcolnode), + (op->op == SETOP_UNION && op->all)); /* * Verify the coercions are actually possible. If not, we'd fail @@ -1643,11 +1654,13 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, rescolnode = makeNode(SetToDefault); rescolnode->typeId = rescoltype; rescolnode->typeMod = rescoltypmod; + rescolnode->collid = rescolcoll; rescolnode->location = exprLocation(bestexpr); *colInfo = lappend(*colInfo, rescolnode); op->colTypes = lappend_oid(op->colTypes, rescoltype); op->colTypmods = lappend_int(op->colTypmods, rescoltypmod); + op->colCollations = lappend_oid(op->colCollations, rescolcoll); /* * For all cases except UNION ALL, identify the grouping operators diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index ced78734bbf..a1bcf02f5be 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -261,6 +261,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ %type <list> func_name handler_name qual_Op qual_all_Op subquery_Op opt_class opt_inline_handler opt_validator validator_clause + opt_collate %type <range> qualified_name OptConstrFromTable @@ -394,7 +395,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ %type <list> copy_generic_opt_list copy_generic_opt_arg_list %type <list> copy_options -%type <typnam> Typename SimpleTypename ConstTypename +%type <typnam> Typename SimpleTypename SimpleTypenameWithoutCollation + ConstTypename GenericType Numeric opt_float Character ConstCharacter CharacterWithLength CharacterWithoutLength @@ -5323,38 +5325,45 @@ index_params: index_elem { $$ = list_make1($1); } * expressions in parens. For backwards-compatibility reasons, we allow * an expression that's just a function call to be written without parens. */ -index_elem: ColId opt_class opt_asc_desc opt_nulls_order +index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order { $$ = makeNode(IndexElem); $$->name = $1; $$->expr = NULL; $$->indexcolname = NULL; - $$->opclass = $2; - $$->ordering = $3; - $$->nulls_ordering = $4; + $$->collation = $2; + $$->opclass = $3; + $$->ordering = $4; + $$->nulls_ordering = $5; } - | func_expr opt_class opt_asc_desc opt_nulls_order + | func_expr opt_collate opt_class opt_asc_desc opt_nulls_order { $$ = makeNode(IndexElem); $$->name = NULL; $$->expr = $1; $$->indexcolname = NULL; - $$->opclass = $2; - $$->ordering = $3; - $$->nulls_ordering = $4; + $$->collation = $2; + $$->opclass = $3; + $$->ordering = $4; + $$->nulls_ordering = $5; } - | '(' a_expr ')' opt_class opt_asc_desc opt_nulls_order + | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order { $$ = makeNode(IndexElem); $$->name = NULL; $$->expr = $2; $$->indexcolname = NULL; - $$->opclass = $4; - $$->ordering = $5; - $$->nulls_ordering = $6; + $$->collation = $4; + $$->opclass = $5; + $$->ordering = $6; + $$->nulls_ordering = $7; } ; +opt_collate: COLLATE any_name { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + opt_class: any_name { $$ = $1; } | USING any_name { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } @@ -8776,6 +8785,13 @@ opt_array_bounds: ; SimpleTypename: + SimpleTypenameWithoutCollation opt_collate + { + $$ = $1; + $$->collnames = $2; + } + +SimpleTypenameWithoutCollation: GenericType { $$ = $1; } | Numeric { $$ = $1; } | Bit { $$ = $1; } @@ -9811,6 +9827,14 @@ c_expr: columnref { $$ = $1; } r->location = @1; $$ = (Node *)r; } + | c_expr COLLATE any_name + { + CollateClause *n = makeNode(CollateClause); + n->arg = (Expr *) $1; + n->collnames = $3; + n->location = @2; + $$ = (Node *)n; + } ; /* diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index e43bc54b3fd..8267627c42f 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -723,6 +723,7 @@ build_aggregate_fnexprs(Oid *agg_input_types, Oid agg_result_type, Oid transfn_oid, Oid finalfn_oid, + Oid collation, Expr **transfnexpr, Expr **finalfnexpr) { @@ -741,6 +742,7 @@ build_aggregate_fnexprs(Oid *agg_input_types, argp->paramid = -1; argp->paramtype = agg_state_type; argp->paramtypmod = -1; + argp->paramcollation = collation; argp->location = -1; args = list_make1(argp); @@ -752,6 +754,7 @@ build_aggregate_fnexprs(Oid *agg_input_types, argp->paramid = -1; argp->paramtype = agg_input_types[i]; argp->paramtypmod = -1; + argp->paramcollation = collation; argp->location = -1; args = lappend(args, argp); } @@ -759,6 +762,7 @@ build_aggregate_fnexprs(Oid *agg_input_types, *transfnexpr = (Expr *) makeFuncExpr(transfn_oid, agg_state_type, args, + collation, COERCE_DONTCARE); /* see if we have a final function */ @@ -776,11 +780,13 @@ build_aggregate_fnexprs(Oid *agg_input_types, argp->paramid = -1; argp->paramtype = agg_state_type; argp->paramtypmod = -1; + argp->paramcollation = collation; argp->location = -1; args = list_make1(argp); *finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid, agg_result_type, args, + collation, COERCE_DONTCARE); } diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index f9560d07b9c..d250e0c8598 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -613,7 +613,8 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) tupdesc = BuildDescFromLists(rte->eref->colnames, rte->funccoltypes, - rte->funccoltypmods); + rte->funccoltypmods, + rte->funccolcollations); CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false); } @@ -1935,6 +1936,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, bool resolveUnknown) { Oid restype = exprType((Node *) tle->expr); + Oid rescollation = exprCollation((Node *) tle->expr); Oid sortop; Oid eqop; bool hashable; @@ -2018,6 +2020,12 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, break; } + if (type_is_collatable(restype) && !OidIsValid(rescollation)) + ereport(ERROR, + (errcode(ERRCODE_INDETERMINATE_COLLATION), + errmsg("no collation was derived for the sort expression"), + errhint("Use the COLLATE clause to set the collation explicitly."))); + cancel_parser_errposition_callback(&pcbstate); /* avoid making duplicate sortlist entries */ diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 5b0dc1420d0..2fd808d26b2 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -16,6 +16,7 @@ #include "catalog/pg_cast.h" #include "catalog/pg_class.h" +#include "catalog/pg_collation.h" #include "catalog/pg_inherits_fn.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" @@ -216,6 +217,7 @@ coerce_type(ParseState *pstate, Node *node, newcon->consttype = baseTypeId; newcon->consttypmod = inputTypeMod; + newcon->constcollid = get_typcollation(newcon->consttype); newcon->constlen = typeLen(targetType); newcon->constbyval = typeByVal(targetType); newcon->constisnull = con->constisnull; @@ -277,6 +279,14 @@ coerce_type(ParseState *pstate, Node *node, if (result) return result; } + if (IsA(node, CollateClause)) + { + CollateClause *cc = (CollateClause *) node; + + cc->arg = (Expr *) coerce_type(pstate, (Node *) cc->arg, inputTypeId, targetTypeId, targetTypeMod, + ccontext, cformat, location); + return (Node *) cc; + } pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId); if (pathtype != COERCION_PATH_NONE) @@ -718,6 +728,7 @@ build_coercion_expression(Node *node, FuncExpr *fexpr; List *args; Const *cons; + Oid collation; Assert(OidIsValid(funcId)); @@ -749,7 +760,9 @@ build_coercion_expression(Node *node, args = lappend(args, cons); } - fexpr = makeFuncExpr(funcId, targetTypeId, args, cformat); + collation = coercion_expression_result_collation(targetTypeId, node); + + fexpr = makeFuncExpr(funcId, targetTypeId, args, collation, cformat); fexpr->location = location; return (Node *) fexpr; } @@ -2081,3 +2094,120 @@ typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId) return result; } + + +/* + * select_common_collation() -- determine one collation to apply for + * an expression node, for evaluating the expression itself or to + * label the result of the expression node. + * + * none_ok means that it is permitted to return "no" collation. It is + * then not possible to sort the result value of whatever expression + * is applying this. none_ok = true reflects the rules of SQL + * standard clause "Result of data type combinations", none_ok = false + * reflects the rules of clause "Collation determination" (in some + * cases invoked via "Grouping operations"). + */ +Oid +select_common_collation(ParseState *pstate, List *exprs, bool none_ok) +{ + ListCell *lc; + + /* + * Check if there are any explicit collation derivations. If so, + * they must all be the same. + */ + foreach(lc, exprs) + { + Node *pexpr = (Node *) lfirst(lc); + Oid pcoll = exprCollation(pexpr); + bool pexplicit = IsA(pexpr, CollateClause); + + if (pcoll && pexplicit) + { + ListCell *lc2; + for_each_cell(lc2, lnext(lc)) + { + Node *nexpr = (Node *) lfirst(lc2); + Oid ncoll = exprCollation(nexpr); + bool nexplicit = IsA(nexpr, CollateClause); + + if (!ncoll || !nexplicit) + continue; + + if (ncoll != pcoll) + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("collation mismatch between explicit collations \"%s\" and \"%s\"", + get_collation_name(pcoll), + get_collation_name(ncoll)), + parser_errposition(pstate, exprLocation(nexpr)))); + } + + return pcoll; + } + } + + /* + * Check if there are any implicit collation derivations. + */ + foreach(lc, exprs) + { + Node *pexpr = (Node *) lfirst(lc); + Oid pcoll = exprCollation(pexpr); + + if (pcoll && pcoll != DEFAULT_COLLATION_OID) + { + ListCell *lc2; + for_each_cell(lc2, lnext(lc)) + { + Node *nexpr = (Node *) lfirst(lc2); + Oid ncoll = exprCollation(nexpr); + + if (!ncoll || ncoll == DEFAULT_COLLATION_OID) + continue; + + if (ncoll != pcoll) + { + if (none_ok) + return InvalidOid; + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"", + get_collation_name(pcoll), + get_collation_name(ncoll)), + errhint("You can override the collation by applying the COLLATE clause to one or both expressions."), + parser_errposition(pstate, exprLocation(nexpr)))); + } + } + + return pcoll; + } + } + + foreach(lc, exprs) + { + Node *pexpr = (Node *) lfirst(lc); + Oid pcoll = exprCollation(pexpr); + + if (pcoll == DEFAULT_COLLATION_OID) + { + ListCell *lc2; + for_each_cell(lc2, lnext(lc)) + { + Node *nexpr = (Node *) lfirst(lc2); + Oid ncoll = exprCollation(nexpr); + + if (ncoll != pcoll) + break; + } + + return pcoll; + } + } + + /* + * Else use default + */ + return InvalidOid; +} diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c index 24ba008f9ee..4d3d33eb079 100644 --- a/src/backend/parser/parse_cte.c +++ b/src/backend/parser/parse_cte.c @@ -14,11 +14,13 @@ */ #include "postgres.h" +#include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "nodes/nodeFuncs.h" #include "parser/analyze.h" #include "parser/parse_cte.h" #include "utils/builtins.h" +#include "utils/lsyscache.h" /* Enumeration of contexts in which a self-reference is disallowed */ @@ -263,11 +265,13 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte) */ ListCell *lctlist, *lctyp, - *lctypmod; + *lctypmod, + *lccoll; int varattno; lctyp = list_head(cte->ctecoltypes); lctypmod = list_head(cte->ctecoltypmods); + lccoll = list_head(cte->ctecolcollations); varattno = 0; foreach(lctlist, query->targetList) { @@ -278,7 +282,7 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte) continue; varattno++; Assert(varattno == te->resno); - if (lctyp == NULL || lctypmod == NULL) /* shouldn't happen */ + if (lctyp == NULL || lctypmod == NULL || lccoll == NULL) /* shouldn't happen */ elog(ERROR, "wrong number of output columns in WITH"); texpr = (Node *) te->expr; if (exprType(texpr) != lfirst_oid(lctyp) || @@ -293,10 +297,20 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte) exprTypmod(texpr))), errhint("Cast the output of the non-recursive term to the correct type."), parser_errposition(pstate, exprLocation(texpr)))); + if (exprCollation(texpr) != lfirst_oid(lccoll)) + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("recursive query \"%s\" column %d has collation \"%s\" in non-recursive term but collation \"%s\" overall", + cte->ctename, varattno, + get_collation_name(lfirst_oid(lccoll)), + get_collation_name(exprCollation(texpr))), + errhint("Use the COLLATE clause to set the collation of the non-recursive term."), + parser_errposition(pstate, exprLocation(texpr)))); lctyp = lnext(lctyp); lctypmod = lnext(lctypmod); + lccoll = lnext(lccoll); } - if (lctyp != NULL || lctypmod != NULL) /* shouldn't happen */ + if (lctyp != NULL || lctypmod != NULL || lccoll != NULL) /* shouldn't happen */ elog(ERROR, "wrong number of output columns in WITH"); } } @@ -331,7 +345,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) * handling.) */ cte->ctecolnames = copyObject(cte->aliascolnames); - cte->ctecoltypes = cte->ctecoltypmods = NIL; + cte->ctecoltypes = cte->ctecoltypmods = cte->ctecolcollations = NIL; numaliases = list_length(cte->aliascolnames); varattno = 0; foreach(tlistitem, tlist) @@ -339,6 +353,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) TargetEntry *te = (TargetEntry *) lfirst(tlistitem); Oid coltype; int32 coltypmod; + Oid colcoll; if (te->resjunk) continue; @@ -353,6 +368,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) } coltype = exprType((Node *) te->expr); coltypmod = exprTypmod((Node *) te->expr); + colcoll = exprCollation((Node *) te->expr); /* * If the CTE is recursive, force the exposed column type of any @@ -366,9 +382,11 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) { coltype = TEXTOID; coltypmod = -1; /* should be -1 already, but be sure */ + colcoll = DEFAULT_COLLATION_OID; } cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype); cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod); + cte->ctecolcollations = lappend_oid(cte->ctecolcollations, colcoll); } if (varattno < numaliases) ereport(ERROR, diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 129b39cb26f..ae565325928 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -65,6 +65,7 @@ static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, static Node *transformIndirection(ParseState *pstate, Node *basenode, List *indirection); static Node *transformTypeCast(ParseState *pstate, TypeCast *tc); +static Node *transformCollateClause(ParseState *pstate, CollateClause *c); static Node *make_row_comparison_op(ParseState *pstate, List *opname, List *largs, List *rargs, int location); static Node *make_row_distinct_op(ParseState *pstate, List *opname, @@ -146,6 +147,12 @@ transformExpr(ParseState *pstate, Node *expr) { TypeCast *tc = (TypeCast *) expr; + if (tc->typeName->collnames) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("COLLATE clause not allowed in cast target"), + parser_errposition(pstate, tc->typeName->location))); + /* * If the subject of the typecast is an ARRAY[] construct and * the target type is an array type, we invoke @@ -185,6 +192,10 @@ transformExpr(ParseState *pstate, Node *expr) break; } + case T_CollateClause: + result = transformCollateClause(pstate, (CollateClause *) expr); + break; + case T_A_Expr: { A_Expr *a = (A_Expr *) expr; @@ -423,6 +434,7 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) exprType(result), InvalidOid, exprTypmod(result), + exprCollation(result), subscripts, NULL); subscripts = NIL; @@ -444,6 +456,7 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) exprType(result), InvalidOid, exprTypmod(result), + exprCollation(result), subscripts, NULL); @@ -1267,6 +1280,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) placeholder = makeNode(CaseTestExpr); placeholder->typeId = exprType(arg); placeholder->typeMod = exprTypmod(arg); + placeholder->collation = exprCollation(arg); } else placeholder = NULL; @@ -1351,6 +1365,8 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) "CASE/WHEN"); } + newc->casecollation = select_common_collation(pstate, resultexprs, true); + newc->location = c->location; return (Node *) newc; @@ -1461,6 +1477,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink) param->paramid = tent->resno; param->paramtype = exprType((Node *) tent->expr); param->paramtypmod = exprTypmod((Node *) tent->expr); + param->paramcollation = exprCollation((Node *) tent->expr); param->location = -1; right_list = lappend(right_list, param); @@ -1704,6 +1721,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c) } newc->args = newcoercedargs; + newc->coalescecollation = select_common_collation(pstate, newcoercedargs, true); newc->location = c->location; return (Node *) newc; } @@ -1728,6 +1746,7 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m) } newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL); + newm->collid = select_common_collation(pstate, newargs, false); /* Convert arguments if necessary */ foreach(args, newargs) @@ -2083,6 +2102,36 @@ transformTypeCast(ParseState *pstate, TypeCast *tc) } /* + * Handle an explicit COLLATE clause. + * + * Transform the argument, and look up the collation name. + */ +static Node * +transformCollateClause(ParseState *pstate, CollateClause *c) +{ + CollateClause *newc; + Oid argtype; + + newc = makeNode(CollateClause); + newc->arg = (Expr *) transformExpr(pstate, (Node *) c->arg); + + argtype = exprType((Node *) newc->arg); + /* The unknown type is not collatable, but coerce_type() takes + * care of it separately, so we'll let it go here. */ + if (!type_is_collatable(argtype) && argtype != UNKNOWNOID) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("collations are not supported by type %s", + format_type_be(argtype)))); + + newc->collOid = LookupCollation(pstate, c->collnames, c->location); + newc->collnames = c->collnames; + newc->location = c->location; + + return (Node *) newc; +} + +/* * Transform a "row compare-op row" construct * * The inputs are lists of already-transformed expressions. @@ -2103,6 +2152,7 @@ make_row_comparison_op(ParseState *pstate, List *opname, List *opexprs; List *opnos; List *opfamilies; + List *collids; ListCell *l, *r; List **opfamily_lists; @@ -2273,6 +2323,7 @@ make_row_comparison_op(ParseState *pstate, List *opname, * possibility that make_op inserted coercion operations. */ opnos = NIL; + collids = NIL; largs = NIL; rargs = NIL; foreach(l, opexprs) @@ -2280,6 +2331,7 @@ make_row_comparison_op(ParseState *pstate, List *opname, OpExpr *cmp = (OpExpr *) lfirst(l); opnos = lappend_oid(opnos, cmp->opno); + collids = lappend_oid(collids, cmp->collid); largs = lappend(largs, linitial(cmp->args)); rargs = lappend(rargs, lsecond(cmp->args)); } @@ -2288,6 +2340,7 @@ make_row_comparison_op(ParseState *pstate, List *opname, rcexpr->rctype = rctype; rcexpr->opnos = opnos; rcexpr->opfamilies = opfamilies; + rcexpr->collids = collids; rcexpr->largs = largs; rcexpr->rargs = rargs; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index f1a4f9b959e..0af9cbd92b3 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -78,6 +78,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, bool retset; int nvargs; FuncDetailCode fdresult; + Oid funccollid; /* * Most of the rest of the parser just assumes that functions do not have @@ -343,6 +344,12 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types); + /* XXX: If we knew which functions required collation information, + * we could selectively set the last argument to true here. */ + funccollid = select_common_collation(pstate, fargs, false); + if (!OidIsValid(funccollid)) + funccollid = get_typcollation(rettype); + /* * If it's a variadic function call, transform the last nvargs arguments * into an array --- unless it's an "any" variadic. @@ -383,6 +390,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, funcexpr->funcretset = retset; funcexpr->funcformat = COERCE_EXPLICIT_CALL; funcexpr->args = fargs; + funcexpr->collid = funccollid; funcexpr->location = location; retval = (Node *) funcexpr; @@ -396,6 +404,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, aggref->aggtype = rettype; /* args, aggorder, aggdistinct will be set by transformAggregateCall */ aggref->aggstar = agg_star; + aggref->collid = funccollid; /* agglevelsup will be set by transformAggregateCall */ aggref->location = location; @@ -453,6 +462,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, /* winref will be set by transformWindowFuncCall */ wfunc->winstar = agg_star; wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE); + wfunc->collid = funccollid; wfunc->location = location; /* @@ -1303,7 +1313,7 @@ FuncNameAsType(List *funcname) Oid result; Type typtup; - typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL); + typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL, NULL); if (typtup == NULL) return InvalidOid; @@ -1380,6 +1390,7 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg, fselect->fieldnum = i + 1; fselect->resulttype = att->atttypid; fselect->resulttypmod = att->atttypmod; + fselect->resultcollation = att->attcollation; return (Node *) fselect; } } @@ -1489,7 +1500,7 @@ LookupTypeNameOid(const TypeName *typename) Oid result; Type typtup; - typtup = LookupTypeName(NULL, typename, NULL); + typtup = LookupTypeName(NULL, typename, NULL, NULL); if (typtup == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index aed404eb1dc..163fc891799 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -189,10 +189,11 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location) sublevels_up; Oid vartypeid; int32 type_mod; + Oid varcollid; vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); - get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod); - result = makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up); + get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod, &varcollid); + result = makeVar(vnum, attrno, vartypeid, type_mod, varcollid, sublevels_up); result->location = location; return result; } @@ -269,6 +270,7 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod) * elementType OID of array's element type (fetch with transformArrayType, * or pass InvalidOid to do it here) * arrayTypMod typmod for the array (which is also typmod for the elements) + * arrayColl OID of collation of array and array's elements * indirection Untransformed list of subscripts (must not be NIL) * assignFrom NULL for array fetch, else transformed expression for source. */ @@ -278,6 +280,7 @@ transformArraySubscripts(ParseState *pstate, Oid arrayType, Oid elementType, int32 arrayTypMod, + Oid arrayColl, List *indirection, Node *assignFrom) { @@ -404,6 +407,7 @@ transformArraySubscripts(ParseState *pstate, aref->refarraytype = arrayType; aref->refelemtype = elementType; aref->reftypmod = arrayTypMod; + aref->refcollid = arrayColl; aref->refupperindexpr = upperIndexpr; aref->reflowerindexpr = lowerIndexpr; aref->refexpr = (Expr *) arrayBase; diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 1f50bdcc342..cad41d46f09 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -782,6 +782,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, List *args; Oid rettype; OpExpr *result; + Oid opcollid; /* Select the operator */ if (rtree == NULL) @@ -861,6 +862,12 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); + /* XXX: If we knew which functions required collation information, + * we could selectively set the last argument to true here. */ + opcollid = select_common_collation(pstate, args, false); + if (!OidIsValid(opcollid)) + opcollid = get_typcollation(rettype); + /* and build the expression node */ result = makeNode(OpExpr); result->opno = oprid(tup); @@ -868,6 +875,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, result->opresulttype = rettype; result->opretset = get_func_retset(opform->oprcode); result->args = args; + result->collid = opcollid; result->location = location; ReleaseSysCache(tup); @@ -896,6 +904,7 @@ make_scalar_array_op(ParseState *pstate, List *opname, List *args; Oid rettype; ScalarArrayOpExpr *result; + Oid opcollid; ltypeId = exprType(ltree); atypeId = exprType(rtree); @@ -990,12 +999,19 @@ make_scalar_array_op(ParseState *pstate, List *opname, /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); + /* XXX: If we knew which functions required collation information, + * we could selectively set the last argument to true here. */ + opcollid = select_common_collation(pstate, args, false); + if (!OidIsValid(opcollid)) + opcollid = get_typcollation(rettype); + /* and build the expression node */ result = makeNode(ScalarArrayOpExpr); result->opno = oprid(tup); result->opfuncid = opform->oprcode; result->useOr = useOr; result->args = args; + result->collid = opcollid; result->location = location; ReleaseSysCache(tup); diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c index c9987d27234..9e9f2e3ca0b 100644 --- a/src/backend/parser/parse_param.c +++ b/src/backend/parser/parse_param.c @@ -30,6 +30,7 @@ #include "nodes/nodeFuncs.h" #include "parser/parse_param.h" #include "utils/builtins.h" +#include "utils/lsyscache.h" typedef struct FixedParamState @@ -113,6 +114,7 @@ fixed_paramref_hook(ParseState *pstate, ParamRef *pref) param->paramid = paramno; param->paramtype = parstate->paramTypes[paramno - 1]; param->paramtypmod = -1; + param->paramcollation = get_typcollation(param->paramtype); param->location = pref->location; return (Node *) param; @@ -165,6 +167,7 @@ variable_paramref_hook(ParseState *pstate, ParamRef *pref) param->paramid = paramno; param->paramtype = *pptype; param->paramtypmod = -1; + param->paramcollation = get_typcollation(param->paramtype); param->location = pref->location; return (Node *) param; diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 331ac670ff8..497c726f314 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1098,6 +1098,7 @@ addRangeTableEntryForFunction(ParseState *pstate, rte->funcexpr = funcexpr; rte->funccoltypes = NIL; rte->funccoltypmods = NIL; + rte->funccolcollations = NIL; rte->alias = alias; eref = makeAlias(alias ? alias->aliasname : funcname, NIL); @@ -1157,6 +1158,7 @@ addRangeTableEntryForFunction(ParseState *pstate, char *attrname; Oid attrtype; int32 attrtypmod; + Oid attrcollation; attrname = pstrdup(n->colname); if (n->typeName->setof) @@ -1165,10 +1167,11 @@ addRangeTableEntryForFunction(ParseState *pstate, errmsg("column \"%s\" cannot be declared SETOF", attrname), parser_errposition(pstate, n->typeName->location))); - typenameTypeIdAndMod(pstate, n->typeName, &attrtype, &attrtypmod); + typenameTypeIdModColl(pstate, n->typeName, &attrtype, &attrtypmod, &attrcollation); eref->colnames = lappend(eref->colnames, makeString(attrname)); rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype); rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod); + rte->funccolcollations = lappend_oid(rte->funccolcollations, attrcollation); } } else @@ -1381,6 +1384,7 @@ addRangeTableEntryForCTE(ParseState *pstate, rte->ctecoltypes = cte->ctecoltypes; rte->ctecoltypmods = cte->ctecoltypmods; + rte->ctecolcollations = cte->ctecolcollations; rte->alias = alias; if (alias) @@ -1573,6 +1577,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, varnode = makeVar(rtindex, varattno, exprType((Node *) te->expr), exprTypmod((Node *) te->expr), + exprCollation((Node *) te->expr), sublevels_up); varnode->location = location; @@ -1612,6 +1617,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, varnode = makeVar(rtindex, 1, funcrettype, -1, + exprCollation(rte->funcexpr), sublevels_up); varnode->location = location; @@ -1626,12 +1632,14 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, { ListCell *l1; ListCell *l2; + ListCell *l3; int attnum = 0; - forboth(l1, rte->funccoltypes, l2, rte->funccoltypmods) + forthree(l1, rte->funccoltypes, l2, rte->funccoltypmods, l3, rte->funccolcollations) { Oid attrtype = lfirst_oid(l1); int32 attrtypmod = lfirst_int(l2); + Oid attrcollation = lfirst_oid(l3); Var *varnode; attnum++; @@ -1639,6 +1647,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, attnum, attrtype, attrtypmod, + attrcollation, sublevels_up); varnode->location = location; *colvars = lappend(*colvars, varnode); @@ -1681,6 +1690,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, varnode = makeVar(rtindex, varattno, exprType(col), exprTypmod(col), + exprCollation(col), sublevels_up); varnode->location = location; *colvars = lappend(*colvars, varnode); @@ -1740,6 +1750,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, varnode = makeVar(rtindex, varattno, exprType(avar), exprTypmod(avar), + exprCollation(avar), sublevels_up); varnode->location = location; @@ -1753,12 +1764,14 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, ListCell *aliasp_item = list_head(rte->eref->colnames); ListCell *lct; ListCell *lcm; + ListCell *lcc; varattno = 0; - forboth(lct, rte->ctecoltypes, lcm, rte->ctecoltypmods) + forthree(lct, rte->ctecoltypes, lcm, rte->ctecoltypmods, lcc, rte->ctecolcollations) { Oid coltype = lfirst_oid(lct); int32 coltypmod = lfirst_int(lcm); + Oid colcoll = lfirst_oid(lcc); varattno++; @@ -1776,7 +1789,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, Var *varnode; varnode = makeVar(rtindex, varattno, - coltype, coltypmod, + coltype, coltypmod, colcoll, sublevels_up); *colvars = lappend(*colvars, varnode); } @@ -1857,7 +1870,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, Var *varnode; varnode = makeVar(rtindex, attr->attnum, - attr->atttypid, attr->atttypmod, + attr->atttypid, attr->atttypmod, attr->attcollation, sublevels_up); varnode->location = location; @@ -1968,7 +1981,7 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) */ void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, - Oid *vartype, int32 *vartypmod) + Oid *vartype, int32 *vartypmod, Oid *varcollid) { switch (rte->rtekind) { @@ -1998,6 +2011,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, get_rel_name(rte->relid)))); *vartype = att_tup->atttypid; *vartypmod = att_tup->atttypmod; + *varcollid = att_tup->attcollation; ReleaseSysCache(tp); } break; @@ -2012,6 +2026,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, rte->eref->aliasname, attnum); *vartype = exprType((Node *) te->expr); *vartypmod = exprTypmod((Node *) te->expr); + *varcollid = exprCollation((Node *) te->expr); } break; case RTE_FUNCTION: @@ -2053,17 +2068,20 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, rte->eref->aliasname))); *vartype = att_tup->atttypid; *vartypmod = att_tup->atttypmod; + *varcollid = att_tup->attcollation; } else if (functypclass == TYPEFUNC_SCALAR) { /* Base data type, i.e. scalar */ *vartype = funcrettype; *vartypmod = -1; + *varcollid = exprCollation(rte->funcexpr); } else if (functypclass == TYPEFUNC_RECORD) { *vartype = list_nth_oid(rte->funccoltypes, attnum - 1); *vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1); + *varcollid = list_nth_oid(rte->funccolcollations, attnum - 1); } else { @@ -2084,6 +2102,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, col = (Node *) list_nth(collist, attnum - 1); *vartype = exprType(col); *vartypmod = exprTypmod(col); + *varcollid = exprCollation(col); } break; case RTE_JOIN: @@ -2097,6 +2116,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, aliasvar = (Node *) list_nth(rte->joinaliasvars, attnum - 1); *vartype = exprType(aliasvar); *vartypmod = exprTypmod(aliasvar); + *varcollid = exprCollation(aliasvar); } break; case RTE_CTE: @@ -2105,6 +2125,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, Assert(attnum > 0 && attnum <= list_length(rte->ctecoltypes)); *vartype = list_nth_oid(rte->ctecoltypes, attnum - 1); *vartypmod = list_nth_int(rte->ctecoltypmods, attnum - 1); + *varcollid = list_nth_oid(rte->ctecolcollations, attnum - 1); } break; default: diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 7d77e0b63f1..a0761da875b 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -374,6 +374,7 @@ transformAssignedExpr(ParseState *pstate, Oid type_id; /* type of value provided */ Oid attrtype; /* type of target column */ int32 attrtypmod; + Oid attrcollation; Relation rd = pstate->p_target_relation; Assert(rd != NULL); @@ -385,6 +386,7 @@ transformAssignedExpr(ParseState *pstate, parser_errposition(pstate, location))); attrtype = attnumTypeId(rd, attrno); attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; + attrcollation = rd->rd_att->attrs[attrno - 1]->attcollation; /* * If the expression is a DEFAULT placeholder, insert the attribute's @@ -400,6 +402,7 @@ transformAssignedExpr(ParseState *pstate, def->typeId = attrtype; def->typeMod = attrtypmod; + def->collid = attrcollation; if (indirection) { if (IsA(linitial(indirection), A_Indices)) @@ -786,6 +789,7 @@ transformAssignmentSubscripts(ParseState *pstate, arrayType, elementTypeId, arrayTypMod, + InvalidOid, subscripts, rhs); @@ -1267,6 +1271,7 @@ ExpandRowReference(ParseState *pstate, Node *expr, fselect->fieldnum = i + 1; fselect->resulttype = att->atttypid; fselect->resulttypmod = att->atttypmod; + fselect->resultcollation = att->attcollation; if (targetlist) { @@ -1338,6 +1343,8 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) exprType(varnode), exprTypmod(varnode), 0); + TupleDescInitEntryCollation(tupleDesc, i, + exprCollation(varnode)); i++; } Assert(lname == NULL && lvar == NULL); /* lists same length? */ @@ -1583,6 +1590,8 @@ FigureColnameInternal(Node *node, char **name) } } break; + case T_CollateClause: + return FigureColnameInternal((Node *) ((CollateClause *) node)->arg, name); case T_CaseExpr: strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult, name); diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 0b601c8b75d..02c1c68827f 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -29,6 +29,8 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ); +static Oid typenameCollation(ParseState *pstate, const TypeName *typeName, + Type typ); /* @@ -36,7 +38,8 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName, * Given a TypeName object, lookup the pg_type syscache entry of the type. * Returns NULL if no such type can be found. If the type is found, * the typmod value represented in the TypeName struct is computed and - * stored into *typmod_p. + * stored into *typmod_p, and the collation is looked up and stored into + * *colloid_p. * * NB: on success, the caller must ReleaseSysCache the type tuple when done * with it. @@ -51,15 +54,18 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName, * found but is a shell, and there is typmod decoration, an error will be * thrown --- this is intentional. * + * colloid_p can also be null. + * * pstate is only used for error location info, and may be NULL. */ Type LookupTypeName(ParseState *pstate, const TypeName *typeName, - int32 *typmod_p) + int32 *typmod_p, Oid *collid_p) { Oid typoid; HeapTuple tup; int32 typmod; + Oid collid; if (typeName->names == NIL) { @@ -174,6 +180,11 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName, if (typmod_p) *typmod_p = typmod; + collid = typenameCollation(pstate, typeName, (Type) tup); + + if (collid_p) + *collid_p = collid; + return (Type) tup; } @@ -185,11 +196,11 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName, * Callers of this can therefore assume the result is a fully valid type. */ Type -typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p) +typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p, Oid *collid_p) { Type tup; - tup = LookupTypeName(pstate, typeName, typmod_p); + tup = LookupTypeName(pstate, typeName, typmod_p, collid_p); if (tup == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), @@ -217,7 +228,7 @@ typenameTypeId(ParseState *pstate, const TypeName *typeName) Oid typoid; Type tup; - tup = typenameType(pstate, typeName, NULL); + tup = typenameType(pstate, typeName, NULL, NULL); typoid = HeapTupleGetOid(tup); ReleaseSysCache(tup); @@ -236,7 +247,25 @@ typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName, { Type tup; - tup = typenameType(pstate, typeName, typmod_p); + tup = typenameType(pstate, typeName, typmod_p, NULL); + *typeid_p = HeapTupleGetOid(tup); + ReleaseSysCache(tup); +} + +/* + * typenameTypeIdModColl - given a TypeName, return the type's OID, + * typmod, and collation + * + * This is equivalent to typenameType, but we only hand back the type OID, + * typmod, and collation, not the syscache entry. + */ +void +typenameTypeIdModColl(ParseState *pstate, const TypeName *typeName, + Oid *typeid_p, int32 *typmod_p, Oid *collid_p) +{ + Type tup; + + tup = typenameType(pstate, typeName, typmod_p, collid_p); *typeid_p = HeapTupleGetOid(tup); ReleaseSysCache(tup); } @@ -351,6 +380,62 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ) } /* + * typenameCollation - given a TypeName, return the collation OID + * + * This will throw an error if the TypeName includes a collation but + * the data type does not support collations. + * + * The actual type OID represented by the TypeName must already have been + * looked up, and is passed as "typ". + * + * pstate is only used for error location info, and may be NULL. + */ +static Oid +typenameCollation(ParseState *pstate, const TypeName *typeName, Type typ) +{ + Oid typcollation = ((Form_pg_type) GETSTRUCT(typ))->typcollation; + + /* return prespecified collation OID if no collation name specified */ + if (typeName->collnames == NIL) + { + if (typeName->collOid == InvalidOid) + return typcollation; + else + return typeName->collOid; + } + + if (!OidIsValid(typcollation)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("collations are not supported by type %s", + format_type_be(HeapTupleGetOid(typ))), + parser_errposition(pstate, typeName->location))); + + return LookupCollation(pstate, typeName->collnames, typeName->location); +} + +/* + * LookupCollation + * + * Look up collation by name, return OID, with support for error + * location. + */ +Oid +LookupCollation(ParseState *pstate, List *collnames, int location) +{ + Oid colloid; + ParseCallbackState pcbstate; + + setup_parser_errposition_callback(&pcbstate, pstate, location); + + colloid = get_collation_oid(collnames, false); + + cancel_parser_errposition_callback(&pcbstate); + + return colloid; +} + +/* * appendTypeNameToBuffer * Append a string representing the name of a TypeName to a StringInfo. * This is the shared guts of TypeNameToString and TypeNameListToString. diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index e0ab88232b1..61ce840a5e5 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -627,7 +627,8 @@ transformInhRelation(CreateStmtContext *cxt, InhRelation *inhRelation) def = makeNode(ColumnDef); def->colname = pstrdup(attributeName); def->typeName = makeTypeNameFromOid(attribute->atttypid, - attribute->atttypmod); + attribute->atttypmod, + attribute->attcollation); def->inhcount = 0; def->is_local = true; def->is_not_null = attribute->attnotnull; @@ -821,7 +822,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename) AssertArg(ofTypename); - tuple = typenameType(NULL, ofTypename, NULL); + tuple = typenameType(NULL, ofTypename, NULL, NULL); typ = (Form_pg_type) GETSTRUCT(tuple); ofTypeId = HeapTupleGetOid(tuple); ofTypename->typeOid = ofTypeId; /* cached for later */ @@ -842,7 +843,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename) continue; n->colname = pstrdup(NameStr(attr->attname)); - n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod); + n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod, attr->attcollation); n->constraints = NULL; n->is_local = true; n->is_from_type = true; @@ -2446,7 +2447,7 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column) /* * All we really need to do here is verify that the type is valid. */ - Type ctype = typenameType(cxt->pstate, column->typeName, NULL); + Type ctype = typenameType(cxt->pstate, column->typeName, NULL, NULL); ReleaseSysCache(ctype); } |