1use super::error::ParseError;
4use super::Parser;
5use crate::ast::{
6 AskCacheClause, AskQuery, BinOp, DeleteQuery, Expr, FieldRef, Filter, InsertEntityType,
7 InsertQuery, OrderByClause, QueryExpr, ReturningItem, UpdateQuery, UpdateTarget,
8};
9use crate::lexer::Token;
10use crate::sql_lowering::{filter_to_expr, fold_expr_to_value};
11use reddb_types::types::Value;
12
13pub(crate) use crate::limits::JSON_LITERAL_MAX_DEPTH;
17
18pub(crate) fn json_literal_depth_check(
22 value: &reddb_types::utils::json::JsonValue,
23) -> Result<(), String> {
24 use reddb_types::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 pub fn parse_insert_query(&mut self) -> Result<QueryExpr, ParseError> {
53 self.expect(Token::Insert)?;
54 self.expect(Token::Into)?;
55 if matches!(self.peek(), Token::Metric) {
63 return Err(ParseError::new(
64 "INSERT INTO METRIC is not supported in Analytics v0 — \
65 write raw samples into an ordinary TABLE/DOCUMENT \
66 collection; the metric descriptor catalog is reached \
67 via CREATE METRIC and red.analytics.metrics \
68 (PRD #782 non-goal)",
69 self.position(),
70 ));
71 }
72 let table = self.expect_ident()?;
73
74 let entity_type = match self.peek().clone() {
76 Token::Node => {
77 self.advance()?;
78 InsertEntityType::Node
79 }
80 Token::Edge => {
81 self.advance()?;
82 InsertEntityType::Edge
83 }
84 Token::Vector => {
85 self.advance()?;
86 InsertEntityType::Vector
87 }
88 Token::Document => {
89 self.advance()?;
90 InsertEntityType::Document
91 }
92 Token::Kv => {
93 self.advance()?;
94 InsertEntityType::Kv
95 }
96 _ => InsertEntityType::Row,
97 };
98
99 self.expect(Token::LParen)?;
101 let columns = self.parse_ident_list()?;
102 self.expect(Token::RParen)?;
103
104 self.expect(Token::Values)?;
106 let mut all_values = Vec::new();
107 let mut all_value_exprs = Vec::new();
108 loop {
109 self.expect(Token::LParen)?;
110 let row_exprs = self.parse_dml_expr_list()?;
111 self.expect(Token::RParen)?;
112 let row_values = row_exprs
121 .iter()
122 .map(|expr| match fold_expr_to_value(expr.clone()) {
123 Ok(value) => Ok(value),
124 Err(msg) => {
125 if crate::sql_lowering::expr_contains_parameter(expr) {
126 Ok(Value::Null)
127 } else {
128 Err(msg)
129 }
130 }
131 })
132 .collect::<Result<Vec<_>, _>>()
133 .map_err(|msg| ParseError::new(msg, self.position()))?;
134 all_value_exprs.push(row_exprs);
135 all_values.push(row_values);
136 if !self.consume(&Token::Comma)? {
137 break;
138 }
139 }
140
141 let (ttl_ms, expires_at_ms, with_metadata, auto_embed) = self.parse_with_clauses()?;
143
144 let returning = self.parse_returning_clause()?;
145
146 let suppress_events = if self.consume_ident_ci("SUPPRESS")? {
147 self.expect_ident_ci("EVENTS")?;
148 true
149 } else {
150 false
151 };
152
153 Ok(QueryExpr::Insert(InsertQuery {
154 table,
155 entity_type,
156 columns,
157 value_exprs: all_value_exprs,
158 values: all_values,
159 returning,
160 ttl_ms,
161 expires_at_ms,
162 with_metadata,
163 auto_embed,
164 suppress_events,
165 }))
166 }
167
168 fn parse_ttl_duration(&mut self) -> Result<u64, ParseError> {
170 let ttl_value = self.parse_float()?;
172 let ttl_unit = match self.peek() {
173 Token::Ident(unit) => {
174 let unit = unit.clone();
175 self.advance()?;
176 unit
177 }
178 _ => "s".to_string(),
179 };
180
181 let multiplier_ms = match ttl_unit.to_ascii_lowercase().as_str() {
182 "ms" | "msec" | "millisecond" | "milliseconds" => 1.0,
183 "s" | "sec" | "secs" | "second" | "seconds" => 1_000.0,
184 "m" | "min" | "mins" | "minute" | "minutes" => 60_000.0,
185 "h" | "hr" | "hrs" | "hour" | "hours" => 3_600_000.0,
186 "d" | "day" | "days" => 86_400_000.0,
187 other => {
188 return Err(ParseError::new(
189 format!(
193 "unsupported TTL unit {other:?}; supported units: ms, s, m, h, d (e.g. `WITH TTL 30 m`)"
194 ),
195 self.position(),
196 ));
197 }
198 };
199
200 Ok((ttl_value * multiplier_ms) as u64)
201 }
202
203 pub fn parse_with_clauses(
206 &mut self,
207 ) -> Result<
208 (
209 Option<u64>,
210 Option<u64>,
211 Vec<(String, Value)>,
212 Option<crate::ast::AutoEmbedConfig>,
213 ),
214 ParseError,
215 > {
216 let mut ttl_ms = None;
217 let mut expires_at_ms = None;
218 let mut with_metadata = Vec::new();
219 let mut auto_embed = None;
220
221 while self.consume(&Token::With)? {
222 if self.consume_ident_ci("TTL")? {
223 ttl_ms = Some(self.parse_ttl_duration()?);
224 } else if self.consume_ident_ci("EXPIRES")? {
225 self.expect_ident_ci("AT")?;
226 let ts = self.parse_expires_at_value()?;
227 expires_at_ms = Some(ts);
228 } else if self.consume(&Token::Metadata)? || self.consume_ident_ci("METADATA")? {
229 with_metadata = self.parse_with_metadata_pairs()?;
230 } else if self.consume_ident_ci("AUTO")? {
231 self.consume_ident_ci("EMBED")?;
233 self.expect(Token::LParen)?;
234 let mut fields = Vec::new();
235 loop {
236 fields.push(self.expect_ident()?);
237 if !self.consume(&Token::Comma)? {
238 break;
239 }
240 }
241 self.expect(Token::RParen)?;
242 let provider = if self.consume(&Token::Using)? {
247 self.expect_ident()?
248 } else {
249 "openai".to_string()
250 };
251 let model = if self.consume_ident_ci("MODEL")? {
252 Some(self.parse_string()?)
253 } else {
254 None
255 };
256 auto_embed = Some(crate::ast::AutoEmbedConfig {
257 fields,
258 provider,
259 model,
260 });
261 } else {
262 return Err(ParseError::expected(
263 vec!["TTL", "EXPIRES AT", "METADATA", "AUTO EMBED"],
264 self.peek(),
265 self.position(),
266 ));
267 }
268 }
269
270 Ok((ttl_ms, expires_at_ms, with_metadata, auto_embed))
271 }
272
273 fn expect_ident_ci(&mut self, expected: &str) -> Result<(), ParseError> {
275 if self.consume_ident_ci(expected)? {
276 Ok(())
277 } else {
278 Err(ParseError::expected(
279 vec![expected],
280 self.peek(),
281 self.position(),
282 ))
283 }
284 }
285
286 fn parse_expires_at_value(&mut self) -> Result<u64, ParseError> {
288 if let Ok(value) = self.parse_integer() {
290 return Ok(value as u64);
291 }
292 if let Ok(text) = self.parse_string() {
294 let trimmed = text.trim();
296 if let Ok(ts) = trimmed.parse::<u64>() {
297 return Ok(ts);
298 }
299 return Err(ParseError::new(
301 format!("EXPIRES AT requires a unix timestamp in milliseconds, got {trimmed:?}"),
305 self.position(),
306 ));
307 }
308 Err(ParseError::expected(
309 vec!["timestamp (unix ms) or 'YYYY-MM-DD'"],
310 self.peek(),
311 self.position(),
312 ))
313 }
314
315 fn parse_with_metadata_pairs(&mut self) -> Result<Vec<(String, Value)>, ParseError> {
317 self.expect(Token::LParen)?;
318 let mut pairs = Vec::new();
319 if !self.check(&Token::RParen) {
320 loop {
321 let key = self.expect_ident_or_keyword()?.to_ascii_lowercase();
322 self.expect(Token::Eq)?;
323 let value = self.parse_literal_value()?;
324 pairs.push((key, value));
325 if !self.consume(&Token::Comma)? {
326 break;
327 }
328 }
329 }
330 self.expect(Token::RParen)?;
331 Ok(pairs)
332 }
333
334 pub fn parse_update_query(&mut self) -> Result<QueryExpr, ParseError> {
336 self.expect(Token::Update)?;
337 let table = self.expect_ident()?;
338 let target = self.parse_update_target()?;
339 self.expect(Token::Set)?;
340
341 let mut assignments = Vec::new();
342 let mut assignment_exprs = Vec::new();
343 let mut compound_assignment_ops = Vec::new();
344 loop {
345 let col = self.expect_column_ident()?;
346 let compound_op = if self.consume(&Token::Eq)? {
347 None
348 } else {
349 let op = match self.peek() {
350 Token::Plus => BinOp::Add,
351 Token::Dash | Token::Minus => BinOp::Sub,
352 Token::Star => BinOp::Mul,
353 Token::Slash => BinOp::Div,
354 Token::Percent => BinOp::Mod,
355 _ => {
356 return Err(ParseError::expected(
357 vec!["=", "+=", "-=", "*=", "/=", "%="],
358 self.peek(),
359 self.position(),
360 ));
361 }
362 };
363 self.advance()?;
364 self.expect(Token::Eq)?;
365 Some(op)
366 };
367 let expr = self.parse_expr()?;
368 let folded = fold_expr_to_value(expr.clone()).ok();
369 assignment_exprs.push((col.clone(), expr));
370 compound_assignment_ops.push(compound_op);
371 if compound_op.is_none() {
372 if let Some(val) = folded {
373 assignments.push((col.clone(), val));
374 }
375 }
376 if !self.consume(&Token::Comma)? {
377 break;
378 }
379 }
380
381 let filter = if self.consume(&Token::Where)? {
382 Some(self.parse_filter()?)
383 } else {
384 None
385 };
386 let where_expr = filter.as_ref().map(filter_to_expr);
387
388 let (ttl_ms, expires_at_ms, with_metadata, _auto_embed) = self.parse_with_clauses()?;
389
390 let mut order_by = if self.consume(&Token::Order)? {
391 self.expect(Token::By)?;
392 let clauses = self.parse_order_by_list()?;
393 validate_update_order_by(&clauses, self.position())?;
394 clauses
395 } else {
396 Vec::new()
397 };
398
399 let limit = if self.consume(&Token::Limit)? {
404 Some(self.parse_integer()? as u64)
405 } else {
406 None
407 };
408 if !order_by.is_empty() && limit.is_none() {
409 return Err(ParseError::new(
410 "UPDATE ORDER BY requires LIMIT",
411 self.position(),
412 ));
413 }
414 if !order_by.is_empty() && !update_order_by_mentions_rid(&order_by) {
415 order_by.push(OrderByClause {
416 field: FieldRef::TableColumn {
417 table: String::new(),
418 column: "rid".to_string(),
419 },
420 expr: None,
421 ascending: true,
422 nulls_first: false,
423 });
424 }
425
426 let returning = self.parse_returning_clause()?;
427
428 let suppress_events = if self.consume_ident_ci("SUPPRESS")? {
429 self.expect_ident_ci("EVENTS")?;
430 true
431 } else {
432 false
433 };
434
435 Ok(QueryExpr::Update(UpdateQuery {
436 table,
437 target,
438 assignment_exprs,
439 compound_assignment_ops,
440 assignments,
441 where_expr,
442 filter,
443 ttl_ms,
444 expires_at_ms,
445 with_metadata,
446 returning,
447 order_by,
448 limit,
449 suppress_events,
450 }))
451 }
452
453 fn parse_update_target(&mut self) -> Result<UpdateTarget, ParseError> {
454 if self.consume(&Token::Kv)? {
455 return Ok(UpdateTarget::Kv);
456 }
457 if self.consume(&Token::Rows)? {
458 return Ok(UpdateTarget::Rows);
459 }
460 if self.consume_ident_ci("DOCUMENTS")? {
461 return Ok(UpdateTarget::Documents);
462 }
463 if self.consume_ident_ci("NODES")? {
464 return Ok(UpdateTarget::Nodes);
465 }
466 if self.consume_ident_ci("EDGES")? {
467 return Ok(UpdateTarget::Edges);
468 }
469 Ok(UpdateTarget::Rows)
470 }
471
472 pub fn parse_delete_query(&mut self) -> Result<QueryExpr, ParseError> {
474 self.expect(Token::Delete)?;
475 self.expect(Token::From)?;
476 let table = self.expect_ident()?;
477
478 let filter = if self.consume(&Token::Where)? {
479 Some(self.parse_filter()?)
480 } else {
481 None
482 };
483
484 let where_expr = filter.as_ref().map(filter_to_expr);
485
486 let returning = self.parse_returning_clause()?;
487
488 let suppress_events = if self.consume_ident_ci("SUPPRESS")? {
489 self.expect_ident_ci("EVENTS")?;
490 true
491 } else {
492 false
493 };
494
495 Ok(QueryExpr::Delete(DeleteQuery {
496 table,
497 where_expr,
498 filter,
499 returning,
500 suppress_events,
501 }))
502 }
503
504 fn parse_returning_clause(&mut self) -> Result<Option<Vec<ReturningItem>>, ParseError> {
508 if !self.consume(&Token::Returning)? {
509 return Ok(None);
510 }
511 if self.consume(&Token::Star)? {
512 return Ok(Some(vec![ReturningItem::All]));
513 }
514 let mut items = Vec::new();
515 loop {
516 if returning_expr_start(self.peek()) {
517 return Err(returning_expr_not_supported(self.position()));
518 }
519 let col = self.expect_update_returning_column()?;
520 items.push(ReturningItem::Column(col));
521 if returning_expr_tail(self.peek()) {
522 return Err(returning_expr_not_supported(self.position()));
523 }
524 if !self.consume(&Token::Comma)? {
525 break;
526 }
527 }
528 if items.is_empty() {
529 return Err(ParseError::expected(
530 vec!["*", "column name"],
531 self.peek(),
532 self.position(),
533 ));
534 }
535 Ok(Some(items))
536 }
537
538 fn expect_update_returning_column(&mut self) -> Result<String, ParseError> {
539 if self.consume(&Token::Weight)? {
540 return Ok("weight".to_string());
541 }
542 self.expect_ident_or_keyword()
543 }
544
545 pub fn parse_ask_query(&mut self) -> Result<QueryExpr, ParseError> {
548 self.parse_ask_query_with_explain(false)
549 }
550
551 pub fn parse_explain_ask_query(&mut self) -> Result<QueryExpr, ParseError> {
553 self.advance()?; if !matches!(self.peek(), Token::Ident(name) if name.eq_ignore_ascii_case("ASK")) {
555 return Err(ParseError::expected(
556 vec!["ASK"],
557 self.peek(),
558 self.position(),
559 ));
560 }
561 self.parse_ask_query_with_explain(true)
562 }
563
564 fn parse_ask_query_with_explain(&mut self, explain: bool) -> Result<QueryExpr, ParseError> {
565 self.advance()?; let (question, question_param) = match self.peek() {
568 Token::String(_) => (self.parse_string()?, None),
569 Token::Dollar | Token::Question => {
570 let index = self.parse_param_slot("ASK question")?;
571 (String::new(), Some(index))
572 }
573 other => {
574 return Err(ParseError::expected(
575 vec!["string", "$N", "?"],
576 other,
577 self.position(),
578 ));
579 }
580 };
581
582 let mut provider = None;
583 let mut model = None;
584 let mut depth = None;
585 let mut limit = None;
586 let mut min_score = None;
587 let mut collection = None;
588 let mut temperature = None;
589 let mut seed = None;
590 let mut strict = true;
591 let mut stream = false;
592 let mut cache = AskCacheClause::Default;
593 let mut as_rql = false;
594 let mut execute = false;
595
596 for _ in 0..14 {
599 if self.consume(&Token::Using)? {
600 provider = Some(match &self.current.token {
601 Token::String(_) => self.parse_string()?,
602 _ => self.expect_ident()?,
603 });
604 } else if self.consume_ident_ci("MODEL")? {
605 model = Some(self.parse_string()?);
606 } else if self.consume(&Token::Depth)? {
607 depth = Some(self.parse_integer()? as usize);
608 } else if self.consume(&Token::Limit)? {
609 limit = Some(self.parse_integer()? as usize);
610 } else if self.consume(&Token::MinScore)? {
611 min_score = Some(self.parse_float()? as f32);
612 } else if self.consume(&Token::Collection)? {
613 collection = Some(self.expect_ident()?);
614 } else if self.consume_ident_ci("TEMPERATURE")? {
615 temperature = Some(self.parse_float()? as f32);
616 } else if self.consume_ident_ci("SEED")? {
617 seed = Some(self.parse_integer()? as u64);
618 } else if self.consume_ident_ci("STRICT")? {
619 let value = self.expect_ident_or_keyword()?;
620 if value.eq_ignore_ascii_case("ON") {
621 strict = true;
622 } else if value.eq_ignore_ascii_case("OFF") {
623 strict = false;
624 } else {
625 return Err(ParseError::new(
626 "Expected ON or OFF after STRICT",
627 self.position(),
628 ));
629 }
630 } else if self.consume_ident_ci("STREAM")? {
631 stream = true;
632 } else if self.consume_ident_ci("CACHE")? {
633 if !matches!(cache, AskCacheClause::Default) {
634 return Err(ParseError::new(
635 "ASK cache clause specified more than once",
636 self.position(),
637 ));
638 }
639 let ttl = self.expect_ident_or_keyword()?;
640 if !ttl.eq_ignore_ascii_case("TTL") {
641 return Err(ParseError::new("Expected TTL after CACHE", self.position()));
642 }
643 cache = AskCacheClause::CacheTtl(self.parse_string()?);
644 } else if self.consume_ident_ci("NOCACHE")? {
645 if !matches!(cache, AskCacheClause::Default) {
646 return Err(ParseError::new(
647 "ASK cache clause specified more than once",
648 self.position(),
649 ));
650 }
651 cache = AskCacheClause::NoCache;
652 } else if self.consume(&Token::As)? {
653 if as_rql {
654 return Err(ParseError::new(
655 "ASK AS RQL specified more than once",
656 self.position(),
657 ));
658 }
659 let output = self.expect_ident_or_keyword()?;
660 if !output.eq_ignore_ascii_case("RQL") {
661 return Err(ParseError::new(
662 "Expected RQL after ASK AS",
663 self.position(),
664 ));
665 }
666 as_rql = true;
667 } else if self.consume_ident_ci("EXECUTE")? {
668 if execute {
669 return Err(ParseError::new(
670 "ASK EXECUTE specified more than once",
671 self.position(),
672 ));
673 }
674 execute = true;
675 } else {
676 break;
677 }
678 }
679
680 Ok(QueryExpr::Ask(AskQuery {
681 explain,
682 question,
683 question_param,
684 provider,
685 model,
686 depth,
687 limit,
688 min_score,
689 collection,
690 temperature,
691 seed,
692 strict,
693 stream,
694 cache,
695 as_rql,
696 execute,
697 }))
698 }
699
700 fn parse_ident_list(&mut self) -> Result<Vec<String>, ParseError> {
702 let mut idents = Vec::new();
703 loop {
704 idents.push(self.expect_ident_or_keyword()?);
705 if !self.consume(&Token::Comma)? {
706 break;
707 }
708 }
709 Ok(idents)
710 }
711
712 fn parse_dml_value_list(&mut self) -> Result<Vec<Value>, ParseError> {
714 self.parse_dml_expr_list()?
715 .into_iter()
716 .map(fold_expr_to_value)
717 .collect::<Result<Vec<_>, _>>()
718 .map_err(|msg| ParseError::new(msg, self.position()))
719 }
720
721 fn parse_dml_expr_list(&mut self) -> Result<Vec<Expr>, ParseError> {
722 let mut values = Vec::new();
723 loop {
724 values.push(self.parse_expr()?);
725 if !self.consume(&Token::Comma)? {
726 break;
727 }
728 }
729 Ok(values)
730 }
731
732 pub(crate) fn parse_literal_value(&mut self) -> Result<Value, ParseError> {
734 self.enter_depth()?;
743 let result = self.parse_literal_value_inner();
744 self.exit_depth();
745 result
746 }
747
748 fn parse_literal_value_inner(&mut self) -> Result<Value, ParseError> {
749 if let Token::Ident(name) = self.peek().clone() {
756 let upper = name.to_uppercase();
757 if upper == "PASSWORD" || upper == "SECRET" {
758 self.advance()?; self.expect(Token::LParen)?;
760 let plaintext = self.parse_string()?;
761 self.expect(Token::RParen)?;
762 return Ok(match upper.as_str() {
763 "PASSWORD" => Value::Password(format!("@@plain@@{plaintext}")),
764 "SECRET" => Value::Secret(format!("@@plain@@{plaintext}").into_bytes()),
765 _ => unreachable!(),
766 });
767 }
768 if upper == "SECRET_REF" {
769 self.advance()?; self.expect(Token::LParen)?;
771 let store = self.expect_ident_or_keyword()?.to_ascii_lowercase();
772 if store != "vault" {
773 return Err(ParseError::expected(
774 vec!["vault"],
775 self.peek(),
776 self.position(),
777 ));
778 }
779 self.expect(Token::Comma)?;
780 let (collection, key) =
781 self.parse_kv_key(reddb_types::catalog::CollectionModel::Vault)?;
782 self.expect(Token::RParen)?;
783 return Ok(secret_ref_value(&store, &collection, &key));
784 }
785 }
786
787 match self.peek().clone() {
788 Token::String(s) => {
789 let s = s.clone();
790 self.advance()?;
791 Ok(Value::text(s))
792 }
793 Token::JsonLiteral(raw) => {
794 self.advance()?;
799 let json_value = reddb_types::utils::json::parse_json(&raw).map_err(|err| {
800 ParseError::new(
801 format!("invalid JSON object literal: {:?}", err.to_string()),
807 self.position(),
808 )
809 })?;
810 json_literal_depth_check(&json_value)
811 .map_err(|err| ParseError::new(err, self.position()))?;
812 let canonical = reddb_types::serde_json::Value::from(json_value);
813 let bytes = reddb_types::json::to_vec(&canonical).map_err(|err| {
814 ParseError::new(
815 format!("failed to encode JSON literal: {:?}", err.to_string()),
819 self.position(),
820 )
821 })?;
822 Ok(Value::Json(bytes))
823 }
824 Token::Integer(n) => {
825 self.advance()?;
826 Ok(Value::Integer(n))
827 }
828 Token::Float(n) => {
829 self.advance()?;
830 Ok(Value::Float(n))
831 }
832 Token::True => {
833 self.advance()?;
834 Ok(Value::Boolean(true))
835 }
836 Token::False => {
837 self.advance()?;
838 Ok(Value::Boolean(false))
839 }
840 Token::Null => {
841 self.advance()?;
842 Ok(Value::Null)
843 }
844 Token::LBracket => {
845 self.advance()?; let mut items = Vec::new();
849 if !self.check(&Token::RBracket) {
850 loop {
851 items.push(self.parse_literal_value()?);
852 if !self.consume(&Token::Comma)? {
853 break;
854 }
855 }
856 }
857 self.expect(Token::RBracket)?;
858
859 let all_numeric = items
861 .iter()
862 .all(|v| matches!(v, Value::Integer(_) | Value::Float(_)));
863 if all_numeric && !items.is_empty() {
864 let floats: Vec<f32> = items
865 .iter()
866 .map(|v| match v {
867 Value::Float(f) => *f as f32,
868 Value::Integer(i) => *i as f32,
869 _ => 0.0,
870 })
871 .collect();
872 Ok(Value::Vector(floats))
873 } else {
874 let json_arr: Vec<reddb_types::json::Value> = items
876 .iter()
877 .map(|v| match v {
878 Value::Null => reddb_types::json::Value::Null,
879 Value::Boolean(b) => reddb_types::json::Value::Bool(*b),
880 Value::Integer(i) => reddb_types::json::Value::Number(*i as f64),
881 Value::Float(f) => reddb_types::json::Value::Number(*f),
882 Value::Text(s) => reddb_types::json::Value::String(s.to_string()),
883 _ => reddb_types::json::Value::Null,
884 })
885 .collect();
886 let json_val = reddb_types::json::Value::Array(json_arr);
887 let bytes = reddb_types::json::to_vec(&json_val).unwrap_or_default();
888 Ok(Value::Json(bytes))
889 }
890 }
891 Token::LBrace => {
892 self.advance()?; let mut map = reddb_types::json::Map::new();
895 if !self.check(&Token::RBrace) {
896 loop {
897 let key = match self.peek().clone() {
904 Token::String(s) => {
905 self.advance()?;
906 s
907 }
908 Token::Ident(s) => {
909 self.advance()?;
910 s
911 }
912 _ => self.expect_ident_or_keyword()?.to_ascii_lowercase(),
913 };
914 if !self.consume(&Token::Colon)? {
916 self.expect(Token::Eq)?;
917 }
918 let val = self.parse_literal_value()?;
920 let json_val = match val {
921 Value::Null => reddb_types::json::Value::Null,
922 Value::Boolean(b) => reddb_types::json::Value::Bool(b),
923 Value::Integer(i) => reddb_types::json::Value::Number(i as f64),
924 Value::Float(f) => reddb_types::json::Value::Number(f),
925 Value::Text(s) => reddb_types::json::Value::String(s.to_string()),
926 Value::Json(ref bytes) => reddb_types::json::from_slice(bytes)
927 .unwrap_or(reddb_types::json::Value::Null),
928 _ => reddb_types::json::Value::Null,
929 };
930 map.insert(key, json_val);
931 if !self.consume(&Token::Comma)? {
932 break;
933 }
934 }
935 }
936 self.expect(Token::RBrace)?;
937 let json_val = reddb_types::json::Value::Object(map);
938 let bytes = reddb_types::json::to_vec(&json_val).unwrap_or_default();
939 Ok(Value::Json(bytes))
940 }
941 ref other => Err(ParseError::expected(
942 vec!["string", "number", "true", "false", "null", "[", "{"],
943 other,
944 self.position(),
945 )),
946 }
947 }
948}
949
950fn returning_expr_start(token: &Token) -> bool {
951 matches!(
952 token,
953 Token::Integer(_)
954 | Token::Float(_)
955 | Token::String(_)
956 | Token::JsonLiteral(_)
957 | Token::Null
958 | Token::True
959 | Token::False
960 | Token::LParen
961 | Token::Minus
962 | Token::Question
963 | Token::Dollar
964 )
965}
966
967fn returning_expr_tail(token: &Token) -> bool {
968 matches!(
969 token,
970 Token::LParen
971 | Token::Plus
972 | Token::Minus
973 | Token::Star
974 | Token::Slash
975 | Token::Percent
976 | Token::DoublePipe
977 | Token::Pipe
978 | Token::Eq
979 | Token::Ne
980 | Token::Lt
981 | Token::Le
982 | Token::Gt
983 | Token::Ge
984 | Token::Dot
985 | Token::Colon
986 )
987}
988
989fn validate_update_order_by(
990 clauses: &[OrderByClause],
991 position: crate::lexer::Position,
992) -> Result<(), ParseError> {
993 for clause in clauses {
994 if clause.expr.is_some() {
995 return Err(ParseError::new(
996 "UPDATE ORDER BY only supports top-level fields",
997 position,
998 ));
999 }
1000 match &clause.field {
1001 FieldRef::TableColumn { table, column }
1002 if table.is_empty() && !column.contains('.') => {}
1003 _ => {
1004 return Err(ParseError::new(
1005 "UPDATE ORDER BY only supports top-level fields",
1006 position,
1007 ));
1008 }
1009 }
1010 }
1011 Ok(())
1012}
1013
1014fn update_order_by_mentions_rid(clauses: &[OrderByClause]) -> bool {
1015 clauses.iter().any(|clause| {
1016 matches!(
1017 &clause.field,
1018 FieldRef::TableColumn { table, column }
1019 if table.is_empty() && column.eq_ignore_ascii_case("rid")
1020 )
1021 })
1022}
1023
1024fn returning_expr_not_supported(position: crate::lexer::Position) -> ParseError {
1025 ParseError::new(
1026 "NOT_YET_SUPPORTED: RETURNING expressions are not supported yet; use RETURNING * or named columns. Track a follow-up issue for RETURNING <expr>.",
1027 position,
1028 )
1029}
1030
1031fn secret_ref_value(store: &str, collection: &str, key: &str) -> Value {
1032 let mut map = reddb_types::json::Map::new();
1033 map.insert(
1034 "type".to_string(),
1035 reddb_types::json::Value::String("secret_ref".to_string()),
1036 );
1037 map.insert(
1038 "store".to_string(),
1039 reddb_types::json::Value::String(store.to_string()),
1040 );
1041 map.insert(
1042 "collection".to_string(),
1043 reddb_types::json::Value::String(collection.to_string()),
1044 );
1045 map.insert(
1046 "key".to_string(),
1047 reddb_types::json::Value::String(key.to_string()),
1048 );
1049 Value::Json(
1050 reddb_types::json::to_vec(&reddb_types::json::Value::Object(map)).unwrap_or_default(),
1051 )
1052}
1053
1054#[cfg(test)]
1055mod tests {
1056 use super::*;
1057 use crate::ast::{InsertEntityType, ReturningItem, UpdateTarget};
1058
1059 fn make_parser(input: &str) -> Parser<'_> {
1060 Parser::new(input).expect("lexer")
1061 }
1062
1063 fn insert(input: &str) -> InsertQuery {
1064 let mut parser = make_parser(input);
1065 let QueryExpr::Insert(query) = parser.parse_insert_query().expect("insert") else {
1066 panic!("expected insert query");
1067 };
1068 query
1069 }
1070
1071 fn update(input: &str) -> UpdateQuery {
1072 let mut parser = make_parser(input);
1073 let QueryExpr::Update(query) = parser.parse_update_query().expect("update") else {
1074 panic!("expected update query");
1075 };
1076 query
1077 }
1078
1079 fn delete(input: &str) -> DeleteQuery {
1080 let mut parser = make_parser(input);
1081 let QueryExpr::Delete(query) = parser.parse_delete_query().expect("delete") else {
1082 panic!("expected delete query");
1083 };
1084 query
1085 }
1086
1087 fn ask(input: &str) -> AskQuery {
1088 let mut parser = make_parser(input);
1089 let QueryExpr::Ask(query) = parser.parse_ask_query().expect("ask") else {
1090 panic!("expected ask query");
1091 };
1092 query
1093 }
1094
1095 #[test]
1096 fn insert_entity_types_with_options_returning_and_suppress_events() {
1097 let cases = [
1098 (
1099 "INSERT INTO items NODE (id) VALUES (1)",
1100 InsertEntityType::Node,
1101 ),
1102 (
1103 "INSERT INTO items EDGE (id) VALUES (1)",
1104 InsertEntityType::Edge,
1105 ),
1106 (
1107 "INSERT INTO items VECTOR (id) VALUES (1)",
1108 InsertEntityType::Vector,
1109 ),
1110 (
1111 "INSERT INTO items DOCUMENT (id) VALUES (1)",
1112 InsertEntityType::Document,
1113 ),
1114 ("INSERT INTO items KV (id) VALUES (1)", InsertEntityType::Kv),
1115 ];
1116 for (input, expected) in cases {
1117 assert_eq!(insert(input).entity_type, expected, "{input}");
1118 }
1119
1120 let query = insert(
1121 "INSERT INTO docs (id, body) VALUES (1, 'red'), (2, ?) \
1122 WITH TTL 2 h WITH EXPIRES AT 999 \
1123 WITH METADATA (source = 'test', score = 3) \
1124 WITH AUTO EMBED (body, title) USING openai MODEL 'text-embedding-3-small' \
1125 RETURNING * SUPPRESS EVENTS",
1126 );
1127 assert_eq!(query.table, "docs");
1128 assert_eq!(query.entity_type, InsertEntityType::Row);
1129 assert_eq!(query.columns, vec!["id", "body"]);
1130 assert_eq!(query.values.len(), 2);
1131 assert_eq!(query.value_exprs.len(), 2);
1132 assert_eq!(query.ttl_ms, Some(7_200_000));
1133 assert_eq!(query.expires_at_ms, Some(999));
1134 assert_eq!(query.with_metadata.len(), 2);
1135 assert_eq!(
1136 query.returning.as_deref(),
1137 Some([ReturningItem::All].as_slice())
1138 );
1139 let auto_embed = query.auto_embed.expect("auto embed");
1140 assert_eq!(auto_embed.fields, vec!["body", "title"]);
1141 assert_eq!(auto_embed.provider, "openai");
1142 assert_eq!(auto_embed.model.as_deref(), Some("text-embedding-3-small"));
1143 assert!(query.suppress_events);
1144 }
1145
1146 #[test]
1147 fn insert_rejects_metric_and_bad_with_clause() {
1148 let mut parser = make_parser("INSERT INTO METRIC cpu.usage (value) VALUES (1)");
1149 let err = parser
1150 .parse_insert_query()
1151 .expect_err("metric insert should fail");
1152 assert!(err.to_string().contains("INSERT INTO METRIC"));
1153
1154 let mut parser = make_parser("INSERT INTO docs (id) VALUES (1) WITH TTL 1 fortnight");
1155 let err = parser
1156 .parse_insert_query()
1157 .expect_err("bad ttl unit should fail");
1158 assert!(err.to_string().contains("unsupported TTL unit"));
1159
1160 let mut parser = make_parser("INSERT INTO docs (id) VALUES (1) WITH UNKNOWN");
1161 let err = parser
1162 .parse_insert_query()
1163 .expect_err("bad WITH should fail");
1164 assert!(err.to_string().contains("expected"));
1165 }
1166
1167 #[test]
1168 fn update_targets_compound_assignments_order_limit_returning() {
1169 let cases = [
1170 ("UPDATE docs KV SET count = 1", UpdateTarget::Kv),
1171 ("UPDATE docs ROWS SET count = 1", UpdateTarget::Rows),
1172 (
1173 "UPDATE docs DOCUMENTS SET count = 1",
1174 UpdateTarget::Documents,
1175 ),
1176 ("UPDATE docs NODES SET count = 1", UpdateTarget::Nodes),
1177 ("UPDATE docs EDGES SET count = 1", UpdateTarget::Edges),
1178 ];
1179 for (input, expected) in cases {
1180 assert_eq!(update(input).target, expected, "{input}");
1181 }
1182
1183 let query = update(
1184 "UPDATE docs DOCUMENTS SET count += 2, title = UPPER(title) \
1185 WHERE id = 1 WITH TTL 30 s WITH METADATA (source = 'update') \
1186 ORDER BY updated_at DESC LIMIT 5 RETURNING weight, title SUPPRESS EVENTS",
1187 );
1188 assert_eq!(query.table, "docs");
1189 assert_eq!(query.target, UpdateTarget::Documents);
1190 assert_eq!(query.assignment_exprs.len(), 2);
1191 assert_eq!(query.compound_assignment_ops, vec![Some(BinOp::Add), None]);
1192 assert_eq!(query.assignments.len(), 0);
1193 assert!(query.filter.is_some());
1194 assert!(query.where_expr.is_some());
1195 assert_eq!(query.ttl_ms, Some(30_000));
1196 assert_eq!(query.with_metadata.len(), 1);
1197 assert_eq!(query.limit, Some(5));
1198 assert_eq!(query.order_by.len(), 2);
1199 assert!(matches!(
1200 &query.order_by[1].field,
1201 FieldRef::TableColumn { column, .. } if column == "rid"
1202 ));
1203 assert_eq!(
1204 query.returning.as_deref(),
1205 Some(
1206 [
1207 ReturningItem::Column("weight".to_string()),
1208 ReturningItem::Column("title".to_string())
1209 ]
1210 .as_slice()
1211 )
1212 );
1213 assert!(query.suppress_events);
1214 }
1215
1216 #[test]
1217 fn update_rejects_invalid_assignment_and_order_by_forms() {
1218 let mut parser = make_parser("UPDATE docs SET count ^= 1");
1219 let err = parser
1220 .parse_update_query()
1221 .expect_err("unknown compound assignment should fail");
1222 assert!(err.to_string().contains("expected"));
1223
1224 let mut parser = make_parser("UPDATE docs SET count = 1 ORDER BY updated_at");
1225 let err = parser
1226 .parse_update_query()
1227 .expect_err("ORDER BY without LIMIT should fail");
1228 assert!(err.to_string().contains("requires LIMIT"));
1229
1230 let mut parser = make_parser("UPDATE docs SET count = 1 ORDER BY updated_at + 1 LIMIT 1");
1231 let err = parser
1232 .parse_update_query()
1233 .expect_err("ORDER BY expression should fail");
1234 assert!(err.to_string().contains("top-level fields"));
1235 }
1236
1237 #[test]
1238 fn delete_returning_and_suppress_events() {
1239 let query = delete("DELETE FROM docs WHERE id = 1 RETURNING id, title SUPPRESS EVENTS");
1240 assert_eq!(query.table, "docs");
1241 assert!(query.filter.is_some());
1242 assert!(query.where_expr.is_some());
1243 assert_eq!(
1244 query.returning.as_deref(),
1245 Some(
1246 [
1247 ReturningItem::Column("id".to_string()),
1248 ReturningItem::Column("title".to_string())
1249 ]
1250 .as_slice()
1251 )
1252 );
1253 assert!(query.suppress_events);
1254
1255 let query = delete("DELETE FROM docs RETURNING *");
1256 assert_eq!(
1257 query.returning.as_deref(),
1258 Some([ReturningItem::All].as_slice())
1259 );
1260 }
1261
1262 #[test]
1263 fn returning_rejects_expression_forms() {
1264 for input in [
1265 "DELETE FROM docs RETURNING 1",
1266 "DELETE FROM docs RETURNING UPPER(title)",
1267 "DELETE FROM docs RETURNING title || body",
1268 ] {
1269 let mut parser = make_parser(input);
1270 let err = parser
1271 .parse_delete_query()
1272 .expect_err("RETURNING expression should fail");
1273 assert!(err.to_string().contains("RETURNING expressions"));
1274 }
1275 }
1276
1277 #[test]
1278 fn ask_parses_all_optional_clauses_and_cache_modes() {
1279 let query = ask(
1280 "ASK 'what changed?' USING 'openai' MODEL 'gpt' DEPTH 3 LIMIT 4 \
1281 MIN_SCORE 0.7 COLLECTION docs TEMPERATURE 0.2 SEED 42 STRICT OFF \
1282 STREAM CACHE TTL '10m'",
1283 );
1284 assert_eq!(query.question, "what changed?");
1285 assert_eq!(query.provider.as_deref(), Some("openai"));
1286 assert_eq!(query.model.as_deref(), Some("gpt"));
1287 assert_eq!(query.depth, Some(3));
1288 assert_eq!(query.limit, Some(4));
1289 assert_eq!(query.min_score, Some(0.7));
1290 assert_eq!(query.collection.as_deref(), Some("docs"));
1291 assert_eq!(query.temperature, Some(0.2));
1292 assert_eq!(query.seed, Some(42));
1293 assert!(!query.strict);
1294 assert!(query.stream);
1295 assert_eq!(query.cache, AskCacheClause::CacheTtl("10m".to_string()));
1296
1297 let query = ask("ASK ? NOCACHE");
1298 assert_eq!(query.question, "");
1299 assert_eq!(query.question_param, Some(0));
1300 assert_eq!(query.cache, AskCacheClause::NoCache);
1301 }
1302
1303 #[test]
1304 fn explain_ask_and_ask_error_paths() {
1305 let mut parser = make_parser("EXPLAIN ASK $2 STRICT ON");
1306 let QueryExpr::Ask(query) = parser.parse_explain_ask_query().expect("explain ask") else {
1307 panic!("expected ask query");
1308 };
1309 assert!(query.explain);
1310 assert_eq!(query.question_param, Some(1));
1311 assert!(query.strict);
1312
1313 let mut parser = make_parser("EXPLAIN SELECT 1");
1314 let err = parser
1315 .parse_explain_ask_query()
1316 .expect_err("missing ASK should fail");
1317 assert!(err.to_string().contains("expected"));
1318
1319 let mut parser = make_parser("ASK 'q' STRICT MAYBE");
1320 let err = parser
1321 .parse_ask_query()
1322 .expect_err("bad strict should fail");
1323 assert!(err.to_string().contains("Expected ON or OFF"));
1324
1325 let mut parser = make_parser("ASK 'q' CACHE TTL '10m' NOCACHE");
1326 let err = parser
1327 .parse_ask_query()
1328 .expect_err("duplicate cache should fail");
1329 assert!(err.to_string().contains("cache clause"));
1330
1331 let mut parser = make_parser("ASK 'q' CACHE FOREVER '10m'");
1332 let err = parser
1333 .parse_ask_query()
1334 .expect_err("bad cache ttl keyword should fail");
1335 assert!(err.to_string().contains("Expected TTL"));
1336 }
1337
1338 #[test]
1339 fn literal_value_special_constructors_arrays_and_objects() {
1340 let mut parser = make_parser("PASSWORD('pw')");
1341 assert!(matches!(
1342 parser.parse_literal_value().expect("password"),
1343 Value::Password(secret) if secret == "@@plain@@pw"
1344 ));
1345
1346 let mut parser = make_parser("SECRET('pw')");
1347 assert!(matches!(
1348 parser.parse_literal_value().expect("secret"),
1349 Value::Secret(bytes) if bytes == b"@@plain@@pw"
1350 ));
1351
1352 let mut parser = make_parser("SECRET_REF(vault, red.vault.api_key)");
1353 let value = parser.parse_literal_value().expect("secret ref");
1354 assert!(matches!(value, Value::Json(_)));
1355
1356 let mut parser = make_parser("[1, 2.5]");
1357 assert!(matches!(
1358 parser.parse_literal_value().expect("vector"),
1359 Value::Vector(values) if values == vec![1.0, 2.5]
1360 ));
1361
1362 let mut parser = make_parser("['a', 2]");
1363 assert!(matches!(
1364 parser.parse_literal_value().expect("json array"),
1365 Value::Json(_)
1366 ));
1367
1368 let mut parser = make_parser("{level = 'info', count: 2}");
1369 assert!(matches!(
1370 parser.parse_literal_value().expect("json object"),
1371 Value::Json(_)
1372 ));
1373 }
1374
1375 #[test]
1376 fn literal_value_rejects_invalid_secret_ref_and_scalar_start() {
1377 let mut parser = make_parser("SECRET_REF(config, red.vault.api_key)");
1378 let err = parser
1379 .parse_literal_value()
1380 .expect_err("non-vault secret ref should fail");
1381 assert!(err.to_string().contains("expected"));
1382
1383 let mut parser = make_parser("ORDER");
1384 let err = parser
1385 .parse_literal_value()
1386 .expect_err("non literal should fail");
1387 assert!(err.to_string().contains("expected"));
1388 }
1389
1390 #[test]
1391 fn json_depth_check_rejects_deep_literals() {
1392 let mut deep = reddb_types::utils::json::JsonValue::Array(vec![]);
1393 for _ in 0..JSON_LITERAL_MAX_DEPTH {
1394 deep = reddb_types::utils::json::JsonValue::Array(vec![deep]);
1395 }
1396 let err = json_literal_depth_check(&deep).expect_err("depth should fail");
1397 assert!(err.contains("JSON_LITERAL_MAX_DEPTH"));
1398 }
1399}