Skip to content

Commit

Permalink
[db] Do not fail insert when alias exists #1097 (#1101)
Browse files Browse the repository at this point in the history
* prevent failure on inserting to existing aliases

* Update queries.md

* Handle mismatched alias and values lengths
  • Loading branch information
michaelvlach authored May 20, 2024
1 parent 48b3354 commit f51bc5f
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 11 deletions.
7 changes: 0 additions & 7 deletions agdb/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -599,13 +599,6 @@ impl<Store: StorageData> DbImpl<Store> {
db_id: DbId,
alias: &String,
) -> Result<(), QueryError> {
if let Some(id) = self.aliases.value(&self.storage, alias)? {
return Err(QueryError::from(format!(
"Alias '{alias}' already exists ({})",
id.0
)));
}

self.undo_stack.push(Command::RemoveAlias {
alias: alias.clone(),
});
Expand Down
21 changes: 21 additions & 0 deletions agdb/src/query/insert_nodes_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::query::query_values::QueryValues;
use crate::DbElement;
use crate::DbImpl;
use crate::QueryError;
use crate::QueryId;
use crate::QueryMut;
use crate::QueryResult;
use crate::StorageData;
Expand Down Expand Up @@ -44,7 +45,27 @@ impl QueryMut for InsertNodesQuery {
QueryValues::Multi(v) => v.iter().collect(),
};

if !self.aliases.is_empty() && values.len() != self.aliases.len() {
return Err(QueryError::from(format!(
"Values ({}) and aliases ({}) must have the same length",
values.len(),
self.aliases.len()
)));
}

for (index, key_values) in values.iter().enumerate() {
if let Some(alias) = self.aliases.get(index) {
if let Ok(db_id) = db.db_id(&QueryId::Alias(alias.to_string())) {
ids.push(db_id);

for key_value in *key_values {
db.insert_or_replace_key_value(db_id, key_value)?;
}

continue;
}
}

let db_id = db.insert_node()?;
ids.push(db_id);

Expand Down
63 changes: 60 additions & 3 deletions agdb/tests/insert_nodes_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ fn insert_node_existing_alias() {
QueryBuilder::insert().nodes().aliases("alias").query(),
&[1],
);
db.exec_mut_error(
db.exec_mut_ids(
QueryBuilder::insert().nodes().aliases("alias").query(),
"Alias 'alias' already exists (1)",
)
&[1],
);
}

#[test]
Expand Down Expand Up @@ -228,3 +228,60 @@ fn insert_nodes_values() {
],
);
}

#[test]
fn insert_nodes_existing_aliases_values() {
let mut db = TestDb::new();
db.exec_mut(
QueryBuilder::insert()
.nodes()
.aliases("alias")
.values(vec![vec![("key", 1).into()]])
.query(),
1,
);
db.exec_mut(
QueryBuilder::insert()
.nodes()
.aliases(vec!["new_alias", "alias", "alias3"])
.values(vec![
vec![("some_key", "value").into()],
vec![("key", 10).into(), ("new_key", 100).into()],
vec![],
])
.query(),
3,
);
db.exec_elements(
QueryBuilder::select()
.ids(vec!["alias", "new_alias"])
.query(),
&[
DbElement {
id: DbId(1),
from: None,
to: None,
values: vec![("key", 10).into(), ("new_key", 100).into()],
},
DbElement {
id: DbId(2),
from: None,
to: None,
values: vec![("some_key", "value").into()],
},
],
);
}

#[test]
fn insert_nodes_aliases_values_mismatched_length() {
let mut db = TestDb::new();
db.exec_mut_error(
QueryBuilder::insert()
.nodes()
.aliases(vec!["alias", "alias2"])
.values(vec![vec![("key", 1).into()]])
.query(),
"Values (1) and aliases (2) must have the same length",
);
}
2 changes: 1 addition & 1 deletion docs/queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ QueryBuilder::insert().nodes().values(vec![vec![("k", 1).into()], vec![("k", 2).

</td></tr></table>

The `count` is the number of nodes to be inserted into the database. It can be omitted (left `0`) if either `values` or `aliases` (or both) are provided. If the `values` is [`QueryValues::Single`](#queryvalues) you must provide either `count` or `aliases`. It is a logic error if the count cannot be inferred and is set to `0`. If both `values` [`QueryValues::Multi`](#queryvalues) and `aliases` are provided their lengths must match, otherwise it will result in a logic error. Empty alias (`""`) are not allowed. The values can be inferred from user defined types if they implement `DbUserValue` trait (`#derive(agdb::UserValue)`). Both singular nad vectorized versions are supported.
The `count` is the number of nodes to be inserted into the database. It can be omitted (left `0`) if either `values` or `aliases` (or both) are provided. If the `values` is [`QueryValues::Single`](#queryvalues) you must provide either `count` or `aliases`. It is not an error if the count is set to `0` but the query will be an no-op and return empty result. If both `values` [`QueryValues::Multi`](#queryvalues) and `aliases` are provided their lengths must match, otherwise it will result in a logic error. Empty alias (`""`) are not allowed. The values can be inferred from user defined types if they implement `DbUserValue` trait (`#derive(agdb::UserValue)`). Both singular nad vectorized versions are supported. If any alias already exists in the database its values will be amended (inserted or replaced) with the provided values.

### Insert values

Expand Down

0 comments on commit f51bc5f

Please sign in to comment.