Skip to main content

linkrs_query/parser/parsing/
dml_parser.rs

1//! Data Modification Statement Parsing Module
2//!
3//! Responsible for parsing statements related to data modification, including INSERT, DELETE, UPDATE, MERGE, etc.
4
5use crate::parser::ast::stmt::*;
6use crate::parser::core::error::ParseError;
7use crate::parser::core::error::ParseErrorKind;
8use crate::parser::core::token::TokenKindExt;
9use crate::parser::parsing::clause_parser::ClauseParser;
10use crate::parser::parsing::expr_parser::parse_expression_with_context;
11use crate::parser::parsing::parse_context::ParseContext;
12use crate::parser::parsing::traversal_parser::TraversalParser;
13use crate::parser::TokenKind;
14use linkrs_core::types::expr::contextual::ContextualExpression;
15use linkrs_core::types::expr::Expression as CoreExpression;
16
17mod copy;
18mod create;
19mod delete;
20mod insert;
21mod merge;
22mod update;
23
24/// Data Modification Parser
25pub struct DmlParser;
26
27impl DmlParser {
28    pub fn new() -> Self {
29        Self
30    }
31
32    /// Extract a literal value from a WHERE clause expression matching `id(<var_name>) == <literal>`.
33    ///
34    /// Returns the extracted value together with the remaining conjunction
35    /// terms (if any), so callers can keep the residual predicate as a
36    /// regular filter instead of silently discarding it.
37    fn extract_id_with_residual(
38        ctx: &mut ParseContext,
39        where_clause: Option<&ContextualExpression>,
40        var_names: &[&str],
41    ) -> (Option<linkrs_core::Value>, Option<ContextualExpression>) {
42        let Some(root) = where_clause.and_then(|w| w.get_expression()) else {
43            return (None, None);
44        };
45        let mut conjuncts = Vec::new();
46        Self::collect_conjuncts(root, &mut conjuncts);
47        let mut extracted = None;
48        let mut rest = Vec::new();
49        for conjunct in conjuncts {
50            if extracted.is_none() {
51                if let Some(value) = Self::match_id_equality(&conjunct, var_names) {
52                    extracted = Some(value);
53                    continue;
54                }
55            }
56            rest.push(conjunct);
57        }
58        (extracted, Self::combine_conjuncts(ctx, rest))
59    }
60
61    /// Split a top-level `AND` chain into its conjuncts.
62    fn collect_conjuncts(expr: CoreExpression, out: &mut Vec<CoreExpression>) {
63        use linkrs_core::types::operators::BinaryOperator;
64        match expr {
65            CoreExpression::Binary { left, op, right } if matches!(op, BinaryOperator::And) => {
66                Self::collect_conjuncts(*left, out);
67                Self::collect_conjuncts(*right, out);
68            }
69            other => out.push(other),
70        }
71    }
72
73    /// Match a single `id(<var>) == <literal>` conjunct.
74    fn match_id_equality(
75        conjunct: &CoreExpression,
76        var_names: &[&str],
77    ) -> Option<linkrs_core::Value> {
78        use linkrs_core::types::operators::BinaryOperator;
79        match conjunct {
80            CoreExpression::Binary { left, op, right } if matches!(op, BinaryOperator::Equal) => {
81                if let CoreExpression::Function { name, args } = left.as_ref() {
82                    if name == "id" && args.len() == 1 {
83                        if let CoreExpression::Variable(v) = args[0].as_expr() {
84                            if var_names.iter().any(|n| **n == *v) {
85                                if let CoreExpression::Literal(value) = right.as_ref() {
86                                    return Some(value.clone());
87                                }
88                            }
89                        }
90                    }
91                }
92                None
93            }
94            _ => None,
95        }
96    }
97
98    /// Rebuild the residual conjunction and register it in the parse context.
99    fn combine_conjuncts(
100        ctx: &mut ParseContext,
101        conjuncts: Vec<CoreExpression>,
102    ) -> Option<ContextualExpression> {
103        use linkrs_core::types::operators::BinaryOperator;
104        let mut iter = conjuncts.into_iter();
105        let mut acc = iter.next()?;
106        for next in iter {
107            acc = CoreExpression::Binary {
108                left: Box::new(acc),
109                op: BinaryOperator::And,
110                right: Box::new(next),
111            };
112        }
113        let meta = linkrs_core::types::expr::ExpressionMeta::with_span(acc, ctx.current_span());
114        let id = ctx.expression_context_clone().register_expression(meta);
115        Some(ContextualExpression::new(
116            id,
117            ctx.expression_context_clone(),
118        ))
119    }
120
121    /// Parse the expression
122    fn parse_expression(
123        &mut self,
124        ctx: &mut ParseContext,
125    ) -> Result<ContextualExpression, ParseError> {
126        parse_expression_with_context(ctx, ctx.expression_context_clone())
127    }
128
129    /// Parse property map: {prop1: value1, prop2: value2}
130    fn parse_property_map(
131        &mut self,
132        ctx: &mut ParseContext,
133    ) -> Result<ContextualExpression, ParseError> {
134        let _start_span = ctx.current_span();
135        let mut properties = Vec::new();
136
137        if !ctx.check_token(TokenKind::RBrace) {
138            loop {
139                let key = ctx.expect_identifier()?;
140                ctx.expect_token(TokenKind::Colon)?;
141                let value = self.parse_expression(ctx)?;
142                let value_expr = value
143                    .expression()
144                    .ok_or_else(|| {
145                        ParseError::new_simple(
146                            "Expression not registered in context".to_string(),
147                            ctx.current_position(),
148                        )
149                    })?
150                    .inner()
151                    .clone();
152                properties.push((key, value_expr));
153
154                if !ctx.match_token(TokenKind::Comma) {
155                    break;
156                }
157            }
158        }
159
160        let expr = CoreExpression::Map(properties);
161        let expr_meta = linkrs_core::types::expr::ExpressionMeta::new(expr);
162        let id = ctx.expression_context().register_expression(expr_meta);
163        Ok(ContextualExpression::new(
164            id,
165            ctx.expression_context_clone(),
166        ))
167    }
168}
169
170impl Default for DmlParser {
171    fn default() -> Self {
172        Self::new()
173    }
174}