pub use super::plan::ValueKinds;
use super::resources::{MemoryError, QueryArena, QueryReservation};
use super::runtime::{RuntimeContext, RuntimeError, WorkCounters, WorkKind};
use super::{QueryError, QueryView};
use crate::property_graph::{EntityId, GraphGeneration};
mod records;
pub use records::*;
#[cfg(feature = "graph-cypher")]
pub(super) mod native;
#[cfg(feature = "graph-cypher")]
#[doc(hidden)]
pub use native::{
Executed, GraphBoundary, GraphQuery, GraphQueryError, GraphQueryErrorKind, GraphQueryExecutor,
GraphQueryOptions,
};
#[cfg(feature = "graph-cypher")]
pub(crate) use native::{GraphQueryCause, native_graph_error_kind};
#[cfg(all(feature = "graph-cypher", test))]
pub(crate) use native::{execute_native_mutation_result, execute_native_result};
mod validate;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct Span {
pub start: u32,
pub len: u32,
}
impl Span {
pub const fn new(start: u32, len: u32) -> Self {
Self { start, len }
}
fn range(self) -> Option<std::ops::Range<usize>> {
let start = usize::try_from(self.start).ok()?;
Some(start..start.checked_add(usize::try_from(self.len).ok()?)?)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ValueIndex(pub u32);
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Value {
Null,
Bool(bool),
I64(i64),
F64(u64),
String(Span),
Node(u32),
Relationship(u32),
List {
children: Span,
element: ListKind,
},
Map {
entries: Span,
},
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Column {
pub name: Span,
pub kinds: ValueKinds,
}
#[derive(Clone, Copy, Default)]
pub struct Pools<'a> {
pub values: &'a [Value],
pub bytes: &'a [u8],
pub columns: &'a [Column],
pub cells: &'a [ValueIndex],
pub children: &'a [ValueIndex],
pub names: &'a [Span],
pub properties: &'a [Property],
pub nodes: &'a [Node],
pub relationships: &'a [Relationship],
pub vectors: &'a [u32],
pub reports: &'a [SearchReport],
pub receipts: &'a [Receipt],
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Outcome {
Read,
Committed {
changed: GraphGeneration,
},
Replayed,
NoOp,
}
#[derive(Clone, Copy)]
pub struct ResultInput<'a> {
pub view: &'a QueryView,
pub pools: Pools<'a>,
pub rows: u32,
pub outcome: Outcome,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SourceError {
Missing(EntityId),
Deleted(EntityId),
ForeignView,
Storage,
}
pub trait ResultSource {
fn result_input(&self) -> Result<ResultInput<'_>, SourceError>;
}
#[derive(Debug)]
pub enum CompletedError {
Source(SourceError),
Runtime(RuntimeError),
Shape,
Utf8,
Limit,
}
impl From<RuntimeError> for CompletedError {
fn from(e: RuntimeError) -> Self {
Self::Runtime(e)
}
}
impl From<MemoryError> for CompletedError {
fn from(e: MemoryError) -> Self {
Self::Runtime(e.into())
}
}
impl From<QueryError> for CompletedError {
fn from(e: QueryError) -> Self {
Self::Runtime(e.into())
}
}
#[derive(Clone, Copy, Debug)]
pub struct Metadata {
pub generation: GraphGeneration,
pub outcome: Outcome,
pub rows: u32,
pub counters: WorkCounters,
pub peak_query_bytes: usize,
}
pub(super) trait OwnedElement: Copy + 'static {}
impl OwnedElement for u8 {}
impl OwnedElement for Value {}
impl OwnedElement for Column {}
impl OwnedElement for ValueIndex {}
impl OwnedElement for Span {}
impl OwnedElement for Property {}
impl OwnedElement for Node {}
impl OwnedElement for Relationship {}
impl OwnedElement for u32 {}
impl OwnedElement for SearchReport {}
impl OwnedElement for Receipt {}
pub struct PreparedGraphResult<'m, 'g> {
values: QueryArena<'m, 'g, Value>,
bytes: QueryArena<'m, 'g, u8>,
columns: QueryArena<'m, 'g, Column>,
cells: QueryArena<'m, 'g, ValueIndex>,
children: QueryArena<'m, 'g, ValueIndex>,
names: QueryArena<'m, 'g, Span>,
properties: QueryArena<'m, 'g, Property>,
nodes: QueryArena<'m, 'g, Node>,
relationships: QueryArena<'m, 'g, Relationship>,
vectors: QueryArena<'m, 'g, u32>,
reports: QueryArena<'m, 'g, SearchReport>,
receipts: QueryArena<'m, 'g, Receipt>,
metadata: Metadata,
represented: usize,
_control: QueryReservation<'m, 'g>,
}
pub struct CompletedGraphResult {
values: Vec<Value>,
bytes: Vec<u8>,
columns: Vec<Column>,
cells: Vec<ValueIndex>,
children: Vec<ValueIndex>,
names: Vec<Span>,
properties: Vec<Property>,
nodes: Vec<Node>,
relationships: Vec<Relationship>,
vectors: Vec<u32>,
reports: Vec<SearchReport>,
receipts: Vec<Receipt>,
metadata: Metadata,
}
fn text(pools: Pools<'_>, span: Span) -> Result<&str, CompletedError> {
let bytes = pools
.bytes
.get(span.range().ok_or(CompletedError::Shape)?)
.ok_or(CompletedError::Shape)?;
std::str::from_utf8(bytes).map_err(|_| CompletedError::Utf8)
}
fn copy<'m, 'g, T: OwnedElement>(
input: &[T],
context: &mut RuntimeContext<'_, 'm, 'g>,
) -> Result<QueryArena<'m, 'g, T>, CompletedError> {
let mut arena = QueryArena::new(context.memory(), input.len())?;
for part in input.chunks((65536 / std::mem::size_of::<T>()).max(1)) {
context.charge(WorkKind::CopiedBytes, std::mem::size_of_val(part) as u64)?;
arena.extend_copy(part)?;
}
Ok(arena)
}
impl<'m, 'g> PreparedGraphResult<'m, 'g> {
pub fn copy_from(
source: &(impl ResultSource + ?Sized),
context: &mut RuntimeContext<'_, 'm, 'g>,
) -> Result<Self, CompletedError> {
context.checkpoint()?;
let input = source.result_input().map_err(CompletedError::Source)?;
if !std::ptr::eq(input.view, context.view()) {
return Err(CompletedError::Source(SourceError::ForeignView));
}
let pools = input.pools;
if input.rows > 65536
|| pools.columns.len() > super::plan::MAX_COLUMNS
|| usize::try_from(input.rows)
.ok()
.and_then(|n| n.checked_mul(pools.columns.len()))
!= Some(pools.cells.len())
{
return Err(CompletedError::Shape);
}
let represented = [
std::mem::size_of::<CompletedGraphResult>(),
std::mem::size_of_val(pools.values),
std::mem::size_of_val(pools.bytes),
std::mem::size_of_val(pools.columns),
std::mem::size_of_val(pools.cells),
std::mem::size_of_val(pools.children),
std::mem::size_of_val(pools.names),
std::mem::size_of_val(pools.properties),
std::mem::size_of_val(pools.nodes),
std::mem::size_of_val(pools.relationships),
std::mem::size_of_val(pools.vectors),
std::mem::size_of_val(pools.reports),
std::mem::size_of_val(pools.receipts),
]
.into_iter()
.try_fold(0_usize, |n, b| n.checked_add(b))
.ok_or(CompletedError::Limit)?;
if represented > 4 * 1024 * 1024 {
return Err(CompletedError::Limit);
}
let control = context.memory().reserve(std::mem::size_of::<Self>())?;
validate::validate(pools, input.outcome, context)?;
Ok(Self {
values: copy(pools.values, context)?,
bytes: copy(pools.bytes, context)?,
columns: copy(pools.columns, context)?,
cells: copy(pools.cells, context)?,
children: copy(pools.children, context)?,
names: copy(pools.names, context)?,
properties: copy(pools.properties, context)?,
nodes: copy(pools.nodes, context)?,
relationships: copy(pools.relationships, context)?,
vectors: copy(pools.vectors, context)?,
reports: copy(pools.reports, context)?,
receipts: copy(pools.receipts, context)?,
metadata: Metadata {
generation: input.view.generation(),
outcome: input.outcome,
rows: input.rows,
counters: WorkCounters::default(),
peak_query_bytes: 0,
},
represented,
_control: control,
})
}
pub fn pools(&self) -> Pools<'_> {
Pools {
values: self.values.as_slice(),
bytes: self.bytes.as_slice(),
columns: self.columns.as_slice(),
cells: self.cells.as_slice(),
children: self.children.as_slice(),
names: self.names.as_slice(),
properties: self.properties.as_slice(),
nodes: self.nodes.as_slice(),
relationships: self.relationships.as_slice(),
vectors: self.vectors.as_slice(),
reports: self.reports.as_slice(),
receipts: self.receipts.as_slice(),
}
}
pub const fn represented_bytes(&self) -> usize {
self.represented
}
pub const fn metadata(&self) -> &Metadata {
&self.metadata
}
pub fn detach(self, counters: WorkCounters, peak_query_bytes: usize) -> CompletedGraphResult {
CompletedGraphResult {
values: self.values.detach_owned(),
bytes: self.bytes.detach_owned(),
columns: self.columns.detach_owned(),
cells: self.cells.detach_owned(),
children: self.children.detach_owned(),
names: self.names.detach_owned(),
properties: self.properties.detach_owned(),
nodes: self.nodes.detach_owned(),
relationships: self.relationships.detach_owned(),
vectors: self.vectors.detach_owned(),
reports: self.reports.detach_owned(),
receipts: self.receipts.detach_owned(),
metadata: Metadata {
counters,
peak_query_bytes,
..self.metadata
},
}
}
}
#[cfg(feature = "graph-cypher")]
pub(crate) struct UnsettledWriteResult(CompletedGraphResult);
#[cfg(feature = "graph-cypher")]
impl UnsettledWriteResult {
pub(crate) fn settle(
self,
receipts: &[crate::property_graph::staging::ItemReceipt],
changed: Option<GraphGeneration>,
) -> CompletedGraphResult {
let mut result = self.0;
let admitted = result.metadata.generation;
for receipt in receipts {
let generation = match changed {
Some(changed) if receipt.generation > admitted => changed,
_ => receipt.generation,
};
match receipt.entity {
EntityId::Node(id) => {
if let Ok(index) = result.nodes.binary_search_by(|node| node.id.cmp(&id))
&& let Some(node) = result.nodes.get_mut(index)
{
node.revision = receipt.revision;
node.generation = generation;
}
}
EntityId::Relationship(id) => {
if let Ok(index) = result
.relationships
.binary_search_by(|relationship| relationship.id.cmp(&id))
&& let Some(relationship) = result.relationships.get_mut(index)
{
relationship.revision = receipt.revision;
relationship.generation = generation;
}
}
}
}
result.metadata.outcome = match changed {
Some(changed) => Outcome::Committed { changed },
None => Outcome::NoOp,
};
result
}
}
impl CompletedGraphResult {
pub fn pools(&self) -> Pools<'_> {
Pools {
values: &self.values,
bytes: &self.bytes,
columns: &self.columns,
cells: &self.cells,
children: &self.children,
names: &self.names,
properties: &self.properties,
nodes: &self.nodes,
relationships: &self.relationships,
vectors: &self.vectors,
reports: &self.reports,
receipts: &self.receipts,
}
}
pub const fn metadata(&self) -> &Metadata {
&self.metadata
}
pub fn cell(&self, row: usize, column: usize) -> Option<&Value> {
if row >= self.metadata.rows as usize || column >= self.columns.len() {
return None;
}
let cell = self
.cells
.get(row.checked_mul(self.columns.len())?.checked_add(column)?)?;
self.values.get(cell.0 as usize)
}
pub fn string(&self, span: Span) -> Option<&str> {
text(self.pools(), span).ok()
}
}