ForeignServer *server;
ForeignDataWrapper *wrapper;
List *options;
- ListCell *lc,
- *prev;
+ ListCell *lc;
/*
* Extract options from FDW objects. We ignore user mappings because
*/
*filename = NULL;
*is_program = false;
- prev = NULL;
foreach(lc, options)
{
DefElem *def = (DefElem *) lfirst(lc);
if (strcmp(def->defname, "filename") == 0)
{
*filename = defGetString(def);
- options = list_delete_cell(options, lc, prev);
+ options = foreach_delete_current(options, lc);
break;
}
else if (strcmp(def->defname, "program") == 0)
{
*filename = defGetString(def);
*is_program = true;
- options = list_delete_cell(options, lc, prev);
+ options = foreach_delete_current(options, lc);
break;
}
- prev = lc;
}
/*
{
regex_arc_t *arcs;
TrgmStateKey destKey;
- ListCell *cell,
- *prev,
- *next;
+ ListCell *cell;
int i,
arcsCount;
* redundancy. We can drop either old key(s) or the new key if we find
* redundancy.
*/
- prev = NULL;
- cell = list_head(state->enterKeys);
- while (cell)
+ foreach(cell, state->enterKeys)
{
TrgmStateKey *existingKey = (TrgmStateKey *) lfirst(cell);
- next = lnext(cell);
if (existingKey->nstate == key->nstate)
{
if (prefixContains(&existingKey->prefix, &key->prefix))
* The new key covers this old key. Remove the old key, it's
* no longer needed once we add this key to the list.
*/
- state->enterKeys = list_delete_cell(state->enterKeys,
- cell, prev);
+ state->enterKeys = foreach_delete_current(state->enterKeys,
+ cell);
}
- else
- prev = cell;
}
- else
- prev = cell;
- cell = next;
}
/* No redundancy, so add this key to the state's list */
{
deparseExpr(lfirst(lowlist_item), context);
appendStringInfoChar(buf, ':');
- lowlist_item = lnext(lowlist_item);
+ lowlist_item = lnext(node->reflowerindexpr, lowlist_item);
}
deparseExpr(lfirst(uplist_item), context);
appendStringInfoChar(buf, ']');
{
if (!first)
appendStringInfoString(buf, ", ");
- if (use_variadic && lnext(arg) == NULL)
+ if (use_variadic && lnext(node->args, arg) == NULL)
appendStringInfoString(buf, "VARIADIC ");
deparseExpr((Expr *) lfirst(arg), context);
first = false;
first = false;
/* Add VARIADIC */
- if (use_variadic && lnext(arg) == NULL)
+ if (use_variadic && lnext(node->args, arg) == NULL)
appendStringInfoString(buf, "VARIADIC ");
deparseExpr((Expr *) n, context);
SubTransactionId parentSubid, void *arg)
{
ListCell *cell;
- ListCell *prev;
- ListCell *next;
if (event == SUBXACT_EVENT_ABORT_SUB)
{
- prev = NULL;
- for (cell = list_head(client_label_pending); cell; cell = next)
+ foreach(cell, client_label_pending)
{
pending_label *plabel = lfirst(cell);
- next = lnext(cell);
-
if (plabel->subid == mySubid)
client_label_pending
- = list_delete_cell(client_label_pending, cell, prev);
- else
- prev = cell;
+ = foreach_delete_current(client_label_pending, cell);
}
}
}
sepgsql_avc_reclaim(void)
{
ListCell *cell;
- ListCell *next;
- ListCell *prev;
int index;
while (avc_num_caches >= avc_threshold - AVC_NUM_RECLAIM)
{
index = avc_lru_hint;
- prev = NULL;
- for (cell = list_head(avc_slots[index]); cell; cell = next)
+ foreach(cell, avc_slots[index])
{
avc_cache *cache = lfirst(cell);
- next = lnext(cell);
if (!cache->hot_cache)
{
avc_slots[index]
- = list_delete_cell(avc_slots[index], cell, prev);
+ = foreach_delete_current(avc_slots[index], cell);
pfree(cache->scontext);
pfree(cache->tcontext);
else
{
cache->hot_cache = false;
- prev = cell;
}
}
avc_lru_hint = (avc_lru_hint + 1) % AVC_NUM_SLOTS;
/* Do we have a non-resjunk tlist item? */
while (tlist_item &&
((TargetEntry *) lfirst(tlist_item))->resjunk)
- tlist_item = lnext(tlist_item);
+ tlist_item = lnext(targetlist, tlist_item);
if (tlist_item)
{
TargetEntry *tle = (TargetEntry *) lfirst(tlist_item);
resorigtbl = tle->resorigtbl;
resorigcol = tle->resorigcol;
- tlist_item = lnext(tlist_item);
+ tlist_item = lnext(targetlist, tlist_item);
}
else
{
prev = list_head(list);
for (;;)
{
- ListCell *curr = lnext(prev);
+ ListCell *curr = lnext(list, prev);
if (curr == NULL || datum < lfirst_oid(curr))
break; /* it belongs after 'prev', before 'curr' */
if (indexpr_item == NULL) /* shouldn't happen */
elog(ERROR, "too few entries in indexprs list");
indexkey = (Node *) lfirst(indexpr_item);
- indexpr_item = lnext(indexpr_item);
+ indexpr_item = lnext(indexInfo->ii_Expressions, indexpr_item);
/*
* Lookup the expression type in pg_type for the type length etc.
if (colnames_item == NULL) /* shouldn't happen */
elog(ERROR, "too few entries in colnames list");
namestrcpy(&to->attname, (const char *) lfirst(colnames_item));
- colnames_item = lnext(colnames_item);
+ colnames_item = lnext(indexColNames, colnames_item);
/*
* Check the opclass and index AM to see if either provides a keytype
iDatum = ExecEvalExprSwitchContext((ExprState *) lfirst(indexpr_item),
GetPerTupleExprContext(estate),
&isNull);
- indexpr_item = lnext(indexpr_item);
+ indexpr_item = lnext(indexInfo->ii_ExpressionsState, indexpr_item);
}
values[i] = iDatum;
isnull[i] = isNull;
if (path->addTemp)
{
if (lc && lfirst_oid(lc) == myTempNamespace)
- lc = lnext(lc);
+ lc = lnext(activeSearchPath, lc);
else
return false;
}
if (path->addCatalog)
{
if (lc && lfirst_oid(lc) == PG_CATALOG_NAMESPACE)
- lc = lnext(lc);
+ lc = lnext(activeSearchPath, lc);
else
return false;
}
foreach(lcp, path->schemas)
{
if (lc && lfirst_oid(lc) == lfirst_oid(lcp))
- lc = lnext(lc);
+ lc = lnext(activeSearchPath, lc);
else
return false;
}
/* Find all attributes referenced */
pull_varattnos(expr, 1, &expr_attrs);
- partexprs_item = lnext(partexprs_item);
+ partexprs_item = lnext(partexprs, partexprs_item);
if (bms_overlap(attnums, expr_attrs))
{
typedef struct SeenRelsEntry
{
Oid rel_id; /* relation oid */
- ListCell *numparents_cell; /* corresponding list cell */
+ int list_index; /* its position in output list(s) */
} SeenRelsEntry;
/*
* indirect children. We can use a single list as both the record of
* already-found rels and the agenda of rels yet to be scanned for more
* children. This is a bit tricky but works because the foreach() macro
- * doesn't fetch the next list element until the bottom of the loop.
+ * doesn't fetch the next list element until the bottom of the loop. Note
+ * that we can't keep pointers into the output lists; but an index is
+ * sufficient.
*/
rels_list = list_make1_oid(parentrelId);
rel_numparents = list_make1_int(0);
if (found)
{
/* if the rel is already there, bump number-of-parents counter */
- lfirst_int(hash_entry->numparents_cell)++;
+ ListCell *numparents_cell;
+
+ numparents_cell = list_nth_cell(rel_numparents,
+ hash_entry->list_index);
+ lfirst_int(numparents_cell)++;
}
else
{
/* if it's not there, add it. expect 1 parent, initially. */
+ hash_entry->list_index = list_length(rels_list);
rels_list = lappend_oid(rels_list, child_oid);
rel_numparents = lappend_int(rel_numparents, 1);
- hash_entry->numparents_cell = rel_numparents->tail;
}
}
}
Assert(list_length(oldDefaults) == oldproc->pronargdefaults);
/* new list can have more defaults than old, advance over 'em */
- newlc = list_head(parameterDefaults);
- for (i = list_length(parameterDefaults) - oldproc->pronargdefaults;
- i > 0;
- i--)
- newlc = lnext(newlc);
+ newlc = list_nth_cell(parameterDefaults,
+ list_length(parameterDefaults) -
+ oldproc->pronargdefaults);
foreach(oldlc, oldDefaults)
{
errhint("Use %s %s first.",
dropcmd,
format_procedure(oldproc->oid))));
- newlc = lnext(newlc);
+ newlc = lnext(parameterDefaults, newlc);
}
}
char *pubname = text_to_cstring(PG_GETARG_TEXT_PP(0));
Publication *publication;
List *tables;
- ListCell **lcp;
/* stuff done only on the first call of the function */
if (SRF_IS_FIRSTCALL())
tables = GetAllTablesPublicationRelations();
else
tables = GetPublicationRelations(publication->oid);
- lcp = (ListCell **) palloc(sizeof(ListCell *));
- *lcp = list_head(tables);
- funcctx->user_fctx = (void *) lcp;
+ funcctx->user_fctx = (void *) tables;
MemoryContextSwitchTo(oldcontext);
}
/* stuff done on every call of the function */
funcctx = SRF_PERCALL_SETUP();
- lcp = (ListCell **) funcctx->user_fctx;
+ tables = (List *) funcctx->user_fctx;
- while (*lcp != NULL)
+ if (funcctx->call_cntr < list_length(tables))
{
- Oid relid = lfirst_oid(*lcp);
+ Oid relid = list_nth_oid(tables, funcctx->call_cntr);
- *lcp = lnext(*lcp);
SRF_RETURN_NEXT(funcctx, ObjectIdGetDatum(relid));
}
if (indexpr_item == NULL) /* shouldn't happen */
elog(ERROR, "too few entries in indexprs list");
indexkey = (Node *) lfirst(indexpr_item);
- indexpr_item = lnext(indexpr_item);
+ indexpr_item = lnext(indexInfo->ii_Expressions,
+ indexpr_item);
thisdata->vacattrstats[tcnt] =
examine_attribute(Irel[ind], i + 1, indexkey);
if (thisdata->vacattrstats[tcnt] != NULL)
pg_listening_channels(PG_FUNCTION_ARGS)
{
FuncCallContext *funcctx;
- ListCell **lcp;
/* stuff done only on the first call of the function */
if (SRF_IS_FIRSTCALL())
/* switch to memory context appropriate for multiple function calls */
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
- /* allocate memory for user context */
- lcp = (ListCell **) palloc(sizeof(ListCell *));
- *lcp = list_head(listenChannels);
- funcctx->user_fctx = (void *) lcp;
-
MemoryContextSwitchTo(oldcontext);
}
/* stuff done on every call of the function */
funcctx = SRF_PERCALL_SETUP();
- lcp = (ListCell **) funcctx->user_fctx;
- while (*lcp != NULL)
+ if (funcctx->call_cntr < list_length(listenChannels))
{
- char *channel = (char *) lfirst(*lcp);
+ char *channel = (char *) list_nth(listenChannels,
+ funcctx->call_cntr);
- *lcp = lnext(*lcp);
SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(channel));
}
Exec_UnlistenCommit(const char *channel)
{
ListCell *q;
- ListCell *prev;
if (Trace_notify)
elog(DEBUG1, "Exec_UnlistenCommit(%s,%d)", channel, MyProcPid);
- prev = NULL;
foreach(q, listenChannels)
{
char *lchan = (char *) lfirst(q);
if (strcmp(lchan, channel) == 0)
{
- listenChannels = list_delete_cell(listenChannels, q, prev);
+ listenChannels = foreach_delete_current(listenChannels, q);
pfree(lchan);
break;
}
- prev = q;
}
/*
* database OID in order to fill the page. So every page is always used up to
* the last byte which simplifies reading the page later.
*
- * We are passed the list cell containing the next notification to write
- * and return the first still-unwritten cell back. Eventually we will return
- * NULL indicating all is done.
+ * We are passed the list cell (in pendingNotifies) containing the next
+ * notification to write and return the first still-unwritten cell back.
+ * Eventually we will return NULL indicating all is done.
*
* We are holding AsyncQueueLock already from the caller and grab AsyncCtlLock
* locally in this function.
if (offset + qe.length <= QUEUE_PAGESIZE)
{
/* OK, so advance nextNotify past this item */
- nextNotify = lnext(nextNotify);
+ nextNotify = lnext(pendingNotifies, nextNotify);
}
else
{
if (lc)
{
colname = strVal(lfirst(lc));
- lc = lnext(lc);
+ lc = lnext(into->colNames, lc);
}
else
colname = tle->resname;
if (lc)
{
colname = strVal(lfirst(lc));
- lc = lnext(lc);
+ lc = lnext(into->colNames, lc);
}
else
colname = NameStr(attribute->attname);
queryString, params, queryEnv);
/* Separate plans with an appropriate separator */
- if (lnext(l) != NULL)
+ if (lnext(rewritten, l) != NULL)
ExplainSeparatePlans(es);
}
}
{
DefElem *od = lfirst(optcell);
ListCell *cell;
- ListCell *prev = NULL;
/*
* Find the element in resultOptions. We need this for validation in
- * all cases. Also identify the previous element.
+ * all cases.
*/
foreach(cell, resultOptions)
{
if (strcmp(def->defname, od->defname) == 0)
break;
- else
- prev = cell;
}
/*
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("option \"%s\" not found",
od->defname)));
- resultOptions = list_delete_cell(resultOptions, cell, prev);
+ resultOptions = list_delete_cell(resultOptions, cell);
break;
case DEFELEM_SET:
indexInfo->ii_ExclusionOps[attn] = opid;
indexInfo->ii_ExclusionProcs[attn] = get_opcode(opid);
indexInfo->ii_ExclusionStrats[attn] = strat;
- nextExclOp = lnext(nextExclOp);
+ nextExclOp = lnext(exclusionOpNames, nextExclOp);
}
/*
/* No need for CommandCounterIncrement, as ExplainOnePlan did it */
/* Separate plans with an appropriate separator */
- if (lnext(p) != NULL)
+ if (lnext(plan_list, p) != NULL)
ExplainSeparatePlans(es);
}
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("no security label providers have been loaded")));
- if (lnext(list_head(label_provider_list)) != NULL)
+ if (list_length(label_provider_list) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("must specify provider when multiple security label providers have been loaded")));
MergeAttributes(List *schema, List *supers, char relpersistence,
bool is_partition, List **supconstr)
{
- ListCell *entry;
List *inhSchema = NIL;
List *constraints = NIL;
bool have_bogus_defaults = false;
int child_attno;
static Node bogus_marker = {0}; /* marks conflicting defaults */
List *saved_schema = NIL;
+ ListCell *entry;
/*
* Check for and reject tables with too many columns. We perform this
* Although we might consider merging such entries in the same way that we
* handle name conflicts for inherited attributes, it seems to make more
* sense to assume such conflicts are errors.
+ *
+ * We don't use foreach() here because we have two nested loops over the
+ * schema list, with possible element deletions in the inner one. If we
+ * used foreach_delete_current() it could only fix up the state of one of
+ * the loops, so it seems cleaner to use looping over list indexes for
+ * both loops. Note that any deletion will happen beyond where the outer
+ * loop is, so its index never needs adjustment.
*/
- foreach(entry, schema)
+ for (int coldefpos = 0; coldefpos < list_length(schema); coldefpos++)
{
- ColumnDef *coldef = lfirst(entry);
- ListCell *rest = lnext(entry);
- ListCell *prev = entry;
+ ColumnDef *coldef = list_nth_node(ColumnDef, schema, coldefpos);
if (!is_partition && coldef->typeName == NULL)
{
coldef->colname)));
}
- while (rest != NULL)
+ /* restpos scans all entries beyond coldef; incr is in loop body */
+ for (int restpos = coldefpos + 1; restpos < list_length(schema);)
{
- ColumnDef *restdef = lfirst(rest);
- ListCell *next = lnext(rest); /* need to save it in case we
- * delete it */
+ ColumnDef *restdef = list_nth_node(ColumnDef, schema, restpos);
if (strcmp(coldef->colname, restdef->colname) == 0)
{
coldef->cooked_default = restdef->cooked_default;
coldef->constraints = restdef->constraints;
coldef->is_from_type = false;
- schema = list_delete_cell(schema, rest, prev);
-
- /*
- * As two elements are merged and one is removed, we
- * should never finish with an empty list.
- */
- Assert(schema != NIL);
+ schema = list_delete_nth_cell(schema, restpos);
}
else
ereport(ERROR,
errmsg("column \"%s\" specified more than once",
coldef->colname)));
}
- prev = rest;
- rest = next;
+ else
+ restpos++;
}
}
* assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
*/
old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
- old_pfeqop_item = lnext(old_pfeqop_item);
+ old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
+ old_pfeqop_item);
}
if (old_check_ok)
{
AtEOXact_on_commit_actions(bool isCommit)
{
ListCell *cur_item;
- ListCell *prev_item;
-
- prev_item = NULL;
- cur_item = list_head(on_commits);
- while (cur_item != NULL)
+ foreach(cur_item, on_commits)
{
OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
oc->creating_subid != InvalidSubTransactionId)
{
/* cur_item must be removed */
- on_commits = list_delete_cell(on_commits, cur_item, prev_item);
+ on_commits = foreach_delete_current(on_commits, cur_item);
pfree(oc);
- if (prev_item)
- cur_item = lnext(prev_item);
- else
- cur_item = list_head(on_commits);
}
else
{
/* cur_item must be preserved */
oc->creating_subid = InvalidSubTransactionId;
oc->deleting_subid = InvalidSubTransactionId;
- prev_item = cur_item;
- cur_item = lnext(prev_item);
}
}
}
SubTransactionId parentSubid)
{
ListCell *cur_item;
- ListCell *prev_item;
- prev_item = NULL;
- cur_item = list_head(on_commits);
-
- while (cur_item != NULL)
+ foreach(cur_item, on_commits)
{
OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
if (!isCommit && oc->creating_subid == mySubid)
{
/* cur_item must be removed */
- on_commits = list_delete_cell(on_commits, cur_item, prev_item);
+ on_commits = foreach_delete_current(on_commits, cur_item);
pfree(oc);
- if (prev_item)
- cur_item = lnext(prev_item);
- else
- cur_item = list_head(on_commits);
}
else
{
oc->creating_subid = parentSubid;
if (oc->deleting_subid == mySubid)
oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
- prev_item = cur_item;
- cur_item = lnext(prev_item);
}
}
}
{
DefElem *defel = (DefElem *) lfirst(pl);
ListCell *cell;
- ListCell *prev;
- ListCell *next;
/*
* Remove any matches ...
*/
- prev = NULL;
- for (cell = list_head(dictoptions); cell; cell = next)
+ foreach(cell, dictoptions)
{
DefElem *oldel = (DefElem *) lfirst(cell);
- next = lnext(cell);
if (strcmp(oldel->defname, defel->defname) == 0)
- dictoptions = list_delete_cell(dictoptions, cell, prev);
- else
- prev = cell;
+ dictoptions = foreach_delete_current(dictoptions, cell);
}
/*
appendStringInfoChar(&buf, ch);
}
appendStringInfoChar(&buf, '\'');
- if (lnext(l) != NULL)
+ if (lnext(deflist, l) != NULL)
appendStringInfoString(&buf, ", ");
}
if (te->resjunk)
continue;
te->resname = pstrdup(strVal(lfirst(alist_item)));
- alist_item = lnext(alist_item);
+ alist_item = lnext(stmt->aliases, alist_item);
if (alist_item == NULL)
break; /* done assigning aliases */
}
{
TargetEntry *tle = lfirst(t);
- t = lnext(t);
+ t = lnext(targetList, t);
if (!tle->resjunk)
{
cleanMap[i] = tle->resno;
datum = ExecEvalExprSwitchContext((ExprState *) lfirst(partexpr_item),
GetPerTupleExprContext(estate),
&isNull);
- partexpr_item = lnext(partexpr_item);
+ partexpr_item = lnext(pd->keystate, partexpr_item);
}
values[i] = datum;
isnull[i] = isNull;
var->vartypmod != -1))
return false; /* type mismatch */
- tlist_item = lnext(tlist_item);
+ tlist_item = lnext(tlist, tlist_item);
}
if (tlist_item)
es = es->next;
while (!es)
{
- eslc = lnext(eslc);
+ eslc = lnext(eslist, eslc);
if (!eslc)
break; /* end of function */
/* advance list of default expressions */
if (cell != NULL)
- cell = lnext(cell);
+ cell = lnext(tstate->coldefexprs, cell);
}
tuplestore_putvalues(tstate->tupstore, tupdesc, values, nulls);
* don't and will then reuse the correct value.
*/
if (list_length(port->hba->radiussecrets) > 1)
- secrets = lnext(secrets);
+ secrets = lnext(port->hba->radiussecrets, secrets);
if (list_length(port->hba->radiusports) > 1)
- radiusports = lnext(radiusports);
+ radiusports = lnext(port->hba->radiusports, radiusports);
if (list_length(port->hba->radiusidentifiers) > 1)
- identifiers = lnext(identifiers);
+ identifiers = lnext(port->hba->radiusidentifiers, identifiers);
}
/* No servers left to try, so give up */
}
/* Get the databases. */
- field = lnext(field);
+ field = lnext(tok_line->fields, field);
if (!field)
{
ereport(elevel,
}
/* Get the roles. */
- field = lnext(field);
+ field = lnext(tok_line->fields, field);
if (!field)
{
ereport(elevel,
if (parsedline->conntype != ctLocal)
{
/* Read the IP address field. (with or without CIDR netmask) */
- field = lnext(field);
+ field = lnext(tok_line->fields, field);
if (!field)
{
ereport(elevel,
{
/* Read the mask field. */
pfree(str);
- field = lnext(field);
+ field = lnext(tok_line->fields, field);
if (!field)
{
ereport(elevel,
} /* != ctLocal */
/* Get the authentication method */
- field = lnext(field);
+ field = lnext(tok_line->fields, field);
if (!field)
{
ereport(elevel,
}
/* Parse remaining arguments */
- while ((field = lnext(field)) != NULL)
+ while ((field = lnext(tok_line->fields, field)) != NULL)
{
tokens = lfirst(field);
foreach(tokencell, tokens)
parsedline->usermap = pstrdup(token->string);
/* Get the ident user token */
- field = lnext(field);
+ field = lnext(tok_line->fields, field);
IDENT_FIELD_ABSENT(field);
tokens = lfirst(field);
IDENT_MULTI_VALUE(tokens);
parsedline->ident_user = pstrdup(token->string);
/* Get the PG rolename token */
- field = lnext(field);
+ field = lnext(tok_line->fields, field);
IDENT_FIELD_ABSENT(field);
tokens = lfirst(field);
IDENT_MULTI_VALUE(tokens);
return newnode;
}
-/* ****************************************************************
- * pg_list.h copy functions
- * ****************************************************************
- */
-
-/*
- * Perform a deep copy of the specified list, using copyObject(). The
- * list MUST be of type T_List; T_IntList and T_OidList nodes don't
- * need deep copies, so they should be copied via list_copy()
- */
-#define COPY_NODE_CELL(new, old) \
- (new) = (ListCell *) palloc(sizeof(ListCell)); \
- lfirst(new) = copyObjectImpl(lfirst(old));
-
-static List *
-_copyList(const List *from)
-{
- List *new;
- ListCell *curr_old;
- ListCell *prev_new;
-
- Assert(list_length(from) >= 1);
-
- new = makeNode(List);
- new->length = from->length;
-
- COPY_NODE_CELL(new->head, from->head);
- prev_new = new->head;
- curr_old = lnext(from->head);
-
- while (curr_old)
- {
- COPY_NODE_CELL(prev_new->next, curr_old);
- prev_new = prev_new->next;
- curr_old = curr_old->next;
- }
- prev_new->next = NULL;
- new->tail = prev_new;
-
- return new;
-}
-
/* ****************************************************************
* extensible.h copy functions
* ****************************************************************
* LIST NODES
*/
case T_List:
- retval = _copyList(from);
+ retval = list_copy_deep(from);
break;
/*
/*-------------------------------------------------------------------------
*
* list.c
- * implementation for PostgreSQL generic linked list package
+ * implementation for PostgreSQL generic list package
+ *
+ * See comments in pg_list.h.
*
*
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
#include "postgres.h"
#include "nodes/pg_list.h"
+#include "utils/memutils.h"
+
+
+/*
+ * The previous List implementation, since it used a separate palloc chunk
+ * for each cons cell, had the property that adding or deleting list cells
+ * did not move the storage of other existing cells in the list. Quite a
+ * bit of existing code depended on that, by retaining ListCell pointers
+ * across such operations on a list. There is no such guarantee in this
+ * implementation, so instead we have debugging support that is meant to
+ * help flush out now-broken assumptions. Defining DEBUG_LIST_MEMORY_USAGE
+ * while building this file causes the List operations to forcibly move
+ * all cells in a list whenever a cell is added or deleted. In combination
+ * with MEMORY_CONTEXT_CHECKING and/or Valgrind, this can usually expose
+ * broken code. It's a bit expensive though, as there's many more palloc
+ * cycles and a lot more data-copying than in a default build.
+ *
+ * By default, we enable this when building for Valgrind.
+ */
+#ifdef USE_VALGRIND
+#define DEBUG_LIST_MEMORY_USAGE
+#endif
+/* Overhead for the fixed part of a List header, measured in ListCells */
+#define LIST_HEADER_OVERHEAD \
+ ((int) ((offsetof(List, initial_elements) - 1) / sizeof(ListCell) + 1))
/*
- * Routines to simplify writing assertions about the type of a list; a
+ * Macros to simplify writing assertions about the type of a list; a
* NIL list is considered to be an empty list of any type.
*/
#define IsPointerList(l) ((l) == NIL || IsA((l), List))
return;
Assert(list->length > 0);
- Assert(list->head != NULL);
- Assert(list->tail != NULL);
+ Assert(list->length <= list->max_length);
+ Assert(list->elements != NULL);
Assert(list->type == T_List ||
list->type == T_IntList ||
list->type == T_OidList);
-
- if (list->length == 1)
- Assert(list->head == list->tail);
- if (list->length == 2)
- Assert(list->head->next == list->tail);
- Assert(list->tail->next == NULL);
}
#else
-#define check_list_invariants(l)
+#define check_list_invariants(l) ((void) 0)
#endif /* USE_ASSERT_CHECKING */
/*
- * Return a freshly allocated List. Since empty non-NIL lists are
- * invalid, new_list() also allocates the head cell of the new list:
- * the caller should be sure to fill in that cell's data.
+ * Return a freshly allocated List with room for at least min_size cells.
+ *
+ * Since empty non-NIL lists are invalid, new_list() sets the initial length
+ * to min_size, effectively marking that number of cells as valid; the caller
+ * is responsible for filling in their data.
*/
static List *
-new_list(NodeTag type)
+new_list(NodeTag type, int min_size)
{
- List *new_list;
- ListCell *new_head;
+ List *newlist;
+ int max_size;
- new_head = (ListCell *) palloc(sizeof(*new_head));
- new_head->next = NULL;
- /* new_head->data is left undefined! */
+ Assert(min_size > 0);
+
+ /*
+ * We allocate all the requested cells, and possibly some more, as part of
+ * the same palloc request as the List header. This is a big win for the
+ * typical case of short fixed-length lists. It can lose if we allocate a
+ * moderately long list and then it gets extended; we'll be wasting more
+ * initial_elements[] space than if we'd made the header small. However,
+ * rounding up the request as we do in the normal code path provides some
+ * defense against small extensions.
+ */
- new_list = (List *) palloc(sizeof(*new_list));
- new_list->type = type;
- new_list->length = 1;
- new_list->head = new_head;
- new_list->tail = new_head;
+#ifndef DEBUG_LIST_MEMORY_USAGE
- return new_list;
+ /*
+ * Normally, we set up a list with some extra cells, to allow it to grow
+ * without a repalloc. Prefer cell counts chosen to make the total
+ * allocation a power-of-2, since palloc would round it up to that anyway.
+ * (That stops being true for very large allocations, but very long lists
+ * are infrequent, so it doesn't seem worth special logic for such cases.)
+ *
+ * The minimum allocation is 8 ListCell units, providing either 4 or 5
+ * available ListCells depending on the machine's word width. Counting
+ * palloc's overhead, this uses the same amount of space as a one-cell
+ * list did in the old implementation, and less space for any longer list.
+ *
+ * We needn't worry about integer overflow; no caller passes min_size
+ * that's more than twice the size of an existing list, so the size limits
+ * within palloc will ensure that we don't overflow here.
+ */
+ max_size = 8; /* semi-arbitrary small power of 2 */
+ while (max_size < min_size + LIST_HEADER_OVERHEAD)
+ max_size *= 2;
+ max_size -= LIST_HEADER_OVERHEAD;
+#else
+
+ /*
+ * For debugging, don't allow any extra space. This forces any cell
+ * addition to go through enlarge_list() and thus move the existing data.
+ */
+ max_size = min_size;
+#endif
+
+ newlist = (List *) palloc(offsetof(List, initial_elements) +
+ max_size * sizeof(ListCell));
+ newlist->type = type;
+ newlist->length = min_size;
+ newlist->max_length = max_size;
+ newlist->elements = newlist->initial_elements;
+
+ return newlist;
}
/*
- * Allocate a new cell and make it the head of the specified
- * list. Assumes the list it is passed is non-NIL.
+ * Enlarge an existing non-NIL List to have room for at least min_size cells.
+ *
+ * This does *not* update list->length, as some callers would find that
+ * inconvenient. (list->length had better be the correct number of existing
+ * valid cells, though.)
+ */
+static void
+enlarge_list(List *list, int min_size)
+{
+ int new_max_len;
+
+ Assert(min_size > list->max_length); /* else we shouldn't be here */
+
+#ifndef DEBUG_LIST_MEMORY_USAGE
+
+ /*
+ * As above, we prefer power-of-two total allocations; but here we need
+ * not account for list header overhead. The existing max length might
+ * not be a power of 2, so don't rely on that.
+ */
+ new_max_len = 16; /* semi-arbitrary small power of 2 */
+ while (new_max_len < min_size)
+ new_max_len *= 2;
+#else
+ /* As above, don't allocate anything extra */
+ new_max_len = min_size;
+#endif
+
+ if (list->elements == list->initial_elements)
+ {
+ List *newlist PG_USED_FOR_ASSERTS_ONLY;
+
+ /*
+ * Replace original in-line allocation with a separate palloc block.
+ * Ensure it is in the same memory context as the List header. (The
+ * previous List implementation did not offer any guarantees about
+ * keeping all list cells in the same context, but it seems reasonable
+ * to create such a guarantee now.)
+ */
+ list->elements = (ListCell *)
+ MemoryContextAlloc(GetMemoryChunkContext(list),
+ new_max_len * sizeof(ListCell));
+ memcpy(list->elements, list->initial_elements,
+ list->length * sizeof(ListCell));
+
+ /*
+ * Currently, asking aset.c to reduce the allocated size of the List
+ * header is pointless in terms of reclaiming space, unless the list
+ * is very long. However, it seems worth doing anyway to cause the
+ * no-longer-needed initial_elements[] space to be cleared in
+ * debugging builds.
+ */
+ newlist = (List *) repalloc(list, offsetof(List, initial_elements));
+
+ /* That better not have failed, nor moved the list header */
+ Assert(newlist == list);
+ }
+ else
+ {
+#ifndef DEBUG_LIST_MEMORY_USAGE
+ /* Normally, let repalloc deal with enlargement */
+ list->elements = (ListCell *) repalloc(list->elements,
+ new_max_len * sizeof(ListCell));
+#else
+ /*
+ * repalloc() might enlarge the space in-place, which we don't want
+ * for debugging purposes, so forcibly move the data somewhere else.
+ */
+ ListCell *newelements;
+
+ newelements = (ListCell *)
+ MemoryContextAlloc(GetMemoryChunkContext(list),
+ new_max_len * sizeof(ListCell));
+ memcpy(newelements, list->elements,
+ list->length * sizeof(ListCell));
+ pfree(list->elements);
+ list->elements = newelements;
+#endif
+ }
+
+ list->max_length = new_max_len;
+}
+
+/*
+ * Convenience functions to construct short Lists from given values.
+ * (These are normally invoked via the list_makeN macros.)
+ */
+List *
+list_make1_impl(NodeTag t, ListCell datum1)
+{
+ List *list = new_list(t, 1);
+
+ list->elements[0] = datum1;
+ check_list_invariants(list);
+ return list;
+}
+
+List *
+list_make2_impl(NodeTag t, ListCell datum1, ListCell datum2)
+{
+ List *list = new_list(t, 2);
+
+ list->elements[0] = datum1;
+ list->elements[1] = datum2;
+ check_list_invariants(list);
+ return list;
+}
+
+List *
+list_make3_impl(NodeTag t, ListCell datum1, ListCell datum2,
+ ListCell datum3)
+{
+ List *list = new_list(t, 3);
+
+ list->elements[0] = datum1;
+ list->elements[1] = datum2;
+ list->elements[2] = datum3;
+ check_list_invariants(list);
+ return list;
+}
+
+List *
+list_make4_impl(NodeTag t, ListCell datum1, ListCell datum2,
+ ListCell datum3, ListCell datum4)
+{
+ List *list = new_list(t, 4);
+
+ list->elements[0] = datum1;
+ list->elements[1] = datum2;
+ list->elements[2] = datum3;
+ list->elements[3] = datum4;
+ check_list_invariants(list);
+ return list;
+}
+
+/*
+ * Make room for a new head cell in the given (non-NIL) list.
*
* The data in the new head cell is undefined; the caller should be
* sure to fill it in
static void
new_head_cell(List *list)
{
- ListCell *new_head;
-
- new_head = (ListCell *) palloc(sizeof(*new_head));
- new_head->next = list->head;
-
- list->head = new_head;
+ /* Enlarge array if necessary */
+ if (list->length >= list->max_length)
+ enlarge_list(list, list->length + 1);
+ /* Now shove the existing data over */
+ memmove(&list->elements[1], &list->elements[0],
+ list->length * sizeof(ListCell));
list->length++;
}
/*
- * Allocate a new cell and make it the tail of the specified
- * list. Assumes the list it is passed is non-NIL.
+ * Make room for a new tail cell in the given (non-NIL) list.
*
* The data in the new tail cell is undefined; the caller should be
* sure to fill it in
static void
new_tail_cell(List *list)
{
- ListCell *new_tail;
-
- new_tail = (ListCell *) palloc(sizeof(*new_tail));
- new_tail->next = NULL;
-
- list->tail->next = new_tail;
- list->tail = new_tail;
+ /* Enlarge array if necessary */
+ if (list->length >= list->max_length)
+ enlarge_list(list, list->length + 1);
list->length++;
}
Assert(IsPointerList(list));
if (list == NIL)
- list = new_list(T_List);
+ list = new_list(T_List, 1);
else
new_tail_cell(list);
- lfirst(list->tail) = datum;
+ lfirst(list_tail(list)) = datum;
check_list_invariants(list);
return list;
}
Assert(IsIntegerList(list));
if (list == NIL)
- list = new_list(T_IntList);
+ list = new_list(T_IntList, 1);
else
new_tail_cell(list);
- lfirst_int(list->tail) = datum;
+ lfirst_int(list_tail(list)) = datum;
check_list_invariants(list);
return list;
}
Assert(IsOidList(list));
if (list == NIL)
- list = new_list(T_OidList);
+ list = new_list(T_OidList, 1);
else
new_tail_cell(list);
- lfirst_oid(list->tail) = datum;
+ lfirst_oid(list_tail(list)) = datum;
check_list_invariants(list);
return list;
}
/*
- * Add a new cell to the list, in the position after 'prev_cell'. The
- * data in the cell is left undefined, and must be filled in by the
- * caller. 'list' is assumed to be non-NIL, and 'prev_cell' is assumed
- * to be non-NULL and a member of 'list'.
+ * Make room for a new cell at position 'pos' (measured from 0).
+ * The data in the cell is left undefined, and must be filled in by the
+ * caller. 'list' is assumed to be non-NIL, and 'pos' must be a valid
+ * list position, ie, 0 <= pos <= list's length.
+ * Returns address of the new cell.
*/
static ListCell *
-add_new_cell(List *list, ListCell *prev_cell)
+insert_new_cell(List *list, int pos)
{
- ListCell *new_cell;
+ Assert(pos >= 0 && pos <= list->length);
+
+ /* Enlarge array if necessary */
+ if (list->length >= list->max_length)
+ enlarge_list(list, list->length + 1);
+ /* Now shove the existing data over */
+ if (pos < list->length)
+ memmove(&list->elements[pos + 1], &list->elements[pos],
+ (list->length - pos) * sizeof(ListCell));
+ list->length++;
- new_cell = (ListCell *) palloc(sizeof(*new_cell));
- /* new_cell->data is left undefined! */
- new_cell->next = prev_cell->next;
- prev_cell->next = new_cell;
+ return &list->elements[pos];
+}
- if (list->tail == prev_cell)
- list->tail = new_cell;
+/*
+ * Insert the given datum at position 'pos' (measured from 0) in the list.
+ * 'pos' must be valid, ie, 0 <= pos <= list's length.
+ */
+List *
+list_insert_nth(List *list, int pos, void *datum)
+{
+ if (list == NIL)
+ {
+ Assert(pos == 0);
+ return list_make1(datum);
+ }
+ Assert(IsPointerList(list));
+ lfirst(insert_new_cell(list, pos)) = datum;
+ check_list_invariants(list);
+ return list;
+}
- list->length++;
+List *
+list_insert_nth_int(List *list, int pos, int datum)
+{
+ if (list == NIL)
+ {
+ Assert(pos == 0);
+ return list_make1_int(datum);
+ }
+ Assert(IsIntegerList(list));
+ lfirst_int(insert_new_cell(list, pos)) = datum;
+ check_list_invariants(list);
+ return list;
+}
- return new_cell;
+List *
+list_insert_nth_oid(List *list, int pos, Oid datum)
+{
+ if (list == NIL)
+ {
+ Assert(pos == 0);
+ return list_make1_oid(datum);
+ }
+ Assert(IsOidList(list));
+ lfirst_oid(insert_new_cell(list, pos)) = datum;
+ check_list_invariants(list);
+ return list;
+}
+
+/*
+ * Add a new cell to the list, in the position after 'prev_cell'. The
+ * data in the cell is left undefined, and must be filled in by the
+ * caller. 'list' is assumed to be non-NIL, and 'prev_cell' is assumed
+ * to be non-NULL and a member of 'list'. Returns address of new cell.
+ *
+ * Caution: prev_cell might no longer point into the list after this!
+ */
+static ListCell *
+add_new_cell_after(List *list, ListCell *prev_cell)
+{
+ /* insert_new_cell will assert that this is in-range: */
+ int pos = prev_cell - list->elements;
+
+ return insert_new_cell(list, pos + 1);
}
/*
* Add a new cell to the specified list (which must be non-NIL);
* it will be placed after the list cell 'prev' (which must be
* non-NULL and a member of 'list'). The data placed in the new cell
- * is 'datum'. The newly-constructed cell is returned.
+ * is 'datum'.
*/
-ListCell *
+void
lappend_cell(List *list, ListCell *prev, void *datum)
{
- ListCell *new_cell;
-
Assert(IsPointerList(list));
-
- new_cell = add_new_cell(list, prev);
- lfirst(new_cell) = datum;
+ lfirst(add_new_cell_after(list, prev)) = datum;
check_list_invariants(list);
- return new_cell;
}
-ListCell *
+void
lappend_cell_int(List *list, ListCell *prev, int datum)
{
- ListCell *new_cell;
-
Assert(IsIntegerList(list));
-
- new_cell = add_new_cell(list, prev);
- lfirst_int(new_cell) = datum;
+ lfirst_int(add_new_cell_after(list, prev)) = datum;
check_list_invariants(list);
- return new_cell;
}
-ListCell *
+void
lappend_cell_oid(List *list, ListCell *prev, Oid datum)
{
- ListCell *new_cell;
-
Assert(IsOidList(list));
-
- new_cell = add_new_cell(list, prev);
- lfirst_oid(new_cell) = datum;
+ lfirst_oid(add_new_cell_after(list, prev)) = datum;
check_list_invariants(list);
- return new_cell;
}
/*
Assert(IsPointerList(list));
if (list == NIL)
- list = new_list(T_List);
+ list = new_list(T_List, 1);
else
new_head_cell(list);
- lfirst(list->head) = datum;
+ lfirst(list_head(list)) = datum;
check_list_invariants(list);
return list;
}
Assert(IsIntegerList(list));
if (list == NIL)
- list = new_list(T_IntList);
+ list = new_list(T_IntList, 1);
else
new_head_cell(list);
- lfirst_int(list->head) = datum;
+ lfirst_int(list_head(list)) = datum;
check_list_invariants(list);
return list;
}
Assert(IsOidList(list));
if (list == NIL)
- list = new_list(T_OidList);
+ list = new_list(T_OidList, 1);
else
new_head_cell(list);
- lfirst_oid(list->head) = datum;
+ lfirst_oid(list_head(list)) = datum;
check_list_invariants(list);
return list;
}
/*
* Concatenate list2 to the end of list1, and return list1. list1 is
- * destructively changed. Callers should be sure to use the return
- * value as the new pointer to the concatenated list: the 'list1'
- * input pointer may or may not be the same as the returned pointer.
- *
- * The nodes in list2 are merely appended to the end of list1 in-place
- * (i.e. they aren't copied; the two lists will share some of the same
- * storage). Therefore, invoking list_free() on list2 will also
- * invalidate a portion of list1.
+ * destructively changed, list2 is not. (However, in the case of pointer
+ * lists, list1 and list2 will point to the same structures.) Callers
+ * should be sure to use the return value as the new pointer to the
+ * concatenated list: the 'list1' input pointer may or may not be the
+ * same as the returned pointer.
*/
List *
-list_concat(List *list1, List *list2)
+list_concat(List *list1, const List *list2)
{
+ int new_len;
+
if (list1 == NIL)
- return list2;
+ return list_copy(list2);
if (list2 == NIL)
return list1;
- if (list1 == list2)
- elog(ERROR, "cannot list_concat() a list to itself");
Assert(list1->type == list2->type);
- list1->length += list2->length;
- list1->tail->next = list2->head;
- list1->tail = list2->tail;
+ new_len = list1->length + list2->length;
+ /* Enlarge array if necessary */
+ if (new_len > list1->max_length)
+ enlarge_list(list1, new_len);
+
+ /* Even if list1 == list2, using memcpy should be safe here */
+ memcpy(&list1->elements[list1->length], &list2->elements[0],
+ list2->length * sizeof(ListCell));
+ list1->length = new_len;
check_list_invariants(list1);
return list1;
List *
list_truncate(List *list, int new_size)
{
- ListCell *cell;
- int n;
-
if (new_size <= 0)
return NIL; /* truncate to zero length */
/* If asked to effectively extend the list, do nothing */
- if (new_size >= list_length(list))
- return list;
+ if (new_size < list_length(list))
+ list->length = new_size;
- n = 1;
- foreach(cell, list)
- {
- if (n == new_size)
- {
- cell->next = NULL;
- list->tail = cell;
- list->length = new_size;
- check_list_invariants(list);
- return list;
- }
- n++;
- }
+ /*
+ * Note: unlike the individual-list-cell deletion functions, we don't move
+ * the list cells to new storage, even in DEBUG_LIST_MEMORY_USAGE mode.
+ * This is because none of them can move in this operation, so just like
+ * in the old cons-cell-based implementation, this function doesn't
+ * invalidate any pointers to cells of the list. This is also the reason
+ * for not wiping the memory of the deleted cells: the old code didn't
+ * free them either. Perhaps later we'll tighten this up.
+ */
- /* keep the compiler quiet; never reached */
- Assert(false);
return list;
}
-/*
- * Locate the n'th cell (counting from 0) of the list. It is an assertion
- * failure if there is no such cell.
- */
-ListCell *
-list_nth_cell(const List *list, int n)
-{
- ListCell *match;
-
- Assert(list != NIL);
- Assert(n >= 0);
- Assert(n < list->length);
- check_list_invariants(list);
-
- /* Does the caller actually mean to fetch the tail? */
- if (n == list->length - 1)
- return list->tail;
-
- for (match = list->head; n-- > 0; match = match->next)
- ;
-
- return match;
-}
-
-/*
- * Return the data value contained in the n'th element of the
- * specified list. (List elements begin at 0.)
- */
-void *
-list_nth(const List *list, int n)
-{
- Assert(IsPointerList(list));
- return lfirst(list_nth_cell(list, n));
-}
-
-/*
- * Return the integer value contained in the n'th element of the
- * specified list.
- */
-int
-list_nth_int(const List *list, int n)
-{
- Assert(IsIntegerList(list));
- return lfirst_int(list_nth_cell(list, n));
-}
-
-/*
- * Return the OID value contained in the n'th element of the specified
- * list.
- */
-Oid
-list_nth_oid(const List *list, int n)
-{
- Assert(IsOidList(list));
- return lfirst_oid(list_nth_cell(list, n));
-}
-
/*
* Return true iff 'datum' is a member of the list. Equality is
* determined via equal(), so callers should ensure that they pass a
}
/*
- * Delete 'cell' from 'list'; 'prev' is the previous element to 'cell'
- * in 'list', if any (i.e. prev == NULL iff list->head == cell)
+ * Delete the n'th cell (counting from 0) in list.
*
- * The cell is pfree'd, as is the List header if this was the last member.
+ * The List is pfree'd if this was the last member.
*/
List *
-list_delete_cell(List *list, ListCell *cell, ListCell *prev)
+list_delete_nth_cell(List *list, int n)
{
check_list_invariants(list);
- Assert(prev != NULL ? lnext(prev) == cell : list_head(list) == cell);
+
+ Assert(n >= 0 && n < list->length);
/*
* If we're about to delete the last node from the list, free the whole
}
/*
- * Otherwise, adjust the necessary list links, deallocate the particular
- * node we have just removed, and return the list we were given.
+ * Otherwise, we normally just collapse out the removed element. But for
+ * debugging purposes, move the whole list contents someplace else.
+ *
+ * (Note that we *must* keep the contents in the same memory context.)
*/
+#ifndef DEBUG_LIST_MEMORY_USAGE
+ memmove(&list->elements[n], &list->elements[n + 1],
+ (list->length - 1 - n) * sizeof(ListCell));
list->length--;
+#else
+ {
+ ListCell *newelems;
+ int newmaxlen = list->length - 1;
+
+ newelems = (ListCell *)
+ MemoryContextAlloc(GetMemoryChunkContext(list),
+ newmaxlen * sizeof(ListCell));
+ memcpy(newelems, list->elements, n * sizeof(ListCell));
+ memcpy(&newelems[n], &list->elements[n + 1],
+ (list->length - 1 - n) * sizeof(ListCell));
+ if (list->elements != list->initial_elements)
+ pfree(list->elements);
+ else
+ {
+ /*
+ * As in enlarge_list(), tell palloc code we're not using the
+ * initial_elements space anymore.
+ */
+ List *newlist PG_USED_FOR_ASSERTS_ONLY;
+
+ newlist = (List *) repalloc(list, offsetof(List, initial_elements));
+ Assert(newlist == list);
+ }
+ list->elements = newelems;
+ list->max_length = newmaxlen;
+ list->length--;
+ check_list_invariants(list);
+ }
+#endif
- if (prev)
- prev->next = cell->next;
- else
- list->head = cell->next;
-
- if (list->tail == cell)
- list->tail = prev;
-
- pfree(cell);
return list;
}
+/*
+ * Delete 'cell' from 'list'.
+ *
+ * The List is pfree'd if this was the last member. However, we do not
+ * touch any data the cell might've been pointing to.
+ */
+List *
+list_delete_cell(List *list, ListCell *cell)
+{
+ return list_delete_nth_cell(list, cell - list->elements);
+}
+
/*
* Delete the first cell in list that matches datum, if any.
* Equality is determined via equal().
list_delete(List *list, void *datum)
{
ListCell *cell;
- ListCell *prev;
Assert(IsPointerList(list));
check_list_invariants(list);
- prev = NULL;
foreach(cell, list)
{
if (equal(lfirst(cell), datum))
- return list_delete_cell(list, cell, prev);
-
- prev = cell;
+ return list_delete_cell(list, cell);
}
/* Didn't find a match: return the list unmodified */
list_delete_ptr(List *list, void *datum)
{
ListCell *cell;
- ListCell *prev;
Assert(IsPointerList(list));
check_list_invariants(list);
- prev = NULL;
foreach(cell, list)
{
if (lfirst(cell) == datum)
- return list_delete_cell(list, cell, prev);
-
- prev = cell;
+ return list_delete_cell(list, cell);
}
/* Didn't find a match: return the list unmodified */
list_delete_int(List *list, int datum)
{
ListCell *cell;
- ListCell *prev;
Assert(IsIntegerList(list));
check_list_invariants(list);
- prev = NULL;
foreach(cell, list)
{
if (lfirst_int(cell) == datum)
- return list_delete_cell(list, cell, prev);
-
- prev = cell;
+ return list_delete_cell(list, cell);
}
/* Didn't find a match: return the list unmodified */
list_delete_oid(List *list, Oid datum)
{
ListCell *cell;
- ListCell *prev;
Assert(IsOidList(list));
check_list_invariants(list);
- prev = NULL;
foreach(cell, list)
{
if (lfirst_oid(cell) == datum)
- return list_delete_cell(list, cell, prev);
-
- prev = cell;
+ return list_delete_cell(list, cell);
}
/* Didn't find a match: return the list unmodified */
*
* This is useful to replace the Lisp-y code "list = lnext(list);" in cases
* where the intent is to alter the list rather than just traverse it.
- * Beware that the removed cell is freed, whereas the lnext() coding leaves
- * the original list head intact if there's another pointer to it.
+ * Beware that the list is modified, whereas the Lisp-y coding leaves
+ * the original list head intact in case there's another pointer to it.
*/
List *
list_delete_first(List *list)
if (list == NIL)
return NIL; /* would an error be better? */
- return list_delete_cell(list, list_head(list), NULL);
+ return list_delete_nth_cell(list, 0);
}
/*
* in list1 (so it only performs a "union" if list1 is known unique to
* start with). Also, if you are about to write "x = list_union(x, y)"
* you probably want to use list_concat_unique() instead to avoid wasting
- * the list cells of the old x list.
+ * the storage of the old x list.
*
* This function could probably be implemented a lot faster if it is a
* performance bottleneck.
* This is almost the same functionality as list_union(), but list1 is
* modified in-place rather than being copied. However, callers of this
* function may have strict ordering expectations -- i.e. that the relative
- * order of those list2 elements that are not duplicates is preserved. Note
- * also that list2's cells are not inserted in list1, so the analogy to
- * list_concat() isn't perfect.
+ * order of those list2 elements that are not duplicates is preserved.
*/
List *
-list_concat_unique(List *list1, List *list2)
+list_concat_unique(List *list1, const List *list2)
{
ListCell *cell;
* simple pointer equality.
*/
List *
-list_concat_unique_ptr(List *list1, List *list2)
+list_concat_unique_ptr(List *list1, const List *list2)
{
ListCell *cell;
* This variant of list_concat_unique() operates upon lists of integers.
*/
List *
-list_concat_unique_int(List *list1, List *list2)
+list_concat_unique_int(List *list1, const List *list2)
{
ListCell *cell;
* This variant of list_concat_unique() operates upon lists of OIDs.
*/
List *
-list_concat_unique_oid(List *list1, List *list2)
+list_concat_unique_oid(List *list1, const List *list2)
{
ListCell *cell;
static void
list_free_private(List *list, bool deep)
{
- ListCell *cell;
+ if (list == NIL)
+ return; /* nothing to do */
check_list_invariants(list);
- cell = list_head(list);
- while (cell != NULL)
+ if (deep)
{
- ListCell *tmp = cell;
-
- cell = lnext(cell);
- if (deep)
- pfree(lfirst(tmp));
- pfree(tmp);
+ for (int i = 0; i < list->length; i++)
+ pfree(lfirst(&list->elements[i]));
}
-
- if (list)
- pfree(list);
+ if (list->elements != list->initial_elements)
+ pfree(list->elements);
+ pfree(list);
}
/*
list_copy(const List *oldlist)
{
List *newlist;
- ListCell *newlist_prev;
- ListCell *oldlist_cur;
if (oldlist == NIL)
return NIL;
- newlist = new_list(oldlist->type);
- newlist->length = oldlist->length;
-
- /*
- * Copy over the data in the first cell; new_list() has already allocated
- * the head cell itself
- */
- newlist->head->data = oldlist->head->data;
-
- newlist_prev = newlist->head;
- oldlist_cur = oldlist->head->next;
- while (oldlist_cur)
- {
- ListCell *newlist_cur;
-
- newlist_cur = (ListCell *) palloc(sizeof(*newlist_cur));
- newlist_cur->data = oldlist_cur->data;
- newlist_prev->next = newlist_cur;
-
- newlist_prev = newlist_cur;
- oldlist_cur = oldlist_cur->next;
- }
-
- newlist_prev->next = NULL;
- newlist->tail = newlist_prev;
+ newlist = new_list(oldlist->type, oldlist->length);
+ memcpy(newlist->elements, oldlist->elements,
+ newlist->length * sizeof(ListCell));
check_list_invariants(newlist);
return newlist;
list_copy_tail(const List *oldlist, int nskip)
{
List *newlist;
- ListCell *newlist_prev;
- ListCell *oldlist_cur;
if (nskip < 0)
nskip = 0; /* would it be better to elog? */
if (oldlist == NIL || nskip >= oldlist->length)
return NIL;
- newlist = new_list(oldlist->type);
- newlist->length = oldlist->length - nskip;
-
- /*
- * Skip over the unwanted elements.
- */
- oldlist_cur = oldlist->head;
- while (nskip-- > 0)
- oldlist_cur = oldlist_cur->next;
+ newlist = new_list(oldlist->type, oldlist->length - nskip);
+ memcpy(newlist->elements, &oldlist->elements[nskip],
+ newlist->length * sizeof(ListCell));
- /*
- * Copy over the data in the first remaining cell; new_list() has already
- * allocated the head cell itself
- */
- newlist->head->data = oldlist_cur->data;
+ check_list_invariants(newlist);
+ return newlist;
+}
- newlist_prev = newlist->head;
- oldlist_cur = oldlist_cur->next;
- while (oldlist_cur)
- {
- ListCell *newlist_cur;
+/*
+ * Return a deep copy of the specified list.
+ *
+ * The list elements are copied via copyObject(), so that this function's
+ * idea of a "deep" copy is considerably deeper than what list_free_deep()
+ * means by the same word.
+ */
+List *
+list_copy_deep(const List *oldlist)
+{
+ List *newlist;
- newlist_cur = (ListCell *) palloc(sizeof(*newlist_cur));
- newlist_cur->data = oldlist_cur->data;
- newlist_prev->next = newlist_cur;
+ if (oldlist == NIL)
+ return NIL;
- newlist_prev = newlist_cur;
- oldlist_cur = oldlist_cur->next;
- }
+ /* This is only sensible for pointer Lists */
+ Assert(IsA(oldlist, List));
- newlist_prev->next = NULL;
- newlist->tail = newlist_prev;
+ newlist = new_list(oldlist->type, oldlist->length);
+ for (int i = 0; i < newlist->length; i++)
+ lfirst(&newlist->elements[i]) =
+ copyObjectImpl(lfirst(&oldlist->elements[i]));
check_list_invariants(newlist);
return newlist;
* fresh copies of any pointed-to data.
*
* The comparator function receives arguments of type ListCell **.
+ * (XXX that's really inefficient now, but changing it seems like
+ * material for a standalone patch.)
*/
List *
list_qsort(const List *list, list_qsort_comparator cmp)
int len = list_length(list);
ListCell **list_arr;
List *newlist;
- ListCell *newlist_prev;
ListCell *cell;
int i;
if (len == 0)
return NIL;
- /* Flatten list cells into an array, so we can use qsort */
+ /* We have to make an array of pointers to satisfy the API */
list_arr = (ListCell **) palloc(sizeof(ListCell *) * len);
i = 0;
foreach(cell, list)
qsort(list_arr, len, sizeof(ListCell *), cmp);
/* Construct new list (this code is much like list_copy) */
- newlist = new_list(list->type);
- newlist->length = len;
-
- /*
- * Copy over the data in the first cell; new_list() has already allocated
- * the head cell itself
- */
- newlist->head->data = list_arr[0]->data;
+ newlist = new_list(list->type, len);
- newlist_prev = newlist->head;
- for (i = 1; i < len; i++)
- {
- ListCell *newlist_cur;
-
- newlist_cur = (ListCell *) palloc(sizeof(*newlist_cur));
- newlist_cur->data = list_arr[i]->data;
- newlist_prev->next = newlist_cur;
-
- newlist_prev = newlist_cur;
- }
-
- newlist_prev->next = NULL;
- newlist->tail = newlist_prev;
+ for (i = 0; i < len; i++)
+ newlist->elements[i] = *list_arr[i];
/* Might as well free the workspace array */
pfree(list_arr);
check_list_invariants(newlist);
return newlist;
}
-
-/*
- * Temporary compatibility functions
- *
- * In order to avoid warnings for these function definitions, we need
- * to include a prototype here as well as in pg_list.h. That's because
- * we don't enable list API compatibility in list.c, so we
- * don't see the prototypes for these functions.
- */
-
-/*
- * Given a list, return its length. This is merely defined for the
- * sake of backward compatibility: we can't afford to define a macro
- * called "length", so it must be a function. New code should use the
- * list_length() macro in order to avoid the overhead of a function
- * call.
- */
-int length(const List *list);
-
-int
-length(const List *list)
-{
- return list_length(list);
-}
typmod = exprTypmod((Node *) linitial(cexpr->args));
if (typmod < 0)
return -1; /* no point in trying harder */
- for_each_cell(arg, lnext(list_head(cexpr->args)))
+ for_each_cell(arg, cexpr->args, list_second_cell(cexpr->args))
{
Node *e = (Node *) lfirst(arg);
typmod = exprTypmod((Node *) linitial(mexpr->args));
if (typmod < 0)
return -1; /* no point in trying harder */
- for_each_cell(arg, lnext(list_head(mexpr->args)))
+ for_each_cell(arg, mexpr->args, list_second_cell(mexpr->args))
{
Node *e = (Node *) lfirst(arg);
if (IsA(node, List))
{
outNode(str, lfirst(lc));
- if (lnext(lc))
+ if (lnext(node, lc))
appendStringInfoChar(str, ' ');
}
else if (IsA(node, IntList))
foreach(l, e->args)
{
print_expr(lfirst(l), rtable);
- if (lnext(l))
+ if (lnext(e->args, l))
printf(",");
}
printf(")");
print_expr((Node *) mem->em_expr, rtable);
}
printf(")");
- if (lnext(i))
+ if (lnext(pathkeys, i))
printf(", ");
}
printf(")\n");
merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, int num_gene,
bool force)
{
- ListCell *prev;
ListCell *lc;
/* Look for a clump that new_clump can join to */
- prev = NULL;
foreach(lc, clumps)
{
Clump *old_clump = (Clump *) lfirst(lc);
pfree(new_clump);
/* Remove old_clump from list */
- clumps = list_delete_cell(clumps, lc, prev);
+ clumps = foreach_delete_current(clumps, lc);
/*
* Recursively try to merge the enlarged old_clump with
return merge_clump(root, clumps, old_clump, num_gene, force);
}
}
- prev = lc;
}
/*
/* Else search for the place to insert it */
for (;;)
{
- ListCell *nxt = lnext(lc);
+ ListCell *nxt = lnext(clumps, lc);
if (nxt == NULL || new_clump->size > ((Clump *) lfirst(nxt))->size)
break; /* it belongs after 'lc', before 'nxt' */
elog(ERROR, "wrong number of tlist entries");
if (exprType((Node *) tle->expr) != lfirst_oid(colType))
safetyInfo->unsafeColumns[tle->resno] = true;
- colType = lnext(colType);
+ colType = lnext(colTypes, colType);
}
if (colType != NULL)
elog(ERROR, "wrong number of tlist entries");
RestrictInfo *c = lfirst(l);
print_expr((Node *) c->clause, root->parse->rtable);
- if (lnext(l))
+ if (lnext(clauses, l))
printf(", ");
}
}
* For each of the remaining subpaths, add its cost to the array element
* with minimum cost.
*/
- for_each_cell(l, cell)
+ for_each_cell(l, subpaths, cell)
{
Path *subpath = (Path *) lfirst(l);
int i;
bool ref_is_outer;
List *removedlist;
ListCell *cell;
- ListCell *prev;
- ListCell *next;
/*
* This FK is not relevant unless it connects a baserel on one side of
worklist = list_copy(worklist);
removedlist = NIL;
- prev = NULL;
- for (cell = list_head(worklist); cell; cell = next)
+ foreach(cell, worklist)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell);
bool remove_it = false;
int i;
- next = lnext(cell);
/* Drop this clause if it matches any column of the FK */
for (i = 0; i < fkinfo->nkeys; i++)
{
}
if (remove_it)
{
- worklist = list_delete_cell(worklist, cell, prev);
+ worklist = foreach_delete_current(worklist, cell);
removedlist = lappend(removedlist, rinfo);
}
- else
- prev = cell;
}
/*
{
bool found;
ListCell *cell;
- ListCell *prev;
- ListCell *next;
/* Outer loop repeats until we find no more deductions */
do
found = false;
/* Process the LEFT JOIN clauses */
- prev = NULL;
- for (cell = list_head(root->left_join_clauses); cell; cell = next)
+ foreach(cell, root->left_join_clauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell);
- next = lnext(cell);
if (reconsider_outer_join_clause(root, rinfo, true))
{
found = true;
/* remove it from the list */
root->left_join_clauses =
- list_delete_cell(root->left_join_clauses, cell, prev);
+ foreach_delete_current(root->left_join_clauses, cell);
/* we throw it back anyway (see notes above) */
/* but the thrown-back clause has no extra selectivity */
rinfo->norm_selec = 2.0;
rinfo->outer_selec = 1.0;
distribute_restrictinfo_to_rels(root, rinfo);
}
- else
- prev = cell;
}
/* Process the RIGHT JOIN clauses */
- prev = NULL;
- for (cell = list_head(root->right_join_clauses); cell; cell = next)
+ foreach(cell, root->right_join_clauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell);
- next = lnext(cell);
if (reconsider_outer_join_clause(root, rinfo, false))
{
found = true;
/* remove it from the list */
root->right_join_clauses =
- list_delete_cell(root->right_join_clauses, cell, prev);
+ foreach_delete_current(root->right_join_clauses, cell);
/* we throw it back anyway (see notes above) */
/* but the thrown-back clause has no extra selectivity */
rinfo->norm_selec = 2.0;
rinfo->outer_selec = 1.0;
distribute_restrictinfo_to_rels(root, rinfo);
}
- else
- prev = cell;
}
/* Process the FULL JOIN clauses */
- prev = NULL;
- for (cell = list_head(root->full_join_clauses); cell; cell = next)
+ foreach(cell, root->full_join_clauses)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(cell);
- next = lnext(cell);
if (reconsider_full_join_clause(root, rinfo))
{
found = true;
/* remove it from the list */
root->full_join_clauses =
- list_delete_cell(root->full_join_clauses, cell, prev);
+ foreach_delete_current(root->full_join_clauses, cell);
/* we throw it back anyway (see notes above) */
/* but the thrown-back clause has no extra selectivity */
rinfo->norm_selec = 2.0;
rinfo->outer_selec = 1.0;
distribute_restrictinfo_to_rels(root, rinfo);
}
- else
- prev = cell;
}
} while (found);
foreach(lc1, root->eq_classes)
{
EquivalenceClass *cur_ec = (EquivalenceClass *) lfirst(lc1);
- ListCell *lc2;
+ int num_members;
/*
* If this EC contains a volatile expression, then generating child
if (!bms_is_subset(child_rel->top_parent_relids, cur_ec->ec_relids))
continue;
- foreach(lc2, cur_ec->ec_members)
+ /*
+ * We don't use foreach() here because there's no point in scanning
+ * newly-added child members, so we can stop after the last
+ * pre-existing EC member.
+ */
+ num_members = list_length(cur_ec->ec_members);
+ for (int pos = 0; pos < num_members; pos++)
{
- EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc2);
+ EquivalenceMember *cur_em = (EquivalenceMember *) list_nth(cur_ec->ec_members, pos);
if (cur_em->em_is_const)
continue; /* ignore consts here */
IndexClause *iclause = (IndexClause *) lfirst(lc);
Relids clause_relids = iclause->rinfo->clause_relids;
EquivalenceClass *parent_ec = iclause->rinfo->parent_ec;
- ListCell *lc2;
+ int num_considered_relids;
/* If we already tried its relids set, no need to do so again */
if (bms_equal_any(clause_relids, *considered_relids))
* exponential growth of planning time when there are many clauses,
* limit the number of relid sets accepted to 10 * considered_clauses.
*
- * Note: get_join_index_paths adds entries to *considered_relids, but
- * it prepends them to the list, so that we won't visit new entries
- * during the inner foreach loop. No real harm would be done if we
- * did, since the subset check would reject them; but it would waste
- * some cycles.
+ * Note: get_join_index_paths appends entries to *considered_relids,
+ * but we do not need to visit such newly-added entries within this
+ * loop, so we don't use foreach() here. No real harm would be done
+ * if we did visit them, since the subset check would reject them; but
+ * it would waste some cycles.
*/
- foreach(lc2, *considered_relids)
+ num_considered_relids = list_length(*considered_relids);
+ for (int pos = 0; pos < num_considered_relids; pos++)
{
- Relids oldrelids = (Relids) lfirst(lc2);
+ Relids oldrelids = (Relids) list_nth(*considered_relids, pos);
/*
* If either is a subset of the other, no new set is possible.
get_index_paths(root, rel, index, &clauseset, bitindexpaths);
/*
- * Remember we considered paths for this set of relids. We use lcons not
- * lappend to avoid confusing the loop in consider_index_join_outer_rels.
+ * Remember we considered paths for this set of relids.
*/
- *considered_relids = lcons(relids, *considered_relids);
+ *considered_relids = lappend(*considered_relids, relids);
}
/*
Cost costsofar;
List *qualsofar;
Bitmapset *clauseidsofar;
- ListCell *lastcell;
pathinfo = pathinfoarray[i];
paths = list_make1(pathinfo->path);
qualsofar = list_concat(list_copy(pathinfo->quals),
list_copy(pathinfo->preds));
clauseidsofar = bms_copy(pathinfo->clauseids);
- lastcell = list_head(paths); /* for quick deletions */
for (j = i + 1; j < npaths; j++)
{
list_copy(pathinfo->preds));
clauseidsofar = bms_add_members(clauseidsofar,
pathinfo->clauseids);
- lastcell = lnext(lastcell);
}
else
{
/* reject new path, remove it from paths list */
- paths = list_delete_cell(paths, lnext(lastcell), lastcell);
+ paths = list_truncate(paths, list_length(paths) - 1);
}
- Assert(lnext(lastcell) == NULL);
}
/* Keep the cheapest AND-group (or singleton) */
List *new_ops;
List *var_args;
List *non_var_args;
- ListCell *vargs_cell;
- ListCell *nargs_cell;
- ListCell *opnos_cell;
- ListCell *collids_cell;
iclause->rinfo = rinfo;
iclause->indexcol = indexcol;
* indexed relation.
*/
matching_cols = 1;
- vargs_cell = lnext(list_head(var_args));
- nargs_cell = lnext(list_head(non_var_args));
- opnos_cell = lnext(list_head(clause->opnos));
- collids_cell = lnext(list_head(clause->inputcollids));
- while (vargs_cell != NULL)
+ while (matching_cols < list_length(var_args))
{
- Node *varop = (Node *) lfirst(vargs_cell);
- Node *constop = (Node *) lfirst(nargs_cell);
+ Node *varop = (Node *) list_nth(var_args, matching_cols);
+ Node *constop = (Node *) list_nth(non_var_args, matching_cols);
int i;
- expr_op = lfirst_oid(opnos_cell);
+ expr_op = list_nth_oid(clause->opnos, matching_cols);
if (!var_on_left)
{
/* indexkey is on right, so commute the operator */
get_op_opfamily_strategy(expr_op,
index->opfamily[i]) == op_strategy &&
IndexCollMatchesExprColl(index->indexcollations[i],
- lfirst_oid(collids_cell)))
+ list_nth_oid(clause->inputcollids,
+ matching_cols)))
break;
}
if (i >= index->nkeycolumns)
/* This column matches, keep scanning */
matching_cols++;
- vargs_cell = lnext(vargs_cell);
- nargs_cell = lnext(nargs_cell);
- opnos_cell = lnext(opnos_cell);
- collids_cell = lnext(collids_cell);
}
/* Result is non-lossy if all columns are usable as index quals */
{
if (indexpr_item == NULL)
elog(ERROR, "wrong number of index expressions");
- indexpr_item = lnext(indexpr_item);
+ indexpr_item = lnext(index->indexprs, indexpr_item);
}
}
if (indexpr_item == NULL)
static void make_rels_by_clause_joins(PlannerInfo *root,
RelOptInfo *old_rel,
+ List *other_rels_list,
ListCell *other_rels);
static void make_rels_by_clauseless_joins(PlannerInfo *root,
RelOptInfo *old_rel,
- ListCell *other_rels);
+ List *other_rels);
static bool has_join_restriction(PlannerInfo *root, RelOptInfo *rel);
static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel);
static bool restriction_is_constant_false(List *restrictlist,
* to each initial rel they don't already include but have a join
* clause or restriction with.
*/
+ List *other_rels_list;
ListCell *other_rels;
if (level == 2) /* consider remaining initial rels */
- other_rels = lnext(r);
+ {
+ other_rels_list = joinrels[level - 1];
+ other_rels = lnext(other_rels_list, r);
+ }
else /* consider all initial rels */
- other_rels = list_head(joinrels[1]);
+ {
+ other_rels_list = joinrels[1];
+ other_rels = list_head(other_rels_list);
+ }
make_rels_by_clause_joins(root,
old_rel,
+ other_rels_list,
other_rels);
}
else
*/
make_rels_by_clauseless_joins(root,
old_rel,
- list_head(joinrels[1]));
+ joinrels[1]);
}
}
foreach(r, joinrels[k])
{
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
+ List *other_rels_list;
ListCell *other_rels;
ListCell *r2;
continue;
if (k == other_level)
- other_rels = lnext(r); /* only consider remaining rels */
+ {
+ /* only consider remaining rels */
+ other_rels_list = joinrels[k];
+ other_rels = lnext(other_rels_list, r);
+ }
else
- other_rels = list_head(joinrels[other_level]);
+ {
+ other_rels_list = joinrels[other_level];
+ other_rels = list_head(other_rels_list);
+ }
- for_each_cell(r2, other_rels)
+ for_each_cell(r2, other_rels_list, other_rels)
{
RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);
make_rels_by_clauseless_joins(root,
old_rel,
- list_head(joinrels[1]));
+ joinrels[1]);
}
/*----------
* automatically ensures that each new joinrel is only added to the list once.
*
* 'old_rel' is the relation entry for the relation to be joined
- * 'other_rels': the first cell in a linked list containing the other
+ * 'other_rels_list': a list containing the other
* rels to be considered for joining
+ * 'other_rels': the first cell to be considered
*
* Currently, this is only used with initial rels in other_rels, but it
* will work for joining to joinrels too.
static void
make_rels_by_clause_joins(PlannerInfo *root,
RelOptInfo *old_rel,
+ List *other_rels_list,
ListCell *other_rels)
{
ListCell *l;
- for_each_cell(l, other_rels)
+ for_each_cell(l, other_rels_list, other_rels)
{
RelOptInfo *other_rel = (RelOptInfo *) lfirst(l);
* The join rels are returned in root->join_rel_level[join_cur_level].
*
* 'old_rel' is the relation entry for the relation to be joined
- * 'other_rels': the first cell of a linked list containing the
- * other rels to be considered for joining
+ * 'other_rels': a list containing the other rels to be considered for joining
*
* Currently, this is only used with initial rels in other_rels, but it would
* work for joining to joinrels too.
static void
make_rels_by_clauseless_joins(PlannerInfo *root,
RelOptInfo *old_rel,
- ListCell *other_rels)
+ List *other_rels)
{
ListCell *l;
- for_each_cell(l, other_rels)
+ foreach(l, other_rels)
{
RelOptInfo *other_rel = (RelOptInfo *) lfirst(l);
if (!lop)
elog(ERROR, "too few pathkeys for mergeclauses");
opathkey = (PathKey *) lfirst(lop);
- lop = lnext(lop);
+ lop = lnext(outer_pathkeys, lop);
lastoeclass = opathkey->pk_eclass;
if (oeclass != lastoeclass)
elog(ERROR, "outer pathkeys do not match mergeclause");
lip = list_head(pathkeys);
pathkey = (PathKey *) lfirst(lip);
pathkey_ec = pathkey->pk_eclass;
- lip = lnext(lip);
+ lip = lnext(pathkeys, lip);
matched_pathkey = false;
/* Scan mergeclauses to see how many we can use */
break;
pathkey = (PathKey *) lfirst(lip);
pathkey_ec = pathkey->pk_eclass;
- lip = lnext(lip);
+ lip = lnext(pathkeys, lip);
matched_pathkey = false;
}
/*
* We can delete this SpecialJoinInfo from the list too, since it's no
- * longer of interest.
+ * longer of interest. (Since we'll restart the foreach loop
+ * immediately, we don't bother with foreach_delete_current.)
*/
- root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo);
+ root->join_info_list = list_delete_cell(root->join_info_list, lc);
/*
* Restart the scan. This is necessary to ensure we find all
* removable joins independently of ordering of the join_info_list
* (note that removal of attr_needed bits may make a join appear
- * removable that did not before). Also, since we just deleted the
- * current list cell, we'd have to have some kluge to continue the
- * list scan anyway.
+ * removable that did not before).
*/
goto restart;
}
List *joininfos;
Index rti;
ListCell *l;
- ListCell *nextl;
/*
* Mark the rel as "dead" to show it is no longer part of the join tree.
* remove or just update the PHV. There is no corresponding test in
* join_is_removable because it doesn't need to distinguish those cases.
*/
- for (l = list_head(root->placeholder_list); l != NULL; l = nextl)
+ foreach(l, root->placeholder_list)
{
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(l);
- nextl = lnext(l);
Assert(!bms_is_member(relid, phinfo->ph_lateral));
if (bms_is_subset(phinfo->ph_needed, joinrelids) &&
bms_is_member(relid, phinfo->ph_eval_at))
- root->placeholder_list = list_delete_ptr(root->placeholder_list,
- phinfo);
+ root->placeholder_list = foreach_delete_current(root->placeholder_list,
+ l);
else
{
phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at, relid);
reduce_unique_semijoins(PlannerInfo *root)
{
ListCell *lc;
- ListCell *next;
/*
- * Scan the join_info_list to find semijoins. We can't use foreach
- * because we may delete the current cell.
+ * Scan the join_info_list to find semijoins.
*/
- for (lc = list_head(root->join_info_list); lc != NULL; lc = next)
+ foreach(lc, root->join_info_list)
{
SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc);
int innerrelid;
Relids joinrelids;
List *restrictlist;
- next = lnext(lc);
-
/*
* Must be a non-delaying semijoin to a single baserel, else we aren't
* going to be able to do anything with it. (It's probably not
continue;
/* OK, remove the SpecialJoinInfo from the list. */
- root->join_info_list = list_delete_ptr(root->join_info_list, sjinfo);
+ root->join_info_list = foreach_delete_current(root->join_info_list, lc);
}
}
/* non-resjunk columns should have grouping clauses */
Assert(lg != NULL);
sgc = (SortGroupClause *) lfirst(lg);
- lg = lnext(lg);
+ lg = lnext(topop->groupClauses, lg);
opid = distinct_col_search(tle->resno, colnos, opids);
if (!OidIsValid(opid) ||
chain = NIL;
if (list_length(rollups) > 1)
{
- ListCell *lc2 = lnext(list_head(rollups));
bool is_first_sort = ((RollupData *) linitial(rollups))->is_hashed;
- for_each_cell(lc, lc2)
+ for_each_cell(lc, rollups, list_second_cell(rollups))
{
RollupData *rollup = lfirst(lc);
AttrNumber *new_grpColIdx;
elog(ERROR, "outer pathkeys do not match mergeclauses");
opathkey = (PathKey *) lfirst(lop);
opeclass = opathkey->pk_eclass;
- lop = lnext(lop);
+ lop = lnext(outerpathkeys, lop);
if (oeclass != opeclass)
elog(ERROR, "outer pathkeys do not match mergeclauses");
}
if (ieclass == ipeclass)
{
/* successful first match to this inner pathkey */
- lip = lnext(lip);
+ lip = lnext(innerpathkeys, lip);
first_inner_match = true;
}
}
else
elog(ERROR, "index key does not match expected index column");
}
- indexpr_item = lnext(indexpr_item);
+ indexpr_item = lnext(index->indexprs, indexpr_item);
}
}
while (lc1 && lfirst(lc1) == NIL)
{
++num_empty;
- lc1 = lnext(lc1);
+ lc1 = lnext(groupingSets, lc1);
}
/* bail out now if it turns out that all we had were empty sets. */
j = 0;
i = 1;
- for_each_cell(lc, lc1)
+ for_each_cell(lc, groupingSets, lc1)
{
List *candidate = (List *) lfirst(lc);
Bitmapset *candidate_set = NULL;
{
unhashed_rollup = lfirst_node(RollupData, l_start);
exclude_groups = unhashed_rollup->numGroups;
- l_start = lnext(l_start);
+ l_start = lnext(gd->rollups, l_start);
}
hashsize = estimate_hashagg_tablesize(path,
*/
sets_data = list_copy(gd->unsortable_sets);
- for_each_cell(lc, l_start)
+ for_each_cell(lc, gd->rollups, l_start)
{
RollupData *rollup = lfirst_node(RollupData, lc);
* below, must use the same condition.
*/
i = 0;
- for_each_cell(lc, lnext(list_head(gd->rollups)))
+ for_each_cell(lc, gd->rollups, list_second_cell(gd->rollups))
{
RollupData *rollup = lfirst_node(RollupData, lc);
rollups = list_make1(linitial(gd->rollups));
i = 0;
- for_each_cell(lc, lnext(list_head(gd->rollups)))
+ for_each_cell(lc, gd->rollups, list_second_cell(gd->rollups))
{
RollupData *rollup = lfirst_node(RollupData, lc);
-1.0);
}
- if (lnext(l))
+ if (lnext(activeWindows, l))
{
/*
* Add the current WindowFuncs to the output target for this
Assert(orig_tlist_item != NULL);
orig_tle = lfirst_node(TargetEntry, orig_tlist_item);
- orig_tlist_item = lnext(orig_tlist_item);
+ orig_tlist_item = lnext(orig_tlist, orig_tlist_item);
if (orig_tle->resjunk) /* should not happen */
elog(ERROR, "resjunk output columns are not implemented");
Assert(new_tle->resno == orig_tle->resno);
{
ptr += sprintf(ptr, "$%d%s",
lfirst_int(lc),
- lnext(lc) ? "," : ")");
+ lnext(splan->setParam, lc) ? "," : ")");
}
}
remove_useless_result_rtes(PlannerInfo *root)
{
ListCell *cell;
- ListCell *prev;
- ListCell *next;
/* Top level of jointree must always be a FromExpr */
Assert(IsA(root->parse->jointree, FromExpr));
* RTE_RESULT RTE; otherwise we'll generate a whole-row Var for the
* RTE_RESULT, which the executor has no support for.
*/
- prev = NULL;
- for (cell = list_head(root->rowMarks); cell; cell = next)
+ foreach(cell, root->rowMarks)
{
PlanRowMark *rc = (PlanRowMark *) lfirst(cell);
- next = lnext(cell);
if (rt_fetch(rc->rti, root->parse->rtable)->rtekind == RTE_RESULT)
- root->rowMarks = list_delete_cell(root->rowMarks, cell, prev);
- else
- prev = cell;
+ root->rowMarks = foreach_delete_current(root->rowMarks, cell);
}
}
FromExpr *f = (FromExpr *) jtnode;
Relids result_relids = NULL;
ListCell *cell;
- ListCell *prev;
- ListCell *next;
/*
* We can drop RTE_RESULT rels from the fromlist so long as at least
* one child remains, since joining to a one-row table changes
* nothing. The easiest way to mechanize this rule is to modify the
- * list in-place, using list_delete_cell.
+ * list in-place.
*/
- prev = NULL;
- for (cell = list_head(f->fromlist); cell; cell = next)
+ foreach(cell, f->fromlist)
{
Node *child = (Node *) lfirst(cell);
int varno;
child = remove_useless_results_recurse(root, child);
/* ... and stick it back into the tree */
lfirst(cell) = child;
- next = lnext(cell);
/*
* If it's an RTE_RESULT with at least one sibling, we can drop
if (list_length(f->fromlist) > 1 &&
(varno = get_result_relid(root, child)) != 0)
{
- f->fromlist = list_delete_cell(f->fromlist, cell, prev);
+ f->fromlist = foreach_delete_current(f->fromlist, cell);
result_relids = bms_add_member(result_relids, varno);
}
- else
- prev = cell;
}
/*
if (!old_tle->resjunk && old_tle->resno == attrno)
{
new_tle = old_tle;
- tlist_item = lnext(tlist_item);
+ tlist_item = lnext(tlist, tlist_item);
}
}
}
new_tlist = lappend(new_tlist, old_tle);
attrno++;
- tlist_item = lnext(tlist_item);
+ tlist_item = lnext(tlist, tlist_item);
}
return new_tlist;
/* types disagree, so force typmod to -1 */
colTypmods[colindex] = -1;
}
- curColType = lnext(curColType);
+ curColType = lnext(colTypes, curColType);
colindex++;
}
Assert(curColType == NULL);
/* non-resjunk columns should have grouping clauses */
Assert(lg != NULL);
sgc = (SortGroupClause *) lfirst(lg);
- lg = lnext(lg);
+ lg = lnext(grouplist, lg);
Assert(sgc->tleSortGroupRef == 0);
sgc->tleSortGroupRef = tle->ressortgroupref;
return false; /* too many tlist items */
coltype = lfirst_oid(clistitem);
- clistitem = lnext(clistitem);
+ clistitem = lnext(coltypelist, clistitem);
if (exprType((Node *) tle->expr) != coltype)
return false; /* column type mismatch */
{
List *result;
ListCell *cell;
- ListCell *prev;
- ListCell *next;
result = NIL;
- prev = NULL;
- for (cell = list_head(root->curOuterParams); cell; cell = next)
+ foreach(cell, root->curOuterParams)
{
NestLoopParam *nlp = (NestLoopParam *) lfirst(cell);
- next = lnext(cell);
-
/*
* We are looking for Vars and PHVs that can be supplied by the
* lefthand rels. The "bms_overlap" test is just an optimization to
if (IsA(nlp->paramval, Var) &&
bms_is_member(nlp->paramval->varno, leftrelids))
{
- root->curOuterParams = list_delete_cell(root->curOuterParams,
- cell, prev);
+ root->curOuterParams = foreach_delete_current(root->curOuterParams,
+ cell);
result = lappend(result, nlp);
}
else if (IsA(nlp->paramval, PlaceHolderVar) &&
false)->ph_eval_at,
leftrelids))
{
- root->curOuterParams = list_delete_cell(root->curOuterParams,
- cell, prev);
+ root->curOuterParams = foreach_delete_current(root->curOuterParams,
+ cell);
result = lappend(result, nlp);
}
- else
- prev = cell;
}
return result;
}
add_path(RelOptInfo *parent_rel, Path *new_path)
{
bool accept_new = true; /* unless we find a superior old path */
- ListCell *insert_after = NULL; /* where to insert new item */
+ int insert_at = 0; /* where to insert new item */
List *new_path_pathkeys;
ListCell *p1;
- ListCell *p1_prev;
- ListCell *p1_next;
/*
* This is a convenient place to check for query cancel --- no part of the
* Loop to check proposed new path against old paths. Note it is possible
* for more than one old path to be tossed out because new_path dominates
* it.
- *
- * We can't use foreach here because the loop body may delete the current
- * list cell.
*/
- p1_prev = NULL;
- for (p1 = list_head(parent_rel->pathlist); p1 != NULL; p1 = p1_next)
+ foreach(p1, parent_rel->pathlist)
{
Path *old_path = (Path *) lfirst(p1);
bool remove_old = false; /* unless new proves superior */
PathKeysComparison keyscmp;
BMS_Comparison outercmp;
- p1_next = lnext(p1);
-
/*
* Do a fuzzy cost comparison with standard fuzziness limit.
*/
*/
if (remove_old)
{
- parent_rel->pathlist = list_delete_cell(parent_rel->pathlist,
- p1, p1_prev);
+ parent_rel->pathlist = foreach_delete_current(parent_rel->pathlist,
+ p1);
/*
* Delete the data pointed-to by the deleted cell, if possible
*/
if (!IsA(old_path, IndexPath))
pfree(old_path);
- /* p1_prev does not advance */
}
else
{
/* new belongs after this old path if it has cost >= old's */
if (new_path->total_cost >= old_path->total_cost)
- insert_after = p1;
- /* p1_prev advances */
- p1_prev = p1;
+ insert_at = foreach_current_index(p1) + 1;
}
/*
if (accept_new)
{
/* Accept the new path: insert it at proper place in pathlist */
- if (insert_after)
- lappend_cell(parent_rel->pathlist, insert_after, new_path);
- else
- parent_rel->pathlist = lcons(new_path, parent_rel->pathlist);
+ parent_rel->pathlist =
+ list_insert_nth(parent_rel->pathlist, insert_at, new_path);
}
else
{
add_partial_path(RelOptInfo *parent_rel, Path *new_path)
{
bool accept_new = true; /* unless we find a superior old path */
- ListCell *insert_after = NULL; /* where to insert new item */
+ int insert_at = 0; /* where to insert new item */
ListCell *p1;
- ListCell *p1_prev;
- ListCell *p1_next;
/* Check for query cancel. */
CHECK_FOR_INTERRUPTS();
* As in add_path, throw out any paths which are dominated by the new
* path, but throw out the new path if some existing path dominates it.
*/
- p1_prev = NULL;
- for (p1 = list_head(parent_rel->partial_pathlist); p1 != NULL;
- p1 = p1_next)
+ foreach(p1, parent_rel->partial_pathlist)
{
Path *old_path = (Path *) lfirst(p1);
bool remove_old = false; /* unless new proves superior */
PathKeysComparison keyscmp;
- p1_next = lnext(p1);
-
/* Compare pathkeys. */
keyscmp = compare_pathkeys(new_path->pathkeys, old_path->pathkeys);
if (remove_old)
{
parent_rel->partial_pathlist =
- list_delete_cell(parent_rel->partial_pathlist, p1, p1_prev);
+ foreach_delete_current(parent_rel->partial_pathlist, p1);
pfree(old_path);
- /* p1_prev does not advance */
}
else
{
/* new belongs after this old path if it has cost >= old's */
if (new_path->total_cost >= old_path->total_cost)
- insert_after = p1;
- /* p1_prev advances */
- p1_prev = p1;
+ insert_at = foreach_current_index(p1) + 1;
}
/*
if (accept_new)
{
/* Accept the new path: insert it at proper place */
- if (insert_after)
- lappend_cell(parent_rel->partial_pathlist, insert_after, new_path);
- else
- parent_rel->partial_pathlist =
- lcons(new_path, parent_rel->partial_pathlist);
+ parent_rel->partial_pathlist =
+ list_insert_nth(parent_rel->partial_pathlist, insert_at, new_path);
}
else
{
if (indexpr_item == NULL)
elog(ERROR, "wrong number of index expressions");
indexvar = (Expr *) lfirst(indexpr_item);
- indexpr_item = lnext(indexpr_item);
+ indexpr_item = lnext(index->indexprs, indexpr_item);
}
tlist = lappend(tlist,
/* Re-stamp the expression with given varno. */
partexpr = (Expr *) copyObject(lfirst(lc));
ChangeVarNodes((Node *) partexpr, 1, varno, 0);
- lc = lnext(lc);
+ lc = lnext(partkey->partexprs, lc);
}
partexprs[cnt] = list_make1(partexpr);
{
/* node-type-specific iteration state */
void *state;
+ List *state_list;
/* initialize to do the iteration */
void (*startup_fn) (Node *clause, PredIterInfo info);
/* next-component iteration function */
static void
list_startup_fn(Node *clause, PredIterInfo info)
{
- info->state = (void *) list_head((List *) clause);
+ info->state_list = (List *) clause;
+ info->state = (void *) list_head(info->state_list);
}
static Node *
if (l == NULL)
return NULL;
n = lfirst(l);
- info->state = (void *) lnext(l);
+ info->state = (void *) lnext(info->state_list, l);
return n;
}
static void
boolexpr_startup_fn(Node *clause, PredIterInfo info)
{
- info->state = (void *) list_head(((BoolExpr *) clause)->args);
+ info->state_list = ((BoolExpr *) clause)->args;
+ info->state = (void *) list_head(info->state_list);
}
/*
/* Initialize iteration variable to first member of ArrayExpr */
arrayexpr = (ArrayExpr *) lsecond(saop->args);
+ info->state_list = arrayexpr->elements;
state->next = list_head(arrayexpr->elements);
}
if (state->next == NULL)
return NULL;
lsecond(state->opexpr.args) = lfirst(state->next);
- state->next = lnext(state->next);
+ state->next = lnext(info->state_list, state->next);
return (Node *) &(state->opexpr);
}
return false; /* tlist longer than colTypes */
if (exprType((Node *) tle->expr) != lfirst_oid(curColType))
return false;
- curColType = lnext(curColType);
+ curColType = lnext(colTypes, curColType);
}
}
if (curColType != NULL)
return false; /* tlist longer than colCollations */
if (exprCollation((Node *) tle->expr) != lfirst_oid(curColColl))
return false;
- curColColl = lnext(curColColl);
+ curColColl = lnext(colCollations, curColColl);
}
}
if (curColColl != NULL)
List *level_srfs = (List *) lfirst(lc1);
PathTarget *ntarget;
- if (lnext(lc1) == NULL)
+ if (lnext(context.level_srfs, lc1) == NULL)
{
ntarget = target;
}
* later levels.
*/
add_sp_items_to_pathtarget(ntarget, level_srfs);
- for_each_cell(lc, lnext(lc2))
+ for_each_cell(lc, context.level_input_vars,
+ lnext(context.level_input_vars, lc2))
{
List *input_vars = (List *) lfirst(lc);
add_sp_items_to_pathtarget(ntarget, input_vars);
}
- for_each_cell(lc, lnext(lc3))
+ for_each_cell(lc, context.level_input_srfs,
+ lnext(context.level_input_srfs, lc3))
{
List *input_srfs = (List *) lfirst(lc);
ListCell *lcx;
target_rte->updatedCols = bms_add_member(target_rte->updatedCols,
attrno - FirstLowInvalidHeapAttributeNumber);
- orig_tl = lnext(orig_tl);
+ orig_tl = lnext(origTlist, orig_tl);
}
if (orig_tl != NULL)
elog(ERROR, "UPDATE target count mismatch --- internal error");
else if (IsA(lfirst(l), A_Star))
{
/* We only allow '*' at the end of a ColumnRef */
- if (lnext(l) != NULL)
+ if (lnext(indirection, l) != NULL)
parser_yyerror("improper use of \"*\"");
}
nfields++;
{
if (IsA(lfirst(l), A_Star))
{
- if (lnext(l) != NULL)
+ if (lnext(indirection, l) != NULL)
parser_yyerror("improper use of \"*\"");
}
}
core_yyscan_t yyscanner)
{
ListCell *cell;
- ListCell *prev;
- ListCell *next;
*collClause = NULL;
- prev = NULL;
- for (cell = list_head(qualList); cell; cell = next)
+ foreach(cell, qualList)
{
Node *n = (Node *) lfirst(cell);
- next = lnext(cell);
if (IsA(n, Constraint))
{
/* keep it in list */
- prev = cell;
continue;
}
if (IsA(n, CollateClause))
else
elog(ERROR, "unexpected node type %d", (int) n->type);
/* remove non-Constraint nodes from qualList */
- qualList = list_delete_cell(qualList, cell, prev);
+ qualList = foreach_delete_current(qualList, cell);
}
*constraintList = qualList;
}
if (gset_common)
{
- for_each_cell(l, lnext(list_head(gsets)))
+ for_each_cell(l, gsets, list_second_cell(gsets))
{
gset_common = list_intersection_int(gset_common, lfirst(l));
if (!gset_common)
result = lappend(result, list_union_int(NIL, (List *) lfirst(lc)));
}
- for_each_cell(lc, lnext(list_head(expanded_groups)))
+ for_each_cell(lc, expanded_groups, list_second_cell(expanded_groups))
{
List *p = lfirst(lc);
List *new_result = NIL;
parser_coercion_errposition(pstate, location, expr)));
newargs = lappend(newargs, cexpr);
ucolno++;
- arg = lnext(arg);
+ arg = lnext(args, arg);
}
if (arg != NULL)
ereport(ERROR,
Assert(exprs != NIL);
pexpr = (Node *) linitial(exprs);
- lc = lnext(list_head(exprs));
+ lc = list_second_cell(exprs);
ptype = exprType(pexpr);
/*