Skip to content

Correct DDL generation for various CHANGE STREAM alterations#79

Open
toga4 wants to merge 7 commits intodaichirata:masterfrom
toga4:fix-changestream-drop
Open

Correct DDL generation for various CHANGE STREAM alterations#79
toga4 wants to merge 7 commits intodaichirata:masterfrom
toga4:fix-changestream-drop

Conversation

@toga4
Copy link
Contributor

@toga4 toga4 commented Dec 9, 2025

This PR fixes several issues in DDL generation for CHANGE STREAM alterations to generate more optimal and correct DDL statements.

Issues Fixed:

  • TABLES → NONE watch type change: Previously used DROP + CREATE, now correctly uses ALTER CHANGE STREAM ... DROP FOR ALL
  • Watch target migration between tables: When moving a CHANGE STREAM from one table to another (e.g., FOR Singers → FOR Albums), previously used DROP + CREATE, now correctly uses ALTER CHANGE STREAM ... SET FOR
  • Option removal: When removing CHANGE STREAM options, previously set hardcoded default values (e.g., retention_period = '1d'), now correctly sets options to null to reset to server defaults
  • Table-bound CHANGE STREAM handling on table deletion: When a table is deleted but the CHANGE STREAM still exists in the target schema (watching a different table), the CHANGE STREAM is no longer incorrectly dropped

@daichirata
Copy link
Owner

Thanks for the PR! It looks like the changes now conflict due to the merge of #78. Could you review the updates?

- Use ALTER ... DROP FOR ALL instead of DROP + CREATE when changing from TABLES to NONE watch type
- Use ALTER ... SET FOR instead of DROP + CREATE when moving watch target to another table
- Set options to null instead of hardcoded defaults when removing CHANGE STREAM options
- Properly handle table-bound CHANGE STREAM deletion when the table is deleted but the CHANGE STREAM still exists in target schema
- Add comprehensive tests for all 7 CHANGE STREAM options
@toga4 toga4 force-pushed the fix-changestream-drop branch from 885149a to 7dd548f Compare December 11, 2025 13:49
@toga4
Copy link
Contributor Author

toga4 commented Dec 11, 2025

@daichirata Conflict now resolved! Could you please review?

Copy link
Owner

@daichirata daichirata left a comment

Choose a reason for hiding this comment

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

@toga4 Thanks for resolving the conflict! Could you please check a few test cases?

CHANGE STREAM operations (ALTER/DROP) must be executed before dropping the tables they reference.
This fixes issues where dropping a table before handling its associated CHANGE STREAM would cause errors.
@toga4 toga4 force-pushed the fix-changestream-drop branch from e34adf1 to cfebec0 Compare January 24, 2026 12:17
@toga4
Copy link
Contributor Author

toga4 commented Jan 24, 2026

@daichirata Thanks for catching these cases!

I've added both test cases and fixed the issues. Additionally, I've fixed an incorrect expected value in one test case.

Please take a look when you have a chance!

// drop change streams
for _, fromChangeStream := range g.from.changeStreams {
if _, exists := g.findChangeStreamByName(g.to, identsToComparable(fromChangeStream.Name)); !exists {
ddl.AppendDDL(g.generateDDLForDropChangeStream(fromChangeStream))
Copy link
Owner

Choose a reason for hiding this comment

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

Since the original implementation is structured this way, this comment may be slightly out of scope for this PR, but for the sake of implementation consistency, would it make sense to also add it to g.dropedChangeStream here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, thanks for catching this!
This loop only processes FOR ALL change streams, which are mutually exclusive from table-specific ones, so duplicate drops can't currently occur here. But I agree it's better to add both the g.dropedChangeStream append and the isDropedChangeStream() check for consistency with other loops.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done! 2159130

continue
}
}
ddl.Append(&ast.DropChangeStream{Name: cs.Name})
Copy link
Contributor Author

Choose a reason for hiding this comment

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

While looking into this, I also found a related bug at L534: it uses ddl.Append(&ast.DropChangeStream{...}) directly instead of g.generateDDLForDropChangeStream(), which causes an unnecessary REVOKE to be generated when dropping a table with an associated change stream that has a GRANT. I'll fix that too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed! 9a9bacb

toga4 added 2 commits January 28, 2026 10:51
Add isDropedChangeStream check and dropedChangeStream append to the loop handling database-level change streams (FOR ALL) for consistency with table-level change stream handling.
Use generateDDLForDropChangeStream instead of directly appending DropChangeStream to ensure GRANT is recorded in droppedGrant.
Previously, when dropping a table that has an associated change stream with a GRANT, an unnecessary REVOKE statement was generated because the GRANT was not tracked in droppedGrant.
g.dropedChangeStream = append(g.dropedChangeStream, identsToComparable(cs.Name))
}
}
}
Copy link
Owner

Choose a reason for hiding this comment

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

When adding a test like the following, ALTER CHANGE STREAM gets emitted multiple times.

		{
			from: `
CREATE TABLE t1 (
  id INT64 NOT NULL,
) PRIMARY KEY(id);
CREATE TABLE t2 (
  id INT64 NOT NULL,
) PRIMARY KEY(id);
CREATE CHANGE STREAM SomeStream FOR t1, t2;
  `,
			to: `
CREATE TABLE t2 (
  id INT64 NOT NULL,
) PRIMARY KEY(id);
CREATE CHANGE STREAM SomeStream FOR t2;
  `,
			ignoreAlterDatabase: true,
			expected: []string{
				"ALTER CHANGE STREAM SomeStream SET FOR t2",
				"DROP TABLE t1",
			},
		},
=== Failed
=== FAIL: internal/hammer TestDiff/avoid_duplicate_alter_change_stream_set_for_on_drop_table (0.00s)
    diff_test.go:2420: (-want, +got)
          []string{
        + 	"ALTER CHANGE STREAM SomeStream SET FOR t2",
          	"ALTER CHANGE STREAM SomeStream SET FOR t2",
          	"DROP TABLE t1",
          }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed! c7a66e8
The duplicate occurred because generateDDLForDropConstraintIndexAndTable and the main change stream diff loop both emitted ALTER for the same change stream. Added a willCreateOrAlterChangeStreamIDs check to skip it when already scheduled.
Also fixed a related bug where unnecessary ALTER was emitted before DROP CHANGE STREAM (7d0c5aa), and added some edge case tests (f11fb05).

toga4 added 3 commits January 29, 2026 11:17
When a table is dropped and a change stream watching multiple tables needs to be altered, the ALTER CHANGE STREAM statement was being emitted twice: once in generateDDLForDropConstraintIndexAndTable and again in GenerateDDL's L313-323 loop.
This fix checks willCreateOrAlterChangeStreamIDs before emitting the ALTER statement in generateDDLForDropConstraintIndexAndTable, skipping the output if the change stream is already scheduled for alteration.
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.

2 participants