Header And Logo

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

Functions

parse_clause.h File Reference

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

Go to the source code of this file.

Functions

void transformFromClause (ParseState *pstate, List *frmList)
int setTargetTable (ParseState *pstate, RangeVar *relation, bool inh, bool alsoSource, AclMode requiredPerms)
bool interpretInhOption (InhOption inhOpt)
bool interpretOidsOption (List *defList, bool allowOids)
NodetransformWhereClause (ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
NodetransformLimitClause (ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
ListtransformGroupClause (ParseState *pstate, List *grouplist, List **targetlist, List *sortClause, ParseExprKind exprKind, bool useSQL99)
ListtransformSortClause (ParseState *pstate, List *orderlist, List **targetlist, ParseExprKind exprKind, bool resolveUnknown, bool useSQL99)
ListtransformWindowDefinitions (ParseState *pstate, List *windowdefs, List **targetlist)
ListtransformDistinctClause (ParseState *pstate, List **targetlist, List *sortClause, bool is_agg)
ListtransformDistinctOnClause (ParseState *pstate, List *distinctlist, List **targetlist, List *sortClause)
Index assignSortGroupRef (TargetEntry *tle, List *tlist)
bool targetIsInSortList (TargetEntry *tle, Oid sortop, List *sortList)

Function Documentation

Index assignSortGroupRef ( TargetEntry tle,
List tlist 
)

Definition at line 2226 of file parse_clause.c.

References lfirst, and TargetEntry::ressortgroupref.

Referenced by addTargetToGroupList(), addTargetToSortList(), build_minmax_path(), create_unique_plan(), and transformDistinctOnClause().

{
    Index       maxRef;
    ListCell   *l;

    if (tle->ressortgroupref)   /* already has one? */
        return tle->ressortgroupref;

    /* easiest way to pick an unused refnumber: max used + 1 */
    maxRef = 0;
    foreach(l, tlist)
    {
        Index       ref = ((TargetEntry *) lfirst(l))->ressortgroupref;

        if (ref > maxRef)
            maxRef = ref;
    }
    tle->ressortgroupref = maxRef + 1;
    return tle->ressortgroupref;
}

bool interpretInhOption ( InhOption  inhOpt  ) 

Definition at line 226 of file parse_clause.c.

References elog, ERROR, INH_DEFAULT, INH_NO, INH_YES, and SQL_inheritance.

Referenced by AlterTable(), ExecuteTruncate(), LockTableCommand(), renameatt(), RenameConstraint(), transformDeleteStmt(), transformTableEntry(), and transformUpdateStmt().

{
    switch (inhOpt)
    {
        case INH_NO:
            return false;
        case INH_YES:
            return true;
        case INH_DEFAULT:
            return SQL_inheritance;
    }
    elog(ERROR, "bogus InhOption value: %d", inhOpt);
    return false;               /* keep compiler quiet */
}

bool interpretOidsOption ( List defList,
bool  allowOids 
)

Definition at line 252 of file parse_clause.c.

References default_with_oids, defGetBoolean(), DefElem::defname, DefElem::defnamespace, ereport, errcode(), errmsg(), ERROR, lfirst, NULL, and pg_strcasecmp().

Referenced by DefineRelation(), GetIntoRelEFlags(), and transformCreateStmt().

{
    ListCell   *cell;

    /* Scan list to see if OIDS was included */
    foreach(cell, defList)
    {
        DefElem    *def = (DefElem *) lfirst(cell);

        if (def->defnamespace == NULL &&
            pg_strcasecmp(def->defname, "oids") == 0)
        {
            if (!allowOids)
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                         errmsg("unrecognized parameter \"%s\"",
                                def->defname)));
            return defGetBoolean(def);
        }
    }

    /* Force no-OIDS result if caller disallows OIDS. */
    if (!allowOids)
        return false;

    /* OIDS option was not specified, so use default. */
    return default_with_oids;
}

int setTargetTable ( ParseState pstate,
RangeVar relation,
bool  inh,
bool  alsoSource,
AclMode  requiredPerms 
)

Definition at line 166 of file parse_clause.c.

References addRangeTableEntryForRelation(), addRTEtoQuery(), RangeVar::alias, Assert, heap_close, list_length(), NoLock, NULL, ParseState::p_rtable, ParseState::p_target_rangetblentry, ParseState::p_target_relation, parserOpenTable(), RangeTblEntry::requiredPerms, RowExclusiveLock, and rt_fetch.

Referenced by transformDeleteStmt(), transformInsertStmt(), and transformUpdateStmt().

{
    RangeTblEntry *rte;
    int         rtindex;

    /* Close old target; this could only happen for multi-action rules */
    if (pstate->p_target_relation != NULL)
        heap_close(pstate->p_target_relation, NoLock);

    /*
     * Open target rel and grab suitable lock (which we will hold till end of
     * transaction).
     *
     * free_parsestate() will eventually do the corresponding heap_close(),
     * but *not* release the lock.
     */
    pstate->p_target_relation = parserOpenTable(pstate, relation,
                                                RowExclusiveLock);

    /*
     * Now build an RTE.
     */
    rte = addRangeTableEntryForRelation(pstate, pstate->p_target_relation,
                                        relation->alias, inh, false);
    pstate->p_target_rangetblentry = rte;

    /* assume new rte is at end */
    rtindex = list_length(pstate->p_rtable);
    Assert(rte == rt_fetch(rtindex, pstate->p_rtable));

    /*
     * Override addRangeTableEntry's default ACL_SELECT permissions check, and
     * instead mark target table as requiring exactly the specified
     * permissions.
     *
     * If we find an explicit reference to the rel later during parse
     * analysis, we will add the ACL_SELECT bit back again; see
     * markVarForSelectPriv and its callers.
     */
    rte->requiredPerms = requiredPerms;

    /*
     * If UPDATE/DELETE, add table to joinlist and namespace.
     */
    if (alsoSource)
        addRTEtoQuery(pstate, rte, true, true, true);

    return rtindex;
}

bool targetIsInSortList ( TargetEntry tle,
Oid  sortop,
List sortList 
)

Definition at line 2267 of file parse_clause.c.

References get_commutator(), InvalidOid, lfirst, TargetEntry::ressortgroupref, SortGroupClause::sortop, and SortGroupClause::tleSortGroupRef.

Referenced by addTargetToGroupList(), addTargetToSortList(), examine_simple_variable(), qual_is_pushdown_safe(), transformDistinctOnClause(), and transformGroupClause().

{
    Index       ref = tle->ressortgroupref;
    ListCell   *l;

    /* no need to scan list if tle has no marker */
    if (ref == 0)
        return false;

    foreach(l, sortList)
    {
        SortGroupClause *scl = (SortGroupClause *) lfirst(l);

        if (scl->tleSortGroupRef == ref &&
            (sortop == InvalidOid ||
             sortop == scl->sortop ||
             sortop == get_commutator(scl->sortop)))
            return true;
    }
    return false;
}

List* transformDistinctClause ( ParseState pstate,
List **  targetlist,
List sortClause,
bool  is_agg 
)

Definition at line 1819 of file parse_clause.c.

References addTargetToGroupList(), copyObject(), ereport, errcode(), errmsg(), ERROR, TargetEntry::expr, exprLocation(), get_sortgroupclause_tle(), lappend(), lfirst, parser_errposition(), and TargetEntry::resjunk.

Referenced by transformAggregateCall(), and transformSelectStmt().

{
    List       *result = NIL;
    ListCell   *slitem;
    ListCell   *tlitem;

    /*
     * The distinctClause should consist of all ORDER BY items followed by all
     * other non-resjunk targetlist items.  There must not be any resjunk
     * ORDER BY items --- that would imply that we are sorting by a value that
     * isn't necessarily unique within a DISTINCT group, so the results
     * wouldn't be well-defined.  This construction ensures we follow the rule
     * that sortClause and distinctClause match; in fact the sortClause will
     * always be a prefix of distinctClause.
     *
     * Note a corner case: the same TLE could be in the ORDER BY list multiple
     * times with different sortops.  We have to include it in the
     * distinctClause the same way to preserve the prefix property. The net
     * effect will be that the TLE value will be made unique according to both
     * sortops.
     */
    foreach(slitem, sortClause)
    {
        SortGroupClause *scl = (SortGroupClause *) lfirst(slitem);
        TargetEntry *tle = get_sortgroupclause_tle(scl, *targetlist);

        if (tle->resjunk)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                     is_agg ?
                     errmsg("in an aggregate with DISTINCT, ORDER BY expressions must appear in argument list") :
                     errmsg("for SELECT DISTINCT, ORDER BY expressions must appear in select list"),
                     parser_errposition(pstate,
                                        exprLocation((Node *) tle->expr))));
        result = lappend(result, copyObject(scl));
    }

    /*
     * Now add any remaining non-resjunk tlist items, using default sort/group
     * semantics for their data types.
     */
    foreach(tlitem, *targetlist)
    {
        TargetEntry *tle = (TargetEntry *) lfirst(tlitem);

        if (tle->resjunk)
            continue;           /* ignore junk */
        result = addTargetToGroupList(pstate, tle,
                                      result, *targetlist,
                                      exprLocation((Node *) tle->expr),
                                      true);
    }

    return result;
}

List* transformDistinctOnClause ( ParseState pstate,
List distinctlist,
List **  targetlist,
List sortClause 
)

Definition at line 1890 of file parse_clause.c.

References addTargetToGroupList(), assignSortGroupRef(), copyObject(), ereport, errcode(), errmsg(), ERROR, EXPR_KIND_DISTINCT_ON, exprLocation(), findTargetlistEntrySQL92(), forboth, get_matching_location(), get_sortgroupref_tle(), InvalidOid, lappend(), lappend_int(), lfirst, lfirst_int, list_member_int(), parser_errposition(), targetIsInSortList(), and SortGroupClause::tleSortGroupRef.

Referenced by transformSelectStmt().

{
    List       *result = NIL;
    List       *sortgrouprefs = NIL;
    bool        skipped_sortitem;
    ListCell   *lc;
    ListCell   *lc2;

    /*
     * Add all the DISTINCT ON expressions to the tlist (if not already
     * present, they are added as resjunk items).  Assign sortgroupref numbers
     * to them, and make a list of these numbers.  (NB: we rely below on the
     * sortgrouprefs list being one-for-one with the original distinctlist.
     * Also notice that we could have duplicate DISTINCT ON expressions and
     * hence duplicate entries in sortgrouprefs.)
     */
    foreach(lc, distinctlist)
    {
        Node       *dexpr = (Node *) lfirst(lc);
        int         sortgroupref;
        TargetEntry *tle;

        tle = findTargetlistEntrySQL92(pstate, dexpr, targetlist,
                                       EXPR_KIND_DISTINCT_ON);
        sortgroupref = assignSortGroupRef(tle, *targetlist);
        sortgrouprefs = lappend_int(sortgrouprefs, sortgroupref);
    }

    /*
     * If the user writes both DISTINCT ON and ORDER BY, adopt the sorting
     * semantics from ORDER BY items that match DISTINCT ON items, and also
     * adopt their column sort order.  We insist that the distinctClause and
     * sortClause match, so throw error if we find the need to add any more
     * distinctClause items after we've skipped an ORDER BY item that wasn't
     * in DISTINCT ON.
     */
    skipped_sortitem = false;
    foreach(lc, sortClause)
    {
        SortGroupClause *scl = (SortGroupClause *) lfirst(lc);

        if (list_member_int(sortgrouprefs, scl->tleSortGroupRef))
        {
            if (skipped_sortitem)
                ereport(ERROR,
                        (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                         errmsg("SELECT DISTINCT ON expressions must match initial ORDER BY expressions"),
                         parser_errposition(pstate,
                                  get_matching_location(scl->tleSortGroupRef,
                                                        sortgrouprefs,
                                                        distinctlist))));
            else
                result = lappend(result, copyObject(scl));
        }
        else
            skipped_sortitem = true;
    }

    /*
     * Now add any remaining DISTINCT ON items, using default sort/group
     * semantics for their data types.  (Note: this is pretty questionable; if
     * the ORDER BY list doesn't include all the DISTINCT ON items and more
     * besides, you certainly aren't using DISTINCT ON in the intended way,
     * and you probably aren't going to get consistent results.  It might be
     * better to throw an error or warning here.  But historically we've
     * allowed it, so keep doing so.)
     */
    forboth(lc, distinctlist, lc2, sortgrouprefs)
    {
        Node       *dexpr = (Node *) lfirst(lc);
        int         sortgroupref = lfirst_int(lc2);
        TargetEntry *tle = get_sortgroupref_tle(sortgroupref, *targetlist);

        if (targetIsInSortList(tle, InvalidOid, result))
            continue;           /* already in list (with some semantics) */
        if (skipped_sortitem)
            ereport(ERROR,
                    (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
                     errmsg("SELECT DISTINCT ON expressions must match initial ORDER BY expressions"),
                     parser_errposition(pstate, exprLocation(dexpr))));
        result = addTargetToGroupList(pstate, tle,
                                      result, *targetlist,
                                      exprLocation(dexpr),
                                      true);
    }

    return result;
}

void transformFromClause ( ParseState pstate,
List frmList 
)

Definition at line 100 of file parse_clause.c.

References checkNameSpaceConflicts(), lappend(), lfirst, list_concat(), ParseState::p_joinlist, ParseState::p_namespace, setNamespaceLateralState(), and transformFromClauseItem().

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

{
    ListCell   *fl;

    /*
     * The grammar will have produced a list of RangeVars, RangeSubselects,
     * RangeFunctions, and/or JoinExprs. Transform each one (possibly adding
     * entries to the rtable), check for duplicate refnames, and then add it
     * to the joinlist and namespace.
     *
     * Note we must process the items left-to-right for proper handling of
     * LATERAL references.
     */
    foreach(fl, frmList)
    {
        Node       *n = lfirst(fl);
        RangeTblEntry *rte;
        int         rtindex;
        List       *namespace;

        n = transformFromClauseItem(pstate, n,
                                    &rte,
                                    &rtindex,
                                    &namespace);

        checkNameSpaceConflicts(pstate, pstate->p_namespace, namespace);

        /* Mark the new namespace items as visible only to LATERAL */
        setNamespaceLateralState(namespace, true, true);

        pstate->p_joinlist = lappend(pstate->p_joinlist, n);
        pstate->p_namespace = list_concat(pstate->p_namespace, namespace);
    }

    /*
     * We're done parsing the FROM list, so make all namespace items
     * unconditionally visible.  Note that this will also reset lateral_only
     * for any namespace items that were already present when we were called;
     * but those should have been that way already.
     */
    setNamespaceLateralState(pstate->p_namespace, false, true);
}

List* transformGroupClause ( ParseState pstate,
List grouplist,
List **  targetlist,
List sortClause,
ParseExprKind  exprKind,
bool  useSQL99 
)

Definition at line 1557 of file parse_clause.c.

References addTargetToGroupList(), copyObject(), exprLocation(), findTargetlistEntrySQL92(), findTargetlistEntrySQL99(), InvalidOid, lappend(), lfirst, TargetEntry::ressortgroupref, targetIsInSortList(), and SortGroupClause::tleSortGroupRef.

Referenced by transformSelectStmt(), and transformWindowDefinitions().

{
    List       *result = NIL;
    ListCell   *gl;

    foreach(gl, grouplist)
    {
        Node       *gexpr = (Node *) lfirst(gl);
        TargetEntry *tle;
        bool        found = false;

        if (useSQL99)
            tle = findTargetlistEntrySQL99(pstate, gexpr,
                                           targetlist, exprKind);
        else
            tle = findTargetlistEntrySQL92(pstate, gexpr,
                                           targetlist, exprKind);

        /* Eliminate duplicates (GROUP BY x, x) */
        if (targetIsInSortList(tle, InvalidOid, result))
            continue;

        /*
         * If the GROUP BY tlist entry also appears in ORDER BY, copy operator
         * info from the (first) matching ORDER BY item.  This means that if
         * you write something like "GROUP BY foo ORDER BY foo USING <<<", the
         * GROUP BY operation silently takes on the equality semantics implied
         * by the ORDER BY.  There are two reasons to do this: it improves the
         * odds that we can implement both GROUP BY and ORDER BY with a single
         * sort step, and it allows the user to choose the equality semantics
         * used by GROUP BY, should she be working with a datatype that has
         * more than one equality operator.
         */
        if (tle->ressortgroupref > 0)
        {
            ListCell   *sl;

            foreach(sl, sortClause)
            {
                SortGroupClause *sc = (SortGroupClause *) lfirst(sl);

                if (sc->tleSortGroupRef == tle->ressortgroupref)
                {
                    result = lappend(result, copyObject(sc));
                    found = true;
                    break;
                }
            }
        }

        /*
         * If no match in ORDER BY, just add it to the result using default
         * sort/group semantics.
         */
        if (!found)
            result = addTargetToGroupList(pstate, tle,
                                          result, *targetlist,
                                          exprLocation(gexpr),
                                          true);
    }

    return result;
}

Node* transformLimitClause ( ParseState pstate,
Node clause,
ParseExprKind  exprKind,
const char *  constructName 
)

Definition at line 1218 of file parse_clause.c.

References checkExprIsVarFree(), coerce_to_specific_type(), INT8OID, NULL, and transformExpr().

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

{
    Node       *qual;

    if (clause == NULL)
        return NULL;

    qual = transformExpr(pstate, clause, exprKind);

    qual = coerce_to_specific_type(pstate, qual, INT8OID, constructName);

    /* LIMIT can't refer to any variables of the current query */
    checkExprIsVarFree(pstate, qual, constructName);

    return qual;
}

List* transformSortClause ( ParseState pstate,
List orderlist,
List **  targetlist,
ParseExprKind  exprKind,
bool  resolveUnknown,
bool  useSQL99 
)

Definition at line 1634 of file parse_clause.c.

References addTargetToSortList(), findTargetlistEntrySQL92(), findTargetlistEntrySQL99(), lfirst, and SortBy::node.

Referenced by transformAggregateCall(), transformSelectStmt(), transformSetOperationStmt(), transformValuesClause(), and transformWindowDefinitions().

{
    List       *sortlist = NIL;
    ListCell   *olitem;

    foreach(olitem, orderlist)
    {
        SortBy     *sortby = (SortBy *) lfirst(olitem);
        TargetEntry *tle;

        if (useSQL99)
            tle = findTargetlistEntrySQL99(pstate, sortby->node,
                                           targetlist, exprKind);
        else
            tle = findTargetlistEntrySQL92(pstate, sortby->node,
                                           targetlist, exprKind);

        sortlist = addTargetToSortList(pstate, tle,
                                       sortlist, *targetlist, sortby,
                                       resolveUnknown);
    }

    return sortlist;
}

Node* transformWhereClause ( ParseState pstate,
Node clause,
ParseExprKind  exprKind,
const char *  constructName 
)

Definition at line 1191 of file parse_clause.c.

References coerce_to_boolean(), NULL, and transformExpr().

Referenced by CreateTrigger(), transformDeleteStmt(), transformIndexStmt(), transformJoinOnClause(), transformRuleStmt(), transformSelectStmt(), and transformUpdateStmt().

{
    Node       *qual;

    if (clause == NULL)
        return NULL;

    qual = transformExpr(pstate, clause, exprKind);

    qual = coerce_to_boolean(pstate, qual, constructName);

    return qual;
}

List* transformWindowDefinitions ( ParseState pstate,
List windowdefs,
List **  targetlist 
)

Definition at line 1669 of file parse_clause.c.

References WindowClause::copiedOrder, copyObject(), WindowDef::endOffset, WindowClause::endOffset, ereport, errcode(), errmsg(), ERROR, EXPR_KIND_WINDOW_ORDER, EXPR_KIND_WINDOW_PARTITION, findWindowClause(), FRAMEOPTION_DEFAULTS, WindowDef::frameOptions, WindowClause::frameOptions, lappend(), lfirst, WindowDef::location, makeNode, WindowClause::name, WindowDef::name, NULL, WindowClause::orderClause, WindowDef::orderClause, parser_errposition(), WindowClause::partitionClause, WindowDef::partitionClause, WindowClause::refname, WindowDef::refname, WindowDef::startOffset, WindowClause::startOffset, transformFrameOffset(), transformGroupClause(), transformSortClause(), and WindowClause::winref.

Referenced by transformSelectStmt().

{
    List       *result = NIL;
    Index       winref = 0;
    ListCell   *lc;

    foreach(lc, windowdefs)
    {
        WindowDef  *windef = (WindowDef *) lfirst(lc);
        WindowClause *refwc = NULL;
        List       *partitionClause;
        List       *orderClause;
        WindowClause *wc;

        winref++;

        /*
         * Check for duplicate window names.
         */
        if (windef->name &&
            findWindowClause(result, windef->name) != NULL)
            ereport(ERROR,
                    (errcode(ERRCODE_WINDOWING_ERROR),
                     errmsg("window \"%s\" is already defined", windef->name),
                     parser_errposition(pstate, windef->location)));

        /*
         * If it references a previous window, look that up.
         */
        if (windef->refname)
        {
            refwc = findWindowClause(result, windef->refname);
            if (refwc == NULL)
                ereport(ERROR,
                        (errcode(ERRCODE_UNDEFINED_OBJECT),
                         errmsg("window \"%s\" does not exist",
                                windef->refname),
                         parser_errposition(pstate, windef->location)));
        }

        /*
         * Transform PARTITION and ORDER specs, if any.  These are treated
         * almost exactly like top-level GROUP BY and ORDER BY clauses,
         * including the special handling of nondefault operator semantics.
         */
        orderClause = transformSortClause(pstate,
                                          windef->orderClause,
                                          targetlist,
                                          EXPR_KIND_WINDOW_ORDER,
                                          true /* fix unknowns */ ,
                                          true /* force SQL99 rules */ );
        partitionClause = transformGroupClause(pstate,
                                               windef->partitionClause,
                                               targetlist,
                                               orderClause,
                                               EXPR_KIND_WINDOW_PARTITION,
                                               true /* force SQL99 rules */ );

        /*
         * And prepare the new WindowClause.
         */
        wc = makeNode(WindowClause);
        wc->name = windef->name;
        wc->refname = windef->refname;

        /*
         * Per spec, a windowdef that references a previous one copies the
         * previous partition clause (and mustn't specify its own).  It can
         * specify its own ordering clause. but only if the previous one had
         * none.  It always specifies its own frame clause, and the previous
         * one must not have a frame clause.  (Yeah, it's bizarre that each of
         * these cases works differently, but SQL:2008 says so; see 7.11
         * <window clause> syntax rule 10 and general rule 1.)
         */
        if (refwc)
        {
            if (partitionClause)
                ereport(ERROR,
                        (errcode(ERRCODE_WINDOWING_ERROR),
                errmsg("cannot override PARTITION BY clause of window \"%s\"",
                       windef->refname),
                         parser_errposition(pstate, windef->location)));
            wc->partitionClause = copyObject(refwc->partitionClause);
        }
        else
            wc->partitionClause = partitionClause;
        if (refwc)
        {
            if (orderClause && refwc->orderClause)
                ereport(ERROR,
                        (errcode(ERRCODE_WINDOWING_ERROR),
                   errmsg("cannot override ORDER BY clause of window \"%s\"",
                          windef->refname),
                         parser_errposition(pstate, windef->location)));
            if (orderClause)
            {
                wc->orderClause = orderClause;
                wc->copiedOrder = false;
            }
            else
            {
                wc->orderClause = copyObject(refwc->orderClause);
                wc->copiedOrder = true;
            }
        }
        else
        {
            wc->orderClause = orderClause;
            wc->copiedOrder = false;
        }
        if (refwc && refwc->frameOptions != FRAMEOPTION_DEFAULTS)
            ereport(ERROR,
                    (errcode(ERRCODE_WINDOWING_ERROR),
                     errmsg("cannot override frame clause of window \"%s\"",
                            windef->refname),
                     parser_errposition(pstate, windef->location)));
        wc->frameOptions = windef->frameOptions;
        /* Process frame offset expressions */
        wc->startOffset = transformFrameOffset(pstate, wc->frameOptions,
                                               windef->startOffset);
        wc->endOffset = transformFrameOffset(pstate, wc->frameOptions,
                                             windef->endOffset);
        wc->winref = winref;

        result = lappend(result, wc);
    }

    return result;
}