aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/test_decoding/expected/decoding_in_xact.out48
-rw-r--r--contrib/test_decoding/sql/decoding_in_xact.sql27
-rw-r--r--src/backend/access/transam/xact.c13
-rw-r--r--src/backend/utils/cache/relcache.c54
4 files changed, 122 insertions, 20 deletions
diff --git a/contrib/test_decoding/expected/decoding_in_xact.out b/contrib/test_decoding/expected/decoding_in_xact.out
index b65253f4630..ac03ff12030 100644
--- a/contrib/test_decoding/expected/decoding_in_xact.out
+++ b/contrib/test_decoding/expected/decoding_in_xact.out
@@ -79,6 +79,54 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
COMMIT
(6 rows)
+-- Decoding works in transaction that issues DDL
+--
+-- We had issues handling relcache invalidations with these, see
+-- https://www.postgresql.org/message-id/e56be7d9-14b1-664d-0bfc-00ce9772721c@gmail.com
+CREATE TABLE tbl_created_outside_xact(id SERIAL PRIMARY KEY);
+BEGIN;
+ -- TRUNCATE changes the relfilenode and sends relcache invalidation
+ TRUNCATE tbl_created_outside_xact;
+ INSERT INTO tbl_created_outside_xact(id) VALUES('1');
+ -- don't show yet, haven't committed
+ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+ data
+------
+(0 rows)
+
+COMMIT;
+SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+ data
+--------------------------------------------------------------
+ BEGIN
+ table public.tbl_created_outside_xact: TRUNCATE: (no-flags)
+ table public.tbl_created_outside_xact: INSERT: id[integer]:1
+ COMMIT
+(4 rows)
+
+SET debug_logical_replication_streaming = immediate;
+BEGIN;
+ CREATE TABLE tbl_created_in_xact(id SERIAL PRIMARY KEY);
+ INSERT INTO tbl_created_in_xact VALUES (1);
+ CHECKPOINT; -- Force WAL flush, so that the above changes will be streamed
+ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
+ data
+------------------------------------------
+ opening a streamed block for transaction
+ streaming change for transaction
+ closing a streamed block for transaction
+(3 rows)
+
+COMMIT;
+RESET debug_logical_replication_streaming;
+SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+ data
+---------------------------------------------------------
+ BEGIN
+ table public.tbl_created_in_xact: INSERT: id[integer]:1
+ COMMIT
+(3 rows)
+
SELECT 'stop' FROM pg_drop_replication_slot('regression_slot');
?column?
----------
diff --git a/contrib/test_decoding/sql/decoding_in_xact.sql b/contrib/test_decoding/sql/decoding_in_xact.sql
index 108782dc2e9..233ade5b6ce 100644
--- a/contrib/test_decoding/sql/decoding_in_xact.sql
+++ b/contrib/test_decoding/sql/decoding_in_xact.sql
@@ -38,4 +38,31 @@ COMMIT;
INSERT INTO nobarf(data) VALUES('3');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+-- Decoding works in transaction that issues DDL
+--
+-- We had issues handling relcache invalidations with these, see
+-- https://www.postgresql.org/message-id/e56be7d9-14b1-664d-0bfc-00ce9772721c@gmail.com
+CREATE TABLE tbl_created_outside_xact(id SERIAL PRIMARY KEY);
+BEGIN;
+ -- TRUNCATE changes the relfilenode and sends relcache invalidation
+ TRUNCATE tbl_created_outside_xact;
+ INSERT INTO tbl_created_outside_xact(id) VALUES('1');
+
+ -- don't show yet, haven't committed
+ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+COMMIT;
+SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+
+SET debug_logical_replication_streaming = immediate;
+BEGIN;
+ CREATE TABLE tbl_created_in_xact(id SERIAL PRIMARY KEY);
+ INSERT INTO tbl_created_in_xact VALUES (1);
+
+ CHECKPOINT; -- Force WAL flush, so that the above changes will be streamed
+
+ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1', 'stream-changes', '1');
+COMMIT;
+RESET debug_logical_replication_streaming;
+SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
+
SELECT 'stop' FROM pg_drop_replication_slot('regression_slot');
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 4f4ce757623..9bda1aa6bc6 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -5279,20 +5279,7 @@ AbortSubTransaction(void)
AtEOSubXact_RelationCache(false, s->subTransactionId,
s->parent->subTransactionId);
-
-
- /*
- * AtEOSubXact_Inval sometimes needs to temporarily bump the refcount
- * on the relcache entries that it processes. We cannot use the
- * subtransaction's resource owner anymore, because we've already
- * started releasing it. But we can use the parent resource owner.
- */
- CurrentResourceOwner = s->parent->curTransactionOwner;
-
AtEOSubXact_Inval(false);
-
- CurrentResourceOwner = s->curTransactionOwner;
-
ResourceOwnerRelease(s->curTransactionOwner,
RESOURCE_RELEASE_LOCKS,
false, false);
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index cc9b0c6524f..35dbb87ae3d 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -275,6 +275,7 @@ static HTAB *OpClassCache = NULL;
static void RelationCloseCleanup(Relation relation);
static void RelationDestroyRelation(Relation relation, bool remember_tupdesc);
+static void RelationInvalidateRelation(Relation relation);
static void RelationClearRelation(Relation relation, bool rebuild);
static void RelationReloadIndexInfo(Relation relation);
@@ -2513,6 +2514,31 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
}
/*
+ * RelationInvalidateRelation - mark a relation cache entry as invalid
+ *
+ * An entry that's marked as invalid will be reloaded on next access.
+ */
+static void
+RelationInvalidateRelation(Relation relation)
+{
+ /*
+ * Make sure smgr and lower levels close the relation's files, if they
+ * weren't closed already. If the relation is not getting deleted, the
+ * next smgr access should reopen the files automatically. This ensures
+ * that the low-level file access state is updated after, say, a vacuum
+ * truncation.
+ */
+ RelationCloseSmgr(relation);
+
+ /* Free AM cached data, if any */
+ if (relation->rd_amcache)
+ pfree(relation->rd_amcache);
+ relation->rd_amcache = NULL;
+
+ relation->rd_isvalid = false;
+}
+
+/*
* RelationClearRelation
*
* Physically blow away a relation cache entry, or reset it and rebuild
@@ -2846,14 +2872,28 @@ RelationFlushRelation(Relation relation)
* New relcache entries are always rebuilt, not flushed; else we'd
* forget the "new" status of the relation. Ditto for the
* new-relfilenumber status.
- *
- * The rel could have zero refcnt here, so temporarily increment the
- * refcnt to ensure it's safe to rebuild it. We can assume that the
- * current transaction has some lock on the rel already.
*/
- RelationIncrementReferenceCount(relation);
- RelationClearRelation(relation, true);
- RelationDecrementReferenceCount(relation);
+ if (IsTransactionState() && relation->rd_droppedSubid == InvalidSubTransactionId)
+ {
+ /*
+ * The rel could have zero refcnt here, so temporarily increment
+ * the refcnt to ensure it's safe to rebuild it. We can assume
+ * that the current transaction has some lock on the rel already.
+ */
+ RelationIncrementReferenceCount(relation);
+ RelationClearRelation(relation, true);
+ RelationDecrementReferenceCount(relation);
+ }
+ else
+ {
+ /*
+ * During abort processing, the current resource owner is not
+ * valid and we cannot hold a refcnt. Without a valid
+ * transaction, RelationClearRelation() would just mark the rel as
+ * invalid anyway, so we can do the same directly.
+ */
+ RelationInvalidateRelation(relation);
+ }
}
else
{