openGauss-server/src/gausskernel/cbb/utils/partition/partitionmap.cpp

1814 lines
73 KiB
C++

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
* File Name : partitionmap.cpp
* Target : data partition
* Brief :
* Description :
* History :
*
* IDENTIFICATION
* src/gausskernel/cbb/utils/partition/partitionmap.cpp
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include "access/heapam.h"
#include "catalog/pg_partition.h"
#include "catalog/pg_partition_fn.h"
#include "catalog/pg_type.h"
#include "datatype/timestamp.h"
#include "executor/executor.h"
#include "nodes/value.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_node.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "utils/catcache.h"
#include "utils/syscache.h"
#include "utils/array.h"
#include "utils/numeric.h"
#include "utils/numeric_gs.h"
#include "utils/datetime.h"
#include "utils/int8.h"
#include "utils/lsyscache.h"
#include "utils/partitionmap.h"
#include "utils/partitionmap_gs.h"
#include "utils/partitionkey.h"
#include "utils/date.h"
#include "utils/resowner.h"
#include "utils/rel.h"
#include "utils/rel_gs.h"
#include "fmgr.h"
#include "utils/memutils.h"
#include "utils/datum.h"
#define SAMESIGN(a, b) (((a) < 0) == ((b) < 0))
#define overFlowCheck(arg) \
do { \
if ((arg) > (MAX_PARTITION_NUM - 1)) { \
ereport(ERROR, \
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE), \
errmsg("inserted partition key does not map to any table partition"), \
errdetail("the sequnece number is too large for range table"))); \
} \
} while (0)
#define SN(max, tpoint, scale, dist, mode, isclose, missIsOk, seqnum) \
do { \
(dist) = (max) - (tpoint); \
Assert(0 <= (dist) && 0 < (scale)); \
(mode) = (dist) % (scale); \
*(seqnum) = (dist) / (scale); \
if (!(mode) && !(isclose)) { \
*(seqnum) = *(seqnum) - 1; \
} \
if (!(missIsOk)) { \
overFlowCheck(*(seqnum)); \
} \
} while (0)
#define Int2VectorSize(n) (offsetof(int2vector, values) + (n) * sizeof(int2))
#define constIsMaxValue(value) ((value)->ismaxvalue)
#define int_cmp_partition(arg1, arg2, compare) \
do { \
if ((arg1) < (arg2)) { \
(compare) = -1; \
} else if ((arg1) > (arg2)) { \
(compare) = 1; \
} else { \
(compare) = 0; \
} \
} while (0)
/*
* 1. NAN = NAN
* 2. NAN > non-NAN
* 3. non-NAN < NAN
* 4. non-NAN cmp non-NAN
*/
#define float_cmp_partition(arg1, arg2, compare) \
do { \
if (isnan(arg1)) { \
if (isnan(arg2)) { \
(compare) = 0; \
} else { \
(compare) = 1; \
} \
} else if (isnan(arg2)) { \
(compare) = -1; \
} else { \
if ((arg1) > (arg2)) { \
(compare) = 1; \
} else if ((arg1) < (arg2)) { \
(compare) = -1; \
} else { \
(compare) = 0; \
} \
} \
} while (0)
#define numerics_cmp_partition(arg1, arg2, compare) \
do { \
(compare) = cmp_numerics((arg1), (arg2)); \
} while (0)
#define bpchar_cmp_partition(arg1, arg2, collid, compare) \
do { \
int len1 = bcTruelen(arg1); \
int len2 = bcTruelen(arg2); \
(compare) = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, (collid)); \
} while (0)
#define text_cmp_partition(arg1, arg2, collid, compare) \
do { \
char* a1p = NULL; \
char* a2p = NULL; \
int len1; \
int len2; \
a1p = VARDATA_ANY(arg1); \
a2p = VARDATA_ANY(arg2); \
len1 = VARSIZE_ANY_EXHDR(arg1); \
len2 = VARSIZE_ANY_EXHDR(arg2); \
(compare) = varstr_cmp(a1p, len1, a2p, len2, (collid)); \
} while (0)
#define date_cmp_partition(arg1, arg2, compare) \
do { \
if ((arg1) < (arg2)) { \
(compare) = -1; \
} else if ((arg1) > (arg2)) { \
(compare) = 1; \
} else { \
(compare) = 0; \
} \
} while (0)
#define timestamp_cmp_partition(arg1, arg2, compare) \
do { \
(compare) = timestamp_cmp_internal((arg1), (arg2)); \
} while (0)
#define name_cmp_partition(arg1, arg2, compare) \
do { \
Assert(PointerIsValid(arg1)); \
Assert(PointerIsValid(arg2)); \
(compare) = strncmp(NameStr(*(arg1)), NameStr(*(arg2)), NAMEDATALEN); \
} while (0)
#define interval_cmp_partition(arg1, arg2, compare) \
do { \
Assert(PointerIsValid(arg1)); \
Assert(PointerIsValid(arg1)); \
(compare) = interval_cmp_internal((arg1), (arg2)); \
} while (0)
#define constCompare_baseType(value1, value2, compare) \
do { \
switch ((value1)->consttype) { \
case INT2OID: \
Assert((value2)->consttype == INT2OID); \
int_cmp_partition( \
DatumGetInt16((value1)->constvalue), DatumGetInt16((value2)->constvalue), (compare)); \
break; \
case INT4OID: \
Assert((value2)->consttype == INT4OID); \
int_cmp_partition( \
DatumGetInt32((value1)->constvalue), DatumGetInt32((value2)->constvalue), (compare)); \
break; \
case INT8OID: \
Assert((value2)->consttype == INT8OID); \
int_cmp_partition( \
DatumGetInt64((value1)->constvalue), DatumGetInt64((value2)->constvalue), (compare)); \
break; \
case FLOAT4OID: \
Assert((value2)->consttype == FLOAT4OID); \
float_cmp_partition( \
DatumGetFloat4((value1)->constvalue), DatumGetFloat4((value2)->constvalue), (compare)); \
break; \
case FLOAT8OID: \
Assert((value2)->consttype == FLOAT8OID); \
float_cmp_partition( \
DatumGetFloat8((value1)->constvalue), DatumGetFloat8((value2)->constvalue), (compare)); \
break; \
case NUMERICOID: \
Assert((value2)->consttype == NUMERICOID); \
numerics_cmp_partition( \
DatumGetNumeric((value1)->constvalue), DatumGetNumeric((value2)->constvalue), (compare)); \
break; \
case BPCHAROID: \
Assert((value2)->consttype == BPCHAROID); \
Assert((value1)->constcollid == (value2)->constcollid); \
bpchar_cmp_partition(DatumGetBpCharP((value1)->constvalue), \
DatumGetBpCharP((value2)->constvalue), \
(value1)->constcollid, \
(compare)); \
break; \
case VARCHAROID: \
Assert((value2)->consttype == VARCHAROID); \
Assert((value1)->constcollid == (value2)->constcollid); \
text_cmp_partition(DatumGetTextP((value1)->constvalue), \
DatumGetTextP((value2)->constvalue), \
(value1)->constcollid, \
(compare)); \
break; \
case TEXTOID: \
Assert((value2)->consttype == TEXTOID); \
Assert((value1)->constcollid == (value2)->constcollid); \
text_cmp_partition(DatumGetTextP((value1)->constvalue), \
DatumGetTextP((value2)->constvalue), \
(value1)->constcollid, \
(compare)); \
break; \
case DATEOID: \
Assert((value2)->consttype == DATEOID); \
date_cmp_partition( \
DatumGetDateADT((value1)->constvalue), DatumGetDateADT((value2)->constvalue), (compare)); \
break; \
case TIMESTAMPOID: \
Assert((value2)->consttype == TIMESTAMPOID); \
timestamp_cmp_partition( \
DatumGetTimestamp((value1)->constvalue), DatumGetTimestamp((value2)->constvalue), (compare)); \
break; \
case TIMESTAMPTZOID: \
Assert((value2)->consttype == TIMESTAMPTZOID); \
timestamp_cmp_partition( \
DatumGetTimestampTz((value1)->constvalue), DatumGetTimestampTz((value2)->constvalue), (compare)); \
break; \
case NAMEOID: \
Assert((value2)->consttype == NAMEOID); \
name_cmp_partition(DatumGetName((value1)->constvalue), DatumGetName((value2)->constvalue), (compare)); \
break; \
case INTERVALOID: \
Assert((value2)->consttype == INTERVALOID); \
interval_cmp_partition( \
DatumGetIntervalP((value1)->constvalue), DatumGetIntervalP((value2)->constvalue), (compare)); \
break; \
default: \
(compare) = constCompare_constType((value1), (value2)); \
break; \
} \
} while (0)
/*
* @Description: partition value routing
* @Param[IN] compare: returned value
* @Param[IN] value1: left value to compare
* @Param[OUT] value2: right value to compare
* @See also:
*/
static inline void constCompare(Const* value1, Const* value2, int& compare)
{
if (t_thrd.utils_cxt.gValueCompareContext == NULL) {
/*
* In a query each tuple be updated delete or update, constcompare is called,
* the memory allocated in function is not freed until query end, we allocate a
* temp momeryContext to free the memory allocated in constcompare fucntion to
* avoid take up too many memory in a query.
* create this memory context under TOP memory context.
*/
t_thrd.utils_cxt.gValueCompareContext = AllocSetContextCreate(t_thrd.top_mem_cxt,
"CONST_COMPARE_CONTEXT",
ALLOCSET_SMALL_MINSIZE,
ALLOCSET_SMALL_INITSIZE,
ALLOCSET_SMALL_MAXSIZE);
t_thrd.utils_cxt.ContextUsedCount = 1;
} else {
t_thrd.utils_cxt.ContextUsedCount++;
}
MemoryContext oldContext = MemoryContextSwitchTo(t_thrd.utils_cxt.gValueCompareContext);
PG_TRY();
{
if (value1->consttype == value2->consttype) {
constCompare_baseType(value1, value2, compare);
} else {
compare = constCompare_constType(value1, value2);
}
}
PG_CATCH();
{
t_thrd.utils_cxt.ContextUsedCount--;
/* switch to previous memory context */
(void)MemoryContextSwitchTo(oldContext);
if (t_thrd.utils_cxt.ContextUsedCount == 0) {
MemoryContextReset(t_thrd.utils_cxt.gValueCompareContext);
}
PG_RE_THROW();
}
PG_END_TRY();
/* switch to previous memory context */
(void)MemoryContextSwitchTo(oldContext);
/* reset this memory context after each partition routing */
if (t_thrd.utils_cxt.ContextUsedCount > 0) {
t_thrd.utils_cxt.ContextUsedCount--;
}
if (t_thrd.utils_cxt.ContextUsedCount == 0) {
MemoryContextReset(t_thrd.utils_cxt.gValueCompareContext);
}
}
#define partitonKeyCompareForRouting(value1, value2, len, compare) \
do { \
uint8 i = 0; \
Const* v1 = NULL; \
Const* v2 = NULL; \
for (; i < (len); i++) { \
v1 = *((value1) + i); \
v2 = *((value2) + i); \
if (v1 == NULL || v2 == NULL) { \
if (v1 == NULL && v2 == NULL) { \
ereport( \
ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("NULL can not be compared with NULL"))); \
} else if (v1 == NULL) { \
compare = -1; \
} else { \
compare = 1; \
} \
break; \
} \
if (constIsMaxValue(v1) || constIsMaxValue(v2)) { \
if (constIsMaxValue(v1) && constIsMaxValue(v2)) { \
compare = 0; \
continue; \
} else if (constIsMaxValue(v1)) { \
compare = 1; \
} else { \
compare = -1; \
} \
break; \
} \
if (v1->constisnull || v2->constisnull) { \
if (v1->constisnull && v2->constisnull) { \
ereport(ERROR, \
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE), \
errmsg("null value can not be compared with null value."))); \
} else if (v1->constisnull) { \
compare = 1; \
} else { \
compare = -1; \
} \
break; \
} \
constCompare(v1, v2, compare); \
if ((compare) != 0) { \
break; \
} \
} \
} while (0)
#define BuildRangeElement(range, type, typelen, relid, attrno, tuple, desc, isInter) \
do { \
Assert(PointerIsValid(range)); \
Assert(PointerIsValid(type) && PointerIsValid(attrno)); \
Assert(PointerIsValid(tuple) && PointerIsValid(desc)); \
Assert((attrno)->dim1 <= RANGE_PARTKEYMAXNUM); \
Assert((attrno)->dim1 == (typelen)); \
unserializePartitionStringAttribute((range)->boundary, \
RANGE_PARTKEYMAXNUM, \
(type), \
(typelen), \
(relid), \
(attrno), \
(tuple), \
Anum_pg_partition_boundaries, \
(desc)); \
(range)->partitionOid = HeapTupleGetOid(tuple); \
(range)->len = (typelen); \
(range)->isInterval = (isInter); \
} while (0)
static void RebuildRangePartitionMap(RangePartitionMap* oldMap, RangePartitionMap* newMap);
/* these routines are partition map related */
static void buildRangePartitionMap(Relation relation, Form_pg_partition partitioned_form, HeapTuple partitioned_tuple,
Relation pg_partition, const List* partition_list);
static RangeElement* copyRangeElements(RangeElement* src, int elementNum, int partkeyNum);
static int rangeElementCmp(const void* a, const void* b);
ValuePartitionMap* buildValuePartitionMap(Relation relation, Relation pg_partition, HeapTuple partitioned_tuple);
/*
* @@GaussDB@@
* Brief :
* Description : read boundaries, transitpoint or interval attribute value
* from pg_partition for partition and transform it to maxvalue array
* Notes :
*/
void unserializePartitionStringAttribute(Const** outMaxValue, int outMaxValueLen, Oid* partKeyDataType,
int partKeyDataTypeLen, Oid relid, int2vector* partKeyAttrNo, HeapTuple partition_tuple, int att_num,
TupleDesc pg_partition_tupledsc)
{
Datum attribute_raw_value;
bool isNull = true;
List* boundary = NULL;
ListCell* cell = NULL;
int counter = 0;
/* this function is used to retrive maxvalue list in these 3 fields */
Assert(att_num == Anum_pg_partition_boundaries || att_num == Anum_pg_partition_transit ||
att_num == Anum_pg_partition_interval);
Assert(partKeyDataTypeLen == partKeyAttrNo->dim1);
attribute_raw_value = heap_getattr(partition_tuple, (uint32)att_num, pg_partition_tupledsc, &isNull);
if (isNull) {
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("null maxvalue for tuple %u", HeapTupleGetOid(partition_tuple))));
}
/* unstransform string items to Value list */
boundary = untransformPartitionBoundary(attribute_raw_value);
Assert(boundary->length == partKeyAttrNo->dim1);
Assert(boundary->length <= RANGE_PARTKEYMAXNUM);
/* Now, for each max value item, call it's typin function, save it in datum */
counter = 0;
foreach (cell, boundary) {
int16 typlen = 0;
bool typbyval = false;
char typalign;
char typdelim;
Oid typioparam = InvalidOid;
Oid func = InvalidOid;
Oid typid = InvalidOid;
Oid typelem = InvalidOid;
Oid typcollation = InvalidOid;
int32 typmod = -1;
Datum value;
Value* max_value = NULL;
max_value = (Value*)lfirst(cell);
/* get the oid/mod/collation/ of column i */
get_atttypetypmodcoll(relid, partKeyAttrNo->values[counter], &typid, &typmod, &typcollation);
/* deal with null */
if (!PointerIsValid(max_value->val.str)) {
outMaxValue[counter++] = makeMaxConst(typid, typmod, typcollation);
continue;
}
/*
* for interval column in pg_partition, typmod above is not correct
* have to get typmod from 'intervalvalue(typmod)'
*/
if (INTERVALOID == partKeyDataType[counter]) {
typmod = -1;
}
/* get the typein function's oid of current type */
get_type_io_data(
partKeyDataType[counter], IOFunc_input, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &func);
typelem = get_element_type(partKeyDataType[counter]);
/* now call the typein function with collation,string, element_type, typemod
* as it's parameters.
*/
value = OidFunctionCall3Coll(
func, typcollation, CStringGetDatum(max_value->val.str), ObjectIdGetDatum(typelem), Int32GetDatum(typmod));
/* save the output values */
outMaxValue[counter++] = makeConst(typid, typmod, typcollation, typlen, value, false, typbyval);
}
list_free_ext(boundary);
}
/*
* getPartitionKeyAttrNo(), get out typid and attribute number of partition key
* parameters: [OUT] outOid and return value
*
* @@GaussDB@@
* Brief :
* Description : get attribute number and data type oid of partition key from
* partitioned-table tuple.
* Notes :
* Parameters : [out]typeOids: data type array of partition key
* [in] pg_partitioin_tuple: partitioned-table tuple in pg_partition
* [in] pg_partition_tupledsc: TupleDesc of pg_partition
* [in] base_table_tupledsc: TupleDesc of partitioned-table
* return : attribute number array of partition key
*/
int2vector* getPartitionKeyAttrNo(
Oid** typeOids, HeapTuple pg_partition_tuple, TupleDesc pg_partition_tupledsc, TupleDesc base_table_tupledsc)
{
Datum partkey_raw;
ArrayType* partkey_columns = NULL;
bool isNull = false;
int16* attnums = NULL;
int n_key_column, i, j;
int2vector* partkey = NULL;
Oid* oidArr = NULL;
Form_pg_attribute* rel_attrs = base_table_tupledsc->attrs;
Assert(PointerIsValid(typeOids));
/* Get the raw data which contain patition key's columns */
partkey_raw = heap_getattr(pg_partition_tuple, Anum_pg_partition_partkey, pg_partition_tupledsc, &isNull);
/* if the raw value of partition key is null, then report error */
if (isNull) {
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("null maxvalue for tuple %u", HeapTupleGetOid(pg_partition_tuple))));
}
/* convert Datum to ArrayType */
partkey_columns = DatumGetArrayTypeP(partkey_raw);
/* Get number of partition key columns from int2verctor */
n_key_column = ARR_DIMS(partkey_columns)[0];
/* CHECK: the ArrayType of partition key is valid */
if (ARR_NDIM(partkey_columns) != 1 || n_key_column < 0 || ARR_HASNULL(partkey_columns) ||
ARR_ELEMTYPE(partkey_columns) != INT2OID) {
ereport(ERROR,
(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
errmsg("partition key column's number is not a 1-D smallint array")));
}
/* Get int2 array of partition key column numbers */
attnums = (int16*)ARR_DATA_PTR(partkey_columns);
Assert(n_key_column <= RANGE_PARTKEYMAXNUM);
/* Initialize int2verctor structure for attribute number array of partition key */
partkey = buildint2vector(NULL, n_key_column);
oidArr = (Oid*)palloc0(sizeof(Oid) * n_key_column);
/* specify value to int2verctor and build type oid array */
for (i = 0; i < n_key_column; i++) {
int16 attnum = attnums[i];
partkey->values[i] = attnum;
for (j = 0; j < base_table_tupledsc->natts; j++) {
if (attnum == rel_attrs[j]->attnum) {
oidArr[i] = rel_attrs[j]->atttypid;
break;
}
}
}
*typeOids = oidArr;
return partkey;
}
/*
* @@GaussDB@@
* Brief :
* Description : When load RelationData to relcache, intalize PartitionMap which
* : stores partition boundary.
* Notes : We must note than the data of the catalog may change between
* : two access. So it is reasonable if
* : partitioned_form->intervalnum + partitioned_form->rangenum != partition_list->length
*/
void RelationInitPartitionMap(Relation relation)
{
List* partition_list = NIL;
Relation pg_partition = NULL;
HeapTuple partitioned_tuple = NULL;
Form_pg_partition partitioned_form = NULL;
MemoryContext old_context;
MemoryContext tmp_context;
Assert(PointerIsValid(relation));
/*
* Unsupport to bulid partitionmap for non-partitioned table.
* Never happen, just to be self-contained.
*/
if (RelationIsNonpartitioned(relation)) {
ereport(ERROR,
(errcode(ERRCODE_PARTITION_ERROR),
errmsg("Fail to build partitionmap for realtion\"%s\".", RelationGetRelationName(relation)),
errdetail("Relation is a non-partitioned table.")));
}
/* create a tmp memorycontext */
tmp_context = AllocSetContextCreate(t_thrd.top_mem_cxt,
"InitPartitionMapTmpMemoryContext",
ALLOCSET_SMALL_MINSIZE,
ALLOCSET_SMALL_INITSIZE,
ALLOCSET_SMALL_MAXSIZE);
/*
* switch memeorycontext to relcache's memeorycontext
*/
old_context = MemoryContextSwitchTo(tmp_context);
pg_partition = relation_open(PartitionRelationId, AccessShareLock);
partitioned_tuple = searchPgPartitionByParentIdCopy(PART_OBJ_TYPE_PARTED_TABLE, relation->rd_id);
if (!HeapTupleIsValid(partitioned_tuple)) {
if (RecoveryInProgress()) {
ereport(ERROR,
(errcode(ERRCODE_RUN_TRANSACTION_DURING_RECOVERY),
errmsg("Can not run transaction to remote nodes during recovery.")));
}
Assert(0);
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find tuple with partition OID %u.", relation->rd_id)));
}
partitioned_form = (Form_pg_partition)GETSTRUCT(partitioned_tuple);
/*
* For value based partition-table, we only have to retrieve partkeys
*/
if (partitioned_form->partstrategy == PART_STRATEGY_VALUE) {
/* create ValuePartitionMap */
(void)MemoryContextSwitchTo(u_sess->cache_mem_cxt);
relation->partMap = (PartitionMap*)buildValuePartitionMap(relation, pg_partition, partitioned_tuple);
/* release the partition_list and partitioined_table_tuple */
heap_freetuple_ext(partitioned_tuple);
freePartList(partition_list);
/* close pg_partition */
relation_close(pg_partition, AccessShareLock);
(void)MemoryContextSwitchTo(old_context);
MemoryContextDelete(tmp_context);
return;
}
/*
* Fail to get relation tuple for the partitioned table
* Never happen, just to be self-contained
*/
if (!PointerIsValid(partitioned_tuple)) {
relation_close(pg_partition, AccessShareLock);
(void)MemoryContextSwitchTo(old_context);
MemoryContextDelete(tmp_context);
ereport(ERROR,
(errcode(ERRCODE_PARTITION_ERROR),
errmsg("Fail to build partitionmap for partitioned table \"%s\"", RelationGetRelationName(relation)),
errdetail("Could not find the partitioned table")));
}
/* read out patition tuples from pg_partition */
partition_list = searchPgPartitionByParentId(PART_OBJ_TYPE_TABLE_PARTITION, relation->rd_id);
/*
* Fail to get relation tuple for the partitioned table
* Never happen, just to be self-contained
*/
if (!PointerIsValid(partition_list)) {
heap_freetuple_ext(partitioned_tuple);
relation_close(pg_partition, AccessShareLock);
(void)MemoryContextSwitchTo(old_context);
MemoryContextDelete(tmp_context);
ereport(ERROR,
(errcode(ERRCODE_PARTITION_ERROR),
errmsg("Fail to build partitionmap for partitioned table \"%s\".", RelationGetRelationName(relation)),
errdetail("Could not find partition for the partitioned table.")));
}
switch (partitioned_form->partstrategy) {
case PART_STRATEGY_RANGE:
case PART_STRATEGY_INTERVAL:
buildRangePartitionMap(relation, partitioned_form, partitioned_tuple, pg_partition, partition_list);
break;
default:
(void)MemoryContextSwitchTo(old_context);
MemoryContextDelete(tmp_context);
ereport(ERROR,
(errcode(ERRCODE_PARTITION_ERROR),
errmsg("Fail to build partitionmap for partitioned table \"%u\".", partitioned_form->parentid),
errdetail(
"Incorrect partition strategy \"%c\" for partitioned table.", partitioned_form->partstrategy)));
break;
}
relation->partMap->refcount = 0;
relation->partMap->isDirty = false;
/* release the partition_list and partitioined_table_tuple */
heap_freetuple_ext(partitioned_tuple);
freePartList(partition_list);
/* close pg_partition */
relation_close(pg_partition, AccessShareLock);
(void)MemoryContextSwitchTo(old_context);
MemoryContextDelete(tmp_context);
}
/*
* Brief: build PartitionMap data structure for value partition table
* Input:
* @relation: Relation descriptor for value partitioned table
* @pg_relation: Relation descriptor for pg_partition
* @partitioned_tuple: the primary tuple of pg_partition that describe
* how this relation is (value) partitioned
* Return Value : ValuePartitoinMap pointer for given relation.
* Notes : None.
*/
ValuePartitionMap* buildValuePartitionMap(Relation relation, Relation pg_partition, HeapTuple partitioned_tuple)
{
Oid* partitionKeyDataType = NULL;
int2vector* partitionKey = NULL;
ValuePartitionMap* vpm = NULL;
/* create ValuePartitionMap */
vpm = (ValuePartitionMap*)palloc0(sizeof(ValuePartitionMap));
vpm->type.type = PART_TYPE_VALUE;
vpm->relid = RelationGetRelid(relation);
/* get attno. which is a member of partitionkey */
partitionKey = getPartitionKeyAttrNo(
&(partitionKeyDataType), partitioned_tuple, RelationGetDescr(pg_partition), RelationGetDescr(relation));
/* build partList */
for (int i = 0; i < partitionKey->dim1; i++) {
vpm->partList = lappend_int(vpm->partList, partitionKey->values[i]);
}
return vpm;
}
void RebuildPartitonMap(PartitionMap* oldMap, PartitionMap* newMap)
{
if (oldMap->type != newMap->type) {
ereport(ERROR,
(errcode(ERRCODE_PARTITION_ERROR),
errmsg("rebuild partition map ERROR"),
errdetail("NEW partitioned table MUST have same partition strategy as OLD partitioned table")));
}
// when the map is referenced, don't rebuild the partitionmap
if (oldMap->refcount == 0) {
RebuildRangePartitionMap((RangePartitionMap*)oldMap, (RangePartitionMap*)newMap);
} else {
oldMap->isDirty = true;
elog(LOG, "map refcount is not zero when RebuildPartitonMap ");
}
}
#define PARTITIONMAP_SWAPFIELD(fieldType, fieldName) \
do { \
fieldType _temp = oldMap->fieldName; \
oldMap->fieldName = newMap->fieldName; \
newMap->fieldName = _temp; \
} while (0);
static void RebuildRangePartitionMap(RangePartitionMap* oldMap, RangePartitionMap* newMap)
{
RangePartitionMap tempMap;
errno_t rc = 0;
rc = memcpy_s(&tempMap, sizeof(RangePartitionMap), newMap, sizeof(RangePartitionMap));
securec_check(rc, "\0", "\0");
rc = memcpy_s(newMap, sizeof(RangePartitionMap), oldMap, sizeof(RangePartitionMap));
securec_check(rc, "\0", "\0");
rc = memcpy_s(oldMap, sizeof(RangePartitionMap), &tempMap, sizeof(RangePartitionMap));
securec_check(rc, "\0", "\0");
PARTITIONMAP_SWAPFIELD(int2vector*, partitionKey);
PARTITIONMAP_SWAPFIELD(Oid*, partitionKeyDataType);
}
/*
* copy the rangeElement
*/
static RangeElement* copyRangeElements(RangeElement* src, int elementNum, int partkeyNum)
{
int i = 0;
int j = 0;
Size size_ret = sizeof(RangeElement) * elementNum;
RangeElement* ret = NULL;
errno_t rc = 0;
ret = (RangeElement*)palloc0(size_ret);
rc = memcpy_s(ret, size_ret, src, size_ret);
securec_check(rc, "\0", "\0");
for (i = 0; i < elementNum; i++) {
for (j = 0; j < partkeyNum; j++) {
ret[i].boundary[j] = (Const*)copyObject(src[i].boundary[j]);
}
}
return ret;
}
RangeElement* CopyRangeElementsWithoutBoundary(const RangeElement* src, int elementNum)
{
Size size_ret = sizeof(RangeElement) * elementNum;
RangeElement* ret = (RangeElement*)palloc0(size_ret);
errno_t rc = memcpy_s(ret, size_ret, src, size_ret);
securec_check(rc, "\0", "\0");
return ret;
}
char* ReadIntervalStr(HeapTuple tuple, TupleDesc tupleDesc)
{
bool isNull = true;
Oid elemType;
int16 elemLen;
bool elemByval = false;
char elemAlign;
int numElems;
Datum* elemValues = NULL;
bool* elemNulls = NULL;
Datum attrRawValue = heap_getattr(tuple, (uint32)Anum_pg_partition_interval, tupleDesc, &isNull);
ArrayType* array = DatumGetArrayTypeP(attrRawValue);
elemType = ARR_ELEMTYPE(array);
Assert(elemType == TEXTOID);
get_typlenbyvalalign(elemType, &elemLen, &elemByval, &elemAlign);
deconstruct_array(array, elemType, elemLen, elemByval, elemAlign, &elemValues, &elemNulls, &numElems);
Assert(numElems == 1);
Assert(!elemNulls[0]);
char* intervalStr = text_to_cstring(DatumGetTextP(*elemValues));
pfree(elemValues);
pfree(elemNulls);
return intervalStr;
}
static Interval* ReadInterval(HeapTuple tuple, TupleDesc tupleDesc)
{
int32 typmod = -1;
char* intervalStr = ReadIntervalStr(tuple, tupleDesc);
Interval* res = char_to_interval(intervalStr, typmod);
pfree(intervalStr);
return res;
}
oidvector* ReadIntervalTablespace(HeapTuple tuple, TupleDesc tupleDesc)
{
Datum tablespaceRaw;
ArrayType* tablespaceArray = NULL;
bool isNull = false;
Oid* values = NULL;
int arraySize;
/* Get the raw data which contain interval tablespace's columns */
tablespaceRaw = heap_getattr(tuple, Anum_pg_partition_intablespace, tupleDesc, &isNull);
if (isNull) {
return NULL;
}
/* convert Datum to ArrayType */
tablespaceArray = DatumGetArrayTypeP(tablespaceRaw);
arraySize = ARR_DIMS(tablespaceArray)[0];
/* CHECK: the ArrayType of interval tablespace is valid */
if (ARR_NDIM(tablespaceArray) != 1 || arraySize <= 0 || ARR_HASNULL(tablespaceArray) ||
ARR_ELEMTYPE(tablespaceArray) != OIDOID) {
ereport(ERROR,
(errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("interval tablespace column's number is not a oid array")));
}
values = (Oid*)ARR_DATA_PTR(tablespaceArray);
return buildoidvector(values, arraySize);
}
/*
* @@GaussDB@@
* Target : data partition
* Brief : build RangePartitionMap for partitioned table
* Description :
* Notes :
*/
static void buildRangePartitionMap(Relation relation, Form_pg_partition partitioned_form, HeapTuple partitioned_tuple,
Relation pg_partition, const List* partition_list)
{
int range_itr = 0;
RangePartitionMap* range_map = NULL;
RangeElement* range_eles = NULL;
Form_pg_partition partition_form = NULL;
HeapTuple partition_tuple = NULL;
ListCell* tuple_cell = NULL;
int2vector* partitionKey = NULL;
MemoryContext old_context = NULL;
Oid* partitionKeyDataType = NULL;
errno_t rc = 0;
/* build RangePartitionMap */
range_map = (RangePartitionMap*)palloc0(sizeof(RangePartitionMap));
range_map->type.type = PART_TYPE_RANGE;
range_map->relid = RelationGetRelid(relation);
range_map->rangeElementsNum = partition_list->length;
/* get attribute NO. which is a member of partitionkey */
partitionKey = getPartitionKeyAttrNo(
&(partitionKeyDataType), partitioned_tuple, RelationGetDescr(pg_partition), RelationGetDescr(relation));
/* copy the partitionKey */
old_context = MemoryContextSwitchTo(u_sess->cache_mem_cxt);
range_map->partitionKey = (int2vector*)palloc(Int2VectorSize(partitionKey->dim1));
rc = memcpy_s(
range_map->partitionKey, Int2VectorSize(partitionKey->dim1), partitionKey, Int2VectorSize(partitionKey->dim1));
securec_check(rc, "\0", "\0");
range_map->partitionKeyDataType = (Oid*)palloc(sizeof(Oid) * partitionKey->dim1);
rc = memcpy_s(range_map->partitionKeyDataType,
sizeof(Oid) * partitionKey->dim1,
partitionKeyDataType,
sizeof(Oid) * partitionKey->dim1);
securec_check(rc, "\0", "\0");
if (partitioned_form->partstrategy == PART_STRATEGY_INTERVAL) {
range_map->type.type = PART_TYPE_INTERVAL;
/* the interval partition only supports one partition key */
Assert(partitionKey->dim1 == 1);
range_map->intervalValue = ReadInterval(partitioned_tuple, RelationGetDescr(pg_partition));
range_map->intervalTablespace = ReadIntervalTablespace(partitioned_tuple, RelationGetDescr(pg_partition));
}
(void)MemoryContextSwitchTo(old_context);
/* allocate range element array */
range_eles = (RangeElement*)palloc0(sizeof(RangeElement) * (range_map->rangeElementsNum));
/* iterate partition tuples, build RangeElement for per partition tuple */
range_itr = 0;
foreach (tuple_cell, partition_list) {
partition_tuple = (HeapTuple)lfirst(tuple_cell);
partition_form = (Form_pg_partition)GETSTRUCT(partition_tuple);
if (partition_form->partstrategy != PART_STRATEGY_RANGE &&
partition_form->partstrategy != PART_STRATEGY_INTERVAL) {
pfree_ext(range_eles);
pfree_ext(range_map);
ereport(ERROR,
(errcode(ERRCODE_PARTITION_ERROR),
errmsg("Fail to build partitionmap for partitioned table \"%u\"", partition_form->parentid),
errdetail("Incorrect partition strategy for partition %u", HeapTupleGetOid(partition_tuple))));
}
BuildRangeElement(&(range_eles[range_itr]),
range_map->partitionKeyDataType,
range_map->partitionKey->dim1,
RelationGetRelid(relation),
range_map->partitionKey,
partition_tuple,
RelationGetDescr(pg_partition),
partition_form->partstrategy == PART_STRATEGY_INTERVAL);
range_itr++;
}
qsort(range_eles, range_map->rangeElementsNum, sizeof(RangeElement), rangeElementCmp);
/* range element array back in RangePartitionMap */
old_context = MemoryContextSwitchTo(u_sess->cache_mem_cxt);
range_map->rangeElements = copyRangeElements(range_eles, range_map->rangeElementsNum, partitionKey->dim1);
relation->partMap = (PartitionMap*)palloc(sizeof(RangePartitionMap));
rc = memcpy_s(relation->partMap, sizeof(RangePartitionMap), range_map, sizeof(RangePartitionMap));
securec_check(rc, "\0", "\0");
(void)MemoryContextSwitchTo(old_context);
}
/*
* @@GaussDB@@
* Brief :
* Description : transform Datum data type to Const
* Notes :
*/
Const* transformDatum2Const(TupleDesc tupledesc, int16 attnum, Datum datumValue, bool isnull, Const* cnst)
{
Oid typid = InvalidOid;
Oid collid = InvalidOid;
int32 attindex = -1;
int32 typmod = -1;
int16 typlen = 0;
bool typbyval = false;
Form_pg_attribute att = NULL;
Assert(PointerIsValid(tupledesc));
Assert(attnum <= tupledesc->natts);
Assert(attnum >= 1);
attindex = attnum - 1;
att = tupledesc->attrs[attindex];
typid = att->atttypid;
typmod = att->atttypmod;
collid = att->attcollation;
typlen = att->attlen;
typbyval = att->attbyval;
cnst->xpr.type = T_Const;
cnst->consttype = typid;
cnst->consttypmod = typmod;
cnst->constcollid = collid;
cnst->constlen = typlen;
cnst->constvalue = isnull ? 0 : datumValue;
cnst->constisnull = isnull;
cnst->constbyval = typbyval;
cnst->location = -1; /* "unknown" */
cnst->ismaxvalue = false;
return cnst;
}
/* 1. increace the ref count of partition map, to protect from rebuilding of partition map
* 2. copy out the boundary of src partition, forming a new boundary list.
* 3. decreace the ref count of partition map.
*/
List* getPartitionBoundaryList(Relation rel, int sequence)
{
List* result = NIL;
RangePartitionMap* partMap = (RangePartitionMap*)rel->partMap;
if (!RELATION_IS_PARTITIONED(rel))
return NIL;
incre_partmap_refcount(rel->partMap);
if (sequence >= 0 && sequence < partMap->rangeElementsNum) {
int i = 0;
int partKeyNum = partMap->partitionKey->dim1;
Const** srcBound = partMap->rangeElements[sequence].boundary;
for (i = 0; i < partKeyNum; i++) {
result = lappend(result, (Const*)copyObject(srcBound[i]));
}
} else {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid partition sequence: %d of relation \"%s\"", sequence, RelationGetRelationName(rel))));
}
decre_partmap_refcount(rel->partMap);
return result;
}
/*
* @@GaussDB@@
* Brief :
* Description : routing partition key value list to targeting partition
* Notes :
*/
Oid partitionKeyValueListGetPartitionOid(Relation rel, List* partKeyValueList, bool topClosed)
{
ListCell* cell = NULL;
int len = 0;
foreach (cell, partKeyValueList) {
t_thrd.utils_cxt.valueItemArr[len++] = (Const*)lfirst(cell);
}
partitionRoutingForValue(rel, t_thrd.utils_cxt.valueItemArr, len, topClosed, false, &(*t_thrd.utils_cxt.partId));
return (*t_thrd.utils_cxt.partId).partitionId;
}
/*
* @@GaussDB@@
* Brief :
* Description :give the tuple, return oid of the corresponding partition
* Notes :
*/
Oid getRangePartitionOid(Relation relation, Const** partKeyValue, int32* partSeq, bool topClosed)
{
RangeElement* rangeElementIterator = NULL;
RangePartitionMap* rangePartMap = NULL;
Oid result = InvalidOid;
int keyNums;
int hit = -1;
int min_part_id = 0;
int max_part_id = 0;
int local_part_id = 0;
int compare = 0;
Const** boundary = NULL;
Assert(PointerIsValid(relation->partMap));
Assert(PointerIsValid(partKeyValue));
incre_partmap_refcount(relation->partMap);
rangePartMap = (RangePartitionMap*)(relation->partMap);
keyNums = rangePartMap->partitionKey->dim1;
max_part_id = rangePartMap->rangeElementsNum - 1;
boundary = rangePartMap->rangeElements[max_part_id].boundary;
partitonKeyCompareForRouting(partKeyValue, boundary, (uint32)keyNums, compare);
if (compare == 0) {
if (topClosed) {
hit = -1;
} else {
hit = max_part_id;
}
} else if (compare > 0) {
hit = -1;
} else {
/* semi-seek */
while (max_part_id > min_part_id) {
local_part_id = (uint32)(max_part_id + min_part_id) >> 1;
rangeElementIterator = &(rangePartMap->rangeElements[local_part_id]);
boundary = rangeElementIterator->boundary;
partitonKeyCompareForRouting(partKeyValue, boundary, keyNums, compare);
if (compare == 0) {
hit = local_part_id;
if (topClosed) {
hit += 1;
}
break;
} else if (compare > 0) {
min_part_id = local_part_id + 1;
} else {
max_part_id = local_part_id;
}
}
/* get it */
if (max_part_id == min_part_id) {
hit = max_part_id;
}
}
if (PointerIsValid(partSeq)) {
*partSeq = hit;
}
if (hit >= 0) {
result = rangePartMap->rangeElements[hit].partitionOid;
}
decre_partmap_refcount(relation->partMap);
return result;
}
inline Const* CalcLowBoundary(const Const* upBoundary, Interval* intervalValue)
{
Assert(upBoundary->consttype == TIMESTAMPOID || upBoundary->consttype == TIMESTAMPTZOID);
Timestamp lowTs = timestamp_mi_interval(DatumGetTimestamp(upBoundary->constvalue), intervalValue);
return makeConst(upBoundary->consttype,
upBoundary->consttypmod,
upBoundary->constcollid,
upBoundary->constlen,
TimestampGetDatum(lowTs),
upBoundary->constisnull,
upBoundary->constbyval);
}
inline int ValueCmpLowBoudary(Const** partKeyValue, const RangeElement* partition, Interval* intervalValue)
{
Assert(partition->isInterval);
Assert(partition->len == 1);
int compare = 0;
Const* lowBoundary = CalcLowBoundary(partition->boundary[0], intervalValue);
partitonKeyCompareForRouting(partKeyValue, &lowBoundary, partition->len, compare);
pfree(lowBoundary);
return compare;
}
/* the low boundary is close */
bool ValueSatisfyLowBoudary(Const** partKeyValue, RangeElement* partition, Interval* intervalValue, bool topClosed)
{
int compare = ValueCmpLowBoudary(partKeyValue, partition, intervalValue);
if (compare > 0 || (compare == 0 && topClosed)) {
return true;
}
return false;
}
/*
* @@GaussDB@@
* Target : data partition
* Brief :
* Description :
* Notes :
*/
int getNumberOfRangePartitions(Relation rel)
{
int ret;
if (!RELATION_IS_PARTITIONED(rel)) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("CAN NOT get number of partition against NON-PARTITIONED relation")));
}
if (rel->partMap->type == PART_TYPE_RANGE || rel->partMap->type == PART_TYPE_INTERVAL) {
RangePartitionMap* rangeMap = NULL;
rangeMap = (RangePartitionMap*)(rel->partMap);
ret = rangeMap->rangeElementsNum;
} else { /* type equals to "PART_TYPE_INTERVAL" */
ret = 0;
}
return ret;
}
int getNumberOfPartitions(Relation rel)
{
return getNumberOfRangePartitions(rel);
}
Oid partIDGetPartOid(Relation relation, PartitionIdentifier* partID)
{
RangePartitionMap* rang_map = NULL;
Oid partid = InvalidOid;
if (!PointerIsValid(relation) || !PointerIsValid(partID)) {
ereport(
ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("partIDGetPartOid(), invalid input parameters")));
}
if (!relation->partMap || partID->partSeq < 0) {
return InvalidOid;
}
if (relation->partMap->type == PART_TYPE_RANGE || relation->partMap->type == PART_TYPE_INTERVAL) {
rang_map = (RangePartitionMap*)(relation->partMap);
if (partID->partSeq <= rang_map->rangeElementsNum) {
partid = (rang_map->rangeElements + partID->partSeq)->partitionOid;
} else {
partid = InvalidOid;
ereport(ERROR,
(errcode(ERRCODE_INTERVAL_FIELD_OVERFLOW),
errmsg("fail to get partition oid, because range partition index is overflow.")));
}
} else {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unsupported partition strategy")));
}
return partid;
}
/*
* @@GaussDB@@
* Target : data partition
* Brief :
* Description :
* Notes :
*/
PartitionIdentifier* partOidGetPartID(Relation rel, Oid partOid)
{
PartitionIdentifier* result = NULL;
/* never happen */
if (!PointerIsValid(rel) || !OidIsValid(partOid)) {
ereport(ERROR, (errcode(ERRCODE_FETCH_DATA_FAILED), errmsg("fail to get partition strategy")));
}
result = (PartitionIdentifier*)palloc0(sizeof(PartitionIdentifier));
if (rel->partMap->type == PART_TYPE_RANGE || rel->partMap->type == PART_TYPE_INTERVAL) {
int i;
RangePartitionMap* rangeMap = (RangePartitionMap*)rel->partMap;
for (i = 0; i < rangeMap->rangeElementsNum; i++) {
if (partOid == rangeMap->rangeElements[i].partitionOid) {
if (rangeMap->rangeElements[i].isInterval) {
result->partArea = PART_AREA_INTERVAL;
} else {
result->partArea = PART_AREA_RANGE;
}
result->partSeq = i;
result->fileExist = true;
result->partitionId = partOid;
break;
}
}
} else {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unsupported partition strategy")));
}
return result;
}
// comment: the function result value of the first partition seq is 1
// a wrapper for partOidGetPartID
int partOidGetPartSequence(Relation rel, Oid partOid)
{
int resultPartSequence = 0;
PartitionIdentifier* resultPartID = NULL;
if (RelationIsNonpartitioned(rel))
return -1;
resultPartID = partOidGetPartID(rel, partOid);
if (resultPartID == NULL) {
resultPartSequence = -1;
} else if (false == resultPartID->fileExist || PART_AREA_NONE == resultPartID->partArea) {
resultPartSequence = -1;
} else {
resultPartSequence = resultPartID->partSeq + 1;
}
pfree_ext(resultPartID);
return resultPartSequence;
}
/* IMPORTANT: This function will case invalidation message process, the relation may
be rebuild, and the relation->partMap may be changed.
After call this founction, should not call getNumberOfRangePartitions/getNumberOfPartitions,
using list_length(partitionList) instead
*/
List* relationGetPartitionList(Relation relation, LOCKMODE lockmode)
{
List* partitionOidList = NIL;
List* partitionList = NIL;
partitionOidList = relationGetPartitionOidList(relation);
if (PointerIsValid(partitionOidList)) {
ListCell* cell = NULL;
Oid partitionId = InvalidOid;
Partition partition = NULL;
foreach (cell, partitionOidList) {
partitionId = lfirst_oid(cell);
Assert(OidIsValid(partitionId));
partition = partitionOpen(relation, partitionId, lockmode);
partitionList = lappend(partitionList, partition);
}
list_free_ext(partitionOidList);
}
return partitionList;
}
// give one partitioned index relation,
// return a list, consisting of oid of all its index partition
List* indexGetPartitionOidList(Relation indexRelation)
{
List* indexPartitionOidList = NIL;
List* indexPartitionTupleList = NIL;
ListCell* cell = NULL;
if (indexRelation->rd_rel->relkind != RELKIND_INDEX || RelationIsNonpartitioned(indexRelation))
return indexPartitionOidList;
indexPartitionTupleList = searchPgPartitionByParentId(PART_OBJ_TYPE_INDEX_PARTITION, indexRelation->rd_id);
foreach (cell, indexPartitionTupleList) {
Oid indexPartOid = HeapTupleGetOid((HeapTuple)lfirst(cell));
if (OidIsValid(indexPartOid)) {
indexPartitionOidList = lappend_oid(indexPartitionOidList, indexPartOid);
}
}
freePartList(indexPartitionTupleList);
return indexPartitionOidList;
}
List* indexGetPartitionList(Relation indexRelation, LOCKMODE lockmode)
{
List* indexPartitionList = NIL;
List* indexPartitionTupleList = NIL;
ListCell* cell = NULL;
if (indexRelation->rd_rel->relkind != RELKIND_INDEX || RelationIsNonpartitioned(indexRelation))
return indexPartitionList;
indexPartitionTupleList = searchPgPartitionByParentId(PART_OBJ_TYPE_INDEX_PARTITION, indexRelation->rd_id);
foreach (cell, indexPartitionTupleList) {
Partition indexPartition = NULL;
Oid indexPartOid = HeapTupleGetOid((HeapTuple)lfirst(cell));
if (OidIsValid(indexPartOid)) {
indexPartition = partitionOpen(indexRelation, indexPartOid, lockmode);
indexPartitionList = lappend(indexPartitionList, indexPartition);
}
}
freePartList(indexPartitionTupleList);
return indexPartitionList;
}
void releasePartitionList(Relation relation, List** partList, LOCKMODE lockmode)
{
ListCell* cell = NULL;
Partition partition = NULL;
foreach (cell, *partList) {
partition = (Partition)lfirst(cell);
Assert(PointerIsValid(partition));
partitionClose(relation, partition, lockmode);
}
list_free_ext(*partList);
*partList = NULL;
}
/*
* @@GaussDB@@
* Target : data partition
* Brief :
* Description :
* Notes :
*/
List* relationGetPartitionOidList(Relation rel)
{
List* result = NIL;
Oid partitionId = InvalidOid;
if (rel == NULL || rel->partMap == NULL) {
return NIL;
}
PartitionMap* map = rel->partMap;
int sumTotal = getPartitionNumber(map);
for (int conuter = 0; conuter < sumTotal; ++conuter) {
partitionId = ((RangePartitionMap*)map)->rangeElements[conuter].partitionOid;
result = lappend_oid(result, partitionId);
}
return result;
}
void releasePartitionOidList(List** partList)
{
if (PointerIsValid(partList)) {
list_free_ext(*partList);
*partList = NIL;
}
}
/*
* @Description: compare two const,datatype of const must be one of datatype partition key supported,
* and the datatype is bpchar varchar or text,the collation id of two consts must be same.
* @param
* @param
* @return 0: value1==value2 1:value1>vlaue2 -1:value1<value2
*/
int constCompare_constType(Const* value1, Const* value2)
{
EState* estate = NULL;
ExprContext* econtext = NULL;
ExprState* exprstate = NULL;
bool result = false;
int ret = 0;
Expr* eqExpr = NULL;
Expr* gtExpr = NULL;
bool isNull = false;
ParseState* pstate = NULL;
pstate = make_parsestate(NULL);
eqExpr = (Expr*)makeSimpleA_Expr(AEXPR_OP, "=", (Node*)value1, (Node*)value2, -1);
eqExpr = (Expr*)transformExpr(pstate, (Node*)eqExpr);
gtExpr = (Expr*)makeSimpleA_Expr(AEXPR_OP, ">", (Node*)value1, (Node*)value2, -1);
gtExpr = (Expr*)transformExpr(pstate, (Node*)gtExpr);
((OpExpr*)gtExpr)->inputcollid = value1->constcollid;
estate = CreateExecutorState();
econtext = GetPerTupleExprContext(estate);
exprstate = ExecPrepareExpr(eqExpr, estate);
if (!PointerIsValid(exprstate)) {
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_INVALID_SQLSTATE_RETURNED),
errmsg("failed when making EQUAL expression state for constCompare")));
}
result = DatumGetBool(ExecEvalExpr(exprstate, econtext, &isNull, NULL));
FreeExecutorState(estate);
if (result) {
ret = 0;
} else {
estate = CreateExecutorState();
econtext = GetPerTupleExprContext(estate);
exprstate = ExecPrepareExpr(gtExpr, estate);
if (!PointerIsValid(exprstate)) {
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_INVALID_SQLSTATE_RETURNED),
errmsg("failed when making GREATE-THAN expression state for constCompare")));
}
result = DatumGetBool(ExecEvalExpr(exprstate, econtext, &isNull, NULL));
if (result) {
ret = 1;
} else {
ret = -1;
}
FreeExecutorState(estate);
}
pfree_ext(pstate);
return ret;
}
int partitonKeyCompare(Const** value1, Const** value2, int len)
{
uint8 i = 0;
int compare = 0;
Const* v1 = NULL;
Const* v2 = NULL;
for (; i < len; i++) {
v1 = *(value1 + i);
v2 = *(value2 + i);
if (v1 == NULL || v2 == NULL) {
if (v1 == NULL && v2 == NULL)
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("NULL can not be compared with NULL")));
else if (v1 == NULL)
compare = -1;
else
compare = 1;
break;
}
if (constIsMaxValue(v1) || constIsMaxValue(v2)) {
if (constIsMaxValue(v1) && constIsMaxValue(v2)) {
compare = 0;
continue;
} else if (constIsMaxValue(v1))
compare = 1;
else
compare = -1;
break;
}
if (v1->constisnull || v2->constisnull) {
if (v1->constisnull && v2->constisnull)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
errmsg("null value can not be compared with null value.")));
else if (v1->constisnull)
compare = 1;
else
compare = -1;
break;
}
constCompare(v1, v2, compare);
if (0 != compare)
break;
}
return compare;
}
int rangeElementCmp(const void* a, const void* b)
{
const RangeElement* rea = (const RangeElement*)a;
const RangeElement* reb = (const RangeElement*)b;
Assert(rea->len == reb->len);
Assert(rea->len <= RANGE_PARTKEYMAXNUM);
return partitonKeyCompare((Const**)rea->boundary, (Const**)reb->boundary, rea->len);
}
/*
* @@GaussDB@@
* Brief :
* Description : return the partition number of a partitioned table ,include range partitions and
* interval partitions
*
* Notes : caller should keep a suitable lock on the parittioned table
*/
int getPartitionNumber(PartitionMap* map)
{
int result = -1;
if (map->type == PART_TYPE_RANGE || map->type == PART_TYPE_INTERVAL) {
result = ((RangePartitionMap*)map)->rangeElementsNum;
} else {
ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("unsupported partitioned strategy")));
}
return result;
}
/*
* @@GaussDB@@
* Target : data partition
* Brief : check the partition key in the targetlist
* Description :
* Notes :
*/
bool targetListHasPartitionKey(List* targetList, Oid partitiondtableid)
{
bool ret = false;
Relation rel;
rel = heap_open(partitiondtableid, NoLock);
if (RELATION_IS_PARTITIONED(rel)) {
RangePartitionMap* map = (RangePartitionMap*)rel->partMap;
int2vector* partKey = map->partitionKey;
ListCell* lc = NULL;
int j = 0;
foreach (lc, targetList) {
TargetEntry* entry = (TargetEntry*)lfirst(lc);
if (entry->resjunk) {
continue;
}
/* check partkey has the column */
for (j = 0; j < partKey->dim1; j++) {
if (partKey->values[j] == entry->resno) {
heap_close(rel, NoLock);
return true;
}
}
}
}
heap_close(rel, NoLock);
return ret;
}
// Description : Check the tuple whether locate the partition
bool tupleLocateThePartition(Relation partTableRel, int partSeq, TupleDesc tupleDesc, HeapTuple tuple)
{
RangePartitionMap* partMap = NULL;
int2vector* partkeyColumns = NULL;
int partkeyColumnNum = 0;
int i = 0;
Const* tuplePartKeyValues[RANGE_PARTKEYMAXNUM];
Const consts[RANGE_PARTKEYMAXNUM];
Datum tuplePartKeyValue;
bool isNull = false;
bool isInPartition = false;
if (partSeq < 0 || partSeq >= ((RangePartitionMap*)(partTableRel->partMap))->rangeElementsNum)
return false;
incre_partmap_refcount(partTableRel->partMap);
partMap = (RangePartitionMap*)(partTableRel->partMap);
partkeyColumns = partMap->partitionKey;
partkeyColumnNum = partkeyColumns->dim1;
for (i = 0; i < partkeyColumnNum; i++) {
isNull = false;
tuplePartKeyValue = heap_getattr(tuple, (int)partkeyColumns->values[i], tupleDesc, &isNull);
tuplePartKeyValues[i] =
transformDatum2Const(tupleDesc, partkeyColumns->values[i], tuplePartKeyValue, isNull, &consts[i]);
}
isInPartition = isPartKeyValuesInPartition(partMap, tuplePartKeyValues, partkeyColumnNum, partSeq);
decre_partmap_refcount(partTableRel->partMap);
return isInPartition;
}
bool isPartKeyValuesInPartition(RangePartitionMap* partMap, Const** partKeyValues, int partkeyColumnNum, int partSeq)
{
Assert(partMap && partKeyValues);
Assert(partkeyColumnNum == partMap->partitionKey->dim1);
int compareBottom = 0;
int compareTop = 0;
bool greaterThanBottom = false;
bool lessThanTop = false;
if (0 == partSeq) {
/* is in first partition (-inf, boundary) */
greaterThanBottom = true;
partitonKeyCompareForRouting(partKeyValues, partMap->rangeElements[0].boundary, partkeyColumnNum, compareTop);
if (compareTop < 0) {
lessThanTop = true;
}
} else {
/* is in [last_partiton_boundary, boundary) */
partitonKeyCompareForRouting(
partKeyValues, partMap->rangeElements[partSeq - 1].boundary, partkeyColumnNum, compareBottom);
if (compareBottom >= 0) {
greaterThanBottom = true;
}
partitonKeyCompareForRouting(
partKeyValues, partMap->rangeElements[partSeq].boundary, partkeyColumnNum, compareTop);
if (compareTop < 0) {
lessThanTop = true;
}
}
return greaterThanBottom && lessThanTop;
}
// check the partition has toast
bool partitionHasToast(Oid partOid)
{
HeapTuple tuple = NULL;
Form_pg_partition partForm = NULL;
bool result = false;
tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partOid));
if (!HeapTupleIsValid(tuple)) {
Assert(0);
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for table partition %u", partOid)));
}
Assert(HeapTupleGetOid(tuple) == partOid);
partForm = (Form_pg_partition)GETSTRUCT(tuple);
if (OidIsValid(partForm->reltoastrelid)) {
result = true;
}
ReleaseSysCache(tuple);
return result;
}
int comparePartitionKey(RangePartitionMap* partMap, Const** values1, Const** values2, int partKeyNum)
{
int compare = 0;
incre_partmap_refcount((PartitionMap*)partMap);
partitonKeyCompareForRouting(values1, values2, partKeyNum, compare);
decre_partmap_refcount((PartitionMap*)partMap);
return compare;
}
void incre_partmap_refcount(PartitionMap* map)
{
ResourceOwnerEnlargePartitionMapRefs(t_thrd.utils_cxt.CurrentResourceOwner);
map->refcount += 1;
if (!IsBootstrapProcessingMode())
ResourceOwnerRememberPartitionMapRef(t_thrd.utils_cxt.CurrentResourceOwner, map);
}
void decre_partmap_refcount(PartitionMap* map)
{
Assert(map->refcount > 0);
map->refcount -= 1;
if (!IsBootstrapProcessingMode())
ResourceOwnerForgetPartitionMapRef(t_thrd.utils_cxt.CurrentResourceOwner, map);
}
/*
* Get the oid of the partition which is a interval partition and next to the droped range partition which is
* specificed by partOid. If the droped partition is a interval partition, the next partition no need to
* be changed to range partition, return InvalidOid. If the next partition is a range partition, nothing need
* to do, return InvalidOid.
*/
Oid GetNeedDegradToRangePartOid(Relation rel, Oid partOid)
{
/* never happen */
if (!PointerIsValid(rel) || !OidIsValid(partOid)) {
ereport(ERROR,
(errcode(ERRCODE_FETCH_DATA_FAILED), errmsg("invalid partitioned table relaiton or partition table oid")));
}
Assert(rel->partMap->type == PART_TYPE_RANGE || rel->partMap->type == PART_TYPE_INTERVAL);
/* In normal range partitioned tabel, there has no interval ranges. */
if (rel->partMap->type == PART_TYPE_RANGE) {
return InvalidOid;
}
RangePartitionMap* rangeMap = (RangePartitionMap*)rel->partMap;
for (int i = 0; i < rangeMap->rangeElementsNum; i++) {
if (rangeMap->rangeElements[i].partitionOid == partOid) {
/*
* 1. the droped range is interval range
* 2. there is no more ranges
* 3. the next partition is a range partition
*/
if (rangeMap->rangeElements[i].isInterval || (i == rangeMap->rangeElementsNum - 1) ||
!rangeMap->rangeElements[i + 1].isInterval) {
return InvalidOid;
}
return rangeMap->rangeElements[i + 1].partitionOid;
}
}
/* It must never happened. */
ereport(ERROR, (errcode(ERRCODE_CASE_NOT_FOUND), errmsg("Not find the target partiton %u", partOid)));
return InvalidOid;
}