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.parse_update_assignment_target(target)?;
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 (claim_limit, claim_exact) = if self.consume_ident_ci("CLAIM")? {
391 if self.consume_ident_ci("EXACT")? {
392 (Some(self.parse_integer()? as u64), true)
393 } else {
394 self.expect(Token::Limit)?;
395 (Some(self.parse_integer()? as u64), false)
396 }
397 } else {
398 (None, false)
399 };
400
401 let mut order_by = if self.consume(&Token::Order)? {
402 self.expect(Token::By)?;
403 let clauses = self.parse_order_by_list()?;
404 validate_update_order_by(&clauses, self.position())?;
405 clauses
406 } else {
407 Vec::new()
408 };
409
410 let limit = if self.consume(&Token::Limit)? {
415 Some(self.parse_integer()? as u64)
416 } else {
417 None
418 };
419 let effective_limit = limit.is_some() || claim_limit.is_some();
422 if !order_by.is_empty() && !effective_limit {
423 return Err(ParseError::new(
424 "UPDATE ORDER BY requires LIMIT",
425 self.position(),
426 ));
427 }
428 if claim_limit.is_some() && order_by.is_empty() {
429 return Err(ParseError::new(
430 "UPDATE CLAIM requires ORDER BY",
431 self.position(),
432 ));
433 }
434 if !order_by.is_empty() && !update_order_by_mentions_rid(&order_by) {
435 order_by.push(OrderByClause {
436 field: FieldRef::TableColumn {
437 table: String::new(),
438 column: "rid".to_string(),
439 },
440 expr: None,
441 ascending: true,
442 nulls_first: false,
443 });
444 }
445
446 let returning = self.parse_returning_clause()?;
447
448 let suppress_events = if self.consume_ident_ci("SUPPRESS")? {
449 self.expect_ident_ci("EVENTS")?;
450 true
451 } else {
452 false
453 };
454
455 Ok(QueryExpr::Update(UpdateQuery {
456 table,
457 target,
458 assignment_exprs,
459 compound_assignment_ops,
460 assignments,
461 where_expr,
462 filter,
463 ttl_ms,
464 expires_at_ms,
465 with_metadata,
466 returning,
467 claim_limit,
468 claim_exact,
469 order_by,
470 limit,
471 suppress_events,
472 }))
473 }
474
475 fn parse_update_assignment_target(
476 &mut self,
477 target: UpdateTarget,
478 ) -> Result<String, ParseError> {
479 let mut segments = vec![self.expect_column_ident()?];
480 if matches!(target, UpdateTarget::Documents) {
481 while self.consume(&Token::Dot)? {
482 segments.push(self.expect_column_ident()?);
483 }
484 }
485 Ok(segments.join("."))
486 }
487
488 fn parse_update_target(&mut self) -> Result<UpdateTarget, ParseError> {
489 if self.consume(&Token::Kv)? {
490 return Ok(UpdateTarget::Kv);
491 }
492 if self.consume(&Token::Rows)? {
493 return Ok(UpdateTarget::Rows);
494 }
495 if self.consume_ident_ci("DOCUMENTS")? {
496 return Ok(UpdateTarget::Documents);
497 }
498 if self.consume_ident_ci("NODES")? {
499 return Ok(UpdateTarget::Nodes);
500 }
501 if self.consume_ident_ci("EDGES")? {
502 return Ok(UpdateTarget::Edges);
503 }
504 Ok(UpdateTarget::Rows)
505 }
506
507 pub fn parse_delete_query(&mut self) -> Result<QueryExpr, ParseError> {
509 self.expect(Token::Delete)?;
510 self.expect(Token::From)?;
511 let table = self.expect_ident()?;
512
513 let filter = if self.consume(&Token::Where)? {
514 Some(self.parse_filter()?)
515 } else {
516 None
517 };
518
519 let where_expr = filter.as_ref().map(filter_to_expr);
520
521 let returning = self.parse_returning_clause()?;
522
523 let suppress_events = if self.consume_ident_ci("SUPPRESS")? {
524 self.expect_ident_ci("EVENTS")?;
525 true
526 } else {
527 false
528 };
529
530 Ok(QueryExpr::Delete(DeleteQuery {
531 table,
532 where_expr,
533 filter,
534 returning,
535 suppress_events,
536 }))
537 }
538
539 fn parse_returning_clause(&mut self) -> Result<Option<Vec<ReturningItem>>, ParseError> {
543 if !self.consume(&Token::Returning)? {
544 return Ok(None);
545 }
546 if self.consume(&Token::Star)? {
547 return Ok(Some(vec![ReturningItem::All]));
548 }
549 let mut items = Vec::new();
550 loop {
551 if returning_expr_start(self.peek()) {
552 return Err(returning_expr_not_supported(self.position()));
553 }
554 let col = self.expect_update_returning_column()?;
555 items.push(ReturningItem::Column(col));
556 if returning_expr_tail(self.peek()) {
557 return Err(returning_expr_not_supported(self.position()));
558 }
559 if !self.consume(&Token::Comma)? {
560 break;
561 }
562 }
563 if items.is_empty() {
564 return Err(ParseError::expected(
565 vec!["*", "column name"],
566 self.peek(),
567 self.position(),
568 ));
569 }
570 Ok(Some(items))
571 }
572
573 fn expect_update_returning_column(&mut self) -> Result<String, ParseError> {
574 if self.consume(&Token::Weight)? {
575 return Ok("weight".to_string());
576 }
577 self.expect_ident_or_keyword()
578 }
579
580 pub fn parse_ask_query(&mut self) -> Result<QueryExpr, ParseError> {
583 self.parse_ask_query_with_explain(false)
584 }
585
586 pub fn parse_explain_ask_query(&mut self) -> Result<QueryExpr, ParseError> {
588 self.advance()?; if !matches!(self.peek(), Token::Ident(name) if name.eq_ignore_ascii_case("ASK")) {
590 return Err(ParseError::expected(
591 vec!["ASK"],
592 self.peek(),
593 self.position(),
594 ));
595 }
596 self.parse_ask_query_with_explain(true)
597 }
598
599 fn parse_ask_query_with_explain(&mut self, explain: bool) -> Result<QueryExpr, ParseError> {
600 self.advance()?; let (question, question_param) = match self.peek() {
603 Token::String(_) => (self.parse_string()?, None),
604 Token::Dollar | Token::Question => {
605 let index = self.parse_param_slot("ASK question")?;
606 (String::new(), Some(index))
607 }
608 other => {
609 return Err(ParseError::expected(
610 vec!["string", "$N", "?"],
611 other,
612 self.position(),
613 ));
614 }
615 };
616
617 let mut provider = None;
618 let mut model = None;
619 let mut depth = None;
620 let mut limit = None;
621 let mut min_score = None;
622 let mut collection = None;
623 let mut temperature = None;
624 let mut seed = None;
625 let mut strict = true;
626 let mut stream = false;
627 let mut cache = AskCacheClause::Default;
628 let mut as_rql = false;
629 let mut execute = false;
630
631 for _ in 0..14 {
634 if self.consume(&Token::Using)? {
635 provider = Some(match &self.current.token {
636 Token::String(_) => self.parse_string()?,
637 _ => self.expect_ident()?,
638 });
639 } else if self.consume_ident_ci("MODEL")? {
640 model = Some(self.parse_string()?);
641 } else if self.consume(&Token::Depth)? {
642 depth = Some(self.parse_integer()? as usize);
643 } else if self.consume(&Token::Limit)? {
644 limit = Some(self.parse_integer()? as usize);
645 } else if self.consume(&Token::MinScore)? {
646 min_score = Some(self.parse_float()? as f32);
647 } else if self.consume(&Token::Collection)? {
648 collection = Some(self.expect_ident()?);
649 } else if self.consume_ident_ci("TEMPERATURE")? {
650 temperature = Some(self.parse_float()? as f32);
651 } else if self.consume_ident_ci("SEED")? {
652 seed = Some(self.parse_integer()? as u64);
653 } else if self.consume_ident_ci("STRICT")? {
654 let value = self.expect_ident_or_keyword()?;
655 if value.eq_ignore_ascii_case("ON") {
656 strict = true;
657 } else if value.eq_ignore_ascii_case("OFF") {
658 strict = false;
659 } else {
660 return Err(ParseError::new(
661 "Expected ON or OFF after STRICT",
662 self.position(),
663 ));
664 }
665 } else if self.consume_ident_ci("STREAM")? {
666 stream = true;
667 } else if self.consume_ident_ci("CACHE")? {
668 if !matches!(cache, AskCacheClause::Default) {
669 return Err(ParseError::new(
670 "ASK cache clause specified more than once",
671 self.position(),
672 ));
673 }
674 let ttl = self.expect_ident_or_keyword()?;
675 if !ttl.eq_ignore_ascii_case("TTL") {
676 return Err(ParseError::new("Expected TTL after CACHE", self.position()));
677 }
678 cache = AskCacheClause::CacheTtl(self.parse_string()?);
679 } else if self.consume_ident_ci("NOCACHE")? {
680 if !matches!(cache, AskCacheClause::Default) {
681 return Err(ParseError::new(
682 "ASK cache clause specified more than once",
683 self.position(),
684 ));
685 }
686 cache = AskCacheClause::NoCache;
687 } else if self.consume(&Token::As)? {
688 if as_rql {
689 return Err(ParseError::new(
690 "ASK AS RQL specified more than once",
691 self.position(),
692 ));
693 }
694 let output = self.expect_ident_or_keyword()?;
695 if !output.eq_ignore_ascii_case("RQL") {
696 return Err(ParseError::new(
697 "Expected RQL after ASK AS",
698 self.position(),
699 ));
700 }
701 as_rql = true;
702 } else if self.consume_ident_ci("EXECUTE")? {
703 if execute {
704 return Err(ParseError::new(
705 "ASK EXECUTE specified more than once",
706 self.position(),
707 ));
708 }
709 execute = true;
710 } else {
711 break;
712 }
713 }
714
715 Ok(QueryExpr::Ask(AskQuery {
716 explain,
717 question,
718 question_param,
719 provider,
720 model,
721 depth,
722 limit,
723 min_score,
724 collection,
725 temperature,
726 seed,
727 strict,
728 stream,
729 cache,
730 as_rql,
731 execute,
732 }))
733 }
734
735 fn parse_ident_list(&mut self) -> Result<Vec<String>, ParseError> {
737 let mut idents = Vec::new();
738 loop {
739 idents.push(self.expect_ident_or_keyword()?);
740 if !self.consume(&Token::Comma)? {
741 break;
742 }
743 }
744 Ok(idents)
745 }
746
747 fn parse_dml_value_list(&mut self) -> Result<Vec<Value>, ParseError> {
749 self.parse_dml_expr_list()?
750 .into_iter()
751 .map(fold_expr_to_value)
752 .collect::<Result<Vec<_>, _>>()
753 .map_err(|msg| ParseError::new(msg, self.position()))
754 }
755
756 fn parse_dml_expr_list(&mut self) -> Result<Vec<Expr>, ParseError> {
757 let mut values = Vec::new();
758 loop {
759 values.push(self.parse_expr()?);
760 if !self.consume(&Token::Comma)? {
761 break;
762 }
763 }
764 Ok(values)
765 }
766
767 pub(crate) fn parse_literal_value(&mut self) -> Result<Value, ParseError> {
769 self.enter_depth()?;
778 let result = self.parse_literal_value_inner();
779 self.exit_depth();
780 result
781 }
782
783 fn parse_literal_value_inner(&mut self) -> Result<Value, ParseError> {
784 if let Token::Ident(name) = self.peek().clone() {
791 let upper = name.to_uppercase();
792 if upper == "PASSWORD" || upper == "SECRET" {
793 self.advance()?; self.expect(Token::LParen)?;
795 let plaintext = self.parse_string()?;
796 self.expect(Token::RParen)?;
797 return Ok(match upper.as_str() {
798 "PASSWORD" => Value::Password(format!("@@plain@@{plaintext}")),
799 "SECRET" => Value::Secret(format!("@@plain@@{plaintext}").into_bytes()),
800 _ => unreachable!(),
801 });
802 }
803 if upper == "SECRET_REF" {
804 self.advance()?; self.expect(Token::LParen)?;
806 let store = self.expect_ident_or_keyword()?.to_ascii_lowercase();
807 if store != "vault" {
808 return Err(ParseError::expected(
809 vec!["vault"],
810 self.peek(),
811 self.position(),
812 ));
813 }
814 self.expect(Token::Comma)?;
815 let (collection, key) =
816 self.parse_kv_key(reddb_types::catalog::CollectionModel::Vault)?;
817 self.expect(Token::RParen)?;
818 return Ok(secret_ref_value(&store, &collection, &key));
819 }
820 }
821
822 match self.peek().clone() {
823 Token::String(s) => {
824 let s = s.clone();
825 self.advance()?;
826 Ok(Value::text(s))
827 }
828 Token::JsonLiteral(raw) => {
829 self.advance()?;
834 let json_value = reddb_types::utils::json::parse_json(&raw).map_err(|err| {
835 ParseError::new(
836 format!("invalid JSON object literal: {:?}", err.to_string()),
842 self.position(),
843 )
844 })?;
845 json_literal_depth_check(&json_value)
846 .map_err(|err| ParseError::new(err, self.position()))?;
847 let canonical = reddb_types::serde_json::Value::from(json_value);
848 let bytes = reddb_types::json::to_vec(&canonical).map_err(|err| {
849 ParseError::new(
850 format!("failed to encode JSON literal: {:?}", err.to_string()),
854 self.position(),
855 )
856 })?;
857 Ok(Value::Json(bytes))
858 }
859 Token::Integer(n) => {
860 self.advance()?;
861 Ok(Value::Integer(n))
862 }
863 Token::Float(n) => {
864 self.advance()?;
865 Ok(Value::Float(n))
866 }
867 Token::True => {
868 self.advance()?;
869 Ok(Value::Boolean(true))
870 }
871 Token::False => {
872 self.advance()?;
873 Ok(Value::Boolean(false))
874 }
875 Token::Null => {
876 self.advance()?;
877 Ok(Value::Null)
878 }
879 Token::LBracket => {
880 self.advance()?; let mut items = Vec::new();
884 if !self.check(&Token::RBracket) {
885 loop {
886 items.push(self.parse_literal_value()?);
887 if !self.consume(&Token::Comma)? {
888 break;
889 }
890 }
891 }
892 self.expect(Token::RBracket)?;
893
894 let all_numeric = items
896 .iter()
897 .all(|v| matches!(v, Value::Integer(_) | Value::Float(_)));
898 if all_numeric && !items.is_empty() {
899 let floats: Vec<f32> = items
900 .iter()
901 .map(|v| match v {
902 Value::Float(f) => *f as f32,
903 Value::Integer(i) => *i as f32,
904 _ => 0.0,
905 })
906 .collect();
907 Ok(Value::Vector(floats))
908 } else {
909 let json_arr: Vec<reddb_types::json::Value> = items
911 .iter()
912 .map(|v| match v {
913 Value::Null => reddb_types::json::Value::Null,
914 Value::Boolean(b) => reddb_types::json::Value::Bool(*b),
915 Value::Integer(i) => reddb_types::json::Value::Number(*i as f64),
916 Value::Float(f) => reddb_types::json::Value::Number(*f),
917 Value::Text(s) => reddb_types::json::Value::String(s.to_string()),
918 _ => reddb_types::json::Value::Null,
919 })
920 .collect();
921 let json_val = reddb_types::json::Value::Array(json_arr);
922 let bytes = reddb_types::json::to_vec(&json_val).unwrap_or_default();
923 Ok(Value::Json(bytes))
924 }
925 }
926 Token::LBrace => {
927 self.advance()?; let mut map = reddb_types::json::Map::new();
930 if !self.check(&Token::RBrace) {
931 loop {
932 let key = match self.peek().clone() {
939 Token::String(s) => {
940 self.advance()?;
941 s
942 }
943 Token::Ident(s) => {
944 self.advance()?;
945 s
946 }
947 _ => self.expect_ident_or_keyword()?.to_ascii_lowercase(),
948 };
949 if !self.consume(&Token::Colon)? {
951 self.expect(Token::Eq)?;
952 }
953 let val = self.parse_literal_value()?;
955 let json_val = match val {
956 Value::Null => reddb_types::json::Value::Null,
957 Value::Boolean(b) => reddb_types::json::Value::Bool(b),
958 Value::Integer(i) => reddb_types::json::Value::Number(i as f64),
959 Value::Float(f) => reddb_types::json::Value::Number(f),
960 Value::Text(s) => reddb_types::json::Value::String(s.to_string()),
961 Value::Json(ref bytes) => reddb_types::json::from_slice(bytes)
962 .unwrap_or(reddb_types::json::Value::Null),
963 _ => reddb_types::json::Value::Null,
964 };
965 map.insert(key, json_val);
966 if !self.consume(&Token::Comma)? {
967 break;
968 }
969 }
970 }
971 self.expect(Token::RBrace)?;
972 let json_val = reddb_types::json::Value::Object(map);
973 let bytes = reddb_types::json::to_vec(&json_val).unwrap_or_default();
974 Ok(Value::Json(bytes))
975 }
976 ref other => Err(ParseError::expected(
977 vec!["string", "number", "true", "false", "null", "[", "{"],
978 other,
979 self.position(),
980 )),
981 }
982 }
983}
984
985fn returning_expr_start(token: &Token) -> bool {
986 matches!(
987 token,
988 Token::Integer(_)
989 | Token::Float(_)
990 | Token::String(_)
991 | Token::JsonLiteral(_)
992 | Token::Null
993 | Token::True
994 | Token::False
995 | Token::LParen
996 | Token::Minus
997 | Token::Question
998 | Token::Dollar
999 )
1000}
1001
1002fn returning_expr_tail(token: &Token) -> bool {
1003 matches!(
1004 token,
1005 Token::LParen
1006 | Token::Plus
1007 | Token::Minus
1008 | Token::Star
1009 | Token::Slash
1010 | Token::Percent
1011 | Token::DoublePipe
1012 | Token::Pipe
1013 | Token::Eq
1014 | Token::Ne
1015 | Token::Lt
1016 | Token::Le
1017 | Token::Gt
1018 | Token::Ge
1019 | Token::Dot
1020 | Token::Colon
1021 )
1022}
1023
1024fn validate_update_order_by(
1025 clauses: &[OrderByClause],
1026 position: crate::lexer::Position,
1027) -> Result<(), ParseError> {
1028 for clause in clauses {
1029 if clause.expr.is_some() {
1030 return Err(ParseError::new(
1031 "UPDATE ORDER BY only supports top-level fields",
1032 position,
1033 ));
1034 }
1035 match &clause.field {
1036 FieldRef::TableColumn { table, column }
1037 if table.is_empty() && !column.contains('.') => {}
1038 _ => {
1039 return Err(ParseError::new(
1040 "UPDATE ORDER BY only supports top-level fields",
1041 position,
1042 ));
1043 }
1044 }
1045 }
1046 Ok(())
1047}
1048
1049fn update_order_by_mentions_rid(clauses: &[OrderByClause]) -> bool {
1050 clauses.iter().any(|clause| {
1051 matches!(
1052 &clause.field,
1053 FieldRef::TableColumn { table, column }
1054 if table.is_empty() && column.eq_ignore_ascii_case("rid")
1055 )
1056 })
1057}
1058
1059fn returning_expr_not_supported(position: crate::lexer::Position) -> ParseError {
1060 ParseError::new(
1061 "NOT_YET_SUPPORTED: RETURNING expressions are not supported yet; use RETURNING * or named columns. Track a follow-up issue for RETURNING <expr>.",
1062 position,
1063 )
1064}
1065
1066fn secret_ref_value(store: &str, collection: &str, key: &str) -> Value {
1067 let mut map = reddb_types::json::Map::new();
1068 map.insert(
1069 "type".to_string(),
1070 reddb_types::json::Value::String("secret_ref".to_string()),
1071 );
1072 map.insert(
1073 "store".to_string(),
1074 reddb_types::json::Value::String(store.to_string()),
1075 );
1076 map.insert(
1077 "collection".to_string(),
1078 reddb_types::json::Value::String(collection.to_string()),
1079 );
1080 map.insert(
1081 "key".to_string(),
1082 reddb_types::json::Value::String(key.to_string()),
1083 );
1084 Value::Json(
1085 reddb_types::json::to_vec(&reddb_types::json::Value::Object(map)).unwrap_or_default(),
1086 )
1087}
1088
1089#[cfg(test)]
1090mod tests {
1091 use super::*;
1092 use crate::ast::{InsertEntityType, ReturningItem, UpdateTarget};
1093
1094 fn make_parser(input: &str) -> Parser<'_> {
1095 Parser::new(input).expect("lexer")
1096 }
1097
1098 fn insert(input: &str) -> InsertQuery {
1099 let mut parser = make_parser(input);
1100 let QueryExpr::Insert(query) = parser.parse_insert_query().expect("insert") else {
1101 panic!("expected insert query");
1102 };
1103 query
1104 }
1105
1106 fn update(input: &str) -> UpdateQuery {
1107 let mut parser = make_parser(input);
1108 let QueryExpr::Update(query) = parser.parse_update_query().expect("update") else {
1109 panic!("expected update query");
1110 };
1111 query
1112 }
1113
1114 fn delete(input: &str) -> DeleteQuery {
1115 let mut parser = make_parser(input);
1116 let QueryExpr::Delete(query) = parser.parse_delete_query().expect("delete") else {
1117 panic!("expected delete query");
1118 };
1119 query
1120 }
1121
1122 fn ask(input: &str) -> AskQuery {
1123 let mut parser = make_parser(input);
1124 let QueryExpr::Ask(query) = parser.parse_ask_query().expect("ask") else {
1125 panic!("expected ask query");
1126 };
1127 query
1128 }
1129
1130 #[test]
1131 fn insert_entity_types_with_options_returning_and_suppress_events() {
1132 let cases = [
1133 (
1134 "INSERT INTO items NODE (id) VALUES (1)",
1135 InsertEntityType::Node,
1136 ),
1137 (
1138 "INSERT INTO items EDGE (id) VALUES (1)",
1139 InsertEntityType::Edge,
1140 ),
1141 (
1142 "INSERT INTO items VECTOR (id) VALUES (1)",
1143 InsertEntityType::Vector,
1144 ),
1145 (
1146 "INSERT INTO items DOCUMENT (id) VALUES (1)",
1147 InsertEntityType::Document,
1148 ),
1149 ("INSERT INTO items KV (id) VALUES (1)", InsertEntityType::Kv),
1150 ];
1151 for (input, expected) in cases {
1152 assert_eq!(insert(input).entity_type, expected, "{input}");
1153 }
1154
1155 let query = insert(
1156 "INSERT INTO docs (id, body) VALUES (1, 'red'), (2, ?) \
1157 WITH TTL 2 h WITH EXPIRES AT 999 \
1158 WITH METADATA (source = 'test', score = 3) \
1159 WITH AUTO EMBED (body, title) USING openai MODEL 'text-embedding-3-small' \
1160 RETURNING * SUPPRESS EVENTS",
1161 );
1162 assert_eq!(query.table, "docs");
1163 assert_eq!(query.entity_type, InsertEntityType::Row);
1164 assert_eq!(query.columns, vec!["id", "body"]);
1165 assert_eq!(query.values.len(), 2);
1166 assert_eq!(query.value_exprs.len(), 2);
1167 assert_eq!(query.ttl_ms, Some(7_200_000));
1168 assert_eq!(query.expires_at_ms, Some(999));
1169 assert_eq!(query.with_metadata.len(), 2);
1170 assert_eq!(
1171 query.returning.as_deref(),
1172 Some([ReturningItem::All].as_slice())
1173 );
1174 let auto_embed = query.auto_embed.expect("auto embed");
1175 assert_eq!(auto_embed.fields, vec!["body", "title"]);
1176 assert_eq!(auto_embed.provider, "openai");
1177 assert_eq!(auto_embed.model.as_deref(), Some("text-embedding-3-small"));
1178 assert!(query.suppress_events);
1179 }
1180
1181 #[test]
1182 fn insert_rejects_metric_and_bad_with_clause() {
1183 let mut parser = make_parser("INSERT INTO METRIC cpu.usage (value) VALUES (1)");
1184 let err = parser
1185 .parse_insert_query()
1186 .expect_err("metric insert should fail");
1187 assert!(err.to_string().contains("INSERT INTO METRIC"));
1188
1189 let mut parser = make_parser("INSERT INTO docs (id) VALUES (1) WITH TTL 1 fortnight");
1190 let err = parser
1191 .parse_insert_query()
1192 .expect_err("bad ttl unit should fail");
1193 assert!(err.to_string().contains("unsupported TTL unit"));
1194
1195 let mut parser = make_parser("INSERT INTO docs (id) VALUES (1) WITH UNKNOWN");
1196 let err = parser
1197 .parse_insert_query()
1198 .expect_err("bad WITH should fail");
1199 assert!(err.to_string().contains("expected"));
1200 }
1201
1202 #[test]
1203 fn update_targets_compound_assignments_order_limit_returning() {
1204 let cases = [
1205 ("UPDATE docs KV SET count = 1", UpdateTarget::Kv),
1206 ("UPDATE docs ROWS SET count = 1", UpdateTarget::Rows),
1207 (
1208 "UPDATE docs DOCUMENTS SET count = 1",
1209 UpdateTarget::Documents,
1210 ),
1211 ("UPDATE docs NODES SET count = 1", UpdateTarget::Nodes),
1212 ("UPDATE docs EDGES SET count = 1", UpdateTarget::Edges),
1213 ];
1214 for (input, expected) in cases {
1215 assert_eq!(update(input).target, expected, "{input}");
1216 }
1217
1218 let query = update(
1219 "UPDATE docs DOCUMENTS SET count += 2, title = UPPER(title) \
1220 WHERE id = 1 WITH TTL 30 s WITH METADATA (source = 'update') \
1221 ORDER BY updated_at DESC LIMIT 5 RETURNING weight, title SUPPRESS EVENTS",
1222 );
1223 assert_eq!(query.table, "docs");
1224 assert_eq!(query.target, UpdateTarget::Documents);
1225 assert_eq!(query.assignment_exprs.len(), 2);
1226 assert_eq!(query.compound_assignment_ops, vec![Some(BinOp::Add), None]);
1227 assert_eq!(query.assignments.len(), 0);
1228 assert!(query.filter.is_some());
1229 assert!(query.where_expr.is_some());
1230 assert_eq!(query.ttl_ms, Some(30_000));
1231 assert_eq!(query.with_metadata.len(), 1);
1232 assert_eq!(query.claim_limit, None);
1233 assert!(!query.claim_exact);
1234 assert_eq!(query.limit, Some(5));
1235 assert_eq!(query.order_by.len(), 2);
1236 assert!(matches!(
1237 &query.order_by[1].field,
1238 FieldRef::TableColumn { column, .. } if column == "rid"
1239 ));
1240 assert_eq!(
1241 query.returning.as_deref(),
1242 Some(
1243 [
1244 ReturningItem::Column("weight".to_string()),
1245 ReturningItem::Column("title".to_string())
1246 ]
1247 .as_slice()
1248 )
1249 );
1250 assert!(query.suppress_events);
1251 }
1252
1253 #[test]
1254 fn update_claim_limit_requires_order_by() {
1255 let query = update(
1256 "UPDATE docs SET status = 'reserved' WHERE status = 'available' \
1257 CLAIM LIMIT 2 ORDER BY priority ASC RETURNING id",
1258 );
1259 assert_eq!(query.claim_limit, Some(2));
1260 assert!(!query.claim_exact);
1261 assert_eq!(query.limit, None);
1262 assert_eq!(query.order_by.len(), 2);
1263 assert!(matches!(
1264 &query.order_by[1].field,
1265 FieldRef::TableColumn { column, .. } if column == "rid"
1266 ));
1267
1268 let query = update(
1269 "UPDATE docs SET status = 'reserved' WHERE status = 'available' \
1270 CLAIM EXACT 2 ORDER BY priority ASC RETURNING id",
1271 );
1272 assert_eq!(query.claim_limit, Some(2));
1273 assert!(query.claim_exact);
1274 assert_eq!(query.limit, None);
1275 assert_eq!(query.order_by.len(), 2);
1276 assert!(matches!(
1277 &query.order_by[1].field,
1278 FieldRef::TableColumn { column, .. } if column == "rid"
1279 ));
1280
1281 let mut parser = make_parser("UPDATE docs SET status = 'reserved' CLAIM LIMIT 1");
1282 let err = parser
1283 .parse_update_query()
1284 .expect_err("claim without order should fail");
1285 assert!(err.to_string().contains("CLAIM requires ORDER BY"));
1286 }
1287
1288 #[test]
1289 fn update_rejects_invalid_assignment_and_order_by_forms() {
1290 let mut parser = make_parser("UPDATE docs SET count ^= 1");
1291 let err = parser
1292 .parse_update_query()
1293 .expect_err("unknown compound assignment should fail");
1294 assert!(err.to_string().contains("expected"));
1295
1296 let mut parser = make_parser("UPDATE docs SET count = 1 ORDER BY updated_at");
1297 let err = parser
1298 .parse_update_query()
1299 .expect_err("ORDER BY without LIMIT should fail");
1300 assert!(err.to_string().contains("requires LIMIT"));
1301
1302 let mut parser = make_parser("UPDATE docs SET count = 1 ORDER BY updated_at + 1 LIMIT 1");
1303 let err = parser
1304 .parse_update_query()
1305 .expect_err("ORDER BY expression should fail");
1306 assert!(err.to_string().contains("top-level fields"));
1307 }
1308
1309 #[test]
1310 fn delete_returning_and_suppress_events() {
1311 let query = delete("DELETE FROM docs WHERE id = 1 RETURNING id, title SUPPRESS EVENTS");
1312 assert_eq!(query.table, "docs");
1313 assert!(query.filter.is_some());
1314 assert!(query.where_expr.is_some());
1315 assert_eq!(
1316 query.returning.as_deref(),
1317 Some(
1318 [
1319 ReturningItem::Column("id".to_string()),
1320 ReturningItem::Column("title".to_string())
1321 ]
1322 .as_slice()
1323 )
1324 );
1325 assert!(query.suppress_events);
1326
1327 let query = delete("DELETE FROM docs RETURNING *");
1328 assert_eq!(
1329 query.returning.as_deref(),
1330 Some([ReturningItem::All].as_slice())
1331 );
1332 }
1333
1334 #[test]
1335 fn returning_rejects_expression_forms() {
1336 for input in [
1337 "DELETE FROM docs RETURNING 1",
1338 "DELETE FROM docs RETURNING UPPER(title)",
1339 "DELETE FROM docs RETURNING title || body",
1340 ] {
1341 let mut parser = make_parser(input);
1342 let err = parser
1343 .parse_delete_query()
1344 .expect_err("RETURNING expression should fail");
1345 assert!(err.to_string().contains("RETURNING expressions"));
1346 }
1347 }
1348
1349 #[test]
1350 fn ask_parses_all_optional_clauses_and_cache_modes() {
1351 let query = ask(
1352 "ASK 'what changed?' USING 'openai' MODEL 'gpt' DEPTH 3 LIMIT 4 \
1353 MIN_SCORE 0.7 COLLECTION docs TEMPERATURE 0.2 SEED 42 STRICT OFF \
1354 STREAM CACHE TTL '10m'",
1355 );
1356 assert_eq!(query.question, "what changed?");
1357 assert_eq!(query.provider.as_deref(), Some("openai"));
1358 assert_eq!(query.model.as_deref(), Some("gpt"));
1359 assert_eq!(query.depth, Some(3));
1360 assert_eq!(query.limit, Some(4));
1361 assert_eq!(query.min_score, Some(0.7));
1362 assert_eq!(query.collection.as_deref(), Some("docs"));
1363 assert_eq!(query.temperature, Some(0.2));
1364 assert_eq!(query.seed, Some(42));
1365 assert!(!query.strict);
1366 assert!(query.stream);
1367 assert_eq!(query.cache, AskCacheClause::CacheTtl("10m".to_string()));
1368
1369 let query = ask("ASK ? NOCACHE");
1370 assert_eq!(query.question, "");
1371 assert_eq!(query.question_param, Some(0));
1372 assert_eq!(query.cache, AskCacheClause::NoCache);
1373 }
1374
1375 #[test]
1376 fn explain_ask_and_ask_error_paths() {
1377 let mut parser = make_parser("EXPLAIN ASK $2 STRICT ON");
1378 let QueryExpr::Ask(query) = parser.parse_explain_ask_query().expect("explain ask") else {
1379 panic!("expected ask query");
1380 };
1381 assert!(query.explain);
1382 assert_eq!(query.question_param, Some(1));
1383 assert!(query.strict);
1384
1385 let mut parser = make_parser("EXPLAIN SELECT 1");
1386 let err = parser
1387 .parse_explain_ask_query()
1388 .expect_err("missing ASK should fail");
1389 assert!(err.to_string().contains("expected"));
1390
1391 let mut parser = make_parser("ASK 'q' STRICT MAYBE");
1392 let err = parser
1393 .parse_ask_query()
1394 .expect_err("bad strict should fail");
1395 assert!(err.to_string().contains("Expected ON or OFF"));
1396
1397 let mut parser = make_parser("ASK 'q' CACHE TTL '10m' NOCACHE");
1398 let err = parser
1399 .parse_ask_query()
1400 .expect_err("duplicate cache should fail");
1401 assert!(err.to_string().contains("cache clause"));
1402
1403 let mut parser = make_parser("ASK 'q' CACHE FOREVER '10m'");
1404 let err = parser
1405 .parse_ask_query()
1406 .expect_err("bad cache ttl keyword should fail");
1407 assert!(err.to_string().contains("Expected TTL"));
1408 }
1409
1410 #[test]
1411 fn literal_value_special_constructors_arrays_and_objects() {
1412 let mut parser = make_parser("PASSWORD('pw')");
1413 assert!(matches!(
1414 parser.parse_literal_value().expect("password"),
1415 Value::Password(secret) if secret == "@@plain@@pw"
1416 ));
1417
1418 let mut parser = make_parser("SECRET('pw')");
1419 assert!(matches!(
1420 parser.parse_literal_value().expect("secret"),
1421 Value::Secret(bytes) if bytes == b"@@plain@@pw"
1422 ));
1423
1424 let mut parser = make_parser("SECRET_REF(vault, red.vault.api_key)");
1425 let value = parser.parse_literal_value().expect("secret ref");
1426 assert!(matches!(value, Value::Json(_)));
1427
1428 let mut parser = make_parser("[1, 2.5]");
1429 assert!(matches!(
1430 parser.parse_literal_value().expect("vector"),
1431 Value::Vector(values) if values == vec![1.0, 2.5]
1432 ));
1433
1434 let mut parser = make_parser("['a', 2]");
1435 assert!(matches!(
1436 parser.parse_literal_value().expect("json array"),
1437 Value::Json(_)
1438 ));
1439
1440 let mut parser = make_parser("{level = 'info', count: 2}");
1441 assert!(matches!(
1442 parser.parse_literal_value().expect("json object"),
1443 Value::Json(_)
1444 ));
1445 }
1446
1447 #[test]
1448 fn literal_value_rejects_invalid_secret_ref_and_scalar_start() {
1449 let mut parser = make_parser("SECRET_REF(config, red.vault.api_key)");
1450 let err = parser
1451 .parse_literal_value()
1452 .expect_err("non-vault secret ref should fail");
1453 assert!(err.to_string().contains("expected"));
1454
1455 let mut parser = make_parser("ORDER");
1456 let err = parser
1457 .parse_literal_value()
1458 .expect_err("non literal should fail");
1459 assert!(err.to_string().contains("expected"));
1460 }
1461
1462 #[test]
1463 fn json_depth_check_rejects_deep_literals() {
1464 let mut deep = reddb_types::utils::json::JsonValue::Array(vec![]);
1465 for _ in 0..JSON_LITERAL_MAX_DEPTH {
1466 deep = reddb_types::utils::json::JsonValue::Array(vec![deep]);
1467 }
1468 let err = json_literal_depth_check(&deep).expect_err("depth should fail");
1469 assert!(err.contains("JSON_LITERAL_MAX_DEPTH"));
1470 }
1471}