1use crate::sqljoin::{self, JoinExec, Strategy};
42use crate::sqlplan::{Plan, Stage};
43use crate::sqlpush::Pushdown;
44
45use anyhow::{bail, Result};
46use serde_json::{Map, Value};
47
48#[derive(Debug, Clone, PartialEq)]
53pub enum Tok {
54 Word { upper: String, raw: String },
59 Quoted(String),
62 Str(String),
64 Num(f64),
65 Op(String),
66 Punct(char),
67 Eof,
68}
69
70impl Tok {
71 fn is_kw(&self, kw: &str) -> bool {
72 matches!(self, Tok::Word { upper, .. } if upper == kw)
73 }
74 #[allow(dead_code)] fn ident(&self) -> Option<String> {
77 match self {
78 Tok::Word { raw, .. } => Some(raw.clone()),
79 Tok::Quoted(s) => Some(s.clone()),
80 _ => None,
81 }
82 }
83}
84
85const OPERATORS: &[&str] = &[
89 "!~*", "!~", "~*", "<>", "!=", ">=", "<=", "||", "::",
90 "=", "<", ">", "~", "+", "-", "*", "/", "%",
91];
92
93pub fn lex(src: &str) -> Result<Vec<Tok>> {
94 let b: Vec<char> = src.chars().collect();
95 let mut out = vec![];
96 let mut i = 0usize;
97
98 while i < b.len() {
99 let c = b[i];
100
101 if c.is_whitespace() {
103 i += 1;
104 continue;
105 }
106
107 if c == '-' && b.get(i + 1) == Some(&'-') {
109 while i < b.len() && b[i] != '\n' {
110 i += 1;
111 }
112 continue;
113 }
114
115 if c == '/' && b.get(i + 1) == Some(&'*') {
117 let mut depth = 1usize;
118 i += 2;
119 while i < b.len() && depth > 0 {
120 if b[i] == '/' && b.get(i + 1) == Some(&'*') {
121 depth += 1;
122 i += 2;
123 } else if b[i] == '*' && b.get(i + 1) == Some(&'/') {
124 depth -= 1;
125 i += 2;
126 } else {
127 i += 1;
128 }
129 }
130 if depth > 0 {
131 bail!("unterminated /* comment");
132 }
133 continue;
134 }
135
136 if c == '\'' {
138 i += 1;
139 let mut s = String::new();
140 loop {
141 match b.get(i) {
142 None => bail!("unterminated string literal"),
143 Some('\'') if b.get(i + 1) == Some(&'\'') => {
144 s.push('\'');
145 i += 2;
146 }
147 Some('\'') => {
148 i += 1;
149 break;
150 }
151 Some(ch) => {
152 s.push(*ch);
153 i += 1;
154 }
155 }
156 }
157 out.push(Tok::Str(s));
158 continue;
159 }
160
161 if (c == 'E' || c == 'e') && b.get(i + 1) == Some(&'\'') {
164 i += 2;
165 let mut s = String::new();
166 loop {
167 match b.get(i) {
168 None => bail!("unterminated E'' string literal"),
169 Some('\\') => {
170 let esc = b.get(i + 1).copied().unwrap_or('\\');
174 s.push(match esc {
175 'n' => '\n',
176 't' => '\t',
177 'r' => '\r',
178 '0' => '\0',
179 other => other,
180 });
181 i += 2;
182 }
183 Some('\'') if b.get(i + 1) == Some(&'\'') => {
184 s.push('\'');
185 i += 2;
186 }
187 Some('\'') => {
188 i += 1;
189 break;
190 }
191 Some(ch) => {
192 s.push(*ch);
193 i += 1;
194 }
195 }
196 }
197 out.push(Tok::Str(s));
198 continue;
199 }
200
201 if c == '"' {
203 i += 1;
204 let mut s = String::new();
205 loop {
206 match b.get(i) {
207 None => bail!("unterminated quoted identifier"),
208 Some('"') if b.get(i + 1) == Some(&'"') => {
209 s.push('"');
210 i += 2;
211 }
212 Some('"') => {
213 i += 1;
214 break;
215 }
216 Some(ch) => {
217 s.push(*ch);
218 i += 1;
219 }
220 }
221 }
222 out.push(Tok::Quoted(s));
223 continue;
224 }
225
226 if c.is_ascii_digit()
228 || (c == '.' && b.get(i + 1).map(|d| d.is_ascii_digit()).unwrap_or(false))
229 {
230 let start = i;
231 while i < b.len() && (b[i].is_ascii_digit() || b[i] == '.') {
232 i += 1;
233 }
234 if i < b.len() && (b[i] == 'e' || b[i] == 'E') {
235 let save = i;
236 i += 1;
237 if i < b.len() && (b[i] == '+' || b[i] == '-') {
238 i += 1;
239 }
240 if i < b.len() && b[i].is_ascii_digit() {
241 while i < b.len() && b[i].is_ascii_digit() {
242 i += 1;
243 }
244 } else {
245 i = save; }
247 }
248 let text: String = b[start..i].iter().collect();
249 let n: f64 = text
250 .parse()
251 .map_err(|_| anyhow::anyhow!("not a number: {:?}", text))?;
252 out.push(Tok::Num(n));
253 continue;
254 }
255
256 if c.is_alphabetic() || c == '_' {
258 let start = i;
259 while i < b.len() && (b[i].is_alphanumeric() || b[i] == '_' || b[i] == '$') {
260 i += 1;
261 }
262 let raw: String = b[start..i].iter().collect();
263 out.push(Tok::Word { upper: raw.to_uppercase(), raw });
264 continue;
265 }
266
267 let rest: String = b[i..].iter().take(3).collect();
269 if let Some(op) = OPERATORS.iter().find(|o| rest.starts_with(**o)) {
270 i += op.chars().count();
271 out.push(Tok::Op((*op).to_string()));
272 continue;
273 }
274
275 if matches!(c, '(' | ')' | ',' | ';' | '.' | '[' | ']') {
276 out.push(Tok::Punct(c));
277 i += 1;
278 continue;
279 }
280
281 bail!("unexpected character {:?} in SQL", c);
284 }
285
286 out.push(Tok::Eof);
287 Ok(out)
288}
289
290#[derive(Debug, Clone, PartialEq)]
295pub enum Expr {
296 Column { qual: Option<String>, name: String },
300 Literal(Value),
301 Star,
303 QualifiedStar(String),
305 Func { name: String, args: Vec<Expr> },
306 Case {
311 operand: Option<Box<Expr>>,
312 whens: Vec<(Expr, Expr)>,
313 else_: Option<Box<Expr>>,
314 },
315 Binary { op: String, left: Box<Expr>, right: Box<Expr> },
316 Unary { op: String, expr: Box<Expr> },
317 InList { expr: Box<Expr>, list: Vec<Expr>, negated: bool },
319 IsNull { expr: Box<Expr>, negated: bool },
321 Cast { expr: Box<Expr>, ty: String },
326}
327
328#[derive(Debug, Clone, PartialEq)]
329pub struct SelectItem {
330 pub expr: Expr,
331 pub alias: Option<String>,
334}
335
336#[derive(Debug, Clone, Copy, PartialEq, Eq)]
337pub enum JoinKind { Inner, Left, Right, Full, Cross }
338
339#[derive(Debug, Clone, PartialEq)]
340pub struct TableRef {
341 pub name: String,
345 pub alias: Option<String>,
346}
347
348impl TableRef {
349 pub fn binding(&self) -> String {
352 self.alias.clone().unwrap_or_else(|| {
353 self.name.rsplit('.').next().unwrap_or(&self.name).to_string()
354 })
355 }
356}
357
358#[derive(Debug, Clone, PartialEq)]
359pub struct Join {
360 pub kind: JoinKind,
361 pub table: TableRef,
362 pub on: Option<Expr>,
363}
364
365#[derive(Debug, Clone, Copy, PartialEq)]
366pub enum Dir { Asc, Desc }
367
368#[derive(Debug, Clone, PartialEq)]
369pub struct OrderBy {
370 pub ordinal: Option<usize>,
374 pub expr: Option<Expr>,
375 pub dir: Dir,
376 pub nulls_first: bool,
378}
379
380#[derive(Debug, Clone, PartialEq)]
381pub struct Select {
382 pub distinct: bool,
383 pub items: Vec<SelectItem>,
384 pub from: Option<TableRef>,
385 pub joins: Vec<Join>,
386 pub where_: Option<Expr>,
387 pub order_by: Vec<OrderBy>,
388 pub limit: Option<usize>,
389 pub offset: Option<usize>,
390}
391
392fn binding_power(op: &str) -> Option<u8> {
403 Some(match op {
404 "OR" => 1,
405 "AND" => 2,
406 "=" | "!=" | "<>" | "<" | "<=" | ">" | ">=" | "~" | "~*" | "!~" | "!~*"
410 | "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => 4,
411 "||" => 5,
412 "+" | "-" => 6,
413 "*" | "/" | "%" => 7,
414 _ => return None,
415 })
416}
417
418struct Parser {
419 toks: Vec<Tok>,
420 pos: usize,
421}
422
423impl Parser {
424 fn peek(&self) -> &Tok {
425 self.toks.get(self.pos).unwrap_or(&Tok::Eof)
426 }
427 fn peek_at(&self, n: usize) -> &Tok {
428 self.toks.get(self.pos + n).unwrap_or(&Tok::Eof)
429 }
430 fn next(&mut self) -> Tok {
431 let t = self.peek().clone();
432 self.pos += 1;
433 t
434 }
435 fn eat_kw(&mut self, kw: &str) -> bool {
436 if self.peek().is_kw(kw) {
437 self.pos += 1;
438 true
439 } else {
440 false
441 }
442 }
443 fn expect_kw(&mut self, kw: &str) -> Result<()> {
444 if self.eat_kw(kw) {
445 Ok(())
446 } else {
447 bail!("expected {} , got {:?}", kw, self.peek())
448 }
449 }
450 fn eat_punct(&mut self, c: char) -> bool {
451 if matches!(self.peek(), Tok::Punct(p) if *p == c) {
452 self.pos += 1;
453 true
454 } else {
455 false
456 }
457 }
458 fn expect_punct(&mut self, c: char) -> Result<()> {
459 if self.eat_punct(c) {
460 Ok(())
461 } else {
462 bail!("expected {:?}, got {:?}", c, self.peek())
463 }
464 }
465 fn eat_op(&mut self, op: &str) -> bool {
466 if matches!(self.peek(), Tok::Op(o) if o == op) {
467 self.pos += 1;
468 true
469 } else {
470 false
471 }
472 }
473
474 fn parse_expr(&mut self) -> Result<Expr> {
477 self.parse_bin(0)
478 }
479
480 fn parse_bin(&mut self, min_bp: u8) -> Result<Expr> {
483 let mut left = self.parse_unary()?;
484
485 loop {
486 if self.peek().is_kw("OPERATOR") && matches!(self.peek_at(1), Tok::Punct('(')) {
493 let save = self.pos;
494 self.pos += 2;
495 let mut sym = None;
497 while sym.is_none() {
498 match self.next() {
499 Tok::Op(o) => sym = Some(o),
500 Tok::Word { .. } | Tok::Punct('.') => continue,
501 _ => break,
502 }
503 }
504 match sym {
505 Some(o) if binding_power(&o).is_some() && self.eat_punct(')') => {
506 let bp = binding_power(&o).unwrap();
507 if bp < min_bp {
508 self.pos = save;
509 break;
510 }
511 let right = self.parse_bin(bp + 1)?;
512 left = Expr::Binary {
513 op: o,
514 left: Box::new(left),
515 right: Box::new(right),
516 };
517 continue;
518 }
519 _ => {
522 self.pos = save;
523 break;
524 }
525 }
526 }
527
528 let (op, width) = match self.peek() {
529 Tok::Op(o) if binding_power(o).is_some() => (o.clone(), 1usize),
530 Tok::Word { upper, .. } if upper == "AND" || upper == "OR" => (upper.clone(), 1),
531 Tok::Word { upper, .. } if upper == "LIKE" || upper == "ILIKE" => (upper.clone(), 1),
532 Tok::Word { upper, .. } if upper == "NOT" => {
533 match self.peek_at(1) {
535 Tok::Word { upper: u2, .. } if u2 == "LIKE" || u2 == "ILIKE" => {
536 (format!("NOT {}", u2), 2)
537 }
538 _ => break,
539 }
540 }
541 _ => break,
542 };
543
544 let bp = match binding_power(&op) {
545 Some(bp) if bp >= min_bp => bp,
546 _ => break,
547 };
548 self.pos += width;
549 let right = self.parse_bin(bp + 1)?;
551 left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
552 }
553
554 Ok(left)
555 }
556
557 fn parse_postfix(&mut self, mut e: Expr) -> Result<Expr> {
558 loop {
559 if self.peek().is_kw("IS") {
561 self.pos += 1;
562 let negated = self.eat_kw("NOT");
563 if !self.eat_kw("NULL") {
564 if self.eat_kw("TRUE") {
566 e = Expr::Binary {
567 op: "=".into(),
568 left: Box::new(e),
569 right: Box::new(Expr::Literal(Value::Bool(!negated))),
570 };
571 continue;
572 }
573 if self.eat_kw("FALSE") {
574 e = Expr::Binary {
575 op: "=".into(),
576 left: Box::new(e),
577 right: Box::new(Expr::Literal(Value::Bool(negated))),
578 };
579 continue;
580 }
581 bail!("expected NULL, TRUE or FALSE after IS, got {:?}", self.peek());
582 }
583 e = Expr::IsNull { expr: Box::new(e), negated };
584 continue;
585 }
586
587 let negated_in = if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("IN") {
589 self.pos += 2;
590 true
591 } else if self.peek().is_kw("IN") {
592 self.pos += 1;
593 false
594 } else {
595 let negated_between =
597 if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("BETWEEN") {
598 self.pos += 2;
599 true
600 } else if self.peek().is_kw("BETWEEN") {
601 self.pos += 1;
602 false
603 } else {
604 break;
605 };
606 let low = self.parse_bin(3)?;
610 self.expect_kw("AND")?;
611 let high = self.parse_bin(3)?;
612 let ge = Expr::Binary {
613 op: ">=".into(),
614 left: Box::new(e.clone()),
615 right: Box::new(low),
616 };
617 let le = Expr::Binary {
618 op: "<=".into(),
619 left: Box::new(e),
620 right: Box::new(high),
621 };
622 let both = Expr::Binary {
623 op: "AND".into(),
624 left: Box::new(ge),
625 right: Box::new(le),
626 };
627 e = if negated_between {
628 Expr::Unary { op: "NOT".into(), expr: Box::new(both) }
629 } else {
630 both
631 };
632 continue;
633 };
634
635 self.expect_punct('(')?;
636 let mut list = vec![];
637 if !self.eat_punct(')') {
638 loop {
639 list.push(self.parse_expr()?);
640 if self.eat_punct(',') {
641 continue;
642 }
643 self.expect_punct(')')?;
644 break;
645 }
646 }
647 e = Expr::InList { expr: Box::new(e), list, negated: negated_in };
648 }
649 Ok(e)
650 }
651
652 fn parse_unary(&mut self) -> Result<Expr> {
653 if self.peek().is_kw("NOT") {
654 self.pos += 1;
655 let e = self.parse_bin(3)?;
658 return Ok(Expr::Unary { op: "NOT".into(), expr: Box::new(e) });
659 }
660 if self.eat_op("-") {
661 let e = self.parse_unary()?;
662 return Ok(Expr::Unary { op: "-".into(), expr: Box::new(e) });
663 }
664 if self.eat_op("+") {
665 return self.parse_unary();
666 }
667 let atom = self.parse_atom()?;
668 let cast = self.parse_casts(atom)?;
669 self.parse_postfix(cast)
680 }
681
682 fn parse_casts(&mut self, mut e: Expr) -> Result<Expr> {
684 loop {
685 if self.peek().is_kw("COLLATE") {
690 self.pos += 1;
691 match self.next() {
692 Tok::Word { .. } | Tok::Quoted(_) => {}
693 other => bail!("expected a collation name after COLLATE, got {:?}", other),
694 }
695 while self.eat_punct('.') {
697 match self.next() {
698 Tok::Word { .. } | Tok::Quoted(_) => {}
699 other => bail!("expected a name after '.', got {:?}", other),
700 }
701 }
702 continue;
703 }
704 if !self.eat_op("::") {
705 break;
706 }
707 let mut ty = match self.next() {
708 Tok::Word { raw, .. } => raw,
709 Tok::Quoted(s) => s,
710 other => bail!("expected a type name after ::, got {:?}", other),
711 };
712 while self.eat_punct('.') {
714 match self.next() {
715 Tok::Word { raw, .. } => ty = raw,
716 Tok::Quoted(s) => ty = s,
717 other => bail!("expected a type name after ., got {:?}", other),
718 }
719 }
720 while self.eat_punct('[') {
722 self.expect_punct(']')?;
723 ty.push_str("[]");
724 }
725 e = Expr::Cast { expr: Box::new(e), ty };
726 }
727 Ok(e)
728 }
729
730
731 fn parse_atom(&mut self) -> Result<Expr> {
732 if self.eat_punct('(') {
739 if self.peek().is_kw("SELECT") {
740 bail!("a subquery is not supported by this SELECT path");
741 }
742 let e = self.parse_expr()?;
743 self.expect_punct(')')?;
744 return Ok(e);
745 }
746
747 if self.peek().is_kw("ARRAY") {
750 bail!("the ARRAY(...) constructor is not supported by this SELECT path");
751 }
752 if self.peek().is_kw("EXISTS") {
753 bail!("EXISTS (...) is not supported by this SELECT path");
754 }
755
756 if self.peek().is_kw("CASE") {
758 return self.parse_case();
759 }
760
761 match self.next() {
762 Tok::Num(n) => Ok(Expr::Literal(from_f64(n))),
763 Tok::Str(s) => Ok(Expr::Literal(Value::String(s))),
764 Tok::Op(o) if o == "*" => Ok(Expr::Star),
765 Tok::Quoted(name) => self.parse_name_tail(None, name),
766 Tok::Word { upper, raw } => match upper.as_str() {
767 "NULL" => Ok(Expr::Literal(Value::Null)),
768 "TRUE" => Ok(Expr::Literal(Value::Bool(true))),
769 "FALSE" => Ok(Expr::Literal(Value::Bool(false))),
770 "CURRENT_SCHEMA" | "CURRENT_DATABASE" | "CURRENT_USER" | "SESSION_USER"
774 | "CURRENT_CATALOG" | "USER" | "VERSION"
775 if !matches!(self.peek(), Tok::Punct('(')) =>
776 {
777 Ok(Expr::Func { name: upper.to_lowercase(), args: vec![] })
778 }
779 _ => self.parse_name_tail(None, raw),
780 },
781 other => bail!("unexpected {:?} in an expression", other),
782 }
783 }
784
785 fn parse_name_tail(&mut self, _schema: Option<String>, first: String) -> Result<Expr> {
792 let mut parts = vec![first];
793 while self.eat_punct('.') {
794 if self.eat_op("*") {
796 return Ok(Expr::QualifiedStar(parts.pop().unwrap_or_default()));
797 }
798 match self.next() {
799 Tok::Word { raw, .. } => parts.push(raw),
800 Tok::Quoted(s) => parts.push(s),
801 other => bail!("expected a name after '.', got {:?}", other),
802 }
803 }
804
805 if matches!(self.peek(), Tok::Punct('(')) {
809 self.pos += 1;
810 let name = parts.pop().unwrap_or_default().to_lowercase();
811 let mut args = vec![];
812 if !self.eat_punct(')') {
813 loop {
814 if self.eat_op("*") {
816 args.push(Expr::Star);
817 } else {
818 args.push(self.parse_expr()?);
819 }
820 if self.eat_punct(',') {
821 continue;
822 }
823 self.expect_punct(')')?;
824 break;
825 }
826 }
827 return Ok(Expr::Func { name, args });
828 }
829
830 let name = parts.pop().unwrap_or_default();
831 let qual = parts.pop();
835 Ok(Expr::Column { qual, name })
836 }
837
838 fn parse_case(&mut self) -> Result<Expr> {
839 self.expect_kw("CASE")?;
840 let operand = if self.peek().is_kw("WHEN") {
842 None
843 } else {
844 Some(Box::new(self.parse_expr()?))
845 };
846 let mut whens = vec![];
847 while self.eat_kw("WHEN") {
848 let cond = self.parse_expr()?;
849 self.expect_kw("THEN")?;
850 let then = self.parse_expr()?;
851 whens.push((cond, then));
852 }
853 if whens.is_empty() {
854 bail!("CASE needs at least one WHEN branch");
855 }
856 let else_ = if self.eat_kw("ELSE") {
857 Some(Box::new(self.parse_expr()?))
858 } else {
859 None
860 };
861 self.expect_kw("END")?;
862 Ok(Expr::Case { operand, whens, else_ })
863 }
864
865 fn parse_table_ref(&mut self) -> Result<TableRef> {
868 let mut parts = vec![match self.next() {
869 Tok::Word { raw, .. } => raw,
870 Tok::Quoted(s) => s,
871 other => bail!("expected a table name, got {:?}", other),
872 }];
873 while self.eat_punct('.') {
874 match self.next() {
875 Tok::Word { raw, .. } => parts.push(raw),
876 Tok::Quoted(s) => parts.push(s),
877 other => bail!("expected a name after '.', got {:?}", other),
878 }
879 }
880 let name = parts.join(".");
881
882 let alias = if self.eat_kw("AS") {
886 match self.next() {
887 Tok::Word { raw, .. } => Some(raw),
888 Tok::Quoted(s) => Some(s),
889 other => bail!("expected an alias after AS, got {:?}", other),
890 }
891 } else {
892 match self.peek().clone() {
893 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
894 self.pos += 1;
895 Some(raw)
896 }
897 Tok::Quoted(s) => {
898 self.pos += 1;
899 Some(s)
900 }
901 _ => None,
902 }
903 };
904 Ok(TableRef { name, alias })
905 }
906
907 fn parse_select(&mut self) -> Result<Select> {
908 self.expect_kw("SELECT")?;
909 let distinct = self.eat_kw("DISTINCT");
910 if distinct && self.peek().is_kw("ON") {
911 bail!("DISTINCT ON is not supported");
912 }
913 let _ = self.eat_kw("ALL");
914
915 let mut items = vec![];
916 loop {
917 let expr = self.parse_expr()?;
918 let alias = if self.eat_kw("AS") {
920 match self.next() {
921 Tok::Word { raw, .. } => Some(raw),
922 Tok::Quoted(s) => Some(s),
923 other => bail!("expected an alias after AS, got {:?}", other),
924 }
925 } else {
926 match self.peek().clone() {
927 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
928 self.pos += 1;
929 Some(raw)
930 }
931 Tok::Quoted(s) => {
932 self.pos += 1;
933 Some(s)
934 }
935 _ => None,
936 }
937 };
938 items.push(SelectItem { expr, alias });
939 if self.eat_punct(',') {
940 continue;
941 }
942 break;
943 }
944
945 let mut from = None;
946 let mut joins = vec![];
947 if self.eat_kw("FROM") {
948 from = Some(self.parse_table_ref()?);
949 while self.eat_punct(',') {
951 let table = self.parse_table_ref()?;
952 joins.push(Join { kind: JoinKind::Cross, table, on: None });
953 }
954 loop {
955 let kind = if self.peek().is_kw("JOIN") {
956 self.pos += 1;
957 JoinKind::Inner
958 } else if self.peek().is_kw("INNER") && self.peek_at(1).is_kw("JOIN") {
959 self.pos += 2;
960 JoinKind::Inner
961 } else if self.peek().is_kw("CROSS") && self.peek_at(1).is_kw("JOIN") {
962 self.pos += 2;
963 JoinKind::Cross
964 } else if self.peek().is_kw("LEFT") {
965 self.pos += 1;
966 let _ = self.eat_kw("OUTER");
967 self.expect_kw("JOIN")?;
968 JoinKind::Left
969 } else if self.peek().is_kw("RIGHT") {
970 self.pos += 1;
971 let _ = self.eat_kw("OUTER");
972 self.expect_kw("JOIN")?;
973 JoinKind::Right
974 } else if self.peek().is_kw("FULL") {
975 self.pos += 1;
976 let _ = self.eat_kw("OUTER");
977 self.expect_kw("JOIN")?;
978 JoinKind::Full
979 } else {
980 break;
981 };
982 let table = self.parse_table_ref()?;
983 let on = if self.eat_kw("ON") {
984 Some(self.parse_expr()?)
985 } else if self.peek().is_kw("USING") {
986 bail!("JOIN ... USING is not supported — write ON a.col = b.col");
987 } else {
988 None
989 };
990 if on.is_none() && !matches!(kind, JoinKind::Cross) {
991 bail!("a {:?} JOIN needs an ON clause", kind);
992 }
993 joins.push(Join { kind, table, on });
994 }
995 }
996
997 let where_ = if self.eat_kw("WHERE") {
998 Some(self.parse_expr()?)
999 } else {
1000 None
1001 };
1002
1003 if self.peek().is_kw("GROUP") {
1004 bail!("GROUP BY is not supported by this SELECT path");
1005 }
1006 if self.peek().is_kw("HAVING") {
1007 bail!("HAVING is not supported by this SELECT path");
1008 }
1009
1010 let mut order_by = vec![];
1011 if self.eat_kw("ORDER") {
1012 self.expect_kw("BY")?;
1013 loop {
1014 let (ordinal, expr) = match self.peek().clone() {
1018 Tok::Num(n)
1019 if n.fract() == 0.0
1020 && n >= 1.0
1021 && !matches!(self.peek_at(1), Tok::Op(_)) =>
1022 {
1023 self.pos += 1;
1024 (Some(n as usize), None)
1025 }
1026 _ => (None, Some(self.parse_expr()?)),
1027 };
1028 let dir = if self.eat_kw("DESC") {
1029 Dir::Desc
1030 } else {
1031 let _ = self.eat_kw("ASC");
1032 Dir::Asc
1033 };
1034 let mut nulls_first = matches!(dir, Dir::Desc);
1036 if self.eat_kw("NULLS") {
1037 if self.eat_kw("FIRST") {
1038 nulls_first = true;
1039 } else if self.eat_kw("LAST") {
1040 nulls_first = false;
1041 } else {
1042 bail!("expected FIRST or LAST after NULLS, got {:?}", self.peek());
1043 }
1044 }
1045 order_by.push(OrderBy { ordinal, expr, dir, nulls_first });
1046 if self.eat_punct(',') {
1047 continue;
1048 }
1049 break;
1050 }
1051 }
1052
1053 let mut limit = None;
1054 let mut offset = None;
1055 loop {
1057 if self.eat_kw("LIMIT") {
1058 if self.eat_kw("ALL") {
1059 limit = None;
1060 } else {
1061 limit = Some(self.parse_count("LIMIT")?);
1062 }
1063 continue;
1064 }
1065 if self.eat_kw("OFFSET") {
1066 offset = Some(self.parse_count("OFFSET")?);
1067 let _ = self.eat_kw("ROW") || self.eat_kw("ROWS");
1068 continue;
1069 }
1070 break;
1071 }
1072
1073 let _ = self.eat_punct(';');
1074 if !matches!(self.peek(), Tok::Eof) {
1075 bail!("unexpected trailing tokens: {:?}", self.peek());
1076 }
1077
1078 Ok(Select { distinct, items, from, joins, where_, order_by, limit, offset })
1079 }
1080
1081 fn parse_count(&mut self, what: &str) -> Result<usize> {
1082 match self.next() {
1083 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Ok(n as usize),
1084 other => bail!("{} expects a non-negative integer, got {:?}", what, other),
1085 }
1086 }
1087}
1088
1089fn is_clause_keyword(upper: &str) -> bool {
1094 matches!(
1095 upper,
1096 "FROM" | "WHERE" | "GROUP" | "HAVING" | "ORDER" | "LIMIT" | "OFFSET"
1097 | "JOIN" | "LEFT" | "RIGHT" | "FULL" | "INNER" | "CROSS" | "OUTER"
1098 | "ON" | "USING" | "AND" | "OR" | "AS" | "UNION" | "INTERSECT"
1099 | "EXCEPT" | "FETCH" | "FOR" | "WINDOW" | "RETURNING" | "INTO"
1100 | "ASC" | "DESC" | "NULLS" | "IS" | "IN" | "NOT" | "LIKE" | "ILIKE"
1101 | "BETWEEN" | "THEN" | "WHEN" | "ELSE" | "END" | "CASE" | "DISTINCT"
1102 | "SELECT" | "WITH" | "ALL"
1103 )
1104}
1105
1106pub fn parse(sql: &str) -> Result<Select> {
1108 let toks = lex(sql)?;
1109 let mut p = Parser { toks, pos: 0 };
1110 p.parse_select()
1111}
1112
1113pub struct Bound<'a> {
1126 pub parts: Vec<(String, Option<&'a Value>)>,
1127}
1128
1129impl<'a> Bound<'a> {
1130 fn column(&self, qual: Option<&str>, name: &str) -> Value {
1136 match qual {
1137 Some(q) => {
1138 for (binding, row) in &self.parts {
1139 if binding.eq_ignore_ascii_case(q) {
1140 return row
1141 .and_then(|r| r.get(name))
1142 .cloned()
1143 .unwrap_or(Value::Null);
1144 }
1145 }
1146 Value::Null
1147 }
1148 None => {
1149 for (_, row) in &self.parts {
1150 if let Some(v) = row.and_then(|r| r.get(name)) {
1151 return v.clone();
1152 }
1153 }
1154 Value::Null
1155 }
1156 }
1157 }
1158
1159 fn has_binding(&self, qual: &str) -> bool {
1163 self.parts.iter().any(|(b, _)| b.eq_ignore_ascii_case(qual))
1164 }
1165
1166 fn flatten(&self) -> Vec<(String, Value)> {
1168 let mut out = vec![];
1169 for (_, row) in &self.parts {
1170 if let Some(Value::Object(m)) = row {
1171 for (k, v) in m {
1172 out.push((k.clone(), v.clone()));
1173 }
1174 }
1175 }
1176 out
1177 }
1178
1179 fn flatten_binding(&self, qual: &str) -> Vec<(String, Value)> {
1180 let mut out = vec![];
1181 for (binding, row) in &self.parts {
1182 if binding.eq_ignore_ascii_case(qual) {
1183 if let Some(Value::Object(m)) = row {
1184 for (k, v) in m {
1185 out.push((k.clone(), v.clone()));
1186 }
1187 }
1188 }
1189 }
1190 out
1191 }
1192}
1193
1194type Truth = Option<bool>;
1202
1203fn truthy(v: &Value) -> Truth {
1204 match v {
1205 Value::Null => None,
1206 Value::Bool(b) => Some(*b),
1207 _ => None,
1211 }
1212}
1213
1214fn cmp_values(a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
1220 use std::cmp::Ordering;
1221 match (a, b) {
1222 (Value::Null, _) | (_, Value::Null) => None,
1223 (Value::Number(x), Value::Number(y)) => {
1224 x.as_f64().partial_cmp(&y.as_f64())
1225 }
1226 (Value::String(x), Value::String(y)) => Some(x.cmp(y)),
1227 (Value::Bool(x), Value::Bool(y)) => Some(x.cmp(y)),
1228 (Value::Number(x), Value::String(y)) => match y.parse::<f64>() {
1231 Ok(n) => x.as_f64().partial_cmp(&Some(n)),
1232 Err(_) => Some(as_text(a).cmp(&as_text(b))),
1233 },
1234 (Value::String(x), Value::Number(y)) => match x.parse::<f64>() {
1235 Ok(n) => Some(n).partial_cmp(&y.as_f64()),
1236 Err(_) => Some(as_text(a).cmp(&as_text(b))),
1237 },
1238 _ => {
1239 let (x, y) = (as_text(a), as_text(b));
1240 if x == y { Some(Ordering::Equal) } else { Some(x.cmp(&y)) }
1241 }
1242 }
1243}
1244
1245fn as_text(v: &Value) -> String {
1247 match v {
1248 Value::String(s) => s.clone(),
1249 Value::Null => String::new(),
1250 Value::Bool(b) => (if *b { "t" } else { "f" }).to_string(),
1251 other => other.to_string(),
1252 }
1253}
1254
1255fn num(v: &Value) -> Option<f64> {
1256 match v {
1257 Value::Number(n) => n.as_f64(),
1258 Value::String(s) => s.parse().ok(),
1259 Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
1260 _ => None,
1261 }
1262}
1263
1264fn from_f64(f: f64) -> Value {
1278 if f.is_finite() && f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
1279 return Value::Number((f as i64).into());
1280 }
1281 serde_json::Number::from_f64(f).map(Value::Number).unwrap_or(Value::Null)
1282}
1283
1284pub fn eval(e: &Expr, row: &Bound) -> Result<Value> {
1286 Ok(match e {
1287 Expr::Literal(v) => v.clone(),
1288
1289 Expr::Column { qual, name } => {
1290 if let Some(q) = qual {
1294 if !row.has_binding(q) {
1295 bail!("no table or alias named {:?} in this query", q);
1296 }
1297 }
1298 row.column(qual.as_deref(), name)
1299 }
1300
1301 Expr::Cast { expr, .. } => eval(expr, row)?,
1302
1303 Expr::Star | Expr::QualifiedStar(_) => {
1304 bail!("`*` is only valid in a select list or as count(*)")
1305 }
1306
1307 Expr::Unary { op, expr } => {
1308 let v = eval(expr, row)?;
1309 match op.as_str() {
1310 "NOT" => match truthy(&v) {
1311 None => Value::Null,
1313 Some(b) => Value::Bool(!b),
1314 },
1315 "-" => match num(&v) {
1316 Some(n) => from_f64(-n),
1317 None => Value::Null,
1318 },
1319 other => bail!("unsupported unary operator {:?}", other),
1320 }
1321 }
1322
1323 Expr::Binary { op, left, right } => {
1324 if op == "AND" {
1329 let l = truthy(&eval(left, row)?);
1330 if l == Some(false) {
1331 return Ok(Value::Bool(false));
1332 }
1333 let r = truthy(&eval(right, row)?);
1334 return Ok(match (l, r) {
1335 (_, Some(false)) => Value::Bool(false),
1336 (Some(true), Some(true)) => Value::Bool(true),
1337 _ => Value::Null,
1338 });
1339 }
1340 if op == "OR" {
1341 let l = truthy(&eval(left, row)?);
1342 if l == Some(true) {
1343 return Ok(Value::Bool(true));
1344 }
1345 let r = truthy(&eval(right, row)?);
1346 return Ok(match (l, r) {
1347 (_, Some(true)) => Value::Bool(true),
1348 (Some(false), Some(false)) => Value::Bool(false),
1349 _ => Value::Null,
1350 });
1351 }
1352
1353 let l = eval(left, row)?;
1354 let r = eval(right, row)?;
1355
1356 let compare = |ord: fn(std::cmp::Ordering) -> bool| -> Value {
1358 match cmp_values(&l, &r) {
1359 None => Value::Null,
1360 Some(o) => Value::Bool(ord(o)),
1361 }
1362 };
1363
1364 match op.as_str() {
1365 "=" => compare(|o| o.is_eq()),
1366 "!=" | "<>" => compare(|o| o.is_ne()),
1367 "<" => compare(|o| o.is_lt()),
1368 "<=" => compare(|o| o.is_le()),
1369 ">" => compare(|o| o.is_gt()),
1370 ">=" => compare(|o| o.is_ge()),
1371
1372 "~" | "~*" | "!~" | "!~*" => {
1373 if l.is_null() || r.is_null() {
1374 Value::Null
1375 } else {
1376 let pat = as_text(&r);
1377 if let Some(bad) = crate::nql::unsupported_regex_char_pub(&pat) {
1378 bail!(
1379 "regex {:?} uses {:?}, which this engine does not \
1380 implement. The supported subset is ^ $ . and \
1381 literal text",
1382 pat, bad
1383 );
1384 }
1385 let hit = crate::nql::regex_match_pub(
1386 &as_text(&l), &pat, op.ends_with('*'));
1387 Value::Bool(hit != op.starts_with('!'))
1388 }
1389 }
1390
1391 "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => {
1392 if l.is_null() || r.is_null() {
1393 Value::Null
1394 } else {
1395 let hit = crate::nql::like_match_pub(
1396 &as_text(&l), &as_text(&r), op.ends_with("ILIKE"));
1397 Value::Bool(hit != op.starts_with("NOT"))
1398 }
1399 }
1400
1401 "||" => {
1403 if l.is_null() || r.is_null() {
1404 Value::Null
1405 } else {
1406 Value::String(format!("{}{}", as_text(&l), as_text(&r)))
1407 }
1408 }
1409
1410 "+" | "-" | "*" | "/" | "%" => match (num(&l), num(&r)) {
1411 (Some(a), Some(b)) => match op.as_str() {
1412 "+" => from_f64(a + b),
1413 "-" => from_f64(a - b),
1414 "*" => from_f64(a * b),
1415 "/" if b == 0.0 => bail!("division by zero"),
1418 "/" => from_f64(a / b),
1419 "%" if b == 0.0 => bail!("division by zero"),
1420 "%" => from_f64(a % b),
1421 _ => unreachable!(),
1422 },
1423 _ => Value::Null,
1424 },
1425
1426 other => bail!("unsupported operator {:?}", other),
1427 }
1428 }
1429
1430 Expr::IsNull { expr, negated } => {
1431 let v = eval(expr, row)?;
1432 Value::Bool(v.is_null() != *negated)
1435 }
1436
1437 Expr::InList { expr, list, negated } => {
1438 let v = eval(expr, row)?;
1439 if v.is_null() {
1440 return Ok(Value::Null);
1441 }
1442 let mut any_null = false;
1443 let mut found = false;
1444 for item in list {
1445 let iv = eval(item, row)?;
1446 if iv.is_null() {
1447 any_null = true;
1448 continue;
1449 }
1450 if matches!(cmp_values(&v, &iv), Some(std::cmp::Ordering::Equal)) {
1451 found = true;
1452 break;
1453 }
1454 }
1455 if found {
1459 Value::Bool(!*negated)
1460 } else if any_null {
1461 Value::Null
1462 } else {
1463 Value::Bool(*negated)
1464 }
1465 }
1466
1467 Expr::Case { operand, whens, else_ } => {
1468 let subject = match operand {
1469 Some(o) => Some(eval(o, row)?),
1470 None => None,
1471 };
1472 for (cond, then) in whens {
1473 let hit = match &subject {
1474 Some(sv) => {
1476 let cv = eval(cond, row)?;
1477 matches!(cmp_values(sv, &cv), Some(std::cmp::Ordering::Equal))
1478 }
1479 None => truthy(&eval(cond, row)?) == Some(true),
1482 };
1483 if hit {
1484 return eval(then, row);
1485 }
1486 }
1487 match else_ {
1488 Some(e) => eval(e, row)?,
1489 None => Value::Null,
1492 }
1493 }
1494
1495 Expr::Func { name, args } => eval_func(name, args, row)?,
1496 })
1497}
1498
1499fn eval_func(name: &str, args: &[Expr], row: &Bound) -> Result<Value> {
1505 let arg = |i: usize| -> Result<Value> {
1508 match args.get(i) {
1509 Some(e) => eval(e, row),
1510 None => Ok(Value::Null),
1511 }
1512 };
1513
1514 Ok(match name {
1515 "pg_get_userbyid" | "current_user" | "session_user" | "user" => {
1519 Value::String("nedb".into())
1520 }
1521 "current_schema" => Value::String("public".into()),
1522 "current_database" | "current_catalog" => Value::String("nedb".into()),
1523 "version" => Value::String(crate::pgwire::version_string()),
1524
1525 "pg_table_is_visible" | "pg_type_is_visible" | "pg_function_is_visible"
1530 | "pg_opclass_is_visible" | "pg_conversion_is_visible" => Value::Bool(true),
1531
1532 "pg_encoding_to_char" => Value::String("UTF8".into()),
1534 "pg_get_expr" | "pg_get_indexdef" | "pg_get_constraintdef"
1535 | "pg_get_viewdef" | "pg_get_partkeydef" | "obj_description"
1536 | "col_description" | "shobj_description" => Value::Null,
1537
1538 "lower" => match arg(0)? {
1540 Value::Null => Value::Null,
1541 v => Value::String(as_text(&v).to_lowercase()),
1542 },
1543 "upper" => match arg(0)? {
1544 Value::Null => Value::Null,
1545 v => Value::String(as_text(&v).to_uppercase()),
1546 },
1547 "length" | "char_length" | "character_length" => match arg(0)? {
1548 Value::Null => Value::Null,
1549 v => from_f64(as_text(&v).chars().count() as f64),
1550 },
1551 "format_type" => match arg(0)? {
1552 Value::Null => Value::Null,
1553 v => Value::String(crate::pgcatalog::type_name_pub(
1554 num(&v).unwrap_or(25.0) as i32).to_string()),
1555 },
1556 "array_to_string" | "pg_catalog.array_to_string" => {
1557 match arg(0)? {
1561 Value::Array(items) => {
1562 let sep = as_text(&arg(1)?);
1563 Value::String(
1564 items.iter().map(as_text).collect::<Vec<_>>().join(&sep),
1565 )
1566 }
1567 _ => Value::Null,
1568 }
1569 }
1570 "quote_ident" => Value::String(as_text(&arg(0)?)),
1571
1572 "coalesce" => {
1574 let mut out = Value::Null;
1575 for a in args {
1576 let v = eval(a, row)?;
1577 if !v.is_null() {
1578 out = v;
1579 break;
1580 }
1581 }
1582 out
1583 }
1584 "nullif" => {
1585 let a = arg(0)?;
1586 let b = arg(1)?;
1587 if matches!(cmp_values(&a, &b), Some(std::cmp::Ordering::Equal)) {
1588 Value::Null
1589 } else {
1590 a
1591 }
1592 }
1593
1594 "int4" | "int8" | "int2" => match num(&arg(0)?) {
1596 Some(n) => from_f64(n.trunc()),
1597 None => Value::Null,
1598 },
1599 "text" => match arg(0)? {
1600 Value::Null => Value::Null,
1601 v => Value::String(as_text(&v)),
1602 },
1603
1604 other => bail!(
1605 "the function {}() is not implemented. It is refused rather than \
1606 answered with NULL, because a NULL column reads as missing DATA \
1607 rather than a missing feature",
1608 other
1609 ),
1610 })
1611}
1612
1613fn validate_bindings(sel: &Select) -> Result<()> {
1629 let mut seen: Vec<String> = vec![];
1630 if let Some(f) = &sel.from {
1631 seen.push(f.binding());
1632 }
1633 for j in &sel.joins {
1634 seen.push(j.table.binding());
1635 }
1636 for (i, b) in seen.iter().enumerate() {
1637 if let Some(prev) = seen[..i].iter().find(|p| p.eq_ignore_ascii_case(b)) {
1638 bail!(
1639 "ambiguous relation binding: {:?} appears more than once; use \
1640 aliases (for example `FROM {} JOIN {} AS {}2 ...`)",
1641 prev, prev, prev, prev
1642 );
1643 }
1644 }
1645 Ok(())
1646}
1647
1648#[derive(Debug, Clone, PartialEq, Eq)]
1660pub struct OutCol {
1661 pub key: String,
1662 pub name: String,
1663}
1664
1665fn unique_key(taken: &[OutCol], name: &str) -> String {
1669 if !taken.iter().any(|c| c.key == name) {
1670 return name.to_string();
1671 }
1672 format!("{name}\u{1}{}", taken.len())
1673}
1674
1675type JoinedRow = Vec<(String, Option<Value>)>;
1677
1678fn bind<'a>(row: &'a JoinedRow) -> Bound<'a> {
1679 Bound {
1680 parts: row.iter().map(|(b, v)| (b.clone(), v.as_ref())).collect(),
1681 }
1682}
1683
1684fn derived_name(e: &Expr) -> String {
1692 match e {
1693 Expr::Column { name, .. } => name.clone(),
1694 Expr::Func { name, .. } => name.clone(),
1695 Expr::Cast { expr, .. } => derived_name(expr),
1696 Expr::Case { .. } => "case".to_string(),
1697 _ => "?column?".to_string(),
1698 }
1699}
1700
1701pub trait Relation {
1722 fn next_row(&mut self) -> Result<Option<Value>>;
1724
1725 fn size_hint(&self) -> Option<usize> {
1727 None
1728 }
1729}
1730
1731pub struct VecRelation {
1737 iter: std::vec::IntoIter<Value>,
1738 len: usize,
1739}
1740
1741impl Relation for VecRelation {
1742 fn next_row(&mut self) -> Result<Option<Value>> {
1743 Ok(self.iter.next())
1744 }
1745 fn size_hint(&self) -> Option<usize> {
1746 Some(self.len)
1747 }
1748}
1749
1750pub fn from_vec(rows: Vec<Value>) -> Box<dyn Relation> {
1752 let len = rows.len();
1753 Box::new(VecRelation { iter: rows.into_iter(), len })
1754}
1755
1756pub type Resolver<'r> = dyn Fn(&str) -> Result<Option<Box<dyn Relation>>> + 'r;
1762
1763pub fn execute(sel: &Select, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
1768 let (cols, rows, _) = execute_explain(sel, resolve, JoinExec::Auto)?;
1769 Ok((cols, rows))
1770}
1771
1772pub fn execute_explain(
1779 sel: &Select,
1780 resolve: &Resolver,
1781 exec: JoinExec,
1782) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
1783 execute_with(sel, resolve, exec, true)
1784}
1785
1786#[derive(Debug, Clone, Copy)]
1791pub struct Opts {
1792 pub exec: JoinExec,
1793 pub pushdown: bool,
1794 pub fuse_filter: bool,
1798}
1799
1800impl Default for Opts {
1801 fn default() -> Self {
1802 Opts { exec: JoinExec::Auto, pushdown: true, fuse_filter: true }
1803 }
1804}
1805
1806impl Opts {
1807 pub fn exec(exec: JoinExec) -> Self {
1808 Opts { exec, ..Default::default() }
1809 }
1810}
1811
1812pub fn execute_with(
1819 sel: &Select,
1820 resolve: &Resolver,
1821 exec: JoinExec,
1822 pushdown: bool,
1823) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
1824 execute_opts(sel, resolve, Opts { exec, pushdown, ..Default::default() })
1825}
1826
1827pub fn execute_opts(
1829 sel: &Select,
1830 resolve: &Resolver,
1831 opts: Opts,
1832) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
1833 let exec = opts.exec;
1834 let pushdown = opts.pushdown;
1835 let mut plan = Plan::default();
1836
1837 validate_bindings(sel)?;
1839
1840 let fuse = opts.fuse_filter && sel.where_.is_some() && !sel.joins.is_empty();
1867
1868 let budget: Option<usize> = match sel.limit {
1869 Some(lim)
1870 if sel.order_by.is_empty()
1871 && !sel.distinct
1872 && !sel.joins.is_empty()
1873 && (sel.where_.is_none() || fuse) =>
1874 {
1875 Some(lim.saturating_add(sel.offset.unwrap_or(0)))
1876 }
1877 _ => None,
1878 };
1879 plan.budget = budget;
1880
1881 let all_bindings: Vec<String> = sel
1887 .from
1888 .iter()
1889 .map(|t| t.binding())
1890 .chain(sel.joins.iter().map(|j| j.table.binding()))
1891 .collect();
1892 let nullable = crate::sqlpush::nullable_bindings(sel);
1893 let push = if pushdown {
1894 crate::sqlpush::plan(sel.where_.as_ref(), &all_bindings, &nullable)
1895 } else {
1896 Pushdown::default()
1897 };
1898 plan.refusals = push.refusals.clone();
1899
1900 let mut base_scan_at: Option<usize> = None;
1901 let mut base_prefilter_at: Option<usize> = None;
1902
1903 let mut left_src: Box<dyn LeftSource> = match &sel.from {
1911 None => {
1912 Box::new(VecLeft { rows: vec![vec![]], at: 0 })
1915 }
1916 Some(t) => {
1917 let rel = fetch(&t.name, resolve)?;
1918 let binding = t.binding();
1919 base_scan_at = Some(plan.stages.len());
1924 plan.push(Stage::Scan {
1925 table: t.name.clone(),
1926 binding: binding.clone(),
1927 rows: 0,
1928 });
1929 let preds = push.for_binding(&binding).cloned().unwrap_or_default();
1930 if !preds.is_empty() {
1931 base_prefilter_at = Some(plan.stages.len());
1932 plan.push(Stage::Prefilter {
1933 binding: binding.clone(),
1934 predicates: preds.len(),
1935 in_rows: 0,
1936 out_rows: 0,
1937 });
1938 }
1939 Box::new(StreamLeft { rel, binding, preds, pulled: 0, kept: 0 })
1940 }
1941 };
1942
1943 let mut left_bindings: Vec<String> = match &sel.from {
1949 None => vec![],
1950 Some(t) => vec![t.binding()],
1951 };
1952 let last = sel.joins.len().saturating_sub(1);
1953 let mut rows: Vec<JoinedRow> = vec![];
1954 let mut base_pulled: Option<usize> = None;
1955 let mut base_kept: Option<usize> = None;
1956
1957 for (ji, join) in sel.joins.iter().enumerate() {
1958 let right_rel = fetch(&join.table.name, resolve)?;
1959 let rb = join.table.binding();
1960 let right_all = drain(right_rel)?;
1961 plan.push(Stage::Scan {
1962 table: join.table.name.clone(),
1963 binding: rb.clone(),
1964 rows: right_all.len(),
1965 });
1966 let right_rows = prefilter(right_all, &rb, &push, &mut plan)?;
1967
1968 let is_last = ji == last;
1973 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
1974 let join_budget = if is_last { budget } else { None };
1975
1976 let keys = sqljoin::hash_keys(join.on.as_ref(), &left_bindings, &rb);
1979 let left_hint = left_src.hint().unwrap_or(usize::MAX);
1980 let strategy = sqljoin::choose(exec, keys.len(), left_hint, right_rows.len());
1981
1982 let (out, removed, consumed) = match strategy {
1983 Strategy::NestedLoop => join_nested_loop(
1984 left_src.as_mut(), &left_bindings, join, &right_rows, &rb,
1985 join_budget, post,
1986 )?,
1987 Strategy::Hash => join_hash(
1988 left_src.as_mut(), &left_bindings, join, &right_rows, &rb, &keys,
1989 join_budget, post,
1990 )?,
1991 };
1992
1993 plan.push(Stage::Join {
1994 kind: join.kind,
1995 table: join.table.name.clone(),
1996 binding: rb.clone(),
1997 strategy,
1998 keys: keys.len(),
1999 left_rows: consumed,
2000 right_rows: right_rows.len(),
2001 out_rows: out.len(),
2002 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
2003 post_filter_removed: post.map(|_| removed),
2004 });
2005 left_bindings.push(rb);
2006 if ji == 0 {
2008 if let Some((pulled, kept)) = left_src.stats() {
2009 base_pulled = Some(pulled);
2010 base_kept = Some(kept);
2011 }
2012 }
2013 rows = out;
2014 left_src = Box::new(VecLeft { rows: std::mem::take(&mut rows), at: 0 });
2016 }
2017
2018 rows = left_src.take_rows();
2021 if let Some(i) = base_scan_at {
2022 if let (Some(pulled), Some(kept)) = (base_pulled, base_kept) {
2023 if let Some(Stage::Scan { rows: r, .. }) = plan.stages.get_mut(i) {
2024 *r = pulled;
2025 }
2026 if let Some(j) = base_prefilter_at {
2027 if let Some(Stage::Prefilter { in_rows, out_rows, .. }) =
2028 plan.stages.get_mut(j)
2029 {
2030 *in_rows = pulled;
2031 *out_rows = kept;
2032 }
2033 }
2034 }
2035 }
2036
2037 if let Some(pred) = sel.where_.as_ref().filter(|_| !fuse) {
2039 let in_rows = rows.len();
2040 let mut kept = Vec::with_capacity(rows.len());
2041 for r in rows {
2042 if truthy(&eval(pred, &bind(&r))?) == Some(true) {
2046 kept.push(r);
2047 }
2048 }
2049 rows = kept;
2050 plan.push(Stage::Filter { in_rows, out_rows: rows.len() });
2051 }
2052
2053 let mut cols: Vec<OutCol> = vec![];
2064 let mut spans: Vec<(usize, usize)> = Vec::with_capacity(sel.items.len());
2065 for item in &sel.items {
2066 let start = cols.len();
2067 match &item.expr {
2068 Expr::Star => {
2069 if let Some(first) = rows.first() {
2070 for (n, _) in bind(first).flatten() {
2071 if !cols.iter().any(|c| c.name == n) {
2073 cols.push(OutCol { key: n.clone(), name: n });
2074 }
2075 }
2076 }
2077 }
2078 Expr::QualifiedStar(q) => {
2079 if let Some(first) = rows.first() {
2080 for (n, _) in bind(first).flatten_binding(q) {
2081 if !cols.iter().any(|c| c.name == n) {
2082 cols.push(OutCol { key: n.clone(), name: n });
2083 }
2084 }
2085 }
2086 }
2087 _ => {
2088 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
2089 let key = unique_key(&cols, &name);
2095 cols.push(OutCol { key, name });
2096 }
2097 }
2098 spans.push((start, cols.len()));
2099 }
2100
2101 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = Vec::with_capacity(rows.len());
2105 for r in rows {
2106 let b = bind(&r);
2107 let mut obj = Map::new();
2108 for (i, item) in sel.items.iter().enumerate() {
2109 let (start, end) = spans[i];
2110 match &item.expr {
2111 Expr::Star => {
2112 for (n, v) in b.flatten() {
2113 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
2114 obj.entry(c.key.clone()).or_insert(v);
2115 }
2116 }
2117 }
2118 Expr::QualifiedStar(q) => {
2119 for (n, v) in b.flatten_binding(q) {
2120 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
2121 obj.entry(c.key.clone()).or_insert(v);
2122 }
2123 }
2124 }
2125 _ => {
2126 let v = eval(&item.expr, &b)?;
2127 if let Some(c) = cols.get(start) {
2128 obj.insert(c.key.clone(), v);
2129 }
2130 }
2131 }
2132 }
2133 projected.push((obj, r));
2134 }
2135
2136 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
2137
2138 if sel.distinct {
2140 let in_rows = projected.len();
2141 let mut seen: Vec<String> = vec![];
2142 let mut kept = vec![];
2143 for (obj, src) in projected {
2144 let key = cols
2147 .iter()
2148 .map(|c| format!("{:?}", obj.get(&c.key).unwrap_or(&Value::Null)))
2149 .collect::<Vec<_>>()
2150 .join("\u{1}");
2151 if !seen.contains(&key) {
2152 seen.push(key);
2153 kept.push((obj, src));
2154 }
2155 }
2156 projected = kept;
2157 plan.push(Stage::Distinct { in_rows, out_rows: projected.len() });
2158 }
2159
2160 if !sel.order_by.is_empty() {
2162 let mut keyed: Vec<(Vec<Value>, (Map<String, Value>, JoinedRow))> = vec![];
2166 for (obj, src) in projected {
2167 let mut key = vec![];
2168 for ob in &sel.order_by {
2169 let v = match (ob.ordinal, &ob.expr) {
2170 (Some(n), _) => {
2171 let c = cols.get(n - 1).ok_or_else(|| {
2172 anyhow::anyhow!(
2173 "ORDER BY {} is out of range: the select list has {} \
2174 column(s)", n, cols.len())
2175 })?;
2176 obj.get(&c.key).cloned().unwrap_or(Value::Null)
2177 }
2178 (None, Some(e)) => {
2179 eval(e, &bind(&src))?
2183 }
2184 (None, None) => Value::Null,
2185 };
2186 key.push(v);
2187 }
2188 keyed.push((key, (obj, src)));
2189 }
2190
2191 keyed.sort_by(|a, b| {
2192 for (i, ob) in sel.order_by.iter().enumerate() {
2193 let (x, y) = (&a.0[i], &b.0[i]);
2194 let ord = match (x.is_null(), y.is_null()) {
2195 (true, true) => std::cmp::Ordering::Equal,
2196 (true, false) => {
2200 return if ob.nulls_first {
2201 std::cmp::Ordering::Less
2202 } else {
2203 std::cmp::Ordering::Greater
2204 }
2205 }
2206 (false, true) => {
2207 return if ob.nulls_first {
2208 std::cmp::Ordering::Greater
2209 } else {
2210 std::cmp::Ordering::Less
2211 }
2212 }
2213 (false, false) => cmp_values(x, y).unwrap_or(std::cmp::Ordering::Equal),
2214 };
2215 let ord = if matches!(ob.dir, Dir::Desc) { ord.reverse() } else { ord };
2216 if !ord.is_eq() {
2217 return ord;
2218 }
2219 }
2220 std::cmp::Ordering::Equal
2221 });
2222
2223 projected = keyed.into_iter().map(|(_, row)| row).collect();
2224 plan.push(Stage::Sort { keys: sel.order_by.len(), rows: projected.len() });
2225 }
2226
2227 let mut out: Vec<Value> = projected
2229 .into_iter()
2230 .map(|(obj, _)| Value::Object(obj))
2231 .collect();
2232 let in_rows = out.len();
2233 if let Some(off) = sel.offset {
2234 out = if off >= out.len() { vec![] } else { out.split_off(off) };
2235 }
2236 if let Some(lim) = sel.limit {
2237 out.truncate(lim);
2238 }
2239 if sel.limit.is_some() || sel.offset.is_some() {
2240 plan.push(Stage::Limit {
2241 limit: sel.limit,
2242 offset: sel.offset,
2243 in_rows,
2244 out_rows: out.len(),
2245 });
2246 }
2247
2248 Ok((cols, out, plan))
2249}
2250
2251fn keep_row(cand: &JoinedRow, post: Option<&Expr>, removed: &mut usize) -> Result<bool> {
2284 let Some(p) = post else { return Ok(true) };
2285 if truthy(&eval(p, &bind(cand))?) == Some(true) {
2287 Ok(true)
2288 } else {
2289 *removed += 1;
2290 Ok(false)
2291 }
2292}
2293
2294fn emit_unmatched_right(
2300 out: &mut Vec<JoinedRow>,
2301 kind: JoinKind,
2302 left_bindings: &[String],
2303 right_rows: &[Value],
2304 right_matched: &[bool],
2305 rb: &str,
2306 post: Option<&Expr>,
2307 removed: &mut usize,
2308) -> Result<()> {
2309 if !matches!(kind, JoinKind::Right | JoinKind::Full) {
2310 return Ok(());
2311 }
2312 for (ri, right) in right_rows.iter().enumerate() {
2313 if right_matched[ri] {
2314 continue;
2315 }
2316 let mut cand: JoinedRow = left_bindings.iter().map(|b| (b.clone(), None)).collect();
2317 cand.push((rb.to_string(), Some(right.clone())));
2318 if keep_row(&cand, post, removed)? {
2321 out.push(cand);
2322 }
2323 }
2324 Ok(())
2325}
2326
2327fn join_nested_loop(
2333 left_src: &mut dyn LeftSource,
2334 left_bindings: &[String],
2335 join: &Join,
2336 right_rows: &[Value],
2337 rb: &str,
2338 budget: Option<usize>,
2339 post: Option<&Expr>,
2340) -> Result<(Vec<JoinedRow>, usize, usize)> {
2341 let mut out: Vec<JoinedRow> = vec![];
2342 let mut removed = 0usize;
2343 let mut right_matched = vec![false; right_rows.len()];
2345
2346 let mut consumed = 0usize;
2347 while let Some(left) = {
2348 if budget.is_some_and(|b| out.len() >= b) {
2349 None
2352 } else {
2353 left_src.next_left()?
2354 }
2355 } {
2356 consumed += 1;
2357 let left = &left;
2358 let mut matched = false;
2361 for (ri, right) in right_rows.iter().enumerate() {
2362 let mut cand: JoinedRow = left.clone();
2363 cand.push((rb.to_string(), Some(right.clone())));
2364 let joins_here = match &join.on {
2365 None => true,
2367 Some(on) => truthy(&eval(on, &bind(&cand))?) == Some(true),
2371 };
2372 if joins_here {
2373 matched = true;
2374 right_matched[ri] = true;
2375 if keep_row(&cand, post, &mut removed)? {
2376 out.push(cand);
2377 }
2378 }
2379 }
2380 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
2382 let mut cand: JoinedRow = left.clone();
2383 cand.push((rb.to_string(), None));
2384 if keep_row(&cand, post, &mut removed)? {
2385 out.push(cand);
2386 }
2387 }
2388 }
2389
2390 if !budget.is_some_and(|b| out.len() >= b) {
2395 emit_unmatched_right(
2396 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
2397 &mut removed,
2398 )?;
2399 }
2400 Ok((out, removed, consumed))
2401}
2402
2403fn join_hash(
2411 left_src: &mut dyn LeftSource,
2412 left_bindings: &[String],
2413 join: &Join,
2414 right_rows: &[Value],
2415 rb: &str,
2416 keys: &[(Expr, Expr)],
2417 budget: Option<usize>,
2418 post: Option<&Expr>,
2419) -> Result<(Vec<JoinedRow>, usize, usize)> {
2420 debug_assert!(!keys.is_empty(), "the planner must not choose Hash with no keys");
2421
2422 let side = sqljoin::HashSide::build(right_rows.len(), |i| {
2424 let one: JoinedRow = vec![(rb.to_string(), Some(right_rows[i].clone()))];
2427 let b = bind(&one);
2428 let mut k = Vec::with_capacity(keys.len());
2429 for (_, right_expr) in keys {
2430 match sqljoin::hkey(&eval(right_expr, &b)?) {
2431 Some(h) => k.push(h),
2432 None => return Ok(None),
2434 }
2435 }
2436 Ok(Some(k))
2437 })?;
2438
2439 let mut out: Vec<JoinedRow> = vec![];
2441 let mut removed = 0usize;
2442 let mut right_matched = vec![false; right_rows.len()];
2443
2444 let mut consumed = 0usize;
2445 while let Some(left) = {
2446 if budget.is_some_and(|b| out.len() >= b) {
2447 None
2448 } else {
2449 left_src.next_left()?
2450 }
2451 } {
2452 consumed += 1;
2453 let left = &left;
2454 let lb = bind(left);
2455 let mut lk = Vec::with_capacity(keys.len());
2456 let mut null_key = false;
2457 for (left_expr, _) in keys {
2458 match sqljoin::hkey(&eval(left_expr, &lb)?) {
2459 Some(h) => lk.push(h),
2460 None => {
2461 null_key = true;
2462 break;
2463 }
2464 }
2465 }
2466
2467 let mut matched = false;
2468 if !null_key {
2472 for &ri in side.probe(&lk) {
2473 let mut cand: JoinedRow = left.clone();
2474 cand.push((rb.to_string(), Some(right_rows[ri].clone())));
2475 let joins_here = match &join.on {
2477 None => true,
2478 Some(on) => truthy(&eval(on, &bind(&cand))?) == Some(true),
2479 };
2480 if joins_here {
2481 matched = true;
2482 right_matched[ri] = true;
2483 if keep_row(&cand, post, &mut removed)? {
2484 out.push(cand);
2485 }
2486 }
2487 }
2488 }
2489 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
2490 let mut cand: JoinedRow = left.clone();
2491 cand.push((rb.to_string(), None));
2492 if keep_row(&cand, post, &mut removed)? {
2493 out.push(cand);
2494 }
2495 }
2496 }
2497
2498 if !budget.is_some_and(|b| out.len() >= b) {
2501 emit_unmatched_right(
2502 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
2503 &mut removed,
2504 )?;
2505 }
2506 Ok((out, removed, consumed))
2507}
2508
2509fn prefilter(
2516 rows: Vec<Value>,
2517 binding: &str,
2518 push: &Pushdown,
2519 plan: &mut Plan,
2520) -> Result<Vec<Value>> {
2521 let Some(preds) = push.for_binding(binding) else { return Ok(rows) };
2522 if preds.is_empty() {
2523 return Ok(rows);
2524 }
2525 let in_rows = rows.len();
2526 let mut kept = Vec::with_capacity(rows.len());
2527 for row in rows {
2528 let one: JoinedRow = vec![(binding.to_string(), Some(row))];
2529 let b = bind(&one);
2530 let mut keep = true;
2531 for p in preds {
2532 if truthy(&eval(p, &b)?) != Some(true) {
2538 keep = false;
2539 break;
2540 }
2541 }
2542 if keep {
2543 if let Some((_, Some(v))) = one.into_iter().next() {
2545 kept.push(v);
2546 }
2547 }
2548 }
2549 plan.push(Stage::Prefilter {
2550 binding: binding.to_string(),
2551 predicates: preds.len(),
2552 in_rows,
2553 out_rows: kept.len(),
2554 });
2555 Ok(kept)
2556}
2557
2558fn fetch(name: &str, resolve: &Resolver) -> Result<Box<dyn Relation>> {
2559 match resolve(name)? {
2560 Some(rel) => Ok(rel),
2561 None => bail!("relation {:?} does not exist", name),
2564 }
2565}
2566
2567trait LeftSource {
2574 fn next_left(&mut self) -> Result<Option<JoinedRow>>;
2575 fn hint(&self) -> Option<usize>;
2577 fn take_rows(&mut self) -> Vec<JoinedRow>;
2579 fn stats(&self) -> Option<(usize, usize)> {
2581 None
2582 }
2583}
2584
2585struct StreamLeft {
2591 rel: Box<dyn Relation>,
2592 binding: String,
2593 preds: Vec<Expr>,
2594 pulled: usize,
2598 kept: usize,
2599}
2600
2601impl LeftSource for StreamLeft {
2602 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
2603 while let Some(row) = self.rel.next_row()? {
2604 self.pulled += 1;
2605 let one: JoinedRow = vec![(self.binding.clone(), Some(row))];
2606 if !self.preds.is_empty() {
2607 let b = bind(&one);
2608 let mut keep = true;
2609 for p in &self.preds {
2610 if truthy(&eval(p, &b)?) != Some(true) {
2611 keep = false;
2612 break;
2613 }
2614 }
2615 if !keep {
2616 continue;
2617 }
2618 }
2619 self.kept += 1;
2620 return Ok(Some(one));
2621 }
2622 Ok(None)
2623 }
2624 fn hint(&self) -> Option<usize> {
2625 self.rel.size_hint()
2630 }
2631 fn take_rows(&mut self) -> Vec<JoinedRow> {
2632 let mut out = vec![];
2635 while let Ok(Some(r)) = self.next_left() {
2636 out.push(r);
2637 }
2638 out
2639 }
2640 fn stats(&self) -> Option<(usize, usize)> {
2641 Some((self.pulled, self.kept))
2642 }
2643}
2644
2645struct VecLeft {
2648 rows: Vec<JoinedRow>,
2649 at: usize,
2650}
2651
2652impl LeftSource for VecLeft {
2653 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
2654 let r = self.rows.get(self.at).cloned();
2655 if r.is_some() {
2656 self.at += 1;
2657 }
2658 Ok(r)
2659 }
2660 fn hint(&self) -> Option<usize> {
2661 Some(self.rows.len().saturating_sub(self.at))
2662 }
2663 fn take_rows(&mut self) -> Vec<JoinedRow> {
2664 let mut v = std::mem::take(&mut self.rows);
2665 if self.at > 0 {
2666 v = v.split_off(self.at);
2667 }
2668 self.at = 0;
2669 v
2670 }
2671}
2672
2673fn drain(mut rel: Box<dyn Relation>) -> Result<Vec<Value>> {
2680 let mut out = Vec::with_capacity(rel.size_hint().unwrap_or(0));
2681 while let Some(row) = rel.next_row()? {
2682 out.push(row);
2683 }
2684 Ok(out)
2685}
2686
2687pub fn run(sql: &str, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
2689 let sel = parse(sql)?;
2690 execute(&sel, resolve)
2691}
2692
2693#[cfg(test)]
2694mod lexer_tests {
2695 use super::*;
2696
2697 fn kinds(src: &str) -> Vec<Tok> {
2698 let mut t = lex(src).expect("lexes");
2699 t.pop(); t
2701 }
2702
2703 #[test]
2704 fn a_word_keeps_both_its_canonical_and_raw_spelling() {
2705 assert_eq!(
2708 kinds("Select"),
2709 vec![Tok::Word { upper: "SELECT".into(), raw: "Select".into() }]
2710 );
2711 }
2712
2713 #[test]
2714 fn a_quoted_identifier_is_never_a_keyword() {
2715 assert_eq!(kinds(r#""select""#), vec![Tok::Quoted("select".into())]);
2716 assert_eq!(kinds(r#""Name""#), vec![Tok::Quoted("Name".into())]);
2718 }
2719
2720 #[test]
2721 fn a_doubled_quote_is_one_literal_quote() {
2722 assert_eq!(kinds("'it''s'"), vec![Tok::Str("it's".into())]);
2723 assert_eq!(kinds(r#""a""b""#), vec![Tok::Quoted("a\"b".into())]);
2724 }
2725
2726 #[test]
2727 fn an_E_string_decodes_the_escapes_catalogue_sql_uses() {
2728 assert_eq!(kinds(r"E'\n'"), vec![Tok::Str("\n".into())]);
2730 assert_eq!(kinds(r"E'a\tb'"), vec![Tok::Str("a\tb".into())]);
2731 assert_eq!(kinds(r"E'\q'"), vec![Tok::Str("q".into())]);
2733 }
2734
2735 #[test]
2736 fn operators_match_longest_first() {
2737 assert_eq!(kinds("!~*"), vec![Tok::Op("!~*".into())]);
2739 assert_eq!(kinds("!~"), vec![Tok::Op("!~".into())]);
2740 assert_eq!(kinds("~*"), vec![Tok::Op("~*".into())]);
2741 assert_eq!(kinds("<>"), vec![Tok::Op("<>".into())]);
2742 assert_eq!(kinds("!="), vec![Tok::Op("!=".into())]);
2743 assert_eq!(kinds(">="), vec![Tok::Op(">=".into())]);
2744 assert_eq!(kinds("::"), vec![Tok::Op("::".into())]);
2745 assert_eq!(kinds("||"), vec![Tok::Op("||".into())]);
2746 assert_eq!(kinds("~"), vec![Tok::Op("~".into())]);
2747 }
2748
2749 #[test]
2750 fn comments_are_skipped_including_nested_block_comments() {
2751 assert_eq!(kinds("1 -- trailing\n"), vec![Tok::Num(1.0)]);
2752 assert_eq!(kinds("1 /* a */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
2753 assert_eq!(kinds("1 /* a /* b */ c */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
2755 assert!(lex("1 /* unterminated").is_err());
2756 }
2757
2758 #[test]
2759 fn numbers_parse_including_fractions_and_exponents() {
2760 assert_eq!(kinds("42"), vec![Tok::Num(42.0)]);
2761 assert_eq!(kinds("4.5"), vec![Tok::Num(4.5)]);
2762 assert_eq!(kinds(".5"), vec![Tok::Num(0.5)]);
2763 assert_eq!(kinds("1e3"), vec![Tok::Num(1000.0)]);
2764 assert_eq!(kinds("1e-2"), vec![Tok::Num(0.01)]);
2765 assert_eq!(
2767 kinds("1e"),
2768 vec![Tok::Num(1.0), Tok::Word { upper: "E".into(), raw: "e".into() }]
2769 );
2770 }
2771
2772 #[test]
2773 fn an_unterminated_literal_is_an_error_not_a_truncation() {
2774 assert!(lex("'abc").is_err());
2775 assert!(lex(r#""abc"#).is_err());
2776 }
2777
2778 #[test]
2779 fn an_unknown_character_is_REFUSED_rather_than_skipped() {
2780 let e = lex("SELECT 1 @ 2").unwrap_err().to_string();
2783 assert!(e.contains('@'), "{}", e);
2784 }
2785
2786 #[test]
2787 fn the_real_dn_query_lexes() {
2788 let sql = r#"SELECT n.nspname AS "Name",
2789 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
2790 FROM pg_catalog.pg_namespace n
2791 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
2792 ORDER BY 1;"#;
2793 let toks = lex(sql).expect("psql's \\dn must lex");
2794 assert!(toks.contains(&Tok::Quoted("Name".into())));
2795 assert!(toks.contains(&Tok::Op("!~".into())));
2796 assert!(toks.contains(&Tok::Op("<>".into())));
2797 assert!(toks.contains(&Tok::Str("^pg_".into())));
2798 }
2799
2800 #[test]
2801 fn the_real_dt_query_lexes() {
2802 let sql = r#"SELECT n.nspname as "Schema", c.relname as "Name",
2803 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' END as "Type",
2804 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
2805 FROM pg_catalog.pg_class c
2806 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
2807 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
2808 WHERE c.relkind IN ('r','p','')
2809 AND n.nspname <> 'pg_catalog'
2810 AND n.nspname !~ '^pg_toast'
2811 AND pg_catalog.pg_table_is_visible(c.oid)
2812 ORDER BY 1,2;"#;
2813 let toks = lex(sql).expect("psql's \\dt must lex");
2814 assert!(toks.iter().any(|t| t.is_kw("CASE")));
2815 assert!(toks.iter().any(|t| t.is_kw("LEFT")));
2816 assert!(toks.iter().any(|t| t.is_kw("JOIN")));
2817 assert!(toks.contains(&Tok::Str(String::new())));
2819 }
2820}
2821
2822#[cfg(test)]
2823mod parser_tests {
2824 use super::*;
2825 use serde_json::json;
2826
2827 fn col(qual: Option<&str>, name: &str) -> Expr {
2828 Expr::Column { qual: qual.map(str::to_string), name: name.to_string() }
2829 }
2830
2831 #[test]
2832 fn a_bare_select_list_and_from() {
2833 let s = parse("SELECT a, b FROM t").unwrap();
2834 assert_eq!(s.items.len(), 2);
2835 assert_eq!(s.items[0].expr, col(None, "a"));
2836 assert_eq!(s.from.unwrap().name, "t");
2837 }
2838
2839 #[test]
2840 fn a_clause_keyword_is_never_read_as_a_bare_alias() {
2841 let s = parse("SELECT a FROM t WHERE a = 1").unwrap();
2845 assert_eq!(s.from.clone().unwrap().alias, None);
2846 assert!(s.where_.is_some(), "the WHERE clause must survive");
2847 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
2848 assert_eq!(s.from.unwrap().alias, None);
2849 assert_eq!(s.order_by.len(), 1);
2850 }
2851
2852 #[test]
2853 fn a_real_alias_is_kept_in_both_spellings() {
2854 assert_eq!(parse("SELECT a FROM t x").unwrap().from.unwrap().alias,
2855 Some("x".to_string()));
2856 assert_eq!(parse("SELECT a FROM t AS x").unwrap().from.unwrap().alias,
2857 Some("x".to_string()));
2858 }
2859
2860 #[test]
2861 fn a_tables_binding_is_its_alias_else_its_bare_name() {
2862 let t = TableRef { name: "pg_catalog.pg_class".into(), alias: Some("c".into()) };
2863 assert_eq!(t.binding(), "c");
2864 let t = TableRef { name: "pg_catalog.pg_class".into(), alias: None };
2865 assert_eq!(t.binding(), "pg_class", "the schema is not how a column is addressed");
2866 }
2867
2868 #[test]
2869 fn a_qualified_column_keeps_only_its_immediate_qualifier() {
2870 assert_eq!(parse("SELECT n.nspname FROM x").unwrap().items[0].expr,
2871 col(Some("n"), "nspname"));
2872 assert_eq!(parse("SELECT public.orders.id FROM x").unwrap().items[0].expr,
2875 col(Some("orders"), "id"));
2876 }
2877
2878 #[test]
2879 fn an_alias_may_be_a_quoted_string_with_significant_case() {
2880 let s = parse(r#"SELECT n.nspname AS "Name" FROM x"#).unwrap();
2881 assert_eq!(s.items[0].alias, Some("Name".to_string()));
2882 }
2883
2884 #[test]
2885 fn a_schema_qualified_function_drops_its_schema() {
2886 let s = parse("SELECT pg_catalog.pg_get_userbyid(n.nspowner) FROM x").unwrap();
2889 match &s.items[0].expr {
2890 Expr::Func { name, args } => {
2891 assert_eq!(name, "pg_get_userbyid");
2892 assert_eq!(args.len(), 1);
2893 assert_eq!(args[0], col(Some("n"), "nspowner"));
2894 }
2895 other => panic!("{:?}", other),
2896 }
2897 }
2898
2899 #[test]
2900 fn operator_precedence_matches_sql() {
2901 let s = parse("SELECT 1 FROM t WHERE a = 1 OR b = 2 AND c = 3").unwrap();
2904 match s.where_.unwrap() {
2905 Expr::Binary { op, right, .. } => {
2906 assert_eq!(op, "OR");
2907 assert!(matches!(*right, Expr::Binary { ref op, .. } if op == "AND"),
2908 "AND must bind tighter than OR");
2909 }
2910 other => panic!("{:?}", other),
2911 }
2912 let s = parse("SELECT 1 FROM t WHERE a = 1 AND b = 2").unwrap();
2914 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
2915 let s = parse("SELECT 1 + 2 * 3 FROM t").unwrap();
2917 match &s.items[0].expr {
2918 Expr::Binary { op, right, .. } => {
2919 assert_eq!(op, "+");
2920 assert!(matches!(**right, Expr::Binary { ref op, .. } if op == "*"));
2921 }
2922 other => panic!("{:?}", other),
2923 }
2924 }
2925
2926 #[test]
2927 fn parentheses_override_precedence() {
2928 let s = parse("SELECT 1 FROM t WHERE (a = 1 OR b = 2) AND c = 3").unwrap();
2929 match s.where_.unwrap() {
2930 Expr::Binary { op, left, .. } => {
2931 assert_eq!(op, "AND");
2932 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "OR"));
2933 }
2934 other => panic!("{:?}", other),
2935 }
2936 }
2937
2938 #[test]
2939 fn in_and_is_null_and_between_parse_in_both_polarities() {
2940 let s = parse("SELECT 1 FROM t WHERE k IN ('r','p','')").unwrap();
2941 match s.where_.unwrap() {
2942 Expr::InList { list, negated, .. } => {
2943 assert_eq!(list.len(), 3);
2944 assert!(!negated);
2945 assert_eq!(list[2], Expr::Literal(json!("")));
2947 }
2948 other => panic!("{:?}", other),
2949 }
2950 assert!(matches!(parse("SELECT 1 FROM t WHERE k NOT IN (1)").unwrap().where_.unwrap(),
2951 Expr::InList { negated: true, .. }));
2952 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NULL").unwrap().where_.unwrap(),
2953 Expr::IsNull { negated: false, .. }));
2954 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NOT NULL").unwrap().where_.unwrap(),
2955 Expr::IsNull { negated: true, .. }));
2956 let s = parse("SELECT 1 FROM t WHERE n BETWEEN 1 AND 5").unwrap();
2958 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
2959 }
2960
2961 #[test]
2962 fn both_case_spellings_parse() {
2963 let s = parse("SELECT CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
2965 ELSE 'other' END FROM t").unwrap();
2966 match &s.items[0].expr {
2967 Expr::Case { operand, whens, else_ } => {
2968 assert!(operand.is_some());
2969 assert_eq!(whens.len(), 2);
2970 assert!(else_.is_some());
2971 }
2972 other => panic!("{:?}", other),
2973 }
2974 let s = parse("SELECT CASE WHEN k = 'r' THEN 1 END FROM t").unwrap();
2976 match &s.items[0].expr {
2977 Expr::Case { operand, whens, else_ } => {
2978 assert!(operand.is_none());
2979 assert_eq!(whens.len(), 1);
2980 assert!(else_.is_none());
2981 }
2982 other => panic!("{:?}", other),
2983 }
2984 assert!(parse("SELECT CASE k END FROM t").is_err());
2986 }
2987
2988 #[test]
2989 fn every_join_flavour_parses_and_an_inner_join_demands_ON() {
2990 for (sql, kind) in [
2991 ("SELECT 1 FROM a JOIN b ON a.x = b.x", JoinKind::Inner),
2992 ("SELECT 1 FROM a INNER JOIN b ON a.x = b.x", JoinKind::Inner),
2993 ("SELECT 1 FROM a LEFT JOIN b ON a.x = b.x", JoinKind::Left),
2994 ("SELECT 1 FROM a LEFT OUTER JOIN b ON a.x = b.x", JoinKind::Left),
2995 ("SELECT 1 FROM a RIGHT JOIN b ON a.x = b.x", JoinKind::Right),
2996 ("SELECT 1 FROM a FULL OUTER JOIN b ON a.x = b.x", JoinKind::Full),
2997 ("SELECT 1 FROM a CROSS JOIN b", JoinKind::Cross),
2998 ] {
2999 let s = parse(sql).unwrap_or_else(|e| panic!("{}: {}", sql, e));
3000 assert_eq!(s.joins.len(), 1, "{}", sql);
3001 assert_eq!(s.joins[0].kind, kind, "{}", sql);
3002 }
3003 let s = parse("SELECT 1 FROM a, b").unwrap();
3005 assert_eq!(s.joins[0].kind, JoinKind::Cross);
3006 assert!(parse("SELECT 1 FROM a LEFT JOIN b").is_err());
3009 assert!(parse("SELECT 1 FROM a JOIN b USING (x)").is_err());
3010 }
3011
3012 #[test]
3013 fn order_by_reads_a_number_as_an_ORDINAL() {
3014 let s = parse("SELECT a, b FROM t ORDER BY 1, 2 DESC").unwrap();
3018 assert_eq!(s.order_by.len(), 2);
3019 assert_eq!(s.order_by[0].ordinal, Some(1));
3020 assert_eq!(s.order_by[0].dir, Dir::Asc);
3021 assert_eq!(s.order_by[1].ordinal, Some(2));
3022 assert_eq!(s.order_by[1].dir, Dir::Desc);
3023 let s = parse("SELECT a FROM t ORDER BY lower(a) ASC").unwrap();
3025 assert!(s.order_by[0].ordinal.is_none());
3026 assert!(s.order_by[0].expr.is_some());
3027 }
3028
3029 #[test]
3030 fn null_ordering_defaults_the_way_postgres_defaults() {
3031 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
3032 assert!(!s.order_by[0].nulls_first, "ASC defaults to NULLS LAST");
3033 let s = parse("SELECT a FROM t ORDER BY a DESC").unwrap();
3034 assert!(s.order_by[0].nulls_first, "DESC defaults to NULLS FIRST");
3035 let s = parse("SELECT a FROM t ORDER BY a NULLS FIRST").unwrap();
3036 assert!(s.order_by[0].nulls_first, "an explicit clause wins");
3037 }
3038
3039 #[test]
3040 fn limit_and_offset_parse_in_either_order() {
3041 let s = parse("SELECT a FROM t LIMIT 5 OFFSET 2").unwrap();
3042 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
3043 let s = parse("SELECT a FROM t OFFSET 2 LIMIT 5").unwrap();
3044 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
3045 let s = parse("SELECT a FROM t LIMIT ALL").unwrap();
3046 assert_eq!(s.limit, None);
3047 }
3048
3049 #[test]
3050 fn casts_parse_and_are_recorded_rather_than_rejected() {
3051 let s = parse("SELECT x::int2 FROM t").unwrap();
3053 assert!(matches!(s.items[0].expr, Expr::Cast { .. }));
3054 let s = parse("SELECT x::pg_catalog.int2[] FROM t").unwrap();
3055 match &s.items[0].expr {
3056 Expr::Cast { ty, .. } => assert_eq!(ty, "int2[]"),
3057 other => panic!("{:?}", other),
3058 }
3059 }
3060
3061 #[test]
3062 fn star_and_qualified_star_parse() {
3063 assert_eq!(parse("SELECT * FROM t").unwrap().items[0].expr, Expr::Star);
3064 assert_eq!(parse("SELECT c.* FROM t c").unwrap().items[0].expr,
3065 Expr::QualifiedStar("c".into()));
3066 match &parse("SELECT count(*) FROM t").unwrap().items[0].expr {
3067 Expr::Func { name, args } => {
3068 assert_eq!(name, "count");
3069 assert_eq!(args, &vec![Expr::Star]);
3070 }
3071 other => panic!("{:?}", other),
3072 }
3073 }
3074
3075 #[test]
3076 fn a_parenthesis_free_function_parses_as_a_zero_arg_call() {
3077 match &parse("SELECT current_schema FROM t").unwrap().items[0].expr {
3079 Expr::Func { name, args } => {
3080 assert_eq!(name, "current_schema");
3081 assert!(args.is_empty());
3082 }
3083 other => panic!("{:?}", other),
3084 }
3085 }
3086
3087 #[test]
3088 fn unsupported_clauses_are_refused_by_name() {
3089 for (sql, needle) in [
3090 ("SELECT a FROM t GROUP BY a", "GROUP BY"),
3091 ("SELECT a FROM t HAVING count(*) > 1", "HAVING"),
3092 ("SELECT DISTINCT ON (a) a FROM t", "DISTINCT ON"),
3093 ] {
3094 let e = parse(sql).unwrap_err().to_string();
3095 assert!(e.contains(needle), "{} -> {}", sql, e);
3096 }
3097 assert!(parse("SELECT a FROM t JUNK JUNK2").is_err());
3099 }
3100
3101 #[test]
3102 fn THE_dn_QUERY_parses_completely() {
3103 let s = parse(
3104 r#"SELECT n.nspname AS "Name",
3105 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
3106 FROM pg_catalog.pg_namespace n
3107 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
3108 ORDER BY 1;"#,
3109 )
3110 .expect("psql's \\dn must parse");
3111
3112 assert_eq!(s.items.len(), 2);
3113 assert_eq!(s.items[0].alias, Some("Name".into()));
3114 assert_eq!(s.items[1].alias, Some("Owner".into()));
3115 let from = s.from.unwrap();
3116 assert_eq!(from.name, "pg_catalog.pg_namespace");
3117 assert_eq!(from.binding(), "n");
3118 assert!(s.where_.is_some());
3119 assert_eq!(s.order_by[0].ordinal, Some(1));
3120 }
3121
3122 #[test]
3123 fn THE_dt_QUERY_parses_completely() {
3124 let s = parse(
3125 r#"SELECT n.nspname as "Schema",
3126 c.relname as "Name",
3127 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
3128 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
3129 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
3130 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
3131 WHEN 'I' THEN 'partitioned index' END as "Type",
3132 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
3133 FROM pg_catalog.pg_class c
3134 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
3135 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
3136 WHERE c.relkind IN ('r','p','')
3137 AND n.nspname <> 'pg_catalog'
3138 AND n.nspname !~ '^pg_toast'
3139 AND n.nspname <> 'information_schema'
3140 AND pg_catalog.pg_table_is_visible(c.oid)
3141 ORDER BY 1,2;"#,
3142 )
3143 .expect("psql's \\dt must parse");
3144
3145 assert_eq!(s.items.len(), 4);
3146 assert_eq!(s.items[2].alias, Some("Type".into()));
3147 match &s.items[2].expr {
3148 Expr::Case { whens, .. } => assert_eq!(whens.len(), 9, "all nine branches"),
3149 other => panic!("{:?}", other),
3150 }
3151 assert_eq!(s.joins.len(), 2);
3152 assert!(s.joins.iter().all(|j| j.kind == JoinKind::Left && j.on.is_some()));
3153 assert_eq!(s.from.unwrap().binding(), "c");
3154 assert_eq!(s.order_by.len(), 2);
3155 assert_eq!(
3156 (s.order_by[0].ordinal, s.order_by[1].ordinal),
3157 (Some(1), Some(2))
3158 );
3159 }
3160}
3161
3162#[cfg(test)]
3163mod eval_tests {
3164 use super::*;
3165 use serde_json::json;
3166
3167 fn one(row: &Value) -> Bound<'_> {
3169 Bound { parts: vec![("t".to_string(), Some(row))] }
3170 }
3171
3172 fn ev(sql_expr: &str, row: &Value) -> Result<Value> {
3173 let s = parse(&format!("SELECT {} FROM t", sql_expr))?;
3174 eval(&s.items[0].expr, &one(row))
3175 }
3176
3177 fn v(sql_expr: &str, row: &Value) -> Value {
3178 ev(sql_expr, row).unwrap_or_else(|e| panic!("{}: {}", sql_expr, e))
3179 }
3180
3181 #[test]
3182 fn literals_and_columns_resolve() {
3183 let r = json!({"a": 1, "s": "x", "b": true, "n": null});
3184 assert_eq!(v("42", &r), json!(42));
3185 assert_eq!(v("'hi'", &r), json!("hi"));
3186 assert_eq!(v("NULL", &r), Value::Null);
3187 assert_eq!(v("TRUE", &r), json!(true));
3188 assert_eq!(v("a", &r), json!(1));
3189 assert_eq!(v("t.a", &r), json!(1));
3190 assert_eq!(v("s", &r), json!("x"));
3191 assert_eq!(v("nosuch", &r), Value::Null);
3194 }
3195
3196 #[test]
3197 fn an_unknown_table_ALIAS_is_an_error_while_an_unknown_column_is_null() {
3198 let r = json!({"a": 1});
3201 assert_eq!(v("t.nosuch", &r), Value::Null);
3202 let e = ev("zz.a", &r).unwrap_err().to_string();
3203 assert!(e.contains("zz"), "{}", e);
3204 }
3205
3206 #[test]
3209 fn every_comparison_over_NULL_is_UNKNOWN_including_null_equals_null() {
3210 let r = json!({"n": null, "a": 1});
3211 assert_eq!(v("n = 1", &r), Value::Null);
3212 assert_eq!(v("n != 1", &r), Value::Null);
3213 assert_eq!(v("n < 1", &r), Value::Null);
3214 assert_eq!(v("n = n", &r), Value::Null);
3216 assert_eq!(v("n = NULL", &r), Value::Null);
3217 }
3218
3219 #[test]
3220 fn NOT_UNKNOWN_is_UNKNOWN_not_true() {
3221 let r = json!({"n": null});
3225 assert_eq!(v("NOT (n = 1)", &r), Value::Null);
3226 assert_eq!(v("NOT TRUE", &r), json!(false));
3227 assert_eq!(v("NOT FALSE", &r), json!(true));
3228 }
3229
3230 #[test]
3231 fn AND_and_OR_follow_the_three_valued_truth_tables() {
3232 let r = json!({"n": null});
3233 assert_eq!(v("FALSE AND n = 1", &r), json!(false));
3235 assert_eq!(v("TRUE AND n = 1", &r), Value::Null);
3237 assert_eq!(v("TRUE OR n = 1", &r), json!(true));
3239 assert_eq!(v("FALSE OR n = 1", &r), Value::Null);
3241 assert_eq!(v("TRUE AND TRUE", &r), json!(true));
3243 assert_eq!(v("TRUE AND FALSE", &r), json!(false));
3244 assert_eq!(v("FALSE OR FALSE", &r), json!(false));
3245 }
3246
3247 #[test]
3248 fn IS_NULL_is_the_one_predicate_that_is_never_unknown() {
3249 let r = json!({"n": null, "a": 1});
3250 assert_eq!(v("n IS NULL", &r), json!(true));
3251 assert_eq!(v("n IS NOT NULL", &r), json!(false));
3252 assert_eq!(v("a IS NULL", &r), json!(false));
3253 assert_eq!(v("a IS NOT NULL", &r), json!(true));
3254 assert_eq!(v("nosuch IS NULL", &r), json!(true));
3257 }
3258
3259 #[test]
3260 fn NOT_IN_with_a_NULL_in_the_list_is_UNKNOWN_the_classic_trap() {
3261 let r = json!({"a": 2});
3262 assert_eq!(v("a IN (1, 2)", &r), json!(true));
3263 assert_eq!(v("a IN (1, 3)", &r), json!(false));
3264 assert_eq!(v("a NOT IN (1, 3)", &r), json!(true));
3265 assert_eq!(v("a NOT IN (1, NULL)", &r), Value::Null);
3268 assert_eq!(v("a IN (2, NULL)", &r), json!(true));
3270 assert_eq!(v("nosuch IN (1)", &r), Value::Null);
3272 }
3273
3274 #[test]
3277 fn comparisons_work_across_numbers_strings_and_booleans() {
3278 let r = json!({"n": 5, "s": "b", "t": true});
3279 assert_eq!(v("n > 3", &r), json!(true));
3280 assert_eq!(v("n <= 5", &r), json!(true));
3281 assert_eq!(v("s < 'c'", &r), json!(true));
3282 assert_eq!(v("s > 'c'", &r), json!(false));
3283 assert_eq!(v("n = '5'", &r), json!(true));
3286 assert_eq!(v("n = '5.0'", &r), json!(true));
3287 assert_eq!(v("n = 'five'", &r), json!(false));
3290 }
3291
3292 #[test]
3293 fn the_regex_operators_use_the_SAME_matcher_as_NQL() {
3294 let r = json!({"s": "pg_catalog"});
3297 assert_eq!(v("s ~ '^pg_'", &r), json!(true));
3298 assert_eq!(v("s !~ '^pg_'", &r), json!(false));
3299 assert_eq!(v("s ~ '^PG_'", &r), json!(false));
3300 assert_eq!(v("s ~* '^PG_'", &r), json!(true));
3301 assert_eq!(v("s !~ '^zz'", &r), json!(true));
3302 assert_eq!(v("nosuch ~ '^x'", &r), Value::Null);
3304 assert!(ev("s ~ 'a+b'", &r).is_err());
3306 }
3307
3308 #[test]
3309 fn like_works_in_all_four_spellings() {
3310 let r = json!({"s": "Acme Pool"});
3311 assert_eq!(v("s LIKE 'Acme%'", &r), json!(true));
3312 assert_eq!(v("s LIKE 'acme%'", &r), json!(false));
3313 assert_eq!(v("s ILIKE 'acme%'", &r), json!(true));
3314 assert_eq!(v("s NOT LIKE 'zz%'", &r), json!(true));
3315 assert_eq!(v("nosuch LIKE 'x'", &r), Value::Null);
3316 }
3317
3318 #[test]
3319 fn arithmetic_and_concatenation_propagate_null_and_refuse_div_by_zero() {
3320 let r = json!({"a": 7, "b": 2});
3321 assert_eq!(v("a + b", &r), json!(9));
3322 assert_eq!(v("a - b", &r), json!(5));
3323 assert_eq!(v("a * b", &r), json!(14));
3324 assert_eq!(v("a / b", &r), json!(3.5));
3325 assert_eq!(v("a % b", &r), json!(1));
3326 assert_eq!(v("-a", &r), json!(-7));
3327 assert_eq!(v("a / b", &r), json!(3.5));
3330 assert_eq!(v("b / a", &r), json!(2.0 / 7.0));
3331 assert_eq!(v("'x' || 'y'", &r), json!("xy"));
3332 assert_eq!(v("'x' || nosuch", &r), Value::Null);
3333 assert_eq!(v("a + nosuch", &r), Value::Null);
3334 assert!(ev("a / 0", &r).is_err());
3337 assert!(ev("a % 0", &r).is_err());
3338 }
3339
3340 #[test]
3341 fn a_cast_is_transparent_rather_than_rejected() {
3342 let r = json!({"a": 7});
3345 assert_eq!(v("a::int2", &r), json!(7));
3346 assert_eq!(v("a::pg_catalog.int2[]", &r), json!(7));
3347 }
3348
3349 #[test]
3352 fn a_simple_CASE_picks_the_matching_branch() {
3353 let expr = "CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
3355 WHEN 'i' THEN 'index' END";
3356 assert_eq!(v(expr, &json!({"k": "r"})), json!("table"));
3357 assert_eq!(v(expr, &json!({"k": "v"})), json!("view"));
3358 assert_eq!(v(expr, &json!({"k": "i"})), json!("index"));
3359 assert_eq!(v(expr, &json!({"k": "z"})), Value::Null);
3362 }
3363
3364 #[test]
3365 fn a_searched_CASE_evaluates_predicates_and_UNKNOWN_does_not_match() {
3366 let expr = "CASE WHEN n > 5 THEN 'big' WHEN n > 0 THEN 'small' ELSE 'none' END";
3367 assert_eq!(v(expr, &json!({"n": 9})), json!("big"));
3368 assert_eq!(v(expr, &json!({"n": 2})), json!("small"));
3369 assert_eq!(v(expr, &json!({"n": -1})), json!("none"));
3370 assert_eq!(v(expr, &json!({"other": 1})), json!("none"));
3372 }
3373
3374 #[test]
3375 fn an_ELSE_branch_is_used_when_nothing_matches() {
3376 assert_eq!(
3377 v("CASE k WHEN 'r' THEN 'table' ELSE 'other' END", &json!({"k": "z"})),
3378 json!("other")
3379 );
3380 }
3381
3382 #[test]
3385 fn the_catalogue_functions_psql_calls_all_answer() {
3386 let r = json!({"o": 10, "enc": 6});
3387 assert_eq!(v("pg_get_userbyid(o)", &r), json!("nedb"));
3389 assert_eq!(v("pg_catalog.pg_get_userbyid(o)", &r), json!("nedb"));
3390 assert_eq!(v("pg_table_is_visible(o)", &r), json!(true));
3392 assert_eq!(v("pg_encoding_to_char(enc)", &r), json!("UTF8"));
3393 assert_eq!(v("current_schema", &r), json!("public"));
3394 assert_eq!(v("current_database()", &r), json!("nedb"));
3395 assert_eq!(v("current_user", &r), json!("nedb"));
3396 assert_eq!(v("pg_get_expr(o, o)", &r), Value::Null);
3399 assert_eq!(v("obj_description(o)", &r), Value::Null);
3400 }
3401
3402 #[test]
3403 fn text_and_null_handling_functions_work() {
3404 let r = json!({"s": "AbC", "n": null});
3405 assert_eq!(v("lower(s)", &r), json!("abc"));
3406 assert_eq!(v("upper(s)", &r), json!("ABC"));
3407 assert_eq!(v("length(s)", &r), json!(3));
3408 assert_eq!(v("lower(n)", &r), Value::Null);
3409 assert_eq!(v("coalesce(n, 'fallback')", &r), json!("fallback"));
3410 assert_eq!(v("coalesce(s, 'fallback')", &r), json!("AbC"));
3411 assert_eq!(v("coalesce(n, n)", &r), Value::Null);
3412 assert_eq!(v("nullif(s, 'AbC')", &r), Value::Null);
3413 assert_eq!(v("nullif(s, 'zz')", &r), json!("AbC"));
3414 assert_eq!(v("format_type(20, NULL)", &r), json!("bigint"));
3416 }
3417
3418 #[test]
3419 fn coalesce_does_not_evaluate_past_its_first_non_null() {
3420 let r = json!({"a": 1});
3422 assert_eq!(v("coalesce(a, a / 0)", &r), json!(1));
3423 }
3424
3425 #[test]
3426 fn an_unknown_function_is_REFUSED_rather_than_answered_with_NULL() {
3427 let e = ev("pg_stat_get_numscans(1)", &json!({})).unwrap_err().to_string();
3430 assert!(e.contains("pg_stat_get_numscans"), "{}", e);
3431 assert!(e.contains("refused"), "{}", e);
3432 }
3433
3434 #[test]
3437 fn a_qualified_column_reads_only_its_OWN_binding() {
3438 let a = json!({"name": "left", "x": 1});
3441 let b = json!({"name": "right", "y": 2});
3442 let row = Bound {
3443 parts: vec![("a".into(), Some(&a)), ("b".into(), Some(&b))],
3444 };
3445 let get = |e: &str| {
3446 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
3447 eval(&s.items[0].expr, &row).unwrap()
3448 };
3449 assert_eq!(get("a.name"), json!("left"));
3450 assert_eq!(get("b.name"), json!("right"));
3451 assert_eq!(get("name"), json!("left"));
3453 assert_eq!(get("y"), json!(2), "a bare name still finds a later binding");
3454 }
3455
3456 #[test]
3457 fn an_unmatched_LEFT_JOIN_side_reads_as_NULL_not_as_a_missing_column() {
3458 let a = json!({"x": 1});
3461 let row = Bound {
3462 parts: vec![("a".into(), Some(&a)), ("b".into(), None)],
3463 };
3464 let get = |e: &str| {
3465 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
3466 eval(&s.items[0].expr, &row).unwrap()
3467 };
3468 assert_eq!(get("b.anything"), Value::Null);
3469 assert_eq!(get("b.anything IS NULL"), json!(true));
3470 assert_eq!(get("a.x"), json!(1));
3471 }
3472}
3473
3474#[cfg(test)]
3475mod exec_tests {
3476 use super::*;
3477 use serde_json::json;
3478
3479 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
3481 let owned: Vec<(String, Vec<Value>)> =
3482 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
3483 move |name: &str| {
3484 let bare = name.rsplit('.').next().unwrap_or(name);
3486 Ok(owned
3487 .iter()
3488 .find(|(n, _)| n == name || n == bare)
3489 .map(|(_, r)| from_vec(r.clone())))
3490 }
3491 }
3492
3493 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
3494 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
3495 (cols.into_iter().map(|c| c.name).collect(), rows)
3496 }
3497
3498 fn col(rows: &[Value], name: &str) -> Vec<Value> {
3499 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
3500 }
3501
3502 #[test]
3505 fn select_columns_where_order_limit_offset() {
3506 let t = tables(vec![(
3507 "t",
3508 vec![json!({"a": 3, "s": "c"}), json!({"a": 1, "s": "a"}), json!({"a": 2, "s": "b"})],
3509 )]);
3510 let (names, rows) = go("SELECT a, s FROM t ORDER BY a", &t);
3511 assert_eq!(names, vec!["a", "s"]);
3512 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2), json!(3)]);
3513
3514 let (_, rows) = go("SELECT a FROM t ORDER BY a DESC", &t);
3515 assert_eq!(col(&rows, "a"), vec![json!(3), json!(2), json!(1)]);
3516
3517 let (_, rows) = go("SELECT a FROM t WHERE a > 1 ORDER BY a", &t);
3518 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
3519
3520 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 2", &t);
3521 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
3522
3523 let (_, rows) = go("SELECT a FROM t ORDER BY a OFFSET 1", &t);
3524 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
3525
3526 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 1 OFFSET 1", &t);
3527 assert_eq!(col(&rows, "a"), vec![json!(2)]);
3528
3529 let (_, rows) = go("SELECT a FROM t OFFSET 99", &t);
3531 assert!(rows.is_empty());
3532 }
3533
3534 #[test]
3535 fn an_output_column_takes_its_alias_or_a_derived_name() {
3536 let t = tables(vec![("t", vec![json!({"a": 1})])]);
3539 assert_eq!(go(r#"SELECT a AS "Name" FROM t"#, &t).0, vec!["Name"]);
3540 assert_eq!(go("SELECT a FROM t", &t).0, vec!["a"]);
3541 assert_eq!(go("SELECT lower('X') FROM t", &t).0, vec!["lower"]);
3542 assert_eq!(go("SELECT 1 + 1 FROM t", &t).0, vec!["?column?"]);
3543 assert_eq!(go("SELECT CASE a WHEN 1 THEN 'x' END FROM t", &t).0, vec!["case"]);
3544 }
3545
3546 #[test]
3547 fn star_expands_from_the_rows_and_a_qualified_star_from_one_binding() {
3548 let t = tables(vec![
3549 ("a", vec![json!({"x": 1, "y": 2})]),
3550 ("b", vec![json!({"z": 3})]),
3551 ]);
3552 let (names, rows) = go("SELECT * FROM a", &t);
3553 assert_eq!(names, vec!["x", "y"]);
3554 assert_eq!(rows.len(), 1);
3555
3556 let (names, _) = go("SELECT a.* FROM a CROSS JOIN b", &t);
3557 assert_eq!(names, vec!["x", "y"], "a qualified star takes ONE binding");
3558
3559 let empty = tables(vec![("e", vec![])]);
3562 assert_eq!(go("SELECT * FROM e", &empty).0, Vec::<String>::new());
3563 }
3564
3565 #[test]
3566 fn distinct_dedupes_on_the_projected_values() {
3567 let t = tables(vec![(
3568 "t",
3569 vec![json!({"g": "x"}), json!({"g": "x"}), json!({"g": "y"})],
3570 )]);
3571 let (_, rows) = go("SELECT DISTINCT g FROM t ORDER BY 1", &t);
3572 assert_eq!(col(&rows, "g"), vec![json!("x"), json!("y")]);
3573 let (_, rows) = go("SELECT g FROM t", &t);
3574 assert_eq!(rows.len(), 3, "without DISTINCT every row survives");
3575 }
3576
3577 #[test]
3578 fn order_by_an_ORDINAL_sorts_the_projected_column() {
3579 let t = tables(vec![(
3580 "t",
3581 vec![json!({"a": 2, "b": "z"}), json!({"a": 1, "b": "y"})],
3582 )]);
3583 let (_, rows) = go("SELECT a, b FROM t ORDER BY 1", &t);
3584 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
3585 let (_, rows) = go("SELECT a, b FROM t ORDER BY 2 DESC", &t);
3586 assert_eq!(col(&rows, "b"), vec![json!("z"), json!("y")]);
3587 let e = run("SELECT a FROM t ORDER BY 3", &t).unwrap_err().to_string();
3589 assert!(e.contains("out of range"), "{}", e);
3590 }
3591
3592 #[test]
3593 fn order_by_an_expression_may_use_a_column_NOT_in_the_select_list() {
3594 let t = tables(vec![(
3595 "t",
3596 vec![json!({"a": 1, "hidden": 9}), json!({"a": 2, "hidden": 1})],
3597 )]);
3598 let (_, rows) = go("SELECT a FROM t ORDER BY hidden", &t);
3599 assert_eq!(col(&rows, "a"), vec![json!(2), json!(1)]);
3600 }
3601
3602 #[test]
3603 fn null_ordering_follows_the_direction_defaults() {
3604 let t = tables(vec![(
3605 "t",
3606 vec![json!({"a": 2}), json!({"a": null}), json!({"a": 1})],
3607 )]);
3608 assert_eq!(col(&go("SELECT a FROM t ORDER BY a", &t).1, "a"),
3610 vec![json!(1), json!(2), Value::Null]);
3611 assert_eq!(col(&go("SELECT a FROM t ORDER BY a DESC", &t).1, "a"),
3613 vec![Value::Null, json!(2), json!(1)]);
3614 assert_eq!(col(&go("SELECT a FROM t ORDER BY a NULLS FIRST", &t).1, "a"),
3616 vec![Value::Null, json!(1), json!(2)]);
3617 }
3618
3619 #[test]
3620 fn a_where_clause_that_is_UNKNOWN_excludes_the_row() {
3621 let t = tables(vec![(
3622 "t",
3623 vec![json!({"a": 1}), json!({"a": null}), json!({"other": 1})],
3624 )]);
3625 let (_, rows) = go("SELECT a FROM t WHERE a = 1", &t);
3627 assert_eq!(rows.len(), 1);
3628 let (_, rows) = go("SELECT a FROM t WHERE NOT (a = 1)", &t);
3630 assert_eq!(rows.len(), 0, "NOT UNKNOWN must not resurrect a null row");
3631 }
3632
3633 #[test]
3634 fn select_with_no_FROM_returns_exactly_one_row() {
3635 let t = tables(vec![]);
3637 let (names, rows) = go("SELECT 1", &t);
3638 assert_eq!(rows.len(), 1);
3639 assert_eq!(names, vec!["?column?"]);
3640 assert_eq!(go("SELECT current_schema", &t).1.len(), 1);
3641 }
3642
3643 #[test]
3644 fn an_unknown_relation_is_NAMED_rather_than_answered_with_no_rows() {
3645 let t = tables(vec![("t", vec![])]);
3648 let e = run("SELECT a FROM nosuchtable", &t).unwrap_err().to_string();
3649 assert!(e.contains("nosuchtable"), "{}", e);
3650 assert!(e.contains("does not exist"), "{}", e);
3651 }
3652
3653 #[test]
3656 fn an_inner_join_keeps_only_matching_pairs() {
3657 let t = tables(vec![
3658 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
3659 ("r", vec![json!({"lid": 1, "v": "x"})]),
3660 ]);
3661 let (_, rows) = go("SELECT l.n, r.v FROM l JOIN r ON r.lid = l.id", &t);
3662 assert_eq!(rows.len(), 1);
3663 assert_eq!(col(&rows, "n"), vec![json!("a")]);
3664 }
3665
3666 #[test]
3667 fn a_LEFT_join_keeps_unmatched_left_rows_with_NULLs() {
3668 let t = tables(vec![
3670 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
3671 ("r", vec![json!({"lid": 1, "v": "x"})]),
3672 ]);
3673 let (_, rows) = go("SELECT l.n, r.v FROM l LEFT JOIN r ON r.lid = l.id ORDER BY 1", &t);
3674 assert_eq!(rows.len(), 2);
3675 assert_eq!(col(&rows, "n"), vec![json!("a"), json!("b")]);
3676 assert_eq!(col(&rows, "v"), vec![json!("x"), Value::Null]);
3677 }
3678
3679 #[test]
3680 fn a_RIGHT_join_keeps_unmatched_right_rows_and_FULL_keeps_both() {
3681 let t = tables(vec![
3682 ("l", vec![json!({"id": 1})]),
3683 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
3684 ]);
3685 let (_, rows) = go("SELECT l.id, r.lid FROM l RIGHT JOIN r ON r.lid = l.id", &t);
3686 assert_eq!(rows.len(), 2);
3687 assert!(col(&rows, "id").contains(&Value::Null), "the unmatched right row keeps NULLs on the left");
3688
3689 let t2 = tables(vec![
3690 ("l", vec![json!({"id": 1}), json!({"id": 5})]),
3691 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
3692 ]);
3693 let (_, rows) = go("SELECT l.id, r.lid FROM l FULL OUTER JOIN r ON r.lid = l.id", &t2);
3694 assert_eq!(rows.len(), 3, "one match plus one orphan on each side");
3695 }
3696
3697 #[test]
3698 fn a_cross_join_is_the_cartesian_product() {
3699 let t = tables(vec![
3700 ("a", vec![json!({"x": 1}), json!({"x": 2})]),
3701 ("b", vec![json!({"y": 1}), json!({"y": 2}), json!({"y": 3})]),
3702 ]);
3703 assert_eq!(go("SELECT a.x, b.y FROM a CROSS JOIN b", &t).1.len(), 6);
3704 assert_eq!(go("SELECT a.x, b.y FROM a, b", &t).1.len(), 6);
3706 }
3707
3708 #[test]
3709 fn an_ON_clause_that_is_UNKNOWN_does_not_join() {
3710 let t = tables(vec![
3713 ("l", vec![json!({"id": null})]),
3714 ("r", vec![json!({"lid": null})]),
3715 ]);
3716 let (_, rows) = go("SELECT l.id FROM l JOIN r ON r.lid = l.id", &t);
3717 assert!(rows.is_empty(), "NULL = NULL is UNKNOWN, so nothing joins");
3718 let (_, rows) = go("SELECT l.id FROM l LEFT JOIN r ON r.lid = l.id", &t);
3720 assert_eq!(rows.len(), 1);
3721 }
3722
3723 #[test]
3724 fn two_joins_chain() {
3725 let t = tables(vec![
3726 ("a", vec![json!({"id": 1, "bid": 10, "cid": 100})]),
3727 ("b", vec![json!({"id": 10, "bn": "B"})]),
3728 ("c", vec![json!({"id": 100, "cn": "C"})]),
3729 ]);
3730 let (_, rows) = go(
3731 "SELECT a.id, b.bn, c.cn FROM a \
3732 LEFT JOIN b ON b.id = a.bid \
3733 LEFT JOIN c ON c.id = a.cid",
3734 &t,
3735 );
3736 assert_eq!(rows.len(), 1);
3737 assert_eq!(col(&rows, "bn"), vec![json!("B")]);
3738 assert_eq!(col(&rows, "cn"), vec![json!("C")]);
3739 }
3740
3741 fn catalog() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
3745 tables(vec![
3746 (
3747 "pg_namespace",
3748 vec![
3749 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
3750 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
3751 json!({"oid": 13000, "nspname": "information_schema", "nspowner": 10}),
3752 ],
3753 ),
3754 (
3755 "pg_class",
3756 vec![
3757 json!({"oid": 16401, "relname": "orders", "relnamespace": 2200,
3758 "relkind": "r", "relowner": 10, "relam": 2}),
3759 json!({"oid": 16402, "relname": "drivers", "relnamespace": 2200,
3760 "relkind": "r", "relowner": 10, "relam": 2}),
3761 ],
3762 ),
3763 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
3764 ])
3765 }
3766
3767 #[test]
3768 fn THE_dn_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
3769 let (names, rows) = go(
3770 r#"SELECT n.nspname AS "Name",
3771 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
3772 FROM pg_catalog.pg_namespace n
3773 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
3774 ORDER BY 1;"#,
3775 &catalog(),
3776 );
3777
3778 assert_eq!(names, vec!["Name", "Owner"], "psql reads these BY NAME");
3779 assert_eq!(col(&rows, "Name"), vec![json!("public")]);
3782 assert_eq!(col(&rows, "Owner"), vec![json!("nedb")]);
3783 }
3784
3785 #[test]
3786 fn THE_dt_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
3787 let (names, rows) = go(
3788 r#"SELECT n.nspname as "Schema",
3789 c.relname as "Name",
3790 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
3791 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
3792 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
3793 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
3794 WHEN 'I' THEN 'partitioned index' END as "Type",
3795 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
3796 FROM pg_catalog.pg_class c
3797 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
3798 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
3799 WHERE c.relkind IN ('r','p','')
3800 AND n.nspname <> 'pg_catalog'
3801 AND n.nspname !~ '^pg_toast'
3802 AND n.nspname <> 'information_schema'
3803 AND pg_catalog.pg_table_is_visible(c.oid)
3804 ORDER BY 1,2;"#,
3805 &catalog(),
3806 );
3807
3808 assert_eq!(names, vec!["Schema", "Name", "Type", "Owner"]);
3809 assert_eq!(col(&rows, "Name"), vec![json!("drivers"), json!("orders")]);
3811 assert_eq!(col(&rows, "Schema"), vec![json!("public"), json!("public")]);
3812 assert_eq!(col(&rows, "Type"), vec![json!("table"), json!("table")]);
3814 assert_eq!(col(&rows, "Owner"), vec![json!("nedb"), json!("nedb")]);
3815 }
3816
3817 #[test]
3818 fn the_dt_query_still_filters_correctly_with_a_system_relation_present() {
3819 let t = tables(vec![
3823 (
3824 "pg_namespace",
3825 vec![
3826 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
3827 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
3828 ],
3829 ),
3830 (
3831 "pg_class",
3832 vec![
3833 json!({"oid": 1, "relname": "mine", "relnamespace": 2200,
3834 "relkind": "r", "relowner": 10, "relam": 2}),
3835 json!({"oid": 2, "relname": "pg_internal", "relnamespace": 11,
3836 "relkind": "r", "relowner": 10, "relam": 2}),
3837 json!({"oid": 3, "relname": "an_index", "relnamespace": 2200,
3838 "relkind": "i", "relowner": 10, "relam": 2}),
3839 ],
3840 ),
3841 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
3842 ]);
3843 let (_, rows) = go(
3844 r#"SELECT c.relname as "Name" FROM pg_catalog.pg_class c
3845 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
3846 WHERE c.relkind IN ('r','p','') AND n.nspname <> 'pg_catalog'
3847 ORDER BY 1"#,
3848 &t,
3849 );
3850 assert_eq!(col(&rows, "Name"), vec![json!("mine")],
3851 "a system relation and an index must both be filtered out");
3852 }
3853}
3854
3855#[cfg(test)]
3856mod operator_syntax_tests {
3857 use super::*;
3858 use serde_json::json;
3859
3860 #[test]
3861 fn the_OPERATOR_qualification_psql_generates_is_understood() {
3862 let s = parse(
3866 "SELECT a FROM t WHERE n OPERATOR(pg_catalog.~) '^x' \
3867 AND m OPERATOR(pg_catalog.=) 1",
3868 )
3869 .expect("psql's OPERATOR() form must parse");
3870 match s.where_.unwrap() {
3871 Expr::Binary { op, left, .. } => {
3872 assert_eq!(op, "AND");
3873 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "~"));
3874 }
3875 other => panic!("{:?}", other),
3876 }
3877 }
3878
3879 #[test]
3880 fn an_OPERATOR_qualified_comparison_EVALUATES() {
3881 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
3882 Ok(Some(from_vec(vec![json!({"n": "orders"}), json!({"n": "pg_toast_1"})])))
3883 };
3884 let (_, rows) = run(
3885 "SELECT n FROM pg_class WHERE n OPERATOR(pg_catalog.~) '^ord'",
3886 &t,
3887 )
3888 .unwrap();
3889 assert_eq!(rows.len(), 1);
3890 assert_eq!(rows[0]["n"], json!("orders"));
3891 }
3892
3893 #[test]
3894 fn a_subquery_an_ARRAY_constructor_and_EXISTS_are_all_refused_BY_NAME() {
3895 for (sql, needle) in [
3899 ("SELECT a FROM t WHERE x = (SELECT 1)", "subquery"),
3900 ("SELECT array_to_string(ARRAY(SELECT a FROM b), ',') FROM t", "ARRAY"),
3901 ("SELECT a FROM t WHERE EXISTS (SELECT 1)", "EXISTS"),
3902 ] {
3903 let e = parse(sql).unwrap_err().to_string();
3904 assert!(e.contains(needle), "{} -> {}", sql, e);
3905 }
3906 }
3907}
3908
3909#[cfg(test)]
3910mod collate_tests {
3911 use super::*;
3912 use serde_json::json;
3913
3914 #[test]
3915 fn COLLATE_is_consumed_because_it_cannot_change_the_answer() {
3916 for sql in [
3920 r#"SELECT a FROM t ORDER BY a COLLATE "C""#,
3921 r#"SELECT a COLLATE "C" FROM t"#,
3922 r#"SELECT a FROM t WHERE a COLLATE pg_catalog."C" = 'x'"#,
3923 ] {
3924 parse(sql).unwrap_or_else(|e| panic!("{} -> {}", sql, e));
3925 }
3926 assert!(parse("SELECT a FROM t ORDER BY a COLLATE").is_err());
3928 }
3929
3930 #[test]
3931 fn a_COLLATE_annotated_comparison_still_evaluates() {
3932 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
3933 Ok(Some(from_vec(vec![json!({"n": "b"}), json!({"n": "a"})])))
3934 };
3935 let (_, rows) = run(r#"SELECT n FROM pg_class ORDER BY n COLLATE "C""#, &t).unwrap();
3936 assert_eq!(rows[0]["n"], json!("a"), "the ORDER BY still sorts");
3937 }
3938}