single table scan

main
Ziyang Hu 2 years ago
parent bd8181dc7d
commit bd8e7584db

@ -7,7 +7,7 @@ use crate::ddl::reify::{AssocInfo, DdlContext, DdlReifyError, EdgeInfo, IndexInf
use anyhow::Result; use anyhow::Result;
use cozorocks::PinnableSlicePtr; use cozorocks::PinnableSlicePtr;
use std::collections::btree_map::Entry; use std::collections::btree_map::Entry;
use std::collections::BTreeMap; use std::collections::{BTreeMap, BTreeSet};
use std::rc::Rc; use std::rc::Rc;
mod filter; mod filter;
@ -15,6 +15,7 @@ mod from;
mod group; mod group;
mod insert; mod insert;
mod limit; mod limit;
mod scan;
mod select; mod select;
mod tagged; mod tagged;
mod values; mod values;
@ -28,6 +29,7 @@ pub(crate) use from::*;
pub(crate) use group::*; pub(crate) use group::*;
pub(crate) use insert::*; pub(crate) use insert::*;
pub(crate) use limit::*; pub(crate) use limit::*;
pub(crate) use scan::*;
pub(crate) use select::*; pub(crate) use select::*;
pub(crate) use tagged::*; pub(crate) use tagged::*;
pub(crate) use values::*; pub(crate) use values::*;
@ -173,6 +175,7 @@ impl<'a> InterpretContext for TempDbContext<'a> {
pub(crate) trait RelationalAlgebra { pub(crate) trait RelationalAlgebra {
fn name(&self) -> &str; fn name(&self) -> &str;
fn bindings(&self) -> Result<BTreeSet<String>>;
fn binding_map(&self) -> Result<BindingMap>; fn binding_map(&self) -> Result<BindingMap>;
fn iter<'a>(&'a self) -> Result<Box<dyn Iterator<Item = Result<TupleSet>> + 'a>>; fn iter<'a>(&'a self) -> Result<Box<dyn Iterator<Item = Result<TupleSet>> + 'a>>;
fn identity(&self) -> Option<TableInfo>; fn identity(&self) -> Option<TableInfo>;
@ -180,12 +183,16 @@ pub(crate) trait RelationalAlgebra {
let bmap = self.binding_map()?; let bmap = self.binding_map()?;
let bmap = bmap let bmap = bmap
.into_iter() .into_iter()
.map(|(k, v)| { .filter_map(|(k, v)| {
if k.starts_with('@') {
None
} else {
let v = v let v = v
.into_iter() .into_iter()
.map(|(k, v)| (k, Expr::TupleSetIdx(v))) .map(|(k, v)| (k, Expr::TupleSetIdx(v)))
.collect::<BTreeMap<_, _>>(); .collect::<BTreeMap<_, _>>();
(k, Expr::Dict(v)) Some((k, Expr::Dict(v)))
}
}) })
.collect::<BTreeMap<_, _>>(); .collect::<BTreeMap<_, _>>();
let bmap = Expr::Dict(bmap); let bmap = Expr::Dict(bmap);

@ -1,9 +1,15 @@
use crate::algebra::op::RelationalAlgebra; use crate::algebra::op::{RelationalAlgebra, TableScan};
use crate::algebra::parser::{assert_rule, AlgebraParseError};
use crate::context::TempDbContext; use crate::context::TempDbContext;
use crate::parser::Pairs; use crate::data::tuple::DataKind;
use crate::data::tuple_set::{BindingMap, TupleSet};
use crate::data::uuid::random_uuid_v1;
use crate::ddl::reify::TableInfo;
use crate::parser::text_identifier::build_name_in_def;
use crate::parser::{Pair, Pairs, Rule};
use anyhow::Result; use anyhow::Result;
use std::collections::BTreeSet;
use std::sync::Arc; use std::sync::Arc;
use crate::algebra::parser::AlgebraParseError;
pub(crate) const NAME_FROM: &str = "From"; pub(crate) const NAME_FROM: &str = "From";
@ -13,10 +19,133 @@ pub(crate) fn build_from_clause<'a>(
mut args: Pairs, mut args: Pairs,
) -> Result<Arc<dyn RelationalAlgebra + 'a>> { ) -> Result<Arc<dyn RelationalAlgebra + 'a>> {
if !matches!(prev, None) { if !matches!(prev, None) {
return Err( return Err(AlgebraParseError::Unchainable(NAME_FROM.to_string()).into());
AlgebraParseError::Unchainable(NAME_FROM.to_string()).into(),
);
} }
let not_enough_args = || AlgebraParseError::NotEnoughArguments(NAME_FROM.to_string());
let chain = args
.next()
.ok_or_else(not_enough_args)?
.into_inner()
.next()
.ok_or_else(not_enough_args)?;
let mut chain = parse_chain(chain)?.into_iter();
let mut last_el = chain.next().ok_or_else(not_enough_args)?;
let mut ret = Arc::new(TableScan::build(ctx, &last_el, true)?);
for el in chain {
todo!() todo!()
} }
Ok(ret)
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum ChainPartEdgeDir {
Fwd,
Bwd,
Bidi,
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum JoinType {
Inner,
Left,
Right,
FullOuter,
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum ChainPart {
Node,
Edge {
dir: ChainPartEdgeDir,
join: JoinType,
},
}
#[derive(Clone, Debug)]
pub(crate) struct ChainEl {
pub(crate) part: ChainPart,
pub(crate) binding: String,
pub(crate) target: String,
pub(crate) assocs: BTreeSet<String>,
}
pub(crate) fn parse_chain(pair: Pair) -> Result<Vec<ChainEl>> {
assert_rule(&pair, Rule::chain, NAME_FROM, 0)?;
let mut collected = vec![];
for pair in pair.into_inner() {
match pair.as_rule() {
Rule::node_part => {
let (binding, target, assocs) = parse_node_part(pair)?;
collected.push(ChainEl {
part: ChainPart::Node,
binding,
target,
assocs,
});
}
Rule::edge_part => {
let mut pairs = pair.into_inner();
let src_marker = pairs.next().unwrap();
let (is_bwd, src_outer) = parse_edge_marker(src_marker);
let middle = pairs.next().unwrap();
let (binding, target, assocs) = parse_node_part(middle)?;
let dst_marker = pairs.next().unwrap();
let (is_fwd, dst_outer) = parse_edge_marker(dst_marker);
let dir = if (is_fwd && is_bwd) || (!is_fwd && !is_bwd) {
ChainPartEdgeDir::Bidi
} else if is_fwd {
ChainPartEdgeDir::Fwd
} else {
ChainPartEdgeDir::Bwd
};
let join = match (src_outer, dst_outer) {
(true, true) => JoinType::FullOuter,
(true, false) => JoinType::Right,
(false, true) => JoinType::Left,
(false, false) => JoinType::Inner,
};
collected.push(ChainEl {
part: ChainPart::Edge { dir, join },
binding,
target,
assocs,
});
}
_ => unreachable!(),
}
}
Ok(collected)
}
fn parse_node_part(pair: Pair) -> Result<(String, String, BTreeSet<String>)> {
let mut pairs = pair.into_inner();
let mut nxt = pairs.next().unwrap();
let binding = if nxt.as_rule() == Rule::ident {
let binding = nxt.as_str().to_string();
nxt = pairs.next().unwrap();
binding
} else {
let mut ret = "@".to_string();
ret += &random_uuid_v1()?.to_string();
ret
};
let mut pairs = nxt.into_inner();
let table_name = build_name_in_def(pairs.next().unwrap(), true)?;
let assoc_names = pairs
.map(|v| build_name_in_def(v, true))
.collect::<Result<BTreeSet<_>>>()?;
Ok((binding, table_name, assoc_names))
}
fn parse_edge_marker(pair: Pair) -> (bool, bool) {
let mut arrow_mark = false;
let mut outer_mark = false;
for pair in pair.into_inner() {
match pair.as_rule() {
Rule::outer_marker => outer_mark = true,
Rule::bwd_marker | Rule::fwd_marker => arrow_mark = true,
_ => unreachable!(),
}
}
(arrow_mark, outer_mark)
}

@ -1,4 +1,6 @@
use crate::algebra::op::{InterpretContext, KeyBuilderSet, RelationalAlgebra}; use crate::algebra::op::{
build_binding_map_from_info, InterpretContext, KeyBuilderSet, RelationalAlgebra,
};
use crate::algebra::parser::{assert_rule, build_relational_expr, AlgebraParseError}; use crate::algebra::parser::{assert_rule, build_relational_expr, AlgebraParseError};
use crate::context::TempDbContext; use crate::context::TempDbContext;
use crate::data::expr::{Expr, StaticExpr}; use crate::data::expr::{Expr, StaticExpr};
@ -15,7 +17,7 @@ use crate::parser::{Pairs, Rule};
use crate::runtime::options::{default_read_options, default_write_options}; use crate::runtime::options::{default_read_options, default_write_options};
use anyhow::Result; use anyhow::Result;
use cozorocks::PinnableSlicePtr; use cozorocks::PinnableSlicePtr;
use std::collections::BTreeMap; use std::collections::{BTreeMap, BTreeSet};
use std::sync::Arc; use std::sync::Arc;
pub(crate) const NAME_INSERTION: &str = "Insert"; pub(crate) const NAME_INSERTION: &str = "Insert";
@ -84,92 +86,6 @@ impl<'a> Insertion<'a> {
upsert, upsert,
}) })
} }
fn build_binding_map_inner(&self) -> Result<BTreeMap<String, TupleSetIdx>> {
let mut binding_map_inner = BTreeMap::new();
match &self.target_info {
TableInfo::Node(n) => {
for (i, k) in n.keys.iter().enumerate() {
binding_map_inner.insert(
k.name.clone(),
TupleSetIdx {
is_key: true,
t_set: 0,
col_idx: i,
},
);
}
for (i, k) in n.vals.iter().enumerate() {
binding_map_inner.insert(
k.name.clone(),
TupleSetIdx {
is_key: false,
t_set: 0,
col_idx: i,
},
);
}
}
TableInfo::Edge(e) => {
let src = self.ctx.get_table_info(e.src_id)?.to_node()?;
let dst = self.ctx.get_table_info(e.dst_id)?.to_node()?;
for (i, k) in src.keys.iter().enumerate() {
binding_map_inner.insert(
k.name.clone(),
TupleSetIdx {
is_key: true,
t_set: 0,
col_idx: i + 1,
},
);
}
for (i, k) in dst.keys.iter().enumerate() {
binding_map_inner.insert(
k.name.clone(),
TupleSetIdx {
is_key: true,
t_set: 0,
col_idx: i + 2 + src.keys.len(),
},
);
}
for (i, k) in e.keys.iter().enumerate() {
binding_map_inner.insert(
k.name.clone(),
TupleSetIdx {
is_key: true,
t_set: 0,
col_idx: i + 2 + src.keys.len() + dst.keys.len(),
},
);
}
for (i, k) in e.vals.iter().enumerate() {
binding_map_inner.insert(
k.name.clone(),
TupleSetIdx {
is_key: false,
t_set: 0,
col_idx: i,
},
);
}
}
_ => unreachable!(),
}
for (iset, info) in self.assoc_infos.iter().enumerate() {
for (i, k) in info.vals.iter().enumerate() {
binding_map_inner.insert(
k.name.clone(),
TupleSetIdx {
is_key: false,
t_set: iset + 1,
col_idx: i,
},
);
}
}
Ok(binding_map_inner)
}
} }
impl<'a> RelationalAlgebra for Insertion<'a> { impl<'a> RelationalAlgebra for Insertion<'a> {
@ -181,8 +97,12 @@ impl<'a> RelationalAlgebra for Insertion<'a> {
} }
} }
fn bindings(&self) -> Result<BTreeSet<String>> {
Ok(BTreeSet::from([self.binding.clone()]))
}
fn binding_map(&self) -> Result<BindingMap> { fn binding_map(&self) -> Result<BindingMap> {
let inner = self.build_binding_map_inner()?; let inner = build_binding_map_from_info(self.ctx, &self.target_info, &self.assoc_infos)?;
Ok(BTreeMap::from([(self.binding.clone(), inner)])) Ok(BTreeMap::from([(self.binding.clone(), inner)]))
} }

@ -0,0 +1,218 @@
use crate::algebra::op::{ChainEl, ChainPart, InterpretContext, RelationalAlgebra};
use crate::algebra::parser::AlgebraParseError;
use crate::context::TempDbContext;
use crate::data::tuple::{DataKind, OwnTuple, Tuple};
use crate::data::tuple_set::{BindingMap, TupleSet, TupleSetIdx};
use crate::ddl::reify::{AssocInfo, TableInfo};
use crate::runtime::options::default_read_options;
use anyhow::Result;
use cozorocks::IteratorPtr;
use std::collections::{BTreeMap, BTreeSet};
pub(crate) struct TableScan<'a> {
ctx: &'a TempDbContext<'a>,
binding: String,
table_info: TableInfo,
assoc_infos: Vec<AssocInfo>,
}
impl<'a> TableScan<'a> {
pub(crate) fn build(ctx: &'a TempDbContext<'a>, el: &ChainEl, only_one: bool) -> Result<Self> {
let table_id = ctx
.resolve_table(&el.target)
.ok_or_else(|| AlgebraParseError::TableNotFound(el.target.to_string()))?;
let table_info = ctx.get_table_info(table_id)?;
match el.part {
ChainPart::Node => {
if only_one {
table_info
.as_node()
.map(|_| ())
.or_else(|_| table_info.as_edge().map(|_| ()))?;
} else {
table_info.as_node()?;
}
}
ChainPart::Edge { .. } => {
table_info.as_edge()?;
}
}
let mut assoc_infos = vec![];
for assoc_name in &el.assocs {
let tid = ctx
.resolve_table(assoc_name)
.ok_or_else(|| AlgebraParseError::TableNotFound(assoc_name.to_string()))?;
let tinfo = ctx.get_table_info(tid)?.to_assoc()?;
if tinfo.src_id != table_info.table_id() {
return Err(AlgebraParseError::NoAssociation(
el.target.to_string(),
assoc_name.to_string(),
)
.into());
}
assoc_infos.push(tinfo);
}
Ok(Self {
ctx,
binding: el.binding.clone(),
table_info,
assoc_infos,
})
}
}
pub(crate) const NAME_TABLE_SCAN: &str = "TableScan";
impl<'b> RelationalAlgebra for TableScan<'b> {
fn name(&self) -> &str {
NAME_TABLE_SCAN
}
fn bindings(&self) -> Result<BTreeSet<String>> {
Ok(BTreeSet::from([self.binding.clone()]))
}
fn binding_map(&self) -> Result<BindingMap> {
let inner = build_binding_map_from_info(self.ctx, &self.table_info, &self.assoc_infos)?;
Ok(BTreeMap::from([(self.binding.clone(), inner)]))
}
fn iter<'a>(&'a self) -> Result<Box<dyn Iterator<Item = Result<TupleSet>> + 'a>> {
let tid = self.table_info.table_id();
let iter = if tid.in_root {
self.ctx.txn.iterator(&self.ctx.sess.r_opts_main)
} else {
self.ctx.sess.temp.iterator(&self.ctx.sess.r_opts_temp)
};
let start_key = OwnTuple::with_prefix(tid.id);
iter.seek(&start_key);
let iterator = ScanTableIterator {
inner: iter,
started: false,
};
Ok(Box::new(iterator))
}
fn identity(&self) -> Option<TableInfo> {
Some(self.table_info.clone())
}
}
pub(crate) struct ScanTableIterator {
inner: IteratorPtr,
started: bool,
}
impl Iterator for ScanTableIterator {
type Item = Result<TupleSet>;
fn next(&mut self) -> Option<Self::Item> {
if self.started {
self.inner.next();
} else {
self.started = true;
}
while let Some((k, v)) = self.inner.pair() {
let v = Tuple::new(v);
if let Ok(DataKind::Data) = v.data_kind() {
let k = Tuple::new(k);
let mut tset = TupleSet::default();
tset.push_key(k.into());
tset.push_val(v.into());
return Some(Ok(tset));
}
self.inner.next();
}
None
}
}
pub(crate) fn build_binding_map_from_info(
ctx: &TempDbContext,
info: &TableInfo,
assoc_infos: &[AssocInfo],
) -> Result<BTreeMap<String, TupleSetIdx>> {
let mut binding_map_inner = BTreeMap::new();
match info {
TableInfo::Node(n) => {
for (i, k) in n.keys.iter().enumerate() {
binding_map_inner.insert(
k.name.clone(),
TupleSetIdx {
is_key: true,
t_set: 0,
col_idx: i,
},
);
}
for (i, k) in n.vals.iter().enumerate() {
binding_map_inner.insert(
k.name.clone(),
TupleSetIdx {
is_key: false,
t_set: 0,
col_idx: i,
},
);
}
}
TableInfo::Edge(e) => {
let src = ctx.get_table_info(e.src_id)?.to_node()?;
let dst = ctx.get_table_info(e.dst_id)?.to_node()?;
for (i, k) in src.keys.iter().enumerate() {
binding_map_inner.insert(
"_src_".to_string() + &k.name,
TupleSetIdx {
is_key: true,
t_set: 0,
col_idx: i + 1,
},
);
}
for (i, k) in dst.keys.iter().enumerate() {
binding_map_inner.insert(
"_dst_".to_string() + &k.name,
TupleSetIdx {
is_key: true,
t_set: 0,
col_idx: i + 2 + src.keys.len(),
},
);
}
for (i, k) in e.keys.iter().enumerate() {
binding_map_inner.insert(
k.name.clone(),
TupleSetIdx {
is_key: true,
t_set: 0,
col_idx: i + 2 + src.keys.len() + dst.keys.len(),
},
);
}
for (i, k) in e.vals.iter().enumerate() {
binding_map_inner.insert(
k.name.clone(),
TupleSetIdx {
is_key: false,
t_set: 0,
col_idx: i,
},
);
}
}
_ => unreachable!(),
}
for (iset, info) in assoc_infos.iter().enumerate() {
for (i, k) in info.vals.iter().enumerate() {
binding_map_inner.insert(
k.name.clone(),
TupleSetIdx {
is_key: false,
t_set: iset + 1,
col_idx: i,
},
);
}
}
Ok(binding_map_inner)
}

@ -12,7 +12,7 @@ use crate::runtime::options::{default_read_options, default_write_options};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use cozorocks::PinnableSlicePtr; use cozorocks::PinnableSlicePtr;
use pest::Parser; use pest::Parser;
use std::collections::BTreeMap; use std::collections::{BTreeMap, BTreeSet};
use std::sync::Arc; use std::sync::Arc;
pub(crate) const NAME_TAGGED_INSERTION: &str = "InsertTagged"; pub(crate) const NAME_TAGGED_INSERTION: &str = "InsertTagged";
@ -251,6 +251,10 @@ impl<'b> RelationalAlgebra for TaggedInsertion<'b> {
} }
} }
fn bindings(&self) -> Result<BTreeSet<String>> {
Ok(BTreeSet::from([self.binding.clone()]))
}
fn binding_map(&self) -> Result<BindingMap> { fn binding_map(&self) -> Result<BindingMap> {
Ok(BTreeMap::from([( Ok(BTreeMap::from([(
self.binding.clone(), self.binding.clone(),

@ -8,7 +8,7 @@ use crate::data::value::{StaticValue, Value};
use crate::ddl::reify::TableInfo; use crate::ddl::reify::TableInfo;
use crate::parser::{Pairs, Rule}; use crate::parser::{Pairs, Rule};
use anyhow::Result; use anyhow::Result;
use std::collections::BTreeMap; use std::collections::{BTreeMap, BTreeSet};
use std::sync::Arc; use std::sync::Arc;
pub(crate) const NAME_RELATION_FROM_VALUES: &str = "Values"; pub(crate) const NAME_RELATION_FROM_VALUES: &str = "Values";
@ -92,6 +92,14 @@ impl RelationalAlgebra for RelationFromValues {
NAME_RELATION_FROM_VALUES NAME_RELATION_FROM_VALUES
} }
fn bindings(&self) -> Result<BTreeSet<String>> {
Ok(self
.binding_map
.iter()
.map(|(k, v)| k.to_string())
.collect())
}
fn binding_map(&self) -> Result<BindingMap> { fn binding_map(&self) -> Result<BindingMap> {
Ok(self.binding_map.clone()) Ok(self.binding_map.clone())
} }

@ -117,16 +117,12 @@ pub(crate) mod tests {
.next() .next()
.unwrap(), .unwrap(),
)?; )?;
for t in ra.iter().unwrap() { dbg!(ra.get_values()?);
t.unwrap(); ctx.txn.commit().unwrap();
} }
{
let s = format!( let ctx = sess.temp_ctx(true);
r#" let s = format!("UpsertTagged({})", HR_DATA);
UpsertTagged({})
"#,
HR_DATA
);
let ra = build_relational_expr( let ra = build_relational_expr(
&ctx, &ctx,
CozoParser::parse(Rule::ra_expr_all, &s) CozoParser::parse(Rule::ra_expr_all, &s)
@ -144,8 +140,24 @@ pub(crate) mod tests {
} }
let duration = start.elapsed(); let duration = start.elapsed();
let start = Instant::now(); let start = Instant::now();
{
let ctx = sess.temp_ctx(true);
let s = "From(e:HasDependent)";
let ra = build_relational_expr(
&ctx,
CozoParser::parse(Rule::ra_expr_all, s)
.unwrap()
.into_iter()
.next()
.unwrap(),
)?;
dbg!(ra.get_values()?);
}
let duration2 = start.elapsed();
let start = Instant::now();
let mut r_opts = default_read_options(); let mut r_opts = default_read_options();
r_opts.set_total_order_seek(true); r_opts.set_total_order_seek(true);
r_opts.set_prefix_same_as_start(false);
let it = sess.main.iterator(&r_opts); let it = sess.main.iterator(&r_opts);
it.to_first(); it.to_first();
let mut n: BTreeMap<u32, usize> = BTreeMap::new(); let mut n: BTreeMap<u32, usize> = BTreeMap::new();
@ -158,8 +170,8 @@ pub(crate) mod tests {
} }
it.next(); it.next();
} }
let duration2 = start.elapsed(); let duration3 = start.elapsed();
dbg!(duration, duration2, n); dbg!(duration, duration2, duration3, n);
Ok(()) Ok(())
} }
} }

@ -6,4 +6,5 @@ pub(crate) mod parser;
pub(crate) mod tuple; pub(crate) mod tuple;
pub(crate) mod tuple_set; pub(crate) mod tuple_set;
pub(crate) mod typing; pub(crate) mod typing;
pub(crate) mod uuid;
pub(crate) mod value; pub(crate) mod value;

@ -0,0 +1,19 @@
use anyhow::Result;
use rand::Rng;
use std::time::{SystemTime, UNIX_EPOCH};
use uuid::v1::{Context, Timestamp};
use uuid::Uuid;
pub(crate) fn random_uuid_v1() -> Result<Uuid> {
let mut rng = rand::thread_rng();
let uuid_ctx = Context::new(rng.gen());
let now = SystemTime::now();
let since_epoch = now.duration_since(UNIX_EPOCH)?;
let ts = Timestamp::from_unix(uuid_ctx, since_epoch.as_secs(), since_epoch.subsec_nanos());
let mut rand_vals = [0u8; 6];
rng.fill(&mut rand_vals);
let id = Uuid::new_v1(ts, &rand_vals)?;
Ok(id)
}

@ -194,7 +194,7 @@ persist_block = {"persist!" ~ "{" ~ definition* ~ "}" }
// ra = { (ra_source | ra_fn_call) ~ ra_method_call* } // ra = { (ra_source | ra_fn_call) ~ ra_method_call* }
chain = { (node_part ~ (edge_part ~ node_part)* ~ edge_part?) | (edge_part ~ (node_part ~ edge_part)* ~ node_part?) } chain = { (node_part ~ (edge_part ~ node_part)* ~ edge_part?) | (edge_part ~ (node_part ~ edge_part)* ~ node_part?) }
node_part = {ident? ~ ":" ~ name_in_def} node_part = {ident? ~ ":" ~ table_with_assocs}
edge_part = {edge_src_marker ~ node_part ~ edge_dst_marker} edge_part = {edge_src_marker ~ node_part ~ edge_dst_marker}
edge_src_marker = {outer_marker? ~ bwd_marker? ~ "-["} edge_src_marker = {outer_marker? ~ bwd_marker? ~ "-["}
edge_dst_marker = {"]-" ~ fwd_marker? ~ outer_marker?} edge_dst_marker = {"]-" ~ fwd_marker? ~ outer_marker?}

@ -24,7 +24,10 @@ pub fn default_options() -> OptionsPtr {
} }
pub fn default_read_options() -> ReadOptionsPtr { pub fn default_read_options() -> ReadOptionsPtr {
ReadOptionsPtr::default() let mut ret = ReadOptionsPtr::default();
ret.set_prefix_same_as_start(true);
ret.set_total_order_seek(false);
ret
} }
pub fn default_write_options() -> WriteOptionsPtr { pub fn default_write_options() -> WriteOptionsPtr {

Loading…
Cancel
Save