Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "lbug"
version = "0.16.0"
version = "0.17.0"
description = "An in-process property graph database management system built for query speed and scalability"
keywords = ["database", "graph", "ffi"]
readme = "lbug-src/README.md"
Expand Down Expand Up @@ -31,6 +31,7 @@ include = [
arrow = { version = "55", optional = true, default-features = false, features = ["ffi"] }
cxx = "=1.0.138" # the last version that builds on clang 15
rust_decimal = { version = "1.37", default-features = false }
serde_json = "1"
time = "0.3"
uuid = "1.6"

Expand Down
4 changes: 1 addition & 3 deletions include/lbug_rs.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,7 @@ inline void connection_set_query_timeout(lbug::main::Connection& connection, uin
/* PreparedStatement */
rust::String prepared_statement_error_message(const lbug::main::PreparedStatement& statement);
inline bool prepared_statement_is_read_only(const lbug::main::PreparedStatement& statement) {
lbug_prepared_statement c_statement{
const_cast<lbug::main::PreparedStatement*>(&statement), nullptr};
return lbug_prepared_statement_is_read_only(&c_statement);
return statement.isReadOnly();
}
inline bool prepared_statement_is_success(const lbug::main::PreparedStatement& statement) {
return statement.isSuccess();
Expand Down
10 changes: 10 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ pub enum Error {
FailedPreparedStatement(String),
/// Message produced when you attempt to pass read-only types over the FFI boundary
ReadOnlyType(LogicalType),
/// Message produced when JSON serialization fails
JsonError(serde_json::Error),
#[cfg(feature = "arrow")]
ArrowError(arrow::error::ArrowError),
}
Expand All @@ -25,6 +27,7 @@ impl std::fmt::Display for Error {
Error::ReadOnlyType(typ) => {
write!(f, "Attempted to pass read only type {typ:?} over ffi!")
}
Error::JsonError(err) => write!(f, "{err}"),
#[cfg(feature = "arrow")]
Error::ArrowError(err) => write!(f, "{err}"),
}
Expand All @@ -41,6 +44,7 @@ impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::CxxException(cxx) => Some(cxx),
Error::JsonError(err) => Some(err),
_ => None,
}
}
Expand All @@ -52,6 +56,12 @@ impl From<cxx::Exception> for Error {
}
}

impl From<serde_json::Error> for Error {
fn from(item: serde_json::Error) -> Self {
Error::JsonError(item)
}
}

#[cfg(feature = "arrow")]
impl From<arrow::error::ArrowError> for Error {
fn from(item: arrow::error::ArrowError) -> Self {
Expand Down
2 changes: 2 additions & 0 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub(crate) mod ffi {
UNION = 56,

UUID = 59,
JSON = 60,
}

// From types.h
Expand Down Expand Up @@ -113,6 +114,7 @@ pub(crate) mod ffi {

// Variable size types.
STRING = 20,
JSON = 21,
LIST = 22,
ARRAY = 23,
STRUCT = 24,
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub const VERSION: &str = env!("CARGO_PKG_VERSION");
///
/// This is `external` when `LBUG_LIBRARY_DIR`/`LBUG_INCLUDE_DIR` were supplied, `source` when the
/// bundled C++ source was built, or a value such as `run:LadybugDB/ladybug/25646256977` or
/// `release:LadybugDB/ladybug/v0.16.0` when a precompiled archive was downloaded.
/// `release:LadybugDB/ladybug/v0.17.0` when a precompiled archive was downloaded.
pub const LBUG_LIBRARY_SOURCE: &str = env!("LBUG_PRECOMPILED_SOURCE");
/// The directory containing the linked precompiled Lbug library, if one was used.
pub const LBUG_LIBRARY_DIR: &str = env!("LBUG_PRECOMPILED_LIBRARY_DIR");
Expand Down
7 changes: 6 additions & 1 deletion src/logical_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ pub enum LogicalType {
},
/// Correponds to [`Value::UUID`](crate::value::Value::UUID)
UUID,
/// Corresponds to [`Value::Json`](crate::value::Value::Json)
Json,
/// Correponds to [`Value::Decimal`](crate::value::Value::Decimal)
Decimal {
precision: u32,
Expand Down Expand Up @@ -188,6 +190,7 @@ impl From<&ffi::LogicalType> for LogicalType {
}
}
LogicalTypeID::UUID => LogicalType::UUID,
LogicalTypeID::JSON => LogicalType::Json,
LogicalTypeID::DECIMAL => {
let precision = ffi::logical_type_get_decimal_precision(logical_type);
let scale = ffi::logical_type_get_decimal_scale(logical_type);
Expand Down Expand Up @@ -230,7 +233,8 @@ impl From<&LogicalType> for cxx::UniquePtr<ffi::LogicalType> {
| LogicalType::Node
| LogicalType::Rel
| LogicalType::RecursiveRel
| LogicalType::UUID => ffi::create_logical_type(typ.id()),
| LogicalType::UUID
| LogicalType::Json => ffi::create_logical_type(typ.id()),
LogicalType::List { child_type } => {
ffi::create_logical_type_list(child_type.as_ref().into())
}
Expand Down Expand Up @@ -304,6 +308,7 @@ impl LogicalType {
LogicalType::Map { .. } => LogicalTypeID::MAP,
LogicalType::Union { .. } => LogicalTypeID::UNION,
LogicalType::UUID => LogicalTypeID::UUID,
LogicalType::Json => LogicalTypeID::JSON,
LogicalType::Decimal { .. } => LogicalTypeID::DECIMAL,
}
}
Expand Down
23 changes: 22 additions & 1 deletion src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub enum ConversionError {
TimestampNs(i64),
TimestampMs(i64),
TimestampSec(i64),
Json(String, serde_json::Error),
}

impl std::fmt::Display for ConversionError {
Expand All @@ -26,7 +27,7 @@ impl std::fmt::Display for ConversionError {
impl std::fmt::Debug for ConversionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ConversionError::{
Date, Timestamp, TimestampMs, TimestampNs, TimestampSec, TimestampTz,
Date, Json, Timestamp, TimestampMs, TimestampNs, TimestampSec, TimestampTz,
};
match self {
Date(days) => write!(f, "Could not convert Lbug date offset of UNIX_EPOCH + {days} days to time::Date"),
Expand All @@ -35,6 +36,7 @@ impl std::fmt::Debug for ConversionError {
TimestampNs(ns) => write!(f, "Could not convert Lbug timestamp_ns offset of UNIX_EPOCH + {ns} nanoseconds to time::OffsetDateTime"),
TimestampMs(ms) => write!(f, "Could not convert Lbug timestamp_ms offset of UNIX_EPOCH + {ms} milliseconds to time::OffsetDateTime"),
TimestampSec(sec) => write!(f, "Could not convert Lbug timestamp_sec offset of UNIX_EPOCH + {sec} seconds to time::OffsetDateTime"),
Json(value, err) => write!(f, "Could not convert Lbug JSON value {value:?}: {err}"),
}
}
}
Expand Down Expand Up @@ -237,6 +239,7 @@ pub enum Value {
InternalID(InternalID),
/// <https://ladybugdb.com/docusaurus/cypher/data-types/string.html>
String(String),
Json(serde_json::Value),
Blob(Vec<u8>),
// TODO: Enforce type of contents
// LogicalType is necessary so that we can pass the correct type to the C++ API if the list is empty.
Expand Down Expand Up @@ -296,6 +299,7 @@ impl std::fmt::Display for Value {
Value::Int128(x) => write!(f, "{x}"),
Value::Date(x) => write!(f, "{x}"),
Value::String(x) => write!(f, "{x}"),
Value::Json(x) => write!(f, "{x}"),
Value::Blob(x) => write!(f, "{x:x?}"),
Value::Null(_) => write!(f, ""),
Value::List(_, x) | Value::Array(_, x) => display_list(f, x),
Expand Down Expand Up @@ -369,6 +373,7 @@ impl From<&Value> for LogicalType {
Value::TimestampMs(_) => LogicalType::TimestampMs,
Value::TimestampSec(_) => LogicalType::TimestampSec,
Value::String(_) => LogicalType::String,
Value::Json(_) => LogicalType::Json,
Value::Blob(_) => LogicalType::Blob,
Value::Null(x) => x.clone(),
Value::List(x, _) => LogicalType::List {
Expand Down Expand Up @@ -458,6 +463,12 @@ impl TryFrom<&ffi::Value> for Value {
LogicalTypeID::FLOAT => Ok(Value::Float(ffi::value_get_float(value))),
LogicalTypeID::DOUBLE => Ok(Value::Double(ffi::value_get_double(value))),
LogicalTypeID::STRING => Ok(Value::String(ffi::value_get_string(value).to_string())),
LogicalTypeID::JSON => {
let json = ffi::value_get_string(value).to_string();
serde_json::from_str(&json)
.map(Value::Json)
.map_err(|err| ConversionError::Json(json, err))
}
LogicalTypeID::BLOB => Ok(Value::Blob(
ffi::value_get_string(value).as_bytes().to_vec(),
)),
Expand Down Expand Up @@ -754,6 +765,10 @@ impl TryInto<cxx::UniquePtr<ffi::Value>> for Value {
ffi::LogicalTypeID::STRING,
value.as_bytes(),
)),
Value::Json(value) => Ok(ffi::create_value_string(
ffi::LogicalTypeID::JSON,
&serde_json::to_vec(&value)?,
)),
Value::Blob(value) => Ok(ffi::create_value_string(ffi::LogicalTypeID::BLOB, &value)),
Value::Timestamp(value) => {
Ok(ffi::create_value_timestamp(datetime_to_timestamp_t(value)))
Expand Down Expand Up @@ -953,6 +968,12 @@ impl From<String> for Value {
}
}

impl From<serde_json::Value> for Value {
fn from(item: serde_json::Value) -> Self {
Value::Json(item)
}
}

impl From<&str> for Value {
fn from(item: &str) -> Self {
Value::String(item.to_string())
Expand Down
Loading