Header And Logo

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

Functions

parse_cte.h File Reference

#include "parser/parse_node.h"
Include dependency graph for parse_cte.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

ListtransformWithClause (ParseState *pstate, WithClause *withClause)
void analyzeCTETargetList (ParseState *pstate, CommonTableExpr *cte, List *tlist)

Function Documentation

void analyzeCTETargetList ( ParseState pstate,
CommonTableExpr cte,
List tlist 
)

Definition at line 352 of file parse_cte.c.

References CommonTableExpr::aliascolnames, Assert, copyObject(), CommonTableExpr::ctecolcollations, CommonTableExpr::ctecolnames, CommonTableExpr::ctecoltypes, CommonTableExpr::ctecoltypmods, CommonTableExpr::ctename, CommonTableExpr::cterecursive, ereport, errcode(), errmsg(), ERROR, TargetEntry::expr, exprCollation(), exprType(), exprTypmod(), lappend(), lappend_int(), lappend_oid(), lfirst, list_length(), CommonTableExpr::location, makeString(), NIL, OidIsValid, parser_errposition(), pstrdup(), TargetEntry::resjunk, TargetEntry::resname, TargetEntry::resno, and UNKNOWNOID.

Referenced by analyzeCTE(), and determineRecursiveColTypes().

{
    int         numaliases;
    int         varattno;
    ListCell   *tlistitem;

    /* Not done already ... */
    Assert(cte->ctecolnames == NIL);

    /*
     * We need to determine column names, types, and collations.  The alias
     * column names override anything coming from the query itself.  (Note:
     * the SQL spec says that the alias list must be empty or exactly as long
     * as the output column set; but we allow it to be shorter for consistency
     * with Alias handling.)
     */
    cte->ctecolnames = copyObject(cte->aliascolnames);
    cte->ctecoltypes = cte->ctecoltypmods = cte->ctecolcollations = NIL;
    numaliases = list_length(cte->aliascolnames);
    varattno = 0;
    foreach(tlistitem, tlist)
    {
        TargetEntry *te = (TargetEntry *) lfirst(tlistitem);
        Oid         coltype;
        int32       coltypmod;
        Oid         colcoll;

        if (te->resjunk)
            continue;
        varattno++;
        Assert(varattno == te->resno);
        if (varattno > numaliases)
        {
            char       *attrname;

            attrname = pstrdup(te->resname);
            cte->ctecolnames = lappend(cte->ctecolnames, makeString(attrname));
        }
        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
         * "unknown" column to "text".  This corresponds to the fact that
         * SELECT 'foo' UNION SELECT 'bar' will ultimately produce text. We
         * might see "unknown" as a result of an untyped literal in the
         * non-recursive term's select list, and if we don't convert to text
         * then we'll have a mismatch against the UNION result.
         *
         * The column might contain 'foo' COLLATE "bar", so don't override
         * collation if it's already set.
         */
        if (cte->cterecursive && coltype == UNKNOWNOID)
        {
            coltype = TEXTOID;
            coltypmod = -1;     /* should be -1 already, but be sure */
            if (!OidIsValid(colcoll))
                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,
                (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                 errmsg("WITH query \"%s\" has %d columns available but %d columns specified",
                        cte->ctename, varattno, numaliases),
                 parser_errposition(pstate, cte->location)));
}

List* transformWithClause ( ParseState pstate,
WithClause withClause 
)

Definition at line 105 of file parse_cte.c.

References analyzeCTE(), Assert, checkWellFormedRecursion(), CteItem::cte, CommonTableExpr::ctename, CommonTableExpr::ctequery, CommonTableExpr::cterecursive, CommonTableExpr::cterefcount, WithClause::ctes, ereport, errcode(), errmsg(), ERROR, for_each_cell, i, CteItem::id, IsA, CteState::items, lappend(), lfirst, list_copy(), list_delete_first(), list_length(), lnext, CommonTableExpr::location, makeDependencyGraph(), NIL, CteState::numitems, ParseState::p_ctenamespace, ParseState::p_future_ctes, ParseState::p_hasModifyingCTE, palloc0(), parser_errposition(), CteState::pstate, and WithClause::recursive.

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

{
    ListCell   *lc;

    /* Only one WITH clause per query level */
    Assert(pstate->p_ctenamespace == NIL);
    Assert(pstate->p_future_ctes == NIL);

    /*
     * For either type of WITH, there must not be duplicate CTE names in the
     * list.  Check this right away so we needn't worry later.
     *
     * Also, tentatively mark each CTE as non-recursive, and initialize its
     * reference count to zero, and set pstate->p_hasModifyingCTE if needed.
     */
    foreach(lc, withClause->ctes)
    {
        CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
        ListCell   *rest;

        for_each_cell(rest, lnext(lc))
        {
            CommonTableExpr *cte2 = (CommonTableExpr *) lfirst(rest);

            if (strcmp(cte->ctename, cte2->ctename) == 0)
                ereport(ERROR,
                        (errcode(ERRCODE_DUPLICATE_ALIAS),
                    errmsg("WITH query name \"%s\" specified more than once",
                           cte2->ctename),
                         parser_errposition(pstate, cte2->location)));
        }

        cte->cterecursive = false;
        cte->cterefcount = 0;

        if (!IsA(cte->ctequery, SelectStmt))
        {
            /* must be a data-modifying statement */
            Assert(IsA(cte->ctequery, InsertStmt) ||
                   IsA(cte->ctequery, UpdateStmt) ||
                   IsA(cte->ctequery, DeleteStmt));

            pstate->p_hasModifyingCTE = true;
        }
    }

    if (withClause->recursive)
    {
        /*
         * For WITH RECURSIVE, we rearrange the list elements if needed to
         * eliminate forward references.  First, build a work array and set up
         * the data structure needed by the tree walkers.
         */
        CteState    cstate;
        int         i;

        cstate.pstate = pstate;
        cstate.numitems = list_length(withClause->ctes);
        cstate.items = (CteItem *) palloc0(cstate.numitems * sizeof(CteItem));
        i = 0;
        foreach(lc, withClause->ctes)
        {
            cstate.items[i].cte = (CommonTableExpr *) lfirst(lc);
            cstate.items[i].id = i;
            i++;
        }

        /*
         * Find all the dependencies and sort the CteItems into a safe
         * processing order.  Also, mark CTEs that contain self-references.
         */
        makeDependencyGraph(&cstate);

        /*
         * Check that recursive queries are well-formed.
         */
        checkWellFormedRecursion(&cstate);

        /*
         * Set up the ctenamespace for parse analysis.  Per spec, all the WITH
         * items are visible to all others, so stuff them all in before parse
         * analysis.  We build the list in safe processing order so that the
         * planner can process the queries in sequence.
         */
        for (i = 0; i < cstate.numitems; i++)
        {
            CommonTableExpr *cte = cstate.items[i].cte;

            pstate->p_ctenamespace = lappend(pstate->p_ctenamespace, cte);
        }

        /*
         * Do parse analysis in the order determined by the topological sort.
         */
        for (i = 0; i < cstate.numitems; i++)
        {
            CommonTableExpr *cte = cstate.items[i].cte;

            analyzeCTE(pstate, cte);
        }
    }
    else
    {
        /*
         * For non-recursive WITH, just analyze each CTE in sequence and then
         * add it to the ctenamespace.  This corresponds to the spec's
         * definition of the scope of each WITH name.  However, to allow error
         * reports to be aware of the possibility of an erroneous reference,
         * we maintain a list in p_future_ctes of the not-yet-visible CTEs.
         */
        pstate->p_future_ctes = list_copy(withClause->ctes);

        foreach(lc, withClause->ctes)
        {
            CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);

            analyzeCTE(pstate, cte);
            pstate->p_ctenamespace = lappend(pstate->p_ctenamespace, cte);
            pstate->p_future_ctes = list_delete_first(pstate->p_future_ctes);
        }
    }

    return pstate->p_ctenamespace;
}