#![recursion_limit = "256"]
pub mod procedures_plugin;
pub mod projection_store;
pub mod query;
pub mod types;
pub use query::executor::core::{OperatorStats, ProfileOutput};
pub use query::executor::procedure::{
ProcedureOutput, ProcedureParam, ProcedureRegistry, ProcedureValueType, RegisteredProcedure,
};
pub use query::executor::{CustomFunctionRegistry, CustomScalarFn, Executor, ResultNormalizer};
pub use query::df_udfs_plugin::{
CURRENT_PRINCIPAL, SESSION_PLUGIN_REGISTRY, current_principal, current_session_plugin_registry,
maybe_scope_with_principal, scoped_with_principal, scoped_with_session_context,
scoped_with_session_plugin_registry,
};
pub use query::planner::{
CostEstimates, ExplainOutput, ForkIndexLookup, FusionKind, IndexUsage, LogicalPlan,
QueryPlanner, fuse_create_set, rewrite_for_fork_fusion,
};
pub use types::{
Edge, ExecuteResult, FromValue, Node, Path, QueryCursor, QueryMetrics, QueryResult,
QueryWarning, Row, Value,
};
pub use uni_cypher::ast::{Query as CypherQuery, TimeTravelSpec};
pub fn validate_read_only(query: &CypherQuery) -> Result<(), String> {
validate_read_only_with(query, &|_| false)
}
pub fn validate_read_only_with(
query: &CypherQuery,
is_write_procedure: &dyn Fn(&str) -> bool,
) -> Result<(), String> {
use uni_cypher::ast::{CallKind, Clause, Query, Statement};
fn check_statement(
stmt: &Statement,
is_write_procedure: &dyn Fn(&str) -> bool,
) -> Result<(), String> {
for clause in &stmt.clauses {
match clause {
Clause::Create(_)
| Clause::Merge(_)
| Clause::Delete(_)
| Clause::Set(_)
| Clause::Remove(_) => {
return Err(
"Write clauses (CREATE, MERGE, DELETE, SET, REMOVE) are not allowed \
in a read-only context"
.to_string(),
);
}
Clause::Call(call) => match &call.kind {
CallKind::Subquery(inner) => check_query(inner, is_write_procedure)?,
CallKind::Procedure { procedure, .. } => {
if is_write_procedure(procedure) {
return Err(format!(
"Write procedure CALL {procedure}(...) is not allowed \
in a read-only context"
));
}
}
},
_ => {}
}
}
Ok(())
}
fn check_query(q: &Query, is_write_procedure: &dyn Fn(&str) -> bool) -> Result<(), String> {
match q {
Query::Single(stmt) => check_statement(stmt, is_write_procedure),
Query::Union { left, right, .. } => {
check_query(left, is_write_procedure)?;
check_query(right, is_write_procedure)
}
Query::Explain(inner) => check_query(inner, is_write_procedure),
Query::TimeTravel { query, .. } => check_query(query, is_write_procedure),
Query::Schema(cmd) => {
use uni_cypher::ast::SchemaCommand;
match cmd.as_ref() {
SchemaCommand::ShowConstraints(_)
| SchemaCommand::ShowIndexes(_)
| SchemaCommand::ShowDatabase
| SchemaCommand::ShowConfig
| SchemaCommand::ShowStatistics => Ok(()),
_ => Err(
"Mutating schema commands are not allowed in read-only context".to_string(),
),
}
}
}
}
check_query(query, is_write_procedure)
}