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 self.expect_kw("SYSTEM")?;
1372 self.expect_kw("TIME")?;
1373 match self.next() {
1374 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Some(n as u64),
1375 Tok::Str(s) => Some(WallClock::parse(&s)?.as_marker()),
1376 other => bail!(
1377 "AS OF SYSTEM TIME takes a sequence number or a quoted datetime \
1378 (got {:?}). Bare integers stay sequence numbers — exact, never \
1379 garbage-collected; quote a datetime to travel by wall clock",
1380 other
1381 ),
1382 }
1383 } else {
1384 None
1385 };
1386 let valid_as_of = if self.peek().is_kw("VALID")
1402 && self.peek_at(1).is_kw("AS")
1403 && self.peek_at(2).is_kw("OF")
1404 {
1405 self.next();
1406 self.next();
1407 self.next();
1408 match self.next() {
1409 Tok::Str(s) => Some(s),
1410 other => bail!(
1411 "VALID AS OF takes a date string here (got {:?}). System time is a \
1412 sequence and application-time validity is a date — they are different \
1413 questions, so they take different arguments",
1414 other
1415 ),
1416 }
1417 } else {
1418 None
1419 };
1420
1421 let search = if self.peek().is_kw("SEARCH") && matches!(self.peek_at(1), Tok::Str(_)) {
1422 self.next();
1423 match self.next() {
1424 Tok::Str(s) => Some(s),
1425 other => bail!("SEARCH takes a string here, got {:?}", other),
1428 }
1429 } else {
1430 None
1431 };
1432
1433 fn names_a_thing(t: &Tok) -> bool {
1443 match t {
1444 Tok::Word { upper, .. } => !is_clause_keyword(upper),
1445 Tok::Quoted(_) => true,
1446 _ => false,
1447 }
1448 }
1449
1450 let (trace, trace_reverse) = if self.peek().is_kw("TRACE")
1451 && names_a_thing(self.peek_at(1))
1452 {
1453 self.next();
1454 let edge = match self.next() {
1455 Tok::Word { raw, .. } => raw,
1456 Tok::Quoted(s) => s,
1457 other => bail!("TRACE takes an edge type here, got {:?}", other),
1458 };
1459 let rev = if self.peek().is_kw("REVERSE") {
1460 self.next();
1461 true
1462 } else {
1463 false
1464 };
1465 (Some(edge), rev)
1466 } else {
1467 (None, false)
1468 };
1469
1470 let traverse = if self.peek().is_kw("TRAVERSE")
1471 && names_a_thing(self.peek_at(1))
1472 {
1473 self.next();
1474 match self.next() {
1475 Tok::Word { raw, .. } => Some(raw),
1476 Tok::Quoted(s) => Some(s),
1477 other => bail!("TRAVERSE takes a relation name here, got {:?}", other),
1478 }
1479 } else {
1480 None
1481 };
1482
1483 let (alias, col_aliases) = self.parse_table_alias()?;
1484 Ok(TableRef { name, alias, sub: None, args: None, col_aliases, lateral: false, as_of, valid_as_of, search, trace, trace_reverse, traverse })
1485 }
1486
1487 fn parse_table_alias(&mut self) -> Result<(Option<String>, Vec<String>)> {
1492 let alias = if self.eat_kw("AS") {
1493 match self.next() {
1494 Tok::Word { raw, .. } => Some(raw),
1495 Tok::Quoted(s) => Some(s),
1496 other => bail!("expected an alias after AS, got {:?}", other),
1497 }
1498 } else {
1499 match self.peek().clone() {
1500 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1501 self.pos += 1;
1502 Some(raw)
1503 }
1504 Tok::Quoted(s) => {
1505 self.pos += 1;
1506 Some(s)
1507 }
1508 _ => None,
1509 }
1510 };
1511 let mut col_aliases = vec![];
1512 if alias.is_some() && self.eat_punct('(') {
1513 loop {
1514 match self.next() {
1515 Tok::Word { raw, .. } => col_aliases.push(raw),
1516 Tok::Quoted(s) => col_aliases.push(s),
1517 other => bail!("expected a column alias, got {:?}", other),
1518 }
1519 if self.eat_punct(',') {
1520 continue;
1521 }
1522 self.expect_punct(')')?;
1523 break;
1524 }
1525 }
1526 Ok((alias, col_aliases))
1527 }
1528
1529 fn parse_query(&mut self) -> Result<Select> {
1536 let mut first = self.parse_select_body()?;
1537 loop {
1538 let op = if self.eat_kw("UNION") {
1539 SetOp::Union
1540 } else if self.eat_kw("INTERSECT") {
1541 SetOp::Intersect
1542 } else if self.eat_kw("EXCEPT") {
1543 SetOp::Except
1544 } else {
1545 break;
1546 };
1547 let all = self.eat_kw("ALL");
1548 if !all {
1549 let _ = self.eat_kw("DISTINCT");
1550 }
1551 let query = if self.eat_punct('(') {
1553 let q = self.parse_query()?;
1554 self.expect_punct(')')?;
1555 q
1556 } else {
1557 self.parse_select_body()?
1558 };
1559 first.set_ops.push(SetArm { op, all, query });
1560 }
1561 self.parse_query_tail(&mut first)?;
1562 Ok(first)
1563 }
1564
1565 fn parse_sort_list(&mut self) -> Result<Vec<OrderBy>> {
1569 let mut out = vec![];
1570 loop {
1571 let (ordinal, expr) = match self.peek().clone() {
1575 Tok::Num(n)
1576 if n.fract() == 0.0
1577 && n >= 1.0
1578 && !matches!(self.peek_at(1), Tok::Op(_)) =>
1579 {
1580 self.pos += 1;
1581 (Some(n as usize), None)
1582 }
1583 _ => (None, Some(self.parse_expr()?)),
1584 };
1585 let dir = if self.eat_kw("DESC") {
1586 Dir::Desc
1587 } else {
1588 let _ = self.eat_kw("ASC");
1589 Dir::Asc
1590 };
1591 let mut nulls_first = matches!(dir, Dir::Desc);
1593 if self.eat_kw("NULLS") {
1594 if self.eat_kw("FIRST") {
1595 nulls_first = true;
1596 } else if self.eat_kw("LAST") {
1597 nulls_first = false;
1598 } else {
1599 bail!("expected FIRST or LAST after NULLS, got {:?}", self.peek());
1600 }
1601 }
1602 out.push(OrderBy { ordinal, expr, dir, nulls_first });
1603 if self.eat_punct(',') {
1604 continue;
1605 }
1606 break;
1607 }
1608 Ok(out)
1609 }
1610
1611 fn parse_query_tail(&mut self, sel: &mut Select) -> Result<()> {
1612 let mut order_by = vec![];
1613 if self.eat_kw("ORDER") {
1614 self.expect_kw("BY")?;
1615 order_by = self.parse_sort_list()?;
1616 }
1617
1618 let mut limit = None;
1619 let mut offset = None;
1620 loop {
1622 if self.eat_kw("LIMIT") {
1623 if self.eat_kw("ALL") {
1624 limit = None;
1625 } else {
1626 limit = Some(self.parse_count("LIMIT")?);
1627 }
1628 continue;
1629 }
1630 if self.eat_kw("OFFSET") {
1631 offset = Some(self.parse_count("OFFSET")?);
1632 let _ = self.eat_kw("ROW") || self.eat_kw("ROWS");
1633 continue;
1634 }
1635 break;
1636 }
1637 sel.order_by = order_by;
1638 sel.limit = limit;
1639 sel.offset = offset;
1640 Ok(())
1641 }
1642
1643 fn parse_select_body(&mut self) -> Result<Select> {
1645 self.expect_kw("SELECT")?;
1646 let distinct = self.eat_kw("DISTINCT");
1647 if distinct && self.peek().is_kw("ON") {
1648 bail!("DISTINCT ON is not supported");
1649 }
1650 let _ = self.eat_kw("ALL");
1651
1652 let mut items = vec![];
1653 loop {
1654 let expr = self.parse_expr()?;
1655 let alias = if self.eat_kw("AS") {
1657 match self.next() {
1658 Tok::Word { raw, .. } => Some(raw),
1659 Tok::Quoted(s) => Some(s),
1660 other => bail!("expected an alias after AS, got {:?}", other),
1661 }
1662 } else {
1663 match self.peek().clone() {
1664 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1665 self.pos += 1;
1666 Some(raw)
1667 }
1668 Tok::Quoted(s) => {
1669 self.pos += 1;
1670 Some(s)
1671 }
1672 _ => None,
1673 }
1674 };
1675 items.push(SelectItem { expr, alias });
1676 if self.eat_punct(',') {
1677 continue;
1678 }
1679 break;
1680 }
1681
1682 let mut from = None;
1683 let mut joins = vec![];
1684 if self.eat_kw("FROM") {
1685 from = Some(self.parse_table_ref()?);
1686 loop {
1687 if self.eat_punct(',') {
1693 let table = self.parse_table_ref()?;
1694 joins.push(Join { kind: JoinKind::Cross, table, on: None });
1695 continue;
1696 }
1697 let kind = if self.peek().is_kw("JOIN") {
1698 self.pos += 1;
1699 JoinKind::Inner
1700 } else if self.peek().is_kw("INNER") && self.peek_at(1).is_kw("JOIN") {
1701 self.pos += 2;
1702 JoinKind::Inner
1703 } else if self.peek().is_kw("CROSS") && self.peek_at(1).is_kw("JOIN") {
1704 self.pos += 2;
1705 JoinKind::Cross
1706 } else if self.peek().is_kw("LEFT") {
1707 self.pos += 1;
1708 let _ = self.eat_kw("OUTER");
1709 self.expect_kw("JOIN")?;
1710 JoinKind::Left
1711 } else if self.peek().is_kw("RIGHT") {
1712 self.pos += 1;
1713 let _ = self.eat_kw("OUTER");
1714 self.expect_kw("JOIN")?;
1715 JoinKind::Right
1716 } else if self.peek().is_kw("FULL") {
1717 self.pos += 1;
1718 let _ = self.eat_kw("OUTER");
1719 self.expect_kw("JOIN")?;
1720 JoinKind::Full
1721 } else {
1722 break;
1723 };
1724 let table = self.parse_table_ref()?;
1725 let on = if self.eat_kw("ON") {
1726 Some(self.parse_expr()?)
1727 } else if self.peek().is_kw("USING") {
1728 bail!("JOIN ... USING is not supported — write ON a.col = b.col");
1729 } else {
1730 None
1731 };
1732 if on.is_none() && !matches!(kind, JoinKind::Cross) {
1733 bail!("a {:?} JOIN needs an ON clause", kind);
1734 }
1735 joins.push(Join { kind, table, on });
1736 }
1737 }
1738
1739 let where_ = if self.eat_kw("WHERE") {
1740 Some(self.parse_expr()?)
1741 } else {
1742 None
1743 };
1744
1745 let mut group_by = vec![];
1746 if self.eat_kw("GROUP") {
1747 self.expect_kw("BY")?;
1748 if self.eat_kw("ALL") || self.eat_kw("DISTINCT") {
1749 bail!("GROUP BY ALL / DISTINCT is not supported — list the keys");
1750 }
1751 loop {
1752 if self.peek().is_kw("ROLLUP")
1753 || self.peek().is_kw("CUBE")
1754 || self.peek().is_kw("GROUPING")
1755 {
1756 bail!("GROUP BY ROLLUP / CUBE / GROUPING SETS is not supported");
1757 }
1758 group_by.push(self.parse_expr()?);
1759 if self.eat_punct(',') {
1760 continue;
1761 }
1762 break;
1763 }
1764 }
1765
1766 let having = if self.eat_kw("HAVING") {
1767 Some(self.parse_expr()?)
1768 } else {
1769 None
1770 };
1771 if having.is_some() && group_by.is_empty() && !items.iter().any(|i| has_aggregate(&i.expr))
1772 {
1773 bail!("HAVING needs a GROUP BY or an aggregate — it filters groups, not rows; \
1774 use WHERE to filter rows");
1775 }
1776
1777 Ok(Select {
1778 distinct,
1779 items,
1780 from,
1781 joins,
1782 where_,
1783 group_by,
1784 having,
1785 order_by: vec![],
1786 limit: None,
1787 offset: None,
1788 set_ops: vec![],
1789 })
1790 }
1791
1792 fn parse_count(&mut self, what: &str) -> Result<usize> {
1793 match self.next() {
1794 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Ok(n as usize),
1795 other => bail!("{} expects a non-negative integer, got {:?}", what, other),
1796 }
1797 }
1798}
1799
1800fn is_clause_keyword(upper: &str) -> bool {
1805 matches!(
1806 upper,
1807 "FROM" | "WHERE" | "GROUP" | "HAVING" | "ORDER" | "LIMIT" | "OFFSET"
1808 | "JOIN" | "LEFT" | "RIGHT" | "FULL" | "INNER" | "CROSS" | "OUTER"
1809 | "ON" | "USING" | "AND" | "OR" | "AS" | "UNION" | "INTERSECT"
1810 | "EXCEPT" | "FETCH" | "FOR" | "WINDOW" | "RETURNING" | "INTO"
1811 | "ASC" | "DESC" | "NULLS" | "IS" | "IN" | "NOT" | "LIKE" | "ILIKE"
1812 | "BETWEEN" | "THEN" | "WHEN" | "ELSE" | "END" | "CASE" | "DISTINCT"
1813 | "SELECT" | "WITH" | "ALL"
1814 )
1815}
1816
1817pub fn parse(sql: &str) -> Result<Select> {
1819 let toks = lex(sql)?;
1820 let mut p = Parser { toks, pos: 0 };
1821 let sel = if matches!(p.peek(), Tok::Punct('(')) && p.peek_at(1).is_kw("SELECT") {
1823 p.pos += 1;
1824 let mut first = p.parse_query()?;
1825 p.expect_punct(')')?;
1826 loop {
1828 let op = if p.eat_kw("UNION") {
1829 SetOp::Union
1830 } else if p.eat_kw("INTERSECT") {
1831 SetOp::Intersect
1832 } else if p.eat_kw("EXCEPT") {
1833 SetOp::Except
1834 } else {
1835 break;
1836 };
1837 let all = p.eat_kw("ALL");
1838 if !all {
1839 let _ = p.eat_kw("DISTINCT");
1840 }
1841 let query = if p.eat_punct('(') {
1842 let q = p.parse_query()?;
1843 p.expect_punct(')')?;
1844 q
1845 } else {
1846 p.parse_select_body()?
1847 };
1848 first.set_ops.push(SetArm { op, all, query });
1849 }
1850 p.parse_query_tail(&mut first)?;
1851 first
1852 } else {
1853 p.parse_query()?
1854 };
1855 let _ = p.eat_punct(';');
1856 if !matches!(p.peek(), Tok::Eof) {
1857 bail!("unexpected trailing tokens: {:?}", p.peek());
1858 }
1859 Ok(sel)
1860}
1861
1862pub struct Bound<'a> {
1875 pub parts: Vec<(String, Option<&'a Value>)>,
1876 pub ctx: EvalCtx<'a>,
1878}
1879
1880#[derive(Clone, Copy, Default)]
1893pub struct EvalCtx<'a> {
1894 pub resolver: Option<&'a Resolver<'a>>,
1895 pub outer: Option<&'a Bound<'a>>,
1896}
1897
1898impl<'a> Bound<'a> {
1899 pub fn new(parts: Vec<(String, Option<&'a Value>)>) -> Self {
1901 Bound { parts, ctx: EvalCtx::default() }
1902 }
1903
1904 fn column(&self, qual: Option<&str>, name: &str) -> Value {
1911 match qual {
1912 Some(q) => {
1913 for (binding, row) in &self.parts {
1914 if binding.eq_ignore_ascii_case(q) {
1915 return row
1916 .and_then(|r| r.get(name))
1917 .cloned()
1918 .unwrap_or(Value::Null);
1919 }
1920 }
1921 match self.ctx.outer {
1922 Some(o) if o.has_binding(q) => o.column(qual, name),
1923 _ => Value::Null,
1924 }
1925 }
1926 None => {
1927 for (_, row) in &self.parts {
1928 if let Some(v) = row.and_then(|r| r.get(name)) {
1929 return v.clone();
1930 }
1931 }
1932 match self.ctx.outer {
1933 Some(o) => o.column(None, name),
1934 None => Value::Null,
1935 }
1936 }
1937 }
1938 }
1939
1940 fn has_binding(&self, qual: &str) -> bool {
1945 self.parts.iter().any(|(b, _)| b.eq_ignore_ascii_case(qual))
1946 || self.ctx.outer.is_some_and(|o| o.has_binding(qual))
1947 }
1948
1949 fn flatten(&self) -> Vec<(String, Value)> {
1951 let mut out = vec![];
1952 for (_, row) in &self.parts {
1953 if let Some(Value::Object(m)) = row {
1954 for (k, v) in m {
1955 out.push((k.clone(), v.clone()));
1956 }
1957 }
1958 }
1959 out
1960 }
1961
1962 fn flatten_binding(&self, qual: &str) -> Vec<(String, Value)> {
1963 let mut out = vec![];
1964 for (binding, row) in &self.parts {
1965 if binding.eq_ignore_ascii_case(qual) {
1966 if let Some(Value::Object(m)) = row {
1967 for (k, v) in m {
1968 out.push((k.clone(), v.clone()));
1969 }
1970 }
1971 }
1972 }
1973 out
1974 }
1975}
1976
1977type Truth = Option<bool>;
1985
1986fn truthy(v: &Value) -> Truth {
1987 match v {
1988 Value::Null => None,
1989 Value::Bool(b) => Some(*b),
1990 _ => None,
1994 }
1995}
1996
1997fn cmp_values(a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
2003 use std::cmp::Ordering;
2004 match (a, b) {
2005 (Value::Null, _) | (_, Value::Null) => None,
2006 (Value::Number(x), Value::Number(y)) => {
2007 x.as_f64().partial_cmp(&y.as_f64())
2008 }
2009 (Value::String(x), Value::String(y)) => Some(x.cmp(y)),
2010 (Value::Bool(x), Value::Bool(y)) => Some(x.cmp(y)),
2011 (Value::Number(x), Value::String(y)) => match y.parse::<f64>() {
2014 Ok(n) => x.as_f64().partial_cmp(&Some(n)),
2015 Err(_) => Some(as_text(a).cmp(&as_text(b))),
2016 },
2017 (Value::String(x), Value::Number(y)) => match x.parse::<f64>() {
2018 Ok(n) => Some(n).partial_cmp(&y.as_f64()),
2019 Err(_) => Some(as_text(a).cmp(&as_text(b))),
2020 },
2021 _ => {
2022 let (x, y) = (as_text(a), as_text(b));
2023 if x == y { Some(Ordering::Equal) } else { Some(x.cmp(&y)) }
2024 }
2025 }
2026}
2027
2028fn as_text(v: &Value) -> String {
2030 match v {
2031 Value::String(s) => s.clone(),
2032 Value::Null => String::new(),
2033 Value::Bool(b) => (if *b { "t" } else { "f" }).to_string(),
2034 Value::Array(items) => {
2037 let inner: Vec<String> = items
2038 .iter()
2039 .map(|i| match i {
2040 Value::Null => "NULL".to_string(),
2041 Value::String(s) if s.is_empty()
2042 || s.chars().any(|c| c.is_whitespace() || matches!(c, ',' | '{' | '}' | '"' | '\\')) =>
2043 {
2044 format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
2045 }
2046 other => as_text(other),
2047 })
2048 .collect();
2049 format!("{{{}}}", inner.join(","))
2050 }
2051 other => other.to_string(),
2052 }
2053}
2054
2055fn num(v: &Value) -> Option<f64> {
2056 match v {
2057 Value::Number(n) => n.as_f64(),
2058 Value::String(s) => s.parse().ok(),
2059 Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
2060 _ => None,
2061 }
2062}
2063
2064fn from_f64(f: f64) -> Value {
2078 if f.is_finite() && f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
2079 return Value::Number((f as i64).into());
2080 }
2081 serde_json::Number::from_f64(f).map(Value::Number).unwrap_or(Value::Null)
2082}
2083
2084pub fn eval(e: &Expr, row: &Bound) -> Result<Value> {
2086 Ok(match e {
2087 Expr::Literal(v) => v.clone(),
2088
2089 Expr::Column { qual, name } => {
2090 if let Some(q) = qual {
2094 if !row.has_binding(q) {
2095 bail!("no table or alias named {:?} in this query", q);
2096 }
2097 }
2098 row.column(qual.as_deref(), name)
2099 }
2100
2101 Expr::Cast { expr, .. } => eval(expr, row)?,
2102
2103 Expr::Star | Expr::QualifiedStar(_) => {
2104 bail!("`*` is only valid in a select list or as count(*)")
2105 }
2106
2107 Expr::Unary { op, expr } => {
2108 let v = eval(expr, row)?;
2109 match op.as_str() {
2110 "NOT" => match truthy(&v) {
2111 None => Value::Null,
2113 Some(b) => Value::Bool(!b),
2114 },
2115 "-" => match num(&v) {
2116 Some(n) => from_f64(-n),
2117 None => Value::Null,
2118 },
2119 other => bail!("unsupported unary operator {:?}", other),
2120 }
2121 }
2122
2123 Expr::Binary { op, left, right } => {
2124 if op == "AND" {
2129 let l = truthy(&eval(left, row)?);
2130 if l == Some(false) {
2131 return Ok(Value::Bool(false));
2132 }
2133 let r = truthy(&eval(right, row)?);
2134 return Ok(match (l, r) {
2135 (_, Some(false)) => Value::Bool(false),
2136 (Some(true), Some(true)) => Value::Bool(true),
2137 _ => Value::Null,
2138 });
2139 }
2140 if op == "OR" {
2141 let l = truthy(&eval(left, row)?);
2142 if l == Some(true) {
2143 return Ok(Value::Bool(true));
2144 }
2145 let r = truthy(&eval(right, row)?);
2146 return Ok(match (l, r) {
2147 (_, Some(true)) => Value::Bool(true),
2148 (Some(false), Some(false)) => Value::Bool(false),
2149 _ => Value::Null,
2150 });
2151 }
2152
2153 let l = eval(left, row)?;
2154 let r = eval(right, row)?;
2155 apply_op(op, l, r)?
2156 }
2157
2158 Expr::IsNull { expr, negated } => {
2159 let v = eval(expr, row)?;
2160 Value::Bool(v.is_null() != *negated)
2163 }
2164
2165 Expr::InList { expr, list, negated } => {
2166 let v = eval(expr, row)?;
2167 if v.is_null() {
2168 return Ok(Value::Null);
2169 }
2170 let mut items = Vec::with_capacity(list.len());
2171 for item in list {
2172 items.push(eval(item, row)?);
2173 }
2174 in_values(&v, &items, *negated)?
2175 }
2176
2177 Expr::Subquery(q) => {
2179 let (cols, rows) = run_sub(q, row)?;
2180 if cols.len() != 1 {
2181 bail!("a subquery used as an expression must return exactly one \
2182 column, this one returns {}", cols.len());
2183 }
2184 match rows.len() {
2185 0 => Value::Null,
2186 1 => rows[0].get(&cols[0].key).cloned().unwrap_or(Value::Null),
2187 n => bail!("more than one row returned by a subquery used as an \
2188 expression ({} rows)", n),
2189 }
2190 }
2191 Expr::Exists { query, negated } => {
2192 let (_, rows) = run_sub(query, row)?;
2193 Value::Bool(!rows.is_empty() != *negated)
2194 }
2195 Expr::ArrayQuery(q) => Value::Array(first_column(q, row)?),
2196 Expr::InSubquery { expr, query, negated } => {
2197 let v = eval(expr, row)?;
2198 if v.is_null() {
2199 return Ok(Value::Null);
2200 }
2201 let items = first_column(query, row)?;
2202 in_values(&v, &items, *negated)?
2203 }
2204 Expr::Quantified { op, left, all, right } => {
2205 let l = eval(left, row)?;
2206 let r = eval(right, row)?;
2207 let items = match r {
2208 Value::Null => return Ok(Value::Null),
2209 Value::Array(items) => items,
2210 other => bail!(
2211 "{} requires an array or a subquery on its right side, got {}",
2212 if *all { "ALL" } else { "ANY" },
2213 as_text(&other)
2214 ),
2215 };
2216 let mut saw_true = false;
2220 let mut saw_false = false;
2221 let mut saw_null = false;
2222 for item in items {
2223 match truthy(&apply_op(op, l.clone(), item)?) {
2224 Some(true) => saw_true = true,
2225 Some(false) => saw_false = true,
2226 None => saw_null = true,
2227 }
2228 }
2229 if *all {
2230 if saw_false {
2231 Value::Bool(false)
2232 } else if saw_null {
2233 Value::Null
2234 } else {
2235 Value::Bool(true)
2236 }
2237 } else if saw_true {
2238 Value::Bool(true)
2239 } else if saw_null {
2240 Value::Null
2241 } else {
2242 Value::Bool(false)
2243 }
2244 }
2245 Expr::Index { expr, index } => {
2246 let arr = eval(expr, row)?;
2247 let i = eval(index, row)?;
2248 match (arr, num(&i)) {
2249 (Value::Array(items), Some(n)) if n >= 1.0 => {
2250 items.get(n as usize - 1).cloned().unwrap_or(Value::Null)
2251 }
2252 _ => Value::Null,
2253 }
2254 }
2255 Expr::ArrayLit(items) => {
2256 let mut out = Vec::with_capacity(items.len());
2257 for i in items {
2258 out.push(eval(i, row)?);
2259 }
2260 Value::Array(out)
2261 }
2262
2263 Expr::Case { operand, whens, else_ } => {
2264 let subject = match operand {
2265 Some(o) => Some(eval(o, row)?),
2266 None => None,
2267 };
2268 for (cond, then) in whens {
2269 let hit = match &subject {
2270 Some(sv) => {
2272 let cv = eval(cond, row)?;
2273 matches!(cmp_values(sv, &cv), Some(std::cmp::Ordering::Equal))
2274 }
2275 None => truthy(&eval(cond, row)?) == Some(true),
2278 };
2279 if hit {
2280 return eval(then, row);
2281 }
2282 }
2283 match else_ {
2284 Some(e) => eval(e, row)?,
2285 None => Value::Null,
2288 }
2289 }
2290
2291 Expr::Func { name, args } => eval_func(name, args, row)?,
2292
2293 Expr::Agg { name, .. } => bail!(
2299 "{}() is an aggregate and has no value for one row — it is reduced \
2300 over a GROUP. Reaching this point is an engine bug, not a problem \
2301 with the query",
2302 name
2303 ),
2304 })
2305}
2306
2307fn in_values(v: &Value, items: &[Value], negated: bool) -> Result<Value> {
2309 let mut any_null = false;
2310 let mut found = false;
2311 for iv in items {
2312 if iv.is_null() {
2313 any_null = true;
2314 continue;
2315 }
2316 if matches!(cmp_values(v, iv), Some(std::cmp::Ordering::Equal)) {
2317 found = true;
2318 break;
2319 }
2320 }
2321 Ok(if found {
2325 Value::Bool(!negated)
2326 } else if any_null {
2327 Value::Null
2328 } else {
2329 Value::Bool(negated)
2330 })
2331}
2332
2333fn run_sub(q: &Select, row: &Bound) -> Result<(Vec<OutCol>, Vec<Value>)> {
2341 let Some(resolve) = row.ctx.resolver else {
2342 bail!("a subquery cannot run here: this evaluation has no relation resolver");
2343 };
2344 let (cols, rows, _) = execute_inner(q, resolve, Opts::default(), Some(row))?;
2345 Ok((cols, rows))
2346}
2347
2348fn first_column(q: &Select, row: &Bound) -> Result<Vec<Value>> {
2351 let (cols, rows) = run_sub(q, row)?;
2352 let Some(first) = cols.first() else {
2353 bail!("the subquery returns no columns");
2354 };
2355 Ok(rows
2356 .into_iter()
2357 .map(|r| r.get(&first.key).cloned().unwrap_or(Value::Null))
2358 .collect())
2359}
2360
2361fn apply_op(op: &str, l: Value, r: Value) -> Result<Value> {
2365 let compare = |ord: fn(std::cmp::Ordering) -> bool| -> Value {
2367 match cmp_values(&l, &r) {
2368 None => Value::Null,
2369 Some(o) => Value::Bool(ord(o)),
2370 }
2371 };
2372
2373 Ok(match op {
2374 "IS DISTINCT FROM" | "IS NOT DISTINCT FROM" => {
2375 let distinct = match (l.is_null(), r.is_null()) {
2376 (true, true) => false,
2377 (true, false) | (false, true) => true,
2378 (false, false) => !matches!(cmp_values(&l, &r), Some(std::cmp::Ordering::Equal)),
2379 };
2380 Value::Bool(distinct != op.starts_with("IS NOT"))
2381 }
2382 "=" => compare(|o| o.is_eq()),
2383 "!=" | "<>" => compare(|o| o.is_ne()),
2384 "<" => compare(|o| o.is_lt()),
2385 "<=" => compare(|o| o.is_le()),
2386 ">" => compare(|o| o.is_gt()),
2387 ">=" => compare(|o| o.is_ge()),
2388
2389 "~" | "~*" | "!~" | "!~*" => {
2390 if l.is_null() || r.is_null() {
2391 Value::Null
2392 } else {
2393 let pat = as_text(&r);
2394 if let Some(why) = crate::nql::regex_error_pub(&pat) {
2395 bail!(
2396 "{} — in {:?}. The supported subset is ^ $ . | ( ) \
2397 [ ] * + ? and literal text",
2398 why, pat
2399 );
2400 }
2401 let hit = crate::nql::regex_match_pub(
2402 &as_text(&l), &pat, op.ends_with('*'));
2403 Value::Bool(hit != op.starts_with('!'))
2404 }
2405 }
2406
2407 _ if op.starts_with("LIKE") || op.starts_with("ILIKE")
2408 || op.starts_with("NOT LIKE") || op.starts_with("NOT ILIKE") => {
2409 if l.is_null() || r.is_null() {
2410 Value::Null
2411 } else {
2412 let (base, esc): (&str, Option<char>) =
2414 match op.split_once(" ESCAPE ") {
2415 Some((b, e)) => (b, e.chars().next()),
2416 None => (&op[..], None),
2417 };
2418 let ci = base.ends_with("ILIKE");
2419 let txt = as_text(&l);
2420 let pat = as_text(&r);
2421 let hit = match esc {
2422 Some(c) => crate::nql::like_match_escape_pub(&txt, &pat, ci, c),
2423 None => crate::nql::like_match_pub(&txt, &pat, ci),
2424 };
2425 Value::Bool(hit != base.starts_with("NOT"))
2426 }
2427 }
2428
2429 "||" => {
2431 if l.is_null() || r.is_null() {
2432 Value::Null
2433 } else {
2434 Value::String(format!("{}{}", as_text(&l), as_text(&r)))
2435 }
2436 }
2437
2438 "+" | "-" | "*" | "/" | "%" => match (num(&l), num(&r)) {
2439 (Some(a), Some(b)) => match op {
2440 "+" => from_f64(a + b),
2441 "-" => from_f64(a - b),
2442 "*" => from_f64(a * b),
2443 "/" if b == 0.0 => bail!("division by zero"),
2446 "/" => from_f64(a / b),
2447 "%" if b == 0.0 => bail!("division by zero"),
2448 "%" => from_f64(a % b),
2449 _ => unreachable!(),
2450 },
2451 _ => Value::Null,
2452 },
2453
2454 other => bail!("unsupported operator {:?}", other),
2455 })
2456}
2457
2458fn eval_func(name: &str, args: &[Expr], row: &Bound) -> Result<Value> {
2464 let arg = |i: usize| -> Result<Value> {
2467 match args.get(i) {
2468 Some(e) => eval(e, row),
2469 None => Ok(Value::Null),
2470 }
2471 };
2472
2473 Ok(match name {
2474 "pg_get_userbyid" | "current_user" | "session_user" | "user" => {
2478 Value::String("nedb".into())
2479 }
2480 "current_schema" => Value::String("public".into()),
2481 "current_database" | "current_catalog" => Value::String("nedb".into()),
2482 "version" => Value::String(crate::pgwire::version_string()),
2483
2484 "pg_table_is_visible" | "pg_type_is_visible" | "pg_function_is_visible"
2489 | "pg_opclass_is_visible" | "pg_conversion_is_visible" => Value::Bool(true),
2490
2491 "pg_encoding_to_char" => Value::String("UTF8".into()),
2493 "pg_get_expr" | "pg_get_indexdef" | "pg_get_constraintdef"
2494 | "pg_get_viewdef" | "pg_get_partkeydef" | "obj_description"
2495 | "col_description" | "shobj_description" => Value::Null,
2496
2497 "lower" => match arg(0)? {
2499 Value::Null => Value::Null,
2500 v => Value::String(as_text(&v).to_lowercase()),
2501 },
2502 "upper" => match arg(0)? {
2503 Value::Null => Value::Null,
2504 v => Value::String(as_text(&v).to_uppercase()),
2505 },
2506 "length" | "char_length" | "character_length" => match arg(0)? {
2507 Value::Null => Value::Null,
2508 v => from_f64(as_text(&v).chars().count() as f64),
2509 },
2510 "format_type" => match arg(0)? {
2511 Value::Null => Value::Null,
2512 v => Value::String(crate::pgcatalog::type_name_pub(
2513 num(&v).unwrap_or(25.0) as i32).to_string()),
2514 },
2515 "array_to_string" | "pg_catalog.array_to_string" => {
2516 match arg(0)? {
2520 Value::Array(items) => {
2521 let sep = as_text(&arg(1)?);
2522 Value::String(
2523 items.iter().map(as_text).collect::<Vec<_>>().join(&sep),
2524 )
2525 }
2526 _ => Value::Null,
2527 }
2528 }
2529 "quote_ident" => Value::String(as_text(&arg(0)?)),
2530 "quote_literal" => Value::String(format!("'{}'", as_text(&arg(0)?).replace('\'', "''"))),
2531 "format" => {
2535 let fmt = as_text(&arg(0)?);
2536 let mut out = String::new();
2537 let mut next = 1usize;
2538 let mut chars = fmt.chars().peekable();
2539 while let Some(c) = chars.next() {
2540 if c != '%' {
2541 out.push(c);
2542 continue;
2543 }
2544 match chars.next() {
2545 Some('%') => out.push('%'),
2546 Some(spec @ ('s' | 'I' | 'L')) => {
2547 let v = arg(next)?;
2548 next += 1;
2549 match (spec, &v) {
2550 ('L', Value::Null) => out.push_str("NULL"),
2551 ('L', v) => out.push_str(&format!("'{}'", as_text(v).replace('\'', "''"))),
2552 (_, v) => out.push_str(&as_text(v)),
2553 }
2554 }
2555 other => bail!("format(): unsupported conversion %{}", other.map(String::from).unwrap_or_default()),
2556 }
2557 }
2558 Value::String(out)
2559 }
2560
2561 "array_length" | "array_upper" | "cardinality" => match arg(0)? {
2565 Value::Array(items) if !items.is_empty() => from_f64(items.len() as f64),
2566 Value::Array(_) if name == "cardinality" => from_f64(0.0),
2567 _ => Value::Null,
2568 },
2569 "array_lower" => match arg(0)? {
2570 Value::Array(items) if !items.is_empty() => from_f64(1.0),
2571 _ => Value::Null,
2572 },
2573
2574 "pg_table_size" | "pg_total_relation_size" | "pg_relation_size"
2581 | "pg_indexes_size" | "pg_database_size" => Value::Null,
2582 "pg_size_pretty" => match num(&arg(0)?) {
2583 None => Value::Null,
2584 Some(n) => {
2585 let units = ["bytes", "kB", "MB", "GB", "TB", "PB"];
2586 let mut v = n;
2587 let mut u = 0usize;
2588 while v.abs() >= 10240.0 && u + 1 < units.len() {
2589 v /= 1024.0;
2590 u += 1;
2591 }
2592 Value::String(format!("{} {}", v.round() as i64, units[u]))
2593 }
2594 },
2595
2596 "pg_get_triggerdef" | "pg_get_ruledef" | "pg_get_statisticsobjdef"
2602 | "pg_get_statisticsobjdef_columns" | "pg_get_function_result"
2603 | "pg_get_function_arguments" | "pg_get_function_identity_arguments"
2604 | "pg_get_functiondef" | "pg_get_serial_sequence" | "pg_get_partition_constraintdef"
2605 | "pg_relation_filepath" | "pg_tablespace_location" => Value::Null,
2606 "pg_relation_is_publishable" => Value::Bool(true),
2607 "pg_statistics_obj_is_visible" | "pg_opfamily_is_visible" | "pg_collation_is_visible"
2608 | "pg_ts_config_is_visible" | "pg_ts_dict_is_visible" | "pg_ts_parser_is_visible"
2609 | "pg_ts_template_is_visible" | "has_table_privilege" | "has_schema_privilege"
2610 | "has_database_privilege" | "pg_has_role" => Value::Bool(true),
2611 "current_setting" => match arg(0)? {
2614 Value::Null => Value::Null,
2615 v => match as_text(&v).to_lowercase().as_str() {
2616 "server_version" => Value::String(crate::pgwire::version_string()),
2617 "server_encoding" | "client_encoding" => Value::String("UTF8".into()),
2618 "standard_conforming_strings" | "integer_datetimes" | "is_superuser" => {
2619 Value::String("on".into())
2620 }
2621 "timezone" | "log_timezone" => Value::String("UTC".into()),
2622 "search_path" => Value::String("\"$user\", public".into()),
2623 "intervalstyle" => Value::String("postgres".into()),
2624 "datestyle" => Value::String("ISO, MDY".into()),
2625 "session_authorization" => Value::String("nedb".into()),
2626 "application_name" | "default_transaction_read_only" => Value::String(String::new()),
2627 "transaction_isolation" | "default_transaction_isolation" => {
2628 Value::String("read committed".into())
2629 }
2630 "max_identifier_length" => Value::String("63".into()),
2631 other => {
2632 if truthy(&arg(1)?) == Some(true) {
2635 Value::Null
2636 } else {
2637 bail!("unrecognized configuration parameter \"{}\"", other)
2638 }
2639 }
2640 },
2641 },
2642 "pg_backend_pid" => from_f64(std::process::id() as f64),
2643 "pg_is_in_recovery" => Value::Bool(false),
2644 "txid_current" => from_f64(0.0),
2645 "now" | "current_timestamp" | "statement_timestamp" | "clock_timestamp" => {
2646 Value::String(now_iso())
2647 }
2648 "to_char" => match arg(0)? {
2649 Value::Null => Value::Null,
2650 v => Value::String(as_text(&v)),
2651 },
2652 "generate_series" | "unnest" => bail!(
2653 "{}() returns a set of rows — write it in FROM, not in the select list", name
2654 ),
2655
2656 "coalesce" => {
2658 let mut out = Value::Null;
2659 for a in args {
2660 let v = eval(a, row)?;
2661 if !v.is_null() {
2662 out = v;
2663 break;
2664 }
2665 }
2666 out
2667 }
2668 "nullif" => {
2669 let a = arg(0)?;
2670 let b = arg(1)?;
2671 if matches!(cmp_values(&a, &b), Some(std::cmp::Ordering::Equal)) {
2672 Value::Null
2673 } else {
2674 a
2675 }
2676 }
2677
2678 "int4" | "int8" | "int2" => match num(&arg(0)?) {
2680 Some(n) => from_f64(n.trunc()),
2681 None => Value::Null,
2682 },
2683 "text" => match arg(0)? {
2684 Value::Null => Value::Null,
2685 v => Value::String(as_text(&v)),
2686 },
2687
2688 other if is_aggregate(other) => bail!(
2689 "{}() is an aggregate, which is only meaningful over a whole result set — \
2690 it is evaluated by the executor, never per row",
2691 other
2692 ),
2693
2694 other => bail!(
2695 "the function {}() is not implemented. It is refused rather than \
2696 answered with NULL, because a NULL column reads as missing DATA \
2697 rather than a missing feature",
2698 other
2699 ),
2700 })
2701}
2702
2703fn now_iso() -> String {
2705 let secs = std::time::SystemTime::now()
2706 .duration_since(std::time::UNIX_EPOCH)
2707 .map(|d| d.as_secs())
2708 .unwrap_or(0);
2709 let days = (secs / 86_400) as i64;
2711 let rem = secs % 86_400;
2712 let z = days + 719_468;
2713 let era = z.div_euclid(146_097);
2714 let doe = z.rem_euclid(146_097);
2715 let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
2716 let y = yoe + era * 400;
2717 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2718 let mp = (5 * doy + 2) / 153;
2719 let d = doy - (153 * mp + 2) / 5 + 1;
2720 let m = if mp < 10 { mp + 3 } else { mp - 9 };
2721 let y = if m <= 2 { y + 1 } else { y };
2722 format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}+00", y, m, d, rem / 3600, (rem % 3600) / 60, rem % 60)
2723}
2724
2725const AGGREGATES: &[&str] = &[
2730 "count", "sum", "avg", "min", "max", "string_agg", "array_agg", "bool_and",
2731 "bool_or", "every",
2732];
2733
2734fn is_aggregate(name: &str) -> bool {
2735 AGGREGATES.iter().any(|a| a.eq_ignore_ascii_case(name))
2736}
2737
2738pub fn has_aggregate(e: &Expr) -> bool {
2741 match e {
2742 Expr::Agg { .. } => true,
2743 Expr::Func { args, .. } => args.iter().any(has_aggregate),
2744 Expr::Binary { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2745 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2746 has_aggregate(expr)
2747 }
2748 Expr::InList { expr, list, .. } => has_aggregate(expr) || list.iter().any(has_aggregate),
2749 Expr::Case { operand, whens, else_ } => {
2750 operand.as_deref().is_some_and(has_aggregate)
2751 || whens.iter().any(|(c, t)| has_aggregate(c) || has_aggregate(t))
2752 || else_.as_deref().is_some_and(has_aggregate)
2753 }
2754 Expr::Quantified { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2755 Expr::Index { expr, index } => has_aggregate(expr) || has_aggregate(index),
2756 Expr::ArrayLit(items) => items.iter().any(has_aggregate),
2757 Expr::InSubquery { expr, .. } => has_aggregate(expr),
2758 Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => false,
2759 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => false,
2760 }
2761}
2762
2763fn aggregate(
2770 name: &str,
2771 args: &[Expr],
2772 order_by: &[OrderBy],
2773 distinct: bool,
2774 rows: &[JoinedRow],
2775 ctx: EvalCtx,
2776) -> Result<Value> {
2777 let lname = name.to_lowercase();
2778
2779 let ordered: Vec<JoinedRow> = if order_by.is_empty() {
2782 rows.to_vec()
2783 } else {
2784 let mut keyed: Vec<(Vec<Value>, JoinedRow)> = Vec::with_capacity(rows.len());
2785 for r in rows {
2786 let b = bind(r, ctx);
2787 let mut key = vec![];
2788 for ob in order_by {
2789 match (&ob.expr, ob.ordinal) {
2792 (Some(e), _) => key.push(eval(e, &b)?),
2793 (None, Some(n)) => bail!(
2794 "ORDER BY {} inside an aggregate refers to a select-list \
2795 position, which an aggregate does not have — name the \
2796 column instead", n
2797 ),
2798 (None, None) => key.push(Value::Null),
2799 }
2800 }
2801 keyed.push((key, r.clone()));
2802 }
2803 keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, order_by));
2804 keyed.into_iter().map(|(_, r)| r).collect()
2805 };
2806 let rows: &[JoinedRow] = &ordered;
2807
2808 if lname == "count" && (args.is_empty() || matches!(args[0], Expr::Star)) {
2811 return Ok(from_f64(rows.len() as f64));
2812 }
2813 let Some(target) = args.first() else {
2814 bail!("{}() needs an argument", name);
2815 };
2816 let mut vals: Vec<Value> = Vec::with_capacity(rows.len());
2817 let mut all_vals: Vec<Value> = Vec::with_capacity(rows.len());
2818 for r in rows {
2819 let v = eval(target, &bind(r, ctx))?;
2820 if !v.is_null() {
2821 vals.push(v.clone());
2822 }
2823 all_vals.push(v);
2824 }
2825 if distinct {
2826 let mut seen: Vec<String> = vec![];
2827 vals.retain(|v| {
2828 let k = format!("{:?}", v);
2829 if seen.contains(&k) { false } else { seen.push(k); true }
2830 });
2831 let mut seen2: Vec<String> = vec![];
2832 all_vals.retain(|v| {
2833 let k = format!("{:?}", v);
2834 if seen2.contains(&k) { false } else { seen2.push(k); true }
2835 });
2836 }
2837 Ok(match lname.as_str() {
2838 "count" => from_f64(vals.len() as f64),
2839 "sum" | "avg" => {
2840 let nums: Vec<f64> = vals.iter().filter_map(num).collect();
2841 if nums.is_empty() {
2842 Value::Null
2843 } else if lname == "sum" {
2844 from_f64(nums.iter().sum())
2845 } else {
2846 from_f64(nums.iter().sum::<f64>() / nums.len() as f64)
2847 }
2848 }
2849 "min" | "max" => {
2850 let mut best: Option<Value> = None;
2851 for v in vals {
2852 best = Some(match best {
2853 None => v,
2854 Some(b) => {
2855 let take = match cmp_values(&v, &b) {
2856 Some(o) if lname == "min" => o.is_lt(),
2857 Some(o) => o.is_gt(),
2858 None => false,
2859 };
2860 if take { v } else { b }
2861 }
2862 });
2863 }
2864 best.unwrap_or(Value::Null)
2865 }
2866 "string_agg" => {
2867 if vals.is_empty() {
2868 Value::Null
2869 } else {
2870 let sep = match args.get(1) {
2873 Some(e) => as_text(&eval(e, &Bound { parts: vec![], ctx })?),
2874 None => String::new(),
2875 };
2876 Value::String(vals.iter().map(as_text).collect::<Vec<_>>().join(&sep))
2877 }
2878 }
2879 "array_agg" => {
2881 if all_vals.is_empty() { Value::Null } else { Value::Array(all_vals) }
2882 }
2883 "bool_and" | "every" => {
2884 if vals.is_empty() {
2885 Value::Null
2886 } else {
2887 Value::Bool(vals.iter().all(|v| truthy(v) == Some(true)))
2888 }
2889 }
2890 "bool_or" => {
2891 if vals.is_empty() {
2892 Value::Null
2893 } else {
2894 Value::Bool(vals.iter().any(|v| truthy(v) == Some(true)))
2895 }
2896 }
2897 _ => unreachable!("is_aggregate gates this"),
2898 })
2899}
2900
2901fn fold_aggregates(
2908 e: &Expr,
2909 rows: &[JoinedRow],
2910 ctx: EvalCtx,
2911 keys: &[Expr],
2912) -> Result<Expr> {
2913 let fold = |x: &Expr| fold_aggregates(x, rows, ctx, keys);
2914 Ok(match e {
2915 Expr::Agg { name, args, order_by, distinct } => {
2916 Expr::Literal(aggregate(name, args, order_by, *distinct, rows, ctx)?)
2917 }
2918 Expr::Func { name, args } => Expr::Func {
2919 name: name.clone(),
2920 args: args.iter().map(&fold).collect::<Result<_>>()?,
2921 },
2922 Expr::Column { .. } if keys.iter().any(|k| k == e) => e.clone(),
2928 Expr::Column { qual, name } => bail!(
2929 "column \"{}{}\" must appear in the GROUP BY clause or be used in an \
2930 aggregate function",
2931 qual.as_ref().map(|q| format!("{q}.")).unwrap_or_default(),
2932 name
2933 ),
2934 Expr::Binary { op, left, right } => Expr::Binary {
2935 op: op.clone(),
2936 left: Box::new(fold(left)?),
2937 right: Box::new(fold(right)?),
2938 },
2939 Expr::Unary { op, expr } => Expr::Unary {
2940 op: op.clone(),
2941 expr: Box::new(fold(expr)?),
2942 },
2943 Expr::Cast { expr, ty } => Expr::Cast {
2944 expr: Box::new(fold(expr)?),
2945 ty: ty.clone(),
2946 },
2947 Expr::IsNull { expr, negated } => Expr::IsNull {
2948 expr: Box::new(fold(expr)?),
2949 negated: *negated,
2950 },
2951 Expr::InList { expr, list, negated } => Expr::InList {
2952 expr: Box::new(fold(expr)?),
2953 list: list.iter().map(&fold).collect::<Result<_>>()?,
2954 negated: *negated,
2955 },
2956 Expr::Case { operand, whens, else_ } => Expr::Case {
2957 operand: match operand {
2958 Some(o) => Some(Box::new(fold(o)?)),
2959 None => None,
2960 },
2961 whens: whens
2962 .iter()
2963 .map(|(c, t)| Ok((fold(c)?, fold(t)?)))
2964 .collect::<Result<_>>()?,
2965 else_: match else_ {
2966 Some(x) => Some(Box::new(fold(x)?)),
2967 None => None,
2968 },
2969 },
2970 Expr::Quantified { op, left, all, right } => Expr::Quantified {
2971 op: op.clone(),
2972 left: Box::new(fold(left)?),
2973 all: *all,
2974 right: Box::new(fold(right)?),
2975 },
2976 Expr::Index { expr, index } => Expr::Index {
2977 expr: Box::new(fold(expr)?),
2978 index: Box::new(fold(index)?),
2979 },
2980 Expr::ArrayLit(items) => Expr::ArrayLit(
2981 items.iter().map(&fold).collect::<Result<_>>()?,
2982 ),
2983 Expr::InSubquery { expr, query, negated } => Expr::InSubquery {
2984 expr: Box::new(fold(expr)?),
2985 query: query.clone(),
2986 negated: *negated,
2987 },
2988 Expr::Star | Expr::QualifiedStar(_) => {
2991 bail!("`*` cannot be mixed with an aggregate outside count(*)")
2992 }
2993 Expr::Literal(_) | Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => {
2995 e.clone()
2996 }
2997 })
2998}
2999
3000fn validate_bindings(sel: &Select) -> Result<()> {
3016 let mut seen: Vec<String> = vec![];
3017 if let Some(f) = &sel.from {
3018 seen.push(f.binding());
3019 }
3020 for j in &sel.joins {
3021 seen.push(j.table.binding());
3022 }
3023 for (i, b) in seen.iter().enumerate() {
3024 if let Some(prev) = seen[..i].iter().find(|p| p.eq_ignore_ascii_case(b)) {
3025 bail!(
3026 "ambiguous relation binding: {:?} appears more than once; use \
3027 aliases (for example `FROM {} JOIN {} AS {}2 ...`)",
3028 prev, prev, prev, prev
3029 );
3030 }
3031 }
3032 Ok(())
3033}
3034
3035#[derive(Debug, Clone, PartialEq, Eq)]
3047pub struct OutCol {
3048 pub key: String,
3049 pub name: String,
3050}
3051
3052fn unique_key(taken: &[OutCol], name: &str) -> String {
3056 if !taken.iter().any(|c| c.key == name) {
3057 return name.to_string();
3058 }
3059 format!("{name}\u{1}{}", taken.len())
3060}
3061
3062type JoinedRow = Vec<(String, Option<Value>)>;
3064
3065fn bind<'a>(row: &'a JoinedRow, ctx: EvalCtx<'a>) -> Bound<'a> {
3066 Bound {
3067 parts: row.iter().map(|(b, v)| (b.clone(), v.as_ref())).collect(),
3068 ctx,
3069 }
3070}
3071
3072fn derived_name(e: &Expr) -> String {
3080 match e {
3081 Expr::Column { name, .. } => name.clone(),
3082 Expr::Func { name, .. } | Expr::Agg { name, .. } => name.clone(),
3083 Expr::Cast { expr, .. } => derived_name(expr),
3084 Expr::Case { .. } => "case".to_string(),
3085 Expr::ArrayQuery(_) | Expr::ArrayLit(_) => "array".to_string(),
3086 Expr::Exists { .. } => "exists".to_string(),
3087 Expr::Subquery(q) => q
3089 .items
3090 .first()
3091 .map(|i| i.alias.clone().unwrap_or_else(|| derived_name(&i.expr)))
3092 .unwrap_or_else(|| "?column?".to_string()),
3093 _ => "?column?".to_string(),
3094 }
3095}
3096
3097pub trait Relation {
3118 fn next_row(&mut self) -> Result<Option<Value>>;
3120
3121 fn size_hint(&self) -> Option<usize> {
3123 None
3124 }
3125}
3126
3127pub struct VecRelation {
3133 iter: std::vec::IntoIter<Value>,
3134 len: usize,
3135}
3136
3137impl Relation for VecRelation {
3138 fn next_row(&mut self) -> Result<Option<Value>> {
3139 Ok(self.iter.next())
3140 }
3141 fn size_hint(&self) -> Option<usize> {
3142 Some(self.len)
3143 }
3144}
3145
3146pub fn from_vec(rows: Vec<Value>) -> Box<dyn Relation> {
3148 let len = rows.len();
3149 Box::new(VecRelation { iter: rows.into_iter(), len })
3150}
3151
3152pub type Resolver<'r> = dyn Fn(&str) -> Result<Option<Box<dyn Relation>>> + 'r;
3158
3159pub fn execute(sel: &Select, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
3164 let (cols, rows, _) = execute_explain(sel, resolve, JoinExec::Auto)?;
3165 Ok((cols, rows))
3166}
3167
3168pub fn execute_explain(
3175 sel: &Select,
3176 resolve: &Resolver,
3177 exec: JoinExec,
3178) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3179 execute_with(sel, resolve, exec, true)
3180}
3181
3182#[derive(Debug, Clone, Copy)]
3187pub struct Opts {
3188 pub exec: JoinExec,
3189 pub pushdown: bool,
3190 pub fuse_filter: bool,
3194}
3195
3196impl Default for Opts {
3197 fn default() -> Self {
3198 Opts { exec: JoinExec::Auto, pushdown: true, fuse_filter: true }
3199 }
3200}
3201
3202impl Opts {
3203 pub fn exec(exec: JoinExec) -> Self {
3204 Opts { exec, ..Default::default() }
3205 }
3206}
3207
3208pub fn execute_with(
3215 sel: &Select,
3216 resolve: &Resolver,
3217 exec: JoinExec,
3218 pushdown: bool,
3219) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3220 execute_opts(sel, resolve, Opts { exec, pushdown, ..Default::default() })
3221}
3222
3223pub fn execute_opts(
3225 sel: &Select,
3226 resolve: &Resolver,
3227 opts: Opts,
3228) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3229 execute_inner(sel, resolve, opts, None)
3230}
3231
3232fn row_key(cols: &[OutCol], obj: &Map<String, Value>) -> String {
3234 cols.iter()
3235 .map(|c| format!("{:?}", obj.get(&c.key).unwrap_or(&Value::Null)))
3236 .collect::<Vec<_>>()
3237 .join("\u{1}")
3238}
3239
3240fn execute_set_ops<'a>(
3248 sel: &Select,
3249 resolve: &'a Resolver<'a>,
3250 opts: Opts,
3251 outer: Option<&'a Bound<'a>>,
3252) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3253 let ctx = EvalCtx { resolver: Some(resolve), outer };
3254 let mut head = sel.clone();
3255 head.set_ops.clear();
3256 head.order_by.clear();
3257 head.limit = None;
3258 head.offset = None;
3259 let (cols, rows, mut plan) = execute_inner(&head, resolve, opts, outer)?;
3260 let mut left: Vec<Map<String, Value>> = rows
3261 .into_iter()
3262 .map(|r| match r {
3263 Value::Object(m) => m,
3264 _ => Map::new(),
3265 })
3266 .collect();
3267
3268 for arm in &sel.set_ops {
3269 let (acols, arows, _) = execute_inner(&arm.query, resolve, opts, outer)?;
3270 let op_name = match arm.op {
3271 SetOp::Union => "UNION",
3272 SetOp::Intersect => "INTERSECT",
3273 SetOp::Except => "EXCEPT",
3274 };
3275 if acols.len() != cols.len() {
3276 bail!(
3277 "each {} query must have the same number of columns: {} vs {}",
3278 op_name, cols.len(), acols.len()
3279 );
3280 }
3281 let right: Vec<Map<String, Value>> = arows
3283 .into_iter()
3284 .map(|r| {
3285 let m = match r {
3286 Value::Object(m) => m,
3287 _ => Map::new(),
3288 };
3289 let mut out = Map::new();
3290 for (i, c) in cols.iter().enumerate() {
3291 out.insert(c.key.clone(), m.get(&acols[i].key).cloned().unwrap_or(Value::Null));
3292 }
3293 out
3294 })
3295 .collect();
3296 let (nl, nr) = (left.len(), right.len());
3297 let right_keys: std::collections::HashSet<String> =
3298 right.iter().map(|m| row_key(&cols, m)).collect();
3299 let mut combined: Vec<Map<String, Value>> = match arm.op {
3300 SetOp::Union => {
3301 left.extend(right);
3302 left
3303 }
3304 SetOp::Intersect => left.into_iter().filter(|m| right_keys.contains(&row_key(&cols, m))).collect(),
3305 SetOp::Except => left.into_iter().filter(|m| !right_keys.contains(&row_key(&cols, m))).collect(),
3306 };
3307 if !arm.all {
3308 let mut seen = std::collections::HashSet::new();
3309 combined.retain(|m| seen.insert(row_key(&cols, m)));
3310 }
3311 plan.notes.push(format!(
3312 "{}{}: {} + {} rows -> {} (each arm planned separately; only the first arm's plan is shown)",
3313 op_name, if arm.all { " ALL" } else { "" }, nl, nr, combined.len()
3314 ));
3315 left = combined;
3316 }
3317
3318 let projected: Vec<(Map<String, Value>, JoinedRow)> = left
3321 .into_iter()
3322 .map(|m| {
3323 let src: JoinedRow = vec![(String::new(), Some(Value::Object(m.clone())))];
3324 (m, src)
3325 })
3326 .collect();
3327 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3328 Ok((cols, out, plan))
3329}
3330
3331fn execute_inner<'a>(
3334 sel: &Select,
3335 resolve: &'a Resolver<'a>,
3336 opts: Opts,
3337 outer: Option<&'a Bound<'a>>,
3338) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3339 if !sel.set_ops.is_empty() {
3340 return execute_set_ops(sel, resolve, opts, outer);
3341 }
3342 let ctx = EvalCtx { resolver: Some(resolve), outer };
3343 let exec = opts.exec;
3344 let pushdown = opts.pushdown;
3345 let mut plan = Plan::default();
3346
3347 validate_bindings(sel)?;
3349
3350 let fuse = opts.fuse_filter && sel.where_.is_some() && !sel.joins.is_empty();
3377
3378 let budget: Option<usize> = match sel.limit {
3379 Some(lim)
3380 if sel.order_by.is_empty()
3381 && !sel.distinct
3382 && !sel.joins.is_empty()
3383 && (sel.where_.is_none() || fuse) =>
3384 {
3385 Some(lim.saturating_add(sel.offset.unwrap_or(0)))
3386 }
3387 _ => None,
3388 };
3389 plan.budget = budget;
3390
3391 let all_bindings: Vec<String> = sel
3397 .from
3398 .iter()
3399 .map(|t| t.binding())
3400 .chain(sel.joins.iter().map(|j| j.table.binding()))
3401 .collect();
3402 let nullable = crate::sqlpush::nullable_bindings(sel);
3403 let push = if pushdown {
3404 crate::sqlpush::plan(sel.where_.as_ref(), &all_bindings, &nullable)
3405 } else {
3406 Pushdown::default()
3407 };
3408 plan.refusals = push.refusals.clone();
3409
3410 let mut base_scan_at: Option<usize> = None;
3411 let mut base_prefilter_at: Option<usize> = None;
3412
3413 let mut left_src: Box<dyn LeftSource + 'a> = match &sel.from {
3421 None => {
3422 Box::new(VecLeft { rows: vec![vec![]], at: 0 })
3425 }
3426 Some(t) => {
3427 let rel = fetch(t, resolve, ctx)?;
3428 let binding = t.binding();
3429 base_scan_at = Some(plan.stages.len());
3434 plan.push(Stage::Scan {
3435 table: t.name.clone(),
3436 binding: binding.clone(),
3437 rows: 0,
3438 });
3439 let preds = push.for_binding(&binding).cloned().unwrap_or_default();
3440 if !preds.is_empty() {
3441 base_prefilter_at = Some(plan.stages.len());
3442 plan.push(Stage::Prefilter {
3443 binding: binding.clone(),
3444 predicates: preds.len(),
3445 in_rows: 0,
3446 out_rows: 0,
3447 });
3448 }
3449 Box::new(StreamLeft { rel, binding, preds, pulled: 0, kept: 0, ctx })
3450 }
3451 };
3452
3453 let mut left_bindings: Vec<String> = match &sel.from {
3459 None => vec![],
3460 Some(t) => vec![t.binding()],
3461 };
3462 let last = sel.joins.len().saturating_sub(1);
3463 let mut rows: Vec<JoinedRow> = vec![];
3464 let mut base_pulled: Option<usize> = None;
3465 let mut base_kept: Option<usize> = None;
3466
3467 for (ji, join) in sel.joins.iter().enumerate() {
3468 let is_last = ji == last;
3469 let rb = join.table.binding();
3470
3471 if join.table.lateral {
3475 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3476 let join_budget = if is_last { budget } else { None };
3477 let (out, removed, consumed, produced) = join_lateral(
3478 left_src.as_mut(), join, resolve, join_budget, post, ctx,
3479 )?;
3480 plan.push(Stage::Scan { table: join.table.name.clone(), binding: rb.clone(), rows: produced });
3481 plan.push(Stage::Join {
3482 kind: join.kind,
3483 table: join.table.name.clone(),
3484 binding: rb.clone(),
3485 strategy: Strategy::NestedLoop,
3486 keys: 0,
3487 left_rows: consumed,
3488 right_rows: produced,
3489 out_rows: out.len(),
3490 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3491 post_filter_removed: post.map(|_| removed),
3492 });
3493 plan.notes.push(format!("LATERAL {}: the subquery ran once per left row ({} times)", rb, consumed));
3494 left_bindings.push(rb);
3495 if ji == 0 {
3496 if let Some((pulled, kept)) = left_src.stats() {
3497 base_pulled = Some(pulled);
3498 base_kept = Some(kept);
3499 }
3500 }
3501 left_src = Box::new(VecLeft { rows: out, at: 0 });
3502 continue;
3503 }
3504
3505 let right_rel = fetch(&join.table, resolve, ctx)?;
3506 let right_all = drain(right_rel)?;
3507 plan.push(Stage::Scan {
3508 table: join.table.name.clone(),
3509 binding: rb.clone(),
3510 rows: right_all.len(),
3511 });
3512 let right_rows = prefilter(right_all, &rb, &push, &mut plan, ctx)?;
3513
3514 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3519 let join_budget = if is_last { budget } else { None };
3520
3521 let keys = sqljoin::hash_keys(join.on.as_ref(), &left_bindings, &rb);
3524 let left_hint = left_src.hint().unwrap_or(usize::MAX);
3525 let strategy = sqljoin::choose(exec, keys.len(), left_hint, right_rows.len());
3526
3527 let (out, removed, consumed) = match strategy {
3528 Strategy::NestedLoop => join_nested_loop(
3529 left_src.as_mut(), &left_bindings, join, &right_rows, &rb,
3530 join_budget, post, ctx,
3531 )?,
3532 Strategy::Hash => join_hash(
3533 left_src.as_mut(), &left_bindings, join, &right_rows, &rb, &keys,
3534 join_budget, post, ctx,
3535 )?,
3536 };
3537
3538 plan.push(Stage::Join {
3539 kind: join.kind,
3540 table: join.table.name.clone(),
3541 binding: rb.clone(),
3542 strategy,
3543 keys: keys.len(),
3544 left_rows: consumed,
3545 right_rows: right_rows.len(),
3546 out_rows: out.len(),
3547 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3548 post_filter_removed: post.map(|_| removed),
3549 });
3550 left_bindings.push(rb);
3551 if ji == 0 {
3553 if let Some((pulled, kept)) = left_src.stats() {
3554 base_pulled = Some(pulled);
3555 base_kept = Some(kept);
3556 }
3557 }
3558 rows = out;
3559 left_src = Box::new(VecLeft { rows: std::mem::take(&mut rows), at: 0 });
3561 }
3562
3563 rows = left_src.take_rows();
3566 if let Some(i) = base_scan_at {
3567 if let (Some(pulled), Some(kept)) = (base_pulled, base_kept) {
3568 if let Some(Stage::Scan { rows: r, .. }) = plan.stages.get_mut(i) {
3569 *r = pulled;
3570 }
3571 if let Some(j) = base_prefilter_at {
3572 if let Some(Stage::Prefilter { in_rows, out_rows, .. }) =
3573 plan.stages.get_mut(j)
3574 {
3575 *in_rows = pulled;
3576 *out_rows = kept;
3577 }
3578 }
3579 }
3580 }
3581
3582 if let Some(pred) = sel.where_.as_ref().filter(|_| !fuse) {
3584 let in_rows = rows.len();
3585 let mut kept = Vec::with_capacity(rows.len());
3586 for r in rows {
3587 if truthy(&eval(pred, &bind(&r, ctx))?) == Some(true) {
3591 kept.push(r);
3592 }
3593 }
3594 rows = kept;
3595 plan.push(Stage::Filter { in_rows, out_rows: rows.len() });
3596 }
3597
3598 let grouping = !sel.group_by.is_empty();
3611 let aggregating = grouping
3612 || sel.items.iter().any(|i| has_aggregate(&i.expr))
3613 || sel.having.as_ref().is_some_and(has_aggregate);
3614 if aggregating {
3615 let mut groups: Vec<(Vec<Value>, Vec<JoinedRow>)> = vec![];
3619 if grouping {
3620 for r in rows {
3621 let b = bind(&r, ctx);
3622 let mut key = Vec::with_capacity(sel.group_by.len());
3623 for g in &sel.group_by {
3624 key.push(eval(g, &b)?);
3625 }
3626 match groups.iter_mut().find(|(k, _)| {
3627 k.len() == key.len()
3628 && k.iter().zip(&key).all(|(a, b)| {
3629 (a.is_null() && b.is_null())
3632 || matches!(cmp_values(a, b), Some(std::cmp::Ordering::Equal))
3633 })
3634 }) {
3635 Some((_, bucket)) => bucket.push(r),
3636 None => groups.push((key, vec![r])),
3637 }
3638 }
3639 } else {
3640 groups.push((vec![], rows));
3643 }
3644
3645 let n_groups = groups.len();
3646 let mut cols: Vec<OutCol> = vec![];
3647 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = vec![];
3648 let empty: JoinedRow = vec![];
3649
3650 for (gi, (_key, grows)) in groups.iter().enumerate() {
3651 let scope = grows.first().unwrap_or(&empty);
3656
3657 if let Some(h) = &sel.having {
3658 let folded = fold_aggregates(h, grows, ctx, &sel.group_by)?;
3659 if truthy(&eval(&folded, &bind(scope, ctx))?) != Some(true) {
3660 continue;
3661 }
3662 }
3663
3664 let mut obj = Map::new();
3665 for item in &sel.items {
3666 let folded = fold_aggregates(&item.expr, grows, ctx, &sel.group_by)?;
3667 let v = eval(&folded, &bind(scope, ctx))?;
3668 if gi == 0 {
3670 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3671 let key = unique_key(&cols, &name);
3672 obj.insert(key.clone(), v);
3673 cols.push(OutCol { key, name });
3674 } else {
3675 let idx = obj.len();
3676 if let Some(c) = cols.get(idx) {
3677 obj.insert(c.key.clone(), v);
3678 }
3679 }
3680 }
3681 projected.push((obj, scope.clone()));
3682 }
3683
3684 plan.notes.push(if grouping {
3685 format!(
3686 "GroupAggregate on {} key(s): {} rows -> {} group(s){}",
3687 sel.group_by.len(),
3688 n_rows_before_group(&plan),
3689 n_groups,
3690 if sel.having.is_some() {
3691 format!(", HAVING kept {}", projected.len())
3692 } else {
3693 String::new()
3694 }
3695 )
3696 } else {
3697 format!("Aggregate over {} row(s) -> 1 row", groups[0].1.len())
3698 });
3699 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3700 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3701 return Ok((cols, out, plan));
3702 }
3703
3704 let mut cols: Vec<OutCol> = vec![];
3735 let mut spans: Vec<(usize, usize)> = Vec::with_capacity(sel.items.len());
3736 for item in &sel.items {
3737 let start = cols.len();
3738 match &item.expr {
3739 Expr::Star => {
3740 let mut plain: Vec<String> = vec![];
3741 let mut meta: Vec<String> = vec![];
3742 for r in &rows {
3743 for (n, _) in bind(r, ctx).flatten() {
3744 let target = if n.starts_with('_') { &mut meta } else { &mut plain };
3745 if !target.contains(&n) {
3747 target.push(n);
3748 }
3749 }
3750 }
3751 meta.sort();
3754 for n in plain.into_iter().chain(meta) {
3755 if !cols.iter().any(|c| c.name == n) {
3756 cols.push(OutCol { key: n.clone(), name: n });
3757 }
3758 }
3759 }
3760 Expr::QualifiedStar(q) => {
3761 if let Some(first) = rows.first() {
3762 for (n, _) in bind(first, ctx).flatten_binding(q) {
3763 if !cols.iter().any(|c| c.name == n) {
3764 cols.push(OutCol { key: n.clone(), name: n });
3765 }
3766 }
3767 }
3768 }
3769 _ => {
3770 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3771 let key = unique_key(&cols, &name);
3777 cols.push(OutCol { key, name });
3778 }
3779 }
3780 spans.push((start, cols.len()));
3781 }
3782
3783 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = Vec::with_capacity(rows.len());
3787 for r in rows {
3788 let b = bind(&r, ctx);
3789 let mut obj = Map::new();
3790 for (i, item) in sel.items.iter().enumerate() {
3791 let (start, end) = spans[i];
3792 match &item.expr {
3793 Expr::Star => {
3794 for (n, v) in b.flatten() {
3795 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3796 obj.entry(c.key.clone()).or_insert(v);
3797 }
3798 }
3799 }
3800 Expr::QualifiedStar(q) => {
3801 for (n, v) in b.flatten_binding(q) {
3802 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3803 obj.entry(c.key.clone()).or_insert(v);
3804 }
3805 }
3806 }
3807 _ => {
3808 let v = eval(&item.expr, &b)?;
3809 if let Some(c) = cols.get(start) {
3810 obj.insert(c.key.clone(), v);
3811 }
3812 }
3813 }
3814 }
3815 projected.push((obj, r));
3816 }
3817
3818 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3819
3820 if sel.distinct {
3822 let in_rows = projected.len();
3823 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
3824 projected.retain(|(obj, _)| seen.insert(row_key(&cols, obj)));
3827 plan.push(Stage::Distinct { in_rows, out_rows: projected.len() });
3828 }
3829
3830 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3831 Ok((cols, out, plan))
3832}
3833
3834fn n_rows_before_group(plan: &Plan) -> usize {
3838 plan.stages
3839 .iter()
3840 .rev()
3841 .find_map(|st| match st {
3842 Stage::Filter { out_rows, .. } => Some(*out_rows),
3843 Stage::Join { out_rows, .. } => Some(*out_rows),
3844 Stage::Prefilter { out_rows, .. } => Some(*out_rows),
3845 Stage::Scan { rows, .. } => Some(*rows),
3846 _ => None,
3847 })
3848 .unwrap_or(0)
3849}
3850
3851fn sort_keys(a: &[Value], b: &[Value], order_by: &[OrderBy]) -> std::cmp::Ordering {
3858 for (i, ob) in order_by.iter().enumerate() {
3859 let (Some(x), Some(y)) = (a.get(i), b.get(i)) else { continue };
3860 let ord = match (x.is_null(), y.is_null()) {
3861 (true, true) => std::cmp::Ordering::Equal,
3862 (true, false) => {
3865 return if ob.nulls_first {
3866 std::cmp::Ordering::Less
3867 } else {
3868 std::cmp::Ordering::Greater
3869 }
3870 }
3871 (false, true) => {
3872 return if ob.nulls_first {
3873 std::cmp::Ordering::Greater
3874 } else {
3875 std::cmp::Ordering::Less
3876 }
3877 }
3878 (false, false) => cmp_values(x, y).unwrap_or(std::cmp::Ordering::Equal),
3879 };
3880 let ord = if matches!(ob.dir, Dir::Desc) { ord.reverse() } else { ord };
3881 if !ord.is_eq() {
3882 return ord;
3883 }
3884 }
3885 std::cmp::Ordering::Equal
3886}
3887
3888fn finish(
3890 sel: &Select,
3891 cols: &[OutCol],
3892 mut projected: Vec<(Map<String, Value>, JoinedRow)>,
3893 ctx: EvalCtx,
3894 plan: &mut Plan,
3895) -> Result<Vec<Value>> {
3896 if !sel.order_by.is_empty() {
3898 let mut keyed: Vec<(Vec<Value>, (Map<String, Value>, JoinedRow))> = vec![];
3902 for (obj, src) in projected {
3903 let mut key = vec![];
3904 for ob in &sel.order_by {
3905 let v = match (ob.ordinal, &ob.expr) {
3906 (Some(n), _) => {
3907 let c = cols.get(n - 1).ok_or_else(|| {
3908 anyhow::anyhow!(
3909 "ORDER BY {} is out of range: the select list has {} \
3910 column(s)", n, cols.len())
3911 })?;
3912 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3913 }
3914 (None, Some(Expr::Column { qual: None, name }))
3919 if cols.iter().any(|c| c.name == *name) =>
3920 {
3921 let c = cols.iter().find(|c| c.name == *name).expect("checked");
3922 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3923 }
3924 (None, Some(e)) => {
3925 eval(e, &bind(&src, ctx))?
3929 }
3930 (None, None) => Value::Null,
3931 };
3932 key.push(v);
3933 }
3934 keyed.push((key, (obj, src)));
3935 }
3936
3937 keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, &sel.order_by));
3938
3939 projected = keyed.into_iter().map(|(_, row)| row).collect();
3940 plan.push(Stage::Sort { keys: sel.order_by.len(), rows: projected.len() });
3941 }
3942
3943 let mut out: Vec<Value> = projected
3945 .into_iter()
3946 .map(|(obj, _)| Value::Object(obj))
3947 .collect();
3948 let in_rows = out.len();
3949 if let Some(off) = sel.offset {
3950 out = if off >= out.len() { vec![] } else { out.split_off(off) };
3951 }
3952 if let Some(lim) = sel.limit {
3953 out.truncate(lim);
3954 }
3955 if sel.limit.is_some() || sel.offset.is_some() {
3956 plan.push(Stage::Limit {
3957 limit: sel.limit,
3958 offset: sel.offset,
3959 in_rows,
3960 out_rows: out.len(),
3961 });
3962 }
3963
3964 Ok(out)
3965}
3966
3967fn keep_row(cand: &JoinedRow, post: Option<&Expr>, removed: &mut usize, ctx: EvalCtx) -> Result<bool> {
4000 let Some(p) = post else { return Ok(true) };
4001 if truthy(&eval(p, &bind(cand, ctx))?) == Some(true) {
4003 Ok(true)
4004 } else {
4005 *removed += 1;
4006 Ok(false)
4007 }
4008}
4009
4010#[allow(clippy::too_many_arguments)]
4016fn emit_unmatched_right(
4017 out: &mut Vec<JoinedRow>,
4018 kind: JoinKind,
4019 left_bindings: &[String],
4020 right_rows: &[Value],
4021 right_matched: &[bool],
4022 rb: &str,
4023 post: Option<&Expr>,
4024 removed: &mut usize,
4025 ctx: EvalCtx,
4026) -> Result<()> {
4027 if !matches!(kind, JoinKind::Right | JoinKind::Full) {
4028 return Ok(());
4029 }
4030 for (ri, right) in right_rows.iter().enumerate() {
4031 if right_matched[ri] {
4032 continue;
4033 }
4034 let mut cand: JoinedRow = left_bindings.iter().map(|b| (b.clone(), None)).collect();
4035 cand.push((rb.to_string(), Some(right.clone())));
4036 if keep_row(&cand, post, removed, ctx)? {
4039 out.push(cand);
4040 }
4041 }
4042 Ok(())
4043}
4044
4045fn join_lateral(
4052 left_src: &mut dyn LeftSource,
4053 join: &Join,
4054 resolve: &Resolver,
4055 budget: Option<usize>,
4056 post: Option<&Expr>,
4057 ctx: EvalCtx,
4058) -> Result<(Vec<JoinedRow>, usize, usize, usize)> {
4059 let sub = join
4060 .table
4061 .sub
4062 .as_deref()
4063 .ok_or_else(|| anyhow::anyhow!("LATERAL requires a subquery"))?;
4064 let rb = join.table.binding();
4065 let mut out: Vec<JoinedRow> = vec![];
4066 let mut removed = 0usize;
4067 let mut consumed = 0usize;
4068 let mut produced = 0usize;
4069 while let Some(left) = {
4070 if budget.is_some_and(|b| out.len() >= b) { None } else { left_src.next_left()? }
4071 } {
4072 consumed += 1;
4073 let scope = bind(&left, ctx);
4074 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), Some(&scope))?;
4075 produced += rows.len();
4076 let mut matched = false;
4077 for r in rows {
4078 let m = match r {
4079 Value::Object(m) => m,
4080 _ => Map::new(),
4081 };
4082 let mut named = Map::new();
4083 for (i, c) in cols.iter().enumerate() {
4084 let name = join.table.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
4085 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
4086 }
4087 let mut cand = left.clone();
4088 cand.push((rb.clone(), Some(Value::Object(named))));
4089 let on_ok = match &join.on {
4090 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4091 None => true,
4092 };
4093 if !on_ok {
4094 continue;
4095 }
4096 matched = true;
4097 if keep_row(&cand, post, &mut removed, ctx)? {
4098 out.push(cand);
4099 }
4100 }
4101 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4102 let mut cand = left.clone();
4103 cand.push((rb.clone(), None));
4104 if keep_row(&cand, post, &mut removed, ctx)? {
4105 out.push(cand);
4106 }
4107 }
4108 }
4109 Ok((out, removed, consumed, produced))
4110}
4111
4112#[allow(clippy::too_many_arguments)]
4118fn join_nested_loop(
4119 left_src: &mut dyn LeftSource,
4120 left_bindings: &[String],
4121 join: &Join,
4122 right_rows: &[Value],
4123 rb: &str,
4124 budget: Option<usize>,
4125 post: Option<&Expr>,
4126 ctx: EvalCtx,
4127) -> Result<(Vec<JoinedRow>, usize, usize)> {
4128 let mut out: Vec<JoinedRow> = vec![];
4129 let mut removed = 0usize;
4130 let mut right_matched = vec![false; right_rows.len()];
4132
4133 let mut consumed = 0usize;
4134 while let Some(left) = {
4135 if budget.is_some_and(|b| out.len() >= b) {
4136 None
4139 } else {
4140 left_src.next_left()?
4141 }
4142 } {
4143 consumed += 1;
4144 let left = &left;
4145 let mut matched = false;
4148 for (ri, right) in right_rows.iter().enumerate() {
4149 let mut cand: JoinedRow = left.clone();
4150 cand.push((rb.to_string(), Some(right.clone())));
4151 let joins_here = match &join.on {
4152 None => true,
4154 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4158 };
4159 if joins_here {
4160 matched = true;
4161 right_matched[ri] = true;
4162 if keep_row(&cand, post, &mut removed, ctx)? {
4163 out.push(cand);
4164 }
4165 }
4166 }
4167 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4169 let mut cand: JoinedRow = left.clone();
4170 cand.push((rb.to_string(), None));
4171 if keep_row(&cand, post, &mut removed, ctx)? {
4172 out.push(cand);
4173 }
4174 }
4175 }
4176
4177 if !budget.is_some_and(|b| out.len() >= b) {
4182 emit_unmatched_right(
4183 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4184 &mut removed, ctx,
4185 )?;
4186 }
4187 Ok((out, removed, consumed))
4188}
4189
4190#[allow(clippy::too_many_arguments)]
4198fn join_hash(
4199 left_src: &mut dyn LeftSource,
4200 left_bindings: &[String],
4201 join: &Join,
4202 right_rows: &[Value],
4203 rb: &str,
4204 keys: &[(Expr, Expr)],
4205 budget: Option<usize>,
4206 post: Option<&Expr>,
4207 ctx: EvalCtx,
4208) -> Result<(Vec<JoinedRow>, usize, usize)> {
4209 debug_assert!(!keys.is_empty(), "the planner must not choose Hash with no keys");
4210
4211 let side = sqljoin::HashSide::build(right_rows.len(), |i| {
4213 let one: JoinedRow = vec![(rb.to_string(), Some(right_rows[i].clone()))];
4216 let b = bind(&one, ctx);
4217 let mut k = Vec::with_capacity(keys.len());
4218 for (_, right_expr) in keys {
4219 match sqljoin::hkey(&eval(right_expr, &b)?) {
4220 Some(h) => k.push(h),
4221 None => return Ok(None),
4223 }
4224 }
4225 Ok(Some(k))
4226 })?;
4227
4228 let mut out: Vec<JoinedRow> = vec![];
4230 let mut removed = 0usize;
4231 let mut right_matched = vec![false; right_rows.len()];
4232
4233 let mut consumed = 0usize;
4234 while let Some(left) = {
4235 if budget.is_some_and(|b| out.len() >= b) {
4236 None
4237 } else {
4238 left_src.next_left()?
4239 }
4240 } {
4241 consumed += 1;
4242 let left = &left;
4243 let lb = bind(left, ctx);
4244 let mut lk = Vec::with_capacity(keys.len());
4245 let mut null_key = false;
4246 for (left_expr, _) in keys {
4247 match sqljoin::hkey(&eval(left_expr, &lb)?) {
4248 Some(h) => lk.push(h),
4249 None => {
4250 null_key = true;
4251 break;
4252 }
4253 }
4254 }
4255
4256 let mut matched = false;
4257 if !null_key {
4261 for &ri in side.probe(&lk) {
4262 let mut cand: JoinedRow = left.clone();
4263 cand.push((rb.to_string(), Some(right_rows[ri].clone())));
4264 let joins_here = match &join.on {
4266 None => true,
4267 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4268 };
4269 if joins_here {
4270 matched = true;
4271 right_matched[ri] = true;
4272 if keep_row(&cand, post, &mut removed, ctx)? {
4273 out.push(cand);
4274 }
4275 }
4276 }
4277 }
4278 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4279 let mut cand: JoinedRow = left.clone();
4280 cand.push((rb.to_string(), None));
4281 if keep_row(&cand, post, &mut removed, ctx)? {
4282 out.push(cand);
4283 }
4284 }
4285 }
4286
4287 if !budget.is_some_and(|b| out.len() >= b) {
4290 emit_unmatched_right(
4291 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4292 &mut removed, ctx,
4293 )?;
4294 }
4295 Ok((out, removed, consumed))
4296}
4297
4298fn prefilter(
4305 rows: Vec<Value>,
4306 binding: &str,
4307 push: &Pushdown,
4308 plan: &mut Plan,
4309 ctx: EvalCtx,
4310) -> Result<Vec<Value>> {
4311 let Some(preds) = push.for_binding(binding) else { return Ok(rows) };
4312 if preds.is_empty() {
4313 return Ok(rows);
4314 }
4315 let in_rows = rows.len();
4316 let mut kept = Vec::with_capacity(rows.len());
4317 for row in rows {
4318 let one: JoinedRow = vec![(binding.to_string(), Some(row))];
4319 let b = bind(&one, ctx);
4320 let mut keep = true;
4321 for p in preds {
4322 if truthy(&eval(p, &b)?) != Some(true) {
4328 keep = false;
4329 break;
4330 }
4331 }
4332 if keep {
4333 if let Some((_, Some(v))) = one.into_iter().next() {
4335 kept.push(v);
4336 }
4337 }
4338 }
4339 plan.push(Stage::Prefilter {
4340 binding: binding.to_string(),
4341 predicates: preds.len(),
4342 in_rows,
4343 out_rows: kept.len(),
4344 });
4345 Ok(kept)
4346}
4347
4348fn fetch(t: &TableRef, resolve: &Resolver, ctx: EvalCtx) -> Result<Box<dyn Relation>> {
4351 if let Some(sub) = &t.sub {
4355 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), ctx.outer)?;
4356 let out = rows
4357 .into_iter()
4358 .map(|r| {
4359 let m = match r {
4360 Value::Object(m) => m,
4361 _ => Map::new(),
4362 };
4363 let mut named = Map::new();
4364 for (i, c) in cols.iter().enumerate() {
4365 let name = t.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
4366 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
4369 }
4370 Value::Object(named)
4371 })
4372 .collect();
4373 return Ok(from_vec(out));
4374 }
4375
4376 if let Some(args) = &t.args {
4380 let empty: JoinedRow = vec![];
4381 let scope = bind(&empty, ctx);
4382 let col = |i: usize, default: &str| -> String {
4383 t.col_aliases.get(i).cloned().unwrap_or_else(|| default.to_string())
4384 };
4385 let rows: Vec<Value> = match t.name.as_str() {
4386 "generate_series" => {
4387 let a = num(&eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_series() needs a start"))?, &scope)?);
4388 let b = num(&eval(args.get(1).ok_or_else(|| anyhow::anyhow!("generate_series() needs a stop"))?, &scope)?);
4389 let step = match args.get(2) {
4390 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4391 None => 1.0,
4392 };
4393 match (a, b) {
4394 (Some(a), Some(b)) if step != 0.0 => {
4396 let mut out = vec![];
4397 let mut x = a;
4398 while (step > 0.0 && x <= b) || (step < 0.0 && x >= b) {
4399 let mut m = Map::new();
4400 m.insert(col(0, "generate_series"), from_f64(x));
4401 out.push(Value::Object(m));
4402 x += step;
4403 if out.len() > 1_000_000 {
4404 bail!("generate_series() would produce more than a million rows");
4405 }
4406 }
4407 out
4408 }
4409 (Some(_), Some(_)) => bail!("generate_series() step cannot equal zero"),
4410 _ => vec![],
4411 }
4412 }
4413 "unnest" => match eval(args.first().ok_or_else(|| anyhow::anyhow!("unnest() needs an array"))?, &scope)? {
4414 Value::Array(items) => items
4415 .into_iter()
4416 .map(|v| {
4417 let mut m = Map::new();
4418 m.insert(col(0, "unnest"), v);
4419 Value::Object(m)
4420 })
4421 .collect(),
4422 _ => vec![],
4424 },
4425 "generate_subscripts" => {
4430 let dim = match args.get(1) {
4431 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4432 None => 1.0,
4433 };
4434 match eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_subscripts() needs an array"))?, &scope)? {
4435 Value::Array(items) if dim == 1.0 => (1..=items.len())
4439 .map(|i| {
4440 let mut m = Map::new();
4441 m.insert(col(0, "generate_subscripts"), from_f64(i as f64));
4442 Value::Object(m)
4443 })
4444 .collect(),
4445 _ => vec![],
4446 }
4447 }
4448 "pg_partition_tree" | "pg_partition_ancestors" => vec![],
4451 other => bail!(
4452 "the table function {}() is not implemented. It is refused rather \
4453 than answered with no rows, because an empty relation reads as \
4454 missing DATA rather than a missing feature",
4455 other
4456 ),
4457 };
4458 return Ok(from_vec(rows));
4459 }
4460
4461 match resolve(&t.name)? {
4462 Some(rel) => Ok(rel),
4463 None => bail!("relation {:?} does not exist", t.name),
4466 }
4467}
4468
4469trait LeftSource {
4476 fn next_left(&mut self) -> Result<Option<JoinedRow>>;
4477 fn hint(&self) -> Option<usize>;
4479 fn take_rows(&mut self) -> Vec<JoinedRow>;
4481 fn stats(&self) -> Option<(usize, usize)> {
4483 None
4484 }
4485}
4486
4487struct StreamLeft<'a> {
4493 rel: Box<dyn Relation>,
4494 binding: String,
4495 preds: Vec<Expr>,
4496 ctx: EvalCtx<'a>,
4497 pulled: usize,
4501 kept: usize,
4502}
4503
4504impl<'a> LeftSource for StreamLeft<'a> {
4505 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4506 while let Some(row) = self.rel.next_row()? {
4507 self.pulled += 1;
4508 let one: JoinedRow = vec![(self.binding.clone(), Some(row))];
4509 if !self.preds.is_empty() {
4510 let b = bind(&one, self.ctx);
4511 let mut keep = true;
4512 for p in &self.preds {
4513 if truthy(&eval(p, &b)?) != Some(true) {
4514 keep = false;
4515 break;
4516 }
4517 }
4518 if !keep {
4519 continue;
4520 }
4521 }
4522 self.kept += 1;
4523 return Ok(Some(one));
4524 }
4525 Ok(None)
4526 }
4527 fn hint(&self) -> Option<usize> {
4528 self.rel.size_hint()
4533 }
4534 fn take_rows(&mut self) -> Vec<JoinedRow> {
4535 let mut out = vec![];
4538 while let Ok(Some(r)) = self.next_left() {
4539 out.push(r);
4540 }
4541 out
4542 }
4543 fn stats(&self) -> Option<(usize, usize)> {
4544 Some((self.pulled, self.kept))
4545 }
4546}
4547
4548struct VecLeft {
4551 rows: Vec<JoinedRow>,
4552 at: usize,
4553}
4554
4555impl LeftSource for VecLeft {
4556 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4557 let r = self.rows.get(self.at).cloned();
4558 if r.is_some() {
4559 self.at += 1;
4560 }
4561 Ok(r)
4562 }
4563 fn hint(&self) -> Option<usize> {
4564 Some(self.rows.len().saturating_sub(self.at))
4565 }
4566 fn take_rows(&mut self) -> Vec<JoinedRow> {
4567 let mut v = std::mem::take(&mut self.rows);
4568 if self.at > 0 {
4569 v = v.split_off(self.at);
4570 }
4571 self.at = 0;
4572 v
4573 }
4574}
4575
4576fn drain(mut rel: Box<dyn Relation>) -> Result<Vec<Value>> {
4583 let mut out = Vec::with_capacity(rel.size_hint().unwrap_or(0));
4584 while let Some(row) = rel.next_row()? {
4585 out.push(row);
4586 }
4587 Ok(out)
4588}
4589
4590pub fn run(sql: &str, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
4592 let sel = parse(sql)?;
4593 execute(&sel, resolve)
4594}
4595
4596#[cfg(test)]
4597mod lexer_tests {
4598 use super::*;
4599
4600 fn kinds(src: &str) -> Vec<Tok> {
4601 let mut t = lex(src).expect("lexes");
4602 t.pop(); t
4604 }
4605
4606 #[test]
4607 fn a_word_keeps_both_its_canonical_and_raw_spelling() {
4608 assert_eq!(
4611 kinds("Select"),
4612 vec![Tok::Word { upper: "SELECT".into(), raw: "Select".into() }]
4613 );
4614 }
4615
4616 #[test]
4617 fn a_quoted_identifier_is_never_a_keyword() {
4618 assert_eq!(kinds(r#""select""#), vec![Tok::Quoted("select".into())]);
4619 assert_eq!(kinds(r#""Name""#), vec![Tok::Quoted("Name".into())]);
4621 }
4622
4623 #[test]
4624 fn a_doubled_quote_is_one_literal_quote() {
4625 assert_eq!(kinds("'it''s'"), vec![Tok::Str("it's".into())]);
4626 assert_eq!(kinds(r#""a""b""#), vec![Tok::Quoted("a\"b".into())]);
4627 }
4628
4629 #[test]
4630 fn an_E_string_decodes_the_escapes_catalogue_sql_uses() {
4631 assert_eq!(kinds(r"E'\n'"), vec![Tok::Str("\n".into())]);
4633 assert_eq!(kinds(r"E'a\tb'"), vec![Tok::Str("a\tb".into())]);
4634 assert_eq!(kinds(r"E'\q'"), vec![Tok::Str("q".into())]);
4636 }
4637
4638 #[test]
4639 fn operators_match_longest_first() {
4640 assert_eq!(kinds("!~*"), vec![Tok::Op("!~*".into())]);
4642 assert_eq!(kinds("!~"), vec![Tok::Op("!~".into())]);
4643 assert_eq!(kinds("~*"), vec![Tok::Op("~*".into())]);
4644 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 }
4651
4652 #[test]
4653 fn comments_are_skipped_including_nested_block_comments() {
4654 assert_eq!(kinds("1 -- trailing\n"), vec![Tok::Num(1.0)]);
4655 assert_eq!(kinds("1 /* a */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4656 assert_eq!(kinds("1 /* a /* b */ c */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4658 assert!(lex("1 /* unterminated").is_err());
4659 }
4660
4661 #[test]
4662 fn numbers_parse_including_fractions_and_exponents() {
4663 assert_eq!(kinds("42"), vec![Tok::Num(42.0)]);
4664 assert_eq!(kinds("4.5"), vec![Tok::Num(4.5)]);
4665 assert_eq!(kinds(".5"), vec![Tok::Num(0.5)]);
4666 assert_eq!(kinds("1e3"), vec![Tok::Num(1000.0)]);
4667 assert_eq!(kinds("1e-2"), vec![Tok::Num(0.01)]);
4668 assert_eq!(
4670 kinds("1e"),
4671 vec![Tok::Num(1.0), Tok::Word { upper: "E".into(), raw: "e".into() }]
4672 );
4673 }
4674
4675 #[test]
4676 fn an_unterminated_literal_is_an_error_not_a_truncation() {
4677 assert!(lex("'abc").is_err());
4678 assert!(lex(r#""abc"#).is_err());
4679 }
4680
4681 #[test]
4682 fn an_unknown_character_is_REFUSED_rather_than_skipped() {
4683 let e = lex("SELECT 1 @ 2").unwrap_err().to_string();
4686 assert!(e.contains('@'), "{}", e);
4687 }
4688
4689 #[test]
4690 fn the_real_dn_query_lexes() {
4691 let sql = r#"SELECT n.nspname AS "Name",
4692 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
4693 FROM pg_catalog.pg_namespace n
4694 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
4695 ORDER BY 1;"#;
4696 let toks = lex(sql).expect("psql's \\dn must lex");
4697 assert!(toks.contains(&Tok::Quoted("Name".into())));
4698 assert!(toks.contains(&Tok::Op("!~".into())));
4699 assert!(toks.contains(&Tok::Op("<>".into())));
4700 assert!(toks.contains(&Tok::Str("^pg_".into())));
4701 }
4702
4703 #[test]
4704 fn the_real_dt_query_lexes() {
4705 let sql = r#"SELECT n.nspname as "Schema", c.relname as "Name",
4706 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' END as "Type",
4707 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
4708 FROM pg_catalog.pg_class c
4709 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
4710 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
4711 WHERE c.relkind IN ('r','p','')
4712 AND n.nspname <> 'pg_catalog'
4713 AND n.nspname !~ '^pg_toast'
4714 AND pg_catalog.pg_table_is_visible(c.oid)
4715 ORDER BY 1,2;"#;
4716 let toks = lex(sql).expect("psql's \\dt must lex");
4717 assert!(toks.iter().any(|t| t.is_kw("CASE")));
4718 assert!(toks.iter().any(|t| t.is_kw("LEFT")));
4719 assert!(toks.iter().any(|t| t.is_kw("JOIN")));
4720 assert!(toks.contains(&Tok::Str(String::new())));
4722 }
4723}
4724
4725#[cfg(test)]
4726mod parser_tests {
4727 use super::*;
4728 use serde_json::json;
4729
4730 fn col(qual: Option<&str>, name: &str) -> Expr {
4731 Expr::Column { qual: qual.map(str::to_string), name: name.to_string() }
4732 }
4733
4734 #[test]
4735 fn a_bare_select_list_and_from() {
4736 let s = parse("SELECT a, b FROM t").unwrap();
4737 assert_eq!(s.items.len(), 2);
4738 assert_eq!(s.items[0].expr, col(None, "a"));
4739 assert_eq!(s.from.unwrap().name, "t");
4740 }
4741
4742 #[test]
4746 fn as_of_system_time_is_read_per_table() {
4747 let s = parse("SELECT total FROM orders AS OF SYSTEM TIME 42").unwrap();
4748 assert_eq!(s.from.clone().unwrap().as_of, Some(42));
4749 assert_eq!(s.from.unwrap().alias, None);
4750
4751 let s = parse("SELECT o.total FROM orders AS OF SYSTEM TIME 42 o").unwrap();
4753 let t = s.from.unwrap();
4754 assert_eq!((t.as_of, t.alias.as_deref()), (Some(42), Some("o")));
4755
4756 let t = parse("SELECT x FROM orders AS o").unwrap().from.unwrap();
4761 assert_eq!((t.as_of, t.alias.as_deref()), (None, Some("o")));
4762
4763 let s = parse(
4766 "SELECT o.total, n.total FROM orders AS OF SYSTEM TIME 1 o \
4767 JOIN orders n ON o._id = n._id").unwrap();
4768 assert_eq!(s.from.unwrap().as_of, Some(1));
4769 assert_eq!(s.joins[0].table.as_of, None);
4770
4771 let s = parse("SELECT total FROM orders AS OF SYSTEM TIME 7 WHERE _id = '1'").unwrap();
4773 assert_eq!(s.from.unwrap().as_of, Some(7));
4774 assert!(s.where_.is_some());
4775 }
4776
4777 #[test]
4782 fn nqls_verbs_are_table_qualifiers_in_sql() {
4783 let t = parse("SELECT _id FROM orders SEARCH 'acme'").unwrap().from.unwrap();
4784 assert_eq!(t.search.as_deref(), Some("acme"));
4785
4786 let t = parse("SELECT _id FROM orders VALID AS OF '2026-01-01'").unwrap().from.unwrap();
4787 assert_eq!(t.valid_as_of.as_deref(), Some("2026-01-01"));
4788
4789 let t = parse(
4791 "SELECT o._id FROM orders AS OF SYSTEM TIME 9 VALID AS OF '2026-01-01' \
4792 SEARCH 'acme' o").unwrap().from.unwrap();
4793 assert_eq!(
4794 (t.as_of, t.valid_as_of.as_deref(), t.search.as_deref(), t.alias.as_deref()),
4795 (Some(9), Some("2026-01-01"), Some("acme"), Some("o")));
4796
4797 let s = parse(
4799 "SELECT o._id FROM orders SEARCH 'acme' o JOIN drivers d ON o.driver = d._id")
4800 .unwrap();
4801 assert_eq!(s.from.unwrap().search.as_deref(), Some("acme"));
4802 assert_eq!(s.joins[0].table.search, None);
4803 }
4804
4805 #[test]
4813 fn the_new_verbs_do_not_steal_names_that_already_worked() {
4814 let t = parse("SELECT search.total FROM orders search").unwrap().from.unwrap();
4815 assert_eq!((t.alias.as_deref(), t.search.as_deref()), (Some("search"), None));
4816
4817 let t = parse("SELECT valid.total FROM orders valid").unwrap().from.unwrap();
4818 assert_eq!((t.alias.as_deref(), t.valid_as_of.as_deref()), (Some("valid"), None));
4819
4820 assert!(parse("SELECT search, valid FROM orders").is_ok());
4822 assert!(parse("SELECT _id FROM orders WHERE search = 'x'").is_ok());
4823
4824 let t = parse("SELECT x FROM orders AS valid").unwrap().from.unwrap();
4826 assert_eq!(t.alias.as_deref(), Some("valid"));
4827 }
4828
4829 #[test]
4830 fn a_verbs_argument_is_refused_when_it_is_the_wrong_kind_of_thing() {
4831 let e = parse("SELECT _id FROM orders VALID AS OF 42").unwrap_err().to_string();
4835 assert!(e.contains("date string"), "{}", e);
4836 }
4837
4838 #[test]
4839 fn a_quoted_datetime_is_a_wall_clock_marker_and_garbage_still_refuses() {
4840 let s = parse("SELECT _id FROM orders AS OF SYSTEM TIME '2026-09-15T17:00:00Z'")
4846 .expect("a quoted RFC 3339 datetime is accepted");
4847 let marker = s.from.unwrap().as_of.expect("the marker is set");
4848 assert_ne!(marker & crate::wallclock::WALL_CLOCK_FLAG, 0,
4849 "the marker must be wall-clock-tagged, not a bare seq");
4850 let decoded = crate::wallclock::WallClock::from_marker(marker)
4851 .expect("the marker decodes");
4852 assert_eq!(decoded.epoch_secs(), 1_789_491_600.0); let s = parse("SELECT _id FROM orders AS OF SYSTEM TIME 42").unwrap();
4856 let marker = s.from.unwrap().as_of.unwrap();
4857 assert_eq!(marker, 42);
4858 assert_eq!(marker & crate::wallclock::WALL_CLOCK_FLAG, 0);
4859
4860 for sql in [
4861 "SELECT total FROM orders AS OF SYSTEM TIME now()",
4862 "SELECT total FROM orders AS OF SYSTEM TIME -1",
4863 "SELECT total FROM orders AS OF SYSTEM TIME 1.5",
4864 "SELECT total FROM orders AS OF SYSTEM TIME 'not a time'",
4865 ] {
4866 let e = parse(sql).unwrap_err().to_string();
4867 assert!(
4868 e.contains("sequence number or a quoted datetime")
4869 || e.contains("unrecognized datetime"),
4870 "{} -> {} (neither the grammar's nor the parser's refusal)",
4871 sql,
4872 e
4873 );
4874 }
4875 }
4876
4877 #[test]
4878 fn a_clause_keyword_is_never_read_as_a_bare_alias() {
4879 let s = parse("SELECT a FROM t WHERE a = 1").unwrap();
4883 assert_eq!(s.from.clone().unwrap().alias, None);
4884 assert!(s.where_.is_some(), "the WHERE clause must survive");
4885 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4886 assert_eq!(s.from.unwrap().alias, None);
4887 assert_eq!(s.order_by.len(), 1);
4888 }
4889
4890 #[test]
4891 fn a_real_alias_is_kept_in_both_spellings() {
4892 assert_eq!(parse("SELECT a FROM t x").unwrap().from.unwrap().alias,
4893 Some("x".to_string()));
4894 assert_eq!(parse("SELECT a FROM t AS x").unwrap().from.unwrap().alias,
4895 Some("x".to_string()));
4896 }
4897
4898 #[test]
4899 fn a_tables_binding_is_its_alias_else_its_bare_name() {
4900 let t = TableRef::named("pg_catalog.pg_class", Some("c".into()));
4901 assert_eq!(t.binding(), "c");
4902 let t = TableRef::named("pg_catalog.pg_class", None);
4903 assert_eq!(t.binding(), "pg_class", "the schema is not how a column is addressed");
4904 }
4905
4906 #[test]
4907 fn a_qualified_column_keeps_only_its_immediate_qualifier() {
4908 assert_eq!(parse("SELECT n.nspname FROM x").unwrap().items[0].expr,
4909 col(Some("n"), "nspname"));
4910 assert_eq!(parse("SELECT public.orders.id FROM x").unwrap().items[0].expr,
4913 col(Some("orders"), "id"));
4914 }
4915
4916 #[test]
4917 fn an_alias_may_be_a_quoted_string_with_significant_case() {
4918 let s = parse(r#"SELECT n.nspname AS "Name" FROM x"#).unwrap();
4919 assert_eq!(s.items[0].alias, Some("Name".to_string()));
4920 }
4921
4922 #[test]
4923 fn a_schema_qualified_function_drops_its_schema() {
4924 let s = parse("SELECT pg_catalog.pg_get_userbyid(n.nspowner) FROM x").unwrap();
4927 match &s.items[0].expr {
4928 Expr::Func { name, args } => {
4929 assert_eq!(name, "pg_get_userbyid");
4930 assert_eq!(args.len(), 1);
4931 assert_eq!(args[0], col(Some("n"), "nspowner"));
4932 }
4933 other => panic!("{:?}", other),
4934 }
4935 }
4936
4937 #[test]
4938 fn operator_precedence_matches_sql() {
4939 let s = parse("SELECT 1 FROM t WHERE a = 1 OR b = 2 AND c = 3").unwrap();
4942 match s.where_.unwrap() {
4943 Expr::Binary { op, right, .. } => {
4944 assert_eq!(op, "OR");
4945 assert!(matches!(*right, Expr::Binary { ref op, .. } if op == "AND"),
4946 "AND must bind tighter than OR");
4947 }
4948 other => panic!("{:?}", other),
4949 }
4950 let s = parse("SELECT 1 FROM t WHERE a = 1 AND b = 2").unwrap();
4952 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4953 let s = parse("SELECT 1 + 2 * 3 FROM t").unwrap();
4955 match &s.items[0].expr {
4956 Expr::Binary { op, right, .. } => {
4957 assert_eq!(op, "+");
4958 assert!(matches!(**right, Expr::Binary { ref op, .. } if op == "*"));
4959 }
4960 other => panic!("{:?}", other),
4961 }
4962 }
4963
4964 #[test]
4965 fn parentheses_override_precedence() {
4966 let s = parse("SELECT 1 FROM t WHERE (a = 1 OR b = 2) AND c = 3").unwrap();
4967 match s.where_.unwrap() {
4968 Expr::Binary { op, left, .. } => {
4969 assert_eq!(op, "AND");
4970 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "OR"));
4971 }
4972 other => panic!("{:?}", other),
4973 }
4974 }
4975
4976 #[test]
4977 fn in_and_is_null_and_between_parse_in_both_polarities() {
4978 let s = parse("SELECT 1 FROM t WHERE k IN ('r','p','')").unwrap();
4979 match s.where_.unwrap() {
4980 Expr::InList { list, negated, .. } => {
4981 assert_eq!(list.len(), 3);
4982 assert!(!negated);
4983 assert_eq!(list[2], Expr::Literal(json!("")));
4985 }
4986 other => panic!("{:?}", other),
4987 }
4988 assert!(matches!(parse("SELECT 1 FROM t WHERE k NOT IN (1)").unwrap().where_.unwrap(),
4989 Expr::InList { negated: true, .. }));
4990 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NULL").unwrap().where_.unwrap(),
4991 Expr::IsNull { negated: false, .. }));
4992 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NOT NULL").unwrap().where_.unwrap(),
4993 Expr::IsNull { negated: true, .. }));
4994 let s = parse("SELECT 1 FROM t WHERE n BETWEEN 1 AND 5").unwrap();
4996 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4997 }
4998
4999 #[test]
5000 fn both_case_spellings_parse() {
5001 let s = parse("SELECT CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
5003 ELSE 'other' END FROM t").unwrap();
5004 match &s.items[0].expr {
5005 Expr::Case { operand, whens, else_ } => {
5006 assert!(operand.is_some());
5007 assert_eq!(whens.len(), 2);
5008 assert!(else_.is_some());
5009 }
5010 other => panic!("{:?}", other),
5011 }
5012 let s = parse("SELECT CASE WHEN k = 'r' THEN 1 END FROM t").unwrap();
5014 match &s.items[0].expr {
5015 Expr::Case { operand, whens, else_ } => {
5016 assert!(operand.is_none());
5017 assert_eq!(whens.len(), 1);
5018 assert!(else_.is_none());
5019 }
5020 other => panic!("{:?}", other),
5021 }
5022 assert!(parse("SELECT CASE k END FROM t").is_err());
5024 }
5025
5026 #[test]
5027 fn every_join_flavour_parses_and_an_inner_join_demands_ON() {
5028 for (sql, kind) in [
5029 ("SELECT 1 FROM a JOIN b ON a.x = b.x", JoinKind::Inner),
5030 ("SELECT 1 FROM a INNER JOIN b ON a.x = b.x", JoinKind::Inner),
5031 ("SELECT 1 FROM a LEFT JOIN b ON a.x = b.x", JoinKind::Left),
5032 ("SELECT 1 FROM a LEFT OUTER JOIN b ON a.x = b.x", JoinKind::Left),
5033 ("SELECT 1 FROM a RIGHT JOIN b ON a.x = b.x", JoinKind::Right),
5034 ("SELECT 1 FROM a FULL OUTER JOIN b ON a.x = b.x", JoinKind::Full),
5035 ("SELECT 1 FROM a CROSS JOIN b", JoinKind::Cross),
5036 ] {
5037 let s = parse(sql).unwrap_or_else(|e| panic!("{}: {}", sql, e));
5038 assert_eq!(s.joins.len(), 1, "{}", sql);
5039 assert_eq!(s.joins[0].kind, kind, "{}", sql);
5040 }
5041 let s = parse("SELECT 1 FROM a, b").unwrap();
5043 assert_eq!(s.joins[0].kind, JoinKind::Cross);
5044 assert!(parse("SELECT 1 FROM a LEFT JOIN b").is_err());
5047 assert!(parse("SELECT 1 FROM a JOIN b USING (x)").is_err());
5048 }
5049
5050 #[test]
5051 fn order_by_reads_a_number_as_an_ORDINAL() {
5052 let s = parse("SELECT a, b FROM t ORDER BY 1, 2 DESC").unwrap();
5056 assert_eq!(s.order_by.len(), 2);
5057 assert_eq!(s.order_by[0].ordinal, Some(1));
5058 assert_eq!(s.order_by[0].dir, Dir::Asc);
5059 assert_eq!(s.order_by[1].ordinal, Some(2));
5060 assert_eq!(s.order_by[1].dir, Dir::Desc);
5061 let s = parse("SELECT a FROM t ORDER BY lower(a) ASC").unwrap();
5063 assert!(s.order_by[0].ordinal.is_none());
5064 assert!(s.order_by[0].expr.is_some());
5065 }
5066
5067 #[test]
5068 fn null_ordering_defaults_the_way_postgres_defaults() {
5069 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
5070 assert!(!s.order_by[0].nulls_first, "ASC defaults to NULLS LAST");
5071 let s = parse("SELECT a FROM t ORDER BY a DESC").unwrap();
5072 assert!(s.order_by[0].nulls_first, "DESC defaults to NULLS FIRST");
5073 let s = parse("SELECT a FROM t ORDER BY a NULLS FIRST").unwrap();
5074 assert!(s.order_by[0].nulls_first, "an explicit clause wins");
5075 }
5076
5077 #[test]
5078 fn limit_and_offset_parse_in_either_order() {
5079 let s = parse("SELECT a FROM t LIMIT 5 OFFSET 2").unwrap();
5080 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
5081 let s = parse("SELECT a FROM t OFFSET 2 LIMIT 5").unwrap();
5082 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
5083 let s = parse("SELECT a FROM t LIMIT ALL").unwrap();
5084 assert_eq!(s.limit, None);
5085 }
5086
5087 #[test]
5088 fn casts_parse_and_are_recorded_rather_than_rejected() {
5089 let s = parse("SELECT x::int2 FROM t").unwrap();
5091 assert!(matches!(s.items[0].expr, Expr::Cast { .. }));
5092 let s = parse("SELECT x::pg_catalog.int2[] FROM t").unwrap();
5093 match &s.items[0].expr {
5094 Expr::Cast { ty, .. } => assert_eq!(ty, "int2[]"),
5095 other => panic!("{:?}", other),
5096 }
5097 }
5098
5099 #[test]
5100 fn star_and_qualified_star_parse() {
5101 assert_eq!(parse("SELECT * FROM t").unwrap().items[0].expr, Expr::Star);
5102 assert_eq!(parse("SELECT c.* FROM t c").unwrap().items[0].expr,
5103 Expr::QualifiedStar("c".into()));
5104 match &parse("SELECT count(*) FROM t").unwrap().items[0].expr {
5107 Expr::Agg { name, args, order_by, distinct } => {
5108 assert_eq!(name, "count");
5109 assert_eq!(args, &vec![Expr::Star]);
5110 assert!(order_by.is_empty());
5111 assert!(!distinct);
5112 }
5113 other => panic!("{:?}", other),
5114 }
5115 assert!(matches!(
5117 &parse("SELECT lower(s) FROM t").unwrap().items[0].expr,
5118 Expr::Func { name, .. } if name == "lower"
5119 ));
5120 }
5121
5122 #[test]
5123 fn an_aggregate_carries_its_own_DISTINCT_and_ORDER_BY() {
5124 match &parse("SELECT array_agg(a.attname ORDER BY a.ord) FROM t a").unwrap().items[0].expr {
5128 Expr::Agg { name, args, order_by, distinct } => {
5129 assert_eq!(name, "array_agg");
5130 assert_eq!(args.len(), 1);
5131 assert_eq!(order_by.len(), 1);
5132 assert!(matches!(order_by[0].dir, Dir::Asc));
5133 assert!(!distinct);
5134 }
5135 other => panic!("{:?}", other),
5136 }
5137 match &parse("SELECT string_agg(DISTINCT s, ',' ORDER BY b DESC NULLS LAST, c) FROM t").unwrap().items[0].expr {
5140 Expr::Agg { name, args, order_by, distinct } => {
5141 assert_eq!(name, "string_agg");
5142 assert_eq!(args.len(), 2, "the separator is an argument, not a sort key");
5143 assert_eq!(order_by.len(), 2);
5144 assert!(matches!(order_by[0].dir, Dir::Desc));
5145 assert!(!order_by[0].nulls_first, "NULLS LAST overrides the DESC default");
5146 assert!(matches!(order_by[1].dir, Dir::Asc));
5147 assert!(distinct);
5148 }
5149 other => panic!("{:?}", other),
5150 }
5151 assert!(parse("SELECT lower(DISTINCT s) FROM t").is_err());
5154 }
5155
5156 #[test]
5157 fn a_parenthesis_free_function_parses_as_a_zero_arg_call() {
5158 match &parse("SELECT current_schema FROM t").unwrap().items[0].expr {
5160 Expr::Func { name, args } => {
5161 assert_eq!(name, "current_schema");
5162 assert!(args.is_empty());
5163 }
5164 other => panic!("{:?}", other),
5165 }
5166 }
5167
5168 #[test]
5169 fn GROUP_BY_and_HAVING_parse_and_what_remains_is_refused_by_name() {
5170 let s = parse("SELECT a, count(*) FROM t GROUP BY a").unwrap();
5173 assert_eq!(s.group_by, vec![Expr::Column { qual: None, name: "a".into() }]);
5174 assert!(s.having.is_none());
5175
5176 let s = parse("SELECT a, b, count(*) FROM t GROUP BY a, b HAVING count(*) > 1").unwrap();
5177 assert_eq!(s.group_by.len(), 2);
5178 assert!(s.having.is_some());
5179
5180 let e = parse("SELECT a FROM t HAVING a > 1").unwrap_err().to_string();
5184 assert!(e.contains("HAVING needs a GROUP BY"), "{}", e);
5185
5186 for (sql, needle) in [
5187 ("SELECT DISTINCT ON (a) a FROM t", "DISTINCT ON"),
5188 ("SELECT a, count(*) FROM t GROUP BY ROLLUP (a)", "ROLLUP"),
5189 ("SELECT a, count(*) FROM t GROUP BY CUBE (a)", "CUBE"),
5190 ] {
5191 let e = parse(sql).unwrap_err().to_string();
5192 assert!(e.contains(needle), "{} -> {}", sql, e);
5193 }
5194 assert!(parse("SELECT a FROM t JUNK JUNK2").is_err());
5196 }
5197
5198 #[test]
5199 fn THE_dn_QUERY_parses_completely() {
5200 let s = parse(
5201 r#"SELECT n.nspname AS "Name",
5202 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5203 FROM pg_catalog.pg_namespace n
5204 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5205 ORDER BY 1;"#,
5206 )
5207 .expect("psql's \\dn must parse");
5208
5209 assert_eq!(s.items.len(), 2);
5210 assert_eq!(s.items[0].alias, Some("Name".into()));
5211 assert_eq!(s.items[1].alias, Some("Owner".into()));
5212 let from = s.from.unwrap();
5213 assert_eq!(from.name, "pg_catalog.pg_namespace");
5214 assert_eq!(from.binding(), "n");
5215 assert!(s.where_.is_some());
5216 assert_eq!(s.order_by[0].ordinal, Some(1));
5217 }
5218
5219 #[test]
5220 fn THE_dt_QUERY_parses_completely() {
5221 let s = parse(
5222 r#"SELECT n.nspname as "Schema",
5223 c.relname as "Name",
5224 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5225 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5226 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5227 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5228 WHEN 'I' THEN 'partitioned index' END as "Type",
5229 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5230 FROM pg_catalog.pg_class c
5231 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5232 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5233 WHERE c.relkind IN ('r','p','')
5234 AND n.nspname <> 'pg_catalog'
5235 AND n.nspname !~ '^pg_toast'
5236 AND n.nspname <> 'information_schema'
5237 AND pg_catalog.pg_table_is_visible(c.oid)
5238 ORDER BY 1,2;"#,
5239 )
5240 .expect("psql's \\dt must parse");
5241
5242 assert_eq!(s.items.len(), 4);
5243 assert_eq!(s.items[2].alias, Some("Type".into()));
5244 match &s.items[2].expr {
5245 Expr::Case { whens, .. } => assert_eq!(whens.len(), 9, "all nine branches"),
5246 other => panic!("{:?}", other),
5247 }
5248 assert_eq!(s.joins.len(), 2);
5249 assert!(s.joins.iter().all(|j| j.kind == JoinKind::Left && j.on.is_some()));
5250 assert_eq!(s.from.unwrap().binding(), "c");
5251 assert_eq!(s.order_by.len(), 2);
5252 assert_eq!(
5253 (s.order_by[0].ordinal, s.order_by[1].ordinal),
5254 (Some(1), Some(2))
5255 );
5256 }
5257}
5258
5259#[cfg(test)]
5260mod eval_tests {
5261 use super::*;
5262 use serde_json::json;
5263
5264 fn one(row: &Value) -> Bound<'_> {
5266 Bound::new(vec![("t".to_string(), Some(row))])
5267 }
5268
5269 fn ev(sql_expr: &str, row: &Value) -> Result<Value> {
5270 let s = parse(&format!("SELECT {} FROM t", sql_expr))?;
5271 eval(&s.items[0].expr, &one(row))
5272 }
5273
5274 fn v(sql_expr: &str, row: &Value) -> Value {
5275 ev(sql_expr, row).unwrap_or_else(|e| panic!("{}: {}", sql_expr, e))
5276 }
5277
5278 #[test]
5279 fn literals_and_columns_resolve() {
5280 let r = json!({"a": 1, "s": "x", "b": true, "n": null});
5281 assert_eq!(v("42", &r), json!(42));
5282 assert_eq!(v("'hi'", &r), json!("hi"));
5283 assert_eq!(v("NULL", &r), Value::Null);
5284 assert_eq!(v("TRUE", &r), json!(true));
5285 assert_eq!(v("a", &r), json!(1));
5286 assert_eq!(v("t.a", &r), json!(1));
5287 assert_eq!(v("s", &r), json!("x"));
5288 assert_eq!(v("nosuch", &r), Value::Null);
5291 }
5292
5293 #[test]
5294 fn an_unknown_table_ALIAS_is_an_error_while_an_unknown_column_is_null() {
5295 let r = json!({"a": 1});
5298 assert_eq!(v("t.nosuch", &r), Value::Null);
5299 let e = ev("zz.a", &r).unwrap_err().to_string();
5300 assert!(e.contains("zz"), "{}", e);
5301 }
5302
5303 #[test]
5306 fn every_comparison_over_NULL_is_UNKNOWN_including_null_equals_null() {
5307 let r = json!({"n": null, "a": 1});
5308 assert_eq!(v("n = 1", &r), Value::Null);
5309 assert_eq!(v("n != 1", &r), Value::Null);
5310 assert_eq!(v("n < 1", &r), Value::Null);
5311 assert_eq!(v("n = n", &r), Value::Null);
5313 assert_eq!(v("n = NULL", &r), Value::Null);
5314 }
5315
5316 #[test]
5317 fn NOT_UNKNOWN_is_UNKNOWN_not_true() {
5318 let r = json!({"n": null});
5322 assert_eq!(v("NOT (n = 1)", &r), Value::Null);
5323 assert_eq!(v("NOT TRUE", &r), json!(false));
5324 assert_eq!(v("NOT FALSE", &r), json!(true));
5325 }
5326
5327 #[test]
5328 fn AND_and_OR_follow_the_three_valued_truth_tables() {
5329 let r = json!({"n": null});
5330 assert_eq!(v("FALSE AND n = 1", &r), json!(false));
5332 assert_eq!(v("TRUE AND n = 1", &r), Value::Null);
5334 assert_eq!(v("TRUE OR n = 1", &r), json!(true));
5336 assert_eq!(v("FALSE OR n = 1", &r), Value::Null);
5338 assert_eq!(v("TRUE AND TRUE", &r), json!(true));
5340 assert_eq!(v("TRUE AND FALSE", &r), json!(false));
5341 assert_eq!(v("FALSE OR FALSE", &r), json!(false));
5342 }
5343
5344 #[test]
5345 fn IS_NULL_is_the_one_predicate_that_is_never_unknown() {
5346 let r = json!({"n": null, "a": 1});
5347 assert_eq!(v("n IS NULL", &r), json!(true));
5348 assert_eq!(v("n IS NOT NULL", &r), json!(false));
5349 assert_eq!(v("a IS NULL", &r), json!(false));
5350 assert_eq!(v("a IS NOT NULL", &r), json!(true));
5351 assert_eq!(v("nosuch IS NULL", &r), json!(true));
5354 }
5355
5356 #[test]
5357 fn NOT_IN_with_a_NULL_in_the_list_is_UNKNOWN_the_classic_trap() {
5358 let r = json!({"a": 2});
5359 assert_eq!(v("a IN (1, 2)", &r), json!(true));
5360 assert_eq!(v("a IN (1, 3)", &r), json!(false));
5361 assert_eq!(v("a NOT IN (1, 3)", &r), json!(true));
5362 assert_eq!(v("a NOT IN (1, NULL)", &r), Value::Null);
5365 assert_eq!(v("a IN (2, NULL)", &r), json!(true));
5367 assert_eq!(v("nosuch IN (1)", &r), Value::Null);
5369 }
5370
5371 #[test]
5374 fn comparisons_work_across_numbers_strings_and_booleans() {
5375 let r = json!({"n": 5, "s": "b", "t": true});
5376 assert_eq!(v("n > 3", &r), json!(true));
5377 assert_eq!(v("n <= 5", &r), json!(true));
5378 assert_eq!(v("s < 'c'", &r), json!(true));
5379 assert_eq!(v("s > 'c'", &r), json!(false));
5380 assert_eq!(v("n = '5'", &r), json!(true));
5383 assert_eq!(v("n = '5.0'", &r), json!(true));
5384 assert_eq!(v("n = 'five'", &r), json!(false));
5387 }
5388
5389 #[test]
5390 fn the_regex_operators_use_the_SAME_matcher_as_NQL() {
5391 let r = json!({"s": "pg_catalog"});
5394 assert_eq!(v("s ~ '^pg_'", &r), json!(true));
5395 assert_eq!(v("s !~ '^pg_'", &r), json!(false));
5396 assert_eq!(v("s ~ '^PG_'", &r), json!(false));
5397 assert_eq!(v("s ~* '^PG_'", &r), json!(true));
5398 assert_eq!(v("s !~ '^zz'", &r), json!(true));
5399 assert_eq!(v("nosuch ~ '^x'", &r), Value::Null);
5401 assert_eq!(v("s ~ '^(pg_catalog)$'", &r), json!(true));
5404 assert_eq!(v("s ~ '^(pg_.*)$'", &r), json!(true));
5405 assert_eq!(v("s ~ '^(public|pg_catalog)$'", &r), json!(true));
5406 assert_eq!(v("s ~ '^pg_[a-z]+$'", &r), json!(true));
5407 assert_eq!(v("s ~ '^pg_[0-9]+$'", &r), json!(false));
5408 let e = ev("s ~ 'a{2}'", &r).unwrap_err().to_string();
5410 assert!(e.contains("interval"), "{}", e);
5411 }
5412
5413 #[test]
5414 fn like_works_in_all_four_spellings() {
5415 let r = json!({"s": "Acme Pool"});
5416 assert_eq!(v("s LIKE 'Acme%'", &r), json!(true));
5417 assert_eq!(v("s LIKE 'acme%'", &r), json!(false));
5418 assert_eq!(v("s ILIKE 'acme%'", &r), json!(true));
5419 assert_eq!(v("s NOT LIKE 'zz%'", &r), json!(true));
5420
5421 assert_eq!(v(r"s ILIKE 'acme_pool' ESCAPE '/'", &r), json!(true),
5435 "an unescaped _ is still a wildcard, and matches the space");
5436 assert_eq!(v(r"s ILIKE 'acme/_pool' ESCAPE '/'", &r), json!(false),
5437 "escaped _ is a LITERAL underscore, which 'Acme Pool' lacks");
5438 assert_eq!(v(r"s ILIKE 'acme%' ESCAPE '/'", &r), json!(true),
5439 "an unescaped % is still a wildcard");
5440 assert_eq!(v(r"s ILIKE 'acme/%' ESCAPE '/'", &r), json!(false),
5441 "escaped % is a literal percent sign, not 'anything'");
5442 assert_eq!(v(r"s NOT ILIKE 'acme/_pool' ESCAPE '/'", &r), json!(true),
5443 "negation composes with ESCAPE");
5444 assert_eq!(v(r"s ILIKE 'acme/' ESCAPE '/'", &r), json!(false),
5448 "a dangling escape is treated as a literal, never an error");
5449 assert_eq!(v("nosuch LIKE 'x'", &r), Value::Null);
5450 }
5451
5452 #[test]
5453 fn arithmetic_and_concatenation_propagate_null_and_refuse_div_by_zero() {
5454 let r = json!({"a": 7, "b": 2});
5455 assert_eq!(v("a + b", &r), json!(9));
5456 assert_eq!(v("a - b", &r), json!(5));
5457 assert_eq!(v("a * b", &r), json!(14));
5458 assert_eq!(v("a / b", &r), json!(3.5));
5459 assert_eq!(v("a % b", &r), json!(1));
5460 assert_eq!(v("-a", &r), json!(-7));
5461 assert_eq!(v("a / b", &r), json!(3.5));
5464 assert_eq!(v("b / a", &r), json!(2.0 / 7.0));
5465 assert_eq!(v("'x' || 'y'", &r), json!("xy"));
5466 assert_eq!(v("'x' || nosuch", &r), Value::Null);
5467 assert_eq!(v("a + nosuch", &r), Value::Null);
5468 assert!(ev("a / 0", &r).is_err());
5471 assert!(ev("a % 0", &r).is_err());
5472 }
5473
5474 #[test]
5475 fn a_cast_is_transparent_rather_than_rejected() {
5476 let r = json!({"a": 7});
5479 assert_eq!(v("a::int2", &r), json!(7));
5480 assert_eq!(v("a::pg_catalog.int2[]", &r), json!(7));
5481 }
5482
5483 #[test]
5486 fn a_simple_CASE_picks_the_matching_branch() {
5487 let expr = "CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
5489 WHEN 'i' THEN 'index' END";
5490 assert_eq!(v(expr, &json!({"k": "r"})), json!("table"));
5491 assert_eq!(v(expr, &json!({"k": "v"})), json!("view"));
5492 assert_eq!(v(expr, &json!({"k": "i"})), json!("index"));
5493 assert_eq!(v(expr, &json!({"k": "z"})), Value::Null);
5496 }
5497
5498 #[test]
5499 fn a_searched_CASE_evaluates_predicates_and_UNKNOWN_does_not_match() {
5500 let expr = "CASE WHEN n > 5 THEN 'big' WHEN n > 0 THEN 'small' ELSE 'none' END";
5501 assert_eq!(v(expr, &json!({"n": 9})), json!("big"));
5502 assert_eq!(v(expr, &json!({"n": 2})), json!("small"));
5503 assert_eq!(v(expr, &json!({"n": -1})), json!("none"));
5504 assert_eq!(v(expr, &json!({"other": 1})), json!("none"));
5506 }
5507
5508 #[test]
5509 fn an_ELSE_branch_is_used_when_nothing_matches() {
5510 assert_eq!(
5511 v("CASE k WHEN 'r' THEN 'table' ELSE 'other' END", &json!({"k": "z"})),
5512 json!("other")
5513 );
5514 }
5515
5516 #[test]
5519 fn the_catalogue_functions_psql_calls_all_answer() {
5520 let r = json!({"o": 10, "enc": 6});
5521 assert_eq!(v("pg_get_userbyid(o)", &r), json!("nedb"));
5523 assert_eq!(v("pg_catalog.pg_get_userbyid(o)", &r), json!("nedb"));
5524 assert_eq!(v("pg_table_is_visible(o)", &r), json!(true));
5526 assert_eq!(v("pg_encoding_to_char(enc)", &r), json!("UTF8"));
5527 assert_eq!(v("current_schema", &r), json!("public"));
5528 assert_eq!(v("current_database()", &r), json!("nedb"));
5529 assert_eq!(v("current_user", &r), json!("nedb"));
5530 assert_eq!(v("pg_get_expr(o, o)", &r), Value::Null);
5533 assert_eq!(v("obj_description(o)", &r), Value::Null);
5534 }
5535
5536 #[test]
5537 fn text_and_null_handling_functions_work() {
5538 let r = json!({"s": "AbC", "n": null});
5539 assert_eq!(v("lower(s)", &r), json!("abc"));
5540 assert_eq!(v("upper(s)", &r), json!("ABC"));
5541 assert_eq!(v("length(s)", &r), json!(3));
5542 assert_eq!(v("lower(n)", &r), Value::Null);
5543 assert_eq!(v("coalesce(n, 'fallback')", &r), json!("fallback"));
5544 assert_eq!(v("coalesce(s, 'fallback')", &r), json!("AbC"));
5545 assert_eq!(v("coalesce(n, n)", &r), Value::Null);
5546 assert_eq!(v("nullif(s, 'AbC')", &r), Value::Null);
5547 assert_eq!(v("nullif(s, 'zz')", &r), json!("AbC"));
5548 assert_eq!(v("format_type(20, NULL)", &r), json!("bigint"));
5550 }
5551
5552 #[test]
5553 fn coalesce_does_not_evaluate_past_its_first_non_null() {
5554 let r = json!({"a": 1});
5556 assert_eq!(v("coalesce(a, a / 0)", &r), json!(1));
5557 }
5558
5559 #[test]
5560 fn an_unknown_function_is_REFUSED_rather_than_answered_with_NULL() {
5561 let e = ev("pg_stat_get_numscans(1)", &json!({})).unwrap_err().to_string();
5564 assert!(e.contains("pg_stat_get_numscans"), "{}", e);
5565 assert!(e.contains("refused"), "{}", e);
5566 }
5567
5568 #[test]
5571 fn a_qualified_column_reads_only_its_OWN_binding() {
5572 let a = json!({"name": "left", "x": 1});
5575 let b = json!({"name": "right", "y": 2});
5576 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), Some(&b))]);
5577 let get = |e: &str| {
5578 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5579 eval(&s.items[0].expr, &row).unwrap()
5580 };
5581 assert_eq!(get("a.name"), json!("left"));
5582 assert_eq!(get("b.name"), json!("right"));
5583 assert_eq!(get("name"), json!("left"));
5585 assert_eq!(get("y"), json!(2), "a bare name still finds a later binding");
5586 }
5587
5588 #[test]
5589 fn an_unmatched_LEFT_JOIN_side_reads_as_NULL_not_as_a_missing_column() {
5590 let a = json!({"x": 1});
5593 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), None)]);
5594 let get = |e: &str| {
5595 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5596 eval(&s.items[0].expr, &row).unwrap()
5597 };
5598 assert_eq!(get("b.anything"), Value::Null);
5599 assert_eq!(get("b.anything IS NULL"), json!(true));
5600 assert_eq!(get("a.x"), json!(1));
5601 }
5602}
5603
5604#[cfg(test)]
5605mod exec_tests {
5606 use super::*;
5607 use serde_json::json;
5608
5609 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5611 let owned: Vec<(String, Vec<Value>)> =
5612 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
5613 move |name: &str| {
5614 let bare = name.rsplit('.').next().unwrap_or(name);
5616 Ok(owned
5617 .iter()
5618 .find(|(n, _)| n == name || n == bare)
5619 .map(|(_, r)| from_vec(r.clone())))
5620 }
5621 }
5622
5623 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
5624 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
5625 (cols.into_iter().map(|c| c.name).collect(), rows)
5626 }
5627
5628 fn col(rows: &[Value], name: &str) -> Vec<Value> {
5629 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
5630 }
5631
5632 #[test]
5635 fn select_columns_where_order_limit_offset() {
5636 let t = tables(vec![(
5637 "t",
5638 vec![json!({"a": 3, "s": "c"}), json!({"a": 1, "s": "a"}), json!({"a": 2, "s": "b"})],
5639 )]);
5640 let (names, rows) = go("SELECT a, s FROM t ORDER BY a", &t);
5641 assert_eq!(names, vec!["a", "s"]);
5642 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2), json!(3)]);
5643
5644 let (_, rows) = go("SELECT a FROM t ORDER BY a DESC", &t);
5645 assert_eq!(col(&rows, "a"), vec![json!(3), json!(2), json!(1)]);
5646
5647 let (_, rows) = go("SELECT a FROM t WHERE a > 1 ORDER BY a", &t);
5648 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5649
5650 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 2", &t);
5651 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5652
5653 let (_, rows) = go("SELECT a FROM t ORDER BY a OFFSET 1", &t);
5654 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5655
5656 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 1 OFFSET 1", &t);
5657 assert_eq!(col(&rows, "a"), vec![json!(2)]);
5658
5659 let (_, rows) = go("SELECT a FROM t OFFSET 99", &t);
5661 assert!(rows.is_empty());
5662 }
5663
5664 #[test]
5665 fn an_output_column_takes_its_alias_or_a_derived_name() {
5666 let t = tables(vec![("t", vec![json!({"a": 1})])]);
5669 assert_eq!(go(r#"SELECT a AS "Name" FROM t"#, &t).0, vec!["Name"]);
5670 assert_eq!(go("SELECT a FROM t", &t).0, vec!["a"]);
5671 assert_eq!(go("SELECT lower('X') FROM t", &t).0, vec!["lower"]);
5672 assert_eq!(go("SELECT 1 + 1 FROM t", &t).0, vec!["?column?"]);
5673 assert_eq!(go("SELECT CASE a WHEN 1 THEN 'x' END FROM t", &t).0, vec!["case"]);
5674 }
5675
5676 #[test]
5677 fn star_expands_from_the_rows_and_a_qualified_star_from_one_binding() {
5678 let t = tables(vec![
5679 ("a", vec![json!({"x": 1, "y": 2})]),
5680 ("b", vec![json!({"z": 3})]),
5681 ]);
5682 let (names, rows) = go("SELECT * FROM a", &t);
5683 assert_eq!(names, vec!["x", "y"]);
5684 assert_eq!(rows.len(), 1);
5685
5686 let (names, _) = go("SELECT a.* FROM a CROSS JOIN b", &t);
5687 assert_eq!(names, vec!["x", "y"], "a qualified star takes ONE binding");
5688
5689 let empty = tables(vec![("e", vec![])]);
5692 assert_eq!(go("SELECT * FROM e", &empty).0, Vec::<String>::new());
5693 }
5694
5695 #[test]
5696 fn distinct_dedupes_on_the_projected_values() {
5697 let t = tables(vec![(
5698 "t",
5699 vec![json!({"g": "x"}), json!({"g": "x"}), json!({"g": "y"})],
5700 )]);
5701 let (_, rows) = go("SELECT DISTINCT g FROM t ORDER BY 1", &t);
5702 assert_eq!(col(&rows, "g"), vec![json!("x"), json!("y")]);
5703 let (_, rows) = go("SELECT g FROM t", &t);
5704 assert_eq!(rows.len(), 3, "without DISTINCT every row survives");
5705 }
5706
5707 #[test]
5708 fn order_by_an_ORDINAL_sorts_the_projected_column() {
5709 let t = tables(vec![(
5710 "t",
5711 vec![json!({"a": 2, "b": "z"}), json!({"a": 1, "b": "y"})],
5712 )]);
5713 let (_, rows) = go("SELECT a, b FROM t ORDER BY 1", &t);
5714 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5715 let (_, rows) = go("SELECT a, b FROM t ORDER BY 2 DESC", &t);
5716 assert_eq!(col(&rows, "b"), vec![json!("z"), json!("y")]);
5717 let e = run("SELECT a FROM t ORDER BY 3", &t).unwrap_err().to_string();
5719 assert!(e.contains("out of range"), "{}", e);
5720 }
5721
5722 #[test]
5723 fn order_by_an_expression_may_use_a_column_NOT_in_the_select_list() {
5724 let t = tables(vec![(
5725 "t",
5726 vec![json!({"a": 1, "hidden": 9}), json!({"a": 2, "hidden": 1})],
5727 )]);
5728 let (_, rows) = go("SELECT a FROM t ORDER BY hidden", &t);
5729 assert_eq!(col(&rows, "a"), vec![json!(2), json!(1)]);
5730 }
5731
5732 #[test]
5733 fn null_ordering_follows_the_direction_defaults() {
5734 let t = tables(vec![(
5735 "t",
5736 vec![json!({"a": 2}), json!({"a": null}), json!({"a": 1})],
5737 )]);
5738 assert_eq!(col(&go("SELECT a FROM t ORDER BY a", &t).1, "a"),
5740 vec![json!(1), json!(2), Value::Null]);
5741 assert_eq!(col(&go("SELECT a FROM t ORDER BY a DESC", &t).1, "a"),
5743 vec![Value::Null, json!(2), json!(1)]);
5744 assert_eq!(col(&go("SELECT a FROM t ORDER BY a NULLS FIRST", &t).1, "a"),
5746 vec![Value::Null, json!(1), json!(2)]);
5747 }
5748
5749 #[test]
5750 fn a_where_clause_that_is_UNKNOWN_excludes_the_row() {
5751 let t = tables(vec![(
5752 "t",
5753 vec![json!({"a": 1}), json!({"a": null}), json!({"other": 1})],
5754 )]);
5755 let (_, rows) = go("SELECT a FROM t WHERE a = 1", &t);
5757 assert_eq!(rows.len(), 1);
5758 let (_, rows) = go("SELECT a FROM t WHERE NOT (a = 1)", &t);
5760 assert_eq!(rows.len(), 0, "NOT UNKNOWN must not resurrect a null row");
5761 }
5762
5763 #[test]
5764 fn select_with_no_FROM_returns_exactly_one_row() {
5765 let t = tables(vec![]);
5767 let (names, rows) = go("SELECT 1", &t);
5768 assert_eq!(rows.len(), 1);
5769 assert_eq!(names, vec!["?column?"]);
5770 assert_eq!(go("SELECT current_schema", &t).1.len(), 1);
5771 }
5772
5773 #[test]
5774 fn an_unknown_relation_is_NAMED_rather_than_answered_with_no_rows() {
5775 let t = tables(vec![("t", vec![])]);
5778 let e = run("SELECT a FROM nosuchtable", &t).unwrap_err().to_string();
5779 assert!(e.contains("nosuchtable"), "{}", e);
5780 assert!(e.contains("does not exist"), "{}", e);
5781 }
5782
5783 #[test]
5786 fn an_inner_join_keeps_only_matching_pairs() {
5787 let t = tables(vec![
5788 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5789 ("r", vec![json!({"lid": 1, "v": "x"})]),
5790 ]);
5791 let (_, rows) = go("SELECT l.n, r.v FROM l JOIN r ON r.lid = l.id", &t);
5792 assert_eq!(rows.len(), 1);
5793 assert_eq!(col(&rows, "n"), vec![json!("a")]);
5794 }
5795
5796 #[test]
5797 fn a_LEFT_join_keeps_unmatched_left_rows_with_NULLs() {
5798 let t = tables(vec![
5800 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5801 ("r", vec![json!({"lid": 1, "v": "x"})]),
5802 ]);
5803 let (_, rows) = go("SELECT l.n, r.v FROM l LEFT JOIN r ON r.lid = l.id ORDER BY 1", &t);
5804 assert_eq!(rows.len(), 2);
5805 assert_eq!(col(&rows, "n"), vec![json!("a"), json!("b")]);
5806 assert_eq!(col(&rows, "v"), vec![json!("x"), Value::Null]);
5807 }
5808
5809 #[test]
5810 fn a_RIGHT_join_keeps_unmatched_right_rows_and_FULL_keeps_both() {
5811 let t = tables(vec![
5812 ("l", vec![json!({"id": 1})]),
5813 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5814 ]);
5815 let (_, rows) = go("SELECT l.id, r.lid FROM l RIGHT JOIN r ON r.lid = l.id", &t);
5816 assert_eq!(rows.len(), 2);
5817 assert!(col(&rows, "id").contains(&Value::Null), "the unmatched right row keeps NULLs on the left");
5818
5819 let t2 = tables(vec![
5820 ("l", vec![json!({"id": 1}), json!({"id": 5})]),
5821 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5822 ]);
5823 let (_, rows) = go("SELECT l.id, r.lid FROM l FULL OUTER JOIN r ON r.lid = l.id", &t2);
5824 assert_eq!(rows.len(), 3, "one match plus one orphan on each side");
5825 }
5826
5827 #[test]
5828 fn a_cross_join_is_the_cartesian_product() {
5829 let t = tables(vec![
5830 ("a", vec![json!({"x": 1}), json!({"x": 2})]),
5831 ("b", vec![json!({"y": 1}), json!({"y": 2}), json!({"y": 3})]),
5832 ]);
5833 assert_eq!(go("SELECT a.x, b.y FROM a CROSS JOIN b", &t).1.len(), 6);
5834 assert_eq!(go("SELECT a.x, b.y FROM a, b", &t).1.len(), 6);
5836 }
5837
5838 #[test]
5839 fn an_ON_clause_that_is_UNKNOWN_does_not_join() {
5840 let t = tables(vec![
5843 ("l", vec![json!({"id": null})]),
5844 ("r", vec![json!({"lid": null})]),
5845 ]);
5846 let (_, rows) = go("SELECT l.id FROM l JOIN r ON r.lid = l.id", &t);
5847 assert!(rows.is_empty(), "NULL = NULL is UNKNOWN, so nothing joins");
5848 let (_, rows) = go("SELECT l.id FROM l LEFT JOIN r ON r.lid = l.id", &t);
5850 assert_eq!(rows.len(), 1);
5851 }
5852
5853 #[test]
5854 fn two_joins_chain() {
5855 let t = tables(vec![
5856 ("a", vec![json!({"id": 1, "bid": 10, "cid": 100})]),
5857 ("b", vec![json!({"id": 10, "bn": "B"})]),
5858 ("c", vec![json!({"id": 100, "cn": "C"})]),
5859 ]);
5860 let (_, rows) = go(
5861 "SELECT a.id, b.bn, c.cn FROM a \
5862 LEFT JOIN b ON b.id = a.bid \
5863 LEFT JOIN c ON c.id = a.cid",
5864 &t,
5865 );
5866 assert_eq!(rows.len(), 1);
5867 assert_eq!(col(&rows, "bn"), vec![json!("B")]);
5868 assert_eq!(col(&rows, "cn"), vec![json!("C")]);
5869 }
5870
5871 fn catalog() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5875 tables(vec![
5876 (
5877 "pg_namespace",
5878 vec![
5879 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5880 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5881 json!({"oid": 13000, "nspname": "information_schema", "nspowner": 10}),
5882 ],
5883 ),
5884 (
5885 "pg_class",
5886 vec![
5887 json!({"oid": 16401, "relname": "orders", "relnamespace": 2200,
5888 "relkind": "r", "relowner": 10, "relam": 2}),
5889 json!({"oid": 16402, "relname": "drivers", "relnamespace": 2200,
5890 "relkind": "r", "relowner": 10, "relam": 2}),
5891 ],
5892 ),
5893 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5894 ])
5895 }
5896
5897 #[test]
5898 fn THE_dn_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5899 let (names, rows) = go(
5900 r#"SELECT n.nspname AS "Name",
5901 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5902 FROM pg_catalog.pg_namespace n
5903 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5904 ORDER BY 1;"#,
5905 &catalog(),
5906 );
5907
5908 assert_eq!(names, vec!["Name", "Owner"], "psql reads these BY NAME");
5909 assert_eq!(col(&rows, "Name"), vec![json!("public")]);
5912 assert_eq!(col(&rows, "Owner"), vec![json!("nedb")]);
5913 }
5914
5915 #[test]
5916 fn THE_dt_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5917 let (names, rows) = go(
5918 r#"SELECT n.nspname as "Schema",
5919 c.relname as "Name",
5920 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5921 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5922 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5923 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5924 WHEN 'I' THEN 'partitioned index' END as "Type",
5925 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5926 FROM pg_catalog.pg_class c
5927 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5928 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5929 WHERE c.relkind IN ('r','p','')
5930 AND n.nspname <> 'pg_catalog'
5931 AND n.nspname !~ '^pg_toast'
5932 AND n.nspname <> 'information_schema'
5933 AND pg_catalog.pg_table_is_visible(c.oid)
5934 ORDER BY 1,2;"#,
5935 &catalog(),
5936 );
5937
5938 assert_eq!(names, vec!["Schema", "Name", "Type", "Owner"]);
5939 assert_eq!(col(&rows, "Name"), vec![json!("drivers"), json!("orders")]);
5941 assert_eq!(col(&rows, "Schema"), vec![json!("public"), json!("public")]);
5942 assert_eq!(col(&rows, "Type"), vec![json!("table"), json!("table")]);
5944 assert_eq!(col(&rows, "Owner"), vec![json!("nedb"), json!("nedb")]);
5945 }
5946
5947 #[test]
5948 fn the_dt_query_still_filters_correctly_with_a_system_relation_present() {
5949 let t = tables(vec![
5953 (
5954 "pg_namespace",
5955 vec![
5956 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5957 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5958 ],
5959 ),
5960 (
5961 "pg_class",
5962 vec![
5963 json!({"oid": 1, "relname": "mine", "relnamespace": 2200,
5964 "relkind": "r", "relowner": 10, "relam": 2}),
5965 json!({"oid": 2, "relname": "pg_internal", "relnamespace": 11,
5966 "relkind": "r", "relowner": 10, "relam": 2}),
5967 json!({"oid": 3, "relname": "an_index", "relnamespace": 2200,
5968 "relkind": "i", "relowner": 10, "relam": 2}),
5969 ],
5970 ),
5971 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5972 ]);
5973 let (_, rows) = go(
5974 r#"SELECT c.relname as "Name" FROM pg_catalog.pg_class c
5975 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5976 WHERE c.relkind IN ('r','p','') AND n.nspname <> 'pg_catalog'
5977 ORDER BY 1"#,
5978 &t,
5979 );
5980 assert_eq!(col(&rows, "Name"), vec![json!("mine")],
5981 "a system relation and an index must both be filtered out");
5982 }
5983}
5984
5985#[cfg(test)]
5986mod operator_syntax_tests {
5987 use super::*;
5988 use serde_json::json;
5989
5990 #[test]
5991 fn the_OPERATOR_qualification_psql_generates_is_understood() {
5992 let s = parse(
5996 "SELECT a FROM t WHERE n OPERATOR(pg_catalog.~) '^x' \
5997 AND m OPERATOR(pg_catalog.=) 1",
5998 )
5999 .expect("psql's OPERATOR() form must parse");
6000 match s.where_.unwrap() {
6001 Expr::Binary { op, left, .. } => {
6002 assert_eq!(op, "AND");
6003 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "~"));
6004 }
6005 other => panic!("{:?}", other),
6006 }
6007 }
6008
6009 #[test]
6010 fn an_OPERATOR_qualified_comparison_EVALUATES() {
6011 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
6012 Ok(Some(from_vec(vec![json!({"n": "orders"}), json!({"n": "pg_toast_1"})])))
6013 };
6014 let (_, rows) = run(
6015 "SELECT n FROM pg_class WHERE n OPERATOR(pg_catalog.~) '^ord'",
6016 &t,
6017 )
6018 .unwrap();
6019 assert_eq!(rows.len(), 1);
6020 assert_eq!(rows[0]["n"], json!("orders"));
6021 }
6022
6023 #[test]
6024 fn a_subquery_an_ARRAY_constructor_and_EXISTS_all_PARSE() {
6025 let s = parse("SELECT a FROM t WHERE x = (SELECT 1)").unwrap();
6028 assert!(matches!(s.where_, Some(Expr::Binary { ref right, .. }) if matches!(**right, Expr::Subquery(_))));
6029 let s = parse("SELECT array_to_string(ARRAY(SELECT a FROM b), ',') FROM t").unwrap();
6030 assert!(matches!(&s.items[0].expr, Expr::Func { args, .. } if matches!(args[0], Expr::ArrayQuery(_))));
6031 let s = parse("SELECT a FROM t WHERE EXISTS (SELECT 1)").unwrap();
6032 assert!(matches!(s.where_, Some(Expr::Exists { negated: false, .. })));
6033 let s = parse("SELECT a FROM t WHERE NOT EXISTS (SELECT 1)").unwrap();
6034 assert!(matches!(s.where_, Some(Expr::Unary { ref expr, .. }) if matches!(**expr, Expr::Exists { .. })));
6035 let s = parse("SELECT a FROM t WHERE oid = ANY (polroles) AND 'd' = any(kinds) AND x <> ALL (SELECT y FROM u)").unwrap();
6037 assert!(s.where_.is_some());
6038 let s = parse("SELECT prattrs[s] FROM t").unwrap();
6039 assert!(matches!(s.items[0].expr, Expr::Index { .. }));
6040 let s = parse("SELECT CAST('tuple' AS pg_catalog.text), CAST(n AS int2[]) FROM t").unwrap();
6041 assert!(matches!(&s.items[0].expr, Expr::Cast { ty, .. } if ty == "text"));
6042 assert!(matches!(&s.items[1].expr, Expr::Cast { ty, .. } if ty == "int2[]"));
6043 let s = parse("SELECT a FROM t WHERE a IS DISTINCT FROM b").unwrap();
6044 assert!(matches!(s.where_, Some(Expr::Binary { ref op, .. }) if op == "IS DISTINCT FROM"));
6045 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();
6047 assert_eq!(s.joins.len(), 3);
6048 assert!(matches!(s.joins[1].kind, JoinKind::Cross));
6049 let s = parse("SELECT 1 FROM c, LATERAL (SELECT 2 AS two) s").unwrap();
6051 assert!(s.joins[0].table.lateral && s.joins[0].table.sub.is_some());
6052 let s = parse("SELECT tt.a FROM (SELECT 1 AS a UNION ALL SELECT 2) AS tt ORDER BY 1").unwrap();
6053 assert_eq!(s.from.as_ref().unwrap().sub.as_ref().unwrap().set_ops.len(), 1);
6054 }
6055
6056 #[test]
6057 fn a_compound_query_keeps_ORDER_BY_for_the_whole() {
6058 let s = parse("SELECT a FROM t UNION SELECT b FROM u UNION ALL SELECT c FROM v ORDER BY 1 LIMIT 5").unwrap();
6059 assert_eq!(s.set_ops.len(), 2);
6060 assert_eq!(s.set_ops[0].op, SetOp::Union);
6061 assert!(!s.set_ops[0].all);
6062 assert!(s.set_ops[1].all);
6063 assert_eq!(s.order_by.len(), 1);
6064 assert_eq!(s.limit, Some(5));
6065 assert!(s.set_ops[1].query.order_by.is_empty(), "the tail belongs to the whole, not the last arm");
6066 }
6067}
6068
6069#[cfg(test)]
6070mod subquery_exec_tests {
6071 use super::*;
6072 use serde_json::json;
6073
6074 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
6075 let owned: Vec<(String, Vec<Value>)> =
6076 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
6077 move |name: &str| {
6078 let bare = name.rsplit('.').next().unwrap_or(name);
6079 Ok(owned.iter().find(|(n, _)| n == name || n == bare).map(|(_, r)| from_vec(r.clone())))
6080 }
6081 }
6082
6083 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
6084 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
6085 (cols.into_iter().map(|c| c.name).collect(), rows)
6086 }
6087
6088 fn col(rows: &[Value], name: &str) -> Vec<Value> {
6089 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
6090 }
6091
6092 fn shop() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
6093 tables(vec![
6094 ("c", vec![
6095 json!({"id": 1, "name": "ann", "tags": ["a", "b"]}),
6096 json!({"id": 2, "name": "bob", "tags": []}),
6097 json!({"id": 3, "name": "cyd", "tags": null}),
6098 ]),
6099 ("o", vec![
6100 json!({"oid": 10, "cid": 1, "total": 5}),
6101 json!({"oid": 11, "cid": 1, "total": 7}),
6102 json!({"oid": 12, "cid": 2, "total": 9}),
6103 ]),
6104 ])
6105 }
6106
6107 #[test]
6108 fn a_correlated_scalar_subquery_sees_the_outer_row() {
6109 let t = shop();
6110 let (_, rows) = go(
6111 "SELECT c.name, (SELECT sum(o.total) FROM o WHERE o.cid = c.id) AS spent FROM c ORDER BY c.id",
6112 &t,
6113 );
6114 assert_eq!(col(&rows, "spent"), vec![json!(12), json!(9), Value::Null]);
6115 let e = run("SELECT (SELECT o.total FROM o WHERE o.cid = c.id) FROM c", &t).unwrap_err().to_string();
6117 assert!(e.contains("more than one row"), "{}", e);
6118 let e = run("SELECT (SELECT oid, total FROM o) FROM c", &t).unwrap_err().to_string();
6120 assert!(e.contains("exactly one column"), "{}", e);
6121 }
6122
6123 #[test]
6124 fn EXISTS_and_NOT_EXISTS_are_never_unknown() {
6125 let t = shop();
6126 let (_, rows) = go("SELECT c.name FROM c WHERE EXISTS (SELECT 1 FROM o WHERE o.cid = c.id) ORDER BY 1", &t);
6127 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6128 let (_, rows) = go("SELECT c.name FROM c WHERE NOT EXISTS (SELECT 1 FROM o WHERE o.cid = c.id)", &t);
6129 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6130 }
6131
6132 #[test]
6133 fn ARRAY_of_a_subquery_and_array_to_string_compose_like_psql_dp() {
6134 let t = shop();
6135 let (_, rows) = go(
6136 "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",
6137 &t,
6138 );
6139 assert_eq!(col(&rows, "totals"), vec![json!("5,7"), json!("9"), json!("")]);
6141 let (_, rows) = go("SELECT array_length(ARRAY(SELECT oid FROM o), 1) AS n FROM c WHERE c.id = 1", &t);
6142 assert_eq!(col(&rows, "n"), vec![json!(3)]);
6143 }
6144
6145 #[test]
6146 fn ANY_ALL_and_IN_over_arrays_and_subqueries() {
6147 let t = shop();
6148 let (_, rows) = go("SELECT c.name FROM c WHERE 'a' = ANY (c.tags) ORDER BY 1", &t);
6149 assert_eq!(col(&rows, "name"), vec![json!("ann")]);
6150 let (_, rows) = go("SELECT c.name FROM c WHERE 'zz' = ANY (c.tags)", &t);
6152 assert!(rows.is_empty());
6153 let (_, rows) = go("SELECT c.name FROM c WHERE c.id = ANY (SELECT o.cid FROM o) ORDER BY 1", &t);
6154 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6155 let (_, rows) = go("SELECT c.name FROM c WHERE c.id <> ALL (SELECT o.cid FROM o)", &t);
6156 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6157 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);
6158 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6159 let (_, rows) = go("SELECT c.name FROM c WHERE c.id NOT IN (SELECT o.cid FROM o)", &t);
6160 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6161 let (_, rows) = go("SELECT c.tags[2] AS second FROM c WHERE c.id = 1", &t);
6163 assert_eq!(col(&rows, "second"), vec![json!("b")]);
6164 }
6165
6166 #[test]
6167 fn set_operations_combine_arms_and_sort_the_whole() {
6168 let t = shop();
6169 let (names, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1", &t);
6170 assert_eq!(names, vec!["k"], "column names come from the first arm");
6171 assert_eq!(col(&rows, "k"), vec![json!(1), json!(1), json!(1), json!(2), json!(2), json!(3)]);
6172 let (_, rows) = go("SELECT c.id AS k FROM c UNION SELECT o.cid FROM o ORDER BY 1", &t);
6173 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2), json!(3)]);
6174 let (_, rows) = go("SELECT c.id AS k FROM c INTERSECT SELECT o.cid FROM o ORDER BY 1", &t);
6175 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2)]);
6176 let (_, rows) = go("SELECT c.id AS k FROM c EXCEPT SELECT o.cid FROM o", &t);
6177 assert_eq!(col(&rows, "k"), vec![json!(3)]);
6178 let (_, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1 DESC LIMIT 2", &t);
6179 assert_eq!(col(&rows, "k"), vec![json!(3), json!(2)]);
6180 let e = run("SELECT c.id FROM c UNION SELECT o.oid, o.cid FROM o", &t).unwrap_err().to_string();
6181 assert!(e.contains("same number of columns"), "{}", e);
6182 }
6183
6184 #[test]
6185 fn a_derived_table_is_a_relation_and_LATERAL_sees_its_left() {
6186 let t = shop();
6187 let (_, rows) = go(
6188 "SELECT tt.who FROM (SELECT c.name AS who FROM c WHERE c.id < 3) AS tt ORDER BY 1",
6189 &t,
6190 );
6191 assert_eq!(col(&rows, "who"), vec![json!("ann"), json!("bob")]);
6192 let (_, rows) = go("SELECT tt.x FROM (SELECT c.name FROM c WHERE c.id = 1) AS tt(x)", &t);
6194 assert_eq!(col(&rows, "x"), vec![json!("ann")]);
6195 let (_, rows) = go(
6197 "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\"",
6198 &t,
6199 );
6200 assert_eq!(col(&rows, "Name"), vec![json!("ann"), json!("bob"), json!("cyd")]);
6201 assert_eq!(col(&rows, "Orders"), vec![json!(2), json!(1), json!(0)]);
6202 }
6203
6204 #[test]
6205 fn table_functions_generate_series_and_unnest() {
6206 let t = shop();
6207 let (_, rows) = go("SELECT s.generate_series AS n FROM generate_series(1, 3) s", &t);
6208 assert_eq!(col(&rows, "n"), vec![json!(1), json!(2), json!(3)]);
6209 let (_, rows) = go("SELECT x FROM pg_catalog.unnest(ARRAY['p', 'q']) AS t(x)", &t);
6210 assert_eq!(col(&rows, "x"), vec![json!("p"), json!("q")]);
6211 let (_, rows) = go(
6213 "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",
6214 &t,
6215 );
6216 assert_eq!(col(&rows, "tags"), vec![json!("a, b"), json!(""), json!("")]);
6217 let e = run("SELECT 1 FROM nosuchfn(1) f", &t).unwrap_err().to_string();
6218 assert!(e.contains("table function nosuchfn()"), "{}", e);
6219 }
6220
6221 #[test]
6222 fn aggregates_without_GROUP_BY_collapse_to_one_row() {
6223 let t = shop();
6224 let (names, rows) = go(
6225 "SELECT count(*), count(c.tags) AS tagged, min(c.name), max(c.name) AS hi, string_agg(c.name, '|') AS all FROM c",
6226 &t,
6227 );
6228 assert_eq!(names, vec!["count", "tagged", "min", "hi", "all"]);
6229 assert_eq!(rows.len(), 1);
6230 assert_eq!(rows[0]["count"], json!(3));
6231 assert_eq!(rows[0]["tagged"], json!(2), "count(x) skips NULL");
6232 assert_eq!(rows[0]["min"], json!("ann"));
6233 assert_eq!(rows[0]["hi"], json!("cyd"));
6234 assert_eq!(rows[0]["all"], json!("ann|bob|cyd"));
6235 let (_, rows) = go("SELECT count(*) AS n, sum(o.total) AS s FROM o WHERE o.total > 100", &t);
6237 assert_eq!(rows[0]["n"], json!(0));
6238 assert_eq!(rows[0]["s"], Value::Null);
6239 let (_, rows) = go("SELECT sum(o.total) / count(*) AS avg_total, avg(o.total) AS a FROM o", &t);
6242 assert_eq!(rows[0]["avg_total"], json!(7));
6243 assert_eq!(rows[0]["a"], json!(7));
6244 let e = run("SELECT c.name, count(*) FROM c", &t).unwrap_err().to_string();
6245 assert!(e.contains("must appear in the GROUP BY clause"), "{}", e);
6246 }
6247
6248 #[test]
6249 fn IS_DISTINCT_FROM_is_null_safe() {
6250 let t = shop();
6251 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS DISTINCT FROM NULL ORDER BY 1", &t);
6252 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6253 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS NOT DISTINCT FROM NULL", &t);
6254 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6255 }
6256
6257 #[test]
6258 fn THE_dT_QUERY_RUNS_over_a_catalogue_fixture() {
6259 let t = tables(vec![
6261 ("pg_namespace", vec![
6262 json!({"oid": 11, "nspname": "pg_catalog"}),
6263 json!({"oid": 2200, "nspname": "public"}),
6264 ]),
6265 ("pg_type", vec![
6266 json!({"oid": 25, "typname": "text", "typnamespace": 11, "typrelid": 0, "typelem": 0, "typarray": 1009}),
6267 json!({"oid": 1009, "typname": "_text", "typnamespace": 11, "typrelid": 0, "typelem": 25, "typarray": 0}),
6268 json!({"oid": 70000, "typname": "mood", "typnamespace": 2200, "typrelid": 0, "typelem": 0, "typarray": 70001}),
6269 json!({"oid": 70001, "typname": "_mood", "typnamespace": 2200, "typrelid": 0, "typelem": 70000, "typarray": 0}),
6270 ]),
6271 ("pg_class", vec![]),
6272 ]);
6273 let (_, rows) = go(
6274 r#"SELECT n.nspname as "Schema",
6275 pg_catalog.format_type(t.oid, NULL) AS "Name",
6276 pg_catalog.obj_description(t.oid, 'pg_type') as "Description"
6277 FROM pg_catalog.pg_type t
6278 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
6279 WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
6280 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
6281 AND n.nspname <> 'pg_catalog'
6282 AND n.nspname <> 'information_schema'
6283 AND pg_catalog.pg_type_is_visible(t.oid)
6284 ORDER BY 1, 2;"#,
6285 &t,
6286 );
6287 assert_eq!(rows.len(), 1, "{:?}", rows);
6290 assert_eq!(col(&rows, "Schema"), vec![json!("public")]);
6291 }
6292}
6293
6294#[cfg(test)]
6295mod collate_tests {
6296 use super::*;
6297 use serde_json::json;
6298
6299 #[test]
6300 fn COLLATE_is_consumed_because_it_cannot_change_the_answer() {
6301 for sql in [
6305 r#"SELECT a FROM t ORDER BY a COLLATE "C""#,
6306 r#"SELECT a COLLATE "C" FROM t"#,
6307 r#"SELECT a FROM t WHERE a COLLATE pg_catalog."C" = 'x'"#,
6308 ] {
6309 parse(sql).unwrap_or_else(|e| panic!("{} -> {}", sql, e));
6310 }
6311 assert!(parse("SELECT a FROM t ORDER BY a COLLATE").is_err());
6313 }
6314
6315 #[test]
6316 fn a_COLLATE_annotated_comparison_still_evaluates() {
6317 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
6318 Ok(Some(from_vec(vec![json!({"n": "b"}), json!({"n": "a"})])))
6319 };
6320 let (_, rows) = run(r#"SELECT n FROM pg_class ORDER BY n COLLATE "C""#, &t).unwrap();
6321 assert_eq!(rows[0]["n"], json!("a"), "the ORDER BY still sorts");
6322 }
6323}