Header And Logo

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

Functions

preptlist.c File Reference

#include "postgres.h"
#include "access/heapam.h"
#include "access/sysattr.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "optimizer/prep.h"
#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
#include "utils/rel.h"
Include dependency graph for preptlist.c:

Go to the source code of this file.

Functions

static Listexpand_targetlist (List *tlist, int command_type, Index result_relation, List *range_table)
Listpreprocess_targetlist (PlannerInfo *root, List *tlist)
PlanRowMarkget_plan_rowmark (List *rowmarks, Index rtindex)

Function Documentation

static List * expand_targetlist ( List tlist,
int  command_type,
Index  result_relation,
List range_table 
) [static]

Definition at line 197 of file preptlist.c.

References tupleDesc::attrs, CMD_INSERT, CMD_UPDATE, COERCE_IMPLICIT_CAST, coerce_to_domain(), elog, ERROR, flatCopyTargetEntry(), getrelid, heap_close, heap_open(), INT4OID, InvalidOid, lappend(), lfirst, list_head(), lnext, makeConst(), makeTargetEntry(), makeVar(), NameStr, NoLock, NULL, pstrdup(), RelationData::rd_att, RelationGetNumberOfAttributes, TargetEntry::resjunk, and TargetEntry::resno.

Referenced by preprocess_targetlist().

{
    List       *new_tlist = NIL;
    ListCell   *tlist_item;
    Relation    rel;
    int         attrno,
                numattrs;

    tlist_item = list_head(tlist);

    /*
     * The rewriter should have already ensured that the TLEs are in correct
     * order; but we have to insert TLEs for any missing attributes.
     *
     * Scan the tuple description in the relation's relcache entry to make
     * sure we have all the user attributes in the right order.  We assume
     * that the rewriter already acquired at least AccessShareLock on the
     * relation, so we need no lock here.
     */
    rel = heap_open(getrelid(result_relation, range_table), NoLock);

    numattrs = RelationGetNumberOfAttributes(rel);

    for (attrno = 1; attrno <= numattrs; attrno++)
    {
        Form_pg_attribute att_tup = rel->rd_att->attrs[attrno - 1];
        TargetEntry *new_tle = NULL;

        if (tlist_item != NULL)
        {
            TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);

            if (!old_tle->resjunk && old_tle->resno == attrno)
            {
                new_tle = old_tle;
                tlist_item = lnext(tlist_item);
            }
        }

        if (new_tle == NULL)
        {
            /*
             * Didn't find a matching tlist entry, so make one.
             *
             * For INSERT, generate a NULL constant.  (We assume the rewriter
             * would have inserted any available default value.) Also, if the
             * column isn't dropped, apply any domain constraints that might
             * exist --- this is to catch domain NOT NULL.
             *
             * For UPDATE, generate a Var reference to the existing value of
             * the attribute, so that it gets copied to the new tuple. But
             * generate a NULL for dropped columns (we want to drop any old
             * values).
             *
             * When generating a NULL constant for a dropped column, we label
             * it INT4 (any other guaranteed-to-exist datatype would do as
             * well). We can't label it with the dropped column's datatype
             * since that might not exist anymore.  It does not really matter
             * what we claim the type is, since NULL is NULL --- its
             * representation is datatype-independent.  This could perhaps
             * confuse code comparing the finished plan to the target
             * relation, however.
             */
            Oid         atttype = att_tup->atttypid;
            int32       atttypmod = att_tup->atttypmod;
            Oid         attcollation = att_tup->attcollation;
            Node       *new_expr;

            switch (command_type)
            {
                case CMD_INSERT:
                    if (!att_tup->attisdropped)
                    {
                        new_expr = (Node *) makeConst(atttype,
                                                      -1,
                                                      attcollation,
                                                      att_tup->attlen,
                                                      (Datum) 0,
                                                      true,     /* isnull */
                                                      att_tup->attbyval);
                        new_expr = coerce_to_domain(new_expr,
                                                    InvalidOid, -1,
                                                    atttype,
                                                    COERCE_IMPLICIT_CAST,
                                                    -1,
                                                    false,
                                                    false);
                    }
                    else
                    {
                        /* Insert NULL for dropped column */
                        new_expr = (Node *) makeConst(INT4OID,
                                                      -1,
                                                      InvalidOid,
                                                      sizeof(int32),
                                                      (Datum) 0,
                                                      true,     /* isnull */
                                                      true /* byval */ );
                    }
                    break;
                case CMD_UPDATE:
                    if (!att_tup->attisdropped)
                    {
                        new_expr = (Node *) makeVar(result_relation,
                                                    attrno,
                                                    atttype,
                                                    atttypmod,
                                                    attcollation,
                                                    0);
                    }
                    else
                    {
                        /* Insert NULL for dropped column */
                        new_expr = (Node *) makeConst(INT4OID,
                                                      -1,
                                                      InvalidOid,
                                                      sizeof(int32),
                                                      (Datum) 0,
                                                      true,     /* isnull */
                                                      true /* byval */ );
                    }
                    break;
                default:
                    elog(ERROR, "unrecognized command_type: %d",
                         (int) command_type);
                    new_expr = NULL;    /* keep compiler quiet */
                    break;
            }

            new_tle = makeTargetEntry((Expr *) new_expr,
                                      attrno,
                                      pstrdup(NameStr(att_tup->attname)),
                                      false);
        }

        new_tlist = lappend(new_tlist, new_tle);
    }

    /*
     * The remaining tlist entries should be resjunk; append them all to the
     * end of the new tlist, making sure they have resnos higher than the last
     * real attribute.  (Note: although the rewriter already did such
     * renumbering, we have to do it again here in case we are doing an UPDATE
     * in a table with dropped columns, or an inheritance child table with
     * extra columns.)
     */
    while (tlist_item)
    {
        TargetEntry *old_tle = (TargetEntry *) lfirst(tlist_item);

        if (!old_tle->resjunk)
            elog(ERROR, "targetlist is not sorted correctly");
        /* Get the resno right, but don't copy unnecessarily */
        if (old_tle->resno != attrno)
        {
            old_tle = flatCopyTargetEntry(old_tle);
            old_tle->resno = attrno;
        }
        new_tlist = lappend(new_tlist, old_tle);
        attrno++;
        tlist_item = lnext(tlist_item);
    }

    heap_close(rel, NoLock);

    return new_tlist;
}

PlanRowMark* get_plan_rowmark ( List rowmarks,
Index  rtindex 
)

Definition at line 373 of file preptlist.c.

References lfirst, and PlanRowMark::rti.

Referenced by AcquireExecutorLocks(), and expand_inherited_rtentry().

{
    ListCell   *l;

    foreach(l, rowmarks)
    {
        PlanRowMark *rc = (PlanRowMark *) lfirst(l);

        if (rc->rti == rtindex)
            return rc;
    }
    return NULL;
}

List* preprocess_targetlist ( PlannerInfo root,
List tlist 
)

Definition at line 50 of file preptlist.c.

References CMD_INSERT, CMD_UPDATE, Query::commandType, elog, ERROR, expand_targetlist(), InvalidOid, IsA, PlanRowMark::isParent, lappend(), lfirst, list_free(), list_length(), makeTargetEntry(), makeVar(), makeWholeRowVar(), PlanRowMark::markType, NULL, OIDOID, PlannerInfo::parse, parse(), PlanRowMark::prti, pstrdup(), pull_var_clause(), PVC_INCLUDE_PLACEHOLDERS, PVC_RECURSE_AGGREGATES, RangeTblEntry::relid, Query::resultRelation, Query::returningList, ROW_MARK_COPY, PlanRowMark::rowmarkId, PlannerInfo::rowMarks, rt_fetch, Query::rtable, PlanRowMark::rti, SelfItemPointerAttributeNumber, snprintf(), RangeTblEntry::subquery, TableOidAttributeNumber, TIDOID, tlist_member(), and Var::varno.

Referenced by grouping_planner(), and inheritance_planner().

{
    Query      *parse = root->parse;
    int         result_relation = parse->resultRelation;
    List       *range_table = parse->rtable;
    CmdType     command_type = parse->commandType;
    ListCell   *lc;

    /*
     * Sanity check: if there is a result relation, it'd better be a real
     * relation not a subquery.  Else parser or rewriter messed up.
     */
    if (result_relation)
    {
        RangeTblEntry *rte = rt_fetch(result_relation, range_table);

        if (rte->subquery != NULL || rte->relid == InvalidOid)
            elog(ERROR, "subquery cannot be result relation");
    }

    /*
     * for heap_form_tuple to work, the targetlist must match the exact order
     * of the attributes. We also need to fill in any missing attributes. -ay
     * 10/94
     */
    if (command_type == CMD_INSERT || command_type == CMD_UPDATE)
        tlist = expand_targetlist(tlist, command_type,
                                  result_relation, range_table);

    /*
     * Add necessary junk columns for rowmarked rels.  These values are needed
     * for locking of rels selected FOR UPDATE/SHARE, and to do EvalPlanQual
     * rechecking.  See comments for PlanRowMark in plannodes.h.
     */
    foreach(lc, root->rowMarks)
    {
        PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
        Var        *var;
        char        resname[32];
        TargetEntry *tle;

        /* child rels use the same junk attrs as their parents */
        if (rc->rti != rc->prti)
            continue;

        if (rc->markType != ROW_MARK_COPY)
        {
            /* It's a regular table, so fetch its TID */
            var = makeVar(rc->rti,
                          SelfItemPointerAttributeNumber,
                          TIDOID,
                          -1,
                          InvalidOid,
                          0);
            snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId);
            tle = makeTargetEntry((Expr *) var,
                                  list_length(tlist) + 1,
                                  pstrdup(resname),
                                  true);
            tlist = lappend(tlist, tle);

            /* if parent of inheritance tree, need the tableoid too */
            if (rc->isParent)
            {
                var = makeVar(rc->rti,
                              TableOidAttributeNumber,
                              OIDOID,
                              -1,
                              InvalidOid,
                              0);
                snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId);
                tle = makeTargetEntry((Expr *) var,
                                      list_length(tlist) + 1,
                                      pstrdup(resname),
                                      true);
                tlist = lappend(tlist, tle);
            }
        }
        else
        {
            /* Not a table, so we need the whole row as a junk var */
            var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
                                  rc->rti,
                                  0,
                                  false);
            snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
            tle = makeTargetEntry((Expr *) var,
                                  list_length(tlist) + 1,
                                  pstrdup(resname),
                                  true);
            tlist = lappend(tlist, tle);
        }
    }

    /*
     * If the query has a RETURNING list, add resjunk entries for any Vars
     * used in RETURNING that belong to other relations.  We need to do this
     * to make these Vars available for the RETURNING calculation.  Vars that
     * belong to the result rel don't need to be added, because they will be
     * made to refer to the actual heap tuple.
     */
    if (parse->returningList && list_length(parse->rtable) > 1)
    {
        List       *vars;
        ListCell   *l;

        vars = pull_var_clause((Node *) parse->returningList,
                               PVC_RECURSE_AGGREGATES,
                               PVC_INCLUDE_PLACEHOLDERS);
        foreach(l, vars)
        {
            Var        *var = (Var *) lfirst(l);
            TargetEntry *tle;

            if (IsA(var, Var) &&
                var->varno == result_relation)
                continue;       /* don't need it */

            if (tlist_member((Node *) var, tlist))
                continue;       /* already got it */

            tle = makeTargetEntry((Expr *) var,
                                  list_length(tlist) + 1,
                                  NULL,
                                  true);

            tlist = lappend(tlist, tle);
        }
        list_free(vars);
    }

    return tlist;
}