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}
348
349#[derive(Debug, Clone, PartialEq)]
350pub struct SelectItem {
351 pub expr: Expr,
352 pub alias: Option<String>,
355}
356
357#[derive(Debug, Clone, Copy, PartialEq, Eq)]
358pub enum JoinKind { Inner, Left, Right, Full, Cross }
359
360#[derive(Debug, Clone, PartialEq)]
361pub struct TableRef {
362 pub name: String,
370 pub alias: Option<String>,
371 pub sub: Option<Box<Select>>,
374 pub args: Option<Vec<Expr>>,
378 pub col_aliases: Vec<String>,
380 pub lateral: bool,
384}
385
386impl TableRef {
387 pub fn named(name: impl Into<String>, alias: Option<String>) -> Self {
389 TableRef { name: name.into(), alias, sub: None, args: None, col_aliases: vec![], lateral: false }
390 }
391
392 pub fn binding(&self) -> String {
395 self.alias.clone().unwrap_or_else(|| {
396 self.name.rsplit('.').next().unwrap_or(&self.name).to_string()
397 })
398 }
399}
400
401#[derive(Debug, Clone, Copy, PartialEq, Eq)]
403pub enum SetOp { Union, Intersect, Except }
404
405#[derive(Debug, Clone, PartialEq)]
407pub struct SetArm {
408 pub op: SetOp,
409 pub all: bool,
410 pub query: Select,
411}
412
413#[derive(Debug, Clone, PartialEq)]
414pub struct Join {
415 pub kind: JoinKind,
416 pub table: TableRef,
417 pub on: Option<Expr>,
418}
419
420#[derive(Debug, Clone, Copy, PartialEq)]
421pub enum Dir { Asc, Desc }
422
423#[derive(Debug, Clone, PartialEq)]
424pub struct OrderBy {
425 pub ordinal: Option<usize>,
429 pub expr: Option<Expr>,
430 pub dir: Dir,
431 pub nulls_first: bool,
433}
434
435#[derive(Debug, Clone, PartialEq)]
436pub struct Select {
437 pub distinct: bool,
438 pub items: Vec<SelectItem>,
439 pub from: Option<TableRef>,
440 pub joins: Vec<Join>,
441 pub where_: Option<Expr>,
442 pub order_by: Vec<OrderBy>,
446 pub limit: Option<usize>,
447 pub offset: Option<usize>,
448 pub set_ops: Vec<SetArm>,
451}
452
453impl Select {
454 pub fn base_relations(&self) -> Vec<String> {
463 let mut out = vec![];
464 self.collect_relations(&mut out);
465 out
466 }
467
468 fn collect_relations(&self, out: &mut Vec<String>) {
469 fn table(t: &TableRef, out: &mut Vec<String>) {
470 if let Some(sub) = &t.sub {
471 sub.collect_relations(out);
472 } else if let Some(args) = &t.args {
473 for a in args {
474 expr(a, out);
475 }
476 } else {
477 out.push(t.name.clone());
478 }
479 }
480 fn expr(e: &Expr, out: &mut Vec<String>) {
481 match e {
482 Expr::Subquery(q) | Expr::ArrayQuery(q) => q.collect_relations(out),
483 Expr::Exists { query, .. } => query.collect_relations(out),
484 Expr::InSubquery { expr: x, query, .. } => {
485 expr(x, out);
486 query.collect_relations(out);
487 }
488 Expr::Quantified { left, right, .. } => {
489 expr(left, out);
490 expr(right, out);
491 }
492 Expr::Index { expr: x, index } => {
493 expr(x, out);
494 expr(index, out);
495 }
496 Expr::ArrayLit(items) | Expr::InList { list: items, .. } => {
497 if let Expr::InList { expr: x, .. } = e {
498 expr(x, out);
499 }
500 for i in items {
501 expr(i, out);
502 }
503 }
504 Expr::Func { args, .. } => {
505 for a in args {
506 expr(a, out);
507 }
508 }
509 Expr::Case { operand, whens, else_ } => {
510 if let Some(o) = operand {
511 expr(o, out);
512 }
513 for (w, t) in whens {
514 expr(w, out);
515 expr(t, out);
516 }
517 if let Some(x) = else_ {
518 expr(x, out);
519 }
520 }
521 Expr::Binary { left, right, .. } => {
522 expr(left, out);
523 expr(right, out);
524 }
525 Expr::Unary { expr: x, .. } | Expr::Cast { expr: x, .. } | Expr::IsNull { expr: x, .. } => {
526 expr(x, out)
527 }
528 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => {}
529 }
530 }
531 if let Some(f) = &self.from {
532 table(f, out);
533 }
534 for j in &self.joins {
535 table(&j.table, out);
536 if let Some(on) = &j.on {
537 expr(on, out);
538 }
539 }
540 for item in &self.items {
541 expr(&item.expr, out);
542 }
543 if let Some(w) = &self.where_ {
544 expr(w, out);
545 }
546 for ob in &self.order_by {
547 if let Some(e) = &ob.expr {
548 expr(e, out);
549 }
550 }
551 for arm in &self.set_ops {
552 arm.query.collect_relations(out);
553 }
554 }
555}
556
557fn binding_power(op: &str) -> Option<u8> {
568 Some(match op {
569 "OR" => 1,
570 "AND" => 2,
571 "=" | "!=" | "<>" | "<" | "<=" | ">" | ">=" | "~" | "~*" | "!~" | "!~*"
575 | "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => 4,
576 "||" => 5,
577 "+" | "-" => 6,
578 "*" | "/" | "%" => 7,
579 _ => return None,
580 })
581}
582
583struct Parser {
584 toks: Vec<Tok>,
585 pos: usize,
586}
587
588impl Parser {
589 fn peek(&self) -> &Tok {
590 self.toks.get(self.pos).unwrap_or(&Tok::Eof)
591 }
592 fn peek_at(&self, n: usize) -> &Tok {
593 self.toks.get(self.pos + n).unwrap_or(&Tok::Eof)
594 }
595 fn next(&mut self) -> Tok {
596 let t = self.peek().clone();
597 self.pos += 1;
598 t
599 }
600 fn eat_kw(&mut self, kw: &str) -> bool {
601 if self.peek().is_kw(kw) {
602 self.pos += 1;
603 true
604 } else {
605 false
606 }
607 }
608 fn expect_kw(&mut self, kw: &str) -> Result<()> {
609 if self.eat_kw(kw) {
610 Ok(())
611 } else {
612 bail!("expected {} , got {:?}", kw, self.peek())
613 }
614 }
615 fn eat_punct(&mut self, c: char) -> bool {
616 if matches!(self.peek(), Tok::Punct(p) if *p == c) {
617 self.pos += 1;
618 true
619 } else {
620 false
621 }
622 }
623 fn expect_punct(&mut self, c: char) -> Result<()> {
624 if self.eat_punct(c) {
625 Ok(())
626 } else {
627 bail!("expected {:?}, got {:?}", c, self.peek())
628 }
629 }
630 fn eat_op(&mut self, op: &str) -> bool {
631 if matches!(self.peek(), Tok::Op(o) if o == op) {
632 self.pos += 1;
633 true
634 } else {
635 false
636 }
637 }
638
639 fn parse_expr(&mut self) -> Result<Expr> {
642 self.parse_bin(0)
643 }
644
645 fn parse_bin(&mut self, min_bp: u8) -> Result<Expr> {
648 let mut left = self.parse_unary()?;
649
650 loop {
651 if self.peek().is_kw("OPERATOR") && matches!(self.peek_at(1), Tok::Punct('(')) {
658 let save = self.pos;
659 self.pos += 2;
660 let mut sym = None;
662 while sym.is_none() {
663 match self.next() {
664 Tok::Op(o) => sym = Some(o),
665 Tok::Word { .. } | Tok::Punct('.') => continue,
666 _ => break,
667 }
668 }
669 match sym {
670 Some(o) if binding_power(&o).is_some() && self.eat_punct(')') => {
671 let bp = binding_power(&o).unwrap();
672 if bp < min_bp {
673 self.pos = save;
674 break;
675 }
676 let right = self.parse_bin(bp + 1)?;
677 left = Expr::Binary {
678 op: o,
679 left: Box::new(left),
680 right: Box::new(right),
681 };
682 continue;
683 }
684 _ => {
687 self.pos = save;
688 break;
689 }
690 }
691 }
692
693 let (op, width) = match self.peek() {
694 Tok::Op(o) if binding_power(o).is_some() => (o.clone(), 1usize),
695 Tok::Word { upper, .. } if upper == "AND" || upper == "OR" => (upper.clone(), 1),
696 Tok::Word { upper, .. } if upper == "LIKE" || upper == "ILIKE" => (upper.clone(), 1),
697 Tok::Word { upper, .. } if upper == "NOT" => {
698 match self.peek_at(1) {
700 Tok::Word { upper: u2, .. } if u2 == "LIKE" || u2 == "ILIKE" => {
701 (format!("NOT {}", u2), 2)
702 }
703 _ => break,
704 }
705 }
706 _ => break,
707 };
708
709 let bp = match binding_power(&op) {
710 Some(bp) if bp >= min_bp => bp,
711 _ => break,
712 };
713 self.pos += width;
714
715 let quant = match self.peek() {
721 Tok::Word { upper, .. }
722 if matches!(upper.as_str(), "ANY" | "SOME" | "ALL")
723 && matches!(self.peek_at(1), Tok::Punct('(')) =>
724 {
725 Some(upper == "ALL")
726 }
727 _ => None,
728 };
729 if let Some(all) = quant {
730 self.pos += 2; let right = if self.peek().is_kw("SELECT") {
732 Expr::ArrayQuery(Box::new(self.parse_query()?))
733 } else {
734 self.parse_expr()?
735 };
736 self.expect_punct(')')?;
737 left = Expr::Quantified { op, left: Box::new(left), all, right: Box::new(right) };
738 continue;
739 }
740
741 let right = self.parse_bin(bp + 1)?;
743 left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
744 }
745
746 Ok(left)
747 }
748
749 fn parse_postfix(&mut self, mut e: Expr) -> Result<Expr> {
750 loop {
751 if matches!(self.peek(), Tok::Punct('[')) {
754 self.pos += 1;
755 let index = self.parse_expr()?;
756 self.expect_punct(']')?;
757 e = Expr::Index { expr: Box::new(e), index: Box::new(index) };
758 continue;
759 }
760
761 if self.peek().is_kw("IS") {
763 self.pos += 1;
764 let negated = self.eat_kw("NOT");
765 if self.eat_kw("DISTINCT") {
769 self.expect_kw("FROM")?;
770 let rhs = self.parse_bin(5)?;
772 e = Expr::Binary {
773 op: if negated { "IS NOT DISTINCT FROM".into() } else { "IS DISTINCT FROM".into() },
774 left: Box::new(e),
775 right: Box::new(rhs),
776 };
777 continue;
778 }
779 if !self.eat_kw("NULL") {
780 if self.eat_kw("TRUE") {
782 e = Expr::Binary {
783 op: "=".into(),
784 left: Box::new(e),
785 right: Box::new(Expr::Literal(Value::Bool(!negated))),
786 };
787 continue;
788 }
789 if self.eat_kw("FALSE") {
790 e = Expr::Binary {
791 op: "=".into(),
792 left: Box::new(e),
793 right: Box::new(Expr::Literal(Value::Bool(negated))),
794 };
795 continue;
796 }
797 bail!("expected NULL, TRUE or FALSE after IS, got {:?}", self.peek());
798 }
799 e = Expr::IsNull { expr: Box::new(e), negated };
800 continue;
801 }
802
803 let negated_in = if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("IN") {
805 self.pos += 2;
806 true
807 } else if self.peek().is_kw("IN") {
808 self.pos += 1;
809 false
810 } else {
811 let negated_between =
813 if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("BETWEEN") {
814 self.pos += 2;
815 true
816 } else if self.peek().is_kw("BETWEEN") {
817 self.pos += 1;
818 false
819 } else {
820 break;
821 };
822 let low = self.parse_bin(3)?;
826 self.expect_kw("AND")?;
827 let high = self.parse_bin(3)?;
828 let ge = Expr::Binary {
829 op: ">=".into(),
830 left: Box::new(e.clone()),
831 right: Box::new(low),
832 };
833 let le = Expr::Binary {
834 op: "<=".into(),
835 left: Box::new(e),
836 right: Box::new(high),
837 };
838 let both = Expr::Binary {
839 op: "AND".into(),
840 left: Box::new(ge),
841 right: Box::new(le),
842 };
843 e = if negated_between {
844 Expr::Unary { op: "NOT".into(), expr: Box::new(both) }
845 } else {
846 both
847 };
848 continue;
849 };
850
851 self.expect_punct('(')?;
852 if self.peek().is_kw("SELECT") {
854 let query = Box::new(self.parse_query()?);
855 self.expect_punct(')')?;
856 e = Expr::InSubquery { expr: Box::new(e), query, negated: negated_in };
857 continue;
858 }
859 let mut list = vec![];
860 if !self.eat_punct(')') {
861 loop {
862 list.push(self.parse_expr()?);
863 if self.eat_punct(',') {
864 continue;
865 }
866 self.expect_punct(')')?;
867 break;
868 }
869 }
870 e = Expr::InList { expr: Box::new(e), list, negated: negated_in };
871 }
872 Ok(e)
873 }
874
875 fn parse_unary(&mut self) -> Result<Expr> {
876 if self.peek().is_kw("NOT") {
877 self.pos += 1;
878 let e = self.parse_bin(3)?;
881 return Ok(Expr::Unary { op: "NOT".into(), expr: Box::new(e) });
882 }
883 if self.eat_op("-") {
884 let e = self.parse_unary()?;
885 return Ok(Expr::Unary { op: "-".into(), expr: Box::new(e) });
886 }
887 if self.eat_op("+") {
888 return self.parse_unary();
889 }
890 let atom = self.parse_atom()?;
891 let cast = self.parse_casts(atom)?;
892 self.parse_postfix(cast)
903 }
904
905 fn parse_casts(&mut self, mut e: Expr) -> Result<Expr> {
907 loop {
908 if self.peek().is_kw("COLLATE") {
913 self.pos += 1;
914 match self.next() {
915 Tok::Word { .. } | Tok::Quoted(_) => {}
916 other => bail!("expected a collation name after COLLATE, got {:?}", other),
917 }
918 while self.eat_punct('.') {
920 match self.next() {
921 Tok::Word { .. } | Tok::Quoted(_) => {}
922 other => bail!("expected a name after '.', got {:?}", other),
923 }
924 }
925 continue;
926 }
927 if !self.eat_op("::") {
928 break;
929 }
930 let mut ty = match self.next() {
931 Tok::Word { raw, .. } => raw,
932 Tok::Quoted(s) => s,
933 other => bail!("expected a type name after ::, got {:?}", other),
934 };
935 while self.eat_punct('.') {
937 match self.next() {
938 Tok::Word { raw, .. } => ty = raw,
939 Tok::Quoted(s) => ty = s,
940 other => bail!("expected a type name after ., got {:?}", other),
941 }
942 }
943 while self.eat_punct('[') {
945 self.expect_punct(']')?;
946 ty.push_str("[]");
947 }
948 e = Expr::Cast { expr: Box::new(e), ty };
949 }
950 Ok(e)
951 }
952
953
954 fn parse_atom(&mut self) -> Result<Expr> {
955 if self.eat_punct('(') {
962 if self.peek().is_kw("SELECT") {
965 let q = self.parse_query()?;
966 self.expect_punct(')')?;
967 return Ok(Expr::Subquery(Box::new(q)));
968 }
969 let e = self.parse_expr()?;
970 self.expect_punct(')')?;
971 return Ok(e);
972 }
973
974 if self.peek().is_kw("ARRAY") && matches!(self.peek_at(1), Tok::Punct('(') | Tok::Punct('[')) {
978 self.pos += 1;
979 if self.eat_punct('(') {
980 if !self.peek().is_kw("SELECT") {
981 bail!("ARRAY(...) takes a subquery; for a list of values write ARRAY[...]");
982 }
983 let q = self.parse_query()?;
984 self.expect_punct(')')?;
985 return Ok(Expr::ArrayQuery(Box::new(q)));
986 }
987 self.expect_punct('[')?;
988 let mut items = vec![];
989 if !self.eat_punct(']') {
990 loop {
991 items.push(self.parse_expr()?);
992 if self.eat_punct(',') {
993 continue;
994 }
995 self.expect_punct(']')?;
996 break;
997 }
998 }
999 return Ok(Expr::ArrayLit(items));
1000 }
1001
1002 if self.peek().is_kw("EXISTS") && matches!(self.peek_at(1), Tok::Punct('(')) {
1006 self.pos += 2;
1007 if !self.peek().is_kw("SELECT") {
1008 bail!("EXISTS (...) takes a subquery");
1009 }
1010 let q = self.parse_query()?;
1011 self.expect_punct(')')?;
1012 return Ok(Expr::Exists { query: Box::new(q), negated: false });
1013 }
1014
1015 if self.peek().is_kw("CAST") && matches!(self.peek_at(1), Tok::Punct('(')) {
1018 self.pos += 2;
1019 let inner = self.parse_expr()?;
1020 self.expect_kw("AS")?;
1021 let mut ty = match self.next() {
1022 Tok::Word { raw, .. } => raw,
1023 Tok::Quoted(s) => s,
1024 other => bail!("expected a type name in CAST, got {:?}", other),
1025 };
1026 while self.eat_punct('.') {
1027 match self.next() {
1028 Tok::Word { raw, .. } => ty = raw,
1029 Tok::Quoted(s) => ty = s,
1030 other => bail!("expected a type name after ., got {:?}", other),
1031 }
1032 }
1033 while self.eat_punct('[') {
1034 self.expect_punct(']')?;
1035 ty.push_str("[]");
1036 }
1037 self.expect_punct(')')?;
1038 return Ok(Expr::Cast { expr: Box::new(inner), ty });
1039 }
1040
1041 if self.peek().is_kw("CASE") {
1043 return self.parse_case();
1044 }
1045
1046 match self.next() {
1047 Tok::Num(n) => Ok(Expr::Literal(from_f64(n))),
1048 Tok::Str(s) => Ok(Expr::Literal(Value::String(s))),
1049 Tok::Op(o) if o == "*" => Ok(Expr::Star),
1050 Tok::Quoted(name) => self.parse_name_tail(None, name),
1051 Tok::Word { upper, raw } => match upper.as_str() {
1052 "NULL" => Ok(Expr::Literal(Value::Null)),
1053 "TRUE" => Ok(Expr::Literal(Value::Bool(true))),
1054 "FALSE" => Ok(Expr::Literal(Value::Bool(false))),
1055 "CURRENT_SCHEMA" | "CURRENT_DATABASE" | "CURRENT_USER" | "SESSION_USER"
1059 | "CURRENT_CATALOG" | "USER" | "VERSION"
1060 if !matches!(self.peek(), Tok::Punct('(')) =>
1061 {
1062 Ok(Expr::Func { name: upper.to_lowercase(), args: vec![] })
1063 }
1064 _ => self.parse_name_tail(None, raw),
1065 },
1066 other => bail!("unexpected {:?} in an expression", other),
1067 }
1068 }
1069
1070 fn parse_name_tail(&mut self, _schema: Option<String>, first: String) -> Result<Expr> {
1077 let mut parts = vec![first];
1078 while self.eat_punct('.') {
1079 if self.eat_op("*") {
1081 return Ok(Expr::QualifiedStar(parts.pop().unwrap_or_default()));
1082 }
1083 match self.next() {
1084 Tok::Word { raw, .. } => parts.push(raw),
1085 Tok::Quoted(s) => parts.push(s),
1086 other => bail!("expected a name after '.', got {:?}", other),
1087 }
1088 }
1089
1090 if matches!(self.peek(), Tok::Punct('(')) {
1094 self.pos += 1;
1095 let name = parts.pop().unwrap_or_default().to_lowercase();
1096 let mut args = vec![];
1097 if !self.eat_punct(')') {
1098 loop {
1099 if self.eat_op("*") {
1101 args.push(Expr::Star);
1102 } else {
1103 args.push(self.parse_expr()?);
1104 }
1105 if self.eat_punct(',') {
1106 continue;
1107 }
1108 self.expect_punct(')')?;
1109 break;
1110 }
1111 }
1112 return Ok(Expr::Func { name, args });
1113 }
1114
1115 let name = parts.pop().unwrap_or_default();
1116 let qual = parts.pop();
1120 Ok(Expr::Column { qual, name })
1121 }
1122
1123 fn parse_case(&mut self) -> Result<Expr> {
1124 self.expect_kw("CASE")?;
1125 let operand = if self.peek().is_kw("WHEN") {
1127 None
1128 } else {
1129 Some(Box::new(self.parse_expr()?))
1130 };
1131 let mut whens = vec![];
1132 while self.eat_kw("WHEN") {
1133 let cond = self.parse_expr()?;
1134 self.expect_kw("THEN")?;
1135 let then = self.parse_expr()?;
1136 whens.push((cond, then));
1137 }
1138 if whens.is_empty() {
1139 bail!("CASE needs at least one WHEN branch");
1140 }
1141 let else_ = if self.eat_kw("ELSE") {
1142 Some(Box::new(self.parse_expr()?))
1143 } else {
1144 None
1145 };
1146 self.expect_kw("END")?;
1147 Ok(Expr::Case { operand, whens, else_ })
1148 }
1149
1150 fn parse_table_ref(&mut self) -> Result<TableRef> {
1153 let lateral = self.eat_kw("LATERAL");
1154 if self.eat_punct('(') {
1156 if !self.peek().is_kw("SELECT") {
1157 bail!("expected a subquery after '(' in FROM, got {:?}", self.peek());
1158 }
1159 let sub = self.parse_query()?;
1160 self.expect_punct(')')?;
1161 let (alias, col_aliases) = self.parse_table_alias()?;
1162 if alias.is_none() {
1163 bail!("a subquery in FROM must have an alias");
1164 }
1165 return Ok(TableRef {
1166 name: "(subquery)".into(),
1167 alias,
1168 sub: Some(Box::new(sub)),
1169 args: None,
1170 col_aliases,
1171 lateral,
1172 });
1173 }
1174 if lateral {
1175 bail!("LATERAL applies to a subquery in FROM; write LATERAL (SELECT ...)");
1176 }
1177
1178 let mut parts = vec![match self.next() {
1179 Tok::Word { raw, .. } => raw,
1180 Tok::Quoted(s) => s,
1181 other => bail!("expected a table name, got {:?}", other),
1182 }];
1183 while self.eat_punct('.') {
1184 match self.next() {
1185 Tok::Word { raw, .. } => parts.push(raw),
1186 Tok::Quoted(s) => parts.push(s),
1187 other => bail!("expected a name after '.', got {:?}", other),
1188 }
1189 }
1190 let name = parts.join(".");
1191
1192 if self.eat_punct('(') {
1195 let mut args = vec![];
1196 if !self.eat_punct(')') {
1197 loop {
1198 args.push(self.parse_expr()?);
1199 if self.eat_punct(',') {
1200 continue;
1201 }
1202 self.expect_punct(')')?;
1203 break;
1204 }
1205 }
1206 let fname = name.rsplit('.').next().unwrap_or(&name).to_lowercase();
1207 let (alias, col_aliases) = self.parse_table_alias()?;
1208 return Ok(TableRef { name: fname, alias, sub: None, args: Some(args), col_aliases, lateral: false });
1209 }
1210
1211 let (alias, col_aliases) = self.parse_table_alias()?;
1212 Ok(TableRef { name, alias, sub: None, args: None, col_aliases, lateral: false })
1213 }
1214
1215 fn parse_table_alias(&mut self) -> Result<(Option<String>, Vec<String>)> {
1220 let alias = if self.eat_kw("AS") {
1221 match self.next() {
1222 Tok::Word { raw, .. } => Some(raw),
1223 Tok::Quoted(s) => Some(s),
1224 other => bail!("expected an alias after AS, got {:?}", other),
1225 }
1226 } else {
1227 match self.peek().clone() {
1228 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1229 self.pos += 1;
1230 Some(raw)
1231 }
1232 Tok::Quoted(s) => {
1233 self.pos += 1;
1234 Some(s)
1235 }
1236 _ => None,
1237 }
1238 };
1239 let mut col_aliases = vec![];
1240 if alias.is_some() && self.eat_punct('(') {
1241 loop {
1242 match self.next() {
1243 Tok::Word { raw, .. } => col_aliases.push(raw),
1244 Tok::Quoted(s) => col_aliases.push(s),
1245 other => bail!("expected a column alias, got {:?}", other),
1246 }
1247 if self.eat_punct(',') {
1248 continue;
1249 }
1250 self.expect_punct(')')?;
1251 break;
1252 }
1253 }
1254 Ok((alias, col_aliases))
1255 }
1256
1257 fn parse_query(&mut self) -> Result<Select> {
1264 let mut first = self.parse_select_body()?;
1265 loop {
1266 let op = if self.eat_kw("UNION") {
1267 SetOp::Union
1268 } else if self.eat_kw("INTERSECT") {
1269 SetOp::Intersect
1270 } else if self.eat_kw("EXCEPT") {
1271 SetOp::Except
1272 } else {
1273 break;
1274 };
1275 let all = self.eat_kw("ALL");
1276 if !all {
1277 let _ = self.eat_kw("DISTINCT");
1278 }
1279 let query = if self.eat_punct('(') {
1281 let q = self.parse_query()?;
1282 self.expect_punct(')')?;
1283 q
1284 } else {
1285 self.parse_select_body()?
1286 };
1287 first.set_ops.push(SetArm { op, all, query });
1288 }
1289 self.parse_query_tail(&mut first)?;
1290 Ok(first)
1291 }
1292
1293 fn parse_query_tail(&mut self, sel: &mut Select) -> Result<()> {
1294 let mut order_by = vec![];
1295 if self.eat_kw("ORDER") {
1296 self.expect_kw("BY")?;
1297 loop {
1298 let (ordinal, expr) = match self.peek().clone() {
1302 Tok::Num(n)
1303 if n.fract() == 0.0
1304 && n >= 1.0
1305 && !matches!(self.peek_at(1), Tok::Op(_)) =>
1306 {
1307 self.pos += 1;
1308 (Some(n as usize), None)
1309 }
1310 _ => (None, Some(self.parse_expr()?)),
1311 };
1312 let dir = if self.eat_kw("DESC") {
1313 Dir::Desc
1314 } else {
1315 let _ = self.eat_kw("ASC");
1316 Dir::Asc
1317 };
1318 let mut nulls_first = matches!(dir, Dir::Desc);
1320 if self.eat_kw("NULLS") {
1321 if self.eat_kw("FIRST") {
1322 nulls_first = true;
1323 } else if self.eat_kw("LAST") {
1324 nulls_first = false;
1325 } else {
1326 bail!("expected FIRST or LAST after NULLS, got {:?}", self.peek());
1327 }
1328 }
1329 order_by.push(OrderBy { ordinal, expr, dir, nulls_first });
1330 if self.eat_punct(',') {
1331 continue;
1332 }
1333 break;
1334 }
1335 }
1336
1337 let mut limit = None;
1338 let mut offset = None;
1339 loop {
1341 if self.eat_kw("LIMIT") {
1342 if self.eat_kw("ALL") {
1343 limit = None;
1344 } else {
1345 limit = Some(self.parse_count("LIMIT")?);
1346 }
1347 continue;
1348 }
1349 if self.eat_kw("OFFSET") {
1350 offset = Some(self.parse_count("OFFSET")?);
1351 let _ = self.eat_kw("ROW") || self.eat_kw("ROWS");
1352 continue;
1353 }
1354 break;
1355 }
1356 sel.order_by = order_by;
1357 sel.limit = limit;
1358 sel.offset = offset;
1359 Ok(())
1360 }
1361
1362 fn parse_select_body(&mut self) -> Result<Select> {
1364 self.expect_kw("SELECT")?;
1365 let distinct = self.eat_kw("DISTINCT");
1366 if distinct && self.peek().is_kw("ON") {
1367 bail!("DISTINCT ON is not supported");
1368 }
1369 let _ = self.eat_kw("ALL");
1370
1371 let mut items = vec![];
1372 loop {
1373 let expr = self.parse_expr()?;
1374 let alias = if self.eat_kw("AS") {
1376 match self.next() {
1377 Tok::Word { raw, .. } => Some(raw),
1378 Tok::Quoted(s) => Some(s),
1379 other => bail!("expected an alias after AS, got {:?}", other),
1380 }
1381 } else {
1382 match self.peek().clone() {
1383 Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1384 self.pos += 1;
1385 Some(raw)
1386 }
1387 Tok::Quoted(s) => {
1388 self.pos += 1;
1389 Some(s)
1390 }
1391 _ => None,
1392 }
1393 };
1394 items.push(SelectItem { expr, alias });
1395 if self.eat_punct(',') {
1396 continue;
1397 }
1398 break;
1399 }
1400
1401 let mut from = None;
1402 let mut joins = vec![];
1403 if self.eat_kw("FROM") {
1404 from = Some(self.parse_table_ref()?);
1405 loop {
1406 if self.eat_punct(',') {
1412 let table = self.parse_table_ref()?;
1413 joins.push(Join { kind: JoinKind::Cross, table, on: None });
1414 continue;
1415 }
1416 let kind = if self.peek().is_kw("JOIN") {
1417 self.pos += 1;
1418 JoinKind::Inner
1419 } else if self.peek().is_kw("INNER") && self.peek_at(1).is_kw("JOIN") {
1420 self.pos += 2;
1421 JoinKind::Inner
1422 } else if self.peek().is_kw("CROSS") && self.peek_at(1).is_kw("JOIN") {
1423 self.pos += 2;
1424 JoinKind::Cross
1425 } else if self.peek().is_kw("LEFT") {
1426 self.pos += 1;
1427 let _ = self.eat_kw("OUTER");
1428 self.expect_kw("JOIN")?;
1429 JoinKind::Left
1430 } else if self.peek().is_kw("RIGHT") {
1431 self.pos += 1;
1432 let _ = self.eat_kw("OUTER");
1433 self.expect_kw("JOIN")?;
1434 JoinKind::Right
1435 } else if self.peek().is_kw("FULL") {
1436 self.pos += 1;
1437 let _ = self.eat_kw("OUTER");
1438 self.expect_kw("JOIN")?;
1439 JoinKind::Full
1440 } else {
1441 break;
1442 };
1443 let table = self.parse_table_ref()?;
1444 let on = if self.eat_kw("ON") {
1445 Some(self.parse_expr()?)
1446 } else if self.peek().is_kw("USING") {
1447 bail!("JOIN ... USING is not supported — write ON a.col = b.col");
1448 } else {
1449 None
1450 };
1451 if on.is_none() && !matches!(kind, JoinKind::Cross) {
1452 bail!("a {:?} JOIN needs an ON clause", kind);
1453 }
1454 joins.push(Join { kind, table, on });
1455 }
1456 }
1457
1458 let where_ = if self.eat_kw("WHERE") {
1459 Some(self.parse_expr()?)
1460 } else {
1461 None
1462 };
1463
1464 if self.peek().is_kw("GROUP") {
1465 bail!("GROUP BY is not supported by this SELECT path");
1466 }
1467 if self.peek().is_kw("HAVING") {
1468 bail!("HAVING is not supported by this SELECT path");
1469 }
1470
1471 Ok(Select {
1472 distinct,
1473 items,
1474 from,
1475 joins,
1476 where_,
1477 order_by: vec![],
1478 limit: None,
1479 offset: None,
1480 set_ops: vec![],
1481 })
1482 }
1483
1484 fn parse_count(&mut self, what: &str) -> Result<usize> {
1485 match self.next() {
1486 Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Ok(n as usize),
1487 other => bail!("{} expects a non-negative integer, got {:?}", what, other),
1488 }
1489 }
1490}
1491
1492fn is_clause_keyword(upper: &str) -> bool {
1497 matches!(
1498 upper,
1499 "FROM" | "WHERE" | "GROUP" | "HAVING" | "ORDER" | "LIMIT" | "OFFSET"
1500 | "JOIN" | "LEFT" | "RIGHT" | "FULL" | "INNER" | "CROSS" | "OUTER"
1501 | "ON" | "USING" | "AND" | "OR" | "AS" | "UNION" | "INTERSECT"
1502 | "EXCEPT" | "FETCH" | "FOR" | "WINDOW" | "RETURNING" | "INTO"
1503 | "ASC" | "DESC" | "NULLS" | "IS" | "IN" | "NOT" | "LIKE" | "ILIKE"
1504 | "BETWEEN" | "THEN" | "WHEN" | "ELSE" | "END" | "CASE" | "DISTINCT"
1505 | "SELECT" | "WITH" | "ALL"
1506 )
1507}
1508
1509pub fn parse(sql: &str) -> Result<Select> {
1511 let toks = lex(sql)?;
1512 let mut p = Parser { toks, pos: 0 };
1513 let sel = if matches!(p.peek(), Tok::Punct('(')) && p.peek_at(1).is_kw("SELECT") {
1515 p.pos += 1;
1516 let mut first = p.parse_query()?;
1517 p.expect_punct(')')?;
1518 loop {
1520 let op = if p.eat_kw("UNION") {
1521 SetOp::Union
1522 } else if p.eat_kw("INTERSECT") {
1523 SetOp::Intersect
1524 } else if p.eat_kw("EXCEPT") {
1525 SetOp::Except
1526 } else {
1527 break;
1528 };
1529 let all = p.eat_kw("ALL");
1530 if !all {
1531 let _ = p.eat_kw("DISTINCT");
1532 }
1533 let query = if p.eat_punct('(') {
1534 let q = p.parse_query()?;
1535 p.expect_punct(')')?;
1536 q
1537 } else {
1538 p.parse_select_body()?
1539 };
1540 first.set_ops.push(SetArm { op, all, query });
1541 }
1542 p.parse_query_tail(&mut first)?;
1543 first
1544 } else {
1545 p.parse_query()?
1546 };
1547 let _ = p.eat_punct(';');
1548 if !matches!(p.peek(), Tok::Eof) {
1549 bail!("unexpected trailing tokens: {:?}", p.peek());
1550 }
1551 Ok(sel)
1552}
1553
1554pub struct Bound<'a> {
1567 pub parts: Vec<(String, Option<&'a Value>)>,
1568 pub ctx: EvalCtx<'a>,
1570}
1571
1572#[derive(Clone, Copy, Default)]
1585pub struct EvalCtx<'a> {
1586 pub resolver: Option<&'a Resolver<'a>>,
1587 pub outer: Option<&'a Bound<'a>>,
1588}
1589
1590impl<'a> Bound<'a> {
1591 pub fn new(parts: Vec<(String, Option<&'a Value>)>) -> Self {
1593 Bound { parts, ctx: EvalCtx::default() }
1594 }
1595
1596 fn column(&self, qual: Option<&str>, name: &str) -> Value {
1603 match qual {
1604 Some(q) => {
1605 for (binding, row) in &self.parts {
1606 if binding.eq_ignore_ascii_case(q) {
1607 return row
1608 .and_then(|r| r.get(name))
1609 .cloned()
1610 .unwrap_or(Value::Null);
1611 }
1612 }
1613 match self.ctx.outer {
1614 Some(o) if o.has_binding(q) => o.column(qual, name),
1615 _ => Value::Null,
1616 }
1617 }
1618 None => {
1619 for (_, row) in &self.parts {
1620 if let Some(v) = row.and_then(|r| r.get(name)) {
1621 return v.clone();
1622 }
1623 }
1624 match self.ctx.outer {
1625 Some(o) => o.column(None, name),
1626 None => Value::Null,
1627 }
1628 }
1629 }
1630 }
1631
1632 fn has_binding(&self, qual: &str) -> bool {
1637 self.parts.iter().any(|(b, _)| b.eq_ignore_ascii_case(qual))
1638 || self.ctx.outer.is_some_and(|o| o.has_binding(qual))
1639 }
1640
1641 fn flatten(&self) -> Vec<(String, Value)> {
1643 let mut out = vec![];
1644 for (_, row) in &self.parts {
1645 if let Some(Value::Object(m)) = row {
1646 for (k, v) in m {
1647 out.push((k.clone(), v.clone()));
1648 }
1649 }
1650 }
1651 out
1652 }
1653
1654 fn flatten_binding(&self, qual: &str) -> Vec<(String, Value)> {
1655 let mut out = vec![];
1656 for (binding, row) in &self.parts {
1657 if binding.eq_ignore_ascii_case(qual) {
1658 if let Some(Value::Object(m)) = row {
1659 for (k, v) in m {
1660 out.push((k.clone(), v.clone()));
1661 }
1662 }
1663 }
1664 }
1665 out
1666 }
1667}
1668
1669type Truth = Option<bool>;
1677
1678fn truthy(v: &Value) -> Truth {
1679 match v {
1680 Value::Null => None,
1681 Value::Bool(b) => Some(*b),
1682 _ => None,
1686 }
1687}
1688
1689fn cmp_values(a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
1695 use std::cmp::Ordering;
1696 match (a, b) {
1697 (Value::Null, _) | (_, Value::Null) => None,
1698 (Value::Number(x), Value::Number(y)) => {
1699 x.as_f64().partial_cmp(&y.as_f64())
1700 }
1701 (Value::String(x), Value::String(y)) => Some(x.cmp(y)),
1702 (Value::Bool(x), Value::Bool(y)) => Some(x.cmp(y)),
1703 (Value::Number(x), Value::String(y)) => match y.parse::<f64>() {
1706 Ok(n) => x.as_f64().partial_cmp(&Some(n)),
1707 Err(_) => Some(as_text(a).cmp(&as_text(b))),
1708 },
1709 (Value::String(x), Value::Number(y)) => match x.parse::<f64>() {
1710 Ok(n) => Some(n).partial_cmp(&y.as_f64()),
1711 Err(_) => Some(as_text(a).cmp(&as_text(b))),
1712 },
1713 _ => {
1714 let (x, y) = (as_text(a), as_text(b));
1715 if x == y { Some(Ordering::Equal) } else { Some(x.cmp(&y)) }
1716 }
1717 }
1718}
1719
1720fn as_text(v: &Value) -> String {
1722 match v {
1723 Value::String(s) => s.clone(),
1724 Value::Null => String::new(),
1725 Value::Bool(b) => (if *b { "t" } else { "f" }).to_string(),
1726 Value::Array(items) => {
1729 let inner: Vec<String> = items
1730 .iter()
1731 .map(|i| match i {
1732 Value::Null => "NULL".to_string(),
1733 Value::String(s) if s.is_empty()
1734 || s.chars().any(|c| c.is_whitespace() || matches!(c, ',' | '{' | '}' | '"' | '\\')) =>
1735 {
1736 format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
1737 }
1738 other => as_text(other),
1739 })
1740 .collect();
1741 format!("{{{}}}", inner.join(","))
1742 }
1743 other => other.to_string(),
1744 }
1745}
1746
1747fn num(v: &Value) -> Option<f64> {
1748 match v {
1749 Value::Number(n) => n.as_f64(),
1750 Value::String(s) => s.parse().ok(),
1751 Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
1752 _ => None,
1753 }
1754}
1755
1756fn from_f64(f: f64) -> Value {
1770 if f.is_finite() && f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
1771 return Value::Number((f as i64).into());
1772 }
1773 serde_json::Number::from_f64(f).map(Value::Number).unwrap_or(Value::Null)
1774}
1775
1776pub fn eval(e: &Expr, row: &Bound) -> Result<Value> {
1778 Ok(match e {
1779 Expr::Literal(v) => v.clone(),
1780
1781 Expr::Column { qual, name } => {
1782 if let Some(q) = qual {
1786 if !row.has_binding(q) {
1787 bail!("no table or alias named {:?} in this query", q);
1788 }
1789 }
1790 row.column(qual.as_deref(), name)
1791 }
1792
1793 Expr::Cast { expr, .. } => eval(expr, row)?,
1794
1795 Expr::Star | Expr::QualifiedStar(_) => {
1796 bail!("`*` is only valid in a select list or as count(*)")
1797 }
1798
1799 Expr::Unary { op, expr } => {
1800 let v = eval(expr, row)?;
1801 match op.as_str() {
1802 "NOT" => match truthy(&v) {
1803 None => Value::Null,
1805 Some(b) => Value::Bool(!b),
1806 },
1807 "-" => match num(&v) {
1808 Some(n) => from_f64(-n),
1809 None => Value::Null,
1810 },
1811 other => bail!("unsupported unary operator {:?}", other),
1812 }
1813 }
1814
1815 Expr::Binary { op, left, right } => {
1816 if op == "AND" {
1821 let l = truthy(&eval(left, row)?);
1822 if l == Some(false) {
1823 return Ok(Value::Bool(false));
1824 }
1825 let r = truthy(&eval(right, row)?);
1826 return Ok(match (l, r) {
1827 (_, Some(false)) => Value::Bool(false),
1828 (Some(true), Some(true)) => Value::Bool(true),
1829 _ => Value::Null,
1830 });
1831 }
1832 if op == "OR" {
1833 let l = truthy(&eval(left, row)?);
1834 if l == Some(true) {
1835 return Ok(Value::Bool(true));
1836 }
1837 let r = truthy(&eval(right, row)?);
1838 return Ok(match (l, r) {
1839 (_, Some(true)) => Value::Bool(true),
1840 (Some(false), Some(false)) => Value::Bool(false),
1841 _ => Value::Null,
1842 });
1843 }
1844
1845 let l = eval(left, row)?;
1846 let r = eval(right, row)?;
1847 apply_op(op, l, r)?
1848 }
1849
1850 Expr::IsNull { expr, negated } => {
1851 let v = eval(expr, row)?;
1852 Value::Bool(v.is_null() != *negated)
1855 }
1856
1857 Expr::InList { expr, list, negated } => {
1858 let v = eval(expr, row)?;
1859 if v.is_null() {
1860 return Ok(Value::Null);
1861 }
1862 let mut items = Vec::with_capacity(list.len());
1863 for item in list {
1864 items.push(eval(item, row)?);
1865 }
1866 in_values(&v, &items, *negated)?
1867 }
1868
1869 Expr::Subquery(q) => {
1871 let (cols, rows) = run_sub(q, row)?;
1872 if cols.len() != 1 {
1873 bail!("a subquery used as an expression must return exactly one \
1874 column, this one returns {}", cols.len());
1875 }
1876 match rows.len() {
1877 0 => Value::Null,
1878 1 => rows[0].get(&cols[0].key).cloned().unwrap_or(Value::Null),
1879 n => bail!("more than one row returned by a subquery used as an \
1880 expression ({} rows)", n),
1881 }
1882 }
1883 Expr::Exists { query, negated } => {
1884 let (_, rows) = run_sub(query, row)?;
1885 Value::Bool(!rows.is_empty() != *negated)
1886 }
1887 Expr::ArrayQuery(q) => Value::Array(first_column(q, row)?),
1888 Expr::InSubquery { expr, query, negated } => {
1889 let v = eval(expr, row)?;
1890 if v.is_null() {
1891 return Ok(Value::Null);
1892 }
1893 let items = first_column(query, row)?;
1894 in_values(&v, &items, *negated)?
1895 }
1896 Expr::Quantified { op, left, all, right } => {
1897 let l = eval(left, row)?;
1898 let r = eval(right, row)?;
1899 let items = match r {
1900 Value::Null => return Ok(Value::Null),
1901 Value::Array(items) => items,
1902 other => bail!(
1903 "{} requires an array or a subquery on its right side, got {}",
1904 if *all { "ALL" } else { "ANY" },
1905 as_text(&other)
1906 ),
1907 };
1908 let mut saw_true = false;
1912 let mut saw_false = false;
1913 let mut saw_null = false;
1914 for item in items {
1915 match truthy(&apply_op(op, l.clone(), item)?) {
1916 Some(true) => saw_true = true,
1917 Some(false) => saw_false = true,
1918 None => saw_null = true,
1919 }
1920 }
1921 if *all {
1922 if saw_false {
1923 Value::Bool(false)
1924 } else if saw_null {
1925 Value::Null
1926 } else {
1927 Value::Bool(true)
1928 }
1929 } else if saw_true {
1930 Value::Bool(true)
1931 } else if saw_null {
1932 Value::Null
1933 } else {
1934 Value::Bool(false)
1935 }
1936 }
1937 Expr::Index { expr, index } => {
1938 let arr = eval(expr, row)?;
1939 let i = eval(index, row)?;
1940 match (arr, num(&i)) {
1941 (Value::Array(items), Some(n)) if n >= 1.0 => {
1942 items.get(n as usize - 1).cloned().unwrap_or(Value::Null)
1943 }
1944 _ => Value::Null,
1945 }
1946 }
1947 Expr::ArrayLit(items) => {
1948 let mut out = Vec::with_capacity(items.len());
1949 for i in items {
1950 out.push(eval(i, row)?);
1951 }
1952 Value::Array(out)
1953 }
1954
1955 Expr::Case { operand, whens, else_ } => {
1956 let subject = match operand {
1957 Some(o) => Some(eval(o, row)?),
1958 None => None,
1959 };
1960 for (cond, then) in whens {
1961 let hit = match &subject {
1962 Some(sv) => {
1964 let cv = eval(cond, row)?;
1965 matches!(cmp_values(sv, &cv), Some(std::cmp::Ordering::Equal))
1966 }
1967 None => truthy(&eval(cond, row)?) == Some(true),
1970 };
1971 if hit {
1972 return eval(then, row);
1973 }
1974 }
1975 match else_ {
1976 Some(e) => eval(e, row)?,
1977 None => Value::Null,
1980 }
1981 }
1982
1983 Expr::Func { name, args } => eval_func(name, args, row)?,
1984 })
1985}
1986
1987fn in_values(v: &Value, items: &[Value], negated: bool) -> Result<Value> {
1989 let mut any_null = false;
1990 let mut found = false;
1991 for iv in items {
1992 if iv.is_null() {
1993 any_null = true;
1994 continue;
1995 }
1996 if matches!(cmp_values(v, iv), Some(std::cmp::Ordering::Equal)) {
1997 found = true;
1998 break;
1999 }
2000 }
2001 Ok(if found {
2005 Value::Bool(!negated)
2006 } else if any_null {
2007 Value::Null
2008 } else {
2009 Value::Bool(negated)
2010 })
2011}
2012
2013fn run_sub(q: &Select, row: &Bound) -> Result<(Vec<OutCol>, Vec<Value>)> {
2021 let Some(resolve) = row.ctx.resolver else {
2022 bail!("a subquery cannot run here: this evaluation has no relation resolver");
2023 };
2024 let (cols, rows, _) = execute_inner(q, resolve, Opts::default(), Some(row))?;
2025 Ok((cols, rows))
2026}
2027
2028fn first_column(q: &Select, row: &Bound) -> Result<Vec<Value>> {
2031 let (cols, rows) = run_sub(q, row)?;
2032 let Some(first) = cols.first() else {
2033 bail!("the subquery returns no columns");
2034 };
2035 Ok(rows
2036 .into_iter()
2037 .map(|r| r.get(&first.key).cloned().unwrap_or(Value::Null))
2038 .collect())
2039}
2040
2041fn apply_op(op: &str, l: Value, r: Value) -> Result<Value> {
2045 let compare = |ord: fn(std::cmp::Ordering) -> bool| -> Value {
2047 match cmp_values(&l, &r) {
2048 None => Value::Null,
2049 Some(o) => Value::Bool(ord(o)),
2050 }
2051 };
2052
2053 Ok(match op {
2054 "IS DISTINCT FROM" | "IS NOT DISTINCT FROM" => {
2055 let distinct = match (l.is_null(), r.is_null()) {
2056 (true, true) => false,
2057 (true, false) | (false, true) => true,
2058 (false, false) => !matches!(cmp_values(&l, &r), Some(std::cmp::Ordering::Equal)),
2059 };
2060 Value::Bool(distinct != op.starts_with("IS NOT"))
2061 }
2062 "=" => compare(|o| o.is_eq()),
2063 "!=" | "<>" => compare(|o| o.is_ne()),
2064 "<" => compare(|o| o.is_lt()),
2065 "<=" => compare(|o| o.is_le()),
2066 ">" => compare(|o| o.is_gt()),
2067 ">=" => compare(|o| o.is_ge()),
2068
2069 "~" | "~*" | "!~" | "!~*" => {
2070 if l.is_null() || r.is_null() {
2071 Value::Null
2072 } else {
2073 let pat = as_text(&r);
2074 if let Some(why) = crate::nql::regex_error_pub(&pat) {
2075 bail!(
2076 "{} — in {:?}. The supported subset is ^ $ . | ( ) \
2077 [ ] * + ? and literal text",
2078 why, pat
2079 );
2080 }
2081 let hit = crate::nql::regex_match_pub(
2082 &as_text(&l), &pat, op.ends_with('*'));
2083 Value::Bool(hit != op.starts_with('!'))
2084 }
2085 }
2086
2087 "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => {
2088 if l.is_null() || r.is_null() {
2089 Value::Null
2090 } else {
2091 let hit = crate::nql::like_match_pub(
2092 &as_text(&l), &as_text(&r), op.ends_with("ILIKE"));
2093 Value::Bool(hit != op.starts_with("NOT"))
2094 }
2095 }
2096
2097 "||" => {
2099 if l.is_null() || r.is_null() {
2100 Value::Null
2101 } else {
2102 Value::String(format!("{}{}", as_text(&l), as_text(&r)))
2103 }
2104 }
2105
2106 "+" | "-" | "*" | "/" | "%" => match (num(&l), num(&r)) {
2107 (Some(a), Some(b)) => match op {
2108 "+" => from_f64(a + b),
2109 "-" => from_f64(a - b),
2110 "*" => from_f64(a * b),
2111 "/" if b == 0.0 => bail!("division by zero"),
2114 "/" => from_f64(a / b),
2115 "%" if b == 0.0 => bail!("division by zero"),
2116 "%" => from_f64(a % b),
2117 _ => unreachable!(),
2118 },
2119 _ => Value::Null,
2120 },
2121
2122 other => bail!("unsupported operator {:?}", other),
2123 })
2124}
2125
2126fn eval_func(name: &str, args: &[Expr], row: &Bound) -> Result<Value> {
2132 let arg = |i: usize| -> Result<Value> {
2135 match args.get(i) {
2136 Some(e) => eval(e, row),
2137 None => Ok(Value::Null),
2138 }
2139 };
2140
2141 Ok(match name {
2142 "pg_get_userbyid" | "current_user" | "session_user" | "user" => {
2146 Value::String("nedb".into())
2147 }
2148 "current_schema" => Value::String("public".into()),
2149 "current_database" | "current_catalog" => Value::String("nedb".into()),
2150 "version" => Value::String(crate::pgwire::version_string()),
2151
2152 "pg_table_is_visible" | "pg_type_is_visible" | "pg_function_is_visible"
2157 | "pg_opclass_is_visible" | "pg_conversion_is_visible" => Value::Bool(true),
2158
2159 "pg_encoding_to_char" => Value::String("UTF8".into()),
2161 "pg_get_expr" | "pg_get_indexdef" | "pg_get_constraintdef"
2162 | "pg_get_viewdef" | "pg_get_partkeydef" | "obj_description"
2163 | "col_description" | "shobj_description" => Value::Null,
2164
2165 "lower" => match arg(0)? {
2167 Value::Null => Value::Null,
2168 v => Value::String(as_text(&v).to_lowercase()),
2169 },
2170 "upper" => match arg(0)? {
2171 Value::Null => Value::Null,
2172 v => Value::String(as_text(&v).to_uppercase()),
2173 },
2174 "length" | "char_length" | "character_length" => match arg(0)? {
2175 Value::Null => Value::Null,
2176 v => from_f64(as_text(&v).chars().count() as f64),
2177 },
2178 "format_type" => match arg(0)? {
2179 Value::Null => Value::Null,
2180 v => Value::String(crate::pgcatalog::type_name_pub(
2181 num(&v).unwrap_or(25.0) as i32).to_string()),
2182 },
2183 "array_to_string" | "pg_catalog.array_to_string" => {
2184 match arg(0)? {
2188 Value::Array(items) => {
2189 let sep = as_text(&arg(1)?);
2190 Value::String(
2191 items.iter().map(as_text).collect::<Vec<_>>().join(&sep),
2192 )
2193 }
2194 _ => Value::Null,
2195 }
2196 }
2197 "quote_ident" => Value::String(as_text(&arg(0)?)),
2198 "quote_literal" => Value::String(format!("'{}'", as_text(&arg(0)?).replace('\'', "''"))),
2199 "format" => {
2203 let fmt = as_text(&arg(0)?);
2204 let mut out = String::new();
2205 let mut next = 1usize;
2206 let mut chars = fmt.chars().peekable();
2207 while let Some(c) = chars.next() {
2208 if c != '%' {
2209 out.push(c);
2210 continue;
2211 }
2212 match chars.next() {
2213 Some('%') => out.push('%'),
2214 Some(spec @ ('s' | 'I' | 'L')) => {
2215 let v = arg(next)?;
2216 next += 1;
2217 match (spec, &v) {
2218 ('L', Value::Null) => out.push_str("NULL"),
2219 ('L', v) => out.push_str(&format!("'{}'", as_text(v).replace('\'', "''"))),
2220 (_, v) => out.push_str(&as_text(v)),
2221 }
2222 }
2223 other => bail!("format(): unsupported conversion %{}", other.map(String::from).unwrap_or_default()),
2224 }
2225 }
2226 Value::String(out)
2227 }
2228
2229 "array_length" | "array_upper" | "cardinality" => match arg(0)? {
2233 Value::Array(items) if !items.is_empty() => from_f64(items.len() as f64),
2234 Value::Array(_) if name == "cardinality" => from_f64(0.0),
2235 _ => Value::Null,
2236 },
2237 "array_lower" => match arg(0)? {
2238 Value::Array(items) if !items.is_empty() => from_f64(1.0),
2239 _ => Value::Null,
2240 },
2241
2242 "pg_table_size" | "pg_total_relation_size" | "pg_relation_size"
2249 | "pg_indexes_size" | "pg_database_size" => Value::Null,
2250 "pg_size_pretty" => match num(&arg(0)?) {
2251 None => Value::Null,
2252 Some(n) => {
2253 let units = ["bytes", "kB", "MB", "GB", "TB", "PB"];
2254 let mut v = n;
2255 let mut u = 0usize;
2256 while v.abs() >= 10240.0 && u + 1 < units.len() {
2257 v /= 1024.0;
2258 u += 1;
2259 }
2260 Value::String(format!("{} {}", v.round() as i64, units[u]))
2261 }
2262 },
2263
2264 "pg_get_triggerdef" | "pg_get_ruledef" | "pg_get_statisticsobjdef"
2270 | "pg_get_statisticsobjdef_columns" | "pg_get_function_result"
2271 | "pg_get_function_arguments" | "pg_get_function_identity_arguments"
2272 | "pg_get_functiondef" | "pg_get_serial_sequence" | "pg_get_partition_constraintdef"
2273 | "pg_relation_filepath" | "pg_tablespace_location" => Value::Null,
2274 "pg_relation_is_publishable" => Value::Bool(true),
2275 "pg_statistics_obj_is_visible" | "pg_opfamily_is_visible" | "pg_collation_is_visible"
2276 | "pg_ts_config_is_visible" | "pg_ts_dict_is_visible" | "pg_ts_parser_is_visible"
2277 | "pg_ts_template_is_visible" | "has_table_privilege" | "has_schema_privilege"
2278 | "has_database_privilege" | "pg_has_role" => Value::Bool(true),
2279 "current_setting" => match arg(0)? {
2282 Value::Null => Value::Null,
2283 v => match as_text(&v).to_lowercase().as_str() {
2284 "server_version" => Value::String(crate::pgwire::version_string()),
2285 "server_encoding" | "client_encoding" => Value::String("UTF8".into()),
2286 "standard_conforming_strings" | "integer_datetimes" | "is_superuser" => {
2287 Value::String("on".into())
2288 }
2289 "timezone" | "log_timezone" => Value::String("UTC".into()),
2290 "search_path" => Value::String("\"$user\", public".into()),
2291 "intervalstyle" => Value::String("postgres".into()),
2292 "datestyle" => Value::String("ISO, MDY".into()),
2293 "session_authorization" => Value::String("nedb".into()),
2294 "application_name" | "default_transaction_read_only" => Value::String(String::new()),
2295 "transaction_isolation" | "default_transaction_isolation" => {
2296 Value::String("read committed".into())
2297 }
2298 "max_identifier_length" => Value::String("63".into()),
2299 other => {
2300 if truthy(&arg(1)?) == Some(true) {
2303 Value::Null
2304 } else {
2305 bail!("unrecognized configuration parameter \"{}\"", other)
2306 }
2307 }
2308 },
2309 },
2310 "pg_backend_pid" => from_f64(std::process::id() as f64),
2311 "pg_is_in_recovery" => Value::Bool(false),
2312 "txid_current" => from_f64(0.0),
2313 "now" | "current_timestamp" | "statement_timestamp" | "clock_timestamp" => {
2314 Value::String(now_iso())
2315 }
2316 "to_char" => match arg(0)? {
2317 Value::Null => Value::Null,
2318 v => Value::String(as_text(&v)),
2319 },
2320 "generate_series" | "unnest" => bail!(
2321 "{}() returns a set of rows — write it in FROM, not in the select list", name
2322 ),
2323
2324 "coalesce" => {
2326 let mut out = Value::Null;
2327 for a in args {
2328 let v = eval(a, row)?;
2329 if !v.is_null() {
2330 out = v;
2331 break;
2332 }
2333 }
2334 out
2335 }
2336 "nullif" => {
2337 let a = arg(0)?;
2338 let b = arg(1)?;
2339 if matches!(cmp_values(&a, &b), Some(std::cmp::Ordering::Equal)) {
2340 Value::Null
2341 } else {
2342 a
2343 }
2344 }
2345
2346 "int4" | "int8" | "int2" => match num(&arg(0)?) {
2348 Some(n) => from_f64(n.trunc()),
2349 None => Value::Null,
2350 },
2351 "text" => match arg(0)? {
2352 Value::Null => Value::Null,
2353 v => Value::String(as_text(&v)),
2354 },
2355
2356 other if is_aggregate(other) => bail!(
2357 "{}() is an aggregate, which is only meaningful over a whole result set — \
2358 it is evaluated by the executor, never per row",
2359 other
2360 ),
2361
2362 other => bail!(
2363 "the function {}() is not implemented. It is refused rather than \
2364 answered with NULL, because a NULL column reads as missing DATA \
2365 rather than a missing feature",
2366 other
2367 ),
2368 })
2369}
2370
2371fn now_iso() -> String {
2373 let secs = std::time::SystemTime::now()
2374 .duration_since(std::time::UNIX_EPOCH)
2375 .map(|d| d.as_secs())
2376 .unwrap_or(0);
2377 let days = (secs / 86_400) as i64;
2379 let rem = secs % 86_400;
2380 let z = days + 719_468;
2381 let era = z.div_euclid(146_097);
2382 let doe = z.rem_euclid(146_097);
2383 let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
2384 let y = yoe + era * 400;
2385 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2386 let mp = (5 * doy + 2) / 153;
2387 let d = doy - (153 * mp + 2) / 5 + 1;
2388 let m = if mp < 10 { mp + 3 } else { mp - 9 };
2389 let y = if m <= 2 { y + 1 } else { y };
2390 format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}+00", y, m, d, rem / 3600, (rem % 3600) / 60, rem % 60)
2391}
2392
2393const AGGREGATES: &[&str] = &[
2398 "count", "sum", "avg", "min", "max", "string_agg", "array_agg", "bool_and",
2399 "bool_or", "every",
2400];
2401
2402fn is_aggregate(name: &str) -> bool {
2403 AGGREGATES.iter().any(|a| a.eq_ignore_ascii_case(name))
2404}
2405
2406pub fn has_aggregate(e: &Expr) -> bool {
2409 match e {
2410 Expr::Func { name, args } => is_aggregate(name) || args.iter().any(has_aggregate),
2411 Expr::Binary { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2412 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2413 has_aggregate(expr)
2414 }
2415 Expr::InList { expr, list, .. } => has_aggregate(expr) || list.iter().any(has_aggregate),
2416 Expr::Case { operand, whens, else_ } => {
2417 operand.as_deref().is_some_and(has_aggregate)
2418 || whens.iter().any(|(c, t)| has_aggregate(c) || has_aggregate(t))
2419 || else_.as_deref().is_some_and(has_aggregate)
2420 }
2421 Expr::Quantified { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2422 Expr::Index { expr, index } => has_aggregate(expr) || has_aggregate(index),
2423 Expr::ArrayLit(items) => items.iter().any(has_aggregate),
2424 Expr::InSubquery { expr, .. } => has_aggregate(expr),
2425 Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => false,
2426 Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => false,
2427 }
2428}
2429
2430fn aggregate(name: &str, args: &[Expr], rows: &[JoinedRow], ctx: EvalCtx) -> Result<Value> {
2432 let lname = name.to_lowercase();
2433 if lname == "count" && (args.is_empty() || matches!(args[0], Expr::Star)) {
2436 return Ok(from_f64(rows.len() as f64));
2437 }
2438 let Some(target) = args.first() else {
2439 bail!("{}() needs an argument", name);
2440 };
2441 let mut vals: Vec<Value> = Vec::with_capacity(rows.len());
2442 let mut all_vals: Vec<Value> = Vec::with_capacity(rows.len());
2443 for r in rows {
2444 let v = eval(target, &bind(r, ctx))?;
2445 if !v.is_null() {
2446 vals.push(v.clone());
2447 }
2448 all_vals.push(v);
2449 }
2450 Ok(match lname.as_str() {
2451 "count" => from_f64(vals.len() as f64),
2452 "sum" | "avg" => {
2453 let nums: Vec<f64> = vals.iter().filter_map(num).collect();
2454 if nums.is_empty() {
2455 Value::Null
2456 } else if lname == "sum" {
2457 from_f64(nums.iter().sum())
2458 } else {
2459 from_f64(nums.iter().sum::<f64>() / nums.len() as f64)
2460 }
2461 }
2462 "min" | "max" => {
2463 let mut best: Option<Value> = None;
2464 for v in vals {
2465 best = Some(match best {
2466 None => v,
2467 Some(b) => {
2468 let take = match cmp_values(&v, &b) {
2469 Some(o) if lname == "min" => o.is_lt(),
2470 Some(o) => o.is_gt(),
2471 None => false,
2472 };
2473 if take { v } else { b }
2474 }
2475 });
2476 }
2477 best.unwrap_or(Value::Null)
2478 }
2479 "string_agg" => {
2480 if vals.is_empty() {
2481 Value::Null
2482 } else {
2483 let sep = match args.get(1) {
2486 Some(e) => as_text(&eval(e, &Bound { parts: vec![], ctx })?),
2487 None => String::new(),
2488 };
2489 Value::String(vals.iter().map(as_text).collect::<Vec<_>>().join(&sep))
2490 }
2491 }
2492 "array_agg" => {
2494 if all_vals.is_empty() { Value::Null } else { Value::Array(all_vals) }
2495 }
2496 "bool_and" | "every" => {
2497 if vals.is_empty() {
2498 Value::Null
2499 } else {
2500 Value::Bool(vals.iter().all(|v| truthy(v) == Some(true)))
2501 }
2502 }
2503 "bool_or" => {
2504 if vals.is_empty() {
2505 Value::Null
2506 } else {
2507 Value::Bool(vals.iter().any(|v| truthy(v) == Some(true)))
2508 }
2509 }
2510 _ => unreachable!("is_aggregate gates this"),
2511 })
2512}
2513
2514fn fold_aggregates(e: &Expr, rows: &[JoinedRow], ctx: EvalCtx) -> Result<Expr> {
2521 Ok(match e {
2522 Expr::Func { name, args } if is_aggregate(name) => {
2523 Expr::Literal(aggregate(name, args, rows, ctx)?)
2524 }
2525 Expr::Func { name, args } => Expr::Func {
2526 name: name.clone(),
2527 args: args.iter().map(|a| fold_aggregates(a, rows, ctx)).collect::<Result<_>>()?,
2528 },
2529 Expr::Column { qual, name } => bail!(
2530 "column \"{}{}\" must appear in the GROUP BY clause or be used in an \
2531 aggregate function",
2532 qual.as_ref().map(|q| format!("{q}.")).unwrap_or_default(),
2533 name
2534 ),
2535 Expr::Binary { op, left, right } => Expr::Binary {
2536 op: op.clone(),
2537 left: Box::new(fold_aggregates(left, rows, ctx)?),
2538 right: Box::new(fold_aggregates(right, rows, ctx)?),
2539 },
2540 Expr::Unary { op, expr } => Expr::Unary {
2541 op: op.clone(),
2542 expr: Box::new(fold_aggregates(expr, rows, ctx)?),
2543 },
2544 Expr::Cast { expr, ty } => Expr::Cast {
2545 expr: Box::new(fold_aggregates(expr, rows, ctx)?),
2546 ty: ty.clone(),
2547 },
2548 Expr::IsNull { expr, negated } => Expr::IsNull {
2549 expr: Box::new(fold_aggregates(expr, rows, ctx)?),
2550 negated: *negated,
2551 },
2552 Expr::InList { expr, list, negated } => Expr::InList {
2553 expr: Box::new(fold_aggregates(expr, rows, ctx)?),
2554 list: list.iter().map(|i| fold_aggregates(i, rows, ctx)).collect::<Result<_>>()?,
2555 negated: *negated,
2556 },
2557 Expr::Case { operand, whens, else_ } => Expr::Case {
2558 operand: match operand {
2559 Some(o) => Some(Box::new(fold_aggregates(o, rows, ctx)?)),
2560 None => None,
2561 },
2562 whens: whens
2563 .iter()
2564 .map(|(c, t)| Ok((fold_aggregates(c, rows, ctx)?, fold_aggregates(t, rows, ctx)?)))
2565 .collect::<Result<_>>()?,
2566 else_: match else_ {
2567 Some(x) => Some(Box::new(fold_aggregates(x, rows, ctx)?)),
2568 None => None,
2569 },
2570 },
2571 Expr::Quantified { op, left, all, right } => Expr::Quantified {
2572 op: op.clone(),
2573 left: Box::new(fold_aggregates(left, rows, ctx)?),
2574 all: *all,
2575 right: Box::new(fold_aggregates(right, rows, ctx)?),
2576 },
2577 Expr::Index { expr, index } => Expr::Index {
2578 expr: Box::new(fold_aggregates(expr, rows, ctx)?),
2579 index: Box::new(fold_aggregates(index, rows, ctx)?),
2580 },
2581 Expr::ArrayLit(items) => Expr::ArrayLit(
2582 items.iter().map(|i| fold_aggregates(i, rows, ctx)).collect::<Result<_>>()?,
2583 ),
2584 Expr::InSubquery { expr, query, negated } => Expr::InSubquery {
2585 expr: Box::new(fold_aggregates(expr, rows, ctx)?),
2586 query: query.clone(),
2587 negated: *negated,
2588 },
2589 Expr::Star | Expr::QualifiedStar(_) => {
2590 bail!("`*` cannot be mixed with an aggregate outside count(*)")
2591 }
2592 Expr::Literal(_) | Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => {
2594 e.clone()
2595 }
2596 })
2597}
2598
2599fn validate_bindings(sel: &Select) -> Result<()> {
2615 let mut seen: Vec<String> = vec![];
2616 if let Some(f) = &sel.from {
2617 seen.push(f.binding());
2618 }
2619 for j in &sel.joins {
2620 seen.push(j.table.binding());
2621 }
2622 for (i, b) in seen.iter().enumerate() {
2623 if let Some(prev) = seen[..i].iter().find(|p| p.eq_ignore_ascii_case(b)) {
2624 bail!(
2625 "ambiguous relation binding: {:?} appears more than once; use \
2626 aliases (for example `FROM {} JOIN {} AS {}2 ...`)",
2627 prev, prev, prev, prev
2628 );
2629 }
2630 }
2631 Ok(())
2632}
2633
2634#[derive(Debug, Clone, PartialEq, Eq)]
2646pub struct OutCol {
2647 pub key: String,
2648 pub name: String,
2649}
2650
2651fn unique_key(taken: &[OutCol], name: &str) -> String {
2655 if !taken.iter().any(|c| c.key == name) {
2656 return name.to_string();
2657 }
2658 format!("{name}\u{1}{}", taken.len())
2659}
2660
2661type JoinedRow = Vec<(String, Option<Value>)>;
2663
2664fn bind<'a>(row: &'a JoinedRow, ctx: EvalCtx<'a>) -> Bound<'a> {
2665 Bound {
2666 parts: row.iter().map(|(b, v)| (b.clone(), v.as_ref())).collect(),
2667 ctx,
2668 }
2669}
2670
2671fn derived_name(e: &Expr) -> String {
2679 match e {
2680 Expr::Column { name, .. } => name.clone(),
2681 Expr::Func { name, .. } => name.clone(),
2682 Expr::Cast { expr, .. } => derived_name(expr),
2683 Expr::Case { .. } => "case".to_string(),
2684 Expr::ArrayQuery(_) | Expr::ArrayLit(_) => "array".to_string(),
2685 Expr::Exists { .. } => "exists".to_string(),
2686 Expr::Subquery(q) => q
2688 .items
2689 .first()
2690 .map(|i| i.alias.clone().unwrap_or_else(|| derived_name(&i.expr)))
2691 .unwrap_or_else(|| "?column?".to_string()),
2692 _ => "?column?".to_string(),
2693 }
2694}
2695
2696pub trait Relation {
2717 fn next_row(&mut self) -> Result<Option<Value>>;
2719
2720 fn size_hint(&self) -> Option<usize> {
2722 None
2723 }
2724}
2725
2726pub struct VecRelation {
2732 iter: std::vec::IntoIter<Value>,
2733 len: usize,
2734}
2735
2736impl Relation for VecRelation {
2737 fn next_row(&mut self) -> Result<Option<Value>> {
2738 Ok(self.iter.next())
2739 }
2740 fn size_hint(&self) -> Option<usize> {
2741 Some(self.len)
2742 }
2743}
2744
2745pub fn from_vec(rows: Vec<Value>) -> Box<dyn Relation> {
2747 let len = rows.len();
2748 Box::new(VecRelation { iter: rows.into_iter(), len })
2749}
2750
2751pub type Resolver<'r> = dyn Fn(&str) -> Result<Option<Box<dyn Relation>>> + 'r;
2757
2758pub fn execute(sel: &Select, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
2763 let (cols, rows, _) = execute_explain(sel, resolve, JoinExec::Auto)?;
2764 Ok((cols, rows))
2765}
2766
2767pub fn execute_explain(
2774 sel: &Select,
2775 resolve: &Resolver,
2776 exec: JoinExec,
2777) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
2778 execute_with(sel, resolve, exec, true)
2779}
2780
2781#[derive(Debug, Clone, Copy)]
2786pub struct Opts {
2787 pub exec: JoinExec,
2788 pub pushdown: bool,
2789 pub fuse_filter: bool,
2793}
2794
2795impl Default for Opts {
2796 fn default() -> Self {
2797 Opts { exec: JoinExec::Auto, pushdown: true, fuse_filter: true }
2798 }
2799}
2800
2801impl Opts {
2802 pub fn exec(exec: JoinExec) -> Self {
2803 Opts { exec, ..Default::default() }
2804 }
2805}
2806
2807pub fn execute_with(
2814 sel: &Select,
2815 resolve: &Resolver,
2816 exec: JoinExec,
2817 pushdown: bool,
2818) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
2819 execute_opts(sel, resolve, Opts { exec, pushdown, ..Default::default() })
2820}
2821
2822pub fn execute_opts(
2824 sel: &Select,
2825 resolve: &Resolver,
2826 opts: Opts,
2827) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
2828 execute_inner(sel, resolve, opts, None)
2829}
2830
2831fn row_key(cols: &[OutCol], obj: &Map<String, Value>) -> String {
2833 cols.iter()
2834 .map(|c| format!("{:?}", obj.get(&c.key).unwrap_or(&Value::Null)))
2835 .collect::<Vec<_>>()
2836 .join("\u{1}")
2837}
2838
2839fn execute_set_ops<'a>(
2847 sel: &Select,
2848 resolve: &'a Resolver<'a>,
2849 opts: Opts,
2850 outer: Option<&'a Bound<'a>>,
2851) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
2852 let ctx = EvalCtx { resolver: Some(resolve), outer };
2853 let mut head = sel.clone();
2854 head.set_ops.clear();
2855 head.order_by.clear();
2856 head.limit = None;
2857 head.offset = None;
2858 let (cols, rows, mut plan) = execute_inner(&head, resolve, opts, outer)?;
2859 let mut left: Vec<Map<String, Value>> = rows
2860 .into_iter()
2861 .map(|r| match r {
2862 Value::Object(m) => m,
2863 _ => Map::new(),
2864 })
2865 .collect();
2866
2867 for arm in &sel.set_ops {
2868 let (acols, arows, _) = execute_inner(&arm.query, resolve, opts, outer)?;
2869 let op_name = match arm.op {
2870 SetOp::Union => "UNION",
2871 SetOp::Intersect => "INTERSECT",
2872 SetOp::Except => "EXCEPT",
2873 };
2874 if acols.len() != cols.len() {
2875 bail!(
2876 "each {} query must have the same number of columns: {} vs {}",
2877 op_name, cols.len(), acols.len()
2878 );
2879 }
2880 let right: Vec<Map<String, Value>> = arows
2882 .into_iter()
2883 .map(|r| {
2884 let m = match r {
2885 Value::Object(m) => m,
2886 _ => Map::new(),
2887 };
2888 let mut out = Map::new();
2889 for (i, c) in cols.iter().enumerate() {
2890 out.insert(c.key.clone(), m.get(&acols[i].key).cloned().unwrap_or(Value::Null));
2891 }
2892 out
2893 })
2894 .collect();
2895 let (nl, nr) = (left.len(), right.len());
2896 let right_keys: std::collections::HashSet<String> =
2897 right.iter().map(|m| row_key(&cols, m)).collect();
2898 let mut combined: Vec<Map<String, Value>> = match arm.op {
2899 SetOp::Union => {
2900 left.extend(right);
2901 left
2902 }
2903 SetOp::Intersect => left.into_iter().filter(|m| right_keys.contains(&row_key(&cols, m))).collect(),
2904 SetOp::Except => left.into_iter().filter(|m| !right_keys.contains(&row_key(&cols, m))).collect(),
2905 };
2906 if !arm.all {
2907 let mut seen = std::collections::HashSet::new();
2908 combined.retain(|m| seen.insert(row_key(&cols, m)));
2909 }
2910 plan.notes.push(format!(
2911 "{}{}: {} + {} rows -> {} (each arm planned separately; only the first arm's plan is shown)",
2912 op_name, if arm.all { " ALL" } else { "" }, nl, nr, combined.len()
2913 ));
2914 left = combined;
2915 }
2916
2917 let projected: Vec<(Map<String, Value>, JoinedRow)> = left
2920 .into_iter()
2921 .map(|m| {
2922 let src: JoinedRow = vec![(String::new(), Some(Value::Object(m.clone())))];
2923 (m, src)
2924 })
2925 .collect();
2926 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
2927 Ok((cols, out, plan))
2928}
2929
2930fn execute_inner<'a>(
2933 sel: &Select,
2934 resolve: &'a Resolver<'a>,
2935 opts: Opts,
2936 outer: Option<&'a Bound<'a>>,
2937) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
2938 if !sel.set_ops.is_empty() {
2939 return execute_set_ops(sel, resolve, opts, outer);
2940 }
2941 let ctx = EvalCtx { resolver: Some(resolve), outer };
2942 let exec = opts.exec;
2943 let pushdown = opts.pushdown;
2944 let mut plan = Plan::default();
2945
2946 validate_bindings(sel)?;
2948
2949 let fuse = opts.fuse_filter && sel.where_.is_some() && !sel.joins.is_empty();
2976
2977 let budget: Option<usize> = match sel.limit {
2978 Some(lim)
2979 if sel.order_by.is_empty()
2980 && !sel.distinct
2981 && !sel.joins.is_empty()
2982 && (sel.where_.is_none() || fuse) =>
2983 {
2984 Some(lim.saturating_add(sel.offset.unwrap_or(0)))
2985 }
2986 _ => None,
2987 };
2988 plan.budget = budget;
2989
2990 let all_bindings: Vec<String> = sel
2996 .from
2997 .iter()
2998 .map(|t| t.binding())
2999 .chain(sel.joins.iter().map(|j| j.table.binding()))
3000 .collect();
3001 let nullable = crate::sqlpush::nullable_bindings(sel);
3002 let push = if pushdown {
3003 crate::sqlpush::plan(sel.where_.as_ref(), &all_bindings, &nullable)
3004 } else {
3005 Pushdown::default()
3006 };
3007 plan.refusals = push.refusals.clone();
3008
3009 let mut base_scan_at: Option<usize> = None;
3010 let mut base_prefilter_at: Option<usize> = None;
3011
3012 let mut left_src: Box<dyn LeftSource + 'a> = match &sel.from {
3020 None => {
3021 Box::new(VecLeft { rows: vec![vec![]], at: 0 })
3024 }
3025 Some(t) => {
3026 let rel = fetch(t, resolve, ctx)?;
3027 let binding = t.binding();
3028 base_scan_at = Some(plan.stages.len());
3033 plan.push(Stage::Scan {
3034 table: t.name.clone(),
3035 binding: binding.clone(),
3036 rows: 0,
3037 });
3038 let preds = push.for_binding(&binding).cloned().unwrap_or_default();
3039 if !preds.is_empty() {
3040 base_prefilter_at = Some(plan.stages.len());
3041 plan.push(Stage::Prefilter {
3042 binding: binding.clone(),
3043 predicates: preds.len(),
3044 in_rows: 0,
3045 out_rows: 0,
3046 });
3047 }
3048 Box::new(StreamLeft { rel, binding, preds, pulled: 0, kept: 0, ctx })
3049 }
3050 };
3051
3052 let mut left_bindings: Vec<String> = match &sel.from {
3058 None => vec![],
3059 Some(t) => vec![t.binding()],
3060 };
3061 let last = sel.joins.len().saturating_sub(1);
3062 let mut rows: Vec<JoinedRow> = vec![];
3063 let mut base_pulled: Option<usize> = None;
3064 let mut base_kept: Option<usize> = None;
3065
3066 for (ji, join) in sel.joins.iter().enumerate() {
3067 let is_last = ji == last;
3068 let rb = join.table.binding();
3069
3070 if join.table.lateral {
3074 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3075 let join_budget = if is_last { budget } else { None };
3076 let (out, removed, consumed, produced) = join_lateral(
3077 left_src.as_mut(), join, resolve, join_budget, post, ctx,
3078 )?;
3079 plan.push(Stage::Scan { table: join.table.name.clone(), binding: rb.clone(), rows: produced });
3080 plan.push(Stage::Join {
3081 kind: join.kind,
3082 table: join.table.name.clone(),
3083 binding: rb.clone(),
3084 strategy: Strategy::NestedLoop,
3085 keys: 0,
3086 left_rows: consumed,
3087 right_rows: produced,
3088 out_rows: out.len(),
3089 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3090 post_filter_removed: post.map(|_| removed),
3091 });
3092 plan.notes.push(format!("LATERAL {}: the subquery ran once per left row ({} times)", rb, consumed));
3093 left_bindings.push(rb);
3094 if ji == 0 {
3095 if let Some((pulled, kept)) = left_src.stats() {
3096 base_pulled = Some(pulled);
3097 base_kept = Some(kept);
3098 }
3099 }
3100 left_src = Box::new(VecLeft { rows: out, at: 0 });
3101 continue;
3102 }
3103
3104 let right_rel = fetch(&join.table, resolve, ctx)?;
3105 let right_all = drain(right_rel)?;
3106 plan.push(Stage::Scan {
3107 table: join.table.name.clone(),
3108 binding: rb.clone(),
3109 rows: right_all.len(),
3110 });
3111 let right_rows = prefilter(right_all, &rb, &push, &mut plan, ctx)?;
3112
3113 let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3118 let join_budget = if is_last { budget } else { None };
3119
3120 let keys = sqljoin::hash_keys(join.on.as_ref(), &left_bindings, &rb);
3123 let left_hint = left_src.hint().unwrap_or(usize::MAX);
3124 let strategy = sqljoin::choose(exec, keys.len(), left_hint, right_rows.len());
3125
3126 let (out, removed, consumed) = match strategy {
3127 Strategy::NestedLoop => join_nested_loop(
3128 left_src.as_mut(), &left_bindings, join, &right_rows, &rb,
3129 join_budget, post, ctx,
3130 )?,
3131 Strategy::Hash => join_hash(
3132 left_src.as_mut(), &left_bindings, join, &right_rows, &rb, &keys,
3133 join_budget, post, ctx,
3134 )?,
3135 };
3136
3137 plan.push(Stage::Join {
3138 kind: join.kind,
3139 table: join.table.name.clone(),
3140 binding: rb.clone(),
3141 strategy,
3142 keys: keys.len(),
3143 left_rows: consumed,
3144 right_rows: right_rows.len(),
3145 out_rows: out.len(),
3146 early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3147 post_filter_removed: post.map(|_| removed),
3148 });
3149 left_bindings.push(rb);
3150 if ji == 0 {
3152 if let Some((pulled, kept)) = left_src.stats() {
3153 base_pulled = Some(pulled);
3154 base_kept = Some(kept);
3155 }
3156 }
3157 rows = out;
3158 left_src = Box::new(VecLeft { rows: std::mem::take(&mut rows), at: 0 });
3160 }
3161
3162 rows = left_src.take_rows();
3165 if let Some(i) = base_scan_at {
3166 if let (Some(pulled), Some(kept)) = (base_pulled, base_kept) {
3167 if let Some(Stage::Scan { rows: r, .. }) = plan.stages.get_mut(i) {
3168 *r = pulled;
3169 }
3170 if let Some(j) = base_prefilter_at {
3171 if let Some(Stage::Prefilter { in_rows, out_rows, .. }) =
3172 plan.stages.get_mut(j)
3173 {
3174 *in_rows = pulled;
3175 *out_rows = kept;
3176 }
3177 }
3178 }
3179 }
3180
3181 if let Some(pred) = sel.where_.as_ref().filter(|_| !fuse) {
3183 let in_rows = rows.len();
3184 let mut kept = Vec::with_capacity(rows.len());
3185 for r in rows {
3186 if truthy(&eval(pred, &bind(&r, ctx))?) == Some(true) {
3190 kept.push(r);
3191 }
3192 }
3193 rows = kept;
3194 plan.push(Stage::Filter { in_rows, out_rows: rows.len() });
3195 }
3196
3197 if sel.items.iter().any(|i| has_aggregate(&i.expr)) {
3204 let mut cols: Vec<OutCol> = vec![];
3205 let mut obj = Map::new();
3206 let empty: JoinedRow = vec![];
3207 for item in &sel.items {
3208 let folded = fold_aggregates(&item.expr, &rows, ctx)?;
3209 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3210 let key = unique_key(&cols, &name);
3211 let v = eval(&folded, &bind(&empty, ctx))?;
3212 obj.insert(key.clone(), v);
3213 cols.push(OutCol { key, name });
3214 }
3215 plan.notes.push(format!("Aggregate over {} rows -> 1 row", rows.len()));
3216 plan.push(Stage::Project { columns: cols.len(), out_rows: 1 });
3217 let projected = vec![(obj, empty)];
3218 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3219 return Ok((cols, out, plan));
3220 }
3221
3222 let mut cols: Vec<OutCol> = vec![];
3233 let mut spans: Vec<(usize, usize)> = Vec::with_capacity(sel.items.len());
3234 for item in &sel.items {
3235 let start = cols.len();
3236 match &item.expr {
3237 Expr::Star => {
3238 if let Some(first) = rows.first() {
3239 for (n, _) in bind(first, ctx).flatten() {
3240 if !cols.iter().any(|c| c.name == n) {
3242 cols.push(OutCol { key: n.clone(), name: n });
3243 }
3244 }
3245 }
3246 }
3247 Expr::QualifiedStar(q) => {
3248 if let Some(first) = rows.first() {
3249 for (n, _) in bind(first, ctx).flatten_binding(q) {
3250 if !cols.iter().any(|c| c.name == n) {
3251 cols.push(OutCol { key: n.clone(), name: n });
3252 }
3253 }
3254 }
3255 }
3256 _ => {
3257 let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3258 let key = unique_key(&cols, &name);
3264 cols.push(OutCol { key, name });
3265 }
3266 }
3267 spans.push((start, cols.len()));
3268 }
3269
3270 let mut projected: Vec<(Map<String, Value>, JoinedRow)> = Vec::with_capacity(rows.len());
3274 for r in rows {
3275 let b = bind(&r, ctx);
3276 let mut obj = Map::new();
3277 for (i, item) in sel.items.iter().enumerate() {
3278 let (start, end) = spans[i];
3279 match &item.expr {
3280 Expr::Star => {
3281 for (n, v) in b.flatten() {
3282 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3283 obj.entry(c.key.clone()).or_insert(v);
3284 }
3285 }
3286 }
3287 Expr::QualifiedStar(q) => {
3288 for (n, v) in b.flatten_binding(q) {
3289 if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3290 obj.entry(c.key.clone()).or_insert(v);
3291 }
3292 }
3293 }
3294 _ => {
3295 let v = eval(&item.expr, &b)?;
3296 if let Some(c) = cols.get(start) {
3297 obj.insert(c.key.clone(), v);
3298 }
3299 }
3300 }
3301 }
3302 projected.push((obj, r));
3303 }
3304
3305 plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3306
3307 if sel.distinct {
3309 let in_rows = projected.len();
3310 let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
3311 projected.retain(|(obj, _)| seen.insert(row_key(&cols, obj)));
3314 plan.push(Stage::Distinct { in_rows, out_rows: projected.len() });
3315 }
3316
3317 let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3318 Ok((cols, out, plan))
3319}
3320
3321fn finish(
3323 sel: &Select,
3324 cols: &[OutCol],
3325 mut projected: Vec<(Map<String, Value>, JoinedRow)>,
3326 ctx: EvalCtx,
3327 plan: &mut Plan,
3328) -> Result<Vec<Value>> {
3329 if !sel.order_by.is_empty() {
3331 let mut keyed: Vec<(Vec<Value>, (Map<String, Value>, JoinedRow))> = vec![];
3335 for (obj, src) in projected {
3336 let mut key = vec![];
3337 for ob in &sel.order_by {
3338 let v = match (ob.ordinal, &ob.expr) {
3339 (Some(n), _) => {
3340 let c = cols.get(n - 1).ok_or_else(|| {
3341 anyhow::anyhow!(
3342 "ORDER BY {} is out of range: the select list has {} \
3343 column(s)", n, cols.len())
3344 })?;
3345 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3346 }
3347 (None, Some(Expr::Column { qual: None, name }))
3352 if cols.iter().any(|c| c.name == *name) =>
3353 {
3354 let c = cols.iter().find(|c| c.name == *name).expect("checked");
3355 obj.get(&c.key).cloned().unwrap_or(Value::Null)
3356 }
3357 (None, Some(e)) => {
3358 eval(e, &bind(&src, ctx))?
3362 }
3363 (None, None) => Value::Null,
3364 };
3365 key.push(v);
3366 }
3367 keyed.push((key, (obj, src)));
3368 }
3369
3370 keyed.sort_by(|a, b| {
3371 for (i, ob) in sel.order_by.iter().enumerate() {
3372 let (x, y) = (&a.0[i], &b.0[i]);
3373 let ord = match (x.is_null(), y.is_null()) {
3374 (true, true) => std::cmp::Ordering::Equal,
3375 (true, false) => {
3379 return if ob.nulls_first {
3380 std::cmp::Ordering::Less
3381 } else {
3382 std::cmp::Ordering::Greater
3383 }
3384 }
3385 (false, true) => {
3386 return if ob.nulls_first {
3387 std::cmp::Ordering::Greater
3388 } else {
3389 std::cmp::Ordering::Less
3390 }
3391 }
3392 (false, false) => cmp_values(x, y).unwrap_or(std::cmp::Ordering::Equal),
3393 };
3394 let ord = if matches!(ob.dir, Dir::Desc) { ord.reverse() } else { ord };
3395 if !ord.is_eq() {
3396 return ord;
3397 }
3398 }
3399 std::cmp::Ordering::Equal
3400 });
3401
3402 projected = keyed.into_iter().map(|(_, row)| row).collect();
3403 plan.push(Stage::Sort { keys: sel.order_by.len(), rows: projected.len() });
3404 }
3405
3406 let mut out: Vec<Value> = projected
3408 .into_iter()
3409 .map(|(obj, _)| Value::Object(obj))
3410 .collect();
3411 let in_rows = out.len();
3412 if let Some(off) = sel.offset {
3413 out = if off >= out.len() { vec![] } else { out.split_off(off) };
3414 }
3415 if let Some(lim) = sel.limit {
3416 out.truncate(lim);
3417 }
3418 if sel.limit.is_some() || sel.offset.is_some() {
3419 plan.push(Stage::Limit {
3420 limit: sel.limit,
3421 offset: sel.offset,
3422 in_rows,
3423 out_rows: out.len(),
3424 });
3425 }
3426
3427 Ok(out)
3428}
3429
3430fn keep_row(cand: &JoinedRow, post: Option<&Expr>, removed: &mut usize, ctx: EvalCtx) -> Result<bool> {
3463 let Some(p) = post else { return Ok(true) };
3464 if truthy(&eval(p, &bind(cand, ctx))?) == Some(true) {
3466 Ok(true)
3467 } else {
3468 *removed += 1;
3469 Ok(false)
3470 }
3471}
3472
3473#[allow(clippy::too_many_arguments)]
3479fn emit_unmatched_right(
3480 out: &mut Vec<JoinedRow>,
3481 kind: JoinKind,
3482 left_bindings: &[String],
3483 right_rows: &[Value],
3484 right_matched: &[bool],
3485 rb: &str,
3486 post: Option<&Expr>,
3487 removed: &mut usize,
3488 ctx: EvalCtx,
3489) -> Result<()> {
3490 if !matches!(kind, JoinKind::Right | JoinKind::Full) {
3491 return Ok(());
3492 }
3493 for (ri, right) in right_rows.iter().enumerate() {
3494 if right_matched[ri] {
3495 continue;
3496 }
3497 let mut cand: JoinedRow = left_bindings.iter().map(|b| (b.clone(), None)).collect();
3498 cand.push((rb.to_string(), Some(right.clone())));
3499 if keep_row(&cand, post, removed, ctx)? {
3502 out.push(cand);
3503 }
3504 }
3505 Ok(())
3506}
3507
3508fn join_lateral(
3515 left_src: &mut dyn LeftSource,
3516 join: &Join,
3517 resolve: &Resolver,
3518 budget: Option<usize>,
3519 post: Option<&Expr>,
3520 ctx: EvalCtx,
3521) -> Result<(Vec<JoinedRow>, usize, usize, usize)> {
3522 let sub = join
3523 .table
3524 .sub
3525 .as_deref()
3526 .ok_or_else(|| anyhow::anyhow!("LATERAL requires a subquery"))?;
3527 let rb = join.table.binding();
3528 let mut out: Vec<JoinedRow> = vec![];
3529 let mut removed = 0usize;
3530 let mut consumed = 0usize;
3531 let mut produced = 0usize;
3532 while let Some(left) = {
3533 if budget.is_some_and(|b| out.len() >= b) { None } else { left_src.next_left()? }
3534 } {
3535 consumed += 1;
3536 let scope = bind(&left, ctx);
3537 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), Some(&scope))?;
3538 produced += rows.len();
3539 let mut matched = false;
3540 for r in rows {
3541 let m = match r {
3542 Value::Object(m) => m,
3543 _ => Map::new(),
3544 };
3545 let mut named = Map::new();
3546 for (i, c) in cols.iter().enumerate() {
3547 let name = join.table.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
3548 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
3549 }
3550 let mut cand = left.clone();
3551 cand.push((rb.clone(), Some(Value::Object(named))));
3552 let on_ok = match &join.on {
3553 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
3554 None => true,
3555 };
3556 if !on_ok {
3557 continue;
3558 }
3559 matched = true;
3560 if keep_row(&cand, post, &mut removed, ctx)? {
3561 out.push(cand);
3562 }
3563 }
3564 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
3565 let mut cand = left.clone();
3566 cand.push((rb.clone(), None));
3567 if keep_row(&cand, post, &mut removed, ctx)? {
3568 out.push(cand);
3569 }
3570 }
3571 }
3572 Ok((out, removed, consumed, produced))
3573}
3574
3575#[allow(clippy::too_many_arguments)]
3581fn join_nested_loop(
3582 left_src: &mut dyn LeftSource,
3583 left_bindings: &[String],
3584 join: &Join,
3585 right_rows: &[Value],
3586 rb: &str,
3587 budget: Option<usize>,
3588 post: Option<&Expr>,
3589 ctx: EvalCtx,
3590) -> Result<(Vec<JoinedRow>, usize, usize)> {
3591 let mut out: Vec<JoinedRow> = vec![];
3592 let mut removed = 0usize;
3593 let mut right_matched = vec![false; right_rows.len()];
3595
3596 let mut consumed = 0usize;
3597 while let Some(left) = {
3598 if budget.is_some_and(|b| out.len() >= b) {
3599 None
3602 } else {
3603 left_src.next_left()?
3604 }
3605 } {
3606 consumed += 1;
3607 let left = &left;
3608 let mut matched = false;
3611 for (ri, right) in right_rows.iter().enumerate() {
3612 let mut cand: JoinedRow = left.clone();
3613 cand.push((rb.to_string(), Some(right.clone())));
3614 let joins_here = match &join.on {
3615 None => true,
3617 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
3621 };
3622 if joins_here {
3623 matched = true;
3624 right_matched[ri] = true;
3625 if keep_row(&cand, post, &mut removed, ctx)? {
3626 out.push(cand);
3627 }
3628 }
3629 }
3630 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
3632 let mut cand: JoinedRow = left.clone();
3633 cand.push((rb.to_string(), None));
3634 if keep_row(&cand, post, &mut removed, ctx)? {
3635 out.push(cand);
3636 }
3637 }
3638 }
3639
3640 if !budget.is_some_and(|b| out.len() >= b) {
3645 emit_unmatched_right(
3646 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
3647 &mut removed, ctx,
3648 )?;
3649 }
3650 Ok((out, removed, consumed))
3651}
3652
3653#[allow(clippy::too_many_arguments)]
3661fn join_hash(
3662 left_src: &mut dyn LeftSource,
3663 left_bindings: &[String],
3664 join: &Join,
3665 right_rows: &[Value],
3666 rb: &str,
3667 keys: &[(Expr, Expr)],
3668 budget: Option<usize>,
3669 post: Option<&Expr>,
3670 ctx: EvalCtx,
3671) -> Result<(Vec<JoinedRow>, usize, usize)> {
3672 debug_assert!(!keys.is_empty(), "the planner must not choose Hash with no keys");
3673
3674 let side = sqljoin::HashSide::build(right_rows.len(), |i| {
3676 let one: JoinedRow = vec![(rb.to_string(), Some(right_rows[i].clone()))];
3679 let b = bind(&one, ctx);
3680 let mut k = Vec::with_capacity(keys.len());
3681 for (_, right_expr) in keys {
3682 match sqljoin::hkey(&eval(right_expr, &b)?) {
3683 Some(h) => k.push(h),
3684 None => return Ok(None),
3686 }
3687 }
3688 Ok(Some(k))
3689 })?;
3690
3691 let mut out: Vec<JoinedRow> = vec![];
3693 let mut removed = 0usize;
3694 let mut right_matched = vec![false; right_rows.len()];
3695
3696 let mut consumed = 0usize;
3697 while let Some(left) = {
3698 if budget.is_some_and(|b| out.len() >= b) {
3699 None
3700 } else {
3701 left_src.next_left()?
3702 }
3703 } {
3704 consumed += 1;
3705 let left = &left;
3706 let lb = bind(left, ctx);
3707 let mut lk = Vec::with_capacity(keys.len());
3708 let mut null_key = false;
3709 for (left_expr, _) in keys {
3710 match sqljoin::hkey(&eval(left_expr, &lb)?) {
3711 Some(h) => lk.push(h),
3712 None => {
3713 null_key = true;
3714 break;
3715 }
3716 }
3717 }
3718
3719 let mut matched = false;
3720 if !null_key {
3724 for &ri in side.probe(&lk) {
3725 let mut cand: JoinedRow = left.clone();
3726 cand.push((rb.to_string(), Some(right_rows[ri].clone())));
3727 let joins_here = match &join.on {
3729 None => true,
3730 Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
3731 };
3732 if joins_here {
3733 matched = true;
3734 right_matched[ri] = true;
3735 if keep_row(&cand, post, &mut removed, ctx)? {
3736 out.push(cand);
3737 }
3738 }
3739 }
3740 }
3741 if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
3742 let mut cand: JoinedRow = left.clone();
3743 cand.push((rb.to_string(), None));
3744 if keep_row(&cand, post, &mut removed, ctx)? {
3745 out.push(cand);
3746 }
3747 }
3748 }
3749
3750 if !budget.is_some_and(|b| out.len() >= b) {
3753 emit_unmatched_right(
3754 &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
3755 &mut removed, ctx,
3756 )?;
3757 }
3758 Ok((out, removed, consumed))
3759}
3760
3761fn prefilter(
3768 rows: Vec<Value>,
3769 binding: &str,
3770 push: &Pushdown,
3771 plan: &mut Plan,
3772 ctx: EvalCtx,
3773) -> Result<Vec<Value>> {
3774 let Some(preds) = push.for_binding(binding) else { return Ok(rows) };
3775 if preds.is_empty() {
3776 return Ok(rows);
3777 }
3778 let in_rows = rows.len();
3779 let mut kept = Vec::with_capacity(rows.len());
3780 for row in rows {
3781 let one: JoinedRow = vec![(binding.to_string(), Some(row))];
3782 let b = bind(&one, ctx);
3783 let mut keep = true;
3784 for p in preds {
3785 if truthy(&eval(p, &b)?) != Some(true) {
3791 keep = false;
3792 break;
3793 }
3794 }
3795 if keep {
3796 if let Some((_, Some(v))) = one.into_iter().next() {
3798 kept.push(v);
3799 }
3800 }
3801 }
3802 plan.push(Stage::Prefilter {
3803 binding: binding.to_string(),
3804 predicates: preds.len(),
3805 in_rows,
3806 out_rows: kept.len(),
3807 });
3808 Ok(kept)
3809}
3810
3811fn fetch(t: &TableRef, resolve: &Resolver, ctx: EvalCtx) -> Result<Box<dyn Relation>> {
3814 if let Some(sub) = &t.sub {
3818 let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), ctx.outer)?;
3819 let out = rows
3820 .into_iter()
3821 .map(|r| {
3822 let m = match r {
3823 Value::Object(m) => m,
3824 _ => Map::new(),
3825 };
3826 let mut named = Map::new();
3827 for (i, c) in cols.iter().enumerate() {
3828 let name = t.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
3829 named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
3832 }
3833 Value::Object(named)
3834 })
3835 .collect();
3836 return Ok(from_vec(out));
3837 }
3838
3839 if let Some(args) = &t.args {
3843 let empty: JoinedRow = vec![];
3844 let scope = bind(&empty, ctx);
3845 let col = |i: usize, default: &str| -> String {
3846 t.col_aliases.get(i).cloned().unwrap_or_else(|| default.to_string())
3847 };
3848 let rows: Vec<Value> = match t.name.as_str() {
3849 "generate_series" => {
3850 let a = num(&eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_series() needs a start"))?, &scope)?);
3851 let b = num(&eval(args.get(1).ok_or_else(|| anyhow::anyhow!("generate_series() needs a stop"))?, &scope)?);
3852 let step = match args.get(2) {
3853 Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
3854 None => 1.0,
3855 };
3856 match (a, b) {
3857 (Some(a), Some(b)) if step != 0.0 => {
3859 let mut out = vec![];
3860 let mut x = a;
3861 while (step > 0.0 && x <= b) || (step < 0.0 && x >= b) {
3862 let mut m = Map::new();
3863 m.insert(col(0, "generate_series"), from_f64(x));
3864 out.push(Value::Object(m));
3865 x += step;
3866 if out.len() > 1_000_000 {
3867 bail!("generate_series() would produce more than a million rows");
3868 }
3869 }
3870 out
3871 }
3872 (Some(_), Some(_)) => bail!("generate_series() step cannot equal zero"),
3873 _ => vec![],
3874 }
3875 }
3876 "unnest" => match eval(args.first().ok_or_else(|| anyhow::anyhow!("unnest() needs an array"))?, &scope)? {
3877 Value::Array(items) => items
3878 .into_iter()
3879 .map(|v| {
3880 let mut m = Map::new();
3881 m.insert(col(0, "unnest"), v);
3882 Value::Object(m)
3883 })
3884 .collect(),
3885 _ => vec![],
3887 },
3888 "pg_partition_tree" | "pg_partition_ancestors" => vec![],
3891 other => bail!(
3892 "the table function {}() is not implemented. It is refused rather \
3893 than answered with no rows, because an empty relation reads as \
3894 missing DATA rather than a missing feature",
3895 other
3896 ),
3897 };
3898 return Ok(from_vec(rows));
3899 }
3900
3901 match resolve(&t.name)? {
3902 Some(rel) => Ok(rel),
3903 None => bail!("relation {:?} does not exist", t.name),
3906 }
3907}
3908
3909trait LeftSource {
3916 fn next_left(&mut self) -> Result<Option<JoinedRow>>;
3917 fn hint(&self) -> Option<usize>;
3919 fn take_rows(&mut self) -> Vec<JoinedRow>;
3921 fn stats(&self) -> Option<(usize, usize)> {
3923 None
3924 }
3925}
3926
3927struct StreamLeft<'a> {
3933 rel: Box<dyn Relation>,
3934 binding: String,
3935 preds: Vec<Expr>,
3936 ctx: EvalCtx<'a>,
3937 pulled: usize,
3941 kept: usize,
3942}
3943
3944impl<'a> LeftSource for StreamLeft<'a> {
3945 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
3946 while let Some(row) = self.rel.next_row()? {
3947 self.pulled += 1;
3948 let one: JoinedRow = vec![(self.binding.clone(), Some(row))];
3949 if !self.preds.is_empty() {
3950 let b = bind(&one, self.ctx);
3951 let mut keep = true;
3952 for p in &self.preds {
3953 if truthy(&eval(p, &b)?) != Some(true) {
3954 keep = false;
3955 break;
3956 }
3957 }
3958 if !keep {
3959 continue;
3960 }
3961 }
3962 self.kept += 1;
3963 return Ok(Some(one));
3964 }
3965 Ok(None)
3966 }
3967 fn hint(&self) -> Option<usize> {
3968 self.rel.size_hint()
3973 }
3974 fn take_rows(&mut self) -> Vec<JoinedRow> {
3975 let mut out = vec![];
3978 while let Ok(Some(r)) = self.next_left() {
3979 out.push(r);
3980 }
3981 out
3982 }
3983 fn stats(&self) -> Option<(usize, usize)> {
3984 Some((self.pulled, self.kept))
3985 }
3986}
3987
3988struct VecLeft {
3991 rows: Vec<JoinedRow>,
3992 at: usize,
3993}
3994
3995impl LeftSource for VecLeft {
3996 fn next_left(&mut self) -> Result<Option<JoinedRow>> {
3997 let r = self.rows.get(self.at).cloned();
3998 if r.is_some() {
3999 self.at += 1;
4000 }
4001 Ok(r)
4002 }
4003 fn hint(&self) -> Option<usize> {
4004 Some(self.rows.len().saturating_sub(self.at))
4005 }
4006 fn take_rows(&mut self) -> Vec<JoinedRow> {
4007 let mut v = std::mem::take(&mut self.rows);
4008 if self.at > 0 {
4009 v = v.split_off(self.at);
4010 }
4011 self.at = 0;
4012 v
4013 }
4014}
4015
4016fn drain(mut rel: Box<dyn Relation>) -> Result<Vec<Value>> {
4023 let mut out = Vec::with_capacity(rel.size_hint().unwrap_or(0));
4024 while let Some(row) = rel.next_row()? {
4025 out.push(row);
4026 }
4027 Ok(out)
4028}
4029
4030pub fn run(sql: &str, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
4032 let sel = parse(sql)?;
4033 execute(&sel, resolve)
4034}
4035
4036#[cfg(test)]
4037mod lexer_tests {
4038 use super::*;
4039
4040 fn kinds(src: &str) -> Vec<Tok> {
4041 let mut t = lex(src).expect("lexes");
4042 t.pop(); t
4044 }
4045
4046 #[test]
4047 fn a_word_keeps_both_its_canonical_and_raw_spelling() {
4048 assert_eq!(
4051 kinds("Select"),
4052 vec![Tok::Word { upper: "SELECT".into(), raw: "Select".into() }]
4053 );
4054 }
4055
4056 #[test]
4057 fn a_quoted_identifier_is_never_a_keyword() {
4058 assert_eq!(kinds(r#""select""#), vec![Tok::Quoted("select".into())]);
4059 assert_eq!(kinds(r#""Name""#), vec![Tok::Quoted("Name".into())]);
4061 }
4062
4063 #[test]
4064 fn a_doubled_quote_is_one_literal_quote() {
4065 assert_eq!(kinds("'it''s'"), vec![Tok::Str("it's".into())]);
4066 assert_eq!(kinds(r#""a""b""#), vec![Tok::Quoted("a\"b".into())]);
4067 }
4068
4069 #[test]
4070 fn an_E_string_decodes_the_escapes_catalogue_sql_uses() {
4071 assert_eq!(kinds(r"E'\n'"), vec![Tok::Str("\n".into())]);
4073 assert_eq!(kinds(r"E'a\tb'"), vec![Tok::Str("a\tb".into())]);
4074 assert_eq!(kinds(r"E'\q'"), vec![Tok::Str("q".into())]);
4076 }
4077
4078 #[test]
4079 fn operators_match_longest_first() {
4080 assert_eq!(kinds("!~*"), vec![Tok::Op("!~*".into())]);
4082 assert_eq!(kinds("!~"), vec![Tok::Op("!~".into())]);
4083 assert_eq!(kinds("~*"), vec![Tok::Op("~*".into())]);
4084 assert_eq!(kinds("<>"), vec![Tok::Op("<>".into())]);
4085 assert_eq!(kinds("!="), vec![Tok::Op("!=".into())]);
4086 assert_eq!(kinds(">="), vec![Tok::Op(">=".into())]);
4087 assert_eq!(kinds("::"), vec![Tok::Op("::".into())]);
4088 assert_eq!(kinds("||"), vec![Tok::Op("||".into())]);
4089 assert_eq!(kinds("~"), vec![Tok::Op("~".into())]);
4090 }
4091
4092 #[test]
4093 fn comments_are_skipped_including_nested_block_comments() {
4094 assert_eq!(kinds("1 -- trailing\n"), vec![Tok::Num(1.0)]);
4095 assert_eq!(kinds("1 /* a */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4096 assert_eq!(kinds("1 /* a /* b */ c */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4098 assert!(lex("1 /* unterminated").is_err());
4099 }
4100
4101 #[test]
4102 fn numbers_parse_including_fractions_and_exponents() {
4103 assert_eq!(kinds("42"), vec![Tok::Num(42.0)]);
4104 assert_eq!(kinds("4.5"), vec![Tok::Num(4.5)]);
4105 assert_eq!(kinds(".5"), vec![Tok::Num(0.5)]);
4106 assert_eq!(kinds("1e3"), vec![Tok::Num(1000.0)]);
4107 assert_eq!(kinds("1e-2"), vec![Tok::Num(0.01)]);
4108 assert_eq!(
4110 kinds("1e"),
4111 vec![Tok::Num(1.0), Tok::Word { upper: "E".into(), raw: "e".into() }]
4112 );
4113 }
4114
4115 #[test]
4116 fn an_unterminated_literal_is_an_error_not_a_truncation() {
4117 assert!(lex("'abc").is_err());
4118 assert!(lex(r#""abc"#).is_err());
4119 }
4120
4121 #[test]
4122 fn an_unknown_character_is_REFUSED_rather_than_skipped() {
4123 let e = lex("SELECT 1 @ 2").unwrap_err().to_string();
4126 assert!(e.contains('@'), "{}", e);
4127 }
4128
4129 #[test]
4130 fn the_real_dn_query_lexes() {
4131 let sql = r#"SELECT n.nspname AS "Name",
4132 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
4133 FROM pg_catalog.pg_namespace n
4134 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
4135 ORDER BY 1;"#;
4136 let toks = lex(sql).expect("psql's \\dn must lex");
4137 assert!(toks.contains(&Tok::Quoted("Name".into())));
4138 assert!(toks.contains(&Tok::Op("!~".into())));
4139 assert!(toks.contains(&Tok::Op("<>".into())));
4140 assert!(toks.contains(&Tok::Str("^pg_".into())));
4141 }
4142
4143 #[test]
4144 fn the_real_dt_query_lexes() {
4145 let sql = r#"SELECT n.nspname as "Schema", c.relname as "Name",
4146 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' END as "Type",
4147 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
4148 FROM pg_catalog.pg_class c
4149 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
4150 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
4151 WHERE c.relkind IN ('r','p','')
4152 AND n.nspname <> 'pg_catalog'
4153 AND n.nspname !~ '^pg_toast'
4154 AND pg_catalog.pg_table_is_visible(c.oid)
4155 ORDER BY 1,2;"#;
4156 let toks = lex(sql).expect("psql's \\dt must lex");
4157 assert!(toks.iter().any(|t| t.is_kw("CASE")));
4158 assert!(toks.iter().any(|t| t.is_kw("LEFT")));
4159 assert!(toks.iter().any(|t| t.is_kw("JOIN")));
4160 assert!(toks.contains(&Tok::Str(String::new())));
4162 }
4163}
4164
4165#[cfg(test)]
4166mod parser_tests {
4167 use super::*;
4168 use serde_json::json;
4169
4170 fn col(qual: Option<&str>, name: &str) -> Expr {
4171 Expr::Column { qual: qual.map(str::to_string), name: name.to_string() }
4172 }
4173
4174 #[test]
4175 fn a_bare_select_list_and_from() {
4176 let s = parse("SELECT a, b FROM t").unwrap();
4177 assert_eq!(s.items.len(), 2);
4178 assert_eq!(s.items[0].expr, col(None, "a"));
4179 assert_eq!(s.from.unwrap().name, "t");
4180 }
4181
4182 #[test]
4183 fn a_clause_keyword_is_never_read_as_a_bare_alias() {
4184 let s = parse("SELECT a FROM t WHERE a = 1").unwrap();
4188 assert_eq!(s.from.clone().unwrap().alias, None);
4189 assert!(s.where_.is_some(), "the WHERE clause must survive");
4190 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4191 assert_eq!(s.from.unwrap().alias, None);
4192 assert_eq!(s.order_by.len(), 1);
4193 }
4194
4195 #[test]
4196 fn a_real_alias_is_kept_in_both_spellings() {
4197 assert_eq!(parse("SELECT a FROM t x").unwrap().from.unwrap().alias,
4198 Some("x".to_string()));
4199 assert_eq!(parse("SELECT a FROM t AS x").unwrap().from.unwrap().alias,
4200 Some("x".to_string()));
4201 }
4202
4203 #[test]
4204 fn a_tables_binding_is_its_alias_else_its_bare_name() {
4205 let t = TableRef::named("pg_catalog.pg_class", Some("c".into()));
4206 assert_eq!(t.binding(), "c");
4207 let t = TableRef::named("pg_catalog.pg_class", None);
4208 assert_eq!(t.binding(), "pg_class", "the schema is not how a column is addressed");
4209 }
4210
4211 #[test]
4212 fn a_qualified_column_keeps_only_its_immediate_qualifier() {
4213 assert_eq!(parse("SELECT n.nspname FROM x").unwrap().items[0].expr,
4214 col(Some("n"), "nspname"));
4215 assert_eq!(parse("SELECT public.orders.id FROM x").unwrap().items[0].expr,
4218 col(Some("orders"), "id"));
4219 }
4220
4221 #[test]
4222 fn an_alias_may_be_a_quoted_string_with_significant_case() {
4223 let s = parse(r#"SELECT n.nspname AS "Name" FROM x"#).unwrap();
4224 assert_eq!(s.items[0].alias, Some("Name".to_string()));
4225 }
4226
4227 #[test]
4228 fn a_schema_qualified_function_drops_its_schema() {
4229 let s = parse("SELECT pg_catalog.pg_get_userbyid(n.nspowner) FROM x").unwrap();
4232 match &s.items[0].expr {
4233 Expr::Func { name, args } => {
4234 assert_eq!(name, "pg_get_userbyid");
4235 assert_eq!(args.len(), 1);
4236 assert_eq!(args[0], col(Some("n"), "nspowner"));
4237 }
4238 other => panic!("{:?}", other),
4239 }
4240 }
4241
4242 #[test]
4243 fn operator_precedence_matches_sql() {
4244 let s = parse("SELECT 1 FROM t WHERE a = 1 OR b = 2 AND c = 3").unwrap();
4247 match s.where_.unwrap() {
4248 Expr::Binary { op, right, .. } => {
4249 assert_eq!(op, "OR");
4250 assert!(matches!(*right, Expr::Binary { ref op, .. } if op == "AND"),
4251 "AND must bind tighter than OR");
4252 }
4253 other => panic!("{:?}", other),
4254 }
4255 let s = parse("SELECT 1 FROM t WHERE a = 1 AND b = 2").unwrap();
4257 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4258 let s = parse("SELECT 1 + 2 * 3 FROM t").unwrap();
4260 match &s.items[0].expr {
4261 Expr::Binary { op, right, .. } => {
4262 assert_eq!(op, "+");
4263 assert!(matches!(**right, Expr::Binary { ref op, .. } if op == "*"));
4264 }
4265 other => panic!("{:?}", other),
4266 }
4267 }
4268
4269 #[test]
4270 fn parentheses_override_precedence() {
4271 let s = parse("SELECT 1 FROM t WHERE (a = 1 OR b = 2) AND c = 3").unwrap();
4272 match s.where_.unwrap() {
4273 Expr::Binary { op, left, .. } => {
4274 assert_eq!(op, "AND");
4275 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "OR"));
4276 }
4277 other => panic!("{:?}", other),
4278 }
4279 }
4280
4281 #[test]
4282 fn in_and_is_null_and_between_parse_in_both_polarities() {
4283 let s = parse("SELECT 1 FROM t WHERE k IN ('r','p','')").unwrap();
4284 match s.where_.unwrap() {
4285 Expr::InList { list, negated, .. } => {
4286 assert_eq!(list.len(), 3);
4287 assert!(!negated);
4288 assert_eq!(list[2], Expr::Literal(json!("")));
4290 }
4291 other => panic!("{:?}", other),
4292 }
4293 assert!(matches!(parse("SELECT 1 FROM t WHERE k NOT IN (1)").unwrap().where_.unwrap(),
4294 Expr::InList { negated: true, .. }));
4295 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NULL").unwrap().where_.unwrap(),
4296 Expr::IsNull { negated: false, .. }));
4297 assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NOT NULL").unwrap().where_.unwrap(),
4298 Expr::IsNull { negated: true, .. }));
4299 let s = parse("SELECT 1 FROM t WHERE n BETWEEN 1 AND 5").unwrap();
4301 assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4302 }
4303
4304 #[test]
4305 fn both_case_spellings_parse() {
4306 let s = parse("SELECT CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
4308 ELSE 'other' END FROM t").unwrap();
4309 match &s.items[0].expr {
4310 Expr::Case { operand, whens, else_ } => {
4311 assert!(operand.is_some());
4312 assert_eq!(whens.len(), 2);
4313 assert!(else_.is_some());
4314 }
4315 other => panic!("{:?}", other),
4316 }
4317 let s = parse("SELECT CASE WHEN k = 'r' THEN 1 END FROM t").unwrap();
4319 match &s.items[0].expr {
4320 Expr::Case { operand, whens, else_ } => {
4321 assert!(operand.is_none());
4322 assert_eq!(whens.len(), 1);
4323 assert!(else_.is_none());
4324 }
4325 other => panic!("{:?}", other),
4326 }
4327 assert!(parse("SELECT CASE k END FROM t").is_err());
4329 }
4330
4331 #[test]
4332 fn every_join_flavour_parses_and_an_inner_join_demands_ON() {
4333 for (sql, kind) in [
4334 ("SELECT 1 FROM a JOIN b ON a.x = b.x", JoinKind::Inner),
4335 ("SELECT 1 FROM a INNER JOIN b ON a.x = b.x", JoinKind::Inner),
4336 ("SELECT 1 FROM a LEFT JOIN b ON a.x = b.x", JoinKind::Left),
4337 ("SELECT 1 FROM a LEFT OUTER JOIN b ON a.x = b.x", JoinKind::Left),
4338 ("SELECT 1 FROM a RIGHT JOIN b ON a.x = b.x", JoinKind::Right),
4339 ("SELECT 1 FROM a FULL OUTER JOIN b ON a.x = b.x", JoinKind::Full),
4340 ("SELECT 1 FROM a CROSS JOIN b", JoinKind::Cross),
4341 ] {
4342 let s = parse(sql).unwrap_or_else(|e| panic!("{}: {}", sql, e));
4343 assert_eq!(s.joins.len(), 1, "{}", sql);
4344 assert_eq!(s.joins[0].kind, kind, "{}", sql);
4345 }
4346 let s = parse("SELECT 1 FROM a, b").unwrap();
4348 assert_eq!(s.joins[0].kind, JoinKind::Cross);
4349 assert!(parse("SELECT 1 FROM a LEFT JOIN b").is_err());
4352 assert!(parse("SELECT 1 FROM a JOIN b USING (x)").is_err());
4353 }
4354
4355 #[test]
4356 fn order_by_reads_a_number_as_an_ORDINAL() {
4357 let s = parse("SELECT a, b FROM t ORDER BY 1, 2 DESC").unwrap();
4361 assert_eq!(s.order_by.len(), 2);
4362 assert_eq!(s.order_by[0].ordinal, Some(1));
4363 assert_eq!(s.order_by[0].dir, Dir::Asc);
4364 assert_eq!(s.order_by[1].ordinal, Some(2));
4365 assert_eq!(s.order_by[1].dir, Dir::Desc);
4366 let s = parse("SELECT a FROM t ORDER BY lower(a) ASC").unwrap();
4368 assert!(s.order_by[0].ordinal.is_none());
4369 assert!(s.order_by[0].expr.is_some());
4370 }
4371
4372 #[test]
4373 fn null_ordering_defaults_the_way_postgres_defaults() {
4374 let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4375 assert!(!s.order_by[0].nulls_first, "ASC defaults to NULLS LAST");
4376 let s = parse("SELECT a FROM t ORDER BY a DESC").unwrap();
4377 assert!(s.order_by[0].nulls_first, "DESC defaults to NULLS FIRST");
4378 let s = parse("SELECT a FROM t ORDER BY a NULLS FIRST").unwrap();
4379 assert!(s.order_by[0].nulls_first, "an explicit clause wins");
4380 }
4381
4382 #[test]
4383 fn limit_and_offset_parse_in_either_order() {
4384 let s = parse("SELECT a FROM t LIMIT 5 OFFSET 2").unwrap();
4385 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
4386 let s = parse("SELECT a FROM t OFFSET 2 LIMIT 5").unwrap();
4387 assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
4388 let s = parse("SELECT a FROM t LIMIT ALL").unwrap();
4389 assert_eq!(s.limit, None);
4390 }
4391
4392 #[test]
4393 fn casts_parse_and_are_recorded_rather_than_rejected() {
4394 let s = parse("SELECT x::int2 FROM t").unwrap();
4396 assert!(matches!(s.items[0].expr, Expr::Cast { .. }));
4397 let s = parse("SELECT x::pg_catalog.int2[] FROM t").unwrap();
4398 match &s.items[0].expr {
4399 Expr::Cast { ty, .. } => assert_eq!(ty, "int2[]"),
4400 other => panic!("{:?}", other),
4401 }
4402 }
4403
4404 #[test]
4405 fn star_and_qualified_star_parse() {
4406 assert_eq!(parse("SELECT * FROM t").unwrap().items[0].expr, Expr::Star);
4407 assert_eq!(parse("SELECT c.* FROM t c").unwrap().items[0].expr,
4408 Expr::QualifiedStar("c".into()));
4409 match &parse("SELECT count(*) FROM t").unwrap().items[0].expr {
4410 Expr::Func { name, args } => {
4411 assert_eq!(name, "count");
4412 assert_eq!(args, &vec![Expr::Star]);
4413 }
4414 other => panic!("{:?}", other),
4415 }
4416 }
4417
4418 #[test]
4419 fn a_parenthesis_free_function_parses_as_a_zero_arg_call() {
4420 match &parse("SELECT current_schema FROM t").unwrap().items[0].expr {
4422 Expr::Func { name, args } => {
4423 assert_eq!(name, "current_schema");
4424 assert!(args.is_empty());
4425 }
4426 other => panic!("{:?}", other),
4427 }
4428 }
4429
4430 #[test]
4431 fn unsupported_clauses_are_refused_by_name() {
4432 for (sql, needle) in [
4433 ("SELECT a FROM t GROUP BY a", "GROUP BY"),
4434 ("SELECT a FROM t HAVING count(*) > 1", "HAVING"),
4435 ("SELECT DISTINCT ON (a) a FROM t", "DISTINCT ON"),
4436 ] {
4437 let e = parse(sql).unwrap_err().to_string();
4438 assert!(e.contains(needle), "{} -> {}", sql, e);
4439 }
4440 assert!(parse("SELECT a FROM t JUNK JUNK2").is_err());
4442 }
4443
4444 #[test]
4445 fn THE_dn_QUERY_parses_completely() {
4446 let s = parse(
4447 r#"SELECT n.nspname AS "Name",
4448 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
4449 FROM pg_catalog.pg_namespace n
4450 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
4451 ORDER BY 1;"#,
4452 )
4453 .expect("psql's \\dn must parse");
4454
4455 assert_eq!(s.items.len(), 2);
4456 assert_eq!(s.items[0].alias, Some("Name".into()));
4457 assert_eq!(s.items[1].alias, Some("Owner".into()));
4458 let from = s.from.unwrap();
4459 assert_eq!(from.name, "pg_catalog.pg_namespace");
4460 assert_eq!(from.binding(), "n");
4461 assert!(s.where_.is_some());
4462 assert_eq!(s.order_by[0].ordinal, Some(1));
4463 }
4464
4465 #[test]
4466 fn THE_dt_QUERY_parses_completely() {
4467 let s = parse(
4468 r#"SELECT n.nspname as "Schema",
4469 c.relname as "Name",
4470 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
4471 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
4472 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
4473 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
4474 WHEN 'I' THEN 'partitioned index' END as "Type",
4475 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
4476 FROM pg_catalog.pg_class c
4477 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
4478 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
4479 WHERE c.relkind IN ('r','p','')
4480 AND n.nspname <> 'pg_catalog'
4481 AND n.nspname !~ '^pg_toast'
4482 AND n.nspname <> 'information_schema'
4483 AND pg_catalog.pg_table_is_visible(c.oid)
4484 ORDER BY 1,2;"#,
4485 )
4486 .expect("psql's \\dt must parse");
4487
4488 assert_eq!(s.items.len(), 4);
4489 assert_eq!(s.items[2].alias, Some("Type".into()));
4490 match &s.items[2].expr {
4491 Expr::Case { whens, .. } => assert_eq!(whens.len(), 9, "all nine branches"),
4492 other => panic!("{:?}", other),
4493 }
4494 assert_eq!(s.joins.len(), 2);
4495 assert!(s.joins.iter().all(|j| j.kind == JoinKind::Left && j.on.is_some()));
4496 assert_eq!(s.from.unwrap().binding(), "c");
4497 assert_eq!(s.order_by.len(), 2);
4498 assert_eq!(
4499 (s.order_by[0].ordinal, s.order_by[1].ordinal),
4500 (Some(1), Some(2))
4501 );
4502 }
4503}
4504
4505#[cfg(test)]
4506mod eval_tests {
4507 use super::*;
4508 use serde_json::json;
4509
4510 fn one(row: &Value) -> Bound<'_> {
4512 Bound::new(vec![("t".to_string(), Some(row))])
4513 }
4514
4515 fn ev(sql_expr: &str, row: &Value) -> Result<Value> {
4516 let s = parse(&format!("SELECT {} FROM t", sql_expr))?;
4517 eval(&s.items[0].expr, &one(row))
4518 }
4519
4520 fn v(sql_expr: &str, row: &Value) -> Value {
4521 ev(sql_expr, row).unwrap_or_else(|e| panic!("{}: {}", sql_expr, e))
4522 }
4523
4524 #[test]
4525 fn literals_and_columns_resolve() {
4526 let r = json!({"a": 1, "s": "x", "b": true, "n": null});
4527 assert_eq!(v("42", &r), json!(42));
4528 assert_eq!(v("'hi'", &r), json!("hi"));
4529 assert_eq!(v("NULL", &r), Value::Null);
4530 assert_eq!(v("TRUE", &r), json!(true));
4531 assert_eq!(v("a", &r), json!(1));
4532 assert_eq!(v("t.a", &r), json!(1));
4533 assert_eq!(v("s", &r), json!("x"));
4534 assert_eq!(v("nosuch", &r), Value::Null);
4537 }
4538
4539 #[test]
4540 fn an_unknown_table_ALIAS_is_an_error_while_an_unknown_column_is_null() {
4541 let r = json!({"a": 1});
4544 assert_eq!(v("t.nosuch", &r), Value::Null);
4545 let e = ev("zz.a", &r).unwrap_err().to_string();
4546 assert!(e.contains("zz"), "{}", e);
4547 }
4548
4549 #[test]
4552 fn every_comparison_over_NULL_is_UNKNOWN_including_null_equals_null() {
4553 let r = json!({"n": null, "a": 1});
4554 assert_eq!(v("n = 1", &r), Value::Null);
4555 assert_eq!(v("n != 1", &r), Value::Null);
4556 assert_eq!(v("n < 1", &r), Value::Null);
4557 assert_eq!(v("n = n", &r), Value::Null);
4559 assert_eq!(v("n = NULL", &r), Value::Null);
4560 }
4561
4562 #[test]
4563 fn NOT_UNKNOWN_is_UNKNOWN_not_true() {
4564 let r = json!({"n": null});
4568 assert_eq!(v("NOT (n = 1)", &r), Value::Null);
4569 assert_eq!(v("NOT TRUE", &r), json!(false));
4570 assert_eq!(v("NOT FALSE", &r), json!(true));
4571 }
4572
4573 #[test]
4574 fn AND_and_OR_follow_the_three_valued_truth_tables() {
4575 let r = json!({"n": null});
4576 assert_eq!(v("FALSE AND n = 1", &r), json!(false));
4578 assert_eq!(v("TRUE AND n = 1", &r), Value::Null);
4580 assert_eq!(v("TRUE OR n = 1", &r), json!(true));
4582 assert_eq!(v("FALSE OR n = 1", &r), Value::Null);
4584 assert_eq!(v("TRUE AND TRUE", &r), json!(true));
4586 assert_eq!(v("TRUE AND FALSE", &r), json!(false));
4587 assert_eq!(v("FALSE OR FALSE", &r), json!(false));
4588 }
4589
4590 #[test]
4591 fn IS_NULL_is_the_one_predicate_that_is_never_unknown() {
4592 let r = json!({"n": null, "a": 1});
4593 assert_eq!(v("n IS NULL", &r), json!(true));
4594 assert_eq!(v("n IS NOT NULL", &r), json!(false));
4595 assert_eq!(v("a IS NULL", &r), json!(false));
4596 assert_eq!(v("a IS NOT NULL", &r), json!(true));
4597 assert_eq!(v("nosuch IS NULL", &r), json!(true));
4600 }
4601
4602 #[test]
4603 fn NOT_IN_with_a_NULL_in_the_list_is_UNKNOWN_the_classic_trap() {
4604 let r = json!({"a": 2});
4605 assert_eq!(v("a IN (1, 2)", &r), json!(true));
4606 assert_eq!(v("a IN (1, 3)", &r), json!(false));
4607 assert_eq!(v("a NOT IN (1, 3)", &r), json!(true));
4608 assert_eq!(v("a NOT IN (1, NULL)", &r), Value::Null);
4611 assert_eq!(v("a IN (2, NULL)", &r), json!(true));
4613 assert_eq!(v("nosuch IN (1)", &r), Value::Null);
4615 }
4616
4617 #[test]
4620 fn comparisons_work_across_numbers_strings_and_booleans() {
4621 let r = json!({"n": 5, "s": "b", "t": true});
4622 assert_eq!(v("n > 3", &r), json!(true));
4623 assert_eq!(v("n <= 5", &r), json!(true));
4624 assert_eq!(v("s < 'c'", &r), json!(true));
4625 assert_eq!(v("s > 'c'", &r), json!(false));
4626 assert_eq!(v("n = '5'", &r), json!(true));
4629 assert_eq!(v("n = '5.0'", &r), json!(true));
4630 assert_eq!(v("n = 'five'", &r), json!(false));
4633 }
4634
4635 #[test]
4636 fn the_regex_operators_use_the_SAME_matcher_as_NQL() {
4637 let r = json!({"s": "pg_catalog"});
4640 assert_eq!(v("s ~ '^pg_'", &r), json!(true));
4641 assert_eq!(v("s !~ '^pg_'", &r), json!(false));
4642 assert_eq!(v("s ~ '^PG_'", &r), json!(false));
4643 assert_eq!(v("s ~* '^PG_'", &r), json!(true));
4644 assert_eq!(v("s !~ '^zz'", &r), json!(true));
4645 assert_eq!(v("nosuch ~ '^x'", &r), Value::Null);
4647 assert_eq!(v("s ~ '^(pg_catalog)$'", &r), json!(true));
4650 assert_eq!(v("s ~ '^(pg_.*)$'", &r), json!(true));
4651 assert_eq!(v("s ~ '^(public|pg_catalog)$'", &r), json!(true));
4652 assert_eq!(v("s ~ '^pg_[a-z]+$'", &r), json!(true));
4653 assert_eq!(v("s ~ '^pg_[0-9]+$'", &r), json!(false));
4654 let e = ev("s ~ 'a{2}'", &r).unwrap_err().to_string();
4656 assert!(e.contains("interval"), "{}", e);
4657 }
4658
4659 #[test]
4660 fn like_works_in_all_four_spellings() {
4661 let r = json!({"s": "Acme Pool"});
4662 assert_eq!(v("s LIKE 'Acme%'", &r), json!(true));
4663 assert_eq!(v("s LIKE 'acme%'", &r), json!(false));
4664 assert_eq!(v("s ILIKE 'acme%'", &r), json!(true));
4665 assert_eq!(v("s NOT LIKE 'zz%'", &r), json!(true));
4666 assert_eq!(v("nosuch LIKE 'x'", &r), Value::Null);
4667 }
4668
4669 #[test]
4670 fn arithmetic_and_concatenation_propagate_null_and_refuse_div_by_zero() {
4671 let r = json!({"a": 7, "b": 2});
4672 assert_eq!(v("a + b", &r), json!(9));
4673 assert_eq!(v("a - b", &r), json!(5));
4674 assert_eq!(v("a * b", &r), json!(14));
4675 assert_eq!(v("a / b", &r), json!(3.5));
4676 assert_eq!(v("a % b", &r), json!(1));
4677 assert_eq!(v("-a", &r), json!(-7));
4678 assert_eq!(v("a / b", &r), json!(3.5));
4681 assert_eq!(v("b / a", &r), json!(2.0 / 7.0));
4682 assert_eq!(v("'x' || 'y'", &r), json!("xy"));
4683 assert_eq!(v("'x' || nosuch", &r), Value::Null);
4684 assert_eq!(v("a + nosuch", &r), Value::Null);
4685 assert!(ev("a / 0", &r).is_err());
4688 assert!(ev("a % 0", &r).is_err());
4689 }
4690
4691 #[test]
4692 fn a_cast_is_transparent_rather_than_rejected() {
4693 let r = json!({"a": 7});
4696 assert_eq!(v("a::int2", &r), json!(7));
4697 assert_eq!(v("a::pg_catalog.int2[]", &r), json!(7));
4698 }
4699
4700 #[test]
4703 fn a_simple_CASE_picks_the_matching_branch() {
4704 let expr = "CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
4706 WHEN 'i' THEN 'index' END";
4707 assert_eq!(v(expr, &json!({"k": "r"})), json!("table"));
4708 assert_eq!(v(expr, &json!({"k": "v"})), json!("view"));
4709 assert_eq!(v(expr, &json!({"k": "i"})), json!("index"));
4710 assert_eq!(v(expr, &json!({"k": "z"})), Value::Null);
4713 }
4714
4715 #[test]
4716 fn a_searched_CASE_evaluates_predicates_and_UNKNOWN_does_not_match() {
4717 let expr = "CASE WHEN n > 5 THEN 'big' WHEN n > 0 THEN 'small' ELSE 'none' END";
4718 assert_eq!(v(expr, &json!({"n": 9})), json!("big"));
4719 assert_eq!(v(expr, &json!({"n": 2})), json!("small"));
4720 assert_eq!(v(expr, &json!({"n": -1})), json!("none"));
4721 assert_eq!(v(expr, &json!({"other": 1})), json!("none"));
4723 }
4724
4725 #[test]
4726 fn an_ELSE_branch_is_used_when_nothing_matches() {
4727 assert_eq!(
4728 v("CASE k WHEN 'r' THEN 'table' ELSE 'other' END", &json!({"k": "z"})),
4729 json!("other")
4730 );
4731 }
4732
4733 #[test]
4736 fn the_catalogue_functions_psql_calls_all_answer() {
4737 let r = json!({"o": 10, "enc": 6});
4738 assert_eq!(v("pg_get_userbyid(o)", &r), json!("nedb"));
4740 assert_eq!(v("pg_catalog.pg_get_userbyid(o)", &r), json!("nedb"));
4741 assert_eq!(v("pg_table_is_visible(o)", &r), json!(true));
4743 assert_eq!(v("pg_encoding_to_char(enc)", &r), json!("UTF8"));
4744 assert_eq!(v("current_schema", &r), json!("public"));
4745 assert_eq!(v("current_database()", &r), json!("nedb"));
4746 assert_eq!(v("current_user", &r), json!("nedb"));
4747 assert_eq!(v("pg_get_expr(o, o)", &r), Value::Null);
4750 assert_eq!(v("obj_description(o)", &r), Value::Null);
4751 }
4752
4753 #[test]
4754 fn text_and_null_handling_functions_work() {
4755 let r = json!({"s": "AbC", "n": null});
4756 assert_eq!(v("lower(s)", &r), json!("abc"));
4757 assert_eq!(v("upper(s)", &r), json!("ABC"));
4758 assert_eq!(v("length(s)", &r), json!(3));
4759 assert_eq!(v("lower(n)", &r), Value::Null);
4760 assert_eq!(v("coalesce(n, 'fallback')", &r), json!("fallback"));
4761 assert_eq!(v("coalesce(s, 'fallback')", &r), json!("AbC"));
4762 assert_eq!(v("coalesce(n, n)", &r), Value::Null);
4763 assert_eq!(v("nullif(s, 'AbC')", &r), Value::Null);
4764 assert_eq!(v("nullif(s, 'zz')", &r), json!("AbC"));
4765 assert_eq!(v("format_type(20, NULL)", &r), json!("bigint"));
4767 }
4768
4769 #[test]
4770 fn coalesce_does_not_evaluate_past_its_first_non_null() {
4771 let r = json!({"a": 1});
4773 assert_eq!(v("coalesce(a, a / 0)", &r), json!(1));
4774 }
4775
4776 #[test]
4777 fn an_unknown_function_is_REFUSED_rather_than_answered_with_NULL() {
4778 let e = ev("pg_stat_get_numscans(1)", &json!({})).unwrap_err().to_string();
4781 assert!(e.contains("pg_stat_get_numscans"), "{}", e);
4782 assert!(e.contains("refused"), "{}", e);
4783 }
4784
4785 #[test]
4788 fn a_qualified_column_reads_only_its_OWN_binding() {
4789 let a = json!({"name": "left", "x": 1});
4792 let b = json!({"name": "right", "y": 2});
4793 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), Some(&b))]);
4794 let get = |e: &str| {
4795 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
4796 eval(&s.items[0].expr, &row).unwrap()
4797 };
4798 assert_eq!(get("a.name"), json!("left"));
4799 assert_eq!(get("b.name"), json!("right"));
4800 assert_eq!(get("name"), json!("left"));
4802 assert_eq!(get("y"), json!(2), "a bare name still finds a later binding");
4803 }
4804
4805 #[test]
4806 fn an_unmatched_LEFT_JOIN_side_reads_as_NULL_not_as_a_missing_column() {
4807 let a = json!({"x": 1});
4810 let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), None)]);
4811 let get = |e: &str| {
4812 let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
4813 eval(&s.items[0].expr, &row).unwrap()
4814 };
4815 assert_eq!(get("b.anything"), Value::Null);
4816 assert_eq!(get("b.anything IS NULL"), json!(true));
4817 assert_eq!(get("a.x"), json!(1));
4818 }
4819}
4820
4821#[cfg(test)]
4822mod exec_tests {
4823 use super::*;
4824 use serde_json::json;
4825
4826 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
4828 let owned: Vec<(String, Vec<Value>)> =
4829 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
4830 move |name: &str| {
4831 let bare = name.rsplit('.').next().unwrap_or(name);
4833 Ok(owned
4834 .iter()
4835 .find(|(n, _)| n == name || n == bare)
4836 .map(|(_, r)| from_vec(r.clone())))
4837 }
4838 }
4839
4840 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
4841 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
4842 (cols.into_iter().map(|c| c.name).collect(), rows)
4843 }
4844
4845 fn col(rows: &[Value], name: &str) -> Vec<Value> {
4846 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
4847 }
4848
4849 #[test]
4852 fn select_columns_where_order_limit_offset() {
4853 let t = tables(vec![(
4854 "t",
4855 vec![json!({"a": 3, "s": "c"}), json!({"a": 1, "s": "a"}), json!({"a": 2, "s": "b"})],
4856 )]);
4857 let (names, rows) = go("SELECT a, s FROM t ORDER BY a", &t);
4858 assert_eq!(names, vec!["a", "s"]);
4859 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2), json!(3)]);
4860
4861 let (_, rows) = go("SELECT a FROM t ORDER BY a DESC", &t);
4862 assert_eq!(col(&rows, "a"), vec![json!(3), json!(2), json!(1)]);
4863
4864 let (_, rows) = go("SELECT a FROM t WHERE a > 1 ORDER BY a", &t);
4865 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
4866
4867 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 2", &t);
4868 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
4869
4870 let (_, rows) = go("SELECT a FROM t ORDER BY a OFFSET 1", &t);
4871 assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
4872
4873 let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 1 OFFSET 1", &t);
4874 assert_eq!(col(&rows, "a"), vec![json!(2)]);
4875
4876 let (_, rows) = go("SELECT a FROM t OFFSET 99", &t);
4878 assert!(rows.is_empty());
4879 }
4880
4881 #[test]
4882 fn an_output_column_takes_its_alias_or_a_derived_name() {
4883 let t = tables(vec![("t", vec![json!({"a": 1})])]);
4886 assert_eq!(go(r#"SELECT a AS "Name" FROM t"#, &t).0, vec!["Name"]);
4887 assert_eq!(go("SELECT a FROM t", &t).0, vec!["a"]);
4888 assert_eq!(go("SELECT lower('X') FROM t", &t).0, vec!["lower"]);
4889 assert_eq!(go("SELECT 1 + 1 FROM t", &t).0, vec!["?column?"]);
4890 assert_eq!(go("SELECT CASE a WHEN 1 THEN 'x' END FROM t", &t).0, vec!["case"]);
4891 }
4892
4893 #[test]
4894 fn star_expands_from_the_rows_and_a_qualified_star_from_one_binding() {
4895 let t = tables(vec![
4896 ("a", vec![json!({"x": 1, "y": 2})]),
4897 ("b", vec![json!({"z": 3})]),
4898 ]);
4899 let (names, rows) = go("SELECT * FROM a", &t);
4900 assert_eq!(names, vec!["x", "y"]);
4901 assert_eq!(rows.len(), 1);
4902
4903 let (names, _) = go("SELECT a.* FROM a CROSS JOIN b", &t);
4904 assert_eq!(names, vec!["x", "y"], "a qualified star takes ONE binding");
4905
4906 let empty = tables(vec![("e", vec![])]);
4909 assert_eq!(go("SELECT * FROM e", &empty).0, Vec::<String>::new());
4910 }
4911
4912 #[test]
4913 fn distinct_dedupes_on_the_projected_values() {
4914 let t = tables(vec![(
4915 "t",
4916 vec![json!({"g": "x"}), json!({"g": "x"}), json!({"g": "y"})],
4917 )]);
4918 let (_, rows) = go("SELECT DISTINCT g FROM t ORDER BY 1", &t);
4919 assert_eq!(col(&rows, "g"), vec![json!("x"), json!("y")]);
4920 let (_, rows) = go("SELECT g FROM t", &t);
4921 assert_eq!(rows.len(), 3, "without DISTINCT every row survives");
4922 }
4923
4924 #[test]
4925 fn order_by_an_ORDINAL_sorts_the_projected_column() {
4926 let t = tables(vec![(
4927 "t",
4928 vec![json!({"a": 2, "b": "z"}), json!({"a": 1, "b": "y"})],
4929 )]);
4930 let (_, rows) = go("SELECT a, b FROM t ORDER BY 1", &t);
4931 assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
4932 let (_, rows) = go("SELECT a, b FROM t ORDER BY 2 DESC", &t);
4933 assert_eq!(col(&rows, "b"), vec![json!("z"), json!("y")]);
4934 let e = run("SELECT a FROM t ORDER BY 3", &t).unwrap_err().to_string();
4936 assert!(e.contains("out of range"), "{}", e);
4937 }
4938
4939 #[test]
4940 fn order_by_an_expression_may_use_a_column_NOT_in_the_select_list() {
4941 let t = tables(vec![(
4942 "t",
4943 vec![json!({"a": 1, "hidden": 9}), json!({"a": 2, "hidden": 1})],
4944 )]);
4945 let (_, rows) = go("SELECT a FROM t ORDER BY hidden", &t);
4946 assert_eq!(col(&rows, "a"), vec![json!(2), json!(1)]);
4947 }
4948
4949 #[test]
4950 fn null_ordering_follows_the_direction_defaults() {
4951 let t = tables(vec![(
4952 "t",
4953 vec![json!({"a": 2}), json!({"a": null}), json!({"a": 1})],
4954 )]);
4955 assert_eq!(col(&go("SELECT a FROM t ORDER BY a", &t).1, "a"),
4957 vec![json!(1), json!(2), Value::Null]);
4958 assert_eq!(col(&go("SELECT a FROM t ORDER BY a DESC", &t).1, "a"),
4960 vec![Value::Null, json!(2), json!(1)]);
4961 assert_eq!(col(&go("SELECT a FROM t ORDER BY a NULLS FIRST", &t).1, "a"),
4963 vec![Value::Null, json!(1), json!(2)]);
4964 }
4965
4966 #[test]
4967 fn a_where_clause_that_is_UNKNOWN_excludes_the_row() {
4968 let t = tables(vec![(
4969 "t",
4970 vec![json!({"a": 1}), json!({"a": null}), json!({"other": 1})],
4971 )]);
4972 let (_, rows) = go("SELECT a FROM t WHERE a = 1", &t);
4974 assert_eq!(rows.len(), 1);
4975 let (_, rows) = go("SELECT a FROM t WHERE NOT (a = 1)", &t);
4977 assert_eq!(rows.len(), 0, "NOT UNKNOWN must not resurrect a null row");
4978 }
4979
4980 #[test]
4981 fn select_with_no_FROM_returns_exactly_one_row() {
4982 let t = tables(vec![]);
4984 let (names, rows) = go("SELECT 1", &t);
4985 assert_eq!(rows.len(), 1);
4986 assert_eq!(names, vec!["?column?"]);
4987 assert_eq!(go("SELECT current_schema", &t).1.len(), 1);
4988 }
4989
4990 #[test]
4991 fn an_unknown_relation_is_NAMED_rather_than_answered_with_no_rows() {
4992 let t = tables(vec![("t", vec![])]);
4995 let e = run("SELECT a FROM nosuchtable", &t).unwrap_err().to_string();
4996 assert!(e.contains("nosuchtable"), "{}", e);
4997 assert!(e.contains("does not exist"), "{}", e);
4998 }
4999
5000 #[test]
5003 fn an_inner_join_keeps_only_matching_pairs() {
5004 let t = tables(vec![
5005 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5006 ("r", vec![json!({"lid": 1, "v": "x"})]),
5007 ]);
5008 let (_, rows) = go("SELECT l.n, r.v FROM l JOIN r ON r.lid = l.id", &t);
5009 assert_eq!(rows.len(), 1);
5010 assert_eq!(col(&rows, "n"), vec![json!("a")]);
5011 }
5012
5013 #[test]
5014 fn a_LEFT_join_keeps_unmatched_left_rows_with_NULLs() {
5015 let t = tables(vec![
5017 ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5018 ("r", vec![json!({"lid": 1, "v": "x"})]),
5019 ]);
5020 let (_, rows) = go("SELECT l.n, r.v FROM l LEFT JOIN r ON r.lid = l.id ORDER BY 1", &t);
5021 assert_eq!(rows.len(), 2);
5022 assert_eq!(col(&rows, "n"), vec![json!("a"), json!("b")]);
5023 assert_eq!(col(&rows, "v"), vec![json!("x"), Value::Null]);
5024 }
5025
5026 #[test]
5027 fn a_RIGHT_join_keeps_unmatched_right_rows_and_FULL_keeps_both() {
5028 let t = tables(vec![
5029 ("l", vec![json!({"id": 1})]),
5030 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5031 ]);
5032 let (_, rows) = go("SELECT l.id, r.lid FROM l RIGHT JOIN r ON r.lid = l.id", &t);
5033 assert_eq!(rows.len(), 2);
5034 assert!(col(&rows, "id").contains(&Value::Null), "the unmatched right row keeps NULLs on the left");
5035
5036 let t2 = tables(vec![
5037 ("l", vec![json!({"id": 1}), json!({"id": 5})]),
5038 ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5039 ]);
5040 let (_, rows) = go("SELECT l.id, r.lid FROM l FULL OUTER JOIN r ON r.lid = l.id", &t2);
5041 assert_eq!(rows.len(), 3, "one match plus one orphan on each side");
5042 }
5043
5044 #[test]
5045 fn a_cross_join_is_the_cartesian_product() {
5046 let t = tables(vec![
5047 ("a", vec![json!({"x": 1}), json!({"x": 2})]),
5048 ("b", vec![json!({"y": 1}), json!({"y": 2}), json!({"y": 3})]),
5049 ]);
5050 assert_eq!(go("SELECT a.x, b.y FROM a CROSS JOIN b", &t).1.len(), 6);
5051 assert_eq!(go("SELECT a.x, b.y FROM a, b", &t).1.len(), 6);
5053 }
5054
5055 #[test]
5056 fn an_ON_clause_that_is_UNKNOWN_does_not_join() {
5057 let t = tables(vec![
5060 ("l", vec![json!({"id": null})]),
5061 ("r", vec![json!({"lid": null})]),
5062 ]);
5063 let (_, rows) = go("SELECT l.id FROM l JOIN r ON r.lid = l.id", &t);
5064 assert!(rows.is_empty(), "NULL = NULL is UNKNOWN, so nothing joins");
5065 let (_, rows) = go("SELECT l.id FROM l LEFT JOIN r ON r.lid = l.id", &t);
5067 assert_eq!(rows.len(), 1);
5068 }
5069
5070 #[test]
5071 fn two_joins_chain() {
5072 let t = tables(vec![
5073 ("a", vec![json!({"id": 1, "bid": 10, "cid": 100})]),
5074 ("b", vec![json!({"id": 10, "bn": "B"})]),
5075 ("c", vec![json!({"id": 100, "cn": "C"})]),
5076 ]);
5077 let (_, rows) = go(
5078 "SELECT a.id, b.bn, c.cn FROM a \
5079 LEFT JOIN b ON b.id = a.bid \
5080 LEFT JOIN c ON c.id = a.cid",
5081 &t,
5082 );
5083 assert_eq!(rows.len(), 1);
5084 assert_eq!(col(&rows, "bn"), vec![json!("B")]);
5085 assert_eq!(col(&rows, "cn"), vec![json!("C")]);
5086 }
5087
5088 fn catalog() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5092 tables(vec![
5093 (
5094 "pg_namespace",
5095 vec![
5096 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5097 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5098 json!({"oid": 13000, "nspname": "information_schema", "nspowner": 10}),
5099 ],
5100 ),
5101 (
5102 "pg_class",
5103 vec![
5104 json!({"oid": 16401, "relname": "orders", "relnamespace": 2200,
5105 "relkind": "r", "relowner": 10, "relam": 2}),
5106 json!({"oid": 16402, "relname": "drivers", "relnamespace": 2200,
5107 "relkind": "r", "relowner": 10, "relam": 2}),
5108 ],
5109 ),
5110 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5111 ])
5112 }
5113
5114 #[test]
5115 fn THE_dn_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5116 let (names, rows) = go(
5117 r#"SELECT n.nspname AS "Name",
5118 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5119 FROM pg_catalog.pg_namespace n
5120 WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5121 ORDER BY 1;"#,
5122 &catalog(),
5123 );
5124
5125 assert_eq!(names, vec!["Name", "Owner"], "psql reads these BY NAME");
5126 assert_eq!(col(&rows, "Name"), vec![json!("public")]);
5129 assert_eq!(col(&rows, "Owner"), vec![json!("nedb")]);
5130 }
5131
5132 #[test]
5133 fn THE_dt_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5134 let (names, rows) = go(
5135 r#"SELECT n.nspname as "Schema",
5136 c.relname as "Name",
5137 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5138 WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5139 WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5140 WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5141 WHEN 'I' THEN 'partitioned index' END as "Type",
5142 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5143 FROM pg_catalog.pg_class c
5144 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5145 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5146 WHERE c.relkind IN ('r','p','')
5147 AND n.nspname <> 'pg_catalog'
5148 AND n.nspname !~ '^pg_toast'
5149 AND n.nspname <> 'information_schema'
5150 AND pg_catalog.pg_table_is_visible(c.oid)
5151 ORDER BY 1,2;"#,
5152 &catalog(),
5153 );
5154
5155 assert_eq!(names, vec!["Schema", "Name", "Type", "Owner"]);
5156 assert_eq!(col(&rows, "Name"), vec![json!("drivers"), json!("orders")]);
5158 assert_eq!(col(&rows, "Schema"), vec![json!("public"), json!("public")]);
5159 assert_eq!(col(&rows, "Type"), vec![json!("table"), json!("table")]);
5161 assert_eq!(col(&rows, "Owner"), vec![json!("nedb"), json!("nedb")]);
5162 }
5163
5164 #[test]
5165 fn the_dt_query_still_filters_correctly_with_a_system_relation_present() {
5166 let t = tables(vec![
5170 (
5171 "pg_namespace",
5172 vec![
5173 json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5174 json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5175 ],
5176 ),
5177 (
5178 "pg_class",
5179 vec![
5180 json!({"oid": 1, "relname": "mine", "relnamespace": 2200,
5181 "relkind": "r", "relowner": 10, "relam": 2}),
5182 json!({"oid": 2, "relname": "pg_internal", "relnamespace": 11,
5183 "relkind": "r", "relowner": 10, "relam": 2}),
5184 json!({"oid": 3, "relname": "an_index", "relnamespace": 2200,
5185 "relkind": "i", "relowner": 10, "relam": 2}),
5186 ],
5187 ),
5188 ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5189 ]);
5190 let (_, rows) = go(
5191 r#"SELECT c.relname as "Name" FROM pg_catalog.pg_class c
5192 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5193 WHERE c.relkind IN ('r','p','') AND n.nspname <> 'pg_catalog'
5194 ORDER BY 1"#,
5195 &t,
5196 );
5197 assert_eq!(col(&rows, "Name"), vec![json!("mine")],
5198 "a system relation and an index must both be filtered out");
5199 }
5200}
5201
5202#[cfg(test)]
5203mod operator_syntax_tests {
5204 use super::*;
5205 use serde_json::json;
5206
5207 #[test]
5208 fn the_OPERATOR_qualification_psql_generates_is_understood() {
5209 let s = parse(
5213 "SELECT a FROM t WHERE n OPERATOR(pg_catalog.~) '^x' \
5214 AND m OPERATOR(pg_catalog.=) 1",
5215 )
5216 .expect("psql's OPERATOR() form must parse");
5217 match s.where_.unwrap() {
5218 Expr::Binary { op, left, .. } => {
5219 assert_eq!(op, "AND");
5220 assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "~"));
5221 }
5222 other => panic!("{:?}", other),
5223 }
5224 }
5225
5226 #[test]
5227 fn an_OPERATOR_qualified_comparison_EVALUATES() {
5228 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
5229 Ok(Some(from_vec(vec![json!({"n": "orders"}), json!({"n": "pg_toast_1"})])))
5230 };
5231 let (_, rows) = run(
5232 "SELECT n FROM pg_class WHERE n OPERATOR(pg_catalog.~) '^ord'",
5233 &t,
5234 )
5235 .unwrap();
5236 assert_eq!(rows.len(), 1);
5237 assert_eq!(rows[0]["n"], json!("orders"));
5238 }
5239
5240 #[test]
5241 fn a_subquery_an_ARRAY_constructor_and_EXISTS_all_PARSE() {
5242 let s = parse("SELECT a FROM t WHERE x = (SELECT 1)").unwrap();
5245 assert!(matches!(s.where_, Some(Expr::Binary { ref right, .. }) if matches!(**right, Expr::Subquery(_))));
5246 let s = parse("SELECT array_to_string(ARRAY(SELECT a FROM b), ',') FROM t").unwrap();
5247 assert!(matches!(&s.items[0].expr, Expr::Func { args, .. } if matches!(args[0], Expr::ArrayQuery(_))));
5248 let s = parse("SELECT a FROM t WHERE EXISTS (SELECT 1)").unwrap();
5249 assert!(matches!(s.where_, Some(Expr::Exists { negated: false, .. })));
5250 let s = parse("SELECT a FROM t WHERE NOT EXISTS (SELECT 1)").unwrap();
5251 assert!(matches!(s.where_, Some(Expr::Unary { ref expr, .. }) if matches!(**expr, Expr::Exists { .. })));
5252 let s = parse("SELECT a FROM t WHERE oid = ANY (polroles) AND 'd' = any(kinds) AND x <> ALL (SELECT y FROM u)").unwrap();
5254 assert!(s.where_.is_some());
5255 let s = parse("SELECT prattrs[s] FROM t").unwrap();
5256 assert!(matches!(s.items[0].expr, Expr::Index { .. }));
5257 let s = parse("SELECT CAST('tuple' AS pg_catalog.text), CAST(n AS int2[]) FROM t").unwrap();
5258 assert!(matches!(&s.items[0].expr, Expr::Cast { ty, .. } if ty == "text"));
5259 assert!(matches!(&s.items[1].expr, Expr::Cast { ty, .. } if ty == "int2[]"));
5260 let s = parse("SELECT a FROM t WHERE a IS DISTINCT FROM b").unwrap();
5261 assert!(matches!(s.where_, Some(Expr::Binary { ref op, .. }) if op == "IS DISTINCT FROM"));
5262 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();
5264 assert_eq!(s.joins.len(), 3);
5265 assert!(matches!(s.joins[1].kind, JoinKind::Cross));
5266 let s = parse("SELECT 1 FROM c, LATERAL (SELECT 2 AS two) s").unwrap();
5268 assert!(s.joins[0].table.lateral && s.joins[0].table.sub.is_some());
5269 let s = parse("SELECT tt.a FROM (SELECT 1 AS a UNION ALL SELECT 2) AS tt ORDER BY 1").unwrap();
5270 assert_eq!(s.from.as_ref().unwrap().sub.as_ref().unwrap().set_ops.len(), 1);
5271 }
5272
5273 #[test]
5274 fn a_compound_query_keeps_ORDER_BY_for_the_whole() {
5275 let s = parse("SELECT a FROM t UNION SELECT b FROM u UNION ALL SELECT c FROM v ORDER BY 1 LIMIT 5").unwrap();
5276 assert_eq!(s.set_ops.len(), 2);
5277 assert_eq!(s.set_ops[0].op, SetOp::Union);
5278 assert!(!s.set_ops[0].all);
5279 assert!(s.set_ops[1].all);
5280 assert_eq!(s.order_by.len(), 1);
5281 assert_eq!(s.limit, Some(5));
5282 assert!(s.set_ops[1].query.order_by.is_empty(), "the tail belongs to the whole, not the last arm");
5283 }
5284}
5285
5286#[cfg(test)]
5287mod subquery_exec_tests {
5288 use super::*;
5289 use serde_json::json;
5290
5291 fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5292 let owned: Vec<(String, Vec<Value>)> =
5293 defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
5294 move |name: &str| {
5295 let bare = name.rsplit('.').next().unwrap_or(name);
5296 Ok(owned.iter().find(|(n, _)| n == name || n == bare).map(|(_, r)| from_vec(r.clone())))
5297 }
5298 }
5299
5300 fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
5301 let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n -> {}", sql, e));
5302 (cols.into_iter().map(|c| c.name).collect(), rows)
5303 }
5304
5305 fn col(rows: &[Value], name: &str) -> Vec<Value> {
5306 rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
5307 }
5308
5309 fn shop() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5310 tables(vec![
5311 ("c", vec![
5312 json!({"id": 1, "name": "ann", "tags": ["a", "b"]}),
5313 json!({"id": 2, "name": "bob", "tags": []}),
5314 json!({"id": 3, "name": "cyd", "tags": null}),
5315 ]),
5316 ("o", vec![
5317 json!({"oid": 10, "cid": 1, "total": 5}),
5318 json!({"oid": 11, "cid": 1, "total": 7}),
5319 json!({"oid": 12, "cid": 2, "total": 9}),
5320 ]),
5321 ])
5322 }
5323
5324 #[test]
5325 fn a_correlated_scalar_subquery_sees_the_outer_row() {
5326 let t = shop();
5327 let (_, rows) = go(
5328 "SELECT c.name, (SELECT sum(o.total) FROM o WHERE o.cid = c.id) AS spent FROM c ORDER BY c.id",
5329 &t,
5330 );
5331 assert_eq!(col(&rows, "spent"), vec![json!(12), json!(9), Value::Null]);
5332 let e = run("SELECT (SELECT o.total FROM o WHERE o.cid = c.id) FROM c", &t).unwrap_err().to_string();
5334 assert!(e.contains("more than one row"), "{}", e);
5335 let e = run("SELECT (SELECT oid, total FROM o) FROM c", &t).unwrap_err().to_string();
5337 assert!(e.contains("exactly one column"), "{}", e);
5338 }
5339
5340 #[test]
5341 fn EXISTS_and_NOT_EXISTS_are_never_unknown() {
5342 let t = shop();
5343 let (_, rows) = go("SELECT c.name FROM c WHERE EXISTS (SELECT 1 FROM o WHERE o.cid = c.id) ORDER BY 1", &t);
5344 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5345 let (_, rows) = go("SELECT c.name FROM c WHERE NOT EXISTS (SELECT 1 FROM o WHERE o.cid = c.id)", &t);
5346 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5347 }
5348
5349 #[test]
5350 fn ARRAY_of_a_subquery_and_array_to_string_compose_like_psql_dp() {
5351 let t = shop();
5352 let (_, rows) = go(
5353 "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",
5354 &t,
5355 );
5356 assert_eq!(col(&rows, "totals"), vec![json!("5,7"), json!("9"), json!("")]);
5358 let (_, rows) = go("SELECT array_length(ARRAY(SELECT oid FROM o), 1) AS n FROM c WHERE c.id = 1", &t);
5359 assert_eq!(col(&rows, "n"), vec![json!(3)]);
5360 }
5361
5362 #[test]
5363 fn ANY_ALL_and_IN_over_arrays_and_subqueries() {
5364 let t = shop();
5365 let (_, rows) = go("SELECT c.name FROM c WHERE 'a' = ANY (c.tags) ORDER BY 1", &t);
5366 assert_eq!(col(&rows, "name"), vec![json!("ann")]);
5367 let (_, rows) = go("SELECT c.name FROM c WHERE 'zz' = ANY (c.tags)", &t);
5369 assert!(rows.is_empty());
5370 let (_, rows) = go("SELECT c.name FROM c WHERE c.id = ANY (SELECT o.cid FROM o) ORDER BY 1", &t);
5371 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5372 let (_, rows) = go("SELECT c.name FROM c WHERE c.id <> ALL (SELECT o.cid FROM o)", &t);
5373 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5374 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);
5375 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5376 let (_, rows) = go("SELECT c.name FROM c WHERE c.id NOT IN (SELECT o.cid FROM o)", &t);
5377 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5378 let (_, rows) = go("SELECT c.tags[2] AS second FROM c WHERE c.id = 1", &t);
5380 assert_eq!(col(&rows, "second"), vec![json!("b")]);
5381 }
5382
5383 #[test]
5384 fn set_operations_combine_arms_and_sort_the_whole() {
5385 let t = shop();
5386 let (names, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1", &t);
5387 assert_eq!(names, vec!["k"], "column names come from the first arm");
5388 assert_eq!(col(&rows, "k"), vec![json!(1), json!(1), json!(1), json!(2), json!(2), json!(3)]);
5389 let (_, rows) = go("SELECT c.id AS k FROM c UNION SELECT o.cid FROM o ORDER BY 1", &t);
5390 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2), json!(3)]);
5391 let (_, rows) = go("SELECT c.id AS k FROM c INTERSECT SELECT o.cid FROM o ORDER BY 1", &t);
5392 assert_eq!(col(&rows, "k"), vec![json!(1), json!(2)]);
5393 let (_, rows) = go("SELECT c.id AS k FROM c EXCEPT SELECT o.cid FROM o", &t);
5394 assert_eq!(col(&rows, "k"), vec![json!(3)]);
5395 let (_, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1 DESC LIMIT 2", &t);
5396 assert_eq!(col(&rows, "k"), vec![json!(3), json!(2)]);
5397 let e = run("SELECT c.id FROM c UNION SELECT o.oid, o.cid FROM o", &t).unwrap_err().to_string();
5398 assert!(e.contains("same number of columns"), "{}", e);
5399 }
5400
5401 #[test]
5402 fn a_derived_table_is_a_relation_and_LATERAL_sees_its_left() {
5403 let t = shop();
5404 let (_, rows) = go(
5405 "SELECT tt.who FROM (SELECT c.name AS who FROM c WHERE c.id < 3) AS tt ORDER BY 1",
5406 &t,
5407 );
5408 assert_eq!(col(&rows, "who"), vec![json!("ann"), json!("bob")]);
5409 let (_, rows) = go("SELECT tt.x FROM (SELECT c.name FROM c WHERE c.id = 1) AS tt(x)", &t);
5411 assert_eq!(col(&rows, "x"), vec![json!("ann")]);
5412 let (_, rows) = go(
5414 "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\"",
5415 &t,
5416 );
5417 assert_eq!(col(&rows, "Name"), vec![json!("ann"), json!("bob"), json!("cyd")]);
5418 assert_eq!(col(&rows, "Orders"), vec![json!(2), json!(1), json!(0)]);
5419 }
5420
5421 #[test]
5422 fn table_functions_generate_series_and_unnest() {
5423 let t = shop();
5424 let (_, rows) = go("SELECT s.generate_series AS n FROM generate_series(1, 3) s", &t);
5425 assert_eq!(col(&rows, "n"), vec![json!(1), json!(2), json!(3)]);
5426 let (_, rows) = go("SELECT x FROM pg_catalog.unnest(ARRAY['p', 'q']) AS t(x)", &t);
5427 assert_eq!(col(&rows, "x"), vec![json!("p"), json!("q")]);
5428 let (_, rows) = go(
5430 "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",
5431 &t,
5432 );
5433 assert_eq!(col(&rows, "tags"), vec![json!("a, b"), json!(""), json!("")]);
5434 let e = run("SELECT 1 FROM nosuchfn(1) f", &t).unwrap_err().to_string();
5435 assert!(e.contains("table function nosuchfn()"), "{}", e);
5436 }
5437
5438 #[test]
5439 fn aggregates_without_GROUP_BY_collapse_to_one_row() {
5440 let t = shop();
5441 let (names, rows) = go(
5442 "SELECT count(*), count(c.tags) AS tagged, min(c.name), max(c.name) AS hi, string_agg(c.name, '|') AS all FROM c",
5443 &t,
5444 );
5445 assert_eq!(names, vec!["count", "tagged", "min", "hi", "all"]);
5446 assert_eq!(rows.len(), 1);
5447 assert_eq!(rows[0]["count"], json!(3));
5448 assert_eq!(rows[0]["tagged"], json!(2), "count(x) skips NULL");
5449 assert_eq!(rows[0]["min"], json!("ann"));
5450 assert_eq!(rows[0]["hi"], json!("cyd"));
5451 assert_eq!(rows[0]["all"], json!("ann|bob|cyd"));
5452 let (_, rows) = go("SELECT count(*) AS n, sum(o.total) AS s FROM o WHERE o.total > 100", &t);
5454 assert_eq!(rows[0]["n"], json!(0));
5455 assert_eq!(rows[0]["s"], Value::Null);
5456 let (_, rows) = go("SELECT sum(o.total) / count(*) AS avg_total, avg(o.total) AS a FROM o", &t);
5459 assert_eq!(rows[0]["avg_total"], json!(7));
5460 assert_eq!(rows[0]["a"], json!(7));
5461 let e = run("SELECT c.name, count(*) FROM c", &t).unwrap_err().to_string();
5462 assert!(e.contains("must appear in the GROUP BY clause"), "{}", e);
5463 }
5464
5465 #[test]
5466 fn IS_DISTINCT_FROM_is_null_safe() {
5467 let t = shop();
5468 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS DISTINCT FROM NULL ORDER BY 1", &t);
5469 assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5470 let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS NOT DISTINCT FROM NULL", &t);
5471 assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5472 }
5473
5474 #[test]
5475 fn THE_dT_QUERY_RUNS_over_a_catalogue_fixture() {
5476 let t = tables(vec![
5478 ("pg_namespace", vec![
5479 json!({"oid": 11, "nspname": "pg_catalog"}),
5480 json!({"oid": 2200, "nspname": "public"}),
5481 ]),
5482 ("pg_type", vec![
5483 json!({"oid": 25, "typname": "text", "typnamespace": 11, "typrelid": 0, "typelem": 0, "typarray": 1009}),
5484 json!({"oid": 1009, "typname": "_text", "typnamespace": 11, "typrelid": 0, "typelem": 25, "typarray": 0}),
5485 json!({"oid": 70000, "typname": "mood", "typnamespace": 2200, "typrelid": 0, "typelem": 0, "typarray": 70001}),
5486 json!({"oid": 70001, "typname": "_mood", "typnamespace": 2200, "typrelid": 0, "typelem": 70000, "typarray": 0}),
5487 ]),
5488 ("pg_class", vec![]),
5489 ]);
5490 let (_, rows) = go(
5491 r#"SELECT n.nspname as "Schema",
5492 pg_catalog.format_type(t.oid, NULL) AS "Name",
5493 pg_catalog.obj_description(t.oid, 'pg_type') as "Description"
5494 FROM pg_catalog.pg_type t
5495 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
5496 WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
5497 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
5498 AND n.nspname <> 'pg_catalog'
5499 AND n.nspname <> 'information_schema'
5500 AND pg_catalog.pg_type_is_visible(t.oid)
5501 ORDER BY 1, 2;"#,
5502 &t,
5503 );
5504 assert_eq!(rows.len(), 1, "{:?}", rows);
5507 assert_eq!(col(&rows, "Schema"), vec![json!("public")]);
5508 }
5509}
5510
5511#[cfg(test)]
5512mod collate_tests {
5513 use super::*;
5514 use serde_json::json;
5515
5516 #[test]
5517 fn COLLATE_is_consumed_because_it_cannot_change_the_answer() {
5518 for sql in [
5522 r#"SELECT a FROM t ORDER BY a COLLATE "C""#,
5523 r#"SELECT a COLLATE "C" FROM t"#,
5524 r#"SELECT a FROM t WHERE a COLLATE pg_catalog."C" = 'x'"#,
5525 ] {
5526 parse(sql).unwrap_or_else(|e| panic!("{} -> {}", sql, e));
5527 }
5528 assert!(parse("SELECT a FROM t ORDER BY a COLLATE").is_err());
5530 }
5531
5532 #[test]
5533 fn a_COLLATE_annotated_comparison_still_evaluates() {
5534 let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
5535 Ok(Some(from_vec(vec![json!({"n": "b"}), json!({"n": "a"})])))
5536 };
5537 let (_, rows) = run(r#"SELECT n FROM pg_class ORDER BY n COLLATE "C""#, &t).unwrap();
5538 assert_eq!(rows[0]["n"], json!("a"), "the ORDER BY still sorts");
5539 }
5540}