Skip to main content

sql_fun_sqlast/sem/data_source/
analyze.rs

1mod impl_analyze_range_var_node;
2
3use sql_fun_core::IVec;
4
5use crate::{
6    AstAndContextPair,
7    sem::{
8        AliasSpec, AnalysisError, ColumnName, DataSourceRowFunctions, DataSourceRowsFromFunc,
9        FromClause, FullName, JoinKind, ParseContext, SemAst, WithClause, analyze_node,
10        analyze_scaler_expr,
11        data_source::{JoinExprDataSource, join_expr::JoinUsingClause},
12    },
13    syn::{ListOpt, Opt, ScanToken},
14};
15
16use super::{DataSource, SubQueryDataSource};
17
18impl DataSource {
19    // subquery can not to use CTEs, not need to with_clause
20    fn analyze_range_subselect<TParseContext>(
21        mut context: TParseContext,
22        range_subsel: crate::syn::RangeSubselect,
23        tokens: &IVec<ScanToken>,
24    ) -> Result<(Self, TParseContext), AnalysisError>
25    where
26        TParseContext: ParseContext,
27    {
28        let Some(alias) = range_subsel.get_alias().as_inner() else {
29            AnalysisError::raise_unexpected_none("range_subsel.alias")?
30        };
31        let (alias_name, new_context) = AliasSpec::analyze_without_relname(context, &alias)?;
32        context = new_context;
33
34        let Some(query) = range_subsel.get_subquery().as_inner() else {
35            AnalysisError::raise_unexpected_none("range_subselect.subquery")?
36        };
37        let AstAndContextPair(sem, new_context) = analyze_node(context, &None, query, tokens)?;
38        let SemAst::SelectStatement(s) = sem else {
39            AnalysisError::raise_unexpected_none("subquery is not select?")?
40        };
41        Ok((
42            SubQueryDataSource::new_data_source(&s, &alias_name),
43            new_context,
44        ))
45    }
46}
47
48#[cfg(test)]
49mod test_analyze_range_subselect {
50    use sql_fun_core::IVec;
51    use testresult::TestResult;
52
53    use crate::{
54        syn::{Node, NodeList, WithClauseOpt},
55        test_helpers::{SynBuilder, TestParseContext, test_context},
56    };
57
58    #[rstest::rstest]
59    fn test_analyze_range_subselect(test_context: TestParseContext) -> TestResult {
60        let builder = SynBuilder::new();
61
62        let res_target = builder.res_target(
63            "name",
64            crate::syn::NodeInner::AConst(builder.const_int4(10)).into(),
65        );
66        let query = builder.select_stmt(
67            WithClauseOpt::none(),
68            NodeList::from(Vec::<Node>::new()),
69            vec![res_target],
70        );
71        let alias_spec = builder.alias_name_only("alias1");
72        let range_subselect = builder.range_subselect(alias_spec, query);
73        let tokens = IVec::default();
74
75        super::DataSource::analyze_range_subselect(test_context, range_subselect, &tokens)?;
76        Ok(())
77    }
78}
79
80impl DataSource {
81    fn analyze_join_using_clause<TParseContext>(
82        mut context: TParseContext,
83        join_expr: crate::syn::JoinExpr,
84    ) -> Result<(Option<JoinUsingClause>, TParseContext), AnalysisError>
85    where
86        TParseContext: ParseContext,
87    {
88        if let Some(using_columns) = join_expr.get_using_clause().as_inner()
89            && !using_columns.is_empty()
90        {
91            let using_columns: Vec<ColumnName> = using_columns
92                .iter()
93                .filter_map(|c| c.as_string().get_sval())
94                .map(|v| ColumnName::from(v.as_str()))
95                .collect();
96
97            let (alias, new_context) =
98                if let Some(alias) = join_expr.get_join_using_alias().as_inner() {
99                    let (alias, new_context) = AliasSpec::analyze_without_relname(context, &alias)?;
100                    (Some(alias), new_context)
101                } else {
102                    (None, context)
103                };
104
105            context = new_context;
106            let join_using = JoinUsingClause::new(&using_columns, &alias);
107            Ok((Some(join_using), context))
108        } else {
109            Ok((None, context))
110        }
111    }
112
113    fn analyze_join_expr<TParseContext>(
114        mut context: TParseContext,
115        with_clause: &WithClause,
116        from_clause: &FromClause,
117        join_expr: crate::syn::JoinExpr,
118        tokens: &IVec<ScanToken>,
119    ) -> Result<(Self, TParseContext), AnalysisError>
120    where
121        TParseContext: ParseContext,
122    {
123        let mut from_clause = from_clause.clone();
124
125        let Some(left) = join_expr.get_larg().as_inner() else {
126            AnalysisError::raise_unexpected_none("join_expr.larg")?
127        };
128        let Some(right) = join_expr.get_rarg().as_inner() else {
129            AnalysisError::raise_unexpected_none("join_expr.rarg")?
130        };
131
132        let (left_source, new_context) =
133            Self::analyze_from_clause_node(context, with_clause, &from_clause, left, tokens)?;
134        context = new_context;
135        let (right_source, new_context) =
136            Self::analyze_from_clause_node(context, with_clause, &from_clause, right, tokens)?;
137        context = new_context;
138
139        from_clause.push(left_source.clone());
140        from_clause.push(right_source.clone());
141
142        let qualifications = if let Some(qualifications) = join_expr.get_quals().as_inner() {
143            let (quals, new_context) =
144                analyze_scaler_expr(context, with_clause, &from_clause, qualifications, tokens)?;
145            context = new_context;
146            Some(quals)
147        } else {
148            None
149        };
150
151        let alias = if let Some(alias) = join_expr.get_alias().as_inner() {
152            let (alias, new_context) = AliasSpec::analyze_without_relname(context, &alias)?;
153            context = new_context;
154            Some(alias)
155        } else {
156            None
157        };
158
159        let join_kind = JoinKind::try_from(join_expr.get_jointype())?;
160        let natural = join_expr.get_is_natural();
161        let (using_clause, new_context) = Self::analyze_join_using_clause(context, join_expr)?;
162        context = new_context;
163
164        Ok((
165            JoinExprDataSource::new_data_source(
166                &join_kind,
167                &left_source,
168                &right_source,
169                &qualifications,
170                &alias,
171                &using_clause,
172                natural,
173            ),
174            context,
175        ))
176    }
177}
178
179#[cfg(test)]
180mod test_analyze_join_expr {
181    use crate::{
182        sem::{
183            ColumnDefinition, ColumnName, FromClause, FullName, SchemaName, TypeReference,
184            WithClause,
185        },
186        syn::{AliasOpt, JoinType, NodeInner},
187        test_helpers::{SynBuilder, TestParseContext, test_context},
188    };
189    use sql_fun_core::IVec;
190    use testresult::TestResult;
191
192    #[rstest::rstest]
193    fn test_analyze_join_expr(mut test_context: TestParseContext) -> TestResult {
194        test_context.set_get_search_path_result(&vec![
195            SchemaName::from("public"),
196            SchemaName::from("pg_catalog"),
197        ]);
198        let int4_type_ref = TypeReference::from_master_type_name("int4");
199        test_context.setup_type(int4_type_ref.clone());
200        let columns = vec![ColumnDefinition::new(
201            &Some(ColumnName::from("id")),
202            Some(&int4_type_ref),
203            Some(true),
204        )];
205        test_context.setup_table("table1", &columns);
206        test_context.setup_table("table2", &columns);
207        test_context.setup_binary("=", "int4", "int4", "bool", true);
208
209        let builder = SynBuilder::new();
210
211        let name = FullName::from_local_name("table1");
212        let alias = builder.alias("t1", &[]);
213        let table1 = NodeInner::RangeVar(builder.range_var(&name, &alias.into()));
214        let name = FullName::from_local_name("table2");
215        let alias = builder.alias("t2", &[]);
216        let table2 = NodeInner::RangeVar(builder.range_var(&name, &alias.into()));
217        let left = builder.column_ref("t1", "id");
218        let right = builder.column_ref("t2", "id");
219        let qual = builder.operator_equals(
220            NodeInner::ColumnRef(left).into(),
221            NodeInner::ColumnRef(right).into(),
222        );
223
224        let join_expr = builder.join_expr(
225            JoinType::JoinInner.into(),
226            table1.into(),
227            table2.into(),
228            qual,
229            AliasOpt::none(),
230            &[],
231            AliasOpt::none(),
232            false,
233        );
234
235        let with_clause = WithClause::default();
236        let from_clause = FromClause::default();
237        let tokens = IVec::default();
238
239        let (_ds, _) = super::DataSource::analyze_join_expr(
240            test_context,
241            &with_clause,
242            &from_clause,
243            join_expr,
244            &tokens,
245        )?;
246
247        Ok(())
248    }
249}
250
251impl DataSource {
252    fn analyze_range_table_func<TParseContext>(
253        context: TParseContext,
254        with_clause: &WithClause,
255        from_clause: &FromClause,
256        node: crate::syn::RangeTableFunc,
257        tokens: &IVec<ScanToken>,
258    ) -> Result<(Self, TParseContext), AnalysisError>
259    where
260        TParseContext: ParseContext,
261    {
262        let Some(row_expr) = node.get_rowexpr().as_inner() else {
263            AnalysisError::raise_unexpected_none("RangeTableFunc.rowexpr")?
264        };
265        let Some(call) = row_expr.as_func_call().as_inner() else {
266            AnalysisError::raise_unexpected_none("RangeTableFunc.rowexpr is not FuncCall")?
267        };
268        let _func_name = FullName::try_from(call.get_funcname())?;
269
270        Self::call_table_func(
271            context,
272            with_clause,
273            from_clause,
274            call,
275            node.get_alias().as_inner(),
276            tokens,
277        )
278    }
279}
280
281#[cfg(test)]
282mod test_data_source_analyze_range_table_func {
283    use sql_fun_core::IVec;
284    use testresult::TestResult;
285
286    use crate::{
287        sem::{DataSource, SchemaName, WithClause},
288        test_helpers::{SynBuilder, TestParseContext, setup_function_overload, test_context},
289    };
290
291    #[rstest::rstest]
292    fn test_analyze_range_table_func(mut test_context: TestParseContext) -> TestResult {
293        test_context.set_get_search_path_result(&vec![SchemaName::from("public")]);
294        let (int4, overload) = setup_function_overload();
295        test_context.setup_type(int4);
296        test_context.setup_function("table_func", &[overload.clone()]);
297
298        let builder = SynBuilder::new();
299        let func_call_node = builder.build_func_call_node("table_func");
300        let alias = builder.alias("func_alias", &[]);
301        let range_table_func = builder.build_range_table_func(func_call_node, alias);
302        let tokens = IVec::default();
303        let with_clause = WithClause::default();
304        let from_clause = super::FromClause::default();
305
306        let (ds, _) = super::DataSource::analyze_range_table_func(
307            test_context,
308            &with_clause,
309            &from_clause,
310            range_table_func,
311            &tokens,
312        )?;
313
314        let mut aliases = Vec::new();
315        ds.push_alias_names(&mut aliases);
316        assert_eq!(aliases.len(), 1);
317        assert_eq!(aliases[0].name().as_str(), "func_alias");
318        let DataSource::CallTableFunc(table_func_ds) = ds else {
319            panic!("expected table function datasource");
320        };
321        assert_eq!(table_func_ds.function(), &overload);
322        Ok(())
323    }
324}
325
326impl DataSource {
327    fn analyze_range_func_rows_from<TParseContext>(
328        mut context: TParseContext,
329        with_clause: &WithClause,
330        from_clause: &FromClause,
331        node: crate::syn::RangeFunction,
332        tokens: &IVec<ScanToken>,
333    ) -> Result<(Self, TParseContext), AnalysisError>
334    where
335        TParseContext: ParseContext,
336    {
337        let Some(alias) = node.get_alias().as_inner() else {
338            AnalysisError::raise_unexpected_none("RangeFunction.alias")?
339        };
340        let (alias, new_context) = AliasSpec::analyze_without_relname(context, &alias)?;
341        context = new_context;
342
343        let Some(functions) = node.get_functions().as_inner() else {
344            AnalysisError::raise_unexpected_none("RangeFunction.functions")?
345        };
346        let mut sem_functions = Vec::new();
347        for function in functions {
348            let (fun, new_context) = DataSourceRowsFromFunc::analyze_node(
349                context,
350                with_clause,
351                from_clause,
352                function,
353                tokens,
354            )?;
355            context = new_context;
356            sem_functions.push(fun);
357        }
358        let rf = DataSourceRowFunctions::new(&sem_functions.into(), &alias);
359
360        Ok((Self::RowFunctions(rf), context))
361    }
362}
363
364#[cfg(test)]
365mod test_data_source_analyze_range_func_rows_from {
366    use sql_fun_core::IVec;
367    use testresult::TestResult;
368
369    use crate::{
370        sem::{
371            AliasSpec, DataSource, DataSourceRowFunctions, DataSourceRowsFromFunc, SchemaName,
372            WithClause,
373        },
374        test_helpers::{SynBuilder, TestParseContext, setup_function_overload, test_context},
375    };
376
377    #[rstest::rstest]
378    fn test_analyze_range_func_rows_from(mut test_context: TestParseContext) -> TestResult {
379        test_context.set_get_search_path_result(&vec![SchemaName::from("public")]);
380        let (int4, overload) = setup_function_overload();
381        test_context.setup_type(int4);
382        let func_name = test_context.setup_function("rows_func", &[overload.clone()]);
383
384        let builder = SynBuilder::new();
385        let func_call_node = builder.build_func_call_node(func_name.local_name().as_str());
386        let range_function = builder.build_range_function(func_call_node, "rows_alias", true);
387        let tokens = IVec::default();
388        let with_clause = WithClause::default();
389        let from_clause = super::FromClause::default();
390
391        let (ds, _) = super::DataSource::analyze_range_func_rows_from(
392            test_context,
393            &with_clause,
394            &from_clause,
395            range_function,
396            &tokens,
397        )?;
398
399        let expected_rows_func = DataSourceRowsFromFunc::FuncCall(
400            func_name.clone(),
401            Vec::new(),
402            overload.returning_table(),
403        );
404        let expected_functions: IVec<_> = vec![expected_rows_func].into();
405        let expected_alias = AliasSpec::from_local_name("rows_alias");
406        let expected_row_functions =
407            DataSourceRowFunctions::new(&expected_functions, &expected_alias);
408        let expected = DataSource::RowFunctions(expected_row_functions);
409        assert_eq!(expected, ds);
410        Ok(())
411    }
412}
413
414impl DataSource {
415    fn analyze_range_func<TParseContext>(
416        context: TParseContext,
417        with_clause: &WithClause,
418        from_clause: &FromClause,
419        node: crate::syn::RangeFunction,
420        tokens: &IVec<ScanToken>,
421    ) -> Result<(Self, TParseContext), AnalysisError>
422    where
423        TParseContext: ParseContext,
424    {
425        if node.get_is_rowsfrom() {
426            return Self::analyze_range_func_rows_from(
427                context,
428                with_clause,
429                from_clause,
430                node,
431                tokens,
432            );
433        }
434
435        let Some(functions) = node
436            .get_functions()
437            .map_values(|v| v.as_list().get_items().as_inner())
438        else {
439            AnalysisError::raise_unexpected_none("RangeFunction.functions")?
440        };
441        let functions: Vec<_> = functions
442            .iter()
443            .flatten()
444            .filter_map(crate::syn::Opt::as_inner)
445            .collect();
446        if functions.len() != 1 {
447            AnalysisError::raise_unexpected_input(&format!(
448                "RangeFunction.functions len!=1 : {functions:?}"
449            ))?;
450        }
451        let function = &functions[0];
452
453        let Some(call) = function.as_func_call().as_inner() else {
454            AnalysisError::raise_unexpected_input(&format!(
455                "RangeFunction.functions[0] is not FuncCall : {functions:#?}"
456            ))?
457        };
458
459        Self::call_table_func(
460            context,
461            with_clause,
462            from_clause,
463            call,
464            node.get_alias().as_inner(),
465            tokens,
466        )
467    }
468}
469
470#[cfg(test)]
471mod test_data_source_analyze_range_functions {
472    use sql_fun_core::IVec;
473    use testresult::TestResult;
474
475    use crate::{
476        sem::{DataSource, SchemaName, WithClause},
477        test_helpers::{SynBuilder, TestParseContext, setup_function_overload, test_context},
478    };
479
480    #[rstest::rstest]
481    fn test_analyze_range_func(mut test_context: TestParseContext) -> TestResult {
482        test_context.set_get_search_path_result(&vec![SchemaName::from("public")]);
483        let (int4, overload) = setup_function_overload();
484        test_context.setup_type(int4);
485        test_context.setup_function("set_func", &[overload.clone()]);
486
487        let builder = SynBuilder::new();
488        let func_call_node = builder.build_func_call_node("set_func");
489        let functions_node = builder.build_list_node(vec![func_call_node]);
490        let range_function = builder.build_range_function(functions_node, "set_alias", false);
491        let tokens = IVec::default();
492        let with_clause = WithClause::default();
493        let from_clause = super::FromClause::default();
494
495        let (ds, _) = super::DataSource::analyze_range_func(
496            test_context,
497            &with_clause,
498            &from_clause,
499            range_function,
500            &tokens,
501        )?;
502
503        let mut aliases = Vec::new();
504        ds.push_alias_names(&mut aliases);
505        assert_eq!(aliases.len(), 1);
506        assert_eq!(aliases[0].name().as_str(), "set_alias");
507        let DataSource::CallTableFunc(table_func) = ds else {
508            panic!("expected table function datasource");
509        };
510        assert_eq!(table_func.function(), &overload);
511        Ok(())
512    }
513}
514
515impl DataSource {
516    /// semantic analyze from clause items
517    pub fn analyze_from_clause_node<TParseContext>(
518        context: TParseContext,
519        with_clause: &WithClause,
520        from_clause: &FromClause,
521        node: crate::syn::Node,
522        tokens: &IVec<ScanToken>,
523    ) -> Result<(Self, TParseContext), AnalysisError>
524    where
525        TParseContext: ParseContext,
526    {
527        if let Some(range_var) = node.as_range_var().as_inner() {
528            Ok(Self::analyze_range_var_node(
529                context,
530                with_clause,
531                range_var,
532            )?)
533        } else if let Some(range_subq) = node.as_range_subselect().as_inner() {
534            Ok(Self::analyze_range_subselect(context, range_subq, tokens)?)
535        } else if let Some(join_expr) = node.as_join_expr().as_inner() {
536            Ok(Self::analyze_join_expr(
537                context,
538                with_clause,
539                from_clause,
540                join_expr,
541                tokens,
542            )?)
543        } else if let Some(range_tablef) = node.as_range_table_func().as_inner() {
544            Ok(Self::analyze_range_table_func(
545                context,
546                with_clause,
547                from_clause,
548                range_tablef,
549                tokens,
550            )?)
551        } else if let Some(range_fun) = node.as_range_function().as_inner() {
552            Ok(Self::analyze_range_func(
553                context,
554                with_clause,
555                from_clause,
556                range_fun,
557                tokens,
558            )?)
559        } else if let Some(_range_samp) = node.as_range_table_sample().as_inner() {
560            todo!()
561        } else {
562            AnalysisError::raise_unexpected_none("from_clause inner node")?
563        }
564    }
565}