Header And Logo

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

Functions

rewriteHandler.h File Reference

#include "utils/relcache.h"
#include "nodes/parsenodes.h"
Include dependency graph for rewriteHandler.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

ListQueryRewrite (Query *parsetree)
void AcquireRewriteLocks (Query *parsetree, bool forUpdatePushedDown)
Nodebuild_column_default (Relation rel, int attrno)
bool relation_is_updatable (Oid reloid, int req_events)

Function Documentation

void AcquireRewriteLocks ( Query parsetree,
bool  forUpdatePushedDown 
)

Definition at line 103 of file rewriteHandler.c.

References acquireLocksOnSubLinks(), AcquireRewriteLocks(), Assert, Query::cteList, CommonTableExpr::ctequery, elog, ERROR, get_parse_rowmark(), get_rte_attribute_is_dropped(), Query::hasSubLinks, heap_close, heap_open(), INT4OID, InvalidOid, IsA, RangeTblEntry::joinaliasvars, lappend(), lfirst, makeNullConst(), NoLock, NULL, QTW_IGNORE_RC_SUBQUERIES, query_tree_walker(), RelationData::rd_rel, RangeTblEntry::relid, RangeTblEntry::relkind, Query::resultRelation, RowShareLock, rt_fetch, Query::rtable, RTE_JOIN, RTE_RELATION, RTE_SUBQUERY, RangeTblEntry::rtekind, RangeTblEntry::subquery, Var::varattno, Var::varlevelsup, and Var::varno.

Referenced by acquireLocksOnSubLinks(), AcquireRewriteLocks(), ApplyRetrieveRule(), get_query_def(), make_ruledef(), and rewriteRuleAction().

{
    ListCell   *l;
    int         rt_index;

    /*
     * First, process RTEs of the current query level.
     */
    rt_index = 0;
    foreach(l, parsetree->rtable)
    {
        RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
        Relation    rel;
        LOCKMODE    lockmode;
        List       *newaliasvars;
        Index       curinputvarno;
        RangeTblEntry *curinputrte;
        ListCell   *ll;

        ++rt_index;
        switch (rte->rtekind)
        {
            case RTE_RELATION:

                /*
                 * Grab the appropriate lock type for the relation, and do not
                 * release it until end of transaction. This protects the
                 * rewriter and planner against schema changes mid-query.
                 *
                 * If the relation is the query's result relation, then we
                 * need RowExclusiveLock.  Otherwise, check to see if the
                 * relation is accessed FOR [KEY] UPDATE/SHARE or not.  We can't
                 * just grab AccessShareLock because then the executor would
                 * be trying to upgrade the lock, leading to possible
                 * deadlocks.
                 */
                if (rt_index == parsetree->resultRelation)
                    lockmode = RowExclusiveLock;
                else if (forUpdatePushedDown ||
                         get_parse_rowmark(parsetree, rt_index) != NULL)
                    lockmode = RowShareLock;
                else
                    lockmode = AccessShareLock;

                rel = heap_open(rte->relid, lockmode);

                /*
                 * While we have the relation open, update the RTE's relkind,
                 * just in case it changed since this rule was made.
                 */
                rte->relkind = rel->rd_rel->relkind;

                heap_close(rel, NoLock);
                break;

            case RTE_JOIN:

                /*
                 * Scan the join's alias var list to see if any columns have
                 * been dropped, and if so replace those Vars with NULL
                 * Consts.
                 *
                 * Since a join has only two inputs, we can expect to see
                 * multiple references to the same input RTE; optimize away
                 * multiple fetches.
                 */
                newaliasvars = NIL;
                curinputvarno = 0;
                curinputrte = NULL;
                foreach(ll, rte->joinaliasvars)
                {
                    Var        *aliasvar = (Var *) lfirst(ll);

                    /*
                     * If the list item isn't a simple Var, then it must
                     * represent a merged column, ie a USING column, and so it
                     * couldn't possibly be dropped, since it's referenced in
                     * the join clause.  (Conceivably it could also be a NULL
                     * constant already?  But that's OK too.)
                     */
                    if (IsA(aliasvar, Var))
                    {
                        /*
                         * The elements of an alias list have to refer to
                         * earlier RTEs of the same rtable, because that's the
                         * order the planner builds things in.  So we already
                         * processed the referenced RTE, and so it's safe to
                         * use get_rte_attribute_is_dropped on it. (This might
                         * not hold after rewriting or planning, but it's OK
                         * to assume here.)
                         */
                        Assert(aliasvar->varlevelsup == 0);
                        if (aliasvar->varno != curinputvarno)
                        {
                            curinputvarno = aliasvar->varno;
                            if (curinputvarno >= rt_index)
                                elog(ERROR, "unexpected varno %d in JOIN RTE %d",
                                     curinputvarno, rt_index);
                            curinputrte = rt_fetch(curinputvarno,
                                                   parsetree->rtable);
                        }
                        if (get_rte_attribute_is_dropped(curinputrte,
                                                         aliasvar->varattno))
                        {
                            /*
                             * can't use vartype here, since that might be a
                             * now-dropped type OID, but it doesn't really
                             * matter what type the Const claims to be.
                             */
                            aliasvar = (Var *) makeNullConst(INT4OID, -1, InvalidOid);
                        }
                    }
                    newaliasvars = lappend(newaliasvars, aliasvar);
                }
                rte->joinaliasvars = newaliasvars;
                break;

            case RTE_SUBQUERY:

                /*
                 * The subquery RTE itself is all right, but we have to
                 * recurse to process the represented subquery.
                 */
                AcquireRewriteLocks(rte->subquery,
                                    (forUpdatePushedDown ||
                            get_parse_rowmark(parsetree, rt_index) != NULL));
                break;

            default:
                /* ignore other types of RTEs */
                break;
        }
    }

    /* Recurse into subqueries in WITH */
    foreach(l, parsetree->cteList)
    {
        CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);

        AcquireRewriteLocks((Query *) cte->ctequery, false);
    }

    /*
     * Recurse into sublink subqueries, too.  But we already did the ones in
     * the rtable and cteList.
     */
    if (parsetree->hasSubLinks)
        query_tree_walker(parsetree, acquireLocksOnSubLinks, NULL,
                          QTW_IGNORE_RC_SUBQUERIES);
}

Node* build_column_default ( Relation  rel,
int  attrno 
)

Definition at line 974 of file rewriteHandler.c.

References tupleDesc::attrs, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, tupleDesc::constr, tupleConstr::defval, ereport, errcode(), errhint(), errmsg(), ERROR, exprType(), format_type_be(), get_typdefault(), NameStr, NULL, tupleConstr::num_defval, RelationData::rd_att, and stringToNode().

Referenced by ATExecAddColumn(), ATExecAlterColumnType(), BeginCopyFrom(), rewriteTargetListIU(), and rewriteValuesRTE().

{
    TupleDesc   rd_att = rel->rd_att;
    Form_pg_attribute att_tup = rd_att->attrs[attrno - 1];
    Oid         atttype = att_tup->atttypid;
    int32       atttypmod = att_tup->atttypmod;
    Node       *expr = NULL;
    Oid         exprtype;

    /*
     * Scan to see if relation has a default for this column.
     */
    if (rd_att->constr && rd_att->constr->num_defval > 0)
    {
        AttrDefault *defval = rd_att->constr->defval;
        int         ndef = rd_att->constr->num_defval;

        while (--ndef >= 0)
        {
            if (attrno == defval[ndef].adnum)
            {
                /*
                 * Found it, convert string representation to node tree.
                 */
                expr = stringToNode(defval[ndef].adbin);
                break;
            }
        }
    }

    if (expr == NULL)
    {
        /*
         * No per-column default, so look for a default for the type itself.
         */
        expr = get_typdefault(atttype);
    }

    if (expr == NULL)
        return NULL;            /* No default anywhere */

    /*
     * Make sure the value is coerced to the target column type; this will
     * generally be true already, but there seem to be some corner cases
     * involving domain defaults where it might not be true. This should match
     * the parser's processing of non-defaulted expressions --- see
     * transformAssignedExpr().
     */
    exprtype = exprType(expr);

    expr = coerce_to_target_type(NULL,  /* no UNKNOWN params here */
                                 expr, exprtype,
                                 atttype, atttypmod,
                                 COERCION_ASSIGNMENT,
                                 COERCE_IMPLICIT_CAST,
                                 -1);
    if (expr == NULL)
        ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
                 errmsg("column \"%s\" is of type %s"
                        " but default expression is of type %s",
                        NameStr(att_tup->attname),
                        format_type_be(atttype),
                        format_type_be(exprtype)),
               errhint("You will need to rewrite or cast the expression.")));

    return expr;
}

List* QueryRewrite ( Query parsetree  ) 

Definition at line 2854 of file rewriteHandler.c.

References Assert, Query::canSetTag, Query::commandType, fireRIRrules(), lappend(), lfirst, NIL, NULL, QSRC_INSTEAD_RULE, QSRC_ORIGINAL, QSRC_QUAL_INSTEAD_RULE, Query::queryId, Query::querySource, and RewriteQuery().

Referenced by ExecCreateTableAs(), ExplainOneUtility(), ExplainQuery(), pg_rewrite_query(), PrepareQuery(), and refresh_matview_datafill().

{
    uint32      input_query_id = parsetree->queryId;
    List       *querylist;
    List       *results;
    ListCell   *l;
    CmdType     origCmdType;
    bool        foundOriginalQuery;
    Query      *lastInstead;

    /*
     * This function is only applied to top-level original queries
     */
    Assert(parsetree->querySource == QSRC_ORIGINAL);
    Assert(parsetree->canSetTag);

    /*
     * Step 1
     *
     * Apply all non-SELECT rules possibly getting 0 or many queries
     */
    querylist = RewriteQuery(parsetree, NIL);

    /*
     * Step 2
     *
     * Apply all the RIR rules on each query
     *
     * This is also a handy place to mark each query with the original queryId
     */
    results = NIL;
    foreach(l, querylist)
    {
        Query      *query = (Query *) lfirst(l);

        query = fireRIRrules(query, NIL, false);

        query->queryId = input_query_id;

        results = lappend(results, query);
    }

    /*
     * Step 3
     *
     * Determine which, if any, of the resulting queries is supposed to set
     * the command-result tag; and update the canSetTag fields accordingly.
     *
     * If the original query is still in the list, it sets the command tag.
     * Otherwise, the last INSTEAD query of the same kind as the original is
     * allowed to set the tag.  (Note these rules can leave us with no query
     * setting the tag.  The tcop code has to cope with this by setting up a
     * default tag based on the original un-rewritten query.)
     *
     * The Asserts verify that at most one query in the result list is marked
     * canSetTag.  If we aren't checking asserts, we can fall out of the loop
     * as soon as we find the original query.
     */
    origCmdType = parsetree->commandType;
    foundOriginalQuery = false;
    lastInstead = NULL;

    foreach(l, results)
    {
        Query      *query = (Query *) lfirst(l);

        if (query->querySource == QSRC_ORIGINAL)
        {
            Assert(query->canSetTag);
            Assert(!foundOriginalQuery);
            foundOriginalQuery = true;
#ifndef USE_ASSERT_CHECKING
            break;
#endif
        }
        else
        {
            Assert(!query->canSetTag);
            if (query->commandType == origCmdType &&
                (query->querySource == QSRC_INSTEAD_RULE ||
                 query->querySource == QSRC_QUAL_INSTEAD_RULE))
                lastInstead = query;
        }
    }

    if (!foundOriginalQuery && lastInstead != NULL)
        lastInstead->canSetTag = true;

    return results;
}

bool relation_is_updatable ( Oid  reloid,
int  req_events 
)

Definition at line 2083 of file rewriteHandler.c.

References AccessShareLock, RewriteRule::event, FromExpr::fromlist, get_view_query(), i, RewriteRule::isInstead, Query::jointree, linitial, NULL, RuleLock::numLocks, RewriteRule::qual, RelationData::rd_rel, RelationData::rd_rules, relation_close(), relation_is_updatable(), RangeTblEntry::relid, RangeTblEntry::relkind, RELKIND_RELATION, RELKIND_VIEW, rt_fetch, Query::rtable, RangeTblRef::rtindex, RuleLock::rules, try_relation_open(), and view_is_auto_updatable().

Referenced by pg_view_is_insertable(), pg_view_is_updatable(), and relation_is_updatable().

{
    Relation    rel;
    RuleLock   *rulelocks;

    rel = try_relation_open(reloid, AccessShareLock);

    /*
     * If the relation doesn't exist, say "false" rather than throwing an
     * error.  This is helpful since scanning an information_schema view
     * under MVCC rules can result in referencing rels that were just
     * deleted according to a SnapshotNow probe.
     */
    if (rel == NULL)
        return false;

    /* Look for unconditional DO INSTEAD rules, and note supported events */
    rulelocks = rel->rd_rules;
    if (rulelocks != NULL)
    {
        int         events = 0;
        int         i;

        for (i = 0; i < rulelocks->numLocks; i++)
        {
            if (rulelocks->rules[i]->isInstead &&
                rulelocks->rules[i]->qual == NULL)
            {
                events |= 1 << rulelocks->rules[i]->event;
            }
        }

        /* If we have all rules needed, say "yes" */
        if ((events & req_events) == req_events)
        {
            relation_close(rel, AccessShareLock);
            return true;
        }
    }

    /* Check if this is an automatically updatable view */
    if (rel->rd_rel->relkind == RELKIND_VIEW &&
        view_is_auto_updatable(rel) == NULL)
    {
        Query      *viewquery;
        RangeTblRef *rtr;
        RangeTblEntry *base_rte;
        Oid         baseoid;

        /* The base relation must also be updatable */
        viewquery = get_view_query(rel);
        rtr = (RangeTblRef *) linitial(viewquery->jointree->fromlist);
        base_rte = rt_fetch(rtr->rtindex, viewquery->rtable);

        if (base_rte->relkind == RELKIND_RELATION)
        {
            /* Tables are always updatable */
            relation_close(rel, AccessShareLock);
            return true;
        }
        else
        {
            /* Do a recursive check for any other kind of base relation */
            baseoid = base_rte->relid;
            relation_close(rel, AccessShareLock);
            return relation_is_updatable(baseoid, req_events);
        }
    }

    /* If we reach here, the relation is not updatable */
    relation_close(rel, AccessShareLock);
    return false;
}