akar_processor/physical/write_ops/
insert.rs1use crate::physical::types::{OperatorResult, PhysicalOperatorExec};
4use crate::physical::write_ops::set::evaluate_expression_for_row;
5use akar_common::types::{PhysicalTypeID, Value};
6use akar_common::vector::{DataChunk, ValueVector};
7use akar_storage::table::TableCatalog;
8use std::sync::Arc;
9
10pub struct PhysicalInsertNode {
12 pub table_name: String,
13 pub table_id: u64,
14 pub out_var_name: String,
15 pub properties: Vec<(String, akar_parser::ast::Expression)>,
16 pub table_catalog: Arc<TableCatalog>,
17}
18
19impl PhysicalOperatorExec for PhysicalInsertNode {
20 fn operator_type(&self) -> &str {
21 "insert_node"
22 }
23
24 fn execute(&self, input: Vec<DataChunk>) -> OperatorResult {
25 let mut assigned_row_ids: Vec<i64> = Vec::new();
26 let mut table = self
27 .table_catalog
28 .get_node_table_by_name_mut(&self.table_name)
29 .ok_or_else(|| format!("Node table '{}' not found for INSERT", self.table_name))?;
30
31 let chunks = if input.is_empty() {
34 let mut v = ValueVector::new(PhysicalTypeID::Int64, 1);
35 v.resize(1);
36 v.set_i64(0, 0);
37 let arr = akar_common::arrow_vector::ArrowVector::from_legacy(&v).array;
38 vec![DataChunk::new(vec![arr], vec![PhysicalTypeID::Int64])]
39 } else {
40 input
41 };
42
43 let num_cols = table.columns.len();
44 for chunk in &chunks {
45 for row in 0..chunk.size {
46 let mut row_values = vec![Value::Null; num_cols];
47
48 for (prop_name, expr) in &self.properties {
49 if let Some(col_idx) = table.columns.iter().position(|c| c.name == *prop_name) {
50 let val = evaluate_expression_for_row(expr, chunk, row);
51 row_values[col_idx] = val;
52 }
53 }
54
55 if let Ok(row_id) = table.insert_row(row_values) {
57 assigned_row_ids.push(row_id as i64);
58 }
59 }
60 }
61
62 let inserted_count = assigned_row_ids.len();
63 tracing::info!("INSERT NODE: added {inserted_count} rows to '{}'", self.table_name);
64
65 let mut count_v = ValueVector::new(PhysicalTypeID::Int64, 1);
66 count_v.resize(1);
67 count_v.set_i64(0, inserted_count as i64);
68 let arr_count = akar_common::arrow_vector::ArrowVector::from_legacy(&count_v).array;
69
70 let mut ids_v = ValueVector::new(PhysicalTypeID::Int64, assigned_row_ids.len());
72 ids_v.resize(assigned_row_ids.len());
73 for (i, rid) in assigned_row_ids.iter().enumerate() {
74 ids_v.set_i64(i, *rid);
75 }
76 let arr_ids = akar_common::arrow_vector::ArrowVector::from_legacy(&ids_v).array;
77
78 Ok(vec![DataChunk::new(
79 vec![arr_count, arr_ids],
80 vec![PhysicalTypeID::Int64, PhysicalTypeID::Int64],
81 )])
82 }
83}
84
85pub struct PhysicalInsertRel {
87 pub table_name: String,
88 pub table_id: u64,
89 pub src_node_name: String,
90 pub dst_node_name: String,
91 pub properties: Vec<(String, akar_parser::ast::Expression)>,
92 pub table_catalog: Arc<TableCatalog>,
93}
94
95impl PhysicalOperatorExec for PhysicalInsertRel {
96 fn operator_type(&self) -> &str {
97 "insert_rel"
98 }
99
100 fn execute(&self, input: Vec<DataChunk>) -> OperatorResult {
101 let mut inserted_count = 0u64;
102 let mut table = self
103 .table_catalog
104 .get_rel_table_by_name_mut(&self.table_name)
105 .ok_or_else(|| format!("Rel table '{}' not found for INSERT", self.table_name))?;
106
107 let num_cols = table.columns.len();
108 let mut rels_to_insert = Vec::new();
109
110 for chunk in &input {
111 let src_name_id = format!("{}.{}", self.src_node_name, "_id");
112 let src_name_pk = format!("{}.{}", self.src_node_name, "id");
113 let src_name_pk_upper = format!("{}.{}", self.src_node_name, "ID");
114 let src_node_col_idx = chunk
115 .field_names
116 .iter()
117 .position(|name| name == &src_name_id)
118 .or_else(|| chunk.field_names.iter().position(|name| name == &self.src_node_name))
119 .or_else(|| chunk.field_names.iter().position(|name| name == &src_name_pk))
120 .or_else(|| chunk.field_names.iter().position(|name| name == &src_name_pk_upper))
121 .or_else(|| {
122 chunk
123 .field_names
124 .iter()
125 .position(|name| name.eq_ignore_ascii_case(&src_name_pk))
126 })
127 .ok_or_else(|| {
128 format!(
129 "Source node variable {} not found (fields: {:?})",
130 self.src_node_name, chunk.field_names
131 )
132 })?;
133
134 let dst_name_id = format!("{}.{}", self.dst_node_name, "_id");
135 let dst_name_pk = format!("{}.{}", self.dst_node_name, "id");
136 let dst_name_pk_upper = format!("{}.{}", self.dst_node_name, "ID");
137 let dst_node_col_idx = chunk
138 .field_names
139 .iter()
140 .position(|name| name == &dst_name_id)
141 .or_else(|| chunk.field_names.iter().position(|name| name == &self.dst_node_name))
142 .or_else(|| chunk.field_names.iter().position(|name| name == &dst_name_pk))
143 .or_else(|| chunk.field_names.iter().position(|name| name == &dst_name_pk_upper))
144 .or_else(|| {
145 chunk
146 .field_names
147 .iter()
148 .position(|name| name.eq_ignore_ascii_case(&dst_name_pk))
149 })
150 .ok_or_else(|| {
151 format!(
152 "Destination node variable {} not found (fields: {:?})",
153 self.dst_node_name, chunk.field_names
154 )
155 })?;
156
157 if src_node_col_idx >= chunk.fields.len() || dst_node_col_idx >= chunk.fields.len() {
158 return Err("Src/Dst node column index out of bounds in INSERT REL".into());
159 }
160
161 for row in 0..chunk.size {
162 let src_id = if let Some(Value::Int64(val)) = chunk.get_value(src_node_col_idx, row) {
163 val as u64
164 } else {
165 0
166 };
167 let dst_id = if let Some(Value::Int64(val)) = chunk.get_value(dst_node_col_idx, row) {
168 val as u64
169 } else {
170 0
171 };
172
173 let mut props = vec![Value::Null; num_cols];
174 for (prop_name, expr) in &self.properties {
175 if let Some(col_idx) = table.columns.iter().position(|c| c.name == *prop_name) {
176 let val = evaluate_expression_for_row(expr, chunk, row);
177 props[col_idx] = val;
178 }
179 }
180
181 rels_to_insert.push((src_id, dst_id, props));
182 }
183 }
184
185 if !rels_to_insert.is_empty() {
187 inserted_count = table
188 .insert_rels_batch(&rels_to_insert)
189 .map_err(|e| format!("BatchInsert rel error: {e}"))?;
190 }
191
192 tracing::info!("INSERT REL: added {inserted_count} rels to '{}'", self.table_name);
193
194 let mut v = ValueVector::new(PhysicalTypeID::Int64, 1);
195 v.resize(1);
196 v.set_i64(0, inserted_count as i64);
197 let arr = akar_common::arrow_vector::ArrowVector::from_legacy(&v).array;
198 Ok(vec![DataChunk::new(vec![arr], vec![PhysicalTypeID::Int64])])
199 }
200}