clippy happy

main
Ziyang Hu 2 years ago
parent b4e33af5c2
commit 341b94bbcd

@ -26,4 +26,4 @@ fn main() {
println!("cargo:rerun-if-changed=src/bridge.rs"); println!("cargo:rerun-if-changed=src/bridge.rs");
println!("cargo:rerun-if-changed=bridge/cozorocks.cc"); println!("cargo:rerun-if-changed=bridge/cozorocks.cc");
println!("cargo:rerun-if-changed=bridge/cozorocks.h"); println!("cargo:rerun-if-changed=bridge/cozorocks.h");
} }

@ -104,7 +104,9 @@ mod ffi {
fn new_transaction_options() -> UniquePtr<TransactionOptions>; fn new_transaction_options() -> UniquePtr<TransactionOptions>;
fn set_deadlock_detect(o: Pin<&mut TransactionOptions>, v: bool); fn set_deadlock_detect(o: Pin<&mut TransactionOptions>, v: bool);
type OptimisticTransactionOptions; type OptimisticTransactionOptions;
fn new_optimistic_transaction_options(cmp: &RustComparator) -> UniquePtr<OptimisticTransactionOptions>; fn new_optimistic_transaction_options(
cmp: &RustComparator,
) -> UniquePtr<OptimisticTransactionOptions>;
type TransactionDBOptions; type TransactionDBOptions;
fn new_tdb_options() -> UniquePtr<TransactionDBOptions>; fn new_tdb_options() -> UniquePtr<TransactionDBOptions>;
type OptimisticTransactionDBOptions; type OptimisticTransactionDBOptions;
@ -116,7 +118,11 @@ mod ffi {
fn set_allow_write_stall(o: Pin<&mut FlushOptions>, v: bool); fn set_allow_write_stall(o: Pin<&mut FlushOptions>, v: bool);
type RustComparator; type RustComparator;
fn new_rust_comparator(name: &str, cmp: fn(&[u8], &[u8]) -> i8, diff_bytes_can_equal: bool) -> UniquePtr<RustComparator>; fn new_rust_comparator(
name: &str,
cmp: fn(&[u8], &[u8]) -> i8,
diff_bytes_can_equal: bool,
) -> UniquePtr<RustComparator>;
pub type IteratorBridge; pub type IteratorBridge;
fn seek_to_first(self: &IteratorBridge); fn seek_to_first(self: &IteratorBridge);
@ -136,26 +142,76 @@ mod ffi {
fn set_savepoint(self: &TransactionBridge); fn set_savepoint(self: &TransactionBridge);
fn rollback_to_savepoint(self: &TransactionBridge, status: &mut BridgeStatus); fn rollback_to_savepoint(self: &TransactionBridge, status: &mut BridgeStatus);
fn pop_savepoint(self: &TransactionBridge, status: &mut BridgeStatus); fn pop_savepoint(self: &TransactionBridge, status: &mut BridgeStatus);
fn get_txn(self: &TransactionBridge, cf: &ColumnFamilyHandle, key: &[u8], fn get_txn(
status: &mut BridgeStatus) -> SharedPtr<PinnableSlice>; self: &TransactionBridge,
fn get_for_update_txn(self: &TransactionBridge, cf: &ColumnFamilyHandle, key: &[u8], cf: &ColumnFamilyHandle,
status: &mut BridgeStatus) -> SharedPtr<PinnableSlice>; key: &[u8],
fn get_raw(self: &TransactionBridge, cf: &ColumnFamilyHandle, key: &[u8], status: &mut BridgeStatus,
status: &mut BridgeStatus) -> SharedPtr<PinnableSlice>; ) -> SharedPtr<PinnableSlice>;
fn put_txn(self: &TransactionBridge, cf: &ColumnFamilyHandle, key: &[u8], val: &[u8], fn get_for_update_txn(
status: &mut BridgeStatus); self: &TransactionBridge,
fn put_raw(self: &TransactionBridge, cf: &ColumnFamilyHandle, key: &[u8], val: &[u8], cf: &ColumnFamilyHandle,
status: &mut BridgeStatus); key: &[u8],
fn del_txn(self: &TransactionBridge, cf: &ColumnFamilyHandle, key: &[u8], status: &mut BridgeStatus,
status: &mut BridgeStatus); ) -> SharedPtr<PinnableSlice>;
fn del_raw(self: &TransactionBridge, cf: &ColumnFamilyHandle, key: &[u8], fn get_raw(
status: &mut BridgeStatus); self: &TransactionBridge,
fn del_range_raw(self: &TransactionBridge, cf: &ColumnFamilyHandle, cf: &ColumnFamilyHandle,
start_key: &[u8], end_key: &[u8], status: &mut BridgeStatus); key: &[u8],
fn flush_raw(self: &TransactionBridge, cf: &ColumnFamilyHandle, options: &FlushOptions, status: &mut BridgeStatus); status: &mut BridgeStatus,
fn compact_all_raw(self: &TransactionBridge, cf: &ColumnFamilyHandle, status: &mut BridgeStatus); ) -> SharedPtr<PinnableSlice>;
fn iterator_txn(self: &TransactionBridge, cf: &ColumnFamilyHandle) -> UniquePtr<IteratorBridge>; fn put_txn(
fn iterator_raw(self: &TransactionBridge, cf: &ColumnFamilyHandle) -> UniquePtr<IteratorBridge>; self: &TransactionBridge,
cf: &ColumnFamilyHandle,
key: &[u8],
val: &[u8],
status: &mut BridgeStatus,
);
fn put_raw(
self: &TransactionBridge,
cf: &ColumnFamilyHandle,
key: &[u8],
val: &[u8],
status: &mut BridgeStatus,
);
fn del_txn(
self: &TransactionBridge,
cf: &ColumnFamilyHandle,
key: &[u8],
status: &mut BridgeStatus,
);
fn del_raw(
self: &TransactionBridge,
cf: &ColumnFamilyHandle,
key: &[u8],
status: &mut BridgeStatus,
);
fn del_range_raw(
self: &TransactionBridge,
cf: &ColumnFamilyHandle,
start_key: &[u8],
end_key: &[u8],
status: &mut BridgeStatus,
);
fn flush_raw(
self: &TransactionBridge,
cf: &ColumnFamilyHandle,
options: &FlushOptions,
status: &mut BridgeStatus,
);
fn compact_all_raw(
self: &TransactionBridge,
cf: &ColumnFamilyHandle,
status: &mut BridgeStatus,
);
fn iterator_txn(
self: &TransactionBridge,
cf: &ColumnFamilyHandle,
) -> UniquePtr<IteratorBridge>;
fn iterator_raw(
self: &TransactionBridge,
cf: &ColumnFamilyHandle,
) -> UniquePtr<IteratorBridge>;
// fn multiget_txn(self: &TransactionBridge, cf: &ColumnFamilyHandle, // fn multiget_txn(self: &TransactionBridge, cf: &ColumnFamilyHandle,
// keys: &[&[u8]], statuses: &mut [BridgeStatus]) -> UniquePtr<CxxVector<PinnableSlice>>; // keys: &[&[u8]], statuses: &mut [BridgeStatus]) -> UniquePtr<CxxVector<PinnableSlice>>;
// fn multiget_raw(self: &TransactionBridge, cf: &ColumnFamilyHandle, // fn multiget_raw(self: &TransactionBridge, cf: &ColumnFamilyHandle,
@ -164,109 +220,138 @@ mod ffi {
pub type ColumnFamilyHandle; pub type ColumnFamilyHandle;
type TDBBridge; type TDBBridge;
fn begin_t_transaction(self: &TDBBridge, fn begin_t_transaction(
w_ops: UniquePtr<WriteOptions>, self: &TDBBridge,
raw_w_ops: UniquePtr<WriteOptions>, w_ops: UniquePtr<WriteOptions>,
r_ops: UniquePtr<ReadOptions>, raw_w_ops: UniquePtr<WriteOptions>,
raw_r_ops: UniquePtr<ReadOptions>, r_ops: UniquePtr<ReadOptions>,
txn_options: UniquePtr<TransactionOptions>) -> UniquePtr<TransactionBridge>; raw_r_ops: UniquePtr<ReadOptions>,
fn begin_o_transaction(self: &TDBBridge, txn_options: UniquePtr<TransactionOptions>,
w_ops: UniquePtr<WriteOptions>, ) -> UniquePtr<TransactionBridge>;
raw_w_ops: UniquePtr<WriteOptions>, fn begin_o_transaction(
r_ops: UniquePtr<ReadOptions>, self: &TDBBridge,
raw_r_ops: UniquePtr<ReadOptions>, w_ops: UniquePtr<WriteOptions>,
txn_options: UniquePtr<OptimisticTransactionOptions>) -> UniquePtr<TransactionBridge>; raw_w_ops: UniquePtr<WriteOptions>,
r_ops: UniquePtr<ReadOptions>,
raw_r_ops: UniquePtr<ReadOptions>,
txn_options: UniquePtr<OptimisticTransactionOptions>,
) -> UniquePtr<TransactionBridge>;
fn get_cf_handle_raw(self: &TDBBridge, name: &CxxString) -> SharedPtr<ColumnFamilyHandle>; fn get_cf_handle_raw(self: &TDBBridge, name: &CxxString) -> SharedPtr<ColumnFamilyHandle>;
fn get_default_cf_handle_raw(self: &TDBBridge) -> SharedPtr<ColumnFamilyHandle>; fn get_default_cf_handle_raw(self: &TDBBridge) -> SharedPtr<ColumnFamilyHandle>;
fn create_column_family_raw(self: &TDBBridge, options: &Options, name: &CxxString, status: &mut BridgeStatus) -> SharedPtr<ColumnFamilyHandle>; fn create_column_family_raw(
self: &TDBBridge,
options: &Options,
name: &CxxString,
status: &mut BridgeStatus,
) -> SharedPtr<ColumnFamilyHandle>;
fn drop_column_family_raw(self: &TDBBridge, name: &CxxString, status: &mut BridgeStatus); fn drop_column_family_raw(self: &TDBBridge, name: &CxxString, status: &mut BridgeStatus);
fn get_column_family_names_raw(self: &TDBBridge) -> UniquePtr<CxxVector<CxxString>>; fn get_column_family_names_raw(self: &TDBBridge) -> UniquePtr<CxxVector<CxxString>>;
fn open_tdb_raw(options: &Options, fn open_tdb_raw(
txn_options: &TransactionDBOptions, options: &Options,
path: &CxxString, txn_options: &TransactionDBOptions,
status: &mut BridgeStatus) -> UniquePtr<TDBBridge>; path: &CxxString,
fn open_odb_raw(options: &Options, status: &mut BridgeStatus,
txn_options: &OptimisticTransactionDBOptions, ) -> UniquePtr<TDBBridge>;
path: &CxxString, fn open_odb_raw(
status: &mut BridgeStatus) -> UniquePtr<TDBBridge>; options: &Options,
txn_options: &OptimisticTransactionDBOptions,
path: &CxxString,
status: &mut BridgeStatus,
) -> UniquePtr<TDBBridge>;
} }
} }
pub use ffi::*; pub use ffi::*;
use std::fmt::Formatter; use std::fmt::Formatter;
impl std::fmt::Display for StatusBridgeCode { impl std::fmt::Display for StatusBridgeCode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match *self { write!(
StatusBridgeCode::OK => "Ok", f,
StatusBridgeCode::LOCK_ERROR => "LockError", "{}",
StatusBridgeCode::EXISTING_ERROR => "ExistingError", match *self {
StatusBridgeCode::NOT_FOUND_ERROR => "NotFoundError", StatusBridgeCode::OK => "Ok",
_ => "Unknown" StatusBridgeCode::LOCK_ERROR => "LockError",
}) StatusBridgeCode::EXISTING_ERROR => "ExistingError",
StatusBridgeCode::NOT_FOUND_ERROR => "NotFoundError",
_ => "Unknown",
}
)
} }
} }
impl std::fmt::Display for StatusCode { impl std::fmt::Display for StatusCode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match *self { write!(
StatusCode::kOk => "Ok", f,
StatusCode::kNotFound => "NotFound", "{}",
StatusCode::kCorruption => "Corruption", match *self {
StatusCode::kNotSupported => "NotSupported", StatusCode::kOk => "Ok",
StatusCode::kInvalidArgument => "InvalidArgument", StatusCode::kNotFound => "NotFound",
StatusCode::kIOError => "IoError", StatusCode::kCorruption => "Corruption",
StatusCode::kMergeInProgress => "MergeInProgress", StatusCode::kNotSupported => "NotSupported",
StatusCode::kIncomplete => "Incomplete", StatusCode::kInvalidArgument => "InvalidArgument",
StatusCode::kShutdownInProgress => "ShutdownInProgress", StatusCode::kIOError => "IoError",
StatusCode::kTimedOut => "TimedOut", StatusCode::kMergeInProgress => "MergeInProgress",
StatusCode::kAborted => "Aborted", StatusCode::kIncomplete => "Incomplete",
StatusCode::kBusy => "Busy", StatusCode::kShutdownInProgress => "ShutdownInProgress",
StatusCode::kExpired => "Expired", StatusCode::kTimedOut => "TimedOut",
StatusCode::kTryAgain => "TryAgain", StatusCode::kAborted => "Aborted",
StatusCode::kCompactionTooLarge => "CompactionTooLarge", StatusCode::kBusy => "Busy",
StatusCode::kColumnFamilyDropped => "ColumnFamilyDropped", StatusCode::kExpired => "Expired",
StatusCode::kMaxCode => "MaxCode", StatusCode::kTryAgain => "TryAgain",
_ => "Unknown" StatusCode::kCompactionTooLarge => "CompactionTooLarge",
}) StatusCode::kColumnFamilyDropped => "ColumnFamilyDropped",
StatusCode::kMaxCode => "MaxCode",
_ => "Unknown",
}
)
} }
} }
impl std::fmt::Display for StatusSubCode { impl std::fmt::Display for StatusSubCode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match *self { write!(
StatusSubCode::kNone => "None", f,
StatusSubCode::kMutexTimeout => "MutexTimeout", "{}",
StatusSubCode::kLockTimeout => "LockTimeout", match *self {
StatusSubCode::kLockLimit => "LockLimit", StatusSubCode::kNone => "None",
StatusSubCode::kNoSpace => "NoSpace", StatusSubCode::kMutexTimeout => "MutexTimeout",
StatusSubCode::kDeadlock => "DeadLock", StatusSubCode::kLockTimeout => "LockTimeout",
StatusSubCode::kStaleFile => "StaleFile", StatusSubCode::kLockLimit => "LockLimit",
StatusSubCode::kMemoryLimit => "MemoryLimit", StatusSubCode::kNoSpace => "NoSpace",
StatusSubCode::kSpaceLimit => "SpaceLimit", StatusSubCode::kDeadlock => "DeadLock",
StatusSubCode::kPathNotFound => "PathNotFound", StatusSubCode::kStaleFile => "StaleFile",
StatusSubCode::KMergeOperandsInsufficientCapacity => "MergeOperandsInsufficientCapacity", StatusSubCode::kMemoryLimit => "MemoryLimit",
StatusSubCode::kManualCompactionPaused => "ManualCompactionPaused", StatusSubCode::kSpaceLimit => "SpaceLimit",
StatusSubCode::kOverwritten => "Overwritten", StatusSubCode::kPathNotFound => "PathNotFound",
StatusSubCode::kTxnNotPrepared => "TxnNotPrepared", StatusSubCode::KMergeOperandsInsufficientCapacity =>
StatusSubCode::kIOFenced => "IoFenced", "MergeOperandsInsufficientCapacity",
StatusSubCode::kMaxSubCode => "MaxSubCode", StatusSubCode::kManualCompactionPaused => "ManualCompactionPaused",
_ => "Unknown" StatusSubCode::kOverwritten => "Overwritten",
}) StatusSubCode::kTxnNotPrepared => "TxnNotPrepared",
StatusSubCode::kIOFenced => "IoFenced",
StatusSubCode::kMaxSubCode => "MaxSubCode",
_ => "Unknown",
}
)
} }
} }
impl std::fmt::Display for StatusSeverity { impl std::fmt::Display for StatusSeverity {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match *self { write!(
StatusSeverity::kNoError => "NoError", f,
StatusSeverity::kSoftError => "SoftError", "{}",
StatusSeverity::kHardError => "HardError", match *self {
StatusSeverity::kFatalError => "FatalError", StatusSeverity::kNoError => "NoError",
StatusSeverity::kUnrecoverableError => "UnrecoverableError", StatusSeverity::kSoftError => "SoftError",
StatusSeverity::kMaxSeverity => "MaxSeverity", StatusSeverity::kHardError => "HardError",
_ => "Unknown" StatusSeverity::kFatalError => "FatalError",
}) StatusSeverity::kUnrecoverableError => "UnrecoverableError",
StatusSeverity::kMaxSeverity => "MaxSeverity",
_ => "Unknown",
}
)
} }
} }

@ -2,25 +2,28 @@ mod bridge;
use bridge::*; use bridge::*;
use std::fmt::{Display, Formatter};
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use cxx::{let_cxx_string};
pub use cxx::{UniquePtr, SharedPtr};
pub use bridge::BridgeStatus; pub use bridge::BridgeStatus;
pub use bridge::ColumnFamilyHandle;
pub use bridge::PinnableSlice;
pub use bridge::Slice;
pub use bridge::StatusBridgeCode; pub use bridge::StatusBridgeCode;
pub use bridge::StatusCode; pub use bridge::StatusCode;
pub use bridge::StatusSubCode;
pub use bridge::StatusSeverity; pub use bridge::StatusSeverity;
pub use bridge::Slice; pub use bridge::StatusSubCode;
pub use bridge::PinnableSlice; use cxx::let_cxx_string;
pub use bridge::ColumnFamilyHandle; pub use cxx::{SharedPtr, UniquePtr};
use std::fmt::Debug;
use std::fmt::{Display, Formatter};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
impl std::fmt::Display for BridgeStatus { impl std::fmt::Display for BridgeStatus {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "BridgeStatus({}, {}, {}, {})", self.code, self.subcode, self.severity, self.bridge_code) write!(
f,
"BridgeStatus({}, {}, {}, {})",
self.code, self.subcode, self.severity, self.bridge_code
)
} }
} }
@ -31,7 +34,11 @@ pub struct BridgeError {
impl Display for BridgeError { impl Display for BridgeError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "BridgeError({}, {}, {}, {})", self.status.code, self.status.subcode, self.status.severity, self.status.bridge_code) write!(
f,
"BridgeError({}, {}, {}, {})",
self.status.code, self.status.subcode, self.status.severity, self.status.bridge_code
)
} }
} }
@ -55,7 +62,7 @@ impl BridgeStatus {
let err: Option<BridgeError> = self.into(); let err: Option<BridgeError> = self.into();
match err { match err {
Some(e) => Err(e), Some(e) => Err(e),
None => Ok(data) None => Ok(data),
} }
} }
} }
@ -63,9 +70,10 @@ impl BridgeStatus {
impl From<BridgeStatus> for Option<BridgeError> { impl From<BridgeStatus> for Option<BridgeError> {
#[inline] #[inline]
fn from(s: BridgeStatus) -> Self { fn from(s: BridgeStatus) -> Self {
if s.severity == StatusSeverity::kNoError && if s.severity == StatusSeverity::kNoError
s.bridge_code == StatusBridgeCode::OK && && s.bridge_code == StatusBridgeCode::OK
s.code == StatusCode::kOk { && s.code == StatusCode::kOk
{
None None
} else { } else {
Some(BridgeError { status: s }) Some(BridgeError { status: s })
@ -85,12 +93,8 @@ impl AsRef<[u8]> for SlicePtr {
#[inline] #[inline]
fn as_ref(&self) -> &[u8] { fn as_ref(&self) -> &[u8] {
match self { match self {
SlicePtr::Plain(s) => { SlicePtr::Plain(s) => convert_slice_back(s),
convert_slice_back(s) SlicePtr::Pinnable(s) => convert_pinnable_slice_back(s),
}
SlicePtr::Pinnable(s) => {
convert_pinnable_slice_back(s)
}
} }
} }
} }
@ -183,7 +187,6 @@ impl OptionsPtr {
} }
} }
pub struct ReadOptionsPtr(UniquePtr<ReadOptions>); pub struct ReadOptionsPtr(UniquePtr<ReadOptions>);
impl Deref for ReadOptionsPtr { impl Deref for ReadOptionsPtr {
@ -202,7 +205,6 @@ impl DerefMut for ReadOptionsPtr {
} }
} }
impl ReadOptionsPtr { impl ReadOptionsPtr {
#[inline] #[inline]
pub fn default() -> Self { pub fn default() -> Self {
@ -457,8 +459,10 @@ impl<'a> IteratorPtr<'a> {
/// `next()` must not be called on the iterator when the returned value is still used /// `next()` must not be called on the iterator when the returned value is still used
pub unsafe fn pair(&self) -> Option<(SlicePtr, SlicePtr)> { pub unsafe fn pair(&self) -> Option<(SlicePtr, SlicePtr)> {
if self.is_valid() { if self.is_valid() {
Some((SlicePtr::Plain(IteratorBridge::key_raw(self)), Some((
SlicePtr::Plain(IteratorBridge::value_raw(self)))) SlicePtr::Plain(IteratorBridge::key_raw(self)),
SlicePtr::Plain(IteratorBridge::value_raw(self)),
))
} else { } else {
None None
} }
@ -479,7 +483,6 @@ impl Deref for TransactionPtr {
} }
} }
impl TransactionPtr { impl TransactionPtr {
#[inline] #[inline]
pub fn null() -> Self { pub fn null() -> Self {
@ -518,7 +521,12 @@ impl TransactionPtr {
status.check_err(()) status.check_err(())
} }
#[inline] #[inline]
pub fn get(&self, transact: bool, cf: &ColumnFamilyHandle, key: impl AsRef<[u8]>) -> Result<Option<SlicePtr>> { pub fn get(
&self,
transact: bool,
cf: &ColumnFamilyHandle,
key: impl AsRef<[u8]>,
) -> Result<Option<SlicePtr>> {
let mut status = BridgeStatus::default(); let mut status = BridgeStatus::default();
let res = if transact { let res = if transact {
let ret = self.get_txn(cf, key.as_ref(), &mut status); let ret = self.get_txn(cf, key.as_ref(), &mut status);
@ -529,18 +537,27 @@ impl TransactionPtr {
}; };
match res { match res {
Ok(r) => Ok(Some(r)), Ok(r) => Ok(Some(r)),
Err(e) if e.status.code == StatusCode::kNotFound => Ok(None), Err(e) if e.status.code == StatusCode::kNotFound => Ok(None),
res => res.map(|_| None) res => res.map(|_| None),
} }
} }
#[inline] #[inline]
pub fn get_for_update(&self, cf: &ColumnFamilyHandle, key: impl AsRef<[u8]>) -> Result<SlicePtr> { pub fn get_for_update(
&self,
cf: &ColumnFamilyHandle,
key: impl AsRef<[u8]>,
) -> Result<SlicePtr> {
let mut status = BridgeStatus::default(); let mut status = BridgeStatus::default();
let ret = self.get_for_update_txn(cf, key.as_ref(), &mut status); let ret = self.get_for_update_txn(cf, key.as_ref(), &mut status);
status.check_err(SlicePtr::Pinnable(ret)) status.check_err(SlicePtr::Pinnable(ret))
} }
#[inline] #[inline]
pub fn del(&self, transact: bool, cf: &ColumnFamilyHandle, key: impl AsRef<[u8]>) -> Result<()> { pub fn del(
&self,
transact: bool,
cf: &ColumnFamilyHandle,
key: impl AsRef<[u8]>,
) -> Result<()> {
let mut status = BridgeStatus::default(); let mut status = BridgeStatus::default();
if transact { if transact {
let ret = self.del_txn(cf, key.as_ref(), &mut status); let ret = self.del_txn(cf, key.as_ref(), &mut status);
@ -551,7 +568,12 @@ impl TransactionPtr {
} }
} }
#[inline] #[inline]
pub fn del_range(&self, cf: &ColumnFamilyHandle, start_key: impl AsRef<[u8]>, end_key: impl AsRef<[u8]>) -> Result<()> { pub fn del_range(
&self,
cf: &ColumnFamilyHandle,
start_key: impl AsRef<[u8]>,
end_key: impl AsRef<[u8]>,
) -> Result<()> {
let mut status = BridgeStatus::default(); let mut status = BridgeStatus::default();
let ret = self.del_range_raw(cf, start_key.as_ref(), end_key.as_ref(), &mut status); let ret = self.del_range_raw(cf, start_key.as_ref(), end_key.as_ref(), &mut status);
status.check_err(ret) status.check_err(ret)
@ -569,7 +591,13 @@ impl TransactionPtr {
status.check_err(()) status.check_err(())
} }
#[inline] #[inline]
pub fn put(&self, transact: bool, cf: &ColumnFamilyHandle, key: impl AsRef<[u8]>, val: impl AsRef<[u8]>) -> Result<()> { pub fn put(
&self,
transact: bool,
cf: &ColumnFamilyHandle,
key: impl AsRef<[u8]>,
val: impl AsRef<[u8]>,
) -> Result<()> {
let mut status = BridgeStatus::default(); let mut status = BridgeStatus::default();
if transact { if transact {
let ret = self.put_txn(cf, key.as_ref(), val.as_ref(), &mut status); let ret = self.put_txn(cf, key.as_ref(), val.as_ref(), &mut status);
@ -584,12 +612,12 @@ impl TransactionPtr {
if transact { if transact {
IteratorPtr { IteratorPtr {
inner: self.iterator_txn(cf), inner: self.iterator_txn(cf),
txn: PhantomData txn: PhantomData,
} }
} else { } else {
IteratorPtr { IteratorPtr {
inner: self.iterator_raw(cf), inner: self.iterator_raw(cf),
txn: PhantomData txn: PhantomData,
} }
} }
} }
@ -621,43 +649,44 @@ pub enum TDBOptions {
} }
impl DBPtr { impl DBPtr {
pub fn open(options: &OptionsPtr, t_options: &TDBOptions, path: impl AsRef<str>) -> Result<Self> { pub fn open(
options: &OptionsPtr,
t_options: &TDBOptions,
path: impl AsRef<str>,
) -> Result<Self> {
let_cxx_string!(cname = path.as_ref()); let_cxx_string!(cname = path.as_ref());
let mut status = BridgeStatus::default(); let mut status = BridgeStatus::default();
let ret = match t_options { let ret = match t_options {
TDBOptions::Pessimistic(o) => open_tdb_raw(options, o, &cname, &mut status), TDBOptions::Pessimistic(o) => open_tdb_raw(options, o, &cname, &mut status),
TDBOptions::Optimistic(o) => open_odb_raw(options, o, &cname, &mut status) TDBOptions::Optimistic(o) => open_odb_raw(options, o, &cname, &mut status),
}; };
status.check_err(Self(ret)) status.check_err(Self(ret))
} }
#[inline] #[inline]
pub fn make_transaction(&self, pub fn make_transaction(
options: TransactOptions, &self,
read_ops: ReadOptionsPtr, options: TransactOptions,
raw_read_ops: ReadOptionsPtr, read_ops: ReadOptionsPtr,
write_ops: WriteOptionsPtr, raw_read_ops: ReadOptionsPtr,
raw_write_ops: WriteOptionsPtr, write_ops: WriteOptionsPtr,
raw_write_ops: WriteOptionsPtr,
) -> TransactionPtr { ) -> TransactionPtr {
TransactionPtr(match options { TransactionPtr(match options {
TransactOptions::Optimistic(o) => { TransactOptions::Optimistic(o) => self.begin_o_transaction(
self.begin_o_transaction( write_ops.0,
write_ops.0, raw_write_ops.0,
raw_write_ops.0, read_ops.0,
read_ops.0, raw_read_ops.0,
raw_read_ops.0, o.0,
o.0, ),
) TransactOptions::Pessimistic(o) => self.begin_t_transaction(
} write_ops.0,
TransactOptions::Pessimistic(o) => { raw_write_ops.0,
self.begin_t_transaction( read_ops.0,
write_ops.0, raw_read_ops.0,
raw_write_ops.0, o.0,
read_ops.0, ),
raw_read_ops.0,
o.0,
)
}
}) })
} }
#[inline] #[inline]
@ -675,7 +704,11 @@ impl DBPtr {
self.get_default_cf_handle_raw() self.get_default_cf_handle_raw()
} }
#[inline] #[inline]
pub fn create_cf(&self, options: &OptionsPtr, name: impl AsRef<str>) -> Result<SharedPtr<ColumnFamilyHandle>> { pub fn create_cf(
&self,
options: &OptionsPtr,
name: impl AsRef<str>,
) -> Result<SharedPtr<ColumnFamilyHandle>> {
let_cxx_string!(name = name.as_ref()); let_cxx_string!(name = name.as_ref());
let mut status = BridgeStatus::default(); let mut status = BridgeStatus::default();
let ret = self.create_column_family_raw(options, &name, &mut status); let ret = self.create_column_family_raw(options, &name, &mut status);
@ -690,7 +723,10 @@ impl DBPtr {
} }
#[inline] #[inline]
pub fn cf_names(&self) -> Vec<String> { pub fn cf_names(&self) -> Vec<String> {
self.get_column_family_names_raw().iter().map(|v| v.to_string_lossy().to_string()).collect() self.get_column_family_names_raw()
.iter()
.map(|v| v.to_string_lossy().to_string())
.collect()
} }
pub fn drop_non_default_cfs(&self) { pub fn drop_non_default_cfs(&self) {
for name in self.cf_names() { for name in self.cf_names() {
@ -699,4 +735,4 @@ impl DBPtr {
} }
} }
} }
} }

@ -1,10 +1,10 @@
pub mod cnf_transform;
pub mod ddl;
pub mod engine; pub mod engine;
pub mod env;
pub mod eval; pub mod eval;
pub mod query; pub mod iterator;
pub mod mutation; pub mod mutation;
pub mod table;
pub mod plan; pub mod plan;
pub mod ddl; pub mod query;
pub mod env; pub mod table;
pub mod cnf_transform;
pub mod iterator;

@ -1,7 +1,7 @@
use std::collections::{BTreeSet};
use crate::db::table::TableId; use crate::db::table::TableId;
use crate::relation::value; use crate::relation::value;
use crate::relation::value::Value; use crate::relation::value::Value;
use std::collections::BTreeSet;
pub fn extract_tables(val: &Value) -> BTreeSet<TableId> { pub fn extract_tables(val: &Value) -> BTreeSet<TableId> {
let mut coll = BTreeSet::new(); let mut coll = BTreeSet::new();
@ -11,12 +11,12 @@ pub fn extract_tables(val: &Value) -> BTreeSet<TableId> {
fn do_extract_tables(val: &Value, coll: &mut BTreeSet<TableId>) { fn do_extract_tables(val: &Value, coll: &mut BTreeSet<TableId>) {
match val { match val {
Value::Null | Value::Null
Value::Bool(_) | | Value::Bool(_)
Value::Int(_) | | Value::Int(_)
Value::Float(_) | | Value::Float(_)
Value::Uuid(_) | | Value::Uuid(_)
Value::Text(_) => {} | Value::Text(_) => {}
Value::List(l) => { Value::List(l) => {
for v in l { for v in l {
do_extract_tables(v, coll); do_extract_tables(v, coll);
@ -59,7 +59,7 @@ fn do_cnf_transform(val: Value) -> (bool, Value) {
value::OP_OR => cnf_transform_or(args), value::OP_OR => cnf_transform_or(args),
value::OP_AND => cnf_transform_and(args), value::OP_AND => cnf_transform_and(args),
value::OP_NEGATE => cnf_transform_negate(args.into_iter().next().unwrap()), value::OP_NEGATE => cnf_transform_negate(args.into_iter().next().unwrap()),
_ => (false, Value::Apply(op, args)) _ => (false, Value::Apply(op, args)),
} }
} else { } else {
(false, val) (false, val)
@ -87,18 +87,21 @@ fn cnf_transform_or(args: Vec<Value>) -> (bool, Value) {
collected.push(Value::Apply(op, args)) collected.push(Value::Apply(op, args))
} }
} }
_ => collected.push(Value::Apply(op, args)) _ => collected.push(Value::Apply(op, args)),
} }
} else { } else {
collected.push(v); collected.push(v);
} }
} }
if let Some(to_and) = to_and { if let Some(to_and) = to_and {
let args = to_and.into_iter().map(|v| { let args = to_and
let mut to_or = collected.clone(); .into_iter()
to_or.push(v); .map(|v| {
Value::Apply(value::OP_OR.into(), to_or) let mut to_or = collected.clone();
}).collect(); to_or.push(v);
Value::Apply(value::OP_OR.into(), to_or)
})
.collect();
(true, Value::Apply(value::OP_AND.into(), args)) (true, Value::Apply(value::OP_AND.into(), args))
} else if collected.is_empty() { } else if collected.is_empty() {
(true, true.into()) (true, true.into())
@ -151,35 +154,66 @@ fn cnf_transform_negate(arg: Value) -> (bool, Value) {
new_args.push(v); new_args.push(v);
} }
match op.as_ref() { match op.as_ref() {
value::OP_OR => (true, Value::Apply(value::OP_AND.into(), new_args.into_iter().map(|v| { value::OP_OR => (
let (_, v) = do_cnf_transform(v); true,
Value::Apply(value::OP_NEGATE.into(), vec![v]) Value::Apply(
}).collect())), value::OP_AND.into(),
value::OP_AND => (true, Value::Apply(value::OP_OR.into(), new_args.into_iter().map(|v| { new_args
let (_, v) = do_cnf_transform(v); .into_iter()
Value::Apply(value::OP_NEGATE.into(), vec![v]) .map(|v| {
}).collect())), let (_, v) = do_cnf_transform(v);
value::OP_NEGATE => { Value::Apply(value::OP_NEGATE.into(), vec![v])
(true, new_args.into_iter().next().unwrap()) })
} .collect(),
_ => (changed, Value::Apply(value::OP_NEGATE.into(), vec![Value::Apply(op, new_args)])) ),
),
value::OP_AND => (
true,
Value::Apply(
value::OP_OR.into(),
new_args
.into_iter()
.map(|v| {
let (_, v) = do_cnf_transform(v);
Value::Apply(value::OP_NEGATE.into(), vec![v])
})
.collect(),
),
),
value::OP_NEGATE => (true, new_args.into_iter().next().unwrap()),
_ => (
changed,
Value::Apply(value::OP_NEGATE.into(), vec![Value::Apply(op, new_args)]),
),
} }
} else { } else {
(false, Value::Apply(value::OP_NEGATE.into(), vec![arg])) (false, Value::Apply(value::OP_NEGATE.into(), vec![arg]))
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::db::cnf_transform::cnf_transform; use crate::db::cnf_transform::cnf_transform;
use crate::relation::value::Value;
use crate::error::Result; use crate::error::Result;
use crate::relation::value::Value;
#[test] #[test]
fn test_cnf() -> Result<()> { fn test_cnf() -> Result<()> {
for s in ["a", "!a", "!!a", "!!!a", "!(b || c)", "a && (b && c)", "a && b || c", "a || b && c && d", for s in [
"a && (b || d && e)", "a && !b || c && !!d || !!!e && f", "(a || !b || !c) && (!d || e || f)", "(a || b) && c", "a || b"] { "a",
"!a",
"!!a",
"!!!a",
"!(b || c)",
"a && (b && c)",
"a && b || c",
"a || b && c && d",
"a && (b || d && e)",
"a && !b || c && !!d || !!!e && f",
"(a || !b || !c) && (!d || e || f)",
"(a || b) && c",
"a || b",
] {
let v = Value::parse_str(s)?; let v = Value::parse_str(s)?;
println!("{}", v); println!("{}", v);
let v2 = cnf_transform(v); let v2 = cnf_transform(v);
@ -188,4 +222,4 @@ mod tests {
Ok(()) Ok(())
} }
} }

@ -1,76 +1,109 @@
use std::collections::HashSet;
use pest::iterators::{Pair, Pairs};
use crate::db::engine::Session; use crate::db::engine::Session;
use crate::db::table::TableId; use crate::db::table::TableId;
use crate::relation::tuple::{OwnTuple, SliceTuple, Tuple};
use crate::relation::typing::Typing;
use crate::parser::Rule;
use crate::error::{CozoError, Result}; use crate::error::{CozoError, Result};
use crate::parser::text_identifier::build_name_in_def; use crate::parser::text_identifier::build_name_in_def;
use crate::parser::Rule;
use crate::relation::data::DataKind; use crate::relation::data::DataKind;
use crate::relation::tuple::{OwnTuple, SliceTuple, Tuple};
use crate::relation::typing::Typing;
use crate::relation::value::Value; use crate::relation::value::Value;
use pest::iterators::{Pair, Pairs};
use std::collections::HashSet;
const STORAGE_ID_START: i64 = 10000; const STORAGE_ID_START: i64 = 10000;
impl<'s> Session<'s> { impl<'s> Session<'s> {
pub fn encode_definable_key(&self, name: &str, in_root: bool) -> OwnTuple { pub fn encode_definable_key(&self, name: &str, in_root: bool) -> OwnTuple {
let depth_code = if in_root { 0 } else { self.get_stack_depth() as i64 }; let depth_code = if in_root {
0
} else {
self.get_stack_depth() as i64
};
let mut tuple = Tuple::with_null_prefix(); let mut tuple = Tuple::with_null_prefix();
tuple.push_str(name); tuple.push_str(name);
tuple.push_int(depth_code); tuple.push_int(depth_code);
tuple tuple
} }
fn parse_cols(&self, pair: Pair<Rule>) -> Result<(Typing, Typing)> { fn parse_cols(&self, pair: Pair<Rule>) -> Result<(Typing, Typing)> {
let col_res = pair.into_inner().map(|p| { let col_res = pair
let mut ps = p.into_inner(); .into_inner()
let mut name_ps = ps.next().unwrap().into_inner(); .map(|p| {
let is_key; let mut ps = p.into_inner();
let mut name_p = name_ps.next().unwrap(); let mut name_ps = ps.next().unwrap().into_inner();
match name_p.as_rule() { let is_key;
Rule::key_marker => { let mut name_p = name_ps.next().unwrap();
is_key = true; match name_p.as_rule() {
name_p = name_ps.next().unwrap(); Rule::key_marker => {
is_key = true;
name_p = name_ps.next().unwrap();
}
_ => is_key = false,
} }
_ => { is_key = false } let name = build_name_in_def(name_p, true)?;
} let type_p = Typing::from_pair(ps.next().unwrap(), Some(self))?;
let name = build_name_in_def(name_p, true)?; Ok((is_key, name, type_p))
let type_p = Typing::from_pair(ps.next().unwrap(), Some(self))?; })
Ok((is_key, name, type_p)) .collect::<Result<Vec<_>>>()?;
}).collect::<Result<Vec<_>>>()?;
let all_names = col_res.iter().map(|(_, n, _)| n).collect::<HashSet<_>>(); let all_names = col_res.iter().map(|(_, n, _)| n).collect::<HashSet<_>>();
if all_names.len() != col_res.len() { if all_names.len() != col_res.len() {
return Err(CozoError::DuplicateNames(col_res.iter().map(|(_, n, _)| n.to_string()).collect::<Vec<_>>())); return Err(CozoError::DuplicateNames(
col_res
.iter()
.map(|(_, n, _)| n.to_string())
.collect::<Vec<_>>(),
));
} }
let (keys, cols): (Vec<_>, Vec<_>) = col_res.iter().partition(|(is_key, _, _)| *is_key); let (keys, cols): (Vec<_>, Vec<_>) = col_res.iter().partition(|(is_key, _, _)| *is_key);
let keys_typing = Typing::NamedTuple(keys.iter().map(|(_, n, t)| (n.to_string(), t.clone())).collect()); let keys_typing = Typing::NamedTuple(
let vals_typing = Typing::NamedTuple(cols.iter().map(|(_, n, t)| (n.to_string(), t.clone())).collect()); keys.iter()
.map(|(_, n, t)| (n.to_string(), t.clone()))
.collect(),
);
let vals_typing = Typing::NamedTuple(
cols.iter()
.map(|(_, n, t)| (n.to_string(), t.clone()))
.collect(),
);
Ok((keys_typing, vals_typing)) Ok((keys_typing, vals_typing))
} }
fn parse_definition(&self, pair: Pair<Rule>, in_root: bool) -> Result<(bool, (String, OwnTuple, Vec<OwnTuple>))> { #[allow(clippy::type_complexity)]
fn parse_definition(
&self,
pair: Pair<Rule>,
in_root: bool,
) -> Result<(bool, (String, OwnTuple, Vec<OwnTuple>))> {
Ok(match pair.as_rule() { Ok(match pair.as_rule() {
Rule::node_def => (true, self.parse_node_def(pair.into_inner(), in_root)?), Rule::node_def => (true, self.parse_node_def(pair.into_inner(), in_root)?),
Rule::edge_def => (true, self.parse_edge_def(pair.into_inner(), in_root)?), Rule::edge_def => (true, self.parse_edge_def(pair.into_inner(), in_root)?),
Rule::associate_def => (true, self.parse_assoc_def(pair.into_inner(), in_root)?), Rule::associate_def => (true, self.parse_assoc_def(pair.into_inner(), in_root)?),
Rule::index_def => todo!(), Rule::index_def => todo!(),
Rule::type_def => (false, self.parse_type_def(pair.into_inner(), in_root)?), Rule::type_def => (false, self.parse_type_def(pair.into_inner(), in_root)?),
_ => unreachable!() _ => unreachable!(),
}) })
} }
fn parse_assoc_def(&self, mut pairs: Pairs<Rule>, in_root: bool) -> Result<(String, OwnTuple, Vec<OwnTuple>)> { fn parse_assoc_def(
&self,
mut pairs: Pairs<Rule>,
in_root: bool,
) -> Result<(String, OwnTuple, Vec<OwnTuple>)> {
let name = build_name_in_def(pairs.next().unwrap(), true)?; let name = build_name_in_def(pairs.next().unwrap(), true)?;
let src_name = build_name_in_def(pairs.next().unwrap(), true)?; let src_name = build_name_in_def(pairs.next().unwrap(), true)?;
let src_tbl = match self.resolve(&src_name)? { let src_tbl = match self.resolve(&src_name)? {
Some(res) => res, Some(res) => res,
None => return Err(CozoError::UndefinedType(src_name)) None => return Err(CozoError::UndefinedType(src_name)),
}; };
let (_kind, src_global, src_id) = Self::extract_table_id(src_tbl)?; let (_kind, src_global, src_id) = Self::extract_table_id(src_tbl)?;
if in_root && !src_global { if in_root && !src_global {
return Err(CozoError::LogicError("Cannot have global edge with local nodes".to_string())); return Err(CozoError::LogicError(
"Cannot have global edge with local nodes".to_string(),
));
} }
let (keys_typing, vals_typing) = self.parse_cols(pairs.next().unwrap())?; let (keys_typing, vals_typing) = self.parse_cols(pairs.next().unwrap())?;
if keys_typing.to_string() != "{}" { if keys_typing.to_string() != "{}" {
return Err(CozoError::LogicError("Cannot have keys in assoc".to_string())); return Err(CozoError::LogicError(
"Cannot have keys in assoc".to_string(),
));
} }
let mut tuple = Tuple::with_data_prefix(DataKind::Assoc); let mut tuple = Tuple::with_data_prefix(DataKind::Assoc);
@ -81,20 +114,32 @@ impl<'s> Session<'s> {
let mut for_src = Tuple::with_prefix(0); let mut for_src = Tuple::with_prefix(0);
for_src.push_null(); for_src.push_null();
for_src.push_str(&src_name); for_src.push_str(&src_name);
for_src.push_int(if in_root { 0 } else { self.get_stack_depth() as i64 }); for_src.push_int(if in_root {
0
} else {
self.get_stack_depth() as i64
});
for_src.push_int(DataKind::Assoc as i64); for_src.push_int(DataKind::Assoc as i64);
for_src.push_str(&name); for_src.push_str(&name);
let mut for_src_i = Tuple::with_prefix(0); let mut for_src_i = Tuple::with_prefix(0);
for_src_i.push_null(); for_src_i.push_null();
for_src_i.push_int(if in_root { 0 } else { self.get_stack_depth() as i64 }); for_src_i.push_int(if in_root {
0
} else {
self.get_stack_depth() as i64
});
for_src_i.push_str(&src_name); for_src_i.push_str(&src_name);
for_src_i.push_int(DataKind::Assoc as i64); for_src_i.push_int(DataKind::Assoc as i64);
for_src_i.push_str(&name); for_src_i.push_str(&name);
Ok((name, tuple, vec![for_src, for_src_i])) Ok((name, tuple, vec![for_src, for_src_i]))
} }
fn parse_type_def(&self, mut pairs: Pairs<Rule>, _in_root: bool) -> Result<(String, OwnTuple, Vec<OwnTuple>)> { fn parse_type_def(
&self,
mut pairs: Pairs<Rule>,
_in_root: bool,
) -> Result<(String, OwnTuple, Vec<OwnTuple>)> {
let name = build_name_in_def(pairs.next().unwrap(), true)?; let name = build_name_in_def(pairs.next().unwrap(), true)?;
let typ = Typing::from_pair(pairs.next().unwrap(), Some(self))?; let typ = Typing::from_pair(pairs.next().unwrap(), Some(self))?;
let mut data = Tuple::with_data_prefix(DataKind::Type); let mut data = Tuple::with_data_prefix(DataKind::Type);
@ -102,15 +147,21 @@ impl<'s> Session<'s> {
Ok((name, data, vec![])) Ok((name, data, vec![]))
} }
fn parse_edge_def(&self, mut pairs: Pairs<Rule>, in_root: bool) -> Result<(String, OwnTuple, Vec<OwnTuple>)> { fn parse_edge_def(
&self,
mut pairs: Pairs<Rule>,
in_root: bool,
) -> Result<(String, OwnTuple, Vec<OwnTuple>)> {
let src_name = build_name_in_def(pairs.next().unwrap(), true)?; let src_name = build_name_in_def(pairs.next().unwrap(), true)?;
let src_tbl = match self.resolve(&src_name)? { let src_tbl = match self.resolve(&src_name)? {
Some(res) => res, Some(res) => res,
None => return Err(CozoError::UndefinedType(src_name)) None => return Err(CozoError::UndefinedType(src_name)),
}; };
let (kind, src_global, src_id) = Self::extract_table_id(src_tbl)?; let (kind, src_global, src_id) = Self::extract_table_id(src_tbl)?;
if in_root && !src_global { if in_root && !src_global {
return Err(CozoError::LogicError("Cannot have global edge with local nodes".to_string())); return Err(CozoError::LogicError(
"Cannot have global edge with local nodes".to_string(),
));
} }
if kind != DataKind::Node { if kind != DataKind::Node {
return Err(CozoError::UnexpectedDataKind(kind)); return Err(CozoError::UnexpectedDataKind(kind));
@ -119,18 +170,20 @@ impl<'s> Session<'s> {
let dst_name = build_name_in_def(pairs.next().unwrap(), true)?; let dst_name = build_name_in_def(pairs.next().unwrap(), true)?;
let dst_tbl = match self.resolve(&dst_name)? { let dst_tbl = match self.resolve(&dst_name)? {
Some(res) => res, Some(res) => res,
None => return Err(CozoError::UndefinedType(dst_name)) None => return Err(CozoError::UndefinedType(dst_name)),
}; };
let (kind, dst_global, dst_id) = Self::extract_table_id(dst_tbl)?; let (kind, dst_global, dst_id) = Self::extract_table_id(dst_tbl)?;
if in_root && !dst_global { if in_root && !dst_global {
return Err(CozoError::LogicError("Cannot have global edge with local nodes".to_string())); return Err(CozoError::LogicError(
"Cannot have global edge with local nodes".to_string(),
));
} }
if kind != DataKind::Node { if kind != DataKind::Node {
return Err(CozoError::UnexpectedDataKind(kind)); return Err(CozoError::UnexpectedDataKind(kind));
} }
let (keys_typing, vals_typing) = match pairs.next() { let (keys_typing, vals_typing) = match pairs.next() {
Some(p) => self.parse_cols(p)?, Some(p) => self.parse_cols(p)?,
None => (Typing::NamedTuple(vec![]), Typing::NamedTuple(vec![])) None => (Typing::NamedTuple(vec![]), Typing::NamedTuple(vec![])),
}; };
let mut tuple = Tuple::with_data_prefix(DataKind::Edge); let mut tuple = Tuple::with_data_prefix(DataKind::Edge);
@ -148,7 +201,11 @@ impl<'s> Session<'s> {
let mut for_src = Tuple::with_prefix(0); let mut for_src = Tuple::with_prefix(0);
for_src.push_null(); for_src.push_null();
for_src.push_str(&src_name); for_src.push_str(&src_name);
for_src.push_int(if in_root { 0 } else { self.get_stack_depth() as i64 }); for_src.push_int(if in_root {
0
} else {
self.get_stack_depth() as i64
});
for_src.push_int(DataKind::Edge as i64); for_src.push_int(DataKind::Edge as i64);
for_src.push_str(&name); for_src.push_str(&name);
@ -156,7 +213,11 @@ impl<'s> Session<'s> {
let mut for_src_i = Tuple::with_prefix(0); let mut for_src_i = Tuple::with_prefix(0);
for_src_i.push_null(); for_src_i.push_null();
for_src_i.push_int(if in_root { 0 } else { self.get_stack_depth() as i64 }); for_src_i.push_int(if in_root {
0
} else {
self.get_stack_depth() as i64
});
for_src_i.push_str(&src_name); for_src_i.push_str(&src_name);
for_src_i.push_int(DataKind::Edge as i64); for_src_i.push_int(DataKind::Edge as i64);
for_src_i.push_str(&name); for_src_i.push_str(&name);
@ -167,7 +228,11 @@ impl<'s> Session<'s> {
let mut for_dst = Tuple::with_prefix(0); let mut for_dst = Tuple::with_prefix(0);
for_dst.push_null(); for_dst.push_null();
for_dst.push_str(&dst_name); for_dst.push_str(&dst_name);
for_dst.push_int(if in_root { 0 } else { self.get_stack_depth() as i64 }); for_dst.push_int(if in_root {
0
} else {
self.get_stack_depth() as i64
});
for_dst.push_int(DataKind::Edge as i64); for_dst.push_int(DataKind::Edge as i64);
for_dst.push_str(&name); for_dst.push_str(&name);
@ -175,7 +240,11 @@ impl<'s> Session<'s> {
let mut for_dst_i = Tuple::with_prefix(0); let mut for_dst_i = Tuple::with_prefix(0);
for_dst_i.push_null(); for_dst_i.push_null();
for_dst_i.push_int(if in_root { 0 } else { self.get_stack_depth() as i64 }); for_dst_i.push_int(if in_root {
0
} else {
self.get_stack_depth() as i64
});
for_dst_i.push_str(&dst_name); for_dst_i.push_str(&dst_name);
for_dst_i.push_int(DataKind::Edge as i64); for_dst_i.push_int(DataKind::Edge as i64);
for_dst_i.push_str(&name); for_dst_i.push_str(&name);
@ -189,14 +258,20 @@ impl<'s> Session<'s> {
fn extract_table_id<T: AsRef<[u8]>>(src_tbl: Tuple<T>) -> Result<(DataKind, bool, i64)> { fn extract_table_id<T: AsRef<[u8]>>(src_tbl: Tuple<T>) -> Result<(DataKind, bool, i64)> {
let kind = src_tbl.data_kind()?; let kind = src_tbl.data_kind()?;
match kind { match kind {
DataKind::Data | DataKind::Val | DataKind::Type => return Err(CozoError::UnexpectedDataKind(kind)), DataKind::Data | DataKind::Val | DataKind::Type => {
return Err(CozoError::UnexpectedDataKind(kind))
}
_ => {} _ => {}
}; };
let is_global = src_tbl.get_bool(0).expect("Data corrupt"); let is_global = src_tbl.get_bool(0).expect("Data corrupt");
let table_id = src_tbl.get_int(1).expect("Data corrupt"); let table_id = src_tbl.get_int(1).expect("Data corrupt");
Ok((kind, is_global, table_id)) Ok((kind, is_global, table_id))
} }
fn parse_node_def(&self, mut pairs: Pairs<Rule>, _in_root: bool) -> Result<(String, OwnTuple, Vec<OwnTuple>)> { fn parse_node_def(
&self,
mut pairs: Pairs<Rule>,
_in_root: bool,
) -> Result<(String, OwnTuple, Vec<OwnTuple>)> {
let name = build_name_in_def(pairs.next().unwrap(), true)?; let name = build_name_in_def(pairs.next().unwrap(), true)?;
let col_pair = pairs.next().unwrap(); let col_pair = pairs.next().unwrap();
let (keys_typing, vals_typing) = self.parse_cols(col_pair)?; let (keys_typing, vals_typing) = self.parse_cols(col_pair)?;
@ -211,16 +286,14 @@ impl<'s> Session<'s> {
let in_root = match pair.as_rule() { let in_root = match pair.as_rule() {
Rule::global_def => true, Rule::global_def => true,
Rule::local_def => false, Rule::local_def => false,
r => panic!("Encountered definition with rule {:?}", r) r => panic!("Encountered definition with rule {:?}", r),
}; };
let (need_id, (name, mut tuple, assoc_defs)) = self.parse_definition( let (need_id, (name, mut tuple, assoc_defs)) =
pair.into_inner().next().unwrap(), in_root, self.parse_definition(pair.into_inner().next().unwrap(), in_root)?;
)?;
if need_id { if need_id {
let id = self.get_next_storage_id(in_root)?; let id = self.get_next_storage_id(in_root)?;
tuple = tuple.insert_values_at(0, &[in_root.into(), tuple = tuple.insert_values_at(0, &[in_root.into(), id.into()]);
id.into()]);
let mut id_key = Tuple::with_null_prefix(); let mut id_key = Tuple::with_null_prefix();
id_key.push_bool(true); id_key.push_bool(true);
id_key.push_int(id); id_key.push_int(id);
@ -232,7 +305,6 @@ impl<'s> Session<'s> {
self.define_data(&name, tuple, in_root) self.define_data(&name, tuple, in_root)
} }
fn get_next_storage_id(&self, in_root: bool) -> Result<i64> { fn get_next_storage_id(&self, in_root: bool) -> Result<i64> {
let mut key_entry = Tuple::with_null_prefix(); let mut key_entry = Tuple::with_null_prefix();
key_entry.push_null(); key_entry.push_null();
@ -247,7 +319,9 @@ impl<'s> Session<'s> {
} else { } else {
panic!("Unexpected value in storage id"); panic!("Unexpected value in storage id");
} }
} else { STORAGE_ID_START }; } else {
STORAGE_ID_START
};
let mut new_data = Tuple::with_null_prefix(); let mut new_data = Tuple::with_null_prefix();
new_data.push_int(u + 1); new_data.push_int(u + 1);
if in_root { if in_root {
@ -290,7 +364,9 @@ impl<'s> Session<'s> {
if !cur.starts_with(&prefix) { if !cur.starts_with(&prefix) {
break; break;
} }
let name = cur.get_text(4).ok_or_else(|| CozoError::LogicError("Bad data".to_string()))?; let name = cur
.get_text(4)
.ok_or_else(|| CozoError::LogicError("Bad data".to_string()))?;
if let Some(data) = self.resolve(&name)? { if let Some(data) = self.resolve(&name)? {
if data.data_kind()? == DataKind::Assoc { if data.data_kind()? == DataKind::Assoc {
assocs.push((name.to_string(), data)); assocs.push((name.to_string(), data));
@ -306,7 +382,9 @@ impl<'s> Session<'s> {
if !cur.starts_with(&prefix) { if !cur.starts_with(&prefix) {
break; break;
} }
let name = cur.get_text(4).ok_or_else(|| CozoError::LogicError("Bad data".to_string()))?; let name = cur
.get_text(4)
.ok_or_else(|| CozoError::LogicError("Bad data".to_string()))?;
if let Some(data) = self.resolve(&name)? { if let Some(data) = self.resolve(&name)? {
if data.data_kind()? == DataKind::Assoc { if data.data_kind()? == DataKind::Assoc {
assocs.push((name.to_string(), data)); assocs.push((name.to_string(), data));
@ -340,4 +418,4 @@ impl<'s> Session<'s> {
Ok(()) Ok(())
} }
} }

@ -1,16 +1,15 @@
// single engine per db storage // single engine per db storage
// will be shared among threads // will be shared among threads
use crate::error::CozoError::{Poisoned, SessionErr};
use crate::error::{CozoError, Result};
use crate::relation::tuple::{Tuple, PREFIX_LEN};
use cozorocks::*; use cozorocks::*;
use rand::Rng;
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use uuid::Uuid;
use uuid::v1::{Context, Timestamp}; use uuid::v1::{Context, Timestamp};
use rand::Rng; use uuid::Uuid;
use crate::error::{CozoError, Result};
use crate::error::CozoError::{Poisoned, SessionErr};
use crate::relation::tuple::{PREFIX_LEN, Tuple};
pub struct EngineOptions { pub struct EngineOptions {
cmp: RustComparatorPtr, cmp: RustComparatorPtr,
@ -37,10 +36,7 @@ impl Engine {
} else { } else {
TDBOptions::Pessimistic(PTxnDBOptionsPtr::default()) TDBOptions::Pessimistic(PTxnDBOptionsPtr::default())
}; };
let cmp = RustComparatorPtr::new( let cmp = RustComparatorPtr::new("cozo_cmp_v1", crate::relation::key_order::compare, false);
"cozo_cmp_v1",
crate::relation::key_order::compare,
false);
let mut options = OptionsPtr::default(); let mut options = OptionsPtr::default();
options options
.set_comparator(&cmp) .set_comparator(&cmp)
@ -71,13 +67,17 @@ impl Engine {
pub fn session(&self) -> Result<Session> { pub fn session(&self) -> Result<Session> {
// find a handle if there is one available // find a handle if there is one available
// otherwise create a new one // otherwise create a new one
let mut guard = self.session_handles.lock().map_err(|_| CozoError::Poisoned)?; let mut guard = self
let old_handle = guard.iter().find(|v| { .session_handles
match v.read() { .lock()
.map_err(|_| CozoError::Poisoned)?;
let old_handle = guard
.iter()
.find(|v| match v.read() {
Ok(content) => content.status == SessionStatus::Completed, Ok(content) => content.status == SessionStatus::Completed,
Err(_) => false Err(_) => false,
} })
}).cloned(); .cloned();
let handle = match old_handle { let handle = match old_handle {
None => { None => {
let now = SystemTime::now(); let now = SystemTime::now();
@ -88,7 +88,17 @@ impl Engine {
since_epoch.subsec_nanos(), since_epoch.subsec_nanos(),
); );
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let id = Uuid::new_v1(ts, &[rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen()])?; let id = Uuid::new_v1(
ts,
&[
rng.gen(),
rng.gen(),
rng.gen(),
rng.gen(),
rng.gen(),
rng.gen(),
],
)?;
let cf_ident = id.to_string(); let cf_ident = id.to_string();
self.db.create_cf(&self.options_store.options, &cf_ident)?; self.db.create_cf(&self.options_store.options, &cf_ident)?;
@ -99,7 +109,7 @@ impl Engine {
guard.push(ret.clone()); guard.push(ret.clone());
ret ret
} }
Some(h) => h Some(h) => h,
}; };
let mut sess = Session { let mut sess = Session {
@ -133,12 +143,14 @@ impl<'a> Session<'a> {
fn start_with_total_seek(&mut self, total_seek: bool) -> Result<()> { fn start_with_total_seek(&mut self, total_seek: bool) -> Result<()> {
self.perm_cf = self.engine.db.default_cf(); self.perm_cf = self.engine.db.default_cf();
assert!(!self.perm_cf.is_null()); assert!(!self.perm_cf.is_null());
self.temp_cf = self.engine.db.get_cf(&self.handle.read().map_err(|_| Poisoned)?.cf_ident).ok_or(SessionErr)?; self.temp_cf = self
.engine
.db
.get_cf(&self.handle.read().map_err(|_| Poisoned)?.cf_ident)
.ok_or(SessionErr)?;
assert!(!self.temp_cf.is_null()); assert!(!self.temp_cf.is_null());
let t_options = match self.engine.options_store.t_options { let t_options = match self.engine.options_store.t_options {
TDBOptions::Pessimistic(_) => { TDBOptions::Pessimistic(_) => TransactOptions::Pessimistic(PTxnOptionsPtr::default()),
TransactOptions::Pessimistic(PTxnOptionsPtr::default())
}
TDBOptions::Optimistic(_) => { TDBOptions::Optimistic(_) => {
TransactOptions::Optimistic(OTxnOptionsPtr::new(&self.engine.options_store.cmp)) TransactOptions::Optimistic(OTxnOptionsPtr::new(&self.engine.options_store.cmp))
} }
@ -167,7 +179,10 @@ impl<'a> Session<'a> {
let w_opts = WriteOptionsPtr::default(); let w_opts = WriteOptionsPtr::default();
let mut wx_opts = WriteOptionsPtr::default(); let mut wx_opts = WriteOptionsPtr::default();
wx_opts.set_disable_wal(true); wx_opts.set_disable_wal(true);
self.txn = self.engine.db.make_transaction(t_options, r_opts, rx_opts, w_opts, wx_opts); self.txn = self
.engine
.db
.make_transaction(t_options, r_opts, rx_opts, w_opts, wx_opts);
if self.txn.is_null() { if self.txn.is_null() {
panic!("Starting session failed as opening transaction failed"); panic!("Starting session failed as opening transaction failed");
} }
@ -183,7 +198,8 @@ impl<'a> Session<'a> {
Ok(()) Ok(())
} }
pub fn finish_work(&mut self) -> Result<()> { pub fn finish_work(&mut self) -> Result<()> {
self.txn.del_range(&self.temp_cf, Tuple::with_null_prefix(), Tuple::max_tuple())?; self.txn
.del_range(&self.temp_cf, Tuple::with_null_prefix(), Tuple::max_tuple())?;
self.txn.compact_all(&self.temp_cf)?; self.txn.compact_all(&self.temp_cf)?;
// let mut options = FlushOptionsPtr::default(); // let mut options = FlushOptionsPtr::default();
// options.set_allow_write_stall(true).set_flush_wait(true); // options.set_allow_write_stall(true).set_flush_wait(true);
@ -220,9 +236,9 @@ pub enum SessionStatus {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::{fs, thread};
use crate::relation::tuple::Tuple;
use super::*; use super::*;
use crate::relation::tuple::Tuple;
use std::{fs, thread};
#[test] #[test]
fn push_get() { fn push_get() {
@ -328,18 +344,23 @@ mod tests {
})) }))
} }
for t in thread_handles { for t in thread_handles {
t.join().unwrap(); t.join().unwrap();
} }
println!("All OK"); println!("All OK");
{ {
let handles = engine2.session_handles.lock().unwrap(); let handles = engine2.session_handles.lock().unwrap();
println!("got handles {:#?}", handles.iter().map(|h| h.read().unwrap().cf_ident.to_string()).collect::<Vec<_>>()); println!(
"got handles {:#?}",
handles
.iter()
.map(|h| h.read().unwrap().cf_ident.to_string())
.collect::<Vec<_>>()
);
} }
} }
let _ = fs::remove_dir_all(p1); let _ = fs::remove_dir_all(p1);
let _ = fs::remove_dir_all(p2); let _ = fs::remove_dir_all(p2);
let _ = fs::remove_dir_all(p3); let _ = fs::remove_dir_all(p3);
} }
} }

@ -1,8 +1,8 @@
use crate::db::engine::Session; use crate::db::engine::Session;
use crate::relation::value::Value;
use crate::error::{CozoError, Result}; use crate::error::{CozoError, Result};
use crate::relation::data::{DataKind, EMPTY_DATA}; use crate::relation::data::{DataKind, EMPTY_DATA};
use crate::relation::tuple::{OwnTuple, SliceTuple, Tuple}; use crate::relation::tuple::{OwnTuple, SliceTuple, Tuple};
use crate::relation::value::Value;
/// # layouts for sector 0 /// # layouts for sector 0
/// ///
@ -13,7 +13,6 @@ use crate::relation::tuple::{OwnTuple, SliceTuple, Tuple};
/// `[Null, Int, Text, Int, Text]` inverted index for related tables /// `[Null, Int, Text, Int, Text]` inverted index for related tables
/// `[True, Int]` table info, value is key /// `[True, Int]` table info, value is key
impl<'s> Session<'s> { impl<'s> Session<'s> {
pub fn define_variable(&mut self, name: &str, val: &Value, in_root: bool) -> Result<()> { pub fn define_variable(&mut self, name: &str, val: &Value, in_root: bool) -> Result<()> {
let mut data = Tuple::with_data_prefix(DataKind::Val); let mut data = Tuple::with_data_prefix(DataKind::Val);
@ -36,16 +35,37 @@ impl<'s> Session<'s> {
} }
pub fn key_exists(&self, key: &OwnTuple, in_root: bool) -> Result<bool> { pub fn key_exists(&self, key: &OwnTuple, in_root: bool) -> Result<bool> {
let res = self.txn.get(in_root, if in_root { &self.perm_cf } else { &self.temp_cf }, key)?; let res = self.txn.get(
in_root,
if in_root {
&self.perm_cf
} else {
&self.temp_cf
},
key,
)?;
Ok(res.is_some()) Ok(res.is_some())
} }
pub fn del_key(&self, key: &OwnTuple, in_root: bool) -> Result<()> { pub fn del_key(&self, key: &OwnTuple, in_root: bool) -> Result<()> {
self.txn.del(in_root, if in_root { &self.perm_cf } else { &self.temp_cf }, key)?; self.txn.del(
in_root,
if in_root {
&self.perm_cf
} else {
&self.temp_cf
},
key,
)?;
Ok(()) Ok(())
} }
pub fn define_raw_key(&self, key: &OwnTuple, value: Option<&OwnTuple>, in_root: bool) -> Result<()> { pub fn define_raw_key(
&self,
key: &OwnTuple,
value: Option<&OwnTuple>,
in_root: bool,
) -> Result<()> {
if in_root { if in_root {
match value { match value {
None => { None => {
@ -71,14 +91,14 @@ impl<'s> Session<'s> {
pub fn resolve_value(&self, name: &str) -> Result<Option<Value>> { pub fn resolve_value(&self, name: &str) -> Result<Option<Value>> {
match self.resolve(name)? { match self.resolve(name)? {
None => Ok(None), None => Ok(None),
Some(t) => { Some(t) => match t.data_kind()? {
match t.data_kind()? { DataKind::Val => Ok(Some(
DataKind::Val => Ok(Some(t.get(0) t.get(0)
.ok_or_else(|| CozoError::LogicError("Corrupt".to_string()))? .ok_or_else(|| CozoError::LogicError("Corrupt".to_string()))?
.to_static())), .to_static(),
k => Err(CozoError::UnexpectedDataKind(k)) )),
} k => Err(CozoError::UnexpectedDataKind(k)),
} },
} }
} }
pub fn get_stack_depth(&self) -> i32 { pub fn get_stack_depth(&self) -> i32 {
@ -108,15 +128,16 @@ impl<'s> Session<'s> {
ikey.push_value(&name); ikey.push_value(&name);
ikey.push_int(self.stack_depth as i64); ikey.push_int(self.stack_depth as i64);
let data = self.txn.get(false, &self.temp_cf, &ikey)? let data = self
.txn
.get(false, &self.temp_cf, &ikey)?
.ok_or_else(|| CozoError::LogicError("Bad format for ikey".to_string()))?; .ok_or_else(|| CozoError::LogicError("Bad format for ikey".to_string()))?;
let data = Tuple::new(data); let data = Tuple::new(data);
match data.data_kind()? { match data.data_kind()? {
DataKind::Node | DataKind::Node | DataKind::Edge | DataKind::Assoc | DataKind::Index => {
DataKind::Edge | let id = data.get_int(1).ok_or_else(|| {
DataKind::Assoc | CozoError::LogicError("Bad table index".to_string())
DataKind::Index => { })?;
let id = data.get_int(1).ok_or_else(|| CozoError::LogicError("Bad table index".to_string()))?;
let mut rkey = Tuple::with_null_prefix(); let mut rkey = Tuple::with_null_prefix();
rkey.push_bool(true); rkey.push_bool(true);
rkey.push_int(id); rkey.push_int(id);
@ -184,7 +205,10 @@ impl<'s> Session<'s> {
} }
} }
let root_key = self.encode_definable_key(name, true); let root_key = self.encode_definable_key(name, true);
let res = self.txn.get(true, &self.perm_cf, root_key).map(|v| v.map(Tuple::new))?; let res = self
.txn
.get(true, &self.perm_cf, root_key)
.map(|v| v.map(Tuple::new))?;
Ok(res) Ok(res)
} }
} }

File diff suppressed because it is too large Load Diff

@ -1,16 +1,15 @@
use std::cmp::Ordering;
use std::{iter, mem};
use std::fmt::{Debug, Formatter};
use cozorocks::IteratorPtr;
use crate::db::eval::{compare_tuple_by_keys, tuple_eval}; use crate::db::eval::{compare_tuple_by_keys, tuple_eval};
use crate::db::table::{ColId, TableId}; use crate::db::table::{ColId, TableId};
use crate::error::CozoError::LogicError; use crate::error::CozoError::LogicError;
use crate::error::{Result}; use crate::error::Result;
use crate::relation::data::{DataKind, EMPTY_DATA}; use crate::relation::data::{DataKind, EMPTY_DATA};
use crate::relation::table::MegaTuple; use crate::relation::table::MegaTuple;
use crate::relation::tuple::{CowSlice, CowTuple, OwnTuple, Tuple}; use crate::relation::tuple::{CowSlice, CowTuple, OwnTuple, Tuple};
use crate::relation::value::Value; use crate::relation::value::Value;
use cozorocks::IteratorPtr;
use std::cmp::Ordering;
use std::fmt::{Debug, Formatter};
use std::{iter, mem};
pub enum IteratorSlot<'a> { pub enum IteratorSlot<'a> {
Dummy, Dummy,
@ -21,7 +20,7 @@ impl<'a> Debug for IteratorSlot<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
IteratorSlot::Dummy => write!(f, "DummyIterator"), IteratorSlot::Dummy => write!(f, "DummyIterator"),
IteratorSlot::Reified(_) => write!(f, "BaseIterator") IteratorSlot::Reified(_) => write!(f, "BaseIterator"),
} }
} }
} }
@ -36,7 +35,7 @@ impl<'a> IteratorSlot<'a> {
pub fn try_get(&self) -> Result<&IteratorPtr<'a>> { pub fn try_get(&self) -> Result<&IteratorPtr<'a>> {
match self { match self {
IteratorSlot::Dummy => Err(LogicError("Cannot iter over dummy".to_string())), IteratorSlot::Dummy => Err(LogicError("Cannot iter over dummy".to_string())),
IteratorSlot::Reified(r) => Ok(r) IteratorSlot::Reified(r) => Ok(r),
} }
} }
} }
@ -100,7 +99,7 @@ pub enum ExecPlan<'a> {
out_prefix: u32, out_prefix: u32,
}, },
BagsUnionIt { BagsUnionIt {
bags: Vec<ExecPlan<'a>> bags: Vec<ExecPlan<'a>>,
}, },
} }
@ -126,120 +125,109 @@ impl<'a> ExecPlan<'a> {
let prefix_tuple = OwnTuple::with_prefix(*tid); let prefix_tuple = OwnTuple::with_prefix(*tid);
it.seek(prefix_tuple); it.seek(prefix_tuple);
Ok(Box::new(NodeIterator { Ok(Box::new(NodeIterator { it, started: false }))
it,
started: false,
}))
} }
ExecPlan::EdgeIt { it, tid } => { ExecPlan::EdgeIt { it, tid } => {
let it = it.try_get()?; let it = it.try_get()?;
let prefix_tuple = OwnTuple::with_prefix(*tid); let prefix_tuple = OwnTuple::with_prefix(*tid);
it.seek(prefix_tuple); it.seek(prefix_tuple);
Ok(Box::new(EdgeIterator { Ok(Box::new(EdgeIterator { it, started: false }))
it,
started: false,
}))
} }
ExecPlan::EdgeKeyOnlyBwdIt { it, tid } => { ExecPlan::EdgeKeyOnlyBwdIt { it, tid } => {
let it = it.try_get()?; let it = it.try_get()?;
let prefix_tuple = OwnTuple::with_prefix(*tid); let prefix_tuple = OwnTuple::with_prefix(*tid);
it.seek(prefix_tuple); it.seek(prefix_tuple);
Ok(Box::new(EdgeKeyOnlyBwdIterator { Ok(Box::new(EdgeKeyOnlyBwdIterator { it, started: false }))
it,
started: false,
}))
} }
ExecPlan::KeySortedWithAssocIt { main, associates } => { ExecPlan::KeySortedWithAssocIt { main, associates } => {
let buffer = iter::repeat_with(|| None).take(associates.len()).collect(); let buffer = iter::repeat_with(|| None).take(associates.len()).collect();
let associates = associates.into_iter().map(|(tid, it)| { let associates = associates
it.try_get().map(|it| { .iter()
let prefix_tuple = OwnTuple::with_prefix(*tid); .map(|(tid, it)| {
it.seek(prefix_tuple); it.try_get().map(|it| {
let prefix_tuple = OwnTuple::with_prefix(*tid);
NodeIterator { it.seek(prefix_tuple);
it,
started: false, NodeIterator { it, started: false }
} })
}) })
}).collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
Ok(Box::new(KeySortedWithAssocIterator { Ok(Box::new(KeySortedWithAssocIterator {
main: main.iter()?, main: main.iter()?,
associates, associates,
buffer, buffer,
})) }))
} }
ExecPlan::CartesianProdIt { left, right } => { ExecPlan::CartesianProdIt { left, right } => Ok(Box::new(CartesianProdIterator {
Ok(Box::new(CartesianProdIterator { left: left.iter()?,
left: left.iter()?, left_cache: MegaTuple::empty_tuple(),
left_cache: MegaTuple::empty_tuple(), right_source: right.as_ref(),
right_source: right.as_ref(), right: right.as_ref().iter()?,
right: right.as_ref().iter()?, })),
})) ExecPlan::FilterIt { it, filter } => Ok(Box::new(FilterIterator {
} it: it.iter()?,
ExecPlan::FilterIt { it, filter } => { filter,
Ok(Box::new(FilterIterator { })),
it: it.iter()?, ExecPlan::EvalIt {
filter, it,
})) keys,
} vals,
ExecPlan::EvalIt { it, keys, vals, out_prefix: prefix } => { out_prefix: prefix,
Ok(Box::new(EvalIterator { } => Ok(Box::new(EvalIterator {
it: it.iter()?, it: it.iter()?,
keys, keys,
vals, vals,
prefix: *prefix, prefix: *prefix,
})) })),
} ExecPlan::MergeJoinIt {
ExecPlan::MergeJoinIt { left, right, left_keys, right_keys } => { left,
Ok(Box::new(MergeJoinIterator { right,
left: left.iter()?, left_keys,
right: right.iter()?, right_keys,
left_keys, } => Ok(Box::new(MergeJoinIterator {
right_keys, left: left.iter()?,
})) right: right.iter()?,
} left_keys,
right_keys,
})),
ExecPlan::OuterMergeJoinIt { ExecPlan::OuterMergeJoinIt {
left, right, left,
left_keys, right_keys, left_outer, right_outer, right,
left_len, right_len left_keys,
} => { right_keys,
Ok(Box::new(OuterMergeJoinIterator { left_outer,
left: left.iter()?, right_outer,
right: right.iter()?, left_len,
left_outer: *left_outer, right_len,
right_outer: *right_outer, } => Ok(Box::new(OuterMergeJoinIterator {
left_keys, left: left.iter()?,
right_keys, right: right.iter()?,
left_len: *left_len, left_outer: *left_outer,
right_len: *right_len, right_outer: *right_outer,
left_cache: None, left_keys,
right_cache: None, right_keys,
pull_left: true, left_len: *left_len,
pull_right: true, right_len: *right_len,
})) left_cache: None,
} right_cache: None,
ExecPlan::KeyedUnionIt { left, right } => { pull_left: true,
Ok(Box::new(KeyedUnionIterator { pull_right: true,
left: left.iter()?, })),
right: right.iter()?, ExecPlan::KeyedUnionIt { left, right } => Ok(Box::new(KeyedUnionIterator {
})) left: left.iter()?,
} right: right.iter()?,
ExecPlan::KeyedDifferenceIt { left, right } => { })),
Ok(Box::new(KeyedDifferenceIterator { ExecPlan::KeyedDifferenceIt { left, right } => Ok(Box::new(KeyedDifferenceIterator {
left: left.iter()?, left: left.iter()?,
right: right.iter()?, right: right.iter()?,
right_cache: None, right_cache: None,
started: false, started: false,
})) })),
}
ExecPlan::BagsUnionIt { bags } => { ExecPlan::BagsUnionIt { bags } => {
let bags = bags.iter().map(|i| i.iter()).collect::<Result<Vec<_>>>()?; let bags = bags.iter().map(|i| i.iter()).collect::<Result<Vec<_>>>()?;
Ok(Box::new(BagsUnionIterator { Ok(Box::new(BagsUnionIterator { bags, current: 0 }))
bags,
current: 0,
}))
} }
} }
} }
@ -257,13 +245,13 @@ impl<'a> Iterator for KeyedUnionIterator<'a> {
let mut left_cache = match self.left.next() { let mut left_cache = match self.left.next() {
None => return None, None => return None,
Some(Err(e)) => return Some(Err(e)), Some(Err(e)) => return Some(Err(e)),
Some(Ok(t)) => t Some(Ok(t)) => t,
}; };
let mut right_cache = match self.right.next() { let mut right_cache = match self.right.next() {
None => return None, None => return None,
Some(Err(e)) => return Some(Err(e)), Some(Err(e)) => return Some(Err(e)),
Some(Ok(t)) => t Some(Ok(t)) => t,
}; };
loop { loop {
@ -312,7 +300,7 @@ impl<'a> Iterator for KeyedDifferenceIterator<'a> {
self.right_cache = match self.right.next() { self.right_cache = match self.right.next() {
None => None, None => None,
Some(Err(e)) => return Some(Err(e)), Some(Err(e)) => return Some(Err(e)),
Some(Ok(t)) => Some(t) Some(Ok(t)) => Some(t),
}; };
self.started = true; self.started = true;
@ -321,17 +309,16 @@ impl<'a> Iterator for KeyedDifferenceIterator<'a> {
let mut left_cache = match self.left.next() { let mut left_cache = match self.left.next() {
None => return None, None => return None,
Some(Err(e)) => return Some(Err(e)), Some(Err(e)) => return Some(Err(e)),
Some(Ok(t)) => t Some(Ok(t)) => t,
}; };
loop { loop {
let right = match &self.right_cache { let right = match &self.right_cache {
None => { None => {
// right is exhausted, so all left ones can be returned // right is exhausted, so all left ones can be returned
return Some(Ok(left_cache)); return Some(Ok(left_cache));
} }
Some(r) => r Some(r) => r,
}; };
let cmp_res = left_cache.all_keys_cmp(right); let cmp_res = left_cache.all_keys_cmp(right);
match cmp_res { match cmp_res {
@ -340,12 +327,12 @@ impl<'a> Iterator for KeyedDifferenceIterator<'a> {
left_cache = match self.left.next() { left_cache = match self.left.next() {
None => return None, None => return None,
Some(Err(e)) => return Some(Err(e)), Some(Err(e)) => return Some(Err(e)),
Some(Ok(t)) => t Some(Ok(t)) => t,
}; };
self.right_cache = match self.right.next() { self.right_cache = match self.right.next() {
None => None, None => None,
Some(Err(e)) => return Some(Err(e)), Some(Err(e)) => return Some(Err(e)),
Some(Ok(t)) => Some(t) Some(Ok(t)) => Some(t),
}; };
} }
Ordering::Less => { Ordering::Less => {
@ -386,7 +373,7 @@ impl<'a> Iterator for BagsUnionIterator<'a> {
self.next() self.next()
} }
} }
v => v v => v,
} }
} }
} }
@ -494,36 +481,30 @@ impl<'a> Iterator for KeySortedWithAssocIterator<'a> {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
match self.main.next() { match self.main.next() {
None => None, // main exhausted, we are finished None => None, // main exhausted, we are finished
Some(Err(e)) => return Some(Err(e)), Some(Err(e)) => Some(Err(e)),
Some(Ok(MegaTuple { mut keys, mut vals })) => { Some(Ok(MegaTuple { mut keys, mut vals })) => {
// extract key from main // extract key from main
let k = match keys.pop() { let k = match keys.pop() {
None => return Some(Err(LogicError("Empty keys".to_string()))), None => return Some(Err(LogicError("Empty keys".to_string()))),
Some(k) => k Some(k) => k,
}; };
let l = self.associates.len(); let l = self.associates.len();
// initialize vector for associate values // initialize vector for associate values
let mut assoc_vals: Vec<Option<CowTuple>> = iter::repeat_with(|| None).take(l).collect(); let mut assoc_vals: Vec<Option<CowTuple>> = iter::repeat_with(|| None).take(l).collect();
let l = assoc_vals.len(); // let l = assoc_vals.len();
#[allow(clippy::needless_range_loop)]
for i in 0..l { for i in 0..l {
// for each associate // for each associate
let cached = self.buffer.get(i).unwrap(); let cached = self.buffer.get(i).unwrap();
// if no cache, try to get cache filled first // if no cache, try to get cache filled first
if matches!(cached, None) { if matches!(cached, None) {
let assoc_data = self.associates.get_mut(i).unwrap().next() let assoc_data = self.associates.get_mut(i).unwrap().next().map(|mt| {
.map(|mt| { mt.map(|mut mt| (mt.keys.pop().unwrap(), mt.vals.pop().unwrap()))
mt.map(|mut mt| { });
(mt.keys.pop().unwrap(), mt.vals.pop().unwrap())
})
});
match assoc_data { match assoc_data {
None => { None => self.buffer[i] = None,
self.buffer[i] = None Some(Ok(data)) => self.buffer[i] = Some(data),
} Some(Err(e)) => return Some(Err(e)),
Some(Ok(data)) => {
self.buffer[i] = Some(data)
}
Some(Err(e)) => return Some(Err(e))
} }
} }
@ -537,38 +518,31 @@ impl<'a> Iterator for KeySortedWithAssocIterator<'a> {
Ordering::Equal => { Ordering::Equal => {
// target key equals cache key, we put it into collected values // target key equals cache key, we put it into collected values
let (_, v) = mem::replace(&mut self.buffer[i], None).unwrap(); let (_, v) = mem::replace(&mut self.buffer[i], None).unwrap();
assoc_vals[i] = Some(v.into()); assoc_vals[i] = Some(v);
break; break;
} }
Ordering::Greater => { Ordering::Greater => {
// target key greater than cache key, meaning that the source has holes (maybe due to filtering) // target key greater than cache key, meaning that the source has holes (maybe due to filtering)
// get a new one into buffer // get a new one into buffer
let assoc_data = self.associates.get_mut(i).unwrap().next() let assoc_data =
.map(|mt| { self.associates.get_mut(i).unwrap().next().map(|mt| {
mt.map(|mut mt| { mt.map(|mut mt| {
(mt.keys.pop().unwrap(), mt.vals.pop().unwrap()) (mt.keys.pop().unwrap(), mt.vals.pop().unwrap())
}) })
}); });
match assoc_data { match assoc_data {
None => { None => self.buffer[i] = None,
self.buffer[i] = None Some(Ok(data)) => self.buffer[i] = Some(data),
} Some(Err(e)) => return Some(Err(e)),
Some(Ok(data)) => {
self.buffer[i] = Some(data)
}
Some(Err(e)) => return Some(Err(e))
} }
} }
} }
} }
} }
vals.extend(assoc_vals.into_iter().map(|v| vals.extend(assoc_vals.into_iter().map(|v| match v {
match v { None => CowTuple::new(CowSlice::Own(EMPTY_DATA.into())),
None => { Some(v) => v,
CowTuple::new(CowSlice::Own(EMPTY_DATA.into())) }));
}
Some(v) => v
}));
Some(Ok(MegaTuple { Some(Ok(MegaTuple {
keys: vec![k], keys: vec![k],
vals, vals,
@ -601,7 +575,7 @@ impl<'a> Iterator for OuterMergeJoinIterator<'a> {
self.left_cache = match self.left.next() { self.left_cache = match self.left.next() {
None => None, None => None,
Some(Err(e)) => return Some(Err(e)), Some(Err(e)) => return Some(Err(e)),
Some(Ok(t)) => Some(t) Some(Ok(t)) => Some(t),
}; };
self.pull_left = false; self.pull_left = false;
} }
@ -610,15 +584,23 @@ impl<'a> Iterator for OuterMergeJoinIterator<'a> {
self.right_cache = match self.right.next() { self.right_cache = match self.right.next() {
None => None, None => None,
Some(Err(e)) => return Some(Err(e)), Some(Err(e)) => return Some(Err(e)),
Some(Ok(t)) => Some(t) Some(Ok(t)) => Some(t),
}; };
self.pull_right = false; self.pull_right = false;
} }
let make_empty_tuple = |is_left: bool| -> MegaTuple { let make_empty_tuple = |is_left: bool| -> MegaTuple {
let lengths = if is_left { self.left_len } else { self.right_len }; let lengths = if is_left {
let keys = iter::repeat_with(|| OwnTuple::empty_tuple().into()).take(lengths.0).collect(); self.left_len
let vals = iter::repeat_with(|| OwnTuple::empty_tuple().into()).take(lengths.1).collect(); } else {
self.right_len
};
let keys = iter::repeat_with(|| OwnTuple::empty_tuple().into())
.take(lengths.0)
.collect();
let vals = iter::repeat_with(|| OwnTuple::empty_tuple().into())
.take(lengths.1)
.collect();
MegaTuple { keys, vals } MegaTuple { keys, vals }
}; };
@ -640,7 +622,7 @@ impl<'a> Iterator for OuterMergeJoinIterator<'a> {
} }
}; };
} }
Some(t) => t Some(t) => t,
}; };
let right_cache = match &self.right_cache { let right_cache = match &self.right_cache {
None => { None => {
@ -659,12 +641,14 @@ impl<'a> Iterator for OuterMergeJoinIterator<'a> {
} }
}; };
} }
Some(t) => t Some(t) => t,
}; };
let cmp_res = match compare_tuple_by_keys((&left_cache, self.left_keys), let cmp_res = match compare_tuple_by_keys(
(&right_cache, self.right_keys)) { (left_cache, self.left_keys),
(right_cache, self.right_keys),
) {
Ok(r) => r, Ok(r) => r,
Err(e) => return Some(Err(e)) Err(e) => return Some(Err(e)),
}; };
match cmp_res { match cmp_res {
Ordering::Equal => { Ordering::Equal => {
@ -731,20 +715,22 @@ impl<'a> Iterator for MergeJoinIterator<'a> {
let mut left_cache = match self.left.next() { let mut left_cache = match self.left.next() {
None => return None, None => return None,
Some(Err(e)) => return Some(Err(e)), Some(Err(e)) => return Some(Err(e)),
Some(Ok(t)) => t Some(Ok(t)) => t,
}; };
let mut right_cache = match self.right.next() { let mut right_cache = match self.right.next() {
None => return None, None => return None,
Some(Err(e)) => return Some(Err(e)), Some(Err(e)) => return Some(Err(e)),
Some(Ok(t)) => t Some(Ok(t)) => t,
}; };
loop { loop {
let cmp_res = match compare_tuple_by_keys((&left_cache, self.left_keys), let cmp_res = match compare_tuple_by_keys(
(&right_cache, self.right_keys)) { (&left_cache, self.left_keys),
(&right_cache, self.right_keys),
) {
Ok(r) => r, Ok(r) => r,
Err(e) => return Some(Err(e)) Err(e) => return Some(Err(e)),
}; };
match cmp_res { match cmp_res {
Ordering::Equal => { Ordering::Equal => {
@ -791,29 +777,29 @@ impl<'a> Iterator for CartesianProdIterator<'a> {
self.left_cache = match self.left.next() { self.left_cache = match self.left.next() {
None => return None, None => return None,
Some(Ok(v)) => v, Some(Ok(v)) => v,
Some(Err(e)) => return Some(Err(e)) Some(Err(e)) => return Some(Err(e)),
} }
} }
let r_tpl = match self.right.next() { let r_tpl = match self.right.next() {
None => { None => {
self.right = match self.right_source.iter() { self.right = match self.right_source.iter() {
Ok(it) => it, Ok(it) => it,
Err(e) => return Some(Err(e)) Err(e) => return Some(Err(e)),
}; };
self.left_cache = match self.left.next() { self.left_cache = match self.left.next() {
None => return None, None => return None,
Some(Ok(v)) => v, Some(Ok(v)) => v,
Some(Err(e)) => return Some(Err(e)) Some(Err(e)) => return Some(Err(e)),
}; };
match self.right.next() { match self.right.next() {
// early return in case right is empty // early return in case right is empty
None => return None, None => return None,
Some(Ok(r_tpl)) => r_tpl, Some(Ok(r_tpl)) => r_tpl,
Some(Err(e)) => return Some(Err(e)) Some(Err(e)) => return Some(Err(e)),
} }
} }
Some(Ok(r_tpl)) => r_tpl, Some(Ok(r_tpl)) => r_tpl,
Some(Err(e)) => return Some(Err(e)) Some(Err(e)) => return Some(Err(e)),
}; };
let mut ret = self.left_cache.clone(); let mut ret = self.left_cache.clone();
ret.keys.extend(r_tpl.keys); ret.keys.extend(r_tpl.keys);
@ -833,17 +819,17 @@ impl<'a> Iterator for FilterIterator<'a> {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
for t in self.it.by_ref() { for t in self.it.by_ref() {
match t { match t {
Ok(t) => { Ok(t) => match tuple_eval(self.filter, &t) {
match tuple_eval(self.filter, &t) { Ok(Value::Bool(true)) => {
Ok(Value::Bool(true)) => { return Some(Ok(t));
return Some(Ok(t));
}
Ok(Value::Bool(false)) | Ok(Value::Null) => {}
Ok(_v) => return Some(Err(LogicError("Unexpected type in filter".to_string()))),
Err(e) => return Some(Err(e))
} }
} Ok(Value::Bool(false)) | Ok(Value::Null) => {}
Err(e) => return Some(Err(e)) Ok(_v) => {
return Some(Err(LogicError("Unexpected type in filter".to_string())));
}
Err(e) => return Some(Err(e)),
},
Err(e) => return Some(Err(e)),
} }
} }
None None
@ -871,7 +857,7 @@ impl<'a> Iterator for OutputIterator<'a> {
match self.it.next() { match self.it.next() {
None => None, None => None,
Some(Err(e)) => Some(Err(e)), Some(Err(e)) => Some(Err(e)),
Some(Ok(t)) => Some(tuple_eval(self.transform, &t).map(|v| v.to_static())) Some(Ok(t)) => Some(tuple_eval(self.transform, &t).map(|v| v.to_static())),
} }
} }
} }
@ -896,16 +882,19 @@ impl<'a> Iterator for EvalIterator<'a> {
for k in self.keys { for k in self.keys {
match tuple_eval(k, &t) { match tuple_eval(k, &t) {
Ok(v) => key_tuple.push_value(&v), Ok(v) => key_tuple.push_value(&v),
Err(e) => return Some(Err(e)) Err(e) => return Some(Err(e)),
} }
} }
for k in self.vals { for k in self.vals {
match tuple_eval(k, &t) { match tuple_eval(k, &t) {
Ok(v) => val_tuple.push_value(&v), Ok(v) => val_tuple.push_value(&v),
Err(e) => return Some(Err(e)) Err(e) => return Some(Err(e)),
} }
} }
Some(Ok(MegaTuple { keys: vec![key_tuple.into()], vals: vec![val_tuple.into()] })) Some(Ok(MegaTuple {
keys: vec![key_tuple.into()],
vals: vec![val_tuple.into()],
}))
} }
} }
} }
@ -913,16 +902,16 @@ impl<'a> Iterator for EvalIterator<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::BTreeMap;
use std::fs;
use std::time::Instant;
use crate::db::engine::Engine; use crate::db::engine::Engine;
use crate::parser::{Parser, Rule};
use pest::Parser as PestParser;
use crate::db::iterator::{ExecPlan, OutputIterator}; use crate::db::iterator::{ExecPlan, OutputIterator};
use crate::db::query::FromEl; use crate::db::query::FromEl;
use crate::relation::value::Value;
use crate::error::Result; use crate::error::Result;
use crate::parser::{Parser, Rule};
use crate::relation::value::Value;
use pest::Parser as PestParser;
use std::collections::BTreeMap;
use std::fs;
use std::time::Instant;
#[test] #[test]
fn pair_value() -> Result<()> { fn pair_value() -> Result<()> {
@ -961,25 +950,46 @@ mod tests {
let start2 = Instant::now(); let start2 = Instant::now();
let s = "from e:Employee"; let s = "from e:Employee";
let p = Parser::parse(Rule::from_pattern, s).unwrap().next().unwrap(); let p = Parser::parse(Rule::from_pattern, s)
.unwrap()
.next()
.unwrap();
let from_pat = match sess.parse_from_pattern(p).unwrap().pop().unwrap() { let from_pat = match sess.parse_from_pattern(p).unwrap().pop().unwrap() {
FromEl::Simple(s) => s, FromEl::Simple(s) => s,
FromEl::Chain(_) => panic!() FromEl::Chain(_) => panic!(),
}; };
let s = "where e.id >= 100, e.id <= 105 || e.id == 110"; let s = "where e.id >= 100, e.id <= 105 || e.id == 110";
let p = Parser::parse(Rule::where_pattern, s).unwrap().next().unwrap(); let p = Parser::parse(Rule::where_pattern, s)
.unwrap()
.next()
.unwrap();
let where_pat = sess.parse_where_pattern(p).unwrap(); let where_pat = sess.parse_where_pattern(p).unwrap();
let s = r#"select {id: e.id, let s = r#"select {id: e.id,
full_name: e.first_name ++ ' ' ++ e.last_name, bibio_name: e.last_name ++ ', ' full_name: e.first_name ++ ' ' ++ e.last_name, bibio_name: e.last_name ++ ', '
++ e.first_name ++ ': ' ++ (e.phone_number ~ 'N.A.')}"#; ++ e.first_name ++ ': ' ++ (e.phone_number ~ 'N.A.')}"#;
let p = Parser::parse(Rule::select_pattern, s).unwrap().next().unwrap(); let p = Parser::parse(Rule::select_pattern, s)
.unwrap()
.next()
.unwrap();
let sel_pat = sess.parse_select_pattern(p).unwrap(); let sel_pat = sess.parse_select_pattern(p).unwrap();
let amap = sess.base_relation_to_accessor_map(&from_pat.table, &from_pat.binding, &from_pat.info); let amap = sess.base_relation_to_accessor_map(
let (_, vals) = sess.partial_eval(sel_pat.vals, &Default::default(), &amap).unwrap(); &from_pat.table,
let (_, where_vals) = sess.partial_eval(where_pat, &Default::default(), &amap).unwrap(); &from_pat.binding,
println!("{:#?}", sess.cnf_with_table_refs(where_vals.clone(), &Default::default(), &amap)); &from_pat.info,
let (vcoll, mut rel_tbls) = Value::extract_relevant_tables([vals, where_vals].into_iter()).unwrap(); );
let (_, vals) = sess
.partial_eval(sel_pat.vals, &Default::default(), &amap)
.unwrap();
let (_, where_vals) = sess
.partial_eval(where_pat, &Default::default(), &amap)
.unwrap();
println!(
"{:#?}",
sess.cnf_with_table_refs(where_vals.clone(), &Default::default(), &amap)
);
let (vcoll, mut rel_tbls) =
Value::extract_relevant_tables([vals, where_vals].into_iter()).unwrap();
let mut vcoll = vcoll.into_iter(); let mut vcoll = vcoll.into_iter();
let vals = vcoll.next().unwrap(); let vals = vcoll.next().unwrap();
let where_vals = vcoll.next().unwrap(); let where_vals = vcoll.next().unwrap();
@ -991,7 +1001,10 @@ mod tests {
let tbl = rel_tbls.pop().unwrap(); let tbl = rel_tbls.pop().unwrap();
let it = sess.iter_node(tbl); let it = sess.iter_node(tbl);
let it = ExecPlan::FilterIt { filter: where_vals, it: it.into() }; let it = ExecPlan::FilterIt {
filter: where_vals,
it: it.into(),
};
let it = OutputIterator::new(&it, &vals)?; let it = OutputIterator::new(&it, &vals)?;
for val in it { for val in it {
println!("{}", val.unwrap()); println!("{}", val.unwrap());
@ -1001,9 +1014,11 @@ mod tests {
println!("Time elapsed {:?} {:?}", duration, duration2); println!("Time elapsed {:?} {:?}", duration, duration2);
let it = ExecPlan::KeySortedWithAssocIt { let it = ExecPlan::KeySortedWithAssocIt {
main: Box::new(sess.iter_node(tbl)), main: Box::new(sess.iter_node(tbl)),
associates: vec![(tbl.id as u32, sess.raw_iterator(true).into()), associates: vec![
(tbl.id as u32, sess.raw_iterator(true).into()), (tbl.id as u32, sess.raw_iterator(true).into()),
(tbl.id as u32, sess.raw_iterator(true).into())], (tbl.id as u32, sess.raw_iterator(true).into()),
(tbl.id as u32, sess.raw_iterator(true).into()),
],
}; };
{ {
for el in it.iter()? { for el in it.iter()? {
@ -1033,12 +1048,23 @@ mod tests {
// if n % 4096 == 0 { // if n % 4096 == 0 {
// println!("{}: {:?}", n, el) // println!("{}: {:?}", n, el)
// } // }
let _x = el.keys.into_iter().map(|v| v.iter().map(|_v| ()).collect::<Vec<_>>()).collect::<Vec<_>>(); let _x = el
let _y = el.vals.into_iter().map(|v| v.iter().map(|_v| ()).collect::<Vec<_>>()).collect::<Vec<_>>(); .keys
.into_iter()
.map(|v| v.iter().map(|_v| ()).collect::<Vec<_>>())
.collect::<Vec<_>>();
let _y = el
.vals
.into_iter()
.map(|v| v.iter().map(|_v| ()).collect::<Vec<_>>())
.collect::<Vec<_>>();
n += 1; n += 1;
} }
let duration = start.elapsed(); let duration = start.elapsed();
println!("{} items per second", 1e9 * (n as f64) / (duration.as_nanos() as f64)); println!(
"{} items per second",
1e9 * (n as f64) / (duration.as_nanos() as f64)
);
// let a = sess.iter_table(tbl); // let a = sess.iter_table(tbl);
// let ac = (&a).into_iter().count(); // let ac = (&a).into_iter().count();
// println!("{}", ac); // println!("{}", ac);
@ -1047,4 +1073,4 @@ mod tests {
let _ = fs::remove_dir_all(db_path); let _ = fs::remove_dir_all(db_path);
Ok(()) Ok(())
} }
} }

@ -1,17 +1,17 @@
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::{BTreeMap, HashSet};
use std::rc::Rc;
use pest::iterators::Pair;
use crate::db::engine::Session; use crate::db::engine::Session;
use crate::db::table::TableInfo; use crate::db::table::TableInfo;
use crate::error::CozoError::LogicError; use crate::error::CozoError::LogicError;
use crate::error::{CozoError, Result}; use crate::error::{CozoError, Result};
use crate::parser::Rule;
use crate::parser::text_identifier::build_name_in_def; use crate::parser::text_identifier::build_name_in_def;
use crate::parser::Rule;
use crate::relation::data::DataKind; use crate::relation::data::DataKind;
use crate::relation::tuple::Tuple; use crate::relation::tuple::Tuple;
use crate::relation::value::Value; use crate::relation::value::Value;
use pest::iterators::Pair;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::{BTreeMap, HashSet};
use std::rc::Rc;
/// # key layouts /// # key layouts
/// ///
@ -36,23 +36,34 @@ enum MutationKind {
} }
impl<'a> Session<'a> { impl<'a> Session<'a> {
pub fn run_mutation(&mut self, pair: Pair<Rule>, params: &BTreeMap<String, Value>) -> Result<()> { pub fn run_mutation(
&mut self,
pair: Pair<Rule>,
params: &BTreeMap<String, Value>,
) -> Result<()> {
let mut pairs = pair.into_inner(); let mut pairs = pair.into_inner();
let kind = match pairs.next().unwrap().as_rule() { let kind = match pairs.next().unwrap().as_rule() {
Rule::upsert => MutationKind::Upsert, Rule::upsert => MutationKind::Upsert,
Rule::insert => MutationKind::Insert, Rule::insert => MutationKind::Insert,
_ => unreachable!() _ => unreachable!(),
}; };
let (evaluated, expr) = self.partial_eval( let (evaluated, expr) = self.partial_eval(
Value::from_pair(pairs.next().unwrap())?, Value::from_pair(pairs.next().unwrap())?,
params, params,
&Default::default())?; &Default::default(),
)?;
if !evaluated { if !evaluated {
return Err(LogicError("Mutation encountered unevaluated expression".to_string())); return Err(LogicError(
"Mutation encountered unevaluated expression".to_string(),
));
} }
let expr = match expr { let expr = match expr {
Value::List(v) => v, Value::List(v) => v,
_ => return Err(LogicError("Mutation requires iterator of values".to_string())) _ => {
return Err(LogicError(
"Mutation requires iterator of values".to_string(),
))
}
}; };
let mut default_kind = None; let mut default_kind = None;
// let mut filters: Option<()> = None; // let mut filters: Option<()> = None;
@ -60,7 +71,7 @@ impl<'a> Session<'a> {
match p.as_rule() { match p.as_rule() {
Rule::name_in_def => default_kind = Some(build_name_in_def(p, true)?), Rule::name_in_def => default_kind = Some(build_name_in_def(p, true)?),
Rule::mutation_filter => todo!(), // filters = Some(()), // TODO Rule::mutation_filter => todo!(), // filters = Some(()), // TODO
_ => unreachable!() _ => unreachable!(),
} }
} }
// println!("{:?}", kind); // println!("{:?}", kind);
@ -74,7 +85,7 @@ impl<'a> Session<'a> {
for item in expr { for item in expr {
let val_map = match item { let val_map = match item {
Value::Dict(d) => d, Value::Dict(d) => d,
_ => return Err(LogicError("Must be structs".to_string())) _ => return Err(LogicError("Must be structs".to_string())),
}; };
mutation_manager.process_insert(kind == MutationKind::Insert, val_map)?; mutation_manager.process_insert(kind == MutationKind::Insert, val_map)?;
} }
@ -91,27 +102,38 @@ struct MutationManager<'a, 'b> {
impl<'a, 'b> MutationManager<'a, 'b> { impl<'a, 'b> MutationManager<'a, 'b> {
fn new(sess: &'a Session<'b>, default_tbl: Option<String>) -> Self { fn new(sess: &'a Session<'b>, default_tbl: Option<String>) -> Self {
Self { sess, cache: RefCell::new(BTreeMap::new()), default_tbl } Self {
sess,
cache: RefCell::new(BTreeMap::new()),
default_tbl,
}
} }
fn get_table_info(&self, tbl_name: Cow<str>) -> Result<Rc<TableInfo>> { fn get_table_info(&self, tbl_name: Cow<str>) -> Result<Rc<TableInfo>> {
if !self.cache.borrow().contains_key(tbl_name.as_ref()) { if !self.cache.borrow().contains_key(tbl_name.as_ref()) {
let coercer = self.sess.get_table_info(&tbl_name)?; let coercer = self.sess.get_table_info(&tbl_name)?;
self.cache.borrow_mut().insert(tbl_name.as_ref().to_string(), Rc::new(coercer)); self.cache
.borrow_mut()
.insert(tbl_name.as_ref().to_string(), Rc::new(coercer));
} }
let cache = self.cache.borrow(); let cache = self.cache.borrow();
let info = cache.get(tbl_name.as_ref()) let info = cache
.get(tbl_name.as_ref())
.ok_or_else(|| CozoError::LogicError("Cannot resolve table".to_string()))?; .ok_or_else(|| CozoError::LogicError("Cannot resolve table".to_string()))?;
Ok(info.clone()) Ok(info.clone())
} }
fn process_insert(&mut self, error_on_existing: bool, mut val_map: BTreeMap<Cow<str>, Value>) -> Result<()> { fn process_insert(
&mut self,
error_on_existing: bool,
mut val_map: BTreeMap<Cow<str>, Value>,
) -> Result<()> {
let tbl_name = match val_map.get("_type") { let tbl_name = match val_map.get("_type") {
Some(Value::Text(t)) => t.clone(), Some(Value::Text(t)) => t.clone(),
Some(_) => return Err(LogicError("Table kind must be text".to_string())), Some(_) => return Err(LogicError("Table kind must be text".to_string())),
None => match &self.default_tbl { None => match &self.default_tbl {
Some(v) => v.clone().into(), Some(v) => v.clone().into(),
None => return Err(LogicError("Cannot determine table kind".to_string())) None => return Err(LogicError("Cannot determine table kind".to_string())),
} },
}; };
let table_info = self.get_table_info(tbl_name)?; let table_info = self.get_table_info(tbl_name)?;
@ -132,10 +154,18 @@ impl<'a, 'b> MutationManager<'a, 'b> {
let processed = v.coerce(raw)?; let processed = v.coerce(raw)?;
val_tuple.push_value(&processed); val_tuple.push_value(&processed);
} }
if error_on_existing && self.sess.key_exists(&key_tuple, table_info.table_id.in_root)? { if error_on_existing
&& self
.sess
.key_exists(&key_tuple, table_info.table_id.in_root)?
{
return Err(CozoError::KeyConflict(key_tuple)); return Err(CozoError::KeyConflict(key_tuple));
} }
self.sess.define_raw_key(&key_tuple, Some(&val_tuple), table_info.table_id.in_root)?; self.sess.define_raw_key(
&key_tuple,
Some(&val_tuple),
table_info.table_id.in_root,
)?;
} }
DataKind::Edge => { DataKind::Edge => {
key_tuple = Tuple::with_prefix(table_info.table_id.id as u32); key_tuple = Tuple::with_prefix(table_info.table_id.id as u32);
@ -149,7 +179,7 @@ impl<'a, 'b> MutationManager<'a, 'b> {
let src = val_map.remove("_src").unwrap_or(Value::Null); let src = val_map.remove("_src").unwrap_or(Value::Null);
let src_key_list = match src { let src_key_list = match src {
Value::List(v) => v, Value::List(v) => v,
v => vec![v] v => vec![v],
}; };
if src_key_list.len() != table_info.src_key_typing.len() { if src_key_list.len() != table_info.src_key_typing.len() {
@ -158,7 +188,11 @@ impl<'a, 'b> MutationManager<'a, 'b> {
let mut src_keys = Vec::with_capacity(src_key_list.len()); let mut src_keys = Vec::with_capacity(src_key_list.len());
for (t, v) in table_info.src_key_typing.iter().zip(src_key_list.into_iter()) { for (t, v) in table_info
.src_key_typing
.iter()
.zip(src_key_list.into_iter())
{
let v = t.coerce(v)?; let v = t.coerce(v)?;
key_tuple.push_value(&v); key_tuple.push_value(&v);
src_keys.push(v); src_keys.push(v);
@ -169,14 +203,18 @@ impl<'a, 'b> MutationManager<'a, 'b> {
let dst = val_map.remove("_dst").unwrap_or(Value::Null); let dst = val_map.remove("_dst").unwrap_or(Value::Null);
let dst_key_list = match dst { let dst_key_list = match dst {
Value::List(v) => v, Value::List(v) => v,
v => vec![v] v => vec![v],
}; };
if dst_key_list.len() != table_info.dst_key_typing.len() { if dst_key_list.len() != table_info.dst_key_typing.len() {
return Err(CozoError::LogicError("Error in _dst key".to_string())); return Err(CozoError::LogicError("Error in _dst key".to_string()));
} }
for (t, v) in table_info.dst_key_typing.iter().zip(dst_key_list.into_iter()) { for (t, v) in table_info
.dst_key_typing
.iter()
.zip(dst_key_list.into_iter())
{
let v = t.coerce(v)?; let v = t.coerce(v)?;
key_tuple.push_value(&v); key_tuple.push_value(&v);
ikey_tuple.push_value(&v); ikey_tuple.push_value(&v);
@ -198,13 +236,25 @@ impl<'a, 'b> MutationManager<'a, 'b> {
let processed = v.coerce(raw)?; let processed = v.coerce(raw)?;
val_tuple.push_value(&processed); val_tuple.push_value(&processed);
} }
if error_on_existing && self.sess.key_exists(&key_tuple, table_info.table_id.in_root)? { if error_on_existing
&& self
.sess
.key_exists(&key_tuple, table_info.table_id.in_root)?
{
return Err(CozoError::KeyConflict(key_tuple)); return Err(CozoError::KeyConflict(key_tuple));
} }
self.sess.define_raw_key(&key_tuple, Some(&val_tuple), table_info.table_id.in_root)?; self.sess.define_raw_key(
self.sess.define_raw_key(&ikey_tuple, Some(&key_tuple), table_info.table_id.in_root)?; &key_tuple,
Some(&val_tuple),
table_info.table_id.in_root,
)?;
self.sess.define_raw_key(
&ikey_tuple,
Some(&key_tuple),
table_info.table_id.in_root,
)?;
} }
_ => unreachable!() _ => unreachable!(),
} }
let existing_keys: HashSet<_> = val_map.iter().map(|(k, _)| k.to_string()).collect(); let existing_keys: HashSet<_> = val_map.iter().map(|(k, _)| k.to_string()).collect();
@ -218,7 +268,8 @@ impl<'a, 'b> MutationManager<'a, 'b> {
val_tuple.push_value(&processed); val_tuple.push_value(&processed);
} }
key_tuple.overwrite_prefix(assoc.table_id.id as u32); key_tuple.overwrite_prefix(assoc.table_id.id as u32);
self.sess.define_raw_key(&key_tuple, Some(&val_tuple), assoc.table_id.in_root)?; self.sess
.define_raw_key(&key_tuple, Some(&val_tuple), assoc.table_id.in_root)?;
} }
} }
Ok(()) Ok(())
@ -227,14 +278,14 @@ impl<'a, 'b> MutationManager<'a, 'b> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::BTreeMap;
use std::fs;
use std::time::Instant;
use pest::Parser as PestParser;
use crate::db::engine::Engine; use crate::db::engine::Engine;
use crate::parser::{Parser, Rule}; use crate::parser::{Parser, Rule};
use crate::relation::tuple::Tuple; use crate::relation::tuple::Tuple;
use crate::relation::value::Value; use crate::relation::value::Value;
use pest::Parser as PestParser;
use std::collections::BTreeMap;
use std::fs;
use std::time::Instant;
#[test] #[test]
fn test_mutation() { fn test_mutation() {
@ -349,4 +400,4 @@ mod tests {
drop(engine); drop(engine);
let _ = fs::remove_dir_all(db_path); let _ = fs::remove_dir_all(db_path);
} }
} }

@ -1,14 +1,13 @@
use std::collections::BTreeMap;
use pest::iterators::Pair;
use cozorocks::{IteratorPtr};
use crate::db::engine::Session; use crate::db::engine::Session;
use crate::db::iterator::{IteratorSlot, ExecPlan, OutputIt}; use crate::db::iterator::{ExecPlan, IteratorSlot, OutputIt};
use crate::db::query::{FromEl, Selection}; use crate::db::query::{FromEl, Selection};
use crate::db::table::{ColId, TableId, TableInfo}; use crate::db::table::{ColId, TableId, TableInfo};
use crate::relation::value::{StaticValue, Value}; use crate::error::Result;
use crate::parser::Rule; use crate::parser::Rule;
use crate::error::{Result}; use crate::relation::value::{StaticValue, Value};
use cozorocks::IteratorPtr;
use pest::iterators::Pair;
use std::collections::BTreeMap;
#[derive(Eq, PartialEq, Copy, Clone, Debug)] #[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub enum OuterJoinType { pub enum OuterJoinType {
@ -17,14 +16,13 @@ pub enum OuterJoinType {
FullOuterJoin, FullOuterJoin,
} }
pub type AccessorMap = BTreeMap<String, BTreeMap<String, (TableId, ColId)>>; pub type AccessorMap = BTreeMap<String, BTreeMap<String, (TableId, ColId)>>;
impl<'a> Session<'a> { impl<'a> Session<'a> {
pub fn realize_intermediate_plan(&self, plan: ExecPlan) -> ExecPlan { pub fn realize_intermediate_plan(&self, _plan: ExecPlan) -> ExecPlan {
todo!() todo!()
} }
pub fn realize_output_plan(&self, plan: ExecPlan) -> OutputIt { pub fn realize_output_plan(&self, _plan: ExecPlan) -> OutputIt {
todo!() todo!()
} }
pub fn query_to_plan(&self, pair: Pair<Rule>) -> Result<()> { pub fn query_to_plan(&self, pair: Pair<Rule>) -> Result<()> {
@ -37,7 +35,7 @@ impl<'a> Session<'a> {
nxt = pairs.next().unwrap(); nxt = pairs.next().unwrap();
r r
} }
_ => true.into() _ => true.into(),
}; };
let select_data = self.parse_select_pattern(nxt)?; let select_data = self.parse_select_pattern(nxt)?;
let plan = self.convert_from_data_to_plan(from_data)?; let plan = self.convert_from_data_to_plan(from_data)?;
@ -47,9 +45,12 @@ impl<'a> Session<'a> {
Ok(()) Ok(())
} }
fn convert_from_data_to_plan(&self, mut from_data: Vec<FromEl>) -> Result<ExecPlan> { fn convert_from_data_to_plan(&self, mut from_data: Vec<FromEl>) -> Result<ExecPlan> {
let res = match from_data.pop().unwrap() { let _res = match from_data.pop().unwrap() {
FromEl::Simple(el) => { FromEl::Simple(el) => {
println!("{:#?}", self.base_relation_to_accessor_map(&el.table, &el.binding, &el.info)); println!(
"{:#?}",
self.base_relation_to_accessor_map(&el.table, &el.binding, &el.info)
);
todo!() todo!()
// QueryPlan::BaseRelation { // QueryPlan::BaseRelation {
// table: el.table, // table: el.table,
@ -57,11 +58,17 @@ impl<'a> Session<'a> {
// info: el.info, // info: el.info,
// } // }
} }
FromEl::Chain(_) => todo!() FromEl::Chain(_) => todo!(),
}; };
Ok(res) // Ok(res)
// todo!()
} }
pub(crate) fn base_relation_to_accessor_map(&self, _table: &str, binding: &str, info: &TableInfo) -> AccessorMap { pub(crate) fn base_relation_to_accessor_map(
&self,
_table: &str,
binding: &str,
info: &TableInfo,
) -> AccessorMap {
let mut ret = BTreeMap::new(); let mut ret = BTreeMap::new();
for (i, (k, _)) in info.key_typing.iter().enumerate() { for (i, (k, _)) in info.key_typing.iter().enumerate() {
ret.insert(k.into(), (info.table_id, (true, i).into())); ret.insert(k.into(), (info.table_id, (true, i).into()));
@ -79,11 +86,15 @@ impl<'a> Session<'a> {
} }
BTreeMap::from([(binding.to_string(), ret)]) BTreeMap::from([(binding.to_string(), ret)])
} }
fn convert_where_data_to_plan(&self, plan: ExecPlan, where_data: StaticValue) -> Result<ExecPlan> { fn convert_where_data_to_plan(
&self,
plan: ExecPlan,
where_data: StaticValue,
) -> Result<ExecPlan> {
let where_data = self.partial_eval(where_data, &Default::default(), &Default::default()); let where_data = self.partial_eval(where_data, &Default::default(), &Default::default());
let plan = match where_data?.1 { let _plan = match where_data?.1 {
Value::Bool(true) => plan, Value::Bool(true) => plan,
v => { _v => {
todo!() todo!()
// QueryPlan::Filter { rel: Box::new(plan), filter: v } // QueryPlan::Filter { rel: Box::new(plan), filter: v }
} }
@ -91,7 +102,11 @@ impl<'a> Session<'a> {
// Ok(plan) // Ok(plan)
todo!() todo!()
} }
fn convert_select_data_to_plan(&self, plan: ExecPlan, select_data: Selection) -> Result<ExecPlan> { fn convert_select_data_to_plan(
&self,
_plan: ExecPlan,
_select_data: Selection,
) -> Result<ExecPlan> {
// Ok(MegaTupleIt::Projection { arg: Box::new(plan), projection: select_data }) // Ok(MegaTupleIt::Projection { arg: Box::new(plan), projection: select_data })
todo!() todo!()
} }
@ -106,6 +121,9 @@ impl<'a> Session<'a> {
pub fn iter_node(&self, tid: TableId) -> ExecPlan { pub fn iter_node(&self, tid: TableId) -> ExecPlan {
let it = self.raw_iterator(tid.in_root); let it = self.raw_iterator(tid.in_root);
ExecPlan::NodeIt { it: IteratorSlot::Reified(it), tid: tid.id as u32 } ExecPlan::NodeIt {
it: IteratorSlot::Reified(it),
tid: tid.id as u32,
}
} }
} }

@ -1,13 +1,13 @@
use std::collections::BTreeMap;
use pest::iterators::Pair;
use crate::db::engine::Session; use crate::db::engine::Session;
use crate::db::table::TableInfo; use crate::db::table::TableInfo;
use crate::parser::Rule;
use crate::error::{CozoError, Result}; use crate::error::{CozoError, Result};
use crate::parser::text_identifier::{build_name_in_def, parse_string}; use crate::parser::text_identifier::{build_name_in_def, parse_string};
use crate::parser::Rule;
use crate::relation::data::DataKind; use crate::relation::data::DataKind;
use crate::relation::value; use crate::relation::value;
use crate::relation::value::{StaticValue, Value}; use crate::relation::value::{StaticValue, Value};
use pest::iterators::Pair;
use std::collections::BTreeMap;
#[derive(Debug, Eq, PartialEq, Clone)] #[derive(Debug, Eq, PartialEq, Clone)]
pub enum FromEl { pub enum FromEl {
@ -41,13 +41,14 @@ pub struct EdgeOrNodeEl {
impl<'a> Session<'a> { impl<'a> Session<'a> {
pub fn parse_from_pattern(&self, pair: Pair<Rule>) -> Result<Vec<FromEl>> { pub fn parse_from_pattern(&self, pair: Pair<Rule>) -> Result<Vec<FromEl>> {
let res: Result<Vec<_>> = pair.into_inner().map(|p| { let res: Result<Vec<_>> = pair
match p.as_rule() { .into_inner()
.map(|p| match p.as_rule() {
Rule::simple_from_pattern => self.parse_simple_from_pattern(p), Rule::simple_from_pattern => self.parse_simple_from_pattern(p),
Rule::node_edge_pattern => self.parse_node_edge_pattern(p), Rule::node_edge_pattern => self.parse_node_edge_pattern(p),
_ => unreachable!() _ => unreachable!(),
} })
}).collect(); .collect();
res res
} }
@ -55,17 +56,24 @@ impl<'a> Session<'a> {
let mut pairs = pair.into_inner(); let mut pairs = pair.into_inner();
let name = pairs.next().unwrap().as_str(); let name = pairs.next().unwrap().as_str();
if name.starts_with('_') { if name.starts_with('_') {
return Err(CozoError::LogicError("Pattern binding cannot start with underscore".to_string())); return Err(CozoError::LogicError(
"Pattern binding cannot start with underscore".to_string(),
));
} }
let table_name = build_name_in_def(pairs.next().unwrap(), true)?; let table_name = build_name_in_def(pairs.next().unwrap(), true)?;
let table_info = self.get_table_info(&table_name)?; let table_info = self.get_table_info(&table_name)?;
let ret = FromEl::Simple(Box::new(SimpleFromEl { binding: name.to_string(), table: table_name, info: table_info })); let ret = FromEl::Simple(Box::new(SimpleFromEl {
binding: name.to_string(),
table: table_name,
info: table_info,
}));
Ok(ret) Ok(ret)
} }
fn parse_node_edge_pattern(&self, pair: Pair<Rule>) -> Result<FromEl> { fn parse_node_edge_pattern(&self, pair: Pair<Rule>) -> Result<FromEl> {
let res: Result<Vec<_>> = pair.into_inner().map(|p| { let res: Result<Vec<_>> = pair
match p.as_rule() { .into_inner()
.map(|p| match p.as_rule() {
Rule::node_pattern => self.parse_node_pattern(p), Rule::node_pattern => self.parse_node_pattern(p),
Rule::edge_pattern => { Rule::edge_pattern => {
let right_join; let right_join;
@ -78,21 +86,17 @@ impl<'a> Session<'a> {
right_join = false; right_join = false;
} }
let mut edge = match nxt.as_rule() { let mut edge = match nxt.as_rule() {
Rule::fwd_edge_pattern => { Rule::fwd_edge_pattern => self.parse_edge_pattern(nxt, true)?,
self.parse_edge_pattern(nxt, true)? Rule::bwd_edge_pattern => self.parse_edge_pattern(nxt, false)?,
} _ => unreachable!(),
Rule::bwd_edge_pattern => {
self.parse_edge_pattern(nxt, false)?
}
_ => unreachable!()
}; };
edge.left_outer_marker = pairs.next().is_some(); edge.left_outer_marker = pairs.next().is_some();
edge.right_outer_marker = right_join; edge.right_outer_marker = right_join;
Ok(edge) Ok(edge)
} }
_ => unreachable!() _ => unreachable!(),
} })
}).collect(); .collect();
let res = res?; let res = res?;
let connects = res.windows(2).all(|v| { let connects = res.windows(2).all(|v| {
let left = &v[0]; let left = &v[0];
@ -110,7 +114,7 @@ impl<'a> Session<'a> {
(EdgeOrNodeKind::Node, EdgeOrNodeKind::BwdEdge) => { (EdgeOrNodeKind::Node, EdgeOrNodeKind::BwdEdge) => {
left.info.table_id == right.info.dst_table_id left.info.table_id == right.info.dst_table_id
} }
_ => unreachable!() _ => unreachable!(),
} }
}); });
if !connects { if !connects {
@ -146,7 +150,11 @@ impl<'a> Session<'a> {
table, table,
binding, binding,
info, info,
kind: if is_fwd { EdgeOrNodeKind::FwdEdge } else { EdgeOrNodeKind::BwdEdge }, kind: if is_fwd {
EdgeOrNodeKind::FwdEdge
} else {
EdgeOrNodeKind::BwdEdge
},
left_outer_marker: false, left_outer_marker: false,
right_outer_marker: false, right_outer_marker: false,
}) })
@ -171,7 +179,10 @@ impl<'a> Session<'a> {
} }
pub fn parse_where_pattern(&self, pair: Pair<Rule>) -> Result<Value> { pub fn parse_where_pattern(&self, pair: Pair<Rule>) -> Result<Value> {
let conditions = pair.into_inner().map(Value::from_pair).collect::<Result<Vec<_>>>()?; let conditions = pair
.into_inner()
.map(Value::from_pair)
.collect::<Result<Vec<_>>>()?;
Ok(Value::Apply(value::OP_AND.into(), conditions).to_static()) Ok(Value::Apply(value::OP_AND.into(), conditions).to_static())
} }
@ -185,7 +196,7 @@ impl<'a> Session<'a> {
nxt = pp.next().unwrap(); nxt = pp.next().unwrap();
Some(name.to_string()) Some(name.to_string())
} }
_ => None _ => None,
}; };
let mut keys = BTreeMap::new(); let mut keys = BTreeMap::new();
@ -210,8 +221,14 @@ impl<'a> Session<'a> {
Rule::spreading => { Rule::spreading => {
let el = p.into_inner().next().unwrap(); let el = p.into_inner().next().unwrap();
let to_concat = Value::from_pair(el)?; let to_concat = Value::from_pair(el)?;
if !matches!(to_concat, Value::Dict(_) | Value::Variable(_) | if !matches!(
Value::IdxAccess(_, _) | Value:: FieldAccess(_, _) | Value::Apply(_, _)) { to_concat,
Value::Dict(_)
| Value::Variable(_)
| Value::IdxAccess(_, _)
| Value::FieldAccess(_, _)
| Value::Apply(_, _)
) {
return Err(CozoError::LogicError("Cannot spread".to_string())); return Err(CozoError::LogicError("Cannot spread".to_string()));
} }
if !collected_vals.is_empty() { if !collected_vals.is_empty() {
@ -222,11 +239,11 @@ impl<'a> Session<'a> {
} }
Rule::scoped_accessor => { Rule::scoped_accessor => {
let name = parse_string(p.into_inner().next().unwrap())?; let name = parse_string(p.into_inner().next().unwrap())?;
let val = Value::FieldAccess(name.clone().into(), let val =
Value::Variable("_".into()).into()); Value::FieldAccess(name.clone().into(), Value::Variable("_".into()).into());
collected_vals.insert(name.into(), val); collected_vals.insert(name.into(), val);
} }
_ => unreachable!() _ => unreachable!(),
} }
} }
@ -247,23 +264,40 @@ impl<'a> Session<'a> {
match p.as_rule() { match p.as_rule() {
Rule::order_pattern => { Rule::order_pattern => {
for p in p.into_inner() { for p in p.into_inner() {
ordering.push((p.as_rule() == Rule::order_asc, parse_string(p.into_inner().next().unwrap())?)) ordering.push((
p.as_rule() == Rule::order_asc,
parse_string(p.into_inner().next().unwrap())?,
))
} }
} }
Rule::offset_pattern => { Rule::offset_pattern => {
for p in p.into_inner() { for p in p.into_inner() {
match p.as_rule() { match p.as_rule() {
Rule::limit_clause => { Rule::limit_clause => {
limit = Some(p.into_inner().next().unwrap().as_str().replace('_', "").parse::<i64>()?); limit = Some(
p.into_inner()
.next()
.unwrap()
.as_str()
.replace('_', "")
.parse::<i64>()?,
);
} }
Rule::offset_clause => { Rule::offset_clause => {
offset = Some(p.into_inner().next().unwrap().as_str().replace('_', "").parse::<i64>()?); offset = Some(
p.into_inner()
.next()
.unwrap()
.as_str()
.replace('_', "")
.parse::<i64>()?,
);
} }
_ => unreachable!() _ => unreachable!(),
} }
} }
} }
_ => unreachable!() _ => unreachable!(),
} }
} }
@ -292,9 +326,9 @@ pub struct Selection {
mod tests { mod tests {
use std::fs; use std::fs;
// use super::*; // use super::*;
use crate::db::engine::Engine;
use crate::parser::{Parser, Rule}; use crate::parser::{Parser, Rule};
use pest::Parser as PestParser; use pest::Parser as PestParser;
use crate::db::engine::Engine;
#[test] #[test]
fn parse_patterns() { fn parse_patterns() {
@ -335,27 +369,39 @@ mod tests {
sess.commit().unwrap(); sess.commit().unwrap();
let s = "from a:Friend, (b:Person)-[:Friend]->(c:Z), x:Person"; let s = "from a:Friend, (b:Person)-[:Friend]->(c:Z), x:Person";
let parsed = Parser::parse(Rule::from_pattern, s).unwrap().next().unwrap(); let parsed = Parser::parse(Rule::from_pattern, s)
.unwrap()
.next()
.unwrap();
assert_eq!(parsed.as_rule(), Rule::from_pattern); assert_eq!(parsed.as_rule(), Rule::from_pattern);
assert!(sess.parse_from_pattern(parsed).is_err()); assert!(sess.parse_from_pattern(parsed).is_err());
let s = "from a:Friend, (b:Person)-[:Friend]->?(c:Person), x:Person"; let s = "from a:Friend, (b:Person)-[:Friend]->?(c:Person), x:Person";
let parsed = Parser::parse(Rule::from_pattern, s).unwrap().next().unwrap(); let parsed = Parser::parse(Rule::from_pattern, s)
.unwrap()
.next()
.unwrap();
assert_eq!(parsed.as_rule(), Rule::from_pattern); assert_eq!(parsed.as_rule(), Rule::from_pattern);
let from_pattern = sess.parse_from_pattern(parsed).unwrap(); let from_pattern = sess.parse_from_pattern(parsed).unwrap();
println!("{:#?}", from_pattern); println!("{:#?}", from_pattern);
let s = "where b.id > c.id || x.name.is_null(), a.id == 5, x.name == 'Joe', x.name.len() == 3"; let s = "where b.id > c.id || x.name.is_null(), a.id == 5, x.name == 'Joe', x.name.len() == 3";
let parsed = Parser::parse(Rule::where_pattern, s).unwrap().next().unwrap(); let parsed = Parser::parse(Rule::where_pattern, s)
.unwrap()
.next()
.unwrap();
let where_result = sess.parse_where_pattern(parsed).unwrap(); let where_result = sess.parse_where_pattern(parsed).unwrap();
println!("{:#?}", where_result); println!("{:#?}", where_result);
let s = "select {*id: a.id, b: a.b, c: a.c} ordered [e, +c, -b] limit 1 offset 2"; let s = "select {*id: a.id, b: a.b, c: a.c} ordered [e, +c, -b] limit 1 offset 2";
let parsed = Parser::parse(Rule::select_pattern, s).unwrap().next().unwrap(); let parsed = Parser::parse(Rule::select_pattern, s)
.unwrap()
.next()
.unwrap();
let select_result = sess.parse_select_pattern(parsed).unwrap(); let select_result = sess.parse_select_pattern(parsed).unwrap();
println!("{:#?}", select_result); println!("{:#?}", select_result);
} }
drop(engine); drop(engine);
let _ = fs::remove_dir_all(db_path); let _ = fs::remove_dir_all(db_path);
} }
} }

@ -1,10 +1,10 @@
use std::collections::HashSet;
use std::fmt::{Debug, Formatter};
use crate::db::engine::Session; use crate::db::engine::Session;
use crate::error::{CozoError, Result};
use crate::error::CozoError::LogicError; use crate::error::CozoError::LogicError;
use crate::error::{CozoError, Result};
use crate::relation::data::DataKind; use crate::relation::data::DataKind;
use crate::relation::typing::Typing; use crate::relation::typing::Typing;
use std::collections::HashSet;
use std::fmt::{Debug, Formatter};
#[derive(Eq, PartialEq, Clone, Copy, Ord, PartialOrd, Hash)] #[derive(Eq, PartialEq, Clone, Copy, Ord, PartialOrd, Hash)]
pub struct TableId { pub struct TableId {
@ -29,7 +29,10 @@ impl TableId {
impl From<(bool, usize)> for TableId { impl From<(bool, usize)> for TableId {
fn from((in_root, id): (bool, usize)) -> Self { fn from((in_root, id): (bool, usize)) -> Self {
Self { in_root, id: id as i64 } Self {
in_root,
id: id as i64,
}
} }
} }
@ -59,13 +62,19 @@ impl From<(bool, i64)> for ColId {
impl From<(bool, usize)> for ColId { impl From<(bool, usize)> for ColId {
fn from((is_key, id): (bool, usize)) -> Self { fn from((is_key, id): (bool, usize)) -> Self {
Self { is_key, id: id as i64 } Self {
is_key,
id: id as i64,
}
} }
} }
impl Default for TableId { impl Default for TableId {
fn default() -> Self { fn default() -> Self {
TableId { in_root: false, id: -1 } TableId {
in_root: false,
id: -1,
}
} }
} }
@ -85,19 +94,35 @@ pub struct TableInfo {
impl<'a> Session<'a> { impl<'a> Session<'a> {
pub fn get_table_info(&self, tbl_name: &str) -> Result<TableInfo> { pub fn get_table_info(&self, tbl_name: &str) -> Result<TableInfo> {
let table_info = match self.resolve(&tbl_name)? { let table_info = match self.resolve(tbl_name)? {
None => return Err(CozoError::UndefinedType(tbl_name.to_string())), None => return Err(CozoError::UndefinedType(tbl_name.to_string())),
Some(tpl) => { Some(tpl) => {
let mut main_coercer = match tpl.data_kind()? { let mut main_coercer = match tpl.data_kind()? {
DataKind::Node => { DataKind::Node => {
let key_extractor = Typing::try_from(tpl.get_text(2) let key_extractor = Typing::try_from(
.ok_or_else(|| CozoError::BadDataFormat(tpl.data.as_ref().to_vec()))?.as_ref())? tpl.get_text(2)
.extract_named_tuple().ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?; .ok_or_else(|| {
let val_extractor = Typing::try_from(tpl.get_text(3) CozoError::BadDataFormat(tpl.data.as_ref().to_vec())
.ok_or_else(|| CozoError::BadDataFormat(tpl.data.as_ref().to_vec()))?.as_ref())? })?
.extract_named_tuple().ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?; .as_ref(),
let in_root = tpl.get_bool(0).ok_or_else(|| CozoError::LogicError("Cannot extract in root".to_string()))?; )?
let table_id = tpl.get_int(1).ok_or_else(|| CozoError::LogicError("Cannot extract in root".to_string()))?; .extract_named_tuple()
.ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?;
let val_extractor = Typing::try_from(
tpl.get_text(3)
.ok_or_else(|| {
CozoError::BadDataFormat(tpl.data.as_ref().to_vec())
})?
.as_ref(),
)?
.extract_named_tuple()
.ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?;
let in_root = tpl.get_bool(0).ok_or_else(|| {
CozoError::LogicError("Cannot extract in root".to_string())
})?;
let table_id = tpl.get_int(1).ok_or_else(|| {
CozoError::LogicError("Cannot extract in root".to_string())
})?;
let table_id = TableId::new(in_root, table_id); let table_id = TableId::new(in_root, table_id);
TableInfo { TableInfo {
@ -114,38 +139,72 @@ impl<'a> Session<'a> {
} }
} }
DataKind::Edge => { DataKind::Edge => {
let other_key_extractor = Typing::try_from(tpl.get_text(6) let other_key_extractor = Typing::try_from(
.ok_or_else(|| CozoError::LogicError("Key extraction failed".to_string()))?.as_ref())? tpl.get_text(6)
.extract_named_tuple().ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?; .ok_or_else(|| {
let val_extractor = Typing::try_from(tpl.get_text(7) CozoError::LogicError("Key extraction failed".to_string())
.ok_or_else(|| CozoError::LogicError("Val extraction failed".to_string()))?.as_ref())? })?
.extract_named_tuple().ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?; .as_ref(),
let src_in_root = tpl.get_bool(2) )?
.ok_or_else(|| CozoError::LogicError("Src in root extraction failed".to_string()))?; .extract_named_tuple()
let src_id = tpl.get_int(3) .ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?;
.ok_or_else(|| CozoError::LogicError("Src id extraction failed".to_string()))?; let val_extractor = Typing::try_from(
tpl.get_text(7)
.ok_or_else(|| {
CozoError::LogicError("Val extraction failed".to_string())
})?
.as_ref(),
)?
.extract_named_tuple()
.ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?;
let src_in_root = tpl.get_bool(2).ok_or_else(|| {
CozoError::LogicError("Src in root extraction failed".to_string())
})?;
let src_id = tpl.get_int(3).ok_or_else(|| {
CozoError::LogicError("Src id extraction failed".to_string())
})?;
let src_table_id = TableId::new(src_in_root, src_id); let src_table_id = TableId::new(src_in_root, src_id);
let dst_in_root = tpl.get_bool(4) let dst_in_root = tpl.get_bool(4).ok_or_else(|| {
.ok_or_else(|| CozoError::LogicError("Dst in root extraction failed".to_string()))?; CozoError::LogicError("Dst in root extraction failed".to_string())
let dst_id = tpl.get_int(5) })?;
.ok_or_else(|| CozoError::LogicError("Dst id extraction failed".to_string()))?; let dst_id = tpl.get_int(5).ok_or_else(|| {
CozoError::LogicError("Dst id extraction failed".to_string())
})?;
let dst_table_id = TableId::new(dst_in_root, dst_id); let dst_table_id = TableId::new(dst_in_root, dst_id);
let src = self.table_data(src_id, src_in_root)? let src = self.table_data(src_id, src_in_root)?.ok_or_else(|| {
.ok_or_else(|| CozoError::LogicError("Getting src failed".to_string()))?; CozoError::LogicError("Getting src failed".to_string())
let src_key = Typing::try_from(src.get_text(2) })?;
.ok_or_else(|| CozoError::BadDataFormat(tpl.data.as_ref().to_vec()))?.as_ref())? let src_key = Typing::try_from(
.extract_named_tuple().ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?; src.get_text(2)
.ok_or_else(|| {
CozoError::BadDataFormat(tpl.data.as_ref().to_vec())
})?
.as_ref(),
)?
.extract_named_tuple()
.ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?;
let src_key_typing = src_key.into_iter().map(|(_, v)| v).collect(); let src_key_typing = src_key.into_iter().map(|(_, v)| v).collect();
let dst = self.table_data(dst_id, dst_in_root)? let dst = self.table_data(dst_id, dst_in_root)?.ok_or_else(|| {
.ok_or_else(|| CozoError::LogicError("Getting dst failed".to_string()))?; CozoError::LogicError("Getting dst failed".to_string())
let dst_key = Typing::try_from(dst.get_text(2) })?;
.ok_or_else(|| CozoError::BadDataFormat(tpl.data.as_ref().to_vec()))?.as_ref())? let dst_key = Typing::try_from(
.extract_named_tuple().ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?; dst.get_text(2)
.ok_or_else(|| {
CozoError::BadDataFormat(tpl.data.as_ref().to_vec())
})?
.as_ref(),
)?
.extract_named_tuple()
.ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?;
let dst_key_typing = dst_key.into_iter().map(|(_, v)| v).collect(); let dst_key_typing = dst_key.into_iter().map(|(_, v)| v).collect();
let in_root = tpl.get_bool(0).ok_or_else(|| CozoError::LogicError("Cannot extract in root".to_string()))?; let in_root = tpl.get_bool(0).ok_or_else(|| {
let table_id = tpl.get_int(1).ok_or_else(|| CozoError::LogicError("Cannot extract in root".to_string()))?; CozoError::LogicError("Cannot extract in root".to_string())
})?;
let table_id = tpl.get_int(1).ok_or_else(|| {
CozoError::LogicError("Cannot extract in root".to_string())
})?;
let table_id = TableId::new(in_root, table_id); let table_id = TableId::new(in_root, table_id);
TableInfo { TableInfo {
@ -161,16 +220,22 @@ impl<'a> Session<'a> {
associates: vec![], associates: vec![],
} }
} }
_ => return Err(LogicError("Cannot insert into non-tables".to_string())) _ => return Err(LogicError("Cannot insert into non-tables".to_string())),
}; };
let related = self.resolve_related_tables(&tbl_name)?; let related = self.resolve_related_tables(tbl_name)?;
for (_n, d) in related { for (_n, d) in related {
let t = d.get_text(4) let t = d.get_text(4).ok_or_else(|| {
.ok_or_else(|| CozoError::LogicError("Unable to extract typing from assoc".to_string()))?; CozoError::LogicError("Unable to extract typing from assoc".to_string())
})?;
let t = Typing::try_from(t.as_ref())? let t = Typing::try_from(t.as_ref())?
.extract_named_tuple().ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?; .extract_named_tuple()
let in_root = d.get_bool(0).ok_or_else(|| CozoError::LogicError("Cannot extract in root".to_string()))?; .ok_or_else(|| CozoError::LogicError("Corrupt data".to_string()))?;
let table_id = d.get_int(1).ok_or_else(|| CozoError::LogicError("Cannot extract in root".to_string()))?; let in_root = d.get_bool(0).ok_or_else(|| {
CozoError::LogicError("Cannot extract in root".to_string())
})?;
let table_id = d.get_int(1).ok_or_else(|| {
CozoError::LogicError("Cannot extract in root".to_string())
})?;
let table_id = TableId::new(in_root, table_id); let table_id = TableId::new(in_root, table_id);
let coercer = TableInfo { let coercer = TableInfo {
@ -193,4 +258,4 @@ impl<'a> Session<'a> {
}; };
Ok(table_info) Ok(table_info)
} }
} }

@ -1,11 +1,11 @@
use std::result;
use std::time::SystemTimeError;
use thiserror::Error;
use cozorocks::BridgeError;
use crate::parser::Rule; use crate::parser::Rule;
use crate::relation::data::DataKind; use crate::relation::data::DataKind;
use crate::relation::tuple::OwnTuple; use crate::relation::tuple::OwnTuple;
use crate::relation::value::{StaticValue}; use crate::relation::value::StaticValue;
use cozorocks::BridgeError;
use std::result;
use std::time::SystemTimeError;
use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum CozoError { pub enum CozoError {
@ -15,15 +15,14 @@ pub enum CozoError {
#[error("Invalid escape sequence")] #[error("Invalid escape sequence")]
InvalidEscapeSequence, InvalidEscapeSequence,
// #[error("Type mismatch")] // #[error("Type mismatch")]
// TypeError, // TypeError,
#[error("Reserved identifier")] #[error("Reserved identifier")]
ReservedIdent, ReservedIdent,
// #[error("The requested name exists")] // #[error("The requested name exists")]
// NameConflict, // NameConflict,
// //
#[error("Undefined type '{0}'")] #[error("Undefined type '{0}'")]
UndefinedType(String), UndefinedType(String),
@ -50,31 +49,30 @@ pub enum CozoError {
#[error("Undefined parameter {0}")] #[error("Undefined parameter {0}")]
UndefinedParam(String), UndefinedParam(String),
// //
// #[error("Undefined table")] // #[error("Undefined table")]
// UndefinedTable, // UndefinedTable,
// //
// #[error("Undefined parameter")] // #[error("Undefined parameter")]
// UndefinedParam, // UndefinedParam,
// //
// #[error("Value required")] // #[error("Value required")]
// ValueRequired, // ValueRequired,
// //
// #[error("Incompatible value")] // #[error("Incompatible value")]
// IncompatibleValue, // IncompatibleValue,
// //
// #[error("Wrong type")] // #[error("Wrong type")]
// WrongType, // WrongType,
// //
// #[error("Cannot have global edge between local nodes")] // #[error("Cannot have global edge between local nodes")]
// IncompatibleEdge, // IncompatibleEdge,
// //
// #[error("Unexpected index columns found")] // #[error("Unexpected index columns found")]
// UnexpectedIndexColumns, // UnexpectedIndexColumns,
// //
// #[error("Database already closed")] // #[error("Database already closed")]
// DatabaseClosed, // DatabaseClosed,
#[error("InvalidArgument")] #[error("InvalidArgument")]
InvalidArgument, InvalidArgument,
@ -94,8 +92,8 @@ pub enum CozoError {
TypeMismatch, TypeMismatch,
// #[error(transparent)] // #[error(transparent)]
// Storage(#[from] cozo_rocks::BridgeStatus), // Storage(#[from] cozo_rocks::BridgeStatus),
// //
#[error(transparent)] #[error(transparent)]
Io(#[from] std::io::Error), Io(#[from] std::io::Error),
@ -115,4 +113,4 @@ pub enum CozoError {
Bridge(#[from] BridgeError), Bridge(#[from] BridgeError),
} }
pub type Result<T> = result::Result<T, CozoError>; pub type Result<T> = result::Result<T, CozoError>;

@ -11,10 +11,10 @@ extern crate core;
// pub mod storage; // pub mod storage;
// pub mod mutation; // pub mod mutation;
// pub mod plan; // pub mod plan;
pub mod relation;
pub mod db; pub mod db;
pub mod error; pub mod error;
pub mod parser; pub mod parser;
pub mod relation;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -24,4 +24,4 @@ mod tests {
let _o = OptionsPtr::default(); let _o = OptionsPtr::default();
println!("Hello"); println!("Hello");
} }
} }

@ -1,13 +1,11 @@
pub mod text_identifier;
pub mod number; pub mod number;
pub mod text_identifier;
use pest_derive::Parser; use pest_derive::Parser;
#[derive(Parser)] #[derive(Parser)]
#[grammar = "grammar.pest"] #[grammar = "grammar.pest"]
pub struct Parser; pub struct Parser;
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -20,7 +18,10 @@ mod tests {
assert_eq!(Parser::parse(Rule::ident, "x_y").unwrap().as_str(), "x_y"); assert_eq!(Parser::parse(Rule::ident, "x_y").unwrap().as_str(), "x_y");
assert_eq!(Parser::parse(Rule::ident, "x_").unwrap().as_str(), "x_"); assert_eq!(Parser::parse(Rule::ident, "x_").unwrap().as_str(), "x_");
assert_eq!(Parser::parse(Rule::ident, "你好").unwrap().as_str(), "你好"); assert_eq!(Parser::parse(Rule::ident, "你好").unwrap().as_str(), "你好");
assert_eq!(Parser::parse(Rule::ident, "你好123").unwrap().as_str(), "你好123"); assert_eq!(
Parser::parse(Rule::ident, "你好123").unwrap().as_str(),
"你好123"
);
assert_ne!(Parser::parse(Rule::ident, "x$y").unwrap().as_str(), "x$y"); assert_ne!(Parser::parse(Rule::ident, "x$y").unwrap().as_str(), "x$y");
assert_eq!(Parser::parse(Rule::ident, "_x").unwrap().as_str(), "_x"); assert_eq!(Parser::parse(Rule::ident, "_x").unwrap().as_str(), "_x");
@ -38,33 +39,95 @@ mod tests {
#[test] #[test]
fn strings() { fn strings() {
assert_eq!(Parser::parse(Rule::string, r#""""#).unwrap().as_str(), r#""""#); assert_eq!(
assert_eq!(Parser::parse(Rule::string, r#"" b a c""#).unwrap().as_str(), r#"" b a c""#); Parser::parse(Rule::string, r#""""#).unwrap().as_str(),
assert_eq!(Parser::parse(Rule::string, r#""你好👋""#).unwrap().as_str(), r#""你好👋""#); r#""""#
assert_eq!(Parser::parse(Rule::string, r#""\n""#).unwrap().as_str(), r#""\n""#); );
assert_eq!(Parser::parse(Rule::string, r#""\u5678""#).unwrap().as_str(), r#""\u5678""#); assert_eq!(
Parser::parse(Rule::string, r#"" b a c""#).unwrap().as_str(),
r#"" b a c""#
);
assert_eq!(
Parser::parse(Rule::string, r#""你好👋""#).unwrap().as_str(),
r#""你好👋""#
);
assert_eq!(
Parser::parse(Rule::string, r#""\n""#).unwrap().as_str(),
r#""\n""#
);
assert_eq!(
Parser::parse(Rule::string, r#""\u5678""#).unwrap().as_str(),
r#""\u5678""#
);
assert!(Parser::parse(Rule::string, r#""\ux""#).is_err()); assert!(Parser::parse(Rule::string, r#""\ux""#).is_err());
assert_eq!(Parser::parse(Rule::string, r###"r#"a"#"###).unwrap().as_str(), r##"r#"a"#"##); assert_eq!(
Parser::parse(Rule::string, r###"r#"a"#"###)
.unwrap()
.as_str(),
r##"r#"a"#"##
);
} }
#[test] #[test]
fn numbers() { fn numbers() {
assert_eq!(Parser::parse(Rule::number, "123").unwrap().as_str(), "123"); assert_eq!(Parser::parse(Rule::number, "123").unwrap().as_str(), "123");
assert_eq!(Parser::parse(Rule::number, "0").unwrap().as_str(), "0"); assert_eq!(Parser::parse(Rule::number, "0").unwrap().as_str(), "0");
assert_eq!(Parser::parse(Rule::number, "0123").unwrap().as_str(), "0123"); assert_eq!(
assert_eq!(Parser::parse(Rule::number, "000_1").unwrap().as_str(), "000_1"); Parser::parse(Rule::number, "0123").unwrap().as_str(),
"0123"
);
assert_eq!(
Parser::parse(Rule::number, "000_1").unwrap().as_str(),
"000_1"
);
assert!(Parser::parse(Rule::number, "_000_1").is_err()); assert!(Parser::parse(Rule::number, "_000_1").is_err());
assert_eq!(Parser::parse(Rule::number, "0xAf03").unwrap().as_str(), "0xAf03"); assert_eq!(
assert_eq!(Parser::parse(Rule::number, "0o0_7067").unwrap().as_str(), "0o0_7067"); Parser::parse(Rule::number, "0xAf03").unwrap().as_str(),
assert_ne!(Parser::parse(Rule::number, "0o0_7068").unwrap().as_str(), "0o0_7068"); "0xAf03"
assert_eq!(Parser::parse(Rule::number, "0b0000_0000_1111").unwrap().as_str(), "0b0000_0000_1111"); );
assert_ne!(Parser::parse(Rule::number, "0b0000_0000_1112").unwrap().as_str(), "0b0000_0000_1112"); assert_eq!(
Parser::parse(Rule::number, "0o0_7067").unwrap().as_str(),
"0o0_7067"
);
assert_ne!(
Parser::parse(Rule::number, "0o0_7068").unwrap().as_str(),
"0o0_7068"
);
assert_eq!(
Parser::parse(Rule::number, "0b0000_0000_1111")
.unwrap()
.as_str(),
"0b0000_0000_1111"
);
assert_ne!(
Parser::parse(Rule::number, "0b0000_0000_1112")
.unwrap()
.as_str(),
"0b0000_0000_1112"
);
assert_eq!(Parser::parse(Rule::number, "123.45").unwrap().as_str(), "123.45"); assert_eq!(
assert_eq!(Parser::parse(Rule::number, "1_23.4_5_").unwrap().as_str(), "1_23.4_5_"); Parser::parse(Rule::number, "123.45").unwrap().as_str(),
assert_ne!(Parser::parse(Rule::number, "123.").unwrap().as_str(), "123."); "123.45"
assert_eq!(Parser::parse(Rule::number, "123.333e456").unwrap().as_str(), "123.333e456"); );
assert_eq!(Parser::parse(Rule::number, "1_23.33_3e45_6").unwrap().as_str(), "1_23.33_3e45_6"); assert_eq!(
Parser::parse(Rule::number, "1_23.4_5_").unwrap().as_str(),
"1_23.4_5_"
);
assert_ne!(
Parser::parse(Rule::number, "123.").unwrap().as_str(),
"123."
);
assert_eq!(
Parser::parse(Rule::number, "123.333e456").unwrap().as_str(),
"123.333e456"
);
assert_eq!(
Parser::parse(Rule::number, "1_23.33_3e45_6")
.unwrap()
.as_str(),
"1_23.33_3e45_6"
);
} }
#[test] #[test]

@ -1,11 +1,17 @@
use pest::iterators::Pair;
use crate::parser::Rule;
use crate::error::{CozoError, Result}; use crate::error::{CozoError, Result};
use crate::parser::number::parse_int; use crate::parser::number::parse_int;
use crate::parser::Rule;
use pest::iterators::Pair;
#[inline] #[inline]
fn parse_raw_string(pair: Pair<Rule>) -> Result<String> { fn parse_raw_string(pair: Pair<Rule>) -> Result<String> {
Ok(pair.into_inner().into_iter().next().unwrap().as_str().to_string()) Ok(pair
.into_inner()
.into_iter()
.next()
.unwrap()
.as_str()
.to_string())
} }
#[inline] #[inline]
@ -29,13 +35,12 @@ fn parse_quoted_string(pair: Pair<Rule>) -> Result<String> {
ret.push(ch); ret.push(ch);
} }
s if s.starts_with('\\') => return Err(CozoError::InvalidEscapeSequence), s if s.starts_with('\\') => return Err(CozoError::InvalidEscapeSequence),
s => ret.push_str(s) s => ret.push_str(s),
} }
} }
Ok(ret) Ok(ret)
} }
#[inline] #[inline]
fn parse_s_quoted_string(pair: Pair<Rule>) -> Result<String> { fn parse_s_quoted_string(pair: Pair<Rule>) -> Result<String> {
let pairs = pair.into_inner().next().unwrap().into_inner(); let pairs = pair.into_inner().next().unwrap().into_inner();
@ -57,7 +62,7 @@ fn parse_s_quoted_string(pair: Pair<Rule>) -> Result<String> {
ret.push(ch); ret.push(ch);
} }
s if s.starts_with('\\') => return Err(CozoError::InvalidEscapeSequence), s if s.starts_with('\\') => return Err(CozoError::InvalidEscapeSequence),
s => ret.push_str(s) s => ret.push_str(s),
} }
} }
Ok(ret) Ok(ret)
@ -70,7 +75,7 @@ pub fn parse_string(pair: Pair<Rule>) -> Result<String> {
Rule::s_quoted_string => Ok(parse_s_quoted_string(pair)?), Rule::s_quoted_string => Ok(parse_s_quoted_string(pair)?),
Rule::raw_string => Ok(parse_raw_string(pair)?), Rule::raw_string => Ok(parse_raw_string(pair)?),
Rule::ident => Ok(pair.as_str().to_string()), Rule::ident => Ok(pair.as_str().to_string()),
_ => unreachable!() _ => unreachable!(),
} }
} }
@ -83,7 +88,7 @@ pub fn build_name_in_def(pair: Pair<Rule>, forbid_underscore: bool) -> Result<St
let name = match inner.as_rule() { let name = match inner.as_rule() {
Rule::ident => parse_ident(inner), Rule::ident => parse_ident(inner),
Rule::raw_string | Rule::s_quoted_string | Rule::quoted_string => parse_string(inner)?, Rule::raw_string | Rule::s_quoted_string | Rule::quoted_string => parse_string(inner)?,
_ => unreachable!() _ => unreachable!(),
}; };
if forbid_underscore && name.starts_with('_') { if forbid_underscore && name.starts_with('_') {
Err(CozoError::ReservedIdent) Err(CozoError::ReservedIdent)

@ -1,6 +1,6 @@
pub mod tuple; pub mod data;
pub mod value;
pub mod key_order; pub mod key_order;
pub mod typing;
pub mod table; pub mod table;
pub mod data; pub mod tuple;
pub mod typing;
pub mod value;

@ -1,7 +1,7 @@
use std::borrow::Borrow;
use crate::relation::tuple::Tuple;
use crate::error::{CozoError, Result}; use crate::error::{CozoError, Result};
use crate::relation::tuple::Tuple;
use crate::relation::typing::Typing; use crate::relation::typing::Typing;
use std::borrow::Borrow;
#[repr(u32)] #[repr(u32)]
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Clone)] #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Clone)]
@ -32,11 +32,13 @@ impl<T: AsRef<[u8]>> Tuple<T> {
5 => Val, 5 => Val,
6 => Type, 6 => Type,
u32::MAX => Empty, u32::MAX => Empty,
v => return Err(CozoError::UndefinedDataKind(v)) v => return Err(CozoError::UndefinedDataKind(v)),
}) })
} }
pub fn interpret_as_type(&self) -> Result<Typing> { pub fn interpret_as_type(&self) -> Result<Typing> {
let text = self.get_text(0).ok_or_else(|| CozoError::BadDataFormat(self.as_ref().to_vec()))?; let text = self
.get_text(0)
.ok_or_else(|| CozoError::BadDataFormat(self.as_ref().to_vec()))?;
Typing::try_from(text.borrow()) Typing::try_from(text.borrow())
} }
} }

@ -1,5 +1,5 @@
use std::cmp::Ordering;
use crate::relation::tuple::Tuple; use crate::relation::tuple::Tuple;
use std::cmp::Ordering;
impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialOrd<Tuple<T2>> for Tuple<T> { impl<T: AsRef<[u8]>, T2: AsRef<[u8]>> PartialOrd<Tuple<T2>> for Tuple<T> {
fn partial_cmp(&self, other: &Tuple<T2>) -> Option<Ordering> { fn partial_cmp(&self, other: &Tuple<T2>) -> Option<Ordering> {
@ -24,16 +24,16 @@ pub fn compare(a: &[u8], b: &[u8]) -> i8 {
match ta.cmp(&tb) { match ta.cmp(&tb) {
Ordering::Less => -1, Ordering::Less => -1,
Ordering::Greater => 1, Ordering::Greater => 1,
Ordering::Equal => 0 Ordering::Equal => 0,
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::BTreeMap;
use crate::relation::key_order::compare; use crate::relation::key_order::compare;
use crate::relation::tuple::Tuple; use crate::relation::tuple::Tuple;
use crate::relation::value::Value; use crate::relation::value::Value;
use std::collections::BTreeMap;
#[test] #[test]
fn ordering() { fn ordering() {
@ -56,8 +56,15 @@ mod tests {
t2.push_int(123); t2.push_int(123);
assert_eq!(compare(t.as_ref(), t2.as_ref()), -1); assert_eq!(compare(t.as_ref(), t2.as_ref()), -1);
assert_eq!(compare(t.as_ref(), t.as_ref()), 0); assert_eq!(compare(t.as_ref(), t.as_ref()), 0);
let vals: Value = vec![().into(), BTreeMap::new().into(), 1e23.into(), false.into(), "xxyx".into()].into(); let vals: Value = vec![
().into(),
BTreeMap::new().into(),
1e23.into(),
false.into(),
"xxyx".into(),
]
.into();
t.push_value(&vals); t.push_value(&vals);
assert_eq!(compare(t.as_ref(), t.as_ref()), 0); assert_eq!(compare(t.as_ref(), t.as_ref()), 0);
} }
} }

@ -1,6 +1,6 @@
use std::cmp::Ordering; use crate::relation::tuple::CowTuple;
use crate::relation::tuple::{CowTuple};
use crate::relation::typing::Typing; use crate::relation::typing::Typing;
use std::cmp::Ordering;
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
pub struct StorageId { pub struct StorageId {
@ -49,7 +49,10 @@ pub struct MegaTuple {
impl MegaTuple { impl MegaTuple {
pub fn empty_tuple() -> Self { pub fn empty_tuple() -> Self {
MegaTuple { keys: vec![], vals: vec![] } MegaTuple {
keys: vec![],
vals: vec![],
}
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.keys.is_empty() self.keys.is_empty()
@ -72,10 +75,10 @@ impl MegaTuple {
pub fn all_keys_cmp(&self, other: &Self) -> Ordering { pub fn all_keys_cmp(&self, other: &Self) -> Ordering {
for (l, r) in self.keys.iter().zip(&other.keys) { for (l, r) in self.keys.iter().zip(&other.keys) {
match l.key_part_cmp(r) { match l.key_part_cmp(r) {
Ordering::Equal => {}, Ordering::Equal => {}
v => return v v => return v,
} }
} }
Ordering::Equal Ordering::Equal
} }
} }

@ -1,24 +1,28 @@
use std::borrow::{Cow}; use crate::db::table::{ColId, TableId};
use crate::relation::data::DataKind;
use crate::relation::value::{Tag, Value};
use cozorocks::SlicePtr;
use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use uuid::Uuid; use uuid::Uuid;
use cozorocks::SlicePtr;
use crate::db::table::{ColId, TableId};
use crate::relation::data::DataKind;
use crate::relation::value::{Tag, Value};
#[derive(Clone)] #[derive(Clone)]
pub struct Tuple<T> pub struct Tuple<T>
where T: AsRef<[u8]> where
T: AsRef<[u8]>,
{ {
pub data: T, pub data: T,
idx_cache: RefCell<Vec<usize>>, idx_cache: RefCell<Vec<usize>>,
} }
impl<T> AsRef<[u8]> for Tuple<T> where T: AsRef<[u8]> { impl<T> AsRef<[u8]> for Tuple<T>
where
T: AsRef<[u8]>,
{
fn as_ref(&self) -> &[u8] { fn as_ref(&self) -> &[u8] {
self.data.as_ref() self.data.as_ref()
} }
@ -55,7 +59,7 @@ impl AsRef<[u8]> for CowSlice {
fn as_ref(&self) -> &[u8] { fn as_ref(&self) -> &[u8] {
match self { match self {
CowSlice::Ptr(s) => s.as_ref(), CowSlice::Ptr(s) => s.as_ref(),
CowSlice::Own(o) => o.as_ref() CowSlice::Own(o) => o.as_ref(),
} }
} }
} }
@ -76,7 +80,7 @@ impl CowTuple {
pub fn to_owned(self) -> OwnTuple { pub fn to_owned(self) -> OwnTuple {
match self.data { match self.data {
CowSlice::Ptr(p) => OwnTuple::new(p.as_ref().to_vec()), CowSlice::Ptr(p) => OwnTuple::new(p.as_ref().to_vec()),
CowSlice::Own(o) => OwnTuple::new(o) CowSlice::Own(o) => OwnTuple::new(o),
} }
} }
} }
@ -120,7 +124,7 @@ impl<T: AsRef<[u8]>> Tuple<T> {
fn all_cached(&self) -> bool { fn all_cached(&self) -> bool {
match self.idx_cache.borrow().last() { match self.idx_cache.borrow().last() {
None => self.data.as_ref().len() == PREFIX_LEN, None => self.data.as_ref().len() == PREFIX_LEN,
Some(l) => *l == self.data.as_ref().len() Some(l) => *l == self.data.as_ref().len(),
} }
} }
#[inline] #[inline]
@ -157,11 +161,9 @@ impl<T: AsRef<[u8]>> Tuple<T> {
let slen = slen as usize; let slen = slen as usize;
start + slen + offset start + slen + offset
} }
Tag::List | Tag::List | Tag::Apply | Tag::Dict | Tag::IdxAccess | Tag::FieldAccess => {
Tag::Apply | start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize
Tag::Dict | }
Tag::IdxAccess |
Tag::FieldAccess => start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize,
Tag::TupleRef => { Tag::TupleRef => {
let temp = start + 1 + self.parse_varint(start + 1).1 + 1; let temp = start + 1 + self.parse_varint(start + 1).1 + 1;
temp + self.parse_varint(temp).1 temp + self.parse_varint(temp).1
@ -202,7 +204,7 @@ impl<T: AsRef<[u8]>> Tuple<T> {
} }
Some(val) Some(val)
} }
None => None None => None,
} }
} }
@ -210,7 +212,7 @@ impl<T: AsRef<[u8]>> Tuple<T> {
pub fn get_null(&self, idx: usize) -> Option<()> { pub fn get_null(&self, idx: usize) -> Option<()> {
match self.get(idx)? { match self.get(idx)? {
Value::Null => Some(()), Value::Null => Some(()),
_ => None _ => None,
} }
} }
@ -218,7 +220,7 @@ impl<T: AsRef<[u8]>> Tuple<T> {
pub fn get_int(&self, idx: usize) -> Option<i64> { pub fn get_int(&self, idx: usize) -> Option<i64> {
match self.get(idx)? { match self.get(idx)? {
Value::Int(i) => Some(i), Value::Int(i) => Some(i),
_ => None _ => None,
} }
} }
@ -226,7 +228,7 @@ impl<T: AsRef<[u8]>> Tuple<T> {
pub fn get_text(&self, idx: usize) -> Option<Cow<str>> { pub fn get_text(&self, idx: usize) -> Option<Cow<str>> {
match self.get(idx)? { match self.get(idx)? {
Value::Text(d) => Some(d), Value::Text(d) => Some(d),
_ => None _ => None,
} }
} }
@ -234,16 +236,15 @@ impl<T: AsRef<[u8]>> Tuple<T> {
pub fn get_bool(&self, idx: usize) -> Option<bool> { pub fn get_bool(&self, idx: usize) -> Option<bool> {
match self.get(idx)? { match self.get(idx)? {
Value::Bool(b) => Some(b), Value::Bool(b) => Some(b),
_ => None _ => None,
} }
} }
#[inline] #[inline]
pub fn get_float(&self, idx: usize) -> Option<f64> { pub fn get_float(&self, idx: usize) -> Option<f64> {
match self.get(idx)? { match self.get(idx)? {
Value::Float(f) => Some(f.into_inner()), Value::Float(f) => Some(f.into_inner()),
_ => None _ => None,
} }
} }
@ -251,7 +252,7 @@ impl<T: AsRef<[u8]>> Tuple<T> {
pub fn get_uuid(&self, idx: usize) -> Option<Uuid> { pub fn get_uuid(&self, idx: usize) -> Option<Uuid> {
match self.get(idx)? { match self.get(idx)? {
Value::Uuid(u) => Some(u), Value::Uuid(u) => Some(u),
_ => None _ => None,
} }
} }
@ -259,7 +260,7 @@ impl<T: AsRef<[u8]>> Tuple<T> {
pub fn get_list(&self, idx: usize) -> Option<Vec<Value>> { pub fn get_list(&self, idx: usize) -> Option<Vec<Value>> {
match self.get(idx)? { match self.get(idx)? {
Value::List(u) => Some(u), Value::List(u) => Some(u),
_ => None _ => None,
} }
} }
@ -267,7 +268,7 @@ impl<T: AsRef<[u8]>> Tuple<T> {
pub fn get_dict(&self, idx: usize) -> Option<BTreeMap<Cow<str>, Value>> { pub fn get_dict(&self, idx: usize) -> Option<BTreeMap<Cow<str>, Value>> {
match self.get(idx)? { match self.get(idx)? {
Value::Dict(u) => Some(u), Value::Dict(u) => Some(u),
_ => None _ => None,
} }
} }
@ -275,7 +276,7 @@ impl<T: AsRef<[u8]>> Tuple<T> {
pub fn get_variable(&self, idx: usize) -> Option<Cow<str>> { pub fn get_variable(&self, idx: usize) -> Option<Cow<str>> {
match self.get(idx)? { match self.get(idx)? {
Value::Variable(u) => Some(u), Value::Variable(u) => Some(u),
_ => None _ => None,
} }
} }
@ -283,7 +284,7 @@ impl<T: AsRef<[u8]>> Tuple<T> {
pub fn get_apply(&self, idx: usize) -> Option<(Cow<str>, Vec<Value>)> { pub fn get_apply(&self, idx: usize) -> Option<(Cow<str>, Vec<Value>)> {
match self.get(idx)? { match self.get(idx)? {
Value::Apply(n, l) => Some((n, l)), Value::Apply(n, l) => Some((n, l)),
_ => None _ => None,
} }
} }
@ -293,7 +294,7 @@ impl<T: AsRef<[u8]>> Tuple<T> {
let start = pos + 1; let start = pos + 1;
let tag = match Tag::try_from(data[pos]) { let tag = match Tag::try_from(data[pos]) {
Ok(t) => t, Ok(t) => t,
Err(e) => panic!("Cannot parse tag {} for {:?}", e, data) Err(e) => panic!("Cannot parse tag {} for {:?}", e, data),
}; };
let (nxt, val): (usize, Value) = match tag { let (nxt, val): (usize, Value) = match tag {
Tag::Null => (start, ().into()), Tag::Null => (start, ().into()),
@ -304,8 +305,14 @@ impl<T: AsRef<[u8]>> Tuple<T> {
let val = Self::varint_to_zigzag(u); let val = Self::varint_to_zigzag(u);
(start + offset, val.into()) (start + offset, val.into())
} }
Tag::Float => (start + 8, f64::from_be_bytes(data[start..start + 8].try_into().unwrap()).into()), Tag::Float => (
Tag::Uuid => (start + 16, Uuid::from_slice(&data[start..start + 16]).unwrap().into()), start + 8,
f64::from_be_bytes(data[start..start + 8].try_into().unwrap()).into(),
),
Tag::Uuid => (
start + 16,
Uuid::from_slice(&data[start..start + 16]).unwrap().into(),
),
Tag::Text => { Tag::Text => {
let (slen, offset) = self.parse_varint(start); let (slen, offset) = self.parse_varint(start);
let slen = slen as usize; let slen = slen as usize;
@ -325,7 +332,8 @@ impl<T: AsRef<[u8]>> Tuple<T> {
(start + slen + offset, Value::Variable(s.into())) (start + slen + offset, Value::Variable(s.into()))
} }
Tag::List => { Tag::List => {
let end_pos = start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize; let end_pos =
start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize;
let mut start_pos = start + 4; let mut start_pos = start + 4;
let mut collected = vec![]; let mut collected = vec![];
while start_pos < end_pos { while start_pos < end_pos {
@ -336,14 +344,15 @@ impl<T: AsRef<[u8]>> Tuple<T> {
(end_pos, collected.into()) (end_pos, collected.into())
} }
Tag::Apply => { Tag::Apply => {
let end_pos = start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize; let end_pos =
start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize;
let mut start_pos = start + 4; let mut start_pos = start + 4;
let mut collected = vec![]; let mut collected = vec![];
let (val, new_pos) = self.parse_value_at(start_pos); let (val, new_pos) = self.parse_value_at(start_pos);
start_pos = new_pos; start_pos = new_pos;
let op = match val { let op = match val {
Value::Variable(s) => s, Value::Variable(s) => s,
_ => panic!("Corrupt data when parsing Apply") _ => panic!("Corrupt data when parsing Apply"),
}; };
while start_pos < end_pos { while start_pos < end_pos {
let (val, new_pos) = self.parse_value_at(start_pos); let (val, new_pos) = self.parse_value_at(start_pos);
@ -353,7 +362,8 @@ impl<T: AsRef<[u8]>> Tuple<T> {
(end_pos, Value::Apply(op, collected)) (end_pos, Value::Apply(op, collected))
} }
Tag::Dict => { Tag::Dict => {
let end_pos = start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize; let end_pos =
start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize;
let mut start_pos = start + 4; let mut start_pos = start + 4;
let mut collected: BTreeMap<Cow<str>, Value> = BTreeMap::new(); let mut collected: BTreeMap<Cow<str>, Value> = BTreeMap::new();
while start_pos < end_pos { while start_pos < end_pos {
@ -371,7 +381,8 @@ impl<T: AsRef<[u8]>> Tuple<T> {
} }
Tag::MaxTag => (start, Value::EndSentinel), Tag::MaxTag => (start, Value::EndSentinel),
Tag::IdxAccess => { Tag::IdxAccess => {
let end_pos = start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize; let end_pos =
start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize;
let mut start_pos = start + 4; let mut start_pos = start + 4;
let (idx, offset) = self.parse_varint(start_pos); let (idx, offset) = self.parse_varint(start_pos);
start_pos += offset; start_pos += offset;
@ -379,7 +390,8 @@ impl<T: AsRef<[u8]>> Tuple<T> {
(end_pos, Value::IdxAccess(idx as usize, val.into())) (end_pos, Value::IdxAccess(idx as usize, val.into()))
} }
Tag::FieldAccess => { Tag::FieldAccess => {
let end_pos = start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize; let end_pos =
start + u32::from_be_bytes(data[start..start + 4].try_into().unwrap()) as usize;
let mut start_pos = start + 4; let mut start_pos = start + 4;
let (slen, offset) = self.parse_varint(start); let (slen, offset) = self.parse_varint(start);
@ -398,9 +410,19 @@ impl<T: AsRef<[u8]>> Tuple<T> {
let (tidu, parse_len) = self.parse_varint(start + 1); let (tidu, parse_len) = self.parse_varint(start + 1);
let is_key = self.parse_value_at(parse_len + start + 1).0 == Value::Bool(true); let is_key = self.parse_value_at(parse_len + start + 1).0 == Value::Bool(true);
let (cidu, parse_len2) = self.parse_varint(start + 1 + parse_len + 1); let (cidu, parse_len2) = self.parse_varint(start + 1 + parse_len + 1);
(start + 1 + parse_len + 1 + parse_len2, (
Value::TupleRef(TableId { in_root, id: Self::varint_to_zigzag(tidu) }, start + 1 + parse_len + 1 + parse_len2,
ColId { is_key, id: Self::varint_to_zigzag(cidu) })) Value::TupleRef(
TableId {
in_root,
id: Self::varint_to_zigzag(tidu),
},
ColId {
is_key,
id: Self::varint_to_zigzag(cidu),
},
),
)
} }
}; };
(val, nxt) (val, nxt)
@ -431,8 +453,12 @@ impl<T: AsRef<[u8]>> Debug for Tuple<T> {
write!(f, "Tuple<{}>{{", self.get_prefix())?; write!(f, "Tuple<{}>{{", self.get_prefix())?;
} }
} }
let strings = self.iter().enumerate().map(|(i, v)| format!("{}: {}", i, v)) let strings = self
.collect::<Vec<_>>().join(", "); .iter()
.enumerate()
.map(|(i, v)| format!("{}: {}", i, v))
.collect::<Vec<_>>()
.join(", ");
write!(f, "{}}}", strings) write!(f, "{}}}", strings)
} }
} }
@ -641,7 +667,6 @@ impl OwnTuple {
} }
} }
#[inline] #[inline]
fn push_varint(&mut self, u: u64) { fn push_varint(&mut self, u: u64) {
let mut u = u; let mut u = u;
@ -687,7 +712,7 @@ impl OwnTuple {
impl<'a> Extend<Value<'a>> for OwnTuple { impl<'a> Extend<Value<'a>> for OwnTuple {
#[inline] #[inline]
fn extend<T: IntoIterator<Item=Value<'a>>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = Value<'a>>>(&mut self, iter: T) {
for v in iter { for v in iter {
self.push_value(&v) self.push_value(&v)
} }
@ -709,11 +734,10 @@ impl<T: AsRef<[u8]>> Hash for Tuple<T> {
impl<T: AsRef<[u8]>> Eq for Tuple<T> {} impl<T: AsRef<[u8]>> Eq for Tuple<T> {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::collections::BTreeMap;
use super::*; use super::*;
use std::collections::BTreeMap;
#[test] #[test]
fn serde() { fn serde() {
@ -724,11 +748,7 @@ mod tests {
t.push_null(); t.push_null();
t.push_str("abcdef"); t.push_str("abcdef");
t.push_null(); t.push_null();
t.push_value(&vec![ t.push_value(&vec![true.into(), 1e236.into(), "xxyyzz".into()].into());
true.into(),
1e236.into(),
"xxyyzz".into(),
].into());
t.push_int(-123345); t.push_int(-123345);
t.push_value(&BTreeMap::from([]).into()); t.push_value(&BTreeMap::from([]).into());
t.push_int(12121212); t.push_int(12121212);
@ -758,11 +778,14 @@ mod tests {
assert_eq!(Value::Null, t.get(5).unwrap()); assert_eq!(Value::Null, t.get(5).unwrap());
t3.get_pos(6); t3.get_pos(6);
assert_eq!(t.idx_cache.borrow().last(), t3.idx_cache.borrow().last()); assert_eq!(t.idx_cache.borrow().last(), t3.idx_cache.borrow().last());
assert_eq!(Value::from(Value::from(vec![ assert_eq!(
true.into(), Value::from(Value::from(vec![
1e236.into(), true.into(),
"xxyyzz".into(), 1e236.into(),
])), t.get(6).unwrap()); "xxyyzz".into(),
])),
t.get(6).unwrap()
);
t3.get_pos(7); t3.get_pos(7);
assert_eq!(t.idx_cache.borrow().last(), t3.idx_cache.borrow().last()); assert_eq!(t.idx_cache.borrow().last(), t3.idx_cache.borrow().last());
assert_eq!(Value::from(-123345i64), t.get(7).unwrap()); assert_eq!(Value::from(-123345i64), t.get(7).unwrap());
@ -774,7 +797,10 @@ mod tests {
assert_eq!(Value::from(12121212i64), t.get(9).unwrap()); assert_eq!(Value::from(12121212i64), t.get(9).unwrap());
t3.get_pos(10); t3.get_pos(10);
assert_eq!(t.idx_cache.borrow().last(), t3.idx_cache.borrow().last()); assert_eq!(t.idx_cache.borrow().last(), t3.idx_cache.borrow().last());
assert_eq!(Value::from(BTreeMap::from([("yzyz".into(), "fifo".into())])), t.get(10).unwrap()); assert_eq!(
Value::from(BTreeMap::from([("yzyz".into(), "fifo".into())])),
t.get(10).unwrap()
);
t3.get_pos(11); t3.get_pos(11);
assert_eq!(t.idx_cache.borrow().last(), t3.idx_cache.borrow().last()); assert_eq!(t.idx_cache.borrow().last(), t3.idx_cache.borrow().last());
assert_eq!(Value::from(1e245), t.get(11).unwrap()); assert_eq!(Value::from(1e245), t.get(11).unwrap());
@ -797,14 +823,20 @@ mod tests {
assert_eq!(Value::from(BTreeMap::new()), t.get(8).unwrap()); assert_eq!(Value::from(BTreeMap::new()), t.get(8).unwrap());
assert_eq!(Value::Null, t.get(3).unwrap()); assert_eq!(Value::Null, t.get(3).unwrap());
assert_eq!(Value::from("abcdef"), t.get(4).unwrap()); assert_eq!(Value::from("abcdef"), t.get(4).unwrap());
assert_eq!(Value::from(Value::from(vec![ assert_eq!(
true.into(), Value::from(Value::from(vec![
1e236.into(), true.into(),
"xxyyzz".into(), 1e236.into(),
])), t.get(6).unwrap()); "xxyyzz".into(),
])),
t.get(6).unwrap()
);
assert_eq!(None, t.get(13)); assert_eq!(None, t.get(13));
assert_eq!(Value::from(-123345i64), t.get(7).unwrap()); assert_eq!(Value::from(-123345i64), t.get(7).unwrap());
assert_eq!(Value::from(BTreeMap::from([("yzyz".into(), "fifo".into())])), t.get(10).unwrap()); assert_eq!(
Value::from(BTreeMap::from([("yzyz".into(), "fifo".into())])),
t.get(10).unwrap()
);
assert_eq!(None, t.get(13131)); assert_eq!(None, t.get(13131));
println!("{:?}", t.iter().collect::<Vec<Value>>()); println!("{:?}", t.iter().collect::<Vec<Value>>());
@ -834,4 +866,4 @@ mod tests {
v.push_int(-64); v.push_int(-64);
println!("{:?} {:?}", v, v.data); println!("{:?} {:?}", v, v.data);
} }
} }

@ -1,14 +1,13 @@
use std::fmt::{Display, Formatter};
use pest::iterators::Pair;
use crate::error::{Result, CozoError};
use crate::relation::value::Value;
use pest::Parser as PestParser;
use crate::db::engine::Session; use crate::db::engine::Session;
use crate::error::{CozoError, Result};
use crate::parser::text_identifier::build_name_in_def;
use crate::parser::Parser; use crate::parser::Parser;
use crate::parser::Rule; use crate::parser::Rule;
use crate::parser::text_identifier::build_name_in_def;
use crate::relation::data::DataKind; use crate::relation::data::DataKind;
use crate::relation::value::Value;
use pest::iterators::Pair;
use pest::Parser as PestParser;
use std::fmt::{Display, Formatter};
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)] #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone)]
pub enum Typing { pub enum Typing {
@ -42,15 +41,21 @@ impl Display for Typing {
write!(f, "({})", joined) write!(f, "({})", joined)
} }
Typing::NamedTuple(n) => { Typing::NamedTuple(n) => {
let collected = n.iter().map(|(k, v)| let collected = n
format!(r##""{}":{}"##, k, v)).collect::<Vec<_>>(); .iter()
.map(|(k, v)| format!(r##""{}":{}"##, k, v))
.collect::<Vec<_>>();
let joined = collected.join(","); let joined = collected.join(",");
write!(f, "{{")?; write!(f, "{{")?;
write!(f, "{}", joined)?; write!(f, "{}", joined)?;
write!(f, "}}") write!(f, "}}")
} }
Typing::Function(args, ret) => { Typing::Function(args, ret) => {
let args_display = args.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(","); let args_display = args
.iter()
.map(|t| t.to_string())
.collect::<Vec<_>>()
.join(",");
write!(f, "<{}>->{}", args_display, ret) write!(f, "<{}>->{}", args_display, ret)
} }
} }
@ -69,7 +74,7 @@ impl Typing {
} }
impl Typing { impl Typing {
pub fn from_pair<'a, 't>(pair: Pair<Rule>, env: Option<&Session<'a>>) -> Result<Self> { pub fn from_pair<'a>(pair: Pair<Rule>, env: Option<&Session<'a>>) -> Result<Self> {
Ok(match pair.as_rule() { Ok(match pair.as_rule() {
Rule::simple_type => match pair.as_str() { Rule::simple_type => match pair.as_str() {
"Any" => Typing::Any, "Any" => Typing::Any,
@ -78,52 +83,67 @@ impl Typing {
"Float" => Typing::Float, "Float" => Typing::Float,
"Text" => Typing::Text, "Text" => Typing::Text,
"Uuid" => Typing::Uuid, "Uuid" => Typing::Uuid,
t => { t => match env {
match env { None => return Err(CozoError::UndefinedType(t.to_string())),
None => return Err(CozoError::UndefinedType(t.to_string())), Some(env) => {
Some(env) => { let resolved = env.resolve(t)?;
let resolved = env.resolve(t)?; let resolved =
let resolved = resolved.ok_or_else(|| CozoError::UndefinedType(t.to_string()))?; resolved.ok_or_else(|| CozoError::UndefinedType(t.to_string()))?;
match resolved.data_kind()? { match resolved.data_kind()? {
DataKind::Type => resolved.interpret_as_type()?, DataKind::Type => resolved.interpret_as_type()?,
_ => return Err(CozoError::UndefinedType(t.to_string())) _ => return Err(CozoError::UndefinedType(t.to_string())),
}
} }
} }
} },
}, },
Rule::nullable_type => Typing::Nullable(Box::new(Typing::from_pair(pair.into_inner().next().unwrap(), env)?)), Rule::nullable_type => Typing::Nullable(Box::new(Typing::from_pair(
Rule::homogeneous_list_type => Typing::Homogeneous(Box::new(Typing::from_pair(pair.into_inner().next().unwrap(), env)?)), pair.into_inner().next().unwrap(),
env,
)?)),
Rule::homogeneous_list_type => Typing::Homogeneous(Box::new(Typing::from_pair(
pair.into_inner().next().unwrap(),
env,
)?)),
Rule::unnamed_tuple_type => { Rule::unnamed_tuple_type => {
let types = pair.into_inner().map(|p| Typing::from_pair(p, env)).collect::<Result<Vec<Typing>>>()?; let types = pair
.into_inner()
.map(|p| Typing::from_pair(p, env))
.collect::<Result<Vec<Typing>>>()?;
Typing::UnnamedTuple(types) Typing::UnnamedTuple(types)
} }
Rule::named_tuple_type => { Rule::named_tuple_type => {
let types = pair.into_inner().map(|p| -> Result<(String, Typing)> { let types = pair
let mut ps = p.into_inner(); .into_inner()
let name_pair = ps.next().unwrap(); .map(|p| -> Result<(String, Typing)> {
let name = build_name_in_def(name_pair, true)?; let mut ps = p.into_inner();
let typ_pair = ps.next().unwrap(); let name_pair = ps.next().unwrap();
let typ = Typing::from_pair(typ_pair, env)?; let name = build_name_in_def(name_pair, true)?;
Ok((name, typ)) let typ_pair = ps.next().unwrap();
}).collect::<Result<Vec<(String, Typing)>>>()?; let typ = Typing::from_pair(typ_pair, env)?;
Ok((name, typ))
})
.collect::<Result<Vec<(String, Typing)>>>()?;
Typing::NamedTuple(types) Typing::NamedTuple(types)
} }
Rule::function_type => { Rule::function_type => {
let mut pairs = pair.into_inner(); let mut pairs = pair.into_inner();
let args = pairs.next().unwrap().into_inner() let args = pairs
.map(|p| Typing::from_pair(p, env)).collect::<Result<Vec<_>>>()?; .next()
.unwrap()
.into_inner()
.map(|p| Typing::from_pair(p, env))
.collect::<Result<Vec<_>>>()?;
let ret = Typing::from_pair(pairs.next().unwrap(), env)?; let ret = Typing::from_pair(pairs.next().unwrap(), env)?;
Typing::Function(args, ret.into()) Typing::Function(args, ret.into())
} }
_ => unreachable!() _ => unreachable!(),
}) })
} }
pub fn extract_named_tuple(self) -> Option<Vec<(String, Typing)>> { pub fn extract_named_tuple(self) -> Option<Vec<(String, Typing)>> {
match self { match self {
Typing::NamedTuple(t) => Some(t), Typing::NamedTuple(t) => Some(t),
_ => None _ => None,
} }
} }
@ -149,14 +169,14 @@ impl Typing {
Typing::Float => self.coerce_float(v), Typing::Float => self.coerce_float(v),
Typing::Text => self.coerce_text(v), Typing::Text => self.coerce_text(v),
Typing::Uuid => self.coerce_uuid(v), Typing::Uuid => self.coerce_uuid(v),
Typing::Homogeneous(t) => { Typing::Homogeneous(t) => match v {
match v { Value::List(vs) => Ok(Value::List(
Value::List(vs) => { vs.into_iter()
Ok(Value::List(vs.into_iter().map(|v| t.coerce(v)).collect::<Result<Vec<_>>>()?)) .map(|v| t.coerce(v))
} .collect::<Result<Vec<_>>>()?,
_ => Err(CozoError::TypeMismatch) )),
} _ => Err(CozoError::TypeMismatch),
} },
Typing::UnnamedTuple(_ut) => { Typing::UnnamedTuple(_ut) => {
todo!() todo!()
} }
@ -165,37 +185,39 @@ impl Typing {
} }
Typing::Any => unreachable!(), Typing::Any => unreachable!(),
Typing::Nullable(_) => unreachable!(), Typing::Nullable(_) => unreachable!(),
Typing::Function(_, _) => Err(CozoError::LogicError("Cannot coerce function types".to_string())) Typing::Function(_, _) => Err(CozoError::LogicError(
"Cannot coerce function types".to_string(),
)),
} }
} }
fn coerce_bool<'a>(&self, v: Value<'a>) -> Result<Value<'a>> { fn coerce_bool<'a>(&self, v: Value<'a>) -> Result<Value<'a>> {
match v { match v {
v @ Value::Bool(_) => Ok(v), v @ Value::Bool(_) => Ok(v),
_ => Err(CozoError::TypeMismatch) _ => Err(CozoError::TypeMismatch),
} }
} }
fn coerce_int<'a>(&self, v: Value<'a>) -> Result<Value<'a>> { fn coerce_int<'a>(&self, v: Value<'a>) -> Result<Value<'a>> {
match v { match v {
v @ Value::Int(_) => Ok(v), v @ Value::Int(_) => Ok(v),
_ => Err(CozoError::TypeMismatch) _ => Err(CozoError::TypeMismatch),
} }
} }
fn coerce_float<'a>(&self, v: Value<'a>) -> Result<Value<'a>> { fn coerce_float<'a>(&self, v: Value<'a>) -> Result<Value<'a>> {
match v { match v {
v @ Value::Float(_) => Ok(v), v @ Value::Float(_) => Ok(v),
_ => Err(CozoError::TypeMismatch) _ => Err(CozoError::TypeMismatch),
} }
} }
fn coerce_text<'a>(&self, v: Value<'a>) -> Result<Value<'a>> { fn coerce_text<'a>(&self, v: Value<'a>) -> Result<Value<'a>> {
match v { match v {
v @ Value::Text(_) => Ok(v), v @ Value::Text(_) => Ok(v),
_ => Err(CozoError::TypeMismatch) _ => Err(CozoError::TypeMismatch),
} }
} }
fn coerce_uuid<'a>(&self, v: Value<'a>) -> Result<Value<'a>> { fn coerce_uuid<'a>(&self, v: Value<'a>) -> Result<Value<'a>> {
match v { match v {
v @ Value::Uuid(_) => Ok(v), v @ Value::Uuid(_) => Ok(v),
_ => Err(CozoError::TypeMismatch) _ => Err(CozoError::TypeMismatch),
} }
} }
} }
@ -217,7 +239,10 @@ mod tests {
#[test] #[test]
fn to_string() { fn to_string() {
assert_eq!( assert_eq!(
format!("{}", Typing::Nullable(Box::new(Typing::Homogeneous(Box::new(Typing::Text))))), format!(
"{}",
Typing::Nullable(Box::new(Typing::Homogeneous(Box::new(Typing::Text))))
),
"?[Text]" "?[Text]"
); );
} }
@ -246,4 +271,4 @@ mod tests {
println!("{:#?}", res); println!("{:#?}", res);
assert!(res.is_ok()); assert!(res.is_ok());
} }
} }

@ -1,19 +1,18 @@
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::fmt::{Debug, Display, Formatter, Write};
use lazy_static::lazy_static;
use pest::prec_climber::{Assoc, PrecClimber, Operator};
use ordered_float::OrderedFloat;
use pest::Parser as PestParser;
use pest::iterators::Pair;
use uuid::Uuid;
use crate::db::table::{ColId, TableId}; use crate::db::table::{ColId, TableId};
use crate::parser::{Parser, Rule};
use crate::error::{CozoError, Result};
use crate::error::CozoError::LogicError; use crate::error::CozoError::LogicError;
use crate::error::{CozoError, Result};
use crate::parser::number::parse_int; use crate::parser::number::parse_int;
use crate::parser::text_identifier::parse_string; use crate::parser::text_identifier::parse_string;
use crate::parser::{Parser, Rule};
use lazy_static::lazy_static;
use ordered_float::OrderedFloat;
use pest::iterators::Pair;
use pest::prec_climber::{Assoc, Operator, PrecClimber};
use pest::Parser as PestParser;
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::fmt::{Debug, Display, Formatter, Write};
use uuid::Uuid;
#[repr(u8)] #[repr(u8)]
#[derive(Ord, PartialOrd, Eq, PartialEq)] #[derive(Ord, PartialOrd, Eq, PartialEq)]
@ -60,7 +59,7 @@ impl TryFrom<u8> for Tag {
253 => Variable, 253 => Variable,
254 => Apply, 254 => Apply,
255 => MaxTag, 255 => MaxTag,
v => return Err(v) v => return Err(v),
}) })
} }
} }
@ -91,7 +90,6 @@ impl TryFrom<u8> for Tag {
// C64Arr = 73, // C64Arr = 73,
// C128Arr = 74, // C128Arr = 74,
#[derive(Clone, PartialEq, Ord, PartialOrd, Eq)] #[derive(Clone, PartialEq, Ord, PartialOrd, Eq)]
pub enum Value<'a> { pub enum Value<'a> {
// evaluated // evaluated
@ -107,7 +105,7 @@ pub enum Value<'a> {
// not evaluated // not evaluated
Variable(Cow<'a, str>), Variable(Cow<'a, str>),
TupleRef(TableId, ColId), TupleRef(TableId, ColId),
Apply(Cow<'a, str>, Vec<Value<'a>>), // TODO optimization: special case for small number of args (esp. 0, 1, 2) Apply(Cow<'a, str>, Vec<Value<'a>>), // TODO optimization: special case for small number of args (esp. 0, 1, 2)
FieldAccess(Cow<'a, str>, Box<Value<'a>>), FieldAccess(Cow<'a, str>, Box<Value<'a>>),
IdxAccess(usize, Box<Value<'a>>), IdxAccess(usize, Box<Value<'a>>),
// cannot exist // cannot exist
@ -116,7 +114,7 @@ pub enum Value<'a> {
pub type StaticValue = Value<'static>; pub type StaticValue = Value<'static>;
impl <'a> Debug for Value<'a> { impl<'a> Debug for Value<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Value {{ {} }}", self) write!(f, "Value {{ {} }}", self)
} }
@ -133,41 +131,47 @@ impl<'a> Value<'a> {
Value::Uuid(u) => Value::from(u), Value::Uuid(u) => Value::from(u),
Value::Text(t) => Value::from(t.into_owned()), Value::Text(t) => Value::from(t.into_owned()),
Value::Variable(s) => Value::Variable(Cow::Owned(s.into_owned())), Value::Variable(s) => Value::Variable(Cow::Owned(s.into_owned())),
Value::List(l) => l.into_iter().map(|v| v.to_static()).collect::<Vec<StaticValue>>().into(), Value::List(l) => l
Value::Apply(op, args) => { .into_iter()
Value::Apply(Cow::Owned(op.into_owned()), .map(|v| v.to_static())
args.into_iter().map(|v| v.to_static()).collect::<Vec<StaticValue>>()) .collect::<Vec<StaticValue>>()
} .into(),
Value::Dict(d) => d.into_iter() Value::Apply(op, args) => Value::Apply(
Cow::Owned(op.into_owned()),
args.into_iter()
.map(|v| v.to_static())
.collect::<Vec<StaticValue>>(),
),
Value::Dict(d) => d
.into_iter()
.map(|(k, v)| (Cow::Owned(k.into_owned()), v.to_static())) .map(|(k, v)| (Cow::Owned(k.into_owned()), v.to_static()))
.collect::<BTreeMap<Cow<'static, str>, StaticValue>>().into(), .collect::<BTreeMap<Cow<'static, str>, StaticValue>>()
.into(),
Value::EndSentinel => panic!("Cannot process sentinel value"), Value::EndSentinel => panic!("Cannot process sentinel value"),
Value::FieldAccess(field, value) => { Value::FieldAccess(field, value) => {
Value::FieldAccess(Cow::from(field.into_owned()), value.to_static().into()) Value::FieldAccess(Cow::from(field.into_owned()), value.to_static().into())
} }
Value::IdxAccess(idx, value) => { Value::IdxAccess(idx, value) => Value::IdxAccess(idx, value.to_static().into()),
Value::IdxAccess(idx, value.to_static().into()) Value::TupleRef(tid, cid) => Value::TupleRef(tid, cid),
}
Value::TupleRef(tid, cid) => Value::TupleRef(tid, cid)
} }
} }
#[inline] #[inline]
pub fn is_evaluated(&self) -> bool { pub fn is_evaluated(&self) -> bool {
match self { match self {
Value::Null | Value::Null
Value::Bool(_) | | Value::Bool(_)
Value::Int(_) | | Value::Int(_)
Value::Float(_) | | Value::Float(_)
Value::Uuid(_) | | Value::Uuid(_)
Value::Text(_) | | Value::Text(_)
Value::EndSentinel => true, | Value::EndSentinel => true,
Value::List(l) => l.iter().all(|v| v.is_evaluated()), Value::List(l) => l.iter().all(|v| v.is_evaluated()),
Value::Dict(d) => d.values().all(|v| v.is_evaluated()), Value::Dict(d) => d.values().all(|v| v.is_evaluated()),
Value::Variable(_) => false, Value::Variable(_) => false,
Value::Apply(_, _) => false, Value::Apply(_, _) => false,
Value::FieldAccess(_, _) => false, Value::FieldAccess(_, _) => false,
Value::IdxAccess(_, _) => false, Value::IdxAccess(_, _) => false,
Value::TupleRef(_, _) => false Value::TupleRef(_, _) => false,
} }
} }
#[inline] #[inline]
@ -182,7 +186,9 @@ impl<'a> Value<'a> {
Value::from_pair(pair) Value::from_pair(pair)
} }
pub fn extract_relevant_tables<T: Iterator<Item=Self>>(data: T) -> Result<(Vec<Self>, Vec<TableId>)> { pub fn extract_relevant_tables<T: Iterator<Item = Self>>(
data: T,
) -> Result<(Vec<Self>, Vec<TableId>)> {
let mut coll = vec![]; let mut coll = vec![];
let mut res = Vec::with_capacity(data.size_hint().1.unwrap_or(0)); let mut res = Vec::with_capacity(data.size_hint().1.unwrap_or(0));
for v in data { for v in data {
@ -193,24 +199,23 @@ impl<'a> Value<'a> {
fn do_extract_relevant_tables(self, coll: &mut Vec<TableId>) -> Result<Self> { fn do_extract_relevant_tables(self, coll: &mut Vec<TableId>) -> Result<Self> {
Ok(match self { Ok(match self {
v @ (Value::Null | v @ (Value::Null
Value::Bool(_) | | Value::Bool(_)
Value::Int(_) | | Value::Int(_)
Value::Float(_) | | Value::Float(_)
Value::Uuid(_) | | Value::Uuid(_)
Value::Text(_) | | Value::Text(_)
Value::Variable(_)) => v, | Value::Variable(_)) => v,
Value::List(l) => { Value::List(l) => Value::List(
Value::List(l.into_iter() l.into_iter()
.map(|v| v.do_extract_relevant_tables(coll)) .map(|v| v.do_extract_relevant_tables(coll))
.collect::<Result<Vec<_>>>()?) .collect::<Result<Vec<_>>>()?,
} ),
Value::Dict(d) => { Value::Dict(d) => Value::Dict(
Value::Dict(d.into_iter() d.into_iter()
.map(|(k, v)| .map(|(k, v)| v.do_extract_relevant_tables(coll).map(|v| (k, v)))
v.do_extract_relevant_tables(coll).map(|v| (k, v))) .collect::<Result<BTreeMap<_, _>>>()?,
.collect::<Result<BTreeMap<_, _>>>()?) ),
}
Value::TupleRef(tid, cid) => { Value::TupleRef(tid, cid) => {
let pos = coll.iter().position(|id| id == &tid).unwrap_or_else(|| { let pos = coll.iter().position(|id| id == &tid).unwrap_or_else(|| {
let olen = coll.len(); let olen = coll.len();
@ -219,11 +224,12 @@ impl<'a> Value<'a> {
}); });
Value::TupleRef((false, pos).into(), cid) Value::TupleRef((false, pos).into(), cid)
} }
Value::Apply(op, args) => { Value::Apply(op, args) => Value::Apply(
Value::Apply(op, args.into_iter() op,
args.into_iter()
.map(|v| v.do_extract_relevant_tables(coll)) .map(|v| v.do_extract_relevant_tables(coll))
.collect::<Result<Vec<_>>>()?) .collect::<Result<Vec<_>>>()?,
} ),
Value::FieldAccess(field, arg) => { Value::FieldAccess(field, arg) => {
Value::FieldAccess(field, arg.do_extract_relevant_tables(coll)?.into()) Value::FieldAccess(field, arg.do_extract_relevant_tables(coll)?.into())
} }
@ -272,7 +278,6 @@ impl From<f64> for StaticValue {
} }
} }
impl From<OrderedFloat<f64>> for StaticValue { impl From<OrderedFloat<f64>> for StaticValue {
#[inline] #[inline]
fn from(f: OrderedFloat<f64>) -> Self { fn from(f: OrderedFloat<f64>) -> Self {
@ -315,28 +320,55 @@ impl<'a> From<BTreeMap<Cow<'a, str>, Value<'a>>> for Value<'a> {
} }
} }
impl<'a> Display for Value<'a> { impl<'a> Display for Value<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self { match self {
Value::Null => { write!(f, "null")?; } Value::Null => {
Value::Bool(b) => { write!(f, "{}", if *b { "true" } else { "false" })?; } write!(f, "null")?;
Value::Int(i) => { write!(f, "{}", i)?; } }
Value::Float(n) => { write!(f, "{}", n.into_inner())?; } Value::Bool(b) => {
Value::Uuid(u) => { write!(f, "{}", u)?; } write!(f, "{}", if *b { "true" } else { "false" })?;
}
Value::Int(i) => {
write!(f, "{}", i)?;
}
Value::Float(n) => {
write!(f, "{}", n.into_inner())?;
}
Value::Uuid(u) => {
write!(f, "{}", u)?;
}
Value::Text(t) => { Value::Text(t) => {
f.write_char('"')?; f.write_char('"')?;
for char in t.chars() { for char in t.chars() {
match char { match char {
'"' => { f.write_str("\\\"")?; } '"' => {
'\\' => { f.write_str("\\\\")?; } f.write_str("\\\"")?;
'/' => { f.write_str("\\/")?; } }
'\x08' => { f.write_str("\\b")?; } '\\' => {
'\x0c' => { f.write_str("\\f")?; } f.write_str("\\\\")?;
'\n' => { f.write_str("\\n")?; } }
'\r' => { f.write_str("\\r")?; } '/' => {
'\t' => { f.write_str("\\t")?; } f.write_str("\\/")?;
c => { f.write_char(c)?; } }
'\x08' => {
f.write_str("\\b")?;
}
'\x0c' => {
f.write_str("\\f")?;
}
'\n' => {
f.write_str("\\n")?;
}
'\r' => {
f.write_str("\\r")?;
}
'\t' => {
f.write_str("\\t")?;
}
c => {
f.write_char(c)?;
}
} }
} }
f.write_char('"')?; f.write_char('"')?;
@ -367,15 +399,18 @@ impl<'a> Display for Value<'a> {
} }
f.write_char('}')?; f.write_char('}')?;
} }
Value::Variable(s) => { Value::Variable(s) => write!(f, "`{}`", s)?,
write!(f, "`{}`", s)? Value::EndSentinel => write!(f, "Sentinel")?,
}
Value::EndSentinel => {
write!(f, "Sentinel")?
}
Value::Apply(op, args) => { Value::Apply(op, args) => {
write!(f, "({} {})", op, write!(
args.iter().map(|v| v.to_string()).collect::<Vec<_>>().join(" "))?; f,
"({} {})",
op,
args.iter()
.map(|v| v.to_string())
.collect::<Vec<_>>()
.join(" ")
)?;
} }
Value::FieldAccess(field, value) => { Value::FieldAccess(field, value) => {
write!(f, "(.{} {})", field, value)?; write!(f, "(.{} {})", field, value)?;
@ -384,7 +419,14 @@ impl<'a> Display for Value<'a> {
write!(f, "(.{} {})", idx, value)?; write!(f, "(.{} {})", idx, value)?;
} }
Value::TupleRef(tid, cid) => { Value::TupleRef(tid, cid) => {
write!(f, "#{}{}.{}{}", if tid.in_root { 'G' } else { 'L' }, tid.id, if cid.is_key { 'K' } else { 'D' }, cid.id)?; write!(
f,
"#{}{}.{}{}",
if tid.in_root { 'G' } else { 'L' },
tid.id,
if cid.is_key { 'K' } else { 'D' },
cid.id
)?;
} }
} }
Ok(()) Ok(())
@ -398,13 +440,18 @@ lazy_static! {
PrecClimber::new(vec![ PrecClimber::new(vec![
Operator::new(Rule::op_or, Left), Operator::new(Rule::op_or, Left),
Operator::new(Rule::op_and, Left), Operator::new(Rule::op_and, Left),
Operator::new(Rule::op_gt, Left) | Operator::new(Rule::op_lt, Left) | Operator::new(Rule::op_ge,Left) | Operator::new(Rule::op_le, Left), Operator::new(Rule::op_gt, Left)
| Operator::new(Rule::op_lt, Left)
| Operator::new(Rule::op_ge, Left)
| Operator::new(Rule::op_le, Left),
Operator::new(Rule::op_mod, Left), Operator::new(Rule::op_mod, Left),
Operator::new(Rule::op_eq, Left) | Operator::new(Rule::op_ne, Left), Operator::new(Rule::op_eq, Left) | Operator::new(Rule::op_ne, Left),
Operator::new(Rule::op_add, Left) | Operator::new(Rule::op_sub, Left) | Operator::new(Rule::op_str_cat, Left), Operator::new(Rule::op_add, Left)
| Operator::new(Rule::op_sub, Left)
| Operator::new(Rule::op_str_cat, Left),
Operator::new(Rule::op_mul, Left) | Operator::new(Rule::op_div, Left), Operator::new(Rule::op_mul, Left) | Operator::new(Rule::op_div, Left),
Operator::new(Rule::op_pow, Assoc::Right), Operator::new(Rule::op_pow, Assoc::Right),
Operator::new(Rule::op_coalesce, Assoc::Left) Operator::new(Rule::op_coalesce, Assoc::Left),
]) ])
}; };
} }
@ -432,8 +479,11 @@ pub const METHOD_NOT_NULL: &str = "not_null";
pub const METHOD_CONCAT: &str = "concat"; pub const METHOD_CONCAT: &str = "concat";
pub const METHOD_MERGE: &str = "merge"; pub const METHOD_MERGE: &str = "merge";
fn build_expr_infix<'a>(
fn build_expr_infix<'a>(lhs: Result<Value<'a>>, op: Pair<Rule>, rhs: Result<Value<'a>>) -> Result<Value<'a>> { lhs: Result<Value<'a>>,
op: Pair<Rule>,
rhs: Result<Value<'a>>,
) -> Result<Value<'a>> {
let lhs = lhs?; let lhs = lhs?;
let rhs = rhs?; let rhs = rhs?;
let op = match op.as_rule() { let op = match op.as_rule() {
@ -453,12 +503,11 @@ fn build_expr_infix<'a>(lhs: Result<Value<'a>>, op: Pair<Rule>, rhs: Result<Valu
Rule::op_le => OP_LE, Rule::op_le => OP_LE,
Rule::op_pow => OP_POW, Rule::op_pow => OP_POW,
Rule::op_coalesce => OP_COALESCE, Rule::op_coalesce => OP_COALESCE,
_ => unreachable!() _ => unreachable!(),
}; };
Ok(Value::Apply(op.into(), vec![lhs, rhs])) Ok(Value::Apply(op.into(), vec![lhs, rhs]))
} }
fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> { fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> {
match pair.as_rule() { match pair.as_rule() {
Rule::expr => build_expr_primary(pair.into_inner().next().unwrap()), Rule::expr => build_expr_primary(pair.into_inner().next().unwrap()),
@ -483,7 +532,7 @@ fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> {
args.extend(pairs.map(Value::from_pair).collect::<Result<Vec<_>>>()?); args.extend(pairs.map(Value::from_pair).collect::<Result<Vec<_>>>()?);
head = Value::Apply(method_name.into(), args); head = Value::Apply(method_name.into(), args);
} }
_ => todo!() _ => todo!(),
} }
} }
Ok(head) Ok(head)
@ -498,7 +547,7 @@ fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> {
Rule::term => return build_expr_primary(p), Rule::term => return build_expr_primary(p),
Rule::negate => OP_NEGATE, Rule::negate => OP_NEGATE,
Rule::minus => OP_MINUS, Rule::minus => OP_MINUS,
_ => unreachable!() _ => unreachable!(),
}; };
let term = build_expr_primary(inner.next().unwrap())?; let term = build_expr_primary(inner.next().unwrap())?;
Ok(Value::Apply(op.into(), vec![term])) Ok(Value::Apply(op.into(), vec![term]))
@ -508,11 +557,14 @@ fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> {
Rule::hex_pos_int => Ok(Value::Int(parse_int(pair.as_str(), 16))), Rule::hex_pos_int => Ok(Value::Int(parse_int(pair.as_str(), 16))),
Rule::octo_pos_int => Ok(Value::Int(parse_int(pair.as_str(), 8))), Rule::octo_pos_int => Ok(Value::Int(parse_int(pair.as_str(), 8))),
Rule::bin_pos_int => Ok(Value::Int(parse_int(pair.as_str(), 2))), Rule::bin_pos_int => Ok(Value::Int(parse_int(pair.as_str(), 2))),
Rule::dot_float | Rule::sci_float => Ok(Value::Float(pair.as_str().replace('_', "").parse::<f64>()?.into())), Rule::dot_float | Rule::sci_float => Ok(Value::Float(
pair.as_str().replace('_', "").parse::<f64>()?.into(),
)),
Rule::null => Ok(Value::Null), Rule::null => Ok(Value::Null),
Rule::boolean => Ok(Value::Bool(pair.as_str() == "true")), Rule::boolean => Ok(Value::Bool(pair.as_str() == "true")),
Rule::quoted_string | Rule::s_quoted_string | Rule::raw_string => Ok( Rule::quoted_string | Rule::s_quoted_string | Rule::raw_string => {
Value::Text(Cow::Owned(parse_string(pair)?))), Ok(Value::Text(Cow::Owned(parse_string(pair)?)))
}
Rule::list => { Rule::list => {
let mut spread_collected = vec![]; let mut spread_collected = vec![];
let mut collected = vec![]; let mut collected = vec![];
@ -522,8 +574,14 @@ fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> {
Rule::spreading => { Rule::spreading => {
let el = p.into_inner().next().unwrap(); let el = p.into_inner().next().unwrap();
let to_concat = Value::from_pair(el)?; let to_concat = Value::from_pair(el)?;
if !matches!(to_concat, Value::List(_) | Value::Variable(_) | if !matches!(
Value::IdxAccess(_, _) | Value:: FieldAccess(_, _) | Value::Apply(_, _)) { to_concat,
Value::List(_)
| Value::Variable(_)
| Value::IdxAccess(_, _)
| Value::FieldAccess(_, _)
| Value::Apply(_, _)
) {
return Err(CozoError::LogicError("Cannot spread".to_string())); return Err(CozoError::LogicError("Cannot spread".to_string()));
} }
if !collected.is_empty() { if !collected.is_empty() {
@ -532,7 +590,7 @@ fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> {
} }
spread_collected.push(to_concat); spread_collected.push(to_concat);
} }
_ => unreachable!() _ => unreachable!(),
} }
} }
if spread_collected.is_empty() { if spread_collected.is_empty() {
@ -556,15 +614,23 @@ fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> {
} }
Rule::scoped_accessor => { Rule::scoped_accessor => {
let name = parse_string(p.into_inner().next().unwrap())?; let name = parse_string(p.into_inner().next().unwrap())?;
let val = Value::FieldAccess(name.clone().into(), let val = Value::FieldAccess(
Value::Variable("_".into()).into()); name.clone().into(),
Value::Variable("_".into()).into(),
);
collected.insert(name.into(), val); collected.insert(name.into(), val);
} }
Rule::spreading => { Rule::spreading => {
let el = p.into_inner().next().unwrap(); let el = p.into_inner().next().unwrap();
let to_concat = build_expr_primary(el)?; let to_concat = build_expr_primary(el)?;
if !matches!(to_concat, Value::Dict(_) | Value::Variable(_) | if !matches!(
Value::IdxAccess(_, _) | Value:: FieldAccess(_, _) | Value::Apply(_, _)) { to_concat,
Value::Dict(_)
| Value::Variable(_)
| Value::IdxAccess(_, _)
| Value::FieldAccess(_, _)
| Value::Apply(_, _)
) {
return Err(CozoError::LogicError("Cannot spread".to_string())); return Err(CozoError::LogicError("Cannot spread".to_string()));
} }
if !collected.is_empty() { if !collected.is_empty() {
@ -573,7 +639,7 @@ fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> {
} }
spread_collected.push(to_concat); spread_collected.push(to_concat);
} }
_ => unreachable!() _ => unreachable!(),
} }
} }
@ -586,12 +652,8 @@ fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> {
} }
Ok(Value::Apply(METHOD_MERGE.into(), spread_collected)) Ok(Value::Apply(METHOD_MERGE.into(), spread_collected))
} }
Rule::param => { Rule::param => Ok(Value::Variable(pair.as_str().into())),
Ok(Value::Variable(pair.as_str().into())) Rule::ident => Ok(Value::Variable(pair.as_str().into())),
}
Rule::ident => {
Ok(Value::Variable(pair.as_str().into()))
}
_ => { _ => {
println!("Unhandled rule {:?}", pair.as_rule()); println!("Unhandled rule {:?}", pair.as_rule());
unimplemented!() unimplemented!()
@ -599,7 +661,6 @@ fn build_expr_primary(pair: Pair<Rule>) -> Result<Value> {
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -607,7 +668,10 @@ mod tests {
use pest::Parser as PestParser; use pest::Parser as PestParser;
fn parse_expr_from_str<S: AsRef<str>>(s: S) -> Result<StaticValue> { fn parse_expr_from_str<S: AsRef<str>>(s: S) -> Result<StaticValue> {
let pair = Parser::parse(Rule::expr, s.as_ref()).unwrap().next().unwrap(); let pair = Parser::parse(Rule::expr, s.as_ref())
.unwrap()
.next()
.unwrap();
Value::from_pair(pair).map(|v| v.to_static()) Value::from_pair(pair).map(|v| v.to_static())
} }
@ -628,22 +692,55 @@ mod tests {
assert_eq!(parse_expr_from_str("1").unwrap(), Value::Int(1)); assert_eq!(parse_expr_from_str("1").unwrap(), Value::Int(1));
assert_eq!(parse_expr_from_str("12_3").unwrap(), Value::Int(123)); assert_eq!(parse_expr_from_str("12_3").unwrap(), Value::Int(123));
assert_eq!(parse_expr_from_str("0xaf").unwrap(), Value::Int(0xaf)); assert_eq!(parse_expr_from_str("0xaf").unwrap(), Value::Int(0xaf));
assert_eq!(parse_expr_from_str("0xafcE_f").unwrap(), Value::Int(0xafcef)); assert_eq!(
assert_eq!(parse_expr_from_str("0o1234_567").unwrap(), Value::Int(0o1234567)); parse_expr_from_str("0xafcE_f").unwrap(),
assert_eq!(parse_expr_from_str("0o0001234_567").unwrap(), Value::Int(0o1234567)); Value::Int(0xafcef)
assert_eq!(parse_expr_from_str("0b101010").unwrap(), Value::Int(0b101010)); );
assert_eq!(
assert_eq!(parse_expr_from_str("0.0").unwrap(), Value::Float((0.).into())); parse_expr_from_str("0o1234_567").unwrap(),
assert_eq!(parse_expr_from_str("10.022_3").unwrap(), Value::Float(10.0223.into())); Value::Int(0o1234567)
assert_eq!(parse_expr_from_str("10.022_3e-100").unwrap(), Value::Float(10.0223e-100.into())); );
assert_eq!(
parse_expr_from_str("0o0001234_567").unwrap(),
Value::Int(0o1234567)
);
assert_eq!(
parse_expr_from_str("0b101010").unwrap(),
Value::Int(0b101010)
);
assert_eq!(
parse_expr_from_str("0.0").unwrap(),
Value::Float((0.).into())
);
assert_eq!(
parse_expr_from_str("10.022_3").unwrap(),
Value::Float(10.0223.into())
);
assert_eq!(
parse_expr_from_str("10.022_3e-100").unwrap(),
Value::Float(10.0223e-100.into())
);
assert_eq!(parse_expr_from_str("null").unwrap(), Value::Null); assert_eq!(parse_expr_from_str("null").unwrap(), Value::Null);
assert_eq!(parse_expr_from_str("true").unwrap(), Value::Bool(true)); assert_eq!(parse_expr_from_str("true").unwrap(), Value::Bool(true));
assert_eq!(parse_expr_from_str("false").unwrap(), Value::Bool(false)); assert_eq!(parse_expr_from_str("false").unwrap(), Value::Bool(false));
assert_eq!(parse_expr_from_str(r#""x \n \ty \"""#).unwrap(), Value::Text(Cow::Borrowed("x \n \ty \""))); assert_eq!(
assert_eq!(parse_expr_from_str(r#""x'""#).unwrap(), Value::Text("x'".into())); parse_expr_from_str(r#""x \n \ty \"""#).unwrap(),
assert_eq!(parse_expr_from_str(r#"'"x"'"#).unwrap(), Value::Text(r##""x""##.into())); Value::Text(Cow::Borrowed("x \n \ty \""))
assert_eq!(parse_expr_from_str(r#####"r###"x"yz"###"#####).unwrap(), (Value::Text(r##"x"yz"##.into()))); );
assert_eq!(
parse_expr_from_str(r#""x'""#).unwrap(),
Value::Text("x'".into())
);
assert_eq!(
parse_expr_from_str(r#"'"x"'"#).unwrap(),
Value::Text(r##""x""##.into())
);
assert_eq!(
parse_expr_from_str(r#####"r###"x"yz"###"#####).unwrap(),
(Value::Text(r##"x"yz"##.into()))
);
} }
#[test] #[test]
@ -655,4 +752,4 @@ mod tests {
println!("{}", parse_expr_from_str("[...a,...b,1,2,...e,3]")?); println!("{}", parse_expr_from_str("[...a,...b,1,2,...e,3]")?);
Ok(()) Ok(())
} }
} }

Loading…
Cancel
Save