1use crate::sqljoin::{self, JoinExec, Strategy};
42use crate::sqlplan::{Plan, Stage};
43use crate::sqlpush::Pushdown;
44
45use anyhow::{bail, Result};
46use serde_json::{Map, Value};
47
48#[derive(Debug, Clone, PartialEq)]
53pub enum Tok {
54 Word { upper: String, raw: String },
59 Quoted(String),
62 Str(String),
64 Num(f64),
65 Op(String),
66 Punct(char),
67 Eof,
68}
69
70impl Tok {
71 fn is_kw(&self, kw: &str) -> bool {
72 matches!(self, Tok::Word { upper, .. } if upper == kw)
73 }
74 #[allow(dead_code)] fn ident(&self) -> Option<String> {
77 match self {
78 Tok::Word { raw, .. } => Some(raw.clone()),
79 Tok::Quoted(s) => Some(s.clone()),
80 _ => None,
81 }
82 }
83}
84
85const OPERATORS: &[&str] = &[
89 "!~*", "!~", "~*", "<>", "!=", ">=", "<=", "||", "::",
90 "=", "<", ">", "~", "+", "-", "*", "/", "%",
91];
92
93pub fn lex(src: &str) -> Result<Vec<Tok>> {
94 let b: Vec<char> = src.chars().collect();
95 let mut out = vec![];
96 let mut i = 0usize;
97
98 while i < b.len() {
99 let c = b[i];
100
101 if c.is_whitespace() {
103 i += 1;
104 continue;
105 }
106
107 if c == '-' && b.get(i + 1) == Some(&'-') {
109 while i < b.len() && b[i] != '\n' {
110 i += 1;
111 }
112 continue;
113 }
114
115 if c == '/' && b.get(i + 1) == Some(&'*') {
117 let mut depth = 1usize;
118 i += 2;
119 while i < b.len() && depth > 0 {
120 if b[i] == '/' && b.get(i + 1) == Some(&'*') {
121 depth += 1;
122 i += 2;
123 } else if b[i] == '*' && b.get(i + 1) == Some(&'/') {
124 depth -= 1;
125 i += 2;
126 } else {
127 i += 1;
128 }
129 }
130 if depth > 0 {
131 bail!("unterminated /* comment");
132 }
133 continue;
134 }
135
136 if c == '\'' {
138 i += 1;
139 let mut s = String::new();
140 loop {
141 match b.get(i) {
142 None => bail!("unterminated string literal"),
143 Some('\'') if b.get(i + 1) == Some(&'\'') => {
144 s.push('\'');
145 i += 2;
146 }
147 Some('\'') => {
148 i += 1;
149 break;
150 }
151 Some(ch) => {
152 s.push(*ch);
153 i += 1;
154 }
155 }
156 }
157 out.push(Tok::Str(s));
158 continue;
159 }
160
161 if (c == 'E' || c == 'e') && b.get(i + 1) == Some(&'\'') {
164 i += 2;
165 let mut s = String::new();
166 loop {
167 match b.get(i) {
168 None => bail!("unterminated E'' string literal"),
169 Some('\\') => {
170 let esc = b.get(i + 1).copied().unwrap_or('\\');
174 s.push(match esc {
175 'n' => '\n',
176 't' => '\t',
177 'r' => '\r',
178 '0' => '\0',
179 other => other,
180 });
181 i += 2;
182 }
183 Some('\'') if b.get(i + 1) == Some(&'\'') => {
184 s.push('\'');
185 i += 2;
186 }
187 Some('\'') => {
188 i += 1;
189 break;
190 }
191 Some(ch) => {
192 s.push(*ch);
193 i += 1;
194 }
195 }
196 }
197 out.push(Tok::Str(s));
198 continue;
199 }
200
201 if c == '"' {
203 i += 1;
204 let mut s = String::new();
205 loop {
206 match b.get(i) {
207 None => bail!("unterminated quoted identifier"),
208 Some('"') if b.get(i + 1) == Some(&'"') => {
209 s.push('"');
210 i += 2;
211 }
212 Some('"') => {
213 i += 1;
214 break;
215 }
216 Some(ch) => {
217 s.push(*ch);
218 i += 1;
219 }
220 }
221 }
222 out.push(Tok::Quoted(s));
223 continue;
224 }
225
226 if c.is_ascii_digit()
228 || (c == '.' && b.get(i + 1).map(|d| d.is_ascii_digit()).unwrap_or(false))
229 {
230 let start = i;
231 while i < b.len() && (b[i].is_ascii_digit() || b[i] == '.') {
232 i += 1;
233 }
234 if i < b.len() && (b[i] == 'e' || b[i] == 'E') {
235 let save = i;
236 i += 1;
237 if i < b.len() && (b[i] == '+' || b[i] == '-') {
238 i += 1;
239 }
240 if i < b.len() && b[i].is_ascii_digit() {
241 while i < b.len() && b[i].is_ascii_digit() {
242 i += 1;
243 }
244 } else {
245 i = save; }
247 }
248 let text: String = b[start..i].iter().collect();
249 let n: f64 = text
250 .parse()
251 .map_err(|_| anyhow::anyhow!("not a number: {:?}", text))?;
252 out.push(Tok::Num(n));
253 continue;
254 }
255
256 if c.is_alphabetic() || c == '_' {
258 let start = i;
259 while i < b.len() && (b[i].is_alphanumeric() || b[i] == '_' || b[i] == '$') {
260 i += 1;
261 }
262 let raw: String = b[start..i].iter().collect();
263 out.push(Tok::Word { upper: raw.to_uppercase(), raw });
264 continue;
265 }
266
267 let rest: String = b[i..].iter().take(3).collect();
269 if let Some(op) = OPERATORS.iter().find(|o| rest.starts_with(**o)) {
270 i += op.chars().count();
271 out.push(Tok::Op((*op).to_string()));
272 continue;
273 }
274
275 if matches!(c, '(' | ')' | ',' | ';' | '.' | '[' | ']') {
276 out.push(Tok::Punct(c));
277 i += 1;
278 continue;
279 }
280
281 bail!("unexpected character {:?} in SQL", c);
284 }
285
286 out.push(Tok::Eof);
287 Ok(out)
288}
289
290#[derive(Debug, Clone, PartialEq)]
295pub enum Expr {
296 Column { qual: Option<String>, name: String },
300 Literal(Value),
301 Star,
303 QualifiedStar(String),
305 Func { name: String, args: Vec<Expr> },
306 Case {
311 operand: Option<Box<Expr>>,
312 whens: Vec<(Expr, Expr)>,
313 else_: Option<Box<Expr>>,
314 },
315 Binary { op: String, left: Box<Expr>, right: Box<Expr> },
316 Unary { op: String, expr: Box<Expr> },
317 InList { expr: Box<Expr>, list: Vec<Expr>, negated: bool },
319 IsNull { expr: Box<Expr>, negated: bool },
321 Cast { expr: Box<Expr>, ty: String },
326 Subquery(Box<Select>),
330 Exists { query: Box<Select>, negated: bool },
333 ArrayQuery(Box<Select>),
337 InSubquery { expr: Box<Expr>, query: Box<Select>, negated: bool },
339 Quantified { op: String, left: Box<Expr>, all: bool, right: Box<Expr> },
343 Index { expr: Box<Expr>, index: Box<Expr> },
345 ArrayLit(Vec<Expr>),
347 Agg {
361 name: String,
362 args: Vec<Expr>,
363 order_by: Vec<OrderBy>,
365 distinct: bool,
366 },
367}
368
369#[derive(Debug, Clone, PartialEq)]
370pub struct SelectItem {
371 pub expr: Expr,
372 pub alias: Option<String>,
375}
376
377#[derive(Debug, Clone, Copy, PartialEq, Eq)]
378pub enum JoinKind { Inner, Left, Right, Full, Cross }
379
380#[derive(Debug, Clone, PartialEq)]
381pub struct TableRef {
382 pub name: String,
390 pub alias: Option<String>,
391 pub sub: Option<Box<Select>>,
394 pub args: Option<Vec<Expr>>,
398 pub col_aliases: Vec<String>,
400 pub lateral: bool,
404 pub as_of: Option<u64>,
418 pub valid_as_of: Option<String>,
423 pub search: Option<String>,
428 pub trace: Option<String>,
442 pub trace_reverse: bool,
444 pub traverse: Option<String>,
447}
448
449impl TableRef {
450 pub fn named(name: impl Into<String>, alias: Option<String>) -> Self {
452 TableRef { name: name.into(), alias, sub: None, args: None, col_aliases: vec![], lateral: false, as_of: None, valid_as_of: None, search: None, trace: None, trace_reverse: false, traverse: None }
453 }
454
455 pub fn binding(&self) -> String {
458 self.alias.clone().unwrap_or_else(|| {
459 self.name.rsplit('.').next().unwrap_or(&self.name).to_string()
460 })
461 }
462}
463
464#[derive(Debug, Clone, Copy, PartialEq, Eq)]
466pub enum SetOp { Union, Intersect, Except }
467
468#[derive(Debug, Clone, PartialEq)]
470pub struct SetArm {
471 pub op: SetOp,
472 pub all: bool,
473 pub query: Select,
474}
475
476#[derive(Debug, Clone, PartialEq)]
477pub struct Join {
478 pub kind: JoinKind,
479 pub table: TableRef,
480 pub on: Option<Expr>,
481}
482
483#[derive(Debug, Clone, Copy, PartialEq)]
484pub enum Dir { Asc, Desc }
485
486#[derive(Debug, Clone, PartialEq)]
487pub struct OrderBy {
488 pub ordinal: Option<usize>,
492 pub expr: Option<Expr>,
493 pub dir: Dir,
494 pub nulls_first: bool,
496}
497
498#[derive(Debug, Clone, PartialEq)]
499pub struct Select {
500 pub distinct: bool,
501 pub items: Vec<SelectItem>,
502 pub from: Option<TableRef>,
503 pub joins: Vec<Join>,
504 pub where_: Option<Expr>,
505 pub group_by: Vec<Expr>,
508 pub having: Option<Expr>,
511 pub order_by: Vec<OrderBy>,
515 pub limit: Option<usize>,
516 pub offset: Option<usize>,
517 pub set_ops: Vec<SetArm>,
520}
521
522impl Select {
523 pub fn base_relations(&self) -> Vec<String> {
532 let mut out = vec![];
533 self.collect_relations(&mut out);
534 out
535 }
536
537 fn collect_relations(&self, out: &mut Vec<String>) {
538 fn table(t: &TableRef, out: &mut Vec<String>) {
539 if let Some(sub) = &t.sub {
540 sub.collect_relations(out);
541 } else if let Some(args) = &t.args {
542 for a in args {
543 expr(a, out);
544 }
545 } else {
546 out.push(t.name.clone());
547 }
548 }
549 fn expr(e: &Expr, out: &mut Vec<String>) {
550 match e {
551 Expr::Subquery(q) | Expr::ArrayQuery(q) => q.collect_relations(out),
552 Expr::Exists { query, .. } => query.collect_relations(out),
553 Expr::InSubquery { expr: x, query, .. } => {
554 expr(x, out);
555 query.collect_relations(out);
556 }
557 Expr::Quantified { left, right, .. } => {
558 expr(left, out);
559 expr(right, out);
560 }
561 Expr::Index { expr: x, index } => {
562 expr(x, out);
563 expr(index, out);
564 }
565 Expr::ArrayLit(items) | Expr::InList { list: items, .. } => {
566 if let Expr::InList { expr: x, .. } = e {
567 expr(x, out);
568 }
569 for i in items {
570 expr(i, out);
571 }
572 }
573 Expr::Func { args, .. } => {
574 for a in args {
575 expr(a, out);
576 }
577 }
578 Expr::Agg { args, order_by, .. } => {
579 for a in args {
580 expr(a, out);
581 }
582 for ob in order_by {
583 if let Some(e) = &ob.expr {
584 expr(e, out);
585 }
586 }
587 }
588 Expr::Case { operand, whens, else_ } => {
589 if let Some(o) = operand {
590 expr(o, out);
591 }
592 for (w, t) in whens {
593 expr(w, out);
594 expr(t, out);
595 }
596 if let Some(x) = else_ {
597 expr(x, out);
598 }
599 }
600 Expr::Binary { left, right, .. } => {
601 expr(left, out);
602 expr(right, out);
603 }
604 Expr::Unary { expr: x, .. } | Expr::Cast { expr: x, .. } | Expr::IsNull { expr: x, .. } => {
605 expr(x, out)
606 }
607 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => {}
608 }
609 }
610 if let Some(f) = &self.from {
611 table(f, out);
612 }
613 for j in &self.joins {
614 table(&j.table, out);
615 if let Some(on) = &j.on {
616 expr(on, out);
617 }
618 }
619 for item in &self.items {
620 expr(&item.expr, out);
621 }
622 if let Some(w) = &self.where_ {
623 expr(w, out);
624 }
625 for g in &self.group_by {
626 expr(g, out);
627 }
628 if let Some(h) = &self.having {
629 expr(h, out);
630 }
631 for ob in &self.order_by {
632 if let Some(e) = &ob.expr {
633 expr(e, out);
634 }
635 }
636 for arm in &self.set_ops {
637 arm.query.collect_relations(out);
638 }
639 }
640}
641
642fn binding_power(op: &str) -> Option<u8> {
653 Some(match op {
654 "OR" => 1,
655 "AND" => 2,
656 "=" | "!=" | "<>" | "<" | "<=" | ">" | ">=" | "~" | "~*" | "!~" | "!~*"
660 | "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => 4,
661 o if o.starts_with("LIKE ESCAPE ") || o.starts_with("ILIKE ESCAPE ")
663 || o.starts_with("NOT LIKE ESCAPE ") || o.starts_with("NOT ILIKE ESCAPE ") => 4,
664 "||" => 5,
665 "+" | "-" => 6,
666 "*" | "/" | "%" => 7,
667 _ => return None,
668 })
669}
670
671struct Parser {
672 toks: Vec<Tok>,
673 pos: usize,
674}
675
676impl Parser {
677 fn peek(&self) -> &Tok {
678 self.toks.get(self.pos).unwrap_or(&Tok::Eof)
679 }
680 fn peek_at(&self, n: usize) -> &Tok {
681 self.toks.get(self.pos + n).unwrap_or(&Tok::Eof)
682 }
683 fn next(&mut self) -> Tok {
684 let t = self.peek().clone();
685 self.pos += 1;
686 t
687 }
688 fn eat_kw(&mut self, kw: &str) -> bool {
689 if self.peek().is_kw(kw) {
690 self.pos += 1;
691 true
692 } else {
693 false
694 }
695 }
696 fn expect_kw(&mut self, kw: &str) -> Result<()> {
697 if self.eat_kw(kw) {
698 Ok(())
699 } else {
700 bail!("expected {} , got {:?}", kw, self.peek())
701 }
702 }
703 fn eat_punct(&mut self, c: char) -> bool {
704 if matches!(self.peek(), Tok::Punct(p) if *p == c) {
705 self.pos += 1;
706 true
707 } else {
708 false
709 }
710 }
711 fn expect_punct(&mut self, c: char) -> Result<()> {
712 if self.eat_punct(c) {
713 Ok(())
714 } else {
715 bail!("expected {:?}, got {:?}", c, self.peek())
716 }
717 }
718 fn eat_op(&mut self, op: &str) -> bool {
719 if matches!(self.peek(), Tok::Op(o) if o == op) {
720 self.pos += 1;
721 true
722 } else {
723 false
724 }
725 }
726
727 fn parse_expr(&mut self) -> Result<Expr> {
730 self.parse_bin(0)
731 }
732
733 fn parse_bin(&mut self, min_bp: u8) -> Result<Expr> {
736 let mut left = self.parse_unary()?;
737
738 loop {
739 if self.peek().is_kw("OPERATOR") && matches!(self.peek_at(1), Tok::Punct('(')) {
746 let save = self.pos;
747 self.pos += 2;
748 let mut sym = None;
750 while sym.is_none() {
751 match self.next() {
752 Tok::Op(o) => sym = Some(o),
753 Tok::Word { .. } | Tok::Punct('.') => continue,
754 _ => break,
755 }
756 }
757 match sym {
758 Some(o) if binding_power(&o).is_some() && self.eat_punct(')') => {
759 let bp = binding_power(&o).unwrap();
760 if bp < min_bp {
761 self.pos = save;
762 break;
763 }
764 let right = self.parse_bin(bp + 1)?;
765 left = Expr::Binary {
766 op: o,
767 left: Box::new(left),
768 right: Box::new(right),
769 };
770 continue;
771 }
772 _ => {
775 self.pos = save;
776 break;
777 }
778 }
779 }
780
781 let (op, width) = match self.peek() {
782 Tok::Op(o) if binding_power(o).is_some() => (o.clone(), 1usize),
783 Tok::Word { upper, .. } if upper == "AND" || upper == "OR" => (upper.clone(), 1),
784 Tok::Word { upper, .. } if upper == "LIKE" || upper == "ILIKE" => (upper.clone(), 1),
785 Tok::Word { upper, .. } if upper == "ESCAPE" => break,
788 Tok::Word { upper, .. } if upper == "NOT" => {
789 match self.peek_at(1) {
791 Tok::Word { upper: u2, .. } if u2 == "LIKE" || u2 == "ILIKE" => {
792 (format!("NOT {}", u2), 2)
793 }
794 _ => break,
795 }
796 }
797 _ => break,
798 };
799
800 let bp = match binding_power(&op) {
801 Some(bp) if bp >= min_bp => bp,
802 _ => break,
803 };
804 self.pos += width;
805
806 let quant = match self.peek() {
812 Tok::Word { upper, .. }
813 if matches!(upper.as_str(), "ANY" | "SOME" | "ALL")
814 && matches!(self.peek_at(1), Tok::Punct('(')) =>
815 {
816 Some(upper == "ALL")
817 }
818 _ => None,
819 };
820 if let Some(all) = quant {
821 self.pos += 2; let right = if self.peek().is_kw("SELECT") {
823 Expr::ArrayQuery(Box::new(self.parse_query()?))
824 } else {
825 self.parse_expr()?
826 };
827 self.expect_punct(')')?;
828 left = Expr::Quantified { op, left: Box::new(left), all, right: Box::new(right) };
829 continue;
830 }
831
832 let right = self.parse_bin(bp + 1)?;
834
835 let op = if matches!(op.as_str(), "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE")
842 && self.peek().is_kw("ESCAPE")
843 {
844 self.next();
845 match self.next() {
846 Tok::Str(e) => {
847 let mut ch = e.chars();
848 match (ch.next(), ch.next()) {
849 (Some(c), None) => format!("{} ESCAPE {}", op, c),
851 _ => bail!(
852 "ESCAPE takes a single-character string, got {:?}", e),
853 }
854 }
855 other => bail!("ESCAPE takes a string literal, got {:?}", other),
856 }
857 } else {
858 op
859 };
860
861 left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
862 }
863
864 Ok(left)
865 }
866
867 fn parse_postfix(&mut self, mut e: Expr) -> Result<Expr> {
868 loop {
869 if matches!(self.peek(), Tok::Punct('[')) {
872 self.pos += 1;
873 let index = self.parse_expr()?;
874 self.expect_punct(']')?;
875 e = Expr::Index { expr: Box::new(e), index: Box::new(index) };
876 continue;
877 }
878
879 if self.peek().is_kw("IS") {
881 self.pos += 1;
882 let negated = self.eat_kw("NOT");
883 if self.eat_kw("DISTINCT") {
887 self.expect_kw("FROM")?;
888 let rhs = self.parse_bin(5)?;
890 e = Expr::Binary {
891 op: if negated { "IS NOT DISTINCT FROM".into() } else { "IS DISTINCT FROM".into() },
892 left: Box::new(e),
893 right: Box::new(rhs),
894 };
895 continue;
896 }
897 if !self.eat_kw("NULL") {
898 if self.eat_kw("TRUE") {
900 e = Expr::Binary {
901 op: "=".into(),
902 left: Box::new(e),
903 right: Box::new(Expr::Literal(Value::Bool(!negated))),
904 };
905 continue;
906 }
907 if self.eat_kw("FALSE") {
908 e = Expr::Binary {
909 op: "=".into(),
910 left: Box::new(e),
911 right: Box::new(Expr::Literal(Value::Bool(negated))),
912 };
913 continue;
914 }
915 bail!("expected NULL, TRUE or FALSE after IS, got {:?}", self.peek());
916 }
917 e = Expr::IsNull { expr: Box::new(e), negated };
918 continue;
919 }
920
921 let negated_in = if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("IN") {
923 self.pos += 2;
924 true
925 } else if self.peek().is_kw("IN") {
926 self.pos += 1;
927 false
928 } else {
929 let negated_between =
931 if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("BETWEEN") {
932 self.pos += 2;
933 true
934 } else if self.peek().is_kw("BETWEEN") {
935 self.pos += 1;
936 false
937 } else {
938 break;
939 };
940 let low = self.parse_bin(3)?;
944 self.expect_kw("AND")?;
945 let high = self.parse_bin(3)?;
946 let ge = Expr::Binary {
947 op: ">=".into(),
948 left: Box::new(e.clone()),
949 right: Box::new(low),
950 };
951 let le = Expr::Binary {
952 op: "<=".into(),
953 left: Box::new(e),
954 right: Box::new(high),
955 };
956 let both = Expr::Binary {
957 op: "AND".into(),
958 left: Box::new(ge),
959 right: Box::new(le),
960 };
961 e = if negated_between {
962 Expr::Unary { op: "NOT".into(), expr: Box::new(both) }
963 } else {
964 both
965 };
966 continue;
967 };
968
969 self.expect_punct('(')?;
970 if self.peek().is_kw("SELECT") {
972 let query = Box::new(self.parse_query()?);
973 self.expect_punct(')')?;
974 e = Expr::InSubquery { expr: Box::new(e), query, negated: negated_in };
975 continue;
976 }
977 let mut list = vec![];
978 if !self.eat_punct(')') {
979 loop {
980 list.push(self.parse_expr()?);
981 if self.eat_punct(',') {
982 continue;
983 }
984 self.expect_punct(')')?;
985 break;
986 }
987 }
988 e = Expr::InList { expr: Box::new(e), list, negated: negated_in };
989 }
990 Ok(e)
991 }
992
993 fn parse_unary(&mut self) -> Result<Expr> {
994 if self.peek().is_kw("NOT") {
995 self.pos += 1;
996 let e = self.parse_bin(3)?;
999 return Ok(Expr::Unary { op: "NOT".into(), expr: Box::new(e) });
1000 }
1001 if self.eat_op("-") {
1002 let e = self.parse_unary()?;
1003 return Ok(Expr::Unary { op: "-".into(), expr: Box::new(e) });
1004 }
1005 if self.eat_op("+") {
1006 return self.parse_unary();
1007 }
1008 let atom = self.parse_atom()?;
1009 let cast = self.parse_casts(atom)?;
1010 self.parse_postfix(cast)
1021 }
1022
1023 fn parse_casts(&mut self, mut e: Expr) -> Result<Expr> {
1025 loop {
1026 if self.peek().is_kw("COLLATE") {
1031 self.pos += 1;
1032 match self.next() {
1033 Tok::Word { .. } | Tok::Quoted(_) => {}
1034 other => bail!("expected a collation name after COLLATE, got {:?}", other),
1035 }
1036 while self.eat_punct('.') {
1038 match self.next() {
1039 Tok::Word { .. } | Tok::Quoted(_) => {}
1040 other => bail!("expected a name after '.', got {:?}", other),
1041 }
1042 }
1043 continue;
1044 }
1045 if !self.eat_op("::") {
1046 break;
1047 }
1048 let mut ty = match self.next() {
1049 Tok::Word { raw, .. } => raw,
1050 Tok::Quoted(s) => s,
1051 other => bail!("expected a type name after ::, got {:?}", other),
1052 };
1053 while self.eat_punct('.') {
1055 match self.next() {
1056 Tok::Word { raw, .. } => ty = raw,
1057 Tok::Quoted(s) => ty = s,
1058 other => bail!("expected a type name after ., got {:?}", other),
1059 }
1060 }
1061 while self.eat_punct('[') {
1063 self.expect_punct(']')?;
1064 ty.push_str("[]");
1065 }
1066 e = Expr::Cast { expr: Box::new(e), ty };
1067 }
1068 Ok(e)
1069 }
1070
1071
1072 fn parse_atom(&mut self) -> Result<Expr> {
1073 if self.eat_punct('(') {
1080 if self.peek().is_kw("SELECT") {
1083 let q = self.parse_query()?;
1084 self.expect_punct(')')?;
1085 return Ok(Expr::Subquery(Box::new(q)));
1086 }
1087 let e = self.parse_expr()?;
1088 self.expect_punct(')')?;
1089 return Ok(e);
1090 }
1091
1092 if self.peek().is_kw("ARRAY") && matches!(self.peek_at(1), Tok::Punct('(') | Tok::Punct('[')) {
1096 self.pos += 1;
1097 if self.eat_punct('(') {
1098 if !self.peek().is_kw("SELECT") {
1099 bail!("ARRAY(...) takes a subquery; for a list of values write ARRAY[...]");
1100 }
1101 let q = self.parse_query()?;
1102 self.expect_punct(')')?;
1103 return Ok(Expr::ArrayQuery(Box::new(q)));
1104 }
1105 self.expect_punct('[')?;
1106 let mut items = vec![];
1107 if !self.eat_punct(']') {
1108 loop {
1109 items.push(self.parse_expr()?);
1110 if self.eat_punct(',') {
1111 continue;
1112 }
1113 self.expect_punct(']')?;
1114 break;
1115 }
1116 }
1117 return Ok(Expr::ArrayLit(items));
1118 }
1119
1120 if self.peek().is_kw("EXISTS") && matches!(self.peek_at(1), Tok::Punct('(')) {
1124 self.pos += 2;
1125 if !self.peek().is_kw("SELECT") {
1126 bail!("EXISTS (...) takes a subquery");
1127 }
1128 let q = self.parse_query()?;
1129 self.expect_punct(')')?;
1130 return Ok(Expr::Exists { query: Box::new(q), negated: false });
1131 }
1132
1133 if self.peek().is_kw("CAST") && matches!(self.peek_at(1), Tok::Punct('(')) {
1136 self.pos += 2;
1137 let inner = self.parse_expr()?;
1138 self.expect_kw("AS")?;
1139 let mut ty = match self.next() {
1140 Tok::Word { raw, .. } => raw,
1141 Tok::Quoted(s) => s,
1142 other => bail!("expected a type name in CAST, got {:?}", other),
1143 };
1144 while self.eat_punct('.') {
1145 match self.next() {
1146 Tok::Word { raw, .. } => ty = raw,
1147 Tok::Quoted(s) => ty = s,
1148 other => bail!("expected a type name after ., got {:?}", other),
1149 }
1150 }
1151 while self.eat_punct('[') {
1152 self.expect_punct(']')?;
1153 ty.push_str("[]");
1154 }
1155 self.expect_punct(')')?;
1156 return Ok(Expr::Cast { expr: Box::new(inner), ty });
1157 }
1158
1159 if self.peek().is_kw("CASE") {
1161 return self.parse_case();
1162 }
1163
1164 match self.next() {
1165 Tok::Num(n) => Ok(Expr::Literal(from_f64(n))),
1166 Tok::Str(s) => Ok(Expr::Literal(Value::String(s))),
1167 Tok::Op(o) if o == "*" => Ok(Expr::Star),
1168 Tok::Quoted(name) => self.parse_name_tail(None, name),
1169 Tok::Word { upper, raw } => match upper.as_str() {
1170 "NULL" => Ok(Expr::Literal(Value::Null)),
1171 "TRUE" => Ok(Expr::Literal(Value::Bool(true))),
1172 "FALSE" => Ok(Expr::Literal(Value::Bool(false))),
1173 "CURRENT_SCHEMA" | "CURRENT_DATABASE" | "CURRENT_USER" | "SESSION_USER"
1177 | "CURRENT_CATALOG" | "USER" | "VERSION"
1178 if !matches!(self.peek(), Tok::Punct('(')) =>
1179 {
1180 Ok(Expr::Func { name: upper.to_lowercase(), args: vec![] })
1181 }
1182 _ => self.parse_name_tail(None, raw),
1183 },
1184 other => bail!("unexpected {:?} in an expression", other),
1185 }
1186 }
1187
1188 fn parse_name_tail(&mut self, _schema: Option<String>, first: String) -> Result<Expr> {
1195 let mut parts = vec![first];
1196 while self.eat_punct('.') {
1197 if self.eat_op("*") {
1199 return Ok(Expr::QualifiedStar(parts.pop().unwrap_or_default()));
1200 }
1201 match self.next() {
1202 Tok::Word { raw, .. } => parts.push(raw),
1203 Tok::Quoted(s) => parts.push(s),
1204 other => bail!("expected a name after '.', got {:?}", other),
1205 }
1206 }
1207
1208 if matches!(self.peek(), Tok::Punct('(')) {
1212 self.pos += 1;
1213 let name = parts.pop().unwrap_or_default().to_lowercase();
1214 let agg = is_aggregate(&name);
1215 let distinct = agg && self.eat_kw("DISTINCT");
1217 let mut args = vec![];
1218 let mut order_by = vec![];
1219 if !self.eat_punct(')') {
1220 loop {
1221 if self.eat_op("*") {
1223 args.push(Expr::Star);
1224 } else {
1225 args.push(self.parse_expr()?);
1226 }
1227 if self.eat_punct(',') {
1228 continue;
1229 }
1230 if agg && self.peek().is_kw("ORDER") {
1234 self.pos += 1;
1235 self.expect_kw("BY")?;
1236 order_by = self.parse_sort_list()?;
1237 }
1238 self.expect_punct(')')?;
1239 break;
1240 }
1241 }
1242 if agg {
1243 return Ok(Expr::Agg { name, args, order_by, distinct });
1244 }
1245 return Ok(Expr::Func { name, args });
1246 }
1247
1248 let name = parts.pop().unwrap_or_default();
1249 let qual = parts.pop();
1253 Ok(Expr::Column { qual, name })
1254 }
1255
1256 fn parse_case(&mut self) -> Result<Expr> {
1257 self.expect_kw("CASE")?;
1258 let operand = if self.peek().is_kw("WHEN") {
1260 None
1261 } else {
1262 Some(Box::new(self.parse_expr()?))
1263 };
1264 let mut whens = vec![];
1265 while self.eat_kw("WHEN") {
1266 let cond = self.parse_expr()?;
1267 self.expect_kw("THEN")?;
1268 let then = self.parse_expr()?;
1269 whens.push((cond, then));
1270 }
1271 if whens.is_empty() {
1272 bail!("CASE needs at least one WHEN branch");
1273 }
1274 let else_ = if self.eat_kw("ELSE") {
1275 Some(Box::new(self.parse_expr()?))
1276 } else {
1277 None
1278 };
1279 self.expect_kw("END")?;
1280 Ok(Expr::Case { operand, whens, else_ })
1281 }
1282
1283 fn parse_table_ref(&mut self) -> Result<TableRef> {
1286 let lateral = self.eat_kw("LATERAL");
1287 if self.eat_punct('(') {
1289 if !self.peek().is_kw("SELECT") {
1290 bail!("expected a subquery after '(' in FROM, got {:?}", self.peek());
1291 }
1292 let sub = self.parse_query()?;
1293 self.expect_punct(')')?;
1294 let (alias, col_aliases) = self.parse_table_alias()?;
1295 if alias.is_none() {
1296 bail!("a subquery in FROM must have an alias");
1297 }
1298 return Ok(TableRef {
1299 name: "(subquery)".into(),
1300 alias,
1301 sub: Some(Box::new(sub)),
1302 args: None,
1303 col_aliases,
1304 lateral,
1305 trace: None,
1308 trace_reverse: false,
1309 traverse: None,
1310 as_of: None,
1311 valid_as_of: None,
1312 search: None,
1313 });
1314 }
1315 if lateral {
1316 bail!("LATERAL applies to a subquery in FROM; write LATERAL (SELECT ...)");
1317 }
1318
1319 let mut parts = vec![match self.next() {
1320 Tok::Word { raw, .. } => raw,
1321 Tok::Quoted(s) => s,
1322 other => bail!("expected a table name, got {:?}", other),
1323 }];
1324 while self.eat_punct('.') {
1325 match self.next() {
1326 Tok::Word { raw, .. } => parts.push(raw),
1327 Tok::Quoted(s) => parts.push(s),
1328 other => bail!("expected a name after '.', got {:?}", other),
1329 }
1330 }
1331 let name = parts.join(".");
1332
1333 if self.eat_punct('(') {
1336 let mut args = vec![];
1337 if !self.eat_punct(')') {
1338 loop {
1339 args.push(self.parse_expr()?);
1340 if self.eat_punct(',') {
1341 continue;
1342 }
1343 self.expect_punct(')')?;
1344 break;
1345 }
1346 }
1347 let fname = name.rsplit('.').next().unwrap_or(&name).to_lowercase();
1348 let (alias, col_aliases) = self.parse_table_alias()?;
1349 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 });
1350 }
1351
1352 let as_of = if self.peek().is_kw("AS") && self.peek_at(1).is_kw("OF") {
1357 self.next();
1358 self.next();
1359 self.expect_kw("SYSTEM")?;
1360 self.expect_kw("TIME")?;
1361 match self.next() {
1362 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Some(n as u64),
1363 other => bail!(
1364 "AS OF SYSTEM TIME takes a NEDB sequence number here, not a timestamp \
1365 (got {:?}). NEDB's history is sequence-addressed and never \
1366 garbage-collected, so a seq is exact where a wall-clock time would be \
1367 approximate",
1368 other
1369 ),
1370 }
1371 } else {
1372 None
1373 };
1374 let valid_as_of = if self.peek().is_kw("VALID")
1390 && self.peek_at(1).is_kw("AS")
1391 && self.peek_at(2).is_kw("OF")
1392 {
1393 self.next();
1394 self.next();
1395 self.next();
1396 match self.next() {
1397 Tok::Str(s) => Some(s),
1398 other => bail!(
1399 "VALID AS OF takes a date string here (got {:?}). System time is a \
1400 sequence and application-time validity is a date — they are different \
1401 questions, so they take different arguments",
1402 other
1403 ),
1404 }
1405 } else {
1406 None
1407 };
1408
1409 let search = if self.peek().is_kw("SEARCH") && matches!(self.peek_at(1), Tok::Str(_)) {
1410 self.next();
1411 match self.next() {
1412 Tok::Str(s) => Some(s),
1413 other => bail!("SEARCH takes a string here, got {:?}", other),
1416 }
1417 } else {
1418 None
1419 };
1420
1421 fn names_a_thing(t: &Tok) -> bool {
1431 match t {
1432 Tok::Word { upper, .. } => !is_clause_keyword(upper),
1433 Tok::Quoted(_) => true,
1434 _ => false,
1435 }
1436 }
1437
1438 let (trace, trace_reverse) = if self.peek().is_kw("TRACE")
1439 && names_a_thing(self.peek_at(1))
1440 {
1441 self.next();
1442 let edge = match self.next() {
1443 Tok::Word { raw, .. } => raw,
1444 Tok::Quoted(s) => s,
1445 other => bail!("TRACE takes an edge type here, got {:?}", other),
1446 };
1447 let rev = if self.peek().is_kw("REVERSE") {
1448 self.next();
1449 true
1450 } else {
1451 false
1452 };
1453 (Some(edge), rev)
1454 } else {
1455 (None, false)
1456 };
1457
1458 let traverse = if self.peek().is_kw("TRAVERSE")
1459 && names_a_thing(self.peek_at(1))
1460 {
1461 self.next();
1462 match self.next() {
1463 Tok::Word { raw, .. } => Some(raw),
1464 Tok::Quoted(s) => Some(s),
1465 other => bail!("TRAVERSE takes a relation name here, got {:?}", other),
1466 }
1467 } else {
1468 None
1469 };
1470
1471 let (alias, col_aliases) = self.parse_table_alias()?;
1472 Ok(TableRef { name, alias, sub: None, args: None, col_aliases, lateral: false, as_of, valid_as_of, search, trace, trace_reverse, traverse })
1473 }
1474
1475 fn parse_table_alias(&mut self) -> Result<(Option<String>, Vec<String>)> {
1480 let alias = if self.eat_kw("AS") {
1481 match self.next() {
1482 Tok::Word { raw, .. } => Some(raw),
1483 Tok::Quoted(s) => Some(s),
1484 other => bail!("expected an alias after AS, got {:?}", other),
1485 }
1486 } else {
1487 match self.peek().clone() {
1488 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1489 self.pos += 1;
1490 Some(raw)
1491 }
1492 Tok::Quoted(s) => {
1493 self.pos += 1;
1494 Some(s)
1495 }
1496 _ => None,
1497 }
1498 };
1499 let mut col_aliases = vec![];
1500 if alias.is_some() && self.eat_punct('(') {
1501 loop {
1502 match self.next() {
1503 Tok::Word { raw, .. } => col_aliases.push(raw),
1504 Tok::Quoted(s) => col_aliases.push(s),
1505 other => bail!("expected a column alias, got {:?}", other),
1506 }
1507 if self.eat_punct(',') {
1508 continue;
1509 }
1510 self.expect_punct(')')?;
1511 break;
1512 }
1513 }
1514 Ok((alias, col_aliases))
1515 }
1516
1517 fn parse_query(&mut self) -> Result<Select> {
1524 let mut first = self.parse_select_body()?;
1525 loop {
1526 let op = if self.eat_kw("UNION") {
1527 SetOp::Union
1528 } else if self.eat_kw("INTERSECT") {
1529 SetOp::Intersect
1530 } else if self.eat_kw("EXCEPT") {
1531 SetOp::Except
1532 } else {
1533 break;
1534 };
1535 let all = self.eat_kw("ALL");
1536 if !all {
1537 let _ = self.eat_kw("DISTINCT");
1538 }
1539 let query = if self.eat_punct('(') {
1541 let q = self.parse_query()?;
1542 self.expect_punct(')')?;
1543 q
1544 } else {
1545 self.parse_select_body()?
1546 };
1547 first.set_ops.push(SetArm { op, all, query });
1548 }
1549 self.parse_query_tail(&mut first)?;
1550 Ok(first)
1551 }
1552
1553 fn parse_sort_list(&mut self) -> Result<Vec<OrderBy>> {
1557 let mut out = vec![];
1558 loop {
1559 let (ordinal, expr) = match self.peek().clone() {
1563 Tok::Num(n)
1564 if n.fract() == 0.0
1565 && n >= 1.0
1566 && !matches!(self.peek_at(1), Tok::Op(_)) =>
1567 {
1568 self.pos += 1;
1569 (Some(n as usize), None)
1570 }
1571 _ => (None, Some(self.parse_expr()?)),
1572 };
1573 let dir = if self.eat_kw("DESC") {
1574 Dir::Desc
1575 } else {
1576 let _ = self.eat_kw("ASC");
1577 Dir::Asc
1578 };
1579 let mut nulls_first = matches!(dir, Dir::Desc);
1581 if self.eat_kw("NULLS") {
1582 if self.eat_kw("FIRST") {
1583 nulls_first = true;
1584 } else if self.eat_kw("LAST") {
1585 nulls_first = false;
1586 } else {
1587 bail!("expected FIRST or LAST after NULLS, got {:?}", self.peek());
1588 }
1589 }
1590 out.push(OrderBy { ordinal, expr, dir, nulls_first });
1591 if self.eat_punct(',') {
1592 continue;
1593 }
1594 break;
1595 }
1596 Ok(out)
1597 }
1598
1599 fn parse_query_tail(&mut self, sel: &mut Select) -> Result<()> {
1600 let mut order_by = vec![];
1601 if self.eat_kw("ORDER") {
1602 self.expect_kw("BY")?;
1603 order_by = self.parse_sort_list()?;
1604 }
1605
1606 let mut limit = None;
1607 let mut offset = None;
1608 loop {
1610 if self.eat_kw("LIMIT") {
1611 if self.eat_kw("ALL") {
1612 limit = None;
1613 } else {
1614 limit = Some(self.parse_count("LIMIT")?);
1615 }
1616 continue;
1617 }
1618 if self.eat_kw("OFFSET") {
1619 offset = Some(self.parse_count("OFFSET")?);
1620 let _ = self.eat_kw("ROW") || self.eat_kw("ROWS");
1621 continue;
1622 }
1623 break;
1624 }
1625 sel.order_by = order_by;
1626 sel.limit = limit;
1627 sel.offset = offset;
1628 Ok(())
1629 }
1630
1631 fn parse_select_body(&mut self) -> Result<Select> {
1633 self.expect_kw("SELECT")?;
1634 let distinct = self.eat_kw("DISTINCT");
1635 if distinct && self.peek().is_kw("ON") {
1636 bail!("DISTINCT ON is not supported");
1637 }
1638 let _ = self.eat_kw("ALL");
1639
1640 let mut items = vec![];
1641 loop {
1642 let expr = self.parse_expr()?;
1643 let alias = if self.eat_kw("AS") {
1645 match self.next() {
1646 Tok::Word { raw, .. } => Some(raw),
1647 Tok::Quoted(s) => Some(s),
1648 other => bail!("expected an alias after AS, got {:?}", other),
1649 }
1650 } else {
1651 match self.peek().clone() {
1652 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1653 self.pos += 1;
1654 Some(raw)
1655 }
1656 Tok::Quoted(s) => {
1657 self.pos += 1;
1658 Some(s)
1659 }
1660 _ => None,
1661 }
1662 };
1663 items.push(SelectItem { expr, alias });
1664 if self.eat_punct(',') {
1665 continue;
1666 }
1667 break;
1668 }
1669
1670 let mut from = None;
1671 let mut joins = vec![];
1672 if self.eat_kw("FROM") {
1673 from = Some(self.parse_table_ref()?);
1674 loop {
1675 if self.eat_punct(',') {
1681 let table = self.parse_table_ref()?;
1682 joins.push(Join { kind: JoinKind::Cross, table, on: None });
1683 continue;
1684 }
1685 let kind = if self.peek().is_kw("JOIN") {
1686 self.pos += 1;
1687 JoinKind::Inner
1688 } else if self.peek().is_kw("INNER") && self.peek_at(1).is_kw("JOIN") {
1689 self.pos += 2;
1690 JoinKind::Inner
1691 } else if self.peek().is_kw("CROSS") && self.peek_at(1).is_kw("JOIN") {
1692 self.pos += 2;
1693 JoinKind::Cross
1694 } else if self.peek().is_kw("LEFT") {
1695 self.pos += 1;
1696 let _ = self.eat_kw("OUTER");
1697 self.expect_kw("JOIN")?;
1698 JoinKind::Left
1699 } else if self.peek().is_kw("RIGHT") {
1700 self.pos += 1;
1701 let _ = self.eat_kw("OUTER");
1702 self.expect_kw("JOIN")?;
1703 JoinKind::Right
1704 } else if self.peek().is_kw("FULL") {
1705 self.pos += 1;
1706 let _ = self.eat_kw("OUTER");
1707 self.expect_kw("JOIN")?;
1708 JoinKind::Full
1709 } else {
1710 break;
1711 };
1712 let table = self.parse_table_ref()?;
1713 let on = if self.eat_kw("ON") {
1714 Some(self.parse_expr()?)
1715 } else if self.peek().is_kw("USING") {
1716 bail!("JOIN ... USING is not supported — write ON a.col = b.col");
1717 } else {
1718 None
1719 };
1720 if on.is_none() && !matches!(kind, JoinKind::Cross) {
1721 bail!("a {:?} JOIN needs an ON clause", kind);
1722 }
1723 joins.push(Join { kind, table, on });
1724 }
1725 }
1726
1727 let where_ = if self.eat_kw("WHERE") {
1728 Some(self.parse_expr()?)
1729 } else {
1730 None
1731 };
1732
1733 let mut group_by = vec![];
1734 if self.eat_kw("GROUP") {
1735 self.expect_kw("BY")?;
1736 if self.eat_kw("ALL") || self.eat_kw("DISTINCT") {
1737 bail!("GROUP BY ALL / DISTINCT is not supported — list the keys");
1738 }
1739 loop {
1740 if self.peek().is_kw("ROLLUP")
1741 || self.peek().is_kw("CUBE")
1742 || self.peek().is_kw("GROUPING")
1743 {
1744 bail!("GROUP BY ROLLUP / CUBE / GROUPING SETS is not supported");
1745 }
1746 group_by.push(self.parse_expr()?);
1747 if self.eat_punct(',') {
1748 continue;
1749 }
1750 break;
1751 }
1752 }
1753
1754 let having = if self.eat_kw("HAVING") {
1755 Some(self.parse_expr()?)
1756 } else {
1757 None
1758 };
1759 if having.is_some() && group_by.is_empty() && !items.iter().any(|i| has_aggregate(&i.expr))
1760 {
1761 bail!("HAVING needs a GROUP BY or an aggregate — it filters groups, not rows; \
1762 use WHERE to filter rows");
1763 }
1764
1765 Ok(Select {
1766 distinct,
1767 items,
1768 from,
1769 joins,
1770 where_,
1771 group_by,
1772 having,
1773 order_by: vec![],
1774 limit: None,
1775 offset: None,
1776 set_ops: vec![],
1777 })
1778 }
1779
1780 fn parse_count(&mut self, what: &str) -> Result<usize> {
1781 match self.next() {
1782 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Ok(n as usize),
1783 other => bail!("{} expects a non-negative integer, got {:?}", what, other),
1784 }
1785 }
1786}
1787
1788fn is_clause_keyword(upper: &str) -> bool {
1793 matches!(
1794 upper,
1795 "FROM" | "WHERE" | "GROUP" | "HAVING" | "ORDER" | "LIMIT" | "OFFSET"
1796 | "JOIN" | "LEFT" | "RIGHT" | "FULL" | "INNER" | "CROSS" | "OUTER"
1797 | "ON" | "USING" | "AND" | "OR" | "AS" | "UNION" | "INTERSECT"
1798 | "EXCEPT" | "FETCH" | "FOR" | "WINDOW" | "RETURNING" | "INTO"
1799 | "ASC" | "DESC" | "NULLS" | "IS" | "IN" | "NOT" | "LIKE" | "ILIKE"
1800 | "BETWEEN" | "THEN" | "WHEN" | "ELSE" | "END" | "CASE" | "DISTINCT"
1801 | "SELECT" | "WITH" | "ALL"
1802 )
1803}
1804
1805pub fn parse(sql: &str) -> Result<Select> {
1807 let toks = lex(sql)?;
1808 let mut p = Parser { toks, pos: 0 };
1809 let sel = if matches!(p.peek(), Tok::Punct('(')) && p.peek_at(1).is_kw("SELECT") {
1811 p.pos += 1;
1812 let mut first = p.parse_query()?;
1813 p.expect_punct(')')?;
1814 loop {
1816 let op = if p.eat_kw("UNION") {
1817 SetOp::Union
1818 } else if p.eat_kw("INTERSECT") {
1819 SetOp::Intersect
1820 } else if p.eat_kw("EXCEPT") {
1821 SetOp::Except
1822 } else {
1823 break;
1824 };
1825 let all = p.eat_kw("ALL");
1826 if !all {
1827 let _ = p.eat_kw("DISTINCT");
1828 }
1829 let query = if p.eat_punct('(') {
1830 let q = p.parse_query()?;
1831 p.expect_punct(')')?;
1832 q
1833 } else {
1834 p.parse_select_body()?
1835 };
1836 first.set_ops.push(SetArm { op, all, query });
1837 }
1838 p.parse_query_tail(&mut first)?;
1839 first
1840 } else {
1841 p.parse_query()?
1842 };
1843 let _ = p.eat_punct(';');
1844 if !matches!(p.peek(), Tok::Eof) {
1845 bail!("unexpected trailing tokens: {:?}", p.peek());
1846 }
1847 Ok(sel)
1848}
1849
1850pub struct Bound<'a> {
1863 pub parts: Vec<(String, Option<&'a Value>)>,
1864 pub ctx: EvalCtx<'a>,
1866}
1867
1868#[derive(Clone, Copy, Default)]
1881pub struct EvalCtx<'a> {
1882 pub resolver: Option<&'a Resolver<'a>>,
1883 pub outer: Option<&'a Bound<'a>>,
1884}
1885
1886impl<'a> Bound<'a> {
1887 pub fn new(parts: Vec<(String, Option<&'a Value>)>) -> Self {
1889 Bound { parts, ctx: EvalCtx::default() }
1890 }
1891
1892 fn column(&self, qual: Option<&str>, name: &str) -> Value {
1899 match qual {
1900 Some(q) => {
1901 for (binding, row) in &self.parts {
1902 if binding.eq_ignore_ascii_case(q) {
1903 return row
1904 .and_then(|r| r.get(name))
1905 .cloned()
1906 .unwrap_or(Value::Null);
1907 }
1908 }
1909 match self.ctx.outer {
1910 Some(o) if o.has_binding(q) => o.column(qual, name),
1911 _ => Value::Null,
1912 }
1913 }
1914 None => {
1915 for (_, row) in &self.parts {
1916 if let Some(v) = row.and_then(|r| r.get(name)) {
1917 return v.clone();
1918 }
1919 }
1920 match self.ctx.outer {
1921 Some(o) => o.column(None, name),
1922 None => Value::Null,
1923 }
1924 }
1925 }
1926 }
1927
1928 fn has_binding(&self, qual: &str) -> bool {
1933 self.parts.iter().any(|(b, _)| b.eq_ignore_ascii_case(qual))
1934 || self.ctx.outer.is_some_and(|o| o.has_binding(qual))
1935 }
1936
1937 fn flatten(&self) -> Vec<(String, Value)> {
1939 let mut out = vec![];
1940 for (_, row) in &self.parts {
1941 if let Some(Value::Object(m)) = row {
1942 for (k, v) in m {
1943 out.push((k.clone(), v.clone()));
1944 }
1945 }
1946 }
1947 out
1948 }
1949
1950 fn flatten_binding(&self, qual: &str) -> Vec<(String, Value)> {
1951 let mut out = vec![];
1952 for (binding, row) in &self.parts {
1953 if binding.eq_ignore_ascii_case(qual) {
1954 if let Some(Value::Object(m)) = row {
1955 for (k, v) in m {
1956 out.push((k.clone(), v.clone()));
1957 }
1958 }
1959 }
1960 }
1961 out
1962 }
1963}
1964
1965type Truth = Option<bool>;
1973
1974fn truthy(v: &Value) -> Truth {
1975 match v {
1976 Value::Null => None,
1977 Value::Bool(b) => Some(*b),
1978 _ => None,
1982 }
1983}
1984
1985fn cmp_values(a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
1991 use std::cmp::Ordering;
1992 match (a, b) {
1993 (Value::Null, _) | (_, Value::Null) => None,
1994 (Value::Number(x), Value::Number(y)) => {
1995 x.as_f64().partial_cmp(&y.as_f64())
1996 }
1997 (Value::String(x), Value::String(y)) => Some(x.cmp(y)),
1998 (Value::Bool(x), Value::Bool(y)) => Some(x.cmp(y)),
1999 (Value::Number(x), Value::String(y)) => match y.parse::<f64>() {
2002 Ok(n) => x.as_f64().partial_cmp(&Some(n)),
2003 Err(_) => Some(as_text(a).cmp(&as_text(b))),
2004 },
2005 (Value::String(x), Value::Number(y)) => match x.parse::<f64>() {
2006 Ok(n) => Some(n).partial_cmp(&y.as_f64()),
2007 Err(_) => Some(as_text(a).cmp(&as_text(b))),
2008 },
2009 _ => {
2010 let (x, y) = (as_text(a), as_text(b));
2011 if x == y { Some(Ordering::Equal) } else { Some(x.cmp(&y)) }
2012 }
2013 }
2014}
2015
2016fn as_text(v: &Value) -> String {
2018 match v {
2019 Value::String(s) => s.clone(),
2020 Value::Null => String::new(),
2021 Value::Bool(b) => (if *b { "t" } else { "f" }).to_string(),
2022 Value::Array(items) => {
2025 let inner: Vec<String> = items
2026 .iter()
2027 .map(|i| match i {
2028 Value::Null => "NULL".to_string(),
2029 Value::String(s) if s.is_empty()
2030 || s.chars().any(|c| c.is_whitespace() || matches!(c, ',' | '{' | '}' | '"' | '\\')) =>
2031 {
2032 format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
2033 }
2034 other => as_text(other),
2035 })
2036 .collect();
2037 format!("{{{}}}", inner.join(","))
2038 }
2039 other => other.to_string(),
2040 }
2041}
2042
2043fn num(v: &Value) -> Option<f64> {
2044 match v {
2045 Value::Number(n) => n.as_f64(),
2046 Value::String(s) => s.parse().ok(),
2047 Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
2048 _ => None,
2049 }
2050}
2051
2052fn from_f64(f: f64) -> Value {
2066 if f.is_finite() && f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
2067 return Value::Number((f as i64).into());
2068 }
2069 serde_json::Number::from_f64(f).map(Value::Number).unwrap_or(Value::Null)
2070}
2071
2072pub fn eval(e: &Expr, row: &Bound) -> Result<Value> {
2074 Ok(match e {
2075 Expr::Literal(v) => v.clone(),
2076
2077 Expr::Column { qual, name } => {
2078 if let Some(q) = qual {
2082 if !row.has_binding(q) {
2083 bail!("no table or alias named {:?} in this query", q);
2084 }
2085 }
2086 row.column(qual.as_deref(), name)
2087 }
2088
2089 Expr::Cast { expr, .. } => eval(expr, row)?,
2090
2091 Expr::Star | Expr::QualifiedStar(_) => {
2092 bail!("`*` is only valid in a select list or as count(*)")
2093 }
2094
2095 Expr::Unary { op, expr } => {
2096 let v = eval(expr, row)?;
2097 match op.as_str() {
2098 "NOT" => match truthy(&v) {
2099 None => Value::Null,
2101 Some(b) => Value::Bool(!b),
2102 },
2103 "-" => match num(&v) {
2104 Some(n) => from_f64(-n),
2105 None => Value::Null,
2106 },
2107 other => bail!("unsupported unary operator {:?}", other),
2108 }
2109 }
2110
2111 Expr::Binary { op, left, right } => {
2112 if op == "AND" {
2117 let l = truthy(&eval(left, row)?);
2118 if l == Some(false) {
2119 return Ok(Value::Bool(false));
2120 }
2121 let r = truthy(&eval(right, row)?);
2122 return Ok(match (l, r) {
2123 (_, Some(false)) => Value::Bool(false),
2124 (Some(true), Some(true)) => Value::Bool(true),
2125 _ => Value::Null,
2126 });
2127 }
2128 if op == "OR" {
2129 let l = truthy(&eval(left, row)?);
2130 if l == Some(true) {
2131 return Ok(Value::Bool(true));
2132 }
2133 let r = truthy(&eval(right, row)?);
2134 return Ok(match (l, r) {
2135 (_, Some(true)) => Value::Bool(true),
2136 (Some(false), Some(false)) => Value::Bool(false),
2137 _ => Value::Null,
2138 });
2139 }
2140
2141 let l = eval(left, row)?;
2142 let r = eval(right, row)?;
2143 apply_op(op, l, r)?
2144 }
2145
2146 Expr::IsNull { expr, negated } => {
2147 let v = eval(expr, row)?;
2148 Value::Bool(v.is_null() != *negated)
2151 }
2152
2153 Expr::InList { expr, list, negated } => {
2154 let v = eval(expr, row)?;
2155 if v.is_null() {
2156 return Ok(Value::Null);
2157 }
2158 let mut items = Vec::with_capacity(list.len());
2159 for item in list {
2160 items.push(eval(item, row)?);
2161 }
2162 in_values(&v, &items, *negated)?
2163 }
2164
2165 Expr::Subquery(q) => {
2167 let (cols, rows) = run_sub(q, row)?;
2168 if cols.len() != 1 {
2169 bail!("a subquery used as an expression must return exactly one \
2170 column, this one returns {}", cols.len());
2171 }
2172 match rows.len() {
2173 0 => Value::Null,
2174 1 => rows[0].get(&cols[0].key).cloned().unwrap_or(Value::Null),
2175 n => bail!("more than one row returned by a subquery used as an \
2176 expression ({} rows)", n),
2177 }
2178 }
2179 Expr::Exists { query, negated } => {
2180 let (_, rows) = run_sub(query, row)?;
2181 Value::Bool(!rows.is_empty() != *negated)
2182 }
2183 Expr::ArrayQuery(q) => Value::Array(first_column(q, row)?),
2184 Expr::InSubquery { expr, query, negated } => {
2185 let v = eval(expr, row)?;
2186 if v.is_null() {
2187 return Ok(Value::Null);
2188 }
2189 let items = first_column(query, row)?;
2190 in_values(&v, &items, *negated)?
2191 }
2192 Expr::Quantified { op, left, all, right } => {
2193 let l = eval(left, row)?;
2194 let r = eval(right, row)?;
2195 let items = match r {
2196 Value::Null => return Ok(Value::Null),
2197 Value::Array(items) => items,
2198 other => bail!(
2199 "{} requires an array or a subquery on its right side, got {}",
2200 if *all { "ALL" } else { "ANY" },
2201 as_text(&other)
2202 ),
2203 };
2204 let mut saw_true = false;
2208 let mut saw_false = false;
2209 let mut saw_null = false;
2210 for item in items {
2211 match truthy(&apply_op(op, l.clone(), item)?) {
2212 Some(true) => saw_true = true,
2213 Some(false) => saw_false = true,
2214 None => saw_null = true,
2215 }
2216 }
2217 if *all {
2218 if saw_false {
2219 Value::Bool(false)
2220 } else if saw_null {
2221 Value::Null
2222 } else {
2223 Value::Bool(true)
2224 }
2225 } else if saw_true {
2226 Value::Bool(true)
2227 } else if saw_null {
2228 Value::Null
2229 } else {
2230 Value::Bool(false)
2231 }
2232 }
2233 Expr::Index { expr, index } => {
2234 let arr = eval(expr, row)?;
2235 let i = eval(index, row)?;
2236 match (arr, num(&i)) {
2237 (Value::Array(items), Some(n)) if n >= 1.0 => {
2238 items.get(n as usize - 1).cloned().unwrap_or(Value::Null)
2239 }
2240 _ => Value::Null,
2241 }
2242 }
2243 Expr::ArrayLit(items) => {
2244 let mut out = Vec::with_capacity(items.len());
2245 for i in items {
2246 out.push(eval(i, row)?);
2247 }
2248 Value::Array(out)
2249 }
2250
2251 Expr::Case { operand, whens, else_ } => {
2252 let subject = match operand {
2253 Some(o) => Some(eval(o, row)?),
2254 None => None,
2255 };
2256 for (cond, then) in whens {
2257 let hit = match &subject {
2258 Some(sv) => {
2260 let cv = eval(cond, row)?;
2261 matches!(cmp_values(sv, &cv), Some(std::cmp::Ordering::Equal))
2262 }
2263 None => truthy(&eval(cond, row)?) == Some(true),
2266 };
2267 if hit {
2268 return eval(then, row);
2269 }
2270 }
2271 match else_ {
2272 Some(e) => eval(e, row)?,
2273 None => Value::Null,
2276 }
2277 }
2278
2279 Expr::Func { name, args } => eval_func(name, args, row)?,
2280
2281 Expr::Agg { name, .. } => bail!(
2287 "{}() is an aggregate and has no value for one row — it is reduced \
2288 over a GROUP. Reaching this point is an engine bug, not a problem \
2289 with the query",
2290 name
2291 ),
2292 })
2293}
2294
2295fn in_values(v: &Value, items: &[Value], negated: bool) -> Result<Value> {
2297 let mut any_null = false;
2298 let mut found = false;
2299 for iv in items {
2300 if iv.is_null() {
2301 any_null = true;
2302 continue;
2303 }
2304 if matches!(cmp_values(v, iv), Some(std::cmp::Ordering::Equal)) {
2305 found = true;
2306 break;
2307 }
2308 }
2309 Ok(if found {
2313 Value::Bool(!negated)
2314 } else if any_null {
2315 Value::Null
2316 } else {
2317 Value::Bool(negated)
2318 })
2319}
2320
2321fn run_sub(q: &Select, row: &Bound) -> Result<(Vec<OutCol>, Vec<Value>)> {
2329 let Some(resolve) = row.ctx.resolver else {
2330 bail!("a subquery cannot run here: this evaluation has no relation resolver");
2331 };
2332 let (cols, rows, _) = execute_inner(q, resolve, Opts::default(), Some(row))?;
2333 Ok((cols, rows))
2334}
2335
2336fn first_column(q: &Select, row: &Bound) -> Result<Vec<Value>> {
2339 let (cols, rows) = run_sub(q, row)?;
2340 let Some(first) = cols.first() else {
2341 bail!("the subquery returns no columns");
2342 };
2343 Ok(rows
2344 .into_iter()
2345 .map(|r| r.get(&first.key).cloned().unwrap_or(Value::Null))
2346 .collect())
2347}
2348
2349fn apply_op(op: &str, l: Value, r: Value) -> Result<Value> {
2353 let compare = |ord: fn(std::cmp::Ordering) -> bool| -> Value {
2355 match cmp_values(&l, &r) {
2356 None => Value::Null,
2357 Some(o) => Value::Bool(ord(o)),
2358 }
2359 };
2360
2361 Ok(match op {
2362 "IS DISTINCT FROM" | "IS NOT DISTINCT FROM" => {
2363 let distinct = match (l.is_null(), r.is_null()) {
2364 (true, true) => false,
2365 (true, false) | (false, true) => true,
2366 (false, false) => !matches!(cmp_values(&l, &r), Some(std::cmp::Ordering::Equal)),
2367 };
2368 Value::Bool(distinct != op.starts_with("IS NOT"))
2369 }
2370 "=" => compare(|o| o.is_eq()),
2371 "!=" | "<>" => compare(|o| o.is_ne()),
2372 "<" => compare(|o| o.is_lt()),
2373 "<=" => compare(|o| o.is_le()),
2374 ">" => compare(|o| o.is_gt()),
2375 ">=" => compare(|o| o.is_ge()),
2376
2377 "~" | "~*" | "!~" | "!~*" => {
2378 if l.is_null() || r.is_null() {
2379 Value::Null
2380 } else {
2381 let pat = as_text(&r);
2382 if let Some(why) = crate::nql::regex_error_pub(&pat) {
2383 bail!(
2384 "{} — in {:?}. The supported subset is ^ $ . | ( ) \
2385 [ ] * + ? and literal text",
2386 why, pat
2387 );
2388 }
2389 let hit = crate::nql::regex_match_pub(
2390 &as_text(&l), &pat, op.ends_with('*'));
2391 Value::Bool(hit != op.starts_with('!'))
2392 }
2393 }
2394
2395 _ if op.starts_with("LIKE") || op.starts_with("ILIKE")
2396 || op.starts_with("NOT LIKE") || op.starts_with("NOT ILIKE") => {
2397 if l.is_null() || r.is_null() {
2398 Value::Null
2399 } else {
2400 let (base, esc): (&str, Option<char>) =
2402 match op.split_once(" ESCAPE ") {
2403 Some((b, e)) => (b, e.chars().next()),
2404 None => (&op[..], None),
2405 };
2406 let ci = base.ends_with("ILIKE");
2407 let txt = as_text(&l);
2408 let pat = as_text(&r);
2409 let hit = match esc {
2410 Some(c) => crate::nql::like_match_escape_pub(&txt, &pat, ci, c),
2411 None => crate::nql::like_match_pub(&txt, &pat, ci),
2412 };
2413 Value::Bool(hit != base.starts_with("NOT"))
2414 }
2415 }
2416
2417 "||" => {
2419 if l.is_null() || r.is_null() {
2420 Value::Null
2421 } else {
2422 Value::String(format!("{}{}", as_text(&l), as_text(&r)))
2423 }
2424 }
2425
2426 "+" | "-" | "*" | "/" | "%" => match (num(&l), num(&r)) {
2427 (Some(a), Some(b)) => match op {
2428 "+" => from_f64(a + b),
2429 "-" => from_f64(a - b),
2430 "*" => from_f64(a * b),
2431 "/" if b == 0.0 => bail!("division by zero"),
2434 "/" => from_f64(a / b),
2435 "%" if b == 0.0 => bail!("division by zero"),
2436 "%" => from_f64(a % b),
2437 _ => unreachable!(),
2438 },
2439 _ => Value::Null,
2440 },
2441
2442 other => bail!("unsupported operator {:?}", other),
2443 })
2444}
2445
2446fn eval_func(name: &str, args: &[Expr], row: &Bound) -> Result<Value> {
2452 let arg = |i: usize| -> Result<Value> {
2455 match args.get(i) {
2456 Some(e) => eval(e, row),
2457 None => Ok(Value::Null),
2458 }
2459 };
2460
2461 Ok(match name {
2462 "pg_get_userbyid" | "current_user" | "session_user" | "user" => {
2466 Value::String("nedb".into())
2467 }
2468 "current_schema" => Value::String("public".into()),
2469 "current_database" | "current_catalog" => Value::String("nedb".into()),
2470 "version" => Value::String(crate::pgwire::version_string()),
2471
2472 "pg_table_is_visible" | "pg_type_is_visible" | "pg_function_is_visible"
2477 | "pg_opclass_is_visible" | "pg_conversion_is_visible" => Value::Bool(true),
2478
2479 "pg_encoding_to_char" => Value::String("UTF8".into()),
2481 "pg_get_expr" | "pg_get_indexdef" | "pg_get_constraintdef"
2482 | "pg_get_viewdef" | "pg_get_partkeydef" | "obj_description"
2483 | "col_description" | "shobj_description" => Value::Null,
2484
2485 "lower" => match arg(0)? {
2487 Value::Null => Value::Null,
2488 v => Value::String(as_text(&v).to_lowercase()),
2489 },
2490 "upper" => match arg(0)? {
2491 Value::Null => Value::Null,
2492 v => Value::String(as_text(&v).to_uppercase()),
2493 },
2494 "length" | "char_length" | "character_length" => match arg(0)? {
2495 Value::Null => Value::Null,
2496 v => from_f64(as_text(&v).chars().count() as f64),
2497 },
2498 "format_type" => match arg(0)? {
2499 Value::Null => Value::Null,
2500 v => Value::String(crate::pgcatalog::type_name_pub(
2501 num(&v).unwrap_or(25.0) as i32).to_string()),
2502 },
2503 "array_to_string" | "pg_catalog.array_to_string" => {
2504 match arg(0)? {
2508 Value::Array(items) => {
2509 let sep = as_text(&arg(1)?);
2510 Value::String(
2511 items.iter().map(as_text).collect::<Vec<_>>().join(&sep),
2512 )
2513 }
2514 _ => Value::Null,
2515 }
2516 }
2517 "quote_ident" => Value::String(as_text(&arg(0)?)),
2518 "quote_literal" => Value::String(format!("'{}'", as_text(&arg(0)?).replace('\'', "''"))),
2519 "format" => {
2523 let fmt = as_text(&arg(0)?);
2524 let mut out = String::new();
2525 let mut next = 1usize;
2526 let mut chars = fmt.chars().peekable();
2527 while let Some(c) = chars.next() {
2528 if c != '%' {
2529 out.push(c);
2530 continue;
2531 }
2532 match chars.next() {
2533 Some('%') => out.push('%'),
2534 Some(spec @ ('s' | 'I' | 'L')) => {
2535 let v = arg(next)?;
2536 next += 1;
2537 match (spec, &v) {
2538 ('L', Value::Null) => out.push_str("NULL"),
2539 ('L', v) => out.push_str(&format!("'{}'", as_text(v).replace('\'', "''"))),
2540 (_, v) => out.push_str(&as_text(v)),
2541 }
2542 }
2543 other => bail!("format(): unsupported conversion %{}", other.map(String::from).unwrap_or_default()),
2544 }
2545 }
2546 Value::String(out)
2547 }
2548
2549 "array_length" | "array_upper" | "cardinality" => match arg(0)? {
2553 Value::Array(items) if !items.is_empty() => from_f64(items.len() as f64),
2554 Value::Array(_) if name == "cardinality" => from_f64(0.0),
2555 _ => Value::Null,
2556 },
2557 "array_lower" => match arg(0)? {
2558 Value::Array(items) if !items.is_empty() => from_f64(1.0),
2559 _ => Value::Null,
2560 },
2561
2562 "pg_table_size" | "pg_total_relation_size" | "pg_relation_size"
2569 | "pg_indexes_size" | "pg_database_size" => Value::Null,
2570 "pg_size_pretty" => match num(&arg(0)?) {
2571 None => Value::Null,
2572 Some(n) => {
2573 let units = ["bytes", "kB", "MB", "GB", "TB", "PB"];
2574 let mut v = n;
2575 let mut u = 0usize;
2576 while v.abs() >= 10240.0 && u + 1 < units.len() {
2577 v /= 1024.0;
2578 u += 1;
2579 }
2580 Value::String(format!("{} {}", v.round() as i64, units[u]))
2581 }
2582 },
2583
2584 "pg_get_triggerdef" | "pg_get_ruledef" | "pg_get_statisticsobjdef"
2590 | "pg_get_statisticsobjdef_columns" | "pg_get_function_result"
2591 | "pg_get_function_arguments" | "pg_get_function_identity_arguments"
2592 | "pg_get_functiondef" | "pg_get_serial_sequence" | "pg_get_partition_constraintdef"
2593 | "pg_relation_filepath" | "pg_tablespace_location" => Value::Null,
2594 "pg_relation_is_publishable" => Value::Bool(true),
2595 "pg_statistics_obj_is_visible" | "pg_opfamily_is_visible" | "pg_collation_is_visible"
2596 | "pg_ts_config_is_visible" | "pg_ts_dict_is_visible" | "pg_ts_parser_is_visible"
2597 | "pg_ts_template_is_visible" | "has_table_privilege" | "has_schema_privilege"
2598 | "has_database_privilege" | "pg_has_role" => Value::Bool(true),
2599 "current_setting" => match arg(0)? {
2602 Value::Null => Value::Null,
2603 v => match as_text(&v).to_lowercase().as_str() {
2604 "server_version" => Value::String(crate::pgwire::version_string()),
2605 "server_encoding" | "client_encoding" => Value::String("UTF8".into()),
2606 "standard_conforming_strings" | "integer_datetimes" | "is_superuser" => {
2607 Value::String("on".into())
2608 }
2609 "timezone" | "log_timezone" => Value::String("UTC".into()),
2610 "search_path" => Value::String("\"$user\", public".into()),
2611 "intervalstyle" => Value::String("postgres".into()),
2612 "datestyle" => Value::String("ISO, MDY".into()),
2613 "session_authorization" => Value::String("nedb".into()),
2614 "application_name" | "default_transaction_read_only" => Value::String(String::new()),
2615 "transaction_isolation" | "default_transaction_isolation" => {
2616 Value::String("read committed".into())
2617 }
2618 "max_identifier_length" => Value::String("63".into()),
2619 other => {
2620 if truthy(&arg(1)?) == Some(true) {
2623 Value::Null
2624 } else {
2625 bail!("unrecognized configuration parameter \"{}\"", other)
2626 }
2627 }
2628 },
2629 },
2630 "pg_backend_pid" => from_f64(std::process::id() as f64),
2631 "pg_is_in_recovery" => Value::Bool(false),
2632 "txid_current" => from_f64(0.0),
2633 "now" | "current_timestamp" | "statement_timestamp" | "clock_timestamp" => {
2634 Value::String(now_iso())
2635 }
2636 "to_char" => match arg(0)? {
2637 Value::Null => Value::Null,
2638 v => Value::String(as_text(&v)),
2639 },
2640 "generate_series" | "unnest" => bail!(
2641 "{}() returns a set of rows — write it in FROM, not in the select list", name
2642 ),
2643
2644 "coalesce" => {
2646 let mut out = Value::Null;
2647 for a in args {
2648 let v = eval(a, row)?;
2649 if !v.is_null() {
2650 out = v;
2651 break;
2652 }
2653 }
2654 out
2655 }
2656 "nullif" => {
2657 let a = arg(0)?;
2658 let b = arg(1)?;
2659 if matches!(cmp_values(&a, &b), Some(std::cmp::Ordering::Equal)) {
2660 Value::Null
2661 } else {
2662 a
2663 }
2664 }
2665
2666 "int4" | "int8" | "int2" => match num(&arg(0)?) {
2668 Some(n) => from_f64(n.trunc()),
2669 None => Value::Null,
2670 },
2671 "text" => match arg(0)? {
2672 Value::Null => Value::Null,
2673 v => Value::String(as_text(&v)),
2674 },
2675
2676 other if is_aggregate(other) => bail!(
2677 "{}() is an aggregate, which is only meaningful over a whole result set — \
2678 it is evaluated by the executor, never per row",
2679 other
2680 ),
2681
2682 other => bail!(
2683 "the function {}() is not implemented. It is refused rather than \
2684 answered with NULL, because a NULL column reads as missing DATA \
2685 rather than a missing feature",
2686 other
2687 ),
2688 })
2689}
2690
2691fn now_iso() -> String {
2693 let secs = std::time::SystemTime::now()
2694 .duration_since(std::time::UNIX_EPOCH)
2695 .map(|d| d.as_secs())
2696 .unwrap_or(0);
2697 let days = (secs / 86_400) as i64;
2699 let rem = secs % 86_400;
2700 let z = days + 719_468;
2701 let era = z.div_euclid(146_097);
2702 let doe = z.rem_euclid(146_097);
2703 let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
2704 let y = yoe + era * 400;
2705 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2706 let mp = (5 * doy + 2) / 153;
2707 let d = doy - (153 * mp + 2) / 5 + 1;
2708 let m = if mp < 10 { mp + 3 } else { mp - 9 };
2709 let y = if m <= 2 { y + 1 } else { y };
2710 format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}+00", y, m, d, rem / 3600, (rem % 3600) / 60, rem % 60)
2711}
2712
2713const AGGREGATES: &[&str] = &[
2718 "count", "sum", "avg", "min", "max", "string_agg", "array_agg", "bool_and",
2719 "bool_or", "every",
2720];
2721
2722fn is_aggregate(name: &str) -> bool {
2723 AGGREGATES.iter().any(|a| a.eq_ignore_ascii_case(name))
2724}
2725
2726pub fn has_aggregate(e: &Expr) -> bool {
2729 match e {
2730 Expr::Agg { .. } => true,
2731 Expr::Func { args, .. } => args.iter().any(has_aggregate),
2732 Expr::Binary { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2733 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2734 has_aggregate(expr)
2735 }
2736 Expr::InList { expr, list, .. } => has_aggregate(expr) || list.iter().any(has_aggregate),
2737 Expr::Case { operand, whens, else_ } => {
2738 operand.as_deref().is_some_and(has_aggregate)
2739 || whens.iter().any(|(c, t)| has_aggregate(c) || has_aggregate(t))
2740 || else_.as_deref().is_some_and(has_aggregate)
2741 }
2742 Expr::Quantified { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2743 Expr::Index { expr, index } => has_aggregate(expr) || has_aggregate(index),
2744 Expr::ArrayLit(items) => items.iter().any(has_aggregate),
2745 Expr::InSubquery { expr, .. } => has_aggregate(expr),
2746 Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => false,
2747 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => false,
2748 }
2749}
2750
2751fn aggregate(
2758 name: &str,
2759 args: &[Expr],
2760 order_by: &[OrderBy],
2761 distinct: bool,
2762 rows: &[JoinedRow],
2763 ctx: EvalCtx,
2764) -> Result<Value> {
2765 let lname = name.to_lowercase();
2766
2767 let ordered: Vec<JoinedRow> = if order_by.is_empty() {
2770 rows.to_vec()
2771 } else {
2772 let mut keyed: Vec<(Vec<Value>, JoinedRow)> = Vec::with_capacity(rows.len());
2773 for r in rows {
2774 let b = bind(r, ctx);
2775 let mut key = vec![];
2776 for ob in order_by {
2777 match (&ob.expr, ob.ordinal) {
2780 (Some(e), _) => key.push(eval(e, &b)?),
2781 (None, Some(n)) => bail!(
2782 "ORDER BY {} inside an aggregate refers to a select-list \
2783 position, which an aggregate does not have — name the \
2784 column instead", n
2785 ),
2786 (None, None) => key.push(Value::Null),
2787 }
2788 }
2789 keyed.push((key, r.clone()));
2790 }
2791 keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, order_by));
2792 keyed.into_iter().map(|(_, r)| r).collect()
2793 };
2794 let rows: &[JoinedRow] = &ordered;
2795
2796 if lname == "count" && (args.is_empty() || matches!(args[0], Expr::Star)) {
2799 return Ok(from_f64(rows.len() as f64));
2800 }
2801 let Some(target) = args.first() else {
2802 bail!("{}() needs an argument", name);
2803 };
2804 let mut vals: Vec<Value> = Vec::with_capacity(rows.len());
2805 let mut all_vals: Vec<Value> = Vec::with_capacity(rows.len());
2806 for r in rows {
2807 let v = eval(target, &bind(r, ctx))?;
2808 if !v.is_null() {
2809 vals.push(v.clone());
2810 }
2811 all_vals.push(v);
2812 }
2813 if distinct {
2814 let mut seen: Vec<String> = vec![];
2815 vals.retain(|v| {
2816 let k = format!("{:?}", v);
2817 if seen.contains(&k) { false } else { seen.push(k); true }
2818 });
2819 let mut seen2: Vec<String> = vec![];
2820 all_vals.retain(|v| {
2821 let k = format!("{:?}", v);
2822 if seen2.contains(&k) { false } else { seen2.push(k); true }
2823 });
2824 }
2825 Ok(match lname.as_str() {
2826 "count" => from_f64(vals.len() as f64),
2827 "sum" | "avg" => {
2828 let nums: Vec<f64> = vals.iter().filter_map(num).collect();
2829 if nums.is_empty() {
2830 Value::Null
2831 } else if lname == "sum" {
2832 from_f64(nums.iter().sum())
2833 } else {
2834 from_f64(nums.iter().sum::<f64>() / nums.len() as f64)
2835 }
2836 }
2837 "min" | "max" => {
2838 let mut best: Option<Value> = None;
2839 for v in vals {
2840 best = Some(match best {
2841 None => v,
2842 Some(b) => {
2843 let take = match cmp_values(&v, &b) {
2844 Some(o) if lname == "min" => o.is_lt(),
2845 Some(o) => o.is_gt(),
2846 None => false,
2847 };
2848 if take { v } else { b }
2849 }
2850 });
2851 }
2852 best.unwrap_or(Value::Null)
2853 }
2854 "string_agg" => {
2855 if vals.is_empty() {
2856 Value::Null
2857 } else {
2858 let sep = match args.get(1) {
2861 Some(e) => as_text(&eval(e, &Bound { parts: vec![], ctx })?),
2862 None => String::new(),
2863 };
2864 Value::String(vals.iter().map(as_text).collect::<Vec<_>>().join(&sep))
2865 }
2866 }
2867 "array_agg" => {
2869 if all_vals.is_empty() { Value::Null } else { Value::Array(all_vals) }
2870 }
2871 "bool_and" | "every" => {
2872 if vals.is_empty() {
2873 Value::Null
2874 } else {
2875 Value::Bool(vals.iter().all(|v| truthy(v) == Some(true)))
2876 }
2877 }
2878 "bool_or" => {
2879 if vals.is_empty() {
2880 Value::Null
2881 } else {
2882 Value::Bool(vals.iter().any(|v| truthy(v) == Some(true)))
2883 }
2884 }
2885 _ => unreachable!("is_aggregate gates this"),
2886 })
2887}
2888
2889fn fold_aggregates(
2896 e: &Expr,
2897 rows: &[JoinedRow],
2898 ctx: EvalCtx,
2899 keys: &[Expr],
2900) -> Result<Expr> {
2901 let fold = |x: &Expr| fold_aggregates(x, rows, ctx, keys);
2902 Ok(match e {
2903 Expr::Agg { name, args, order_by, distinct } => {
2904 Expr::Literal(aggregate(name, args, order_by, *distinct, rows, ctx)?)
2905 }
2906 Expr::Func { name, args } => Expr::Func {
2907 name: name.clone(),
2908 args: args.iter().map(&fold).collect::<Result<_>>()?,
2909 },
2910 Expr::Column { .. } if keys.iter().any(|k| k == e) => e.clone(),
2916 Expr::Column { qual, name } => bail!(
2917 "column \"{}{}\" must appear in the GROUP BY clause or be used in an \
2918 aggregate function",
2919 qual.as_ref().map(|q| format!("{q}.")).unwrap_or_default(),
2920 name
2921 ),
2922 Expr::Binary { op, left, right } => Expr::Binary {
2923 op: op.clone(),
2924 left: Box::new(fold(left)?),
2925 right: Box::new(fold(right)?),
2926 },
2927 Expr::Unary { op, expr } => Expr::Unary {
2928 op: op.clone(),
2929 expr: Box::new(fold(expr)?),
2930 },
2931 Expr::Cast { expr, ty } => Expr::Cast {
2932 expr: Box::new(fold(expr)?),
2933 ty: ty.clone(),
2934 },
2935 Expr::IsNull { expr, negated } => Expr::IsNull {
2936 expr: Box::new(fold(expr)?),
2937 negated: *negated,
2938 },
2939 Expr::InList { expr, list, negated } => Expr::InList {
2940 expr: Box::new(fold(expr)?),
2941 list: list.iter().map(&fold).collect::<Result<_>>()?,
2942 negated: *negated,
2943 },
2944 Expr::Case { operand, whens, else_ } => Expr::Case {
2945 operand: match operand {
2946 Some(o) => Some(Box::new(fold(o)?)),
2947 None => None,
2948 },
2949 whens: whens
2950 .iter()
2951 .map(|(c, t)| Ok((fold(c)?, fold(t)?)))
2952 .collect::<Result<_>>()?,
2953 else_: match else_ {
2954 Some(x) => Some(Box::new(fold(x)?)),
2955 None => None,
2956 },
2957 },
2958 Expr::Quantified { op, left, all, right } => Expr::Quantified {
2959 op: op.clone(),
2960 left: Box::new(fold(left)?),
2961 all: *all,
2962 right: Box::new(fold(right)?),
2963 },
2964 Expr::Index { expr, index } => Expr::Index {
2965 expr: Box::new(fold(expr)?),
2966 index: Box::new(fold(index)?),
2967 },
2968 Expr::ArrayLit(items) => Expr::ArrayLit(
2969 items.iter().map(&fold).collect::<Result<_>>()?,
2970 ),
2971 Expr::InSubquery { expr, query, negated } => Expr::InSubquery {
2972 expr: Box::new(fold(expr)?),
2973 query: query.clone(),
2974 negated: *negated,
2975 },
2976 Expr::Star | Expr::QualifiedStar(_) => {
2979 bail!("`*` cannot be mixed with an aggregate outside count(*)")
2980 }
2981 Expr::Literal(_) | Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => {
2983 e.clone()
2984 }
2985 })
2986}
2987
2988fn validate_bindings(sel: &Select) -> Result<()> {
3004 let mut seen: Vec<String> = vec![];
3005 if let Some(f) = &sel.from {
3006 seen.push(f.binding());
3007 }
3008 for j in &sel.joins {
3009 seen.push(j.table.binding());
3010 }
3011 for (i, b) in seen.iter().enumerate() {
3012 if let Some(prev) = seen[..i].iter().find(|p| p.eq_ignore_ascii_case(b)) {
3013 bail!(
3014 "ambiguous relation binding: {:?} appears more than once; use \
3015 aliases (for example `FROM {} JOIN {} AS {}2 ...`)",
3016 prev, prev, prev, prev
3017 );
3018 }
3019 }
3020 Ok(())
3021}
3022
3023#[derive(Debug, Clone, PartialEq, Eq)]
3035pub struct OutCol {
3036 pub key: String,
3037 pub name: String,
3038}
3039
3040fn unique_key(taken: &[OutCol], name: &str) -> String {
3044 if !taken.iter().any(|c| c.key == name) {
3045 return name.to_string();
3046 }
3047 format!("{name}\u{1}{}", taken.len())
3048}
3049
3050type JoinedRow = Vec<(String, Option<Value>)>;
3052
3053fn bind<'a>(row: &'a JoinedRow, ctx: EvalCtx<'a>) -> Bound<'a> {
3054 Bound {
3055 parts: row.iter().map(|(b, v)| (b.clone(), v.as_ref())).collect(),
3056 ctx,
3057 }
3058}
3059
3060fn derived_name(e: &Expr) -> String {
3068 match e {
3069 Expr::Column { name, .. } => name.clone(),
3070 Expr::Func { name, .. } | Expr::Agg { name, .. } => name.clone(),
3071 Expr::Cast { expr, .. } => derived_name(expr),
3072 Expr::Case { .. } => "case".to_string(),
3073 Expr::ArrayQuery(_) | Expr::ArrayLit(_) => "array".to_string(),
3074 Expr::Exists { .. } => "exists".to_string(),
3075 Expr::Subquery(q) => q
3077 .items
3078 .first()
3079 .map(|i| i.alias.clone().unwrap_or_else(|| derived_name(&i.expr)))
3080 .unwrap_or_else(|| "?column?".to_string()),
3081 _ => "?column?".to_string(),
3082 }
3083}
3084
3085pub trait Relation {
3106 fn next_row(&mut self) -> Result<Option<Value>>;
3108
3109 fn size_hint(&self) -> Option<usize> {
3111 None
3112 }
3113}
3114
3115pub struct VecRelation {
3121 iter: std::vec::IntoIter<Value>,
3122 len: usize,
3123}
3124
3125impl Relation for VecRelation {
3126 fn next_row(&mut self) -> Result<Option<Value>> {
3127 Ok(self.iter.next())
3128 }
3129 fn size_hint(&self) -> Option<usize> {
3130 Some(self.len)
3131 }
3132}
3133
3134pub fn from_vec(rows: Vec<Value>) -> Box<dyn Relation> {
3136 let len = rows.len();
3137 Box::new(VecRelation { iter: rows.into_iter(), len })
3138}
3139
3140pub type Resolver<'r> = dyn Fn(&str) -> Result<Option<Box<dyn Relation>>> + 'r;
3146
3147pub fn execute(sel: &Select, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
3152 let (cols, rows, _) = execute_explain(sel, resolve, JoinExec::Auto)?;
3153 Ok((cols, rows))
3154}
3155
3156pub fn execute_explain(
3163 sel: &Select,
3164 resolve: &Resolver,
3165 exec: JoinExec,
3166) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3167 execute_with(sel, resolve, exec, true)
3168}
3169
3170#[derive(Debug, Clone, Copy)]
3175pub struct Opts {
3176 pub exec: JoinExec,
3177 pub pushdown: bool,
3178 pub fuse_filter: bool,
3182}
3183
3184impl Default for Opts {
3185 fn default() -> Self {
3186 Opts { exec: JoinExec::Auto, pushdown: true, fuse_filter: true }
3187 }
3188}
3189
3190impl Opts {
3191 pub fn exec(exec: JoinExec) -> Self {
3192 Opts { exec, ..Default::default() }
3193 }
3194}
3195
3196pub fn execute_with(
3203 sel: &Select,
3204 resolve: &Resolver,
3205 exec: JoinExec,
3206 pushdown: bool,
3207) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3208 execute_opts(sel, resolve, Opts { exec, pushdown, ..Default::default() })
3209}
3210
3211pub fn execute_opts(
3213 sel: &Select,
3214 resolve: &Resolver,
3215 opts: Opts,
3216) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3217 execute_inner(sel, resolve, opts, None)
3218}
3219
3220fn row_key(cols: &[OutCol], obj: &Map<String, Value>) -> String {
3222 cols.iter()
3223 .map(|c| format!("{:?}", obj.get(&c.key).unwrap_or(&Value::Null)))
3224 .collect::<Vec<_>>()
3225 .join("\u{1}")
3226}
3227
3228fn execute_set_ops<'a>(
3236 sel: &Select,
3237 resolve: &'a Resolver<'a>,
3238 opts: Opts,
3239 outer: Option<&'a Bound<'a>>,
3240) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3241 let ctx = EvalCtx { resolver: Some(resolve), outer };
3242 let mut head = sel.clone();
3243 head.set_ops.clear();
3244 head.order_by.clear();
3245 head.limit = None;
3246 head.offset = None;
3247 let (cols, rows, mut plan) = execute_inner(&head, resolve, opts, outer)?;
3248 let mut left: Vec<Map<String, Value>> = rows
3249 .into_iter()
3250 .map(|r| match r {
3251 Value::Object(m) => m,
3252 _ => Map::new(),
3253 })
3254 .collect();
3255
3256 for arm in &sel.set_ops {
3257 let (acols, arows, _) = execute_inner(&arm.query, resolve, opts, outer)?;
3258 let op_name = match arm.op {
3259 SetOp::Union => "UNION",
3260 SetOp::Intersect => "INTERSECT",
3261 SetOp::Except => "EXCEPT",
3262 };
3263 if acols.len() != cols.len() {
3264 bail!(
3265 "each {} query must have the same number of columns: {} vs {}",
3266 op_name, cols.len(), acols.len()
3267 );
3268 }
3269 let right: Vec<Map<String, Value>> = arows
3271 .into_iter()
3272 .map(|r| {
3273 let m = match r {
3274 Value::Object(m) => m,
3275 _ => Map::new(),
3276 };
3277 let mut out = Map::new();
3278 for (i, c) in cols.iter().enumerate() {
3279 out.insert(c.key.clone(), m.get(&acols[i].key).cloned().unwrap_or(Value::Null));
3280 }
3281 out
3282 })
3283 .collect();
3284 let (nl, nr) = (left.len(), right.len());
3285 let right_keys: std::collections::HashSet<String> =
3286 right.iter().map(|m| row_key(&cols, m)).collect();
3287 let mut combined: Vec<Map<String, Value>> = match arm.op {
3288 SetOp::Union => {
3289 left.extend(right);
3290 left
3291 }
3292 SetOp::Intersect => left.into_iter().filter(|m| right_keys.contains(&row_key(&cols, m))).collect(),
3293 SetOp::Except => left.into_iter().filter(|m| !right_keys.contains(&row_key(&cols, m))).collect(),
3294 };
3295 if !arm.all {
3296 let mut seen = std::collections::HashSet::new();
3297 combined.retain(|m| seen.insert(row_key(&cols, m)));
3298 }
3299 plan.notes.push(format!(
3300 "{}{}: {} + {} rows -> {} (each arm planned separately; only the first arm's plan is shown)",
3301 op_name, if arm.all { " ALL" } else { "" }, nl, nr, combined.len()
3302 ));
3303 left = combined;
3304 }
3305
3306 let projected: Vec<(Map<String, Value>, JoinedRow)> = left
3309 .into_iter()
3310 .map(|m| {
3311 let src: JoinedRow = vec![(String::new(), Some(Value::Object(m.clone())))];
3312 (m, src)
3313 })
3314 .collect();
3315 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3316 Ok((cols, out, plan))
3317}
3318
3319fn execute_inner<'a>(
3322 sel: &Select,
3323 resolve: &'a Resolver<'a>,
3324 opts: Opts,
3325 outer: Option<&'a Bound<'a>>,
3326) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3327 if !sel.set_ops.is_empty() {
3328 return execute_set_ops(sel, resolve, opts, outer);
3329 }
3330 let ctx = EvalCtx { resolver: Some(resolve), outer };
3331 let exec = opts.exec;
3332 let pushdown = opts.pushdown;
3333 let mut plan = Plan::default();
3334
3335 validate_bindings(sel)?;
3337
3338 let fuse = opts.fuse_filter && sel.where_.is_some() && !sel.joins.is_empty();
3365
3366 let budget: Option<usize> = match sel.limit {
3367 Some(lim)
3368 if sel.order_by.is_empty()
3369 && !sel.distinct
3370 && !sel.joins.is_empty()
3371 && (sel.where_.is_none() || fuse) =>
3372 {
3373 Some(lim.saturating_add(sel.offset.unwrap_or(0)))
3374 }
3375 _ => None,
3376 };
3377 plan.budget = budget;
3378
3379 let all_bindings: Vec<String> = sel
3385 .from
3386 .iter()
3387 .map(|t| t.binding())
3388 .chain(sel.joins.iter().map(|j| j.table.binding()))
3389 .collect();
3390 let nullable = crate::sqlpush::nullable_bindings(sel);
3391 let push = if pushdown {
3392 crate::sqlpush::plan(sel.where_.as_ref(), &all_bindings, &nullable)
3393 } else {
3394 Pushdown::default()
3395 };
3396 plan.refusals = push.refusals.clone();
3397
3398 let mut base_scan_at: Option<usize> = None;
3399 let mut base_prefilter_at: Option<usize> = None;
3400
3401 let mut left_src: Box<dyn LeftSource + 'a> = match &sel.from {
3409 None => {
3410 Box::new(VecLeft { rows: vec![vec![]], at: 0 })
3413 }
3414 Some(t) => {
3415 let rel = fetch(t, resolve, ctx)?;
3416 let binding = t.binding();
3417 base_scan_at = Some(plan.stages.len());
3422 plan.push(Stage::Scan {
3423 table: t.name.clone(),
3424 binding: binding.clone(),
3425 rows: 0,
3426 });
3427 let preds = push.for_binding(&binding).cloned().unwrap_or_default();
3428 if !preds.is_empty() {
3429 base_prefilter_at = Some(plan.stages.len());
3430 plan.push(Stage::Prefilter {
3431 binding: binding.clone(),
3432 predicates: preds.len(),
3433 in_rows: 0,
3434 out_rows: 0,
3435 });
3436 }
3437 Box::new(StreamLeft { rel, binding, preds, pulled: 0, kept: 0, ctx })
3438 }
3439 };
3440
3441 let mut left_bindings: Vec<String> = match &sel.from {
3447 None => vec![],
3448 Some(t) => vec![t.binding()],
3449 };
3450 let last = sel.joins.len().saturating_sub(1);
3451 let mut rows: Vec<JoinedRow> = vec![];
3452 let mut base_pulled: Option<usize> = None;
3453 let mut base_kept: Option<usize> = None;
3454
3455 for (ji, join) in sel.joins.iter().enumerate() {
3456 let is_last = ji == last;
3457 let rb = join.table.binding();
3458
3459 if join.table.lateral {
3463 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3464 let join_budget = if is_last { budget } else { None };
3465 let (out, removed, consumed, produced) = join_lateral(
3466 left_src.as_mut(), join, resolve, join_budget, post, ctx,
3467 )?;
3468 plan.push(Stage::Scan { table: join.table.name.clone(), binding: rb.clone(), rows: produced });
3469 plan.push(Stage::Join {
3470 kind: join.kind,
3471 table: join.table.name.clone(),
3472 binding: rb.clone(),
3473 strategy: Strategy::NestedLoop,
3474 keys: 0,
3475 left_rows: consumed,
3476 right_rows: produced,
3477 out_rows: out.len(),
3478 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3479 post_filter_removed: post.map(|_| removed),
3480 });
3481 plan.notes.push(format!("LATERAL {}: the subquery ran once per left row ({} times)", rb, consumed));
3482 left_bindings.push(rb);
3483 if ji == 0 {
3484 if let Some((pulled, kept)) = left_src.stats() {
3485 base_pulled = Some(pulled);
3486 base_kept = Some(kept);
3487 }
3488 }
3489 left_src = Box::new(VecLeft { rows: out, at: 0 });
3490 continue;
3491 }
3492
3493 let right_rel = fetch(&join.table, resolve, ctx)?;
3494 let right_all = drain(right_rel)?;
3495 plan.push(Stage::Scan {
3496 table: join.table.name.clone(),
3497 binding: rb.clone(),
3498 rows: right_all.len(),
3499 });
3500 let right_rows = prefilter(right_all, &rb, &push, &mut plan, ctx)?;
3501
3502 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3507 let join_budget = if is_last { budget } else { None };
3508
3509 let keys = sqljoin::hash_keys(join.on.as_ref(), &left_bindings, &rb);
3512 let left_hint = left_src.hint().unwrap_or(usize::MAX);
3513 let strategy = sqljoin::choose(exec, keys.len(), left_hint, right_rows.len());
3514
3515 let (out, removed, consumed) = match strategy {
3516 Strategy::NestedLoop => join_nested_loop(
3517 left_src.as_mut(), &left_bindings, join, &right_rows, &rb,
3518 join_budget, post, ctx,
3519 )?,
3520 Strategy::Hash => join_hash(
3521 left_src.as_mut(), &left_bindings, join, &right_rows, &rb, &keys,
3522 join_budget, post, ctx,
3523 )?,
3524 };
3525
3526 plan.push(Stage::Join {
3527 kind: join.kind,
3528 table: join.table.name.clone(),
3529 binding: rb.clone(),
3530 strategy,
3531 keys: keys.len(),
3532 left_rows: consumed,
3533 right_rows: right_rows.len(),
3534 out_rows: out.len(),
3535 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3536 post_filter_removed: post.map(|_| removed),
3537 });
3538 left_bindings.push(rb);
3539 if ji == 0 {
3541 if let Some((pulled, kept)) = left_src.stats() {
3542 base_pulled = Some(pulled);
3543 base_kept = Some(kept);
3544 }
3545 }
3546 rows = out;
3547 left_src = Box::new(VecLeft { rows: std::mem::take(&mut rows), at: 0 });
3549 }
3550
3551 rows = left_src.take_rows();
3554 if let Some(i) = base_scan_at {
3555 if let (Some(pulled), Some(kept)) = (base_pulled, base_kept) {
3556 if let Some(Stage::Scan { rows: r, .. }) = plan.stages.get_mut(i) {
3557 *r = pulled;
3558 }
3559 if let Some(j) = base_prefilter_at {
3560 if let Some(Stage::Prefilter { in_rows, out_rows, .. }) =
3561 plan.stages.get_mut(j)
3562 {
3563 *in_rows = pulled;
3564 *out_rows = kept;
3565 }
3566 }
3567 }
3568 }
3569
3570 if let Some(pred) = sel.where_.as_ref().filter(|_| !fuse) {
3572 let in_rows = rows.len();
3573 let mut kept = Vec::with_capacity(rows.len());
3574 for r in rows {
3575 if truthy(&eval(pred, &bind(&r, ctx))?) == Some(true) {
3579 kept.push(r);
3580 }
3581 }
3582 rows = kept;
3583 plan.push(Stage::Filter { in_rows, out_rows: rows.len() });
3584 }
3585
3586 let grouping = !sel.group_by.is_empty();
3599 let aggregating = grouping
3600 || sel.items.iter().any(|i| has_aggregate(&i.expr))
3601 || sel.having.as_ref().is_some_and(has_aggregate);
3602 if aggregating {
3603 let mut groups: Vec<(Vec<Value>, Vec<JoinedRow>)> = vec![];
3607 if grouping {
3608 for r in rows {
3609 let b = bind(&r, ctx);
3610 let mut key = Vec::with_capacity(sel.group_by.len());
3611 for g in &sel.group_by {
3612 key.push(eval(g, &b)?);
3613 }
3614 match groups.iter_mut().find(|(k, _)| {
3615 k.len() == key.len()
3616 && k.iter().zip(&key).all(|(a, b)| {
3617 (a.is_null() && b.is_null())
3620 || matches!(cmp_values(a, b), Some(std::cmp::Ordering::Equal))
3621 })
3622 }) {
3623 Some((_, bucket)) => bucket.push(r),
3624 None => groups.push((key, vec![r])),
3625 }
3626 }
3627 } else {
3628 groups.push((vec![], rows));
3631 }
3632
3633 let n_groups = groups.len();
3634 let mut cols: Vec<OutCol> = vec![];
3635 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = vec![];
3636 let empty: JoinedRow = vec![];
3637
3638 for (gi, (_key, grows)) in groups.iter().enumerate() {
3639 let scope = grows.first().unwrap_or(&empty);
3644
3645 if let Some(h) = &sel.having {
3646 let folded = fold_aggregates(h, grows, ctx, &sel.group_by)?;
3647 if truthy(&eval(&folded, &bind(scope, ctx))?) != Some(true) {
3648 continue;
3649 }
3650 }
3651
3652 let mut obj = Map::new();
3653 for item in &sel.items {
3654 let folded = fold_aggregates(&item.expr, grows, ctx, &sel.group_by)?;
3655 let v = eval(&folded, &bind(scope, ctx))?;
3656 if gi == 0 {
3658 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3659 let key = unique_key(&cols, &name);
3660 obj.insert(key.clone(), v);
3661 cols.push(OutCol { key, name });
3662 } else {
3663 let idx = obj.len();
3664 if let Some(c) = cols.get(idx) {
3665 obj.insert(c.key.clone(), v);
3666 }
3667 }
3668 }
3669 projected.push((obj, scope.clone()));
3670 }
3671
3672 plan.notes.push(if grouping {
3673 format!(
3674 "GroupAggregate on {} key(s): {} rows -> {} group(s){}",
3675 sel.group_by.len(),
3676 n_rows_before_group(&plan),
3677 n_groups,
3678 if sel.having.is_some() {
3679 format!(", HAVING kept {}", projected.len())
3680 } else {
3681 String::new()
3682 }
3683 )
3684 } else {
3685 format!("Aggregate over {} row(s) -> 1 row", groups[0].1.len())
3686 });
3687 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3688 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3689 return Ok((cols, out, plan));
3690 }
3691
3692 let mut cols: Vec<OutCol> = vec![];
3723 let mut spans: Vec<(usize, usize)> = Vec::with_capacity(sel.items.len());
3724 for item in &sel.items {
3725 let start = cols.len();
3726 match &item.expr {
3727 Expr::Star => {
3728 let mut plain: Vec<String> = vec![];
3729 let mut meta: Vec<String> = vec![];
3730 for r in &rows {
3731 for (n, _) in bind(r, ctx).flatten() {
3732 let target = if n.starts_with('_') { &mut meta } else { &mut plain };
3733 if !target.contains(&n) {
3735 target.push(n);
3736 }
3737 }
3738 }
3739 meta.sort();
3742 for n in plain.into_iter().chain(meta) {
3743 if !cols.iter().any(|c| c.name == n) {
3744 cols.push(OutCol { key: n.clone(), name: n });
3745 }
3746 }
3747 }
3748 Expr::QualifiedStar(q) => {
3749 if let Some(first) = rows.first() {
3750 for (n, _) in bind(first, ctx).flatten_binding(q) {
3751 if !cols.iter().any(|c| c.name == n) {
3752 cols.push(OutCol { key: n.clone(), name: n });
3753 }
3754 }
3755 }
3756 }
3757 _ => {
3758 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3759 let key = unique_key(&cols, &name);
3765 cols.push(OutCol { key, name });
3766 }
3767 }
3768 spans.push((start, cols.len()));
3769 }
3770
3771 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = Vec::with_capacity(rows.len());
3775 for r in rows {
3776 let b = bind(&r, ctx);
3777 let mut obj = Map::new();
3778 for (i, item) in sel.items.iter().enumerate() {
3779 let (start, end) = spans[i];
3780 match &item.expr {
3781 Expr::Star => {
3782 for (n, v) in b.flatten() {
3783 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3784 obj.entry(c.key.clone()).or_insert(v);
3785 }
3786 }
3787 }
3788 Expr::QualifiedStar(q) => {
3789 for (n, v) in b.flatten_binding(q) {
3790 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3791 obj.entry(c.key.clone()).or_insert(v);
3792 }
3793 }
3794 }
3795 _ => {
3796 let v = eval(&item.expr, &b)?;
3797 if let Some(c) = cols.get(start) {
3798 obj.insert(c.key.clone(), v);
3799 }
3800 }
3801 }
3802 }
3803 projected.push((obj, r));
3804 }
3805
3806 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3807
3808 if sel.distinct {
3810 let in_rows = projected.len();
3811 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
3812 projected.retain(|(obj, _)| seen.insert(row_key(&cols, obj)));
3815 plan.push(Stage::Distinct { in_rows, out_rows: projected.len() });
3816 }
3817
3818 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3819 Ok((cols, out, plan))
3820}
3821
3822fn n_rows_before_group(plan: &Plan) -> usize {
3826 plan.stages
3827 .iter()
3828 .rev()
3829 .find_map(|st| match st {
3830 Stage::Filter { out_rows, .. } => Some(*out_rows),
3831 Stage::Join { out_rows, .. } => Some(*out_rows),
3832 Stage::Prefilter { out_rows, .. } => Some(*out_rows),
3833 Stage::Scan { rows, .. } => Some(*rows),
3834 _ => None,
3835 })
3836 .unwrap_or(0)
3837}
3838
3839fn sort_keys(a: &[Value], b: &[Value], order_by: &[OrderBy]) -> std::cmp::Ordering {
3846 for (i, ob) in order_by.iter().enumerate() {
3847 let (Some(x), Some(y)) = (a.get(i), b.get(i)) else { continue };
3848 let ord = match (x.is_null(), y.is_null()) {
3849 (true, true) => std::cmp::Ordering::Equal,
3850 (true, false) => {
3853 return if ob.nulls_first {
3854 std::cmp::Ordering::Less
3855 } else {
3856 std::cmp::Ordering::Greater
3857 }
3858 }
3859 (false, true) => {
3860 return if ob.nulls_first {
3861 std::cmp::Ordering::Greater
3862 } else {
3863 std::cmp::Ordering::Less
3864 }
3865 }
3866 (false, false) => cmp_values(x, y).unwrap_or(std::cmp::Ordering::Equal),
3867 };
3868 let ord = if matches!(ob.dir, Dir::Desc) { ord.reverse() } else { ord };
3869 if !ord.is_eq() {
3870 return ord;
3871 }
3872 }
3873 std::cmp::Ordering::Equal
3874}
3875
3876fn finish(
3878 sel: &Select,
3879 cols: &[OutCol],
3880 mut projected: Vec<(Map<String, Value>, JoinedRow)>,
3881 ctx: EvalCtx,
3882 plan: &mut Plan,
3883) -> Result<Vec<Value>> {
3884 if !sel.order_by.is_empty() {
3886 let mut keyed: Vec<(Vec<Value>, (Map<String, Value>, JoinedRow))> = vec![];
3890 for (obj, src) in projected {
3891 let mut key = vec![];
3892 for ob in &sel.order_by {
3893 let v = match (ob.ordinal, &ob.expr) {
3894 (Some(n), _) => {
3895 let c = cols.get(n - 1).ok_or_else(|| {
3896 anyhow::anyhow!(
3897 "ORDER BY {} is out of range: the select list has {} \
3898 column(s)", n, cols.len())
3899 })?;
3900 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3901 }
3902 (None, Some(Expr::Column { qual: None, name }))
3907 if cols.iter().any(|c| c.name == *name) =>
3908 {
3909 let c = cols.iter().find(|c| c.name == *name).expect("checked");
3910 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3911 }
3912 (None, Some(e)) => {
3913 eval(e, &bind(&src, ctx))?
3917 }
3918 (None, None) => Value::Null,
3919 };
3920 key.push(v);
3921 }
3922 keyed.push((key, (obj, src)));
3923 }
3924
3925 keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, &sel.order_by));
3926
3927 projected = keyed.into_iter().map(|(_, row)| row).collect();
3928 plan.push(Stage::Sort { keys: sel.order_by.len(), rows: projected.len() });
3929 }
3930
3931 let mut out: Vec<Value> = projected
3933 .into_iter()
3934 .map(|(obj, _)| Value::Object(obj))
3935 .collect();
3936 let in_rows = out.len();
3937 if let Some(off) = sel.offset {
3938 out = if off >= out.len() { vec![] } else { out.split_off(off) };
3939 }
3940 if let Some(lim) = sel.limit {
3941 out.truncate(lim);
3942 }
3943 if sel.limit.is_some() || sel.offset.is_some() {
3944 plan.push(Stage::Limit {
3945 limit: sel.limit,
3946 offset: sel.offset,
3947 in_rows,
3948 out_rows: out.len(),
3949 });
3950 }
3951
3952 Ok(out)
3953}
3954
3955fn keep_row(cand: &JoinedRow, post: Option<&Expr>, removed: &mut usize, ctx: EvalCtx) -> Result<bool> {
3988 let Some(p) = post else { return Ok(true) };
3989 if truthy(&eval(p, &bind(cand, ctx))?) == Some(true) {
3991 Ok(true)
3992 } else {
3993 *removed += 1;
3994 Ok(false)
3995 }
3996}
3997
3998#[allow(clippy::too_many_arguments)]
4004fn emit_unmatched_right(
4005 out: &mut Vec<JoinedRow>,
4006 kind: JoinKind,
4007 left_bindings: &[String],
4008 right_rows: &[Value],
4009 right_matched: &[bool],
4010 rb: &str,
4011 post: Option<&Expr>,
4012 removed: &mut usize,
4013 ctx: EvalCtx,
4014) -> Result<()> {
4015 if !matches!(kind, JoinKind::Right | JoinKind::Full) {
4016 return Ok(());
4017 }
4018 for (ri, right) in right_rows.iter().enumerate() {
4019 if right_matched[ri] {
4020 continue;
4021 }
4022 let mut cand: JoinedRow = left_bindings.iter().map(|b| (b.clone(), None)).collect();
4023 cand.push((rb.to_string(), Some(right.clone())));
4024 if keep_row(&cand, post, removed, ctx)? {
4027 out.push(cand);
4028 }
4029 }
4030 Ok(())
4031}
4032
4033fn join_lateral(
4040 left_src: &mut dyn LeftSource,
4041 join: &Join,
4042 resolve: &Resolver,
4043 budget: Option<usize>,
4044 post: Option<&Expr>,
4045 ctx: EvalCtx,
4046) -> Result<(Vec<JoinedRow>, usize, usize, usize)> {
4047 let sub = join
4048 .table
4049 .sub
4050 .as_deref()
4051 .ok_or_else(|| anyhow::anyhow!("LATERAL requires a subquery"))?;
4052 let rb = join.table.binding();
4053 let mut out: Vec<JoinedRow> = vec![];
4054 let mut removed = 0usize;
4055 let mut consumed = 0usize;
4056 let mut produced = 0usize;
4057 while let Some(left) = {
4058 if budget.is_some_and(|b| out.len() >= b) { None } else { left_src.next_left()? }
4059 } {
4060 consumed += 1;
4061 let scope = bind(&left, ctx);
4062 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), Some(&scope))?;
4063 produced += rows.len();
4064 let mut matched = false;
4065 for r in rows {
4066 let m = match r {
4067 Value::Object(m) => m,
4068 _ => Map::new(),
4069 };
4070 let mut named = Map::new();
4071 for (i, c) in cols.iter().enumerate() {
4072 let name = join.table.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
4073 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
4074 }
4075 let mut cand = left.clone();
4076 cand.push((rb.clone(), Some(Value::Object(named))));
4077 let on_ok = match &join.on {
4078 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4079 None => true,
4080 };
4081 if !on_ok {
4082 continue;
4083 }
4084 matched = true;
4085 if keep_row(&cand, post, &mut removed, ctx)? {
4086 out.push(cand);
4087 }
4088 }
4089 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4090 let mut cand = left.clone();
4091 cand.push((rb.clone(), None));
4092 if keep_row(&cand, post, &mut removed, ctx)? {
4093 out.push(cand);
4094 }
4095 }
4096 }
4097 Ok((out, removed, consumed, produced))
4098}
4099
4100#[allow(clippy::too_many_arguments)]
4106fn join_nested_loop(
4107 left_src: &mut dyn LeftSource,
4108 left_bindings: &[String],
4109 join: &Join,
4110 right_rows: &[Value],
4111 rb: &str,
4112 budget: Option<usize>,
4113 post: Option<&Expr>,
4114 ctx: EvalCtx,
4115) -> Result<(Vec<JoinedRow>, usize, usize)> {
4116 let mut out: Vec<JoinedRow> = vec![];
4117 let mut removed = 0usize;
4118 let mut right_matched = vec![false; right_rows.len()];
4120
4121 let mut consumed = 0usize;
4122 while let Some(left) = {
4123 if budget.is_some_and(|b| out.len() >= b) {
4124 None
4127 } else {
4128 left_src.next_left()?
4129 }
4130 } {
4131 consumed += 1;
4132 let left = &left;
4133 let mut matched = false;
4136 for (ri, right) in right_rows.iter().enumerate() {
4137 let mut cand: JoinedRow = left.clone();
4138 cand.push((rb.to_string(), Some(right.clone())));
4139 let joins_here = match &join.on {
4140 None => true,
4142 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4146 };
4147 if joins_here {
4148 matched = true;
4149 right_matched[ri] = true;
4150 if keep_row(&cand, post, &mut removed, ctx)? {
4151 out.push(cand);
4152 }
4153 }
4154 }
4155 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4157 let mut cand: JoinedRow = left.clone();
4158 cand.push((rb.to_string(), None));
4159 if keep_row(&cand, post, &mut removed, ctx)? {
4160 out.push(cand);
4161 }
4162 }
4163 }
4164
4165 if !budget.is_some_and(|b| out.len() >= b) {
4170 emit_unmatched_right(
4171 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4172 &mut removed, ctx,
4173 )?;
4174 }
4175 Ok((out, removed, consumed))
4176}
4177
4178#[allow(clippy::too_many_arguments)]
4186fn join_hash(
4187 left_src: &mut dyn LeftSource,
4188 left_bindings: &[String],
4189 join: &Join,
4190 right_rows: &[Value],
4191 rb: &str,
4192 keys: &[(Expr, Expr)],
4193 budget: Option<usize>,
4194 post: Option<&Expr>,
4195 ctx: EvalCtx,
4196) -> Result<(Vec<JoinedRow>, usize, usize)> {
4197 debug_assert!(!keys.is_empty(), "the planner must not choose Hash with no keys");
4198
4199 let side = sqljoin::HashSide::build(right_rows.len(), |i| {
4201 let one: JoinedRow = vec![(rb.to_string(), Some(right_rows[i].clone()))];
4204 let b = bind(&one, ctx);
4205 let mut k = Vec::with_capacity(keys.len());
4206 for (_, right_expr) in keys {
4207 match sqljoin::hkey(&eval(right_expr, &b)?) {
4208 Some(h) => k.push(h),
4209 None => return Ok(None),
4211 }
4212 }
4213 Ok(Some(k))
4214 })?;
4215
4216 let mut out: Vec<JoinedRow> = vec![];
4218 let mut removed = 0usize;
4219 let mut right_matched = vec![false; right_rows.len()];
4220
4221 let mut consumed = 0usize;
4222 while let Some(left) = {
4223 if budget.is_some_and(|b| out.len() >= b) {
4224 None
4225 } else {
4226 left_src.next_left()?
4227 }
4228 } {
4229 consumed += 1;
4230 let left = &left;
4231 let lb = bind(left, ctx);
4232 let mut lk = Vec::with_capacity(keys.len());
4233 let mut null_key = false;
4234 for (left_expr, _) in keys {
4235 match sqljoin::hkey(&eval(left_expr, &lb)?) {
4236 Some(h) => lk.push(h),
4237 None => {
4238 null_key = true;
4239 break;
4240 }
4241 }
4242 }
4243
4244 let mut matched = false;
4245 if !null_key {
4249 for &ri in side.probe(&lk) {
4250 let mut cand: JoinedRow = left.clone();
4251 cand.push((rb.to_string(), Some(right_rows[ri].clone())));
4252 let joins_here = match &join.on {
4254 None => true,
4255 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4256 };
4257 if joins_here {
4258 matched = true;
4259 right_matched[ri] = true;
4260 if keep_row(&cand, post, &mut removed, ctx)? {
4261 out.push(cand);
4262 }
4263 }
4264 }
4265 }
4266 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4267 let mut cand: JoinedRow = left.clone();
4268 cand.push((rb.to_string(), None));
4269 if keep_row(&cand, post, &mut removed, ctx)? {
4270 out.push(cand);
4271 }
4272 }
4273 }
4274
4275 if !budget.is_some_and(|b| out.len() >= b) {
4278 emit_unmatched_right(
4279 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4280 &mut removed, ctx,
4281 )?;
4282 }
4283 Ok((out, removed, consumed))
4284}
4285
4286fn prefilter(
4293 rows: Vec<Value>,
4294 binding: &str,
4295 push: &Pushdown,
4296 plan: &mut Plan,
4297 ctx: EvalCtx,
4298) -> Result<Vec<Value>> {
4299 let Some(preds) = push.for_binding(binding) else { return Ok(rows) };
4300 if preds.is_empty() {
4301 return Ok(rows);
4302 }
4303 let in_rows = rows.len();
4304 let mut kept = Vec::with_capacity(rows.len());
4305 for row in rows {
4306 let one: JoinedRow = vec![(binding.to_string(), Some(row))];
4307 let b = bind(&one, ctx);
4308 let mut keep = true;
4309 for p in preds {
4310 if truthy(&eval(p, &b)?) != Some(true) {
4316 keep = false;
4317 break;
4318 }
4319 }
4320 if keep {
4321 if let Some((_, Some(v))) = one.into_iter().next() {
4323 kept.push(v);
4324 }
4325 }
4326 }
4327 plan.push(Stage::Prefilter {
4328 binding: binding.to_string(),
4329 predicates: preds.len(),
4330 in_rows,
4331 out_rows: kept.len(),
4332 });
4333 Ok(kept)
4334}
4335
4336fn fetch(t: &TableRef, resolve: &Resolver, ctx: EvalCtx) -> Result<Box<dyn Relation>> {
4339 if let Some(sub) = &t.sub {
4343 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), ctx.outer)?;
4344 let out = rows
4345 .into_iter()
4346 .map(|r| {
4347 let m = match r {
4348 Value::Object(m) => m,
4349 _ => Map::new(),
4350 };
4351 let mut named = Map::new();
4352 for (i, c) in cols.iter().enumerate() {
4353 let name = t.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
4354 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
4357 }
4358 Value::Object(named)
4359 })
4360 .collect();
4361 return Ok(from_vec(out));
4362 }
4363
4364 if let Some(args) = &t.args {
4368 let empty: JoinedRow = vec![];
4369 let scope = bind(&empty, ctx);
4370 let col = |i: usize, default: &str| -> String {
4371 t.col_aliases.get(i).cloned().unwrap_or_else(|| default.to_string())
4372 };
4373 let rows: Vec<Value> = match t.name.as_str() {
4374 "generate_series" => {
4375 let a = num(&eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_series() needs a start"))?, &scope)?);
4376 let b = num(&eval(args.get(1).ok_or_else(|| anyhow::anyhow!("generate_series() needs a stop"))?, &scope)?);
4377 let step = match args.get(2) {
4378 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4379 None => 1.0,
4380 };
4381 match (a, b) {
4382 (Some(a), Some(b)) if step != 0.0 => {
4384 let mut out = vec![];
4385 let mut x = a;
4386 while (step > 0.0 && x <= b) || (step < 0.0 && x >= b) {
4387 let mut m = Map::new();
4388 m.insert(col(0, "generate_series"), from_f64(x));
4389 out.push(Value::Object(m));
4390 x += step;
4391 if out.len() > 1_000_000 {
4392 bail!("generate_series() would produce more than a million rows");
4393 }
4394 }
4395 out
4396 }
4397 (Some(_), Some(_)) => bail!("generate_series() step cannot equal zero"),
4398 _ => vec![],
4399 }
4400 }
4401 "unnest" => match eval(args.first().ok_or_else(|| anyhow::anyhow!("unnest() needs an array"))?, &scope)? {
4402 Value::Array(items) => items
4403 .into_iter()
4404 .map(|v| {
4405 let mut m = Map::new();
4406 m.insert(col(0, "unnest"), v);
4407 Value::Object(m)
4408 })
4409 .collect(),
4410 _ => vec![],
4412 },
4413 "generate_subscripts" => {
4418 let dim = match args.get(1) {
4419 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4420 None => 1.0,
4421 };
4422 match eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_subscripts() needs an array"))?, &scope)? {
4423 Value::Array(items) if dim == 1.0 => (1..=items.len())
4427 .map(|i| {
4428 let mut m = Map::new();
4429 m.insert(col(0, "generate_subscripts"), from_f64(i as f64));
4430 Value::Object(m)
4431 })
4432 .collect(),
4433 _ => vec![],
4434 }
4435 }
4436 "pg_partition_tree" | "pg_partition_ancestors" => vec![],
4439 other => bail!(
4440 "the table function {}() is not implemented. It is refused rather \
4441 than answered with no rows, because an empty relation reads as \
4442 missing DATA rather than a missing feature",
4443 other
4444 ),
4445 };
4446 return Ok(from_vec(rows));
4447 }
4448
4449 match resolve(&t.name)? {
4450 Some(rel) => Ok(rel),
4451 None => bail!("relation {:?} does not exist", t.name),
4454 }
4455}
4456
4457trait LeftSource {
4464 fn next_left(&mut self) -> Result<Option<JoinedRow>>;
4465 fn hint(&self) -> Option<usize>;
4467 fn take_rows(&mut self) -> Vec<JoinedRow>;
4469 fn stats(&self) -> Option<(usize, usize)> {
4471 None
4472 }
4473}
4474
4475struct StreamLeft<'a> {
4481 rel: Box<dyn Relation>,
4482 binding: String,
4483 preds: Vec<Expr>,
4484 ctx: EvalCtx<'a>,
4485 pulled: usize,
4489 kept: usize,
4490}
4491
4492impl<'a> LeftSource for StreamLeft<'a> {
4493 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4494 while let Some(row) = self.rel.next_row()? {
4495 self.pulled += 1;
4496 let one: JoinedRow = vec![(self.binding.clone(), Some(row))];
4497 if !self.preds.is_empty() {
4498 let b = bind(&one, self.ctx);
4499 let mut keep = true;
4500 for p in &self.preds {
4501 if truthy(&eval(p, &b)?) != Some(true) {
4502 keep = false;
4503 break;
4504 }
4505 }
4506 if !keep {
4507 continue;
4508 }
4509 }
4510 self.kept += 1;
4511 return Ok(Some(one));
4512 }
4513 Ok(None)
4514 }
4515 fn hint(&self) -> Option<usize> {
4516 self.rel.size_hint()
4521 }
4522 fn take_rows(&mut self) -> Vec<JoinedRow> {
4523 let mut out = vec![];
4526 while let Ok(Some(r)) = self.next_left() {
4527 out.push(r);
4528 }
4529 out
4530 }
4531 fn stats(&self) -> Option<(usize, usize)> {
4532 Some((self.pulled, self.kept))
4533 }
4534}
4535
4536struct VecLeft {
4539 rows: Vec<JoinedRow>,
4540 at: usize,
4541}
4542
4543impl LeftSource for VecLeft {
4544 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4545 let r = self.rows.get(self.at).cloned();
4546 if r.is_some() {
4547 self.at += 1;
4548 }
4549 Ok(r)
4550 }
4551 fn hint(&self) -> Option<usize> {
4552 Some(self.rows.len().saturating_sub(self.at))
4553 }
4554 fn take_rows(&mut self) -> Vec<JoinedRow> {
4555 let mut v = std::mem::take(&mut self.rows);
4556 if self.at > 0 {
4557 v = v.split_off(self.at);
4558 }
4559 self.at = 0;
4560 v
4561 }
4562}
4563
4564fn drain(mut rel: Box<dyn Relation>) -> Result<Vec<Value>> {
4571 let mut out = Vec::with_capacity(rel.size_hint().unwrap_or(0));
4572 while let Some(row) = rel.next_row()? {
4573 out.push(row);
4574 }
4575 Ok(out)
4576}
4577
4578pub fn run(sql: &str, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
4580 let sel = parse(sql)?;
4581 execute(&sel, resolve)
4582}
4583
4584#[cfg(test)]
4585mod lexer_tests {
4586 use super::*;
4587
4588 fn kinds(src: &str) -> Vec<Tok> {
4589 let mut t = lex(src).expect("lexes");
4590 t.pop(); t
4592 }
4593
4594 #[test]
4595 fn a_word_keeps_both_its_canonical_and_raw_spelling() {
4596 assert_eq!(
4599 kinds("Select"),
4600 vec![Tok::Word { upper: "SELECT".into(), raw: "Select".into() }]
4601 );
4602 }
4603
4604 #[test]
4605 fn a_quoted_identifier_is_never_a_keyword() {
4606 assert_eq!(kinds(r#""select""#), vec![Tok::Quoted("select".into())]);
4607 assert_eq!(kinds(r#""Name""#), vec![Tok::Quoted("Name".into())]);
4609 }
4610
4611 #[test]
4612 fn a_doubled_quote_is_one_literal_quote() {
4613 assert_eq!(kinds("'it''s'"), vec![Tok::Str("it's".into())]);
4614 assert_eq!(kinds(r#""a""b""#), vec![Tok::Quoted("a\"b".into())]);
4615 }
4616
4617 #[test]
4618 fn an_E_string_decodes_the_escapes_catalogue_sql_uses() {
4619 assert_eq!(kinds(r"E'\n'"), vec![Tok::Str("\n".into())]);
4621 assert_eq!(kinds(r"E'a\tb'"), vec![Tok::Str("a\tb".into())]);
4622 assert_eq!(kinds(r"E'\q'"), vec![Tok::Str("q".into())]);
4624 }
4625
4626 #[test]
4627 fn operators_match_longest_first() {
4628 assert_eq!(kinds("!~*"), vec![Tok::Op("!~*".into())]);
4630 assert_eq!(kinds("!~"), vec![Tok::Op("!~".into())]);
4631 assert_eq!(kinds("~*"), vec![Tok::Op("~*".into())]);
4632 assert_eq!(kinds("<>"), vec![Tok::Op("<>".into())]);
4633 assert_eq!(kinds("!="), vec![Tok::Op("!=".into())]);
4634 assert_eq!(kinds(">="), vec![Tok::Op(">=".into())]);
4635 assert_eq!(kinds("::"), vec![Tok::Op("::".into())]);
4636 assert_eq!(kinds("||"), vec![Tok::Op("||".into())]);
4637 assert_eq!(kinds("~"), vec![Tok::Op("~".into())]);
4638 }
4639
4640 #[test]
4641 fn comments_are_skipped_including_nested_block_comments() {
4642 assert_eq!(kinds("1 -- trailing\n"), vec![Tok::Num(1.0)]);
4643 assert_eq!(kinds("1 /* a */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4644 assert_eq!(kinds("1 /* a /* b */ c */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4646 assert!(lex("1 /* unterminated").is_err());
4647 }
4648
4649 #[test]
4650 fn numbers_parse_including_fractions_and_exponents() {
4651 assert_eq!(kinds("42"), vec![Tok::Num(42.0)]);
4652 assert_eq!(kinds("4.5"), vec![Tok::Num(4.5)]);
4653 assert_eq!(kinds(".5"), vec![Tok::Num(0.5)]);
4654 assert_eq!(kinds("1e3"), vec![Tok::Num(1000.0)]);
4655 assert_eq!(kinds("1e-2"), vec![Tok::Num(0.01)]);
4656 assert_eq!(
4658 kinds("1e"),
4659 vec![Tok::Num(1.0), Tok::Word { upper: "E".into(), raw: "e".into() }]
4660 );
4661 }
4662
4663 #[test]
4664 fn an_unterminated_literal_is_an_error_not_a_truncation() {
4665 assert!(lex("'abc").is_err());
4666 assert!(lex(r#""abc"#).is_err());
4667 }
4668
4669 #[test]
4670 fn an_unknown_character_is_REFUSED_rather_than_skipped() {
4671 let e = lex("SELECT 1 @ 2").unwrap_err().to_string();
4674 assert!(e.contains('@'), "{}", e);
4675 }
4676
4677 #[test]
4678 fn the_real_dn_query_lexes() {
4679 let sql = r#"SELECT n.nspname AS "Name",
4680 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
4681 FROM pg_catalog.pg_namespace n
4682 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
4683 ORDER BY 1;"#;
4684 let toks = lex(sql).expect("psql's \\dn must lex");
4685 assert!(toks.contains(&Tok::Quoted("Name".into())));
4686 assert!(toks.contains(&Tok::Op("!~".into())));
4687 assert!(toks.contains(&Tok::Op("<>".into())));
4688 assert!(toks.contains(&Tok::Str("^pg_".into())));
4689 }
4690
4691 #[test]
4692 fn the_real_dt_query_lexes() {
4693 let sql = r#"SELECT n.nspname as "Schema", c.relname as "Name",
4694 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' END as "Type",
4695 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
4696 FROM pg_catalog.pg_class c
4697 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
4698 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
4699 WHERE c.relkind IN ('r','p','')
4700 AND n.nspname <> 'pg_catalog'
4701 AND n.nspname !~ '^pg_toast'
4702 AND pg_catalog.pg_table_is_visible(c.oid)
4703 ORDER BY 1,2;"#;
4704 let toks = lex(sql).expect("psql's \\dt must lex");
4705 assert!(toks.iter().any(|t| t.is_kw("CASE")));
4706 assert!(toks.iter().any(|t| t.is_kw("LEFT")));
4707 assert!(toks.iter().any(|t| t.is_kw("JOIN")));
4708 assert!(toks.contains(&Tok::Str(String::new())));
4710 }
4711}
4712
4713#[cfg(test)]
4714mod parser_tests {
4715 use super::*;
4716 use serde_json::json;
4717
4718 fn col(qual: Option<&str>, name: &str) -> Expr {
4719 Expr::Column { qual: qual.map(str::to_string), name: name.to_string() }
4720 }
4721
4722 #[test]
4723 fn a_bare_select_list_and_from() {
4724 let s = parse("SELECT a, b FROM t").unwrap();
4725 assert_eq!(s.items.len(), 2);
4726 assert_eq!(s.items[0].expr, col(None, "a"));
4727 assert_eq!(s.from.unwrap().name, "t");
4728 }
4729
4730 #[test]
4734 fn as_of_system_time_is_read_per_table() {
4735 let s = parse("SELECT total FROM orders AS OF SYSTEM TIME 42").unwrap();
4736 assert_eq!(s.from.clone().unwrap().as_of, Some(42));
4737 assert_eq!(s.from.unwrap().alias, None);
4738
4739 let s = parse("SELECT o.total FROM orders AS OF SYSTEM TIME 42 o").unwrap();
4741 let t = s.from.unwrap();
4742 assert_eq!((t.as_of, t.alias.as_deref()), (Some(42), Some("o")));
4743
4744 let t = parse("SELECT x FROM orders AS o").unwrap().from.unwrap();
4749 assert_eq!((t.as_of, t.alias.as_deref()), (None, Some("o")));
4750
4751 let s = parse(
4754 "SELECT o.total, n.total FROM orders AS OF SYSTEM TIME 1 o \
4755 JOIN orders n ON o._id = n._id").unwrap();
4756 assert_eq!(s.from.unwrap().as_of, Some(1));
4757 assert_eq!(s.joins[0].table.as_of, None);
4758
4759 let s = parse("SELECT total FROM orders AS OF SYSTEM TIME 7 WHERE _id = '1'").unwrap();
4761 assert_eq!(s.from.unwrap().as_of, Some(7));
4762 assert!(s.where_.is_some());
4763 }
4764
4765 #[test]
4770 fn nqls_verbs_are_table_qualifiers_in_sql() {
4771 let t = parse("SELECT _id FROM orders SEARCH 'acme'").unwrap().from.unwrap();
4772 assert_eq!(t.search.as_deref(), Some("acme"));
4773
4774 let t = parse("SELECT _id FROM orders VALID AS OF '2026-01-01'").unwrap().from.unwrap();
4775 assert_eq!(t.valid_as_of.as_deref(), Some("2026-01-01"));
4776
4777 let t = parse(
4779 "SELECT o._id FROM orders AS OF SYSTEM TIME 9 VALID AS OF '2026-01-01' \
4780 SEARCH 'acme' o").unwrap().from.unwrap();
4781 assert_eq!(
4782 (t.as_of, t.valid_as_of.as_deref(), t.search.as_deref(), t.alias.as_deref()),
4783 (Some(9), Some("2026-01-01"), Some("acme"), Some("o")));
4784
4785 let s = parse(
4787 "SELECT o._id FROM orders SEARCH 'acme' o JOIN drivers d ON o.driver = d._id")
4788 .unwrap();
4789 assert_eq!(s.from.unwrap().search.as_deref(), Some("acme"));
4790 assert_eq!(s.joins[0].table.search, None);
4791 }
4792
4793 #[test]
4801 fn the_new_verbs_do_not_steal_names_that_already_worked() {
4802 let t = parse("SELECT search.total FROM orders search").unwrap().from.unwrap();
4803 assert_eq!((t.alias.as_deref(), t.search.as_deref()), (Some("search"), None));
4804
4805 let t = parse("SELECT valid.total FROM orders valid").unwrap().from.unwrap();
4806 assert_eq!((t.alias.as_deref(), t.valid_as_of.as_deref()), (Some("valid"), None));
4807
4808 assert!(parse("SELECT search, valid FROM orders").is_ok());
4810 assert!(parse("SELECT _id FROM orders WHERE search = 'x'").is_ok());
4811
4812 let t = parse("SELECT x FROM orders AS valid").unwrap().from.unwrap();
4814 assert_eq!(t.alias.as_deref(), Some("valid"));
4815 }
4816
4817 #[test]
4818 fn a_verbs_argument_is_refused_when_it_is_the_wrong_kind_of_thing() {
4819 let e = parse("SELECT _id FROM orders VALID AS OF 42").unwrap_err().to_string();
4823 assert!(e.contains("date string"), "{}", e);
4824 }
4825
4826 #[test]
4827 fn a_wall_clock_as_of_is_refused_with_the_reason() {
4828 for sql in [
4832 "SELECT total FROM orders AS OF SYSTEM TIME '2026-01-01'",
4833 "SELECT total FROM orders AS OF SYSTEM TIME now()",
4834 "SELECT total FROM orders AS OF SYSTEM TIME -1",
4835 "SELECT total FROM orders AS OF SYSTEM TIME 1.5",
4836 ] {
4837 let e = parse(sql).unwrap_err().to_string();
4838 assert!(e.contains("sequence number"), "{} -> {}", sql, e);
4839 }
4840 }
4841
4842 #[test]
4843 fn a_clause_keyword_is_never_read_as_a_bare_alias() {
4844 let s = parse("SELECT a FROM t WHERE a = 1").unwrap();
4848 assert_eq!(s.from.clone().unwrap().alias, None);
4849 assert!(s.where_.is_some(), "the WHERE clause must survive");
4850 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4851 assert_eq!(s.from.unwrap().alias, None);
4852 assert_eq!(s.order_by.len(), 1);
4853 }
4854
4855 #[test]
4856 fn a_real_alias_is_kept_in_both_spellings() {
4857 assert_eq!(parse("SELECT a FROM t x").unwrap().from.unwrap().alias,
4858 Some("x".to_string()));
4859 assert_eq!(parse("SELECT a FROM t AS x").unwrap().from.unwrap().alias,
4860 Some("x".to_string()));
4861 }
4862
4863 #[test]
4864 fn a_tables_binding_is_its_alias_else_its_bare_name() {
4865 let t = TableRef::named("pg_catalog.pg_class", Some("c".into()));
4866 assert_eq!(t.binding(), "c");
4867 let t = TableRef::named("pg_catalog.pg_class", None);
4868 assert_eq!(t.binding(), "pg_class", "the schema is not how a column is addressed");
4869 }
4870
4871 #[test]
4872 fn a_qualified_column_keeps_only_its_immediate_qualifier() {
4873 assert_eq!(parse("SELECT n.nspname FROM x").unwrap().items[0].expr,
4874 col(Some("n"), "nspname"));
4875 assert_eq!(parse("SELECT public.orders.id FROM x").unwrap().items[0].expr,
4878 col(Some("orders"), "id"));
4879 }
4880
4881 #[test]
4882 fn an_alias_may_be_a_quoted_string_with_significant_case() {
4883 let s = parse(r#"SELECT n.nspname AS "Name" FROM x"#).unwrap();
4884 assert_eq!(s.items[0].alias, Some("Name".to_string()));
4885 }
4886
4887 #[test]
4888 fn a_schema_qualified_function_drops_its_schema() {
4889 let s = parse("SELECT pg_catalog.pg_get_userbyid(n.nspowner) FROM x").unwrap();
4892 match &s.items[0].expr {
4893 Expr::Func { name, args } => {
4894 assert_eq!(name, "pg_get_userbyid");
4895 assert_eq!(args.len(), 1);
4896 assert_eq!(args[0], col(Some("n"), "nspowner"));
4897 }
4898 other => panic!("{:?}", other),
4899 }
4900 }
4901
4902 #[test]
4903 fn operator_precedence_matches_sql() {
4904 let s = parse("SELECT 1 FROM t WHERE a = 1 OR b = 2 AND c = 3").unwrap();
4907 match s.where_.unwrap() {
4908 Expr::Binary { op, right, .. } => {
4909 assert_eq!(op, "OR");
4910 assert!(matches!(*right, Expr::Binary { ref op, .. } if op == "AND"),
4911 "AND must bind tighter than OR");
4912 }
4913 other => panic!("{:?}", other),
4914 }
4915 let s = parse("SELECT 1 FROM t WHERE a = 1 AND b = 2").unwrap();
4917 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4918 let s = parse("SELECT 1 + 2 * 3 FROM t").unwrap();
4920 match &s.items[0].expr {
4921 Expr::Binary { op, right, .. } => {
4922 assert_eq!(op, "+");
4923 assert!(matches!(**right, Expr::Binary { ref op, .. } if op == "*"));
4924 }
4925 other => panic!("{:?}", other),
4926 }
4927 }
4928
4929 #[test]
4930 fn parentheses_override_precedence() {
4931 let s = parse("SELECT 1 FROM t WHERE (a = 1 OR b = 2) AND c = 3").unwrap();
4932 match s.where_.unwrap() {
4933 Expr::Binary { op, left, .. } => {
4934 assert_eq!(op, "AND");
4935 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "OR"));
4936 }
4937 other => panic!("{:?}", other),
4938 }
4939 }
4940
4941 #[test]
4942 fn in_and_is_null_and_between_parse_in_both_polarities() {
4943 let s = parse("SELECT 1 FROM t WHERE k IN ('r','p','')").unwrap();
4944 match s.where_.unwrap() {
4945 Expr::InList { list, negated, .. } => {
4946 assert_eq!(list.len(), 3);
4947 assert!(!negated);
4948 assert_eq!(list[2], Expr::Literal(json!("")));
4950 }
4951 other => panic!("{:?}", other),
4952 }
4953 assert!(matches!(parse("SELECT 1 FROM t WHERE k NOT IN (1)").unwrap().where_.unwrap(),
4954 Expr::InList { negated: true, .. }));
4955 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NULL").unwrap().where_.unwrap(),
4956 Expr::IsNull { negated: false, .. }));
4957 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NOT NULL").unwrap().where_.unwrap(),
4958 Expr::IsNull { negated: true, .. }));
4959 let s = parse("SELECT 1 FROM t WHERE n BETWEEN 1 AND 5").unwrap();
4961 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4962 }
4963
4964 #[test]
4965 fn both_case_spellings_parse() {
4966 let s = parse("SELECT CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
4968 ELSE 'other' END FROM t").unwrap();
4969 match &s.items[0].expr {
4970 Expr::Case { operand, whens, else_ } => {
4971 assert!(operand.is_some());
4972 assert_eq!(whens.len(), 2);
4973 assert!(else_.is_some());
4974 }
4975 other => panic!("{:?}", other),
4976 }
4977 let s = parse("SELECT CASE WHEN k = 'r' THEN 1 END FROM t").unwrap();
4979 match &s.items[0].expr {
4980 Expr::Case { operand, whens, else_ } => {
4981 assert!(operand.is_none());
4982 assert_eq!(whens.len(), 1);
4983 assert!(else_.is_none());
4984 }
4985 other => panic!("{:?}", other),
4986 }
4987 assert!(parse("SELECT CASE k END FROM t").is_err());
4989 }
4990
4991 #[test]
4992 fn every_join_flavour_parses_and_an_inner_join_demands_ON() {
4993 for (sql, kind) in [
4994 ("SELECT 1 FROM a JOIN b ON a.x = b.x", JoinKind::Inner),
4995 ("SELECT 1 FROM a INNER JOIN b ON a.x = b.x", JoinKind::Inner),
4996 ("SELECT 1 FROM a LEFT JOIN b ON a.x = b.x", JoinKind::Left),
4997 ("SELECT 1 FROM a LEFT OUTER JOIN b ON a.x = b.x", JoinKind::Left),
4998 ("SELECT 1 FROM a RIGHT JOIN b ON a.x = b.x", JoinKind::Right),
4999 ("SELECT 1 FROM a FULL OUTER JOIN b ON a.x = b.x", JoinKind::Full),
5000 ("SELECT 1 FROM a CROSS JOIN b", JoinKind::Cross),
5001 ] {
5002 let s = parse(sql).unwrap_or_else(|e| panic!("{}: {}", sql, e));
5003 assert_eq!(s.joins.len(), 1, "{}", sql);
5004 assert_eq!(s.joins[0].kind, kind, "{}", sql);
5005 }
5006 let s = parse("SELECT 1 FROM a, b").unwrap();
5008 assert_eq!(s.joins[0].kind, JoinKind::Cross);
5009 assert!(parse("SELECT 1 FROM a LEFT JOIN b").is_err());
5012 assert!(parse("SELECT 1 FROM a JOIN b USING (x)").is_err());
5013 }
5014
5015 #[test]
5016 fn order_by_reads_a_number_as_an_ORDINAL() {
5017 let s = parse("SELECT a, b FROM t ORDER BY 1, 2 DESC").unwrap();
5021 assert_eq!(s.order_by.len(), 2);
5022 assert_eq!(s.order_by[0].ordinal, Some(1));
5023 assert_eq!(s.order_by[0].dir, Dir::Asc);
5024 assert_eq!(s.order_by[1].ordinal, Some(2));
5025 assert_eq!(s.order_by[1].dir, Dir::Desc);
5026 let s = parse("SELECT a FROM t ORDER BY lower(a) ASC").unwrap();
5028 assert!(s.order_by[0].ordinal.is_none());
5029 assert!(s.order_by[0].expr.is_some());
5030 }
5031
5032 #[test]
5033 fn null_ordering_defaults_the_way_postgres_defaults() {
5034 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
5035 assert!(!s.order_by[0].nulls_first, "ASC defaults to NULLS LAST");
5036 let s = parse("SELECT a FROM t ORDER BY a DESC").unwrap();
5037 assert!(s.order_by[0].nulls_first, "DESC defaults to NULLS FIRST");
5038 let s = parse("SELECT a FROM t ORDER BY a NULLS FIRST").unwrap();
5039 assert!(s.order_by[0].nulls_first, "an explicit clause wins");
5040 }
5041
5042 #[test]
5043 fn limit_and_offset_parse_in_either_order() {
5044 let s = parse("SELECT a FROM t LIMIT 5 OFFSET 2").unwrap();
5045 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
5046 let s = parse("SELECT a FROM t OFFSET 2 LIMIT 5").unwrap();
5047 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
5048 let s = parse("SELECT a FROM t LIMIT ALL").unwrap();
5049 assert_eq!(s.limit, None);
5050 }
5051
5052 #[test]
5053 fn casts_parse_and_are_recorded_rather_than_rejected() {
5054 let s = parse("SELECT x::int2 FROM t").unwrap();
5056 assert!(matches!(s.items[0].expr, Expr::Cast { .. }));
5057 let s = parse("SELECT x::pg_catalog.int2[] FROM t").unwrap();
5058 match &s.items[0].expr {
5059 Expr::Cast { ty, .. } => assert_eq!(ty, "int2[]"),
5060 other => panic!("{:?}", other),
5061 }
5062 }
5063
5064 #[test]
5065 fn star_and_qualified_star_parse() {
5066 assert_eq!(parse("SELECT * FROM t").unwrap().items[0].expr, Expr::Star);
5067 assert_eq!(parse("SELECT c.* FROM t c").unwrap().items[0].expr,
5068 Expr::QualifiedStar("c".into()));
5069 match &parse("SELECT count(*) FROM t").unwrap().items[0].expr {
5072 Expr::Agg { name, args, order_by, distinct } => {
5073 assert_eq!(name, "count");
5074 assert_eq!(args, &vec![Expr::Star]);
5075 assert!(order_by.is_empty());
5076 assert!(!distinct);
5077 }
5078 other => panic!("{:?}", other),
5079 }
5080 assert!(matches!(
5082 &parse("SELECT lower(s) FROM t").unwrap().items[0].expr,
5083 Expr::Func { name, .. } if name == "lower"
5084 ));
5085 }
5086
5087 #[test]
5088 fn an_aggregate_carries_its_own_DISTINCT_and_ORDER_BY() {
5089 match &parse("SELECT array_agg(a.attname ORDER BY a.ord) FROM t a").unwrap().items[0].expr {
5093 Expr::Agg { name, args, order_by, distinct } => {
5094 assert_eq!(name, "array_agg");
5095 assert_eq!(args.len(), 1);
5096 assert_eq!(order_by.len(), 1);
5097 assert!(matches!(order_by[0].dir, Dir::Asc));
5098 assert!(!distinct);
5099 }
5100 other => panic!("{:?}", other),
5101 }
5102 match &parse("SELECT string_agg(DISTINCT s, ',' ORDER BY b DESC NULLS LAST, c) FROM t").unwrap().items[0].expr {
5105 Expr::Agg { name, args, order_by, distinct } => {
5106 assert_eq!(name, "string_agg");
5107 assert_eq!(args.len(), 2, "the separator is an argument, not a sort key");
5108 assert_eq!(order_by.len(), 2);
5109 assert!(matches!(order_by[0].dir, Dir::Desc));
5110 assert!(!order_by[0].nulls_first, "NULLS LAST overrides the DESC default");
5111 assert!(matches!(order_by[1].dir, Dir::Asc));
5112 assert!(distinct);
5113 }
5114 other => panic!("{:?}", other),
5115 }
5116 assert!(parse("SELECT lower(DISTINCT s) FROM t").is_err());
5119 }
5120
5121 #[test]
5122 fn a_parenthesis_free_function_parses_as_a_zero_arg_call() {
5123 match &parse("SELECT current_schema FROM t").unwrap().items[0].expr {
5125 Expr::Func { name, args } => {
5126 assert_eq!(name, "current_schema");
5127 assert!(args.is_empty());
5128 }
5129 other => panic!("{:?}", other),
5130 }
5131 }
5132
5133 #[test]
5134 fn GROUP_BY_and_HAVING_parse_and_what_remains_is_refused_by_name() {
5135 let s = parse("SELECT a, count(*) FROM t GROUP BY a").unwrap();
5138 assert_eq!(s.group_by, vec![Expr::Column { qual: None, name: "a".into() }]);
5139 assert!(s.having.is_none());
5140
5141 let s = parse("SELECT a, b, count(*) FROM t GROUP BY a, b HAVING count(*) > 1").unwrap();
5142 assert_eq!(s.group_by.len(), 2);
5143 assert!(s.having.is_some());
5144
5145 let e = parse("SELECT a FROM t HAVING a > 1").unwrap_err().to_string();
5149 assert!(e.contains("HAVING needs a GROUP BY"), "{}", e);
5150
5151 for (sql, needle) in [
5152 ("SELECT DISTINCT ON (a) a FROM t", "DISTINCT ON"),
5153 ("SELECT a, count(*) FROM t GROUP BY ROLLUP (a)", "ROLLUP"),
5154 ("SELECT a, count(*) FROM t GROUP BY CUBE (a)", "CUBE"),
5155 ] {
5156 let e = parse(sql).unwrap_err().to_string();
5157 assert!(e.contains(needle), "{} -> {}", sql, e);
5158 }
5159 assert!(parse("SELECT a FROM t JUNK JUNK2").is_err());
5161 }
5162
5163 #[test]
5164 fn THE_dn_QUERY_parses_completely() {
5165 let s = parse(
5166 r#"SELECT n.nspname AS "Name",
5167 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5168 FROM pg_catalog.pg_namespace n
5169 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5170 ORDER BY 1;"#,
5171 )
5172 .expect("psql's \\dn must parse");
5173
5174 assert_eq!(s.items.len(), 2);
5175 assert_eq!(s.items[0].alias, Some("Name".into()));
5176 assert_eq!(s.items[1].alias, Some("Owner".into()));
5177 let from = s.from.unwrap();
5178 assert_eq!(from.name, "pg_catalog.pg_namespace");
5179 assert_eq!(from.binding(), "n");
5180 assert!(s.where_.is_some());
5181 assert_eq!(s.order_by[0].ordinal, Some(1));
5182 }
5183
5184 #[test]
5185 fn THE_dt_QUERY_parses_completely() {
5186 let s = parse(
5187 r#"SELECT n.nspname as "Schema",
5188 c.relname as "Name",
5189 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5190 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5191 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5192 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5193 WHEN 'I' THEN 'partitioned index' END as "Type",
5194 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5195 FROM pg_catalog.pg_class c
5196 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5197 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5198 WHERE c.relkind IN ('r','p','')
5199 AND n.nspname <> 'pg_catalog'
5200 AND n.nspname !~ '^pg_toast'
5201 AND n.nspname <> 'information_schema'
5202 AND pg_catalog.pg_table_is_visible(c.oid)
5203 ORDER BY 1,2;"#,
5204 )
5205 .expect("psql's \\dt must parse");
5206
5207 assert_eq!(s.items.len(), 4);
5208 assert_eq!(s.items[2].alias, Some("Type".into()));
5209 match &s.items[2].expr {
5210 Expr::Case { whens, .. } => assert_eq!(whens.len(), 9, "all nine branches"),
5211 other => panic!("{:?}", other),
5212 }
5213 assert_eq!(s.joins.len(), 2);
5214 assert!(s.joins.iter().all(|j| j.kind == JoinKind::Left && j.on.is_some()));
5215 assert_eq!(s.from.unwrap().binding(), "c");
5216 assert_eq!(s.order_by.len(), 2);
5217 assert_eq!(
5218 (s.order_by[0].ordinal, s.order_by[1].ordinal),
5219 (Some(1), Some(2))
5220 );
5221 }
5222}
5223
5224#[cfg(test)]
5225mod eval_tests {
5226 use super::*;
5227 use serde_json::json;
5228
5229 fn one(row: &Value) -> Bound<'_> {
5231 Bound::new(vec![("t".to_string(), Some(row))])
5232 }
5233
5234 fn ev(sql_expr: &str, row: &Value) -> Result<Value> {
5235 let s = parse(&format!("SELECT {} FROM t", sql_expr))?;
5236 eval(&s.items[0].expr, &one(row))
5237 }
5238
5239 fn v(sql_expr: &str, row: &Value) -> Value {
5240 ev(sql_expr, row).unwrap_or_else(|e| panic!("{}: {}", sql_expr, e))
5241 }
5242
5243 #[test]
5244 fn literals_and_columns_resolve() {
5245 let r = json!({"a": 1, "s": "x", "b": true, "n": null});
5246 assert_eq!(v("42", &r), json!(42));
5247 assert_eq!(v("'hi'", &r), json!("hi"));
5248 assert_eq!(v("NULL", &r), Value::Null);
5249 assert_eq!(v("TRUE", &r), json!(true));
5250 assert_eq!(v("a", &r), json!(1));
5251 assert_eq!(v("t.a", &r), json!(1));
5252 assert_eq!(v("s", &r), json!("x"));
5253 assert_eq!(v("nosuch", &r), Value::Null);
5256 }
5257
5258 #[test]
5259 fn an_unknown_table_ALIAS_is_an_error_while_an_unknown_column_is_null() {
5260 let r = json!({"a": 1});
5263 assert_eq!(v("t.nosuch", &r), Value::Null);
5264 let e = ev("zz.a", &r).unwrap_err().to_string();
5265 assert!(e.contains("zz"), "{}", e);
5266 }
5267
5268 #[test]
5271 fn every_comparison_over_NULL_is_UNKNOWN_including_null_equals_null() {
5272 let r = json!({"n": null, "a": 1});
5273 assert_eq!(v("n = 1", &r), Value::Null);
5274 assert_eq!(v("n != 1", &r), Value::Null);
5275 assert_eq!(v("n < 1", &r), Value::Null);
5276 assert_eq!(v("n = n", &r), Value::Null);
5278 assert_eq!(v("n = NULL", &r), Value::Null);
5279 }
5280
5281 #[test]
5282 fn NOT_UNKNOWN_is_UNKNOWN_not_true() {
5283 let r = json!({"n": null});
5287 assert_eq!(v("NOT (n = 1)", &r), Value::Null);
5288 assert_eq!(v("NOT TRUE", &r), json!(false));
5289 assert_eq!(v("NOT FALSE", &r), json!(true));
5290 }
5291
5292 #[test]
5293 fn AND_and_OR_follow_the_three_valued_truth_tables() {
5294 let r = json!({"n": null});
5295 assert_eq!(v("FALSE AND n = 1", &r), json!(false));
5297 assert_eq!(v("TRUE AND n = 1", &r), Value::Null);
5299 assert_eq!(v("TRUE OR n = 1", &r), json!(true));
5301 assert_eq!(v("FALSE OR n = 1", &r), Value::Null);
5303 assert_eq!(v("TRUE AND TRUE", &r), json!(true));
5305 assert_eq!(v("TRUE AND FALSE", &r), json!(false));
5306 assert_eq!(v("FALSE OR FALSE", &r), json!(false));
5307 }
5308
5309 #[test]
5310 fn IS_NULL_is_the_one_predicate_that_is_never_unknown() {
5311 let r = json!({"n": null, "a": 1});
5312 assert_eq!(v("n IS NULL", &r), json!(true));
5313 assert_eq!(v("n IS NOT NULL", &r), json!(false));
5314 assert_eq!(v("a IS NULL", &r), json!(false));
5315 assert_eq!(v("a IS NOT NULL", &r), json!(true));
5316 assert_eq!(v("nosuch IS NULL", &r), json!(true));
5319 }
5320
5321 #[test]
5322 fn NOT_IN_with_a_NULL_in_the_list_is_UNKNOWN_the_classic_trap() {
5323 let r = json!({"a": 2});
5324 assert_eq!(v("a IN (1, 2)", &r), json!(true));
5325 assert_eq!(v("a IN (1, 3)", &r), json!(false));
5326 assert_eq!(v("a NOT IN (1, 3)", &r), json!(true));
5327 assert_eq!(v("a NOT IN (1, NULL)", &r), Value::Null);
5330 assert_eq!(v("a IN (2, NULL)", &r), json!(true));
5332 assert_eq!(v("nosuch IN (1)", &r), Value::Null);
5334 }
5335
5336 #[test]
5339 fn comparisons_work_across_numbers_strings_and_booleans() {
5340 let r = json!({"n": 5, "s": "b", "t": true});
5341 assert_eq!(v("n > 3", &r), json!(true));
5342 assert_eq!(v("n <= 5", &r), json!(true));
5343 assert_eq!(v("s < 'c'", &r), json!(true));
5344 assert_eq!(v("s > 'c'", &r), json!(false));
5345 assert_eq!(v("n = '5'", &r), json!(true));
5348 assert_eq!(v("n = '5.0'", &r), json!(true));
5349 assert_eq!(v("n = 'five'", &r), json!(false));
5352 }
5353
5354 #[test]
5355 fn the_regex_operators_use_the_SAME_matcher_as_NQL() {
5356 let r = json!({"s": "pg_catalog"});
5359 assert_eq!(v("s ~ '^pg_'", &r), json!(true));
5360 assert_eq!(v("s !~ '^pg_'", &r), json!(false));
5361 assert_eq!(v("s ~ '^PG_'", &r), json!(false));
5362 assert_eq!(v("s ~* '^PG_'", &r), json!(true));
5363 assert_eq!(v("s !~ '^zz'", &r), json!(true));
5364 assert_eq!(v("nosuch ~ '^x'", &r), Value::Null);
5366 assert_eq!(v("s ~ '^(pg_catalog)$'", &r), json!(true));
5369 assert_eq!(v("s ~ '^(pg_.*)$'", &r), json!(true));
5370 assert_eq!(v("s ~ '^(public|pg_catalog)$'", &r), json!(true));
5371 assert_eq!(v("s ~ '^pg_[a-z]+$'", &r), json!(true));
5372 assert_eq!(v("s ~ '^pg_[0-9]+$'", &r), json!(false));
5373 let e = ev("s ~ 'a{2}'", &r).unwrap_err().to_string();
5375 assert!(e.contains("interval"), "{}", e);
5376 }
5377
5378 #[test]
5379 fn like_works_in_all_four_spellings() {
5380 let r = json!({"s": "Acme Pool"});
5381 assert_eq!(v("s LIKE 'Acme%'", &r), json!(true));
5382 assert_eq!(v("s LIKE 'acme%'", &r), json!(false));
5383 assert_eq!(v("s ILIKE 'acme%'", &r), json!(true));
5384 assert_eq!(v("s NOT LIKE 'zz%'", &r), json!(true));
5385
5386 assert_eq!(v(r"s ILIKE 'acme_pool' ESCAPE '/'", &r), json!(true),
5400 "an unescaped _ is still a wildcard, and matches the space");
5401 assert_eq!(v(r"s ILIKE 'acme/_pool' ESCAPE '/'", &r), json!(false),
5402 "escaped _ is a LITERAL underscore, which 'Acme Pool' lacks");
5403 assert_eq!(v(r"s ILIKE 'acme%' ESCAPE '/'", &r), json!(true),
5404 "an unescaped % is still a wildcard");
5405 assert_eq!(v(r"s ILIKE 'acme/%' ESCAPE '/'", &r), json!(false),
5406 "escaped % is a literal percent sign, not 'anything'");
5407 assert_eq!(v(r"s NOT ILIKE 'acme/_pool' ESCAPE '/'", &r), json!(true),
5408 "negation composes with ESCAPE");
5409 assert_eq!(v(r"s ILIKE 'acme/' ESCAPE '/'", &r), json!(false),
5413 "a dangling escape is treated as a literal, never an error");
5414 assert_eq!(v("nosuch LIKE 'x'", &r), Value::Null);
5415 }
5416
5417 #[test]
5418 fn arithmetic_and_concatenation_propagate_null_and_refuse_div_by_zero() {
5419 let r = json!({"a": 7, "b": 2});
5420 assert_eq!(v("a + b", &r), json!(9));
5421 assert_eq!(v("a - b", &r), json!(5));
5422 assert_eq!(v("a * b", &r), json!(14));
5423 assert_eq!(v("a / b", &r), json!(3.5));
5424 assert_eq!(v("a % b", &r), json!(1));
5425 assert_eq!(v("-a", &r), json!(-7));
5426 assert_eq!(v("a / b", &r), json!(3.5));
5429 assert_eq!(v("b / a", &r), json!(2.0 / 7.0));
5430 assert_eq!(v("'x' || 'y'", &r), json!("xy"));
5431 assert_eq!(v("'x' || nosuch", &r), Value::Null);
5432 assert_eq!(v("a + nosuch", &r), Value::Null);
5433 assert!(ev("a / 0", &r).is_err());
5436 assert!(ev("a % 0", &r).is_err());
5437 }
5438
5439 #[test]
5440 fn a_cast_is_transparent_rather_than_rejected() {
5441 let r = json!({"a": 7});
5444 assert_eq!(v("a::int2", &r), json!(7));
5445 assert_eq!(v("a::pg_catalog.int2[]", &r), json!(7));
5446 }
5447
5448 #[test]
5451 fn a_simple_CASE_picks_the_matching_branch() {
5452 let expr = "CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
5454 WHEN 'i' THEN 'index' END";
5455 assert_eq!(v(expr, &json!({"k": "r"})), json!("table"));
5456 assert_eq!(v(expr, &json!({"k": "v"})), json!("view"));
5457 assert_eq!(v(expr, &json!({"k": "i"})), json!("index"));
5458 assert_eq!(v(expr, &json!({"k": "z"})), Value::Null);
5461 }
5462
5463 #[test]
5464 fn a_searched_CASE_evaluates_predicates_and_UNKNOWN_does_not_match() {
5465 let expr = "CASE WHEN n > 5 THEN 'big' WHEN n > 0 THEN 'small' ELSE 'none' END";
5466 assert_eq!(v(expr, &json!({"n": 9})), json!("big"));
5467 assert_eq!(v(expr, &json!({"n": 2})), json!("small"));
5468 assert_eq!(v(expr, &json!({"n": -1})), json!("none"));
5469 assert_eq!(v(expr, &json!({"other": 1})), json!("none"));
5471 }
5472
5473 #[test]
5474 fn an_ELSE_branch_is_used_when_nothing_matches() {
5475 assert_eq!(
5476 v("CASE k WHEN 'r' THEN 'table' ELSE 'other' END", &json!({"k": "z"})),
5477 json!("other")
5478 );
5479 }
5480
5481 #[test]
5484 fn the_catalogue_functions_psql_calls_all_answer() {
5485 let r = json!({"o": 10, "enc": 6});
5486 assert_eq!(v("pg_get_userbyid(o)", &r), json!("nedb"));
5488 assert_eq!(v("pg_catalog.pg_get_userbyid(o)", &r), json!("nedb"));
5489 assert_eq!(v("pg_table_is_visible(o)", &r), json!(true));
5491 assert_eq!(v("pg_encoding_to_char(enc)", &r), json!("UTF8"));
5492 assert_eq!(v("current_schema", &r), json!("public"));
5493 assert_eq!(v("current_database()", &r), json!("nedb"));
5494 assert_eq!(v("current_user", &r), json!("nedb"));
5495 assert_eq!(v("pg_get_expr(o, o)", &r), Value::Null);
5498 assert_eq!(v("obj_description(o)", &r), Value::Null);
5499 }
5500
5501 #[test]
5502 fn text_and_null_handling_functions_work() {
5503 let r = json!({"s": "AbC", "n": null});
5504 assert_eq!(v("lower(s)", &r), json!("abc"));
5505 assert_eq!(v("upper(s)", &r), json!("ABC"));
5506 assert_eq!(v("length(s)", &r), json!(3));
5507 assert_eq!(v("lower(n)", &r), Value::Null);
5508 assert_eq!(v("coalesce(n, 'fallback')", &r), json!("fallback"));
5509 assert_eq!(v("coalesce(s, 'fallback')", &r), json!("AbC"));
5510 assert_eq!(v("coalesce(n, n)", &r), Value::Null);
5511 assert_eq!(v("nullif(s, 'AbC')", &r), Value::Null);
5512 assert_eq!(v("nullif(s, 'zz')", &r), json!("AbC"));
5513 assert_eq!(v("format_type(20, NULL)", &r), json!("bigint"));
5515 }
5516
5517 #[test]
5518 fn coalesce_does_not_evaluate_past_its_first_non_null() {
5519 let r = json!({"a": 1});
5521 assert_eq!(v("coalesce(a, a / 0)", &r), json!(1));
5522 }
5523
5524 #[test]
5525 fn an_unknown_function_is_REFUSED_rather_than_answered_with_NULL() {
5526 let e = ev("pg_stat_get_numscans(1)", &json!({})).unwrap_err().to_string();
5529 assert!(e.contains("pg_stat_get_numscans"), "{}", e);
5530 assert!(e.contains("refused"), "{}", e);
5531 }
5532
5533 #[test]
5536 fn a_qualified_column_reads_only_its_OWN_binding() {
5537 let a = json!({"name": "left", "x": 1});
5540 let b = json!({"name": "right", "y": 2});
5541 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), Some(&b))]);
5542 let get = |e: &str| {
5543 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5544 eval(&s.items[0].expr, &row).unwrap()
5545 };
5546 assert_eq!(get("a.name"), json!("left"));
5547 assert_eq!(get("b.name"), json!("right"));
5548 assert_eq!(get("name"), json!("left"));
5550 assert_eq!(get("y"), json!(2), "a bare name still finds a later binding");
5551 }
5552
5553 #[test]
5554 fn an_unmatched_LEFT_JOIN_side_reads_as_NULL_not_as_a_missing_column() {
5555 let a = json!({"x": 1});
5558 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), None)]);
5559 let get = |e: &str| {
5560 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5561 eval(&s.items[0].expr, &row).unwrap()
5562 };
5563 assert_eq!(get("b.anything"), Value::Null);
5564 assert_eq!(get("b.anything IS NULL"), json!(true));
5565 assert_eq!(get("a.x"), json!(1));
5566 }
5567}
5568
5569#[cfg(test)]
5570mod exec_tests {
5571 use super::*;
5572 use serde_json::json;
5573
5574 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5576 let owned: Vec<(String, Vec<Value>)> =
5577 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
5578 move |name: &str| {
5579 let bare = name.rsplit('.').next().unwrap_or(name);
5581 Ok(owned
5582 .iter()
5583 .find(|(n, _)| n == name || n == bare)
5584 .map(|(_, r)| from_vec(r.clone())))
5585 }
5586 }
5587
5588 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
5589 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
5590 (cols.into_iter().map(|c| c.name).collect(), rows)
5591 }
5592
5593 fn col(rows: &[Value], name: &str) -> Vec<Value> {
5594 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
5595 }
5596
5597 #[test]
5600 fn select_columns_where_order_limit_offset() {
5601 let t = tables(vec![(
5602 "t",
5603 vec![json!({"a": 3, "s": "c"}), json!({"a": 1, "s": "a"}), json!({"a": 2, "s": "b"})],
5604 )]);
5605 let (names, rows) = go("SELECT a, s FROM t ORDER BY a", &t);
5606 assert_eq!(names, vec!["a", "s"]);
5607 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2), json!(3)]);
5608
5609 let (_, rows) = go("SELECT a FROM t ORDER BY a DESC", &t);
5610 assert_eq!(col(&rows, "a"), vec![json!(3), json!(2), json!(1)]);
5611
5612 let (_, rows) = go("SELECT a FROM t WHERE a > 1 ORDER BY a", &t);
5613 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5614
5615 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 2", &t);
5616 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5617
5618 let (_, rows) = go("SELECT a FROM t ORDER BY a OFFSET 1", &t);
5619 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5620
5621 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 1 OFFSET 1", &t);
5622 assert_eq!(col(&rows, "a"), vec![json!(2)]);
5623
5624 let (_, rows) = go("SELECT a FROM t OFFSET 99", &t);
5626 assert!(rows.is_empty());
5627 }
5628
5629 #[test]
5630 fn an_output_column_takes_its_alias_or_a_derived_name() {
5631 let t = tables(vec![("t", vec![json!({"a": 1})])]);
5634 assert_eq!(go(r#"SELECT a AS "Name" FROM t"#, &t).0, vec!["Name"]);
5635 assert_eq!(go("SELECT a FROM t", &t).0, vec!["a"]);
5636 assert_eq!(go("SELECT lower('X') FROM t", &t).0, vec!["lower"]);
5637 assert_eq!(go("SELECT 1 + 1 FROM t", &t).0, vec!["?column?"]);
5638 assert_eq!(go("SELECT CASE a WHEN 1 THEN 'x' END FROM t", &t).0, vec!["case"]);
5639 }
5640
5641 #[test]
5642 fn star_expands_from_the_rows_and_a_qualified_star_from_one_binding() {
5643 let t = tables(vec![
5644 ("a", vec![json!({"x": 1, "y": 2})]),
5645 ("b", vec![json!({"z": 3})]),
5646 ]);
5647 let (names, rows) = go("SELECT * FROM a", &t);
5648 assert_eq!(names, vec!["x", "y"]);
5649 assert_eq!(rows.len(), 1);
5650
5651 let (names, _) = go("SELECT a.* FROM a CROSS JOIN b", &t);
5652 assert_eq!(names, vec!["x", "y"], "a qualified star takes ONE binding");
5653
5654 let empty = tables(vec![("e", vec![])]);
5657 assert_eq!(go("SELECT * FROM e", &empty).0, Vec::<String>::new());
5658 }
5659
5660 #[test]
5661 fn distinct_dedupes_on_the_projected_values() {
5662 let t = tables(vec![(
5663 "t",
5664 vec![json!({"g": "x"}), json!({"g": "x"}), json!({"g": "y"})],
5665 )]);
5666 let (_, rows) = go("SELECT DISTINCT g FROM t ORDER BY 1", &t);
5667 assert_eq!(col(&rows, "g"), vec![json!("x"), json!("y")]);
5668 let (_, rows) = go("SELECT g FROM t", &t);
5669 assert_eq!(rows.len(), 3, "without DISTINCT every row survives");
5670 }
5671
5672 #[test]
5673 fn order_by_an_ORDINAL_sorts_the_projected_column() {
5674 let t = tables(vec![(
5675 "t",
5676 vec![json!({"a": 2, "b": "z"}), json!({"a": 1, "b": "y"})],
5677 )]);
5678 let (_, rows) = go("SELECT a, b FROM t ORDER BY 1", &t);
5679 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5680 let (_, rows) = go("SELECT a, b FROM t ORDER BY 2 DESC", &t);
5681 assert_eq!(col(&rows, "b"), vec![json!("z"), json!("y")]);
5682 let e = run("SELECT a FROM t ORDER BY 3", &t).unwrap_err().to_string();
5684 assert!(e.contains("out of range"), "{}", e);
5685 }
5686
5687 #[test]
5688 fn order_by_an_expression_may_use_a_column_NOT_in_the_select_list() {
5689 let t = tables(vec![(
5690 "t",
5691 vec![json!({"a": 1, "hidden": 9}), json!({"a": 2, "hidden": 1})],
5692 )]);
5693 let (_, rows) = go("SELECT a FROM t ORDER BY hidden", &t);
5694 assert_eq!(col(&rows, "a"), vec![json!(2), json!(1)]);
5695 }
5696
5697 #[test]
5698 fn null_ordering_follows_the_direction_defaults() {
5699 let t = tables(vec![(
5700 "t",
5701 vec![json!({"a": 2}), json!({"a": null}), json!({"a": 1})],
5702 )]);
5703 assert_eq!(col(&go("SELECT a FROM t ORDER BY a", &t).1, "a"),
5705 vec![json!(1), json!(2), Value::Null]);
5706 assert_eq!(col(&go("SELECT a FROM t ORDER BY a DESC", &t).1, "a"),
5708 vec![Value::Null, json!(2), json!(1)]);
5709 assert_eq!(col(&go("SELECT a FROM t ORDER BY a NULLS FIRST", &t).1, "a"),
5711 vec![Value::Null, json!(1), json!(2)]);
5712 }
5713
5714 #[test]
5715 fn a_where_clause_that_is_UNKNOWN_excludes_the_row() {
5716 let t = tables(vec![(
5717 "t",
5718 vec![json!({"a": 1}), json!({"a": null}), json!({"other": 1})],
5719 )]);
5720 let (_, rows) = go("SELECT a FROM t WHERE a = 1", &t);
5722 assert_eq!(rows.len(), 1);
5723 let (_, rows) = go("SELECT a FROM t WHERE NOT (a = 1)", &t);
5725 assert_eq!(rows.len(), 0, "NOT UNKNOWN must not resurrect a null row");
5726 }
5727
5728 #[test]
5729 fn select_with_no_FROM_returns_exactly_one_row() {
5730 let t = tables(vec![]);
5732 let (names, rows) = go("SELECT 1", &t);
5733 assert_eq!(rows.len(), 1);
5734 assert_eq!(names, vec!["?column?"]);
5735 assert_eq!(go("SELECT current_schema", &t).1.len(), 1);
5736 }
5737
5738 #[test]
5739 fn an_unknown_relation_is_NAMED_rather_than_answered_with_no_rows() {
5740 let t = tables(vec![("t", vec![])]);
5743 let e = run("SELECT a FROM nosuchtable", &t).unwrap_err().to_string();
5744 assert!(e.contains("nosuchtable"), "{}", e);
5745 assert!(e.contains("does not exist"), "{}", e);
5746 }
5747
5748 #[test]
5751 fn an_inner_join_keeps_only_matching_pairs() {
5752 let t = tables(vec![
5753 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5754 ("r", vec![json!({"lid": 1, "v": "x"})]),
5755 ]);
5756 let (_, rows) = go("SELECT l.n, r.v FROM l JOIN r ON r.lid = l.id", &t);
5757 assert_eq!(rows.len(), 1);
5758 assert_eq!(col(&rows, "n"), vec![json!("a")]);
5759 }
5760
5761 #[test]
5762 fn a_LEFT_join_keeps_unmatched_left_rows_with_NULLs() {
5763 let t = tables(vec![
5765 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5766 ("r", vec![json!({"lid": 1, "v": "x"})]),
5767 ]);
5768 let (_, rows) = go("SELECT l.n, r.v FROM l LEFT JOIN r ON r.lid = l.id ORDER BY 1", &t);
5769 assert_eq!(rows.len(), 2);
5770 assert_eq!(col(&rows, "n"), vec![json!("a"), json!("b")]);
5771 assert_eq!(col(&rows, "v"), vec![json!("x"), Value::Null]);
5772 }
5773
5774 #[test]
5775 fn a_RIGHT_join_keeps_unmatched_right_rows_and_FULL_keeps_both() {
5776 let t = tables(vec![
5777 ("l", vec![json!({"id": 1})]),
5778 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5779 ]);
5780 let (_, rows) = go("SELECT l.id, r.lid FROM l RIGHT JOIN r ON r.lid = l.id", &t);
5781 assert_eq!(rows.len(), 2);
5782 assert!(col(&rows, "id").contains(&Value::Null), "the unmatched right row keeps NULLs on the left");
5783
5784 let t2 = tables(vec![
5785 ("l", vec![json!({"id": 1}), json!({"id": 5})]),
5786 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5787 ]);
5788 let (_, rows) = go("SELECT l.id, r.lid FROM l FULL OUTER JOIN r ON r.lid = l.id", &t2);
5789 assert_eq!(rows.len(), 3, "one match plus one orphan on each side");
5790 }
5791
5792 #[test]
5793 fn a_cross_join_is_the_cartesian_product() {
5794 let t = tables(vec![
5795 ("a", vec![json!({"x": 1}), json!({"x": 2})]),
5796 ("b", vec![json!({"y": 1}), json!({"y": 2}), json!({"y": 3})]),
5797 ]);
5798 assert_eq!(go("SELECT a.x, b.y FROM a CROSS JOIN b", &t).1.len(), 6);
5799 assert_eq!(go("SELECT a.x, b.y FROM a, b", &t).1.len(), 6);
5801 }
5802
5803 #[test]
5804 fn an_ON_clause_that_is_UNKNOWN_does_not_join() {
5805 let t = tables(vec![
5808 ("l", vec![json!({"id": null})]),
5809 ("r", vec![json!({"lid": null})]),
5810 ]);
5811 let (_, rows) = go("SELECT l.id FROM l JOIN r ON r.lid = l.id", &t);
5812 assert!(rows.is_empty(), "NULL = NULL is UNKNOWN, so nothing joins");
5813 let (_, rows) = go("SELECT l.id FROM l LEFT JOIN r ON r.lid = l.id", &t);
5815 assert_eq!(rows.len(), 1);
5816 }
5817
5818 #[test]
5819 fn two_joins_chain() {
5820 let t = tables(vec![
5821 ("a", vec![json!({"id": 1, "bid": 10, "cid": 100})]),
5822 ("b", vec![json!({"id": 10, "bn": "B"})]),
5823 ("c", vec![json!({"id": 100, "cn": "C"})]),
5824 ]);
5825 let (_, rows) = go(
5826 "SELECT a.id, b.bn, c.cn FROM a \
5827 LEFT JOIN b ON b.id = a.bid \
5828 LEFT JOIN c ON c.id = a.cid",
5829 &t,
5830 );
5831 assert_eq!(rows.len(), 1);
5832 assert_eq!(col(&rows, "bn"), vec![json!("B")]);
5833 assert_eq!(col(&rows, "cn"), vec![json!("C")]);
5834 }
5835
5836 fn catalog() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5840 tables(vec![
5841 (
5842 "pg_namespace",
5843 vec![
5844 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5845 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5846 json!({"oid": 13000, "nspname": "information_schema", "nspowner": 10}),
5847 ],
5848 ),
5849 (
5850 "pg_class",
5851 vec![
5852 json!({"oid": 16401, "relname": "orders", "relnamespace": 2200,
5853 "relkind": "r", "relowner": 10, "relam": 2}),
5854 json!({"oid": 16402, "relname": "drivers", "relnamespace": 2200,
5855 "relkind": "r", "relowner": 10, "relam": 2}),
5856 ],
5857 ),
5858 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5859 ])
5860 }
5861
5862 #[test]
5863 fn THE_dn_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5864 let (names, rows) = go(
5865 r#"SELECT n.nspname AS "Name",
5866 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5867 FROM pg_catalog.pg_namespace n
5868 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5869 ORDER BY 1;"#,
5870 &catalog(),
5871 );
5872
5873 assert_eq!(names, vec!["Name", "Owner"], "psql reads these BY NAME");
5874 assert_eq!(col(&rows, "Name"), vec![json!("public")]);
5877 assert_eq!(col(&rows, "Owner"), vec![json!("nedb")]);
5878 }
5879
5880 #[test]
5881 fn THE_dt_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5882 let (names, rows) = go(
5883 r#"SELECT n.nspname as "Schema",
5884 c.relname as "Name",
5885 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5886 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5887 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5888 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5889 WHEN 'I' THEN 'partitioned index' END as "Type",
5890 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5891 FROM pg_catalog.pg_class c
5892 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5893 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5894 WHERE c.relkind IN ('r','p','')
5895 AND n.nspname <> 'pg_catalog'
5896 AND n.nspname !~ '^pg_toast'
5897 AND n.nspname <> 'information_schema'
5898 AND pg_catalog.pg_table_is_visible(c.oid)
5899 ORDER BY 1,2;"#,
5900 &catalog(),
5901 );
5902
5903 assert_eq!(names, vec!["Schema", "Name", "Type", "Owner"]);
5904 assert_eq!(col(&rows, "Name"), vec![json!("drivers"), json!("orders")]);
5906 assert_eq!(col(&rows, "Schema"), vec![json!("public"), json!("public")]);
5907 assert_eq!(col(&rows, "Type"), vec![json!("table"), json!("table")]);
5909 assert_eq!(col(&rows, "Owner"), vec![json!("nedb"), json!("nedb")]);
5910 }
5911
5912 #[test]
5913 fn the_dt_query_still_filters_correctly_with_a_system_relation_present() {
5914 let t = tables(vec![
5918 (
5919 "pg_namespace",
5920 vec![
5921 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5922 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5923 ],
5924 ),
5925 (
5926 "pg_class",
5927 vec![
5928 json!({"oid": 1, "relname": "mine", "relnamespace": 2200,
5929 "relkind": "r", "relowner": 10, "relam": 2}),
5930 json!({"oid": 2, "relname": "pg_internal", "relnamespace": 11,
5931 "relkind": "r", "relowner": 10, "relam": 2}),
5932 json!({"oid": 3, "relname": "an_index", "relnamespace": 2200,
5933 "relkind": "i", "relowner": 10, "relam": 2}),
5934 ],
5935 ),
5936 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5937 ]);
5938 let (_, rows) = go(
5939 r#"SELECT c.relname as "Name" FROM pg_catalog.pg_class c
5940 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5941 WHERE c.relkind IN ('r','p','') AND n.nspname <> 'pg_catalog'
5942 ORDER BY 1"#,
5943 &t,
5944 );
5945 assert_eq!(col(&rows, "Name"), vec![json!("mine")],
5946 "a system relation and an index must both be filtered out");
5947 }
5948}
5949
5950#[cfg(test)]
5951mod operator_syntax_tests {
5952 use super::*;
5953 use serde_json::json;
5954
5955 #[test]
5956 fn the_OPERATOR_qualification_psql_generates_is_understood() {
5957 let s = parse(
5961 "SELECT a FROM t WHERE n OPERATOR(pg_catalog.~) '^x' \
5962 AND m OPERATOR(pg_catalog.=) 1",
5963 )
5964 .expect("psql's OPERATOR() form must parse");
5965 match s.where_.unwrap() {
5966 Expr::Binary { op, left, .. } => {
5967 assert_eq!(op, "AND");
5968 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "~"));
5969 }
5970 other => panic!("{:?}", other),
5971 }
5972 }
5973
5974 #[test]
5975 fn an_OPERATOR_qualified_comparison_EVALUATES() {
5976 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
5977 Ok(Some(from_vec(vec![json!({"n": "orders"}), json!({"n": "pg_toast_1"})])))
5978 };
5979 let (_, rows) = run(
5980 "SELECT n FROM pg_class WHERE n OPERATOR(pg_catalog.~) '^ord'",
5981 &t,
5982 )
5983 .unwrap();
5984 assert_eq!(rows.len(), 1);
5985 assert_eq!(rows[0]["n"], json!("orders"));
5986 }
5987
5988 #[test]
5989 fn a_subquery_an_ARRAY_constructor_and_EXISTS_all_PARSE() {
5990 let s = parse("SELECT a FROM t WHERE x = (SELECT 1)").unwrap();
5993 assert!(matches!(s.where_, Some(Expr::Binary { ref right, .. }) if matches!(**right, Expr::Subquery(_))));
5994 let s = parse("SELECT array_to_string(ARRAY(SELECT a FROM b), ',') FROM t").unwrap();
5995 assert!(matches!(&s.items[0].expr, Expr::Func { args, .. } if matches!(args[0], Expr::ArrayQuery(_))));
5996 let s = parse("SELECT a FROM t WHERE EXISTS (SELECT 1)").unwrap();
5997 assert!(matches!(s.where_, Some(Expr::Exists { negated: false, .. })));
5998 let s = parse("SELECT a FROM t WHERE NOT EXISTS (SELECT 1)").unwrap();
5999 assert!(matches!(s.where_, Some(Expr::Unary { ref expr, .. }) if matches!(**expr, Expr::Exists { .. })));
6000 let s = parse("SELECT a FROM t WHERE oid = ANY (polroles) AND 'd' = any(kinds) AND x <> ALL (SELECT y FROM u)").unwrap();
6002 assert!(s.where_.is_some());
6003 let s = parse("SELECT prattrs[s] FROM t").unwrap();
6004 assert!(matches!(s.items[0].expr, Expr::Index { .. }));
6005 let s = parse("SELECT CAST('tuple' AS pg_catalog.text), CAST(n AS int2[]) FROM t").unwrap();
6006 assert!(matches!(&s.items[0].expr, Expr::Cast { ty, .. } if ty == "text"));
6007 assert!(matches!(&s.items[1].expr, Expr::Cast { ty, .. } if ty == "int2[]"));
6008 let s = parse("SELECT a FROM t WHERE a IS DISTINCT FROM b").unwrap();
6009 assert!(matches!(s.where_, Some(Expr::Binary { ref op, .. }) if op == "IS DISTINCT FROM"));
6010 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();
6012 assert_eq!(s.joins.len(), 3);
6013 assert!(matches!(s.joins[1].kind, JoinKind::Cross));
6014 let s = parse("SELECT 1 FROM c, LATERAL (SELECT 2 AS two) s").unwrap();
6016 assert!(s.joins[0].table.lateral && s.joins[0].table.sub.is_some());
6017 let s = parse("SELECT tt.a FROM (SELECT 1 AS a UNION ALL SELECT 2) AS tt ORDER BY 1").unwrap();
6018 assert_eq!(s.from.as_ref().unwrap().sub.as_ref().unwrap().set_ops.len(), 1);
6019 }
6020
6021 #[test]
6022 fn a_compound_query_keeps_ORDER_BY_for_the_whole() {
6023 let s = parse("SELECT a FROM t UNION SELECT b FROM u UNION ALL SELECT c FROM v ORDER BY 1 LIMIT 5").unwrap();
6024 assert_eq!(s.set_ops.len(), 2);
6025 assert_eq!(s.set_ops[0].op, SetOp::Union);
6026 assert!(!s.set_ops[0].all);
6027 assert!(s.set_ops[1].all);
6028 assert_eq!(s.order_by.len(), 1);
6029 assert_eq!(s.limit, Some(5));
6030 assert!(s.set_ops[1].query.order_by.is_empty(), "the tail belongs to the whole, not the last arm");
6031 }
6032}
6033
6034#[cfg(test)]
6035mod subquery_exec_tests {
6036 use super::*;
6037 use serde_json::json;
6038
6039 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
6040 let owned: Vec<(String, Vec<Value>)> =
6041 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
6042 move |name: &str| {
6043 let bare = name.rsplit('.').next().unwrap_or(name);
6044 Ok(owned.iter().find(|(n, _)| n == name || n == bare).map(|(_, r)| from_vec(r.clone())))
6045 }
6046 }
6047
6048 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
6049 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
6050 (cols.into_iter().map(|c| c.name).collect(), rows)
6051 }
6052
6053 fn col(rows: &[Value], name: &str) -> Vec<Value> {
6054 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
6055 }
6056
6057 fn shop() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
6058 tables(vec![
6059 ("c", vec![
6060 json!({"id": 1, "name": "ann", "tags": ["a", "b"]}),
6061 json!({"id": 2, "name": "bob", "tags": []}),
6062 json!({"id": 3, "name": "cyd", "tags": null}),
6063 ]),
6064 ("o", vec![
6065 json!({"oid": 10, "cid": 1, "total": 5}),
6066 json!({"oid": 11, "cid": 1, "total": 7}),
6067 json!({"oid": 12, "cid": 2, "total": 9}),
6068 ]),
6069 ])
6070 }
6071
6072 #[test]
6073 fn a_correlated_scalar_subquery_sees_the_outer_row() {
6074 let t = shop();
6075 let (_, rows) = go(
6076 "SELECT c.name, (SELECT sum(o.total) FROM o WHERE o.cid = c.id) AS spent FROM c ORDER BY c.id",
6077 &t,
6078 );
6079 assert_eq!(col(&rows, "spent"), vec![json!(12), json!(9), Value::Null]);
6080 let e = run("SELECT (SELECT o.total FROM o WHERE o.cid = c.id) FROM c", &t).unwrap_err().to_string();
6082 assert!(e.contains("more than one row"), "{}", e);
6083 let e = run("SELECT (SELECT oid, total FROM o) FROM c", &t).unwrap_err().to_string();
6085 assert!(e.contains("exactly one column"), "{}", e);
6086 }
6087
6088 #[test]
6089 fn EXISTS_and_NOT_EXISTS_are_never_unknown() {
6090 let t = shop();
6091 let (_, rows) = go("SELECT c.name FROM c WHERE EXISTS (SELECT 1 FROM o WHERE o.cid = c.id) ORDER BY 1", &t);
6092 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6093 let (_, rows) = go("SELECT c.name FROM c WHERE NOT EXISTS (SELECT 1 FROM o WHERE o.cid = c.id)", &t);
6094 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6095 }
6096
6097 #[test]
6098 fn ARRAY_of_a_subquery_and_array_to_string_compose_like_psql_dp() {
6099 let t = shop();
6100 let (_, rows) = go(
6101 "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",
6102 &t,
6103 );
6104 assert_eq!(col(&rows, "totals"), vec![json!("5,7"), json!("9"), json!("")]);
6106 let (_, rows) = go("SELECT array_length(ARRAY(SELECT oid FROM o), 1) AS n FROM c WHERE c.id = 1", &t);
6107 assert_eq!(col(&rows, "n"), vec![json!(3)]);
6108 }
6109
6110 #[test]
6111 fn ANY_ALL_and_IN_over_arrays_and_subqueries() {
6112 let t = shop();
6113 let (_, rows) = go("SELECT c.name FROM c WHERE 'a' = ANY (c.tags) ORDER BY 1", &t);
6114 assert_eq!(col(&rows, "name"), vec![json!("ann")]);
6115 let (_, rows) = go("SELECT c.name FROM c WHERE 'zz' = ANY (c.tags)", &t);
6117 assert!(rows.is_empty());
6118 let (_, rows) = go("SELECT c.name FROM c WHERE c.id = ANY (SELECT o.cid FROM o) ORDER BY 1", &t);
6119 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6120 let (_, rows) = go("SELECT c.name FROM c WHERE c.id <> ALL (SELECT o.cid FROM o)", &t);
6121 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6122 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);
6123 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6124 let (_, rows) = go("SELECT c.name FROM c WHERE c.id NOT IN (SELECT o.cid FROM o)", &t);
6125 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6126 let (_, rows) = go("SELECT c.tags[2] AS second FROM c WHERE c.id = 1", &t);
6128 assert_eq!(col(&rows, "second"), vec![json!("b")]);
6129 }
6130
6131 #[test]
6132 fn set_operations_combine_arms_and_sort_the_whole() {
6133 let t = shop();
6134 let (names, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1", &t);
6135 assert_eq!(names, vec!["k"], "column names come from the first arm");
6136 assert_eq!(col(&rows, "k"), vec![json!(1), json!(1), json!(1), json!(2), json!(2), json!(3)]);
6137 let (_, rows) = go("SELECT c.id AS k FROM c UNION SELECT o.cid FROM o ORDER BY 1", &t);
6138 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2), json!(3)]);
6139 let (_, rows) = go("SELECT c.id AS k FROM c INTERSECT SELECT o.cid FROM o ORDER BY 1", &t);
6140 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2)]);
6141 let (_, rows) = go("SELECT c.id AS k FROM c EXCEPT SELECT o.cid FROM o", &t);
6142 assert_eq!(col(&rows, "k"), vec![json!(3)]);
6143 let (_, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1 DESC LIMIT 2", &t);
6144 assert_eq!(col(&rows, "k"), vec![json!(3), json!(2)]);
6145 let e = run("SELECT c.id FROM c UNION SELECT o.oid, o.cid FROM o", &t).unwrap_err().to_string();
6146 assert!(e.contains("same number of columns"), "{}", e);
6147 }
6148
6149 #[test]
6150 fn a_derived_table_is_a_relation_and_LATERAL_sees_its_left() {
6151 let t = shop();
6152 let (_, rows) = go(
6153 "SELECT tt.who FROM (SELECT c.name AS who FROM c WHERE c.id < 3) AS tt ORDER BY 1",
6154 &t,
6155 );
6156 assert_eq!(col(&rows, "who"), vec![json!("ann"), json!("bob")]);
6157 let (_, rows) = go("SELECT tt.x FROM (SELECT c.name FROM c WHERE c.id = 1) AS tt(x)", &t);
6159 assert_eq!(col(&rows, "x"), vec![json!("ann")]);
6160 let (_, rows) = go(
6162 "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\"",
6163 &t,
6164 );
6165 assert_eq!(col(&rows, "Name"), vec![json!("ann"), json!("bob"), json!("cyd")]);
6166 assert_eq!(col(&rows, "Orders"), vec![json!(2), json!(1), json!(0)]);
6167 }
6168
6169 #[test]
6170 fn table_functions_generate_series_and_unnest() {
6171 let t = shop();
6172 let (_, rows) = go("SELECT s.generate_series AS n FROM generate_series(1, 3) s", &t);
6173 assert_eq!(col(&rows, "n"), vec![json!(1), json!(2), json!(3)]);
6174 let (_, rows) = go("SELECT x FROM pg_catalog.unnest(ARRAY['p', 'q']) AS t(x)", &t);
6175 assert_eq!(col(&rows, "x"), vec![json!("p"), json!("q")]);
6176 let (_, rows) = go(
6178 "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",
6179 &t,
6180 );
6181 assert_eq!(col(&rows, "tags"), vec![json!("a, b"), json!(""), json!("")]);
6182 let e = run("SELECT 1 FROM nosuchfn(1) f", &t).unwrap_err().to_string();
6183 assert!(e.contains("table function nosuchfn()"), "{}", e);
6184 }
6185
6186 #[test]
6187 fn aggregates_without_GROUP_BY_collapse_to_one_row() {
6188 let t = shop();
6189 let (names, rows) = go(
6190 "SELECT count(*), count(c.tags) AS tagged, min(c.name), max(c.name) AS hi, string_agg(c.name, '|') AS all FROM c",
6191 &t,
6192 );
6193 assert_eq!(names, vec!["count", "tagged", "min", "hi", "all"]);
6194 assert_eq!(rows.len(), 1);
6195 assert_eq!(rows[0]["count"], json!(3));
6196 assert_eq!(rows[0]["tagged"], json!(2), "count(x) skips NULL");
6197 assert_eq!(rows[0]["min"], json!("ann"));
6198 assert_eq!(rows[0]["hi"], json!("cyd"));
6199 assert_eq!(rows[0]["all"], json!("ann|bob|cyd"));
6200 let (_, rows) = go("SELECT count(*) AS n, sum(o.total) AS s FROM o WHERE o.total > 100", &t);
6202 assert_eq!(rows[0]["n"], json!(0));
6203 assert_eq!(rows[0]["s"], Value::Null);
6204 let (_, rows) = go("SELECT sum(o.total) / count(*) AS avg_total, avg(o.total) AS a FROM o", &t);
6207 assert_eq!(rows[0]["avg_total"], json!(7));
6208 assert_eq!(rows[0]["a"], json!(7));
6209 let e = run("SELECT c.name, count(*) FROM c", &t).unwrap_err().to_string();
6210 assert!(e.contains("must appear in the GROUP BY clause"), "{}", e);
6211 }
6212
6213 #[test]
6214 fn IS_DISTINCT_FROM_is_null_safe() {
6215 let t = shop();
6216 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS DISTINCT FROM NULL ORDER BY 1", &t);
6217 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6218 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS NOT DISTINCT FROM NULL", &t);
6219 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6220 }
6221
6222 #[test]
6223 fn THE_dT_QUERY_RUNS_over_a_catalogue_fixture() {
6224 let t = tables(vec![
6226 ("pg_namespace", vec![
6227 json!({"oid": 11, "nspname": "pg_catalog"}),
6228 json!({"oid": 2200, "nspname": "public"}),
6229 ]),
6230 ("pg_type", vec![
6231 json!({"oid": 25, "typname": "text", "typnamespace": 11, "typrelid": 0, "typelem": 0, "typarray": 1009}),
6232 json!({"oid": 1009, "typname": "_text", "typnamespace": 11, "typrelid": 0, "typelem": 25, "typarray": 0}),
6233 json!({"oid": 70000, "typname": "mood", "typnamespace": 2200, "typrelid": 0, "typelem": 0, "typarray": 70001}),
6234 json!({"oid": 70001, "typname": "_mood", "typnamespace": 2200, "typrelid": 0, "typelem": 70000, "typarray": 0}),
6235 ]),
6236 ("pg_class", vec![]),
6237 ]);
6238 let (_, rows) = go(
6239 r#"SELECT n.nspname as "Schema",
6240 pg_catalog.format_type(t.oid, NULL) AS "Name",
6241 pg_catalog.obj_description(t.oid, 'pg_type') as "Description"
6242 FROM pg_catalog.pg_type t
6243 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
6244 WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
6245 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
6246 AND n.nspname <> 'pg_catalog'
6247 AND n.nspname <> 'information_schema'
6248 AND pg_catalog.pg_type_is_visible(t.oid)
6249 ORDER BY 1, 2;"#,
6250 &t,
6251 );
6252 assert_eq!(rows.len(), 1, "{:?}", rows);
6255 assert_eq!(col(&rows, "Schema"), vec![json!("public")]);
6256 }
6257}
6258
6259#[cfg(test)]
6260mod collate_tests {
6261 use super::*;
6262 use serde_json::json;
6263
6264 #[test]
6265 fn COLLATE_is_consumed_because_it_cannot_change_the_answer() {
6266 for sql in [
6270 r#"SELECT a FROM t ORDER BY a COLLATE "C""#,
6271 r#"SELECT a COLLATE "C" FROM t"#,
6272 r#"SELECT a FROM t WHERE a COLLATE pg_catalog."C" = 'x'"#,
6273 ] {
6274 parse(sql).unwrap_or_else(|e| panic!("{} -> {}", sql, e));
6275 }
6276 assert!(parse("SELECT a FROM t ORDER BY a COLLATE").is_err());
6278 }
6279
6280 #[test]
6281 fn a_COLLATE_annotated_comparison_still_evaluates() {
6282 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
6283 Ok(Some(from_vec(vec![json!({"n": "b"}), json!({"n": "a"})])))
6284 };
6285 let (_, rows) = run(r#"SELECT n FROM pg_class ORDER BY n COLLATE "C""#, &t).unwrap();
6286 assert_eq!(rows[0]["n"], json!("a"), "the ORDER BY still sorts");
6287 }
6288}