access level API

main
Ziyang Hu 2 years ago
parent 1c682000e0
commit e6deae71fa

36
Cargo.lock generated

@ -280,9 +280,9 @@ checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
[[package]]
name = "clap"
version = "3.2.21"
version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ed5341b2301a26ab80be5cbdced622e80ed808483c52e45e3310a877d3b37d7"
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [
"atty",
"bitflags",
@ -292,7 +292,7 @@ dependencies = [
"once_cell",
"strsim",
"termcolor",
"textwrap",
"textwrap 0.16.0",
]
[[package]]
@ -788,9 +788,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.135"
version = "0.2.136"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
checksum = "55edcf6c0bb319052dea84732cf99db461780fd5e8d3eb46ab6ff312ab31f197"
[[package]]
name = "link-cplusplus"
@ -859,7 +859,7 @@ dependencies = [
"supports-hyperlinks",
"supports-unicode",
"terminal_size",
"textwrap",
"textwrap 0.15.2",
"thiserror",
"unicode-width",
]
@ -1394,7 +1394,7 @@ dependencies = [
"serde_json",
"sha1",
"threadpool",
"time 0.3.15",
"time 0.3.16",
"tiny_http",
"url",
]
@ -1646,15 +1646,21 @@ dependencies = [
[[package]]
name = "textwrap"
version = "0.15.0"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
checksum = "b7b3e525a49ec206798b40326a44121291b530c963cfb01018f63e135bac543d"
dependencies = [
"smawk",
"unicode-linebreak",
"unicode-width",
]
[[package]]
name = "textwrap"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "thiserror"
version = "1.0.37"
@ -1728,14 +1734,22 @@ dependencies = [
[[package]]
name = "time"
version = "0.3.15"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d634a985c4d4238ec39cacaed2e7ae552fbd3c476b552c1deac3021b7d7eaf0c"
checksum = "0fab5c8b9980850e06d92ddbe3ab839c062c801f3927c0fb8abd6fc8e918fbca"
dependencies = [
"libc",
"num_threads",
"serde",
"time-core",
]
[[package]]
name = "time-core"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd"
[[package]]
name = "tiny_http"
version = "0.12.0"

@ -28,26 +28,37 @@ Ops on stored relations
List all stored relations currently in the database
.. function:: ::relation columns <REL_NAME>
.. function:: ::columns <REL_NAME>
List all columns for the stored relation ``<REL_NAME>``.
.. function:: ::relation remove <REL_NAME> (, <REL_NAME>)*
.. function:: ::remove <REL_NAME> (, <REL_NAME>)*
Remove stored relations. Several can be specified, joined by commas.
.. function:: ::relation rename <OLD_NAME> -> <NEW_NAME> (, <OLD_NAME> -> <NEW_NAME>)*
.. function:: ::rename <OLD_NAME> -> <NEW_NAME> (, <OLD_NAME> -> <NEW_NAME>)*
Rename stored relation ``<OLD_NAME>`` into ``<NEW_NAME>``. Several may be specified, joined by commas.
.. function:: ::relation show_triggers <REL_NAME>
.. function:: ::show_triggers <REL_NAME>
Display triggers associated with the stored relation ``<REL_NAME>``.
.. function:: ::relation set_triggers <REL_NAME> <TRIGGERS>
.. function:: ::set_triggers <REL_NAME> <TRIGGERS>
Set triggers for the stored relation ``<REL_NAME>``. This is explained in more detail in the transactions chapter.
.. function:: ::access_level <REL_NAME> <ACCESS_LEVEL>
Sets the access level of ``<REL_NAME>`` to the given level. The levels are:
* ``normal`` allows everything,
* ``protected`` disallows ``::remove`` and ``:replace``,
* ``read_only`` additionally disallows any mutations and setting triggers,
* ``hidden`` additionally disallows any data access (metadata access via ``::relations``, etc., are still allowed).
It is recommended to give the appropriate access levels to tables to prevent data loss from programming mistakes.
------------------------------------
Monitor and kill
------------------------------------

@ -1797,7 +1797,7 @@
}
],
"source": [
"::relation columns stored "
"::columns stored "
]
},
{
@ -2295,7 +2295,7 @@
}
],
"source": [
"::relation remove stored"
"::remove stored"
]
},
{
@ -2728,7 +2728,7 @@
}
],
"source": [
"::relation columns fd"
"::columns fd"
]
},
{
@ -2740,6 +2740,14 @@
"We won't overload you with all the complexities in this tutorial."
]
},
{
"cell_type": "markdown",
"id": "726ae4cf-d643-4c27-abe2-56c04050e02e",
"metadata": {},
"source": [
"The stored relations API are designed to be easy to use. However, sometimes being too easy can be bad, for example, it is possible to wipe an important stored relation full of data by a `:replace` erroneously introduced in a rarely used piece of code somewhere. For valuable stored relations, therefore, we recommend that you set the appropriate _access level_, as explained [here](https://cozodb.github.io/current/manual/stored.html#stored-relations)."
]
},
{
"cell_type": "markdown",
"id": "30504cd6-7fb6-4ae3-ba2c-79eefa288095",
@ -2799,7 +2807,7 @@
}
],
"source": [
"::relation remove fd"
"::remove fd"
]
},
{

@ -3,18 +3,20 @@ query_script = {SOI ~ (option | rule | const_rule | algo_rule)+ ~ EOI}
query_script_inner = {"{" ~ (option | rule | const_rule | algo_rule)+ ~ "}"}
multi_script = {SOI ~ query_script_inner+ ~ EOI}
sys_script = {SOI ~ "::" ~ (compact_op | list_relations_op | list_relation_op | remove_relations_op | trigger_relation_op |
trigger_relation_show_op | rename_relations_op | running_op | kill_op | explain_op) ~ EOI}
trigger_relation_show_op | rename_relations_op | running_op | kill_op | explain_op | access_level_op) ~ EOI}
compact_op = {"compact"}
running_op = {"running"}
kill_op = {"kill" ~ int}
explain_op = {"explain" ~ query_script_inner}
list_relations_op = {"relations"}
list_relation_op = {"relation" ~ "columns" ~ compound_ident}
remove_relations_op = {"relation" ~ "remove" ~ (compound_ident ~ ",")* ~ compound_ident }
rename_relations_op = {"relation" ~ "rename" ~ (rename_pair ~ ",")* ~ rename_pair }
trigger_relation_show_op = {"relation" ~ "show_triggers" ~ compound_ident }
trigger_relation_op = {"relation" ~ "set_triggers" ~ compound_ident ~ trigger_clause* }
list_relation_op = {"columns" ~ compound_ident}
remove_relations_op = {"remove" ~ (compound_ident ~ ",")* ~ compound_ident }
rename_relations_op = {"rename" ~ (rename_pair ~ ",")* ~ rename_pair }
access_level_op = {"access_level" ~ access_level ~ compound_ident}
access_level = {("normal" | "protected" | "read_only" | "hidden")}
trigger_relation_show_op = {"show_triggers" ~ compound_ident }
trigger_relation_op = {"set_triggers" ~ compound_ident ~ trigger_clause* }
trigger_clause = { "on" ~ (trigger_put | trigger_rm | trigger_replace) ~ query_script_inner }
trigger_put = {"put"}
trigger_rm = {"rm"}

@ -774,7 +774,9 @@ fn make_empty_const_rule(prog: &mut InputProgram, bindings: &[Symbol]) {
entry_symbol.clone(),
InputInlineRulesOrAlgo::Algo {
algo: AlgoApply {
algo: AlgoHandle { name: entry_symbol },
algo: AlgoHandle {
name: Symbol::new("Constant", Default::default()),
},
rule_args: vec![],
options,
head: bindings.to_vec(),

@ -9,6 +9,7 @@ use crate::data::symb::Symbol;
use crate::data::value::DataValue;
use crate::parse::query::parse_query;
use crate::parse::{ExtractSpan, Pairs, Rule, SourceSpan};
use crate::runtime::relation::AccessLevel;
pub(crate) enum SysOp {
Compact,
@ -21,6 +22,7 @@ pub(crate) enum SysOp {
RenameRelation(Vec<(Symbol, Symbol)>),
ShowTrigger(Symbol),
SetTriggers(Symbol, Vec<String>, Vec<String>, Vec<String>),
SetAccessLevel(Symbol, AccessLevel),
}
#[derive(Debug, Diagnostic, Error)]
@ -76,6 +78,19 @@ pub(crate) fn parse_sys(
.collect_vec();
SysOp::RenameRelation(rename_pairs)
}
Rule::access_level_op => {
let mut ps = inner.into_inner();
let access_level = match ps.next().unwrap().as_str() {
"normal" => AccessLevel::Normal,
"protected" => AccessLevel::Protected,
"read_only" => AccessLevel::ReadOnly,
"hidden" => AccessLevel::Hidden,
_ => unreachable!()
};
let rel_p = ps.next().unwrap();
let rel = Symbol::new(rel_p.as_str(), rel_p.extract_span());
SysOp::SetAccessLevel(rel, access_level)
}
Rule::trigger_relation_show_op => {
let rels_p = inner.into_inner().next().unwrap();
let rel = Symbol::new(rels_p.as_str(), rels_p.extract_span());

@ -1,19 +1,21 @@
use std::collections::{BTreeMap, BTreeSet};
use itertools::Itertools;
use miette::{ensure, Context, Diagnostic, Result};
use miette::{bail, ensure, Context, Diagnostic, Result};
use thiserror::Error;
use crate::data::aggr::Aggregation;
use crate::data::expr::Expr;
use crate::data::program::{
MagicAlgoApply, MagicAtom, MagicInlineRule, MagicRulesOrAlgo, MagicSymbol, StratifiedMagicProgram,
MagicAlgoApply, MagicAtom, MagicInlineRule, MagicRulesOrAlgo, MagicSymbol,
StratifiedMagicProgram,
};
use crate::data::symb::Symbol;
use crate::data::value::DataValue;
use crate::parse::SourceSpan;
use crate::query::relation::RelAlgebra;
use crate::runtime::in_mem::InMemRelation;
use crate::runtime::relation::{AccessLevel, InsufficientAccessLevel};
use crate::runtime::transact::SessionTx;
pub(crate) type CompiledProgram = BTreeMap<MagicSymbol, CompiledRuleSet>;
@ -195,6 +197,13 @@ impl SessionTx {
}
MagicAtom::Relation(rel_app) => {
let store = self.get_relation(&rel_app.name, false)?;
if store.access_level < AccessLevel::ReadOnly {
bail!(InsufficientAccessLevel(
store.name.to_string(),
"reading rows".to_string(),
store.access_level
));
}
ensure!(
store.arity() == rel_app.args.len(),
ArityMismatch(

@ -21,7 +21,7 @@ use crate::utils::swap_option_result;
pub(crate) enum RelAlgebra {
Fixed(InlineFixedRA),
InMem(InMemRelationRA),
Relation(RelationRA),
Stored(StoredRA),
Join(Box<InnerJoin>),
NegJoin(Box<NegJoin>),
Reorder(ReorderRA),
@ -34,7 +34,7 @@ impl RelAlgebra {
match self {
RelAlgebra::Fixed(i) => i.span,
RelAlgebra::InMem(i) => i.span,
RelAlgebra::Relation(i) => i.span,
RelAlgebra::Stored(i) => i.span,
RelAlgebra::Join(i) => i.span,
RelAlgebra::NegJoin(i) => i.span,
RelAlgebra::Reorder(i) => i.relation.span(),
@ -253,7 +253,7 @@ impl Debug for RelAlgebra {
.field(&r.storage.rule_name)
.field(&r.filters)
.finish(),
RelAlgebra::Relation(r) => f
RelAlgebra::Stored(r) => f
.debug_tuple("Derived")
.field(&bindings)
.field(&r.storage.name)
@ -307,7 +307,7 @@ impl RelAlgebra {
RelAlgebra::InMem(d) => {
d.fill_binding_indices()?;
}
RelAlgebra::Relation(v) => {
RelAlgebra::Stored(v) => {
v.fill_binding_indices()?;
}
RelAlgebra::Reorder(r) => {
@ -357,7 +357,7 @@ impl RelAlgebra {
storage: RelationHandle,
span: SourceSpan,
) -> Self {
Self::Relation(RelationRA {
Self::Stored(StoredRA {
bindings,
storage,
filters: vec![],
@ -412,14 +412,14 @@ impl RelAlgebra {
span,
})
}
RelAlgebra::Relation(RelationRA {
RelAlgebra::Stored(StoredRA {
bindings,
storage,
mut filters,
span,
}) => {
filters.push(filter);
RelAlgebra::Relation(RelationRA {
RelAlgebra::Stored(StoredRA {
bindings,
storage,
filters,
@ -720,14 +720,14 @@ fn get_eliminate_indices(bindings: &[Symbol], eliminate: &BTreeSet<Symbol>) -> B
}
#[derive(Debug)]
pub(crate) struct RelationRA {
pub(crate) struct StoredRA {
pub(crate) bindings: Vec<Symbol>,
pub(crate) storage: RelationHandle,
pub(crate) filters: Vec<Expr>,
pub(crate) span: SourceSpan,
}
impl RelationRA {
impl StoredRA {
fn fill_binding_indices(&mut self) -> Result<()> {
let bindings: BTreeMap<_, _> = self
.bindings
@ -1253,7 +1253,7 @@ impl RelAlgebra {
match self {
RelAlgebra::Fixed(r) => r.do_eliminate_temp_vars(used),
RelAlgebra::InMem(_r) => Ok(()),
RelAlgebra::Relation(_v) => Ok(()),
RelAlgebra::Stored(_v) => Ok(()),
RelAlgebra::Join(r) => r.do_eliminate_temp_vars(used),
RelAlgebra::Reorder(r) => r.relation.eliminate_temp_vars(used),
RelAlgebra::Filter(r) => r.do_eliminate_temp_vars(used),
@ -1266,7 +1266,7 @@ impl RelAlgebra {
match self {
RelAlgebra::Fixed(r) => Some(&r.to_eliminate),
RelAlgebra::InMem(_) => None,
RelAlgebra::Relation(_) => None,
RelAlgebra::Stored(_) => None,
RelAlgebra::Join(r) => Some(&r.to_eliminate),
RelAlgebra::Reorder(_) => None,
RelAlgebra::Filter(r) => Some(&r.to_eliminate),
@ -1290,7 +1290,7 @@ impl RelAlgebra {
match self {
RelAlgebra::Fixed(f) => f.bindings.clone(),
RelAlgebra::InMem(d) => d.bindings.clone(),
RelAlgebra::Relation(v) => v.bindings.clone(),
RelAlgebra::Stored(v) => v.bindings.clone(),
RelAlgebra::Join(j) => j.bindings(),
RelAlgebra::Reorder(r) => r.bindings(),
RelAlgebra::Filter(r) => r.parent.bindings_after_eliminate(),
@ -1311,7 +1311,7 @@ impl RelAlgebra {
match self {
RelAlgebra::Fixed(f) => Ok(Box::new(f.data.iter().map(|t| Ok(Tuple(t.clone()))))),
RelAlgebra::InMem(r) => r.iter(epoch, use_delta),
RelAlgebra::Relation(v) => v.iter(tx),
RelAlgebra::Stored(v) => v.iter(tx),
RelAlgebra::Join(j) => j.iter(tx, epoch, use_delta),
RelAlgebra::Reorder(r) => r.iter(tx, epoch, use_delta),
RelAlgebra::Filter(r) => r.iter(tx, epoch, use_delta),
@ -1360,7 +1360,7 @@ impl NegJoin {
"mem_neg_mat_join"
}
}
RelAlgebra::Relation(_) => {
RelAlgebra::Stored(_) => {
let join_indices = self
.joiner
.join_indices(
@ -1403,7 +1403,7 @@ impl NegJoin {
eliminate_indices,
)
}
RelAlgebra::Relation(v) => {
RelAlgebra::Stored(v) => {
let join_indices = self
.joiner
.join_indices(
@ -1481,7 +1481,7 @@ impl InnerJoin {
"mem_mat_join"
}
}
RelAlgebra::Relation(_) => {
RelAlgebra::Stored(_) => {
let join_indices = self
.joiner
.join_indices(
@ -1549,7 +1549,7 @@ impl InnerJoin {
self.materialized_join(tx, eliminate_indices, epoch, use_delta)
}
}
RelAlgebra::Relation(r) => {
RelAlgebra::Stored(r) => {
let join_indices = self
.joiner
.join_indices(

@ -8,13 +8,13 @@ use thiserror::Error;
use crate::algo::constant::Constant;
use crate::algo::AlgoHandle;
use crate::data::expr::Expr;
use crate::data::program::{AlgoApply, InputProgram, InputInlineRulesOrAlgo, RelationOp};
use crate::data::program::{AlgoApply, InputInlineRulesOrAlgo, InputProgram, RelationOp};
use crate::data::relation::{ColumnDef, NullableColType};
use crate::data::symb::Symbol;
use crate::data::tuple::{EncodedTuple, Tuple};
use crate::data::value::DataValue;
use crate::parse::parse_script;
use crate::runtime::relation::InputRelationHandle;
use crate::runtime::relation::{AccessLevel, InputRelationHandle, InsufficientAccessLevel};
use crate::runtime::transact::SessionTx;
use crate::Db;
@ -36,6 +36,13 @@ impl SessionTx {
let mut replaced_old_triggers = None;
if op == RelationOp::Replace {
if let Ok(old_handle) = self.get_relation(&meta.name, true) {
if old_handle.access_level < AccessLevel::Normal {
bail!(InsufficientAccessLevel(
old_handle.name.to_string(),
"relation replacement".to_string(),
old_handle.access_level
));
}
if old_handle.has_triggers() {
replaced_old_triggers = Some((old_handle.put_triggers, old_handle.rm_triggers))
}
@ -76,6 +83,13 @@ impl SessionTx {
match op {
RelationOp::Rm => {
if relation_store.access_level < AccessLevel::Protected {
bail!(InsufficientAccessLevel(
relation_store.name.to_string(),
"row removal".to_string(),
relation_store.access_level
));
}
let key_extractors = make_extractors(
&relation_store.metadata.keys,
&metadata.keys,
@ -147,6 +161,14 @@ impl SessionTx {
}
}
RelationOp::Ensure => {
if relation_store.access_level < AccessLevel::ReadOnly {
bail!(InsufficientAccessLevel(
relation_store.name.to_string(),
"row check".to_string(),
relation_store.access_level
));
}
let mut key_extractors = make_extractors(
&relation_store.metadata.keys,
&metadata.keys,
@ -198,6 +220,14 @@ impl SessionTx {
}
}
RelationOp::EnsureNot => {
if relation_store.access_level < AccessLevel::ReadOnly {
bail!(InsufficientAccessLevel(
relation_store.name.to_string(),
"row check".to_string(),
relation_store.access_level
));
}
let key_extractors = make_extractors(
&relation_store.metadata.keys,
&metadata.keys,
@ -225,6 +255,14 @@ impl SessionTx {
}
}
RelationOp::Create | RelationOp::Replace | RelationOp::Put => {
if relation_store.access_level < AccessLevel::Protected {
bail!(InsufficientAccessLevel(
relation_store.name.to_string(),
"row insertion".to_string(),
relation_store.access_level
));
}
let mut key_extractors = make_extractors(
&relation_store.metadata.keys,
&metadata.keys,
@ -398,9 +436,7 @@ fn make_const_rule(
rule_symbol.clone(),
InputInlineRulesOrAlgo::Algo {
algo: AlgoApply {
algo: AlgoHandle {
name: rule_symbol,
},
algo: AlgoHandle { name: rule_symbol },
rule_args: vec![],
options,
head: bindings,

@ -25,7 +25,7 @@ use crate::parse::sys::SysOp;
use crate::parse::{parse_script, CozoScript, SourceSpan};
use crate::query::compile::{CompiledProgram, CompiledRule, CompiledRuleSet};
use crate::query::relation::{
FilteredRA, InMemRelationRA, InnerJoin, NegJoin, RelAlgebra, RelationRA, ReorderRA,
FilteredRA, InMemRelationRA, InnerJoin, NegJoin, RelAlgebra, StoredRA, ReorderRA,
UnificationRA,
};
use crate::runtime::relation::{RelationHandle, RelationId};
@ -306,7 +306,7 @@ impl Db {
json!(null),
json!(filters.iter().map(|f| f.to_string()).collect_vec()),
),
RelAlgebra::Relation(RelationRA {
RelAlgebra::Stored(StoredRA {
storage, filters, ..
}) => (
"load_stored",
@ -479,6 +479,12 @@ impl Db {
tx.commit_tx()?;
Ok(json!({"headers": ["status"], "rows": [["OK"]]}))
}
SysOp::SetAccessLevel(name, level) => {
let mut tx = self.transact_write()?;
tx.set_access_level(name, level)?;
tx.commit_tx()?;
Ok(json!({"headers": ["status"], "rows": [["OK"]]}))
}
}
}
pub(crate) fn run_query(
@ -720,9 +726,11 @@ impl Db {
let n_dependents = meta.metadata.non_keys.len();
let arity = n_keys + n_dependents;
let name = meta.name;
let access_level = meta.access_level.to_string();
collected.push(json!([
name,
arity,
access_level,
n_keys,
n_dependents,
meta.put_triggers.len(),
@ -732,7 +740,7 @@ impl Db {
it.next();
}
Ok(json!({"rows": collected, "headers":
["name", "arity", "n_keys", "n_non_keys", "n_put_triggers", "n_rm_triggers", "n_replace_triggers"]}))
["name", "arity", "access_level", "n_keys", "n_non_keys", "n_put_triggers", "n_rm_triggers", "n_replace_triggers"]}))
}
}

@ -1,6 +1,6 @@
use std::cmp::max;
use std::cmp::Ordering::Greater;
use std::fmt::{Debug, Formatter};
use std::fmt::{Debug, Display, Formatter};
use std::sync::atomic::Ordering;
use log::error;
@ -64,15 +64,38 @@ pub(crate) struct RelationHandle {
pub(crate) put_triggers: Vec<String>,
pub(crate) rm_triggers: Vec<String>,
pub(crate) replace_triggers: Vec<String>,
pub(crate) state: RelationState,
pub(crate) access_level: AccessLevel,
}
#[derive(Clone, Eq, PartialEq, serde_derive::Serialize, serde_derive::Deserialize)]
pub(crate) enum RelationState {
Normal,
Protected,
#[derive(
Copy,
Clone,
Debug,
Eq,
PartialEq,
serde_derive::Serialize,
serde_derive::Deserialize,
Default,
Ord,
PartialOrd,
)]
pub(crate) enum AccessLevel {
Hidden,
ReadOnly,
Hidden
Protected,
#[default]
Normal,
}
impl Display for AccessLevel {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
AccessLevel::Normal => f.write_str("normal"),
AccessLevel::Protected => f.write_str("protected"),
AccessLevel::ReadOnly => f.write_str("read_only"),
AccessLevel::Hidden => f.write_str("hidden"),
}
}
}
#[derive(Debug, Error, Diagnostic)]
@ -300,6 +323,13 @@ impl SessionTx {
replaces: Vec<String>,
) -> Result<()> {
let mut original = self.get_relation(&name, true)?;
if original.access_level < AccessLevel::Protected {
bail!(InsufficientAccessLevel(
original.name.to_string(),
"set triggers".to_string(),
original.access_level
))
}
original.put_triggers = puts;
original.rm_triggers = rms;
original.replace_triggers = replaces;
@ -335,7 +365,7 @@ impl SessionTx {
put_triggers: vec![],
rm_triggers: vec![],
replace_triggers: vec![],
state: RelationState::Normal
access_level: AccessLevel::Normal,
};
self.tx.put(&encoded, &meta.id.raw_encode())?;
@ -370,6 +400,13 @@ impl SessionTx {
}
pub(crate) fn destroy_relation(&mut self, name: &str) -> Result<(Vec<u8>, Vec<u8>)> {
let store = self.get_relation(name, true)?;
if store.access_level < AccessLevel::Normal {
bail!(InsufficientAccessLevel(
store.name.to_string(),
"relation removal".to_string(),
store.access_level
))
}
let key = DataValue::Str(SmartString::from(name as &str));
let encoded = Tuple(vec![key]).encode_as_key(RelationId::SYSTEM);
self.tx.del(&encoded)?;
@ -377,6 +414,20 @@ impl SessionTx {
let upper_bound = Tuple::default().encode_as_key(store.id.next());
Ok((lower_bound, upper_bound))
}
pub(crate) fn set_access_level(&mut self, rel: Symbol, level: AccessLevel) -> Result<()> {
let mut meta = self.get_relation(&rel, true)?;
meta.access_level = level;
let name_key =
Tuple(vec![DataValue::Str(meta.name.clone())]).encode_as_key(RelationId::SYSTEM);
let mut meta_val = vec![];
meta.serialize(&mut Serializer::new(&mut meta_val).with_struct_map())
.unwrap();
self.tx.put(&name_key, &meta_val)?;
Ok(())
}
pub(crate) fn rename_relation(&mut self, old: Symbol, new: Symbol) -> Result<()> {
let new_key = DataValue::Str(new.name.clone());
let new_encoded = Tuple(vec![new_key]).encode_as_key(RelationId::SYSTEM);
@ -389,6 +440,13 @@ impl SessionTx {
let old_encoded = Tuple(vec![old_key]).encode_as_key(RelationId::SYSTEM);
let mut rel = self.get_relation(&old, true)?;
if rel.access_level < AccessLevel::Normal {
bail!(InsufficientAccessLevel(
rel.name.to_string(),
"renaming relation".to_string(),
rel.access_level
));
}
rel.name = new.name;
let mut meta_val = vec![];
@ -399,3 +457,11 @@ impl SessionTx {
Ok(())
}
}
#[derive(Debug, Error, Diagnostic)]
#[error("Insufficient access level {2} for {1} on stored relation '{0}'")]
pub(crate) struct InsufficientAccessLevel(
pub(crate) String,
pub(crate) String,
pub(crate) AccessLevel,
);

Loading…
Cancel
Save