blob: 9be40eba69f40d0295f1e2f9344de130ab997ec0 [file]
/*-------------------------------------------------------------------------
*
* resgroupcmds.c
* Commands for manipulating resource group.
*
* Portions Copyright (c) 2006-2017, Greenplum inc.
* Portions Copyright (c) 2012-Present VMware, Inc. or its affiliates.
*
* IDENTIFICATION
* src/backend/commands/resgroupcmds.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <math.h>
#include "funcapi.h"
#include "libpq-fe.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/xact.h"
#include "catalog/heap.h"
#include "catalog/oid_dispatch.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_resgroup.h"
#include "catalog/pg_resgroupcapability.h"
#include "cdb/cdbdisp_query.h"
#include "cdb/cdbdispatchresult.h"
#include "cdb/cdbvars.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/tablespace.h"
#include "commands/resgroupcmds.h"
#include "miscadmin.h"
#include "nodes/pg_list.h"
#include "utils/builtins.h"
#include "utils/datetime.h"
#include "utils/fmgroids.h"
#include "utils/palloc.h"
#include "utils/resgroup.h"
#include "utils/cgroup.h"
#include "utils/resource_manager.h"
#include "utils/resowner.h"
#include "utils/syscache.h"
#include "utils/faultinjector.h"
#include "catalog/gp_indexing.h"
#define RESGROUP_DEFAULT_CONCURRENCY (20)
#define RESGROUP_DEFAULT_CPU_WEIGHT (100)
#define RESGROUP_MIN_CONCURRENCY (0)
#define RESGROUP_MAX_CONCURRENCY (MaxConnections)
#define RESGROUP_MAX_CPU_MAX_PERCENT (100)
#define RESGROUP_MIN_CPU_MAX_PERCENT (1)
#define RESGROUP_MIN_CPU_WEIGHT (1)
#define RESGROUP_MAX_CPU_WEIGHT (500)
#define RESGROUP_MIN_MEMORY_QUOTA (0)
#define RESGROUP_DEFAULT_MEMORY_QUOTA (-1)
#define RESGROUP_MIN_MIN_COST (0)
static int str2Int(const char *str, const char *prop);
static ResGroupLimitType getResgroupOptionType(const char* defname);
static ResGroupCap getResgroupOptionValue(DefElem *defel);
static const char *getResgroupOptionName(ResGroupLimitType type);
static void checkResgroupCapLimit(ResGroupLimitType type, ResGroupCap value);
static void parseStmtOptions(CreateResourceGroupStmt *stmt, ResGroupCaps *caps);
static void validateCapabilities(Relation rel, Oid groupid, ResGroupCaps *caps, bool newGroup);
static void insertResgroupCapabilityEntry(Relation rel, Oid groupid, uint16 type, const char *value);
static void updateResgroupCapabilityEntry(Relation rel,
Oid groupId,
ResGroupLimitType limitType,
ResGroupCap value,
const char *strValue);
static void insertResgroupCapabilities(Relation rel, Oid groupId, ResGroupCaps *caps);
static void deleteResgroupCapabilities(Oid groupid);
static void checkAuthIdForDrop(Oid groupId);
static void createResgroupCallback(XactEvent event, void *arg);
static void dropResgroupCallback(XactEvent event, void *arg);
static void alterResgroupCallback(XactEvent event, void *arg);
static void checkCpusetSyntax(const char *cpuset);
/*
* CREATE RESOURCE GROUP
*/
void
CreateResourceGroup(CreateResourceGroupStmt *stmt)
{
Relation pg_resgroup_rel;
Relation pg_resgroupcapability_rel;
TupleDesc pg_resgroup_dsc;
ScanKeyData scankey;
SysScanDesc sscan;
HeapTuple tuple;
Oid groupid;
Datum new_record[Natts_pg_resgroup];
bool new_record_nulls[Natts_pg_resgroup];
ResGroupCaps caps;
int nResGroups;
MemoryContext oldContext;
/* Permission check - only superuser can create groups. */
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create resource groups")));
/*
* Check for an illegal name ('none' is used to signify no group in ALTER ROLE).
*/
if (strcmp(stmt->name, "none") == 0)
ereport(ERROR,
(errcode(ERRCODE_RESERVED_NAME),
errmsg("resource group name \"none\" is reserved")));
ClearResGroupCaps(&caps);
parseStmtOptions(stmt, &caps);
/*
* Both CREATE and ALTER resource group need check the intersection of cpuset,
* to make it simple, acquire ExclusiveLock lock on pg_resgroupcapability at
* the beginning of CREATE and ALTER.
*/
pg_resgroupcapability_rel = table_open(ResGroupCapabilityRelationId, ExclusiveLock);
pg_resgroup_rel = table_open(ResGroupRelationId, RowExclusiveLock);
sscan = systable_beginscan(pg_resgroup_rel, ResGroupRsgnameIndexId, false,
NULL, 0, NULL);
nResGroups = 0;
while (systable_getnext(sscan) != NULL)
nResGroups++;
systable_endscan(sscan);
/* Check if MaxResourceGroups limit is reached */
if (nResGroups >= MaxResourceGroups)
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_RESOURCES),
errmsg("insufficient resource groups available")));
/*
* Check the pg_resgroup relation to be certain the group doesn't already
* exist.
*/
ScanKeyInit(&scankey,
Anum_pg_resgroup_rsgname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(stmt->name));
sscan = systable_beginscan(pg_resgroup_rel, ResGroupRsgnameIndexId, true,
NULL, 1, &scankey);
if (HeapTupleIsValid(systable_getnext(sscan)))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("resource group \"%s\" already exists",
stmt->name)));
systable_endscan(sscan);
/*
* Build a tuple to insert
*/
MemSet(new_record, 0, sizeof(new_record));
MemSet(new_record_nulls, false, sizeof(new_record_nulls));
new_record[Anum_pg_resgroup_rsgname - 1] =
DirectFunctionCall1(namein, CStringGetDatum(stmt->name));
new_record[Anum_pg_resgroup_parent - 1] = ObjectIdGetDatum(0);
groupid = GetNewOidForResGroup(pg_resgroup_rel, ResGroupOidIndexId,
Anum_pg_resgroup_oid,
stmt->name);
new_record[Anum_pg_resgroup_oid - 1] = groupid;
pg_resgroup_dsc = RelationGetDescr(pg_resgroup_rel);
tuple = heap_form_tuple(pg_resgroup_dsc, new_record, new_record_nulls);
/*
* Insert new record in the pg_resgroup table
*/
CatalogTupleInsert(pg_resgroup_rel, tuple);
/* process the WITH (...) list items */
validateCapabilities(pg_resgroupcapability_rel, groupid, &caps, true);
insertResgroupCapabilities(pg_resgroupcapability_rel, groupid, &caps);
/* Dispatch the statement to segments */
if (Gp_role == GP_ROLE_DISPATCH)
{
CdbDispatchUtilityStatement((Node *) stmt,
DF_CANCEL_ON_ERROR|
DF_WITH_SNAPSHOT|
DF_NEED_TWO_PHASE,
GetAssignedOidsForDispatch(),
NULL);
MetaTrackAddObject(ResGroupRelationId,
groupid,
GetUserId(), /* not ownerid */
"CREATE", "RESOURCE GROUP");
}
table_close(pg_resgroup_rel, NoLock);
table_close(pg_resgroupcapability_rel, NoLock);
/* Add this group into shared memory */
if (IsResGroupActivated())
{
ResourceGroupCallbackContext *callbackCtx;
AllocResGroupEntry(groupid, &caps);
/* Argument of callback function should be allocated in heap region */
oldContext = MemoryContextSwitchTo(TopMemoryContext);
callbackCtx = (ResourceGroupCallbackContext *) palloc0(sizeof(*callbackCtx));
callbackCtx->groupid = groupid;
callbackCtx->caps = caps;
MemoryContextSwitchTo(oldContext);
RegisterXactCallbackOnce(createResgroupCallback, callbackCtx);
/* Create os dependent part for this resource group */
cgroupOpsRoutine->createcgroup(groupid);
if (caps.io_limit != NIL)
cgroupOpsRoutine->setio(groupid, caps.io_limit);
if (CpusetIsEmpty(caps.cpuset))
{
cgroupOpsRoutine->setcpulimit(groupid, caps.cpuMaxPercent);
cgroupOpsRoutine->setcpuweight(groupid, caps.cpuWeight);
}
else
{
EnsureCpusetIsAvailable(ERROR);
char *cpuset = getCpuSetByRole(caps.cpuset);
cgroupOpsRoutine->setcpuset(groupid, cpuset);
/* reset default group, subtract new group cpu cores */
char defaultGroupCpuset[MaxCpuSetLength];
cgroupOpsRoutine->getcpuset(DEFAULT_CPUSET_GROUP_ID,
defaultGroupCpuset,
MaxCpuSetLength);
CpusetDifference(defaultGroupCpuset, cpuset, MaxCpuSetLength);
cgroupOpsRoutine->setcpuset(DEFAULT_CPUSET_GROUP_ID, defaultGroupCpuset);
}
SIMPLE_FAULT_INJECTOR("create_resource_group_fail");
}
else if (Gp_role == GP_ROLE_DISPATCH)
ereport(WARNING,
(errmsg("resource group is disabled"),
errhint("To enable set gp_resource_manager=group")));
}
/*
* DROP RESOURCE GROUP
*/
void
DropResourceGroup(DropResourceGroupStmt *stmt)
{
Relation pg_resgroup_rel;
HeapTuple tuple;
ScanKeyData scankey;
SysScanDesc sscan;
Oid groupid;
ResourceGroupCallbackContext *callbackCtx;
/* Permission check - only superuser can drop resource groups. */
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to drop resource groups")));
/*
* Check the pg_resgroup relation to be certain the resource group already
* exists.
*/
pg_resgroup_rel = heap_open(ResGroupRelationId, RowExclusiveLock);
ScanKeyInit(&scankey,
Anum_pg_resgroup_rsgname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(stmt->name));
sscan = systable_beginscan(pg_resgroup_rel, ResGroupRsgnameIndexId, true,
NULL, 1, &scankey);
tuple = systable_getnext(sscan);
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("resource group \"%s\" does not exist",
stmt->name)));
/*
* Remember the Oid, for destroying the in-memory
* resource group later.
*/
groupid = ((Form_pg_resgroup) GETSTRUCT(tuple))->oid;
/* cannot DROP default resource groups */
if (groupid == DEFAULTRESGROUP_OID
|| groupid == ADMINRESGROUP_OID
|| groupid == SYSTEMRESGROUP_OID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot drop default resource group \"%s\"",
stmt->name)));
/* check before dispatch to segment */
if (IsResGroupActivated())
{
/* Argument of callback function should be allocated in heap region */
callbackCtx = (ResourceGroupCallbackContext *)
MemoryContextAlloc(TopMemoryContext, sizeof(*callbackCtx));
callbackCtx->groupid = groupid;
RegisterXactCallbackOnce(dropResgroupCallback, callbackCtx);
ResGroupCheckForDrop(groupid, stmt->name);
}
/*
* Check to see if any roles are in this resource group.
*/
checkAuthIdForDrop(groupid);
/*
* Delete the resource group from the catalog.
*/
simple_heap_delete(pg_resgroup_rel, &tuple->t_self);
systable_endscan(sscan);
heap_close(pg_resgroup_rel, NoLock);
/* drop the extended attributes for this resource group */
deleteResgroupCapabilities(groupid);
/*
* Remove any comments on this resource group
*/
DeleteSharedComments(groupid, ResGroupRelationId);
if (Gp_role == GP_ROLE_DISPATCH)
{
/* metadata tracking */
MetaTrackDropObject(ResGroupRelationId, groupid);
CdbDispatchUtilityStatement((Node *) stmt,
DF_CANCEL_ON_ERROR|
DF_WITH_SNAPSHOT|
DF_NEED_TWO_PHASE,
NIL,
NULL);
}
}
/*
* ALTER RESOURCE GROUP
*/
void
AlterResourceGroup(AlterResourceGroupStmt *stmt)
{
Relation pg_resgroupcapability_rel;
Oid groupid;
DefElem *defel;
ResGroupLimitType limitType;
ResGroupCaps caps;
ResGroupCaps oldCaps;
ResGroupCap value = 0;
const char *cpuset = NULL;
char *io_limit = NULL;
ResourceGroupCallbackContext *callbackCtx;
MemoryContext oldContext;
/* Permission check - only superuser can alter resource groups. */
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to alter resource groups")));
/* Currently we only support to ALTER one limit at one time */
Assert(list_length(stmt->options) == 1);
defel = (DefElem *) lfirst(list_head(stmt->options));
limitType = getResgroupOptionType(defel->defname);
if (limitType == RESGROUP_LIMIT_TYPE_UNKNOWN)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("option \"%s\" not recognized", defel->defname)));
}
else if (limitType == RESGROUP_LIMIT_TYPE_CPUSET)
{
EnsureCpusetIsAvailable(ERROR);
cpuset = defGetString(defel);
checkCpuSetByRole(cpuset);
}
else if (limitType == RESGROUP_LIMIT_TYPE_IO_LIMIT)
io_limit = defGetString(defel);
else
{
value = getResgroupOptionValue(defel);
checkResgroupCapLimit(limitType, value);
}
/*
* Check the pg_resgroup relation to be certain the resource group already
* exists.
*/
groupid = get_resgroup_oid(stmt->name, false);
if (limitType == RESGROUP_LIMIT_TYPE_CONCURRENCY &&
value == 0 &&
groupid == ADMINRESGROUP_OID)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_LIMIT_VALUE),
errmsg("admin_group must have at least one concurrency")));
}
/*
* We use ExclusiveLock here to prevent concurrent increase on different
* resource group.
*
* We can't use AccessExclusiveLock here, the reason is that, if there is
* a database recovery happened when run "alter resource group" and acquire
* this kind of lock, the initialization of resource group in function
* InitResGroups will be pending during database startup, since this function
* will open this table with AccessShareLock, AccessExclusiveLock is not
* compatible with any other lock. ExclusiveLock and AccessShareLock are
* compatible.
*/
pg_resgroupcapability_rel = heap_open(ResGroupCapabilityRelationId,
ExclusiveLock);
/* Load current resource group capabilities */
GetResGroupCapabilities(pg_resgroupcapability_rel, groupid, &oldCaps);
/* If io_limit not been altered, reset io_limit field to NIL */
if (limitType != RESGROUP_LIMIT_TYPE_IO_LIMIT && oldCaps.io_limit != NIL)
{
cgroupOpsRoutine->freeio(oldCaps.io_limit);
oldCaps.io_limit = NIL;
}
caps = oldCaps;
switch (limitType)
{
case RESGROUP_LIMIT_TYPE_CPU:
caps.cpuMaxPercent = value;
SetCpusetEmpty(caps.cpuset, sizeof(caps.cpuset));
break;
case RESGROUP_LIMIT_TYPE_CPU_SHARES:
caps.cpuWeight = value;
break;
case RESGROUP_LIMIT_TYPE_CONCURRENCY:
caps.concurrency = value;
break;
case RESGROUP_LIMIT_TYPE_CPUSET:
strlcpy(caps.cpuset, cpuset, sizeof(caps.cpuset));
caps.cpuMaxPercent = CPU_MAX_PERCENT_DISABLED;
caps.cpuWeight = RESGROUP_DEFAULT_CPU_WEIGHT;
break;
case RESGROUP_LIMIT_TYPE_MEMORY_QUOTA:
caps.memory_quota = value;
break;
case RESGROUP_LIMIT_TYPE_MIN_COST:
caps.min_cost = value;
break;
case RESGROUP_LIMIT_TYPE_IO_LIMIT:
oldContext = CurrentMemoryContext;
if (cgroupOpsRoutine == NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("resource group must be enabled to use io limit feature")));
MemoryContextSwitchTo(TopMemoryContext);
caps.io_limit = cgroupOpsRoutine->parseio(io_limit);
MemoryContextSwitchTo(oldContext);
break;
default:
break;
}
validateCapabilities(pg_resgroupcapability_rel, groupid, &caps, false);
AssertImply(limitType != RESGROUP_LIMIT_TYPE_IO_LIMIT, caps.io_limit == NIL);
/* cpuset & cpu_max_percent can not coexist.
* if cpuset is active, then cpu_max_percent must set to CPU_RATE_LIMIT_DISABLED,
* if cpu_max_percent is active, then cpuset must set to "" */
if (limitType == RESGROUP_LIMIT_TYPE_CPUSET)
{
updateResgroupCapabilityEntry(pg_resgroupcapability_rel,
groupid, RESGROUP_LIMIT_TYPE_CPU,
CPU_MAX_PERCENT_DISABLED, "");
updateResgroupCapabilityEntry(pg_resgroupcapability_rel,
groupid, RESGROUP_LIMIT_TYPE_CPU_SHARES,
RESGROUP_DEFAULT_CPU_WEIGHT, "");
updateResgroupCapabilityEntry(pg_resgroupcapability_rel,
groupid, RESGROUP_LIMIT_TYPE_CPUSET,
0, caps.cpuset);
}
else if (limitType == RESGROUP_LIMIT_TYPE_CPU)
{
updateResgroupCapabilityEntry(pg_resgroupcapability_rel,
groupid, RESGROUP_LIMIT_TYPE_CPUSET,
0, DefaultCpuset);
updateResgroupCapabilityEntry(pg_resgroupcapability_rel,
groupid, RESGROUP_LIMIT_TYPE_CPU,
value, "");
}
else if (limitType == RESGROUP_LIMIT_TYPE_IO_LIMIT)
{
if (caps.io_limit != NIL)
updateResgroupCapabilityEntry(pg_resgroupcapability_rel,
groupid, RESGROUP_LIMIT_TYPE_IO_LIMIT,
0, cgroupOpsRoutine->dumpio(caps.io_limit));
else
{
/*
* When alter io_limit to -1 , the caps.io_limit will be nil.
* So we should update the io_limit in capability relation to -1.
*/
updateResgroupCapabilityEntry(pg_resgroupcapability_rel,
groupid, RESGROUP_LIMIT_TYPE_IO_LIMIT,
0, DefaultIOLimit);
}
}
else
{
updateResgroupCapabilityEntry(pg_resgroupcapability_rel,
groupid, limitType, value, "");
}
heap_close(pg_resgroupcapability_rel, NoLock);
if (Gp_role == GP_ROLE_DISPATCH)
{
MetaTrackUpdObject(ResGroupCapabilityRelationId,
groupid,
GetUserId(),
"ALTER", defel->defname);
CdbDispatchUtilityStatement((Node *) stmt,
DF_CANCEL_ON_ERROR|
DF_WITH_SNAPSHOT|
DF_NEED_TWO_PHASE,
GetAssignedOidsForDispatch(),
NULL);
}
if (IsResGroupActivated())
{
/* Argument of callback function should be allocated in heap region */
oldContext = MemoryContextSwitchTo(TopMemoryContext);
callbackCtx = (ResourceGroupCallbackContext *) palloc0(sizeof(*callbackCtx));
callbackCtx->groupid = groupid;
callbackCtx->limittype = limitType;
callbackCtx->caps = caps;
callbackCtx->oldCaps = oldCaps;
MemoryContextSwitchTo(oldContext);
RegisterXactCallbackOnce(alterResgroupCallback, callbackCtx);
}
}
/*
* Get all the capabilities of one resource group in pg_resgroupcapability.
*
* Note: the io_limit in ResGroupCaps will be NIL if parse io_limit string failed.
*/
void
GetResGroupCapabilities(Relation rel, Oid groupId, ResGroupCaps *resgroupCaps)
{
MemoryContext oldContext;
SysScanDesc sscan;
ScanKeyData key;
HeapTuple tuple;
bool isNull;
/*
* We maintain a bit mask to track which resgroup limit capability types
* have been retrieved, when mask is 0 then no limit capability is found
* for the given groupId.
*/
int mask = 0;
ClearResGroupCaps(resgroupCaps);
/* Init cpuset with proper value */
strcpy(resgroupCaps->cpuset, DefaultCpuset);
ScanKeyInit(&key,
Anum_pg_resgroupcapability_resgroupid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(groupId));
sscan = systable_beginscan(rel,
ResGroupCapabilityResgroupidIndexId,
true,
NULL, 1, &key);
while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
{
Datum typeDatum;
ResGroupLimitType type;
Datum valueDatum;
char *value;
typeDatum = heap_getattr(tuple, Anum_pg_resgroupcapability_reslimittype,
rel->rd_att, &isNull);
type = (ResGroupLimitType) DatumGetInt16(typeDatum);
Assert(type > RESGROUP_LIMIT_TYPE_UNKNOWN);
Assert(!(mask & (1 << type)));
if (type >= RESGROUP_LIMIT_TYPE_COUNT)
continue;
mask |= 1 << type;
valueDatum = heap_getattr(tuple, Anum_pg_resgroupcapability_value,
rel->rd_att, &isNull);
value = TextDatumGetCString(valueDatum);
switch (type)
{
case RESGROUP_LIMIT_TYPE_CONCURRENCY:
resgroupCaps->concurrency = str2Int(value,
getResgroupOptionName(type));
break;
case RESGROUP_LIMIT_TYPE_CPU:
resgroupCaps->cpuMaxPercent = str2Int(value,
getResgroupOptionName(type));
break;
case RESGROUP_LIMIT_TYPE_CPU_SHARES:
resgroupCaps->cpuWeight = str2Int(value,
getResgroupOptionName(type));
break;
case RESGROUP_LIMIT_TYPE_CPUSET:
strlcpy(resgroupCaps->cpuset, value, sizeof(resgroupCaps->cpuset));
break;
case RESGROUP_LIMIT_TYPE_MEMORY_QUOTA:
resgroupCaps->memory_quota = str2Int(value,
getResgroupOptionName(type));
break;
case RESGROUP_LIMIT_TYPE_MIN_COST:
resgroupCaps->min_cost = str2Int(value,
getResgroupOptionName(type));
break;
case RESGROUP_LIMIT_TYPE_IO_LIMIT:
if (cgroupOpsRoutine != NULL)
{
/* if InterruptHoldoffCount doesn't restore in PG_CATCH,
* the catch will failed. */
int32 savedholdoffCount = InterruptHoldoffCount;
oldContext = CurrentMemoryContext;
MemoryContextSwitchTo(TopMemoryContext);
/*
* This function will be called in InitResGroups and AlterResourceGroup which
* shoud not be abort. In some circumstances, for example, the directory of a
* tablespace in io_limit be removed, then parseio will throw error. If
* InitResGroups be aborted, the cluster cannot launched. */
PG_TRY();
{
resgroupCaps->io_limit = cgroupOpsRoutine->parseio(value);
}
PG_CATCH();
{
resgroupCaps->io_limit = NIL;
InterruptHoldoffCount = savedholdoffCount;
if (elog_demote(WARNING))
{
EmitErrorReport();
FlushErrorState();
}
}
PG_END_TRY();
MemoryContextSwitchTo(oldContext);
}
else
resgroupCaps->io_limit = NIL;
break;
default:
break;
}
}
systable_endscan(sscan);
if (!mask)
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("cannot find limit capabilities for resource group: %d",
groupId)));
}
}
/*
* Get resource group id for a role in pg_authid.
*
* An exception is thrown if the current role is invalid. This can happen if,
* for example, a role was logged into psql and that role was dropped by another
* psql session. But, normally something like "ERROR: role 16385 was
* concurrently dropped" would happen before the code reaches this function.
*/
Oid
GetResGroupIdForRole(Oid roleid)
{
HeapTuple tuple;
Oid groupId;
bool isNull;
Relation rel;
ScanKeyData key;
SysScanDesc sscan;
rel = table_open(AuthIdRelationId, AccessShareLock);
ScanKeyInit(&key,
Anum_pg_authid_oid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(roleid));
sscan = systable_beginscan(rel, AuthIdOidIndexId, true, NULL, 1, &key);
tuple = systable_getnext(sscan);
if (!HeapTupleIsValid(tuple))
{
systable_endscan(sscan);
table_close(rel, AccessShareLock);
/*
* Role should have been dropped by other backends in this case, so this
* session cannot execute any command anymore
*/
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("role with Oid %u was dropped", roleid),
errdetail("Cannot execute commands anymore, please terminate this session.")));
}
/* must access tuple before systable_endscan */
groupId = DatumGetObjectId(heap_getattr(tuple, Anum_pg_authid_rolresgroup,
rel->rd_att, &isNull));
systable_endscan(sscan);
/*
* release lock here to guarantee we have no lock held when acquiring
* resource group slot
*/
table_close(rel, AccessShareLock);
if (!OidIsValid(groupId))
groupId = InvalidOid;
return groupId;
}
/*
* get_resgroup_oid -- Return the Oid for a resource group name
*
* If missing_ok is false, throw an error if database name not found. If
* true, just return InvalidOid.
*
* Notes:
* Used by the various admin commands to convert a user supplied group name
* to Oid.
*/
Oid
get_resgroup_oid(const char *name, bool missing_ok)
{
Oid oid;
oid = GetSysCacheOid1(RESGROUPNAME, Anum_pg_resgroup_oid,
CStringGetDatum(name));
if (!OidIsValid(oid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("resource group \"%s\" does not exist",
name)));
return oid;
}
/*
* GetResGroupNameForId -- Return the resource group name for an Oid
*/
char *
GetResGroupNameForId(Oid oid)
{
HeapTuple tuple;
char *name;
tuple = SearchSysCache1(RESGROUPOID,
ObjectIdGetDatum(oid));
if (HeapTupleIsValid(tuple))
{
bool isnull;
Datum nameDatum;
Name resGroupName;
nameDatum = SysCacheGetAttr(RESGROUPOID, tuple,
Anum_pg_resgroup_rsgname,
&isnull);
Assert(!isnull);
resGroupName = DatumGetName(nameDatum);
name = pstrdup(NameStr(*resGroupName));
}
else
return "unknown";
ReleaseSysCache(tuple);
return name;
}
/*
* Check to see if the group can be assigned with role
*/
void
ResGroupCheckForRole(Oid groupId)
{
Relation pg_resgroupcapability_rel;
ResGroupCaps caps;
pg_resgroupcapability_rel = heap_open(ResGroupCapabilityRelationId,
AccessShareLock);
/* Load current resource group capabilities */
GetResGroupCapabilities(pg_resgroupcapability_rel, groupId, &caps);
if (cgroupOpsRoutine != NULL && caps.io_limit != NIL)
cgroupOpsRoutine->freeio(caps.io_limit);
heap_close(pg_resgroupcapability_rel, AccessShareLock);
}
/*
* Get the option type from a name string.
*
* @param defname the name string
*
* @return the option type or UNKNOWN if the name is unknown
*/
static ResGroupLimitType
getResgroupOptionType(const char* defname)
{
if (strcmp(defname, "cpu_max_percent") == 0)
return RESGROUP_LIMIT_TYPE_CPU;
else if (strcmp(defname, "concurrency") == 0)
return RESGROUP_LIMIT_TYPE_CONCURRENCY;
else if (strcmp(defname, "cpuset") == 0)
return RESGROUP_LIMIT_TYPE_CPUSET;
else if (strcmp(defname, "cpu_weight") == 0)
return RESGROUP_LIMIT_TYPE_CPU_SHARES;
else if (strcmp(defname, "memory_quota") == 0)
return RESGROUP_LIMIT_TYPE_MEMORY_QUOTA;
else if (strcmp(defname, "min_cost") == 0)
return RESGROUP_LIMIT_TYPE_MIN_COST;
else if (strcmp(defname, "io_limit") == 0)
return RESGROUP_LIMIT_TYPE_IO_LIMIT;
else
return RESGROUP_LIMIT_TYPE_UNKNOWN;
}
/*
* Get capability value from DefElem, convert from int64 to int
*/
static ResGroupCap
getResgroupOptionValue(DefElem *defel)
{
int64 value;
value = defGetInt64(defel);
if (value < INT_MIN || value > INT_MAX)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("capability %s is out of range", defel->defname)));
return (ResGroupCap)value;
}
/*
* Get the option name from type.
*
* @param type the resgroup limit type
*
* @return the name of type
*/
static const char *
getResgroupOptionName(ResGroupLimitType type)
{
switch (type)
{
case RESGROUP_LIMIT_TYPE_CONCURRENCY:
return "concurrency";
case RESGROUP_LIMIT_TYPE_CPU:
return "cpu_max_percent";
case RESGROUP_LIMIT_TYPE_CPUSET:
return "cpuset";
case RESGROUP_LIMIT_TYPE_CPU_SHARES:
return "cpu_weight";
case RESGROUP_LIMIT_TYPE_MEMORY_QUOTA:
return "memory_quota";
case RESGROUP_LIMIT_TYPE_MIN_COST:
return "min_cost";
default:
return "unknown";
}
}
/*
* Check if capability value exceeds max and min value
*/
static void
checkResgroupCapLimit(ResGroupLimitType type, int value)
{
switch (type)
{
case RESGROUP_LIMIT_TYPE_CONCURRENCY:
if (value < RESGROUP_MIN_CONCURRENCY ||
value > RESGROUP_MAX_CONCURRENCY)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("concurrency range is [%d, 'max_connections']",
RESGROUP_MIN_CONCURRENCY)));
break;
case RESGROUP_LIMIT_TYPE_CPU:
if (value > RESGROUP_MAX_CPU_MAX_PERCENT ||
(value < RESGROUP_MIN_CPU_MAX_PERCENT && value != CPU_MAX_PERCENT_DISABLED))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cpu_max_percent range is [%d, %d] or equals to %d",
RESGROUP_MIN_CPU_MAX_PERCENT, RESGROUP_MAX_CPU_MAX_PERCENT,
CPU_MAX_PERCENT_DISABLED)));
break;
case RESGROUP_LIMIT_TYPE_CPU_SHARES:
if (value < RESGROUP_MIN_CPU_WEIGHT ||
value > RESGROUP_MAX_CPU_WEIGHT)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cpu_weight range is [%d, %d]",
RESGROUP_MIN_CPU_WEIGHT, RESGROUP_MAX_CPU_WEIGHT)));
break;
case RESGROUP_LIMIT_TYPE_MEMORY_QUOTA:
if (value < RESGROUP_MIN_MEMORY_QUOTA && value != RESGROUP_DEFAULT_MEMORY_QUOTA)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("memory_quota range is [%d, INT_MAX] or equals to %d",
RESGROUP_MIN_MEMORY_QUOTA, RESGROUP_DEFAULT_MEMORY_QUOTA)));
break;
case RESGROUP_LIMIT_TYPE_MIN_COST:
if (value < RESGROUP_MIN_MIN_COST)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("The min_cost value can't be less than %d.",
RESGROUP_MIN_MIN_COST)));
break;
default:
Assert(!"unexpected options");
break;
}
}
/*
* Parse a statement and store the settings in options.
*
* @param stmt the statement
* @param caps used to store the settings
*/
static void
parseStmtOptions(CreateResourceGroupStmt *stmt, ResGroupCaps *caps)
{
MemoryContext oldContext;
ListCell *cell;
ResGroupCap value;
int mask = 0;
foreach(cell, stmt->options)
{
DefElem *defel = (DefElem *) lfirst(cell);
int type = getResgroupOptionType(defel->defname);
if (type == RESGROUP_LIMIT_TYPE_UNKNOWN)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("option \"%s\" not recognized", defel->defname)));
if (mask & (1 << type))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("found duplicate resource group resource type: %s",
defel->defname)));
else
mask |= 1 << type;
if (type == RESGROUP_LIMIT_TYPE_CPUSET)
{
const char *cpuset = defGetString(defel);
strlcpy(caps->cpuset, cpuset, sizeof(caps->cpuset));
checkCpuSetByRole(cpuset);
caps->cpuMaxPercent = CPU_MAX_PERCENT_DISABLED;
caps->cpuWeight = RESGROUP_DEFAULT_CPU_WEIGHT;
}
else if (type == RESGROUP_LIMIT_TYPE_IO_LIMIT)
{
char *io_limit_str = defGetString(defel);
oldContext = CurrentMemoryContext;
if (cgroupOpsRoutine == NULL)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("resource group must be enabled to use io limit feature")));
MemoryContextSwitchTo(TopMemoryContext);
caps->io_limit = cgroupOpsRoutine->parseio(io_limit_str);
MemoryContextSwitchTo(oldContext);
}
else
{
value = getResgroupOptionValue(defel);
checkResgroupCapLimit(type, value);
switch (type)
{
case RESGROUP_LIMIT_TYPE_CONCURRENCY:
caps->concurrency = value;
break;
case RESGROUP_LIMIT_TYPE_CPU:
caps->cpuMaxPercent = value;
SetCpusetEmpty(caps->cpuset, sizeof(caps->cpuset));
break;
case RESGROUP_LIMIT_TYPE_CPU_SHARES:
caps->cpuWeight = value;
break;
case RESGROUP_LIMIT_TYPE_MEMORY_QUOTA:
caps->memory_quota = value;
break;
case RESGROUP_LIMIT_TYPE_MIN_COST:
caps->min_cost = value;
break;
default:
break;
}
}
}
if ((mask & (1 << RESGROUP_LIMIT_TYPE_CPUSET)))
EnsureCpusetIsAvailable(ERROR);
if ((mask & (1 << RESGROUP_LIMIT_TYPE_CPU)) &&
(mask & (1 << RESGROUP_LIMIT_TYPE_CPUSET)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("can't specify both cpu_max_percent and cpuset")));
if (!(mask & (1 << RESGROUP_LIMIT_TYPE_CPU)) &&
!(mask & (1 << RESGROUP_LIMIT_TYPE_CPUSET)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("must specify cpu_max_percent or cpuset")));
if (!(mask & (1 << RESGROUP_LIMIT_TYPE_CONCURRENCY)))
caps->concurrency = RESGROUP_DEFAULT_CONCURRENCY;
if (!(mask & (1 << RESGROUP_LIMIT_TYPE_MEMORY_QUOTA)))
caps->memory_quota = -1;
if (!(mask & (1 << RESGROUP_LIMIT_TYPE_MIN_COST)))
caps->min_cost = 0;
if ((mask & (1 << RESGROUP_LIMIT_TYPE_CPU)) &&
!(mask & (1 << RESGROUP_LIMIT_TYPE_CPU_SHARES)))
caps->cpuWeight = RESGROUP_DEFAULT_CPU_WEIGHT;
}
/*
* Resource group call back function
*
* Remove resource group entry in shared memory when abort transaction which
* creates resource groups
*/
static void
createResgroupCallback(XactEvent event, void *arg)
{
ResourceGroupCallbackContext *callbackCtx = arg;
if (event != XACT_EVENT_COMMIT)
{
ResGroupCreateOnAbort(callbackCtx);
}
if (callbackCtx->caps.io_limit != NIL)
cgroupOpsRoutine->freeio(callbackCtx->caps.io_limit);
pfree(callbackCtx);
}
/*
* Resource group call back function
*
* When DROP RESOURCE GROUP transaction ends, need wake up
* the queued transactions and cleanup shared menory entry.
*/
static void
dropResgroupCallback(XactEvent event, void *arg)
{
ResourceGroupCallbackContext *callbackCtx = arg;
ResGroupDropFinish(callbackCtx, event == XACT_EVENT_COMMIT);
pfree(callbackCtx);
}
/*
* Resource group call back function
*
* When ALTER RESOURCE GROUP SET CONCURRENCY commits, some queuing
* transaction of this resource group may need to be woke up.
*/
static void
alterResgroupCallback(XactEvent event, void *arg)
{
ResourceGroupCallbackContext *callbackCtx = arg;
if (event == XACT_EVENT_COMMIT)
ResGroupAlterOnCommit(callbackCtx);
if (callbackCtx->caps.io_limit != NIL)
cgroupOpsRoutine->freeio(callbackCtx->caps.io_limit);
if (callbackCtx->oldCaps.io_limit != NIL)
cgroupOpsRoutine->freeio(callbackCtx->oldCaps.io_limit);
pfree(callbackCtx);
}
/*
* Catalog access functions
*/
/*
* Insert all the capabilities to the capability table.
*
* We store the capabilities in multiple lines for one group,
* so we have to insert them one by one. This function will
* handle the type conversion etc..
*
* @param groupid oid of the resource group
* @param caps the capabilities
*/
static void
insertResgroupCapabilities(Relation rel, Oid groupId, ResGroupCaps *caps)
{
char value[64];
snprintf(value, sizeof(value), "%d", caps->concurrency);
insertResgroupCapabilityEntry(rel, groupId,
RESGROUP_LIMIT_TYPE_CONCURRENCY, value);
snprintf(value, sizeof(value), "%d", caps->cpuMaxPercent);
insertResgroupCapabilityEntry(rel, groupId,
RESGROUP_LIMIT_TYPE_CPU, value);
snprintf(value, sizeof(value), "%d", caps->cpuWeight);
insertResgroupCapabilityEntry(rel, groupId,
RESGROUP_LIMIT_TYPE_CPU_SHARES, value);
insertResgroupCapabilityEntry(rel, groupId,
RESGROUP_LIMIT_TYPE_CPUSET, caps->cpuset);
snprintf(value, sizeof(value), "%d", caps->memory_quota);
insertResgroupCapabilityEntry(rel, groupId,
RESGROUP_LIMIT_TYPE_MEMORY_QUOTA, value);
snprintf(value, sizeof(value), "%d", caps->min_cost);
insertResgroupCapabilityEntry(rel, groupId,
RESGROUP_LIMIT_TYPE_MIN_COST, value);
if (caps->io_limit != NIL)
insertResgroupCapabilityEntry(rel, groupId,
RESGROUP_LIMIT_TYPE_IO_LIMIT,
cgroupOpsRoutine->dumpio((caps->io_limit)));
else
insertResgroupCapabilityEntry(rel, groupId,
RESGROUP_LIMIT_TYPE_IO_LIMIT, DefaultIOLimit);
}
/*
* Update all the capabilities of one resgroup in pg_resgroupcapability
*
* groupId and limitType are the scan keys.
*/
static void
updateResgroupCapabilityEntry(Relation rel,
Oid groupId,
ResGroupLimitType limitType,
ResGroupCap value,
const char *strValue)
{
HeapTuple oldTuple;
HeapTuple newTuple;
SysScanDesc sscan;
ScanKeyData scankey[2];
Datum values[Natts_pg_resgroupcapability];
bool isnull[Natts_pg_resgroupcapability];
bool repl[Natts_pg_resgroupcapability];
char stringBuffer[MaxCpuSetLength];
ScanKeyInit(&scankey[0],
Anum_pg_resgroupcapability_resgroupid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(groupId));
ScanKeyInit(&scankey[1],
Anum_pg_resgroupcapability_reslimittype,
BTEqualStrategyNumber, F_INT2EQ,
Int16GetDatum(limitType));
sscan = systable_beginscan(rel,
ResGroupCapabilityResgroupidResLimittypeIndexId, true,
NULL, 2, scankey);
MemSet(values, 0, sizeof(values));
MemSet(isnull, 0, sizeof(isnull));
MemSet(repl, 0, sizeof(repl));
oldTuple = systable_getnext(sscan);
if (!HeapTupleIsValid(oldTuple))
{
/*
* It's possible for a cap to be missing, e.g. a resgroup is created
* with v5.0 which does not support cap=7 (cpuset), then we binary
* switch to v5.10 and alter it, then we'll find cap=7 missing here.
* Instead of raising an error we should fallback to insert a new cap.
*/
systable_endscan(sscan);
insertResgroupCapabilityEntry(rel, groupId, limitType, (char *) strValue);
return;
}
if (limitType == RESGROUP_LIMIT_TYPE_CPUSET)
{
strlcpy(stringBuffer, strValue, sizeof(stringBuffer));
}
else
{
snprintf(stringBuffer, sizeof(stringBuffer), "%d", value);
}
if (limitType == RESGROUP_LIMIT_TYPE_IO_LIMIT)
{
/* Because stringBuffer is a limited length array, so it not suitable for io limit. */
values[Anum_pg_resgroupcapability_value - 1] = CStringGetTextDatum(strValue);
}
else
values[Anum_pg_resgroupcapability_value - 1] = CStringGetTextDatum(stringBuffer);
isnull[Anum_pg_resgroupcapability_value - 1] = false;
repl[Anum_pg_resgroupcapability_value - 1] = true;
newTuple = heap_modify_tuple(oldTuple, RelationGetDescr(rel),
values, isnull, repl);
CatalogTupleUpdate(rel, &oldTuple->t_self, newTuple);
systable_endscan(sscan);
}
/*
* Validate the capabilities.
*
* The policy is resouces can't be over used, take memory for example,
* all the allocated memory can not exceed 100.
*
* Also detect for duplicate settings for the group.
*
* @param rel the relation
* @param groupid oid of the resource group
* @param caps the capabilities for the resource group
*/
static void
validateCapabilities(Relation rel,
Oid groupid,
ResGroupCaps *caps,
bool newGroup)
{
HeapTuple tuple;
SysScanDesc sscan;
char cpusetAll[MaxCpuSetLength] = {0};
char cpusetMissing[MaxCpuSetLength] = {0};
Bitmapset *bmsCurrent = NULL;
Bitmapset *bmsCommon = NULL;
if (!CpusetIsEmpty(caps->cpuset))
{
EnsureCpusetIsAvailable(ERROR);
}
/*
* initialize the variables only when resource group is activated
*/
if (IsResGroupActivated() &&
gp_resource_group_enable_cgroup_cpuset)
{
Bitmapset *bmsAll = NULL;
Bitmapset *bmsMissing = NULL;
/* Get all available cores */
cgroupOpsRoutine->getcpuset(CGROUP_ROOT_ID,
cpusetAll,
MaxCpuSetLength);
bmsAll = CpusetToBitset(cpusetAll, MaxCpuSetLength);
/* Check whether the cores in this group are available */
if (!CpusetIsEmpty(caps->cpuset))
{
char *cpuset = getCpuSetByRole(caps->cpuset);
bmsCurrent = CpusetToBitset(cpuset, MaxCpuSetLength);
bmsCommon = bms_intersect(bmsCurrent, bmsAll);
bmsMissing = bms_difference(bmsCurrent, bmsCommon);
if (!bms_is_empty(bmsMissing))
{
BitsetToCpuset(bmsMissing, cpusetMissing, MaxCpuSetLength);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cpu cores %s are unavailable on the system",
cpusetMissing)));
}
}
}
sscan = systable_beginscan(rel, ResGroupCapabilityResgroupidIndexId,
true, NULL, 0, NULL);
while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
{
Datum groupIdDatum;
Datum typeDatum;
Datum valueDatum;
ResGroupLimitType reslimittype;
Oid resgroupid;
char *valueStr;
bool isNull;
groupIdDatum = heap_getattr(tuple, Anum_pg_resgroupcapability_resgroupid,
rel->rd_att, &isNull);
resgroupid = DatumGetObjectId(groupIdDatum);
if (resgroupid == groupid)
{
if (!newGroup)
continue;
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("found duplicate resource group id: %d",
groupid)));
}
typeDatum = heap_getattr(tuple, Anum_pg_resgroupcapability_reslimittype,
rel->rd_att, &isNull);
reslimittype = (ResGroupLimitType) DatumGetInt16(typeDatum);
valueDatum = heap_getattr(tuple, Anum_pg_resgroupcapability_value,
rel->rd_att, &isNull);
/* we need to check the configuration of cpuset for intersection. */
if (reslimittype == RESGROUP_LIMIT_TYPE_CPUSET)
{
/*
* do the check when resource group is activated
*/
if (IsResGroupActivated() && !CpusetIsEmpty(caps->cpuset))
{
valueStr = TextDatumGetCString(valueDatum);
if (!CpusetIsEmpty(valueStr))
{
Bitmapset *bmsOther = NULL;
EnsureCpusetIsAvailable(ERROR);
Assert(!bms_is_empty(bmsCurrent));
char *cpuset = getCpuSetByRole(valueStr);
bmsOther = CpusetToBitset(cpuset, MaxCpuSetLength);
bmsCommon = bms_intersect(bmsCurrent, bmsOther);
if (!bms_is_empty(bmsCommon))
{
BitsetToCpuset(bmsCommon, cpusetMissing, MaxCpuSetLength);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("cpu cores %s are used by resource group %s",
cpusetMissing,
GetResGroupNameForId(resgroupid))));
}
}
}
}
}
systable_endscan(sscan);
}
/*
* Insert one capability to the capability table.
*
* @param rel the relation
* @param groupid oid of the resource group
* @param type the resource limit type
* @param value the limit value
*/
static void
insertResgroupCapabilityEntry(Relation rel,
Oid groupid,
uint16 type,
const char *value)
{
Datum new_record[Natts_pg_resgroupcapability];
bool new_record_nulls[Natts_pg_resgroupcapability];
HeapTuple tuple;
TupleDesc tupleDesc = RelationGetDescr(rel);
MemSet(new_record, 0, sizeof(new_record));
MemSet(new_record_nulls, false, sizeof(new_record_nulls));
new_record[Anum_pg_resgroupcapability_resgroupid - 1] = ObjectIdGetDatum(groupid);
new_record[Anum_pg_resgroupcapability_reslimittype - 1] = UInt16GetDatum(type);
new_record[Anum_pg_resgroupcapability_value - 1] = CStringGetTextDatum(value);
tuple = heap_form_tuple(tupleDesc, new_record, new_record_nulls);
CatalogTupleInsert(rel, tuple);
}
/*
* Delete capability entries of one resource group.
*/
static void
deleteResgroupCapabilities(Oid groupid)
{
Relation resgroup_capability_rel;
HeapTuple tuple;
ScanKeyData scankey;
SysScanDesc sscan;
resgroup_capability_rel = heap_open(ResGroupCapabilityRelationId,
RowExclusiveLock);
ScanKeyInit(&scankey,
Anum_pg_resgroupcapability_resgroupid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(groupid));
sscan = systable_beginscan(resgroup_capability_rel,
ResGroupCapabilityResgroupidIndexId,
true, NULL, 1, &scankey);
while ((tuple = systable_getnext(sscan)) != NULL)
simple_heap_delete(resgroup_capability_rel, &tuple->t_self);
systable_endscan(sscan);
heap_close(resgroup_capability_rel, NoLock);
}
/*
* Check to see if any roles are in this resource group.
*/
static void
checkAuthIdForDrop(Oid groupId)
{
Relation authIdRel;
ScanKeyData authidScankey;
SysScanDesc authidScan;
authIdRel = heap_open(AuthIdRelationId, RowExclusiveLock);
ScanKeyInit(&authidScankey,
Anum_pg_authid_rolresgroup,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(groupId));
authidScan = systable_beginscan(authIdRel, AuthIdRolResGroupIndexId, true,
NULL, 1, &authidScankey);
if (HeapTupleIsValid(systable_getnext(authidScan)))
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("resource group is used by at least one role")));
systable_endscan(authidScan);
heap_close(authIdRel, RowExclusiveLock);
}
/*
* Convert a C str to a integer value.
*
* @param str the C str
* @param prop the property name
*/
static int
str2Int(const char *str, const char *prop)
{
char *end = NULL;
double val = strtod(str, &end);
/* both the property name and value are already checked
* by the syntax parser, but we'll check it again anyway for safe. */
if (end == NULL || end == str || *end != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("%s requires a numeric value", prop)));
return floor(val);
}
/*
* check whether the cpuset value is syntactically right
*/
static void
checkCpusetSyntax(const char *cpuset)
{
if (cpuset == NULL)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cpuset invalid")));
}
if (strlen(cpuset) >= MaxCpuSetLength)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("the length of cpuset reached the upper limit %d",
MaxCpuSetLength)));
}
if (!CpusetToBitset(cpuset,
strlen(cpuset)))
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cpuset invalid")));
}
return;
}
/*
* Check Cpuset by coordinator and segment
*/
extern void
checkCpuSetByRole(const char *cpuset)
{
char *first = NULL;
char *last = NULL;
if (cpuset == NULL)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cpuset invalid")));
}
first = strchr(cpuset, ';');
last = strrchr(cpuset, ';');
/*
* If point first not equal last, that means
* ';' character exceed limit numbers.
*/
if (last != first)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cpuset invalid")));
}
if (first == NULL)
checkCpusetSyntax(cpuset);
else
{
char mcpu[MaxCpuSetLength] = {0};
strncpy(mcpu, cpuset, first - cpuset);
checkCpusetSyntax(mcpu);
checkCpusetSyntax(first + 1);
}
return;
}
/*
* Seperate cpuset by coordinator and segment
* Return as splitcpuset
*
* ex:
* cpuset = "1;4"
* then we should assign '1' to corrdinator and '4' to segment
*
* cpuset = "1"
* assign '1' to both coordinator and segment
*/
extern char *
getCpuSetByRole(const char *cpuset)
{
char *splitcpuset = NULL;
if (cpuset == NULL)
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("Unexpected cpuset invalid in getCpuSetByRole")));
}
char *first = strchr(cpuset, ';');
if (first == NULL)
splitcpuset = (char *)cpuset;
else
{
char *scpu = first + 1;
/* Get result cpuset by IS_QUERY_DISPATCHER(), on master or segment */
if (IS_QUERY_DISPATCHER())
splitcpuset = scpu;
else
{
char *mcpu = (char *)palloc0(sizeof(char) * MaxCpuSetLength);
strncpy(mcpu, cpuset, first - cpuset);
splitcpuset = mcpu;
}
}
return splitcpuset;
}