diff --git a/src/bin/pg_dump/pg_dump.cpp b/src/bin/pg_dump/pg_dump.cpp index 3a2c8855a..888796643 100644 --- a/src/bin/pg_dump/pg_dump.cpp +++ b/src/bin/pg_dump/pg_dump.cpp @@ -31,6 +31,7 @@ */ #include "postgres_fe.h" +#include #ifdef WIN32_PG_DUMP #undef PGDLLIMPORT @@ -17498,6 +17499,72 @@ static void OutputIntervalPartitionDef(Archive* fout, const PGresult* res, PQExp free(interTblSpcs); } +static bool PartkeyexprIsNull(Archive* fout, TableInfo* tbinfo, bool isSubPart) +{ + PQExpBuffer partkeyexpr = createPQExpBuffer(); + PGresult* partkeyexpr_res = NULL; + int i_partkeyexpr; + bool partkeyexprIsNull = true; + if (!isSubPart) + appendPQExpBuffer(partkeyexpr, + "select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = \'%s\' and " + "relnamespace = %u));",tbinfo->dobj.name, tbinfo->dobj.nmspace->dobj.catId.oid); + else + appendPQExpBuffer(partkeyexpr, + "select distinct partkeyexpr from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = \'%s\' and " + "relnamespace = %u));",tbinfo->dobj.name, tbinfo->dobj.nmspace->dobj.catId.oid); + partkeyexpr_res = ExecuteSqlQueryForSingleRow(fout, partkeyexpr->data); + i_partkeyexpr = PQfnumber(partkeyexpr_res, "partkeyexpr"); + char* partkeyexpr_buf = PQgetvalue(partkeyexpr_res, 0, i_partkeyexpr); + if (partkeyexpr_buf && strcmp(partkeyexpr_buf, "") != 0) + partkeyexprIsNull = false; + PQclear(partkeyexpr_res); + destroyPQExpBuffer(partkeyexpr); + return partkeyexprIsNull; +} + +static void GetPartkeyexprSrc(PQExpBuffer result, Archive* fout, TableInfo* tbinfo, bool isSubPart) +{ + PQExpBuffer tabledef = createPQExpBuffer(); + PGresult* tabledef_res = NULL; + int i_tabledef; + appendPQExpBuffer(tabledef,"select pg_get_tabledef(\'%s\');",tbinfo->dobj.name); + tabledef_res = ExecuteSqlQueryForSingleRow(fout, tabledef->data); + i_tabledef = PQfnumber(tabledef_res, "pg_get_tabledef"); + char* tabledef_buf = PQgetvalue(tabledef_res, 0, i_tabledef); + std::string tabledef_str(tabledef_buf); + size_t start; + if (isSubPart) { + start = tabledef_str.find("SUBPARTITION BY"); + start+=16; + } else { + start = tabledef_str.find("PARTITION BY"); + start+=13; + } + destroyPQExpBuffer(tabledef); + PQclear(tabledef_res); + if (start != std::string::npos) { + std::string tmp = tabledef_str.substr(start); + size_t pos = tmp.find("("); + size_t i = 0; + size_t j = pos+1; + size_t icount = 0; + size_t jcount = 0; + i = tmp.find(")"); + while ((j = tmp.find("(", j)) != std::string::npos && j < i) { + j++; + jcount++; + } + while (i != std::string::npos && icount < jcount) { + i++; + i = tmp.find(")", i); + icount++; + } + std::string res = tmp.substr(pos+1,i-pos-1); + appendPQExpBuffer(result, "%s", res.c_str()); + } +} + static void GenerateSubPartitionBy(PQExpBuffer result, Archive *fout, TableInfo *tbinfo) { int i; @@ -17509,6 +17576,7 @@ static void GenerateSubPartitionBy(PQExpBuffer result, Archive *fout, TableInfo PQExpBuffer subPartStrategyQ = createPQExpBuffer(); PGresult *res = NULL; + bool partkeyexprIsNull = PartkeyexprIsNull(fout, tbinfo, true); /* get subpartitioned table's partstrategy */ appendPQExpBuffer(subPartStrategyQ, "SELECT partstrategy " @@ -17550,14 +17618,19 @@ static void GenerateSubPartitionBy(PQExpBuffer result, Archive *fout, TableInfo } /* assign subpartitioning columns */ - for (i = 0; i < partkeynum; i++) { - if (i > 0) - appendPQExpBuffer(result, ", "); - if (partkeycols[i] < 1) { - exit_horribly(NULL, "array index should not be smaller than zero: %d\n", partkeycols[i] - 1); + if (partkeyexprIsNull) { + for (i = 0; i < partkeynum; i++) { + if (i > 0) + appendPQExpBuffer(result, ", "); + if (partkeycols[i] < 1) { + exit_horribly(NULL, "array index should not be smaller than zero: %d\n", partkeycols[i] - 1); + } + appendPQExpBuffer(result, "%s", fmtId(tbinfo->attnames[partkeycols[i] - 1])); } - appendPQExpBuffer(result, "%s", fmtId(tbinfo->attnames[partkeycols[i] - 1])); + } else { + GetPartkeyexprSrc(result, fout, tbinfo, true); } + appendPQExpBuffer(result, ")\n"); PQclear(res); @@ -17738,6 +17811,7 @@ static PQExpBuffer createTablePartition(Archive* fout, TableInfo* tbinfo) int i; int j; + bool partkeyexprIsNull = PartkeyexprIsNull(fout, tbinfo, false); /* get partitioned table info */ appendPQExpBuffer(defq, "SELECT partstrategy, interval[1], " @@ -17804,13 +17878,17 @@ static PQExpBuffer createTablePartition(Archive* fout, TableInfo* tbinfo) } /* assign partitioning columns */ - for (i = 0; i < partkeynum; i++) { - if (i > 0) - appendPQExpBuffer(result, ", "); - if (partkeycols[i] < 1) { - exit_horribly(NULL, "array index should not be smaller than zero: %d\n", partkeycols[i] - 1); + if (partkeyexprIsNull) { + for (i = 0; i < partkeynum; i++) { + if (i > 0) + appendPQExpBuffer(result, ", "); + if (partkeycols[i] < 1) { + exit_horribly(NULL, "array index should not be smaller than zero: %d\n", partkeycols[i] - 1); + } + appendPQExpBuffer(result, "%s", fmtId(tbinfo->attnames[partkeycols[i] - 1])); } - appendPQExpBuffer(result, "%s", fmtId(tbinfo->attnames[partkeycols[i] - 1])); + } else { + GetPartkeyexprSrc(result, fout, tbinfo, false); } if (tbinfo->parttype == PARTTYPE_SUBPARTITIONED_RELATION) { diff --git a/src/common/backend/catalog/heap.cpp b/src/common/backend/catalog/heap.cpp index 3883eefbc..764110d48 100644 --- a/src/common/backend/catalog/heap.cpp +++ b/src/common/backend/catalog/heap.cpp @@ -143,10 +143,11 @@ static void deletePartitionTuple(Oid part_id); static void addNewPartitionTuplesForPartition(Relation pg_partition_rel, Oid relid, Oid reltablespace, Oid bucketOid, PartitionState* partTableState, Oid ownerid, - Datum reloptions, const TupleDesc tupledesc, char strategy, StorageType storage_type, LOCKMODE partLockMode); + Datum reloptions, const TupleDesc tupledesc, char strategy, StorageType storage_type, LOCKMODE partLockMode, + bool partkeyexprIsNull = true); static void addNewPartitionTupleForTable(Relation pg_partition_rel, const char* relname, const Oid reloid, const Oid reltablespaceid, const TupleDesc reltupledesc, const PartitionState* partTableState, Oid ownerid, - Datum reloptions); + Datum reloptions, bool partkeyexprIsNull = true); static void addNewPartitionTupleForValuePartitionedTable(Relation pg_partition_rel, const char* relname, const Oid reloid, const Oid reltablespaceid, const TupleDesc reltupledesc, const PartitionState* partTableState, @@ -2509,6 +2510,57 @@ static Datum AddSegmentOption(Datum relOptions) return transformRelOptions((Datum)0, optsList, NULL, NULL, false, false); } +Node* GetColumnRef(Node* key, bool* isExpr) +{ + Node* result = NULL; + A_Expr* aexpr = NULL; + Node* col = NULL; + FuncCall *funcexpr = NULL; + ListCell* cell = NULL; + switch (key->type) { + case T_ColumnRef: + result = key; + break; + case T_A_Expr: + *isExpr = true; + aexpr = (A_Expr*)key; + col = GetColumnRef(aexpr->lexpr, isExpr); + if(col) + result = col; + else + result = GetColumnRef(aexpr->rexpr, isExpr); + break; + case T_FuncCall: + *isExpr = true; + funcexpr = (FuncCall*)key; + foreach(cell, funcexpr->args) { + result = GetColumnRef((Node*)(lfirst(cell)), isExpr); + if (result) + break; + } + break; + default: + break; + } + return result; +} + +static bool CheckPartitionExprKey(List* keys, char partStrategy) +{ + bool isExpr = false; + ListCell* cell = NULL; + foreach (cell, keys) { + Node* col = GetColumnRef((Node*)lfirst(cell), &isExpr); + if (!col) + ereport(ERROR,(errcode(ERRCODE_UNDEFINED_COLUMN),(errmsg("The partition key doesn't have any column.")))); + if (isExpr && keys->length > 1) + ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),(errmsg("The multi partition expr keys are not supported.")))); + } + if (isExpr && (partStrategy == PART_STRATEGY_VALUE || partStrategy == PART_STRATEGY_INTERVAL)) + ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),(errmsg("The partition expr key doesn't supprt value or interval partition")))); + return !isExpr; +} + /* -------------------------------- * heap_create_with_catalog * @@ -2740,7 +2792,13 @@ Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltable /* Get uids info from reloptions */ relhasuids = StdRdOptionsHasUids(hreloptions, relkind); - + bool partkeyexprIsNull = true; + bool subpartkeyexprIsNull = true; + if (partTableState) { + partkeyexprIsNull = CheckPartitionExprKey(partTableState->partitionKey, partTableState->partitionStrategy); + if (partTableState->subPartitionState) + subpartkeyexprIsNull = CheckPartitionExprKey(partTableState->subPartitionState->partitionKey, partTableState->subPartitionState->partitionStrategy); + } /* * Create the relcache entry (mostly dummy at this point) and the physical * disk file. (If we fail further down, it's the smgr's responsibility to @@ -2944,7 +3002,8 @@ Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltable tupdesc, /*partitioned table's tuple descriptor*/ partTableState, /*partition schema of partitioned table*/ ownerid, /*partitioned table's owner id*/ - reloptions); + reloptions, + partkeyexprIsNull); /* add partition entry to pg_partition */ addNewPartitionTuplesForPartition(pg_partition_desc, /*RelationData pointer for pg_partition*/ @@ -2957,7 +3016,8 @@ Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltable tupdesc, partTableState->partitionStrategy, storage_type, - partLockMode); + partLockMode, + subpartkeyexprIsNull); } } @@ -5257,8 +5317,13 @@ int2vector* buildPartitionKey(List* keys, TupleDesc tupledsc) int2vector* partkey = NULL; partkey = buildint2vector(NULL, partkeyNum); + bool isExpr = false; foreach (cell, keys) { - col = (ColumnRef*)lfirst(cell); + col = (ColumnRef*)GetColumnRef((Node*)lfirst(cell), &isExpr); + if (!col) + ereport(ERROR,(errcode(ERRCODE_UNDEFINED_COLUMN),(errmsg("The partition key doesn't have any column.")))); + if (isExpr && keys->length > 1) + ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),(errmsg("The multi partition expr keys are not supported.")))); columName = ((Value*)linitial(col->fields))->val.str; finded = false; for (j = 0; j < attnum; j++) { @@ -5327,7 +5392,7 @@ static Datum BuildInterval(Node* partInterval) * Notes : */ void addNewPartitionTuple(Relation pg_part_desc, Partition new_part_desc, int2vector* pkey, oidvector* intablespace, - Datum interval, Datum maxValues, Datum transitionPoint, Datum reloptions) + Datum interval, Datum maxValues, Datum transitionPoint, Datum reloptions, bool partkeyexprIsNull) { Form_pg_partition new_part_tup = new_part_desc->pd_part; /* @@ -5362,7 +5427,8 @@ void addNewPartitionTuple(Relation pg_part_desc, Partition new_part_desc, int2ve maxValues, transitionPoint, reloptions, - new_part_tup->parttype); + new_part_tup->parttype, + partkeyexprIsNull); } static void deletePartitionTuple(Oid part_id) @@ -6012,7 +6078,7 @@ void GetNewPartitionOidAndNewPartrelfileOid(List *subPartitionDefState, Oid *new */ Oid heapAddRangePartition(Relation pgPartRel, Oid partTableOid, Oid partTablespace, Oid bucketOid, RangePartitionDefState *newPartDef, Oid ownerid, Datum reloptions, const bool *isTimestamptz, - StorageType storage_type, LOCKMODE partLockMode, int2vector* subpartition_key, bool isSubpartition) + StorageType storage_type, LOCKMODE partLockMode, int2vector* subpartition_key, bool isSubpartition, bool partkeyexprIsNull) { Datum boundaryValue = (Datum)0; Oid newPartitionOid = InvalidOid; @@ -6103,7 +6169,8 @@ Oid heapAddRangePartition(Relation pgPartRel, Oid partTableOid, Oid partTablespa (Datum)0, /* interval*/ boundaryValue, /* max values */ (Datum)0, /* transition point */ - reloptions); + reloptions, + partkeyexprIsNull); if (isSubpartition) { PartitionCloseSmgr(newPartition); @@ -6321,7 +6388,7 @@ Oid HeapAddIntervalPartition(Relation pgPartRel, Relation rel, Oid partTableOid, Oid HeapAddListPartition(Relation pgPartRel, Oid partTableOid, Oid partTablespace, Oid bucketOid, ListPartitionDefState* newListPartDef, Oid ownerid, Datum reloptions, const bool* isTimestamptz, - StorageType storage_type, int2vector* subpartition_key, bool isSubpartition) + StorageType storage_type, int2vector* subpartition_key, bool isSubpartition, bool partkeyexprIsNull) { Datum boundaryValue = (Datum)0; Oid newListPartitionOid = InvalidOid; @@ -6402,7 +6469,8 @@ Oid HeapAddListPartition(Relation pgPartRel, Oid partTableOid, Oid partTablespac (Datum)0, /* interval*/ boundaryValue, /* max values */ (Datum)0, /* transition point */ - reloptions); + reloptions, + partkeyexprIsNull); if (isSubpartition) { PartitionCloseSmgr(newListPartition); @@ -6613,7 +6681,7 @@ Oid AddNewIntervalPartition(Relation rel, void* insertTuple, bool isDDL) Oid HeapAddHashPartition(Relation pgPartRel, Oid partTableOid, Oid partTablespace, Oid bucketOid, HashPartitionDefState* newHashPartDef, Oid ownerid, Datum reloptions, const bool* isTimestamptz, - StorageType storage_type, int2vector* subpartition_key, bool isSubpartition) + StorageType storage_type, int2vector* subpartition_key, bool isSubpartition, bool partkeyexprIsNull) { Datum boundaryValue = (Datum)0; Oid newHashPartitionOid = InvalidOid; @@ -6704,7 +6772,8 @@ Oid HeapAddHashPartition(Relation pgPartRel, Oid partTableOid, Oid partTablespac (Datum)0, /* interval*/ boundaryValue, /* max values */ (Datum)0, /* transition point */ - reloptions); + reloptions, + partkeyexprIsNull); if (isSubpartition) { PartitionCloseSmgr(newHashPartition); @@ -6814,7 +6883,7 @@ static void addNewPartitionTupleForValuePartitionedTable(Relation pg_partition_r */ static void addNewPartitionTupleForTable(Relation pg_partition_rel, const char* relname, const Oid reloid, const Oid reltablespaceid, const TupleDesc reltupledesc, const PartitionState* partTableState, Oid ownerid, - Datum reloptions) + Datum reloptions, bool partkeyexprIsNull) { Datum interval = (Datum)0; Datum transition_point = (Datum)0; @@ -6892,7 +6961,8 @@ static void addNewPartitionTupleForTable(Relation pg_partition_rel, const char* interval, /* interval partitioned table's interval*/ (Datum)0, /* partitioned table's boundary value is empty in pg_partition */ transition_point, /* interval's partitioned table's transition point*/ - newOptions); + newOptions, + partkeyexprIsNull); relation = relation_open(reloid, NoLock); partitionClose(relation, new_partition, NoLock); relation_close(relation, NoLock); @@ -7153,7 +7223,7 @@ Oid GetPartTablespaceOidForSubpartition(Oid reltablespace, const char* partTable */ static void addNewPartitionTuplesForPartition(Relation pg_partition_rel, Oid relid, Oid reltablespace, Oid bucketOid, PartitionState* partTableState, Oid ownerid, Datum reloptions, - const TupleDesc tupledesc, char strategy, StorageType storage_type, LOCKMODE partLockMode) + const TupleDesc tupledesc, char strategy, StorageType storage_type, LOCKMODE partLockMode, bool partkeyexprIsNull) { int partKeyNum = list_length(partTableState->partitionKey); bool isTimestamptzForPartKey[partKeyNum]; @@ -7197,7 +7267,9 @@ static void addNewPartitionTuplesForPartition(Relation pg_partition_rel, Oid rel reloptions, isTimestamptzForPartKey, storage_type, - subpartition_key_attr_no); + subpartition_key_attr_no, + false, + partkeyexprIsNull); Oid partTablespaceOid = GetPartTablespaceOidForSubpartition(reltablespace, partitionDefState->tablespacename); @@ -7223,7 +7295,9 @@ static void addNewPartitionTuplesForPartition(Relation pg_partition_rel, Oid rel reloptions, isTimestamptzForPartKey, storage_type, - subpartition_key_attr_no); + subpartition_key_attr_no, + false, + partkeyexprIsNull); Oid partTablespaceOid = GetPartTablespaceOidForSubpartition(reltablespace, partitionDefState->tablespacename); @@ -7250,7 +7324,9 @@ static void addNewPartitionTuplesForPartition(Relation pg_partition_rel, Oid rel isTimestamptzForPartKey, storage_type, partLockMode, - subpartition_key_attr_no); + subpartition_key_attr_no, + false, + partkeyexprIsNull); Oid partTablespaceOid = GetPartTablespaceOidForSubpartition(reltablespace, partitionDefState->tablespacename); diff --git a/src/common/backend/catalog/pg_partition.cpp b/src/common/backend/catalog/pg_partition.cpp index 2c43ed2e8..e5b876ad2 100644 --- a/src/common/backend/catalog/pg_partition.cpp +++ b/src/common/backend/catalog/pg_partition.cpp @@ -47,7 +47,7 @@ void insertPartitionEntry(Relation pg_partition_desc, Partition new_part_desc, Oid new_part_id, int2vector* pkey, const oidvector* tablespaces, Datum interval, Datum maxValues, Datum transitionPoint, Datum reloptions, - char parttype) + char parttype, bool partkeyexprIsNull) { Datum values[Natts_pg_partition]; bool nulls[Natts_pg_partition]; @@ -148,7 +148,11 @@ void insertPartitionEntry(Relation pg_partition_desc, Partition new_part_desc, O values[Anum_pg_partition_relminmxid - 1] = InvalidMultiXactId; #endif } - + if (partkeyexprIsNull) { + nulls[Anum_pg_partition_partkeyexpr - 1] = true; + } else { + values[Anum_pg_partition_partkeyexpr - 1] = CStringGetTextDatum(""); + } /* form a tuple using values and null array, and insert it */ tup = heap_form_tuple(RelationGetDescr(pg_partition_desc), values, nulls); HeapTupleSetOid(tup, new_part_id); @@ -1523,3 +1527,25 @@ void UnlockRelationForAddIntervalPartition(Relation rel) t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; } +bool PartExprKeyIsNull(Relation rel, Relation partitionRel) +{ + Relation pgPartitionRel = partitionRel; + HeapTuple partTuple = NULL; + if (!partitionRel) + pgPartitionRel = heap_open(PartitionRelationId, RowExclusiveLock); + if (OidIsValid(rel->parentId)) + partTuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(rel->rd_id)); + else + partTuple = searchPgPartitionByParentIdCopy(PART_OBJ_TYPE_PARTED_TABLE, rel->rd_id); + if (!partTuple) + ereport(ERROR,(errcode(ERRCODE_PARTITION_ERROR),errmsg("The partition can't be found"))); + bool isnull = false; + fastgetattr(partTuple, Anum_pg_partition_partkeyexpr, RelationGetDescr(pgPartitionRel), &isnull); + if (OidIsValid(rel->parentId)) + ReleaseSysCache(partTuple); + else + heap_freetuple(partTuple); + if (!partitionRel) + heap_close(pgPartitionRel, RowExclusiveLock); + return isnull; +} diff --git a/src/common/backend/parser/gram.y b/src/common/backend/parser/gram.y index 165a93b28..60d192a21 100644 --- a/src/common/backend/parser/gram.y +++ b/src/common/backend/parser/gram.y @@ -266,6 +266,7 @@ static bool CheckWhetherInColList(char *colname, List *col_list); static int GetFillerColIndex(char *filler_col_name, List *col_list); static void RemoveFillerCol(List *filler_list, List *col_list); static int errstate; +static void CheckPartitionExpr(Node* expr, int* colCount); static void setDelimiterName(core_yyscan_t yyscanner, char*input, VariableSetStmt*n); %} @@ -6364,9 +6365,14 @@ column_item_list: ; column_item: - ColId + a_expr { - $$ = makeColumnRef($1, NIL, @1, yyscanner); + //$$ = makeColumnRef($1, NIL, @1, yyscanner); + $$ = $1; + int colCount = 0; + CheckPartitionExpr($$, &colCount); + if (colCount == 0) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("No Column in the part expr"))); } ; @@ -29064,6 +29070,45 @@ static void checkDeleteRelationError() errmsg("multi-relation delete only support in B-format database"))); } +#ifndef MAX_SUPPORTED_FUNC_FOR_PART_EXPR +#define MAX_SUPPORTED_FUNC_FOR_PART_EXPR 23 +#endif +static void CheckPartitionExpr(Node* expr, int* colCount) +{ + if (expr == NULL) + ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("The expr can't be NULL"))); + if (expr->type == T_A_Expr) { + char* name = strVal(linitial(((A_Expr*)expr)->name)); + if (strcmp(name, "+") != 0 && strcmp(name, "-") != 0 && strcmp(name, "*") != 0) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("The %s operator is not supported for Partition Expr", name))); + CheckPartitionExpr(((A_Expr*)expr)->lexpr, colCount); + CheckPartitionExpr(((A_Expr*)expr)->rexpr, colCount); + } else if (expr->type == T_FuncCall) { + char* validFuncName[MAX_SUPPORTED_FUNC_FOR_PART_EXPR] = {"abs","ceiling","datediff","day","dayofmonth","dayofweek","dayofyear","extract","floor","hour", + "microsecond","minute","mod","month","quarter","second","time_to_sec","to_days","to_seconds","unix_timestamp","weekday","year","yearweek"}; + char* funcname = strVal(linitial(((FuncCall*)expr)->funcname)); + int count = 0; + for (;count < MAX_SUPPORTED_FUNC_FOR_PART_EXPR;count++) { + if (strcmp(funcname, validFuncName[count]) == 0) + break; + else + continue; + } + if (count == MAX_SUPPORTED_FUNC_FOR_PART_EXPR) + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("The %s func is not supported for Partition Expr", funcname))); + ListCell* cell = NULL; + foreach (cell, ((FuncCall*)expr)->args) { + CheckPartitionExpr((Node*)(lfirst(cell)),colCount); + } + } else if (expr->type == T_ColumnRef) { + (*colCount)++; + } else if (expr->type == T_A_Const) { + return; + } else { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("The Partition Expr can't be %d type", expr->type))); + } +} + /* * Must undefine this stuff before including scan.c, since it has different * definitions for these macros. diff --git a/src/common/backend/utils/adt/ruleutils.cpp b/src/common/backend/utils/adt/ruleutils.cpp index f92f582ac..eb4642aaa 100644 --- a/src/common/backend/utils/adt/ruleutils.cpp +++ b/src/common/backend/utils/adt/ruleutils.cpp @@ -332,7 +332,7 @@ static char* flatten_reloptions(Oid relid); static Oid SearchSysTable(const char* query); static void replace_cl_types_in_argtypes(Oid func_id, int numargs, Oid* argtypes, bool *is_client_logic); -static void AppendSubPartitionByInfo(StringInfo buf, Oid tableoid, SubpartitionInfo *subpartinfo); +static void AppendSubPartitionByInfo(StringInfo buf, Oid tableoid, SubpartitionInfo *subpartinfo, tableInfo* tableinfo = NULL); static void AppendSubPartitionDetail(StringInfo buf, tableInfo tableinfo, SubpartitionInfo *subpartinfo); static void AppendRangeIntervalPartitionInfo(StringInfo buf, Oid tableoid, tableInfo tableinfo, int partkeynum, Oid *iPartboundary, SubpartitionInfo *subpartinfo); @@ -959,6 +959,32 @@ static void GetListDistributionDef(StringInfo query, StringInfo buf, Oid tableoi return; } +void GetPartitionExprKeySrc(StringInfo buf, Datum* datum, char* relname, Oid tableoid, int* partkeynum, Oid** iPartboundary) +{ + *partkeynum = 1; + *iPartboundary = (Oid*)palloc0(*partkeynum * sizeof(Oid)); + char* partkeystr = MemoryContextStrdup(LocalMyDBCacheMemCxt(), TextDatumGetCString(*datum)); + Node* partkeyexpr = NULL; + if (partkeystr) + partkeyexpr = (Node*)stringToNode_skip_extern_fields(partkeystr); + else + ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("fata error: The partkeystr can't be NULL"))); + if (!partkeyexpr) + ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("The partkeyexpr can't be NULL"))); + if (partkeyexpr->type == T_OpExpr) + (*iPartboundary)[0] = ((OpExpr*)partkeyexpr)->opresulttype; + else if (partkeyexpr->type == T_FuncExpr) + (*iPartboundary)[0] = ((FuncExpr*)partkeyexpr)->funcresulttype; + else + ereport(ERROR, + (errcode(ERRCODE_NODE_ID_MISSMATCH), + errmsg("The node type %d is wrong, it must be T_OpExpr or T_FuncExpr", partkeyexpr->type))); + char* partKeyExprSrc = deparse_expression(partkeyexpr, deparse_context_for(relname, tableoid), false, false); + appendStringInfo(buf, "%s", partKeyExprSrc); + pfree_ext(partKeyExprSrc); + pfree_ext(partkeystr); +} + /* * @Description: get partition table defination * @in query - append query for SPI_execute. @@ -970,6 +996,7 @@ static void GetListDistributionDef(StringInfo query, StringInfo buf, Oid tableoi static void get_table_partitiondef(StringInfo query, StringInfo buf, Oid tableoid, tableInfo tableinfo) { bool isnull = false; + bool isPartExprKeyNull = false; Relation relation = NULL; ScanKeyData key[2]; SysScanDesc scan = NULL; @@ -1003,7 +1030,10 @@ static void get_table_partitiondef(StringInfo query, StringInfo buf, Oid tableoi scan = systable_beginscan(relation, PartitionParentOidIndexId, true, NULL, 2, key); if (HeapTupleIsValid(tuple = systable_getnext(scan))) { int2vector* partVec = NULL; - Datum datum = SysCacheGetAttr(PARTRELID, tuple, Anum_pg_partition_partkey, &isnull); + Datum datum = 0; + datum = SysCacheGetAttr(PARTRELID, tuple, Anum_pg_partition_partkeyexpr, &isPartExprKeyNull); + if (isPartExprKeyNull) + datum = SysCacheGetAttr(PARTRELID, tuple, Anum_pg_partition_partkey, &isnull); partition = (Form_pg_partition)GETSTRUCT(tuple); appendStringInfo(buf, "\n"); @@ -1036,7 +1066,7 @@ static void get_table_partitiondef(StringInfo query, StringInfo buf, Oid tableoi } } - if (isnull == false) { + if (isnull == false && isPartExprKeyNull) { partVec = (int2vector*)DatumGetPointer(datum); partkeynum = partVec->dim1; iPartboundary = (Oid*)palloc0(partkeynum * sizeof(Oid)); @@ -1052,6 +1082,8 @@ static void get_table_partitiondef(StringInfo query, StringInfo buf, Oid tableoi appendStringInfo(buf, "%s", quote_identifier(attname)); pfree_ext(attname); } + } else if (!isPartExprKeyNull) { + GetPartitionExprKeySrc(buf, &datum, tableinfo.relname, tableoid, &partkeynum, &iPartboundary); } appendStringInfo(buf, ")"); } @@ -1075,7 +1107,7 @@ static void get_table_partitiondef(StringInfo query, StringInfo buf, Oid tableoi SubpartitionInfo *subpartinfo = (SubpartitionInfo *)palloc0(sizeof(SubpartitionInfo)); if (parttype == PARTTYPE_SUBPARTITIONED_RELATION) { - AppendSubPartitionByInfo(buf, tableoid, subpartinfo); + AppendSubPartitionByInfo(buf, tableoid, subpartinfo, &tableinfo); } if (partstrategy == PART_STRATEGY_RANGE || partstrategy == PART_STRATEGY_INTERVAL) { @@ -1097,7 +1129,7 @@ static void get_table_partitiondef(StringInfo query, StringInfo buf, Oid tableoi pfree_ext(subpartinfo); } -static void AppendSubPartitionByInfo(StringInfo buf, Oid tableoid, SubpartitionInfo *subpartinfo) +static void AppendSubPartitionByInfo(StringInfo buf, Oid tableoid, SubpartitionInfo *subpartinfo, tableInfo* tableinfo) { Relation partrel = NULL; ScanKeyData key[2]; @@ -1107,6 +1139,7 @@ static void AppendSubPartitionByInfo(StringInfo buf, Oid tableoid, SubpartitionI SysScanDesc subscan = NULL; HeapTuple subparttuple = NULL; bool isnull = false; + bool isPartExprKeyNull = false; partrel = heap_open(PartitionRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_partition_parttype, BTEqualStrategyNumber, F_CHAREQ, @@ -1121,27 +1154,18 @@ static void AppendSubPartitionByInfo(StringInfo buf, Oid tableoid, SubpartitionI ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("could not find partition tuple for subpartition relation %u", tableoid))); } + Datum datum = 0; + datum = SysCacheGetAttr(PARTRELID, parttuple, Anum_pg_partition_partkeyexpr, &isPartExprKeyNull); + if (isPartExprKeyNull) + datum = SysCacheGetAttr(PARTRELID, parttuple, Anum_pg_partition_partkey, &isnull); + Assert(!isnull || !isPartExprKeyNull); - Datum datum = SysCacheGetAttr(PARTRELID, parttuple, Anum_pg_partition_partkey, &isnull); - Assert(!isnull); - int2vector *partVec = (int2vector *)DatumGetPointer(datum); - int partkeynum = partVec->dim1; - if (partkeynum != 1) { - systable_endscan(scan); - heap_close(partrel, AccessShareLock); - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("only support one partkey in subpartition table"))); - } - char *attname = get_attname(tableoid, partVec->values[0]); Oid subparentid = HeapTupleGetOid(parttuple); - ScanKeyInit(&subkey[0], Anum_pg_partition_parttype, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(PARTTYPE_SUBPARTITIONED_RELATION)); ScanKeyInit(&subkey[1], Anum_pg_partition_parentid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(subparentid)); subscan = systable_beginscan(partrel, PartitionParentOidIndexId, true, NULL, 2, subkey); subparttuple = systable_getnext(subscan); - if (!HeapTupleIsValid(subparttuple)) { systable_endscan(scan); systable_endscan(subscan); @@ -1149,7 +1173,6 @@ static void AppendSubPartitionByInfo(StringInfo buf, Oid tableoid, SubpartitionI ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("could not find subpartition tuple for subpartition relation %u", tableoid))); } - Form_pg_partition part = (Form_pg_partition)GETSTRUCT(subparttuple); switch (part->partstrategy) { case PART_STRATEGY_RANGE: @@ -1167,14 +1190,33 @@ static void AppendSubPartitionByInfo(StringInfo buf, Oid tableoid, SubpartitionI ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unrecognized subpartition type %c", part->partstrategy))); } - appendStringInfo(buf, "%s", quote_identifier(attname)); + + int partkeynum = 0; + if (!isnull && isPartExprKeyNull) { + int2vector *partVec = (int2vector *)DatumGetPointer(datum); + partkeynum = partVec->dim1; + char *attname = get_attname(tableoid, partVec->values[0]); + appendStringInfo(buf, "%s", quote_identifier(attname)); + pfree_ext(attname); + subpartinfo->attnum = partVec->values[0]; + subpartinfo->subpartkeytype = get_atttype(tableoid, subpartinfo->attnum); + } else if (!isPartExprKeyNull) { + Oid* iPartboundary = NULL; + GetPartitionExprKeySrc(buf, &datum, tableinfo->relname, tableoid, &partkeynum, &iPartboundary); + subpartinfo->subpartkeytype = *iPartboundary; + pfree_ext(iPartboundary); + } appendStringInfo(buf, ")"); - pfree_ext(attname); + if (partkeynum != 1) { + systable_endscan(scan); + heap_close(partrel, AccessShareLock); + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("only support one partkey in subpartition table"))); + } subpartinfo->issubpartition = true; - subpartinfo->attnum = partVec->values[0]; subpartinfo->subparttype = part->partstrategy; - subpartinfo->subpartkeytype = get_atttype(tableoid, subpartinfo->attnum); subpartinfo->istypestring = isTypeString(subpartinfo->subpartkeytype); systable_endscan(scan); diff --git a/src/common/backend/utils/init/globals.cpp b/src/common/backend/utils/init/globals.cpp index 8d2baa9d2..caab07249 100644 --- a/src/common/backend/utils/init/globals.cpp +++ b/src/common/backend/utils/init/globals.cpp @@ -59,7 +59,7 @@ bool open_join_children = true; bool will_shutdown = false; /* hard-wired binary version number */ -const uint32 GRAND_VERSION_NUM = 92835; +const uint32 GRAND_VERSION_NUM = 92836; const uint32 SELECT_INTO_VAR_VERSION_NUM = 92834; const uint32 DOLPHIN_ENABLE_DROP_NUM = 92830; diff --git a/src/gausskernel/cbb/utils/partition/partitionmap.cpp b/src/gausskernel/cbb/utils/partition/partitionmap.cpp index db00f36d3..0edd012d7 100644 --- a/src/gausskernel/cbb/utils/partition/partitionmap.cpp +++ b/src/gausskernel/cbb/utils/partition/partitionmap.cpp @@ -1500,6 +1500,112 @@ Oid GetRootPartitionOid(Relation relation) return relid; } +static char* CheckPartExprKey(HeapTuple partitioned_tuple, Relation pg_partition) +{ + bool isNull = false; + auto val = fastgetattr(partitioned_tuple, Anum_pg_partition_partkeyexpr, RelationGetDescr(pg_partition), &isNull); + char* partkeystr = NULL; + if (!isNull) + partkeystr = MemoryContextStrdup(LocalMyDBCacheMemCxt(), TextDatumGetCString(val)); + return partkeystr; +} + +static void BuildElementForPartKeyExpr(void* element, HeapTuple partTuple, TupleDesc pgPartitionTupledsc, + char* partkeystr, char partstrategy) +{ + RangeElement* rangeEle = NULL; + ListPartElement* listEle = NULL; + HashPartElement* hashEle = NULL; + Node* partkeyexpr = NULL; + HeapTuple typeTuple = NULL; + Oid typoid = InvalidOid; + Form_pg_type pgTypeForm = NULL; + Datum boundaryRawVal = 0; + bool isNull = false; + List* boundary = NULL; + int counter = 0; + ListCell* cell = NULL; + Assert(PointerIsValid(element) && PointerIsValid(partTuple) && PointerIsValid(pgPartitionTupledsc) && PointerIsValid(partkeystr)); + boundaryRawVal = tableam_tops_tuple_getattr(partTuple, Anum_pg_partition_boundaries, pgPartitionTupledsc, &isNull); + if (isNull) { + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("null maxvalue for tuple %u", HeapTupleGetOid(partTuple)))); + } + boundary = untransformPartitionBoundary(boundaryRawVal); + if (PART_STRATEGY_RANGE == partstrategy) { + Assert(boundary->length == 1); + rangeEle = (RangeElement*)element; + rangeEle->isInterval = false; + rangeEle->len = 1; + rangeEle->partitionOid = HeapTupleGetOid(partTuple); + } else if (PART_STRATEGY_LIST == partstrategy) { + listEle = (ListPartElement*)element; + listEle->len = list_length(boundary); + listEle->boundary = (Const**)palloc0(sizeof(Const*) * listEle->len); + listEle->partitionOid = HeapTupleGetOid(partTuple); + } else if (PART_STRATEGY_HASH == partstrategy) { + hashEle = (HashPartElement*)element; + hashEle->partitionOid = HeapTupleGetOid(partTuple); + } else { + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("The partstrategy %c is not supported", partstrategy))); + } + + partkeyexpr = (Node*)stringToNode_skip_extern_fields(partkeystr); + if (partkeyexpr->type == T_OpExpr) + typoid = ((OpExpr*)partkeyexpr)->opresulttype; + else if (partkeyexpr->type == T_FuncExpr) + typoid = ((FuncExpr*)partkeyexpr)->funcresulttype; + else + ereport(ERROR, + (errcode(ERRCODE_NODE_ID_MISSMATCH), + errmsg("The node type %d is wrong, it must be T_OpExpr or T_FuncExpr", partkeyexpr->type))); + Relation typeRel = heap_open(TypeRelationId, RowExclusiveLock); + typeTuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typoid)); + pgTypeForm = (Form_pg_type)GETSTRUCT(typeTuple); + Oid typcollation = pgTypeForm->typcollation; + int32 typmod = pgTypeForm->typtypmod; + if (PART_STRATEGY_HASH == partstrategy) + typoid = INT4OID; + + foreach (cell, boundary) { + int16 typlen = 0; + bool typbyval = false; + Oid func = InvalidOid; + Oid typelem = InvalidOid; + char typalign; + char typdelim; + Oid typioparam = InvalidOid; + Datum value; + Value* max_value = NULL; + max_value = (Value*)lfirst(cell); + /* deal with null */ + if (!PointerIsValid(max_value->val.str) && (PART_STRATEGY_HASH != partstrategy)) { + if (PART_STRATEGY_RANGE == partstrategy) + rangeEle->boundary[counter++] = makeMaxConst(typoid, typmod, typcollation); + else + listEle->boundary[counter++] = makeMaxConst(typoid, typmod, typcollation); + continue; + } + + /* get the typein function's oid of current type */ + get_type_io_data(typoid, IOFunc_input, &typlen, &typbyval, &typalign, &typdelim, &typioparam, &func); + typelem = get_element_type(typoid); + value = OidFunctionCall3Coll( + func, typcollation, CStringGetDatum(max_value->val.str), ObjectIdGetDatum(typelem), Int32GetDatum(typmod)); + /* save the output values */ + if (PART_STRATEGY_RANGE == partstrategy) + rangeEle->boundary[counter++] = makeConst(typoid, typmod, typcollation, typlen, value, false, typbyval); + else if (PART_STRATEGY_LIST == partstrategy) + listEle->boundary[counter++] = makeConst(typoid, typmod, typcollation, typlen, value, false, typbyval); + else + hashEle->boundary[counter++] = makeConst(typoid, -1, InvalidOid, sizeof(int32), value, false, true); + } + list_free_ext(boundary); + ReleaseSysCache(typeTuple); + heap_close(typeRel, RowExclusiveLock); +} + static void BuildListPartitionMap(Relation relation, Form_pg_partition partitioned_form, HeapTuple partitioned_tuple, Relation pg_partition, List* partition_list) { @@ -1549,6 +1655,7 @@ static void BuildListPartitionMap(Relation relation, Form_pg_partition partition /* iterate partition tuples, build RangeElement for per partition tuple */ list_itr = 0; + char* partkeystr = CheckPartExprKey(partitioned_tuple, pg_partition); foreach (tuple_cell, partition_list) { partition_tuple = (HeapTuple)lfirst(tuple_cell); partition_form = (Form_pg_partition)GETSTRUCT(partition_tuple); @@ -1562,14 +1669,18 @@ static void BuildListPartitionMap(Relation relation, Form_pg_partition partition errmsg("Fail to build partitionmap for partitioned table \"%u\"", partition_form->parentid), errdetail("Incorrect partition strategy for partition %u", HeapTupleGetOid(partition_tuple)))); } - - buildListElement(&(list_eles[list_itr]), - list_map->partitionKeyDataType, - list_map->partitionKey->dim1, - rootPartitionOid, - list_map->partitionKey, - partition_tuple, - RelationGetDescr(pg_partition)); + if (!partkeystr || (pg_strcasecmp(partkeystr, "") == 0)) { + buildListElement(&(list_eles[list_itr]), + list_map->partitionKeyDataType, + list_map->partitionKey->dim1, + rootPartitionOid, + list_map->partitionKey, + partition_tuple, + RelationGetDescr(pg_partition)); + } else { + BuildElementForPartKeyExpr(&(list_eles[list_itr]), partition_tuple, RelationGetDescr(pg_partition), + partkeystr, PART_STRATEGY_LIST); + } list_itr++; } @@ -1658,6 +1769,7 @@ static void BuildHashPartitionMap(Relation relation, Form_pg_partition partition /* iterate partition tuples, build RangeElement for per partition tuple */ hash_itr = 0; + char* partkeystr = CheckPartExprKey(partitioned_tuple, pg_partition); foreach (tuple_cell, partition_list) { partition_tuple = (HeapTuple)lfirst(tuple_cell); partition_form = (Form_pg_partition)GETSTRUCT(partition_tuple); @@ -1671,15 +1783,19 @@ static void BuildHashPartitionMap(Relation relation, Form_pg_partition partition errmsg("Fail to build partitionmap for partitioned table \"%u\"", partition_form->parentid), errdetail("Incorrect partition strategy for partition %u", HeapTupleGetOid(partition_tuple)))); } - - buildHashElement(&(hash_eles[hash_itr]), - hash_map->partitionKeyDataType, - hash_map->partitionKey->dim1, - rootPartitionOid, - hash_map->partitionKey, - partition_tuple, - RelationGetDescr(pg_partition)); - + if (!partkeystr || (pg_strcasecmp(partkeystr, "") == 0)) { + buildHashElement(&(hash_eles[hash_itr]), + hash_map->partitionKeyDataType, + hash_map->partitionKey->dim1, + rootPartitionOid, + hash_map->partitionKey, + partition_tuple, + RelationGetDescr(pg_partition)); + } else { + BuildElementForPartKeyExpr(&(hash_eles[hash_itr]), partition_tuple, RelationGetDescr(pg_partition), + partkeystr, PART_STRATEGY_HASH); + } + hash_itr++; } @@ -1762,6 +1878,8 @@ static void buildRangePartitionMap(Relation relation, Form_pg_partition partitio /* iterate partition tuples, build RangeElement for per partition tuple */ range_itr = 0; + Datum val = 0; + char* partkeystr = CheckPartExprKey(partitioned_tuple, pg_partition); foreach (tuple_cell, partition_list) { partition_tuple = (HeapTuple)lfirst(tuple_cell); partition_form = (Form_pg_partition)GETSTRUCT(partition_tuple); @@ -1776,15 +1894,20 @@ static void buildRangePartitionMap(Relation relation, Form_pg_partition partitio errmsg("Fail to build partitionmap for partitioned table \"%u\"", partition_form->parentid), errdetail("Incorrect partition strategy for partition %u", HeapTupleGetOid(partition_tuple)))); } + if (!partkeystr || (pg_strcasecmp(partkeystr, "") == 0)) { + BuildRangeElement(&(range_eles[range_itr]), + range_map->partitionKeyDataType, + range_map->partitionKey->dim1, + rootPartitionOid, + range_map->partitionKey, + partition_tuple, + RelationGetDescr(pg_partition), + partition_form->partstrategy == PART_STRATEGY_INTERVAL); + } else { + BuildElementForPartKeyExpr(&(range_eles[range_itr]), partition_tuple, RelationGetDescr(pg_partition), + partkeystr, PART_STRATEGY_RANGE); + } - BuildRangeElement(&(range_eles[range_itr]), - range_map->partitionKeyDataType, - range_map->partitionKey->dim1, - rootPartitionOid, - range_map->partitionKey, - partition_tuple, - RelationGetDescr(pg_partition), - partition_form->partstrategy == PART_STRATEGY_INTERVAL); range_itr++; } @@ -1802,6 +1925,30 @@ static void buildRangePartitionMap(Relation relation, Form_pg_partition partitio partitionMapDestroyRangeArray(range_eles, range_map->rangeElementsNum); } +Const* transformDatum2ConstForPartKeyExpr(PartitionMap* partMap, Datum datumValue, bool isnull, Const* cnst) +{ + Const* boundary = NULL; + if (partMap->type == PART_TYPE_RANGE) + boundary = ((RangePartitionMap*)partMap)->rangeElements[0].boundary[0]; + else if (partMap->type == PART_TYPE_LIST) + boundary = ((ListPartitionMap*)partMap)->listElements[0].boundary[0]; + else if (partMap->type == PART_TYPE_HASH) + boundary = ((HashPartitionMap*)partMap)->hashElements[0].boundary[0]; + else + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("The PartitionType %d is not supported for expression key partition", partMap->type))); + cnst->xpr.type = T_Const; + cnst->consttype = boundary->consttype; + cnst->consttypmod = boundary->consttypmod; + cnst->constcollid = boundary->constcollid; + cnst->constlen = boundary->constlen; + cnst->constvalue = isnull ? 0 : datumValue; + cnst->constisnull = isnull; + cnst->constbyval = boundary->constbyval; + cnst->location = -1; /* "unknown" */ + cnst->ismaxvalue = false; + return cnst; +} + /* * @@GaussDB@@ * Brief : diff --git a/src/gausskernel/optimizer/commands/tablecmds.cpp b/src/gausskernel/optimizer/commands/tablecmds.cpp index 79fd3a2f8..16419c76a 100644 --- a/src/gausskernel/optimizer/commands/tablecmds.cpp +++ b/src/gausskernel/optimizer/commands/tablecmds.cpp @@ -1913,6 +1913,53 @@ static List *GetSubpPartitionDefList(PartitionState *partTableState, ListCell *c return subPartitionList; } +void UpdatePartKeyExpr(Relation rel, PartitionState *partTableState, Oid partOid) +{ + ParseState* pstate = NULL; + RangeTblEntry* rte = NULL; + HeapTuple partTuple = NULL; + pstate = make_parsestate(NULL); + rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true); + addRTEtoQuery(pstate, rte, true, true, true); + Relation pgPartitionRel = heap_open(PartitionRelationId, RowExclusiveLock); + if (OidIsValid(partOid)) + partTuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partOid)); + else + partTuple = searchPgPartitionByParentIdCopy(PART_OBJ_TYPE_PARTED_TABLE, rel->rd_id); + if (!partTuple) + ereport(ERROR,(errcode(ERRCODE_PARTITION_ERROR),errmsg("The partition can't be found"))); + bool isnull = false; + Datum val = fastgetattr(partTuple, Anum_pg_partition_partkeyexpr, RelationGetDescr(pgPartitionRel), &isnull); + if (isnull) { + if (OidIsValid(partOid)) + ReleaseSysCache(partTuple); + else + heap_freetuple(partTuple); + heap_close(pgPartitionRel, RowExclusiveLock); + return; + } + // Oid* partitionKeyDataType = NULL; + Node* expr = transformExpr(pstate, (Node*)(linitial(partTableState->partitionKey))); + assign_expr_collations(pstate, expr); + bool nulls[Natts_pg_partition] = {false}; + bool replaces[Natts_pg_partition] = {false}; + Datum values[Natts_pg_partition] = {0}; + replaces[Anum_pg_partition_partkeyexpr - 1] = true; + char* partkeyexpr = nodeToString(expr); + values[Anum_pg_partition_partkeyexpr - 1] = partkeyexpr ? CStringGetTextDatum(partkeyexpr) : CStringGetTextDatum(""); + HeapTuple new_tuple = heap_modify_tuple(partTuple, RelationGetDescr(pgPartitionRel), values, nulls, replaces); + simple_heap_update(pgPartitionRel, &new_tuple->t_self, new_tuple); + CatalogUpdateIndexes(pgPartitionRel, new_tuple); + if (OidIsValid(partOid)) + ReleaseSysCache(partTuple); + else + heap_freetuple(partTuple); + heap_freetuple_ext(new_tuple); + if (pgPartitionRel) { + heap_close(pgPartitionRel, RowExclusiveLock); + } +} + /* ---------------------------------------------------------------- * DefineRelation * Creates a new relation. @@ -2515,15 +2562,17 @@ Oid DefineRelation(CreateStmt* stmt, char relkind, Oid ownerId, bool isCTAS) ColumnRef *partitionKeyRef = (ColumnRef *)linitial(stmt->partTableState->partitionKey); ColumnRef *subPartitionKeyRef = (ColumnRef *)linitial(stmt->partTableState->subPartitionState->partitionKey); - char *partitonKeyName = ((Value *)linitial(partitionKeyRef->fields))->val.str; - char *subPartitonKeyName = ((Value *)linitial(subPartitionKeyRef->fields))->val.str; - if (!strcmp(partitonKeyName, subPartitonKeyName)) { - ereport( - ERROR, - (errmodule(MOD_COMMAND), errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("The two partition keys of a subpartition partition table are the same."), errdetail("N/A"), - errcause("The two partition keys of a subpartition partition table cannot be the same."), - erraction("Partition keys cannot be the same column."))); + if (IsA(partitionKeyRef,ColumnRef) && IsA(subPartitionKeyRef,ColumnRef)) { + char *partitonKeyName = ((Value *)linitial(partitionKeyRef->fields))->val.str; + char *subPartitonKeyName = ((Value *)linitial(subPartitionKeyRef->fields))->val.str; + if (!strcmp(partitonKeyName, subPartitonKeyName)) { + ereport( + ERROR, + (errmodule(MOD_COMMAND), errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("The two partition keys of a subpartition partition table are the same."), errdetail("N/A"), + errcause("The two partition keys of a subpartition partition table cannot be the same."), + erraction("Partition keys cannot be the same column."))); + } } foreach (cell, stmt->partTableState->partitionList) { stmt->partTableState->subPartitionState->partitionList = @@ -2890,6 +2939,20 @@ Oid DefineRelation(CreateStmt* stmt, char relkind, Oid ownerId, bool isCTAS) ereport(DEBUG1, (errmsg("Define relation <%s.%s>, reloid: %u, relfilenode: %u", stmt->relation->schemaname, stmt->relation->relname, relationId, rel->rd_node.relNode))); + + if (stmt->partTableState) { + UpdatePartKeyExpr(rel, stmt->partTableState, InvalidOid); + if (stmt->partTableState->subPartitionState) { + List* partitionList = relationGetPartitionList(rel, NoLock); + ListCell* cell = NULL; + foreach (cell, partitionList) { + Partition partition = (Partition)(lfirst(cell)); + UpdatePartKeyExpr(rel, stmt->partTableState->subPartitionState, partition->pd_id); + } + releasePartitionList(rel, &partitionList, NoLock); + } + CommandCounterIncrement(); + } /* * Now add any newly specified column default and generation expressions * to the new relation. These are passed to us in the form of raw @@ -19173,6 +19236,7 @@ void checkPartNotInUse(Partition part, const char* stmt) } } +extern Node* GetColumnRef(Node* key, bool* isExpr); /* * @@GaussDB@@ * Target : data partition @@ -19201,9 +19265,13 @@ List* GetPartitionkeyPos(List* partitionkeys, List* schema) errno_t rc = EOK; rc = memset_s(is_exist, len * sizeof(bool), 0, len * sizeof(bool)); securec_check(rc, "\0", "\0"); - + bool isExpr = false; foreach (partitionkey_cell, partitionkeys) { - ColumnRef* partitionkey_ref = (ColumnRef*)lfirst(partitionkey_cell); + ColumnRef* partitionkey_ref = (ColumnRef*)GetColumnRef((Node*)lfirst(partitionkey_cell),&isExpr); + if (!partitionkey_ref) + ereport(ERROR,(errcode(ERRCODE_UNDEFINED_COLUMN),(errmsg("The partition key doesn't have any column.")))); + if (isExpr && partitionkeys->length > 1) + ereport(ERROR,(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),(errmsg("The multi partition expr keys are not supported.")))); char* partitonkey_name = ((Value*)linitial(partitionkey_ref->fields))->val.str; foreach (schema_cell, schema) { diff --git a/src/gausskernel/optimizer/util/pruning.cpp b/src/gausskernel/optimizer/util/pruning.cpp index 802454ff6..ffd03e3bb 100644 --- a/src/gausskernel/optimizer/util/pruning.cpp +++ b/src/gausskernel/optimizer/util/pruning.cpp @@ -508,12 +508,19 @@ PruningResult* partitionPruningForExpr(PlannerInfo* root, RangeTblEntry* rte, Re context->GetPartitionMap = GetRelPartitionMap; context->pruningType = PruningPartition; - if (rel->partMap != NULL && (rel->partMap->type == PART_TYPE_LIST || rel->partMap->type == PART_TYPE_HASH)) { - // for List/Hash partitioned table - result = partitionEqualPruningWalker(rel->partMap->type, expr, context); + bool isnull = PartExprKeyIsNull(rel, NULL); + if (isnull) { + if (rel->partMap != NULL && (rel->partMap->type == PART_TYPE_LIST || rel->partMap->type == PART_TYPE_HASH)) { + // for List/Hash partitioned table + result = partitionEqualPruningWalker(rel->partMap->type, expr, context); + } else { + // for Range/Interval partitioned table + result = partitionPruningWalker(expr, context); + } } else { - // for Range/Interval partitioned table - result = partitionPruningWalker(expr, context); + result = makeNode(PruningResult); + result->state = PRUNING_RESULT_FULL; + result->isPbeSinlePartition = false; } if (result->exprPart != NULL || result->paramArg != NULL) { diff --git a/src/gausskernel/runtime/executor/nodeModifyTable.cpp b/src/gausskernel/runtime/executor/nodeModifyTable.cpp index 5de1dd492..82ad8da0c 100644 --- a/src/gausskernel/runtime/executor/nodeModifyTable.cpp +++ b/src/gausskernel/runtime/executor/nodeModifyTable.cpp @@ -1015,6 +1015,7 @@ static Oid ExecUpsert(ModifyTableState* state, TupleTableSlot* slot, TupleTableS return newid; } +extern Tuple ComputePartKeyExprTuple(Relation rel, EState *estate, TupleTableSlot *slot, Tuple oldtuple, Relation partRel); /* ---------------------------------------------------------------- * ExecInsert * @@ -1342,8 +1343,11 @@ TupleTableSlot* ExecInsertT(ModifyTableState* state, TupleTableSlot* slot, Tuple case PARTTYPE_PARTITIONED_RELATION: { /* get partititon oid for insert the record */ - partition_id = - heapTupleGetPartitionId(result_relation_desc, tuple, false, estate->es_plannedstmt->hasIgnore); + Tuple newtuple = ComputePartKeyExprTuple(result_relation_desc, estate, slot, tuple, NULL); + if (newtuple) + partition_id = heapTupleGetPartitionId(result_relation_desc, newtuple, false, estate->es_plannedstmt->hasIgnore); + else + partition_id = heapTupleGetPartitionId(result_relation_desc, tuple, false, estate->es_plannedstmt->hasIgnore); /* if cannot find valid partition oid and sql has keyword ignore, return and don't insert */ if (estate->es_plannedstmt->hasIgnore && partition_id == InvalidOid) { return NULL; @@ -1400,8 +1404,11 @@ TupleTableSlot* ExecInsertT(ModifyTableState* state, TupleTableSlot* slot, Tuple Partition subPart = NULL; /* get partititon oid for insert the record */ - partitionId = heapTupleGetPartitionId(result_relation_desc, tuple, false, - estate->es_plannedstmt->hasIgnore); + Tuple newtuple = ComputePartKeyExprTuple(result_relation_desc, estate, slot, tuple, NULL); + if (newtuple) + partitionId = heapTupleGetPartitionId(result_relation_desc, newtuple, false, estate->es_plannedstmt->hasIgnore); + else + partitionId = heapTupleGetPartitionId(result_relation_desc, tuple, false, estate->es_plannedstmt->hasIgnore); if (estate->es_plannedstmt->hasIgnore && partitionId == InvalidOid) { return NULL; } @@ -1418,7 +1425,11 @@ TupleTableSlot* ExecInsertT(ModifyTableState* state, TupleTableSlot* slot, Tuple } /* get subpartititon oid for insert the record */ - subPartitionId = heapTupleGetPartitionId(partRel, tuple, false, estate->es_plannedstmt->hasIgnore); + Tuple newsubtuple = ComputePartKeyExprTuple(result_relation_desc, estate, slot, tuple, partRel); + if (newsubtuple) + subPartitionId = heapTupleGetPartitionId(partRel, newsubtuple, false, estate->es_plannedstmt->hasIgnore); + else + subPartitionId = heapTupleGetPartitionId(partRel, tuple, false, estate->es_plannedstmt->hasIgnore); if (estate->es_plannedstmt->hasIgnore && subPartitionId == InvalidOid) { return NULL; } @@ -2407,15 +2418,25 @@ lreplace: row_movement = false; new_partId = oldPartitionOid; } else { - partitionRoutingForTuple(result_relation_desc, tuple, u_sess->exec_cxt.route, can_ignore); + Tuple newtuple = ComputePartKeyExprTuple(result_relation_desc, estate, slot, tuple, NULL); + if (newtuple) { + partitionRoutingForTuple(result_relation_desc, newtuple, u_sess->exec_cxt.route, can_ignore); + } else { + partitionRoutingForTuple(result_relation_desc, tuple, u_sess->exec_cxt.route, can_ignore); + } if (u_sess->exec_cxt.route->fileExist) { new_partId = u_sess->exec_cxt.route->partitionId; if (RelationIsSubPartitioned(result_relation_desc)) { Partition part = partitionOpen(result_relation_desc, new_partId, RowExclusiveLock); Relation partRel = partitionGetRelation(result_relation_desc, part); + Tuple newsubtuple = ComputePartKeyExprTuple(result_relation_desc, estate, slot, tuple, partRel); + if (newsubtuple) { + partitionRoutingForTuple(partRel, newsubtuple, u_sess->exec_cxt.route, can_ignore); + } else { + partitionRoutingForTuple(partRel, tuple, u_sess->exec_cxt.route, can_ignore); + } - partitionRoutingForTuple(partRel, tuple, u_sess->exec_cxt.route, can_ignore); if (u_sess->exec_cxt.route->fileExist) { new_partId = u_sess->exec_cxt.route->partitionId; } else { diff --git a/src/gausskernel/runtime/opfusion/opfusion_insert.cpp b/src/gausskernel/runtime/opfusion/opfusion_insert.cpp index b3d6fa5f0..c12f19b0b 100644 --- a/src/gausskernel/runtime/opfusion/opfusion_insert.cpp +++ b/src/gausskernel/runtime/opfusion/opfusion_insert.cpp @@ -181,6 +181,79 @@ void InsertFusion::refreshParameterIfNecessary() } } +extern HeapTuple searchPgPartitionByParentIdCopy(char parttype, Oid parentId); +Tuple ComputePartKeyExprTuple(Relation rel, EState *estate, TupleTableSlot *slot, Tuple oldtuple, Relation partRel) +{ + Relation pgPartition = NULL; + HeapTuple partitionedTuple = NULL; + bool isnull = false; + Datum val = 0; + char* partkeystr = ""; + Node* partkeyexpr = NULL; + Tuple newtuple = NULL; + Relation tmpRel = NULL; + pgPartition = relation_open(PartitionRelationId, AccessShareLock); + if (PointerIsValid(partRel)) + partitionedTuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(partRel->rd_id)); + else + partitionedTuple = searchPgPartitionByParentIdCopy(PART_OBJ_TYPE_PARTED_TABLE, rel->rd_id); + val = fastgetattr(partitionedTuple, Anum_pg_partition_partkeyexpr, pgPartition->rd_att, &isnull); + if (isnull) { + relation_close(pgPartition, AccessShareLock); + if (PointerIsValid(partRel)) + ReleaseSysCache(partitionedTuple); + else + heap_freetuple(partitionedTuple); + return newtuple; + } + int2vector* partitionKey = NULL; + Oid* partitionKeyDataType = NULL; + partitionKey = getPartitionKeyAttrNo( + &(partitionKeyDataType), partitionedTuple, RelationGetDescr(pgPartition), RelationGetDescr(rel)); + partkeystr = MemoryContextStrdup(LocalMyDBCacheMemCxt(), TextDatumGetCString(val)); + relation_close(pgPartition, AccessShareLock); + if (pg_strcasecmp(partkeystr, "") != 0) { + partkeyexpr = (Node*)stringToNode_skip_extern_fields(partkeystr); + } + (void)lockNextvalWalker(partkeyexpr, NULL); + ExprState *exprstate = ExecPrepareExpr((Expr *)partkeyexpr, estate); + ExprContext *econtext; + econtext = GetPerTupleExprContext(estate); + econtext->ecxt_scantuple = slot; + isnull = false; + val = 0; + val = ExecEvalExpr(exprstate, econtext, &isnull, NULL); + Const** boundary = NULL; + if (PointerIsValid(partRel)) + tmpRel = partRel; + else + tmpRel = rel; + + if (tmpRel->partMap->type == PART_TYPE_RANGE) + boundary = ((RangePartitionMap*)(tmpRel->partMap))->rangeElements[0].boundary; + else if (tmpRel->partMap->type == PART_TYPE_LIST) + boundary = ((ListPartitionMap*)(tmpRel->partMap))->listElements[0].boundary; + else if (tmpRel->partMap->type == PART_TYPE_HASH) + boundary = ((HashPartitionMap*)(tmpRel->partMap))->hashElements[0].boundary; + else + ereport(ERROR, (errcode(ERRCODE_PARTITION_ERROR), errmsg("Unsupported partition type : %d", tmpRel->partMap->type))); + + if (!isnull) + val = datumCopy(val, boundary[0]->constbyval, boundary[0]->constlen); + bool nulls[rel->rd_att->natts] = {false}; + bool replaces[rel->rd_att->natts] = {false}; + Datum values[rel->rd_att->natts] = {0}; + values[partitionKey->values[0]-1] = val; + nulls[partitionKey->values[0]-1] = isnull; + replaces[partitionKey->values[0]-1] = true; + newtuple = tableam_tops_modify_tuple(oldtuple, rel->rd_att, values, nulls, replaces); + if (PointerIsValid(partRel)) + ReleaseSysCache(partitionedTuple); + else + heap_freetuple(partitionedTuple); + return newtuple; +} + unsigned long InsertFusion::ExecInsert(Relation rel, ResultRelInfo* result_rel_info) { /******************* diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h index 7f14c5203..5f875d3e2 100644 --- a/src/include/catalog/heap.h +++ b/src/include/catalog/heap.h @@ -146,15 +146,18 @@ extern void heapDropPartitionToastList(List* toastList); extern void heapDropPartitionList(Relation rel, List* partitionList); extern Oid heapAddRangePartition(Relation pgPartRel, Oid partTableOid, Oid partTablespace, Oid bucketOid, RangePartitionDefState *newPartDef, Oid ownerid, Datum reloptions, - const bool* isTimestamptz, StorageType storage_type, LOCKMODE partLockMode, int2vector* subpartition_key = NULL, bool isSubPartition = false); + const bool* isTimestamptz, StorageType storage_type, LOCKMODE partLockMode, int2vector* subpartition_key = NULL, bool isSubPartition = false, + bool partkeyexprIsNull = true); extern Oid HeapAddListPartition(Relation pgPartRel, Oid partTableOid, Oid partTablespace, Oid bucketOid, ListPartitionDefState *newPartDef, Oid ownerid, Datum reloptions, - const bool* isTimestamptz, StorageType storage_type, int2vector* subpartition_key = NULL, bool isSubPartition = false); + const bool* isTimestamptz, StorageType storage_type, int2vector* subpartition_key = NULL, bool isSubPartition = false, + bool partkeyexprIsNull = true); extern Oid HeapAddHashPartition(Relation pgPartRel, Oid partTableOid, Oid partTablespace, Oid bucketOid, HashPartitionDefState *newPartDef, Oid ownerid, Datum reloptions, - const bool* isTimestamptz, StorageType storage_type, int2vector* subpartition_key = NULL, bool isSubPartition = false); + const bool* isTimestamptz, StorageType storage_type, int2vector* subpartition_key = NULL, bool isSubPartition = false, + bool partkeyexprIsNull = true); extern Node *MakeDefaultSubpartition(PartitionState *partitionState, Node *partitionDefState); extern List *addNewSubPartitionTuplesForPartition(Relation pgPartRel, Oid partTableOid, Oid partTablespace, Oid bucketOid, Oid ownerid, Datum reloptions, const bool *isTimestamptz, StorageType storage_type, @@ -164,7 +167,7 @@ extern Oid GetPartTablespaceOidForSubpartition(Oid reltablespace, const char* pa extern void heapDropPartitionIndex(Relation parentIndex, Oid partIndexId); extern void addNewPartitionTuple(Relation pg_part_desc, Partition new_part_desc, int2vector* pkey, oidvector *intablespace, - Datum interval, Datum maxValues, Datum transitionPoint, Datum reloptions); + Datum interval, Datum maxValues, Datum transitionPoint, Datum reloptions, bool partkeyexprIsNull = true); extern void heap_truncate_one_part(Relation rel , Oid partOid); extern Oid heapTupleGetPartitionId(Relation rel, void *tuple, bool isDDL = false, bool canIgnore = false); diff --git a/src/include/catalog/pg_partition.h b/src/include/catalog/pg_partition.h index 97f50e9a9..392e16b29 100644 --- a/src/include/catalog/pg_partition.h +++ b/src/include/catalog/pg_partition.h @@ -65,6 +65,7 @@ CATALOG(pg_partition,9016) BKI_ROWTYPE_OID(3790) BKI_SCHEMA_MACRO TransactionId relfrozenxid64; TransactionId relminmxid; /* all multixacts in this rel are >= this. * this is really a MultiXactId */ + text partkeyexpr; } FormData_pg_partition; /* Size of fixed part of pg_partition tuples, not counting var-length fields */ #define PARTITION_TUPLE_SIZE \ @@ -95,7 +96,7 @@ typedef FormData_pg_partition *Form_pg_partition; #define PART_OBJ_TYPE_TABLE_SUB_PARTITION 's' #define PART_OBJ_TYPE_INDEX_PARTITION 'x' -#define Natts_pg_partition 29 +#define Natts_pg_partition 30 #define Anum_pg_partition_relname 1 #define Anum_pg_partition_parttype 2 #define Anum_pg_partition_parentid 3 @@ -125,5 +126,6 @@ typedef FormData_pg_partition *Form_pg_partition; #define Anum_pg_partition_reloptions 27 #define Anum_pg_partition_relfrozenxid64 28 #define Anum_pg_partition_relminmxid 29 +#define Anum_pg_partition_partkeyexpr 30 #endif/*PG_PARTITION_H*/ diff --git a/src/include/catalog/pg_partition_fn.h b/src/include/catalog/pg_partition_fn.h index a4550d66e..da7378f3f 100644 --- a/src/include/catalog/pg_partition_fn.h +++ b/src/include/catalog/pg_partition_fn.h @@ -74,7 +74,7 @@ typedef void (*PartitionNameGetPartidCallback) (Oid partitioned_relation, const Oid oldPartId, char partition_type, void *callback_arg, LOCKMODE callbackobj_lockMode); extern void insertPartitionEntry(Relation pg_partition_desc, Partition new_part_desc, Oid new_part_id, int2vector *pkey, const oidvector *inttablespace, Datum interval, - Datum maxValues, Datum transitionPoint, Datum reloptions, char parttype); + Datum maxValues, Datum transitionPoint, Datum reloptions, char parttype, bool partkeyexprIsNull = true); extern bool isPartitionedObject(Oid relid, char relkind, bool missing_ok); extern bool isSubPartitionedObject(Oid relid, char relkind, bool missing_ok); extern bool isPartitionObject(Oid partid, char partkind, bool missing_ok); @@ -125,6 +125,7 @@ extern void releasePartitionList(Relation relation, List** partList, LOCKMODE l extern void releaseSubPartitionList(Relation relation, List** partList, LOCKMODE lockmode); extern void releasePartitionOidList(List** partList); extern void ReleaseSubPartitionOidList(List** partList); +extern bool PartExprKeyIsNull(Relation rel, Relation partitionRel); #endif diff --git a/src/include/utils/partitionmap.h b/src/include/utils/partitionmap.h index 061fd6c06..983527e8d 100644 --- a/src/include/utils/partitionmap.h +++ b/src/include/utils/partitionmap.h @@ -113,6 +113,7 @@ extern int getNumberOfListPartitions(Relation rel); extern int getNumberOfHashPartitions(Relation rel); extern int getNumberOfPartitions(Relation rel); extern Const* transformDatum2Const(TupleDesc tupledesc, int16 attnum, Datum datumValue, bool isnull, Const* cnst); +Const* transformDatum2ConstForPartKeyExpr(PartitionMap* partMap, Datum datumValue, bool isnull, Const* cnst); extern int2vector* getPartitionKeyAttrNo( Oid** typeOids, HeapTuple pg_part_tup, TupleDesc tupledsc, TupleDesc rel_tupledsc); diff --git a/src/include/utils/partitionmap_gs.h b/src/include/utils/partitionmap_gs.h index 9e938c9d9..4fb196ae6 100644 --- a/src/include/utils/partitionmap_gs.h +++ b/src/include/utils/partitionmap_gs.h @@ -165,8 +165,14 @@ typedef struct HashPartitionMap { column_raw = (is_ustore) \ ? UHeapFastGetAttr((UHeapTuple)(tuple), partkey_column->values[i], tuple_desc, &isnull) \ : fastgetattr((HeapTuple)(tuple), partkey_column->values[i], tuple_desc, &isnull); \ - values[i] = \ - transformDatum2Const((rel)->rd_att, partkey_column->values[i], column_raw, isnull, &consts[i]); \ + bool isNull = PartExprKeyIsNull(rel, NULL); \ + if (isNull) { \ + values[i] = \ + transformDatum2Const((rel)->rd_att, partkey_column->values[i], column_raw, isnull, &consts[i]); \ + } else { \ + values[i] = \ + transformDatum2ConstForPartKeyExpr((rel)->partMap, column_raw, isnull, &consts[i]); \ + } \ } \ if (PartitionMapIsInterval((rel)->partMap) && values[0]->constisnull) { \ if (canIgnore) { \ diff --git a/src/test/regress/input/partition_expr_key.source b/src/test/regress/input/partition_expr_key.source new file mode 100644 index 000000000..d35e74cfd --- /dev/null +++ b/src/test/regress/input/partition_expr_key.source @@ -0,0 +1,250 @@ +drop database if exists part_expr_key_db; +create database part_expr_key_db; +\c part_expr_key_db +--test error sql +create table testrangepart(a int, b int) partition by range(a/2) (partition p0 values less than(100),partition p1 values less than(200)); +create table testlistpart(a int, b int) partition by list(a/2) (partition p0 values(100,200),partition p1 values(300,400)); +create table testhashpart(a int, b int) partition by hash(a/2) (partition p0 ,partition p1); +create table testrangepart(a int, b int) partition by range(int4mul(a,2)) (partition p0 values less than(100),partition p1 values less than(200)); +create table testlistpart(a int, b int) partition by list(int4mul(a,2)) (partition p0 values(100,200),partition p1 values(300,400)); +create table testhashpart(a int, b int) partition by hash(int4mul(a,2)) (partition p0 ,partition p1); +create table testrangepart(a int, b int) partition by range(a,b*2) (partition p0 values less than(100,1000),partition p1 values less than(200,2000)); +create table testrangepart(a int, b int) partition by range(a*2,b) (partition p0 values less than(100,1000),partition p1 values less than(200,2000)); +CREATE TABLE testrangepart(a date) PARTITION BY RANGE (a*2) INTERVAL ('1 month') +( + PARTITION p0 VALUES LESS THAN ('2020-03-01'), + PARTITION p1 VALUES LESS THAN ('2020-04-01') +); + +--test partkeyexpr in pg_partition +create table testtmp1(a int, b int) partition by range(a) subpartition by range(b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000) (subpartition p10 values less than(200)) +); +select count(partkeyexpr) from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testtmp1')); +select count(partkeyexpr) from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testtmp1')); +select count(partkeyexpr) from pg_partition where (parttype = 's') and (parentid in (select oid from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testtmp1')))); +create table testtmp2(a int, b int) partition by range(a) subpartition by range(b*2) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +select count(partkeyexpr) from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testtmp2')); +select partkeyexpr from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testtmp2')); +select count(partkeyexpr) from pg_partition where (parttype = 's') and (parentid in (select oid from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testtmp2')))); +create table testtmp3(a int, b int) partition by range(a*2) subpartition by range(b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testtmp3')); +select count(partkeyexpr) from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testtmp3')); +select count(partkeyexpr) from pg_partition where (parttype = 's') and (parentid in (select oid from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testtmp3')))); +drop table testtmp1,testtmp2,testtmp3; + +create table testtab(a int, b int); +--test range partition +create table testrangepart(a int, b int) partition by range(abs(a*2)) +( + partition p0 values less than(100), + partition p1 values less than(200) +); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testrangepart')); +insert into testrangepart values(-51,1),(49,2); +insert into testrangepart values(-101,1); +select * from testrangepart partition(p0); +select * from testrangepart partition(p1); +select * from testrangepart where a = -51; +update testrangepart set a = -48 where a = -51; +select * from testrangepart partition(p0); +select * from testrangepart partition(p1); +delete from testrangepart where a = -48 or a = 49; +select * from testrangepart; +insert into testtab values(-51,1),(51,2); +insert into testrangepart select * from testtab; +select * from testrangepart partition(p0); +select * from testrangepart partition(p1); +delete from testtab; + +--test list partition +create table testlistpart(a int, b int) partition by list(abs(a*2)) +( + partition p0 values(100,200), + partition p1 values(300,400) +); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testlistpart')); +insert into testlistpart values(-50,1),(200,2); +insert into testlistpart values(300,1); +select * from testlistpart partition(p0); +select * from testlistpart partition(p1); +select * from testlistpart where a = -50; +update testlistpart set a = -150 where a = -50; +select * from testlistpart partition(p0); +select * from testlistpart partition(p1); +delete from testlistpart where a = -150 or a = 200; +select * from testlistpart; +insert into testtab values(-50,1),(200,2); +insert into testlistpart select * from testtab; +select * from testlistpart partition(p0); +select * from testlistpart partition(p1); +delete from testtab; + +--test hash partition +create table testhashpart(a int, b int) partition by hash(abs(a*2)) (partition p0,partition p1); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testhashpart')); +insert into testhashpart values(-51,1),(50,2); +select * from testhashpart partition(p0); +select * from testhashpart partition(p1); +select * from testhashpart where a = -51; +update testhashpart set a = -49 where a = -51; +select * from testhashpart partition(p0); +select * from testhashpart partition(p1); +delete from testhashpart where a = 50 or a = -49; +select * from testhashpart; +insert into testtab values(-51,1),(50,2); +insert into testhashpart select * from testtab; +select * from testhashpart partition(p0); +select * from testhashpart partition(p1); +delete from testtab; + +--test range subpartition +create table testrangesubpart(a int, b int) partition by range(a+b) subpartition by range(a-b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testrangesubpart')); +select partkeyexpr from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testrangesubpart')); +insert into testrangesubpart values(500,401),(600,450); +insert into testrangesubpart values(600,299); +select * from testrangesubpart partition(p0); +select * from testrangesubpart partition(p1); +select * from testrangesubpart where a = 500; +update testrangesubpart set a = 400 where a = 500; +select * from testrangesubpart partition(p0); +select * from testrangesubpart partition(p1); +delete from testrangesubpart where a = 400 or a = 600; +select * from testrangesubpart; +insert into testtab values(500,401),(600,450); +insert into testrangesubpart select * from testtab; +select * from testrangesubpart partition(p0); +select * from testrangesubpart partition(p1); +delete from testtab; + +--test list subpartition +create table testlistsubpart(a int, b int) partition by range(abs(a*2)) subpartition by list(abs(b*2)) (partition p0 values less than(1000)(subpartition p00 values(100)),partition p1 values less than(2000) (subpartition p10 values(200))); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testlistsubpart')); +select partkeyexpr from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testlistsubpart')); +insert into testlistsubpart values(-499,-50),(600,-100); +insert into testlistsubpart values(600,200); +select * from testlistsubpart partition(p0); +select * from testlistsubpart partition(p1); +select * from testlistsubpart where a = -499; +update testlistsubpart set a = 499 where a = -499; +select * from testlistsubpart partition(p0); +select * from testlistsubpart partition(p1); +delete from testlistsubpart where a = 499 or a = 600; +select * from testlistsubpart; +insert into testtab values(-499,-50),(600,-100); +insert into testlistsubpart select * from testtab; +select * from testlistsubpart partition(p0); +select * from testlistsubpart partition(p1); +delete from testtab; + +--test hash subpartition +create table testhashsubpart(a int, b int) partition by range(a) subpartition by hash(abs(b*2)) (partition p0 values less than(1000)(subpartition p00,subpartition p01),partition p1 values less than(2000) (subpartition p10,subpartition p11)); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testhashsubpart')); +select partkeyexpr from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testhashsubpart')); +insert into testhashsubpart values(500,-400),(1600,450); +select * from testhashsubpart partition(p0); +select * from testhashsubpart partition(p1); +select * from testhashsubpart where a = 500; +update testhashsubpart set a = 400 where a = 500; +select * from testhashsubpart partition(p0); +select * from testhashsubpart partition(p1); +delete from testhashsubpart where a = 400 or a = 1600; +select * from testhashsubpart; +insert into testtab values(500,-400),(1600,450); +insert into testhashsubpart select * from testtab; +select * from testhashsubpart partition(p0); +select * from testhashsubpart partition(p1); +delete from testtab; + + +--test pg_get_tabledef and pg_dump +create table testnormalsubpart(a int, b int) partition by range(a) subpartition by range(b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +select pg_get_tabledef('testrangepart'); +select pg_get_tabledef('testlistpart'); +select pg_get_tabledef('testhashpart'); +select pg_get_tabledef('testrangesubpart'); +select pg_get_tabledef('testlistsubpart'); +select pg_get_tabledef('testhashsubpart'); +select pg_get_tabledef('testnormalsubpart'); + +drop table testrangepart; +drop table testlistpart; +drop table testhashpart; +drop table testrangesubpart; +drop table testlistsubpart; +drop table testhashsubpart; +drop table testnormalsubpart; +drop table testtab; +\c regression +drop database if exists part_expr_key_db; +create database part_expr_key_db; +\c part_expr_key_db +create table testrangepart(a int, b int) partition by range(abs(a*2)) +( + partition p0 values less than(100), + partition p1 values less than(200) +); +create table testlistpart(a int, b int) partition by list(abs(a*2)) +( + partition p0 values(100,200), + partition p1 values(300,400) +); +create table testhashpart(a int, b int) partition by hash(abs(a*2)) (partition p0,partition p1); +create table testrangesubpart(a int, b int) partition by range(a+b) subpartition by range(a-b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +create table testlistsubpart(a int, b int) partition by range(abs(a*2)) subpartition by list(abs(b*2)) +( + partition p0 values less than(1000)(subpartition p00 values(100)), + partition p1 values less than(2000) (subpartition p10 values(200)) +); +create table testhashsubpart(a int, b int) partition by range(a) subpartition by hash(abs(b*2)) +( + partition p0 values less than(1000)(subpartition p00,subpartition p01), + partition p1 values less than(2000) (subpartition p10,subpartition p11) +); +create table testnormalsubpart(a int, b int) partition by range(a) subpartition by range(b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +\! @abs_bindir@/gs_dump part_expr_key_db -p @portstring@ -f @abs_bindir@/gs_dump_partition_expr.sql >/dev/null 2>&1; echo $? +drop table testrangepart; +drop table testlistpart; +drop table testhashpart; +drop table testrangesubpart; +drop table testlistsubpart; +drop table testhashsubpart; +drop table testnormalsubpart; +\! @abs_bindir@/gsql -p @portstring@ -d part_expr_key_db -f @abs_bindir@/gs_dump_partition_expr.sql >/dev/null 2>&1; echo $? +select pg_get_tabledef('testrangepart'); +select pg_get_tabledef('testlistpart'); +select pg_get_tabledef('testhashpart'); +select pg_get_tabledef('testrangesubpart'); +select pg_get_tabledef('testlistsubpart'); +select pg_get_tabledef('testhashsubpart'); +select pg_get_tabledef('testnormalsubpart'); +\c regression + +drop database if exists part_expr_key_db; \ No newline at end of file diff --git a/src/test/regress/output/partition_expr_key.source b/src/test/regress/output/partition_expr_key.source new file mode 100644 index 000000000..0eec08c4e --- /dev/null +++ b/src/test/regress/output/partition_expr_key.source @@ -0,0 +1,859 @@ +drop database if exists part_expr_key_db; +NOTICE: database "part_expr_key_db" does not exist, skipping +create database part_expr_key_db; +\c part_expr_key_db +--test error sql +create table testrangepart(a int, b int) partition by range(a/2) (partition p0 values less than(100),partition p1 values less than(200)); +ERROR: The / operator is not supported for Partition Expr +create table testlistpart(a int, b int) partition by list(a/2) (partition p0 values(100,200),partition p1 values(300,400)); +ERROR: The / operator is not supported for Partition Expr +create table testhashpart(a int, b int) partition by hash(a/2) (partition p0 ,partition p1); +ERROR: The / operator is not supported for Partition Expr +create table testrangepart(a int, b int) partition by range(int4mul(a,2)) (partition p0 values less than(100),partition p1 values less than(200)); +ERROR: The int4mul func is not supported for Partition Expr +create table testlistpart(a int, b int) partition by list(int4mul(a,2)) (partition p0 values(100,200),partition p1 values(300,400)); +ERROR: The int4mul func is not supported for Partition Expr +create table testhashpart(a int, b int) partition by hash(int4mul(a,2)) (partition p0 ,partition p1); +ERROR: The int4mul func is not supported for Partition Expr +create table testrangepart(a int, b int) partition by range(a,b*2) (partition p0 values less than(100,1000),partition p1 values less than(200,2000)); +ERROR: The multi partition expr keys are not supported. +create table testrangepart(a int, b int) partition by range(a*2,b) (partition p0 values less than(100,1000),partition p1 values less than(200,2000)); +ERROR: The multi partition expr keys are not supported. +CREATE TABLE testrangepart(a date) PARTITION BY RANGE (a*2) INTERVAL ('1 month') +( + PARTITION p0 VALUES LESS THAN ('2020-03-01'), + PARTITION p1 VALUES LESS THAN ('2020-04-01') +); +ERROR: The partition expr key doesn't supprt value or interval partition +--test partkeyexpr in pg_partition +create table testtmp1(a int, b int) partition by range(a) subpartition by range(b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000) (subpartition p10 values less than(200)) +); +select count(partkeyexpr) from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testtmp1')); + count +------- + 0 +(1 row) + +select count(partkeyexpr) from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testtmp1')); + count +------- + 0 +(1 row) + +select count(partkeyexpr) from pg_partition where (parttype = 's') and (parentid in (select oid from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testtmp1')))); + count +------- + 0 +(1 row) + +create table testtmp2(a int, b int) partition by range(a) subpartition by range(b*2) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +select count(partkeyexpr) from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testtmp2')); + count +------- + 0 +(1 row) + +select partkeyexpr from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testtmp2')); + partkeyexpr +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {OPEXPR :opno 514 :opfuncid 141 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 2 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 2 :location 80} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :ismaxvalue false :location 82 :constvalue 4 [ 2 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno -1 :is_open false :found false :not_found false :null_open false :null_fetch false}) :location 81} + {OPEXPR :opno 514 :opfuncid 141 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 2 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 2 :location 80} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :ismaxvalue false :location 82 :constvalue 4 [ 2 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno -1 :is_open false :found false :not_found false :null_open false :null_fetch false}) :location 81} +(2 rows) + +select count(partkeyexpr) from pg_partition where (parttype = 's') and (parentid in (select oid from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testtmp2')))); + count +------- + 0 +(1 row) + +create table testtmp3(a int, b int) partition by range(a*2) subpartition by range(b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testtmp3')); + partkeyexpr +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {OPEXPR :opno 514 :opfuncid 141 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 1 :location 55} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :ismaxvalue false :location 57 :constvalue 4 [ 2 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno -1 :is_open false :found false :not_found false :null_open false :null_fetch false}) :location 56} +(1 row) + +select count(partkeyexpr) from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testtmp3')); + count +------- + 0 +(1 row) + +select count(partkeyexpr) from pg_partition where (parttype = 's') and (parentid in (select oid from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testtmp3')))); + count +------- + 0 +(1 row) + +drop table testtmp1,testtmp2,testtmp3; +create table testtab(a int, b int); +--test range partition +create table testrangepart(a int, b int) partition by range(abs(a*2)) +( + partition p0 values less than(100), + partition p1 values less than(200) +); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testrangepart')); + partkeyexpr +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {FUNCEXPR :funcid 1397 :funcresulttype 23 :funcresulttype_orig -1 :funcretset false :funcformat 0 :funccollid 0 :inputcollid 0 :args ({OPEXPR :opno 514 :opfuncid 141 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 1 :location 64} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :ismaxvalue false :location 66 :constvalue 4 [ 2 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno -1 :is_open false :found false :not_found false :null_open false :null_fetch false}) :location 65}) :location 60 :refSynOid 0} +(1 row) + +insert into testrangepart values(-51,1),(49,2); +insert into testrangepart values(-101,1); +ERROR: inserted partition key does not map to any table partition +select * from testrangepart partition(p0); + a | b +----+--- + 49 | 2 +(1 row) + +select * from testrangepart partition(p1); + a | b +-----+--- + -51 | 1 +(1 row) + +select * from testrangepart where a = -51; + a | b +-----+--- + -51 | 1 +(1 row) + +update testrangepart set a = -48 where a = -51; +select * from testrangepart partition(p0); + a | b +-----+--- + 49 | 2 + -48 | 1 +(2 rows) + +select * from testrangepart partition(p1); + a | b +---+--- +(0 rows) + +delete from testrangepart where a = -48 or a = 49; +select * from testrangepart; + a | b +---+--- +(0 rows) + +insert into testtab values(-51,1),(51,2); +insert into testrangepart select * from testtab; +select * from testrangepart partition(p0); + a | b +---+--- +(0 rows) + +select * from testrangepart partition(p1); + a | b +-----+--- + -51 | 1 + 51 | 2 +(2 rows) + +delete from testtab; +--test list partition +create table testlistpart(a int, b int) partition by list(abs(a*2)) +( + partition p0 values(100,200), + partition p1 values(300,400) +); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testlistpart')); + partkeyexpr +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {FUNCEXPR :funcid 1397 :funcresulttype 23 :funcresulttype_orig -1 :funcretset false :funcformat 0 :funccollid 0 :inputcollid 0 :args ({OPEXPR :opno 514 :opfuncid 141 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 1 :location 62} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :ismaxvalue false :location 64 :constvalue 4 [ 2 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno -1 :is_open false :found false :not_found false :null_open false :null_fetch false}) :location 63}) :location 58 :refSynOid 0} +(1 row) + +insert into testlistpart values(-50,1),(200,2); +insert into testlistpart values(300,1); +ERROR: inserted partition key does not map to any table partition +select * from testlistpart partition(p0); + a | b +-----+--- + -50 | 1 +(1 row) + +select * from testlistpart partition(p1); + a | b +-----+--- + 200 | 2 +(1 row) + +select * from testlistpart where a = -50; + a | b +-----+--- + -50 | 1 +(1 row) + +update testlistpart set a = -150 where a = -50; +select * from testlistpart partition(p0); + a | b +---+--- +(0 rows) + +select * from testlistpart partition(p1); + a | b +------+--- + 200 | 2 + -150 | 1 +(2 rows) + +delete from testlistpart where a = -150 or a = 200; +select * from testlistpart; + a | b +---+--- +(0 rows) + +insert into testtab values(-50,1),(200,2); +insert into testlistpart select * from testtab; +select * from testlistpart partition(p0); + a | b +-----+--- + -50 | 1 +(1 row) + +select * from testlistpart partition(p1); + a | b +-----+--- + 200 | 2 +(1 row) + +delete from testtab; +--test hash partition +create table testhashpart(a int, b int) partition by hash(abs(a*2)) (partition p0,partition p1); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testhashpart')); + partkeyexpr +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {FUNCEXPR :funcid 1397 :funcresulttype 23 :funcresulttype_orig -1 :funcretset false :funcformat 0 :funccollid 0 :inputcollid 0 :args ({OPEXPR :opno 514 :opfuncid 141 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 1 :location 62} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :ismaxvalue false :location 64 :constvalue 4 [ 2 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno -1 :is_open false :found false :not_found false :null_open false :null_fetch false}) :location 63}) :location 58 :refSynOid 0} +(1 row) + +insert into testhashpart values(-51,1),(50,2); +select * from testhashpart partition(p0); + a | b +----+--- + 50 | 2 +(1 row) + +select * from testhashpart partition(p1); + a | b +-----+--- + -51 | 1 +(1 row) + +select * from testhashpart where a = -51; + a | b +-----+--- + -51 | 1 +(1 row) + +update testhashpart set a = -49 where a = -51; +select * from testhashpart partition(p0); + a | b +-----+--- + 50 | 2 + -49 | 1 +(2 rows) + +select * from testhashpart partition(p1); + a | b +---+--- +(0 rows) + +delete from testhashpart where a = 50 or a = -49; +select * from testhashpart; + a | b +---+--- +(0 rows) + +insert into testtab values(-51,1),(50,2); +insert into testhashpart select * from testtab; +select * from testhashpart partition(p0); + a | b +----+--- + 50 | 2 +(1 row) + +select * from testhashpart partition(p1); + a | b +-----+--- + -51 | 1 +(1 row) + +delete from testtab; +--test range subpartition +create table testrangesubpart(a int, b int) partition by range(a+b) subpartition by range(a-b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testrangesubpart')); + partkeyexpr +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {OPEXPR :opno 551 :opfuncid 177 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 1 :location 63} {VAR :varno 1 :varattno 2 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 2 :location 65}) :location 64} +(1 row) + +select partkeyexpr from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testrangesubpart')); + partkeyexpr +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {OPEXPR :opno 555 :opfuncid 181 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 1 :location 90} {VAR :varno 1 :varattno 2 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 2 :location 92}) :location 91} + {OPEXPR :opno 555 :opfuncid 181 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 1 :location 90} {VAR :varno 1 :varattno 2 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 2 :location 92}) :location 91} +(2 rows) + +insert into testrangesubpart values(500,401),(600,450); +insert into testrangesubpart values(600,299); +ERROR: inserted partition key does not map to any table partition +select * from testrangesubpart partition(p0); + a | b +-----+----- + 500 | 401 +(1 row) + +select * from testrangesubpart partition(p1); + a | b +-----+----- + 600 | 450 +(1 row) + +select * from testrangesubpart where a = 500; + a | b +-----+----- + 500 | 401 +(1 row) + +update testrangesubpart set a = 400 where a = 500; +select * from testrangesubpart partition(p0); + a | b +-----+----- + 400 | 401 +(1 row) + +select * from testrangesubpart partition(p1); + a | b +-----+----- + 600 | 450 +(1 row) + +delete from testrangesubpart where a = 400 or a = 600; +select * from testrangesubpart; + a | b +---+--- +(0 rows) + +insert into testtab values(500,401),(600,450); +insert into testrangesubpart select * from testtab; +select * from testrangesubpart partition(p0); + a | b +-----+----- + 500 | 401 +(1 row) + +select * from testrangesubpart partition(p1); + a | b +-----+----- + 600 | 450 +(1 row) + +delete from testtab; +--test list subpartition +create table testlistsubpart(a int, b int) partition by range(abs(a*2)) subpartition by list(abs(b*2)) (partition p0 values less than(1000)(subpartition p00 values(100)),partition p1 values less than(2000) (subpartition p10 values(200))); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testlistsubpart')); + partkeyexpr +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {FUNCEXPR :funcid 1397 :funcresulttype 23 :funcresulttype_orig -1 :funcretset false :funcformat 0 :funccollid 0 :inputcollid 0 :args ({OPEXPR :opno 514 :opfuncid 141 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 1 :location 66} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :ismaxvalue false :location 68 :constvalue 4 [ 2 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno -1 :is_open false :found false :not_found false :null_open false :null_fetch false}) :location 67}) :location 62 :refSynOid 0} +(1 row) + +select partkeyexpr from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testlistsubpart')); + partkeyexpr +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {FUNCEXPR :funcid 1397 :funcresulttype 23 :funcresulttype_orig -1 :funcretset false :funcformat 0 :funccollid 0 :inputcollid 0 :args ({OPEXPR :opno 514 :opfuncid 141 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 2 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 2 :location 97} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :ismaxvalue false :location 99 :constvalue 4 [ 2 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno -1 :is_open false :found false :not_found false :null_open false :null_fetch false}) :location 98}) :location 93 :refSynOid 0} + {FUNCEXPR :funcid 1397 :funcresulttype 23 :funcresulttype_orig -1 :funcretset false :funcformat 0 :funccollid 0 :inputcollid 0 :args ({OPEXPR :opno 514 :opfuncid 141 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 2 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 2 :location 97} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :ismaxvalue false :location 99 :constvalue 4 [ 2 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno -1 :is_open false :found false :not_found false :null_open false :null_fetch false}) :location 98}) :location 93 :refSynOid 0} +(2 rows) + +insert into testlistsubpart values(-499,-50),(600,-100); +insert into testlistsubpart values(600,200); +ERROR: inserted partition key does not map to any table partition +select * from testlistsubpart partition(p0); + a | b +------+----- + -499 | -50 +(1 row) + +select * from testlistsubpart partition(p1); + a | b +-----+------ + 600 | -100 +(1 row) + +select * from testlistsubpart where a = -499; + a | b +------+----- + -499 | -50 +(1 row) + +update testlistsubpart set a = 499 where a = -499; +select * from testlistsubpart partition(p0); + a | b +-----+----- + 499 | -50 +(1 row) + +select * from testlistsubpart partition(p1); + a | b +-----+------ + 600 | -100 +(1 row) + +delete from testlistsubpart where a = 499 or a = 600; +select * from testlistsubpart; + a | b +---+--- +(0 rows) + +insert into testtab values(-499,-50),(600,-100); +insert into testlistsubpart select * from testtab; +select * from testlistsubpart partition(p0); + a | b +------+----- + -499 | -50 +(1 row) + +select * from testlistsubpart partition(p1); + a | b +-----+------ + 600 | -100 +(1 row) + +delete from testtab; +--test hash subpartition +create table testhashsubpart(a int, b int) partition by range(a) subpartition by hash(abs(b*2)) (partition p0 values less than(1000)(subpartition p00,subpartition p01),partition p1 values less than(2000) (subpartition p10,subpartition p11)); +select partkeyexpr from pg_partition where (parttype = 'r') and (parentid in (select oid from pg_class where relname = 'testhashsubpart')); + partkeyexpr +------------- + +(1 row) + +select partkeyexpr from pg_partition where (parttype = 'p') and (parentid in (select oid from pg_class where relname = 'testhashsubpart')); + partkeyexpr +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + {FUNCEXPR :funcid 1397 :funcresulttype 23 :funcresulttype_orig -1 :funcretset false :funcformat 0 :funccollid 0 :inputcollid 0 :args ({OPEXPR :opno 514 :opfuncid 141 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 2 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 2 :location 90} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :ismaxvalue false :location 92 :constvalue 4 [ 2 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno -1 :is_open false :found false :not_found false :null_open false :null_fetch false}) :location 91}) :location 86 :refSynOid 0} + {FUNCEXPR :funcid 1397 :funcresulttype 23 :funcresulttype_orig -1 :funcretset false :funcformat 0 :funccollid 0 :inputcollid 0 :args ({OPEXPR :opno 514 :opfuncid 141 :opresulttype 23 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 2 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 2 :location 90} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :ismaxvalue false :location 92 :constvalue 4 [ 2 0 0 0 0 0 0 0 ] :cursor_data :row_count 0 :cur_dno -1 :is_open false :found false :not_found false :null_open false :null_fetch false}) :location 91}) :location 86 :refSynOid 0} +(2 rows) + +insert into testhashsubpart values(500,-400),(1600,450); +select * from testhashsubpart partition(p0); + a | b +-----+------ + 500 | -400 +(1 row) + +select * from testhashsubpart partition(p1); + a | b +------+----- + 1600 | 450 +(1 row) + +select * from testhashsubpart where a = 500; + a | b +-----+------ + 500 | -400 +(1 row) + +update testhashsubpart set a = 400 where a = 500; +select * from testhashsubpart partition(p0); + a | b +-----+------ + 400 | -400 +(1 row) + +select * from testhashsubpart partition(p1); + a | b +------+----- + 1600 | 450 +(1 row) + +delete from testhashsubpart where a = 400 or a = 1600; +select * from testhashsubpart; + a | b +---+--- +(0 rows) + +insert into testtab values(500,-400),(1600,450); +insert into testhashsubpart select * from testtab; +select * from testhashsubpart partition(p0); + a | b +-----+------ + 500 | -400 +(1 row) + +select * from testhashsubpart partition(p1); + a | b +------+----- + 1600 | 450 +(1 row) + +delete from testtab; +--test pg_get_tabledef and pg_dump +create table testnormalsubpart(a int, b int) partition by range(a) subpartition by range(b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +select pg_get_tabledef('testrangepart'); + pg_get_tabledef +---------------------------------------------------------------- + SET search_path = public; + + CREATE TABLE testrangepart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY RANGE (abs((a * 2))) + + ( + + PARTITION p0 VALUES LESS THAN (100) TABLESPACE pg_default,+ + PARTITION p1 VALUES LESS THAN (200) TABLESPACE pg_default + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +select pg_get_tabledef('testlistpart'); + pg_get_tabledef +---------------------------------------------------------- + SET search_path = public; + + CREATE TABLE testlistpart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY LIST (abs((a * 2))) + + ( + + PARTITION p0 VALUES (100,200) TABLESPACE pg_default,+ + PARTITION p1 VALUES (300,400) TABLESPACE pg_default + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +select pg_get_tabledef('testhashpart'); + pg_get_tabledef +----------------------------------------- + SET search_path = public; + + CREATE TABLE testhashpart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY HASH (abs((a * 2))) + + ( + + PARTITION p0 TABLESPACE pg_default,+ + PARTITION p1 TABLESPACE pg_default + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +select pg_get_tabledef('testrangesubpart'); + pg_get_tabledef +----------------------------------------------------------------------- + SET search_path = public; + + CREATE TABLE testrangesubpart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY RANGE ((a + b)) SUBPARTITION BY RANGE ((a - b)) + + ( + + PARTITION p0 VALUES LESS THAN (1000) TABLESPACE pg_default + + ( + + SUBPARTITION p00 VALUES LESS THAN (100) TABLESPACE pg_default+ + ), + + PARTITION p1 VALUES LESS THAN (2000) TABLESPACE pg_default + + ( + + SUBPARTITION p10 VALUES LESS THAN (200) TABLESPACE pg_default+ + ) + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +select pg_get_tabledef('testlistsubpart'); + pg_get_tabledef +----------------------------------------------------------------------- + SET search_path = public; + + CREATE TABLE testlistsubpart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY RANGE (abs((a * 2))) SUBPARTITION BY LIST (abs((b * 2)))+ + ( + + PARTITION p0 VALUES LESS THAN (1000) TABLESPACE pg_default + + ( + + SUBPARTITION p00 VALUES (100) TABLESPACE pg_default + + ), + + PARTITION p1 VALUES LESS THAN (2000) TABLESPACE pg_default + + ( + + SUBPARTITION p10 VALUES (200) TABLESPACE pg_default + + ) + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +select pg_get_tabledef('testhashsubpart'); + pg_get_tabledef +---------------------------------------------------------------- + SET search_path = public; + + CREATE TABLE testhashsubpart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY RANGE (a) SUBPARTITION BY HASH (abs((b * 2))) + + ( + + PARTITION p0 VALUES LESS THAN (1000) TABLESPACE pg_default+ + ( + + SUBPARTITION p00 TABLESPACE pg_default, + + SUBPARTITION p01 TABLESPACE pg_default + + ), + + PARTITION p1 VALUES LESS THAN (2000) TABLESPACE pg_default+ + ( + + SUBPARTITION p10 TABLESPACE pg_default, + + SUBPARTITION p11 TABLESPACE pg_default + + ) + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +select pg_get_tabledef('testnormalsubpart'); + pg_get_tabledef +----------------------------------------------------------------------- + SET search_path = public; + + CREATE TABLE testnormalsubpart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY RANGE (a) SUBPARTITION BY RANGE (b) + + ( + + PARTITION p0 VALUES LESS THAN (1000) TABLESPACE pg_default + + ( + + SUBPARTITION p00 VALUES LESS THAN (100) TABLESPACE pg_default+ + ), + + PARTITION p1 VALUES LESS THAN (2000) TABLESPACE pg_default + + ( + + SUBPARTITION p10 VALUES LESS THAN (200) TABLESPACE pg_default+ + ) + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +drop table testrangepart; +drop table testlistpart; +drop table testhashpart; +drop table testrangesubpart; +drop table testlistsubpart; +drop table testhashsubpart; +drop table testnormalsubpart; +drop table testtab; +\c regression +drop database if exists part_expr_key_db; +create database part_expr_key_db; +\c part_expr_key_db +create table testrangepart(a int, b int) partition by range(abs(a*2)) +( + partition p0 values less than(100), + partition p1 values less than(200) +); +create table testlistpart(a int, b int) partition by list(abs(a*2)) +( + partition p0 values(100,200), + partition p1 values(300,400) +); +create table testhashpart(a int, b int) partition by hash(abs(a*2)) (partition p0,partition p1); +create table testrangesubpart(a int, b int) partition by range(a+b) subpartition by range(a-b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +create table testlistsubpart(a int, b int) partition by range(abs(a*2)) subpartition by list(abs(b*2)) +( + partition p0 values less than(1000)(subpartition p00 values(100)), + partition p1 values less than(2000) (subpartition p10 values(200)) +); +create table testhashsubpart(a int, b int) partition by range(a) subpartition by hash(abs(b*2)) +( + partition p0 values less than(1000)(subpartition p00,subpartition p01), + partition p1 values less than(2000) (subpartition p10,subpartition p11) +); +create table testnormalsubpart(a int, b int) partition by range(a) subpartition by range(b) +( + partition p0 values less than(1000)(subpartition p00 values less than(100)), + partition p1 values less than(2000)(subpartition p10 values less than(200)) +); +\! @abs_bindir@/gs_dump part_expr_key_db -p @portstring@ -f @abs_bindir@/gs_dump_partition_expr.sql >/dev/null 2>&1; echo $? +0 +drop table testrangepart; +drop table testlistpart; +drop table testhashpart; +drop table testrangesubpart; +drop table testlistsubpart; +drop table testhashsubpart; +drop table testnormalsubpart; +\! @abs_bindir@/gsql -p @portstring@ -d part_expr_key_db -f @abs_bindir@/gs_dump_partition_expr.sql >/dev/null 2>&1; echo $? +0 +select pg_get_tabledef('testrangepart'); + pg_get_tabledef +---------------------------------------------------------------- + SET search_path = public; + + CREATE TABLE testrangepart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY RANGE (abs((a * 2))) + + ( + + PARTITION p0 VALUES LESS THAN (100) TABLESPACE pg_default,+ + PARTITION p1 VALUES LESS THAN (200) TABLESPACE pg_default + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +select pg_get_tabledef('testlistpart'); + pg_get_tabledef +---------------------------------------------------------- + SET search_path = public; + + CREATE TABLE testlistpart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY LIST (abs((a * 2))) + + ( + + PARTITION p0 VALUES (100,200) TABLESPACE pg_default,+ + PARTITION p1 VALUES (300,400) TABLESPACE pg_default + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +select pg_get_tabledef('testhashpart'); + pg_get_tabledef +----------------------------------------- + SET search_path = public; + + CREATE TABLE testhashpart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY HASH (abs((a * 2))) + + ( + + PARTITION p0 TABLESPACE pg_default,+ + PARTITION p1 TABLESPACE pg_default + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +select pg_get_tabledef('testrangesubpart'); + pg_get_tabledef +----------------------------------------------------------------------- + SET search_path = public; + + CREATE TABLE testrangesubpart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY RANGE ((a + b)) SUBPARTITION BY RANGE ((a - b)) + + ( + + PARTITION p0 VALUES LESS THAN (1000) TABLESPACE pg_default + + ( + + SUBPARTITION p00 VALUES LESS THAN (100) TABLESPACE pg_default+ + ), + + PARTITION p1 VALUES LESS THAN (2000) TABLESPACE pg_default + + ( + + SUBPARTITION p10 VALUES LESS THAN (200) TABLESPACE pg_default+ + ) + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +select pg_get_tabledef('testlistsubpart'); + pg_get_tabledef +----------------------------------------------------------------------- + SET search_path = public; + + CREATE TABLE testlistsubpart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY RANGE (abs((a * 2))) SUBPARTITION BY LIST (abs((b * 2)))+ + ( + + PARTITION p0 VALUES LESS THAN (1000) TABLESPACE pg_default + + ( + + SUBPARTITION p00 VALUES (100) TABLESPACE pg_default + + ), + + PARTITION p1 VALUES LESS THAN (2000) TABLESPACE pg_default + + ( + + SUBPARTITION p10 VALUES (200) TABLESPACE pg_default + + ) + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +select pg_get_tabledef('testhashsubpart'); + pg_get_tabledef +---------------------------------------------------------------- + SET search_path = public; + + CREATE TABLE testhashsubpart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY RANGE (a) SUBPARTITION BY HASH (abs((b * 2))) + + ( + + PARTITION p0 VALUES LESS THAN (1000) TABLESPACE pg_default+ + ( + + SUBPARTITION p00 TABLESPACE pg_default, + + SUBPARTITION p01 TABLESPACE pg_default + + ), + + PARTITION p1 VALUES LESS THAN (2000) TABLESPACE pg_default+ + ( + + SUBPARTITION p10 TABLESPACE pg_default, + + SUBPARTITION p11 TABLESPACE pg_default + + ) + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +select pg_get_tabledef('testnormalsubpart'); + pg_get_tabledef +----------------------------------------------------------------------- + SET search_path = public; + + CREATE TABLE testnormalsubpart ( + + a integer, + + b integer + + ) + + WITH (orientation=row, compression=no) + + PARTITION BY RANGE (a) SUBPARTITION BY RANGE (b) + + ( + + PARTITION p0 VALUES LESS THAN (1000) TABLESPACE pg_default + + ( + + SUBPARTITION p00 VALUES LESS THAN (100) TABLESPACE pg_default+ + ), + + PARTITION p1 VALUES LESS THAN (2000) TABLESPACE pg_default + + ( + + SUBPARTITION p10 VALUES LESS THAN (200) TABLESPACE pg_default+ + ) + + ) + + ENABLE ROW MOVEMENT; +(1 row) + +\c regression +drop database if exists part_expr_key_db; diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index 7a5963dc5..d2d0235f1 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -1022,3 +1022,6 @@ test: row_count_function # show_warnings test: show_warnings + +# partition expression key +test: partition_expr_key