Skip to main content

reddb_server/storage/query/parser/
dml.rs

1//! DML SQL Parser: INSERT, UPDATE, DELETE
2
3use super::super::ast::{
4    AskCacheClause, AskQuery, BinOp, DeleteQuery, Expr, FieldRef, Filter, InsertEntityType,
5    InsertQuery, OrderByClause, QueryExpr, ReturningItem, UpdateQuery, UpdateTarget,
6};
7use super::super::lexer::Token;
8use super::error::ParseError;
9use super::Parser;
10use crate::storage::query::sql_lowering::{filter_to_expr, fold_expr_to_value};
11use crate::storage::schema::Value;
12
13/// DoS guard: maximum JSON nesting depth accepted by the parser.
14/// Mirrors typical web-server JSON limits and bails out before stack
15/// usage gets dangerous in downstream traversals.
16pub(crate) const JSON_LITERAL_MAX_DEPTH: u32 = 128;
17
18/// Walk a parsed `JsonValue` tree and bail out if nesting exceeds
19/// `JSON_LITERAL_MAX_DEPTH`. Iterative to avoid the very stack
20/// overflow we're trying to prevent.
21pub(crate) fn json_literal_depth_check(
22    value: &crate::utils::json::JsonValue,
23) -> Result<(), String> {
24    use crate::utils::json::JsonValue;
25    let mut stack: Vec<(&JsonValue, u32)> = vec![(value, 1)];
26    while let Some((node, depth)) = stack.pop() {
27        if depth > JSON_LITERAL_MAX_DEPTH {
28            return Err(format!(
29                "JSON object literal exceeds JSON_LITERAL_MAX_DEPTH ({})",
30                JSON_LITERAL_MAX_DEPTH
31            ));
32        }
33        match node {
34            JsonValue::Object(entries) => {
35                for (_, v) in entries {
36                    stack.push((v, depth + 1));
37                }
38            }
39            JsonValue::Array(items) => {
40                for v in items {
41                    stack.push((v, depth + 1));
42                }
43            }
44            _ => {}
45        }
46    }
47    Ok(())
48}
49
50impl<'a> Parser<'a> {
51    /// Parse: INSERT INTO table [NODE|EDGE|VECTOR|DOCUMENT|KV] (col1, col2) VALUES (val1, val2), (val3, val4) [RETURNING]
52    pub fn parse_insert_query(&mut self) -> Result<QueryExpr, ParseError> {
53        self.expect(Token::Insert)?;
54        self.expect(Token::Into)?;
55        let table = self.expect_ident()?;
56
57        // Check for entity type keyword
58        let entity_type = match self.peek().clone() {
59            Token::Node => {
60                self.advance()?;
61                InsertEntityType::Node
62            }
63            Token::Edge => {
64                self.advance()?;
65                InsertEntityType::Edge
66            }
67            Token::Vector => {
68                self.advance()?;
69                InsertEntityType::Vector
70            }
71            Token::Document => {
72                self.advance()?;
73                InsertEntityType::Document
74            }
75            Token::Kv => {
76                self.advance()?;
77                InsertEntityType::Kv
78            }
79            _ => InsertEntityType::Row,
80        };
81
82        // Parse column list
83        self.expect(Token::LParen)?;
84        let columns = self.parse_ident_list()?;
85        self.expect(Token::RParen)?;
86
87        // Parse VALUES
88        self.expect(Token::Values)?;
89        let mut all_values = Vec::new();
90        let mut all_value_exprs = Vec::new();
91        loop {
92            self.expect(Token::LParen)?;
93            let row_exprs = self.parse_dml_expr_list()?;
94            self.expect(Token::RParen)?;
95            // Tolerate `$N` / `?` placeholders in VALUES rows: fold to
96            // Value::Null and rely on `user_params::bind` to substitute
97            // the caller's values before execution. Issue #355.
98            // Tolerate `$N` / `?` placeholders in VALUES rows: if fold
99            // fails on an expression that contains `Expr::Parameter`,
100            // emit a `Value::Null` placeholder. `user_params::bind`
101            // substitutes the caller-supplied value before execution.
102            // Issue #355.
103            let row_values = row_exprs
104                .iter()
105                .map(|expr| match fold_expr_to_value(expr.clone()) {
106                    Ok(value) => Ok(value),
107                    Err(msg) => {
108                        if crate::storage::query::user_params::expr_contains_parameter(&expr) {
109                            Ok(Value::Null)
110                        } else {
111                            Err(msg)
112                        }
113                    }
114                })
115                .collect::<Result<Vec<_>, _>>()
116                .map_err(|msg| ParseError::new(msg, self.position()))?;
117            all_value_exprs.push(row_exprs);
118            all_values.push(row_values);
119            if !self.consume(&Token::Comma)? {
120                break;
121            }
122        }
123
124        // Parse optional WITH clauses
125        let (ttl_ms, expires_at_ms, with_metadata, auto_embed) = self.parse_with_clauses()?;
126
127        let returning = self.parse_returning_clause()?;
128
129        let suppress_events = if self.consume_ident_ci("SUPPRESS")? {
130            self.expect_ident_ci("EVENTS")?;
131            true
132        } else {
133            false
134        };
135
136        Ok(QueryExpr::Insert(InsertQuery {
137            table,
138            entity_type,
139            columns,
140            value_exprs: all_value_exprs,
141            values: all_values,
142            returning,
143            ttl_ms,
144            expires_at_ms,
145            with_metadata,
146            auto_embed,
147            suppress_events,
148        }))
149    }
150
151    /// Parse TTL duration value using the same logic as CREATE TABLE ... WITH TTL.
152    fn parse_ttl_duration(&mut self) -> Result<u64, ParseError> {
153        // Reuse the DDL TTL parser: expects a number followed by optional unit
154        let ttl_value = self.parse_float()?;
155        let ttl_unit = match self.peek() {
156            Token::Ident(unit) => {
157                let unit = unit.clone();
158                self.advance()?;
159                unit
160            }
161            _ => "s".to_string(),
162        };
163
164        let multiplier_ms = match ttl_unit.to_ascii_lowercase().as_str() {
165            "ms" | "msec" | "millisecond" | "milliseconds" => 1.0,
166            "s" | "sec" | "secs" | "second" | "seconds" => 1_000.0,
167            "m" | "min" | "mins" | "minute" | "minutes" => 60_000.0,
168            "h" | "hr" | "hrs" | "hour" | "hours" => 3_600_000.0,
169            "d" | "day" | "days" => 86_400_000.0,
170            other => {
171                return Err(ParseError::new(
172                    // F-05: render `other` via `{:?}` so caller-controlled
173                    // bytes (CR / LF / NUL / quotes) are escaped before
174                    // landing in the JSON/audit/log/gRPC error sinks.
175                    format!(
176                        "unsupported TTL unit {other:?}; supported units: ms, s, m, h, d (e.g. `WITH TTL 30 m`)"
177                    ),
178                    self.position(),
179                ));
180            }
181        };
182
183        Ok((ttl_value * multiplier_ms) as u64)
184    }
185
186    /// Parse WITH clauses: WITH TTL | EXPIRES AT | METADATA | AUTO EMBED
187    /// Returns (ttl_ms, expires_at_ms, metadata, auto_embed)
188    pub fn parse_with_clauses(
189        &mut self,
190    ) -> Result<
191        (
192            Option<u64>,
193            Option<u64>,
194            Vec<(String, Value)>,
195            Option<crate::storage::query::ast::AutoEmbedConfig>,
196        ),
197        ParseError,
198    > {
199        let mut ttl_ms = None;
200        let mut expires_at_ms = None;
201        let mut with_metadata = Vec::new();
202        let mut auto_embed = None;
203
204        while self.consume(&Token::With)? {
205            if self.consume_ident_ci("TTL")? {
206                ttl_ms = Some(self.parse_ttl_duration()?);
207            } else if self.consume_ident_ci("EXPIRES")? {
208                self.expect_ident_ci("AT")?;
209                let ts = self.parse_expires_at_value()?;
210                expires_at_ms = Some(ts);
211            } else if self.consume(&Token::Metadata)? || self.consume_ident_ci("METADATA")? {
212                with_metadata = self.parse_with_metadata_pairs()?;
213            } else if self.consume_ident_ci("AUTO")? {
214                // WITH AUTO EMBED (field1, field2) [USING provider] [MODEL 'model']
215                self.consume_ident_ci("EMBED")?;
216                self.expect(Token::LParen)?;
217                let mut fields = Vec::new();
218                loop {
219                    fields.push(self.expect_ident()?);
220                    if !self.consume(&Token::Comma)? {
221                        break;
222                    }
223                }
224                self.expect(Token::RParen)?;
225                // `USING` is a reserved keyword (`Token::Using`), so
226                // `consume_ident_ci` would never match. Use the typed
227                // consumer instead. See bug #108 (mirrors the #92 fix
228                // for migration `DEPENDS ON`).
229                let provider = if self.consume(&Token::Using)? {
230                    self.expect_ident()?
231                } else {
232                    "openai".to_string()
233                };
234                let model = if self.consume_ident_ci("MODEL")? {
235                    Some(self.parse_string()?)
236                } else {
237                    None
238                };
239                auto_embed = Some(crate::storage::query::ast::AutoEmbedConfig {
240                    fields,
241                    provider,
242                    model,
243                });
244            } else {
245                return Err(ParseError::expected(
246                    vec!["TTL", "EXPIRES AT", "METADATA", "AUTO EMBED"],
247                    self.peek(),
248                    self.position(),
249                ));
250            }
251        }
252
253        Ok((ttl_ms, expires_at_ms, with_metadata, auto_embed))
254    }
255
256    /// Expect a case-insensitive identifier (error if not found)
257    fn expect_ident_ci(&mut self, expected: &str) -> Result<(), ParseError> {
258        if self.consume_ident_ci(expected)? {
259            Ok(())
260        } else {
261            Err(ParseError::expected(
262                vec![expected],
263                self.peek(),
264                self.position(),
265            ))
266        }
267    }
268
269    /// Parse an absolute expiration timestamp (unix ms or string date)
270    fn parse_expires_at_value(&mut self) -> Result<u64, ParseError> {
271        // Try integer (unix timestamp in ms)
272        if let Ok(value) = self.parse_integer() {
273            return Ok(value as u64);
274        }
275        // Try string like '2026-12-31' — convert to unix ms
276        if let Ok(text) = self.parse_string() {
277            // Simple ISO date parsing: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS
278            let trimmed = text.trim();
279            if let Ok(ts) = trimmed.parse::<u64>() {
280                return Ok(ts);
281            }
282            // Basic date parsing — delegate to chrono if available, or simple heuristic
283            return Err(ParseError::new(
284                // F-05: `trimmed` is caller-controlled string-literal bytes.
285                // Render via `{:?}` so CR/LF/NUL/quotes are escaped before
286                // the message reaches the JSON / audit / log / gRPC sinks.
287                format!("EXPIRES AT requires a unix timestamp in milliseconds, got {trimmed:?}"),
288                self.position(),
289            ));
290        }
291        Err(ParseError::expected(
292            vec!["timestamp (unix ms) or 'YYYY-MM-DD'"],
293            self.peek(),
294            self.position(),
295        ))
296    }
297
298    /// Parse WITH METADATA (key1 = 'value1', key2 = 42)
299    fn parse_with_metadata_pairs(&mut self) -> Result<Vec<(String, Value)>, ParseError> {
300        self.expect(Token::LParen)?;
301        let mut pairs = Vec::new();
302        if !self.check(&Token::RParen) {
303            loop {
304                let key = self.expect_ident_or_keyword()?.to_ascii_lowercase();
305                self.expect(Token::Eq)?;
306                let value = self.parse_literal_value()?;
307                pairs.push((key, value));
308                if !self.consume(&Token::Comma)? {
309                    break;
310                }
311            }
312        }
313        self.expect(Token::RParen)?;
314        Ok(pairs)
315    }
316
317    /// Parse: UPDATE table SET col1=val1, col2=val2 [WHERE filter] [WITH TTL|EXPIRES AT|METADATA]
318    pub fn parse_update_query(&mut self) -> Result<QueryExpr, ParseError> {
319        self.expect(Token::Update)?;
320        let table = self.expect_ident()?;
321        let target = self.parse_update_target()?;
322        self.expect(Token::Set)?;
323
324        let mut assignments = Vec::new();
325        let mut assignment_exprs = Vec::new();
326        let mut compound_assignment_ops = Vec::new();
327        loop {
328            let col = self.expect_column_ident()?;
329            let compound_op = if self.consume(&Token::Eq)? {
330                None
331            } else {
332                let op = match self.peek() {
333                    Token::Plus => BinOp::Add,
334                    Token::Dash | Token::Minus => BinOp::Sub,
335                    Token::Star => BinOp::Mul,
336                    Token::Slash => BinOp::Div,
337                    Token::Percent => BinOp::Mod,
338                    _ => {
339                        return Err(ParseError::expected(
340                            vec!["=", "+=", "-=", "*=", "/=", "%="],
341                            self.peek(),
342                            self.position(),
343                        ));
344                    }
345                };
346                self.advance()?;
347                self.expect(Token::Eq)?;
348                Some(op)
349            };
350            let expr = self.parse_expr()?;
351            let folded = fold_expr_to_value(expr.clone()).ok();
352            assignment_exprs.push((col.clone(), expr));
353            compound_assignment_ops.push(compound_op);
354            if compound_op.is_none() {
355                if let Some(val) = folded {
356                    assignments.push((col.clone(), val));
357                }
358            }
359            if !self.consume(&Token::Comma)? {
360                break;
361            }
362        }
363
364        let filter = if self.consume(&Token::Where)? {
365            Some(self.parse_filter()?)
366        } else {
367            None
368        };
369        let where_expr = filter.as_ref().map(filter_to_expr);
370
371        let (ttl_ms, expires_at_ms, with_metadata, _auto_embed) = self.parse_with_clauses()?;
372
373        let mut order_by = if self.consume(&Token::Order)? {
374            self.expect(Token::By)?;
375            let clauses = self.parse_order_by_list()?;
376            validate_update_order_by(&clauses, self.position())?;
377            clauses
378        } else {
379            Vec::new()
380        };
381
382        // Optional `LIMIT N` — used by `BATCH N ROWS` data migrations
383        // to cap a single batch. Must come after WHERE / WITH because
384        // those have their own keyword tokens that the LIMIT branch
385        // would otherwise mis-consume.
386        let limit = if self.consume(&Token::Limit)? {
387            Some(self.parse_integer()? as u64)
388        } else {
389            None
390        };
391        if !order_by.is_empty() && limit.is_none() {
392            return Err(ParseError::new(
393                "UPDATE ORDER BY requires LIMIT",
394                self.position(),
395            ));
396        }
397        if !order_by.is_empty() && !update_order_by_mentions_rid(&order_by) {
398            order_by.push(OrderByClause {
399                field: FieldRef::TableColumn {
400                    table: String::new(),
401                    column: "rid".to_string(),
402                },
403                expr: None,
404                ascending: true,
405                nulls_first: false,
406            });
407        }
408
409        let returning = self.parse_returning_clause()?;
410
411        let suppress_events = if self.consume_ident_ci("SUPPRESS")? {
412            self.expect_ident_ci("EVENTS")?;
413            true
414        } else {
415            false
416        };
417
418        Ok(QueryExpr::Update(UpdateQuery {
419            table,
420            target,
421            assignment_exprs,
422            compound_assignment_ops,
423            assignments,
424            where_expr,
425            filter,
426            ttl_ms,
427            expires_at_ms,
428            with_metadata,
429            returning,
430            order_by,
431            limit,
432            suppress_events,
433        }))
434    }
435
436    fn parse_update_target(&mut self) -> Result<UpdateTarget, ParseError> {
437        if self.consume(&Token::Kv)? {
438            return Ok(UpdateTarget::Kv);
439        }
440        if self.consume_ident_ci("ROWS")? {
441            return Ok(UpdateTarget::Rows);
442        }
443        if self.consume_ident_ci("DOCUMENTS")? {
444            return Ok(UpdateTarget::Documents);
445        }
446        if self.consume_ident_ci("NODES")? {
447            return Ok(UpdateTarget::Nodes);
448        }
449        if self.consume_ident_ci("EDGES")? {
450            return Ok(UpdateTarget::Edges);
451        }
452        Ok(UpdateTarget::Rows)
453    }
454
455    /// Parse: DELETE FROM table [WHERE filter]
456    pub fn parse_delete_query(&mut self) -> Result<QueryExpr, ParseError> {
457        self.expect(Token::Delete)?;
458        self.expect(Token::From)?;
459        let table = self.expect_ident()?;
460
461        let filter = if self.consume(&Token::Where)? {
462            Some(self.parse_filter()?)
463        } else {
464            None
465        };
466
467        let where_expr = filter.as_ref().map(filter_to_expr);
468
469        let returning = self.parse_returning_clause()?;
470
471        let suppress_events = if self.consume_ident_ci("SUPPRESS")? {
472            self.expect_ident_ci("EVENTS")?;
473            true
474        } else {
475            false
476        };
477
478        Ok(QueryExpr::Delete(DeleteQuery {
479            table,
480            where_expr,
481            filter,
482            returning,
483            suppress_events,
484        }))
485    }
486
487    /// Parse optional `RETURNING (* | col [, col ...])` clause.
488    /// Returns `None` if no RETURNING token, errors if RETURNING is present
489    /// but not followed by `*` or a non-empty column list.
490    fn parse_returning_clause(&mut self) -> Result<Option<Vec<ReturningItem>>, ParseError> {
491        if !self.consume(&Token::Returning)? {
492            return Ok(None);
493        }
494        if self.consume(&Token::Star)? {
495            return Ok(Some(vec![ReturningItem::All]));
496        }
497        let mut items = Vec::new();
498        loop {
499            if returning_expr_start(self.peek()) {
500                return Err(returning_expr_not_supported(self.position()));
501            }
502            let col = self.expect_update_returning_column()?;
503            items.push(ReturningItem::Column(col));
504            if returning_expr_tail(self.peek()) {
505                return Err(returning_expr_not_supported(self.position()));
506            }
507            if !self.consume(&Token::Comma)? {
508                break;
509            }
510        }
511        if items.is_empty() {
512            return Err(ParseError::expected(
513                vec!["*", "column name"],
514                self.peek(),
515                self.position(),
516            ));
517        }
518        Ok(Some(items))
519    }
520
521    fn expect_update_returning_column(&mut self) -> Result<String, ParseError> {
522        if self.consume(&Token::Weight)? {
523            return Ok("weight".to_string());
524        }
525        self.expect_ident_or_keyword()
526    }
527
528    /// Parse: ASK 'question' [USING provider] [MODEL 'model'] [DEPTH n]
529    /// [LIMIT n] [MIN_SCORE x] [COLLECTION col]
530    pub fn parse_ask_query(&mut self) -> Result<QueryExpr, ParseError> {
531        self.parse_ask_query_with_explain(false)
532    }
533
534    /// Parse: EXPLAIN ASK 'question' ...
535    pub fn parse_explain_ask_query(&mut self) -> Result<QueryExpr, ParseError> {
536        self.advance()?; // consume EXPLAIN
537        if !matches!(self.peek(), Token::Ident(name) if name.eq_ignore_ascii_case("ASK")) {
538            return Err(ParseError::expected(
539                vec!["ASK"],
540                self.peek(),
541                self.position(),
542            ));
543        }
544        self.parse_ask_query_with_explain(true)
545    }
546
547    fn parse_ask_query_with_explain(&mut self, explain: bool) -> Result<QueryExpr, ParseError> {
548        self.advance()?; // consume ASK
549
550        let (question, question_param) = match self.peek() {
551            Token::String(_) => (self.parse_string()?, None),
552            Token::Dollar | Token::Question => {
553                let index = self.parse_param_slot("ASK question")?;
554                (String::new(), Some(index))
555            }
556            other => {
557                return Err(ParseError::expected(
558                    vec!["string", "$N", "?"],
559                    other,
560                    self.position(),
561                ));
562            }
563        };
564
565        let mut provider = None;
566        let mut model = None;
567        let mut depth = None;
568        let mut limit = None;
569        let mut min_score = None;
570        let mut collection = None;
571        let mut temperature = None;
572        let mut seed = None;
573        let mut strict = true;
574        let mut stream = false;
575        let mut cache = AskCacheClause::Default;
576
577        // Parse optional clauses in any order. Loop bound = number of
578        // clause kinds, so each can appear at most once.
579        for _ in 0..12 {
580            if self.consume(&Token::Using)? {
581                provider = Some(match &self.current.token {
582                    Token::String(_) => self.parse_string()?,
583                    _ => self.expect_ident()?,
584                });
585            } else if self.consume_ident_ci("MODEL")? {
586                model = Some(self.parse_string()?);
587            } else if self.consume(&Token::Depth)? {
588                depth = Some(self.parse_integer()? as usize);
589            } else if self.consume(&Token::Limit)? {
590                limit = Some(self.parse_integer()? as usize);
591            } else if self.consume(&Token::MinScore)? {
592                min_score = Some(self.parse_float()? as f32);
593            } else if self.consume(&Token::Collection)? {
594                collection = Some(self.expect_ident()?);
595            } else if self.consume_ident_ci("TEMPERATURE")? {
596                temperature = Some(self.parse_float()? as f32);
597            } else if self.consume_ident_ci("SEED")? {
598                seed = Some(self.parse_integer()? as u64);
599            } else if self.consume_ident_ci("STRICT")? {
600                let value = self.expect_ident_or_keyword()?;
601                if value.eq_ignore_ascii_case("ON") {
602                    strict = true;
603                } else if value.eq_ignore_ascii_case("OFF") {
604                    strict = false;
605                } else {
606                    return Err(ParseError::new(
607                        "Expected ON or OFF after STRICT",
608                        self.position(),
609                    ));
610                }
611            } else if self.consume_ident_ci("STREAM")? {
612                stream = true;
613            } else if self.consume_ident_ci("CACHE")? {
614                if !matches!(cache, AskCacheClause::Default) {
615                    return Err(ParseError::new(
616                        "ASK cache clause specified more than once",
617                        self.position(),
618                    ));
619                }
620                let ttl = self.expect_ident_or_keyword()?;
621                if !ttl.eq_ignore_ascii_case("TTL") {
622                    return Err(ParseError::new("Expected TTL after CACHE", self.position()));
623                }
624                cache = AskCacheClause::CacheTtl(self.parse_string()?);
625            } else if self.consume_ident_ci("NOCACHE")? {
626                if !matches!(cache, AskCacheClause::Default) {
627                    return Err(ParseError::new(
628                        "ASK cache clause specified more than once",
629                        self.position(),
630                    ));
631                }
632                cache = AskCacheClause::NoCache;
633            } else {
634                break;
635            }
636        }
637
638        Ok(QueryExpr::Ask(AskQuery {
639            explain,
640            question,
641            question_param,
642            provider,
643            model,
644            depth,
645            limit,
646            min_score,
647            collection,
648            temperature,
649            seed,
650            strict,
651            stream,
652            cache,
653        }))
654    }
655
656    /// Parse comma-separated identifiers (accepts keywords as column names in DML context)
657    fn parse_ident_list(&mut self) -> Result<Vec<String>, ParseError> {
658        let mut idents = Vec::new();
659        loop {
660            idents.push(self.expect_ident_or_keyword()?);
661            if !self.consume(&Token::Comma)? {
662                break;
663            }
664        }
665        Ok(idents)
666    }
667
668    /// Parse comma-separated literal values for DML statements
669    fn parse_dml_value_list(&mut self) -> Result<Vec<Value>, ParseError> {
670        self.parse_dml_expr_list()?
671            .into_iter()
672            .map(fold_expr_to_value)
673            .collect::<Result<Vec<_>, _>>()
674            .map_err(|msg| ParseError::new(msg, self.position()))
675    }
676
677    fn parse_dml_expr_list(&mut self) -> Result<Vec<Expr>, ParseError> {
678        let mut values = Vec::new();
679        loop {
680            values.push(self.parse_expr()?);
681            if !self.consume(&Token::Comma)? {
682                break;
683            }
684        }
685        Ok(values)
686    }
687
688    /// Parse a single literal value (string, number, true, false, null, array)
689    pub(crate) fn parse_literal_value(&mut self) -> Result<Value, ParseError> {
690        // Depth guard: this function recurses for nested array `[…]`
691        // and object `{…}` literals (see the LBracket / LBrace arms
692        // below). Without entering the depth counter, an adversarial
693        // payload like `[[[[…(10k×)…]]]]` would overflow the Rust
694        // stack BEFORE `ParserLimits::max_depth` fires. The
695        // `JsonLiteral` token path uses `json_literal_depth_check`
696        // (iterative) — the bare `[`/`{` path needs the recursion
697        // counter explicitly.
698        self.enter_depth()?;
699        let result = self.parse_literal_value_inner();
700        self.exit_depth();
701        result
702    }
703
704    fn parse_literal_value_inner(&mut self) -> Result<Value, ParseError> {
705        // Recognize PASSWORD('plaintext') and SECRET('plaintext') as
706        // typed literal constructors. The parser stores them as
707        // sentinel-prefixed values so that the INSERT executor can
708        // apply the crypto transform (argon2id hash / AES-256-GCM
709        // encrypt) without the parser depending on auth or crypto
710        // subsystems.
711        if let Token::Ident(name) = self.peek().clone() {
712            let upper = name.to_uppercase();
713            if upper == "PASSWORD" || upper == "SECRET" {
714                self.advance()?; // consume ident
715                self.expect(Token::LParen)?;
716                let plaintext = self.parse_string()?;
717                self.expect(Token::RParen)?;
718                return Ok(match upper.as_str() {
719                    "PASSWORD" => Value::Password(format!("@@plain@@{plaintext}")),
720                    "SECRET" => Value::Secret(format!("@@plain@@{plaintext}").into_bytes()),
721                    _ => unreachable!(),
722                });
723            }
724            if upper == "SECRET_REF" {
725                self.advance()?; // consume ident
726                self.expect(Token::LParen)?;
727                let store = self.expect_ident_or_keyword()?.to_ascii_lowercase();
728                if store != "vault" {
729                    return Err(ParseError::expected(
730                        vec!["vault"],
731                        self.peek(),
732                        self.position(),
733                    ));
734                }
735                self.expect(Token::Comma)?;
736                let (collection, key) =
737                    self.parse_kv_key(crate::catalog::CollectionModel::Vault)?;
738                self.expect(Token::RParen)?;
739                return Ok(secret_ref_value(&store, &collection, &key));
740            }
741        }
742
743        match self.peek().clone() {
744            Token::String(s) => {
745                let s = s.clone();
746                self.advance()?;
747                Ok(Value::text(s))
748            }
749            Token::JsonLiteral(raw) => {
750                // The lexer already validated brace balance and the
751                // 16 MiB payload ceiling. Parse the raw text into a
752                // canonical JsonValue then re-encode via `to_vec` so
753                // the on-disk bytes match the quoted form.
754                self.advance()?;
755                let json_value = crate::utils::json::parse_json(&raw).map_err(|err| {
756                    ParseError::new(
757                        // F-05: render the underlying parse-error string
758                        // via `{:?}` so any user fragment serde echoed
759                        // back (unexpected character, key text, …) is
760                        // Debug-escaped before reaching the downstream
761                        // JSON / audit / log / gRPC sinks.
762                        format!("invalid JSON object literal: {:?}", err.to_string()),
763                        self.position(),
764                    )
765                })?;
766                json_literal_depth_check(&json_value)
767                    .map_err(|err| ParseError::new(err, self.position()))?;
768                let canonical = crate::serde_json::Value::from(json_value);
769                let bytes = crate::json::to_vec(&canonical).map_err(|err| {
770                    ParseError::new(
771                        // F-05: escape the encoder error via `{:?}` so any
772                        // user fragment it carries cannot smuggle control
773                        // bytes through downstream serialization sinks.
774                        format!("failed to encode JSON literal: {:?}", err.to_string()),
775                        self.position(),
776                    )
777                })?;
778                Ok(Value::Json(bytes))
779            }
780            Token::Integer(n) => {
781                self.advance()?;
782                Ok(Value::Integer(n))
783            }
784            Token::Float(n) => {
785                self.advance()?;
786                Ok(Value::Float(n))
787            }
788            Token::True => {
789                self.advance()?;
790                Ok(Value::Boolean(true))
791            }
792            Token::False => {
793                self.advance()?;
794                Ok(Value::Boolean(false))
795            }
796            Token::Null => {
797                self.advance()?;
798                Ok(Value::Null)
799            }
800            Token::LBracket => {
801                // Parse array literal [val1, val2, ...]
802                // For numeric arrays, produce Value::Vector; for others, produce Value::Json
803                self.advance()?; // consume '['
804                let mut items = Vec::new();
805                if !self.check(&Token::RBracket) {
806                    loop {
807                        items.push(self.parse_literal_value()?);
808                        if !self.consume(&Token::Comma)? {
809                            break;
810                        }
811                    }
812                }
813                self.expect(Token::RBracket)?;
814
815                // Check if all items are numeric (Integer or Float) -> Value::Vector
816                let all_numeric = items
817                    .iter()
818                    .all(|v| matches!(v, Value::Integer(_) | Value::Float(_)));
819                if all_numeric && !items.is_empty() {
820                    let floats: Vec<f32> = items
821                        .iter()
822                        .map(|v| match v {
823                            Value::Float(f) => *f as f32,
824                            Value::Integer(i) => *i as f32,
825                            _ => 0.0,
826                        })
827                        .collect();
828                    Ok(Value::Vector(floats))
829                } else {
830                    // Encode as JSON bytes
831                    let json_arr: Vec<crate::json::Value> = items
832                        .iter()
833                        .map(|v| match v {
834                            Value::Null => crate::json::Value::Null,
835                            Value::Boolean(b) => crate::json::Value::Bool(*b),
836                            Value::Integer(i) => crate::json::Value::Number(*i as f64),
837                            Value::Float(f) => crate::json::Value::Number(*f),
838                            Value::Text(s) => crate::json::Value::String(s.to_string()),
839                            _ => crate::json::Value::Null,
840                        })
841                        .collect();
842                    let json_val = crate::json::Value::Array(json_arr);
843                    let bytes = crate::json::to_vec(&json_val).unwrap_or_default();
844                    Ok(Value::Json(bytes))
845                }
846            }
847            Token::LBrace => {
848                // Parse JSON object literal {key: value, ...}
849                self.advance()?; // consume '{'
850                let mut map = crate::json::Map::new();
851                if !self.check(&Token::RBrace) {
852                    loop {
853                        // Key: string or identifier. Reserved-word
854                        // keys (`level`, `msg`, `type`, …) fall through
855                        // to `expect_ident_or_keyword`, which returns
856                        // the canonical UPPERCASE spelling; lowercase
857                        // that path so the JSON object preserves the
858                        // source casing.
859                        let key = match self.peek().clone() {
860                            Token::String(s) => {
861                                self.advance()?;
862                                s
863                            }
864                            Token::Ident(s) => {
865                                self.advance()?;
866                                s
867                            }
868                            _ => self.expect_ident_or_keyword()?.to_ascii_lowercase(),
869                        };
870                        // Separator: ':' or '='
871                        if !self.consume(&Token::Colon)? {
872                            self.expect(Token::Eq)?;
873                        }
874                        // Value: recursive
875                        let val = self.parse_literal_value()?;
876                        let json_val = match val {
877                            Value::Null => crate::json::Value::Null,
878                            Value::Boolean(b) => crate::json::Value::Bool(b),
879                            Value::Integer(i) => crate::json::Value::Number(i as f64),
880                            Value::Float(f) => crate::json::Value::Number(f),
881                            Value::Text(s) => crate::json::Value::String(s.to_string()),
882                            Value::Json(ref bytes) => {
883                                crate::json::from_slice(bytes).unwrap_or(crate::json::Value::Null)
884                            }
885                            _ => crate::json::Value::Null,
886                        };
887                        map.insert(key, json_val);
888                        if !self.consume(&Token::Comma)? {
889                            break;
890                        }
891                    }
892                }
893                self.expect(Token::RBrace)?;
894                let json_val = crate::json::Value::Object(map);
895                let bytes = crate::json::to_vec(&json_val).unwrap_or_default();
896                Ok(Value::Json(bytes))
897            }
898            ref other => Err(ParseError::expected(
899                vec!["string", "number", "true", "false", "null", "[", "{"],
900                other,
901                self.position(),
902            )),
903        }
904    }
905}
906
907fn returning_expr_start(token: &Token) -> bool {
908    matches!(
909        token,
910        Token::Integer(_)
911            | Token::Float(_)
912            | Token::String(_)
913            | Token::JsonLiteral(_)
914            | Token::Null
915            | Token::True
916            | Token::False
917            | Token::LParen
918            | Token::Minus
919            | Token::Question
920            | Token::Dollar
921    )
922}
923
924fn returning_expr_tail(token: &Token) -> bool {
925    matches!(
926        token,
927        Token::LParen
928            | Token::Plus
929            | Token::Minus
930            | Token::Star
931            | Token::Slash
932            | Token::Percent
933            | Token::DoublePipe
934            | Token::Pipe
935            | Token::Eq
936            | Token::Ne
937            | Token::Lt
938            | Token::Le
939            | Token::Gt
940            | Token::Ge
941            | Token::Dot
942            | Token::Colon
943    )
944}
945
946fn validate_update_order_by(
947    clauses: &[OrderByClause],
948    position: super::super::lexer::Position,
949) -> Result<(), ParseError> {
950    for clause in clauses {
951        if clause.expr.is_some() {
952            return Err(ParseError::new(
953                "UPDATE ORDER BY only supports top-level fields",
954                position,
955            ));
956        }
957        match &clause.field {
958            FieldRef::TableColumn { table, column }
959                if table.is_empty() && !column.contains('.') => {}
960            _ => {
961                return Err(ParseError::new(
962                    "UPDATE ORDER BY only supports top-level fields",
963                    position,
964                ));
965            }
966        }
967    }
968    Ok(())
969}
970
971fn update_order_by_mentions_rid(clauses: &[OrderByClause]) -> bool {
972    clauses.iter().any(|clause| {
973        matches!(
974            &clause.field,
975            FieldRef::TableColumn { table, column }
976                if table.is_empty() && column.eq_ignore_ascii_case("rid")
977        )
978    })
979}
980
981fn returning_expr_not_supported(position: super::super::lexer::Position) -> ParseError {
982    ParseError::new(
983        "NOT_YET_SUPPORTED: RETURNING expressions are not supported yet; use RETURNING * or named columns. Track a follow-up issue for RETURNING <expr>.",
984        position,
985    )
986}
987
988fn secret_ref_value(store: &str, collection: &str, key: &str) -> Value {
989    let mut map = crate::json::Map::new();
990    map.insert(
991        "type".to_string(),
992        crate::json::Value::String("secret_ref".to_string()),
993    );
994    map.insert(
995        "store".to_string(),
996        crate::json::Value::String(store.to_string()),
997    );
998    map.insert(
999        "collection".to_string(),
1000        crate::json::Value::String(collection.to_string()),
1001    );
1002    map.insert(
1003        "key".to_string(),
1004        crate::json::Value::String(key.to_string()),
1005    );
1006    Value::Json(crate::json::to_vec(&crate::json::Value::Object(map)).unwrap_or_default())
1007}