Skip to content

Commit bbce9d4

Browse files
update desc schema compat logic to mirror decoding, update decoding comments
1 parent 6080b0b commit bbce9d4

2 files changed

Lines changed: 43 additions & 23 deletions

File tree

src/mysql-util/src/decoding.rs

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,25 +34,32 @@ pub fn pack_mysql_row(
3434
let mut packer = row_container.packer();
3535

3636
// For each column in `table_desc` (in descriptor order), resolve its wire
37-
// index. Non-fallback rows are matched by name so a reordered upstream
38-
// still decodes correctly; fallback rows have no names and are matched
39-
// positionally. A `None` here means the upstream row is missing this
40-
// column and is only tolerated for ignored columns.
37+
// index. With binlog_full_metadata=true, columns are matched by name so a reordered upstream
38+
// still decodes correctly; without binlog_full_metadata, rows have no column names and must be
39+
// matched positionally. A `None` here means the upstream row is missing this column and is
40+
// only tolerated for ignored columns, and for binlog_full_metadata = false, is only tolerated
41+
// for ignored columns at the end of the table.
4142
for (i, col_desc) in table_desc.columns.iter().enumerate() {
43+
if col_desc.column_type.is_none() {
44+
// This column is ignored, so don't decode it.
45+
continue;
46+
}
4247
let wire_idx = if !binlog_full_metadata {
48+
// No column name metadata, so we match by index.
4349
(i < row.len()).then_some(i)
4450
} else {
51+
// This means the row from the binlog has column name included in the metadata,
52+
// so we can match on that instead of position.
4553
row.columns_ref()
4654
.iter()
4755
.position(|wc| wc.name_str() == col_desc.name.as_str())
4856
};
49-
if col_desc.column_type.is_none() {
50-
// This column is ignored, so don't decode it.
51-
continue;
52-
}
57+
5358
let wire_idx = match wire_idx {
5459
Some(idx) => idx,
5560
None => {
61+
// We could not find a column in the incoming row that matches this descriptor column.
62+
// This is an error as the column is not ignored (ignored columns have already been skipped).
5663
return Err(decode_error(
5764
"extra column description",
5865
col_desc,

src/mysql-util/src/desc.rs

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl MySqlTableDesc {
7878
pub fn determine_compatibility(
7979
&self,
8080
other: &MySqlTableDesc,
81-
full_metadata: bool,
81+
binlog_full_metadata: bool,
8282
) -> Result<(), anyhow::Error> {
8383
if self == other {
8484
return Ok(());
@@ -95,28 +95,41 @@ impl MySqlTableDesc {
9595
}
9696

9797
// In the case that we don't have full binlog row metadata, `columns` is ordered by the
98-
// ordinal_position of each column in the table, so as long as `self.columns` is a
98+
// ordinal position of each column in the table, so as long as `self.columns` is a
9999
// compatible prefix of `other.columns`, we can ignore extra columns from `other.columns`.
100100
//
101101
// If we do have full metadata, then we can match columns by name and just check that all
102102
// columns in `self.columns` are present and compatible with columns in `other.columns`.
103-
let mut other_columns = other.columns.iter();
104-
for self_column in self.columns.iter() {
105-
let other_column = if full_metadata {
106-
if self_column.column_type.is_none() {
107-
// This is an excluded column and can be ignored, as it may not have a
108-
// corresponding column in `other.columns` if the column was dropped upstream.
109-
continue;
110-
}
111-
other.columns.iter().find(|c| c.name == self_column.name)
112-
} else {
113-
other_columns.next()
114-
};
103+
for (i, self_column) in self.columns.iter().enumerate() {
115104
if self_column.column_type.is_none() {
116105
// This is an excluded column and can be ignored.
117106
continue;
118107
}
119-
let other_column = other_column.ok_or_else(|| {
108+
let wire_idx = if !binlog_full_metadata {
109+
// No column name metadata, so we match by index.
110+
(i < other.columns.len()).then_some(i)
111+
} else {
112+
// This means the row from the binlog has column name included in the metadata,
113+
// so we can match on that instead of position.
114+
other
115+
.columns
116+
.iter()
117+
.position(|oc| oc.name.as_str() == self_column.name.as_str())
118+
};
119+
120+
let wire_idx = match wire_idx {
121+
Some(idx) => idx,
122+
None => {
123+
// We could not find a column in the incoming row that matches this descriptor column.
124+
// This is an error as the column is not ignored (ignored columns have already been skipped).
125+
return Err(anyhow::anyhow!(
126+
"column {} no longer present in table {}",
127+
self_column.name,
128+
self.name
129+
));
130+
}
131+
};
132+
let other_column = other.columns.get(wire_idx).ok_or_else(|| {
120133
anyhow::anyhow!(
121134
"column {} no longer present in table {}",
122135
self_column.name,

0 commit comments

Comments
 (0)