static void _outList(StringInfo str, List *node) { ListCell *lc; if (node == NULL) { int16 tg = 0; appendBinaryStringInfo(str, (const char *)&tg, sizeof(int16)); return; } WRITE_NODE_TYPE(""); WRITE_INT_FIELD(length); foreach(lc, node) { if (IsA(node, List)) { _outNode(str, lfirst(lc)); } else if (IsA(node, IntList)) { int n = lfirst_int(lc); appendBinaryStringInfo(str, (const char *)&n, sizeof(int)); } else if (IsA(node, OidList)) { Oid n = lfirst_oid(lc); appendBinaryStringInfo(str, (const char *)&n, sizeof(Oid)); } } }
/* * get_next_id * * Gets the smallest possible id to assign to the next continuous view. * We keep this minimal so that we can minimize the size of bitmaps used * to tag stream buffer events with. */ static int32 get_next_id(Relation rel) { HeapScanDesc scandesc; HeapTuple tup; int32 id = 1; List *idsList = NIL; ListCell *lc; scandesc = heap_beginscan_catalog(rel, 0, NULL); while ((tup = heap_getnext(scandesc, ForwardScanDirection)) != NULL) { Form_pipeline_query row = (Form_pipeline_query) GETSTRUCT(tup); idsList = lappend_int(idsList, row->id); } heap_endscan(scandesc); if (idsList != NIL) { int32 ids[idsList->length]; int i = 0; foreach(lc, idsList) { ids[i] = lfirst_int(lc); i++; }
/* * Reads the GP catalog tables and build a CdbComponentDatabases structure. * It then converts this to a Gang structure and initializes all the non-connection related fields. * * Call this function in GangContext. * Returns a not-null pointer. */ Gang * buildGangDefinition(List *segments, SegmentType segmentType) { Gang *newGangDefinition = NULL; ListCell *lc; int i = 0; int size; int contentId; size = list_length(segments); ELOG_DISPATCHER_DEBUG("buildGangDefinition:Starting %d qExec processes for gang", size); Assert(CurrentMemoryContext == DispatcherContext); /* allocate a gang */ newGangDefinition = (Gang *) palloc0(sizeof(Gang)); newGangDefinition->type = GANGTYPE_UNALLOCATED; newGangDefinition->size = size; newGangDefinition->allocated = false; newGangDefinition->db_descriptors = (SegmentDatabaseDescriptor **) palloc0(size * sizeof(SegmentDatabaseDescriptor*)); PG_TRY(); { /* initialize db_descriptors */ foreach_with_count (lc, segments , i) { contentId = lfirst_int(lc); newGangDefinition->db_descriptors[i] = cdbcomponent_allocateIdleQE(contentId, segmentType); } }
/* * GetPreferredReplicationNode * Pick any Datanode from given list, however fetch a preferred node first. */ List * GetPreferredReplicationNode(List *relNodes) { /* * Try to find the first node in given list relNodes * that is in the list of preferred nodes */ if (num_preferred_data_nodes != 0) { ListCell *item; foreach(item, relNodes) { int relation_nodeid = lfirst_int(item); int i; for (i = 0; i < num_preferred_data_nodes; i++) { #ifdef XCP char nodetype = PGXC_NODE_DATANODE; int nodeid = PGXCNodeGetNodeId(preferred_data_node[i], &nodetype); #else int nodeid = PGXCNodeGetNodeId(preferred_data_node[i], PGXC_NODE_DATANODE); #endif /* OK, found one */ if (nodeid == relation_nodeid) return lappend_int(NULL, nodeid); } }
void tf_write_one_row(StringInfo msgbuf, FmgrInfo *out_funcs, List *attnumlist, Datum *values, bool *nulls) { ListCell *cur; char *string; bool need_delim = false; foreach(cur, attnumlist) { int attnum = lfirst_int(cur); Datum value = values[attnum - 1]; bool isnull = nulls[attnum - 1]; if (need_delim) tf_write_char(msgbuf, '\t'); need_delim = true; if (isnull) { tf_write_string(msgbuf, "\\N"); } else { string = OutputFunctionCall(&out_funcs[attnum - 1], value); tf_write_attribute_out_text(msgbuf,'\t',string); } }
static void add_projection_desc_httpheader(CHURL_HEADERS headers, ProjectionInfo *projInfo, List *qualsAttributes) { int i; char long_number[sizeof(int32) * 8]; int *varNumbers = projInfo->pi_varNumbers; StringInfoData formatter; initStringInfo(&formatter); /* Convert the number of projection columns to a string */ pg_ltoa(list_length(projInfo->pi_targetlist) + list_length(qualsAttributes), long_number); churl_headers_append(headers, "X-GP-ATTRS-PROJ", long_number); for(i = 0; i < list_length(projInfo->pi_targetlist); i++) { int number = varNumbers[i] - 1; pg_ltoa(number, long_number); resetStringInfo(&formatter); appendStringInfo(&formatter, "X-GP-ATTRS-PROJ-IDX"); churl_headers_append(headers, formatter.data,long_number); } ListCell *attribute = NULL; foreach(attribute, qualsAttributes) { AttrNumber attrNumber = lfirst_int(attribute); pg_ltoa(attrNumber, long_number); resetStringInfo(&formatter); appendStringInfo(&formatter, "X-GP-ATTRS-PROJ-IDX"); churl_headers_append(headers, formatter.data,long_number); }
/* * This function extracts the command type and id of the modified relation from a * a PlannedStmt. This is done in preparation to call auto_stats() */ void autostats_get_cmdtype(QueryDesc *queryDesc, AutoStatsCmdType * pcmdType, Oid *prelationOid) { PlannedStmt *stmt = queryDesc->plannedstmt; Oid relationOid = InvalidOid; /* relation that is modified */ AutoStatsCmdType cmdType = AUTOSTATS_CMDTYPE_SENTINEL; /* command type */ RangeTblEntry *rte = NULL; switch (stmt->commandType) { case CMD_SELECT: if (stmt->intoClause != NULL) { /* CTAS */ if (queryDesc->estate->es_into_relation_descriptor) relationOid = RelationGetRelid(queryDesc->estate->es_into_relation_descriptor); cmdType = AUTOSTATS_CMDTYPE_CTAS; } break; case CMD_INSERT: rte = rt_fetch(lfirst_int(list_head(stmt->resultRelations)), stmt->rtable); relationOid = rte->relid; cmdType = AUTOSTATS_CMDTYPE_INSERT; break; case CMD_UPDATE: rte = rt_fetch(lfirst_int(list_head(stmt->resultRelations)), stmt->rtable); relationOid = rte->relid; cmdType = AUTOSTATS_CMDTYPE_UPDATE; break; case CMD_DELETE: rte = rt_fetch(lfirst_int(list_head(stmt->resultRelations)), stmt->rtable); relationOid = rte->relid; cmdType = AUTOSTATS_CMDTYPE_DELETE; break; case CMD_UTILITY: case CMD_UNKNOWN: case CMD_NOTHING: break; default: Assert(false); break; } Assert(cmdType >= 0 && cmdType <= AUTOSTATS_CMDTYPE_SENTINEL); *pcmdType = cmdType; *prelationOid = relationOid; }
/* * This variant of list_concat_unique() operates upon lists of integers. */ List * list_concat_unique_int(List *list1, List *list2) { ListCell *cell; Assert(IsIntegerList(list1)); Assert(IsIntegerList(list2)); foreach(cell, list2) { if (!list_member_int(list1, lfirst_int(cell))) list1 = lappend_int(list1, lfirst_int(cell)); } check_list_invariants(list1); return list1; }
/* * find_all_inheritors - * Returns a list of relation OIDs including the given rel plus * all relations that inherit from it, directly or indirectly. * Optionally, it also returns the number of parents found for * each such relation within the inheritance tree rooted at the * given rel. * * The specified lock type is acquired on all child relations (but not on the * given rel; caller should already have locked it). If lockmode is NoLock * then no locks are acquired, but caller must beware of race conditions * against possible DROPs of child relations. */ List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents) { List *rels_list, *rel_numparents; ListCell *l; /* * We build a list starting with the given rel and adding all direct and * 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. */ rels_list = list_make1_oid(parentrelId); rel_numparents = list_make1_int(0); foreach(l, rels_list) { Oid currentrel = lfirst_oid(l); List *currentchildren; ListCell *lc; /* Get the direct children of this rel */ currentchildren = find_inheritance_children(currentrel, lockmode); /* * Add to the queue only those children not already seen. This avoids * making duplicate entries in case of multiple inheritance paths from * the same parent. (It'll also keep us from getting into an infinite * loop, though theoretically there can't be any cycles in the * inheritance graph anyway.) */ foreach(lc, currentchildren) { Oid child_oid = lfirst_oid(lc); bool found = false; ListCell *lo; ListCell *li; /* if the rel is already there, bump number-of-parents counter */ forboth(lo, rels_list, li, rel_numparents) { if (lfirst_oid(lo) == child_oid) { lfirst_int(li)++; found = true; break; } } /* if it's not there, add it. expect 1 parent, initially. */ if (!found) { rels_list = lappend_oid(rels_list, child_oid); rel_numparents = lappend_int(rel_numparents, 1); } }
/* * RemoteCopy_GetRelationLoc * Get relation node list based on COPY data involved. An empty list is * returned to caller if relation involved has no locator information * as it is the case of a system relation. */ void RemoteCopy_GetRelationLoc(RemoteCopyData *state, Relation rel, List *attnums) { ExecNodes *exec_nodes = NULL; /* * If target table does not exists on nodes (e.g. system table) * the location info returned is NULL. This is the criteria, when * we need to run COPY on Coordinator */ state->rel_loc = GetRelationLocInfo(RelationGetRelid(rel)); if (state->rel_loc) { /* * Pick up one node only * This case corresponds to a replicated table with COPY TO * */ exec_nodes = makeNode(ExecNodes); if (!state->is_from && IsLocatorReplicated(state->rel_loc->locatorType)) exec_nodes->nodeList = GetPreferredReplicationNode(state->rel_loc->nodeList); else { /* All nodes necessary */ exec_nodes->nodeList = list_concat(exec_nodes->nodeList, state->rel_loc->nodeList); } } state->idx_dist_by_col = -1; if (state->rel_loc && state->rel_loc->partAttrNum != 0) { /* * Find the column used as key for data distribution. * First scan attributes of tuple descriptor with the list * of attributes used in COPY if any list is specified. * If no list is specified, set this value to the one of * locator information. */ if (attnums != NIL) { ListCell *cur; foreach(cur, attnums) { int attnum = lfirst_int(cur); if (state->rel_loc->partAttrNum == attnum) { state->idx_dist_by_col = attnum - 1; break; } } }
/* * This variant of list_union() operates upon lists of integers. */ List * list_union_int(const List *list1, const List *list2) { List *result; const ListCell *cell; Assert(IsIntegerList(list1)); Assert(IsIntegerList(list2)); result = list_copy(list1); foreach(cell, list2) { if (!list_member_int(result, lfirst_int(cell))) result = lappend_int(result, lfirst_int(cell)); } check_list_invariants(result); return result; }
ListCell * 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; check_list_invariants(list); return new_cell; }
/* * This variant of list_difference() operates upon lists of integers. */ List * list_difference_int(const List *list1, const List *list2) { const ListCell *cell; List *result = NIL; Assert(IsIntegerList(list1)); Assert(IsIntegerList(list2)); if (list2 == NIL) return list_copy(list1); foreach(cell, list1) { if (!list_member_int(list2, lfirst_int(cell))) result = lappend_int(result, lfirst_int(cell)); } check_list_invariants(result); return result; }
/* * As list_intersection but operates on lists of integers. */ List * list_intersection_int(const List *list1, const List *list2) { List *result; const ListCell *cell; if (list1 == NIL || list2 == NIL) return NIL; Assert(IsIntegerList(list1)); Assert(IsIntegerList(list2)); result = NIL; foreach(cell, list1) { if (list_member_int(list2, lfirst_int(cell))) result = lappend_int(result, lfirst_int(cell)); } check_list_invariants(result); return result; }
/* * Scan the (pre-parsed) hba file line by line, looking for a match * to the port's connection request. */ static bool check_hba(POOL_CONNECTION *frontend) { bool found_entry = false; bool error = false; ListCell *line; ListCell *line_num; forboth(line, hba_lines, line_num, hba_line_nums) { parse_hba(lfirst(line), lfirst_int(line_num), frontend, &found_entry, &error); if (found_entry || error) break; }
/* * Prepend an integer to the list. See lcons() */ List * lcons_int(int datum, List *list) { Assert(IsIntegerList(list)); if (list == NIL) list = new_list(T_IntList); else new_head_cell(list); lfirst_int(list->head) = datum; check_list_invariants(list); return list; }
/* * Append an integer to the specified list. See lappend() */ List * lappend_int(List *list, int datum) { Assert(IsIntegerList(list)); if (list == NIL) list = new_list(T_IntList); else new_tail_cell(list); lfirst_int(list->tail) = datum; check_list_invariants(list); return list; }
/* Use connection id. */ int useConnectionID(int32_t *connid) { /* Ensure that we have potential enough connection IDs to utilize. */ if ( PCONTRACK->FreeConnIDs == NULL ) { *connid = INVALID_CONNID; return CONNTRACK_CONNID_FULL; } *connid = lfirst_int(list_head(PCONTRACK->FreeConnIDs)); MEMORY_CONTEXT_SWITCH_TO(PCONTEXT) PCONTRACK->FreeConnIDs = list_delete_first(PCONTRACK->FreeConnIDs); MEMORY_CONTEXT_SWITCH_BACK elog(DEBUG3, "Resource manager uses connection track ID %d", *connid); return FUNC_RETURN_OK; }
/* * Return true iff the integer 'datum' is a member of the list. */ bool list_member_int(const List *list, int datum) { const ListCell *cell; Assert(IsIntegerList(list)); check_list_invariants(list); foreach(cell, list) { if (lfirst_int(cell) == datum) return true; } return false; }
/* As above, but for integers */ List * 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; } /* Didn't find a match: return the list unmodified */ return list; }
/* * bms_overlap_list - does a set overlap an integer list? */ bool bms_overlap_list(const Bitmapset *a, const List *b) { ListCell *lc; int wordnum, bitnum; if (a == NULL || b == NIL) return false; foreach(lc, b) { int x = lfirst_int(lc); if (x < 0) elog(ERROR, "negative bitmapset member not allowed"); wordnum = WORDNUM(x); bitnum = BITNUM(x); if (wordnum < a->nwords) if ((a->words[wordnum] & ((bitmapword) 1 << bitnum)) != 0) return true; }
/* * GetAnyDataNode * Pick any Datanode from given list, but try a preferred node */ List * GetAnyDataNode(List *relNodes) { /* * Try to find the first node in given list relNodes * that is in the list of preferred nodes */ if (num_preferred_data_nodes != 0) { ListCell *item; foreach(item, relNodes) { int relation_nodeid = lfirst_int(item); int i; for (i = 0; i < num_preferred_data_nodes; i++) { int nodeid = PGXCNodeGetNodeId(preferred_data_node[i], PGXC_NODE_DATANODE); /* OK, found one */ if (nodeid == relation_nodeid) return lappend_int(NULL, nodeid); } }
uint64 memReservedForParquetScan(Oid rel_oid, List* attr_list) { uint64 rowgroupsize = 0; char *compresstype = NULL; uint64 memReserved = 0; int attrNum = get_relnatts(rel_oid); /*Get the total attribute number of the relation*/ uint64 attsWidth = 0; /*the sum width of attributes to be scanned*/ uint64 recordWidth = 0; /*the average width of one record in the relation*/ /* The width array for all the attributes in the relation*/ int32 *attWidth = (int32*)palloc0(attrNum * sizeof(int32)); /** The variables for traversing through attribute list*/ ListCell *cell; /* Get rowgroup size and compress type */ AppendOnlyEntry *aoEntry = GetAppendOnlyEntry(rel_oid, SnapshotNow); rowgroupsize = aoEntry->blocksize; compresstype = aoEntry->compresstype; /** For each column in the relation, get the column width * 1) Get the column width from pg_attribute, estimate column width for to-be-scanned columns: * If fixed column width, the attlen is the column width; if not fixed, refer to typmod * 2) Get the average column width for variable length type column from table pg_statistic, if the * stawidth not equals 0, set it as the column width. */ for(int i = 0; i < attrNum; i++){ int att_id = i + 1; HeapTuple attTuple = caql_getfirst(NULL, cql("SELECT * FROM pg_attribute" " WHERE attrelid = :1 " " AND attnum = :2 ", ObjectIdGetDatum(rel_oid), Int16GetDatum(att_id))); if (HeapTupleIsValid(attTuple)) { /*Step1: estimate attwidth according to pg_attributes*/ Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attTuple); estimateColumnWidth(attWidth, &i, att, false); i--; int32 stawidth = 0; /*Step2: adjust addwidth according to pg_statistic*/ switch (att->atttypid) { case HAWQ_TYPE_VARCHAR: case HAWQ_TYPE_TEXT: case HAWQ_TYPE_XML: case HAWQ_TYPE_PATH: case HAWQ_TYPE_POLYGON: stawidth = get_attavgwidth(rel_oid, att_id); if(stawidth != 0) attWidth[i] = stawidth; break; case HAWQ_TYPE_VARBIT: stawidth = get_attavgwidth(rel_oid, att_id); if(stawidth != 0) attWidth[i] = stawidth + 4; break; default: break; } } recordWidth += attWidth[i]; } /* Reverse through the to-be-scanned attribute list, sum up the width */ Assert (1 <= list_length(attr_list)); foreach(cell, attr_list) { AttrNumber att_id = lfirst_int(cell); Assert(1 <= att_id); Assert(att_id <= attrNum); attsWidth += attWidth[att_id - 1]; /*sum up the attribute width in the to-be-scanned list*/ }
/* * get next tuple in in-memory heap table. */ HeapTuple InMemHeap_GetNext(InMemHeapScanDesc scan, ScanDirection direction) { bool valid = true; InMemHeapTuple pmemtup = NULL; Assert(NULL != scan); if (scan->hashIndexOk) { if (FALSE == scan->indexScanInitialized) { Oid key; bool found; key = DatumGetObjectId(scan->rs_key[scan->hashKeyIndexInScanKey].sk_argument); MemHeapHashIndexEntry *entry; entry = (MemHeapHashIndexEntry *) hash_search(scan->rs_rd->hashIndex, &key, HASH_FIND, &found); if (found) { if (BackwardScanDirection == direction) { /* if direction is backward, reverse list */ scan->indexReverseList = list_reverse_ints(entry->values); entry->values = scan->indexReverseList; } scan->indexNext = list_head(entry->values); } else scan->indexNext = NULL; scan->indexScanInitialized = TRUE; scan->indexScanKey = key; } for (; scan->indexNext != NULL; scan->indexNext = lnext(scan->indexNext)) { int32 index = lfirst_int(scan->indexNext); elog(DEBUG1, "read index %d key %d for relation %s", index, scan->indexScanKey, scan->rs_rd->relname); pmemtup = &scan->rs_rd->tuples[index]; HeapKeyTest(pmemtup->tuple, RelationGetDescr(scan->rs_rd->rel), scan->rs_nkeys, scan->rs_key, &valid); if (!valid) { continue; } scan->rs_ctup = pmemtup->tuple; scan->indexNext = lnext(scan->indexNext); return scan->rs_ctup; } } else { /* for backward scan, change direction of iterator */ while (InMemHeap_GetNextIndex(scan, direction)) { pmemtup = &scan->rs_rd->tuples[scan->rs_index]; Assert(NULL != pmemtup->tuple); if (scan->rs_key != NULL) { Assert(NULL != scan->rs_rd->rel); HeapKeyTest(pmemtup->tuple, RelationGetDescr(scan->rs_rd->rel), scan->rs_nkeys, scan->rs_key, &valid); } if (!valid) { continue; } scan->rs_ctup = pmemtup->tuple; return scan->rs_ctup; } } /* * read from local read only heap table. */ if (NULL != scan->hscan) { return heap_getnext(scan->hscan, direction); } return NULL ; }
static void ServiceListenLoop(ServiceCtrl *serviceCtrl) { ServiceConfig *serviceConfig = (ServiceConfig*)serviceCtrl->serviceConfig; uint8 *inputBuff; int n, highsock = 0, newsockfd; mpp_fd_set rset, rrset; struct sockaddr_in addr; socklen_t addrlen; List *connectedSockets = NIL; ListCell *cell; Assert(TopMemoryContext != NULL); MemoryContextSwitchTo(TopMemoryContext); Assert(CurrentMemoryContext == TopMemoryContext); /* * Setup scratch buffer. */ inputBuff = palloc(serviceConfig->requestLen); MPP_FD_ZERO(&rset); MPP_FD_SET(serviceCtrl->listenerFd, &rset); highsock = serviceCtrl->listenerFd + 1; /* we'll handle many incoming sockets but keep the sockets in blocking * mode since we are dealing with very small messages. */ while(true) { struct timeval shutdownTimeout = {1,0}; // 1 second. // Use local variable since select modifies // the timeout parameter with remaining time. CHECK_FOR_INTERRUPTS(); if (serviceConfig->ServiceShutdownRequested()) { if (serviceConfig->ServiceShutdown != NULL) { serviceConfig->ServiceShutdown(); } break; } /* no need to live on if postmaster has died */ if (!PostmasterIsAlive(true)) { if (serviceConfig->ServicePostmasterDied != NULL) { serviceConfig->ServicePostmasterDied(); } else { ereport(LOG, (errmsg("exiting because postmaster has died"))); proc_exit(1); } } memcpy(&rrset, &rset, sizeof(mpp_fd_set)); n = select(highsock + 1, (fd_set *)&rrset, NULL, NULL, &shutdownTimeout); if (n == 0 || (n < 0 && errno == EINTR)) { /* intr or timeout: Have we been here too long ? */ continue; } if (n < 0) { /* this may be a little severe, but if we error on select() * we'll just go ahead and blow up. This will result in the * postmaster re-spawning a new process. */ ereport(ERROR, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("'%s': error during select() call (error:%d).", serviceConfig->title, errno))); break; } /* is it someone tickling our listener port? */ if (MPP_FD_ISSET(serviceCtrl->listenerFd, &rrset)) { addrlen = sizeof(addr); if ((newsockfd = accept(serviceCtrl->listenerFd, (struct sockaddr *) & addr, &addrlen)) < 0) { /* * TODO: would be nice to read the errno and try and provide * more useful info as to why this happened. */ ereport(NOTICE, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("'%s': error from client connection: %s)", serviceConfig->title, strerror(errno)))); } /* make socket non-blocking BEFORE we connect. */ if (!pg_set_noblock(newsockfd)) { /* * TODO: would be nice to read the errno and try and provide * more useful info as to why this happened. */ ereport(NOTICE, (errcode(ERRCODE_GP_INTERCONNECTION_ERROR), errmsg("'%s': could not set outbound socket to non-blocking mode: %s", serviceConfig->title, strerror(errno)))); } if (newsockfd > highsock) highsock = newsockfd + 1; MPP_FD_SET(newsockfd, &rset); /* * Read connection message. */ // UNDONE: temporarily turn off new connection flag... if( !ServiceProcessRequest(serviceCtrl, newsockfd, inputBuff, false)) { /* close it down */ MPP_FD_CLR( newsockfd, &rset); shutdown(newsockfd, SHUT_WR); close(newsockfd); } else { connectedSockets = lappend_int(connectedSockets, newsockfd); } } /* loop through all of our established sockets */ cell = list_head(connectedSockets); while (cell != NULL) { int fd = lfirst_int(cell); /* get the next cell ready before we delete */ cell = lnext(cell); if (MPP_FD_ISSET(fd, &rrset)) { if( !ServiceProcessRequest(serviceCtrl, fd, inputBuff, false)) { /* close it down */ MPP_FD_CLR( fd, &rset); connectedSockets = list_delete_int(connectedSockets, fd); shutdown(fd, SHUT_WR); close(fd); } } } } ereport(LOG, (errmsg("normal shutdown"))); proc_exit(0); }
/* * 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)); }
/* * find_all_inheritors - * Returns a list of relation OIDs including the given rel plus * all relations that inherit from it, directly or indirectly. * Optionally, it also returns the number of parents found for * each such relation within the inheritance tree rooted at the * given rel. * * The specified lock type is acquired on all child relations (but not on the * given rel; caller should already have locked it). If lockmode is NoLock * then no locks are acquired, but caller must beware of race conditions * against possible DROPs of child relations. */ List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents) { /* hash table for O(1) rel_oid -> rel_numparents cell lookup */ HTAB *seen_rels; HASHCTL ctl; List *rels_list, *rel_numparents; ListCell *l; memset(&ctl, 0, sizeof(ctl)); ctl.keysize = sizeof(Oid); ctl.entrysize = sizeof(SeenRelsEntry); ctl.hcxt = CurrentMemoryContext; seen_rels = hash_create("find_all_inheritors temporary table", 32, /* start small and extend */ &ctl, HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); /* * We build a list starting with the given rel and adding all direct and * 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. */ rels_list = list_make1_oid(parentrelId); rel_numparents = list_make1_int(0); foreach(l, rels_list) { Oid currentrel = lfirst_oid(l); List *currentchildren; ListCell *lc; /* Get the direct children of this rel */ currentchildren = find_inheritance_children(currentrel, lockmode); /* * Add to the queue only those children not already seen. This avoids * making duplicate entries in case of multiple inheritance paths from * the same parent. (It'll also keep us from getting into an infinite * loop, though theoretically there can't be any cycles in the * inheritance graph anyway.) */ foreach(lc, currentchildren) { Oid child_oid = lfirst_oid(lc); bool found; SeenRelsEntry *hash_entry; hash_entry = hash_search(seen_rels, &child_oid, HASH_ENTER, &found); if (found) { /* if the rel is already there, bump number-of-parents counter */ lfirst_int(hash_entry->numparents_cell)++; } else { /* if it's not there, add it. expect 1 parent, initially. */ rels_list = lappend_oid(rels_list, child_oid); rel_numparents = lappend_int(rel_numparents, 1); hash_entry->numparents_cell = rel_numparents->tail; } }