Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions src/backend/access/aocs/aocsam_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -1817,6 +1817,7 @@ aoco_index_build_range_scan(Relation heapRelation,
{
bool tupleIsAlive;
BlockNumber blockno;
AOTupleId *aoTupleId;

CHECK_FOR_INTERRUPTS();
blockno = ItemPointerGetBlockNumber(&slot->tts_tid);
Expand All @@ -1841,14 +1842,20 @@ aoco_index_build_range_scan(Relation heapRelation,
blockcounts);
}

aoTupleId = (AOTupleId *) &slot->tts_tid;
/*
* appendonly_getnext did the time qual check
*
* GPDB_12_MERGE_FIXME: in heapam, we do visibility checks in SnapshotAny case
* here. Is that not needed with AO_COLUMN tables?
* We didn't perform the check to see if the tuple was deleted in
* aocs_getnext(), since we passed it SnapshotAny. See aocs_getnext()
* for details. We need to do this to avoid spurious conflicts with
* deleted tuples for unique index builds.
*/
tupleIsAlive = true;
reltuples += 1;
if (AppendOnlyVisimap_IsVisible(&aocoscan->visibilityMap, aoTupleId))
{
tupleIsAlive = true;
reltuples += 1;
}
else
tupleIsAlive = false; /* excluded from unique-checking */

MemoryContextReset(econtext->ecxt_per_tuple_memory);

Expand Down
19 changes: 13 additions & 6 deletions src/backend/access/appendonly/appendonlyam_handler.c
Original file line number Diff line number Diff line change
Expand Up @@ -1700,6 +1700,7 @@ appendonly_index_build_range_scan(Relation heapRelation,
while (appendonly_getnextslot(&aoscan->rs_base, ForwardScanDirection, slot))
{
bool tupleIsAlive;
AOTupleId *aoTupleId;

CHECK_FOR_INTERRUPTS();

Expand All @@ -1718,14 +1719,20 @@ appendonly_index_build_range_scan(Relation heapRelation,
pgstat_progress_update_param(PROGRESS_SCAN_BLOCKS_DONE, hscan->rs_nblocks);
}

aoTupleId = (AOTupleId *) &slot->tts_tid;
/*
* appendonly_getnext did the time qual check
*
* GPDB_12_MERGE_FIXME: in heapam, we do visibility checks in SnapshotAny case
* here. Is that not needed with AO tables?
* We didn't perform the check to see if the tuple was deleted in
* appendonlygettup(), since we passed it SnapshotAny. See
* appendonlygettup() for details. We need to do this to avoid spurious
* conflicts with deleted tuples for unique index builds.
*/
tupleIsAlive = true;
reltuples += 1;
if (AppendOnlyVisimap_IsVisible(&aoscan->visibilityMap, aoTupleId))
{
tupleIsAlive = true;
reltuples += 1;
}
else
tupleIsAlive = false; /* excluded from unique-checking */

MemoryContextReset(econtext->ecxt_per_tuple_memory);

Expand Down
101 changes: 101 additions & 0 deletions src/test/isolation2/input/uao/index_build_reltuples.source
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
-- Coverage to ensure that reltuples is updated correctly upon an index build
-- (i.e. CREATE INDEX) on AO/CO tables.
-- FIXME: Currently doesn't assert reltuples on QD (at the moment, we don't
-- aggregate the reltuples counts on QD at end of command)

SET default_table_access_method TO @amname@;

-- Case 1: Verify that CREATE INDEX is able to update both the aorel's reltuples
-- and the index's reltuples, to equal the actual segment tuple counts.

CREATE TABLE index_build_reltuples_@amname@(a int);
INSERT INTO index_build_reltuples_@amname@ SELECT generate_series(1, 10);

CREATE INDEX ON index_build_reltuples_@amname@(a);

SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@
GROUP BY gp_segment_id ORDER BY gp_segment_id;
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class')
WHERE relname='index_build_reltuples_@amname@' ORDER BY gp_segment_id;
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class')
WHERE relname='index_build_reltuples_@amname@_a_idx' ORDER BY gp_segment_id;

DROP TABLE index_build_reltuples_@amname@;

-- Case 2: Verify that CREATE INDEX is able to update the aorel's reltuples
-- to equal the actual segment tuple counts, when there are deleted tuples. For
-- the index, since we don't have a notion of "recently dead" vs surely dead,
-- we are conservative and form index entries even for deleted tuples. Thus, the
-- reltuples count for the index would also account for deleted tuples.

CREATE TABLE index_build_reltuples_@amname@(a int);
INSERT INTO index_build_reltuples_@amname@ SELECT generate_series(1, 20);

SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@
GROUP BY gp_segment_id ORDER BY gp_segment_id;

DELETE FROM index_build_reltuples_@amname@ WHERE a <= 10;

CREATE INDEX ON index_build_reltuples_@amname@(a);

SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@
GROUP BY gp_segment_id ORDER BY gp_segment_id;
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class')
WHERE relname='index_build_reltuples_@amname@' ORDER BY gp_segment_id;
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class')
WHERE relname='index_build_reltuples_@amname@_a_idx' ORDER BY gp_segment_id;

DROP TABLE index_build_reltuples_@amname@;

-- Case 3: Verify that CREATE INDEX is able to update both the aorel's reltuples
-- and the index's reltuples, to equal the actual segment tuple counts, when
-- there are aborted tuples.

CREATE TABLE index_build_reltuples_@amname@(a int);

INSERT INTO index_build_reltuples_@amname@ SELECT generate_series(1, 10);

SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@
GROUP BY gp_segment_id ORDER BY gp_segment_id;

BEGIN;
INSERT INTO index_build_reltuples_@amname@ SELECT generate_series(11, 20);
ABORT;

CREATE INDEX ON index_build_reltuples_@amname@(a);

SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@
GROUP BY gp_segment_id ORDER BY gp_segment_id;
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class')
WHERE relname='index_build_reltuples_@amname@' ORDER BY gp_segment_id;
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class')
WHERE relname='index_build_reltuples_@amname@_a_idx' ORDER BY gp_segment_id;

DROP TABLE index_build_reltuples_@amname@;

-- Case 4: Verify that CREATE INDEX is able to update both the aorel's reltuples
-- and the index's reltuples, to equal the latest segment tuple counts, even
-- when it is executed in a transaction with a snapshot that precedes the INSERT
-- (highlights the need for using SnapshotAny)

CREATE TABLE index_build_reltuples_@amname@(a int);

1: BEGIN ISOLATION LEVEL REPEATABLE READ;
1: SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@
GROUP BY gp_segment_id ORDER BY gp_segment_id;

INSERT INTO index_build_reltuples_@amname@ SELECT generate_series(1, 10);

1: CREATE INDEX ON index_build_reltuples_@amname@(a);
1: COMMIT;

SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@
GROUP BY gp_segment_id ORDER BY gp_segment_id;
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class')
WHERE relname='index_build_reltuples_@amname@' ORDER BY gp_segment_id;
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class')
WHERE relname='index_build_reltuples_@amname@_a_idx' ORDER BY gp_segment_id;

DROP TABLE index_build_reltuples_@amname@;

RESET default_table_access_method;
2 changes: 2 additions & 0 deletions src/test/isolation2/isolation2_schedule
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ test: uao/cursor_before_update_row
test: uao/cursor_withhold_row
test: uao/cursor_withhold2_row
test: uao/delete_while_vacuum_row
test: uao/index_build_reltuples_row
test: uao/insert_policy_row
test: uao/insert_while_vacuum_row
test: uao/max_concurrency_row
Expand Down Expand Up @@ -175,6 +176,7 @@ test: uao/cursor_before_update_column
test: uao/cursor_withhold_column
test: uao/cursor_withhold2_column
test: uao/delete_while_vacuum_column
test: uao/index_build_reltuples_column
test: uao/insert_policy_column
test: uao/insert_while_vacuum_column
test: uao/max_concurrency_column
Expand Down
197 changes: 197 additions & 0 deletions src/test/isolation2/output/uao/index_build_reltuples.source
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
-- Coverage to ensure that reltuples is updated correctly upon an index build
-- (i.e. CREATE INDEX) on AO/CO tables.
-- FIXME: Currently doesn't assert reltuples on QD (at the moment, we don't
-- aggregate the reltuples counts on QD at end of command)

SET default_table_access_method TO @amname@;
SET

-- Case 1: Verify that CREATE INDEX is able to update both the aorel's reltuples
-- and the index's reltuples, to equal the actual segment tuple counts.

CREATE TABLE index_build_reltuples_@amname@(a int);
CREATE
INSERT INTO index_build_reltuples_@amname@ SELECT generate_series(1, 10);
INSERT 10

CREATE INDEX ON index_build_reltuples_@amname@(a);
CREATE

SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@ GROUP BY gp_segment_id ORDER BY gp_segment_id;
gp_segment_id | count
---------------+-------
0 | 5
1 | 1
2 | 4
(3 rows)
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class') WHERE relname='index_build_reltuples_@amname@' ORDER BY gp_segment_id;
gp_segment_id | reltuples
---------------+-----------
0 | 5
1 | 1
2 | 4
(3 rows)
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class') WHERE relname='index_build_reltuples_@amname@_a_idx' ORDER BY gp_segment_id;
gp_segment_id | reltuples
---------------+-----------
0 | 5
1 | 1
2 | 4
(3 rows)

DROP TABLE index_build_reltuples_@amname@;
DROP

-- Case 2: Verify that CREATE INDEX is able to update the aorel's reltuples
-- to equal the actual segment tuple counts, when there are deleted tuples. For
-- the index, since we don't have a notion of "recently dead" vs surely dead,
-- we are conservative and form index entries even for deleted tuples. Thus, the
-- reltuples count for the index would also account for deleted tuples.

CREATE TABLE index_build_reltuples_@amname@(a int);
CREATE
INSERT INTO index_build_reltuples_@amname@ SELECT generate_series(1, 20);
INSERT 20

SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@ GROUP BY gp_segment_id ORDER BY gp_segment_id;
gp_segment_id | count
---------------+-------
0 | 8
1 | 4
2 | 8
(3 rows)

DELETE FROM index_build_reltuples_@amname@ WHERE a <= 10;
DELETE 10

CREATE INDEX ON index_build_reltuples_@amname@(a);
CREATE

SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@ GROUP BY gp_segment_id ORDER BY gp_segment_id;
gp_segment_id | count
---------------+-------
0 | 3
1 | 3
2 | 4
(3 rows)
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class') WHERE relname='index_build_reltuples_@amname@' ORDER BY gp_segment_id;
gp_segment_id | reltuples
---------------+-----------
0 | 3
1 | 3
2 | 4
(3 rows)
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class') WHERE relname='index_build_reltuples_@amname@_a_idx' ORDER BY gp_segment_id;
gp_segment_id | reltuples
---------------+-----------
0 | 8
1 | 4
2 | 8
(3 rows)

DROP TABLE index_build_reltuples_@amname@;
DROP

-- Case 3: Verify that CREATE INDEX is able to update both the aorel's reltuples
-- and the index's reltuples, to equal the actual segment tuple counts, when
-- there are aborted tuples.

CREATE TABLE index_build_reltuples_@amname@(a int);
CREATE

INSERT INTO index_build_reltuples_@amname@ SELECT generate_series(1, 10);
INSERT 10

SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@ GROUP BY gp_segment_id ORDER BY gp_segment_id;
gp_segment_id | count
---------------+-------
0 | 5
1 | 1
2 | 4
(3 rows)

BEGIN;
BEGIN
INSERT INTO index_build_reltuples_@amname@ SELECT generate_series(11, 20);
INSERT 10
ABORT;
ABORT

CREATE INDEX ON index_build_reltuples_@amname@(a);
CREATE

SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@ GROUP BY gp_segment_id ORDER BY gp_segment_id;
gp_segment_id | count
---------------+-------
0 | 5
1 | 1
2 | 4
(3 rows)
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class') WHERE relname='index_build_reltuples_@amname@' ORDER BY gp_segment_id;
gp_segment_id | reltuples
---------------+-----------
0 | 5
1 | 1
2 | 4
(3 rows)
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class') WHERE relname='index_build_reltuples_@amname@_a_idx' ORDER BY gp_segment_id;
gp_segment_id | reltuples
---------------+-----------
0 | 5
1 | 1
2 | 4
(3 rows)

DROP TABLE index_build_reltuples_@amname@;
DROP

-- Case 4: Verify that CREATE INDEX is able to update both the aorel's reltuples
-- and the index's reltuples, to equal the latest segment tuple counts, even
-- when it is executed in a transaction with a snapshot that precedes the INSERT
-- (highlights the need for using SnapshotAny)

CREATE TABLE index_build_reltuples_@amname@(a int);
CREATE

1: BEGIN ISOLATION LEVEL REPEATABLE READ;
BEGIN
1: SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@ GROUP BY gp_segment_id ORDER BY gp_segment_id;
gp_segment_id | count
---------------+-------
(0 rows)

INSERT INTO index_build_reltuples_@amname@ SELECT generate_series(1, 10);
INSERT 10

1: CREATE INDEX ON index_build_reltuples_@amname@(a);
CREATE
1: COMMIT;
COMMIT

SELECT gp_segment_id, count(*) FROM index_build_reltuples_@amname@ GROUP BY gp_segment_id ORDER BY gp_segment_id;
gp_segment_id | count
---------------+-------
0 | 5
1 | 1
2 | 4
(3 rows)
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class') WHERE relname='index_build_reltuples_@amname@' ORDER BY gp_segment_id;
gp_segment_id | reltuples
---------------+-----------
0 | 5
1 | 1
2 | 4
(3 rows)
SELECT gp_segment_id, reltuples FROM gp_dist_random('pg_class') WHERE relname='index_build_reltuples_@amname@_a_idx' ORDER BY gp_segment_id;
gp_segment_id | reltuples
---------------+-----------
0 | 5
1 | 1
2 | 4
(3 rows)

DROP TABLE index_build_reltuples_@amname@;
DROP

RESET default_table_access_method;
RESET
3 changes: 3 additions & 0 deletions src/test/regress/greenplum_schedule
Original file line number Diff line number Diff line change
Expand Up @@ -335,4 +335,7 @@ test: bfv_meta_track
# tests of ao/aoco seg file count for parallel plan
test: ao_segfile

# test CREATE UNIQUE INDEX on AO/CO tables.
test: uao_dml/ao_unique_index_build_row uao_dml/ao_unique_index_build_column

# end of tests