surql_parser/upstream/sql/
literal.rs1use crate::compat::types::{
2 PublicBytes, PublicDatetime, PublicDuration, PublicFile, PublicGeometry, PublicRegex,
3 PublicUuid,
4};
5use crate::compat::val::Geometry;
6use crate::upstream::fmt::{CoverStmts, EscapeObjectKey, Float, QuoteStr};
7use crate::upstream::sql::{Expr, RecordIdLit};
8use geo::{LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon};
9use rust_decimal::Decimal;
10use surrealdb_types::{SqlFormat, ToSql, write_sql};
11#[derive(Clone, Debug)]
12#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
13pub enum Literal {
14 None,
15 Null,
16 UnboundedRange,
17 Bool(bool),
18 Float(f64),
19 Integer(i64),
20 Decimal(
21 #[cfg_attr(
22 feature = "arbitrary",
23 arbitrary(with = crate::upstream::sql::arbitrary::arb_decimal)
24 )]
25 Decimal,
26 ),
27 Duration(PublicDuration),
28 String(String),
29 RecordId(RecordIdLit),
30 Datetime(PublicDatetime),
31 Uuid(PublicUuid),
32 Regex(PublicRegex),
33 Array(Vec<Expr>),
34 Set(Vec<Expr>),
35 Object(Vec<ObjectEntry>),
36 Geometry(PublicGeometry),
37 File(PublicFile),
38 Bytes(PublicBytes),
39}
40impl PartialEq for Literal {
41 fn eq(&self, other: &Self) -> bool {
42 match (self, other) {
43 (Literal::None, Literal::None) => true,
44 (Literal::Null, Literal::Null) => true,
45 (Literal::Bool(a), Literal::Bool(b)) => a == b,
46 (Literal::Float(a), Literal::Float(b)) => a.to_bits() == b.to_bits(),
47 (Literal::Integer(a), Literal::Integer(b)) => a == b,
48 (Literal::Decimal(a), Literal::Decimal(b)) => a == b,
49 (Literal::String(a), Literal::String(b)) => a == b,
50 (Literal::Bytes(a), Literal::Bytes(b)) => a == b,
51 (Literal::Regex(a), Literal::Regex(b)) => a == b,
52 (Literal::RecordId(a), Literal::RecordId(b)) => a == b,
53 (Literal::Array(a), Literal::Array(b)) => a == b,
54 (Literal::Set(a), Literal::Set(b)) => a == b,
55 (Literal::Object(a), Literal::Object(b)) => a == b,
56 (Literal::Duration(a), Literal::Duration(b)) => a == b,
57 (Literal::Datetime(a), Literal::Datetime(b)) => a == b,
58 (Literal::Uuid(a), Literal::Uuid(b)) => a == b,
59 (Literal::Geometry(a), Literal::Geometry(b)) => a == b,
60 (Literal::File(a), Literal::File(b)) => a == b,
61 _ => false,
62 }
63 }
64}
65impl Eq for Literal {}
66impl ToSql for Literal {
67 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
68 match self {
69 Literal::None => f.push_str("NONE"),
70 Literal::Null => f.push_str("NULL"),
71 Literal::UnboundedRange => f.push_str(".."),
72 Literal::Bool(x) => {
73 if *x {
74 f.push_str("true");
75 } else {
76 f.push_str("false");
77 }
78 }
79 Literal::Float(float) => write_sql!(f, fmt, "{}", Float(*float)),
80 Literal::Integer(x) => f.push_str(&x.to_string()),
81 Literal::Decimal(d) => d.fmt_sql(f, fmt),
82 Literal::String(strand) => write_sql!(f, fmt, "{}", QuoteStr(strand)),
83 Literal::Bytes(bytes) => bytes.fmt_sql(f, fmt),
84 Literal::Regex(regex) => regex.fmt_sql(f, fmt),
85 Literal::RecordId(record_id_lit) => record_id_lit.fmt_sql(f, fmt),
86 Literal::Array(exprs) => {
87 f.push('[');
88 if !exprs.is_empty() {
89 let fmt = fmt.increment();
90 if fmt.is_pretty() {
91 f.push('\n');
92 fmt.write_indent(f);
93 }
94 for (i, expr) in exprs.iter().enumerate() {
95 if i > 0 {
96 fmt.write_separator(f);
97 }
98 CoverStmts(expr).fmt_sql(f, fmt);
99 }
100 if fmt.is_pretty() {
101 f.push('\n');
102 if let SqlFormat::Indented(level) = fmt
103 && level > 0
104 {
105 for _ in 0..(level - 1) {
106 f.push('\t');
107 }
108 }
109 }
110 }
111 f.push(']');
112 }
113 Literal::Set(exprs) => {
114 f.push('{');
115 if !exprs.is_empty() {
116 let fmt = fmt.increment();
117 if fmt.is_pretty() {
118 f.push('\n');
119 fmt.write_indent(f);
120 }
121 for (i, expr) in exprs.iter().enumerate() {
122 if i > 0 {
123 fmt.write_separator(f);
124 } else if let Expr::Literal(Literal::RecordId(_)) = *expr {
125 f.push('(');
126 expr.fmt_sql(f, fmt);
127 f.push(')');
128 continue;
129 }
130 CoverStmts(expr).fmt_sql(f, fmt);
131 }
132 if exprs.len() == 1 {
133 f.push(',');
134 }
135 if fmt.is_pretty() {
136 f.push('\n');
137 if let SqlFormat::Indented(level) = fmt
138 && level > 0
139 {
140 for _ in 0..(level - 1) {
141 f.push('\t');
142 }
143 }
144 }
145 } else {
146 f.push(',');
147 }
148 f.push('}');
149 }
150 Literal::Object(items) => {
151 if fmt.is_pretty() {
152 f.push('{');
153 } else {
154 f.push_str("{ ");
155 }
156 if !items.is_empty() {
157 let fmt = fmt.increment();
158 if fmt.is_pretty() {
159 f.push('\n');
160 fmt.write_indent(f);
161 }
162 for (i, entry) in items.iter().enumerate() {
163 if i > 0 {
164 fmt.write_separator(f);
165 }
166 write_sql!(
167 f,
168 fmt,
169 "{}: {}",
170 EscapeObjectKey(&entry.key),
171 CoverStmts(&entry.value)
172 );
173 }
174 if fmt.is_pretty() {
175 f.push('\n');
176 if let SqlFormat::Indented(level) = fmt
177 && level > 0
178 {
179 for _ in 0..(level - 1) {
180 f.push('\t');
181 }
182 }
183 }
184 }
185 if fmt.is_pretty() {
186 f.push('}');
187 } else {
188 f.push_str(" }");
189 }
190 }
191 Literal::Duration(duration) => duration.fmt_sql(f, fmt),
192 Literal::Datetime(datetime) => {
193 f.push('d');
194 write_sql!(f, fmt, "{}", QuoteStr(&datetime.to_string()));
195 }
196 Literal::Uuid(uuid) => uuid.fmt_sql(f, fmt),
197 Literal::Geometry(geometry) => geometry.fmt_sql(f, fmt),
198 Literal::File(file) => file.fmt_sql(f, fmt),
199 }
200 }
201}
202fn collect_geometry(map: &[ObjectEntry]) -> Option<Geometry> {
209 if map.len() != 2 {
210 return None;
211 }
212 let ty_idx = map.iter().position(|x| x.key == "type")?;
213 let other = 1 ^ ty_idx;
214 let Expr::Literal(Literal::String(ty)) = &map[ty_idx].value else {
215 return None;
216 };
217 match ty.as_str() {
218 "Point" => {
219 let other = &map[other];
220 if other.key != "coordinates" {
221 return None;
222 }
223 let geom = collect_point(&other.value)?;
224 Some(Geometry::Point(geom))
225 }
226 "LineString" => {
227 let other = &map[other];
228 if other.key != "coordinates" {
229 return None;
230 }
231 let geom = collect_array(&other.value, collect_point)?;
232 Some(Geometry::Line(LineString::from(geom)))
233 }
234 "Polygon" => {
235 let other = &map[other];
236 if other.key != "coordinates" {
237 return None;
238 }
239 let geom = collect_polygon(&other.value)?;
240 Some(Geometry::Polygon(geom))
241 }
242 "MultiPoint" => {
243 let other = &map[other];
244 if other.key != "coordinates" {
245 return None;
246 }
247 let geom = collect_array(&other.value, collect_point)?;
248 Some(Geometry::MultiPoint(MultiPoint::new(geom)))
249 }
250 "MultiLineString" => {
251 let other = &map[other];
252 if other.key != "coordinates" {
253 return None;
254 }
255 let geom = collect_array(&other.value, |x| {
256 collect_array(x, collect_point).map(LineString::from)
257 })?;
258 Some(Geometry::MultiLine(MultiLineString::new(geom)))
259 }
260 "MultiPolygon" => {
261 let other = &map[other];
262 if other.key != "coordinates" {
263 return None;
264 }
265 let geom = collect_array(&other.value, collect_polygon)?;
266 Some(Geometry::MultiPolygon(MultiPolygon::new(geom)))
267 }
268 "GeometryCollection" => {
269 let other = &map[other];
270 if other.key != "geometries" {
271 return None;
272 }
273 let geom = collect_array(&other.value, |x| {
274 let Expr::Literal(Literal::Object(x)) = x else {
275 return None;
276 };
277 collect_geometry(x)
278 })?;
279 Some(Geometry::Collection(geom))
280 }
281 _ => None,
282 }
283}
284fn collect_polygon(expr: &Expr) -> Option<Polygon<f64>> {
285 let Expr::Literal(Literal::Array(x)) = expr else {
286 return None;
287 };
288 if x.is_empty() {
289 return None;
290 }
291 let first = LineString::from(collect_array(&x[0], collect_point)?);
292 let mut res = Vec::new();
293 for x in &x[1..] {
294 res.push(LineString::from(collect_array(x, collect_point)?))
295 }
296 Some(Polygon::new(first, res))
297}
298fn collect_point(expr: &Expr) -> Option<Point<f64>> {
299 let Expr::Literal(Literal::Array(array)) = expr else {
300 return None;
301 };
302 if array.len() != 2 {
303 return None;
304 }
305 let x = collect_number(&array[0])?;
306 let y = collect_number(&array[1])?;
307 Some(Point::new(x, y))
308}
309fn collect_number(expr: &Expr) -> Option<f64> {
310 let Expr::Literal(l) = expr else {
311 return None;
312 };
313 match l {
314 Literal::Integer(x) => Some(*x as f64),
315 Literal::Float(f) => Some(*f),
316 Literal::Decimal(_) => None,
317 _ => None,
318 }
319}
320fn collect_array<R, F: Fn(&Expr) -> Option<R>>(expr: &Expr, f: F) -> Option<Vec<R>> {
321 let Expr::Literal(Literal::Array(x)) = expr else {
322 return None;
323 };
324 x.iter().map(f).collect()
325}
326#[derive(Clone, Debug, Eq, PartialEq)]
327#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
328pub struct ObjectEntry {
329 pub key: String,
330 pub value: Expr,
331}
332impl ToSql for ObjectEntry {
333 fn fmt_sql(&self, f: &mut String, fmt: SqlFormat) {
334 write_sql!(f, fmt, "{}: {}", EscapeObjectKey(&self.key), self.value);
335 }
336}