From ffae76f1236899148d9d7c3c59a9703506f0017f Mon Sep 17 00:00:00 2001 From: teooooozhang Date: Wed, 29 Jun 2022 16:16:26 +0800 Subject: [PATCH] ignore_hint: issue handling for interval partition condition, which cannot report warning when inserting null value --- src/common/backend/catalog/heap.cpp | 4 +- .../runtime/executor/nodeModifyTable.cpp | 15 +++-- src/include/utils/partitionmap_gs.h | 10 ++- .../ignore/ignore_no_matched_partition.out | 65 +++++++++++++++++++ .../ignore/ignore_no_matched_partition.sql | 36 ++++++++++ 5 files changed, 120 insertions(+), 10 deletions(-) diff --git a/src/common/backend/catalog/heap.cpp b/src/common/backend/catalog/heap.cpp index e7d5e89e8..447e6645c 100644 --- a/src/common/backend/catalog/heap.cpp +++ b/src/common/backend/catalog/heap.cpp @@ -6260,7 +6260,7 @@ Oid AddNewIntervalPartition(Relation rel, void* insertTuple, bool isDDL) UnlockRelationForAccessIntervalPartTabIfHeld(rel); /* it will accept invalidation messages generated by other sessions in lockRelationForAddIntervalPartition. */ LockRelationForAddIntervalPartition(rel); - partitionRoutingForTuple(rel, insertTuple, u_sess->catalog_cxt.route); + partitionRoutingForTuple(rel, insertTuple, u_sess->catalog_cxt.route, false); /* if the partition exists, return partition's oid */ if (u_sess->catalog_cxt.route->fileExist) { @@ -7132,7 +7132,7 @@ Oid heapTupleGetPartitionId(Relation rel, void *tuple, bool isDDL, bool canIgnor Oid partitionid = InvalidOid; /* get routing result */ - partitionRoutingForTuple(rel, tuple, u_sess->catalog_cxt.route); + partitionRoutingForTuple(rel, tuple, u_sess->catalog_cxt.route, canIgnore); /* if the partition exists, return partition's oid */ if (u_sess->catalog_cxt.route->fileExist) { diff --git a/src/gausskernel/runtime/executor/nodeModifyTable.cpp b/src/gausskernel/runtime/executor/nodeModifyTable.cpp index c16e057da..8283e4cea 100644 --- a/src/gausskernel/runtime/executor/nodeModifyTable.cpp +++ b/src/gausskernel/runtime/executor/nodeModifyTable.cpp @@ -2124,11 +2124,12 @@ lreplace: bool row_movement = false; bool need_create_file = false; int seqNum = -1; + bool can_ignore = estate->es_plannedstmt->hasIgnore; if (!partKeyUpdate) { row_movement = false; new_partId = oldPartitionOid; } else { - partitionRoutingForTuple(result_relation_desc, tuple, u_sess->exec_cxt.route); + 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; @@ -2136,11 +2137,11 @@ lreplace: Partition part = partitionOpen(result_relation_desc, new_partId, RowExclusiveLock); Relation partRel = partitionGetRelation(result_relation_desc, part); - partitionRoutingForTuple(partRel, tuple, u_sess->exec_cxt.route); + 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 { - int level = estate->es_plannedstmt->hasIgnore ? WARNING : ERROR; + int level = can_ignore ? WARNING : ERROR; ereport(level, (errmodule(MOD_EXECUTOR), (errcode(ERRCODE_PARTITION_ERROR), errmsg("fail to update partitioned table \"%s\"", @@ -2150,7 +2151,7 @@ lreplace: releaseDummyRelation(&partRel); partitionClose(result_relation_desc, part, NoLock); - if (!u_sess->exec_cxt.route->fileExist && estate->es_plannedstmt->hasIgnore) { + if (!u_sess->exec_cxt.route->fileExist && can_ignore) { return NULL; } } @@ -2174,7 +2175,7 @@ lreplace: * it can not be a range area */ if (u_sess->exec_cxt.route->partArea != PART_AREA_INTERVAL) { - if (epqstate->parentestate->es_plannedstmt->hasIgnore) { + if (can_ignore) { ereport(WARNING, (errmsg("fail to update partitioned table \"%s\".new tuple does not " "map to any table partition.", RelationGetRelationName(result_relation_desc)))); @@ -2235,7 +2236,7 @@ lreplace: /* * check constraints first if SQL has keyword IGNORE */ - if (estate->es_plannedstmt && estate->es_plannedstmt->hasIgnore && + if (can_ignore && !ExecCheckIndexConstraints(slot, estate, fake_relation, partition, &isgpi, bucketid, &conflictInfo, &conflictPartOid, &conflictBucketid)) { ereport(WARNING, (errmsg("duplicate key value violates unique constraint in table \"%s\"", @@ -2447,7 +2448,7 @@ lreplace: /* * check constraints first if SQL has keyword IGNORE */ - if (estate->es_plannedstmt && estate->es_plannedstmt->hasIgnore && + if (can_ignore && !ExecCheckIndexConstraints(slot, estate, fake_insert_relation, insert_partition, &isgpi, bucketid, &conflictInfo, &conflictPartOid, &conflictBucketid)) { ereport(WARNING, (errmsg("duplicate key value violates unique constraint in table \"%s\"", diff --git a/src/include/utils/partitionmap_gs.h b/src/include/utils/partitionmap_gs.h index cc59c7d3a..e3bc01fe1 100644 --- a/src/include/utils/partitionmap_gs.h +++ b/src/include/utils/partitionmap_gs.h @@ -146,7 +146,7 @@ typedef struct HashPartitionMap { } \ } while (0) -#define partitionRoutingForTuple(rel, tuple, partIdentfier) \ +#define partitionRoutingForTuple(rel, tuple, partIdentfier, canIgnore) \ do { \ TupleDesc tuple_desc = NULL; \ int2vector *partkey_column = NULL; \ @@ -169,6 +169,14 @@ typedef struct HashPartitionMap { transformDatum2Const((rel)->rd_att, partkey_column->values[i], column_raw, isnull, &consts[i]); \ } \ if (PartitionMapIsInterval((rel)->partMap) && values[0]->constisnull) { \ + if (canIgnore) { \ + /* treat type as PART_TYPE_RANGE because PART_TYPE_INTERVAL will create a new partition. \ + * this will be handled by caller and directly return */ \ + (partIdentfier)->partArea = PART_AREA_RANGE; \ + (partIdentfier)->fileExist = false; \ + (partIdentfier)->partitionId = InvalidOid; \ + break; \ + } \ ereport(ERROR, \ (errcode(ERRCODE_INTERNAL_ERROR), errmsg("inserted partition key does not map to any partition"), \ errdetail("inserted partition key cannot be NULL for interval-partitioned table"))); \ diff --git a/src/test/regress/expected/ignore/ignore_no_matched_partition.out b/src/test/regress/expected/ignore/ignore_no_matched_partition.out index e4543f573..f31207504 100644 --- a/src/test/regress/expected/ignore/ignore_no_matched_partition.out +++ b/src/test/regress/expected/ignore/ignore_no_matched_partition.out @@ -304,6 +304,71 @@ select * from t_ignore; 3000 | abc (1 row) +-- test for interval partition table +drop table if exists t_interval_partition cascade; +NOTICE: table "t_interval_partition" does not exist, skipping +create table t_interval_partition +( + id1 integer primary key, + id3 integer, + c_3 date +) +PARTITION BY RANGE (c_3) INTERVAL('1 year') +( + PARTITION P1 values less than ('2018-03-16 16:27:04'), + PARTITION P2 values less than ('2020-03-16 16:27:04'), + PARTITION P4 values less than ('2022-03-16 16:27:04') +) ENABLE ROW MOVEMENT; +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t_interval_partition_pkey" for table "t_interval_partition" +-- test for interval partition table, opfusion: on +set enable_partition_opfusion = on; +insert into t_interval_partition values (1, 1, null); +ERROR: inserted partition key does not map to any partition +DETAIL: inserted partition key cannot be NULL for interval-partitioned table +insert /*+ ignore_error */ into t_interval_partition values (1, 1, null); +WARNING: inserted partition key does not map to any table partition +select * from t_interval_partition; + id1 | id3 | c_3 +-----+-----+----- +(0 rows) + +insert into t_interval_partition values (1, 1, '2023-02-01 00:00:00'); +update t_interval_partition set c_3 = null where id1 = 1; +ERROR: inserted partition key does not map to any partition +DETAIL: inserted partition key cannot be NULL for interval-partitioned table +update /*+ ignore_error */ t_interval_partition set c_3 = null where id1 = 1; +WARNING: fail to update partitioned table "t_interval_partition".new tuple does not map to any table partition. +select * from t_interval_partition; + id1 | id3 | c_3 +-----+-----+------------ + 1 | 1 | 02-01-2023 +(1 row) + +-- test for interval partition table, opfusion: off +delete from t_interval_partition; +set enable_partition_opfusion = off; +insert into t_interval_partition values (1, 1, null); +ERROR: inserted partition key does not map to any partition +DETAIL: inserted partition key cannot be NULL for interval-partitioned table +insert /*+ ignore_error */ into t_interval_partition values (1, 1, null); +WARNING: inserted partition key does not map to any table partition +select * from t_interval_partition; + id1 | id3 | c_3 +-----+-----+----- +(0 rows) + +insert into t_interval_partition values (1, 1, '2023-02-01 00:00:00'); +update t_interval_partition set c_3 = null where id1 = 1; +ERROR: inserted partition key does not map to any partition +DETAIL: inserted partition key cannot be NULL for interval-partitioned table +update /*+ ignore_error */ t_interval_partition set c_3 = null where id1 = 1; +WARNING: fail to update partitioned table "t_interval_partition".new tuple does not map to any table partition. +select * from t_interval_partition; + id1 | id3 | c_3 +-----+-----+------------ + 1 | 1 | 02-01-2023 +(1 row) + set enable_opfusion = on; set enable_partition_opfusion = off; drop table t_ignore; diff --git a/src/test/regress/sql/ignore/ignore_no_matched_partition.sql b/src/test/regress/sql/ignore/ignore_no_matched_partition.sql index 0e436bee5..2fd2bb11a 100644 --- a/src/test/regress/sql/ignore/ignore_no_matched_partition.sql +++ b/src/test/regress/sql/ignore/ignore_no_matched_partition.sql @@ -153,6 +153,42 @@ insert into t_ignore values(3000, 'abc'); update /*+ ignore_error */ t_ignore set col1 = 20000 where col1 = 3000; select * from t_ignore; +-- test for interval partition table +drop table if exists t_interval_partition cascade; +create table t_interval_partition +( + id1 integer primary key, + id3 integer, + c_3 date +) +PARTITION BY RANGE (c_3) INTERVAL('1 year') +( + PARTITION P1 values less than ('2018-03-16 16:27:04'), + PARTITION P2 values less than ('2020-03-16 16:27:04'), + PARTITION P4 values less than ('2022-03-16 16:27:04') +) ENABLE ROW MOVEMENT; + +-- test for interval partition table, opfusion: on +set enable_partition_opfusion = on; +insert into t_interval_partition values (1, 1, null); +insert /*+ ignore_error */ into t_interval_partition values (1, 1, null); +select * from t_interval_partition; +insert into t_interval_partition values (1, 1, '2023-02-01 00:00:00'); +update t_interval_partition set c_3 = null where id1 = 1; +update /*+ ignore_error */ t_interval_partition set c_3 = null where id1 = 1; +select * from t_interval_partition; + +-- test for interval partition table, opfusion: off +delete from t_interval_partition; +set enable_partition_opfusion = off; +insert into t_interval_partition values (1, 1, null); +insert /*+ ignore_error */ into t_interval_partition values (1, 1, null); +select * from t_interval_partition; +insert into t_interval_partition values (1, 1, '2023-02-01 00:00:00'); +update t_interval_partition set c_3 = null where id1 = 1; +update /*+ ignore_error */ t_interval_partition set c_3 = null where id1 = 1; +select * from t_interval_partition; + set enable_opfusion = on; set enable_partition_opfusion = off; drop table t_ignore;