risingwave_frontend/optimizer/rule/
table_function_to_mysql_query_rule.rs

1// Copyright 2025 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use itertools::Itertools;
16use risingwave_common::catalog::{Field, Schema};
17use risingwave_common::types::{DataType, ScalarImpl};
18
19use super::prelude::{PlanRef, *};
20use crate::expr::{Expr, TableFunctionType};
21use crate::optimizer::plan_node::generic::GenericPlanRef;
22use crate::optimizer::plan_node::{LogicalMySqlQuery, LogicalTableFunction};
23
24/// Transform a special `TableFunction` (with `MYSQL_QUERY` table function type) into a `LogicalMySqlQuery`
25pub struct TableFunctionToMySqlQueryRule {}
26impl Rule<Logical> for TableFunctionToMySqlQueryRule {
27    fn apply(&self, plan: PlanRef) -> Option<PlanRef> {
28        let logical_table_function: &LogicalTableFunction = plan.as_logical_table_function()?;
29        if logical_table_function.table_function.function_type != TableFunctionType::MysqlQuery {
30            return None;
31        }
32        assert!(!logical_table_function.with_ordinality);
33        let table_function_return_type = logical_table_function.table_function().return_type();
34
35        if let DataType::Struct(st) = table_function_return_type.clone() {
36            let fields = st
37                .iter()
38                .map(|(name, data_type)| Field::with_name(data_type.clone(), name.to_owned()))
39                .collect_vec();
40
41            let schema = Schema::new(fields);
42
43            assert_eq!(logical_table_function.table_function().args.len(), 6);
44            let mut eval_args = vec![];
45            for arg in &logical_table_function.table_function().args {
46                assert_eq!(arg.return_type(), DataType::Varchar);
47                let value = arg.try_fold_const().unwrap().unwrap();
48                match value {
49                    Some(ScalarImpl::Utf8(s)) => {
50                        eval_args.push(s.to_string());
51                    }
52                    _ => {
53                        unreachable!("must be a varchar")
54                    }
55                }
56            }
57            let hostname = eval_args[0].clone();
58            let port = eval_args[1].clone();
59            let username = eval_args[2].clone();
60            let password = eval_args[3].clone();
61            let database = eval_args[4].clone();
62            let query = eval_args[5].clone();
63
64            Some(
65                LogicalMySqlQuery::new(
66                    logical_table_function.ctx(),
67                    schema,
68                    hostname,
69                    port,
70                    username,
71                    password,
72                    database,
73                    query,
74                )
75                .into(),
76            )
77        } else {
78            unreachable!("TableFunction return type should be struct")
79        }
80    }
81}
82
83impl TableFunctionToMySqlQueryRule {
84    pub fn create() -> BoxedRule {
85        Box::new(TableFunctionToMySqlQueryRule {})
86    }
87}