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 Subquery(Box<Select>),
330 Exists { query: Box<Select>, negated: bool },
333 ArrayQuery(Box<Select>),
337 InSubquery { expr: Box<Expr>, query: Box<Select>, negated: bool },
339 Quantified { op: String, left: Box<Expr>, all: bool, right: Box<Expr> },
343 Index { expr: Box<Expr>, index: Box<Expr> },
345 ArrayLit(Vec<Expr>),
347 Agg {
361 name: String,
362 args: Vec<Expr>,
363 order_by: Vec<OrderBy>,
365 distinct: bool,
366 },
367}
368
369#[derive(Debug, Clone, PartialEq)]
370pub struct SelectItem {
371 pub expr: Expr,
372 pub alias: Option<String>,
375}
376
377#[derive(Debug, Clone, Copy, PartialEq, Eq)]
378pub enum JoinKind { Inner, Left, Right, Full, Cross }
379
380#[derive(Debug, Clone, PartialEq)]
381pub struct TableRef {
382 pub name: String,
390 pub alias: Option<String>,
391 pub sub: Option<Box<Select>>,
394 pub args: Option<Vec<Expr>>,
398 pub col_aliases: Vec<String>,
400 pub lateral: bool,
404}
405
406impl TableRef {
407 pub fn named(name: impl Into<String>, alias: Option<String>) -> Self {
409 TableRef { name: name.into(), alias, sub: None, args: None, col_aliases: vec![], lateral: false }
410 }
411
412 pub fn binding(&self) -> String {
415 self.alias.clone().unwrap_or_else(|| {
416 self.name.rsplit('.').next().unwrap_or(&self.name).to_string()
417 })
418 }
419}
420
421#[derive(Debug, Clone, Copy, PartialEq, Eq)]
423pub enum SetOp { Union, Intersect, Except }
424
425#[derive(Debug, Clone, PartialEq)]
427pub struct SetArm {
428 pub op: SetOp,
429 pub all: bool,
430 pub query: Select,
431}
432
433#[derive(Debug, Clone, PartialEq)]
434pub struct Join {
435 pub kind: JoinKind,
436 pub table: TableRef,
437 pub on: Option<Expr>,
438}
439
440#[derive(Debug, Clone, Copy, PartialEq)]
441pub enum Dir { Asc, Desc }
442
443#[derive(Debug, Clone, PartialEq)]
444pub struct OrderBy {
445 pub ordinal: Option<usize>,
449 pub expr: Option<Expr>,
450 pub dir: Dir,
451 pub nulls_first: bool,
453}
454
455#[derive(Debug, Clone, PartialEq)]
456pub struct Select {
457 pub distinct: bool,
458 pub items: Vec<SelectItem>,
459 pub from: Option<TableRef>,
460 pub joins: Vec<Join>,
461 pub where_: Option<Expr>,
462 pub group_by: Vec<Expr>,
465 pub having: Option<Expr>,
468 pub order_by: Vec<OrderBy>,
472 pub limit: Option<usize>,
473 pub offset: Option<usize>,
474 pub set_ops: Vec<SetArm>,
477}
478
479impl Select {
480 pub fn base_relations(&self) -> Vec<String> {
489 let mut out = vec![];
490 self.collect_relations(&mut out);
491 out
492 }
493
494 fn collect_relations(&self, out: &mut Vec<String>) {
495 fn table(t: &TableRef, out: &mut Vec<String>) {
496 if let Some(sub) = &t.sub {
497 sub.collect_relations(out);
498 } else if let Some(args) = &t.args {
499 for a in args {
500 expr(a, out);
501 }
502 } else {
503 out.push(t.name.clone());
504 }
505 }
506 fn expr(e: &Expr, out: &mut Vec<String>) {
507 match e {
508 Expr::Subquery(q) | Expr::ArrayQuery(q) => q.collect_relations(out),
509 Expr::Exists { query, .. } => query.collect_relations(out),
510 Expr::InSubquery { expr: x, query, .. } => {
511 expr(x, out);
512 query.collect_relations(out);
513 }
514 Expr::Quantified { left, right, .. } => {
515 expr(left, out);
516 expr(right, out);
517 }
518 Expr::Index { expr: x, index } => {
519 expr(x, out);
520 expr(index, out);
521 }
522 Expr::ArrayLit(items) | Expr::InList { list: items, .. } => {
523 if let Expr::InList { expr: x, .. } = e {
524 expr(x, out);
525 }
526 for i in items {
527 expr(i, out);
528 }
529 }
530 Expr::Func { args, .. } => {
531 for a in args {
532 expr(a, out);
533 }
534 }
535 Expr::Agg { args, order_by, .. } => {
536 for a in args {
537 expr(a, out);
538 }
539 for ob in order_by {
540 if let Some(e) = &ob.expr {
541 expr(e, out);
542 }
543 }
544 }
545 Expr::Case { operand, whens, else_ } => {
546 if let Some(o) = operand {
547 expr(o, out);
548 }
549 for (w, t) in whens {
550 expr(w, out);
551 expr(t, out);
552 }
553 if let Some(x) = else_ {
554 expr(x, out);
555 }
556 }
557 Expr::Binary { left, right, .. } => {
558 expr(left, out);
559 expr(right, out);
560 }
561 Expr::Unary { expr: x, .. } | Expr::Cast { expr: x, .. } | Expr::IsNull { expr: x, .. } => {
562 expr(x, out)
563 }
564 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => {}
565 }
566 }
567 if let Some(f) = &self.from {
568 table(f, out);
569 }
570 for j in &self.joins {
571 table(&j.table, out);
572 if let Some(on) = &j.on {
573 expr(on, out);
574 }
575 }
576 for item in &self.items {
577 expr(&item.expr, out);
578 }
579 if let Some(w) = &self.where_ {
580 expr(w, out);
581 }
582 for g in &self.group_by {
583 expr(g, out);
584 }
585 if let Some(h) = &self.having {
586 expr(h, out);
587 }
588 for ob in &self.order_by {
589 if let Some(e) = &ob.expr {
590 expr(e, out);
591 }
592 }
593 for arm in &self.set_ops {
594 arm.query.collect_relations(out);
595 }
596 }
597}
598
599fn binding_power(op: &str) -> Option<u8> {
610 Some(match op {
611 "OR" => 1,
612 "AND" => 2,
613 "=" | "!=" | "<>" | "<" | "<=" | ">" | ">=" | "~" | "~*" | "!~" | "!~*"
617 | "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => 4,
618 "||" => 5,
619 "+" | "-" => 6,
620 "*" | "/" | "%" => 7,
621 _ => return None,
622 })
623}
624
625struct Parser {
626 toks: Vec<Tok>,
627 pos: usize,
628}
629
630impl Parser {
631 fn peek(&self) -> &Tok {
632 self.toks.get(self.pos).unwrap_or(&Tok::Eof)
633 }
634 fn peek_at(&self, n: usize) -> &Tok {
635 self.toks.get(self.pos + n).unwrap_or(&Tok::Eof)
636 }
637 fn next(&mut self) -> Tok {
638 let t = self.peek().clone();
639 self.pos += 1;
640 t
641 }
642 fn eat_kw(&mut self, kw: &str) -> bool {
643 if self.peek().is_kw(kw) {
644 self.pos += 1;
645 true
646 } else {
647 false
648 }
649 }
650 fn expect_kw(&mut self, kw: &str) -> Result<()> {
651 if self.eat_kw(kw) {
652 Ok(())
653 } else {
654 bail!("expected {} , got {:?}", kw, self.peek())
655 }
656 }
657 fn eat_punct(&mut self, c: char) -> bool {
658 if matches!(self.peek(), Tok::Punct(p) if *p == c) {
659 self.pos += 1;
660 true
661 } else {
662 false
663 }
664 }
665 fn expect_punct(&mut self, c: char) -> Result<()> {
666 if self.eat_punct(c) {
667 Ok(())
668 } else {
669 bail!("expected {:?}, got {:?}", c, self.peek())
670 }
671 }
672 fn eat_op(&mut self, op: &str) -> bool {
673 if matches!(self.peek(), Tok::Op(o) if o == op) {
674 self.pos += 1;
675 true
676 } else {
677 false
678 }
679 }
680
681 fn parse_expr(&mut self) -> Result<Expr> {
684 self.parse_bin(0)
685 }
686
687 fn parse_bin(&mut self, min_bp: u8) -> Result<Expr> {
690 let mut left = self.parse_unary()?;
691
692 loop {
693 if self.peek().is_kw("OPERATOR") && matches!(self.peek_at(1), Tok::Punct('(')) {
700 let save = self.pos;
701 self.pos += 2;
702 let mut sym = None;
704 while sym.is_none() {
705 match self.next() {
706 Tok::Op(o) => sym = Some(o),
707 Tok::Word { .. } | Tok::Punct('.') => continue,
708 _ => break,
709 }
710 }
711 match sym {
712 Some(o) if binding_power(&o).is_some() && self.eat_punct(')') => {
713 let bp = binding_power(&o).unwrap();
714 if bp < min_bp {
715 self.pos = save;
716 break;
717 }
718 let right = self.parse_bin(bp + 1)?;
719 left = Expr::Binary {
720 op: o,
721 left: Box::new(left),
722 right: Box::new(right),
723 };
724 continue;
725 }
726 _ => {
729 self.pos = save;
730 break;
731 }
732 }
733 }
734
735 let (op, width) = match self.peek() {
736 Tok::Op(o) if binding_power(o).is_some() => (o.clone(), 1usize),
737 Tok::Word { upper, .. } if upper == "AND" || upper == "OR" => (upper.clone(), 1),
738 Tok::Word { upper, .. } if upper == "LIKE" || upper == "ILIKE" => (upper.clone(), 1),
739 Tok::Word { upper, .. } if upper == "NOT" => {
740 match self.peek_at(1) {
742 Tok::Word { upper: u2, .. } if u2 == "LIKE" || u2 == "ILIKE" => {
743 (format!("NOT {}", u2), 2)
744 }
745 _ => break,
746 }
747 }
748 _ => break,
749 };
750
751 let bp = match binding_power(&op) {
752 Some(bp) if bp >= min_bp => bp,
753 _ => break,
754 };
755 self.pos += width;
756
757 let quant = match self.peek() {
763 Tok::Word { upper, .. }
764 if matches!(upper.as_str(), "ANY" | "SOME" | "ALL")
765 && matches!(self.peek_at(1), Tok::Punct('(')) =>
766 {
767 Some(upper == "ALL")
768 }
769 _ => None,
770 };
771 if let Some(all) = quant {
772 self.pos += 2; let right = if self.peek().is_kw("SELECT") {
774 Expr::ArrayQuery(Box::new(self.parse_query()?))
775 } else {
776 self.parse_expr()?
777 };
778 self.expect_punct(')')?;
779 left = Expr::Quantified { op, left: Box::new(left), all, right: Box::new(right) };
780 continue;
781 }
782
783 let right = self.parse_bin(bp + 1)?;
785 left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
786 }
787
788 Ok(left)
789 }
790
791 fn parse_postfix(&mut self, mut e: Expr) -> Result<Expr> {
792 loop {
793 if matches!(self.peek(), Tok::Punct('[')) {
796 self.pos += 1;
797 let index = self.parse_expr()?;
798 self.expect_punct(']')?;
799 e = Expr::Index { expr: Box::new(e), index: Box::new(index) };
800 continue;
801 }
802
803 if self.peek().is_kw("IS") {
805 self.pos += 1;
806 let negated = self.eat_kw("NOT");
807 if self.eat_kw("DISTINCT") {
811 self.expect_kw("FROM")?;
812 let rhs = self.parse_bin(5)?;
814 e = Expr::Binary {
815 op: if negated { "IS NOT DISTINCT FROM".into() } else { "IS DISTINCT FROM".into() },
816 left: Box::new(e),
817 right: Box::new(rhs),
818 };
819 continue;
820 }
821 if !self.eat_kw("NULL") {
822 if self.eat_kw("TRUE") {
824 e = Expr::Binary {
825 op: "=".into(),
826 left: Box::new(e),
827 right: Box::new(Expr::Literal(Value::Bool(!negated))),
828 };
829 continue;
830 }
831 if self.eat_kw("FALSE") {
832 e = Expr::Binary {
833 op: "=".into(),
834 left: Box::new(e),
835 right: Box::new(Expr::Literal(Value::Bool(negated))),
836 };
837 continue;
838 }
839 bail!("expected NULL, TRUE or FALSE after IS, got {:?}", self.peek());
840 }
841 e = Expr::IsNull { expr: Box::new(e), negated };
842 continue;
843 }
844
845 let negated_in = if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("IN") {
847 self.pos += 2;
848 true
849 } else if self.peek().is_kw("IN") {
850 self.pos += 1;
851 false
852 } else {
853 let negated_between =
855 if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("BETWEEN") {
856 self.pos += 2;
857 true
858 } else if self.peek().is_kw("BETWEEN") {
859 self.pos += 1;
860 false
861 } else {
862 break;
863 };
864 let low = self.parse_bin(3)?;
868 self.expect_kw("AND")?;
869 let high = self.parse_bin(3)?;
870 let ge = Expr::Binary {
871 op: ">=".into(),
872 left: Box::new(e.clone()),
873 right: Box::new(low),
874 };
875 let le = Expr::Binary {
876 op: "<=".into(),
877 left: Box::new(e),
878 right: Box::new(high),
879 };
880 let both = Expr::Binary {
881 op: "AND".into(),
882 left: Box::new(ge),
883 right: Box::new(le),
884 };
885 e = if negated_between {
886 Expr::Unary { op: "NOT".into(), expr: Box::new(both) }
887 } else {
888 both
889 };
890 continue;
891 };
892
893 self.expect_punct('(')?;
894 if self.peek().is_kw("SELECT") {
896 let query = Box::new(self.parse_query()?);
897 self.expect_punct(')')?;
898 e = Expr::InSubquery { expr: Box::new(e), query, negated: negated_in };
899 continue;
900 }
901 let mut list = vec![];
902 if !self.eat_punct(')') {
903 loop {
904 list.push(self.parse_expr()?);
905 if self.eat_punct(',') {
906 continue;
907 }
908 self.expect_punct(')')?;
909 break;
910 }
911 }
912 e = Expr::InList { expr: Box::new(e), list, negated: negated_in };
913 }
914 Ok(e)
915 }
916
917 fn parse_unary(&mut self) -> Result<Expr> {
918 if self.peek().is_kw("NOT") {
919 self.pos += 1;
920 let e = self.parse_bin(3)?;
923 return Ok(Expr::Unary { op: "NOT".into(), expr: Box::new(e) });
924 }
925 if self.eat_op("-") {
926 let e = self.parse_unary()?;
927 return Ok(Expr::Unary { op: "-".into(), expr: Box::new(e) });
928 }
929 if self.eat_op("+") {
930 return self.parse_unary();
931 }
932 let atom = self.parse_atom()?;
933 let cast = self.parse_casts(atom)?;
934 self.parse_postfix(cast)
945 }
946
947 fn parse_casts(&mut self, mut e: Expr) -> Result<Expr> {
949 loop {
950 if self.peek().is_kw("COLLATE") {
955 self.pos += 1;
956 match self.next() {
957 Tok::Word { .. } | Tok::Quoted(_) => {}
958 other => bail!("expected a collation name after COLLATE, got {:?}", other),
959 }
960 while self.eat_punct('.') {
962 match self.next() {
963 Tok::Word { .. } | Tok::Quoted(_) => {}
964 other => bail!("expected a name after '.', got {:?}", other),
965 }
966 }
967 continue;
968 }
969 if !self.eat_op("::") {
970 break;
971 }
972 let mut ty = match self.next() {
973 Tok::Word { raw, .. } => raw,
974 Tok::Quoted(s) => s,
975 other => bail!("expected a type name after ::, got {:?}", other),
976 };
977 while self.eat_punct('.') {
979 match self.next() {
980 Tok::Word { raw, .. } => ty = raw,
981 Tok::Quoted(s) => ty = s,
982 other => bail!("expected a type name after ., got {:?}", other),
983 }
984 }
985 while self.eat_punct('[') {
987 self.expect_punct(']')?;
988 ty.push_str("[]");
989 }
990 e = Expr::Cast { expr: Box::new(e), ty };
991 }
992 Ok(e)
993 }
994
995
996 fn parse_atom(&mut self) -> Result<Expr> {
997 if self.eat_punct('(') {
1004 if self.peek().is_kw("SELECT") {
1007 let q = self.parse_query()?;
1008 self.expect_punct(')')?;
1009 return Ok(Expr::Subquery(Box::new(q)));
1010 }
1011 let e = self.parse_expr()?;
1012 self.expect_punct(')')?;
1013 return Ok(e);
1014 }
1015
1016 if self.peek().is_kw("ARRAY") && matches!(self.peek_at(1), Tok::Punct('(') | Tok::Punct('[')) {
1020 self.pos += 1;
1021 if self.eat_punct('(') {
1022 if !self.peek().is_kw("SELECT") {
1023 bail!("ARRAY(...) takes a subquery; for a list of values write ARRAY[...]");
1024 }
1025 let q = self.parse_query()?;
1026 self.expect_punct(')')?;
1027 return Ok(Expr::ArrayQuery(Box::new(q)));
1028 }
1029 self.expect_punct('[')?;
1030 let mut items = vec![];
1031 if !self.eat_punct(']') {
1032 loop {
1033 items.push(self.parse_expr()?);
1034 if self.eat_punct(',') {
1035 continue;
1036 }
1037 self.expect_punct(']')?;
1038 break;
1039 }
1040 }
1041 return Ok(Expr::ArrayLit(items));
1042 }
1043
1044 if self.peek().is_kw("EXISTS") && matches!(self.peek_at(1), Tok::Punct('(')) {
1048 self.pos += 2;
1049 if !self.peek().is_kw("SELECT") {
1050 bail!("EXISTS (...) takes a subquery");
1051 }
1052 let q = self.parse_query()?;
1053 self.expect_punct(')')?;
1054 return Ok(Expr::Exists { query: Box::new(q), negated: false });
1055 }
1056
1057 if self.peek().is_kw("CAST") && matches!(self.peek_at(1), Tok::Punct('(')) {
1060 self.pos += 2;
1061 let inner = self.parse_expr()?;
1062 self.expect_kw("AS")?;
1063 let mut ty = match self.next() {
1064 Tok::Word { raw, .. } => raw,
1065 Tok::Quoted(s) => s,
1066 other => bail!("expected a type name in CAST, got {:?}", other),
1067 };
1068 while self.eat_punct('.') {
1069 match self.next() {
1070 Tok::Word { raw, .. } => ty = raw,
1071 Tok::Quoted(s) => ty = s,
1072 other => bail!("expected a type name after ., got {:?}", other),
1073 }
1074 }
1075 while self.eat_punct('[') {
1076 self.expect_punct(']')?;
1077 ty.push_str("[]");
1078 }
1079 self.expect_punct(')')?;
1080 return Ok(Expr::Cast { expr: Box::new(inner), ty });
1081 }
1082
1083 if self.peek().is_kw("CASE") {
1085 return self.parse_case();
1086 }
1087
1088 match self.next() {
1089 Tok::Num(n) => Ok(Expr::Literal(from_f64(n))),
1090 Tok::Str(s) => Ok(Expr::Literal(Value::String(s))),
1091 Tok::Op(o) if o == "*" => Ok(Expr::Star),
1092 Tok::Quoted(name) => self.parse_name_tail(None, name),
1093 Tok::Word { upper, raw } => match upper.as_str() {
1094 "NULL" => Ok(Expr::Literal(Value::Null)),
1095 "TRUE" => Ok(Expr::Literal(Value::Bool(true))),
1096 "FALSE" => Ok(Expr::Literal(Value::Bool(false))),
1097 "CURRENT_SCHEMA" | "CURRENT_DATABASE" | "CURRENT_USER" | "SESSION_USER"
1101 | "CURRENT_CATALOG" | "USER" | "VERSION"
1102 if !matches!(self.peek(), Tok::Punct('(')) =>
1103 {
1104 Ok(Expr::Func { name: upper.to_lowercase(), args: vec![] })
1105 }
1106 _ => self.parse_name_tail(None, raw),
1107 },
1108 other => bail!("unexpected {:?} in an expression", other),
1109 }
1110 }
1111
1112 fn parse_name_tail(&mut self, _schema: Option<String>, first: String) -> Result<Expr> {
1119 let mut parts = vec![first];
1120 while self.eat_punct('.') {
1121 if self.eat_op("*") {
1123 return Ok(Expr::QualifiedStar(parts.pop().unwrap_or_default()));
1124 }
1125 match self.next() {
1126 Tok::Word { raw, .. } => parts.push(raw),
1127 Tok::Quoted(s) => parts.push(s),
1128 other => bail!("expected a name after '.', got {:?}", other),
1129 }
1130 }
1131
1132 if matches!(self.peek(), Tok::Punct('(')) {
1136 self.pos += 1;
1137 let name = parts.pop().unwrap_or_default().to_lowercase();
1138 let agg = is_aggregate(&name);
1139 let distinct = agg && self.eat_kw("DISTINCT");
1141 let mut args = vec![];
1142 let mut order_by = vec![];
1143 if !self.eat_punct(')') {
1144 loop {
1145 if self.eat_op("*") {
1147 args.push(Expr::Star);
1148 } else {
1149 args.push(self.parse_expr()?);
1150 }
1151 if self.eat_punct(',') {
1152 continue;
1153 }
1154 if agg && self.peek().is_kw("ORDER") {
1158 self.pos += 1;
1159 self.expect_kw("BY")?;
1160 order_by = self.parse_sort_list()?;
1161 }
1162 self.expect_punct(')')?;
1163 break;
1164 }
1165 }
1166 if agg {
1167 return Ok(Expr::Agg { name, args, order_by, distinct });
1168 }
1169 return Ok(Expr::Func { name, args });
1170 }
1171
1172 let name = parts.pop().unwrap_or_default();
1173 let qual = parts.pop();
1177 Ok(Expr::Column { qual, name })
1178 }
1179
1180 fn parse_case(&mut self) -> Result<Expr> {
1181 self.expect_kw("CASE")?;
1182 let operand = if self.peek().is_kw("WHEN") {
1184 None
1185 } else {
1186 Some(Box::new(self.parse_expr()?))
1187 };
1188 let mut whens = vec![];
1189 while self.eat_kw("WHEN") {
1190 let cond = self.parse_expr()?;
1191 self.expect_kw("THEN")?;
1192 let then = self.parse_expr()?;
1193 whens.push((cond, then));
1194 }
1195 if whens.is_empty() {
1196 bail!("CASE needs at least one WHEN branch");
1197 }
1198 let else_ = if self.eat_kw("ELSE") {
1199 Some(Box::new(self.parse_expr()?))
1200 } else {
1201 None
1202 };
1203 self.expect_kw("END")?;
1204 Ok(Expr::Case { operand, whens, else_ })
1205 }
1206
1207 fn parse_table_ref(&mut self) -> Result<TableRef> {
1210 let lateral = self.eat_kw("LATERAL");
1211 if self.eat_punct('(') {
1213 if !self.peek().is_kw("SELECT") {
1214 bail!("expected a subquery after '(' in FROM, got {:?}", self.peek());
1215 }
1216 let sub = self.parse_query()?;
1217 self.expect_punct(')')?;
1218 let (alias, col_aliases) = self.parse_table_alias()?;
1219 if alias.is_none() {
1220 bail!("a subquery in FROM must have an alias");
1221 }
1222 return Ok(TableRef {
1223 name: "(subquery)".into(),
1224 alias,
1225 sub: Some(Box::new(sub)),
1226 args: None,
1227 col_aliases,
1228 lateral,
1229 });
1230 }
1231 if lateral {
1232 bail!("LATERAL applies to a subquery in FROM; write LATERAL (SELECT ...)");
1233 }
1234
1235 let mut parts = vec![match self.next() {
1236 Tok::Word { raw, .. } => raw,
1237 Tok::Quoted(s) => s,
1238 other => bail!("expected a table name, got {:?}", other),
1239 }];
1240 while self.eat_punct('.') {
1241 match self.next() {
1242 Tok::Word { raw, .. } => parts.push(raw),
1243 Tok::Quoted(s) => parts.push(s),
1244 other => bail!("expected a name after '.', got {:?}", other),
1245 }
1246 }
1247 let name = parts.join(".");
1248
1249 if self.eat_punct('(') {
1252 let mut args = vec![];
1253 if !self.eat_punct(')') {
1254 loop {
1255 args.push(self.parse_expr()?);
1256 if self.eat_punct(',') {
1257 continue;
1258 }
1259 self.expect_punct(')')?;
1260 break;
1261 }
1262 }
1263 let fname = name.rsplit('.').next().unwrap_or(&name).to_lowercase();
1264 let (alias, col_aliases) = self.parse_table_alias()?;
1265 return Ok(TableRef { name: fname, alias, sub: None, args: Some(args), col_aliases, lateral: false });
1266 }
1267
1268 let (alias, col_aliases) = self.parse_table_alias()?;
1269 Ok(TableRef { name, alias, sub: None, args: None, col_aliases, lateral: false })
1270 }
1271
1272 fn parse_table_alias(&mut self) -> Result<(Option<String>, Vec<String>)> {
1277 let alias = if self.eat_kw("AS") {
1278 match self.next() {
1279 Tok::Word { raw, .. } => Some(raw),
1280 Tok::Quoted(s) => Some(s),
1281 other => bail!("expected an alias after AS, got {:?}", other),
1282 }
1283 } else {
1284 match self.peek().clone() {
1285 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1286 self.pos += 1;
1287 Some(raw)
1288 }
1289 Tok::Quoted(s) => {
1290 self.pos += 1;
1291 Some(s)
1292 }
1293 _ => None,
1294 }
1295 };
1296 let mut col_aliases = vec![];
1297 if alias.is_some() && self.eat_punct('(') {
1298 loop {
1299 match self.next() {
1300 Tok::Word { raw, .. } => col_aliases.push(raw),
1301 Tok::Quoted(s) => col_aliases.push(s),
1302 other => bail!("expected a column alias, got {:?}", other),
1303 }
1304 if self.eat_punct(',') {
1305 continue;
1306 }
1307 self.expect_punct(')')?;
1308 break;
1309 }
1310 }
1311 Ok((alias, col_aliases))
1312 }
1313
1314 fn parse_query(&mut self) -> Result<Select> {
1321 let mut first = self.parse_select_body()?;
1322 loop {
1323 let op = if self.eat_kw("UNION") {
1324 SetOp::Union
1325 } else if self.eat_kw("INTERSECT") {
1326 SetOp::Intersect
1327 } else if self.eat_kw("EXCEPT") {
1328 SetOp::Except
1329 } else {
1330 break;
1331 };
1332 let all = self.eat_kw("ALL");
1333 if !all {
1334 let _ = self.eat_kw("DISTINCT");
1335 }
1336 let query = if self.eat_punct('(') {
1338 let q = self.parse_query()?;
1339 self.expect_punct(')')?;
1340 q
1341 } else {
1342 self.parse_select_body()?
1343 };
1344 first.set_ops.push(SetArm { op, all, query });
1345 }
1346 self.parse_query_tail(&mut first)?;
1347 Ok(first)
1348 }
1349
1350 fn parse_sort_list(&mut self) -> Result<Vec<OrderBy>> {
1354 let mut out = vec![];
1355 loop {
1356 let (ordinal, expr) = match self.peek().clone() {
1360 Tok::Num(n)
1361 if n.fract() == 0.0
1362 && n >= 1.0
1363 && !matches!(self.peek_at(1), Tok::Op(_)) =>
1364 {
1365 self.pos += 1;
1366 (Some(n as usize), None)
1367 }
1368 _ => (None, Some(self.parse_expr()?)),
1369 };
1370 let dir = if self.eat_kw("DESC") {
1371 Dir::Desc
1372 } else {
1373 let _ = self.eat_kw("ASC");
1374 Dir::Asc
1375 };
1376 let mut nulls_first = matches!(dir, Dir::Desc);
1378 if self.eat_kw("NULLS") {
1379 if self.eat_kw("FIRST") {
1380 nulls_first = true;
1381 } else if self.eat_kw("LAST") {
1382 nulls_first = false;
1383 } else {
1384 bail!("expected FIRST or LAST after NULLS, got {:?}", self.peek());
1385 }
1386 }
1387 out.push(OrderBy { ordinal, expr, dir, nulls_first });
1388 if self.eat_punct(',') {
1389 continue;
1390 }
1391 break;
1392 }
1393 Ok(out)
1394 }
1395
1396 fn parse_query_tail(&mut self, sel: &mut Select) -> Result<()> {
1397 let mut order_by = vec![];
1398 if self.eat_kw("ORDER") {
1399 self.expect_kw("BY")?;
1400 order_by = self.parse_sort_list()?;
1401 }
1402
1403 let mut limit = None;
1404 let mut offset = None;
1405 loop {
1407 if self.eat_kw("LIMIT") {
1408 if self.eat_kw("ALL") {
1409 limit = None;
1410 } else {
1411 limit = Some(self.parse_count("LIMIT")?);
1412 }
1413 continue;
1414 }
1415 if self.eat_kw("OFFSET") {
1416 offset = Some(self.parse_count("OFFSET")?);
1417 let _ = self.eat_kw("ROW") || self.eat_kw("ROWS");
1418 continue;
1419 }
1420 break;
1421 }
1422 sel.order_by = order_by;
1423 sel.limit = limit;
1424 sel.offset = offset;
1425 Ok(())
1426 }
1427
1428 fn parse_select_body(&mut self) -> Result<Select> {
1430 self.expect_kw("SELECT")?;
1431 let distinct = self.eat_kw("DISTINCT");
1432 if distinct && self.peek().is_kw("ON") {
1433 bail!("DISTINCT ON is not supported");
1434 }
1435 let _ = self.eat_kw("ALL");
1436
1437 let mut items = vec![];
1438 loop {
1439 let expr = self.parse_expr()?;
1440 let alias = if self.eat_kw("AS") {
1442 match self.next() {
1443 Tok::Word { raw, .. } => Some(raw),
1444 Tok::Quoted(s) => Some(s),
1445 other => bail!("expected an alias after AS, got {:?}", other),
1446 }
1447 } else {
1448 match self.peek().clone() {
1449 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1450 self.pos += 1;
1451 Some(raw)
1452 }
1453 Tok::Quoted(s) => {
1454 self.pos += 1;
1455 Some(s)
1456 }
1457 _ => None,
1458 }
1459 };
1460 items.push(SelectItem { expr, alias });
1461 if self.eat_punct(',') {
1462 continue;
1463 }
1464 break;
1465 }
1466
1467 let mut from = None;
1468 let mut joins = vec![];
1469 if self.eat_kw("FROM") {
1470 from = Some(self.parse_table_ref()?);
1471 loop {
1472 if self.eat_punct(',') {
1478 let table = self.parse_table_ref()?;
1479 joins.push(Join { kind: JoinKind::Cross, table, on: None });
1480 continue;
1481 }
1482 let kind = if self.peek().is_kw("JOIN") {
1483 self.pos += 1;
1484 JoinKind::Inner
1485 } else if self.peek().is_kw("INNER") && self.peek_at(1).is_kw("JOIN") {
1486 self.pos += 2;
1487 JoinKind::Inner
1488 } else if self.peek().is_kw("CROSS") && self.peek_at(1).is_kw("JOIN") {
1489 self.pos += 2;
1490 JoinKind::Cross
1491 } else if self.peek().is_kw("LEFT") {
1492 self.pos += 1;
1493 let _ = self.eat_kw("OUTER");
1494 self.expect_kw("JOIN")?;
1495 JoinKind::Left
1496 } else if self.peek().is_kw("RIGHT") {
1497 self.pos += 1;
1498 let _ = self.eat_kw("OUTER");
1499 self.expect_kw("JOIN")?;
1500 JoinKind::Right
1501 } else if self.peek().is_kw("FULL") {
1502 self.pos += 1;
1503 let _ = self.eat_kw("OUTER");
1504 self.expect_kw("JOIN")?;
1505 JoinKind::Full
1506 } else {
1507 break;
1508 };
1509 let table = self.parse_table_ref()?;
1510 let on = if self.eat_kw("ON") {
1511 Some(self.parse_expr()?)
1512 } else if self.peek().is_kw("USING") {
1513 bail!("JOIN ... USING is not supported — write ON a.col = b.col");
1514 } else {
1515 None
1516 };
1517 if on.is_none() && !matches!(kind, JoinKind::Cross) {
1518 bail!("a {:?} JOIN needs an ON clause", kind);
1519 }
1520 joins.push(Join { kind, table, on });
1521 }
1522 }
1523
1524 let where_ = if self.eat_kw("WHERE") {
1525 Some(self.parse_expr()?)
1526 } else {
1527 None
1528 };
1529
1530 let mut group_by = vec![];
1531 if self.eat_kw("GROUP") {
1532 self.expect_kw("BY")?;
1533 if self.eat_kw("ALL") || self.eat_kw("DISTINCT") {
1534 bail!("GROUP BY ALL / DISTINCT is not supported — list the keys");
1535 }
1536 loop {
1537 if self.peek().is_kw("ROLLUP")
1538 || self.peek().is_kw("CUBE")
1539 || self.peek().is_kw("GROUPING")
1540 {
1541 bail!("GROUP BY ROLLUP / CUBE / GROUPING SETS is not supported");
1542 }
1543 group_by.push(self.parse_expr()?);
1544 if self.eat_punct(',') {
1545 continue;
1546 }
1547 break;
1548 }
1549 }
1550
1551 let having = if self.eat_kw("HAVING") {
1552 Some(self.parse_expr()?)
1553 } else {
1554 None
1555 };
1556 if having.is_some() && group_by.is_empty() && !items.iter().any(|i| has_aggregate(&i.expr))
1557 {
1558 bail!("HAVING needs a GROUP BY or an aggregate — it filters groups, not rows; \
1559 use WHERE to filter rows");
1560 }
1561
1562 Ok(Select {
1563 distinct,
1564 items,
1565 from,
1566 joins,
1567 where_,
1568 group_by,
1569 having,
1570 order_by: vec![],
1571 limit: None,
1572 offset: None,
1573 set_ops: vec![],
1574 })
1575 }
1576
1577 fn parse_count(&mut self, what: &str) -> Result<usize> {
1578 match self.next() {
1579 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Ok(n as usize),
1580 other => bail!("{} expects a non-negative integer, got {:?}", what, other),
1581 }
1582 }
1583}
1584
1585fn is_clause_keyword(upper: &str) -> bool {
1590 matches!(
1591 upper,
1592 "FROM" | "WHERE" | "GROUP" | "HAVING" | "ORDER" | "LIMIT" | "OFFSET"
1593 | "JOIN" | "LEFT" | "RIGHT" | "FULL" | "INNER" | "CROSS" | "OUTER"
1594 | "ON" | "USING" | "AND" | "OR" | "AS" | "UNION" | "INTERSECT"
1595 | "EXCEPT" | "FETCH" | "FOR" | "WINDOW" | "RETURNING" | "INTO"
1596 | "ASC" | "DESC" | "NULLS" | "IS" | "IN" | "NOT" | "LIKE" | "ILIKE"
1597 | "BETWEEN" | "THEN" | "WHEN" | "ELSE" | "END" | "CASE" | "DISTINCT"
1598 | "SELECT" | "WITH" | "ALL"
1599 )
1600}
1601
1602pub fn parse(sql: &str) -> Result<Select> {
1604 let toks = lex(sql)?;
1605 let mut p = Parser { toks, pos: 0 };
1606 let sel = if matches!(p.peek(), Tok::Punct('(')) && p.peek_at(1).is_kw("SELECT") {
1608 p.pos += 1;
1609 let mut first = p.parse_query()?;
1610 p.expect_punct(')')?;
1611 loop {
1613 let op = if p.eat_kw("UNION") {
1614 SetOp::Union
1615 } else if p.eat_kw("INTERSECT") {
1616 SetOp::Intersect
1617 } else if p.eat_kw("EXCEPT") {
1618 SetOp::Except
1619 } else {
1620 break;
1621 };
1622 let all = p.eat_kw("ALL");
1623 if !all {
1624 let _ = p.eat_kw("DISTINCT");
1625 }
1626 let query = if p.eat_punct('(') {
1627 let q = p.parse_query()?;
1628 p.expect_punct(')')?;
1629 q
1630 } else {
1631 p.parse_select_body()?
1632 };
1633 first.set_ops.push(SetArm { op, all, query });
1634 }
1635 p.parse_query_tail(&mut first)?;
1636 first
1637 } else {
1638 p.parse_query()?
1639 };
1640 let _ = p.eat_punct(';');
1641 if !matches!(p.peek(), Tok::Eof) {
1642 bail!("unexpected trailing tokens: {:?}", p.peek());
1643 }
1644 Ok(sel)
1645}
1646
1647pub struct Bound<'a> {
1660 pub parts: Vec<(String, Option<&'a Value>)>,
1661 pub ctx: EvalCtx<'a>,
1663}
1664
1665#[derive(Clone, Copy, Default)]
1678pub struct EvalCtx<'a> {
1679 pub resolver: Option<&'a Resolver<'a>>,
1680 pub outer: Option<&'a Bound<'a>>,
1681}
1682
1683impl<'a> Bound<'a> {
1684 pub fn new(parts: Vec<(String, Option<&'a Value>)>) -> Self {
1686 Bound { parts, ctx: EvalCtx::default() }
1687 }
1688
1689 fn column(&self, qual: Option<&str>, name: &str) -> Value {
1696 match qual {
1697 Some(q) => {
1698 for (binding, row) in &self.parts {
1699 if binding.eq_ignore_ascii_case(q) {
1700 return row
1701 .and_then(|r| r.get(name))
1702 .cloned()
1703 .unwrap_or(Value::Null);
1704 }
1705 }
1706 match self.ctx.outer {
1707 Some(o) if o.has_binding(q) => o.column(qual, name),
1708 _ => Value::Null,
1709 }
1710 }
1711 None => {
1712 for (_, row) in &self.parts {
1713 if let Some(v) = row.and_then(|r| r.get(name)) {
1714 return v.clone();
1715 }
1716 }
1717 match self.ctx.outer {
1718 Some(o) => o.column(None, name),
1719 None => Value::Null,
1720 }
1721 }
1722 }
1723 }
1724
1725 fn has_binding(&self, qual: &str) -> bool {
1730 self.parts.iter().any(|(b, _)| b.eq_ignore_ascii_case(qual))
1731 || self.ctx.outer.is_some_and(|o| o.has_binding(qual))
1732 }
1733
1734 fn flatten(&self) -> Vec<(String, Value)> {
1736 let mut out = vec![];
1737 for (_, row) in &self.parts {
1738 if let Some(Value::Object(m)) = row {
1739 for (k, v) in m {
1740 out.push((k.clone(), v.clone()));
1741 }
1742 }
1743 }
1744 out
1745 }
1746
1747 fn flatten_binding(&self, qual: &str) -> Vec<(String, Value)> {
1748 let mut out = vec![];
1749 for (binding, row) in &self.parts {
1750 if binding.eq_ignore_ascii_case(qual) {
1751 if let Some(Value::Object(m)) = row {
1752 for (k, v) in m {
1753 out.push((k.clone(), v.clone()));
1754 }
1755 }
1756 }
1757 }
1758 out
1759 }
1760}
1761
1762type Truth = Option<bool>;
1770
1771fn truthy(v: &Value) -> Truth {
1772 match v {
1773 Value::Null => None,
1774 Value::Bool(b) => Some(*b),
1775 _ => None,
1779 }
1780}
1781
1782fn cmp_values(a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
1788 use std::cmp::Ordering;
1789 match (a, b) {
1790 (Value::Null, _) | (_, Value::Null) => None,
1791 (Value::Number(x), Value::Number(y)) => {
1792 x.as_f64().partial_cmp(&y.as_f64())
1793 }
1794 (Value::String(x), Value::String(y)) => Some(x.cmp(y)),
1795 (Value::Bool(x), Value::Bool(y)) => Some(x.cmp(y)),
1796 (Value::Number(x), Value::String(y)) => match y.parse::<f64>() {
1799 Ok(n) => x.as_f64().partial_cmp(&Some(n)),
1800 Err(_) => Some(as_text(a).cmp(&as_text(b))),
1801 },
1802 (Value::String(x), Value::Number(y)) => match x.parse::<f64>() {
1803 Ok(n) => Some(n).partial_cmp(&y.as_f64()),
1804 Err(_) => Some(as_text(a).cmp(&as_text(b))),
1805 },
1806 _ => {
1807 let (x, y) = (as_text(a), as_text(b));
1808 if x == y { Some(Ordering::Equal) } else { Some(x.cmp(&y)) }
1809 }
1810 }
1811}
1812
1813fn as_text(v: &Value) -> String {
1815 match v {
1816 Value::String(s) => s.clone(),
1817 Value::Null => String::new(),
1818 Value::Bool(b) => (if *b { "t" } else { "f" }).to_string(),
1819 Value::Array(items) => {
1822 let inner: Vec<String> = items
1823 .iter()
1824 .map(|i| match i {
1825 Value::Null => "NULL".to_string(),
1826 Value::String(s) if s.is_empty()
1827 || s.chars().any(|c| c.is_whitespace() || matches!(c, ',' | '{' | '}' | '"' | '\\')) =>
1828 {
1829 format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
1830 }
1831 other => as_text(other),
1832 })
1833 .collect();
1834 format!("{{{}}}", inner.join(","))
1835 }
1836 other => other.to_string(),
1837 }
1838}
1839
1840fn num(v: &Value) -> Option<f64> {
1841 match v {
1842 Value::Number(n) => n.as_f64(),
1843 Value::String(s) => s.parse().ok(),
1844 Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
1845 _ => None,
1846 }
1847}
1848
1849fn from_f64(f: f64) -> Value {
1863 if f.is_finite() && f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
1864 return Value::Number((f as i64).into());
1865 }
1866 serde_json::Number::from_f64(f).map(Value::Number).unwrap_or(Value::Null)
1867}
1868
1869pub fn eval(e: &Expr, row: &Bound) -> Result<Value> {
1871 Ok(match e {
1872 Expr::Literal(v) => v.clone(),
1873
1874 Expr::Column { qual, name } => {
1875 if let Some(q) = qual {
1879 if !row.has_binding(q) {
1880 bail!("no table or alias named {:?} in this query", q);
1881 }
1882 }
1883 row.column(qual.as_deref(), name)
1884 }
1885
1886 Expr::Cast { expr, .. } => eval(expr, row)?,
1887
1888 Expr::Star | Expr::QualifiedStar(_) => {
1889 bail!("`*` is only valid in a select list or as count(*)")
1890 }
1891
1892 Expr::Unary { op, expr } => {
1893 let v = eval(expr, row)?;
1894 match op.as_str() {
1895 "NOT" => match truthy(&v) {
1896 None => Value::Null,
1898 Some(b) => Value::Bool(!b),
1899 },
1900 "-" => match num(&v) {
1901 Some(n) => from_f64(-n),
1902 None => Value::Null,
1903 },
1904 other => bail!("unsupported unary operator {:?}", other),
1905 }
1906 }
1907
1908 Expr::Binary { op, left, right } => {
1909 if op == "AND" {
1914 let l = truthy(&eval(left, row)?);
1915 if l == Some(false) {
1916 return Ok(Value::Bool(false));
1917 }
1918 let r = truthy(&eval(right, row)?);
1919 return Ok(match (l, r) {
1920 (_, Some(false)) => Value::Bool(false),
1921 (Some(true), Some(true)) => Value::Bool(true),
1922 _ => Value::Null,
1923 });
1924 }
1925 if op == "OR" {
1926 let l = truthy(&eval(left, row)?);
1927 if l == Some(true) {
1928 return Ok(Value::Bool(true));
1929 }
1930 let r = truthy(&eval(right, row)?);
1931 return Ok(match (l, r) {
1932 (_, Some(true)) => Value::Bool(true),
1933 (Some(false), Some(false)) => Value::Bool(false),
1934 _ => Value::Null,
1935 });
1936 }
1937
1938 let l = eval(left, row)?;
1939 let r = eval(right, row)?;
1940 apply_op(op, l, r)?
1941 }
1942
1943 Expr::IsNull { expr, negated } => {
1944 let v = eval(expr, row)?;
1945 Value::Bool(v.is_null() != *negated)
1948 }
1949
1950 Expr::InList { expr, list, negated } => {
1951 let v = eval(expr, row)?;
1952 if v.is_null() {
1953 return Ok(Value::Null);
1954 }
1955 let mut items = Vec::with_capacity(list.len());
1956 for item in list {
1957 items.push(eval(item, row)?);
1958 }
1959 in_values(&v, &items, *negated)?
1960 }
1961
1962 Expr::Subquery(q) => {
1964 let (cols, rows) = run_sub(q, row)?;
1965 if cols.len() != 1 {
1966 bail!("a subquery used as an expression must return exactly one \
1967 column, this one returns {}", cols.len());
1968 }
1969 match rows.len() {
1970 0 => Value::Null,
1971 1 => rows[0].get(&cols[0].key).cloned().unwrap_or(Value::Null),
1972 n => bail!("more than one row returned by a subquery used as an \
1973 expression ({} rows)", n),
1974 }
1975 }
1976 Expr::Exists { query, negated } => {
1977 let (_, rows) = run_sub(query, row)?;
1978 Value::Bool(!rows.is_empty() != *negated)
1979 }
1980 Expr::ArrayQuery(q) => Value::Array(first_column(q, row)?),
1981 Expr::InSubquery { expr, query, negated } => {
1982 let v = eval(expr, row)?;
1983 if v.is_null() {
1984 return Ok(Value::Null);
1985 }
1986 let items = first_column(query, row)?;
1987 in_values(&v, &items, *negated)?
1988 }
1989 Expr::Quantified { op, left, all, right } => {
1990 let l = eval(left, row)?;
1991 let r = eval(right, row)?;
1992 let items = match r {
1993 Value::Null => return Ok(Value::Null),
1994 Value::Array(items) => items,
1995 other => bail!(
1996 "{} requires an array or a subquery on its right side, got {}",
1997 if *all { "ALL" } else { "ANY" },
1998 as_text(&other)
1999 ),
2000 };
2001 let mut saw_true = false;
2005 let mut saw_false = false;
2006 let mut saw_null = false;
2007 for item in items {
2008 match truthy(&apply_op(op, l.clone(), item)?) {
2009 Some(true) => saw_true = true,
2010 Some(false) => saw_false = true,
2011 None => saw_null = true,
2012 }
2013 }
2014 if *all {
2015 if saw_false {
2016 Value::Bool(false)
2017 } else if saw_null {
2018 Value::Null
2019 } else {
2020 Value::Bool(true)
2021 }
2022 } else if saw_true {
2023 Value::Bool(true)
2024 } else if saw_null {
2025 Value::Null
2026 } else {
2027 Value::Bool(false)
2028 }
2029 }
2030 Expr::Index { expr, index } => {
2031 let arr = eval(expr, row)?;
2032 let i = eval(index, row)?;
2033 match (arr, num(&i)) {
2034 (Value::Array(items), Some(n)) if n >= 1.0 => {
2035 items.get(n as usize - 1).cloned().unwrap_or(Value::Null)
2036 }
2037 _ => Value::Null,
2038 }
2039 }
2040 Expr::ArrayLit(items) => {
2041 let mut out = Vec::with_capacity(items.len());
2042 for i in items {
2043 out.push(eval(i, row)?);
2044 }
2045 Value::Array(out)
2046 }
2047
2048 Expr::Case { operand, whens, else_ } => {
2049 let subject = match operand {
2050 Some(o) => Some(eval(o, row)?),
2051 None => None,
2052 };
2053 for (cond, then) in whens {
2054 let hit = match &subject {
2055 Some(sv) => {
2057 let cv = eval(cond, row)?;
2058 matches!(cmp_values(sv, &cv), Some(std::cmp::Ordering::Equal))
2059 }
2060 None => truthy(&eval(cond, row)?) == Some(true),
2063 };
2064 if hit {
2065 return eval(then, row);
2066 }
2067 }
2068 match else_ {
2069 Some(e) => eval(e, row)?,
2070 None => Value::Null,
2073 }
2074 }
2075
2076 Expr::Func { name, args } => eval_func(name, args, row)?,
2077
2078 Expr::Agg { name, .. } => bail!(
2084 "{}() is an aggregate and has no value for one row — it is reduced \
2085 over a GROUP. Reaching this point is an engine bug, not a problem \
2086 with the query",
2087 name
2088 ),
2089 })
2090}
2091
2092fn in_values(v: &Value, items: &[Value], negated: bool) -> Result<Value> {
2094 let mut any_null = false;
2095 let mut found = false;
2096 for iv in items {
2097 if iv.is_null() {
2098 any_null = true;
2099 continue;
2100 }
2101 if matches!(cmp_values(v, iv), Some(std::cmp::Ordering::Equal)) {
2102 found = true;
2103 break;
2104 }
2105 }
2106 Ok(if found {
2110 Value::Bool(!negated)
2111 } else if any_null {
2112 Value::Null
2113 } else {
2114 Value::Bool(negated)
2115 })
2116}
2117
2118fn run_sub(q: &Select, row: &Bound) -> Result<(Vec<OutCol>, Vec<Value>)> {
2126 let Some(resolve) = row.ctx.resolver else {
2127 bail!("a subquery cannot run here: this evaluation has no relation resolver");
2128 };
2129 let (cols, rows, _) = execute_inner(q, resolve, Opts::default(), Some(row))?;
2130 Ok((cols, rows))
2131}
2132
2133fn first_column(q: &Select, row: &Bound) -> Result<Vec<Value>> {
2136 let (cols, rows) = run_sub(q, row)?;
2137 let Some(first) = cols.first() else {
2138 bail!("the subquery returns no columns");
2139 };
2140 Ok(rows
2141 .into_iter()
2142 .map(|r| r.get(&first.key).cloned().unwrap_or(Value::Null))
2143 .collect())
2144}
2145
2146fn apply_op(op: &str, l: Value, r: Value) -> Result<Value> {
2150 let compare = |ord: fn(std::cmp::Ordering) -> bool| -> Value {
2152 match cmp_values(&l, &r) {
2153 None => Value::Null,
2154 Some(o) => Value::Bool(ord(o)),
2155 }
2156 };
2157
2158 Ok(match op {
2159 "IS DISTINCT FROM" | "IS NOT DISTINCT FROM" => {
2160 let distinct = match (l.is_null(), r.is_null()) {
2161 (true, true) => false,
2162 (true, false) | (false, true) => true,
2163 (false, false) => !matches!(cmp_values(&l, &r), Some(std::cmp::Ordering::Equal)),
2164 };
2165 Value::Bool(distinct != op.starts_with("IS NOT"))
2166 }
2167 "=" => compare(|o| o.is_eq()),
2168 "!=" | "<>" => compare(|o| o.is_ne()),
2169 "<" => compare(|o| o.is_lt()),
2170 "<=" => compare(|o| o.is_le()),
2171 ">" => compare(|o| o.is_gt()),
2172 ">=" => compare(|o| o.is_ge()),
2173
2174 "~" | "~*" | "!~" | "!~*" => {
2175 if l.is_null() || r.is_null() {
2176 Value::Null
2177 } else {
2178 let pat = as_text(&r);
2179 if let Some(why) = crate::nql::regex_error_pub(&pat) {
2180 bail!(
2181 "{} — in {:?}. The supported subset is ^ $ . | ( ) \
2182 [ ] * + ? and literal text",
2183 why, pat
2184 );
2185 }
2186 let hit = crate::nql::regex_match_pub(
2187 &as_text(&l), &pat, op.ends_with('*'));
2188 Value::Bool(hit != op.starts_with('!'))
2189 }
2190 }
2191
2192 "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => {
2193 if l.is_null() || r.is_null() {
2194 Value::Null
2195 } else {
2196 let hit = crate::nql::like_match_pub(
2197 &as_text(&l), &as_text(&r), op.ends_with("ILIKE"));
2198 Value::Bool(hit != op.starts_with("NOT"))
2199 }
2200 }
2201
2202 "||" => {
2204 if l.is_null() || r.is_null() {
2205 Value::Null
2206 } else {
2207 Value::String(format!("{}{}", as_text(&l), as_text(&r)))
2208 }
2209 }
2210
2211 "+" | "-" | "*" | "/" | "%" => match (num(&l), num(&r)) {
2212 (Some(a), Some(b)) => match op {
2213 "+" => from_f64(a + b),
2214 "-" => from_f64(a - b),
2215 "*" => from_f64(a * b),
2216 "/" if b == 0.0 => bail!("division by zero"),
2219 "/" => from_f64(a / b),
2220 "%" if b == 0.0 => bail!("division by zero"),
2221 "%" => from_f64(a % b),
2222 _ => unreachable!(),
2223 },
2224 _ => Value::Null,
2225 },
2226
2227 other => bail!("unsupported operator {:?}", other),
2228 })
2229}
2230
2231fn eval_func(name: &str, args: &[Expr], row: &Bound) -> Result<Value> {
2237 let arg = |i: usize| -> Result<Value> {
2240 match args.get(i) {
2241 Some(e) => eval(e, row),
2242 None => Ok(Value::Null),
2243 }
2244 };
2245
2246 Ok(match name {
2247 "pg_get_userbyid" | "current_user" | "session_user" | "user" => {
2251 Value::String("nedb".into())
2252 }
2253 "current_schema" => Value::String("public".into()),
2254 "current_database" | "current_catalog" => Value::String("nedb".into()),
2255 "version" => Value::String(crate::pgwire::version_string()),
2256
2257 "pg_table_is_visible" | "pg_type_is_visible" | "pg_function_is_visible"
2262 | "pg_opclass_is_visible" | "pg_conversion_is_visible" => Value::Bool(true),
2263
2264 "pg_encoding_to_char" => Value::String("UTF8".into()),
2266 "pg_get_expr" | "pg_get_indexdef" | "pg_get_constraintdef"
2267 | "pg_get_viewdef" | "pg_get_partkeydef" | "obj_description"
2268 | "col_description" | "shobj_description" => Value::Null,
2269
2270 "lower" => match arg(0)? {
2272 Value::Null => Value::Null,
2273 v => Value::String(as_text(&v).to_lowercase()),
2274 },
2275 "upper" => match arg(0)? {
2276 Value::Null => Value::Null,
2277 v => Value::String(as_text(&v).to_uppercase()),
2278 },
2279 "length" | "char_length" | "character_length" => match arg(0)? {
2280 Value::Null => Value::Null,
2281 v => from_f64(as_text(&v).chars().count() as f64),
2282 },
2283 "format_type" => match arg(0)? {
2284 Value::Null => Value::Null,
2285 v => Value::String(crate::pgcatalog::type_name_pub(
2286 num(&v).unwrap_or(25.0) as i32).to_string()),
2287 },
2288 "array_to_string" | "pg_catalog.array_to_string" => {
2289 match arg(0)? {
2293 Value::Array(items) => {
2294 let sep = as_text(&arg(1)?);
2295 Value::String(
2296 items.iter().map(as_text).collect::<Vec<_>>().join(&sep),
2297 )
2298 }
2299 _ => Value::Null,
2300 }
2301 }
2302 "quote_ident" => Value::String(as_text(&arg(0)?)),
2303 "quote_literal" => Value::String(format!("'{}'", as_text(&arg(0)?).replace('\'', "''"))),
2304 "format" => {
2308 let fmt = as_text(&arg(0)?);
2309 let mut out = String::new();
2310 let mut next = 1usize;
2311 let mut chars = fmt.chars().peekable();
2312 while let Some(c) = chars.next() {
2313 if c != '%' {
2314 out.push(c);
2315 continue;
2316 }
2317 match chars.next() {
2318 Some('%') => out.push('%'),
2319 Some(spec @ ('s' | 'I' | 'L')) => {
2320 let v = arg(next)?;
2321 next += 1;
2322 match (spec, &v) {
2323 ('L', Value::Null) => out.push_str("NULL"),
2324 ('L', v) => out.push_str(&format!("'{}'", as_text(v).replace('\'', "''"))),
2325 (_, v) => out.push_str(&as_text(v)),
2326 }
2327 }
2328 other => bail!("format(): unsupported conversion %{}", other.map(String::from).unwrap_or_default()),
2329 }
2330 }
2331 Value::String(out)
2332 }
2333
2334 "array_length" | "array_upper" | "cardinality" => match arg(0)? {
2338 Value::Array(items) if !items.is_empty() => from_f64(items.len() as f64),
2339 Value::Array(_) if name == "cardinality" => from_f64(0.0),
2340 _ => Value::Null,
2341 },
2342 "array_lower" => match arg(0)? {
2343 Value::Array(items) if !items.is_empty() => from_f64(1.0),
2344 _ => Value::Null,
2345 },
2346
2347 "pg_table_size" | "pg_total_relation_size" | "pg_relation_size"
2354 | "pg_indexes_size" | "pg_database_size" => Value::Null,
2355 "pg_size_pretty" => match num(&arg(0)?) {
2356 None => Value::Null,
2357 Some(n) => {
2358 let units = ["bytes", "kB", "MB", "GB", "TB", "PB"];
2359 let mut v = n;
2360 let mut u = 0usize;
2361 while v.abs() >= 10240.0 && u + 1 < units.len() {
2362 v /= 1024.0;
2363 u += 1;
2364 }
2365 Value::String(format!("{} {}", v.round() as i64, units[u]))
2366 }
2367 },
2368
2369 "pg_get_triggerdef" | "pg_get_ruledef" | "pg_get_statisticsobjdef"
2375 | "pg_get_statisticsobjdef_columns" | "pg_get_function_result"
2376 | "pg_get_function_arguments" | "pg_get_function_identity_arguments"
2377 | "pg_get_functiondef" | "pg_get_serial_sequence" | "pg_get_partition_constraintdef"
2378 | "pg_relation_filepath" | "pg_tablespace_location" => Value::Null,
2379 "pg_relation_is_publishable" => Value::Bool(true),
2380 "pg_statistics_obj_is_visible" | "pg_opfamily_is_visible" | "pg_collation_is_visible"
2381 | "pg_ts_config_is_visible" | "pg_ts_dict_is_visible" | "pg_ts_parser_is_visible"
2382 | "pg_ts_template_is_visible" | "has_table_privilege" | "has_schema_privilege"
2383 | "has_database_privilege" | "pg_has_role" => Value::Bool(true),
2384 "current_setting" => match arg(0)? {
2387 Value::Null => Value::Null,
2388 v => match as_text(&v).to_lowercase().as_str() {
2389 "server_version" => Value::String(crate::pgwire::version_string()),
2390 "server_encoding" | "client_encoding" => Value::String("UTF8".into()),
2391 "standard_conforming_strings" | "integer_datetimes" | "is_superuser" => {
2392 Value::String("on".into())
2393 }
2394 "timezone" | "log_timezone" => Value::String("UTC".into()),
2395 "search_path" => Value::String("\"$user\", public".into()),
2396 "intervalstyle" => Value::String("postgres".into()),
2397 "datestyle" => Value::String("ISO, MDY".into()),
2398 "session_authorization" => Value::String("nedb".into()),
2399 "application_name" | "default_transaction_read_only" => Value::String(String::new()),
2400 "transaction_isolation" | "default_transaction_isolation" => {
2401 Value::String("read committed".into())
2402 }
2403 "max_identifier_length" => Value::String("63".into()),
2404 other => {
2405 if truthy(&arg(1)?) == Some(true) {
2408 Value::Null
2409 } else {
2410 bail!("unrecognized configuration parameter \"{}\"", other)
2411 }
2412 }
2413 },
2414 },
2415 "pg_backend_pid" => from_f64(std::process::id() as f64),
2416 "pg_is_in_recovery" => Value::Bool(false),
2417 "txid_current" => from_f64(0.0),
2418 "now" | "current_timestamp" | "statement_timestamp" | "clock_timestamp" => {
2419 Value::String(now_iso())
2420 }
2421 "to_char" => match arg(0)? {
2422 Value::Null => Value::Null,
2423 v => Value::String(as_text(&v)),
2424 },
2425 "generate_series" | "unnest" => bail!(
2426 "{}() returns a set of rows — write it in FROM, not in the select list", name
2427 ),
2428
2429 "coalesce" => {
2431 let mut out = Value::Null;
2432 for a in args {
2433 let v = eval(a, row)?;
2434 if !v.is_null() {
2435 out = v;
2436 break;
2437 }
2438 }
2439 out
2440 }
2441 "nullif" => {
2442 let a = arg(0)?;
2443 let b = arg(1)?;
2444 if matches!(cmp_values(&a, &b), Some(std::cmp::Ordering::Equal)) {
2445 Value::Null
2446 } else {
2447 a
2448 }
2449 }
2450
2451 "int4" | "int8" | "int2" => match num(&arg(0)?) {
2453 Some(n) => from_f64(n.trunc()),
2454 None => Value::Null,
2455 },
2456 "text" => match arg(0)? {
2457 Value::Null => Value::Null,
2458 v => Value::String(as_text(&v)),
2459 },
2460
2461 other if is_aggregate(other) => bail!(
2462 "{}() is an aggregate, which is only meaningful over a whole result set — \
2463 it is evaluated by the executor, never per row",
2464 other
2465 ),
2466
2467 other => bail!(
2468 "the function {}() is not implemented. It is refused rather than \
2469 answered with NULL, because a NULL column reads as missing DATA \
2470 rather than a missing feature",
2471 other
2472 ),
2473 })
2474}
2475
2476fn now_iso() -> String {
2478 let secs = std::time::SystemTime::now()
2479 .duration_since(std::time::UNIX_EPOCH)
2480 .map(|d| d.as_secs())
2481 .unwrap_or(0);
2482 let days = (secs / 86_400) as i64;
2484 let rem = secs % 86_400;
2485 let z = days + 719_468;
2486 let era = z.div_euclid(146_097);
2487 let doe = z.rem_euclid(146_097);
2488 let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
2489 let y = yoe + era * 400;
2490 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2491 let mp = (5 * doy + 2) / 153;
2492 let d = doy - (153 * mp + 2) / 5 + 1;
2493 let m = if mp < 10 { mp + 3 } else { mp - 9 };
2494 let y = if m <= 2 { y + 1 } else { y };
2495 format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}+00", y, m, d, rem / 3600, (rem % 3600) / 60, rem % 60)
2496}
2497
2498const AGGREGATES: &[&str] = &[
2503 "count", "sum", "avg", "min", "max", "string_agg", "array_agg", "bool_and",
2504 "bool_or", "every",
2505];
2506
2507fn is_aggregate(name: &str) -> bool {
2508 AGGREGATES.iter().any(|a| a.eq_ignore_ascii_case(name))
2509}
2510
2511pub fn has_aggregate(e: &Expr) -> bool {
2514 match e {
2515 Expr::Agg { .. } => true,
2516 Expr::Func { args, .. } => args.iter().any(has_aggregate),
2517 Expr::Binary { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2518 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2519 has_aggregate(expr)
2520 }
2521 Expr::InList { expr, list, .. } => has_aggregate(expr) || list.iter().any(has_aggregate),
2522 Expr::Case { operand, whens, else_ } => {
2523 operand.as_deref().is_some_and(has_aggregate)
2524 || whens.iter().any(|(c, t)| has_aggregate(c) || has_aggregate(t))
2525 || else_.as_deref().is_some_and(has_aggregate)
2526 }
2527 Expr::Quantified { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2528 Expr::Index { expr, index } => has_aggregate(expr) || has_aggregate(index),
2529 Expr::ArrayLit(items) => items.iter().any(has_aggregate),
2530 Expr::InSubquery { expr, .. } => has_aggregate(expr),
2531 Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => false,
2532 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => false,
2533 }
2534}
2535
2536fn aggregate(
2543 name: &str,
2544 args: &[Expr],
2545 order_by: &[OrderBy],
2546 distinct: bool,
2547 rows: &[JoinedRow],
2548 ctx: EvalCtx,
2549) -> Result<Value> {
2550 let lname = name.to_lowercase();
2551
2552 let ordered: Vec<JoinedRow> = if order_by.is_empty() {
2555 rows.to_vec()
2556 } else {
2557 let mut keyed: Vec<(Vec<Value>, JoinedRow)> = Vec::with_capacity(rows.len());
2558 for r in rows {
2559 let b = bind(r, ctx);
2560 let mut key = vec![];
2561 for ob in order_by {
2562 match (&ob.expr, ob.ordinal) {
2565 (Some(e), _) => key.push(eval(e, &b)?),
2566 (None, Some(n)) => bail!(
2567 "ORDER BY {} inside an aggregate refers to a select-list \
2568 position, which an aggregate does not have — name the \
2569 column instead", n
2570 ),
2571 (None, None) => key.push(Value::Null),
2572 }
2573 }
2574 keyed.push((key, r.clone()));
2575 }
2576 keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, order_by));
2577 keyed.into_iter().map(|(_, r)| r).collect()
2578 };
2579 let rows: &[JoinedRow] = &ordered;
2580
2581 if lname == "count" && (args.is_empty() || matches!(args[0], Expr::Star)) {
2584 return Ok(from_f64(rows.len() as f64));
2585 }
2586 let Some(target) = args.first() else {
2587 bail!("{}() needs an argument", name);
2588 };
2589 let mut vals: Vec<Value> = Vec::with_capacity(rows.len());
2590 let mut all_vals: Vec<Value> = Vec::with_capacity(rows.len());
2591 for r in rows {
2592 let v = eval(target, &bind(r, ctx))?;
2593 if !v.is_null() {
2594 vals.push(v.clone());
2595 }
2596 all_vals.push(v);
2597 }
2598 if distinct {
2599 let mut seen: Vec<String> = vec![];
2600 vals.retain(|v| {
2601 let k = format!("{:?}", v);
2602 if seen.contains(&k) { false } else { seen.push(k); true }
2603 });
2604 let mut seen2: Vec<String> = vec![];
2605 all_vals.retain(|v| {
2606 let k = format!("{:?}", v);
2607 if seen2.contains(&k) { false } else { seen2.push(k); true }
2608 });
2609 }
2610 Ok(match lname.as_str() {
2611 "count" => from_f64(vals.len() as f64),
2612 "sum" | "avg" => {
2613 let nums: Vec<f64> = vals.iter().filter_map(num).collect();
2614 if nums.is_empty() {
2615 Value::Null
2616 } else if lname == "sum" {
2617 from_f64(nums.iter().sum())
2618 } else {
2619 from_f64(nums.iter().sum::<f64>() / nums.len() as f64)
2620 }
2621 }
2622 "min" | "max" => {
2623 let mut best: Option<Value> = None;
2624 for v in vals {
2625 best = Some(match best {
2626 None => v,
2627 Some(b) => {
2628 let take = match cmp_values(&v, &b) {
2629 Some(o) if lname == "min" => o.is_lt(),
2630 Some(o) => o.is_gt(),
2631 None => false,
2632 };
2633 if take { v } else { b }
2634 }
2635 });
2636 }
2637 best.unwrap_or(Value::Null)
2638 }
2639 "string_agg" => {
2640 if vals.is_empty() {
2641 Value::Null
2642 } else {
2643 let sep = match args.get(1) {
2646 Some(e) => as_text(&eval(e, &Bound { parts: vec![], ctx })?),
2647 None => String::new(),
2648 };
2649 Value::String(vals.iter().map(as_text).collect::<Vec<_>>().join(&sep))
2650 }
2651 }
2652 "array_agg" => {
2654 if all_vals.is_empty() { Value::Null } else { Value::Array(all_vals) }
2655 }
2656 "bool_and" | "every" => {
2657 if vals.is_empty() {
2658 Value::Null
2659 } else {
2660 Value::Bool(vals.iter().all(|v| truthy(v) == Some(true)))
2661 }
2662 }
2663 "bool_or" => {
2664 if vals.is_empty() {
2665 Value::Null
2666 } else {
2667 Value::Bool(vals.iter().any(|v| truthy(v) == Some(true)))
2668 }
2669 }
2670 _ => unreachable!("is_aggregate gates this"),
2671 })
2672}
2673
2674fn fold_aggregates(
2681 e: &Expr,
2682 rows: &[JoinedRow],
2683 ctx: EvalCtx,
2684 keys: &[Expr],
2685) -> Result<Expr> {
2686 let fold = |x: &Expr| fold_aggregates(x, rows, ctx, keys);
2687 Ok(match e {
2688 Expr::Agg { name, args, order_by, distinct } => {
2689 Expr::Literal(aggregate(name, args, order_by, *distinct, rows, ctx)?)
2690 }
2691 Expr::Func { name, args } => Expr::Func {
2692 name: name.clone(),
2693 args: args.iter().map(&fold).collect::<Result<_>>()?,
2694 },
2695 Expr::Column { .. } if keys.iter().any(|k| k == e) => e.clone(),
2701 Expr::Column { qual, name } => bail!(
2702 "column \"{}{}\" must appear in the GROUP BY clause or be used in an \
2703 aggregate function",
2704 qual.as_ref().map(|q| format!("{q}.")).unwrap_or_default(),
2705 name
2706 ),
2707 Expr::Binary { op, left, right } => Expr::Binary {
2708 op: op.clone(),
2709 left: Box::new(fold(left)?),
2710 right: Box::new(fold(right)?),
2711 },
2712 Expr::Unary { op, expr } => Expr::Unary {
2713 op: op.clone(),
2714 expr: Box::new(fold(expr)?),
2715 },
2716 Expr::Cast { expr, ty } => Expr::Cast {
2717 expr: Box::new(fold(expr)?),
2718 ty: ty.clone(),
2719 },
2720 Expr::IsNull { expr, negated } => Expr::IsNull {
2721 expr: Box::new(fold(expr)?),
2722 negated: *negated,
2723 },
2724 Expr::InList { expr, list, negated } => Expr::InList {
2725 expr: Box::new(fold(expr)?),
2726 list: list.iter().map(&fold).collect::<Result<_>>()?,
2727 negated: *negated,
2728 },
2729 Expr::Case { operand, whens, else_ } => Expr::Case {
2730 operand: match operand {
2731 Some(o) => Some(Box::new(fold(o)?)),
2732 None => None,
2733 },
2734 whens: whens
2735 .iter()
2736 .map(|(c, t)| Ok((fold(c)?, fold(t)?)))
2737 .collect::<Result<_>>()?,
2738 else_: match else_ {
2739 Some(x) => Some(Box::new(fold(x)?)),
2740 None => None,
2741 },
2742 },
2743 Expr::Quantified { op, left, all, right } => Expr::Quantified {
2744 op: op.clone(),
2745 left: Box::new(fold(left)?),
2746 all: *all,
2747 right: Box::new(fold(right)?),
2748 },
2749 Expr::Index { expr, index } => Expr::Index {
2750 expr: Box::new(fold(expr)?),
2751 index: Box::new(fold(index)?),
2752 },
2753 Expr::ArrayLit(items) => Expr::ArrayLit(
2754 items.iter().map(&fold).collect::<Result<_>>()?,
2755 ),
2756 Expr::InSubquery { expr, query, negated } => Expr::InSubquery {
2757 expr: Box::new(fold(expr)?),
2758 query: query.clone(),
2759 negated: *negated,
2760 },
2761 Expr::Star | Expr::QualifiedStar(_) => {
2764 bail!("`*` cannot be mixed with an aggregate outside count(*)")
2765 }
2766 Expr::Literal(_) | Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => {
2768 e.clone()
2769 }
2770 })
2771}
2772
2773fn validate_bindings(sel: &Select) -> Result<()> {
2789 let mut seen: Vec<String> = vec![];
2790 if let Some(f) = &sel.from {
2791 seen.push(f.binding());
2792 }
2793 for j in &sel.joins {
2794 seen.push(j.table.binding());
2795 }
2796 for (i, b) in seen.iter().enumerate() {
2797 if let Some(prev) = seen[..i].iter().find(|p| p.eq_ignore_ascii_case(b)) {
2798 bail!(
2799 "ambiguous relation binding: {:?} appears more than once; use \
2800 aliases (for example `FROM {} JOIN {} AS {}2 ...`)",
2801 prev, prev, prev, prev
2802 );
2803 }
2804 }
2805 Ok(())
2806}
2807
2808#[derive(Debug, Clone, PartialEq, Eq)]
2820pub struct OutCol {
2821 pub key: String,
2822 pub name: String,
2823}
2824
2825fn unique_key(taken: &[OutCol], name: &str) -> String {
2829 if !taken.iter().any(|c| c.key == name) {
2830 return name.to_string();
2831 }
2832 format!("{name}\u{1}{}", taken.len())
2833}
2834
2835type JoinedRow = Vec<(String, Option<Value>)>;
2837
2838fn bind<'a>(row: &'a JoinedRow, ctx: EvalCtx<'a>) -> Bound<'a> {
2839 Bound {
2840 parts: row.iter().map(|(b, v)| (b.clone(), v.as_ref())).collect(),
2841 ctx,
2842 }
2843}
2844
2845fn derived_name(e: &Expr) -> String {
2853 match e {
2854 Expr::Column { name, .. } => name.clone(),
2855 Expr::Func { name, .. } | Expr::Agg { name, .. } => name.clone(),
2856 Expr::Cast { expr, .. } => derived_name(expr),
2857 Expr::Case { .. } => "case".to_string(),
2858 Expr::ArrayQuery(_) | Expr::ArrayLit(_) => "array".to_string(),
2859 Expr::Exists { .. } => "exists".to_string(),
2860 Expr::Subquery(q) => q
2862 .items
2863 .first()
2864 .map(|i| i.alias.clone().unwrap_or_else(|| derived_name(&i.expr)))
2865 .unwrap_or_else(|| "?column?".to_string()),
2866 _ => "?column?".to_string(),
2867 }
2868}
2869
2870pub trait Relation {
2891 fn next_row(&mut self) -> Result<Option<Value>>;
2893
2894 fn size_hint(&self) -> Option<usize> {
2896 None
2897 }
2898}
2899
2900pub struct VecRelation {
2906 iter: std::vec::IntoIter<Value>,
2907 len: usize,
2908}
2909
2910impl Relation for VecRelation {
2911 fn next_row(&mut self) -> Result<Option<Value>> {
2912 Ok(self.iter.next())
2913 }
2914 fn size_hint(&self) -> Option<usize> {
2915 Some(self.len)
2916 }
2917}
2918
2919pub fn from_vec(rows: Vec<Value>) -> Box<dyn Relation> {
2921 let len = rows.len();
2922 Box::new(VecRelation { iter: rows.into_iter(), len })
2923}
2924
2925pub type Resolver<'r> = dyn Fn(&str) -> Result<Option<Box<dyn Relation>>> + 'r;
2931
2932pub fn execute(sel: &Select, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
2937 let (cols, rows, _) = execute_explain(sel, resolve, JoinExec::Auto)?;
2938 Ok((cols, rows))
2939}
2940
2941pub fn execute_explain(
2948 sel: &Select,
2949 resolve: &Resolver,
2950 exec: JoinExec,
2951) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
2952 execute_with(sel, resolve, exec, true)
2953}
2954
2955#[derive(Debug, Clone, Copy)]
2960pub struct Opts {
2961 pub exec: JoinExec,
2962 pub pushdown: bool,
2963 pub fuse_filter: bool,
2967}
2968
2969impl Default for Opts {
2970 fn default() -> Self {
2971 Opts { exec: JoinExec::Auto, pushdown: true, fuse_filter: true }
2972 }
2973}
2974
2975impl Opts {
2976 pub fn exec(exec: JoinExec) -> Self {
2977 Opts { exec, ..Default::default() }
2978 }
2979}
2980
2981pub fn execute_with(
2988 sel: &Select,
2989 resolve: &Resolver,
2990 exec: JoinExec,
2991 pushdown: bool,
2992) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
2993 execute_opts(sel, resolve, Opts { exec, pushdown, ..Default::default() })
2994}
2995
2996pub fn execute_opts(
2998 sel: &Select,
2999 resolve: &Resolver,
3000 opts: Opts,
3001) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3002 execute_inner(sel, resolve, opts, None)
3003}
3004
3005fn row_key(cols: &[OutCol], obj: &Map<String, Value>) -> String {
3007 cols.iter()
3008 .map(|c| format!("{:?}", obj.get(&c.key).unwrap_or(&Value::Null)))
3009 .collect::<Vec<_>>()
3010 .join("\u{1}")
3011}
3012
3013fn execute_set_ops<'a>(
3021 sel: &Select,
3022 resolve: &'a Resolver<'a>,
3023 opts: Opts,
3024 outer: Option<&'a Bound<'a>>,
3025) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3026 let ctx = EvalCtx { resolver: Some(resolve), outer };
3027 let mut head = sel.clone();
3028 head.set_ops.clear();
3029 head.order_by.clear();
3030 head.limit = None;
3031 head.offset = None;
3032 let (cols, rows, mut plan) = execute_inner(&head, resolve, opts, outer)?;
3033 let mut left: Vec<Map<String, Value>> = rows
3034 .into_iter()
3035 .map(|r| match r {
3036 Value::Object(m) => m,
3037 _ => Map::new(),
3038 })
3039 .collect();
3040
3041 for arm in &sel.set_ops {
3042 let (acols, arows, _) = execute_inner(&arm.query, resolve, opts, outer)?;
3043 let op_name = match arm.op {
3044 SetOp::Union => "UNION",
3045 SetOp::Intersect => "INTERSECT",
3046 SetOp::Except => "EXCEPT",
3047 };
3048 if acols.len() != cols.len() {
3049 bail!(
3050 "each {} query must have the same number of columns: {} vs {}",
3051 op_name, cols.len(), acols.len()
3052 );
3053 }
3054 let right: Vec<Map<String, Value>> = arows
3056 .into_iter()
3057 .map(|r| {
3058 let m = match r {
3059 Value::Object(m) => m,
3060 _ => Map::new(),
3061 };
3062 let mut out = Map::new();
3063 for (i, c) in cols.iter().enumerate() {
3064 out.insert(c.key.clone(), m.get(&acols[i].key).cloned().unwrap_or(Value::Null));
3065 }
3066 out
3067 })
3068 .collect();
3069 let (nl, nr) = (left.len(), right.len());
3070 let right_keys: std::collections::HashSet<String> =
3071 right.iter().map(|m| row_key(&cols, m)).collect();
3072 let mut combined: Vec<Map<String, Value>> = match arm.op {
3073 SetOp::Union => {
3074 left.extend(right);
3075 left
3076 }
3077 SetOp::Intersect => left.into_iter().filter(|m| right_keys.contains(&row_key(&cols, m))).collect(),
3078 SetOp::Except => left.into_iter().filter(|m| !right_keys.contains(&row_key(&cols, m))).collect(),
3079 };
3080 if !arm.all {
3081 let mut seen = std::collections::HashSet::new();
3082 combined.retain(|m| seen.insert(row_key(&cols, m)));
3083 }
3084 plan.notes.push(format!(
3085 "{}{}: {} + {} rows -> {} (each arm planned separately; only the first arm's plan is shown)",
3086 op_name, if arm.all { " ALL" } else { "" }, nl, nr, combined.len()
3087 ));
3088 left = combined;
3089 }
3090
3091 let projected: Vec<(Map<String, Value>, JoinedRow)> = left
3094 .into_iter()
3095 .map(|m| {
3096 let src: JoinedRow = vec![(String::new(), Some(Value::Object(m.clone())))];
3097 (m, src)
3098 })
3099 .collect();
3100 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3101 Ok((cols, out, plan))
3102}
3103
3104fn execute_inner<'a>(
3107 sel: &Select,
3108 resolve: &'a Resolver<'a>,
3109 opts: Opts,
3110 outer: Option<&'a Bound<'a>>,
3111) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3112 if !sel.set_ops.is_empty() {
3113 return execute_set_ops(sel, resolve, opts, outer);
3114 }
3115 let ctx = EvalCtx { resolver: Some(resolve), outer };
3116 let exec = opts.exec;
3117 let pushdown = opts.pushdown;
3118 let mut plan = Plan::default();
3119
3120 validate_bindings(sel)?;
3122
3123 let fuse = opts.fuse_filter && sel.where_.is_some() && !sel.joins.is_empty();
3150
3151 let budget: Option<usize> = match sel.limit {
3152 Some(lim)
3153 if sel.order_by.is_empty()
3154 && !sel.distinct
3155 && !sel.joins.is_empty()
3156 && (sel.where_.is_none() || fuse) =>
3157 {
3158 Some(lim.saturating_add(sel.offset.unwrap_or(0)))
3159 }
3160 _ => None,
3161 };
3162 plan.budget = budget;
3163
3164 let all_bindings: Vec<String> = sel
3170 .from
3171 .iter()
3172 .map(|t| t.binding())
3173 .chain(sel.joins.iter().map(|j| j.table.binding()))
3174 .collect();
3175 let nullable = crate::sqlpush::nullable_bindings(sel);
3176 let push = if pushdown {
3177 crate::sqlpush::plan(sel.where_.as_ref(), &all_bindings, &nullable)
3178 } else {
3179 Pushdown::default()
3180 };
3181 plan.refusals = push.refusals.clone();
3182
3183 let mut base_scan_at: Option<usize> = None;
3184 let mut base_prefilter_at: Option<usize> = None;
3185
3186 let mut left_src: Box<dyn LeftSource + 'a> = match &sel.from {
3194 None => {
3195 Box::new(VecLeft { rows: vec![vec![]], at: 0 })
3198 }
3199 Some(t) => {
3200 let rel = fetch(t, resolve, ctx)?;
3201 let binding = t.binding();
3202 base_scan_at = Some(plan.stages.len());
3207 plan.push(Stage::Scan {
3208 table: t.name.clone(),
3209 binding: binding.clone(),
3210 rows: 0,
3211 });
3212 let preds = push.for_binding(&binding).cloned().unwrap_or_default();
3213 if !preds.is_empty() {
3214 base_prefilter_at = Some(plan.stages.len());
3215 plan.push(Stage::Prefilter {
3216 binding: binding.clone(),
3217 predicates: preds.len(),
3218 in_rows: 0,
3219 out_rows: 0,
3220 });
3221 }
3222 Box::new(StreamLeft { rel, binding, preds, pulled: 0, kept: 0, ctx })
3223 }
3224 };
3225
3226 let mut left_bindings: Vec<String> = match &sel.from {
3232 None => vec![],
3233 Some(t) => vec![t.binding()],
3234 };
3235 let last = sel.joins.len().saturating_sub(1);
3236 let mut rows: Vec<JoinedRow> = vec![];
3237 let mut base_pulled: Option<usize> = None;
3238 let mut base_kept: Option<usize> = None;
3239
3240 for (ji, join) in sel.joins.iter().enumerate() {
3241 let is_last = ji == last;
3242 let rb = join.table.binding();
3243
3244 if join.table.lateral {
3248 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3249 let join_budget = if is_last { budget } else { None };
3250 let (out, removed, consumed, produced) = join_lateral(
3251 left_src.as_mut(), join, resolve, join_budget, post, ctx,
3252 )?;
3253 plan.push(Stage::Scan { table: join.table.name.clone(), binding: rb.clone(), rows: produced });
3254 plan.push(Stage::Join {
3255 kind: join.kind,
3256 table: join.table.name.clone(),
3257 binding: rb.clone(),
3258 strategy: Strategy::NestedLoop,
3259 keys: 0,
3260 left_rows: consumed,
3261 right_rows: produced,
3262 out_rows: out.len(),
3263 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3264 post_filter_removed: post.map(|_| removed),
3265 });
3266 plan.notes.push(format!("LATERAL {}: the subquery ran once per left row ({} times)", rb, consumed));
3267 left_bindings.push(rb);
3268 if ji == 0 {
3269 if let Some((pulled, kept)) = left_src.stats() {
3270 base_pulled = Some(pulled);
3271 base_kept = Some(kept);
3272 }
3273 }
3274 left_src = Box::new(VecLeft { rows: out, at: 0 });
3275 continue;
3276 }
3277
3278 let right_rel = fetch(&join.table, resolve, ctx)?;
3279 let right_all = drain(right_rel)?;
3280 plan.push(Stage::Scan {
3281 table: join.table.name.clone(),
3282 binding: rb.clone(),
3283 rows: right_all.len(),
3284 });
3285 let right_rows = prefilter(right_all, &rb, &push, &mut plan, ctx)?;
3286
3287 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3292 let join_budget = if is_last { budget } else { None };
3293
3294 let keys = sqljoin::hash_keys(join.on.as_ref(), &left_bindings, &rb);
3297 let left_hint = left_src.hint().unwrap_or(usize::MAX);
3298 let strategy = sqljoin::choose(exec, keys.len(), left_hint, right_rows.len());
3299
3300 let (out, removed, consumed) = match strategy {
3301 Strategy::NestedLoop => join_nested_loop(
3302 left_src.as_mut(), &left_bindings, join, &right_rows, &rb,
3303 join_budget, post, ctx,
3304 )?,
3305 Strategy::Hash => join_hash(
3306 left_src.as_mut(), &left_bindings, join, &right_rows, &rb, &keys,
3307 join_budget, post, ctx,
3308 )?,
3309 };
3310
3311 plan.push(Stage::Join {
3312 kind: join.kind,
3313 table: join.table.name.clone(),
3314 binding: rb.clone(),
3315 strategy,
3316 keys: keys.len(),
3317 left_rows: consumed,
3318 right_rows: right_rows.len(),
3319 out_rows: out.len(),
3320 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3321 post_filter_removed: post.map(|_| removed),
3322 });
3323 left_bindings.push(rb);
3324 if ji == 0 {
3326 if let Some((pulled, kept)) = left_src.stats() {
3327 base_pulled = Some(pulled);
3328 base_kept = Some(kept);
3329 }
3330 }
3331 rows = out;
3332 left_src = Box::new(VecLeft { rows: std::mem::take(&mut rows), at: 0 });
3334 }
3335
3336 rows = left_src.take_rows();
3339 if let Some(i) = base_scan_at {
3340 if let (Some(pulled), Some(kept)) = (base_pulled, base_kept) {
3341 if let Some(Stage::Scan { rows: r, .. }) = plan.stages.get_mut(i) {
3342 *r = pulled;
3343 }
3344 if let Some(j) = base_prefilter_at {
3345 if let Some(Stage::Prefilter { in_rows, out_rows, .. }) =
3346 plan.stages.get_mut(j)
3347 {
3348 *in_rows = pulled;
3349 *out_rows = kept;
3350 }
3351 }
3352 }
3353 }
3354
3355 if let Some(pred) = sel.where_.as_ref().filter(|_| !fuse) {
3357 let in_rows = rows.len();
3358 let mut kept = Vec::with_capacity(rows.len());
3359 for r in rows {
3360 if truthy(&eval(pred, &bind(&r, ctx))?) == Some(true) {
3364 kept.push(r);
3365 }
3366 }
3367 rows = kept;
3368 plan.push(Stage::Filter { in_rows, out_rows: rows.len() });
3369 }
3370
3371 let grouping = !sel.group_by.is_empty();
3384 let aggregating = grouping
3385 || sel.items.iter().any(|i| has_aggregate(&i.expr))
3386 || sel.having.as_ref().is_some_and(has_aggregate);
3387 if aggregating {
3388 let mut groups: Vec<(Vec<Value>, Vec<JoinedRow>)> = vec![];
3392 if grouping {
3393 for r in rows {
3394 let b = bind(&r, ctx);
3395 let mut key = Vec::with_capacity(sel.group_by.len());
3396 for g in &sel.group_by {
3397 key.push(eval(g, &b)?);
3398 }
3399 match groups.iter_mut().find(|(k, _)| {
3400 k.len() == key.len()
3401 && k.iter().zip(&key).all(|(a, b)| {
3402 (a.is_null() && b.is_null())
3405 || matches!(cmp_values(a, b), Some(std::cmp::Ordering::Equal))
3406 })
3407 }) {
3408 Some((_, bucket)) => bucket.push(r),
3409 None => groups.push((key, vec![r])),
3410 }
3411 }
3412 } else {
3413 groups.push((vec![], rows));
3416 }
3417
3418 let n_groups = groups.len();
3419 let mut cols: Vec<OutCol> = vec![];
3420 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = vec![];
3421 let empty: JoinedRow = vec![];
3422
3423 for (gi, (_key, grows)) in groups.iter().enumerate() {
3424 let scope = grows.first().unwrap_or(&empty);
3429
3430 if let Some(h) = &sel.having {
3431 let folded = fold_aggregates(h, grows, ctx, &sel.group_by)?;
3432 if truthy(&eval(&folded, &bind(scope, ctx))?) != Some(true) {
3433 continue;
3434 }
3435 }
3436
3437 let mut obj = Map::new();
3438 for item in &sel.items {
3439 let folded = fold_aggregates(&item.expr, grows, ctx, &sel.group_by)?;
3440 let v = eval(&folded, &bind(scope, ctx))?;
3441 if gi == 0 {
3443 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3444 let key = unique_key(&cols, &name);
3445 obj.insert(key.clone(), v);
3446 cols.push(OutCol { key, name });
3447 } else {
3448 let idx = obj.len();
3449 if let Some(c) = cols.get(idx) {
3450 obj.insert(c.key.clone(), v);
3451 }
3452 }
3453 }
3454 projected.push((obj, scope.clone()));
3455 }
3456
3457 plan.notes.push(if grouping {
3458 format!(
3459 "GroupAggregate on {} key(s): {} rows -> {} group(s){}",
3460 sel.group_by.len(),
3461 n_rows_before_group(&plan),
3462 n_groups,
3463 if sel.having.is_some() {
3464 format!(", HAVING kept {}", projected.len())
3465 } else {
3466 String::new()
3467 }
3468 )
3469 } else {
3470 format!("Aggregate over {} row(s) -> 1 row", groups[0].1.len())
3471 });
3472 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3473 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3474 return Ok((cols, out, plan));
3475 }
3476
3477 let mut cols: Vec<OutCol> = vec![];
3488 let mut spans: Vec<(usize, usize)> = Vec::with_capacity(sel.items.len());
3489 for item in &sel.items {
3490 let start = cols.len();
3491 match &item.expr {
3492 Expr::Star => {
3493 if let Some(first) = rows.first() {
3494 for (n, _) in bind(first, ctx).flatten() {
3495 if !cols.iter().any(|c| c.name == n) {
3497 cols.push(OutCol { key: n.clone(), name: n });
3498 }
3499 }
3500 }
3501 }
3502 Expr::QualifiedStar(q) => {
3503 if let Some(first) = rows.first() {
3504 for (n, _) in bind(first, ctx).flatten_binding(q) {
3505 if !cols.iter().any(|c| c.name == n) {
3506 cols.push(OutCol { key: n.clone(), name: n });
3507 }
3508 }
3509 }
3510 }
3511 _ => {
3512 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3513 let key = unique_key(&cols, &name);
3519 cols.push(OutCol { key, name });
3520 }
3521 }
3522 spans.push((start, cols.len()));
3523 }
3524
3525 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = Vec::with_capacity(rows.len());
3529 for r in rows {
3530 let b = bind(&r, ctx);
3531 let mut obj = Map::new();
3532 for (i, item) in sel.items.iter().enumerate() {
3533 let (start, end) = spans[i];
3534 match &item.expr {
3535 Expr::Star => {
3536 for (n, v) in b.flatten() {
3537 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3538 obj.entry(c.key.clone()).or_insert(v);
3539 }
3540 }
3541 }
3542 Expr::QualifiedStar(q) => {
3543 for (n, v) in b.flatten_binding(q) {
3544 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3545 obj.entry(c.key.clone()).or_insert(v);
3546 }
3547 }
3548 }
3549 _ => {
3550 let v = eval(&item.expr, &b)?;
3551 if let Some(c) = cols.get(start) {
3552 obj.insert(c.key.clone(), v);
3553 }
3554 }
3555 }
3556 }
3557 projected.push((obj, r));
3558 }
3559
3560 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3561
3562 if sel.distinct {
3564 let in_rows = projected.len();
3565 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
3566 projected.retain(|(obj, _)| seen.insert(row_key(&cols, obj)));
3569 plan.push(Stage::Distinct { in_rows, out_rows: projected.len() });
3570 }
3571
3572 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3573 Ok((cols, out, plan))
3574}
3575
3576fn n_rows_before_group(plan: &Plan) -> usize {
3580 plan.stages
3581 .iter()
3582 .rev()
3583 .find_map(|st| match st {
3584 Stage::Filter { out_rows, .. } => Some(*out_rows),
3585 Stage::Join { out_rows, .. } => Some(*out_rows),
3586 Stage::Prefilter { out_rows, .. } => Some(*out_rows),
3587 Stage::Scan { rows, .. } => Some(*rows),
3588 _ => None,
3589 })
3590 .unwrap_or(0)
3591}
3592
3593fn sort_keys(a: &[Value], b: &[Value], order_by: &[OrderBy]) -> std::cmp::Ordering {
3600 for (i, ob) in order_by.iter().enumerate() {
3601 let (Some(x), Some(y)) = (a.get(i), b.get(i)) else { continue };
3602 let ord = match (x.is_null(), y.is_null()) {
3603 (true, true) => std::cmp::Ordering::Equal,
3604 (true, false) => {
3607 return if ob.nulls_first {
3608 std::cmp::Ordering::Less
3609 } else {
3610 std::cmp::Ordering::Greater
3611 }
3612 }
3613 (false, true) => {
3614 return if ob.nulls_first {
3615 std::cmp::Ordering::Greater
3616 } else {
3617 std::cmp::Ordering::Less
3618 }
3619 }
3620 (false, false) => cmp_values(x, y).unwrap_or(std::cmp::Ordering::Equal),
3621 };
3622 let ord = if matches!(ob.dir, Dir::Desc) { ord.reverse() } else { ord };
3623 if !ord.is_eq() {
3624 return ord;
3625 }
3626 }
3627 std::cmp::Ordering::Equal
3628}
3629
3630fn finish(
3632 sel: &Select,
3633 cols: &[OutCol],
3634 mut projected: Vec<(Map<String, Value>, JoinedRow)>,
3635 ctx: EvalCtx,
3636 plan: &mut Plan,
3637) -> Result<Vec<Value>> {
3638 if !sel.order_by.is_empty() {
3640 let mut keyed: Vec<(Vec<Value>, (Map<String, Value>, JoinedRow))> = vec![];
3644 for (obj, src) in projected {
3645 let mut key = vec![];
3646 for ob in &sel.order_by {
3647 let v = match (ob.ordinal, &ob.expr) {
3648 (Some(n), _) => {
3649 let c = cols.get(n - 1).ok_or_else(|| {
3650 anyhow::anyhow!(
3651 "ORDER BY {} is out of range: the select list has {} \
3652 column(s)", n, cols.len())
3653 })?;
3654 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3655 }
3656 (None, Some(Expr::Column { qual: None, name }))
3661 if cols.iter().any(|c| c.name == *name) =>
3662 {
3663 let c = cols.iter().find(|c| c.name == *name).expect("checked");
3664 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3665 }
3666 (None, Some(e)) => {
3667 eval(e, &bind(&src, ctx))?
3671 }
3672 (None, None) => Value::Null,
3673 };
3674 key.push(v);
3675 }
3676 keyed.push((key, (obj, src)));
3677 }
3678
3679 keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, &sel.order_by));
3680
3681 projected = keyed.into_iter().map(|(_, row)| row).collect();
3682 plan.push(Stage::Sort { keys: sel.order_by.len(), rows: projected.len() });
3683 }
3684
3685 let mut out: Vec<Value> = projected
3687 .into_iter()
3688 .map(|(obj, _)| Value::Object(obj))
3689 .collect();
3690 let in_rows = out.len();
3691 if let Some(off) = sel.offset {
3692 out = if off >= out.len() { vec![] } else { out.split_off(off) };
3693 }
3694 if let Some(lim) = sel.limit {
3695 out.truncate(lim);
3696 }
3697 if sel.limit.is_some() || sel.offset.is_some() {
3698 plan.push(Stage::Limit {
3699 limit: sel.limit,
3700 offset: sel.offset,
3701 in_rows,
3702 out_rows: out.len(),
3703 });
3704 }
3705
3706 Ok(out)
3707}
3708
3709fn keep_row(cand: &JoinedRow, post: Option<&Expr>, removed: &mut usize, ctx: EvalCtx) -> Result<bool> {
3742 let Some(p) = post else { return Ok(true) };
3743 if truthy(&eval(p, &bind(cand, ctx))?) == Some(true) {
3745 Ok(true)
3746 } else {
3747 *removed += 1;
3748 Ok(false)
3749 }
3750}
3751
3752#[allow(clippy::too_many_arguments)]
3758fn emit_unmatched_right(
3759 out: &mut Vec<JoinedRow>,
3760 kind: JoinKind,
3761 left_bindings: &[String],
3762 right_rows: &[Value],
3763 right_matched: &[bool],
3764 rb: &str,
3765 post: Option<&Expr>,
3766 removed: &mut usize,
3767 ctx: EvalCtx,
3768) -> Result<()> {
3769 if !matches!(kind, JoinKind::Right | JoinKind::Full) {
3770 return Ok(());
3771 }
3772 for (ri, right) in right_rows.iter().enumerate() {
3773 if right_matched[ri] {
3774 continue;
3775 }
3776 let mut cand: JoinedRow = left_bindings.iter().map(|b| (b.clone(), None)).collect();
3777 cand.push((rb.to_string(), Some(right.clone())));
3778 if keep_row(&cand, post, removed, ctx)? {
3781 out.push(cand);
3782 }
3783 }
3784 Ok(())
3785}
3786
3787fn join_lateral(
3794 left_src: &mut dyn LeftSource,
3795 join: &Join,
3796 resolve: &Resolver,
3797 budget: Option<usize>,
3798 post: Option<&Expr>,
3799 ctx: EvalCtx,
3800) -> Result<(Vec<JoinedRow>, usize, usize, usize)> {
3801 let sub = join
3802 .table
3803 .sub
3804 .as_deref()
3805 .ok_or_else(|| anyhow::anyhow!("LATERAL requires a subquery"))?;
3806 let rb = join.table.binding();
3807 let mut out: Vec<JoinedRow> = vec![];
3808 let mut removed = 0usize;
3809 let mut consumed = 0usize;
3810 let mut produced = 0usize;
3811 while let Some(left) = {
3812 if budget.is_some_and(|b| out.len() >= b) { None } else { left_src.next_left()? }
3813 } {
3814 consumed += 1;
3815 let scope = bind(&left, ctx);
3816 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), Some(&scope))?;
3817 produced += rows.len();
3818 let mut matched = false;
3819 for r in rows {
3820 let m = match r {
3821 Value::Object(m) => m,
3822 _ => Map::new(),
3823 };
3824 let mut named = Map::new();
3825 for (i, c) in cols.iter().enumerate() {
3826 let name = join.table.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
3827 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
3828 }
3829 let mut cand = left.clone();
3830 cand.push((rb.clone(), Some(Value::Object(named))));
3831 let on_ok = match &join.on {
3832 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
3833 None => true,
3834 };
3835 if !on_ok {
3836 continue;
3837 }
3838 matched = true;
3839 if keep_row(&cand, post, &mut removed, ctx)? {
3840 out.push(cand);
3841 }
3842 }
3843 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
3844 let mut cand = left.clone();
3845 cand.push((rb.clone(), None));
3846 if keep_row(&cand, post, &mut removed, ctx)? {
3847 out.push(cand);
3848 }
3849 }
3850 }
3851 Ok((out, removed, consumed, produced))
3852}
3853
3854#[allow(clippy::too_many_arguments)]
3860fn join_nested_loop(
3861 left_src: &mut dyn LeftSource,
3862 left_bindings: &[String],
3863 join: &Join,
3864 right_rows: &[Value],
3865 rb: &str,
3866 budget: Option<usize>,
3867 post: Option<&Expr>,
3868 ctx: EvalCtx,
3869) -> Result<(Vec<JoinedRow>, usize, usize)> {
3870 let mut out: Vec<JoinedRow> = vec![];
3871 let mut removed = 0usize;
3872 let mut right_matched = vec![false; right_rows.len()];
3874
3875 let mut consumed = 0usize;
3876 while let Some(left) = {
3877 if budget.is_some_and(|b| out.len() >= b) {
3878 None
3881 } else {
3882 left_src.next_left()?
3883 }
3884 } {
3885 consumed += 1;
3886 let left = &left;
3887 let mut matched = false;
3890 for (ri, right) in right_rows.iter().enumerate() {
3891 let mut cand: JoinedRow = left.clone();
3892 cand.push((rb.to_string(), Some(right.clone())));
3893 let joins_here = match &join.on {
3894 None => true,
3896 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
3900 };
3901 if joins_here {
3902 matched = true;
3903 right_matched[ri] = true;
3904 if keep_row(&cand, post, &mut removed, ctx)? {
3905 out.push(cand);
3906 }
3907 }
3908 }
3909 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
3911 let mut cand: JoinedRow = left.clone();
3912 cand.push((rb.to_string(), None));
3913 if keep_row(&cand, post, &mut removed, ctx)? {
3914 out.push(cand);
3915 }
3916 }
3917 }
3918
3919 if !budget.is_some_and(|b| out.len() >= b) {
3924 emit_unmatched_right(
3925 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
3926 &mut removed, ctx,
3927 )?;
3928 }
3929 Ok((out, removed, consumed))
3930}
3931
3932#[allow(clippy::too_many_arguments)]
3940fn join_hash(
3941 left_src: &mut dyn LeftSource,
3942 left_bindings: &[String],
3943 join: &Join,
3944 right_rows: &[Value],
3945 rb: &str,
3946 keys: &[(Expr, Expr)],
3947 budget: Option<usize>,
3948 post: Option<&Expr>,
3949 ctx: EvalCtx,
3950) -> Result<(Vec<JoinedRow>, usize, usize)> {
3951 debug_assert!(!keys.is_empty(), "the planner must not choose Hash with no keys");
3952
3953 let side = sqljoin::HashSide::build(right_rows.len(), |i| {
3955 let one: JoinedRow = vec![(rb.to_string(), Some(right_rows[i].clone()))];
3958 let b = bind(&one, ctx);
3959 let mut k = Vec::with_capacity(keys.len());
3960 for (_, right_expr) in keys {
3961 match sqljoin::hkey(&eval(right_expr, &b)?) {
3962 Some(h) => k.push(h),
3963 None => return Ok(None),
3965 }
3966 }
3967 Ok(Some(k))
3968 })?;
3969
3970 let mut out: Vec<JoinedRow> = vec![];
3972 let mut removed = 0usize;
3973 let mut right_matched = vec![false; right_rows.len()];
3974
3975 let mut consumed = 0usize;
3976 while let Some(left) = {
3977 if budget.is_some_and(|b| out.len() >= b) {
3978 None
3979 } else {
3980 left_src.next_left()?
3981 }
3982 } {
3983 consumed += 1;
3984 let left = &left;
3985 let lb = bind(left, ctx);
3986 let mut lk = Vec::with_capacity(keys.len());
3987 let mut null_key = false;
3988 for (left_expr, _) in keys {
3989 match sqljoin::hkey(&eval(left_expr, &lb)?) {
3990 Some(h) => lk.push(h),
3991 None => {
3992 null_key = true;
3993 break;
3994 }
3995 }
3996 }
3997
3998 let mut matched = false;
3999 if !null_key {
4003 for &ri in side.probe(&lk) {
4004 let mut cand: JoinedRow = left.clone();
4005 cand.push((rb.to_string(), Some(right_rows[ri].clone())));
4006 let joins_here = match &join.on {
4008 None => true,
4009 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4010 };
4011 if joins_here {
4012 matched = true;
4013 right_matched[ri] = true;
4014 if keep_row(&cand, post, &mut removed, ctx)? {
4015 out.push(cand);
4016 }
4017 }
4018 }
4019 }
4020 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4021 let mut cand: JoinedRow = left.clone();
4022 cand.push((rb.to_string(), None));
4023 if keep_row(&cand, post, &mut removed, ctx)? {
4024 out.push(cand);
4025 }
4026 }
4027 }
4028
4029 if !budget.is_some_and(|b| out.len() >= b) {
4032 emit_unmatched_right(
4033 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4034 &mut removed, ctx,
4035 )?;
4036 }
4037 Ok((out, removed, consumed))
4038}
4039
4040fn prefilter(
4047 rows: Vec<Value>,
4048 binding: &str,
4049 push: &Pushdown,
4050 plan: &mut Plan,
4051 ctx: EvalCtx,
4052) -> Result<Vec<Value>> {
4053 let Some(preds) = push.for_binding(binding) else { return Ok(rows) };
4054 if preds.is_empty() {
4055 return Ok(rows);
4056 }
4057 let in_rows = rows.len();
4058 let mut kept = Vec::with_capacity(rows.len());
4059 for row in rows {
4060 let one: JoinedRow = vec![(binding.to_string(), Some(row))];
4061 let b = bind(&one, ctx);
4062 let mut keep = true;
4063 for p in preds {
4064 if truthy(&eval(p, &b)?) != Some(true) {
4070 keep = false;
4071 break;
4072 }
4073 }
4074 if keep {
4075 if let Some((_, Some(v))) = one.into_iter().next() {
4077 kept.push(v);
4078 }
4079 }
4080 }
4081 plan.push(Stage::Prefilter {
4082 binding: binding.to_string(),
4083 predicates: preds.len(),
4084 in_rows,
4085 out_rows: kept.len(),
4086 });
4087 Ok(kept)
4088}
4089
4090fn fetch(t: &TableRef, resolve: &Resolver, ctx: EvalCtx) -> Result<Box<dyn Relation>> {
4093 if let Some(sub) = &t.sub {
4097 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), ctx.outer)?;
4098 let out = rows
4099 .into_iter()
4100 .map(|r| {
4101 let m = match r {
4102 Value::Object(m) => m,
4103 _ => Map::new(),
4104 };
4105 let mut named = Map::new();
4106 for (i, c) in cols.iter().enumerate() {
4107 let name = t.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
4108 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
4111 }
4112 Value::Object(named)
4113 })
4114 .collect();
4115 return Ok(from_vec(out));
4116 }
4117
4118 if let Some(args) = &t.args {
4122 let empty: JoinedRow = vec![];
4123 let scope = bind(&empty, ctx);
4124 let col = |i: usize, default: &str| -> String {
4125 t.col_aliases.get(i).cloned().unwrap_or_else(|| default.to_string())
4126 };
4127 let rows: Vec<Value> = match t.name.as_str() {
4128 "generate_series" => {
4129 let a = num(&eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_series() needs a start"))?, &scope)?);
4130 let b = num(&eval(args.get(1).ok_or_else(|| anyhow::anyhow!("generate_series() needs a stop"))?, &scope)?);
4131 let step = match args.get(2) {
4132 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4133 None => 1.0,
4134 };
4135 match (a, b) {
4136 (Some(a), Some(b)) if step != 0.0 => {
4138 let mut out = vec![];
4139 let mut x = a;
4140 while (step > 0.0 && x <= b) || (step < 0.0 && x >= b) {
4141 let mut m = Map::new();
4142 m.insert(col(0, "generate_series"), from_f64(x));
4143 out.push(Value::Object(m));
4144 x += step;
4145 if out.len() > 1_000_000 {
4146 bail!("generate_series() would produce more than a million rows");
4147 }
4148 }
4149 out
4150 }
4151 (Some(_), Some(_)) => bail!("generate_series() step cannot equal zero"),
4152 _ => vec![],
4153 }
4154 }
4155 "unnest" => match eval(args.first().ok_or_else(|| anyhow::anyhow!("unnest() needs an array"))?, &scope)? {
4156 Value::Array(items) => items
4157 .into_iter()
4158 .map(|v| {
4159 let mut m = Map::new();
4160 m.insert(col(0, "unnest"), v);
4161 Value::Object(m)
4162 })
4163 .collect(),
4164 _ => vec![],
4166 },
4167 "generate_subscripts" => {
4172 let dim = match args.get(1) {
4173 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4174 None => 1.0,
4175 };
4176 match eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_subscripts() needs an array"))?, &scope)? {
4177 Value::Array(items) if dim == 1.0 => (1..=items.len())
4181 .map(|i| {
4182 let mut m = Map::new();
4183 m.insert(col(0, "generate_subscripts"), from_f64(i as f64));
4184 Value::Object(m)
4185 })
4186 .collect(),
4187 _ => vec![],
4188 }
4189 }
4190 "pg_partition_tree" | "pg_partition_ancestors" => vec![],
4193 other => bail!(
4194 "the table function {}() is not implemented. It is refused rather \
4195 than answered with no rows, because an empty relation reads as \
4196 missing DATA rather than a missing feature",
4197 other
4198 ),
4199 };
4200 return Ok(from_vec(rows));
4201 }
4202
4203 match resolve(&t.name)? {
4204 Some(rel) => Ok(rel),
4205 None => bail!("relation {:?} does not exist", t.name),
4208 }
4209}
4210
4211trait LeftSource {
4218 fn next_left(&mut self) -> Result<Option<JoinedRow>>;
4219 fn hint(&self) -> Option<usize>;
4221 fn take_rows(&mut self) -> Vec<JoinedRow>;
4223 fn stats(&self) -> Option<(usize, usize)> {
4225 None
4226 }
4227}
4228
4229struct StreamLeft<'a> {
4235 rel: Box<dyn Relation>,
4236 binding: String,
4237 preds: Vec<Expr>,
4238 ctx: EvalCtx<'a>,
4239 pulled: usize,
4243 kept: usize,
4244}
4245
4246impl<'a> LeftSource for StreamLeft<'a> {
4247 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4248 while let Some(row) = self.rel.next_row()? {
4249 self.pulled += 1;
4250 let one: JoinedRow = vec![(self.binding.clone(), Some(row))];
4251 if !self.preds.is_empty() {
4252 let b = bind(&one, self.ctx);
4253 let mut keep = true;
4254 for p in &self.preds {
4255 if truthy(&eval(p, &b)?) != Some(true) {
4256 keep = false;
4257 break;
4258 }
4259 }
4260 if !keep {
4261 continue;
4262 }
4263 }
4264 self.kept += 1;
4265 return Ok(Some(one));
4266 }
4267 Ok(None)
4268 }
4269 fn hint(&self) -> Option<usize> {
4270 self.rel.size_hint()
4275 }
4276 fn take_rows(&mut self) -> Vec<JoinedRow> {
4277 let mut out = vec![];
4280 while let Ok(Some(r)) = self.next_left() {
4281 out.push(r);
4282 }
4283 out
4284 }
4285 fn stats(&self) -> Option<(usize, usize)> {
4286 Some((self.pulled, self.kept))
4287 }
4288}
4289
4290struct VecLeft {
4293 rows: Vec<JoinedRow>,
4294 at: usize,
4295}
4296
4297impl LeftSource for VecLeft {
4298 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4299 let r = self.rows.get(self.at).cloned();
4300 if r.is_some() {
4301 self.at += 1;
4302 }
4303 Ok(r)
4304 }
4305 fn hint(&self) -> Option<usize> {
4306 Some(self.rows.len().saturating_sub(self.at))
4307 }
4308 fn take_rows(&mut self) -> Vec<JoinedRow> {
4309 let mut v = std::mem::take(&mut self.rows);
4310 if self.at > 0 {
4311 v = v.split_off(self.at);
4312 }
4313 self.at = 0;
4314 v
4315 }
4316}
4317
4318fn drain(mut rel: Box<dyn Relation>) -> Result<Vec<Value>> {
4325 let mut out = Vec::with_capacity(rel.size_hint().unwrap_or(0));
4326 while let Some(row) = rel.next_row()? {
4327 out.push(row);
4328 }
4329 Ok(out)
4330}
4331
4332pub fn run(sql: &str, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
4334 let sel = parse(sql)?;
4335 execute(&sel, resolve)
4336}
4337
4338#[cfg(test)]
4339mod lexer_tests {
4340 use super::*;
4341
4342 fn kinds(src: &str) -> Vec<Tok> {
4343 let mut t = lex(src).expect("lexes");
4344 t.pop(); t
4346 }
4347
4348 #[test]
4349 fn a_word_keeps_both_its_canonical_and_raw_spelling() {
4350 assert_eq!(
4353 kinds("Select"),
4354 vec![Tok::Word { upper: "SELECT".into(), raw: "Select".into() }]
4355 );
4356 }
4357
4358 #[test]
4359 fn a_quoted_identifier_is_never_a_keyword() {
4360 assert_eq!(kinds(r#""select""#), vec![Tok::Quoted("select".into())]);
4361 assert_eq!(kinds(r#""Name""#), vec![Tok::Quoted("Name".into())]);
4363 }
4364
4365 #[test]
4366 fn a_doubled_quote_is_one_literal_quote() {
4367 assert_eq!(kinds("'it''s'"), vec![Tok::Str("it's".into())]);
4368 assert_eq!(kinds(r#""a""b""#), vec![Tok::Quoted("a\"b".into())]);
4369 }
4370
4371 #[test]
4372 fn an_E_string_decodes_the_escapes_catalogue_sql_uses() {
4373 assert_eq!(kinds(r"E'\n'"), vec![Tok::Str("\n".into())]);
4375 assert_eq!(kinds(r"E'a\tb'"), vec![Tok::Str("a\tb".into())]);
4376 assert_eq!(kinds(r"E'\q'"), vec![Tok::Str("q".into())]);
4378 }
4379
4380 #[test]
4381 fn operators_match_longest_first() {
4382 assert_eq!(kinds("!~*"), vec![Tok::Op("!~*".into())]);
4384 assert_eq!(kinds("!~"), vec![Tok::Op("!~".into())]);
4385 assert_eq!(kinds("~*"), vec![Tok::Op("~*".into())]);
4386 assert_eq!(kinds("<>"), vec![Tok::Op("<>".into())]);
4387 assert_eq!(kinds("!="), vec![Tok::Op("!=".into())]);
4388 assert_eq!(kinds(">="), vec![Tok::Op(">=".into())]);
4389 assert_eq!(kinds("::"), vec![Tok::Op("::".into())]);
4390 assert_eq!(kinds("||"), vec![Tok::Op("||".into())]);
4391 assert_eq!(kinds("~"), vec![Tok::Op("~".into())]);
4392 }
4393
4394 #[test]
4395 fn comments_are_skipped_including_nested_block_comments() {
4396 assert_eq!(kinds("1 -- trailing\n"), vec![Tok::Num(1.0)]);
4397 assert_eq!(kinds("1 /* a */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4398 assert_eq!(kinds("1 /* a /* b */ c */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4400 assert!(lex("1 /* unterminated").is_err());
4401 }
4402
4403 #[test]
4404 fn numbers_parse_including_fractions_and_exponents() {
4405 assert_eq!(kinds("42"), vec![Tok::Num(42.0)]);
4406 assert_eq!(kinds("4.5"), vec![Tok::Num(4.5)]);
4407 assert_eq!(kinds(".5"), vec![Tok::Num(0.5)]);
4408 assert_eq!(kinds("1e3"), vec![Tok::Num(1000.0)]);
4409 assert_eq!(kinds("1e-2"), vec![Tok::Num(0.01)]);
4410 assert_eq!(
4412 kinds("1e"),
4413 vec![Tok::Num(1.0), Tok::Word { upper: "E".into(), raw: "e".into() }]
4414 );
4415 }
4416
4417 #[test]
4418 fn an_unterminated_literal_is_an_error_not_a_truncation() {
4419 assert!(lex("'abc").is_err());
4420 assert!(lex(r#""abc"#).is_err());
4421 }
4422
4423 #[test]
4424 fn an_unknown_character_is_REFUSED_rather_than_skipped() {
4425 let e = lex("SELECT 1 @ 2").unwrap_err().to_string();
4428 assert!(e.contains('@'), "{}", e);
4429 }
4430
4431 #[test]
4432 fn the_real_dn_query_lexes() {
4433 let sql = r#"SELECT n.nspname AS "Name",
4434 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
4435 FROM pg_catalog.pg_namespace n
4436 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
4437 ORDER BY 1;"#;
4438 let toks = lex(sql).expect("psql's \\dn must lex");
4439 assert!(toks.contains(&Tok::Quoted("Name".into())));
4440 assert!(toks.contains(&Tok::Op("!~".into())));
4441 assert!(toks.contains(&Tok::Op("<>".into())));
4442 assert!(toks.contains(&Tok::Str("^pg_".into())));
4443 }
4444
4445 #[test]
4446 fn the_real_dt_query_lexes() {
4447 let sql = r#"SELECT n.nspname as "Schema", c.relname as "Name",
4448 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' END as "Type",
4449 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
4450 FROM pg_catalog.pg_class c
4451 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
4452 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
4453 WHERE c.relkind IN ('r','p','')
4454 AND n.nspname <> 'pg_catalog'
4455 AND n.nspname !~ '^pg_toast'
4456 AND pg_catalog.pg_table_is_visible(c.oid)
4457 ORDER BY 1,2;"#;
4458 let toks = lex(sql).expect("psql's \\dt must lex");
4459 assert!(toks.iter().any(|t| t.is_kw("CASE")));
4460 assert!(toks.iter().any(|t| t.is_kw("LEFT")));
4461 assert!(toks.iter().any(|t| t.is_kw("JOIN")));
4462 assert!(toks.contains(&Tok::Str(String::new())));
4464 }
4465}
4466
4467#[cfg(test)]
4468mod parser_tests {
4469 use super::*;
4470 use serde_json::json;
4471
4472 fn col(qual: Option<&str>, name: &str) -> Expr {
4473 Expr::Column { qual: qual.map(str::to_string), name: name.to_string() }
4474 }
4475
4476 #[test]
4477 fn a_bare_select_list_and_from() {
4478 let s = parse("SELECT a, b FROM t").unwrap();
4479 assert_eq!(s.items.len(), 2);
4480 assert_eq!(s.items[0].expr, col(None, "a"));
4481 assert_eq!(s.from.unwrap().name, "t");
4482 }
4483
4484 #[test]
4485 fn a_clause_keyword_is_never_read_as_a_bare_alias() {
4486 let s = parse("SELECT a FROM t WHERE a = 1").unwrap();
4490 assert_eq!(s.from.clone().unwrap().alias, None);
4491 assert!(s.where_.is_some(), "the WHERE clause must survive");
4492 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4493 assert_eq!(s.from.unwrap().alias, None);
4494 assert_eq!(s.order_by.len(), 1);
4495 }
4496
4497 #[test]
4498 fn a_real_alias_is_kept_in_both_spellings() {
4499 assert_eq!(parse("SELECT a FROM t x").unwrap().from.unwrap().alias,
4500 Some("x".to_string()));
4501 assert_eq!(parse("SELECT a FROM t AS x").unwrap().from.unwrap().alias,
4502 Some("x".to_string()));
4503 }
4504
4505 #[test]
4506 fn a_tables_binding_is_its_alias_else_its_bare_name() {
4507 let t = TableRef::named("pg_catalog.pg_class", Some("c".into()));
4508 assert_eq!(t.binding(), "c");
4509 let t = TableRef::named("pg_catalog.pg_class", None);
4510 assert_eq!(t.binding(), "pg_class", "the schema is not how a column is addressed");
4511 }
4512
4513 #[test]
4514 fn a_qualified_column_keeps_only_its_immediate_qualifier() {
4515 assert_eq!(parse("SELECT n.nspname FROM x").unwrap().items[0].expr,
4516 col(Some("n"), "nspname"));
4517 assert_eq!(parse("SELECT public.orders.id FROM x").unwrap().items[0].expr,
4520 col(Some("orders"), "id"));
4521 }
4522
4523 #[test]
4524 fn an_alias_may_be_a_quoted_string_with_significant_case() {
4525 let s = parse(r#"SELECT n.nspname AS "Name" FROM x"#).unwrap();
4526 assert_eq!(s.items[0].alias, Some("Name".to_string()));
4527 }
4528
4529 #[test]
4530 fn a_schema_qualified_function_drops_its_schema() {
4531 let s = parse("SELECT pg_catalog.pg_get_userbyid(n.nspowner) FROM x").unwrap();
4534 match &s.items[0].expr {
4535 Expr::Func { name, args } => {
4536 assert_eq!(name, "pg_get_userbyid");
4537 assert_eq!(args.len(), 1);
4538 assert_eq!(args[0], col(Some("n"), "nspowner"));
4539 }
4540 other => panic!("{:?}", other),
4541 }
4542 }
4543
4544 #[test]
4545 fn operator_precedence_matches_sql() {
4546 let s = parse("SELECT 1 FROM t WHERE a = 1 OR b = 2 AND c = 3").unwrap();
4549 match s.where_.unwrap() {
4550 Expr::Binary { op, right, .. } => {
4551 assert_eq!(op, "OR");
4552 assert!(matches!(*right, Expr::Binary { ref op, .. } if op == "AND"),
4553 "AND must bind tighter than OR");
4554 }
4555 other => panic!("{:?}", other),
4556 }
4557 let s = parse("SELECT 1 FROM t WHERE a = 1 AND b = 2").unwrap();
4559 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4560 let s = parse("SELECT 1 + 2 * 3 FROM t").unwrap();
4562 match &s.items[0].expr {
4563 Expr::Binary { op, right, .. } => {
4564 assert_eq!(op, "+");
4565 assert!(matches!(**right, Expr::Binary { ref op, .. } if op == "*"));
4566 }
4567 other => panic!("{:?}", other),
4568 }
4569 }
4570
4571 #[test]
4572 fn parentheses_override_precedence() {
4573 let s = parse("SELECT 1 FROM t WHERE (a = 1 OR b = 2) AND c = 3").unwrap();
4574 match s.where_.unwrap() {
4575 Expr::Binary { op, left, .. } => {
4576 assert_eq!(op, "AND");
4577 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "OR"));
4578 }
4579 other => panic!("{:?}", other),
4580 }
4581 }
4582
4583 #[test]
4584 fn in_and_is_null_and_between_parse_in_both_polarities() {
4585 let s = parse("SELECT 1 FROM t WHERE k IN ('r','p','')").unwrap();
4586 match s.where_.unwrap() {
4587 Expr::InList { list, negated, .. } => {
4588 assert_eq!(list.len(), 3);
4589 assert!(!negated);
4590 assert_eq!(list[2], Expr::Literal(json!("")));
4592 }
4593 other => panic!("{:?}", other),
4594 }
4595 assert!(matches!(parse("SELECT 1 FROM t WHERE k NOT IN (1)").unwrap().where_.unwrap(),
4596 Expr::InList { negated: true, .. }));
4597 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NULL").unwrap().where_.unwrap(),
4598 Expr::IsNull { negated: false, .. }));
4599 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NOT NULL").unwrap().where_.unwrap(),
4600 Expr::IsNull { negated: true, .. }));
4601 let s = parse("SELECT 1 FROM t WHERE n BETWEEN 1 AND 5").unwrap();
4603 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4604 }
4605
4606 #[test]
4607 fn both_case_spellings_parse() {
4608 let s = parse("SELECT CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
4610 ELSE 'other' END FROM t").unwrap();
4611 match &s.items[0].expr {
4612 Expr::Case { operand, whens, else_ } => {
4613 assert!(operand.is_some());
4614 assert_eq!(whens.len(), 2);
4615 assert!(else_.is_some());
4616 }
4617 other => panic!("{:?}", other),
4618 }
4619 let s = parse("SELECT CASE WHEN k = 'r' THEN 1 END FROM t").unwrap();
4621 match &s.items[0].expr {
4622 Expr::Case { operand, whens, else_ } => {
4623 assert!(operand.is_none());
4624 assert_eq!(whens.len(), 1);
4625 assert!(else_.is_none());
4626 }
4627 other => panic!("{:?}", other),
4628 }
4629 assert!(parse("SELECT CASE k END FROM t").is_err());
4631 }
4632
4633 #[test]
4634 fn every_join_flavour_parses_and_an_inner_join_demands_ON() {
4635 for (sql, kind) in [
4636 ("SELECT 1 FROM a JOIN b ON a.x = b.x", JoinKind::Inner),
4637 ("SELECT 1 FROM a INNER JOIN b ON a.x = b.x", JoinKind::Inner),
4638 ("SELECT 1 FROM a LEFT JOIN b ON a.x = b.x", JoinKind::Left),
4639 ("SELECT 1 FROM a LEFT OUTER JOIN b ON a.x = b.x", JoinKind::Left),
4640 ("SELECT 1 FROM a RIGHT JOIN b ON a.x = b.x", JoinKind::Right),
4641 ("SELECT 1 FROM a FULL OUTER JOIN b ON a.x = b.x", JoinKind::Full),
4642 ("SELECT 1 FROM a CROSS JOIN b", JoinKind::Cross),
4643 ] {
4644 let s = parse(sql).unwrap_or_else(|e| panic!("{}: {}", sql, e));
4645 assert_eq!(s.joins.len(), 1, "{}", sql);
4646 assert_eq!(s.joins[0].kind, kind, "{}", sql);
4647 }
4648 let s = parse("SELECT 1 FROM a, b").unwrap();
4650 assert_eq!(s.joins[0].kind, JoinKind::Cross);
4651 assert!(parse("SELECT 1 FROM a LEFT JOIN b").is_err());
4654 assert!(parse("SELECT 1 FROM a JOIN b USING (x)").is_err());
4655 }
4656
4657 #[test]
4658 fn order_by_reads_a_number_as_an_ORDINAL() {
4659 let s = parse("SELECT a, b FROM t ORDER BY 1, 2 DESC").unwrap();
4663 assert_eq!(s.order_by.len(), 2);
4664 assert_eq!(s.order_by[0].ordinal, Some(1));
4665 assert_eq!(s.order_by[0].dir, Dir::Asc);
4666 assert_eq!(s.order_by[1].ordinal, Some(2));
4667 assert_eq!(s.order_by[1].dir, Dir::Desc);
4668 let s = parse("SELECT a FROM t ORDER BY lower(a) ASC").unwrap();
4670 assert!(s.order_by[0].ordinal.is_none());
4671 assert!(s.order_by[0].expr.is_some());
4672 }
4673
4674 #[test]
4675 fn null_ordering_defaults_the_way_postgres_defaults() {
4676 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4677 assert!(!s.order_by[0].nulls_first, "ASC defaults to NULLS LAST");
4678 let s = parse("SELECT a FROM t ORDER BY a DESC").unwrap();
4679 assert!(s.order_by[0].nulls_first, "DESC defaults to NULLS FIRST");
4680 let s = parse("SELECT a FROM t ORDER BY a NULLS FIRST").unwrap();
4681 assert!(s.order_by[0].nulls_first, "an explicit clause wins");
4682 }
4683
4684 #[test]
4685 fn limit_and_offset_parse_in_either_order() {
4686 let s = parse("SELECT a FROM t LIMIT 5 OFFSET 2").unwrap();
4687 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
4688 let s = parse("SELECT a FROM t OFFSET 2 LIMIT 5").unwrap();
4689 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
4690 let s = parse("SELECT a FROM t LIMIT ALL").unwrap();
4691 assert_eq!(s.limit, None);
4692 }
4693
4694 #[test]
4695 fn casts_parse_and_are_recorded_rather_than_rejected() {
4696 let s = parse("SELECT x::int2 FROM t").unwrap();
4698 assert!(matches!(s.items[0].expr, Expr::Cast { .. }));
4699 let s = parse("SELECT x::pg_catalog.int2[] FROM t").unwrap();
4700 match &s.items[0].expr {
4701 Expr::Cast { ty, .. } => assert_eq!(ty, "int2[]"),
4702 other => panic!("{:?}", other),
4703 }
4704 }
4705
4706 #[test]
4707 fn star_and_qualified_star_parse() {
4708 assert_eq!(parse("SELECT * FROM t").unwrap().items[0].expr, Expr::Star);
4709 assert_eq!(parse("SELECT c.* FROM t c").unwrap().items[0].expr,
4710 Expr::QualifiedStar("c".into()));
4711 match &parse("SELECT count(*) FROM t").unwrap().items[0].expr {
4714 Expr::Agg { name, args, order_by, distinct } => {
4715 assert_eq!(name, "count");
4716 assert_eq!(args, &vec![Expr::Star]);
4717 assert!(order_by.is_empty());
4718 assert!(!distinct);
4719 }
4720 other => panic!("{:?}", other),
4721 }
4722 assert!(matches!(
4724 &parse("SELECT lower(s) FROM t").unwrap().items[0].expr,
4725 Expr::Func { name, .. } if name == "lower"
4726 ));
4727 }
4728
4729 #[test]
4730 fn an_aggregate_carries_its_own_DISTINCT_and_ORDER_BY() {
4731 match &parse("SELECT array_agg(a.attname ORDER BY a.ord) FROM t a").unwrap().items[0].expr {
4735 Expr::Agg { name, args, order_by, distinct } => {
4736 assert_eq!(name, "array_agg");
4737 assert_eq!(args.len(), 1);
4738 assert_eq!(order_by.len(), 1);
4739 assert!(matches!(order_by[0].dir, Dir::Asc));
4740 assert!(!distinct);
4741 }
4742 other => panic!("{:?}", other),
4743 }
4744 match &parse("SELECT string_agg(DISTINCT s, ',' ORDER BY b DESC NULLS LAST, c) FROM t").unwrap().items[0].expr {
4747 Expr::Agg { name, args, order_by, distinct } => {
4748 assert_eq!(name, "string_agg");
4749 assert_eq!(args.len(), 2, "the separator is an argument, not a sort key");
4750 assert_eq!(order_by.len(), 2);
4751 assert!(matches!(order_by[0].dir, Dir::Desc));
4752 assert!(!order_by[0].nulls_first, "NULLS LAST overrides the DESC default");
4753 assert!(matches!(order_by[1].dir, Dir::Asc));
4754 assert!(distinct);
4755 }
4756 other => panic!("{:?}", other),
4757 }
4758 assert!(parse("SELECT lower(DISTINCT s) FROM t").is_err());
4761 }
4762
4763 #[test]
4764 fn a_parenthesis_free_function_parses_as_a_zero_arg_call() {
4765 match &parse("SELECT current_schema FROM t").unwrap().items[0].expr {
4767 Expr::Func { name, args } => {
4768 assert_eq!(name, "current_schema");
4769 assert!(args.is_empty());
4770 }
4771 other => panic!("{:?}", other),
4772 }
4773 }
4774
4775 #[test]
4776 fn GROUP_BY_and_HAVING_parse_and_what_remains_is_refused_by_name() {
4777 let s = parse("SELECT a, count(*) FROM t GROUP BY a").unwrap();
4780 assert_eq!(s.group_by, vec![Expr::Column { qual: None, name: "a".into() }]);
4781 assert!(s.having.is_none());
4782
4783 let s = parse("SELECT a, b, count(*) FROM t GROUP BY a, b HAVING count(*) > 1").unwrap();
4784 assert_eq!(s.group_by.len(), 2);
4785 assert!(s.having.is_some());
4786
4787 let e = parse("SELECT a FROM t HAVING a > 1").unwrap_err().to_string();
4791 assert!(e.contains("HAVING needs a GROUP BY"), "{}", e);
4792
4793 for (sql, needle) in [
4794 ("SELECT DISTINCT ON (a) a FROM t", "DISTINCT ON"),
4795 ("SELECT a, count(*) FROM t GROUP BY ROLLUP (a)", "ROLLUP"),
4796 ("SELECT a, count(*) FROM t GROUP BY CUBE (a)", "CUBE"),
4797 ] {
4798 let e = parse(sql).unwrap_err().to_string();
4799 assert!(e.contains(needle), "{} -> {}", sql, e);
4800 }
4801 assert!(parse("SELECT a FROM t JUNK JUNK2").is_err());
4803 }
4804
4805 #[test]
4806 fn THE_dn_QUERY_parses_completely() {
4807 let s = parse(
4808 r#"SELECT n.nspname AS "Name",
4809 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
4810 FROM pg_catalog.pg_namespace n
4811 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
4812 ORDER BY 1;"#,
4813 )
4814 .expect("psql's \\dn must parse");
4815
4816 assert_eq!(s.items.len(), 2);
4817 assert_eq!(s.items[0].alias, Some("Name".into()));
4818 assert_eq!(s.items[1].alias, Some("Owner".into()));
4819 let from = s.from.unwrap();
4820 assert_eq!(from.name, "pg_catalog.pg_namespace");
4821 assert_eq!(from.binding(), "n");
4822 assert!(s.where_.is_some());
4823 assert_eq!(s.order_by[0].ordinal, Some(1));
4824 }
4825
4826 #[test]
4827 fn THE_dt_QUERY_parses_completely() {
4828 let s = parse(
4829 r#"SELECT n.nspname as "Schema",
4830 c.relname as "Name",
4831 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
4832 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
4833 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
4834 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
4835 WHEN 'I' THEN 'partitioned index' END as "Type",
4836 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
4837 FROM pg_catalog.pg_class c
4838 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
4839 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
4840 WHERE c.relkind IN ('r','p','')
4841 AND n.nspname <> 'pg_catalog'
4842 AND n.nspname !~ '^pg_toast'
4843 AND n.nspname <> 'information_schema'
4844 AND pg_catalog.pg_table_is_visible(c.oid)
4845 ORDER BY 1,2;"#,
4846 )
4847 .expect("psql's \\dt must parse");
4848
4849 assert_eq!(s.items.len(), 4);
4850 assert_eq!(s.items[2].alias, Some("Type".into()));
4851 match &s.items[2].expr {
4852 Expr::Case { whens, .. } => assert_eq!(whens.len(), 9, "all nine branches"),
4853 other => panic!("{:?}", other),
4854 }
4855 assert_eq!(s.joins.len(), 2);
4856 assert!(s.joins.iter().all(|j| j.kind == JoinKind::Left && j.on.is_some()));
4857 assert_eq!(s.from.unwrap().binding(), "c");
4858 assert_eq!(s.order_by.len(), 2);
4859 assert_eq!(
4860 (s.order_by[0].ordinal, s.order_by[1].ordinal),
4861 (Some(1), Some(2))
4862 );
4863 }
4864}
4865
4866#[cfg(test)]
4867mod eval_tests {
4868 use super::*;
4869 use serde_json::json;
4870
4871 fn one(row: &Value) -> Bound<'_> {
4873 Bound::new(vec![("t".to_string(), Some(row))])
4874 }
4875
4876 fn ev(sql_expr: &str, row: &Value) -> Result<Value> {
4877 let s = parse(&format!("SELECT {} FROM t", sql_expr))?;
4878 eval(&s.items[0].expr, &one(row))
4879 }
4880
4881 fn v(sql_expr: &str, row: &Value) -> Value {
4882 ev(sql_expr, row).unwrap_or_else(|e| panic!("{}: {}", sql_expr, e))
4883 }
4884
4885 #[test]
4886 fn literals_and_columns_resolve() {
4887 let r = json!({"a": 1, "s": "x", "b": true, "n": null});
4888 assert_eq!(v("42", &r), json!(42));
4889 assert_eq!(v("'hi'", &r), json!("hi"));
4890 assert_eq!(v("NULL", &r), Value::Null);
4891 assert_eq!(v("TRUE", &r), json!(true));
4892 assert_eq!(v("a", &r), json!(1));
4893 assert_eq!(v("t.a", &r), json!(1));
4894 assert_eq!(v("s", &r), json!("x"));
4895 assert_eq!(v("nosuch", &r), Value::Null);
4898 }
4899
4900 #[test]
4901 fn an_unknown_table_ALIAS_is_an_error_while_an_unknown_column_is_null() {
4902 let r = json!({"a": 1});
4905 assert_eq!(v("t.nosuch", &r), Value::Null);
4906 let e = ev("zz.a", &r).unwrap_err().to_string();
4907 assert!(e.contains("zz"), "{}", e);
4908 }
4909
4910 #[test]
4913 fn every_comparison_over_NULL_is_UNKNOWN_including_null_equals_null() {
4914 let r = json!({"n": null, "a": 1});
4915 assert_eq!(v("n = 1", &r), Value::Null);
4916 assert_eq!(v("n != 1", &r), Value::Null);
4917 assert_eq!(v("n < 1", &r), Value::Null);
4918 assert_eq!(v("n = n", &r), Value::Null);
4920 assert_eq!(v("n = NULL", &r), Value::Null);
4921 }
4922
4923 #[test]
4924 fn NOT_UNKNOWN_is_UNKNOWN_not_true() {
4925 let r = json!({"n": null});
4929 assert_eq!(v("NOT (n = 1)", &r), Value::Null);
4930 assert_eq!(v("NOT TRUE", &r), json!(false));
4931 assert_eq!(v("NOT FALSE", &r), json!(true));
4932 }
4933
4934 #[test]
4935 fn AND_and_OR_follow_the_three_valued_truth_tables() {
4936 let r = json!({"n": null});
4937 assert_eq!(v("FALSE AND n = 1", &r), json!(false));
4939 assert_eq!(v("TRUE AND n = 1", &r), Value::Null);
4941 assert_eq!(v("TRUE OR n = 1", &r), json!(true));
4943 assert_eq!(v("FALSE OR n = 1", &r), Value::Null);
4945 assert_eq!(v("TRUE AND TRUE", &r), json!(true));
4947 assert_eq!(v("TRUE AND FALSE", &r), json!(false));
4948 assert_eq!(v("FALSE OR FALSE", &r), json!(false));
4949 }
4950
4951 #[test]
4952 fn IS_NULL_is_the_one_predicate_that_is_never_unknown() {
4953 let r = json!({"n": null, "a": 1});
4954 assert_eq!(v("n IS NULL", &r), json!(true));
4955 assert_eq!(v("n IS NOT NULL", &r), json!(false));
4956 assert_eq!(v("a IS NULL", &r), json!(false));
4957 assert_eq!(v("a IS NOT NULL", &r), json!(true));
4958 assert_eq!(v("nosuch IS NULL", &r), json!(true));
4961 }
4962
4963 #[test]
4964 fn NOT_IN_with_a_NULL_in_the_list_is_UNKNOWN_the_classic_trap() {
4965 let r = json!({"a": 2});
4966 assert_eq!(v("a IN (1, 2)", &r), json!(true));
4967 assert_eq!(v("a IN (1, 3)", &r), json!(false));
4968 assert_eq!(v("a NOT IN (1, 3)", &r), json!(true));
4969 assert_eq!(v("a NOT IN (1, NULL)", &r), Value::Null);
4972 assert_eq!(v("a IN (2, NULL)", &r), json!(true));
4974 assert_eq!(v("nosuch IN (1)", &r), Value::Null);
4976 }
4977
4978 #[test]
4981 fn comparisons_work_across_numbers_strings_and_booleans() {
4982 let r = json!({"n": 5, "s": "b", "t": true});
4983 assert_eq!(v("n > 3", &r), json!(true));
4984 assert_eq!(v("n <= 5", &r), json!(true));
4985 assert_eq!(v("s < 'c'", &r), json!(true));
4986 assert_eq!(v("s > 'c'", &r), json!(false));
4987 assert_eq!(v("n = '5'", &r), json!(true));
4990 assert_eq!(v("n = '5.0'", &r), json!(true));
4991 assert_eq!(v("n = 'five'", &r), json!(false));
4994 }
4995
4996 #[test]
4997 fn the_regex_operators_use_the_SAME_matcher_as_NQL() {
4998 let r = json!({"s": "pg_catalog"});
5001 assert_eq!(v("s ~ '^pg_'", &r), json!(true));
5002 assert_eq!(v("s !~ '^pg_'", &r), json!(false));
5003 assert_eq!(v("s ~ '^PG_'", &r), json!(false));
5004 assert_eq!(v("s ~* '^PG_'", &r), json!(true));
5005 assert_eq!(v("s !~ '^zz'", &r), json!(true));
5006 assert_eq!(v("nosuch ~ '^x'", &r), Value::Null);
5008 assert_eq!(v("s ~ '^(pg_catalog)$'", &r), json!(true));
5011 assert_eq!(v("s ~ '^(pg_.*)$'", &r), json!(true));
5012 assert_eq!(v("s ~ '^(public|pg_catalog)$'", &r), json!(true));
5013 assert_eq!(v("s ~ '^pg_[a-z]+$'", &r), json!(true));
5014 assert_eq!(v("s ~ '^pg_[0-9]+$'", &r), json!(false));
5015 let e = ev("s ~ 'a{2}'", &r).unwrap_err().to_string();
5017 assert!(e.contains("interval"), "{}", e);
5018 }
5019
5020 #[test]
5021 fn like_works_in_all_four_spellings() {
5022 let r = json!({"s": "Acme Pool"});
5023 assert_eq!(v("s LIKE 'Acme%'", &r), json!(true));
5024 assert_eq!(v("s LIKE 'acme%'", &r), json!(false));
5025 assert_eq!(v("s ILIKE 'acme%'", &r), json!(true));
5026 assert_eq!(v("s NOT LIKE 'zz%'", &r), json!(true));
5027 assert_eq!(v("nosuch LIKE 'x'", &r), Value::Null);
5028 }
5029
5030 #[test]
5031 fn arithmetic_and_concatenation_propagate_null_and_refuse_div_by_zero() {
5032 let r = json!({"a": 7, "b": 2});
5033 assert_eq!(v("a + b", &r), json!(9));
5034 assert_eq!(v("a - b", &r), json!(5));
5035 assert_eq!(v("a * b", &r), json!(14));
5036 assert_eq!(v("a / b", &r), json!(3.5));
5037 assert_eq!(v("a % b", &r), json!(1));
5038 assert_eq!(v("-a", &r), json!(-7));
5039 assert_eq!(v("a / b", &r), json!(3.5));
5042 assert_eq!(v("b / a", &r), json!(2.0 / 7.0));
5043 assert_eq!(v("'x' || 'y'", &r), json!("xy"));
5044 assert_eq!(v("'x' || nosuch", &r), Value::Null);
5045 assert_eq!(v("a + nosuch", &r), Value::Null);
5046 assert!(ev("a / 0", &r).is_err());
5049 assert!(ev("a % 0", &r).is_err());
5050 }
5051
5052 #[test]
5053 fn a_cast_is_transparent_rather_than_rejected() {
5054 let r = json!({"a": 7});
5057 assert_eq!(v("a::int2", &r), json!(7));
5058 assert_eq!(v("a::pg_catalog.int2[]", &r), json!(7));
5059 }
5060
5061 #[test]
5064 fn a_simple_CASE_picks_the_matching_branch() {
5065 let expr = "CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
5067 WHEN 'i' THEN 'index' END";
5068 assert_eq!(v(expr, &json!({"k": "r"})), json!("table"));
5069 assert_eq!(v(expr, &json!({"k": "v"})), json!("view"));
5070 assert_eq!(v(expr, &json!({"k": "i"})), json!("index"));
5071 assert_eq!(v(expr, &json!({"k": "z"})), Value::Null);
5074 }
5075
5076 #[test]
5077 fn a_searched_CASE_evaluates_predicates_and_UNKNOWN_does_not_match() {
5078 let expr = "CASE WHEN n > 5 THEN 'big' WHEN n > 0 THEN 'small' ELSE 'none' END";
5079 assert_eq!(v(expr, &json!({"n": 9})), json!("big"));
5080 assert_eq!(v(expr, &json!({"n": 2})), json!("small"));
5081 assert_eq!(v(expr, &json!({"n": -1})), json!("none"));
5082 assert_eq!(v(expr, &json!({"other": 1})), json!("none"));
5084 }
5085
5086 #[test]
5087 fn an_ELSE_branch_is_used_when_nothing_matches() {
5088 assert_eq!(
5089 v("CASE k WHEN 'r' THEN 'table' ELSE 'other' END", &json!({"k": "z"})),
5090 json!("other")
5091 );
5092 }
5093
5094 #[test]
5097 fn the_catalogue_functions_psql_calls_all_answer() {
5098 let r = json!({"o": 10, "enc": 6});
5099 assert_eq!(v("pg_get_userbyid(o)", &r), json!("nedb"));
5101 assert_eq!(v("pg_catalog.pg_get_userbyid(o)", &r), json!("nedb"));
5102 assert_eq!(v("pg_table_is_visible(o)", &r), json!(true));
5104 assert_eq!(v("pg_encoding_to_char(enc)", &r), json!("UTF8"));
5105 assert_eq!(v("current_schema", &r), json!("public"));
5106 assert_eq!(v("current_database()", &r), json!("nedb"));
5107 assert_eq!(v("current_user", &r), json!("nedb"));
5108 assert_eq!(v("pg_get_expr(o, o)", &r), Value::Null);
5111 assert_eq!(v("obj_description(o)", &r), Value::Null);
5112 }
5113
5114 #[test]
5115 fn text_and_null_handling_functions_work() {
5116 let r = json!({"s": "AbC", "n": null});
5117 assert_eq!(v("lower(s)", &r), json!("abc"));
5118 assert_eq!(v("upper(s)", &r), json!("ABC"));
5119 assert_eq!(v("length(s)", &r), json!(3));
5120 assert_eq!(v("lower(n)", &r), Value::Null);
5121 assert_eq!(v("coalesce(n, 'fallback')", &r), json!("fallback"));
5122 assert_eq!(v("coalesce(s, 'fallback')", &r), json!("AbC"));
5123 assert_eq!(v("coalesce(n, n)", &r), Value::Null);
5124 assert_eq!(v("nullif(s, 'AbC')", &r), Value::Null);
5125 assert_eq!(v("nullif(s, 'zz')", &r), json!("AbC"));
5126 assert_eq!(v("format_type(20, NULL)", &r), json!("bigint"));
5128 }
5129
5130 #[test]
5131 fn coalesce_does_not_evaluate_past_its_first_non_null() {
5132 let r = json!({"a": 1});
5134 assert_eq!(v("coalesce(a, a / 0)", &r), json!(1));
5135 }
5136
5137 #[test]
5138 fn an_unknown_function_is_REFUSED_rather_than_answered_with_NULL() {
5139 let e = ev("pg_stat_get_numscans(1)", &json!({})).unwrap_err().to_string();
5142 assert!(e.contains("pg_stat_get_numscans"), "{}", e);
5143 assert!(e.contains("refused"), "{}", e);
5144 }
5145
5146 #[test]
5149 fn a_qualified_column_reads_only_its_OWN_binding() {
5150 let a = json!({"name": "left", "x": 1});
5153 let b = json!({"name": "right", "y": 2});
5154 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), Some(&b))]);
5155 let get = |e: &str| {
5156 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5157 eval(&s.items[0].expr, &row).unwrap()
5158 };
5159 assert_eq!(get("a.name"), json!("left"));
5160 assert_eq!(get("b.name"), json!("right"));
5161 assert_eq!(get("name"), json!("left"));
5163 assert_eq!(get("y"), json!(2), "a bare name still finds a later binding");
5164 }
5165
5166 #[test]
5167 fn an_unmatched_LEFT_JOIN_side_reads_as_NULL_not_as_a_missing_column() {
5168 let a = json!({"x": 1});
5171 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), None)]);
5172 let get = |e: &str| {
5173 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5174 eval(&s.items[0].expr, &row).unwrap()
5175 };
5176 assert_eq!(get("b.anything"), Value::Null);
5177 assert_eq!(get("b.anything IS NULL"), json!(true));
5178 assert_eq!(get("a.x"), json!(1));
5179 }
5180}
5181
5182#[cfg(test)]
5183mod exec_tests {
5184 use super::*;
5185 use serde_json::json;
5186
5187 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5189 let owned: Vec<(String, Vec<Value>)> =
5190 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
5191 move |name: &str| {
5192 let bare = name.rsplit('.').next().unwrap_or(name);
5194 Ok(owned
5195 .iter()
5196 .find(|(n, _)| n == name || n == bare)
5197 .map(|(_, r)| from_vec(r.clone())))
5198 }
5199 }
5200
5201 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
5202 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
5203 (cols.into_iter().map(|c| c.name).collect(), rows)
5204 }
5205
5206 fn col(rows: &[Value], name: &str) -> Vec<Value> {
5207 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
5208 }
5209
5210 #[test]
5213 fn select_columns_where_order_limit_offset() {
5214 let t = tables(vec![(
5215 "t",
5216 vec![json!({"a": 3, "s": "c"}), json!({"a": 1, "s": "a"}), json!({"a": 2, "s": "b"})],
5217 )]);
5218 let (names, rows) = go("SELECT a, s FROM t ORDER BY a", &t);
5219 assert_eq!(names, vec!["a", "s"]);
5220 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2), json!(3)]);
5221
5222 let (_, rows) = go("SELECT a FROM t ORDER BY a DESC", &t);
5223 assert_eq!(col(&rows, "a"), vec![json!(3), json!(2), json!(1)]);
5224
5225 let (_, rows) = go("SELECT a FROM t WHERE a > 1 ORDER BY a", &t);
5226 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5227
5228 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 2", &t);
5229 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5230
5231 let (_, rows) = go("SELECT a FROM t ORDER BY a OFFSET 1", &t);
5232 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5233
5234 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 1 OFFSET 1", &t);
5235 assert_eq!(col(&rows, "a"), vec![json!(2)]);
5236
5237 let (_, rows) = go("SELECT a FROM t OFFSET 99", &t);
5239 assert!(rows.is_empty());
5240 }
5241
5242 #[test]
5243 fn an_output_column_takes_its_alias_or_a_derived_name() {
5244 let t = tables(vec![("t", vec![json!({"a": 1})])]);
5247 assert_eq!(go(r#"SELECT a AS "Name" FROM t"#, &t).0, vec!["Name"]);
5248 assert_eq!(go("SELECT a FROM t", &t).0, vec!["a"]);
5249 assert_eq!(go("SELECT lower('X') FROM t", &t).0, vec!["lower"]);
5250 assert_eq!(go("SELECT 1 + 1 FROM t", &t).0, vec!["?column?"]);
5251 assert_eq!(go("SELECT CASE a WHEN 1 THEN 'x' END FROM t", &t).0, vec!["case"]);
5252 }
5253
5254 #[test]
5255 fn star_expands_from_the_rows_and_a_qualified_star_from_one_binding() {
5256 let t = tables(vec![
5257 ("a", vec![json!({"x": 1, "y": 2})]),
5258 ("b", vec![json!({"z": 3})]),
5259 ]);
5260 let (names, rows) = go("SELECT * FROM a", &t);
5261 assert_eq!(names, vec!["x", "y"]);
5262 assert_eq!(rows.len(), 1);
5263
5264 let (names, _) = go("SELECT a.* FROM a CROSS JOIN b", &t);
5265 assert_eq!(names, vec!["x", "y"], "a qualified star takes ONE binding");
5266
5267 let empty = tables(vec![("e", vec![])]);
5270 assert_eq!(go("SELECT * FROM e", &empty).0, Vec::<String>::new());
5271 }
5272
5273 #[test]
5274 fn distinct_dedupes_on_the_projected_values() {
5275 let t = tables(vec![(
5276 "t",
5277 vec![json!({"g": "x"}), json!({"g": "x"}), json!({"g": "y"})],
5278 )]);
5279 let (_, rows) = go("SELECT DISTINCT g FROM t ORDER BY 1", &t);
5280 assert_eq!(col(&rows, "g"), vec![json!("x"), json!("y")]);
5281 let (_, rows) = go("SELECT g FROM t", &t);
5282 assert_eq!(rows.len(), 3, "without DISTINCT every row survives");
5283 }
5284
5285 #[test]
5286 fn order_by_an_ORDINAL_sorts_the_projected_column() {
5287 let t = tables(vec![(
5288 "t",
5289 vec![json!({"a": 2, "b": "z"}), json!({"a": 1, "b": "y"})],
5290 )]);
5291 let (_, rows) = go("SELECT a, b FROM t ORDER BY 1", &t);
5292 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5293 let (_, rows) = go("SELECT a, b FROM t ORDER BY 2 DESC", &t);
5294 assert_eq!(col(&rows, "b"), vec![json!("z"), json!("y")]);
5295 let e = run("SELECT a FROM t ORDER BY 3", &t).unwrap_err().to_string();
5297 assert!(e.contains("out of range"), "{}", e);
5298 }
5299
5300 #[test]
5301 fn order_by_an_expression_may_use_a_column_NOT_in_the_select_list() {
5302 let t = tables(vec![(
5303 "t",
5304 vec![json!({"a": 1, "hidden": 9}), json!({"a": 2, "hidden": 1})],
5305 )]);
5306 let (_, rows) = go("SELECT a FROM t ORDER BY hidden", &t);
5307 assert_eq!(col(&rows, "a"), vec![json!(2), json!(1)]);
5308 }
5309
5310 #[test]
5311 fn null_ordering_follows_the_direction_defaults() {
5312 let t = tables(vec![(
5313 "t",
5314 vec![json!({"a": 2}), json!({"a": null}), json!({"a": 1})],
5315 )]);
5316 assert_eq!(col(&go("SELECT a FROM t ORDER BY a", &t).1, "a"),
5318 vec![json!(1), json!(2), Value::Null]);
5319 assert_eq!(col(&go("SELECT a FROM t ORDER BY a DESC", &t).1, "a"),
5321 vec![Value::Null, json!(2), json!(1)]);
5322 assert_eq!(col(&go("SELECT a FROM t ORDER BY a NULLS FIRST", &t).1, "a"),
5324 vec![Value::Null, json!(1), json!(2)]);
5325 }
5326
5327 #[test]
5328 fn a_where_clause_that_is_UNKNOWN_excludes_the_row() {
5329 let t = tables(vec![(
5330 "t",
5331 vec![json!({"a": 1}), json!({"a": null}), json!({"other": 1})],
5332 )]);
5333 let (_, rows) = go("SELECT a FROM t WHERE a = 1", &t);
5335 assert_eq!(rows.len(), 1);
5336 let (_, rows) = go("SELECT a FROM t WHERE NOT (a = 1)", &t);
5338 assert_eq!(rows.len(), 0, "NOT UNKNOWN must not resurrect a null row");
5339 }
5340
5341 #[test]
5342 fn select_with_no_FROM_returns_exactly_one_row() {
5343 let t = tables(vec![]);
5345 let (names, rows) = go("SELECT 1", &t);
5346 assert_eq!(rows.len(), 1);
5347 assert_eq!(names, vec!["?column?"]);
5348 assert_eq!(go("SELECT current_schema", &t).1.len(), 1);
5349 }
5350
5351 #[test]
5352 fn an_unknown_relation_is_NAMED_rather_than_answered_with_no_rows() {
5353 let t = tables(vec![("t", vec![])]);
5356 let e = run("SELECT a FROM nosuchtable", &t).unwrap_err().to_string();
5357 assert!(e.contains("nosuchtable"), "{}", e);
5358 assert!(e.contains("does not exist"), "{}", e);
5359 }
5360
5361 #[test]
5364 fn an_inner_join_keeps_only_matching_pairs() {
5365 let t = tables(vec![
5366 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5367 ("r", vec![json!({"lid": 1, "v": "x"})]),
5368 ]);
5369 let (_, rows) = go("SELECT l.n, r.v FROM l JOIN r ON r.lid = l.id", &t);
5370 assert_eq!(rows.len(), 1);
5371 assert_eq!(col(&rows, "n"), vec![json!("a")]);
5372 }
5373
5374 #[test]
5375 fn a_LEFT_join_keeps_unmatched_left_rows_with_NULLs() {
5376 let t = tables(vec![
5378 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5379 ("r", vec![json!({"lid": 1, "v": "x"})]),
5380 ]);
5381 let (_, rows) = go("SELECT l.n, r.v FROM l LEFT JOIN r ON r.lid = l.id ORDER BY 1", &t);
5382 assert_eq!(rows.len(), 2);
5383 assert_eq!(col(&rows, "n"), vec![json!("a"), json!("b")]);
5384 assert_eq!(col(&rows, "v"), vec![json!("x"), Value::Null]);
5385 }
5386
5387 #[test]
5388 fn a_RIGHT_join_keeps_unmatched_right_rows_and_FULL_keeps_both() {
5389 let t = tables(vec![
5390 ("l", vec![json!({"id": 1})]),
5391 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5392 ]);
5393 let (_, rows) = go("SELECT l.id, r.lid FROM l RIGHT JOIN r ON r.lid = l.id", &t);
5394 assert_eq!(rows.len(), 2);
5395 assert!(col(&rows, "id").contains(&Value::Null), "the unmatched right row keeps NULLs on the left");
5396
5397 let t2 = tables(vec![
5398 ("l", vec![json!({"id": 1}), json!({"id": 5})]),
5399 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5400 ]);
5401 let (_, rows) = go("SELECT l.id, r.lid FROM l FULL OUTER JOIN r ON r.lid = l.id", &t2);
5402 assert_eq!(rows.len(), 3, "one match plus one orphan on each side");
5403 }
5404
5405 #[test]
5406 fn a_cross_join_is_the_cartesian_product() {
5407 let t = tables(vec![
5408 ("a", vec![json!({"x": 1}), json!({"x": 2})]),
5409 ("b", vec![json!({"y": 1}), json!({"y": 2}), json!({"y": 3})]),
5410 ]);
5411 assert_eq!(go("SELECT a.x, b.y FROM a CROSS JOIN b", &t).1.len(), 6);
5412 assert_eq!(go("SELECT a.x, b.y FROM a, b", &t).1.len(), 6);
5414 }
5415
5416 #[test]
5417 fn an_ON_clause_that_is_UNKNOWN_does_not_join() {
5418 let t = tables(vec![
5421 ("l", vec![json!({"id": null})]),
5422 ("r", vec![json!({"lid": null})]),
5423 ]);
5424 let (_, rows) = go("SELECT l.id FROM l JOIN r ON r.lid = l.id", &t);
5425 assert!(rows.is_empty(), "NULL = NULL is UNKNOWN, so nothing joins");
5426 let (_, rows) = go("SELECT l.id FROM l LEFT JOIN r ON r.lid = l.id", &t);
5428 assert_eq!(rows.len(), 1);
5429 }
5430
5431 #[test]
5432 fn two_joins_chain() {
5433 let t = tables(vec![
5434 ("a", vec![json!({"id": 1, "bid": 10, "cid": 100})]),
5435 ("b", vec![json!({"id": 10, "bn": "B"})]),
5436 ("c", vec![json!({"id": 100, "cn": "C"})]),
5437 ]);
5438 let (_, rows) = go(
5439 "SELECT a.id, b.bn, c.cn FROM a \
5440 LEFT JOIN b ON b.id = a.bid \
5441 LEFT JOIN c ON c.id = a.cid",
5442 &t,
5443 );
5444 assert_eq!(rows.len(), 1);
5445 assert_eq!(col(&rows, "bn"), vec![json!("B")]);
5446 assert_eq!(col(&rows, "cn"), vec![json!("C")]);
5447 }
5448
5449 fn catalog() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5453 tables(vec![
5454 (
5455 "pg_namespace",
5456 vec![
5457 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5458 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5459 json!({"oid": 13000, "nspname": "information_schema", "nspowner": 10}),
5460 ],
5461 ),
5462 (
5463 "pg_class",
5464 vec![
5465 json!({"oid": 16401, "relname": "orders", "relnamespace": 2200,
5466 "relkind": "r", "relowner": 10, "relam": 2}),
5467 json!({"oid": 16402, "relname": "drivers", "relnamespace": 2200,
5468 "relkind": "r", "relowner": 10, "relam": 2}),
5469 ],
5470 ),
5471 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5472 ])
5473 }
5474
5475 #[test]
5476 fn THE_dn_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5477 let (names, rows) = go(
5478 r#"SELECT n.nspname AS "Name",
5479 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5480 FROM pg_catalog.pg_namespace n
5481 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5482 ORDER BY 1;"#,
5483 &catalog(),
5484 );
5485
5486 assert_eq!(names, vec!["Name", "Owner"], "psql reads these BY NAME");
5487 assert_eq!(col(&rows, "Name"), vec![json!("public")]);
5490 assert_eq!(col(&rows, "Owner"), vec![json!("nedb")]);
5491 }
5492
5493 #[test]
5494 fn THE_dt_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5495 let (names, rows) = go(
5496 r#"SELECT n.nspname as "Schema",
5497 c.relname as "Name",
5498 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5499 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5500 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5501 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5502 WHEN 'I' THEN 'partitioned index' END as "Type",
5503 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5504 FROM pg_catalog.pg_class c
5505 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5506 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5507 WHERE c.relkind IN ('r','p','')
5508 AND n.nspname <> 'pg_catalog'
5509 AND n.nspname !~ '^pg_toast'
5510 AND n.nspname <> 'information_schema'
5511 AND pg_catalog.pg_table_is_visible(c.oid)
5512 ORDER BY 1,2;"#,
5513 &catalog(),
5514 );
5515
5516 assert_eq!(names, vec!["Schema", "Name", "Type", "Owner"]);
5517 assert_eq!(col(&rows, "Name"), vec![json!("drivers"), json!("orders")]);
5519 assert_eq!(col(&rows, "Schema"), vec![json!("public"), json!("public")]);
5520 assert_eq!(col(&rows, "Type"), vec![json!("table"), json!("table")]);
5522 assert_eq!(col(&rows, "Owner"), vec![json!("nedb"), json!("nedb")]);
5523 }
5524
5525 #[test]
5526 fn the_dt_query_still_filters_correctly_with_a_system_relation_present() {
5527 let t = tables(vec![
5531 (
5532 "pg_namespace",
5533 vec![
5534 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5535 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5536 ],
5537 ),
5538 (
5539 "pg_class",
5540 vec![
5541 json!({"oid": 1, "relname": "mine", "relnamespace": 2200,
5542 "relkind": "r", "relowner": 10, "relam": 2}),
5543 json!({"oid": 2, "relname": "pg_internal", "relnamespace": 11,
5544 "relkind": "r", "relowner": 10, "relam": 2}),
5545 json!({"oid": 3, "relname": "an_index", "relnamespace": 2200,
5546 "relkind": "i", "relowner": 10, "relam": 2}),
5547 ],
5548 ),
5549 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5550 ]);
5551 let (_, rows) = go(
5552 r#"SELECT c.relname as "Name" FROM pg_catalog.pg_class c
5553 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5554 WHERE c.relkind IN ('r','p','') AND n.nspname <> 'pg_catalog'
5555 ORDER BY 1"#,
5556 &t,
5557 );
5558 assert_eq!(col(&rows, "Name"), vec![json!("mine")],
5559 "a system relation and an index must both be filtered out");
5560 }
5561}
5562
5563#[cfg(test)]
5564mod operator_syntax_tests {
5565 use super::*;
5566 use serde_json::json;
5567
5568 #[test]
5569 fn the_OPERATOR_qualification_psql_generates_is_understood() {
5570 let s = parse(
5574 "SELECT a FROM t WHERE n OPERATOR(pg_catalog.~) '^x' \
5575 AND m OPERATOR(pg_catalog.=) 1",
5576 )
5577 .expect("psql's OPERATOR() form must parse");
5578 match s.where_.unwrap() {
5579 Expr::Binary { op, left, .. } => {
5580 assert_eq!(op, "AND");
5581 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "~"));
5582 }
5583 other => panic!("{:?}", other),
5584 }
5585 }
5586
5587 #[test]
5588 fn an_OPERATOR_qualified_comparison_EVALUATES() {
5589 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
5590 Ok(Some(from_vec(vec![json!({"n": "orders"}), json!({"n": "pg_toast_1"})])))
5591 };
5592 let (_, rows) = run(
5593 "SELECT n FROM pg_class WHERE n OPERATOR(pg_catalog.~) '^ord'",
5594 &t,
5595 )
5596 .unwrap();
5597 assert_eq!(rows.len(), 1);
5598 assert_eq!(rows[0]["n"], json!("orders"));
5599 }
5600
5601 #[test]
5602 fn a_subquery_an_ARRAY_constructor_and_EXISTS_all_PARSE() {
5603 let s = parse("SELECT a FROM t WHERE x = (SELECT 1)").unwrap();
5606 assert!(matches!(s.where_, Some(Expr::Binary { ref right, .. }) if matches!(**right, Expr::Subquery(_))));
5607 let s = parse("SELECT array_to_string(ARRAY(SELECT a FROM b), ',') FROM t").unwrap();
5608 assert!(matches!(&s.items[0].expr, Expr::Func { args, .. } if matches!(args[0], Expr::ArrayQuery(_))));
5609 let s = parse("SELECT a FROM t WHERE EXISTS (SELECT 1)").unwrap();
5610 assert!(matches!(s.where_, Some(Expr::Exists { negated: false, .. })));
5611 let s = parse("SELECT a FROM t WHERE NOT EXISTS (SELECT 1)").unwrap();
5612 assert!(matches!(s.where_, Some(Expr::Unary { ref expr, .. }) if matches!(**expr, Expr::Exists { .. })));
5613 let s = parse("SELECT a FROM t WHERE oid = ANY (polroles) AND 'd' = any(kinds) AND x <> ALL (SELECT y FROM u)").unwrap();
5615 assert!(s.where_.is_some());
5616 let s = parse("SELECT prattrs[s] FROM t").unwrap();
5617 assert!(matches!(s.items[0].expr, Expr::Index { .. }));
5618 let s = parse("SELECT CAST('tuple' AS pg_catalog.text), CAST(n AS int2[]) FROM t").unwrap();
5619 assert!(matches!(&s.items[0].expr, Expr::Cast { ty, .. } if ty == "text"));
5620 assert!(matches!(&s.items[1].expr, Expr::Cast { ty, .. } if ty == "int2[]"));
5621 let s = parse("SELECT a FROM t WHERE a IS DISTINCT FROM b").unwrap();
5622 assert!(matches!(s.where_, Some(Expr::Binary { ref op, .. }) if op == "IS DISTINCT FROM"));
5623 let s = parse("SELECT 1 FROM c LEFT JOIN n ON n.oid = c.ns, p LEFT JOIN np ON np.oid = p.ns").unwrap();
5625 assert_eq!(s.joins.len(), 3);
5626 assert!(matches!(s.joins[1].kind, JoinKind::Cross));
5627 let s = parse("SELECT 1 FROM c, LATERAL (SELECT 2 AS two) s").unwrap();
5629 assert!(s.joins[0].table.lateral && s.joins[0].table.sub.is_some());
5630 let s = parse("SELECT tt.a FROM (SELECT 1 AS a UNION ALL SELECT 2) AS tt ORDER BY 1").unwrap();
5631 assert_eq!(s.from.as_ref().unwrap().sub.as_ref().unwrap().set_ops.len(), 1);
5632 }
5633
5634 #[test]
5635 fn a_compound_query_keeps_ORDER_BY_for_the_whole() {
5636 let s = parse("SELECT a FROM t UNION SELECT b FROM u UNION ALL SELECT c FROM v ORDER BY 1 LIMIT 5").unwrap();
5637 assert_eq!(s.set_ops.len(), 2);
5638 assert_eq!(s.set_ops[0].op, SetOp::Union);
5639 assert!(!s.set_ops[0].all);
5640 assert!(s.set_ops[1].all);
5641 assert_eq!(s.order_by.len(), 1);
5642 assert_eq!(s.limit, Some(5));
5643 assert!(s.set_ops[1].query.order_by.is_empty(), "the tail belongs to the whole, not the last arm");
5644 }
5645}
5646
5647#[cfg(test)]
5648mod subquery_exec_tests {
5649 use super::*;
5650 use serde_json::json;
5651
5652 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5653 let owned: Vec<(String, Vec<Value>)> =
5654 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
5655 move |name: &str| {
5656 let bare = name.rsplit('.').next().unwrap_or(name);
5657 Ok(owned.iter().find(|(n, _)| n == name || n == bare).map(|(_, r)| from_vec(r.clone())))
5658 }
5659 }
5660
5661 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
5662 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
5663 (cols.into_iter().map(|c| c.name).collect(), rows)
5664 }
5665
5666 fn col(rows: &[Value], name: &str) -> Vec<Value> {
5667 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
5668 }
5669
5670 fn shop() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5671 tables(vec![
5672 ("c", vec![
5673 json!({"id": 1, "name": "ann", "tags": ["a", "b"]}),
5674 json!({"id": 2, "name": "bob", "tags": []}),
5675 json!({"id": 3, "name": "cyd", "tags": null}),
5676 ]),
5677 ("o", vec![
5678 json!({"oid": 10, "cid": 1, "total": 5}),
5679 json!({"oid": 11, "cid": 1, "total": 7}),
5680 json!({"oid": 12, "cid": 2, "total": 9}),
5681 ]),
5682 ])
5683 }
5684
5685 #[test]
5686 fn a_correlated_scalar_subquery_sees_the_outer_row() {
5687 let t = shop();
5688 let (_, rows) = go(
5689 "SELECT c.name, (SELECT sum(o.total) FROM o WHERE o.cid = c.id) AS spent FROM c ORDER BY c.id",
5690 &t,
5691 );
5692 assert_eq!(col(&rows, "spent"), vec![json!(12), json!(9), Value::Null]);
5693 let e = run("SELECT (SELECT o.total FROM o WHERE o.cid = c.id) FROM c", &t).unwrap_err().to_string();
5695 assert!(e.contains("more than one row"), "{}", e);
5696 let e = run("SELECT (SELECT oid, total FROM o) FROM c", &t).unwrap_err().to_string();
5698 assert!(e.contains("exactly one column"), "{}", e);
5699 }
5700
5701 #[test]
5702 fn EXISTS_and_NOT_EXISTS_are_never_unknown() {
5703 let t = shop();
5704 let (_, rows) = go("SELECT c.name FROM c WHERE EXISTS (SELECT 1 FROM o WHERE o.cid = c.id) ORDER BY 1", &t);
5705 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5706 let (_, rows) = go("SELECT c.name FROM c WHERE NOT EXISTS (SELECT 1 FROM o WHERE o.cid = c.id)", &t);
5707 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5708 }
5709
5710 #[test]
5711 fn ARRAY_of_a_subquery_and_array_to_string_compose_like_psql_dp() {
5712 let t = shop();
5713 let (_, rows) = go(
5714 "SELECT c.name, array_to_string(ARRAY(SELECT o.total FROM o WHERE o.cid = c.id ORDER BY o.total), ',') AS totals FROM c ORDER BY c.id",
5715 &t,
5716 );
5717 assert_eq!(col(&rows, "totals"), vec![json!("5,7"), json!("9"), json!("")]);
5719 let (_, rows) = go("SELECT array_length(ARRAY(SELECT oid FROM o), 1) AS n FROM c WHERE c.id = 1", &t);
5720 assert_eq!(col(&rows, "n"), vec![json!(3)]);
5721 }
5722
5723 #[test]
5724 fn ANY_ALL_and_IN_over_arrays_and_subqueries() {
5725 let t = shop();
5726 let (_, rows) = go("SELECT c.name FROM c WHERE 'a' = ANY (c.tags) ORDER BY 1", &t);
5727 assert_eq!(col(&rows, "name"), vec![json!("ann")]);
5728 let (_, rows) = go("SELECT c.name FROM c WHERE 'zz' = ANY (c.tags)", &t);
5730 assert!(rows.is_empty());
5731 let (_, rows) = go("SELECT c.name FROM c WHERE c.id = ANY (SELECT o.cid FROM o) ORDER BY 1", &t);
5732 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5733 let (_, rows) = go("SELECT c.name FROM c WHERE c.id <> ALL (SELECT o.cid FROM o)", &t);
5734 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5735 let (_, rows) = go("SELECT c.name FROM c WHERE c.id IN (SELECT o.cid FROM o WHERE o.total > 6) ORDER BY 1", &t);
5736 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5737 let (_, rows) = go("SELECT c.name FROM c WHERE c.id NOT IN (SELECT o.cid FROM o)", &t);
5738 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5739 let (_, rows) = go("SELECT c.tags[2] AS second FROM c WHERE c.id = 1", &t);
5741 assert_eq!(col(&rows, "second"), vec![json!("b")]);
5742 }
5743
5744 #[test]
5745 fn set_operations_combine_arms_and_sort_the_whole() {
5746 let t = shop();
5747 let (names, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1", &t);
5748 assert_eq!(names, vec!["k"], "column names come from the first arm");
5749 assert_eq!(col(&rows, "k"), vec![json!(1), json!(1), json!(1), json!(2), json!(2), json!(3)]);
5750 let (_, rows) = go("SELECT c.id AS k FROM c UNION SELECT o.cid FROM o ORDER BY 1", &t);
5751 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2), json!(3)]);
5752 let (_, rows) = go("SELECT c.id AS k FROM c INTERSECT SELECT o.cid FROM o ORDER BY 1", &t);
5753 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2)]);
5754 let (_, rows) = go("SELECT c.id AS k FROM c EXCEPT SELECT o.cid FROM o", &t);
5755 assert_eq!(col(&rows, "k"), vec![json!(3)]);
5756 let (_, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1 DESC LIMIT 2", &t);
5757 assert_eq!(col(&rows, "k"), vec![json!(3), json!(2)]);
5758 let e = run("SELECT c.id FROM c UNION SELECT o.oid, o.cid FROM o", &t).unwrap_err().to_string();
5759 assert!(e.contains("same number of columns"), "{}", e);
5760 }
5761
5762 #[test]
5763 fn a_derived_table_is_a_relation_and_LATERAL_sees_its_left() {
5764 let t = shop();
5765 let (_, rows) = go(
5766 "SELECT tt.who FROM (SELECT c.name AS who FROM c WHERE c.id < 3) AS tt ORDER BY 1",
5767 &t,
5768 );
5769 assert_eq!(col(&rows, "who"), vec![json!("ann"), json!("bob")]);
5770 let (_, rows) = go("SELECT tt.x FROM (SELECT c.name FROM c WHERE c.id = 1) AS tt(x)", &t);
5772 assert_eq!(col(&rows, "x"), vec![json!("ann")]);
5773 let (_, rows) = go(
5775 "SELECT c.name AS \"Name\", s.n AS \"Orders\" FROM c, LATERAL (SELECT count(*) AS n FROM o WHERE o.cid = c.id) s ORDER BY \"Orders\" DESC, \"Name\"",
5776 &t,
5777 );
5778 assert_eq!(col(&rows, "Name"), vec![json!("ann"), json!("bob"), json!("cyd")]);
5779 assert_eq!(col(&rows, "Orders"), vec![json!(2), json!(1), json!(0)]);
5780 }
5781
5782 #[test]
5783 fn table_functions_generate_series_and_unnest() {
5784 let t = shop();
5785 let (_, rows) = go("SELECT s.generate_series AS n FROM generate_series(1, 3) s", &t);
5786 assert_eq!(col(&rows, "n"), vec![json!(1), json!(2), json!(3)]);
5787 let (_, rows) = go("SELECT x FROM pg_catalog.unnest(ARRAY['p', 'q']) AS t(x)", &t);
5788 assert_eq!(col(&rows, "x"), vec![json!("p"), json!("q")]);
5789 let (_, rows) = go(
5791 "SELECT c.name, array_to_string(array(select x from pg_catalog.unnest(c.tags) as t(x)), ', ') AS tags FROM c ORDER BY c.id",
5792 &t,
5793 );
5794 assert_eq!(col(&rows, "tags"), vec![json!("a, b"), json!(""), json!("")]);
5795 let e = run("SELECT 1 FROM nosuchfn(1) f", &t).unwrap_err().to_string();
5796 assert!(e.contains("table function nosuchfn()"), "{}", e);
5797 }
5798
5799 #[test]
5800 fn aggregates_without_GROUP_BY_collapse_to_one_row() {
5801 let t = shop();
5802 let (names, rows) = go(
5803 "SELECT count(*), count(c.tags) AS tagged, min(c.name), max(c.name) AS hi, string_agg(c.name, '|') AS all FROM c",
5804 &t,
5805 );
5806 assert_eq!(names, vec!["count", "tagged", "min", "hi", "all"]);
5807 assert_eq!(rows.len(), 1);
5808 assert_eq!(rows[0]["count"], json!(3));
5809 assert_eq!(rows[0]["tagged"], json!(2), "count(x) skips NULL");
5810 assert_eq!(rows[0]["min"], json!("ann"));
5811 assert_eq!(rows[0]["hi"], json!("cyd"));
5812 assert_eq!(rows[0]["all"], json!("ann|bob|cyd"));
5813 let (_, rows) = go("SELECT count(*) AS n, sum(o.total) AS s FROM o WHERE o.total > 100", &t);
5815 assert_eq!(rows[0]["n"], json!(0));
5816 assert_eq!(rows[0]["s"], Value::Null);
5817 let (_, rows) = go("SELECT sum(o.total) / count(*) AS avg_total, avg(o.total) AS a FROM o", &t);
5820 assert_eq!(rows[0]["avg_total"], json!(7));
5821 assert_eq!(rows[0]["a"], json!(7));
5822 let e = run("SELECT c.name, count(*) FROM c", &t).unwrap_err().to_string();
5823 assert!(e.contains("must appear in the GROUP BY clause"), "{}", e);
5824 }
5825
5826 #[test]
5827 fn IS_DISTINCT_FROM_is_null_safe() {
5828 let t = shop();
5829 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS DISTINCT FROM NULL ORDER BY 1", &t);
5830 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5831 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS NOT DISTINCT FROM NULL", &t);
5832 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5833 }
5834
5835 #[test]
5836 fn THE_dT_QUERY_RUNS_over_a_catalogue_fixture() {
5837 let t = tables(vec![
5839 ("pg_namespace", vec![
5840 json!({"oid": 11, "nspname": "pg_catalog"}),
5841 json!({"oid": 2200, "nspname": "public"}),
5842 ]),
5843 ("pg_type", vec![
5844 json!({"oid": 25, "typname": "text", "typnamespace": 11, "typrelid": 0, "typelem": 0, "typarray": 1009}),
5845 json!({"oid": 1009, "typname": "_text", "typnamespace": 11, "typrelid": 0, "typelem": 25, "typarray": 0}),
5846 json!({"oid": 70000, "typname": "mood", "typnamespace": 2200, "typrelid": 0, "typelem": 0, "typarray": 70001}),
5847 json!({"oid": 70001, "typname": "_mood", "typnamespace": 2200, "typrelid": 0, "typelem": 70000, "typarray": 0}),
5848 ]),
5849 ("pg_class", vec![]),
5850 ]);
5851 let (_, rows) = go(
5852 r#"SELECT n.nspname as "Schema",
5853 pg_catalog.format_type(t.oid, NULL) AS "Name",
5854 pg_catalog.obj_description(t.oid, 'pg_type') as "Description"
5855 FROM pg_catalog.pg_type t
5856 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
5857 WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
5858 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
5859 AND n.nspname <> 'pg_catalog'
5860 AND n.nspname <> 'information_schema'
5861 AND pg_catalog.pg_type_is_visible(t.oid)
5862 ORDER BY 1, 2;"#,
5863 &t,
5864 );
5865 assert_eq!(rows.len(), 1, "{:?}", rows);
5868 assert_eq!(col(&rows, "Schema"), vec![json!("public")]);
5869 }
5870}
5871
5872#[cfg(test)]
5873mod collate_tests {
5874 use super::*;
5875 use serde_json::json;
5876
5877 #[test]
5878 fn COLLATE_is_consumed_because_it_cannot_change_the_answer() {
5879 for sql in [
5883 r#"SELECT a FROM t ORDER BY a COLLATE "C""#,
5884 r#"SELECT a COLLATE "C" FROM t"#,
5885 r#"SELECT a FROM t WHERE a COLLATE pg_catalog."C" = 'x'"#,
5886 ] {
5887 parse(sql).unwrap_or_else(|e| panic!("{} -> {}", sql, e));
5888 }
5889 assert!(parse("SELECT a FROM t ORDER BY a COLLATE").is_err());
5891 }
5892
5893 #[test]
5894 fn a_COLLATE_annotated_comparison_still_evaluates() {
5895 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
5896 Ok(Some(from_vec(vec![json!({"n": "b"}), json!({"n": "a"})])))
5897 };
5898 let (_, rows) = run(r#"SELECT n FROM pg_class ORDER BY n COLLATE "C""#, &t).unwrap();
5899 assert_eq!(rows[0]["n"], json!("a"), "the ORDER BY still sorts");
5900 }
5901}