Small improvements accumulated while building the logins API on top of Mentat. (#779) r=grisha
These build on #778, and implement a variety of small fixes (related parts are labelled as such), and one non-trivial part -- matching tuple results with the `BindingTuple` trait. In practice, this is very helpful, and greatly streamlined the logins API.
This commit is contained in:
commit
46f7db36c9
14 changed files with 407 additions and 28 deletions
|
@ -8,9 +8,18 @@
|
|||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use ::std::convert::{
|
||||
AsRef,
|
||||
};
|
||||
|
||||
use ::std::ffi::{
|
||||
CString,
|
||||
};
|
||||
|
||||
use ::std::ops::{
|
||||
Deref,
|
||||
};
|
||||
|
||||
use ::std::os::raw::c_char;
|
||||
|
||||
use ::std::rc::{
|
||||
|
@ -282,12 +291,47 @@ impl From<Vec<Binding>> for Binding {
|
|||
}
|
||||
|
||||
impl Binding {
|
||||
pub fn val(self) -> Option<TypedValue> {
|
||||
pub fn into_scalar(self) -> Option<TypedValue> {
|
||||
match self {
|
||||
Binding::Scalar(v) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_vec(self) -> Option<ValueRc<Vec<Binding>>> {
|
||||
match self {
|
||||
Binding::Vec(v) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_map(self) -> Option<ValueRc<StructuredMap>> {
|
||||
match self {
|
||||
Binding::Map(v) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_scalar(&self) -> Option<&TypedValue> {
|
||||
match self {
|
||||
&Binding::Scalar(ref v) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vec(&self) -> Option<&Vec<Binding>> {
|
||||
match self {
|
||||
&Binding::Vec(ref v) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_map(&self) -> Option<&StructuredMap> {
|
||||
match self {
|
||||
&Binding::Map(ref v) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A pull expression expands a binding into a structure. The returned structure
|
||||
|
@ -303,6 +347,14 @@ impl Binding {
|
|||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
pub struct StructuredMap(pub IndexMap<ValueRc<Keyword>, Binding>);
|
||||
|
||||
impl Deref for StructuredMap {
|
||||
type Target = IndexMap<ValueRc<Keyword>, Binding>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl StructuredMap {
|
||||
pub fn insert<N, B>(&mut self, name: N, value: B) where N: Into<ValueRc<Keyword>>, B: Into<Binding> {
|
||||
self.0.insert(name.into(), value.into());
|
||||
|
@ -379,15 +431,15 @@ impl TypedValue {
|
|||
/// Construct a new `TypedValue::Keyword` instance by cloning the provided
|
||||
/// values and wrapping them in a new `ValueRc`. This is expensive, so this might
|
||||
/// be best limited to tests.
|
||||
pub fn typed_ns_keyword(ns: &str, name: &str) -> TypedValue {
|
||||
Keyword::namespaced(ns, name).into()
|
||||
pub fn typed_ns_keyword<S: AsRef<str>, T: AsRef<str>>(ns: S, name: T) -> TypedValue {
|
||||
Keyword::namespaced(ns.as_ref(), name.as_ref()).into()
|
||||
}
|
||||
|
||||
/// Construct a new `TypedValue::String` instance by cloning the provided
|
||||
/// value and wrapping it in a new `ValueRc`. This is expensive, so this might
|
||||
/// be best limited to tests.
|
||||
pub fn typed_string(s: &str) -> TypedValue {
|
||||
s.into()
|
||||
pub fn typed_string<S: AsRef<str>>(s: S) -> TypedValue {
|
||||
s.as_ref().into()
|
||||
}
|
||||
|
||||
pub fn current_instant() -> TypedValue {
|
||||
|
@ -736,6 +788,62 @@ impl Binding {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_entid(&self) -> Option<&Entid> {
|
||||
match self {
|
||||
&Binding::Scalar(TypedValue::Ref(ref v)) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_kw(&self) -> Option<&ValueRc<Keyword>> {
|
||||
match self {
|
||||
&Binding::Scalar(TypedValue::Keyword(ref v)) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_boolean(&self) -> Option<&bool> {
|
||||
match self {
|
||||
&Binding::Scalar(TypedValue::Boolean(ref v)) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_long(&self) -> Option<&i64> {
|
||||
match self {
|
||||
&Binding::Scalar(TypedValue::Long(ref v)) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_double(&self) -> Option<&f64> {
|
||||
match self {
|
||||
&Binding::Scalar(TypedValue::Double(ref v)) => Some(&v.0),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_instant(&self) -> Option<&DateTime<Utc>> {
|
||||
match self {
|
||||
&Binding::Scalar(TypedValue::Instant(ref v)) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> Option<&ValueRc<String>> {
|
||||
match self {
|
||||
&Binding::Scalar(TypedValue::String(ref v)) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_uuid(&self) -> Option<&Uuid> {
|
||||
match self {
|
||||
&Binding::Scalar(TypedValue::Uuid(ref v)) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -57,9 +57,11 @@ pub use parse::ParseError;
|
|||
pub use uuid::ParseError as UuidParseError;
|
||||
pub use types::{
|
||||
FromMicros,
|
||||
FromMillis,
|
||||
Span,
|
||||
SpannedValue,
|
||||
ToMicros,
|
||||
ToMillis,
|
||||
Value,
|
||||
ValueAndSpan,
|
||||
};
|
||||
|
|
|
@ -545,19 +545,19 @@ impl std::fmt::Display for PullAttributeSpec {
|
|||
}
|
||||
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Pull {
|
||||
pub var: Variable,
|
||||
pub patterns: Vec<PullAttributeSpec>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Aggregate {
|
||||
pub func: QueryFunction,
|
||||
pub args: Vec<FnArg>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Element {
|
||||
Variable(Variable),
|
||||
Aggregate(Aggregate),
|
||||
|
@ -650,7 +650,7 @@ pub enum Limit {
|
|||
/// # }
|
||||
/// ```
|
||||
///
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub enum FindSpec {
|
||||
/// Returns an array of arrays, represented as a single array with length a multiple of width.
|
||||
FindRel(Vec<Element>),
|
||||
|
|
|
@ -649,6 +649,28 @@ impl ToMicros for DateTime<Utc> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait FromMillis {
|
||||
fn from_millis(ts: i64) -> Self;
|
||||
}
|
||||
|
||||
impl FromMillis for DateTime<Utc> {
|
||||
fn from_millis(ts: i64) -> Self {
|
||||
Utc.timestamp(ts / 1_000, ((ts % 1_000).abs() as u32) * 1_000)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToMillis {
|
||||
fn to_millis(&self) -> i64;
|
||||
}
|
||||
|
||||
impl ToMillis for DateTime<Utc> {
|
||||
fn to_millis(&self) -> i64 {
|
||||
let major: i64 = self.timestamp() * 1_000;
|
||||
let minor: i64 = self.timestamp_subsec_millis() as i64;
|
||||
major + minor
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
extern crate chrono;
|
||||
|
|
159
query-projector/src/binding_tuple.rs
Normal file
159
query-projector/src/binding_tuple.rs
Normal file
|
@ -0,0 +1,159 @@
|
|||
// Copyright 2018 Mozilla
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License. You may obtain a copy of the
|
||||
// License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
use mentat_core::{
|
||||
Binding,
|
||||
};
|
||||
|
||||
use errors::{
|
||||
ProjectorError,
|
||||
Result,
|
||||
};
|
||||
|
||||
/// A `BindingTuple` is any type that can accommodate a Mentat tuple query result of fixed length.
|
||||
///
|
||||
/// Currently Rust tuples of length 1 through 6 (i.e., `(A)` through `(A, B, C, D, E, F)`) are
|
||||
/// supported as are vectors (i.e., `Vec<>`).
|
||||
pub trait BindingTuple: Sized {
|
||||
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>>;
|
||||
}
|
||||
|
||||
// This is a no-op, essentially: we can always produce a vector representation of a tuple result.
|
||||
impl BindingTuple for Vec<Binding> {
|
||||
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
|
||||
match vec {
|
||||
None => Ok(None),
|
||||
Some(vec) => {
|
||||
if expected != vec.len() {
|
||||
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
|
||||
} else {
|
||||
Ok(Some(vec))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: generate these repetitive implementations with a little macro.
|
||||
impl BindingTuple for (Binding,) {
|
||||
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
|
||||
if expected != 1 {
|
||||
return Err(ProjectorError::UnexpectedResultsTupleLength(1, expected));
|
||||
}
|
||||
match vec {
|
||||
None => Ok(None),
|
||||
Some(vec) => {
|
||||
if expected != vec.len() {
|
||||
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
|
||||
} else {
|
||||
let mut iter = vec.into_iter();
|
||||
Ok(Some((iter.next().unwrap(),)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BindingTuple for (Binding, Binding) {
|
||||
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
|
||||
if expected != 2 {
|
||||
return Err(ProjectorError::UnexpectedResultsTupleLength(2, expected));
|
||||
}
|
||||
match vec {
|
||||
None => Ok(None),
|
||||
Some(vec) => {
|
||||
if expected != vec.len() {
|
||||
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
|
||||
} else {
|
||||
let mut iter = vec.into_iter();
|
||||
Ok(Some((iter.next().unwrap(), iter.next().unwrap())))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BindingTuple for (Binding, Binding, Binding) {
|
||||
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
|
||||
if expected != 3 {
|
||||
return Err(ProjectorError::UnexpectedResultsTupleLength(3, expected));
|
||||
}
|
||||
match vec {
|
||||
None => Ok(None),
|
||||
Some(vec) => {
|
||||
if expected != vec.len() {
|
||||
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
|
||||
} else {
|
||||
let mut iter = vec.into_iter();
|
||||
Ok(Some((iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap())))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BindingTuple for (Binding, Binding, Binding, Binding) {
|
||||
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
|
||||
if expected != 4 {
|
||||
return Err(ProjectorError::UnexpectedResultsTupleLength(4, expected));
|
||||
}
|
||||
match vec {
|
||||
None => Ok(None),
|
||||
Some(vec) => {
|
||||
if expected != vec.len() {
|
||||
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
|
||||
} else {
|
||||
let mut iter = vec.into_iter();
|
||||
Ok(Some((iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap())))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BindingTuple for (Binding, Binding, Binding, Binding, Binding) {
|
||||
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
|
||||
if expected != 5 {
|
||||
return Err(ProjectorError::UnexpectedResultsTupleLength(5, expected));
|
||||
}
|
||||
match vec {
|
||||
None => Ok(None),
|
||||
Some(vec) => {
|
||||
if expected != vec.len() {
|
||||
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
|
||||
} else {
|
||||
let mut iter = vec.into_iter();
|
||||
Ok(Some((iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap())))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: allow binding tuples of length more than 6. Folks who are binding such large tuples are
|
||||
// probably doing something wrong -- they should investigate a pull expression.
|
||||
impl BindingTuple for (Binding, Binding, Binding, Binding, Binding, Binding) {
|
||||
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
|
||||
if expected != 6 {
|
||||
return Err(ProjectorError::UnexpectedResultsTupleLength(6, expected));
|
||||
}
|
||||
match vec {
|
||||
None => Ok(None),
|
||||
Some(vec) => {
|
||||
if expected != vec.len() {
|
||||
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
|
||||
} else {
|
||||
let mut iter = vec.into_iter();
|
||||
Ok(Some((iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap())))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -59,6 +59,9 @@ pub enum ProjectorError {
|
|||
#[fail(display = "expected {}, got {}", _0, _1)]
|
||||
UnexpectedResultsType(&'static str, &'static str),
|
||||
|
||||
#[fail(display = "expected tuple of length {}, got tuple of length {}", _0, _1)]
|
||||
UnexpectedResultsTupleLength(usize, usize),
|
||||
|
||||
#[fail(display = "min/max expressions: {} (max 1), corresponding: {}", _0, _1)]
|
||||
AmbiguousAggregates(usize, usize),
|
||||
|
||||
|
|
|
@ -73,6 +73,10 @@ use mentat_query_sql::{
|
|||
pub mod errors;
|
||||
|
||||
mod aggregates;
|
||||
mod binding_tuple;
|
||||
pub use binding_tuple::{
|
||||
BindingTuple,
|
||||
};
|
||||
mod project;
|
||||
mod projectors;
|
||||
mod pull;
|
||||
|
@ -117,7 +121,7 @@ pub use errors::{
|
|||
Result,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct QueryOutput {
|
||||
pub spec: Rc<FindSpec>,
|
||||
pub results: QueryResults,
|
||||
|
@ -267,8 +271,15 @@ impl QueryOutput {
|
|||
self.results.into_coll()
|
||||
}
|
||||
|
||||
pub fn into_tuple(self) -> Result<Option<Vec<Binding>>> {
|
||||
self.results.into_tuple()
|
||||
/// Mentat tuple results can be expressed as multiple different data structures. Some
|
||||
/// structures are generic (vectors) and some are easier for pattern matching (fixed length
|
||||
/// tuples).
|
||||
///
|
||||
/// This is the moral equivalent of `collect` (and `BindingTuple` of `FromIterator`), but
|
||||
/// specialized to tuples of expected length.
|
||||
pub fn into_tuple<B>(self) -> Result<Option<B>> where B: BindingTuple {
|
||||
let expected = self.spec.expected_column_count();
|
||||
self.results.into_tuple().and_then(|vec| B::from_binding_vec(expected, vec))
|
||||
}
|
||||
|
||||
pub fn into_rel(self) -> Result<RelResult<Binding>> {
|
||||
|
@ -515,3 +526,46 @@ pub fn query_projection(schema: &Schema, query: &AlgebraicQuery) -> Result<Eithe
|
|||
}.map(Either::Right)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_tuple() {
|
||||
let query_output = QueryOutput {
|
||||
spec: Rc::new(FindSpec::FindTuple(vec![Element::Variable(Variable::from_valid_name("?x")),
|
||||
Element::Variable(Variable::from_valid_name("?y"))])),
|
||||
results: QueryResults::Tuple(Some(vec![Binding::Scalar(TypedValue::Long(0)),
|
||||
Binding::Scalar(TypedValue::Long(2))])),
|
||||
};
|
||||
|
||||
assert_eq!(query_output.clone().into_tuple().expect("into_tuple"),
|
||||
Some((Binding::Scalar(TypedValue::Long(0)),
|
||||
Binding::Scalar(TypedValue::Long(2)))));
|
||||
|
||||
match query_output.clone().into_tuple() {
|
||||
Err(ProjectorError::UnexpectedResultsTupleLength(expected, got)) => {
|
||||
assert_eq!((expected, got), (3, 2));
|
||||
},
|
||||
// This forces the result type.
|
||||
Ok(Some((_, _, _))) | _ => panic!("expected error"),
|
||||
}
|
||||
|
||||
let query_output = QueryOutput {
|
||||
spec: Rc::new(FindSpec::FindTuple(vec![Element::Variable(Variable::from_valid_name("?x")),
|
||||
Element::Variable(Variable::from_valid_name("?y"))])),
|
||||
results: QueryResults::Tuple(None),
|
||||
};
|
||||
|
||||
|
||||
match query_output.clone().into_tuple() {
|
||||
Ok(None) => {},
|
||||
// This forces the result type.
|
||||
Ok(Some((_, _))) | _ => panic!("expected error"),
|
||||
}
|
||||
|
||||
match query_output.clone().into_tuple() {
|
||||
Err(ProjectorError::UnexpectedResultsTupleLength(expected, got)) => {
|
||||
assert_eq!((expected, got), (3, 2));
|
||||
},
|
||||
// This forces the result type.
|
||||
Ok(Some((_, _, _))) | _ => panic!("expected error"),
|
||||
}
|
||||
}
|
||||
|
|
16
src/conn.rs
16
src/conn.rs
|
@ -387,6 +387,11 @@ impl<'a, 'c> HasSchema for InProgress<'a, 'c> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'c> InProgressRead<'a, 'c> {
|
||||
pub fn last_tx_id(&self) -> Entid {
|
||||
self.0.last_tx_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'c> InProgress<'a, 'c> {
|
||||
pub fn builder(self) -> InProgressBuilder<'a, 'c> {
|
||||
|
@ -526,6 +531,10 @@ impl<'a, 'c> InProgress<'a, 'c> {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn last_tx_id(&self) -> Entid {
|
||||
self.partition_map[":db.part/tx"].index - 1
|
||||
}
|
||||
}
|
||||
|
||||
struct InProgressTransactWatcher<'a, 'o> {
|
||||
|
@ -617,6 +626,13 @@ impl Conn {
|
|||
self.metadata.lock().unwrap().attribute_cache.clone()
|
||||
}
|
||||
|
||||
pub fn last_tx_id(&self) -> Entid {
|
||||
// The mutex is taken during this entire method.
|
||||
let metadata = self.metadata.lock().unwrap();
|
||||
|
||||
metadata.partition_map[":db.part/tx"].index - 1
|
||||
}
|
||||
|
||||
/// Query the Mentat store, using the given connection and the current metadata.
|
||||
pub fn q_once<T>(&self,
|
||||
sqlite: &rusqlite::Connection,
|
||||
|
|
29
src/lib.rs
29
src/lib.rs
|
@ -34,18 +34,20 @@ extern crate mentat_tolstoy;
|
|||
|
||||
pub use mentat_core::{
|
||||
Attribute,
|
||||
Entid,
|
||||
DateTime,
|
||||
HasSchema,
|
||||
KnownEntid,
|
||||
Keyword,
|
||||
Schema,
|
||||
Binding,
|
||||
DateTime,
|
||||
Entid,
|
||||
HasSchema,
|
||||
Keyword,
|
||||
KnownEntid,
|
||||
Schema,
|
||||
StructuredMap,
|
||||
TxReport,
|
||||
TypedValue,
|
||||
Uuid,
|
||||
Utc,
|
||||
Uuid,
|
||||
ValueType,
|
||||
now,
|
||||
};
|
||||
|
||||
pub use mentat_query::{
|
||||
|
@ -109,10 +111,19 @@ pub use errors::{
|
|||
Result,
|
||||
};
|
||||
|
||||
pub use edn::ParseError;
|
||||
pub use edn::{
|
||||
FromMicros,
|
||||
FromMillis,
|
||||
ParseError,
|
||||
ToMicros,
|
||||
ToMillis,
|
||||
};
|
||||
pub use mentat_db::DbError;
|
||||
pub use mentat_query_algebrizer::AlgebrizerError;
|
||||
pub use mentat_query_projector::ProjectorError;
|
||||
pub use mentat_query_projector::{
|
||||
BindingTuple,
|
||||
ProjectorError,
|
||||
};
|
||||
pub use mentat_query_pull::PullError;
|
||||
pub use mentat_sql::SQLError;
|
||||
|
||||
|
|
|
@ -234,7 +234,7 @@ pub fn lookup_value<'sqlite, 'schema, 'cache, E, A>
|
|||
fetch_values(sqlite, known, entid, attrid, true)
|
||||
.into_scalar_result()
|
||||
// Safe to unwrap: we never retrieve structure.
|
||||
.map(|r| r.map(|v| v.val().unwrap()))
|
||||
.map(|r| r.map(|v| v.into_scalar().unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -256,7 +256,7 @@ pub fn lookup_values<'sqlite, E, A>
|
|||
fetch_values(sqlite, known, entid, attrid, false)
|
||||
.into_coll_result()
|
||||
// Safe to unwrap: we never retrieve structure.
|
||||
.map(|v| v.into_iter().map(|x| x.val().unwrap()).collect())
|
||||
.map(|v| v.into_iter().map(|x| x.into_scalar().unwrap()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -191,6 +191,10 @@ impl Store {
|
|||
pub fn unregister_observer(&mut self, key: &String) {
|
||||
self.conn.unregister_observer(key);
|
||||
}
|
||||
|
||||
pub fn last_tx_id(&self) -> Entid {
|
||||
self.conn.last_tx_id()
|
||||
}
|
||||
}
|
||||
|
||||
impl Queryable for Store {
|
||||
|
@ -609,7 +613,7 @@ mod tests {
|
|||
let mut builder = in_progress.builder().describe_tempid(&name);
|
||||
builder.add(kw!(:todo/uuid), TypedValue::Uuid(uuid)).expect("Expected added uuid");
|
||||
changeset.insert(uuid_entid.clone());
|
||||
builder.add(kw!(:todo/name), TypedValue::typed_string(&name)).expect("Expected added name");
|
||||
builder.add(kw!(:todo/name), TypedValue::typed_string(name)).expect("Expected added name");
|
||||
changeset.insert(name_entid.clone());
|
||||
if i % 2 == 0 {
|
||||
builder.add(kw!(:todo/completion_date), TypedValue::current_instant()).expect("Expected added date");
|
||||
|
@ -678,7 +682,7 @@ mod tests {
|
|||
for i in 0..3 {
|
||||
let name = format!("label{}", i);
|
||||
let mut builder = in_progress.builder().describe_tempid(&name);
|
||||
builder.add(kw!(:label/name), TypedValue::typed_string(&name)).expect("Expected added name");
|
||||
builder.add(kw!(:label/name), TypedValue::typed_string(name)).expect("Expected added name");
|
||||
builder.add(kw!(:label/color), TypedValue::typed_string("blue")).expect("Expected added color");
|
||||
let (ip, _) = builder.transact();
|
||||
in_progress = ip;
|
||||
|
|
|
@ -227,7 +227,7 @@ pub struct Definition {
|
|||
/// for row in results.into_iter() {
|
||||
/// let mut r = row.into_iter();
|
||||
/// let e = r.next().and_then(|e| e.into_known_entid()).expect("entity");
|
||||
/// let obsolete = r.next().expect("value").val().expect("typed value");
|
||||
/// let obsolete = r.next().expect("value").into_scalar().expect("typed value");
|
||||
/// builder.retract(e, link_title, obsolete)?;
|
||||
/// }
|
||||
/// ip.transact_builder(builder)?;
|
||||
|
|
|
@ -86,7 +86,7 @@ fn test_simple_pull() {
|
|||
.expect("hoods")
|
||||
.into_iter()
|
||||
.map(|b| {
|
||||
b.val().and_then(|tv| tv.into_entid()).expect("scalar")
|
||||
b.into_scalar().and_then(|tv| tv.into_entid()).expect("scalar")
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
|
@ -350,7 +350,7 @@ fn av(row: Vec<Binding>) -> (KnownEntid, TypedValue) {
|
|||
let mut row = row.into_iter();
|
||||
match (row.next(), row.next()) {
|
||||
(Some(Binding::Scalar(TypedValue::Ref(a))), Some(v)) => {
|
||||
(KnownEntid(a), v.val().unwrap())
|
||||
(KnownEntid(a), v.into_scalar().unwrap())
|
||||
},
|
||||
_ => panic!("Incorrect query shape for 'av' helper."),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue