diff --git a/src/common/backend/catalog/heap.cpp b/src/common/backend/catalog/heap.cpp index 95c700468..04af2e8a1 100755 --- a/src/common/backend/catalog/heap.cpp +++ b/src/common/backend/catalog/heap.cpp @@ -5091,9 +5091,15 @@ Datum Timestamp2Boundarys(Relation rel, Timestamp ts) { Const consts; RangePartitionMap* partMap = (RangePartitionMap*)rel->partMap; - bool isTimestamptz = partMap->rangeElements[partMap->rangeElementsNum - 1].boundary[0]->consttype == TIMESTAMPTZOID; - Datum columnRaw = TimestampGetDatum(ts); - int2vector* partKeyColumn = partMap->partitionKey; + Const* lastPartBoundary = partMap->rangeElements[partMap->rangeElementsNum - 1].boundary[0]; + bool isTimestamptz = lastPartBoundary->consttype == TIMESTAMPTZOID; + Datum columnRaw; + if (lastPartBoundary->consttype == DATEOID) { + columnRaw = timestamp2date(ts); + } else { + columnRaw = TimestampGetDatum(ts); + } + int2vector* partKeyColumn = partMap->partitionKey; Assert(partKeyColumn->dim1 == 1); (void)transformDatum2Const(rel->rd_att, partKeyColumn->values[0], columnRaw, false, &consts); @@ -5110,14 +5116,22 @@ Datum GetPartBoundaryByTuple(Relation rel, HeapTuple tuple) Assert(partKeyColumn->dim1 == 1); Assert(partMap->type.type == PART_TYPE_INTERVAL); Assert(partMap->rangeElementsNum >= 1); - Assert(partMap->rangeElements[partMap->rangeElementsNum - 1].boundary[0]->consttype == TIMESTAMPOID || - partMap->rangeElements[partMap->rangeElementsNum - 1].boundary[0]->consttype == TIMESTAMPTZOID); + + Const* lastPartBoundary = partMap->rangeElements[partMap->rangeElementsNum - 1].boundary[0]; + Assert(lastPartBoundary->consttype == TIMESTAMPOID || lastPartBoundary->consttype == TIMESTAMPTZOID || + lastPartBoundary->consttype == DATEOID); bool isNull = false; Datum columnRaw = fastgetattr(tuple, partKeyColumn->values[0], rel->rd_att, &isNull); - Timestamp value = DatumGetTimestamp(columnRaw); - Timestamp boundaryTs = - DatumGetTimestamp(partMap->rangeElements[partMap->rangeElementsNum - 1].boundary[0]->constvalue); + Timestamp value; + Timestamp boundaryTs; + if (lastPartBoundary->consttype == DATEOID) { + value = date2timestamp(DatumGetDateADT(columnRaw)); + boundaryTs = date2timestamp(DatumGetDateADT(lastPartBoundary->constvalue)); + } else { + value = DatumGetTimestamp(columnRaw); + boundaryTs = DatumGetTimestamp(lastPartBoundary->constvalue); + } return Timestamp2Boundarys(rel, Align2UpBoundary(value, partMap->intervalValue, boundaryTs)); } diff --git a/src/common/backend/utils/adt/date.cpp b/src/common/backend/utils/adt/date.cpp index 06d684d87..3d70b6045 100755 --- a/src/common/backend/utils/adt/date.cpp +++ b/src/common/backend/utils/adt/date.cpp @@ -965,12 +965,8 @@ Datum date_timestamp(PG_FUNCTION_ARGS) PG_RETURN_TIMESTAMP(result); } -/* timestamp_date() - * Convert timestamp to date data type. - */ -Datum timestamp_date(PG_FUNCTION_ARGS) +Datum timestamp2date(Timestamp timestamp) { - Timestamp timestamp = PG_GETARG_TIMESTAMP(0); DateADT result; struct pg_tm tt, *tm = &tt; fsec_t fsec; @@ -986,10 +982,18 @@ Datum timestamp_date(PG_FUNCTION_ARGS) result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE; } - PG_RETURN_DATEADT(result); } +/* timestamp_date() + * Convert timestamp to date data type. + */ +Datum timestamp_date(PG_FUNCTION_ARGS) +{ + Timestamp timestamp = PG_GETARG_TIMESTAMP(0); + return timestamp2date(timestamp); +} + /* date_timestamptz() * Convert date to timestamp with time zone data type. */ @@ -1577,7 +1581,7 @@ Datum timestamptz_time(PG_FUNCTION_ARGS) if (TIMESTAMP_NOT_FINITE(timestamp)) { PG_RETURN_NULL(); - } + } if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0) { ereport(ERROR, (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE), errmsg("timestamp out of range"))); diff --git a/src/gausskernel/cbb/utils/partition/partitionmap.cpp b/src/gausskernel/cbb/utils/partition/partitionmap.cpp index 860ac3ae5..71ed65cf5 100644 --- a/src/gausskernel/cbb/utils/partition/partitionmap.cpp +++ b/src/gausskernel/cbb/utils/partition/partitionmap.cpp @@ -1160,13 +1160,23 @@ Oid getRangePartitionOid(Relation relation, Const** partKeyValue, int32* partSeq inline Const* CalcLowBoundary(const Const* upBoundary, Interval* intervalValue) { - Assert(upBoundary->consttype == TIMESTAMPOID || upBoundary->consttype == TIMESTAMPTZOID); - Timestamp lowTs = timestamp_mi_interval(DatumGetTimestamp(upBoundary->constvalue), intervalValue); + Assert(upBoundary->consttype == TIMESTAMPOID || upBoundary->consttype == TIMESTAMPTZOID || + upBoundary->consttype == DATEOID); + Datum lowValue; + if (upBoundary->consttype == DATEOID) { + Timestamp lowTs = timestamp_mi_interval(date2timestamp(DatumGetDateADT(upBoundary->constvalue)), intervalValue); + lowValue = timestamp2date(lowTs); + + } else { + Timestamp lowTs = timestamp_mi_interval(DatumGetTimestamp(upBoundary->constvalue), intervalValue); + lowValue = TimestampGetDatum(lowTs); + } + return makeConst(upBoundary->consttype, upBoundary->consttypmod, upBoundary->constcollid, upBoundary->constlen, - TimestampGetDatum(lowTs), + lowValue, upBoundary->constisnull, upBoundary->constbyval); } diff --git a/src/gausskernel/optimizer/commands/tablecmds.cpp b/src/gausskernel/optimizer/commands/tablecmds.cpp index d9d4f04f4..388bf6e2d 100644 --- a/src/gausskernel/optimizer/commands/tablecmds.cpp +++ b/src/gausskernel/optimizer/commands/tablecmds.cpp @@ -539,6 +539,8 @@ static void RangeVarCallbackForAlterRelation( static bool CheckRangePartitionKeyType(Oid typoid); static void CheckRangePartitionKeyType(Form_pg_attribute* attrs, List* pos); static void CheckIntervalPartitionKeyType(Form_pg_attribute* attrs, List* pos); +static void CheckIntervalValue( + const Form_pg_attribute* attrs, const List* pos, const IntervalPartitionDefState* intervalPartDef); static void CheckPartitionTablespace(const char* spcname, Oid owner); static void ComparePartitionValue(List* pos, Form_pg_attribute* attrs, PartitionState* partTableState); static bool ConfirmTypeInfo(Oid* target_oid, int* target_mod, Const* src, Form_pg_attribute attrs, bool isinterval); @@ -1801,6 +1803,7 @@ Oid DefineRelation(CreateStmt* stmt, char relkind, Oid ownerId) CheckValuePartitionKeyType(descriptor->attrs, pos); } else if (stmt->partTableState->partitionStrategy == PART_STRATEGY_INTERVAL) { CheckIntervalPartitionKeyType(descriptor->attrs, pos); + CheckIntervalValue(descriptor->attrs, pos, stmt->partTableState->intervalPartDef); } else { CheckRangePartitionKeyType(descriptor->attrs, pos); } @@ -15794,7 +15797,7 @@ static void CheckIntervalPartitionKeyType(Form_pg_attribute* attrs, List* pos) ListCell* cell = list_head(pos); int location = lfirst_int(cell); Oid typoid = attrs[location]->atttypid; - if (typoid != TIMESTAMPOID && typoid != TIMESTAMPTZOID) { + if (typoid != TIMESTAMPOID && typoid != TIMESTAMPTZOID && typoid != DATEOID) { list_free_ext(pos); ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), @@ -15803,6 +15806,31 @@ static void CheckIntervalPartitionKeyType(Form_pg_attribute* attrs, List* pos) } } +static void CheckIntervalValue( + const Form_pg_attribute* attrs, const List* pos, const IntervalPartitionDefState* intervalPartDef) +{ + /* must be one partitionkey for interval partition, have checked before */ + Assert(pos->length == 1); + + ListCell* cell = list_head(pos); + int location = lfirst_int(cell); + Oid typoid = attrs[location]->atttypid; + if (typoid != DATEOID) { + return; + } + + int32 typmod = -1; + Interval* interval = NULL; + A_Const* node = reinterpret_cast(intervalPartDef->partInterval); + interval = char_to_interval(node->val.val.str, typmod); + if (interval->time != 0) { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("the interval of DATE type must be an integer multiple of days"))); + } + pfree(interval); +} + /* * @@GaussDB@@ * Target : value-partition type check diff --git a/src/include/utils/date.h b/src/include/utils/date.h index d7c452f82..e145e741f 100644 --- a/src/include/utils/date.h +++ b/src/include/utils/date.h @@ -147,6 +147,7 @@ extern Datum varchar_date(PG_FUNCTION_ARGS); extern Datum bpchar_date(PG_FUNCTION_ARGS); extern Datum text_date(PG_FUNCTION_ARGS); +extern Datum timestamp2date(Timestamp timestamp); extern Datum timestamp_date(PG_FUNCTION_ARGS); extern Datum date_timestamptz(PG_FUNCTION_ARGS); extern Datum timestamptz_date(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/hw_partition_interval_compatibility.out b/src/test/regress/expected/hw_partition_interval_compatibility.out new file mode 100644 index 000000000..856c4865e --- /dev/null +++ b/src/test/regress/expected/hw_partition_interval_compatibility.out @@ -0,0 +1,154 @@ +-- the date tyte of the compatible B/C/PG version is different from the A compatible version +create database pg_db dbcompatibility 'PG'; +\c pg_db +CREATE TABLE interval_tab1 ( + city_id int not null, + logdate date not null, + peaktemp int, + unitsales int +) +PARTITION BY RANGE (logdate) +INTERVAL ('1 day') +( + PARTITION p1 VALUES LESS THAN (('2020-03-01')) +); +select relname, parttype, parentid, partstrategy, boundaries from pg_partition; + relname | parttype | parentid | partstrategy | boundaries +---------------+----------+----------+--------------+-------------- + interval_tab1 | r | 20186 | i | + p1 | p | 20186 | r | {2020-03-01} +(2 rows) + +insert into interval_tab1 values(1,'2020-2-29', 1, 1); +insert into interval_tab1 values(1,'2020-3-1', 1, 1); +insert into interval_tab1 values(1,'2020-3-1 02:00:00', 1, 1); +insert into interval_tab1 values(1,'2020-3-3', 1, 1); +insert into interval_tab1 values(1,'2020-3-4', 1, 1); +insert into interval_tab1 values(1,'2020-3-5', 1, 1); +insert into interval_tab1 values(1,'2020-3-6', 1, 1); +insert into interval_tab1 values(1,'2020-3-7', 1, 1); +insert into interval_tab1 values(1,'2020-4-2', 1, 1); +select relname, parttype, parentid, partstrategy, boundaries from pg_partition; + relname | parttype | parentid | partstrategy | boundaries +---------------+----------+----------+--------------+-------------- + interval_tab1 | r | 20186 | i | + p1 | p | 20186 | r | {2020-03-01} + sys_p1 | p | 20186 | i | {03-02-2020} + sys_p2 | p | 20186 | i | {03-04-2020} + sys_p3 | p | 20186 | i | {03-05-2020} + sys_p4 | p | 20186 | i | {03-06-2020} + sys_p5 | p | 20186 | i | {03-07-2020} + sys_p6 | p | 20186 | i | {03-08-2020} + sys_p7 | p | 20186 | i | {04-03-2020} +(9 rows) + +select * from interval_tab1 where logdate >= '2020-3-1' and logdate < '2020-3-7'; + city_id | logdate | peaktemp | unitsales +---------+------------+----------+----------- + 1 | 03-01-2020 | 1 | 1 + 1 | 03-01-2020 | 1 | 1 + 1 | 03-03-2020 | 1 | 1 + 1 | 03-04-2020 | 1 | 1 + 1 | 03-05-2020 | 1 | 1 + 1 | 03-06-2020 | 1 | 1 +(6 rows) + +explain (costs off, verbose on, nodes off) select * from interval_tab1 where logdate >= '2020-3-1' and logdate < '2020-3-7'; + QUERY PLAN +------------------------------------------------------------------------------------------------------------------ + Partition Iterator + Output: city_id, logdate, peaktemp, unitsales + Iterations: 5 + -> Partitioned Seq Scan on public.interval_tab1 + Output: city_id, logdate, peaktemp, unitsales + Filter: ((interval_tab1.logdate >= '03-01-2020'::date) AND (interval_tab1.logdate < '03-07-2020'::date)) + Selected Partitions: 2..6 +(7 rows) + +alter table interval_tab1 drop partition p1; +select relname, parttype, parentid, partstrategy, boundaries from pg_partition; + relname | parttype | parentid | partstrategy | boundaries +---------------+----------+----------+--------------+-------------- + interval_tab1 | r | 20186 | i | + sys_p2 | p | 20186 | i | {03-04-2020} + sys_p3 | p | 20186 | i | {03-05-2020} + sys_p4 | p | 20186 | i | {03-06-2020} + sys_p5 | p | 20186 | i | {03-07-2020} + sys_p6 | p | 20186 | i | {03-08-2020} + sys_p7 | p | 20186 | i | {04-03-2020} + sys_p1 | p | 20186 | r | {03-02-2020} +(8 rows) + +alter table interval_tab1 merge partitions sys_p5, sys_p6 into partition sys_p5_p6; +select relname, parttype, parentid, partstrategy, boundaries from pg_partition; + relname | parttype | parentid | partstrategy | boundaries +---------------+----------+----------+--------------+-------------- + interval_tab1 | r | 20186 | i | + sys_p7 | p | 20186 | i | {04-03-2020} + sys_p1 | p | 20186 | r | {03-02-2020} + sys_p4 | p | 20186 | r | {03-06-2020} + sys_p3 | p | 20186 | r | {03-05-2020} + sys_p2 | p | 20186 | r | {03-04-2020} + sys_p5_p6 | p | 20186 | r | {03-08-2020} +(7 rows) + +drop table interval_tab1; +CREATE TABLE interval_tab1 ( + city_id int not null, + logdate date not null, + peaktemp int, + unitsales int +) +PARTITION BY RANGE (logdate) +INTERVAL ('2 day') +( + PARTITION p1 VALUES LESS THAN (('2020-03-01')) +); +insert into interval_tab1 values(1,'2020-2-29', 1, 1); +insert into interval_tab1 values(1,'2020-3-1', 1, 1); +insert into interval_tab1 values(1,'2020-3-1 02:00:00', 1, 1); +insert into interval_tab1 values(1,'2020-3-2', 1, 1); +insert into interval_tab1 values(1,'2020-4-2', 1, 1); +select relname, parttype, parentid, partstrategy, boundaries from pg_partition; + relname | parttype | parentid | partstrategy | boundaries +---------------+----------+----------+--------------+-------------- + interval_tab1 | r | 20201 | i | + p1 | p | 20201 | r | {2020-03-01} + sys_p1 | p | 20201 | i | {03-03-2020} + sys_p2 | p | 20201 | i | {04-04-2020} +(4 rows) + +select * from interval_tab1 partition(sys_p1); + city_id | logdate | peaktemp | unitsales +---------+------------+----------+----------- + 1 | 03-01-2020 | 1 | 1 + 1 | 03-01-2020 | 1 | 1 + 1 | 03-02-2020 | 1 | 1 +(3 rows) + +alter table interval_tab1 split partition sys_p1 at (to_date('2020-03-02', 'YYYY-MM-DD')) into (partition sys_p1_1, partition sys_p1_2); +select relname, parttype, parentid, partstrategy, boundaries from pg_partition; + relname | parttype | parentid | partstrategy | boundaries +---------------+----------+----------+--------------+-------------- + interval_tab1 | r | 20201 | i | + p1 | p | 20201 | r | {2020-03-01} + sys_p2 | p | 20201 | i | {04-04-2020} + sys_p1_1 | p | 20201 | r | {03-02-2020} + sys_p1_2 | p | 20201 | r | {03-03-2020} +(5 rows) + +drop table interval_tab1; +CREATE TABLE interval_tab1 ( + city_id int not null, + logdate date not null, + peaktemp int, + unitsales int +) +PARTITION BY RANGE (logdate) +INTERVAL ('1 hour') +( + PARTITION p1 VALUES LESS THAN (('2020-03-01')) +); +ERROR: the interval of DATE type must be an integer multiple of days +\c regression +drop database pg_db; diff --git a/src/test/regress/parallel_schedule18 b/src/test/regress/parallel_schedule18 index 899b412d4..e6c001309 100644 --- a/src/test/regress/parallel_schedule18 +++ b/src/test/regress/parallel_schedule18 @@ -26,6 +26,7 @@ test: hw_partition_interval_check_syntax #test: hw_partition_vacuum test: hw_partition_interval_split test: hw_partition_interval_merge +test: hw_partition_interval_compatibility # Global Partition index feature testcase # gpi create diff --git a/src/test/regress/sql/hw_partition_interval_compatibility.sql b/src/test/regress/sql/hw_partition_interval_compatibility.sql new file mode 100644 index 000000000..df2fc4238 --- /dev/null +++ b/src/test/regress/sql/hw_partition_interval_compatibility.sql @@ -0,0 +1,88 @@ +-- the date tyte of the compatible B/C/PG version is different from the A compatible version + +create database pg_db dbcompatibility 'PG'; +\c pg_db + +CREATE TABLE interval_tab1 ( + city_id int not null, + logdate date not null, + peaktemp int, + unitsales int +) +PARTITION BY RANGE (logdate) +INTERVAL ('1 day') +( + PARTITION p1 VALUES LESS THAN (('2020-03-01')) +); + +select relname, parttype, parentid, partstrategy, boundaries from pg_partition; + +insert into interval_tab1 values(1,'2020-2-29', 1, 1); +insert into interval_tab1 values(1,'2020-3-1', 1, 1); +insert into interval_tab1 values(1,'2020-3-1 02:00:00', 1, 1); +insert into interval_tab1 values(1,'2020-3-3', 1, 1); +insert into interval_tab1 values(1,'2020-3-4', 1, 1); +insert into interval_tab1 values(1,'2020-3-5', 1, 1); +insert into interval_tab1 values(1,'2020-3-6', 1, 1); +insert into interval_tab1 values(1,'2020-3-7', 1, 1); +insert into interval_tab1 values(1,'2020-4-2', 1, 1); + + +select relname, parttype, parentid, partstrategy, boundaries from pg_partition; + +select * from interval_tab1 where logdate >= '2020-3-1' and logdate < '2020-3-7'; + +explain (costs off, verbose on, nodes off) select * from interval_tab1 where logdate >= '2020-3-1' and logdate < '2020-3-7'; + +alter table interval_tab1 drop partition p1; + +select relname, parttype, parentid, partstrategy, boundaries from pg_partition; + +alter table interval_tab1 merge partitions sys_p5, sys_p6 into partition sys_p5_p6; + +select relname, parttype, parentid, partstrategy, boundaries from pg_partition; + +drop table interval_tab1; + +CREATE TABLE interval_tab1 ( + city_id int not null, + logdate date not null, + peaktemp int, + unitsales int +) +PARTITION BY RANGE (logdate) +INTERVAL ('2 day') +( + PARTITION p1 VALUES LESS THAN (('2020-03-01')) +); + +insert into interval_tab1 values(1,'2020-2-29', 1, 1); +insert into interval_tab1 values(1,'2020-3-1', 1, 1); +insert into interval_tab1 values(1,'2020-3-1 02:00:00', 1, 1); +insert into interval_tab1 values(1,'2020-3-2', 1, 1); +insert into interval_tab1 values(1,'2020-4-2', 1, 1); + +select relname, parttype, parentid, partstrategy, boundaries from pg_partition; + +select * from interval_tab1 partition(sys_p1); + +alter table interval_tab1 split partition sys_p1 at (to_date('2020-03-02', 'YYYY-MM-DD')) into (partition sys_p1_1, partition sys_p1_2); + +select relname, parttype, parentid, partstrategy, boundaries from pg_partition; + +drop table interval_tab1; + +CREATE TABLE interval_tab1 ( + city_id int not null, + logdate date not null, + peaktemp int, + unitsales int +) +PARTITION BY RANGE (logdate) +INTERVAL ('1 hour') +( + PARTITION p1 VALUES LESS THAN (('2020-03-01')) +); + +\c regression +drop database pg_db;