1use crate::sqljoin::{self, JoinExec, Strategy};
42use crate::sqlplan::{Plan, Stage};
43use crate::sqlpush::Pushdown;
44use crate::wallclock::WallClock;
45
46use anyhow::{bail, Result};
47use serde_json::{Map, Value};
48
49#[derive(Debug, Clone, PartialEq)]
54pub enum Tok {
55 Word { upper: String, raw: String },
60 Quoted(String),
63 Str(String),
65 Num(f64),
66 Op(String),
67 Punct(char),
68 Eof,
69}
70
71impl Tok {
72 fn is_kw(&self, kw: &str) -> bool {
73 matches!(self, Tok::Word { upper, .. } if upper == kw)
74 }
75 #[allow(dead_code)] fn ident(&self) -> Option<String> {
78 match self {
79 Tok::Word { raw, .. } => Some(raw.clone()),
80 Tok::Quoted(s) => Some(s.clone()),
81 _ => None,
82 }
83 }
84}
85
86const OPERATORS: &[&str] = &[
90 "!~*", "!~", "~*", "<>", "!=", ">=", "<=", "||", "::",
91 "=", "<", ">", "~", "+", "-", "*", "/", "%",
92];
93
94pub fn lex(src: &str) -> Result<Vec<Tok>> {
95 let b: Vec<char> = src.chars().collect();
96 let mut out = vec![];
97 let mut i = 0usize;
98
99 while i < b.len() {
100 let c = b[i];
101
102 if c.is_whitespace() {
104 i += 1;
105 continue;
106 }
107
108 if c == '-' && b.get(i + 1) == Some(&'-') {
110 while i < b.len() && b[i] != '\n' {
111 i += 1;
112 }
113 continue;
114 }
115
116 if c == '/' && b.get(i + 1) == Some(&'*') {
118 let mut depth = 1usize;
119 i += 2;
120 while i < b.len() && depth > 0 {
121 if b[i] == '/' && b.get(i + 1) == Some(&'*') {
122 depth += 1;
123 i += 2;
124 } else if b[i] == '*' && b.get(i + 1) == Some(&'/') {
125 depth -= 1;
126 i += 2;
127 } else {
128 i += 1;
129 }
130 }
131 if depth > 0 {
132 bail!("unterminated /* comment");
133 }
134 continue;
135 }
136
137 if c == '\'' {
139 i += 1;
140 let mut s = String::new();
141 loop {
142 match b.get(i) {
143 None => bail!("unterminated string literal"),
144 Some('\'') if b.get(i + 1) == Some(&'\'') => {
145 s.push('\'');
146 i += 2;
147 }
148 Some('\'') => {
149 i += 1;
150 break;
151 }
152 Some(ch) => {
153 s.push(*ch);
154 i += 1;
155 }
156 }
157 }
158 out.push(Tok::Str(s));
159 continue;
160 }
161
162 if (c == 'E' || c == 'e') && b.get(i + 1) == Some(&'\'') {
165 i += 2;
166 let mut s = String::new();
167 loop {
168 match b.get(i) {
169 None => bail!("unterminated E'' string literal"),
170 Some('\\') => {
171 let esc = b.get(i + 1).copied().unwrap_or('\\');
175 s.push(match esc {
176 'n' => '\n',
177 't' => '\t',
178 'r' => '\r',
179 '0' => '\0',
180 other => other,
181 });
182 i += 2;
183 }
184 Some('\'') if b.get(i + 1) == Some(&'\'') => {
185 s.push('\'');
186 i += 2;
187 }
188 Some('\'') => {
189 i += 1;
190 break;
191 }
192 Some(ch) => {
193 s.push(*ch);
194 i += 1;
195 }
196 }
197 }
198 out.push(Tok::Str(s));
199 continue;
200 }
201
202 if c == '"' {
204 i += 1;
205 let mut s = String::new();
206 loop {
207 match b.get(i) {
208 None => bail!("unterminated quoted identifier"),
209 Some('"') if b.get(i + 1) == Some(&'"') => {
210 s.push('"');
211 i += 2;
212 }
213 Some('"') => {
214 i += 1;
215 break;
216 }
217 Some(ch) => {
218 s.push(*ch);
219 i += 1;
220 }
221 }
222 }
223 out.push(Tok::Quoted(s));
224 continue;
225 }
226
227 if c.is_ascii_digit()
229 || (c == '.' && b.get(i + 1).map(|d| d.is_ascii_digit()).unwrap_or(false))
230 {
231 let start = i;
232 while i < b.len() && (b[i].is_ascii_digit() || b[i] == '.') {
233 i += 1;
234 }
235 if i < b.len() && (b[i] == 'e' || b[i] == 'E') {
236 let save = i;
237 i += 1;
238 if i < b.len() && (b[i] == '+' || b[i] == '-') {
239 i += 1;
240 }
241 if i < b.len() && b[i].is_ascii_digit() {
242 while i < b.len() && b[i].is_ascii_digit() {
243 i += 1;
244 }
245 } else {
246 i = save; }
248 }
249 let text: String = b[start..i].iter().collect();
250 let n: f64 = text
251 .parse()
252 .map_err(|_| anyhow::anyhow!("not a number: {:?}", text))?;
253 out.push(Tok::Num(n));
254 continue;
255 }
256
257 if c.is_alphabetic() || c == '_' {
259 let start = i;
260 while i < b.len() && (b[i].is_alphanumeric() || b[i] == '_' || b[i] == '$') {
261 i += 1;
262 }
263 let raw: String = b[start..i].iter().collect();
264 out.push(Tok::Word { upper: raw.to_uppercase(), raw });
265 continue;
266 }
267
268 let rest: String = b[i..].iter().take(3).collect();
270 if let Some(op) = OPERATORS.iter().find(|o| rest.starts_with(**o)) {
271 i += op.chars().count();
272 out.push(Tok::Op((*op).to_string()));
273 continue;
274 }
275
276 if matches!(c, '(' | ')' | ',' | ';' | '.' | '[' | ']') {
277 out.push(Tok::Punct(c));
278 i += 1;
279 continue;
280 }
281
282 bail!("unexpected character {:?} in SQL", c);
285 }
286
287 out.push(Tok::Eof);
288 Ok(out)
289}
290
291#[derive(Debug, Clone, PartialEq)]
296pub enum Expr {
297 Column { qual: Option<String>, name: String },
301 Literal(Value),
302 Star,
304 QualifiedStar(String),
306 Func { name: String, args: Vec<Expr> },
307 Case {
312 operand: Option<Box<Expr>>,
313 whens: Vec<(Expr, Expr)>,
314 else_: Option<Box<Expr>>,
315 },
316 Binary { op: String, left: Box<Expr>, right: Box<Expr> },
317 Unary { op: String, expr: Box<Expr> },
318 InList { expr: Box<Expr>, list: Vec<Expr>, negated: bool },
320 IsNull { expr: Box<Expr>, negated: bool },
322 Cast { expr: Box<Expr>, ty: String },
327 Subquery(Box<Select>),
331 Exists { query: Box<Select>, negated: bool },
334 ArrayQuery(Box<Select>),
338 InSubquery { expr: Box<Expr>, query: Box<Select>, negated: bool },
340 Quantified { op: String, left: Box<Expr>, all: bool, right: Box<Expr> },
344 Index { expr: Box<Expr>, index: Box<Expr> },
346 ArrayLit(Vec<Expr>),
348 Agg {
362 name: String,
363 args: Vec<Expr>,
364 order_by: Vec<OrderBy>,
366 distinct: bool,
367 },
368}
369
370#[derive(Debug, Clone, PartialEq)]
371pub struct SelectItem {
372 pub expr: Expr,
373 pub alias: Option<String>,
376}
377
378#[derive(Debug, Clone, Copy, PartialEq, Eq)]
379pub enum JoinKind { Inner, Left, Right, Full, Cross }
380
381#[derive(Debug, Clone, PartialEq)]
382pub struct TableRef {
383 pub name: String,
391 pub alias: Option<String>,
392 pub sub: Option<Box<Select>>,
395 pub args: Option<Vec<Expr>>,
399 pub col_aliases: Vec<String>,
401 pub lateral: bool,
405 pub as_of: Option<u64>,
420 pub valid_as_of: Option<String>,
425 pub search: Option<String>,
430 pub trace: Option<String>,
444 pub trace_reverse: bool,
446 pub traverse: Option<String>,
449}
450
451impl TableRef {
452 pub fn named(name: impl Into<String>, alias: Option<String>) -> Self {
454 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 }
455 }
456
457 pub fn binding(&self) -> String {
460 self.alias.clone().unwrap_or_else(|| {
461 self.name.rsplit('.').next().unwrap_or(&self.name).to_string()
462 })
463 }
464}
465
466#[derive(Debug, Clone, Copy, PartialEq, Eq)]
468pub enum SetOp { Union, Intersect, Except }
469
470#[derive(Debug, Clone, PartialEq)]
472pub struct SetArm {
473 pub op: SetOp,
474 pub all: bool,
475 pub query: Select,
476}
477
478#[derive(Debug, Clone, PartialEq)]
479pub struct Join {
480 pub kind: JoinKind,
481 pub table: TableRef,
482 pub on: Option<Expr>,
483}
484
485#[derive(Debug, Clone, Copy, PartialEq)]
486pub enum Dir { Asc, Desc }
487
488#[derive(Debug, Clone, PartialEq)]
489pub struct OrderBy {
490 pub ordinal: Option<usize>,
494 pub expr: Option<Expr>,
495 pub dir: Dir,
496 pub nulls_first: bool,
498}
499
500#[derive(Debug, Clone, PartialEq)]
501pub struct Select {
502 pub distinct: bool,
503 pub items: Vec<SelectItem>,
504 pub from: Option<TableRef>,
505 pub joins: Vec<Join>,
506 pub where_: Option<Expr>,
507 pub group_by: Vec<Expr>,
510 pub having: Option<Expr>,
513 pub order_by: Vec<OrderBy>,
517 pub limit: Option<usize>,
518 pub offset: Option<usize>,
519 pub set_ops: Vec<SetArm>,
522}
523
524impl Select {
525 pub fn base_relations(&self) -> Vec<String> {
534 let mut out = vec![];
535 self.collect_relations(&mut out);
536 out
537 }
538
539 fn collect_relations(&self, out: &mut Vec<String>) {
540 fn table(t: &TableRef, out: &mut Vec<String>) {
541 if let Some(sub) = &t.sub {
542 sub.collect_relations(out);
543 } else if let Some(args) = &t.args {
544 for a in args {
545 expr(a, out);
546 }
547 } else {
548 out.push(t.name.clone());
549 }
550 }
551 fn expr(e: &Expr, out: &mut Vec<String>) {
552 match e {
553 Expr::Subquery(q) | Expr::ArrayQuery(q) => q.collect_relations(out),
554 Expr::Exists { query, .. } => query.collect_relations(out),
555 Expr::InSubquery { expr: x, query, .. } => {
556 expr(x, out);
557 query.collect_relations(out);
558 }
559 Expr::Quantified { left, right, .. } => {
560 expr(left, out);
561 expr(right, out);
562 }
563 Expr::Index { expr: x, index } => {
564 expr(x, out);
565 expr(index, out);
566 }
567 Expr::ArrayLit(items) | Expr::InList { list: items, .. } => {
568 if let Expr::InList { expr: x, .. } = e {
569 expr(x, out);
570 }
571 for i in items {
572 expr(i, out);
573 }
574 }
575 Expr::Func { args, .. } => {
576 for a in args {
577 expr(a, out);
578 }
579 }
580 Expr::Agg { args, order_by, .. } => {
581 for a in args {
582 expr(a, out);
583 }
584 for ob in order_by {
585 if let Some(e) = &ob.expr {
586 expr(e, out);
587 }
588 }
589 }
590 Expr::Case { operand, whens, else_ } => {
591 if let Some(o) = operand {
592 expr(o, out);
593 }
594 for (w, t) in whens {
595 expr(w, out);
596 expr(t, out);
597 }
598 if let Some(x) = else_ {
599 expr(x, out);
600 }
601 }
602 Expr::Binary { left, right, .. } => {
603 expr(left, out);
604 expr(right, out);
605 }
606 Expr::Unary { expr: x, .. } | Expr::Cast { expr: x, .. } | Expr::IsNull { expr: x, .. } => {
607 expr(x, out)
608 }
609 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => {}
610 }
611 }
612 if let Some(f) = &self.from {
613 table(f, out);
614 }
615 for j in &self.joins {
616 table(&j.table, out);
617 if let Some(on) = &j.on {
618 expr(on, out);
619 }
620 }
621 for item in &self.items {
622 expr(&item.expr, out);
623 }
624 if let Some(w) = &self.where_ {
625 expr(w, out);
626 }
627 for g in &self.group_by {
628 expr(g, out);
629 }
630 if let Some(h) = &self.having {
631 expr(h, out);
632 }
633 for ob in &self.order_by {
634 if let Some(e) = &ob.expr {
635 expr(e, out);
636 }
637 }
638 for arm in &self.set_ops {
639 arm.query.collect_relations(out);
640 }
641 }
642}
643
644fn binding_power(op: &str) -> Option<u8> {
655 Some(match op {
656 "OR" => 1,
657 "AND" => 2,
658 "=" | "!=" | "<>" | "<" | "<=" | ">" | ">=" | "~" | "~*" | "!~" | "!~*"
662 | "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => 4,
663 o if o.starts_with("LIKE ESCAPE ") || o.starts_with("ILIKE ESCAPE ")
665 || o.starts_with("NOT LIKE ESCAPE ") || o.starts_with("NOT ILIKE ESCAPE ") => 4,
666 "||" => 5,
667 "+" | "-" => 6,
668 "*" | "/" | "%" => 7,
669 _ => return None,
670 })
671}
672
673struct Parser {
674 toks: Vec<Tok>,
675 pos: usize,
676}
677
678impl Parser {
679 fn peek(&self) -> &Tok {
680 self.toks.get(self.pos).unwrap_or(&Tok::Eof)
681 }
682 fn peek_at(&self, n: usize) -> &Tok {
683 self.toks.get(self.pos + n).unwrap_or(&Tok::Eof)
684 }
685 fn next(&mut self) -> Tok {
686 let t = self.peek().clone();
687 self.pos += 1;
688 t
689 }
690 fn eat_kw(&mut self, kw: &str) -> bool {
691 if self.peek().is_kw(kw) {
692 self.pos += 1;
693 true
694 } else {
695 false
696 }
697 }
698 fn expect_kw(&mut self, kw: &str) -> Result<()> {
699 if self.eat_kw(kw) {
700 Ok(())
701 } else {
702 bail!("expected {} , got {:?}", kw, self.peek())
703 }
704 }
705 fn eat_punct(&mut self, c: char) -> bool {
706 if matches!(self.peek(), Tok::Punct(p) if *p == c) {
707 self.pos += 1;
708 true
709 } else {
710 false
711 }
712 }
713 fn expect_punct(&mut self, c: char) -> Result<()> {
714 if self.eat_punct(c) {
715 Ok(())
716 } else {
717 bail!("expected {:?}, got {:?}", c, self.peek())
718 }
719 }
720 fn eat_op(&mut self, op: &str) -> bool {
721 if matches!(self.peek(), Tok::Op(o) if o == op) {
722 self.pos += 1;
723 true
724 } else {
725 false
726 }
727 }
728
729 fn parse_expr(&mut self) -> Result<Expr> {
732 self.parse_bin(0)
733 }
734
735 fn parse_bin(&mut self, min_bp: u8) -> Result<Expr> {
738 let mut left = self.parse_unary()?;
739
740 loop {
741 if self.peek().is_kw("OPERATOR") && matches!(self.peek_at(1), Tok::Punct('(')) {
748 let save = self.pos;
749 self.pos += 2;
750 let mut sym = None;
752 while sym.is_none() {
753 match self.next() {
754 Tok::Op(o) => sym = Some(o),
755 Tok::Word { .. } | Tok::Punct('.') => continue,
756 _ => break,
757 }
758 }
759 match sym {
760 Some(o) if binding_power(&o).is_some() && self.eat_punct(')') => {
761 let bp = binding_power(&o).unwrap();
762 if bp < min_bp {
763 self.pos = save;
764 break;
765 }
766 let right = self.parse_bin(bp + 1)?;
767 left = Expr::Binary {
768 op: o,
769 left: Box::new(left),
770 right: Box::new(right),
771 };
772 continue;
773 }
774 _ => {
777 self.pos = save;
778 break;
779 }
780 }
781 }
782
783 let (op, width) = match self.peek() {
784 Tok::Op(o) if binding_power(o).is_some() => (o.clone(), 1usize),
785 Tok::Word { upper, .. } if upper == "AND" || upper == "OR" => (upper.clone(), 1),
786 Tok::Word { upper, .. } if upper == "LIKE" || upper == "ILIKE" => (upper.clone(), 1),
787 Tok::Word { upper, .. } if upper == "ESCAPE" => break,
790 Tok::Word { upper, .. } if upper == "NOT" => {
791 match self.peek_at(1) {
793 Tok::Word { upper: u2, .. } if u2 == "LIKE" || u2 == "ILIKE" => {
794 (format!("NOT {}", u2), 2)
795 }
796 _ => break,
797 }
798 }
799 _ => break,
800 };
801
802 let bp = match binding_power(&op) {
803 Some(bp) if bp >= min_bp => bp,
804 _ => break,
805 };
806 self.pos += width;
807
808 let quant = match self.peek() {
814 Tok::Word { upper, .. }
815 if matches!(upper.as_str(), "ANY" | "SOME" | "ALL")
816 && matches!(self.peek_at(1), Tok::Punct('(')) =>
817 {
818 Some(upper == "ALL")
819 }
820 _ => None,
821 };
822 if let Some(all) = quant {
823 self.pos += 2; let right = if self.peek().is_kw("SELECT") {
825 Expr::ArrayQuery(Box::new(self.parse_query()?))
826 } else {
827 self.parse_expr()?
828 };
829 self.expect_punct(')')?;
830 left = Expr::Quantified { op, left: Box::new(left), all, right: Box::new(right) };
831 continue;
832 }
833
834 let right = self.parse_bin(bp + 1)?;
836
837 let op = if matches!(op.as_str(), "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE")
844 && self.peek().is_kw("ESCAPE")
845 {
846 self.next();
847 match self.next() {
848 Tok::Str(e) => {
849 let mut ch = e.chars();
850 match (ch.next(), ch.next()) {
851 (Some(c), None) => format!("{} ESCAPE {}", op, c),
853 _ => bail!(
854 "ESCAPE takes a single-character string, got {:?}", e),
855 }
856 }
857 other => bail!("ESCAPE takes a string literal, got {:?}", other),
858 }
859 } else {
860 op
861 };
862
863 left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
864 }
865
866 Ok(left)
867 }
868
869 fn parse_postfix(&mut self, mut e: Expr) -> Result<Expr> {
870 loop {
871 if matches!(self.peek(), Tok::Punct('[')) {
874 self.pos += 1;
875 let index = self.parse_expr()?;
876 self.expect_punct(']')?;
877 e = Expr::Index { expr: Box::new(e), index: Box::new(index) };
878 continue;
879 }
880
881 if self.peek().is_kw("IS") {
883 self.pos += 1;
884 let negated = self.eat_kw("NOT");
885 if self.eat_kw("DISTINCT") {
889 self.expect_kw("FROM")?;
890 let rhs = self.parse_bin(5)?;
892 e = Expr::Binary {
893 op: if negated { "IS NOT DISTINCT FROM".into() } else { "IS DISTINCT FROM".into() },
894 left: Box::new(e),
895 right: Box::new(rhs),
896 };
897 continue;
898 }
899 if !self.eat_kw("NULL") {
900 if self.eat_kw("TRUE") {
902 e = Expr::Binary {
903 op: "=".into(),
904 left: Box::new(e),
905 right: Box::new(Expr::Literal(Value::Bool(!negated))),
906 };
907 continue;
908 }
909 if self.eat_kw("FALSE") {
910 e = Expr::Binary {
911 op: "=".into(),
912 left: Box::new(e),
913 right: Box::new(Expr::Literal(Value::Bool(negated))),
914 };
915 continue;
916 }
917 bail!("expected NULL, TRUE or FALSE after IS, got {:?}", self.peek());
918 }
919 e = Expr::IsNull { expr: Box::new(e), negated };
920 continue;
921 }
922
923 let negated_in = if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("IN") {
925 self.pos += 2;
926 true
927 } else if self.peek().is_kw("IN") {
928 self.pos += 1;
929 false
930 } else {
931 let negated_between =
933 if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("BETWEEN") {
934 self.pos += 2;
935 true
936 } else if self.peek().is_kw("BETWEEN") {
937 self.pos += 1;
938 false
939 } else {
940 break;
941 };
942 let low = self.parse_bin(3)?;
946 self.expect_kw("AND")?;
947 let high = self.parse_bin(3)?;
948 let ge = Expr::Binary {
949 op: ">=".into(),
950 left: Box::new(e.clone()),
951 right: Box::new(low),
952 };
953 let le = Expr::Binary {
954 op: "<=".into(),
955 left: Box::new(e),
956 right: Box::new(high),
957 };
958 let both = Expr::Binary {
959 op: "AND".into(),
960 left: Box::new(ge),
961 right: Box::new(le),
962 };
963 e = if negated_between {
964 Expr::Unary { op: "NOT".into(), expr: Box::new(both) }
965 } else {
966 both
967 };
968 continue;
969 };
970
971 self.expect_punct('(')?;
972 if self.peek().is_kw("SELECT") {
974 let query = Box::new(self.parse_query()?);
975 self.expect_punct(')')?;
976 e = Expr::InSubquery { expr: Box::new(e), query, negated: negated_in };
977 continue;
978 }
979 let mut list = vec![];
980 if !self.eat_punct(')') {
981 loop {
982 list.push(self.parse_expr()?);
983 if self.eat_punct(',') {
984 continue;
985 }
986 self.expect_punct(')')?;
987 break;
988 }
989 }
990 e = Expr::InList { expr: Box::new(e), list, negated: negated_in };
991 }
992 Ok(e)
993 }
994
995 fn parse_unary(&mut self) -> Result<Expr> {
996 if self.peek().is_kw("NOT") {
997 self.pos += 1;
998 let e = self.parse_bin(3)?;
1001 return Ok(Expr::Unary { op: "NOT".into(), expr: Box::new(e) });
1002 }
1003 if self.eat_op("-") {
1004 let e = self.parse_unary()?;
1005 return Ok(Expr::Unary { op: "-".into(), expr: Box::new(e) });
1006 }
1007 if self.eat_op("+") {
1008 return self.parse_unary();
1009 }
1010 let atom = self.parse_atom()?;
1011 let cast = self.parse_casts(atom)?;
1012 self.parse_postfix(cast)
1023 }
1024
1025 fn parse_casts(&mut self, mut e: Expr) -> Result<Expr> {
1027 loop {
1028 if self.peek().is_kw("COLLATE") {
1033 self.pos += 1;
1034 match self.next() {
1035 Tok::Word { .. } | Tok::Quoted(_) => {}
1036 other => bail!("expected a collation name after COLLATE, got {:?}", other),
1037 }
1038 while self.eat_punct('.') {
1040 match self.next() {
1041 Tok::Word { .. } | Tok::Quoted(_) => {}
1042 other => bail!("expected a name after '.', got {:?}", other),
1043 }
1044 }
1045 continue;
1046 }
1047 if !self.eat_op("::") {
1048 break;
1049 }
1050 let mut ty = match self.next() {
1051 Tok::Word { raw, .. } => raw,
1052 Tok::Quoted(s) => s,
1053 other => bail!("expected a type name after ::, got {:?}", other),
1054 };
1055 while self.eat_punct('.') {
1057 match self.next() {
1058 Tok::Word { raw, .. } => ty = raw,
1059 Tok::Quoted(s) => ty = s,
1060 other => bail!("expected a type name after ., got {:?}", other),
1061 }
1062 }
1063 while self.eat_punct('[') {
1065 self.expect_punct(']')?;
1066 ty.push_str("[]");
1067 }
1068 e = Expr::Cast { expr: Box::new(e), ty };
1069 }
1070 Ok(e)
1071 }
1072
1073
1074 fn parse_atom(&mut self) -> Result<Expr> {
1075 if self.eat_punct('(') {
1082 if self.peek().is_kw("SELECT") {
1085 let q = self.parse_query()?;
1086 self.expect_punct(')')?;
1087 return Ok(Expr::Subquery(Box::new(q)));
1088 }
1089 let e = self.parse_expr()?;
1090 self.expect_punct(')')?;
1091 return Ok(e);
1092 }
1093
1094 if self.peek().is_kw("ARRAY") && matches!(self.peek_at(1), Tok::Punct('(') | Tok::Punct('[')) {
1098 self.pos += 1;
1099 if self.eat_punct('(') {
1100 if !self.peek().is_kw("SELECT") {
1101 bail!("ARRAY(...) takes a subquery; for a list of values write ARRAY[...]");
1102 }
1103 let q = self.parse_query()?;
1104 self.expect_punct(')')?;
1105 return Ok(Expr::ArrayQuery(Box::new(q)));
1106 }
1107 self.expect_punct('[')?;
1108 let mut items = vec![];
1109 if !self.eat_punct(']') {
1110 loop {
1111 items.push(self.parse_expr()?);
1112 if self.eat_punct(',') {
1113 continue;
1114 }
1115 self.expect_punct(']')?;
1116 break;
1117 }
1118 }
1119 return Ok(Expr::ArrayLit(items));
1120 }
1121
1122 if self.peek().is_kw("EXISTS") && matches!(self.peek_at(1), Tok::Punct('(')) {
1126 self.pos += 2;
1127 if !self.peek().is_kw("SELECT") {
1128 bail!("EXISTS (...) takes a subquery");
1129 }
1130 let q = self.parse_query()?;
1131 self.expect_punct(')')?;
1132 return Ok(Expr::Exists { query: Box::new(q), negated: false });
1133 }
1134
1135 if self.peek().is_kw("CAST") && matches!(self.peek_at(1), Tok::Punct('(')) {
1138 self.pos += 2;
1139 let inner = self.parse_expr()?;
1140 self.expect_kw("AS")?;
1141 let mut ty = match self.next() {
1142 Tok::Word { raw, .. } => raw,
1143 Tok::Quoted(s) => s,
1144 other => bail!("expected a type name in CAST, got {:?}", other),
1145 };
1146 while self.eat_punct('.') {
1147 match self.next() {
1148 Tok::Word { raw, .. } => ty = raw,
1149 Tok::Quoted(s) => ty = s,
1150 other => bail!("expected a type name after ., got {:?}", other),
1151 }
1152 }
1153 while self.eat_punct('[') {
1154 self.expect_punct(']')?;
1155 ty.push_str("[]");
1156 }
1157 self.expect_punct(')')?;
1158 return Ok(Expr::Cast { expr: Box::new(inner), ty });
1159 }
1160
1161 if self.peek().is_kw("CASE") {
1163 return self.parse_case();
1164 }
1165
1166 match self.next() {
1167 Tok::Num(n) => Ok(Expr::Literal(from_f64(n))),
1168 Tok::Str(s) => Ok(Expr::Literal(Value::String(s))),
1169 Tok::Op(o) if o == "*" => Ok(Expr::Star),
1170 Tok::Quoted(name) => self.parse_name_tail(None, name),
1171 Tok::Word { upper, raw } => match upper.as_str() {
1172 "NULL" => Ok(Expr::Literal(Value::Null)),
1173 "TRUE" => Ok(Expr::Literal(Value::Bool(true))),
1174 "FALSE" => Ok(Expr::Literal(Value::Bool(false))),
1175 "CURRENT_SCHEMA" | "CURRENT_DATABASE" | "CURRENT_USER" | "SESSION_USER"
1179 | "CURRENT_CATALOG" | "USER" | "VERSION"
1180 if !matches!(self.peek(), Tok::Punct('(')) =>
1181 {
1182 Ok(Expr::Func { name: upper.to_lowercase(), args: vec![] })
1183 }
1184 _ => self.parse_name_tail(None, raw),
1185 },
1186 other => bail!("unexpected {:?} in an expression", other),
1187 }
1188 }
1189
1190 fn parse_name_tail(&mut self, _schema: Option<String>, first: String) -> Result<Expr> {
1197 let mut parts = vec![first];
1198 while self.eat_punct('.') {
1199 if self.eat_op("*") {
1201 return Ok(Expr::QualifiedStar(parts.pop().unwrap_or_default()));
1202 }
1203 match self.next() {
1204 Tok::Word { raw, .. } => parts.push(raw),
1205 Tok::Quoted(s) => parts.push(s),
1206 other => bail!("expected a name after '.', got {:?}", other),
1207 }
1208 }
1209
1210 if matches!(self.peek(), Tok::Punct('(')) {
1214 self.pos += 1;
1215 let name = parts.pop().unwrap_or_default().to_lowercase();
1216 let agg = is_aggregate(&name);
1217 let distinct = agg && self.eat_kw("DISTINCT");
1219 let mut args = vec![];
1220 let mut order_by = vec![];
1221 if !self.eat_punct(')') {
1222 loop {
1223 if self.eat_op("*") {
1225 args.push(Expr::Star);
1226 } else {
1227 args.push(self.parse_expr()?);
1228 }
1229 if self.eat_punct(',') {
1230 continue;
1231 }
1232 if agg && self.peek().is_kw("ORDER") {
1236 self.pos += 1;
1237 self.expect_kw("BY")?;
1238 order_by = self.parse_sort_list()?;
1239 }
1240 self.expect_punct(')')?;
1241 break;
1242 }
1243 }
1244 if agg {
1245 return Ok(Expr::Agg { name, args, order_by, distinct });
1246 }
1247 return Ok(Expr::Func { name, args });
1248 }
1249
1250 let name = parts.pop().unwrap_or_default();
1251 let qual = parts.pop();
1255 Ok(Expr::Column { qual, name })
1256 }
1257
1258 fn parse_case(&mut self) -> Result<Expr> {
1259 self.expect_kw("CASE")?;
1260 let operand = if self.peek().is_kw("WHEN") {
1262 None
1263 } else {
1264 Some(Box::new(self.parse_expr()?))
1265 };
1266 let mut whens = vec![];
1267 while self.eat_kw("WHEN") {
1268 let cond = self.parse_expr()?;
1269 self.expect_kw("THEN")?;
1270 let then = self.parse_expr()?;
1271 whens.push((cond, then));
1272 }
1273 if whens.is_empty() {
1274 bail!("CASE needs at least one WHEN branch");
1275 }
1276 let else_ = if self.eat_kw("ELSE") {
1277 Some(Box::new(self.parse_expr()?))
1278 } else {
1279 None
1280 };
1281 self.expect_kw("END")?;
1282 Ok(Expr::Case { operand, whens, else_ })
1283 }
1284
1285 fn parse_table_ref(&mut self) -> Result<TableRef> {
1288 let lateral = self.eat_kw("LATERAL");
1289 if self.eat_punct('(') {
1291 if !self.peek().is_kw("SELECT") {
1292 bail!("expected a subquery after '(' in FROM, got {:?}", self.peek());
1293 }
1294 let sub = self.parse_query()?;
1295 self.expect_punct(')')?;
1296 let (alias, col_aliases) = self.parse_table_alias()?;
1297 if alias.is_none() {
1298 bail!("a subquery in FROM must have an alias");
1299 }
1300 return Ok(TableRef {
1301 name: "(subquery)".into(),
1302 alias,
1303 sub: Some(Box::new(sub)),
1304 args: None,
1305 col_aliases,
1306 lateral,
1307 trace: None,
1310 trace_reverse: false,
1311 traverse: None,
1312 as_of: None,
1313 valid_as_of: None,
1314 search: None,
1315 });
1316 }
1317 if lateral {
1318 bail!("LATERAL applies to a subquery in FROM; write LATERAL (SELECT ...)");
1319 }
1320
1321 let mut parts = vec![match self.next() {
1322 Tok::Word { raw, .. } => raw,
1323 Tok::Quoted(s) => s,
1324 other => bail!("expected a table name, got {:?}", other),
1325 }];
1326 while self.eat_punct('.') {
1327 match self.next() {
1328 Tok::Word { raw, .. } => parts.push(raw),
1329 Tok::Quoted(s) => parts.push(s),
1330 other => bail!("expected a name after '.', got {:?}", other),
1331 }
1332 }
1333 let name = parts.join(".");
1334
1335 if self.eat_punct('(') {
1338 let mut args = vec![];
1339 if !self.eat_punct(')') {
1340 loop {
1341 args.push(self.parse_expr()?);
1342 if self.eat_punct(',') {
1343 continue;
1344 }
1345 self.expect_punct(')')?;
1346 break;
1347 }
1348 }
1349 let fname = name.rsplit('.').next().unwrap_or(&name).to_lowercase();
1350 let (alias, col_aliases) = self.parse_table_alias()?;
1351 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 });
1352 }
1353
1354 let as_of = if self.peek().is_kw("AS") && self.peek_at(1).is_kw("OF") {
1369 self.next();
1370 self.next();
1371 if self.eat_kw("SYSTEM") {
1374 self.expect_kw("TIME")?;
1375 }
1376 match self.next() {
1377 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Some(n as u64),
1378 Tok::Str(s) => Some(WallClock::parse(&s)?.as_marker()),
1379 other => bail!(
1380 "AS OF SYSTEM TIME takes a sequence number or a quoted datetime \
1381 (got {:?}). Bare integers stay sequence numbers — exact, never \
1382 garbage-collected; quote a datetime to travel by wall clock",
1383 other
1384 ),
1385 }
1386 } else {
1387 None
1388 };
1389 let valid_as_of = if self.peek().is_kw("VALID")
1405 && self.peek_at(1).is_kw("AS")
1406 && self.peek_at(2).is_kw("OF")
1407 {
1408 self.next();
1409 self.next();
1410 self.next();
1411 match self.next() {
1412 Tok::Str(s) => Some(s),
1413 other => bail!(
1414 "VALID AS OF takes a date string here (got {:?}). System time is a \
1415 sequence and application-time validity is a date — they are different \
1416 questions, so they take different arguments",
1417 other
1418 ),
1419 }
1420 } else {
1421 None
1422 };
1423
1424 let search = if self.peek().is_kw("SEARCH") && matches!(self.peek_at(1), Tok::Str(_)) {
1425 self.next();
1426 match self.next() {
1427 Tok::Str(s) => Some(s),
1428 other => bail!("SEARCH takes a string here, got {:?}", other),
1431 }
1432 } else {
1433 None
1434 };
1435
1436 fn names_a_thing(t: &Tok) -> bool {
1446 match t {
1447 Tok::Word { upper, .. } => !is_clause_keyword(upper),
1448 Tok::Quoted(_) => true,
1449 _ => false,
1450 }
1451 }
1452
1453 let (trace, trace_reverse) = if self.peek().is_kw("TRACE")
1454 && names_a_thing(self.peek_at(1))
1455 {
1456 self.next();
1457 let edge = match self.next() {
1458 Tok::Word { raw, .. } => raw,
1459 Tok::Quoted(s) => s,
1460 other => bail!("TRACE takes an edge type here, got {:?}", other),
1461 };
1462 let rev = if self.peek().is_kw("REVERSE") {
1463 self.next();
1464 true
1465 } else {
1466 false
1467 };
1468 (Some(edge), rev)
1469 } else {
1470 (None, false)
1471 };
1472
1473 let traverse = if self.peek().is_kw("TRAVERSE")
1474 && names_a_thing(self.peek_at(1))
1475 {
1476 self.next();
1477 match self.next() {
1478 Tok::Word { raw, .. } => Some(raw),
1479 Tok::Quoted(s) => Some(s),
1480 other => bail!("TRAVERSE takes a relation name here, got {:?}", other),
1481 }
1482 } else {
1483 None
1484 };
1485
1486 let (alias, col_aliases) = self.parse_table_alias()?;
1487 Ok(TableRef { name, alias, sub: None, args: None, col_aliases, lateral: false, as_of, valid_as_of, search, trace, trace_reverse, traverse })
1488 }
1489
1490 fn parse_table_alias(&mut self) -> Result<(Option<String>, Vec<String>)> {
1495 let alias = if self.eat_kw("AS") {
1496 match self.next() {
1497 Tok::Word { raw, .. } => Some(raw),
1498 Tok::Quoted(s) => Some(s),
1499 other => bail!("expected an alias after AS, got {:?}", other),
1500 }
1501 } else {
1502 match self.peek().clone() {
1503 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1504 self.pos += 1;
1505 Some(raw)
1506 }
1507 Tok::Quoted(s) => {
1508 self.pos += 1;
1509 Some(s)
1510 }
1511 _ => None,
1512 }
1513 };
1514 let mut col_aliases = vec![];
1515 if alias.is_some() && self.eat_punct('(') {
1516 loop {
1517 match self.next() {
1518 Tok::Word { raw, .. } => col_aliases.push(raw),
1519 Tok::Quoted(s) => col_aliases.push(s),
1520 other => bail!("expected a column alias, got {:?}", other),
1521 }
1522 if self.eat_punct(',') {
1523 continue;
1524 }
1525 self.expect_punct(')')?;
1526 break;
1527 }
1528 }
1529 Ok((alias, col_aliases))
1530 }
1531
1532 fn parse_query(&mut self) -> Result<Select> {
1539 let mut first = self.parse_select_body()?;
1540 loop {
1541 let op = if self.eat_kw("UNION") {
1542 SetOp::Union
1543 } else if self.eat_kw("INTERSECT") {
1544 SetOp::Intersect
1545 } else if self.eat_kw("EXCEPT") {
1546 SetOp::Except
1547 } else {
1548 break;
1549 };
1550 let all = self.eat_kw("ALL");
1551 if !all {
1552 let _ = self.eat_kw("DISTINCT");
1553 }
1554 let query = if self.eat_punct('(') {
1556 let q = self.parse_query()?;
1557 self.expect_punct(')')?;
1558 q
1559 } else {
1560 self.parse_select_body()?
1561 };
1562 first.set_ops.push(SetArm { op, all, query });
1563 }
1564 self.parse_query_tail(&mut first)?;
1565 Ok(first)
1566 }
1567
1568 fn parse_sort_list(&mut self) -> Result<Vec<OrderBy>> {
1572 let mut out = vec![];
1573 loop {
1574 let (ordinal, expr) = match self.peek().clone() {
1578 Tok::Num(n)
1579 if n.fract() == 0.0
1580 && n >= 1.0
1581 && !matches!(self.peek_at(1), Tok::Op(_)) =>
1582 {
1583 self.pos += 1;
1584 (Some(n as usize), None)
1585 }
1586 _ => (None, Some(self.parse_expr()?)),
1587 };
1588 let dir = if self.eat_kw("DESC") {
1589 Dir::Desc
1590 } else {
1591 let _ = self.eat_kw("ASC");
1592 Dir::Asc
1593 };
1594 let mut nulls_first = matches!(dir, Dir::Desc);
1596 if self.eat_kw("NULLS") {
1597 if self.eat_kw("FIRST") {
1598 nulls_first = true;
1599 } else if self.eat_kw("LAST") {
1600 nulls_first = false;
1601 } else {
1602 bail!("expected FIRST or LAST after NULLS, got {:?}", self.peek());
1603 }
1604 }
1605 out.push(OrderBy { ordinal, expr, dir, nulls_first });
1606 if self.eat_punct(',') {
1607 continue;
1608 }
1609 break;
1610 }
1611 Ok(out)
1612 }
1613
1614 fn parse_query_tail(&mut self, sel: &mut Select) -> Result<()> {
1615 let mut order_by = vec![];
1616 if self.eat_kw("ORDER") {
1617 self.expect_kw("BY")?;
1618 order_by = self.parse_sort_list()?;
1619 }
1620
1621 let mut limit = None;
1622 let mut offset = None;
1623 loop {
1625 if self.eat_kw("LIMIT") {
1626 if self.eat_kw("ALL") {
1627 limit = None;
1628 } else {
1629 limit = Some(self.parse_count("LIMIT")?);
1630 }
1631 continue;
1632 }
1633 if self.eat_kw("OFFSET") {
1634 offset = Some(self.parse_count("OFFSET")?);
1635 let _ = self.eat_kw("ROW") || self.eat_kw("ROWS");
1636 continue;
1637 }
1638 break;
1639 }
1640 sel.order_by = order_by;
1641 sel.limit = limit;
1642 sel.offset = offset;
1643 Ok(())
1644 }
1645
1646 fn parse_select_body(&mut self) -> Result<Select> {
1648 self.expect_kw("SELECT")?;
1649 let distinct = self.eat_kw("DISTINCT");
1650 if distinct && self.peek().is_kw("ON") {
1651 bail!("DISTINCT ON is not supported");
1652 }
1653 let _ = self.eat_kw("ALL");
1654
1655 let mut items = vec![];
1656 loop {
1657 let expr = self.parse_expr()?;
1658 let alias = if self.eat_kw("AS") {
1660 match self.next() {
1661 Tok::Word { raw, .. } => Some(raw),
1662 Tok::Quoted(s) => Some(s),
1663 other => bail!("expected an alias after AS, got {:?}", other),
1664 }
1665 } else {
1666 match self.peek().clone() {
1667 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1668 self.pos += 1;
1669 Some(raw)
1670 }
1671 Tok::Quoted(s) => {
1672 self.pos += 1;
1673 Some(s)
1674 }
1675 _ => None,
1676 }
1677 };
1678 items.push(SelectItem { expr, alias });
1679 if self.eat_punct(',') {
1680 continue;
1681 }
1682 break;
1683 }
1684
1685 let mut from = None;
1686 let mut joins = vec![];
1687 if self.eat_kw("FROM") {
1688 from = Some(self.parse_table_ref()?);
1689 loop {
1690 if self.eat_punct(',') {
1696 let table = self.parse_table_ref()?;
1697 joins.push(Join { kind: JoinKind::Cross, table, on: None });
1698 continue;
1699 }
1700 let kind = if self.peek().is_kw("JOIN") {
1701 self.pos += 1;
1702 JoinKind::Inner
1703 } else if self.peek().is_kw("INNER") && self.peek_at(1).is_kw("JOIN") {
1704 self.pos += 2;
1705 JoinKind::Inner
1706 } else if self.peek().is_kw("CROSS") && self.peek_at(1).is_kw("JOIN") {
1707 self.pos += 2;
1708 JoinKind::Cross
1709 } else if self.peek().is_kw("LEFT") {
1710 self.pos += 1;
1711 let _ = self.eat_kw("OUTER");
1712 self.expect_kw("JOIN")?;
1713 JoinKind::Left
1714 } else if self.peek().is_kw("RIGHT") {
1715 self.pos += 1;
1716 let _ = self.eat_kw("OUTER");
1717 self.expect_kw("JOIN")?;
1718 JoinKind::Right
1719 } else if self.peek().is_kw("FULL") {
1720 self.pos += 1;
1721 let _ = self.eat_kw("OUTER");
1722 self.expect_kw("JOIN")?;
1723 JoinKind::Full
1724 } else {
1725 break;
1726 };
1727 let table = self.parse_table_ref()?;
1728 let on = if self.eat_kw("ON") {
1729 Some(self.parse_expr()?)
1730 } else if self.peek().is_kw("USING") {
1731 bail!("JOIN ... USING is not supported — write ON a.col = b.col");
1732 } else {
1733 None
1734 };
1735 if on.is_none() && !matches!(kind, JoinKind::Cross) {
1736 bail!("a {:?} JOIN needs an ON clause", kind);
1737 }
1738 joins.push(Join { kind, table, on });
1739 }
1740 }
1741
1742 let where_ = if self.eat_kw("WHERE") {
1743 Some(self.parse_expr()?)
1744 } else {
1745 None
1746 };
1747
1748 let mut group_by = vec![];
1749 if self.eat_kw("GROUP") {
1750 self.expect_kw("BY")?;
1751 if self.eat_kw("ALL") || self.eat_kw("DISTINCT") {
1752 bail!("GROUP BY ALL / DISTINCT is not supported — list the keys");
1753 }
1754 loop {
1755 if self.peek().is_kw("ROLLUP")
1756 || self.peek().is_kw("CUBE")
1757 || self.peek().is_kw("GROUPING")
1758 {
1759 bail!("GROUP BY ROLLUP / CUBE / GROUPING SETS is not supported");
1760 }
1761 group_by.push(self.parse_expr()?);
1762 if self.eat_punct(',') {
1763 continue;
1764 }
1765 break;
1766 }
1767 }
1768
1769 let having = if self.eat_kw("HAVING") {
1770 Some(self.parse_expr()?)
1771 } else {
1772 None
1773 };
1774 if having.is_some() && group_by.is_empty() && !items.iter().any(|i| has_aggregate(&i.expr))
1775 {
1776 bail!("HAVING needs a GROUP BY or an aggregate — it filters groups, not rows; \
1777 use WHERE to filter rows");
1778 }
1779
1780 Ok(Select {
1781 distinct,
1782 items,
1783 from,
1784 joins,
1785 where_,
1786 group_by,
1787 having,
1788 order_by: vec![],
1789 limit: None,
1790 offset: None,
1791 set_ops: vec![],
1792 })
1793 }
1794
1795 fn parse_count(&mut self, what: &str) -> Result<usize> {
1796 match self.next() {
1797 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Ok(n as usize),
1798 other => bail!("{} expects a non-negative integer, got {:?}", what, other),
1799 }
1800 }
1801}
1802
1803fn is_clause_keyword(upper: &str) -> bool {
1808 matches!(
1809 upper,
1810 "FROM" | "WHERE" | "GROUP" | "HAVING" | "ORDER" | "LIMIT" | "OFFSET"
1811 | "JOIN" | "LEFT" | "RIGHT" | "FULL" | "INNER" | "CROSS" | "OUTER"
1812 | "ON" | "USING" | "AND" | "OR" | "AS" | "UNION" | "INTERSECT"
1813 | "EXCEPT" | "FETCH" | "FOR" | "WINDOW" | "RETURNING" | "INTO"
1814 | "ASC" | "DESC" | "NULLS" | "IS" | "IN" | "NOT" | "LIKE" | "ILIKE"
1815 | "BETWEEN" | "THEN" | "WHEN" | "ELSE" | "END" | "CASE" | "DISTINCT"
1816 | "SELECT" | "WITH" | "ALL"
1817 )
1818}
1819
1820pub fn parse(sql: &str) -> Result<Select> {
1822 let toks = lex(sql)?;
1823 let mut p = Parser { toks, pos: 0 };
1824 let sel = if matches!(p.peek(), Tok::Punct('(')) && p.peek_at(1).is_kw("SELECT") {
1826 p.pos += 1;
1827 let mut first = p.parse_query()?;
1828 p.expect_punct(')')?;
1829 loop {
1831 let op = if p.eat_kw("UNION") {
1832 SetOp::Union
1833 } else if p.eat_kw("INTERSECT") {
1834 SetOp::Intersect
1835 } else if p.eat_kw("EXCEPT") {
1836 SetOp::Except
1837 } else {
1838 break;
1839 };
1840 let all = p.eat_kw("ALL");
1841 if !all {
1842 let _ = p.eat_kw("DISTINCT");
1843 }
1844 let query = if p.eat_punct('(') {
1845 let q = p.parse_query()?;
1846 p.expect_punct(')')?;
1847 q
1848 } else {
1849 p.parse_select_body()?
1850 };
1851 first.set_ops.push(SetArm { op, all, query });
1852 }
1853 p.parse_query_tail(&mut first)?;
1854 first
1855 } else {
1856 p.parse_query()?
1857 };
1858 let _ = p.eat_punct(';');
1859 if !matches!(p.peek(), Tok::Eof) {
1860 bail!("unexpected trailing tokens: {:?}", p.peek());
1861 }
1862 Ok(sel)
1863}
1864
1865pub struct Bound<'a> {
1878 pub parts: Vec<(String, Option<&'a Value>)>,
1879 pub ctx: EvalCtx<'a>,
1881}
1882
1883#[derive(Clone, Copy, Default)]
1896pub struct EvalCtx<'a> {
1897 pub resolver: Option<&'a Resolver<'a>>,
1898 pub outer: Option<&'a Bound<'a>>,
1899}
1900
1901impl<'a> Bound<'a> {
1902 pub fn new(parts: Vec<(String, Option<&'a Value>)>) -> Self {
1904 Bound { parts, ctx: EvalCtx::default() }
1905 }
1906
1907 fn column(&self, qual: Option<&str>, name: &str) -> Value {
1914 match qual {
1915 Some(q) => {
1916 for (binding, row) in &self.parts {
1917 if binding.eq_ignore_ascii_case(q) {
1918 return row
1919 .and_then(|r| r.get(name))
1920 .cloned()
1921 .unwrap_or(Value::Null);
1922 }
1923 }
1924 match self.ctx.outer {
1925 Some(o) if o.has_binding(q) => o.column(qual, name),
1926 _ => Value::Null,
1927 }
1928 }
1929 None => {
1930 for (_, row) in &self.parts {
1931 if let Some(v) = row.and_then(|r| r.get(name)) {
1932 return v.clone();
1933 }
1934 }
1935 match self.ctx.outer {
1936 Some(o) => o.column(None, name),
1937 None => Value::Null,
1938 }
1939 }
1940 }
1941 }
1942
1943 fn has_binding(&self, qual: &str) -> bool {
1948 self.parts.iter().any(|(b, _)| b.eq_ignore_ascii_case(qual))
1949 || self.ctx.outer.is_some_and(|o| o.has_binding(qual))
1950 }
1951
1952 fn flatten(&self) -> Vec<(String, Value)> {
1954 let mut out = vec![];
1955 for (_, row) in &self.parts {
1956 if let Some(Value::Object(m)) = row {
1957 for (k, v) in m {
1958 out.push((k.clone(), v.clone()));
1959 }
1960 }
1961 }
1962 out
1963 }
1964
1965 fn flatten_binding(&self, qual: &str) -> Vec<(String, Value)> {
1966 let mut out = vec![];
1967 for (binding, row) in &self.parts {
1968 if binding.eq_ignore_ascii_case(qual) {
1969 if let Some(Value::Object(m)) = row {
1970 for (k, v) in m {
1971 out.push((k.clone(), v.clone()));
1972 }
1973 }
1974 }
1975 }
1976 out
1977 }
1978}
1979
1980type Truth = Option<bool>;
1988
1989fn truthy(v: &Value) -> Truth {
1990 match v {
1991 Value::Null => None,
1992 Value::Bool(b) => Some(*b),
1993 _ => None,
1997 }
1998}
1999
2000fn cmp_values(a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
2006 use std::cmp::Ordering;
2007 match (a, b) {
2008 (Value::Null, _) | (_, Value::Null) => None,
2009 (Value::Number(x), Value::Number(y)) => {
2010 x.as_f64().partial_cmp(&y.as_f64())
2011 }
2012 (Value::String(x), Value::String(y)) => Some(x.cmp(y)),
2013 (Value::Bool(x), Value::Bool(y)) => Some(x.cmp(y)),
2014 (Value::Number(x), Value::String(y)) => match y.parse::<f64>() {
2017 Ok(n) => x.as_f64().partial_cmp(&Some(n)),
2018 Err(_) => Some(as_text(a).cmp(&as_text(b))),
2019 },
2020 (Value::String(x), Value::Number(y)) => match x.parse::<f64>() {
2021 Ok(n) => Some(n).partial_cmp(&y.as_f64()),
2022 Err(_) => Some(as_text(a).cmp(&as_text(b))),
2023 },
2024 _ => {
2025 let (x, y) = (as_text(a), as_text(b));
2026 if x == y { Some(Ordering::Equal) } else { Some(x.cmp(&y)) }
2027 }
2028 }
2029}
2030
2031fn as_text(v: &Value) -> String {
2033 match v {
2034 Value::String(s) => s.clone(),
2035 Value::Null => String::new(),
2036 Value::Bool(b) => (if *b { "t" } else { "f" }).to_string(),
2037 Value::Array(items) => {
2040 let inner: Vec<String> = items
2041 .iter()
2042 .map(|i| match i {
2043 Value::Null => "NULL".to_string(),
2044 Value::String(s) if s.is_empty()
2045 || s.chars().any(|c| c.is_whitespace() || matches!(c, ',' | '{' | '}' | '"' | '\\')) =>
2046 {
2047 format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
2048 }
2049 other => as_text(other),
2050 })
2051 .collect();
2052 format!("{{{}}}", inner.join(","))
2053 }
2054 other => other.to_string(),
2055 }
2056}
2057
2058fn num(v: &Value) -> Option<f64> {
2059 match v {
2060 Value::Number(n) => n.as_f64(),
2061 Value::String(s) => s.parse().ok(),
2062 Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
2063 _ => None,
2064 }
2065}
2066
2067fn from_f64(f: f64) -> Value {
2081 if f.is_finite() && f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
2082 return Value::Number((f as i64).into());
2083 }
2084 serde_json::Number::from_f64(f).map(Value::Number).unwrap_or(Value::Null)
2085}
2086
2087pub fn eval(e: &Expr, row: &Bound) -> Result<Value> {
2089 Ok(match e {
2090 Expr::Literal(v) => v.clone(),
2091
2092 Expr::Column { qual, name } => {
2093 if let Some(q) = qual {
2097 if !row.has_binding(q) {
2098 bail!("no table or alias named {:?} in this query", q);
2099 }
2100 }
2101 row.column(qual.as_deref(), name)
2102 }
2103
2104 Expr::Cast { expr, .. } => eval(expr, row)?,
2105
2106 Expr::Star | Expr::QualifiedStar(_) => {
2107 bail!("`*` is only valid in a select list or as count(*)")
2108 }
2109
2110 Expr::Unary { op, expr } => {
2111 let v = eval(expr, row)?;
2112 match op.as_str() {
2113 "NOT" => match truthy(&v) {
2114 None => Value::Null,
2116 Some(b) => Value::Bool(!b),
2117 },
2118 "-" => match num(&v) {
2119 Some(n) => from_f64(-n),
2120 None => Value::Null,
2121 },
2122 other => bail!("unsupported unary operator {:?}", other),
2123 }
2124 }
2125
2126 Expr::Binary { op, left, right } => {
2127 if op == "AND" {
2132 let l = truthy(&eval(left, row)?);
2133 if l == Some(false) {
2134 return Ok(Value::Bool(false));
2135 }
2136 let r = truthy(&eval(right, row)?);
2137 return Ok(match (l, r) {
2138 (_, Some(false)) => Value::Bool(false),
2139 (Some(true), Some(true)) => Value::Bool(true),
2140 _ => Value::Null,
2141 });
2142 }
2143 if op == "OR" {
2144 let l = truthy(&eval(left, row)?);
2145 if l == Some(true) {
2146 return Ok(Value::Bool(true));
2147 }
2148 let r = truthy(&eval(right, row)?);
2149 return Ok(match (l, r) {
2150 (_, Some(true)) => Value::Bool(true),
2151 (Some(false), Some(false)) => Value::Bool(false),
2152 _ => Value::Null,
2153 });
2154 }
2155
2156 let l = eval(left, row)?;
2157 let r = eval(right, row)?;
2158 apply_op(op, l, r)?
2159 }
2160
2161 Expr::IsNull { expr, negated } => {
2162 let v = eval(expr, row)?;
2163 Value::Bool(v.is_null() != *negated)
2166 }
2167
2168 Expr::InList { expr, list, negated } => {
2169 let v = eval(expr, row)?;
2170 if v.is_null() {
2171 return Ok(Value::Null);
2172 }
2173 let mut items = Vec::with_capacity(list.len());
2174 for item in list {
2175 items.push(eval(item, row)?);
2176 }
2177 in_values(&v, &items, *negated)?
2178 }
2179
2180 Expr::Subquery(q) => {
2182 let (cols, rows) = run_sub(q, row)?;
2183 if cols.len() != 1 {
2184 bail!("a subquery used as an expression must return exactly one \
2185 column, this one returns {}", cols.len());
2186 }
2187 match rows.len() {
2188 0 => Value::Null,
2189 1 => rows[0].get(&cols[0].key).cloned().unwrap_or(Value::Null),
2190 n => bail!("more than one row returned by a subquery used as an \
2191 expression ({} rows)", n),
2192 }
2193 }
2194 Expr::Exists { query, negated } => {
2195 let (_, rows) = run_sub(query, row)?;
2196 Value::Bool(!rows.is_empty() != *negated)
2197 }
2198 Expr::ArrayQuery(q) => Value::Array(first_column(q, row)?),
2199 Expr::InSubquery { expr, query, negated } => {
2200 let v = eval(expr, row)?;
2201 if v.is_null() {
2202 return Ok(Value::Null);
2203 }
2204 let items = first_column(query, row)?;
2205 in_values(&v, &items, *negated)?
2206 }
2207 Expr::Quantified { op, left, all, right } => {
2208 let l = eval(left, row)?;
2209 let r = eval(right, row)?;
2210 let items = match r {
2211 Value::Null => return Ok(Value::Null),
2212 Value::Array(items) => items,
2213 other => bail!(
2214 "{} requires an array or a subquery on its right side, got {}",
2215 if *all { "ALL" } else { "ANY" },
2216 as_text(&other)
2217 ),
2218 };
2219 let mut saw_true = false;
2223 let mut saw_false = false;
2224 let mut saw_null = false;
2225 for item in items {
2226 match truthy(&apply_op(op, l.clone(), item)?) {
2227 Some(true) => saw_true = true,
2228 Some(false) => saw_false = true,
2229 None => saw_null = true,
2230 }
2231 }
2232 if *all {
2233 if saw_false {
2234 Value::Bool(false)
2235 } else if saw_null {
2236 Value::Null
2237 } else {
2238 Value::Bool(true)
2239 }
2240 } else if saw_true {
2241 Value::Bool(true)
2242 } else if saw_null {
2243 Value::Null
2244 } else {
2245 Value::Bool(false)
2246 }
2247 }
2248 Expr::Index { expr, index } => {
2249 let arr = eval(expr, row)?;
2250 let i = eval(index, row)?;
2251 match (arr, num(&i)) {
2252 (Value::Array(items), Some(n)) if n >= 1.0 => {
2253 items.get(n as usize - 1).cloned().unwrap_or(Value::Null)
2254 }
2255 _ => Value::Null,
2256 }
2257 }
2258 Expr::ArrayLit(items) => {
2259 let mut out = Vec::with_capacity(items.len());
2260 for i in items {
2261 out.push(eval(i, row)?);
2262 }
2263 Value::Array(out)
2264 }
2265
2266 Expr::Case { operand, whens, else_ } => {
2267 let subject = match operand {
2268 Some(o) => Some(eval(o, row)?),
2269 None => None,
2270 };
2271 for (cond, then) in whens {
2272 let hit = match &subject {
2273 Some(sv) => {
2275 let cv = eval(cond, row)?;
2276 matches!(cmp_values(sv, &cv), Some(std::cmp::Ordering::Equal))
2277 }
2278 None => truthy(&eval(cond, row)?) == Some(true),
2281 };
2282 if hit {
2283 return eval(then, row);
2284 }
2285 }
2286 match else_ {
2287 Some(e) => eval(e, row)?,
2288 None => Value::Null,
2291 }
2292 }
2293
2294 Expr::Func { name, args } => eval_func(name, args, row)?,
2295
2296 Expr::Agg { name, .. } => bail!(
2302 "{}() is an aggregate and has no value for one row — it is reduced \
2303 over a GROUP. Reaching this point is an engine bug, not a problem \
2304 with the query",
2305 name
2306 ),
2307 })
2308}
2309
2310fn in_values(v: &Value, items: &[Value], negated: bool) -> Result<Value> {
2312 let mut any_null = false;
2313 let mut found = false;
2314 for iv in items {
2315 if iv.is_null() {
2316 any_null = true;
2317 continue;
2318 }
2319 if matches!(cmp_values(v, iv), Some(std::cmp::Ordering::Equal)) {
2320 found = true;
2321 break;
2322 }
2323 }
2324 Ok(if found {
2328 Value::Bool(!negated)
2329 } else if any_null {
2330 Value::Null
2331 } else {
2332 Value::Bool(negated)
2333 })
2334}
2335
2336fn run_sub(q: &Select, row: &Bound) -> Result<(Vec<OutCol>, Vec<Value>)> {
2344 let Some(resolve) = row.ctx.resolver else {
2345 bail!("a subquery cannot run here: this evaluation has no relation resolver");
2346 };
2347 let (cols, rows, _) = execute_inner(q, resolve, Opts::default(), Some(row))?;
2348 Ok((cols, rows))
2349}
2350
2351fn first_column(q: &Select, row: &Bound) -> Result<Vec<Value>> {
2354 let (cols, rows) = run_sub(q, row)?;
2355 let Some(first) = cols.first() else {
2356 bail!("the subquery returns no columns");
2357 };
2358 Ok(rows
2359 .into_iter()
2360 .map(|r| r.get(&first.key).cloned().unwrap_or(Value::Null))
2361 .collect())
2362}
2363
2364fn apply_op(op: &str, l: Value, r: Value) -> Result<Value> {
2368 let compare = |ord: fn(std::cmp::Ordering) -> bool| -> Value {
2370 match cmp_values(&l, &r) {
2371 None => Value::Null,
2372 Some(o) => Value::Bool(ord(o)),
2373 }
2374 };
2375
2376 Ok(match op {
2377 "IS DISTINCT FROM" | "IS NOT DISTINCT FROM" => {
2378 let distinct = match (l.is_null(), r.is_null()) {
2379 (true, true) => false,
2380 (true, false) | (false, true) => true,
2381 (false, false) => !matches!(cmp_values(&l, &r), Some(std::cmp::Ordering::Equal)),
2382 };
2383 Value::Bool(distinct != op.starts_with("IS NOT"))
2384 }
2385 "=" => compare(|o| o.is_eq()),
2386 "!=" | "<>" => compare(|o| o.is_ne()),
2387 "<" => compare(|o| o.is_lt()),
2388 "<=" => compare(|o| o.is_le()),
2389 ">" => compare(|o| o.is_gt()),
2390 ">=" => compare(|o| o.is_ge()),
2391
2392 "~" | "~*" | "!~" | "!~*" => {
2393 if l.is_null() || r.is_null() {
2394 Value::Null
2395 } else {
2396 let pat = as_text(&r);
2397 if let Some(why) = crate::nql::regex_error_pub(&pat) {
2398 bail!(
2399 "{} — in {:?}. The supported subset is ^ $ . | ( ) \
2400 [ ] * + ? and literal text",
2401 why, pat
2402 );
2403 }
2404 let hit = crate::nql::regex_match_pub(
2405 &as_text(&l), &pat, op.ends_with('*'));
2406 Value::Bool(hit != op.starts_with('!'))
2407 }
2408 }
2409
2410 _ if op.starts_with("LIKE") || op.starts_with("ILIKE")
2411 || op.starts_with("NOT LIKE") || op.starts_with("NOT ILIKE") => {
2412 if l.is_null() || r.is_null() {
2413 Value::Null
2414 } else {
2415 let (base, esc): (&str, Option<char>) =
2417 match op.split_once(" ESCAPE ") {
2418 Some((b, e)) => (b, e.chars().next()),
2419 None => (&op[..], None),
2420 };
2421 let ci = base.ends_with("ILIKE");
2422 let txt = as_text(&l);
2423 let pat = as_text(&r);
2424 let hit = match esc {
2425 Some(c) => crate::nql::like_match_escape_pub(&txt, &pat, ci, c),
2426 None => crate::nql::like_match_pub(&txt, &pat, ci),
2427 };
2428 Value::Bool(hit != base.starts_with("NOT"))
2429 }
2430 }
2431
2432 "||" => {
2434 if l.is_null() || r.is_null() {
2435 Value::Null
2436 } else {
2437 Value::String(format!("{}{}", as_text(&l), as_text(&r)))
2438 }
2439 }
2440
2441 "+" | "-" | "*" | "/" | "%" => match (num(&l), num(&r)) {
2442 (Some(a), Some(b)) => match op {
2443 "+" => from_f64(a + b),
2444 "-" => from_f64(a - b),
2445 "*" => from_f64(a * b),
2446 "/" if b == 0.0 => bail!("division by zero"),
2449 "/" => from_f64(a / b),
2450 "%" if b == 0.0 => bail!("division by zero"),
2451 "%" => from_f64(a % b),
2452 _ => unreachable!(),
2453 },
2454 _ => Value::Null,
2455 },
2456
2457 other => bail!("unsupported operator {:?}", other),
2458 })
2459}
2460
2461fn eval_func(name: &str, args: &[Expr], row: &Bound) -> Result<Value> {
2467 let arg = |i: usize| -> Result<Value> {
2470 match args.get(i) {
2471 Some(e) => eval(e, row),
2472 None => Ok(Value::Null),
2473 }
2474 };
2475
2476 Ok(match name {
2477 "pg_get_userbyid" | "current_user" | "session_user" | "user" => {
2481 Value::String("nedb".into())
2482 }
2483 "current_schema" => Value::String("public".into()),
2484 "current_database" | "current_catalog" => Value::String("nedb".into()),
2485 "version" => Value::String(crate::pgwire::version_string()),
2486
2487 "pg_table_is_visible" | "pg_type_is_visible" | "pg_function_is_visible"
2492 | "pg_opclass_is_visible" | "pg_conversion_is_visible" => Value::Bool(true),
2493
2494 "pg_encoding_to_char" => Value::String("UTF8".into()),
2496 "pg_get_expr" | "pg_get_indexdef" | "pg_get_constraintdef"
2497 | "pg_get_viewdef" | "pg_get_partkeydef" | "obj_description"
2498 | "col_description" | "shobj_description" => Value::Null,
2499
2500 "lower" => match arg(0)? {
2502 Value::Null => Value::Null,
2503 v => Value::String(as_text(&v).to_lowercase()),
2504 },
2505 "upper" => match arg(0)? {
2506 Value::Null => Value::Null,
2507 v => Value::String(as_text(&v).to_uppercase()),
2508 },
2509 "length" | "char_length" | "character_length" => match arg(0)? {
2510 Value::Null => Value::Null,
2511 v => from_f64(as_text(&v).chars().count() as f64),
2512 },
2513 "format_type" => match arg(0)? {
2514 Value::Null => Value::Null,
2515 v => Value::String(crate::pgcatalog::type_name_pub(
2516 num(&v).unwrap_or(25.0) as i32).to_string()),
2517 },
2518 "array_to_string" | "pg_catalog.array_to_string" => {
2519 match arg(0)? {
2523 Value::Array(items) => {
2524 let sep = as_text(&arg(1)?);
2525 Value::String(
2526 items.iter().map(as_text).collect::<Vec<_>>().join(&sep),
2527 )
2528 }
2529 _ => Value::Null,
2530 }
2531 }
2532 "quote_ident" => Value::String(as_text(&arg(0)?)),
2533 "quote_literal" => Value::String(format!("'{}'", as_text(&arg(0)?).replace('\'', "''"))),
2534 "format" => {
2538 let fmt = as_text(&arg(0)?);
2539 let mut out = String::new();
2540 let mut next = 1usize;
2541 let mut chars = fmt.chars().peekable();
2542 while let Some(c) = chars.next() {
2543 if c != '%' {
2544 out.push(c);
2545 continue;
2546 }
2547 match chars.next() {
2548 Some('%') => out.push('%'),
2549 Some(spec @ ('s' | 'I' | 'L')) => {
2550 let v = arg(next)?;
2551 next += 1;
2552 match (spec, &v) {
2553 ('L', Value::Null) => out.push_str("NULL"),
2554 ('L', v) => out.push_str(&format!("'{}'", as_text(v).replace('\'', "''"))),
2555 (_, v) => out.push_str(&as_text(v)),
2556 }
2557 }
2558 other => bail!("format(): unsupported conversion %{}", other.map(String::from).unwrap_or_default()),
2559 }
2560 }
2561 Value::String(out)
2562 }
2563
2564 "array_length" | "array_upper" | "cardinality" => match arg(0)? {
2568 Value::Array(items) if !items.is_empty() => from_f64(items.len() as f64),
2569 Value::Array(_) if name == "cardinality" => from_f64(0.0),
2570 _ => Value::Null,
2571 },
2572 "array_lower" => match arg(0)? {
2573 Value::Array(items) if !items.is_empty() => from_f64(1.0),
2574 _ => Value::Null,
2575 },
2576
2577 "pg_table_size" | "pg_total_relation_size" | "pg_relation_size"
2584 | "pg_indexes_size" | "pg_database_size" => Value::Null,
2585 "pg_size_pretty" => match num(&arg(0)?) {
2586 None => Value::Null,
2587 Some(n) => {
2588 let units = ["bytes", "kB", "MB", "GB", "TB", "PB"];
2589 let mut v = n;
2590 let mut u = 0usize;
2591 while v.abs() >= 10240.0 && u + 1 < units.len() {
2592 v /= 1024.0;
2593 u += 1;
2594 }
2595 Value::String(format!("{} {}", v.round() as i64, units[u]))
2596 }
2597 },
2598
2599 "pg_get_triggerdef" | "pg_get_ruledef" | "pg_get_statisticsobjdef"
2605 | "pg_get_statisticsobjdef_columns" | "pg_get_function_result"
2606 | "pg_get_function_arguments" | "pg_get_function_identity_arguments"
2607 | "pg_get_functiondef" | "pg_get_serial_sequence" | "pg_get_partition_constraintdef"
2608 | "pg_relation_filepath" | "pg_tablespace_location" => Value::Null,
2609 "pg_relation_is_publishable" => Value::Bool(true),
2610 "pg_statistics_obj_is_visible" | "pg_opfamily_is_visible" | "pg_collation_is_visible"
2611 | "pg_ts_config_is_visible" | "pg_ts_dict_is_visible" | "pg_ts_parser_is_visible"
2612 | "pg_ts_template_is_visible" | "has_table_privilege" | "has_schema_privilege"
2613 | "has_database_privilege" | "pg_has_role" => Value::Bool(true),
2614 "current_setting" => match arg(0)? {
2617 Value::Null => Value::Null,
2618 v => match as_text(&v).to_lowercase().as_str() {
2619 "server_version" => Value::String(crate::pgwire::version_string()),
2620 "server_encoding" | "client_encoding" => Value::String("UTF8".into()),
2621 "standard_conforming_strings" | "integer_datetimes" | "is_superuser" => {
2622 Value::String("on".into())
2623 }
2624 "timezone" | "log_timezone" => Value::String("UTC".into()),
2625 "search_path" => Value::String("\"$user\", public".into()),
2626 "intervalstyle" => Value::String("postgres".into()),
2627 "datestyle" => Value::String("ISO, MDY".into()),
2628 "session_authorization" => Value::String("nedb".into()),
2629 "application_name" | "default_transaction_read_only" => Value::String(String::new()),
2630 "transaction_isolation" | "default_transaction_isolation" => {
2631 Value::String("read committed".into())
2632 }
2633 "max_identifier_length" => Value::String("63".into()),
2634 other => {
2635 if truthy(&arg(1)?) == Some(true) {
2638 Value::Null
2639 } else {
2640 bail!("unrecognized configuration parameter \"{}\"", other)
2641 }
2642 }
2643 },
2644 },
2645 "pg_backend_pid" => from_f64(std::process::id() as f64),
2646 "pg_is_in_recovery" => Value::Bool(false),
2647 "txid_current" => from_f64(0.0),
2648 "now" | "current_timestamp" | "statement_timestamp" | "clock_timestamp" => {
2649 Value::String(now_iso())
2650 }
2651 "to_char" => match arg(0)? {
2652 Value::Null => Value::Null,
2653 v => Value::String(as_text(&v)),
2654 },
2655 "generate_series" | "unnest" => bail!(
2656 "{}() returns a set of rows — write it in FROM, not in the select list", name
2657 ),
2658
2659 "coalesce" => {
2661 let mut out = Value::Null;
2662 for a in args {
2663 let v = eval(a, row)?;
2664 if !v.is_null() {
2665 out = v;
2666 break;
2667 }
2668 }
2669 out
2670 }
2671 "nullif" => {
2672 let a = arg(0)?;
2673 let b = arg(1)?;
2674 if matches!(cmp_values(&a, &b), Some(std::cmp::Ordering::Equal)) {
2675 Value::Null
2676 } else {
2677 a
2678 }
2679 }
2680
2681 "int4" | "int8" | "int2" => match num(&arg(0)?) {
2683 Some(n) => from_f64(n.trunc()),
2684 None => Value::Null,
2685 },
2686 "text" => match arg(0)? {
2687 Value::Null => Value::Null,
2688 v => Value::String(as_text(&v)),
2689 },
2690
2691 other if is_aggregate(other) => bail!(
2692 "{}() is an aggregate, which is only meaningful over a whole result set — \
2693 it is evaluated by the executor, never per row",
2694 other
2695 ),
2696
2697 other => bail!(
2698 "the function {}() is not implemented. It is refused rather than \
2699 answered with NULL, because a NULL column reads as missing DATA \
2700 rather than a missing feature",
2701 other
2702 ),
2703 })
2704}
2705
2706fn now_iso() -> String {
2708 let secs = std::time::SystemTime::now()
2709 .duration_since(std::time::UNIX_EPOCH)
2710 .map(|d| d.as_secs())
2711 .unwrap_or(0);
2712 let days = (secs / 86_400) as i64;
2714 let rem = secs % 86_400;
2715 let z = days + 719_468;
2716 let era = z.div_euclid(146_097);
2717 let doe = z.rem_euclid(146_097);
2718 let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
2719 let y = yoe + era * 400;
2720 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2721 let mp = (5 * doy + 2) / 153;
2722 let d = doy - (153 * mp + 2) / 5 + 1;
2723 let m = if mp < 10 { mp + 3 } else { mp - 9 };
2724 let y = if m <= 2 { y + 1 } else { y };
2725 format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}+00", y, m, d, rem / 3600, (rem % 3600) / 60, rem % 60)
2726}
2727
2728const AGGREGATES: &[&str] = &[
2733 "count", "sum", "avg", "min", "max", "string_agg", "array_agg", "bool_and",
2734 "bool_or", "every",
2735];
2736
2737fn is_aggregate(name: &str) -> bool {
2738 AGGREGATES.iter().any(|a| a.eq_ignore_ascii_case(name))
2739}
2740
2741pub fn has_aggregate(e: &Expr) -> bool {
2744 match e {
2745 Expr::Agg { .. } => true,
2746 Expr::Func { args, .. } => args.iter().any(has_aggregate),
2747 Expr::Binary { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2748 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2749 has_aggregate(expr)
2750 }
2751 Expr::InList { expr, list, .. } => has_aggregate(expr) || list.iter().any(has_aggregate),
2752 Expr::Case { operand, whens, else_ } => {
2753 operand.as_deref().is_some_and(has_aggregate)
2754 || whens.iter().any(|(c, t)| has_aggregate(c) || has_aggregate(t))
2755 || else_.as_deref().is_some_and(has_aggregate)
2756 }
2757 Expr::Quantified { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2758 Expr::Index { expr, index } => has_aggregate(expr) || has_aggregate(index),
2759 Expr::ArrayLit(items) => items.iter().any(has_aggregate),
2760 Expr::InSubquery { expr, .. } => has_aggregate(expr),
2761 Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => false,
2762 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => false,
2763 }
2764}
2765
2766fn aggregate(
2773 name: &str,
2774 args: &[Expr],
2775 order_by: &[OrderBy],
2776 distinct: bool,
2777 rows: &[JoinedRow],
2778 ctx: EvalCtx,
2779) -> Result<Value> {
2780 let lname = name.to_lowercase();
2781
2782 let ordered: Vec<JoinedRow> = if order_by.is_empty() {
2785 rows.to_vec()
2786 } else {
2787 let mut keyed: Vec<(Vec<Value>, JoinedRow)> = Vec::with_capacity(rows.len());
2788 for r in rows {
2789 let b = bind(r, ctx);
2790 let mut key = vec![];
2791 for ob in order_by {
2792 match (&ob.expr, ob.ordinal) {
2795 (Some(e), _) => key.push(eval(e, &b)?),
2796 (None, Some(n)) => bail!(
2797 "ORDER BY {} inside an aggregate refers to a select-list \
2798 position, which an aggregate does not have — name the \
2799 column instead", n
2800 ),
2801 (None, None) => key.push(Value::Null),
2802 }
2803 }
2804 keyed.push((key, r.clone()));
2805 }
2806 keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, order_by));
2807 keyed.into_iter().map(|(_, r)| r).collect()
2808 };
2809 let rows: &[JoinedRow] = &ordered;
2810
2811 if lname == "count" && (args.is_empty() || matches!(args[0], Expr::Star)) {
2814 return Ok(from_f64(rows.len() as f64));
2815 }
2816 let Some(target) = args.first() else {
2817 bail!("{}() needs an argument", name);
2818 };
2819 let mut vals: Vec<Value> = Vec::with_capacity(rows.len());
2820 let mut all_vals: Vec<Value> = Vec::with_capacity(rows.len());
2821 for r in rows {
2822 let v = eval(target, &bind(r, ctx))?;
2823 if !v.is_null() {
2824 vals.push(v.clone());
2825 }
2826 all_vals.push(v);
2827 }
2828 if distinct {
2829 let mut seen: Vec<String> = vec![];
2830 vals.retain(|v| {
2831 let k = format!("{:?}", v);
2832 if seen.contains(&k) { false } else { seen.push(k); true }
2833 });
2834 let mut seen2: Vec<String> = vec![];
2835 all_vals.retain(|v| {
2836 let k = format!("{:?}", v);
2837 if seen2.contains(&k) { false } else { seen2.push(k); true }
2838 });
2839 }
2840 Ok(match lname.as_str() {
2841 "count" => from_f64(vals.len() as f64),
2842 "sum" | "avg" => {
2843 let nums: Vec<f64> = vals.iter().filter_map(num).collect();
2844 if nums.is_empty() {
2845 Value::Null
2846 } else if lname == "sum" {
2847 from_f64(nums.iter().sum())
2848 } else {
2849 from_f64(nums.iter().sum::<f64>() / nums.len() as f64)
2850 }
2851 }
2852 "min" | "max" => {
2853 let mut best: Option<Value> = None;
2854 for v in vals {
2855 best = Some(match best {
2856 None => v,
2857 Some(b) => {
2858 let take = match cmp_values(&v, &b) {
2859 Some(o) if lname == "min" => o.is_lt(),
2860 Some(o) => o.is_gt(),
2861 None => false,
2862 };
2863 if take { v } else { b }
2864 }
2865 });
2866 }
2867 best.unwrap_or(Value::Null)
2868 }
2869 "string_agg" => {
2870 if vals.is_empty() {
2871 Value::Null
2872 } else {
2873 let sep = match args.get(1) {
2876 Some(e) => as_text(&eval(e, &Bound { parts: vec![], ctx })?),
2877 None => String::new(),
2878 };
2879 Value::String(vals.iter().map(as_text).collect::<Vec<_>>().join(&sep))
2880 }
2881 }
2882 "array_agg" => {
2884 if all_vals.is_empty() { Value::Null } else { Value::Array(all_vals) }
2885 }
2886 "bool_and" | "every" => {
2887 if vals.is_empty() {
2888 Value::Null
2889 } else {
2890 Value::Bool(vals.iter().all(|v| truthy(v) == Some(true)))
2891 }
2892 }
2893 "bool_or" => {
2894 if vals.is_empty() {
2895 Value::Null
2896 } else {
2897 Value::Bool(vals.iter().any(|v| truthy(v) == Some(true)))
2898 }
2899 }
2900 _ => unreachable!("is_aggregate gates this"),
2901 })
2902}
2903
2904fn fold_aggregates(
2911 e: &Expr,
2912 rows: &[JoinedRow],
2913 ctx: EvalCtx,
2914 keys: &[Expr],
2915) -> Result<Expr> {
2916 let fold = |x: &Expr| fold_aggregates(x, rows, ctx, keys);
2917 Ok(match e {
2918 Expr::Agg { name, args, order_by, distinct } => {
2919 Expr::Literal(aggregate(name, args, order_by, *distinct, rows, ctx)?)
2920 }
2921 Expr::Func { name, args } => Expr::Func {
2922 name: name.clone(),
2923 args: args.iter().map(&fold).collect::<Result<_>>()?,
2924 },
2925 Expr::Column { .. } if keys.iter().any(|k| k == e) => e.clone(),
2931 Expr::Column { qual, name } => bail!(
2932 "column \"{}{}\" must appear in the GROUP BY clause or be used in an \
2933 aggregate function",
2934 qual.as_ref().map(|q| format!("{q}.")).unwrap_or_default(),
2935 name
2936 ),
2937 Expr::Binary { op, left, right } => Expr::Binary {
2938 op: op.clone(),
2939 left: Box::new(fold(left)?),
2940 right: Box::new(fold(right)?),
2941 },
2942 Expr::Unary { op, expr } => Expr::Unary {
2943 op: op.clone(),
2944 expr: Box::new(fold(expr)?),
2945 },
2946 Expr::Cast { expr, ty } => Expr::Cast {
2947 expr: Box::new(fold(expr)?),
2948 ty: ty.clone(),
2949 },
2950 Expr::IsNull { expr, negated } => Expr::IsNull {
2951 expr: Box::new(fold(expr)?),
2952 negated: *negated,
2953 },
2954 Expr::InList { expr, list, negated } => Expr::InList {
2955 expr: Box::new(fold(expr)?),
2956 list: list.iter().map(&fold).collect::<Result<_>>()?,
2957 negated: *negated,
2958 },
2959 Expr::Case { operand, whens, else_ } => Expr::Case {
2960 operand: match operand {
2961 Some(o) => Some(Box::new(fold(o)?)),
2962 None => None,
2963 },
2964 whens: whens
2965 .iter()
2966 .map(|(c, t)| Ok((fold(c)?, fold(t)?)))
2967 .collect::<Result<_>>()?,
2968 else_: match else_ {
2969 Some(x) => Some(Box::new(fold(x)?)),
2970 None => None,
2971 },
2972 },
2973 Expr::Quantified { op, left, all, right } => Expr::Quantified {
2974 op: op.clone(),
2975 left: Box::new(fold(left)?),
2976 all: *all,
2977 right: Box::new(fold(right)?),
2978 },
2979 Expr::Index { expr, index } => Expr::Index {
2980 expr: Box::new(fold(expr)?),
2981 index: Box::new(fold(index)?),
2982 },
2983 Expr::ArrayLit(items) => Expr::ArrayLit(
2984 items.iter().map(&fold).collect::<Result<_>>()?,
2985 ),
2986 Expr::InSubquery { expr, query, negated } => Expr::InSubquery {
2987 expr: Box::new(fold(expr)?),
2988 query: query.clone(),
2989 negated: *negated,
2990 },
2991 Expr::Star | Expr::QualifiedStar(_) => {
2994 bail!("`*` cannot be mixed with an aggregate outside count(*)")
2995 }
2996 Expr::Literal(_) | Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => {
2998 e.clone()
2999 }
3000 })
3001}
3002
3003fn validate_bindings(sel: &Select) -> Result<()> {
3019 let mut seen: Vec<String> = vec![];
3020 if let Some(f) = &sel.from {
3021 seen.push(f.binding());
3022 }
3023 for j in &sel.joins {
3024 seen.push(j.table.binding());
3025 }
3026 for (i, b) in seen.iter().enumerate() {
3027 if let Some(prev) = seen[..i].iter().find(|p| p.eq_ignore_ascii_case(b)) {
3028 bail!(
3029 "ambiguous relation binding: {:?} appears more than once; use \
3030 aliases (for example `FROM {} JOIN {} AS {}2 ...`)",
3031 prev, prev, prev, prev
3032 );
3033 }
3034 }
3035 Ok(())
3036}
3037
3038#[derive(Debug, Clone, PartialEq, Eq)]
3050pub struct OutCol {
3051 pub key: String,
3052 pub name: String,
3053}
3054
3055fn unique_key(taken: &[OutCol], name: &str) -> String {
3059 if !taken.iter().any(|c| c.key == name) {
3060 return name.to_string();
3061 }
3062 format!("{name}\u{1}{}", taken.len())
3063}
3064
3065type JoinedRow = Vec<(String, Option<Value>)>;
3067
3068fn bind<'a>(row: &'a JoinedRow, ctx: EvalCtx<'a>) -> Bound<'a> {
3069 Bound {
3070 parts: row.iter().map(|(b, v)| (b.clone(), v.as_ref())).collect(),
3071 ctx,
3072 }
3073}
3074
3075fn derived_name(e: &Expr) -> String {
3083 match e {
3084 Expr::Column { name, .. } => name.clone(),
3085 Expr::Func { name, .. } | Expr::Agg { name, .. } => name.clone(),
3086 Expr::Cast { expr, .. } => derived_name(expr),
3087 Expr::Case { .. } => "case".to_string(),
3088 Expr::ArrayQuery(_) | Expr::ArrayLit(_) => "array".to_string(),
3089 Expr::Exists { .. } => "exists".to_string(),
3090 Expr::Subquery(q) => q
3092 .items
3093 .first()
3094 .map(|i| i.alias.clone().unwrap_or_else(|| derived_name(&i.expr)))
3095 .unwrap_or_else(|| "?column?".to_string()),
3096 _ => "?column?".to_string(),
3097 }
3098}
3099
3100pub trait Relation {
3121 fn next_row(&mut self) -> Result<Option<Value>>;
3123
3124 fn size_hint(&self) -> Option<usize> {
3126 None
3127 }
3128}
3129
3130pub struct VecRelation {
3136 iter: std::vec::IntoIter<Value>,
3137 len: usize,
3138}
3139
3140impl Relation for VecRelation {
3141 fn next_row(&mut self) -> Result<Option<Value>> {
3142 Ok(self.iter.next())
3143 }
3144 fn size_hint(&self) -> Option<usize> {
3145 Some(self.len)
3146 }
3147}
3148
3149pub fn from_vec(rows: Vec<Value>) -> Box<dyn Relation> {
3151 let len = rows.len();
3152 Box::new(VecRelation { iter: rows.into_iter(), len })
3153}
3154
3155pub type Resolver<'r> = dyn Fn(&str) -> Result<Option<Box<dyn Relation>>> + 'r;
3161
3162pub fn execute(sel: &Select, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
3167 let (cols, rows, _) = execute_explain(sel, resolve, JoinExec::Auto)?;
3168 Ok((cols, rows))
3169}
3170
3171pub fn execute_explain(
3178 sel: &Select,
3179 resolve: &Resolver,
3180 exec: JoinExec,
3181) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3182 execute_with(sel, resolve, exec, true)
3183}
3184
3185#[derive(Debug, Clone, Copy)]
3190pub struct Opts {
3191 pub exec: JoinExec,
3192 pub pushdown: bool,
3193 pub fuse_filter: bool,
3197}
3198
3199impl Default for Opts {
3200 fn default() -> Self {
3201 Opts { exec: JoinExec::Auto, pushdown: true, fuse_filter: true }
3202 }
3203}
3204
3205impl Opts {
3206 pub fn exec(exec: JoinExec) -> Self {
3207 Opts { exec, ..Default::default() }
3208 }
3209}
3210
3211pub fn execute_with(
3218 sel: &Select,
3219 resolve: &Resolver,
3220 exec: JoinExec,
3221 pushdown: bool,
3222) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3223 execute_opts(sel, resolve, Opts { exec, pushdown, ..Default::default() })
3224}
3225
3226pub fn execute_opts(
3228 sel: &Select,
3229 resolve: &Resolver,
3230 opts: Opts,
3231) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3232 execute_inner(sel, resolve, opts, None)
3233}
3234
3235fn row_key(cols: &[OutCol], obj: &Map<String, Value>) -> String {
3237 cols.iter()
3238 .map(|c| format!("{:?}", obj.get(&c.key).unwrap_or(&Value::Null)))
3239 .collect::<Vec<_>>()
3240 .join("\u{1}")
3241}
3242
3243fn execute_set_ops<'a>(
3251 sel: &Select,
3252 resolve: &'a Resolver<'a>,
3253 opts: Opts,
3254 outer: Option<&'a Bound<'a>>,
3255) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3256 let ctx = EvalCtx { resolver: Some(resolve), outer };
3257 let mut head = sel.clone();
3258 head.set_ops.clear();
3259 head.order_by.clear();
3260 head.limit = None;
3261 head.offset = None;
3262 let (cols, rows, mut plan) = execute_inner(&head, resolve, opts, outer)?;
3263 let mut left: Vec<Map<String, Value>> = rows
3264 .into_iter()
3265 .map(|r| match r {
3266 Value::Object(m) => m,
3267 _ => Map::new(),
3268 })
3269 .collect();
3270
3271 for arm in &sel.set_ops {
3272 let (acols, arows, _) = execute_inner(&arm.query, resolve, opts, outer)?;
3273 let op_name = match arm.op {
3274 SetOp::Union => "UNION",
3275 SetOp::Intersect => "INTERSECT",
3276 SetOp::Except => "EXCEPT",
3277 };
3278 if acols.len() != cols.len() {
3279 bail!(
3280 "each {} query must have the same number of columns: {} vs {}",
3281 op_name, cols.len(), acols.len()
3282 );
3283 }
3284 let right: Vec<Map<String, Value>> = arows
3286 .into_iter()
3287 .map(|r| {
3288 let m = match r {
3289 Value::Object(m) => m,
3290 _ => Map::new(),
3291 };
3292 let mut out = Map::new();
3293 for (i, c) in cols.iter().enumerate() {
3294 out.insert(c.key.clone(), m.get(&acols[i].key).cloned().unwrap_or(Value::Null));
3295 }
3296 out
3297 })
3298 .collect();
3299 let (nl, nr) = (left.len(), right.len());
3300 let right_keys: std::collections::HashSet<String> =
3301 right.iter().map(|m| row_key(&cols, m)).collect();
3302 let mut combined: Vec<Map<String, Value>> = match arm.op {
3303 SetOp::Union => {
3304 left.extend(right);
3305 left
3306 }
3307 SetOp::Intersect => left.into_iter().filter(|m| right_keys.contains(&row_key(&cols, m))).collect(),
3308 SetOp::Except => left.into_iter().filter(|m| !right_keys.contains(&row_key(&cols, m))).collect(),
3309 };
3310 if !arm.all {
3311 let mut seen = std::collections::HashSet::new();
3312 combined.retain(|m| seen.insert(row_key(&cols, m)));
3313 }
3314 plan.notes.push(format!(
3315 "{}{}: {} + {} rows -> {} (each arm planned separately; only the first arm's plan is shown)",
3316 op_name, if arm.all { " ALL" } else { "" }, nl, nr, combined.len()
3317 ));
3318 left = combined;
3319 }
3320
3321 let projected: Vec<(Map<String, Value>, JoinedRow)> = left
3324 .into_iter()
3325 .map(|m| {
3326 let src: JoinedRow = vec![(String::new(), Some(Value::Object(m.clone())))];
3327 (m, src)
3328 })
3329 .collect();
3330 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3331 Ok((cols, out, plan))
3332}
3333
3334fn execute_inner<'a>(
3337 sel: &Select,
3338 resolve: &'a Resolver<'a>,
3339 opts: Opts,
3340 outer: Option<&'a Bound<'a>>,
3341) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3342 if !sel.set_ops.is_empty() {
3343 return execute_set_ops(sel, resolve, opts, outer);
3344 }
3345 let ctx = EvalCtx { resolver: Some(resolve), outer };
3346 let exec = opts.exec;
3347 let pushdown = opts.pushdown;
3348 let mut plan = Plan::default();
3349
3350 validate_bindings(sel)?;
3352
3353 let fuse = opts.fuse_filter && sel.where_.is_some() && !sel.joins.is_empty();
3380
3381 let budget: Option<usize> = match sel.limit {
3382 Some(lim)
3383 if sel.order_by.is_empty()
3384 && !sel.distinct
3385 && !sel.joins.is_empty()
3386 && (sel.where_.is_none() || fuse) =>
3387 {
3388 Some(lim.saturating_add(sel.offset.unwrap_or(0)))
3389 }
3390 _ => None,
3391 };
3392 plan.budget = budget;
3393
3394 let all_bindings: Vec<String> = sel
3400 .from
3401 .iter()
3402 .map(|t| t.binding())
3403 .chain(sel.joins.iter().map(|j| j.table.binding()))
3404 .collect();
3405 let nullable = crate::sqlpush::nullable_bindings(sel);
3406 let push = if pushdown {
3407 crate::sqlpush::plan(sel.where_.as_ref(), &all_bindings, &nullable)
3408 } else {
3409 Pushdown::default()
3410 };
3411 plan.refusals = push.refusals.clone();
3412
3413 let mut base_scan_at: Option<usize> = None;
3414 let mut base_prefilter_at: Option<usize> = None;
3415
3416 let mut left_src: Box<dyn LeftSource + 'a> = match &sel.from {
3424 None => {
3425 Box::new(VecLeft { rows: vec![vec![]], at: 0 })
3428 }
3429 Some(t) => {
3430 let rel = fetch(t, resolve, ctx)?;
3431 let binding = t.binding();
3432 base_scan_at = Some(plan.stages.len());
3437 plan.push(Stage::Scan {
3438 table: t.name.clone(),
3439 binding: binding.clone(),
3440 rows: 0,
3441 });
3442 let preds = push.for_binding(&binding).cloned().unwrap_or_default();
3443 if !preds.is_empty() {
3444 base_prefilter_at = Some(plan.stages.len());
3445 plan.push(Stage::Prefilter {
3446 binding: binding.clone(),
3447 predicates: preds.len(),
3448 in_rows: 0,
3449 out_rows: 0,
3450 });
3451 }
3452 Box::new(StreamLeft { rel, binding, preds, pulled: 0, kept: 0, ctx })
3453 }
3454 };
3455
3456 let mut left_bindings: Vec<String> = match &sel.from {
3462 None => vec![],
3463 Some(t) => vec![t.binding()],
3464 };
3465 let last = sel.joins.len().saturating_sub(1);
3466 let mut rows: Vec<JoinedRow> = vec![];
3467 let mut base_pulled: Option<usize> = None;
3468 let mut base_kept: Option<usize> = None;
3469
3470 for (ji, join) in sel.joins.iter().enumerate() {
3471 let is_last = ji == last;
3472 let rb = join.table.binding();
3473
3474 if join.table.lateral {
3478 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3479 let join_budget = if is_last { budget } else { None };
3480 let (out, removed, consumed, produced) = join_lateral(
3481 left_src.as_mut(), join, resolve, join_budget, post, ctx,
3482 )?;
3483 plan.push(Stage::Scan { table: join.table.name.clone(), binding: rb.clone(), rows: produced });
3484 plan.push(Stage::Join {
3485 kind: join.kind,
3486 table: join.table.name.clone(),
3487 binding: rb.clone(),
3488 strategy: Strategy::NestedLoop,
3489 keys: 0,
3490 left_rows: consumed,
3491 right_rows: produced,
3492 out_rows: out.len(),
3493 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3494 post_filter_removed: post.map(|_| removed),
3495 });
3496 plan.notes.push(format!("LATERAL {}: the subquery ran once per left row ({} times)", rb, consumed));
3497 left_bindings.push(rb);
3498 if ji == 0 {
3499 if let Some((pulled, kept)) = left_src.stats() {
3500 base_pulled = Some(pulled);
3501 base_kept = Some(kept);
3502 }
3503 }
3504 left_src = Box::new(VecLeft { rows: out, at: 0 });
3505 continue;
3506 }
3507
3508 let right_rel = fetch(&join.table, resolve, ctx)?;
3509 let right_all = drain(right_rel)?;
3510 plan.push(Stage::Scan {
3511 table: join.table.name.clone(),
3512 binding: rb.clone(),
3513 rows: right_all.len(),
3514 });
3515 let right_rows = prefilter(right_all, &rb, &push, &mut plan, ctx)?;
3516
3517 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3522 let join_budget = if is_last { budget } else { None };
3523
3524 let keys = sqljoin::hash_keys(join.on.as_ref(), &left_bindings, &rb);
3527 let left_hint = left_src.hint().unwrap_or(usize::MAX);
3528 let strategy = sqljoin::choose(exec, keys.len(), left_hint, right_rows.len());
3529
3530 let (out, removed, consumed) = match strategy {
3531 Strategy::NestedLoop => join_nested_loop(
3532 left_src.as_mut(), &left_bindings, join, &right_rows, &rb,
3533 join_budget, post, ctx,
3534 )?,
3535 Strategy::Hash => join_hash(
3536 left_src.as_mut(), &left_bindings, join, &right_rows, &rb, &keys,
3537 join_budget, post, ctx,
3538 )?,
3539 };
3540
3541 plan.push(Stage::Join {
3542 kind: join.kind,
3543 table: join.table.name.clone(),
3544 binding: rb.clone(),
3545 strategy,
3546 keys: keys.len(),
3547 left_rows: consumed,
3548 right_rows: right_rows.len(),
3549 out_rows: out.len(),
3550 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3551 post_filter_removed: post.map(|_| removed),
3552 });
3553 left_bindings.push(rb);
3554 if ji == 0 {
3556 if let Some((pulled, kept)) = left_src.stats() {
3557 base_pulled = Some(pulled);
3558 base_kept = Some(kept);
3559 }
3560 }
3561 rows = out;
3562 left_src = Box::new(VecLeft { rows: std::mem::take(&mut rows), at: 0 });
3564 }
3565
3566 rows = left_src.take_rows();
3569 if let Some(i) = base_scan_at {
3570 if let (Some(pulled), Some(kept)) = (base_pulled, base_kept) {
3571 if let Some(Stage::Scan { rows: r, .. }) = plan.stages.get_mut(i) {
3572 *r = pulled;
3573 }
3574 if let Some(j) = base_prefilter_at {
3575 if let Some(Stage::Prefilter { in_rows, out_rows, .. }) =
3576 plan.stages.get_mut(j)
3577 {
3578 *in_rows = pulled;
3579 *out_rows = kept;
3580 }
3581 }
3582 }
3583 }
3584
3585 if let Some(pred) = sel.where_.as_ref().filter(|_| !fuse) {
3587 let in_rows = rows.len();
3588 let mut kept = Vec::with_capacity(rows.len());
3589 for r in rows {
3590 if truthy(&eval(pred, &bind(&r, ctx))?) == Some(true) {
3594 kept.push(r);
3595 }
3596 }
3597 rows = kept;
3598 plan.push(Stage::Filter { in_rows, out_rows: rows.len() });
3599 }
3600
3601 let grouping = !sel.group_by.is_empty();
3614 let aggregating = grouping
3615 || sel.items.iter().any(|i| has_aggregate(&i.expr))
3616 || sel.having.as_ref().is_some_and(has_aggregate);
3617 if aggregating {
3618 let mut groups: Vec<(Vec<Value>, Vec<JoinedRow>)> = vec![];
3622 if grouping {
3623 for r in rows {
3624 let b = bind(&r, ctx);
3625 let mut key = Vec::with_capacity(sel.group_by.len());
3626 for g in &sel.group_by {
3627 key.push(eval(g, &b)?);
3628 }
3629 match groups.iter_mut().find(|(k, _)| {
3630 k.len() == key.len()
3631 && k.iter().zip(&key).all(|(a, b)| {
3632 (a.is_null() && b.is_null())
3635 || matches!(cmp_values(a, b), Some(std::cmp::Ordering::Equal))
3636 })
3637 }) {
3638 Some((_, bucket)) => bucket.push(r),
3639 None => groups.push((key, vec![r])),
3640 }
3641 }
3642 } else {
3643 groups.push((vec![], rows));
3646 }
3647
3648 let n_groups = groups.len();
3649 let mut cols: Vec<OutCol> = vec![];
3650 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = vec![];
3651 let empty: JoinedRow = vec![];
3652
3653 for (gi, (_key, grows)) in groups.iter().enumerate() {
3654 let scope = grows.first().unwrap_or(&empty);
3659
3660 if let Some(h) = &sel.having {
3661 let folded = fold_aggregates(h, grows, ctx, &sel.group_by)?;
3662 if truthy(&eval(&folded, &bind(scope, ctx))?) != Some(true) {
3663 continue;
3664 }
3665 }
3666
3667 let mut obj = Map::new();
3668 for item in &sel.items {
3669 let folded = fold_aggregates(&item.expr, grows, ctx, &sel.group_by)?;
3670 let v = eval(&folded, &bind(scope, ctx))?;
3671 if gi == 0 {
3673 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3674 let key = unique_key(&cols, &name);
3675 obj.insert(key.clone(), v);
3676 cols.push(OutCol { key, name });
3677 } else {
3678 let idx = obj.len();
3679 if let Some(c) = cols.get(idx) {
3680 obj.insert(c.key.clone(), v);
3681 }
3682 }
3683 }
3684 projected.push((obj, scope.clone()));
3685 }
3686
3687 plan.notes.push(if grouping {
3688 format!(
3689 "GroupAggregate on {} key(s): {} rows -> {} group(s){}",
3690 sel.group_by.len(),
3691 n_rows_before_group(&plan),
3692 n_groups,
3693 if sel.having.is_some() {
3694 format!(", HAVING kept {}", projected.len())
3695 } else {
3696 String::new()
3697 }
3698 )
3699 } else {
3700 format!("Aggregate over {} row(s) -> 1 row", groups[0].1.len())
3701 });
3702 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3703 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3704 return Ok((cols, out, plan));
3705 }
3706
3707 let mut cols: Vec<OutCol> = vec![];
3738 let mut spans: Vec<(usize, usize)> = Vec::with_capacity(sel.items.len());
3739 for item in &sel.items {
3740 let start = cols.len();
3741 match &item.expr {
3742 Expr::Star => {
3743 let mut plain: Vec<String> = vec![];
3744 let mut meta: Vec<String> = vec![];
3745 for r in &rows {
3746 for (n, _) in bind(r, ctx).flatten() {
3747 let target = if n.starts_with('_') { &mut meta } else { &mut plain };
3748 if !target.contains(&n) {
3750 target.push(n);
3751 }
3752 }
3753 }
3754 meta.sort();
3757 for n in plain.into_iter().chain(meta) {
3758 if !cols.iter().any(|c| c.name == n) {
3759 cols.push(OutCol { key: n.clone(), name: n });
3760 }
3761 }
3762 }
3763 Expr::QualifiedStar(q) => {
3764 if let Some(first) = rows.first() {
3765 for (n, _) in bind(first, ctx).flatten_binding(q) {
3766 if !cols.iter().any(|c| c.name == n) {
3767 cols.push(OutCol { key: n.clone(), name: n });
3768 }
3769 }
3770 }
3771 }
3772 _ => {
3773 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3774 let key = unique_key(&cols, &name);
3780 cols.push(OutCol { key, name });
3781 }
3782 }
3783 spans.push((start, cols.len()));
3784 }
3785
3786 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = Vec::with_capacity(rows.len());
3790 for r in rows {
3791 let b = bind(&r, ctx);
3792 let mut obj = Map::new();
3793 for (i, item) in sel.items.iter().enumerate() {
3794 let (start, end) = spans[i];
3795 match &item.expr {
3796 Expr::Star => {
3797 for (n, v) in b.flatten() {
3798 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3799 obj.entry(c.key.clone()).or_insert(v);
3800 }
3801 }
3802 }
3803 Expr::QualifiedStar(q) => {
3804 for (n, v) in b.flatten_binding(q) {
3805 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3806 obj.entry(c.key.clone()).or_insert(v);
3807 }
3808 }
3809 }
3810 _ => {
3811 let v = eval(&item.expr, &b)?;
3812 if let Some(c) = cols.get(start) {
3813 obj.insert(c.key.clone(), v);
3814 }
3815 }
3816 }
3817 }
3818 projected.push((obj, r));
3819 }
3820
3821 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3822
3823 if sel.distinct {
3825 let in_rows = projected.len();
3826 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
3827 projected.retain(|(obj, _)| seen.insert(row_key(&cols, obj)));
3830 plan.push(Stage::Distinct { in_rows, out_rows: projected.len() });
3831 }
3832
3833 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3834 Ok((cols, out, plan))
3835}
3836
3837fn n_rows_before_group(plan: &Plan) -> usize {
3841 plan.stages
3842 .iter()
3843 .rev()
3844 .find_map(|st| match st {
3845 Stage::Filter { out_rows, .. } => Some(*out_rows),
3846 Stage::Join { out_rows, .. } => Some(*out_rows),
3847 Stage::Prefilter { out_rows, .. } => Some(*out_rows),
3848 Stage::Scan { rows, .. } => Some(*rows),
3849 _ => None,
3850 })
3851 .unwrap_or(0)
3852}
3853
3854fn sort_keys(a: &[Value], b: &[Value], order_by: &[OrderBy]) -> std::cmp::Ordering {
3861 for (i, ob) in order_by.iter().enumerate() {
3862 let (Some(x), Some(y)) = (a.get(i), b.get(i)) else { continue };
3863 let ord = match (x.is_null(), y.is_null()) {
3864 (true, true) => std::cmp::Ordering::Equal,
3865 (true, false) => {
3868 return if ob.nulls_first {
3869 std::cmp::Ordering::Less
3870 } else {
3871 std::cmp::Ordering::Greater
3872 }
3873 }
3874 (false, true) => {
3875 return if ob.nulls_first {
3876 std::cmp::Ordering::Greater
3877 } else {
3878 std::cmp::Ordering::Less
3879 }
3880 }
3881 (false, false) => cmp_values(x, y).unwrap_or(std::cmp::Ordering::Equal),
3882 };
3883 let ord = if matches!(ob.dir, Dir::Desc) { ord.reverse() } else { ord };
3884 if !ord.is_eq() {
3885 return ord;
3886 }
3887 }
3888 std::cmp::Ordering::Equal
3889}
3890
3891fn finish(
3893 sel: &Select,
3894 cols: &[OutCol],
3895 mut projected: Vec<(Map<String, Value>, JoinedRow)>,
3896 ctx: EvalCtx,
3897 plan: &mut Plan,
3898) -> Result<Vec<Value>> {
3899 if !sel.order_by.is_empty() {
3901 let mut keyed: Vec<(Vec<Value>, (Map<String, Value>, JoinedRow))> = vec![];
3905 for (obj, src) in projected {
3906 let mut key = vec![];
3907 for ob in &sel.order_by {
3908 let v = match (ob.ordinal, &ob.expr) {
3909 (Some(n), _) => {
3910 let c = cols.get(n - 1).ok_or_else(|| {
3911 anyhow::anyhow!(
3912 "ORDER BY {} is out of range: the select list has {} \
3913 column(s)", n, cols.len())
3914 })?;
3915 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3916 }
3917 (None, Some(Expr::Column { qual: None, name }))
3922 if cols.iter().any(|c| c.name == *name) =>
3923 {
3924 let c = cols.iter().find(|c| c.name == *name).expect("checked");
3925 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3926 }
3927 (None, Some(e)) => {
3928 eval(e, &bind(&src, ctx))?
3932 }
3933 (None, None) => Value::Null,
3934 };
3935 key.push(v);
3936 }
3937 keyed.push((key, (obj, src)));
3938 }
3939
3940 keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, &sel.order_by));
3941
3942 projected = keyed.into_iter().map(|(_, row)| row).collect();
3943 plan.push(Stage::Sort { keys: sel.order_by.len(), rows: projected.len() });
3944 }
3945
3946 let mut out: Vec<Value> = projected
3948 .into_iter()
3949 .map(|(obj, _)| Value::Object(obj))
3950 .collect();
3951 let in_rows = out.len();
3952 if let Some(off) = sel.offset {
3953 out = if off >= out.len() { vec![] } else { out.split_off(off) };
3954 }
3955 if let Some(lim) = sel.limit {
3956 out.truncate(lim);
3957 }
3958 if sel.limit.is_some() || sel.offset.is_some() {
3959 plan.push(Stage::Limit {
3960 limit: sel.limit,
3961 offset: sel.offset,
3962 in_rows,
3963 out_rows: out.len(),
3964 });
3965 }
3966
3967 Ok(out)
3968}
3969
3970fn keep_row(cand: &JoinedRow, post: Option<&Expr>, removed: &mut usize, ctx: EvalCtx) -> Result<bool> {
4003 let Some(p) = post else { return Ok(true) };
4004 if truthy(&eval(p, &bind(cand, ctx))?) == Some(true) {
4006 Ok(true)
4007 } else {
4008 *removed += 1;
4009 Ok(false)
4010 }
4011}
4012
4013#[allow(clippy::too_many_arguments)]
4019fn emit_unmatched_right(
4020 out: &mut Vec<JoinedRow>,
4021 kind: JoinKind,
4022 left_bindings: &[String],
4023 right_rows: &[Value],
4024 right_matched: &[bool],
4025 rb: &str,
4026 post: Option<&Expr>,
4027 removed: &mut usize,
4028 ctx: EvalCtx,
4029) -> Result<()> {
4030 if !matches!(kind, JoinKind::Right | JoinKind::Full) {
4031 return Ok(());
4032 }
4033 for (ri, right) in right_rows.iter().enumerate() {
4034 if right_matched[ri] {
4035 continue;
4036 }
4037 let mut cand: JoinedRow = left_bindings.iter().map(|b| (b.clone(), None)).collect();
4038 cand.push((rb.to_string(), Some(right.clone())));
4039 if keep_row(&cand, post, removed, ctx)? {
4042 out.push(cand);
4043 }
4044 }
4045 Ok(())
4046}
4047
4048fn join_lateral(
4055 left_src: &mut dyn LeftSource,
4056 join: &Join,
4057 resolve: &Resolver,
4058 budget: Option<usize>,
4059 post: Option<&Expr>,
4060 ctx: EvalCtx,
4061) -> Result<(Vec<JoinedRow>, usize, usize, usize)> {
4062 let sub = join
4063 .table
4064 .sub
4065 .as_deref()
4066 .ok_or_else(|| anyhow::anyhow!("LATERAL requires a subquery"))?;
4067 let rb = join.table.binding();
4068 let mut out: Vec<JoinedRow> = vec![];
4069 let mut removed = 0usize;
4070 let mut consumed = 0usize;
4071 let mut produced = 0usize;
4072 while let Some(left) = {
4073 if budget.is_some_and(|b| out.len() >= b) { None } else { left_src.next_left()? }
4074 } {
4075 consumed += 1;
4076 let scope = bind(&left, ctx);
4077 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), Some(&scope))?;
4078 produced += rows.len();
4079 let mut matched = false;
4080 for r in rows {
4081 let m = match r {
4082 Value::Object(m) => m,
4083 _ => Map::new(),
4084 };
4085 let mut named = Map::new();
4086 for (i, c) in cols.iter().enumerate() {
4087 let name = join.table.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
4088 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
4089 }
4090 let mut cand = left.clone();
4091 cand.push((rb.clone(), Some(Value::Object(named))));
4092 let on_ok = match &join.on {
4093 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4094 None => true,
4095 };
4096 if !on_ok {
4097 continue;
4098 }
4099 matched = true;
4100 if keep_row(&cand, post, &mut removed, ctx)? {
4101 out.push(cand);
4102 }
4103 }
4104 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4105 let mut cand = left.clone();
4106 cand.push((rb.clone(), None));
4107 if keep_row(&cand, post, &mut removed, ctx)? {
4108 out.push(cand);
4109 }
4110 }
4111 }
4112 Ok((out, removed, consumed, produced))
4113}
4114
4115#[allow(clippy::too_many_arguments)]
4121fn join_nested_loop(
4122 left_src: &mut dyn LeftSource,
4123 left_bindings: &[String],
4124 join: &Join,
4125 right_rows: &[Value],
4126 rb: &str,
4127 budget: Option<usize>,
4128 post: Option<&Expr>,
4129 ctx: EvalCtx,
4130) -> Result<(Vec<JoinedRow>, usize, usize)> {
4131 let mut out: Vec<JoinedRow> = vec![];
4132 let mut removed = 0usize;
4133 let mut right_matched = vec![false; right_rows.len()];
4135
4136 let mut consumed = 0usize;
4137 while let Some(left) = {
4138 if budget.is_some_and(|b| out.len() >= b) {
4139 None
4142 } else {
4143 left_src.next_left()?
4144 }
4145 } {
4146 consumed += 1;
4147 let left = &left;
4148 let mut matched = false;
4151 for (ri, right) in right_rows.iter().enumerate() {
4152 let mut cand: JoinedRow = left.clone();
4153 cand.push((rb.to_string(), Some(right.clone())));
4154 let joins_here = match &join.on {
4155 None => true,
4157 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4161 };
4162 if joins_here {
4163 matched = true;
4164 right_matched[ri] = true;
4165 if keep_row(&cand, post, &mut removed, ctx)? {
4166 out.push(cand);
4167 }
4168 }
4169 }
4170 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4172 let mut cand: JoinedRow = left.clone();
4173 cand.push((rb.to_string(), None));
4174 if keep_row(&cand, post, &mut removed, ctx)? {
4175 out.push(cand);
4176 }
4177 }
4178 }
4179
4180 if !budget.is_some_and(|b| out.len() >= b) {
4185 emit_unmatched_right(
4186 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4187 &mut removed, ctx,
4188 )?;
4189 }
4190 Ok((out, removed, consumed))
4191}
4192
4193#[allow(clippy::too_many_arguments)]
4201fn join_hash(
4202 left_src: &mut dyn LeftSource,
4203 left_bindings: &[String],
4204 join: &Join,
4205 right_rows: &[Value],
4206 rb: &str,
4207 keys: &[(Expr, Expr)],
4208 budget: Option<usize>,
4209 post: Option<&Expr>,
4210 ctx: EvalCtx,
4211) -> Result<(Vec<JoinedRow>, usize, usize)> {
4212 debug_assert!(!keys.is_empty(), "the planner must not choose Hash with no keys");
4213
4214 let side = sqljoin::HashSide::build(right_rows.len(), |i| {
4216 let one: JoinedRow = vec![(rb.to_string(), Some(right_rows[i].clone()))];
4219 let b = bind(&one, ctx);
4220 let mut k = Vec::with_capacity(keys.len());
4221 for (_, right_expr) in keys {
4222 match sqljoin::hkey(&eval(right_expr, &b)?) {
4223 Some(h) => k.push(h),
4224 None => return Ok(None),
4226 }
4227 }
4228 Ok(Some(k))
4229 })?;
4230
4231 let mut out: Vec<JoinedRow> = vec![];
4233 let mut removed = 0usize;
4234 let mut right_matched = vec![false; right_rows.len()];
4235
4236 let mut consumed = 0usize;
4237 while let Some(left) = {
4238 if budget.is_some_and(|b| out.len() >= b) {
4239 None
4240 } else {
4241 left_src.next_left()?
4242 }
4243 } {
4244 consumed += 1;
4245 let left = &left;
4246 let lb = bind(left, ctx);
4247 let mut lk = Vec::with_capacity(keys.len());
4248 let mut null_key = false;
4249 for (left_expr, _) in keys {
4250 match sqljoin::hkey(&eval(left_expr, &lb)?) {
4251 Some(h) => lk.push(h),
4252 None => {
4253 null_key = true;
4254 break;
4255 }
4256 }
4257 }
4258
4259 let mut matched = false;
4260 if !null_key {
4264 for &ri in side.probe(&lk) {
4265 let mut cand: JoinedRow = left.clone();
4266 cand.push((rb.to_string(), Some(right_rows[ri].clone())));
4267 let joins_here = match &join.on {
4269 None => true,
4270 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4271 };
4272 if joins_here {
4273 matched = true;
4274 right_matched[ri] = true;
4275 if keep_row(&cand, post, &mut removed, ctx)? {
4276 out.push(cand);
4277 }
4278 }
4279 }
4280 }
4281 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4282 let mut cand: JoinedRow = left.clone();
4283 cand.push((rb.to_string(), None));
4284 if keep_row(&cand, post, &mut removed, ctx)? {
4285 out.push(cand);
4286 }
4287 }
4288 }
4289
4290 if !budget.is_some_and(|b| out.len() >= b) {
4293 emit_unmatched_right(
4294 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4295 &mut removed, ctx,
4296 )?;
4297 }
4298 Ok((out, removed, consumed))
4299}
4300
4301fn prefilter(
4308 rows: Vec<Value>,
4309 binding: &str,
4310 push: &Pushdown,
4311 plan: &mut Plan,
4312 ctx: EvalCtx,
4313) -> Result<Vec<Value>> {
4314 let Some(preds) = push.for_binding(binding) else { return Ok(rows) };
4315 if preds.is_empty() {
4316 return Ok(rows);
4317 }
4318 let in_rows = rows.len();
4319 let mut kept = Vec::with_capacity(rows.len());
4320 for row in rows {
4321 let one: JoinedRow = vec![(binding.to_string(), Some(row))];
4322 let b = bind(&one, ctx);
4323 let mut keep = true;
4324 for p in preds {
4325 if truthy(&eval(p, &b)?) != Some(true) {
4331 keep = false;
4332 break;
4333 }
4334 }
4335 if keep {
4336 if let Some((_, Some(v))) = one.into_iter().next() {
4338 kept.push(v);
4339 }
4340 }
4341 }
4342 plan.push(Stage::Prefilter {
4343 binding: binding.to_string(),
4344 predicates: preds.len(),
4345 in_rows,
4346 out_rows: kept.len(),
4347 });
4348 Ok(kept)
4349}
4350
4351fn fetch(t: &TableRef, resolve: &Resolver, ctx: EvalCtx) -> Result<Box<dyn Relation>> {
4354 if let Some(sub) = &t.sub {
4358 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), ctx.outer)?;
4359 let out = rows
4360 .into_iter()
4361 .map(|r| {
4362 let m = match r {
4363 Value::Object(m) => m,
4364 _ => Map::new(),
4365 };
4366 let mut named = Map::new();
4367 for (i, c) in cols.iter().enumerate() {
4368 let name = t.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
4369 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
4372 }
4373 Value::Object(named)
4374 })
4375 .collect();
4376 return Ok(from_vec(out));
4377 }
4378
4379 if let Some(args) = &t.args {
4383 let empty: JoinedRow = vec![];
4384 let scope = bind(&empty, ctx);
4385 let col = |i: usize, default: &str| -> String {
4386 t.col_aliases.get(i).cloned().unwrap_or_else(|| default.to_string())
4387 };
4388 let rows: Vec<Value> = match t.name.as_str() {
4389 "generate_series" => {
4390 let a = num(&eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_series() needs a start"))?, &scope)?);
4391 let b = num(&eval(args.get(1).ok_or_else(|| anyhow::anyhow!("generate_series() needs a stop"))?, &scope)?);
4392 let step = match args.get(2) {
4393 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4394 None => 1.0,
4395 };
4396 match (a, b) {
4397 (Some(a), Some(b)) if step != 0.0 => {
4399 let mut out = vec![];
4400 let mut x = a;
4401 while (step > 0.0 && x <= b) || (step < 0.0 && x >= b) {
4402 let mut m = Map::new();
4403 m.insert(col(0, "generate_series"), from_f64(x));
4404 out.push(Value::Object(m));
4405 x += step;
4406 if out.len() > 1_000_000 {
4407 bail!("generate_series() would produce more than a million rows");
4408 }
4409 }
4410 out
4411 }
4412 (Some(_), Some(_)) => bail!("generate_series() step cannot equal zero"),
4413 _ => vec![],
4414 }
4415 }
4416 "unnest" => match eval(args.first().ok_or_else(|| anyhow::anyhow!("unnest() needs an array"))?, &scope)? {
4417 Value::Array(items) => items
4418 .into_iter()
4419 .map(|v| {
4420 let mut m = Map::new();
4421 m.insert(col(0, "unnest"), v);
4422 Value::Object(m)
4423 })
4424 .collect(),
4425 _ => vec![],
4427 },
4428 "generate_subscripts" => {
4433 let dim = match args.get(1) {
4434 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4435 None => 1.0,
4436 };
4437 match eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_subscripts() needs an array"))?, &scope)? {
4438 Value::Array(items) if dim == 1.0 => (1..=items.len())
4442 .map(|i| {
4443 let mut m = Map::new();
4444 m.insert(col(0, "generate_subscripts"), from_f64(i as f64));
4445 Value::Object(m)
4446 })
4447 .collect(),
4448 _ => vec![],
4449 }
4450 }
4451 "pg_partition_tree" | "pg_partition_ancestors" => vec![],
4454 other => bail!(
4455 "the table function {}() is not implemented. It is refused rather \
4456 than answered with no rows, because an empty relation reads as \
4457 missing DATA rather than a missing feature",
4458 other
4459 ),
4460 };
4461 return Ok(from_vec(rows));
4462 }
4463
4464 match resolve(&t.name)? {
4465 Some(rel) => Ok(rel),
4466 None => bail!("relation {:?} does not exist", t.name),
4469 }
4470}
4471
4472trait LeftSource {
4479 fn next_left(&mut self) -> Result<Option<JoinedRow>>;
4480 fn hint(&self) -> Option<usize>;
4482 fn take_rows(&mut self) -> Vec<JoinedRow>;
4484 fn stats(&self) -> Option<(usize, usize)> {
4486 None
4487 }
4488}
4489
4490struct StreamLeft<'a> {
4496 rel: Box<dyn Relation>,
4497 binding: String,
4498 preds: Vec<Expr>,
4499 ctx: EvalCtx<'a>,
4500 pulled: usize,
4504 kept: usize,
4505}
4506
4507impl<'a> LeftSource for StreamLeft<'a> {
4508 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4509 while let Some(row) = self.rel.next_row()? {
4510 self.pulled += 1;
4511 let one: JoinedRow = vec![(self.binding.clone(), Some(row))];
4512 if !self.preds.is_empty() {
4513 let b = bind(&one, self.ctx);
4514 let mut keep = true;
4515 for p in &self.preds {
4516 if truthy(&eval(p, &b)?) != Some(true) {
4517 keep = false;
4518 break;
4519 }
4520 }
4521 if !keep {
4522 continue;
4523 }
4524 }
4525 self.kept += 1;
4526 return Ok(Some(one));
4527 }
4528 Ok(None)
4529 }
4530 fn hint(&self) -> Option<usize> {
4531 self.rel.size_hint()
4536 }
4537 fn take_rows(&mut self) -> Vec<JoinedRow> {
4538 let mut out = vec![];
4541 while let Ok(Some(r)) = self.next_left() {
4542 out.push(r);
4543 }
4544 out
4545 }
4546 fn stats(&self) -> Option<(usize, usize)> {
4547 Some((self.pulled, self.kept))
4548 }
4549}
4550
4551struct VecLeft {
4554 rows: Vec<JoinedRow>,
4555 at: usize,
4556}
4557
4558impl LeftSource for VecLeft {
4559 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4560 let r = self.rows.get(self.at).cloned();
4561 if r.is_some() {
4562 self.at += 1;
4563 }
4564 Ok(r)
4565 }
4566 fn hint(&self) -> Option<usize> {
4567 Some(self.rows.len().saturating_sub(self.at))
4568 }
4569 fn take_rows(&mut self) -> Vec<JoinedRow> {
4570 let mut v = std::mem::take(&mut self.rows);
4571 if self.at > 0 {
4572 v = v.split_off(self.at);
4573 }
4574 self.at = 0;
4575 v
4576 }
4577}
4578
4579fn drain(mut rel: Box<dyn Relation>) -> Result<Vec<Value>> {
4586 let mut out = Vec::with_capacity(rel.size_hint().unwrap_or(0));
4587 while let Some(row) = rel.next_row()? {
4588 out.push(row);
4589 }
4590 Ok(out)
4591}
4592
4593pub fn run(sql: &str, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
4595 let sel = parse(sql)?;
4596 execute(&sel, resolve)
4597}
4598
4599#[cfg(test)]
4600mod lexer_tests {
4601 use super::*;
4602
4603 fn kinds(src: &str) -> Vec<Tok> {
4604 let mut t = lex(src).expect("lexes");
4605 t.pop(); t
4607 }
4608
4609 #[test]
4610 fn a_word_keeps_both_its_canonical_and_raw_spelling() {
4611 assert_eq!(
4614 kinds("Select"),
4615 vec![Tok::Word { upper: "SELECT".into(), raw: "Select".into() }]
4616 );
4617 }
4618
4619 #[test]
4620 fn a_quoted_identifier_is_never_a_keyword() {
4621 assert_eq!(kinds(r#""select""#), vec![Tok::Quoted("select".into())]);
4622 assert_eq!(kinds(r#""Name""#), vec![Tok::Quoted("Name".into())]);
4624 }
4625
4626 #[test]
4627 fn a_doubled_quote_is_one_literal_quote() {
4628 assert_eq!(kinds("'it''s'"), vec![Tok::Str("it's".into())]);
4629 assert_eq!(kinds(r#""a""b""#), vec![Tok::Quoted("a\"b".into())]);
4630 }
4631
4632 #[test]
4633 fn an_E_string_decodes_the_escapes_catalogue_sql_uses() {
4634 assert_eq!(kinds(r"E'\n'"), vec![Tok::Str("\n".into())]);
4636 assert_eq!(kinds(r"E'a\tb'"), vec![Tok::Str("a\tb".into())]);
4637 assert_eq!(kinds(r"E'\q'"), vec![Tok::Str("q".into())]);
4639 }
4640
4641 #[test]
4642 fn operators_match_longest_first() {
4643 assert_eq!(kinds("!~*"), vec![Tok::Op("!~*".into())]);
4645 assert_eq!(kinds("!~"), vec![Tok::Op("!~".into())]);
4646 assert_eq!(kinds("~*"), vec![Tok::Op("~*".into())]);
4647 assert_eq!(kinds("<>"), vec![Tok::Op("<>".into())]);
4648 assert_eq!(kinds("!="), vec![Tok::Op("!=".into())]);
4649 assert_eq!(kinds(">="), vec![Tok::Op(">=".into())]);
4650 assert_eq!(kinds("::"), vec![Tok::Op("::".into())]);
4651 assert_eq!(kinds("||"), vec![Tok::Op("||".into())]);
4652 assert_eq!(kinds("~"), vec![Tok::Op("~".into())]);
4653 }
4654
4655 #[test]
4656 fn comments_are_skipped_including_nested_block_comments() {
4657 assert_eq!(kinds("1 -- trailing\n"), vec![Tok::Num(1.0)]);
4658 assert_eq!(kinds("1 /* a */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4659 assert_eq!(kinds("1 /* a /* b */ c */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4661 assert!(lex("1 /* unterminated").is_err());
4662 }
4663
4664 #[test]
4665 fn numbers_parse_including_fractions_and_exponents() {
4666 assert_eq!(kinds("42"), vec![Tok::Num(42.0)]);
4667 assert_eq!(kinds("4.5"), vec![Tok::Num(4.5)]);
4668 assert_eq!(kinds(".5"), vec![Tok::Num(0.5)]);
4669 assert_eq!(kinds("1e3"), vec![Tok::Num(1000.0)]);
4670 assert_eq!(kinds("1e-2"), vec![Tok::Num(0.01)]);
4671 assert_eq!(
4673 kinds("1e"),
4674 vec![Tok::Num(1.0), Tok::Word { upper: "E".into(), raw: "e".into() }]
4675 );
4676 }
4677
4678 #[test]
4679 fn an_unterminated_literal_is_an_error_not_a_truncation() {
4680 assert!(lex("'abc").is_err());
4681 assert!(lex(r#""abc"#).is_err());
4682 }
4683
4684 #[test]
4685 fn an_unknown_character_is_REFUSED_rather_than_skipped() {
4686 let e = lex("SELECT 1 @ 2").unwrap_err().to_string();
4689 assert!(e.contains('@'), "{}", e);
4690 }
4691
4692 #[test]
4693 fn the_real_dn_query_lexes() {
4694 let sql = r#"SELECT n.nspname AS "Name",
4695 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
4696 FROM pg_catalog.pg_namespace n
4697 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
4698 ORDER BY 1;"#;
4699 let toks = lex(sql).expect("psql's \\dn must lex");
4700 assert!(toks.contains(&Tok::Quoted("Name".into())));
4701 assert!(toks.contains(&Tok::Op("!~".into())));
4702 assert!(toks.contains(&Tok::Op("<>".into())));
4703 assert!(toks.contains(&Tok::Str("^pg_".into())));
4704 }
4705
4706 #[test]
4707 fn the_real_dt_query_lexes() {
4708 let sql = r#"SELECT n.nspname as "Schema", c.relname as "Name",
4709 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' END as "Type",
4710 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
4711 FROM pg_catalog.pg_class c
4712 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
4713 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
4714 WHERE c.relkind IN ('r','p','')
4715 AND n.nspname <> 'pg_catalog'
4716 AND n.nspname !~ '^pg_toast'
4717 AND pg_catalog.pg_table_is_visible(c.oid)
4718 ORDER BY 1,2;"#;
4719 let toks = lex(sql).expect("psql's \\dt must lex");
4720 assert!(toks.iter().any(|t| t.is_kw("CASE")));
4721 assert!(toks.iter().any(|t| t.is_kw("LEFT")));
4722 assert!(toks.iter().any(|t| t.is_kw("JOIN")));
4723 assert!(toks.contains(&Tok::Str(String::new())));
4725 }
4726}
4727
4728#[cfg(test)]
4729mod parser_tests {
4730 use super::*;
4731 use serde_json::json;
4732
4733 fn col(qual: Option<&str>, name: &str) -> Expr {
4734 Expr::Column { qual: qual.map(str::to_string), name: name.to_string() }
4735 }
4736
4737 #[test]
4738 fn a_bare_select_list_and_from() {
4739 let s = parse("SELECT a, b FROM t").unwrap();
4740 assert_eq!(s.items.len(), 2);
4741 assert_eq!(s.items[0].expr, col(None, "a"));
4742 assert_eq!(s.from.unwrap().name, "t");
4743 }
4744
4745 #[test]
4749 fn as_of_system_time_is_read_per_table() {
4750 let s = parse("SELECT total FROM orders AS OF SYSTEM TIME 42").unwrap();
4751 assert_eq!(s.from.clone().unwrap().as_of, Some(42));
4752 assert_eq!(s.from.unwrap().alias, None);
4753
4754 let s = parse("SELECT o.total FROM orders AS OF SYSTEM TIME 42 o").unwrap();
4756 let t = s.from.unwrap();
4757 assert_eq!((t.as_of, t.alias.as_deref()), (Some(42), Some("o")));
4758
4759 let t = parse("SELECT x FROM orders AS o").unwrap().from.unwrap();
4764 assert_eq!((t.as_of, t.alias.as_deref()), (None, Some("o")));
4765
4766 let s = parse(
4769 "SELECT o.total, n.total FROM orders AS OF SYSTEM TIME 1 o \
4770 JOIN orders n ON o._id = n._id").unwrap();
4771 assert_eq!(s.from.unwrap().as_of, Some(1));
4772 assert_eq!(s.joins[0].table.as_of, None);
4773
4774 let s = parse("SELECT total FROM orders AS OF SYSTEM TIME 7 WHERE _id = '1'").unwrap();
4776 assert_eq!(s.from.unwrap().as_of, Some(7));
4777 assert!(s.where_.is_some());
4778 }
4779
4780 #[test]
4785 fn nqls_verbs_are_table_qualifiers_in_sql() {
4786 let t = parse("SELECT _id FROM orders SEARCH 'acme'").unwrap().from.unwrap();
4787 assert_eq!(t.search.as_deref(), Some("acme"));
4788
4789 let t = parse("SELECT _id FROM orders VALID AS OF '2026-01-01'").unwrap().from.unwrap();
4790 assert_eq!(t.valid_as_of.as_deref(), Some("2026-01-01"));
4791
4792 let t = parse(
4794 "SELECT o._id FROM orders AS OF SYSTEM TIME 9 VALID AS OF '2026-01-01' \
4795 SEARCH 'acme' o").unwrap().from.unwrap();
4796 assert_eq!(
4797 (t.as_of, t.valid_as_of.as_deref(), t.search.as_deref(), t.alias.as_deref()),
4798 (Some(9), Some("2026-01-01"), Some("acme"), Some("o")));
4799
4800 let s = parse(
4802 "SELECT o._id FROM orders SEARCH 'acme' o JOIN drivers d ON o.driver = d._id")
4803 .unwrap();
4804 assert_eq!(s.from.unwrap().search.as_deref(), Some("acme"));
4805 assert_eq!(s.joins[0].table.search, None);
4806 }
4807
4808 #[test]
4816 fn the_new_verbs_do_not_steal_names_that_already_worked() {
4817 let t = parse("SELECT search.total FROM orders search").unwrap().from.unwrap();
4818 assert_eq!((t.alias.as_deref(), t.search.as_deref()), (Some("search"), None));
4819
4820 let t = parse("SELECT valid.total FROM orders valid").unwrap().from.unwrap();
4821 assert_eq!((t.alias.as_deref(), t.valid_as_of.as_deref()), (Some("valid"), None));
4822
4823 assert!(parse("SELECT search, valid FROM orders").is_ok());
4825 assert!(parse("SELECT _id FROM orders WHERE search = 'x'").is_ok());
4826
4827 let t = parse("SELECT x FROM orders AS valid").unwrap().from.unwrap();
4829 assert_eq!(t.alias.as_deref(), Some("valid"));
4830 }
4831
4832 #[test]
4833 fn a_verbs_argument_is_refused_when_it_is_the_wrong_kind_of_thing() {
4834 let e = parse("SELECT _id FROM orders VALID AS OF 42").unwrap_err().to_string();
4838 assert!(e.contains("date string"), "{}", e);
4839 }
4840
4841 #[test]
4842 fn a_quoted_datetime_is_a_wall_clock_marker_and_garbage_still_refuses() {
4843 let s = parse("SELECT _id FROM orders AS OF SYSTEM TIME '2026-09-15T17:00:00Z'")
4849 .expect("a quoted RFC 3339 datetime is accepted");
4850 let marker = s.from.unwrap().as_of.expect("the marker is set");
4851 assert_ne!(marker & crate::wallclock::WALL_CLOCK_FLAG, 0,
4852 "the marker must be wall-clock-tagged, not a bare seq");
4853 let decoded = crate::wallclock::WallClock::from_marker(marker)
4854 .expect("the marker decodes");
4855 assert_eq!(decoded.epoch_secs(), 1_789_491_600.0); let s = parse("SELECT _id FROM orders AS OF SYSTEM TIME 42").unwrap();
4859 let marker = s.from.unwrap().as_of.unwrap();
4860 assert_eq!(marker, 42);
4861 assert_eq!(marker & crate::wallclock::WALL_CLOCK_FLAG, 0);
4862
4863 for sql in [
4864 "SELECT total FROM orders AS OF SYSTEM TIME now()",
4865 "SELECT total FROM orders AS OF SYSTEM TIME -1",
4866 "SELECT total FROM orders AS OF SYSTEM TIME 1.5",
4867 "SELECT total FROM orders AS OF SYSTEM TIME 'not a time'",
4868 ] {
4869 let e = parse(sql).unwrap_err().to_string();
4870 assert!(
4871 e.contains("sequence number or a quoted datetime")
4872 || e.contains("unrecognized datetime"),
4873 "{} -> {} (neither the grammar's nor the parser's refusal)",
4874 sql,
4875 e
4876 );
4877 }
4878 }
4879
4880 #[test]
4881 fn a_clause_keyword_is_never_read_as_a_bare_alias() {
4882 let s = parse("SELECT a FROM t WHERE a = 1").unwrap();
4886 assert_eq!(s.from.clone().unwrap().alias, None);
4887 assert!(s.where_.is_some(), "the WHERE clause must survive");
4888 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4889 assert_eq!(s.from.unwrap().alias, None);
4890 assert_eq!(s.order_by.len(), 1);
4891 }
4892
4893 #[test]
4894 fn a_real_alias_is_kept_in_both_spellings() {
4895 assert_eq!(parse("SELECT a FROM t x").unwrap().from.unwrap().alias,
4896 Some("x".to_string()));
4897 assert_eq!(parse("SELECT a FROM t AS x").unwrap().from.unwrap().alias,
4898 Some("x".to_string()));
4899 }
4900
4901 #[test]
4902 fn a_tables_binding_is_its_alias_else_its_bare_name() {
4903 let t = TableRef::named("pg_catalog.pg_class", Some("c".into()));
4904 assert_eq!(t.binding(), "c");
4905 let t = TableRef::named("pg_catalog.pg_class", None);
4906 assert_eq!(t.binding(), "pg_class", "the schema is not how a column is addressed");
4907 }
4908
4909 #[test]
4910 fn a_qualified_column_keeps_only_its_immediate_qualifier() {
4911 assert_eq!(parse("SELECT n.nspname FROM x").unwrap().items[0].expr,
4912 col(Some("n"), "nspname"));
4913 assert_eq!(parse("SELECT public.orders.id FROM x").unwrap().items[0].expr,
4916 col(Some("orders"), "id"));
4917 }
4918
4919 #[test]
4920 fn an_alias_may_be_a_quoted_string_with_significant_case() {
4921 let s = parse(r#"SELECT n.nspname AS "Name" FROM x"#).unwrap();
4922 assert_eq!(s.items[0].alias, Some("Name".to_string()));
4923 }
4924
4925 #[test]
4926 fn a_schema_qualified_function_drops_its_schema() {
4927 let s = parse("SELECT pg_catalog.pg_get_userbyid(n.nspowner) FROM x").unwrap();
4930 match &s.items[0].expr {
4931 Expr::Func { name, args } => {
4932 assert_eq!(name, "pg_get_userbyid");
4933 assert_eq!(args.len(), 1);
4934 assert_eq!(args[0], col(Some("n"), "nspowner"));
4935 }
4936 other => panic!("{:?}", other),
4937 }
4938 }
4939
4940 #[test]
4941 fn operator_precedence_matches_sql() {
4942 let s = parse("SELECT 1 FROM t WHERE a = 1 OR b = 2 AND c = 3").unwrap();
4945 match s.where_.unwrap() {
4946 Expr::Binary { op, right, .. } => {
4947 assert_eq!(op, "OR");
4948 assert!(matches!(*right, Expr::Binary { ref op, .. } if op == "AND"),
4949 "AND must bind tighter than OR");
4950 }
4951 other => panic!("{:?}", other),
4952 }
4953 let s = parse("SELECT 1 FROM t WHERE a = 1 AND b = 2").unwrap();
4955 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4956 let s = parse("SELECT 1 + 2 * 3 FROM t").unwrap();
4958 match &s.items[0].expr {
4959 Expr::Binary { op, right, .. } => {
4960 assert_eq!(op, "+");
4961 assert!(matches!(**right, Expr::Binary { ref op, .. } if op == "*"));
4962 }
4963 other => panic!("{:?}", other),
4964 }
4965 }
4966
4967 #[test]
4968 fn parentheses_override_precedence() {
4969 let s = parse("SELECT 1 FROM t WHERE (a = 1 OR b = 2) AND c = 3").unwrap();
4970 match s.where_.unwrap() {
4971 Expr::Binary { op, left, .. } => {
4972 assert_eq!(op, "AND");
4973 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "OR"));
4974 }
4975 other => panic!("{:?}", other),
4976 }
4977 }
4978
4979 #[test]
4980 fn in_and_is_null_and_between_parse_in_both_polarities() {
4981 let s = parse("SELECT 1 FROM t WHERE k IN ('r','p','')").unwrap();
4982 match s.where_.unwrap() {
4983 Expr::InList { list, negated, .. } => {
4984 assert_eq!(list.len(), 3);
4985 assert!(!negated);
4986 assert_eq!(list[2], Expr::Literal(json!("")));
4988 }
4989 other => panic!("{:?}", other),
4990 }
4991 assert!(matches!(parse("SELECT 1 FROM t WHERE k NOT IN (1)").unwrap().where_.unwrap(),
4992 Expr::InList { negated: true, .. }));
4993 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NULL").unwrap().where_.unwrap(),
4994 Expr::IsNull { negated: false, .. }));
4995 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NOT NULL").unwrap().where_.unwrap(),
4996 Expr::IsNull { negated: true, .. }));
4997 let s = parse("SELECT 1 FROM t WHERE n BETWEEN 1 AND 5").unwrap();
4999 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
5000 }
5001
5002 #[test]
5003 fn both_case_spellings_parse() {
5004 let s = parse("SELECT CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
5006 ELSE 'other' END FROM t").unwrap();
5007 match &s.items[0].expr {
5008 Expr::Case { operand, whens, else_ } => {
5009 assert!(operand.is_some());
5010 assert_eq!(whens.len(), 2);
5011 assert!(else_.is_some());
5012 }
5013 other => panic!("{:?}", other),
5014 }
5015 let s = parse("SELECT CASE WHEN k = 'r' THEN 1 END FROM t").unwrap();
5017 match &s.items[0].expr {
5018 Expr::Case { operand, whens, else_ } => {
5019 assert!(operand.is_none());
5020 assert_eq!(whens.len(), 1);
5021 assert!(else_.is_none());
5022 }
5023 other => panic!("{:?}", other),
5024 }
5025 assert!(parse("SELECT CASE k END FROM t").is_err());
5027 }
5028
5029 #[test]
5030 fn every_join_flavour_parses_and_an_inner_join_demands_ON() {
5031 for (sql, kind) in [
5032 ("SELECT 1 FROM a JOIN b ON a.x = b.x", JoinKind::Inner),
5033 ("SELECT 1 FROM a INNER JOIN b ON a.x = b.x", JoinKind::Inner),
5034 ("SELECT 1 FROM a LEFT JOIN b ON a.x = b.x", JoinKind::Left),
5035 ("SELECT 1 FROM a LEFT OUTER JOIN b ON a.x = b.x", JoinKind::Left),
5036 ("SELECT 1 FROM a RIGHT JOIN b ON a.x = b.x", JoinKind::Right),
5037 ("SELECT 1 FROM a FULL OUTER JOIN b ON a.x = b.x", JoinKind::Full),
5038 ("SELECT 1 FROM a CROSS JOIN b", JoinKind::Cross),
5039 ] {
5040 let s = parse(sql).unwrap_or_else(|e| panic!("{}: {}", sql, e));
5041 assert_eq!(s.joins.len(), 1, "{}", sql);
5042 assert_eq!(s.joins[0].kind, kind, "{}", sql);
5043 }
5044 let s = parse("SELECT 1 FROM a, b").unwrap();
5046 assert_eq!(s.joins[0].kind, JoinKind::Cross);
5047 assert!(parse("SELECT 1 FROM a LEFT JOIN b").is_err());
5050 assert!(parse("SELECT 1 FROM a JOIN b USING (x)").is_err());
5051 }
5052
5053 #[test]
5054 fn order_by_reads_a_number_as_an_ORDINAL() {
5055 let s = parse("SELECT a, b FROM t ORDER BY 1, 2 DESC").unwrap();
5059 assert_eq!(s.order_by.len(), 2);
5060 assert_eq!(s.order_by[0].ordinal, Some(1));
5061 assert_eq!(s.order_by[0].dir, Dir::Asc);
5062 assert_eq!(s.order_by[1].ordinal, Some(2));
5063 assert_eq!(s.order_by[1].dir, Dir::Desc);
5064 let s = parse("SELECT a FROM t ORDER BY lower(a) ASC").unwrap();
5066 assert!(s.order_by[0].ordinal.is_none());
5067 assert!(s.order_by[0].expr.is_some());
5068 }
5069
5070 #[test]
5071 fn null_ordering_defaults_the_way_postgres_defaults() {
5072 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
5073 assert!(!s.order_by[0].nulls_first, "ASC defaults to NULLS LAST");
5074 let s = parse("SELECT a FROM t ORDER BY a DESC").unwrap();
5075 assert!(s.order_by[0].nulls_first, "DESC defaults to NULLS FIRST");
5076 let s = parse("SELECT a FROM t ORDER BY a NULLS FIRST").unwrap();
5077 assert!(s.order_by[0].nulls_first, "an explicit clause wins");
5078 }
5079
5080 #[test]
5081 fn limit_and_offset_parse_in_either_order() {
5082 let s = parse("SELECT a FROM t LIMIT 5 OFFSET 2").unwrap();
5083 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
5084 let s = parse("SELECT a FROM t OFFSET 2 LIMIT 5").unwrap();
5085 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
5086 let s = parse("SELECT a FROM t LIMIT ALL").unwrap();
5087 assert_eq!(s.limit, None);
5088 }
5089
5090 #[test]
5091 fn casts_parse_and_are_recorded_rather_than_rejected() {
5092 let s = parse("SELECT x::int2 FROM t").unwrap();
5094 assert!(matches!(s.items[0].expr, Expr::Cast { .. }));
5095 let s = parse("SELECT x::pg_catalog.int2[] FROM t").unwrap();
5096 match &s.items[0].expr {
5097 Expr::Cast { ty, .. } => assert_eq!(ty, "int2[]"),
5098 other => panic!("{:?}", other),
5099 }
5100 }
5101
5102 #[test]
5103 fn star_and_qualified_star_parse() {
5104 assert_eq!(parse("SELECT * FROM t").unwrap().items[0].expr, Expr::Star);
5105 assert_eq!(parse("SELECT c.* FROM t c").unwrap().items[0].expr,
5106 Expr::QualifiedStar("c".into()));
5107 match &parse("SELECT count(*) FROM t").unwrap().items[0].expr {
5110 Expr::Agg { name, args, order_by, distinct } => {
5111 assert_eq!(name, "count");
5112 assert_eq!(args, &vec![Expr::Star]);
5113 assert!(order_by.is_empty());
5114 assert!(!distinct);
5115 }
5116 other => panic!("{:?}", other),
5117 }
5118 assert!(matches!(
5120 &parse("SELECT lower(s) FROM t").unwrap().items[0].expr,
5121 Expr::Func { name, .. } if name == "lower"
5122 ));
5123 }
5124
5125 #[test]
5126 fn an_aggregate_carries_its_own_DISTINCT_and_ORDER_BY() {
5127 match &parse("SELECT array_agg(a.attname ORDER BY a.ord) FROM t a").unwrap().items[0].expr {
5131 Expr::Agg { name, args, order_by, distinct } => {
5132 assert_eq!(name, "array_agg");
5133 assert_eq!(args.len(), 1);
5134 assert_eq!(order_by.len(), 1);
5135 assert!(matches!(order_by[0].dir, Dir::Asc));
5136 assert!(!distinct);
5137 }
5138 other => panic!("{:?}", other),
5139 }
5140 match &parse("SELECT string_agg(DISTINCT s, ',' ORDER BY b DESC NULLS LAST, c) FROM t").unwrap().items[0].expr {
5143 Expr::Agg { name, args, order_by, distinct } => {
5144 assert_eq!(name, "string_agg");
5145 assert_eq!(args.len(), 2, "the separator is an argument, not a sort key");
5146 assert_eq!(order_by.len(), 2);
5147 assert!(matches!(order_by[0].dir, Dir::Desc));
5148 assert!(!order_by[0].nulls_first, "NULLS LAST overrides the DESC default");
5149 assert!(matches!(order_by[1].dir, Dir::Asc));
5150 assert!(distinct);
5151 }
5152 other => panic!("{:?}", other),
5153 }
5154 assert!(parse("SELECT lower(DISTINCT s) FROM t").is_err());
5157 }
5158
5159 #[test]
5160 fn a_parenthesis_free_function_parses_as_a_zero_arg_call() {
5161 match &parse("SELECT current_schema FROM t").unwrap().items[0].expr {
5163 Expr::Func { name, args } => {
5164 assert_eq!(name, "current_schema");
5165 assert!(args.is_empty());
5166 }
5167 other => panic!("{:?}", other),
5168 }
5169 }
5170
5171 #[test]
5172 fn GROUP_BY_and_HAVING_parse_and_what_remains_is_refused_by_name() {
5173 let s = parse("SELECT a, count(*) FROM t GROUP BY a").unwrap();
5176 assert_eq!(s.group_by, vec![Expr::Column { qual: None, name: "a".into() }]);
5177 assert!(s.having.is_none());
5178
5179 let s = parse("SELECT a, b, count(*) FROM t GROUP BY a, b HAVING count(*) > 1").unwrap();
5180 assert_eq!(s.group_by.len(), 2);
5181 assert!(s.having.is_some());
5182
5183 let e = parse("SELECT a FROM t HAVING a > 1").unwrap_err().to_string();
5187 assert!(e.contains("HAVING needs a GROUP BY"), "{}", e);
5188
5189 for (sql, needle) in [
5190 ("SELECT DISTINCT ON (a) a FROM t", "DISTINCT ON"),
5191 ("SELECT a, count(*) FROM t GROUP BY ROLLUP (a)", "ROLLUP"),
5192 ("SELECT a, count(*) FROM t GROUP BY CUBE (a)", "CUBE"),
5193 ] {
5194 let e = parse(sql).unwrap_err().to_string();
5195 assert!(e.contains(needle), "{} -> {}", sql, e);
5196 }
5197 assert!(parse("SELECT a FROM t JUNK JUNK2").is_err());
5199 }
5200
5201 #[test]
5202 fn THE_dn_QUERY_parses_completely() {
5203 let s = parse(
5204 r#"SELECT n.nspname AS "Name",
5205 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5206 FROM pg_catalog.pg_namespace n
5207 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5208 ORDER BY 1;"#,
5209 )
5210 .expect("psql's \\dn must parse");
5211
5212 assert_eq!(s.items.len(), 2);
5213 assert_eq!(s.items[0].alias, Some("Name".into()));
5214 assert_eq!(s.items[1].alias, Some("Owner".into()));
5215 let from = s.from.unwrap();
5216 assert_eq!(from.name, "pg_catalog.pg_namespace");
5217 assert_eq!(from.binding(), "n");
5218 assert!(s.where_.is_some());
5219 assert_eq!(s.order_by[0].ordinal, Some(1));
5220 }
5221
5222 #[test]
5223 fn THE_dt_QUERY_parses_completely() {
5224 let s = parse(
5225 r#"SELECT n.nspname as "Schema",
5226 c.relname as "Name",
5227 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5228 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5229 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5230 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5231 WHEN 'I' THEN 'partitioned index' END as "Type",
5232 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5233 FROM pg_catalog.pg_class c
5234 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5235 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5236 WHERE c.relkind IN ('r','p','')
5237 AND n.nspname <> 'pg_catalog'
5238 AND n.nspname !~ '^pg_toast'
5239 AND n.nspname <> 'information_schema'
5240 AND pg_catalog.pg_table_is_visible(c.oid)
5241 ORDER BY 1,2;"#,
5242 )
5243 .expect("psql's \\dt must parse");
5244
5245 assert_eq!(s.items.len(), 4);
5246 assert_eq!(s.items[2].alias, Some("Type".into()));
5247 match &s.items[2].expr {
5248 Expr::Case { whens, .. } => assert_eq!(whens.len(), 9, "all nine branches"),
5249 other => panic!("{:?}", other),
5250 }
5251 assert_eq!(s.joins.len(), 2);
5252 assert!(s.joins.iter().all(|j| j.kind == JoinKind::Left && j.on.is_some()));
5253 assert_eq!(s.from.unwrap().binding(), "c");
5254 assert_eq!(s.order_by.len(), 2);
5255 assert_eq!(
5256 (s.order_by[0].ordinal, s.order_by[1].ordinal),
5257 (Some(1), Some(2))
5258 );
5259 }
5260}
5261
5262#[cfg(test)]
5263mod eval_tests {
5264 use super::*;
5265 use serde_json::json;
5266
5267 fn one(row: &Value) -> Bound<'_> {
5269 Bound::new(vec![("t".to_string(), Some(row))])
5270 }
5271
5272 fn ev(sql_expr: &str, row: &Value) -> Result<Value> {
5273 let s = parse(&format!("SELECT {} FROM t", sql_expr))?;
5274 eval(&s.items[0].expr, &one(row))
5275 }
5276
5277 fn v(sql_expr: &str, row: &Value) -> Value {
5278 ev(sql_expr, row).unwrap_or_else(|e| panic!("{}: {}", sql_expr, e))
5279 }
5280
5281 #[test]
5282 fn literals_and_columns_resolve() {
5283 let r = json!({"a": 1, "s": "x", "b": true, "n": null});
5284 assert_eq!(v("42", &r), json!(42));
5285 assert_eq!(v("'hi'", &r), json!("hi"));
5286 assert_eq!(v("NULL", &r), Value::Null);
5287 assert_eq!(v("TRUE", &r), json!(true));
5288 assert_eq!(v("a", &r), json!(1));
5289 assert_eq!(v("t.a", &r), json!(1));
5290 assert_eq!(v("s", &r), json!("x"));
5291 assert_eq!(v("nosuch", &r), Value::Null);
5294 }
5295
5296 #[test]
5297 fn an_unknown_table_ALIAS_is_an_error_while_an_unknown_column_is_null() {
5298 let r = json!({"a": 1});
5301 assert_eq!(v("t.nosuch", &r), Value::Null);
5302 let e = ev("zz.a", &r).unwrap_err().to_string();
5303 assert!(e.contains("zz"), "{}", e);
5304 }
5305
5306 #[test]
5309 fn every_comparison_over_NULL_is_UNKNOWN_including_null_equals_null() {
5310 let r = json!({"n": null, "a": 1});
5311 assert_eq!(v("n = 1", &r), Value::Null);
5312 assert_eq!(v("n != 1", &r), Value::Null);
5313 assert_eq!(v("n < 1", &r), Value::Null);
5314 assert_eq!(v("n = n", &r), Value::Null);
5316 assert_eq!(v("n = NULL", &r), Value::Null);
5317 }
5318
5319 #[test]
5320 fn NOT_UNKNOWN_is_UNKNOWN_not_true() {
5321 let r = json!({"n": null});
5325 assert_eq!(v("NOT (n = 1)", &r), Value::Null);
5326 assert_eq!(v("NOT TRUE", &r), json!(false));
5327 assert_eq!(v("NOT FALSE", &r), json!(true));
5328 }
5329
5330 #[test]
5331 fn AND_and_OR_follow_the_three_valued_truth_tables() {
5332 let r = json!({"n": null});
5333 assert_eq!(v("FALSE AND n = 1", &r), json!(false));
5335 assert_eq!(v("TRUE AND n = 1", &r), Value::Null);
5337 assert_eq!(v("TRUE OR n = 1", &r), json!(true));
5339 assert_eq!(v("FALSE OR n = 1", &r), Value::Null);
5341 assert_eq!(v("TRUE AND TRUE", &r), json!(true));
5343 assert_eq!(v("TRUE AND FALSE", &r), json!(false));
5344 assert_eq!(v("FALSE OR FALSE", &r), json!(false));
5345 }
5346
5347 #[test]
5348 fn IS_NULL_is_the_one_predicate_that_is_never_unknown() {
5349 let r = json!({"n": null, "a": 1});
5350 assert_eq!(v("n IS NULL", &r), json!(true));
5351 assert_eq!(v("n IS NOT NULL", &r), json!(false));
5352 assert_eq!(v("a IS NULL", &r), json!(false));
5353 assert_eq!(v("a IS NOT NULL", &r), json!(true));
5354 assert_eq!(v("nosuch IS NULL", &r), json!(true));
5357 }
5358
5359 #[test]
5360 fn NOT_IN_with_a_NULL_in_the_list_is_UNKNOWN_the_classic_trap() {
5361 let r = json!({"a": 2});
5362 assert_eq!(v("a IN (1, 2)", &r), json!(true));
5363 assert_eq!(v("a IN (1, 3)", &r), json!(false));
5364 assert_eq!(v("a NOT IN (1, 3)", &r), json!(true));
5365 assert_eq!(v("a NOT IN (1, NULL)", &r), Value::Null);
5368 assert_eq!(v("a IN (2, NULL)", &r), json!(true));
5370 assert_eq!(v("nosuch IN (1)", &r), Value::Null);
5372 }
5373
5374 #[test]
5377 fn comparisons_work_across_numbers_strings_and_booleans() {
5378 let r = json!({"n": 5, "s": "b", "t": true});
5379 assert_eq!(v("n > 3", &r), json!(true));
5380 assert_eq!(v("n <= 5", &r), json!(true));
5381 assert_eq!(v("s < 'c'", &r), json!(true));
5382 assert_eq!(v("s > 'c'", &r), json!(false));
5383 assert_eq!(v("n = '5'", &r), json!(true));
5386 assert_eq!(v("n = '5.0'", &r), json!(true));
5387 assert_eq!(v("n = 'five'", &r), json!(false));
5390 }
5391
5392 #[test]
5393 fn the_regex_operators_use_the_SAME_matcher_as_NQL() {
5394 let r = json!({"s": "pg_catalog"});
5397 assert_eq!(v("s ~ '^pg_'", &r), json!(true));
5398 assert_eq!(v("s !~ '^pg_'", &r), json!(false));
5399 assert_eq!(v("s ~ '^PG_'", &r), json!(false));
5400 assert_eq!(v("s ~* '^PG_'", &r), json!(true));
5401 assert_eq!(v("s !~ '^zz'", &r), json!(true));
5402 assert_eq!(v("nosuch ~ '^x'", &r), Value::Null);
5404 assert_eq!(v("s ~ '^(pg_catalog)$'", &r), json!(true));
5407 assert_eq!(v("s ~ '^(pg_.*)$'", &r), json!(true));
5408 assert_eq!(v("s ~ '^(public|pg_catalog)$'", &r), json!(true));
5409 assert_eq!(v("s ~ '^pg_[a-z]+$'", &r), json!(true));
5410 assert_eq!(v("s ~ '^pg_[0-9]+$'", &r), json!(false));
5411 let e = ev("s ~ 'a{2}'", &r).unwrap_err().to_string();
5413 assert!(e.contains("interval"), "{}", e);
5414 }
5415
5416 #[test]
5417 fn like_works_in_all_four_spellings() {
5418 let r = json!({"s": "Acme Pool"});
5419 assert_eq!(v("s LIKE 'Acme%'", &r), json!(true));
5420 assert_eq!(v("s LIKE 'acme%'", &r), json!(false));
5421 assert_eq!(v("s ILIKE 'acme%'", &r), json!(true));
5422 assert_eq!(v("s NOT LIKE 'zz%'", &r), json!(true));
5423
5424 assert_eq!(v(r"s ILIKE 'acme_pool' ESCAPE '/'", &r), json!(true),
5438 "an unescaped _ is still a wildcard, and matches the space");
5439 assert_eq!(v(r"s ILIKE 'acme/_pool' ESCAPE '/'", &r), json!(false),
5440 "escaped _ is a LITERAL underscore, which 'Acme Pool' lacks");
5441 assert_eq!(v(r"s ILIKE 'acme%' ESCAPE '/'", &r), json!(true),
5442 "an unescaped % is still a wildcard");
5443 assert_eq!(v(r"s ILIKE 'acme/%' ESCAPE '/'", &r), json!(false),
5444 "escaped % is a literal percent sign, not 'anything'");
5445 assert_eq!(v(r"s NOT ILIKE 'acme/_pool' ESCAPE '/'", &r), json!(true),
5446 "negation composes with ESCAPE");
5447 assert_eq!(v(r"s ILIKE 'acme/' ESCAPE '/'", &r), json!(false),
5451 "a dangling escape is treated as a literal, never an error");
5452 assert_eq!(v("nosuch LIKE 'x'", &r), Value::Null);
5453 }
5454
5455 #[test]
5456 fn arithmetic_and_concatenation_propagate_null_and_refuse_div_by_zero() {
5457 let r = json!({"a": 7, "b": 2});
5458 assert_eq!(v("a + b", &r), json!(9));
5459 assert_eq!(v("a - b", &r), json!(5));
5460 assert_eq!(v("a * b", &r), json!(14));
5461 assert_eq!(v("a / b", &r), json!(3.5));
5462 assert_eq!(v("a % b", &r), json!(1));
5463 assert_eq!(v("-a", &r), json!(-7));
5464 assert_eq!(v("a / b", &r), json!(3.5));
5467 assert_eq!(v("b / a", &r), json!(2.0 / 7.0));
5468 assert_eq!(v("'x' || 'y'", &r), json!("xy"));
5469 assert_eq!(v("'x' || nosuch", &r), Value::Null);
5470 assert_eq!(v("a + nosuch", &r), Value::Null);
5471 assert!(ev("a / 0", &r).is_err());
5474 assert!(ev("a % 0", &r).is_err());
5475 }
5476
5477 #[test]
5478 fn a_cast_is_transparent_rather_than_rejected() {
5479 let r = json!({"a": 7});
5482 assert_eq!(v("a::int2", &r), json!(7));
5483 assert_eq!(v("a::pg_catalog.int2[]", &r), json!(7));
5484 }
5485
5486 #[test]
5489 fn a_simple_CASE_picks_the_matching_branch() {
5490 let expr = "CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
5492 WHEN 'i' THEN 'index' END";
5493 assert_eq!(v(expr, &json!({"k": "r"})), json!("table"));
5494 assert_eq!(v(expr, &json!({"k": "v"})), json!("view"));
5495 assert_eq!(v(expr, &json!({"k": "i"})), json!("index"));
5496 assert_eq!(v(expr, &json!({"k": "z"})), Value::Null);
5499 }
5500
5501 #[test]
5502 fn a_searched_CASE_evaluates_predicates_and_UNKNOWN_does_not_match() {
5503 let expr = "CASE WHEN n > 5 THEN 'big' WHEN n > 0 THEN 'small' ELSE 'none' END";
5504 assert_eq!(v(expr, &json!({"n": 9})), json!("big"));
5505 assert_eq!(v(expr, &json!({"n": 2})), json!("small"));
5506 assert_eq!(v(expr, &json!({"n": -1})), json!("none"));
5507 assert_eq!(v(expr, &json!({"other": 1})), json!("none"));
5509 }
5510
5511 #[test]
5512 fn an_ELSE_branch_is_used_when_nothing_matches() {
5513 assert_eq!(
5514 v("CASE k WHEN 'r' THEN 'table' ELSE 'other' END", &json!({"k": "z"})),
5515 json!("other")
5516 );
5517 }
5518
5519 #[test]
5522 fn the_catalogue_functions_psql_calls_all_answer() {
5523 let r = json!({"o": 10, "enc": 6});
5524 assert_eq!(v("pg_get_userbyid(o)", &r), json!("nedb"));
5526 assert_eq!(v("pg_catalog.pg_get_userbyid(o)", &r), json!("nedb"));
5527 assert_eq!(v("pg_table_is_visible(o)", &r), json!(true));
5529 assert_eq!(v("pg_encoding_to_char(enc)", &r), json!("UTF8"));
5530 assert_eq!(v("current_schema", &r), json!("public"));
5531 assert_eq!(v("current_database()", &r), json!("nedb"));
5532 assert_eq!(v("current_user", &r), json!("nedb"));
5533 assert_eq!(v("pg_get_expr(o, o)", &r), Value::Null);
5536 assert_eq!(v("obj_description(o)", &r), Value::Null);
5537 }
5538
5539 #[test]
5540 fn text_and_null_handling_functions_work() {
5541 let r = json!({"s": "AbC", "n": null});
5542 assert_eq!(v("lower(s)", &r), json!("abc"));
5543 assert_eq!(v("upper(s)", &r), json!("ABC"));
5544 assert_eq!(v("length(s)", &r), json!(3));
5545 assert_eq!(v("lower(n)", &r), Value::Null);
5546 assert_eq!(v("coalesce(n, 'fallback')", &r), json!("fallback"));
5547 assert_eq!(v("coalesce(s, 'fallback')", &r), json!("AbC"));
5548 assert_eq!(v("coalesce(n, n)", &r), Value::Null);
5549 assert_eq!(v("nullif(s, 'AbC')", &r), Value::Null);
5550 assert_eq!(v("nullif(s, 'zz')", &r), json!("AbC"));
5551 assert_eq!(v("format_type(20, NULL)", &r), json!("bigint"));
5553 }
5554
5555 #[test]
5556 fn coalesce_does_not_evaluate_past_its_first_non_null() {
5557 let r = json!({"a": 1});
5559 assert_eq!(v("coalesce(a, a / 0)", &r), json!(1));
5560 }
5561
5562 #[test]
5563 fn an_unknown_function_is_REFUSED_rather_than_answered_with_NULL() {
5564 let e = ev("pg_stat_get_numscans(1)", &json!({})).unwrap_err().to_string();
5567 assert!(e.contains("pg_stat_get_numscans"), "{}", e);
5568 assert!(e.contains("refused"), "{}", e);
5569 }
5570
5571 #[test]
5574 fn a_qualified_column_reads_only_its_OWN_binding() {
5575 let a = json!({"name": "left", "x": 1});
5578 let b = json!({"name": "right", "y": 2});
5579 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), Some(&b))]);
5580 let get = |e: &str| {
5581 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5582 eval(&s.items[0].expr, &row).unwrap()
5583 };
5584 assert_eq!(get("a.name"), json!("left"));
5585 assert_eq!(get("b.name"), json!("right"));
5586 assert_eq!(get("name"), json!("left"));
5588 assert_eq!(get("y"), json!(2), "a bare name still finds a later binding");
5589 }
5590
5591 #[test]
5592 fn an_unmatched_LEFT_JOIN_side_reads_as_NULL_not_as_a_missing_column() {
5593 let a = json!({"x": 1});
5596 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), None)]);
5597 let get = |e: &str| {
5598 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5599 eval(&s.items[0].expr, &row).unwrap()
5600 };
5601 assert_eq!(get("b.anything"), Value::Null);
5602 assert_eq!(get("b.anything IS NULL"), json!(true));
5603 assert_eq!(get("a.x"), json!(1));
5604 }
5605}
5606
5607#[cfg(test)]
5608mod exec_tests {
5609 use super::*;
5610 use serde_json::json;
5611
5612 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5614 let owned: Vec<(String, Vec<Value>)> =
5615 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
5616 move |name: &str| {
5617 let bare = name.rsplit('.').next().unwrap_or(name);
5619 Ok(owned
5620 .iter()
5621 .find(|(n, _)| n == name || n == bare)
5622 .map(|(_, r)| from_vec(r.clone())))
5623 }
5624 }
5625
5626 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
5627 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
5628 (cols.into_iter().map(|c| c.name).collect(), rows)
5629 }
5630
5631 fn col(rows: &[Value], name: &str) -> Vec<Value> {
5632 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
5633 }
5634
5635 #[test]
5638 fn select_columns_where_order_limit_offset() {
5639 let t = tables(vec![(
5640 "t",
5641 vec![json!({"a": 3, "s": "c"}), json!({"a": 1, "s": "a"}), json!({"a": 2, "s": "b"})],
5642 )]);
5643 let (names, rows) = go("SELECT a, s FROM t ORDER BY a", &t);
5644 assert_eq!(names, vec!["a", "s"]);
5645 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2), json!(3)]);
5646
5647 let (_, rows) = go("SELECT a FROM t ORDER BY a DESC", &t);
5648 assert_eq!(col(&rows, "a"), vec![json!(3), json!(2), json!(1)]);
5649
5650 let (_, rows) = go("SELECT a FROM t WHERE a > 1 ORDER BY a", &t);
5651 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5652
5653 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 2", &t);
5654 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5655
5656 let (_, rows) = go("SELECT a FROM t ORDER BY a OFFSET 1", &t);
5657 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5658
5659 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 1 OFFSET 1", &t);
5660 assert_eq!(col(&rows, "a"), vec![json!(2)]);
5661
5662 let (_, rows) = go("SELECT a FROM t OFFSET 99", &t);
5664 assert!(rows.is_empty());
5665 }
5666
5667 #[test]
5668 fn an_output_column_takes_its_alias_or_a_derived_name() {
5669 let t = tables(vec![("t", vec![json!({"a": 1})])]);
5672 assert_eq!(go(r#"SELECT a AS "Name" FROM t"#, &t).0, vec!["Name"]);
5673 assert_eq!(go("SELECT a FROM t", &t).0, vec!["a"]);
5674 assert_eq!(go("SELECT lower('X') FROM t", &t).0, vec!["lower"]);
5675 assert_eq!(go("SELECT 1 + 1 FROM t", &t).0, vec!["?column?"]);
5676 assert_eq!(go("SELECT CASE a WHEN 1 THEN 'x' END FROM t", &t).0, vec!["case"]);
5677 }
5678
5679 #[test]
5680 fn star_expands_from_the_rows_and_a_qualified_star_from_one_binding() {
5681 let t = tables(vec![
5682 ("a", vec![json!({"x": 1, "y": 2})]),
5683 ("b", vec![json!({"z": 3})]),
5684 ]);
5685 let (names, rows) = go("SELECT * FROM a", &t);
5686 assert_eq!(names, vec!["x", "y"]);
5687 assert_eq!(rows.len(), 1);
5688
5689 let (names, _) = go("SELECT a.* FROM a CROSS JOIN b", &t);
5690 assert_eq!(names, vec!["x", "y"], "a qualified star takes ONE binding");
5691
5692 let empty = tables(vec![("e", vec![])]);
5695 assert_eq!(go("SELECT * FROM e", &empty).0, Vec::<String>::new());
5696 }
5697
5698 #[test]
5699 fn distinct_dedupes_on_the_projected_values() {
5700 let t = tables(vec![(
5701 "t",
5702 vec![json!({"g": "x"}), json!({"g": "x"}), json!({"g": "y"})],
5703 )]);
5704 let (_, rows) = go("SELECT DISTINCT g FROM t ORDER BY 1", &t);
5705 assert_eq!(col(&rows, "g"), vec![json!("x"), json!("y")]);
5706 let (_, rows) = go("SELECT g FROM t", &t);
5707 assert_eq!(rows.len(), 3, "without DISTINCT every row survives");
5708 }
5709
5710 #[test]
5711 fn order_by_an_ORDINAL_sorts_the_projected_column() {
5712 let t = tables(vec![(
5713 "t",
5714 vec![json!({"a": 2, "b": "z"}), json!({"a": 1, "b": "y"})],
5715 )]);
5716 let (_, rows) = go("SELECT a, b FROM t ORDER BY 1", &t);
5717 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5718 let (_, rows) = go("SELECT a, b FROM t ORDER BY 2 DESC", &t);
5719 assert_eq!(col(&rows, "b"), vec![json!("z"), json!("y")]);
5720 let e = run("SELECT a FROM t ORDER BY 3", &t).unwrap_err().to_string();
5722 assert!(e.contains("out of range"), "{}", e);
5723 }
5724
5725 #[test]
5726 fn order_by_an_expression_may_use_a_column_NOT_in_the_select_list() {
5727 let t = tables(vec![(
5728 "t",
5729 vec![json!({"a": 1, "hidden": 9}), json!({"a": 2, "hidden": 1})],
5730 )]);
5731 let (_, rows) = go("SELECT a FROM t ORDER BY hidden", &t);
5732 assert_eq!(col(&rows, "a"), vec![json!(2), json!(1)]);
5733 }
5734
5735 #[test]
5736 fn null_ordering_follows_the_direction_defaults() {
5737 let t = tables(vec![(
5738 "t",
5739 vec![json!({"a": 2}), json!({"a": null}), json!({"a": 1})],
5740 )]);
5741 assert_eq!(col(&go("SELECT a FROM t ORDER BY a", &t).1, "a"),
5743 vec![json!(1), json!(2), Value::Null]);
5744 assert_eq!(col(&go("SELECT a FROM t ORDER BY a DESC", &t).1, "a"),
5746 vec![Value::Null, json!(2), json!(1)]);
5747 assert_eq!(col(&go("SELECT a FROM t ORDER BY a NULLS FIRST", &t).1, "a"),
5749 vec![Value::Null, json!(1), json!(2)]);
5750 }
5751
5752 #[test]
5753 fn a_where_clause_that_is_UNKNOWN_excludes_the_row() {
5754 let t = tables(vec![(
5755 "t",
5756 vec![json!({"a": 1}), json!({"a": null}), json!({"other": 1})],
5757 )]);
5758 let (_, rows) = go("SELECT a FROM t WHERE a = 1", &t);
5760 assert_eq!(rows.len(), 1);
5761 let (_, rows) = go("SELECT a FROM t WHERE NOT (a = 1)", &t);
5763 assert_eq!(rows.len(), 0, "NOT UNKNOWN must not resurrect a null row");
5764 }
5765
5766 #[test]
5767 fn select_with_no_FROM_returns_exactly_one_row() {
5768 let t = tables(vec![]);
5770 let (names, rows) = go("SELECT 1", &t);
5771 assert_eq!(rows.len(), 1);
5772 assert_eq!(names, vec!["?column?"]);
5773 assert_eq!(go("SELECT current_schema", &t).1.len(), 1);
5774 }
5775
5776 #[test]
5777 fn an_unknown_relation_is_NAMED_rather_than_answered_with_no_rows() {
5778 let t = tables(vec![("t", vec![])]);
5781 let e = run("SELECT a FROM nosuchtable", &t).unwrap_err().to_string();
5782 assert!(e.contains("nosuchtable"), "{}", e);
5783 assert!(e.contains("does not exist"), "{}", e);
5784 }
5785
5786 #[test]
5789 fn an_inner_join_keeps_only_matching_pairs() {
5790 let t = tables(vec![
5791 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5792 ("r", vec![json!({"lid": 1, "v": "x"})]),
5793 ]);
5794 let (_, rows) = go("SELECT l.n, r.v FROM l JOIN r ON r.lid = l.id", &t);
5795 assert_eq!(rows.len(), 1);
5796 assert_eq!(col(&rows, "n"), vec![json!("a")]);
5797 }
5798
5799 #[test]
5800 fn a_LEFT_join_keeps_unmatched_left_rows_with_NULLs() {
5801 let t = tables(vec![
5803 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5804 ("r", vec![json!({"lid": 1, "v": "x"})]),
5805 ]);
5806 let (_, rows) = go("SELECT l.n, r.v FROM l LEFT JOIN r ON r.lid = l.id ORDER BY 1", &t);
5807 assert_eq!(rows.len(), 2);
5808 assert_eq!(col(&rows, "n"), vec![json!("a"), json!("b")]);
5809 assert_eq!(col(&rows, "v"), vec![json!("x"), Value::Null]);
5810 }
5811
5812 #[test]
5813 fn a_RIGHT_join_keeps_unmatched_right_rows_and_FULL_keeps_both() {
5814 let t = tables(vec![
5815 ("l", vec![json!({"id": 1})]),
5816 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5817 ]);
5818 let (_, rows) = go("SELECT l.id, r.lid FROM l RIGHT JOIN r ON r.lid = l.id", &t);
5819 assert_eq!(rows.len(), 2);
5820 assert!(col(&rows, "id").contains(&Value::Null), "the unmatched right row keeps NULLs on the left");
5821
5822 let t2 = tables(vec![
5823 ("l", vec![json!({"id": 1}), json!({"id": 5})]),
5824 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5825 ]);
5826 let (_, rows) = go("SELECT l.id, r.lid FROM l FULL OUTER JOIN r ON r.lid = l.id", &t2);
5827 assert_eq!(rows.len(), 3, "one match plus one orphan on each side");
5828 }
5829
5830 #[test]
5831 fn a_cross_join_is_the_cartesian_product() {
5832 let t = tables(vec![
5833 ("a", vec![json!({"x": 1}), json!({"x": 2})]),
5834 ("b", vec![json!({"y": 1}), json!({"y": 2}), json!({"y": 3})]),
5835 ]);
5836 assert_eq!(go("SELECT a.x, b.y FROM a CROSS JOIN b", &t).1.len(), 6);
5837 assert_eq!(go("SELECT a.x, b.y FROM a, b", &t).1.len(), 6);
5839 }
5840
5841 #[test]
5842 fn an_ON_clause_that_is_UNKNOWN_does_not_join() {
5843 let t = tables(vec![
5846 ("l", vec![json!({"id": null})]),
5847 ("r", vec![json!({"lid": null})]),
5848 ]);
5849 let (_, rows) = go("SELECT l.id FROM l JOIN r ON r.lid = l.id", &t);
5850 assert!(rows.is_empty(), "NULL = NULL is UNKNOWN, so nothing joins");
5851 let (_, rows) = go("SELECT l.id FROM l LEFT JOIN r ON r.lid = l.id", &t);
5853 assert_eq!(rows.len(), 1);
5854 }
5855
5856 #[test]
5857 fn two_joins_chain() {
5858 let t = tables(vec![
5859 ("a", vec![json!({"id": 1, "bid": 10, "cid": 100})]),
5860 ("b", vec![json!({"id": 10, "bn": "B"})]),
5861 ("c", vec![json!({"id": 100, "cn": "C"})]),
5862 ]);
5863 let (_, rows) = go(
5864 "SELECT a.id, b.bn, c.cn FROM a \
5865 LEFT JOIN b ON b.id = a.bid \
5866 LEFT JOIN c ON c.id = a.cid",
5867 &t,
5868 );
5869 assert_eq!(rows.len(), 1);
5870 assert_eq!(col(&rows, "bn"), vec![json!("B")]);
5871 assert_eq!(col(&rows, "cn"), vec![json!("C")]);
5872 }
5873
5874 fn catalog() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5878 tables(vec![
5879 (
5880 "pg_namespace",
5881 vec![
5882 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5883 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5884 json!({"oid": 13000, "nspname": "information_schema", "nspowner": 10}),
5885 ],
5886 ),
5887 (
5888 "pg_class",
5889 vec![
5890 json!({"oid": 16401, "relname": "orders", "relnamespace": 2200,
5891 "relkind": "r", "relowner": 10, "relam": 2}),
5892 json!({"oid": 16402, "relname": "drivers", "relnamespace": 2200,
5893 "relkind": "r", "relowner": 10, "relam": 2}),
5894 ],
5895 ),
5896 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5897 ])
5898 }
5899
5900 #[test]
5901 fn THE_dn_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5902 let (names, rows) = go(
5903 r#"SELECT n.nspname AS "Name",
5904 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5905 FROM pg_catalog.pg_namespace n
5906 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5907 ORDER BY 1;"#,
5908 &catalog(),
5909 );
5910
5911 assert_eq!(names, vec!["Name", "Owner"], "psql reads these BY NAME");
5912 assert_eq!(col(&rows, "Name"), vec![json!("public")]);
5915 assert_eq!(col(&rows, "Owner"), vec![json!("nedb")]);
5916 }
5917
5918 #[test]
5919 fn THE_dt_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5920 let (names, rows) = go(
5921 r#"SELECT n.nspname as "Schema",
5922 c.relname as "Name",
5923 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5924 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5925 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5926 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5927 WHEN 'I' THEN 'partitioned index' END as "Type",
5928 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5929 FROM pg_catalog.pg_class c
5930 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5931 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5932 WHERE c.relkind IN ('r','p','')
5933 AND n.nspname <> 'pg_catalog'
5934 AND n.nspname !~ '^pg_toast'
5935 AND n.nspname <> 'information_schema'
5936 AND pg_catalog.pg_table_is_visible(c.oid)
5937 ORDER BY 1,2;"#,
5938 &catalog(),
5939 );
5940
5941 assert_eq!(names, vec!["Schema", "Name", "Type", "Owner"]);
5942 assert_eq!(col(&rows, "Name"), vec![json!("drivers"), json!("orders")]);
5944 assert_eq!(col(&rows, "Schema"), vec![json!("public"), json!("public")]);
5945 assert_eq!(col(&rows, "Type"), vec![json!("table"), json!("table")]);
5947 assert_eq!(col(&rows, "Owner"), vec![json!("nedb"), json!("nedb")]);
5948 }
5949
5950 #[test]
5951 fn the_dt_query_still_filters_correctly_with_a_system_relation_present() {
5952 let t = tables(vec![
5956 (
5957 "pg_namespace",
5958 vec![
5959 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5960 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5961 ],
5962 ),
5963 (
5964 "pg_class",
5965 vec![
5966 json!({"oid": 1, "relname": "mine", "relnamespace": 2200,
5967 "relkind": "r", "relowner": 10, "relam": 2}),
5968 json!({"oid": 2, "relname": "pg_internal", "relnamespace": 11,
5969 "relkind": "r", "relowner": 10, "relam": 2}),
5970 json!({"oid": 3, "relname": "an_index", "relnamespace": 2200,
5971 "relkind": "i", "relowner": 10, "relam": 2}),
5972 ],
5973 ),
5974 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5975 ]);
5976 let (_, rows) = go(
5977 r#"SELECT c.relname as "Name" FROM pg_catalog.pg_class c
5978 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5979 WHERE c.relkind IN ('r','p','') AND n.nspname <> 'pg_catalog'
5980 ORDER BY 1"#,
5981 &t,
5982 );
5983 assert_eq!(col(&rows, "Name"), vec![json!("mine")],
5984 "a system relation and an index must both be filtered out");
5985 }
5986}
5987
5988#[cfg(test)]
5989mod operator_syntax_tests {
5990 use super::*;
5991 use serde_json::json;
5992
5993 #[test]
5994 fn the_OPERATOR_qualification_psql_generates_is_understood() {
5995 let s = parse(
5999 "SELECT a FROM t WHERE n OPERATOR(pg_catalog.~) '^x' \
6000 AND m OPERATOR(pg_catalog.=) 1",
6001 )
6002 .expect("psql's OPERATOR() form must parse");
6003 match s.where_.unwrap() {
6004 Expr::Binary { op, left, .. } => {
6005 assert_eq!(op, "AND");
6006 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "~"));
6007 }
6008 other => panic!("{:?}", other),
6009 }
6010 }
6011
6012 #[test]
6013 fn an_OPERATOR_qualified_comparison_EVALUATES() {
6014 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
6015 Ok(Some(from_vec(vec![json!({"n": "orders"}), json!({"n": "pg_toast_1"})])))
6016 };
6017 let (_, rows) = run(
6018 "SELECT n FROM pg_class WHERE n OPERATOR(pg_catalog.~) '^ord'",
6019 &t,
6020 )
6021 .unwrap();
6022 assert_eq!(rows.len(), 1);
6023 assert_eq!(rows[0]["n"], json!("orders"));
6024 }
6025
6026 #[test]
6027 fn a_subquery_an_ARRAY_constructor_and_EXISTS_all_PARSE() {
6028 let s = parse("SELECT a FROM t WHERE x = (SELECT 1)").unwrap();
6031 assert!(matches!(s.where_, Some(Expr::Binary { ref right, .. }) if matches!(**right, Expr::Subquery(_))));
6032 let s = parse("SELECT array_to_string(ARRAY(SELECT a FROM b), ',') FROM t").unwrap();
6033 assert!(matches!(&s.items[0].expr, Expr::Func { args, .. } if matches!(args[0], Expr::ArrayQuery(_))));
6034 let s = parse("SELECT a FROM t WHERE EXISTS (SELECT 1)").unwrap();
6035 assert!(matches!(s.where_, Some(Expr::Exists { negated: false, .. })));
6036 let s = parse("SELECT a FROM t WHERE NOT EXISTS (SELECT 1)").unwrap();
6037 assert!(matches!(s.where_, Some(Expr::Unary { ref expr, .. }) if matches!(**expr, Expr::Exists { .. })));
6038 let s = parse("SELECT a FROM t WHERE oid = ANY (polroles) AND 'd' = any(kinds) AND x <> ALL (SELECT y FROM u)").unwrap();
6040 assert!(s.where_.is_some());
6041 let s = parse("SELECT prattrs[s] FROM t").unwrap();
6042 assert!(matches!(s.items[0].expr, Expr::Index { .. }));
6043 let s = parse("SELECT CAST('tuple' AS pg_catalog.text), CAST(n AS int2[]) FROM t").unwrap();
6044 assert!(matches!(&s.items[0].expr, Expr::Cast { ty, .. } if ty == "text"));
6045 assert!(matches!(&s.items[1].expr, Expr::Cast { ty, .. } if ty == "int2[]"));
6046 let s = parse("SELECT a FROM t WHERE a IS DISTINCT FROM b").unwrap();
6047 assert!(matches!(s.where_, Some(Expr::Binary { ref op, .. }) if op == "IS DISTINCT FROM"));
6048 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();
6050 assert_eq!(s.joins.len(), 3);
6051 assert!(matches!(s.joins[1].kind, JoinKind::Cross));
6052 let s = parse("SELECT 1 FROM c, LATERAL (SELECT 2 AS two) s").unwrap();
6054 assert!(s.joins[0].table.lateral && s.joins[0].table.sub.is_some());
6055 let s = parse("SELECT tt.a FROM (SELECT 1 AS a UNION ALL SELECT 2) AS tt ORDER BY 1").unwrap();
6056 assert_eq!(s.from.as_ref().unwrap().sub.as_ref().unwrap().set_ops.len(), 1);
6057 }
6058
6059 #[test]
6060 fn a_compound_query_keeps_ORDER_BY_for_the_whole() {
6061 let s = parse("SELECT a FROM t UNION SELECT b FROM u UNION ALL SELECT c FROM v ORDER BY 1 LIMIT 5").unwrap();
6062 assert_eq!(s.set_ops.len(), 2);
6063 assert_eq!(s.set_ops[0].op, SetOp::Union);
6064 assert!(!s.set_ops[0].all);
6065 assert!(s.set_ops[1].all);
6066 assert_eq!(s.order_by.len(), 1);
6067 assert_eq!(s.limit, Some(5));
6068 assert!(s.set_ops[1].query.order_by.is_empty(), "the tail belongs to the whole, not the last arm");
6069 }
6070}
6071
6072#[cfg(test)]
6073mod subquery_exec_tests {
6074 use super::*;
6075 use serde_json::json;
6076
6077 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
6078 let owned: Vec<(String, Vec<Value>)> =
6079 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
6080 move |name: &str| {
6081 let bare = name.rsplit('.').next().unwrap_or(name);
6082 Ok(owned.iter().find(|(n, _)| n == name || n == bare).map(|(_, r)| from_vec(r.clone())))
6083 }
6084 }
6085
6086 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
6087 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
6088 (cols.into_iter().map(|c| c.name).collect(), rows)
6089 }
6090
6091 fn col(rows: &[Value], name: &str) -> Vec<Value> {
6092 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
6093 }
6094
6095 fn shop() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
6096 tables(vec![
6097 ("c", vec![
6098 json!({"id": 1, "name": "ann", "tags": ["a", "b"]}),
6099 json!({"id": 2, "name": "bob", "tags": []}),
6100 json!({"id": 3, "name": "cyd", "tags": null}),
6101 ]),
6102 ("o", vec![
6103 json!({"oid": 10, "cid": 1, "total": 5}),
6104 json!({"oid": 11, "cid": 1, "total": 7}),
6105 json!({"oid": 12, "cid": 2, "total": 9}),
6106 ]),
6107 ])
6108 }
6109
6110 #[test]
6111 fn a_correlated_scalar_subquery_sees_the_outer_row() {
6112 let t = shop();
6113 let (_, rows) = go(
6114 "SELECT c.name, (SELECT sum(o.total) FROM o WHERE o.cid = c.id) AS spent FROM c ORDER BY c.id",
6115 &t,
6116 );
6117 assert_eq!(col(&rows, "spent"), vec![json!(12), json!(9), Value::Null]);
6118 let e = run("SELECT (SELECT o.total FROM o WHERE o.cid = c.id) FROM c", &t).unwrap_err().to_string();
6120 assert!(e.contains("more than one row"), "{}", e);
6121 let e = run("SELECT (SELECT oid, total FROM o) FROM c", &t).unwrap_err().to_string();
6123 assert!(e.contains("exactly one column"), "{}", e);
6124 }
6125
6126 #[test]
6127 fn EXISTS_and_NOT_EXISTS_are_never_unknown() {
6128 let t = shop();
6129 let (_, rows) = go("SELECT c.name FROM c WHERE EXISTS (SELECT 1 FROM o WHERE o.cid = c.id) ORDER BY 1", &t);
6130 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6131 let (_, rows) = go("SELECT c.name FROM c WHERE NOT EXISTS (SELECT 1 FROM o WHERE o.cid = c.id)", &t);
6132 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6133 }
6134
6135 #[test]
6136 fn ARRAY_of_a_subquery_and_array_to_string_compose_like_psql_dp() {
6137 let t = shop();
6138 let (_, rows) = go(
6139 "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",
6140 &t,
6141 );
6142 assert_eq!(col(&rows, "totals"), vec![json!("5,7"), json!("9"), json!("")]);
6144 let (_, rows) = go("SELECT array_length(ARRAY(SELECT oid FROM o), 1) AS n FROM c WHERE c.id = 1", &t);
6145 assert_eq!(col(&rows, "n"), vec![json!(3)]);
6146 }
6147
6148 #[test]
6149 fn ANY_ALL_and_IN_over_arrays_and_subqueries() {
6150 let t = shop();
6151 let (_, rows) = go("SELECT c.name FROM c WHERE 'a' = ANY (c.tags) ORDER BY 1", &t);
6152 assert_eq!(col(&rows, "name"), vec![json!("ann")]);
6153 let (_, rows) = go("SELECT c.name FROM c WHERE 'zz' = ANY (c.tags)", &t);
6155 assert!(rows.is_empty());
6156 let (_, rows) = go("SELECT c.name FROM c WHERE c.id = ANY (SELECT o.cid FROM o) ORDER BY 1", &t);
6157 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6158 let (_, rows) = go("SELECT c.name FROM c WHERE c.id <> ALL (SELECT o.cid FROM o)", &t);
6159 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6160 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);
6161 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6162 let (_, rows) = go("SELECT c.name FROM c WHERE c.id NOT IN (SELECT o.cid FROM o)", &t);
6163 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6164 let (_, rows) = go("SELECT c.tags[2] AS second FROM c WHERE c.id = 1", &t);
6166 assert_eq!(col(&rows, "second"), vec![json!("b")]);
6167 }
6168
6169 #[test]
6170 fn set_operations_combine_arms_and_sort_the_whole() {
6171 let t = shop();
6172 let (names, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1", &t);
6173 assert_eq!(names, vec!["k"], "column names come from the first arm");
6174 assert_eq!(col(&rows, "k"), vec![json!(1), json!(1), json!(1), json!(2), json!(2), json!(3)]);
6175 let (_, rows) = go("SELECT c.id AS k FROM c UNION SELECT o.cid FROM o ORDER BY 1", &t);
6176 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2), json!(3)]);
6177 let (_, rows) = go("SELECT c.id AS k FROM c INTERSECT SELECT o.cid FROM o ORDER BY 1", &t);
6178 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2)]);
6179 let (_, rows) = go("SELECT c.id AS k FROM c EXCEPT SELECT o.cid FROM o", &t);
6180 assert_eq!(col(&rows, "k"), vec![json!(3)]);
6181 let (_, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1 DESC LIMIT 2", &t);
6182 assert_eq!(col(&rows, "k"), vec![json!(3), json!(2)]);
6183 let e = run("SELECT c.id FROM c UNION SELECT o.oid, o.cid FROM o", &t).unwrap_err().to_string();
6184 assert!(e.contains("same number of columns"), "{}", e);
6185 }
6186
6187 #[test]
6188 fn a_derived_table_is_a_relation_and_LATERAL_sees_its_left() {
6189 let t = shop();
6190 let (_, rows) = go(
6191 "SELECT tt.who FROM (SELECT c.name AS who FROM c WHERE c.id < 3) AS tt ORDER BY 1",
6192 &t,
6193 );
6194 assert_eq!(col(&rows, "who"), vec![json!("ann"), json!("bob")]);
6195 let (_, rows) = go("SELECT tt.x FROM (SELECT c.name FROM c WHERE c.id = 1) AS tt(x)", &t);
6197 assert_eq!(col(&rows, "x"), vec![json!("ann")]);
6198 let (_, rows) = go(
6200 "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\"",
6201 &t,
6202 );
6203 assert_eq!(col(&rows, "Name"), vec![json!("ann"), json!("bob"), json!("cyd")]);
6204 assert_eq!(col(&rows, "Orders"), vec![json!(2), json!(1), json!(0)]);
6205 }
6206
6207 #[test]
6208 fn table_functions_generate_series_and_unnest() {
6209 let t = shop();
6210 let (_, rows) = go("SELECT s.generate_series AS n FROM generate_series(1, 3) s", &t);
6211 assert_eq!(col(&rows, "n"), vec![json!(1), json!(2), json!(3)]);
6212 let (_, rows) = go("SELECT x FROM pg_catalog.unnest(ARRAY['p', 'q']) AS t(x)", &t);
6213 assert_eq!(col(&rows, "x"), vec![json!("p"), json!("q")]);
6214 let (_, rows) = go(
6216 "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",
6217 &t,
6218 );
6219 assert_eq!(col(&rows, "tags"), vec![json!("a, b"), json!(""), json!("")]);
6220 let e = run("SELECT 1 FROM nosuchfn(1) f", &t).unwrap_err().to_string();
6221 assert!(e.contains("table function nosuchfn()"), "{}", e);
6222 }
6223
6224 #[test]
6225 fn aggregates_without_GROUP_BY_collapse_to_one_row() {
6226 let t = shop();
6227 let (names, rows) = go(
6228 "SELECT count(*), count(c.tags) AS tagged, min(c.name), max(c.name) AS hi, string_agg(c.name, '|') AS all FROM c",
6229 &t,
6230 );
6231 assert_eq!(names, vec!["count", "tagged", "min", "hi", "all"]);
6232 assert_eq!(rows.len(), 1);
6233 assert_eq!(rows[0]["count"], json!(3));
6234 assert_eq!(rows[0]["tagged"], json!(2), "count(x) skips NULL");
6235 assert_eq!(rows[0]["min"], json!("ann"));
6236 assert_eq!(rows[0]["hi"], json!("cyd"));
6237 assert_eq!(rows[0]["all"], json!("ann|bob|cyd"));
6238 let (_, rows) = go("SELECT count(*) AS n, sum(o.total) AS s FROM o WHERE o.total > 100", &t);
6240 assert_eq!(rows[0]["n"], json!(0));
6241 assert_eq!(rows[0]["s"], Value::Null);
6242 let (_, rows) = go("SELECT sum(o.total) / count(*) AS avg_total, avg(o.total) AS a FROM o", &t);
6245 assert_eq!(rows[0]["avg_total"], json!(7));
6246 assert_eq!(rows[0]["a"], json!(7));
6247 let e = run("SELECT c.name, count(*) FROM c", &t).unwrap_err().to_string();
6248 assert!(e.contains("must appear in the GROUP BY clause"), "{}", e);
6249 }
6250
6251 #[test]
6252 fn IS_DISTINCT_FROM_is_null_safe() {
6253 let t = shop();
6254 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS DISTINCT FROM NULL ORDER BY 1", &t);
6255 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6256 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS NOT DISTINCT FROM NULL", &t);
6257 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6258 }
6259
6260 #[test]
6261 fn THE_dT_QUERY_RUNS_over_a_catalogue_fixture() {
6262 let t = tables(vec![
6264 ("pg_namespace", vec![
6265 json!({"oid": 11, "nspname": "pg_catalog"}),
6266 json!({"oid": 2200, "nspname": "public"}),
6267 ]),
6268 ("pg_type", vec![
6269 json!({"oid": 25, "typname": "text", "typnamespace": 11, "typrelid": 0, "typelem": 0, "typarray": 1009}),
6270 json!({"oid": 1009, "typname": "_text", "typnamespace": 11, "typrelid": 0, "typelem": 25, "typarray": 0}),
6271 json!({"oid": 70000, "typname": "mood", "typnamespace": 2200, "typrelid": 0, "typelem": 0, "typarray": 70001}),
6272 json!({"oid": 70001, "typname": "_mood", "typnamespace": 2200, "typrelid": 0, "typelem": 70000, "typarray": 0}),
6273 ]),
6274 ("pg_class", vec![]),
6275 ]);
6276 let (_, rows) = go(
6277 r#"SELECT n.nspname as "Schema",
6278 pg_catalog.format_type(t.oid, NULL) AS "Name",
6279 pg_catalog.obj_description(t.oid, 'pg_type') as "Description"
6280 FROM pg_catalog.pg_type t
6281 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
6282 WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
6283 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
6284 AND n.nspname <> 'pg_catalog'
6285 AND n.nspname <> 'information_schema'
6286 AND pg_catalog.pg_type_is_visible(t.oid)
6287 ORDER BY 1, 2;"#,
6288 &t,
6289 );
6290 assert_eq!(rows.len(), 1, "{:?}", rows);
6293 assert_eq!(col(&rows, "Schema"), vec![json!("public")]);
6294 }
6295}
6296
6297#[cfg(test)]
6298mod collate_tests {
6299 use super::*;
6300 use serde_json::json;
6301
6302 #[test]
6303 fn COLLATE_is_consumed_because_it_cannot_change_the_answer() {
6304 for sql in [
6308 r#"SELECT a FROM t ORDER BY a COLLATE "C""#,
6309 r#"SELECT a COLLATE "C" FROM t"#,
6310 r#"SELECT a FROM t WHERE a COLLATE pg_catalog."C" = 'x'"#,
6311 ] {
6312 parse(sql).unwrap_or_else(|e| panic!("{} -> {}", sql, e));
6313 }
6314 assert!(parse("SELECT a FROM t ORDER BY a COLLATE").is_err());
6316 }
6317
6318 #[test]
6319 fn a_COLLATE_annotated_comparison_still_evaluates() {
6320 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
6321 Ok(Some(from_vec(vec![json!({"n": "b"}), json!({"n": "a"})])))
6322 };
6323 let (_, rows) = run(r#"SELECT n FROM pg_class ORDER BY n COLLATE "C""#, &t).unwrap();
6324 assert_eq!(rows[0]["n"], json!("a"), "the ORDER BY still sorts");
6325 }
6326}