selene_gql/analyze/write_set.rs
1//! Mutation write-set enumeration.
2
3use selene_core::DbString;
4
5use crate::{
6 DeleteMode, LabelExpr, SourceSpan, ValueExpr,
7 analyze::binding::{BindingDeclKind, BindingId},
8};
9
10/// Enumerated per-pipeline summary of writes produced by a mutation statement.
11#[derive(Clone, Debug, Default, Eq, PartialEq)]
12pub struct MutationWriteSet {
13 /// Each source write site emits one entry.
14 pub entries: Vec<WriteSetEntry>,
15}
16
17impl MutationWriteSet {
18 pub(crate) fn push(&mut self, entry: WriteSetEntry) {
19 self.entries.push(entry);
20 }
21}
22
23/// One write site in a mutation pipeline.
24#[derive(Clone, Debug, Eq, PartialEq)]
25pub struct WriteSetEntry {
26 /// Zero-based mutation-statement index within the owning pipeline.
27 pub statement_index: usize,
28 /// Source span of the write site.
29 pub span: SourceSpan,
30 /// Kind of write.
31 pub kind: WriteKind,
32}
33
34/// Structural write operation recorded by the analyzer.
35#[derive(Clone, Debug, Eq, PartialEq)]
36#[non_exhaustive]
37pub enum WriteKind {
38 /// `INSERT` introduces a node. Anonymous inserts carry no binding.
39 InsertNode {
40 /// Inserted-node binding, when the pattern names one.
41 binding: Option<BindingId>,
42 /// Static label expression declared on the pattern.
43 label_expr: Option<LabelExpr>,
44 /// Property keys written by the inline property map.
45 property_keys: Box<[DbString]>,
46 },
47 /// `INSERT` introduces an edge. Anonymous inserts carry no binding.
48 InsertEdge {
49 /// Inserted-edge binding, when the pattern names one.
50 binding: Option<BindingId>,
51 /// Static label expression declared on the pattern.
52 label_expr: Option<LabelExpr>,
53 /// Property keys written by the inline property map.
54 property_keys: Box<[DbString]>,
55 },
56 /// `SET n.key = expr` or `SET n = { key: expr }`.
57 SetProperty {
58 /// Resolved target binding.
59 target: BindingId,
60 /// Element kind of the resolved target.
61 element: ElementKind,
62 /// Property key being written.
63 key: DbString,
64 /// Source span of the value expression for this specific write.
65 value_span: SourceSpan,
66 },
67 /// `SET n :Label`.
68 SetLabel {
69 /// Resolved target binding.
70 target: BindingId,
71 /// Element kind of the resolved target.
72 element: ElementKind,
73 /// Label being added.
74 label: DbString,
75 },
76 /// `REMOVE n.key`.
77 RemoveProperty {
78 /// Resolved target binding.
79 target: BindingId,
80 /// Element kind of the resolved target.
81 element: ElementKind,
82 /// Property key being removed.
83 key: DbString,
84 },
85 /// `REMOVE n :Label`.
86 RemoveLabel {
87 /// Resolved target binding.
88 target: BindingId,
89 /// Element kind of the resolved target.
90 element: ElementKind,
91 /// Label being removed.
92 label: DbString,
93 },
94 /// `DELETE n` / `DETACH DELETE n` / `NODETACH DELETE n`.
95 DeleteTarget {
96 /// Resolved target binding.
97 target: BindingId,
98 /// Element kind of the resolved target.
99 element: ElementKind,
100 /// Delete mode requested by syntax.
101 mode: DeleteMode,
102 },
103}
104
105/// Element kind of a resolved write target.
106#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
107pub enum ElementKind {
108 /// Node binding.
109 Node,
110 /// Edge binding.
111 Edge,
112 /// Path binding.
113 Path,
114 /// Value alias or procedure output column.
115 Alias,
116}
117
118impl ElementKind {
119 /// Convert a binding declaration kind to its write-target element kind.
120 #[must_use]
121 pub const fn from_decl_kind(kind: BindingDeclKind) -> Self {
122 match kind {
123 BindingDeclKind::NodePattern | BindingDeclKind::InsertNode => Self::Node,
124 BindingDeclKind::EdgePattern | BindingDeclKind::InsertEdge => Self::Edge,
125 BindingDeclKind::PathBinding => Self::Path,
126 BindingDeclKind::LetAlias
127 | BindingDeclKind::ForAlias
128 | BindingDeclKind::ProjectionAlias
129 | BindingDeclKind::YieldColumn => Self::Alias,
130 }
131 }
132}
133
134pub(crate) fn property_keys(properties: &[(DbString, ValueExpr)]) -> Box<[DbString]> {
135 properties
136 .iter()
137 .map(|(key, _)| key.clone())
138 .collect::<Vec<_>>()
139 .into_boxed_slice()
140}