mirror of https://github.com/citusdata/citus.git
commit
54f0e8619a
|
@ -14,7 +14,7 @@
|
|||
#include "access/tableam.h"
|
||||
#include "access/tsmapi.h"
|
||||
#if PG_VERSION_NUM >= 130000
|
||||
#include "access/heaptoast.h"
|
||||
#include "access/detoast.h"
|
||||
#else
|
||||
#include "access/tuptoaster.h"
|
||||
#endif
|
||||
|
@ -106,9 +106,10 @@ static void LogRelationStats(Relation rel, int elevel);
|
|||
static void TruncateColumnar(Relation rel, int elevel);
|
||||
static HeapTuple ColumnarSlotCopyHeapTuple(TupleTableSlot *slot);
|
||||
static void ColumnarCheckLogicalReplication(Relation rel);
|
||||
static Datum * detoast_values(TupleDesc tupleDesc, Datum *orig_values, bool *isnull);
|
||||
|
||||
/* Custom tuple slot ops used for columnar. Initialized in columnar_tableam_init(). */
|
||||
TupleTableSlotOps TTSOpsColumnar;
|
||||
static TupleTableSlotOps TTSOpsColumnar;
|
||||
|
||||
static List *
|
||||
RelationColumnList(Relation rel)
|
||||
|
@ -435,24 +436,16 @@ columnar_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
|
|||
TableWriteState *writeState = columnar_init_write_state(relation,
|
||||
RelationGetDescr(relation),
|
||||
GetCurrentSubTransactionId());
|
||||
|
||||
MemoryContext oldContext = MemoryContextSwitchTo(writeState->perTupleContext);
|
||||
|
||||
HeapTuple heapTuple = ExecCopySlotHeapTuple(slot);
|
||||
|
||||
ColumnarCheckLogicalReplication(relation);
|
||||
if (HeapTupleHasExternal(heapTuple))
|
||||
{
|
||||
/* detoast any toasted attributes */
|
||||
HeapTuple newTuple = toast_flatten_tuple(heapTuple,
|
||||
slot->tts_tupleDescriptor);
|
||||
|
||||
ExecForceStoreHeapTuple(newTuple, slot, true);
|
||||
}
|
||||
|
||||
slot_getallattrs(slot);
|
||||
|
||||
ColumnarWriteRow(writeState, slot->tts_values, slot->tts_isnull);
|
||||
Datum *values = detoast_values(slot->tts_tupleDescriptor,
|
||||
slot->tts_values, slot->tts_isnull);
|
||||
|
||||
ColumnarWriteRow(writeState, values, slot->tts_isnull);
|
||||
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
MemoryContextReset(writeState->perTupleContext);
|
||||
|
@ -485,28 +478,23 @@ columnar_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
|
|||
GetCurrentSubTransactionId());
|
||||
|
||||
ColumnarCheckLogicalReplication(relation);
|
||||
|
||||
MemoryContext oldContext = MemoryContextSwitchTo(writeState->perTupleContext);
|
||||
|
||||
for (int i = 0; i < ntuples; i++)
|
||||
{
|
||||
TupleTableSlot *tupleSlot = slots[i];
|
||||
MemoryContext oldContext = MemoryContextSwitchTo(writeState->perTupleContext);
|
||||
HeapTuple heapTuple = ExecCopySlotHeapTuple(tupleSlot);
|
||||
|
||||
if (HeapTupleHasExternal(heapTuple))
|
||||
{
|
||||
/* detoast any toasted attributes */
|
||||
HeapTuple newTuple = toast_flatten_tuple(heapTuple,
|
||||
tupleSlot->tts_tupleDescriptor);
|
||||
|
||||
ExecForceStoreHeapTuple(newTuple, tupleSlot, true);
|
||||
}
|
||||
|
||||
slot_getallattrs(tupleSlot);
|
||||
|
||||
ColumnarWriteRow(writeState, tupleSlot->tts_values, tupleSlot->tts_isnull);
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
Datum *values = detoast_values(tupleSlot->tts_tupleDescriptor,
|
||||
tupleSlot->tts_values, tupleSlot->tts_isnull);
|
||||
|
||||
ColumnarWriteRow(writeState, values, tupleSlot->tts_isnull);
|
||||
MemoryContextReset(writeState->perTupleContext);
|
||||
}
|
||||
|
||||
MemoryContextReset(writeState->perTupleContext);
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1400,6 +1388,45 @@ columnar_handler(PG_FUNCTION_ARGS)
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
* detoast_values
|
||||
*
|
||||
* Detoast and decompress all values. If there's no work to do, return
|
||||
* original pointer; otherwise return a newly-allocated values array. Should
|
||||
* be called in per-tuple context.
|
||||
*/
|
||||
static Datum *
|
||||
detoast_values(TupleDesc tupleDesc, Datum *orig_values, bool *isnull)
|
||||
{
|
||||
int natts = tupleDesc->natts;
|
||||
|
||||
/* copy on write to optimize for case where nothing is toasted */
|
||||
Datum *values = orig_values;
|
||||
|
||||
for (int i = 0; i < tupleDesc->natts; i++)
|
||||
{
|
||||
if (!isnull[i] && tupleDesc->attrs[i].attlen == -1 &&
|
||||
VARATT_IS_EXTENDED(values[i]))
|
||||
{
|
||||
/* make a copy */
|
||||
if (values == orig_values)
|
||||
{
|
||||
values = palloc(sizeof(Datum) * natts);
|
||||
memcpy_s(values, sizeof(Datum) * natts,
|
||||
orig_values, sizeof(Datum) * natts);
|
||||
}
|
||||
|
||||
/* will be freed when per-tuple context is reset */
|
||||
struct varlena *new_value = (struct varlena *) DatumGetPointer(values[i]);
|
||||
new_value = detoast_attr(new_value);
|
||||
values[i] = PointerGetDatum(new_value);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ColumnarCheckLogicalReplication throws an error if the relation is
|
||||
* part of any publication. This should be called before any write to
|
||||
|
|
|
@ -43,4 +43,8 @@
|
|||
#define table_endscan heap_endscan
|
||||
#endif
|
||||
|
||||
#if PG_VERSION_NUM < 130000
|
||||
#define detoast_attr(X) heap_tuple_untoast_attr(X)
|
||||
#endif
|
||||
|
||||
#endif /* COLUMNAR_COMPAT_H */
|
||||
|
|
|
@ -96,3 +96,38 @@ DROP PUBLICATION test_columnar_publication;
|
|||
-- should succeed
|
||||
INSERT INTO test_logical_replication VALUES (3);
|
||||
DROP TABLE test_logical_replication;
|
||||
--
|
||||
-- test toast interactions
|
||||
--
|
||||
-- row table with data in different storage formats
|
||||
CREATE TABLE test_toast_row(plain TEXT, main TEXT, external TEXT, extended TEXT);
|
||||
ALTER TABLE test_toast_row ALTER COLUMN plain SET STORAGE plain; -- inline, uncompressed
|
||||
ALTER TABLE test_toast_row ALTER COLUMN main SET STORAGE main; -- inline, compressed
|
||||
ALTER TABLE test_toast_row ALTER COLUMN external SET STORAGE external; -- out-of-line, uncompressed
|
||||
ALTER TABLE test_toast_row ALTER COLUMN extended SET STORAGE extended; -- out-of-line, compressed
|
||||
INSERT INTO test_toast_row VALUES(
|
||||
repeat('w', 5000), repeat('x', 5000), repeat('y', 5000), repeat('z', 5000));
|
||||
SELECT
|
||||
pg_column_size(plain), pg_column_size(main),
|
||||
pg_column_size(external), pg_column_size(extended)
|
||||
FROM test_toast_row;
|
||||
pg_column_size | pg_column_size | pg_column_size | pg_column_size
|
||||
---------------------------------------------------------------------
|
||||
5004 | 69 | 5000 | 65
|
||||
(1 row)
|
||||
|
||||
CREATE TABLE test_toast_columnar(plain TEXT, main TEXT, external TEXT, extended TEXT)
|
||||
USING columnar;
|
||||
INSERT INTO test_toast_columnar SELECT plain, main, external, extended
|
||||
FROM test_toast_row;
|
||||
SELECT
|
||||
pg_column_size(plain), pg_column_size(main),
|
||||
pg_column_size(external), pg_column_size(extended)
|
||||
FROM test_toast_columnar;
|
||||
pg_column_size | pg_column_size | pg_column_size | pg_column_size
|
||||
---------------------------------------------------------------------
|
||||
5004 | 5004 | 5004 | 5004
|
||||
(1 row)
|
||||
|
||||
DROP TABLE test_toast_row;
|
||||
DROP TABLE test_toast_columnar;
|
||||
|
|
|
@ -66,3 +66,34 @@ DROP PUBLICATION test_columnar_publication;
|
|||
-- should succeed
|
||||
INSERT INTO test_logical_replication VALUES (3);
|
||||
DROP TABLE test_logical_replication;
|
||||
|
||||
--
|
||||
-- test toast interactions
|
||||
--
|
||||
|
||||
-- row table with data in different storage formats
|
||||
CREATE TABLE test_toast_row(plain TEXT, main TEXT, external TEXT, extended TEXT);
|
||||
ALTER TABLE test_toast_row ALTER COLUMN plain SET STORAGE plain; -- inline, uncompressed
|
||||
ALTER TABLE test_toast_row ALTER COLUMN main SET STORAGE main; -- inline, compressed
|
||||
ALTER TABLE test_toast_row ALTER COLUMN external SET STORAGE external; -- out-of-line, uncompressed
|
||||
ALTER TABLE test_toast_row ALTER COLUMN extended SET STORAGE extended; -- out-of-line, compressed
|
||||
|
||||
INSERT INTO test_toast_row VALUES(
|
||||
repeat('w', 5000), repeat('x', 5000), repeat('y', 5000), repeat('z', 5000));
|
||||
|
||||
SELECT
|
||||
pg_column_size(plain), pg_column_size(main),
|
||||
pg_column_size(external), pg_column_size(extended)
|
||||
FROM test_toast_row;
|
||||
|
||||
CREATE TABLE test_toast_columnar(plain TEXT, main TEXT, external TEXT, extended TEXT)
|
||||
USING columnar;
|
||||
INSERT INTO test_toast_columnar SELECT plain, main, external, extended
|
||||
FROM test_toast_row;
|
||||
SELECT
|
||||
pg_column_size(plain), pg_column_size(main),
|
||||
pg_column_size(external), pg_column_size(extended)
|
||||
FROM test_toast_columnar;
|
||||
|
||||
DROP TABLE test_toast_row;
|
||||
DROP TABLE test_toast_columnar;
|
||||
|
|
Loading…
Reference in New Issue