1use std::collections::HashMap;
7use std::sync::Arc;
8
9use linkrs_core::types::expr::contextual::ContextualExpression;
10use linkrs_core::types::semantic::AliasType;
11
12use linkrs_core::types::Span;
13
14#[derive(Debug, Clone)]
19pub struct ValidatedStatement {
20 pub ast: Arc<crate::parser::ast::stmt::Ast>,
21 pub validation_info: ValidationInfo,
22}
23
24impl ValidatedStatement {
25 pub fn new(ast: Arc<crate::parser::ast::stmt::Ast>, validation_info: ValidationInfo) -> Self {
26 Self {
27 ast,
28 validation_info,
29 }
30 }
31
32 pub fn stmt(&self) -> &crate::parser::ast::Stmt {
33 &self.ast.stmt
34 }
35
36 pub fn statement_type(&self) -> &'static str {
37 self.ast.stmt.kind()
38 }
39
40 pub fn alias_map(&self) -> &HashMap<String, AliasType> {
41 &self.validation_info.alias_map
42 }
43
44 pub fn expr_context(
45 &self,
46 ) -> &Arc<linkrs_core::types::expr::expression_context::ExpressionAnalysisContext> {
47 &self.ast.expr_context
48 }
49}
50
51#[derive(Debug, Clone, Default)]
53pub struct ValidationInfo {
54 pub alias_map: HashMap<String, AliasType>,
55 pub path_analysis: Vec<PathAnalysis>,
56 pub optimization_hints: Vec<OptimizationHint>,
57 pub variable_definitions: HashMap<String, Span>,
58 pub validated_clauses: Vec<ClauseKind>,
59 pub semantic_info: SemanticInfo,
60}
61
62impl ValidationInfo {
63 pub fn new() -> Self {
64 Self::default()
65 }
66
67 pub fn add_alias(&mut self, name: String, alias_type: AliasType) {
68 self.alias_map.insert(name, alias_type);
69 }
70
71 pub fn add_path_analysis(&mut self, analysis: PathAnalysis) {
72 self.path_analysis.push(analysis);
73 }
74
75 pub fn add_optimization_hint(&mut self, hint: OptimizationHint) {
76 self.optimization_hints.push(hint);
77 }
78
79 pub fn get_alias_type(&self, name: &str) -> Option<&AliasType> {
80 self.alias_map.get(name)
81 }
82
83 pub fn is_node_variable(&self, name: &str) -> bool {
84 matches!(
85 self.alias_map.get(name),
86 Some(AliasType::Node) | Some(AliasType::NodeList)
87 )
88 }
89
90 pub fn is_edge_variable(&self, name: &str) -> bool {
91 matches!(
92 self.alias_map.get(name),
93 Some(AliasType::Edge) | Some(AliasType::EdgeList)
94 )
95 }
96}
97
98#[derive(Debug, Clone)]
99pub struct PathAnalysis {
100 pub alias: Option<String>,
101 pub node_count: usize,
102 pub edge_count: usize,
103 pub has_direction: bool,
104 pub min_hops: Option<usize>,
105 pub max_hops: Option<usize>,
106 pub variables: Vec<String>,
107 pub labels: Vec<String>,
108 pub edge_types: Vec<String>,
109}
110
111impl PathAnalysis {
112 pub fn new() -> Self {
113 Self {
114 alias: None,
115 node_count: 0,
116 edge_count: 0,
117 has_direction: true,
118 min_hops: None,
119 max_hops: None,
120 variables: Vec::new(),
121 labels: Vec::new(),
122 edge_types: Vec::new(),
123 }
124 }
125}
126
127impl Default for PathAnalysis {
128 fn default() -> Self {
129 Self::new()
130 }
131}
132
133#[derive(Debug, Clone)]
134pub enum OptimizationHint {
135 UseIndexScan {
136 table: String,
137 column: String,
138 condition: ContextualExpression,
139 },
140 LimitResults {
141 reason: String,
142 suggested_limit: usize,
143 },
144 PreFilter {
145 condition: ContextualExpression,
146 selectivity: f64,
147 },
148 JoinOrder {
149 optimal_order: Vec<String>,
150 estimated_cost: f64,
151 },
152 PerformanceWarning {
153 message: String,
154 severity: HintSeverity,
155 },
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159pub enum HintSeverity {
160 Info,
161 Warning,
162 Critical,
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
166pub enum ClauseKind {
167 Match,
168 Where,
169 Return,
170 OrderBy,
171 Limit,
172 Skip,
173 With,
174 Unwind,
175 Create,
176 Delete,
177 Set,
178 Remove,
179 Yield,
180 Go,
181 Over,
182 From,
183}
184
185#[derive(Debug, Clone, Default)]
186pub struct SemanticInfo {
187 pub referenced_tags: Vec<String>,
188 pub referenced_edges: Vec<String>,
189 pub referenced_properties: Vec<String>,
190 pub used_variables: Vec<String>,
191 pub defined_variables: Vec<String>,
192 pub aggregate_calls: Vec<AggregateCallInfo>,
193 pub output_fields: Vec<String>,
194 pub ordering_fields: Vec<String>,
195 pub pagination_offset: Option<usize>,
196 pub pagination_limit: Option<usize>,
197 pub query_type: Option<String>,
198 pub query_complexity: Option<usize>,
199 pub space_name: Option<String>,
200 pub referenced_schemas: Vec<String>,
201}
202
203#[derive(Debug, Clone)]
204pub struct AggregateCallInfo {
205 pub function_name: String,
206 pub arguments: Vec<ContextualExpression>,
207 pub distinct: bool,
208 pub alias: Option<String>,
209}
210
211#[derive(Debug, Clone, PartialEq, Eq, Copy, Hash)]
213pub enum CypherClauseKind {
214 Match,
215 Where,
216 Return,
217 With,
218 Unwind,
219 Yield,
220 OrderBy,
221 Pagination,
222}
223
224impl CypherClauseKind {
225 pub fn as_str(&self) -> &'static str {
226 match self {
227 Self::Match => "MATCH",
228 Self::Where => "WHERE",
229 Self::Return => "RETURN",
230 Self::With => "WITH",
231 Self::Unwind => "UNWIND",
232 Self::Yield => "YIELD",
233 Self::OrderBy => "ORDER BY",
234 Self::Pagination => "PAGINATION",
235 }
236 }
237}
238
239use linkrs_core::types::OrderDirection;
242use linkrs_core::YieldColumn;
243
244#[derive(Debug, Clone)]
245pub struct WhereClauseContext {
246 pub filter: Option<ContextualExpression>,
247 pub aliases_available: HashMap<String, AliasType>,
248 pub aliases_generated: HashMap<String, AliasType>,
249}
250
251#[derive(Debug, Clone)]
252pub struct OrderByClauseContext {
253 pub indexed_order_factors: Vec<(usize, OrderDirection)>,
254}
255
256#[derive(Debug, Clone)]
257pub struct PaginationContext {
258 pub skip: i64,
259 pub limit: i64,
260}
261
262#[derive(Debug, Clone)]
263pub struct YieldClauseContext {
264 pub yield_columns: Vec<YieldColumn>,
265 pub aliases_available: HashMap<String, AliasType>,
266 pub aliases_generated: HashMap<String, AliasType>,
267 pub distinct: bool,
268 pub has_agg: bool,
269 pub group_keys: Vec<ContextualExpression>,
270 pub group_items: Vec<ContextualExpression>,
271 pub need_gen_project: bool,
272 pub agg_output_column_names: Vec<String>,
273 pub proj_output_column_names: Vec<String>,
274 pub filter_condition: Option<ContextualExpression>,
275 pub skip: Option<usize>,
276 pub limit: Option<usize>,
277}
278
279#[derive(Debug, Clone)]
280pub struct WithClauseContext {
281 pub yield_clause: YieldClauseContext,
282 pub aliases_available: HashMap<String, AliasType>,
283 pub aliases_generated: HashMap<String, AliasType>,
284 pub where_clause: Option<WhereClauseContext>,
285 pub pagination: Option<PaginationContext>,
286 pub order_by: Option<OrderByClauseContext>,
287 pub distinct: bool,
288}