1use std::{collections::HashMap, marker::PhantomData, sync::Arc};
5
6use arrow_array::ArrayRef;
7use arrow_schema::FieldRef;
8use reifydb_catalog::{
9 catalog::Catalog,
10 error::{CatalogError, CatalogObjectKind},
11};
12use reifydb_codec::row::{
13 bytes::{EncodedBytes, RowBuilder, SourceRowBuilder},
14 pod::EncodedPodRow,
15 shape::RowShape,
16};
17use reifydb_core::{
18 common::TimeSource,
19 delta::Delta,
20 durability::Durability,
21 interface::catalog::{
22 column::Column,
23 namespace::Namespace,
24 object::ObjectId,
25 policy::{DataOp, PolicyTargetType},
26 ringbuffer::RingBuffer,
27 series::{Series, SeriesPartitionMetadata, decode_series_partition_metadata},
28 storage::StorageId,
29 table::Table,
30 },
31 internal_error,
32 key::{any::TaggedKey, series::SeriesPartitionMetadataKey},
33 value::batch::batch,
34};
35use reifydb_evaluate::stack::SymbolTable;
36use reifydb_transaction::{
37 multi::transaction::write::Rewrite,
38 transaction::{Transaction, command::CommandTransaction},
39};
40use reifydb_value::{
41 error::Error,
42 fragment::Fragment,
43 value::{
44 Value, column_view::ColumnView, constraint::Constraint, datetime::DateTime, identity::IdentityId,
45 partition::Partition, value_type::ValueType,
46 },
47};
48
49use super::{
50 BulkInsertResult, RingBufferInsertResult, SeriesInsertResult, TableInsertResult,
51 coerce::{RowCoercer, with_row_note},
52 validation::{CoercedTags, coerce_columns},
53};
54use crate::{
55 Result,
56 bulk_insert::storage::{
57 ringbuffer::{PendingRingBufferInsert, RingBufferInsertBuilder},
58 series::{PendingSeriesInsert, SeriesInsertBuilder},
59 table::{PendingTableInsert, TableInsertBuilder},
60 },
61 engine::StandardEngine,
62 error::EngineError,
63 flow_sync::{load_flow_diff_need, sync_transactional_views},
64 partition::resolve_partitions,
65 policy::PolicyEvaluator,
66 transaction::operation::series::{SeriesRows, write_series_rows},
67 vm::{
68 instruction::dml::{
69 coerce::key_out_of_range,
70 columns::{CastColumns, fill_sequences, intern_dictionary_columns},
71 partition::{ringbuffer_batch_partitions, save_all_partition_metadata},
72 primary_key::{Violation, get_primary_key},
73 ringbuffer_insert::insert_ringbuffer_chunks,
74 series_insert::{assign_series_keys, series_batch_partitions, write_inserted_series_metadata},
75 shape::{
76 get_or_create_ringbuffer_shape, get_or_create_series_shape, get_or_create_table_shape,
77 },
78 table_insert::{EncodedTableRows, table_batch_partitions, write_table_rows},
79 time::{RowTime, populator_index},
80 },
81 services::Services,
82 },
83};
84
85pub trait ValidationMode: sealed::Sealed + 'static {
86 const VALIDATED: bool;
87
88 fn run<F, R>(txn: &mut CommandTransaction, total_rows: usize, body: F) -> Result<R>
89 where
90 F: FnOnce(&mut CommandTransaction) -> Result<R>;
91}
92
93pub struct Validated;
94impl ValidationMode for Validated {
95 const VALIDATED: bool = true;
96
97 fn run<F, R>(txn: &mut CommandTransaction, total_rows: usize, body: F) -> Result<R>
98 where
99 F: FnOnce(&mut CommandTransaction) -> Result<R>,
100 {
101 run_checked(txn, total_rows, body)
102 }
103}
104
105pub struct Unchecked;
106impl ValidationMode for Unchecked {
107 const VALIDATED: bool = false;
108
109 fn run<F, R>(txn: &mut CommandTransaction, _total_rows: usize, body: F) -> Result<R>
110 where
111 F: FnOnce(&mut CommandTransaction) -> Result<R>,
112 {
113 txn.execute_bulk_unchecked(body)
114 }
115}
116
117fn run_checked<F, R>(txn: &mut CommandTransaction, total_rows: usize, body: F) -> Result<R>
118where
119 F: FnOnce(&mut CommandTransaction) -> Result<R>,
120{
121 if total_rows > 0 {
122 txn.reserve_writes(total_rows.saturating_mul(2))?;
123 }
124 let r = body(txn)?;
125 txn.commit()?;
126 Ok(r)
127}
128
129pub mod sealed {
130
131 use super::{Unchecked, Validated};
132 pub trait Sealed {}
133 impl Sealed for Validated {}
134 impl Sealed for Unchecked {}
135}
136
137pub struct BulkInsertBuilder<'e, V: ValidationMode = Validated> {
138 engine: &'e StandardEngine,
139 identity: IdentityId,
140 pending_tables: Vec<PendingTableInsert>,
141 pending_ringbuffers: Vec<PendingRingBufferInsert>,
142 pending_series: Vec<PendingSeriesInsert>,
143 _validation: PhantomData<V>,
144}
145
146impl<'e> BulkInsertBuilder<'e, Validated> {
147 pub(crate) fn new(engine: &'e StandardEngine, identity: IdentityId) -> Self {
148 Self {
149 engine,
150 identity,
151 pending_tables: Vec::new(),
152 pending_ringbuffers: Vec::new(),
153 pending_series: Vec::new(),
154 _validation: PhantomData,
155 }
156 }
157}
158
159impl<'e> BulkInsertBuilder<'e, Unchecked> {
160 pub(crate) fn new_unchecked(engine: &'e StandardEngine, identity: IdentityId) -> Self {
161 Self {
162 engine,
163 identity,
164 pending_tables: Vec::new(),
165 pending_ringbuffers: Vec::new(),
166 pending_series: Vec::new(),
167 _validation: PhantomData,
168 }
169 }
170}
171
172impl<'e, V: ValidationMode> BulkInsertBuilder<'e, V> {
173 pub fn table<'a>(&'a mut self, qualified_name: &str) -> TableInsertBuilder<'a, 'e, V> {
174 let (namespace, table) = parse_qualified_name(qualified_name);
175 TableInsertBuilder::new(self, namespace, table)
176 }
177
178 pub fn ringbuffer<'a>(&'a mut self, qualified_name: &str) -> RingBufferInsertBuilder<'a, 'e, V> {
179 let (namespace, ringbuffer) = parse_qualified_name(qualified_name);
180 RingBufferInsertBuilder::new(self, namespace, ringbuffer)
181 }
182
183 pub fn series<'a>(&'a mut self, qualified_name: &str) -> SeriesInsertBuilder<'a, 'e, V> {
184 let (namespace, series) = parse_qualified_name(qualified_name);
185 SeriesInsertBuilder::new(self, namespace, series)
186 }
187
188 pub(super) fn add_table_insert(&mut self, pending: PendingTableInsert) {
189 self.pending_tables.push(pending);
190 }
191
192 pub(super) fn add_ringbuffer_insert(&mut self, pending: PendingRingBufferInsert) {
193 self.pending_ringbuffers.push(pending);
194 }
195
196 pub(super) fn add_series_insert(&mut self, pending: PendingSeriesInsert) {
197 self.pending_series.push(pending);
198 }
199
200 pub fn execute(self) -> Result<BulkInsertResult> {
201 self.run()
202 }
203
204 fn run(self) -> Result<BulkInsertResult> {
205 self.engine.reject_if_read_only()?;
206 let mut txn = self.engine.begin_command(self.identity, Durability::Async)?;
207 let catalog = self.engine.catalog();
208 let services = self.engine.services();
209 let clock = self.engine.clock();
210 let total_rows = self.total_pending_rows();
211 let pending_tables = self.pending_tables;
212 let pending_ringbuffers = self.pending_ringbuffers;
213 let pending_series = self.pending_series;
214 let identity = self.identity;
215 let checked_commit =
216 !pending_ringbuffers.is_empty() || has_keyed_table(&catalog, &mut txn, &pending_tables)?;
217
218 let body = move |txn: &mut CommandTransaction| {
219 let writer = Writer {
220 catalog: &catalog,
221 services: &services,
222 now: clock.now(),
223 coercer: RowCoercer::new(identity),
224 };
225 load_flow_diff_need(writer.catalog, &mut Transaction::Command(txn))?;
226 let result = run_all_pending::<V>(
227 &writer,
228 txn,
229 pending_tables,
230 pending_ringbuffers,
231 pending_series,
232 )?;
233 sync_transactional_views(writer.services, Transaction::Command(txn))?;
234 Ok(result)
235 };
236 if checked_commit {
237 run_checked(&mut txn, total_rows, body)
238 } else {
239 V::run(&mut txn, total_rows, body)
240 }
241 }
242
243 #[inline]
244 fn total_pending_rows(&self) -> usize {
245 self.pending_tables.iter().map(|p| p.row_count()).sum::<usize>()
246 + self.pending_ringbuffers.iter().map(|p| p.row_count()).sum::<usize>()
247 + self.pending_series.iter().map(|p| p.row_count()).sum::<usize>()
248 }
249}
250
251struct Writer<'a> {
252 catalog: &'a Catalog,
253 services: &'a Arc<Services>,
254 now: DateTime,
255 coercer: RowCoercer,
256}
257
258#[inline]
259fn run_all_pending<V: ValidationMode>(
260 writer: &Writer<'_>,
261 txn: &mut CommandTransaction,
262 pending_tables: Vec<PendingTableInsert>,
263 pending_ringbuffers: Vec<PendingRingBufferInsert>,
264 pending_series: Vec<PendingSeriesInsert>,
265) -> Result<BulkInsertResult> {
266 let mut result = BulkInsertResult::default();
267 for pending in pending_tables {
268 result.tables.push(execute_table_insert::<V>(writer, txn, &pending)?);
269 }
270 for pending in pending_ringbuffers {
271 result.ringbuffers.push(execute_ringbuffer_insert::<V>(writer, txn, &pending)?);
272 }
273 for pending in pending_series {
274 result.series.push(execute_series_insert::<V>(writer, txn, &pending)?);
275 }
276 Ok(result)
277}
278
279struct Checks<'a> {
280 name: &'a str,
281 columns: &'a [Column],
282 filled: &'a [Vec<usize>],
283 series: Option<&'a Series>,
284 time: &'a TimeSource,
285}
286
287struct Checked {
288 keys: Vec<u64>,
289 tags: Vec<Option<u8>>,
290}
291
292fn check_rows<V: ValidationMode>(
293 checks: &Checks<'_>,
294 columns: &[(FieldRef, ArrayRef)],
295 tags: Option<CoercedTags>,
296 rows: usize,
297) -> Result<Checked> {
298 let views = columns.iter().map(ColumnView::try_from).collect::<Result<Vec<_>>>()?;
299 let mut failure: Option<(usize, Error)> = None;
300 let limit = |failure: &Option<(usize, Error)>| failure.as_ref().map_or(rows, |(row, _)| *row);
301
302 if V::VALIDATED {
303 for ((column, view), filled) in checks.columns.iter().zip(&views).zip(checks.filled) {
304 if let Some(row) = first_constraint_failure(column, view, filled, limit(&failure)) {
305 failure = Some((row, constraint_error(column, view.get_value(row), checks.name, row)));
306 }
307 }
308 }
309
310 let mut keys = Vec::new();
311 if let Some(series) = checks.series {
312 let index = key_column_index(series)?;
313 let (column, view) = (&checks.columns[index], &views[index]);
314 let given = series.key.keys_to_u64(view);
315 let dictionary_key = column.dictionary_id.is_some();
316 for (row, key) in given.iter().enumerate().take(limit(&failure)) {
317 if view.none_at(row) {
318 failure = Some((row, constraint_error(column, view.get_value(row), checks.name, row)));
319 break;
320 }
321 if dictionary_key || key.is_none() {
322 failure = Some((row, key_out_of_range(&series.key, &view.get_value(row))));
323 break;
324 }
325 }
326 if failure.is_none() {
327 keys = given.into_iter().collect::<Option<Vec<u64>>>().ok_or_else(|| {
328 internal_error!("bulk insert into series {} kept a row without a key", series.name)
329 })?;
330 }
331 }
332
333 let mut variant_tags = vec![None; rows];
334 if let Some((tags, tag_failure)) = tags {
335 variant_tags = tags;
336 if let Some((row, error)) = tag_failure
337 && row < limit(&failure)
338 {
339 failure = Some((row, error));
340 }
341 }
342
343 if let TimeSource::Event {
344 ts,
345 } = checks.time && let Some(index) = checks.columns.iter().position(|column| &column.name == ts)
346 && checks.columns[index].dictionary_id.is_none()
347 {
348 let view = &views[index];
349 if let Some(row) = (0..limit(&failure)).find(|&row| view.none_at(row)) {
350 failure = Some((
351 row,
352 EngineError::TimePopulatorNotDateTime {
353 object: checks.name.to_string(),
354 column: ts.to_string(),
355 found: format!("{:?}", Value::none()),
356 }
357 .into(),
358 ));
359 }
360 }
361
362 if let Some((_, error)) = failure {
363 return Err(error);
364 }
365 Ok(Checked {
366 keys,
367 tags: variant_tags,
368 })
369}
370
371fn first_constraint_failure(column: &Column, view: &ColumnView<'_>, filled: &[usize], limit: usize) -> Option<usize> {
372 let target = column.constraint.get_type();
373 let max_bytes = match column.constraint.constraint() {
374 Some(Constraint::MaxBytes(max)) => Some(usize::from(*max)),
375 _ => None,
376 };
377 (0..limit).find(|&row| {
378 if view.none_at(row) {
379 return !target.is_option() && filled.binary_search(&row).is_err();
380 }
381 match (target.inner_type(), max_bytes) {
382 (ValueType::Utf8, Some(max)) => view.get_str(row).is_some_and(|text| text.len() > max),
383 (ValueType::Blob, Some(max)) => view.get_bytes(row).is_some_and(|bytes| bytes.len() > max),
384 _ => false,
385 }
386 })
387}
388
389fn constraint_error(column: &Column, mut value: Value, name: &str, row: usize) -> Error {
390 let mut error = match column.constraint.coerce(&mut value) {
391 Err(error) => error,
392 Ok(()) => internal_error!("bulk column {} passed the constraint its scan refused", column.name),
393 };
394 error.0.fragment = Fragment::internal(&column.name);
395 with_row_note(error, name, row)
396}
397
398fn key_column_index(series: &Series) -> Result<usize> {
399 let key_col_name = series.key.column();
400 series.columns
401 .iter()
402 .position(|c| c.name == key_col_name)
403 .ok_or_else(|| internal_error!("series {} key column {} not found", series.name, key_col_name))
404}
405
406fn enforce_write_policies(
407 writer: &Writer<'_>,
408 txn: &mut CommandTransaction,
409 namespace: &Namespace,
410 object: &str,
411 target: PolicyTargetType,
412 columns: &[(FieldRef, ArrayRef)],
413) -> Result<()> {
414 let symbols = SymbolTable::new();
415 PolicyEvaluator::new(writer.services, &symbols).enforce_write_policies(
416 &mut Transaction::Command(txn),
417 namespace.name(),
418 object,
419 DataOp::Insert,
420 &batch(columns.to_vec())?,
421 target,
422 )
423}
424
425fn stamp<B: SourceRowBuilder>(row: &mut B, times: &RowTime<'_>, index: usize, now: DateTime) -> Result<()> {
426 row.set_timestamps(now, now);
427 if let Some(time) = times.inserted(index, row.as_slice(), now)? {
428 row.set_time(time);
429 }
430 Ok(())
431}
432
433fn execute_table_insert<V: ValidationMode>(
434 writer: &Writer<'_>,
435 txn: &mut CommandTransaction,
436 pending: &PendingTableInsert,
437) -> Result<TableInsertResult> {
438 let catalog = writer.catalog;
439 let (namespace, table) = resolve_table(catalog, txn, pending)?;
440 let shape = get_or_create_table_shape(catalog, &table, &mut Transaction::Command(txn))?;
441 let (columns, _) =
442 coerce_columns(&pending.rows, &pending.batches, &table.columns, None, &table.name, &writer.coercer)?;
443 let rows = pending.row_count();
444 if rows == 0 {
445 return Ok(empty_table_result(pending));
446 }
447 enforce_write_policies(writer, txn, &namespace, &table.name, PolicyTargetType::Table, &columns)?;
448
449 let filled = auto_increment_rows(&table.columns, &columns)?;
450 check_rows::<V>(
451 &Checks {
452 name: &table.name,
453 columns: &table.columns,
454 filled: &filled,
455 series: None,
456 time: &table.time,
457 },
458 &columns,
459 None,
460 rows,
461 )?;
462 let fills = filled.into_iter().enumerate().filter(|(_, rows)| !rows.is_empty()).collect();
463 let mut cast = [CastColumns::with_fills(columns, fills, rows)];
464 intern_dictionary_columns(catalog, &mut Transaction::Command(txn), &table.columns, None, &mut cast)?;
465 fill_sequences(catalog, &mut Transaction::Command(txn), &table.columns, table.id.into(), &mut cast)?;
466 let mut encoded = EncodedTableRows {
467 rows: (0..rows).map(|_| shape.allocate_table()).collect(),
468 ..EncodedTableRows::default()
469 };
470 cast[0].write(&shape, &mut encoded.rows)?;
471 let now = writer.now;
472 let times = RowTime::new(
473 &table.name,
474 &table.time,
475 &shape,
476 populator_index(&table.time, &shape).map(|index| cast[0].view(index)).transpose()?,
477 );
478 for (index, row) in encoded.rows.iter_mut().enumerate() {
479 stamp(row, ×, index, now)?;
480 }
481 table_batch_partitions(&table, &cast[0], &mut encoded)?;
482 write_table_rows(
483 catalog,
484 &mut Transaction::Command(txn),
485 &table,
486 &shape,
487 &mut encoded,
488 &Violation::BulkRow(&table.name),
489 )?;
490 Ok(TableInsertResult {
491 namespace: pending.namespace.clone(),
492 table: pending.table.clone(),
493 inserted: rows as u64,
494 })
495}
496
497fn auto_increment_rows(columns: &[Column], cast: &[(FieldRef, ArrayRef)]) -> Result<Vec<Vec<usize>>> {
498 columns.iter()
499 .zip(cast)
500 .map(|(column, cast)| {
501 if !column.auto_increment {
502 return Ok(Vec::new());
503 }
504 let view = ColumnView::try_from(cast)?;
505 Ok((0..view.len()).filter(|&row| view.none_at(row)).collect())
506 })
507 .collect()
508}
509
510#[inline]
511fn empty_table_result(pending: &PendingTableInsert) -> TableInsertResult {
512 TableInsertResult {
513 namespace: pending.namespace.clone(),
514 table: pending.table.clone(),
515 inserted: 0,
516 }
517}
518
519fn resolve_namespace(catalog: &Catalog, txn: &mut CommandTransaction, name: &str) -> Result<Namespace> {
520 catalog.find_namespace_by_name(&mut Transaction::Command(txn), name)?.ok_or_else(|| {
521 CatalogError::NotFound {
522 kind: CatalogObjectKind::Namespace,
523 namespace: name.to_string(),
524 name: String::new(),
525 fragment: Fragment::None,
526 }
527 .into()
528 })
529}
530
531fn has_keyed_table(
532 catalog: &Catalog,
533 txn: &mut CommandTransaction,
534 pending_tables: &[PendingTableInsert],
535) -> Result<bool> {
536 for pending in pending_tables {
537 let (_, table) = resolve_table(catalog, txn, pending)?;
538 if get_primary_key(catalog, &mut Transaction::Command(txn), &table)?.is_some() {
539 return Ok(true);
540 }
541 }
542 Ok(false)
543}
544
545fn resolve_table(
546 catalog: &Catalog,
547 txn: &mut CommandTransaction,
548 pending: &PendingTableInsert,
549) -> Result<(Namespace, Table)> {
550 let namespace = resolve_namespace(catalog, txn, &pending.namespace)?;
551 let table = catalog
552 .find_table_by_name(&mut Transaction::Command(txn), namespace.id(), &pending.table)?
553 .ok_or_else(|| -> Error {
554 CatalogError::NotFound {
555 kind: CatalogObjectKind::Table,
556 namespace: pending.namespace.to_string(),
557 name: pending.table.to_string(),
558 fragment: Fragment::None,
559 }
560 .into()
561 })?;
562 Ok((namespace, table))
563}
564
565fn execute_ringbuffer_insert<V: ValidationMode>(
566 writer: &Writer<'_>,
567 txn: &mut CommandTransaction,
568 pending: &PendingRingBufferInsert,
569) -> Result<RingBufferInsertResult> {
570 let catalog = writer.catalog;
571 let (namespace, ringbuffer) = resolve_ringbuffer(catalog, txn, pending)?;
572 let shape = get_or_create_ringbuffer_shape(catalog, &ringbuffer, &mut Transaction::Command(txn))?;
573 let (columns, _) = coerce_columns(
574 &pending.rows,
575 &pending.batches,
576 &ringbuffer.columns,
577 None,
578 &ringbuffer.name,
579 &writer.coercer,
580 )?;
581 let rows = pending.row_count();
582 let inserted = if rows == 0 {
583 0
584 } else {
585 insert_ringbuffer_rows::<V>(writer, txn, &namespace, &ringbuffer, &shape, columns, rows)?
586 };
587 Ok(RingBufferInsertResult {
588 namespace: pending.namespace.clone(),
589 ringbuffer: pending.ringbuffer.clone(),
590 inserted,
591 })
592}
593
594#[inline]
595fn resolve_ringbuffer(
596 catalog: &Catalog,
597 txn: &mut CommandTransaction,
598 pending: &PendingRingBufferInsert,
599) -> Result<(Namespace, RingBuffer)> {
600 let namespace = resolve_namespace(catalog, txn, &pending.namespace)?;
601 let ringbuffer = catalog
602 .find_ringbuffer_by_name(&mut Transaction::Command(txn), namespace.id(), &pending.ringbuffer)?
603 .ok_or_else(|| -> Error {
604 CatalogError::NotFound {
605 kind: CatalogObjectKind::RingBuffer,
606 namespace: pending.namespace.to_string(),
607 name: pending.ringbuffer.to_string(),
608 fragment: Fragment::None,
609 }
610 .into()
611 })?;
612 Ok((namespace, ringbuffer))
613}
614
615fn insert_ringbuffer_rows<V: ValidationMode>(
616 writer: &Writer<'_>,
617 txn: &mut CommandTransaction,
618 namespace: &Namespace,
619 ringbuffer: &RingBuffer,
620 shape: &RowShape,
621 columns: Vec<(FieldRef, ArrayRef)>,
622 rows: usize,
623) -> Result<u64> {
624 let catalog = writer.catalog;
625 enforce_write_policies(writer, txn, namespace, &ringbuffer.name, PolicyTargetType::RingBuffer, &columns)?;
626 check_rows::<V>(
627 &Checks {
628 name: &ringbuffer.name,
629 columns: &ringbuffer.columns,
630 filled: &vec![Vec::new(); ringbuffer.columns.len()],
631 series: None,
632 time: &ringbuffer.time,
633 },
634 &columns,
635 None,
636 rows,
637 )?;
638
639 let mut cast = [CastColumns::new(columns, rows)];
640 intern_dictionary_columns(catalog, &mut Transaction::Command(txn), &ringbuffer.columns, None, &mut cast)?;
641 let mut built: Vec<_> = (0..rows).map(|_| shape.allocate_ringbuffer()).collect();
642 cast[0].write(shape, &mut built)?;
643
644 let batch = ringbuffer_batch_partitions(ringbuffer, &cast[0])?;
645 let mut verified = HashMap::new();
646 resolve_partitions(
647 &mut Transaction::Command(txn),
648 ObjectId::ringbuffer(ringbuffer.id),
649 &batch.distinct,
650 &mut verified,
651 )?;
652 let now = writer.now;
653 let times = RowTime::new(
654 &ringbuffer.name,
655 &ringbuffer.time,
656 shape,
657 populator_index(&ringbuffer.time, shape).map(|index| cast[0].view(index)).transpose()?,
658 );
659 let mut entries = Vec::with_capacity(rows);
660 for (row, mut builder) in built.into_iter().enumerate() {
661 stamp(&mut builder, ×, row, now)?;
662 entries.push(builder.freeze_bytes());
663 }
664
665 let mut cache = HashMap::new();
666 let inserted = insert_ringbuffer_chunks(
667 catalog,
668 &mut Transaction::Command(txn),
669 ringbuffer,
670 shape,
671 &entries,
672 &batch.rows,
673 &verified,
674 &mut cache,
675 None,
676 )?;
677 save_all_partition_metadata(catalog, &mut Transaction::Command(txn), ringbuffer, &cache)?;
678 Ok(inserted)
679}
680
681fn execute_series_insert<V: ValidationMode>(
682 writer: &Writer<'_>,
683 txn: &mut CommandTransaction,
684 pending: &PendingSeriesInsert,
685) -> Result<SeriesInsertResult> {
686 let catalog = writer.catalog;
687 let (namespace, series) = resolve_series(catalog, txn, pending)?;
688 let mut metadata_by_partition: HashMap<Partition, SeriesPartitionMetadata> = HashMap::new();
689 let mut inserted_keys: HashMap<Partition, Vec<u64>> = HashMap::new();
690 let shape = get_or_create_series_shape(catalog, &series, &mut Transaction::Command(txn))?;
691 let sumtype = series.tag.map(|id| catalog.get_sumtype(&mut Transaction::Command(txn), id)).transpose()?;
692 let (columns, tags) = coerce_columns(
693 &pending.rows,
694 &pending.batches,
695 &series.columns,
696 sumtype.as_ref(),
697 &series.name,
698 &writer.coercer,
699 )?;
700 let rows = pending.row_count();
701 let inserted = if rows == 0 {
702 0
703 } else {
704 enforce_write_policies(writer, txn, &namespace, &series.name, PolicyTargetType::Series, &columns)?;
705 let checked = check_rows::<V>(
706 &Checks {
707 name: &series.name,
708 columns: &series.columns,
709 filled: &vec![Vec::new(); series.columns.len()],
710 series: Some(&series),
711 time: &series.time,
712 },
713 &columns,
714 tags,
715 rows,
716 )?;
717 insert_series_rows(
718 writer,
719 txn,
720 &series,
721 &shape,
722 columns,
723 checked,
724 &mut metadata_by_partition,
725 &mut inserted_keys,
726 )?
727 };
728 let now = writer.now;
729 for (partition, metadata) in metadata_by_partition {
730 let keys = inserted_keys.remove(&partition).unwrap_or_default();
731 write_inserted_series_metadata(
732 catalog,
733 &mut Transaction::Command(txn),
734 &series,
735 partition,
736 metadata,
737 keys,
738 now,
739 V::VALIDATED,
740 )?;
741 if !V::VALIDATED {
742 txn.rewrite_on(
743 &SeriesPartitionMetadataKey::new(series.id, partition).into(),
744 renumber_series_rows(StorageId::series(series.id), partition),
745 )?;
746 }
747 }
748 Ok(SeriesInsertResult {
749 namespace: pending.namespace.clone(),
750 series: pending.series.clone(),
751 inserted,
752 })
753}
754
755#[inline]
756fn resolve_series(
757 catalog: &Catalog,
758 txn: &mut CommandTransaction,
759 pending: &PendingSeriesInsert,
760) -> Result<(Namespace, Series)> {
761 let namespace = resolve_namespace(catalog, txn, &pending.namespace)?;
762 let series = catalog
763 .find_series_by_name(&mut Transaction::Command(txn), namespace.id(), &pending.series)?
764 .ok_or_else(|| -> Error {
765 CatalogError::NotFound {
766 kind: CatalogObjectKind::Series,
767 namespace: pending.namespace.to_string(),
768 name: pending.series.to_string(),
769 fragment: Fragment::None,
770 }
771 .into()
772 })?;
773 Ok((namespace, series))
774}
775
776fn insert_series_rows(
777 writer: &Writer<'_>,
778 txn: &mut CommandTransaction,
779 series: &Series,
780 shape: &RowShape,
781 columns: Vec<(FieldRef, ArrayRef)>,
782 checked: Checked,
783 metadata_by_partition: &mut HashMap<Partition, SeriesPartitionMetadata>,
784 inserted_keys: &mut HashMap<Partition, Vec<u64>>,
785) -> Result<u64> {
786 let catalog = writer.catalog;
787 let rows = checked.keys.len();
788 let key_index = key_column_index(series)?;
789 let mut cast = [CastColumns::new(columns, rows)];
790 intern_dictionary_columns(
791 catalog,
792 &mut Transaction::Command(txn),
793 &series.columns,
794 Some(&series.key),
795 &mut cast,
796 )?;
797 let [cast] = cast;
798
799 let key_column = series.key_column_data(checked.keys.clone());
800 let mut built: Vec<_> = (0..rows).map(|_| shape.allocate_series()).collect();
801 let mut views = Vec::with_capacity(series.columns.len());
802 views.push(ColumnView::try_from(&key_column)?);
803 for index in 0..series.columns.len() {
804 if index != key_index {
805 views.push(cast.view(index)?);
806 }
807 }
808 shape.write_columns(&mut built, &views)?;
809 let now = writer.now;
810 let times = RowTime::new(
811 &series.name,
812 &series.time,
813 shape,
814 populator_index(&series.time, shape).map(|index| views[index].clone()),
815 );
816
817 let batch = series_batch_partitions(series, &cast, Some(&views[0]))?;
818 resolve_partitions(
819 &mut Transaction::Command(txn),
820 ObjectId::Series(series.id),
821 &batch.distinct,
822 &mut HashMap::new(),
823 )?;
824 let assigned = assign_series_keys(
825 catalog,
826 &mut Transaction::Command(txn),
827 series,
828 &batch.rows,
829 checked.keys.into_iter().map(Some).collect(),
830 &checked.tags,
831 metadata_by_partition,
832 |_| Err(internal_error!("bulk insert into series {} kept a row without a key", series.name)),
833 )?;
834 for (row, &key) in assigned.keys.iter().enumerate() {
835 inserted_keys.entry(batch.rows.get(row).copied().unwrap_or_default()).or_default().push(key);
836 }
837 for (row, builder) in built.iter_mut().enumerate() {
838 stamp(builder, ×, row, now)?;
839 }
840 let written = write_series_rows(
841 &mut Transaction::Command(txn),
842 series,
843 shape,
844 SeriesRows {
845 keys: assigned.storage,
846 partitions: &batch.rows,
847 rows: built,
848 },
849 )?;
850 Ok(written.len() as u64)
851}
852
853fn renumber_series_rows(storage: StorageId, partition: Partition) -> Rewrite {
854 Arc::new(move |deltas, at_base, at_latest| {
855 let counter = |bytes: Option<&EncodedBytes>| -> Result<u64> {
856 Ok(match bytes {
857 Some(bytes) => {
858 decode_series_partition_metadata(EncodedPodRow::view(bytes))?.sequence_counter
859 }
860 None => 0,
861 })
862 };
863 let base = counter(at_base)?;
864 let gap = counter(at_latest)?.checked_sub(base).ok_or_else(|| {
865 internal_error!(
866 "series {:?} partition counter fell below the bulk's snapshot counter {}",
867 storage,
868 base
869 )
870 })?;
871 if gap == 0 {
872 return Ok(());
873 }
874 for delta in deltas.make_mut() {
875 if let Delta::Set {
876 key,
877 ..
878 } = delta
879 {
880 match key {
881 TaggedKey::SeriesRow(row) if row.storage == storage && row.sequence > base => {
882 row.sequence += gap;
883 }
884 TaggedKey::PartitionedSeriesRow(row)
885 if row.storage == storage
886 && row.partition == partition && row.sequence > base =>
887 {
888 row.sequence += gap;
889 }
890 _ => {}
891 }
892 }
893 }
894 Ok(())
895 })
896}
897
898fn parse_qualified_name(qualified_name: &str) -> (String, String) {
899 if let Some((ns, name)) = qualified_name.rsplit_once("::") {
900 (ns.to_string(), name.to_string())
901 } else {
902 ("default".to_string(), qualified_name.to_string())
903 }
904}
905
906#[cfg(test)]
907mod tests {
908 use super::*;
909
910 #[test]
911 fn parse_qualified_name_simple() {
912 assert_eq!(parse_qualified_name("table"), ("default".to_string(), "table".to_string()));
913 }
914
915 #[test]
916 fn parse_qualified_name_single_namespace() {
917 assert_eq!(parse_qualified_name("ns::table"), ("ns".to_string(), "table".to_string()));
918 }
919
920 #[test]
921 fn parse_qualified_name_nested_namespace() {
922 assert_eq!(parse_qualified_name("a::b::table"), ("a::b".to_string(), "table".to_string()));
923 }
924
925 #[test]
926 fn parse_qualified_name_deeply_nested_namespace() {
927 assert_eq!(parse_qualified_name("a::b::c::table"), ("a::b::c".to_string(), "table".to_string()));
928 }
929
930 #[test]
931 fn parse_qualified_name_empty_string() {
932 assert_eq!(parse_qualified_name(""), ("default".to_string(), "".to_string()));
933 }
934}