use smallvec::SmallVec;
use super::*;
#[derive(Clone, Copy)]
pub(super) enum PropertyMapTarget {
Node,
Edge,
}
pub(super) fn property_map(
property_inits: &[PropertyInit],
target: PropertyMapTarget,
row: &Binding,
schema: &BindingTableSchema,
ctx: &EvalCtx<'_, '_, '_, '_>,
) -> Result<PropertyMap, ExecutorError> {
let mut pairs = Vec::with_capacity(property_inits.len());
let mut rows_since_check = 0;
for init in property_inits {
ctx.tx.check_cancellation_stride(&mut rows_since_check, 1)?;
pairs.push((
init.key.clone(),
evaluator::evaluate(&init.value.expr, row, schema, ctx)?,
));
}
PropertyMap::from_pairs(pairs).map_err(|source| {
let subclass = match target {
PropertyMapTarget::Node => DataExceptionSubclass::NodePropertiesExceedSupportedMaximum,
PropertyMapTarget::Edge => DataExceptionSubclass::EdgePropertiesExceedSupportedMaximum,
};
ExecutorError::data_exception(
subclass,
format!("property map construction failed: {source}"),
property_inits
.first()
.map(|init| init.span)
.unwrap_or_default(),
)
})
}
pub(super) fn node_labels(
label_expr: Option<&LabelExpr>,
span: SourceSpan,
) -> Result<LabelSet, ExecutorError> {
match label_expr {
None => Ok(LabelSet::new()),
Some(LabelExpr::Single(label)) => Ok(LabelSet::single(label.clone())),
Some(LabelExpr::Conjunction(parts)) => {
let mut labels = LabelSet::new();
for part in parts {
let LabelExpr::Single(label) = part else {
return Err(unsupported_node_label_expr(span));
};
labels.insert(label.clone());
}
Ok(labels)
}
Some(_) => Err(unsupported_node_label_expr(span)),
}
}
pub(super) fn unsupported_node_label_expr(span: SourceSpan) -> ExecutorError {
ExecutorError::FeatureNotSupportedYet {
feature: "INSERT label expression form",
span,
}
}
pub(super) fn edge_label(
label_expr: Option<&LabelExpr>,
span: SourceSpan,
) -> Result<DbString, ExecutorError> {
match label_expr {
Some(LabelExpr::Single(label)) => Ok(label.clone()),
None => Err(ExecutorError::data_exception(
DataExceptionSubclass::EdgeLabelsBelowSupportedMinimum,
"INSERT edge requires one label under IL001",
span,
)),
Some(LabelExpr::Conjunction(_)) => Err(ExecutorError::data_exception(
DataExceptionSubclass::EdgeLabelsExceedSupportedMaximum,
"INSERT edge supports exactly one label under IL001",
span,
)),
Some(_) => Err(ExecutorError::FeatureNotSupportedYet {
feature: "INSERT edge label expression form",
span,
}),
}
}
pub(super) fn endpoint_node(
row: &Binding,
endpoint: InsertEndpointRef,
span: SourceSpan,
) -> Result<NodeId, ExecutorError> {
match endpoint {
InsertEndpointRef::Binding { column_index, .. } => target_node(row, column_index, span)?
.ok_or(ExecutorError::ImplementationDefined {
detail: "insert edge endpoint is null",
}),
InsertEndpointRef::InsertedNode(site_id) => {
row.inserted_node(site_id)
.ok_or(ExecutorError::ImplementationDefined {
detail: "insert edge endpoint site missing",
})
}
}
}
pub(super) fn edge_endpoints(
left: NodeId,
right: NodeId,
direction: EdgeDirection,
span: SourceSpan,
) -> Result<(NodeId, NodeId), ExecutorError> {
match direction {
EdgeDirection::Right => Ok((left, right)),
EdgeDirection::Left => Ok((right, left)),
EdgeDirection::Undirected => Err(ExecutorError::FeatureNotSupportedYet {
feature: "INSERT undirected edge",
span,
}),
}
}
pub(super) fn target_node(
row: &Binding,
column_index: u32,
span: SourceSpan,
) -> Result<Option<NodeId>, ExecutorError> {
match target_value(row, column_index, span)? {
Value::NodeRef(id) => Ok(Some(id)),
Value::Null => Ok(None),
_ => Err(ExecutorError::ImplementationDefined {
detail: "mutation target is not a node",
}),
}
}
pub(super) fn target_edge(
row: &Binding,
column_index: u32,
span: SourceSpan,
) -> Result<Option<EdgeId>, ExecutorError> {
match target_value(row, column_index, span)? {
Value::EdgeRef(id) => Ok(Some(id)),
Value::Null => Ok(None),
_ => Err(ExecutorError::ImplementationDefined {
detail: "mutation target is not an edge",
}),
}
}
pub(super) fn target_value(
row: &Binding,
column_index: u32,
span: SourceSpan,
) -> Result<Value, ExecutorError> {
row.get(column_index as usize)
.cloned()
.ok_or_else(|| ExecutorError::InvalidReference {
name: format!("binding column {column_index}"),
span,
})
}
pub(super) fn extend_schema(
schema: &mut BindingTableSchema,
output_column_index: Option<u32>,
output_column: Option<&BindingTableColumn>,
) -> Result<(), ExecutorError> {
let (Some(index), Some(column)) = (output_column_index, output_column) else {
return Ok(());
};
let index = index as usize;
if index != schema.columns.len() {
return Err(ExecutorError::ImplementationDefined {
detail: "mutation output column index is not append position",
});
}
schema.columns.push(column.clone());
Ok(())
}
pub(super) fn set_output_value(values: &mut SmallVec<[Value; 8]>, column_index: u32, value: Value) {
let column_index = column_index as usize;
values.resize(column_index + 1, Value::Null);
values[column_index] = value;
}
pub(super) fn record_insert_site(
sites: &mut SmallVec<[(crate::InsertSiteId, NodeId); 4]>,
site_id: crate::InsertSiteId,
node_id: NodeId,
) {
if let Some((_, existing)) = sites.iter_mut().find(|(site, _)| *site == site_id) {
*existing = node_id;
} else {
sites.push((site_id, node_id));
}
}
pub(super) fn label_diff(
added: impl IntoIterator<Item = DbString>,
removed: impl IntoIterator<Item = DbString>,
span: SourceSpan,
) -> Result<LabelDiff, ExecutorError> {
LabelDiff::new(added, removed).map_err(|source| {
ExecutorError::data_exception(
DataExceptionSubclass::DataException,
format!("label diff construction failed: {source}"),
span,
)
})
}
pub(super) fn property_diff(
set: impl IntoIterator<Item = (DbString, Value)>,
removed: impl IntoIterator<Item = DbString>,
span: SourceSpan,
) -> Result<PropertyDiff, ExecutorError> {
PropertyDiff::new(set, removed).map_err(|source| {
ExecutorError::data_exception(
DataExceptionSubclass::MultipleAssignmentsToGraphElementProperty,
format!("property diff construction failed: {source}"),
span,
)
})
}
pub(super) fn graph_mutation(source: selene_graph::GraphError, span: SourceSpan) -> ExecutorError {
ExecutorError::GraphMutation { source, span }
}
pub(super) fn unsupported_target(element: ElementKind) -> ExecutorError {
match element {
ElementKind::Path => ExecutorError::ImplementationDefined {
detail: "path mutation target not implemented",
},
ElementKind::Alias => ExecutorError::ImplementationDefined {
detail: "alias mutation target not supported",
},
ElementKind::Node | ElementKind::Edge => ExecutorError::ImplementationDefined {
detail: "mutation target element kind not supported",
},
}
}