surql_parser/upstream/sql/
lookup.rs1use crate::upstream::fmt::{EscapeKwFreeIdent, Fmt};
2use crate::upstream::sql::order::Ordering;
3use crate::upstream::sql::{
4 Cond, Dir, Fields, Groups, Idiom, Limit, RecordIdKeyRangeLit, Splits, Start,
5};
6use std::ops::Bound;
7use surrealdb_types::{SqlFormat, ToSql, write_sql};
8#[derive(Clone, Debug, Default, PartialEq, Eq)]
11pub struct Lookup {
12 pub kind: LookupKind,
13 pub expr: Option<Fields>,
14 pub what: Vec<LookupSubject>,
15 pub cond: Option<Cond>,
16 pub split: Option<Splits>,
17 pub group: Option<Groups>,
18 pub order: Option<Ordering>,
19 pub limit: Option<Limit>,
20 pub start: Option<Start>,
21 pub alias: Option<Idiom>,
22}
23impl ToSql for Lookup {
24 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
25 if self.what.len() <= 1
26 && self.what.iter().all(|v| {
27 if v.referencing_field().is_some() {
28 return false;
29 }
30 if let LookupSubject::Range {
31 range: RecordIdKeyRangeLit {
32 end: Bound::Unbounded,
33 ..
34 },
35 ..
36 } = v
37 {
38 return false;
39 }
40 true
41 }) && self.cond.is_none()
42 && self.alias.is_none()
43 && self.expr.is_none()
44 {
45 self.kind.fmt_sql(f, fmt);
46 if self.what.is_empty() {
47 f.push('?');
48 } else {
49 write_sql!(f, fmt, "{}", Fmt::comma_separated(self.what.iter()));
50 }
51 } else {
52 write_sql!(f, fmt, "{}(", self.kind);
53 if let Some(ref expr) = self.expr {
54 write_sql!(f, fmt, "SELECT {} FROM ", expr);
55 }
56 if self.what.is_empty() {
57 f.push('?');
58 } else {
59 write_sql!(f, fmt, "{}", Fmt::comma_separated(&self.what));
60 }
61 if let Some(ref v) = self.cond {
62 write_sql!(f, fmt, " {v}");
63 }
64 if let Some(ref v) = self.split {
65 write_sql!(f, fmt, " {v}");
66 }
67 if let Some(ref v) = self.group {
68 write_sql!(f, fmt, " {v}");
69 }
70 if let Some(ref v) = self.order {
71 write_sql!(f, fmt, " {v}");
72 }
73 if let Some(ref v) = self.limit {
74 write_sql!(f, fmt, " {v}");
75 }
76 if let Some(ref v) = self.start {
77 write_sql!(f, fmt, " {v}");
78 }
79 if let Some(ref v) = self.alias {
80 write_sql!(f, fmt, " AS {v}");
81 }
82 f.push(')');
83 }
84 }
85}
86#[derive(Clone, Debug, PartialEq, Eq)]
88#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
89pub enum LookupKind {
90 Graph(Dir),
91 Reference,
92}
93impl Default for LookupKind {
94 fn default() -> Self {
95 Self::Graph(Dir::Both)
96 }
97}
98impl ToSql for LookupKind {
99 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
100 match self {
101 Self::Graph(dir) => dir.fmt_sql(f, fmt),
102 Self::Reference => f.push_str("<~"),
103 }
104 }
105}
106#[derive(Clone, Debug, PartialEq, Eq)]
108#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
109pub enum LookupSubject {
110 Table {
111 table: String,
112 referencing_field: Option<String>,
113 },
114 Range {
115 table: String,
116 range: RecordIdKeyRangeLit,
117 referencing_field: Option<String>,
118 },
119}
120impl LookupSubject {
121 pub fn referencing_field(&self) -> Option<&String> {
122 match self {
123 LookupSubject::Table {
124 referencing_field, ..
125 } => referencing_field.as_ref(),
126 LookupSubject::Range {
127 referencing_field, ..
128 } => referencing_field.as_ref(),
129 }
130 }
131}
132impl ToSql for LookupSubject {
133 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
134 match self {
135 Self::Table {
136 table,
137 referencing_field,
138 } => {
139 write_sql!(f, fmt, "{}", EscapeKwFreeIdent(table));
140 if let Some(referencing_field) = referencing_field {
141 write_sql!(f, fmt, " FIELD {}", EscapeKwFreeIdent(referencing_field));
142 }
143 }
144 Self::Range {
145 table,
146 range,
147 referencing_field,
148 } => {
149 write_sql!(f, fmt, "{}:{range}", EscapeKwFreeIdent(table));
150 if let Some(referencing_field) = referencing_field {
151 write_sql!(f, fmt, " FIELD {}", EscapeKwFreeIdent(referencing_field));
152 }
153 }
154 }
155 }
156}