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 pub trace: Option<String>,
442 pub trace_reverse: bool,
444 pub traverse: Option<String>,
447}
448
449impl TableRef {
450 pub fn named(name: impl Into<String>, alias: Option<String>) -> Self {
452 TableRef { name: name.into(), alias, sub: None, args: None, col_aliases: vec![], lateral: false, as_of: None, valid_as_of: None, search: None, trace: None, trace_reverse: false, traverse: None }
453 }
454
455 pub fn binding(&self) -> String {
458 self.alias.clone().unwrap_or_else(|| {
459 self.name.rsplit('.').next().unwrap_or(&self.name).to_string()
460 })
461 }
462}
463
464#[derive(Debug, Clone, Copy, PartialEq, Eq)]
466pub enum SetOp { Union, Intersect, Except }
467
468#[derive(Debug, Clone, PartialEq)]
470pub struct SetArm {
471 pub op: SetOp,
472 pub all: bool,
473 pub query: Select,
474}
475
476#[derive(Debug, Clone, PartialEq)]
477pub struct Join {
478 pub kind: JoinKind,
479 pub table: TableRef,
480 pub on: Option<Expr>,
481}
482
483#[derive(Debug, Clone, Copy, PartialEq)]
484pub enum Dir { Asc, Desc }
485
486#[derive(Debug, Clone, PartialEq)]
487pub struct OrderBy {
488 pub ordinal: Option<usize>,
492 pub expr: Option<Expr>,
493 pub dir: Dir,
494 pub nulls_first: bool,
496}
497
498#[derive(Debug, Clone, PartialEq)]
499pub struct Select {
500 pub distinct: bool,
501 pub items: Vec<SelectItem>,
502 pub from: Option<TableRef>,
503 pub joins: Vec<Join>,
504 pub where_: Option<Expr>,
505 pub group_by: Vec<Expr>,
508 pub having: Option<Expr>,
511 pub order_by: Vec<OrderBy>,
515 pub limit: Option<usize>,
516 pub offset: Option<usize>,
517 pub set_ops: Vec<SetArm>,
520}
521
522impl Select {
523 pub fn base_relations(&self) -> Vec<String> {
532 let mut out = vec![];
533 self.collect_relations(&mut out);
534 out
535 }
536
537 fn collect_relations(&self, out: &mut Vec<String>) {
538 fn table(t: &TableRef, out: &mut Vec<String>) {
539 if let Some(sub) = &t.sub {
540 sub.collect_relations(out);
541 } else if let Some(args) = &t.args {
542 for a in args {
543 expr(a, out);
544 }
545 } else {
546 out.push(t.name.clone());
547 }
548 }
549 fn expr(e: &Expr, out: &mut Vec<String>) {
550 match e {
551 Expr::Subquery(q) | Expr::ArrayQuery(q) => q.collect_relations(out),
552 Expr::Exists { query, .. } => query.collect_relations(out),
553 Expr::InSubquery { expr: x, query, .. } => {
554 expr(x, out);
555 query.collect_relations(out);
556 }
557 Expr::Quantified { left, right, .. } => {
558 expr(left, out);
559 expr(right, out);
560 }
561 Expr::Index { expr: x, index } => {
562 expr(x, out);
563 expr(index, out);
564 }
565 Expr::ArrayLit(items) | Expr::InList { list: items, .. } => {
566 if let Expr::InList { expr: x, .. } = e {
567 expr(x, out);
568 }
569 for i in items {
570 expr(i, out);
571 }
572 }
573 Expr::Func { args, .. } => {
574 for a in args {
575 expr(a, out);
576 }
577 }
578 Expr::Agg { args, order_by, .. } => {
579 for a in args {
580 expr(a, out);
581 }
582 for ob in order_by {
583 if let Some(e) = &ob.expr {
584 expr(e, out);
585 }
586 }
587 }
588 Expr::Case { operand, whens, else_ } => {
589 if let Some(o) = operand {
590 expr(o, out);
591 }
592 for (w, t) in whens {
593 expr(w, out);
594 expr(t, out);
595 }
596 if let Some(x) = else_ {
597 expr(x, out);
598 }
599 }
600 Expr::Binary { left, right, .. } => {
601 expr(left, out);
602 expr(right, out);
603 }
604 Expr::Unary { expr: x, .. } | Expr::Cast { expr: x, .. } | Expr::IsNull { expr: x, .. } => {
605 expr(x, out)
606 }
607 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => {}
608 }
609 }
610 if let Some(f) = &self.from {
611 table(f, out);
612 }
613 for j in &self.joins {
614 table(&j.table, out);
615 if let Some(on) = &j.on {
616 expr(on, out);
617 }
618 }
619 for item in &self.items {
620 expr(&item.expr, out);
621 }
622 if let Some(w) = &self.where_ {
623 expr(w, out);
624 }
625 for g in &self.group_by {
626 expr(g, out);
627 }
628 if let Some(h) = &self.having {
629 expr(h, out);
630 }
631 for ob in &self.order_by {
632 if let Some(e) = &ob.expr {
633 expr(e, out);
634 }
635 }
636 for arm in &self.set_ops {
637 arm.query.collect_relations(out);
638 }
639 }
640}
641
642fn binding_power(op: &str) -> Option<u8> {
653 Some(match op {
654 "OR" => 1,
655 "AND" => 2,
656 "=" | "!=" | "<>" | "<" | "<=" | ">" | ">=" | "~" | "~*" | "!~" | "!~*"
660 | "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => 4,
661 "||" => 5,
662 "+" | "-" => 6,
663 "*" | "/" | "%" => 7,
664 _ => return None,
665 })
666}
667
668struct Parser {
669 toks: Vec<Tok>,
670 pos: usize,
671}
672
673impl Parser {
674 fn peek(&self) -> &Tok {
675 self.toks.get(self.pos).unwrap_or(&Tok::Eof)
676 }
677 fn peek_at(&self, n: usize) -> &Tok {
678 self.toks.get(self.pos + n).unwrap_or(&Tok::Eof)
679 }
680 fn next(&mut self) -> Tok {
681 let t = self.peek().clone();
682 self.pos += 1;
683 t
684 }
685 fn eat_kw(&mut self, kw: &str) -> bool {
686 if self.peek().is_kw(kw) {
687 self.pos += 1;
688 true
689 } else {
690 false
691 }
692 }
693 fn expect_kw(&mut self, kw: &str) -> Result<()> {
694 if self.eat_kw(kw) {
695 Ok(())
696 } else {
697 bail!("expected {} , got {:?}", kw, self.peek())
698 }
699 }
700 fn eat_punct(&mut self, c: char) -> bool {
701 if matches!(self.peek(), Tok::Punct(p) if *p == c) {
702 self.pos += 1;
703 true
704 } else {
705 false
706 }
707 }
708 fn expect_punct(&mut self, c: char) -> Result<()> {
709 if self.eat_punct(c) {
710 Ok(())
711 } else {
712 bail!("expected {:?}, got {:?}", c, self.peek())
713 }
714 }
715 fn eat_op(&mut self, op: &str) -> bool {
716 if matches!(self.peek(), Tok::Op(o) if o == op) {
717 self.pos += 1;
718 true
719 } else {
720 false
721 }
722 }
723
724 fn parse_expr(&mut self) -> Result<Expr> {
727 self.parse_bin(0)
728 }
729
730 fn parse_bin(&mut self, min_bp: u8) -> Result<Expr> {
733 let mut left = self.parse_unary()?;
734
735 loop {
736 if self.peek().is_kw("OPERATOR") && matches!(self.peek_at(1), Tok::Punct('(')) {
743 let save = self.pos;
744 self.pos += 2;
745 let mut sym = None;
747 while sym.is_none() {
748 match self.next() {
749 Tok::Op(o) => sym = Some(o),
750 Tok::Word { .. } | Tok::Punct('.') => continue,
751 _ => break,
752 }
753 }
754 match sym {
755 Some(o) if binding_power(&o).is_some() && self.eat_punct(')') => {
756 let bp = binding_power(&o).unwrap();
757 if bp < min_bp {
758 self.pos = save;
759 break;
760 }
761 let right = self.parse_bin(bp + 1)?;
762 left = Expr::Binary {
763 op: o,
764 left: Box::new(left),
765 right: Box::new(right),
766 };
767 continue;
768 }
769 _ => {
772 self.pos = save;
773 break;
774 }
775 }
776 }
777
778 let (op, width) = match self.peek() {
779 Tok::Op(o) if binding_power(o).is_some() => (o.clone(), 1usize),
780 Tok::Word { upper, .. } if upper == "AND" || upper == "OR" => (upper.clone(), 1),
781 Tok::Word { upper, .. } if upper == "LIKE" || upper == "ILIKE" => (upper.clone(), 1),
782 Tok::Word { upper, .. } if upper == "NOT" => {
783 match self.peek_at(1) {
785 Tok::Word { upper: u2, .. } if u2 == "LIKE" || u2 == "ILIKE" => {
786 (format!("NOT {}", u2), 2)
787 }
788 _ => break,
789 }
790 }
791 _ => break,
792 };
793
794 let bp = match binding_power(&op) {
795 Some(bp) if bp >= min_bp => bp,
796 _ => break,
797 };
798 self.pos += width;
799
800 let quant = match self.peek() {
806 Tok::Word { upper, .. }
807 if matches!(upper.as_str(), "ANY" | "SOME" | "ALL")
808 && matches!(self.peek_at(1), Tok::Punct('(')) =>
809 {
810 Some(upper == "ALL")
811 }
812 _ => None,
813 };
814 if let Some(all) = quant {
815 self.pos += 2; let right = if self.peek().is_kw("SELECT") {
817 Expr::ArrayQuery(Box::new(self.parse_query()?))
818 } else {
819 self.parse_expr()?
820 };
821 self.expect_punct(')')?;
822 left = Expr::Quantified { op, left: Box::new(left), all, right: Box::new(right) };
823 continue;
824 }
825
826 let right = self.parse_bin(bp + 1)?;
828 left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
829 }
830
831 Ok(left)
832 }
833
834 fn parse_postfix(&mut self, mut e: Expr) -> Result<Expr> {
835 loop {
836 if matches!(self.peek(), Tok::Punct('[')) {
839 self.pos += 1;
840 let index = self.parse_expr()?;
841 self.expect_punct(']')?;
842 e = Expr::Index { expr: Box::new(e), index: Box::new(index) };
843 continue;
844 }
845
846 if self.peek().is_kw("IS") {
848 self.pos += 1;
849 let negated = self.eat_kw("NOT");
850 if self.eat_kw("DISTINCT") {
854 self.expect_kw("FROM")?;
855 let rhs = self.parse_bin(5)?;
857 e = Expr::Binary {
858 op: if negated { "IS NOT DISTINCT FROM".into() } else { "IS DISTINCT FROM".into() },
859 left: Box::new(e),
860 right: Box::new(rhs),
861 };
862 continue;
863 }
864 if !self.eat_kw("NULL") {
865 if self.eat_kw("TRUE") {
867 e = Expr::Binary {
868 op: "=".into(),
869 left: Box::new(e),
870 right: Box::new(Expr::Literal(Value::Bool(!negated))),
871 };
872 continue;
873 }
874 if self.eat_kw("FALSE") {
875 e = Expr::Binary {
876 op: "=".into(),
877 left: Box::new(e),
878 right: Box::new(Expr::Literal(Value::Bool(negated))),
879 };
880 continue;
881 }
882 bail!("expected NULL, TRUE or FALSE after IS, got {:?}", self.peek());
883 }
884 e = Expr::IsNull { expr: Box::new(e), negated };
885 continue;
886 }
887
888 let negated_in = if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("IN") {
890 self.pos += 2;
891 true
892 } else if self.peek().is_kw("IN") {
893 self.pos += 1;
894 false
895 } else {
896 let negated_between =
898 if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("BETWEEN") {
899 self.pos += 2;
900 true
901 } else if self.peek().is_kw("BETWEEN") {
902 self.pos += 1;
903 false
904 } else {
905 break;
906 };
907 let low = self.parse_bin(3)?;
911 self.expect_kw("AND")?;
912 let high = self.parse_bin(3)?;
913 let ge = Expr::Binary {
914 op: ">=".into(),
915 left: Box::new(e.clone()),
916 right: Box::new(low),
917 };
918 let le = Expr::Binary {
919 op: "<=".into(),
920 left: Box::new(e),
921 right: Box::new(high),
922 };
923 let both = Expr::Binary {
924 op: "AND".into(),
925 left: Box::new(ge),
926 right: Box::new(le),
927 };
928 e = if negated_between {
929 Expr::Unary { op: "NOT".into(), expr: Box::new(both) }
930 } else {
931 both
932 };
933 continue;
934 };
935
936 self.expect_punct('(')?;
937 if self.peek().is_kw("SELECT") {
939 let query = Box::new(self.parse_query()?);
940 self.expect_punct(')')?;
941 e = Expr::InSubquery { expr: Box::new(e), query, negated: negated_in };
942 continue;
943 }
944 let mut list = vec![];
945 if !self.eat_punct(')') {
946 loop {
947 list.push(self.parse_expr()?);
948 if self.eat_punct(',') {
949 continue;
950 }
951 self.expect_punct(')')?;
952 break;
953 }
954 }
955 e = Expr::InList { expr: Box::new(e), list, negated: negated_in };
956 }
957 Ok(e)
958 }
959
960 fn parse_unary(&mut self) -> Result<Expr> {
961 if self.peek().is_kw("NOT") {
962 self.pos += 1;
963 let e = self.parse_bin(3)?;
966 return Ok(Expr::Unary { op: "NOT".into(), expr: Box::new(e) });
967 }
968 if self.eat_op("-") {
969 let e = self.parse_unary()?;
970 return Ok(Expr::Unary { op: "-".into(), expr: Box::new(e) });
971 }
972 if self.eat_op("+") {
973 return self.parse_unary();
974 }
975 let atom = self.parse_atom()?;
976 let cast = self.parse_casts(atom)?;
977 self.parse_postfix(cast)
988 }
989
990 fn parse_casts(&mut self, mut e: Expr) -> Result<Expr> {
992 loop {
993 if self.peek().is_kw("COLLATE") {
998 self.pos += 1;
999 match self.next() {
1000 Tok::Word { .. } | Tok::Quoted(_) => {}
1001 other => bail!("expected a collation name after COLLATE, got {:?}", other),
1002 }
1003 while self.eat_punct('.') {
1005 match self.next() {
1006 Tok::Word { .. } | Tok::Quoted(_) => {}
1007 other => bail!("expected a name after '.', got {:?}", other),
1008 }
1009 }
1010 continue;
1011 }
1012 if !self.eat_op("::") {
1013 break;
1014 }
1015 let mut ty = match self.next() {
1016 Tok::Word { raw, .. } => raw,
1017 Tok::Quoted(s) => s,
1018 other => bail!("expected a type name after ::, got {:?}", other),
1019 };
1020 while self.eat_punct('.') {
1022 match self.next() {
1023 Tok::Word { raw, .. } => ty = raw,
1024 Tok::Quoted(s) => ty = s,
1025 other => bail!("expected a type name after ., got {:?}", other),
1026 }
1027 }
1028 while self.eat_punct('[') {
1030 self.expect_punct(']')?;
1031 ty.push_str("[]");
1032 }
1033 e = Expr::Cast { expr: Box::new(e), ty };
1034 }
1035 Ok(e)
1036 }
1037
1038
1039 fn parse_atom(&mut self) -> Result<Expr> {
1040 if self.eat_punct('(') {
1047 if self.peek().is_kw("SELECT") {
1050 let q = self.parse_query()?;
1051 self.expect_punct(')')?;
1052 return Ok(Expr::Subquery(Box::new(q)));
1053 }
1054 let e = self.parse_expr()?;
1055 self.expect_punct(')')?;
1056 return Ok(e);
1057 }
1058
1059 if self.peek().is_kw("ARRAY") && matches!(self.peek_at(1), Tok::Punct('(') | Tok::Punct('[')) {
1063 self.pos += 1;
1064 if self.eat_punct('(') {
1065 if !self.peek().is_kw("SELECT") {
1066 bail!("ARRAY(...) takes a subquery; for a list of values write ARRAY[...]");
1067 }
1068 let q = self.parse_query()?;
1069 self.expect_punct(')')?;
1070 return Ok(Expr::ArrayQuery(Box::new(q)));
1071 }
1072 self.expect_punct('[')?;
1073 let mut items = vec![];
1074 if !self.eat_punct(']') {
1075 loop {
1076 items.push(self.parse_expr()?);
1077 if self.eat_punct(',') {
1078 continue;
1079 }
1080 self.expect_punct(']')?;
1081 break;
1082 }
1083 }
1084 return Ok(Expr::ArrayLit(items));
1085 }
1086
1087 if self.peek().is_kw("EXISTS") && matches!(self.peek_at(1), Tok::Punct('(')) {
1091 self.pos += 2;
1092 if !self.peek().is_kw("SELECT") {
1093 bail!("EXISTS (...) takes a subquery");
1094 }
1095 let q = self.parse_query()?;
1096 self.expect_punct(')')?;
1097 return Ok(Expr::Exists { query: Box::new(q), negated: false });
1098 }
1099
1100 if self.peek().is_kw("CAST") && matches!(self.peek_at(1), Tok::Punct('(')) {
1103 self.pos += 2;
1104 let inner = self.parse_expr()?;
1105 self.expect_kw("AS")?;
1106 let mut ty = match self.next() {
1107 Tok::Word { raw, .. } => raw,
1108 Tok::Quoted(s) => s,
1109 other => bail!("expected a type name in CAST, got {:?}", other),
1110 };
1111 while self.eat_punct('.') {
1112 match self.next() {
1113 Tok::Word { raw, .. } => ty = raw,
1114 Tok::Quoted(s) => ty = s,
1115 other => bail!("expected a type name after ., got {:?}", other),
1116 }
1117 }
1118 while self.eat_punct('[') {
1119 self.expect_punct(']')?;
1120 ty.push_str("[]");
1121 }
1122 self.expect_punct(')')?;
1123 return Ok(Expr::Cast { expr: Box::new(inner), ty });
1124 }
1125
1126 if self.peek().is_kw("CASE") {
1128 return self.parse_case();
1129 }
1130
1131 match self.next() {
1132 Tok::Num(n) => Ok(Expr::Literal(from_f64(n))),
1133 Tok::Str(s) => Ok(Expr::Literal(Value::String(s))),
1134 Tok::Op(o) if o == "*" => Ok(Expr::Star),
1135 Tok::Quoted(name) => self.parse_name_tail(None, name),
1136 Tok::Word { upper, raw } => match upper.as_str() {
1137 "NULL" => Ok(Expr::Literal(Value::Null)),
1138 "TRUE" => Ok(Expr::Literal(Value::Bool(true))),
1139 "FALSE" => Ok(Expr::Literal(Value::Bool(false))),
1140 "CURRENT_SCHEMA" | "CURRENT_DATABASE" | "CURRENT_USER" | "SESSION_USER"
1144 | "CURRENT_CATALOG" | "USER" | "VERSION"
1145 if !matches!(self.peek(), Tok::Punct('(')) =>
1146 {
1147 Ok(Expr::Func { name: upper.to_lowercase(), args: vec![] })
1148 }
1149 _ => self.parse_name_tail(None, raw),
1150 },
1151 other => bail!("unexpected {:?} in an expression", other),
1152 }
1153 }
1154
1155 fn parse_name_tail(&mut self, _schema: Option<String>, first: String) -> Result<Expr> {
1162 let mut parts = vec![first];
1163 while self.eat_punct('.') {
1164 if self.eat_op("*") {
1166 return Ok(Expr::QualifiedStar(parts.pop().unwrap_or_default()));
1167 }
1168 match self.next() {
1169 Tok::Word { raw, .. } => parts.push(raw),
1170 Tok::Quoted(s) => parts.push(s),
1171 other => bail!("expected a name after '.', got {:?}", other),
1172 }
1173 }
1174
1175 if matches!(self.peek(), Tok::Punct('(')) {
1179 self.pos += 1;
1180 let name = parts.pop().unwrap_or_default().to_lowercase();
1181 let agg = is_aggregate(&name);
1182 let distinct = agg && self.eat_kw("DISTINCT");
1184 let mut args = vec![];
1185 let mut order_by = vec![];
1186 if !self.eat_punct(')') {
1187 loop {
1188 if self.eat_op("*") {
1190 args.push(Expr::Star);
1191 } else {
1192 args.push(self.parse_expr()?);
1193 }
1194 if self.eat_punct(',') {
1195 continue;
1196 }
1197 if agg && self.peek().is_kw("ORDER") {
1201 self.pos += 1;
1202 self.expect_kw("BY")?;
1203 order_by = self.parse_sort_list()?;
1204 }
1205 self.expect_punct(')')?;
1206 break;
1207 }
1208 }
1209 if agg {
1210 return Ok(Expr::Agg { name, args, order_by, distinct });
1211 }
1212 return Ok(Expr::Func { name, args });
1213 }
1214
1215 let name = parts.pop().unwrap_or_default();
1216 let qual = parts.pop();
1220 Ok(Expr::Column { qual, name })
1221 }
1222
1223 fn parse_case(&mut self) -> Result<Expr> {
1224 self.expect_kw("CASE")?;
1225 let operand = if self.peek().is_kw("WHEN") {
1227 None
1228 } else {
1229 Some(Box::new(self.parse_expr()?))
1230 };
1231 let mut whens = vec![];
1232 while self.eat_kw("WHEN") {
1233 let cond = self.parse_expr()?;
1234 self.expect_kw("THEN")?;
1235 let then = self.parse_expr()?;
1236 whens.push((cond, then));
1237 }
1238 if whens.is_empty() {
1239 bail!("CASE needs at least one WHEN branch");
1240 }
1241 let else_ = if self.eat_kw("ELSE") {
1242 Some(Box::new(self.parse_expr()?))
1243 } else {
1244 None
1245 };
1246 self.expect_kw("END")?;
1247 Ok(Expr::Case { operand, whens, else_ })
1248 }
1249
1250 fn parse_table_ref(&mut self) -> Result<TableRef> {
1253 let lateral = self.eat_kw("LATERAL");
1254 if self.eat_punct('(') {
1256 if !self.peek().is_kw("SELECT") {
1257 bail!("expected a subquery after '(' in FROM, got {:?}", self.peek());
1258 }
1259 let sub = self.parse_query()?;
1260 self.expect_punct(')')?;
1261 let (alias, col_aliases) = self.parse_table_alias()?;
1262 if alias.is_none() {
1263 bail!("a subquery in FROM must have an alias");
1264 }
1265 return Ok(TableRef {
1266 name: "(subquery)".into(),
1267 alias,
1268 sub: Some(Box::new(sub)),
1269 args: None,
1270 col_aliases,
1271 lateral,
1272 trace: None,
1275 trace_reverse: false,
1276 traverse: None,
1277 as_of: None,
1278 valid_as_of: None,
1279 search: None,
1280 });
1281 }
1282 if lateral {
1283 bail!("LATERAL applies to a subquery in FROM; write LATERAL (SELECT ...)");
1284 }
1285
1286 let mut parts = vec![match self.next() {
1287 Tok::Word { raw, .. } => raw,
1288 Tok::Quoted(s) => s,
1289 other => bail!("expected a table name, got {:?}", other),
1290 }];
1291 while self.eat_punct('.') {
1292 match self.next() {
1293 Tok::Word { raw, .. } => parts.push(raw),
1294 Tok::Quoted(s) => parts.push(s),
1295 other => bail!("expected a name after '.', got {:?}", other),
1296 }
1297 }
1298 let name = parts.join(".");
1299
1300 if self.eat_punct('(') {
1303 let mut args = vec![];
1304 if !self.eat_punct(')') {
1305 loop {
1306 args.push(self.parse_expr()?);
1307 if self.eat_punct(',') {
1308 continue;
1309 }
1310 self.expect_punct(')')?;
1311 break;
1312 }
1313 }
1314 let fname = name.rsplit('.').next().unwrap_or(&name).to_lowercase();
1315 let (alias, col_aliases) = self.parse_table_alias()?;
1316 return Ok(TableRef { name: fname, alias, sub: None, args: Some(args), col_aliases, lateral: false, as_of: None, valid_as_of: None, search: None, trace: None, trace_reverse: false, traverse: None });
1317 }
1318
1319 let as_of = if self.peek().is_kw("AS") && self.peek_at(1).is_kw("OF") {
1324 self.next();
1325 self.next();
1326 self.expect_kw("SYSTEM")?;
1327 self.expect_kw("TIME")?;
1328 match self.next() {
1329 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Some(n as u64),
1330 other => bail!(
1331 "AS OF SYSTEM TIME takes a NEDB sequence number here, not a timestamp \
1332 (got {:?}). NEDB's history is sequence-addressed and never \
1333 garbage-collected, so a seq is exact where a wall-clock time would be \
1334 approximate",
1335 other
1336 ),
1337 }
1338 } else {
1339 None
1340 };
1341 let valid_as_of = if self.peek().is_kw("VALID")
1357 && self.peek_at(1).is_kw("AS")
1358 && self.peek_at(2).is_kw("OF")
1359 {
1360 self.next();
1361 self.next();
1362 self.next();
1363 match self.next() {
1364 Tok::Str(s) => Some(s),
1365 other => bail!(
1366 "VALID AS OF takes a date string here (got {:?}). System time is a \
1367 sequence and application-time validity is a date — they are different \
1368 questions, so they take different arguments",
1369 other
1370 ),
1371 }
1372 } else {
1373 None
1374 };
1375
1376 let search = if self.peek().is_kw("SEARCH") && matches!(self.peek_at(1), Tok::Str(_)) {
1377 self.next();
1378 match self.next() {
1379 Tok::Str(s) => Some(s),
1380 other => bail!("SEARCH takes a string here, got {:?}", other),
1383 }
1384 } else {
1385 None
1386 };
1387
1388 fn names_a_thing(t: &Tok) -> bool {
1398 match t {
1399 Tok::Word { upper, .. } => !is_clause_keyword(upper),
1400 Tok::Quoted(_) => true,
1401 _ => false,
1402 }
1403 }
1404
1405 let (trace, trace_reverse) = if self.peek().is_kw("TRACE")
1406 && names_a_thing(self.peek_at(1))
1407 {
1408 self.next();
1409 let edge = match self.next() {
1410 Tok::Word { raw, .. } => raw,
1411 Tok::Quoted(s) => s,
1412 other => bail!("TRACE takes an edge type here, got {:?}", other),
1413 };
1414 let rev = if self.peek().is_kw("REVERSE") {
1415 self.next();
1416 true
1417 } else {
1418 false
1419 };
1420 (Some(edge), rev)
1421 } else {
1422 (None, false)
1423 };
1424
1425 let traverse = if self.peek().is_kw("TRAVERSE")
1426 && names_a_thing(self.peek_at(1))
1427 {
1428 self.next();
1429 match self.next() {
1430 Tok::Word { raw, .. } => Some(raw),
1431 Tok::Quoted(s) => Some(s),
1432 other => bail!("TRAVERSE takes a relation name here, got {:?}", other),
1433 }
1434 } else {
1435 None
1436 };
1437
1438 let (alias, col_aliases) = self.parse_table_alias()?;
1439 Ok(TableRef { name, alias, sub: None, args: None, col_aliases, lateral: false, as_of, valid_as_of, search, trace, trace_reverse, traverse })
1440 }
1441
1442 fn parse_table_alias(&mut self) -> Result<(Option<String>, Vec<String>)> {
1447 let alias = if self.eat_kw("AS") {
1448 match self.next() {
1449 Tok::Word { raw, .. } => Some(raw),
1450 Tok::Quoted(s) => Some(s),
1451 other => bail!("expected an alias after AS, got {:?}", other),
1452 }
1453 } else {
1454 match self.peek().clone() {
1455 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1456 self.pos += 1;
1457 Some(raw)
1458 }
1459 Tok::Quoted(s) => {
1460 self.pos += 1;
1461 Some(s)
1462 }
1463 _ => None,
1464 }
1465 };
1466 let mut col_aliases = vec![];
1467 if alias.is_some() && self.eat_punct('(') {
1468 loop {
1469 match self.next() {
1470 Tok::Word { raw, .. } => col_aliases.push(raw),
1471 Tok::Quoted(s) => col_aliases.push(s),
1472 other => bail!("expected a column alias, got {:?}", other),
1473 }
1474 if self.eat_punct(',') {
1475 continue;
1476 }
1477 self.expect_punct(')')?;
1478 break;
1479 }
1480 }
1481 Ok((alias, col_aliases))
1482 }
1483
1484 fn parse_query(&mut self) -> Result<Select> {
1491 let mut first = self.parse_select_body()?;
1492 loop {
1493 let op = if self.eat_kw("UNION") {
1494 SetOp::Union
1495 } else if self.eat_kw("INTERSECT") {
1496 SetOp::Intersect
1497 } else if self.eat_kw("EXCEPT") {
1498 SetOp::Except
1499 } else {
1500 break;
1501 };
1502 let all = self.eat_kw("ALL");
1503 if !all {
1504 let _ = self.eat_kw("DISTINCT");
1505 }
1506 let query = if self.eat_punct('(') {
1508 let q = self.parse_query()?;
1509 self.expect_punct(')')?;
1510 q
1511 } else {
1512 self.parse_select_body()?
1513 };
1514 first.set_ops.push(SetArm { op, all, query });
1515 }
1516 self.parse_query_tail(&mut first)?;
1517 Ok(first)
1518 }
1519
1520 fn parse_sort_list(&mut self) -> Result<Vec<OrderBy>> {
1524 let mut out = vec![];
1525 loop {
1526 let (ordinal, expr) = match self.peek().clone() {
1530 Tok::Num(n)
1531 if n.fract() == 0.0
1532 && n >= 1.0
1533 && !matches!(self.peek_at(1), Tok::Op(_)) =>
1534 {
1535 self.pos += 1;
1536 (Some(n as usize), None)
1537 }
1538 _ => (None, Some(self.parse_expr()?)),
1539 };
1540 let dir = if self.eat_kw("DESC") {
1541 Dir::Desc
1542 } else {
1543 let _ = self.eat_kw("ASC");
1544 Dir::Asc
1545 };
1546 let mut nulls_first = matches!(dir, Dir::Desc);
1548 if self.eat_kw("NULLS") {
1549 if self.eat_kw("FIRST") {
1550 nulls_first = true;
1551 } else if self.eat_kw("LAST") {
1552 nulls_first = false;
1553 } else {
1554 bail!("expected FIRST or LAST after NULLS, got {:?}", self.peek());
1555 }
1556 }
1557 out.push(OrderBy { ordinal, expr, dir, nulls_first });
1558 if self.eat_punct(',') {
1559 continue;
1560 }
1561 break;
1562 }
1563 Ok(out)
1564 }
1565
1566 fn parse_query_tail(&mut self, sel: &mut Select) -> Result<()> {
1567 let mut order_by = vec![];
1568 if self.eat_kw("ORDER") {
1569 self.expect_kw("BY")?;
1570 order_by = self.parse_sort_list()?;
1571 }
1572
1573 let mut limit = None;
1574 let mut offset = None;
1575 loop {
1577 if self.eat_kw("LIMIT") {
1578 if self.eat_kw("ALL") {
1579 limit = None;
1580 } else {
1581 limit = Some(self.parse_count("LIMIT")?);
1582 }
1583 continue;
1584 }
1585 if self.eat_kw("OFFSET") {
1586 offset = Some(self.parse_count("OFFSET")?);
1587 let _ = self.eat_kw("ROW") || self.eat_kw("ROWS");
1588 continue;
1589 }
1590 break;
1591 }
1592 sel.order_by = order_by;
1593 sel.limit = limit;
1594 sel.offset = offset;
1595 Ok(())
1596 }
1597
1598 fn parse_select_body(&mut self) -> Result<Select> {
1600 self.expect_kw("SELECT")?;
1601 let distinct = self.eat_kw("DISTINCT");
1602 if distinct && self.peek().is_kw("ON") {
1603 bail!("DISTINCT ON is not supported");
1604 }
1605 let _ = self.eat_kw("ALL");
1606
1607 let mut items = vec![];
1608 loop {
1609 let expr = self.parse_expr()?;
1610 let alias = if self.eat_kw("AS") {
1612 match self.next() {
1613 Tok::Word { raw, .. } => Some(raw),
1614 Tok::Quoted(s) => Some(s),
1615 other => bail!("expected an alias after AS, got {:?}", other),
1616 }
1617 } else {
1618 match self.peek().clone() {
1619 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1620 self.pos += 1;
1621 Some(raw)
1622 }
1623 Tok::Quoted(s) => {
1624 self.pos += 1;
1625 Some(s)
1626 }
1627 _ => None,
1628 }
1629 };
1630 items.push(SelectItem { expr, alias });
1631 if self.eat_punct(',') {
1632 continue;
1633 }
1634 break;
1635 }
1636
1637 let mut from = None;
1638 let mut joins = vec![];
1639 if self.eat_kw("FROM") {
1640 from = Some(self.parse_table_ref()?);
1641 loop {
1642 if self.eat_punct(',') {
1648 let table = self.parse_table_ref()?;
1649 joins.push(Join { kind: JoinKind::Cross, table, on: None });
1650 continue;
1651 }
1652 let kind = if self.peek().is_kw("JOIN") {
1653 self.pos += 1;
1654 JoinKind::Inner
1655 } else if self.peek().is_kw("INNER") && self.peek_at(1).is_kw("JOIN") {
1656 self.pos += 2;
1657 JoinKind::Inner
1658 } else if self.peek().is_kw("CROSS") && self.peek_at(1).is_kw("JOIN") {
1659 self.pos += 2;
1660 JoinKind::Cross
1661 } else if self.peek().is_kw("LEFT") {
1662 self.pos += 1;
1663 let _ = self.eat_kw("OUTER");
1664 self.expect_kw("JOIN")?;
1665 JoinKind::Left
1666 } else if self.peek().is_kw("RIGHT") {
1667 self.pos += 1;
1668 let _ = self.eat_kw("OUTER");
1669 self.expect_kw("JOIN")?;
1670 JoinKind::Right
1671 } else if self.peek().is_kw("FULL") {
1672 self.pos += 1;
1673 let _ = self.eat_kw("OUTER");
1674 self.expect_kw("JOIN")?;
1675 JoinKind::Full
1676 } else {
1677 break;
1678 };
1679 let table = self.parse_table_ref()?;
1680 let on = if self.eat_kw("ON") {
1681 Some(self.parse_expr()?)
1682 } else if self.peek().is_kw("USING") {
1683 bail!("JOIN ... USING is not supported — write ON a.col = b.col");
1684 } else {
1685 None
1686 };
1687 if on.is_none() && !matches!(kind, JoinKind::Cross) {
1688 bail!("a {:?} JOIN needs an ON clause", kind);
1689 }
1690 joins.push(Join { kind, table, on });
1691 }
1692 }
1693
1694 let where_ = if self.eat_kw("WHERE") {
1695 Some(self.parse_expr()?)
1696 } else {
1697 None
1698 };
1699
1700 let mut group_by = vec![];
1701 if self.eat_kw("GROUP") {
1702 self.expect_kw("BY")?;
1703 if self.eat_kw("ALL") || self.eat_kw("DISTINCT") {
1704 bail!("GROUP BY ALL / DISTINCT is not supported — list the keys");
1705 }
1706 loop {
1707 if self.peek().is_kw("ROLLUP")
1708 || self.peek().is_kw("CUBE")
1709 || self.peek().is_kw("GROUPING")
1710 {
1711 bail!("GROUP BY ROLLUP / CUBE / GROUPING SETS is not supported");
1712 }
1713 group_by.push(self.parse_expr()?);
1714 if self.eat_punct(',') {
1715 continue;
1716 }
1717 break;
1718 }
1719 }
1720
1721 let having = if self.eat_kw("HAVING") {
1722 Some(self.parse_expr()?)
1723 } else {
1724 None
1725 };
1726 if having.is_some() && group_by.is_empty() && !items.iter().any(|i| has_aggregate(&i.expr))
1727 {
1728 bail!("HAVING needs a GROUP BY or an aggregate — it filters groups, not rows; \
1729 use WHERE to filter rows");
1730 }
1731
1732 Ok(Select {
1733 distinct,
1734 items,
1735 from,
1736 joins,
1737 where_,
1738 group_by,
1739 having,
1740 order_by: vec![],
1741 limit: None,
1742 offset: None,
1743 set_ops: vec![],
1744 })
1745 }
1746
1747 fn parse_count(&mut self, what: &str) -> Result<usize> {
1748 match self.next() {
1749 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Ok(n as usize),
1750 other => bail!("{} expects a non-negative integer, got {:?}", what, other),
1751 }
1752 }
1753}
1754
1755fn is_clause_keyword(upper: &str) -> bool {
1760 matches!(
1761 upper,
1762 "FROM" | "WHERE" | "GROUP" | "HAVING" | "ORDER" | "LIMIT" | "OFFSET"
1763 | "JOIN" | "LEFT" | "RIGHT" | "FULL" | "INNER" | "CROSS" | "OUTER"
1764 | "ON" | "USING" | "AND" | "OR" | "AS" | "UNION" | "INTERSECT"
1765 | "EXCEPT" | "FETCH" | "FOR" | "WINDOW" | "RETURNING" | "INTO"
1766 | "ASC" | "DESC" | "NULLS" | "IS" | "IN" | "NOT" | "LIKE" | "ILIKE"
1767 | "BETWEEN" | "THEN" | "WHEN" | "ELSE" | "END" | "CASE" | "DISTINCT"
1768 | "SELECT" | "WITH" | "ALL"
1769 )
1770}
1771
1772pub fn parse(sql: &str) -> Result<Select> {
1774 let toks = lex(sql)?;
1775 let mut p = Parser { toks, pos: 0 };
1776 let sel = if matches!(p.peek(), Tok::Punct('(')) && p.peek_at(1).is_kw("SELECT") {
1778 p.pos += 1;
1779 let mut first = p.parse_query()?;
1780 p.expect_punct(')')?;
1781 loop {
1783 let op = if p.eat_kw("UNION") {
1784 SetOp::Union
1785 } else if p.eat_kw("INTERSECT") {
1786 SetOp::Intersect
1787 } else if p.eat_kw("EXCEPT") {
1788 SetOp::Except
1789 } else {
1790 break;
1791 };
1792 let all = p.eat_kw("ALL");
1793 if !all {
1794 let _ = p.eat_kw("DISTINCT");
1795 }
1796 let query = if p.eat_punct('(') {
1797 let q = p.parse_query()?;
1798 p.expect_punct(')')?;
1799 q
1800 } else {
1801 p.parse_select_body()?
1802 };
1803 first.set_ops.push(SetArm { op, all, query });
1804 }
1805 p.parse_query_tail(&mut first)?;
1806 first
1807 } else {
1808 p.parse_query()?
1809 };
1810 let _ = p.eat_punct(';');
1811 if !matches!(p.peek(), Tok::Eof) {
1812 bail!("unexpected trailing tokens: {:?}", p.peek());
1813 }
1814 Ok(sel)
1815}
1816
1817pub struct Bound<'a> {
1830 pub parts: Vec<(String, Option<&'a Value>)>,
1831 pub ctx: EvalCtx<'a>,
1833}
1834
1835#[derive(Clone, Copy, Default)]
1848pub struct EvalCtx<'a> {
1849 pub resolver: Option<&'a Resolver<'a>>,
1850 pub outer: Option<&'a Bound<'a>>,
1851}
1852
1853impl<'a> Bound<'a> {
1854 pub fn new(parts: Vec<(String, Option<&'a Value>)>) -> Self {
1856 Bound { parts, ctx: EvalCtx::default() }
1857 }
1858
1859 fn column(&self, qual: Option<&str>, name: &str) -> Value {
1866 match qual {
1867 Some(q) => {
1868 for (binding, row) in &self.parts {
1869 if binding.eq_ignore_ascii_case(q) {
1870 return row
1871 .and_then(|r| r.get(name))
1872 .cloned()
1873 .unwrap_or(Value::Null);
1874 }
1875 }
1876 match self.ctx.outer {
1877 Some(o) if o.has_binding(q) => o.column(qual, name),
1878 _ => Value::Null,
1879 }
1880 }
1881 None => {
1882 for (_, row) in &self.parts {
1883 if let Some(v) = row.and_then(|r| r.get(name)) {
1884 return v.clone();
1885 }
1886 }
1887 match self.ctx.outer {
1888 Some(o) => o.column(None, name),
1889 None => Value::Null,
1890 }
1891 }
1892 }
1893 }
1894
1895 fn has_binding(&self, qual: &str) -> bool {
1900 self.parts.iter().any(|(b, _)| b.eq_ignore_ascii_case(qual))
1901 || self.ctx.outer.is_some_and(|o| o.has_binding(qual))
1902 }
1903
1904 fn flatten(&self) -> Vec<(String, Value)> {
1906 let mut out = vec![];
1907 for (_, row) in &self.parts {
1908 if let Some(Value::Object(m)) = row {
1909 for (k, v) in m {
1910 out.push((k.clone(), v.clone()));
1911 }
1912 }
1913 }
1914 out
1915 }
1916
1917 fn flatten_binding(&self, qual: &str) -> Vec<(String, Value)> {
1918 let mut out = vec![];
1919 for (binding, row) in &self.parts {
1920 if binding.eq_ignore_ascii_case(qual) {
1921 if let Some(Value::Object(m)) = row {
1922 for (k, v) in m {
1923 out.push((k.clone(), v.clone()));
1924 }
1925 }
1926 }
1927 }
1928 out
1929 }
1930}
1931
1932type Truth = Option<bool>;
1940
1941fn truthy(v: &Value) -> Truth {
1942 match v {
1943 Value::Null => None,
1944 Value::Bool(b) => Some(*b),
1945 _ => None,
1949 }
1950}
1951
1952fn cmp_values(a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
1958 use std::cmp::Ordering;
1959 match (a, b) {
1960 (Value::Null, _) | (_, Value::Null) => None,
1961 (Value::Number(x), Value::Number(y)) => {
1962 x.as_f64().partial_cmp(&y.as_f64())
1963 }
1964 (Value::String(x), Value::String(y)) => Some(x.cmp(y)),
1965 (Value::Bool(x), Value::Bool(y)) => Some(x.cmp(y)),
1966 (Value::Number(x), Value::String(y)) => match y.parse::<f64>() {
1969 Ok(n) => x.as_f64().partial_cmp(&Some(n)),
1970 Err(_) => Some(as_text(a).cmp(&as_text(b))),
1971 },
1972 (Value::String(x), Value::Number(y)) => match x.parse::<f64>() {
1973 Ok(n) => Some(n).partial_cmp(&y.as_f64()),
1974 Err(_) => Some(as_text(a).cmp(&as_text(b))),
1975 },
1976 _ => {
1977 let (x, y) = (as_text(a), as_text(b));
1978 if x == y { Some(Ordering::Equal) } else { Some(x.cmp(&y)) }
1979 }
1980 }
1981}
1982
1983fn as_text(v: &Value) -> String {
1985 match v {
1986 Value::String(s) => s.clone(),
1987 Value::Null => String::new(),
1988 Value::Bool(b) => (if *b { "t" } else { "f" }).to_string(),
1989 Value::Array(items) => {
1992 let inner: Vec<String> = items
1993 .iter()
1994 .map(|i| match i {
1995 Value::Null => "NULL".to_string(),
1996 Value::String(s) if s.is_empty()
1997 || s.chars().any(|c| c.is_whitespace() || matches!(c, ',' | '{' | '}' | '"' | '\\')) =>
1998 {
1999 format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
2000 }
2001 other => as_text(other),
2002 })
2003 .collect();
2004 format!("{{{}}}", inner.join(","))
2005 }
2006 other => other.to_string(),
2007 }
2008}
2009
2010fn num(v: &Value) -> Option<f64> {
2011 match v {
2012 Value::Number(n) => n.as_f64(),
2013 Value::String(s) => s.parse().ok(),
2014 Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
2015 _ => None,
2016 }
2017}
2018
2019fn from_f64(f: f64) -> Value {
2033 if f.is_finite() && f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
2034 return Value::Number((f as i64).into());
2035 }
2036 serde_json::Number::from_f64(f).map(Value::Number).unwrap_or(Value::Null)
2037}
2038
2039pub fn eval(e: &Expr, row: &Bound) -> Result<Value> {
2041 Ok(match e {
2042 Expr::Literal(v) => v.clone(),
2043
2044 Expr::Column { qual, name } => {
2045 if let Some(q) = qual {
2049 if !row.has_binding(q) {
2050 bail!("no table or alias named {:?} in this query", q);
2051 }
2052 }
2053 row.column(qual.as_deref(), name)
2054 }
2055
2056 Expr::Cast { expr, .. } => eval(expr, row)?,
2057
2058 Expr::Star | Expr::QualifiedStar(_) => {
2059 bail!("`*` is only valid in a select list or as count(*)")
2060 }
2061
2062 Expr::Unary { op, expr } => {
2063 let v = eval(expr, row)?;
2064 match op.as_str() {
2065 "NOT" => match truthy(&v) {
2066 None => Value::Null,
2068 Some(b) => Value::Bool(!b),
2069 },
2070 "-" => match num(&v) {
2071 Some(n) => from_f64(-n),
2072 None => Value::Null,
2073 },
2074 other => bail!("unsupported unary operator {:?}", other),
2075 }
2076 }
2077
2078 Expr::Binary { op, left, right } => {
2079 if op == "AND" {
2084 let l = truthy(&eval(left, row)?);
2085 if l == Some(false) {
2086 return Ok(Value::Bool(false));
2087 }
2088 let r = truthy(&eval(right, row)?);
2089 return Ok(match (l, r) {
2090 (_, Some(false)) => Value::Bool(false),
2091 (Some(true), Some(true)) => Value::Bool(true),
2092 _ => Value::Null,
2093 });
2094 }
2095 if op == "OR" {
2096 let l = truthy(&eval(left, row)?);
2097 if l == Some(true) {
2098 return Ok(Value::Bool(true));
2099 }
2100 let r = truthy(&eval(right, row)?);
2101 return Ok(match (l, r) {
2102 (_, Some(true)) => Value::Bool(true),
2103 (Some(false), Some(false)) => Value::Bool(false),
2104 _ => Value::Null,
2105 });
2106 }
2107
2108 let l = eval(left, row)?;
2109 let r = eval(right, row)?;
2110 apply_op(op, l, r)?
2111 }
2112
2113 Expr::IsNull { expr, negated } => {
2114 let v = eval(expr, row)?;
2115 Value::Bool(v.is_null() != *negated)
2118 }
2119
2120 Expr::InList { expr, list, negated } => {
2121 let v = eval(expr, row)?;
2122 if v.is_null() {
2123 return Ok(Value::Null);
2124 }
2125 let mut items = Vec::with_capacity(list.len());
2126 for item in list {
2127 items.push(eval(item, row)?);
2128 }
2129 in_values(&v, &items, *negated)?
2130 }
2131
2132 Expr::Subquery(q) => {
2134 let (cols, rows) = run_sub(q, row)?;
2135 if cols.len() != 1 {
2136 bail!("a subquery used as an expression must return exactly one \
2137 column, this one returns {}", cols.len());
2138 }
2139 match rows.len() {
2140 0 => Value::Null,
2141 1 => rows[0].get(&cols[0].key).cloned().unwrap_or(Value::Null),
2142 n => bail!("more than one row returned by a subquery used as an \
2143 expression ({} rows)", n),
2144 }
2145 }
2146 Expr::Exists { query, negated } => {
2147 let (_, rows) = run_sub(query, row)?;
2148 Value::Bool(!rows.is_empty() != *negated)
2149 }
2150 Expr::ArrayQuery(q) => Value::Array(first_column(q, row)?),
2151 Expr::InSubquery { expr, query, negated } => {
2152 let v = eval(expr, row)?;
2153 if v.is_null() {
2154 return Ok(Value::Null);
2155 }
2156 let items = first_column(query, row)?;
2157 in_values(&v, &items, *negated)?
2158 }
2159 Expr::Quantified { op, left, all, right } => {
2160 let l = eval(left, row)?;
2161 let r = eval(right, row)?;
2162 let items = match r {
2163 Value::Null => return Ok(Value::Null),
2164 Value::Array(items) => items,
2165 other => bail!(
2166 "{} requires an array or a subquery on its right side, got {}",
2167 if *all { "ALL" } else { "ANY" },
2168 as_text(&other)
2169 ),
2170 };
2171 let mut saw_true = false;
2175 let mut saw_false = false;
2176 let mut saw_null = false;
2177 for item in items {
2178 match truthy(&apply_op(op, l.clone(), item)?) {
2179 Some(true) => saw_true = true,
2180 Some(false) => saw_false = true,
2181 None => saw_null = true,
2182 }
2183 }
2184 if *all {
2185 if saw_false {
2186 Value::Bool(false)
2187 } else if saw_null {
2188 Value::Null
2189 } else {
2190 Value::Bool(true)
2191 }
2192 } else if saw_true {
2193 Value::Bool(true)
2194 } else if saw_null {
2195 Value::Null
2196 } else {
2197 Value::Bool(false)
2198 }
2199 }
2200 Expr::Index { expr, index } => {
2201 let arr = eval(expr, row)?;
2202 let i = eval(index, row)?;
2203 match (arr, num(&i)) {
2204 (Value::Array(items), Some(n)) if n >= 1.0 => {
2205 items.get(n as usize - 1).cloned().unwrap_or(Value::Null)
2206 }
2207 _ => Value::Null,
2208 }
2209 }
2210 Expr::ArrayLit(items) => {
2211 let mut out = Vec::with_capacity(items.len());
2212 for i in items {
2213 out.push(eval(i, row)?);
2214 }
2215 Value::Array(out)
2216 }
2217
2218 Expr::Case { operand, whens, else_ } => {
2219 let subject = match operand {
2220 Some(o) => Some(eval(o, row)?),
2221 None => None,
2222 };
2223 for (cond, then) in whens {
2224 let hit = match &subject {
2225 Some(sv) => {
2227 let cv = eval(cond, row)?;
2228 matches!(cmp_values(sv, &cv), Some(std::cmp::Ordering::Equal))
2229 }
2230 None => truthy(&eval(cond, row)?) == Some(true),
2233 };
2234 if hit {
2235 return eval(then, row);
2236 }
2237 }
2238 match else_ {
2239 Some(e) => eval(e, row)?,
2240 None => Value::Null,
2243 }
2244 }
2245
2246 Expr::Func { name, args } => eval_func(name, args, row)?,
2247
2248 Expr::Agg { name, .. } => bail!(
2254 "{}() is an aggregate and has no value for one row — it is reduced \
2255 over a GROUP. Reaching this point is an engine bug, not a problem \
2256 with the query",
2257 name
2258 ),
2259 })
2260}
2261
2262fn in_values(v: &Value, items: &[Value], negated: bool) -> Result<Value> {
2264 let mut any_null = false;
2265 let mut found = false;
2266 for iv in items {
2267 if iv.is_null() {
2268 any_null = true;
2269 continue;
2270 }
2271 if matches!(cmp_values(v, iv), Some(std::cmp::Ordering::Equal)) {
2272 found = true;
2273 break;
2274 }
2275 }
2276 Ok(if found {
2280 Value::Bool(!negated)
2281 } else if any_null {
2282 Value::Null
2283 } else {
2284 Value::Bool(negated)
2285 })
2286}
2287
2288fn run_sub(q: &Select, row: &Bound) -> Result<(Vec<OutCol>, Vec<Value>)> {
2296 let Some(resolve) = row.ctx.resolver else {
2297 bail!("a subquery cannot run here: this evaluation has no relation resolver");
2298 };
2299 let (cols, rows, _) = execute_inner(q, resolve, Opts::default(), Some(row))?;
2300 Ok((cols, rows))
2301}
2302
2303fn first_column(q: &Select, row: &Bound) -> Result<Vec<Value>> {
2306 let (cols, rows) = run_sub(q, row)?;
2307 let Some(first) = cols.first() else {
2308 bail!("the subquery returns no columns");
2309 };
2310 Ok(rows
2311 .into_iter()
2312 .map(|r| r.get(&first.key).cloned().unwrap_or(Value::Null))
2313 .collect())
2314}
2315
2316fn apply_op(op: &str, l: Value, r: Value) -> Result<Value> {
2320 let compare = |ord: fn(std::cmp::Ordering) -> bool| -> Value {
2322 match cmp_values(&l, &r) {
2323 None => Value::Null,
2324 Some(o) => Value::Bool(ord(o)),
2325 }
2326 };
2327
2328 Ok(match op {
2329 "IS DISTINCT FROM" | "IS NOT DISTINCT FROM" => {
2330 let distinct = match (l.is_null(), r.is_null()) {
2331 (true, true) => false,
2332 (true, false) | (false, true) => true,
2333 (false, false) => !matches!(cmp_values(&l, &r), Some(std::cmp::Ordering::Equal)),
2334 };
2335 Value::Bool(distinct != op.starts_with("IS NOT"))
2336 }
2337 "=" => compare(|o| o.is_eq()),
2338 "!=" | "<>" => compare(|o| o.is_ne()),
2339 "<" => compare(|o| o.is_lt()),
2340 "<=" => compare(|o| o.is_le()),
2341 ">" => compare(|o| o.is_gt()),
2342 ">=" => compare(|o| o.is_ge()),
2343
2344 "~" | "~*" | "!~" | "!~*" => {
2345 if l.is_null() || r.is_null() {
2346 Value::Null
2347 } else {
2348 let pat = as_text(&r);
2349 if let Some(why) = crate::nql::regex_error_pub(&pat) {
2350 bail!(
2351 "{} — in {:?}. The supported subset is ^ $ . | ( ) \
2352 [ ] * + ? and literal text",
2353 why, pat
2354 );
2355 }
2356 let hit = crate::nql::regex_match_pub(
2357 &as_text(&l), &pat, op.ends_with('*'));
2358 Value::Bool(hit != op.starts_with('!'))
2359 }
2360 }
2361
2362 "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => {
2363 if l.is_null() || r.is_null() {
2364 Value::Null
2365 } else {
2366 let hit = crate::nql::like_match_pub(
2367 &as_text(&l), &as_text(&r), op.ends_with("ILIKE"));
2368 Value::Bool(hit != op.starts_with("NOT"))
2369 }
2370 }
2371
2372 "||" => {
2374 if l.is_null() || r.is_null() {
2375 Value::Null
2376 } else {
2377 Value::String(format!("{}{}", as_text(&l), as_text(&r)))
2378 }
2379 }
2380
2381 "+" | "-" | "*" | "/" | "%" => match (num(&l), num(&r)) {
2382 (Some(a), Some(b)) => match op {
2383 "+" => from_f64(a + b),
2384 "-" => from_f64(a - b),
2385 "*" => from_f64(a * b),
2386 "/" if b == 0.0 => bail!("division by zero"),
2389 "/" => from_f64(a / b),
2390 "%" if b == 0.0 => bail!("division by zero"),
2391 "%" => from_f64(a % b),
2392 _ => unreachable!(),
2393 },
2394 _ => Value::Null,
2395 },
2396
2397 other => bail!("unsupported operator {:?}", other),
2398 })
2399}
2400
2401fn eval_func(name: &str, args: &[Expr], row: &Bound) -> Result<Value> {
2407 let arg = |i: usize| -> Result<Value> {
2410 match args.get(i) {
2411 Some(e) => eval(e, row),
2412 None => Ok(Value::Null),
2413 }
2414 };
2415
2416 Ok(match name {
2417 "pg_get_userbyid" | "current_user" | "session_user" | "user" => {
2421 Value::String("nedb".into())
2422 }
2423 "current_schema" => Value::String("public".into()),
2424 "current_database" | "current_catalog" => Value::String("nedb".into()),
2425 "version" => Value::String(crate::pgwire::version_string()),
2426
2427 "pg_table_is_visible" | "pg_type_is_visible" | "pg_function_is_visible"
2432 | "pg_opclass_is_visible" | "pg_conversion_is_visible" => Value::Bool(true),
2433
2434 "pg_encoding_to_char" => Value::String("UTF8".into()),
2436 "pg_get_expr" | "pg_get_indexdef" | "pg_get_constraintdef"
2437 | "pg_get_viewdef" | "pg_get_partkeydef" | "obj_description"
2438 | "col_description" | "shobj_description" => Value::Null,
2439
2440 "lower" => match arg(0)? {
2442 Value::Null => Value::Null,
2443 v => Value::String(as_text(&v).to_lowercase()),
2444 },
2445 "upper" => match arg(0)? {
2446 Value::Null => Value::Null,
2447 v => Value::String(as_text(&v).to_uppercase()),
2448 },
2449 "length" | "char_length" | "character_length" => match arg(0)? {
2450 Value::Null => Value::Null,
2451 v => from_f64(as_text(&v).chars().count() as f64),
2452 },
2453 "format_type" => match arg(0)? {
2454 Value::Null => Value::Null,
2455 v => Value::String(crate::pgcatalog::type_name_pub(
2456 num(&v).unwrap_or(25.0) as i32).to_string()),
2457 },
2458 "array_to_string" | "pg_catalog.array_to_string" => {
2459 match arg(0)? {
2463 Value::Array(items) => {
2464 let sep = as_text(&arg(1)?);
2465 Value::String(
2466 items.iter().map(as_text).collect::<Vec<_>>().join(&sep),
2467 )
2468 }
2469 _ => Value::Null,
2470 }
2471 }
2472 "quote_ident" => Value::String(as_text(&arg(0)?)),
2473 "quote_literal" => Value::String(format!("'{}'", as_text(&arg(0)?).replace('\'', "''"))),
2474 "format" => {
2478 let fmt = as_text(&arg(0)?);
2479 let mut out = String::new();
2480 let mut next = 1usize;
2481 let mut chars = fmt.chars().peekable();
2482 while let Some(c) = chars.next() {
2483 if c != '%' {
2484 out.push(c);
2485 continue;
2486 }
2487 match chars.next() {
2488 Some('%') => out.push('%'),
2489 Some(spec @ ('s' | 'I' | 'L')) => {
2490 let v = arg(next)?;
2491 next += 1;
2492 match (spec, &v) {
2493 ('L', Value::Null) => out.push_str("NULL"),
2494 ('L', v) => out.push_str(&format!("'{}'", as_text(v).replace('\'', "''"))),
2495 (_, v) => out.push_str(&as_text(v)),
2496 }
2497 }
2498 other => bail!("format(): unsupported conversion %{}", other.map(String::from).unwrap_or_default()),
2499 }
2500 }
2501 Value::String(out)
2502 }
2503
2504 "array_length" | "array_upper" | "cardinality" => match arg(0)? {
2508 Value::Array(items) if !items.is_empty() => from_f64(items.len() as f64),
2509 Value::Array(_) if name == "cardinality" => from_f64(0.0),
2510 _ => Value::Null,
2511 },
2512 "array_lower" => match arg(0)? {
2513 Value::Array(items) if !items.is_empty() => from_f64(1.0),
2514 _ => Value::Null,
2515 },
2516
2517 "pg_table_size" | "pg_total_relation_size" | "pg_relation_size"
2524 | "pg_indexes_size" | "pg_database_size" => Value::Null,
2525 "pg_size_pretty" => match num(&arg(0)?) {
2526 None => Value::Null,
2527 Some(n) => {
2528 let units = ["bytes", "kB", "MB", "GB", "TB", "PB"];
2529 let mut v = n;
2530 let mut u = 0usize;
2531 while v.abs() >= 10240.0 && u + 1 < units.len() {
2532 v /= 1024.0;
2533 u += 1;
2534 }
2535 Value::String(format!("{} {}", v.round() as i64, units[u]))
2536 }
2537 },
2538
2539 "pg_get_triggerdef" | "pg_get_ruledef" | "pg_get_statisticsobjdef"
2545 | "pg_get_statisticsobjdef_columns" | "pg_get_function_result"
2546 | "pg_get_function_arguments" | "pg_get_function_identity_arguments"
2547 | "pg_get_functiondef" | "pg_get_serial_sequence" | "pg_get_partition_constraintdef"
2548 | "pg_relation_filepath" | "pg_tablespace_location" => Value::Null,
2549 "pg_relation_is_publishable" => Value::Bool(true),
2550 "pg_statistics_obj_is_visible" | "pg_opfamily_is_visible" | "pg_collation_is_visible"
2551 | "pg_ts_config_is_visible" | "pg_ts_dict_is_visible" | "pg_ts_parser_is_visible"
2552 | "pg_ts_template_is_visible" | "has_table_privilege" | "has_schema_privilege"
2553 | "has_database_privilege" | "pg_has_role" => Value::Bool(true),
2554 "current_setting" => match arg(0)? {
2557 Value::Null => Value::Null,
2558 v => match as_text(&v).to_lowercase().as_str() {
2559 "server_version" => Value::String(crate::pgwire::version_string()),
2560 "server_encoding" | "client_encoding" => Value::String("UTF8".into()),
2561 "standard_conforming_strings" | "integer_datetimes" | "is_superuser" => {
2562 Value::String("on".into())
2563 }
2564 "timezone" | "log_timezone" => Value::String("UTC".into()),
2565 "search_path" => Value::String("\"$user\", public".into()),
2566 "intervalstyle" => Value::String("postgres".into()),
2567 "datestyle" => Value::String("ISO, MDY".into()),
2568 "session_authorization" => Value::String("nedb".into()),
2569 "application_name" | "default_transaction_read_only" => Value::String(String::new()),
2570 "transaction_isolation" | "default_transaction_isolation" => {
2571 Value::String("read committed".into())
2572 }
2573 "max_identifier_length" => Value::String("63".into()),
2574 other => {
2575 if truthy(&arg(1)?) == Some(true) {
2578 Value::Null
2579 } else {
2580 bail!("unrecognized configuration parameter \"{}\"", other)
2581 }
2582 }
2583 },
2584 },
2585 "pg_backend_pid" => from_f64(std::process::id() as f64),
2586 "pg_is_in_recovery" => Value::Bool(false),
2587 "txid_current" => from_f64(0.0),
2588 "now" | "current_timestamp" | "statement_timestamp" | "clock_timestamp" => {
2589 Value::String(now_iso())
2590 }
2591 "to_char" => match arg(0)? {
2592 Value::Null => Value::Null,
2593 v => Value::String(as_text(&v)),
2594 },
2595 "generate_series" | "unnest" => bail!(
2596 "{}() returns a set of rows — write it in FROM, not in the select list", name
2597 ),
2598
2599 "coalesce" => {
2601 let mut out = Value::Null;
2602 for a in args {
2603 let v = eval(a, row)?;
2604 if !v.is_null() {
2605 out = v;
2606 break;
2607 }
2608 }
2609 out
2610 }
2611 "nullif" => {
2612 let a = arg(0)?;
2613 let b = arg(1)?;
2614 if matches!(cmp_values(&a, &b), Some(std::cmp::Ordering::Equal)) {
2615 Value::Null
2616 } else {
2617 a
2618 }
2619 }
2620
2621 "int4" | "int8" | "int2" => match num(&arg(0)?) {
2623 Some(n) => from_f64(n.trunc()),
2624 None => Value::Null,
2625 },
2626 "text" => match arg(0)? {
2627 Value::Null => Value::Null,
2628 v => Value::String(as_text(&v)),
2629 },
2630
2631 other if is_aggregate(other) => bail!(
2632 "{}() is an aggregate, which is only meaningful over a whole result set — \
2633 it is evaluated by the executor, never per row",
2634 other
2635 ),
2636
2637 other => bail!(
2638 "the function {}() is not implemented. It is refused rather than \
2639 answered with NULL, because a NULL column reads as missing DATA \
2640 rather than a missing feature",
2641 other
2642 ),
2643 })
2644}
2645
2646fn now_iso() -> String {
2648 let secs = std::time::SystemTime::now()
2649 .duration_since(std::time::UNIX_EPOCH)
2650 .map(|d| d.as_secs())
2651 .unwrap_or(0);
2652 let days = (secs / 86_400) as i64;
2654 let rem = secs % 86_400;
2655 let z = days + 719_468;
2656 let era = z.div_euclid(146_097);
2657 let doe = z.rem_euclid(146_097);
2658 let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
2659 let y = yoe + era * 400;
2660 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2661 let mp = (5 * doy + 2) / 153;
2662 let d = doy - (153 * mp + 2) / 5 + 1;
2663 let m = if mp < 10 { mp + 3 } else { mp - 9 };
2664 let y = if m <= 2 { y + 1 } else { y };
2665 format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}+00", y, m, d, rem / 3600, (rem % 3600) / 60, rem % 60)
2666}
2667
2668const AGGREGATES: &[&str] = &[
2673 "count", "sum", "avg", "min", "max", "string_agg", "array_agg", "bool_and",
2674 "bool_or", "every",
2675];
2676
2677fn is_aggregate(name: &str) -> bool {
2678 AGGREGATES.iter().any(|a| a.eq_ignore_ascii_case(name))
2679}
2680
2681pub fn has_aggregate(e: &Expr) -> bool {
2684 match e {
2685 Expr::Agg { .. } => true,
2686 Expr::Func { args, .. } => args.iter().any(has_aggregate),
2687 Expr::Binary { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2688 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2689 has_aggregate(expr)
2690 }
2691 Expr::InList { expr, list, .. } => has_aggregate(expr) || list.iter().any(has_aggregate),
2692 Expr::Case { operand, whens, else_ } => {
2693 operand.as_deref().is_some_and(has_aggregate)
2694 || whens.iter().any(|(c, t)| has_aggregate(c) || has_aggregate(t))
2695 || else_.as_deref().is_some_and(has_aggregate)
2696 }
2697 Expr::Quantified { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2698 Expr::Index { expr, index } => has_aggregate(expr) || has_aggregate(index),
2699 Expr::ArrayLit(items) => items.iter().any(has_aggregate),
2700 Expr::InSubquery { expr, .. } => has_aggregate(expr),
2701 Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => false,
2702 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => false,
2703 }
2704}
2705
2706fn aggregate(
2713 name: &str,
2714 args: &[Expr],
2715 order_by: &[OrderBy],
2716 distinct: bool,
2717 rows: &[JoinedRow],
2718 ctx: EvalCtx,
2719) -> Result<Value> {
2720 let lname = name.to_lowercase();
2721
2722 let ordered: Vec<JoinedRow> = if order_by.is_empty() {
2725 rows.to_vec()
2726 } else {
2727 let mut keyed: Vec<(Vec<Value>, JoinedRow)> = Vec::with_capacity(rows.len());
2728 for r in rows {
2729 let b = bind(r, ctx);
2730 let mut key = vec![];
2731 for ob in order_by {
2732 match (&ob.expr, ob.ordinal) {
2735 (Some(e), _) => key.push(eval(e, &b)?),
2736 (None, Some(n)) => bail!(
2737 "ORDER BY {} inside an aggregate refers to a select-list \
2738 position, which an aggregate does not have — name the \
2739 column instead", n
2740 ),
2741 (None, None) => key.push(Value::Null),
2742 }
2743 }
2744 keyed.push((key, r.clone()));
2745 }
2746 keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, order_by));
2747 keyed.into_iter().map(|(_, r)| r).collect()
2748 };
2749 let rows: &[JoinedRow] = &ordered;
2750
2751 if lname == "count" && (args.is_empty() || matches!(args[0], Expr::Star)) {
2754 return Ok(from_f64(rows.len() as f64));
2755 }
2756 let Some(target) = args.first() else {
2757 bail!("{}() needs an argument", name);
2758 };
2759 let mut vals: Vec<Value> = Vec::with_capacity(rows.len());
2760 let mut all_vals: Vec<Value> = Vec::with_capacity(rows.len());
2761 for r in rows {
2762 let v = eval(target, &bind(r, ctx))?;
2763 if !v.is_null() {
2764 vals.push(v.clone());
2765 }
2766 all_vals.push(v);
2767 }
2768 if distinct {
2769 let mut seen: Vec<String> = vec![];
2770 vals.retain(|v| {
2771 let k = format!("{:?}", v);
2772 if seen.contains(&k) { false } else { seen.push(k); true }
2773 });
2774 let mut seen2: Vec<String> = vec![];
2775 all_vals.retain(|v| {
2776 let k = format!("{:?}", v);
2777 if seen2.contains(&k) { false } else { seen2.push(k); true }
2778 });
2779 }
2780 Ok(match lname.as_str() {
2781 "count" => from_f64(vals.len() as f64),
2782 "sum" | "avg" => {
2783 let nums: Vec<f64> = vals.iter().filter_map(num).collect();
2784 if nums.is_empty() {
2785 Value::Null
2786 } else if lname == "sum" {
2787 from_f64(nums.iter().sum())
2788 } else {
2789 from_f64(nums.iter().sum::<f64>() / nums.len() as f64)
2790 }
2791 }
2792 "min" | "max" => {
2793 let mut best: Option<Value> = None;
2794 for v in vals {
2795 best = Some(match best {
2796 None => v,
2797 Some(b) => {
2798 let take = match cmp_values(&v, &b) {
2799 Some(o) if lname == "min" => o.is_lt(),
2800 Some(o) => o.is_gt(),
2801 None => false,
2802 };
2803 if take { v } else { b }
2804 }
2805 });
2806 }
2807 best.unwrap_or(Value::Null)
2808 }
2809 "string_agg" => {
2810 if vals.is_empty() {
2811 Value::Null
2812 } else {
2813 let sep = match args.get(1) {
2816 Some(e) => as_text(&eval(e, &Bound { parts: vec![], ctx })?),
2817 None => String::new(),
2818 };
2819 Value::String(vals.iter().map(as_text).collect::<Vec<_>>().join(&sep))
2820 }
2821 }
2822 "array_agg" => {
2824 if all_vals.is_empty() { Value::Null } else { Value::Array(all_vals) }
2825 }
2826 "bool_and" | "every" => {
2827 if vals.is_empty() {
2828 Value::Null
2829 } else {
2830 Value::Bool(vals.iter().all(|v| truthy(v) == Some(true)))
2831 }
2832 }
2833 "bool_or" => {
2834 if vals.is_empty() {
2835 Value::Null
2836 } else {
2837 Value::Bool(vals.iter().any(|v| truthy(v) == Some(true)))
2838 }
2839 }
2840 _ => unreachable!("is_aggregate gates this"),
2841 })
2842}
2843
2844fn fold_aggregates(
2851 e: &Expr,
2852 rows: &[JoinedRow],
2853 ctx: EvalCtx,
2854 keys: &[Expr],
2855) -> Result<Expr> {
2856 let fold = |x: &Expr| fold_aggregates(x, rows, ctx, keys);
2857 Ok(match e {
2858 Expr::Agg { name, args, order_by, distinct } => {
2859 Expr::Literal(aggregate(name, args, order_by, *distinct, rows, ctx)?)
2860 }
2861 Expr::Func { name, args } => Expr::Func {
2862 name: name.clone(),
2863 args: args.iter().map(&fold).collect::<Result<_>>()?,
2864 },
2865 Expr::Column { .. } if keys.iter().any(|k| k == e) => e.clone(),
2871 Expr::Column { qual, name } => bail!(
2872 "column \"{}{}\" must appear in the GROUP BY clause or be used in an \
2873 aggregate function",
2874 qual.as_ref().map(|q| format!("{q}.")).unwrap_or_default(),
2875 name
2876 ),
2877 Expr::Binary { op, left, right } => Expr::Binary {
2878 op: op.clone(),
2879 left: Box::new(fold(left)?),
2880 right: Box::new(fold(right)?),
2881 },
2882 Expr::Unary { op, expr } => Expr::Unary {
2883 op: op.clone(),
2884 expr: Box::new(fold(expr)?),
2885 },
2886 Expr::Cast { expr, ty } => Expr::Cast {
2887 expr: Box::new(fold(expr)?),
2888 ty: ty.clone(),
2889 },
2890 Expr::IsNull { expr, negated } => Expr::IsNull {
2891 expr: Box::new(fold(expr)?),
2892 negated: *negated,
2893 },
2894 Expr::InList { expr, list, negated } => Expr::InList {
2895 expr: Box::new(fold(expr)?),
2896 list: list.iter().map(&fold).collect::<Result<_>>()?,
2897 negated: *negated,
2898 },
2899 Expr::Case { operand, whens, else_ } => Expr::Case {
2900 operand: match operand {
2901 Some(o) => Some(Box::new(fold(o)?)),
2902 None => None,
2903 },
2904 whens: whens
2905 .iter()
2906 .map(|(c, t)| Ok((fold(c)?, fold(t)?)))
2907 .collect::<Result<_>>()?,
2908 else_: match else_ {
2909 Some(x) => Some(Box::new(fold(x)?)),
2910 None => None,
2911 },
2912 },
2913 Expr::Quantified { op, left, all, right } => Expr::Quantified {
2914 op: op.clone(),
2915 left: Box::new(fold(left)?),
2916 all: *all,
2917 right: Box::new(fold(right)?),
2918 },
2919 Expr::Index { expr, index } => Expr::Index {
2920 expr: Box::new(fold(expr)?),
2921 index: Box::new(fold(index)?),
2922 },
2923 Expr::ArrayLit(items) => Expr::ArrayLit(
2924 items.iter().map(&fold).collect::<Result<_>>()?,
2925 ),
2926 Expr::InSubquery { expr, query, negated } => Expr::InSubquery {
2927 expr: Box::new(fold(expr)?),
2928 query: query.clone(),
2929 negated: *negated,
2930 },
2931 Expr::Star | Expr::QualifiedStar(_) => {
2934 bail!("`*` cannot be mixed with an aggregate outside count(*)")
2935 }
2936 Expr::Literal(_) | Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => {
2938 e.clone()
2939 }
2940 })
2941}
2942
2943fn validate_bindings(sel: &Select) -> Result<()> {
2959 let mut seen: Vec<String> = vec![];
2960 if let Some(f) = &sel.from {
2961 seen.push(f.binding());
2962 }
2963 for j in &sel.joins {
2964 seen.push(j.table.binding());
2965 }
2966 for (i, b) in seen.iter().enumerate() {
2967 if let Some(prev) = seen[..i].iter().find(|p| p.eq_ignore_ascii_case(b)) {
2968 bail!(
2969 "ambiguous relation binding: {:?} appears more than once; use \
2970 aliases (for example `FROM {} JOIN {} AS {}2 ...`)",
2971 prev, prev, prev, prev
2972 );
2973 }
2974 }
2975 Ok(())
2976}
2977
2978#[derive(Debug, Clone, PartialEq, Eq)]
2990pub struct OutCol {
2991 pub key: String,
2992 pub name: String,
2993}
2994
2995fn unique_key(taken: &[OutCol], name: &str) -> String {
2999 if !taken.iter().any(|c| c.key == name) {
3000 return name.to_string();
3001 }
3002 format!("{name}\u{1}{}", taken.len())
3003}
3004
3005type JoinedRow = Vec<(String, Option<Value>)>;
3007
3008fn bind<'a>(row: &'a JoinedRow, ctx: EvalCtx<'a>) -> Bound<'a> {
3009 Bound {
3010 parts: row.iter().map(|(b, v)| (b.clone(), v.as_ref())).collect(),
3011 ctx,
3012 }
3013}
3014
3015fn derived_name(e: &Expr) -> String {
3023 match e {
3024 Expr::Column { name, .. } => name.clone(),
3025 Expr::Func { name, .. } | Expr::Agg { name, .. } => name.clone(),
3026 Expr::Cast { expr, .. } => derived_name(expr),
3027 Expr::Case { .. } => "case".to_string(),
3028 Expr::ArrayQuery(_) | Expr::ArrayLit(_) => "array".to_string(),
3029 Expr::Exists { .. } => "exists".to_string(),
3030 Expr::Subquery(q) => q
3032 .items
3033 .first()
3034 .map(|i| i.alias.clone().unwrap_or_else(|| derived_name(&i.expr)))
3035 .unwrap_or_else(|| "?column?".to_string()),
3036 _ => "?column?".to_string(),
3037 }
3038}
3039
3040pub trait Relation {
3061 fn next_row(&mut self) -> Result<Option<Value>>;
3063
3064 fn size_hint(&self) -> Option<usize> {
3066 None
3067 }
3068}
3069
3070pub struct VecRelation {
3076 iter: std::vec::IntoIter<Value>,
3077 len: usize,
3078}
3079
3080impl Relation for VecRelation {
3081 fn next_row(&mut self) -> Result<Option<Value>> {
3082 Ok(self.iter.next())
3083 }
3084 fn size_hint(&self) -> Option<usize> {
3085 Some(self.len)
3086 }
3087}
3088
3089pub fn from_vec(rows: Vec<Value>) -> Box<dyn Relation> {
3091 let len = rows.len();
3092 Box::new(VecRelation { iter: rows.into_iter(), len })
3093}
3094
3095pub type Resolver<'r> = dyn Fn(&str) -> Result<Option<Box<dyn Relation>>> + 'r;
3101
3102pub fn execute(sel: &Select, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
3107 let (cols, rows, _) = execute_explain(sel, resolve, JoinExec::Auto)?;
3108 Ok((cols, rows))
3109}
3110
3111pub fn execute_explain(
3118 sel: &Select,
3119 resolve: &Resolver,
3120 exec: JoinExec,
3121) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3122 execute_with(sel, resolve, exec, true)
3123}
3124
3125#[derive(Debug, Clone, Copy)]
3130pub struct Opts {
3131 pub exec: JoinExec,
3132 pub pushdown: bool,
3133 pub fuse_filter: bool,
3137}
3138
3139impl Default for Opts {
3140 fn default() -> Self {
3141 Opts { exec: JoinExec::Auto, pushdown: true, fuse_filter: true }
3142 }
3143}
3144
3145impl Opts {
3146 pub fn exec(exec: JoinExec) -> Self {
3147 Opts { exec, ..Default::default() }
3148 }
3149}
3150
3151pub fn execute_with(
3158 sel: &Select,
3159 resolve: &Resolver,
3160 exec: JoinExec,
3161 pushdown: bool,
3162) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3163 execute_opts(sel, resolve, Opts { exec, pushdown, ..Default::default() })
3164}
3165
3166pub fn execute_opts(
3168 sel: &Select,
3169 resolve: &Resolver,
3170 opts: Opts,
3171) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3172 execute_inner(sel, resolve, opts, None)
3173}
3174
3175fn row_key(cols: &[OutCol], obj: &Map<String, Value>) -> String {
3177 cols.iter()
3178 .map(|c| format!("{:?}", obj.get(&c.key).unwrap_or(&Value::Null)))
3179 .collect::<Vec<_>>()
3180 .join("\u{1}")
3181}
3182
3183fn execute_set_ops<'a>(
3191 sel: &Select,
3192 resolve: &'a Resolver<'a>,
3193 opts: Opts,
3194 outer: Option<&'a Bound<'a>>,
3195) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3196 let ctx = EvalCtx { resolver: Some(resolve), outer };
3197 let mut head = sel.clone();
3198 head.set_ops.clear();
3199 head.order_by.clear();
3200 head.limit = None;
3201 head.offset = None;
3202 let (cols, rows, mut plan) = execute_inner(&head, resolve, opts, outer)?;
3203 let mut left: Vec<Map<String, Value>> = rows
3204 .into_iter()
3205 .map(|r| match r {
3206 Value::Object(m) => m,
3207 _ => Map::new(),
3208 })
3209 .collect();
3210
3211 for arm in &sel.set_ops {
3212 let (acols, arows, _) = execute_inner(&arm.query, resolve, opts, outer)?;
3213 let op_name = match arm.op {
3214 SetOp::Union => "UNION",
3215 SetOp::Intersect => "INTERSECT",
3216 SetOp::Except => "EXCEPT",
3217 };
3218 if acols.len() != cols.len() {
3219 bail!(
3220 "each {} query must have the same number of columns: {} vs {}",
3221 op_name, cols.len(), acols.len()
3222 );
3223 }
3224 let right: Vec<Map<String, Value>> = arows
3226 .into_iter()
3227 .map(|r| {
3228 let m = match r {
3229 Value::Object(m) => m,
3230 _ => Map::new(),
3231 };
3232 let mut out = Map::new();
3233 for (i, c) in cols.iter().enumerate() {
3234 out.insert(c.key.clone(), m.get(&acols[i].key).cloned().unwrap_or(Value::Null));
3235 }
3236 out
3237 })
3238 .collect();
3239 let (nl, nr) = (left.len(), right.len());
3240 let right_keys: std::collections::HashSet<String> =
3241 right.iter().map(|m| row_key(&cols, m)).collect();
3242 let mut combined: Vec<Map<String, Value>> = match arm.op {
3243 SetOp::Union => {
3244 left.extend(right);
3245 left
3246 }
3247 SetOp::Intersect => left.into_iter().filter(|m| right_keys.contains(&row_key(&cols, m))).collect(),
3248 SetOp::Except => left.into_iter().filter(|m| !right_keys.contains(&row_key(&cols, m))).collect(),
3249 };
3250 if !arm.all {
3251 let mut seen = std::collections::HashSet::new();
3252 combined.retain(|m| seen.insert(row_key(&cols, m)));
3253 }
3254 plan.notes.push(format!(
3255 "{}{}: {} + {} rows -> {} (each arm planned separately; only the first arm's plan is shown)",
3256 op_name, if arm.all { " ALL" } else { "" }, nl, nr, combined.len()
3257 ));
3258 left = combined;
3259 }
3260
3261 let projected: Vec<(Map<String, Value>, JoinedRow)> = left
3264 .into_iter()
3265 .map(|m| {
3266 let src: JoinedRow = vec![(String::new(), Some(Value::Object(m.clone())))];
3267 (m, src)
3268 })
3269 .collect();
3270 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3271 Ok((cols, out, plan))
3272}
3273
3274fn execute_inner<'a>(
3277 sel: &Select,
3278 resolve: &'a Resolver<'a>,
3279 opts: Opts,
3280 outer: Option<&'a Bound<'a>>,
3281) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3282 if !sel.set_ops.is_empty() {
3283 return execute_set_ops(sel, resolve, opts, outer);
3284 }
3285 let ctx = EvalCtx { resolver: Some(resolve), outer };
3286 let exec = opts.exec;
3287 let pushdown = opts.pushdown;
3288 let mut plan = Plan::default();
3289
3290 validate_bindings(sel)?;
3292
3293 let fuse = opts.fuse_filter && sel.where_.is_some() && !sel.joins.is_empty();
3320
3321 let budget: Option<usize> = match sel.limit {
3322 Some(lim)
3323 if sel.order_by.is_empty()
3324 && !sel.distinct
3325 && !sel.joins.is_empty()
3326 && (sel.where_.is_none() || fuse) =>
3327 {
3328 Some(lim.saturating_add(sel.offset.unwrap_or(0)))
3329 }
3330 _ => None,
3331 };
3332 plan.budget = budget;
3333
3334 let all_bindings: Vec<String> = sel
3340 .from
3341 .iter()
3342 .map(|t| t.binding())
3343 .chain(sel.joins.iter().map(|j| j.table.binding()))
3344 .collect();
3345 let nullable = crate::sqlpush::nullable_bindings(sel);
3346 let push = if pushdown {
3347 crate::sqlpush::plan(sel.where_.as_ref(), &all_bindings, &nullable)
3348 } else {
3349 Pushdown::default()
3350 };
3351 plan.refusals = push.refusals.clone();
3352
3353 let mut base_scan_at: Option<usize> = None;
3354 let mut base_prefilter_at: Option<usize> = None;
3355
3356 let mut left_src: Box<dyn LeftSource + 'a> = match &sel.from {
3364 None => {
3365 Box::new(VecLeft { rows: vec![vec![]], at: 0 })
3368 }
3369 Some(t) => {
3370 let rel = fetch(t, resolve, ctx)?;
3371 let binding = t.binding();
3372 base_scan_at = Some(plan.stages.len());
3377 plan.push(Stage::Scan {
3378 table: t.name.clone(),
3379 binding: binding.clone(),
3380 rows: 0,
3381 });
3382 let preds = push.for_binding(&binding).cloned().unwrap_or_default();
3383 if !preds.is_empty() {
3384 base_prefilter_at = Some(plan.stages.len());
3385 plan.push(Stage::Prefilter {
3386 binding: binding.clone(),
3387 predicates: preds.len(),
3388 in_rows: 0,
3389 out_rows: 0,
3390 });
3391 }
3392 Box::new(StreamLeft { rel, binding, preds, pulled: 0, kept: 0, ctx })
3393 }
3394 };
3395
3396 let mut left_bindings: Vec<String> = match &sel.from {
3402 None => vec![],
3403 Some(t) => vec![t.binding()],
3404 };
3405 let last = sel.joins.len().saturating_sub(1);
3406 let mut rows: Vec<JoinedRow> = vec![];
3407 let mut base_pulled: Option<usize> = None;
3408 let mut base_kept: Option<usize> = None;
3409
3410 for (ji, join) in sel.joins.iter().enumerate() {
3411 let is_last = ji == last;
3412 let rb = join.table.binding();
3413
3414 if join.table.lateral {
3418 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3419 let join_budget = if is_last { budget } else { None };
3420 let (out, removed, consumed, produced) = join_lateral(
3421 left_src.as_mut(), join, resolve, join_budget, post, ctx,
3422 )?;
3423 plan.push(Stage::Scan { table: join.table.name.clone(), binding: rb.clone(), rows: produced });
3424 plan.push(Stage::Join {
3425 kind: join.kind,
3426 table: join.table.name.clone(),
3427 binding: rb.clone(),
3428 strategy: Strategy::NestedLoop,
3429 keys: 0,
3430 left_rows: consumed,
3431 right_rows: produced,
3432 out_rows: out.len(),
3433 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3434 post_filter_removed: post.map(|_| removed),
3435 });
3436 plan.notes.push(format!("LATERAL {}: the subquery ran once per left row ({} times)", rb, consumed));
3437 left_bindings.push(rb);
3438 if ji == 0 {
3439 if let Some((pulled, kept)) = left_src.stats() {
3440 base_pulled = Some(pulled);
3441 base_kept = Some(kept);
3442 }
3443 }
3444 left_src = Box::new(VecLeft { rows: out, at: 0 });
3445 continue;
3446 }
3447
3448 let right_rel = fetch(&join.table, resolve, ctx)?;
3449 let right_all = drain(right_rel)?;
3450 plan.push(Stage::Scan {
3451 table: join.table.name.clone(),
3452 binding: rb.clone(),
3453 rows: right_all.len(),
3454 });
3455 let right_rows = prefilter(right_all, &rb, &push, &mut plan, ctx)?;
3456
3457 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3462 let join_budget = if is_last { budget } else { None };
3463
3464 let keys = sqljoin::hash_keys(join.on.as_ref(), &left_bindings, &rb);
3467 let left_hint = left_src.hint().unwrap_or(usize::MAX);
3468 let strategy = sqljoin::choose(exec, keys.len(), left_hint, right_rows.len());
3469
3470 let (out, removed, consumed) = match strategy {
3471 Strategy::NestedLoop => join_nested_loop(
3472 left_src.as_mut(), &left_bindings, join, &right_rows, &rb,
3473 join_budget, post, ctx,
3474 )?,
3475 Strategy::Hash => join_hash(
3476 left_src.as_mut(), &left_bindings, join, &right_rows, &rb, &keys,
3477 join_budget, post, ctx,
3478 )?,
3479 };
3480
3481 plan.push(Stage::Join {
3482 kind: join.kind,
3483 table: join.table.name.clone(),
3484 binding: rb.clone(),
3485 strategy,
3486 keys: keys.len(),
3487 left_rows: consumed,
3488 right_rows: right_rows.len(),
3489 out_rows: out.len(),
3490 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3491 post_filter_removed: post.map(|_| removed),
3492 });
3493 left_bindings.push(rb);
3494 if ji == 0 {
3496 if let Some((pulled, kept)) = left_src.stats() {
3497 base_pulled = Some(pulled);
3498 base_kept = Some(kept);
3499 }
3500 }
3501 rows = out;
3502 left_src = Box::new(VecLeft { rows: std::mem::take(&mut rows), at: 0 });
3504 }
3505
3506 rows = left_src.take_rows();
3509 if let Some(i) = base_scan_at {
3510 if let (Some(pulled), Some(kept)) = (base_pulled, base_kept) {
3511 if let Some(Stage::Scan { rows: r, .. }) = plan.stages.get_mut(i) {
3512 *r = pulled;
3513 }
3514 if let Some(j) = base_prefilter_at {
3515 if let Some(Stage::Prefilter { in_rows, out_rows, .. }) =
3516 plan.stages.get_mut(j)
3517 {
3518 *in_rows = pulled;
3519 *out_rows = kept;
3520 }
3521 }
3522 }
3523 }
3524
3525 if let Some(pred) = sel.where_.as_ref().filter(|_| !fuse) {
3527 let in_rows = rows.len();
3528 let mut kept = Vec::with_capacity(rows.len());
3529 for r in rows {
3530 if truthy(&eval(pred, &bind(&r, ctx))?) == Some(true) {
3534 kept.push(r);
3535 }
3536 }
3537 rows = kept;
3538 plan.push(Stage::Filter { in_rows, out_rows: rows.len() });
3539 }
3540
3541 let grouping = !sel.group_by.is_empty();
3554 let aggregating = grouping
3555 || sel.items.iter().any(|i| has_aggregate(&i.expr))
3556 || sel.having.as_ref().is_some_and(has_aggregate);
3557 if aggregating {
3558 let mut groups: Vec<(Vec<Value>, Vec<JoinedRow>)> = vec![];
3562 if grouping {
3563 for r in rows {
3564 let b = bind(&r, ctx);
3565 let mut key = Vec::with_capacity(sel.group_by.len());
3566 for g in &sel.group_by {
3567 key.push(eval(g, &b)?);
3568 }
3569 match groups.iter_mut().find(|(k, _)| {
3570 k.len() == key.len()
3571 && k.iter().zip(&key).all(|(a, b)| {
3572 (a.is_null() && b.is_null())
3575 || matches!(cmp_values(a, b), Some(std::cmp::Ordering::Equal))
3576 })
3577 }) {
3578 Some((_, bucket)) => bucket.push(r),
3579 None => groups.push((key, vec![r])),
3580 }
3581 }
3582 } else {
3583 groups.push((vec![], rows));
3586 }
3587
3588 let n_groups = groups.len();
3589 let mut cols: Vec<OutCol> = vec![];
3590 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = vec![];
3591 let empty: JoinedRow = vec![];
3592
3593 for (gi, (_key, grows)) in groups.iter().enumerate() {
3594 let scope = grows.first().unwrap_or(&empty);
3599
3600 if let Some(h) = &sel.having {
3601 let folded = fold_aggregates(h, grows, ctx, &sel.group_by)?;
3602 if truthy(&eval(&folded, &bind(scope, ctx))?) != Some(true) {
3603 continue;
3604 }
3605 }
3606
3607 let mut obj = Map::new();
3608 for item in &sel.items {
3609 let folded = fold_aggregates(&item.expr, grows, ctx, &sel.group_by)?;
3610 let v = eval(&folded, &bind(scope, ctx))?;
3611 if gi == 0 {
3613 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3614 let key = unique_key(&cols, &name);
3615 obj.insert(key.clone(), v);
3616 cols.push(OutCol { key, name });
3617 } else {
3618 let idx = obj.len();
3619 if let Some(c) = cols.get(idx) {
3620 obj.insert(c.key.clone(), v);
3621 }
3622 }
3623 }
3624 projected.push((obj, scope.clone()));
3625 }
3626
3627 plan.notes.push(if grouping {
3628 format!(
3629 "GroupAggregate on {} key(s): {} rows -> {} group(s){}",
3630 sel.group_by.len(),
3631 n_rows_before_group(&plan),
3632 n_groups,
3633 if sel.having.is_some() {
3634 format!(", HAVING kept {}", projected.len())
3635 } else {
3636 String::new()
3637 }
3638 )
3639 } else {
3640 format!("Aggregate over {} row(s) -> 1 row", groups[0].1.len())
3641 });
3642 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3643 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3644 return Ok((cols, out, plan));
3645 }
3646
3647 let mut cols: Vec<OutCol> = vec![];
3678 let mut spans: Vec<(usize, usize)> = Vec::with_capacity(sel.items.len());
3679 for item in &sel.items {
3680 let start = cols.len();
3681 match &item.expr {
3682 Expr::Star => {
3683 let mut plain: Vec<String> = vec![];
3684 let mut meta: Vec<String> = vec![];
3685 for r in &rows {
3686 for (n, _) in bind(r, ctx).flatten() {
3687 let target = if n.starts_with('_') { &mut meta } else { &mut plain };
3688 if !target.contains(&n) {
3690 target.push(n);
3691 }
3692 }
3693 }
3694 meta.sort();
3697 for n in plain.into_iter().chain(meta) {
3698 if !cols.iter().any(|c| c.name == n) {
3699 cols.push(OutCol { key: n.clone(), name: n });
3700 }
3701 }
3702 }
3703 Expr::QualifiedStar(q) => {
3704 if let Some(first) = rows.first() {
3705 for (n, _) in bind(first, ctx).flatten_binding(q) {
3706 if !cols.iter().any(|c| c.name == n) {
3707 cols.push(OutCol { key: n.clone(), name: n });
3708 }
3709 }
3710 }
3711 }
3712 _ => {
3713 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3714 let key = unique_key(&cols, &name);
3720 cols.push(OutCol { key, name });
3721 }
3722 }
3723 spans.push((start, cols.len()));
3724 }
3725
3726 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = Vec::with_capacity(rows.len());
3730 for r in rows {
3731 let b = bind(&r, ctx);
3732 let mut obj = Map::new();
3733 for (i, item) in sel.items.iter().enumerate() {
3734 let (start, end) = spans[i];
3735 match &item.expr {
3736 Expr::Star => {
3737 for (n, v) in b.flatten() {
3738 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3739 obj.entry(c.key.clone()).or_insert(v);
3740 }
3741 }
3742 }
3743 Expr::QualifiedStar(q) => {
3744 for (n, v) in b.flatten_binding(q) {
3745 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3746 obj.entry(c.key.clone()).or_insert(v);
3747 }
3748 }
3749 }
3750 _ => {
3751 let v = eval(&item.expr, &b)?;
3752 if let Some(c) = cols.get(start) {
3753 obj.insert(c.key.clone(), v);
3754 }
3755 }
3756 }
3757 }
3758 projected.push((obj, r));
3759 }
3760
3761 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3762
3763 if sel.distinct {
3765 let in_rows = projected.len();
3766 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
3767 projected.retain(|(obj, _)| seen.insert(row_key(&cols, obj)));
3770 plan.push(Stage::Distinct { in_rows, out_rows: projected.len() });
3771 }
3772
3773 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3774 Ok((cols, out, plan))
3775}
3776
3777fn n_rows_before_group(plan: &Plan) -> usize {
3781 plan.stages
3782 .iter()
3783 .rev()
3784 .find_map(|st| match st {
3785 Stage::Filter { out_rows, .. } => Some(*out_rows),
3786 Stage::Join { out_rows, .. } => Some(*out_rows),
3787 Stage::Prefilter { out_rows, .. } => Some(*out_rows),
3788 Stage::Scan { rows, .. } => Some(*rows),
3789 _ => None,
3790 })
3791 .unwrap_or(0)
3792}
3793
3794fn sort_keys(a: &[Value], b: &[Value], order_by: &[OrderBy]) -> std::cmp::Ordering {
3801 for (i, ob) in order_by.iter().enumerate() {
3802 let (Some(x), Some(y)) = (a.get(i), b.get(i)) else { continue };
3803 let ord = match (x.is_null(), y.is_null()) {
3804 (true, true) => std::cmp::Ordering::Equal,
3805 (true, false) => {
3808 return if ob.nulls_first {
3809 std::cmp::Ordering::Less
3810 } else {
3811 std::cmp::Ordering::Greater
3812 }
3813 }
3814 (false, true) => {
3815 return if ob.nulls_first {
3816 std::cmp::Ordering::Greater
3817 } else {
3818 std::cmp::Ordering::Less
3819 }
3820 }
3821 (false, false) => cmp_values(x, y).unwrap_or(std::cmp::Ordering::Equal),
3822 };
3823 let ord = if matches!(ob.dir, Dir::Desc) { ord.reverse() } else { ord };
3824 if !ord.is_eq() {
3825 return ord;
3826 }
3827 }
3828 std::cmp::Ordering::Equal
3829}
3830
3831fn finish(
3833 sel: &Select,
3834 cols: &[OutCol],
3835 mut projected: Vec<(Map<String, Value>, JoinedRow)>,
3836 ctx: EvalCtx,
3837 plan: &mut Plan,
3838) -> Result<Vec<Value>> {
3839 if !sel.order_by.is_empty() {
3841 let mut keyed: Vec<(Vec<Value>, (Map<String, Value>, JoinedRow))> = vec![];
3845 for (obj, src) in projected {
3846 let mut key = vec![];
3847 for ob in &sel.order_by {
3848 let v = match (ob.ordinal, &ob.expr) {
3849 (Some(n), _) => {
3850 let c = cols.get(n - 1).ok_or_else(|| {
3851 anyhow::anyhow!(
3852 "ORDER BY {} is out of range: the select list has {} \
3853 column(s)", n, cols.len())
3854 })?;
3855 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3856 }
3857 (None, Some(Expr::Column { qual: None, name }))
3862 if cols.iter().any(|c| c.name == *name) =>
3863 {
3864 let c = cols.iter().find(|c| c.name == *name).expect("checked");
3865 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3866 }
3867 (None, Some(e)) => {
3868 eval(e, &bind(&src, ctx))?
3872 }
3873 (None, None) => Value::Null,
3874 };
3875 key.push(v);
3876 }
3877 keyed.push((key, (obj, src)));
3878 }
3879
3880 keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, &sel.order_by));
3881
3882 projected = keyed.into_iter().map(|(_, row)| row).collect();
3883 plan.push(Stage::Sort { keys: sel.order_by.len(), rows: projected.len() });
3884 }
3885
3886 let mut out: Vec<Value> = projected
3888 .into_iter()
3889 .map(|(obj, _)| Value::Object(obj))
3890 .collect();
3891 let in_rows = out.len();
3892 if let Some(off) = sel.offset {
3893 out = if off >= out.len() { vec![] } else { out.split_off(off) };
3894 }
3895 if let Some(lim) = sel.limit {
3896 out.truncate(lim);
3897 }
3898 if sel.limit.is_some() || sel.offset.is_some() {
3899 plan.push(Stage::Limit {
3900 limit: sel.limit,
3901 offset: sel.offset,
3902 in_rows,
3903 out_rows: out.len(),
3904 });
3905 }
3906
3907 Ok(out)
3908}
3909
3910fn keep_row(cand: &JoinedRow, post: Option<&Expr>, removed: &mut usize, ctx: EvalCtx) -> Result<bool> {
3943 let Some(p) = post else { return Ok(true) };
3944 if truthy(&eval(p, &bind(cand, ctx))?) == Some(true) {
3946 Ok(true)
3947 } else {
3948 *removed += 1;
3949 Ok(false)
3950 }
3951}
3952
3953#[allow(clippy::too_many_arguments)]
3959fn emit_unmatched_right(
3960 out: &mut Vec<JoinedRow>,
3961 kind: JoinKind,
3962 left_bindings: &[String],
3963 right_rows: &[Value],
3964 right_matched: &[bool],
3965 rb: &str,
3966 post: Option<&Expr>,
3967 removed: &mut usize,
3968 ctx: EvalCtx,
3969) -> Result<()> {
3970 if !matches!(kind, JoinKind::Right | JoinKind::Full) {
3971 return Ok(());
3972 }
3973 for (ri, right) in right_rows.iter().enumerate() {
3974 if right_matched[ri] {
3975 continue;
3976 }
3977 let mut cand: JoinedRow = left_bindings.iter().map(|b| (b.clone(), None)).collect();
3978 cand.push((rb.to_string(), Some(right.clone())));
3979 if keep_row(&cand, post, removed, ctx)? {
3982 out.push(cand);
3983 }
3984 }
3985 Ok(())
3986}
3987
3988fn join_lateral(
3995 left_src: &mut dyn LeftSource,
3996 join: &Join,
3997 resolve: &Resolver,
3998 budget: Option<usize>,
3999 post: Option<&Expr>,
4000 ctx: EvalCtx,
4001) -> Result<(Vec<JoinedRow>, usize, usize, usize)> {
4002 let sub = join
4003 .table
4004 .sub
4005 .as_deref()
4006 .ok_or_else(|| anyhow::anyhow!("LATERAL requires a subquery"))?;
4007 let rb = join.table.binding();
4008 let mut out: Vec<JoinedRow> = vec![];
4009 let mut removed = 0usize;
4010 let mut consumed = 0usize;
4011 let mut produced = 0usize;
4012 while let Some(left) = {
4013 if budget.is_some_and(|b| out.len() >= b) { None } else { left_src.next_left()? }
4014 } {
4015 consumed += 1;
4016 let scope = bind(&left, ctx);
4017 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), Some(&scope))?;
4018 produced += rows.len();
4019 let mut matched = false;
4020 for r in rows {
4021 let m = match r {
4022 Value::Object(m) => m,
4023 _ => Map::new(),
4024 };
4025 let mut named = Map::new();
4026 for (i, c) in cols.iter().enumerate() {
4027 let name = join.table.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
4028 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
4029 }
4030 let mut cand = left.clone();
4031 cand.push((rb.clone(), Some(Value::Object(named))));
4032 let on_ok = match &join.on {
4033 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4034 None => true,
4035 };
4036 if !on_ok {
4037 continue;
4038 }
4039 matched = true;
4040 if keep_row(&cand, post, &mut removed, ctx)? {
4041 out.push(cand);
4042 }
4043 }
4044 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4045 let mut cand = left.clone();
4046 cand.push((rb.clone(), None));
4047 if keep_row(&cand, post, &mut removed, ctx)? {
4048 out.push(cand);
4049 }
4050 }
4051 }
4052 Ok((out, removed, consumed, produced))
4053}
4054
4055#[allow(clippy::too_many_arguments)]
4061fn join_nested_loop(
4062 left_src: &mut dyn LeftSource,
4063 left_bindings: &[String],
4064 join: &Join,
4065 right_rows: &[Value],
4066 rb: &str,
4067 budget: Option<usize>,
4068 post: Option<&Expr>,
4069 ctx: EvalCtx,
4070) -> Result<(Vec<JoinedRow>, usize, usize)> {
4071 let mut out: Vec<JoinedRow> = vec![];
4072 let mut removed = 0usize;
4073 let mut right_matched = vec![false; right_rows.len()];
4075
4076 let mut consumed = 0usize;
4077 while let Some(left) = {
4078 if budget.is_some_and(|b| out.len() >= b) {
4079 None
4082 } else {
4083 left_src.next_left()?
4084 }
4085 } {
4086 consumed += 1;
4087 let left = &left;
4088 let mut matched = false;
4091 for (ri, right) in right_rows.iter().enumerate() {
4092 let mut cand: JoinedRow = left.clone();
4093 cand.push((rb.to_string(), Some(right.clone())));
4094 let joins_here = match &join.on {
4095 None => true,
4097 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4101 };
4102 if joins_here {
4103 matched = true;
4104 right_matched[ri] = true;
4105 if keep_row(&cand, post, &mut removed, ctx)? {
4106 out.push(cand);
4107 }
4108 }
4109 }
4110 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4112 let mut cand: JoinedRow = left.clone();
4113 cand.push((rb.to_string(), None));
4114 if keep_row(&cand, post, &mut removed, ctx)? {
4115 out.push(cand);
4116 }
4117 }
4118 }
4119
4120 if !budget.is_some_and(|b| out.len() >= b) {
4125 emit_unmatched_right(
4126 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4127 &mut removed, ctx,
4128 )?;
4129 }
4130 Ok((out, removed, consumed))
4131}
4132
4133#[allow(clippy::too_many_arguments)]
4141fn join_hash(
4142 left_src: &mut dyn LeftSource,
4143 left_bindings: &[String],
4144 join: &Join,
4145 right_rows: &[Value],
4146 rb: &str,
4147 keys: &[(Expr, Expr)],
4148 budget: Option<usize>,
4149 post: Option<&Expr>,
4150 ctx: EvalCtx,
4151) -> Result<(Vec<JoinedRow>, usize, usize)> {
4152 debug_assert!(!keys.is_empty(), "the planner must not choose Hash with no keys");
4153
4154 let side = sqljoin::HashSide::build(right_rows.len(), |i| {
4156 let one: JoinedRow = vec![(rb.to_string(), Some(right_rows[i].clone()))];
4159 let b = bind(&one, ctx);
4160 let mut k = Vec::with_capacity(keys.len());
4161 for (_, right_expr) in keys {
4162 match sqljoin::hkey(&eval(right_expr, &b)?) {
4163 Some(h) => k.push(h),
4164 None => return Ok(None),
4166 }
4167 }
4168 Ok(Some(k))
4169 })?;
4170
4171 let mut out: Vec<JoinedRow> = vec![];
4173 let mut removed = 0usize;
4174 let mut right_matched = vec![false; right_rows.len()];
4175
4176 let mut consumed = 0usize;
4177 while let Some(left) = {
4178 if budget.is_some_and(|b| out.len() >= b) {
4179 None
4180 } else {
4181 left_src.next_left()?
4182 }
4183 } {
4184 consumed += 1;
4185 let left = &left;
4186 let lb = bind(left, ctx);
4187 let mut lk = Vec::with_capacity(keys.len());
4188 let mut null_key = false;
4189 for (left_expr, _) in keys {
4190 match sqljoin::hkey(&eval(left_expr, &lb)?) {
4191 Some(h) => lk.push(h),
4192 None => {
4193 null_key = true;
4194 break;
4195 }
4196 }
4197 }
4198
4199 let mut matched = false;
4200 if !null_key {
4204 for &ri in side.probe(&lk) {
4205 let mut cand: JoinedRow = left.clone();
4206 cand.push((rb.to_string(), Some(right_rows[ri].clone())));
4207 let joins_here = match &join.on {
4209 None => true,
4210 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4211 };
4212 if joins_here {
4213 matched = true;
4214 right_matched[ri] = true;
4215 if keep_row(&cand, post, &mut removed, ctx)? {
4216 out.push(cand);
4217 }
4218 }
4219 }
4220 }
4221 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4222 let mut cand: JoinedRow = left.clone();
4223 cand.push((rb.to_string(), None));
4224 if keep_row(&cand, post, &mut removed, ctx)? {
4225 out.push(cand);
4226 }
4227 }
4228 }
4229
4230 if !budget.is_some_and(|b| out.len() >= b) {
4233 emit_unmatched_right(
4234 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4235 &mut removed, ctx,
4236 )?;
4237 }
4238 Ok((out, removed, consumed))
4239}
4240
4241fn prefilter(
4248 rows: Vec<Value>,
4249 binding: &str,
4250 push: &Pushdown,
4251 plan: &mut Plan,
4252 ctx: EvalCtx,
4253) -> Result<Vec<Value>> {
4254 let Some(preds) = push.for_binding(binding) else { return Ok(rows) };
4255 if preds.is_empty() {
4256 return Ok(rows);
4257 }
4258 let in_rows = rows.len();
4259 let mut kept = Vec::with_capacity(rows.len());
4260 for row in rows {
4261 let one: JoinedRow = vec![(binding.to_string(), Some(row))];
4262 let b = bind(&one, ctx);
4263 let mut keep = true;
4264 for p in preds {
4265 if truthy(&eval(p, &b)?) != Some(true) {
4271 keep = false;
4272 break;
4273 }
4274 }
4275 if keep {
4276 if let Some((_, Some(v))) = one.into_iter().next() {
4278 kept.push(v);
4279 }
4280 }
4281 }
4282 plan.push(Stage::Prefilter {
4283 binding: binding.to_string(),
4284 predicates: preds.len(),
4285 in_rows,
4286 out_rows: kept.len(),
4287 });
4288 Ok(kept)
4289}
4290
4291fn fetch(t: &TableRef, resolve: &Resolver, ctx: EvalCtx) -> Result<Box<dyn Relation>> {
4294 if let Some(sub) = &t.sub {
4298 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), ctx.outer)?;
4299 let out = rows
4300 .into_iter()
4301 .map(|r| {
4302 let m = match r {
4303 Value::Object(m) => m,
4304 _ => Map::new(),
4305 };
4306 let mut named = Map::new();
4307 for (i, c) in cols.iter().enumerate() {
4308 let name = t.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
4309 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
4312 }
4313 Value::Object(named)
4314 })
4315 .collect();
4316 return Ok(from_vec(out));
4317 }
4318
4319 if let Some(args) = &t.args {
4323 let empty: JoinedRow = vec![];
4324 let scope = bind(&empty, ctx);
4325 let col = |i: usize, default: &str| -> String {
4326 t.col_aliases.get(i).cloned().unwrap_or_else(|| default.to_string())
4327 };
4328 let rows: Vec<Value> = match t.name.as_str() {
4329 "generate_series" => {
4330 let a = num(&eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_series() needs a start"))?, &scope)?);
4331 let b = num(&eval(args.get(1).ok_or_else(|| anyhow::anyhow!("generate_series() needs a stop"))?, &scope)?);
4332 let step = match args.get(2) {
4333 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4334 None => 1.0,
4335 };
4336 match (a, b) {
4337 (Some(a), Some(b)) if step != 0.0 => {
4339 let mut out = vec![];
4340 let mut x = a;
4341 while (step > 0.0 && x <= b) || (step < 0.0 && x >= b) {
4342 let mut m = Map::new();
4343 m.insert(col(0, "generate_series"), from_f64(x));
4344 out.push(Value::Object(m));
4345 x += step;
4346 if out.len() > 1_000_000 {
4347 bail!("generate_series() would produce more than a million rows");
4348 }
4349 }
4350 out
4351 }
4352 (Some(_), Some(_)) => bail!("generate_series() step cannot equal zero"),
4353 _ => vec![],
4354 }
4355 }
4356 "unnest" => match eval(args.first().ok_or_else(|| anyhow::anyhow!("unnest() needs an array"))?, &scope)? {
4357 Value::Array(items) => items
4358 .into_iter()
4359 .map(|v| {
4360 let mut m = Map::new();
4361 m.insert(col(0, "unnest"), v);
4362 Value::Object(m)
4363 })
4364 .collect(),
4365 _ => vec![],
4367 },
4368 "generate_subscripts" => {
4373 let dim = match args.get(1) {
4374 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4375 None => 1.0,
4376 };
4377 match eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_subscripts() needs an array"))?, &scope)? {
4378 Value::Array(items) if dim == 1.0 => (1..=items.len())
4382 .map(|i| {
4383 let mut m = Map::new();
4384 m.insert(col(0, "generate_subscripts"), from_f64(i as f64));
4385 Value::Object(m)
4386 })
4387 .collect(),
4388 _ => vec![],
4389 }
4390 }
4391 "pg_partition_tree" | "pg_partition_ancestors" => vec![],
4394 other => bail!(
4395 "the table function {}() is not implemented. It is refused rather \
4396 than answered with no rows, because an empty relation reads as \
4397 missing DATA rather than a missing feature",
4398 other
4399 ),
4400 };
4401 return Ok(from_vec(rows));
4402 }
4403
4404 match resolve(&t.name)? {
4405 Some(rel) => Ok(rel),
4406 None => bail!("relation {:?} does not exist", t.name),
4409 }
4410}
4411
4412trait LeftSource {
4419 fn next_left(&mut self) -> Result<Option<JoinedRow>>;
4420 fn hint(&self) -> Option<usize>;
4422 fn take_rows(&mut self) -> Vec<JoinedRow>;
4424 fn stats(&self) -> Option<(usize, usize)> {
4426 None
4427 }
4428}
4429
4430struct StreamLeft<'a> {
4436 rel: Box<dyn Relation>,
4437 binding: String,
4438 preds: Vec<Expr>,
4439 ctx: EvalCtx<'a>,
4440 pulled: usize,
4444 kept: usize,
4445}
4446
4447impl<'a> LeftSource for StreamLeft<'a> {
4448 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4449 while let Some(row) = self.rel.next_row()? {
4450 self.pulled += 1;
4451 let one: JoinedRow = vec![(self.binding.clone(), Some(row))];
4452 if !self.preds.is_empty() {
4453 let b = bind(&one, self.ctx);
4454 let mut keep = true;
4455 for p in &self.preds {
4456 if truthy(&eval(p, &b)?) != Some(true) {
4457 keep = false;
4458 break;
4459 }
4460 }
4461 if !keep {
4462 continue;
4463 }
4464 }
4465 self.kept += 1;
4466 return Ok(Some(one));
4467 }
4468 Ok(None)
4469 }
4470 fn hint(&self) -> Option<usize> {
4471 self.rel.size_hint()
4476 }
4477 fn take_rows(&mut self) -> Vec<JoinedRow> {
4478 let mut out = vec![];
4481 while let Ok(Some(r)) = self.next_left() {
4482 out.push(r);
4483 }
4484 out
4485 }
4486 fn stats(&self) -> Option<(usize, usize)> {
4487 Some((self.pulled, self.kept))
4488 }
4489}
4490
4491struct VecLeft {
4494 rows: Vec<JoinedRow>,
4495 at: usize,
4496}
4497
4498impl LeftSource for VecLeft {
4499 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4500 let r = self.rows.get(self.at).cloned();
4501 if r.is_some() {
4502 self.at += 1;
4503 }
4504 Ok(r)
4505 }
4506 fn hint(&self) -> Option<usize> {
4507 Some(self.rows.len().saturating_sub(self.at))
4508 }
4509 fn take_rows(&mut self) -> Vec<JoinedRow> {
4510 let mut v = std::mem::take(&mut self.rows);
4511 if self.at > 0 {
4512 v = v.split_off(self.at);
4513 }
4514 self.at = 0;
4515 v
4516 }
4517}
4518
4519fn drain(mut rel: Box<dyn Relation>) -> Result<Vec<Value>> {
4526 let mut out = Vec::with_capacity(rel.size_hint().unwrap_or(0));
4527 while let Some(row) = rel.next_row()? {
4528 out.push(row);
4529 }
4530 Ok(out)
4531}
4532
4533pub fn run(sql: &str, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
4535 let sel = parse(sql)?;
4536 execute(&sel, resolve)
4537}
4538
4539#[cfg(test)]
4540mod lexer_tests {
4541 use super::*;
4542
4543 fn kinds(src: &str) -> Vec<Tok> {
4544 let mut t = lex(src).expect("lexes");
4545 t.pop(); t
4547 }
4548
4549 #[test]
4550 fn a_word_keeps_both_its_canonical_and_raw_spelling() {
4551 assert_eq!(
4554 kinds("Select"),
4555 vec![Tok::Word { upper: "SELECT".into(), raw: "Select".into() }]
4556 );
4557 }
4558
4559 #[test]
4560 fn a_quoted_identifier_is_never_a_keyword() {
4561 assert_eq!(kinds(r#""select""#), vec![Tok::Quoted("select".into())]);
4562 assert_eq!(kinds(r#""Name""#), vec![Tok::Quoted("Name".into())]);
4564 }
4565
4566 #[test]
4567 fn a_doubled_quote_is_one_literal_quote() {
4568 assert_eq!(kinds("'it''s'"), vec![Tok::Str("it's".into())]);
4569 assert_eq!(kinds(r#""a""b""#), vec![Tok::Quoted("a\"b".into())]);
4570 }
4571
4572 #[test]
4573 fn an_E_string_decodes_the_escapes_catalogue_sql_uses() {
4574 assert_eq!(kinds(r"E'\n'"), vec![Tok::Str("\n".into())]);
4576 assert_eq!(kinds(r"E'a\tb'"), vec![Tok::Str("a\tb".into())]);
4577 assert_eq!(kinds(r"E'\q'"), vec![Tok::Str("q".into())]);
4579 }
4580
4581 #[test]
4582 fn operators_match_longest_first() {
4583 assert_eq!(kinds("!~*"), vec![Tok::Op("!~*".into())]);
4585 assert_eq!(kinds("!~"), vec![Tok::Op("!~".into())]);
4586 assert_eq!(kinds("~*"), vec![Tok::Op("~*".into())]);
4587 assert_eq!(kinds("<>"), vec![Tok::Op("<>".into())]);
4588 assert_eq!(kinds("!="), vec![Tok::Op("!=".into())]);
4589 assert_eq!(kinds(">="), vec![Tok::Op(">=".into())]);
4590 assert_eq!(kinds("::"), vec![Tok::Op("::".into())]);
4591 assert_eq!(kinds("||"), vec![Tok::Op("||".into())]);
4592 assert_eq!(kinds("~"), vec![Tok::Op("~".into())]);
4593 }
4594
4595 #[test]
4596 fn comments_are_skipped_including_nested_block_comments() {
4597 assert_eq!(kinds("1 -- trailing\n"), vec![Tok::Num(1.0)]);
4598 assert_eq!(kinds("1 /* a */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4599 assert_eq!(kinds("1 /* a /* b */ c */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4601 assert!(lex("1 /* unterminated").is_err());
4602 }
4603
4604 #[test]
4605 fn numbers_parse_including_fractions_and_exponents() {
4606 assert_eq!(kinds("42"), vec![Tok::Num(42.0)]);
4607 assert_eq!(kinds("4.5"), vec![Tok::Num(4.5)]);
4608 assert_eq!(kinds(".5"), vec![Tok::Num(0.5)]);
4609 assert_eq!(kinds("1e3"), vec![Tok::Num(1000.0)]);
4610 assert_eq!(kinds("1e-2"), vec![Tok::Num(0.01)]);
4611 assert_eq!(
4613 kinds("1e"),
4614 vec![Tok::Num(1.0), Tok::Word { upper: "E".into(), raw: "e".into() }]
4615 );
4616 }
4617
4618 #[test]
4619 fn an_unterminated_literal_is_an_error_not_a_truncation() {
4620 assert!(lex("'abc").is_err());
4621 assert!(lex(r#""abc"#).is_err());
4622 }
4623
4624 #[test]
4625 fn an_unknown_character_is_REFUSED_rather_than_skipped() {
4626 let e = lex("SELECT 1 @ 2").unwrap_err().to_string();
4629 assert!(e.contains('@'), "{}", e);
4630 }
4631
4632 #[test]
4633 fn the_real_dn_query_lexes() {
4634 let sql = r#"SELECT n.nspname AS "Name",
4635 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
4636 FROM pg_catalog.pg_namespace n
4637 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
4638 ORDER BY 1;"#;
4639 let toks = lex(sql).expect("psql's \\dn must lex");
4640 assert!(toks.contains(&Tok::Quoted("Name".into())));
4641 assert!(toks.contains(&Tok::Op("!~".into())));
4642 assert!(toks.contains(&Tok::Op("<>".into())));
4643 assert!(toks.contains(&Tok::Str("^pg_".into())));
4644 }
4645
4646 #[test]
4647 fn the_real_dt_query_lexes() {
4648 let sql = r#"SELECT n.nspname as "Schema", c.relname as "Name",
4649 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' END as "Type",
4650 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
4651 FROM pg_catalog.pg_class c
4652 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
4653 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
4654 WHERE c.relkind IN ('r','p','')
4655 AND n.nspname <> 'pg_catalog'
4656 AND n.nspname !~ '^pg_toast'
4657 AND pg_catalog.pg_table_is_visible(c.oid)
4658 ORDER BY 1,2;"#;
4659 let toks = lex(sql).expect("psql's \\dt must lex");
4660 assert!(toks.iter().any(|t| t.is_kw("CASE")));
4661 assert!(toks.iter().any(|t| t.is_kw("LEFT")));
4662 assert!(toks.iter().any(|t| t.is_kw("JOIN")));
4663 assert!(toks.contains(&Tok::Str(String::new())));
4665 }
4666}
4667
4668#[cfg(test)]
4669mod parser_tests {
4670 use super::*;
4671 use serde_json::json;
4672
4673 fn col(qual: Option<&str>, name: &str) -> Expr {
4674 Expr::Column { qual: qual.map(str::to_string), name: name.to_string() }
4675 }
4676
4677 #[test]
4678 fn a_bare_select_list_and_from() {
4679 let s = parse("SELECT a, b FROM t").unwrap();
4680 assert_eq!(s.items.len(), 2);
4681 assert_eq!(s.items[0].expr, col(None, "a"));
4682 assert_eq!(s.from.unwrap().name, "t");
4683 }
4684
4685 #[test]
4689 fn as_of_system_time_is_read_per_table() {
4690 let s = parse("SELECT total FROM orders AS OF SYSTEM TIME 42").unwrap();
4691 assert_eq!(s.from.clone().unwrap().as_of, Some(42));
4692 assert_eq!(s.from.unwrap().alias, None);
4693
4694 let s = parse("SELECT o.total FROM orders AS OF SYSTEM TIME 42 o").unwrap();
4696 let t = s.from.unwrap();
4697 assert_eq!((t.as_of, t.alias.as_deref()), (Some(42), Some("o")));
4698
4699 let t = parse("SELECT x FROM orders AS o").unwrap().from.unwrap();
4704 assert_eq!((t.as_of, t.alias.as_deref()), (None, Some("o")));
4705
4706 let s = parse(
4709 "SELECT o.total, n.total FROM orders AS OF SYSTEM TIME 1 o \
4710 JOIN orders n ON o._id = n._id").unwrap();
4711 assert_eq!(s.from.unwrap().as_of, Some(1));
4712 assert_eq!(s.joins[0].table.as_of, None);
4713
4714 let s = parse("SELECT total FROM orders AS OF SYSTEM TIME 7 WHERE _id = '1'").unwrap();
4716 assert_eq!(s.from.unwrap().as_of, Some(7));
4717 assert!(s.where_.is_some());
4718 }
4719
4720 #[test]
4725 fn nqls_verbs_are_table_qualifiers_in_sql() {
4726 let t = parse("SELECT _id FROM orders SEARCH 'acme'").unwrap().from.unwrap();
4727 assert_eq!(t.search.as_deref(), Some("acme"));
4728
4729 let t = parse("SELECT _id FROM orders VALID AS OF '2026-01-01'").unwrap().from.unwrap();
4730 assert_eq!(t.valid_as_of.as_deref(), Some("2026-01-01"));
4731
4732 let t = parse(
4734 "SELECT o._id FROM orders AS OF SYSTEM TIME 9 VALID AS OF '2026-01-01' \
4735 SEARCH 'acme' o").unwrap().from.unwrap();
4736 assert_eq!(
4737 (t.as_of, t.valid_as_of.as_deref(), t.search.as_deref(), t.alias.as_deref()),
4738 (Some(9), Some("2026-01-01"), Some("acme"), Some("o")));
4739
4740 let s = parse(
4742 "SELECT o._id FROM orders SEARCH 'acme' o JOIN drivers d ON o.driver = d._id")
4743 .unwrap();
4744 assert_eq!(s.from.unwrap().search.as_deref(), Some("acme"));
4745 assert_eq!(s.joins[0].table.search, None);
4746 }
4747
4748 #[test]
4756 fn the_new_verbs_do_not_steal_names_that_already_worked() {
4757 let t = parse("SELECT search.total FROM orders search").unwrap().from.unwrap();
4758 assert_eq!((t.alias.as_deref(), t.search.as_deref()), (Some("search"), None));
4759
4760 let t = parse("SELECT valid.total FROM orders valid").unwrap().from.unwrap();
4761 assert_eq!((t.alias.as_deref(), t.valid_as_of.as_deref()), (Some("valid"), None));
4762
4763 assert!(parse("SELECT search, valid FROM orders").is_ok());
4765 assert!(parse("SELECT _id FROM orders WHERE search = 'x'").is_ok());
4766
4767 let t = parse("SELECT x FROM orders AS valid").unwrap().from.unwrap();
4769 assert_eq!(t.alias.as_deref(), Some("valid"));
4770 }
4771
4772 #[test]
4773 fn a_verbs_argument_is_refused_when_it_is_the_wrong_kind_of_thing() {
4774 let e = parse("SELECT _id FROM orders VALID AS OF 42").unwrap_err().to_string();
4778 assert!(e.contains("date string"), "{}", e);
4779 }
4780
4781 #[test]
4782 fn a_wall_clock_as_of_is_refused_with_the_reason() {
4783 for sql in [
4787 "SELECT total FROM orders AS OF SYSTEM TIME '2026-01-01'",
4788 "SELECT total FROM orders AS OF SYSTEM TIME now()",
4789 "SELECT total FROM orders AS OF SYSTEM TIME -1",
4790 "SELECT total FROM orders AS OF SYSTEM TIME 1.5",
4791 ] {
4792 let e = parse(sql).unwrap_err().to_string();
4793 assert!(e.contains("sequence number"), "{} -> {}", sql, e);
4794 }
4795 }
4796
4797 #[test]
4798 fn a_clause_keyword_is_never_read_as_a_bare_alias() {
4799 let s = parse("SELECT a FROM t WHERE a = 1").unwrap();
4803 assert_eq!(s.from.clone().unwrap().alias, None);
4804 assert!(s.where_.is_some(), "the WHERE clause must survive");
4805 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4806 assert_eq!(s.from.unwrap().alias, None);
4807 assert_eq!(s.order_by.len(), 1);
4808 }
4809
4810 #[test]
4811 fn a_real_alias_is_kept_in_both_spellings() {
4812 assert_eq!(parse("SELECT a FROM t x").unwrap().from.unwrap().alias,
4813 Some("x".to_string()));
4814 assert_eq!(parse("SELECT a FROM t AS x").unwrap().from.unwrap().alias,
4815 Some("x".to_string()));
4816 }
4817
4818 #[test]
4819 fn a_tables_binding_is_its_alias_else_its_bare_name() {
4820 let t = TableRef::named("pg_catalog.pg_class", Some("c".into()));
4821 assert_eq!(t.binding(), "c");
4822 let t = TableRef::named("pg_catalog.pg_class", None);
4823 assert_eq!(t.binding(), "pg_class", "the schema is not how a column is addressed");
4824 }
4825
4826 #[test]
4827 fn a_qualified_column_keeps_only_its_immediate_qualifier() {
4828 assert_eq!(parse("SELECT n.nspname FROM x").unwrap().items[0].expr,
4829 col(Some("n"), "nspname"));
4830 assert_eq!(parse("SELECT public.orders.id FROM x").unwrap().items[0].expr,
4833 col(Some("orders"), "id"));
4834 }
4835
4836 #[test]
4837 fn an_alias_may_be_a_quoted_string_with_significant_case() {
4838 let s = parse(r#"SELECT n.nspname AS "Name" FROM x"#).unwrap();
4839 assert_eq!(s.items[0].alias, Some("Name".to_string()));
4840 }
4841
4842 #[test]
4843 fn a_schema_qualified_function_drops_its_schema() {
4844 let s = parse("SELECT pg_catalog.pg_get_userbyid(n.nspowner) FROM x").unwrap();
4847 match &s.items[0].expr {
4848 Expr::Func { name, args } => {
4849 assert_eq!(name, "pg_get_userbyid");
4850 assert_eq!(args.len(), 1);
4851 assert_eq!(args[0], col(Some("n"), "nspowner"));
4852 }
4853 other => panic!("{:?}", other),
4854 }
4855 }
4856
4857 #[test]
4858 fn operator_precedence_matches_sql() {
4859 let s = parse("SELECT 1 FROM t WHERE a = 1 OR b = 2 AND c = 3").unwrap();
4862 match s.where_.unwrap() {
4863 Expr::Binary { op, right, .. } => {
4864 assert_eq!(op, "OR");
4865 assert!(matches!(*right, Expr::Binary { ref op, .. } if op == "AND"),
4866 "AND must bind tighter than OR");
4867 }
4868 other => panic!("{:?}", other),
4869 }
4870 let s = parse("SELECT 1 FROM t WHERE a = 1 AND b = 2").unwrap();
4872 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4873 let s = parse("SELECT 1 + 2 * 3 FROM t").unwrap();
4875 match &s.items[0].expr {
4876 Expr::Binary { op, right, .. } => {
4877 assert_eq!(op, "+");
4878 assert!(matches!(**right, Expr::Binary { ref op, .. } if op == "*"));
4879 }
4880 other => panic!("{:?}", other),
4881 }
4882 }
4883
4884 #[test]
4885 fn parentheses_override_precedence() {
4886 let s = parse("SELECT 1 FROM t WHERE (a = 1 OR b = 2) AND c = 3").unwrap();
4887 match s.where_.unwrap() {
4888 Expr::Binary { op, left, .. } => {
4889 assert_eq!(op, "AND");
4890 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "OR"));
4891 }
4892 other => panic!("{:?}", other),
4893 }
4894 }
4895
4896 #[test]
4897 fn in_and_is_null_and_between_parse_in_both_polarities() {
4898 let s = parse("SELECT 1 FROM t WHERE k IN ('r','p','')").unwrap();
4899 match s.where_.unwrap() {
4900 Expr::InList { list, negated, .. } => {
4901 assert_eq!(list.len(), 3);
4902 assert!(!negated);
4903 assert_eq!(list[2], Expr::Literal(json!("")));
4905 }
4906 other => panic!("{:?}", other),
4907 }
4908 assert!(matches!(parse("SELECT 1 FROM t WHERE k NOT IN (1)").unwrap().where_.unwrap(),
4909 Expr::InList { negated: true, .. }));
4910 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NULL").unwrap().where_.unwrap(),
4911 Expr::IsNull { negated: false, .. }));
4912 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NOT NULL").unwrap().where_.unwrap(),
4913 Expr::IsNull { negated: true, .. }));
4914 let s = parse("SELECT 1 FROM t WHERE n BETWEEN 1 AND 5").unwrap();
4916 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4917 }
4918
4919 #[test]
4920 fn both_case_spellings_parse() {
4921 let s = parse("SELECT CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
4923 ELSE 'other' END FROM t").unwrap();
4924 match &s.items[0].expr {
4925 Expr::Case { operand, whens, else_ } => {
4926 assert!(operand.is_some());
4927 assert_eq!(whens.len(), 2);
4928 assert!(else_.is_some());
4929 }
4930 other => panic!("{:?}", other),
4931 }
4932 let s = parse("SELECT CASE WHEN k = 'r' THEN 1 END FROM t").unwrap();
4934 match &s.items[0].expr {
4935 Expr::Case { operand, whens, else_ } => {
4936 assert!(operand.is_none());
4937 assert_eq!(whens.len(), 1);
4938 assert!(else_.is_none());
4939 }
4940 other => panic!("{:?}", other),
4941 }
4942 assert!(parse("SELECT CASE k END FROM t").is_err());
4944 }
4945
4946 #[test]
4947 fn every_join_flavour_parses_and_an_inner_join_demands_ON() {
4948 for (sql, kind) in [
4949 ("SELECT 1 FROM a JOIN b ON a.x = b.x", JoinKind::Inner),
4950 ("SELECT 1 FROM a INNER JOIN b ON a.x = b.x", JoinKind::Inner),
4951 ("SELECT 1 FROM a LEFT JOIN b ON a.x = b.x", JoinKind::Left),
4952 ("SELECT 1 FROM a LEFT OUTER JOIN b ON a.x = b.x", JoinKind::Left),
4953 ("SELECT 1 FROM a RIGHT JOIN b ON a.x = b.x", JoinKind::Right),
4954 ("SELECT 1 FROM a FULL OUTER JOIN b ON a.x = b.x", JoinKind::Full),
4955 ("SELECT 1 FROM a CROSS JOIN b", JoinKind::Cross),
4956 ] {
4957 let s = parse(sql).unwrap_or_else(|e| panic!("{}: {}", sql, e));
4958 assert_eq!(s.joins.len(), 1, "{}", sql);
4959 assert_eq!(s.joins[0].kind, kind, "{}", sql);
4960 }
4961 let s = parse("SELECT 1 FROM a, b").unwrap();
4963 assert_eq!(s.joins[0].kind, JoinKind::Cross);
4964 assert!(parse("SELECT 1 FROM a LEFT JOIN b").is_err());
4967 assert!(parse("SELECT 1 FROM a JOIN b USING (x)").is_err());
4968 }
4969
4970 #[test]
4971 fn order_by_reads_a_number_as_an_ORDINAL() {
4972 let s = parse("SELECT a, b FROM t ORDER BY 1, 2 DESC").unwrap();
4976 assert_eq!(s.order_by.len(), 2);
4977 assert_eq!(s.order_by[0].ordinal, Some(1));
4978 assert_eq!(s.order_by[0].dir, Dir::Asc);
4979 assert_eq!(s.order_by[1].ordinal, Some(2));
4980 assert_eq!(s.order_by[1].dir, Dir::Desc);
4981 let s = parse("SELECT a FROM t ORDER BY lower(a) ASC").unwrap();
4983 assert!(s.order_by[0].ordinal.is_none());
4984 assert!(s.order_by[0].expr.is_some());
4985 }
4986
4987 #[test]
4988 fn null_ordering_defaults_the_way_postgres_defaults() {
4989 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4990 assert!(!s.order_by[0].nulls_first, "ASC defaults to NULLS LAST");
4991 let s = parse("SELECT a FROM t ORDER BY a DESC").unwrap();
4992 assert!(s.order_by[0].nulls_first, "DESC defaults to NULLS FIRST");
4993 let s = parse("SELECT a FROM t ORDER BY a NULLS FIRST").unwrap();
4994 assert!(s.order_by[0].nulls_first, "an explicit clause wins");
4995 }
4996
4997 #[test]
4998 fn limit_and_offset_parse_in_either_order() {
4999 let s = parse("SELECT a FROM t LIMIT 5 OFFSET 2").unwrap();
5000 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
5001 let s = parse("SELECT a FROM t OFFSET 2 LIMIT 5").unwrap();
5002 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
5003 let s = parse("SELECT a FROM t LIMIT ALL").unwrap();
5004 assert_eq!(s.limit, None);
5005 }
5006
5007 #[test]
5008 fn casts_parse_and_are_recorded_rather_than_rejected() {
5009 let s = parse("SELECT x::int2 FROM t").unwrap();
5011 assert!(matches!(s.items[0].expr, Expr::Cast { .. }));
5012 let s = parse("SELECT x::pg_catalog.int2[] FROM t").unwrap();
5013 match &s.items[0].expr {
5014 Expr::Cast { ty, .. } => assert_eq!(ty, "int2[]"),
5015 other => panic!("{:?}", other),
5016 }
5017 }
5018
5019 #[test]
5020 fn star_and_qualified_star_parse() {
5021 assert_eq!(parse("SELECT * FROM t").unwrap().items[0].expr, Expr::Star);
5022 assert_eq!(parse("SELECT c.* FROM t c").unwrap().items[0].expr,
5023 Expr::QualifiedStar("c".into()));
5024 match &parse("SELECT count(*) FROM t").unwrap().items[0].expr {
5027 Expr::Agg { name, args, order_by, distinct } => {
5028 assert_eq!(name, "count");
5029 assert_eq!(args, &vec![Expr::Star]);
5030 assert!(order_by.is_empty());
5031 assert!(!distinct);
5032 }
5033 other => panic!("{:?}", other),
5034 }
5035 assert!(matches!(
5037 &parse("SELECT lower(s) FROM t").unwrap().items[0].expr,
5038 Expr::Func { name, .. } if name == "lower"
5039 ));
5040 }
5041
5042 #[test]
5043 fn an_aggregate_carries_its_own_DISTINCT_and_ORDER_BY() {
5044 match &parse("SELECT array_agg(a.attname ORDER BY a.ord) FROM t a").unwrap().items[0].expr {
5048 Expr::Agg { name, args, order_by, distinct } => {
5049 assert_eq!(name, "array_agg");
5050 assert_eq!(args.len(), 1);
5051 assert_eq!(order_by.len(), 1);
5052 assert!(matches!(order_by[0].dir, Dir::Asc));
5053 assert!(!distinct);
5054 }
5055 other => panic!("{:?}", other),
5056 }
5057 match &parse("SELECT string_agg(DISTINCT s, ',' ORDER BY b DESC NULLS LAST, c) FROM t").unwrap().items[0].expr {
5060 Expr::Agg { name, args, order_by, distinct } => {
5061 assert_eq!(name, "string_agg");
5062 assert_eq!(args.len(), 2, "the separator is an argument, not a sort key");
5063 assert_eq!(order_by.len(), 2);
5064 assert!(matches!(order_by[0].dir, Dir::Desc));
5065 assert!(!order_by[0].nulls_first, "NULLS LAST overrides the DESC default");
5066 assert!(matches!(order_by[1].dir, Dir::Asc));
5067 assert!(distinct);
5068 }
5069 other => panic!("{:?}", other),
5070 }
5071 assert!(parse("SELECT lower(DISTINCT s) FROM t").is_err());
5074 }
5075
5076 #[test]
5077 fn a_parenthesis_free_function_parses_as_a_zero_arg_call() {
5078 match &parse("SELECT current_schema FROM t").unwrap().items[0].expr {
5080 Expr::Func { name, args } => {
5081 assert_eq!(name, "current_schema");
5082 assert!(args.is_empty());
5083 }
5084 other => panic!("{:?}", other),
5085 }
5086 }
5087
5088 #[test]
5089 fn GROUP_BY_and_HAVING_parse_and_what_remains_is_refused_by_name() {
5090 let s = parse("SELECT a, count(*) FROM t GROUP BY a").unwrap();
5093 assert_eq!(s.group_by, vec![Expr::Column { qual: None, name: "a".into() }]);
5094 assert!(s.having.is_none());
5095
5096 let s = parse("SELECT a, b, count(*) FROM t GROUP BY a, b HAVING count(*) > 1").unwrap();
5097 assert_eq!(s.group_by.len(), 2);
5098 assert!(s.having.is_some());
5099
5100 let e = parse("SELECT a FROM t HAVING a > 1").unwrap_err().to_string();
5104 assert!(e.contains("HAVING needs a GROUP BY"), "{}", e);
5105
5106 for (sql, needle) in [
5107 ("SELECT DISTINCT ON (a) a FROM t", "DISTINCT ON"),
5108 ("SELECT a, count(*) FROM t GROUP BY ROLLUP (a)", "ROLLUP"),
5109 ("SELECT a, count(*) FROM t GROUP BY CUBE (a)", "CUBE"),
5110 ] {
5111 let e = parse(sql).unwrap_err().to_string();
5112 assert!(e.contains(needle), "{} -> {}", sql, e);
5113 }
5114 assert!(parse("SELECT a FROM t JUNK JUNK2").is_err());
5116 }
5117
5118 #[test]
5119 fn THE_dn_QUERY_parses_completely() {
5120 let s = parse(
5121 r#"SELECT n.nspname AS "Name",
5122 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5123 FROM pg_catalog.pg_namespace n
5124 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5125 ORDER BY 1;"#,
5126 )
5127 .expect("psql's \\dn must parse");
5128
5129 assert_eq!(s.items.len(), 2);
5130 assert_eq!(s.items[0].alias, Some("Name".into()));
5131 assert_eq!(s.items[1].alias, Some("Owner".into()));
5132 let from = s.from.unwrap();
5133 assert_eq!(from.name, "pg_catalog.pg_namespace");
5134 assert_eq!(from.binding(), "n");
5135 assert!(s.where_.is_some());
5136 assert_eq!(s.order_by[0].ordinal, Some(1));
5137 }
5138
5139 #[test]
5140 fn THE_dt_QUERY_parses_completely() {
5141 let s = parse(
5142 r#"SELECT n.nspname as "Schema",
5143 c.relname as "Name",
5144 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5145 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5146 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5147 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5148 WHEN 'I' THEN 'partitioned index' END as "Type",
5149 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5150 FROM pg_catalog.pg_class c
5151 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5152 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5153 WHERE c.relkind IN ('r','p','')
5154 AND n.nspname <> 'pg_catalog'
5155 AND n.nspname !~ '^pg_toast'
5156 AND n.nspname <> 'information_schema'
5157 AND pg_catalog.pg_table_is_visible(c.oid)
5158 ORDER BY 1,2;"#,
5159 )
5160 .expect("psql's \\dt must parse");
5161
5162 assert_eq!(s.items.len(), 4);
5163 assert_eq!(s.items[2].alias, Some("Type".into()));
5164 match &s.items[2].expr {
5165 Expr::Case { whens, .. } => assert_eq!(whens.len(), 9, "all nine branches"),
5166 other => panic!("{:?}", other),
5167 }
5168 assert_eq!(s.joins.len(), 2);
5169 assert!(s.joins.iter().all(|j| j.kind == JoinKind::Left && j.on.is_some()));
5170 assert_eq!(s.from.unwrap().binding(), "c");
5171 assert_eq!(s.order_by.len(), 2);
5172 assert_eq!(
5173 (s.order_by[0].ordinal, s.order_by[1].ordinal),
5174 (Some(1), Some(2))
5175 );
5176 }
5177}
5178
5179#[cfg(test)]
5180mod eval_tests {
5181 use super::*;
5182 use serde_json::json;
5183
5184 fn one(row: &Value) -> Bound<'_> {
5186 Bound::new(vec![("t".to_string(), Some(row))])
5187 }
5188
5189 fn ev(sql_expr: &str, row: &Value) -> Result<Value> {
5190 let s = parse(&format!("SELECT {} FROM t", sql_expr))?;
5191 eval(&s.items[0].expr, &one(row))
5192 }
5193
5194 fn v(sql_expr: &str, row: &Value) -> Value {
5195 ev(sql_expr, row).unwrap_or_else(|e| panic!("{}: {}", sql_expr, e))
5196 }
5197
5198 #[test]
5199 fn literals_and_columns_resolve() {
5200 let r = json!({"a": 1, "s": "x", "b": true, "n": null});
5201 assert_eq!(v("42", &r), json!(42));
5202 assert_eq!(v("'hi'", &r), json!("hi"));
5203 assert_eq!(v("NULL", &r), Value::Null);
5204 assert_eq!(v("TRUE", &r), json!(true));
5205 assert_eq!(v("a", &r), json!(1));
5206 assert_eq!(v("t.a", &r), json!(1));
5207 assert_eq!(v("s", &r), json!("x"));
5208 assert_eq!(v("nosuch", &r), Value::Null);
5211 }
5212
5213 #[test]
5214 fn an_unknown_table_ALIAS_is_an_error_while_an_unknown_column_is_null() {
5215 let r = json!({"a": 1});
5218 assert_eq!(v("t.nosuch", &r), Value::Null);
5219 let e = ev("zz.a", &r).unwrap_err().to_string();
5220 assert!(e.contains("zz"), "{}", e);
5221 }
5222
5223 #[test]
5226 fn every_comparison_over_NULL_is_UNKNOWN_including_null_equals_null() {
5227 let r = json!({"n": null, "a": 1});
5228 assert_eq!(v("n = 1", &r), Value::Null);
5229 assert_eq!(v("n != 1", &r), Value::Null);
5230 assert_eq!(v("n < 1", &r), Value::Null);
5231 assert_eq!(v("n = n", &r), Value::Null);
5233 assert_eq!(v("n = NULL", &r), Value::Null);
5234 }
5235
5236 #[test]
5237 fn NOT_UNKNOWN_is_UNKNOWN_not_true() {
5238 let r = json!({"n": null});
5242 assert_eq!(v("NOT (n = 1)", &r), Value::Null);
5243 assert_eq!(v("NOT TRUE", &r), json!(false));
5244 assert_eq!(v("NOT FALSE", &r), json!(true));
5245 }
5246
5247 #[test]
5248 fn AND_and_OR_follow_the_three_valued_truth_tables() {
5249 let r = json!({"n": null});
5250 assert_eq!(v("FALSE AND n = 1", &r), json!(false));
5252 assert_eq!(v("TRUE AND n = 1", &r), Value::Null);
5254 assert_eq!(v("TRUE OR n = 1", &r), json!(true));
5256 assert_eq!(v("FALSE OR n = 1", &r), Value::Null);
5258 assert_eq!(v("TRUE AND TRUE", &r), json!(true));
5260 assert_eq!(v("TRUE AND FALSE", &r), json!(false));
5261 assert_eq!(v("FALSE OR FALSE", &r), json!(false));
5262 }
5263
5264 #[test]
5265 fn IS_NULL_is_the_one_predicate_that_is_never_unknown() {
5266 let r = json!({"n": null, "a": 1});
5267 assert_eq!(v("n IS NULL", &r), json!(true));
5268 assert_eq!(v("n IS NOT NULL", &r), json!(false));
5269 assert_eq!(v("a IS NULL", &r), json!(false));
5270 assert_eq!(v("a IS NOT NULL", &r), json!(true));
5271 assert_eq!(v("nosuch IS NULL", &r), json!(true));
5274 }
5275
5276 #[test]
5277 fn NOT_IN_with_a_NULL_in_the_list_is_UNKNOWN_the_classic_trap() {
5278 let r = json!({"a": 2});
5279 assert_eq!(v("a IN (1, 2)", &r), json!(true));
5280 assert_eq!(v("a IN (1, 3)", &r), json!(false));
5281 assert_eq!(v("a NOT IN (1, 3)", &r), json!(true));
5282 assert_eq!(v("a NOT IN (1, NULL)", &r), Value::Null);
5285 assert_eq!(v("a IN (2, NULL)", &r), json!(true));
5287 assert_eq!(v("nosuch IN (1)", &r), Value::Null);
5289 }
5290
5291 #[test]
5294 fn comparisons_work_across_numbers_strings_and_booleans() {
5295 let r = json!({"n": 5, "s": "b", "t": true});
5296 assert_eq!(v("n > 3", &r), json!(true));
5297 assert_eq!(v("n <= 5", &r), json!(true));
5298 assert_eq!(v("s < 'c'", &r), json!(true));
5299 assert_eq!(v("s > 'c'", &r), json!(false));
5300 assert_eq!(v("n = '5'", &r), json!(true));
5303 assert_eq!(v("n = '5.0'", &r), json!(true));
5304 assert_eq!(v("n = 'five'", &r), json!(false));
5307 }
5308
5309 #[test]
5310 fn the_regex_operators_use_the_SAME_matcher_as_NQL() {
5311 let r = json!({"s": "pg_catalog"});
5314 assert_eq!(v("s ~ '^pg_'", &r), json!(true));
5315 assert_eq!(v("s !~ '^pg_'", &r), json!(false));
5316 assert_eq!(v("s ~ '^PG_'", &r), json!(false));
5317 assert_eq!(v("s ~* '^PG_'", &r), json!(true));
5318 assert_eq!(v("s !~ '^zz'", &r), json!(true));
5319 assert_eq!(v("nosuch ~ '^x'", &r), Value::Null);
5321 assert_eq!(v("s ~ '^(pg_catalog)$'", &r), json!(true));
5324 assert_eq!(v("s ~ '^(pg_.*)$'", &r), json!(true));
5325 assert_eq!(v("s ~ '^(public|pg_catalog)$'", &r), json!(true));
5326 assert_eq!(v("s ~ '^pg_[a-z]+$'", &r), json!(true));
5327 assert_eq!(v("s ~ '^pg_[0-9]+$'", &r), json!(false));
5328 let e = ev("s ~ 'a{2}'", &r).unwrap_err().to_string();
5330 assert!(e.contains("interval"), "{}", e);
5331 }
5332
5333 #[test]
5334 fn like_works_in_all_four_spellings() {
5335 let r = json!({"s": "Acme Pool"});
5336 assert_eq!(v("s LIKE 'Acme%'", &r), json!(true));
5337 assert_eq!(v("s LIKE 'acme%'", &r), json!(false));
5338 assert_eq!(v("s ILIKE 'acme%'", &r), json!(true));
5339 assert_eq!(v("s NOT LIKE 'zz%'", &r), json!(true));
5340 assert_eq!(v("nosuch LIKE 'x'", &r), Value::Null);
5341 }
5342
5343 #[test]
5344 fn arithmetic_and_concatenation_propagate_null_and_refuse_div_by_zero() {
5345 let r = json!({"a": 7, "b": 2});
5346 assert_eq!(v("a + b", &r), json!(9));
5347 assert_eq!(v("a - b", &r), json!(5));
5348 assert_eq!(v("a * b", &r), json!(14));
5349 assert_eq!(v("a / b", &r), json!(3.5));
5350 assert_eq!(v("a % b", &r), json!(1));
5351 assert_eq!(v("-a", &r), json!(-7));
5352 assert_eq!(v("a / b", &r), json!(3.5));
5355 assert_eq!(v("b / a", &r), json!(2.0 / 7.0));
5356 assert_eq!(v("'x' || 'y'", &r), json!("xy"));
5357 assert_eq!(v("'x' || nosuch", &r), Value::Null);
5358 assert_eq!(v("a + nosuch", &r), Value::Null);
5359 assert!(ev("a / 0", &r).is_err());
5362 assert!(ev("a % 0", &r).is_err());
5363 }
5364
5365 #[test]
5366 fn a_cast_is_transparent_rather_than_rejected() {
5367 let r = json!({"a": 7});
5370 assert_eq!(v("a::int2", &r), json!(7));
5371 assert_eq!(v("a::pg_catalog.int2[]", &r), json!(7));
5372 }
5373
5374 #[test]
5377 fn a_simple_CASE_picks_the_matching_branch() {
5378 let expr = "CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
5380 WHEN 'i' THEN 'index' END";
5381 assert_eq!(v(expr, &json!({"k": "r"})), json!("table"));
5382 assert_eq!(v(expr, &json!({"k": "v"})), json!("view"));
5383 assert_eq!(v(expr, &json!({"k": "i"})), json!("index"));
5384 assert_eq!(v(expr, &json!({"k": "z"})), Value::Null);
5387 }
5388
5389 #[test]
5390 fn a_searched_CASE_evaluates_predicates_and_UNKNOWN_does_not_match() {
5391 let expr = "CASE WHEN n > 5 THEN 'big' WHEN n > 0 THEN 'small' ELSE 'none' END";
5392 assert_eq!(v(expr, &json!({"n": 9})), json!("big"));
5393 assert_eq!(v(expr, &json!({"n": 2})), json!("small"));
5394 assert_eq!(v(expr, &json!({"n": -1})), json!("none"));
5395 assert_eq!(v(expr, &json!({"other": 1})), json!("none"));
5397 }
5398
5399 #[test]
5400 fn an_ELSE_branch_is_used_when_nothing_matches() {
5401 assert_eq!(
5402 v("CASE k WHEN 'r' THEN 'table' ELSE 'other' END", &json!({"k": "z"})),
5403 json!("other")
5404 );
5405 }
5406
5407 #[test]
5410 fn the_catalogue_functions_psql_calls_all_answer() {
5411 let r = json!({"o": 10, "enc": 6});
5412 assert_eq!(v("pg_get_userbyid(o)", &r), json!("nedb"));
5414 assert_eq!(v("pg_catalog.pg_get_userbyid(o)", &r), json!("nedb"));
5415 assert_eq!(v("pg_table_is_visible(o)", &r), json!(true));
5417 assert_eq!(v("pg_encoding_to_char(enc)", &r), json!("UTF8"));
5418 assert_eq!(v("current_schema", &r), json!("public"));
5419 assert_eq!(v("current_database()", &r), json!("nedb"));
5420 assert_eq!(v("current_user", &r), json!("nedb"));
5421 assert_eq!(v("pg_get_expr(o, o)", &r), Value::Null);
5424 assert_eq!(v("obj_description(o)", &r), Value::Null);
5425 }
5426
5427 #[test]
5428 fn text_and_null_handling_functions_work() {
5429 let r = json!({"s": "AbC", "n": null});
5430 assert_eq!(v("lower(s)", &r), json!("abc"));
5431 assert_eq!(v("upper(s)", &r), json!("ABC"));
5432 assert_eq!(v("length(s)", &r), json!(3));
5433 assert_eq!(v("lower(n)", &r), Value::Null);
5434 assert_eq!(v("coalesce(n, 'fallback')", &r), json!("fallback"));
5435 assert_eq!(v("coalesce(s, 'fallback')", &r), json!("AbC"));
5436 assert_eq!(v("coalesce(n, n)", &r), Value::Null);
5437 assert_eq!(v("nullif(s, 'AbC')", &r), Value::Null);
5438 assert_eq!(v("nullif(s, 'zz')", &r), json!("AbC"));
5439 assert_eq!(v("format_type(20, NULL)", &r), json!("bigint"));
5441 }
5442
5443 #[test]
5444 fn coalesce_does_not_evaluate_past_its_first_non_null() {
5445 let r = json!({"a": 1});
5447 assert_eq!(v("coalesce(a, a / 0)", &r), json!(1));
5448 }
5449
5450 #[test]
5451 fn an_unknown_function_is_REFUSED_rather_than_answered_with_NULL() {
5452 let e = ev("pg_stat_get_numscans(1)", &json!({})).unwrap_err().to_string();
5455 assert!(e.contains("pg_stat_get_numscans"), "{}", e);
5456 assert!(e.contains("refused"), "{}", e);
5457 }
5458
5459 #[test]
5462 fn a_qualified_column_reads_only_its_OWN_binding() {
5463 let a = json!({"name": "left", "x": 1});
5466 let b = json!({"name": "right", "y": 2});
5467 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), Some(&b))]);
5468 let get = |e: &str| {
5469 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5470 eval(&s.items[0].expr, &row).unwrap()
5471 };
5472 assert_eq!(get("a.name"), json!("left"));
5473 assert_eq!(get("b.name"), json!("right"));
5474 assert_eq!(get("name"), json!("left"));
5476 assert_eq!(get("y"), json!(2), "a bare name still finds a later binding");
5477 }
5478
5479 #[test]
5480 fn an_unmatched_LEFT_JOIN_side_reads_as_NULL_not_as_a_missing_column() {
5481 let a = json!({"x": 1});
5484 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), None)]);
5485 let get = |e: &str| {
5486 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5487 eval(&s.items[0].expr, &row).unwrap()
5488 };
5489 assert_eq!(get("b.anything"), Value::Null);
5490 assert_eq!(get("b.anything IS NULL"), json!(true));
5491 assert_eq!(get("a.x"), json!(1));
5492 }
5493}
5494
5495#[cfg(test)]
5496mod exec_tests {
5497 use super::*;
5498 use serde_json::json;
5499
5500 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5502 let owned: Vec<(String, Vec<Value>)> =
5503 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
5504 move |name: &str| {
5505 let bare = name.rsplit('.').next().unwrap_or(name);
5507 Ok(owned
5508 .iter()
5509 .find(|(n, _)| n == name || n == bare)
5510 .map(|(_, r)| from_vec(r.clone())))
5511 }
5512 }
5513
5514 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
5515 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
5516 (cols.into_iter().map(|c| c.name).collect(), rows)
5517 }
5518
5519 fn col(rows: &[Value], name: &str) -> Vec<Value> {
5520 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
5521 }
5522
5523 #[test]
5526 fn select_columns_where_order_limit_offset() {
5527 let t = tables(vec![(
5528 "t",
5529 vec![json!({"a": 3, "s": "c"}), json!({"a": 1, "s": "a"}), json!({"a": 2, "s": "b"})],
5530 )]);
5531 let (names, rows) = go("SELECT a, s FROM t ORDER BY a", &t);
5532 assert_eq!(names, vec!["a", "s"]);
5533 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2), json!(3)]);
5534
5535 let (_, rows) = go("SELECT a FROM t ORDER BY a DESC", &t);
5536 assert_eq!(col(&rows, "a"), vec![json!(3), json!(2), json!(1)]);
5537
5538 let (_, rows) = go("SELECT a FROM t WHERE a > 1 ORDER BY a", &t);
5539 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5540
5541 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 2", &t);
5542 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5543
5544 let (_, rows) = go("SELECT a FROM t ORDER BY a OFFSET 1", &t);
5545 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5546
5547 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 1 OFFSET 1", &t);
5548 assert_eq!(col(&rows, "a"), vec![json!(2)]);
5549
5550 let (_, rows) = go("SELECT a FROM t OFFSET 99", &t);
5552 assert!(rows.is_empty());
5553 }
5554
5555 #[test]
5556 fn an_output_column_takes_its_alias_or_a_derived_name() {
5557 let t = tables(vec![("t", vec![json!({"a": 1})])]);
5560 assert_eq!(go(r#"SELECT a AS "Name" FROM t"#, &t).0, vec!["Name"]);
5561 assert_eq!(go("SELECT a FROM t", &t).0, vec!["a"]);
5562 assert_eq!(go("SELECT lower('X') FROM t", &t).0, vec!["lower"]);
5563 assert_eq!(go("SELECT 1 + 1 FROM t", &t).0, vec!["?column?"]);
5564 assert_eq!(go("SELECT CASE a WHEN 1 THEN 'x' END FROM t", &t).0, vec!["case"]);
5565 }
5566
5567 #[test]
5568 fn star_expands_from_the_rows_and_a_qualified_star_from_one_binding() {
5569 let t = tables(vec![
5570 ("a", vec![json!({"x": 1, "y": 2})]),
5571 ("b", vec![json!({"z": 3})]),
5572 ]);
5573 let (names, rows) = go("SELECT * FROM a", &t);
5574 assert_eq!(names, vec!["x", "y"]);
5575 assert_eq!(rows.len(), 1);
5576
5577 let (names, _) = go("SELECT a.* FROM a CROSS JOIN b", &t);
5578 assert_eq!(names, vec!["x", "y"], "a qualified star takes ONE binding");
5579
5580 let empty = tables(vec![("e", vec![])]);
5583 assert_eq!(go("SELECT * FROM e", &empty).0, Vec::<String>::new());
5584 }
5585
5586 #[test]
5587 fn distinct_dedupes_on_the_projected_values() {
5588 let t = tables(vec![(
5589 "t",
5590 vec![json!({"g": "x"}), json!({"g": "x"}), json!({"g": "y"})],
5591 )]);
5592 let (_, rows) = go("SELECT DISTINCT g FROM t ORDER BY 1", &t);
5593 assert_eq!(col(&rows, "g"), vec![json!("x"), json!("y")]);
5594 let (_, rows) = go("SELECT g FROM t", &t);
5595 assert_eq!(rows.len(), 3, "without DISTINCT every row survives");
5596 }
5597
5598 #[test]
5599 fn order_by_an_ORDINAL_sorts_the_projected_column() {
5600 let t = tables(vec![(
5601 "t",
5602 vec![json!({"a": 2, "b": "z"}), json!({"a": 1, "b": "y"})],
5603 )]);
5604 let (_, rows) = go("SELECT a, b FROM t ORDER BY 1", &t);
5605 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5606 let (_, rows) = go("SELECT a, b FROM t ORDER BY 2 DESC", &t);
5607 assert_eq!(col(&rows, "b"), vec![json!("z"), json!("y")]);
5608 let e = run("SELECT a FROM t ORDER BY 3", &t).unwrap_err().to_string();
5610 assert!(e.contains("out of range"), "{}", e);
5611 }
5612
5613 #[test]
5614 fn order_by_an_expression_may_use_a_column_NOT_in_the_select_list() {
5615 let t = tables(vec![(
5616 "t",
5617 vec![json!({"a": 1, "hidden": 9}), json!({"a": 2, "hidden": 1})],
5618 )]);
5619 let (_, rows) = go("SELECT a FROM t ORDER BY hidden", &t);
5620 assert_eq!(col(&rows, "a"), vec![json!(2), json!(1)]);
5621 }
5622
5623 #[test]
5624 fn null_ordering_follows_the_direction_defaults() {
5625 let t = tables(vec![(
5626 "t",
5627 vec![json!({"a": 2}), json!({"a": null}), json!({"a": 1})],
5628 )]);
5629 assert_eq!(col(&go("SELECT a FROM t ORDER BY a", &t).1, "a"),
5631 vec![json!(1), json!(2), Value::Null]);
5632 assert_eq!(col(&go("SELECT a FROM t ORDER BY a DESC", &t).1, "a"),
5634 vec![Value::Null, json!(2), json!(1)]);
5635 assert_eq!(col(&go("SELECT a FROM t ORDER BY a NULLS FIRST", &t).1, "a"),
5637 vec![Value::Null, json!(1), json!(2)]);
5638 }
5639
5640 #[test]
5641 fn a_where_clause_that_is_UNKNOWN_excludes_the_row() {
5642 let t = tables(vec![(
5643 "t",
5644 vec![json!({"a": 1}), json!({"a": null}), json!({"other": 1})],
5645 )]);
5646 let (_, rows) = go("SELECT a FROM t WHERE a = 1", &t);
5648 assert_eq!(rows.len(), 1);
5649 let (_, rows) = go("SELECT a FROM t WHERE NOT (a = 1)", &t);
5651 assert_eq!(rows.len(), 0, "NOT UNKNOWN must not resurrect a null row");
5652 }
5653
5654 #[test]
5655 fn select_with_no_FROM_returns_exactly_one_row() {
5656 let t = tables(vec![]);
5658 let (names, rows) = go("SELECT 1", &t);
5659 assert_eq!(rows.len(), 1);
5660 assert_eq!(names, vec!["?column?"]);
5661 assert_eq!(go("SELECT current_schema", &t).1.len(), 1);
5662 }
5663
5664 #[test]
5665 fn an_unknown_relation_is_NAMED_rather_than_answered_with_no_rows() {
5666 let t = tables(vec![("t", vec![])]);
5669 let e = run("SELECT a FROM nosuchtable", &t).unwrap_err().to_string();
5670 assert!(e.contains("nosuchtable"), "{}", e);
5671 assert!(e.contains("does not exist"), "{}", e);
5672 }
5673
5674 #[test]
5677 fn an_inner_join_keeps_only_matching_pairs() {
5678 let t = tables(vec![
5679 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5680 ("r", vec![json!({"lid": 1, "v": "x"})]),
5681 ]);
5682 let (_, rows) = go("SELECT l.n, r.v FROM l JOIN r ON r.lid = l.id", &t);
5683 assert_eq!(rows.len(), 1);
5684 assert_eq!(col(&rows, "n"), vec![json!("a")]);
5685 }
5686
5687 #[test]
5688 fn a_LEFT_join_keeps_unmatched_left_rows_with_NULLs() {
5689 let t = tables(vec![
5691 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5692 ("r", vec![json!({"lid": 1, "v": "x"})]),
5693 ]);
5694 let (_, rows) = go("SELECT l.n, r.v FROM l LEFT JOIN r ON r.lid = l.id ORDER BY 1", &t);
5695 assert_eq!(rows.len(), 2);
5696 assert_eq!(col(&rows, "n"), vec![json!("a"), json!("b")]);
5697 assert_eq!(col(&rows, "v"), vec![json!("x"), Value::Null]);
5698 }
5699
5700 #[test]
5701 fn a_RIGHT_join_keeps_unmatched_right_rows_and_FULL_keeps_both() {
5702 let t = tables(vec![
5703 ("l", vec![json!({"id": 1})]),
5704 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5705 ]);
5706 let (_, rows) = go("SELECT l.id, r.lid FROM l RIGHT JOIN r ON r.lid = l.id", &t);
5707 assert_eq!(rows.len(), 2);
5708 assert!(col(&rows, "id").contains(&Value::Null), "the unmatched right row keeps NULLs on the left");
5709
5710 let t2 = tables(vec![
5711 ("l", vec![json!({"id": 1}), json!({"id": 5})]),
5712 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5713 ]);
5714 let (_, rows) = go("SELECT l.id, r.lid FROM l FULL OUTER JOIN r ON r.lid = l.id", &t2);
5715 assert_eq!(rows.len(), 3, "one match plus one orphan on each side");
5716 }
5717
5718 #[test]
5719 fn a_cross_join_is_the_cartesian_product() {
5720 let t = tables(vec![
5721 ("a", vec![json!({"x": 1}), json!({"x": 2})]),
5722 ("b", vec![json!({"y": 1}), json!({"y": 2}), json!({"y": 3})]),
5723 ]);
5724 assert_eq!(go("SELECT a.x, b.y FROM a CROSS JOIN b", &t).1.len(), 6);
5725 assert_eq!(go("SELECT a.x, b.y FROM a, b", &t).1.len(), 6);
5727 }
5728
5729 #[test]
5730 fn an_ON_clause_that_is_UNKNOWN_does_not_join() {
5731 let t = tables(vec![
5734 ("l", vec![json!({"id": null})]),
5735 ("r", vec![json!({"lid": null})]),
5736 ]);
5737 let (_, rows) = go("SELECT l.id FROM l JOIN r ON r.lid = l.id", &t);
5738 assert!(rows.is_empty(), "NULL = NULL is UNKNOWN, so nothing joins");
5739 let (_, rows) = go("SELECT l.id FROM l LEFT JOIN r ON r.lid = l.id", &t);
5741 assert_eq!(rows.len(), 1);
5742 }
5743
5744 #[test]
5745 fn two_joins_chain() {
5746 let t = tables(vec![
5747 ("a", vec![json!({"id": 1, "bid": 10, "cid": 100})]),
5748 ("b", vec![json!({"id": 10, "bn": "B"})]),
5749 ("c", vec![json!({"id": 100, "cn": "C"})]),
5750 ]);
5751 let (_, rows) = go(
5752 "SELECT a.id, b.bn, c.cn FROM a \
5753 LEFT JOIN b ON b.id = a.bid \
5754 LEFT JOIN c ON c.id = a.cid",
5755 &t,
5756 );
5757 assert_eq!(rows.len(), 1);
5758 assert_eq!(col(&rows, "bn"), vec![json!("B")]);
5759 assert_eq!(col(&rows, "cn"), vec![json!("C")]);
5760 }
5761
5762 fn catalog() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5766 tables(vec![
5767 (
5768 "pg_namespace",
5769 vec![
5770 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5771 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5772 json!({"oid": 13000, "nspname": "information_schema", "nspowner": 10}),
5773 ],
5774 ),
5775 (
5776 "pg_class",
5777 vec![
5778 json!({"oid": 16401, "relname": "orders", "relnamespace": 2200,
5779 "relkind": "r", "relowner": 10, "relam": 2}),
5780 json!({"oid": 16402, "relname": "drivers", "relnamespace": 2200,
5781 "relkind": "r", "relowner": 10, "relam": 2}),
5782 ],
5783 ),
5784 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5785 ])
5786 }
5787
5788 #[test]
5789 fn THE_dn_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5790 let (names, rows) = go(
5791 r#"SELECT n.nspname AS "Name",
5792 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5793 FROM pg_catalog.pg_namespace n
5794 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5795 ORDER BY 1;"#,
5796 &catalog(),
5797 );
5798
5799 assert_eq!(names, vec!["Name", "Owner"], "psql reads these BY NAME");
5800 assert_eq!(col(&rows, "Name"), vec![json!("public")]);
5803 assert_eq!(col(&rows, "Owner"), vec![json!("nedb")]);
5804 }
5805
5806 #[test]
5807 fn THE_dt_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5808 let (names, rows) = go(
5809 r#"SELECT n.nspname as "Schema",
5810 c.relname as "Name",
5811 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5812 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5813 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5814 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5815 WHEN 'I' THEN 'partitioned index' END as "Type",
5816 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5817 FROM pg_catalog.pg_class c
5818 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5819 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5820 WHERE c.relkind IN ('r','p','')
5821 AND n.nspname <> 'pg_catalog'
5822 AND n.nspname !~ '^pg_toast'
5823 AND n.nspname <> 'information_schema'
5824 AND pg_catalog.pg_table_is_visible(c.oid)
5825 ORDER BY 1,2;"#,
5826 &catalog(),
5827 );
5828
5829 assert_eq!(names, vec!["Schema", "Name", "Type", "Owner"]);
5830 assert_eq!(col(&rows, "Name"), vec![json!("drivers"), json!("orders")]);
5832 assert_eq!(col(&rows, "Schema"), vec![json!("public"), json!("public")]);
5833 assert_eq!(col(&rows, "Type"), vec![json!("table"), json!("table")]);
5835 assert_eq!(col(&rows, "Owner"), vec![json!("nedb"), json!("nedb")]);
5836 }
5837
5838 #[test]
5839 fn the_dt_query_still_filters_correctly_with_a_system_relation_present() {
5840 let t = tables(vec![
5844 (
5845 "pg_namespace",
5846 vec![
5847 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5848 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5849 ],
5850 ),
5851 (
5852 "pg_class",
5853 vec![
5854 json!({"oid": 1, "relname": "mine", "relnamespace": 2200,
5855 "relkind": "r", "relowner": 10, "relam": 2}),
5856 json!({"oid": 2, "relname": "pg_internal", "relnamespace": 11,
5857 "relkind": "r", "relowner": 10, "relam": 2}),
5858 json!({"oid": 3, "relname": "an_index", "relnamespace": 2200,
5859 "relkind": "i", "relowner": 10, "relam": 2}),
5860 ],
5861 ),
5862 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5863 ]);
5864 let (_, rows) = go(
5865 r#"SELECT c.relname as "Name" FROM pg_catalog.pg_class c
5866 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5867 WHERE c.relkind IN ('r','p','') AND n.nspname <> 'pg_catalog'
5868 ORDER BY 1"#,
5869 &t,
5870 );
5871 assert_eq!(col(&rows, "Name"), vec![json!("mine")],
5872 "a system relation and an index must both be filtered out");
5873 }
5874}
5875
5876#[cfg(test)]
5877mod operator_syntax_tests {
5878 use super::*;
5879 use serde_json::json;
5880
5881 #[test]
5882 fn the_OPERATOR_qualification_psql_generates_is_understood() {
5883 let s = parse(
5887 "SELECT a FROM t WHERE n OPERATOR(pg_catalog.~) '^x' \
5888 AND m OPERATOR(pg_catalog.=) 1",
5889 )
5890 .expect("psql's OPERATOR() form must parse");
5891 match s.where_.unwrap() {
5892 Expr::Binary { op, left, .. } => {
5893 assert_eq!(op, "AND");
5894 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "~"));
5895 }
5896 other => panic!("{:?}", other),
5897 }
5898 }
5899
5900 #[test]
5901 fn an_OPERATOR_qualified_comparison_EVALUATES() {
5902 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
5903 Ok(Some(from_vec(vec![json!({"n": "orders"}), json!({"n": "pg_toast_1"})])))
5904 };
5905 let (_, rows) = run(
5906 "SELECT n FROM pg_class WHERE n OPERATOR(pg_catalog.~) '^ord'",
5907 &t,
5908 )
5909 .unwrap();
5910 assert_eq!(rows.len(), 1);
5911 assert_eq!(rows[0]["n"], json!("orders"));
5912 }
5913
5914 #[test]
5915 fn a_subquery_an_ARRAY_constructor_and_EXISTS_all_PARSE() {
5916 let s = parse("SELECT a FROM t WHERE x = (SELECT 1)").unwrap();
5919 assert!(matches!(s.where_, Some(Expr::Binary { ref right, .. }) if matches!(**right, Expr::Subquery(_))));
5920 let s = parse("SELECT array_to_string(ARRAY(SELECT a FROM b), ',') FROM t").unwrap();
5921 assert!(matches!(&s.items[0].expr, Expr::Func { args, .. } if matches!(args[0], Expr::ArrayQuery(_))));
5922 let s = parse("SELECT a FROM t WHERE EXISTS (SELECT 1)").unwrap();
5923 assert!(matches!(s.where_, Some(Expr::Exists { negated: false, .. })));
5924 let s = parse("SELECT a FROM t WHERE NOT EXISTS (SELECT 1)").unwrap();
5925 assert!(matches!(s.where_, Some(Expr::Unary { ref expr, .. }) if matches!(**expr, Expr::Exists { .. })));
5926 let s = parse("SELECT a FROM t WHERE oid = ANY (polroles) AND 'd' = any(kinds) AND x <> ALL (SELECT y FROM u)").unwrap();
5928 assert!(s.where_.is_some());
5929 let s = parse("SELECT prattrs[s] FROM t").unwrap();
5930 assert!(matches!(s.items[0].expr, Expr::Index { .. }));
5931 let s = parse("SELECT CAST('tuple' AS pg_catalog.text), CAST(n AS int2[]) FROM t").unwrap();
5932 assert!(matches!(&s.items[0].expr, Expr::Cast { ty, .. } if ty == "text"));
5933 assert!(matches!(&s.items[1].expr, Expr::Cast { ty, .. } if ty == "int2[]"));
5934 let s = parse("SELECT a FROM t WHERE a IS DISTINCT FROM b").unwrap();
5935 assert!(matches!(s.where_, Some(Expr::Binary { ref op, .. }) if op == "IS DISTINCT FROM"));
5936 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();
5938 assert_eq!(s.joins.len(), 3);
5939 assert!(matches!(s.joins[1].kind, JoinKind::Cross));
5940 let s = parse("SELECT 1 FROM c, LATERAL (SELECT 2 AS two) s").unwrap();
5942 assert!(s.joins[0].table.lateral && s.joins[0].table.sub.is_some());
5943 let s = parse("SELECT tt.a FROM (SELECT 1 AS a UNION ALL SELECT 2) AS tt ORDER BY 1").unwrap();
5944 assert_eq!(s.from.as_ref().unwrap().sub.as_ref().unwrap().set_ops.len(), 1);
5945 }
5946
5947 #[test]
5948 fn a_compound_query_keeps_ORDER_BY_for_the_whole() {
5949 let s = parse("SELECT a FROM t UNION SELECT b FROM u UNION ALL SELECT c FROM v ORDER BY 1 LIMIT 5").unwrap();
5950 assert_eq!(s.set_ops.len(), 2);
5951 assert_eq!(s.set_ops[0].op, SetOp::Union);
5952 assert!(!s.set_ops[0].all);
5953 assert!(s.set_ops[1].all);
5954 assert_eq!(s.order_by.len(), 1);
5955 assert_eq!(s.limit, Some(5));
5956 assert!(s.set_ops[1].query.order_by.is_empty(), "the tail belongs to the whole, not the last arm");
5957 }
5958}
5959
5960#[cfg(test)]
5961mod subquery_exec_tests {
5962 use super::*;
5963 use serde_json::json;
5964
5965 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5966 let owned: Vec<(String, Vec<Value>)> =
5967 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
5968 move |name: &str| {
5969 let bare = name.rsplit('.').next().unwrap_or(name);
5970 Ok(owned.iter().find(|(n, _)| n == name || n == bare).map(|(_, r)| from_vec(r.clone())))
5971 }
5972 }
5973
5974 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
5975 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
5976 (cols.into_iter().map(|c| c.name).collect(), rows)
5977 }
5978
5979 fn col(rows: &[Value], name: &str) -> Vec<Value> {
5980 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
5981 }
5982
5983 fn shop() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5984 tables(vec![
5985 ("c", vec![
5986 json!({"id": 1, "name": "ann", "tags": ["a", "b"]}),
5987 json!({"id": 2, "name": "bob", "tags": []}),
5988 json!({"id": 3, "name": "cyd", "tags": null}),
5989 ]),
5990 ("o", vec![
5991 json!({"oid": 10, "cid": 1, "total": 5}),
5992 json!({"oid": 11, "cid": 1, "total": 7}),
5993 json!({"oid": 12, "cid": 2, "total": 9}),
5994 ]),
5995 ])
5996 }
5997
5998 #[test]
5999 fn a_correlated_scalar_subquery_sees_the_outer_row() {
6000 let t = shop();
6001 let (_, rows) = go(
6002 "SELECT c.name, (SELECT sum(o.total) FROM o WHERE o.cid = c.id) AS spent FROM c ORDER BY c.id",
6003 &t,
6004 );
6005 assert_eq!(col(&rows, "spent"), vec![json!(12), json!(9), Value::Null]);
6006 let e = run("SELECT (SELECT o.total FROM o WHERE o.cid = c.id) FROM c", &t).unwrap_err().to_string();
6008 assert!(e.contains("more than one row"), "{}", e);
6009 let e = run("SELECT (SELECT oid, total FROM o) FROM c", &t).unwrap_err().to_string();
6011 assert!(e.contains("exactly one column"), "{}", e);
6012 }
6013
6014 #[test]
6015 fn EXISTS_and_NOT_EXISTS_are_never_unknown() {
6016 let t = shop();
6017 let (_, rows) = go("SELECT c.name FROM c WHERE EXISTS (SELECT 1 FROM o WHERE o.cid = c.id) ORDER BY 1", &t);
6018 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6019 let (_, rows) = go("SELECT c.name FROM c WHERE NOT EXISTS (SELECT 1 FROM o WHERE o.cid = c.id)", &t);
6020 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6021 }
6022
6023 #[test]
6024 fn ARRAY_of_a_subquery_and_array_to_string_compose_like_psql_dp() {
6025 let t = shop();
6026 let (_, rows) = go(
6027 "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",
6028 &t,
6029 );
6030 assert_eq!(col(&rows, "totals"), vec![json!("5,7"), json!("9"), json!("")]);
6032 let (_, rows) = go("SELECT array_length(ARRAY(SELECT oid FROM o), 1) AS n FROM c WHERE c.id = 1", &t);
6033 assert_eq!(col(&rows, "n"), vec![json!(3)]);
6034 }
6035
6036 #[test]
6037 fn ANY_ALL_and_IN_over_arrays_and_subqueries() {
6038 let t = shop();
6039 let (_, rows) = go("SELECT c.name FROM c WHERE 'a' = ANY (c.tags) ORDER BY 1", &t);
6040 assert_eq!(col(&rows, "name"), vec![json!("ann")]);
6041 let (_, rows) = go("SELECT c.name FROM c WHERE 'zz' = ANY (c.tags)", &t);
6043 assert!(rows.is_empty());
6044 let (_, rows) = go("SELECT c.name FROM c WHERE c.id = ANY (SELECT o.cid FROM o) ORDER BY 1", &t);
6045 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6046 let (_, rows) = go("SELECT c.name FROM c WHERE c.id <> ALL (SELECT o.cid FROM o)", &t);
6047 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6048 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);
6049 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6050 let (_, rows) = go("SELECT c.name FROM c WHERE c.id NOT IN (SELECT o.cid FROM o)", &t);
6051 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6052 let (_, rows) = go("SELECT c.tags[2] AS second FROM c WHERE c.id = 1", &t);
6054 assert_eq!(col(&rows, "second"), vec![json!("b")]);
6055 }
6056
6057 #[test]
6058 fn set_operations_combine_arms_and_sort_the_whole() {
6059 let t = shop();
6060 let (names, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1", &t);
6061 assert_eq!(names, vec!["k"], "column names come from the first arm");
6062 assert_eq!(col(&rows, "k"), vec![json!(1), json!(1), json!(1), json!(2), json!(2), json!(3)]);
6063 let (_, rows) = go("SELECT c.id AS k FROM c UNION SELECT o.cid FROM o ORDER BY 1", &t);
6064 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2), json!(3)]);
6065 let (_, rows) = go("SELECT c.id AS k FROM c INTERSECT SELECT o.cid FROM o ORDER BY 1", &t);
6066 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2)]);
6067 let (_, rows) = go("SELECT c.id AS k FROM c EXCEPT SELECT o.cid FROM o", &t);
6068 assert_eq!(col(&rows, "k"), vec![json!(3)]);
6069 let (_, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1 DESC LIMIT 2", &t);
6070 assert_eq!(col(&rows, "k"), vec![json!(3), json!(2)]);
6071 let e = run("SELECT c.id FROM c UNION SELECT o.oid, o.cid FROM o", &t).unwrap_err().to_string();
6072 assert!(e.contains("same number of columns"), "{}", e);
6073 }
6074
6075 #[test]
6076 fn a_derived_table_is_a_relation_and_LATERAL_sees_its_left() {
6077 let t = shop();
6078 let (_, rows) = go(
6079 "SELECT tt.who FROM (SELECT c.name AS who FROM c WHERE c.id < 3) AS tt ORDER BY 1",
6080 &t,
6081 );
6082 assert_eq!(col(&rows, "who"), vec![json!("ann"), json!("bob")]);
6083 let (_, rows) = go("SELECT tt.x FROM (SELECT c.name FROM c WHERE c.id = 1) AS tt(x)", &t);
6085 assert_eq!(col(&rows, "x"), vec![json!("ann")]);
6086 let (_, rows) = go(
6088 "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\"",
6089 &t,
6090 );
6091 assert_eq!(col(&rows, "Name"), vec![json!("ann"), json!("bob"), json!("cyd")]);
6092 assert_eq!(col(&rows, "Orders"), vec![json!(2), json!(1), json!(0)]);
6093 }
6094
6095 #[test]
6096 fn table_functions_generate_series_and_unnest() {
6097 let t = shop();
6098 let (_, rows) = go("SELECT s.generate_series AS n FROM generate_series(1, 3) s", &t);
6099 assert_eq!(col(&rows, "n"), vec![json!(1), json!(2), json!(3)]);
6100 let (_, rows) = go("SELECT x FROM pg_catalog.unnest(ARRAY['p', 'q']) AS t(x)", &t);
6101 assert_eq!(col(&rows, "x"), vec![json!("p"), json!("q")]);
6102 let (_, rows) = go(
6104 "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",
6105 &t,
6106 );
6107 assert_eq!(col(&rows, "tags"), vec![json!("a, b"), json!(""), json!("")]);
6108 let e = run("SELECT 1 FROM nosuchfn(1) f", &t).unwrap_err().to_string();
6109 assert!(e.contains("table function nosuchfn()"), "{}", e);
6110 }
6111
6112 #[test]
6113 fn aggregates_without_GROUP_BY_collapse_to_one_row() {
6114 let t = shop();
6115 let (names, rows) = go(
6116 "SELECT count(*), count(c.tags) AS tagged, min(c.name), max(c.name) AS hi, string_agg(c.name, '|') AS all FROM c",
6117 &t,
6118 );
6119 assert_eq!(names, vec!["count", "tagged", "min", "hi", "all"]);
6120 assert_eq!(rows.len(), 1);
6121 assert_eq!(rows[0]["count"], json!(3));
6122 assert_eq!(rows[0]["tagged"], json!(2), "count(x) skips NULL");
6123 assert_eq!(rows[0]["min"], json!("ann"));
6124 assert_eq!(rows[0]["hi"], json!("cyd"));
6125 assert_eq!(rows[0]["all"], json!("ann|bob|cyd"));
6126 let (_, rows) = go("SELECT count(*) AS n, sum(o.total) AS s FROM o WHERE o.total > 100", &t);
6128 assert_eq!(rows[0]["n"], json!(0));
6129 assert_eq!(rows[0]["s"], Value::Null);
6130 let (_, rows) = go("SELECT sum(o.total) / count(*) AS avg_total, avg(o.total) AS a FROM o", &t);
6133 assert_eq!(rows[0]["avg_total"], json!(7));
6134 assert_eq!(rows[0]["a"], json!(7));
6135 let e = run("SELECT c.name, count(*) FROM c", &t).unwrap_err().to_string();
6136 assert!(e.contains("must appear in the GROUP BY clause"), "{}", e);
6137 }
6138
6139 #[test]
6140 fn IS_DISTINCT_FROM_is_null_safe() {
6141 let t = shop();
6142 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS DISTINCT FROM NULL ORDER BY 1", &t);
6143 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6144 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS NOT DISTINCT FROM NULL", &t);
6145 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6146 }
6147
6148 #[test]
6149 fn THE_dT_QUERY_RUNS_over_a_catalogue_fixture() {
6150 let t = tables(vec![
6152 ("pg_namespace", vec![
6153 json!({"oid": 11, "nspname": "pg_catalog"}),
6154 json!({"oid": 2200, "nspname": "public"}),
6155 ]),
6156 ("pg_type", vec![
6157 json!({"oid": 25, "typname": "text", "typnamespace": 11, "typrelid": 0, "typelem": 0, "typarray": 1009}),
6158 json!({"oid": 1009, "typname": "_text", "typnamespace": 11, "typrelid": 0, "typelem": 25, "typarray": 0}),
6159 json!({"oid": 70000, "typname": "mood", "typnamespace": 2200, "typrelid": 0, "typelem": 0, "typarray": 70001}),
6160 json!({"oid": 70001, "typname": "_mood", "typnamespace": 2200, "typrelid": 0, "typelem": 70000, "typarray": 0}),
6161 ]),
6162 ("pg_class", vec![]),
6163 ]);
6164 let (_, rows) = go(
6165 r#"SELECT n.nspname as "Schema",
6166 pg_catalog.format_type(t.oid, NULL) AS "Name",
6167 pg_catalog.obj_description(t.oid, 'pg_type') as "Description"
6168 FROM pg_catalog.pg_type t
6169 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
6170 WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
6171 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
6172 AND n.nspname <> 'pg_catalog'
6173 AND n.nspname <> 'information_schema'
6174 AND pg_catalog.pg_type_is_visible(t.oid)
6175 ORDER BY 1, 2;"#,
6176 &t,
6177 );
6178 assert_eq!(rows.len(), 1, "{:?}", rows);
6181 assert_eq!(col(&rows, "Schema"), vec![json!("public")]);
6182 }
6183}
6184
6185#[cfg(test)]
6186mod collate_tests {
6187 use super::*;
6188 use serde_json::json;
6189
6190 #[test]
6191 fn COLLATE_is_consumed_because_it_cannot_change_the_answer() {
6192 for sql in [
6196 r#"SELECT a FROM t ORDER BY a COLLATE "C""#,
6197 r#"SELECT a COLLATE "C" FROM t"#,
6198 r#"SELECT a FROM t WHERE a COLLATE pg_catalog."C" = 'x'"#,
6199 ] {
6200 parse(sql).unwrap_or_else(|e| panic!("{} -> {}", sql, e));
6201 }
6202 assert!(parse("SELECT a FROM t ORDER BY a COLLATE").is_err());
6204 }
6205
6206 #[test]
6207 fn a_COLLATE_annotated_comparison_still_evaluates() {
6208 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
6209 Ok(Some(from_vec(vec![json!({"n": "b"}), json!({"n": "a"})])))
6210 };
6211 let (_, rows) = run(r#"SELECT n FROM pg_class ORDER BY n COLLATE "C""#, &t).unwrap();
6212 assert_eq!(rows[0]["n"], json!("a"), "the ORDER BY still sorts");
6213 }
6214}