Skip to content

Commit

Permalink
Allocate target's tuple table slot in PortalHeapMemory during split p…
Browse files Browse the repository at this point in the history
…artition

    When we execute an ALTER TABLE statement that splits default partition into a "New" and a new Default partition, we perform the following 5 steps:

     * copy the tuples located in the default partition into a temporary table,
     * drop default partition,
     * create the “New“ partition,
     * create the new DEFAULT partition, and
     * copy all tuples from the temporary table into the newly created partitions.

    The last step is executed in split_rows, where for each tuple in the temporary table,

     * we determine the target partition/table,
     * construct the slot of the target table (if is NULL), and
     * store the tuple into the target table.

    Currently, we allocate the tuple table slot of the target table into a per-tuple memory context. If the size of that memory context exceeds 50KB, then we reset it. This will cause issues, since it will free target table's slot and at the next iteration (copy the next tuple) we will try to access the attributes of a freed (and not NULL) slot. In addition, the target table slot lifespan is much longer than an individual tuple. So it is not correct to allocate the slot in a per-tuple memory context.

    To solve this issue, we allocate target's tuple table slot in PortalHeapMemory. This is the CurrentMemoryContext that is used in split_rows before we start copying tuples. PortalHeapMemory is used already for storing the tuple table slot of the temporary table and the ResultRelInfo of the target tables. PortalHeapMemory is not freed while we copy tuples. After copying all tuples to the new partitions, we drop target tables' slots.
Signed-off-by: Nikos Armenatzoglou <nikos.armenatzoglou@gmail.com>
  • Loading branch information
George Caragea authored and armenatzoglou committed Jun 24, 2016
1 parent c342265 commit c0e1f00
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 14 deletions.
29 changes: 15 additions & 14 deletions src/backend/commands/tablecmds.c
Expand Up @@ -15248,6 +15248,10 @@ split_rows(Relation intoa, Relation intob, Relation temprel)
rrib->ri_partSlot = MakeSingleTupleTableSlot(RelationGetDescr(intob));
map_part_attrs(temprel, intoa, &rria->ri_partInsertMap, true);
map_part_attrs(temprel, intob, &rrib->ri_partInsertMap, true);
Assert(NULL != rria->ri_RelationDesc);
rria->ri_resultSlot = MakeSingleTupleTableSlot(rria->ri_RelationDesc->rd_att);
Assert(NULL != rrib->ri_RelationDesc);
rrib->ri_resultSlot = MakeSingleTupleTableSlot(rrib->ri_RelationDesc->rd_att);

/* constr might not be defined if this is a default partition */
if (intoa->rd_att->constr && intoa->rd_att->constr->num_check)
Expand Down Expand Up @@ -15445,21 +15449,18 @@ split_rows(Relation intoa, Relation intob, Relation temprel)
ExecDropSingleTupleTableSlot(rrib->ri_partSlot);

/*
* We may have created "cached" version of our target result tuple table slot
* inside reconstructMatchingTupleSlot. Drop any such slots.
* We created our target result tuple table slots upfront.
* We can drop them now.
*/
if (NULL != rria->ri_resultSlot)
{
Assert(NULL != rria->ri_resultSlot->tts_tupleDescriptor);
ExecDropSingleTupleTableSlot(rria->ri_resultSlot);
rria->ri_resultSlot = NULL;
}
if (NULL != rrib->ri_resultSlot)
{
Assert(NULL != rrib->ri_resultSlot->tts_tupleDescriptor);
ExecDropSingleTupleTableSlot(rrib->ri_resultSlot);
rrib->ri_resultSlot = NULL;
}
Assert(NULL != rria->ri_resultSlot);
Assert(NULL != rria->ri_resultSlot->tts_tupleDescriptor);
ExecDropSingleTupleTableSlot(rria->ri_resultSlot);
rria->ri_resultSlot = NULL;

Assert(NULL != rrib->ri_resultSlot);
Assert(NULL != rrib->ri_resultSlot->tts_tupleDescriptor);
ExecDropSingleTupleTableSlot(rrib->ri_resultSlot);
rrib->ri_resultSlot = NULL;

if (rria->ri_partInsertMap)
pfree(rria->ri_partInsertMap);
Expand Down
39 changes: 39 additions & 0 deletions src/test/regress/expected/partition.out
Expand Up @@ -7830,3 +7830,42 @@ select * from pt_td_leak where col1 = 5;

drop table pt_td_leak;
drop table pt_td_leak_exchange;
-- Test split default partition while per tuple memory context is reset
drop table if exists test_split_part cascade;
NOTICE: table "test_split_part" does not exist, skipping
CREATE TABLE test_split_part ( log_id int NOT NULL, f_array int[] NOT NULL)
DISTRIBUTED BY (log_id)
PARTITION BY RANGE(log_id)
(
START (1::int) END (100::int) EVERY (5) WITH (appendonly=false),
PARTITION "Old" START (101::int) END (201::int) WITH (appendonly=false),
DEFAULT PARTITION other_log_ids WITH (appendonly=false)
);
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_other_log_ids" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_2" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_3" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_4" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_5" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_6" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_7" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_8" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_9" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_10" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_11" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_12" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_13" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_14" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_15" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_16" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_17" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_18" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_19" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_20" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_21" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_Old" for table "test_split_part"
insert into test_split_part (log_id , f_array) select id, '{10}' from generate_series(1,1000) id;
ALTER TABLE test_split_part SPLIT DEFAULT PARTITION START (201) INCLUSIVE END (301) EXCLUSIVE INTO (PARTITION "New", DEFAULT PARTITION);
NOTICE: exchanged partition "other_log_ids" of relation "test_split_part" with relation "pg_temp_325194"
NOTICE: dropped partition "other_log_ids" for relation "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_New" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_other_log_ids" for table "test_split_part"
39 changes: 39 additions & 0 deletions src/test/regress/expected/partition_optimizer.out
Expand Up @@ -7800,3 +7800,42 @@ select * from pt_td_leak where col1 = 5;

drop table pt_td_leak;
drop table pt_td_leak_exchange;
-- Test split default partition while per tuple memory context is reset
drop table if exists test_split_part cascade;
NOTICE: table "test_split_part" does not exist, skipping
CREATE TABLE test_split_part ( log_id int NOT NULL, f_array int[] NOT NULL)
DISTRIBUTED BY (log_id)
PARTITION BY RANGE(log_id)
(
START (1::int) END (100::int) EVERY (5) WITH (appendonly=false),
PARTITION "Old" START (101::int) END (201::int) WITH (appendonly=false),
DEFAULT PARTITION other_log_ids WITH (appendonly=false)
);
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_other_log_ids" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_2" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_3" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_4" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_5" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_6" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_7" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_8" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_9" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_10" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_11" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_12" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_13" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_14" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_15" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_16" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_17" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_18" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_19" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_20" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_21" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_Old" for table "test_split_part"
insert into test_split_part (log_id , f_array) select id, '{10}' from generate_series(1,1000) id;
ALTER TABLE test_split_part SPLIT DEFAULT PARTITION START (201) INCLUSIVE END (301) EXCLUSIVE INTO (PARTITION "New", DEFAULT PARTITION);
NOTICE: exchanged partition "other_log_ids" of relation "test_split_part" with relation "pg_temp_325194"
NOTICE: dropped partition "other_log_ids" for relation "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_New" for table "test_split_part"
NOTICE: CREATE TABLE will create partition "test_split_part_1_prt_other_log_ids" for table "test_split_part"
17 changes: 17 additions & 0 deletions src/test/regress/sql/partition.sql
Expand Up @@ -3730,3 +3730,20 @@ select * from pt_td_leak where col1 = 5;

drop table pt_td_leak;
drop table pt_td_leak_exchange;

-- Test split default partition while per tuple memory context is reset
drop table if exists test_split_part cascade;

CREATE TABLE test_split_part ( log_id int NOT NULL, f_array int[] NOT NULL)
DISTRIBUTED BY (log_id)
PARTITION BY RANGE(log_id)
(
START (1::int) END (100::int) EVERY (5) WITH (appendonly=false),
PARTITION "Old" START (101::int) END (201::int) WITH (appendonly=false),
DEFAULT PARTITION other_log_ids WITH (appendonly=false)
);

insert into test_split_part (log_id , f_array) select id, '{10}' from generate_series(1,1000) id;

ALTER TABLE test_split_part SPLIT DEFAULT PARTITION START (201) INCLUSIVE END (301) EXCLUSIVE INTO (PARTITION "New", DEFAULT PARTITION);

0 comments on commit c0e1f00

Please sign in to comment.