1use std::collections::{BTreeMap, BTreeSet};
71use std::fmt::Write as _;
72use std::sync::Arc;
73
74use crate::core::Value;
75use crate::plan::{Agg, OpId, Plan};
76use crate::split::Placed;
77use crate::ty::{Ty, TyDecl};
78
79#[derive(Clone, Copy, Debug, PartialEq, Eq)]
89pub enum SqlTy {
90 Boolean,
91 Bigint,
92 Double,
93 Text,
94}
95
96impl SqlTy {
97 pub fn oid(self) -> u32 {
99 match self {
100 SqlTy::Boolean => 16,
101 SqlTy::Bigint => 20,
102 SqlTy::Text => 25,
103 SqlTy::Double => 701,
104 }
105 }
106
107 pub fn width(self) -> i16 {
109 match self {
110 SqlTy::Boolean => 1,
111 SqlTy::Bigint | SqlTy::Double => 8,
112 SqlTy::Text => -1,
113 }
114 }
115
116 pub fn name(self) -> &'static str {
117 match self {
118 SqlTy::Boolean => "boolean",
119 SqlTy::Bigint => "bigint",
120 SqlTy::Double => "double precision",
121 SqlTy::Text => "text",
122 }
123 }
124}
125
126#[derive(Clone, Debug)]
127pub struct Column {
128 pub name: Arc<str>,
129 pub ty: SqlTy,
130 pub nullable: bool,
133}
134
135#[derive(Clone, Debug, PartialEq, Eq)]
137pub enum Source {
138 State(Vec<Arc<str>>),
143 View(OpId),
148 Catalogue,
150 Pg(crate::pg::Rel),
152}
153
154#[derive(Clone, Copy, Debug, PartialEq, Eq)]
156pub enum Cardinality {
157 Many,
159 One,
161}
162
163#[derive(Clone, Debug)]
164pub struct Table {
165 pub name: Arc<str>,
166 pub columns: Vec<Column>,
167 pub source: Source,
168 pub cardinality: Cardinality,
169 pub element: Arc<str>,
171}
172
173impl Table {
174 pub fn column(&self, name: &str) -> Option<(usize, &Column)> {
175 self.columns
176 .iter()
177 .enumerate()
178 .find(|(_, c)| c.name.as_ref() == name)
179 }
180
181 pub fn row_values(&self, v: &Value) -> Vec<Value> {
191 match unwrap(v) {
192 Value::Data(d) if d.variant.is_none() && !d.fields.is_empty() => self
194 .columns
195 .iter()
196 .map(|c| match d.fields.get(&c.name) {
197 Some(f) => column_value(f),
198 None => Value::Unit,
199 })
200 .collect(),
201 other => self.columns.iter().map(|_| column_value(other)).collect(),
203 }
204 }
205
206 pub fn row(&self, v: &Value) -> Vec<Cell> {
213 self.row_values(v)
214 .iter()
215 .zip(&self.columns)
216 .map(|(v, c)| cell_of(v, c))
217 .collect()
218 }
219}
220
221pub type Cell = Option<Datum>;
223
224#[derive(Clone, Debug, PartialEq)]
225pub enum Datum {
226 Boolean(bool),
227 Bigint(i64),
228 Double(f64),
229 Text(String),
230}
231
232impl Datum {
233 pub fn ty(&self) -> SqlTy {
234 match self {
235 Datum::Boolean(_) => SqlTy::Boolean,
236 Datum::Bigint(_) => SqlTy::Bigint,
237 Datum::Double(_) => SqlTy::Double,
238 Datum::Text(_) => SqlTy::Text,
239 }
240 }
241
242 pub fn text(&self) -> String {
245 match self {
246 Datum::Boolean(b) => if *b { "t" } else { "f" }.to_string(),
247 Datum::Bigint(i) => i.to_string(),
248 Datum::Double(f) => f.to_string(),
252 Datum::Text(s) => s.clone(),
253 }
254 }
255}
256
257pub fn cell_of(v: &Value, c: &Column) -> Cell {
259 let v = unwrap(v);
260 if let Value::Data(d) = v {
263 if d.variant.as_deref() == Some("None") {
264 return None;
265 }
266 if d.variant.as_deref() == Some("Some") {
267 return match d.fields.values().next() {
268 Some(inner) => cell_of(inner, c),
269 None => None,
270 };
271 }
272 }
273 match (c.ty, v) {
274 (SqlTy::Boolean, Value::Bool(b)) => Some(Datum::Boolean(*b)),
275 (SqlTy::Bigint, Value::Int(i)) => Some(Datum::Bigint(*i)),
276 (SqlTy::Double, _) => v.as_f64().map(Datum::Double),
277 (SqlTy::Text, Value::Str(s)) => Some(Datum::Text(s.to_string())),
278 (SqlTy::Text, other) => Some(Datum::Text(match other {
282 Value::Unit => return None,
283 _ => serde_json::to_string(&other.to_json()).unwrap_or_else(|_| other.display()),
284 })),
285 _ => None,
286 }
287}
288
289fn column_value(v: &Value) -> Value {
298 let v = unwrap(v);
299 if let Value::Data(d) = v {
300 match d.variant.as_deref() {
301 Some("None") => return Value::Unit,
302 Some("Some") => {
303 return match d.fields.values().next() {
304 Some(inner) => column_value(inner),
305 None => Value::Unit,
306 }
307 }
308 _ => {}
309 }
310 }
311 v.clone()
312}
313
314fn unwrap(v: &Value) -> &Value {
316 match v {
317 Value::Data(d) if d.variant.is_none() && d.fields.len() == 1 => {
318 match d.fields.values().next() {
319 Some(inner) => unwrap(inner),
320 None => v,
321 }
322 }
323 _ => v,
324 }
325}
326
327#[derive(Clone, Debug, Default)]
333pub struct Schema {
334 pub tables: Vec<Table>,
336 pub pg: Vec<Table>,
344}
345
346impl Schema {
347 pub const CATALOGUE: &'static str = "beck_columns";
350
351 pub fn table(&self, name: &str) -> Option<&Table> {
352 self.tables.iter().find(|t| t.name.as_ref() == name)
353 }
354
355 pub fn relation(&self, namespace: Option<&str>, name: &str) -> Result<&Table, SqlError> {
362 match namespace {
363 None => self
364 .table(name)
365 .or_else(|| self.pg.iter().find(|t| t.name.as_ref() == name))
366 .ok_or_else(|| {
367 SqlError::no_table(format!(
368 "there is no read model called \"{name}\". `select * from {}` lists what \
369 there is",
370 Schema::CATALOGUE
371 ))
372 }),
373 Some(crate::pg::CATALOG) => self
374 .pg
375 .iter()
376 .find(|t| t.name.as_ref() == name)
377 .ok_or_else(|| SqlError::no_table(crate::pg::Rel::missing(name))),
378 Some(crate::pg::PUBLIC) => self.table(name).ok_or_else(|| {
379 SqlError::no_table(format!(
380 "there is no read model called \"{name}\". `select * from {}` lists what \
381 there is",
382 Schema::CATALOGUE
383 ))
384 }),
385 Some(other) => Err(SqlError::no_table(format!(
386 "there is no schema called \"{other}\" here. A program's read models are in \
387 \"{}\" and the catalogue that describes them is in \"{}\"",
388 crate::pg::PUBLIC,
389 crate::pg::CATALOG
390 ))),
391 }
392 }
393
394 pub fn builtin_rows(&self, t: &Table) -> Option<Vec<Value>> {
399 match &t.source {
400 Source::Catalogue => Some(self.catalogue_values()),
401 Source::Pg(rel) => Some(crate::pg::rows(*rel, self)),
402 _ => None,
403 }
404 }
405
406 pub fn of(placed: &Placed, plan: &Plan) -> Schema {
408 let types = &placed.program.types;
409 let mut tables: Vec<Table> = Vec::new();
410 let mut taken: BTreeSet<Arc<str>> = BTreeSet::new();
411
412 for role in &placed.roles.states {
413 let base: Vec<Arc<str>> = role.field.iter().cloned().collect();
414 let ty = resolve(&role.ty, types);
415 match collection_elem(&ty, types) {
416 Some(elem) => push(
418 &mut tables,
419 &mut taken,
420 table(
421 role.name.clone(),
422 &elem,
423 types,
424 Source::State(base),
425 Cardinality::Many,
426 ),
427 ),
428 None => {
429 let fields = model_fields(&ty, types).unwrap_or_default();
430 let mut scalars: Vec<(Arc<str>, Ty)> = Vec::new();
431 for (name, fty) in fields {
432 let fty = resolve(&fty, types);
433 match collection_elem(&fty, types) {
434 Some(elem) => {
435 let mut path = base.clone();
436 path.push(name.clone());
437 push(
438 &mut tables,
439 &mut taken,
440 table(
441 name,
442 &elem,
443 types,
444 Source::State(path),
445 Cardinality::Many,
446 ),
447 );
448 }
449 None => scalars.push((name, fty)),
450 }
451 }
452 if !scalars.is_empty() {
457 push(
458 &mut tables,
459 &mut taken,
460 Table {
461 name: role.name.clone(),
462 columns: scalars
463 .iter()
464 .map(|(n, t)| column(n.clone(), t, types))
465 .collect(),
466 source: Source::State(base.clone()),
467 cardinality: Cardinality::One,
468 element: Arc::from(ty.to_string()),
469 },
470 );
471 }
472 }
473 }
474 }
475
476 let by_op: BTreeMap<&str, OpId> = plan
479 .signals
480 .iter()
481 .map(|(n, id)| (n.as_ref(), *id))
482 .collect();
483 let folds: BTreeSet<&str> = placed
484 .roles
485 .states
486 .iter()
487 .map(|s| s.name.as_ref())
488 .collect();
489 for (name, &sig) in &placed.graph.by_name {
490 let Some(&op) = by_op.get(name.as_ref()) else {
491 continue;
492 };
493 if plan.nodes[op].per_session {
494 continue;
495 }
496 if folds.contains(name.as_ref()) {
500 continue;
501 }
502 let ty = resolve(
503 &crate::signal::signal_elem(&placed.graph.node(sig).ty),
504 types,
505 );
506 let t = match collection_elem(&ty, types) {
507 Some(elem) => table(
508 name.clone(),
509 &elem,
510 types,
511 Source::View(op),
512 Cardinality::Many,
513 ),
514 None if model_fields(&ty, types).is_some() || scalar(&ty).is_some() => {
518 table(name.clone(), &ty, types, Source::View(op), Cardinality::One)
519 }
520 None => continue,
521 };
522 push(&mut tables, &mut taken, t);
523 }
524
525 tables.sort_by(|a, b| a.name.cmp(&b.name));
526 tables.push(Table {
527 name: Arc::from(Schema::CATALOGUE),
528 columns: [
529 "table_name",
530 "column_name",
531 "data_type",
532 "nullable",
533 "position",
534 ]
535 .iter()
536 .enumerate()
537 .map(|(i, n)| Column {
538 name: Arc::from(*n),
539 ty: if i == 4 {
540 SqlTy::Bigint
541 } else if i == 3 {
542 SqlTy::Boolean
543 } else {
544 SqlTy::Text
545 },
546 nullable: false,
547 })
548 .collect(),
549 source: Source::Catalogue,
550 cardinality: Cardinality::Many,
551 element: Arc::from("Column"),
552 });
553 Schema {
554 tables,
555 pg: crate::pg::relations(),
556 }
557 }
558
559 pub fn catalogue_values(&self) -> Vec<Value> {
564 let mut rows = Vec::new();
565 for t in &self.tables {
566 for (i, c) in t.columns.iter().enumerate() {
567 rows.push(Value::record(
568 "Column",
569 None,
570 [
571 ("table_name", Value::text(t.name.to_string())),
572 ("column_name", Value::text(c.name.to_string())),
573 ("data_type", Value::text(c.ty.name().to_string())),
574 ("nullable", Value::Bool(c.nullable)),
575 ("position", Value::Int(i as i64 + 1)),
576 ],
577 ));
578 }
579 }
580 rows
581 }
582
583 pub fn ddl(&self) -> String {
588 let mut out = String::new();
589 for t in &self.tables {
590 let what = match &t.source {
591 Source::State(path) if path.is_empty() => "the accumulator".to_string(),
592 Source::State(path) => format!("state.{}", join(path)),
593 Source::View(op) => format!("plan operator {op}, maintained and shared"),
594 Source::Catalogue => "this schema".to_string(),
595 Source::Pg(rel) => format!("this schema, as {}", rel.name()),
596 };
597 let _ = writeln!(
598 out,
599 "-- {} of {}, from {what}",
600 match t.cardinality {
601 Cardinality::Many => "the elements",
602 Cardinality::One => "one row",
603 },
604 t.element
605 );
606 let _ = writeln!(out, "create table {} (", quote_ident(&t.name));
607 let n = t.columns.len();
608 for (i, c) in t.columns.iter().enumerate() {
609 let _ = writeln!(
610 out,
611 " {:<20} {}{}{}",
612 quote_ident(&c.name),
613 c.ty.name(),
614 if c.nullable { "" } else { " not null" },
615 if i + 1 == n { "" } else { "," }
616 );
617 }
618 let _ = writeln!(out, ");");
619 }
620 out
621 }
622}
623
624const RESERVED: &[&str] = &[
630 "abort", "and", "as", "asc", "begin", "by", "commit", "count", "cross", "desc", "discard",
631 "distinct", "end", "false", "from", "full", "group", "having", "inner", "is", "join", "left",
632 "limit", "natural", "not", "null", "offset", "on", "or", "order", "outer", "right", "rollback",
633 "select", "set", "start", "table", "true", "where",
634];
635
636pub fn quote_ident(name: &str) -> String {
638 let plain = !name.is_empty()
639 && !name.starts_with(|c: char| c.is_ascii_digit())
640 && name
641 .chars()
642 .all(|c| c == '_' || c.is_ascii_lowercase() || c.is_ascii_digit());
643 if plain && !RESERVED.contains(&name) {
644 return name.to_string();
645 }
646 format!("\"{}\"", name.replace('"', "\"\""))
647}
648
649fn join(path: &[Arc<str>]) -> String {
650 path.iter()
651 .map(|p| p.to_string())
652 .collect::<Vec<_>>()
653 .join(".")
654}
655
656fn push(tables: &mut Vec<Table>, taken: &mut BTreeSet<Arc<str>>, t: Table) {
659 if taken.insert(t.name.clone()) {
660 tables.push(t);
661 }
662}
663
664fn table(
665 name: Arc<str>,
666 elem: &Ty,
667 types: &BTreeMap<Arc<str>, TyDecl>,
668 source: Source,
669 cardinality: Cardinality,
670) -> Table {
671 let elem = resolve(elem, types);
672 let columns = match model_fields(&elem, types) {
673 Some(fields) => fields
674 .into_iter()
675 .map(|(n, t)| column(n, &t, types))
676 .collect(),
677 None => vec![column(Arc::from("value"), &elem, types)],
679 };
680 Table {
681 name,
682 columns,
683 source,
684 cardinality,
685 element: Arc::from(elem.to_string()),
686 }
687}
688
689fn column(name: Arc<str>, ty: &Ty, types: &BTreeMap<Arc<str>, TyDecl>) -> Column {
690 let (ty, nullable) = sql_ty(ty, types);
691 Column { name, ty, nullable }
692}
693
694fn sql_ty(ty: &Ty, types: &BTreeMap<Arc<str>, TyDecl>) -> (SqlTy, bool) {
696 let ty = resolve(ty, types);
697 if let Ty::Con(n, args) = &ty {
698 if n.as_ref() == Ty::OPTION && args.len() == 1 {
699 return (sql_ty(&args[0], types).0, true);
700 }
701 }
702 (scalar(&ty).unwrap_or(SqlTy::Text), false)
703}
704
705fn scalar(ty: &Ty) -> Option<SqlTy> {
707 match ty {
708 Ty::Con(n, args) if args.is_empty() => match n.as_ref() {
709 Ty::INT => Some(SqlTy::Bigint),
710 Ty::FLOAT => Some(SqlTy::Double),
711 Ty::BOOL => Some(SqlTy::Boolean),
712 Ty::STR => Some(SqlTy::Text),
713 _ => None,
714 },
715 _ => None,
716 }
717}
718
719fn resolve(ty: &Ty, types: &BTreeMap<Arc<str>, TyDecl>) -> Ty {
722 let mut ty = ty.clone();
723 for _ in 0..16 {
726 let Ty::Con(name, args) = &ty else { return ty };
727 let next = match types.get(name) {
728 Some(TyDecl::Newtype { params, inner, .. }) => substitute(inner, params, args),
729 Some(TyDecl::Alias { params, ty: t, .. }) => substitute(t, params, args),
730 _ => return ty,
731 };
732 ty = next;
733 }
734 ty
735}
736
737fn substitute(ty: &Ty, params: &[Arc<str>], args: &[Ty]) -> Ty {
738 if params.is_empty() {
739 return ty.clone();
740 }
741 match ty {
742 Ty::Con(n, inner) if inner.is_empty() => match params.iter().position(|p| p == n) {
743 Some(i) if i < args.len() => args[i].clone(),
744 _ => ty.clone(),
745 },
746 Ty::Con(n, inner) => Ty::Con(
747 n.clone(),
748 inner.iter().map(|t| substitute(t, params, args)).collect(),
749 ),
750 _ => ty.clone(),
751 }
752}
753
754fn collection_elem(ty: &Ty, types: &BTreeMap<Arc<str>, TyDecl>) -> Option<Ty> {
756 match resolve(ty, types) {
757 Ty::Con(n, args) if n.as_ref() == Ty::LIST && args.len() == 1 => Some(args[0].clone()),
758 Ty::Con(n, args) if n.as_ref() == Ty::MAP && args.len() == 2 => Some(args[1].clone()),
759 _ => None,
760 }
761}
762
763fn model_fields(ty: &Ty, types: &BTreeMap<Arc<str>, TyDecl>) -> Option<Vec<(Arc<str>, Ty)>> {
770 let Ty::Con(name, args) = resolve(ty, types) else {
771 return None;
772 };
773 match types.get(&name) {
774 Some(TyDecl::Model { params, fields, .. }) if !fields.is_empty() => Some(
775 fields
776 .iter()
777 .map(|(n, t)| (n.clone(), substitute(t, params, &args)))
778 .collect(),
779 ),
780 _ => None,
781 }
782}
783
784pub fn elements(v: &Value) -> Vec<Value> {
786 match v {
787 Value::List(xs) => xs.to_vec(),
788 Value::Map(m) => m.iter().map(|(_, v)| v.clone()).collect(),
789 other => vec![other.clone()],
790 }
791}
792
793pub fn at_path(v: &Value, path: &[Arc<str>]) -> Option<Value> {
795 let mut cur = v.clone();
796 for step in path {
797 let Value::Data(d) = &cur else { return None };
798 cur = d.fields.get(step)?.clone();
799 }
800 Some(cur)
801}
802
803#[derive(Clone, Debug)]
820pub struct Select {
821 pub distinct: bool,
823 pub items: Vec<Item>,
824 pub from: Vec<From>,
827 pub filter: Vec<Expr>,
833 pub group: Vec<Name>,
835 pub order: Vec<Order>,
838 pub limit: Option<usize>,
839 pub offset: usize,
840}
841
842#[derive(Clone, Debug)]
844pub struct Order {
845 pub by: OrderBy,
846 pub asc: bool,
847}
848
849#[derive(Clone, Debug)]
851pub enum OrderBy {
852 Ordinal(usize),
854 Expr(Expr),
859}
860
861#[derive(Clone, Debug)]
863pub struct From {
864 pub namespace: Option<String>,
866 pub table: String,
868 pub alias: String,
870 pub on: Vec<(Name, Name)>,
874 pub left: bool,
878 pub function: Option<String>,
882}
883
884#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
886pub struct Name {
887 pub table: Option<String>,
888 pub column: String,
889}
890
891impl Name {
892 pub fn bare(column: impl Into<String>) -> Name {
893 Name {
894 table: None,
895 column: column.into(),
896 }
897 }
898}
899
900impl std::fmt::Display for Name {
901 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
902 match &self.table {
903 Some(t) => write!(f, "{t}.{}", self.column),
904 None => f.write_str(&self.column),
905 }
906 }
907}
908
909#[derive(Clone, Debug)]
910pub enum Item {
911 All(Option<String>),
913 Column(Name, Option<String>),
914 Count(Option<String>),
915 Aggregate(Agg, Name, Option<String>),
919 Literal(Datum, Option<String>),
920 Expr(Expr, Option<String>),
927}
928
929impl Item {
930 pub fn aggregates(&self) -> bool {
932 matches!(self, Item::Count(_) | Item::Aggregate(..))
933 }
934}
935
936#[derive(Clone, Debug)]
941pub enum Expr {
942 Column(Name),
943 Literal(Cell),
945 And(Vec<Expr>),
946 Or(Vec<Expr>),
947 Not(Box<Expr>),
948 Cmp(Box<Expr>, CmpOp, Box<Expr>),
951 Is {
953 value: Box<Expr>,
954 to: Option<bool>,
956 negated: bool,
957 },
958 In {
960 value: Box<Expr>,
961 list: Vec<Expr>,
962 negated: bool,
963 },
964 Match {
967 value: Box<Expr>,
968 pattern: Box<Expr>,
969 negated: bool,
970 insensitive: bool,
971 },
972 Case {
979 operand: Option<Box<Expr>>,
980 arms: Vec<(Expr, Expr)>,
981 otherwise: Option<Box<Expr>>,
982 },
983 Call {
986 name: String,
987 args: Vec<Expr>,
988 },
989 Cast {
992 value: Box<Expr>,
993 ty: String,
994 },
995 Concat(Box<Expr>, Box<Expr>),
997 Subquery {
1003 id: usize,
1004 select: Box<Select>,
1005 },
1006 Array {
1010 id: usize,
1011 select: Box<Select>,
1012 },
1013 Any(Box<Expr>),
1014 Subscript(Box<Expr>, Box<Expr>),
1017}
1018
1019impl Expr {
1020 pub fn names(&self, out: &mut Vec<Name>) {
1026 match self {
1027 Expr::Column(n) => out.push(n.clone()),
1028 Expr::Literal(_) => {}
1029 Expr::And(xs) | Expr::Or(xs) => xs.iter().for_each(|x| x.names(out)),
1030 Expr::Not(x) | Expr::Any(x) => x.names(out),
1031 Expr::Cmp(a, _, b) | Expr::Concat(a, b) | Expr::Subscript(a, b) => {
1032 a.names(out);
1033 b.names(out);
1034 }
1035 Expr::Is { value, .. } | Expr::Cast { value, .. } => value.names(out),
1036 Expr::In { value, list, .. } => {
1037 value.names(out);
1038 list.iter().for_each(|x| x.names(out));
1039 }
1040 Expr::Match { value, pattern, .. } => {
1041 value.names(out);
1042 pattern.names(out);
1043 }
1044 Expr::Case {
1045 operand,
1046 arms,
1047 otherwise,
1048 } => {
1049 if let Some(o) = operand {
1050 o.names(out);
1051 }
1052 for (w, t) in arms {
1053 w.names(out);
1054 t.names(out);
1055 }
1056 if let Some(e) = otherwise {
1057 e.names(out);
1058 }
1059 }
1060 Expr::Call { args, .. } => args.iter().for_each(|x| x.names(out)),
1061 Expr::Subquery { select, .. } | Expr::Array { select, .. } => {
1062 for item in &select.items {
1063 match item {
1064 Item::Column(n, _) => out.push(n.clone()),
1065 Item::Aggregate(_, n, _) => out.push(n.clone()),
1066 Item::Expr(e, _) => e.names(out),
1067 _ => {}
1068 }
1069 }
1070 select.filter.iter().for_each(|x| x.names(out));
1071 }
1072 }
1073 }
1074}
1075
1076#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1077pub enum CmpOp {
1078 Eq,
1079 Ne,
1080 Lt,
1081 Le,
1082 Gt,
1083 Ge,
1084}
1085
1086impl CmpOp {
1087 fn holds(self, o: std::cmp::Ordering) -> bool {
1088 match self {
1089 CmpOp::Eq => o.is_eq(),
1090 CmpOp::Ne => o.is_ne(),
1091 CmpOp::Lt => o.is_lt(),
1092 CmpOp::Le => o.is_le(),
1093 CmpOp::Gt => o.is_gt(),
1094 CmpOp::Ge => o.is_ge(),
1095 }
1096 }
1097}
1098
1099#[derive(Clone, Debug)]
1102pub enum Stmt {
1103 Select(Select),
1104 Union {
1110 branches: Vec<Select>,
1111 all: bool,
1112 order: Vec<Order>,
1113 limit: Option<usize>,
1114 offset: usize,
1115 },
1116 Ignored(&'static str),
1120}
1121
1122pub struct Answer {
1124 pub columns: Vec<Column>,
1125 pub rows: Vec<Vec<Cell>>,
1126 pub tag: String,
1128}
1129
1130#[derive(Clone, Debug, PartialEq, Eq)]
1132pub struct SqlError {
1133 pub message: String,
1134 pub code: &'static str,
1136}
1137
1138impl SqlError {
1139 pub fn syntax(m: impl Into<String>) -> SqlError {
1140 SqlError {
1141 message: m.into(),
1142 code: "42601",
1143 }
1144 }
1145 pub fn no_table(m: impl Into<String>) -> SqlError {
1146 SqlError {
1147 message: m.into(),
1148 code: "42P01",
1149 }
1150 }
1151 pub fn no_column(m: impl Into<String>) -> SqlError {
1152 SqlError {
1153 message: m.into(),
1154 code: "42703",
1155 }
1156 }
1157 pub fn unsupported(m: impl Into<String>) -> SqlError {
1158 SqlError {
1159 message: m.into(),
1160 code: "0A000",
1161 }
1162 }
1163}
1164
1165impl std::fmt::Display for SqlError {
1166 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1167 f.write_str(&self.message)
1168 }
1169}
1170
1171impl std::error::Error for SqlError {}
1172
1173pub trait Rows {
1175 fn scan(&self, table: &Table) -> Result<Vec<Value>, SqlError>;
1177
1178 fn count(&self, table: &Table) -> Result<Option<u64>, SqlError> {
1190 let _ = table;
1191 Ok(None)
1192 }
1193
1194 fn backend(&self) -> Option<&dyn crate::backend::Backend> {
1207 None
1208 }
1209}
1210
1211#[derive(Clone, Debug)]
1218pub struct Field {
1219 pub column: Column,
1220 pub of: Option<Arc<str>>,
1223}
1224
1225impl Field {
1226 pub fn of_table(t: &Table) -> Vec<Field> {
1228 Field::of_table_as(t, t.name.clone())
1229 }
1230
1231 pub fn of_table_as(t: &Table, alias: Arc<str>) -> Vec<Field> {
1238 t.columns
1239 .iter()
1240 .map(|c| Field {
1241 column: c.clone(),
1242 of: Some(alias.clone()),
1243 })
1244 .collect()
1245 }
1246}
1247
1248impl Schema {
1249 pub fn run(&self, sql: &str, rows: &dyn Rows) -> Result<Answer, SqlError> {
1251 match parse(sql)? {
1252 Stmt::Ignored(tag) => Ok(Answer {
1253 columns: Vec::new(),
1254 rows: Vec::new(),
1255 tag: tag.to_string(),
1256 }),
1257 Stmt::Select(s) => self.select(&s, rows),
1258 Stmt::Union {
1259 branches,
1260 all,
1261 order,
1262 limit,
1263 offset,
1264 } => self.union(&branches, all, &order, limit, offset, rows),
1265 }
1266 }
1267
1268 #[allow(clippy::too_many_arguments)]
1272 fn union(
1273 &self,
1274 branches: &[Select],
1275 all: bool,
1276 order: &[Order],
1277 limit: Option<usize>,
1278 offset: usize,
1279 rows_of: &dyn Rows,
1280 ) -> Result<Answer, SqlError> {
1281 let mut columns: Vec<Column> = Vec::new();
1282 let mut rows: Vec<Vec<Cell>> = Vec::new();
1283 for (i, b) in branches.iter().enumerate() {
1284 let answer = self.select(b, rows_of)?;
1285 if i == 0 {
1286 columns = answer.columns;
1287 } else if answer.columns.len() != columns.len() {
1288 return Err(SqlError::syntax(format!(
1289 "each `union` branch has to answer the same number of columns; the first \
1290 answers {} and this one answers {}",
1291 columns.len(),
1292 answer.columns.len()
1293 )));
1294 }
1295 rows.extend(answer.rows);
1296 }
1297 if !all {
1298 let mut seen = BTreeSet::new();
1299 rows.retain(|r| seen.insert(row_key(r)));
1300 }
1301 let fields: Vec<Field> = columns
1302 .iter()
1303 .map(|c| Field {
1304 column: c.clone(),
1305 of: None,
1306 })
1307 .collect();
1308 let proj: Vec<Proj> = (0..columns.len()).map(Proj::Column).collect();
1309 let ev = Eval::new(self, &fields, rows_of);
1310 let mut rows = order_rows(&ev, order, &columns, &proj, rows)?;
1311 cut(&mut rows, offset, limit);
1312 Ok(Answer {
1313 tag: format!("SELECT {}", rows.len()),
1314 columns,
1315 rows,
1316 })
1317 }
1318
1319 pub fn describe(&self, sql: &str) -> Result<Vec<Column>, SqlError> {
1322 match parse(sql)? {
1323 Stmt::Ignored(_) => Ok(Vec::new()),
1324 Stmt::Union { branches, .. } => match branches.first() {
1325 Some(first) => self.describe_select(first),
1326 None => Ok(Vec::new()),
1327 },
1328 Stmt::Select(s) => self.describe_select(&s),
1329 }
1330 }
1331
1332 fn describe_select(&self, s: &Select) -> Result<Vec<Column>, SqlError> {
1333 if crate::query::relational(s) {
1334 let compiled = crate::query::compile(self, s)?;
1335 if compiled.projected {
1336 return Ok(compiled.fields.iter().map(|f| f.column.clone()).collect());
1337 }
1338 return Ok(self.project_columns(s, &compiled.fields)?.0);
1339 }
1340 let fields = self.scan_fields(s)?;
1341 Ok(self.project_columns(s, &fields)?.0)
1342 }
1343
1344 fn scan_fields(&self, s: &Select) -> Result<Vec<Field>, SqlError> {
1346 Ok(match (self.resolve_from(s)?, s.from.first()) {
1347 (Some(t), Some(f)) => Field::of_table_as(t, Arc::from(f.alias.as_str())),
1348 _ => Vec::new(),
1349 })
1350 }
1351
1352 fn resolve_from(&self, s: &Select) -> Result<Option<&Table>, SqlError> {
1354 match s.from.first() {
1355 None => Ok(None),
1356 Some(f) => {
1357 if let Some(call) = &f.function {
1358 return Err(SqlError::unsupported(format!(
1359 "`{call}(…)` in a `from` is a set-returning function, and this read model \
1360 has none: what a `from` names here is a relation"
1361 )));
1362 }
1363 self.relation(f.namespace.as_deref(), &f.table).map(Some)
1364 }
1365 }
1366 }
1367
1368 fn project_columns(
1370 &self,
1371 s: &Select,
1372 fields: &[Field],
1373 ) -> Result<(Vec<Column>, Vec<Proj>), SqlError> {
1374 let mut columns = Vec::new();
1375 let mut proj = Vec::new();
1376 for item in &s.items {
1377 match item {
1378 Item::All(qualifier) => {
1379 if fields.is_empty() {
1380 return Err(SqlError::syntax("`select *` needs a `from`"));
1381 }
1382 for (i, f) in fields.iter().enumerate() {
1383 if let Some(t) = qualifier {
1384 if f.of.as_deref() != Some(t.as_str()) {
1385 continue;
1386 }
1387 }
1388 columns.push(f.column.clone());
1389 proj.push(Proj::Column(i));
1390 }
1391 }
1392 Item::Column(name, alias) => {
1393 if fields.is_empty() {
1394 return Err(SqlError::no_column(format!(
1395 "there is no column \"{name}\" here, because there is no `from`"
1396 )));
1397 }
1398 let i = resolve_field(fields, name)?;
1399 let mut c = fields[i].column.clone();
1400 if let Some(a) = alias {
1401 c.name = Arc::from(a.as_str());
1402 }
1403 columns.push(c);
1404 proj.push(Proj::Column(i));
1405 }
1406 Item::Count(alias) => {
1407 columns.push(Column {
1408 name: Arc::from(alias.as_deref().unwrap_or("count")),
1409 ty: SqlTy::Bigint,
1410 nullable: false,
1411 });
1412 proj.push(Proj::Count);
1413 }
1414 Item::Aggregate(agg, name, _) => {
1418 return Err(SqlError::unsupported(format!(
1419 "`{}({name})` is a question about a group, and this query has none",
1420 agg.name()
1421 )))
1422 }
1423 Item::Literal(d, alias) => {
1424 columns.push(Column {
1425 name: Arc::from(alias.as_deref().unwrap_or("?column?")),
1426 ty: d.ty(),
1427 nullable: false,
1428 });
1429 proj.push(Proj::Literal(d.clone()));
1430 }
1431 Item::Expr(e, alias) => {
1436 columns.push(Column {
1437 name: Arc::from(match alias.as_deref() {
1438 Some(a) => a,
1439 None => match e {
1442 Expr::Call { name, .. } => name.as_str(),
1443 _ => "?column?",
1444 },
1445 }),
1446 ty: SqlTy::Text,
1447 nullable: true,
1448 });
1449 proj.push(Proj::Expr(e.clone()));
1450 }
1451 }
1452 }
1453 Ok((columns, proj))
1454 }
1455
1456 fn select(&self, s: &Select, rows_of: &dyn Rows) -> Result<Answer, SqlError> {
1457 if crate::query::relational(s) {
1462 let compiled = crate::query::compile(self, s)?;
1463 let rows = compiled.run(self, rows_of)?;
1464 return self.finish(
1465 s,
1466 &compiled.fields,
1467 rows,
1468 &compiled.residual,
1469 compiled.projected,
1470 rows_of,
1471 );
1472 }
1473
1474 let table = self.resolve_from(s)?;
1475 let fields = self.scan_fields(s)?;
1476 let (columns, proj) = self.project_columns(s, &fields)?;
1477
1478 if let Some(t) = table {
1487 let bare = proj.iter().any(|p| matches!(p, Proj::Count))
1488 && proj
1489 .iter()
1490 .all(|p| matches!(p, Proj::Count | Proj::Literal(_)))
1491 && s.filter.is_empty()
1492 && s.order.is_empty()
1493 && s.limit.is_none()
1494 && s.offset == 0;
1495 if bare {
1496 let n = match self.builtin_rows(t) {
1497 Some(rows) => Some(rows.len() as u64),
1498 None => rows_of.count(t)?,
1499 };
1500 if let Some(n) = n {
1501 let n = match t.cardinality {
1502 Cardinality::Many => n,
1503 Cardinality::One => n.min(1),
1504 };
1505 let row: Vec<Cell> = proj
1506 .iter()
1507 .map(|p| match p {
1508 Proj::Count => Some(Datum::Bigint(n as i64)),
1509 Proj::Literal(d) => Some(d.clone()),
1510 Proj::Column(_) | Proj::Expr(_) => None,
1511 })
1512 .collect();
1513 return Ok(Answer {
1514 tag: "SELECT 1".to_string(),
1515 columns,
1516 rows: vec![row],
1517 });
1518 }
1519 }
1520 }
1521
1522 let Some(t) = table else {
1525 let ev = Eval::new(self, &fields, rows_of);
1526 let row: Vec<Cell> = proj
1527 .iter()
1528 .map(|p| match p {
1529 Proj::Literal(d) => Ok(Some(d.clone())),
1530 Proj::Count => Ok(Some(Datum::Bigint(1))),
1531 Proj::Expr(e) => ev.cell(e, &[]),
1532 Proj::Column(_) => Ok(None),
1533 })
1534 .collect::<Result<_, _>>()?;
1535 return Ok(Answer {
1536 tag: "SELECT 1".to_string(),
1537 columns,
1538 rows: vec![row],
1539 });
1540 };
1541
1542 let rows: Vec<Vec<Cell>> = match self.builtin_rows(t) {
1543 Some(values) => values.iter().map(|v| t.row(v)).collect(),
1544 None => {
1545 let values = rows_of.scan(t)?;
1546 match t.cardinality {
1547 Cardinality::Many => values.iter().map(|v| t.row(v)).collect(),
1548 Cardinality::One => values.iter().take(1).map(|v| t.row(v)).collect(),
1551 }
1552 }
1553 };
1554 self.finish(s, &fields, rows, &s.filter, false, rows_of)
1555 }
1556
1557 fn finish(
1560 &self,
1561 s: &Select,
1562 fields: &[Field],
1563 mut rows: Vec<Vec<Cell>>,
1564 filter: &[Expr],
1565 projected: bool,
1566 rows_of: &dyn Rows,
1567 ) -> Result<Answer, SqlError> {
1568 let ev = Eval::new(self, fields, rows_of);
1569
1570 let (columns, proj) = match projected {
1573 true => (
1574 fields.iter().map(|f| f.column.clone()).collect(),
1575 (0..fields.len()).map(Proj::Column).collect(),
1576 ),
1577 false => self.project_columns(s, fields)?,
1578 };
1579
1580 if !filter.is_empty() {
1583 let mut kept = Vec::with_capacity(rows.len());
1584 for row in rows {
1585 if ev.holds(filter, &row)? {
1586 kept.push(row);
1587 }
1588 }
1589 rows = kept;
1590 }
1591 rows = order_rows(&ev, &s.order, &columns, &proj, rows)?;
1592 cut(&mut rows, s.offset, s.limit);
1593
1594 if projected {
1595 return Ok(Answer {
1596 tag: format!("SELECT {}", rows.len()),
1597 columns,
1598 rows,
1599 });
1600 }
1601
1602 let out: Vec<Vec<Cell>> = if proj.iter().any(|p| matches!(p, Proj::Count)) {
1605 let n = rows.len();
1606 vec![proj
1607 .iter()
1608 .map(|p| match p {
1609 Proj::Count => Ok(Some(Datum::Bigint(n as i64))),
1610 Proj::Literal(d) => Ok(Some(d.clone())),
1611 Proj::Expr(e) => match rows.first() {
1615 Some(r) => ev.cell(e, r),
1616 None => Ok(None),
1617 },
1618 Proj::Column(_) => Ok(None),
1619 })
1620 .collect::<Result<_, _>>()?]
1621 } else {
1622 let mut out = Vec::with_capacity(rows.len());
1623 for r in &rows {
1624 out.push(project(&ev, &proj, r)?);
1625 }
1626 out
1627 };
1628 Ok(Answer {
1629 tag: format!("SELECT {}", out.len()),
1630 columns,
1631 rows: out,
1632 })
1633 }
1634}
1635
1636fn project(ev: &Eval, proj: &[Proj], row: &[Cell]) -> Result<Vec<Cell>, SqlError> {
1638 proj.iter()
1639 .map(|p| match p {
1640 Proj::Column(i) => Ok(row[*i].clone()),
1641 Proj::Literal(d) => Ok(Some(d.clone())),
1642 Proj::Expr(e) => ev.cell(e, row),
1643 Proj::Count => Ok(None),
1644 })
1645 .collect()
1646}
1647
1648fn order_rows(
1660 ev: &Eval,
1661 order: &[Order],
1662 columns: &[Column],
1663 proj: &[Proj],
1664 rows: Vec<Vec<Cell>>,
1665) -> Result<Vec<Vec<Cell>>, SqlError> {
1666 if order.is_empty() || rows.len() < 2 {
1667 return Ok(rows);
1668 }
1669 enum Key<'a> {
1671 Out(&'a Proj),
1672 Expr(&'a Expr),
1673 }
1674 let mut keys = Vec::with_capacity(order.len());
1675 for o in order {
1676 let key = match &o.by {
1677 OrderBy::Ordinal(n) => match n.checked_sub(1).and_then(|i| proj.get(i)) {
1678 Some(p) => Key::Out(p),
1679 None => {
1680 return Err(SqlError::no_column(format!(
1681 "`order by {n}` names the {n}th column of the select list, and there {}",
1682 match columns.len() {
1683 0 => "is none".to_string(),
1684 1 => "is one".to_string(),
1685 n => format!("are {n}"),
1686 }
1687 )))
1688 }
1689 },
1690 OrderBy::Expr(Expr::Column(n))
1694 if n.table.is_none() && columns.iter().any(|c| c.name.as_ref() == n.column) =>
1695 {
1696 let i = columns
1697 .iter()
1698 .position(|c| c.name.as_ref() == n.column)
1699 .expect("just found");
1700 Key::Out(&proj[i])
1701 }
1702 OrderBy::Expr(e) => Key::Expr(e),
1703 };
1704 keys.push((key, o.asc));
1705 }
1706
1707 let mut keyed: Vec<(Vec<Cell>, Vec<Cell>)> = Vec::with_capacity(rows.len());
1710 for row in rows {
1711 let mut k = Vec::with_capacity(keys.len());
1712 for (key, _) in &keys {
1713 k.push(match key {
1714 Key::Out(p) => project(ev, std::slice::from_ref(*p), &row)?.remove(0),
1715 Key::Expr(e) => ev.cell(e, &row)?,
1716 });
1717 }
1718 keyed.push((k, row));
1719 }
1720 keyed.sort_by(|a, b| {
1723 for (i, (_, asc)) in keys.iter().enumerate() {
1724 let o = compare(&a.0[i], &b.0[i]);
1725 let o = if *asc { o } else { o.reverse() };
1726 if !o.is_eq() {
1727 return o;
1728 }
1729 }
1730 std::cmp::Ordering::Equal
1731 });
1732 Ok(keyed.into_iter().map(|(_, row)| row).collect())
1733}
1734
1735fn cut(rows: &mut Vec<Vec<Cell>>, offset: usize, limit: Option<usize>) {
1736 if offset > 0 {
1737 *rows = rows.split_off(offset.min(rows.len()));
1738 }
1739 if let Some(n) = limit {
1740 rows.truncate(n);
1741 }
1742}
1743
1744fn row_key(row: &[Cell]) -> Vec<Option<String>> {
1749 row.iter().map(|c| c.as_ref().map(Datum::text)).collect()
1750}
1751
1752enum Proj {
1753 Column(usize),
1754 Count,
1755 Literal(Datum),
1756 Expr(Expr),
1757}
1758
1759pub fn resolve_field(fields: &[Field], n: &Name) -> Result<usize, SqlError> {
1765 let matching: Vec<usize> = (0..fields.len())
1766 .filter(|&i| {
1767 fields[i].column.name.as_ref() == n.column
1768 && match &n.table {
1769 Some(t) => fields[i].of.as_deref() == Some(t.as_str()),
1770 None => true,
1771 }
1772 })
1773 .collect();
1774 match matching.as_slice() {
1775 [one] => Ok(*one),
1776 [] => Err(SqlError::no_column(format!(
1777 "there is no column \"{n}\" here; there is {}",
1778 names_of(fields)
1779 ))),
1780 _ => Err(SqlError::no_column(format!(
1781 "\"{n}\" is ambiguous: more than one table in this query has a column called \
1782 \"{}\", so qualify it — `t.{}`",
1783 n.column, n.column
1784 ))),
1785 }
1786}
1787
1788pub fn names_of(fields: &[Field]) -> String {
1790 fields
1791 .iter()
1792 .map(|f| match &f.of {
1793 Some(t) => format!("\"{t}.{}\"", f.column.name),
1794 None => format!("\"{}\"", f.column.name),
1795 })
1796 .collect::<Vec<_>>()
1797 .join(", ")
1798}
1799
1800pub struct Eval<'a> {
1817 schema: &'a Schema,
1818 fields: &'a [Field],
1819 rows: &'a dyn Rows,
1820 resolved: std::cell::RefCell<BTreeMap<Name, usize>>,
1821 subqueries: std::cell::RefCell<BTreeMap<usize, Cell>>,
1822}
1823
1824impl<'a> Eval<'a> {
1825 pub fn new(schema: &'a Schema, fields: &'a [Field], rows: &'a dyn Rows) -> Eval<'a> {
1826 Eval {
1827 schema,
1828 fields,
1829 rows,
1830 resolved: Default::default(),
1831 subqueries: Default::default(),
1832 }
1833 }
1834
1835 pub fn resolve(&self, n: &Name) -> Result<usize, SqlError> {
1837 if let Some(i) = self.resolved.borrow().get(n) {
1838 return Ok(*i);
1839 }
1840 let i = resolve_field(self.fields, n)?;
1841 self.resolved.borrow_mut().insert(n.clone(), i);
1842 Ok(i)
1843 }
1844
1845 pub fn holds(&self, terms: &[Expr], row: &[Cell]) -> Result<bool, SqlError> {
1847 for t in terms {
1848 if !truthy(&self.cell(t, row)?) {
1849 return Ok(false);
1850 }
1851 }
1852 Ok(true)
1853 }
1854
1855 pub fn cell(&self, e: &Expr, row: &[Cell]) -> Result<Cell, SqlError> {
1860 match e {
1861 Expr::Column(n) => Ok(row.get(self.resolve(n)?).cloned().flatten()),
1862 Expr::Literal(c) => Ok(c.clone()),
1863 Expr::And(xs) => {
1866 let mut unknown = false;
1867 for x in xs {
1868 match self.cell(x, row)? {
1869 Some(Datum::Boolean(false)) => return Ok(Some(Datum::Boolean(false))),
1870 Some(Datum::Boolean(true)) => {}
1871 _ => unknown = true,
1872 }
1873 }
1874 Ok(match unknown {
1875 true => None,
1876 false => Some(Datum::Boolean(true)),
1877 })
1878 }
1879 Expr::Or(xs) => {
1880 let mut unknown = false;
1881 for x in xs {
1882 match self.cell(x, row)? {
1883 Some(Datum::Boolean(true)) => return Ok(Some(Datum::Boolean(true))),
1884 Some(Datum::Boolean(false)) => {}
1885 _ => unknown = true,
1886 }
1887 }
1888 Ok(match unknown {
1889 true => None,
1890 false => Some(Datum::Boolean(false)),
1891 })
1892 }
1893 Expr::Not(x) => Ok(match self.cell(x, row)? {
1894 Some(Datum::Boolean(b)) => Some(Datum::Boolean(!b)),
1895 _ => None,
1896 }),
1897 Expr::Cmp(a, op, b) => {
1898 let (a, b) = (self.cell(a, row)?, self.cell(b, row)?);
1899 Ok(match (a, b) {
1900 (Some(a), Some(b)) => {
1901 Some(Datum::Boolean(op.holds(compare(&Some(a), &Some(b)))))
1902 }
1903 _ => None,
1905 })
1906 }
1907 Expr::Is { value, to, negated } => {
1908 let v = self.cell(value, row)?;
1909 let is = match to {
1910 None => v.is_none(),
1911 Some(b) => v == Some(Datum::Boolean(*b)),
1912 };
1913 Ok(Some(Datum::Boolean(is != *negated)))
1914 }
1915 Expr::In {
1916 value,
1917 list,
1918 negated,
1919 } => {
1920 let v = self.cell(value, row)?;
1921 if v.is_none() {
1922 return Ok(None);
1923 }
1924 let mut unknown = false;
1925 for item in list {
1926 match self.cell(item, row)? {
1927 None => unknown = true,
1928 other if other == v => return Ok(Some(Datum::Boolean(!*negated))),
1929 _ => {}
1930 }
1931 }
1932 Ok(match unknown {
1933 true => None,
1934 false => Some(Datum::Boolean(*negated)),
1935 })
1936 }
1937 Expr::Match {
1938 value,
1939 pattern,
1940 negated,
1941 insensitive,
1942 } => {
1943 let (v, p) = (self.cell(value, row)?, self.cell(pattern, row)?);
1944 let (Some(v), Some(p)) = (v, p) else {
1945 return Ok(None);
1946 };
1947 let hit = regex::matches(&p.text(), &v.text(), *insensitive)?;
1948 Ok(Some(Datum::Boolean(hit != *negated)))
1949 }
1950 Expr::Case {
1951 operand,
1952 arms,
1953 otherwise,
1954 } => {
1955 let subject = match operand {
1956 Some(o) => Some(self.cell(o, row)?),
1957 None => None,
1958 };
1959 for (when, then) in arms {
1960 let hit = match &subject {
1961 Some(s) => {
1963 let w = self.cell(when, row)?;
1964 s.is_some() && w.is_some() && compare(s, &w).is_eq()
1965 }
1966 None => truthy(&self.cell(when, row)?),
1967 };
1968 if hit {
1969 return self.cell(then, row);
1970 }
1971 }
1972 match otherwise {
1973 Some(e) => self.cell(e, row),
1974 None => Ok(None),
1977 }
1978 }
1979 Expr::Call { name, args } => {
1980 let mut values = Vec::with_capacity(args.len());
1981 for a in args {
1982 values.push(self.cell(a, row)?);
1983 }
1984 match crate::pg::call(name, &values) {
1985 Some(Ok(c)) => Ok(c),
1986 Some(Err(why)) => Err(SqlError::unsupported(why)),
1987 None => Err(SqlError::unsupported(format!(
1988 "`{name}(…)` is not a function this read model has. The catalogue answers \
1989 the ones `psql` asks it — format_type, pg_get_userbyid, \
1990 pg_table_is_visible, pg_get_expr, pg_encoding_to_char — and there is no \
1991 expression language behind them"
1992 ))),
1993 }
1994 }
1995 Expr::Cast { value, ty } => {
1996 let v = self.cell(value, row)?;
1997 Ok(match (v, ty.as_str()) {
1998 (None, _) => None,
1999 (Some(v), "text" | "varchar" | "name" | "char") => Some(Datum::Text(v.text())),
2000 (Some(v), "bool" | "boolean") => match v {
2001 Datum::Boolean(_) => Some(v),
2002 other => Some(Datum::Boolean(other.text() == "t")),
2003 },
2004 (Some(v), "int" | "int2" | "int4" | "int8" | "bigint" | "integer" | "oid") => {
2005 match v {
2006 Datum::Bigint(_) => Some(v),
2007 other => other.text().parse::<i64>().ok().map(Datum::Bigint),
2008 }
2009 }
2010 (Some(v), "float4" | "float8" | "real" | "numeric") => match v {
2011 Datum::Double(_) => Some(v),
2012 other => other.text().parse::<f64>().ok().map(Datum::Double),
2013 },
2014 (Some(_), other) => {
2015 return Err(SqlError::unsupported(format!(
2016 "`::{other}` is a cast to a type this read model has no values of. \
2017 The four types are boolean, bigint, double precision and text, and \
2018 an object identifier printed as a name is a lookup in a catalogue \
2019 with nothing to look up"
2020 )))
2021 }
2022 })
2023 }
2024 Expr::Concat(a, b) => {
2025 let (a, b) = (self.cell(a, row)?, self.cell(b, row)?);
2026 Ok(match (a, b) {
2027 (Some(a), Some(b)) => Some(Datum::Text(format!("{}{}", a.text(), b.text()))),
2028 _ => None,
2029 })
2030 }
2031 Expr::Subquery { id, select } => self.subquery(*id, select),
2032 Expr::Array { .. } => Err(SqlError::unsupported(
2033 "`array(select …)` builds an array, and an array is not one of this read model's \
2034 four types",
2035 )),
2036 Expr::Any(_) => Err(SqlError::unsupported(
2037 "`any(…)` compares against the elements of an array, and an array is not one of \
2038 this read model's four types",
2039 )),
2040 Expr::Subscript(..) => Err(SqlError::unsupported(
2041 "`x[i]` reads an element of an array, and an array is not one of this read \
2042 model's four types",
2043 )),
2044 }
2045 }
2046
2047 pub fn subquery(&self, id: usize, select: &Select) -> Result<Cell, SqlError> {
2061 if let Some(c) = self.subqueries.borrow().get(&id) {
2062 return Ok(c.clone());
2063 }
2064 let mut widened = select.clone();
2065 let own: Vec<Field> = widened
2066 .from
2067 .iter()
2068 .filter(|f| f.function.is_none())
2069 .try_fold(Vec::new(), |mut acc: Vec<Field>, f| {
2070 acc.extend(Field::of_table_as(
2071 self.schema.relation(f.namespace.as_deref(), &f.table)?,
2072 Arc::from(f.alias.as_str()),
2073 ));
2074 Ok::<_, SqlError>(acc)
2075 })?;
2076 widened.filter.retain(|term| {
2077 let mut names = Vec::new();
2078 term.names(&mut names);
2079 names.iter().all(|n| resolve_field(&own, n).is_ok())
2080 });
2081 let answer = self.schema.select(&widened, self.rows)?;
2082 let value = match answer.rows.len() {
2083 0 => None,
2084 _ => {
2085 return Err(SqlError::unsupported(format!(
2086 "this subquery answers {} row{} once the conditions that mention the outer \
2087 query are dropped, so its value depends on which row is asking — and a \
2088 correlated subquery is not in this SQL subset",
2089 answer.rows.len(),
2090 match answer.rows.len() {
2091 1 => "",
2092 _ => "s",
2093 }
2094 )))
2095 }
2096 };
2097 self.subqueries.borrow_mut().insert(id, value.clone());
2098 Ok(value)
2099 }
2100}
2101
2102fn truthy(c: &Cell) -> bool {
2105 matches!(c, Some(Datum::Boolean(true)))
2106}
2107
2108fn compare(a: &Cell, b: &Cell) -> std::cmp::Ordering {
2110 use std::cmp::Ordering;
2111 match (a, b) {
2112 (None, None) => Ordering::Equal,
2113 (None, Some(_)) => Ordering::Greater,
2114 (Some(_), None) => Ordering::Less,
2115 (Some(x), Some(y)) => match (x, y) {
2116 (Datum::Bigint(p), Datum::Bigint(q)) => p.cmp(q),
2117 (Datum::Double(p), Datum::Double(q)) => p.partial_cmp(q).unwrap_or(Ordering::Equal),
2118 (Datum::Bigint(p), Datum::Double(q)) => {
2119 (*p as f64).partial_cmp(q).unwrap_or(Ordering::Equal)
2120 }
2121 (Datum::Double(p), Datum::Bigint(q)) => {
2122 p.partial_cmp(&(*q as f64)).unwrap_or(Ordering::Equal)
2123 }
2124 (Datum::Boolean(p), Datum::Boolean(q)) => p.cmp(q),
2125 (Datum::Text(p), Datum::Text(q)) => p.cmp(q),
2126 _ => x.text().cmp(&y.text()),
2130 },
2131 }
2132}
2133
2134mod regex {
2159 use super::SqlError;
2160
2161 pub fn matches(pattern: &str, text: &str, insensitive: bool) -> Result<bool, SqlError> {
2163 let program = compile(pattern, insensitive)?;
2164 Ok(run(&program, text, insensitive))
2165 }
2166
2167 enum Ins {
2168 Char(char),
2169 Any,
2170 Class(Vec<(char, char)>, bool),
2172 Start,
2175 End,
2176 Split(usize, usize),
2177 Jmp(usize),
2178 Match,
2179 }
2180
2181 enum Node {
2183 Empty,
2184 Char(char),
2185 Any,
2186 Class(Vec<(char, char)>, bool),
2187 Start,
2188 End,
2189 Concat(Vec<Node>),
2190 Alt(Vec<Node>),
2191 Repeat(Box<Node>, u32, bool),
2193 }
2194
2195 struct P<'a> {
2196 cs: &'a [char],
2197 i: usize,
2198 }
2199
2200 impl P<'_> {
2201 fn peek(&self) -> Option<char> {
2202 self.cs.get(self.i).copied()
2203 }
2204
2205 fn alt(&mut self) -> Result<Node, SqlError> {
2206 let mut branches = vec![self.concat()?];
2207 while self.peek() == Some('|') {
2208 self.i += 1;
2209 branches.push(self.concat()?);
2210 }
2211 Ok(match branches.len() {
2212 1 => branches.pop().expect("one branch"),
2213 _ => Node::Alt(branches),
2214 })
2215 }
2216
2217 fn concat(&mut self) -> Result<Node, SqlError> {
2218 let mut parts = Vec::new();
2219 while !matches!(self.peek(), None | Some('|') | Some(')')) {
2220 parts.push(self.repeat()?);
2221 }
2222 Ok(match parts.len() {
2223 0 => Node::Empty,
2224 1 => parts.pop().expect("one part"),
2225 _ => Node::Concat(parts),
2226 })
2227 }
2228
2229 fn repeat(&mut self) -> Result<Node, SqlError> {
2230 let mut node = self.atom()?;
2231 loop {
2232 node = match self.peek() {
2233 Some('*') => Node::Repeat(Box::new(node), 0, true),
2234 Some('+') => Node::Repeat(Box::new(node), 1, true),
2235 Some('?') => Node::Repeat(Box::new(node), 0, false),
2236 Some('{') => {
2237 return Err(SqlError::unsupported(
2238 "a counted repetition `{n,m}` is not in this regular expression \
2239 subset: `*`, `+` and `?` are what there is",
2240 ))
2241 }
2242 _ => return Ok(node),
2243 };
2244 self.i += 1;
2245 }
2246 }
2247
2248 fn atom(&mut self) -> Result<Node, SqlError> {
2249 let Some(c) = self.peek() else {
2250 return Ok(Node::Empty);
2251 };
2252 self.i += 1;
2253 Ok(match c {
2254 '(' => {
2255 let inner = self.alt()?;
2256 if self.peek() != Some(')') {
2257 return Err(SqlError::syntax(
2258 "a group in a regular expression is not closed",
2259 ));
2260 }
2261 self.i += 1;
2262 inner
2263 }
2264 '[' => self.class()?,
2265 '.' => Node::Any,
2266 '^' => Node::Start,
2267 '$' => Node::End,
2268 '\\' => match self.peek() {
2269 Some(e) => {
2270 self.i += 1;
2271 Node::Char(e)
2272 }
2273 None => {
2274 return Err(SqlError::syntax(
2275 "a regular expression ends in a backslash, which escapes nothing",
2276 ))
2277 }
2278 },
2279 other => Node::Char(other),
2280 })
2281 }
2282
2283 fn class(&mut self) -> Result<Node, SqlError> {
2284 let negated = self.peek() == Some('^');
2285 if negated {
2286 self.i += 1;
2287 }
2288 let mut ranges = Vec::new();
2289 let mut first = true;
2291 loop {
2292 let Some(c) = self.peek() else {
2293 return Err(SqlError::syntax(
2294 "a bracket expression in a regular expression is not closed",
2295 ));
2296 };
2297 if c == ']' && !first {
2298 self.i += 1;
2299 return Ok(Node::Class(ranges, negated));
2300 }
2301 first = false;
2302 self.i += 1;
2303 let lo = match c {
2304 '\\' => match self.peek() {
2305 Some(e) => {
2306 self.i += 1;
2307 e
2308 }
2309 None => {
2310 return Err(SqlError::syntax(
2311 "a bracket expression ends in a backslash",
2312 ))
2313 }
2314 },
2315 other => other,
2316 };
2317 if self.peek() == Some('-') && self.cs.get(self.i + 1).is_some_and(|c| *c != ']') {
2318 self.i += 1;
2319 let hi = self.cs[self.i];
2320 self.i += 1;
2321 ranges.push((lo, hi));
2322 } else {
2323 ranges.push((lo, lo));
2324 }
2325 }
2326 }
2327 }
2328
2329 fn compile(pattern: &str, insensitive: bool) -> Result<Vec<Ins>, SqlError> {
2330 let cs: Vec<char> = pattern.chars().collect();
2331 let mut p = P { cs: &cs, i: 0 };
2332 let node = p.alt()?;
2333 if p.i < cs.len() {
2334 return Err(SqlError::syntax(format!(
2335 "a regular expression has a `{}` with no group to close",
2336 cs[p.i]
2337 )));
2338 }
2339 let mut out = Vec::new();
2340 emit(&node, &mut out, insensitive);
2341 out.push(Ins::Match);
2342 Ok(out)
2343 }
2344
2345 fn emit(node: &Node, out: &mut Vec<Ins>, insensitive: bool) {
2346 let fold = |c: char| match insensitive {
2347 true => c.to_lowercase().next().unwrap_or(c),
2348 false => c,
2349 };
2350 match node {
2351 Node::Empty => {}
2352 Node::Char(c) => out.push(Ins::Char(fold(*c))),
2353 Node::Any => out.push(Ins::Any),
2354 Node::Class(ranges, negated) => out.push(Ins::Class(
2355 ranges.iter().map(|(a, b)| (fold(*a), fold(*b))).collect(),
2356 *negated,
2357 )),
2358 Node::Start => out.push(Ins::Start),
2359 Node::End => out.push(Ins::End),
2360 Node::Concat(parts) => parts.iter().for_each(|p| emit(p, out, insensitive)),
2361 Node::Alt(branches) => {
2362 let mut jumps = Vec::new();
2364 for (i, b) in branches.iter().enumerate() {
2365 if i + 1 < branches.len() {
2366 let split = out.len();
2367 out.push(Ins::Split(0, 0));
2368 emit(b, out, insensitive);
2369 jumps.push(out.len());
2370 out.push(Ins::Jmp(0));
2371 let next = out.len();
2372 out[split] = Ins::Split(split + 1, next);
2373 } else {
2374 emit(b, out, insensitive);
2375 }
2376 }
2377 let end = out.len();
2378 for j in jumps {
2379 out[j] = Ins::Jmp(end);
2380 }
2381 }
2382 Node::Repeat(inner, min, many) => {
2383 if *min == 1 {
2384 emit(inner, out, insensitive);
2387 }
2388 let split = out.len();
2389 out.push(Ins::Split(0, 0));
2390 emit(inner, out, insensitive);
2391 if *many {
2392 out.push(Ins::Jmp(split));
2393 }
2394 let after = out.len();
2395 out[split] = Ins::Split(split + 1, after);
2396 }
2397 }
2398 }
2399
2400 fn run(program: &[Ins], text: &str, insensitive: bool) -> bool {
2405 let cs: Vec<char> = match insensitive {
2406 true => text.to_lowercase().chars().collect(),
2407 false => text.chars().collect(),
2408 };
2409 let mut current: Vec<usize> = Vec::new();
2410 add(
2411 program,
2412 0,
2413 0,
2414 cs.len(),
2415 &mut current,
2416 &mut vec![false; program.len()],
2417 );
2418 for (i, c) in cs.iter().enumerate() {
2419 let mut next = Vec::new();
2420 let mut next_on = vec![false; program.len()];
2423 for pc in ¤t {
2424 let step = match &program[*pc] {
2425 Ins::Char(want) => *want == *c,
2426 Ins::Any => true,
2427 Ins::Class(ranges, negated) => {
2428 ranges.iter().any(|(lo, hi)| *lo <= *c && *c <= *hi) != *negated
2429 }
2430 Ins::Match => return true,
2431 _ => continue,
2432 };
2433 if step {
2434 add(program, pc + 1, i + 1, cs.len(), &mut next, &mut next_on);
2435 }
2436 }
2437 add(program, 0, i + 1, cs.len(), &mut next, &mut next_on);
2440 current = next;
2441 }
2442 current.iter().any(|pc| matches!(program[*pc], Ins::Match))
2443 }
2444
2445 fn add(
2447 program: &[Ins],
2448 pc: usize,
2449 at: usize,
2450 len: usize,
2451 set: &mut Vec<usize>,
2452 on: &mut [bool],
2453 ) {
2454 if on[pc] {
2455 return;
2456 }
2457 on[pc] = true;
2458 match &program[pc] {
2459 Ins::Jmp(to) => add(program, *to, at, len, set, on),
2460 Ins::Split(a, b) => {
2461 add(program, *a, at, len, set, on);
2462 add(program, *b, at, len, set, on);
2463 }
2464 Ins::Start if at == 0 => add(program, pc + 1, at, len, set, on),
2465 Ins::End if at == len => add(program, pc + 1, at, len, set, on),
2466 Ins::Start | Ins::End => {}
2468 _ => set.push(pc),
2469 }
2470 }
2471}
2472
2473#[derive(Clone, Debug, PartialEq)]
2478enum Tok {
2479 Word(String),
2480 Quoted(String),
2481 Str(String),
2482 Num(String),
2483 Sym(String),
2484}
2485
2486fn lex(sql: &str) -> Result<Vec<Tok>, SqlError> {
2487 let cs: Vec<char> = sql.chars().collect();
2488 let mut i = 0;
2489 let mut out = Vec::new();
2490 while i < cs.len() {
2491 let c = cs[i];
2492 if c.is_whitespace() {
2493 i += 1;
2494 } else if c == '-' && cs.get(i + 1) == Some(&'-') {
2495 while i < cs.len() && cs[i] != '\n' {
2496 i += 1;
2497 }
2498 } else if (c == 'E' || c == 'e') && cs.get(i + 1) == Some(&'\'') {
2499 i += 2;
2503 let mut s = String::new();
2504 loop {
2505 match cs.get(i) {
2506 None => return Err(SqlError::syntax("an escape string is not closed")),
2507 Some('\'') if cs.get(i + 1) == Some(&'\'') => {
2508 s.push('\'');
2509 i += 2;
2510 }
2511 Some('\'') => {
2512 i += 1;
2513 break;
2514 }
2515 Some('\\') => {
2516 i += 1;
2517 let e = cs.get(i).copied().unwrap_or('\\');
2518 i += 1;
2519 s.push(match e {
2520 'n' => '\n',
2521 't' => '\t',
2522 'r' => '\r',
2523 other => other,
2524 });
2525 }
2526 Some(ch) => {
2527 s.push(*ch);
2528 i += 1;
2529 }
2530 }
2531 }
2532 out.push(Tok::Str(s));
2533 } else if c == '_' || c.is_alphabetic() {
2534 let start = i;
2535 while i < cs.len() && (cs[i] == '_' || cs[i] == '$' || cs[i].is_alphanumeric()) {
2536 i += 1;
2537 }
2538 out.push(Tok::Word(cs[start..i].iter().collect()));
2539 } else if c.is_ascii_digit()
2540 || (c == '.' && cs.get(i + 1).is_some_and(char::is_ascii_digit))
2541 {
2542 let start = i;
2543 while i < cs.len() && (cs[i].is_ascii_digit() || cs[i] == '.') {
2544 i += 1;
2545 }
2546 out.push(Tok::Num(cs[start..i].iter().collect()));
2547 } else if c == '\'' {
2548 i += 1;
2549 let mut s = String::new();
2550 loop {
2551 match cs.get(i) {
2552 None => return Err(SqlError::syntax("a string literal is not closed")),
2553 Some('\'') if cs.get(i + 1) == Some(&'\'') => {
2555 s.push('\'');
2556 i += 2;
2557 }
2558 Some('\'') => {
2559 i += 1;
2560 break;
2561 }
2562 Some(ch) => {
2563 s.push(*ch);
2564 i += 1;
2565 }
2566 }
2567 }
2568 out.push(Tok::Str(s));
2569 } else if c == '"' {
2570 i += 1;
2571 let mut s = String::new();
2572 loop {
2573 match cs.get(i) {
2574 None => return Err(SqlError::syntax("a quoted name is not closed")),
2575 Some('"') if cs.get(i + 1) == Some(&'"') => {
2576 s.push('"');
2577 i += 2;
2578 }
2579 Some('"') => {
2580 i += 1;
2581 break;
2582 }
2583 Some(ch) => {
2584 s.push(*ch);
2585 i += 1;
2586 }
2587 }
2588 }
2589 out.push(Tok::Quoted(s));
2590 } else {
2591 let three: String = cs[i..(i + 3).min(cs.len())].iter().collect();
2594 let two: String = cs[i..(i + 2).min(cs.len())].iter().collect();
2595 if three == "!~*" {
2596 out.push(Tok::Sym(three));
2597 i += 3;
2598 } else if matches!(
2599 two.as_str(),
2600 "<=" | ">=" | "<>" | "!=" | "::" | "||" | "!~" | "~*"
2601 ) {
2602 out.push(Tok::Sym(two));
2603 i += 2;
2604 } else {
2605 out.push(Tok::Sym(c.to_string()));
2606 i += 1;
2607 }
2608 }
2609 }
2610 Ok(out)
2611}
2612
2613struct P {
2614 toks: Vec<Tok>,
2615 i: usize,
2616 subqueries: usize,
2619}
2620
2621impl P {
2622 fn subquery_id(&mut self) -> usize {
2623 self.subqueries += 1;
2624 self.subqueries - 1
2625 }
2626
2627 fn peek(&self) -> Option<&Tok> {
2628 self.toks.get(self.i)
2629 }
2630
2631 fn keyword(&self) -> Option<String> {
2633 match self.peek() {
2634 Some(Tok::Word(w)) => Some(w.to_lowercase()),
2635 _ => None,
2636 }
2637 }
2638
2639 fn eat_keyword(&mut self, k: &str) -> bool {
2640 if self.keyword().as_deref() == Some(k) {
2641 self.i += 1;
2642 return true;
2643 }
2644 false
2645 }
2646
2647 fn eat_sym(&mut self, s: &str) -> bool {
2648 if self.peek() == Some(&Tok::Sym(s.to_string())) {
2649 self.i += 1;
2650 return true;
2651 }
2652 false
2653 }
2654
2655 fn name(&mut self) -> Option<String> {
2658 match self.peek().cloned() {
2659 Some(Tok::Word(w)) => {
2660 self.i += 1;
2661 Some(w.to_lowercase())
2662 }
2663 Some(Tok::Quoted(w)) => {
2664 self.i += 1;
2665 Some(w)
2666 }
2667 _ => None,
2668 }
2669 }
2670
2671 fn literal(&mut self) -> Option<Option<Datum>> {
2672 match self.peek().cloned() {
2673 Some(Tok::Str(s)) => {
2674 self.i += 1;
2675 Some(Some(Datum::Text(s)))
2676 }
2677 Some(Tok::Num(n)) => {
2678 self.i += 1;
2679 Some(Some(match n.parse::<i64>() {
2680 Ok(i) => Datum::Bigint(i),
2681 Err(_) => Datum::Double(n.parse::<f64>().unwrap_or(0.0)),
2682 }))
2683 }
2684 Some(Tok::Sym(s)) if s == "-" => {
2685 self.i += 1;
2686 match self.literal() {
2687 Some(Some(Datum::Bigint(i))) => Some(Some(Datum::Bigint(-i))),
2688 Some(Some(Datum::Double(f))) => Some(Some(Datum::Double(-f))),
2689 _ => None,
2690 }
2691 }
2692 Some(Tok::Word(w)) => match w.to_lowercase().as_str() {
2693 "true" => {
2694 self.i += 1;
2695 Some(Some(Datum::Boolean(true)))
2696 }
2697 "false" => {
2698 self.i += 1;
2699 Some(Some(Datum::Boolean(false)))
2700 }
2701 "null" => {
2702 self.i += 1;
2703 Some(None)
2704 }
2705 _ => None,
2706 },
2707 _ => None,
2708 }
2709 }
2710}
2711
2712pub fn parse(sql: &str) -> Result<Stmt, SqlError> {
2714 let toks = lex(sql)?;
2715 let mut p = P {
2716 toks,
2717 i: 0,
2718 subqueries: 0,
2719 };
2720 let head = p.keyword().unwrap_or_default();
2721 match head.as_str() {
2722 "select" => {
2723 p.i += 1;
2724 let mut branches = vec![select(&mut p)?];
2725 let mut all = false;
2726 for word in ["intersect", "except"] {
2727 if p.keyword().as_deref() == Some(word) {
2728 return Err(SqlError::unsupported(format!(
2729 "`{word}` is not in this SQL subset; `union` is the one set operation \
2730 there is"
2731 )));
2732 }
2733 }
2734 while p.eat_keyword("union") {
2735 all |= p.eat_keyword("all");
2736 if !p.eat_keyword("select") {
2737 return Err(SqlError::syntax("`union` wants a `select` after it"));
2738 }
2739 branches.push(select(&mut p)?);
2740 }
2741 p.eat_sym(";");
2744 if p.peek().is_some() {
2745 return Err(SqlError::unsupported(
2746 "one statement per query: this SQL has no multi-statement form",
2747 ));
2748 }
2749 if branches.len() == 1 {
2750 return Ok(Stmt::Select(branches.pop().expect("one branch")));
2751 }
2752 let last = branches.last_mut().expect("at least two branches");
2755 let order = std::mem::take(&mut last.order);
2756 let limit = last.limit.take();
2757 let offset = std::mem::replace(&mut last.offset, 0);
2758 Ok(Stmt::Union {
2759 branches,
2760 all,
2761 order,
2762 limit,
2763 offset,
2764 })
2765 }
2766 "set" => Ok(Stmt::Ignored("SET")),
2767 "begin" | "start" => Ok(Stmt::Ignored("BEGIN")),
2768 "commit" | "end" => Ok(Stmt::Ignored("COMMIT")),
2769 "rollback" | "abort" => Ok(Stmt::Ignored("ROLLBACK")),
2770 "discard" => Ok(Stmt::Ignored("DISCARD ALL")),
2771 "" => Err(SqlError::syntax("an empty query")),
2772 other => Err(SqlError::unsupported(format!(
2773 "a read model is read-only and this SQL is a subset: `{other}` is not one of \
2774 select, set, begin, commit, rollback"
2775 ))),
2776 }
2777}
2778
2779fn select(p: &mut P) -> Result<Select, SqlError> {
2780 let distinct = p.eat_keyword("distinct");
2784 if p.eat_keyword("on") {
2785 return Err(SqlError::unsupported(
2786 "`distinct on` is a PostgreSQL extension this SQL does not have; \
2787 `distinct` over the whole select list is what there is",
2788 ));
2789 }
2790 let mut items = Vec::new();
2791 loop {
2792 items.push(item(p)?);
2793 if !p.eat_sym(",") {
2794 break;
2795 }
2796 }
2797
2798 let mut from = Vec::new();
2799 if p.eat_keyword("from") {
2800 from.push(from_item(p, false)?);
2801 loop {
2802 if p.eat_sym(",") {
2806 from.push(from_item(p, false)?);
2807 continue;
2808 }
2809 if p.eat_keyword("natural") {
2810 return Err(SqlError::unsupported(
2811 "a natural join names no key, and every join in this SQL is an equi-join \
2812 because that is the operator underneath it: write `join … on <equality>`",
2813 ));
2814 }
2815 if p.eat_keyword("cross") {
2816 if !p.eat_keyword("join") {
2817 return Err(SqlError::syntax("`cross` wants `join`"));
2818 }
2819 from.push(from_item(p, false)?);
2820 continue;
2821 }
2822 for outer in ["right", "full"] {
2823 if p.keyword().as_deref() == Some(outer) {
2824 return Err(SqlError::unsupported(format!(
2825 "`{outer} join` is not in this SQL subset. The `from` list is joined \
2826 left-deep, one stage per entry, so the rows a `{outer} join` keeps are \
2827 the ones no stage has yet produced; write it as a `left join` with the \
2828 tables the other way round"
2829 )));
2830 }
2831 }
2832 let left = p.eat_keyword("left");
2833 if left {
2834 p.eat_keyword("outer");
2835 } else {
2836 p.eat_keyword("inner");
2837 }
2838 if !p.eat_keyword("join") {
2839 break;
2840 }
2841 let mut entry = from_item(p, left)?;
2842 if !p.eat_keyword("on") {
2843 return Err(SqlError::syntax(format!(
2844 "`join {}` wants `on <column> = <column>`",
2845 entry.table
2846 )));
2847 }
2848 let parenthesised = p.eat_sym("(");
2850 loop {
2851 let left = column_name(p).ok_or_else(|| SqlError::syntax("`on` wants a column"))?;
2852 if !p.eat_sym("=") {
2853 return Err(SqlError::unsupported(format!(
2854 "a join is an equality here: `on {left} = <column>`, and no other \
2855 comparison"
2856 )));
2857 }
2858 let right =
2859 column_name(p).ok_or_else(|| SqlError::syntax("`on` wants a column"))?;
2860 entry.on.push((left, right));
2861 if !p.eat_keyword("and") {
2862 break;
2863 }
2864 }
2865 if parenthesised && !p.eat_sym(")") {
2866 return Err(SqlError::syntax("an `on` in brackets is not closed"));
2867 }
2868 from.push(entry);
2869 }
2870 }
2871
2872 let mut filter = Vec::new();
2873 if p.eat_keyword("where") {
2874 conjuncts(expr(p)?, &mut filter);
2875 }
2876
2877 let mut group = Vec::new();
2878 if p.eat_keyword("group") {
2879 if !p.eat_keyword("by") {
2880 return Err(SqlError::syntax("`group` wants `by`"));
2881 }
2882 loop {
2883 group
2884 .push(column_name(p).ok_or_else(|| SqlError::syntax("`group by` wants a column"))?);
2885 if !p.eat_sym(",") {
2886 break;
2887 }
2888 }
2889 }
2890 if p.eat_keyword("having") {
2891 return Err(SqlError::unsupported(
2892 "`having` is not in this SQL subset: a `where` narrows the rows before they are \
2893 grouped, and there is no filter over the groups themselves",
2894 ));
2895 }
2896
2897 if group.is_empty()
2900 && items.iter().any(Item::aggregates)
2901 && items
2902 .iter()
2903 .any(|i| matches!(i, Item::All(_) | Item::Column(..)))
2904 {
2905 return Err(SqlError::unsupported(
2906 "an aggregate beside a column needs a `group by` saying which rows it aggregates",
2907 ));
2908 }
2909
2910 let mut order = Vec::new();
2911 if p.eat_keyword("order") {
2912 if !p.eat_keyword("by") {
2913 return Err(SqlError::syntax("`order` wants `by`"));
2914 }
2915 loop {
2916 let e = expr(p)?;
2917 let by = match e {
2918 Expr::Literal(Some(Datum::Bigint(n))) if n > 0 => OrderBy::Ordinal(n as usize),
2921 other => OrderBy::Expr(other),
2922 };
2923 let asc = if p.eat_keyword("desc") {
2924 false
2925 } else {
2926 p.eat_keyword("asc");
2927 true
2928 };
2929 if p.eat_keyword("nulls") {
2933 let which = p.name().unwrap_or_default();
2934 return Err(SqlError::unsupported(format!(
2935 "`nulls {which}` is not in this SQL subset: nulls sort last ascending and \
2936 first descending, which is PostgreSQL's default and the only order there is"
2937 )));
2938 }
2939 order.push(Order { by, asc });
2940 if !p.eat_sym(",") {
2941 break;
2942 }
2943 }
2944 }
2945
2946 let mut limit = None;
2947 let mut offset = 0;
2948 loop {
2949 if p.eat_keyword("limit") {
2950 match p.literal() {
2951 Some(Some(Datum::Bigint(n))) if n >= 0 => limit = Some(n as usize),
2952 _ => return Err(SqlError::syntax("`limit` wants a whole number")),
2953 }
2954 } else if p.eat_keyword("offset") {
2955 match p.literal() {
2956 Some(Some(Datum::Bigint(n))) if n >= 0 => offset = n as usize,
2957 _ => return Err(SqlError::syntax("`offset` wants a whole number")),
2958 }
2959 } else {
2960 break;
2961 }
2962 }
2963
2964 Ok(Select {
2965 distinct,
2966 items,
2967 from,
2968 filter,
2969 group,
2970 order,
2971 limit,
2972 offset,
2973 })
2974}
2975
2976fn conjuncts(e: Expr, out: &mut Vec<Expr>) {
2978 match e {
2979 Expr::And(xs) => xs.into_iter().for_each(|x| conjuncts(x, out)),
2980 other => out.push(other),
2981 }
2982}
2983
2984fn from_item(p: &mut P, left: bool) -> Result<From, SqlError> {
2986 let first = p
2987 .name()
2988 .ok_or_else(|| SqlError::syntax("`from` wants a table name"))?;
2989 let (namespace, name) = match p.eat_sym(".") {
2990 true => (
2991 Some(first),
2992 p.name()
2993 .ok_or_else(|| SqlError::syntax("a qualified name wants a table after the `.`"))?,
2994 ),
2995 false => (None, first),
2996 };
2997 let function = match p.eat_sym("(") {
3000 false => None,
3001 true => {
3002 let mut depth = 1;
3003 while depth > 0 {
3004 match p.peek() {
3005 None => return Err(SqlError::syntax("a call in a `from` is not closed")),
3006 Some(Tok::Sym(s)) if s == "(" => depth += 1,
3007 Some(Tok::Sym(s)) if s == ")" => depth -= 1,
3008 _ => {}
3009 }
3010 p.i += 1;
3011 }
3012 Some(name.clone())
3013 }
3014 };
3015 let alias = table_alias(p)?.unwrap_or_else(|| name.clone());
3016 Ok(From {
3017 namespace,
3018 table: name,
3019 alias,
3020 on: Vec::new(),
3021 left,
3022 function,
3023 })
3024}
3025
3026fn table_alias(p: &mut P) -> Result<Option<String>, SqlError> {
3032 if p.eat_keyword("as") {
3033 return Ok(Some(
3034 p.name()
3035 .ok_or_else(|| SqlError::syntax("`as` wants a name"))?,
3036 ));
3037 }
3038 match p.keyword().as_deref() {
3039 Some("where") | Some("order") | Some("group") | Some("having") | Some("limit")
3040 | Some("offset") | Some("join") | Some("inner") | Some("left") | Some("right")
3041 | Some("full") | Some("outer") | Some("cross") | Some("natural") | Some("on")
3042 | Some("union") | Some("intersect") | Some("except") | None => Ok(None),
3043 Some(_) => Ok(p.name()),
3044 }
3045}
3046
3047fn column_name(p: &mut P) -> Option<Name> {
3049 let first = p.name()?;
3050 if p.eat_sym(".") {
3051 return match p.name() {
3052 Some(column) => Some(Name {
3053 table: Some(first),
3054 column,
3055 }),
3056 None => Some(Name::bare(first)),
3057 };
3058 }
3059 Some(Name::bare(first))
3060}
3061
3062fn item(p: &mut P) -> Result<Item, SqlError> {
3063 if p.eat_sym("*") {
3064 return Ok(Item::All(None));
3065 }
3066 let save = p.i;
3070 if let Some(name) = p.name() {
3071 if p.eat_sym(".") && p.eat_sym("*") {
3072 return Ok(Item::All(Some(name)));
3073 }
3074 p.i = save;
3075 }
3076 let save = p.i;
3077 if let Some(name) = p.name() {
3078 if p.eat_sym("(") {
3079 let aggregate = match name.as_str() {
3083 "count" => {
3084 if !p.eat_sym("*") {
3085 return Err(SqlError::unsupported(
3086 "`count` counts rows here: `count(*)` is the only form, and \
3087 `count(c)` would be a count of the rows whose `c` is not null",
3088 ));
3089 }
3090 Some(Item::Count(None))
3091 }
3092 "min" | "max" | "sum" => {
3093 let agg = match name.as_str() {
3094 "min" => Agg::Min,
3095 "max" => Agg::Max,
3096 _ => Agg::Sum,
3097 };
3098 let column = column_name(p).ok_or_else(|| {
3099 SqlError::unsupported(format!(
3100 "`{name}` takes a column here: `{name}(c)`, and no expression"
3101 ))
3102 })?;
3103 Some(Item::Aggregate(agg, column, None))
3104 }
3105 _ => None,
3106 };
3107 if let Some(aggregate) = aggregate {
3108 if !p.eat_sym(")") {
3109 return Err(SqlError::syntax("a call is not closed"));
3110 }
3111 let a = alias(p);
3112 return Ok(match aggregate {
3113 Item::Count(_) => Item::Count(a),
3114 Item::Aggregate(agg, c, _) => Item::Aggregate(agg, c, a),
3115 other => other,
3116 });
3117 }
3118 }
3119 p.i = save;
3120 }
3121 let e = expr(p)?;
3122 let a = alias(p);
3123 Ok(match e {
3127 Expr::Column(n) => Item::Column(n, a),
3128 Expr::Literal(Some(d)) => Item::Literal(d, a),
3129 other => Item::Expr(other, a),
3130 })
3131}
3132
3133fn alias(p: &mut P) -> Option<String> {
3134 if p.eat_keyword("as") {
3135 return p.name();
3136 }
3137 match p.keyword().as_deref() {
3139 Some("from") | Some("where") | Some("group") | Some("having") | Some("order")
3140 | Some("limit") | Some("offset") | Some("as") | Some("union") | Some("intersect")
3141 | Some("except") | None => None,
3142 Some(_) => p.name(),
3143 }
3144}
3145
3146fn expr(p: &mut P) -> Result<Expr, SqlError> {
3155 let mut xs = vec![and_expr(p)?];
3156 while p.eat_keyword("or") {
3157 xs.push(and_expr(p)?);
3158 }
3159 Ok(match xs.len() {
3160 1 => xs.pop().expect("one term"),
3161 _ => Expr::Or(xs),
3162 })
3163}
3164
3165fn and_expr(p: &mut P) -> Result<Expr, SqlError> {
3166 let mut xs = vec![not_expr(p)?];
3167 while p.eat_keyword("and") {
3168 xs.push(not_expr(p)?);
3169 }
3170 Ok(match xs.len() {
3171 1 => xs.pop().expect("one term"),
3172 _ => Expr::And(xs),
3173 })
3174}
3175
3176fn not_expr(p: &mut P) -> Result<Expr, SqlError> {
3177 if p.eat_keyword("not") {
3178 return Ok(Expr::Not(Box::new(not_expr(p)?)));
3179 }
3180 cmp_expr(p)
3181}
3182
3183fn cmp_expr(p: &mut P) -> Result<Expr, SqlError> {
3184 let lhs = concat_expr(p)?;
3185 if p.eat_keyword("is") {
3186 let negated = p.eat_keyword("not");
3187 let to =
3188 match p.keyword().as_deref() {
3189 Some("null") => None,
3190 Some("true") => Some(true),
3191 Some("false") => Some(false),
3192 _ => return Err(SqlError::unsupported(
3193 "`is` is followed by `null`, `true` or `false` here; `is distinct from` and \
3194 `is unknown` are not in this SQL subset",
3195 )),
3196 };
3197 p.i += 1;
3198 return Ok(Expr::Is {
3199 value: Box::new(lhs),
3200 to,
3201 negated,
3202 });
3203 }
3204 let negated = match p.keyword().as_deref() {
3205 Some("not") if p.toks.get(p.i + 1) == Some(&Tok::Word("in".into())) => {
3206 p.i += 1;
3207 true
3208 }
3209 _ => false,
3210 };
3211 if p.eat_keyword("in") {
3212 if !p.eat_sym("(") {
3213 return Err(SqlError::syntax("`in` wants a bracketed list"));
3214 }
3215 let mut list = Vec::new();
3216 if !p.eat_sym(")") {
3217 loop {
3218 list.push(expr(p)?);
3219 if !p.eat_sym(",") {
3220 break;
3221 }
3222 }
3223 if !p.eat_sym(")") {
3224 return Err(SqlError::syntax("an `in` list is not closed"));
3225 }
3226 }
3227 return Ok(Expr::In {
3228 value: Box::new(lhs),
3229 list,
3230 negated,
3231 });
3232 }
3233 if negated {
3234 return Err(SqlError::syntax("`not` here wants `in`"));
3235 }
3236 let symbol = if p.eat_keyword("operator") {
3239 if !p.eat_sym("(") {
3240 return Err(SqlError::syntax(
3241 "`operator` wants a bracketed operator name",
3242 ));
3243 }
3244 let mut sym = String::new();
3245 loop {
3246 match p.peek().cloned() {
3247 Some(Tok::Sym(s)) if s == ")" => {
3248 p.i += 1;
3249 break;
3250 }
3251 Some(Tok::Sym(s)) if s == "." => {
3254 sym.clear();
3255 p.i += 1;
3256 }
3257 Some(Tok::Sym(s)) => {
3258 sym.push_str(&s);
3259 p.i += 1;
3260 }
3261 Some(Tok::Word(_)) => {
3262 p.i += 1;
3263 }
3264 _ => return Err(SqlError::syntax("an `operator(…)` is not closed")),
3265 }
3266 }
3267 Some(sym)
3268 } else {
3269 None
3270 };
3271 let take = |p: &mut P, s: &str| -> bool {
3272 match &symbol {
3273 Some(sym) => sym == s,
3274 None => p.eat_sym(s),
3275 }
3276 };
3277 let op = if take(p, "=") {
3278 CmpOp::Eq
3279 } else if take(p, "<>") || take(p, "!=") {
3280 CmpOp::Ne
3281 } else if take(p, "<=") {
3282 CmpOp::Le
3283 } else if take(p, ">=") {
3284 CmpOp::Ge
3285 } else if take(p, "<") {
3286 CmpOp::Lt
3287 } else if take(p, ">") {
3288 CmpOp::Gt
3289 } else {
3290 for (sym, negated, insensitive) in [
3291 ("~", false, false),
3292 ("!~", true, false),
3293 ("~*", false, true),
3294 ("!~*", true, true),
3295 ] {
3296 if take(p, sym) {
3297 return Ok(Expr::Match {
3298 value: Box::new(lhs),
3299 pattern: Box::new(concat_expr(p)?),
3300 negated,
3301 insensitive,
3302 });
3303 }
3304 }
3305 return match symbol {
3306 Some(sym) => Err(SqlError::unsupported(format!(
3307 "`operator({sym})` is not one of the comparisons here: =, <>, <, <=, >, >=, and \
3308 the four regular-expression matches ~, !~, ~*, !~*"
3309 ))),
3310 None => Ok(lhs),
3311 };
3312 };
3313 Ok(Expr::Cmp(Box::new(lhs), op, Box::new(concat_expr(p)?)))
3314}
3315
3316fn concat_expr(p: &mut P) -> Result<Expr, SqlError> {
3317 let mut e = postfix(p)?;
3318 while p.eat_sym("||") {
3319 e = Expr::Concat(Box::new(e), Box::new(postfix(p)?));
3320 }
3321 Ok(e)
3322}
3323
3324fn postfix(p: &mut P) -> Result<Expr, SqlError> {
3326 let mut e = primary(p)?;
3327 loop {
3328 if p.eat_sym("::") {
3329 let mut ty = p
3332 .name()
3333 .ok_or_else(|| SqlError::syntax("`::` wants a type name"))?;
3334 if p.eat_sym(".") {
3335 ty = p
3336 .name()
3337 .ok_or_else(|| SqlError::syntax("a qualified type wants a name"))?;
3338 }
3339 while p.eat_sym("[") {
3340 if !p.eat_sym("]") {
3341 return Err(SqlError::syntax("an array type wants `[]`"));
3342 }
3343 }
3344 e = Expr::Cast {
3345 value: Box::new(e),
3346 ty,
3347 };
3348 } else if p.eat_sym("[") {
3349 let index = expr(p)?;
3350 if !p.eat_sym("]") {
3351 return Err(SqlError::syntax("a subscript is not closed"));
3352 }
3353 e = Expr::Subscript(Box::new(e), Box::new(index));
3354 } else if p.eat_keyword("collate") {
3355 let _ = column_name(p);
3357 } else {
3358 return Ok(e);
3359 }
3360 }
3361}
3362
3363fn primary(p: &mut P) -> Result<Expr, SqlError> {
3364 if let Some(lit) = p.literal() {
3365 return Ok(Expr::Literal(lit));
3366 }
3367 if p.eat_sym("(") {
3368 let e = match p.keyword().as_deref() {
3370 Some("select") => {
3371 p.i += 1;
3372 let id = p.subquery_id();
3373 Expr::Subquery {
3374 id,
3375 select: Box::new(select(p)?),
3376 }
3377 }
3378 _ => expr(p)?,
3379 };
3380 if !p.eat_sym(")") {
3381 return Err(SqlError::syntax("a bracket is not closed"));
3382 }
3383 return Ok(e);
3384 }
3385 if p.eat_keyword("case") {
3386 let operand = match p.keyword().as_deref() {
3387 Some("when") => None,
3388 _ => Some(Box::new(expr(p)?)),
3389 };
3390 let mut arms = Vec::new();
3391 while p.eat_keyword("when") {
3392 let when = expr(p)?;
3393 if !p.eat_keyword("then") {
3394 return Err(SqlError::syntax("a `case` arm wants `then`"));
3395 }
3396 arms.push((when, expr(p)?));
3397 }
3398 let otherwise = match p.eat_keyword("else") {
3399 true => Some(Box::new(expr(p)?)),
3400 false => None,
3401 };
3402 if !p.eat_keyword("end") {
3403 return Err(SqlError::syntax("a `case` wants `end`"));
3404 }
3405 return Ok(Expr::Case {
3406 operand,
3407 arms,
3408 otherwise,
3409 });
3410 }
3411 let first = p
3412 .name()
3413 .ok_or_else(|| SqlError::syntax("an expression wants a column, a literal or a call"))?;
3414 if p.peek() == Some(&Tok::Sym("(".into())) {
3415 return call(p, first);
3416 }
3417 if p.eat_sym(".") {
3418 let second = p
3419 .name()
3420 .ok_or_else(|| SqlError::syntax(format!("`{first}.` wants a name")))?;
3421 if p.peek() == Some(&Tok::Sym("(".into())) {
3423 return call(p, second);
3424 }
3425 if p.eat_sym(".") {
3428 let third = p
3429 .name()
3430 .ok_or_else(|| SqlError::syntax("a qualified column wants a name"))?;
3431 return Ok(Expr::Column(Name {
3432 table: Some(second),
3433 column: third,
3434 }));
3435 }
3436 return Ok(Expr::Column(Name {
3437 table: Some(first),
3438 column: second,
3439 }));
3440 }
3441 Ok(Expr::Column(Name::bare(first)))
3442}
3443
3444fn call(p: &mut P, name: String) -> Result<Expr, SqlError> {
3446 p.eat_sym("(");
3447 if p.keyword().as_deref() == Some("select") {
3450 p.i += 1;
3451 let id = p.subquery_id();
3452 let inner = Box::new(select(p)?);
3453 if !p.eat_sym(")") {
3454 return Err(SqlError::syntax("a subquery is not closed"));
3455 }
3456 return Ok(match name.as_str() {
3457 "array" => Expr::Array { id, select: inner },
3458 _ => Expr::Call {
3459 name,
3460 args: vec![Expr::Subquery { id, select: inner }],
3461 },
3462 });
3463 }
3464 let mut args = Vec::new();
3465 if !p.eat_sym(")") {
3466 loop {
3467 args.push(expr(p)?);
3468 if !p.eat_sym(",") {
3469 break;
3470 }
3471 }
3472 if !p.eat_sym(")") {
3473 return Err(SqlError::syntax(format!("`{name}(` is not closed")));
3474 }
3475 }
3476 Ok(match (name.as_str(), args.len()) {
3477 ("any", 1) => Expr::Any(Box::new(args.pop().expect("one argument"))),
3478 _ => Expr::Call { name, args },
3479 })
3480}
3481
3482pub fn version() -> String {
3488 format!(
3489 "PostgreSQL 15.0 (beck {}) — a read model, not a database",
3490 env!("CARGO_PKG_VERSION")
3491 )
3492}
3493
3494#[cfg(test)]
3495mod tests {
3496 use super::*;
3497
3498 fn ok(sql: &str) -> Select {
3499 match parse(sql).expect("parses") {
3500 Stmt::Select(s) => s,
3501 other => panic!("not a select: {other:?}"),
3502 }
3503 }
3504
3505 struct NoRows;
3507
3508 impl Rows for NoRows {
3509 fn scan(&self, table: &Table) -> Result<Vec<Value>, SqlError> {
3510 Err(SqlError::no_table(format!(
3511 "no program: \"{}\"",
3512 table.name
3513 )))
3514 }
3515 }
3516
3517 fn literal_of(e: &Expr) -> &Cell {
3519 match e {
3520 Expr::Cmp(_, _, rhs) => match &**rhs {
3521 Expr::Literal(c) => c,
3522 other => panic!("not a literal: {other:?}"),
3523 },
3524 other => panic!("not a comparison: {other:?}"),
3525 }
3526 }
3527
3528 #[test]
3529 fn a_select_is_case_folded_and_a_quoted_name_is_not() {
3530 let s = ok("SELECT Text FROM Todos");
3531 assert_eq!(s.from[0].table, "todos");
3532 assert!(matches!(&s.items[0], Item::Column(c, _) if c.column == "text"));
3533 let s = ok(r#"select "Text" from "Todos""#);
3534 assert_eq!(s.from[0].table, "Todos");
3535 assert!(matches!(&s.items[0], Item::Column(c, _) if c.column == "Text"));
3536 }
3537
3538 #[test]
3545 fn and_binds_tighter_than_or() {
3546 let s = ok("select * from t where a = 1 or b = 2 and c = 3");
3547 assert_eq!(s.filter.len(), 1);
3548 let Expr::Or(branches) = &s.filter[0] else {
3549 panic!("not an `or`: {:?}", s.filter[0])
3550 };
3551 assert_eq!(branches.len(), 2);
3552 assert!(matches!(&branches[0], Expr::Cmp(..)));
3553 assert!(
3554 matches!(&branches[1], Expr::And(xs) if xs.len() == 2),
3555 "{:?}",
3556 branches[1]
3557 );
3558 let s = ok("select * from t where a = 1 and b = 2 or c = 3");
3560 assert_eq!(s.filter.len(), 1);
3561 assert!(matches!(&s.filter[0], Expr::Or(xs) if xs.len() == 2));
3562 }
3563
3564 #[test]
3565 fn an_and_is_the_unit_a_where_is_pushed_down_as() {
3566 let s = ok("select * from t where a = 1 and b = 2 and c = 3");
3567 assert_eq!(s.filter.len(), 3);
3568 }
3569
3570 #[test]
3571 fn a_negative_literal_is_one_number() {
3572 let s = ok("select * from t where n < -3");
3573 assert_eq!(literal_of(&s.filter[0]), &Some(Datum::Bigint(-3)));
3574 }
3575
3576 #[test]
3577 fn an_escaped_quote_is_one_character() {
3578 let s = ok("select * from t where name = 'it''s'");
3579 assert_eq!(
3580 literal_of(&s.filter[0]),
3581 &Some(Datum::Text("it's".to_string()))
3582 );
3583 let s = ok(r"select * from t where name = E'a\nb'");
3585 assert_eq!(
3586 literal_of(&s.filter[0]),
3587 &Some(Datum::Text("a\nb".to_string()))
3588 );
3589 }
3590
3591 #[test]
3592 fn a_write_is_refused_by_name() {
3593 let e = parse("insert into todos values (1)").expect_err("refused");
3594 assert_eq!(e.code, "0A000");
3595 assert!(e.message.contains("read-only"), "{}", e.message);
3596 }
3597
3598 #[test]
3599 fn an_aggregate_beside_a_column_needs_a_group_by() {
3600 let e = parse("select id, count(*) from todos").expect_err("refused");
3601 assert!(e.message.contains("group by"), "{}", e.message);
3602 let s = ok("select id, count(*) from todos group by id");
3604 assert_eq!(s.group.len(), 1);
3605 assert!(crate::query::relational(&s));
3606 }
3607
3608 #[test]
3609 fn a_second_statement_is_refused() {
3610 assert!(parse("select 1; select 2").is_err());
3611 }
3612
3613 #[test]
3614 fn nulls_sort_last_and_compare_as_unknown() {
3615 let schema = Schema::default();
3616 let fields = vec![Field {
3617 column: Column {
3618 name: Arc::from("x"),
3619 ty: SqlTy::Bigint,
3620 nullable: true,
3621 },
3622 of: None,
3623 }];
3624 let ev = Eval::new(&schema, &fields, &NoRows);
3625 let term = |sql: &str| ok(&format!("select * from t where {sql}")).filter.remove(0);
3626 assert_eq!(ev.cell(&term("x = 1"), &[None]).expect("evaluates"), None);
3628 assert!(!ev.holds(&[term("x = 1")], &[None]).expect("evaluates"));
3629 assert!(!ev.holds(&[term("x <> 1")], &[None]).expect("evaluates"));
3630 assert!(ev.holds(&[term("x is null")], &[None]).expect("evaluates"));
3632 assert!(!ev
3633 .holds(&[term("x is null")], &[Some(Datum::Bigint(1))])
3634 .expect("evaluates"));
3635 assert!(compare(&None, &Some(Datum::Bigint(1))).is_gt());
3636 }
3637
3638 #[test]
3639 fn a_case_evaluates_the_arm_that_matches_and_no_other() {
3640 let schema = Schema::default();
3641 let fields = Vec::new();
3642 let ev = Eval::new(&schema, &fields, &NoRows);
3643 let one = |sql: &str| {
3644 let s = ok(sql);
3645 let Item::Expr(e, _) = &s.items[0] else {
3646 panic!("not an expression: {:?}", s.items[0])
3647 };
3648 ev.cell(e, &[]).expect("evaluates")
3649 };
3650 assert_eq!(
3653 one("select case when 1 = 1 then 'yes' else no_such_function() end"),
3654 Some(Datum::Text("yes".into()))
3655 );
3656 assert_eq!(
3658 one("select case 2 when 1 then 'a' when 2 then 'b' end"),
3659 Some(Datum::Text("b".into()))
3660 );
3661 assert_eq!(one("select case 9 when 1 then 'a' end"), None);
3662 }
3663
3664 #[test]
3667 fn a_regular_expression_anchors_alternates_and_does_not_backtrack() {
3668 assert!(regex::matches("^(todos)$", "todos", false).expect("matches"));
3669 assert!(!regex::matches("^(todos)$", "my_todos", false).expect("matches"));
3670 assert!(regex::matches("^pg_", "pg_class", false).expect("matches"));
3671 assert!(!regex::matches("^pg_toast", "pg_class", false).expect("matches"));
3672 assert!(regex::matches("^(a|b)c$", "bc", false).expect("matches"));
3673 assert!(regex::matches("os", "todos", false).expect("matches"));
3674 assert!(regex::matches("^TODOS$", "todos", true).expect("matches"));
3675 assert!(regex::matches(r"^a\.b$", "a.b", false).expect("matches"));
3676 assert!(!regex::matches(r"^a\.b$", "axb", false).expect("matches"));
3677 assert!(regex::matches("^[a-c]+$", "abcabc", false).expect("matches"));
3678 assert!(!regex::matches("^[^a-c]+$", "abc", false).expect("matches"));
3679 assert!(!regex::matches("^(a|a)*b$", "aaaaaaaaaaaaaaaaaaaaaaaa", false).expect("matches"));
3682 }
3683
3684 #[test]
3685 fn a_join_carries_its_tables_its_names_and_its_equality() {
3686 let s = ok(
3687 "select o.id, i.name from orders o join items as i on o.item = i.id \
3688 where i.stocked = true order by o.id limit 5",
3689 );
3690 assert_eq!(s.from.len(), 2);
3691 assert_eq!(
3692 (s.from[0].table.as_str(), s.from[0].alias.as_str()),
3693 ("orders", "o")
3694 );
3695 assert_eq!(
3696 (s.from[1].table.as_str(), s.from[1].alias.as_str()),
3697 ("items", "i")
3698 );
3699 assert_eq!(s.from[1].on.len(), 1);
3700 assert_eq!(s.from[1].on[0].0.to_string(), "o.item");
3701 assert_eq!(s.from[1].on[0].1.to_string(), "i.id");
3702 assert_eq!(s.filter.len(), 1);
3704 assert_eq!(s.order.len(), 1);
3705 assert!(
3706 matches!(&s.order[0].by, OrderBy::Expr(Expr::Column(n)) if n.to_string() == "o.id"),
3707 "{:?}",
3708 s.order[0].by
3709 );
3710 assert_eq!(s.limit, Some(5));
3711 assert!(crate::query::relational(&s));
3712 }
3713
3714 #[test]
3715 fn a_bare_from_takes_the_tables_own_name_and_no_clause_becomes_an_alias() {
3716 for sql in [
3717 "select * from todos where done = true",
3718 "select * from todos order by text",
3719 "select count(*) from todos group by owner",
3720 "select * from todos limit 1",
3721 ] {
3722 let s = ok(sql);
3723 assert_eq!(s.from[0].alias, "todos", "{sql}");
3724 }
3725 }
3726
3727 #[test]
3730 fn a_left_join_parses_and_the_two_that_cannot_be_left_deep_are_refused() {
3731 let s = ok("select * from a left outer join b on a.k = b.k");
3732 assert_eq!(s.from.len(), 2);
3733 assert!(s.from[1].left);
3734 assert!(!s.from[0].left);
3735 for sql in [
3736 "select * from a right join b on a.k = b.k",
3737 "select * from a full outer join b on a.k = b.k",
3738 "select * from a natural join b",
3739 ] {
3740 let e = parse(sql).expect_err("refused");
3741 assert_eq!(e.code, "0A000", "{sql}");
3742 }
3743 for sql in [
3745 "select * from a, b where a.k = b.k",
3746 "select * from a cross join b",
3747 ] {
3748 let s = ok(sql);
3749 assert_eq!(s.from.len(), 2, "{sql}");
3750 assert!(s.from[1].on.is_empty(), "{sql}");
3751 }
3752 let e = parse("select * from a join b on a.k < b.k").expect_err("refused");
3754 assert!(e.message.contains("equality"), "{}", e.message);
3755 }
3756
3757 #[test]
3758 fn a_catalogue_query_parses_the_way_psql_writes_one() {
3759 let s = ok("SELECT n.nspname as \"Schema\", c.relname as \"Name\", \
3761 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' END as \"Type\", \
3762 pg_catalog.pg_get_userbyid(c.relowner) as \"Owner\" \
3763 FROM pg_catalog.pg_class c \
3764 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace \
3765 LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam \
3766 WHERE c.relkind IN ('r','p','v','m','S','f','') \
3767 AND n.nspname <> 'pg_catalog' AND n.nspname !~ '^pg_toast' \
3768 AND pg_catalog.pg_table_is_visible(c.oid) ORDER BY 1,2");
3769 assert_eq!(s.from.len(), 3);
3770 assert_eq!(s.from[0].namespace.as_deref(), Some("pg_catalog"));
3771 assert!(s.from[1].left && s.from[2].left);
3772 assert_eq!(s.filter.len(), 4);
3773 assert!(matches!(&s.filter[0], Expr::In { .. }));
3774 assert!(matches!(&s.filter[2], Expr::Match { negated: true, .. }));
3775 assert_eq!(s.order.len(), 2);
3776 assert!(matches!(s.order[0].by, OrderBy::Ordinal(1)));
3777 assert!(matches!(s.order[1].by, OrderBy::Ordinal(2)));
3778 let s = ok("select c.oid from pg_catalog.pg_class c \
3780 where c.relname OPERATOR(pg_catalog.~) '^(todos)$' COLLATE pg_catalog.default");
3781 assert!(matches!(&s.filter[0], Expr::Match { negated: false, .. }));
3782 }
3783
3784 #[test]
3785 fn a_union_is_one_statement_and_its_order_by_is_the_unions() {
3786 let Stmt::Union {
3787 branches,
3788 all,
3789 order,
3790 ..
3791 } = parse("select a from t union select b from u order by 1").expect("parses")
3792 else {
3793 panic!("not a union")
3794 };
3795 assert_eq!(branches.len(), 2);
3796 assert!(!all);
3797 assert_eq!(order.len(), 1);
3798 assert!(branches[1].order.is_empty());
3799 assert!(parse("select 1 intersect select 2").is_err());
3800 }
3801
3802 #[test]
3803 fn a_having_is_refused_and_says_what_a_where_does_instead() {
3804 let e = parse("select owner, count(*) from t group by owner having count(*) > 1")
3805 .expect_err("refused");
3806 assert!(
3807 e.message.contains("before they are grouped"),
3808 "{}",
3809 e.message
3810 );
3811 }
3812
3813 #[test]
3814 fn only_a_query_that_relates_groups_or_deduplicates_needs_the_plan() {
3815 for sql in [
3817 "select * from todos",
3818 "select count(*) from todos",
3819 "select count(*) from todos where done = false",
3820 "select 1",
3821 ] {
3822 assert!(!crate::query::relational(&ok(sql)), "{sql}");
3823 }
3824 for sql in [
3825 "select distinct owner from todos",
3826 "select owner, count(*) from todos group by owner",
3827 "select sum(amount) from postings",
3828 "select * from a join b on a.k = b.k",
3829 ] {
3830 assert!(crate::query::relational(&ok(sql)), "{sql}");
3831 }
3832 }
3833}