Skip to content

Remap filtered rows to relation descriptor for dropped columns#540

Open
SAY-5 wants to merge 1 commit into
pgEdge:mainfrom
SAY-5:fix-table-data-filtered-dropped-columns
Open

Remap filtered rows to relation descriptor for dropped columns#540
SAY-5 wants to merge 1 commit into
pgEdge:mainfrom
SAY-5:fix-table-data-filtered-dropped-columns

Conversation

@SAY-5

@SAY-5 SAY-5 commented Jul 17, 2026

Copy link
Copy Markdown

spock_table_data_filtered() runs SELECT * FROM <rel> through SPI and puts the result tuples straight into the tuplestore, whose descriptor is the full relation descriptor. SELECT * omits dropped columns, so those tuples only carry the live attributes and every attribute past a dropped position is then read at the wrong offset, giving corrupt/NULL data on the subscriber or a provider crash when a misread varlena is detoasted (#528).

This remaps each SPI tuple to the relation descriptor with convert_tuples_by_position() / execute_attr_map_tuple(), which inserts NULLs at the dropped positions; when the descriptors already match, convert_tuples_by_position() returns NULL and the tuples are stored unchanged.

Reproducing and adding the row-filter regression case both need a running provider/subscriber cluster, which I could not spin up locally, so I verified the change compiles clean against the PG server headers but did not run the sync suite. A minimal SQL repro from the issue:

CREATE TABLE public.tdf_repro (id int PRIMARY KEY, a text, junk text, b text);
INSERT INTO public.tdf_repro VALUES (1, 'aa', 'xx', 'bb');
ALTER TABLE public.tdf_repro DROP COLUMN junk;
-- before: spock.table_data_filtered() returns b as NULL / misaligned; after: correct rows

Signed-off-by: Sai Asish Y <say.apm35@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Filtered tuple descriptor conversion

Layer / File(s) Summary
Filtered tuple descriptor conversion
src/spock_functions.c
spock_table_data_filtered converts SPI result tuples by position to the target relation descriptor before storing them, including dropped-column positions, and frees the conversion map afterward.

Poem

I’m a rabbit with tuples to spare,
Remapping each row with care.
Dropped columns make room,
Descriptors now bloom,
And clean maps hop through the air.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: remapping filtered rows to the relation descriptor for dropped columns.
Description check ✅ Passed The description directly explains the code change, its motivation, and verification status.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 duplication

Metric Results
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/spock_functions.c`:
- Around line 3040-3048: In the SPI result loop, update the tuple handling
around execute_attr_map_tuple so each newly remapped HeapTuple is freed after
tuplestore_puttuple stores it, while leaving original SPI tuples untouched when
no mapping occurs. Ensure cleanup happens only for tuples allocated by
execute_attr_map_tuple.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7d4567e6-f8fa-457e-b352-946e5e4de88a

📥 Commits

Reviewing files that changed from the base of the PR and between db380dd and df605f1.

📒 Files selected for processing (1)
  • src/spock_functions.c

Comment thread src/spock_functions.c
Comment on lines +3040 to +3048
for (i = 0; i < SPI_processed; i++)
{
HeapTuple tup = SPI_tuptable->vals[i];

if (map != NULL)
tup = execute_attr_map_tuple(tup, map);

tuplestore_puttuple(tupstore, tup);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Free the remapped tuple to prevent severe memory leaks.

execute_attr_map_tuple allocates a new HeapTuple for each row. Since SPI_execute already materializes the entire filtered dataset in memory, accumulating newly allocated tuples without freeing them will double the memory usage. For large tables, this resource leak can quickly lead to an Out-Of-Memory (OOM) error and crash the backend during initial synchronization.

Free the remapped tuple after storing it, as tuplestore_puttuple safely copies the tuple into the tuplestore's memory or disk context.

🐛 Proposed fix
 		for (i = 0; i < SPI_processed; i++)
 		{
 			HeapTuple	tup = SPI_tuptable->vals[i];
 
 			if (map != NULL)
-				tup = execute_attr_map_tuple(tup, map);
-
-			tuplestore_puttuple(tupstore, tup);
+			{
+				HeapTuple mapped_tup = execute_attr_map_tuple(tup, map);
+				tuplestore_puttuple(tupstore, mapped_tup);
+				heap_freetuple(mapped_tup);
+			}
+			else
+			{
+				tuplestore_puttuple(tupstore, tup);
+			}
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (i = 0; i < SPI_processed; i++)
{
HeapTuple tup = SPI_tuptable->vals[i];
if (map != NULL)
tup = execute_attr_map_tuple(tup, map);
tuplestore_puttuple(tupstore, tup);
}
for (i = 0; i < SPI_processed; i++)
{
HeapTuple tup = SPI_tuptable->vals[i];
if (map != NULL)
{
HeapTuple mapped_tup = execute_attr_map_tuple(tup, map);
tuplestore_puttuple(tupstore, mapped_tup);
heap_freetuple(mapped_tup);
}
else
{
tuplestore_puttuple(tupstore, tup);
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/spock_functions.c` around lines 3040 - 3048, In the SPI result loop,
update the tuple handling around execute_attr_map_tuple so each newly remapped
HeapTuple is freed after tuplestore_puttuple stores it, while leaving original
SPI tuples untouched when no mapping occurs. Ensure cleanup happens only for
tuples allocated by execute_attr_map_tuple.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant