1use std::{
5 collections::{HashMap, HashSet},
6 sync::Arc,
7};
8
9use reifydb_codec::row::{
10 bytes::{EncodedBytes, RowBuilder},
11 pod::EncodedPodRow,
12 shape::RowShape,
13 table::{EncodedTableRow, EncodedTableRowBuilder},
14};
15use reifydb_core::{
16 error::diagnostic::{
17 catalog::{namespace_not_found, table_not_found},
18 index::primary_key_violation,
19 },
20 interface::{
21 catalog::{
22 config::{ConfigKey, GetConfig},
23 id::IndexId,
24 key::PrimaryKey,
25 namespace::Namespace,
26 object::ObjectId,
27 policy::{DataOp, PolicyTargetType},
28 table::Table,
29 },
30 resolved::{ResolvedColumn, ResolvedNamespace, ResolvedObject, ResolvedTable},
31 },
32 internal_error,
33 key::catalog::IndexEntryKey,
34 partition::partition_col_indices,
35 value::column::columns::Columns,
36};
37use reifydb_evaluate::stack::SymbolTable;
38use reifydb_rql::nodes::InsertTableNode;
39use reifydb_transaction::transaction::Transaction;
40use reifydb_value::{
41 fragment::Fragment,
42 params::Params,
43 return_error,
44 value::{Value, identity::IdentityId, partition::Partition, row_number::RowNumber},
45};
46use tracing::instrument;
47
48use super::{
49 context::TableTarget,
50 primary_key,
51 returning::{decode_returning_dictionaries, decode_rows_to_columns, evaluate_returning},
52 shape::get_or_create_table_shape,
53};
54use crate::{
55 Result,
56 partition::{partition_values, resolve_partition},
57 policy::PolicyEvaluator,
58 transaction::operation::{dictionary::DictionaryOperations, table::TableOperations},
59 vm::{
60 instruction::dml::{coerce::coerce_value_to_column_type, time::resolve_time},
61 services::Services,
62 volcano::{
63 compile::compile,
64 query::{QueryContext, QueryNode, query_budget},
65 },
66 },
67};
68
69#[instrument(name = "mutate::table::insert", level = "trace", skip_all)]
70pub(crate) fn insert_table(
71 services: &Arc<Services>,
72 txn: &mut Transaction<'_>,
73 plan: InsertTableNode,
74 symbols: &mut SymbolTable,
75) -> Result<Columns> {
76 let InsertTableNode {
77 input,
78 target,
79 returning,
80 } = plan;
81 let (namespace, table) = resolve_insert_table_target(services, txn, &target)?;
82 let shape = get_or_create_table_shape(&services.catalog, &table, txn)?;
83 let target_data = TableTarget {
84 namespace: &namespace,
85 table: &table,
86 fragment: target.identifier(),
87 };
88 let context = build_insert_table_query_context(services, &target_data, symbols, txn.identity());
89 let mut input_node = compile(*input, txn, context.clone());
90 input_node.initialize(txn, &context)?;
91
92 let validated = validate_and_encode_input_rows(
93 services,
94 txn,
95 &target_data,
96 &shape,
97 &context,
98 symbols,
99 &mut input_node,
100 )?;
101
102 if !table.partition_by.is_empty() {
103 let indices = partition_col_indices(&table.columns, &table.partition_by);
104 let mut verified = HashSet::new();
105 for row in &validated {
106 let values = partition_values(&shape, row, &indices);
107 let partition = Partition::of(&values);
108 resolve_partition(txn, ObjectId::Table(table.id), partition, &values, &mut verified)?;
109 }
110 }
111
112 let total_rows = validated.len();
113 if total_rows == 0 {
114 return Ok(insert_table_result(namespace.name(), &table.name, 0));
115 }
116
117 let row_numbers = services.catalog.next_row_number_batch(txn, table.id, total_rows as u64)?;
118 assert_eq!(row_numbers.len(), validated.len());
119
120 let pk_def = primary_key::get_primary_key(&services.catalog, txn, &table)?;
121 let pk_ctx = pk_def.as_ref().map(|pk| PkContext {
122 pk_def: pk,
123 });
124 let returned_rows = insert_validated_table_rows(
125 txn,
126 &target_data,
127 &shape,
128 &validated,
129 &row_numbers,
130 returning.is_some(),
131 pk_ctx.as_ref(),
132 )?;
133
134 if let Some(returning_exprs) = &returning {
135 let mut columns = decode_rows_to_columns(&shape, &returned_rows);
136 decode_returning_dictionaries(services, txn, &table.columns, &mut columns)?;
137 return evaluate_returning(services, symbols, returning_exprs, columns, txn.identity());
138 }
139 Ok(insert_table_result(namespace.name(), &table.name, total_rows as u64))
140}
141
142struct PkContext<'a> {
143 pk_def: &'a PrimaryKey,
144}
145
146struct ColumnView<'a> {
147 columns: &'a Columns,
148 column_map: &'a HashMap<&'a str, usize>,
149}
150
151#[inline]
152fn resolve_insert_table_target(
153 services: &Arc<Services>,
154 txn: &mut Transaction<'_>,
155 target: &ResolvedTable,
156) -> Result<(Namespace, Table)> {
157 let namespace_name = target.namespace().name();
158 let Some(namespace) = services.catalog.find_namespace_by_name(txn, namespace_name)? else {
159 return_error!(namespace_not_found(Fragment::internal(namespace_name), namespace_name));
160 };
161 let table_name = target.name();
162 let Some(table) = services.catalog.find_table_by_name(txn, namespace.id(), table_name)? else {
163 let fragment = target.identifier().clone();
164 return_error!(table_not_found(fragment.clone(), namespace_name, table_name,));
165 };
166 Ok((namespace, table))
167}
168
169#[inline]
170fn build_insert_table_query_context(
171 services: &Arc<Services>,
172 target: &TableTarget<'_>,
173 symbols: &SymbolTable,
174 identity: IdentityId,
175) -> Arc<QueryContext> {
176 let namespace_ident = Fragment::internal(target.namespace.name());
177 let resolved_namespace = ResolvedNamespace::new(namespace_ident, target.namespace.clone());
178 let table_ident = Fragment::internal(target.table.name.clone());
179 let resolved_table = ResolvedTable::new(table_ident, resolved_namespace, target.table.clone());
180 Arc::new(QueryContext {
181 services: services.clone(),
182 source: Some(ResolvedObject::Table(resolved_table)),
183 batch_size: services.catalog.get_config_uint2(ConfigKey::QueryRowBatchSize) as u64,
184 params: Params::None,
185 symbols: symbols.clone(),
186 identity,
187 memory: query_budget(services),
188 })
189}
190
191fn validate_and_encode_input_rows(
192 services: &Arc<Services>,
193 txn: &mut Transaction<'_>,
194 target: &TableTarget<'_>,
195 shape: &RowShape,
196 context: &Arc<QueryContext>,
197 symbols: &SymbolTable,
198 input_node: &mut Box<dyn QueryNode>,
199) -> Result<Vec<EncodedBytes>> {
200 let mut validated: Vec<EncodedBytes> = Vec::new();
201 let mut mutable_context = (**context).clone();
202 while let Some(columns) = input_node.next(txn, &mut mutable_context)? {
203 PolicyEvaluator::new(services, symbols).enforce_write_policies(
204 txn,
205 target.namespace.name(),
206 &target.table.name,
207 DataOp::Insert,
208 &columns,
209 PolicyTargetType::Table,
210 )?;
211 let mut column_map: HashMap<&str, usize> = HashMap::new();
212 for (idx, col) in columns.iter().enumerate() {
213 column_map.insert(col.name().text(), idx);
214 }
215 let view = ColumnView {
216 columns: &columns,
217 column_map: &column_map,
218 };
219 let row_count = columns.row_count();
220 for row_idx in 0..row_count {
221 validated.push(build_insert_table_row(services, txn, target, shape, &view, context, row_idx)?);
222 }
223 }
224 Ok(validated)
225}
226
227#[inline]
228fn build_insert_table_row(
229 services: &Arc<Services>,
230 txn: &mut Transaction<'_>,
231 target: &TableTarget<'_>,
232 shape: &RowShape,
233 view: &ColumnView<'_>,
234 context: &Arc<QueryContext>,
235 row_idx: usize,
236) -> Result<EncodedBytes> {
237 let mut row = shape.allocate_table();
238 for (table_idx, table_column) in target.table.columns.iter().enumerate() {
239 let mut value = if let Some(&input_idx) = view.column_map.get(table_column.name.as_str()) {
240 view.columns[input_idx].get_value(row_idx)
241 } else {
242 Value::none()
243 };
244 if table_column.auto_increment && matches!(value, Value::None { .. }) {
245 value = services.catalog.column_sequence_next_value(txn, target.table.id, table_column.id)?;
246 }
247 let column_ident = view
248 .column_map
249 .get(table_column.name.as_str())
250 .map(|&idx| view.columns.name_at(idx).clone())
251 .unwrap_or_else(|| Fragment::internal(table_column.name.clone()));
252 let resolved_column = ResolvedColumn::new(
253 column_ident.clone(),
254 context.source.clone().unwrap(),
255 table_column.clone(),
256 );
257 value = coerce_value_to_column_type(
258 value,
259 table_column.constraint.get_type(),
260 resolved_column,
261 context,
262 )?;
263 if let Err(mut e) = table_column.constraint.validate(&value) {
264 e.0.fragment = column_ident.clone();
265 return Err(e);
266 }
267 let value = if let Some(dict_id) = table_column.dictionary_id {
268 let dictionary = services.catalog.find_dictionary(txn, dict_id)?.ok_or_else(|| {
269 internal_error!("Dictionary {:?} not found for column {}", dict_id, table_column.name)
270 })?;
271 let entry_id = if matches!(value, Value::None { .. }) {
272 dictionary.id_type.none()
273 } else {
274 txn.insert_into_dictionary(&dictionary, &value)?
275 };
276 entry_id.to_value()
277 } else {
278 value
279 };
280 shape.set_value(&mut row, table_idx, &value);
281 }
282 let now = services.runtime_context.clock.now();
283 row.set_timestamps(now, now);
284 if let Some(time) =
285 resolve_time(&target.table.name, &target.table.columns, &target.table.time, shape, &row, now)?
286 {
287 row.set_time(time);
288 }
289 Ok(row.freeze_bytes())
290}
291
292fn insert_validated_table_rows(
293 txn: &mut Transaction<'_>,
294 target: &TableTarget<'_>,
295 shape: &RowShape,
296 validated: &[EncodedBytes],
297 row_numbers: &[RowNumber],
298 has_returning: bool,
299 pk: Option<&PkContext<'_>>,
300) -> Result<Vec<(RowNumber, EncodedBytes)>> {
301 let mut owned_rows: Vec<EncodedTableRowBuilder> =
302 validated.iter().map(|r| EncodedTableRow::from(r.clone()).thaw()).collect();
303 txn.insert_table(target.table, shape, row_numbers, &mut owned_rows)?;
304
305 if let Some(pk) = pk {
306 for (row, &row_number) in owned_rows.iter().zip(row_numbers.iter()) {
307 write_insert_table_pk_index(txn, target, shape, pk, row, row_number)?;
308 }
309 }
310
311 if has_returning {
312 Ok(row_numbers.iter().copied().zip(owned_rows.into_iter().map(|r| r.freeze_bytes())).collect())
313 } else {
314 Ok(Vec::new())
315 }
316}
317
318#[inline]
319fn write_insert_table_pk_index(
320 txn: &mut Transaction<'_>,
321 target: &TableTarget<'_>,
322 shape: &RowShape,
323 pk: &PkContext<'_>,
324 row: &[u8],
325 row_number: RowNumber,
326) -> Result<()> {
327 let index_key = primary_key::encode_primary_key(pk.pk_def, row, target.table, shape)?;
328 let index_entry_key = IndexEntryKey::new(target.table.id, IndexId::primary(pk.pk_def.id), index_key.clone());
329 if txn.contains(&index_entry_key)? {
330 let key_columns = pk.pk_def.columns.iter().map(|c| c.name.clone()).collect();
331 return_error!(primary_key_violation(target.fragment.clone(), target.table.name.clone(), key_columns,));
332 }
333 txn.set(&index_entry_key, EncodedPodRow::new(&u64::from(row_number).to_be_bytes()).into_bytes())?;
334 Ok(())
335}
336
337#[inline]
338fn insert_table_result(namespace: &str, table: &str, inserted: u64) -> Columns {
339 Columns::single_row([
340 ("namespace", Value::Utf8(namespace.to_string())),
341 ("table", Value::Utf8(table.to_string())),
342 ("inserted", Value::Uint8(inserted)),
343 ])
344}