1use std::collections::hash_map::Entry;
16use std::ops::Deref;
17
18use itertools::{EitherOrBoth, Itertools};
19use risingwave_common::bail;
20use risingwave_common::catalog::{Field, TableId};
21use risingwave_sqlparser::ast::{
22 AsOf, Expr as ParserExpr, FunctionArg, FunctionArgExpr, Ident, ObjectName, TableAlias,
23 TableFactor,
24};
25use thiserror::Error;
26use thiserror_ext::AsReport;
27
28use super::bind_context::ColumnBinding;
29use super::statement::RewriteExprsRecursive;
30use crate::binder::Binder;
31use crate::binder::bind_context::{BindingCte, BindingCteState};
32use crate::error::{ErrorCode, Result, RwError};
33use crate::expr::{ExprImpl, InputRef};
34
35mod gap_fill;
36mod join;
37mod match_recognize;
38mod share;
39mod subquery;
40mod table_function;
41mod table_or_source;
42mod watermark;
43mod window_table_function;
44
45pub use gap_fill::BoundGapFill;
46pub use join::BoundJoin;
47pub use match_recognize::{
48 BoundMatchRecognize, BoundMeasure, BoundSymbolDefinition, MeasureSlotKind,
49};
50pub use share::{BoundShare, BoundShareInput};
51pub use subquery::BoundSubquery;
52pub use table_or_source::{
53 BoundBaseTable, BoundIcebergMetadataTable, BoundSource, BoundSystemTable,
54};
55pub use watermark::BoundWatermark;
56pub use window_table_function::{BoundWindowTableFunction, WindowTableFunctionKind};
57
58use crate::expr::{CorrelatedId, ScopeId};
59
60#[derive(Debug, Clone)]
63pub enum Relation {
64 Source(Box<BoundSource>),
65 BaseTable(Box<BoundBaseTable>),
66 SystemTable(Box<BoundSystemTable>),
67 IcebergMetadataTable(Box<BoundIcebergMetadataTable>),
68 Subquery(Box<BoundSubquery>),
69 Join(Box<BoundJoin>),
70 Apply(Box<BoundJoin>),
71 WindowTableFunction(Box<BoundWindowTableFunction>),
72 TableFunction {
74 expr: ExprImpl,
75 with_ordinality: bool,
76 },
77 Watermark(Box<BoundWatermark>),
78 Share(Box<BoundShare>),
79 GapFill(Box<BoundGapFill>),
80 MatchRecognize(Box<BoundMatchRecognize>),
81}
82
83impl RewriteExprsRecursive for Relation {
84 fn rewrite_exprs_recursive(&mut self, rewriter: &mut impl crate::expr::ExprRewriter) {
85 match self {
86 Relation::Subquery(inner) => inner.rewrite_exprs_recursive(rewriter),
87 Relation::Join(inner) => inner.rewrite_exprs_recursive(rewriter),
88 Relation::Apply(inner) => inner.rewrite_exprs_recursive(rewriter),
89 Relation::WindowTableFunction(inner) => inner.rewrite_exprs_recursive(rewriter),
90 Relation::Watermark(inner) => inner.rewrite_exprs_recursive(rewriter),
91 Relation::Share(inner) => inner.rewrite_exprs_recursive(rewriter),
92 Relation::TableFunction { expr: inner, .. } => {
93 *inner = rewriter.rewrite_expr(inner.take())
94 }
95 Relation::MatchRecognize(inner) => {
96 inner.input.rewrite_exprs_recursive(rewriter);
97 for e in inner.exprs_mut() {
98 *e = rewriter.rewrite_expr(e.take());
99 }
100 }
101 _ => {}
102 }
103 }
104}
105
106impl Relation {
107 pub fn is_correlated_by_scope(&self, scope: ScopeId) -> bool {
108 match self {
109 Relation::Subquery(subquery) => subquery.query.is_correlated_by_scope(scope),
110 Relation::Join(join) | Relation::Apply(join) => {
111 join.cond.has_correlated_input_ref_by_scope(scope)
112 || join.left.is_correlated_by_scope(scope)
113 || join.right.is_correlated_by_scope(scope)
114 }
115 Relation::TableFunction {
116 expr: table_function,
117 with_ordinality: _,
118 } => table_function.has_correlated_input_ref_by_scope(scope),
119 Relation::Share(share) => match &share.input {
120 BoundShareInput::Query(query) => query.is_correlated_by_scope(scope),
121 BoundShareInput::ChangeLog { relation, .. } => {
122 relation.is_correlated_by_scope(scope)
123 }
124 },
125 Relation::MatchRecognize(inner) => {
126 inner.input.is_correlated_by_scope(scope)
127 || inner
128 .exprs()
129 .any(|e| e.has_correlated_input_ref_by_scope(scope))
130 }
131 _ => false,
132 }
133 }
134
135 pub fn is_correlated_by_correlated_id(&self, correlated_id: CorrelatedId) -> bool {
136 match self {
137 Relation::Subquery(subquery) => {
138 subquery.query.is_correlated_by_correlated_id(correlated_id)
139 }
140 Relation::Join(join) | Relation::Apply(join) => {
141 join.cond
142 .has_correlated_input_ref_by_correlated_id(correlated_id)
143 || join.left.is_correlated_by_correlated_id(correlated_id)
144 || join.right.is_correlated_by_correlated_id(correlated_id)
145 }
146 Relation::TableFunction {
147 expr: table_function,
148 with_ordinality: _,
149 } => table_function.has_correlated_input_ref_by_correlated_id(correlated_id),
150 Relation::Share(share) => match &share.input {
151 BoundShareInput::Query(query) => {
152 query.is_correlated_by_correlated_id(correlated_id)
153 }
154 BoundShareInput::ChangeLog { relation, .. } => {
155 relation.is_correlated_by_correlated_id(correlated_id)
156 }
157 },
158 Relation::MatchRecognize(inner) => {
159 inner.input.is_correlated_by_correlated_id(correlated_id)
160 || inner
161 .exprs()
162 .any(|e| e.has_correlated_input_ref_by_correlated_id(correlated_id))
163 }
164 _ => false,
165 }
166 }
167
168 pub fn collect_correlated_indices_by_scope_and_assign_id(
169 &mut self,
170 scope: ScopeId,
171 correlated_id: CorrelatedId,
172 ) -> Vec<usize> {
173 match self {
174 Relation::Subquery(subquery) => subquery
175 .query
176 .collect_correlated_indices_by_scope_and_assign_id(scope, correlated_id),
177 Relation::Join(join) | Relation::Apply(join) => {
178 let mut correlated_indices = vec![];
179 correlated_indices.extend(
180 join.cond
181 .collect_correlated_indices_by_scope_and_assign_id(scope, correlated_id),
182 );
183 correlated_indices.extend(
184 join.left
185 .collect_correlated_indices_by_scope_and_assign_id(scope, correlated_id),
186 );
187 correlated_indices.extend(
188 join.right
189 .collect_correlated_indices_by_scope_and_assign_id(scope, correlated_id),
190 );
191 correlated_indices
192 }
193 Relation::TableFunction {
194 expr: table_function,
195 with_ordinality: _,
196 } => table_function
197 .collect_correlated_indices_by_scope_and_assign_id(scope, correlated_id),
198 Relation::Share(share) => {
199 match &mut share.input {
200 BoundShareInput::Query(query) => query
201 .collect_correlated_indices_by_scope_and_assign_id(scope, correlated_id),
202 BoundShareInput::ChangeLog { relation, .. } => relation
203 .collect_correlated_indices_by_scope_and_assign_id(scope, correlated_id),
204 }
205 }
206 Relation::MatchRecognize(inner) => {
207 let mut indices = inner
208 .input
209 .collect_correlated_indices_by_scope_and_assign_id(scope, correlated_id);
210 for e in inner.exprs_mut() {
211 indices.extend(
212 e.collect_correlated_indices_by_scope_and_assign_id(scope, correlated_id),
213 );
214 }
215 indices
216 }
217 _ => vec![],
218 }
219 }
220}
221
222#[derive(Debug)]
223#[non_exhaustive]
224pub enum ResolveQualifiedNameErrorKind {
225 QualifiedNameTooLong,
226 NotCurrentDatabase,
227}
228
229#[derive(Debug, Error)]
230pub struct ResolveQualifiedNameError {
231 qualified: String,
232 kind: ResolveQualifiedNameErrorKind,
233}
234
235impl std::fmt::Display for ResolveQualifiedNameError {
236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
237 match self.kind {
238 ResolveQualifiedNameErrorKind::QualifiedNameTooLong => write!(
239 f,
240 "improper qualified name (too many dotted names): {}",
241 self.qualified
242 ),
243 ResolveQualifiedNameErrorKind::NotCurrentDatabase => write!(
244 f,
245 "cross-database references are not implemented: \"{}\"",
246 self.qualified
247 ),
248 }
249 }
250}
251
252impl ResolveQualifiedNameError {
253 pub fn new(qualified: String, kind: ResolveQualifiedNameErrorKind) -> Self {
254 Self { qualified, kind }
255 }
256}
257
258impl From<ResolveQualifiedNameError> for RwError {
259 fn from(e: ResolveQualifiedNameError) -> Self {
260 ErrorCode::InvalidInputSyntax(format!("{}", e.as_report())).into()
261 }
262}
263
264impl Binder {
265 pub fn resolve_schema_qualified_name(
267 db_name: &str,
268 name: &ObjectName,
269 ) -> std::result::Result<(Option<String>, String), ResolveQualifiedNameError> {
270 let formatted_name = name.to_string();
271 let mut identifiers = name.0.clone();
272
273 if identifiers.len() > 3 {
274 return Err(ResolveQualifiedNameError::new(
275 formatted_name,
276 ResolveQualifiedNameErrorKind::QualifiedNameTooLong,
277 ));
278 }
279
280 let name = identifiers.pop().unwrap().real_value();
281
282 let schema_name = identifiers.pop().map(|ident| ident.real_value());
283 let database_name = identifiers.pop().map(|ident| ident.real_value());
284
285 if let Some(database_name) = database_name
286 && database_name != db_name
287 {
288 return Err(ResolveQualifiedNameError::new(
289 formatted_name,
290 ResolveQualifiedNameErrorKind::NotCurrentDatabase,
291 ));
292 }
293
294 Ok((schema_name, name))
295 }
296
297 pub fn validate_cross_db_reference(
299 db_name: &str,
300 name: &ObjectName,
301 ) -> std::result::Result<(), ResolveQualifiedNameError> {
302 let formatted_name = name.to_string();
303 let identifiers = &name.0;
304 if identifiers.len() > 3 {
305 return Err(ResolveQualifiedNameError::new(
306 formatted_name,
307 ResolveQualifiedNameErrorKind::QualifiedNameTooLong,
308 ));
309 }
310
311 if identifiers.len() == 3 && identifiers[0].real_value() != db_name {
312 return Err(ResolveQualifiedNameError::new(
313 formatted_name,
314 ResolveQualifiedNameErrorKind::NotCurrentDatabase,
315 ));
316 }
317
318 Ok(())
319 }
320
321 pub fn resolve_db_schema_qualified_name(
323 name: &ObjectName,
324 ) -> std::result::Result<(Option<String>, Option<String>, String), ResolveQualifiedNameError>
325 {
326 let formatted_name = name.to_string();
327 let mut identifiers = name.0.clone();
328
329 if identifiers.len() > 3 {
330 return Err(ResolveQualifiedNameError::new(
331 formatted_name,
332 ResolveQualifiedNameErrorKind::QualifiedNameTooLong,
333 ));
334 }
335
336 let name = identifiers.pop().unwrap().real_value();
337 let schema_name = identifiers.pop().map(|ident| ident.real_value());
338 let database_name = identifiers.pop().map(|ident| ident.real_value());
339
340 Ok((database_name, schema_name, name))
341 }
342
343 fn resolve_single_name(mut identifiers: Vec<Ident>, ident_desc: &str) -> Result<String> {
345 if identifiers.len() > 1 {
346 bail!("{} must contain 1 argument", ident_desc);
347 }
348 let name = identifiers.pop().unwrap().real_value();
349
350 Ok(name)
351 }
352
353 pub fn resolve_database_name(name: ObjectName) -> Result<String> {
355 Self::resolve_single_name(name.0, "database name")
356 }
357
358 pub fn resolve_schema_name(name: ObjectName) -> Result<String> {
360 Self::resolve_single_name(name.0, "schema name")
361 }
362
363 pub fn resolve_index_name(name: ObjectName) -> Result<String> {
365 Self::resolve_single_name(name.0, "index name")
366 }
367
368 pub fn resolve_view_name(name: ObjectName) -> Result<String> {
370 Self::resolve_single_name(name.0, "view name")
371 }
372
373 pub fn resolve_sink_name(name: ObjectName) -> Result<String> {
375 Self::resolve_single_name(name.0, "sink name")
376 }
377
378 pub fn resolve_subscription_name(name: ObjectName) -> Result<String> {
380 Self::resolve_single_name(name.0, "subscription name")
381 }
382
383 pub fn resolve_table_name(name: ObjectName) -> Result<String> {
385 Self::resolve_single_name(name.0, "table name")
386 }
387
388 pub fn resolve_source_name(name: ObjectName) -> Result<String> {
390 Self::resolve_single_name(name.0, "source name")
391 }
392
393 pub fn resolve_user_name(name: ObjectName) -> Result<String> {
395 Self::resolve_single_name(name.0, "user name")
396 }
397
398 pub(super) fn bind_table_to_context(
400 &mut self,
401 columns: impl IntoIterator<Item = (bool, Field)>, table_name: String,
403 schema_name: Option<String>,
404 alias: Option<&TableAlias>,
405 ) -> Result<()> {
406 const EMPTY: [Ident; 0] = [];
407 let (resolved_schema_name, table_name, column_aliases, table_alias) = match alias {
408 None => (schema_name.clone(), table_name, &EMPTY[..], None),
409 Some(TableAlias { name, columns }) => (
410 None,
411 name.real_value(),
412 columns.as_slice(),
413 Some(table_name),
414 ),
415 };
416
417 let num_col_aliases = column_aliases.len();
418
419 let begin = self.context.columns.len();
420 let mut alias_iter = column_aliases.iter().fuse();
423 let mut index = 0;
424 columns.into_iter().for_each(|(is_hidden, mut field)| {
425 let name = match is_hidden {
426 true => field.name.clone(),
427 false => alias_iter
428 .next()
429 .map(|t| t.real_value())
430 .unwrap_or_else(|| field.name.clone()),
431 };
432 field.name.clone_from(&name);
433 self.context.columns.push(ColumnBinding::new(
434 table_name.clone(),
435 schema_name.clone(),
436 table_alias.clone(),
437 begin + index,
438 is_hidden,
439 field,
440 ));
441 self.context
442 .indices_of
443 .entry(name)
444 .or_default()
445 .push(self.context.columns.len() - 1);
446 index += 1;
447 });
448
449 let num_cols = index;
450 if num_cols < num_col_aliases {
451 return Err(ErrorCode::BindError(format!(
452 "table \"{table_name}\" has {num_cols} columns available but {num_col_aliases} column aliases specified",
453 ))
454 .into());
455 }
456
457 match self
458 .context
459 .range_of
460 .entry((resolved_schema_name, table_name.clone()))
461 {
462 Entry::Occupied(_) => Err(ErrorCode::InternalError(format!(
463 "Duplicated table name while binding table to context: {}",
464 table_name
465 ))
466 .into()),
467 Entry::Vacant(entry) => {
468 entry.insert((begin, self.context.columns.len()));
469 Ok(())
470 }
471 }
472 }
473
474 pub fn bind_relation_by_name(
479 &mut self,
480 name: &ObjectName,
481 alias: Option<&TableAlias>,
482 as_of: Option<&AsOf>,
483 allow_cross_db: bool,
484 ) -> Result<Relation> {
485 let (db_name, schema_name, table_name) = if allow_cross_db {
486 Self::resolve_db_schema_qualified_name(name)?
487 } else {
488 let (schema_name, table_name) =
489 Self::resolve_schema_qualified_name(&self.db_name, name)?;
490 (None, schema_name, table_name)
491 };
492
493 if schema_name.is_none()
494 && let Some(item) = self.context.cte_to_relation.get(&table_name)
496 {
497 if as_of.is_some() {
500 return Err(ErrorCode::BindError(
501 "Right table of a temporal join should not be a CTE. \
502 It should be a table, index, or materialized view"
503 .to_owned(),
504 )
505 .into());
506 }
507
508 let BindingCte {
509 share_id,
510 state: cte_state,
511 alias: mut original_alias,
512 } = item.deref().borrow().clone();
513
514 debug_assert_eq!(original_alias.name.real_value(), table_name);
516
517 if let Some(from_alias) = alias {
518 original_alias.name = from_alias.name.clone();
519 original_alias.columns = original_alias
520 .columns
521 .into_iter()
522 .zip_longest(from_alias.columns.iter().cloned())
523 .map(EitherOrBoth::into_right)
524 .collect();
525 }
526
527 let exposed_table_name = original_alias.name.real_value();
528 self.context
529 .check_relation_name_conflict(&exposed_table_name)?;
530
531 match cte_state {
532 BindingCteState::Bound { query } => {
533 let input = BoundShareInput::Query(query);
534 self.bind_table_to_context(
535 input.fields()?,
536 table_name,
537 None,
538 Some(&original_alias),
539 )?;
540 self.context.add_cte_name(exposed_table_name);
541 Ok(Relation::Share(Box::new(BoundShare { share_id, input })))
544 }
545 BindingCteState::ChangeLog { table, key_indices } => {
546 let input = BoundShareInput::ChangeLog {
547 relation: table,
548 key_indices,
549 };
550 self.bind_table_to_context(
551 input.fields()?,
552 table_name,
553 None,
554 Some(&original_alias),
555 )?;
556 self.context.add_cte_name(exposed_table_name);
557 Ok(Relation::Share(Box::new(BoundShare { share_id, input })))
558 }
559 }
560 } else {
561 let exposed_table_name = alias
562 .map(|alias| alias.name.real_value())
563 .unwrap_or_else(|| table_name.clone());
564 self.context.check_catalog_name(&exposed_table_name)?;
565 self.bind_catalog_relation_by_name(
566 db_name.as_deref(),
567 schema_name.as_deref(),
568 &table_name,
569 alias,
570 as_of,
571 false,
572 )
573 }
574 }
575
576 fn bind_relation_by_function_arg(
578 &mut self,
579 arg: Option<&FunctionArg>,
580 err_msg: &str,
581 ) -> Result<(Relation, ObjectName)> {
582 let Some(FunctionArg::Unnamed(FunctionArgExpr::Expr(expr))) = arg else {
583 return Err(ErrorCode::BindError(err_msg.to_owned()).into());
584 };
585 let table_name = match expr {
586 ParserExpr::Identifier(ident) => Ok::<_, RwError>(ObjectName(vec![ident.clone()])),
587 ParserExpr::CompoundIdentifier(idents) => Ok(ObjectName(idents.clone())),
588 _ => Err(ErrorCode::BindError(err_msg.to_owned()).into()),
589 }?;
590
591 Ok((
592 self.bind_relation_by_name(&table_name, None, None, true)?,
593 table_name,
594 ))
595 }
596
597 fn bind_column_by_function_args(
599 &mut self,
600 arg: Option<&FunctionArg>,
601 err_msg: &str,
602 ) -> Result<Box<InputRef>> {
603 if let Some(time_col_arg) = arg
604 && let Some(ExprImpl::InputRef(time_col)) =
605 self.bind_function_arg(time_col_arg)?.into_iter().next()
606 {
607 Ok(time_col)
608 } else {
609 Err(ErrorCode::BindError(err_msg.to_owned()).into())
610 }
611 }
612
613 fn bind_internal_table(
615 &mut self,
616 args: &[FunctionArg],
617 alias: Option<&TableAlias>,
618 ) -> Result<Relation> {
619 if args.is_empty() || args.len() > 2 {
620 return Err(
621 ErrorCode::BindError("usage: rw_table(table_id[,schema_name])".to_owned()).into(),
622 );
623 }
624
625 let table_id: TableId = args[0]
626 .to_string()
627 .parse::<u32>()
628 .map_err(|err| {
629 RwError::from(ErrorCode::BindError(format!(
630 "invalid table id: {}",
631 err.as_report()
632 )))
633 })?
634 .into();
635
636 let schema = args.get(1).map(|arg| arg.to_string());
637
638 let table_name = self.catalog.get_table_name_by_id(table_id)?;
639 self.bind_catalog_relation_by_name(None, schema.as_deref(), &table_name, alias, None, false)
640 }
641
642 pub(super) fn bind_table_factor(&mut self, table_factor: &TableFactor) -> Result<Relation> {
643 match table_factor {
644 TableFactor::Table { name, alias, as_of } => {
645 self.bind_relation_by_name(name, alias.as_ref(), as_of.as_ref(), true)
646 }
647 TableFactor::TableFunction {
648 name,
649 alias,
650 args,
651 with_ordinality,
652 } => {
653 let visibility = self.mark_lateral_contexts_visible();
654 let result = self.bind_table_function(name, alias.as_ref(), args, *with_ordinality);
655 self.restore_lateral_contexts_visibility(visibility);
656 result
657 }
658 TableFactor::Derived {
659 lateral,
660 subquery,
661 alias,
662 } => {
663 if *lateral {
664 let visibility = self.mark_lateral_contexts_visible();
665
666 let result = self.bind_subquery_relation(subquery, alias.as_ref(), true);
668
669 self.restore_lateral_contexts_visibility(visibility);
670 result.map(|subquery| Relation::Subquery(Box::new(subquery)))
671 } else {
672 self.push_lateral_context();
674 let bound_subquery =
675 self.bind_subquery_relation(subquery, alias.as_ref(), false)?;
676 self.pop_and_merge_lateral_context()?;
677 Ok(Relation::Subquery(Box::new(bound_subquery)))
678 }
679 }
680 TableFactor::NestedJoin(table_with_joins) => {
681 self.push_lateral_context();
682 let bound_join = self.bind_table_with_joins(table_with_joins)?;
683 self.pop_and_merge_lateral_context()?;
684 Ok(bound_join)
685 }
686 TableFactor::MatchRecognize {
687 table,
688 partition_by,
689 order_by,
690 measures,
691 rows_per_match,
692 after_match_skip,
693 pattern,
694 within,
695 subsets,
696 symbols,
697 alias,
698 } => Ok(Relation::MatchRecognize(Box::new(
699 self.bind_match_recognize(
700 table,
701 partition_by,
702 order_by,
703 measures,
704 rows_per_match,
705 after_match_skip,
706 pattern,
707 within,
708 subsets,
709 symbols,
710 alias.as_ref(),
711 )?,
712 ))),
713 }
714 }
715}