Refactor pgoutput_change().
Instead of mostly-duplicate code for different operation (insert/update/delete) types, write a common code to compute old/new tuples, and check the row filter. Author: Hou Zhijie Reviewed-by: Peter Smith, Amit Kapila Discussion: https://postgr.es/m/OS0PR01MB5716194A47FFA8D91133687D94BF9@OS0PR01MB5716.jpnprd01.prod.outlook.com
This commit is contained in:
parent
902ecd3bd4
commit
da324d6cd4
@ -1440,6 +1440,17 @@ pgoutput_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
|
|||||||
case REORDER_BUFFER_CHANGE_DELETE:
|
case REORDER_BUFFER_CHANGE_DELETE:
|
||||||
if (!relentry->pubactions.pubdelete)
|
if (!relentry->pubactions.pubdelete)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is only possible if deletes are allowed even when replica
|
||||||
|
* identity is not defined for a table. Since the DELETE action
|
||||||
|
* can't be published, we simply return.
|
||||||
|
*/
|
||||||
|
if (!change->data.tp.oldtuple)
|
||||||
|
{
|
||||||
|
elog(DEBUG1, "didn't send DELETE change because of missing oldtuple");
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Assert(false);
|
Assert(false);
|
||||||
@ -1448,187 +1459,99 @@ pgoutput_change(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
|
|||||||
/* Avoid leaking memory by using and resetting our own context */
|
/* Avoid leaking memory by using and resetting our own context */
|
||||||
old = MemoryContextSwitchTo(data->context);
|
old = MemoryContextSwitchTo(data->context);
|
||||||
|
|
||||||
|
/* Switch relation if publishing via root. */
|
||||||
|
if (relentry->publish_as_relid != RelationGetRelid(relation))
|
||||||
|
{
|
||||||
|
Assert(relation->rd_rel->relispartition);
|
||||||
|
ancestor = RelationIdGetRelation(relentry->publish_as_relid);
|
||||||
|
targetrel = ancestor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (change->data.tp.oldtuple)
|
||||||
|
{
|
||||||
|
old_slot = relentry->old_slot;
|
||||||
|
ExecStoreHeapTuple(&change->data.tp.oldtuple->tuple, old_slot, false);
|
||||||
|
|
||||||
|
/* Convert tuple if needed. */
|
||||||
|
if (relentry->attrmap)
|
||||||
|
{
|
||||||
|
TupleTableSlot *slot = MakeTupleTableSlot(RelationGetDescr(targetrel),
|
||||||
|
&TTSOpsVirtual);
|
||||||
|
|
||||||
|
old_slot = execute_attr_map_slot(relentry->attrmap, old_slot, slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (change->data.tp.newtuple)
|
||||||
|
{
|
||||||
|
new_slot = relentry->new_slot;
|
||||||
|
ExecStoreHeapTuple(&change->data.tp.newtuple->tuple, new_slot, false);
|
||||||
|
|
||||||
|
/* Convert tuple if needed. */
|
||||||
|
if (relentry->attrmap)
|
||||||
|
{
|
||||||
|
TupleTableSlot *slot = MakeTupleTableSlot(RelationGetDescr(targetrel),
|
||||||
|
&TTSOpsVirtual);
|
||||||
|
|
||||||
|
new_slot = execute_attr_map_slot(relentry->attrmap, new_slot, slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check row filter.
|
||||||
|
*
|
||||||
|
* Updates could be transformed to inserts or deletes based on the results
|
||||||
|
* of the row filter for old and new tuple.
|
||||||
|
*/
|
||||||
|
if (!pgoutput_row_filter(targetrel, old_slot, &new_slot, relentry, &action))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send BEGIN if we haven't yet.
|
||||||
|
*
|
||||||
|
* We send the BEGIN message after ensuring that we will actually send the
|
||||||
|
* change. This avoids sending a pair of BEGIN/COMMIT messages for empty
|
||||||
|
* transactions.
|
||||||
|
*/
|
||||||
|
if (txndata && !txndata->sent_begin_txn)
|
||||||
|
pgoutput_send_begin(ctx, txn);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Schema should be sent using the original relation because it also sends
|
||||||
|
* the ancestor's relation.
|
||||||
|
*/
|
||||||
|
maybe_send_schema(ctx, change, relation, relentry);
|
||||||
|
|
||||||
|
OutputPluginPrepareWrite(ctx, true);
|
||||||
|
|
||||||
/* Send the data */
|
/* Send the data */
|
||||||
switch (action)
|
switch (action)
|
||||||
{
|
{
|
||||||
case REORDER_BUFFER_CHANGE_INSERT:
|
case REORDER_BUFFER_CHANGE_INSERT:
|
||||||
new_slot = relentry->new_slot;
|
|
||||||
ExecStoreHeapTuple(&change->data.tp.newtuple->tuple,
|
|
||||||
new_slot, false);
|
|
||||||
|
|
||||||
/* Switch relation if publishing via root. */
|
|
||||||
if (relentry->publish_as_relid != RelationGetRelid(relation))
|
|
||||||
{
|
|
||||||
Assert(relation->rd_rel->relispartition);
|
|
||||||
ancestor = RelationIdGetRelation(relentry->publish_as_relid);
|
|
||||||
targetrel = ancestor;
|
|
||||||
/* Convert tuple if needed. */
|
|
||||||
if (relentry->attrmap)
|
|
||||||
{
|
|
||||||
TupleDesc tupdesc = RelationGetDescr(targetrel);
|
|
||||||
|
|
||||||
new_slot = execute_attr_map_slot(relentry->attrmap,
|
|
||||||
new_slot,
|
|
||||||
MakeTupleTableSlot(tupdesc, &TTSOpsVirtual));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check row filter */
|
|
||||||
if (!pgoutput_row_filter(targetrel, NULL, &new_slot, relentry,
|
|
||||||
&action))
|
|
||||||
break;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Send BEGIN if we haven't yet.
|
|
||||||
*
|
|
||||||
* We send the BEGIN message after ensuring that we will actually
|
|
||||||
* send the change. This avoids sending a pair of BEGIN/COMMIT
|
|
||||||
* messages for empty transactions.
|
|
||||||
*/
|
|
||||||
if (txndata && !txndata->sent_begin_txn)
|
|
||||||
pgoutput_send_begin(ctx, txn);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Schema should be sent using the original relation because it
|
|
||||||
* also sends the ancestor's relation.
|
|
||||||
*/
|
|
||||||
maybe_send_schema(ctx, change, relation, relentry);
|
|
||||||
|
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
|
||||||
logicalrep_write_insert(ctx->out, xid, targetrel, new_slot,
|
logicalrep_write_insert(ctx->out, xid, targetrel, new_slot,
|
||||||
data->binary, relentry->columns);
|
data->binary, relentry->columns);
|
||||||
OutputPluginWrite(ctx, true);
|
|
||||||
break;
|
break;
|
||||||
case REORDER_BUFFER_CHANGE_UPDATE:
|
case REORDER_BUFFER_CHANGE_UPDATE:
|
||||||
if (change->data.tp.oldtuple)
|
logicalrep_write_update(ctx->out, xid, targetrel, old_slot,
|
||||||
{
|
new_slot, data->binary, relentry->columns);
|
||||||
old_slot = relentry->old_slot;
|
|
||||||
ExecStoreHeapTuple(&change->data.tp.oldtuple->tuple,
|
|
||||||
old_slot, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
new_slot = relentry->new_slot;
|
|
||||||
ExecStoreHeapTuple(&change->data.tp.newtuple->tuple,
|
|
||||||
new_slot, false);
|
|
||||||
|
|
||||||
/* Switch relation if publishing via root. */
|
|
||||||
if (relentry->publish_as_relid != RelationGetRelid(relation))
|
|
||||||
{
|
|
||||||
Assert(relation->rd_rel->relispartition);
|
|
||||||
ancestor = RelationIdGetRelation(relentry->publish_as_relid);
|
|
||||||
targetrel = ancestor;
|
|
||||||
/* Convert tuples if needed. */
|
|
||||||
if (relentry->attrmap)
|
|
||||||
{
|
|
||||||
TupleDesc tupdesc = RelationGetDescr(targetrel);
|
|
||||||
|
|
||||||
if (old_slot)
|
|
||||||
old_slot = execute_attr_map_slot(relentry->attrmap,
|
|
||||||
old_slot,
|
|
||||||
MakeTupleTableSlot(tupdesc, &TTSOpsVirtual));
|
|
||||||
|
|
||||||
new_slot = execute_attr_map_slot(relentry->attrmap,
|
|
||||||
new_slot,
|
|
||||||
MakeTupleTableSlot(tupdesc, &TTSOpsVirtual));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check row filter */
|
|
||||||
if (!pgoutput_row_filter(targetrel, old_slot, &new_slot,
|
|
||||||
relentry, &action))
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Send BEGIN if we haven't yet */
|
|
||||||
if (txndata && !txndata->sent_begin_txn)
|
|
||||||
pgoutput_send_begin(ctx, txn);
|
|
||||||
|
|
||||||
maybe_send_schema(ctx, change, relation, relentry);
|
|
||||||
|
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Updates could be transformed to inserts or deletes based on the
|
|
||||||
* results of the row filter for old and new tuple.
|
|
||||||
*/
|
|
||||||
switch (action)
|
|
||||||
{
|
|
||||||
case REORDER_BUFFER_CHANGE_INSERT:
|
|
||||||
logicalrep_write_insert(ctx->out, xid, targetrel,
|
|
||||||
new_slot, data->binary,
|
|
||||||
relentry->columns);
|
|
||||||
break;
|
|
||||||
case REORDER_BUFFER_CHANGE_UPDATE:
|
|
||||||
logicalrep_write_update(ctx->out, xid, targetrel,
|
|
||||||
old_slot, new_slot, data->binary,
|
|
||||||
relentry->columns);
|
|
||||||
break;
|
|
||||||
case REORDER_BUFFER_CHANGE_DELETE:
|
|
||||||
logicalrep_write_delete(ctx->out, xid, targetrel,
|
|
||||||
old_slot, data->binary,
|
|
||||||
relentry->columns);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Assert(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputPluginWrite(ctx, true);
|
|
||||||
break;
|
break;
|
||||||
case REORDER_BUFFER_CHANGE_DELETE:
|
case REORDER_BUFFER_CHANGE_DELETE:
|
||||||
if (change->data.tp.oldtuple)
|
logicalrep_write_delete(ctx->out, xid, targetrel, old_slot,
|
||||||
{
|
data->binary, relentry->columns);
|
||||||
old_slot = relentry->old_slot;
|
|
||||||
|
|
||||||
ExecStoreHeapTuple(&change->data.tp.oldtuple->tuple,
|
|
||||||
old_slot, false);
|
|
||||||
|
|
||||||
/* Switch relation if publishing via root. */
|
|
||||||
if (relentry->publish_as_relid != RelationGetRelid(relation))
|
|
||||||
{
|
|
||||||
Assert(relation->rd_rel->relispartition);
|
|
||||||
ancestor = RelationIdGetRelation(relentry->publish_as_relid);
|
|
||||||
targetrel = ancestor;
|
|
||||||
/* Convert tuple if needed. */
|
|
||||||
if (relentry->attrmap)
|
|
||||||
{
|
|
||||||
TupleDesc tupdesc = RelationGetDescr(targetrel);
|
|
||||||
|
|
||||||
old_slot = execute_attr_map_slot(relentry->attrmap,
|
|
||||||
old_slot,
|
|
||||||
MakeTupleTableSlot(tupdesc, &TTSOpsVirtual));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check row filter */
|
|
||||||
if (!pgoutput_row_filter(targetrel, old_slot, &new_slot,
|
|
||||||
relentry, &action))
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Send BEGIN if we haven't yet */
|
|
||||||
if (txndata && !txndata->sent_begin_txn)
|
|
||||||
pgoutput_send_begin(ctx, txn);
|
|
||||||
|
|
||||||
maybe_send_schema(ctx, change, relation, relentry);
|
|
||||||
|
|
||||||
OutputPluginPrepareWrite(ctx, true);
|
|
||||||
logicalrep_write_delete(ctx->out, xid, targetrel,
|
|
||||||
old_slot, data->binary,
|
|
||||||
relentry->columns);
|
|
||||||
OutputPluginWrite(ctx, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
elog(DEBUG1, "didn't send DELETE change because of missing oldtuple");
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Assert(false);
|
Assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OutputPluginWrite(ctx, true);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
if (RelationIsValid(ancestor))
|
if (RelationIsValid(ancestor))
|
||||||
{
|
{
|
||||||
RelationClose(ancestor);
|
RelationClose(ancestor);
|
||||||
ancestor = NULL;
|
ancestor = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cleanup */
|
|
||||||
MemoryContextSwitchTo(old);
|
MemoryContextSwitchTo(old);
|
||||||
MemoryContextReset(data->context);
|
MemoryContextReset(data->context);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user