Detach value newtype (#4498)
Co-authored-by: Tobie Morgan Hitchcock <tobie@surrealdb.com>
This commit is contained in:
parent
e25eca71d7
commit
4c3f4d0fd5
68 changed files with 2982 additions and 1455 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -6053,6 +6053,7 @@ dependencies = [
|
|||
"serial_test",
|
||||
"surrealdb",
|
||||
"surrealdb-async-graphql-axum",
|
||||
"surrealdb-core",
|
||||
"temp-env",
|
||||
"tempfile",
|
||||
"test-log",
|
||||
|
|
|
@ -27,8 +27,8 @@ ml = ["surrealdb/ml"]
|
|||
jwks = ["surrealdb/jwks"]
|
||||
performance-profiler = ["dep:pprof"]
|
||||
# Special features
|
||||
storage-fdb-7_1 = ["surrealdb/kv-fdb-7_1"]
|
||||
storage-fdb-7_3 = ["surrealdb/kv-fdb-7_3"]
|
||||
storage-fdb-7_1 = ["surrealdb-core/kv-fdb-7_1"]
|
||||
storage-fdb-7_3 = ["surrealdb-core/kv-fdb-7_3"]
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
|
@ -123,6 +123,7 @@ surrealdb = { version = "2", path = "lib", features = [
|
|||
"protocol-ws",
|
||||
"rustls",
|
||||
] }
|
||||
surrealdb-core = { version = "2", path = "core" }
|
||||
tempfile = "3.8.1"
|
||||
thiserror = "1.0.50"
|
||||
tokio = { version = "1.34.0", features = ["macros", "signal"] }
|
||||
|
|
|
@ -967,6 +967,7 @@ allow_unsafe = true
|
|||
allow_apis = ["fs"]
|
||||
|
||||
[pkg.surrealdb]
|
||||
allow_unsafe = true
|
||||
allow_apis = ["fs"]
|
||||
build.allow_build_instructions = ["cargo::rustc-check-cfg=*"]
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::err::Error;
|
||||
use crate::sql::value::Value;
|
||||
use crate::sql::Value as CoreValue;
|
||||
use revision::revisioned;
|
||||
use revision::Revisioned;
|
||||
use serde::ser::SerializeStruct;
|
||||
|
@ -25,7 +25,7 @@ pub enum QueryType {
|
|||
#[non_exhaustive]
|
||||
pub struct Response {
|
||||
pub time: Duration,
|
||||
pub result: Result<Value, Error>,
|
||||
pub result: Result<CoreValue, Error>,
|
||||
// Record the query type in case processing the response is necessary (such as tracking live queries).
|
||||
pub query_type: QueryType,
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ impl Response {
|
|||
}
|
||||
|
||||
/// Retrieve the response as a normal result
|
||||
pub fn output(self) -> Result<Value, Error> {
|
||||
pub fn output(self) -> Result<CoreValue, Error> {
|
||||
self.result
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ impl Serialize for Response {
|
|||
}
|
||||
Err(e) => {
|
||||
val.serialize_field("status", &Status::Err)?;
|
||||
val.serialize_field("result", &Value::from(e.to_string()))?;
|
||||
val.serialize_field("result", &CoreValue::from(e.to_string()))?;
|
||||
}
|
||||
}
|
||||
val.end()
|
||||
|
@ -80,7 +80,7 @@ impl Serialize for Response {
|
|||
pub struct QueryMethodResponse {
|
||||
pub time: String,
|
||||
pub status: Status,
|
||||
pub result: Value,
|
||||
pub result: CoreValue,
|
||||
}
|
||||
|
||||
impl From<&Response> for QueryMethodResponse {
|
||||
|
@ -88,7 +88,7 @@ impl From<&Response> for QueryMethodResponse {
|
|||
let time = res.speed();
|
||||
let (status, result) = match &res.result {
|
||||
Ok(value) => (Status::Ok, value.clone()),
|
||||
Err(error) => (Status::Err, Value::from(error.to_string())),
|
||||
Err(error) => (Status::Err, CoreValue::from(error.to_string())),
|
||||
};
|
||||
Self {
|
||||
status,
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::vs::Versionstamp;
|
|||
use std::fmt::Debug;
|
||||
use std::ops::Range;
|
||||
|
||||
#[allow(dead_code)] // not used when non of the storage backends are enabled.
|
||||
pub trait Transaction {
|
||||
/// Specify how we should handle unclosed transactions.
|
||||
///
|
||||
|
|
|
@ -21,9 +21,11 @@ pub enum SizedClock {
|
|||
}
|
||||
|
||||
impl SizedClock {
|
||||
#[allow(dead_code)] // not used when non of the storage backends are enabled.
|
||||
pub(crate) fn system() -> Self {
|
||||
Self::System(Default::default())
|
||||
}
|
||||
|
||||
pub async fn now(&self) -> Timestamp {
|
||||
match self {
|
||||
SizedClock::System(c) => c.now(),
|
||||
|
|
|
@ -5,6 +5,7 @@ pub type Key = Vec<u8>;
|
|||
pub type Val = Vec<u8>;
|
||||
|
||||
/// This trait appends an element to a collection, and allows chaining
|
||||
#[allow(dead_code)] // not used when non of the storage backends are enabled.
|
||||
pub(super) trait Add<T> {
|
||||
fn add(self, v: T) -> Self;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#[allow(unused_imports)] // not used when non of the storage backends are enabled.
|
||||
use super::api::Transaction;
|
||||
use super::Key;
|
||||
use super::Val;
|
||||
|
@ -125,12 +126,26 @@ macro_rules! expand_inner {
|
|||
#[cfg(feature = "kv-surrealkv")]
|
||||
Inner::SurrealKV($arm) => $b,
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Transactor {
|
||||
// Allow unused_variables when no storage is enabled as none of the values are used then.
|
||||
#![cfg_attr(
|
||||
not(any(
|
||||
feature = "kv-mem",
|
||||
feature = "kv-rocksdb",
|
||||
feature = "kv-indxdb",
|
||||
feature = "kv-tikv",
|
||||
feature = "kv-fdb",
|
||||
feature = "kv-surrealkv",
|
||||
)),
|
||||
allow(unused_variables)
|
||||
)]
|
||||
// --------------------------------------------------
|
||||
// Integral methods
|
||||
// --------------------------------------------------
|
||||
|
|
|
@ -47,7 +47,7 @@ impl RpcContext for BasicRpcContext<'_> {
|
|||
&mut self.vars
|
||||
}
|
||||
|
||||
fn version_data(&self) -> impl Into<super::Data> {
|
||||
Value::Strand(self.version_string.clone().into())
|
||||
fn version_data(&self) -> super::Data {
|
||||
Value::Strand(self.version_string.clone().into()).into()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ pub trait RpcContext {
|
|||
fn session_mut(&mut self) -> &mut Session;
|
||||
fn vars(&self) -> &BTreeMap<String, Value>;
|
||||
fn vars_mut(&mut self) -> &mut BTreeMap<String, Value>;
|
||||
fn version_data(&self) -> impl Into<Data>;
|
||||
fn version_data(&self) -> Data;
|
||||
|
||||
const LQ_SUPPORT: bool = false;
|
||||
fn handle_live(&self, _lqid: &Uuid) -> impl std::future::Future<Output = ()> + Send {
|
||||
|
@ -98,7 +98,7 @@ pub trait RpcContext {
|
|||
// Methods for authentication
|
||||
// ------------------------------
|
||||
|
||||
async fn yuse(&mut self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn yuse(&mut self, params: Array) -> Result<Data, RpcError> {
|
||||
// For both ns+db, string = change, null = unset, none = do nothing
|
||||
// We need to be able to adjust either ns or db without affecting the other
|
||||
// To be able to select a namespace, and then list resources in that namespace, as an example
|
||||
|
@ -123,10 +123,10 @@ pub trait RpcContext {
|
|||
self.session_mut().db = Some(db.0);
|
||||
}
|
||||
|
||||
Ok(Value::None)
|
||||
Ok(Value::None.into())
|
||||
}
|
||||
|
||||
async fn signup(&mut self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn signup(&mut self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok(Value::Object(v)) = params.needs_one() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -139,11 +139,10 @@ pub trait RpcContext {
|
|||
.map_err(Into::into);
|
||||
|
||||
*self.session_mut() = tmp_session;
|
||||
|
||||
out
|
||||
out.map(Into::into)
|
||||
}
|
||||
|
||||
async fn signin(&mut self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn signin(&mut self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok(Value::Object(v)) = params.needs_one() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -154,29 +153,29 @@ pub trait RpcContext {
|
|||
.map(Into::into)
|
||||
.map_err(Into::into);
|
||||
*self.session_mut() = tmp_session;
|
||||
out
|
||||
out.map(Into::into)
|
||||
}
|
||||
|
||||
async fn invalidate(&mut self) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn invalidate(&mut self) -> Result<Data, RpcError> {
|
||||
crate::iam::clear::clear(self.session_mut())?;
|
||||
Ok(Value::None)
|
||||
Ok(Value::None.into())
|
||||
}
|
||||
|
||||
async fn authenticate(&mut self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn authenticate(&mut self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok(Value::Strand(token)) = params.needs_one() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
let mut tmp_session = mem::take(self.session_mut());
|
||||
crate::iam::verify::token(self.kvs(), &mut tmp_session, &token.0).await?;
|
||||
*self.session_mut() = tmp_session;
|
||||
Ok(Value::None)
|
||||
Ok(Value::None.into())
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for identification
|
||||
// ------------------------------
|
||||
|
||||
async fn info(&self) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn info(&self) -> Result<Data, RpcError> {
|
||||
// Specify the SQL query string
|
||||
let sql = "SELECT * FROM $auth";
|
||||
// Execute the query on the database
|
||||
|
@ -184,14 +183,14 @@ pub trait RpcContext {
|
|||
// Extract the first value from the result
|
||||
let res = res.remove(0).result?.first();
|
||||
// Return the result to the client
|
||||
Ok(res)
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for setting variables
|
||||
// ------------------------------
|
||||
|
||||
async fn set(&mut self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn set(&mut self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok((Value::Strand(key), val)) = params.needs_one_or_two() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -207,22 +206,22 @@ pub trait RpcContext {
|
|||
// Store the variable if defined
|
||||
v => self.vars_mut().insert(key.0, v),
|
||||
};
|
||||
Ok(Value::Null)
|
||||
Ok(Value::Null.into())
|
||||
}
|
||||
|
||||
async fn unset(&mut self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn unset(&mut self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok(Value::Strand(key)) = params.needs_one() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
self.vars_mut().remove(&key.0);
|
||||
Ok(Value::Null)
|
||||
Ok(Value::Null.into())
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for live queries
|
||||
// ------------------------------
|
||||
|
||||
async fn kill(&mut self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn kill(&mut self, params: Array) -> Result<Data, RpcError> {
|
||||
let id = params.needs_one()?;
|
||||
// Specify the SQL query string
|
||||
let sql = "KILL $id";
|
||||
|
@ -236,10 +235,10 @@ pub trait RpcContext {
|
|||
let mut res = self.query_inner(Value::from(sql), Some(var)).await?;
|
||||
// Extract the first query result
|
||||
let response = res.remove(0);
|
||||
response.result.map_err(Into::into)
|
||||
response.result.map_err(Into::into).map(Into::into)
|
||||
}
|
||||
|
||||
async fn live(&mut self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn live(&mut self, params: Array) -> Result<Data, RpcError> {
|
||||
let (tb, diff) = params.needs_one_or_two()?;
|
||||
// Specify the SQL query string
|
||||
let sql = match diff.is_true() {
|
||||
|
@ -255,14 +254,14 @@ pub trait RpcContext {
|
|||
let mut res = self.query_inner(Value::from(sql), Some(var)).await?;
|
||||
// Extract the first query result
|
||||
let response = res.remove(0);
|
||||
response.result.map_err(Into::into)
|
||||
response.result.map_err(Into::into).map(Into::into)
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for selecting
|
||||
// ------------------------------
|
||||
|
||||
async fn select(&self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn select(&self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok(what) = params.needs_one() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -283,14 +282,14 @@ pub trait RpcContext {
|
|||
false => res.remove(0).result?,
|
||||
};
|
||||
// Return the result to the client
|
||||
Ok(res)
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for inserting
|
||||
// ------------------------------
|
||||
|
||||
async fn insert(&self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn insert(&self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok((what, data)) = params.needs_two() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -312,14 +311,14 @@ pub trait RpcContext {
|
|||
false => res.remove(0).result?,
|
||||
};
|
||||
// Return the result to the client
|
||||
Ok(res)
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for creating
|
||||
// ------------------------------
|
||||
|
||||
async fn create(&self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn create(&self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok((what, data)) = params.needs_one_or_two() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -345,14 +344,14 @@ pub trait RpcContext {
|
|||
false => res.remove(0).result?,
|
||||
};
|
||||
// Return the result to the client
|
||||
Ok(res)
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for upserting
|
||||
// ------------------------------
|
||||
|
||||
async fn upsert(&self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn upsert(&self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok((what, data)) = params.needs_one_or_two() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -378,14 +377,14 @@ pub trait RpcContext {
|
|||
false => res.remove(0).result?,
|
||||
};
|
||||
// Return the result to the client
|
||||
Ok(res)
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for updating
|
||||
// ------------------------------
|
||||
|
||||
async fn update(&self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn update(&self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok((what, data)) = params.needs_one_or_two() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -411,14 +410,14 @@ pub trait RpcContext {
|
|||
false => res.remove(0).result?,
|
||||
};
|
||||
// Return the result to the client
|
||||
Ok(res)
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for merging
|
||||
// ------------------------------
|
||||
|
||||
async fn merge(&self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn merge(&self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok((what, data)) = params.needs_one_or_two() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -444,14 +443,14 @@ pub trait RpcContext {
|
|||
false => res.remove(0).result?,
|
||||
};
|
||||
// Return the result to the client
|
||||
Ok(res)
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for patching
|
||||
// ------------------------------
|
||||
|
||||
async fn patch(&self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn patch(&self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok((what, data, diff)) = params.needs_one_two_or_three() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -476,14 +475,14 @@ pub trait RpcContext {
|
|||
false => res.remove(0).result?,
|
||||
};
|
||||
// Return the result to the client
|
||||
Ok(res)
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for relating
|
||||
// ------------------------------
|
||||
|
||||
async fn relate(&self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn relate(&self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok((from, kind, to, data)) = params.needs_three_or_four() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -511,14 +510,14 @@ pub trait RpcContext {
|
|||
false => res.remove(0).result?,
|
||||
};
|
||||
// Return the result to the client
|
||||
Ok(res)
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for deleting
|
||||
// ------------------------------
|
||||
|
||||
async fn delete(&self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn delete(&self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok(what) = params.needs_one() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -539,14 +538,14 @@ pub trait RpcContext {
|
|||
false => res.remove(0).result?,
|
||||
};
|
||||
// Return the result to the client
|
||||
Ok(res)
|
||||
Ok(res.into())
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for getting info
|
||||
// ------------------------------
|
||||
|
||||
async fn version(&self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn version(&self, params: Array) -> Result<Data, RpcError> {
|
||||
match params.len() {
|
||||
0 => Ok(self.version_data()),
|
||||
_ => Err(RpcError::InvalidParams),
|
||||
|
@ -557,7 +556,7 @@ pub trait RpcContext {
|
|||
// Methods for querying
|
||||
// ------------------------------
|
||||
|
||||
async fn query(&self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn query(&self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok((query, o)) = params.needs_one_or_two() else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
};
|
||||
|
@ -576,14 +575,14 @@ pub trait RpcContext {
|
|||
Some(mut v) => Some(mrg! {v.0, &self.vars()}),
|
||||
None => Some(self.vars().clone()),
|
||||
};
|
||||
self.query_inner(query, vars).await
|
||||
self.query_inner(query, vars).await.map(Into::into)
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// Methods for running functions
|
||||
// ------------------------------
|
||||
|
||||
async fn run(&self, params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
async fn run(&self, params: Array) -> Result<Data, RpcError> {
|
||||
let Ok((Value::Strand(Strand(func_name)), version, args)) = params.needs_one_two_or_three()
|
||||
else {
|
||||
return Err(RpcError::InvalidParams);
|
||||
|
@ -616,7 +615,7 @@ pub trait RpcContext {
|
|||
.kvs()
|
||||
.process(Statement::Value(func).into(), self.session(), Some(self.vars().clone()))
|
||||
.await?;
|
||||
res.remove(0).result.map_err(Into::into)
|
||||
res.remove(0).result.map_err(Into::into).map(Into::into)
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
|
|
|
@ -18,6 +18,17 @@ pub struct Edges {
|
|||
pub what: Tables,
|
||||
}
|
||||
|
||||
impl Edges {
|
||||
#[doc(hidden)]
|
||||
pub fn new(dir: Dir, from: Thing, what: Tables) -> Self {
|
||||
Edges {
|
||||
dir,
|
||||
from,
|
||||
what,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Edges {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.what.len() {
|
||||
|
|
|
@ -14,7 +14,6 @@ use std::{cmp::Ordering, fmt, ops::Bound};
|
|||
#[revisioned(revision = 1)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[non_exhaustive]
|
||||
pub struct IdRange {
|
||||
pub beg: Bound<Id>,
|
||||
pub end: Bound<Id>,
|
||||
|
|
|
@ -9,7 +9,6 @@ use derive::Store;
|
|||
use revision::revisioned;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[revisioned(revision = 3)]
|
||||
|
@ -312,17 +311,18 @@ impl InfoStatement {
|
|||
opt.is_allowed(Action::View, ResourceKind::Actor, &Base::Db)?;
|
||||
// Get the transaction
|
||||
let txn = ctx.tx();
|
||||
// Obtain the index
|
||||
let res = txn.get_tb_index(opt.ns()?, opt.db()?, table, index).await?;
|
||||
// Output
|
||||
let mut out = Object::default();
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if let Some(ib) = ctx.get_index_builder() {
|
||||
if let Some(status) = ib.get_status(res.deref()).await {
|
||||
// Obtain the index
|
||||
let res = txn.get_tb_index(opt.ns()?, opt.db()?, table, index).await?;
|
||||
if let Some(status) = ib.get_status(&res).await {
|
||||
let mut out = Object::default();
|
||||
out.insert("building".to_string(), status.into());
|
||||
return Ok(out.into());
|
||||
}
|
||||
}
|
||||
Ok(out.into())
|
||||
Ok(Object::default().into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
205
core/src/sql/value/serde/ser/part/mod.rs
Normal file
205
core/src/sql/value/serde/ser/part/mod.rs
Normal file
|
@ -0,0 +1,205 @@
|
|||
pub(super) mod vec;
|
||||
|
||||
use crate::err::Error;
|
||||
use crate::sql::value::serde::ser;
|
||||
use crate::sql::Ident;
|
||||
use crate::sql::Part;
|
||||
use crate::sql::Value;
|
||||
use ser::Serializer as _;
|
||||
use serde::ser::Error as _;
|
||||
use serde::ser::Impossible;
|
||||
use serde::ser::Serialize;
|
||||
|
||||
pub(super) struct Serializer;
|
||||
|
||||
impl ser::Serializer for Serializer {
|
||||
type Ok = Part;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<Part, Error>;
|
||||
type SerializeTuple = Impossible<Part, Error>;
|
||||
type SerializeTupleStruct = Impossible<Part, Error>;
|
||||
type SerializeTupleVariant = SerializePart;
|
||||
type SerializeMap = Impossible<Part, Error>;
|
||||
type SerializeStruct = Impossible<Part, Error>;
|
||||
type SerializeStructVariant = Impossible<Part, Error>;
|
||||
|
||||
const EXPECTED: &'static str = "an enum `Part`";
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
) -> Result<Self::Ok, Error> {
|
||||
match variant {
|
||||
"All" => Ok(Part::All),
|
||||
"Last" => Ok(Part::Last),
|
||||
"First" => Ok(Part::First),
|
||||
variant => Err(Error::custom(format!("unexpected unit variant `{name}::{variant}`"))),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_newtype_variant<T>(
|
||||
self,
|
||||
name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok, Error>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
match variant {
|
||||
"Field" => Ok(Part::Field(Ident(value.serialize(ser::string::Serializer.wrap())?))),
|
||||
"Index" => Ok(Part::Index(value.serialize(ser::number::Serializer.wrap())?)),
|
||||
"Where" => Ok(Part::Where(value.serialize(ser::value::Serializer.wrap())?)),
|
||||
"Graph" => Ok(Part::Graph(value.serialize(ser::graph::Serializer.wrap())?)),
|
||||
"Start" => Ok(Part::Start(value.serialize(ser::value::Serializer.wrap())?)),
|
||||
"Value" => Ok(Part::Value(value.serialize(ser::value::Serializer.wrap())?)),
|
||||
variant => {
|
||||
Err(Error::custom(format!("unexpected newtype variant `{name}::{variant}`")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
||||
let inner = match variant {
|
||||
"Method" => Inner::Method(Default::default(), Default::default()),
|
||||
variant => {
|
||||
return Err(Error::custom(format!("unexpected tuple variant `{name}::{variant}`")));
|
||||
}
|
||||
};
|
||||
Ok(SerializePart {
|
||||
inner,
|
||||
index: 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct SerializePart {
|
||||
index: usize,
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
enum Inner {
|
||||
Method(String, Vec<Value>),
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeTupleVariant for SerializePart {
|
||||
type Ok = Part;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Self::Error>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
match (self.index, &mut self.inner) {
|
||||
(0, Inner::Method(ref mut var, _)) => {
|
||||
*var = value.serialize(ser::string::Serializer.wrap())?;
|
||||
}
|
||||
(1, Inner::Method(_, ref mut var)) => {
|
||||
*var = value.serialize(ser::value::vec::Serializer.wrap())?;
|
||||
}
|
||||
(index, inner) => {
|
||||
let variant = match inner {
|
||||
Inner::Method(..) => "Method",
|
||||
};
|
||||
return Err(Error::custom(format!("unexpected `Part::{variant}` index `{index}`")));
|
||||
}
|
||||
}
|
||||
self.index += 1;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Self::Ok, Self::Error> {
|
||||
match self.inner {
|
||||
Inner::Method(one, two) => Ok(Part::Method(one, two)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::sql;
|
||||
use serde::Serialize;
|
||||
|
||||
#[test]
|
||||
fn all() {
|
||||
let part = Part::All;
|
||||
let serialized = part.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn last() {
|
||||
let part = Part::Last;
|
||||
let serialized = part.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first() {
|
||||
let part = Part::First;
|
||||
let serialized = part.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn field() {
|
||||
let part = Part::Field(Default::default());
|
||||
let serialized = part.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn index() {
|
||||
let part = Part::Index(Default::default());
|
||||
let serialized = part.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn r#where() {
|
||||
let part = Part::Where(Default::default());
|
||||
let serialized = part.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph() {
|
||||
let part = Part::Graph(Default::default());
|
||||
let serialized = part.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn start() {
|
||||
let part = Part::Start(sql::thing("foo:bar").unwrap().into());
|
||||
let serialized = part.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn value() {
|
||||
let part = Part::Value(sql::thing("foo:bar").unwrap().into());
|
||||
let serialized = part.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn method() {
|
||||
let part = Part::Method(Default::default(), Default::default());
|
||||
let serialized = part.serialize(Serializer.wrap()).unwrap();
|
||||
assert_eq!(part, serialized);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughpu
|
|||
use std::time::Duration;
|
||||
use surrealdb::dbs::Session;
|
||||
use surrealdb::kvs::Datastore;
|
||||
use surrealdb::sql::Value;
|
||||
use surrealdb::Value;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
fn bench_processor(c: &mut Criterion) {
|
||||
|
|
|
@ -27,7 +27,7 @@ impl super::Routine for Read {
|
|||
|
||||
tasks.spawn(async move {
|
||||
let _: Option<Record> = client
|
||||
.create((table_name, task_id as u64))
|
||||
.create((table_name, task_id as i64))
|
||||
.content(Record {
|
||||
field: Id::rand(),
|
||||
})
|
||||
|
@ -53,7 +53,7 @@ impl super::Routine for Read {
|
|||
tasks.spawn(async move {
|
||||
let _: Option<Record> = criterion::black_box(
|
||||
client
|
||||
.select((table_name, task_id as u64))
|
||||
.select((table_name, task_id as i64))
|
||||
.await
|
||||
.expect("[run] select operation failed")
|
||||
.expect("[run] the select operation returned None"),
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
use super::MlExportConfig;
|
||||
use crate::Result;
|
||||
use crate::{opt::Resource, value::Notification, Result};
|
||||
use bincode::Options;
|
||||
use channel::Sender;
|
||||
use revision::Revisioned;
|
||||
use serde::{ser::SerializeMap as _, Serialize};
|
||||
use std::io::Read;
|
||||
use std::path::PathBuf;
|
||||
use std::{collections::BTreeMap, io::Read};
|
||||
use surrealdb_core::sql::Array;
|
||||
use surrealdb_core::{
|
||||
dbs::Notification,
|
||||
sql::{Object, Query, Value},
|
||||
};
|
||||
use surrealdb_core::sql::{Array as CoreArray, Object as CoreObject, Query, Value as CoreValue};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(any(feature = "protocol-ws", feature = "protocol-http"))]
|
||||
use surrealdb_core::sql::Table as CoreTable;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) enum Command {
|
||||
|
@ -21,48 +20,49 @@ pub(crate) enum Command {
|
|||
database: Option<String>,
|
||||
},
|
||||
Signup {
|
||||
credentials: Object,
|
||||
credentials: CoreObject,
|
||||
},
|
||||
Signin {
|
||||
credentials: Object,
|
||||
credentials: CoreObject,
|
||||
},
|
||||
Authenticate {
|
||||
token: String,
|
||||
},
|
||||
Invalidate,
|
||||
Create {
|
||||
what: Value,
|
||||
data: Option<Value>,
|
||||
what: Resource,
|
||||
data: Option<CoreValue>,
|
||||
},
|
||||
Upsert {
|
||||
what: Value,
|
||||
data: Option<Value>,
|
||||
what: Resource,
|
||||
data: Option<CoreValue>,
|
||||
},
|
||||
Update {
|
||||
what: Value,
|
||||
data: Option<Value>,
|
||||
what: Resource,
|
||||
data: Option<CoreValue>,
|
||||
},
|
||||
Insert {
|
||||
what: Option<Value>,
|
||||
data: Value,
|
||||
// inserts can only be on a table.
|
||||
what: String,
|
||||
data: CoreValue,
|
||||
},
|
||||
Patch {
|
||||
what: Value,
|
||||
data: Option<Value>,
|
||||
what: Resource,
|
||||
data: Option<CoreValue>,
|
||||
},
|
||||
Merge {
|
||||
what: Value,
|
||||
data: Option<Value>,
|
||||
what: Resource,
|
||||
data: Option<CoreValue>,
|
||||
},
|
||||
Select {
|
||||
what: Value,
|
||||
what: Resource,
|
||||
},
|
||||
Delete {
|
||||
what: Value,
|
||||
what: Resource,
|
||||
},
|
||||
Query {
|
||||
query: Query,
|
||||
variables: BTreeMap<String, Value>,
|
||||
variables: CoreObject,
|
||||
},
|
||||
ExportFile {
|
||||
path: PathBuf,
|
||||
|
@ -88,14 +88,14 @@ pub(crate) enum Command {
|
|||
Version,
|
||||
Set {
|
||||
key: String,
|
||||
value: Value,
|
||||
value: CoreValue,
|
||||
},
|
||||
Unset {
|
||||
key: String,
|
||||
},
|
||||
SubscribeLive {
|
||||
uuid: Uuid,
|
||||
notification_sender: Sender<Notification>,
|
||||
notification_sender: Sender<Notification<CoreValue>>,
|
||||
},
|
||||
Kill {
|
||||
uuid: Uuid,
|
||||
|
@ -103,61 +103,60 @@ pub(crate) enum Command {
|
|||
Run {
|
||||
name: String,
|
||||
version: Option<String>,
|
||||
args: Array,
|
||||
args: CoreArray,
|
||||
},
|
||||
}
|
||||
|
||||
impl Command {
|
||||
#[cfg(any(feature = "protocol-ws", feature = "protocol-http"))]
|
||||
pub(crate) fn into_router_request(self, id: Option<i64>) -> Option<RouterRequest> {
|
||||
let id = id.map(Value::from);
|
||||
let res = match self {
|
||||
Command::Use {
|
||||
namespace,
|
||||
database,
|
||||
} => RouterRequest {
|
||||
id,
|
||||
method: Value::from("use"),
|
||||
params: Some(vec![Value::from(namespace), Value::from(database)].into()),
|
||||
method: "use",
|
||||
params: Some(vec![CoreValue::from(namespace), CoreValue::from(database)].into()),
|
||||
},
|
||||
Command::Signup {
|
||||
credentials,
|
||||
} => RouterRequest {
|
||||
id,
|
||||
method: "signup".into(),
|
||||
params: Some(vec![Value::from(credentials)].into()),
|
||||
method: "signup",
|
||||
params: Some(vec![CoreValue::from(credentials)].into()),
|
||||
},
|
||||
Command::Signin {
|
||||
credentials,
|
||||
} => RouterRequest {
|
||||
id,
|
||||
method: "signin".into(),
|
||||
params: Some(vec![Value::from(credentials)].into()),
|
||||
method: "signin",
|
||||
params: Some(vec![CoreValue::from(credentials)].into()),
|
||||
},
|
||||
Command::Authenticate {
|
||||
token,
|
||||
} => RouterRequest {
|
||||
id,
|
||||
method: "authenticate".into(),
|
||||
params: Some(vec![Value::from(token)].into()),
|
||||
method: "authenticate",
|
||||
params: Some(vec![CoreValue::from(token)].into()),
|
||||
},
|
||||
Command::Invalidate => RouterRequest {
|
||||
id,
|
||||
method: "invalidate".into(),
|
||||
method: "invalidate",
|
||||
params: None,
|
||||
},
|
||||
Command::Create {
|
||||
what,
|
||||
data,
|
||||
} => {
|
||||
let mut params = vec![what];
|
||||
let mut params = vec![what.into_core_value()];
|
||||
if let Some(data) = data {
|
||||
params.push(data);
|
||||
}
|
||||
|
||||
RouterRequest {
|
||||
id,
|
||||
method: "create".into(),
|
||||
method: "create",
|
||||
params: Some(params.into()),
|
||||
}
|
||||
}
|
||||
|
@ -166,14 +165,14 @@ impl Command {
|
|||
data,
|
||||
..
|
||||
} => {
|
||||
let mut params = vec![what];
|
||||
let mut params = vec![what.into_core_value()];
|
||||
if let Some(data) = data {
|
||||
params.push(data);
|
||||
}
|
||||
|
||||
RouterRequest {
|
||||
id,
|
||||
method: "upsert".into(),
|
||||
method: "upsert",
|
||||
params: Some(params.into()),
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +181,7 @@ impl Command {
|
|||
data,
|
||||
..
|
||||
} => {
|
||||
let mut params = vec![what];
|
||||
let mut params = vec![what.into_core_value()];
|
||||
|
||||
if let Some(data) = data {
|
||||
params.push(data);
|
||||
|
@ -190,7 +189,7 @@ impl Command {
|
|||
|
||||
RouterRequest {
|
||||
id,
|
||||
method: "update".into(),
|
||||
method: "update",
|
||||
params: Some(params.into()),
|
||||
}
|
||||
}
|
||||
|
@ -198,17 +197,13 @@ impl Command {
|
|||
what,
|
||||
data,
|
||||
} => {
|
||||
let mut params = if let Some(w) = what {
|
||||
vec![w]
|
||||
} else {
|
||||
vec![Value::None]
|
||||
};
|
||||
|
||||
params.push(data);
|
||||
let mut table = CoreTable::default();
|
||||
table.0 = what.clone();
|
||||
let params = vec![CoreValue::from(what), data];
|
||||
|
||||
RouterRequest {
|
||||
id,
|
||||
method: "insert".into(),
|
||||
method: "insert",
|
||||
params: Some(params.into()),
|
||||
}
|
||||
}
|
||||
|
@ -217,14 +212,15 @@ impl Command {
|
|||
data,
|
||||
..
|
||||
} => {
|
||||
let mut params = vec![what];
|
||||
let mut params = vec![what.into_core_value()];
|
||||
|
||||
if let Some(data) = data {
|
||||
params.push(data);
|
||||
}
|
||||
|
||||
RouterRequest {
|
||||
id,
|
||||
method: "patch".into(),
|
||||
method: "patch",
|
||||
params: Some(params.into()),
|
||||
}
|
||||
}
|
||||
|
@ -233,14 +229,14 @@ impl Command {
|
|||
data,
|
||||
..
|
||||
} => {
|
||||
let mut params = vec![what];
|
||||
let mut params = vec![what.into_core_value()];
|
||||
if let Some(data) = data {
|
||||
params.push(data);
|
||||
params.push(data)
|
||||
}
|
||||
|
||||
RouterRequest {
|
||||
id,
|
||||
method: "merge".into(),
|
||||
method: "merge",
|
||||
params: Some(params.into()),
|
||||
}
|
||||
}
|
||||
|
@ -249,25 +245,25 @@ impl Command {
|
|||
..
|
||||
} => RouterRequest {
|
||||
id,
|
||||
method: "select".into(),
|
||||
params: Some(vec![what].into()),
|
||||
method: "select",
|
||||
params: Some(CoreValue::Array(vec![what.into_core_value()].into())),
|
||||
},
|
||||
Command::Delete {
|
||||
what,
|
||||
..
|
||||
} => RouterRequest {
|
||||
id,
|
||||
method: "delete".into(),
|
||||
params: Some(vec![what].into()),
|
||||
method: "delete",
|
||||
params: Some(CoreValue::Array(vec![what.into_core_value()].into())),
|
||||
},
|
||||
Command::Query {
|
||||
query,
|
||||
variables,
|
||||
} => {
|
||||
let params: Vec<Value> = vec![query.into(), variables.into()];
|
||||
let params: Vec<CoreValue> = vec![query.into(), variables.into()];
|
||||
RouterRequest {
|
||||
id,
|
||||
method: "query".into(),
|
||||
method: "query",
|
||||
params: Some(params.into()),
|
||||
}
|
||||
}
|
||||
|
@ -291,12 +287,12 @@ impl Command {
|
|||
} => return None,
|
||||
Command::Health => RouterRequest {
|
||||
id,
|
||||
method: "ping".into(),
|
||||
method: "ping",
|
||||
params: None,
|
||||
},
|
||||
Command::Version => RouterRequest {
|
||||
id,
|
||||
method: "version".into(),
|
||||
method: "version",
|
||||
params: None,
|
||||
},
|
||||
Command::Set {
|
||||
|
@ -304,15 +300,15 @@ impl Command {
|
|||
value,
|
||||
} => RouterRequest {
|
||||
id,
|
||||
method: "let".into(),
|
||||
params: Some(vec![Value::from(key), value].into()),
|
||||
method: "let",
|
||||
params: Some(CoreValue::from(vec![CoreValue::from(key), value])),
|
||||
},
|
||||
Command::Unset {
|
||||
key,
|
||||
} => RouterRequest {
|
||||
id,
|
||||
method: "unset".into(),
|
||||
params: Some(vec![Value::from(key)].into()),
|
||||
method: "unset",
|
||||
params: Some(CoreValue::from(vec![CoreValue::from(key)])),
|
||||
},
|
||||
Command::SubscribeLive {
|
||||
..
|
||||
|
@ -321,8 +317,8 @@ impl Command {
|
|||
uuid,
|
||||
} => RouterRequest {
|
||||
id,
|
||||
method: "kill".into(),
|
||||
params: Some(vec![Value::from(uuid)].into()),
|
||||
method: "kill",
|
||||
params: Some(CoreValue::from(vec![CoreValue::from(uuid)])),
|
||||
},
|
||||
Command::Run {
|
||||
name,
|
||||
|
@ -330,9 +326,10 @@ impl Command {
|
|||
args,
|
||||
} => RouterRequest {
|
||||
id,
|
||||
method: "run".into(),
|
||||
method: "run",
|
||||
params: Some(
|
||||
vec![Value::from(name), Value::from(version), Value::Array(args)].into(),
|
||||
vec![CoreValue::from(name), CoreValue::from(version), CoreValue::Array(args)]
|
||||
.into(),
|
||||
),
|
||||
},
|
||||
};
|
||||
|
@ -340,34 +337,34 @@ impl Command {
|
|||
}
|
||||
|
||||
#[cfg(feature = "protocol-http")]
|
||||
pub(crate) fn needs_one(&self) -> bool {
|
||||
pub(crate) fn needs_flatten(&self) -> bool {
|
||||
match self {
|
||||
Command::Upsert {
|
||||
what,
|
||||
..
|
||||
} => what.is_thing(),
|
||||
Command::Update {
|
||||
}
|
||||
| Command::Update {
|
||||
what,
|
||||
..
|
||||
} => what.is_thing(),
|
||||
}
|
||||
| Command::Patch {
|
||||
what,
|
||||
..
|
||||
}
|
||||
| Command::Merge {
|
||||
what,
|
||||
..
|
||||
}
|
||||
| Command::Select {
|
||||
what,
|
||||
}
|
||||
| Command::Delete {
|
||||
what,
|
||||
} => matches!(what, Resource::RecordId(_)),
|
||||
Command::Insert {
|
||||
data,
|
||||
..
|
||||
} => !data.is_array(),
|
||||
Command::Patch {
|
||||
what,
|
||||
..
|
||||
} => what.is_thing(),
|
||||
Command::Merge {
|
||||
what,
|
||||
..
|
||||
} => what.is_thing(),
|
||||
Command::Select {
|
||||
what,
|
||||
} => what.is_thing(),
|
||||
Command::Delete {
|
||||
what,
|
||||
} => what.is_thing(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -378,9 +375,9 @@ impl Command {
|
|||
/// This struct serializes as if it is a surrealdb_core::sql::Value::Object.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct RouterRequest {
|
||||
id: Option<Value>,
|
||||
method: Value,
|
||||
params: Option<Value>,
|
||||
id: Option<i64>,
|
||||
method: &'static str,
|
||||
params: Option<CoreValue>,
|
||||
}
|
||||
|
||||
impl Serialize for RouterRequest {
|
||||
|
@ -389,6 +386,47 @@ impl Serialize for RouterRequest {
|
|||
S: serde::Serializer,
|
||||
{
|
||||
struct InnerRequest<'a>(&'a RouterRequest);
|
||||
struct InnerNumberVariant(i64);
|
||||
struct InnerNumber(i64);
|
||||
struct InnerMethod(&'static str);
|
||||
struct InnerStrand(&'static str);
|
||||
struct InnerObject<'a>(&'a RouterRequest);
|
||||
|
||||
impl Serialize for InnerNumberVariant {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_newtype_variant("Value", 3, "Number", &InnerNumber(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for InnerNumber {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_newtype_variant("Number", 0, "Int", &self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for InnerMethod {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_newtype_variant("Value", 4, "Strand", &InnerStrand(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for InnerStrand {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_newtype_struct("$surrealdb::private::sql::Strand", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for InnerRequest<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
|
@ -398,9 +436,9 @@ impl Serialize for RouterRequest {
|
|||
let size = 1 + self.0.id.is_some() as usize + self.0.params.is_some() as usize;
|
||||
let mut map = serializer.serialize_map(Some(size))?;
|
||||
if let Some(id) = self.0.id.as_ref() {
|
||||
map.serialize_entry("id", id)?;
|
||||
map.serialize_entry("id", &InnerNumberVariant(*id))?;
|
||||
}
|
||||
map.serialize_entry("method", &self.0.method)?;
|
||||
map.serialize_entry("method", &InnerMethod(self.0.method))?;
|
||||
if let Some(params) = self.0.params.as_ref() {
|
||||
map.serialize_entry("params", params)?;
|
||||
}
|
||||
|
@ -408,7 +446,21 @@ impl Serialize for RouterRequest {
|
|||
}
|
||||
}
|
||||
|
||||
serializer.serialize_newtype_variant("Value", 9, "Object", &InnerRequest(self))
|
||||
impl Serialize for InnerObject<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_newtype_struct("Object", &InnerRequest(self.0))
|
||||
}
|
||||
}
|
||||
|
||||
serializer.serialize_newtype_variant(
|
||||
"$surrealdb::private::sql::Value",
|
||||
9,
|
||||
"Object",
|
||||
&InnerObject(self),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -441,12 +493,38 @@ impl Revisioned for RouterRequest {
|
|||
serializer
|
||||
.serialize_into(&mut *w, "id")
|
||||
.map_err(|err| revision::Error::Serialize(err.to_string()))?;
|
||||
|
||||
// the Value version
|
||||
1u16.serialize_revisioned(w)?;
|
||||
|
||||
// the Value::Number variant
|
||||
3u16.serialize_revisioned(w)?;
|
||||
|
||||
// the Number version
|
||||
1u16.serialize_revisioned(w)?;
|
||||
|
||||
// the Number::Int variant
|
||||
0u16.serialize_revisioned(w)?;
|
||||
|
||||
x.serialize_revisioned(w)?;
|
||||
}
|
||||
|
||||
serializer
|
||||
.serialize_into(&mut *w, "method")
|
||||
.map_err(|err| revision::Error::Serialize(err.to_string()))?;
|
||||
self.method.serialize_revisioned(w)?;
|
||||
|
||||
// the Value version
|
||||
1u16.serialize_revisioned(w)?;
|
||||
|
||||
// the Value::Strand variant
|
||||
4u16.serialize_revisioned(w)?;
|
||||
|
||||
// the Strand version
|
||||
1u16.serialize_revisioned(w)?;
|
||||
|
||||
serializer
|
||||
.serialize_into(&mut *w, self.method)
|
||||
.map_err(|e| revision::Error::Serialize(format!("{:?}", e)))?;
|
||||
|
||||
if let Some(x) = self.params.as_ref() {
|
||||
serializer
|
||||
|
@ -471,7 +549,7 @@ mod test {
|
|||
use std::io::Cursor;
|
||||
|
||||
use revision::Revisioned;
|
||||
use surrealdb_core::sql::Value;
|
||||
use surrealdb_core::sql::{Number, Value};
|
||||
|
||||
use super::RouterRequest;
|
||||
|
||||
|
@ -485,16 +563,27 @@ mod test {
|
|||
let Value::Object(obj) = val else {
|
||||
panic!("not an object");
|
||||
};
|
||||
assert_eq!(obj.get("id").cloned(), req.id);
|
||||
assert_eq!(obj.get("method").unwrap().clone(), req.method);
|
||||
assert_eq!(
|
||||
obj.get("id").cloned().and_then(|x| if let Value::Number(Number::Int(x)) = x {
|
||||
Some(x)
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
req.id
|
||||
);
|
||||
let Some(Value::Strand(x)) = obj.get("method") else {
|
||||
panic!("invalid method field: {}", obj)
|
||||
};
|
||||
assert_eq!(x.0, req.method);
|
||||
|
||||
assert_eq!(obj.get("params").cloned(), req.params);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn router_request_value_conversion() {
|
||||
let request = RouterRequest {
|
||||
id: Some(Value::from(1234i64)),
|
||||
method: Value::from("request"),
|
||||
id: Some(1234),
|
||||
method: "request",
|
||||
params: Some(vec![Value::from(1234i64), Value::from("request")].into()),
|
||||
};
|
||||
|
||||
|
|
|
@ -6,16 +6,16 @@ use crate::api::opt::Endpoint;
|
|||
use crate::api::ExtraFeatures;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::Value;
|
||||
use channel::Receiver;
|
||||
use channel::Sender;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::atomic::AtomicI64;
|
||||
use std::sync::atomic::Ordering;
|
||||
use surrealdb_core::sql::{from_value, Value};
|
||||
use surrealdb_core::sql::{from_value as from_core_value, Value as CoreValue};
|
||||
|
||||
mod cmd;
|
||||
|
||||
pub(crate) use cmd::Command;
|
||||
#[cfg(feature = "protocol-http")]
|
||||
pub(crate) use cmd::RouterRequest;
|
||||
|
@ -70,7 +70,7 @@ impl Router {
|
|||
pub(crate) fn recv(
|
||||
&self,
|
||||
receiver: Receiver<Result<DbResponse>>,
|
||||
) -> BoxFuture<'_, Result<Value>> {
|
||||
) -> BoxFuture<'_, Result<CoreValue>> {
|
||||
Box::pin(async move {
|
||||
let response = receiver.recv().await?;
|
||||
match response? {
|
||||
|
@ -102,7 +102,7 @@ impl Router {
|
|||
Box::pin(async move {
|
||||
let rx = self.send(command).await?;
|
||||
let value = self.recv(rx).await?;
|
||||
from_value(value).map_err(Into::into)
|
||||
from_core_value(value).map_err(Into::into)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -114,8 +114,8 @@ impl Router {
|
|||
Box::pin(async move {
|
||||
let rx = self.send(command).await?;
|
||||
match self.recv(rx).await? {
|
||||
Value::None | Value::Null => Ok(None),
|
||||
value => from_value(value).map_err(Into::into),
|
||||
CoreValue::None | CoreValue::Null => Ok(None),
|
||||
value => from_core_value(value).map_err(Into::into),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -128,11 +128,11 @@ impl Router {
|
|||
Box::pin(async move {
|
||||
let rx = self.send(command).await?;
|
||||
let value = match self.recv(rx).await? {
|
||||
Value::None | Value::Null => Value::Array(Default::default()),
|
||||
Value::Array(array) => Value::Array(array),
|
||||
CoreValue::None | CoreValue::Null => return Ok(Vec::new()),
|
||||
CoreValue::Array(array) => CoreValue::Array(array),
|
||||
value => vec![value].into(),
|
||||
};
|
||||
from_value(value).map_err(Into::into)
|
||||
from_core_value(value).map_err(Into::into)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -141,10 +141,10 @@ impl Router {
|
|||
Box::pin(async move {
|
||||
let rx = self.send(command).await?;
|
||||
match self.recv(rx).await? {
|
||||
Value::None | Value::Null => Ok(()),
|
||||
Value::Array(array) if array.is_empty() => Ok(()),
|
||||
CoreValue::None | CoreValue::Null => Ok(()),
|
||||
CoreValue::Array(array) if array.is_empty() => Ok(()),
|
||||
value => Err(Error::FromValue {
|
||||
value,
|
||||
value: Value::from_inner(value),
|
||||
error: "expected the database to return nothing".to_owned(),
|
||||
}
|
||||
.into()),
|
||||
|
@ -156,7 +156,7 @@ impl Router {
|
|||
pub(crate) fn execute_value(&self, command: Command) -> BoxFuture<'_, Result<Value>> {
|
||||
Box::pin(async move {
|
||||
let rx = self.send(command).await?;
|
||||
self.recv(rx).await
|
||||
Ok(Value::from_inner(self.recv(rx).await?))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ pub enum DbResponse {
|
|||
/// The response sent for the `query` method
|
||||
Query(Response),
|
||||
/// The response sent for any method except `query`
|
||||
Other(Value),
|
||||
Other(CoreValue),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
@ -299,11 +299,13 @@ pub fn connect(address: impl IntoEndpoint) -> Connect<Any, Surreal<Any>> {
|
|||
|
||||
#[cfg(all(test, feature = "kv-mem"))]
|
||||
mod tests {
|
||||
|
||||
use surrealdb_core::sql::Object;
|
||||
|
||||
use super::*;
|
||||
use crate::opt::auth::Root;
|
||||
use crate::opt::capabilities::Capabilities;
|
||||
use crate::sql;
|
||||
use crate::sql::Value;
|
||||
use crate::Value;
|
||||
|
||||
#[tokio::test]
|
||||
async fn local_engine_without_auth() {
|
||||
|
@ -328,7 +330,11 @@ mod tests {
|
|||
let mut res = db.query("INFO FOR ROOT").await.unwrap();
|
||||
let users: Value = res.take("users").unwrap();
|
||||
|
||||
assert_eq!(users, sql::value("{}").unwrap(), "there should be no users in the system");
|
||||
assert_eq!(
|
||||
users.into_inner(),
|
||||
Object::default().into(),
|
||||
"there should be no users in the system"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
@ -20,19 +20,14 @@
|
|||
//! useful is to only enable the in-memory engine (`kv-mem`) during development. Besides letting you not
|
||||
//! worry about those dependencies on your dev machine, it allows you to keep compile times low
|
||||
//! during development while allowing you to test your code fully.
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) mod native;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub(crate) mod wasm;
|
||||
|
||||
use crate::{
|
||||
api::{
|
||||
conn::{Command, DbResponse, RequestData},
|
||||
Connect, Response as QueryResponse, Result, Surreal,
|
||||
},
|
||||
method::Stats,
|
||||
opt::IntoEndpoint,
|
||||
opt::{IntoEndpoint, Resource as ApiResource, Table},
|
||||
value::Notification,
|
||||
};
|
||||
use channel::Sender;
|
||||
use indexmap::IndexMap;
|
||||
|
@ -44,16 +39,18 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
use surrealdb_core::{
|
||||
dbs::{Notification, Response, Session},
|
||||
dbs::{Response, Session},
|
||||
iam,
|
||||
kvs::Datastore,
|
||||
sql::{
|
||||
statements::{
|
||||
CreateStatement, DeleteStatement, InsertStatement, KillStatement, SelectStatement,
|
||||
UpdateStatement, UpsertStatement,
|
||||
},
|
||||
Data, Field, Output, Query, Statement, Uuid, Value,
|
||||
Data, Field, Output, Query, Statement, Value as CoreValue,
|
||||
},
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::api::err::Error;
|
||||
|
@ -80,7 +77,12 @@ use surrealdb_core::{
|
|||
sql::statements::{DefineModelStatement, DefineStatement},
|
||||
};
|
||||
|
||||
use super::value_to_values;
|
||||
use super::resource_to_values;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) mod native;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub(crate) mod wasm;
|
||||
|
||||
const DEFAULT_TICK_INTERVAL: Duration = Duration::from_secs(10);
|
||||
|
||||
|
@ -374,14 +376,19 @@ impl Surreal<Db> {
|
|||
}
|
||||
|
||||
fn process(responses: Vec<Response>) -> QueryResponse {
|
||||
let mut map = IndexMap::with_capacity(responses.len());
|
||||
let mut map = IndexMap::<usize, (Stats, Result<CoreValue>)>::with_capacity(responses.len());
|
||||
for (index, response) in responses.into_iter().enumerate() {
|
||||
let stats = Stats {
|
||||
execution_time: Some(response.time),
|
||||
};
|
||||
match response.result {
|
||||
Ok(value) => map.insert(index, (stats, Ok(value))),
|
||||
Err(error) => map.insert(index, (stats, Err(error.into()))),
|
||||
Ok(value) => {
|
||||
// Deserializing from a core value should always work.
|
||||
map.insert(index, (stats, Ok(value)));
|
||||
}
|
||||
Err(error) => {
|
||||
map.insert(index, (stats, Err(error.into())));
|
||||
}
|
||||
};
|
||||
}
|
||||
QueryResponse {
|
||||
|
@ -390,25 +397,25 @@ fn process(responses: Vec<Response>) -> QueryResponse {
|
|||
}
|
||||
}
|
||||
|
||||
async fn take(one: bool, responses: Vec<Response>) -> Result<Value> {
|
||||
async fn take(one: bool, responses: Vec<Response>) -> Result<CoreValue> {
|
||||
if let Some((_stats, result)) = process(responses).results.swap_remove(&0) {
|
||||
let value = result?;
|
||||
match one {
|
||||
true => match value {
|
||||
Value::Array(mut array) => {
|
||||
if let [value] = &mut array.0[..] {
|
||||
return Ok(mem::take(value));
|
||||
CoreValue::Array(mut array) => {
|
||||
if let [ref mut value] = array[..] {
|
||||
return Ok(mem::replace(value, CoreValue::None));
|
||||
}
|
||||
}
|
||||
Value::None | Value::Null => {}
|
||||
CoreValue::None | CoreValue::Null => {}
|
||||
value => return Ok(value),
|
||||
},
|
||||
false => return Ok(value),
|
||||
}
|
||||
}
|
||||
match one {
|
||||
true => Ok(Value::None),
|
||||
false => Ok(Value::Array(Default::default())),
|
||||
true => Ok(CoreValue::None),
|
||||
false => Ok(CoreValue::Array(Default::default())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -476,8 +483,8 @@ async fn kill_live_query(
|
|||
kvs: &Datastore,
|
||||
id: Uuid,
|
||||
session: &Session,
|
||||
vars: BTreeMap<String, Value>,
|
||||
) -> Result<Value> {
|
||||
vars: BTreeMap<String, CoreValue>,
|
||||
) -> Result<CoreValue> {
|
||||
let mut query = Query::default();
|
||||
let mut kill = KillStatement::default();
|
||||
kill.id = id.into();
|
||||
|
@ -493,8 +500,8 @@ async fn router(
|
|||
}: RequestData,
|
||||
kvs: &Arc<Datastore>,
|
||||
session: &mut Session,
|
||||
vars: &mut BTreeMap<String, Value>,
|
||||
live_queries: &mut HashMap<Uuid, Sender<Notification>>,
|
||||
vars: &mut BTreeMap<String, CoreValue>,
|
||||
live_queries: &mut HashMap<Uuid, Sender<Notification<CoreValue>>>,
|
||||
) -> Result<DbResponse> {
|
||||
match command {
|
||||
Command::Use {
|
||||
|
@ -507,29 +514,29 @@ async fn router(
|
|||
if let Some(db) = database {
|
||||
session.db = Some(db);
|
||||
}
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::Signup {
|
||||
credentials,
|
||||
} => {
|
||||
let response = crate::iam::signup::signup(kvs, session, credentials).await?;
|
||||
let response = iam::signup::signup(kvs, session, credentials).await?;
|
||||
Ok(DbResponse::Other(response.into()))
|
||||
}
|
||||
Command::Signin {
|
||||
credentials,
|
||||
} => {
|
||||
let response = crate::iam::signin::signin(kvs, session, credentials).await?;
|
||||
let response = iam::signin::signin(kvs, session, credentials).await?;
|
||||
Ok(DbResponse::Other(response.into()))
|
||||
}
|
||||
Command::Authenticate {
|
||||
token,
|
||||
} => {
|
||||
crate::iam::verify::token(kvs, session, &token).await?;
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
iam::verify::token(kvs, session, &token).await?;
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::Invalidate => {
|
||||
crate::iam::clear::clear(session)?;
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
iam::clear::clear(session)?;
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::Create {
|
||||
what,
|
||||
|
@ -538,7 +545,7 @@ async fn router(
|
|||
let mut query = Query::default();
|
||||
let statement = {
|
||||
let mut stmt = CreateStatement::default();
|
||||
stmt.what = value_to_values(what);
|
||||
stmt.what = resource_to_values(what);
|
||||
stmt.data = data.map(Data::ContentExpression);
|
||||
stmt.output = Some(Output::After);
|
||||
stmt
|
||||
|
@ -553,16 +560,17 @@ async fn router(
|
|||
data,
|
||||
} => {
|
||||
let mut query = Query::default();
|
||||
let one = what.is_thing_single();
|
||||
let one = matches!(what, ApiResource::RecordId(_));
|
||||
let statement = {
|
||||
let mut stmt = UpsertStatement::default();
|
||||
stmt.what = value_to_values(what);
|
||||
stmt.what = resource_to_values(what);
|
||||
stmt.data = data.map(Data::ContentExpression);
|
||||
stmt.output = Some(Output::After);
|
||||
stmt
|
||||
};
|
||||
query.0 .0 = vec![Statement::Upsert(statement)];
|
||||
let response = kvs.process(query, &*session, Some(vars.clone())).await?;
|
||||
let vars = vars.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
|
||||
let response = kvs.process(query, &*session, Some(vars)).await?;
|
||||
let value = take(one, response).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
}
|
||||
|
@ -571,16 +579,17 @@ async fn router(
|
|||
data,
|
||||
} => {
|
||||
let mut query = Query::default();
|
||||
let one = what.is_thing_single();
|
||||
let one = matches!(what, ApiResource::RecordId(_));
|
||||
let statement = {
|
||||
let mut stmt = UpdateStatement::default();
|
||||
stmt.what = value_to_values(what);
|
||||
stmt.what = resource_to_values(what);
|
||||
stmt.data = data.map(Data::ContentExpression);
|
||||
stmt.output = Some(Output::After);
|
||||
stmt
|
||||
};
|
||||
query.0 .0 = vec![Statement::Update(statement)];
|
||||
let response = kvs.process(query, &*session, Some(vars.clone())).await?;
|
||||
let vars = vars.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
|
||||
let response = kvs.process(query, &*session, Some(vars)).await?;
|
||||
let value = take(one, response).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
}
|
||||
|
@ -592,13 +601,14 @@ async fn router(
|
|||
let one = !data.is_array();
|
||||
let statement = {
|
||||
let mut stmt = InsertStatement::default();
|
||||
stmt.into = what;
|
||||
stmt.into = Some(Table(what).into_core().into());
|
||||
stmt.data = Data::SingleExpression(data);
|
||||
stmt.output = Some(Output::After);
|
||||
stmt
|
||||
};
|
||||
query.0 .0 = vec![Statement::Insert(statement)];
|
||||
let response = kvs.process(query, &*session, Some(vars.clone())).await?;
|
||||
let vars = vars.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
|
||||
let response = kvs.process(query, &*session, Some(vars)).await?;
|
||||
let value = take(one, response).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
}
|
||||
|
@ -607,16 +617,17 @@ async fn router(
|
|||
data,
|
||||
} => {
|
||||
let mut query = Query::default();
|
||||
let one = what.is_thing_single();
|
||||
let one = matches!(what, ApiResource::RecordId(_));
|
||||
let statement = {
|
||||
let mut stmt = UpdateStatement::default();
|
||||
stmt.what = value_to_values(what);
|
||||
stmt.what = resource_to_values(what);
|
||||
stmt.data = data.map(Data::PatchExpression);
|
||||
stmt.output = Some(Output::After);
|
||||
stmt
|
||||
};
|
||||
query.0 .0 = vec![Statement::Update(statement)];
|
||||
let response = kvs.process(query, &*session, Some(vars.clone())).await?;
|
||||
let vars = vars.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
|
||||
let response = kvs.process(query, &*session, Some(vars)).await?;
|
||||
let value = take(one, response).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
}
|
||||
|
@ -625,16 +636,17 @@ async fn router(
|
|||
data,
|
||||
} => {
|
||||
let mut query = Query::default();
|
||||
let one = what.is_thing_single();
|
||||
let one = matches!(what, ApiResource::RecordId(_));
|
||||
let statement = {
|
||||
let mut stmt = UpdateStatement::default();
|
||||
stmt.what = value_to_values(what);
|
||||
stmt.what = resource_to_values(what);
|
||||
stmt.data = data.map(Data::MergeExpression);
|
||||
stmt.output = Some(Output::After);
|
||||
stmt
|
||||
};
|
||||
query.0 .0 = vec![Statement::Update(statement)];
|
||||
let response = kvs.process(query, &*session, Some(vars.clone())).await?;
|
||||
let vars = vars.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
|
||||
let response = kvs.process(query, &*session, Some(vars)).await?;
|
||||
let value = take(one, response).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
}
|
||||
|
@ -642,15 +654,16 @@ async fn router(
|
|||
what,
|
||||
} => {
|
||||
let mut query = Query::default();
|
||||
let one = what.is_thing_single();
|
||||
let one = matches!(what, ApiResource::RecordId(_));
|
||||
let statement = {
|
||||
let mut stmt = SelectStatement::default();
|
||||
stmt.what = value_to_values(what);
|
||||
stmt.what = resource_to_values(what);
|
||||
stmt.expr.0 = vec![Field::All];
|
||||
stmt
|
||||
};
|
||||
query.0 .0 = vec![Statement::Select(statement)];
|
||||
let response = kvs.process(query, &*session, Some(vars.clone())).await?;
|
||||
let vars = vars.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
|
||||
let response = kvs.process(query, &*session, Some(vars)).await?;
|
||||
let value = take(one, response).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
}
|
||||
|
@ -658,15 +671,16 @@ async fn router(
|
|||
what,
|
||||
} => {
|
||||
let mut query = Query::default();
|
||||
let one = what.is_thing_single();
|
||||
let one = matches!(what, ApiResource::RecordId(_));
|
||||
let statement = {
|
||||
let mut stmt = DeleteStatement::default();
|
||||
stmt.what = value_to_values(what);
|
||||
stmt.what = resource_to_values(what);
|
||||
stmt.output = Some(Output::Before);
|
||||
stmt
|
||||
};
|
||||
query.0 .0 = vec![Statement::Delete(statement)];
|
||||
let response = kvs.process(query, &*session, Some(vars.clone())).await?;
|
||||
let vars = vars.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
|
||||
let response = kvs.process(query, &*session, Some(vars)).await?;
|
||||
let value = take(one, response).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
}
|
||||
|
@ -675,7 +689,7 @@ async fn router(
|
|||
mut variables,
|
||||
} => {
|
||||
let mut vars = vars.clone();
|
||||
vars.append(&mut variables);
|
||||
vars.append(&mut variables.0);
|
||||
let response = kvs.process(query, &*session, Some(vars)).await?;
|
||||
let response = process(response);
|
||||
Ok(DbResponse::Query(response))
|
||||
|
@ -746,7 +760,7 @@ async fn router(
|
|||
let copy = copy(file, &mut reader, &mut output);
|
||||
|
||||
tokio::try_join!(export, bridge, copy)?;
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "ml"))]
|
||||
|
@ -793,7 +807,7 @@ async fn router(
|
|||
let copy = copy(path, &mut reader, &mut output);
|
||||
|
||||
tokio::try_join!(export, bridge, copy)?;
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
@ -822,7 +836,7 @@ async fn router(
|
|||
tokio::join!(export, bridge);
|
||||
});
|
||||
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "ml"))]
|
||||
Command::ExportBytesMl {
|
||||
|
@ -851,7 +865,7 @@ async fn router(
|
|||
tokio::join!(export, bridge);
|
||||
});
|
||||
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
Command::ImportFile {
|
||||
|
@ -882,7 +896,7 @@ async fn router(
|
|||
response.result?;
|
||||
}
|
||||
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
#[cfg(all(not(target_arch = "wasm32"), feature = "ml"))]
|
||||
Command::ImportMl {
|
||||
|
@ -946,42 +960,46 @@ async fn router(
|
|||
response.result?;
|
||||
}
|
||||
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::Health => Ok(DbResponse::Other(CoreValue::None)),
|
||||
Command::Version => {
|
||||
Ok(DbResponse::Other(CoreValue::from(surrealdb_core::env::VERSION.to_string())))
|
||||
}
|
||||
Command::Health => Ok(DbResponse::Other(Value::None)),
|
||||
Command::Version => Ok(DbResponse::Other(crate::env::VERSION.into())),
|
||||
Command::Set {
|
||||
key,
|
||||
value,
|
||||
} => {
|
||||
let var = Some(map! {
|
||||
key.clone() => Value::None,
|
||||
=> vars
|
||||
});
|
||||
match kvs.compute(value, &*session, var).await? {
|
||||
Value::None => vars.remove(&key),
|
||||
let mut tmp_vars = vars.clone();
|
||||
tmp_vars.insert(key.clone(), value.clone());
|
||||
|
||||
// Need to compute because certain keys might not be allowed to be set and those should
|
||||
// be rejected by an error.
|
||||
match kvs.compute(value, &*session, Some(tmp_vars)).await? {
|
||||
CoreValue::None => vars.remove(&key),
|
||||
v => vars.insert(key, v),
|
||||
};
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::Unset {
|
||||
key,
|
||||
} => {
|
||||
vars.remove(&key);
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::SubscribeLive {
|
||||
uuid,
|
||||
notification_sender,
|
||||
} => {
|
||||
live_queries.insert(uuid.into(), notification_sender);
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
live_queries.insert(uuid, notification_sender);
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::Kill {
|
||||
uuid,
|
||||
} => {
|
||||
live_queries.remove(&uuid.into());
|
||||
let value = kill_live_query(kvs, uuid.into(), session, vars.clone()).await?;
|
||||
live_queries.remove(&uuid);
|
||||
let value = kill_live_query(kvs, uuid, session, vars.clone()).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
}
|
||||
|
||||
|
@ -990,22 +1008,30 @@ async fn router(
|
|||
version: _version,
|
||||
args,
|
||||
} => {
|
||||
let func: Value = match &name[0..4] {
|
||||
"fn::" => Function::Custom(name.chars().skip(4).collect(), args.0).into(),
|
||||
// should return error, but can't on wasm
|
||||
let func: CoreValue = if let Some(name) = name.strip_prefix("fn::") {
|
||||
Function::Custom(name.to_owned(), args.0).into()
|
||||
} else if let Some(_name) = name.strip_prefix("ml::") {
|
||||
#[cfg(feature = "ml")]
|
||||
"ml::" => {
|
||||
let mut tmp = Model::default();
|
||||
{
|
||||
let mut model = Model::default();
|
||||
|
||||
tmp.name = name.chars().skip(4).collect();
|
||||
tmp.args = args.0;
|
||||
tmp.version = _version
|
||||
model.name = _name.to_owned();
|
||||
model.args = args.0;
|
||||
model.version = _version
|
||||
.ok_or(Error::Query("ML functions must have a version".to_string()))?;
|
||||
tmp
|
||||
model.into()
|
||||
}
|
||||
.into(),
|
||||
_ => Function::Normal(name, args.0).into(),
|
||||
#[cfg(not(feature = "ml"))]
|
||||
{
|
||||
return Err(crate::error::Db::InvalidModel {
|
||||
message: "Machine learning computation is not enabled.".to_owned(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
} else {
|
||||
Function::Custom(name, args.0).into()
|
||||
};
|
||||
|
||||
let stmt = Statement::Value(func);
|
||||
|
||||
let response = kvs.process(stmt.into(), &*session, Some(vars.clone())).await?;
|
||||
|
|
|
@ -1,31 +1,24 @@
|
|||
use crate::api::conn::Connection;
|
||||
use crate::api::conn::Route;
|
||||
use crate::api::conn::Router;
|
||||
use crate::api::engine::local::Db;
|
||||
use crate::api::method::BoxFuture;
|
||||
use crate::api::opt::{Endpoint, EndpointKind};
|
||||
use crate::api::ExtraFeatures;
|
||||
use crate::api::OnceLockExt;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::dbs::Session;
|
||||
use crate::engine::tasks::start_tasks;
|
||||
use crate::iam::Level;
|
||||
use crate::kvs::Datastore;
|
||||
use crate::opt::auth::Root;
|
||||
use crate::opt::WaitFor;
|
||||
use crate::options::EngineOptions;
|
||||
use channel::Receiver;
|
||||
use channel::Sender;
|
||||
use futures::stream::poll_fn;
|
||||
use futures::StreamExt;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::atomic::AtomicI64;
|
||||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
use std::task::Poll;
|
||||
use crate::{
|
||||
api::{
|
||||
conn::{Connection, Route, Router},
|
||||
engine::local::Db,
|
||||
method::BoxFuture,
|
||||
opt::{Endpoint, EndpointKind},
|
||||
ExtraFeatures, OnceLockExt, Result, Surreal,
|
||||
},
|
||||
engine::tasks::start_tasks,
|
||||
opt::{auth::Root, WaitFor},
|
||||
value::Notification,
|
||||
Action,
|
||||
};
|
||||
use channel::{Receiver, Sender};
|
||||
use futures::{stream::poll_fn, StreamExt};
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
sync::{atomic::AtomicI64, Arc, OnceLock},
|
||||
task::Poll,
|
||||
};
|
||||
use surrealdb_core::{dbs::Session, iam::Level, kvs::Datastore, options::EngineOptions};
|
||||
use tokio::sync::watch;
|
||||
|
||||
impl crate::api::Connection for Db {}
|
||||
|
@ -121,7 +114,7 @@ pub(crate) async fn run_router(
|
|||
let kvs = kvs.with_temporary_directory(address.config.temporary_directory);
|
||||
|
||||
let kvs = Arc::new(kvs);
|
||||
let mut vars = BTreeMap::new();
|
||||
let mut vars = BTreeMap::default();
|
||||
let mut live_queries = HashMap::new();
|
||||
let mut session = Session::default().with_rt(true);
|
||||
|
||||
|
@ -166,8 +159,16 @@ pub(crate) async fn run_router(
|
|||
// channel?
|
||||
continue
|
||||
};
|
||||
let id = notification.id;
|
||||
|
||||
let notification = Notification{
|
||||
query_id: *notification.id,
|
||||
action: Action::from_core(notification.action),
|
||||
data: notification.result
|
||||
};
|
||||
|
||||
let id = notification.query_id;
|
||||
if let Some(sender) = live_queries.get(&id) {
|
||||
|
||||
if sender.send(notification).await.is_err() {
|
||||
live_queries.remove(&id);
|
||||
if let Err(error) =
|
||||
|
|
|
@ -16,6 +16,7 @@ use crate::kvs::Datastore;
|
|||
use crate::opt::auth::Root;
|
||||
use crate::opt::WaitFor;
|
||||
use crate::options::EngineOptions;
|
||||
use crate::{Action, Notification};
|
||||
use channel::{Receiver, Sender};
|
||||
use futures::stream::poll_fn;
|
||||
use futures::FutureExt;
|
||||
|
@ -157,10 +158,17 @@ pub(crate) async fn run_router(
|
|||
|
||||
let id = notification.id;
|
||||
if let Some(sender) = live_queries.get(&id) {
|
||||
|
||||
let notification = Notification {
|
||||
query_id: notification.id.0,
|
||||
action: Action::from_core(notification.action),
|
||||
data: notification.result,
|
||||
};
|
||||
|
||||
if sender.send(notification).await.is_err() {
|
||||
live_queries.remove(&id);
|
||||
if let Err(error) =
|
||||
super::kill_live_query(&kvs, id, &session, vars.clone()).await
|
||||
super::kill_live_query(&kvs, *id, &session, vars.clone()).await
|
||||
{
|
||||
warn!("Failed to kill live query '{id}'; {error}");
|
||||
}
|
||||
|
|
|
@ -10,17 +10,17 @@ pub mod any;
|
|||
feature = "kv-surrealkv",
|
||||
))]
|
||||
pub mod local;
|
||||
pub mod proto;
|
||||
#[cfg(any(feature = "protocol-http", feature = "protocol-ws"))]
|
||||
pub mod remote;
|
||||
#[doc(hidden)]
|
||||
pub mod tasks;
|
||||
|
||||
use crate::sql::Value;
|
||||
use crate::sql::Values;
|
||||
use futures::Stream;
|
||||
use std::pin::Pin;
|
||||
use std::task::Context;
|
||||
use std::task::Poll;
|
||||
use surrealdb_core::sql::Values as CoreValues;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::time::Instant;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
@ -30,21 +30,26 @@ use wasmtimer::std::Instant;
|
|||
#[cfg(target_arch = "wasm32")]
|
||||
use wasmtimer::tokio::Interval;
|
||||
|
||||
use crate::Value;
|
||||
|
||||
use super::opt::Resource;
|
||||
use super::opt::Table;
|
||||
|
||||
// used in http and all local engines.
|
||||
#[allow(dead_code)]
|
||||
fn value_to_values(v: Value) -> Values {
|
||||
match v {
|
||||
Value::Array(x) => {
|
||||
let mut values = Values::default();
|
||||
values.0 = x.0;
|
||||
values
|
||||
}
|
||||
x => {
|
||||
let mut values = Values::default();
|
||||
values.0 = vec![x];
|
||||
values
|
||||
fn resource_to_values(r: Resource) -> CoreValues {
|
||||
let mut res = CoreValues::default();
|
||||
match r {
|
||||
Resource::Table(x) => {
|
||||
res.0 = vec![Table(x).into_core().into()];
|
||||
}
|
||||
Resource::RecordId(x) => res.0 = vec![x.into_inner().into()],
|
||||
Resource::Object(x) => res.0 = vec![x.into_inner().into()],
|
||||
Resource::Array(x) => res.0 = Value::array_to_core(x),
|
||||
Resource::Edge(x) => res.0 = vec![x.into_inner().into()],
|
||||
Resource::Range(x) => res.0 = vec![x.into_inner().into()],
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
struct IntervalStream {
|
||||
|
|
41
lib/src/api/engine/proto.rs
Normal file
41
lib/src/api/engine/proto.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use revision::revisioned;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::Value;
|
||||
|
||||
#[revisioned(revision = 1)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub(crate) struct Failure {
|
||||
pub(crate) code: i64,
|
||||
pub(crate) message: String,
|
||||
}
|
||||
|
||||
#[revisioned(revision = 1)]
|
||||
#[derive(Clone, Debug, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
#[non_exhaustive]
|
||||
pub enum ResponseAction {
|
||||
Create,
|
||||
Update,
|
||||
Delete,
|
||||
}
|
||||
|
||||
#[revisioned(revision = 1)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
#[doc(hidden)]
|
||||
#[non_exhaustive]
|
||||
pub enum Status {
|
||||
Ok,
|
||||
Err,
|
||||
}
|
||||
|
||||
#[revisioned(revision = 1)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[doc(hidden)]
|
||||
#[non_exhaustive]
|
||||
pub struct QueryMethodResponse {
|
||||
pub time: String,
|
||||
pub status: Status,
|
||||
pub result: Value,
|
||||
}
|
|
@ -1,10 +1,4 @@
|
|||
//! HTTP engine
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) mod native;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub(crate) mod wasm;
|
||||
|
||||
use crate::api::conn::Command;
|
||||
use crate::api::conn::DbResponse;
|
||||
use crate::api::conn::RequestData;
|
||||
|
@ -20,8 +14,7 @@ use crate::headers::AUTH_NS;
|
|||
use crate::headers::DB;
|
||||
use crate::headers::NS;
|
||||
use crate::opt::IntoEndpoint;
|
||||
use crate::sql::from_value;
|
||||
use crate::sql::Value;
|
||||
use crate::Value;
|
||||
use futures::TryStreamExt;
|
||||
use indexmap::IndexMap;
|
||||
use reqwest::header::HeaderMap;
|
||||
|
@ -32,7 +25,10 @@ use reqwest::RequestBuilder;
|
|||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::marker::PhantomData;
|
||||
use surrealdb_core::sql::Query;
|
||||
use surrealdb_core::sql::{
|
||||
from_value as from_core_value, statements::OutputStatement, Object as CoreObject, Param, Query,
|
||||
Statement, Value as CoreValue,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
|
@ -46,6 +42,11 @@ use tokio_util::compat::FuturesAsyncReadCompatExt;
|
|||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) mod native;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub(crate) mod wasm;
|
||||
|
||||
// const SQL_PATH: &str = "sql";
|
||||
const RPC_PATH: &str = "rpc";
|
||||
|
||||
|
@ -165,7 +166,7 @@ struct AuthResponse {
|
|||
type BackupSender = channel::Sender<Result<Vec<u8>>>;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn export_file(request: RequestBuilder, path: PathBuf) -> Result<Value> {
|
||||
async fn export_file(request: RequestBuilder, path: PathBuf) -> Result<()> {
|
||||
let mut response = request
|
||||
.send()
|
||||
.await?
|
||||
|
@ -193,10 +194,10 @@ async fn export_file(request: RequestBuilder, path: PathBuf) -> Result<Value> {
|
|||
.into());
|
||||
}
|
||||
|
||||
Ok(Value::None)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn export_bytes(request: RequestBuilder, bytes: BackupSender) -> Result<Value> {
|
||||
async fn export_bytes(request: RequestBuilder, bytes: BackupSender) -> Result<()> {
|
||||
let response = request.send().await?.error_for_status()?;
|
||||
|
||||
let future = async move {
|
||||
|
@ -214,11 +215,11 @@ async fn export_bytes(request: RequestBuilder, bytes: BackupSender) -> Result<Va
|
|||
#[cfg(target_arch = "wasm32")]
|
||||
spawn_local(future);
|
||||
|
||||
Ok(Value::None)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
async fn import(request: RequestBuilder, path: PathBuf) -> Result<Value> {
|
||||
async fn import(request: RequestBuilder, path: PathBuf) -> Result<()> {
|
||||
let file = match OpenOptions::new().read(true).open(&path).await {
|
||||
Ok(path) => path,
|
||||
Err(error) => {
|
||||
|
@ -248,15 +249,15 @@ async fn import(request: RequestBuilder, path: PathBuf) -> Result<Value> {
|
|||
}
|
||||
}
|
||||
}
|
||||
Ok(Value::None)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn health(request: RequestBuilder) -> Result<Value> {
|
||||
pub(crate) async fn health(request: RequestBuilder) -> Result<()> {
|
||||
request.send().await?.error_for_status()?;
|
||||
Ok(Value::None)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn process_req(
|
||||
async fn send_request(
|
||||
req: RouterRequest,
|
||||
base_url: &Url,
|
||||
client: &reqwest::Client,
|
||||
|
@ -269,34 +270,29 @@ async fn process_req(
|
|||
let response = http_req.send().await?.error_for_status()?;
|
||||
let bytes = response.bytes().await?;
|
||||
|
||||
let response: Response = deserialize(&mut &bytes[..], false)?;
|
||||
DbResponse::from(response.result)
|
||||
let response: Response = deserialize(&bytes, false)?;
|
||||
DbResponse::from_server_result(response.result)
|
||||
}
|
||||
|
||||
fn try_one(res: DbResponse, needed: bool) -> DbResponse {
|
||||
if !needed {
|
||||
return res;
|
||||
}
|
||||
fn flatten_dbresponse_array(res: DbResponse) -> DbResponse {
|
||||
match res {
|
||||
DbResponse::Other(Value::Array(arr)) if arr.len() == 1 => {
|
||||
DbResponse::Other(arr.into_iter().next().unwrap())
|
||||
DbResponse::Other(CoreValue::Array(array)) if array.len() == 1 => {
|
||||
let v = array.into_iter().next().unwrap();
|
||||
DbResponse::Other(v)
|
||||
}
|
||||
r => r,
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
|
||||
async fn router(
|
||||
RequestData {
|
||||
command,
|
||||
..
|
||||
}: RequestData,
|
||||
req: RequestData,
|
||||
base_url: &Url,
|
||||
client: &reqwest::Client,
|
||||
headers: &mut HeaderMap,
|
||||
vars: &mut IndexMap<String, Value>,
|
||||
vars: &mut IndexMap<String, CoreValue>,
|
||||
auth: &mut Option<Auth>,
|
||||
) -> Result<DbResponse> {
|
||||
match command {
|
||||
match req.command {
|
||||
Command::Query {
|
||||
query,
|
||||
mut variables,
|
||||
|
@ -308,40 +304,29 @@ async fn router(
|
|||
}
|
||||
.into_router_request(None)
|
||||
.expect("query should be valid request");
|
||||
process_req(req, base_url, client, headers, auth).await
|
||||
send_request(req, base_url, client, headers, auth).await
|
||||
}
|
||||
ref cmd @ Command::Use {
|
||||
ref namespace,
|
||||
ref database,
|
||||
Command::Use {
|
||||
namespace,
|
||||
database,
|
||||
} => {
|
||||
let req = cmd
|
||||
.clone()
|
||||
.into_router_request(None)
|
||||
.expect("use should be a valid router request");
|
||||
let req = Command::Use {
|
||||
namespace: namespace.clone(),
|
||||
database: database.clone(),
|
||||
}
|
||||
.into_router_request(None)
|
||||
.unwrap();
|
||||
// process request to check permissions
|
||||
let out = process_req(req, base_url, client, headers, auth).await?;
|
||||
match namespace {
|
||||
Some(ns) => match HeaderValue::try_from(ns) {
|
||||
Ok(ns) => {
|
||||
headers.insert(&NS, ns);
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(Error::InvalidNsName(ns.to_owned()).into());
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
let out = send_request(req, base_url, client, headers, auth).await?;
|
||||
if let Some(ns) = namespace {
|
||||
let value =
|
||||
HeaderValue::try_from(&ns).map_err(|_| Error::InvalidNsName(ns.to_owned()))?;
|
||||
headers.insert(&NS, value);
|
||||
};
|
||||
|
||||
match database {
|
||||
Some(db) => match HeaderValue::try_from(db) {
|
||||
Ok(db) => {
|
||||
headers.insert(&DB, db);
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(Error::InvalidDbName(db.to_owned()).into());
|
||||
}
|
||||
},
|
||||
None => {}
|
||||
if let Some(db) = database {
|
||||
let value =
|
||||
HeaderValue::try_from(&db).map_err(|_| Error::InvalidDbName(db.to_owned()))?;
|
||||
headers.insert(&DB, value);
|
||||
};
|
||||
|
||||
Ok(out)
|
||||
|
@ -356,9 +341,12 @@ async fn router(
|
|||
.expect("signin should be a valid router request");
|
||||
|
||||
let DbResponse::Other(value) =
|
||||
process_req(req, base_url, client, headers, auth).await?
|
||||
send_request(req, base_url, client, headers, auth).await?
|
||||
else {
|
||||
unreachable!("didn't make query")
|
||||
return Err(Error::InternalError(
|
||||
"recieved invalid result from server".to_string(),
|
||||
)
|
||||
.into());
|
||||
};
|
||||
|
||||
if let Ok(Credentials {
|
||||
|
@ -366,7 +354,7 @@ async fn router(
|
|||
pass,
|
||||
ns,
|
||||
db,
|
||||
}) = from_value(credentials.into())
|
||||
}) = from_core_value(credentials.into())
|
||||
{
|
||||
*auth = Some(Auth::Basic {
|
||||
user,
|
||||
|
@ -390,44 +378,50 @@ async fn router(
|
|||
}
|
||||
.into_router_request(None)
|
||||
.expect("authenticate should be a valid router request");
|
||||
process_req(req, base_url, client, headers, auth).await?;
|
||||
send_request(req, base_url, client, headers, auth).await?;
|
||||
|
||||
*auth = Some(Auth::Bearer {
|
||||
token,
|
||||
});
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::Invalidate => {
|
||||
*auth = None;
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::Set {
|
||||
key,
|
||||
value,
|
||||
} => {
|
||||
let query: Query = surrealdb_core::sql::parse(&format!("RETURN ${key};"))?;
|
||||
let mut output_stmt = OutputStatement::default();
|
||||
output_stmt.what = CoreValue::Param(Param::from(key.clone()));
|
||||
let query = Query::from(Statement::Output(output_stmt));
|
||||
let mut variables = CoreObject::default();
|
||||
variables.insert(key.clone(), value);
|
||||
let req = Command::Query {
|
||||
query,
|
||||
variables: [(key.clone(), value)].into(),
|
||||
variables,
|
||||
}
|
||||
.into_router_request(None)
|
||||
.expect("query is valid request");
|
||||
let DbResponse::Query(mut res) =
|
||||
process_req(req, base_url, client, headers, auth).await?
|
||||
send_request(req, base_url, client, headers, auth).await?
|
||||
else {
|
||||
unreachable!("made query request so response must be query")
|
||||
return Err(Error::InternalError(
|
||||
"recieved invalid result from server".to_string(),
|
||||
)
|
||||
.into());
|
||||
};
|
||||
|
||||
let val: Value = res.take(0)?;
|
||||
|
||||
vars.insert(key, val);
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
vars.insert(key, val.0);
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::Unset {
|
||||
key,
|
||||
} => {
|
||||
vars.shift_remove(&key);
|
||||
Ok(DbResponse::Other(Value::None))
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
Command::ExportFile {
|
||||
|
@ -456,8 +450,8 @@ async fn router(
|
|||
.headers(headers.clone())
|
||||
.auth(auth)
|
||||
.header(ACCEPT, "application/octet-stream");
|
||||
let value = export_file(request, path).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
export_file(request, path).await?;
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::ExportBytes {
|
||||
bytes,
|
||||
|
@ -468,8 +462,8 @@ async fn router(
|
|||
.headers(headers.clone())
|
||||
.auth(auth)
|
||||
.header(ACCEPT, "application/octet-stream");
|
||||
let value = export_bytes(request, bytes).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
export_bytes(request, bytes).await?;
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
Command::ExportMl {
|
||||
|
@ -483,8 +477,8 @@ async fn router(
|
|||
.headers(headers.clone())
|
||||
.auth(auth)
|
||||
.header(ACCEPT, "application/octet-stream");
|
||||
let value = export_file(request, path).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
export_file(request, path).await?;
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::ExportBytesMl {
|
||||
bytes,
|
||||
|
@ -497,8 +491,8 @@ async fn router(
|
|||
.headers(headers.clone())
|
||||
.auth(auth)
|
||||
.header(ACCEPT, "application/octet-stream");
|
||||
let value = export_bytes(request, bytes).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
export_bytes(request, bytes).await?;
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
Command::ImportFile {
|
||||
|
@ -510,8 +504,8 @@ async fn router(
|
|||
.headers(headers.clone())
|
||||
.auth(auth)
|
||||
.header(CONTENT_TYPE, "application/octet-stream");
|
||||
let value = import(request, path).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
import(request, path).await?;
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
Command::ImportMl {
|
||||
|
@ -523,19 +517,20 @@ async fn router(
|
|||
.headers(headers.clone())
|
||||
.auth(auth)
|
||||
.header(CONTENT_TYPE, "application/octet-stream");
|
||||
let value = import(request, path).await?;
|
||||
Ok(DbResponse::Other(value))
|
||||
import(request, path).await?;
|
||||
Ok(DbResponse::Other(CoreValue::None))
|
||||
}
|
||||
Command::SubscribeLive {
|
||||
..
|
||||
} => Err(Error::LiveQueriesNotSupported.into()),
|
||||
|
||||
cmd => {
|
||||
let one = cmd.needs_one();
|
||||
let req = cmd
|
||||
.into_router_request(None)
|
||||
.expect("all invalid variants should have been caught");
|
||||
process_req(req, base_url, client, headers, auth).await.map(|r| try_one(r, one))
|
||||
let needs_flatten = cmd.needs_flatten();
|
||||
let req = cmd.into_router_request(None).unwrap();
|
||||
let mut res = send_request(req, base_url, client, headers, auth).await?;
|
||||
if needs_flatten {
|
||||
res = flatten_dbresponse_array(res);
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,14 +8,8 @@ pub mod http;
|
|||
#[cfg_attr(docsrs, doc(cfg(feature = "protocol-ws")))]
|
||||
pub mod ws;
|
||||
|
||||
use crate::api;
|
||||
use crate::api::conn::DbResponse;
|
||||
use crate::api::err::Error;
|
||||
use crate::api::method::query::QueryResult;
|
||||
use crate::api::Result;
|
||||
use crate::dbs::Notification;
|
||||
use crate::dbs::QueryMethodResponse;
|
||||
use crate::dbs::Status;
|
||||
use crate::api::{self, conn::DbResponse, err::Error, method::query::QueryResult, Result};
|
||||
use crate::dbs::{self, Status};
|
||||
use crate::method::Stats;
|
||||
use indexmap::IndexMap;
|
||||
use revision::revisioned;
|
||||
|
@ -24,14 +18,15 @@ use rust_decimal::prelude::ToPrimitive;
|
|||
use rust_decimal::Decimal;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Deserialize;
|
||||
use std::io::Read;
|
||||
use std::time::Duration;
|
||||
use surrealdb_core::sql::Value;
|
||||
use surrealdb_core::sql::Value as CoreValue;
|
||||
|
||||
const NANOS_PER_SEC: i64 = 1_000_000_000;
|
||||
const NANOS_PER_MILLI: i64 = 1_000_000;
|
||||
const NANOS_PER_MICRO: i64 = 1_000;
|
||||
|
||||
pub struct WsNotification {}
|
||||
|
||||
// Converts a debug representation of `std::time::Duration` back
|
||||
fn duration_from_str(duration: &str) -> Option<std::time::Duration> {
|
||||
let nanos = if let Some(duration) = duration.strip_suffix("ns") {
|
||||
|
@ -93,9 +88,9 @@ pub(crate) struct Failure {
|
|||
#[revisioned(revision = 1)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(crate) enum Data {
|
||||
Other(Value),
|
||||
Query(Vec<QueryMethodResponse>),
|
||||
Live(Notification),
|
||||
Other(CoreValue),
|
||||
Query(Vec<dbs::QueryMethodResponse>),
|
||||
Live(dbs::Notification),
|
||||
}
|
||||
|
||||
type ServerResult = std::result::Result<Data, Failure>;
|
||||
|
@ -120,7 +115,7 @@ impl From<Failure> for crate::Error {
|
|||
}
|
||||
|
||||
impl DbResponse {
|
||||
fn from(result: ServerResult) -> Result<Self> {
|
||||
fn from_server_result(result: ServerResult) -> Result<Self> {
|
||||
match result.map_err(Error::from)? {
|
||||
Data::Other(value) => Ok(DbResponse::Other(value)),
|
||||
Data::Query(responses) => {
|
||||
|
@ -159,7 +154,7 @@ impl DbResponse {
|
|||
#[revisioned(revision = 1)]
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub(crate) struct Response {
|
||||
id: Option<Value>,
|
||||
id: Option<CoreValue>,
|
||||
pub(crate) result: ServerResult,
|
||||
}
|
||||
|
||||
|
@ -172,18 +167,16 @@ where
|
|||
value.serialize_revisioned(&mut buf).map_err(|error| crate::Error::Db(error.into()))?;
|
||||
return Ok(buf);
|
||||
}
|
||||
crate::sql::serde::serialize(value).map_err(|error| crate::Error::Db(error.into()))
|
||||
surrealdb_core::sql::serde::serialize(value).map_err(|error| crate::Error::Db(error.into()))
|
||||
}
|
||||
|
||||
fn deserialize<A, T>(bytes: &mut A, revisioned: bool) -> Result<T>
|
||||
fn deserialize<T>(bytes: &[u8], revisioned: bool) -> Result<T>
|
||||
where
|
||||
A: Read,
|
||||
T: Revisioned + DeserializeOwned,
|
||||
{
|
||||
if revisioned {
|
||||
return T::deserialize_revisioned(bytes).map_err(|x| crate::Error::Db(x.into()));
|
||||
let mut read = std::io::Cursor::new(bytes);
|
||||
return T::deserialize_revisioned(&mut read).map_err(|x| crate::Error::Db(x.into()));
|
||||
}
|
||||
let mut buf = Vec::new();
|
||||
bytes.read_to_end(&mut buf).map_err(crate::err::Error::Io)?;
|
||||
crate::sql::serde::deserialize(&buf).map_err(|error| crate::Error::Db(error.into()))
|
||||
surrealdb_core::sql::serde::deserialize(bytes).map_err(|error| crate::Error::Db(error.into()))
|
||||
}
|
||||
|
|
|
@ -11,13 +11,13 @@ use crate::api::Connect;
|
|||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::opt::IntoEndpoint;
|
||||
use crate::sql::Value;
|
||||
use crate::value::Notification;
|
||||
use channel::Sender;
|
||||
use indexmap::IndexMap;
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::time::Duration;
|
||||
use surrealdb_core::dbs::Notification as CoreNotification;
|
||||
use surrealdb_core::sql::Value as CoreValue;
|
||||
use trice::Instant;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -29,7 +29,7 @@ enum RequestEffect {
|
|||
/// Completing this request sets a variable to a give value.
|
||||
Set {
|
||||
key: String,
|
||||
value: Value,
|
||||
value: CoreValue,
|
||||
},
|
||||
/// Completing this request sets a variable to a give value.
|
||||
Clear {
|
||||
|
@ -59,11 +59,11 @@ struct PendingRequest {
|
|||
|
||||
struct RouterState<Sink, Stream> {
|
||||
/// Vars currently set by the set method,
|
||||
vars: IndexMap<String, Value>,
|
||||
vars: IndexMap<String, CoreValue>,
|
||||
/// Messages which aught to be replayed on a reconnect.
|
||||
replay: IndexMap<ReplayMethod, Command>,
|
||||
/// Pending live queries
|
||||
live_queries: HashMap<Uuid, channel::Sender<CoreNotification>>,
|
||||
live_queries: HashMap<Uuid, channel::Sender<Notification<CoreValue>>>,
|
||||
/// Send requests which are still awaiting an awnser.
|
||||
pending_requests: HashMap<i64, PendingRequest>,
|
||||
/// The last time a message was recieved from the server.
|
||||
|
|
|
@ -19,7 +19,7 @@ use crate::api::Surreal;
|
|||
use crate::engine::remote::Data;
|
||||
use crate::engine::IntervalStream;
|
||||
use crate::opt::WaitFor;
|
||||
use crate::sql::Value;
|
||||
use crate::{Action, Notification};
|
||||
use channel::Receiver;
|
||||
use futures::stream::{SplitSink, SplitStream};
|
||||
use futures::SinkExt;
|
||||
|
@ -31,6 +31,7 @@ use std::collections::HashSet;
|
|||
use std::sync::atomic::AtomicI64;
|
||||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
use surrealdb_core::sql::Value as CoreValue;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::sync::watch;
|
||||
use tokio::time;
|
||||
|
@ -193,7 +194,7 @@ async fn router_handle_route(
|
|||
ref notification_sender,
|
||||
} => {
|
||||
state.live_queries.insert(*uuid, notification_sender.clone());
|
||||
if response.clone().send(Ok(DbResponse::Other(Value::None))).await.is_err() {
|
||||
if response.clone().send(Ok(DbResponse::Other(CoreValue::None))).await.is_err() {
|
||||
trace!("Receiver dropped");
|
||||
}
|
||||
// There is nothing to send to the server here
|
||||
|
@ -278,27 +279,32 @@ async fn router_handle_response(
|
|||
// If `id` is set this is a normal response
|
||||
Some(id) => {
|
||||
if let Ok(id) = id.coerce_to_i64() {
|
||||
// We can only route responses with IDs
|
||||
if let Some(pending) = state.pending_requests.remove(&id) {
|
||||
let resp = match DbResponse::from_server_result(response.result) {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
let _ = pending.response_channel.send(Err(e)).await;
|
||||
return HandleResult::Ok;
|
||||
}
|
||||
};
|
||||
// We can only route responses with IDs
|
||||
match pending.effect {
|
||||
RequestEffect::None => {}
|
||||
RequestEffect::Insert => {
|
||||
// For insert, we need to flatten single responses in an array
|
||||
if let Ok(Data::Other(Value::Array(value))) =
|
||||
response.result
|
||||
{
|
||||
if value.len() == 1 {
|
||||
if let DbResponse::Other(CoreValue::Array(array)) = resp {
|
||||
if array.len() == 1 {
|
||||
let _ = pending
|
||||
.response_channel
|
||||
.send(DbResponse::from(Ok(Data::Other(
|
||||
value.into_iter().next().unwrap(),
|
||||
))))
|
||||
.send(Ok(DbResponse::Other(
|
||||
array.into_iter().next().unwrap(),
|
||||
)))
|
||||
.await;
|
||||
} else {
|
||||
let _ = pending
|
||||
.response_channel
|
||||
.send(DbResponse::from(Ok(Data::Other(
|
||||
Value::Array(value),
|
||||
.send(Ok(DbResponse::Other(CoreValue::Array(
|
||||
array,
|
||||
))))
|
||||
.await;
|
||||
}
|
||||
|
@ -317,10 +323,7 @@ async fn router_handle_response(
|
|||
state.vars.shift_remove(&key);
|
||||
}
|
||||
}
|
||||
let _res = pending
|
||||
.response_channel
|
||||
.send(DbResponse::from(response.result))
|
||||
.await;
|
||||
let _res = pending.response_channel.send(Ok(resp)).await;
|
||||
} else {
|
||||
warn!("got response for request with id '{id}', which was not in pending requests")
|
||||
}
|
||||
|
@ -334,11 +337,17 @@ async fn router_handle_response(
|
|||
// Check if this live query is registered
|
||||
if let Some(sender) = state.live_queries.get(&live_query_id) {
|
||||
// Send the notification back to the caller or kill live query if the receiver is already dropped
|
||||
|
||||
let notification = Notification {
|
||||
query_id: *notification.id,
|
||||
action: Action::from_core(notification.action),
|
||||
data: notification.result,
|
||||
};
|
||||
if sender.send(notification).await.is_err() {
|
||||
state.live_queries.remove(&live_query_id);
|
||||
let kill = {
|
||||
let request = Command::Kill {
|
||||
uuid: *live_query_id,
|
||||
uuid: live_query_id.0,
|
||||
}
|
||||
.into_router_request(None)
|
||||
.unwrap();
|
||||
|
@ -364,18 +373,18 @@ async fn router_handle_response(
|
|||
Err(error) => {
|
||||
#[revisioned(revision = 1)]
|
||||
#[derive(Deserialize)]
|
||||
struct Response {
|
||||
id: Option<Value>,
|
||||
struct ErrorResponse {
|
||||
id: Option<CoreValue>,
|
||||
}
|
||||
|
||||
// Let's try to find out the ID of the response that failed to deserialise
|
||||
if let Message::Binary(binary) = response {
|
||||
if let Ok(Response {
|
||||
if let Ok(ErrorResponse {
|
||||
id,
|
||||
}) = deserialize(&mut &binary[..], endpoint.supports_revision)
|
||||
}) = deserialize(&binary, endpoint.supports_revision)
|
||||
{
|
||||
// Return an error if an ID was returned
|
||||
if let Some(Ok(id)) = id.map(Value::coerce_to_i64) {
|
||||
if let Some(Ok(id)) = id.map(CoreValue::coerce_to_i64) {
|
||||
if let Some(pending) = state.pending_requests.remove(&id) {
|
||||
let _res = pending.response_channel.send(Err(error)).await;
|
||||
} else {
|
||||
|
@ -590,7 +599,7 @@ impl Response {
|
|||
Ok(None)
|
||||
}
|
||||
Message::Binary(binary) => {
|
||||
deserialize(&mut &binary[..], supports_revision).map(Some).map_err(|error| {
|
||||
deserialize(binary, supports_revision).map(Some).map_err(|error| {
|
||||
Error::ResponseFromBinary {
|
||||
binary: binary.clone(),
|
||||
error: bincode::ErrorKind::Custom(error.to_string()).into(),
|
||||
|
|
|
@ -17,7 +17,7 @@ use crate::api::Surreal;
|
|||
use crate::engine::remote::Data;
|
||||
use crate::engine::IntervalStream;
|
||||
use crate::opt::WaitFor;
|
||||
use crate::sql::Value;
|
||||
use crate::{Action, Notification};
|
||||
use channel::{Receiver, Sender};
|
||||
use futures::stream::{SplitSink, SplitStream};
|
||||
use futures::FutureExt;
|
||||
|
@ -36,6 +36,7 @@ use std::sync::atomic::AtomicI64;
|
|||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
use std::time::Duration;
|
||||
use surrealdb_core::sql::Value as CoreValue;
|
||||
use tokio::sync::watch;
|
||||
use trice::Instant;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
|
@ -137,7 +138,7 @@ async fn router_handle_request(
|
|||
ref notification_sender,
|
||||
} => {
|
||||
state.live_queries.insert(*uuid, notification_sender.clone());
|
||||
if response.send(Ok(DbResponse::Other(Value::None))).await.is_err() {
|
||||
if response.send(Ok(DbResponse::Other(CoreValue::None))).await.is_err() {
|
||||
trace!("Receiver dropped");
|
||||
}
|
||||
// There is nothing to send to the server here
|
||||
|
@ -225,22 +226,24 @@ async fn router_handle_response(
|
|||
RequestEffect::None => {}
|
||||
RequestEffect::Insert => {
|
||||
// For insert, we need to flatten single responses in an array
|
||||
if let Ok(Data::Other(Value::Array(value))) =
|
||||
if let Ok(Data::Other(CoreValue::Array(value))) =
|
||||
response.result
|
||||
{
|
||||
if value.len() == 1 {
|
||||
let _ = pending
|
||||
.response_channel
|
||||
.send(DbResponse::from(Ok(Data::Other(
|
||||
value.into_iter().next().unwrap(),
|
||||
))))
|
||||
.send(DbResponse::from_server_result(Ok(
|
||||
Data::Other(
|
||||
value.into_iter().next().unwrap(),
|
||||
),
|
||||
)))
|
||||
.await;
|
||||
} else {
|
||||
let _ = pending
|
||||
.response_channel
|
||||
.send(DbResponse::from(Ok(Data::Other(
|
||||
Value::Array(value),
|
||||
))))
|
||||
.send(DbResponse::from_server_result(Ok(
|
||||
Data::Other(CoreValue::Array(value)),
|
||||
)))
|
||||
.await;
|
||||
}
|
||||
return HandleResult::Ok;
|
||||
|
@ -260,7 +263,7 @@ async fn router_handle_response(
|
|||
}
|
||||
let _res = pending
|
||||
.response_channel
|
||||
.send(DbResponse::from(response.result))
|
||||
.send(DbResponse::from_server_result(response.result))
|
||||
.await;
|
||||
} else {
|
||||
warn!("got response for request with id '{id}', which was not in pending requests")
|
||||
|
@ -274,6 +277,12 @@ async fn router_handle_response(
|
|||
// Check if this live query is registered
|
||||
if let Some(sender) = state.live_queries.get(&live_query_id) {
|
||||
// Send the notification back to the caller or kill live query if the receiver is already dropped
|
||||
let notification = Notification {
|
||||
query_id: notification.id.0,
|
||||
action: Action::from_core(notification.action),
|
||||
data: notification.result,
|
||||
};
|
||||
|
||||
if sender.send(notification).await.is_err() {
|
||||
state.live_queries.remove(&live_query_id);
|
||||
let kill = {
|
||||
|
@ -304,7 +313,7 @@ async fn router_handle_response(
|
|||
#[derive(Deserialize)]
|
||||
#[revisioned(revision = 1)]
|
||||
struct Response {
|
||||
id: Option<Value>,
|
||||
id: Option<CoreValue>,
|
||||
}
|
||||
|
||||
// Let's try to find out the ID of the response that failed to deserialise
|
||||
|
@ -314,7 +323,7 @@ async fn router_handle_response(
|
|||
}) = deserialize(&mut &binary[..], endpoint.supports_revision)
|
||||
{
|
||||
// Return an error if an ID was returned
|
||||
if let Some(Ok(id)) = id.map(Value::coerce_to_i64) {
|
||||
if let Some(Ok(id)) = id.map(CoreValue::coerce_to_i64) {
|
||||
if let Some(req) = state.pending_requests.remove(&id) {
|
||||
let _res = req.response_channel.send(Err(error)).await;
|
||||
} else {
|
||||
|
@ -434,7 +443,7 @@ pub(crate) async fn run_router(
|
|||
let ping = {
|
||||
let mut request = BTreeMap::new();
|
||||
request.insert("method".to_owned(), "ping".into());
|
||||
let value = Value::from(request);
|
||||
let value = CoreValue::from(request);
|
||||
let value = serialize(&value, endpoint.supports_revision).unwrap();
|
||||
Message::Binary(value)
|
||||
};
|
||||
|
|
|
@ -1,18 +1,17 @@
|
|||
use crate::engine::IntervalStream;
|
||||
use crate::kvs::Datastore;
|
||||
use crate::options::EngineOptions;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use crate::Error as RootError;
|
||||
use futures::StreamExt;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::spawn as spawn_future;
|
||||
use surrealdb_core::{kvs::Datastore, options::EngineOptions};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::task::JoinHandle;
|
||||
use crate::Error as RootError;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::{spawn as spawn_future, task::JoinHandle};
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen_futures::spawn_local as spawn_future;
|
||||
|
||||
|
@ -38,7 +37,7 @@ impl Tasks {
|
|||
Err(e) => {
|
||||
error!("Node agent task failed: {}", e);
|
||||
let inner_err =
|
||||
crate::err::Error::NodeAgent("node task failed and has been logged");
|
||||
surrealdb_core::err::Error::NodeAgent("node task failed and has been logged");
|
||||
return Err(RootError::Db(inner_err));
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +64,7 @@ pub fn start_tasks(opt: &EngineOptions, dbs: Arc<Datastore>) -> (Tasks, [oneshot
|
|||
// This function needs to be called before after the dbs::init and before the net::init functions.
|
||||
// It needs to be before net::init because the net::init function blocks until the web server stops.
|
||||
fn init(opt: &EngineOptions, dbs: Arc<Datastore>) -> (FutureTask, oneshot::Sender<()>) {
|
||||
let _init = crate::dbs::LoggingLifecycle::new("node agent initialisation".to_string());
|
||||
let _init = surrealdb_core::dbs::LoggingLifecycle::new("node agent initialisation".to_string());
|
||||
let tick_interval = opt.tick_interval;
|
||||
|
||||
trace!("Ticker interval is {:?}", tick_interval);
|
||||
|
@ -78,7 +77,7 @@ fn init(opt: &EngineOptions, dbs: Arc<Datastore>) -> (FutureTask, oneshot::Sende
|
|||
let (tx, mut rx) = oneshot::channel();
|
||||
|
||||
let _fut = spawn_future(async move {
|
||||
let _lifecycle = crate::dbs::LoggingLifecycle::new("heartbeat task".to_string());
|
||||
let _lifecycle = surrealdb_core::dbs::LoggingLifecycle::new("heartbeat task".to_string());
|
||||
let mut ticker = interval_ticker(tick_interval).await;
|
||||
|
||||
loop {
|
||||
|
@ -125,10 +124,9 @@ async fn interval_ticker(interval: Duration) -> IntervalStream {
|
|||
#[cfg(feature = "kv-mem")]
|
||||
mod test {
|
||||
use crate::engine::tasks::start_tasks;
|
||||
use crate::kvs::Datastore;
|
||||
use crate::options::EngineOptions;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use surrealdb_core::{kvs::Datastore, options::EngineOptions};
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
pub async fn tasks_complete() {
|
||||
|
|
|
@ -1,12 +1,7 @@
|
|||
use crate::api::Response;
|
||||
use crate::sql::Array;
|
||||
use crate::sql::Edges;
|
||||
use crate::sql::Object;
|
||||
use crate::sql::Thing;
|
||||
use crate::sql::Value;
|
||||
use crate::{api::Response, Value};
|
||||
use serde::Serialize;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::{convert::Infallible, io};
|
||||
use surrealdb_core::dbs::capabilities::{ParseFuncTargetError, ParseNetTargetError};
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -43,26 +38,29 @@ pub enum Error {
|
|||
InvalidBindings(Value),
|
||||
|
||||
/// Tried to use a range query on a record ID
|
||||
#[error("Range on record IDs not supported: {0}")]
|
||||
RangeOnRecordId(Thing),
|
||||
#[error("Tried to add a range to an record-id resource")]
|
||||
RangeOnRecordId,
|
||||
|
||||
/// Tried to use a range query on an object
|
||||
#[error("Range on objects not supported: {0}")]
|
||||
RangeOnObject(Object),
|
||||
#[error("Tried to add a range to an object resource")]
|
||||
RangeOnObject,
|
||||
|
||||
/// Tried to use a range query on an array
|
||||
#[error("Range on arrays not supported: {0}")]
|
||||
RangeOnArray(Array),
|
||||
#[error("Tried to add a range to an array resource")]
|
||||
RangeOnArray,
|
||||
|
||||
/// Tried to use a range query on an edge or edges
|
||||
#[error("Range on edges not supported: {0}")]
|
||||
RangeOnEdges(Edges),
|
||||
#[error("Tried to add a range to an edge resource")]
|
||||
RangeOnEdges,
|
||||
|
||||
/// Tried to use a range query on an existing range
|
||||
#[error("Tried to add a range to an resource which was already a range")]
|
||||
RangeOnRange,
|
||||
|
||||
/// Tried to use `table:id` syntax as a method parameter when `(table, id)` should be used instead
|
||||
#[error("`{table}:{id}` is not allowed as a method parameter; try `({table}, {id})`")]
|
||||
#[error("Table name `{table}` contained a colon (:), this is dissallowed to avoid confusion with record-id's try `Table(\"{table}\")` instead.")]
|
||||
TableColonId {
|
||||
table: String,
|
||||
id: String,
|
||||
},
|
||||
|
||||
/// Duplicate request ID
|
||||
|
@ -172,16 +170,16 @@ pub enum Error {
|
|||
LiveQueriesNotSupported,
|
||||
|
||||
/// Tried to use a range query on an object
|
||||
#[error("Live queries on objects not supported: {0}")]
|
||||
LiveOnObject(Object),
|
||||
#[error("Live queries on objects not supported")]
|
||||
LiveOnObject,
|
||||
|
||||
/// Tried to use a range query on an array
|
||||
#[error("Live queries on arrays not supported: {0}")]
|
||||
LiveOnArray(Array),
|
||||
#[error("Live queries on arrays not supported")]
|
||||
LiveOnArray,
|
||||
|
||||
/// Tried to use a range query on an edge or edges
|
||||
#[error("Live queries on edges not supported: {0}")]
|
||||
LiveOnEdges(Edges),
|
||||
#[error("Live queries on edges not supported")]
|
||||
LiveOnEdges,
|
||||
|
||||
/// Tried to access a query statement as a live query when it isn't a live query
|
||||
#[error("Query statement {0} is not a live query")]
|
||||
|
@ -196,22 +194,64 @@ pub enum Error {
|
|||
ResponseAlreadyTaken,
|
||||
|
||||
/// Tried to insert on an object
|
||||
#[error("Insert queries on objects not supported: {0}")]
|
||||
InsertOnObject(Object),
|
||||
#[error("Insert queries on objects are not supported")]
|
||||
InsertOnObject,
|
||||
|
||||
/// Tried to insert on an array
|
||||
#[error("Insert queries on arrays not supported: {0}")]
|
||||
InsertOnArray(Array),
|
||||
#[error("Insert queries on arrays are not supported")]
|
||||
InsertOnArray,
|
||||
|
||||
/// Tried to insert on an edge or edges
|
||||
#[error("Insert queries on edges not supported: {0}")]
|
||||
InsertOnEdges(Edges),
|
||||
#[error("Insert queries on edges are not supported")]
|
||||
InsertOnEdges,
|
||||
|
||||
/// Tried to insert on an edge or edges
|
||||
#[error("Insert queries on ranges are not supported")]
|
||||
InsertOnRange,
|
||||
|
||||
#[error("{0}")]
|
||||
InvalidNetTarget(#[from] ParseNetTargetError),
|
||||
|
||||
#[error("{0}")]
|
||||
InvalidFuncTarget(#[from] ParseFuncTargetError),
|
||||
|
||||
#[error("failed to serialize Value: {0}")]
|
||||
SerializeValue(String),
|
||||
#[error("failed to deserialize Value: {0}")]
|
||||
DeSerializeValue(String),
|
||||
|
||||
#[error("failed to serialize to a Value: {0}")]
|
||||
Serializer(String),
|
||||
#[error("failed to deserialize from a Value: {0}")]
|
||||
Deserializer(String),
|
||||
|
||||
/// Tried to convert an value which contained something like for example a query or future.
|
||||
#[error("tried to convert from a value which contained non-primitive values to a value which only allows primitive values.")]
|
||||
RecievedInvalidValue,
|
||||
}
|
||||
|
||||
impl serde::ser::Error for Error {
|
||||
fn custom<T>(msg: T) -> Self
|
||||
where
|
||||
T: std::fmt::Display,
|
||||
{
|
||||
Error::SerializeValue(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::de::Error for Error {
|
||||
fn custom<T>(msg: T) -> Self
|
||||
where
|
||||
T: std::fmt::Display,
|
||||
{
|
||||
Error::DeSerializeValue(msg.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Infallible> for crate::Error {
|
||||
fn from(_: Infallible) -> Self {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseNetTargetError> for crate::Error {
|
||||
|
|
|
@ -4,9 +4,9 @@ use crate::api::method::Commit;
|
|||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::sql::statements::BeginStatement;
|
||||
use std::future::IntoFuture;
|
||||
use std::ops::Deref;
|
||||
use surrealdb_core::sql::statements::BeginStatement;
|
||||
|
||||
/// A beginning of a transaction
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -2,8 +2,8 @@ use crate::api::method::BoxFuture;
|
|||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::sql::statements::CancelStatement;
|
||||
use std::future::IntoFuture;
|
||||
use surrealdb_core::sql::statements::CancelStatement;
|
||||
|
||||
/// A transaction cancellation future
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -2,8 +2,8 @@ use crate::api::method::BoxFuture;
|
|||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::api::Surreal;
|
||||
use crate::sql::statements::CommitStatement;
|
||||
use std::future::IntoFuture;
|
||||
use surrealdb_core::sql::statements::CommitStatement;
|
||||
|
||||
/// A transaction commit future
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -3,8 +3,8 @@ use crate::api::method::BoxFuture;
|
|||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::method::OnceLockExt;
|
||||
use crate::sql::Value;
|
||||
use crate::Surreal;
|
||||
use crate::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::borrow::Cow;
|
||||
use std::future::IntoFuture;
|
||||
|
|
|
@ -4,14 +4,14 @@ use crate::api::opt::Resource;
|
|||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::method::OnceLockExt;
|
||||
use crate::sql::Value;
|
||||
use crate::Surreal;
|
||||
use crate::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::future::IntoFuture;
|
||||
use std::marker::PhantomData;
|
||||
use surrealdb_core::sql::to_value;
|
||||
use surrealdb_core::sql::{to_value as to_core_value, Value as CoreValue};
|
||||
|
||||
use super::Content;
|
||||
|
||||
|
@ -48,7 +48,7 @@ macro_rules! into_future {
|
|||
Box::pin(async move {
|
||||
let router = client.router.extract()?;
|
||||
let cmd = Command::Create {
|
||||
what: resource?.into(),
|
||||
what: resource?,
|
||||
data: None,
|
||||
};
|
||||
router.$method(cmd).await
|
||||
|
@ -99,15 +99,15 @@ where
|
|||
D: Serialize + 'static,
|
||||
{
|
||||
Content::from_closure(self.client, || {
|
||||
let content = to_value(data)?;
|
||||
let content = to_core_value(data)?;
|
||||
|
||||
let data = match content {
|
||||
Value::None | Value::Null => None,
|
||||
CoreValue::None | CoreValue::Null => None,
|
||||
content => Some(content),
|
||||
};
|
||||
|
||||
Ok(Command::Create {
|
||||
what: self.resource?.into(),
|
||||
what: self.resource?,
|
||||
data,
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
use crate::api::conn::Command;
|
||||
use crate::api::method::BoxFuture;
|
||||
use crate::api::opt::Range;
|
||||
use crate::api::opt::Resource;
|
||||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::method::OnceLockExt;
|
||||
use crate::sql::Id;
|
||||
use crate::sql::Value;
|
||||
use crate::opt::KeyRange;
|
||||
use crate::Surreal;
|
||||
use crate::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::borrow::Cow;
|
||||
use std::future::IntoFuture;
|
||||
|
@ -19,7 +18,6 @@ use std::marker::PhantomData;
|
|||
pub struct Delete<'r, C: Connection, R> {
|
||||
pub(super) client: Cow<'r, Surreal<C>>,
|
||||
pub(super) resource: Result<Resource>,
|
||||
pub(super) range: Option<Range<Id>>,
|
||||
pub(super) response_type: PhantomData<R>,
|
||||
}
|
||||
|
||||
|
@ -42,18 +40,13 @@ macro_rules! into_future {
|
|||
let Delete {
|
||||
client,
|
||||
resource,
|
||||
range,
|
||||
..
|
||||
} = self;
|
||||
Box::pin(async move {
|
||||
let param: Value = match range {
|
||||
Some(range) => resource?.with_range(range)?.into(),
|
||||
None => resource?.into(),
|
||||
};
|
||||
let router = client.router.extract()?;
|
||||
router
|
||||
.$method(Command::Delete {
|
||||
what: param,
|
||||
what: resource?,
|
||||
})
|
||||
.await
|
||||
})
|
||||
|
@ -98,8 +91,8 @@ where
|
|||
C: Connection,
|
||||
{
|
||||
/// Restricts a range of records to delete
|
||||
pub fn range(mut self, bounds: impl Into<Range<Id>>) -> Self {
|
||||
self.range = Some(bounds.into());
|
||||
pub fn range(mut self, range: impl Into<KeyRange>) -> Self {
|
||||
self.resource = self.resource.and_then(|x| x.with_range(range.into()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -109,8 +102,8 @@ where
|
|||
C: Connection,
|
||||
{
|
||||
/// Restricts a range of records to delete
|
||||
pub fn range(mut self, bounds: impl Into<Range<Id>>) -> Self {
|
||||
self.range = Some(bounds.into());
|
||||
pub fn range(mut self, range: impl Into<KeyRange>) -> Self {
|
||||
self.resource = self.resource.and_then(|x| x.with_range(range.into()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,14 @@ use crate::api::opt::Resource;
|
|||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::method::OnceLockExt;
|
||||
use crate::sql::Ident;
|
||||
use crate::sql::Part;
|
||||
use crate::sql::Table;
|
||||
use crate::sql::Value;
|
||||
use crate::Surreal;
|
||||
use crate::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::future::IntoFuture;
|
||||
use std::marker::PhantomData;
|
||||
use surrealdb_core::sql::{to_value as to_core_value, Object as CoreObject, Value as CoreValue};
|
||||
|
||||
/// An insert future
|
||||
#[derive(Debug)]
|
||||
|
@ -49,19 +47,25 @@ macro_rules! into_future {
|
|||
} = self;
|
||||
Box::pin(async move {
|
||||
let (table, data) = match resource? {
|
||||
Resource::Table(table) => (table.into(), Value::Object(Default::default())),
|
||||
Resource::Table(table) => (table.into(), CoreObject::default()),
|
||||
Resource::RecordId(record_id) => {
|
||||
let mut table = Table::default();
|
||||
table.0 = record_id.tb.clone();
|
||||
(table.into(), map! { String::from("id") => record_id.into() }.into())
|
||||
let record_id = record_id.into_inner();
|
||||
let mut map = CoreObject::default();
|
||||
map.insert("id".to_string(), record_id.id.into());
|
||||
(record_id.tb, map)
|
||||
}
|
||||
Resource::Object(obj) => return Err(Error::InsertOnObject(obj).into()),
|
||||
Resource::Array(arr) => return Err(Error::InsertOnArray(arr).into()),
|
||||
Resource::Edges(edges) => return Err(Error::InsertOnEdges(edges).into()),
|
||||
Resource::Object(_) => return Err(Error::InsertOnObject.into()),
|
||||
Resource::Array(_) => return Err(Error::InsertOnArray.into()),
|
||||
Resource::Edge {
|
||||
..
|
||||
} => return Err(Error::InsertOnEdges.into()),
|
||||
Resource::Range {
|
||||
..
|
||||
} => return Err(Error::InsertOnRange.into()),
|
||||
};
|
||||
let cmd = Command::Insert {
|
||||
what: Some(table),
|
||||
data,
|
||||
what: table.to_string(),
|
||||
data: data.into(),
|
||||
};
|
||||
|
||||
let router = client.router.extract()?;
|
||||
|
@ -114,10 +118,10 @@ where
|
|||
D: Serialize + 'static,
|
||||
{
|
||||
Content::from_closure(self.client, || {
|
||||
let mut data = crate::sql::to_value(data)?;
|
||||
let mut data = to_core_value(data)?;
|
||||
match self.resource? {
|
||||
Resource::Table(table) => Ok(Command::Insert {
|
||||
what: Some(table.into()),
|
||||
what: table,
|
||||
data,
|
||||
}),
|
||||
Resource::RecordId(thing) => {
|
||||
|
@ -127,22 +131,21 @@ where
|
|||
)
|
||||
.into())
|
||||
} else {
|
||||
let mut table = Table::default();
|
||||
table.0.clone_from(&thing.tb);
|
||||
let what = Value::Table(table);
|
||||
let mut ident = Ident::default();
|
||||
"id".clone_into(&mut ident.0);
|
||||
let id = Part::Field(ident);
|
||||
data.put(&[id], thing.into());
|
||||
let thing = thing.into_inner();
|
||||
if let CoreValue::Object(ref mut x) = data {
|
||||
x.insert("id".to_string(), thing.id.into());
|
||||
}
|
||||
|
||||
Ok(Command::Insert {
|
||||
what: Some(what),
|
||||
what: thing.tb,
|
||||
data,
|
||||
})
|
||||
}
|
||||
}
|
||||
Resource::Object(obj) => Err(Error::InsertOnObject(obj).into()),
|
||||
Resource::Array(arr) => Err(Error::InsertOnArray(arr).into()),
|
||||
Resource::Edges(edges) => Err(Error::InsertOnEdges(edges).into()),
|
||||
Resource::Object(_) => Err(Error::InsertOnObject.into()),
|
||||
Resource::Array(_) => Err(Error::InsertOnArray.into()),
|
||||
Resource::Edge(_) => Err(Error::InsertOnEdges.into()),
|
||||
Resource::Range(_) => Err(Error::InsertOnRange.into()),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -5,25 +5,15 @@ use crate::api::method::BoxFuture;
|
|||
use crate::api::Connection;
|
||||
use crate::api::ExtraFeatures;
|
||||
use crate::api::Result;
|
||||
use crate::dbs;
|
||||
use crate::engine::any::Any;
|
||||
use crate::method::Live;
|
||||
use crate::method::OnceLockExt;
|
||||
use crate::method::Query;
|
||||
use crate::method::Select;
|
||||
use crate::opt::Resource;
|
||||
use crate::sql::from_value;
|
||||
use crate::sql::statements::LiveStatement;
|
||||
use crate::sql::Field;
|
||||
use crate::sql::Fields;
|
||||
use crate::sql::Ident;
|
||||
use crate::sql::Idiom;
|
||||
use crate::sql::Part;
|
||||
use crate::sql::Statement;
|
||||
use crate::sql::Table;
|
||||
use crate::sql::Value;
|
||||
use crate::Notification;
|
||||
use crate::value::Notification;
|
||||
use crate::Surreal;
|
||||
use crate::Value;
|
||||
use channel::Receiver;
|
||||
use futures::StreamExt;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
@ -32,81 +22,91 @@ use std::marker::PhantomData;
|
|||
use std::pin::Pin;
|
||||
use std::task::Context;
|
||||
use std::task::Poll;
|
||||
use surrealdb_core::sql::{
|
||||
statements::LiveStatement, Cond, Expression, Field, Fields, Ident, Idiom, Operator, Part,
|
||||
Statement, Table, Value as CoreValue,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
use tokio::spawn;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen_futures::spawn_local as spawn;
|
||||
|
||||
const ID: &str = "id";
|
||||
|
||||
macro_rules! into_future {
|
||||
() => {
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
let Select {
|
||||
client,
|
||||
resource,
|
||||
range,
|
||||
..
|
||||
} = self;
|
||||
Box::pin(async move {
|
||||
let router = client.router.extract()?;
|
||||
if !router.features.contains(&ExtraFeatures::LiveQueries) {
|
||||
return Err(Error::LiveQueriesNotSupported.into());
|
||||
}
|
||||
let mut fields = Fields::default();
|
||||
fields.0 = vec![Field::All];
|
||||
let mut stmt = LiveStatement::new(fields);
|
||||
let mut table = Table::default();
|
||||
match range {
|
||||
Some(range) => {
|
||||
let record = resource?.with_range(range)?;
|
||||
table.0 = record.tb.clone();
|
||||
stmt.what = table.into();
|
||||
stmt.cond = record.to_cond();
|
||||
}
|
||||
None => match resource? {
|
||||
Resource::Table(table) => {
|
||||
stmt.what = table.into();
|
||||
}
|
||||
Resource::RecordId(record) => {
|
||||
table.0 = record.tb.clone();
|
||||
stmt.what = table.into();
|
||||
let mut ident = Ident::default();
|
||||
ident.0 = ID.to_owned();
|
||||
let mut idiom = Idiom::default();
|
||||
idiom.0 = vec![Part::from(ident)];
|
||||
stmt.cond = record.to_cond();
|
||||
}
|
||||
Resource::Object(object) => return Err(Error::LiveOnObject(object).into()),
|
||||
Resource::Array(array) => return Err(Error::LiveOnArray(array).into()),
|
||||
Resource::Edges(edges) => return Err(Error::LiveOnEdges(edges).into()),
|
||||
},
|
||||
}
|
||||
let query = Query::new(
|
||||
client.clone(),
|
||||
vec![Statement::Live(stmt)],
|
||||
Default::default(),
|
||||
false,
|
||||
);
|
||||
let Value::Uuid(id) = query.await?.take(0)? else {
|
||||
return Err(Error::InternalError(
|
||||
"successufull live query didn't return a uuid".to_string(),
|
||||
)
|
||||
.into());
|
||||
};
|
||||
let rx = register(router, id.0).await?;
|
||||
Ok(Stream::new(
|
||||
Surreal::new_from_router_waiter(client.router.clone(), client.waiter.clone()),
|
||||
id.0,
|
||||
Some(rx),
|
||||
))
|
||||
})
|
||||
fn into_future<C, O>(this: Select<C, O, Live>) -> BoxFuture<Result<Stream<O>>>
|
||||
where
|
||||
C: Connection,
|
||||
{
|
||||
let Select {
|
||||
client,
|
||||
resource,
|
||||
..
|
||||
} = this;
|
||||
Box::pin(async move {
|
||||
let router = client.router.extract()?;
|
||||
if !router.features.contains(&ExtraFeatures::LiveQueries) {
|
||||
return Err(Error::LiveQueriesNotSupported.into());
|
||||
}
|
||||
};
|
||||
let mut fields = Fields::default();
|
||||
fields.0 = vec![Field::All];
|
||||
let mut stmt = LiveStatement::new(fields);
|
||||
let mut table = Table::default();
|
||||
match resource? {
|
||||
Resource::Table(table) => {
|
||||
let mut core_table = Table::default();
|
||||
core_table.0 = table;
|
||||
stmt.what = core_table.into()
|
||||
}
|
||||
Resource::RecordId(record) => {
|
||||
let record = record.into_inner();
|
||||
table.0 = record.tb.clone();
|
||||
stmt.what = table.into();
|
||||
let mut ident = Ident::default();
|
||||
ident.0 = ID.to_owned();
|
||||
let mut idiom = Idiom::default();
|
||||
idiom.0 = vec![Part::from(ident)];
|
||||
let mut cond = Cond::default();
|
||||
cond.0 = surrealdb_core::sql::Value::Expression(Box::new(Expression::new(
|
||||
idiom.into(),
|
||||
Operator::Equal,
|
||||
record.into(),
|
||||
)));
|
||||
stmt.cond = Some(cond);
|
||||
}
|
||||
Resource::Object(_) => return Err(Error::LiveOnObject.into()),
|
||||
Resource::Array(_) => return Err(Error::LiveOnArray.into()),
|
||||
Resource::Edge(_) => return Err(Error::LiveOnEdges.into()),
|
||||
Resource::Range(range) => {
|
||||
let range = range.into_inner();
|
||||
table.0 = range.tb.clone();
|
||||
stmt.what = table.into();
|
||||
stmt.cond = range.to_cond();
|
||||
}
|
||||
}
|
||||
let query =
|
||||
Query::new(client.clone(), vec![Statement::Live(stmt)], Default::default(), false);
|
||||
let CoreValue::Uuid(id) = query.await?.take::<Value>(0)?.into_inner() else {
|
||||
return Err(Error::InternalError(
|
||||
"successufull live query didn't return a uuid".to_string(),
|
||||
)
|
||||
.into());
|
||||
};
|
||||
let rx = register(router, *id).await?;
|
||||
Ok(Stream::new(
|
||||
Surreal::new_from_router_waiter(client.router.clone(), client.waiter.clone()),
|
||||
*id,
|
||||
Some(rx),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) async fn register(router: &Router, id: Uuid) -> Result<Receiver<dbs::Notification>> {
|
||||
pub(crate) async fn register(
|
||||
router: &Router,
|
||||
id: Uuid,
|
||||
) -> Result<Receiver<Notification<CoreValue>>> {
|
||||
let (tx, rx) = channel::unbounded();
|
||||
router
|
||||
.execute_unit(Command::SubscribeLive {
|
||||
|
@ -124,7 +124,9 @@ where
|
|||
type Output = Result<Stream<Value>>;
|
||||
type IntoFuture = BoxFuture<'r, Self::Output>;
|
||||
|
||||
into_future! {}
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
into_future(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, Client, R> IntoFuture for Select<'r, Client, Option<R>, Live>
|
||||
|
@ -135,7 +137,9 @@ where
|
|||
type Output = Result<Stream<Option<R>>>;
|
||||
type IntoFuture = BoxFuture<'r, Self::Output>;
|
||||
|
||||
into_future! {}
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
into_future(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r, Client, R> IntoFuture for Select<'r, Client, Vec<R>, Live>
|
||||
|
@ -146,7 +150,9 @@ where
|
|||
type Output = Result<Stream<Vec<R>>>;
|
||||
type IntoFuture = BoxFuture<'r, Self::Output>;
|
||||
|
||||
into_future! {}
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
into_future(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A stream of live query notifications
|
||||
|
@ -157,7 +163,7 @@ pub struct Stream<R> {
|
|||
// We no longer need the lifetime and the type parameter
|
||||
// Leaving them in for backwards compatibility
|
||||
pub(crate) id: Uuid,
|
||||
pub(crate) rx: Option<Receiver<dbs::Notification>>,
|
||||
pub(crate) rx: Option<Receiver<Notification<CoreValue>>>,
|
||||
pub(crate) response_type: PhantomData<R>,
|
||||
}
|
||||
|
||||
|
@ -165,7 +171,7 @@ impl<R> Stream<R> {
|
|||
pub(crate) fn new(
|
||||
client: Surreal<Any>,
|
||||
id: Uuid,
|
||||
rx: Option<Receiver<dbs::Notification>>,
|
||||
rx: Option<Receiver<Notification<CoreValue>>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
|
@ -195,23 +201,22 @@ impl futures::Stream for Stream<Value> {
|
|||
type Item = Notification<Value>;
|
||||
|
||||
poll_next! {
|
||||
notification => Poll::Ready(Some(Notification {
|
||||
query_id: notification.id.0,
|
||||
action: notification.action.into(),
|
||||
data: notification.result,
|
||||
}))
|
||||
notification => {
|
||||
let r = Notification{
|
||||
query_id: notification.query_id,
|
||||
action: notification.action,
|
||||
data: Value::from_inner(notification.data),
|
||||
};
|
||||
Poll::Ready(Some(r))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! poll_next_and_convert {
|
||||
() => {
|
||||
poll_next! {
|
||||
notification => match from_value(notification.result) {
|
||||
Ok(data) => Poll::Ready(Some(Ok(Notification {
|
||||
data,
|
||||
query_id: notification.id.0,
|
||||
action: notification.action.into(),
|
||||
}))),
|
||||
notification => match notification.map_deserialize(){
|
||||
Ok(data) => Poll::Ready(Some(Ok(data))),
|
||||
Err(error) => Poll::Ready(Some(Err(error.into()))),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
use crate::api::conn::Command;
|
||||
use crate::api::method::BoxFuture;
|
||||
use crate::api::opt::Range;
|
||||
use crate::api::opt::Resource;
|
||||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::method::OnceLockExt;
|
||||
use crate::sql::to_value;
|
||||
use crate::sql::Id;
|
||||
use crate::sql::Value;
|
||||
use crate::value::Value;
|
||||
use crate::Surreal;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::future::IntoFuture;
|
||||
use std::marker::PhantomData;
|
||||
use surrealdb_core::sql::{to_value as to_core_value, Value as CoreValue};
|
||||
|
||||
/// A merge future
|
||||
#[derive(Debug)]
|
||||
|
@ -21,7 +19,6 @@ use std::marker::PhantomData;
|
|||
pub struct Merge<'r, C: Connection, D, R> {
|
||||
pub(super) client: Cow<'r, Surreal<C>>,
|
||||
pub(super) resource: Result<Resource>,
|
||||
pub(super) range: Option<Range<Id>>,
|
||||
pub(super) content: D,
|
||||
pub(super) response_type: PhantomData<R>,
|
||||
}
|
||||
|
@ -45,25 +42,19 @@ macro_rules! into_future {
|
|||
let Merge {
|
||||
client,
|
||||
resource,
|
||||
range,
|
||||
content,
|
||||
..
|
||||
} = self;
|
||||
let content = to_value(content);
|
||||
let content = to_core_value(content);
|
||||
Box::pin(async move {
|
||||
let param: Value = match range {
|
||||
Some(range) => resource?.with_range(range)?.into(),
|
||||
None => resource?.into(),
|
||||
};
|
||||
|
||||
let content = match content? {
|
||||
Value::None | Value::Null => None,
|
||||
CoreValue::None | CoreValue::Null => None,
|
||||
x => Some(x),
|
||||
};
|
||||
|
||||
let router = client.router.extract()?;
|
||||
let cmd = Command::Merge {
|
||||
what: param,
|
||||
what: resource?,
|
||||
data: content,
|
||||
};
|
||||
router.$method(cmd).await
|
||||
|
|
|
@ -11,8 +11,6 @@ use crate::api::OnceLockExt;
|
|||
use crate::api::Surreal;
|
||||
use crate::opt::IntoExportDestination;
|
||||
use crate::opt::WaitFor;
|
||||
use crate::sql::to_value;
|
||||
use run::IntoArgs;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::marker::PhantomData;
|
||||
|
@ -21,6 +19,7 @@ use std::pin::Pin;
|
|||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
use std::time::Duration;
|
||||
use surrealdb_core::sql::{to_value as to_core_value, Array as CoreArray};
|
||||
|
||||
pub(crate) mod live;
|
||||
pub(crate) mod query;
|
||||
|
@ -77,8 +76,8 @@ pub use merge::Merge;
|
|||
pub use patch::Patch;
|
||||
pub use query::Query;
|
||||
pub use query::QueryStream;
|
||||
use run::IntoFn;
|
||||
pub use run::Run;
|
||||
pub use run::{IntoArgs, IntoFn};
|
||||
pub use select::Select;
|
||||
use serde_content::Serializer;
|
||||
pub use set::Set;
|
||||
|
@ -92,6 +91,8 @@ pub use use_db::UseDb;
|
|||
pub use use_ns::UseNs;
|
||||
pub use version::Version;
|
||||
|
||||
use super::opt::IntoResource;
|
||||
|
||||
/// A alias for an often used type of future returned by async methods in this library.
|
||||
pub(crate) type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + Sync + 'a>>;
|
||||
|
||||
|
@ -329,7 +330,7 @@ where
|
|||
Set {
|
||||
client: Cow::Borrowed(self),
|
||||
key: key.into(),
|
||||
value: to_value(value).map_err(Into::into),
|
||||
value: to_core_value(value).map_err(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -692,11 +693,10 @@ where
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn select<R>(&self, resource: impl opt::IntoResource<R>) -> Select<C, R> {
|
||||
pub fn select<O>(&self, resource: impl IntoResource<O>) -> Select<C, O> {
|
||||
Select {
|
||||
client: Cow::Borrowed(self),
|
||||
resource: resource.into_resource(),
|
||||
range: None,
|
||||
response_type: PhantomData,
|
||||
query_type: PhantomData,
|
||||
}
|
||||
|
@ -748,7 +748,7 @@ where
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn create<R>(&self, resource: impl opt::IntoResource<R>) -> Create<C, R> {
|
||||
pub fn create<R>(&self, resource: impl IntoResource<R>) -> Create<C, R> {
|
||||
Create {
|
||||
client: Cow::Borrowed(self),
|
||||
resource: resource.into_resource(),
|
||||
|
@ -849,7 +849,7 @@ where
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn insert<R>(&self, resource: impl opt::IntoResource<R>) -> Insert<C, R> {
|
||||
pub fn insert<O>(&self, resource: impl IntoResource<O>) -> Insert<C, O> {
|
||||
Insert {
|
||||
client: Cow::Borrowed(self),
|
||||
resource: resource.into_resource(),
|
||||
|
@ -1007,11 +1007,10 @@ where
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn upsert<R>(&self, resource: impl opt::IntoResource<R>) -> Upsert<C, R> {
|
||||
pub fn upsert<O>(&self, resource: impl IntoResource<O>) -> Upsert<C, O> {
|
||||
Upsert {
|
||||
client: Cow::Borrowed(self),
|
||||
resource: resource.into_resource(),
|
||||
range: None,
|
||||
response_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -1166,11 +1165,10 @@ where
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn update<R>(&self, resource: impl opt::IntoResource<R>) -> Update<C, R> {
|
||||
pub fn update<O>(&self, resource: impl IntoResource<O>) -> Update<C, O> {
|
||||
Update {
|
||||
client: Cow::Borrowed(self),
|
||||
resource: resource.into_resource(),
|
||||
range: None,
|
||||
response_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -1199,11 +1197,10 @@ where
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn delete<R>(&self, resource: impl opt::IntoResource<R>) -> Delete<C, R> {
|
||||
pub fn delete<O>(&self, resource: impl IntoResource<O>) -> Delete<C, O> {
|
||||
Delete {
|
||||
client: Cow::Borrowed(self),
|
||||
resource: resource.into_resource(),
|
||||
range: None,
|
||||
response_type: PhantomData,
|
||||
}
|
||||
}
|
||||
|
@ -1226,14 +1223,18 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Re-enable doc tests
|
||||
/// Runs a function
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// ```ignore
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> surrealdb::Result<()> {
|
||||
/// # let db = surrealdb::engine::any::connect("mem://").await?;
|
||||
/// // Note that the sdk is currently undergoing some changes so the below examples might not
|
||||
/// work until the sdk is somewhat more stable.
|
||||
///
|
||||
/// // specify no args with an empty tuple, vec, or slice
|
||||
/// let foo = db.run("fn::foo", ()).await?; // fn::foo()
|
||||
/// // a single value will be turned into one arguement unless it is a tuple or vec
|
||||
|
@ -1251,11 +1252,13 @@ where
|
|||
///
|
||||
pub fn run(&self, name: impl IntoFn, args: impl IntoArgs) -> Run<C> {
|
||||
let (name, version) = name.into_fn();
|
||||
let mut arguments = CoreArray::default();
|
||||
arguments.0 = crate::Value::array_to_core(args.into_args());
|
||||
Run {
|
||||
client: Cow::Borrowed(self),
|
||||
name,
|
||||
version,
|
||||
args: args.into_args(),
|
||||
args: arguments,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
use crate::api::conn::Command;
|
||||
use crate::api::method::BoxFuture;
|
||||
use crate::api::opt::PatchOp;
|
||||
use crate::api::opt::Range;
|
||||
use crate::api::opt::Resource;
|
||||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::method::OnceLockExt;
|
||||
use crate::sql::to_value;
|
||||
use crate::sql::Id;
|
||||
use crate::sql::Value;
|
||||
use crate::Surreal;
|
||||
use crate::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_content::Value as Content;
|
||||
use std::borrow::Cow;
|
||||
use std::future::IntoFuture;
|
||||
use std::marker::PhantomData;
|
||||
use surrealdb_core::sql::{to_value as to_core_value, Value as CoreValue};
|
||||
|
||||
/// A patch future
|
||||
#[derive(Debug)]
|
||||
|
@ -22,7 +20,6 @@ use std::marker::PhantomData;
|
|||
pub struct Patch<'r, C: Connection, R> {
|
||||
pub(super) client: Cow<'r, Surreal<C>>,
|
||||
pub(super) resource: Result<Resource>,
|
||||
pub(super) range: Option<Range<Id>>,
|
||||
pub(super) patches: Vec<serde_content::Result<Content<'static>>>,
|
||||
pub(super) response_type: PhantomData<R>,
|
||||
}
|
||||
|
@ -46,25 +43,20 @@ macro_rules! into_future {
|
|||
let Patch {
|
||||
client,
|
||||
resource,
|
||||
range,
|
||||
patches,
|
||||
..
|
||||
} = self;
|
||||
Box::pin(async move {
|
||||
let param: Value = match range {
|
||||
Some(range) => resource?.with_range(range)?.into(),
|
||||
None => resource?.into(),
|
||||
};
|
||||
let mut vec = Vec::with_capacity(patches.len());
|
||||
for result in patches {
|
||||
let content = result.map_err(crate::error::Db::from)?;
|
||||
let value = to_value(content)?;
|
||||
let value = to_core_value(content)?;
|
||||
vec.push(value);
|
||||
}
|
||||
let patches = Value::from(vec);
|
||||
let patches = CoreValue::from(vec);
|
||||
let router = client.router.extract()?;
|
||||
let cmd = Command::Patch {
|
||||
what: param,
|
||||
what: resource?,
|
||||
data: Some(patches),
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use super::live;
|
||||
use super::Stream;
|
||||
|
||||
use super::{live, Stream};
|
||||
use crate::api::conn::Command;
|
||||
use crate::api::err::Error;
|
||||
use crate::api::method::BoxFuture;
|
||||
|
@ -12,12 +10,8 @@ use crate::engine::any::Any;
|
|||
use crate::method::OnceLockExt;
|
||||
use crate::method::Stats;
|
||||
use crate::method::WithStats;
|
||||
use crate::sql;
|
||||
use crate::sql::to_value;
|
||||
use crate::sql::Statement;
|
||||
use crate::sql::Value;
|
||||
use crate::Notification;
|
||||
use crate::Surreal;
|
||||
use crate::value::Notification;
|
||||
use crate::{Surreal, Value};
|
||||
use futures::future::Either;
|
||||
use futures::stream::SelectAll;
|
||||
use futures::StreamExt;
|
||||
|
@ -25,13 +19,14 @@ use indexmap::IndexMap;
|
|||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::future::IntoFuture;
|
||||
use std::mem;
|
||||
use std::pin::Pin;
|
||||
use std::task::Context;
|
||||
use std::task::Poll;
|
||||
use surrealdb_core::sql::{
|
||||
self, to_value as to_core_value, Object as CoreObject, Statement, Value as CoreValue,
|
||||
};
|
||||
|
||||
/// A query future
|
||||
#[derive(Debug)]
|
||||
|
@ -44,7 +39,7 @@ pub struct Query<'r, C: Connection> {
|
|||
pub(crate) struct ValidQuery<'r, C: Connection> {
|
||||
pub client: Cow<'r, Surreal<C>>,
|
||||
pub query: Vec<Statement>,
|
||||
pub bindings: BTreeMap<String, Value>,
|
||||
pub bindings: CoreObject,
|
||||
pub register_live_queries: bool,
|
||||
}
|
||||
|
||||
|
@ -55,7 +50,7 @@ where
|
|||
pub(crate) fn new(
|
||||
client: Cow<'r, Surreal<C>>,
|
||||
query: Vec<Statement>,
|
||||
bindings: BTreeMap<String, Value>,
|
||||
bindings: CoreObject,
|
||||
register_live_queries: bool,
|
||||
) -> Self {
|
||||
Query {
|
||||
|
@ -173,7 +168,7 @@ where
|
|||
// creating another public error variant for this internal error.
|
||||
let res = match result {
|
||||
Ok(id) => {
|
||||
let Value::Uuid(uuid) = id else {
|
||||
let CoreValue::Uuid(uuid) = id else {
|
||||
return Err(Error::InternalError(
|
||||
"successfull live query did not return a uuid".to_string(),
|
||||
)
|
||||
|
@ -278,17 +273,28 @@ where
|
|||
/// ```
|
||||
pub fn bind(self, bindings: impl Serialize + 'static) -> Self {
|
||||
self.map_valid(move |mut valid| {
|
||||
let mut bindings = to_value(bindings)?;
|
||||
if let Value::Array(array) = &mut bindings {
|
||||
if let [Value::Strand(key), value] = &mut array.0[..] {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert(mem::take(&mut key.0), mem::take(value));
|
||||
bindings = map.into();
|
||||
let bindings = to_core_value(bindings)?;
|
||||
match bindings {
|
||||
CoreValue::Object(mut map) => valid.bindings.append(&mut map.0),
|
||||
CoreValue::Array(array) => {
|
||||
if array.len() != 2 || !matches!(array[0], CoreValue::Strand(_)) {
|
||||
let bindings = CoreValue::Array(array);
|
||||
let bindings = Value::from_inner(bindings);
|
||||
return Err(Error::InvalidBindings(bindings).into());
|
||||
}
|
||||
|
||||
let mut iter = array.into_iter();
|
||||
let Some(CoreValue::Strand(key)) = iter.next() else {
|
||||
unreachable!()
|
||||
};
|
||||
let Some(value) = iter.next() else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
valid.bindings.0.insert(key.0, value);
|
||||
}
|
||||
}
|
||||
match &mut bindings {
|
||||
Value::Object(map) => valid.bindings.append(&mut map.0),
|
||||
_ => {
|
||||
let bindings = Value::from_inner(bindings);
|
||||
return Err(Error::InvalidBindings(bindings).into());
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +304,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) type QueryResult = Result<Value>;
|
||||
pub(crate) type QueryResult = Result<CoreValue>;
|
||||
|
||||
/// The response type of a `Surreal::query` request
|
||||
#[derive(Debug)]
|
||||
|
@ -419,7 +425,7 @@ impl Response {
|
|||
/// ```no_run
|
||||
/// use serde::Deserialize;
|
||||
/// use surrealdb::Notification;
|
||||
/// use surrealdb::sql::Value;
|
||||
/// use surrealdb::Value;
|
||||
///
|
||||
/// #[derive(Debug, Deserialize)]
|
||||
/// # #[allow(dead_code)]
|
||||
|
@ -464,7 +470,6 @@ impl Response {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use surrealdb::sql;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> surrealdb::Result<()> {
|
||||
|
@ -495,7 +500,6 @@ impl Response {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use surrealdb::sql;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> surrealdb::Result<()> {
|
||||
|
@ -526,7 +530,6 @@ impl Response {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use surrealdb::sql;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> surrealdb::Result<()> {
|
||||
|
@ -553,7 +556,6 @@ impl WithStats<Response> {
|
|||
///
|
||||
/// ```no_run
|
||||
/// use serde::Deserialize;
|
||||
/// use surrealdb::sql;
|
||||
///
|
||||
/// #[derive(Debug, Deserialize)]
|
||||
/// # #[allow(dead_code)]
|
||||
|
@ -623,7 +625,6 @@ impl WithStats<Response> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use surrealdb::sql;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> surrealdb::Result<()> {
|
||||
|
@ -654,7 +655,6 @@ impl WithStats<Response> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use surrealdb::sql;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> surrealdb::Result<()> {
|
||||
|
@ -674,7 +674,6 @@ impl WithStats<Response> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use surrealdb::sql;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> surrealdb::Result<()> {
|
||||
|
@ -698,8 +697,9 @@ impl WithStats<Response> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Error::Api;
|
||||
use crate::{value::to_value, Error::Api};
|
||||
use serde::Deserialize;
|
||||
use surrealdb_core::sql::Value as CoreValue;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct Summary {
|
||||
|
@ -728,7 +728,7 @@ mod tests {
|
|||
fn take_from_an_empty_response() {
|
||||
let mut response = Response::new();
|
||||
let value: Value = response.take(0).unwrap();
|
||||
assert!(value.is_none());
|
||||
assert!(value.into_inner().is_none());
|
||||
|
||||
let mut response = Response::new();
|
||||
let option: Option<String> = response.take(0).unwrap();
|
||||
|
@ -781,7 +781,7 @@ mod tests {
|
|||
..Response::new()
|
||||
};
|
||||
let value: Value = response.take(0).unwrap();
|
||||
assert_eq!(value, Value::from(scalar));
|
||||
assert_eq!(value.into_inner(), CoreValue::from(scalar));
|
||||
|
||||
let mut response = Response {
|
||||
results: to_map(vec![Ok(scalar.into())]),
|
||||
|
@ -794,7 +794,7 @@ mod tests {
|
|||
results: to_map(vec![Ok(scalar.into())]),
|
||||
..Response::new()
|
||||
};
|
||||
let vec: Vec<usize> = response.take(0).unwrap();
|
||||
let vec: Vec<i64> = response.take(0).unwrap();
|
||||
assert_eq!(vec, vec![scalar]);
|
||||
|
||||
let scalar = true;
|
||||
|
@ -804,7 +804,7 @@ mod tests {
|
|||
..Response::new()
|
||||
};
|
||||
let value: Value = response.take(0).unwrap();
|
||||
assert_eq!(value, Value::from(scalar));
|
||||
assert_eq!(value.into_inner(), CoreValue::from(scalar));
|
||||
|
||||
let mut response = Response {
|
||||
results: to_map(vec![Ok(scalar.into())]),
|
||||
|
@ -849,7 +849,7 @@ mod tests {
|
|||
};
|
||||
assert_eq!(zero, 0);
|
||||
let one: Value = response.take(1).unwrap();
|
||||
assert_eq!(one, Value::from(1));
|
||||
assert_eq!(one.into_inner(), CoreValue::from(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -860,14 +860,14 @@ mod tests {
|
|||
let value = to_value(summary.clone()).unwrap();
|
||||
|
||||
let mut response = Response {
|
||||
results: to_map(vec![Ok(value.clone())]),
|
||||
results: to_map(vec![Ok(value.clone().into_inner())]),
|
||||
..Response::new()
|
||||
};
|
||||
let title: Value = response.take("title").unwrap();
|
||||
assert_eq!(title, Value::from(summary.title.as_str()));
|
||||
assert_eq!(title.into_inner(), CoreValue::from(summary.title.as_str()));
|
||||
|
||||
let mut response = Response {
|
||||
results: to_map(vec![Ok(value.clone())]),
|
||||
results: to_map(vec![Ok(value.clone().into_inner())]),
|
||||
..Response::new()
|
||||
};
|
||||
let Some(title): Option<String> = response.take("title").unwrap() else {
|
||||
|
@ -876,7 +876,7 @@ mod tests {
|
|||
assert_eq!(title, summary.title);
|
||||
|
||||
let mut response = Response {
|
||||
results: to_map(vec![Ok(value)]),
|
||||
results: to_map(vec![Ok(value.into_inner())]),
|
||||
..Response::new()
|
||||
};
|
||||
let vec: Vec<String> = response.take("title").unwrap();
|
||||
|
@ -889,7 +889,7 @@ mod tests {
|
|||
let value = to_value(article.clone()).unwrap();
|
||||
|
||||
let mut response = Response {
|
||||
results: to_map(vec![Ok(value.clone())]),
|
||||
results: to_map(vec![Ok(value.clone().into_inner())]),
|
||||
..Response::new()
|
||||
};
|
||||
let Some(title): Option<String> = response.take("title").unwrap() else {
|
||||
|
@ -902,18 +902,18 @@ mod tests {
|
|||
assert_eq!(body, article.body);
|
||||
|
||||
let mut response = Response {
|
||||
results: to_map(vec![Ok(value.clone())]),
|
||||
results: to_map(vec![Ok(value.clone().into_inner())]),
|
||||
..Response::new()
|
||||
};
|
||||
let vec: Vec<String> = response.take("title").unwrap();
|
||||
assert_eq!(vec, vec![article.title.clone()]);
|
||||
|
||||
let mut response = Response {
|
||||
results: to_map(vec![Ok(value)]),
|
||||
results: to_map(vec![Ok(value.into_inner())]),
|
||||
..Response::new()
|
||||
};
|
||||
let value: Value = response.take("title").unwrap();
|
||||
assert_eq!(value, Value::from(article.title));
|
||||
assert_eq!(value.into_inner(), CoreValue::from(article.title));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -923,7 +923,7 @@ mod tests {
|
|||
..Response::new()
|
||||
};
|
||||
let value: Value = response.take(0).unwrap();
|
||||
assert_eq!(value, vec![Value::from(true), Value::from(false)].into());
|
||||
assert_eq!(value.into_inner(), vec![CoreValue::from(true), CoreValue::from(false)].into());
|
||||
|
||||
let mut response = Response {
|
||||
results: to_map(vec![Ok(vec![true, false].into())]),
|
||||
|
@ -1008,6 +1008,6 @@ mod tests {
|
|||
};
|
||||
assert_eq!(value, 2);
|
||||
let value: Value = response.take(4).unwrap();
|
||||
assert_eq!(value, Value::from(3));
|
||||
assert_eq!(value.into_inner(), CoreValue::from(3));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@ use crate::api::conn::Command;
|
|||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::method::OnceLockExt;
|
||||
use crate::sql::Array;
|
||||
use crate::sql::Value;
|
||||
use crate::Surreal;
|
||||
use crate::Value;
|
||||
use std::borrow::Cow;
|
||||
use std::future::Future;
|
||||
use std::future::IntoFuture;
|
||||
use std::pin::Pin;
|
||||
use surrealdb_core::sql::Array as CoreArray;
|
||||
|
||||
use super::BoxFuture;
|
||||
|
||||
/// A run future
|
||||
#[derive(Debug)]
|
||||
|
@ -17,7 +17,7 @@ pub struct Run<'r, C: Connection> {
|
|||
pub(super) client: Cow<'r, Surreal<C>>,
|
||||
pub(super) name: String,
|
||||
pub(super) version: Option<String>,
|
||||
pub(super) args: Array,
|
||||
pub(super) args: CoreArray,
|
||||
}
|
||||
impl<C> Run<'_, C>
|
||||
where
|
||||
|
@ -37,7 +37,7 @@ where
|
|||
Client: Connection,
|
||||
{
|
||||
type Output = Result<Value>;
|
||||
type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send + Sync + 'r>>;
|
||||
type IntoFuture = BoxFuture<'r, Self::Output>;
|
||||
|
||||
fn into_future(self) -> Self::IntoFuture {
|
||||
let Run {
|
||||
|
@ -60,19 +60,12 @@ where
|
|||
}
|
||||
|
||||
pub trait IntoArgs {
|
||||
fn into_args(self) -> Array;
|
||||
}
|
||||
|
||||
impl IntoArgs for Array {
|
||||
fn into_args(self) -> Array {
|
||||
self
|
||||
}
|
||||
fn into_args(self) -> Vec<Value>;
|
||||
}
|
||||
|
||||
impl IntoArgs for Value {
|
||||
fn into_args(self) -> Array {
|
||||
let arr: Vec<Value> = vec![self];
|
||||
Array::from(arr)
|
||||
fn into_args(self) -> Vec<Value> {
|
||||
vec![self]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,9 +73,8 @@ impl<T> IntoArgs for Vec<T>
|
|||
where
|
||||
T: Into<Value>,
|
||||
{
|
||||
fn into_args(self) -> Array {
|
||||
let arr: Vec<Value> = self.into_iter().map(Into::into).collect();
|
||||
Array::from(arr)
|
||||
fn into_args(self) -> Vec<Value> {
|
||||
self.into_iter().map(Into::into).collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,9 +82,8 @@ impl<T, const N: usize> IntoArgs for [T; N]
|
|||
where
|
||||
T: Into<Value>,
|
||||
{
|
||||
fn into_args(self) -> Array {
|
||||
let arr: Vec<Value> = self.into_iter().map(Into::into).collect();
|
||||
Array::from(arr)
|
||||
fn into_args(self) -> Vec<Value> {
|
||||
self.into_iter().map(Into::into).collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,9 +91,8 @@ impl<T, const N: usize> IntoArgs for &[T; N]
|
|||
where
|
||||
T: Into<Value> + Clone,
|
||||
{
|
||||
fn into_args(self) -> Array {
|
||||
let arr: Vec<Value> = self.iter().cloned().map(Into::into).collect();
|
||||
Array::from(arr)
|
||||
fn into_args(self) -> Vec<Value> {
|
||||
self.iter().cloned().map(Into::into).collect()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,56 +100,48 @@ impl<T> IntoArgs for &[T]
|
|||
where
|
||||
T: Into<Value> + Clone,
|
||||
{
|
||||
fn into_args(self) -> Array {
|
||||
let arr: Vec<Value> = self.iter().cloned().map(Into::into).collect();
|
||||
Array::from(arr)
|
||||
}
|
||||
}
|
||||
impl IntoArgs for () {
|
||||
fn into_args(self) -> Array {
|
||||
Vec::<Value>::new().into()
|
||||
fn into_args(self) -> Vec<Value> {
|
||||
self.iter().cloned().map(Into::into).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0> IntoArgs for (T0,)
|
||||
where
|
||||
T0: Into<Value>,
|
||||
{
|
||||
fn into_args(self) -> Array {
|
||||
let arr: Vec<Value> = vec![self.0.into()];
|
||||
Array::from(arr)
|
||||
macro_rules! impl_args_tuple {
|
||||
($($i:ident), *$(,)?) => {
|
||||
impl_args_tuple!(@marker $($i,)*);
|
||||
};
|
||||
($($cur:ident,)* @marker $head:ident, $($tail:ident,)*) => {
|
||||
impl<$($cur: Into<Value>,)*> IntoArgs for ($($cur,)*) {
|
||||
#[allow(non_snake_case)]
|
||||
fn into_args(self) -> Vec<Value> {
|
||||
let ($($cur,)*) = self;
|
||||
vec![$($cur.into(),)*]
|
||||
}
|
||||
}
|
||||
|
||||
impl_args_tuple!($($cur,)* $head, @marker $($tail,)*);
|
||||
};
|
||||
($($cur:ident,)* @marker ) => {
|
||||
impl<$($cur: Into<Value>,)*> IntoArgs for ($($cur,)*) {
|
||||
#[allow(non_snake_case)]
|
||||
fn into_args(self) -> Vec<Value> {
|
||||
let ($($cur,)*) = self;
|
||||
vec![$($cur.into(),)*]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0, T1> IntoArgs for (T0, T1)
|
||||
where
|
||||
T0: Into<Value>,
|
||||
T1: Into<Value>,
|
||||
{
|
||||
fn into_args(self) -> Array {
|
||||
let arr: Vec<Value> = vec![self.0.into(), self.1.into()];
|
||||
Array::from(arr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T0, T1, T2> IntoArgs for (T0, T1, T2)
|
||||
where
|
||||
T0: Into<Value>,
|
||||
T1: Into<Value>,
|
||||
T2: Into<Value>,
|
||||
{
|
||||
fn into_args(self) -> Array {
|
||||
let arr: Vec<Value> = vec![self.0.into(), self.1.into(), self.2.into()];
|
||||
Array::from(arr)
|
||||
}
|
||||
}
|
||||
impl_args_tuple!(A, B, C, D, E, F,);
|
||||
|
||||
/* TODO: Removed for now.
|
||||
* The detach value PR removed a lot of conversion methods with, pending later request which might
|
||||
* add them back depending on how the sdk turns out.
|
||||
*
|
||||
macro_rules! into_impl {
|
||||
($type:ty) => {
|
||||
impl IntoArgs for $type {
|
||||
fn into_args(self) -> Array {
|
||||
let val: Value = self.into();
|
||||
Array::from(val)
|
||||
fn into_args(self) -> Vec<Value> {
|
||||
vec![Value::from(self)]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -180,6 +162,7 @@ into_impl!(f32);
|
|||
into_impl!(f64);
|
||||
into_impl!(String);
|
||||
into_impl!(&str);
|
||||
*/
|
||||
|
||||
pub trait IntoFn {
|
||||
fn into_fn(self) -> (String, Option<String>);
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
use crate::api::conn::Command;
|
||||
use crate::api::method::BoxFuture;
|
||||
use crate::api::method::OnceLockExt;
|
||||
use crate::api::opt::Range;
|
||||
use crate::api::opt::Resource;
|
||||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::method::Live;
|
||||
use crate::sql::Id;
|
||||
use crate::sql::Value;
|
||||
use crate::opt::KeyRange;
|
||||
use crate::Surreal;
|
||||
use crate::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::borrow::Cow;
|
||||
use std::future::IntoFuture;
|
||||
|
@ -20,7 +19,6 @@ use std::marker::PhantomData;
|
|||
pub struct Select<'r, C: Connection, R, T = ()> {
|
||||
pub(super) client: Cow<'r, Surreal<C>>,
|
||||
pub(super) resource: Result<Resource>,
|
||||
pub(super) range: Option<Range<Id>>,
|
||||
pub(super) response_type: PhantomData<R>,
|
||||
pub(super) query_type: PhantomData<T>,
|
||||
}
|
||||
|
@ -44,18 +42,13 @@ macro_rules! into_future {
|
|||
let Select {
|
||||
client,
|
||||
resource,
|
||||
range,
|
||||
..
|
||||
} = self;
|
||||
Box::pin(async move {
|
||||
let param: Value = match range {
|
||||
Some(range) => resource?.with_range(range)?.into(),
|
||||
None => resource?.into(),
|
||||
};
|
||||
let router = client.router.extract()?;
|
||||
router
|
||||
.$method(Command::Select {
|
||||
what: param,
|
||||
what: resource?,
|
||||
})
|
||||
.await
|
||||
})
|
||||
|
@ -100,8 +93,8 @@ where
|
|||
C: Connection,
|
||||
{
|
||||
/// Restricts the records selected to those in the specified range
|
||||
pub fn range(mut self, bounds: impl Into<Range<Id>>) -> Self {
|
||||
self.range = Some(bounds.into());
|
||||
pub fn range(mut self, range: impl Into<KeyRange>) -> Self {
|
||||
self.resource = self.resource.and_then(|x| x.with_range(range.into()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -111,8 +104,8 @@ where
|
|||
C: Connection,
|
||||
{
|
||||
/// Restricts the records selected to those in the specified range
|
||||
pub fn range(mut self, bounds: impl Into<Range<Id>>) -> Self {
|
||||
self.range = Some(bounds.into());
|
||||
pub fn range(mut self, range: impl Into<KeyRange>) -> Self {
|
||||
self.resource = self.resource.and_then(|x| x.with_range(range.into()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +166,6 @@ where
|
|||
Select {
|
||||
client: self.client,
|
||||
resource: self.resource,
|
||||
range: self.range,
|
||||
response_type: self.response_type,
|
||||
query_type: PhantomData,
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ use crate::api::method::BoxFuture;
|
|||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::method::OnceLockExt;
|
||||
use crate::sql::Value;
|
||||
use crate::Surreal;
|
||||
use std::borrow::Cow;
|
||||
use std::future::IntoFuture;
|
||||
use surrealdb_core::sql::Value as CoreValue;
|
||||
|
||||
/// A set future
|
||||
#[derive(Debug)]
|
||||
|
@ -14,7 +14,7 @@ use std::future::IntoFuture;
|
|||
pub struct Set<'r, C: Connection> {
|
||||
pub(super) client: Cow<'r, Surreal<C>>,
|
||||
pub(super) key: String,
|
||||
pub(super) value: Result<Value>,
|
||||
pub(super) value: Result<CoreValue>,
|
||||
}
|
||||
|
||||
impl<C> Set<'_, C>
|
||||
|
|
|
@ -14,14 +14,13 @@ use crate::api::opt::auth::Root;
|
|||
use crate::api::opt::PatchOp;
|
||||
use crate::api::Response as QueryResponse;
|
||||
use crate::api::Surreal;
|
||||
use crate::sql::statements::BeginStatement;
|
||||
use crate::sql::statements::CommitStatement;
|
||||
use crate::Value;
|
||||
use once_cell::sync::Lazy;
|
||||
use protocol::Client;
|
||||
use protocol::Test;
|
||||
use semver::Version;
|
||||
use std::ops::Bound;
|
||||
use surrealdb_core::sql::Value;
|
||||
use surrealdb_core::sql::statements::{BeginStatement, CommitStatement};
|
||||
use types::User;
|
||||
use types::USER;
|
||||
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
use super::types::User;
|
||||
use crate::api::conn::Command;
|
||||
use crate::api::conn::DbResponse;
|
||||
use crate::api::conn::Route;
|
||||
use crate::api::conn::{Command, DbResponse, Route};
|
||||
use crate::api::Response as QueryResponse;
|
||||
use crate::sql::to_value;
|
||||
use crate::sql::Id;
|
||||
use crate::sql::Thing;
|
||||
use crate::sql::Value;
|
||||
use crate::opt::Resource;
|
||||
use channel::Receiver;
|
||||
use surrealdb_core::sql::{to_value as to_core_value, Value as CoreValue};
|
||||
|
||||
pub(super) fn mock(route_rx: Receiver<Route>) {
|
||||
tokio::spawn(async move {
|
||||
|
@ -19,7 +15,7 @@ pub(super) fn mock(route_rx: Receiver<Route>) {
|
|||
let cmd = request.command;
|
||||
|
||||
let result = match cmd {
|
||||
Command::Invalidate | Command::Health => Ok(DbResponse::Other(Value::None)),
|
||||
Command::Invalidate | Command::Health => Ok(DbResponse::Other(CoreValue::None)),
|
||||
Command::Authenticate {
|
||||
..
|
||||
}
|
||||
|
@ -28,14 +24,14 @@ pub(super) fn mock(route_rx: Receiver<Route>) {
|
|||
}
|
||||
| Command::Unset {
|
||||
..
|
||||
} => Ok(DbResponse::Other(Value::None)),
|
||||
} => Ok(DbResponse::Other(CoreValue::None)),
|
||||
Command::SubscribeLive {
|
||||
..
|
||||
} => Ok(DbResponse::Other("c6c0e36c-e2cf-42cb-b2d5-75415249b261".to_owned().into())),
|
||||
Command::Version => Ok(DbResponse::Other("1.0.0".into())),
|
||||
Command::Use {
|
||||
..
|
||||
} => Ok(DbResponse::Other(Value::None)),
|
||||
} => Ok(DbResponse::Other(CoreValue::None)),
|
||||
Command::Signup {
|
||||
..
|
||||
}
|
||||
|
@ -44,7 +40,7 @@ pub(super) fn mock(route_rx: Receiver<Route>) {
|
|||
} => Ok(DbResponse::Other("jwt".to_owned().into())),
|
||||
Command::Set {
|
||||
..
|
||||
} => Ok(DbResponse::Other(Value::None)),
|
||||
} => Ok(DbResponse::Other(CoreValue::None)),
|
||||
Command::Query {
|
||||
..
|
||||
} => Ok(DbResponse::Query(QueryResponse::new())),
|
||||
|
@ -52,7 +48,7 @@ pub(super) fn mock(route_rx: Receiver<Route>) {
|
|||
data,
|
||||
..
|
||||
} => match data {
|
||||
None => Ok(DbResponse::Other(to_value(User::default()).unwrap())),
|
||||
None => Ok(DbResponse::Other(to_core_value(User::default()).unwrap())),
|
||||
Some(user) => Ok(DbResponse::Other(user.clone())),
|
||||
},
|
||||
Command::Select {
|
||||
|
@ -63,13 +59,12 @@ pub(super) fn mock(route_rx: Receiver<Route>) {
|
|||
what,
|
||||
..
|
||||
} => match what {
|
||||
Value::Table(..)
|
||||
| Value::Array(..)
|
||||
| Value::Thing(Thing {
|
||||
id: Id::Range(_),
|
||||
..
|
||||
}) => Ok(DbResponse::Other(Value::Array(Default::default()))),
|
||||
Value::Thing(..) => Ok(DbResponse::Other(to_value(User::default()).unwrap())),
|
||||
Resource::Table(..) | Resource::Array(..) | Resource::Range(_) => {
|
||||
Ok(DbResponse::Other(CoreValue::Array(Default::default())))
|
||||
}
|
||||
Resource::RecordId(..) => {
|
||||
Ok(DbResponse::Other(to_core_value(User::default()).unwrap()))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Command::Upsert {
|
||||
|
@ -88,30 +83,26 @@ pub(super) fn mock(route_rx: Receiver<Route>) {
|
|||
what,
|
||||
..
|
||||
} => match what {
|
||||
Value::Table(..)
|
||||
| Value::Array(..)
|
||||
| Value::Thing(Thing {
|
||||
id: Id::Range(_),
|
||||
..
|
||||
}) => Ok(DbResponse::Other(Value::Array(Default::default()))),
|
||||
Value::Thing(..) => Ok(DbResponse::Other(to_value(User::default()).unwrap())),
|
||||
Resource::Table(..) | Resource::Array(..) | Resource::Range(..) => {
|
||||
Ok(DbResponse::Other(CoreValue::Array(Default::default())))
|
||||
}
|
||||
Resource::RecordId(..) => {
|
||||
Ok(DbResponse::Other(to_core_value(User::default()).unwrap()))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
Command::Insert {
|
||||
what,
|
||||
data,
|
||||
} => match (what, data) {
|
||||
(Some(Value::Table(..)), Value::Array(..)) => {
|
||||
Ok(DbResponse::Other(Value::Array(Default::default())))
|
||||
..
|
||||
} => match data {
|
||||
CoreValue::Array(..) => {
|
||||
Ok(DbResponse::Other(CoreValue::Array(Default::default())))
|
||||
}
|
||||
(Some(Value::Table(..)), _) => {
|
||||
Ok(DbResponse::Other(to_value(User::default()).unwrap()))
|
||||
}
|
||||
_ => unreachable!(),
|
||||
_ => Ok(DbResponse::Other(to_core_value(User::default()).unwrap())),
|
||||
},
|
||||
Command::Run {
|
||||
..
|
||||
} => Ok(DbResponse::Other(Value::None)),
|
||||
} => Ok(DbResponse::Other(CoreValue::None)),
|
||||
Command::ExportMl {
|
||||
..
|
||||
}
|
||||
|
@ -129,7 +120,7 @@ pub(super) fn mock(route_rx: Receiver<Route>) {
|
|||
}
|
||||
| Command::ImportFile {
|
||||
..
|
||||
} => Ok(DbResponse::Other(Value::None)),
|
||||
} => Ok(DbResponse::Other(CoreValue::None)),
|
||||
};
|
||||
|
||||
if let Err(message) = response.send(result).await {
|
||||
|
|
|
@ -4,20 +4,19 @@ use crate::api::method::Content;
|
|||
use crate::api::method::Merge;
|
||||
use crate::api::method::Patch;
|
||||
use crate::api::opt::PatchOp;
|
||||
use crate::api::opt::Range;
|
||||
use crate::api::opt::Resource;
|
||||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::method::OnceLockExt;
|
||||
use crate::sql::to_value;
|
||||
use crate::sql::Id;
|
||||
use crate::sql::Value;
|
||||
use crate::opt::KeyRange;
|
||||
use crate::Surreal;
|
||||
use crate::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::future::IntoFuture;
|
||||
use std::marker::PhantomData;
|
||||
use surrealdb_core::sql::{to_value as to_core_value, Value as CoreValue};
|
||||
|
||||
/// An update future
|
||||
#[derive(Debug)]
|
||||
|
@ -25,7 +24,6 @@ use std::marker::PhantomData;
|
|||
pub struct Update<'r, C: Connection, R> {
|
||||
pub(super) client: Cow<'r, Surreal<C>>,
|
||||
pub(super) resource: Result<Resource>,
|
||||
pub(super) range: Option<Range<Id>>,
|
||||
pub(super) response_type: PhantomData<R>,
|
||||
}
|
||||
|
||||
|
@ -48,18 +46,13 @@ macro_rules! into_future {
|
|||
let Update {
|
||||
client,
|
||||
resource,
|
||||
range,
|
||||
..
|
||||
} = self;
|
||||
Box::pin(async move {
|
||||
let param: Value = match range {
|
||||
Some(range) => resource?.with_range(range)?.into(),
|
||||
None => resource?.into(),
|
||||
};
|
||||
let router = client.router.extract()?;
|
||||
router
|
||||
.$method(Command::Update {
|
||||
what: param,
|
||||
what: resource?,
|
||||
data: None,
|
||||
})
|
||||
.await
|
||||
|
@ -105,8 +98,8 @@ where
|
|||
C: Connection,
|
||||
{
|
||||
/// Restricts the records to update to those in the specified range
|
||||
pub fn range(mut self, bounds: impl Into<Range<Id>>) -> Self {
|
||||
self.range = Some(bounds.into());
|
||||
pub fn range(mut self, range: impl Into<KeyRange>) -> Self {
|
||||
self.resource = self.resource.and_then(|x| x.with_range(range.into()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -116,8 +109,8 @@ where
|
|||
C: Connection,
|
||||
{
|
||||
/// Restricts the records to update to those in the specified range
|
||||
pub fn range(mut self, bounds: impl Into<Range<Id>>) -> Self {
|
||||
self.range = Some(bounds.into());
|
||||
pub fn range(mut self, range: impl Into<KeyRange>) -> Self {
|
||||
self.resource = self.resource.and_then(|x| x.with_range(range.into()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -133,15 +126,12 @@ where
|
|||
D: Serialize + 'static,
|
||||
{
|
||||
Content::from_closure(self.client, || {
|
||||
let data = to_value(data)?;
|
||||
let data = to_core_value(data)?;
|
||||
|
||||
let what: Value = match self.range {
|
||||
Some(range) => self.resource?.with_range(range)?.into(),
|
||||
None => self.resource?.into(),
|
||||
};
|
||||
let what = self.resource?;
|
||||
|
||||
let data = match data {
|
||||
Value::None | Value::Null => None,
|
||||
CoreValue::None => None,
|
||||
content => Some(content),
|
||||
};
|
||||
|
||||
|
@ -160,7 +150,6 @@ where
|
|||
Merge {
|
||||
client: self.client,
|
||||
resource: self.resource,
|
||||
range: self.range,
|
||||
content: data,
|
||||
response_type: PhantomData,
|
||||
}
|
||||
|
@ -171,7 +160,6 @@ where
|
|||
Patch {
|
||||
client: self.client,
|
||||
resource: self.resource,
|
||||
range: self.range,
|
||||
patches: vec![patch],
|
||||
response_type: PhantomData,
|
||||
}
|
||||
|
|
|
@ -4,20 +4,19 @@ use crate::api::method::Content;
|
|||
use crate::api::method::Merge;
|
||||
use crate::api::method::Patch;
|
||||
use crate::api::opt::PatchOp;
|
||||
use crate::api::opt::Range;
|
||||
use crate::api::opt::Resource;
|
||||
use crate::api::Connection;
|
||||
use crate::api::Result;
|
||||
use crate::method::OnceLockExt;
|
||||
use crate::sql::to_value;
|
||||
use crate::sql::Id;
|
||||
use crate::sql::Value;
|
||||
use crate::opt::KeyRange;
|
||||
use crate::Surreal;
|
||||
use crate::Value;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::future::IntoFuture;
|
||||
use std::marker::PhantomData;
|
||||
use surrealdb_core::sql::{to_value as to_core_value, Value as CoreValue};
|
||||
|
||||
/// An upsert future
|
||||
#[derive(Debug)]
|
||||
|
@ -25,7 +24,6 @@ use std::marker::PhantomData;
|
|||
pub struct Upsert<'r, C: Connection, R> {
|
||||
pub(super) client: Cow<'r, Surreal<C>>,
|
||||
pub(super) resource: Result<Resource>,
|
||||
pub(super) range: Option<Range<Id>>,
|
||||
pub(super) response_type: PhantomData<R>,
|
||||
}
|
||||
|
||||
|
@ -48,18 +46,13 @@ macro_rules! into_future {
|
|||
let Upsert {
|
||||
client,
|
||||
resource,
|
||||
range,
|
||||
..
|
||||
} = self;
|
||||
Box::pin(async move {
|
||||
let param: Value = match range {
|
||||
Some(range) => resource?.with_range(range)?.into(),
|
||||
None => resource?.into(),
|
||||
};
|
||||
let router = client.router.extract()?;
|
||||
router
|
||||
.$method(Command::Upsert {
|
||||
what: param,
|
||||
what: resource?,
|
||||
data: None,
|
||||
})
|
||||
.await
|
||||
|
@ -105,8 +98,8 @@ where
|
|||
C: Connection,
|
||||
{
|
||||
/// Restricts the records to upsert to those in the specified range
|
||||
pub fn range(mut self, bounds: impl Into<Range<Id>>) -> Self {
|
||||
self.range = Some(bounds.into());
|
||||
pub fn range(mut self, range: impl Into<KeyRange>) -> Self {
|
||||
self.resource = self.resource.and_then(|x| x.with_range(range.into()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -116,8 +109,8 @@ where
|
|||
C: Connection,
|
||||
{
|
||||
/// Restricts the records to upsert to those in the specified range
|
||||
pub fn range(mut self, bounds: impl Into<Range<Id>>) -> Self {
|
||||
self.range = Some(bounds.into());
|
||||
pub fn range(mut self, range: impl Into<KeyRange>) -> Self {
|
||||
self.resource = self.resource.and_then(|x| x.with_range(range.into()));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
@ -133,20 +126,15 @@ where
|
|||
D: Serialize + 'static,
|
||||
{
|
||||
Content::from_closure(self.client, || {
|
||||
let data = to_value(data)?;
|
||||
|
||||
let what: Value = match self.range {
|
||||
Some(range) => self.resource?.with_range(range)?.into(),
|
||||
None => self.resource?.into(),
|
||||
};
|
||||
let data = to_core_value(data)?;
|
||||
|
||||
let data = match data {
|
||||
Value::None | Value::Null => None,
|
||||
CoreValue::None => None,
|
||||
content => Some(content),
|
||||
};
|
||||
|
||||
Ok(Command::Upsert {
|
||||
what,
|
||||
what: self.resource?,
|
||||
data,
|
||||
})
|
||||
})
|
||||
|
@ -160,7 +148,6 @@ where
|
|||
Merge {
|
||||
client: self.client,
|
||||
resource: self.resource,
|
||||
range: self.range,
|
||||
content: data,
|
||||
response_type: PhantomData,
|
||||
}
|
||||
|
@ -171,7 +158,6 @@ where
|
|||
Patch {
|
||||
client: self.client,
|
||||
resource: self.resource,
|
||||
range: self.range,
|
||||
patches: vec![patch],
|
||||
response_type: PhantomData,
|
||||
}
|
||||
|
|
|
@ -37,9 +37,12 @@ where
|
|||
fn into_future(self) -> Self::IntoFuture {
|
||||
Box::pin(async move {
|
||||
let router = self.client.router.extract()?;
|
||||
let version = router.execute_value(Command::Version).await?.convert_to_string()?;
|
||||
let version = router.execute_value(Command::Version).await?;
|
||||
let version = version.into_inner().to_raw_string();
|
||||
let semantic = version.trim_start_matches("surrealdb-");
|
||||
semantic.parse().map_err(|_| Error::InvalidSemanticVersion(semantic.to_string()).into())
|
||||
semantic
|
||||
.parse()
|
||||
.map_err(|_| Error::InvalidSemanticVersion(format!("\"{version}\"")).into())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,23 +1,8 @@
|
|||
//! Functionality for connecting to local and remote databases
|
||||
|
||||
pub mod engine;
|
||||
pub mod err;
|
||||
#[cfg(feature = "protocol-http")]
|
||||
pub mod headers;
|
||||
pub mod method;
|
||||
pub mod opt;
|
||||
|
||||
mod conn;
|
||||
|
||||
pub use method::query::Response;
|
||||
use method::BoxFuture;
|
||||
use semver::Version;
|
||||
use tokio::sync::watch;
|
||||
|
||||
use crate::api::conn::Router;
|
||||
use crate::api::err::Error;
|
||||
use crate::api::opt::Endpoint;
|
||||
use semver::BuildMetadata;
|
||||
use semver::Version;
|
||||
use semver::VersionReq;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
|
@ -25,10 +10,120 @@ use std::future::IntoFuture;
|
|||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
use std::sync::OnceLock;
|
||||
use tokio::sync::watch;
|
||||
|
||||
macro_rules! transparent_wrapper{
|
||||
(
|
||||
$(#[$m:meta])*
|
||||
$vis:vis struct $name:ident($field_vis:vis $inner:ty)
|
||||
) => {
|
||||
$(#[$m])*
|
||||
#[repr(transparent)]
|
||||
$vis struct $name($field_vis $inner);
|
||||
|
||||
impl $name{
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code)]
|
||||
pub fn from_inner(inner: $inner) -> Self{
|
||||
$name(inner)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code)]
|
||||
pub fn from_inner_ref(inner: &$inner) -> &Self{
|
||||
unsafe{
|
||||
std::mem::transmute::<&$inner,&$name>(inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code)]
|
||||
pub fn from_inner_mut(inner: &mut $inner) -> &mut Self{
|
||||
unsafe{
|
||||
std::mem::transmute::<&mut $inner,&mut $name>(inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[allow(dead_code)]
|
||||
pub fn into_inner(self) -> $inner{
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for $name{
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result{
|
||||
self.0.fmt(fmt)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Debug for $name{
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result{
|
||||
self.0.fmt(fmt)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_serialize_wrapper {
|
||||
($ty:ty) => {
|
||||
impl ::revision::Revisioned for $ty {
|
||||
fn revision() -> u16 {
|
||||
CoreValue::revision()
|
||||
}
|
||||
|
||||
fn serialize_revisioned<W: std::io::Write>(
|
||||
&self,
|
||||
w: &mut W,
|
||||
) -> Result<(), revision::Error> {
|
||||
self.0.serialize_revisioned(w)
|
||||
}
|
||||
|
||||
fn deserialize_revisioned<R: std::io::Read>(r: &mut R) -> Result<Self, revision::Error>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
::revision::Revisioned::deserialize_revisioned(r).map(Self::from_inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::serde::Serialize for $ty {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ::serde::ser::Serializer,
|
||||
{
|
||||
self.0.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> ::serde::de::Deserialize<'de> for $ty {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: ::serde::de::Deserializer<'de>,
|
||||
{
|
||||
Ok(Self::from_inner(::serde::de::Deserialize::deserialize(deserializer)?))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub mod engine;
|
||||
pub mod err;
|
||||
#[cfg(feature = "protocol-http")]
|
||||
pub mod headers;
|
||||
pub mod method;
|
||||
pub mod opt;
|
||||
pub mod value;
|
||||
|
||||
mod conn;
|
||||
|
||||
use self::conn::Router;
|
||||
use self::err::Error;
|
||||
use self::opt::Endpoint;
|
||||
use self::opt::EndpointKind;
|
||||
use self::opt::WaitFor;
|
||||
|
||||
pub use method::query::Response;
|
||||
|
||||
/// A specialized `Result` type
|
||||
pub type Result<T> = std::result::Result<T, crate::Error>;
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
//! The different options and types for use in API functions
|
||||
use crate::sql::Thing;
|
||||
use dmp::Diff;
|
||||
use serde::Serialize;
|
||||
|
||||
|
@ -23,9 +22,6 @@ use serde_content::Value as Content;
|
|||
#[cfg(any(feature = "native-tls", feature = "rustls"))]
|
||||
pub use tls::*;
|
||||
|
||||
/// Record ID
|
||||
pub type RecordId = Thing;
|
||||
|
||||
type UnitOp<'a> = InnerOp<'a, ()>;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
use crate::api::{err::Error, Response as QueryResponse, Result};
|
||||
use crate::method;
|
||||
use crate::method::{Stats, Stream};
|
||||
use crate::sql::from_value;
|
||||
use crate::sql::{self, statements::*, Statement, Statements, Value};
|
||||
use crate::{syn, Notification};
|
||||
use crate::{
|
||||
api::{err::Error, Response as QueryResponse, Result},
|
||||
method::{self, Stats, Stream},
|
||||
value::Notification,
|
||||
Value,
|
||||
};
|
||||
use futures::future::Either;
|
||||
use futures::stream::select_all;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use surrealdb_core::{
|
||||
sql::{
|
||||
self, from_value as from_core_value, statements::*, Statement, Statements,
|
||||
Value as CoreValue,
|
||||
},
|
||||
syn,
|
||||
};
|
||||
|
||||
/// A trait for converting inputs into SQL statements
|
||||
pub trait IntoQuery {
|
||||
|
@ -195,8 +202,8 @@ where
|
|||
impl QueryResult<Value> for usize {
|
||||
fn query_result(self, response: &mut QueryResponse) -> Result<Value> {
|
||||
match response.results.swap_remove(&self) {
|
||||
Some((_, result)) => Ok(result?),
|
||||
None => Ok(Value::None),
|
||||
Some((_, result)) => Ok(Value::from_inner(result?)),
|
||||
None => Ok(Value::from_inner(CoreValue::None)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,11 +231,11 @@ where
|
|||
}
|
||||
};
|
||||
let result = match value {
|
||||
Value::Array(vec) => match &mut vec.0[..] {
|
||||
CoreValue::Array(vec) => match &mut vec.0[..] {
|
||||
[] => Ok(None),
|
||||
[value] => {
|
||||
let value = mem::take(value);
|
||||
from_value(value).map_err(Into::into)
|
||||
from_core_value(value).map_err(Into::into)
|
||||
}
|
||||
_ => Err(Error::LossyTake(QueryResponse {
|
||||
results: mem::take(&mut response.results),
|
||||
|
@ -239,7 +246,7 @@ where
|
|||
},
|
||||
_ => {
|
||||
let value = mem::take(value);
|
||||
from_value(value).map_err(Into::into)
|
||||
from_core_value(value).map_err(Into::into)
|
||||
}
|
||||
};
|
||||
response.results.swap_remove(&self);
|
||||
|
@ -264,16 +271,16 @@ impl QueryResult<Value> for (usize, &str) {
|
|||
}
|
||||
},
|
||||
None => {
|
||||
return Ok(Value::None);
|
||||
return Ok(Value::from_inner(CoreValue::None));
|
||||
}
|
||||
};
|
||||
|
||||
let value = match value {
|
||||
Value::Object(object) => object.remove(key).unwrap_or_default(),
|
||||
_ => Value::None,
|
||||
CoreValue::Object(object) => object.remove(key).unwrap_or_default(),
|
||||
_ => CoreValue::None,
|
||||
};
|
||||
|
||||
Ok(value)
|
||||
Ok(Value::from_inner(value))
|
||||
}
|
||||
|
||||
fn stats(&self, response: &QueryResponse) -> Option<Stats> {
|
||||
|
@ -301,7 +308,7 @@ where
|
|||
}
|
||||
};
|
||||
let value = match value {
|
||||
Value::Array(vec) => match &mut vec.0[..] {
|
||||
CoreValue::Array(vec) => match &mut vec.0[..] {
|
||||
[] => {
|
||||
response.results.swap_remove(&index);
|
||||
return Ok(None);
|
||||
|
@ -319,11 +326,11 @@ where
|
|||
value => value,
|
||||
};
|
||||
match value {
|
||||
Value::None | Value::Null => {
|
||||
CoreValue::None => {
|
||||
response.results.swap_remove(&index);
|
||||
Ok(None)
|
||||
}
|
||||
Value::Object(object) => {
|
||||
CoreValue::Object(object) => {
|
||||
if object.is_empty() {
|
||||
response.results.swap_remove(&index);
|
||||
return Ok(None);
|
||||
|
@ -331,7 +338,7 @@ where
|
|||
let Some(value) = object.remove(key) else {
|
||||
return Ok(None);
|
||||
};
|
||||
from_value(value).map_err(Into::into)
|
||||
from_core_value(value).map_err(Into::into)
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
|
@ -349,14 +356,14 @@ where
|
|||
fn query_result(self, response: &mut QueryResponse) -> Result<Vec<T>> {
|
||||
let vec = match response.results.swap_remove(&self) {
|
||||
Some((_, result)) => match result? {
|
||||
Value::Array(vec) => vec.0,
|
||||
CoreValue::Array(vec) => vec.0,
|
||||
vec => vec![vec],
|
||||
},
|
||||
None => {
|
||||
return Ok(vec![]);
|
||||
}
|
||||
};
|
||||
from_value(vec.into()).map_err(Into::into)
|
||||
from_core_value(vec.into()).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn stats(&self, response: &QueryResponse) -> Option<Stats> {
|
||||
|
@ -373,7 +380,7 @@ where
|
|||
let mut response = match response.results.get_mut(&index) {
|
||||
Some((_, result)) => match result {
|
||||
Ok(val) => match val {
|
||||
Value::Array(vec) => mem::take(&mut vec.0),
|
||||
CoreValue::Array(vec) => mem::take(&mut vec.0),
|
||||
val => {
|
||||
let val = mem::take(val);
|
||||
vec![val]
|
||||
|
@ -391,13 +398,13 @@ where
|
|||
};
|
||||
let mut vec = Vec::with_capacity(response.len());
|
||||
for value in response.iter_mut() {
|
||||
if let Value::Object(object) = value {
|
||||
if let CoreValue::Object(object) = value {
|
||||
if let Some(value) = object.remove(key) {
|
||||
vec.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
from_value(vec.into()).map_err(Into::into)
|
||||
from_core_value(vec.into()).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn stats(&self, response: &QueryResponse) -> Option<Stats> {
|
||||
|
|
|
@ -1,58 +1,115 @@
|
|||
use crate::api::{err::Error, Result};
|
||||
use crate::sql::{self, Array, Edges, Id, Object, Table, Thing, Value};
|
||||
use crate::syn;
|
||||
use crate::{
|
||||
api::{err::Error, Result},
|
||||
Object, RecordId, RecordIdKey, Value,
|
||||
};
|
||||
use std::ops::{self, Bound};
|
||||
use surrealdb_core::sql::{
|
||||
Edges as CoreEdges, IdRange as CoreIdRange, Table as CoreTable, Thing as CoreThing,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "protocol-ws", feature = "protocol-http"))]
|
||||
use surrealdb_core::sql::Value as CoreValue;
|
||||
|
||||
/// A wrapper type to assert that you ment to use a string as a table name.
|
||||
///
|
||||
/// To prevent some possible errors, by defauit [`IntoResource`] does not allow `:` in table names
|
||||
/// as this might be an indication that the user might have intended to use a record id instead.
|
||||
/// If you wrap your table name string in this tupe the [`IntoResource`] trait will accept any
|
||||
/// table names.
|
||||
#[derive(Debug)]
|
||||
pub struct Table<T>(pub T);
|
||||
|
||||
impl<T> Table<T>
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
pub(crate) fn into_core(self) -> CoreTable {
|
||||
let mut t = CoreTable::default();
|
||||
t.0 = self.0.into();
|
||||
t
|
||||
}
|
||||
|
||||
/// Add a range of keys to the table.
|
||||
pub fn with_range<R>(self, range: R) -> QueryRange
|
||||
where
|
||||
KeyRange: From<R>,
|
||||
{
|
||||
let range = KeyRange::from(range);
|
||||
let res = CoreIdRange {
|
||||
beg: range.start.map(RecordIdKey::into_inner),
|
||||
end: range.end.map(RecordIdKey::into_inner),
|
||||
};
|
||||
let res = CoreThing::from((self.0.into(), res));
|
||||
QueryRange(res)
|
||||
}
|
||||
}
|
||||
|
||||
transparent_wrapper!(
|
||||
/// A table range.
|
||||
#[derive( Clone, PartialEq)]
|
||||
pub struct QueryRange(CoreThing)
|
||||
);
|
||||
|
||||
transparent_wrapper!(
|
||||
/// A query edge
|
||||
#[derive( Clone, PartialEq)]
|
||||
pub struct Edge(CoreEdges)
|
||||
);
|
||||
|
||||
/// A database resource
|
||||
#[derive(Debug)]
|
||||
///
|
||||
/// A resource is a location, or a range of locations, from which data can be fetched.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[non_exhaustive]
|
||||
pub enum Resource {
|
||||
/// Table name
|
||||
Table(Table),
|
||||
Table(String),
|
||||
/// Record ID
|
||||
RecordId(Thing),
|
||||
RecordId(RecordId),
|
||||
/// An object
|
||||
Object(Object),
|
||||
/// An array
|
||||
Array(Array),
|
||||
Array(Vec<Value>),
|
||||
/// Edges
|
||||
Edges(Edges),
|
||||
Edge(Edge),
|
||||
/// A range of id's on a table.
|
||||
Range(QueryRange),
|
||||
}
|
||||
|
||||
impl Resource {
|
||||
pub(crate) fn with_range(self, range: Range<Id>) -> Result<sql::Thing> {
|
||||
/// Add a range to the resource, this only works if the resource is a table.
|
||||
pub fn with_range(self, range: KeyRange) -> Result<Self> {
|
||||
match self {
|
||||
Resource::Table(table) => Ok(sql::Thing::from((
|
||||
table.0,
|
||||
sql::Id::Range(Box::new(sql::IdRange::try_from((range.start, range.end))?)),
|
||||
))),
|
||||
Resource::RecordId(record_id) => Err(Error::RangeOnRecordId(record_id).into()),
|
||||
Resource::Object(object) => Err(Error::RangeOnObject(object).into()),
|
||||
Resource::Array(array) => Err(Error::RangeOnArray(array).into()),
|
||||
Resource::Edges(edges) => Err(Error::RangeOnEdges(edges).into()),
|
||||
Resource::Table(table) => Ok(Resource::Range(Table(table).with_range(range))),
|
||||
Resource::RecordId(_) => Err(Error::RangeOnRecordId.into()),
|
||||
Resource::Object(_) => Err(Error::RangeOnObject.into()),
|
||||
Resource::Array(_) => Err(Error::RangeOnArray.into()),
|
||||
Resource::Edge(_) => Err(Error::RangeOnEdges.into()),
|
||||
Resource::Range(_) => Err(Error::RangeOnRange.into()),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "protocol-ws", feature = "protocol-http"))]
|
||||
pub(crate) fn into_core_value(self) -> CoreValue {
|
||||
match self {
|
||||
Resource::Table(x) => Table(x).into_core().into(),
|
||||
Resource::RecordId(x) => x.into_inner().into(),
|
||||
Resource::Object(x) => x.into_inner().into(),
|
||||
Resource::Array(x) => Value::array_to_core(x).into(),
|
||||
Resource::Edge(x) => x.into_inner().into(),
|
||||
Resource::Range(x) => x.into_inner().into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Table> for Resource {
|
||||
fn from(table: Table) -> Self {
|
||||
Self::Table(table)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Table> for Resource {
|
||||
fn from(table: &Table) -> Self {
|
||||
Self::Table(table.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Thing> for Resource {
|
||||
fn from(thing: Thing) -> Self {
|
||||
impl From<RecordId> for Resource {
|
||||
fn from(thing: RecordId) -> Self {
|
||||
Self::RecordId(thing)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Thing> for Resource {
|
||||
fn from(thing: &Thing) -> Self {
|
||||
impl From<&RecordId> for Resource {
|
||||
fn from(thing: &RecordId) -> Self {
|
||||
Self::RecordId(thing.clone())
|
||||
}
|
||||
}
|
||||
|
@ -69,36 +126,21 @@ impl From<&Object> for Resource {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Array> for Resource {
|
||||
fn from(array: Array) -> Self {
|
||||
impl From<Vec<Value>> for Resource {
|
||||
fn from(array: Vec<Value>) -> Self {
|
||||
Self::Array(array)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Array> for Resource {
|
||||
fn from(array: &Array) -> Self {
|
||||
Self::Array(array.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Edges> for Resource {
|
||||
fn from(edges: Edges) -> Self {
|
||||
Self::Edges(edges)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Edges> for Resource {
|
||||
fn from(edges: &Edges) -> Self {
|
||||
Self::Edges(edges.clone())
|
||||
impl From<&[Value]> for Resource {
|
||||
fn from(array: &[Value]) -> Self {
|
||||
Self::Array(array.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Resource {
|
||||
fn from(s: &str) -> Self {
|
||||
match syn::thing(s) {
|
||||
Ok(thing) => Self::RecordId(thing),
|
||||
Err(_) => Self::Table(s.into()),
|
||||
}
|
||||
Resource::from(s.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,146 +152,43 @@ impl From<&String> for Resource {
|
|||
|
||||
impl From<String> for Resource {
|
||||
fn from(s: String) -> Self {
|
||||
match syn::thing(s.as_str()) {
|
||||
Ok(thing) => Self::RecordId(thing),
|
||||
Err(_) => Self::Table(s.into()),
|
||||
}
|
||||
Resource::Table(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Edge> for Resource {
|
||||
fn from(value: Edge) -> Self {
|
||||
Resource::Edge(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<QueryRange> for Resource {
|
||||
fn from(value: QueryRange) -> Self {
|
||||
Resource::Range(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, I> From<(T, I)> for Resource
|
||||
where
|
||||
T: Into<String>,
|
||||
I: Into<Id>,
|
||||
I: Into<RecordIdKey>,
|
||||
{
|
||||
fn from((table, id): (T, I)) -> Self {
|
||||
let record_id = (table.into(), id.into());
|
||||
Self::RecordId(record_id.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Resource> for Value {
|
||||
fn from(resource: Resource) -> Self {
|
||||
match resource {
|
||||
Resource::Table(resource) => resource.into(),
|
||||
Resource::RecordId(resource) => resource.into(),
|
||||
Resource::Object(resource) => resource.into(),
|
||||
Resource::Array(resource) => resource.into(),
|
||||
Resource::Edges(resource) => resource.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for converting inputs into database resources
|
||||
pub trait IntoResource<Response>: Sized {
|
||||
/// Converts an input into a database resource
|
||||
fn into_resource(self) -> Result<Resource>;
|
||||
}
|
||||
|
||||
impl IntoResource<Value> for Resource {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Option<R>> for Object {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(Resource::Object(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Option<R>> for Thing {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(Resource::RecordId(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Option<R>> for &Thing {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(Resource::RecordId(self.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, T, I> IntoResource<Option<R>> for (T, I)
|
||||
where
|
||||
T: Into<String>,
|
||||
I: Into<Id>,
|
||||
{
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
let (table, id) = self;
|
||||
let record_id = (table.into(), id.into());
|
||||
Ok(Resource::RecordId(record_id.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Vec<R>> for Array {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(Resource::Array(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Vec<R>> for Edges {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(Resource::Edges(self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Vec<R>> for Table {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(Resource::Table(self))
|
||||
}
|
||||
}
|
||||
|
||||
fn blacklist_colon(input: &str) -> Result<()> {
|
||||
match input.contains(':') {
|
||||
true => {
|
||||
// We already know this string contains a colon
|
||||
let (table, id) = input.split_once(':').unwrap();
|
||||
Err(Error::TableColonId {
|
||||
table: table.to_owned(),
|
||||
id: id.to_owned(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
false => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Vec<R>> for &str {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
blacklist_colon(self)?;
|
||||
let mut table = Table::default();
|
||||
self.clone_into(&mut table.0);
|
||||
Ok(Resource::Table(table))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Vec<R>> for &String {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
blacklist_colon(self)?;
|
||||
IntoResource::<Vec<R>>::into_resource(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Vec<R>> for String {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
blacklist_colon(&self)?;
|
||||
let mut table = Table::default();
|
||||
table.0 = self;
|
||||
Ok(Resource::Table(table))
|
||||
let record_id = RecordId::from_table_key(table, id);
|
||||
Self::RecordId(record_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds the `start` and `end` bounds of a range query
|
||||
#[derive(Debug)]
|
||||
pub struct Range<T> {
|
||||
pub(crate) start: Bound<T>,
|
||||
pub(crate) end: Bound<T>,
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct KeyRange {
|
||||
pub(crate) start: Bound<RecordIdKey>,
|
||||
pub(crate) end: Bound<RecordIdKey>,
|
||||
}
|
||||
|
||||
impl<T> From<(Bound<T>, Bound<T>)> for Range<Id>
|
||||
impl<T> From<(Bound<T>, Bound<T>)> for KeyRange
|
||||
where
|
||||
T: Into<Id>,
|
||||
T: Into<RecordIdKey>,
|
||||
{
|
||||
fn from((start, end): (Bound<T>, Bound<T>)) -> Self {
|
||||
Self {
|
||||
|
@ -267,9 +206,9 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<ops::Range<T>> for Range<Id>
|
||||
impl<T> From<ops::Range<T>> for KeyRange
|
||||
where
|
||||
T: Into<Id>,
|
||||
T: Into<RecordIdKey>,
|
||||
{
|
||||
fn from(
|
||||
ops::Range {
|
||||
|
@ -284,9 +223,9 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<ops::RangeInclusive<T>> for Range<Id>
|
||||
impl<T> From<ops::RangeInclusive<T>> for KeyRange
|
||||
where
|
||||
T: Into<Id>,
|
||||
T: Into<RecordIdKey>,
|
||||
{
|
||||
fn from(range: ops::RangeInclusive<T>) -> Self {
|
||||
let (start, end) = range.into_inner();
|
||||
|
@ -297,9 +236,9 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<ops::RangeFrom<T>> for Range<Id>
|
||||
impl<T> From<ops::RangeFrom<T>> for KeyRange
|
||||
where
|
||||
T: Into<Id>,
|
||||
T: Into<RecordIdKey>,
|
||||
{
|
||||
fn from(
|
||||
ops::RangeFrom {
|
||||
|
@ -313,9 +252,9 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<ops::RangeTo<T>> for Range<Id>
|
||||
impl<T> From<ops::RangeTo<T>> for KeyRange
|
||||
where
|
||||
T: Into<Id>,
|
||||
T: Into<RecordIdKey>,
|
||||
{
|
||||
fn from(
|
||||
ops::RangeTo {
|
||||
|
@ -329,9 +268,9 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<ops::RangeToInclusive<T>> for Range<Id>
|
||||
impl<T> From<ops::RangeToInclusive<T>> for KeyRange
|
||||
where
|
||||
T: Into<Id>,
|
||||
T: Into<RecordIdKey>,
|
||||
{
|
||||
fn from(
|
||||
ops::RangeToInclusive {
|
||||
|
@ -345,7 +284,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ops::RangeFull> for Range<Id> {
|
||||
impl From<ops::RangeFull> for KeyRange {
|
||||
fn from(_: ops::RangeFull) -> Self {
|
||||
Self {
|
||||
start: Bound::Unbounded,
|
||||
|
@ -353,3 +292,101 @@ impl From<ops::RangeFull> for Range<Id> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for types which can be used as a resource selection for a query.
|
||||
pub trait IntoResource<Output> {
|
||||
fn into_resource(self) -> Result<Resource>;
|
||||
}
|
||||
|
||||
fn no_colon(a: &str) -> Result<()> {
|
||||
if a.contains(':') {
|
||||
return Err(Error::TableColonId {
|
||||
table: a.to_string(),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl IntoResource<Value> for Resource {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Option<R>> for Object {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Option<R>> for RecordId {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Option<R>> for &RecordId {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(self.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, T, I> IntoResource<Option<R>> for (T, I)
|
||||
where
|
||||
T: Into<String>,
|
||||
I: Into<RecordIdKey>,
|
||||
{
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Vec<R>> for Vec<Value> {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Vec<R>> for Edge {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Vec<R>> for QueryRange {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R> IntoResource<Vec<R>> for Table<T>
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
let t = self.0.into();
|
||||
Ok(t.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Vec<R>> for &str {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
no_colon(self)?;
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Vec<R>> for String {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
no_colon(&self)?;
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> IntoResource<Vec<R>> for &String {
|
||||
fn into_resource(self) -> Result<Resource> {
|
||||
no_colon(self)?;
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
|
421
lib/src/api/value/core.rs
Normal file
421
lib/src/api/value/core.rs
Normal file
|
@ -0,0 +1,421 @@
|
|||
use crate::{
|
||||
opt::{Edge, KeyRange, QueryRange},
|
||||
Bytes, Datetime, Number, Object, RecordId, RecordIdKey, Uuid, Value,
|
||||
};
|
||||
use std::{collections::BTreeMap, ops::Bound};
|
||||
use surrealdb_core::{
|
||||
dbs::{Action as CoreAction, Notification as CoreNotification},
|
||||
sql::{
|
||||
Array as CoreArray, Bytes as CoreBytes, Datetime as CoreDatetime, Duration as CoreDuration,
|
||||
Edges as CoreEdges, Id, Number as CoreNumber, Object as CoreObject, Range as CoreRange,
|
||||
Strand, Table as CoreTable, Tables as CoreTables, Thing, Uuid as CoreUuid,
|
||||
Value as CoreValue,
|
||||
},
|
||||
};
|
||||
use trice::Duration;
|
||||
|
||||
use super::{Action, Notification};
|
||||
|
||||
pub(crate) trait ToCore: Sized {
|
||||
type Core;
|
||||
|
||||
fn to_core(self) -> Self::Core;
|
||||
|
||||
fn as_core(&self) -> Self::Core;
|
||||
|
||||
/// Create a lib value from it's core counterpart.
|
||||
/// Can return none if the core value cannot be turned into the core value.
|
||||
/// This can be the case with forexample value, where it's AST like values are not present on
|
||||
/// the lib value.
|
||||
fn from_core(this: Self::Core) -> Option<Self>;
|
||||
}
|
||||
|
||||
impl ToCore for Bytes {
|
||||
type Core = CoreBytes;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
CoreBytes::from(self.0)
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
self.clone().to_core()
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
Some(Self(this.into_inner()))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for Datetime {
|
||||
type Core = CoreDatetime;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
CoreDatetime::from(self.0)
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
self.clone().to_core()
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
Some(Self(this.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for Object {
|
||||
type Core = CoreObject;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
CoreObject::from(
|
||||
self.0
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.to_core()))
|
||||
.collect::<BTreeMap<String, CoreValue>>(),
|
||||
)
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
let map: BTreeMap<String, CoreValue> =
|
||||
self.0.iter().map(|(k, v)| (k.clone(), v.as_core())).collect();
|
||||
|
||||
CoreObject::from(map)
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
let mut new = BTreeMap::new();
|
||||
for (k, v) in this.0.into_iter() {
|
||||
new.insert(k, ToCore::from_core(v)?);
|
||||
}
|
||||
Some(Object(new))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for Vec<Value> {
|
||||
type Core = CoreArray;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
CoreArray::from(self.into_iter().map(ToCore::to_core).collect::<Vec<CoreValue>>())
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
CoreArray::from(self.iter().map(ToCore::as_core).collect::<Vec<CoreValue>>())
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
let len = this.0.len();
|
||||
this.0.into_iter().try_fold(Vec::<Value>::with_capacity(len), |mut acc, r| {
|
||||
acc.push(Value::from_core(r)?);
|
||||
Some(acc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for Number {
|
||||
type Core = CoreNumber;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
match self {
|
||||
Number::Float(x) => CoreNumber::Float(x),
|
||||
Number::Integer(x) => CoreNumber::Int(x),
|
||||
Number::Decimal(x) => CoreNumber::Decimal(x),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
self.clone().to_core()
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
let v = match this {
|
||||
CoreNumber::Int(x) => Number::Integer(x),
|
||||
CoreNumber::Float(x) => Number::Float(x),
|
||||
CoreNumber::Decimal(x) => Number::Decimal(x),
|
||||
_ => return None,
|
||||
};
|
||||
Some(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for Uuid {
|
||||
type Core = CoreUuid;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
CoreUuid::from(self)
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
self.to_core()
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
Some(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for String {
|
||||
type Core = Strand;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
Strand::from(self)
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
Strand::from(self.as_str())
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
Some(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for Duration {
|
||||
type Core = CoreDuration;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
CoreDuration::from(self)
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
CoreDuration::from(*self)
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
Some(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for RecordId {
|
||||
type Core = Thing;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
Thing::from((self.table, self.key.to_core()))
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
Thing::from((self.table.clone(), self.key.as_core()))
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
Some(Self {
|
||||
table: this.tb,
|
||||
key: ToCore::from_core(this.id)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for RecordIdKey {
|
||||
type Core = Id;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
match self {
|
||||
RecordIdKey::String(x) => Id::String(x),
|
||||
RecordIdKey::Integer(x) => Id::Number(x),
|
||||
RecordIdKey::Object(x) => Id::Object(x.to_core()),
|
||||
RecordIdKey::Array(x) => Id::Array(x.to_core()),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
match self {
|
||||
RecordIdKey::String(x) => Id::String(x.clone()),
|
||||
RecordIdKey::Integer(x) => Id::Number(*x),
|
||||
RecordIdKey::Object(x) => Id::Object(x.as_core()),
|
||||
RecordIdKey::Array(x) => Id::Array(x.as_core()),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
let v = match this {
|
||||
Id::String(x) => RecordIdKey::String(x),
|
||||
Id::Number(x) => RecordIdKey::Integer(x),
|
||||
Id::Object(x) => RecordIdKey::Object(ToCore::from_core(x)?),
|
||||
Id::Array(x) => RecordIdKey::Array(ToCore::from_core(x)?),
|
||||
_ => return None,
|
||||
};
|
||||
Some(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for Edge {
|
||||
type Core = CoreEdges;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
let from = self.from.to_core();
|
||||
let mut what = CoreTables::default();
|
||||
what.0 = self
|
||||
.tables
|
||||
.into_iter()
|
||||
.map(|x| {
|
||||
let mut t = CoreTable::default();
|
||||
t.0 = x;
|
||||
t
|
||||
})
|
||||
.collect();
|
||||
CoreEdges::new(self.dir, from, what)
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
let from = self.from.as_core();
|
||||
let mut what = CoreTables::default();
|
||||
what.0 = self
|
||||
.tables
|
||||
.iter()
|
||||
.map(|x| {
|
||||
let mut t = CoreTable::default();
|
||||
t.0 = x.clone();
|
||||
t
|
||||
})
|
||||
.collect();
|
||||
CoreEdges::new(self.dir.clone(), from, what)
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
Some(Edge {
|
||||
dir: this.dir,
|
||||
from: RecordId::from_core(this.from)?,
|
||||
tables: this.what.0.into_iter().map(|x| x.0).collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for Notification<Value> {
|
||||
type Core = CoreNotification;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
CoreNotification::new(self.query_id.into(), self.action.to_core(), self.data.to_core())
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
CoreNotification::new(self.query_id.into(), self.action.as_core(), self.data.as_core())
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
Some(Notification {
|
||||
query_id: this.id.0,
|
||||
action: Action::from_core(this.action)?,
|
||||
data: Value::from_core(this.result)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for QueryRange {
|
||||
type Core = CoreRange;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
CoreRange::new(
|
||||
self.table,
|
||||
self.range.start.map(|x| x.to_core()),
|
||||
self.range.end.map(|x| x.to_core()),
|
||||
)
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
self.clone().to_core()
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
let start = match this.beg {
|
||||
Bound::Included(x) => Bound::Included(RecordIdKey::from_core(x)?),
|
||||
Bound::Excluded(x) => Bound::Excluded(RecordIdKey::from_core(x)?),
|
||||
Bound::Unbounded => Bound::Unbounded,
|
||||
};
|
||||
|
||||
let end = match this.end {
|
||||
Bound::Included(x) => Bound::Included(RecordIdKey::from_core(x)?),
|
||||
Bound::Excluded(x) => Bound::Excluded(RecordIdKey::from_core(x)?),
|
||||
Bound::Unbounded => Bound::Unbounded,
|
||||
};
|
||||
|
||||
Some(QueryRange {
|
||||
table: this.tb,
|
||||
range: KeyRange {
|
||||
start,
|
||||
end,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for Action {
|
||||
type Core = CoreAction;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
match self {
|
||||
Action::Create => CoreAction::Create,
|
||||
Action::Update => CoreAction::Update,
|
||||
Action::Delete => CoreAction::Delete,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
match self {
|
||||
Action::Create => CoreAction::Create,
|
||||
Action::Update => CoreAction::Update,
|
||||
Action::Delete => CoreAction::Delete,
|
||||
}
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
match this {
|
||||
CoreAction::Create => Some(Action::Create),
|
||||
CoreAction::Update => Some(Action::Update),
|
||||
CoreAction::Delete => Some(Action::Delete),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCore for Value {
|
||||
type Core = CoreValue;
|
||||
|
||||
fn to_core(self) -> Self::Core {
|
||||
match self {
|
||||
Value::None => CoreValue::None,
|
||||
Value::Bool(x) => CoreValue::Bool(x),
|
||||
Value::Number(x) => CoreValue::Number(x.to_core()),
|
||||
Value::Object(x) => CoreValue::Object(x.to_core()),
|
||||
Value::String(x) => CoreValue::Strand(Strand::from(x)),
|
||||
Value::Array(x) => CoreValue::Array(x.to_core()),
|
||||
Value::Uuid(x) => CoreValue::Uuid(x.to_core()),
|
||||
Value::Datetime(x) => CoreValue::Datetime(x.to_core()),
|
||||
Value::Duration(x) => CoreValue::Duration(x.to_core()),
|
||||
Value::Bytes(x) => CoreValue::Bytes(x.to_core()),
|
||||
Value::RecordId(x) => CoreValue::Thing(x.to_core()),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_core(&self) -> Self::Core {
|
||||
match self {
|
||||
Value::None => CoreValue::None,
|
||||
Value::Bool(x) => CoreValue::Bool(*x),
|
||||
Value::Number(x) => CoreValue::Number(x.as_core()),
|
||||
Value::Object(x) => CoreValue::Object(x.as_core()),
|
||||
Value::String(x) => CoreValue::Strand(Strand::from(x.as_str())),
|
||||
Value::Array(x) => CoreValue::Array(x.as_core()),
|
||||
Value::Uuid(x) => CoreValue::Uuid(x.as_core()),
|
||||
Value::Datetime(x) => CoreValue::Datetime(x.as_core()),
|
||||
Value::Duration(x) => CoreValue::Duration(x.as_core()),
|
||||
Value::Bytes(x) => CoreValue::Bytes(x.as_core()),
|
||||
Value::RecordId(x) => CoreValue::Thing(x.as_core()),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_core(this: Self::Core) -> Option<Self> {
|
||||
let v = match this {
|
||||
CoreValue::None | CoreValue::Null => Value::None,
|
||||
CoreValue::Bool(x) => Value::Bool(x),
|
||||
CoreValue::Number(x) => Value::Number(ToCore::from_core(x)?),
|
||||
CoreValue::Object(x) => Value::Object(ToCore::from_core(x)?),
|
||||
CoreValue::Strand(x) => Value::String(ToCore::from_core(x)?),
|
||||
CoreValue::Array(x) => Value::Array(ToCore::from_core(x)?),
|
||||
CoreValue::Uuid(x) => Value::Uuid(ToCore::from_core(x)?),
|
||||
CoreValue::Datetime(x) => Value::Datetime(ToCore::from_core(x)?),
|
||||
CoreValue::Duration(x) => Value::Duration(ToCore::from_core(x)?),
|
||||
CoreValue::Bytes(x) => Value::Bytes(ToCore::from_core(x)?),
|
||||
CoreValue::Thing(x) => Value::RecordId(ToCore::from_core(x)?),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(v)
|
||||
}
|
||||
}
|
371
lib/src/api/value/mod.rs
Normal file
371
lib/src/api/value/mod.rs
Normal file
|
@ -0,0 +1,371 @@
|
|||
use crate::Error;
|
||||
use revision::revisioned;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use std::{
|
||||
cmp::{Ordering, PartialEq, PartialOrd},
|
||||
fmt,
|
||||
ops::Deref,
|
||||
str::FromStr,
|
||||
};
|
||||
use surrealdb_core::{
|
||||
dbs::Action as CoreAction,
|
||||
sql::{
|
||||
Array as CoreArray, Datetime as CoreDatetime, Id as CoreId, Number as CoreNumber,
|
||||
Thing as CoreThing, Value as CoreValue,
|
||||
},
|
||||
syn,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
mod obj;
|
||||
pub use obj::{IntoIter, Iter, IterMut, Object};
|
||||
|
||||
pub fn from_value<T: DeserializeOwned>(value: Value) -> Result<T, Error> {
|
||||
Ok(surrealdb_core::sql::from_value(value.0)?)
|
||||
}
|
||||
|
||||
pub fn to_value<T: Serialize + 'static>(value: T) -> Result<Value, Error> {
|
||||
let v = surrealdb_core::sql::to_value(value)?;
|
||||
Ok(Value(v))
|
||||
}
|
||||
|
||||
// Keeping bytes implementation minimal since it might be a good idea to use bytes crate here
|
||||
// instead of a plain Vec<u8>.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[revisioned(revision = 1)]
|
||||
pub struct Bytes(Vec<u8>);
|
||||
|
||||
impl Bytes {
|
||||
pub fn copy_from_slice(slice: &[u8]) -> Self {
|
||||
slice.to_vec().into()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<[u8]> for Bytes {
|
||||
fn eq(&self, other: &[u8]) -> bool {
|
||||
self.0 == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<[u8]> for Bytes {
|
||||
fn partial_cmp(&self, other: &[u8]) -> Option<Ordering> {
|
||||
self.0.as_slice().partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Bytes {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Bytes {
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
Bytes(value)
|
||||
}
|
||||
}
|
||||
|
||||
transparent_wrapper!(
|
||||
#[derive( Clone, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct Datetime(CoreDatetime)
|
||||
);
|
||||
|
||||
transparent_wrapper!(
|
||||
/// The key of a [`RecordId`].
|
||||
#[derive( Clone, PartialEq, PartialOrd)]
|
||||
#[non_exhaustive]
|
||||
pub struct RecordIdKey(CoreId)
|
||||
);
|
||||
|
||||
impl From<Object> for RecordIdKey {
|
||||
fn from(value: Object) -> Self {
|
||||
Self::from_inner(CoreId::Object(value.into_inner()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for RecordIdKey {
|
||||
fn from(value: String) -> Self {
|
||||
Self(CoreId::String(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&String> for RecordIdKey {
|
||||
fn from(value: &String) -> Self {
|
||||
Self(CoreId::String(value.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for RecordIdKey {
|
||||
fn from(value: &str) -> Self {
|
||||
Self(CoreId::String(value.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for RecordIdKey {
|
||||
fn from(value: i64) -> Self {
|
||||
Self(CoreId::Number(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Value>> for RecordIdKey {
|
||||
fn from(value: Vec<Value>) -> Self {
|
||||
let res = Value::array_to_core(value);
|
||||
let mut array = CoreArray::default();
|
||||
array.0 = res;
|
||||
Self(CoreId::Array(array))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RecordIdKey> for Value {
|
||||
fn from(key: RecordIdKey) -> Self {
|
||||
match key.0 {
|
||||
CoreId::String(x) => Value::from_inner(CoreValue::from(x)),
|
||||
CoreId::Number(x) => Value::from_inner(CoreValue::from(x)),
|
||||
CoreId::Object(x) => Value::from_inner(CoreValue::from(x)),
|
||||
CoreId::Array(x) => Value::from_inner(CoreValue::from(x)),
|
||||
_ => panic!("lib recieved generate variant of record id"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RecordId> for Value {
|
||||
fn from(key: RecordId) -> Self {
|
||||
Value::from_inner(CoreValue::Thing(key.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Value {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Value::from_inner(surrealdb_core::syn::value(s)?))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RecordIdKeyFromValueError(());
|
||||
|
||||
impl fmt::Display for RecordIdKeyFromValueError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f,"tried to convert a value to a record id key with a value type that is not allowed in a record id key")
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Value> for RecordIdKey {
|
||||
type Error = RecordIdKeyFromValueError;
|
||||
|
||||
fn try_from(key: Value) -> Result<Self, Self::Error> {
|
||||
match key.0 {
|
||||
CoreValue::Strand(x) => Ok(RecordIdKey::from_inner(CoreId::String(x.0))),
|
||||
CoreValue::Number(CoreNumber::Int(x)) => Ok(RecordIdKey::from_inner(CoreId::Number(x))),
|
||||
CoreValue::Object(x) => Ok(RecordIdKey::from_inner(CoreId::Object(x))),
|
||||
CoreValue::Array(x) => Ok(RecordIdKey::from_inner(CoreId::Array(x))),
|
||||
_ => Err(RecordIdKeyFromValueError(())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transparent_wrapper!(
|
||||
/// Struct representing a record id.
|
||||
///
|
||||
/// Record id's consist of a table name and a key.
|
||||
/// For example the record id `user:tkwse1j5o0anqjxonvzx` has the table `user` and the key `tkwse1j5o0anqjxonvzx`.
|
||||
#[derive( Clone, PartialEq, PartialOrd)]
|
||||
pub struct RecordId(CoreThing)
|
||||
);
|
||||
impl_serialize_wrapper!(RecordId);
|
||||
|
||||
impl RecordId {
|
||||
pub fn from_table_key<S, K>(table: S, key: K) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
K: Into<RecordIdKey>,
|
||||
{
|
||||
let tb = table.into();
|
||||
let key = key.into();
|
||||
Self(CoreThing::from((tb, key.0)))
|
||||
}
|
||||
|
||||
pub fn table(&self) -> &str {
|
||||
&self.0.tb
|
||||
}
|
||||
|
||||
pub fn key(&self) -> &RecordIdKey {
|
||||
RecordIdKey::from_inner_ref(&self.0.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for RecordId {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
syn::thing(s).map_err(crate::Error::Db).map(RecordId::from_inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, I> From<(S, I)> for RecordId
|
||||
where
|
||||
S: Into<String>,
|
||||
RecordIdKey: From<I>,
|
||||
{
|
||||
fn from(value: (S, I)) -> Self {
|
||||
Self::from_table_key(value.0, value.1)
|
||||
}
|
||||
}
|
||||
|
||||
transparent_wrapper!(
|
||||
/// The number type of surrealql.
|
||||
/// Can contain either a 64 bit float, 64 bit integer or a decimal.
|
||||
#[derive( Clone, PartialEq, PartialOrd)]
|
||||
pub struct Number(CoreNumber)
|
||||
);
|
||||
impl_serialize_wrapper!(Number);
|
||||
|
||||
impl Number {
|
||||
#[doc(hidden)]
|
||||
pub fn cource_into_i64(self) -> Option<i64> {
|
||||
match self.0 {
|
||||
CoreNumber::Int(x) => Some(x),
|
||||
CoreNumber::Float(x) if x.fract() == x => Some(x as i64),
|
||||
CoreNumber::Decimal(x) => x.try_into().ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transparent_wrapper!(
|
||||
#[derive( Clone, Default, PartialEq, PartialOrd)]
|
||||
pub struct Value(pub(crate) CoreValue)
|
||||
);
|
||||
impl_serialize_wrapper!(Value);
|
||||
|
||||
impl Value {
|
||||
// TODO: Check if all of theses are actually used.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn core_to_array(v: Vec<CoreValue>) -> Vec<Value> {
|
||||
unsafe {
|
||||
// SAFETY: Because Value is `repr(transparent)` transmuting between value and corevalue
|
||||
// is safe.
|
||||
std::mem::transmute::<Vec<CoreValue>, Vec<Value>>(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn core_to_array_ref(v: &Vec<CoreValue>) -> &Vec<Value> {
|
||||
unsafe {
|
||||
// SAFETY: Because Value is `repr(transparent)` transmuting between value and corevalue
|
||||
// is safe.
|
||||
std::mem::transmute::<&Vec<CoreValue>, &Vec<Value>>(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn core_to_array_mut(v: &mut Vec<CoreValue>) -> &mut Vec<Value> {
|
||||
unsafe {
|
||||
// SAFETY: Because Value is `repr(transparent)` transmuting between value and corevalue
|
||||
// is safe.
|
||||
std::mem::transmute::<&mut Vec<CoreValue>, &mut Vec<Value>>(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn array_to_core(v: Vec<Value>) -> Vec<CoreValue> {
|
||||
unsafe {
|
||||
// SAFETY: Because Value is `repr(transparent)` transmuting between value and corevalue
|
||||
// is safe.
|
||||
std::mem::transmute::<Vec<Value>, Vec<CoreValue>>(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn array_to_core_ref(v: &Vec<Value>) -> &Vec<CoreValue> {
|
||||
unsafe {
|
||||
// SAFETY: Because Value is `repr(transparent)` transmuting between value and corevalue
|
||||
// is safe.
|
||||
std::mem::transmute::<&Vec<Value>, &Vec<CoreValue>>(v)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn array_to_core_mut(v: &mut Vec<Value>) -> &mut Vec<CoreValue> {
|
||||
unsafe {
|
||||
// SAFETY: Because Value is `repr(transparent)` transmuting between value and corevalue
|
||||
// is safe.
|
||||
std::mem::transmute::<&mut Vec<Value>, &mut Vec<CoreValue>>(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConversionError {
|
||||
from: &'static str,
|
||||
expected: &'static str,
|
||||
}
|
||||
|
||||
impl fmt::Display for ConversionError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(
|
||||
f,
|
||||
"failed to convert into `{}` from value with type `{:?}`",
|
||||
self.expected, self.from
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// The action performed on a record
|
||||
///
|
||||
/// This is used in live query notifications.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum Action {
|
||||
Create,
|
||||
Update,
|
||||
Delete,
|
||||
}
|
||||
|
||||
impl Action {
|
||||
pub(crate) fn from_core(action: CoreAction) -> Self {
|
||||
match action {
|
||||
CoreAction::Create => Self::Create,
|
||||
CoreAction::Update => Self::Update,
|
||||
CoreAction::Delete => Self::Delete,
|
||||
_ => panic!("unimplemented variant of action"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A live query notification
|
||||
///
|
||||
/// Live queries return a stream of notifications. The notification contains an `action` that triggered the change in the database record and `data` itself.
|
||||
/// For deletions the data is the record before it was deleted. For everything else, it's the newly created record or updated record depending on whether
|
||||
/// the action is create or update.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub struct Notification<R> {
|
||||
pub query_id: Uuid,
|
||||
pub action: Action,
|
||||
pub data: R,
|
||||
}
|
||||
|
||||
impl Notification<CoreValue> {
|
||||
pub fn map_deserialize<R>(self) -> Result<Notification<R>, crate::error::Db>
|
||||
where
|
||||
R: DeserializeOwned,
|
||||
{
|
||||
let data = surrealdb_core::sql::from_value(self.data)?;
|
||||
Ok(Notification {
|
||||
query_id: self.query_id,
|
||||
action: self.action,
|
||||
data,
|
||||
})
|
||||
}
|
||||
}
|
244
lib/src/api/value/obj.rs
Normal file
244
lib/src/api/value/obj.rs
Normal file
|
@ -0,0 +1,244 @@
|
|||
use std::{
|
||||
borrow::Borrow,
|
||||
collections::btree_map::{IntoIter as BIntoIter, Iter as BIter, IterMut as BIterMut},
|
||||
iter::FusedIterator,
|
||||
};
|
||||
use surrealdb_core::sql::{Object as CoreObject, Value as CoreValue};
|
||||
|
||||
use super::Value;
|
||||
|
||||
transparent_wrapper! {
|
||||
#[derive( Clone, Eq, PartialEq, Ord, PartialOrd, Default)]
|
||||
pub struct Object(CoreObject)
|
||||
}
|
||||
impl_serialize_wrapper!(Object);
|
||||
|
||||
impl Object {
|
||||
pub fn new() -> Self {
|
||||
Object(CoreObject::default())
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.0.clear()
|
||||
}
|
||||
|
||||
pub fn get<Q>(&self, key: &Q) -> Option<&Value>
|
||||
where
|
||||
String: Borrow<Q>,
|
||||
Q: Ord + ?Sized,
|
||||
{
|
||||
self.0.get(key).map(Value::from_inner_ref)
|
||||
}
|
||||
|
||||
pub fn get_mut<Q>(&mut self, key: &Q) -> Option<&mut Value>
|
||||
where
|
||||
String: Borrow<Q>,
|
||||
Q: Ord + ?Sized,
|
||||
{
|
||||
self.0.get_mut(key).map(Value::from_inner_mut)
|
||||
}
|
||||
|
||||
pub fn contains_key<Q>(&self, key: &Q) -> bool
|
||||
where
|
||||
String: Borrow<Q>,
|
||||
Q: ?Sized + Ord,
|
||||
{
|
||||
self.0.contains_key(key)
|
||||
}
|
||||
|
||||
pub fn remove<Q>(&mut self, key: &Q) -> Option<Value>
|
||||
where
|
||||
String: Borrow<Q>,
|
||||
Q: ?Sized + Ord,
|
||||
{
|
||||
self.0.remove(key).map(Value::from_inner)
|
||||
}
|
||||
|
||||
pub fn remove_entry<Q>(&mut self, key: &Q) -> Option<(String, Value)>
|
||||
where
|
||||
String: Borrow<Q>,
|
||||
Q: ?Sized + Ord,
|
||||
{
|
||||
self.0.remove_entry(key).map(|(a, b)| (a, Value::from_inner(b)))
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> Iter<'_> {
|
||||
Iter {
|
||||
iter: self.0.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> IterMut<'_> {
|
||||
IterMut {
|
||||
iter: self.0.iter_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.0.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_empty()
|
||||
}
|
||||
|
||||
pub fn insert<V>(&mut self, key: String, value: V) -> Option<Value>
|
||||
where
|
||||
V: Into<Value>,
|
||||
{
|
||||
self.0.insert(key, value.into().into_inner()).map(Value::from_inner)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IntoIter {
|
||||
iter: BIntoIter<String, CoreValue>,
|
||||
}
|
||||
|
||||
impl Iterator for IntoIter {
|
||||
type Item = (String, Value);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|x| (x.0, Value::from_inner(x.1)))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl DoubleEndedIterator for IntoIter {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next_back().map(|x| (x.0, Value::from_inner(x.1)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for IntoIter {
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl FusedIterator for IntoIter {}
|
||||
|
||||
impl IntoIterator for Object {
|
||||
type Item = (String, Value);
|
||||
|
||||
type IntoIter = IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter {
|
||||
iter: self.0 .0.into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Iter<'a> {
|
||||
iter: BIter<'a, String, CoreValue>,
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a Object {
|
||||
type Item = (&'a String, &'a Value);
|
||||
|
||||
type IntoIter = Iter<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = (&'a String, &'a Value);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|(a, b)| (a, Value::from_inner_ref(b)))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
|
||||
fn last(self) -> Option<Self::Item>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.iter.last().map(|(a, b)| (a, Value::from_inner_ref(b)))
|
||||
}
|
||||
|
||||
fn min(mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|(a, b)| (a, Value::from_inner_ref(b)))
|
||||
}
|
||||
|
||||
fn max(mut self) -> Option<Self::Item> {
|
||||
self.iter.next_back().map(|(a, b)| (a, Value::from_inner_ref(b)))
|
||||
}
|
||||
}
|
||||
|
||||
impl FusedIterator for Iter<'_> {}
|
||||
|
||||
impl<'a> DoubleEndedIterator for Iter<'a> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next_back().map(|(a, b)| (a, Value::from_inner_ref(b)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExactSizeIterator for Iter<'a> {
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IterMut<'a> {
|
||||
iter: BIterMut<'a, String, CoreValue>,
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a mut Object {
|
||||
type Item = (&'a String, &'a mut Value);
|
||||
|
||||
type IntoIter = IterMut<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for IterMut<'a> {
|
||||
type Item = (&'a String, &'a mut Value);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|(a, b)| (a, Value::from_inner_mut(b)))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
|
||||
fn last(self) -> Option<Self::Item>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.iter.last().map(|(a, b)| (a, Value::from_inner_mut(b)))
|
||||
}
|
||||
|
||||
fn min(mut self) -> Option<Self::Item> {
|
||||
self.iter.next().map(|(a, b)| (a, Value::from_inner_mut(b)))
|
||||
}
|
||||
|
||||
fn max(mut self) -> Option<Self::Item> {
|
||||
self.iter.next_back().map(|(a, b)| (a, Value::from_inner_mut(b)))
|
||||
}
|
||||
}
|
||||
|
||||
impl FusedIterator for IterMut<'_> {}
|
||||
|
||||
impl<'a> DoubleEndedIterator for IterMut<'a> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next_back().map(|(a, b)| (a, Value::from_inner_mut(b)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExactSizeIterator for IterMut<'a> {
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
|
@ -116,34 +116,14 @@ compile_error!("The `ml` feature is not supported on the `wasm32` architecture."
|
|||
#[macro_use]
|
||||
extern crate tracing;
|
||||
|
||||
#[macro_use]
|
||||
mod mac;
|
||||
|
||||
mod api;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use api::engine;
|
||||
#[cfg(feature = "protocol-http")]
|
||||
#[doc(hidden)]
|
||||
pub use api::headers;
|
||||
#[doc(inline)]
|
||||
pub use api::method;
|
||||
#[doc(inline)]
|
||||
pub use api::opt;
|
||||
#[doc(inline)]
|
||||
pub use api::Connect;
|
||||
#[doc(inline)]
|
||||
pub use api::Connection;
|
||||
#[doc(inline)]
|
||||
pub use api::Response;
|
||||
#[doc(inline)]
|
||||
pub use api::Result;
|
||||
#[doc(inline)]
|
||||
pub use api::Surreal;
|
||||
#[doc(inline)]
|
||||
pub use surrealdb_core::*;
|
||||
|
||||
use uuid::Uuid;
|
||||
pub use uuid::Uuid;
|
||||
|
||||
#[macro_use]
|
||||
mod mac;
|
||||
mod api;
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Channels for receiving a SurrealQL database export
|
||||
|
@ -157,43 +137,21 @@ pub mod channel {
|
|||
/// Different error types for embedded and remote databases
|
||||
pub mod error {
|
||||
pub use crate::api::err::Error as Api;
|
||||
pub use crate::err::Error as Db;
|
||||
pub use surrealdb_core::err::Error as Db;
|
||||
}
|
||||
|
||||
/// The action performed on a record
|
||||
///
|
||||
/// This is used in live query notifications.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum Action {
|
||||
Create,
|
||||
Update,
|
||||
Delete,
|
||||
}
|
||||
#[cfg(feature = "protocol-http")]
|
||||
#[doc(hidden)]
|
||||
pub use api::headers;
|
||||
|
||||
impl From<dbs::Action> for Action {
|
||||
fn from(action: dbs::Action) -> Self {
|
||||
match action {
|
||||
dbs::Action::Create => Self::Create,
|
||||
dbs::Action::Update => Self::Update,
|
||||
dbs::Action::Delete => Self::Delete,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A live query notification
|
||||
///
|
||||
/// Live queries return a stream of notifications. The notification contains an `action` that triggered the change in the database record and `data` itself.
|
||||
/// For deletions the data is the record before it was deleted. For everything else, it's the newly created record or updated record depending on whether
|
||||
/// the action is create or update.
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub struct Notification<R> {
|
||||
pub query_id: Uuid,
|
||||
pub action: Action,
|
||||
pub data: R,
|
||||
}
|
||||
#[doc(inline)]
|
||||
pub use api::{
|
||||
engine, method, opt,
|
||||
value::{
|
||||
self, Action, Bytes, Datetime, Notification, Number, Object, RecordId, RecordIdKey, Value,
|
||||
},
|
||||
Connect, Connection, Response, Result, Surreal,
|
||||
};
|
||||
|
||||
/// An error originating from the SurrealDB client library
|
||||
#[derive(Debug, thiserror::Error, serde::Serialize)]
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
/*
|
||||
/// Creates a new b-tree map of key-value pairs
|
||||
macro_rules! map {
|
||||
($($k:expr $(, if let $grant:pat = $check:expr)? $(, if $guard:expr)? => $v:expr),* $(,)? $( => $x:expr )?) => {{
|
||||
let mut m = ::std::collections::BTreeMap::new();
|
||||
$(m.extend($x.iter().map(|(k, v)| (k.clone(), v.clone())));)?
|
||||
($($k:expr $(, if let $grant:pat = $check:expr)? $(, if $guard:expr)? => $v:expr),* $(,)? $( => $x:expr )?) => {{
|
||||
let mut m = ::std::collections::BTreeMap::new();
|
||||
$(m.extend($x.iter().map(|(k, v)| (k.clone(), v.clone())));)?
|
||||
$( $(if let $grant = $check)? $(if $guard)? { m.insert($k, $v); };)+
|
||||
m
|
||||
}};
|
||||
}
|
||||
m
|
||||
}};
|
||||
}*/
|
||||
|
|
|
@ -26,10 +26,7 @@ mod api_integration {
|
|||
use surrealdb::sql::statements::BeginStatement;
|
||||
use surrealdb::sql::statements::CommitStatement;
|
||||
use surrealdb::sql::thing;
|
||||
use surrealdb::sql::Thing;
|
||||
use surrealdb::sql::Value;
|
||||
use surrealdb::Error;
|
||||
use surrealdb::Surreal;
|
||||
use surrealdb::{Error, RecordId, Surreal, Value};
|
||||
use tokio::sync::Semaphore;
|
||||
use tokio::sync::SemaphorePermit;
|
||||
use tracing_subscriber::filter::EnvFilter;
|
||||
|
@ -48,9 +45,9 @@ mod api_integration {
|
|||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
|
||||
struct RecordId {
|
||||
id: Thing,
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, PartialOrd)]
|
||||
struct ApiRecordId {
|
||||
id: RecordId,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
|
@ -58,9 +55,9 @@ mod api_integration {
|
|||
name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[derive(Debug, Clone, Deserialize, PartialEq, PartialOrd)]
|
||||
struct RecordBuf {
|
||||
id: Thing,
|
||||
id: RecordId,
|
||||
name: String,
|
||||
}
|
||||
|
||||
|
@ -182,6 +179,7 @@ mod api_integration {
|
|||
use surrealdb::engine::local::Db;
|
||||
use surrealdb::engine::local::Mem;
|
||||
use surrealdb::iam;
|
||||
use surrealdb::RecordIdKey;
|
||||
|
||||
async fn new_db() -> (SemaphorePermit<'static>, Surreal<Db>) {
|
||||
let permit = PERMITS.acquire().await.unwrap();
|
||||
|
@ -213,10 +211,11 @@ mod api_integration {
|
|||
async fn signin_first_not_necessary() {
|
||||
let db = Surreal::new::<Mem>(()).await.unwrap();
|
||||
db.use_ns("namespace").use_db("database").await.unwrap();
|
||||
let Some(record): Option<RecordId> = db.create(("item", "foo")).await.unwrap() else {
|
||||
let Some(record): Option<ApiRecordId> = db.create(("item", "foo")).await.unwrap()
|
||||
else {
|
||||
panic!("record not found");
|
||||
};
|
||||
assert_eq!(record.id.to_string(), "item:foo");
|
||||
assert_eq!(*record.id.key(), RecordIdKey::from("foo".to_owned()));
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
|
|
|
@ -10,7 +10,7 @@ async fn export_import() {
|
|||
let db_name = Ulid::new().to_string();
|
||||
db.use_ns(NS).use_db(&db_name).await.unwrap();
|
||||
for i in 0..10 {
|
||||
let _: Vec<RecordId> = db
|
||||
let _: Vec<ApiRecordId> = db
|
||||
.create("user")
|
||||
.content(Record {
|
||||
name: format!("User {i}"),
|
||||
|
|
|
@ -35,9 +35,9 @@ async fn live_select_table() {
|
|||
let mut users = db.select(&table).live().await.unwrap();
|
||||
|
||||
// Create a record
|
||||
let created: Vec<RecordId> = db.create(table).await.unwrap();
|
||||
let created: Vec<ApiRecordId> = db.create(table).await.unwrap();
|
||||
// Pull the notification
|
||||
let notification: Notification<RecordId> =
|
||||
let notification: Notification<ApiRecordId> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap().unwrap();
|
||||
// The returned record should match the created record
|
||||
assert_eq!(created, vec![notification.data.clone()]);
|
||||
|
@ -45,18 +45,18 @@ async fn live_select_table() {
|
|||
assert_eq!(notification.action, Action::Create);
|
||||
|
||||
// Update the record
|
||||
let _: Option<RecordId> =
|
||||
let _: Option<ApiRecordId> =
|
||||
db.update(¬ification.data.id).content(json!({"foo": "bar"})).await.unwrap();
|
||||
// Pull the notification
|
||||
let notification: Notification<RecordId> =
|
||||
let notification: Notification<ApiRecordId> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap().unwrap();
|
||||
// It should be updated
|
||||
assert_eq!(notification.action, Action::Update);
|
||||
|
||||
// Delete the record
|
||||
let _: Option<RecordId> = db.delete(¬ification.data.id).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.delete(¬ification.data.id).await.unwrap();
|
||||
// Pull the notification
|
||||
let notification: Notification<RecordId> = users.next().await.unwrap().unwrap();
|
||||
let notification: Notification<ApiRecordId> = users.next().await.unwrap().unwrap();
|
||||
// It should be deleted
|
||||
assert_eq!(notification.action, Action::Delete);
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ async fn live_select_table() {
|
|||
// Pull the notification
|
||||
let notification = tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap();
|
||||
// The returned record should be an object
|
||||
assert!(notification.data.is_object());
|
||||
assert!(notification.data.into_inner().is_object());
|
||||
// It should be newly created
|
||||
assert_eq!(notification.action, Action::Create);
|
||||
}
|
||||
|
@ -102,15 +102,15 @@ async fn live_select_record_id() {
|
|||
} else {
|
||||
db.query(format!("DEFINE TABLE {table}")).await.unwrap();
|
||||
}
|
||||
let record_id = Thing::from((table, "john".to_owned()));
|
||||
let record_id = RecordId::from((table, "john".to_owned()));
|
||||
|
||||
// Start listening
|
||||
let mut users = db.select(&record_id).live().await.unwrap();
|
||||
|
||||
// Create a record
|
||||
let created: Option<RecordId> = db.create(record_id).await.unwrap();
|
||||
let created: Option<ApiRecordId> = db.create(record_id).await.unwrap();
|
||||
// Pull the notification
|
||||
let notification: Notification<RecordId> =
|
||||
let notification: Notification<ApiRecordId> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap().unwrap();
|
||||
// The returned record should match the created record
|
||||
assert_eq!(created, Some(notification.data.clone()));
|
||||
|
@ -118,18 +118,18 @@ async fn live_select_record_id() {
|
|||
assert_eq!(notification.action, Action::Create);
|
||||
|
||||
// Update the record
|
||||
let _: Option<RecordId> =
|
||||
let _: Option<ApiRecordId> =
|
||||
db.update(¬ification.data.id).content(json!({"foo": "bar"})).await.unwrap();
|
||||
// Pull the notification
|
||||
let notification: Notification<RecordId> =
|
||||
let notification: Notification<ApiRecordId> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap().unwrap();
|
||||
// It should be updated
|
||||
assert_eq!(notification.action, Action::Update);
|
||||
|
||||
// Delete the record
|
||||
let _: Option<RecordId> = db.delete(¬ification.data.id).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.delete(¬ification.data.id).await.unwrap();
|
||||
// Pull the notification
|
||||
let notification: Notification<RecordId> =
|
||||
let notification: Notification<ApiRecordId> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap().unwrap();
|
||||
// It should be deleted
|
||||
assert_eq!(notification.action, Action::Delete);
|
||||
|
@ -144,7 +144,7 @@ async fn live_select_record_id() {
|
|||
} else {
|
||||
db.query(format!("DEFINE TABLE {table}")).await.unwrap();
|
||||
}
|
||||
let record_id = Thing::from((table, "john".to_owned()));
|
||||
let record_id = RecordId::from((table, "john".to_owned()));
|
||||
|
||||
// Start listening
|
||||
let mut users = db.select(Resource::from(&record_id)).live().await.unwrap();
|
||||
|
@ -155,7 +155,7 @@ async fn live_select_record_id() {
|
|||
let notification: Notification<Value> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap();
|
||||
// The returned record should be an object
|
||||
assert!(notification.data.is_object());
|
||||
assert!(notification.data.into_inner().is_object());
|
||||
// It should be newly created
|
||||
assert_eq!(notification.action, Action::Create);
|
||||
}
|
||||
|
@ -183,9 +183,9 @@ async fn live_select_record_ranges() {
|
|||
let mut users = db.select(&table).range("jane".."john").live().await.unwrap();
|
||||
|
||||
// Create a record
|
||||
let created: Option<RecordId> = db.create((table, "jane")).await.unwrap();
|
||||
let created: Option<ApiRecordId> = db.create((table, "jane")).await.unwrap();
|
||||
// Pull the notification
|
||||
let notification: Notification<RecordId> =
|
||||
let notification: Notification<ApiRecordId> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap().unwrap();
|
||||
// The returned record should match the created record
|
||||
assert_eq!(created, Some(notification.data.clone()));
|
||||
|
@ -193,19 +193,19 @@ async fn live_select_record_ranges() {
|
|||
assert_eq!(notification.action, Action::Create);
|
||||
|
||||
// Update the record
|
||||
let _: Option<RecordId> =
|
||||
let _: Option<ApiRecordId> =
|
||||
db.update(¬ification.data.id).content(json!({"foo": "bar"})).await.unwrap();
|
||||
// Pull the notification
|
||||
let notification: Notification<RecordId> =
|
||||
let notification: Notification<ApiRecordId> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap().unwrap();
|
||||
// It should be updated
|
||||
assert_eq!(notification.action, Action::Update);
|
||||
|
||||
// Delete the record
|
||||
let _: Option<RecordId> = db.delete(¬ification.data.id).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.delete(¬ification.data.id).await.unwrap();
|
||||
|
||||
// Pull the notification
|
||||
let notification: Notification<RecordId> =
|
||||
let notification: Notification<ApiRecordId> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap().unwrap();
|
||||
|
||||
// It should be deleted
|
||||
|
@ -227,25 +227,26 @@ async fn live_select_record_ranges() {
|
|||
db.select(Resource::from(&table)).range("jane".."john").live().await.unwrap();
|
||||
|
||||
// Create a record
|
||||
let created_value = match db.create(Resource::from((table, "job"))).await.unwrap() {
|
||||
Value::Object(created_value) => created_value,
|
||||
_ => panic!("Expected an object"),
|
||||
};
|
||||
let created_value =
|
||||
match db.create(Resource::from((table, "job"))).await.unwrap().into_inner() {
|
||||
CoreValue::Object(created_value) => created_value,
|
||||
_ => panic!("Expected an object"),
|
||||
};
|
||||
|
||||
// Pull the notification
|
||||
let notification: Notification<Value> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap();
|
||||
// The returned record should be an object
|
||||
assert!(notification.data.is_object());
|
||||
assert!(notification.data.into_inner().is_object());
|
||||
// It should be newly created
|
||||
assert_eq!(notification.action, Action::Create);
|
||||
|
||||
// Delete the record
|
||||
let thing = match created_value.0.get("id").unwrap() {
|
||||
Value::Thing(thing) => thing,
|
||||
let thing = match created_value.get("id").unwrap() {
|
||||
CoreValue::Thing(thing) => thing,
|
||||
_ => panic!("Expected a thing"),
|
||||
};
|
||||
db.query("DELETE $item").bind(("item", thing.clone())).await.unwrap();
|
||||
db.query("DELETE $item").bind(("item", RecordId::from_inner(thing.clone()))).await.unwrap();
|
||||
|
||||
// Pull the notification
|
||||
let notification: Notification<Value> =
|
||||
|
@ -253,11 +254,11 @@ async fn live_select_record_ranges() {
|
|||
|
||||
// It should be deleted
|
||||
assert_eq!(notification.action, Action::Delete);
|
||||
let notification = match notification.data {
|
||||
Value::Object(notification) => notification,
|
||||
let notification = match notification.data.into_inner() {
|
||||
CoreValue::Object(notification) => notification,
|
||||
_ => panic!("Expected an object"),
|
||||
};
|
||||
assert_eq!(notification.0, created_value.0);
|
||||
assert_eq!(notification, created_value);
|
||||
}
|
||||
|
||||
drop(permit);
|
||||
|
@ -280,7 +281,7 @@ async fn live_select_query() {
|
|||
|
||||
// Start listening
|
||||
info!("Starting live query");
|
||||
let users: QueryStream<Notification<RecordId>> = db
|
||||
let users: QueryStream<Notification<ApiRecordId>> = db
|
||||
.query(format!("LIVE SELECT * FROM {table}"))
|
||||
.await
|
||||
.unwrap()
|
||||
|
@ -290,7 +291,7 @@ async fn live_select_query() {
|
|||
|
||||
// Create a record
|
||||
info!("Creating record");
|
||||
let created: Vec<RecordId> = db.create(table).await.unwrap();
|
||||
let created: Vec<ApiRecordId> = db.create(table).await.unwrap();
|
||||
// Pull the notification
|
||||
let notifications = receive_all_pending_notifications(users.clone(), LQ_TIMEOUT).await;
|
||||
// It should be newly created
|
||||
|
@ -305,7 +306,7 @@ async fn live_select_query() {
|
|||
|
||||
// Update the record
|
||||
info!("Updating record");
|
||||
let _: Option<RecordId> =
|
||||
let _: Option<ApiRecordId> =
|
||||
db.update(¬ifications[0].data.id).content(json!({"foo": "bar"})).await.unwrap();
|
||||
let notifications = receive_all_pending_notifications(users.clone(), LQ_TIMEOUT).await;
|
||||
|
||||
|
@ -319,7 +320,7 @@ async fn live_select_query() {
|
|||
|
||||
// Delete the record
|
||||
info!("Deleting record");
|
||||
let _: Option<RecordId> = db.delete(¬ifications[0].data.id).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.delete(¬ifications[0].data.id).await.unwrap();
|
||||
// Pull the notification
|
||||
let notifications = receive_all_pending_notifications(users.clone(), LQ_TIMEOUT).await;
|
||||
// It should be deleted
|
||||
|
@ -349,7 +350,7 @@ async fn live_select_query() {
|
|||
let notification = tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap();
|
||||
|
||||
// The returned record should be an object
|
||||
assert!(notification.data.is_object());
|
||||
assert!(notification.data.into_inner().is_object());
|
||||
// It should be newly created
|
||||
assert_eq!(notification.action, Action::Create);
|
||||
}
|
||||
|
@ -367,9 +368,9 @@ async fn live_select_query() {
|
|||
.unwrap();
|
||||
|
||||
// Create a record
|
||||
let created: Vec<RecordId> = db.create(table).await.unwrap();
|
||||
let created: Vec<ApiRecordId> = db.create(table).await.unwrap();
|
||||
// Pull the notification
|
||||
let notification: Notification<RecordId> =
|
||||
let notification: Notification<ApiRecordId> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap().unwrap();
|
||||
// The returned record should match the created record
|
||||
assert_eq!(created, vec![notification.data.clone()]);
|
||||
|
@ -377,18 +378,18 @@ async fn live_select_query() {
|
|||
assert_eq!(notification.action, Action::Create, "{:?}", notification);
|
||||
|
||||
// Update the record
|
||||
let _: Option<RecordId> =
|
||||
let _: Option<ApiRecordId> =
|
||||
db.update(¬ification.data.id).content(json!({"foo": "bar"})).await.unwrap();
|
||||
// Pull the notification
|
||||
let notification: Notification<RecordId> =
|
||||
let notification: Notification<ApiRecordId> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap().unwrap();
|
||||
// It should be updated
|
||||
assert_eq!(notification.action, Action::Update, "{:?}", notification);
|
||||
|
||||
// Delete the record
|
||||
let _: Option<RecordId> = db.delete(¬ification.data.id).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.delete(¬ification.data.id).await.unwrap();
|
||||
// Pull the notification
|
||||
let notification: Notification<RecordId> =
|
||||
let notification: Notification<ApiRecordId> =
|
||||
tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap().unwrap();
|
||||
// It should be deleted
|
||||
assert_eq!(notification.action, Action::Delete, "{:?}", notification);
|
||||
|
@ -413,7 +414,7 @@ async fn live_select_query() {
|
|||
// Pull the notification
|
||||
let notification = tokio::time::timeout(LQ_TIMEOUT, users.next()).await.unwrap().unwrap();
|
||||
// The returned record should be an object
|
||||
assert!(notification.data.is_object());
|
||||
assert!(notification.data.into_inner().is_object());
|
||||
// It should be newly created
|
||||
assert_eq!(notification.action, Action::Create);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// Tests common to all protocols and storage engines
|
||||
|
||||
use surrealdb::fflags::FFLAGS;
|
||||
use surrealdb::sql::value;
|
||||
use surrealdb::value;
|
||||
use surrealdb::Response;
|
||||
use surrealdb_core::sql::{Id, Value as CoreValue};
|
||||
|
||||
static PERMITS: Semaphore = Semaphore::const_new(1);
|
||||
|
||||
|
@ -43,7 +44,7 @@ async fn invalidate() {
|
|||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
db.invalidate().await.unwrap();
|
||||
let error = db.create::<Option<RecordId>>(("user", "john")).await.unwrap_err();
|
||||
let error = db.create::<Option<ApiRecordId>>(("user", "john")).await.unwrap_err();
|
||||
assert!(
|
||||
error.to_string().contains("Not enough permissions to perform this action"),
|
||||
"Unexpected error: {:?}",
|
||||
|
@ -370,7 +371,7 @@ async fn query_binds() {
|
|||
assert_eq!(record.name, "John Doe");
|
||||
let mut response = db
|
||||
.query("SELECT * FROM $record_id")
|
||||
.bind(("record_id", thing("user:john").unwrap()))
|
||||
.bind(("record_id", "user:john".parse::<RecordId>().unwrap()))
|
||||
.await
|
||||
.unwrap();
|
||||
let Some(record): Option<RecordName> = response.take(0).unwrap() else {
|
||||
|
@ -404,7 +405,7 @@ async fn query_with_stats() {
|
|||
// Second query statement
|
||||
let (stats, result) = response.take(1).unwrap();
|
||||
assert!(stats.execution_time > Some(Duration::ZERO));
|
||||
let _: Vec<RecordId> = result.unwrap();
|
||||
let _: Vec<ApiRecordId> = result.unwrap();
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
|
@ -432,7 +433,7 @@ async fn mixed_results_query() {
|
|||
let sql = "CREATE bar SET baz = rand('a'); CREATE foo;";
|
||||
let mut response = db.query(sql).await.unwrap();
|
||||
response.take::<Value>(0).unwrap_err();
|
||||
let _: Option<RecordId> = response.take(1).unwrap();
|
||||
let _: Option<ApiRecordId> = response.take(1).unwrap();
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
|
@ -440,7 +441,7 @@ async fn create_record_no_id() {
|
|||
let (permit, db) = new_db().await;
|
||||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let _: Vec<RecordId> = db.create("user").await.unwrap();
|
||||
let _: Vec<ApiRecordId> = db.create("user").await.unwrap();
|
||||
let _: Value = db.create(Resource::from("user")).await.unwrap();
|
||||
}
|
||||
|
||||
|
@ -449,7 +450,7 @@ async fn create_record_with_id() {
|
|||
let (permit, db) = new_db().await;
|
||||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let _: Option<RecordId> = db.create(("user", "jane")).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.create(("user", "jane")).await.unwrap();
|
||||
let _: Value = db.create(Resource::from(("user", "john"))).await.unwrap();
|
||||
let _: Value = db.create(Resource::from("user:doe")).await.unwrap();
|
||||
}
|
||||
|
@ -459,7 +460,7 @@ async fn create_record_no_id_with_content() {
|
|||
let (permit, db) = new_db().await;
|
||||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let _: Vec<RecordId> = db
|
||||
let _: Vec<ApiRecordId> = db
|
||||
.create("user")
|
||||
.content(Record {
|
||||
name: "John Doe".to_owned(),
|
||||
|
@ -480,22 +481,25 @@ async fn create_record_with_id_with_content() {
|
|||
let (permit, db) = new_db().await;
|
||||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let record: Option<RecordId> = db
|
||||
let record: Option<ApiRecordId> = db
|
||||
.create(("user", "john"))
|
||||
.content(Record {
|
||||
name: "John Doe".to_owned(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(record.unwrap().id, thing("user:john").unwrap());
|
||||
assert_eq!(record.unwrap().id, "user:john".parse::<RecordId>().unwrap());
|
||||
let value: Value = db
|
||||
.create(Resource::from("user:jane"))
|
||||
.create(Resource::from("user:jane".parse::<RecordId>().unwrap()))
|
||||
.content(Record {
|
||||
name: "Jane Doe".to_owned(),
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(value.record(), thing("user:jane").ok());
|
||||
assert_eq!(
|
||||
value.into_inner().record(),
|
||||
Some("user:jane".parse::<RecordId>().unwrap().into_inner())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
|
@ -504,14 +508,14 @@ async fn insert_table() {
|
|||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let table = "user";
|
||||
let _: Vec<RecordId> = db.insert(table).await.unwrap();
|
||||
let _: Vec<RecordId> = db.insert(table).content(json!({ "foo": "bar" })).await.unwrap();
|
||||
let _: Vec<RecordId> = db.insert(table).content(json!([{ "foo": "bar" }])).await.unwrap();
|
||||
let _: Vec<ApiRecordId> = db.insert(table).await.unwrap();
|
||||
let _: Vec<ApiRecordId> = db.insert(table).content(json!({ "foo": "bar" })).await.unwrap();
|
||||
let _: Vec<ApiRecordId> = db.insert(table).content(json!([{ "foo": "bar" }])).await.unwrap();
|
||||
let _: Value = db.insert(Resource::from(table)).await.unwrap();
|
||||
let _: Value = db.insert(Resource::from(table)).content(json!({ "foo": "bar" })).await.unwrap();
|
||||
let _: Value =
|
||||
db.insert(Resource::from(table)).content(json!([{ "foo": "bar" }])).await.unwrap();
|
||||
let users: Vec<RecordId> = db.insert(table).await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.insert(table).await.unwrap();
|
||||
assert!(!users.is_empty());
|
||||
}
|
||||
|
||||
|
@ -521,17 +525,17 @@ async fn insert_thing() {
|
|||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let table = "user";
|
||||
let _: Option<RecordId> = db.insert((table, "user1")).await.unwrap();
|
||||
let _: Option<RecordId> =
|
||||
let _: Option<ApiRecordId> = db.insert((table, "user1")).await.unwrap();
|
||||
let _: Option<ApiRecordId> =
|
||||
db.insert((table, "user1")).content(json!({ "foo": "bar" })).await.unwrap();
|
||||
let _: Value = db.insert(Resource::from((table, "user2"))).await.unwrap();
|
||||
let _: Value =
|
||||
db.insert(Resource::from((table, "user2"))).content(json!({ "foo": "bar" })).await.unwrap();
|
||||
let user: Option<RecordId> = db.insert((table, "user3")).await.unwrap();
|
||||
let user: Option<ApiRecordId> = db.insert((table, "user3")).await.unwrap();
|
||||
assert_eq!(
|
||||
user,
|
||||
Some(RecordId {
|
||||
id: thing("user:user3").unwrap(),
|
||||
Some(ApiRecordId {
|
||||
id: "user:user3".parse().unwrap(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
@ -542,10 +546,10 @@ async fn select_table() {
|
|||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let table = "user";
|
||||
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
||||
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
||||
let _: Vec<ApiRecordId> = db.create(table).await.unwrap();
|
||||
let _: Vec<ApiRecordId> = db.create(table).await.unwrap();
|
||||
let _: Value = db.create(Resource::from(table)).await.unwrap();
|
||||
let users: Vec<RecordId> = db.select(table).await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.select(table).await.unwrap();
|
||||
assert_eq!(users.len(), 3);
|
||||
}
|
||||
|
||||
|
@ -555,13 +559,16 @@ async fn select_record_id() {
|
|||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let record_id = ("user", "john");
|
||||
let _: Option<RecordId> = db.create(record_id).await.unwrap();
|
||||
let Some(record): Option<RecordId> = db.select(record_id).await.unwrap() else {
|
||||
let _: Option<ApiRecordId> = db.create(record_id).await.unwrap();
|
||||
let Some(record): Option<ApiRecordId> = db.select(record_id).await.unwrap() else {
|
||||
panic!("record not found");
|
||||
};
|
||||
assert_eq!(record.id, thing("user:john").unwrap());
|
||||
assert_eq!(record.id, "user:john".parse().unwrap());
|
||||
let value: Value = db.select(Resource::from(record_id)).await.unwrap();
|
||||
assert_eq!(value.record(), thing("user:john").ok());
|
||||
assert_eq!(
|
||||
value.into_inner().record(),
|
||||
Some("user:john".parse::<RecordId>().unwrap().into_inner())
|
||||
);
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
|
@ -570,32 +577,39 @@ async fn select_record_ranges() {
|
|||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let table = "user";
|
||||
let _: Option<RecordId> = db.create((table, "amos")).await.unwrap();
|
||||
let _: Option<RecordId> = db.create((table, "jane")).await.unwrap();
|
||||
let _: Option<RecordId> = db.create((table, "john")).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.create((table, "amos")).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.create((table, "jane")).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.create((table, "john")).await.unwrap();
|
||||
let _: Value = db.create(Resource::from((table, "zoey"))).await.unwrap();
|
||||
let convert = |users: Vec<RecordId>| -> Vec<String> {
|
||||
users.into_iter().map(|user| user.id.id.to_string()).collect()
|
||||
let convert = |users: Vec<ApiRecordId>| -> Vec<String> {
|
||||
users
|
||||
.into_iter()
|
||||
.map(|user| {
|
||||
let Id::String(ref x) = user.id.into_inner().id else {
|
||||
panic!()
|
||||
};
|
||||
x.clone()
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let users: Vec<RecordId> = db.select(table).range(..).await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.select(table).range(..).await.unwrap();
|
||||
assert_eq!(convert(users), vec!["amos", "jane", "john", "zoey"]);
|
||||
let users: Vec<RecordId> = db.select(table).range(.."john").await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.select(table).range(.."john").await.unwrap();
|
||||
assert_eq!(convert(users), vec!["amos", "jane"]);
|
||||
let users: Vec<RecordId> = db.select(table).range(..="john").await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.select(table).range(..="john").await.unwrap();
|
||||
assert_eq!(convert(users), vec!["amos", "jane", "john"]);
|
||||
let users: Vec<RecordId> = db.select(table).range("jane"..).await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.select(table).range("jane"..).await.unwrap();
|
||||
assert_eq!(convert(users), vec!["jane", "john", "zoey"]);
|
||||
let users: Vec<RecordId> = db.select(table).range("jane".."john").await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.select(table).range("jane".."john").await.unwrap();
|
||||
assert_eq!(convert(users), vec!["jane"]);
|
||||
let users: Vec<RecordId> = db.select(table).range("jane"..="john").await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.select(table).range("jane"..="john").await.unwrap();
|
||||
assert_eq!(convert(users), vec!["jane", "john"]);
|
||||
let Value::Array(array): Value =
|
||||
db.select(Resource::from(table)).range("jane"..="john").await.unwrap()
|
||||
else {
|
||||
unreachable!();
|
||||
let v: Value = db.select(Resource::from(table)).range("jane"..="john").await.unwrap();
|
||||
let CoreValue::Array(array) = v.into_inner() else {
|
||||
panic!()
|
||||
};
|
||||
assert_eq!(array.len(), 2);
|
||||
let users: Vec<RecordId> =
|
||||
let users: Vec<ApiRecordId> =
|
||||
db.select(table).range((Bound::Excluded("jane"), Bound::Included("john"))).await.unwrap();
|
||||
assert_eq!(convert(users), vec!["john"]);
|
||||
}
|
||||
|
@ -673,8 +687,8 @@ async fn select_records_fetch() {
|
|||
|
||||
let check_fetch = |mut response: Response, expected: &str| {
|
||||
let val: Value = response.take(0).unwrap();
|
||||
let exp = value(expected).unwrap();
|
||||
assert_eq!(format!("{val:#}"), format!("{exp:#}"));
|
||||
let exp = expected.parse().unwrap();
|
||||
assert_eq!(val, exp);
|
||||
};
|
||||
|
||||
let sql = "SELECT * FROM person LIMIT 1 FETCH tags;";
|
||||
|
@ -760,10 +774,10 @@ async fn update_table() {
|
|||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let table = "user";
|
||||
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
||||
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
||||
let _: Vec<ApiRecordId> = db.create(table).await.unwrap();
|
||||
let _: Vec<ApiRecordId> = db.create(table).await.unwrap();
|
||||
let _: Value = db.update(Resource::from(table)).await.unwrap();
|
||||
let users: Vec<RecordId> = db.update(table).await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.update(table).await.unwrap();
|
||||
assert_eq!(users.len(), 2);
|
||||
}
|
||||
|
||||
|
@ -773,9 +787,9 @@ async fn update_record_id() {
|
|||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let table = "user";
|
||||
let _: Option<RecordId> = db.create((table, "john")).await.unwrap();
|
||||
let _: Option<RecordId> = db.create((table, "jane")).await.unwrap();
|
||||
let users: Vec<RecordId> = db.update(table).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.create((table, "john")).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.create((table, "jane")).await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.update(table).await.unwrap();
|
||||
assert_eq!(users.len(), 2);
|
||||
}
|
||||
|
||||
|
@ -802,19 +816,19 @@ async fn update_table_with_content() {
|
|||
.unwrap();
|
||||
let expected = &[
|
||||
RecordBuf {
|
||||
id: thing("user:amos").unwrap(),
|
||||
id: "user:amos".parse().unwrap(),
|
||||
name: "Doe".to_owned(),
|
||||
},
|
||||
RecordBuf {
|
||||
id: thing("user:jane").unwrap(),
|
||||
id: "user:jane".parse().unwrap(),
|
||||
name: "Doe".to_owned(),
|
||||
},
|
||||
RecordBuf {
|
||||
id: thing("user:john").unwrap(),
|
||||
id: "user:john".parse().unwrap(),
|
||||
name: "Doe".to_owned(),
|
||||
},
|
||||
RecordBuf {
|
||||
id: thing("user:zoey").unwrap(),
|
||||
id: "user:zoey".parse().unwrap(),
|
||||
name: "Doe".to_owned(),
|
||||
},
|
||||
];
|
||||
|
@ -849,11 +863,11 @@ async fn update_record_range_with_content() {
|
|||
users,
|
||||
&[
|
||||
RecordBuf {
|
||||
id: thing("user:jane").unwrap(),
|
||||
id: "user:jane".parse().unwrap(),
|
||||
name: "Doe".to_owned(),
|
||||
},
|
||||
RecordBuf {
|
||||
id: thing("user:john").unwrap(),
|
||||
id: "user:john".parse().unwrap(),
|
||||
name: "Doe".to_owned(),
|
||||
},
|
||||
]
|
||||
|
@ -863,19 +877,19 @@ async fn update_record_range_with_content() {
|
|||
users,
|
||||
&[
|
||||
RecordBuf {
|
||||
id: thing("user:amos").unwrap(),
|
||||
id: "user:amos".parse().unwrap(),
|
||||
name: "Amos".to_owned(),
|
||||
},
|
||||
RecordBuf {
|
||||
id: thing("user:jane").unwrap(),
|
||||
id: "user:jane".parse().unwrap(),
|
||||
name: "Doe".to_owned(),
|
||||
},
|
||||
RecordBuf {
|
||||
id: thing("user:john").unwrap(),
|
||||
id: "user:john".parse().unwrap(),
|
||||
name: "Doe".to_owned(),
|
||||
},
|
||||
RecordBuf {
|
||||
id: thing("user:zoey").unwrap(),
|
||||
id: "user:zoey".parse().unwrap(),
|
||||
name: "Zoey".to_owned(),
|
||||
},
|
||||
]
|
||||
|
@ -914,10 +928,10 @@ struct Name {
|
|||
last: Cow<'static, str>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq, PartialOrd)]
|
||||
struct Person {
|
||||
#[serde(skip_serializing)]
|
||||
id: Option<Thing>,
|
||||
id: Option<RecordId>,
|
||||
title: Cow<'static, str>,
|
||||
name: Name,
|
||||
marketing: bool,
|
||||
|
@ -942,14 +956,14 @@ async fn merge_record_id() {
|
|||
})
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(jaime.unwrap().id.unwrap(), thing("person:jaime").unwrap());
|
||||
assert_eq!(jaime.unwrap().id.unwrap(), "person:jaime".parse().unwrap());
|
||||
jaime = db.update(record_id).merge(json!({ "marketing": true })).await.unwrap();
|
||||
assert!(jaime.as_ref().unwrap().marketing);
|
||||
jaime = db.select(record_id).await.unwrap();
|
||||
assert_eq!(
|
||||
jaime.unwrap(),
|
||||
Person {
|
||||
id: Some(thing("person:jaime").unwrap()),
|
||||
id: Some("person:jaime".parse().unwrap()),
|
||||
title: "Founder & COO".into(),
|
||||
name: Name {
|
||||
first: "Jaime".into(),
|
||||
|
@ -962,9 +976,9 @@ async fn merge_record_id() {
|
|||
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn patch_record_id() {
|
||||
#[derive(Debug, Deserialize, Eq, PartialEq)]
|
||||
#[derive(Debug, Deserialize, PartialEq)]
|
||||
struct Record {
|
||||
id: Thing,
|
||||
id: RecordId,
|
||||
baz: String,
|
||||
hello: Vec<String>,
|
||||
}
|
||||
|
@ -973,7 +987,7 @@ async fn patch_record_id() {
|
|||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let id = "john";
|
||||
let _: Option<RecordId> = db
|
||||
let _: Option<ApiRecordId> = db
|
||||
.create(("user", id))
|
||||
.content(json!({
|
||||
"baz": "qux",
|
||||
|
@ -992,7 +1006,7 @@ async fn patch_record_id() {
|
|||
assert_eq!(
|
||||
value,
|
||||
Some(Record {
|
||||
id: thing(&format!("user:{id}")).unwrap(),
|
||||
id: format!("user:{id}").parse().unwrap(),
|
||||
baz: "boo".to_owned(),
|
||||
hello: vec!["world".to_owned()],
|
||||
})
|
||||
|
@ -1005,14 +1019,14 @@ async fn delete_table() {
|
|||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let table = "user";
|
||||
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
||||
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
||||
let _: Vec<RecordId> = db.create(table).await.unwrap();
|
||||
let users: Vec<RecordId> = db.select(table).await.unwrap();
|
||||
let _: Vec<ApiRecordId> = db.create(table).await.unwrap();
|
||||
let _: Vec<ApiRecordId> = db.create(table).await.unwrap();
|
||||
let _: Vec<ApiRecordId> = db.create(table).await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.select(table).await.unwrap();
|
||||
assert_eq!(users.len(), 3);
|
||||
let users: Vec<RecordId> = db.delete(table).await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.delete(table).await.unwrap();
|
||||
assert_eq!(users.len(), 3);
|
||||
let users: Vec<RecordId> = db.select(table).await.unwrap();
|
||||
let users: Vec<ApiRecordId> = db.select(table).await.unwrap();
|
||||
assert!(users.is_empty());
|
||||
}
|
||||
|
||||
|
@ -1022,17 +1036,17 @@ async fn delete_record_id() {
|
|||
db.use_ns(NS).use_db(Ulid::new().to_string()).await.unwrap();
|
||||
drop(permit);
|
||||
let record_id = ("user", "john");
|
||||
let _: Option<RecordId> = db.create(record_id).await.unwrap();
|
||||
let _: Option<RecordId> = db.select(record_id).await.unwrap();
|
||||
let john: Option<RecordId> = db.delete(record_id).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.create(record_id).await.unwrap();
|
||||
let _: Option<ApiRecordId> = db.select(record_id).await.unwrap();
|
||||
let john: Option<ApiRecordId> = db.delete(record_id).await.unwrap();
|
||||
assert!(john.is_some());
|
||||
let john: Option<RecordId> = db.select(record_id).await.unwrap();
|
||||
let john: Option<ApiRecordId> = db.select(record_id).await.unwrap();
|
||||
assert!(john.is_none());
|
||||
// non-existing user
|
||||
let jane: Option<RecordId> = db.delete(("user", "jane")).await.unwrap();
|
||||
let jane: Option<ApiRecordId> = db.delete(("user", "jane")).await.unwrap();
|
||||
assert!(jane.is_none());
|
||||
let value = db.delete(Resource::from(("user", "jane"))).await.unwrap();
|
||||
assert_eq!(value, Value::None);
|
||||
let value: Value = db.delete(Resource::from(("user", "jane"))).await.unwrap();
|
||||
assert_eq!(value.into_inner(), CoreValue::None);
|
||||
}
|
||||
|
||||
#[test_log::test(tokio::test)]
|
||||
|
@ -1054,11 +1068,11 @@ async fn delete_record_range() {
|
|||
users,
|
||||
&[
|
||||
RecordBuf {
|
||||
id: thing("user:jane").unwrap(),
|
||||
id: "user:jane".parse().unwrap(),
|
||||
name: "Jane".to_owned(),
|
||||
},
|
||||
RecordBuf {
|
||||
id: thing("user:john").unwrap(),
|
||||
id: "user:john".parse().unwrap(),
|
||||
name: "John".to_owned(),
|
||||
},
|
||||
]
|
||||
|
@ -1068,11 +1082,11 @@ async fn delete_record_range() {
|
|||
users,
|
||||
&[
|
||||
RecordBuf {
|
||||
id: thing("user:amos").unwrap(),
|
||||
id: "user:amos".parse().unwrap(),
|
||||
name: "Amos".to_owned(),
|
||||
},
|
||||
RecordBuf {
|
||||
id: thing("user:zoey").unwrap(),
|
||||
id: "user:zoey".parse().unwrap(),
|
||||
name: "Zoey".to_owned(),
|
||||
},
|
||||
]
|
||||
|
@ -1108,11 +1122,11 @@ async fn changefeed() {
|
|||
.unwrap();
|
||||
let expected = &[
|
||||
RecordBuf {
|
||||
id: thing("user:amos").unwrap(),
|
||||
id: "user:amos".parse().unwrap(),
|
||||
name: "Doe".to_owned(),
|
||||
},
|
||||
RecordBuf {
|
||||
id: thing("user:jane").unwrap(),
|
||||
id: "user:jane".parse().unwrap(),
|
||||
name: "Doe".to_owned(),
|
||||
},
|
||||
];
|
||||
|
@ -1123,48 +1137,46 @@ async fn changefeed() {
|
|||
SHOW CHANGES FOR TABLE user SINCE 0 LIMIT 10;
|
||||
";
|
||||
let mut response = db.query(sql).await.unwrap();
|
||||
let value: Value = response.take(0).unwrap();
|
||||
let Value::Array(array) = value.clone() else {
|
||||
unreachable!()
|
||||
let v: Value = response.take(0).unwrap();
|
||||
let CoreValue::Array(array) = v.into_inner() else {
|
||||
panic!()
|
||||
};
|
||||
assert_eq!(array.len(), 5);
|
||||
// DEFINE TABLE
|
||||
let a = array.first().unwrap();
|
||||
let Value::Object(a) = a else {
|
||||
let CoreValue::Object(a) = a.clone() else {
|
||||
unreachable!()
|
||||
};
|
||||
let Value::Number(_versionstamp1) = a.get("versionstamp").unwrap() else {
|
||||
let CoreValue::Number(_versionstamp1) = a.get("versionstamp").clone().unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
let changes = a.get("changes").unwrap().to_owned();
|
||||
let changes = a.get("changes").unwrap().clone().to_owned();
|
||||
assert_eq!(
|
||||
changes,
|
||||
surrealdb::sql::value(
|
||||
"[
|
||||
Value::from_inner(changes),
|
||||
"[
|
||||
{
|
||||
define_table: {
|
||||
name: 'user'
|
||||
}
|
||||
}
|
||||
]"
|
||||
)
|
||||
.parse()
|
||||
.unwrap()
|
||||
);
|
||||
// UPDATE user:amos
|
||||
let a = array.get(1).unwrap();
|
||||
let Value::Object(a) = a else {
|
||||
let CoreValue::Object(a) = a.clone() else {
|
||||
unreachable!()
|
||||
};
|
||||
let Value::Number(versionstamp1) = a.get("versionstamp").unwrap() else {
|
||||
let CoreValue::Number(versionstamp1) = a.get("versionstamp").unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
let changes = a.get("changes").unwrap().to_owned();
|
||||
match FFLAGS.change_feed_live_queries.enabled() {
|
||||
true => {
|
||||
assert_eq!(
|
||||
changes,
|
||||
surrealdb::sql::value(
|
||||
r#"[
|
||||
Value::from_inner(changes),
|
||||
r#"[
|
||||
{
|
||||
create: {
|
||||
id: user:amos,
|
||||
|
@ -1172,15 +1184,14 @@ async fn changefeed() {
|
|||
}
|
||||
}
|
||||
]"#
|
||||
)
|
||||
.parse()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
false => {
|
||||
assert_eq!(
|
||||
changes,
|
||||
surrealdb::sql::value(
|
||||
r#"[
|
||||
Value::from_inner(changes),
|
||||
r#"[
|
||||
{
|
||||
update: {
|
||||
id: user:amos,
|
||||
|
@ -1188,27 +1199,26 @@ async fn changefeed() {
|
|||
}
|
||||
}
|
||||
]"#
|
||||
)
|
||||
.parse()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
// UPDATE user:jane
|
||||
let a = array.get(2).unwrap();
|
||||
let Value::Object(a) = a else {
|
||||
let CoreValue::Object(a) = a.clone() else {
|
||||
unreachable!()
|
||||
};
|
||||
let Value::Number(versionstamp2) = a.get("versionstamp").unwrap() else {
|
||||
let CoreValue::Number(versionstamp2) = a.get("versionstamp").unwrap().clone() else {
|
||||
unreachable!()
|
||||
};
|
||||
assert!(versionstamp1 < versionstamp2);
|
||||
assert!(*versionstamp1 < versionstamp2);
|
||||
let changes = a.get("changes").unwrap().to_owned();
|
||||
match FFLAGS.change_feed_live_queries.enabled() {
|
||||
true => {
|
||||
assert_eq!(
|
||||
changes,
|
||||
surrealdb::sql::value(
|
||||
"[
|
||||
Value::from_inner(changes),
|
||||
"[
|
||||
{
|
||||
create: {
|
||||
id: user:jane,
|
||||
|
@ -1216,15 +1226,14 @@ async fn changefeed() {
|
|||
}
|
||||
}
|
||||
]"
|
||||
)
|
||||
.parse()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
false => {
|
||||
assert_eq!(
|
||||
changes,
|
||||
surrealdb::sql::value(
|
||||
"[
|
||||
Value::from_inner(changes),
|
||||
"[
|
||||
{
|
||||
update: {
|
||||
id: user:jane,
|
||||
|
@ -1232,27 +1241,26 @@ async fn changefeed() {
|
|||
}
|
||||
}
|
||||
]"
|
||||
)
|
||||
.parse()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
// UPDATE user:amos
|
||||
let a = array.get(3).unwrap();
|
||||
let Value::Object(a) = a else {
|
||||
let CoreValue::Object(a) = a.clone() else {
|
||||
unreachable!()
|
||||
};
|
||||
let Value::Number(versionstamp3) = a.get("versionstamp").unwrap() else {
|
||||
let CoreValue::Number(versionstamp3) = a.get("versionstamp").unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
assert!(versionstamp2 < versionstamp3);
|
||||
assert!(versionstamp2 < *versionstamp3);
|
||||
let changes = a.get("changes").unwrap().to_owned();
|
||||
match FFLAGS.change_feed_live_queries.enabled() {
|
||||
true => {
|
||||
assert_eq!(
|
||||
changes,
|
||||
surrealdb::sql::value(
|
||||
"[
|
||||
Value::from_inner(changes),
|
||||
"[
|
||||
{
|
||||
create: {
|
||||
id: user:amos,
|
||||
|
@ -1260,15 +1268,14 @@ async fn changefeed() {
|
|||
}
|
||||
}
|
||||
]"
|
||||
)
|
||||
.parse()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
false => {
|
||||
assert_eq!(
|
||||
changes,
|
||||
surrealdb::sql::value(
|
||||
"[
|
||||
Value::from_inner(changes),
|
||||
"[
|
||||
{
|
||||
update: {
|
||||
id: user:amos,
|
||||
|
@ -1276,25 +1283,24 @@ async fn changefeed() {
|
|||
}
|
||||
}
|
||||
]"
|
||||
)
|
||||
.parse()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
};
|
||||
// UPDATE table
|
||||
let a = array.get(4).unwrap();
|
||||
let Value::Object(a) = a else {
|
||||
let CoreValue::Object(a) = a.clone() else {
|
||||
unreachable!()
|
||||
};
|
||||
let Value::Number(versionstamp4) = a.get("versionstamp").unwrap() else {
|
||||
let CoreValue::Number(versionstamp4) = a.get("versionstamp").unwrap() else {
|
||||
unreachable!()
|
||||
};
|
||||
assert!(versionstamp3 < versionstamp4);
|
||||
let changes = a.get("changes").unwrap().to_owned();
|
||||
assert_eq!(
|
||||
changes,
|
||||
surrealdb::sql::value(
|
||||
"[
|
||||
Value::from_inner(changes),
|
||||
"[
|
||||
{
|
||||
update: {
|
||||
id: user:amos,
|
||||
|
@ -1308,7 +1314,7 @@ async fn changefeed() {
|
|||
}
|
||||
}
|
||||
]"
|
||||
)
|
||||
.parse()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
@ -1354,9 +1360,12 @@ async fn return_bool() {
|
|||
assert!(boolean);
|
||||
let mut response = db.query("RETURN false").await.unwrap();
|
||||
let value: Value = response.take(0).unwrap();
|
||||
assert_eq!(value, Value::Bool(false));
|
||||
assert_eq!(value.into_inner(), CoreValue::Bool(false));
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Reenable test.
|
||||
* Disabling run test for now as it depends on value conversions which are removed
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn run() {
|
||||
let (permit, db) = new_db().await;
|
||||
|
@ -1395,3 +1404,4 @@ async fn run() {
|
|||
let tmp = db.run("fn::baz", ()).await.unwrap();
|
||||
assert_eq!(tmp, Value::from(7));
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -2,10 +2,10 @@ mod parse;
|
|||
use parse::Parse;
|
||||
mod helpers;
|
||||
use helpers::new_ds;
|
||||
use surrealdb::dbs::Session;
|
||||
use surrealdb::err::Error;
|
||||
use surrealdb::iam::Role;
|
||||
use surrealdb::sql::Value;
|
||||
use surrealdb_core::dbs::Session;
|
||||
use surrealdb_core::err::Error;
|
||||
use surrealdb_core::iam::Role;
|
||||
use surrealdb_core::sql::Value;
|
||||
|
||||
#[tokio::test]
|
||||
async fn select_field_value() -> Result<(), Error> {
|
||||
|
@ -866,6 +866,8 @@ async fn common_permissions_checks(auth_enabled: bool) {
|
|||
];
|
||||
let statement = "SELECT * FROM person";
|
||||
|
||||
let empty_array = Value::parse("[]");
|
||||
|
||||
for ((level, role), (ns, db), should_succeed, msg) in tests.into_iter() {
|
||||
let sess = Session::for_level(level, role).with_ns(ns).with_db(db);
|
||||
|
||||
|
@ -879,7 +881,7 @@ async fn common_permissions_checks(auth_enabled: bool) {
|
|||
.unwrap();
|
||||
let res = resp.remove(0).output();
|
||||
assert!(
|
||||
res.is_ok() && res.unwrap() != Value::parse("[]"),
|
||||
res.is_ok() && res.unwrap() != empty_array,
|
||||
"unexpected error creating person record"
|
||||
);
|
||||
let mut resp = ds
|
||||
|
@ -888,7 +890,7 @@ async fn common_permissions_checks(auth_enabled: bool) {
|
|||
.unwrap();
|
||||
let res = resp.remove(0).output();
|
||||
assert!(
|
||||
res.is_ok() && res.unwrap() != Value::parse("[]"),
|
||||
res.is_ok() && res.unwrap() != empty_array,
|
||||
"unexpected error creating person record"
|
||||
);
|
||||
let mut resp = ds
|
||||
|
@ -897,7 +899,7 @@ async fn common_permissions_checks(auth_enabled: bool) {
|
|||
.unwrap();
|
||||
let res = resp.remove(0).output();
|
||||
assert!(
|
||||
res.is_ok() && res.unwrap() != Value::parse("[]"),
|
||||
res.is_ok() && res.unwrap() != empty_array,
|
||||
"unexpected error creating person record"
|
||||
);
|
||||
|
||||
|
@ -909,9 +911,9 @@ async fn common_permissions_checks(auth_enabled: bool) {
|
|||
assert!(res.is_ok());
|
||||
|
||||
if should_succeed {
|
||||
assert!(res.unwrap() != Value::parse("[]"), "{}", msg);
|
||||
assert!(res.unwrap() != empty_array, "{}", msg);
|
||||
} else {
|
||||
assert!(res.unwrap() == Value::parse("[]"), "{}", msg);
|
||||
assert!(res.unwrap() == empty_array, "{}", msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,8 @@ use serde_json::ser::PrettyFormatter;
|
|||
use surrealdb::engine::any::{connect, IntoEndpoint};
|
||||
use surrealdb::method::{Stats, WithStats};
|
||||
use surrealdb::opt::{capabilities::Capabilities, Config};
|
||||
use surrealdb::sql::{self, Param, Statement, Value};
|
||||
use surrealdb::{Notification, Response};
|
||||
use surrealdb::sql::{self, Param, Statement, Uuid as CoreUuid, Value as CoreValue};
|
||||
use surrealdb::{Notification, Response, Value};
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct SqlCommandArguments {
|
||||
|
@ -207,7 +207,7 @@ pub async fn init(
|
|||
}
|
||||
|
||||
for var in &vars {
|
||||
query.push(Statement::Value(Value::Param(Param::from(var.as_str()))))
|
||||
query.push(Statement::Value(CoreValue::Param(Param::from(var.as_str()))))
|
||||
}
|
||||
|
||||
// Extract the namespace and database from the current prompt
|
||||
|
@ -284,7 +284,7 @@ fn process(
|
|||
format!("Expected some result for a query with index {index}, but found none")
|
||||
})
|
||||
.map_err(Error::Other)?;
|
||||
let output = result.unwrap_or_else(|e| e.to_string().into());
|
||||
let output = result.unwrap_or_else(|e| Value::from_inner(CoreValue::from(e.to_string())));
|
||||
vec.push((stats, output));
|
||||
}
|
||||
|
||||
|
@ -306,10 +306,10 @@ fn process(
|
|||
let message = match (json, pretty) {
|
||||
// Don't prettify the SurrealQL response
|
||||
(false, false) => {
|
||||
let value = Value::from(map! {
|
||||
String::from("id") => query_id.into(),
|
||||
let value = CoreValue::from(map! {
|
||||
String::from("id") => CoreValue::from(CoreUuid::from(query_id)),
|
||||
String::from("action") => format!("{action:?}").to_ascii_uppercase().into(),
|
||||
String::from("result") => data,
|
||||
String::from("result") => data.into_inner(),
|
||||
});
|
||||
value.to_string()
|
||||
}
|
||||
|
@ -319,10 +319,10 @@ fn process(
|
|||
),
|
||||
// Don't pretty print the JSON response
|
||||
(true, false) => {
|
||||
let value = Value::from(map! {
|
||||
String::from("id") => query_id.into(),
|
||||
let value = CoreValue::from(map! {
|
||||
String::from("id") => CoreValue::from(CoreUuid::from(query_id)),
|
||||
String::from("action") => format!("{action:?}").to_ascii_uppercase().into(),
|
||||
String::from("result") => data,
|
||||
String::from("result") => data.into_inner(),
|
||||
});
|
||||
value.into_json().to_string()
|
||||
}
|
||||
|
@ -333,7 +333,7 @@ fn process(
|
|||
&mut buf,
|
||||
PrettyFormatter::with_indent(b"\t"),
|
||||
);
|
||||
data.into_json().serialize(&mut serializer).unwrap();
|
||||
data.into_inner().into_json().serialize(&mut serializer).unwrap();
|
||||
let output = String::from_utf8(buf).unwrap();
|
||||
format!("-- Notification (action: {action:?}, live query ID: {query_id})\n{output:#}")
|
||||
}
|
||||
|
@ -346,7 +346,8 @@ fn process(
|
|||
Ok(match (json, pretty) {
|
||||
// Don't prettify the SurrealQL response
|
||||
(false, false) => {
|
||||
Value::from(vec.into_iter().map(|(_, x)| x).collect::<Vec<_>>()).to_string()
|
||||
CoreValue::from(vec.into_iter().map(|(_, x)| x.into_inner()).collect::<Vec<_>>())
|
||||
.to_string()
|
||||
}
|
||||
// Yes prettify the SurrealQL response
|
||||
(false, true) => vec
|
||||
|
@ -361,7 +362,8 @@ fn process(
|
|||
.join("\n"),
|
||||
// Don't pretty print the JSON response
|
||||
(true, false) => {
|
||||
let value = Value::from(vec.into_iter().map(|(_, x)| x).collect::<Vec<_>>());
|
||||
let value =
|
||||
CoreValue::from(vec.into_iter().map(|(_, x)| x.into_inner()).collect::<Vec<_>>());
|
||||
serde_json::to_string(&value.into_json()).unwrap()
|
||||
}
|
||||
// Yes prettify the JSON response
|
||||
|
@ -374,7 +376,7 @@ fn process(
|
|||
&mut buf,
|
||||
PrettyFormatter::with_indent(b"\t"),
|
||||
);
|
||||
value.into_json().serialize(&mut serializer).unwrap();
|
||||
value.into_inner().into_json().serialize(&mut serializer).unwrap();
|
||||
let output = String::from_utf8(buf).unwrap();
|
||||
let query_num = index + 1;
|
||||
let execution_time = stats.execution_time.unwrap_or_default();
|
||||
|
|
|
@ -395,8 +395,8 @@ impl RpcContext for Connection {
|
|||
&mut self.vars
|
||||
}
|
||||
|
||||
fn version_data(&self) -> impl Into<Data> {
|
||||
format!("{PKG_NAME}-{}", *PKG_VERSION)
|
||||
fn version_data(&self) -> Data {
|
||||
format!("{PKG_NAME}-{}", *PKG_VERSION).into()
|
||||
}
|
||||
|
||||
const LQ_SUPPORT: bool = true;
|
||||
|
|
|
@ -2,15 +2,16 @@ use std::collections::BTreeMap;
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::cnf::{PKG_NAME, PKG_VERSION};
|
||||
use surrealdb::dbs::Session;
|
||||
use surrealdb_core::dbs::Session;
|
||||
use surrealdb_core::kvs::Datastore;
|
||||
use surrealdb_core::rpc::Data;
|
||||
use surrealdb_core::rpc::RpcContext;
|
||||
use surrealdb_core::rpc::RpcError;
|
||||
use surrealdb_core::sql::Array;
|
||||
use surrealdb_core::sql::Value;
|
||||
|
||||
#[cfg(surrealdb_unstable)]
|
||||
use surrealdb::gql::{Pessimistic, SchemaCache};
|
||||
use surrealdb::kvs::Datastore;
|
||||
use surrealdb::rpc::Data;
|
||||
use surrealdb::rpc::RpcContext;
|
||||
use surrealdb::rpc::RpcError;
|
||||
use surrealdb::sql::Array;
|
||||
use surrealdb::sql::Value;
|
||||
use surrealdb_core::gql::{Pessimistic, SchemaCache};
|
||||
|
||||
pub struct PostRpcContext {
|
||||
pub kvs: Arc<Datastore>,
|
||||
|
@ -53,9 +54,8 @@ impl RpcContext for PostRpcContext {
|
|||
&mut self.vars
|
||||
}
|
||||
|
||||
fn version_data(&self) -> impl Into<Data> {
|
||||
let val: Value = format!("{PKG_NAME}-{}", *PKG_VERSION).into();
|
||||
val
|
||||
fn version_data(&self) -> Data {
|
||||
Value::from(format!("{PKG_NAME}-{}", *PKG_VERSION)).into()
|
||||
}
|
||||
|
||||
#[cfg(surrealdb_unstable)]
|
||||
|
@ -68,14 +68,12 @@ impl RpcContext for PostRpcContext {
|
|||
// disable:
|
||||
|
||||
// doesn't do anything so shouldn't be supported
|
||||
async fn set(&mut self, _params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
let out: Result<Value, RpcError> = Err(RpcError::MethodNotFound);
|
||||
out
|
||||
async fn set(&mut self, _params: Array) -> Result<Data, RpcError> {
|
||||
Err(RpcError::MethodNotFound)
|
||||
}
|
||||
|
||||
// doesn't do anything so shouldn't be supported
|
||||
async fn unset(&mut self, _params: Array) -> Result<impl Into<Data>, RpcError> {
|
||||
let out: Result<Value, RpcError> = Err(RpcError::MethodNotFound);
|
||||
out
|
||||
async fn unset(&mut self, _params: Array) -> Result<Data, RpcError> {
|
||||
Err(RpcError::MethodNotFound)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue