Skip to main content

surrealdb_expr/val/
mod.rs

1#![allow(clippy::derive_ord_xor_partial_ord)]
2
3use std::cmp::Ordering;
4use std::collections::{BTreeMap, HashMap};
5use std::ops::Bound;
6use std::time::Duration as StdDuration;
7
8use anyhow::{Result, bail};
9use chrono::{DateTime, Utc};
10use common::fmt::{EscapeIdent, QuoteStr};
11use geo::Point;
12use revision::revisioned;
13use rust_decimal::prelude::*;
14use storekey::{BorrowDecode, Encode};
15use surrealdb_collections::VecMap;
16pub use surrealdb_strand::Strand;
17use surrealdb_types::{SqlFormat, ToSql, write_sql};
18
19use crate::expr;
20use crate::expr::Error;
21use crate::expr::kind::GeometryKind;
22use crate::expr::statements::info::InfoStructure;
23use crate::val::table_name_public::IntoPublicTable;
24
25pub mod array;
26pub mod bytes;
27pub mod closure;
28pub mod convert_public;
29pub mod datetime;
30pub mod duration;
31pub mod evaluator;
32pub mod field_path;
33pub mod file;
34pub mod geometry;
35pub mod number;
36pub mod object;
37pub mod range;
38pub mod record_id;
39pub mod regex;
40pub mod rnd;
41pub mod set;
42pub mod table_name_public;
43pub mod uuid;
44pub mod value;
45
46pub use surrealdb_strand::TableName;
47
48pub use self::array::Array;
49pub use self::bytes::Bytes;
50pub use self::closure::Closure;
51pub use self::convert_public::convert_public_value_to_internal;
52pub use self::datetime::Datetime;
53pub use self::duration::Duration;
54pub use self::evaluator::{BoxInvokeFut, ClosureEvaluator};
55pub use self::file::File;
56pub use self::geometry::Geometry;
57pub use self::number::Number;
58pub use self::object::Object;
59pub use self::range::Range;
60pub use self::record_id::{RecordId, RecordIdKey, RecordIdKeyRange, RecordIdentity};
61pub use self::regex::Regex;
62pub use self::set::Set;
63pub use self::uuid::Uuid;
64pub use self::value::{CastError, CoerceError};
65
66/// Marker type for a different serialization format for value which does not encode type
67/// information which is not required for indexing.
68pub enum IndexFormat {}
69
70/// Marker type for value conversions from Value::None
71#[derive(Clone, Copy, Eq, PartialEq, PartialOrd)]
72pub struct SqlNone;
73
74/// Marker type for value conversions from Value::Null
75#[derive(Clone, Copy, Eq, PartialEq, PartialOrd)]
76pub struct Null;
77
78// `revision(1)` is the legacy on-disk format (varint discriminant +
79// payload). `revision(2, optimised)` wraps each value in a
80// 1-byte tag (variant id + size class) so walkers can skip any value in
81// O(1) via the `u32_le` length prefix on varlen variants.
82//
83// Optimised enum walkers are "materialised" — they don't expose
84// `into_<variant>()` for streaming descent. Instead the macro emits
85// `<variant>_view()` accessors that return `OwnedVariantView<T>` carrying
86// a `Cow<'r, [u8]>` over the variant body. With the `WalkRevisioned:
87// BorrowedReader` bound the source is slice-backed, so the Cow is
88// borrowed — no `Vec<u8>` allocation per descent. The caller then
89// constructs a fresh walker on the borrowed bytes:
90//
91//     let view = walker.object_view()?;          // zero-alloc borrow
92//     let mut r: &[u8] = view.as_bytes();
93//     let inner = Object::walk_revisioned(&mut r)?; // streaming Object walker
94//
95// This recovers fully streaming descent at the Value level while picking
96// up the rev-2 wire compactness. Combined with rev-2 `Object`/`Array`/
97// `Set` indexed prologues, the planner hot path gets O(log n) Map
98// binary search, O(1) Array random access, and O(1) skip past any Value
99// — all zero-alloc through the descent chain.
100#[revisioned(revision(1), revision(2, optimised))]
101#[derive(Clone, Debug, Default, PartialEq, PartialOrd, Hash, Encode, BorrowDecode)]
102#[storekey(format = "()")]
103#[storekey(format = "IndexFormat")]
104pub enum Value {
105	#[default]
106	#[revision(size = "inline")]
107	None,
108	#[revision(size = "inline")]
109	Null,
110	#[revision(size = "fixed(1)")]
111	Bool(bool),
112	#[revision(size = "varlen")]
113	Number(Number),
114	#[revision(size = "varlen")]
115	String(Strand),
116	#[revision(size = "varlen")]
117	Duration(Duration),
118	#[revision(size = "varlen")]
119	Datetime(Datetime),
120	#[revision(size = "varlen")]
121	Uuid(Uuid),
122	#[revision(size = "varlen")]
123	Array(Array),
124	#[revision(size = "varlen")]
125	Set(Set),
126	#[revision(size = "varlen")]
127	Object(Object),
128	#[revision(size = "varlen")]
129	Geometry(Geometry),
130	#[revision(size = "varlen")]
131	Bytes(Bytes),
132	#[revision(size = "varlen")]
133	Table(TableName),
134	#[revision(size = "varlen")]
135	RecordId(RecordId),
136	#[revision(size = "varlen")]
137	File(File),
138	#[revision(size = "varlen")]
139	Regex(Regex),
140	#[revision(size = "varlen")]
141	Range(Box<Range>),
142	// Closure's manual `SerializeRevisioned` impl errors regardless of the
143	// envelope. Mark `varlen` for completeness; the macro requires every
144	// variant to declare a size class under `optimised`.
145	#[revision(size = "varlen")]
146	Closure(Box<Closure>),
147	// Add new variants here
148}
149
150impl Eq for Value {}
151
152impl Ord for Value {
153	fn cmp(&self, other: &Self) -> Ordering {
154		self.partial_cmp(other).unwrap_or(Ordering::Equal)
155	}
156}
157
158impl Value {
159	/// Whether a `HashMap`/`HashSet` **miss** on this value proves the collection
160	/// does not hold it — i.e. whether this value's [`Hash`](std::hash::Hash) is
161	/// consistent with its [`PartialEq`].
162	///
163	/// [`Number`] is the one leaf where the two disagree, and it cannot be
164	/// repaired: see [`Number`]'s `Hash` impl for why. A value that can reach a
165	/// [`Number`] may hash into a different bucket than an equal value, so
166	/// `contains` answers `false` for something the collection holds.
167	///
168	/// A probe is decisive as soon as *either* side is number-free, because
169	/// equality requires matching variants at every level: two values can only
170	/// hash apart while comparing equal if both carry a [`Number`] in the same
171	/// position. So a caller may trust a miss when this returns `true` for the
172	/// probed value, *or* when it returns `true` for every element of the
173	/// collection.
174	///
175	/// Variants whose hash/equality pairing is not established — [`Value::Geometry`]
176	/// (hashes `f64` coordinates), [`Value::Regex`] (equality with
177	/// [`Value::String`] is asymmetric), [`Value::Range`] and [`Value::Closure`] —
178	/// report `false` so they take the same conservative path.
179	pub fn hash_agrees_with_eq(&self) -> bool {
180		match self {
181			Value::None
182			| Value::Null
183			| Value::Bool(_)
184			| Value::String(_)
185			| Value::Duration(_)
186			| Value::Datetime(_)
187			| Value::Uuid(_)
188			| Value::Bytes(_)
189			| Value::Table(_)
190			| Value::File(_) => true,
191			Value::Number(_)
192			| Value::Geometry(_)
193			| Value::Regex(_)
194			| Value::Range(_)
195			| Value::Closure(_) => false,
196			Value::Array(a) => a.iter().all(Value::hash_agrees_with_eq),
197			Value::Set(s) => s.iter().all(Value::hash_agrees_with_eq),
198			// An `Object`'s equality covers its keys too, but those are `Strand`
199			// (hash is `as_str().hash`), so only the values can carry a `Number`.
200			Value::Object(o) => o.values().all(Value::hash_agrees_with_eq),
201			Value::RecordId(r) => r.key.hash_agrees_with_eq(),
202		}
203	}
204
205	pub const NONE: Self = Self::None;
206
207	// -----------------------------------
208	// Initial record value
209	// -----------------------------------
210
211	/// Create an empty Object Value
212	pub fn empty_object() -> Self {
213		Value::Object(Object::default())
214	}
215
216	// -----------------------------------
217	// Simple value detection
218	// -----------------------------------
219
220	/// Check if this Value is not NONE
221	pub fn is_some(&self) -> bool {
222		!matches!(self, Value::None)
223	}
224
225	/// Check if this Value is NONE or NULL
226	pub fn is_nullish(&self) -> bool {
227		matches!(self, Value::None | Value::Null)
228	}
229
230	/// Check if this Value is NONE
231	pub fn is_empty_array(&self) -> bool {
232		if let Value::Array(v) = self {
233			v.is_empty()
234		} else {
235			false
236		}
237	}
238
239	/// Check if this Value is truthy
240	pub fn is_truthy(&self) -> bool {
241		match self {
242			Value::Bool(v) => *v,
243			Value::Uuid(_) => true,
244			Value::RecordId(_) => true,
245			Value::Geometry(_) => true,
246			Value::Datetime(_) => true,
247			Value::Bytes(v) => !v.is_empty(),
248			Value::Array(v) => !v.is_empty(),
249			Value::Set(v) => !v.is_empty(),
250			Value::Object(v) => !v.is_empty(),
251			Value::String(v) => !v.is_empty(),
252			Value::Number(v) => v.is_truthy(),
253			Value::Duration(v) => v.as_nanos() > 0,
254			_ => false,
255		}
256	}
257
258	/// Check if this Value is an int Number
259	pub fn is_int(&self) -> bool {
260		matches!(self, Value::Number(Number::Int(_)))
261	}
262
263	/// Check if this Value is a float Number
264	pub fn is_float(&self) -> bool {
265		matches!(self, Value::Number(Number::Float(_)))
266	}
267
268	/// Check if this Value is a decimal Number
269	pub fn is_decimal(&self) -> bool {
270		matches!(self, Value::Number(Number::Decimal(_)))
271	}
272
273	/// Check if this Value is a RecordId of a specific type
274	pub fn is_record_type(&self, types: &[TableName]) -> bool {
275		match self {
276			Value::RecordId(v) => v.is_table_type(types),
277			_ => false,
278		}
279	}
280
281	/// Check if this Value is a Geometry of a specific type
282	pub fn is_geometry_type(&self, types: &[GeometryKind]) -> bool {
283		match self {
284			Value::Geometry(Geometry::Point(_)) => {
285				types.iter().any(|t| matches!(t, GeometryKind::Point))
286			}
287			Value::Geometry(Geometry::Line(_)) => {
288				types.iter().any(|t| matches!(t, GeometryKind::Line))
289			}
290			Value::Geometry(Geometry::Polygon(_)) => {
291				types.iter().any(|t| matches!(t, GeometryKind::Polygon))
292			}
293			Value::Geometry(Geometry::MultiPoint(_)) => {
294				types.iter().any(|t| matches!(t, GeometryKind::MultiPoint))
295			}
296			Value::Geometry(Geometry::MultiLine(_)) => {
297				types.iter().any(|t| matches!(t, GeometryKind::MultiLine))
298			}
299			Value::Geometry(Geometry::MultiPolygon(_)) => {
300				types.iter().any(|t| matches!(t, GeometryKind::MultiPolygon))
301			}
302			Value::Geometry(Geometry::Collection(_)) => {
303				types.iter().any(|t| matches!(t, GeometryKind::Collection))
304			}
305			_ => false,
306		}
307	}
308
309	// -----------------------------------
310	// Simple conversion of value
311	// -----------------------------------
312
313	/// Converts this Value into an unquoted String
314	pub fn into_raw_string(self) -> String {
315		match self {
316			Value::String(v) => v.into_string(),
317			Value::Uuid(v) => v.to_string(),
318			Value::Datetime(v) => v.to_string(),
319			_ => self.to_sql(),
320		}
321	}
322
323	/// Converts this Value into an unquoted String
324	pub fn to_raw_string(&self) -> String {
325		match self {
326			Value::String(v) => v.as_str().to_owned(),
327			Value::Uuid(v) => v.to_string(),
328			Value::Datetime(v) => v.to_string(),
329			_ => self.to_sql(),
330		}
331	}
332
333	/// Returns the surql representation of the kind of the value as a string.
334	///
335	/// # Warning
336	/// This function is not fully implement for all variants, make sure you
337	/// don't accidentally use it where it can return an invalid value.
338	pub fn kind_of(&self) -> &'static str {
339		match self {
340			Self::None => "none",
341			Self::Null => "null",
342			Self::Bool(_) => "bool",
343			Self::Uuid(_) => "uuid",
344			Self::Array(_) => "array",
345			Self::Set(_) => "set",
346			Self::Object(_) => "object",
347			Self::String(_) => "string",
348			Self::Duration(_) => "duration",
349			Self::Datetime(_) => "datetime",
350			Self::Closure(_) => "function",
351			Self::Number(Number::Int(_)) => "int",
352			Self::Number(Number::Float(_)) => "float",
353			Self::Number(Number::Decimal(_)) => "decimal",
354			Self::Geometry(Geometry::Point(_)) => "geometry<point>",
355			Self::Geometry(Geometry::Line(_)) => "geometry<line>",
356			Self::Geometry(Geometry::Polygon(_)) => "geometry<polygon>",
357			Self::Geometry(Geometry::MultiPoint(_)) => "geometry<multipoint>",
358			Self::Geometry(Geometry::MultiLine(_)) => "geometry<multiline>",
359			Self::Geometry(Geometry::MultiPolygon(_)) => "geometry<multipolygon>",
360			Self::Geometry(Geometry::Collection(_)) => "geometry<collection>",
361			Self::Regex(_) => "regex",
362			Self::File(_) => "file",
363			Self::Bytes(_) => "bytes",
364			Self::Range(_) => "range",
365			Self::RecordId(_) => "record",
366			Self::Table(_) => "table",
367		}
368	}
369
370	// -----------------------------------
371	// Value operations
372	// -----------------------------------
373
374	/// Check if this Value is equal to another Value
375	pub fn equal(&self, other: &Value) -> bool {
376		match self {
377			Value::None => other.is_none(),
378			Value::Null => other.is_null(),
379			Value::Bool(v) => match other {
380				Value::Bool(w) => v == w,
381				_ => false,
382			},
383			Value::Uuid(v) => match other {
384				Value::Uuid(w) => v == w,
385				_ => false,
386			},
387			Value::RecordId(v) => match other {
388				Value::RecordId(w) => v == w,
389				_ => false,
390			},
391			Value::String(v) => match other {
392				Value::String(w) => v == w,
393				Value::Regex(w) => w.inner().is_match(v.as_str()),
394				_ => false,
395			},
396			Value::Regex(v) => match other {
397				Value::Regex(w) => v == w,
398				Value::String(w) => v.inner().is_match(w.as_str()),
399				_ => false,
400			},
401			Value::Array(v) => match other {
402				Value::Array(w) => v == w,
403				_ => false,
404			},
405			Value::Set(v) => match other {
406				Value::Set(w) => v == w,
407				_ => false,
408			},
409			Value::Object(v) => match other {
410				Value::Object(w) => v == w,
411				_ => false,
412			},
413			Value::Number(v) => match other {
414				Value::Number(w) => v == w,
415				_ => false,
416			},
417			Value::Geometry(v) => match other {
418				Value::Geometry(w) => v == w,
419				_ => false,
420			},
421			Value::Duration(v) => match other {
422				Value::Duration(w) => v == w,
423				_ => false,
424			},
425			Value::Datetime(v) => match other {
426				Value::Datetime(w) => v == w,
427				_ => false,
428			},
429			_ => self == other,
430		}
431	}
432
433	/// Check if all Values in an Array are equal to another Value
434	pub fn all_equal(&self, other: &Value) -> bool {
435		match self {
436			Value::Array(v) => v.iter().all(|v| v.equal(other)),
437			Value::Set(v) => v.iter().all(|v| v.equal(other)),
438			_ => self.equal(other),
439		}
440	}
441
442	/// Check if any Values in an Array are equal to another Value
443	pub fn any_equal(&self, other: &Value) -> bool {
444		match self {
445			Value::Array(v) => v.iter().any(|v| v.equal(other)),
446			Value::Set(v) => v.iter().any(|v| v.equal(other)),
447			_ => self.equal(other),
448		}
449	}
450
451	/// Check if this Value contains another Value
452	pub fn contains(&self, other: &Value) -> bool {
453		match self {
454			Value::Array(v) => v.iter().any(|v| v.equal(other)),
455			Value::Set(v) => v.contains(other),
456			Value::Uuid(v) => match other {
457				Value::String(w) => v.to_string().contains(w.as_str()),
458				_ => false,
459			},
460			Value::String(v) => match other {
461				Value::String(w) => v.contains(w.as_str()),
462				_ => false,
463			},
464			Value::Geometry(v) => match other {
465				Value::Geometry(w) => v.contains(w),
466				_ => false,
467			},
468			Value::Object(v) => match other {
469				Value::String(w) => v.contains_key(w.as_str()),
470				_ => false,
471			},
472			Value::Range(r) => {
473				let beg = match &r.start {
474					Bound::Unbounded => true,
475					Bound::Included(beg) => beg.le(other),
476					Bound::Excluded(beg) => beg.lt(other),
477				};
478
479				beg && match &r.end {
480					Bound::Unbounded => true,
481					Bound::Included(end) => end.ge(other),
482					Bound::Excluded(end) => end.gt(other),
483				}
484			}
485			_ => false,
486		}
487	}
488
489	/// Check if all Values in an Array, Set or String contain another Value
490	pub fn contains_all(&self, other: &Value) -> bool {
491		match other {
492			Value::Array(v) if v.iter().all(|v| v.is_strand()) && self.is_strand() => {
493				// confirmed as strand so all return false is unreachable
494				let Value::String(this) = self else {
495					return false;
496				};
497				v.iter().all(|s| {
498					let Value::String(other_string) = s else {
499						return false;
500					};
501					this.contains(&**other_string)
502				})
503			}
504			Value::Set(v) if v.iter().all(|v| v.is_strand()) && self.is_strand() => {
505				// confirmed as strand so all return false is unreachable
506				let Value::String(this) = self else {
507					return false;
508				};
509				v.iter().all(|s| {
510					let Value::String(other_string) = s else {
511						return false;
512					};
513					this.contains(&**other_string)
514				})
515			}
516			Value::Array(v) => v.iter().all(|v| match self {
517				Value::Array(w) => w.iter().any(|w| v.equal(w)),
518				Value::Set(w) => w.iter().any(|w| v.equal(w)),
519				Value::Geometry(_) => self.contains(v),
520				_ => false,
521			}),
522			Value::Set(v) => v.iter().all(|v| match self {
523				Value::Array(w) => w.iter().any(|w| v.equal(w)),
524				Value::Set(w) => w.iter().any(|w| v.equal(w)),
525				Value::Geometry(_) => self.contains(v),
526				_ => false,
527			}),
528			Value::String(other_strand) => match self {
529				Value::String(s) => s.contains(&**other_strand),
530				_ => false,
531			},
532			_ => false,
533		}
534	}
535
536	/// Check if any Values in an Array, Set or String contain another Value
537	pub fn contains_any(&self, other: &Value) -> bool {
538		match other {
539			Value::Array(v) if v.iter().all(|v| v.is_strand()) && self.is_strand() => {
540				// confirmed as strand so all return false is unreachable
541				let Value::String(this) = self else {
542					return false;
543				};
544				v.iter().any(|s| {
545					let Value::String(other_string) = s else {
546						return false;
547					};
548					this.contains(&**other_string)
549				})
550			}
551			Value::Set(v) if v.iter().all(|v| v.is_strand()) && self.is_strand() => {
552				// confirmed as strand so all return false is unreachable
553				let Value::String(this) = self else {
554					return false;
555				};
556				v.iter().any(|s| {
557					let Value::String(other_string) = s else {
558						return false;
559					};
560					this.contains(&**other_string)
561				})
562			}
563			Value::Array(v) => v.iter().any(|v| match self {
564				Value::Array(w) => w.iter().any(|w| v.equal(w)),
565				Value::Set(w) => w.iter().any(|w| v.equal(w)),
566				Value::Geometry(_) => self.contains(v),
567				_ => false,
568			}),
569			Value::Set(v) => v.iter().any(|v| match self {
570				Value::Array(w) => w.iter().any(|w| v.equal(w)),
571				Value::Set(w) => w.iter().any(|w| v.equal(w)),
572				Value::Geometry(_) => self.contains(v),
573				_ => false,
574			}),
575			Value::String(other_strand) => match self {
576				Value::String(s) => s.contains(&**other_strand),
577				_ => false,
578			},
579			_ => false,
580		}
581	}
582
583	/// Check if this Geometry intersects another Value
584	pub fn intersects(&self, other: &Value) -> bool {
585		match self {
586			Value::Geometry(v) => match other {
587				Value::Geometry(w) => v.intersects(w),
588				_ => false,
589			},
590			_ => false,
591		}
592	}
593
594	// -----------------------------------
595	// Sorting operations
596	// -----------------------------------
597
598	/// Compare this Value to another Value lexicographically
599	pub fn lexical_cmp(&self, other: &Value) -> Option<Ordering> {
600		match (self, other) {
601			(Value::String(a), Value::String(b)) => Some(lexicmp::lexical_cmp(a, b)),
602			_ => self.partial_cmp(other),
603		}
604	}
605
606	/// Compare this Value to another Value using natural numerical comparison
607	pub fn natural_cmp(&self, other: &Value) -> Option<Ordering> {
608		match (self, other) {
609			(Value::String(a), Value::String(b)) => Some(lexicmp::natural_cmp(a, b)),
610			_ => self.partial_cmp(other),
611		}
612	}
613
614	/// Compare this Value to another Value lexicographically and using natural
615	/// numerical comparison
616	pub fn natural_lexical_cmp(&self, other: &Value) -> Option<Ordering> {
617		match (self, other) {
618			(Value::String(a), Value::String(b)) => Some(lexicmp::natural_lexical_cmp(a, b)),
619			_ => self.partial_cmp(other),
620		}
621	}
622
623	/// Turns this value into a literal evaluating to the same value.
624	pub fn into_literal(self) -> expr::Expr {
625		match self {
626			Value::None => expr::Expr::Literal(expr::Literal::None),
627			Value::Null => expr::Expr::Literal(expr::Literal::Null),
628			Value::Bool(x) => expr::Expr::Literal(expr::Literal::Bool(x)),
629			Value::Number(Number::Int(i)) => expr::Expr::Literal(expr::Literal::Integer(i)),
630			Value::Number(Number::Float(f)) => expr::Expr::Literal(expr::Literal::Float(f)),
631			Value::Number(Number::Decimal(d)) => expr::Expr::Literal(expr::Literal::Decimal(d)),
632			Value::String(strand) => expr::Expr::Literal(expr::Literal::String(strand)),
633			Value::Duration(duration) => expr::Expr::Literal(expr::Literal::Duration(duration)),
634			Value::Datetime(datetime) => expr::Expr::Literal(expr::Literal::Datetime(datetime)),
635			Value::Uuid(uuid) => expr::Expr::Literal(expr::Literal::Uuid(uuid)),
636			Value::Array(array) => expr::Expr::Literal(expr::Literal::Array(array.into_literal())),
637			Value::Set(set) => {
638				// Convert set to array for literal representation since there's no set literal
639				// syntax
640				expr::Expr::Literal(expr::Literal::Array(set.into_literal()))
641			}
642			Value::Object(object) => {
643				expr::Expr::Literal(expr::Literal::Object(object.into_literal()))
644			}
645			Value::Geometry(geometry) => expr::Expr::Literal(expr::Literal::Geometry(geometry)),
646			Value::Bytes(bytes) => expr::Expr::Literal(expr::Literal::Bytes(bytes)),
647			Value::RecordId(record_id) => {
648				expr::Expr::Literal(expr::Literal::RecordId(record_id.into_literal()))
649			}
650			Value::Regex(regex) => expr::Expr::Literal(expr::Literal::Regex(regex)),
651			Value::File(file) => expr::Expr::Literal(expr::Literal::File(file)),
652			Value::Closure(closure) => closure.into_expr(),
653			Value::Range(range) => range.into_literal(),
654			Value::Table(t) => expr::Expr::Table(t),
655		}
656	}
657}
658
659impl ToSql for Value {
660	fn fmt_sql(&self, f: &mut String, sql_fmt: SqlFormat) {
661		match self {
662			Value::None => f.push_str("NONE"),
663			Value::Null => f.push_str("NULL"),
664			Value::Bool(v) => v.fmt_sql(f, sql_fmt),
665			Value::Number(v) => v.fmt_sql(f, sql_fmt),
666			Value::String(v) => write_sql!(f, sql_fmt, "{}", QuoteStr(v.as_str())),
667			Value::Duration(v) => v.fmt_sql(f, sql_fmt),
668			Value::Datetime(v) => v.fmt_sql(f, sql_fmt),
669			Value::Uuid(v) => v.fmt_sql(f, sql_fmt),
670			Value::Array(v) => v.fmt_sql(f, sql_fmt),
671			Value::Set(v) => v.fmt_sql(f, sql_fmt),
672			Value::Object(v) => v.fmt_sql(f, sql_fmt),
673			Value::Geometry(v) => v.fmt_sql(f, sql_fmt),
674			Value::Bytes(v) => v.fmt_sql(f, sql_fmt),
675			// Escaped explicitly rather than through the name's own `ToSql`: the
676			// engine's `TableName` deliberately has none, and it derefs to `str`,
677			// so a bare `fmt_sql` here would silently resolve to the unescaped
678			// string impl and emit a name that cannot be read back.
679			Value::Table(v) => EscapeIdent(v.as_str()).fmt_sql(f, sql_fmt),
680			Value::RecordId(v) => v.fmt_sql(f, sql_fmt),
681			Value::File(v) => v.fmt_sql(f, sql_fmt),
682			Value::Regex(v) => v.fmt_sql(f, sql_fmt),
683			Value::Range(v) => v.fmt_sql(f, sql_fmt),
684			Value::Closure(v) => v.fmt_sql(f, sql_fmt),
685		}
686	}
687}
688
689impl InfoStructure for Value {
690	fn structure(self) -> Value {
691		self.to_sql().into()
692	}
693}
694
695// ------------------------------
696
697pub trait TryAdd<Rhs = Self> {
698	type Output;
699	fn try_add(self, rhs: Rhs) -> Result<Self::Output>;
700}
701
702use std::ops::Add;
703
704impl TryAdd for Value {
705	type Output = Self;
706	fn try_add(self, other: Self) -> Result<Self> {
707		Ok(match (self, other) {
708			(Self::Number(v), Self::Number(w)) => Self::Number(v.try_add(w)?),
709			(Self::String(v), Self::String(w)) => {
710				let mut s = String::with_capacity(v.len() + w.len());
711				s.push_str(v.as_str());
712				s.push_str(w.as_str());
713				Value::String(s.into())
714			}
715			(Self::Datetime(v), Self::Duration(w)) => Self::Datetime(w.try_add(v)?),
716			(Self::Duration(v), Self::Datetime(w)) => Self::Datetime(v.try_add(w)?),
717			(Self::Duration(v), Self::Duration(w)) => Self::Duration(v.try_add(w)?),
718			(Self::Array(v), Self::Array(w)) => Self::Array(v.concat(w)),
719			(Self::Array(v), Self::Set(w)) => Self::Array(v.concat_set(w)),
720
721			(Self::Set(mut v), Self::Set(w)) => {
722				v.0.extend(w.0);
723				Self::Set(v)
724			}
725			(Self::Set(mut v), Self::Array(w)) => {
726				v.0.extend(w.0);
727				Self::Set(v)
728			}
729			(Self::Object(v), Self::Object(w)) => Self::Object(v.add(w)),
730			(v, w) => bail!(Error::TryAdd(v.kind_of().to_owned(), w.kind_of().to_owned())),
731		})
732	}
733}
734
735// ------------------------------
736
737pub trait TrySub<Rhs = Self> {
738	type Output;
739	fn try_sub(self, v: Rhs) -> Result<Self::Output>;
740}
741
742impl TrySub for Value {
743	type Output = Self;
744	fn try_sub(self, other: Self) -> Result<Self> {
745		Ok(match (self, other) {
746			(Self::Number(v), Self::Number(w)) => Self::Number(v.try_sub(w)?),
747			(Self::Datetime(v), Self::Datetime(w)) => Self::Duration(v.try_sub(w)?),
748			(Self::Datetime(v), Self::Duration(w)) => Self::Datetime(w.try_sub(v)?),
749			(Self::Duration(v), Self::Datetime(w)) => Self::Datetime(v.try_sub(w)?),
750			(Self::Duration(v), Self::Duration(w)) => Self::Duration(v.try_sub(w)?),
751			(Self::Array(v), Self::Array(x)) => Self::from(v.remove_all(&x.0)),
752			(Self::Array(v), Self::Set(x)) => Self::from(v.remove_all_set(&x)),
753			(Self::Set(mut v), Self::Array(x)) => {
754				for item in x.0 {
755					v.remove(&item);
756				}
757				Self::from(v)
758			}
759			(Self::Set(mut v), Self::Set(x)) => {
760				for item in x {
761					v.remove(&item);
762				}
763				Self::from(v)
764			}
765			(v, w) => bail!(Error::TrySub(v.kind_of().to_owned(), w.kind_of().to_owned())),
766		})
767	}
768}
769
770// ------------------------------
771
772pub trait TryMul<Rhs = Self> {
773	type Output;
774	fn try_mul(self, v: Self) -> Result<Self::Output>;
775}
776
777impl TryMul for Value {
778	type Output = Self;
779	fn try_mul(self, other: Self) -> Result<Self> {
780		match (self, other) {
781			(Self::Number(v), Self::Number(w)) => Ok(Self::Number(v.try_mul(w)?)),
782			(Self::Duration(d), Self::Number(Number::Int(i))) => {
783				if i < 0 {
784					bail!("Cannot multiply a duration with a negative number");
785				}
786
787				let factor: u32 = i.try_into()?;
788
789				let res =
790					d.0.checked_mul(factor)
791						.ok_or_else(|| Error::ArithmeticOverflow(format!("{d} * {i}")))?;
792
793				Ok(Value::Duration(Duration(res)))
794			}
795			(Self::Duration(d), Self::Number(Number::Float(f))) => {
796				if !(f.is_finite() && f >= 0.0) {
797					bail!("Cannot multiply a duration with a non-finite or negative number");
798				}
799
800				let secs = d.0.as_secs_f64() * f;
801				let res = StdDuration::try_from_secs_f64(secs)
802					.map_err(|_| Error::ArithmeticOverflow(format!("{d} * {f}")))?;
803
804				Ok(Value::Duration(Duration(res)))
805			}
806			(Self::Duration(d), Self::Number(Number::Decimal(dec))) => {
807				if !dec.is_sign_positive() && !dec.is_zero() {
808					bail!("Cannot multiply a duration with a negative number");
809				}
810
811				let nanos: Decimal = d.nanos().into();
812				let scaled = (nanos * dec).trunc();
813
814				let scaled_nanos: u128 = scaled
815					.to_u128()
816					.ok_or_else(|| Error::ArithmeticOverflow(format!("{d} * {dec}")))?;
817
818				let secs: u64 = (scaled_nanos / 1_000_000_000)
819					.try_into()
820					.map_err(|_| Error::ArithmeticOverflow(format!("{d} * {dec}")))?;
821
822				let subsec_nanos: u32 = (scaled_nanos % 1_000_000_000).try_into()?;
823
824				Ok(Value::Duration(Duration(StdDuration::new(secs, subsec_nanos))))
825			}
826			(v, w) => bail!(Error::TryMul(v.kind_of().to_owned(), w.kind_of().to_owned())),
827		}
828	}
829}
830
831// ------------------------------
832
833pub trait TryDiv<Rhs = Self> {
834	type Output;
835	fn try_div(self, v: Self) -> Result<Self::Output>;
836}
837
838impl TryDiv for Value {
839	type Output = Self;
840	fn try_div(self, other: Self) -> Result<Self> {
841		match (self, other) {
842			(Self::Number(v), Self::Number(w)) => Ok(Self::Number(v.try_div(w)?)),
843			(Self::Duration(d), Self::Number(Number::Int(i))) => {
844				if i <= 0 {
845					bail!("A duration can only be divided by a value greater than 0.");
846				}
847
848				let denom: u32 = i.try_into()?;
849				let res = d.0 / denom;
850				Ok(Value::Duration(Duration(res)))
851			}
852			(Self::Duration(d), Self::Number(Number::Float(f))) => {
853				if !(f.is_finite() && f > 0.0) {
854					bail!("A duration can only be divided by a finite value greater than 0.");
855				}
856				let secs = d.as_secs_f64() / f;
857
858				let res = StdDuration::try_from_secs_f64(secs)?;
859				Ok(Value::Duration(Duration(res)))
860			}
861			(Self::Duration(d), Self::Number(Number::Decimal(dec))) => {
862				if !dec.is_sign_positive() || dec.is_zero() {
863					bail!("A duration can only be divided by a value greater than 0.");
864				}
865
866				let nanos: Decimal = d.nanos().into();
867
868				let divided = (nanos / dec).trunc();
869
870				let divided_nanos: u128 = divided
871					.to_u128()
872					.ok_or_else(|| anyhow::anyhow!("Resulting duration is out of range"))?;
873
874				let secs: u64 = (divided_nanos / 1_000_000_000)
875					.try_into()
876					.map_err(|_| anyhow::anyhow!("Resulting duration seconds out of range"))?;
877
878				let subsec_nanos: u32 = (divided_nanos % 1_000_000_000).try_into()?;
879
880				Ok(Value::Duration(Duration(StdDuration::new(secs, subsec_nanos))))
881			}
882
883			(v, w) => bail!(Error::TryDiv(v.kind_of().to_owned(), w.kind_of().to_owned())),
884		}
885	}
886}
887
888// ------------------------------
889
890pub trait TryFloatDiv<Rhs = Self> {
891	type Output;
892	fn try_float_div(self, v: Self) -> Result<Self::Output>;
893}
894
895impl TryFloatDiv for Value {
896	type Output = Self;
897	fn try_float_div(self, other: Self) -> Result<Self::Output> {
898		Ok(match (self, other) {
899			(Self::Number(v), Self::Number(w)) => Self::Number(v.try_float_div(w)?),
900			(v, w) => bail!(Error::TryDiv(v.kind_of().to_owned(), w.kind_of().to_owned())),
901		})
902	}
903}
904
905// ------------------------------
906
907pub trait TryRem<Rhs = Self> {
908	type Output;
909	fn try_rem(self, v: Self) -> Result<Self::Output>;
910}
911
912impl TryRem for Value {
913	type Output = Self;
914	fn try_rem(self, other: Self) -> Result<Self> {
915		Ok(match (self, other) {
916			(Self::Number(v), Self::Number(w)) => Self::Number(v.try_rem(w)?),
917			(v, w) => bail!(Error::TryRem(v.kind_of().to_owned(), w.kind_of().to_owned())),
918		})
919	}
920}
921
922// ------------------------------
923
924pub trait TryPow<Rhs = Self> {
925	type Output;
926	fn try_pow(self, v: Self) -> Result<Self::Output>;
927}
928
929impl TryPow for Value {
930	type Output = Self;
931	fn try_pow(self, other: Self) -> Result<Self> {
932		Ok(match (self, other) {
933			(Value::Number(v), Value::Number(w)) => Self::Number(v.try_pow(w)?),
934			(v, w) => bail!(Error::TryPow(v.kind_of().to_owned(), w.kind_of().to_owned())),
935		})
936	}
937}
938
939// ------------------------------
940
941pub trait TryNeg<Rhs = Self> {
942	type Output;
943	fn try_neg(self) -> Result<Self::Output>;
944}
945
946impl TryNeg for Value {
947	type Output = Self;
948	fn try_neg(self) -> Result<Self> {
949		Ok(match self {
950			Self::Number(n) => Self::Number(n.try_neg()?),
951			v => bail!(Error::TryNeg(v.kind_of().to_owned())),
952		})
953	}
954}
955
956// Conversion methods.
957
958/// Macro implementing conversion methods for the variants of the value enum.
959macro_rules! subtypes {
960	($($name:ident$( ( $($t:tt)* ) )? => ($is:ident,$as:ident,$into:ident)),*$(,)?) => {
961		impl Value {
962			$(
963				subtypes!{@method $name $( ($($t)*) )? => $is,$as,$into}
964			)*
965		}
966
967			$(
968				subtypes!{@from $name $( ($($t)*) )? => $is,$as,$into}
969			)*
970
971	};
972
973	(@pat $name:ident($t:ty)) => {
974		Value::$name(_)
975	};
976
977	(@pat $name:ident) => {
978		Value::$name
979	};
980
981	(@method $name:ident($t:ty) => $is:ident,$as:ident,$into:ident) => {
982		#[doc = concat!("Check if the value is a [`",stringify!($name),"`]")]
983		#[allow(dead_code)]
984		pub fn $is(&self) -> bool{
985			matches!(self,Value::$name(_))
986		}
987
988		#[doc = concat!("Return a reference to [`",stringify!($name),"`] if the value is of that type")]
989		#[allow(dead_code)]
990		pub fn $as(&self) -> Option<&$t>{
991			if let Value::$name(x) = self{
992				Some(x)
993			}else{
994				None
995			}
996		}
997
998		#[doc = concat!("Turns the value into a [`",stringify!($name),"`] returning None if the value is not of that type")]
999		#[allow(dead_code)]
1000		pub fn $into(self) -> Option<$t>{
1001			if let Value::$name(x) = self{
1002				Some(x)
1003			}else{
1004				None
1005			}
1006		}
1007	};
1008
1009	(@method $name:ident => $is:ident,$as:ident,$into:ident) => {
1010		#[doc = concat!("Check if the value is a [`",stringify!($name),"`]")]
1011		#[allow(dead_code)]
1012		pub fn $is(&self) -> bool{
1013			matches!(self,Value::$name)
1014		}
1015	};
1016
1017
1018	(@from $name:ident(Box<$inner:ident>) => $is:ident,$as:ident,$into:ident) => {
1019		impl From<$inner> for Value {
1020			fn from(v: $inner) -> Self{
1021				Value::$name(Box::new(v))
1022			}
1023		}
1024
1025		impl From<Box<$inner>> for Value {
1026			fn from(v: Box<$inner>) -> Self{
1027				Value::$name(v)
1028			}
1029		}
1030	};
1031
1032	(@from $name:ident($t:ident) => $is:ident,$as:ident,$into:ident) => {
1033		impl From<$t> for Value {
1034			fn from(v: $t) -> Self{
1035				Value::$name(v)
1036			}
1037		}
1038	};
1039
1040	(@from $name:ident => $is:ident,$as:ident,$into:ident) => {
1041		// skip
1042	};
1043
1044}
1045
1046subtypes! {
1047	None => (is_none,_unused,_unused),
1048	Null => (is_null,_unused,_unused),
1049	Bool(bool) => (is_bool,as_bool,into_bool),
1050	Number(Number) => (is_number,as_number,into_number),
1051	String(Strand) => (is_strand,as_strand,into_strand),
1052	Table(TableName) => (is_table,as_table,into_table),
1053	Duration(Duration) => (is_duration,as_duration,into_duration),
1054	Datetime(Datetime) => (is_datetime,as_datetime,into_datetime),
1055	Uuid(Uuid) => (is_uuid,as_uuid,into_uuid),
1056	Array(Array) => (is_array,as_array,into_array),
1057	Set(Set) => (is_set,as_set,into_set),
1058	Object(Object) => (is_object,as_object,into_object),
1059	Geometry(Geometry) => (is_geometry,as_geometry,into_geometry),
1060	Bytes(Bytes) => (is_bytes,as_bytes,into_bytes),
1061	RecordId(RecordId) => (is_record,as_record,into_record),
1062	Regex(Regex) => (is_regex,as_regex,into_regex),
1063	Range(Box<Range>) => (is_range,as_range,into_range),
1064	Closure(Box<Closure>) => (is_closure,as_closure,into_closure),
1065	File(File) => (is_file,as_file,into_file),
1066}
1067
1068macro_rules! impl_from_number {
1069	($($n:ident),*$(,)?) => {
1070		$(
1071			impl From<$n> for Value{
1072				fn from(v: $n) -> Self{
1073					Value::Number(Number::from(v))
1074				}
1075			}
1076		)*
1077	};
1078}
1079impl_from_number!(i8, i16, i32, i64, u8, u16, u32, isize, f32, f64, Decimal);
1080
1081impl From<Vec<Value>> for Value {
1082	fn from(value: Vec<Value>) -> Self {
1083		Value::Array(Array(value))
1084	}
1085}
1086
1087impl From<Null> for Value {
1088	fn from(_v: Null) -> Self {
1089		Value::Null
1090	}
1091}
1092
1093// `u64`/`usize` always fit losslessly into a Number (as an Int, or a Decimal
1094// when they exceed i64), so these conversions are infallible.
1095impl From<u64> for Value {
1096	fn from(v: u64) -> Self {
1097		Value::Number(Number::from(v))
1098	}
1099}
1100
1101impl From<usize> for Value {
1102	fn from(v: usize) -> Self {
1103		Value::Number(Number::from(v))
1104	}
1105}
1106
1107// `i128`/`u128` can exceed Decimal's range, so these conversions are fallible
1108// and error rather than truncate when the value is too large to represent.
1109impl TryFrom<i128> for Value {
1110	type Error = Error;
1111	fn try_from(v: i128) -> Result<Self, Self::Error> {
1112		Ok(Value::Number(Number::try_from(v)?))
1113	}
1114}
1115
1116impl TryFrom<u128> for Value {
1117	type Error = Error;
1118	fn try_from(v: u128) -> Result<Self, Self::Error> {
1119		Ok(Value::Number(Number::try_from(v)?))
1120	}
1121}
1122
1123impl From<&str> for Value {
1124	fn from(v: &str) -> Self {
1125		Self::String(Strand::from(v))
1126	}
1127}
1128
1129impl From<String> for Value {
1130	fn from(v: String) -> Self {
1131		Self::String(Strand::from(v))
1132	}
1133}
1134
1135impl From<::uuid::Uuid> for Value {
1136	fn from(v: ::uuid::Uuid) -> Self {
1137		Value::Uuid(Uuid::from(v))
1138	}
1139}
1140
1141impl From<DateTime<Utc>> for Value {
1142	fn from(v: DateTime<Utc>) -> Self {
1143		Value::Datetime(Datetime::from(v))
1144	}
1145}
1146
1147impl From<Point<f64>> for Value {
1148	fn from(v: Point<f64>) -> Self {
1149		Value::Geometry(Geometry::from(v))
1150	}
1151}
1152
1153impl From<HashMap<&str, Value>> for Value {
1154	fn from(v: HashMap<&str, Value>) -> Self {
1155		Value::Object(Object::from(v))
1156	}
1157}
1158
1159impl From<HashMap<String, Value>> for Value {
1160	fn from(v: HashMap<String, Value>) -> Self {
1161		Value::Object(Object::from(v))
1162	}
1163}
1164
1165impl From<BTreeMap<String, Value>> for Value {
1166	fn from(v: BTreeMap<String, Value>) -> Self {
1167		Value::Object(Object::from(v))
1168	}
1169}
1170
1171impl From<VecMap<String, Value>> for Value {
1172	fn from(v: VecMap<String, Value>) -> Self {
1173		Value::Object(Object::from(v))
1174	}
1175}
1176
1177impl From<VecMap<Strand, Value>> for Value {
1178	fn from(v: VecMap<Strand, Value>) -> Self {
1179		Value::Object(Object(v))
1180	}
1181}
1182
1183impl From<VecMap<&str, Value>> for Value {
1184	fn from(v: VecMap<&str, Value>) -> Self {
1185		Value::Object(Object::from(v))
1186	}
1187}
1188
1189impl From<BTreeMap<&str, Value>> for Value {
1190	fn from(v: BTreeMap<&str, Value>) -> Self {
1191		Value::Object(Object::from(v))
1192	}
1193}
1194
1195impl TryFrom<Value> for crate::types::PublicValue {
1196	type Error = anyhow::Error;
1197
1198	fn try_from(s: Value) -> Result<Self, Self::Error> {
1199		convert_value_to_public_value(s)
1200	}
1201}
1202
1203impl From<crate::types::PublicValue> for Value {
1204	fn from(s: crate::types::PublicValue) -> Self {
1205		convert_public_value_to_internal(s)
1206	}
1207}
1208
1209impl FromIterator<Value> for Value {
1210	fn from_iter<I: IntoIterator<Item = Value>>(iter: I) -> Self {
1211		Value::Array(Array(iter.into_iter().collect()))
1212	}
1213}
1214
1215impl FromIterator<(String, Value)> for Value {
1216	fn from_iter<I: IntoIterator<Item = (String, Value)>>(iter: I) -> Self {
1217		Value::Object(Object::from_iter(iter))
1218	}
1219}
1220
1221/// Convert our internal value `crate::val::Value` to the public value `surrealdb_types::Value`.
1222///
1223/// In the future, as the two types diverge, this function will need access to the context in order
1224/// to convert certain values to the public value.
1225/// Maximum nesting depth a [`Value`] may carry.
1226///
1227/// Several code paths walk a value with native recursion — the public-value
1228/// conversion every query result passes through, the SQL formatter used by
1229/// export and `INFO`, and the derived `revision` storage codec. None can carry
1230/// its own cumulative depth counter across the others, and `surrealdb-core` is
1231/// built with `panic = "abort"` under the release profile, so a stack overflow
1232/// in any of them is an unconditional process abort rather than a failed
1233/// request.
1234///
1235/// The parser's own caps do not bound this: they are per-parse, and a value is
1236/// grown one level per request by a read-modify-write loop
1237/// (`UPDATE deep:1 SET v = [v]`) that stays far under them at every step. The
1238/// compute cap (`max_computation_depth`) bounds expression nesting, not the
1239/// runtime nesting of a value being cloned or serialised.
1240///
1241/// Sized well above any legitimate document and far below the depth at which
1242/// the recursive walks approach a thread stack.
1243pub const MAX_VALUE_DEPTH: usize = 256;
1244
1245/// A node in the iterative depth walk: a [`Value`] or a [`RecordIdKey`]. A
1246/// record id nests through its key, which is not itself a `Value`, so the
1247/// worklist has to carry either.
1248enum DepthNode<'a> {
1249	Value(&'a Value),
1250	Key(&'a crate::val::RecordIdKey),
1251}
1252
1253/// Rejects a [`Value`] nested deeper than [`MAX_VALUE_DEPTH`].
1254///
1255/// Deliberately iterative: a recursive checker would overflow the stack on
1256/// exactly the input it exists to reject. Traversal stops at the first node
1257/// past the cap, so a deeply-nested value is rejected without being walked in
1258/// full.
1259///
1260/// A record id's key is walked too. The derived storage codec, the SQL
1261/// formatter and the recursive drop all descend into a compound key
1262/// (`t:[[[...]]]`), so a key deep enough to overflow them must be rejected at
1263/// the same point a deep field value is.
1264pub fn check_value_depth(value: &Value) -> Result<()> {
1265	use crate::val::RecordIdKey;
1266	// (node, depth of that node)
1267	let mut stack: Vec<(DepthNode<'_>, usize)> = vec![(DepthNode::Value(value), 1)];
1268	while let Some((node, depth)) = stack.pop() {
1269		if depth > MAX_VALUE_DEPTH {
1270			bail!(Error::ValueTooDeep {
1271				max: MAX_VALUE_DEPTH,
1272			});
1273		}
1274		match node {
1275			DepthNode::Value(value) => match value {
1276				Value::Array(v) => {
1277					stack.extend(v.0.iter().map(|v| (DepthNode::Value(v), depth + 1)))
1278				}
1279				Value::Set(v) => stack.extend(v.0.iter().map(|v| (DepthNode::Value(v), depth + 1))),
1280				Value::Object(v) => {
1281					stack.extend(v.0.values().map(|v| (DepthNode::Value(v), depth + 1)))
1282				}
1283				Value::Range(v) => {
1284					for bound in [&v.start, &v.end] {
1285						match bound {
1286							Bound::Included(v) | Bound::Excluded(v) => {
1287								stack.push((DepthNode::Value(v), depth + 1))
1288							}
1289							Bound::Unbounded => {}
1290						}
1291					}
1292				}
1293				Value::RecordId(v) => stack.push((DepthNode::Key(&v.key), depth + 1)),
1294				// Every other variant is a leaf as far as `Value` nesting goes.
1295				// `Geometry` nests within itself rather than within `Value`, and
1296				// is bounded by the parser when it is read.
1297				_ => {}
1298			},
1299			DepthNode::Key(key) => match key {
1300				RecordIdKey::Array(a) => {
1301					stack.extend(a.0.iter().map(|v| (DepthNode::Value(v), depth + 1)))
1302				}
1303				RecordIdKey::Object(o) => {
1304					stack.extend(o.0.values().map(|v| (DepthNode::Value(v), depth + 1)))
1305				}
1306				RecordIdKey::Range(r) => {
1307					for bound in [&r.start, &r.end] {
1308						match bound {
1309							Bound::Included(k) | Bound::Excluded(k) => {
1310								stack.push((DepthNode::Key(k), depth + 1))
1311							}
1312							Bound::Unbounded => {}
1313						}
1314					}
1315				}
1316				// Number / String / Uuid are leaves.
1317				_ => {}
1318			},
1319		}
1320	}
1321	Ok(())
1322}
1323
1324pub fn convert_value_to_public_value(value: crate::val::Value) -> Result<surrealdb_types::Value> {
1325	convert_value_to_public_value_at(value, 1)
1326}
1327
1328/// [`convert_value_to_public_value`] carrying the depth of `value`.
1329///
1330/// The depth is threaded through the mutual recursion rather than checked by a
1331/// separate pass so the universal query-result path pays only a counter, and so
1332/// a value already in storage from before [`MAX_VALUE_DEPTH`] was enforced
1333/// surfaces an error here instead of aborting the process.
1334fn convert_value_to_public_value_at(
1335	value: crate::val::Value,
1336	depth: usize,
1337) -> Result<surrealdb_types::Value> {
1338	if depth > MAX_VALUE_DEPTH {
1339		bail!(Error::ValueTooDeep {
1340			max: MAX_VALUE_DEPTH,
1341		});
1342	}
1343	match value {
1344		crate::val::Value::None => Ok(surrealdb_types::Value::None),
1345		crate::val::Value::Null => Ok(surrealdb_types::Value::Null),
1346		crate::val::Value::Bool(value) => Ok(surrealdb_types::Value::Bool(value)),
1347		crate::val::Value::Number(value) => convert_number_to_public(value),
1348		crate::val::Value::String(value) => Ok(surrealdb_types::Value::String(value.into())),
1349		crate::val::Value::Datetime(value) => convert_datetime_to_public(value),
1350		crate::val::Value::Duration(value) => convert_duration_to_public(value),
1351		crate::val::Value::Uuid(value) => convert_uuid_to_public(value),
1352		crate::val::Value::Array(value) => convert_array_to_public(value, depth),
1353		crate::val::Value::Set(value) => convert_set_to_public(value, depth),
1354		crate::val::Value::Object(value) => convert_object_to_public(value, depth),
1355		crate::val::Value::Geometry(value) => convert_geometry_to_public(value),
1356		crate::val::Value::Bytes(value) => convert_bytes_to_public(value),
1357		crate::val::Value::RecordId(value) => convert_record_id_to_public(value, depth),
1358		crate::val::Value::File(value) => convert_file_to_public(value),
1359		crate::val::Value::Range(value) => convert_range_to_public(*value),
1360		crate::val::Value::Regex(value) => convert_regex_to_public(value),
1361		crate::val::Value::Table(value) => {
1362			Ok(surrealdb_types::Value::Table(value.into_public_table()))
1363		}
1364		crate::val::Value::Closure(_) => {
1365			Err(anyhow::anyhow!("Closure values cannot be converted to public value"))
1366		}
1367	}
1368}
1369
1370fn convert_number_to_public(value: crate::val::Number) -> Result<surrealdb_types::Value> {
1371	let number = match value {
1372		crate::val::Number::Int(i) => surrealdb_types::Number::Int(i),
1373		crate::val::Number::Float(f) => surrealdb_types::Number::Float(f),
1374		crate::val::Number::Decimal(d) => surrealdb_types::Number::Decimal(d),
1375	};
1376	Ok(surrealdb_types::Value::Number(number))
1377}
1378
1379fn convert_datetime_to_public(value: crate::val::Datetime) -> Result<surrealdb_types::Value> {
1380	Ok(surrealdb_types::Value::Datetime(surrealdb_types::Datetime::from(value.0)))
1381}
1382
1383fn convert_duration_to_public(value: crate::val::Duration) -> Result<surrealdb_types::Value> {
1384	Ok(surrealdb_types::Value::Duration(surrealdb_types::Duration::from_std(value.0)))
1385}
1386
1387fn convert_uuid_to_public(value: crate::val::Uuid) -> Result<surrealdb_types::Value> {
1388	Ok(surrealdb_types::Value::Uuid(surrealdb_types::Uuid::from(value.0)))
1389}
1390
1391fn convert_bytes_to_public(value: crate::val::Bytes) -> Result<surrealdb_types::Value> {
1392	Ok(surrealdb_types::Value::Bytes(surrealdb_types::Bytes::from(value.0)))
1393}
1394
1395fn convert_regex_to_public(value: crate::val::Regex) -> Result<surrealdb_types::Value> {
1396	Ok(surrealdb_types::Value::Regex(surrealdb_types::Regex::from(value.0)))
1397}
1398
1399fn convert_file_to_public(value: crate::val::File) -> Result<surrealdb_types::Value> {
1400	Ok(surrealdb_types::Value::File(surrealdb_types::File::new(value.bucket, value.key)))
1401}
1402
1403fn convert_geometry_to_public(value: crate::val::Geometry) -> Result<surrealdb_types::Value> {
1404	use surrealdb_types::Geometry as PublicGeometry;
1405	let geometry = match value {
1406		crate::val::Geometry::Point(p) => PublicGeometry::Point(p),
1407		crate::val::Geometry::Line(l) => PublicGeometry::Line(l),
1408		crate::val::Geometry::Polygon(p) => PublicGeometry::Polygon(p),
1409		crate::val::Geometry::MultiPoint(mp) => PublicGeometry::MultiPoint(mp),
1410		crate::val::Geometry::MultiLine(ml) => PublicGeometry::MultiLine(ml),
1411		crate::val::Geometry::MultiPolygon(mp) => PublicGeometry::MultiPolygon(mp),
1412		crate::val::Geometry::Collection(c) => {
1413			let converted: Result<Vec<_>> = c
1414				.into_iter()
1415				.map(|g| {
1416					if let surrealdb_types::Value::Geometry(g) = convert_geometry_to_public(g)? {
1417						Ok(g)
1418					} else {
1419						Err(anyhow::anyhow!("Failed to convert geometry collection item"))
1420					}
1421				})
1422				.collect();
1423			PublicGeometry::Collection(converted?)
1424		}
1425	};
1426	Ok(surrealdb_types::Value::Geometry(geometry))
1427}
1428
1429fn convert_array_to_public(
1430	value: crate::val::Array,
1431	depth: usize,
1432) -> Result<surrealdb_types::Value> {
1433	let converted: Result<Vec<_>> =
1434		value.0.into_iter().map(|v| convert_value_to_public_value_at(v, depth + 1)).collect();
1435	Ok(surrealdb_types::Value::Array(surrealdb_types::Array::from(converted?)))
1436}
1437
1438fn convert_set_to_public(value: crate::val::Set, depth: usize) -> Result<surrealdb_types::Value> {
1439	// Set is stored as a sorted Vec internally
1440	let converted: Result<Vec<surrealdb_types::Value>> =
1441		value.0.into_iter().map(|v| convert_value_to_public_value_at(v, depth + 1)).collect();
1442	Ok(surrealdb_types::Value::Set(surrealdb_types::Set::from(converted?)))
1443}
1444
1445fn convert_object_to_public(
1446	value: crate::val::Object,
1447	depth: usize,
1448) -> Result<surrealdb_types::Value> {
1449	let converted = convert_object_to_public_map_at(value, depth)?;
1450	Ok(surrealdb_types::Value::Object(surrealdb_types::Object::from(converted)))
1451}
1452
1453pub fn convert_object_to_public_map(
1454	value: crate::val::Object,
1455) -> Result<BTreeMap<String, surrealdb_types::Value>> {
1456	convert_object_to_public_map_at(value, 1)
1457}
1458
1459fn convert_object_to_public_map_at(
1460	value: crate::val::Object,
1461	depth: usize,
1462) -> Result<BTreeMap<String, surrealdb_types::Value>> {
1463	value
1464		.0
1465		.into_iter()
1466		.map(|(k, v)| convert_value_to_public_value_at(v, depth + 1).map(|v| (k.into_string(), v)))
1467		.collect()
1468}
1469
1470fn convert_record_id_to_public(
1471	value: crate::val::RecordId,
1472	depth: usize,
1473) -> Result<surrealdb_types::Value> {
1474	let key = convert_record_id_key_to_public(value.key, depth)?;
1475	Ok(surrealdb_types::Value::RecordId(surrealdb_types::RecordId {
1476		table: value.table.into_public_table(),
1477		key,
1478	}))
1479}
1480
1481fn convert_record_id_key_to_public(
1482	key: crate::val::RecordIdKey,
1483	depth: usize,
1484) -> Result<surrealdb_types::RecordIdKey> {
1485	if depth > MAX_VALUE_DEPTH {
1486		bail!(Error::ValueTooDeep {
1487			max: MAX_VALUE_DEPTH,
1488		});
1489	}
1490	match key {
1491		crate::val::RecordIdKey::Number(n) => Ok(surrealdb_types::RecordIdKey::Number(n)),
1492		crate::val::RecordIdKey::String(s) => Ok(surrealdb_types::RecordIdKey::String(s.into())),
1493		crate::val::RecordIdKey::Uuid(u) => {
1494			Ok(surrealdb_types::RecordIdKey::Uuid(surrealdb_types::Uuid::from(u.0)))
1495		}
1496		crate::val::RecordIdKey::Array(a) => {
1497			let converted_array = convert_array_to_public(a, depth)?;
1498			if let surrealdb_types::Value::Array(arr) = converted_array {
1499				Ok(surrealdb_types::RecordIdKey::Array(arr))
1500			} else {
1501				Err(anyhow::anyhow!("Failed to convert record id key array"))
1502			}
1503		}
1504		crate::val::RecordIdKey::Object(o) => {
1505			let converted_object = convert_object_to_public(o, depth)?;
1506			if let surrealdb_types::Value::Object(obj) = converted_object {
1507				Ok(surrealdb_types::RecordIdKey::Object(obj))
1508			} else {
1509				Err(anyhow::anyhow!("Failed to convert record id key object"))
1510			}
1511		}
1512		crate::val::RecordIdKey::Range(r) => {
1513			Ok(surrealdb_types::RecordIdKey::Range(Box::new(surrealdb_types::RecordIdKeyRange {
1514				start: match r.start {
1515					Bound::Included(k) => {
1516						Bound::Included(convert_record_id_key_to_public(k, depth + 1)?)
1517					}
1518					Bound::Excluded(k) => {
1519						Bound::Excluded(convert_record_id_key_to_public(k, depth + 1)?)
1520					}
1521					Bound::Unbounded => Bound::Unbounded,
1522				},
1523				end: match r.end {
1524					Bound::Included(k) => {
1525						Bound::Included(convert_record_id_key_to_public(k, depth + 1)?)
1526					}
1527					Bound::Excluded(k) => {
1528						Bound::Excluded(convert_record_id_key_to_public(k, depth + 1)?)
1529					}
1530					Bound::Unbounded => Bound::Unbounded,
1531				},
1532			})))
1533		}
1534	}
1535}
1536
1537fn convert_range_to_public(value: crate::val::Range) -> Result<surrealdb_types::Value> {
1538	Ok(surrealdb_types::Value::Range(Box::new(surrealdb_types::Range {
1539		start: match value.start {
1540			Bound::Included(v) => Bound::Included(convert_value_to_public_value(v)?),
1541			Bound::Excluded(v) => Bound::Excluded(convert_value_to_public_value(v)?),
1542			Bound::Unbounded => Bound::Unbounded,
1543		},
1544		end: match value.end {
1545			Bound::Included(v) => Bound::Included(convert_value_to_public_value(v)?),
1546			Bound::Excluded(v) => Bound::Excluded(convert_value_to_public_value(v)?),
1547			Bound::Unbounded => Bound::Unbounded,
1548		},
1549	})))
1550}
1551
1552#[cfg(test)]
1553mod tests {
1554	use super::{MAX_VALUE_DEPTH, check_value_depth, convert_value_to_public_value};
1555	use crate::val::{Array, Object, Value};
1556
1557	/// Builds a value nested `depth` levels deep: `depth` of 1 is a bare
1558	/// integer, 2 is `[1]`, 3 is `[[1]]`, and so on.
1559	fn nested_array(depth: usize) -> Value {
1560		let mut value = Value::from(1i64);
1561		for _ in 1..depth {
1562			value = Value::Array(Array::from(vec![value]));
1563		}
1564		value
1565	}
1566
1567	fn nested_object(depth: usize) -> Value {
1568		let mut value = Value::from(1i64);
1569		for _ in 1..depth {
1570			let mut obj = Object::default();
1571			obj.insert("v".to_owned(), value);
1572			value = Value::Object(obj);
1573		}
1574		value
1575	}
1576
1577	// The recursion sinks that walk a `Value` — the public-value conversion, the
1578	// SQL formatter, and the derived storage codec — carry no depth guard of
1579	// their own, and a stack overflow in any of them aborts the process under
1580	// the release profile's `panic = "abort"`. A value is grown one level per
1581	// request by a read-modify-write loop, so the parser's per-parse caps never
1582	// fire during accumulation; the depth of the value itself has to be bounded.
1583	#[test]
1584	fn value_depth_within_the_cap_is_accepted() {
1585		check_value_depth(&nested_array(MAX_VALUE_DEPTH))
1586			.expect("a value exactly at the cap must be accepted");
1587		check_value_depth(&nested_object(MAX_VALUE_DEPTH))
1588			.expect("objects nest the same way as arrays");
1589		check_value_depth(&Value::from(1i64)).expect("a scalar is depth 1");
1590	}
1591
1592	#[test]
1593	fn value_depth_past_the_cap_is_rejected() {
1594		let err = check_value_depth(&nested_array(MAX_VALUE_DEPTH + 1))
1595			.expect_err("a value one level past the cap must be rejected");
1596		assert!(err.to_string().contains("nested too deeply"), "unexpected error: {err}");
1597		assert!(check_value_depth(&nested_object(MAX_VALUE_DEPTH + 1)).is_err());
1598	}
1599
1600	// The check must survive the input it exists to reject: a recursive
1601	// implementation would overflow the stack rather than return an error. The
1602	// depth used is far past any thread stack's tolerance for a recursive walk,
1603	// so this test aborts rather than fails if the check regresses to recursion.
1604	//
1605	// The value is leaked deliberately. `Value`'s derived `Drop` is itself
1606	// recursive, so releasing a value this deep overflows the stack whatever
1607	// this check does — which is the reason the depth cap is enforced when a
1608	// record is written rather than only where the recursive walks are: a value
1609	// past the cap must never come to exist, because merely dropping one is
1610	// fatal.
1611	#[test]
1612	fn value_depth_check_does_not_recurse() {
1613		let deep = std::mem::ManuallyDrop::new(nested_array(200_000));
1614		assert!(
1615			check_value_depth(&deep).is_err(),
1616			"a very deeply nested value must be rejected, not overflow the stack"
1617		);
1618	}
1619
1620	// The conversion every query result passes through carries the depth
1621	// through its own mutual recursion, so a value already in storage from
1622	// before the cap was enforced surfaces an error rather than aborting.
1623	#[test]
1624	fn public_value_conversion_rejects_values_past_the_cap() {
1625		convert_value_to_public_value(nested_array(MAX_VALUE_DEPTH))
1626			.expect("a value at the cap must still convert");
1627		let err = convert_value_to_public_value(nested_array(MAX_VALUE_DEPTH + 1))
1628			.expect_err("a value past the cap must not convert");
1629		assert!(err.to_string().contains("nested too deeply"), "unexpected error: {err}");
1630	}
1631
1632	/// A record id nests through its key, which the storage codec and the
1633	/// recursive drop both descend into. Treating a record id as a leaf let a
1634	/// compound key (`t:[[[...]]]`) past the cap through; the key must be walked
1635	/// like any other nested value.
1636	#[test]
1637	fn record_id_key_depth_is_walked() {
1638		use crate::val::{RecordId, RecordIdKey};
1639
1640		// A record id whose key wraps an array nested `inner` levels.
1641		fn record_id_with_nested_key(inner: usize) -> Value {
1642			Value::RecordId(RecordId {
1643				table: "t".into(),
1644				key: RecordIdKey::Array(Array::from(vec![nested_array(inner)])),
1645			})
1646		}
1647
1648		check_value_depth(&record_id_with_nested_key(8))
1649			.expect("a shallow compound key must be accepted");
1650		let err = check_value_depth(&record_id_with_nested_key(MAX_VALUE_DEPTH + 8))
1651			.expect_err("a record id whose key nests past the cap must be rejected");
1652		assert!(err.to_string().contains("nested too deeply"), "unexpected error: {err}");
1653	}
1654
1655	/// A table name that collides with a keyword must render quoted, or the
1656	/// output cannot be parsed back. `TableName` derefs to `str`, so the
1657	/// rendering here cannot rely on method resolution finding an escaping
1658	/// impl — this pins the escaping itself rather than which impl provides it.
1659	/// `Number: Hash` disagrees with `Number: PartialEq`, so any value that can
1660	/// reach a number must report that a hash miss on it proves nothing.
1661	#[test]
1662	fn hash_agrees_with_eq_flags_numbers_at_any_depth() {
1663		use std::collections::BTreeMap;
1664
1665		use crate::val::{Number, Object, RecordId, RecordIdKey, Value};
1666
1667		let num = Value::Number(Number::Float(0.1));
1668		let nested =
1669			|v: Value| Value::Object(Object::from(BTreeMap::from([(Strand::from("v"), v)])));
1670
1671		for v in [
1672			Value::None,
1673			Value::Null,
1674			Value::Bool(true),
1675			Value::String("x".into()),
1676			Value::from(vec![Value::String("x".into())]),
1677			nested(Value::String("x".into())),
1678			Value::RecordId(RecordId {
1679				table: "t".into(),
1680				key: RecordIdKey::Number(1),
1681			}),
1682		] {
1683			assert!(v.hash_agrees_with_eq(), "{v:?}");
1684		}
1685
1686		for v in [
1687			num.clone(),
1688			Value::from(vec![num.clone()]),
1689			nested(num.clone()),
1690			nested(Value::from(vec![num.clone()])),
1691			Value::RecordId(RecordId {
1692				table: "t".into(),
1693				key: RecordIdKey::Array(vec![num].into()),
1694			}),
1695		] {
1696			assert!(!v.hash_agrees_with_eq(), "{v:?}");
1697		}
1698	}
1699
1700	#[test]
1701	fn table_value_renders_escaped() {
1702		use surrealdb_types::ToSql;
1703
1704		use crate::val::{TableName, Value};
1705
1706		assert_eq!(Value::Table(TableName::from("users")).to_sql(), "users");
1707		assert_eq!(Value::Table(TableName::from("table")).to_sql(), "`table`");
1708		assert_eq!(Value::Table(TableName::from("a b")).to_sql(), "`a b`");
1709	}
1710	use chrono::{TimeZone, Utc};
1711	use geo::{MultiLineString, MultiPoint, MultiPolygon, line_string, point, polygon};
1712	use rstest::rstest;
1713	use rust_decimal::Decimal;
1714	use serde_json::{Value as Json, json};
1715
1716	use super::*;
1717	use crate::syn;
1718	use crate::types::{
1719		PublicArray, PublicBytes, PublicDatetime, PublicDuration, PublicGeometry, PublicNumber,
1720		PublicObject, PublicRecordId, PublicRecordIdKey, PublicUuid, PublicValue,
1721	};
1722	use crate::val::Uuid;
1723
1724	macro_rules! parse_val {
1725		($input:expr) => {
1726			crate::val::convert_public_value_to_internal(syn::value($input).unwrap())
1727		};
1728	}
1729
1730	#[test]
1731	fn check_none() {
1732		assert!(Value::None.is_none());
1733		assert!(!Value::Null.is_none());
1734		assert!(!Value::from(1).is_none());
1735	}
1736
1737	#[test]
1738	fn check_null() {
1739		assert!(Value::Null.is_null());
1740		assert!(!Value::None.is_null());
1741		assert!(!Value::from(1).is_null());
1742	}
1743
1744	#[test]
1745	fn convert_truthy() {
1746		assert!(!Value::None.is_truthy());
1747		assert!(!Value::Null.is_truthy());
1748		assert!(Value::Bool(true).is_truthy());
1749		assert!(!Value::Bool(false).is_truthy());
1750		assert!(!Value::from(0).is_truthy());
1751		assert!(Value::from(1).is_truthy());
1752		assert!(Value::from(-1).is_truthy());
1753		assert!(Value::from(1.1).is_truthy());
1754		assert!(Value::from(-1.1).is_truthy());
1755		assert!(Value::from("true").is_truthy());
1756		assert!(Value::from("false").is_truthy());
1757		assert!(Value::from("falsey").is_truthy());
1758		assert!(Value::from("something").is_truthy());
1759		assert!(Value::from(Uuid::nil()).is_truthy());
1760		assert!(Value::from(Utc.with_ymd_and_hms(1948, 12, 3, 0, 0, 0).unwrap()).is_truthy());
1761	}
1762
1763	#[test]
1764	fn convert_string() {
1765		assert_eq!(String::from("NONE"), Value::None.into_raw_string());
1766		assert_eq!(String::from("NULL"), Value::Null.into_raw_string());
1767		assert_eq!(String::from("true"), Value::Bool(true).into_raw_string());
1768		assert_eq!(String::from("false"), Value::Bool(false).into_raw_string());
1769		assert_eq!(String::from("0"), Value::from(0).into_raw_string());
1770		assert_eq!(String::from("1"), Value::from(1).into_raw_string());
1771		assert_eq!(String::from("-1"), Value::from(-1).into_raw_string());
1772		assert_eq!(String::from("1.1f"), Value::from(1.1).into_raw_string());
1773		assert_eq!(String::from("-1.1f"), Value::from(-1.1).into_raw_string());
1774		assert_eq!(String::from("3"), Value::from("3").into_raw_string());
1775		assert_eq!(String::from("true"), Value::from("true").into_raw_string());
1776		assert_eq!(String::from("false"), Value::from("false").into_raw_string());
1777		assert_eq!(String::from("something"), Value::from("something").into_raw_string());
1778	}
1779
1780	#[test]
1781	fn check_size() {
1782		assert!(64 >= std::mem::size_of::<Value>(), "size of value too big");
1783	}
1784
1785	#[rstest]
1786	// Rev-2 Value envelope overhead: every varlen variant pays 1 tag byte
1787	// + 4 bytes `u32_le` payload length on top of the legacy `u16 rev` +
1788	// `u32 disc`. Inline variants (`None`, `Null`) are the entire encoding
1789	// in a single tag byte. `Bool` is `fixed(1)` -> tag + 1 byte.
1790	#[case::none(Value::None, 2)]
1791	#[case::null(Value::Null, 2)]
1792	#[case::bool(Value::Bool(true), 3)]
1793	#[case::bool(Value::Bool(false), 3)]
1794	// String wire shape under rev-2 varlen: rev (1) + tag (1) + u32_le
1795	// payload_length (4) + Strand wire (`usize len || utf8`) = 6 + Strand.
1796	// "test" is 4 utf8 bytes + 1 length varint = 5 bytes, so 11 total.
1797	#[case::string(Value::from("test"), 11)]
1798	// Objects under rev-2 Value + rev-2 Object `indexed_map`:
1799	//   Value envelope:  rev=2 (1) + tag (1) + u32_le payload_length (4) = 6
1800	//   Object envelope: rev=2 (1) + u32_le payload_length (4) = 5
1801	//   indexed_map body (sub-threshold): flag (1) + varint len + (K, V)*
1802	// Total delta vs rev-1 Value + rev-1 Object: +13 bytes per Object Value
1803	// (Value envelope +6, Object envelope +5, indexed_map flag +1, minus
1804	// 4 bytes the rev-1 framing was paying). The Wire-repr zero-copy
1805	// `walk_field_0` removed the need for a single-field offset table.
1806	#[case::object(Value::from(syn::value("{ hello: 'world' }").unwrap()), 31)]
1807	#[case::object(Value::from(syn::value("{ compact: true, schema: 0 }").unwrap()), 40)]
1808	fn check_serialize(#[case] value: Value, #[case] expected: usize) {
1809		let enc: Vec<u8> = revision::to_vec(&value).unwrap();
1810		assert_eq!(expected, enc.len());
1811	}
1812
1813	#[test]
1814	fn serialize_deserialize() {
1815		let val = parse_val!(
1816			"{ test: { something: [1, 'two', null, test:tobie, { trueee: false, noneee: null }] } }"
1817		);
1818		let res = parse_val!(
1819			"{ test: { something: [1, 'two', null, test:tobie, { trueee: false, noneee: null }] } }"
1820		);
1821		let enc: Vec<u8> = revision::to_vec(&val).unwrap();
1822		let dec: Value = revision::from_slice(&enc).unwrap();
1823		assert_eq!(res, dec);
1824	}
1825
1826	#[rstest]
1827	#[case::none(PublicValue::None, json!(null), PublicValue::Null)]
1828	#[case::null(PublicValue::Null, json!(null), PublicValue::Null)]
1829	#[case::bool(PublicValue::Bool(true), json!(true), PublicValue::Bool(true))]
1830	#[case::bool(PublicValue::Bool(false), json!(false), PublicValue::Bool(false))]
1831	#[case::number(
1832		PublicValue::Number(PublicNumber::Int(i64::MIN)),
1833		json!(i64::MIN),
1834		PublicValue::Number(PublicNumber::Int(i64::MIN)),
1835	)]
1836	#[case::number(
1837		PublicValue::Number(PublicNumber::Int(i64::MAX)),
1838		json!(i64::MAX),
1839		PublicValue::Number(PublicNumber::Int(i64::MAX)),
1840	)]
1841	#[case::number(
1842		PublicValue::Number(PublicNumber::Float(1.23)),
1843		json!(1.23),
1844		PublicValue::Number(PublicNumber::Float(1.23)),
1845	)]
1846	#[case::number(
1847		PublicValue::Number(PublicNumber::Float(f64::NEG_INFINITY)),
1848		json!(null),
1849		PublicValue::Null,
1850	)]
1851	#[case::number(
1852		PublicValue::Number(PublicNumber::Float(f64::MIN)),
1853		json!(-1.7976931348623157e308),
1854		PublicValue::Number(PublicNumber::Float(f64::MIN)),
1855	)]
1856	#[case::number(
1857		PublicValue::Number(PublicNumber::Float(0.0)),
1858		json!(0.0),
1859		PublicValue::Number(PublicNumber::Float(0.0)),
1860	)]
1861	#[case::number(
1862		PublicValue::Number(PublicNumber::Float(f64::MAX)),
1863		json!(1.7976931348623157e308),
1864		PublicValue::Number(PublicNumber::Float(f64::MAX)),
1865	)]
1866	#[case::number(
1867		PublicValue::Number(PublicNumber::Float(f64::INFINITY)),
1868		json!(null),
1869		PublicValue::Null,
1870	)]
1871	#[case::number(
1872		PublicValue::Number(PublicNumber::Float(f64::NAN)),
1873		json!(null),
1874		PublicValue::Null,
1875	)]
1876	#[case::number(
1877		PublicValue::Number(PublicNumber::Decimal(Decimal::new(123, 2))),
1878		json!("1.23"),
1879		PublicValue::String("1.23".to_string()),
1880	)]
1881	#[case::strand(
1882		PublicValue::String("".to_string()),
1883		json!(""),
1884		PublicValue::String("".to_string()),
1885	)]
1886	#[case::strand(
1887		PublicValue::String("foo".to_string()),
1888		json!("foo"),
1889		PublicValue::String("foo".to_string()),
1890	)]
1891	#[case::duration(
1892		PublicValue::Duration(PublicDuration::ZERO),
1893		json!("0ns"),
1894		PublicValue::String("0ns".to_string()),
1895	)]
1896	#[case::duration(
1897		PublicValue::Duration(PublicDuration::MAX),
1898		json!("584942417355y3w5d7h15s999ms999µs999ns"),
1899		PublicValue::String("584942417355y3w5d7h15s999ms999µs999ns".to_string()),
1900	)]
1901	#[case::datetime(
1902		PublicValue::Datetime(PublicDatetime::MIN_UTC),
1903		json!("-262143-01-01T00:00:00Z"),
1904		PublicValue::Datetime(PublicDatetime::MIN_UTC),
1905	)]
1906	#[case::datetime(
1907		PublicValue::Datetime(PublicDatetime::MAX_UTC),
1908		json!("+262142-12-31T23:59:59.999999999Z"),
1909		PublicValue::Datetime(PublicDatetime::MAX_UTC),
1910	)]
1911	#[case::uuid(
1912		PublicValue::Uuid(PublicUuid::nil()),
1913		json!("00000000-0000-0000-0000-000000000000"),
1914		PublicValue::Uuid(PublicUuid::nil()),
1915	)]
1916	#[case::uuid(
1917		PublicValue::Uuid(PublicUuid::max()),
1918		json!("ffffffff-ffff-ffff-ffff-ffffffffffff"),
1919		PublicValue::Uuid(PublicUuid::max()),
1920	)]
1921	#[case::bytes(
1922		PublicValue::Bytes(PublicBytes::default()),
1923		json!([]),
1924		PublicValue::Array(PublicArray::new()),
1925	)]
1926	#[case::bytes(
1927		PublicValue::Bytes(PublicBytes::from(b"foo".to_vec())),
1928		json!([102, 111, 111]),
1929		PublicValue::Array(PublicArray::from(vec![
1930			PublicValue::Number(PublicNumber::Int(102)),
1931			PublicValue::Number(PublicNumber::Int(111)),
1932			PublicValue::Number(PublicNumber::Int(111)),
1933		])),
1934	)]
1935	#[case::record_id(
1936		PublicValue::RecordId(PublicRecordId::new("foo", PublicRecordIdKey::String("bar".to_string()))) ,
1937		json!("foo:bar"),
1938		PublicValue::RecordId(PublicRecordId::new("foo", PublicRecordIdKey::String("bar".to_string()))) ,
1939	)]
1940	#[case::array(
1941		PublicValue::Array(PublicArray::new()),
1942		json!([]),
1943		PublicValue::Array(PublicArray::new()),
1944	)]
1945	#[case::array(
1946		PublicValue::Array(PublicArray::from(vec![PublicValue::Bool(true), PublicValue::Bool(false)])),
1947		json!([true, false]),
1948		PublicValue::Array(PublicArray::from(vec![PublicValue::Bool(true), PublicValue::Bool(false)])),
1949	)]
1950	#[case::object(
1951		PublicValue::Object(PublicObject::new()),
1952		json!({}),
1953		PublicValue::Object(PublicObject::new()),
1954	)]
1955	#[case::object(
1956		PublicValue::Object(PublicObject::from_iter([("done".to_owned(), PublicValue::Bool(true))])),
1957		json!({"done": true}),
1958		PublicValue::Object(PublicObject::from_iter([("done".to_owned(), PublicValue::Bool(true))])),
1959	)]
1960	#[case::geometry_point(
1961		PublicValue::Geometry(PublicGeometry::Point(point! { x: 10., y: 20. })),
1962		json!({ "type": "Point", "coordinates": [10., 20.]}),
1963		PublicValue::Geometry(PublicGeometry::Point(point! { x: 10., y: 20. })),
1964	)]
1965	#[case::geometry_line(
1966		PublicValue::Geometry(PublicGeometry::Line(line_string![
1967			( x: 0., y: 0. ),
1968			( x: 10., y: 0. ),
1969		])),
1970		json!({ "type": "LineString", "coordinates": [[0., 0.], [10., 0.]]}),
1971		PublicValue::Geometry(PublicGeometry::Line(line_string![
1972			( x: 0., y: 0. ),
1973			( x: 10., y: 0. ),
1974		])),
1975	)]
1976	#[case::geometry_polygon(
1977		PublicValue::Geometry(PublicGeometry::Polygon(polygon![
1978			(x: -111., y: 45.),
1979			(x: -111., y: 41.),
1980			(x: -104., y: 41.),
1981			(x: -104., y: 45.),
1982		])),
1983		json!({ "type": "Polygon", "coordinates": [[
1984			[-111., 45.],
1985			[-111., 41.],
1986			[-104., 41.],
1987			[-104., 45.],
1988			[-111., 45.],
1989		]]}),
1990		PublicValue::Geometry(PublicGeometry::Polygon(polygon![
1991			(x: -111., y: 45.),
1992			(x: -111., y: 41.),
1993			(x: -104., y: 41.),
1994			(x: -104., y: 45.),
1995		])),
1996	)]
1997	#[case::geometry_multi_point(
1998		PublicValue::Geometry(PublicGeometry::MultiPoint(MultiPoint::new(vec![
1999			point! { x: 0., y: 0. },
2000			point! { x: 1., y: 2. },
2001		]))),
2002		json!({ "type": "MultiPoint", "coordinates": [[0., 0.], [1., 2.]]}),
2003		PublicValue::Geometry(PublicGeometry::MultiPoint(MultiPoint::new(vec![
2004			point! { x: 0., y: 0. },
2005			point! { x: 1., y: 2. },
2006		]))),
2007	)]
2008	#[case::geometry_multi_line(
2009		PublicValue::Geometry(
2010			PublicGeometry::MultiLine(
2011				MultiLineString::new(vec![
2012					line_string![( x: 0., y: 0. ), ( x: 1., y: 2. )],
2013				])
2014			)
2015		),
2016		json!({ "type": "MultiLineString", "coordinates": [[[0., 0.], [1., 2.]]]}),
2017		PublicValue::Geometry(
2018			PublicGeometry::MultiLine(
2019				MultiLineString::new(vec![
2020					line_string![( x: 0., y: 0. ), ( x: 1., y: 2. )],
2021				])
2022			)
2023		),
2024	)]
2025	#[case::geometry_multi_polygon(
2026		PublicValue::Geometry(PublicGeometry::MultiPolygon(MultiPolygon::new(vec![
2027			polygon![
2028				(x: -111., y: 45.),
2029				(x: -111., y: 41.),
2030				(x: -104., y: 41.),
2031				(x: -104., y: 45.),
2032			],
2033		]))),
2034		json!({ "type": "MultiPolygon", "coordinates": [[[
2035			[-111., 45.],
2036			[-111., 41.],
2037			[-104., 41.],
2038			[-104., 45.],
2039			[-111., 45.],
2040		]]]})
2041	,	PublicValue::Geometry(PublicGeometry::MultiPolygon(MultiPolygon::new(vec![
2042			polygon![
2043				(x: -111., y: 45.),
2044				(x: -111., y: 41.),
2045				(x: -104., y: 41.),
2046				(x: -104., y: 45.),
2047			],
2048		]))),
2049	)]
2050	#[case::geometry_collection(
2051		PublicValue::Geometry(PublicGeometry::Collection(vec![])),
2052		json!({
2053			"type": "GeometryCollection",
2054			"geometries": [],
2055		}),
2056		PublicValue::Geometry(PublicGeometry::Collection(vec![])),
2057	)]
2058	#[case::geometry_collection_with_point(
2059		PublicValue::Geometry(PublicGeometry::Collection(vec![PublicGeometry::Point(point! { x: 10., y: 20. })])),
2060		json!({
2061		"type": "GeometryCollection",
2062		"geometries": [ { "type": "Point", "coordinates": [10., 20.] } ],
2063	}),
2064		PublicValue::Geometry(PublicGeometry::Collection(vec![PublicGeometry::Point(point! { x: 10., y: 20. })])),
2065	)]
2066	#[case::geometry_collection_with_line(
2067		PublicValue::Geometry(PublicGeometry::Collection(vec![PublicGeometry::Line(line_string![
2068			( x: 0., y: 0. ),
2069			( x: 10., y: 0. ),
2070		])])),
2071		json!({
2072			"type": "GeometryCollection",
2073			"geometries": [ { "type": "LineString", "coordinates": [[0., 0.], [10., 0.]] } ],
2074		}),
2075		PublicValue::Geometry(PublicGeometry::Collection(vec![PublicGeometry::Line(line_string![
2076			( x: 0., y: 0. ),
2077			( x: 10., y: 0. ),
2078		])])),
2079	)]
2080
2081	fn test_json(
2082		#[case] value: PublicValue,
2083		#[case] expected: Json,
2084		#[case] expected_deserialized: PublicValue,
2085	) {
2086		use surrealdb_syn::ParserConfig;
2087
2088		let json_value = value.into_json_value();
2089		assert_eq!(json_value, expected);
2090
2091		let json_str = serde_json::to_string(&json_value).expect("Failed to serialize to JSON");
2092		let deserialized_sql_value =
2093			crate::syn::value_legacy_strand(&json_str, &ParserConfig::default()).unwrap();
2094		assert_eq!(deserialized_sql_value, expected_deserialized);
2095	}
2096}