Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add VisibilityBitmap to TableMapEvent in replication #813

Merged

Conversation

dongwook-chan
Copy link
Contributor

@dongwook-chan dongwook-chan commented Aug 15, 2023

I was implementing optional_metadata field in table_map_event for python-mysql-replication.
And came across this repo and found that metadata type COLUMN_VISIBILITY isn't implemented.

Changes

  • Added TableMapEvent.VisibilityBitmap
    • VisibilityBitmap is a bitmap where each bit represents the visibility of a corresponding column in a table.
    • If a bit is set, it indicates that the corresponding column is NOT an invinsible column.
    • Invisible column was introduced in MySQL version 8.0.23.
  • TableMapEvent.VisibilityMap
    • VisibilityMap lists boolean values of which is true if column of the same index is NOT an invisible column.

Examples

You can check the visibility of a column by its table by executing DESCRIBE command.

mysql> SELECT VERSION(); -- must be MySQL 8.0.23 or higher. Not supported by MariaDB yet.
+-----------+
| VERSION() |
+-----------+
| 8.0.33-25 |
+-----------+
1 row in set (0.00 sec)

mysql> CREATE TABLE test_visibility (c1 INT, c2 INT INVISIBLE, c3 INT);
Query OK, 0 rows affected (0.03 sec)

mysql> DESC test_visibility;
+-------+------+------+-----+---------+-----------+
| Field | Type | Null | Key | Default | Extra     |
+-------+------+------+-----+---------+-----------+
| c1    | int  | YES  |     | NULL    |           |
| c2    | int  | YES  |     | NULL    | INVISIBLE |
| c3    | int  | YES  |     | NULL    |           |
+-------+------+------+-----+---------+-----------+
3 rows in set (0.00 sec)

@dongwook-chan dongwook-chan changed the title Add VisibilityBitmap to TableMapEvent for MySQL 8.0.23+ Invisible… Add VisibilityBitmap to TableMapEvent in replication Aug 15, 2023
@dongwook-chan dongwook-chan changed the title Add VisibilityBitmap to TableMapEvent in replication Add VisibilityBitmap to TableMapEvent in replication Aug 15, 2023
@dongwook-chan
Copy link
Contributor Author

@lance6716
I appreciate your positive reaction to my PR, and I'm grateful for your contributions to this repository. go-mysql is indispensable to me. Could you please advise whether I should include tests for this new addition?

@lance6716
Copy link
Collaborator

@lance6716 I appreciate your positive reaction to my PR, and I'm grateful for your contributions to this repository. go-mysql is indispensable to me. Could you please advise whether I should include tests for this new addition?

I will take a look some hours later. Thanks for your PR 😄

… Columns

Changes:
- `TableMapEvent.VisibilityBitmap`
`VisibilityBitmap` is a bitmap where each bit represents the visibility of a corresponding column in a table.
If a bit is set, it indicates that the corresponding column is NOT an invinsible column.
Invisible column was introduced in MySQL version 8.0.23.

- `TableMapEvent.VisibilityMap`
`VisibilityMap` lists boolean values of which is true if column of the same index is NOT an invisible column.

Co-authored-by: sean <sean.k1@kakaoent.com>
@dongwook-chan dongwook-chan force-pushed the feature/optional-meta-visibility branch from b2e8633 to ccabd26 Compare August 15, 2023 07:46
@dongwook-chan
Copy link
Contributor Author

I added a co-author.

p := 0
ret := make(map[int]bool)
for i := 0; i < int(e.ColumnCount); i++ {
ret[i] = e.VisibilityBitmap[p/8]&(1<<uint(7-p%8)) != 0
Copy link
Member

Choose a reason for hiding this comment

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

nit: Maybe it's a little easier to understand the original code with countdown instead of 1<<uint(7-p%8)

Copy link
Contributor Author

@dongwook-chan dongwook-chan Aug 15, 2023

Choose a reason for hiding this comment

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

@atercattus
I appreciate your review. Also, I agree with you and I can get this fixed.
Actually VisibilityMap is copy and paste of existing UnsignedMap. Should I fix UnsignedMap too?
I could create a separate PR it if it seems irrelevant to this PR.

Copy link
Member

Choose a reason for hiding this comment

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

Oh. @lance6716 what do you think about this? Apply the Boy Scout Rule or not?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we decide to improve, the code will look like below.

func (e *TableMapEvent) VisibilityMap() map[int]bool {
	if len(e.VisibilityBitmap) == 0 {
		return nil
	}
	ret := make(map[int]bool)
	for _, field := range e.VisibilityBitmap {
		for c, i := 0x80, 0; c != 0; c, i = c>>1, i+1 {
			ret[i] = field&byte(c) != 0
		}
	}
	return ret
}

Copy link
Collaborator

Choose a reason for hiding this comment

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

The old code is really not easy to understand 😢 I also hope we start to improve the readability. As for UnsignedMap, I'm OK that you also modify it in this PR or another PR. WDYT @atercattus

Your new code snippet seems not optimal. The status of i should be kept when iterate bytes of VisibilityBitmap. And we can early stop when the column count is not a multiple of 8 (though the original code seems not optimize about this). Maybe we can

...
i := 0
for e.VisibilityBitmap {
  for c := 0x80; ... {
    ret[i] = ...
    i++
    if i >= e.ColumnCount { return }
  }
}
...

Welcome to find different code

Copy link
Contributor Author

@dongwook-chan dongwook-chan Aug 15, 2023

Choose a reason for hiding this comment

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

My bad. i should indeed be initialized outside of the inner loop.

p := 0
ret := make(map[int]bool)
for i := 0; i < int(e.ColumnCount); i++ {
ret[i] = e.VisibilityBitmap[p/8]&(1<<uint(7-p%8)) != 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

The old code is really not easy to understand 😢 I also hope we start to improve the readability. As for UnsignedMap, I'm OK that you also modify it in this PR or another PR. WDYT @atercattus

Your new code snippet seems not optimal. The status of i should be kept when iterate bytes of VisibilityBitmap. And we can early stop when the column count is not a multiple of 8 (though the original code seems not optimize about this). Maybe we can

...
i := 0
for e.VisibilityBitmap {
  for c := 0x80; ... {
    ret[i] = ...
    i++
    if i >= e.ColumnCount { return }
  }
}
...

Welcome to find different code

// VisibilityMap returns a map: column index -> visiblity.
// Invisible column was introduced in MySQL 8.0.23
// nil is returned if not available.
func (e *TableMapEvent) VisibilityMap() map[int]bool {
Copy link
Collaborator

Choose a reason for hiding this comment

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

As for testing, I think maybe you can capture/construct some VisibilityBitmap bytes and test the result of TableMapEvent.VisibilityMap is expected.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@lance6716
Sure. I think I should create a separate function. Just as TestTableMapOptMetaPrimaryKey is defined solely for primary key type meta data. At first, I thought about squeezing into TestTableMapOptMetaNames or TestTableMapHelperMaps, but it's too packed and I saw no room for any addition. Your advice would be help me a lot. 😀

dongwook-chan added a commit to dongwook-chan/go-mysql that referenced this pull request Aug 15, 2023
Changes:
- Refactpred `UnsignedMap`
- Refactored `VisibilityMap`

Suggested by: go-mysql-org#813 (comment)
Changes:
- Refactpred `UnsignedMap`
- Refactored `VisibilityMap`

Suggested by: go-mysql-org#813 (comment)
@dongwook-chan dongwook-chan force-pushed the feature/optional-meta-visibility branch from 20bf956 to 6260982 Compare August 15, 2023 15:18
Changes:
- Added data for MySQL 8.0 only (Only MySQL 8.0.23+ supports invisible columns)
@dongwook-chan
Copy link
Contributor Author

dongwook-chan commented Aug 20, 2023

I added test case for 8.0 only, because the keyword INVISIBLE is not available in any other vendors or versions.

@lance6716
Copy link
Collaborator

ptal @atercattus

@dongwook-chan
Copy link
Contributor Author

dongwook-chan commented Aug 20, 2023

But on a second thought, I would like to add case 2 where invisible columns do not exist at all! This case will be applicable to the rest of vendors and versions too; MySQL 5.7, MariaDB 10.4 and MairaDB 10.5.

Changes:
- Add test case 2 where invisible columns does not exists at all
@lance6716
Copy link
Collaborator

lgtm now. let's wait @atercattus

@lance6716
Copy link
Collaborator

lance6716 commented Aug 24, 2023

@dongwook-chan seems atercattus is a bit busy. Do you want me to merge it or you can wait a bit longer ?

@dongwook-chan
Copy link
Contributor Author

dongwook-chan commented Aug 24, 2023

@lance6716 No need to wait any longer!
@atercattus Thank you for the approval!

@lance6716 lance6716 merged commit cfe6012 into go-mysql-org:master Aug 24, 2023
13 checks passed
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.

None yet

3 participants