diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out index f0498aa302..cb44941ede 100644 --- a/contrib/test_decoding/expected/ddl.out +++ b/contrib/test_decoding/expected/ddl.out @@ -227,23 +227,28 @@ CREATE TABLE tr_etoomuch (id serial primary key, data int); INSERT INTO tr_etoomuch(data) SELECT g.i FROM generate_series(1, 10234) g(i); DELETE FROM tr_etoomuch WHERE id < 5000; UPDATE tr_etoomuch SET data = - data WHERE id > 5000; +CREATE TABLE tr_oddlength (id text primary key, data text); +INSERT INTO tr_oddlength VALUES('ab', 'foo'); COMMIT; /* display results, but hide most of the output */ SELECT count(*), min(data), max(data) FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1') GROUP BY substring(data, 1, 24) ORDER BY 1,2; - count | min | max --------+-------------------------------------------------+------------------------------------------------------------------------ - 1 | BEGIN | BEGIN - 1 | COMMIT | COMMIT - 20467 | table public.tr_etoomuch: DELETE: id[integer]:1 | table public.tr_etoomuch: UPDATE: id[integer]:9999 data[integer]:-9999 -(3 rows) + count | min | max +-------+-------------------------------------------------------------------+------------------------------------------------------------------------ + 1 | BEGIN | BEGIN + 1 | COMMIT | COMMIT + 1 | table public.tr_oddlength: INSERT: id[text]:'ab' data[text]:'foo' | table public.tr_oddlength: INSERT: id[text]:'ab' data[text]:'foo' + 20467 | table public.tr_etoomuch: DELETE: id[integer]:1 | table public.tr_etoomuch: UPDATE: id[integer]:9999 data[integer]:-9999 +(4 rows) -- check updates of primary keys work correctly BEGIN; CREATE TABLE spoolme AS SELECT g.i FROM generate_series(1, 5000) g(i); UPDATE tr_etoomuch SET id = -id WHERE id = 5000; +UPDATE tr_oddlength SET id = 'x', data = 'quux'; +UPDATE tr_oddlength SET id = 'yy', data = 'a'; DELETE FROM spoolme; DROP TABLE spoolme; COMMIT; @@ -253,7 +258,9 @@ WHERE data ~ 'UPDATE'; data ------------------------------------------------------------------------------------------------------------- table public.tr_etoomuch: UPDATE: old-key: id[integer]:5000 new-tuple: id[integer]:-5000 data[integer]:5000 -(1 row) + table public.tr_oddlength: UPDATE: old-key: id[text]:'ab' new-tuple: id[text]:'x' data[text]:'quux' + table public.tr_oddlength: UPDATE: old-key: id[text]:'x' new-tuple: id[text]:'yy' data[text]:'a' +(3 rows) -- check that a large, spooled, upsert works INSERT INTO tr_etoomuch (id, data) diff --git a/contrib/test_decoding/sql/ddl.sql b/contrib/test_decoding/sql/ddl.sql index ad928ad572..228fe33685 100644 --- a/contrib/test_decoding/sql/ddl.sql +++ b/contrib/test_decoding/sql/ddl.sql @@ -115,6 +115,8 @@ CREATE TABLE tr_etoomuch (id serial primary key, data int); INSERT INTO tr_etoomuch(data) SELECT g.i FROM generate_series(1, 10234) g(i); DELETE FROM tr_etoomuch WHERE id < 5000; UPDATE tr_etoomuch SET data = - data WHERE id > 5000; +CREATE TABLE tr_oddlength (id text primary key, data text); +INSERT INTO tr_oddlength VALUES('ab', 'foo'); COMMIT; /* display results, but hide most of the output */ @@ -127,6 +129,8 @@ ORDER BY 1,2; BEGIN; CREATE TABLE spoolme AS SELECT g.i FROM generate_series(1, 5000) g(i); UPDATE tr_etoomuch SET id = -id WHERE id = 5000; +UPDATE tr_oddlength SET id = 'x', data = 'quux'; +UPDATE tr_oddlength SET id = 'yy', data = 'a'; DELETE FROM spoolme; DROP TABLE spoolme; COMMIT; diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c index d3830095c7..1b6aa7b60f 100644 --- a/src/backend/replication/logical/reorderbuffer.c +++ b/src/backend/replication/logical/reorderbuffer.c @@ -2347,6 +2347,10 @@ ReorderBufferRestoreChanges(ReorderBuffer *rb, ReorderBufferTXN *txn, /* * Convert change from its on-disk format to in-memory format and queue it onto * the TXN's ->changes list. + * + * Note: although "data" is declared char*, at entry it points to a + * maxalign'd buffer, making it safe in most of this function to assume + * that the pointed-to data is suitably aligned for direct access. */ static void ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn, @@ -2374,7 +2378,7 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn, case REORDER_BUFFER_CHANGE_INTERNAL_SPEC_INSERT: if (change->data.tp.oldtuple) { - Size tuplelen = ((HeapTuple) data)->t_len; + uint32 tuplelen = ((HeapTuple) data)->t_len; change->data.tp.oldtuple = ReorderBufferGetTupleBuf(rb, tuplelen - SizeofHeapTupleHeader); @@ -2395,7 +2399,11 @@ ReorderBufferRestoreChange(ReorderBuffer *rb, ReorderBufferTXN *txn, if (change->data.tp.newtuple) { - Size tuplelen = ((HeapTuple) data)->t_len; + /* here, data might not be suitably aligned! */ + uint32 tuplelen; + + memcpy(&tuplelen, data + offsetof(HeapTupleData, t_len), + sizeof(uint32)); change->data.tp.newtuple = ReorderBufferGetTupleBuf(rb, tuplelen - SizeofHeapTupleHeader);