selene-db-gql 1.3.0

ISO/IEC 39075:2024 GQL parser, planner, optimizer, and executor for selene-db.
Documentation
//! Mutation statement AST nodes.

use selene_core::DbString;

use crate::ast::{
    expr::ValueExpr,
    pattern::{GraphPattern, MatchClause},
    span::SourceSpan,
    statement::ReturnClause,
    util::NonEmpty,
};

/// Write-side mutation pipeline.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct MutationPipeline {
    /// Ordered read and mutation statements; type-enforced non-empty because
    /// the grammar requires at least one mutation operation.
    pub statements: NonEmpty<MutationStatement>,
    /// Optional `RETURN` or `FINISH` terminator.
    pub terminator: Option<MutationTerminator>,
    /// Source span.
    pub span: SourceSpan,
}

/// One statement inside a mutation pipeline.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
#[non_exhaustive]
pub enum MutationStatement {
    /// `MATCH`.
    Match(MatchClause),
    /// `FILTER`.
    Filter(ValueExpr),
    /// `INSERT`.
    Insert(InsertStatement),
    /// `SET`.
    Set(Vec<SetItem>),
    /// `REMOVE`.
    Remove(Vec<RemoveItem>),
    /// `DELETE` / `DETACH DELETE` / `NODETACH DELETE`.
    Delete(DeleteStatement),
}

impl MutationStatement {
    /// Return this statement's source span.
    #[must_use]
    pub fn span(&self) -> SourceSpan {
        match self {
            Self::Match(value) => value.span,
            Self::Filter(value) => value.span(),
            Self::Insert(value) => value.span,
            Self::Set(values) => span_from_iter(values.iter().map(SetItem::span)),
            Self::Remove(values) => span_from_iter(values.iter().map(RemoveItem::span)),
            Self::Delete(value) => value.span,
        }
    }
}

/// Mutation pipeline terminator.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
#[non_exhaustive]
pub enum MutationTerminator {
    /// `RETURN`.
    Return(ReturnClause),
    /// `FINISH`.
    Finish(SourceSpan),
}

impl MutationTerminator {
    /// Return this terminator's source span.
    #[must_use]
    pub const fn span(&self) -> SourceSpan {
        match self {
            Self::Return(value) => value.span,
            Self::Finish(span) => *span,
        }
    }
}

/// `INSERT` graph pattern.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct InsertStatement {
    /// Inserted graph patterns.
    pub patterns: Vec<GraphPattern>,
    /// Source span.
    pub span: SourceSpan,
}

/// One `SET` item.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
#[non_exhaustive]
pub enum SetItem {
    /// `SET n.prop = expr`.
    Property {
        /// Bound target variable.
        target: DbString,
        /// Property key.
        key: DbString,
        /// New value.
        value: ValueExpr,
        /// Source span.
        span: SourceSpan,
    },
    /// `SET n = {k: v}`.
    PropertyMerge {
        /// Bound target variable.
        target: DbString,
        /// Replacement/merge properties.
        properties: Vec<(DbString, ValueExpr)>,
        /// Source span.
        span: SourceSpan,
    },
    /// `SET n :Label` / `SET n IS Label`.
    Label {
        /// Bound target variable.
        target: DbString,
        /// Label name.
        label: DbString,
        /// Source span.
        span: SourceSpan,
    },
}

impl SetItem {
    /// Return this item's source span.
    #[must_use]
    pub const fn span(&self) -> SourceSpan {
        match self {
            Self::Property { span, .. }
            | Self::PropertyMerge { span, .. }
            | Self::Label { span, .. } => *span,
        }
    }
}

/// One `REMOVE` item.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
#[non_exhaustive]
pub enum RemoveItem {
    /// `REMOVE n.prop`.
    Property {
        /// Bound target variable.
        target: DbString,
        /// Property key.
        key: DbString,
        /// Source span.
        span: SourceSpan,
    },
    /// `REMOVE n :Label` / `REMOVE n IS Label`.
    Label {
        /// Bound target variable.
        target: DbString,
        /// Label name.
        label: DbString,
        /// Source span.
        span: SourceSpan,
    },
}

impl RemoveItem {
    /// Return this item's source span.
    #[must_use]
    pub const fn span(&self) -> SourceSpan {
        match self {
            Self::Property { span, .. } | Self::Label { span, .. } => *span,
        }
    }
}

/// `DELETE` statement.
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)]
pub struct DeleteStatement {
    /// Delete mode.
    pub mode: DeleteMode,
    /// Bound variables to delete.
    pub items: Vec<DbString>,
    /// Source span.
    pub span: SourceSpan,
}

/// Delete behavior requested by syntax.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, serde::Deserialize, serde::Serialize)]
pub enum DeleteMode {
    /// Bare `DELETE`.
    Bare,
    /// `DETACH DELETE`.
    Detach,
    /// `NODETACH DELETE`.
    NoDetach,
}

fn span_from_iter(mut spans: impl Iterator<Item = SourceSpan>) -> SourceSpan {
    let Some(first) = spans.next() else {
        return SourceSpan::default();
    };
    spans.fold(first, SourceSpan::merge)
}