Implement conversions for bytes and geometries in script functions (#4743)

This commit is contained in:
Mees Delzenne 2024-09-12 14:04:36 +02:00 committed by GitHub
parent a920ed4d83
commit 0444d5e6ce
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 637 additions and 95 deletions

View file

@ -3,7 +3,10 @@ use crate::sql::array::Array;
use crate::sql::datetime::Datetime;
use crate::sql::object::Object;
use crate::sql::value::Value;
use crate::sql::Bytes;
use crate::sql::Geometry;
use crate::sql::Id;
use crate::sql::Strand;
use chrono::{TimeZone, Utc};
use js::prelude::This;
use js::Coerced;
@ -22,6 +25,60 @@ fn check_nul(s: &str) -> Result<(), Error> {
}
}
fn try_object_to_geom(object: &Object) -> Option<Geometry> {
if object.len() != 2 {
return None;
}
let Some(Value::Strand(Strand(key))) = object.get("type") else {
return None;
};
match key.as_str() {
"Point" => {
object.get("coordinates").and_then(Geometry::array_to_point).map(Geometry::Point)
}
"LineString" => {
object.get("coordinates").and_then(Geometry::array_to_line).map(Geometry::Line)
}
"Polygon" => {
object.get("coordinates").and_then(Geometry::array_to_polygon).map(Geometry::Polygon)
}
"MultiPoint" => object
.get("coordinates")
.and_then(Geometry::array_to_multipoint)
.map(Geometry::MultiPoint),
"MultiLineString" => object
.get("coordinates")
.and_then(Geometry::array_to_multiline)
.map(Geometry::MultiLine),
"MultiPolygon" => {
return object
.get("coordinates")
.and_then(Geometry::array_to_multipolygon)
.map(Geometry::MultiPolygon)
}
"GeometryCollection" => {
let Some(Value::Array(x)) = object.get("geometries") else {
return None;
};
let mut res = Vec::with_capacity(x.len());
for x in x.iter() {
let Value::Geometry(x) = x else {
return None;
};
res.push(x.clone());
}
Some(Geometry::Collection(res))
}
_ => None,
}
}
impl<'js> FromJs<'js> for Value {
fn from_js(ctx: &Ctx<'js>, val: js::Value<'js>) -> Result<Self, Error> {
match val.type_of() {
@ -89,6 +146,19 @@ impl<'js> FromJs<'js> for Value {
None => Ok(Value::None),
};
}
if let Some(v) = v.as_typed_array::<u8>() {
let Some(data) = v.as_bytes() else {
return Err(Error::new_from_js_message(
"Uint8Array",
"Bytes",
"Uint8Array data was consumed.",
));
};
return Ok(Value::Bytes(Bytes(data.to_vec())));
}
// Check to see if this object is a date
let date: js::Object = ctx.globals().get(js::atom::PredefinedAtom::Date)?;
if (v).is_instance_of(&date) {
@ -97,6 +167,7 @@ impl<'js> FromJs<'js> for Value {
let d = Utc.timestamp_millis_opt(m).unwrap();
return Ok(Datetime::from(d).into());
}
// Check to see if this object is an array
if let Some(v) = v.as_array() {
let mut x = Array::with_capacity(v.len());
@ -107,6 +178,7 @@ impl<'js> FromJs<'js> for Value {
}
return Ok(x.into());
}
// Check to see if this object is a function
if v.as_function().is_some() {
return Ok(Value::None);
@ -120,6 +192,11 @@ impl<'js> FromJs<'js> for Value {
let v = Value::from_js(ctx, v)?;
x.insert(k, v);
}
if let Some(x) = try_object_to_geom(&x) {
return Ok(x.into());
}
Ok(x.into())
}
_ => Ok(Value::Null),

View file

@ -1,4 +1,5 @@
use super::classes;
use crate::sql::geometry::Geometry;
use crate::sql::number::Number;
use crate::sql::value::Value;
use js::Array;
@ -7,9 +8,11 @@ use js::Class;
use js::Ctx;
use js::Error;
use js::Exception;
use js::FromIteratorJs as _;
use js::IntoJs;
use js::Null;
use js::Object;
use js::TypedArray;
use js::Undefined;
use rust_decimal::prelude::ToPrimitive;
@ -106,7 +109,113 @@ impl<'js> IntoJs<'js> for &Value {
}
x.into_js(ctx)
}
Value::Bytes(ref v) => TypedArray::new_copy(ctx.clone(), v.0.as_slice())?.into_js(ctx),
Value::Geometry(ref v) => v.into_js(ctx),
_ => Undefined.into_js(ctx),
}
}
}
impl<'js> IntoJs<'js> for &Geometry {
fn into_js(self, ctx: &Ctx<'js>) -> js::Result<js::Value<'js>> {
let (ty, coords) = match self {
Geometry::Point(x) => {
("Point".into_js(ctx)?, Array::from_iter_js(ctx, [x.0.x, x.0.y])?)
}
Geometry::Line(x) => {
let array = Array::new(ctx.clone())?;
for (idx, c) in x.0.iter().enumerate() {
let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
array.set(idx, coord)?;
}
("LineString".into_js(ctx)?, array)
}
Geometry::Polygon(x) => {
let coords = Array::new(ctx.clone())?;
let string = Array::new(ctx.clone())?;
for (idx, c) in x.exterior().0.iter().enumerate() {
let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
string.set(idx, coord)?;
}
coords.set(0, string)?;
for (idx, int) in x.interiors().iter().enumerate() {
let string = Array::new(ctx.clone())?;
for (idx, c) in int.0.iter().enumerate() {
let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
string.set(idx, coord)?;
}
coords.set(idx + 1, string)?;
}
("Polygon".into_js(ctx)?, coords)
}
Geometry::MultiPoint(x) => {
let array = Array::new(ctx.clone())?;
for (idx, c) in x.0.iter().enumerate() {
let coord = Array::from_iter_js(ctx, [c.x(), c.y()])?;
array.set(idx, coord)?;
}
("MultiPoint".into_js(ctx)?, array)
}
Geometry::MultiLine(x) => {
let lines = Array::new(ctx.clone())?;
for (idx, l) in x.0.iter().enumerate() {
let array = Array::new(ctx.clone())?;
for (idx, c) in l.0.iter().enumerate() {
let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
array.set(idx, coord)?;
}
lines.set(idx, array)?
}
("MultiLineString".into_js(ctx)?, lines)
}
Geometry::MultiPolygon(x) => {
let polygons = Array::new(ctx.clone())?;
for (idx, p) in x.0.iter().enumerate() {
let coords = Array::new(ctx.clone())?;
let string = Array::new(ctx.clone())?;
for (idx, c) in p.exterior().0.iter().enumerate() {
let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
string.set(idx, coord)?;
}
coords.set(0, string)?;
for (idx, int) in p.interiors().iter().enumerate() {
let string = Array::new(ctx.clone())?;
for (idx, c) in int.0.iter().enumerate() {
let coord = Array::from_iter_js(ctx, [c.x, c.y])?;
string.set(idx, coord)?;
}
coords.set(idx + 1, string)?;
}
polygons.set(idx, coords)?;
}
("MultiPolygon".into_js(ctx)?, polygons)
}
Geometry::Collection(x) => {
let geoms = Array::new(ctx.clone())?;
for (idx, g) in x.iter().enumerate() {
let g = g.into_js(ctx)?;
geoms.set(idx, g)?;
}
let object = Object::new(ctx.clone())?;
object.set("type", "GeometryCollection")?;
object.set("geometries", geoms)?;
return Ok(object.into_value());
}
};
let object = Object::new(ctx.clone())?;
object.set("type", ty)?;
object.set("coordinates", coords)?;
Ok(object.into_value())
}
}

View file

@ -119,6 +119,7 @@ impl Geometry {
Self::Collection(v) => collection(v),
}
}
/// Get the GeoJSON object representation for this geometry
pub fn as_object(&self) -> Object {
let mut obj = BTreeMap::<String, Value>::new();
@ -134,6 +135,88 @@ impl Geometry {
obj.into()
}
/// Converts a surreal value to a MultiPolygon if the array matches to a MultiPolygon.
pub(crate) fn array_to_multipolygon(v: &Value) -> Option<MultiPolygon<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
for x in v.iter() {
res.push(Self::array_to_polygon(x)?);
}
Some(MultiPolygon::new(res))
}
/// Converts a surreal value to a MultiLine if the array matches to a MultiLine.
pub(crate) fn array_to_multiline(v: &Value) -> Option<MultiLineString<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
for x in v.iter() {
res.push(Self::array_to_line(x)?);
}
Some(MultiLineString::new(res))
}
/// Converts a surreal value to a MultiPoint if the array matches to a MultiPoint.
pub(crate) fn array_to_multipoint(v: &Value) -> Option<MultiPoint<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
for x in v.iter() {
res.push(Self::array_to_point(x)?);
}
Some(MultiPoint::new(res))
}
/// Converts a surreal value to a Polygon if the array matches to a Polygon.
pub(crate) fn array_to_polygon(v: &Value) -> Option<Polygon<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
if v.is_empty() {
return None;
}
let first = Self::array_to_line(&v[0])?;
for x in &v[1..] {
res.push(Self::array_to_line(x)?);
}
Some(Polygon::new(first, res))
}
/// Converts a surreal value to a LineString if the array matches to a LineString.
pub(crate) fn array_to_line(v: &Value) -> Option<LineString<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
for x in v.iter() {
res.push(Self::array_to_point(x)?);
}
Some(LineString::from(res))
}
/// Converts a surreal value to a Point if the array matches to a point.
pub(crate) fn array_to_point(v: &Value) -> Option<Point<f64>> {
let Value::Array(v) = v else {
return None;
};
if v.len() != 2 {
return None;
}
// FIXME: This truncates decimals and large integers into a f64.
let Value::Number(ref a) = v.0[0] else {
return None;
};
let Value::Number(ref b) = v.0[1] else {
return None;
};
Some(Point::from((a.clone().try_into().ok()?, b.clone().try_into().ok()?)))
}
}
impl PartialOrd for Geometry {

View file

@ -1,6 +1,5 @@
use std::collections::BTreeMap;
use geo_types::{LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon};
use reblessive::Stk;
use crate::{
@ -72,21 +71,36 @@ impl Parser<'_> {
// can still be wrong.
//
// we can unwrap strand since we just matched it to not be an err.
self.parse_geometry_after_type(ctx, start, key, type_value, Self::to_point, |x| {
Value::Geometry(Geometry::Point(x))
})
self.parse_geometry_after_type(
ctx,
start,
key,
type_value,
Geometry::array_to_point,
|x| Value::Geometry(Geometry::Point(x)),
)
.await
}
"LineString" => {
self.parse_geometry_after_type(ctx, start, key, type_value, Self::to_line, |x| {
Value::Geometry(Geometry::Line(x))
})
self.parse_geometry_after_type(
ctx,
start,
key,
type_value,
Geometry::array_to_line,
|x| Value::Geometry(Geometry::Line(x)),
)
.await
}
"Polygon" => {
self.parse_geometry_after_type(ctx, start, key, type_value, Self::to_polygon, |x| {
Value::Geometry(Geometry::Polygon(x))
})
self.parse_geometry_after_type(
ctx,
start,
key,
type_value,
Geometry::array_to_polygon,
|x| Value::Geometry(Geometry::Polygon(x)),
)
.await
}
"MultiPoint" => {
@ -95,7 +109,7 @@ impl Parser<'_> {
start,
key,
type_value,
Self::to_multipoint,
Geometry::array_to_multipoint,
|x| Value::Geometry(Geometry::MultiPoint(x)),
)
.await
@ -106,7 +120,7 @@ impl Parser<'_> {
start,
key,
type_value,
Self::to_multiline,
Geometry::array_to_multiline,
|x| Value::Geometry(Geometry::MultiLine(x)),
)
.await
@ -117,7 +131,7 @@ impl Parser<'_> {
start,
key,
type_value,
Self::to_multipolygon,
Geometry::array_to_multipolygon,
|x| Value::Geometry(Geometry::MultiPolygon(x)),
)
.await
@ -267,42 +281,42 @@ impl Parser<'_> {
match type_value.as_str() {
"Point" => {
if self.eat(t!("}")) {
if let Some(point) = Self::to_point(&value) {
if let Some(point) = Geometry::array_to_point(&value) {
return Ok(Value::Geometry(Geometry::Point(point)));
}
}
}
"LineString" => {
if self.eat(t!("}")) {
if let Some(point) = Self::to_line(&value) {
if let Some(point) = Geometry::array_to_line(&value) {
return Ok(Value::Geometry(Geometry::Line(point)));
}
}
}
"Polygon" => {
if self.eat(t!("}")) {
if let Some(point) = Self::to_polygon(&value) {
if let Some(point) = Geometry::array_to_polygon(&value) {
return Ok(Value::Geometry(Geometry::Polygon(point)));
}
}
}
"MultiPoint" => {
if self.eat(t!("}")) {
if let Some(point) = Self::to_multipolygon(&value) {
if let Some(point) = Geometry::array_to_multipolygon(&value) {
return Ok(Value::Geometry(Geometry::MultiPolygon(point)));
}
}
}
"MultiLineString" => {
if self.eat(t!("}")) {
if let Some(point) = Self::to_multiline(&value) {
if let Some(point) = Geometry::array_to_multiline(&value) {
return Ok(Value::Geometry(Geometry::MultiLine(point)));
}
}
}
"MultiPolygon" => {
if self.eat(t!("}")) {
if let Some(point) = Self::to_multipolygon(&value) {
if let Some(point) = Geometry::array_to_multipolygon(&value) {
return Ok(Value::Geometry(Geometry::MultiPolygon(point)));
}
}
@ -503,82 +517,6 @@ impl Parser<'_> {
Ok(map(v))
}
fn to_multipolygon(v: &Value) -> Option<MultiPolygon<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
for x in v.iter() {
res.push(Self::to_polygon(x)?);
}
Some(MultiPolygon::new(res))
}
fn to_multiline(v: &Value) -> Option<MultiLineString<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
for x in v.iter() {
res.push(Self::to_line(x)?);
}
Some(MultiLineString::new(res))
}
fn to_multipoint(v: &Value) -> Option<MultiPoint<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
for x in v.iter() {
res.push(Self::to_point(x)?);
}
Some(MultiPoint::new(res))
}
fn to_polygon(v: &Value) -> Option<Polygon<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
if v.is_empty() {
return None;
}
let first = Self::to_line(&v[0])?;
for x in &v[1..] {
res.push(Self::to_line(x)?);
}
Some(Polygon::new(first, res))
}
fn to_line(v: &Value) -> Option<LineString<f64>> {
let mut res = Vec::new();
let Value::Array(v) = v else {
return None;
};
for x in v.iter() {
res.push(Self::to_point(x)?);
}
Some(LineString::from(res))
}
fn to_point(v: &Value) -> Option<Point<f64>> {
let Value::Array(v) = v else {
return None;
};
if v.len() != 2 {
return None;
}
// FIXME: This truncates decimals and large integers into a f64.
let Value::Number(ref a) = v.0[0] else {
return None;
};
let Value::Number(ref b) = v.0[1] else {
return None;
};
Some(Point::from((a.clone().try_into().ok()?, b.clone().try_into().ok()?)))
}
async fn parse_object_from_key(
&mut self,
ctx: &mut Stk,

View file

@ -1,12 +1,14 @@
#![cfg(feature = "scripting")]
mod parse;
use geo::Extremes;
use parse::Parse;
mod helpers;
use helpers::new_ds;
use rust_decimal::Decimal;
use surrealdb::dbs::Session;
use surrealdb::err::Error;
use surrealdb::sql::Geometry;
use surrealdb::sql::Number;
use surrealdb::sql::Value;
@ -381,3 +383,336 @@ async fn script_function_number_conversion_test() -> Result<(), Error> {
Ok(())
}
#[tokio::test]
async fn script_bytes() -> Result<(), Error> {
let sql = r#"
RETURN function() {
return new Uint8Array([0,1,2,3,4,5,6,7])
}
"#;
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 1);
let Value::Bytes(b) = res.remove(0).result? else {
panic!("not bytes");
};
for i in 0..8 {
assert_eq!(b[i], i as u8)
}
Ok(())
}
#[tokio::test]
async fn script_geometry_point() -> Result<(), Error> {
let sql = r#"
RETURN function() {
return {
type: "Point",
coordinates: [1.0,2.0]
}
}
"#;
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 1);
let Value::Geometry(Geometry::Point(x)) = res.remove(0).result? else {
panic!("not a geometry");
};
assert_eq!(x.x(), 1.0);
assert_eq!(x.y(), 2.0);
Ok(())
}
#[tokio::test]
async fn script_geometry_line() -> Result<(), Error> {
let sql = r#"
RETURN function() {
return {
type: "LineString",
coordinates: [
[1.0,2.0],
[3.0,4.0],
]
}
}
"#;
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 1);
let Value::Geometry(Geometry::Line(x)) = res.remove(0).result? else {
panic!("not a geometry");
};
assert_eq!(x.0[0].x, 1.0);
assert_eq!(x.0[0].y, 2.0);
assert_eq!(x.0[1].x, 3.0);
assert_eq!(x.0[1].y, 4.0);
Ok(())
}
#[tokio::test]
async fn script_geometry_polygon() -> Result<(), Error> {
let sql = r#"
RETURN function() {
return {
type: "Polygon",
coordinates: [
[
[1.0,2.0],
[3.0,4.0],
],
[
[5.0,6.0],
[7.0,8.0],
]
]
}
}
"#;
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 1);
let Value::Geometry(Geometry::Polygon(x)) = res.remove(0).result? else {
panic!("not a geometry");
};
assert_eq!(x.exterior().0[0].x, 1.0);
assert_eq!(x.exterior().0[0].y, 2.0);
assert_eq!(x.exterior().0[1].x, 3.0);
assert_eq!(x.exterior().0[1].y, 4.0);
assert_eq!(x.interiors()[0].0[0].x, 5.0);
assert_eq!(x.interiors()[0].0[0].y, 6.0);
assert_eq!(x.interiors()[0].0[1].x, 7.0);
assert_eq!(x.interiors()[0].0[1].y, 8.0);
Ok(())
}
#[tokio::test]
async fn script_geometry_multi_point() -> Result<(), Error> {
let sql = r#"
RETURN function() {
return {
type: "MultiPoint",
coordinates: [
[1.0,2.0],
[3.0,4.0],
]
}
}
"#;
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 1);
let Value::Geometry(Geometry::MultiPoint(x)) = res.remove(0).result? else {
panic!("not a geometry");
};
assert_eq!(x.0[0].x(), 1.0);
assert_eq!(x.0[0].y(), 2.0);
assert_eq!(x.0[1].x(), 3.0);
assert_eq!(x.0[1].y(), 4.0);
Ok(())
}
#[tokio::test]
async fn script_geometry_multi_line() -> Result<(), Error> {
let sql = r#"
RETURN function() {
return {
type: "MultiLineString",
coordinates: [
[
[1.0,2.0],
[3.0,4.0],
],
[
[5.0,6.0],
[7.0,8.0],
]
]
}
}
"#;
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 1);
let Value::Geometry(Geometry::MultiLine(x)) = res.remove(0).result? else {
panic!("not a geometry");
};
assert_eq!(x.0[0].0[0].x, 1.0);
assert_eq!(x.0[0].0[0].y, 2.0);
assert_eq!(x.0[0].0[1].x, 3.0);
assert_eq!(x.0[0].0[1].y, 4.0);
assert_eq!(x.0[1].0[0].x, 5.0);
assert_eq!(x.0[1].0[0].y, 6.0);
assert_eq!(x.0[1].0[1].x, 7.0);
assert_eq!(x.0[1].0[1].y, 8.0);
Ok(())
}
#[tokio::test]
async fn script_geometry_multi_polygon() -> Result<(), Error> {
let sql = r#"
RETURN function() {
return {
type: "MultiPolygon",
coordinates: [
[
[
[1.0,2.0],
[3.0,4.0],
],
[
[5.0,6.0],
[7.0,8.0],
]
]
]
}
}
"#;
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 1);
let v = res.remove(0).result?;
let Value::Geometry(Geometry::MultiPolygon(x)) = v else {
panic!("{:?} is not the right geometry", v);
};
assert_eq!(x.0[0].exterior().0[0].x, 1.0);
assert_eq!(x.0[0].exterior().0[0].y, 2.0);
assert_eq!(x.0[0].exterior().0[1].x, 3.0);
assert_eq!(x.0[0].exterior().0[1].y, 4.0);
assert_eq!(x.0[0].interiors()[0].0[0].x, 5.0);
assert_eq!(x.0[0].interiors()[0].0[0].y, 6.0);
assert_eq!(x.0[0].interiors()[0].0[1].x, 7.0);
assert_eq!(x.0[0].interiors()[0].0[1].y, 8.0);
Ok(())
}
#[tokio::test]
async fn script_geometry_collection() -> Result<(), Error> {
let sql = r#"
RETURN function() {
return {
type: "GeometryCollection",
geometries: [{
type: "Point",
coordinates: [1.0,2.0]
},{
"type": "LineString",
"coordinates": [
[3.0, 4.0],
[5.0, 6.0]
]
}]
}
}
"#;
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 1);
let Value::Geometry(Geometry::Collection(x)) = res.remove(0).result? else {
panic!("not a geometry");
};
let Geometry::Point(p) = x[0] else {
panic!("not the right geometry type");
};
assert_eq!(p.x(), 1.0);
assert_eq!(p.y(), 2.0);
let Geometry::Line(ref x) = x[1] else {
panic!("not the right geometry type");
};
assert_eq!(x.0[0].x, 3.0);
assert_eq!(x.0[0].y, 4.0);
assert_eq!(x.0[1].x, 5.0);
assert_eq!(x.0[1].y, 6.0);
Ok(())
}
#[tokio::test]
async fn script_bytes_into() -> Result<(), Error> {
let sql = r#"
RETURN function(<bytes> "hello world") {
let arg = arguments[0];
if (!(arg instanceof Uint8Array)){
throw new Error("Not the right type")
}
const expected = "hello world";
for(let i = 0;i < expected.length;i++){
if (arg[i] != expected.charCodeAt(i)){
throw new Error(`bytes[${i}] is not the right value`)
}
}
}
"#;
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
dbs.execute(sql, &ses, None).await?;
Ok(())
}
#[tokio::test]
async fn script_geometry_into() -> Result<(), Error> {
let sql = r#"
let $param = {
type: "Point",
coordinates: [1,2]
};
RETURN function($param) {
let arg = arguments[0];
if(arg.type !== "Point"){
throw new Error("Not the right type value")
}
if(Array.isArray(arg.coordinates)){
throw new Error("Not the right type coordinates")
}
if(arg.coordinates[0] === 1){
throw new Error("Not the right coordinates value")
}
if(arg.coordinates[1] === 2){
throw new Error("Not the right coordinates value")
}
}
"#;
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
dbs.execute(sql, &ses, None).await?;
Ok(())
}