1#![recursion_limit = "256"]
24
25pub mod query;
26pub mod types;
27
28pub use query::executor::core::{OperatorStats, ProfileOutput};
29pub use query::executor::procedure::{
30 ProcedureOutput, ProcedureParam, ProcedureRegistry, ProcedureValueType, RegisteredProcedure,
31};
32pub use query::executor::{CustomFunctionRegistry, CustomScalarFn, Executor, ResultNormalizer};
33pub use query::planner::{CostEstimates, ExplainOutput, IndexUsage, LogicalPlan, QueryPlanner};
34pub use types::{
35 Edge, ExecuteResult, FromValue, Node, Path, QueryCursor, QueryMetrics, QueryResult,
36 QueryWarning, Row, Value,
37};
38pub use uni_cypher::ast::{Query as CypherQuery, TimeTravelSpec};
39
40pub fn validate_read_only(query: &CypherQuery) -> Result<(), String> {
51 use uni_cypher::ast::{Clause, Query, Statement};
52
53 fn check_statement(stmt: &Statement) -> Result<(), String> {
54 for clause in &stmt.clauses {
55 match clause {
56 Clause::Create(_)
57 | Clause::Merge(_)
58 | Clause::Delete(_)
59 | Clause::Set(_)
60 | Clause::Remove(_) => {
61 return Err(
62 "Write clauses (CREATE, MERGE, DELETE, SET, REMOVE) are not allowed \
63 with VERSION AS OF / TIMESTAMP AS OF"
64 .to_string(),
65 );
66 }
67 _ => {}
68 }
69 }
70 Ok(())
71 }
72
73 fn check_query(q: &Query) -> Result<(), String> {
74 match q {
75 Query::Single(stmt) => check_statement(stmt),
76 Query::Union { left, right, .. } => {
77 check_query(left)?;
78 check_query(right)
79 }
80 Query::Explain(inner) => check_query(inner),
81 Query::TimeTravel { query, .. } => check_query(query),
82 Query::Schema(cmd) => {
83 use uni_cypher::ast::SchemaCommand;
84 match cmd.as_ref() {
85 SchemaCommand::ShowConstraints(_)
87 | SchemaCommand::ShowIndexes(_)
88 | SchemaCommand::ShowDatabase
89 | SchemaCommand::ShowConfig
90 | SchemaCommand::ShowStatistics => Ok(()),
91 _ => Err(
93 "Mutating schema commands are not allowed in read-only context".to_string(),
94 ),
95 }
96 }
97 }
98 }
99
100 check_query(query)
101}