blob: e8b9b309cb6497dee947895f2f87fdbae6aefbe2 [file]
/*-------------------------------------------------------------------------
*
* segadmin.c
* Functions to support administrative tasks with GPDB segments.
*
* Portions Copyright (c) 2010 Greenplum
* Portions Copyright (c) 2012-Present VMware, Inc. or its affiliates.
*
*
* IDENTIFICATION
* src/backend/utils/gp/segadmin.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "libpq-fe.h"
#include "miscadmin.h"
#include "pqexpbuffer.h"
#include "access/genam.h"
#include "access/table.h"
#include "catalog/pg_proc.h"
#include "catalog/indexing.h"
#ifdef USE_INTERNAL_FTS
#include "catalog/gp_segment_configuration.h"
#endif
#include "cdb/cdbdisp_query.h"
#include "cdb/cdbutil.h"
#include "cdb/cdbvars.h"
#include "cdb/cdbfts.h"
#include "common/hashfn.h"
#include "postmaster/startup.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
#include "catalog/gp_indexing.h"
#define MASTER_ONLY 0x1
#define UTILITY_MODE 0x2
#define SUPERUSER 0x4
#define READ_ONLY 0x8
#define SEGMENT_ONLY 0x10
#define STANDBY_ONLY 0x20
#define SINGLE_USER_MODE 0x40
/* look up a particular segment */
static GpSegConfigEntry *
get_segconfig(int16 dbid)
{
return dbid_get_dbinfo(dbid);
}
/* Convenience routine to look up the mirror for a given segment index */
static int16
content_get_mirror_dbid(int16 contentid)
{
return contentid_get_dbid(contentid, GP_SEGMENT_CONFIGURATION_ROLE_MIRROR, false /* false == current, not
* preferred, role */ );
}
/* Tell the caller whether a mirror exists at a given segment index */
static bool
segment_has_mirror(int16 contentid)
{
return content_get_mirror_dbid(contentid) != 0;
}
/*
* As the function name says, test whether a given dbid is the dbid of the
* standby master.
*/
static bool
dbid_is_master_standby(int16 dbid)
{
int16 standbydbid = content_get_mirror_dbid(MASTER_CONTENT_ID);
return (standbydbid == dbid);
}
/*
* Tell the caller whether a standby master is defined in the system.
*/
static bool
standby_exists()
{
return segment_has_mirror(MASTER_CONTENT_ID);
}
/*
* Get the highest dbid defined in the system. We AccessExclusiveLock
* gp_segment_configuration to prevent races but no one should be calling
* this code concurrently if we've done our job right.
*/
static int16
get_maxdbid()
{
return cdbcomponent_get_maxdbid();
}
/**
* Get an available dbid value. We AccessExclusiveLock
* gp_segment_configuration to prevent races but no one should be calling
* this code concurrently if we've done our job right.
*/
static int16
get_availableDbId()
{
return cdbcomponent_get_availableDbId();
}
/*
* Get the highest contentid defined in the system. As above, we
* AccessExclusiveLock gp_segment_configuration to prevent races but no
* one should be calling this code concurrently if we've done our job right.
*/
static int16
get_maxcontentid()
{
return cdbcomponent_get_maxcontentid();
}
/*
* Check that the code is being called in right context.
*/
static void
mirroring_sanity_check(int flags, const char *func)
{
if ((flags & MASTER_ONLY) == MASTER_ONLY)
{
if (GpIdentity.dbid == UNINITIALIZED_GP_IDENTITY_VALUE)
elog(ERROR, "%s requires valid GpIdentity dbid", func);
if (!IS_QUERY_DISPATCHER())
elog(ERROR, "%s must be run on the master", func);
}
if ((flags & UTILITY_MODE) == UTILITY_MODE)
{
if (Gp_role != GP_ROLE_UTILITY)
elog(ERROR, "%s must be run in utility mode", func);
}
if ((flags & SINGLE_USER_MODE) == SINGLE_USER_MODE)
{
if (IsUnderPostmaster)
elog(ERROR, "%s must be run in single-user mode", func);
}
if ((flags & SUPERUSER) == SUPERUSER)
{
if (!superuser())
elog(ERROR, "%s can only be run by a superuser", func);
}
if ((flags & SEGMENT_ONLY) == SEGMENT_ONLY)
{
if (GpIdentity.dbid == UNINITIALIZED_GP_IDENTITY_VALUE)
elog(ERROR, "%s requires valid GpIdentity dbid", func);
if (IS_QUERY_DISPATCHER())
elog(ERROR, "%s cannot be run on the master", func);
}
if ((flags & STANDBY_ONLY) == STANDBY_ONLY)
{
if (GpIdentity.dbid == UNINITIALIZED_GP_IDENTITY_VALUE)
elog(ERROR, "%s requires valid GpIdentity dbid", func);
if (!dbid_is_master_standby(GpIdentity.dbid))
elog(ERROR, "%s can only be run on the standby master", func);
}
}
/*
* Remove a gp_segment_configuration entry
*/
static void
remove_segment_config(int16 dbid)
{
#ifdef USE_INTERNAL_FTS
int numDel = 0;
ScanKeyData scankey;
SysScanDesc sscan;
HeapTuple tuple;
Relation rel;
rel = table_open(GpSegmentConfigRelationId, RowExclusiveLock);
ScanKeyInit(&scankey,
Anum_gp_segment_configuration_dbid,
BTEqualStrategyNumber, F_INT2EQ,
Int16GetDatum(dbid));
sscan = systable_beginscan(rel, GpSegmentConfigDbidWarehouseIndexId, true,
NULL, 1, &scankey);
while ((tuple = systable_getnext(sscan)) != NULL)
{
Datum attr;
bool isNull;
Oid warehouseid = InvalidOid;
attr = heap_getattr(tuple, Anum_gp_segment_configuration_warehouseid,
RelationGetDescr(rel), &isNull);
Assert(!isNull);
warehouseid = DatumGetObjectId(attr);
if (!OidIsValid(warehouseid) || warehouseid == GetCurrentWarehouseId())
{
CatalogTupleDelete(rel, &tuple->t_self);
numDel++;
}
}
systable_endscan(sscan);
Assert(numDel > 0);
table_close(rel, NoLock);
#else
delSegment(dbid);
#endif
}
#ifdef USE_INTERNAL_FTS
static void
add_segment_config_entry(GpSegConfigEntry *i)
{
Relation rel = table_open(GpSegmentConfigRelationId, AccessExclusiveLock);
Datum values[Natts_gp_segment_configuration];
bool nulls[Natts_gp_segment_configuration];
HeapTuple tuple;
MemSet(nulls, false, sizeof(nulls));
values[Anum_gp_segment_configuration_dbid - 1] = Int16GetDatum(i->dbid);
values[Anum_gp_segment_configuration_content - 1] = Int16GetDatum(i->segindex);
values[Anum_gp_segment_configuration_role - 1] = CharGetDatum(i->role);
values[Anum_gp_segment_configuration_preferred_role - 1] =
CharGetDatum(i->preferred_role);
values[Anum_gp_segment_configuration_mode - 1] =
CharGetDatum(i->mode);
values[Anum_gp_segment_configuration_status - 1] =
CharGetDatum(i->status);
values[Anum_gp_segment_configuration_port - 1] =
Int32GetDatum(i->port);
values[Anum_gp_segment_configuration_hostname - 1] =
CStringGetTextDatum(i->hostname);
values[Anum_gp_segment_configuration_address - 1] =
CStringGetTextDatum(i->address);
values[Anum_gp_segment_configuration_datadir - 1] =
CStringGetTextDatum(i->datadir);
values[Anum_gp_segment_configuration_warehouseid - 1] =
ObjectIdGetDatum(i->warehouseid);
tuple = heap_form_tuple(RelationGetDescr(rel), values, nulls);
/* insert a new tuple */
CatalogTupleInsert(rel, tuple);
table_close(rel, NoLock);
}
#endif
static void
add_segment(GpSegConfigEntry *new_segment_information)
{
int16 primary_dbid = new_segment_information->dbid;
if (new_segment_information->role == GP_SEGMENT_CONFIGURATION_ROLE_MIRROR)
{
primary_dbid = contentid_get_dbid(new_segment_information->segindex, GP_SEGMENT_CONFIGURATION_ROLE_PRIMARY, false /* false == current, not
* preferred, role */ );
if (!primary_dbid)
elog(ERROR, "contentid %i does not point to an existing segment",
new_segment_information->segindex);
/*
* no mirrors should be defined
*/
if (segment_has_mirror(new_segment_information->segindex))
elog(ERROR, "segment already has a mirror defined");
/*
* figure out if the preferred role of this mirror needs to be primary
* or mirror (no preferred primary -- make this one the preferred
* primary)
*/
int preferredPrimaryDbId = contentid_get_dbid(new_segment_information->segindex, GP_SEGMENT_CONFIGURATION_ROLE_PRIMARY, true /* preferred role */ );
if (preferredPrimaryDbId == 0 && new_segment_information->preferred_role == GP_SEGMENT_CONFIGURATION_ROLE_MIRROR)
{
elog(NOTICE, "override preferred_role of this mirror as primary to support rebalance operation.");
new_segment_information->preferred_role = GP_SEGMENT_CONFIGURATION_ROLE_PRIMARY;
}
}
#ifdef USE_INTERNAL_FTS
add_segment_config_entry(new_segment_information);
#else
addSegment(new_segment_information);
#endif
}
/*
* Tell the master about a new primary segment.
*
* gp_add_segment_primary(hostname, address, port)
*
* Args:
* hostname - host name string
* address - either hostname or something else
* port - port number
* datadir - absolute path to primary data directory.
*
* Returns the dbid of the new segment.
*/
Datum
gp_add_segment_primary(PG_FUNCTION_ARGS)
{
GpSegConfigEntry new;
MemSet(&new, 0, sizeof(GpSegConfigEntry));
if (PG_ARGISNULL(0))
elog(ERROR, "hostname cannot be NULL");
new.hostname = TextDatumGetCString(PG_GETARG_DATUM(0));
if (PG_ARGISNULL(1))
elog(ERROR, "address cannot be NULL");
new.address = TextDatumGetCString(PG_GETARG_DATUM(1));
if (PG_ARGISNULL(2))
elog(ERROR, "port cannot be NULL");
new.port = PG_GETARG_INT32(2);
if (PG_ARGISNULL(3))
elog(ERROR, "datadir cannot be NULL");
new.datadir = TextDatumGetCString(PG_GETARG_DATUM(3));
mirroring_sanity_check(MASTER_ONLY | SUPERUSER, "gp_add_segment_primary");
new.segindex = get_maxcontentid() + 1;
new.dbid = get_availableDbId();
new.role = GP_SEGMENT_CONFIGURATION_ROLE_PRIMARY;
new.preferred_role = GP_SEGMENT_CONFIGURATION_ROLE_PRIMARY;
new.mode = GP_SEGMENT_CONFIGURATION_MODE_NOTINSYNC;
new.status = GP_SEGMENT_CONFIGURATION_STATUS_UP;
add_segment(&new);
PG_RETURN_INT16(new.dbid);
}
/*
* Currently this is called by `gpinitsystem`.
*
* This method shouldn't be called at all. `gp_add_segment_primary()` and
* `gp_add_segment_mirror()` should be used instead. This is to avoid setting
* character values of role, preferred_role, mode, status, etc. outside database.
*/
Datum
gp_add_segment(PG_FUNCTION_ARGS)
{
GpSegConfigEntry new;
MemSet(&new, 0, sizeof(GpSegConfigEntry));
if (PG_ARGISNULL(0))
elog(ERROR, "dbid cannot be NULL");
new.dbid = PG_GETARG_INT16(0);
if (PG_ARGISNULL(1))
elog(ERROR, "content cannot be NULL");
new.segindex = PG_GETARG_INT16(1);
if (PG_ARGISNULL(2))
elog(ERROR, "role cannot be NULL");
new.role = PG_GETARG_CHAR(2);
if (PG_ARGISNULL(3))
elog(ERROR, "preferred_role cannot be NULL");
new.preferred_role = PG_GETARG_CHAR(3);
if (PG_ARGISNULL(4))
elog(ERROR, "mode cannot be NULL");
new.mode = PG_GETARG_CHAR(4);
if (PG_ARGISNULL(5))
elog(ERROR, "status cannot be NULL");
new.status = PG_GETARG_CHAR(5);
if (PG_ARGISNULL(6))
elog(ERROR, "port cannot be NULL");
new.port = PG_GETARG_INT32(6);
if (PG_ARGISNULL(7))
elog(ERROR, "hostname cannot be NULL");
new.hostname = TextDatumGetCString(PG_GETARG_DATUM(7));
if (PG_ARGISNULL(8))
elog(ERROR, "address cannot be NULL");
new.address = TextDatumGetCString(PG_GETARG_DATUM(8));
if (PG_ARGISNULL(9))
elog(ERROR, "datadir cannot be NULL");
new.datadir = TextDatumGetCString(PG_GETARG_DATUM(9));
if (new.segindex == MASTER_CONTENT_ID)
new.warehouseid = InvalidOid;
else
new.warehouseid = GetCurrentWarehouseId();
mirroring_sanity_check(MASTER_ONLY | SUPERUSER, "gp_add_segment");
new.mode = GP_SEGMENT_CONFIGURATION_MODE_NOTINSYNC;
elog(NOTICE, "mode is changed to GP_SEGMENT_CONFIGURATION_MODE_NOTINSYNC under walrep.");
add_segment(&new);
PG_RETURN_INT16(new.dbid);
}
/*
* Master function to remove a segment from all catalogs
*/
static void
remove_segment(int16 pridbid, int16 mirdbid)
{
/* Check that we're removing a mirror, not a primary */
get_segconfig(mirdbid);
remove_segment_config(mirdbid);
}
/*
* Remove knowledge of a segment from the master.
*
* gp_remove_segment(dbid)
*
* Args:
* dbid - db identifier
*
* Returns:
* true on success, otherwise error.
*/
Datum
gp_remove_segment(PG_FUNCTION_ARGS)
{
int16 dbid;
if (PG_ARGISNULL(0))
elog(ERROR, "dbid cannot be NULL");
dbid = PG_GETARG_INT16(0);
mirroring_sanity_check(MASTER_ONLY | SUPERUSER | UTILITY_MODE,
"gp_remove_segment");
remove_segment(dbid, dbid);
PG_RETURN_BOOL(true);
}
/*
* Add a mirror of an existing segment.
*
* gp_add_segment_mirror(contentid, hostname, address, port, datadir)
*/
Datum
gp_add_segment_mirror(PG_FUNCTION_ARGS)
{
GpSegConfigEntry new;
if (PG_ARGISNULL(0))
elog(ERROR, "contentid cannot be NULL");
new.segindex = PG_GETARG_INT16(0);
if (PG_ARGISNULL(1))
elog(ERROR, "hostname cannot be NULL");
new.hostname = TextDatumGetCString(PG_GETARG_DATUM(1));
if (PG_ARGISNULL(2))
elog(ERROR, "address cannot be NULL");
new.address = TextDatumGetCString(PG_GETARG_DATUM(2));
if (PG_ARGISNULL(3))
elog(ERROR, "port cannot be NULL");
new.port = PG_GETARG_INT32(3);
if (PG_ARGISNULL(4))
elog(ERROR, "datadir cannot be NULL");
new.datadir = TextDatumGetCString(PG_GETARG_DATUM(4));
if (new.segindex == MASTER_CONTENT_ID)
new.warehouseid = InvalidOid;
else
new.warehouseid = GetCurrentWarehouseId();
mirroring_sanity_check(MASTER_ONLY | SUPERUSER, "gp_add_segment_mirror");
new.dbid = get_availableDbId();
new.mode = GP_SEGMENT_CONFIGURATION_MODE_NOTINSYNC;
new.status = GP_SEGMENT_CONFIGURATION_STATUS_DOWN;
new.role = GP_SEGMENT_CONFIGURATION_ROLE_MIRROR;
new.preferred_role = GP_SEGMENT_CONFIGURATION_ROLE_MIRROR;
add_segment(&new);
PG_RETURN_INT16(new.dbid);
}
/*
* Remove a segment mirror.
*
* gp_remove_segment_mirror(contentid)
*
* Args:
* contentid - segment index at which to remove the mirror
*
* Returns:
* true upon success, otherwise throws error.
*/
Datum
gp_remove_segment_mirror(PG_FUNCTION_ARGS)
{
int16 contentid = 0;
int16 pridbid;
int16 mirdbid;
Relation rel;
if (PG_ARGISNULL(0))
elog(ERROR, "dbid cannot be NULL");
contentid = PG_GETARG_INT16(0);
mirroring_sanity_check(MASTER_ONLY | SUPERUSER, "gp_remove_segment_mirror");
#ifdef USE_INTERNAL_FTS
/* avoid races */
rel = heap_open(GpSegmentConfigRelationId, AccessExclusiveLock);
#else
(void) rel;
#endif
pridbid = contentid_get_dbid(contentid, GP_SEGMENT_CONFIGURATION_ROLE_PRIMARY, false /* false == current, not
* preferred, role */ );
if (!pridbid)
elog(ERROR, "no dbid for contentid %i", contentid);
if (!segment_has_mirror(contentid))
elog(ERROR, "segment does not have a mirror");
mirdbid = contentid_get_dbid(contentid, GP_SEGMENT_CONFIGURATION_ROLE_MIRROR, false /* false == current, not
* preferred, role */ );
if (!mirdbid)
elog(ERROR, "no mirror dbid for contentid %i", contentid);
remove_segment(pridbid, mirdbid);
#ifdef USE_INTERNAL_FTS
heap_close(rel, NoLock);
#endif
PG_RETURN_BOOL(true);
}
/*
* Add a master standby.
*
* gp_add_master_standby(hostname, address, [port])
*
* Args:
* hostname - as above
* address - as above
* port - the port number of new standby
*
* Returns:
* dbid of the new standby
*/
Datum
gp_add_master_standby_port(PG_FUNCTION_ARGS)
{
return gp_add_master_standby(fcinfo);
}
Datum
gp_add_master_standby(PG_FUNCTION_ARGS)
{
int maxdbid;
int16 master_dbid;
GpSegConfigEntry *config;
Relation gprel;
if (PG_ARGISNULL(0))
elog(ERROR, "host name cannot be NULL");
if (PG_ARGISNULL(1))
elog(ERROR, "address cannot be NULL");
if (PG_ARGISNULL(2))
elog(ERROR, "datadir cannot be NULL");
mirroring_sanity_check(MASTER_ONLY | UTILITY_MODE,
"gp_add_master_standby");
/* Check if the system is ok */
if (standby_exists())
elog(ERROR, "only a single master standby may be defined");
#ifdef USE_INTERNAL_FTS
/* Lock exclusively to avoid concurrent changes */
gprel = heap_open(GpSegmentConfigRelationId, AccessExclusiveLock);
#else
(void) gprel;
#endif
maxdbid = get_maxdbid();
/*
* Don't reference GpIdentity.dbid, as it is legitimate to set -1 for -b
* option in utility mode. Content ID = -1 AND role =
* GP_SEGMENT_CONFIGURATION_ROLE_PRIMARY is the definition of primary
* master.
*/
master_dbid = contentid_get_dbid(MASTER_CONTENT_ID,
GP_SEGMENT_CONFIGURATION_ROLE_PRIMARY, false);
config = get_segconfig(master_dbid);
config->dbid = maxdbid + 1;
config->role = GP_SEGMENT_CONFIGURATION_ROLE_MIRROR;
config->preferred_role = GP_SEGMENT_CONFIGURATION_ROLE_MIRROR;
config->mode = GP_SEGMENT_CONFIGURATION_MODE_INSYNC;
config->status = GP_SEGMENT_CONFIGURATION_STATUS_UP;
config->hostname = TextDatumGetCString(PG_GETARG_TEXT_P(0));
config->address = TextDatumGetCString(PG_GETARG_TEXT_P(1));
config->datadir = TextDatumGetCString(PG_GETARG_TEXT_P(2));
config->warehouseid = InvalidOid;
/* Use the new port number if specified */
if (PG_NARGS() > 3 && !PG_ARGISNULL(3))
config->port = PG_GETARG_INT32(3);
#ifdef USE_INTERNAL_FTS
add_segment_config_entry(config);
heap_close(gprel, NoLock);
#else
addSegment(config);
#endif
PG_RETURN_INT16(config->dbid);
}
/*
* Remove the master standby.
*
* gp_remove_master_standby()
*
* Returns:
* true upon success otherwise false
*/
Datum
gp_remove_master_standby(PG_FUNCTION_ARGS)
{
int16 dbid = master_standby_dbid();
mirroring_sanity_check(SUPERUSER | MASTER_ONLY | UTILITY_MODE,
"gp_remove_master_standby");
if (!dbid)
elog(ERROR, "no master standby defined");
remove_segment(GpIdentity.dbid, dbid);
PG_RETURN_BOOL(true);
}
/*
* Update gp_segment_configuration to activate a standby.
*/
static void
catalog_activate_standby(int16 standby_dbid, int16 master_dbid)
{
#ifdef USE_INTERNAL_FTS
/* we use AccessExclusiveLock to prevent races */
Relation rel = table_open(GpSegmentConfigRelationId, AccessExclusiveLock);
HeapTuple tuple;
ScanKeyData scankey;
SysScanDesc sscan;
int numDel = 0;
/* first, delete the old master */
ScanKeyInit(&scankey,
Anum_gp_segment_configuration_dbid,
BTEqualStrategyNumber, F_INT2EQ,
Int16GetDatum(master_dbid));
sscan = systable_beginscan(rel, GpSegmentConfigDbidWarehouseIndexId, true,
NULL, 1, &scankey);
while ((tuple = systable_getnext(sscan)) != NULL)
{
CatalogTupleDelete(rel, &tuple->t_self);
numDel++;
}
systable_endscan(sscan);
if (0 == numDel)
elog(ERROR, "cannot find old master, dbid %i", master_dbid);
/* now, set out rows for old standby. */
ScanKeyInit(&scankey,
Anum_gp_segment_configuration_dbid,
BTEqualStrategyNumber, F_INT2EQ,
Int16GetDatum(standby_dbid));
sscan = systable_beginscan(rel, GpSegmentConfigDbidWarehouseIndexId, true,
NULL, 1, &scankey);
tuple = systable_getnext(sscan);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cannot find standby, dbid %i", standby_dbid);
tuple = heap_copytuple(tuple);
/* old standby keeps its previous dbid. */
((Form_gp_segment_configuration) GETSTRUCT(tuple))->role = GP_SEGMENT_CONFIGURATION_ROLE_PRIMARY;
((Form_gp_segment_configuration) GETSTRUCT(tuple))->preferred_role = GP_SEGMENT_CONFIGURATION_ROLE_PRIMARY;
CatalogTupleUpdate(rel, &tuple->t_self, tuple);
systable_endscan(sscan);
table_close(rel, NoLock);
#else
activateStandby(standby_dbid, master_dbid);
#endif
}
/*
* Activate a standby. To do this, we need to update gp_segment_configuration.
*
* Returns:
* true upon success, otherwise throws error.
*/
bool
gp_activate_standby(void)
{
int16 standby_dbid = GpIdentity.dbid;
int16 master_dbid;
master_dbid = contentid_get_dbid(MASTER_CONTENT_ID, GP_SEGMENT_CONFIGURATION_ROLE_PRIMARY, true);
/*
* This call comes from Startup process post checking state in pg_control
* file to make sure its standby. If user calls (ideally SHOULD NOT) but
* just for troubleshooting or wired case must make usre its only executed
* on Standby. So, this checking should return after matching DBIDs only
* for StartUp Process, to cover for case of crash after updating the
* catalogs during promote.
*/
if (am_startup && (master_dbid == standby_dbid))
{
/*
* Job is already done, nothing needs to be done. We mostly crashed
* after updating the catalogs.
*/
return true;
}
mirroring_sanity_check(SUPERUSER | UTILITY_MODE | STANDBY_ONLY,
PG_FUNCNAME_MACRO);
catalog_activate_standby(standby_dbid, master_dbid);
/* done */
return true;
}
Datum
gp_request_fts_probe_scan(PG_FUNCTION_ARGS)
{
if (Gp_role != GP_ROLE_DISPATCH)
{
elog(ERROR, "this function can only be called by master (without utility mode)");
PG_RETURN_BOOL(false);
}
FtsNotifyProber();
PG_RETURN_BOOL(true);
}
Datum
gp_rewrite_segments_info(PG_FUNCTION_ARGS)
{
#ifdef USE_INTERNAL_FTS
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Should not call this function when using internal fts module")));
#else
char * content = NULL;
if (PG_ARGISNULL(0)) {
elog(ERROR, "content cannot be NULL");
PG_RETURN_BOOL(false);
}
content = TextDatumGetCString(PG_GETARG_DATUM(0));
if (FaultInjector_InjectFaultIfSet("fts_probe",
DDLNotSpecified,
"" /* databaseName */,
"" /* tableName */) == FaultInjectorTypeSkip)
{
PG_RETURN_BOOL(true);
}
rewriteSegments(content, false);
PG_RETURN_BOOL(true);
#endif
}
Datum
gp_update_segment_configuration_mode_status(PG_FUNCTION_ARGS)
{
int dbid = -1;
char mode,status;
if (PG_ARGISNULL(0)) {
elog(ERROR, "dbid cannot be NULL");
PG_RETURN_BOOL(false);
}
if (PG_ARGISNULL(1)) {
elog(ERROR, "mode cannot be NULL");
PG_RETURN_BOOL(false);
}
if (PG_ARGISNULL(2))
{
elog(ERROR, "status cannot be NULL");
PG_RETURN_BOOL(false);
}
dbid = PG_GETARG_INT16(0);
mode = PG_GETARG_CHAR(1);
status = PG_GETARG_CHAR(2);
if (dbid < 0) {
elog(ERROR, "dbid should not less than 0.");
PG_RETURN_BOOL(false);
}
if (mode != GP_SEGMENT_CONFIGURATION_MODE_INSYNC &&
mode != GP_SEGMENT_CONFIGURATION_MODE_NOTINSYNC) {
elog(ERROR, "mode shoud be sync or nosync");
PG_RETURN_BOOL(false);
}
if (status != GP_SEGMENT_CONFIGURATION_STATUS_UP &&
status != GP_SEGMENT_CONFIGURATION_STATUS_DOWN) {
elog(ERROR, "mode shoud be up or down");
PG_RETURN_BOOL(false);
}
#ifdef USE_INTERNAL_FTS
/* we use AccessExclusiveLock to prevent races */
Relation rel = table_open(GpSegmentConfigRelationId, AccessExclusiveLock);
HeapTuple tuple;
ScanKeyData scankey[2];
SysScanDesc sscan;
/* now, set out rows for old standby. */
ScanKeyInit(&scankey[0],
Anum_gp_segment_configuration_dbid,
BTEqualStrategyNumber, F_INT2EQ,
Int16GetDatum(dbid));
ScanKeyInit(&scankey[1],
Anum_gp_segment_configuration_warehouseid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(GetCurrentWarehouseId()));
sscan = systable_beginscan(rel, GpSegmentConfigDbidWarehouseIndexId, true,
NULL, 2, scankey);
tuple = systable_getnext(sscan);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "cannot find, dbid %i", dbid);
tuple = heap_copytuple(tuple);
/* old standby keeps its previous dbid. */
((Form_gp_segment_configuration) GETSTRUCT(tuple))->mode = mode;
((Form_gp_segment_configuration) GETSTRUCT(tuple))->status = status;
CatalogTupleUpdate(rel, &tuple->t_self, tuple);
systable_endscan(sscan);
table_close(rel, NoLock);
#else
updateSegmentModeStatus(dbid, mode, status);
#endif
PG_RETURN_BOOL(true);
}