Remove foreign keys between columnar metadata tables (#4791)

Postgres keeps AFTER trigger state for each transaction, because we can have deferred AFTER triggers which will be fired at the end of a transaction. Postgres cleans up this state at the end of transaction.

Postgres processes ON COMMIT triggers after cleaning-up the AFTER trigger states. So if we fire any triggers in ON COMMIT, the AFTER trigger state won't be cleaned-up properly and the transaction state will be left in an inconsistent state, which might result in assertion failure.

So with this commit, we remove foreign keys between columnar metadata tables and enforce constraints between them manually when dropping columnar tables.
pull/4806/head
Onur Tirtir 2021-03-12 11:28:17 +03:00 committed by GitHub
parent 71a9f45513
commit 874d5fd962
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 181 additions and 15 deletions

View File

@ -95,6 +95,10 @@ static Oid ColumnarChunkGroupRelationId(void);
static Oid ColumnarChunkIndexRelationId(void);
static Oid ColumnarChunkGroupIndexRelationId(void);
static Oid ColumnarNamespaceId(void);
static void DeleteStorageFromColumnarMetadataTable(Oid metadataTableId,
AttrNumber storageIdAtrrNumber,
Oid storageIdIndexId,
uint64 storageId);
static ModifyState * StartModifyRelation(Relation rel);
static void InsertTupleAndEnforceConstraints(ModifyState *state, Datum *values,
bool *nulls);
@ -951,13 +955,12 @@ ReadDataFileStripeList(uint64 storageId, Snapshot snapshot)
/*
* DeleteMetadataRows removes the rows with given relfilenode from columnar.stripe.
* DeleteMetadataRows removes the rows with given relfilenode from columnar
* metadata tables.
*/
void
DeleteMetadataRows(RelFileNode relfilenode)
{
ScanKeyData scanKey[1];
/*
* During a restore for binary upgrade, metadata tables and indexes may or
* may not exist.
@ -977,23 +980,47 @@ DeleteMetadataRows(RelFileNode relfilenode)
return;
}
ScanKeyInit(&scanKey[0], Anum_columnar_stripe_storageid,
BTEqualStrategyNumber, F_INT8EQ, UInt64GetDatum(metapage->storageId));
DeleteStorageFromColumnarMetadataTable(ColumnarStripeRelationId(),
Anum_columnar_stripe_storageid,
ColumnarStripeIndexRelationId(),
metapage->storageId);
DeleteStorageFromColumnarMetadataTable(ColumnarChunkGroupRelationId(),
Anum_columnar_chunkgroup_storageid,
ColumnarChunkGroupIndexRelationId(),
metapage->storageId);
DeleteStorageFromColumnarMetadataTable(ColumnarChunkRelationId(),
Anum_columnar_chunk_storageid,
ColumnarChunkIndexRelationId(),
metapage->storageId);
}
Oid columnarStripesOid = ColumnarStripeRelationId();
Relation columnarStripes = try_relation_open(columnarStripesOid, AccessShareLock);
if (columnarStripes == NULL)
/*
* DeleteStorageFromColumnarMetadataTable removes the rows with given
* storageId from given columnar metadata table.
*/
static void
DeleteStorageFromColumnarMetadataTable(Oid metadataTableId,
AttrNumber storageIdAtrrNumber,
Oid storageIdIndexId, uint64 storageId)
{
ScanKeyData scanKey[1];
ScanKeyInit(&scanKey[0], storageIdAtrrNumber, BTEqualStrategyNumber,
F_INT8EQ, UInt64GetDatum(storageId));
Relation metadataTable = try_relation_open(metadataTableId, AccessShareLock);
if (metadataTable == NULL)
{
/* extension has been dropped */
return;
}
Relation index = index_open(ColumnarStripeIndexRelationId(), AccessShareLock);
Relation index = index_open(storageIdIndexId, AccessShareLock);
SysScanDesc scanDescriptor = systable_beginscan_ordered(columnarStripes, index, NULL,
SysScanDesc scanDescriptor = systable_beginscan_ordered(metadataTable, index, NULL,
1, scanKey);
ModifyState *modifyState = StartModifyRelation(columnarStripes);
ModifyState *modifyState = StartModifyRelation(metadataTable);
HeapTuple heapTuple = systable_getnext(scanDescriptor);
while (HeapTupleIsValid(heapTuple))
@ -1006,7 +1033,7 @@ DeleteMetadataRows(RelFileNode relfilenode)
systable_endscan_ordered(scanDescriptor);
index_close(index, AccessShareLock);
table_close(columnarStripes, AccessShareLock);
table_close(metadataTable, AccessShareLock);
}

View File

@ -0,0 +1,18 @@
/* columnar--10.0-3--10.1-1.sql */
-- Drop foreign keys between columnar metadata tables.
-- Postgres assigns different names to those foreign keys in PG11, so act accordingly.
DO $proc$
BEGIN
IF substring(current_Setting('server_version'), '\d+')::int >= 12 THEN
EXECUTE $$
ALTER TABLE columnar.chunk DROP CONSTRAINT chunk_storage_id_stripe_num_chunk_group_num_fkey;
ALTER TABLE columnar.chunk_group DROP CONSTRAINT chunk_group_storage_id_stripe_num_fkey;
$$;
ELSE
EXECUTE $$
ALTER TABLE columnar.chunk DROP CONSTRAINT chunk_storage_id_fkey;
ALTER TABLE columnar.chunk_group DROP CONSTRAINT chunk_group_storage_id_fkey;
$$;
END IF;
END$proc$;

View File

@ -0,0 +1,10 @@
/* columnar--10.1-1--10.0-3.sql */
-- define foreign keys between columnar metadata tables
ALTER TABLE columnar.chunk
ADD FOREIGN KEY (storage_id, stripe_num, chunk_group_num)
REFERENCES columnar.chunk_group(storage_id, stripe_num, chunk_group_num) ON DELETE CASCADE;
ALTER TABLE columnar.chunk_group
ADD FOREIGN KEY (storage_id, stripe_num)
REFERENCES columnar.stripe(storage_id, stripe_num) ON DELETE CASCADE;

View File

@ -1,4 +1,3 @@
-- citus--10.0-3--10.1-1
-- bump version to 10.1-1
#include "../../columnar/sql/columnar--10.0-3--10.1-1.sql"

View File

@ -1,3 +1,3 @@
-- citus--10.1-1--10.0-2
-- this is an empty downgrade path since citus--10.0-3--10.1-1.sql is empty for now
#include "../../../columnar/sql/downgrades/columnar--10.1-1--10.0-3.sql"

View File

@ -67,3 +67,65 @@ WITH a as (
)
SELECT (SELECT count(*) = 0 FROM c) AND
(SELECT count(*) = 0 FROM f) as consistent;
CREATE FUNCTION columnar_metadata_has_storage_id(input_storage_id bigint) RETURNS boolean
AS $$
DECLARE
union_storage_id_count integer;
BEGIN
SELECT count(*) INTO union_storage_id_count FROM
(
SELECT storage_id FROM columnar.stripe UNION ALL
SELECT storage_id FROM columnar.chunk UNION ALL
SELECT storage_id FROM columnar.chunk_group
) AS union_storage_id
WHERE storage_id=input_storage_id;
IF union_storage_id_count > 0 THEN
RETURN true;
END IF;
RETURN false;
END;
$$ LANGUAGE plpgsql;
CREATE TABLE columnar_table_1 (a int) USING columnar;
INSERT INTO columnar_table_1 VALUES (1);
CREATE MATERIALIZED VIEW columnar_table_1_mv USING columnar
AS SELECT * FROM columnar_table_1;
SELECT columnar_relation_storageid(oid) AS columnar_table_1_mv_storage_id
FROM pg_class WHERE relname='columnar_table_1_mv' \gset
-- test columnar_relation_set_new_filenode
REFRESH MATERIALIZED VIEW columnar_table_1_mv;
SELECT columnar_metadata_has_storage_id(:columnar_table_1_mv_storage_id);
columnar_metadata_has_storage_id
---------------------------------------------------------------------
f
(1 row)
SELECT columnar_relation_storageid(oid) AS columnar_table_1_storage_id
FROM pg_class WHERE relname='columnar_table_1' \gset
BEGIN;
-- test columnar_relation_nontransactional_truncate
TRUNCATE columnar_table_1;
SELECT columnar_metadata_has_storage_id(:columnar_table_1_storage_id);
columnar_metadata_has_storage_id
---------------------------------------------------------------------
f
(1 row)
ROLLBACK;
-- since we rollback'ed above xact, should return true
SELECT columnar_metadata_has_storage_id(:columnar_table_1_storage_id);
columnar_metadata_has_storage_id
---------------------------------------------------------------------
t
(1 row)
-- test dropping columnar table
DROP TABLE columnar_table_1 CASCADE;
NOTICE: drop cascades to materialized view columnar_table_1_mv
SELECT columnar_metadata_has_storage_id(:columnar_table_1_storage_id);
columnar_metadata_has_storage_id
---------------------------------------------------------------------
f
(1 row)

View File

@ -71,3 +71,53 @@ WITH a as (
)
SELECT (SELECT count(*) = 0 FROM c) AND
(SELECT count(*) = 0 FROM f) as consistent;
CREATE FUNCTION columnar_metadata_has_storage_id(input_storage_id bigint) RETURNS boolean
AS $$
DECLARE
union_storage_id_count integer;
BEGIN
SELECT count(*) INTO union_storage_id_count FROM
(
SELECT storage_id FROM columnar.stripe UNION ALL
SELECT storage_id FROM columnar.chunk UNION ALL
SELECT storage_id FROM columnar.chunk_group
) AS union_storage_id
WHERE storage_id=input_storage_id;
IF union_storage_id_count > 0 THEN
RETURN true;
END IF;
RETURN false;
END;
$$ LANGUAGE plpgsql;
CREATE TABLE columnar_table_1 (a int) USING columnar;
INSERT INTO columnar_table_1 VALUES (1);
CREATE MATERIALIZED VIEW columnar_table_1_mv USING columnar
AS SELECT * FROM columnar_table_1;
SELECT columnar_relation_storageid(oid) AS columnar_table_1_mv_storage_id
FROM pg_class WHERE relname='columnar_table_1_mv' \gset
-- test columnar_relation_set_new_filenode
REFRESH MATERIALIZED VIEW columnar_table_1_mv;
SELECT columnar_metadata_has_storage_id(:columnar_table_1_mv_storage_id);
SELECT columnar_relation_storageid(oid) AS columnar_table_1_storage_id
FROM pg_class WHERE relname='columnar_table_1' \gset
BEGIN;
-- test columnar_relation_nontransactional_truncate
TRUNCATE columnar_table_1;
SELECT columnar_metadata_has_storage_id(:columnar_table_1_storage_id);
ROLLBACK;
-- since we rollback'ed above xact, should return true
SELECT columnar_metadata_has_storage_id(:columnar_table_1_storage_id);
-- test dropping columnar table
DROP TABLE columnar_table_1 CASCADE;
SELECT columnar_metadata_has_storage_id(:columnar_table_1_storage_id);