/* * Gets list of publication oids for publications marked as FOR ALL TABLES. */ List * GetAllTablesPublications(void) { List *result; Relation rel; ScanKeyData scankey; SysScanDesc scan; HeapTuple tup; /* Find all publications that are marked as for all tables. */ rel = heap_open(PublicationRelationId, AccessShareLock); ScanKeyInit(&scankey, Anum_pg_publication_puballtables, BTEqualStrategyNumber, F_BOOLEQ, BoolGetDatum(true)); scan = systable_beginscan(rel, InvalidOid, false, NULL, 1, &scankey); result = NIL; while (HeapTupleIsValid(tup = systable_getnext(scan))) { Oid oid = ((Form_pg_publication) GETSTRUCT(tup))->oid; result = lappend_oid(result, oid); } systable_endscan(scan); heap_close(rel, AccessShareLock); return result; }
/* * Gets list of relation oids for a publication. * * This should only be used for normal publications, the FOR ALL TABLES * should use GetAllTablesPublicationRelations(). */ List * GetPublicationRelations(Oid pubid) { List *result; Relation pubrelsrel; ScanKeyData scankey; SysScanDesc scan; HeapTuple tup; /* Find all publications associated with the relation. */ pubrelsrel = heap_open(PublicationRelRelationId, AccessShareLock); ScanKeyInit(&scankey, Anum_pg_publication_rel_prpubid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(pubid)); scan = systable_beginscan(pubrelsrel, PublicationRelPrrelidPrpubidIndexId, true, NULL, 1, &scankey); result = NIL; while (HeapTupleIsValid(tup = systable_getnext(scan))) { Form_pg_publication_rel pubrel; pubrel = (Form_pg_publication_rel) GETSTRUCT(tup); result = lappend_oid(result, pubrel->prrelid); } systable_endscan(scan); heap_close(pubrelsrel, AccessShareLock); return result; }
/* * Gets list of all relation published by FOR ALL TABLES publication(s). */ List * GetAllTablesPublicationRelations(void) { Relation classRel; ScanKeyData key[1]; HeapScanDesc scan; HeapTuple tuple; List *result = NIL; classRel = heap_open(RelationRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_class_relkind, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(RELKIND_RELATION)); scan = heap_beginscan_catalog(classRel, 1, key); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple); Oid relid = relForm->oid; if (is_publishable_class(relid, relForm)) result = lappend_oid(result, relid); } heap_endscan(scan); heap_close(classRel, AccessShareLock); return result; }
/* * Takes a parent relation and returns Oid list of its partitions. The * function errors out if the given relation is not a parent. */ List * PartitionList(Oid parentRelationId) { Relation rel = heap_open(parentRelationId, AccessShareLock); List *partitionList = NIL; #if (PG_VERSION_NUM >= 100000) int partitionIndex = 0; int partitionCount = 0; if (!PartitionedTable(parentRelationId)) { char *relationName = get_rel_name(parentRelationId); ereport(ERROR, (errmsg("\"%s\" is not a parent table", relationName))); } Assert(rel->rd_partdesc != NULL); partitionCount = rel->rd_partdesc->nparts; for (partitionIndex = 0; partitionIndex < partitionCount; ++partitionIndex) { partitionList = lappend_oid(partitionList, rel->rd_partdesc->oids[partitionIndex]); } #endif /* keep the lock */ heap_close(rel, NoLock); return partitionList; }
/* * This variant of list_append_unique() operates upon lists of OIDs. */ List * list_append_unique_oid(List *list, Oid datum) { if (list_member_oid(list, datum)) return list; else return lappend_oid(list, datum); }
/* * 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); } }
/* * get_database_oids */ static List * get_database_oids(void) { List *db_oids = NIL; Relation pg_database; HeapScanDesc scan; HeapTuple tup; MemoryContext resultcxt; /* This is the context that we will allocate our output data in */ resultcxt = CurrentMemoryContext; /* * Start a transaction so we can access pg_database, and get a snapshot. * We don't have a use for the snapshot itself, but we're interested in * the secondary effect that it sets RecentGlobalXmin. (This is critical * for anything that reads heap pages, because HOT may decide to prune * them even if the process doesn't attempt to modify any tuples.) */ StartTransactionCommand(); (void) GetTransactionSnapshot(); /* We take a AccessExclusiveLock so we don't conflict with any DATABASE commands */ pg_database = heap_open(DatabaseRelationId, AccessExclusiveLock); scan = heap_beginscan_catalog(pg_database, 0, NULL); while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection))) { MemoryContext old; Form_pg_database row = (Form_pg_database) GETSTRUCT(tup); /* Ignore template databases or ones that don't allow connections. */ if (row->datistemplate || !row->datallowconn) continue; /* * Allocate our results in the caller's context, not the * transaction's. We do this inside the loop, and restore the original * context at the end, so that leaky things like heap_getnext() are * not called in a potentially long-lived context. */ old = MemoryContextSwitchTo(resultcxt); db_oids = lappend_oid(db_oids, HeapTupleGetOid(tup)); MemoryContextSwitchTo(old); } heap_endscan(scan); heap_close(pg_database, NoLock); CommitTransactionCommand(); return db_oids; }
Datum create_empty_extension(PG_FUNCTION_ARGS) { text *extName = PG_GETARG_TEXT_PP(0); text *schemaName = PG_GETARG_TEXT_PP(1); bool relocatable = PG_GETARG_BOOL(2); text *extVersion = PG_GETARG_TEXT_PP(3); Datum extConfig; Datum extCondition; List *requiredExtensions; if (PG_ARGISNULL(4)) extConfig = PointerGetDatum(NULL); else extConfig = PG_GETARG_DATUM(4); if (PG_ARGISNULL(5)) extCondition = PointerGetDatum(NULL); else extCondition = PG_GETARG_DATUM(5); requiredExtensions = NIL; if (!PG_ARGISNULL(6)) { ArrayType *textArray = PG_GETARG_ARRAYTYPE_P(6); Datum *textDatums; int ndatums; int i; deconstruct_array(textArray, TEXTOID, -1, false, 'i', &textDatums, NULL, &ndatums); for (i = 0; i < ndatums; i++) { text *txtname = DatumGetTextPP(textDatums[i]); char *extName = text_to_cstring(txtname); Oid extOid = get_extension_oid(extName, false); requiredExtensions = lappend_oid(requiredExtensions, extOid); } } InsertExtensionTuple(text_to_cstring(extName), GetUserId(), get_namespace_oid(text_to_cstring(schemaName), false), relocatable, text_to_cstring(extVersion), extConfig, extCondition, requiredExtensions); PG_RETURN_VOID(); }
/* * Collect a list of OIDs of all sequences owned by the specified relation, * and column if specified. */ List * getOwnedSequences(Oid relid, AttrNumber attnum) { List *result = NIL; Relation depRel; ScanKeyData key[3]; SysScanDesc scan; HeapTuple tup; depRel = heap_open(DependRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_depend_refclassid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationRelationId)); ScanKeyInit(&key[1], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); if (attnum) ScanKeyInit(&key[2], Anum_pg_depend_refobjsubid, BTEqualStrategyNumber, F_INT4EQ, Int32GetDatum(attnum)); scan = systable_beginscan(depRel, DependReferenceIndexId, true, NULL, attnum ? 3 : 2, key); while (HeapTupleIsValid(tup = systable_getnext(scan))) { Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup); /* * We assume any auto or internal dependency of a sequence on a column * must be what we are looking for. (We need the relkind test because * indexes can also have auto dependencies on columns.) */ if (deprec->classid == RelationRelationId && deprec->objsubid == 0 && deprec->refobjsubid != 0 && (deprec->deptype == DEPENDENCY_AUTO || deprec->deptype == DEPENDENCY_INTERNAL) && get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE) { result = lappend_oid(result, deprec->objid); } } systable_endscan(scan); heap_close(depRel, AccessShareLock); return result; }
/* * get_partition_ancestors_worker * recursive worker for get_partition_ancestors */ static void get_partition_ancestors_worker(Relation inhRel, Oid relid, List **ancestors) { Oid parentOid; /* Recursion ends at the topmost level, ie., when there's no parent */ parentOid = get_partition_parent_worker(inhRel, relid); if (parentOid == InvalidOid) return; *ancestors = lappend_oid(*ancestors, parentOid); get_partition_ancestors_worker(inhRel, parentOid, ancestors); }
/* * Handle TRUNCATE message. * * TODO: FDW support */ static void apply_handle_truncate(StringInfo s) { bool cascade = false; bool restart_seqs = false; List *remote_relids = NIL; List *remote_rels = NIL; List *rels = NIL; List *relids = NIL; List *relids_logged = NIL; ListCell *lc; ensure_transaction(); remote_relids = logicalrep_read_truncate(s, &cascade, &restart_seqs); foreach(lc, remote_relids) { LogicalRepRelId relid = lfirst_oid(lc); LogicalRepRelMapEntry *rel; rel = logicalrep_rel_open(relid, RowExclusiveLock); if (!should_apply_changes_for_rel(rel)) { /* * The relation can't become interesting in the middle of the * transaction so it's safe to unlock it. */ logicalrep_rel_close(rel, RowExclusiveLock); continue; } remote_rels = lappend(remote_rels, rel); rels = lappend(rels, rel->localrel); relids = lappend_oid(relids, rel->localreloid); if (RelationIsLogicallyLogged(rel->localrel)) relids_logged = lappend_oid(relids, rel->localreloid); }
/* * This variant of list_concat_unique() operates upon lists of OIDs. */ List * list_concat_unique_oid(List *list1, List *list2) { ListCell *cell; Assert(IsOidList(list1)); Assert(IsOidList(list2)); foreach(cell, list2) { if (!list_member_oid(list1, lfirst_oid(cell))) list1 = lappend_oid(list1, lfirst_oid(cell)); } check_list_invariants(list1); return list1; }
/* * This variant of list_union() operates upon lists of OIDs. */ List * list_union_oid(const List *list1, const List *list2) { List *result; const ListCell *cell; Assert(IsOidList(list1)); Assert(IsOidList(list2)); result = list_copy(list1); foreach(cell, list2) { if (!list_member_oid(result, lfirst_oid(cell))) result = lappend_oid(result, lfirst_oid(cell)); } check_list_invariants(result); return result; }
/* * This variant of list_difference() operates upon lists of OIDs. */ List * list_difference_oid(const List *list1, const List *list2) { const ListCell *cell; List *result = NIL; Assert(IsOidList(list1)); Assert(IsOidList(list2)); if (list2 == NIL) return list_copy(list1); foreach(cell, list1) { if (!list_member_oid(list2, lfirst_oid(cell))) result = lappend_oid(result, lfirst_oid(cell)); } check_list_invariants(result); return result; }
Datum kafka_consume_end_all(PG_FUNCTION_ARGS) { HASH_SEQ_STATUS iter; KafkaConsumerProc *proc; List *ids = NIL; ListCell *lc; hash_seq_init(&iter, consumer_procs); while ((proc = (KafkaConsumerProc *) hash_seq_search(&iter)) != NULL) { TerminateBackgroundWorker(&proc->worker); hash_search(consumer_groups, &proc->consumer_id, HASH_REMOVE, NULL); ids = lappend_oid(ids, proc->id); } foreach(lc, ids) { Oid id = lfirst_oid(lc); hash_search(consumer_procs, &id, HASH_REMOVE, NULL); }
/* * Gets list of publication oids for a relation oid. */ List * GetRelationPublications(Oid relid) { List *result = NIL; CatCList *pubrellist; int i; /* Find all publications associated with the relation. */ pubrellist = SearchSysCacheList1(PUBLICATIONRELMAP, ObjectIdGetDatum(relid)); for (i = 0; i < pubrellist->n_members; i++) { HeapTuple tup = &pubrellist->members[i]->tuple; Oid pubid = ((Form_pg_publication_rel) GETSTRUCT(tup))->prpubid; result = lappend_oid(result, pubid); } ReleaseSysCacheList(pubrellist); return result; }
/* * Determine whether a relation can be proven functionally dependent on * a set of grouping columns. If so, return TRUE and add the pg_constraint * OIDs of the constraints needed for the proof to the *constraintDeps list. * * grouping_columns is a list of grouping expressions, in which columns of * the rel of interest are Vars with the indicated varno/varlevelsup. * * Currently we only check to see if the rel has a primary key that is a * subset of the grouping_columns. We could also use plain unique constraints * if all their columns are known not null, but there's a problem: we need * to be able to represent the not-null-ness as part of the constraints added * to *constraintDeps. FIXME whenever not-null constraints get represented * in pg_constraint. */ bool check_functional_grouping(Oid relid, Index varno, Index varlevelsup, List *grouping_columns, List **constraintDeps) { bool result = false; Relation pg_constraint; HeapTuple tuple; SysScanDesc scan; ScanKeyData skey[1]; /* Scan pg_constraint for constraints of the target rel */ pg_constraint = heap_open(ConstraintRelationId, AccessShareLock); ScanKeyInit(&skey[0], Anum_pg_constraint_conrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(relid)); scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true, NULL, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(scan))) { Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple); Datum adatum; bool isNull; ArrayType *arr; int16 *attnums; int numkeys; int i; bool found_col; /* Only PK constraints are of interest for now, see comment above */ if (con->contype != CONSTRAINT_PRIMARY) continue; /* Constraint must be non-deferrable */ if (con->condeferrable) continue; /* Extract the conkey array, ie, attnums of PK's columns */ adatum = heap_getattr(tuple, Anum_pg_constraint_conkey, RelationGetDescr(pg_constraint), &isNull); if (isNull) elog(ERROR, "null conkey for constraint %u", HeapTupleGetOid(tuple)); arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */ numkeys = ARR_DIMS(arr)[0]; if (ARR_NDIM(arr) != 1 || numkeys < 0 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != INT2OID) elog(ERROR, "conkey is not a 1-D smallint array"); attnums = (int16 *) ARR_DATA_PTR(arr); found_col = false; for (i = 0; i < numkeys; i++) { AttrNumber attnum = attnums[i]; ListCell *gl; found_col = false; foreach(gl, grouping_columns) { Var *gvar = (Var *) lfirst(gl); if (IsA(gvar, Var) && gvar->varno == varno && gvar->varlevelsup == varlevelsup && gvar->varattno == attnum) { found_col = true; break; } } if (!found_col) break; } if (found_col) { /* The PK is a subset of grouping_columns, so we win */ *constraintDeps = lappend_oid(*constraintDeps, HeapTupleGetOid(tuple)); result = true; break; } }
Datum binary_upgrade_create_empty_extension(PG_FUNCTION_ARGS) { text *extName; text *schemaName; bool relocatable; text *extVersion; Datum extConfig; Datum extCondition; List *requiredExtensions; CHECK_IS_BINARY_UPGRADE; /* We must check these things before dereferencing the arguments */ if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2) || PG_ARGISNULL(3)) elog(ERROR, "null argument to binary_upgrade_create_empty_extension is not allowed"); extName = PG_GETARG_TEXT_PP(0); schemaName = PG_GETARG_TEXT_PP(1); relocatable = PG_GETARG_BOOL(2); extVersion = PG_GETARG_TEXT_PP(3); if (PG_ARGISNULL(4)) extConfig = PointerGetDatum(NULL); else extConfig = PG_GETARG_DATUM(4); if (PG_ARGISNULL(5)) extCondition = PointerGetDatum(NULL); else extCondition = PG_GETARG_DATUM(5); requiredExtensions = NIL; if (!PG_ARGISNULL(6)) { ArrayType *textArray = PG_GETARG_ARRAYTYPE_P(6); Datum *textDatums; int ndatums; int i; deconstruct_array(textArray, TEXTOID, -1, false, 'i', &textDatums, NULL, &ndatums); for (i = 0; i < ndatums; i++) { text *txtname = DatumGetTextPP(textDatums[i]); char *extName = text_to_cstring(txtname); Oid extOid = get_extension_oid(extName, false); requiredExtensions = lappend_oid(requiredExtensions, extOid); } } InsertExtensionTuple(text_to_cstring(extName), GetUserId(), get_namespace_oid(text_to_cstring(schemaName), false), relocatable, text_to_cstring(extVersion), extConfig, extCondition, requiredExtensions); PG_RETURN_VOID(); }
/* * Alter table space move * * Allows a user to move all of their objects in a given tablespace in the * current database to another tablespace. Only objects which the user is * considered to be an owner of are moved and the user must have CREATE rights * on the new tablespace. These checks should mean that ALTER TABLE will never * fail due to permissions, but note that permissions will also be checked at * that level. Objects can be ALL, TABLES, INDEXES, or MATERIALIZED VIEWS. * * All to-be-moved objects are locked first. If NOWAIT is specified and the * lock can't be acquired then we ereport(ERROR). */ Oid AlterTableSpaceMove(AlterTableSpaceMoveStmt *stmt) { List *relations = NIL; ListCell *l; ScanKeyData key[1]; Relation rel; HeapScanDesc scan; HeapTuple tuple; Oid orig_tablespaceoid; Oid new_tablespaceoid; List *role_oids = roleNamesToIds(stmt->roles); /* Ensure we were not asked to move something we can't */ if (!stmt->move_all && stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX && stmt->objtype != OBJECT_MATVIEW) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("only tables, indexes, and materialized views exist in tablespaces"))); /* Get the orig and new tablespace OIDs */ orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false); new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false); /* Can't move shared relations in to or out of pg_global */ /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */ if (orig_tablespaceoid == GLOBALTABLESPACE_OID || new_tablespaceoid == GLOBALTABLESPACE_OID) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("cannot move relations in to or out of pg_global tablespace"))); /* * Must have CREATE rights on the new tablespace, unless it is the * database default tablespace (which all users implicitly have CREATE * rights on). */ if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace) { AclResult aclresult; aclresult = pg_tablespace_aclcheck(new_tablespaceoid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_TABLESPACE, get_tablespace_name(new_tablespaceoid)); } /* * Now that the checks are done, check if we should set either to * InvalidOid because it is our database's default tablespace. */ if (orig_tablespaceoid == MyDatabaseTableSpace) orig_tablespaceoid = InvalidOid; if (new_tablespaceoid == MyDatabaseTableSpace) new_tablespaceoid = InvalidOid; /* no-op */ if (orig_tablespaceoid == new_tablespaceoid) return new_tablespaceoid; /* * Walk the list of objects in the tablespace and move them. This will * only find objects in our database, of course. */ ScanKeyInit(&key[0], Anum_pg_class_reltablespace, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(orig_tablespaceoid)); rel = heap_open(RelationRelationId, AccessShareLock); scan = heap_beginscan_catalog(rel, 1, key); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { Oid relOid = HeapTupleGetOid(tuple); Form_pg_class relForm; relForm = (Form_pg_class) GETSTRUCT(tuple); /* * Do not move objects in pg_catalog as part of this, if an admin * really wishes to do so, they can issue the individual ALTER * commands directly. * * Also, explicitly avoid any shared tables, temp tables, or TOAST * (TOAST will be moved with the main table). */ if (IsSystemNamespace(relForm->relnamespace) || relForm->relisshared || isAnyTempNamespace(relForm->relnamespace) || relForm->relnamespace == PG_TOAST_NAMESPACE) continue; /* Only consider objects which live in tablespaces */ if (relForm->relkind != RELKIND_RELATION && relForm->relkind != RELKIND_INDEX && relForm->relkind != RELKIND_MATVIEW) continue; /* Check if we were asked to only move a certain type of object */ if (!stmt->move_all && ((stmt->objtype == OBJECT_TABLE && relForm->relkind != RELKIND_RELATION) || (stmt->objtype == OBJECT_INDEX && relForm->relkind != RELKIND_INDEX) || (stmt->objtype == OBJECT_MATVIEW && relForm->relkind != RELKIND_MATVIEW))) continue; /* Check if we are only moving objects owned by certain roles */ if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner)) continue; /* * Handle permissions-checking here since we are locking the tables * and also to avoid doing a bunch of work only to fail part-way. * Note that permissions will also be checked by AlterTableInternal(). * * Caller must be considered an owner on the table to move it. */ if (!pg_class_ownercheck(relOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, NameStr(relForm->relname)); if (stmt->nowait && !ConditionalLockRelationOid(relOid, AccessExclusiveLock)) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("aborting due to \"%s\".\"%s\" --- lock not available", get_namespace_name(relForm->relnamespace), NameStr(relForm->relname)))); else LockRelationOid(relOid, AccessExclusiveLock); /* Add to our list of objects to move */ relations = lappend_oid(relations, relOid); } heap_endscan(scan); heap_close(rel, AccessShareLock); if (relations == NIL) ereport(NOTICE, (errcode(ERRCODE_NO_DATA_FOUND), errmsg("no matching relations in tablespace \"%s\" found", orig_tablespaceoid == InvalidOid ? "(database default)" : get_tablespace_name(orig_tablespaceoid)))); /* Everything is locked, loop through and move all of the relations. */ foreach(l, relations) { List *cmds = NIL; AlterTableCmd *cmd = makeNode(AlterTableCmd); cmd->subtype = AT_SetTableSpace; cmd->name = stmt->new_tablespacename; cmds = lappend(cmds, cmd); AlterTableInternal(lfirst_oid(l), cmds, false); }
/* * find_inheritance_children * * Returns a list containing the OIDs of all relations which * inherit *directly* from the relation with OID 'parentrelId'. * * The specified lock type is acquired on each child relation (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_inheritance_children(Oid parentrelId, LOCKMODE lockmode) { List *list = NIL; Relation relation; SysScanDesc scan; ScanKeyData key[1]; HeapTuple inheritsTuple; Oid inhrelid; Oid *oidarr; int maxoids, numoids, i; /* * Can skip the scan if pg_class shows the relation has never had a * subclass. */ if (!has_subclass(parentrelId)) return NIL; /* * Scan pg_inherits and build a working array of subclass OIDs. */ maxoids = 32; oidarr = (Oid *) palloc(maxoids * sizeof(Oid)); numoids = 0; relation = heap_open(InheritsRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_inherits_inhparent, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(parentrelId)); scan = systable_beginscan(relation, InheritsParentIndexId, true, NULL, 1, key); while ((inheritsTuple = systable_getnext(scan)) != NULL) { inhrelid = ((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhrelid; if (numoids >= maxoids) { maxoids *= 2; oidarr = (Oid *) repalloc(oidarr, maxoids * sizeof(Oid)); } oidarr[numoids++] = inhrelid; } systable_endscan(scan); heap_close(relation, AccessShareLock); /* * If we found more than one child, sort them by OID. This ensures * reasonably consistent behavior regardless of the vagaries of an * indexscan. This is important since we need to be sure all backends * lock children in the same order to avoid needless deadlocks. */ if (numoids > 1) qsort(oidarr, numoids, sizeof(Oid), oid_cmp); /* * Acquire locks and build the result list. */ for (i = 0; i < numoids; i++) { inhrelid = oidarr[i]; if (lockmode != NoLock) { /* Get the lock to synchronize against concurrent drop */ LockRelationOid(inhrelid, lockmode); /* * Now that we have the lock, double-check to see if the relation * really exists or not. If not, assume it was dropped while we * waited to acquire lock, and ignore it. */ if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(inhrelid))) { /* Release useless lock */ UnlockRelationOid(inhrelid, lockmode); /* And ignore this relation */ continue; } } list = lappend_oid(list, inhrelid); } pfree(oidarr); return list; }
/* * 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; } }
/* * 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 Oid get_next_id(Relation rel) { HeapScanDesc scandesc; HeapTuple tup; List *ids_list = NIL; int num_ids; Assert(MAX_CQS % 32 == 0); scandesc = heap_beginscan_catalog(rel, 0, NULL); while ((tup = heap_getnext(scandesc, ForwardScanDirection)) != NULL) { Form_pipeline_query row = (Form_pipeline_query) GETSTRUCT(tup); ids_list = lappend_oid(ids_list, row->id); } heap_endscan(scandesc); num_ids = list_length(ids_list); if (num_ids) { Oid ids[num_ids]; int counts_per_combiner[continuous_query_num_combiners]; int i = 0; Oid max; ListCell *lc; int j; int target_combiner; List *potential_ids; MemSet(counts_per_combiner, 0, sizeof(counts_per_combiner)); foreach(lc, ids_list) { ids[i] = lfirst_oid(lc); counts_per_combiner[ids[i] % continuous_query_num_combiners] += 1; i++; } qsort(ids, num_ids, sizeof(Oid), &compare_oid); if (num_ids == MAX_CQS - 1) /* -1 because 0 is an invalid id */ ereport(ERROR, (errcode(ERRCODE_TOO_MANY_CONTINUOUS_VIEWS), errmsg("maximum number of continuous views exceeded"), errhint("Please drop a existing continuous view before trying to create a new one."))); max = ids[num_ids - 1]; Assert(max >= num_ids); /* * FIXME(usmanm): We do some randomization of ID generation here to make sure that CQs that * are created and dropped in quick succession don't read an event that was not for them. */ /* * Collect any unused ids in [1, max]. */ list_free(ids_list); ids_list = NIL; for (i = 1, j = 0; j < num_ids; i++) { if (ids[j] > i) ids_list = lappend_oid(ids_list, (Oid) i); else j++; } /* * Add all IDs between max and the next multiple of 32. */ j = Min((max / 32 + 1) * 32, MAX_CQS); for (i = max + 1; i < j; i++) ids_list = lappend_oid(ids_list, (Oid) i); /* * Less than 16 options? Throw in some more. */ if (list_length(ids_list) < 16 && j < MAX_CQS) for (i = j; i < j + 32; i++) ids_list = lappend_oid(ids_list, (Oid) i); /* * Figure out the target combiner (one with least IDs allocated) and try to allocate * an ID that belongs to it. */ target_combiner = 0; for (i = 0; i < continuous_query_num_combiners; i++) if (counts_per_combiner[i] < counts_per_combiner[target_combiner]) target_combiner = i; potential_ids = NIL; foreach(lc, ids_list) { Oid id = lfirst_oid(lc); if (id % continuous_query_num_combiners == target_combiner) potential_ids = lappend_oid(potential_ids, id); }
/* * get_op_btree_interpretation * Given an operator's OID, find out which btree opclasses it belongs to, * and what strategy number it has within each one. The results are * returned as an OID list and a parallel integer list. * * In addition to the normal btree operators, we consider a <> operator to be * a "member" of an opclass if its negator is the opclass' equality operator. * ROWCOMPARE_NE is returned as the strategy number for this case. */ void get_op_btree_interpretation(Oid opno, List **opclasses, List **opstrats) { Oid lefttype, righttype; CatCList *catlist; bool op_negated; int i; *opclasses = NIL; *opstrats = NIL; /* * Get the nominal left-hand input type of the operator; we will ignore * opclasses that don't have that as the expected input datatype. This is * a kluge to avoid being confused by binary-compatible opclasses (such as * text_ops and varchar_ops, which share the same operators). */ op_input_types(opno, &lefttype, &righttype); Assert(OidIsValid(lefttype)); /* * Find all the pg_amop entries containing the operator. */ catlist = SearchSysCacheList(AMOPOPID, 1, ObjectIdGetDatum(opno), 0, 0, 0); /* * If we can't find any opclass containing the op, perhaps it is a <> * operator. See if it has a negator that is in an opclass. */ op_negated = false; if (catlist->n_members == 0) { Oid op_negator = get_negator(opno); if (OidIsValid(op_negator)) { op_negated = true; ReleaseSysCacheList(catlist); catlist = SearchSysCacheList(AMOPOPID, 1, ObjectIdGetDatum(op_negator), 0, 0, 0); } } /* Now search the opclasses */ for (i = 0; i < catlist->n_members; i++) { HeapTuple op_tuple = &catlist->members[i]->tuple; Form_pg_amop op_form = (Form_pg_amop) GETSTRUCT(op_tuple); Oid opclass_id; StrategyNumber op_strategy; opclass_id = op_form->amopclaid; /* must be btree */ if (!opclass_is_btree(opclass_id)) continue; /* must match operator input type exactly */ if (get_opclass_input_type(opclass_id) != lefttype) continue; /* Get the operator's btree strategy number */ op_strategy = (StrategyNumber) op_form->amopstrategy; Assert(op_strategy >= 1 && op_strategy <= 5); if (op_negated) { /* Only consider negators that are = */ if (op_strategy != BTEqualStrategyNumber) continue; op_strategy = ROWCOMPARE_NE; } *opclasses = lappend_oid(*opclasses, opclass_id); *opstrats = lappend_int(*opstrats, op_strategy); } ReleaseSysCacheList(catlist); }
/* * nodeRead - * Slightly higher-level reader. * * This routine applies some semantic knowledge on top of the purely * lexical tokenizer pg_strtok(). It can read * * Value token nodes (integers, floats, or strings); * * General nodes (via parseNodeString() from readfuncs.c); * * Lists of the above; * * Lists of integers or OIDs. * The return value is declared void *, not Node *, to avoid having to * cast it explicitly in callers that assign to fields of different types. * * External callers should always pass NULL/0 for the arguments. Internally * a non-NULL token may be passed when the upper recursion level has already * scanned the first token of a node's representation. * * We assume pg_strtok is already initialized with a string to read (hence * this should only be invoked from within a stringToNode operation). */ void * nodeRead(char *token, int tok_len) { Node *result; NodeTag type; if (token == NULL) /* need to read a token? */ { token = pg_strtok(&tok_len); if (token == NULL) /* end of input */ return NULL; } type = nodeTokenType(token, tok_len); switch ((int)type) { case LEFT_BRACE: result = parseNodeString(); token = pg_strtok(&tok_len); /* * CDB: Check for extra fields left over following the ones that * were consumed by the node reader function. If this tree was * read from the catalog, it might have been stored by a future * release which may have added fields that we don't know about. */ while (token && token[0] == ':') { /* * Check for special :prereq tag that a future release may have * inserted to tell us that the node's semantics are not * downward compatible. The node reader function should have * consumed any such tags for features that it supports. If a * :prereq is left to be seen here, that means its feature isn't * implemented in this release and we must reject the statement. * The tag should be followed by a concise feature name or * release id that can be shown to the user in an error message. */ if (tok_len == 7 && memcmp(token, ":prereq", 7) == 0) { token = pg_strtok(&tok_len); token = debackslash(token, tok_len); ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("This operation requires a feature " "called \"%.*s\" which is not " "supported in this version of %s.", tok_len, token, PACKAGE_NAME) )); } /* * Other extra fields can safely be ignored. They are assumed * downward compatible unless a :prereq tag tells us otherwise. */ nodeReadSkip(); ereport(DEBUG2, (errmsg("nodeRead: unknown option '%.*s' ignored", tok_len, token), errdetail("Skipped '%.*s' at offset %d in %s", (int)(pg_strtok_ptr - token), token, (int)(token - pg_strtok_begin), pg_strtok_begin) )); token = pg_strtok(&tok_len); } if (token == NULL ) elog(ERROR, "did not find '}' at end of input node"); if (token[0] != '}') elog(ERROR, "did not find '}' at end of input node, instead found %s",token); break; case LEFT_PAREN: { List *l = NIL; /*---------- * Could be an integer list: (i int int ...) * or an OID list: (o int int ...) * or a list of nodes/values: (node node ...) *---------- */ token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (tok_len == 1 && token[0] == 'i') { /* List of integers */ for (;;) { int val; char *endptr; token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (token[0] == ')') break; val = (int) strtol(token, &endptr, 10); if (endptr != token + tok_len) elog(ERROR, "unrecognized integer: \"%.*s\"", tok_len, token); l = lappend_int(l, val); } } else if (tok_len == 1 && token[0] == 'o') { /* List of OIDs */ for (;;) { Oid val; char *endptr; token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (token[0] == ')') break; val = (Oid) strtoul(token, &endptr, 10); if (endptr != token + tok_len) elog(ERROR, "unrecognized OID: \"%.*s\"", tok_len, token); l = lappend_oid(l, val); } } else { /* List of other node types */ for (;;) { /* We have already scanned next token... */ if (token[0] == ')') break; l = lappend(l, nodeRead(token, tok_len)); token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); } } result = (Node *) l; break; } case RIGHT_PAREN: elog(ERROR, "unexpected right parenthesis"); result = NULL; /* keep compiler happy */ break; case OTHER_TOKEN: if (tok_len == 0) { /* must be "<>" --- represents a null pointer */ result = NULL; } else { elog(ERROR, "unrecognized token: \"%.*s\"", tok_len, token); result = NULL; /* keep compiler happy */ } break; case T_Integer: /* * we know that the token terminates on a char atol will stop at */ result = (Node *) makeInteger(atol(token)); break; case T_Float: { char *fval = (char *) palloc(tok_len + 1); memcpy(fval, token, tok_len); fval[tok_len] = '\0'; result = (Node *) makeFloat(fval); } break; case T_String: /* need to remove leading and trailing quotes, and backslashes */ result = (Node *) makeString(debackslash(token + 1, tok_len - 2)); break; case T_BitString: { char *val = palloc(tok_len); /* skip leading 'b' */ memcpy(val, token + 1, tok_len - 1); val[tok_len - 1] = '\0'; result = (Node *) makeBitString(val); break; } default: elog(ERROR, "unrecognized node type: %d", (int) type); result = NULL; /* keep compiler happy */ break; } return (void *) result; }
/* * purge_dropped_db_segments */ static void purge_dropped_db_segments(bool force) { static TimestampTz last_purge_time = 0; List *db_oids; List *dbs_to_remove = NIL; HASH_SEQ_STATUS status; broker_db_meta *db_meta; if (!force && !TimestampDifferenceExceeds(last_purge_time, GetCurrentTimestamp(), 10 * 1000)) /* 10s */ return; db_oids = get_database_oids(); LWLockAcquire(IPCMessageBrokerIndexLock, LW_SHARED); hash_seq_init(&status, broker_meta->db_meta_hash); while ((db_meta = (broker_db_meta *) hash_seq_search(&status)) != NULL) { bool found = false; ListCell *lc; foreach(lc, db_oids) { if (lfirst_oid(lc) == db_meta->dbid) { found = true; break; } } if (!found) dbs_to_remove = lappend_oid(dbs_to_remove, db_meta->dbid); } LWLockRelease(IPCMessageBrokerIndexLock); if (list_length(dbs_to_remove)) { ListCell *lc; LWLockAcquire(IPCMessageBrokerIndexLock, LW_EXCLUSIVE); foreach(lc, dbs_to_remove) { Oid dbid = lfirst_oid(lc); bool found; db_meta = hash_search(broker_meta->db_meta_hash, &dbid, HASH_FIND, &found); Assert(found); Assert(db_meta->handle > 0); /* detach from main db segment */ if (db_meta->segment) dsm_detach(db_meta->segment); if (db_meta->lqueues) { int i; for (i = 0; i < continuous_query_num_workers; i++) { local_queue *local_buf = &db_meta->lqueues[i]; if (local_buf->slots) list_free_deep(local_buf->slots); } pfree(db_meta->lqueues); } hash_search(broker_meta->db_meta_hash, &dbid, HASH_REMOVE, &found); Assert(found); } mark_unused_locks_as_free(db_oids); LWLockRelease(IPCMessageBrokerIndexLock); }
/* AddUpdResqueueCapabilityEntryInternal: * * Internal function to add a new entry to pg_resqueuecapability, or * update an existing one. Key cols are queueid, restypint. If * old_tuple is set (ie not InvalidOid), the update the ressetting column, * else insert a new row. * */ static List * AddUpdResqueueCapabilityEntryInternal( cqContext *pcqCtx, List *stmtOptIdList, Oid queueid, int resTypeInt, char *pResSetting, Relation rel, HeapTuple old_tuple) { HeapTuple new_tuple; Datum values[Natts_pg_resqueuecapability]; bool isnull[Natts_pg_resqueuecapability]; bool new_record_repl[Natts_pg_resqueuecapability]; MemSet(isnull, 0, sizeof(bool) * Natts_pg_resqueuecapability); MemSet(new_record_repl, 0, sizeof(bool) * Natts_pg_resqueuecapability); values[Anum_pg_resqueuecapability_resqueueid - 1] = ObjectIdGetDatum(queueid); values[Anum_pg_resqueuecapability_restypid - 1] = resTypeInt; Assert(pResSetting); values[Anum_pg_resqueuecapability_ressetting - 1] = CStringGetTextDatum(pResSetting); /* set this column to update */ new_record_repl[Anum_pg_resqueuecapability_ressetting - 1] = true; ValidateResqueueCapabilityEntry(resTypeInt, pResSetting); if (HeapTupleIsValid(old_tuple)) { new_tuple = caql_modify_current(pcqCtx, values, isnull, new_record_repl); caql_update_current(pcqCtx, new_tuple); /* and Update indexes (implicit) */ } else { Oid s1; new_tuple = caql_form_tuple(pcqCtx, values, isnull); /* MPP-11858: synchronize the oids for CREATE/ALTER options... */ if ((Gp_role != GP_ROLE_DISPATCH) && list_length(stmtOptIdList)) { Oid s2 = list_nth_oid(stmtOptIdList, 0); stmtOptIdList = list_delete_first(stmtOptIdList); if (OidIsValid(s2)) HeapTupleSetOid(new_tuple, s2); } s1 = caql_insert(pcqCtx, new_tuple); /* and Update indexes (implicit) */ if (Gp_role == GP_ROLE_DISPATCH) { stmtOptIdList = lappend_oid(stmtOptIdList, s1); } } if (HeapTupleIsValid(old_tuple)) heap_freetuple(new_tuple); return stmtOptIdList; } /* end AddUpdResqueueCapabilityEntryInternal */
/* * nodeRead - * Slightly higher-level reader. * * This routine applies some semantic knowledge on top of the purely * lexical tokenizer pg_strtok(). It can read * * Value token nodes (integers, floats, or strings); * * General nodes (via parseNodeString() from readfuncs.c); * * Lists of the above; * * Lists of integers or OIDs. * The return value is declared void *, not Node *, to avoid having to * cast it explicitly in callers that assign to fields of different types. * * External callers should always pass NULL/0 for the arguments. Internally * a non-NULL token may be passed when the upper recursion level has already * scanned the first token of a node's representation. * * We assume pg_strtok is already initialized with a string to read (hence * this should only be invoked from within a stringToNode operation). */ void * nodeRead(char *token, int tok_len) { Node *result; NodeTag type; if (token == NULL) /* need to read a token? */ { token = pg_strtok(&tok_len); if (token == NULL) /* end of input */ return NULL; } type = nodeTokenType(token, tok_len); switch (type) { case LEFT_BRACE: result = parseNodeString(); token = pg_strtok(&tok_len); if (token == NULL || token[0] != '}') elog(ERROR, "did not find '}' at end of input node"); break; case LEFT_PAREN: { List *l = NIL; /*---------- * Could be an integer list: (i int int ...) * or an OID list: (o int int ...) * or a list of nodes/values: (node node ...) *---------- */ token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (tok_len == 1 && token[0] == 'i') { /* List of integers */ for (;;) { int val; char *endptr; token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (token[0] == ')') break; val = (int) strtol(token, &endptr, 10); if (endptr != token + tok_len) elog(ERROR, "unrecognized integer: \"%.*s\"", tok_len, token); l = lappend_int(l, val); } } else if (tok_len == 1 && token[0] == 'o') { /* List of OIDs */ for (;;) { Oid val; char *endptr; token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); if (token[0] == ')') break; val = (Oid) strtoul(token, &endptr, 10); if (endptr != token + tok_len) elog(ERROR, "unrecognized OID: \"%.*s\"", tok_len, token); l = lappend_oid(l, val); } } else { /* List of other node types */ for (;;) { /* We have already scanned next token... */ if (token[0] == ')') break; l = lappend(l, nodeRead(token, tok_len)); token = pg_strtok(&tok_len); if (token == NULL) elog(ERROR, "unterminated List structure"); } } result = (Node *) l; break; } case RIGHT_PAREN: elog(ERROR, "unexpected right parenthesis"); result = NULL; /* keep compiler happy */ break; case OTHER_TOKEN: if (tok_len == 0) { /* must be "<>" --- represents a null pointer */ result = NULL; } else { elog(ERROR, "unrecognized token: \"%.*s\"", tok_len, token); result = NULL; /* keep compiler happy */ } break; case T_Integer: /* * we know that the token terminates on a char atol will stop at */ result = (Node *) makeInteger(atol(token)); break; case T_Float: { char *fval = (char *) palloc(tok_len + 1); memcpy(fval, token, tok_len); fval[tok_len] = '\0'; result = (Node *) makeFloat(fval); } break; case T_String: /* need to remove leading and trailing quotes, and backslashes */ result = (Node *) makeString(debackslash(token + 1, tok_len - 2)); break; case T_BitString: { char *val = palloc(tok_len); /* skip leading 'b' */ strncpy(val, token + 1, tok_len - 1); val[tok_len - 1] = '\0'; result = (Node *) makeBitString(val); break; } default: elog(ERROR, "unrecognized node type: %d", (int) type); result = NULL; /* keep compiler happy */ break; } return (void *) result; }