1174 lines
44 KiB
C++
1174 lines
44 KiB
C++
/* -------------------------------------------------------------------------
|
|
*
|
|
* execClusterResize.cpp
|
|
* MPPDB ClusterResizing relevant routines
|
|
*
|
|
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* IDENTIFICATION
|
|
* src/gausskernel/runtime/executor/execClusterResize.cpp
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
#include "knl/knl_variable.h"
|
|
|
|
#include "access/tableam.h"
|
|
#include "catalog/heap.h"
|
|
#include "catalog/index.h"
|
|
#include "catalog/namespace.h"
|
|
#include "catalog/pg_namespace.h"
|
|
#include "catalog/pgxc_group.h"
|
|
#include "executor/executor.h"
|
|
#include "executor/nodeModifyTable.h"
|
|
#include "optimizer/clauses.h"
|
|
#include "parser/analyze.h"
|
|
#include "parser/parsetree.h"
|
|
#include "pgxc/pgxc.h"
|
|
#include "pgxc/redistrib.h"
|
|
#include "tcop/utility.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/elog.h"
|
|
#include "utils/guc.h"
|
|
#include "utils/inval.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/rel_gs.h"
|
|
#include "utils/syscache.h"
|
|
#include "utils/snapmgr.h"
|
|
|
|
#include "storage/lmgr.h"
|
|
|
|
/*
|
|
* ---------------------------------------------------------------------------------
|
|
* *Local functions/variables declaration fields*
|
|
* ---------------------------------------------------------------------------------
|
|
*/
|
|
/* delete delta table definition */
|
|
#define Natts_pg_delete_delta 3
|
|
|
|
#define Anum_pg_delete_delta_xcnodeid_and_dntableoid 1
|
|
#define Anum_pg_delete_delta_tablebucketid_and_ctid 2
|
|
|
|
#define RANGE_SCAN_IN_REDIS "tidge+tid+pg_get_redis_rel_start_ctid+tidle+tid+pg_get_redis_rel_end_ctid+"
|
|
|
|
static Node* eval_dnstable_func_mutator(
|
|
Relation rel, Node* node, StringInfo qual_str, RangeScanInRedis *rangeScanInRedis, bool isRoot);
|
|
|
|
static inline bool redis_tupleid_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs);
|
|
|
|
static inline bool redis_blocknum_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs);
|
|
|
|
static inline bool redis_offset_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs);
|
|
|
|
#define REDIS_TUPLEID_RETRIVE_FUNCSIG(rettype, argstype, nargs) \
|
|
(((nargs) == 1 && (rettype) == TIDOID && (argstype)[0] == TEXTOID) || \
|
|
((nargs) == 4 && (rettype) == TIDOID && (argstype)[0] == TEXTOID && (argstype)[1] == NAMEOID && \
|
|
(argstype)[2] == INT4OID && (argstype)[3] == INT4OID))
|
|
|
|
static inline bool redis_tupleid_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs)
|
|
|
|
{
|
|
if (pg_strcasecmp(funcname, "pg_get_redis_rel_start_ctid") == 0 &&
|
|
REDIS_TUPLEID_RETRIVE_FUNCSIG(rettype, argstype, nargs)) {
|
|
return true;
|
|
}
|
|
|
|
if (pg_strcasecmp(funcname, "pg_get_redis_rel_end_ctid") == 0 &&
|
|
REDIS_TUPLEID_RETRIVE_FUNCSIG(rettype, argstype, nargs)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static inline bool redis_offset_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs)
|
|
|
|
{
|
|
if (pg_strcasecmp(funcname, "pg_tupleid_get_offset") == 0 &&
|
|
(nargs == 1 && rettype == INT4OID && argstype[0] == TIDOID)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static inline bool redis_blocknum_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs)
|
|
|
|
{
|
|
if (pg_strcasecmp(funcname, "pg_tupleid_get_blocknum") == 0 &&
|
|
(nargs == 1 && rettype == INT8OID && argstype[0] == TIDOID)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static inline bool redis_ctid_retrive_function(const char* funcname, Oid rettype, const Oid* argstype, int nargs)
|
|
|
|
{
|
|
if (pg_strcasecmp(funcname, "pg_tupleid_get_ctid_to_bigint") == 0 &&
|
|
(nargs == 1 && rettype == INT8OID && argstype[0] == TIDOID)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* - Brief: Record the given tuple's tupleid into pg_delete_delta table
|
|
* - Parameter:
|
|
* @rel: target relation of UPDATE/DELETE operation
|
|
* @tupleid: tupleid that needs record
|
|
* - Return:
|
|
* no return value
|
|
*/
|
|
void RecordDeletedTuple(Oid relid, int2 bucketid, const ItemPointer tupleid, const Relation deldelta_rel)
|
|
{
|
|
Assert(deldelta_rel);
|
|
|
|
Datum values[Natts_pg_delete_delta];
|
|
bool nulls[Natts_pg_delete_delta];
|
|
HeapTuple tup = NULL;
|
|
|
|
/* Iterate through attributes initializing nulls and values */
|
|
for (int i = 0; i < Natts_pg_delete_delta; i++) {
|
|
nulls[i] = false;
|
|
values[i] = (Datum)0;
|
|
}
|
|
values[Anum_pg_delete_delta_xcnodeid_and_dntableoid - 1] =
|
|
UInt64GetDatum(((uint64)u_sess->pgxc_cxt.PGXCNodeIdentifier << 32) | relid);
|
|
values[Anum_pg_delete_delta_tablebucketid_and_ctid - 1] =
|
|
UInt64GetDatum(((uint64)ItemPointerGetBlockNumber(tupleid) << 16) | ItemPointerGetOffsetNumber(tupleid));
|
|
if (bucketid != InvalidBktId) {
|
|
values[Anum_pg_delete_delta_tablebucketid_and_ctid - 1] |= ((uint64)bucketid << 48);
|
|
}
|
|
/* Record delta */
|
|
tup = heap_form_tuple(RelationGetDescr(deldelta_rel), values, nulls);
|
|
(void)simple_heap_insert(deldelta_rel, tup);
|
|
|
|
tableam_tops_free_tuple(tup);
|
|
}
|
|
|
|
/*
|
|
* - Brief: get and open delete_delta rel
|
|
* - Parameter:
|
|
* @rel: target relation of UPDATE/DELETE/TRUNCATE operation
|
|
* @lockmode: lock mode
|
|
* @isMultiCatchup: multi catchup delta or not
|
|
* - Return:
|
|
* delete_delta rel
|
|
*/
|
|
/*
|
|
* - Brief: Determine if the relation is under cluster resizing operation
|
|
* - Parameter:
|
|
* @rel: relation that needs to check
|
|
* - Return:
|
|
* @TRUE: relation is under cluster resizing
|
|
* @FALSE: relation is not under cluster resizing
|
|
*/
|
|
bool RelationInClusterResizing(const Relation rel)
|
|
{
|
|
Assert(rel != NULL);
|
|
|
|
/* Check relation's append_mode status */
|
|
if (!IsInitdb && RelationInRedistribute(rel))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* - Brief: Determine if the relation is under cluster resizing read only operation
|
|
* - Parameter:
|
|
* @rel: relation that needs to check
|
|
* - Return:
|
|
* @TRUE: relation is under cluster resizing read only
|
|
* @FALSE: relation is not under cluster resizing read only
|
|
*/
|
|
bool RelationInClusterResizingReadOnly(const Relation rel)
|
|
{
|
|
Assert(rel != NULL);
|
|
|
|
/* Check relation's append_mode status */
|
|
if (!IsInitdb && (RelationInRedistributeReadOnly(rel) || RelationInRedistributeEndCatchup(rel)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* @Description: check whether relation is in redistribution though range variable.
|
|
* @in range_var: range variable which stored relation info.
|
|
* @return: true for in redistribution.
|
|
*/
|
|
bool CheckRangeVarInRedistribution(const RangeVar* range_var)
|
|
{
|
|
Relation relation;
|
|
Oid relid;
|
|
bool in_redis = false;
|
|
|
|
relid = RangeVarGetRelid(range_var, AccessShareLock, true);
|
|
|
|
if (OidIsValid(relid)) {
|
|
relation = relation_open(relid, NoLock);
|
|
/* If the relation is index, we should check the related table is resizing or not. */
|
|
if (RelationIsIndex(relation)) {
|
|
Oid heapOid = IndexGetRelation(relid, false);
|
|
Relation heapRelation = relation_open(heapOid, AccessShareLock);
|
|
in_redis = RelationInClusterResizing(heapRelation);
|
|
relation_close(heapRelation, AccessShareLock);
|
|
} else {
|
|
in_redis = RelationInClusterResizing(relation);
|
|
}
|
|
|
|
relation_close(relation, NoLock);
|
|
UnlockRelationOid(relid, AccessShareLock);
|
|
}
|
|
return in_redis;
|
|
}
|
|
|
|
/*
|
|
* - Brief: Determine if the table name is delete_delta table.
|
|
* - Parameter:
|
|
* @relname: name of target table
|
|
* - Return:
|
|
* @TRUE: the table is delete_delta table
|
|
* @FALSE: the table is not delete_delta table
|
|
*/
|
|
bool RelationIsDeleteDeltaTable(char* delete_delta_name)
|
|
{
|
|
Oid relid;
|
|
uint64 val;
|
|
char* endptr = NULL;
|
|
HeapTuple tuple;
|
|
|
|
if (IsInitdb) {
|
|
return false;
|
|
}
|
|
|
|
if (strncmp(delete_delta_name, "pg_delete_delta_", 16) != 0) {
|
|
return false;
|
|
}
|
|
|
|
val = strtoull(delete_delta_name + 16, &endptr, 0);
|
|
|
|
if ((errno == ERANGE) || (errno != 0 && val == 0)) {
|
|
return false;
|
|
}
|
|
|
|
if (endptr == delete_delta_name + 16 || *endptr != '\0') {
|
|
return false;
|
|
}
|
|
relid = (Oid)val;
|
|
|
|
if (!OidIsValid(relid)) {
|
|
return false;
|
|
}
|
|
|
|
tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
|
|
if (!HeapTupleIsValid(tuple)) {
|
|
elog(WARNING, "Table %u related to %s does not exists.", relid, delete_delta_name);
|
|
return false;
|
|
}
|
|
ReleaseSysCache(tuple);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* - Brief: Determine if the Progress is under cluster resizing status
|
|
* - Return:
|
|
* @TRUE: Progress is under cluster resizing
|
|
* @FALSE: Progress is not under cluster resizing
|
|
*/
|
|
bool ClusterResizingInProgress()
|
|
{
|
|
Relation pgxc_group_rel = NULL;
|
|
TableScanDesc scan;
|
|
HeapTuple tup = NULL;
|
|
Datum datum;
|
|
bool isNull = false;
|
|
bool result = false;
|
|
|
|
pgxc_group_rel = heap_open(PgxcGroupRelationId, AccessShareLock);
|
|
|
|
if (!pgxc_group_rel) {
|
|
ereport(PANIC, (errcode(ERRCODE_RELATION_OPEN_ERROR), errmsg("can not open pgxc_group")));
|
|
}
|
|
|
|
scan = tableam_scan_begin(pgxc_group_rel, SnapshotNow, 0, NULL);
|
|
while ((tup = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) {
|
|
datum = heap_getattr(tup, Anum_pgxc_group_in_redistribution, RelationGetDescr(pgxc_group_rel), &isNull);
|
|
|
|
if ('y' == DatumGetChar(datum)) {
|
|
result = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
tableam_scan_end(scan);
|
|
heap_close(pgxc_group_rel, AccessShareLock);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* - Brief: get the name of delete_delta table
|
|
* - Parameter:
|
|
* @relname: name of target table
|
|
* @delta_delta_name: output value for delete_delta table name
|
|
* @isMultiCatchup: multi catchup delta or not
|
|
* - Return:
|
|
* no return value
|
|
*/
|
|
static inline void RelationGetDeleteDeltaTableName(Relation rel, char* delete_delta_name, bool isMultiCatchup)
|
|
{
|
|
int rc = 0;
|
|
|
|
/* Check if output parameter it not palloc()-ed from caller side */
|
|
if (delete_delta_name == NULL || rel == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Invalid parameter in function '%s'", __FUNCTION__)));
|
|
}
|
|
|
|
/*
|
|
* Look up Relation's reloptions to get table's cnoid to
|
|
* form the name of delete_delta table
|
|
*/
|
|
if (!IsInitdb) {
|
|
if (RelationInClusterResizing(rel) && !RelationInClusterResizingReadOnly(rel)) {
|
|
if (isMultiCatchup) {
|
|
rc = snprintf_s(delete_delta_name,
|
|
NAMEDATALEN,
|
|
NAMEDATALEN - 1,
|
|
REDIS_MULTI_CATCHUP_DELETE_DELTA_TABLE_PREFIX "%u",
|
|
RelationGetRelCnOid(rel));
|
|
} else {
|
|
rc = snprintf_s(delete_delta_name,
|
|
NAMEDATALEN,
|
|
NAMEDATALEN - 1,
|
|
REDIS_DELETE_DELTA_TABLE_PREFIX "%u",
|
|
RelationGetRelCnOid(rel));
|
|
}
|
|
securec_check_ss(rc, "\0", "\0");
|
|
} else {
|
|
elog(LOG, "rel %s doesn't exist in redistributing", RelationGetRelationName(rel));
|
|
rc = snprintf_s(delete_delta_name,
|
|
NAMEDATALEN,
|
|
NAMEDATALEN - 1,
|
|
REDIS_DELETE_DELTA_TABLE_PREFIX "%s",
|
|
RelationGetRelationName(rel));
|
|
securec_check_ss(rc, "\0", "\0");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
Relation GetAndOpenDeleteDeltaRel(const Relation rel, LOCKMODE lockmode, bool isMultiCatchup)
|
|
{
|
|
Relation deldelta_rel;
|
|
Oid deldelta_relid;
|
|
char delete_delta_tablename[NAMEDATALEN];
|
|
Oid data_redis_namespace;
|
|
errno_t errorno;
|
|
|
|
errorno = memset_s(delete_delta_tablename, NAMEDATALEN, 0, NAMEDATALEN);
|
|
securec_check_c(errorno, "\0", "\0");
|
|
|
|
RelationGetDeleteDeltaTableName(rel, (char*)delete_delta_tablename, isMultiCatchup);
|
|
data_redis_namespace = get_namespace_oid("data_redis", false);
|
|
|
|
/* We are going to fetch the delete delta relation under data_redis schema. */
|
|
deldelta_relid = get_relname_relid(delete_delta_tablename, data_redis_namespace);
|
|
if (!OidIsValid(deldelta_relid)) {
|
|
/*
|
|
* If multi catchup delta table is not there, just return NULL. We should not
|
|
* report error, because it is a valid case. Multi catchup delta table is
|
|
* dropped in each catchup iteration.
|
|
*/
|
|
if (isMultiCatchup) {
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* To support Update or Delete during extension, we need to add 2 more columns.
|
|
* more columns. Limited by MaxHeapAttributeNumber, if the table already contains too many columns,
|
|
* we don't allow update or delete anymore, but insert statement can still proceed.
|
|
*/
|
|
if (((rel->rd_att->natts > (MaxHeapAttributeNumber - (Natts_pg_delete_delta - 1))) &&
|
|
!RELATION_IS_PARTITIONED(rel)) ||
|
|
((rel->rd_att->natts > (MaxHeapAttributeNumber - Natts_pg_delete_delta)) && RELATION_IS_PARTITIONED(rel))) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("do not support update or delete on table %s, when do cluster resizing on it.",
|
|
RelationGetRelationName(rel)),
|
|
errdetail("Can not support online extension, if the table contains too many columns")));
|
|
}
|
|
/* ERROR case, should never come here */
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_TABLE),
|
|
errmsg("delete delta table %s is not found when do cluster resizing table \"%s\"",
|
|
delete_delta_tablename,
|
|
RelationGetRelationName(rel))));
|
|
}
|
|
deldelta_rel = relation_open(deldelta_relid, lockmode);
|
|
elog(DEBUG1,
|
|
"Delete_delta table %s for relation %s being under cluster resizing is valid.",
|
|
delete_delta_tablename,
|
|
RelationGetRelationName(rel));
|
|
|
|
return deldelta_rel;
|
|
}
|
|
|
|
/*
|
|
* - Brief: Check the stmtment during online expansion, block unsupported ddl in cluster resizing.
|
|
* - Parameter:
|
|
* @rel: parsetree of DDL
|
|
* - Return:
|
|
* no return value
|
|
*/
|
|
void BlockUnsupportedDDL(const Node* parsetree)
|
|
{
|
|
if (IsInitdb) {
|
|
return;
|
|
}
|
|
|
|
Relation rel = NULL;
|
|
Oid relid = InvalidOid;
|
|
List* relidlist = NULL;
|
|
ListCell* lc = NULL;
|
|
LOCKMODE lockmode_getrelid = AccessShareLock;
|
|
LOCKMODE lockmode_openrel = AccessShareLock;
|
|
|
|
/*
|
|
* Check for shared-cache-inval messages before trying to access the
|
|
* relation. This is needed to cover the case where the name
|
|
* identifies a rel that has been dropped and recreated since the
|
|
* start of our transaction: if we don't flush the old syscache entry,
|
|
* then we'll latch onto that entry and suffer an error later.
|
|
*/
|
|
AcceptInvalidationMessages();
|
|
|
|
switch (nodeTag(parsetree)) {
|
|
case T_CreatedbStmt:
|
|
case T_AlterDatabaseStmt:
|
|
case T_AlterDatabaseSetStmt:
|
|
case T_CreateTableSpaceStmt:
|
|
case T_DropTableSpaceStmt:
|
|
case T_AlterTableSpaceOptionsStmt: {
|
|
if (ClusterResizingInProgress()) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command during online expansion", CreateCommandTag((Node*)parsetree))));
|
|
}
|
|
return;
|
|
} break;
|
|
|
|
case T_DropdbStmt: {
|
|
if (ClusterResizingInProgress() && !u_sess->attr.attr_common.xc_maintenance_mode) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command during online expansion", CreateCommandTag((Node*)parsetree))));
|
|
} else if (ClusterResizingInProgress()) {
|
|
ereport(WARNING, (errmsg("Drop database during online expansion in maintenance mode!")));
|
|
}
|
|
return;
|
|
} break;
|
|
|
|
/* Block CURSOR for while table in cluster resizing */
|
|
case T_PlannedStmt: {
|
|
PlannedStmt* stmt = (PlannedStmt*)parsetree;
|
|
relidlist = stmt->relationOids;
|
|
} break;
|
|
|
|
/* Block RENAME while table in cluster resizing */
|
|
case T_RenameStmt: {
|
|
RenameStmt* stmt = (RenameStmt*)parsetree;
|
|
|
|
switch (stmt->renameType) {
|
|
case OBJECT_SCHEMA: {
|
|
Oid nsOid = get_namespace_oid(stmt->subname, true);
|
|
TRANSFER_DISABLE_DDL(nsOid);
|
|
break;
|
|
}
|
|
case OBJECT_TABLE: {
|
|
if (stmt->relation != NULL) {
|
|
Oid relOid = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
|
|
if (OidIsValid(relOid)) {
|
|
Oid nsOid = GetNamespaceIdbyRelId(relOid);
|
|
UnlockRelationOid(relOid, AccessShareLock);
|
|
TRANSFER_DISABLE_DDL(nsOid);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (stmt->relation && CheckRangeVarInRedistribution(stmt->relation))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command during online expansion on '%s'",
|
|
CreateCommandTag((Node*)parsetree),
|
|
stmt->relation->relname)));
|
|
} break;
|
|
|
|
/* Block ALTER set schema while table in cluster resizing */
|
|
case T_AlterObjectSchemaStmt: {
|
|
AlterObjectSchemaStmt* stmt = (AlterObjectSchemaStmt*)parsetree;
|
|
|
|
/* disable alter table set schema when transfer */
|
|
if (stmt->relation != NULL) {
|
|
Oid relOid = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
|
|
if (OidIsValid(relOid)) {
|
|
Oid nsOid = GetNamespaceIdbyRelId(relOid);
|
|
UnlockRelationOid(relOid, AccessShareLock);
|
|
TRANSFER_DISABLE_DDL(nsOid);
|
|
|
|
if (stmt->newschema) {
|
|
nsOid = get_namespace_oid(stmt->newschema, true);
|
|
TRANSFER_DISABLE_DDL(nsOid);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (stmt->relation && CheckRangeVarInRedistribution(stmt->relation))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command during online expansion on '%s'",
|
|
CreateCommandTag((Node*)parsetree),
|
|
stmt->relation->relname)));
|
|
} break;
|
|
|
|
/* Block CREATE index while table in cluster resizing(for row table only) */
|
|
case T_IndexStmt: {
|
|
IndexStmt* stmt = (IndexStmt*)parsetree;
|
|
if (stmt->relation) {
|
|
relid = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
|
|
if (OidIsValid(relid)) {
|
|
Relation relation = relation_open(relid, NoLock);
|
|
bool inRedis = RelationIsRowFormat(relation) && RelationInClusterResizing(relation);
|
|
|
|
relation_close(relation, NoLock);
|
|
UnlockRelationOid(relid, AccessShareLock);
|
|
|
|
if (inRedis) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command during online expansion on '%s'",
|
|
CreateCommandTag((Node*)parsetree),
|
|
stmt->relation->relname)));
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
|
|
/* Block REINDEX while table in cluster resizing(for row table only) */
|
|
case T_ReindexStmt: {
|
|
ReindexStmt* stmt = (ReindexStmt*)parsetree;
|
|
if (stmt->relation) {
|
|
relid = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
|
|
if (OidIsValid(relid)) {
|
|
Relation relation = relation_open(relid, NoLock);
|
|
bool inRedis = false;
|
|
|
|
if (RelationIsRelation(relation)) {
|
|
inRedis = RelationIsRowFormat(relation) && RelationInClusterResizing(relation);
|
|
} else if (RelationIsIndex(relation)) {
|
|
Oid heapOid = IndexGetRelation(relid, false);
|
|
Relation heapRelation = relation_open(heapOid, AccessShareLock);
|
|
inRedis = RelationIsRowFormat(heapRelation) && RelationInClusterResizing(heapRelation);
|
|
relation_close(heapRelation, AccessShareLock);
|
|
}
|
|
relation_close(relation, NoLock);
|
|
UnlockRelationOid(relid, AccessShareLock);
|
|
|
|
if (inRedis) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command during online expansion on '%s'",
|
|
CreateCommandTag((Node*)parsetree),
|
|
stmt->relation->relname)));
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
|
|
/* Block ALTER-Table while table in cluster resizing */
|
|
case T_AlterTableStmt: {
|
|
AlterTableStmt* stmt = (AlterTableStmt*)parsetree;
|
|
AlterTableCmd* cmd = NULL;
|
|
foreach (lc, stmt->cmds) {
|
|
cmd = (AlterTableCmd*)lfirst(lc);
|
|
switch (cmd->subtype) {
|
|
case AT_TruncatePartition: {
|
|
/*
|
|
* We do not allow truncate partition when the target is in read only
|
|
* mode during online expansion time.
|
|
*/
|
|
if (stmt->relation) {
|
|
relid = RangeVarGetRelid(stmt->relation, lockmode_getrelid, true);
|
|
if (OidIsValid(relid)) {
|
|
/* disable alter table truncate partition during transfer */
|
|
if (CheckRangeVarInRedistribution(stmt->relation)) {
|
|
Oid nsOid = GetNamespaceIdbyRelId(relid);
|
|
TRANSFER_DISABLE_DDL(nsOid);
|
|
}
|
|
|
|
rel = relation_open(relid, NoLock);
|
|
if (RelationInClusterResizingReadOnly(rel)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command with '%s' option during online expansion on "
|
|
"'%s' because the object is in read only mode.",
|
|
CreateCommandTag((Node*)parsetree),
|
|
CreateAlterTableCommandTag(cmd->subtype),
|
|
RelationGetRelationName(rel))));
|
|
}
|
|
relation_close(rel, NoLock);
|
|
|
|
if (u_sess->attr.attr_sql.enable_parallel_ddl)
|
|
UnlockRelationOid(relid, lockmode_getrelid);
|
|
}
|
|
}
|
|
} break;
|
|
case AT_AddNodeList:
|
|
case AT_DeleteNodeList:
|
|
break;
|
|
|
|
case AT_ResetRelOptions:
|
|
case AT_SetRelOptions:
|
|
if (cmd->def) {
|
|
List* options = (List*)cmd->def;
|
|
ListCell* opt = NULL;
|
|
DefElem* def = NULL;
|
|
foreach (opt, options) {
|
|
def = (DefElem*)lfirst(opt);
|
|
|
|
if (pg_strcasecmp(def->defname, "append_mode") == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If rel option contain append_mode, then not check. */
|
|
if (opt != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
/* fall through */
|
|
default: {
|
|
if (stmt->relation && !u_sess->attr.attr_sql.enable_cluster_resize &&
|
|
CheckRangeVarInRedistribution(stmt->relation))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command with '%s' option during online expansion on '%s'",
|
|
CreateCommandTag((Node*)parsetree),
|
|
CreateAlterTableCommandTag(cmd->subtype),
|
|
stmt->relation->relname)));
|
|
} break;
|
|
}
|
|
}
|
|
return;
|
|
} break;
|
|
|
|
/* Block CREATE-RULE statements while target table in cluster resizing */
|
|
case T_RuleStmt: {
|
|
RuleStmt* stmt = (RuleStmt*)parsetree;
|
|
if (stmt->relation) {
|
|
relid = RangeVarGetRelid(stmt->relation, lockmode_getrelid, true);
|
|
relidlist = list_make1_oid(relid);
|
|
}
|
|
} break;
|
|
|
|
/* Block CREATE SEQUENCE set schema while owner table in cluster resizing */
|
|
case T_CreateSeqStmt: {
|
|
CreateSeqStmt* stmt = (CreateSeqStmt*)parsetree;
|
|
List* owned_by = NULL;
|
|
DefElem* defel = NULL;
|
|
foreach (lc, stmt->options) {
|
|
defel = (DefElem*)lfirst(lc);
|
|
if (pg_strcasecmp(defel->defname, "owned_by") == 0 && nodeTag(defel->arg) == T_List) {
|
|
owned_by = (List*)defel->arg;
|
|
}
|
|
}
|
|
|
|
if (owned_by != NULL) {
|
|
int owned_len = list_length(owned_by);
|
|
if (owned_len != 1) {
|
|
List* relname = list_truncate(list_copy(owned_by), owned_len - 1);
|
|
RangeVar* r = makeRangeVarFromNameList(relname);
|
|
if (r && CheckRangeVarInRedistribution(r))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command during online expansion on '%s'",
|
|
CreateCommandTag((Node*)parsetree),
|
|
r->relname)));
|
|
}
|
|
}
|
|
} break;
|
|
|
|
/* Block ALTER SEQUENCE while owner table in cluster resizing */
|
|
case T_AlterSeqStmt: {
|
|
AlterSeqStmt* stmt = (AlterSeqStmt*)parsetree;
|
|
List* owned_by = NIL;
|
|
DefElem* defel = NULL;
|
|
foreach (lc, stmt->options) {
|
|
defel = (DefElem*)lfirst(lc);
|
|
if (pg_strcasecmp(defel->defname, "owned_by") == 0 && nodeTag(defel->arg) == T_List) {
|
|
owned_by = (List*)defel->arg;
|
|
}
|
|
}
|
|
|
|
if (owned_by != NULL) {
|
|
int owned_len = list_length(owned_by);
|
|
if (owned_len != 1) {
|
|
List* relname = list_truncate(list_copy(owned_by), owned_len - 1);
|
|
RangeVar* r = makeRangeVarFromNameList(relname);
|
|
if (r && CheckRangeVarInRedistribution(r))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command during online expansion on '%s'",
|
|
CreateCommandTag((Node*)parsetree),
|
|
r->relname)));
|
|
}
|
|
}
|
|
} break;
|
|
|
|
/* Block CLUSTER while table in cluster resizing */
|
|
case T_ClusterStmt: {
|
|
ClusterStmt* stmt = (ClusterStmt*)parsetree;
|
|
if (stmt->relation && CheckRangeVarInRedistribution(stmt->relation))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command during online expansion on '%s'",
|
|
CreateCommandTag((Node*)parsetree),
|
|
stmt->relation->relname)));
|
|
} break;
|
|
|
|
/* Block VACUUM FULL while table in cluster resizing */
|
|
case T_VacuumStmt: {
|
|
VacuumStmt* stmt = (VacuumStmt*)parsetree;
|
|
if ((stmt->options & VACOPT_VACUUM) || (stmt->options & VACOPT_MERGE)) {
|
|
if (stmt->relation) {
|
|
relid = RangeVarGetRelid(stmt->relation, lockmode_getrelid, true);
|
|
relidlist = list_make1_oid(relid);
|
|
}
|
|
}
|
|
if (stmt->options & VACOPT_FULL) {
|
|
if (OidIsValid(relid)) {
|
|
rel = relation_open(relid, lockmode_openrel);
|
|
if (RelationInClusterResizing(rel)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport 'VACUUM FULL' command during online expansion on '%s'",
|
|
RelationGetRelationName(rel))));
|
|
}
|
|
relation_close(rel, lockmode_openrel);
|
|
}
|
|
}
|
|
} break;
|
|
|
|
/* Block truncate DDL when the target table is read only in cluster resizing */
|
|
case T_TruncateStmt: {
|
|
ListCell* cell = NULL;
|
|
TruncateStmt* stmt = (TruncateStmt*)parsetree;
|
|
|
|
foreach (cell, stmt->relations) {
|
|
RangeVar* rv = (RangeVar*)lfirst(cell);
|
|
|
|
if (CheckRangeVarInRedistribution(rv)) {
|
|
Oid relOid = RangeVarGetRelid(rv, lockmode_getrelid, true);
|
|
if (OidIsValid(relOid)) {
|
|
Oid nsOid = GetNamespaceIdbyRelId(relOid);
|
|
UnlockRelationOid(relOid, lockmode_getrelid);
|
|
TRANSFER_DISABLE_DDL(nsOid);
|
|
}
|
|
}
|
|
|
|
rel = heap_openrv(rv, lockmode_openrel);
|
|
|
|
if (RelationInClusterResizingReadOnly(rel)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command during online expansion on '%s' because the object is in "
|
|
"read only mode.",
|
|
CreateCommandTag((Node*)parsetree),
|
|
RelationGetRelationName(rel))));
|
|
}
|
|
relation_close(rel, lockmode_openrel);
|
|
}
|
|
} break;
|
|
|
|
case T_DropStmt: {
|
|
DropStmt* stmt = (DropStmt*)parsetree;
|
|
switch (stmt->removeType) {
|
|
case OBJECT_TABLE: {
|
|
/* disable drop table when transfer */
|
|
ListCell* cell = NULL;
|
|
foreach (cell, stmt->objects) {
|
|
RangeVar* rel = makeRangeVarFromNameList((List*)lfirst(cell));
|
|
Oid relOid = RangeVarGetRelid(rel, AccessShareLock, true);
|
|
if (OidIsValid(relOid)) {
|
|
Oid nsOid = GetNamespaceIdbyRelId(relOid);
|
|
UnlockRelationOid(relOid, AccessShareLock);
|
|
TRANSFER_DISABLE_DDL(nsOid);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case OBJECT_SCHEMA: {
|
|
/* disable drop schema when transfer */
|
|
ListCell* cell = NULL;
|
|
foreach (cell, stmt->objects) {
|
|
List* objname = (List*)lfirst(cell);
|
|
char* name = NameListToString(objname);
|
|
Oid nsOid = get_namespace_oid(name, true);
|
|
TRANSFER_DISABLE_DDL(nsOid);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
} break;
|
|
|
|
case T_CreateStmt: {
|
|
/* disable create table when transfer */
|
|
CreateStmt* stmt = (CreateStmt*)parsetree;
|
|
if (stmt->relation != NULL) {
|
|
Oid nsOid = RangeVarGetCreationNamespace(stmt->relation);
|
|
TRANSFER_DISABLE_DDL(nsOid);
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
foreach (lc, relidlist) {
|
|
relid = lfirst_oid(lc);
|
|
if (OidIsValid(relid) && get_rel_name(relid) != NULL) {
|
|
rel = relation_open(relid, lockmode_openrel);
|
|
if (RelationInClusterResizing(rel)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unsupport '%s' command during online expansion on '%s'",
|
|
CreateCommandTag((Node*)parsetree),
|
|
RelationGetRelationName(rel))));
|
|
}
|
|
relation_close(rel, lockmode_openrel);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* - Brief: For online expanions, the shippable function is evaluated here, the module
|
|
* will be invoked in optimizer when do FQS evaluation, we have to define function
|
|
* as STABLE
|
|
* - Parameter:
|
|
* @funcid: oid of user defined function which is createed/dropped in scope of gs_redis
|
|
* - Return:
|
|
* @true: shippable
|
|
* @false: unshippable
|
|
*/
|
|
bool redis_func_shippable(Oid funcid)
|
|
{
|
|
const char* func_name = get_func_name(funcid);
|
|
Oid* argstype = NULL;
|
|
int nargs;
|
|
Oid rettype = InvalidOid;
|
|
bool result = false;
|
|
|
|
if (func_name == NULL) {
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function with OID %u does not exist", funcid)));
|
|
}
|
|
|
|
/* Fetch function signatures */
|
|
rettype = get_func_signature(funcid, &argstype, &nargs);
|
|
|
|
if (redis_tupleid_retrive_function(func_name, rettype, argstype, nargs)) {
|
|
/* tupleid retrive functions is shippable to datanodes */
|
|
result = true;
|
|
} else if (redis_offset_retrive_function(func_name, rettype, argstype, nargs)) {
|
|
result = true;
|
|
} else if (redis_blocknum_retrive_function(func_name, rettype, argstype, nargs)) {
|
|
result = true;
|
|
} else if (redis_ctid_retrive_function(func_name, rettype, argstype, nargs)) {
|
|
result = true;
|
|
}
|
|
|
|
/* pfree */
|
|
if (argstype != NULL) {
|
|
pfree_ext(argstype);
|
|
argstype = NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* - Brief: determine if given funcid reflects a dn-stable function
|
|
* - Parameter:
|
|
* @funcid: function oid that to evaluate
|
|
* - Return:
|
|
* @result: true:dnstable false: not-dnstable function
|
|
*/
|
|
bool redis_func_dnstable(Oid funcid)
|
|
{
|
|
const char* func_name = get_func_name(funcid);
|
|
Oid* argstype = NULL;
|
|
int nargs;
|
|
Oid rettype = InvalidOid;
|
|
bool result = false;
|
|
|
|
if (func_name == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
errmsg("function with OID %u does not exist when checking function dnstable", funcid)));
|
|
}
|
|
|
|
/* Fetch function signatures */
|
|
rettype = get_func_signature(funcid, &argstype, &nargs);
|
|
|
|
if (redis_tupleid_retrive_function(func_name, rettype, argstype, nargs)) {
|
|
/* tupleid retrive functions is dnstable */
|
|
result = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* - Brief: evaluate ctid functions into a const value to avoid per-scanning
|
|
* tuple invokation in seqscan.
|
|
* - Parameter:
|
|
* @rel: the rel being redistributing
|
|
* @original_quals: the original quals possible contains ctid_funcs
|
|
* @isRangeScanInRedis: if is a redis range scan
|
|
* - Return:
|
|
* @new_quals: quals which func call be replaced by a const
|
|
*/
|
|
List* eval_ctid_funcs(Relation rel, List* original_quals, RangeScanInRedis *rangeScanInRedis)
|
|
{
|
|
|
|
StringInfo qual_str = makeStringInfo();
|
|
/*
|
|
* we have to make a copy of the original quals, since the eval_dnstable_func_mutator
|
|
* will modify the it. the original qual will be needed again and again in later
|
|
* to be re-eval in partition table scans.
|
|
*/
|
|
List* new_quals = (List*)copyObject((const void*)(original_quals));
|
|
|
|
rangeScanInRedis->isRangeScanInRedis = false;
|
|
rangeScanInRedis->sliceTotal = 0;
|
|
rangeScanInRedis->sliceIndex = 0;
|
|
(void)eval_dnstable_func_mutator(rel, (Node*)new_quals, qual_str, rangeScanInRedis, true);
|
|
|
|
pfree_ext(qual_str->data);
|
|
pfree_ext(qual_str);
|
|
|
|
return new_quals;
|
|
}
|
|
|
|
static int32 get_expr_const_val(Node *val){
|
|
if (IsA(val, Const) && !((Const*)val)->constisnull && ((Const*)val)->consttype == INT4OID) {
|
|
return DatumGetInt32(((Const*)val)->constvalue);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* - Brief: working house for eval_dnstable_func() to evaluate dn stable function into a const
|
|
* value to avoid per-scanning tuple invocation in seqscan
|
|
* - Parameter:
|
|
* @rel: the rel being redistributing
|
|
* @node: expression node
|
|
* @qual_str: predicate pattern
|
|
* @isRangeScanInRedis: output to indicate if the predicate pattern is range scan in redis
|
|
* @isRoot: we want to compare the predicate pattern only once at root level
|
|
* - Return:
|
|
* @result: expression tree with dn stable function const-evaluated
|
|
*/
|
|
static Node* eval_dnstable_func_mutator(
|
|
Relation rel, Node* node, StringInfo qual_str, RangeScanInRedis *rangeScanInRedis, bool isRoot)
|
|
{
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
if (IS_PGXC_COORDINATOR)
|
|
return node;
|
|
|
|
switch (nodeTag(node)) {
|
|
case T_FuncExpr: {
|
|
FuncExpr* expr = (FuncExpr*)node;
|
|
|
|
/* flatten dn stable function into const value */
|
|
if (redis_func_dnstable(expr->funcid)) {
|
|
Node* new_const = NULL;
|
|
char* funcname = get_func_name(expr->funcid);
|
|
if (funcname == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
errmsg("operation expression function with OID %u does not exist.", expr->funcid)));
|
|
}
|
|
|
|
bool is_func_get_start_ctid = pg_strcasecmp(funcname, "pg_get_redis_rel_start_ctid") == 0;
|
|
bool is_func_get_end_ctid = pg_strcasecmp(funcname, "pg_get_redis_rel_end_ctid") == 0;
|
|
|
|
if (is_func_get_start_ctid || is_func_get_end_ctid){
|
|
int32 numSlices = get_expr_const_val((Node*)list_nth(expr->args, 2));
|
|
int32 idxSlices = get_expr_const_val((Node*)list_nth(expr->args, 3));
|
|
new_const = eval_redis_func_direct(rel, is_func_get_start_ctid, numSlices, idxSlices);
|
|
rangeScanInRedis->sliceIndex = idxSlices;
|
|
rangeScanInRedis->sliceTotal = numSlices;
|
|
} else {
|
|
new_const = eval_const_expressions(NULL, node);
|
|
}
|
|
appendStringInfoString(qual_str, get_func_name(expr->funcid));
|
|
appendStringInfoString(qual_str, "+");
|
|
return new_const;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case T_List: {
|
|
List* l = (List*)node;
|
|
for (int i = 0; i < list_length(l); i++) {
|
|
Node* expr = (Node*)list_nth(l, i);
|
|
Node* new_expr = eval_dnstable_func_mutator(rel, expr, qual_str, rangeScanInRedis, false);
|
|
|
|
/*
|
|
* If a FuncExpr node is evalated into a T_Const value, we are hitting
|
|
* the point so replace it in qual list.
|
|
*/
|
|
if (expr && IsA(expr, FuncExpr) && new_expr && IsA(new_expr, Const)) {
|
|
l = list_delete_ptr(l, expr);
|
|
l = lappend(l, new_expr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the predicate at root is something like "where ctid between pg_get_redis_rel_start_ctid('xx')
|
|
* and pg_get_redis_rel_end_ctid('xx')" on DN, we will pushdown the predicate at scan node.
|
|
*/
|
|
if (isRoot && pg_strcasecmp(qual_str->data, RANGE_SCAN_IN_REDIS) == 0) {
|
|
rangeScanInRedis->isRangeScanInRedis = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
case T_OpExpr: {
|
|
OpExpr* opexpr = (OpExpr*)node;
|
|
char* funcname = get_func_name(opexpr->opfuncid);
|
|
|
|
if (funcname == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
errmsg("operation expression function with OID %u does not exist.", opexpr->opfuncid)));
|
|
}
|
|
appendStringInfoString(qual_str, funcname);
|
|
appendStringInfoString(qual_str, "+");
|
|
eval_dnstable_func_mutator(rel, (Node*)opexpr->args, qual_str, rangeScanInRedis, false);
|
|
|
|
break;
|
|
}
|
|
case T_Var: {
|
|
Var* var = (Var*)node;
|
|
/* we only expect tid column in the predicate */
|
|
if (var->vartype == TIDOID) {
|
|
appendStringInfoString(qual_str, "tid");
|
|
appendStringInfoString(qual_str, "+");
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
appendStringInfoString(qual_str, nodeTagToString(nodeTag(node)));
|
|
appendStringInfoString(qual_str, "+");
|
|
break;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* - Brief: get and open new_table rel
|
|
* - Parameter:
|
|
* @rel: target relation of TRUNCATE operation
|
|
* - Return:
|
|
* new_table rel
|
|
*/
|
|
Relation GetAndOpenNewTableRel(const Relation rel, LOCKMODE lockmode)
|
|
{
|
|
Relation newtable_rel = NULL;
|
|
Oid newtable_relid = InvalidOid;
|
|
Oid data_redis_namespace;
|
|
char new_tablename[NAMEDATALEN];
|
|
errno_t errorno = EOK;
|
|
|
|
errorno = memset_s(new_tablename, NAMEDATALEN, 0, NAMEDATALEN);
|
|
securec_check_c(errorno, "\0", "\0");
|
|
|
|
RelationGetNewTableName(rel, (char*)new_tablename);
|
|
data_redis_namespace = get_namespace_oid("data_redis", false);
|
|
newtable_relid = get_relname_relid(new_tablename, data_redis_namespace);
|
|
if (!OidIsValid(newtable_relid)) {
|
|
/* ERROR case, should never come here */
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
|
errmsg("new table %s is not found when do cluster resizing table \"%s\"",
|
|
new_tablename,
|
|
RelationGetRelationName(rel))));
|
|
}
|
|
newtable_rel = relation_open(newtable_relid, lockmode);
|
|
elog(LOG,
|
|
"New temp table %s for relation %s under cluster resizing is valid.",
|
|
new_tablename,
|
|
RelationGetRelationName(rel));
|
|
|
|
return newtable_rel;
|
|
}
|
|
|
|
/*
|
|
* - Brief: get the name of new table
|
|
* - Parameter:
|
|
* @relname: name of target table
|
|
* @newtable_name: output value for new table name
|
|
* - Return:
|
|
* no return value
|
|
*/
|
|
void RelationGetNewTableName(Relation rel, char* newtable_name)
|
|
{
|
|
int rc = 0;
|
|
|
|
/* Check if output parameter it not palloc()-ed from caller side */
|
|
if (newtable_name == NULL || rel == NULL) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Invalid parameter in function '%s' when getting the name of new table", __FUNCTION__)));
|
|
}
|
|
|
|
/*
|
|
* Look up relaion's reloptions to get table's cnoid to
|
|
* form the name of new table
|
|
*/
|
|
if (!IsInitdb) {
|
|
Oid rel_cn_oid = RelationGetRelCnOid(rel);
|
|
if (OidIsValid(rel_cn_oid)) {
|
|
rc = snprintf_s(newtable_name, NAMEDATALEN, NAMEDATALEN - 1, "data_redis_tmp_%u", rel_cn_oid);
|
|
} else {
|
|
elog(LOG, "rel %s doesn't exist in redistributing", RelationGetRelationName(rel));
|
|
rc = snprintf_s(
|
|
newtable_name, NAMEDATALEN, NAMEDATALEN - 1, "data_redis_tmp_%s", RelationGetRelationName(rel));
|
|
}
|
|
/* check the return value of security function */
|
|
securec_check_ss(rc, "\0", "\0");
|
|
}
|
|
return;
|
|
}
|