use crate::binder::BoundStatement;
use crate::parser::ast::{SetStmt, Stmt};
use crate::planning::plan::core::nodes::VertexUpdateInfo;
use crate::planning::plan::SubPlan;
use crate::planning::planner::{Planner, PlannerError, ValidatedStatement};
use crate::planning::statements::clauses::exists_planner;
use crate::QueryContext;
use linkrs_core::types::{ContextualExpression, ExpressionMeta};
use linkrs_core::Expression;
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct SetPlanner;
impl SetPlanner {
pub fn new() -> Self {
Self
}
fn extract_set_stmt(&self, stmt: &Stmt) -> Result<SetStmt, PlannerError> {
match stmt {
Stmt::Set(set_stmt) => Ok(set_stmt.clone()),
_ => Err(PlannerError::PlanGenerationFailed(
"Statement does not contain SET".to_string(),
)),
}
}
fn extract_vertex_id(&self, target: &ContextualExpression) -> Option<ContextualExpression> {
let expr_meta = target.expression()?;
let expr = expr_meta.inner();
match expr {
Expression::Property { object, .. } => {
match object.as_ref() {
Expression::Literal(_) | Expression::Variable(_) => {
let object_meta = ExpressionMeta::new((**object).clone());
let context = target.context();
let object_id = context.register_expression(object_meta);
Some(ContextualExpression::new(object_id, context.clone()))
}
_ => None,
}
}
_ => None,
}
}
}
impl Planner for SetPlanner {
fn plan_bound(
&mut self,
ctx: &crate::planning::context::PlanContext<'_>,
) -> Result<SubPlan, PlannerError> {
let bound = ctx.bound;
let qctx = ctx.qctx.clone();
let metadata = ctx.metadata;
let validated = ctx.validated;
let _ = (&bound, &qctx, &metadata, &validated);
let set = match bound {
BoundStatement::Set(s) => s,
_ => {
return Err(PlannerError::PlanGenerationFailed(
"Statement does not contain SET".to_string(),
));
}
};
let set_stmt = self.extract_set_stmt(validated.stmt())?;
let check_space_id = qctx.space_id().unwrap_or(1);
let check_space_name = qctx.space_name().unwrap_or_else(|| "default".to_string());
let outer_col_names: Vec<String> = Vec::new();
for assignment in &set_stmt.assignments {
if let Some(expr_meta) = assignment.value.expression() {
exists_planner::check_expression_subqueries(
expr_meta.inner(),
&qctx,
check_space_id,
&check_space_name,
&outer_col_names,
)?;
}
}
let space_name = qctx
.space_name()
.map(|s| s.to_string())
.unwrap_or_else(|| "default".to_string());
let expr_ctx = Arc::new(
linkrs_core::types::expr::expression_context::ExpressionAnalysisContext::new(),
);
let mut vertex_updates: Vec<VertexUpdateInfo> = Vec::new();
let mut variable_assignments: Vec<(String, ContextualExpression)> = Vec::new();
for assignment in &set.assignments {
if let Some(ref target) = assignment.target {
let is_direct_vertex = match target {
crate::binder::bound::BoundExpression::Property { object, .. } => {
matches!(
object.as_ref(),
crate::binder::bound::BoundExpression::Literal(_, _)
| crate::binder::bound::BoundExpression::Variable(_, _)
)
}
_ => false,
};
if is_direct_vertex {
if let crate::binder::bound::BoundExpression::Property { object, .. } = target {
let vertex_id = crate::binder::expr_converter::bound_expr_to_contextual(
object, &expr_ctx,
)
.map_err(PlannerError::PlanGenerationFailed)?;
let value = crate::binder::expr_converter::bound_expr_to_contextual(
&assignment.value,
&expr_ctx,
)
.map_err(PlannerError::PlanGenerationFailed)?;
let mut properties = HashMap::new();
properties.insert(assignment.property.clone(), value);
let vertex_update = VertexUpdateInfo {
space_name: space_name.clone(),
vertex_id,
tag_name: None,
properties,
condition: None,
is_upsert: false,
replace_properties: assignment.is_map_overwrite,
};
vertex_updates.push(vertex_update);
}
} else {
let value = crate::binder::expr_converter::bound_expr_to_contextual(
&assignment.value,
&expr_ctx,
)
.map_err(PlannerError::PlanGenerationFailed)?;
variable_assignments.push((assignment.property.clone(), value));
}
} else {
let value = crate::binder::expr_converter::bound_expr_to_contextual(
&assignment.value,
&expr_ctx,
)
.map_err(PlannerError::PlanGenerationFailed)?;
variable_assignments.push((assignment.property.clone(), value));
}
}
return Err(PlannerError::PlanGenerationFailed(
"standalone SET cannot resolve a tag qualifier; use UPDATE ... ON <tag> SET instead"
.to_string(),
));
}
fn transform(
&mut self,
validated: &ValidatedStatement,
qctx: Arc<QueryContext>,
) -> Result<SubPlan, PlannerError> {
let set_stmt = self.extract_set_stmt(validated.stmt())?;
let check_space_id = qctx.space_id().unwrap_or(1);
let check_space_name = qctx.space_name().unwrap_or_else(|| "default".to_string());
let outer_col_names: Vec<String> = Vec::new();
for assignment in &set_stmt.assignments {
if let Some(expr_meta) = assignment.value.expression() {
exists_planner::check_expression_subqueries(
expr_meta.inner(),
&qctx,
check_space_id,
&check_space_name,
&outer_col_names,
)?;
}
}
let space_name = qctx
.space_name()
.map(|s| s.to_string())
.unwrap_or_else(|| "default".to_string());
let mut vertex_updates: Vec<VertexUpdateInfo> = Vec::new();
let mut variable_assignments: Vec<(String, ContextualExpression)> = Vec::new();
for assignment in &set_stmt.assignments {
if let Some(ref target) = assignment.target {
if let Some(vertex_id_expr) = self.extract_vertex_id(target) {
let mut properties = HashMap::new();
properties.insert(assignment.property.clone(), assignment.value.clone());
let vertex_update = VertexUpdateInfo {
space_name: space_name.clone(),
vertex_id: vertex_id_expr,
tag_name: None, properties,
condition: None,
is_upsert: false,
replace_properties: assignment.is_map_overwrite,
};
vertex_updates.push(vertex_update);
} else {
variable_assignments
.push((assignment.property.clone(), assignment.value.clone()));
}
} else {
variable_assignments.push((assignment.property.clone(), assignment.value.clone()));
}
}
return Err(PlannerError::PlanGenerationFailed(
"standalone SET cannot resolve a tag qualifier; use UPDATE ... ON <tag> SET instead"
.to_string(),
));
}
fn match_planner(&self, stmt: &Stmt) -> bool {
matches!(stmt, Stmt::Set(_))
}
}
impl Default for SetPlanner {
fn default() -> Self {
Self::new()
}
}