Skip to main content

linkrs_query/planning/
planner.rs

1//! Planner registration mechanism
2//! Implement static registration using type-safe enumerations to completely eliminate dynamic distribution.
3//!
4//! # Explanation of the reconstruction process
5//!
6//! This module has been completely restructured, and the old mechanism for matching SentenceKind strings has been removed.
7//! Now, use the direct enumeration mode to match the planner created from the Stmt.
8
9use std::sync::Arc;
10
11use crate::binder::BoundStatement;
12use crate::parser::ast::Stmt;
13use crate::planning::context::PlanContext;
14use crate::planning::plan::SubPlan;
15use crate::QueryContext;
16
17// Re-exported for planner implementations that still reference it.
18pub use crate::binder::validation::ValidatedStatement;
19
20use crate::planning::fulltext_planner::FulltextSearchPlanner;
21use crate::planning::statements::ddl::maintain_planner::MaintainPlanner;
22use crate::planning::statements::ddl::use_planner::UsePlanner;
23use crate::planning::statements::ddl::user_management_planner::UserManagementPlanner;
24use crate::planning::statements::dml::assignment_planner::AssignmentPlanner;
25use crate::planning::statements::dml::copy_planner::CopyPlanner;
26use crate::planning::statements::dml::delete_planner::DeletePlanner;
27use crate::planning::statements::dml::insert_planner::InsertPlanner;
28use crate::planning::statements::dml::merge_planner::MergePlanner;
29use crate::planning::statements::dml::remove_planner::RemovePlanner;
30use crate::planning::statements::dml::set_planner::SetPlanner;
31use crate::planning::statements::dml::update_planner::UpdatePlanner;
32use crate::planning::statements::dql::assign_variable_planner::AssignVariablePlanner;
33use crate::planning::statements::dql::collect_planner::CollectPlanner;
34use crate::planning::statements::dql::explain_planner::ExplainPlanner;
35use crate::planning::statements::dql::fetch_edges_planner::FetchEdgesPlanner;
36use crate::planning::statements::dql::fetch_vertices_planner::FetchVerticesPlanner;
37use crate::planning::statements::dql::filter_planner::FilterPlanner;
38use crate::planning::statements::dql::go_planner::GoPlanner;
39use crate::planning::statements::dql::group_by_planner::GroupByPlanner;
40use crate::planning::statements::dql::lookup_planner::LookupPlanner;
41use crate::planning::statements::dql::path_planner::PathPlanner;
42use crate::planning::statements::dql::pipe_planner::PipePlanner;
43use crate::planning::statements::dql::return_planner::ReturnPlanner;
44use crate::planning::statements::dql::set_operation_planner::SetOperationPlanner;
45use crate::planning::statements::dql::subgraph_planner::SubgraphPlanner;
46use crate::planning::statements::dql::unwind_planner::UnwindPlanner;
47use crate::planning::statements::dql::with_planner::WithPlanner;
48use crate::planning::statements::dql::yield_planner::YieldPlanner;
49use crate::planning::statements::match_statement_planner::MatchStatementPlanner;
50#[cfg(feature = "vector")]
51use crate::planning::vector_planner::VectorSearchPlanner;
52
53///  Planner Configuration
54#[derive(Debug, Clone)]
55pub struct PlannerConfig {
56    pub max_plan_depth: usize,
57    pub enable_parallel_planning: bool,
58    pub enable_rewrite: bool,
59}
60
61impl Default for PlannerConfig {
62    fn default() -> Self {
63        Self {
64            max_plan_depth: 100,
65            enable_parallel_planning: false,
66            enable_rewrite: true,
67        }
68    }
69}
70
71/// Match function type
72pub type MatchFunc = fn(&Stmt) -> bool;
73
74///  Planner Features
75///
76/// # Design Principles
77/// The `transform` method accepts an `Arc<QueryContext>` and a `&ValidatedStatement`.
78/// The `match_planner` method receives an `&Stmt` object, which is used for matching and making judgments.
79pub trait Planner: std::fmt::Debug {
80    /// Translate the verified statement into a sub-plan.
81    ///
82    /// # Parameters
83    /// `validated`: A verified statement that contains ValidationInfo and Ast.
84    /// `qctx`: Query context
85    fn transform(
86        &mut self,
87        validated: &ValidatedStatement,
88        qctx: Arc<QueryContext>,
89    ) -> Result<SubPlan, PlannerError>;
90
91    /// Check whether this planner can handle the given sentence.
92    fn match_planner(&self, stmt: &Stmt) -> bool;
93
94    /// Translate a bound statement directly into a plan sub-tree.
95    ///
96    /// The unified [`PlanContext`] bundles all inputs needed during
97    /// bound-statement planning (bound statement, query context, metadata,
98    /// validated statement). The default implementation returns
99    /// `UnsupportedOperation` so that planners which have not yet been
100    /// migrated to the bound pipeline continue to work unchanged.
101    fn plan_bound(&mut self, _ctx: &PlanContext<'_>) -> Result<SubPlan, PlannerError> {
102        Err(PlannerError::UnsupportedOperation(
103            "plan_bound not yet implemented for this planner".to_string(),
104        ))
105    }
106
107    fn name(&self) -> &'static str {
108        std::any::type_name::<Self>()
109    }
110}
111
112// ============================================================================
113// Implementation of static registration – complete elimination of dynamic distribution
114// ============================================================================
115
116/// Planner Enumeration – Core for Static Distribution
117/// Eliminate dynamic distribution completely and use compile-time polymorphism instead.
118#[derive(Debug, Clone)]
119pub enum PlannerEnum {
120    Match(Box<MatchStatementPlanner>),
121    Go(GoPlanner),
122    Lookup(LookupPlanner),
123    Path(PathPlanner),
124    Subgraph(SubgraphPlanner),
125    FetchVertices(FetchVerticesPlanner),
126    FetchEdges(FetchEdgesPlanner),
127    Maintain(MaintainPlanner),
128    UserManagement(UserManagementPlanner),
129    Assignment(AssignmentPlanner),
130    Insert(InsertPlanner),
131    Copy(CopyPlanner),
132    Delete(DeletePlanner),
133    Update(UpdatePlanner),
134    Remove(RemovePlanner),
135    Set(SetPlanner),
136    Merge(MergePlanner),
137    GroupBy(GroupByPlanner),
138    Filter(FilterPlanner),
139    Collect(CollectPlanner),
140    SetOperation(SetOperationPlanner),
141    Use(UsePlanner),
142    Unwind(UnwindPlanner),
143    With(WithPlanner),
144    Return(ReturnPlanner),
145    AssignVariable(AssignVariablePlanner),
146    Yield(YieldPlanner),
147    Pipe(PipePlanner),
148    Explain(ExplainPlanner),
149    FulltextSearch(FulltextSearchPlanner),
150    #[cfg(feature = "vector")]
151    VectorSearch(VectorSearchPlanner),
152}
153
154impl PlannerEnum {
155    /// Create a planner directly from Arc<Stmt> (the recommended method).
156    /// Use the enumeration pattern for matching to completely eliminate the need for string matching.
157    pub fn from_stmt(stmt: &Arc<Stmt>) -> Option<Self> {
158        Self::from_stmt_ref(stmt.as_ref())
159    }
160
161    /// Create a planner directly from a statement reference.
162    /// This avoids cloning the whole statement tree when only dispatching.
163    pub fn from_stmt_ref(stmt: &Stmt) -> Option<Self> {
164        match stmt {
165            Stmt::Match(_) => Some(PlannerEnum::Match(Box::default())),
166            Stmt::Go(_) => Some(PlannerEnum::Go(GoPlanner::new())),
167            Stmt::Lookup(_) => Some(PlannerEnum::Lookup(LookupPlanner::new())),
168            Stmt::FindPath(_) => Some(PlannerEnum::Path(PathPlanner::new())),
169            Stmt::Subgraph(_) => Some(PlannerEnum::Subgraph(SubgraphPlanner::new())),
170            Stmt::Fetch(fetch_stmt) => match &fetch_stmt.target {
171                crate::parser::ast::FetchTarget::Vertices { .. } => {
172                    Some(PlannerEnum::FetchVertices(FetchVerticesPlanner::new()))
173                }
174                crate::parser::ast::FetchTarget::Edges { .. } => {
175                    Some(PlannerEnum::FetchEdges(FetchEdgesPlanner::new()))
176                }
177            },
178            Stmt::Insert(_) => Some(PlannerEnum::Insert(InsertPlanner::new())),
179            Stmt::Copy(_) => Some(PlannerEnum::Copy(CopyPlanner::new())),
180            Stmt::Delete(_) => Some(PlannerEnum::Delete(DeletePlanner::new())),
181            Stmt::Update(_) => Some(PlannerEnum::Update(UpdatePlanner::new())),
182            Stmt::Remove(_) => Some(PlannerEnum::Remove(RemovePlanner::new())),
183            Stmt::Set(_) => Some(PlannerEnum::Set(SetPlanner::new())),
184            Stmt::Merge(_) => Some(PlannerEnum::Merge(MergePlanner::new())),
185            Stmt::Assignment(_) => Some(PlannerEnum::Assignment(AssignmentPlanner::new())),
186            Stmt::GroupBy(_) => Some(PlannerEnum::GroupBy(GroupByPlanner::new())),
187            Stmt::Filter(_) => Some(PlannerEnum::Filter(FilterPlanner::new())),
188            Stmt::Collect(_) => Some(PlannerEnum::Collect(CollectPlanner::new())),
189            Stmt::SetOperation(_) => Some(PlannerEnum::SetOperation(SetOperationPlanner::new())),
190            Stmt::Use(_) => Some(PlannerEnum::Use(UsePlanner::new())),
191            Stmt::Unwind(_) => Some(PlannerEnum::Unwind(UnwindPlanner::new())),
192            Stmt::With(_) => Some(PlannerEnum::With(WithPlanner::new())),
193            Stmt::Return(_) => Some(PlannerEnum::Return(ReturnPlanner::new())),
194            Stmt::AssignVariable(_) => {
195                Some(PlannerEnum::AssignVariable(AssignVariablePlanner::new()))
196            }
197            Stmt::Yield(_) => Some(PlannerEnum::Yield(YieldPlanner::new())),
198            Stmt::Pipe(_) => Some(PlannerEnum::Pipe(PipePlanner::new())),
199            Stmt::Explain(_) => Some(PlannerEnum::Explain(ExplainPlanner::new())),
200            Stmt::Profile(_) => Some(PlannerEnum::Explain(ExplainPlanner::new_profile())),
201            // Full-text search statements
202            Stmt::CreateFulltextIndex(_)
203            | Stmt::DropFulltextIndex(_)
204            | Stmt::AlterFulltextIndex(_)
205            | Stmt::ShowFulltextIndex(_)
206            | Stmt::DescribeFulltextIndex(_)
207            | Stmt::Search(_)
208            | Stmt::LookupFulltext(_)
209            | Stmt::MatchFulltext(_) => {
210                Some(PlannerEnum::FulltextSearch(FulltextSearchPlanner::new()))
211            }
212            #[cfg(feature = "vector")]
213            Stmt::CreateVectorIndex(_)
214            | Stmt::DropVectorIndex(_)
215            | Stmt::SearchVector(_)
216            | Stmt::LookupVector(_)
217            | Stmt::MatchVector(_) => Some(PlannerEnum::VectorSearch(VectorSearchPlanner::new())),
218            Stmt::CreateUser(_)
219            | Stmt::DropUser(_)
220            | Stmt::AlterUser(_)
221            | Stmt::ChangePassword(_)
222            | Stmt::Grant(_)
223            | Stmt::Revoke(_)
224            | Stmt::DescribeUser(_)
225            | Stmt::ShowUsers(_)
226            | Stmt::ShowRoles(_) => Some(PlannerEnum::UserManagement(UserManagementPlanner::new())),
227            Stmt::Create(_)
228            | Stmt::Drop(_)
229            | Stmt::Show(_)
230            | Stmt::Desc(_)
231            | Stmt::Alter(_)
232            | Stmt::ShowCreate(_)
233            | Stmt::ShowSessions(_)
234            | Stmt::ShowQueries(_)
235            | Stmt::KillQuery(_)
236            | Stmt::ShowConfigs(_)
237            | Stmt::UpdateConfigs(_)
238            | Stmt::ClearSpace(_)
239            | Stmt::BeginTransaction(_)
240            | Stmt::CommitTransaction(_)
241            | Stmt::RollbackTransaction(_)
242            | Stmt::Savepoint(_)
243            | Stmt::ReleaseSavepoint(_)
244            | Stmt::Checkpoint(_)
245            | Stmt::PurgeExpired(_)
246            | Stmt::ExportDatabase(_)
247            | Stmt::ImportDatabase(_)
248            | Stmt::AttachDatabase(_)
249            | Stmt::DetachDatabase(_) => Some(PlannerEnum::Maintain(MaintainPlanner::new())),
250            // New reading clause statements — no dedicated planner yet; route
251            // through the MaintainPlanner fallback (returns UnsupportedOperation
252            // until a proper planner is implemented).
253            Stmt::LoadFrom(_) | Stmt::InQueryCall(_) => {
254                Some(PlannerEnum::Maintain(MaintainPlanner::new()))
255            }
256            // Catalog DDL statements are planned by the MaintainPlanner into
257            // MacroManage / TypeManage plan nodes.
258            Stmt::CreateMacro(_) | Stmt::DropMacro(_) | Stmt::CreateType(_) | Stmt::DropType(_) => {
259                Some(PlannerEnum::Maintain(MaintainPlanner::new()))
260            }
261            // The type of the following sentence does not currently support direct planning.
262            _ => None,
263        }
264    }
265
266    /// Create a planner from Arc<Ast>.
267    /// This is the new recommendation method; the context of the expressions is defined within Ast.
268    pub fn from_ast(ast: &Arc<crate::parser::ast::stmt::Ast>) -> Option<Self> {
269        Self::from_stmt_ref(&ast.stmt)
270    }
271
272    /// Convert the verified statement into an execution plan.
273    pub fn transform(
274        &mut self,
275        validated: &ValidatedStatement,
276        qctx: Arc<QueryContext>,
277    ) -> Result<SubPlan, PlannerError> {
278        match self {
279            PlannerEnum::Match(planner) => planner.transform(validated, qctx),
280            PlannerEnum::Go(planner) => planner.transform(validated, qctx),
281            PlannerEnum::Lookup(planner) => planner.transform(validated, qctx),
282            PlannerEnum::Path(planner) => planner.transform(validated, qctx),
283            PlannerEnum::Subgraph(planner) => planner.transform(validated, qctx),
284            PlannerEnum::FetchVertices(planner) => planner.transform(validated, qctx),
285            PlannerEnum::FetchEdges(planner) => planner.transform(validated, qctx),
286            PlannerEnum::Maintain(planner) => planner.transform(validated, qctx),
287            PlannerEnum::UserManagement(planner) => planner.transform(validated, qctx),
288            PlannerEnum::Assignment(planner) => planner.transform(validated, qctx),
289            PlannerEnum::Insert(planner) => planner.transform(validated, qctx),
290            PlannerEnum::Copy(planner) => planner.transform(validated, qctx),
291            PlannerEnum::Delete(planner) => planner.transform(validated, qctx),
292            PlannerEnum::Update(planner) => planner.transform(validated, qctx),
293            PlannerEnum::Remove(planner) => planner.transform(validated, qctx),
294            PlannerEnum::Set(planner) => planner.transform(validated, qctx),
295            PlannerEnum::Merge(planner) => planner.transform(validated, qctx),
296            PlannerEnum::GroupBy(planner) => planner.transform(validated, qctx),
297            PlannerEnum::Filter(planner) => planner.transform(validated, qctx),
298            PlannerEnum::Collect(planner) => planner.transform(validated, qctx),
299            PlannerEnum::SetOperation(planner) => planner.transform(validated, qctx),
300            PlannerEnum::Use(planner) => planner.transform(validated, qctx),
301            PlannerEnum::Unwind(planner) => planner.transform(validated, qctx),
302            PlannerEnum::With(planner) => planner.transform(validated, qctx),
303            PlannerEnum::Return(planner) => planner.transform(validated, qctx),
304            PlannerEnum::AssignVariable(planner) => planner.transform(validated, qctx),
305            PlannerEnum::Yield(planner) => planner.transform(validated, qctx),
306            PlannerEnum::Pipe(planner) => planner.transform(validated, qctx),
307            PlannerEnum::Explain(planner) => planner.transform(validated, qctx),
308            PlannerEnum::FulltextSearch(planner) => planner.transform(validated, qctx),
309            #[cfg(feature = "vector")]
310            PlannerEnum::VectorSearch(planner) => planner.transform(validated, qctx),
311        }
312    }
313
314    /// Create a planner from a BoundStatement.
315    pub fn from_bound_statement(bound: &BoundStatement) -> Option<Self> {
316        match bound {
317            BoundStatement::Match(_) => Some(PlannerEnum::Match(Box::default())),
318            BoundStatement::Go(_) => Some(PlannerEnum::Go(GoPlanner::new())),
319            BoundStatement::Lookup(_) => Some(PlannerEnum::Lookup(LookupPlanner::new())),
320            BoundStatement::FetchVertices(_) => {
321                Some(PlannerEnum::FetchVertices(FetchVerticesPlanner::new()))
322            }
323            BoundStatement::FetchEdges(_) => {
324                Some(PlannerEnum::FetchEdges(FetchEdgesPlanner::new()))
325            }
326            BoundStatement::FindPath(_) => Some(PlannerEnum::Path(PathPlanner::new())),
327            BoundStatement::Subgraph(_) => Some(PlannerEnum::Subgraph(SubgraphPlanner::new())),
328            BoundStatement::Return(_) => Some(PlannerEnum::Return(ReturnPlanner::new())),
329            BoundStatement::With(_) => Some(PlannerEnum::With(WithPlanner::new())),
330            BoundStatement::Use(_) => Some(PlannerEnum::Use(UsePlanner::new())),
331            BoundStatement::Unwind(_) => Some(PlannerEnum::Unwind(UnwindPlanner::new())),
332            BoundStatement::Pipe(_) => Some(PlannerEnum::Pipe(PipePlanner::new())),
333            BoundStatement::SetOperation(_) => {
334                Some(PlannerEnum::SetOperation(SetOperationPlanner::new()))
335            }
336            BoundStatement::GroupBy(_) => Some(PlannerEnum::GroupBy(GroupByPlanner::new())),
337            BoundStatement::Filter(_) => Some(PlannerEnum::Filter(FilterPlanner::new())),
338            BoundStatement::Yield(_) => Some(PlannerEnum::Yield(YieldPlanner::new())),
339            BoundStatement::Collect(_) => Some(PlannerEnum::Collect(CollectPlanner::new())),
340            BoundStatement::AssignVariable(_) => {
341                Some(PlannerEnum::AssignVariable(AssignVariablePlanner::new()))
342            }
343            BoundStatement::Insert(_) => Some(PlannerEnum::Insert(InsertPlanner::new())),
344            BoundStatement::Update(_) => Some(PlannerEnum::Update(UpdatePlanner::new())),
345            BoundStatement::Delete(_) => Some(PlannerEnum::Delete(DeletePlanner::new())),
346            BoundStatement::Merge(_) => Some(PlannerEnum::Merge(MergePlanner::new())),
347            BoundStatement::Set(_) => Some(PlannerEnum::Set(SetPlanner::new())),
348            BoundStatement::Remove(_) => Some(PlannerEnum::Remove(RemovePlanner::new())),
349            BoundStatement::Copy(_) => Some(PlannerEnum::Copy(CopyPlanner::new())),
350            BoundStatement::Drop(_) => Some(PlannerEnum::Maintain(MaintainPlanner::new())),
351            BoundStatement::Alter(_) => Some(PlannerEnum::Maintain(MaintainPlanner::new())),
352            BoundStatement::Show(_) => Some(PlannerEnum::Maintain(MaintainPlanner::new())),
353            BoundStatement::ShowCreate(_) => Some(PlannerEnum::Maintain(MaintainPlanner::new())),
354            BoundStatement::Desc(_) => Some(PlannerEnum::Maintain(MaintainPlanner::new())),
355            BoundStatement::ClearSpace(_) => Some(PlannerEnum::Maintain(MaintainPlanner::new())),
356            BoundStatement::CreateUser(_)
357            | BoundStatement::DropUser(_)
358            | BoundStatement::AlterUser(_) => {
359                Some(PlannerEnum::UserManagement(UserManagementPlanner::new()))
360            }
361            BoundStatement::CreateFulltextIndex(_) => {
362                Some(PlannerEnum::FulltextSearch(FulltextSearchPlanner::new()))
363            }
364            BoundStatement::CreateVectorIndex(_) => {
365                #[cfg(feature = "vector")]
366                {
367                    Some(PlannerEnum::VectorSearch(VectorSearchPlanner::new()))
368                }
369                #[cfg(not(feature = "vector"))]
370                {
371                    None
372                }
373            }
374            BoundStatement::Explain(_) => Some(PlannerEnum::Explain(ExplainPlanner::new())),
375            BoundStatement::Profile(_) => Some(PlannerEnum::Explain(ExplainPlanner::new_profile())),
376            BoundStatement::BeginTransaction(_)
377            | BoundStatement::Commit(_)
378            | BoundStatement::Rollback(_)
379            | BoundStatement::Savepoint(_)
380            | BoundStatement::ReleaseSavepoint(_) => {
381                Some(PlannerEnum::Maintain(MaintainPlanner::new()))
382            }
383            BoundStatement::Assignment(_) => {
384                Some(PlannerEnum::Assignment(AssignmentPlanner::new()))
385            }
386            BoundStatement::Other(stmt) => Self::from_stmt_ref(stmt.as_ref()),
387        }
388    }
389
390    /// Obtain the name of the planner.
391    pub fn name(&self) -> &'static str {
392        match self {
393            PlannerEnum::Match(_) => "MatchPlanner",
394            PlannerEnum::Go(_) => "GoPlanner",
395            PlannerEnum::Lookup(_) => "LookupPlanner",
396            PlannerEnum::Path(_) => "PathPlanner",
397            PlannerEnum::Subgraph(_) => "SubgraphPlanner",
398            PlannerEnum::FetchVertices(_) => "FetchVerticesPlanner",
399            PlannerEnum::FetchEdges(_) => "FetchEdgesPlanner",
400            PlannerEnum::Maintain(_) => "MaintainPlanner",
401            PlannerEnum::UserManagement(_) => "UserManagementPlanner",
402            PlannerEnum::Assignment(_) => "AssignmentPlanner",
403            PlannerEnum::Insert(_) => "InsertPlanner",
404            PlannerEnum::Copy(_) => "CopyPlanner",
405            PlannerEnum::Delete(_) => "DeletePlanner",
406            PlannerEnum::Update(_) => "UpdatePlanner",
407            PlannerEnum::Remove(_) => "RemovePlanner",
408            PlannerEnum::Set(_) => "SetPlanner",
409            PlannerEnum::Merge(_) => "MergePlanner",
410            PlannerEnum::GroupBy(_) => "GroupByPlanner",
411            PlannerEnum::Filter(_) => "FilterPlanner",
412            PlannerEnum::Collect(_) => "CollectPlanner",
413            PlannerEnum::SetOperation(_) => "SetOperationPlanner",
414            PlannerEnum::Use(_) => "UsePlanner",
415            PlannerEnum::Unwind(_) => "UnwindPlanner",
416            PlannerEnum::With(_) => "WithPlanner",
417            PlannerEnum::Return(_) => "ReturnPlanner",
418            PlannerEnum::AssignVariable(_) => "AssignVariablePlanner",
419            PlannerEnum::Yield(_) => "YieldPlanner",
420            PlannerEnum::Pipe(_) => "PipePlanner",
421            PlannerEnum::Explain(_) => "ExplainPlanner",
422            PlannerEnum::FulltextSearch(_) => "FulltextSearchPlanner",
423            #[cfg(feature = "vector")]
424            PlannerEnum::VectorSearch(_) => "VectorSearchPlanner",
425        }
426    }
427
428    /// Check whether there is a match.
429    pub fn matches(&self, stmt: &Stmt) -> bool {
430        match self {
431            PlannerEnum::Match(planner) => planner.match_planner(stmt),
432            PlannerEnum::Go(planner) => planner.match_planner(stmt),
433            PlannerEnum::Lookup(planner) => planner.match_planner(stmt),
434            PlannerEnum::Path(planner) => planner.match_planner(stmt),
435            PlannerEnum::Subgraph(planner) => planner.match_planner(stmt),
436            PlannerEnum::FetchVertices(planner) => planner.match_planner(stmt),
437            PlannerEnum::FetchEdges(planner) => planner.match_planner(stmt),
438            PlannerEnum::Maintain(planner) => planner.match_planner(stmt),
439            PlannerEnum::UserManagement(planner) => planner.match_planner(stmt),
440            PlannerEnum::Assignment(planner) => planner.match_planner(stmt),
441            PlannerEnum::Insert(planner) => planner.match_planner(stmt),
442            PlannerEnum::Copy(planner) => planner.match_planner(stmt),
443            PlannerEnum::Delete(planner) => planner.match_planner(stmt),
444            PlannerEnum::Update(planner) => planner.match_planner(stmt),
445            PlannerEnum::Remove(planner) => planner.match_planner(stmt),
446            PlannerEnum::Set(planner) => planner.match_planner(stmt),
447            PlannerEnum::Merge(planner) => planner.match_planner(stmt),
448            PlannerEnum::GroupBy(planner) => planner.match_planner(stmt),
449            PlannerEnum::Filter(planner) => planner.match_planner(stmt),
450            PlannerEnum::Collect(planner) => planner.match_planner(stmt),
451            PlannerEnum::SetOperation(planner) => planner.match_planner(stmt),
452            PlannerEnum::Use(planner) => planner.match_planner(stmt),
453            PlannerEnum::Unwind(planner) => planner.match_planner(stmt),
454            PlannerEnum::With(planner) => planner.match_planner(stmt),
455            PlannerEnum::Return(planner) => planner.match_planner(stmt),
456            PlannerEnum::AssignVariable(planner) => planner.match_planner(stmt),
457            PlannerEnum::Yield(planner) => planner.match_planner(stmt),
458            PlannerEnum::Pipe(planner) => planner.match_planner(stmt),
459            PlannerEnum::Explain(planner) => planner.match_planner(stmt),
460            PlannerEnum::FulltextSearch(planner) => planner.match_planner(stmt),
461            #[cfg(feature = "vector")]
462            PlannerEnum::VectorSearch(planner) => planner.match_planner(stmt),
463        }
464    }
465
466    /// Plan from a BoundStatement, producing a SubPlan (PlanNodeEnum tree).
467    pub fn plan_bound(
468        &mut self,
469        ctx: &crate::planning::context::PlanContext<'_>,
470    ) -> Result<SubPlan, PlannerError> {
471        match self {
472            PlannerEnum::Match(planner) => planner.plan_bound(ctx),
473            PlannerEnum::Go(planner) => planner.plan_bound(ctx),
474            PlannerEnum::Lookup(planner) => planner.plan_bound(ctx),
475            PlannerEnum::Path(planner) => planner.plan_bound(ctx),
476            PlannerEnum::Subgraph(planner) => planner.plan_bound(ctx),
477            PlannerEnum::FetchVertices(planner) => planner.plan_bound(ctx),
478            PlannerEnum::FetchEdges(planner) => planner.plan_bound(ctx),
479            PlannerEnum::Maintain(planner) => planner.plan_bound(ctx),
480            PlannerEnum::UserManagement(planner) => planner.plan_bound(ctx),
481            PlannerEnum::Assignment(planner) => planner.plan_bound(ctx),
482            PlannerEnum::Insert(planner) => planner.plan_bound(ctx),
483            PlannerEnum::Copy(planner) => planner.plan_bound(ctx),
484            PlannerEnum::Delete(planner) => planner.plan_bound(ctx),
485            PlannerEnum::Update(planner) => planner.plan_bound(ctx),
486            PlannerEnum::Remove(planner) => planner.plan_bound(ctx),
487            PlannerEnum::Set(planner) => planner.plan_bound(ctx),
488            PlannerEnum::Merge(planner) => planner.plan_bound(ctx),
489            PlannerEnum::GroupBy(planner) => planner.plan_bound(ctx),
490            PlannerEnum::Filter(planner) => planner.plan_bound(ctx),
491            PlannerEnum::Collect(planner) => planner.plan_bound(ctx),
492            PlannerEnum::SetOperation(planner) => planner.plan_bound(ctx),
493            PlannerEnum::Use(planner) => planner.plan_bound(ctx),
494            PlannerEnum::Unwind(planner) => planner.plan_bound(ctx),
495            PlannerEnum::With(planner) => planner.plan_bound(ctx),
496            PlannerEnum::Return(planner) => planner.plan_bound(ctx),
497            PlannerEnum::AssignVariable(planner) => planner.plan_bound(ctx),
498            PlannerEnum::Yield(planner) => planner.plan_bound(ctx),
499            PlannerEnum::Pipe(planner) => planner.plan_bound(ctx),
500            PlannerEnum::Explain(planner) => planner.plan_bound(ctx),
501            PlannerEnum::FulltextSearch(planner) => planner.plan_bound(ctx),
502            #[cfg(feature = "vector")]
503            PlannerEnum::VectorSearch(planner) => planner.plan_bound(ctx),
504        }
505    }
506}
507
508/// Error handling macros
509#[macro_export]
510macro_rules! ng_return_if_error {
511    ($expr:expr) => {
512        match $expr {
513            Ok(val) => val,
514            Err(e) => return Err(e.into()),
515        }
516    };
517}
518
519/// Error handling macro variants
520#[macro_export]
521macro_rules! ng_ok_or_err {
522    ($expr:expr, $msg:expr) => {
523        match $expr {
524            Ok(val) => val,
525            Err(_) => return Err(PlannerError::PlanGenerationFailed($msg.to_string())),
526        }
527    };
528}
529
530/// Planner error type
531#[derive(Debug, thiserror::Error)]
532pub enum PlannerError {
533    #[error("No suitable planner found: {0}")]
534    NoSuitablePlanner(String),
535
536    #[error("Unsupported operation: {0}")]
537    UnsupportedOperation(String),
538
539    #[error("Plan generation failed: {0}")]
540    PlanGenerationFailed(String),
541
542    #[error("Join operation failed: {0}")]
543    JoinFailed(String),
544
545    #[error("Invalid AST context: {0}")]
546    InvalidAstContext(String),
547
548    #[error("Missing input: {0}")]
549    MissingInput(String),
550
551    #[error("Missing variable: {0}")]
552    MissingVariable(String),
553
554    #[error("Invalid operation: {0}")]
555    InvalidOperation(String),
556
557    #[error("Index not found: {0}")]
558    IndexNotFound(String),
559
560    #[error("Unsupported vector filter: {0}")]
561    UnsupportedVectorFilter(String),
562
563    #[error("Tag not found: {0}")]
564    TagNotFound(String),
565
566    #[error("Edge type not found: {0}")]
567    EdgeTypeNotFound(String),
568
569    #[error("Metadata version mismatch: expected {expected}, got {actual}")]
570    MetadataVersionMismatch { expected: u64, actual: u64 },
571
572    #[error("Invalid metadata: {0}")]
573    InvalidMetadata(String),
574}
575
576// Implement the From conversion for the DBError class.
577impl From<linkrs_core::error::DBError> for PlannerError {
578    fn from(err: linkrs_core::error::DBError) -> Self {
579        PlannerError::PlanGenerationFailed(err.to_string())
580    }
581}
582
583#[cfg(test)]
584mod tests {
585    use super::*;
586
587    #[test]
588    fn test_planner_enum_from_stmt() {
589        // Testing the creation of a planner from a Stmt
590        let match_stmt = Stmt::Match(crate::parser::ast::MatchStmt {
591            span: linkrs_core::types::Span::default(),
592            patterns: vec![],
593            where_clause: None,
594            return_clause: None,
595            order_by: None,
596            limit: None,
597            skip: None,
598            join_hint: None,
599            optional: false,
600            delete_clause: None,
601        });
602
603        let planner = PlannerEnum::from_stmt(&Arc::new(match_stmt));
604        assert!(planner.is_some());
605        assert_eq!(
606            planner.expect("Planner should exist").name(),
607            "MatchPlanner"
608        );
609    }
610
611    #[test]
612    fn test_planner_enum_matches() {
613        let match_stmt = Stmt::Match(crate::parser::ast::MatchStmt {
614            span: linkrs_core::types::Span::default(),
615            patterns: vec![],
616            where_clause: None,
617            return_clause: None,
618            order_by: None,
619            limit: None,
620            skip: None,
621            join_hint: None,
622            optional: false,
623            delete_clause: None,
624        });
625
626        let planner = PlannerEnum::Match(Box::default());
627        assert!(planner.matches(&match_stmt));
628    }
629}