Skip to content

Commit 7dc748b

Browse files
committed
graph, store: Do not panic in Entity.insert when given an uninterned key
1 parent 9a5c305 commit 7dc748b

File tree

8 files changed

+32
-24
lines changed

8 files changed

+32
-24
lines changed

graph/src/components/store/entity_cache.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::prelude::ENV_VARS;
99
use crate::schema::InputSchema;
1010
use crate::util::lfu_cache::LfuCache;
1111

12-
use super::{DerivedEntityQuery, EntityType, LoadRelatedRequest};
12+
use super::{DerivedEntityQuery, EntityType, LoadRelatedRequest, StoreError};
1313

1414
/// The scope in which the `EntityCache` should perform a `get` operation
1515
pub enum GetScope {
@@ -113,11 +113,7 @@ impl EntityCache {
113113
self.handler_updates.clear();
114114
}
115115

116-
pub fn get(
117-
&mut self,
118-
key: &EntityKey,
119-
scope: GetScope,
120-
) -> Result<Option<Entity>, s::QueryExecutionError> {
116+
pub fn get(&mut self, key: &EntityKey, scope: GetScope) -> Result<Option<Entity>, StoreError> {
121117
// Get the current entity, apply any updates from `updates`, then
122118
// from `handler_updates`.
123119
let mut entity = match scope {
@@ -133,10 +129,10 @@ impl EntityCache {
133129
});
134130

135131
if let Some(op) = self.updates.get(key).cloned() {
136-
entity = op.apply_to(entity)
132+
entity = op.apply_to(entity).map_err(|e| key.unknown_attribute(e))?;
137133
}
138134
if let Some(op) = self.handler_updates.get(key).cloned() {
139-
entity = op.apply_to(entity)
135+
entity = op.apply_to(entity).map_err(|e| key.unknown_attribute(e))?;
140136
}
141137
Ok(entity)
142138
}
@@ -266,7 +262,7 @@ impl EntityCache {
266262
/// to the current state is actually needed.
267263
///
268264
/// Also returns the updated `LfuCache`.
269-
pub fn as_modifications(mut self) -> Result<ModificationsAndCache, s::QueryExecutionError> {
265+
pub fn as_modifications(mut self) -> Result<ModificationsAndCache, StoreError> {
270266
assert!(!self.in_handler);
271267

272268
// The first step is to make sure all entities being set are in `self.current`.
@@ -305,7 +301,8 @@ impl EntityCache {
305301
// Entity may have been changed
306302
(Some(current), EntityOp::Update(updates)) => {
307303
let mut data = current.clone();
308-
data.merge_remove_null_fields(updates);
304+
data.merge_remove_null_fields(updates)
305+
.map_err(|e| key.unknown_attribute(e))?;
309306
self.current.insert(key.clone(), Some(data.clone()));
310307
if current != data {
311308
Some(Overwrite { key, data })

graph/src/components/store/err.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub enum StoreError {
1818
UnknownField(String),
1919
#[error("unknown table '{0}'")]
2020
UnknownTable(String),
21+
#[error("entity type '{0}' does not have an attribute '{0}'")]
22+
UnknownAttribute(String, String),
2123
#[error("malformed directive '{0}'")]
2224
MalformedDirective(String),
2325
#[error("query execution failed: {0}")]

graph/src/components/store/mod.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::data::store::*;
2929
use crate::data::value::Word;
3030
use crate::data_source::CausalityRegion;
3131
use crate::schema::InputSchema;
32+
use crate::util::intern::{self, Error as InternError};
3233
use crate::{constraint_violation, prelude::*};
3334

3435
/// The type name of an entity. This is the string that is used in the
@@ -139,6 +140,12 @@ pub struct EntityKey {
139140
pub causality_region: CausalityRegion,
140141
}
141142

143+
impl EntityKey {
144+
pub fn unknown_attribute(&self, err: intern::Error) -> StoreError {
145+
StoreError::UnknownAttribute(self.entity_type.to_string(), err.not_interned())
146+
}
147+
}
148+
142149
#[derive(Debug, Clone)]
143150
pub struct LoadRelatedRequest {
144151
/// Name of the entity type.
@@ -1008,14 +1015,14 @@ enum EntityOp {
10081015
}
10091016

10101017
impl EntityOp {
1011-
fn apply_to(self, entity: Option<Entity>) -> Option<Entity> {
1018+
fn apply_to(self, entity: Option<Entity>) -> Result<Option<Entity>, InternError> {
10121019
use EntityOp::*;
10131020
match (self, entity) {
1014-
(Remove, _) => None,
1015-
(Overwrite(new), _) | (Update(new), None) => Some(new),
1021+
(Remove, _) => Ok(None),
1022+
(Overwrite(new), _) | (Update(new), None) => Ok(Some(new)),
10161023
(Update(updates), Some(mut entity)) => {
1017-
entity.merge_remove_null_fields(updates);
1018-
Some(entity)
1024+
entity.merge_remove_null_fields(updates)?;
1025+
Ok(Some(entity))
10191026
}
10201027
}
10211028
}

graph/src/data/store/mod.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
runtime::gas::{Gas, GasSizeOf},
66
schema::InputSchema,
77
util::intern::AtomPool,
8-
util::intern::{NullValue, Object},
8+
util::intern::{Error as InternError, NullValue, Object},
99
};
1010
use crate::{data::subgraph::DeploymentHash, prelude::EntityChange};
1111
use anyhow::{anyhow, Error};
@@ -744,8 +744,8 @@ impl Entity {
744744
self.0.get(key)
745745
}
746746

747-
pub fn insert(&mut self, key: &str, value: Value) -> Option<Value> {
748-
self.0.insert(key, value).expect("key is in AtomPool")
747+
pub fn insert(&mut self, key: &str, value: Value) -> Result<Option<Value>, InternError> {
748+
self.0.insert(key, value)
749749
}
750750

751751
pub fn remove(&mut self, key: &str) -> Option<Value> {
@@ -797,13 +797,14 @@ impl Entity {
797797
/// If a key exists in both entities, the value from `update` is chosen.
798798
/// If a key only exists on one entity, the value from that entity is chosen.
799799
/// If a key is set to `Value::Null` in `update`, the key/value pair is removed.
800-
pub fn merge_remove_null_fields(&mut self, update: Entity) {
800+
pub fn merge_remove_null_fields(&mut self, update: Entity) -> Result<(), InternError> {
801801
for (key, value) in update.0.into_iter() {
802802
match value {
803803
Value::Null => self.remove(&key),
804-
_ => self.insert(&key, value),
804+
_ => self.insert(&key, value)?,
805805
};
806806
}
807+
Ok(())
807808
}
808809

809810
/// Remove all entries with value `Value::Null` from `self`

graph/src/util/intern.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub enum Error {
5252
}
5353

5454
impl Error {
55-
pub fn not_interned(&self) -> &str {
55+
pub fn not_interned(self) -> String {
5656
match self {
5757
Error::NotInterned(s) => s,
5858
}

store/postgres/src/relational_queries.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1762,7 +1762,8 @@ impl<'a> InsertQuery<'a> {
17621762
if !fulltext_field_values.is_empty() {
17631763
entity
17641764
.to_mut()
1765-
.insert(&column.field, Value::List(fulltext_field_values));
1765+
.insert(&column.field, Value::List(fulltext_field_values))
1766+
.map_err(|e| entity_key.unknown_attribute(e))?;
17661767
}
17671768
}
17681769
if !column.is_nullable() && !entity.contains_key(&column.field) {

store/test-store/tests/postgres/relational.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ fn make_user(
341341
}
342342
.unwrap();
343343
if let Some(drinks) = drinks {
344-
user.insert("drinks", drinks.into());
344+
user.insert("drinks", drinks.into()).unwrap();
345345
}
346346
user
347347
}

store/test-store/tests/postgres/store.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ fn update_existing() {
446446
_ => unreachable!(),
447447
};
448448

449-
new_data.insert("bin_name", Value::Bytes(bin_name));
449+
new_data.insert("bin_name", Value::Bytes(bin_name)).unwrap();
450450
assert_eq!(writable.get(&entity_key).unwrap(), Some(new_data));
451451
})
452452
}

0 commit comments

Comments
 (0)