1use crate::json_ops::{compare_json, json_to_display_string, json_to_f64, to_json_number};
7
8pub fn eval_function(name: &str, args: &[serde_json::Value]) -> serde_json::Value {
10 match name {
11 "upper" => str_arg(args, 0).map_or(serde_json::Value::Null, |s| {
13 serde_json::Value::String(s.to_uppercase())
14 }),
15 "lower" => str_arg(args, 0).map_or(serde_json::Value::Null, |s| {
16 serde_json::Value::String(s.to_lowercase())
17 }),
18 "trim" => str_arg(args, 0).map_or(serde_json::Value::Null, |s| {
19 serde_json::Value::String(s.trim().to_string())
20 }),
21 "ltrim" => str_arg(args, 0).map_or(serde_json::Value::Null, |s| {
22 serde_json::Value::String(s.trim_start().to_string())
23 }),
24 "rtrim" => str_arg(args, 0).map_or(serde_json::Value::Null, |s| {
25 serde_json::Value::String(s.trim_end().to_string())
26 }),
27 "length" | "char_length" | "character_length" => str_arg(args, 0)
28 .map_or(serde_json::Value::Null, |s| {
29 serde_json::Value::Number(serde_json::Number::from(s.len() as i64))
30 }),
31 "substr" | "substring" => {
32 let Some(s) = str_arg(args, 0) else {
33 return serde_json::Value::Null;
34 };
35 let start = num_arg(args, 1).unwrap_or(1.0) as usize;
36 let len = num_arg(args, 2).map(|n| n as usize);
37 let start_idx = start.saturating_sub(1); let result: String = match len {
39 Some(l) => s.chars().skip(start_idx).take(l).collect(),
40 None => s.chars().skip(start_idx).collect(),
41 };
42 serde_json::Value::String(result)
43 }
44 "concat" => {
45 let parts: Vec<String> = args.iter().map(json_to_display_string).collect();
46 serde_json::Value::String(parts.join(""))
47 }
48 "replace" => {
49 let Some(s) = str_arg(args, 0) else {
50 return serde_json::Value::Null;
51 };
52 let from = str_arg(args, 1).unwrap_or_default();
53 let to = str_arg(args, 2).unwrap_or_default();
54 serde_json::Value::String(s.replace(&from, &to))
55 }
56 "reverse" => str_arg(args, 0).map_or(serde_json::Value::Null, |s| {
57 serde_json::Value::String(s.chars().rev().collect())
58 }),
59
60 "abs" => num_arg(args, 0).map_or(serde_json::Value::Null, |n| to_json_number(n.abs())),
62 "round" => {
63 let Some(n) = num_arg(args, 0) else {
64 return serde_json::Value::Null;
65 };
66 let decimals = num_arg(args, 1).unwrap_or(0.0) as i32;
67 let factor = 10.0_f64.powi(decimals);
68 to_json_number((n * factor).round() / factor)
69 }
70 "ceil" | "ceiling" => {
71 num_arg(args, 0).map_or(serde_json::Value::Null, |n| to_json_number(n.ceil()))
72 }
73 "floor" => num_arg(args, 0).map_or(serde_json::Value::Null, |n| to_json_number(n.floor())),
74 "power" | "pow" => {
75 let Some(base) = num_arg(args, 0) else {
76 return serde_json::Value::Null;
77 };
78 let exp = num_arg(args, 1).unwrap_or(1.0);
79 to_json_number(base.powf(exp))
80 }
81 "sqrt" => num_arg(args, 0).map_or(serde_json::Value::Null, |n| to_json_number(n.sqrt())),
82 "mod" => {
83 let Some(a) = num_arg(args, 0) else {
84 return serde_json::Value::Null;
85 };
86 let b = num_arg(args, 1).unwrap_or(1.0);
87 if b == 0.0 {
88 serde_json::Value::Null
89 } else {
90 to_json_number(a % b)
91 }
92 }
93 "sign" => num_arg(args, 0).map_or(serde_json::Value::Null, |n| to_json_number(n.signum())),
94 "log" | "ln" => {
95 num_arg(args, 0).map_or(serde_json::Value::Null, |n| to_json_number(n.ln()))
96 }
97 "log10" => num_arg(args, 0).map_or(serde_json::Value::Null, |n| to_json_number(n.log10())),
98 "log2" => num_arg(args, 0).map_or(serde_json::Value::Null, |n| to_json_number(n.log2())),
99 "exp" => num_arg(args, 0).map_or(serde_json::Value::Null, |n| to_json_number(n.exp())),
100
101 "coalesce" => {
103 for arg in args {
104 if !arg.is_null() {
105 return arg.clone();
106 }
107 }
108 serde_json::Value::Null
109 }
110 "nullif" => {
111 if args.len() >= 2 && args[0] == args[1] {
112 serde_json::Value::Null
113 } else {
114 args.first().cloned().unwrap_or(serde_json::Value::Null)
115 }
116 }
117 "greatest" => args
118 .iter()
119 .filter(|v| !v.is_null())
120 .max_by(|a, b| compare_json(a, b))
121 .cloned()
122 .unwrap_or(serde_json::Value::Null),
123 "least" => args
124 .iter()
125 .filter(|v| !v.is_null())
126 .min_by(|a, b| compare_json(a, b))
127 .cloned()
128 .unwrap_or(serde_json::Value::Null),
129
130 "uuid" | "uuid_v4" | "gen_random_uuid" => {
132 serde_json::Value::String(nodedb_types::id_gen::uuid_v4())
133 }
134 "uuid_v7" => serde_json::Value::String(nodedb_types::id_gen::uuid_v7()),
135 "ulid" => serde_json::Value::String(nodedb_types::id_gen::ulid()),
136 "cuid2" => serde_json::Value::String(nodedb_types::id_gen::cuid2()),
137 "nanoid" => {
138 let len = num_arg(args, 0).map(|n| n as usize);
139 match len {
140 Some(l) => serde_json::Value::String(nodedb_types::id_gen::nanoid_with_length(l)),
141 None => serde_json::Value::String(nodedb_types::id_gen::nanoid()),
142 }
143 }
144
145 "is_uuid" => bool_id_check(args, nodedb_types::id_gen::is_uuid),
147 "is_ulid" => bool_id_check(args, nodedb_types::id_gen::is_ulid),
148 "is_cuid2" => bool_id_check(args, nodedb_types::id_gen::is_cuid2),
149 "is_nanoid" => bool_id_check(args, nodedb_types::id_gen::is_nanoid),
150 "id_type" => args
151 .first()
152 .and_then(|v| v.as_str())
153 .map_or(serde_json::Value::String("unknown".into()), |s| {
154 serde_json::Value::String(nodedb_types::id_gen::detect_id_type(s).to_string())
155 }),
156 "uuid_version" => args
157 .first()
158 .and_then(|v| v.as_str())
159 .map_or(serde_json::Value::Number(0.into()), |s| {
160 serde_json::Value::Number(nodedb_types::id_gen::uuid_version(s).into())
161 }),
162 "ulid_timestamp" => args
163 .first()
164 .and_then(|v| v.as_str())
165 .and_then(nodedb_types::id_gen::ulid_timestamp_ms)
166 .map_or(serde_json::Value::Null, |ms| {
167 serde_json::Value::Number(serde_json::Number::from(ms as i64))
168 }),
169
170 "now" | "current_timestamp" => {
172 let dt = nodedb_types::NdbDateTime::now();
173 serde_json::Value::String(dt.to_iso8601())
174 }
175 "datetime" | "to_datetime" => args
176 .first()
177 .and_then(|v| match v {
178 serde_json::Value::String(s) => nodedb_types::NdbDateTime::parse(s)
179 .map(|dt| serde_json::Value::String(dt.to_iso8601())),
180 serde_json::Value::Number(n) => {
181 let micros = n.as_i64().unwrap_or(0);
182 Some(serde_json::Value::String(
183 nodedb_types::NdbDateTime::from_micros(micros).to_iso8601(),
184 ))
185 }
186 _ => None,
187 })
188 .unwrap_or(serde_json::Value::Null),
189 "unix_secs" | "epoch_secs" => args
190 .first()
191 .and_then(|v| v.as_str())
192 .and_then(nodedb_types::NdbDateTime::parse)
193 .map_or(serde_json::Value::Null, |dt| {
194 serde_json::Value::Number(dt.unix_secs().into())
195 }),
196 "unix_millis" | "epoch_millis" => args
197 .first()
198 .and_then(|v| v.as_str())
199 .and_then(nodedb_types::NdbDateTime::parse)
200 .map_or(serde_json::Value::Null, |dt| {
201 serde_json::Value::Number(dt.unix_millis().into())
202 }),
203 "extract" | "date_part" => {
204 let part = args.first().and_then(|v| v.as_str()).unwrap_or("");
205 let dt = args
206 .get(1)
207 .and_then(|v| v.as_str())
208 .and_then(nodedb_types::NdbDateTime::parse);
209 match dt {
210 Some(dt) => {
211 let c = dt.components();
212 let val: i64 = match part.to_lowercase().as_str() {
213 "year" | "y" => c.year as i64,
214 "month" | "mon" => c.month as i64,
215 "day" | "d" => c.day as i64,
216 "hour" | "h" => c.hour as i64,
217 "minute" | "min" | "m" => c.minute as i64,
218 "second" | "sec" | "s" => c.second as i64,
219 "microsecond" | "us" => c.microsecond as i64,
220 "epoch" => dt.unix_secs(),
221 "dow" | "dayofweek" => {
222 let days = dt.micros / 86_400_000_000;
223 (days + 4) % 7
224 }
225 _ => return serde_json::Value::Null,
226 };
227 serde_json::Value::Number(val.into())
228 }
229 None => serde_json::Value::Null,
230 }
231 }
232 "date_trunc" | "datetrunc" => {
233 let part = args.first().and_then(|v| v.as_str()).unwrap_or("");
234 let dt = args
235 .get(1)
236 .and_then(|v| v.as_str())
237 .and_then(nodedb_types::NdbDateTime::parse);
238 match dt {
239 Some(dt) => {
240 let c = dt.components();
241 let truncated = match part.to_lowercase().as_str() {
242 "year" => nodedb_types::NdbDateTime::parse(&format!(
243 "{:04}-01-01T00:00:00Z",
244 c.year
245 )),
246 "month" => nodedb_types::NdbDateTime::parse(&format!(
247 "{:04}-{:02}-01T00:00:00Z",
248 c.year, c.month
249 )),
250 "day" => nodedb_types::NdbDateTime::parse(&format!(
251 "{:04}-{:02}-{:02}T00:00:00Z",
252 c.year, c.month, c.day
253 )),
254 "hour" => nodedb_types::NdbDateTime::parse(&format!(
255 "{:04}-{:02}-{:02}T{:02}:00:00Z",
256 c.year, c.month, c.day, c.hour
257 )),
258 "minute" => nodedb_types::NdbDateTime::parse(&format!(
259 "{:04}-{:02}-{:02}T{:02}:{:02}:00Z",
260 c.year, c.month, c.day, c.hour, c.minute
261 )),
262 "second" => nodedb_types::NdbDateTime::parse(&format!(
263 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}Z",
264 c.year, c.month, c.day, c.hour, c.minute, c.second
265 )),
266 _ => None,
267 };
268 truncated.map_or(serde_json::Value::Null, |t| {
269 serde_json::Value::String(t.to_iso8601())
270 })
271 }
272 None => serde_json::Value::Null,
273 }
274 }
275 "date_add" | "datetime_add" => {
276 let dt = args
277 .first()
278 .and_then(|v| v.as_str())
279 .and_then(nodedb_types::NdbDateTime::parse);
280 let dur = args
281 .get(1)
282 .and_then(|v| v.as_str())
283 .and_then(nodedb_types::NdbDuration::parse);
284 match (dt, dur) {
285 (Some(dt), Some(dur)) => {
286 serde_json::Value::String(dt.add_duration(dur).to_iso8601())
287 }
288 _ => serde_json::Value::Null,
289 }
290 }
291 "date_sub" | "datetime_sub" => {
292 let dt = args
293 .first()
294 .and_then(|v| v.as_str())
295 .and_then(nodedb_types::NdbDateTime::parse);
296 let dur = args
297 .get(1)
298 .and_then(|v| v.as_str())
299 .and_then(nodedb_types::NdbDuration::parse);
300 match (dt, dur) {
301 (Some(dt), Some(dur)) => {
302 serde_json::Value::String(dt.sub_duration(dur).to_iso8601())
303 }
304 _ => serde_json::Value::Null,
305 }
306 }
307 "date_diff" | "datediff" => {
308 let dt1 = args
309 .first()
310 .and_then(|v| v.as_str())
311 .and_then(nodedb_types::NdbDateTime::parse);
312 let dt2 = args
313 .get(1)
314 .and_then(|v| v.as_str())
315 .and_then(nodedb_types::NdbDateTime::parse);
316 match (dt1, dt2) {
317 (Some(a), Some(b)) => to_json_number(a.duration_since(&b).as_secs_f64()),
318 _ => serde_json::Value::Null,
319 }
320 }
321 "duration" | "to_duration" => args
322 .first()
323 .and_then(|v| v.as_str())
324 .and_then(nodedb_types::NdbDuration::parse)
325 .map_or(serde_json::Value::Null, |d| {
326 serde_json::Value::String(d.to_human())
327 }),
328
329 "decimal" | "to_decimal" => args.first().map_or(serde_json::Value::Null, |v| {
330 let s = json_to_display_string(v);
331 match s.parse::<rust_decimal::Decimal>() {
332 Ok(d) => serde_json::Value::String(d.to_string()),
333 Err(_) => serde_json::Value::Null,
334 }
335 }),
336
337 "json_extract" | "json_get" => {
339 let obj = args.first().unwrap_or(&serde_json::Value::Null);
340 let path = args.get(1).and_then(|v| v.as_str()).unwrap_or("");
341 let mut current = obj;
342 for key in path.split('.') {
343 current = match current {
344 serde_json::Value::Object(map) => {
345 map.get(key).unwrap_or(&serde_json::Value::Null)
346 }
347 serde_json::Value::Array(arr) => key
348 .parse::<usize>()
349 .ok()
350 .and_then(|i| arr.get(i))
351 .unwrap_or(&serde_json::Value::Null),
352 _ => &serde_json::Value::Null,
353 };
354 }
355 current.clone()
356 }
357 "json_set" => {
358 let mut obj = args
359 .first()
360 .cloned()
361 .unwrap_or(serde_json::Value::Object(serde_json::Map::new()));
362 let key = args.get(1).and_then(|v| v.as_str()).unwrap_or("");
363 let val = args.get(2).cloned().unwrap_or(serde_json::Value::Null);
364 if let serde_json::Value::Object(ref mut map) = obj {
365 map.insert(key.to_string(), val);
366 }
367 obj
368 }
369 "json_remove" => {
370 let mut obj = args.first().cloned().unwrap_or(serde_json::Value::Null);
371 let key = args.get(1).and_then(|v| v.as_str()).unwrap_or("");
372 if let serde_json::Value::Object(ref mut map) = obj {
373 map.remove(key);
374 }
375 obj
376 }
377 "json_keys" => match args.first() {
378 Some(serde_json::Value::Object(map)) => serde_json::Value::Array(
379 map.keys()
380 .map(|k| serde_json::Value::String(k.clone()))
381 .collect(),
382 ),
383 _ => serde_json::Value::Null,
384 },
385 "json_values" => match args.first() {
386 Some(serde_json::Value::Object(map)) => {
387 serde_json::Value::Array(map.values().cloned().collect())
388 }
389 _ => serde_json::Value::Null,
390 },
391 "json_length" | "json_len" => match args.first() {
392 Some(serde_json::Value::Object(map)) => {
393 serde_json::Value::Number(serde_json::Number::from(map.len() as i64))
394 }
395 Some(serde_json::Value::Array(arr)) => {
396 serde_json::Value::Number(serde_json::Number::from(arr.len() as i64))
397 }
398 Some(serde_json::Value::String(s)) => {
399 serde_json::Value::Number(serde_json::Number::from(s.len() as i64))
400 }
401 _ => serde_json::Value::Null,
402 },
403 "json_type" => {
404 let type_name = match args.first() {
405 Some(serde_json::Value::Null) | None => "null",
406 Some(serde_json::Value::Bool(_)) => "boolean",
407 Some(serde_json::Value::Number(_)) => "number",
408 Some(serde_json::Value::String(_)) => "string",
409 Some(serde_json::Value::Array(_)) => "array",
410 Some(serde_json::Value::Object(_)) => "object",
411 };
412 serde_json::Value::String(type_name.into())
413 }
414 "json_array" => serde_json::Value::Array(args.to_vec()),
415 "json_object" => {
416 let mut map = serde_json::Map::new();
417 let mut i = 0;
418 while i + 1 < args.len() {
419 let key = json_to_display_string(&args[i]);
420 let val = args[i + 1].clone();
421 map.insert(key, val);
422 i += 2;
423 }
424 serde_json::Value::Object(map)
425 }
426 "json_contains" => {
427 let container = args.first().unwrap_or(&serde_json::Value::Null);
428 let needle = args.get(1).unwrap_or(&serde_json::Value::Null);
429 let result = match container {
430 serde_json::Value::Array(arr) => arr.contains(needle),
431 serde_json::Value::Object(map) => {
432 if let Some(key) = needle.as_str() {
433 map.contains_key(key)
434 } else {
435 false
436 }
437 }
438 _ => false,
439 };
440 serde_json::Value::Bool(result)
441 }
442 "json_merge" | "json_patch" => {
443 let mut base = match args.first() {
444 Some(serde_json::Value::Object(m)) => m.clone(),
445 _ => serde_json::Map::new(),
446 };
447 if let Some(serde_json::Value::Object(overlay)) = args.get(1) {
448 for (k, v) in overlay {
449 base.insert(k.clone(), v.clone());
450 }
451 }
452 serde_json::Value::Object(base)
453 }
454
455 "typeof" | "type_of" => {
457 let type_name = match args.first() {
458 Some(serde_json::Value::Null) => "null",
459 Some(serde_json::Value::Bool(_)) => "bool",
460 Some(serde_json::Value::Number(n)) => {
461 if n.is_i64() {
462 "int"
463 } else {
464 "float"
465 }
466 }
467 Some(serde_json::Value::String(_)) => "string",
468 Some(serde_json::Value::Array(_)) => "array",
469 Some(serde_json::Value::Object(_)) => "object",
470 None => "null",
471 };
472 serde_json::Value::String(type_name.to_string())
473 }
474
475 other => {
477 crate::geo_functions::eval_geo_function(other, args).unwrap_or(serde_json::Value::Null)
478 }
479 }
480}
481
482fn str_arg(args: &[serde_json::Value], idx: usize) -> Option<String> {
486 args.get(idx)?.as_str().map(|s| s.to_string())
487}
488
489fn num_arg(args: &[serde_json::Value], idx: usize) -> Option<f64> {
491 args.get(idx).and_then(|v| json_to_f64(v, true))
492}
493
494fn bool_id_check(args: &[serde_json::Value], check: impl Fn(&str) -> bool) -> serde_json::Value {
496 args.first()
497 .and_then(|v| v.as_str())
498 .map_or(serde_json::Value::Bool(false), |s| {
499 serde_json::Value::Bool(check(s))
500 })
501}
502
503#[cfg(test)]
504mod tests {
505 use super::*;
506 use crate::expr::SqlExpr;
507 use serde_json::json;
508
509 fn eval_fn(name: &str, args: Vec<serde_json::Value>) -> serde_json::Value {
510 eval_function(name, &args)
511 }
512
513 #[test]
514 fn upper() {
515 assert_eq!(eval_fn("upper", vec![json!("hello")]), json!("HELLO"));
516 }
517
518 #[test]
519 fn upper_null_propagation() {
520 assert_eq!(eval_fn("upper", vec![json!(null)]), json!(null));
521 }
522
523 #[test]
524 fn substring() {
525 assert_eq!(
526 eval_fn("substr", vec![json!("hello"), json!(2), json!(3)]),
527 json!("ell")
528 );
529 }
530
531 #[test]
532 fn round_with_decimals() {
533 assert_eq!(
534 eval_fn("round", vec![json!(3.15159), json!(2)]),
535 json!(3.15)
536 );
537 }
538
539 #[test]
540 fn typeof_int() {
541 assert_eq!(eval_fn("typeof", vec![json!(42)]), json!("int"));
542 }
543
544 #[test]
545 fn function_via_expr() {
546 let expr = SqlExpr::Function {
547 name: "upper".into(),
548 args: vec![SqlExpr::Column("name".into())],
549 };
550 let doc = json!({"name": "alice"});
551 assert_eq!(expr.eval(&doc), json!("ALICE"));
552 }
553
554 #[test]
555 fn geo_geohash_encode() {
556 let result = eval_fn(
557 "geo_geohash",
558 vec![json!(-73.9857), json!(40.758), json!(6)],
559 );
560 let hash = result.as_str().unwrap();
561 assert_eq!(hash.len(), 6);
562 assert!(hash.starts_with("dr5ru"), "got {hash}");
563 }
564
565 #[test]
566 fn geo_geohash_decode() {
567 let hash = eval_fn("geo_geohash", vec![json!(0.0), json!(0.0), json!(6)]);
568 let result = eval_fn("geo_geohash_decode", vec![hash]);
569 assert!(result.is_object());
570 assert!(result["min_lng"].as_f64().is_some());
571 assert!(result["max_lat"].as_f64().is_some());
572 }
573
574 #[test]
575 fn geo_geohash_neighbors_returns_8() {
576 let hash = eval_fn("geo_geohash", vec![json!(10.0), json!(50.0), json!(6)]);
577 let result = eval_fn("geo_geohash_neighbors", vec![hash]);
578 let arr = result.as_array().unwrap();
579 assert_eq!(arr.len(), 8);
580 }
581
582 fn point_json(lng: f64, lat: f64) -> serde_json::Value {
583 json!({"type": "Point", "coordinates": [lng, lat]})
584 }
585
586 fn square_json() -> serde_json::Value {
587 json!({"type": "Polygon", "coordinates": [[[0.0,0.0],[10.0,0.0],[10.0,10.0],[0.0,10.0],[0.0,0.0]]]})
588 }
589
590 #[test]
591 fn st_contains_sql() {
592 let result = eval_fn("st_contains", vec![square_json(), point_json(5.0, 5.0)]);
593 assert_eq!(result, json!(true));
594 }
595
596 #[test]
597 fn st_intersects_sql() {
598 let result = eval_fn("st_intersects", vec![square_json(), point_json(5.0, 0.0)]);
599 assert_eq!(result, json!(true));
600 }
601
602 #[test]
603 fn st_distance_sql() {
604 let result = eval_fn(
605 "st_distance",
606 vec![point_json(0.0, 0.0), point_json(0.0, 1.0)],
607 );
608 let d = result.as_f64().unwrap();
609 assert!((d - 111_195.0).abs() < 500.0, "got {d}");
610 }
611
612 #[test]
613 fn st_dwithin_sql() {
614 let result = eval_fn(
615 "st_dwithin",
616 vec![point_json(0.0, 0.0), point_json(0.001, 0.0), json!(200.0)],
617 );
618 assert_eq!(result, json!(true));
619 }
620
621 #[test]
622 fn st_buffer_sql() {
623 let result = eval_fn(
624 "st_buffer",
625 vec![point_json(0.0, 0.0), json!(1000.0), json!(8)],
626 );
627 assert!(result.is_object());
628 assert_eq!(result["type"], "Polygon");
629 }
630
631 #[test]
632 fn st_envelope_sql() {
633 let result = eval_fn("st_envelope", vec![square_json()]);
634 assert_eq!(result["type"], "Polygon");
635 }
636
637 #[test]
638 fn geo_length_sql() {
639 let line = json!({"type": "LineString", "coordinates": [[0.0,0.0],[0.0,1.0]]});
640 let result = eval_fn("geo_length", vec![line]);
641 let d = result.as_f64().unwrap();
642 assert!((d - 111_195.0).abs() < 500.0, "got {d}");
643 }
644
645 #[test]
646 fn geo_x_y() {
647 assert_eq!(
648 eval_fn("geo_x", vec![point_json(5.0, 10.0)])
649 .as_f64()
650 .unwrap(),
651 5.0
652 );
653 assert_eq!(
654 eval_fn("geo_y", vec![point_json(5.0, 10.0)])
655 .as_f64()
656 .unwrap(),
657 10.0
658 );
659 }
660
661 #[test]
662 fn geo_type_sql() {
663 assert_eq!(
664 eval_fn("geo_type", vec![point_json(0.0, 0.0)]),
665 json!("Point")
666 );
667 assert_eq!(eval_fn("geo_type", vec![square_json()]), json!("Polygon"));
668 }
669
670 #[test]
671 fn geo_num_points_sql() {
672 assert_eq!(
673 eval_fn("geo_num_points", vec![point_json(0.0, 0.0)]),
674 json!(1)
675 );
676 assert_eq!(eval_fn("geo_num_points", vec![square_json()]), json!(5));
677 }
678
679 #[test]
680 fn geo_is_valid_sql() {
681 assert_eq!(eval_fn("geo_is_valid", vec![square_json()]), json!(true));
682 }
683
684 #[test]
685 fn geo_as_wkt_sql() {
686 let result = eval_fn("geo_as_wkt", vec![point_json(5.0, 10.0)]);
687 assert_eq!(result, json!("POINT(5 10)"));
688 }
689
690 #[test]
691 fn geo_from_wkt_sql() {
692 let result = eval_fn("geo_from_wkt", vec![json!("POINT(5 10)")]);
693 assert_eq!(result["type"], "Point");
694 }
695
696 #[test]
697 fn geo_circle_sql() {
698 let result = eval_fn(
699 "geo_circle",
700 vec![json!(0.0), json!(0.0), json!(1000.0), json!(16)],
701 );
702 assert_eq!(result["type"], "Polygon");
703 }
704
705 #[test]
706 fn geo_bbox_sql() {
707 let result = eval_fn(
708 "geo_bbox",
709 vec![json!(0.0), json!(0.0), json!(10.0), json!(10.0)],
710 );
711 assert_eq!(result["type"], "Polygon");
712 }
713}