From 4f243fbc326ed14b2898b342a90193a8f6c42e11 Mon Sep 17 00:00:00 2001 From: Grisha Kruglov Date: Wed, 31 Jan 2018 22:05:49 -0500 Subject: [PATCH] third pass, compiles and should work --- db/src/lib.rs | 2 +- tolstoy/src/errors.rs | 41 +++++++++++++++++++++++ tolstoy/src/lib.rs | 14 +------- tolstoy/src/metadata.rs | 2 +- tolstoy/src/schema.rs | 2 +- tolstoy/src/tx_client.rs | 71 ++++++++++++++++++++++++---------------- 6 files changed, 88 insertions(+), 44 deletions(-) create mode 100644 tolstoy/src/errors.rs diff --git a/db/src/lib.rs b/db/src/lib.rs index 7af9ed34..a5927978 100644 --- a/db/src/lib.rs +++ b/db/src/lib.rs @@ -37,7 +37,7 @@ pub mod db; mod bootstrap; pub mod debug; mod add_retract_alter_set; -mod entids; +pub mod entids; pub mod errors; mod metadata; mod schema; diff --git a/tolstoy/src/errors.rs b/tolstoy/src/errors.rs new file mode 100644 index 00000000..850a8638 --- /dev/null +++ b/tolstoy/src/errors.rs @@ -0,0 +1,41 @@ +// Copyright 2016 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. + +#![allow(dead_code)] + +use std; +use hyper; +use rusqlite; +use edn; +use mentat_db; + +error_chain! { + types { + Error, ErrorKind, ResultExt, Result; + } + + foreign_links { + IOError(std::io::Error); + HttpError(hyper::Error); + SqlError(rusqlite::Error); + UuidParseError(edn::UuidParseError); + } + + links { + DbError(mentat_db::Error, mentat_db::ErrorKind); + } + + errors { + UnexpectedState(t: String) { + description("encountered unexpected state") + display("encountered unexpected state: {}", t) + } + } +} diff --git a/tolstoy/src/lib.rs b/tolstoy/src/lib.rs index 38fb4562..a9746d59 100644 --- a/tolstoy/src/lib.rs +++ b/tolstoy/src/lib.rs @@ -28,16 +28,4 @@ extern crate uuid; pub mod schema; pub mod metadata; pub mod tx_client; - -error_chain! { - types { - Error, ErrorKind, ResultExt, Result; - } - - foreign_links { - IOError(std::io::Error); - HttpError(hyper::Error); - SqlError(rusqlite::Error); - UuidParseError(edn::UuidParseError); - } -} +pub mod errors; diff --git a/tolstoy/src/metadata.rs b/tolstoy/src/metadata.rs index 2dc26121..b3539a0c 100644 --- a/tolstoy/src/metadata.rs +++ b/tolstoy/src/metadata.rs @@ -14,7 +14,7 @@ use rusqlite; use uuid::Uuid; use schema; -use Result; +use errors::Result; trait HeadTrackable { fn remote_head(&self) -> Result; diff --git a/tolstoy/src/schema.rs b/tolstoy/src/schema.rs index 1a58e06e..c6075b3f 100644 --- a/tolstoy/src/schema.rs +++ b/tolstoy/src/schema.rs @@ -9,7 +9,7 @@ // specific language governing permissions and limitations under the License. use rusqlite; -use Result; +use errors::Result; pub static REMOTE_HEAD_KEY: &str = r#"remote_head"#; diff --git a/tolstoy/src/tx_client.rs b/tolstoy/src/tx_client.rs index bdccdf46..8374e9f7 100644 --- a/tolstoy/src/tx_client.rs +++ b/tolstoy/src/tx_client.rs @@ -25,29 +25,36 @@ // perhaps mentat has useful primitives, but let's begin by just "doing the work" use std::collections::HashMap; +use std::collections::hash_map::Entry; + use rusqlite; -use Result; +use errors::{ + Result +}; use mentat_db::types::{ Entid }; +use mentat_db::{ + entids, + TypedSQLValue +}; + use mentat_core::{ DateTime, Utc, - ValueType + TypedValue }; -use mentat_core::SQLValueType; use edn::FromMicros; pub struct TxPart { e: Entid, - a: i32, - v: Vec, // should be TypedValue to allow for variety of types - added: i32, - value_type_tag: i32 // with v as TypedValue, shouldn't be necessary + a: i64, + v: TypedValue, + added: i32 } pub struct Tx { @@ -77,50 +84,58 @@ impl TxClient { impl TxReader for TxClient { fn txs(&self) -> Result> { let mut txes_by_tx = HashMap::new(); - let mut parts_keyed_by_tx = HashMap::new(); - let mut stmt = self.conn.prepare("SELECT e, a, v, tx, added, value_type_tag, CASE a WHEN :txInstant THEN 1 ELSE 0 END is_transaction FROM transactions ORDER BY is_transaction DESC")?; - let mapped_rows = stmt.query_map_named(&[(":txInstant", entids::DB_TX_INSTANT)], |row| { + // Make sure a=txInstant rows are first, so that we process + // all transactions before we process any transaction parts. + let mut stmt = self.conn.prepare( + "SELECT + e, a, v, tx, added, value_type_tag, + CASE a WHEN :txInstant THEN 1 ELSE 0 END is_transaction + FROM transactions ORDER BY is_transaction DESC" + )?; + let _ = stmt.query_and_then_named(&[(":txInstant", &entids::DB_TX_INSTANT)], |row| { let e = row.get(0); let a = row.get(1); - let v = row.get(2); + let v_instant: i64 = row.get(2); // TODO unify this and typed_value below let tx = row.get(3); let added = row.get(4); let value_type_tag = row.get(5); - + + let raw_value: rusqlite::types::Value = row.get(2); + let typed_value = match TypedValue::from_sql_value_pair(raw_value, value_type_tag) { + Ok(v) => v, + Err(e) => return Err(e) + }; + // Row represents a transaction. if a == entids::DB_TX_INSTANT { txes_by_tx.insert(tx, Tx { tx: tx, - tx_instant: DateTime::::from_micros(v), + // TODO enforce correct type of v and return ErrorKind::BadSQLValuePair + // otherwise. + tx_instant: DateTime::::from_micros(v_instant), parts: Vec::new() }); + Ok(()) // Row represents part of a transaction. Our query statement above guarantees // that we've already processed corresponding transaction at this point. } else { - if let Entry::Occupied(o) = txes_by_tx.entry(tx) { - *o.get_mut().parts.push(TxPart { + if let Entry::Occupied(mut t) = txes_by_tx.entry(tx) { + t.get_mut().parts.push(TxPart { e: e, a: a, - v: v, // TODO tx_instant conversion implied that this value is i64... but it can be many things - added: added, - value_type_tag: value_type_tag + v: typed_value, + added: added }); + Ok(()) } else { - // Shouldn't happen if our query is correct. + // TODO not ok... ErrorKind::UnexpectedError + Ok(()) } } })?; - let mut txes = Vec::new(); - for tx in mapped_rows { - txes.push(match tx? { - Err(e) => return Err(e), - Ok(v) => v - }); - } - - Ok(txes) + Ok(txes_by_tx.into_iter().map(|(_, tx)| tx).collect()) } }