Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Enumerations | Functions

parse_collate.c File Reference

#include "postgres.h"
#include "catalog/pg_collation.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_collate.h"
#include "utils/lsyscache.h"
Include dependency graph for parse_collate.c:

Go to the source code of this file.

Data Structures

struct  assign_collations_context

Enumerations

enum  CollateStrength { COLLATE_NONE, COLLATE_IMPLICIT, COLLATE_CONFLICT, COLLATE_EXPLICIT }

Functions

static bool assign_query_collations_walker (Node *node, ParseState *pstate)
static bool assign_collations_walker (Node *node, assign_collations_context *context)
void assign_query_collations (ParseState *pstate, Query *query)
void assign_list_collations (ParseState *pstate, List *exprs)
void assign_expr_collations (ParseState *pstate, Node *expr)
Oid select_common_collation (ParseState *pstate, List *exprs, bool none_ok)

Enumeration Type Documentation

Enumerator:
COLLATE_NONE 
COLLATE_IMPLICIT 
COLLATE_CONFLICT 
COLLATE_EXPLICIT 

Definition at line 54 of file parse_collate.c.

{
    COLLATE_NONE,               /* expression is of a noncollatable datatype */
    COLLATE_IMPLICIT,           /* collation was derived implicitly */
    COLLATE_CONFLICT,           /* we had a conflict of implicit collations */
    COLLATE_EXPLICIT            /* collation was derived explicitly */
} CollateStrength;


Function Documentation

static bool assign_collations_walker ( Node node,
assign_collations_context context 
) [static]

Definition at line 241 of file parse_collate.c.

References CaseExpr::args, Aggref::args, RowExpr::args, Assert, assign_expr_collations(), assign_list_collations(), COLLATE_CONFLICT, COLLATE_EXPLICIT, COLLATE_IMPLICIT, COLLATE_NONE, assign_collations_context::collation, assign_collations_context::collation2, CollateExpr::collOid, DEFAULT_COLLATION_OID, CaseExpr::defresult, ereport, errcode(), errhint(), errmsg(), ERROR, CaseWhen::expr, TargetEntry::expr, exprCollation(), expression_tree_walker(), exprLocation(), exprSetCollation(), exprSetInputCollation(), exprType(), forboth, get_collation_name(), get_typcollation(), RowCompareExpr::inputcollids, InvalidOid, IsA, lappend_oid(), RowCompareExpr::largs, lfirst, linitial, list_make2, CollateExpr::location, assign_collations_context::location, assign_collations_context::location2, nodeTag, NULL, OidIsValid, parser_errposition(), assign_collations_context::pstate, RowCompareExpr::rargs, TargetEntry::resjunk, CaseWhen::result, FieldSelect::resultcollid, CoerceToDomain::resulttype, select_common_collation(), assign_collations_context::strength, T_Aggref, T_CaseExpr, T_CaseTestExpr, T_CoerceToDomain, T_CoerceToDomainValue, T_CollateExpr, T_Const, T_CurrentOfExpr, T_FieldSelect, T_FromExpr, T_JoinExpr, T_List, T_Param, T_Query, T_RangeTblRef, T_RowCompareExpr, T_RowExpr, T_SetToDefault, T_SortGroupClause, T_TargetEntry, T_Var, and Query::targetList.

Referenced by assign_expr_collations(), and select_common_collation().

{
    assign_collations_context loccontext;
    Oid         collation;
    CollateStrength strength;
    int         location;

    /* Need do nothing for empty subexpressions */
    if (node == NULL)
        return false;

    /*
     * Prepare for recursion.  For most node types, though not all, the first
     * thing we do is recurse to process all nodes below this one. Each level
     * of the tree has its own local context.
     */
    loccontext.pstate = context->pstate;
    loccontext.collation = InvalidOid;
    loccontext.strength = COLLATE_NONE;
    loccontext.location = -1;

    /*
     * Recurse if appropriate, then determine the collation for this node.
     *
     * Note: the general cases are at the bottom of the switch, after various
     * special cases.
     */
    switch (nodeTag(node))
    {
        case T_CollateExpr:
            {
                /*
                 * COLLATE sets an explicitly derived collation, regardless of
                 * what the child state is.  But we must recurse to set up
                 * collation info below here.
                 */
                CollateExpr *expr = (CollateExpr *) node;

                (void) expression_tree_walker(node,
                                              assign_collations_walker,
                                              (void *) &loccontext);

                collation = expr->collOid;
                Assert(OidIsValid(collation));
                strength = COLLATE_EXPLICIT;
                location = expr->location;
            }
            break;
        case T_FieldSelect:
            {
                /*
                 * For FieldSelect, the result has the field's declared
                 * collation, independently of what happened in the arguments.
                 * (The immediate argument must be composite and thus not
                 * collatable, anyhow.)  The field's collation was already
                 * looked up and saved in the node.
                 */
                FieldSelect *expr = (FieldSelect *) node;

                /* ... but first, recurse */
                (void) expression_tree_walker(node,
                                              assign_collations_walker,
                                              (void *) &loccontext);

                if (OidIsValid(expr->resultcollid))
                {
                    /* Node's result type is collatable. */
                    /* Pass up field's collation as an implicit choice. */
                    collation = expr->resultcollid;
                    strength = COLLATE_IMPLICIT;
                    location = exprLocation(node);
                }
                else
                {
                    /* Node's result type isn't collatable. */
                    collation = InvalidOid;
                    strength = COLLATE_NONE;
                    location = -1;      /* won't be used */
                }
            }
            break;
        case T_RowExpr:
            {
                /*
                 * RowExpr is a special case because the subexpressions are
                 * independent: we don't want to complain if some of them have
                 * incompatible explicit collations.
                 */
                RowExpr    *expr = (RowExpr *) node;

                assign_list_collations(context->pstate, expr->args);

                /*
                 * Since the result is always composite and therefore never
                 * has a collation, we can just stop here: this node has no
                 * impact on the collation of its parent.
                 */
                return false;   /* done */
            }
        case T_RowCompareExpr:
            {
                /*
                 * For RowCompare, we have to find the common collation of
                 * each pair of input columns and build a list.  If we can't
                 * find a common collation, we just put InvalidOid into the
                 * list, which may or may not cause an error at runtime.
                 */
                RowCompareExpr *expr = (RowCompareExpr *) node;
                List       *colls = NIL;
                ListCell   *l;
                ListCell   *r;

                forboth(l, expr->largs, r, expr->rargs)
                {
                    Node       *le = (Node *) lfirst(l);
                    Node       *re = (Node *) lfirst(r);
                    Oid         coll;

                    coll = select_common_collation(context->pstate,
                                                   list_make2(le, re),
                                                   true);
                    colls = lappend_oid(colls, coll);
                }
                expr->inputcollids = colls;

                /*
                 * Since the result is always boolean and therefore never has
                 * a collation, we can just stop here: this node has no impact
                 * on the collation of its parent.
                 */
                return false;   /* done */
            }
        case T_CoerceToDomain:
            {
                /*
                 * If the domain declaration included a non-default COLLATE
                 * spec, then use that collation as the output collation of
                 * the coercion.  Otherwise allow the input collation to
                 * bubble up.  (The input should be of the domain's base type,
                 * therefore we don't need to worry about it not being
                 * collatable when the domain is.)
                 */
                CoerceToDomain *expr = (CoerceToDomain *) node;
                Oid         typcollation = get_typcollation(expr->resulttype);

                /* ... but first, recurse */
                (void) expression_tree_walker(node,
                                              assign_collations_walker,
                                              (void *) &loccontext);

                if (OidIsValid(typcollation))
                {
                    /* Node's result type is collatable. */
                    if (typcollation == DEFAULT_COLLATION_OID)
                    {
                        /* Collation state bubbles up from child. */
                        collation = loccontext.collation;
                        strength = loccontext.strength;
                        location = loccontext.location;
                    }
                    else
                    {
                        /* Use domain's collation as an implicit choice. */
                        collation = typcollation;
                        strength = COLLATE_IMPLICIT;
                        location = exprLocation(node);
                    }
                }
                else
                {
                    /* Node's result type isn't collatable. */
                    collation = InvalidOid;
                    strength = COLLATE_NONE;
                    location = -1;      /* won't be used */
                }

                /*
                 * Save the state into the expression node.  We know it
                 * doesn't care about input collation.
                 */
                if (strength == COLLATE_CONFLICT)
                    exprSetCollation(node, InvalidOid);
                else
                    exprSetCollation(node, collation);
            }
            break;
        case T_TargetEntry:
            (void) expression_tree_walker(node,
                                          assign_collations_walker,
                                          (void *) &loccontext);

            /*
             * TargetEntry can have only one child, and should bubble that
             * state up to its parent.  We can't use the general-case code
             * below because exprType and friends don't work on TargetEntry.
             */
            collation = loccontext.collation;
            strength = loccontext.strength;
            location = loccontext.location;

            /*
             * Throw error if the collation is indeterminate for a TargetEntry
             * that is a sort/group target.  We prefer to do this now, instead
             * of leaving the comparison functions to fail at runtime, because
             * we can give a syntax error pointer to help locate the problem.
             * There are some cases where there might not be a failure, for
             * example if the planner chooses to use hash aggregation instead
             * of sorting for grouping; but it seems better to predictably
             * throw an error.  (Compare transformSetOperationTree, which will
             * throw error for indeterminate collation of set-op columns, even
             * though the planner might be able to implement the set-op
             * without sorting.)
             */
            if (strength == COLLATE_CONFLICT &&
                ((TargetEntry *) node)->ressortgroupref != 0)
                ereport(ERROR,
                        (errcode(ERRCODE_COLLATION_MISMATCH),
                         errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"",
                                get_collation_name(loccontext.collation),
                                get_collation_name(loccontext.collation2)),
                         errhint("You can choose the collation by applying the COLLATE clause to one or both expressions."),
                         parser_errposition(context->pstate,
                                            loccontext.location2)));
            break;
        case T_RangeTblRef:
        case T_JoinExpr:
        case T_FromExpr:
        case T_SortGroupClause:
            (void) expression_tree_walker(node,
                                          assign_collations_walker,
                                          (void *) &loccontext);

            /*
             * When we're invoked on a query's jointree, we don't need to do
             * anything with join nodes except recurse through them to process
             * WHERE/ON expressions.  So just stop here.  Likewise, we don't
             * need to do anything when invoked on sort/group lists.
             */
            return false;
        case T_Query:
            {
                /*
                 * We get here when we're invoked on the Query belonging to a
                 * SubLink.  Act as though the Query returns its first output
                 * column, which indeed is what it does for EXPR_SUBLINK and
                 * ARRAY_SUBLINK cases.  In the cases where the SubLink
                 * returns boolean, this info will be ignored.
                 *
                 * We needn't recurse, since the Query is already processed.
                 */
                Query      *qtree = (Query *) node;
                TargetEntry *tent;

                tent = (TargetEntry *) linitial(qtree->targetList);
                Assert(IsA(tent, TargetEntry));
                Assert(!tent->resjunk);
                collation = exprCollation((Node *) tent->expr);
                /* collation doesn't change if it's converted to array */
                strength = COLLATE_IMPLICIT;
                location = exprLocation((Node *) tent->expr);
            }
            break;
        case T_List:
            (void) expression_tree_walker(node,
                                          assign_collations_walker,
                                          (void *) &loccontext);

            /*
             * When processing a list, collation state just bubbles up from
             * the list elements.
             */
            collation = loccontext.collation;
            strength = loccontext.strength;
            location = loccontext.location;
            break;

        case T_Var:
        case T_Const:
        case T_Param:
        case T_CoerceToDomainValue:
        case T_CaseTestExpr:
        case T_SetToDefault:
        case T_CurrentOfExpr:

            /*
             * General case for childless expression nodes.  These should
             * already have a collation assigned; it is not this function's
             * responsibility to look into the catalogs for base-case
             * information.
             */
            collation = exprCollation(node);

            /*
             * Note: in most cases, there will be an assigned collation
             * whenever type_is_collatable(exprType(node)); but an exception
             * occurs for a Var referencing a subquery output column for which
             * a unique collation was not determinable.  That may lead to a
             * runtime failure if a collation-sensitive function is applied to
             * the Var.
             */

            if (OidIsValid(collation))
                strength = COLLATE_IMPLICIT;
            else
                strength = COLLATE_NONE;
            location = exprLocation(node);
            break;

        default:
            {
                /*
                 * General case for most expression nodes with children. First
                 * recurse, then figure out what to assign to this node.
                 */
                Oid         typcollation;

                /*
                 * For most node types, we want to treat all the child
                 * expressions alike; but there are a few exceptions, hence
                 * this inner switch.
                 */
                switch (nodeTag(node))
                {
                    case T_Aggref:
                        {
                            /*
                             * Aggref is a special case because expressions
                             * used only for ordering shouldn't be taken to
                             * conflict with each other or with regular args.
                             * So we apply assign_expr_collations() to them
                             * rather than passing down our loccontext.
                             *
                             * Note that we recurse to each TargetEntry, not
                             * directly to its contained expression, so that
                             * the case above for T_TargetEntry will apply
                             * appropriate checks to agg ORDER BY items.
                             *
                             * We need not recurse into the aggorder or
                             * aggdistinct lists, because those contain only
                             * SortGroupClause nodes which we need not
                             * process.
                             */
                            Aggref     *aggref = (Aggref *) node;
                            ListCell   *lc;

                            foreach(lc, aggref->args)
                            {
                                TargetEntry *tle = (TargetEntry *) lfirst(lc);

                                Assert(IsA(tle, TargetEntry));
                                if (tle->resjunk)
                                    assign_expr_collations(context->pstate,
                                                           (Node *) tle);
                                else
                                    (void) assign_collations_walker((Node *) tle,
                                                                &loccontext);
                            }
                        }
                        break;
                    case T_CaseExpr:
                        {
                            /*
                             * CaseExpr is a special case because we do not
                             * want to recurse into the test expression (if
                             * any).  It was already marked with collations
                             * during transformCaseExpr, and furthermore its
                             * collation is not relevant to the result of the
                             * CASE --- only the output expressions are.
                             */
                            CaseExpr   *expr = (CaseExpr *) node;
                            ListCell   *lc;

                            foreach(lc, expr->args)
                            {
                                CaseWhen   *when = (CaseWhen *) lfirst(lc);

                                Assert(IsA(when, CaseWhen));

                                /*
                                 * The condition expressions mustn't affect
                                 * the CASE's result collation either; but
                                 * since they are known to yield boolean, it's
                                 * safe to recurse directly on them --- they
                                 * won't change loccontext.
                                 */
                                (void) assign_collations_walker((Node *) when->expr,
                                                                &loccontext);
                                (void) assign_collations_walker((Node *) when->result,
                                                                &loccontext);
                            }
                            (void) assign_collations_walker((Node *) expr->defresult,
                                                            &loccontext);
                        }
                        break;
                    default:

                        /*
                         * Normal case: all child expressions contribute
                         * equally to loccontext.
                         */
                        (void) expression_tree_walker(node,
                                                    assign_collations_walker,
                                                      (void *) &loccontext);
                        break;
                }

                /*
                 * Now figure out what collation to assign to this node.
                 */
                typcollation = get_typcollation(exprType(node));
                if (OidIsValid(typcollation))
                {
                    /* Node's result is collatable; what about its input? */
                    if (loccontext.strength > COLLATE_NONE)
                    {
                        /* Collation state bubbles up from children. */
                        collation = loccontext.collation;
                        strength = loccontext.strength;
                        location = loccontext.location;
                    }
                    else
                    {
                        /*
                         * Collatable output produced without any collatable
                         * input.  Use the type's collation (which is usually
                         * DEFAULT_COLLATION_OID, but might be different for a
                         * domain).
                         */
                        collation = typcollation;
                        strength = COLLATE_IMPLICIT;
                        location = exprLocation(node);
                    }
                }
                else
                {
                    /* Node's result type isn't collatable. */
                    collation = InvalidOid;
                    strength = COLLATE_NONE;
                    location = -1;      /* won't be used */
                }

                /*
                 * Save the result collation into the expression node. If the
                 * state is COLLATE_CONFLICT, we'll set the collation to
                 * InvalidOid, which might result in an error at runtime.
                 */
                if (strength == COLLATE_CONFLICT)
                    exprSetCollation(node, InvalidOid);
                else
                    exprSetCollation(node, collation);

                /*
                 * Likewise save the input collation, which is the one that
                 * any function called by this node should use.
                 */
                if (loccontext.strength == COLLATE_CONFLICT)
                    exprSetInputCollation(node, InvalidOid);
                else
                    exprSetInputCollation(node, loccontext.collation);
            }
            break;
    }

    /*
     * Now, merge my information into my parent's state.  If the collation
     * strength for this node is different from what's already in *context,
     * then this node either dominates or is dominated by earlier siblings.
     */
    if (strength > context->strength)
    {
        /* Override previous parent state */
        context->collation = collation;
        context->strength = strength;
        context->location = location;
        /* Bubble up error info if applicable */
        if (strength == COLLATE_CONFLICT)
        {
            context->collation2 = loccontext.collation2;
            context->location2 = loccontext.location2;
        }
    }
    else if (strength == context->strength)
    {
        /* Merge, or detect error if there's a collation conflict */
        switch (strength)
        {
            case COLLATE_NONE:
                /* Nothing + nothing is still nothing */
                break;
            case COLLATE_IMPLICIT:
                if (collation != context->collation)
                {
                    /*
                     * Non-default implicit collation always beats default.
                     */
                    if (context->collation == DEFAULT_COLLATION_OID)
                    {
                        /* Override previous parent state */
                        context->collation = collation;
                        context->strength = strength;
                        context->location = location;
                    }
                    else if (collation != DEFAULT_COLLATION_OID)
                    {
                        /*
                         * Ooops, we have a conflict.  We cannot throw error
                         * here, since the conflict could be resolved by a
                         * later sibling CollateExpr, or the parent might not
                         * care about collation anyway.  Return enough info to
                         * throw the error later, if needed.
                         */
                        context->strength = COLLATE_CONFLICT;
                        context->collation2 = collation;
                        context->location2 = location;
                    }
                }
                break;
            case COLLATE_CONFLICT:
                /* We're still conflicted ... */
                break;
            case COLLATE_EXPLICIT:
                if (collation != context->collation)
                {
                    /*
                     * Ooops, we have a conflict of explicit COLLATE clauses.
                     * Here we choose to throw error immediately; that is what
                     * the SQL standard says to do, and there's no good reason
                     * to be less strict.
                     */
                    ereport(ERROR,
                            (errcode(ERRCODE_COLLATION_MISMATCH),
                             errmsg("collation mismatch between explicit collations \"%s\" and \"%s\"",
                                    get_collation_name(context->collation),
                                    get_collation_name(collation)),
                             parser_errposition(context->pstate, location)));
                }
                break;
        }
    }

    return false;
}

void assign_expr_collations ( ParseState pstate,
Node expr 
)
void assign_list_collations ( ParseState pstate,
List exprs 
)

Definition at line 141 of file parse_collate.c.

References assign_expr_collations(), and lfirst.

Referenced by assign_collations_walker(), and assign_query_collations_walker().

{
    ListCell   *lc;

    foreach(lc, exprs)
    {
        Node       *node = (Node *) lfirst(lc);

        assign_expr_collations(pstate, node);
    }
}

void assign_query_collations ( ParseState pstate,
Query query 
)

Definition at line 87 of file parse_collate.c.

References assign_query_collations_walker(), QTW_IGNORE_RANGE_TABLE, and query_tree_walker().

Referenced by transformDeleteStmt(), transformSelectStmt(), transformSetOperationStmt(), transformUpdateStmt(), and transformValuesClause().

{
    /*
     * We just use query_tree_walker() to visit all the contained expressions.
     * We can skip the rangetable and CTE subqueries, though, since RTEs and
     * subqueries had better have been processed already (else Vars referring
     * to them would not get created with the right collation).
     */
    (void) query_tree_walker(query,
                             assign_query_collations_walker,
                             (void *) pstate,
                             QTW_IGNORE_RANGE_TABLE |
                             QTW_IGNORE_CTE_SUBQUERIES);
}

static bool assign_query_collations_walker ( Node node,
ParseState pstate 
) [static]

Definition at line 112 of file parse_collate.c.

References assign_expr_collations(), assign_list_collations(), IsA, and NULL.

Referenced by assign_query_collations().

{
    /* Need do nothing for empty subexpressions */
    if (node == NULL)
        return false;

    /*
     * We don't want to recurse into a set-operations tree; it's already been
     * fully processed in transformSetOperationStmt.
     */
    if (IsA(node, SetOperationStmt))
        return false;

    if (IsA(node, List))
        assign_list_collations(pstate, (List *) node);
    else
        assign_expr_collations(pstate, node);

    return false;
}

Oid select_common_collation ( ParseState pstate,
List exprs,
bool  none_ok 
)

Definition at line 194 of file parse_collate.c.

References assign_collations_walker(), COLLATE_CONFLICT, assign_collations_context::collation, assign_collations_context::collation2, ereport, errcode(), errhint(), errmsg(), ERROR, get_collation_name(), assign_collations_context::location, assign_collations_context::location2, parser_errposition(), assign_collations_context::pstate, and assign_collations_context::strength.

Referenced by assign_collations_walker(), transformSetOperationTree(), and transformValuesClause().

{
    assign_collations_context context;

    /* initialize context for tree walk */
    context.pstate = pstate;
    context.collation = InvalidOid;
    context.strength = COLLATE_NONE;
    context.location = -1;

    /* and away we go */
    (void) assign_collations_walker((Node *) exprs, &context);

    /* deal with collation conflict */
    if (context.strength == COLLATE_CONFLICT)
    {
        if (none_ok)
            return InvalidOid;
        ereport(ERROR,
                (errcode(ERRCODE_COLLATION_MISMATCH),
                 errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"",
                        get_collation_name(context.collation),
                        get_collation_name(context.collation2)),
                 errhint("You can choose the collation by applying the COLLATE clause to one or both expressions."),
                 parser_errposition(context.pstate, context.location2)));
    }

    /*
     * Note: if strength is still COLLATE_NONE, we'll return InvalidOid, but
     * that's okay because it must mean none of the expressions returned
     * collatable datatypes.
     */
    return context.collation;
}