Skip to content

fix(postgres): validate publication contains all selected streams before CDC#971

Open
ishanyawhocares wants to merge 6 commits into
datazip-inc:stagingfrom
ishanyawhocares:fix/issue-752-validate-publication-streams
Open

fix(postgres): validate publication contains all selected streams before CDC#971
ishanyawhocares wants to merge 6 commits into
datazip-inc:stagingfrom
ishanyawhocares:fix/issue-752-validate-publication-streams

Conversation

@ishanyawhocares

@ishanyawhocares ishanyawhocares commented Jun 5, 2026

Copy link
Copy Markdown

Closes #752

Problem

When a table is selected in streams.json but not added to the Postgres
publication slot, OLake either fails deep in WAL processing with a cryptic
error, or silently produces no CDC data for that table — giving the user
no indication of what's wrong.

Solution

Added an explicit pre-flight check in PreCDC that queries
pg_publication_tables and compares the result against the selected streams.
If any table is missing, the sync fails immediately with a clear, actionable
error: publication validation failed: the following tables are selected in streams.json

but are NOT tracked by publication "my_pub":
public.payments
Run: ALTER PUBLICATION my_pub ADD TABLE public.payments

Changes

  • drivers/postgres/internal/cdc.go: added validatePublicationContainsStreams,
    checkStreamsInPublication, fetchPublicationTables functions; called from PreCDC
  • drivers/postgres/internal/cdc_publication_test.go: 10 table-driven unit tests
    covering happy path, missing tables, empty publication, case-insensitivity, etc.

Ishanya Sharma

BITS Pilani, Goa campus

cc @nayanj98

@CLAassistant

CLAassistant commented Jun 5, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@ishanyawhocares ishanyawhocares changed the base branch from master to staging June 6, 2026 09:12
@ishanyawhocares

Copy link
Copy Markdown
Author

video Explanation

Issue752_Fix_Ishanya_sharma.mp4

@nayanj98

Copy link
Copy Markdown
Collaborator

@ishanyawhocares Assigning @vikaxsh to review your PR

@nayanj98 nayanj98 requested a review from vikaxsh June 10, 2026 05:55
@ishanyawhocares

Copy link
Copy Markdown
Author

Hello @vikaxsh, its been a week. just wanted to request the review to know if there is any changes required to the code, or is it ready to be merged?

@nayanj98

Copy link
Copy Markdown
Collaborator

@ishanyawhocares Sorry for the delay in review. You will definitely have a review soon

if publication == "" || len(streams) == 0 {
return nil
}
pubTables, err := fetchPublicationTables(ctx, conn, publication)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Let's collapse this into two functions: one thin DB layer validatePublicationContainsStreams and one pure matcher checkStreamsInPublication

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

The split is already as:

  • validatePublicationContainsStreams: the DB layer entry point called from
    PreCDC (assigns the actual query to a small helper, fetchPublicationTables).

  • checkStreamsInPublication: Its a pure matcher, no DB dependency.

Let me know if you'd rather have fetchPublicationTables folded back into
validatePublicationContainsStreams directly that is having only 2 functions. I would change that

Comment thread drivers/postgres/internal/cdc.go Outdated

func (p *Postgres) PreCDC(ctx context.Context, streams []types.StreamInterface) error {
// Validate all selected streams exist in the publication before touching the slot.
// Fixes: https://github.com/datazip-inc/olake/issues/752

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

remove this

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Removed it, Thanks!

// Build a normalised lookup set: "schema.table" → present
pubSet := make(map[string]struct{}, len(pubTables))
for _, r := range pubTables {
key := strings.ToLower(r.Schema) + "." + strings.ToLower(r.Table)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why are you lowercasing table names?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It was initially to avoid a case mismatch, but as streams.json is
generated by discover() off information_schema, table names in there are
already lowercase by default.

so this mismatch will never happen, but just kept it as a small defensive check, if someone edits streams.json, afterwards and types a table name with a different casing than the actual publication.

Happy to change it as well, let me know if you'd like that.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

we should keep it case case-sensitive

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

current code might fail on case-sensitive table name (like myTable and myTABLE)

Comment thread drivers/postgres/internal/cdc.go Outdated
@ishanyawhocares

Copy link
Copy Markdown
Author

Hello @vikaxsh , I have updated the rest of the code as per the requirements, can you please confirm on the fetchPublicationTables review so I can update that as well.

@ishanyawhocares

Copy link
Copy Markdown
Author

hello @vikaxsh, just checking in, can you please go through this, so I can do final commits, and ready to merge.

)
}

// Build a normalised lookup set: "schema.table" -> present

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

we have generic types.Set[T] implementation, we can use here as well

// Build a normalised lookup set: "schema.table" → present
pubSet := make(map[string]struct{}, len(pubTables))
for _, r := range pubTables {
key := strings.ToLower(r.Schema) + "." + strings.ToLower(r.Table)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

we should keep it case case-sensitive

// Build a normalised lookup set: "schema.table" → present
pubSet := make(map[string]struct{}, len(pubTables))
for _, r := range pubTables {
key := strings.ToLower(r.Schema) + "." + strings.ToLower(r.Table)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

current code might fail on case-sensitive table name (like myTable and myTABLE)

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.

issue(postgres): check if tables selected in streams.json are subscribed in publication

5 participants