Skip to content

Commit c658340

Browse files
authored
refactor: some CDC table's code (#19255)
1 parent 99703d1 commit c658340

File tree

5 files changed

+154
-192
lines changed

5 files changed

+154
-192
lines changed

src/frontend/src/handler/alter_table_column.rs

+43-45
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use risingwave_pb::stream_plan::stream_node::PbNodeBody;
3030
use risingwave_pb::stream_plan::{ProjectNode, StreamFragmentGraph};
3131
use risingwave_sqlparser::ast::{
3232
AlterTableOperation, ColumnDef, ColumnOption, DataType as AstDataType, Encode,
33-
FormatEncodeOptions, ObjectName, Statement, StructField,
33+
FormatEncodeOptions, Ident, ObjectName, Statement, StructField, TableConstraint,
3434
};
3535
use risingwave_sqlparser::parser::Parser;
3636

@@ -43,34 +43,10 @@ use crate::catalog::table_catalog::TableType;
4343
use crate::error::{ErrorCode, Result, RwError};
4444
use crate::expr::{Expr, ExprImpl, InputRef, Literal};
4545
use crate::handler::create_sink::{fetch_incoming_sinks, insert_merger_to_union_with_project};
46+
use crate::handler::create_table::bind_table_constraints;
4647
use crate::session::SessionImpl;
4748
use crate::{Binder, TableCatalog, WithOptions};
4849

49-
pub async fn replace_table_with_definition(
50-
session: &Arc<SessionImpl>,
51-
table_name: ObjectName,
52-
definition: Statement,
53-
original_catalog: &Arc<TableCatalog>,
54-
format_encode: Option<FormatEncodeOptions>,
55-
) -> Result<()> {
56-
let (source, table, graph, col_index_mapping, job_type) = get_replace_table_plan(
57-
session,
58-
table_name,
59-
definition,
60-
original_catalog,
61-
format_encode,
62-
None,
63-
)
64-
.await?;
65-
66-
let catalog_writer = session.catalog_writer()?;
67-
68-
catalog_writer
69-
.replace_table(source, table, graph, col_index_mapping, job_type)
70-
.await?;
71-
Ok(())
72-
}
73-
7450
/// Used in auto schema change process
7551
pub async fn get_new_table_definition_for_cdc_table(
7652
session: &Arc<SessionImpl>,
@@ -84,9 +60,11 @@ pub async fn get_new_table_definition_for_cdc_table(
8460
.context("unable to parse original table definition")?
8561
.try_into()
8662
.unwrap();
63+
8764
let Statement::CreateTable {
8865
columns: original_columns,
8966
format_encode,
67+
constraints,
9068
..
9169
} = &mut definition
9270
else {
@@ -98,6 +76,22 @@ pub async fn get_new_table_definition_for_cdc_table(
9876
"source schema should be None for CDC table"
9977
);
10078

79+
if bind_table_constraints(constraints)?.is_empty() {
80+
// For table created by `create table t (*)` the constraint is empty, we need to
81+
// retrieve primary key names from original table catalog if available
82+
let pk_names: Vec<_> = original_catalog
83+
.pk
84+
.iter()
85+
.map(|x| original_catalog.columns[x.column_index].name().to_string())
86+
.collect();
87+
88+
constraints.push(TableConstraint::Unique {
89+
name: None,
90+
columns: pk_names.iter().map(Ident::new_unchecked).collect(),
91+
is_primary: true,
92+
});
93+
}
94+
10195
let orig_column_catalog: HashMap<String, ColumnCatalog> = HashMap::from_iter(
10296
original_catalog
10397
.columns()
@@ -163,9 +157,8 @@ fn to_ast_data_type(ty: &DataType) -> Result<AstDataType> {
163157
pub async fn get_replace_table_plan(
164158
session: &Arc<SessionImpl>,
165159
table_name: ObjectName,
166-
definition: Statement,
167-
original_catalog: &Arc<TableCatalog>,
168-
format_encode: Option<FormatEncodeOptions>,
160+
new_definition: Statement,
161+
old_catalog: &Arc<TableCatalog>,
169162
new_version_columns: Option<Vec<ColumnCatalog>>, // only provided in auto schema change
170163
) -> Result<(
171164
Option<Source>,
@@ -175,8 +168,8 @@ pub async fn get_replace_table_plan(
175168
TableJobType,
176169
)> {
177170
// Create handler args as if we're creating a new table with the altered definition.
178-
let handler_args = HandlerArgs::new(session.clone(), &definition, Arc::from(""))?;
179-
let col_id_gen = ColumnIdGenerator::new_alter(original_catalog);
171+
let handler_args = HandlerArgs::new(session.clone(), &new_definition, Arc::from(""))?;
172+
let col_id_gen = ColumnIdGenerator::new_alter(old_catalog);
180173
let Statement::CreateTable {
181174
columns,
182175
constraints,
@@ -186,16 +179,21 @@ pub async fn get_replace_table_plan(
186179
with_version_column,
187180
wildcard_idx,
188181
cdc_table_info,
182+
format_encode,
189183
..
190-
} = definition
184+
} = new_definition
191185
else {
192-
panic!("unexpected statement type: {:?}", definition);
186+
panic!("unexpected statement type: {:?}", new_definition);
193187
};
194188

189+
let format_encode = format_encode
190+
.clone()
191+
.map(|format_encode| format_encode.into_v2_with_warning());
192+
195193
let (mut graph, table, source, job_type) = generate_stream_graph_for_replace_table(
196194
session,
197195
table_name,
198-
original_catalog,
196+
old_catalog,
199197
format_encode,
200198
handler_args.clone(),
201199
col_id_gen,
@@ -213,7 +211,7 @@ pub async fn get_replace_table_plan(
213211

214212
// Calculate the mapping from the original columns to the new columns.
215213
let col_index_mapping = ColIndexMapping::new(
216-
original_catalog
214+
old_catalog
217215
.columns()
218216
.iter()
219217
.map(|old_c| {
@@ -225,7 +223,7 @@ pub async fn get_replace_table_plan(
225223
table.columns.len(),
226224
);
227225

228-
let incoming_sink_ids: HashSet<_> = original_catalog.incoming_sinks.iter().copied().collect();
226+
let incoming_sink_ids: HashSet<_> = old_catalog.incoming_sinks.iter().copied().collect();
229227

230228
let target_columns = table
231229
.columns
@@ -245,7 +243,7 @@ pub async fn get_replace_table_plan(
245243
// Set some fields ourselves so that the meta service does not need to maintain them.
246244
let mut table = table;
247245
table.incoming_sinks = incoming_sink_ids.iter().copied().collect();
248-
table.maybe_vnode_count = VnodeCount::set(original_catalog.vnode_count()).to_protobuf();
246+
table.maybe_vnode_count = VnodeCount::set(old_catalog.vnode_count()).to_protobuf();
249247

250248
Ok((source, table, graph, col_index_mapping, job_type))
251249
}
@@ -332,6 +330,7 @@ pub async fn handle_alter_table_column(
332330
else {
333331
panic!("unexpected statement: {:?}", definition);
334332
};
333+
335334
let format_encode = format_encode
336335
.clone()
337336
.map(|format_encode| format_encode.into_v2_with_warning());
@@ -455,15 +454,14 @@ pub async fn handle_alter_table_column(
455454
_ => unreachable!(),
456455
};
457456

458-
replace_table_with_definition(
459-
&session,
460-
table_name,
461-
definition,
462-
&original_catalog,
463-
format_encode,
464-
)
465-
.await?;
457+
let (source, table, graph, col_index_mapping, job_type) =
458+
get_replace_table_plan(&session, table_name, definition, &original_catalog, None).await?;
466459

460+
let catalog_writer = session.catalog_writer()?;
461+
462+
catalog_writer
463+
.replace_table(source, table, graph, col_index_mapping, job_type)
464+
.await?;
467465
Ok(PgResponse::empty_result(StatementType::ALTER_TABLE))
468466
}
469467

src/frontend/src/handler/alter_table_with_sr.rs

+34-30
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,16 @@
1414

1515
use anyhow::{anyhow, Context};
1616
use fancy_regex::Regex;
17-
use pgwire::pg_response::StatementType;
17+
use pgwire::pg_response::{PgResponse, StatementType};
1818
use risingwave_common::bail_not_implemented;
1919
use risingwave_sqlparser::ast::{FormatEncodeOptions, ObjectName, Statement};
2020
use risingwave_sqlparser::parser::Parser;
2121
use thiserror_ext::AsReport;
2222

2323
use super::alter_source_with_sr::alter_definition_format_encode;
24-
use super::alter_table_column::{
25-
fetch_table_catalog_for_alter, replace_table_with_definition, schema_has_schema_registry,
26-
};
24+
use super::alter_table_column::{fetch_table_catalog_for_alter, schema_has_schema_registry};
2725
use super::util::SourceSchemaCompatExt;
28-
use super::{HandlerArgs, RwPgResponse};
26+
use super::{get_replace_table_plan, HandlerArgs, RwPgResponse};
2927
use crate::error::{ErrorCode, Result};
3028
use crate::TableCatalog;
3129

@@ -66,6 +64,7 @@ pub async fn handle_refresh_schema(
6664
format_encode.unwrap()
6765
};
6866

67+
// NOTE(st1page): since we have not implemented alter format encode for table, it is actually no use.
6968
let definition = alter_definition_format_encode(
7069
&original_table.definition,
7170
format_encode.row_options.clone(),
@@ -76,31 +75,36 @@ pub async fn handle_refresh_schema(
7675
.try_into()
7776
.unwrap();
7877

79-
let result = replace_table_with_definition(
80-
&session,
81-
table_name,
82-
definition,
83-
&original_table,
84-
Some(format_encode),
85-
)
86-
.await;
87-
88-
match result {
89-
Ok(_) => Ok(RwPgResponse::empty_result(StatementType::ALTER_TABLE)),
90-
Err(e) => {
91-
let report = e.to_report_string();
92-
// This is a workaround for reporting errors when columns to drop is referenced by generated column.
93-
// Finding the actual columns to drop requires generating `PbSource` from the sql definition
94-
// and fetching schema from schema registry, which will cause a lot of unnecessary refactor.
95-
// Here we match the error message to yield when failing to bind generated column exprs.
96-
let re = Regex::new(r#"fail to bind expression in generated column "(.*?)""#).unwrap();
97-
let captures = re.captures(&report).map_err(anyhow::Error::from)?;
98-
if let Some(gen_col_name) = captures.and_then(|captures| captures.get(1)) {
99-
Err(anyhow!(e).context(format!("failed to refresh schema because some of the columns to drop are referenced by a generated column \"{}\"",
100-
gen_col_name.as_str())).into())
101-
} else {
102-
Err(e)
78+
let (source, table, graph, col_index_mapping, job_type) = {
79+
let result =
80+
get_replace_table_plan(&session, table_name, definition, &original_table, None).await;
81+
match result {
82+
Ok((source, table, graph, col_index_mapping, job_type)) => {
83+
Ok((source, table, graph, col_index_mapping, job_type))
84+
}
85+
Err(e) => {
86+
let report = e.to_report_string();
87+
// NOTE(yuhao): This is a workaround for reporting errors when columns to drop is referenced by generated column.
88+
// Finding the actual columns to drop requires generating `PbSource` from the sql definition
89+
// and fetching schema from schema registry, which will cause a lot of unnecessary refactor.
90+
// Here we match the error message to yield when failing to bind generated column exprs.
91+
let re =
92+
Regex::new(r#"fail to bind expression in generated column "(.*?)""#).unwrap();
93+
let captures = re.captures(&report).map_err(anyhow::Error::from)?;
94+
if let Some(gen_col_name) = captures.and_then(|captures| captures.get(1)) {
95+
Err(anyhow!(e).context(format!("failed to refresh schema because some of the columns to drop are referenced by a generated column \"{}\"",
96+
gen_col_name.as_str())).into())
97+
} else {
98+
Err(e)
99+
}
103100
}
104101
}
105-
}
102+
}?;
103+
let catalog_writer = session.catalog_writer()?;
104+
105+
catalog_writer
106+
.replace_table(source, table, graph, col_index_mapping, job_type)
107+
.await?;
108+
109+
Ok(PgResponse::empty_result(StatementType::ALTER_TABLE))
106110
}

src/frontend/src/handler/create_source.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ use crate::error::{Result, RwError};
7878
use crate::expr::Expr;
7979
use crate::handler::create_table::{
8080
bind_pk_and_row_id_on_relation, bind_sql_column_constraints, bind_sql_columns,
81-
bind_sql_pk_names, ensure_table_constraints_supported, ColumnIdGenerator,
81+
bind_sql_pk_names, bind_table_constraints, ColumnIdGenerator,
8282
};
8383
use crate::handler::util::SourceSchemaCompatExt;
8484
use crate::handler::HandlerArgs;
@@ -1523,8 +1523,7 @@ pub async fn bind_create_source_or_table_with_connector(
15231523
}
15241524
}
15251525

1526-
ensure_table_constraints_supported(&constraints)?;
1527-
let sql_pk_names = bind_sql_pk_names(sql_columns_defs, &constraints)?;
1526+
let sql_pk_names = bind_sql_pk_names(sql_columns_defs, bind_table_constraints(&constraints)?)?;
15281527

15291528
let columns_from_sql = bind_sql_columns(sql_columns_defs)?;
15301529

0 commit comments

Comments
 (0)