mirror of https://github.com/citusdata/citus.git
commit
54f0e8619a
|
@ -14,7 +14,7 @@
|
||||||
#include "access/tableam.h"
|
#include "access/tableam.h"
|
||||||
#include "access/tsmapi.h"
|
#include "access/tsmapi.h"
|
||||||
#if PG_VERSION_NUM >= 130000
|
#if PG_VERSION_NUM >= 130000
|
||||||
#include "access/heaptoast.h"
|
#include "access/detoast.h"
|
||||||
#else
|
#else
|
||||||
#include "access/tuptoaster.h"
|
#include "access/tuptoaster.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -106,9 +106,10 @@ static void LogRelationStats(Relation rel, int elevel);
|
||||||
static void TruncateColumnar(Relation rel, int elevel);
|
static void TruncateColumnar(Relation rel, int elevel);
|
||||||
static HeapTuple ColumnarSlotCopyHeapTuple(TupleTableSlot *slot);
|
static HeapTuple ColumnarSlotCopyHeapTuple(TupleTableSlot *slot);
|
||||||
static void ColumnarCheckLogicalReplication(Relation rel);
|
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(). */
|
/* Custom tuple slot ops used for columnar. Initialized in columnar_tableam_init(). */
|
||||||
TupleTableSlotOps TTSOpsColumnar;
|
static TupleTableSlotOps TTSOpsColumnar;
|
||||||
|
|
||||||
static List *
|
static List *
|
||||||
RelationColumnList(Relation rel)
|
RelationColumnList(Relation rel)
|
||||||
|
@ -435,24 +436,16 @@ columnar_tuple_insert(Relation relation, TupleTableSlot *slot, CommandId cid,
|
||||||
TableWriteState *writeState = columnar_init_write_state(relation,
|
TableWriteState *writeState = columnar_init_write_state(relation,
|
||||||
RelationGetDescr(relation),
|
RelationGetDescr(relation),
|
||||||
GetCurrentSubTransactionId());
|
GetCurrentSubTransactionId());
|
||||||
|
|
||||||
MemoryContext oldContext = MemoryContextSwitchTo(writeState->perTupleContext);
|
MemoryContext oldContext = MemoryContextSwitchTo(writeState->perTupleContext);
|
||||||
|
|
||||||
HeapTuple heapTuple = ExecCopySlotHeapTuple(slot);
|
|
||||||
|
|
||||||
ColumnarCheckLogicalReplication(relation);
|
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);
|
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);
|
MemoryContextSwitchTo(oldContext);
|
||||||
MemoryContextReset(writeState->perTupleContext);
|
MemoryContextReset(writeState->perTupleContext);
|
||||||
|
@ -485,28 +478,23 @@ columnar_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
|
||||||
GetCurrentSubTransactionId());
|
GetCurrentSubTransactionId());
|
||||||
|
|
||||||
ColumnarCheckLogicalReplication(relation);
|
ColumnarCheckLogicalReplication(relation);
|
||||||
|
|
||||||
|
MemoryContext oldContext = MemoryContextSwitchTo(writeState->perTupleContext);
|
||||||
|
|
||||||
for (int i = 0; i < ntuples; i++)
|
for (int i = 0; i < ntuples; i++)
|
||||||
{
|
{
|
||||||
TupleTableSlot *tupleSlot = slots[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);
|
slot_getallattrs(tupleSlot);
|
||||||
|
|
||||||
ColumnarWriteRow(writeState, tupleSlot->tts_values, tupleSlot->tts_isnull);
|
Datum *values = detoast_values(tupleSlot->tts_tupleDescriptor,
|
||||||
MemoryContextSwitchTo(oldContext);
|
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
|
* ColumnarCheckLogicalReplication throws an error if the relation is
|
||||||
* part of any publication. This should be called before any write to
|
* part of any publication. This should be called before any write to
|
||||||
|
|
|
@ -43,4 +43,8 @@
|
||||||
#define table_endscan heap_endscan
|
#define table_endscan heap_endscan
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if PG_VERSION_NUM < 130000
|
||||||
|
#define detoast_attr(X) heap_tuple_untoast_attr(X)
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif /* COLUMNAR_COMPAT_H */
|
#endif /* COLUMNAR_COMPAT_H */
|
||||||
|
|
|
@ -96,3 +96,38 @@ DROP PUBLICATION test_columnar_publication;
|
||||||
-- should succeed
|
-- should succeed
|
||||||
INSERT INTO test_logical_replication VALUES (3);
|
INSERT INTO test_logical_replication VALUES (3);
|
||||||
DROP TABLE test_logical_replication;
|
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
|
-- should succeed
|
||||||
INSERT INTO test_logical_replication VALUES (3);
|
INSERT INTO test_logical_replication VALUES (3);
|
||||||
DROP TABLE test_logical_replication;
|
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