/* * LockRelationDistributionMetadata returns after getting a the lock used for a * relation's distribution metadata, blocking if required. Only ExclusiveLock * and ShareLock modes are supported. Any locks acquired using this method are * released at transaction end. */ void LockRelationDistributionMetadata(Oid relationId, LOCKMODE lockMode) { Assert(lockMode == ExclusiveLock || lockMode == ShareLock); (void) LockRelationOid(relationId, lockMode); }
/* * Open the sequence and acquire AccessShareLock if needed * * If we haven't touched the sequence already in this transaction, * we need to acquire AccessShareLock. We arrange for the lock to * be owned by the top transaction, so that we don't need to do it * more than once per xact. */ static Relation open_share_lock(SeqTable seq) { LocalTransactionId thislxid = MyProc->lxid; /* Get the lock if not already held in this xact */ if (seq->lxid != thislxid) { ResourceOwner currentOwner; currentOwner = CurrentResourceOwner; PG_TRY(); { CurrentResourceOwner = TopTransactionResourceOwner; LockRelationOid(seq->relid, AccessShareLock); } PG_CATCH(); { /* Ensure CurrentResourceOwner is restored on error */ CurrentResourceOwner = currentOwner; PG_RE_THROW(); } PG_END_TRY(); CurrentResourceOwner = currentOwner; /* Flag that we have a lock in the current xact */ seq->lxid = thislxid; } /* We now know we have AccessShareLock, and can safely open the rel */ return relation_open(seq->relid, NoLock); }
/* * master_drop_all_shards attempts to drop all shards for a given relation. * Unlike master_apply_delete_command, this function can be called even * if the table has already been dropped. */ Datum master_drop_all_shards(PG_FUNCTION_ARGS) { Oid relationId = PG_GETARG_OID(0); text *schemaNameText = PG_GETARG_TEXT_P(1); text *relationNameText = PG_GETARG_TEXT_P(2); List *shardIntervalList = NIL; int droppedShardCount = 0; char *schemaName = text_to_cstring(schemaNameText); char *relationName = text_to_cstring(relationNameText); CheckCitusVersion(ERROR); /* * The SQL_DROP trigger calls this function even for tables that are * not distributed. In that case, silently ignore and return -1. */ if (!IsDistributedTable(relationId) || !EnableDDLPropagation) { PG_RETURN_INT32(-1); } EnsureCoordinator(); CheckTableSchemaNameForDrop(relationId, &schemaName, &relationName); /* * master_drop_all_shards is typically called from the DROP TABLE trigger, * but could be called by a user directly. Make sure we have an * AccessExlusiveLock to prevent any other commands from running on this table * concurrently. */ LockRelationOid(relationId, AccessExclusiveLock); shardIntervalList = LoadShardIntervalList(relationId); droppedShardCount = DropShards(relationId, schemaName, relationName, shardIntervalList); PG_RETURN_INT32(droppedShardCount); }
/** * @fn Datum get_table_and_inheritors(PG_FUNCTION_ARGS) * @brief Return array containing Oids of parent table and its children. * Note that this function does not release relation locks. * * get_table_and_inheritors(table) * * @param table parent table. * @retval regclass[] */ Datum repack_get_table_and_inheritors(PG_FUNCTION_ARGS) { Oid parent = PG_GETARG_OID(0); List *relations; Datum *relations_array; int relations_array_size; ArrayType *result; ListCell *lc; int i; LockRelationOid(parent, AccessShareLock); /* Check that parent table exists */ if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(parent))) PG_RETURN_ARRAYTYPE_P(construct_empty_array(OIDOID)); /* Also check that children exist */ relations = find_all_inheritors(parent, AccessShareLock, NULL); relations_array_size = list_length(relations); if (relations_array_size == 0) PG_RETURN_ARRAYTYPE_P(construct_empty_array(OIDOID)); relations_array = palloc(relations_array_size * sizeof(Datum)); i = 0; foreach (lc, relations) relations_array[i++] = ObjectIdGetDatum(lfirst_oid(lc)); result = construct_array(relations_array, relations_array_size, OIDOID, sizeof(Oid), true, 'i'); pfree(relations_array); PG_RETURN_ARRAYTYPE_P(result); }
/* * RecoverPreparedTransactions recovers any pending prepared * transactions started by this node on other nodes. */ static int RecoverPreparedTransactions(void) { List *workerList = NIL; ListCell *workerNodeCell = NULL; int recoveredTransactionCount = 0; /* * We block here if metadata transactions are ongoing, since we * mustn't commit/abort their prepared transactions under their * feet. We also prevent concurrent recovery. */ LockRelationOid(DistTransactionRelationId(), ExclusiveLock); workerList = WorkerNodeList(); foreach(workerNodeCell, workerList) { WorkerNode *workerNode = (WorkerNode *) lfirst(workerNodeCell); recoveredTransactionCount += RecoverWorkerTransactions(workerNode); }
/* * InitCatCachePhase2 -- external interface for CatalogCacheInitializeCache * * One reason to call this routine is to ensure that the relcache has * created entries for all the catalogs and indexes referenced by catcaches. * Therefore, provide an option to open the index as well as fixing the * cache itself. An exception is the indexes on pg_am, which we don't use * (cf. IndexScanOK). */ void InitCatCachePhase2(CatCache *cache, bool touch_index) { if (cache->cc_tupdesc == NULL) CatalogCacheInitializeCache(cache); if (touch_index && cache->id != AMOID && cache->id != AMNAME) { Relation idesc; /* * We must lock the underlying catalog before opening the index to * avoid deadlock, since index_open could possibly result in reading * this same catalog, and if anyone else is exclusive-locking this * catalog and index they'll be doing it in that order. */ LockRelationOid(cache->cc_reloid, AccessShareLock); idesc = index_open(cache->cc_indexoid, AccessShareLock); index_close(idesc, AccessShareLock); UnlockRelationOid(cache->cc_reloid, AccessShareLock); } }
/* * 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; }
/* * CreateShardsWithRoundRobinPolicy creates empty shards for the given table * based on the specified number of initial shards. The function first updates * metadata on the coordinator node to make this shard (and its placements) * visible. Note that the function assumes the table is hash partitioned and * calculates the min/max hash token ranges for each shard, giving them an equal * split of the hash space. Finally, function creates empty shard placements on * worker nodes. */ void CreateShardsWithRoundRobinPolicy(Oid distributedTableId, int32 shardCount, int32 replicationFactor, bool useExclusiveConnections) { char shardStorageType = 0; List *workerNodeList = NIL; int32 workerNodeCount = 0; uint32 placementAttemptCount = 0; uint64 hashTokenIncrement = 0; List *existingShardList = NIL; int64 shardIndex = 0; DistTableCacheEntry *cacheEntry = DistributedTableCacheEntry(distributedTableId); bool colocatedShard = false; List *insertedShardPlacements = NIL; /* make sure table is hash partitioned */ CheckHashPartitionedTable(distributedTableId); /* * In contrast to append/range partitioned tables it makes more sense to * require ownership privileges - shards for hash-partitioned tables are * only created once, not continually during ingest as for the other * partitioning types. */ EnsureTableOwner(distributedTableId); /* we plan to add shards: get an exclusive lock on relation oid */ LockRelationOid(distributedTableId, ExclusiveLock); /* validate that shards haven't already been created for this table */ existingShardList = LoadShardList(distributedTableId); if (existingShardList != NIL) { char *tableName = get_rel_name(distributedTableId); ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("table \"%s\" has already had shards created for it", tableName))); } /* make sure that at least one shard is specified */ if (shardCount <= 0) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("shard_count must be positive"))); } /* make sure that at least one replica is specified */ if (replicationFactor <= 0) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("replication_factor must be positive"))); } /* make sure that RF=1 if the table is streaming replicated */ if (cacheEntry->replicationModel == REPLICATION_MODEL_STREAMING && replicationFactor > 1) { char *relationName = get_rel_name(cacheEntry->relationId); ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("using replication factor %d with the streaming " "replication model is not supported", replicationFactor), errdetail("The table %s is marked as streaming replicated and " "the shard replication factor of streaming replicated " "tables must be 1.", relationName), errhint("Use replication factor 1."))); } /* calculate the split of the hash space */ hashTokenIncrement = HASH_TOKEN_COUNT / shardCount; /* don't allow concurrent node list changes that require an exclusive lock */ LockRelationOid(DistNodeRelationId(), RowShareLock); /* load and sort the worker node list for deterministic placement */ workerNodeList = ActivePrimaryNodeList(); workerNodeList = SortList(workerNodeList, CompareWorkerNodes); /* make sure we don't process cancel signals until all shards are created */ HOLD_INTERRUPTS(); workerNodeCount = list_length(workerNodeList); if (replicationFactor > workerNodeCount) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("replication_factor (%d) exceeds number of worker nodes " "(%d)", replicationFactor, workerNodeCount), errhint("Add more worker nodes or try again with a lower " "replication factor."))); } /* if we have enough nodes, add an extra placement attempt for backup */ placementAttemptCount = (uint32) replicationFactor; if (workerNodeCount > replicationFactor) { placementAttemptCount++; } /* set shard storage type according to relation type */ shardStorageType = ShardStorageType(distributedTableId); for (shardIndex = 0; shardIndex < shardCount; shardIndex++) { uint32 roundRobinNodeIndex = shardIndex % workerNodeCount; /* initialize the hash token space for this shard */ text *minHashTokenText = NULL; text *maxHashTokenText = NULL; int32 shardMinHashToken = INT32_MIN + (shardIndex * hashTokenIncrement); int32 shardMaxHashToken = shardMinHashToken + (hashTokenIncrement - 1); uint64 shardId = GetNextShardId(); List *currentInsertedShardPlacements = NIL; /* if we are at the last shard, make sure the max token value is INT_MAX */ if (shardIndex == (shardCount - 1)) { shardMaxHashToken = INT32_MAX; } /* insert the shard metadata row along with its min/max values */ minHashTokenText = IntegerToText(shardMinHashToken); maxHashTokenText = IntegerToText(shardMaxHashToken); /* * Grabbing the shard metadata lock isn't technically necessary since * we already hold an exclusive lock on the partition table, but we'll * acquire it for the sake of completeness. As we're adding new active * placements, the mode must be exclusive. */ LockShardDistributionMetadata(shardId, ExclusiveLock); InsertShardRow(distributedTableId, shardId, shardStorageType, minHashTokenText, maxHashTokenText); currentInsertedShardPlacements = InsertShardPlacementRows(distributedTableId, shardId, workerNodeList, roundRobinNodeIndex, replicationFactor); insertedShardPlacements = list_concat(insertedShardPlacements, currentInsertedShardPlacements); } CreateShardsOnWorkers(distributedTableId, insertedShardPlacements, useExclusiveConnections, colocatedShard); if (QueryCancelPending) { ereport(WARNING, (errmsg("cancel requests are ignored during shard creation"))); QueryCancelPending = false; } RESUME_INTERRUPTS(); }
/* * CreateColocatedShards creates shards for the target relation colocated with * the source relation. */ void CreateColocatedShards(Oid targetRelationId, Oid sourceRelationId, bool useExclusiveConnections) { char targetShardStorageType = 0; List *existingShardList = NIL; List *sourceShardIntervalList = NIL; ListCell *sourceShardCell = NULL; bool colocatedShard = true; List *insertedShardPlacements = NIL; /* make sure that tables are hash partitioned */ CheckHashPartitionedTable(targetRelationId); CheckHashPartitionedTable(sourceRelationId); /* * In contrast to append/range partitioned tables it makes more sense to * require ownership privileges - shards for hash-partitioned tables are * only created once, not continually during ingest as for the other * partitioning types. */ EnsureTableOwner(targetRelationId); /* we plan to add shards: get an exclusive lock on target relation oid */ LockRelationOid(targetRelationId, ExclusiveLock); /* we don't want source table to get dropped before we colocate with it */ LockRelationOid(sourceRelationId, AccessShareLock); /* prevent placement changes of the source relation until we colocate with them */ sourceShardIntervalList = LoadShardIntervalList(sourceRelationId); LockShardListMetadata(sourceShardIntervalList, ShareLock); /* validate that shards haven't already been created for this table */ existingShardList = LoadShardList(targetRelationId); if (existingShardList != NIL) { char *targetRelationName = get_rel_name(targetRelationId); ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("table \"%s\" has already had shards created for it", targetRelationName))); } targetShardStorageType = ShardStorageType(targetRelationId); foreach(sourceShardCell, sourceShardIntervalList) { ShardInterval *sourceShardInterval = (ShardInterval *) lfirst(sourceShardCell); uint64 sourceShardId = sourceShardInterval->shardId; uint64 newShardId = GetNextShardId(); ListCell *sourceShardPlacementCell = NULL; int32 shardMinValue = DatumGetInt32(sourceShardInterval->minValue); int32 shardMaxValue = DatumGetInt32(sourceShardInterval->maxValue); text *shardMinValueText = IntegerToText(shardMinValue); text *shardMaxValueText = IntegerToText(shardMaxValue); List *sourceShardPlacementList = ShardPlacementList(sourceShardId); InsertShardRow(targetRelationId, newShardId, targetShardStorageType, shardMinValueText, shardMaxValueText); foreach(sourceShardPlacementCell, sourceShardPlacementList) { ShardPlacement *sourcePlacement = (ShardPlacement *) lfirst(sourceShardPlacementCell); uint32 groupId = sourcePlacement->groupId; const RelayFileState shardState = FILE_FINALIZED; const uint64 shardSize = 0; uint64 shardPlacementId = 0; ShardPlacement *shardPlacement = NULL; /* * Optimistically add shard placement row the pg_dist_shard_placement, in case * of any error it will be roll-backed. */ shardPlacementId = InsertShardPlacementRow(newShardId, INVALID_PLACEMENT_ID, shardState, shardSize, groupId); shardPlacement = LoadShardPlacement(newShardId, shardPlacementId); insertedShardPlacements = lappend(insertedShardPlacements, shardPlacement); }
/* * ExecRefreshMatView -- execute a REFRESH MATERIALIZED VIEW command * * This refreshes the materialized view by creating a new table and swapping * the relfilenodes of the new table and the old materialized view, so the OID * of the original materialized view is preserved. Thus we do not lose GRANT * nor references to this materialized view. * * If WITH NO DATA was specified, this is effectively like a TRUNCATE; * otherwise it is like a TRUNCATE followed by an INSERT using the SELECT * statement associated with the materialized view. The statement node's * skipData field shows whether the clause was used. * * Indexes are rebuilt too, via REINDEX. Since we are effectively bulk-loading * the new heap, it's better to create the indexes afterwards than to fill them * incrementally while we load. * * The matview's "populated" state is changed based on whether the contents * reflect the result set of the materialized view's query. */ void ExecRefreshMatView(RefreshMatViewStmt *stmt, const char *queryString, ParamListInfo params, char *completionTag) { Oid matviewOid; Relation matviewRel; RewriteRule *rule; List *actions; Query *dataQuery; Oid tableSpace; Oid relowner; Oid OIDNewHeap; DestReceiver *dest; bool concurrent; LOCKMODE lockmode; Oid save_userid; int save_sec_context; int save_nestlevel; /* Determine strength of lock needed. */ concurrent = stmt->concurrent; lockmode = concurrent ? ExclusiveLock : AccessExclusiveLock; /* * Get a lock until end of transaction. */ matviewOid = RangeVarGetRelidExtended(stmt->relation, lockmode, false, false, RangeVarCallbackOwnsTable, NULL); matviewRel = heap_open(matviewOid, NoLock); /* Make sure it is a materialized view. */ if (matviewRel->rd_rel->relkind != RELKIND_MATVIEW) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("\"%s\" is not a materialized view", RelationGetRelationName(matviewRel)))); /* Check that CONCURRENTLY is not specified if not populated. */ if (concurrent && !RelationIsPopulated(matviewRel)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CONCURRENTLY cannot be used when the materialized view is not populated"))); /* Check that conflicting options have not been specified. */ if (concurrent && stmt->skipData) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("CONCURRENTLY and WITH NO DATA options cannot be used together"))); /* We don't allow an oid column for a materialized view. */ Assert(!matviewRel->rd_rel->relhasoids); /* * Check that everything is correct for a refresh. Problems at this point * are internal errors, so elog is sufficient. */ if (matviewRel->rd_rel->relhasrules == false || matviewRel->rd_rules->numLocks < 1) elog(ERROR, "materialized view \"%s\" is missing rewrite information", RelationGetRelationName(matviewRel)); if (matviewRel->rd_rules->numLocks > 1) elog(ERROR, "materialized view \"%s\" has too many rules", RelationGetRelationName(matviewRel)); rule = matviewRel->rd_rules->rules[0]; if (rule->event != CMD_SELECT || !(rule->isInstead)) elog(ERROR, "the rule for materialized view \"%s\" is not a SELECT INSTEAD OF rule", RelationGetRelationName(matviewRel)); actions = rule->actions; if (list_length(actions) != 1) elog(ERROR, "the rule for materialized view \"%s\" is not a single action", RelationGetRelationName(matviewRel)); /* * The stored query was rewritten at the time of the MV definition, but * has not been scribbled on by the planner. */ dataQuery = (Query *) linitial(actions); Assert(IsA(dataQuery, Query)); /* * Check for active uses of the relation in the current transaction, such * as open scans. * * NB: We count on this to protect us against problems with refreshing the * data using HEAP_INSERT_FROZEN. */ CheckTableNotInUse(matviewRel, "REFRESH MATERIALIZED VIEW"); /* * Tentatively mark the matview as populated or not (this will roll back * if we fail later). */ SetMatViewPopulatedState(matviewRel, !stmt->skipData); relowner = matviewRel->rd_rel->relowner; /* * Switch to the owner's userid, so that any functions are run as that * user. Also arrange to make GUC variable changes local to this command. * Don't lock it down too tight to create a temporary table just yet. We * will switch modes when we are about to execute user code. */ GetUserIdAndSecContext(&save_userid, &save_sec_context); SetUserIdAndSecContext(relowner, save_sec_context | SECURITY_LOCAL_USERID_CHANGE); save_nestlevel = NewGUCNestLevel(); /* Concurrent refresh builds new data in temp tablespace, and does diff. */ if (concurrent) tableSpace = GetDefaultTablespace(RELPERSISTENCE_TEMP); else tableSpace = matviewRel->rd_rel->reltablespace; /* * Create the transient table that will receive the regenerated data. Lock * it against access by any other process until commit (by which time it * will be gone). */ OIDNewHeap = make_new_heap(matviewOid, tableSpace, concurrent, ExclusiveLock); LockRelationOid(OIDNewHeap, AccessExclusiveLock); dest = CreateTransientRelDestReceiver(OIDNewHeap); /* * Now lock down security-restricted operations. */ SetUserIdAndSecContext(relowner, save_sec_context | SECURITY_RESTRICTED_OPERATION); /* Generate the data, if wanted. */ if (!stmt->skipData) refresh_matview_datafill(dest, dataQuery, queryString); heap_close(matviewRel, NoLock); /* Make the matview match the newly generated data. */ if (concurrent) { int old_depth = matview_maintenance_depth; PG_TRY(); { refresh_by_match_merge(matviewOid, OIDNewHeap, relowner, save_sec_context); } PG_CATCH(); { matview_maintenance_depth = old_depth; PG_RE_THROW(); } PG_END_TRY(); Assert(matview_maintenance_depth == old_depth); } else refresh_by_heap_swap(matviewOid, OIDNewHeap); /* Roll back any GUC changes */ AtEOXact_GUC(false, save_nestlevel); /* Restore userid and security context */ SetUserIdAndSecContext(save_userid, save_sec_context); }
void InitResQueues(void) { HeapTuple tuple; int numQueues = 0; bool queuesok = true; cqContext *pcqCtx; cqContext cqc; Assert(ResScheduler); /* * Need a resource owner to keep the heapam code happy. */ Assert(CurrentResourceOwner == NULL); ResourceOwner owner = ResourceOwnerCreate(NULL, "InitQueues"); CurrentResourceOwner = owner; /** * The resqueue shared mem initialization must be serialized. Only the first session * should do the init. * Serialization is done the ResQueueLock LW_EXCLUSIVE. However, we must obtain all DB * lock before obtaining LWlock. * So, we must have obtained ResQueueRelationId and ResQueueCapabilityRelationId lock * first. */ Relation relResqueue = heap_open(ResQueueRelationId, AccessShareLock); LockRelationOid(ResQueueCapabilityRelationId, RowExclusiveLock); LWLockAcquire(ResQueueLock, LW_EXCLUSIVE); if (ResScheduler->num_queues > 0) { /* Hash table has already been loaded */ LWLockRelease(ResQueueLock); UnlockRelationOid(ResQueueCapabilityRelationId, RowExclusiveLock); heap_close(relResqueue, AccessShareLock); CurrentResourceOwner = NULL; ResourceOwnerDelete(owner); return; } /* XXX XXX: should this be rowexclusive ? */ pcqCtx = caql_beginscan( caql_indexOK( caql_addrel(cqclr(&cqc), relResqueue), false), cql("SELECT * FROM pg_resqueue ", NULL)); while (HeapTupleIsValid(tuple = caql_getnext(pcqCtx))) { Form_pg_resqueue queueform; Oid queueid; bool overcommit; float4 ignorelimit; Cost thresholds[NUM_RES_LIMIT_TYPES]; char *queuename; numQueues++; queueform = (Form_pg_resqueue) GETSTRUCT(tuple); queueid = HeapTupleGetOid(tuple); queuename = NameStr(queueform->rsqname); thresholds[RES_COUNT_LIMIT] = queueform->rsqcountlimit; thresholds[RES_COST_LIMIT] = queueform->rsqcostlimit; thresholds[RES_MEMORY_LIMIT] = ResourceQueueGetMemoryLimit(queueid); overcommit = queueform->rsqovercommit; ignorelimit = queueform->rsqignorecostlimit; queuesok = ResCreateQueue(queueid, thresholds, overcommit, ignorelimit); if (!queuesok) { /** Break out of loop. Close relations, relinquish LWLock and then error out */ break; } } caql_endscan(pcqCtx); LWLockRelease(ResQueueLock); UnlockRelationOid(ResQueueCapabilityRelationId, RowExclusiveLock); heap_close(relResqueue, AccessShareLock); if (!queuesok) ereport(PANIC, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("insufficient resource queues available"), errhint("Increase max_resource_queues to %d.", numQueues))); elog(LOG,"initialized %d resource queues", numQueues); CurrentResourceOwner = NULL; ResourceOwnerDelete(owner); return; }
/* * 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); }