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 pub as_of: Option<u64>,
418 pub valid_as_of: Option<String>,
423 pub search: Option<String>,
428}
429
430impl TableRef {
431 pub fn named(name: impl Into<String>, alias: Option<String>) -> Self {
433 TableRef { name: name.into(), alias, sub: None, args: None, col_aliases: vec![], lateral: false, as_of: None, valid_as_of: None, search: None }
434 }
435
436 pub fn binding(&self) -> String {
439 self.alias.clone().unwrap_or_else(|| {
440 self.name.rsplit('.').next().unwrap_or(&self.name).to_string()
441 })
442 }
443}
444
445#[derive(Debug, Clone, Copy, PartialEq, Eq)]
447pub enum SetOp { Union, Intersect, Except }
448
449#[derive(Debug, Clone, PartialEq)]
451pub struct SetArm {
452 pub op: SetOp,
453 pub all: bool,
454 pub query: Select,
455}
456
457#[derive(Debug, Clone, PartialEq)]
458pub struct Join {
459 pub kind: JoinKind,
460 pub table: TableRef,
461 pub on: Option<Expr>,
462}
463
464#[derive(Debug, Clone, Copy, PartialEq)]
465pub enum Dir { Asc, Desc }
466
467#[derive(Debug, Clone, PartialEq)]
468pub struct OrderBy {
469 pub ordinal: Option<usize>,
473 pub expr: Option<Expr>,
474 pub dir: Dir,
475 pub nulls_first: bool,
477}
478
479#[derive(Debug, Clone, PartialEq)]
480pub struct Select {
481 pub distinct: bool,
482 pub items: Vec<SelectItem>,
483 pub from: Option<TableRef>,
484 pub joins: Vec<Join>,
485 pub where_: Option<Expr>,
486 pub group_by: Vec<Expr>,
489 pub having: Option<Expr>,
492 pub order_by: Vec<OrderBy>,
496 pub limit: Option<usize>,
497 pub offset: Option<usize>,
498 pub set_ops: Vec<SetArm>,
501}
502
503impl Select {
504 pub fn base_relations(&self) -> Vec<String> {
513 let mut out = vec![];
514 self.collect_relations(&mut out);
515 out
516 }
517
518 fn collect_relations(&self, out: &mut Vec<String>) {
519 fn table(t: &TableRef, out: &mut Vec<String>) {
520 if let Some(sub) = &t.sub {
521 sub.collect_relations(out);
522 } else if let Some(args) = &t.args {
523 for a in args {
524 expr(a, out);
525 }
526 } else {
527 out.push(t.name.clone());
528 }
529 }
530 fn expr(e: &Expr, out: &mut Vec<String>) {
531 match e {
532 Expr::Subquery(q) | Expr::ArrayQuery(q) => q.collect_relations(out),
533 Expr::Exists { query, .. } => query.collect_relations(out),
534 Expr::InSubquery { expr: x, query, .. } => {
535 expr(x, out);
536 query.collect_relations(out);
537 }
538 Expr::Quantified { left, right, .. } => {
539 expr(left, out);
540 expr(right, out);
541 }
542 Expr::Index { expr: x, index } => {
543 expr(x, out);
544 expr(index, out);
545 }
546 Expr::ArrayLit(items) | Expr::InList { list: items, .. } => {
547 if let Expr::InList { expr: x, .. } = e {
548 expr(x, out);
549 }
550 for i in items {
551 expr(i, out);
552 }
553 }
554 Expr::Func { args, .. } => {
555 for a in args {
556 expr(a, out);
557 }
558 }
559 Expr::Agg { args, order_by, .. } => {
560 for a in args {
561 expr(a, out);
562 }
563 for ob in order_by {
564 if let Some(e) = &ob.expr {
565 expr(e, out);
566 }
567 }
568 }
569 Expr::Case { operand, whens, else_ } => {
570 if let Some(o) = operand {
571 expr(o, out);
572 }
573 for (w, t) in whens {
574 expr(w, out);
575 expr(t, out);
576 }
577 if let Some(x) = else_ {
578 expr(x, out);
579 }
580 }
581 Expr::Binary { left, right, .. } => {
582 expr(left, out);
583 expr(right, out);
584 }
585 Expr::Unary { expr: x, .. } | Expr::Cast { expr: x, .. } | Expr::IsNull { expr: x, .. } => {
586 expr(x, out)
587 }
588 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => {}
589 }
590 }
591 if let Some(f) = &self.from {
592 table(f, out);
593 }
594 for j in &self.joins {
595 table(&j.table, out);
596 if let Some(on) = &j.on {
597 expr(on, out);
598 }
599 }
600 for item in &self.items {
601 expr(&item.expr, out);
602 }
603 if let Some(w) = &self.where_ {
604 expr(w, out);
605 }
606 for g in &self.group_by {
607 expr(g, out);
608 }
609 if let Some(h) = &self.having {
610 expr(h, out);
611 }
612 for ob in &self.order_by {
613 if let Some(e) = &ob.expr {
614 expr(e, out);
615 }
616 }
617 for arm in &self.set_ops {
618 arm.query.collect_relations(out);
619 }
620 }
621}
622
623fn binding_power(op: &str) -> Option<u8> {
634 Some(match op {
635 "OR" => 1,
636 "AND" => 2,
637 "=" | "!=" | "<>" | "<" | "<=" | ">" | ">=" | "~" | "~*" | "!~" | "!~*"
641 | "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => 4,
642 "||" => 5,
643 "+" | "-" => 6,
644 "*" | "/" | "%" => 7,
645 _ => return None,
646 })
647}
648
649struct Parser {
650 toks: Vec<Tok>,
651 pos: usize,
652}
653
654impl Parser {
655 fn peek(&self) -> &Tok {
656 self.toks.get(self.pos).unwrap_or(&Tok::Eof)
657 }
658 fn peek_at(&self, n: usize) -> &Tok {
659 self.toks.get(self.pos + n).unwrap_or(&Tok::Eof)
660 }
661 fn next(&mut self) -> Tok {
662 let t = self.peek().clone();
663 self.pos += 1;
664 t
665 }
666 fn eat_kw(&mut self, kw: &str) -> bool {
667 if self.peek().is_kw(kw) {
668 self.pos += 1;
669 true
670 } else {
671 false
672 }
673 }
674 fn expect_kw(&mut self, kw: &str) -> Result<()> {
675 if self.eat_kw(kw) {
676 Ok(())
677 } else {
678 bail!("expected {} , got {:?}", kw, self.peek())
679 }
680 }
681 fn eat_punct(&mut self, c: char) -> bool {
682 if matches!(self.peek(), Tok::Punct(p) if *p == c) {
683 self.pos += 1;
684 true
685 } else {
686 false
687 }
688 }
689 fn expect_punct(&mut self, c: char) -> Result<()> {
690 if self.eat_punct(c) {
691 Ok(())
692 } else {
693 bail!("expected {:?}, got {:?}", c, self.peek())
694 }
695 }
696 fn eat_op(&mut self, op: &str) -> bool {
697 if matches!(self.peek(), Tok::Op(o) if o == op) {
698 self.pos += 1;
699 true
700 } else {
701 false
702 }
703 }
704
705 fn parse_expr(&mut self) -> Result<Expr> {
708 self.parse_bin(0)
709 }
710
711 fn parse_bin(&mut self, min_bp: u8) -> Result<Expr> {
714 let mut left = self.parse_unary()?;
715
716 loop {
717 if self.peek().is_kw("OPERATOR") && matches!(self.peek_at(1), Tok::Punct('(')) {
724 let save = self.pos;
725 self.pos += 2;
726 let mut sym = None;
728 while sym.is_none() {
729 match self.next() {
730 Tok::Op(o) => sym = Some(o),
731 Tok::Word { .. } | Tok::Punct('.') => continue,
732 _ => break,
733 }
734 }
735 match sym {
736 Some(o) if binding_power(&o).is_some() && self.eat_punct(')') => {
737 let bp = binding_power(&o).unwrap();
738 if bp < min_bp {
739 self.pos = save;
740 break;
741 }
742 let right = self.parse_bin(bp + 1)?;
743 left = Expr::Binary {
744 op: o,
745 left: Box::new(left),
746 right: Box::new(right),
747 };
748 continue;
749 }
750 _ => {
753 self.pos = save;
754 break;
755 }
756 }
757 }
758
759 let (op, width) = match self.peek() {
760 Tok::Op(o) if binding_power(o).is_some() => (o.clone(), 1usize),
761 Tok::Word { upper, .. } if upper == "AND" || upper == "OR" => (upper.clone(), 1),
762 Tok::Word { upper, .. } if upper == "LIKE" || upper == "ILIKE" => (upper.clone(), 1),
763 Tok::Word { upper, .. } if upper == "NOT" => {
764 match self.peek_at(1) {
766 Tok::Word { upper: u2, .. } if u2 == "LIKE" || u2 == "ILIKE" => {
767 (format!("NOT {}", u2), 2)
768 }
769 _ => break,
770 }
771 }
772 _ => break,
773 };
774
775 let bp = match binding_power(&op) {
776 Some(bp) if bp >= min_bp => bp,
777 _ => break,
778 };
779 self.pos += width;
780
781 let quant = match self.peek() {
787 Tok::Word { upper, .. }
788 if matches!(upper.as_str(), "ANY" | "SOME" | "ALL")
789 && matches!(self.peek_at(1), Tok::Punct('(')) =>
790 {
791 Some(upper == "ALL")
792 }
793 _ => None,
794 };
795 if let Some(all) = quant {
796 self.pos += 2; let right = if self.peek().is_kw("SELECT") {
798 Expr::ArrayQuery(Box::new(self.parse_query()?))
799 } else {
800 self.parse_expr()?
801 };
802 self.expect_punct(')')?;
803 left = Expr::Quantified { op, left: Box::new(left), all, right: Box::new(right) };
804 continue;
805 }
806
807 let right = self.parse_bin(bp + 1)?;
809 left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
810 }
811
812 Ok(left)
813 }
814
815 fn parse_postfix(&mut self, mut e: Expr) -> Result<Expr> {
816 loop {
817 if matches!(self.peek(), Tok::Punct('[')) {
820 self.pos += 1;
821 let index = self.parse_expr()?;
822 self.expect_punct(']')?;
823 e = Expr::Index { expr: Box::new(e), index: Box::new(index) };
824 continue;
825 }
826
827 if self.peek().is_kw("IS") {
829 self.pos += 1;
830 let negated = self.eat_kw("NOT");
831 if self.eat_kw("DISTINCT") {
835 self.expect_kw("FROM")?;
836 let rhs = self.parse_bin(5)?;
838 e = Expr::Binary {
839 op: if negated { "IS NOT DISTINCT FROM".into() } else { "IS DISTINCT FROM".into() },
840 left: Box::new(e),
841 right: Box::new(rhs),
842 };
843 continue;
844 }
845 if !self.eat_kw("NULL") {
846 if self.eat_kw("TRUE") {
848 e = Expr::Binary {
849 op: "=".into(),
850 left: Box::new(e),
851 right: Box::new(Expr::Literal(Value::Bool(!negated))),
852 };
853 continue;
854 }
855 if self.eat_kw("FALSE") {
856 e = Expr::Binary {
857 op: "=".into(),
858 left: Box::new(e),
859 right: Box::new(Expr::Literal(Value::Bool(negated))),
860 };
861 continue;
862 }
863 bail!("expected NULL, TRUE or FALSE after IS, got {:?}", self.peek());
864 }
865 e = Expr::IsNull { expr: Box::new(e), negated };
866 continue;
867 }
868
869 let negated_in = if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("IN") {
871 self.pos += 2;
872 true
873 } else if self.peek().is_kw("IN") {
874 self.pos += 1;
875 false
876 } else {
877 let negated_between =
879 if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("BETWEEN") {
880 self.pos += 2;
881 true
882 } else if self.peek().is_kw("BETWEEN") {
883 self.pos += 1;
884 false
885 } else {
886 break;
887 };
888 let low = self.parse_bin(3)?;
892 self.expect_kw("AND")?;
893 let high = self.parse_bin(3)?;
894 let ge = Expr::Binary {
895 op: ">=".into(),
896 left: Box::new(e.clone()),
897 right: Box::new(low),
898 };
899 let le = Expr::Binary {
900 op: "<=".into(),
901 left: Box::new(e),
902 right: Box::new(high),
903 };
904 let both = Expr::Binary {
905 op: "AND".into(),
906 left: Box::new(ge),
907 right: Box::new(le),
908 };
909 e = if negated_between {
910 Expr::Unary { op: "NOT".into(), expr: Box::new(both) }
911 } else {
912 both
913 };
914 continue;
915 };
916
917 self.expect_punct('(')?;
918 if self.peek().is_kw("SELECT") {
920 let query = Box::new(self.parse_query()?);
921 self.expect_punct(')')?;
922 e = Expr::InSubquery { expr: Box::new(e), query, negated: negated_in };
923 continue;
924 }
925 let mut list = vec![];
926 if !self.eat_punct(')') {
927 loop {
928 list.push(self.parse_expr()?);
929 if self.eat_punct(',') {
930 continue;
931 }
932 self.expect_punct(')')?;
933 break;
934 }
935 }
936 e = Expr::InList { expr: Box::new(e), list, negated: negated_in };
937 }
938 Ok(e)
939 }
940
941 fn parse_unary(&mut self) -> Result<Expr> {
942 if self.peek().is_kw("NOT") {
943 self.pos += 1;
944 let e = self.parse_bin(3)?;
947 return Ok(Expr::Unary { op: "NOT".into(), expr: Box::new(e) });
948 }
949 if self.eat_op("-") {
950 let e = self.parse_unary()?;
951 return Ok(Expr::Unary { op: "-".into(), expr: Box::new(e) });
952 }
953 if self.eat_op("+") {
954 return self.parse_unary();
955 }
956 let atom = self.parse_atom()?;
957 let cast = self.parse_casts(atom)?;
958 self.parse_postfix(cast)
969 }
970
971 fn parse_casts(&mut self, mut e: Expr) -> Result<Expr> {
973 loop {
974 if self.peek().is_kw("COLLATE") {
979 self.pos += 1;
980 match self.next() {
981 Tok::Word { .. } | Tok::Quoted(_) => {}
982 other => bail!("expected a collation name after COLLATE, got {:?}", other),
983 }
984 while self.eat_punct('.') {
986 match self.next() {
987 Tok::Word { .. } | Tok::Quoted(_) => {}
988 other => bail!("expected a name after '.', got {:?}", other),
989 }
990 }
991 continue;
992 }
993 if !self.eat_op("::") {
994 break;
995 }
996 let mut ty = match self.next() {
997 Tok::Word { raw, .. } => raw,
998 Tok::Quoted(s) => s,
999 other => bail!("expected a type name after ::, got {:?}", other),
1000 };
1001 while self.eat_punct('.') {
1003 match self.next() {
1004 Tok::Word { raw, .. } => ty = raw,
1005 Tok::Quoted(s) => ty = s,
1006 other => bail!("expected a type name after ., got {:?}", other),
1007 }
1008 }
1009 while self.eat_punct('[') {
1011 self.expect_punct(']')?;
1012 ty.push_str("[]");
1013 }
1014 e = Expr::Cast { expr: Box::new(e), ty };
1015 }
1016 Ok(e)
1017 }
1018
1019
1020 fn parse_atom(&mut self) -> Result<Expr> {
1021 if self.eat_punct('(') {
1028 if self.peek().is_kw("SELECT") {
1031 let q = self.parse_query()?;
1032 self.expect_punct(')')?;
1033 return Ok(Expr::Subquery(Box::new(q)));
1034 }
1035 let e = self.parse_expr()?;
1036 self.expect_punct(')')?;
1037 return Ok(e);
1038 }
1039
1040 if self.peek().is_kw("ARRAY") && matches!(self.peek_at(1), Tok::Punct('(') | Tok::Punct('[')) {
1044 self.pos += 1;
1045 if self.eat_punct('(') {
1046 if !self.peek().is_kw("SELECT") {
1047 bail!("ARRAY(...) takes a subquery; for a list of values write ARRAY[...]");
1048 }
1049 let q = self.parse_query()?;
1050 self.expect_punct(')')?;
1051 return Ok(Expr::ArrayQuery(Box::new(q)));
1052 }
1053 self.expect_punct('[')?;
1054 let mut items = vec![];
1055 if !self.eat_punct(']') {
1056 loop {
1057 items.push(self.parse_expr()?);
1058 if self.eat_punct(',') {
1059 continue;
1060 }
1061 self.expect_punct(']')?;
1062 break;
1063 }
1064 }
1065 return Ok(Expr::ArrayLit(items));
1066 }
1067
1068 if self.peek().is_kw("EXISTS") && matches!(self.peek_at(1), Tok::Punct('(')) {
1072 self.pos += 2;
1073 if !self.peek().is_kw("SELECT") {
1074 bail!("EXISTS (...) takes a subquery");
1075 }
1076 let q = self.parse_query()?;
1077 self.expect_punct(')')?;
1078 return Ok(Expr::Exists { query: Box::new(q), negated: false });
1079 }
1080
1081 if self.peek().is_kw("CAST") && matches!(self.peek_at(1), Tok::Punct('(')) {
1084 self.pos += 2;
1085 let inner = self.parse_expr()?;
1086 self.expect_kw("AS")?;
1087 let mut ty = match self.next() {
1088 Tok::Word { raw, .. } => raw,
1089 Tok::Quoted(s) => s,
1090 other => bail!("expected a type name in CAST, got {:?}", other),
1091 };
1092 while self.eat_punct('.') {
1093 match self.next() {
1094 Tok::Word { raw, .. } => ty = raw,
1095 Tok::Quoted(s) => ty = s,
1096 other => bail!("expected a type name after ., got {:?}", other),
1097 }
1098 }
1099 while self.eat_punct('[') {
1100 self.expect_punct(']')?;
1101 ty.push_str("[]");
1102 }
1103 self.expect_punct(')')?;
1104 return Ok(Expr::Cast { expr: Box::new(inner), ty });
1105 }
1106
1107 if self.peek().is_kw("CASE") {
1109 return self.parse_case();
1110 }
1111
1112 match self.next() {
1113 Tok::Num(n) => Ok(Expr::Literal(from_f64(n))),
1114 Tok::Str(s) => Ok(Expr::Literal(Value::String(s))),
1115 Tok::Op(o) if o == "*" => Ok(Expr::Star),
1116 Tok::Quoted(name) => self.parse_name_tail(None, name),
1117 Tok::Word { upper, raw } => match upper.as_str() {
1118 "NULL" => Ok(Expr::Literal(Value::Null)),
1119 "TRUE" => Ok(Expr::Literal(Value::Bool(true))),
1120 "FALSE" => Ok(Expr::Literal(Value::Bool(false))),
1121 "CURRENT_SCHEMA" | "CURRENT_DATABASE" | "CURRENT_USER" | "SESSION_USER"
1125 | "CURRENT_CATALOG" | "USER" | "VERSION"
1126 if !matches!(self.peek(), Tok::Punct('(')) =>
1127 {
1128 Ok(Expr::Func { name: upper.to_lowercase(), args: vec![] })
1129 }
1130 _ => self.parse_name_tail(None, raw),
1131 },
1132 other => bail!("unexpected {:?} in an expression", other),
1133 }
1134 }
1135
1136 fn parse_name_tail(&mut self, _schema: Option<String>, first: String) -> Result<Expr> {
1143 let mut parts = vec![first];
1144 while self.eat_punct('.') {
1145 if self.eat_op("*") {
1147 return Ok(Expr::QualifiedStar(parts.pop().unwrap_or_default()));
1148 }
1149 match self.next() {
1150 Tok::Word { raw, .. } => parts.push(raw),
1151 Tok::Quoted(s) => parts.push(s),
1152 other => bail!("expected a name after '.', got {:?}", other),
1153 }
1154 }
1155
1156 if matches!(self.peek(), Tok::Punct('(')) {
1160 self.pos += 1;
1161 let name = parts.pop().unwrap_or_default().to_lowercase();
1162 let agg = is_aggregate(&name);
1163 let distinct = agg && self.eat_kw("DISTINCT");
1165 let mut args = vec![];
1166 let mut order_by = vec![];
1167 if !self.eat_punct(')') {
1168 loop {
1169 if self.eat_op("*") {
1171 args.push(Expr::Star);
1172 } else {
1173 args.push(self.parse_expr()?);
1174 }
1175 if self.eat_punct(',') {
1176 continue;
1177 }
1178 if agg && self.peek().is_kw("ORDER") {
1182 self.pos += 1;
1183 self.expect_kw("BY")?;
1184 order_by = self.parse_sort_list()?;
1185 }
1186 self.expect_punct(')')?;
1187 break;
1188 }
1189 }
1190 if agg {
1191 return Ok(Expr::Agg { name, args, order_by, distinct });
1192 }
1193 return Ok(Expr::Func { name, args });
1194 }
1195
1196 let name = parts.pop().unwrap_or_default();
1197 let qual = parts.pop();
1201 Ok(Expr::Column { qual, name })
1202 }
1203
1204 fn parse_case(&mut self) -> Result<Expr> {
1205 self.expect_kw("CASE")?;
1206 let operand = if self.peek().is_kw("WHEN") {
1208 None
1209 } else {
1210 Some(Box::new(self.parse_expr()?))
1211 };
1212 let mut whens = vec![];
1213 while self.eat_kw("WHEN") {
1214 let cond = self.parse_expr()?;
1215 self.expect_kw("THEN")?;
1216 let then = self.parse_expr()?;
1217 whens.push((cond, then));
1218 }
1219 if whens.is_empty() {
1220 bail!("CASE needs at least one WHEN branch");
1221 }
1222 let else_ = if self.eat_kw("ELSE") {
1223 Some(Box::new(self.parse_expr()?))
1224 } else {
1225 None
1226 };
1227 self.expect_kw("END")?;
1228 Ok(Expr::Case { operand, whens, else_ })
1229 }
1230
1231 fn parse_table_ref(&mut self) -> Result<TableRef> {
1234 let lateral = self.eat_kw("LATERAL");
1235 if self.eat_punct('(') {
1237 if !self.peek().is_kw("SELECT") {
1238 bail!("expected a subquery after '(' in FROM, got {:?}", self.peek());
1239 }
1240 let sub = self.parse_query()?;
1241 self.expect_punct(')')?;
1242 let (alias, col_aliases) = self.parse_table_alias()?;
1243 if alias.is_none() {
1244 bail!("a subquery in FROM must have an alias");
1245 }
1246 return Ok(TableRef {
1247 name: "(subquery)".into(),
1248 alias,
1249 sub: Some(Box::new(sub)),
1250 args: None,
1251 col_aliases,
1252 lateral,
1253 as_of: None,
1254 valid_as_of: None,
1255 search: None,
1256 });
1257 }
1258 if lateral {
1259 bail!("LATERAL applies to a subquery in FROM; write LATERAL (SELECT ...)");
1260 }
1261
1262 let mut parts = vec![match self.next() {
1263 Tok::Word { raw, .. } => raw,
1264 Tok::Quoted(s) => s,
1265 other => bail!("expected a table name, got {:?}", other),
1266 }];
1267 while self.eat_punct('.') {
1268 match self.next() {
1269 Tok::Word { raw, .. } => parts.push(raw),
1270 Tok::Quoted(s) => parts.push(s),
1271 other => bail!("expected a name after '.', got {:?}", other),
1272 }
1273 }
1274 let name = parts.join(".");
1275
1276 if self.eat_punct('(') {
1279 let mut args = vec![];
1280 if !self.eat_punct(')') {
1281 loop {
1282 args.push(self.parse_expr()?);
1283 if self.eat_punct(',') {
1284 continue;
1285 }
1286 self.expect_punct(')')?;
1287 break;
1288 }
1289 }
1290 let fname = name.rsplit('.').next().unwrap_or(&name).to_lowercase();
1291 let (alias, col_aliases) = self.parse_table_alias()?;
1292 return Ok(TableRef { name: fname, alias, sub: None, args: Some(args), col_aliases, lateral: false, as_of: None, valid_as_of: None, search: None });
1293 }
1294
1295 let as_of = if self.peek().is_kw("AS") && self.peek_at(1).is_kw("OF") {
1300 self.next();
1301 self.next();
1302 self.expect_kw("SYSTEM")?;
1303 self.expect_kw("TIME")?;
1304 match self.next() {
1305 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Some(n as u64),
1306 other => bail!(
1307 "AS OF SYSTEM TIME takes a NEDB sequence number here, not a timestamp \
1308 (got {:?}). NEDB's history is sequence-addressed and never \
1309 garbage-collected, so a seq is exact where a wall-clock time would be \
1310 approximate",
1311 other
1312 ),
1313 }
1314 } else {
1315 None
1316 };
1317 let valid_as_of = if self.peek().is_kw("VALID")
1333 && self.peek_at(1).is_kw("AS")
1334 && self.peek_at(2).is_kw("OF")
1335 {
1336 self.next();
1337 self.next();
1338 self.next();
1339 match self.next() {
1340 Tok::Str(s) => Some(s),
1341 other => bail!(
1342 "VALID AS OF takes a date string here (got {:?}). System time is a \
1343 sequence and application-time validity is a date — they are different \
1344 questions, so they take different arguments",
1345 other
1346 ),
1347 }
1348 } else {
1349 None
1350 };
1351
1352 let search = if self.peek().is_kw("SEARCH") && matches!(self.peek_at(1), Tok::Str(_)) {
1353 self.next();
1354 match self.next() {
1355 Tok::Str(s) => Some(s),
1356 other => bail!("SEARCH takes a string here, got {:?}", other),
1359 }
1360 } else {
1361 None
1362 };
1363
1364 let (alias, col_aliases) = self.parse_table_alias()?;
1365 Ok(TableRef { name, alias, sub: None, args: None, col_aliases, lateral: false, as_of, valid_as_of, search })
1366 }
1367
1368 fn parse_table_alias(&mut self) -> Result<(Option<String>, Vec<String>)> {
1373 let alias = if self.eat_kw("AS") {
1374 match self.next() {
1375 Tok::Word { raw, .. } => Some(raw),
1376 Tok::Quoted(s) => Some(s),
1377 other => bail!("expected an alias after AS, got {:?}", other),
1378 }
1379 } else {
1380 match self.peek().clone() {
1381 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1382 self.pos += 1;
1383 Some(raw)
1384 }
1385 Tok::Quoted(s) => {
1386 self.pos += 1;
1387 Some(s)
1388 }
1389 _ => None,
1390 }
1391 };
1392 let mut col_aliases = vec![];
1393 if alias.is_some() && self.eat_punct('(') {
1394 loop {
1395 match self.next() {
1396 Tok::Word { raw, .. } => col_aliases.push(raw),
1397 Tok::Quoted(s) => col_aliases.push(s),
1398 other => bail!("expected a column alias, got {:?}", other),
1399 }
1400 if self.eat_punct(',') {
1401 continue;
1402 }
1403 self.expect_punct(')')?;
1404 break;
1405 }
1406 }
1407 Ok((alias, col_aliases))
1408 }
1409
1410 fn parse_query(&mut self) -> Result<Select> {
1417 let mut first = self.parse_select_body()?;
1418 loop {
1419 let op = if self.eat_kw("UNION") {
1420 SetOp::Union
1421 } else if self.eat_kw("INTERSECT") {
1422 SetOp::Intersect
1423 } else if self.eat_kw("EXCEPT") {
1424 SetOp::Except
1425 } else {
1426 break;
1427 };
1428 let all = self.eat_kw("ALL");
1429 if !all {
1430 let _ = self.eat_kw("DISTINCT");
1431 }
1432 let query = if self.eat_punct('(') {
1434 let q = self.parse_query()?;
1435 self.expect_punct(')')?;
1436 q
1437 } else {
1438 self.parse_select_body()?
1439 };
1440 first.set_ops.push(SetArm { op, all, query });
1441 }
1442 self.parse_query_tail(&mut first)?;
1443 Ok(first)
1444 }
1445
1446 fn parse_sort_list(&mut self) -> Result<Vec<OrderBy>> {
1450 let mut out = vec![];
1451 loop {
1452 let (ordinal, expr) = match self.peek().clone() {
1456 Tok::Num(n)
1457 if n.fract() == 0.0
1458 && n >= 1.0
1459 && !matches!(self.peek_at(1), Tok::Op(_)) =>
1460 {
1461 self.pos += 1;
1462 (Some(n as usize), None)
1463 }
1464 _ => (None, Some(self.parse_expr()?)),
1465 };
1466 let dir = if self.eat_kw("DESC") {
1467 Dir::Desc
1468 } else {
1469 let _ = self.eat_kw("ASC");
1470 Dir::Asc
1471 };
1472 let mut nulls_first = matches!(dir, Dir::Desc);
1474 if self.eat_kw("NULLS") {
1475 if self.eat_kw("FIRST") {
1476 nulls_first = true;
1477 } else if self.eat_kw("LAST") {
1478 nulls_first = false;
1479 } else {
1480 bail!("expected FIRST or LAST after NULLS, got {:?}", self.peek());
1481 }
1482 }
1483 out.push(OrderBy { ordinal, expr, dir, nulls_first });
1484 if self.eat_punct(',') {
1485 continue;
1486 }
1487 break;
1488 }
1489 Ok(out)
1490 }
1491
1492 fn parse_query_tail(&mut self, sel: &mut Select) -> Result<()> {
1493 let mut order_by = vec![];
1494 if self.eat_kw("ORDER") {
1495 self.expect_kw("BY")?;
1496 order_by = self.parse_sort_list()?;
1497 }
1498
1499 let mut limit = None;
1500 let mut offset = None;
1501 loop {
1503 if self.eat_kw("LIMIT") {
1504 if self.eat_kw("ALL") {
1505 limit = None;
1506 } else {
1507 limit = Some(self.parse_count("LIMIT")?);
1508 }
1509 continue;
1510 }
1511 if self.eat_kw("OFFSET") {
1512 offset = Some(self.parse_count("OFFSET")?);
1513 let _ = self.eat_kw("ROW") || self.eat_kw("ROWS");
1514 continue;
1515 }
1516 break;
1517 }
1518 sel.order_by = order_by;
1519 sel.limit = limit;
1520 sel.offset = offset;
1521 Ok(())
1522 }
1523
1524 fn parse_select_body(&mut self) -> Result<Select> {
1526 self.expect_kw("SELECT")?;
1527 let distinct = self.eat_kw("DISTINCT");
1528 if distinct && self.peek().is_kw("ON") {
1529 bail!("DISTINCT ON is not supported");
1530 }
1531 let _ = self.eat_kw("ALL");
1532
1533 let mut items = vec![];
1534 loop {
1535 let expr = self.parse_expr()?;
1536 let alias = if self.eat_kw("AS") {
1538 match self.next() {
1539 Tok::Word { raw, .. } => Some(raw),
1540 Tok::Quoted(s) => Some(s),
1541 other => bail!("expected an alias after AS, got {:?}", other),
1542 }
1543 } else {
1544 match self.peek().clone() {
1545 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1546 self.pos += 1;
1547 Some(raw)
1548 }
1549 Tok::Quoted(s) => {
1550 self.pos += 1;
1551 Some(s)
1552 }
1553 _ => None,
1554 }
1555 };
1556 items.push(SelectItem { expr, alias });
1557 if self.eat_punct(',') {
1558 continue;
1559 }
1560 break;
1561 }
1562
1563 let mut from = None;
1564 let mut joins = vec![];
1565 if self.eat_kw("FROM") {
1566 from = Some(self.parse_table_ref()?);
1567 loop {
1568 if self.eat_punct(',') {
1574 let table = self.parse_table_ref()?;
1575 joins.push(Join { kind: JoinKind::Cross, table, on: None });
1576 continue;
1577 }
1578 let kind = if self.peek().is_kw("JOIN") {
1579 self.pos += 1;
1580 JoinKind::Inner
1581 } else if self.peek().is_kw("INNER") && self.peek_at(1).is_kw("JOIN") {
1582 self.pos += 2;
1583 JoinKind::Inner
1584 } else if self.peek().is_kw("CROSS") && self.peek_at(1).is_kw("JOIN") {
1585 self.pos += 2;
1586 JoinKind::Cross
1587 } else if self.peek().is_kw("LEFT") {
1588 self.pos += 1;
1589 let _ = self.eat_kw("OUTER");
1590 self.expect_kw("JOIN")?;
1591 JoinKind::Left
1592 } else if self.peek().is_kw("RIGHT") {
1593 self.pos += 1;
1594 let _ = self.eat_kw("OUTER");
1595 self.expect_kw("JOIN")?;
1596 JoinKind::Right
1597 } else if self.peek().is_kw("FULL") {
1598 self.pos += 1;
1599 let _ = self.eat_kw("OUTER");
1600 self.expect_kw("JOIN")?;
1601 JoinKind::Full
1602 } else {
1603 break;
1604 };
1605 let table = self.parse_table_ref()?;
1606 let on = if self.eat_kw("ON") {
1607 Some(self.parse_expr()?)
1608 } else if self.peek().is_kw("USING") {
1609 bail!("JOIN ... USING is not supported — write ON a.col = b.col");
1610 } else {
1611 None
1612 };
1613 if on.is_none() && !matches!(kind, JoinKind::Cross) {
1614 bail!("a {:?} JOIN needs an ON clause", kind);
1615 }
1616 joins.push(Join { kind, table, on });
1617 }
1618 }
1619
1620 let where_ = if self.eat_kw("WHERE") {
1621 Some(self.parse_expr()?)
1622 } else {
1623 None
1624 };
1625
1626 let mut group_by = vec![];
1627 if self.eat_kw("GROUP") {
1628 self.expect_kw("BY")?;
1629 if self.eat_kw("ALL") || self.eat_kw("DISTINCT") {
1630 bail!("GROUP BY ALL / DISTINCT is not supported — list the keys");
1631 }
1632 loop {
1633 if self.peek().is_kw("ROLLUP")
1634 || self.peek().is_kw("CUBE")
1635 || self.peek().is_kw("GROUPING")
1636 {
1637 bail!("GROUP BY ROLLUP / CUBE / GROUPING SETS is not supported");
1638 }
1639 group_by.push(self.parse_expr()?);
1640 if self.eat_punct(',') {
1641 continue;
1642 }
1643 break;
1644 }
1645 }
1646
1647 let having = if self.eat_kw("HAVING") {
1648 Some(self.parse_expr()?)
1649 } else {
1650 None
1651 };
1652 if having.is_some() && group_by.is_empty() && !items.iter().any(|i| has_aggregate(&i.expr))
1653 {
1654 bail!("HAVING needs a GROUP BY or an aggregate — it filters groups, not rows; \
1655 use WHERE to filter rows");
1656 }
1657
1658 Ok(Select {
1659 distinct,
1660 items,
1661 from,
1662 joins,
1663 where_,
1664 group_by,
1665 having,
1666 order_by: vec![],
1667 limit: None,
1668 offset: None,
1669 set_ops: vec![],
1670 })
1671 }
1672
1673 fn parse_count(&mut self, what: &str) -> Result<usize> {
1674 match self.next() {
1675 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Ok(n as usize),
1676 other => bail!("{} expects a non-negative integer, got {:?}", what, other),
1677 }
1678 }
1679}
1680
1681fn is_clause_keyword(upper: &str) -> bool {
1686 matches!(
1687 upper,
1688 "FROM" | "WHERE" | "GROUP" | "HAVING" | "ORDER" | "LIMIT" | "OFFSET"
1689 | "JOIN" | "LEFT" | "RIGHT" | "FULL" | "INNER" | "CROSS" | "OUTER"
1690 | "ON" | "USING" | "AND" | "OR" | "AS" | "UNION" | "INTERSECT"
1691 | "EXCEPT" | "FETCH" | "FOR" | "WINDOW" | "RETURNING" | "INTO"
1692 | "ASC" | "DESC" | "NULLS" | "IS" | "IN" | "NOT" | "LIKE" | "ILIKE"
1693 | "BETWEEN" | "THEN" | "WHEN" | "ELSE" | "END" | "CASE" | "DISTINCT"
1694 | "SELECT" | "WITH" | "ALL"
1695 )
1696}
1697
1698pub fn parse(sql: &str) -> Result<Select> {
1700 let toks = lex(sql)?;
1701 let mut p = Parser { toks, pos: 0 };
1702 let sel = if matches!(p.peek(), Tok::Punct('(')) && p.peek_at(1).is_kw("SELECT") {
1704 p.pos += 1;
1705 let mut first = p.parse_query()?;
1706 p.expect_punct(')')?;
1707 loop {
1709 let op = if p.eat_kw("UNION") {
1710 SetOp::Union
1711 } else if p.eat_kw("INTERSECT") {
1712 SetOp::Intersect
1713 } else if p.eat_kw("EXCEPT") {
1714 SetOp::Except
1715 } else {
1716 break;
1717 };
1718 let all = p.eat_kw("ALL");
1719 if !all {
1720 let _ = p.eat_kw("DISTINCT");
1721 }
1722 let query = if p.eat_punct('(') {
1723 let q = p.parse_query()?;
1724 p.expect_punct(')')?;
1725 q
1726 } else {
1727 p.parse_select_body()?
1728 };
1729 first.set_ops.push(SetArm { op, all, query });
1730 }
1731 p.parse_query_tail(&mut first)?;
1732 first
1733 } else {
1734 p.parse_query()?
1735 };
1736 let _ = p.eat_punct(';');
1737 if !matches!(p.peek(), Tok::Eof) {
1738 bail!("unexpected trailing tokens: {:?}", p.peek());
1739 }
1740 Ok(sel)
1741}
1742
1743pub struct Bound<'a> {
1756 pub parts: Vec<(String, Option<&'a Value>)>,
1757 pub ctx: EvalCtx<'a>,
1759}
1760
1761#[derive(Clone, Copy, Default)]
1774pub struct EvalCtx<'a> {
1775 pub resolver: Option<&'a Resolver<'a>>,
1776 pub outer: Option<&'a Bound<'a>>,
1777}
1778
1779impl<'a> Bound<'a> {
1780 pub fn new(parts: Vec<(String, Option<&'a Value>)>) -> Self {
1782 Bound { parts, ctx: EvalCtx::default() }
1783 }
1784
1785 fn column(&self, qual: Option<&str>, name: &str) -> Value {
1792 match qual {
1793 Some(q) => {
1794 for (binding, row) in &self.parts {
1795 if binding.eq_ignore_ascii_case(q) {
1796 return row
1797 .and_then(|r| r.get(name))
1798 .cloned()
1799 .unwrap_or(Value::Null);
1800 }
1801 }
1802 match self.ctx.outer {
1803 Some(o) if o.has_binding(q) => o.column(qual, name),
1804 _ => Value::Null,
1805 }
1806 }
1807 None => {
1808 for (_, row) in &self.parts {
1809 if let Some(v) = row.and_then(|r| r.get(name)) {
1810 return v.clone();
1811 }
1812 }
1813 match self.ctx.outer {
1814 Some(o) => o.column(None, name),
1815 None => Value::Null,
1816 }
1817 }
1818 }
1819 }
1820
1821 fn has_binding(&self, qual: &str) -> bool {
1826 self.parts.iter().any(|(b, _)| b.eq_ignore_ascii_case(qual))
1827 || self.ctx.outer.is_some_and(|o| o.has_binding(qual))
1828 }
1829
1830 fn flatten(&self) -> Vec<(String, Value)> {
1832 let mut out = vec![];
1833 for (_, row) in &self.parts {
1834 if let Some(Value::Object(m)) = row {
1835 for (k, v) in m {
1836 out.push((k.clone(), v.clone()));
1837 }
1838 }
1839 }
1840 out
1841 }
1842
1843 fn flatten_binding(&self, qual: &str) -> Vec<(String, Value)> {
1844 let mut out = vec![];
1845 for (binding, row) in &self.parts {
1846 if binding.eq_ignore_ascii_case(qual) {
1847 if let Some(Value::Object(m)) = row {
1848 for (k, v) in m {
1849 out.push((k.clone(), v.clone()));
1850 }
1851 }
1852 }
1853 }
1854 out
1855 }
1856}
1857
1858type Truth = Option<bool>;
1866
1867fn truthy(v: &Value) -> Truth {
1868 match v {
1869 Value::Null => None,
1870 Value::Bool(b) => Some(*b),
1871 _ => None,
1875 }
1876}
1877
1878fn cmp_values(a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
1884 use std::cmp::Ordering;
1885 match (a, b) {
1886 (Value::Null, _) | (_, Value::Null) => None,
1887 (Value::Number(x), Value::Number(y)) => {
1888 x.as_f64().partial_cmp(&y.as_f64())
1889 }
1890 (Value::String(x), Value::String(y)) => Some(x.cmp(y)),
1891 (Value::Bool(x), Value::Bool(y)) => Some(x.cmp(y)),
1892 (Value::Number(x), Value::String(y)) => match y.parse::<f64>() {
1895 Ok(n) => x.as_f64().partial_cmp(&Some(n)),
1896 Err(_) => Some(as_text(a).cmp(&as_text(b))),
1897 },
1898 (Value::String(x), Value::Number(y)) => match x.parse::<f64>() {
1899 Ok(n) => Some(n).partial_cmp(&y.as_f64()),
1900 Err(_) => Some(as_text(a).cmp(&as_text(b))),
1901 },
1902 _ => {
1903 let (x, y) = (as_text(a), as_text(b));
1904 if x == y { Some(Ordering::Equal) } else { Some(x.cmp(&y)) }
1905 }
1906 }
1907}
1908
1909fn as_text(v: &Value) -> String {
1911 match v {
1912 Value::String(s) => s.clone(),
1913 Value::Null => String::new(),
1914 Value::Bool(b) => (if *b { "t" } else { "f" }).to_string(),
1915 Value::Array(items) => {
1918 let inner: Vec<String> = items
1919 .iter()
1920 .map(|i| match i {
1921 Value::Null => "NULL".to_string(),
1922 Value::String(s) if s.is_empty()
1923 || s.chars().any(|c| c.is_whitespace() || matches!(c, ',' | '{' | '}' | '"' | '\\')) =>
1924 {
1925 format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
1926 }
1927 other => as_text(other),
1928 })
1929 .collect();
1930 format!("{{{}}}", inner.join(","))
1931 }
1932 other => other.to_string(),
1933 }
1934}
1935
1936fn num(v: &Value) -> Option<f64> {
1937 match v {
1938 Value::Number(n) => n.as_f64(),
1939 Value::String(s) => s.parse().ok(),
1940 Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
1941 _ => None,
1942 }
1943}
1944
1945fn from_f64(f: f64) -> Value {
1959 if f.is_finite() && f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
1960 return Value::Number((f as i64).into());
1961 }
1962 serde_json::Number::from_f64(f).map(Value::Number).unwrap_or(Value::Null)
1963}
1964
1965pub fn eval(e: &Expr, row: &Bound) -> Result<Value> {
1967 Ok(match e {
1968 Expr::Literal(v) => v.clone(),
1969
1970 Expr::Column { qual, name } => {
1971 if let Some(q) = qual {
1975 if !row.has_binding(q) {
1976 bail!("no table or alias named {:?} in this query", q);
1977 }
1978 }
1979 row.column(qual.as_deref(), name)
1980 }
1981
1982 Expr::Cast { expr, .. } => eval(expr, row)?,
1983
1984 Expr::Star | Expr::QualifiedStar(_) => {
1985 bail!("`*` is only valid in a select list or as count(*)")
1986 }
1987
1988 Expr::Unary { op, expr } => {
1989 let v = eval(expr, row)?;
1990 match op.as_str() {
1991 "NOT" => match truthy(&v) {
1992 None => Value::Null,
1994 Some(b) => Value::Bool(!b),
1995 },
1996 "-" => match num(&v) {
1997 Some(n) => from_f64(-n),
1998 None => Value::Null,
1999 },
2000 other => bail!("unsupported unary operator {:?}", other),
2001 }
2002 }
2003
2004 Expr::Binary { op, left, right } => {
2005 if op == "AND" {
2010 let l = truthy(&eval(left, row)?);
2011 if l == Some(false) {
2012 return Ok(Value::Bool(false));
2013 }
2014 let r = truthy(&eval(right, row)?);
2015 return Ok(match (l, r) {
2016 (_, Some(false)) => Value::Bool(false),
2017 (Some(true), Some(true)) => Value::Bool(true),
2018 _ => Value::Null,
2019 });
2020 }
2021 if op == "OR" {
2022 let l = truthy(&eval(left, row)?);
2023 if l == Some(true) {
2024 return Ok(Value::Bool(true));
2025 }
2026 let r = truthy(&eval(right, row)?);
2027 return Ok(match (l, r) {
2028 (_, Some(true)) => Value::Bool(true),
2029 (Some(false), Some(false)) => Value::Bool(false),
2030 _ => Value::Null,
2031 });
2032 }
2033
2034 let l = eval(left, row)?;
2035 let r = eval(right, row)?;
2036 apply_op(op, l, r)?
2037 }
2038
2039 Expr::IsNull { expr, negated } => {
2040 let v = eval(expr, row)?;
2041 Value::Bool(v.is_null() != *negated)
2044 }
2045
2046 Expr::InList { expr, list, negated } => {
2047 let v = eval(expr, row)?;
2048 if v.is_null() {
2049 return Ok(Value::Null);
2050 }
2051 let mut items = Vec::with_capacity(list.len());
2052 for item in list {
2053 items.push(eval(item, row)?);
2054 }
2055 in_values(&v, &items, *negated)?
2056 }
2057
2058 Expr::Subquery(q) => {
2060 let (cols, rows) = run_sub(q, row)?;
2061 if cols.len() != 1 {
2062 bail!("a subquery used as an expression must return exactly one \
2063 column, this one returns {}", cols.len());
2064 }
2065 match rows.len() {
2066 0 => Value::Null,
2067 1 => rows[0].get(&cols[0].key).cloned().unwrap_or(Value::Null),
2068 n => bail!("more than one row returned by a subquery used as an \
2069 expression ({} rows)", n),
2070 }
2071 }
2072 Expr::Exists { query, negated } => {
2073 let (_, rows) = run_sub(query, row)?;
2074 Value::Bool(!rows.is_empty() != *negated)
2075 }
2076 Expr::ArrayQuery(q) => Value::Array(first_column(q, row)?),
2077 Expr::InSubquery { expr, query, negated } => {
2078 let v = eval(expr, row)?;
2079 if v.is_null() {
2080 return Ok(Value::Null);
2081 }
2082 let items = first_column(query, row)?;
2083 in_values(&v, &items, *negated)?
2084 }
2085 Expr::Quantified { op, left, all, right } => {
2086 let l = eval(left, row)?;
2087 let r = eval(right, row)?;
2088 let items = match r {
2089 Value::Null => return Ok(Value::Null),
2090 Value::Array(items) => items,
2091 other => bail!(
2092 "{} requires an array or a subquery on its right side, got {}",
2093 if *all { "ALL" } else { "ANY" },
2094 as_text(&other)
2095 ),
2096 };
2097 let mut saw_true = false;
2101 let mut saw_false = false;
2102 let mut saw_null = false;
2103 for item in items {
2104 match truthy(&apply_op(op, l.clone(), item)?) {
2105 Some(true) => saw_true = true,
2106 Some(false) => saw_false = true,
2107 None => saw_null = true,
2108 }
2109 }
2110 if *all {
2111 if saw_false {
2112 Value::Bool(false)
2113 } else if saw_null {
2114 Value::Null
2115 } else {
2116 Value::Bool(true)
2117 }
2118 } else if saw_true {
2119 Value::Bool(true)
2120 } else if saw_null {
2121 Value::Null
2122 } else {
2123 Value::Bool(false)
2124 }
2125 }
2126 Expr::Index { expr, index } => {
2127 let arr = eval(expr, row)?;
2128 let i = eval(index, row)?;
2129 match (arr, num(&i)) {
2130 (Value::Array(items), Some(n)) if n >= 1.0 => {
2131 items.get(n as usize - 1).cloned().unwrap_or(Value::Null)
2132 }
2133 _ => Value::Null,
2134 }
2135 }
2136 Expr::ArrayLit(items) => {
2137 let mut out = Vec::with_capacity(items.len());
2138 for i in items {
2139 out.push(eval(i, row)?);
2140 }
2141 Value::Array(out)
2142 }
2143
2144 Expr::Case { operand, whens, else_ } => {
2145 let subject = match operand {
2146 Some(o) => Some(eval(o, row)?),
2147 None => None,
2148 };
2149 for (cond, then) in whens {
2150 let hit = match &subject {
2151 Some(sv) => {
2153 let cv = eval(cond, row)?;
2154 matches!(cmp_values(sv, &cv), Some(std::cmp::Ordering::Equal))
2155 }
2156 None => truthy(&eval(cond, row)?) == Some(true),
2159 };
2160 if hit {
2161 return eval(then, row);
2162 }
2163 }
2164 match else_ {
2165 Some(e) => eval(e, row)?,
2166 None => Value::Null,
2169 }
2170 }
2171
2172 Expr::Func { name, args } => eval_func(name, args, row)?,
2173
2174 Expr::Agg { name, .. } => bail!(
2180 "{}() is an aggregate and has no value for one row — it is reduced \
2181 over a GROUP. Reaching this point is an engine bug, not a problem \
2182 with the query",
2183 name
2184 ),
2185 })
2186}
2187
2188fn in_values(v: &Value, items: &[Value], negated: bool) -> Result<Value> {
2190 let mut any_null = false;
2191 let mut found = false;
2192 for iv in items {
2193 if iv.is_null() {
2194 any_null = true;
2195 continue;
2196 }
2197 if matches!(cmp_values(v, iv), Some(std::cmp::Ordering::Equal)) {
2198 found = true;
2199 break;
2200 }
2201 }
2202 Ok(if found {
2206 Value::Bool(!negated)
2207 } else if any_null {
2208 Value::Null
2209 } else {
2210 Value::Bool(negated)
2211 })
2212}
2213
2214fn run_sub(q: &Select, row: &Bound) -> Result<(Vec<OutCol>, Vec<Value>)> {
2222 let Some(resolve) = row.ctx.resolver else {
2223 bail!("a subquery cannot run here: this evaluation has no relation resolver");
2224 };
2225 let (cols, rows, _) = execute_inner(q, resolve, Opts::default(), Some(row))?;
2226 Ok((cols, rows))
2227}
2228
2229fn first_column(q: &Select, row: &Bound) -> Result<Vec<Value>> {
2232 let (cols, rows) = run_sub(q, row)?;
2233 let Some(first) = cols.first() else {
2234 bail!("the subquery returns no columns");
2235 };
2236 Ok(rows
2237 .into_iter()
2238 .map(|r| r.get(&first.key).cloned().unwrap_or(Value::Null))
2239 .collect())
2240}
2241
2242fn apply_op(op: &str, l: Value, r: Value) -> Result<Value> {
2246 let compare = |ord: fn(std::cmp::Ordering) -> bool| -> Value {
2248 match cmp_values(&l, &r) {
2249 None => Value::Null,
2250 Some(o) => Value::Bool(ord(o)),
2251 }
2252 };
2253
2254 Ok(match op {
2255 "IS DISTINCT FROM" | "IS NOT DISTINCT FROM" => {
2256 let distinct = match (l.is_null(), r.is_null()) {
2257 (true, true) => false,
2258 (true, false) | (false, true) => true,
2259 (false, false) => !matches!(cmp_values(&l, &r), Some(std::cmp::Ordering::Equal)),
2260 };
2261 Value::Bool(distinct != op.starts_with("IS NOT"))
2262 }
2263 "=" => compare(|o| o.is_eq()),
2264 "!=" | "<>" => compare(|o| o.is_ne()),
2265 "<" => compare(|o| o.is_lt()),
2266 "<=" => compare(|o| o.is_le()),
2267 ">" => compare(|o| o.is_gt()),
2268 ">=" => compare(|o| o.is_ge()),
2269
2270 "~" | "~*" | "!~" | "!~*" => {
2271 if l.is_null() || r.is_null() {
2272 Value::Null
2273 } else {
2274 let pat = as_text(&r);
2275 if let Some(why) = crate::nql::regex_error_pub(&pat) {
2276 bail!(
2277 "{} — in {:?}. The supported subset is ^ $ . | ( ) \
2278 [ ] * + ? and literal text",
2279 why, pat
2280 );
2281 }
2282 let hit = crate::nql::regex_match_pub(
2283 &as_text(&l), &pat, op.ends_with('*'));
2284 Value::Bool(hit != op.starts_with('!'))
2285 }
2286 }
2287
2288 "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => {
2289 if l.is_null() || r.is_null() {
2290 Value::Null
2291 } else {
2292 let hit = crate::nql::like_match_pub(
2293 &as_text(&l), &as_text(&r), op.ends_with("ILIKE"));
2294 Value::Bool(hit != op.starts_with("NOT"))
2295 }
2296 }
2297
2298 "||" => {
2300 if l.is_null() || r.is_null() {
2301 Value::Null
2302 } else {
2303 Value::String(format!("{}{}", as_text(&l), as_text(&r)))
2304 }
2305 }
2306
2307 "+" | "-" | "*" | "/" | "%" => match (num(&l), num(&r)) {
2308 (Some(a), Some(b)) => match op {
2309 "+" => from_f64(a + b),
2310 "-" => from_f64(a - b),
2311 "*" => from_f64(a * b),
2312 "/" if b == 0.0 => bail!("division by zero"),
2315 "/" => from_f64(a / b),
2316 "%" if b == 0.0 => bail!("division by zero"),
2317 "%" => from_f64(a % b),
2318 _ => unreachable!(),
2319 },
2320 _ => Value::Null,
2321 },
2322
2323 other => bail!("unsupported operator {:?}", other),
2324 })
2325}
2326
2327fn eval_func(name: &str, args: &[Expr], row: &Bound) -> Result<Value> {
2333 let arg = |i: usize| -> Result<Value> {
2336 match args.get(i) {
2337 Some(e) => eval(e, row),
2338 None => Ok(Value::Null),
2339 }
2340 };
2341
2342 Ok(match name {
2343 "pg_get_userbyid" | "current_user" | "session_user" | "user" => {
2347 Value::String("nedb".into())
2348 }
2349 "current_schema" => Value::String("public".into()),
2350 "current_database" | "current_catalog" => Value::String("nedb".into()),
2351 "version" => Value::String(crate::pgwire::version_string()),
2352
2353 "pg_table_is_visible" | "pg_type_is_visible" | "pg_function_is_visible"
2358 | "pg_opclass_is_visible" | "pg_conversion_is_visible" => Value::Bool(true),
2359
2360 "pg_encoding_to_char" => Value::String("UTF8".into()),
2362 "pg_get_expr" | "pg_get_indexdef" | "pg_get_constraintdef"
2363 | "pg_get_viewdef" | "pg_get_partkeydef" | "obj_description"
2364 | "col_description" | "shobj_description" => Value::Null,
2365
2366 "lower" => match arg(0)? {
2368 Value::Null => Value::Null,
2369 v => Value::String(as_text(&v).to_lowercase()),
2370 },
2371 "upper" => match arg(0)? {
2372 Value::Null => Value::Null,
2373 v => Value::String(as_text(&v).to_uppercase()),
2374 },
2375 "length" | "char_length" | "character_length" => match arg(0)? {
2376 Value::Null => Value::Null,
2377 v => from_f64(as_text(&v).chars().count() as f64),
2378 },
2379 "format_type" => match arg(0)? {
2380 Value::Null => Value::Null,
2381 v => Value::String(crate::pgcatalog::type_name_pub(
2382 num(&v).unwrap_or(25.0) as i32).to_string()),
2383 },
2384 "array_to_string" | "pg_catalog.array_to_string" => {
2385 match arg(0)? {
2389 Value::Array(items) => {
2390 let sep = as_text(&arg(1)?);
2391 Value::String(
2392 items.iter().map(as_text).collect::<Vec<_>>().join(&sep),
2393 )
2394 }
2395 _ => Value::Null,
2396 }
2397 }
2398 "quote_ident" => Value::String(as_text(&arg(0)?)),
2399 "quote_literal" => Value::String(format!("'{}'", as_text(&arg(0)?).replace('\'', "''"))),
2400 "format" => {
2404 let fmt = as_text(&arg(0)?);
2405 let mut out = String::new();
2406 let mut next = 1usize;
2407 let mut chars = fmt.chars().peekable();
2408 while let Some(c) = chars.next() {
2409 if c != '%' {
2410 out.push(c);
2411 continue;
2412 }
2413 match chars.next() {
2414 Some('%') => out.push('%'),
2415 Some(spec @ ('s' | 'I' | 'L')) => {
2416 let v = arg(next)?;
2417 next += 1;
2418 match (spec, &v) {
2419 ('L', Value::Null) => out.push_str("NULL"),
2420 ('L', v) => out.push_str(&format!("'{}'", as_text(v).replace('\'', "''"))),
2421 (_, v) => out.push_str(&as_text(v)),
2422 }
2423 }
2424 other => bail!("format(): unsupported conversion %{}", other.map(String::from).unwrap_or_default()),
2425 }
2426 }
2427 Value::String(out)
2428 }
2429
2430 "array_length" | "array_upper" | "cardinality" => match arg(0)? {
2434 Value::Array(items) if !items.is_empty() => from_f64(items.len() as f64),
2435 Value::Array(_) if name == "cardinality" => from_f64(0.0),
2436 _ => Value::Null,
2437 },
2438 "array_lower" => match arg(0)? {
2439 Value::Array(items) if !items.is_empty() => from_f64(1.0),
2440 _ => Value::Null,
2441 },
2442
2443 "pg_table_size" | "pg_total_relation_size" | "pg_relation_size"
2450 | "pg_indexes_size" | "pg_database_size" => Value::Null,
2451 "pg_size_pretty" => match num(&arg(0)?) {
2452 None => Value::Null,
2453 Some(n) => {
2454 let units = ["bytes", "kB", "MB", "GB", "TB", "PB"];
2455 let mut v = n;
2456 let mut u = 0usize;
2457 while v.abs() >= 10240.0 && u + 1 < units.len() {
2458 v /= 1024.0;
2459 u += 1;
2460 }
2461 Value::String(format!("{} {}", v.round() as i64, units[u]))
2462 }
2463 },
2464
2465 "pg_get_triggerdef" | "pg_get_ruledef" | "pg_get_statisticsobjdef"
2471 | "pg_get_statisticsobjdef_columns" | "pg_get_function_result"
2472 | "pg_get_function_arguments" | "pg_get_function_identity_arguments"
2473 | "pg_get_functiondef" | "pg_get_serial_sequence" | "pg_get_partition_constraintdef"
2474 | "pg_relation_filepath" | "pg_tablespace_location" => Value::Null,
2475 "pg_relation_is_publishable" => Value::Bool(true),
2476 "pg_statistics_obj_is_visible" | "pg_opfamily_is_visible" | "pg_collation_is_visible"
2477 | "pg_ts_config_is_visible" | "pg_ts_dict_is_visible" | "pg_ts_parser_is_visible"
2478 | "pg_ts_template_is_visible" | "has_table_privilege" | "has_schema_privilege"
2479 | "has_database_privilege" | "pg_has_role" => Value::Bool(true),
2480 "current_setting" => match arg(0)? {
2483 Value::Null => Value::Null,
2484 v => match as_text(&v).to_lowercase().as_str() {
2485 "server_version" => Value::String(crate::pgwire::version_string()),
2486 "server_encoding" | "client_encoding" => Value::String("UTF8".into()),
2487 "standard_conforming_strings" | "integer_datetimes" | "is_superuser" => {
2488 Value::String("on".into())
2489 }
2490 "timezone" | "log_timezone" => Value::String("UTC".into()),
2491 "search_path" => Value::String("\"$user\", public".into()),
2492 "intervalstyle" => Value::String("postgres".into()),
2493 "datestyle" => Value::String("ISO, MDY".into()),
2494 "session_authorization" => Value::String("nedb".into()),
2495 "application_name" | "default_transaction_read_only" => Value::String(String::new()),
2496 "transaction_isolation" | "default_transaction_isolation" => {
2497 Value::String("read committed".into())
2498 }
2499 "max_identifier_length" => Value::String("63".into()),
2500 other => {
2501 if truthy(&arg(1)?) == Some(true) {
2504 Value::Null
2505 } else {
2506 bail!("unrecognized configuration parameter \"{}\"", other)
2507 }
2508 }
2509 },
2510 },
2511 "pg_backend_pid" => from_f64(std::process::id() as f64),
2512 "pg_is_in_recovery" => Value::Bool(false),
2513 "txid_current" => from_f64(0.0),
2514 "now" | "current_timestamp" | "statement_timestamp" | "clock_timestamp" => {
2515 Value::String(now_iso())
2516 }
2517 "to_char" => match arg(0)? {
2518 Value::Null => Value::Null,
2519 v => Value::String(as_text(&v)),
2520 },
2521 "generate_series" | "unnest" => bail!(
2522 "{}() returns a set of rows — write it in FROM, not in the select list", name
2523 ),
2524
2525 "coalesce" => {
2527 let mut out = Value::Null;
2528 for a in args {
2529 let v = eval(a, row)?;
2530 if !v.is_null() {
2531 out = v;
2532 break;
2533 }
2534 }
2535 out
2536 }
2537 "nullif" => {
2538 let a = arg(0)?;
2539 let b = arg(1)?;
2540 if matches!(cmp_values(&a, &b), Some(std::cmp::Ordering::Equal)) {
2541 Value::Null
2542 } else {
2543 a
2544 }
2545 }
2546
2547 "int4" | "int8" | "int2" => match num(&arg(0)?) {
2549 Some(n) => from_f64(n.trunc()),
2550 None => Value::Null,
2551 },
2552 "text" => match arg(0)? {
2553 Value::Null => Value::Null,
2554 v => Value::String(as_text(&v)),
2555 },
2556
2557 other if is_aggregate(other) => bail!(
2558 "{}() is an aggregate, which is only meaningful over a whole result set — \
2559 it is evaluated by the executor, never per row",
2560 other
2561 ),
2562
2563 other => bail!(
2564 "the function {}() is not implemented. It is refused rather than \
2565 answered with NULL, because a NULL column reads as missing DATA \
2566 rather than a missing feature",
2567 other
2568 ),
2569 })
2570}
2571
2572fn now_iso() -> String {
2574 let secs = std::time::SystemTime::now()
2575 .duration_since(std::time::UNIX_EPOCH)
2576 .map(|d| d.as_secs())
2577 .unwrap_or(0);
2578 let days = (secs / 86_400) as i64;
2580 let rem = secs % 86_400;
2581 let z = days + 719_468;
2582 let era = z.div_euclid(146_097);
2583 let doe = z.rem_euclid(146_097);
2584 let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
2585 let y = yoe + era * 400;
2586 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2587 let mp = (5 * doy + 2) / 153;
2588 let d = doy - (153 * mp + 2) / 5 + 1;
2589 let m = if mp < 10 { mp + 3 } else { mp - 9 };
2590 let y = if m <= 2 { y + 1 } else { y };
2591 format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}+00", y, m, d, rem / 3600, (rem % 3600) / 60, rem % 60)
2592}
2593
2594const AGGREGATES: &[&str] = &[
2599 "count", "sum", "avg", "min", "max", "string_agg", "array_agg", "bool_and",
2600 "bool_or", "every",
2601];
2602
2603fn is_aggregate(name: &str) -> bool {
2604 AGGREGATES.iter().any(|a| a.eq_ignore_ascii_case(name))
2605}
2606
2607pub fn has_aggregate(e: &Expr) -> bool {
2610 match e {
2611 Expr::Agg { .. } => true,
2612 Expr::Func { args, .. } => args.iter().any(has_aggregate),
2613 Expr::Binary { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2614 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2615 has_aggregate(expr)
2616 }
2617 Expr::InList { expr, list, .. } => has_aggregate(expr) || list.iter().any(has_aggregate),
2618 Expr::Case { operand, whens, else_ } => {
2619 operand.as_deref().is_some_and(has_aggregate)
2620 || whens.iter().any(|(c, t)| has_aggregate(c) || has_aggregate(t))
2621 || else_.as_deref().is_some_and(has_aggregate)
2622 }
2623 Expr::Quantified { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2624 Expr::Index { expr, index } => has_aggregate(expr) || has_aggregate(index),
2625 Expr::ArrayLit(items) => items.iter().any(has_aggregate),
2626 Expr::InSubquery { expr, .. } => has_aggregate(expr),
2627 Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => false,
2628 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => false,
2629 }
2630}
2631
2632fn aggregate(
2639 name: &str,
2640 args: &[Expr],
2641 order_by: &[OrderBy],
2642 distinct: bool,
2643 rows: &[JoinedRow],
2644 ctx: EvalCtx,
2645) -> Result<Value> {
2646 let lname = name.to_lowercase();
2647
2648 let ordered: Vec<JoinedRow> = if order_by.is_empty() {
2651 rows.to_vec()
2652 } else {
2653 let mut keyed: Vec<(Vec<Value>, JoinedRow)> = Vec::with_capacity(rows.len());
2654 for r in rows {
2655 let b = bind(r, ctx);
2656 let mut key = vec![];
2657 for ob in order_by {
2658 match (&ob.expr, ob.ordinal) {
2661 (Some(e), _) => key.push(eval(e, &b)?),
2662 (None, Some(n)) => bail!(
2663 "ORDER BY {} inside an aggregate refers to a select-list \
2664 position, which an aggregate does not have — name the \
2665 column instead", n
2666 ),
2667 (None, None) => key.push(Value::Null),
2668 }
2669 }
2670 keyed.push((key, r.clone()));
2671 }
2672 keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, order_by));
2673 keyed.into_iter().map(|(_, r)| r).collect()
2674 };
2675 let rows: &[JoinedRow] = &ordered;
2676
2677 if lname == "count" && (args.is_empty() || matches!(args[0], Expr::Star)) {
2680 return Ok(from_f64(rows.len() as f64));
2681 }
2682 let Some(target) = args.first() else {
2683 bail!("{}() needs an argument", name);
2684 };
2685 let mut vals: Vec<Value> = Vec::with_capacity(rows.len());
2686 let mut all_vals: Vec<Value> = Vec::with_capacity(rows.len());
2687 for r in rows {
2688 let v = eval(target, &bind(r, ctx))?;
2689 if !v.is_null() {
2690 vals.push(v.clone());
2691 }
2692 all_vals.push(v);
2693 }
2694 if distinct {
2695 let mut seen: Vec<String> = vec![];
2696 vals.retain(|v| {
2697 let k = format!("{:?}", v);
2698 if seen.contains(&k) { false } else { seen.push(k); true }
2699 });
2700 let mut seen2: Vec<String> = vec![];
2701 all_vals.retain(|v| {
2702 let k = format!("{:?}", v);
2703 if seen2.contains(&k) { false } else { seen2.push(k); true }
2704 });
2705 }
2706 Ok(match lname.as_str() {
2707 "count" => from_f64(vals.len() as f64),
2708 "sum" | "avg" => {
2709 let nums: Vec<f64> = vals.iter().filter_map(num).collect();
2710 if nums.is_empty() {
2711 Value::Null
2712 } else if lname == "sum" {
2713 from_f64(nums.iter().sum())
2714 } else {
2715 from_f64(nums.iter().sum::<f64>() / nums.len() as f64)
2716 }
2717 }
2718 "min" | "max" => {
2719 let mut best: Option<Value> = None;
2720 for v in vals {
2721 best = Some(match best {
2722 None => v,
2723 Some(b) => {
2724 let take = match cmp_values(&v, &b) {
2725 Some(o) if lname == "min" => o.is_lt(),
2726 Some(o) => o.is_gt(),
2727 None => false,
2728 };
2729 if take { v } else { b }
2730 }
2731 });
2732 }
2733 best.unwrap_or(Value::Null)
2734 }
2735 "string_agg" => {
2736 if vals.is_empty() {
2737 Value::Null
2738 } else {
2739 let sep = match args.get(1) {
2742 Some(e) => as_text(&eval(e, &Bound { parts: vec![], ctx })?),
2743 None => String::new(),
2744 };
2745 Value::String(vals.iter().map(as_text).collect::<Vec<_>>().join(&sep))
2746 }
2747 }
2748 "array_agg" => {
2750 if all_vals.is_empty() { Value::Null } else { Value::Array(all_vals) }
2751 }
2752 "bool_and" | "every" => {
2753 if vals.is_empty() {
2754 Value::Null
2755 } else {
2756 Value::Bool(vals.iter().all(|v| truthy(v) == Some(true)))
2757 }
2758 }
2759 "bool_or" => {
2760 if vals.is_empty() {
2761 Value::Null
2762 } else {
2763 Value::Bool(vals.iter().any(|v| truthy(v) == Some(true)))
2764 }
2765 }
2766 _ => unreachable!("is_aggregate gates this"),
2767 })
2768}
2769
2770fn fold_aggregates(
2777 e: &Expr,
2778 rows: &[JoinedRow],
2779 ctx: EvalCtx,
2780 keys: &[Expr],
2781) -> Result<Expr> {
2782 let fold = |x: &Expr| fold_aggregates(x, rows, ctx, keys);
2783 Ok(match e {
2784 Expr::Agg { name, args, order_by, distinct } => {
2785 Expr::Literal(aggregate(name, args, order_by, *distinct, rows, ctx)?)
2786 }
2787 Expr::Func { name, args } => Expr::Func {
2788 name: name.clone(),
2789 args: args.iter().map(&fold).collect::<Result<_>>()?,
2790 },
2791 Expr::Column { .. } if keys.iter().any(|k| k == e) => e.clone(),
2797 Expr::Column { qual, name } => bail!(
2798 "column \"{}{}\" must appear in the GROUP BY clause or be used in an \
2799 aggregate function",
2800 qual.as_ref().map(|q| format!("{q}.")).unwrap_or_default(),
2801 name
2802 ),
2803 Expr::Binary { op, left, right } => Expr::Binary {
2804 op: op.clone(),
2805 left: Box::new(fold(left)?),
2806 right: Box::new(fold(right)?),
2807 },
2808 Expr::Unary { op, expr } => Expr::Unary {
2809 op: op.clone(),
2810 expr: Box::new(fold(expr)?),
2811 },
2812 Expr::Cast { expr, ty } => Expr::Cast {
2813 expr: Box::new(fold(expr)?),
2814 ty: ty.clone(),
2815 },
2816 Expr::IsNull { expr, negated } => Expr::IsNull {
2817 expr: Box::new(fold(expr)?),
2818 negated: *negated,
2819 },
2820 Expr::InList { expr, list, negated } => Expr::InList {
2821 expr: Box::new(fold(expr)?),
2822 list: list.iter().map(&fold).collect::<Result<_>>()?,
2823 negated: *negated,
2824 },
2825 Expr::Case { operand, whens, else_ } => Expr::Case {
2826 operand: match operand {
2827 Some(o) => Some(Box::new(fold(o)?)),
2828 None => None,
2829 },
2830 whens: whens
2831 .iter()
2832 .map(|(c, t)| Ok((fold(c)?, fold(t)?)))
2833 .collect::<Result<_>>()?,
2834 else_: match else_ {
2835 Some(x) => Some(Box::new(fold(x)?)),
2836 None => None,
2837 },
2838 },
2839 Expr::Quantified { op, left, all, right } => Expr::Quantified {
2840 op: op.clone(),
2841 left: Box::new(fold(left)?),
2842 all: *all,
2843 right: Box::new(fold(right)?),
2844 },
2845 Expr::Index { expr, index } => Expr::Index {
2846 expr: Box::new(fold(expr)?),
2847 index: Box::new(fold(index)?),
2848 },
2849 Expr::ArrayLit(items) => Expr::ArrayLit(
2850 items.iter().map(&fold).collect::<Result<_>>()?,
2851 ),
2852 Expr::InSubquery { expr, query, negated } => Expr::InSubquery {
2853 expr: Box::new(fold(expr)?),
2854 query: query.clone(),
2855 negated: *negated,
2856 },
2857 Expr::Star | Expr::QualifiedStar(_) => {
2860 bail!("`*` cannot be mixed with an aggregate outside count(*)")
2861 }
2862 Expr::Literal(_) | Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => {
2864 e.clone()
2865 }
2866 })
2867}
2868
2869fn validate_bindings(sel: &Select) -> Result<()> {
2885 let mut seen: Vec<String> = vec![];
2886 if let Some(f) = &sel.from {
2887 seen.push(f.binding());
2888 }
2889 for j in &sel.joins {
2890 seen.push(j.table.binding());
2891 }
2892 for (i, b) in seen.iter().enumerate() {
2893 if let Some(prev) = seen[..i].iter().find(|p| p.eq_ignore_ascii_case(b)) {
2894 bail!(
2895 "ambiguous relation binding: {:?} appears more than once; use \
2896 aliases (for example `FROM {} JOIN {} AS {}2 ...`)",
2897 prev, prev, prev, prev
2898 );
2899 }
2900 }
2901 Ok(())
2902}
2903
2904#[derive(Debug, Clone, PartialEq, Eq)]
2916pub struct OutCol {
2917 pub key: String,
2918 pub name: String,
2919}
2920
2921fn unique_key(taken: &[OutCol], name: &str) -> String {
2925 if !taken.iter().any(|c| c.key == name) {
2926 return name.to_string();
2927 }
2928 format!("{name}\u{1}{}", taken.len())
2929}
2930
2931type JoinedRow = Vec<(String, Option<Value>)>;
2933
2934fn bind<'a>(row: &'a JoinedRow, ctx: EvalCtx<'a>) -> Bound<'a> {
2935 Bound {
2936 parts: row.iter().map(|(b, v)| (b.clone(), v.as_ref())).collect(),
2937 ctx,
2938 }
2939}
2940
2941fn derived_name(e: &Expr) -> String {
2949 match e {
2950 Expr::Column { name, .. } => name.clone(),
2951 Expr::Func { name, .. } | Expr::Agg { name, .. } => name.clone(),
2952 Expr::Cast { expr, .. } => derived_name(expr),
2953 Expr::Case { .. } => "case".to_string(),
2954 Expr::ArrayQuery(_) | Expr::ArrayLit(_) => "array".to_string(),
2955 Expr::Exists { .. } => "exists".to_string(),
2956 Expr::Subquery(q) => q
2958 .items
2959 .first()
2960 .map(|i| i.alias.clone().unwrap_or_else(|| derived_name(&i.expr)))
2961 .unwrap_or_else(|| "?column?".to_string()),
2962 _ => "?column?".to_string(),
2963 }
2964}
2965
2966pub trait Relation {
2987 fn next_row(&mut self) -> Result<Option<Value>>;
2989
2990 fn size_hint(&self) -> Option<usize> {
2992 None
2993 }
2994}
2995
2996pub struct VecRelation {
3002 iter: std::vec::IntoIter<Value>,
3003 len: usize,
3004}
3005
3006impl Relation for VecRelation {
3007 fn next_row(&mut self) -> Result<Option<Value>> {
3008 Ok(self.iter.next())
3009 }
3010 fn size_hint(&self) -> Option<usize> {
3011 Some(self.len)
3012 }
3013}
3014
3015pub fn from_vec(rows: Vec<Value>) -> Box<dyn Relation> {
3017 let len = rows.len();
3018 Box::new(VecRelation { iter: rows.into_iter(), len })
3019}
3020
3021pub type Resolver<'r> = dyn Fn(&str) -> Result<Option<Box<dyn Relation>>> + 'r;
3027
3028pub fn execute(sel: &Select, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
3033 let (cols, rows, _) = execute_explain(sel, resolve, JoinExec::Auto)?;
3034 Ok((cols, rows))
3035}
3036
3037pub fn execute_explain(
3044 sel: &Select,
3045 resolve: &Resolver,
3046 exec: JoinExec,
3047) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3048 execute_with(sel, resolve, exec, true)
3049}
3050
3051#[derive(Debug, Clone, Copy)]
3056pub struct Opts {
3057 pub exec: JoinExec,
3058 pub pushdown: bool,
3059 pub fuse_filter: bool,
3063}
3064
3065impl Default for Opts {
3066 fn default() -> Self {
3067 Opts { exec: JoinExec::Auto, pushdown: true, fuse_filter: true }
3068 }
3069}
3070
3071impl Opts {
3072 pub fn exec(exec: JoinExec) -> Self {
3073 Opts { exec, ..Default::default() }
3074 }
3075}
3076
3077pub fn execute_with(
3084 sel: &Select,
3085 resolve: &Resolver,
3086 exec: JoinExec,
3087 pushdown: bool,
3088) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3089 execute_opts(sel, resolve, Opts { exec, pushdown, ..Default::default() })
3090}
3091
3092pub fn execute_opts(
3094 sel: &Select,
3095 resolve: &Resolver,
3096 opts: Opts,
3097) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3098 execute_inner(sel, resolve, opts, None)
3099}
3100
3101fn row_key(cols: &[OutCol], obj: &Map<String, Value>) -> String {
3103 cols.iter()
3104 .map(|c| format!("{:?}", obj.get(&c.key).unwrap_or(&Value::Null)))
3105 .collect::<Vec<_>>()
3106 .join("\u{1}")
3107}
3108
3109fn execute_set_ops<'a>(
3117 sel: &Select,
3118 resolve: &'a Resolver<'a>,
3119 opts: Opts,
3120 outer: Option<&'a Bound<'a>>,
3121) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3122 let ctx = EvalCtx { resolver: Some(resolve), outer };
3123 let mut head = sel.clone();
3124 head.set_ops.clear();
3125 head.order_by.clear();
3126 head.limit = None;
3127 head.offset = None;
3128 let (cols, rows, mut plan) = execute_inner(&head, resolve, opts, outer)?;
3129 let mut left: Vec<Map<String, Value>> = rows
3130 .into_iter()
3131 .map(|r| match r {
3132 Value::Object(m) => m,
3133 _ => Map::new(),
3134 })
3135 .collect();
3136
3137 for arm in &sel.set_ops {
3138 let (acols, arows, _) = execute_inner(&arm.query, resolve, opts, outer)?;
3139 let op_name = match arm.op {
3140 SetOp::Union => "UNION",
3141 SetOp::Intersect => "INTERSECT",
3142 SetOp::Except => "EXCEPT",
3143 };
3144 if acols.len() != cols.len() {
3145 bail!(
3146 "each {} query must have the same number of columns: {} vs {}",
3147 op_name, cols.len(), acols.len()
3148 );
3149 }
3150 let right: Vec<Map<String, Value>> = arows
3152 .into_iter()
3153 .map(|r| {
3154 let m = match r {
3155 Value::Object(m) => m,
3156 _ => Map::new(),
3157 };
3158 let mut out = Map::new();
3159 for (i, c) in cols.iter().enumerate() {
3160 out.insert(c.key.clone(), m.get(&acols[i].key).cloned().unwrap_or(Value::Null));
3161 }
3162 out
3163 })
3164 .collect();
3165 let (nl, nr) = (left.len(), right.len());
3166 let right_keys: std::collections::HashSet<String> =
3167 right.iter().map(|m| row_key(&cols, m)).collect();
3168 let mut combined: Vec<Map<String, Value>> = match arm.op {
3169 SetOp::Union => {
3170 left.extend(right);
3171 left
3172 }
3173 SetOp::Intersect => left.into_iter().filter(|m| right_keys.contains(&row_key(&cols, m))).collect(),
3174 SetOp::Except => left.into_iter().filter(|m| !right_keys.contains(&row_key(&cols, m))).collect(),
3175 };
3176 if !arm.all {
3177 let mut seen = std::collections::HashSet::new();
3178 combined.retain(|m| seen.insert(row_key(&cols, m)));
3179 }
3180 plan.notes.push(format!(
3181 "{}{}: {} + {} rows -> {} (each arm planned separately; only the first arm's plan is shown)",
3182 op_name, if arm.all { " ALL" } else { "" }, nl, nr, combined.len()
3183 ));
3184 left = combined;
3185 }
3186
3187 let projected: Vec<(Map<String, Value>, JoinedRow)> = left
3190 .into_iter()
3191 .map(|m| {
3192 let src: JoinedRow = vec![(String::new(), Some(Value::Object(m.clone())))];
3193 (m, src)
3194 })
3195 .collect();
3196 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3197 Ok((cols, out, plan))
3198}
3199
3200fn execute_inner<'a>(
3203 sel: &Select,
3204 resolve: &'a Resolver<'a>,
3205 opts: Opts,
3206 outer: Option<&'a Bound<'a>>,
3207) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3208 if !sel.set_ops.is_empty() {
3209 return execute_set_ops(sel, resolve, opts, outer);
3210 }
3211 let ctx = EvalCtx { resolver: Some(resolve), outer };
3212 let exec = opts.exec;
3213 let pushdown = opts.pushdown;
3214 let mut plan = Plan::default();
3215
3216 validate_bindings(sel)?;
3218
3219 let fuse = opts.fuse_filter && sel.where_.is_some() && !sel.joins.is_empty();
3246
3247 let budget: Option<usize> = match sel.limit {
3248 Some(lim)
3249 if sel.order_by.is_empty()
3250 && !sel.distinct
3251 && !sel.joins.is_empty()
3252 && (sel.where_.is_none() || fuse) =>
3253 {
3254 Some(lim.saturating_add(sel.offset.unwrap_or(0)))
3255 }
3256 _ => None,
3257 };
3258 plan.budget = budget;
3259
3260 let all_bindings: Vec<String> = sel
3266 .from
3267 .iter()
3268 .map(|t| t.binding())
3269 .chain(sel.joins.iter().map(|j| j.table.binding()))
3270 .collect();
3271 let nullable = crate::sqlpush::nullable_bindings(sel);
3272 let push = if pushdown {
3273 crate::sqlpush::plan(sel.where_.as_ref(), &all_bindings, &nullable)
3274 } else {
3275 Pushdown::default()
3276 };
3277 plan.refusals = push.refusals.clone();
3278
3279 let mut base_scan_at: Option<usize> = None;
3280 let mut base_prefilter_at: Option<usize> = None;
3281
3282 let mut left_src: Box<dyn LeftSource + 'a> = match &sel.from {
3290 None => {
3291 Box::new(VecLeft { rows: vec![vec![]], at: 0 })
3294 }
3295 Some(t) => {
3296 let rel = fetch(t, resolve, ctx)?;
3297 let binding = t.binding();
3298 base_scan_at = Some(plan.stages.len());
3303 plan.push(Stage::Scan {
3304 table: t.name.clone(),
3305 binding: binding.clone(),
3306 rows: 0,
3307 });
3308 let preds = push.for_binding(&binding).cloned().unwrap_or_default();
3309 if !preds.is_empty() {
3310 base_prefilter_at = Some(plan.stages.len());
3311 plan.push(Stage::Prefilter {
3312 binding: binding.clone(),
3313 predicates: preds.len(),
3314 in_rows: 0,
3315 out_rows: 0,
3316 });
3317 }
3318 Box::new(StreamLeft { rel, binding, preds, pulled: 0, kept: 0, ctx })
3319 }
3320 };
3321
3322 let mut left_bindings: Vec<String> = match &sel.from {
3328 None => vec![],
3329 Some(t) => vec![t.binding()],
3330 };
3331 let last = sel.joins.len().saturating_sub(1);
3332 let mut rows: Vec<JoinedRow> = vec![];
3333 let mut base_pulled: Option<usize> = None;
3334 let mut base_kept: Option<usize> = None;
3335
3336 for (ji, join) in sel.joins.iter().enumerate() {
3337 let is_last = ji == last;
3338 let rb = join.table.binding();
3339
3340 if join.table.lateral {
3344 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3345 let join_budget = if is_last { budget } else { None };
3346 let (out, removed, consumed, produced) = join_lateral(
3347 left_src.as_mut(), join, resolve, join_budget, post, ctx,
3348 )?;
3349 plan.push(Stage::Scan { table: join.table.name.clone(), binding: rb.clone(), rows: produced });
3350 plan.push(Stage::Join {
3351 kind: join.kind,
3352 table: join.table.name.clone(),
3353 binding: rb.clone(),
3354 strategy: Strategy::NestedLoop,
3355 keys: 0,
3356 left_rows: consumed,
3357 right_rows: produced,
3358 out_rows: out.len(),
3359 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3360 post_filter_removed: post.map(|_| removed),
3361 });
3362 plan.notes.push(format!("LATERAL {}: the subquery ran once per left row ({} times)", rb, consumed));
3363 left_bindings.push(rb);
3364 if ji == 0 {
3365 if let Some((pulled, kept)) = left_src.stats() {
3366 base_pulled = Some(pulled);
3367 base_kept = Some(kept);
3368 }
3369 }
3370 left_src = Box::new(VecLeft { rows: out, at: 0 });
3371 continue;
3372 }
3373
3374 let right_rel = fetch(&join.table, resolve, ctx)?;
3375 let right_all = drain(right_rel)?;
3376 plan.push(Stage::Scan {
3377 table: join.table.name.clone(),
3378 binding: rb.clone(),
3379 rows: right_all.len(),
3380 });
3381 let right_rows = prefilter(right_all, &rb, &push, &mut plan, ctx)?;
3382
3383 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3388 let join_budget = if is_last { budget } else { None };
3389
3390 let keys = sqljoin::hash_keys(join.on.as_ref(), &left_bindings, &rb);
3393 let left_hint = left_src.hint().unwrap_or(usize::MAX);
3394 let strategy = sqljoin::choose(exec, keys.len(), left_hint, right_rows.len());
3395
3396 let (out, removed, consumed) = match strategy {
3397 Strategy::NestedLoop => join_nested_loop(
3398 left_src.as_mut(), &left_bindings, join, &right_rows, &rb,
3399 join_budget, post, ctx,
3400 )?,
3401 Strategy::Hash => join_hash(
3402 left_src.as_mut(), &left_bindings, join, &right_rows, &rb, &keys,
3403 join_budget, post, ctx,
3404 )?,
3405 };
3406
3407 plan.push(Stage::Join {
3408 kind: join.kind,
3409 table: join.table.name.clone(),
3410 binding: rb.clone(),
3411 strategy,
3412 keys: keys.len(),
3413 left_rows: consumed,
3414 right_rows: right_rows.len(),
3415 out_rows: out.len(),
3416 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3417 post_filter_removed: post.map(|_| removed),
3418 });
3419 left_bindings.push(rb);
3420 if ji == 0 {
3422 if let Some((pulled, kept)) = left_src.stats() {
3423 base_pulled = Some(pulled);
3424 base_kept = Some(kept);
3425 }
3426 }
3427 rows = out;
3428 left_src = Box::new(VecLeft { rows: std::mem::take(&mut rows), at: 0 });
3430 }
3431
3432 rows = left_src.take_rows();
3435 if let Some(i) = base_scan_at {
3436 if let (Some(pulled), Some(kept)) = (base_pulled, base_kept) {
3437 if let Some(Stage::Scan { rows: r, .. }) = plan.stages.get_mut(i) {
3438 *r = pulled;
3439 }
3440 if let Some(j) = base_prefilter_at {
3441 if let Some(Stage::Prefilter { in_rows, out_rows, .. }) =
3442 plan.stages.get_mut(j)
3443 {
3444 *in_rows = pulled;
3445 *out_rows = kept;
3446 }
3447 }
3448 }
3449 }
3450
3451 if let Some(pred) = sel.where_.as_ref().filter(|_| !fuse) {
3453 let in_rows = rows.len();
3454 let mut kept = Vec::with_capacity(rows.len());
3455 for r in rows {
3456 if truthy(&eval(pred, &bind(&r, ctx))?) == Some(true) {
3460 kept.push(r);
3461 }
3462 }
3463 rows = kept;
3464 plan.push(Stage::Filter { in_rows, out_rows: rows.len() });
3465 }
3466
3467 let grouping = !sel.group_by.is_empty();
3480 let aggregating = grouping
3481 || sel.items.iter().any(|i| has_aggregate(&i.expr))
3482 || sel.having.as_ref().is_some_and(has_aggregate);
3483 if aggregating {
3484 let mut groups: Vec<(Vec<Value>, Vec<JoinedRow>)> = vec![];
3488 if grouping {
3489 for r in rows {
3490 let b = bind(&r, ctx);
3491 let mut key = Vec::with_capacity(sel.group_by.len());
3492 for g in &sel.group_by {
3493 key.push(eval(g, &b)?);
3494 }
3495 match groups.iter_mut().find(|(k, _)| {
3496 k.len() == key.len()
3497 && k.iter().zip(&key).all(|(a, b)| {
3498 (a.is_null() && b.is_null())
3501 || matches!(cmp_values(a, b), Some(std::cmp::Ordering::Equal))
3502 })
3503 }) {
3504 Some((_, bucket)) => bucket.push(r),
3505 None => groups.push((key, vec![r])),
3506 }
3507 }
3508 } else {
3509 groups.push((vec![], rows));
3512 }
3513
3514 let n_groups = groups.len();
3515 let mut cols: Vec<OutCol> = vec![];
3516 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = vec![];
3517 let empty: JoinedRow = vec![];
3518
3519 for (gi, (_key, grows)) in groups.iter().enumerate() {
3520 let scope = grows.first().unwrap_or(&empty);
3525
3526 if let Some(h) = &sel.having {
3527 let folded = fold_aggregates(h, grows, ctx, &sel.group_by)?;
3528 if truthy(&eval(&folded, &bind(scope, ctx))?) != Some(true) {
3529 continue;
3530 }
3531 }
3532
3533 let mut obj = Map::new();
3534 for item in &sel.items {
3535 let folded = fold_aggregates(&item.expr, grows, ctx, &sel.group_by)?;
3536 let v = eval(&folded, &bind(scope, ctx))?;
3537 if gi == 0 {
3539 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3540 let key = unique_key(&cols, &name);
3541 obj.insert(key.clone(), v);
3542 cols.push(OutCol { key, name });
3543 } else {
3544 let idx = obj.len();
3545 if let Some(c) = cols.get(idx) {
3546 obj.insert(c.key.clone(), v);
3547 }
3548 }
3549 }
3550 projected.push((obj, scope.clone()));
3551 }
3552
3553 plan.notes.push(if grouping {
3554 format!(
3555 "GroupAggregate on {} key(s): {} rows -> {} group(s){}",
3556 sel.group_by.len(),
3557 n_rows_before_group(&plan),
3558 n_groups,
3559 if sel.having.is_some() {
3560 format!(", HAVING kept {}", projected.len())
3561 } else {
3562 String::new()
3563 }
3564 )
3565 } else {
3566 format!("Aggregate over {} row(s) -> 1 row", groups[0].1.len())
3567 });
3568 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3569 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3570 return Ok((cols, out, plan));
3571 }
3572
3573 let mut cols: Vec<OutCol> = vec![];
3604 let mut spans: Vec<(usize, usize)> = Vec::with_capacity(sel.items.len());
3605 for item in &sel.items {
3606 let start = cols.len();
3607 match &item.expr {
3608 Expr::Star => {
3609 let mut plain: Vec<String> = vec![];
3610 let mut meta: Vec<String> = vec![];
3611 for r in &rows {
3612 for (n, _) in bind(r, ctx).flatten() {
3613 let target = if n.starts_with('_') { &mut meta } else { &mut plain };
3614 if !target.contains(&n) {
3616 target.push(n);
3617 }
3618 }
3619 }
3620 meta.sort();
3623 for n in plain.into_iter().chain(meta) {
3624 if !cols.iter().any(|c| c.name == n) {
3625 cols.push(OutCol { key: n.clone(), name: n });
3626 }
3627 }
3628 }
3629 Expr::QualifiedStar(q) => {
3630 if let Some(first) = rows.first() {
3631 for (n, _) in bind(first, ctx).flatten_binding(q) {
3632 if !cols.iter().any(|c| c.name == n) {
3633 cols.push(OutCol { key: n.clone(), name: n });
3634 }
3635 }
3636 }
3637 }
3638 _ => {
3639 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3640 let key = unique_key(&cols, &name);
3646 cols.push(OutCol { key, name });
3647 }
3648 }
3649 spans.push((start, cols.len()));
3650 }
3651
3652 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = Vec::with_capacity(rows.len());
3656 for r in rows {
3657 let b = bind(&r, ctx);
3658 let mut obj = Map::new();
3659 for (i, item) in sel.items.iter().enumerate() {
3660 let (start, end) = spans[i];
3661 match &item.expr {
3662 Expr::Star => {
3663 for (n, v) in b.flatten() {
3664 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3665 obj.entry(c.key.clone()).or_insert(v);
3666 }
3667 }
3668 }
3669 Expr::QualifiedStar(q) => {
3670 for (n, v) in b.flatten_binding(q) {
3671 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3672 obj.entry(c.key.clone()).or_insert(v);
3673 }
3674 }
3675 }
3676 _ => {
3677 let v = eval(&item.expr, &b)?;
3678 if let Some(c) = cols.get(start) {
3679 obj.insert(c.key.clone(), v);
3680 }
3681 }
3682 }
3683 }
3684 projected.push((obj, r));
3685 }
3686
3687 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3688
3689 if sel.distinct {
3691 let in_rows = projected.len();
3692 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
3693 projected.retain(|(obj, _)| seen.insert(row_key(&cols, obj)));
3696 plan.push(Stage::Distinct { in_rows, out_rows: projected.len() });
3697 }
3698
3699 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3700 Ok((cols, out, plan))
3701}
3702
3703fn n_rows_before_group(plan: &Plan) -> usize {
3707 plan.stages
3708 .iter()
3709 .rev()
3710 .find_map(|st| match st {
3711 Stage::Filter { out_rows, .. } => Some(*out_rows),
3712 Stage::Join { out_rows, .. } => Some(*out_rows),
3713 Stage::Prefilter { out_rows, .. } => Some(*out_rows),
3714 Stage::Scan { rows, .. } => Some(*rows),
3715 _ => None,
3716 })
3717 .unwrap_or(0)
3718}
3719
3720fn sort_keys(a: &[Value], b: &[Value], order_by: &[OrderBy]) -> std::cmp::Ordering {
3727 for (i, ob) in order_by.iter().enumerate() {
3728 let (Some(x), Some(y)) = (a.get(i), b.get(i)) else { continue };
3729 let ord = match (x.is_null(), y.is_null()) {
3730 (true, true) => std::cmp::Ordering::Equal,
3731 (true, false) => {
3734 return if ob.nulls_first {
3735 std::cmp::Ordering::Less
3736 } else {
3737 std::cmp::Ordering::Greater
3738 }
3739 }
3740 (false, true) => {
3741 return if ob.nulls_first {
3742 std::cmp::Ordering::Greater
3743 } else {
3744 std::cmp::Ordering::Less
3745 }
3746 }
3747 (false, false) => cmp_values(x, y).unwrap_or(std::cmp::Ordering::Equal),
3748 };
3749 let ord = if matches!(ob.dir, Dir::Desc) { ord.reverse() } else { ord };
3750 if !ord.is_eq() {
3751 return ord;
3752 }
3753 }
3754 std::cmp::Ordering::Equal
3755}
3756
3757fn finish(
3759 sel: &Select,
3760 cols: &[OutCol],
3761 mut projected: Vec<(Map<String, Value>, JoinedRow)>,
3762 ctx: EvalCtx,
3763 plan: &mut Plan,
3764) -> Result<Vec<Value>> {
3765 if !sel.order_by.is_empty() {
3767 let mut keyed: Vec<(Vec<Value>, (Map<String, Value>, JoinedRow))> = vec![];
3771 for (obj, src) in projected {
3772 let mut key = vec![];
3773 for ob in &sel.order_by {
3774 let v = match (ob.ordinal, &ob.expr) {
3775 (Some(n), _) => {
3776 let c = cols.get(n - 1).ok_or_else(|| {
3777 anyhow::anyhow!(
3778 "ORDER BY {} is out of range: the select list has {} \
3779 column(s)", n, cols.len())
3780 })?;
3781 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3782 }
3783 (None, Some(Expr::Column { qual: None, name }))
3788 if cols.iter().any(|c| c.name == *name) =>
3789 {
3790 let c = cols.iter().find(|c| c.name == *name).expect("checked");
3791 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3792 }
3793 (None, Some(e)) => {
3794 eval(e, &bind(&src, ctx))?
3798 }
3799 (None, None) => Value::Null,
3800 };
3801 key.push(v);
3802 }
3803 keyed.push((key, (obj, src)));
3804 }
3805
3806 keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, &sel.order_by));
3807
3808 projected = keyed.into_iter().map(|(_, row)| row).collect();
3809 plan.push(Stage::Sort { keys: sel.order_by.len(), rows: projected.len() });
3810 }
3811
3812 let mut out: Vec<Value> = projected
3814 .into_iter()
3815 .map(|(obj, _)| Value::Object(obj))
3816 .collect();
3817 let in_rows = out.len();
3818 if let Some(off) = sel.offset {
3819 out = if off >= out.len() { vec![] } else { out.split_off(off) };
3820 }
3821 if let Some(lim) = sel.limit {
3822 out.truncate(lim);
3823 }
3824 if sel.limit.is_some() || sel.offset.is_some() {
3825 plan.push(Stage::Limit {
3826 limit: sel.limit,
3827 offset: sel.offset,
3828 in_rows,
3829 out_rows: out.len(),
3830 });
3831 }
3832
3833 Ok(out)
3834}
3835
3836fn keep_row(cand: &JoinedRow, post: Option<&Expr>, removed: &mut usize, ctx: EvalCtx) -> Result<bool> {
3869 let Some(p) = post else { return Ok(true) };
3870 if truthy(&eval(p, &bind(cand, ctx))?) == Some(true) {
3872 Ok(true)
3873 } else {
3874 *removed += 1;
3875 Ok(false)
3876 }
3877}
3878
3879#[allow(clippy::too_many_arguments)]
3885fn emit_unmatched_right(
3886 out: &mut Vec<JoinedRow>,
3887 kind: JoinKind,
3888 left_bindings: &[String],
3889 right_rows: &[Value],
3890 right_matched: &[bool],
3891 rb: &str,
3892 post: Option<&Expr>,
3893 removed: &mut usize,
3894 ctx: EvalCtx,
3895) -> Result<()> {
3896 if !matches!(kind, JoinKind::Right | JoinKind::Full) {
3897 return Ok(());
3898 }
3899 for (ri, right) in right_rows.iter().enumerate() {
3900 if right_matched[ri] {
3901 continue;
3902 }
3903 let mut cand: JoinedRow = left_bindings.iter().map(|b| (b.clone(), None)).collect();
3904 cand.push((rb.to_string(), Some(right.clone())));
3905 if keep_row(&cand, post, removed, ctx)? {
3908 out.push(cand);
3909 }
3910 }
3911 Ok(())
3912}
3913
3914fn join_lateral(
3921 left_src: &mut dyn LeftSource,
3922 join: &Join,
3923 resolve: &Resolver,
3924 budget: Option<usize>,
3925 post: Option<&Expr>,
3926 ctx: EvalCtx,
3927) -> Result<(Vec<JoinedRow>, usize, usize, usize)> {
3928 let sub = join
3929 .table
3930 .sub
3931 .as_deref()
3932 .ok_or_else(|| anyhow::anyhow!("LATERAL requires a subquery"))?;
3933 let rb = join.table.binding();
3934 let mut out: Vec<JoinedRow> = vec![];
3935 let mut removed = 0usize;
3936 let mut consumed = 0usize;
3937 let mut produced = 0usize;
3938 while let Some(left) = {
3939 if budget.is_some_and(|b| out.len() >= b) { None } else { left_src.next_left()? }
3940 } {
3941 consumed += 1;
3942 let scope = bind(&left, ctx);
3943 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), Some(&scope))?;
3944 produced += rows.len();
3945 let mut matched = false;
3946 for r in rows {
3947 let m = match r {
3948 Value::Object(m) => m,
3949 _ => Map::new(),
3950 };
3951 let mut named = Map::new();
3952 for (i, c) in cols.iter().enumerate() {
3953 let name = join.table.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
3954 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
3955 }
3956 let mut cand = left.clone();
3957 cand.push((rb.clone(), Some(Value::Object(named))));
3958 let on_ok = match &join.on {
3959 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
3960 None => true,
3961 };
3962 if !on_ok {
3963 continue;
3964 }
3965 matched = true;
3966 if keep_row(&cand, post, &mut removed, ctx)? {
3967 out.push(cand);
3968 }
3969 }
3970 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
3971 let mut cand = left.clone();
3972 cand.push((rb.clone(), None));
3973 if keep_row(&cand, post, &mut removed, ctx)? {
3974 out.push(cand);
3975 }
3976 }
3977 }
3978 Ok((out, removed, consumed, produced))
3979}
3980
3981#[allow(clippy::too_many_arguments)]
3987fn join_nested_loop(
3988 left_src: &mut dyn LeftSource,
3989 left_bindings: &[String],
3990 join: &Join,
3991 right_rows: &[Value],
3992 rb: &str,
3993 budget: Option<usize>,
3994 post: Option<&Expr>,
3995 ctx: EvalCtx,
3996) -> Result<(Vec<JoinedRow>, usize, usize)> {
3997 let mut out: Vec<JoinedRow> = vec![];
3998 let mut removed = 0usize;
3999 let mut right_matched = vec![false; right_rows.len()];
4001
4002 let mut consumed = 0usize;
4003 while let Some(left) = {
4004 if budget.is_some_and(|b| out.len() >= b) {
4005 None
4008 } else {
4009 left_src.next_left()?
4010 }
4011 } {
4012 consumed += 1;
4013 let left = &left;
4014 let mut matched = false;
4017 for (ri, right) in right_rows.iter().enumerate() {
4018 let mut cand: JoinedRow = left.clone();
4019 cand.push((rb.to_string(), Some(right.clone())));
4020 let joins_here = match &join.on {
4021 None => true,
4023 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4027 };
4028 if joins_here {
4029 matched = true;
4030 right_matched[ri] = true;
4031 if keep_row(&cand, post, &mut removed, ctx)? {
4032 out.push(cand);
4033 }
4034 }
4035 }
4036 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4038 let mut cand: JoinedRow = left.clone();
4039 cand.push((rb.to_string(), None));
4040 if keep_row(&cand, post, &mut removed, ctx)? {
4041 out.push(cand);
4042 }
4043 }
4044 }
4045
4046 if !budget.is_some_and(|b| out.len() >= b) {
4051 emit_unmatched_right(
4052 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4053 &mut removed, ctx,
4054 )?;
4055 }
4056 Ok((out, removed, consumed))
4057}
4058
4059#[allow(clippy::too_many_arguments)]
4067fn join_hash(
4068 left_src: &mut dyn LeftSource,
4069 left_bindings: &[String],
4070 join: &Join,
4071 right_rows: &[Value],
4072 rb: &str,
4073 keys: &[(Expr, Expr)],
4074 budget: Option<usize>,
4075 post: Option<&Expr>,
4076 ctx: EvalCtx,
4077) -> Result<(Vec<JoinedRow>, usize, usize)> {
4078 debug_assert!(!keys.is_empty(), "the planner must not choose Hash with no keys");
4079
4080 let side = sqljoin::HashSide::build(right_rows.len(), |i| {
4082 let one: JoinedRow = vec![(rb.to_string(), Some(right_rows[i].clone()))];
4085 let b = bind(&one, ctx);
4086 let mut k = Vec::with_capacity(keys.len());
4087 for (_, right_expr) in keys {
4088 match sqljoin::hkey(&eval(right_expr, &b)?) {
4089 Some(h) => k.push(h),
4090 None => return Ok(None),
4092 }
4093 }
4094 Ok(Some(k))
4095 })?;
4096
4097 let mut out: Vec<JoinedRow> = vec![];
4099 let mut removed = 0usize;
4100 let mut right_matched = vec![false; right_rows.len()];
4101
4102 let mut consumed = 0usize;
4103 while let Some(left) = {
4104 if budget.is_some_and(|b| out.len() >= b) {
4105 None
4106 } else {
4107 left_src.next_left()?
4108 }
4109 } {
4110 consumed += 1;
4111 let left = &left;
4112 let lb = bind(left, ctx);
4113 let mut lk = Vec::with_capacity(keys.len());
4114 let mut null_key = false;
4115 for (left_expr, _) in keys {
4116 match sqljoin::hkey(&eval(left_expr, &lb)?) {
4117 Some(h) => lk.push(h),
4118 None => {
4119 null_key = true;
4120 break;
4121 }
4122 }
4123 }
4124
4125 let mut matched = false;
4126 if !null_key {
4130 for &ri in side.probe(&lk) {
4131 let mut cand: JoinedRow = left.clone();
4132 cand.push((rb.to_string(), Some(right_rows[ri].clone())));
4133 let joins_here = match &join.on {
4135 None => true,
4136 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4137 };
4138 if joins_here {
4139 matched = true;
4140 right_matched[ri] = true;
4141 if keep_row(&cand, post, &mut removed, ctx)? {
4142 out.push(cand);
4143 }
4144 }
4145 }
4146 }
4147 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4148 let mut cand: JoinedRow = left.clone();
4149 cand.push((rb.to_string(), None));
4150 if keep_row(&cand, post, &mut removed, ctx)? {
4151 out.push(cand);
4152 }
4153 }
4154 }
4155
4156 if !budget.is_some_and(|b| out.len() >= b) {
4159 emit_unmatched_right(
4160 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4161 &mut removed, ctx,
4162 )?;
4163 }
4164 Ok((out, removed, consumed))
4165}
4166
4167fn prefilter(
4174 rows: Vec<Value>,
4175 binding: &str,
4176 push: &Pushdown,
4177 plan: &mut Plan,
4178 ctx: EvalCtx,
4179) -> Result<Vec<Value>> {
4180 let Some(preds) = push.for_binding(binding) else { return Ok(rows) };
4181 if preds.is_empty() {
4182 return Ok(rows);
4183 }
4184 let in_rows = rows.len();
4185 let mut kept = Vec::with_capacity(rows.len());
4186 for row in rows {
4187 let one: JoinedRow = vec![(binding.to_string(), Some(row))];
4188 let b = bind(&one, ctx);
4189 let mut keep = true;
4190 for p in preds {
4191 if truthy(&eval(p, &b)?) != Some(true) {
4197 keep = false;
4198 break;
4199 }
4200 }
4201 if keep {
4202 if let Some((_, Some(v))) = one.into_iter().next() {
4204 kept.push(v);
4205 }
4206 }
4207 }
4208 plan.push(Stage::Prefilter {
4209 binding: binding.to_string(),
4210 predicates: preds.len(),
4211 in_rows,
4212 out_rows: kept.len(),
4213 });
4214 Ok(kept)
4215}
4216
4217fn fetch(t: &TableRef, resolve: &Resolver, ctx: EvalCtx) -> Result<Box<dyn Relation>> {
4220 if let Some(sub) = &t.sub {
4224 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), ctx.outer)?;
4225 let out = rows
4226 .into_iter()
4227 .map(|r| {
4228 let m = match r {
4229 Value::Object(m) => m,
4230 _ => Map::new(),
4231 };
4232 let mut named = Map::new();
4233 for (i, c) in cols.iter().enumerate() {
4234 let name = t.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
4235 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
4238 }
4239 Value::Object(named)
4240 })
4241 .collect();
4242 return Ok(from_vec(out));
4243 }
4244
4245 if let Some(args) = &t.args {
4249 let empty: JoinedRow = vec![];
4250 let scope = bind(&empty, ctx);
4251 let col = |i: usize, default: &str| -> String {
4252 t.col_aliases.get(i).cloned().unwrap_or_else(|| default.to_string())
4253 };
4254 let rows: Vec<Value> = match t.name.as_str() {
4255 "generate_series" => {
4256 let a = num(&eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_series() needs a start"))?, &scope)?);
4257 let b = num(&eval(args.get(1).ok_or_else(|| anyhow::anyhow!("generate_series() needs a stop"))?, &scope)?);
4258 let step = match args.get(2) {
4259 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4260 None => 1.0,
4261 };
4262 match (a, b) {
4263 (Some(a), Some(b)) if step != 0.0 => {
4265 let mut out = vec![];
4266 let mut x = a;
4267 while (step > 0.0 && x <= b) || (step < 0.0 && x >= b) {
4268 let mut m = Map::new();
4269 m.insert(col(0, "generate_series"), from_f64(x));
4270 out.push(Value::Object(m));
4271 x += step;
4272 if out.len() > 1_000_000 {
4273 bail!("generate_series() would produce more than a million rows");
4274 }
4275 }
4276 out
4277 }
4278 (Some(_), Some(_)) => bail!("generate_series() step cannot equal zero"),
4279 _ => vec![],
4280 }
4281 }
4282 "unnest" => match eval(args.first().ok_or_else(|| anyhow::anyhow!("unnest() needs an array"))?, &scope)? {
4283 Value::Array(items) => items
4284 .into_iter()
4285 .map(|v| {
4286 let mut m = Map::new();
4287 m.insert(col(0, "unnest"), v);
4288 Value::Object(m)
4289 })
4290 .collect(),
4291 _ => vec![],
4293 },
4294 "generate_subscripts" => {
4299 let dim = match args.get(1) {
4300 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4301 None => 1.0,
4302 };
4303 match eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_subscripts() needs an array"))?, &scope)? {
4304 Value::Array(items) if dim == 1.0 => (1..=items.len())
4308 .map(|i| {
4309 let mut m = Map::new();
4310 m.insert(col(0, "generate_subscripts"), from_f64(i as f64));
4311 Value::Object(m)
4312 })
4313 .collect(),
4314 _ => vec![],
4315 }
4316 }
4317 "pg_partition_tree" | "pg_partition_ancestors" => vec![],
4320 other => bail!(
4321 "the table function {}() is not implemented. It is refused rather \
4322 than answered with no rows, because an empty relation reads as \
4323 missing DATA rather than a missing feature",
4324 other
4325 ),
4326 };
4327 return Ok(from_vec(rows));
4328 }
4329
4330 match resolve(&t.name)? {
4331 Some(rel) => Ok(rel),
4332 None => bail!("relation {:?} does not exist", t.name),
4335 }
4336}
4337
4338trait LeftSource {
4345 fn next_left(&mut self) -> Result<Option<JoinedRow>>;
4346 fn hint(&self) -> Option<usize>;
4348 fn take_rows(&mut self) -> Vec<JoinedRow>;
4350 fn stats(&self) -> Option<(usize, usize)> {
4352 None
4353 }
4354}
4355
4356struct StreamLeft<'a> {
4362 rel: Box<dyn Relation>,
4363 binding: String,
4364 preds: Vec<Expr>,
4365 ctx: EvalCtx<'a>,
4366 pulled: usize,
4370 kept: usize,
4371}
4372
4373impl<'a> LeftSource for StreamLeft<'a> {
4374 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4375 while let Some(row) = self.rel.next_row()? {
4376 self.pulled += 1;
4377 let one: JoinedRow = vec![(self.binding.clone(), Some(row))];
4378 if !self.preds.is_empty() {
4379 let b = bind(&one, self.ctx);
4380 let mut keep = true;
4381 for p in &self.preds {
4382 if truthy(&eval(p, &b)?) != Some(true) {
4383 keep = false;
4384 break;
4385 }
4386 }
4387 if !keep {
4388 continue;
4389 }
4390 }
4391 self.kept += 1;
4392 return Ok(Some(one));
4393 }
4394 Ok(None)
4395 }
4396 fn hint(&self) -> Option<usize> {
4397 self.rel.size_hint()
4402 }
4403 fn take_rows(&mut self) -> Vec<JoinedRow> {
4404 let mut out = vec![];
4407 while let Ok(Some(r)) = self.next_left() {
4408 out.push(r);
4409 }
4410 out
4411 }
4412 fn stats(&self) -> Option<(usize, usize)> {
4413 Some((self.pulled, self.kept))
4414 }
4415}
4416
4417struct VecLeft {
4420 rows: Vec<JoinedRow>,
4421 at: usize,
4422}
4423
4424impl LeftSource for VecLeft {
4425 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4426 let r = self.rows.get(self.at).cloned();
4427 if r.is_some() {
4428 self.at += 1;
4429 }
4430 Ok(r)
4431 }
4432 fn hint(&self) -> Option<usize> {
4433 Some(self.rows.len().saturating_sub(self.at))
4434 }
4435 fn take_rows(&mut self) -> Vec<JoinedRow> {
4436 let mut v = std::mem::take(&mut self.rows);
4437 if self.at > 0 {
4438 v = v.split_off(self.at);
4439 }
4440 self.at = 0;
4441 v
4442 }
4443}
4444
4445fn drain(mut rel: Box<dyn Relation>) -> Result<Vec<Value>> {
4452 let mut out = Vec::with_capacity(rel.size_hint().unwrap_or(0));
4453 while let Some(row) = rel.next_row()? {
4454 out.push(row);
4455 }
4456 Ok(out)
4457}
4458
4459pub fn run(sql: &str, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
4461 let sel = parse(sql)?;
4462 execute(&sel, resolve)
4463}
4464
4465#[cfg(test)]
4466mod lexer_tests {
4467 use super::*;
4468
4469 fn kinds(src: &str) -> Vec<Tok> {
4470 let mut t = lex(src).expect("lexes");
4471 t.pop(); t
4473 }
4474
4475 #[test]
4476 fn a_word_keeps_both_its_canonical_and_raw_spelling() {
4477 assert_eq!(
4480 kinds("Select"),
4481 vec![Tok::Word { upper: "SELECT".into(), raw: "Select".into() }]
4482 );
4483 }
4484
4485 #[test]
4486 fn a_quoted_identifier_is_never_a_keyword() {
4487 assert_eq!(kinds(r#""select""#), vec![Tok::Quoted("select".into())]);
4488 assert_eq!(kinds(r#""Name""#), vec![Tok::Quoted("Name".into())]);
4490 }
4491
4492 #[test]
4493 fn a_doubled_quote_is_one_literal_quote() {
4494 assert_eq!(kinds("'it''s'"), vec![Tok::Str("it's".into())]);
4495 assert_eq!(kinds(r#""a""b""#), vec![Tok::Quoted("a\"b".into())]);
4496 }
4497
4498 #[test]
4499 fn an_E_string_decodes_the_escapes_catalogue_sql_uses() {
4500 assert_eq!(kinds(r"E'\n'"), vec![Tok::Str("\n".into())]);
4502 assert_eq!(kinds(r"E'a\tb'"), vec![Tok::Str("a\tb".into())]);
4503 assert_eq!(kinds(r"E'\q'"), vec![Tok::Str("q".into())]);
4505 }
4506
4507 #[test]
4508 fn operators_match_longest_first() {
4509 assert_eq!(kinds("!~*"), vec![Tok::Op("!~*".into())]);
4511 assert_eq!(kinds("!~"), vec![Tok::Op("!~".into())]);
4512 assert_eq!(kinds("~*"), vec![Tok::Op("~*".into())]);
4513 assert_eq!(kinds("<>"), vec![Tok::Op("<>".into())]);
4514 assert_eq!(kinds("!="), vec![Tok::Op("!=".into())]);
4515 assert_eq!(kinds(">="), vec![Tok::Op(">=".into())]);
4516 assert_eq!(kinds("::"), vec![Tok::Op("::".into())]);
4517 assert_eq!(kinds("||"), vec![Tok::Op("||".into())]);
4518 assert_eq!(kinds("~"), vec![Tok::Op("~".into())]);
4519 }
4520
4521 #[test]
4522 fn comments_are_skipped_including_nested_block_comments() {
4523 assert_eq!(kinds("1 -- trailing\n"), vec![Tok::Num(1.0)]);
4524 assert_eq!(kinds("1 /* a */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4525 assert_eq!(kinds("1 /* a /* b */ c */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4527 assert!(lex("1 /* unterminated").is_err());
4528 }
4529
4530 #[test]
4531 fn numbers_parse_including_fractions_and_exponents() {
4532 assert_eq!(kinds("42"), vec![Tok::Num(42.0)]);
4533 assert_eq!(kinds("4.5"), vec![Tok::Num(4.5)]);
4534 assert_eq!(kinds(".5"), vec![Tok::Num(0.5)]);
4535 assert_eq!(kinds("1e3"), vec![Tok::Num(1000.0)]);
4536 assert_eq!(kinds("1e-2"), vec![Tok::Num(0.01)]);
4537 assert_eq!(
4539 kinds("1e"),
4540 vec![Tok::Num(1.0), Tok::Word { upper: "E".into(), raw: "e".into() }]
4541 );
4542 }
4543
4544 #[test]
4545 fn an_unterminated_literal_is_an_error_not_a_truncation() {
4546 assert!(lex("'abc").is_err());
4547 assert!(lex(r#""abc"#).is_err());
4548 }
4549
4550 #[test]
4551 fn an_unknown_character_is_REFUSED_rather_than_skipped() {
4552 let e = lex("SELECT 1 @ 2").unwrap_err().to_string();
4555 assert!(e.contains('@'), "{}", e);
4556 }
4557
4558 #[test]
4559 fn the_real_dn_query_lexes() {
4560 let sql = r#"SELECT n.nspname AS "Name",
4561 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
4562 FROM pg_catalog.pg_namespace n
4563 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
4564 ORDER BY 1;"#;
4565 let toks = lex(sql).expect("psql's \\dn must lex");
4566 assert!(toks.contains(&Tok::Quoted("Name".into())));
4567 assert!(toks.contains(&Tok::Op("!~".into())));
4568 assert!(toks.contains(&Tok::Op("<>".into())));
4569 assert!(toks.contains(&Tok::Str("^pg_".into())));
4570 }
4571
4572 #[test]
4573 fn the_real_dt_query_lexes() {
4574 let sql = r#"SELECT n.nspname as "Schema", c.relname as "Name",
4575 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' END as "Type",
4576 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
4577 FROM pg_catalog.pg_class c
4578 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
4579 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
4580 WHERE c.relkind IN ('r','p','')
4581 AND n.nspname <> 'pg_catalog'
4582 AND n.nspname !~ '^pg_toast'
4583 AND pg_catalog.pg_table_is_visible(c.oid)
4584 ORDER BY 1,2;"#;
4585 let toks = lex(sql).expect("psql's \\dt must lex");
4586 assert!(toks.iter().any(|t| t.is_kw("CASE")));
4587 assert!(toks.iter().any(|t| t.is_kw("LEFT")));
4588 assert!(toks.iter().any(|t| t.is_kw("JOIN")));
4589 assert!(toks.contains(&Tok::Str(String::new())));
4591 }
4592}
4593
4594#[cfg(test)]
4595mod parser_tests {
4596 use super::*;
4597 use serde_json::json;
4598
4599 fn col(qual: Option<&str>, name: &str) -> Expr {
4600 Expr::Column { qual: qual.map(str::to_string), name: name.to_string() }
4601 }
4602
4603 #[test]
4604 fn a_bare_select_list_and_from() {
4605 let s = parse("SELECT a, b FROM t").unwrap();
4606 assert_eq!(s.items.len(), 2);
4607 assert_eq!(s.items[0].expr, col(None, "a"));
4608 assert_eq!(s.from.unwrap().name, "t");
4609 }
4610
4611 #[test]
4615 fn as_of_system_time_is_read_per_table() {
4616 let s = parse("SELECT total FROM orders AS OF SYSTEM TIME 42").unwrap();
4617 assert_eq!(s.from.clone().unwrap().as_of, Some(42));
4618 assert_eq!(s.from.unwrap().alias, None);
4619
4620 let s = parse("SELECT o.total FROM orders AS OF SYSTEM TIME 42 o").unwrap();
4622 let t = s.from.unwrap();
4623 assert_eq!((t.as_of, t.alias.as_deref()), (Some(42), Some("o")));
4624
4625 let t = parse("SELECT x FROM orders AS o").unwrap().from.unwrap();
4630 assert_eq!((t.as_of, t.alias.as_deref()), (None, Some("o")));
4631
4632 let s = parse(
4635 "SELECT o.total, n.total FROM orders AS OF SYSTEM TIME 1 o \
4636 JOIN orders n ON o._id = n._id").unwrap();
4637 assert_eq!(s.from.unwrap().as_of, Some(1));
4638 assert_eq!(s.joins[0].table.as_of, None);
4639
4640 let s = parse("SELECT total FROM orders AS OF SYSTEM TIME 7 WHERE _id = '1'").unwrap();
4642 assert_eq!(s.from.unwrap().as_of, Some(7));
4643 assert!(s.where_.is_some());
4644 }
4645
4646 #[test]
4651 fn nqls_verbs_are_table_qualifiers_in_sql() {
4652 let t = parse("SELECT _id FROM orders SEARCH 'acme'").unwrap().from.unwrap();
4653 assert_eq!(t.search.as_deref(), Some("acme"));
4654
4655 let t = parse("SELECT _id FROM orders VALID AS OF '2026-01-01'").unwrap().from.unwrap();
4656 assert_eq!(t.valid_as_of.as_deref(), Some("2026-01-01"));
4657
4658 let t = parse(
4660 "SELECT o._id FROM orders AS OF SYSTEM TIME 9 VALID AS OF '2026-01-01' \
4661 SEARCH 'acme' o").unwrap().from.unwrap();
4662 assert_eq!(
4663 (t.as_of, t.valid_as_of.as_deref(), t.search.as_deref(), t.alias.as_deref()),
4664 (Some(9), Some("2026-01-01"), Some("acme"), Some("o")));
4665
4666 let s = parse(
4668 "SELECT o._id FROM orders SEARCH 'acme' o JOIN drivers d ON o.driver = d._id")
4669 .unwrap();
4670 assert_eq!(s.from.unwrap().search.as_deref(), Some("acme"));
4671 assert_eq!(s.joins[0].table.search, None);
4672 }
4673
4674 #[test]
4682 fn the_new_verbs_do_not_steal_names_that_already_worked() {
4683 let t = parse("SELECT search.total FROM orders search").unwrap().from.unwrap();
4684 assert_eq!((t.alias.as_deref(), t.search.as_deref()), (Some("search"), None));
4685
4686 let t = parse("SELECT valid.total FROM orders valid").unwrap().from.unwrap();
4687 assert_eq!((t.alias.as_deref(), t.valid_as_of.as_deref()), (Some("valid"), None));
4688
4689 assert!(parse("SELECT search, valid FROM orders").is_ok());
4691 assert!(parse("SELECT _id FROM orders WHERE search = 'x'").is_ok());
4692
4693 let t = parse("SELECT x FROM orders AS valid").unwrap().from.unwrap();
4695 assert_eq!(t.alias.as_deref(), Some("valid"));
4696 }
4697
4698 #[test]
4699 fn a_verbs_argument_is_refused_when_it_is_the_wrong_kind_of_thing() {
4700 let e = parse("SELECT _id FROM orders VALID AS OF 42").unwrap_err().to_string();
4704 assert!(e.contains("date string"), "{}", e);
4705 }
4706
4707 #[test]
4708 fn a_wall_clock_as_of_is_refused_with_the_reason() {
4709 for sql in [
4713 "SELECT total FROM orders AS OF SYSTEM TIME '2026-01-01'",
4714 "SELECT total FROM orders AS OF SYSTEM TIME now()",
4715 "SELECT total FROM orders AS OF SYSTEM TIME -1",
4716 "SELECT total FROM orders AS OF SYSTEM TIME 1.5",
4717 ] {
4718 let e = parse(sql).unwrap_err().to_string();
4719 assert!(e.contains("sequence number"), "{} -> {}", sql, e);
4720 }
4721 }
4722
4723 #[test]
4724 fn a_clause_keyword_is_never_read_as_a_bare_alias() {
4725 let s = parse("SELECT a FROM t WHERE a = 1").unwrap();
4729 assert_eq!(s.from.clone().unwrap().alias, None);
4730 assert!(s.where_.is_some(), "the WHERE clause must survive");
4731 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4732 assert_eq!(s.from.unwrap().alias, None);
4733 assert_eq!(s.order_by.len(), 1);
4734 }
4735
4736 #[test]
4737 fn a_real_alias_is_kept_in_both_spellings() {
4738 assert_eq!(parse("SELECT a FROM t x").unwrap().from.unwrap().alias,
4739 Some("x".to_string()));
4740 assert_eq!(parse("SELECT a FROM t AS x").unwrap().from.unwrap().alias,
4741 Some("x".to_string()));
4742 }
4743
4744 #[test]
4745 fn a_tables_binding_is_its_alias_else_its_bare_name() {
4746 let t = TableRef::named("pg_catalog.pg_class", Some("c".into()));
4747 assert_eq!(t.binding(), "c");
4748 let t = TableRef::named("pg_catalog.pg_class", None);
4749 assert_eq!(t.binding(), "pg_class", "the schema is not how a column is addressed");
4750 }
4751
4752 #[test]
4753 fn a_qualified_column_keeps_only_its_immediate_qualifier() {
4754 assert_eq!(parse("SELECT n.nspname FROM x").unwrap().items[0].expr,
4755 col(Some("n"), "nspname"));
4756 assert_eq!(parse("SELECT public.orders.id FROM x").unwrap().items[0].expr,
4759 col(Some("orders"), "id"));
4760 }
4761
4762 #[test]
4763 fn an_alias_may_be_a_quoted_string_with_significant_case() {
4764 let s = parse(r#"SELECT n.nspname AS "Name" FROM x"#).unwrap();
4765 assert_eq!(s.items[0].alias, Some("Name".to_string()));
4766 }
4767
4768 #[test]
4769 fn a_schema_qualified_function_drops_its_schema() {
4770 let s = parse("SELECT pg_catalog.pg_get_userbyid(n.nspowner) FROM x").unwrap();
4773 match &s.items[0].expr {
4774 Expr::Func { name, args } => {
4775 assert_eq!(name, "pg_get_userbyid");
4776 assert_eq!(args.len(), 1);
4777 assert_eq!(args[0], col(Some("n"), "nspowner"));
4778 }
4779 other => panic!("{:?}", other),
4780 }
4781 }
4782
4783 #[test]
4784 fn operator_precedence_matches_sql() {
4785 let s = parse("SELECT 1 FROM t WHERE a = 1 OR b = 2 AND c = 3").unwrap();
4788 match s.where_.unwrap() {
4789 Expr::Binary { op, right, .. } => {
4790 assert_eq!(op, "OR");
4791 assert!(matches!(*right, Expr::Binary { ref op, .. } if op == "AND"),
4792 "AND must bind tighter than OR");
4793 }
4794 other => panic!("{:?}", other),
4795 }
4796 let s = parse("SELECT 1 FROM t WHERE a = 1 AND b = 2").unwrap();
4798 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4799 let s = parse("SELECT 1 + 2 * 3 FROM t").unwrap();
4801 match &s.items[0].expr {
4802 Expr::Binary { op, right, .. } => {
4803 assert_eq!(op, "+");
4804 assert!(matches!(**right, Expr::Binary { ref op, .. } if op == "*"));
4805 }
4806 other => panic!("{:?}", other),
4807 }
4808 }
4809
4810 #[test]
4811 fn parentheses_override_precedence() {
4812 let s = parse("SELECT 1 FROM t WHERE (a = 1 OR b = 2) AND c = 3").unwrap();
4813 match s.where_.unwrap() {
4814 Expr::Binary { op, left, .. } => {
4815 assert_eq!(op, "AND");
4816 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "OR"));
4817 }
4818 other => panic!("{:?}", other),
4819 }
4820 }
4821
4822 #[test]
4823 fn in_and_is_null_and_between_parse_in_both_polarities() {
4824 let s = parse("SELECT 1 FROM t WHERE k IN ('r','p','')").unwrap();
4825 match s.where_.unwrap() {
4826 Expr::InList { list, negated, .. } => {
4827 assert_eq!(list.len(), 3);
4828 assert!(!negated);
4829 assert_eq!(list[2], Expr::Literal(json!("")));
4831 }
4832 other => panic!("{:?}", other),
4833 }
4834 assert!(matches!(parse("SELECT 1 FROM t WHERE k NOT IN (1)").unwrap().where_.unwrap(),
4835 Expr::InList { negated: true, .. }));
4836 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NULL").unwrap().where_.unwrap(),
4837 Expr::IsNull { negated: false, .. }));
4838 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NOT NULL").unwrap().where_.unwrap(),
4839 Expr::IsNull { negated: true, .. }));
4840 let s = parse("SELECT 1 FROM t WHERE n BETWEEN 1 AND 5").unwrap();
4842 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4843 }
4844
4845 #[test]
4846 fn both_case_spellings_parse() {
4847 let s = parse("SELECT CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
4849 ELSE 'other' END FROM t").unwrap();
4850 match &s.items[0].expr {
4851 Expr::Case { operand, whens, else_ } => {
4852 assert!(operand.is_some());
4853 assert_eq!(whens.len(), 2);
4854 assert!(else_.is_some());
4855 }
4856 other => panic!("{:?}", other),
4857 }
4858 let s = parse("SELECT CASE WHEN k = 'r' THEN 1 END FROM t").unwrap();
4860 match &s.items[0].expr {
4861 Expr::Case { operand, whens, else_ } => {
4862 assert!(operand.is_none());
4863 assert_eq!(whens.len(), 1);
4864 assert!(else_.is_none());
4865 }
4866 other => panic!("{:?}", other),
4867 }
4868 assert!(parse("SELECT CASE k END FROM t").is_err());
4870 }
4871
4872 #[test]
4873 fn every_join_flavour_parses_and_an_inner_join_demands_ON() {
4874 for (sql, kind) in [
4875 ("SELECT 1 FROM a JOIN b ON a.x = b.x", JoinKind::Inner),
4876 ("SELECT 1 FROM a INNER JOIN b ON a.x = b.x", JoinKind::Inner),
4877 ("SELECT 1 FROM a LEFT JOIN b ON a.x = b.x", JoinKind::Left),
4878 ("SELECT 1 FROM a LEFT OUTER JOIN b ON a.x = b.x", JoinKind::Left),
4879 ("SELECT 1 FROM a RIGHT JOIN b ON a.x = b.x", JoinKind::Right),
4880 ("SELECT 1 FROM a FULL OUTER JOIN b ON a.x = b.x", JoinKind::Full),
4881 ("SELECT 1 FROM a CROSS JOIN b", JoinKind::Cross),
4882 ] {
4883 let s = parse(sql).unwrap_or_else(|e| panic!("{}: {}", sql, e));
4884 assert_eq!(s.joins.len(), 1, "{}", sql);
4885 assert_eq!(s.joins[0].kind, kind, "{}", sql);
4886 }
4887 let s = parse("SELECT 1 FROM a, b").unwrap();
4889 assert_eq!(s.joins[0].kind, JoinKind::Cross);
4890 assert!(parse("SELECT 1 FROM a LEFT JOIN b").is_err());
4893 assert!(parse("SELECT 1 FROM a JOIN b USING (x)").is_err());
4894 }
4895
4896 #[test]
4897 fn order_by_reads_a_number_as_an_ORDINAL() {
4898 let s = parse("SELECT a, b FROM t ORDER BY 1, 2 DESC").unwrap();
4902 assert_eq!(s.order_by.len(), 2);
4903 assert_eq!(s.order_by[0].ordinal, Some(1));
4904 assert_eq!(s.order_by[0].dir, Dir::Asc);
4905 assert_eq!(s.order_by[1].ordinal, Some(2));
4906 assert_eq!(s.order_by[1].dir, Dir::Desc);
4907 let s = parse("SELECT a FROM t ORDER BY lower(a) ASC").unwrap();
4909 assert!(s.order_by[0].ordinal.is_none());
4910 assert!(s.order_by[0].expr.is_some());
4911 }
4912
4913 #[test]
4914 fn null_ordering_defaults_the_way_postgres_defaults() {
4915 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4916 assert!(!s.order_by[0].nulls_first, "ASC defaults to NULLS LAST");
4917 let s = parse("SELECT a FROM t ORDER BY a DESC").unwrap();
4918 assert!(s.order_by[0].nulls_first, "DESC defaults to NULLS FIRST");
4919 let s = parse("SELECT a FROM t ORDER BY a NULLS FIRST").unwrap();
4920 assert!(s.order_by[0].nulls_first, "an explicit clause wins");
4921 }
4922
4923 #[test]
4924 fn limit_and_offset_parse_in_either_order() {
4925 let s = parse("SELECT a FROM t LIMIT 5 OFFSET 2").unwrap();
4926 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
4927 let s = parse("SELECT a FROM t OFFSET 2 LIMIT 5").unwrap();
4928 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
4929 let s = parse("SELECT a FROM t LIMIT ALL").unwrap();
4930 assert_eq!(s.limit, None);
4931 }
4932
4933 #[test]
4934 fn casts_parse_and_are_recorded_rather_than_rejected() {
4935 let s = parse("SELECT x::int2 FROM t").unwrap();
4937 assert!(matches!(s.items[0].expr, Expr::Cast { .. }));
4938 let s = parse("SELECT x::pg_catalog.int2[] FROM t").unwrap();
4939 match &s.items[0].expr {
4940 Expr::Cast { ty, .. } => assert_eq!(ty, "int2[]"),
4941 other => panic!("{:?}", other),
4942 }
4943 }
4944
4945 #[test]
4946 fn star_and_qualified_star_parse() {
4947 assert_eq!(parse("SELECT * FROM t").unwrap().items[0].expr, Expr::Star);
4948 assert_eq!(parse("SELECT c.* FROM t c").unwrap().items[0].expr,
4949 Expr::QualifiedStar("c".into()));
4950 match &parse("SELECT count(*) FROM t").unwrap().items[0].expr {
4953 Expr::Agg { name, args, order_by, distinct } => {
4954 assert_eq!(name, "count");
4955 assert_eq!(args, &vec![Expr::Star]);
4956 assert!(order_by.is_empty());
4957 assert!(!distinct);
4958 }
4959 other => panic!("{:?}", other),
4960 }
4961 assert!(matches!(
4963 &parse("SELECT lower(s) FROM t").unwrap().items[0].expr,
4964 Expr::Func { name, .. } if name == "lower"
4965 ));
4966 }
4967
4968 #[test]
4969 fn an_aggregate_carries_its_own_DISTINCT_and_ORDER_BY() {
4970 match &parse("SELECT array_agg(a.attname ORDER BY a.ord) FROM t a").unwrap().items[0].expr {
4974 Expr::Agg { name, args, order_by, distinct } => {
4975 assert_eq!(name, "array_agg");
4976 assert_eq!(args.len(), 1);
4977 assert_eq!(order_by.len(), 1);
4978 assert!(matches!(order_by[0].dir, Dir::Asc));
4979 assert!(!distinct);
4980 }
4981 other => panic!("{:?}", other),
4982 }
4983 match &parse("SELECT string_agg(DISTINCT s, ',' ORDER BY b DESC NULLS LAST, c) FROM t").unwrap().items[0].expr {
4986 Expr::Agg { name, args, order_by, distinct } => {
4987 assert_eq!(name, "string_agg");
4988 assert_eq!(args.len(), 2, "the separator is an argument, not a sort key");
4989 assert_eq!(order_by.len(), 2);
4990 assert!(matches!(order_by[0].dir, Dir::Desc));
4991 assert!(!order_by[0].nulls_first, "NULLS LAST overrides the DESC default");
4992 assert!(matches!(order_by[1].dir, Dir::Asc));
4993 assert!(distinct);
4994 }
4995 other => panic!("{:?}", other),
4996 }
4997 assert!(parse("SELECT lower(DISTINCT s) FROM t").is_err());
5000 }
5001
5002 #[test]
5003 fn a_parenthesis_free_function_parses_as_a_zero_arg_call() {
5004 match &parse("SELECT current_schema FROM t").unwrap().items[0].expr {
5006 Expr::Func { name, args } => {
5007 assert_eq!(name, "current_schema");
5008 assert!(args.is_empty());
5009 }
5010 other => panic!("{:?}", other),
5011 }
5012 }
5013
5014 #[test]
5015 fn GROUP_BY_and_HAVING_parse_and_what_remains_is_refused_by_name() {
5016 let s = parse("SELECT a, count(*) FROM t GROUP BY a").unwrap();
5019 assert_eq!(s.group_by, vec![Expr::Column { qual: None, name: "a".into() }]);
5020 assert!(s.having.is_none());
5021
5022 let s = parse("SELECT a, b, count(*) FROM t GROUP BY a, b HAVING count(*) > 1").unwrap();
5023 assert_eq!(s.group_by.len(), 2);
5024 assert!(s.having.is_some());
5025
5026 let e = parse("SELECT a FROM t HAVING a > 1").unwrap_err().to_string();
5030 assert!(e.contains("HAVING needs a GROUP BY"), "{}", e);
5031
5032 for (sql, needle) in [
5033 ("SELECT DISTINCT ON (a) a FROM t", "DISTINCT ON"),
5034 ("SELECT a, count(*) FROM t GROUP BY ROLLUP (a)", "ROLLUP"),
5035 ("SELECT a, count(*) FROM t GROUP BY CUBE (a)", "CUBE"),
5036 ] {
5037 let e = parse(sql).unwrap_err().to_string();
5038 assert!(e.contains(needle), "{} -> {}", sql, e);
5039 }
5040 assert!(parse("SELECT a FROM t JUNK JUNK2").is_err());
5042 }
5043
5044 #[test]
5045 fn THE_dn_QUERY_parses_completely() {
5046 let s = parse(
5047 r#"SELECT n.nspname AS "Name",
5048 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5049 FROM pg_catalog.pg_namespace n
5050 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5051 ORDER BY 1;"#,
5052 )
5053 .expect("psql's \\dn must parse");
5054
5055 assert_eq!(s.items.len(), 2);
5056 assert_eq!(s.items[0].alias, Some("Name".into()));
5057 assert_eq!(s.items[1].alias, Some("Owner".into()));
5058 let from = s.from.unwrap();
5059 assert_eq!(from.name, "pg_catalog.pg_namespace");
5060 assert_eq!(from.binding(), "n");
5061 assert!(s.where_.is_some());
5062 assert_eq!(s.order_by[0].ordinal, Some(1));
5063 }
5064
5065 #[test]
5066 fn THE_dt_QUERY_parses_completely() {
5067 let s = parse(
5068 r#"SELECT n.nspname as "Schema",
5069 c.relname as "Name",
5070 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5071 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5072 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5073 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5074 WHEN 'I' THEN 'partitioned index' END as "Type",
5075 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5076 FROM pg_catalog.pg_class c
5077 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5078 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5079 WHERE c.relkind IN ('r','p','')
5080 AND n.nspname <> 'pg_catalog'
5081 AND n.nspname !~ '^pg_toast'
5082 AND n.nspname <> 'information_schema'
5083 AND pg_catalog.pg_table_is_visible(c.oid)
5084 ORDER BY 1,2;"#,
5085 )
5086 .expect("psql's \\dt must parse");
5087
5088 assert_eq!(s.items.len(), 4);
5089 assert_eq!(s.items[2].alias, Some("Type".into()));
5090 match &s.items[2].expr {
5091 Expr::Case { whens, .. } => assert_eq!(whens.len(), 9, "all nine branches"),
5092 other => panic!("{:?}", other),
5093 }
5094 assert_eq!(s.joins.len(), 2);
5095 assert!(s.joins.iter().all(|j| j.kind == JoinKind::Left && j.on.is_some()));
5096 assert_eq!(s.from.unwrap().binding(), "c");
5097 assert_eq!(s.order_by.len(), 2);
5098 assert_eq!(
5099 (s.order_by[0].ordinal, s.order_by[1].ordinal),
5100 (Some(1), Some(2))
5101 );
5102 }
5103}
5104
5105#[cfg(test)]
5106mod eval_tests {
5107 use super::*;
5108 use serde_json::json;
5109
5110 fn one(row: &Value) -> Bound<'_> {
5112 Bound::new(vec![("t".to_string(), Some(row))])
5113 }
5114
5115 fn ev(sql_expr: &str, row: &Value) -> Result<Value> {
5116 let s = parse(&format!("SELECT {} FROM t", sql_expr))?;
5117 eval(&s.items[0].expr, &one(row))
5118 }
5119
5120 fn v(sql_expr: &str, row: &Value) -> Value {
5121 ev(sql_expr, row).unwrap_or_else(|e| panic!("{}: {}", sql_expr, e))
5122 }
5123
5124 #[test]
5125 fn literals_and_columns_resolve() {
5126 let r = json!({"a": 1, "s": "x", "b": true, "n": null});
5127 assert_eq!(v("42", &r), json!(42));
5128 assert_eq!(v("'hi'", &r), json!("hi"));
5129 assert_eq!(v("NULL", &r), Value::Null);
5130 assert_eq!(v("TRUE", &r), json!(true));
5131 assert_eq!(v("a", &r), json!(1));
5132 assert_eq!(v("t.a", &r), json!(1));
5133 assert_eq!(v("s", &r), json!("x"));
5134 assert_eq!(v("nosuch", &r), Value::Null);
5137 }
5138
5139 #[test]
5140 fn an_unknown_table_ALIAS_is_an_error_while_an_unknown_column_is_null() {
5141 let r = json!({"a": 1});
5144 assert_eq!(v("t.nosuch", &r), Value::Null);
5145 let e = ev("zz.a", &r).unwrap_err().to_string();
5146 assert!(e.contains("zz"), "{}", e);
5147 }
5148
5149 #[test]
5152 fn every_comparison_over_NULL_is_UNKNOWN_including_null_equals_null() {
5153 let r = json!({"n": null, "a": 1});
5154 assert_eq!(v("n = 1", &r), Value::Null);
5155 assert_eq!(v("n != 1", &r), Value::Null);
5156 assert_eq!(v("n < 1", &r), Value::Null);
5157 assert_eq!(v("n = n", &r), Value::Null);
5159 assert_eq!(v("n = NULL", &r), Value::Null);
5160 }
5161
5162 #[test]
5163 fn NOT_UNKNOWN_is_UNKNOWN_not_true() {
5164 let r = json!({"n": null});
5168 assert_eq!(v("NOT (n = 1)", &r), Value::Null);
5169 assert_eq!(v("NOT TRUE", &r), json!(false));
5170 assert_eq!(v("NOT FALSE", &r), json!(true));
5171 }
5172
5173 #[test]
5174 fn AND_and_OR_follow_the_three_valued_truth_tables() {
5175 let r = json!({"n": null});
5176 assert_eq!(v("FALSE AND n = 1", &r), json!(false));
5178 assert_eq!(v("TRUE AND n = 1", &r), Value::Null);
5180 assert_eq!(v("TRUE OR n = 1", &r), json!(true));
5182 assert_eq!(v("FALSE OR n = 1", &r), Value::Null);
5184 assert_eq!(v("TRUE AND TRUE", &r), json!(true));
5186 assert_eq!(v("TRUE AND FALSE", &r), json!(false));
5187 assert_eq!(v("FALSE OR FALSE", &r), json!(false));
5188 }
5189
5190 #[test]
5191 fn IS_NULL_is_the_one_predicate_that_is_never_unknown() {
5192 let r = json!({"n": null, "a": 1});
5193 assert_eq!(v("n IS NULL", &r), json!(true));
5194 assert_eq!(v("n IS NOT NULL", &r), json!(false));
5195 assert_eq!(v("a IS NULL", &r), json!(false));
5196 assert_eq!(v("a IS NOT NULL", &r), json!(true));
5197 assert_eq!(v("nosuch IS NULL", &r), json!(true));
5200 }
5201
5202 #[test]
5203 fn NOT_IN_with_a_NULL_in_the_list_is_UNKNOWN_the_classic_trap() {
5204 let r = json!({"a": 2});
5205 assert_eq!(v("a IN (1, 2)", &r), json!(true));
5206 assert_eq!(v("a IN (1, 3)", &r), json!(false));
5207 assert_eq!(v("a NOT IN (1, 3)", &r), json!(true));
5208 assert_eq!(v("a NOT IN (1, NULL)", &r), Value::Null);
5211 assert_eq!(v("a IN (2, NULL)", &r), json!(true));
5213 assert_eq!(v("nosuch IN (1)", &r), Value::Null);
5215 }
5216
5217 #[test]
5220 fn comparisons_work_across_numbers_strings_and_booleans() {
5221 let r = json!({"n": 5, "s": "b", "t": true});
5222 assert_eq!(v("n > 3", &r), json!(true));
5223 assert_eq!(v("n <= 5", &r), json!(true));
5224 assert_eq!(v("s < 'c'", &r), json!(true));
5225 assert_eq!(v("s > 'c'", &r), json!(false));
5226 assert_eq!(v("n = '5'", &r), json!(true));
5229 assert_eq!(v("n = '5.0'", &r), json!(true));
5230 assert_eq!(v("n = 'five'", &r), json!(false));
5233 }
5234
5235 #[test]
5236 fn the_regex_operators_use_the_SAME_matcher_as_NQL() {
5237 let r = json!({"s": "pg_catalog"});
5240 assert_eq!(v("s ~ '^pg_'", &r), json!(true));
5241 assert_eq!(v("s !~ '^pg_'", &r), json!(false));
5242 assert_eq!(v("s ~ '^PG_'", &r), json!(false));
5243 assert_eq!(v("s ~* '^PG_'", &r), json!(true));
5244 assert_eq!(v("s !~ '^zz'", &r), json!(true));
5245 assert_eq!(v("nosuch ~ '^x'", &r), Value::Null);
5247 assert_eq!(v("s ~ '^(pg_catalog)$'", &r), json!(true));
5250 assert_eq!(v("s ~ '^(pg_.*)$'", &r), json!(true));
5251 assert_eq!(v("s ~ '^(public|pg_catalog)$'", &r), json!(true));
5252 assert_eq!(v("s ~ '^pg_[a-z]+$'", &r), json!(true));
5253 assert_eq!(v("s ~ '^pg_[0-9]+$'", &r), json!(false));
5254 let e = ev("s ~ 'a{2}'", &r).unwrap_err().to_string();
5256 assert!(e.contains("interval"), "{}", e);
5257 }
5258
5259 #[test]
5260 fn like_works_in_all_four_spellings() {
5261 let r = json!({"s": "Acme Pool"});
5262 assert_eq!(v("s LIKE 'Acme%'", &r), json!(true));
5263 assert_eq!(v("s LIKE 'acme%'", &r), json!(false));
5264 assert_eq!(v("s ILIKE 'acme%'", &r), json!(true));
5265 assert_eq!(v("s NOT LIKE 'zz%'", &r), json!(true));
5266 assert_eq!(v("nosuch LIKE 'x'", &r), Value::Null);
5267 }
5268
5269 #[test]
5270 fn arithmetic_and_concatenation_propagate_null_and_refuse_div_by_zero() {
5271 let r = json!({"a": 7, "b": 2});
5272 assert_eq!(v("a + b", &r), json!(9));
5273 assert_eq!(v("a - b", &r), json!(5));
5274 assert_eq!(v("a * b", &r), json!(14));
5275 assert_eq!(v("a / b", &r), json!(3.5));
5276 assert_eq!(v("a % b", &r), json!(1));
5277 assert_eq!(v("-a", &r), json!(-7));
5278 assert_eq!(v("a / b", &r), json!(3.5));
5281 assert_eq!(v("b / a", &r), json!(2.0 / 7.0));
5282 assert_eq!(v("'x' || 'y'", &r), json!("xy"));
5283 assert_eq!(v("'x' || nosuch", &r), Value::Null);
5284 assert_eq!(v("a + nosuch", &r), Value::Null);
5285 assert!(ev("a / 0", &r).is_err());
5288 assert!(ev("a % 0", &r).is_err());
5289 }
5290
5291 #[test]
5292 fn a_cast_is_transparent_rather_than_rejected() {
5293 let r = json!({"a": 7});
5296 assert_eq!(v("a::int2", &r), json!(7));
5297 assert_eq!(v("a::pg_catalog.int2[]", &r), json!(7));
5298 }
5299
5300 #[test]
5303 fn a_simple_CASE_picks_the_matching_branch() {
5304 let expr = "CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
5306 WHEN 'i' THEN 'index' END";
5307 assert_eq!(v(expr, &json!({"k": "r"})), json!("table"));
5308 assert_eq!(v(expr, &json!({"k": "v"})), json!("view"));
5309 assert_eq!(v(expr, &json!({"k": "i"})), json!("index"));
5310 assert_eq!(v(expr, &json!({"k": "z"})), Value::Null);
5313 }
5314
5315 #[test]
5316 fn a_searched_CASE_evaluates_predicates_and_UNKNOWN_does_not_match() {
5317 let expr = "CASE WHEN n > 5 THEN 'big' WHEN n > 0 THEN 'small' ELSE 'none' END";
5318 assert_eq!(v(expr, &json!({"n": 9})), json!("big"));
5319 assert_eq!(v(expr, &json!({"n": 2})), json!("small"));
5320 assert_eq!(v(expr, &json!({"n": -1})), json!("none"));
5321 assert_eq!(v(expr, &json!({"other": 1})), json!("none"));
5323 }
5324
5325 #[test]
5326 fn an_ELSE_branch_is_used_when_nothing_matches() {
5327 assert_eq!(
5328 v("CASE k WHEN 'r' THEN 'table' ELSE 'other' END", &json!({"k": "z"})),
5329 json!("other")
5330 );
5331 }
5332
5333 #[test]
5336 fn the_catalogue_functions_psql_calls_all_answer() {
5337 let r = json!({"o": 10, "enc": 6});
5338 assert_eq!(v("pg_get_userbyid(o)", &r), json!("nedb"));
5340 assert_eq!(v("pg_catalog.pg_get_userbyid(o)", &r), json!("nedb"));
5341 assert_eq!(v("pg_table_is_visible(o)", &r), json!(true));
5343 assert_eq!(v("pg_encoding_to_char(enc)", &r), json!("UTF8"));
5344 assert_eq!(v("current_schema", &r), json!("public"));
5345 assert_eq!(v("current_database()", &r), json!("nedb"));
5346 assert_eq!(v("current_user", &r), json!("nedb"));
5347 assert_eq!(v("pg_get_expr(o, o)", &r), Value::Null);
5350 assert_eq!(v("obj_description(o)", &r), Value::Null);
5351 }
5352
5353 #[test]
5354 fn text_and_null_handling_functions_work() {
5355 let r = json!({"s": "AbC", "n": null});
5356 assert_eq!(v("lower(s)", &r), json!("abc"));
5357 assert_eq!(v("upper(s)", &r), json!("ABC"));
5358 assert_eq!(v("length(s)", &r), json!(3));
5359 assert_eq!(v("lower(n)", &r), Value::Null);
5360 assert_eq!(v("coalesce(n, 'fallback')", &r), json!("fallback"));
5361 assert_eq!(v("coalesce(s, 'fallback')", &r), json!("AbC"));
5362 assert_eq!(v("coalesce(n, n)", &r), Value::Null);
5363 assert_eq!(v("nullif(s, 'AbC')", &r), Value::Null);
5364 assert_eq!(v("nullif(s, 'zz')", &r), json!("AbC"));
5365 assert_eq!(v("format_type(20, NULL)", &r), json!("bigint"));
5367 }
5368
5369 #[test]
5370 fn coalesce_does_not_evaluate_past_its_first_non_null() {
5371 let r = json!({"a": 1});
5373 assert_eq!(v("coalesce(a, a / 0)", &r), json!(1));
5374 }
5375
5376 #[test]
5377 fn an_unknown_function_is_REFUSED_rather_than_answered_with_NULL() {
5378 let e = ev("pg_stat_get_numscans(1)", &json!({})).unwrap_err().to_string();
5381 assert!(e.contains("pg_stat_get_numscans"), "{}", e);
5382 assert!(e.contains("refused"), "{}", e);
5383 }
5384
5385 #[test]
5388 fn a_qualified_column_reads_only_its_OWN_binding() {
5389 let a = json!({"name": "left", "x": 1});
5392 let b = json!({"name": "right", "y": 2});
5393 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), Some(&b))]);
5394 let get = |e: &str| {
5395 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5396 eval(&s.items[0].expr, &row).unwrap()
5397 };
5398 assert_eq!(get("a.name"), json!("left"));
5399 assert_eq!(get("b.name"), json!("right"));
5400 assert_eq!(get("name"), json!("left"));
5402 assert_eq!(get("y"), json!(2), "a bare name still finds a later binding");
5403 }
5404
5405 #[test]
5406 fn an_unmatched_LEFT_JOIN_side_reads_as_NULL_not_as_a_missing_column() {
5407 let a = json!({"x": 1});
5410 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), None)]);
5411 let get = |e: &str| {
5412 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5413 eval(&s.items[0].expr, &row).unwrap()
5414 };
5415 assert_eq!(get("b.anything"), Value::Null);
5416 assert_eq!(get("b.anything IS NULL"), json!(true));
5417 assert_eq!(get("a.x"), json!(1));
5418 }
5419}
5420
5421#[cfg(test)]
5422mod exec_tests {
5423 use super::*;
5424 use serde_json::json;
5425
5426 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5428 let owned: Vec<(String, Vec<Value>)> =
5429 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
5430 move |name: &str| {
5431 let bare = name.rsplit('.').next().unwrap_or(name);
5433 Ok(owned
5434 .iter()
5435 .find(|(n, _)| n == name || n == bare)
5436 .map(|(_, r)| from_vec(r.clone())))
5437 }
5438 }
5439
5440 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
5441 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
5442 (cols.into_iter().map(|c| c.name).collect(), rows)
5443 }
5444
5445 fn col(rows: &[Value], name: &str) -> Vec<Value> {
5446 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
5447 }
5448
5449 #[test]
5452 fn select_columns_where_order_limit_offset() {
5453 let t = tables(vec![(
5454 "t",
5455 vec![json!({"a": 3, "s": "c"}), json!({"a": 1, "s": "a"}), json!({"a": 2, "s": "b"})],
5456 )]);
5457 let (names, rows) = go("SELECT a, s FROM t ORDER BY a", &t);
5458 assert_eq!(names, vec!["a", "s"]);
5459 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2), json!(3)]);
5460
5461 let (_, rows) = go("SELECT a FROM t ORDER BY a DESC", &t);
5462 assert_eq!(col(&rows, "a"), vec![json!(3), json!(2), json!(1)]);
5463
5464 let (_, rows) = go("SELECT a FROM t WHERE a > 1 ORDER BY a", &t);
5465 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5466
5467 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 2", &t);
5468 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5469
5470 let (_, rows) = go("SELECT a FROM t ORDER BY a OFFSET 1", &t);
5471 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5472
5473 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 1 OFFSET 1", &t);
5474 assert_eq!(col(&rows, "a"), vec![json!(2)]);
5475
5476 let (_, rows) = go("SELECT a FROM t OFFSET 99", &t);
5478 assert!(rows.is_empty());
5479 }
5480
5481 #[test]
5482 fn an_output_column_takes_its_alias_or_a_derived_name() {
5483 let t = tables(vec![("t", vec![json!({"a": 1})])]);
5486 assert_eq!(go(r#"SELECT a AS "Name" FROM t"#, &t).0, vec!["Name"]);
5487 assert_eq!(go("SELECT a FROM t", &t).0, vec!["a"]);
5488 assert_eq!(go("SELECT lower('X') FROM t", &t).0, vec!["lower"]);
5489 assert_eq!(go("SELECT 1 + 1 FROM t", &t).0, vec!["?column?"]);
5490 assert_eq!(go("SELECT CASE a WHEN 1 THEN 'x' END FROM t", &t).0, vec!["case"]);
5491 }
5492
5493 #[test]
5494 fn star_expands_from_the_rows_and_a_qualified_star_from_one_binding() {
5495 let t = tables(vec![
5496 ("a", vec![json!({"x": 1, "y": 2})]),
5497 ("b", vec![json!({"z": 3})]),
5498 ]);
5499 let (names, rows) = go("SELECT * FROM a", &t);
5500 assert_eq!(names, vec!["x", "y"]);
5501 assert_eq!(rows.len(), 1);
5502
5503 let (names, _) = go("SELECT a.* FROM a CROSS JOIN b", &t);
5504 assert_eq!(names, vec!["x", "y"], "a qualified star takes ONE binding");
5505
5506 let empty = tables(vec![("e", vec![])]);
5509 assert_eq!(go("SELECT * FROM e", &empty).0, Vec::<String>::new());
5510 }
5511
5512 #[test]
5513 fn distinct_dedupes_on_the_projected_values() {
5514 let t = tables(vec![(
5515 "t",
5516 vec![json!({"g": "x"}), json!({"g": "x"}), json!({"g": "y"})],
5517 )]);
5518 let (_, rows) = go("SELECT DISTINCT g FROM t ORDER BY 1", &t);
5519 assert_eq!(col(&rows, "g"), vec![json!("x"), json!("y")]);
5520 let (_, rows) = go("SELECT g FROM t", &t);
5521 assert_eq!(rows.len(), 3, "without DISTINCT every row survives");
5522 }
5523
5524 #[test]
5525 fn order_by_an_ORDINAL_sorts_the_projected_column() {
5526 let t = tables(vec![(
5527 "t",
5528 vec![json!({"a": 2, "b": "z"}), json!({"a": 1, "b": "y"})],
5529 )]);
5530 let (_, rows) = go("SELECT a, b FROM t ORDER BY 1", &t);
5531 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5532 let (_, rows) = go("SELECT a, b FROM t ORDER BY 2 DESC", &t);
5533 assert_eq!(col(&rows, "b"), vec![json!("z"), json!("y")]);
5534 let e = run("SELECT a FROM t ORDER BY 3", &t).unwrap_err().to_string();
5536 assert!(e.contains("out of range"), "{}", e);
5537 }
5538
5539 #[test]
5540 fn order_by_an_expression_may_use_a_column_NOT_in_the_select_list() {
5541 let t = tables(vec![(
5542 "t",
5543 vec![json!({"a": 1, "hidden": 9}), json!({"a": 2, "hidden": 1})],
5544 )]);
5545 let (_, rows) = go("SELECT a FROM t ORDER BY hidden", &t);
5546 assert_eq!(col(&rows, "a"), vec![json!(2), json!(1)]);
5547 }
5548
5549 #[test]
5550 fn null_ordering_follows_the_direction_defaults() {
5551 let t = tables(vec![(
5552 "t",
5553 vec![json!({"a": 2}), json!({"a": null}), json!({"a": 1})],
5554 )]);
5555 assert_eq!(col(&go("SELECT a FROM t ORDER BY a", &t).1, "a"),
5557 vec![json!(1), json!(2), Value::Null]);
5558 assert_eq!(col(&go("SELECT a FROM t ORDER BY a DESC", &t).1, "a"),
5560 vec![Value::Null, json!(2), json!(1)]);
5561 assert_eq!(col(&go("SELECT a FROM t ORDER BY a NULLS FIRST", &t).1, "a"),
5563 vec![Value::Null, json!(1), json!(2)]);
5564 }
5565
5566 #[test]
5567 fn a_where_clause_that_is_UNKNOWN_excludes_the_row() {
5568 let t = tables(vec![(
5569 "t",
5570 vec![json!({"a": 1}), json!({"a": null}), json!({"other": 1})],
5571 )]);
5572 let (_, rows) = go("SELECT a FROM t WHERE a = 1", &t);
5574 assert_eq!(rows.len(), 1);
5575 let (_, rows) = go("SELECT a FROM t WHERE NOT (a = 1)", &t);
5577 assert_eq!(rows.len(), 0, "NOT UNKNOWN must not resurrect a null row");
5578 }
5579
5580 #[test]
5581 fn select_with_no_FROM_returns_exactly_one_row() {
5582 let t = tables(vec![]);
5584 let (names, rows) = go("SELECT 1", &t);
5585 assert_eq!(rows.len(), 1);
5586 assert_eq!(names, vec!["?column?"]);
5587 assert_eq!(go("SELECT current_schema", &t).1.len(), 1);
5588 }
5589
5590 #[test]
5591 fn an_unknown_relation_is_NAMED_rather_than_answered_with_no_rows() {
5592 let t = tables(vec![("t", vec![])]);
5595 let e = run("SELECT a FROM nosuchtable", &t).unwrap_err().to_string();
5596 assert!(e.contains("nosuchtable"), "{}", e);
5597 assert!(e.contains("does not exist"), "{}", e);
5598 }
5599
5600 #[test]
5603 fn an_inner_join_keeps_only_matching_pairs() {
5604 let t = tables(vec![
5605 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5606 ("r", vec![json!({"lid": 1, "v": "x"})]),
5607 ]);
5608 let (_, rows) = go("SELECT l.n, r.v FROM l JOIN r ON r.lid = l.id", &t);
5609 assert_eq!(rows.len(), 1);
5610 assert_eq!(col(&rows, "n"), vec![json!("a")]);
5611 }
5612
5613 #[test]
5614 fn a_LEFT_join_keeps_unmatched_left_rows_with_NULLs() {
5615 let t = tables(vec![
5617 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5618 ("r", vec![json!({"lid": 1, "v": "x"})]),
5619 ]);
5620 let (_, rows) = go("SELECT l.n, r.v FROM l LEFT JOIN r ON r.lid = l.id ORDER BY 1", &t);
5621 assert_eq!(rows.len(), 2);
5622 assert_eq!(col(&rows, "n"), vec![json!("a"), json!("b")]);
5623 assert_eq!(col(&rows, "v"), vec![json!("x"), Value::Null]);
5624 }
5625
5626 #[test]
5627 fn a_RIGHT_join_keeps_unmatched_right_rows_and_FULL_keeps_both() {
5628 let t = tables(vec![
5629 ("l", vec![json!({"id": 1})]),
5630 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5631 ]);
5632 let (_, rows) = go("SELECT l.id, r.lid FROM l RIGHT JOIN r ON r.lid = l.id", &t);
5633 assert_eq!(rows.len(), 2);
5634 assert!(col(&rows, "id").contains(&Value::Null), "the unmatched right row keeps NULLs on the left");
5635
5636 let t2 = tables(vec![
5637 ("l", vec![json!({"id": 1}), json!({"id": 5})]),
5638 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5639 ]);
5640 let (_, rows) = go("SELECT l.id, r.lid FROM l FULL OUTER JOIN r ON r.lid = l.id", &t2);
5641 assert_eq!(rows.len(), 3, "one match plus one orphan on each side");
5642 }
5643
5644 #[test]
5645 fn a_cross_join_is_the_cartesian_product() {
5646 let t = tables(vec![
5647 ("a", vec![json!({"x": 1}), json!({"x": 2})]),
5648 ("b", vec![json!({"y": 1}), json!({"y": 2}), json!({"y": 3})]),
5649 ]);
5650 assert_eq!(go("SELECT a.x, b.y FROM a CROSS JOIN b", &t).1.len(), 6);
5651 assert_eq!(go("SELECT a.x, b.y FROM a, b", &t).1.len(), 6);
5653 }
5654
5655 #[test]
5656 fn an_ON_clause_that_is_UNKNOWN_does_not_join() {
5657 let t = tables(vec![
5660 ("l", vec![json!({"id": null})]),
5661 ("r", vec![json!({"lid": null})]),
5662 ]);
5663 let (_, rows) = go("SELECT l.id FROM l JOIN r ON r.lid = l.id", &t);
5664 assert!(rows.is_empty(), "NULL = NULL is UNKNOWN, so nothing joins");
5665 let (_, rows) = go("SELECT l.id FROM l LEFT JOIN r ON r.lid = l.id", &t);
5667 assert_eq!(rows.len(), 1);
5668 }
5669
5670 #[test]
5671 fn two_joins_chain() {
5672 let t = tables(vec![
5673 ("a", vec![json!({"id": 1, "bid": 10, "cid": 100})]),
5674 ("b", vec![json!({"id": 10, "bn": "B"})]),
5675 ("c", vec![json!({"id": 100, "cn": "C"})]),
5676 ]);
5677 let (_, rows) = go(
5678 "SELECT a.id, b.bn, c.cn FROM a \
5679 LEFT JOIN b ON b.id = a.bid \
5680 LEFT JOIN c ON c.id = a.cid",
5681 &t,
5682 );
5683 assert_eq!(rows.len(), 1);
5684 assert_eq!(col(&rows, "bn"), vec![json!("B")]);
5685 assert_eq!(col(&rows, "cn"), vec![json!("C")]);
5686 }
5687
5688 fn catalog() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5692 tables(vec![
5693 (
5694 "pg_namespace",
5695 vec![
5696 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5697 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5698 json!({"oid": 13000, "nspname": "information_schema", "nspowner": 10}),
5699 ],
5700 ),
5701 (
5702 "pg_class",
5703 vec![
5704 json!({"oid": 16401, "relname": "orders", "relnamespace": 2200,
5705 "relkind": "r", "relowner": 10, "relam": 2}),
5706 json!({"oid": 16402, "relname": "drivers", "relnamespace": 2200,
5707 "relkind": "r", "relowner": 10, "relam": 2}),
5708 ],
5709 ),
5710 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5711 ])
5712 }
5713
5714 #[test]
5715 fn THE_dn_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5716 let (names, rows) = go(
5717 r#"SELECT n.nspname AS "Name",
5718 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5719 FROM pg_catalog.pg_namespace n
5720 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5721 ORDER BY 1;"#,
5722 &catalog(),
5723 );
5724
5725 assert_eq!(names, vec!["Name", "Owner"], "psql reads these BY NAME");
5726 assert_eq!(col(&rows, "Name"), vec![json!("public")]);
5729 assert_eq!(col(&rows, "Owner"), vec![json!("nedb")]);
5730 }
5731
5732 #[test]
5733 fn THE_dt_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5734 let (names, rows) = go(
5735 r#"SELECT n.nspname as "Schema",
5736 c.relname as "Name",
5737 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5738 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5739 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5740 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5741 WHEN 'I' THEN 'partitioned index' END as "Type",
5742 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5743 FROM pg_catalog.pg_class c
5744 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5745 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5746 WHERE c.relkind IN ('r','p','')
5747 AND n.nspname <> 'pg_catalog'
5748 AND n.nspname !~ '^pg_toast'
5749 AND n.nspname <> 'information_schema'
5750 AND pg_catalog.pg_table_is_visible(c.oid)
5751 ORDER BY 1,2;"#,
5752 &catalog(),
5753 );
5754
5755 assert_eq!(names, vec!["Schema", "Name", "Type", "Owner"]);
5756 assert_eq!(col(&rows, "Name"), vec![json!("drivers"), json!("orders")]);
5758 assert_eq!(col(&rows, "Schema"), vec![json!("public"), json!("public")]);
5759 assert_eq!(col(&rows, "Type"), vec![json!("table"), json!("table")]);
5761 assert_eq!(col(&rows, "Owner"), vec![json!("nedb"), json!("nedb")]);
5762 }
5763
5764 #[test]
5765 fn the_dt_query_still_filters_correctly_with_a_system_relation_present() {
5766 let t = tables(vec![
5770 (
5771 "pg_namespace",
5772 vec![
5773 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5774 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5775 ],
5776 ),
5777 (
5778 "pg_class",
5779 vec![
5780 json!({"oid": 1, "relname": "mine", "relnamespace": 2200,
5781 "relkind": "r", "relowner": 10, "relam": 2}),
5782 json!({"oid": 2, "relname": "pg_internal", "relnamespace": 11,
5783 "relkind": "r", "relowner": 10, "relam": 2}),
5784 json!({"oid": 3, "relname": "an_index", "relnamespace": 2200,
5785 "relkind": "i", "relowner": 10, "relam": 2}),
5786 ],
5787 ),
5788 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5789 ]);
5790 let (_, rows) = go(
5791 r#"SELECT c.relname as "Name" FROM pg_catalog.pg_class c
5792 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5793 WHERE c.relkind IN ('r','p','') AND n.nspname <> 'pg_catalog'
5794 ORDER BY 1"#,
5795 &t,
5796 );
5797 assert_eq!(col(&rows, "Name"), vec![json!("mine")],
5798 "a system relation and an index must both be filtered out");
5799 }
5800}
5801
5802#[cfg(test)]
5803mod operator_syntax_tests {
5804 use super::*;
5805 use serde_json::json;
5806
5807 #[test]
5808 fn the_OPERATOR_qualification_psql_generates_is_understood() {
5809 let s = parse(
5813 "SELECT a FROM t WHERE n OPERATOR(pg_catalog.~) '^x' \
5814 AND m OPERATOR(pg_catalog.=) 1",
5815 )
5816 .expect("psql's OPERATOR() form must parse");
5817 match s.where_.unwrap() {
5818 Expr::Binary { op, left, .. } => {
5819 assert_eq!(op, "AND");
5820 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "~"));
5821 }
5822 other => panic!("{:?}", other),
5823 }
5824 }
5825
5826 #[test]
5827 fn an_OPERATOR_qualified_comparison_EVALUATES() {
5828 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
5829 Ok(Some(from_vec(vec![json!({"n": "orders"}), json!({"n": "pg_toast_1"})])))
5830 };
5831 let (_, rows) = run(
5832 "SELECT n FROM pg_class WHERE n OPERATOR(pg_catalog.~) '^ord'",
5833 &t,
5834 )
5835 .unwrap();
5836 assert_eq!(rows.len(), 1);
5837 assert_eq!(rows[0]["n"], json!("orders"));
5838 }
5839
5840 #[test]
5841 fn a_subquery_an_ARRAY_constructor_and_EXISTS_all_PARSE() {
5842 let s = parse("SELECT a FROM t WHERE x = (SELECT 1)").unwrap();
5845 assert!(matches!(s.where_, Some(Expr::Binary { ref right, .. }) if matches!(**right, Expr::Subquery(_))));
5846 let s = parse("SELECT array_to_string(ARRAY(SELECT a FROM b), ',') FROM t").unwrap();
5847 assert!(matches!(&s.items[0].expr, Expr::Func { args, .. } if matches!(args[0], Expr::ArrayQuery(_))));
5848 let s = parse("SELECT a FROM t WHERE EXISTS (SELECT 1)").unwrap();
5849 assert!(matches!(s.where_, Some(Expr::Exists { negated: false, .. })));
5850 let s = parse("SELECT a FROM t WHERE NOT EXISTS (SELECT 1)").unwrap();
5851 assert!(matches!(s.where_, Some(Expr::Unary { ref expr, .. }) if matches!(**expr, Expr::Exists { .. })));
5852 let s = parse("SELECT a FROM t WHERE oid = ANY (polroles) AND 'd' = any(kinds) AND x <> ALL (SELECT y FROM u)").unwrap();
5854 assert!(s.where_.is_some());
5855 let s = parse("SELECT prattrs[s] FROM t").unwrap();
5856 assert!(matches!(s.items[0].expr, Expr::Index { .. }));
5857 let s = parse("SELECT CAST('tuple' AS pg_catalog.text), CAST(n AS int2[]) FROM t").unwrap();
5858 assert!(matches!(&s.items[0].expr, Expr::Cast { ty, .. } if ty == "text"));
5859 assert!(matches!(&s.items[1].expr, Expr::Cast { ty, .. } if ty == "int2[]"));
5860 let s = parse("SELECT a FROM t WHERE a IS DISTINCT FROM b").unwrap();
5861 assert!(matches!(s.where_, Some(Expr::Binary { ref op, .. }) if op == "IS DISTINCT FROM"));
5862 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();
5864 assert_eq!(s.joins.len(), 3);
5865 assert!(matches!(s.joins[1].kind, JoinKind::Cross));
5866 let s = parse("SELECT 1 FROM c, LATERAL (SELECT 2 AS two) s").unwrap();
5868 assert!(s.joins[0].table.lateral && s.joins[0].table.sub.is_some());
5869 let s = parse("SELECT tt.a FROM (SELECT 1 AS a UNION ALL SELECT 2) AS tt ORDER BY 1").unwrap();
5870 assert_eq!(s.from.as_ref().unwrap().sub.as_ref().unwrap().set_ops.len(), 1);
5871 }
5872
5873 #[test]
5874 fn a_compound_query_keeps_ORDER_BY_for_the_whole() {
5875 let s = parse("SELECT a FROM t UNION SELECT b FROM u UNION ALL SELECT c FROM v ORDER BY 1 LIMIT 5").unwrap();
5876 assert_eq!(s.set_ops.len(), 2);
5877 assert_eq!(s.set_ops[0].op, SetOp::Union);
5878 assert!(!s.set_ops[0].all);
5879 assert!(s.set_ops[1].all);
5880 assert_eq!(s.order_by.len(), 1);
5881 assert_eq!(s.limit, Some(5));
5882 assert!(s.set_ops[1].query.order_by.is_empty(), "the tail belongs to the whole, not the last arm");
5883 }
5884}
5885
5886#[cfg(test)]
5887mod subquery_exec_tests {
5888 use super::*;
5889 use serde_json::json;
5890
5891 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5892 let owned: Vec<(String, Vec<Value>)> =
5893 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
5894 move |name: &str| {
5895 let bare = name.rsplit('.').next().unwrap_or(name);
5896 Ok(owned.iter().find(|(n, _)| n == name || n == bare).map(|(_, r)| from_vec(r.clone())))
5897 }
5898 }
5899
5900 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
5901 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
5902 (cols.into_iter().map(|c| c.name).collect(), rows)
5903 }
5904
5905 fn col(rows: &[Value], name: &str) -> Vec<Value> {
5906 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
5907 }
5908
5909 fn shop() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5910 tables(vec![
5911 ("c", vec![
5912 json!({"id": 1, "name": "ann", "tags": ["a", "b"]}),
5913 json!({"id": 2, "name": "bob", "tags": []}),
5914 json!({"id": 3, "name": "cyd", "tags": null}),
5915 ]),
5916 ("o", vec![
5917 json!({"oid": 10, "cid": 1, "total": 5}),
5918 json!({"oid": 11, "cid": 1, "total": 7}),
5919 json!({"oid": 12, "cid": 2, "total": 9}),
5920 ]),
5921 ])
5922 }
5923
5924 #[test]
5925 fn a_correlated_scalar_subquery_sees_the_outer_row() {
5926 let t = shop();
5927 let (_, rows) = go(
5928 "SELECT c.name, (SELECT sum(o.total) FROM o WHERE o.cid = c.id) AS spent FROM c ORDER BY c.id",
5929 &t,
5930 );
5931 assert_eq!(col(&rows, "spent"), vec![json!(12), json!(9), Value::Null]);
5932 let e = run("SELECT (SELECT o.total FROM o WHERE o.cid = c.id) FROM c", &t).unwrap_err().to_string();
5934 assert!(e.contains("more than one row"), "{}", e);
5935 let e = run("SELECT (SELECT oid, total FROM o) FROM c", &t).unwrap_err().to_string();
5937 assert!(e.contains("exactly one column"), "{}", e);
5938 }
5939
5940 #[test]
5941 fn EXISTS_and_NOT_EXISTS_are_never_unknown() {
5942 let t = shop();
5943 let (_, rows) = go("SELECT c.name FROM c WHERE EXISTS (SELECT 1 FROM o WHERE o.cid = c.id) ORDER BY 1", &t);
5944 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5945 let (_, rows) = go("SELECT c.name FROM c WHERE NOT EXISTS (SELECT 1 FROM o WHERE o.cid = c.id)", &t);
5946 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5947 }
5948
5949 #[test]
5950 fn ARRAY_of_a_subquery_and_array_to_string_compose_like_psql_dp() {
5951 let t = shop();
5952 let (_, rows) = go(
5953 "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",
5954 &t,
5955 );
5956 assert_eq!(col(&rows, "totals"), vec![json!("5,7"), json!("9"), json!("")]);
5958 let (_, rows) = go("SELECT array_length(ARRAY(SELECT oid FROM o), 1) AS n FROM c WHERE c.id = 1", &t);
5959 assert_eq!(col(&rows, "n"), vec![json!(3)]);
5960 }
5961
5962 #[test]
5963 fn ANY_ALL_and_IN_over_arrays_and_subqueries() {
5964 let t = shop();
5965 let (_, rows) = go("SELECT c.name FROM c WHERE 'a' = ANY (c.tags) ORDER BY 1", &t);
5966 assert_eq!(col(&rows, "name"), vec![json!("ann")]);
5967 let (_, rows) = go("SELECT c.name FROM c WHERE 'zz' = ANY (c.tags)", &t);
5969 assert!(rows.is_empty());
5970 let (_, rows) = go("SELECT c.name FROM c WHERE c.id = ANY (SELECT o.cid FROM o) ORDER BY 1", &t);
5971 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5972 let (_, rows) = go("SELECT c.name FROM c WHERE c.id <> ALL (SELECT o.cid FROM o)", &t);
5973 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5974 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);
5975 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5976 let (_, rows) = go("SELECT c.name FROM c WHERE c.id NOT IN (SELECT o.cid FROM o)", &t);
5977 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5978 let (_, rows) = go("SELECT c.tags[2] AS second FROM c WHERE c.id = 1", &t);
5980 assert_eq!(col(&rows, "second"), vec![json!("b")]);
5981 }
5982
5983 #[test]
5984 fn set_operations_combine_arms_and_sort_the_whole() {
5985 let t = shop();
5986 let (names, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1", &t);
5987 assert_eq!(names, vec!["k"], "column names come from the first arm");
5988 assert_eq!(col(&rows, "k"), vec![json!(1), json!(1), json!(1), json!(2), json!(2), json!(3)]);
5989 let (_, rows) = go("SELECT c.id AS k FROM c UNION SELECT o.cid FROM o ORDER BY 1", &t);
5990 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2), json!(3)]);
5991 let (_, rows) = go("SELECT c.id AS k FROM c INTERSECT SELECT o.cid FROM o ORDER BY 1", &t);
5992 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2)]);
5993 let (_, rows) = go("SELECT c.id AS k FROM c EXCEPT SELECT o.cid FROM o", &t);
5994 assert_eq!(col(&rows, "k"), vec![json!(3)]);
5995 let (_, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1 DESC LIMIT 2", &t);
5996 assert_eq!(col(&rows, "k"), vec![json!(3), json!(2)]);
5997 let e = run("SELECT c.id FROM c UNION SELECT o.oid, o.cid FROM o", &t).unwrap_err().to_string();
5998 assert!(e.contains("same number of columns"), "{}", e);
5999 }
6000
6001 #[test]
6002 fn a_derived_table_is_a_relation_and_LATERAL_sees_its_left() {
6003 let t = shop();
6004 let (_, rows) = go(
6005 "SELECT tt.who FROM (SELECT c.name AS who FROM c WHERE c.id < 3) AS tt ORDER BY 1",
6006 &t,
6007 );
6008 assert_eq!(col(&rows, "who"), vec![json!("ann"), json!("bob")]);
6009 let (_, rows) = go("SELECT tt.x FROM (SELECT c.name FROM c WHERE c.id = 1) AS tt(x)", &t);
6011 assert_eq!(col(&rows, "x"), vec![json!("ann")]);
6012 let (_, rows) = go(
6014 "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\"",
6015 &t,
6016 );
6017 assert_eq!(col(&rows, "Name"), vec![json!("ann"), json!("bob"), json!("cyd")]);
6018 assert_eq!(col(&rows, "Orders"), vec![json!(2), json!(1), json!(0)]);
6019 }
6020
6021 #[test]
6022 fn table_functions_generate_series_and_unnest() {
6023 let t = shop();
6024 let (_, rows) = go("SELECT s.generate_series AS n FROM generate_series(1, 3) s", &t);
6025 assert_eq!(col(&rows, "n"), vec![json!(1), json!(2), json!(3)]);
6026 let (_, rows) = go("SELECT x FROM pg_catalog.unnest(ARRAY['p', 'q']) AS t(x)", &t);
6027 assert_eq!(col(&rows, "x"), vec![json!("p"), json!("q")]);
6028 let (_, rows) = go(
6030 "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",
6031 &t,
6032 );
6033 assert_eq!(col(&rows, "tags"), vec![json!("a, b"), json!(""), json!("")]);
6034 let e = run("SELECT 1 FROM nosuchfn(1) f", &t).unwrap_err().to_string();
6035 assert!(e.contains("table function nosuchfn()"), "{}", e);
6036 }
6037
6038 #[test]
6039 fn aggregates_without_GROUP_BY_collapse_to_one_row() {
6040 let t = shop();
6041 let (names, rows) = go(
6042 "SELECT count(*), count(c.tags) AS tagged, min(c.name), max(c.name) AS hi, string_agg(c.name, '|') AS all FROM c",
6043 &t,
6044 );
6045 assert_eq!(names, vec!["count", "tagged", "min", "hi", "all"]);
6046 assert_eq!(rows.len(), 1);
6047 assert_eq!(rows[0]["count"], json!(3));
6048 assert_eq!(rows[0]["tagged"], json!(2), "count(x) skips NULL");
6049 assert_eq!(rows[0]["min"], json!("ann"));
6050 assert_eq!(rows[0]["hi"], json!("cyd"));
6051 assert_eq!(rows[0]["all"], json!("ann|bob|cyd"));
6052 let (_, rows) = go("SELECT count(*) AS n, sum(o.total) AS s FROM o WHERE o.total > 100", &t);
6054 assert_eq!(rows[0]["n"], json!(0));
6055 assert_eq!(rows[0]["s"], Value::Null);
6056 let (_, rows) = go("SELECT sum(o.total) / count(*) AS avg_total, avg(o.total) AS a FROM o", &t);
6059 assert_eq!(rows[0]["avg_total"], json!(7));
6060 assert_eq!(rows[0]["a"], json!(7));
6061 let e = run("SELECT c.name, count(*) FROM c", &t).unwrap_err().to_string();
6062 assert!(e.contains("must appear in the GROUP BY clause"), "{}", e);
6063 }
6064
6065 #[test]
6066 fn IS_DISTINCT_FROM_is_null_safe() {
6067 let t = shop();
6068 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS DISTINCT FROM NULL ORDER BY 1", &t);
6069 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6070 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS NOT DISTINCT FROM NULL", &t);
6071 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6072 }
6073
6074 #[test]
6075 fn THE_dT_QUERY_RUNS_over_a_catalogue_fixture() {
6076 let t = tables(vec![
6078 ("pg_namespace", vec![
6079 json!({"oid": 11, "nspname": "pg_catalog"}),
6080 json!({"oid": 2200, "nspname": "public"}),
6081 ]),
6082 ("pg_type", vec![
6083 json!({"oid": 25, "typname": "text", "typnamespace": 11, "typrelid": 0, "typelem": 0, "typarray": 1009}),
6084 json!({"oid": 1009, "typname": "_text", "typnamespace": 11, "typrelid": 0, "typelem": 25, "typarray": 0}),
6085 json!({"oid": 70000, "typname": "mood", "typnamespace": 2200, "typrelid": 0, "typelem": 0, "typarray": 70001}),
6086 json!({"oid": 70001, "typname": "_mood", "typnamespace": 2200, "typrelid": 0, "typelem": 70000, "typarray": 0}),
6087 ]),
6088 ("pg_class", vec![]),
6089 ]);
6090 let (_, rows) = go(
6091 r#"SELECT n.nspname as "Schema",
6092 pg_catalog.format_type(t.oid, NULL) AS "Name",
6093 pg_catalog.obj_description(t.oid, 'pg_type') as "Description"
6094 FROM pg_catalog.pg_type t
6095 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
6096 WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
6097 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
6098 AND n.nspname <> 'pg_catalog'
6099 AND n.nspname <> 'information_schema'
6100 AND pg_catalog.pg_type_is_visible(t.oid)
6101 ORDER BY 1, 2;"#,
6102 &t,
6103 );
6104 assert_eq!(rows.len(), 1, "{:?}", rows);
6107 assert_eq!(col(&rows, "Schema"), vec![json!("public")]);
6108 }
6109}
6110
6111#[cfg(test)]
6112mod collate_tests {
6113 use super::*;
6114 use serde_json::json;
6115
6116 #[test]
6117 fn COLLATE_is_consumed_because_it_cannot_change_the_answer() {
6118 for sql in [
6122 r#"SELECT a FROM t ORDER BY a COLLATE "C""#,
6123 r#"SELECT a COLLATE "C" FROM t"#,
6124 r#"SELECT a FROM t WHERE a COLLATE pg_catalog."C" = 'x'"#,
6125 ] {
6126 parse(sql).unwrap_or_else(|e| panic!("{} -> {}", sql, e));
6127 }
6128 assert!(parse("SELECT a FROM t ORDER BY a COLLATE").is_err());
6130 }
6131
6132 #[test]
6133 fn a_COLLATE_annotated_comparison_still_evaluates() {
6134 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
6135 Ok(Some(from_vec(vec![json!({"n": "b"}), json!({"n": "a"})])))
6136 };
6137 let (_, rows) = run(r#"SELECT n FROM pg_class ORDER BY n COLLATE "C""#, &t).unwrap();
6138 assert_eq!(rows[0]["n"], json!("a"), "the ORDER BY still sorts");
6139 }
6140}