diff --git a/src/graph.rs b/src/graph.rs index b27c61d..63d0cc4 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -1,5 +1,6 @@ use error::Result; use rusqlite::Connection; +use storage; pub struct UnitProps { pub unit: String, @@ -159,7 +160,7 @@ pub fn compare_unit_props( for row in rows { let unit = row?; if unit.unit == earlier_unit.unit { - break 'go_up; + return Ok(Some(result_if_found)) } if unit.is_on_main_chain == Some(0) && unit.level > earlier_unit.level { @@ -205,7 +206,7 @@ pub fn compare_unit_props( for row in rows { let unit = row?; if unit.unit == earlier_unit.unit { - break 'go_down; + return Ok(Some(result_if_found)); } if unit.is_on_main_chain == Some(0) && unit.level < later_unit.level { @@ -220,6 +221,199 @@ pub fn compare_unit_props( } } } +} + +pub fn determine_if_included( + db: &Connection, + earlier_unit: &String, + later_units: &[String], +) -> Result { + if storage::is_genesis_unit(&earlier_unit) { + return Ok(true); + } + + let (earlier_unit_props, later_units_props) = + storage::read_props_of_units(db, &earlier_unit, later_units)?; + + if earlier_unit_props.is_free == 1 { + return Ok(false); + } + + ensure!(later_units_props.len() > 0, "no later unit props were read"); + + //spec::UnitProps.latest_included_mc_index and spec::UnitProps.main_chain_index is not Option + let max_later_limci = later_units_props + .iter() + .max_by_key(|props| props.latest_included_mc_index) + .unwrap() + .latest_included_mc_index; + if + /*earlier_unit_props.main_chain_index.is_some() + &&*/ + max_later_limci >= earlier_unit_props.main_chain_index { + return Ok(true); + } + + let max_later_level = later_units_props + .iter() + .max_by_key(|props| props.level) + .unwrap() + .level; + if max_later_level < earlier_unit_props.level { + return Ok(false); + } + + let mut start_units = later_units.to_vec(); + + 'go_up: loop { + let start_unit_list = start_units + .iter() + .map(|s| format!("'{}'", s)) + .collect::>() + .join(", "); + + let sql = format!( + "SELECT unit, level, latest_included_mc_index, main_chain_index, is_on_main_chain \ + FROM parenthoods JOIN units ON parent_unit=unit \ + WHERE child_unit IN({})", + start_unit_list + ); - return Ok(Some(result_if_found)); + let mut stmt = db.prepare(&sql)?; + let rows = stmt.query_map(&[], |row| UnitProps { + unit: row.get(0), + level: row.get(1), + latest_included_mc_index: row.get(2), + main_chain_index: row.get(3), + is_on_main_chain: row.get(4), + is_free: 0, //is_free is not queried + })?; + + let mut new_start_units = Vec::new(); + for row in rows { + let unit = row?; + if unit.unit == *earlier_unit { + return Ok(true); + } + + if unit.is_on_main_chain == Some(0) && unit.level > earlier_unit_props.level { + new_start_units.push(unit.unit.clone()); + } + } + + if new_start_units.len() > 0 { + new_start_units.sort(); + new_start_units.dedup(); + start_units = new_start_units; + } else { + return Ok(false); + } + } +} + +pub fn determine_if_included_or_equal( + db: &Connection, + earlier_unit: &String, + later_units: &[String], +) -> Result { + if later_units.contains(earlier_unit) { + return Ok(true); + } + + determine_if_included(db, earlier_unit, later_units) +} + +pub fn read_descendant_units_by_authors_before_mc_index( + db: &Connection, + earlier_unit: &UnitProps, + author_addresses: &[&String], + to_main_chain_index: u32, +) -> Result> { + let mut units = Vec::new(); + + let author_address_list = author_addresses + .iter() + .map(|s| format!("'{}'", s)) + .collect::>() + .join(", "); + + ensure!( + earlier_unit.main_chain_index.is_some(), + "earlier unit has no main chain index" + ); + let earlier_unit_mci = earlier_unit.main_chain_index.unwrap(); + + //Missing db.forceIndex("byMcIndex") from original js + let sql = format!( + "SELECT unit FROM units \" + LEFT JOIN unit_authors USING(unit) \ + WHERE latest_included_mc_index>={} AND main_chain_index>{} \ + AND main_chain_index<={} AND latest_included_mc_index<{} \ + AND address IN({})", + earlier_unit_mci, + earlier_unit_mci, + to_main_chain_index, + to_main_chain_index, + author_address_list + ); + + let mut stmt = db.prepare(&sql)?; + let rows = stmt.query_map(&[], |row| row.get::<_, String>(0))?; + + for row in rows { + units.push(row?) + } + + let mut start_units = Vec::new(); + start_units.push(earlier_unit.unit.clone()); + + 'go_down: loop { + let start_unit_list = start_units + .iter() + .map(|s| format!("'{}'", s)) + .collect::>() + .join(", "); + + let sql = format!( + "SELECT units.unit, unit_authors.address AS author_in_list \ + FROM parenthoods \ + JOIN units ON child_unit=units.unit \ + LEFT JOIN unit_authors ON unit_authors.unit=units.unit \ + AND address IN({}) \ + WHERE parent_unit IN({}) \ + AND latest_included_mc_index<{} \ + AND main_chain_index<={}", + author_address_list, start_unit_list, earlier_unit_mci, to_main_chain_index + ); + + //The query fields have different structures + struct UnitProps { + unit: String, + author_in_list: Option, + } + + let mut stmt = db.prepare(&sql)?; + let rows = stmt.query_map(&[], |row| UnitProps { + unit: row.get(0), + author_in_list: row.get(1), + })?; + + let mut new_start_units = Vec::new(); + + for row in rows { + let unit = row?; + + new_start_units.push(unit.unit.clone()); + + if unit.author_in_list.is_some() { + units.push(unit.unit.clone()); + } + } + + if new_start_units.len() > 0 { + start_units = new_start_units; + } else { + return Ok(units); + } + } } diff --git a/src/storage.rs b/src/storage.rs index 1151346..945df55 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -51,6 +51,14 @@ pub fn read_unit_props(db: &Connection, unit_hash: &String) -> Result Ok(ret) } +pub fn read_props_of_units( + _db: &Connection, + _unit_hash: &String, + _later_unit_hashes: &[String], +) -> Result<(UnitProps, Vec)> { + unimplemented!(); +} + // TODO: need to cache in memory pub fn read_static_unit_property( db: &Connection,