1use std::collections::BTreeMap;
16use std::sync::Arc;
17
18use ahash::{HashMap, HashMapExt, HashSet, HashSetExt};
19use api::v1::alter_table_expr::Kind;
20use api::v1::column_def::options_from_skipping;
21use api::v1::region::{
22 InsertRequest as RegionInsertRequest, InsertRequests as RegionInsertRequests,
23 RegionRequestHeader,
24};
25use api::v1::{
26 AlterTableExpr, ColumnDataType, ColumnSchema, CreateTableExpr, InsertRequests,
27 RowInsertRequest, RowInsertRequests, Rows, SemanticType,
28};
29use catalog::CatalogManagerRef;
30use client::{OutputData, OutputMeta};
31use common_catalog::consts::{
32 DEFAULT_PRIVATE_SCHEMA_NAME, PARENT_SPAN_ID_COLUMN, SERVICE_NAME_COLUMN, TRACE_ID_COLUMN,
33 TRACE_TABLE_NAME, TRACE_TABLE_NAME_SESSION_KEY, default_engine, is_ddl_reserved_table,
34 trace_operations_table_name, trace_services_table_name,
35};
36use common_event_recorder::DEFAULT_EVENTS_TABLE_NAME;
37use common_frontend::slow_query_event::SLOW_QUERY_TABLE_NAME;
38use common_grpc_expr::util::ColumnExpr;
39use common_meta::cache::TableFlownodeSetCacheRef;
40use common_meta::datanode::REGION_STATS_HISTORY_TABLE_NAME;
41use common_meta::node_manager::{AffectedRows, NodeManagerRef};
42use common_meta::peer::Peer;
43use common_meta::rpc::ddl::TriggerReason;
44use common_query::Output;
45use common_query::native_histogram::{is_native_histogram_value_type, native_histogram_value_type};
46use common_query::prelude::{greptime_timestamp, greptime_value};
47use common_telemetry::tracing_context::TracingContext;
48use common_telemetry::{debug, error, warn};
49use datatypes::schema::SkippingIndexOptions;
50use futures_util::future;
51use meter_macros::write_meter;
52use partition::manager::PartitionRuleManagerRef;
53use session::context::QueryContextRef;
54use snafu::ResultExt;
55use snafu::prelude::*;
56use sql::partition::partition_rule_for_hexstring;
57use sql::statements::create::Partitions;
58use sql::statements::insert::Insert;
59use store_api::metric_engine_consts::{
60 LOGICAL_TABLE_METADATA_KEY, METRIC_ENGINE_NAME, PHYSICAL_TABLE_METADATA_KEY,
61};
62use store_api::mito_engine_options::{
63 APPEND_MODE_KEY, COMPACTION_TYPE, COMPACTION_TYPE_TWCS, MERGE_MODE_KEY, TTL_KEY,
64 TWCS_TIME_WINDOW,
65};
66use store_api::storage::{RegionId, TableId};
67use table::TableRef;
68use table::metadata::{TableInfo, TableInfoRef};
69use table::requests::{
70 AUTO_CREATE_TABLE_KEY, InsertRequest as TableInsertRequest, SEMANTIC_PER_TABLE_INDEX_KEY,
71 TABLE_DATA_MODEL, TABLE_DATA_MODEL_TRACE_V1, TRACE_TABLE_PARTITIONS_HINT_KEY,
72 VALID_TABLE_OPTION_KEYS, is_semantic_option_key, validate_semantic_option,
73};
74use table::table_reference::TableReference;
75
76use crate::batcher::PendingRowsBatcher;
77use crate::error::{
78 CatalogSnafu, ColumnOptionsSnafu, CreatePartitionRulesSnafu, FindRegionLeaderSnafu,
79 InvalidInsertRequestSnafu, JoinTaskSnafu, RequestInsertsSnafu, Result, TableNotFoundSnafu,
80};
81use crate::expr_helper;
82use crate::region_req_factory::RegionRequestFactory;
83use crate::req_convert::common::preprocess_row_insert_requests;
84use crate::req_convert::insert::{
85 ColumnToRow, ImpureDefaultFiller, RowToRegion, StatementToRegion, TableToRegion,
86 fill_reqs_with_impure_default, rows_to_record_batch,
87};
88use crate::statement::StatementExecutor;
89
90pub struct Inserter {
91 catalog_manager: CatalogManagerRef,
92 pub(crate) partition_manager: PartitionRuleManagerRef,
93 pub(crate) node_manager: NodeManagerRef,
94 pub(crate) table_flownode_set_cache: TableFlownodeSetCacheRef,
95 auto_create_table: bool,
99 pending_rows_batcher: Option<Arc<dyn PendingRowsBatcher>>,
100}
101
102pub type InserterRef = Arc<Inserter>;
103
104#[derive(Clone)]
106pub enum AutoCreateTableType {
107 Logical(String),
109 Physical,
111 Log,
113 LastNonNull,
115 Trace { alter_existing: bool },
117}
118
119impl AutoCreateTableType {
120 pub fn as_str(&self) -> &'static str {
121 match self {
122 AutoCreateTableType::Logical(_) => "logical",
123 AutoCreateTableType::Physical => "physical",
124 AutoCreateTableType::Log => "log",
125 AutoCreateTableType::LastNonNull => "last_non_null",
126 AutoCreateTableType::Trace { .. } => "trace",
127 }
128 }
129
130 fn alter_existing(&self) -> bool {
131 !matches!(
132 self,
133 Self::Trace {
134 alter_existing: false
135 }
136 )
137 }
138}
139
140#[derive(Clone)]
147pub struct InstantAndNormalInsertRequests {
148 pub normal_requests: RegionInsertRequests,
150 pub instant_requests: RegionInsertRequests,
153}
154
155impl Inserter {
156 pub fn new(
157 catalog_manager: CatalogManagerRef,
158 partition_manager: PartitionRuleManagerRef,
159 node_manager: NodeManagerRef,
160 table_flownode_set_cache: TableFlownodeSetCacheRef,
161 auto_create_table: bool,
162 ) -> Self {
163 Self {
164 catalog_manager,
165 partition_manager,
166 node_manager,
167 table_flownode_set_cache,
168 auto_create_table,
169 pending_rows_batcher: None,
170 }
171 }
172
173 pub fn with_pending_rows_batcher(
175 mut self,
176 batcher: Option<Arc<dyn PendingRowsBatcher>>,
177 ) -> Self {
178 self.pending_rows_batcher = batcher;
179 self
180 }
181
182 pub async fn handle_column_inserts(
183 &self,
184 requests: InsertRequests,
185 ctx: QueryContextRef,
186 statement_executor: &StatementExecutor,
187 ) -> Result<Output> {
188 let row_inserts = ColumnToRow::convert(requests)?;
189 self.handle_row_inserts(row_inserts, ctx, statement_executor, false, false)
190 .await
191 }
192
193 pub async fn handle_row_inserts(
195 &self,
196 mut requests: RowInsertRequests,
197 ctx: QueryContextRef,
198 statement_executor: &StatementExecutor,
199 accommodate_existing_schema: bool,
200 is_single_value: bool,
201 ) -> Result<Output> {
202 preprocess_row_insert_requests(&mut requests.inserts)?;
203 self.handle_row_inserts_with_create_type(
204 requests,
205 ctx,
206 statement_executor,
207 AutoCreateTableType::Physical,
208 accommodate_existing_schema,
209 is_single_value,
210 )
211 .await
212 }
213
214 pub async fn handle_log_inserts(
216 &self,
217 requests: RowInsertRequests,
218 ctx: QueryContextRef,
219 statement_executor: &StatementExecutor,
220 ) -> Result<Output> {
221 self.handle_row_inserts_with_create_type(
222 requests,
223 ctx,
224 statement_executor,
225 AutoCreateTableType::Log,
226 false,
227 false,
228 )
229 .await
230 }
231
232 pub async fn handle_trace_inserts(
233 &self,
234 requests: RowInsertRequests,
235 ctx: QueryContextRef,
236 statement_executor: &StatementExecutor,
237 ) -> Result<Output> {
238 self.handle_row_inserts_with_create_type(
239 requests,
240 ctx,
241 statement_executor,
242 AutoCreateTableType::Trace {
243 alter_existing: true,
244 },
245 false,
246 false,
247 )
248 .await
249 }
250
251 pub async fn handle_last_non_null_inserts(
253 &self,
254 requests: RowInsertRequests,
255 ctx: QueryContextRef,
256 statement_executor: &StatementExecutor,
257 accommodate_existing_schema: bool,
258 is_single_value: bool,
259 ) -> Result<Output> {
260 self.handle_row_inserts_with_create_type(
261 requests,
262 ctx,
263 statement_executor,
264 AutoCreateTableType::LastNonNull,
265 accommodate_existing_schema,
266 is_single_value,
267 )
268 .await
269 }
270
271 async fn handle_row_inserts_with_create_type(
273 &self,
274 mut requests: RowInsertRequests,
275 ctx: QueryContextRef,
276 statement_executor: &StatementExecutor,
277 create_type: AutoCreateTableType,
278 accommodate_existing_schema: bool,
279 is_single_value: bool,
280 ) -> Result<Output> {
281 let skip_wal = ctx.skip_wal();
282
283 let batcher = self
284 .pending_rows_batcher
285 .as_ref()
286 .filter(|_| ctx.batching_enabled());
287
288 requests.inserts.retain(|req| {
290 req.rows
291 .as_ref()
292 .map(|r| !r.rows.is_empty())
293 .unwrap_or_default()
294 });
295 validate_column_count_match(&requests)?;
296
297 let CreateAlterTableResult {
298 instant_table_ids,
299 table_infos,
300 } = self
301 .create_or_alter_tables_on_demand(
302 &mut requests,
303 &ctx,
304 create_type,
305 statement_executor,
306 accommodate_existing_schema,
307 is_single_value,
308 )
309 .await?;
310
311 if let Some(batcher) = batcher
314 && instant_table_ids.is_empty()
315 && table_infos
316 .values()
317 .all(|info| info.meta.engine == default_engine())
318 {
319 return self
320 .submit_pending_rows(requests, table_infos, ctx, batcher)
321 .await;
322 }
323
324 let name_to_info = table_infos
325 .values()
326 .map(|info| (info.name.clone(), info.clone()))
327 .collect::<HashMap<_, _>>();
328 let inserts = RowToRegion::new(
329 name_to_info,
330 instant_table_ids,
331 self.partition_manager.as_ref(),
332 )
333 .convert(requests, skip_wal)
334 .await?;
335
336 self.do_request(inserts, &table_infos, &ctx).await
337 }
338
339 async fn submit_pending_rows(
340 &self,
341 mut requests: RowInsertRequests,
342 table_infos: HashMap<TableId, Arc<TableInfo>>,
343 ctx: QueryContextRef,
344 batcher: &Arc<dyn PendingRowsBatcher>,
345 ) -> Result<Output> {
346 requests.inserts.retain(|request| {
349 request
350 .rows
351 .as_ref()
352 .is_some_and(|rows| !rows.rows.is_empty())
353 });
354 let by_name = table_infos
355 .values()
356 .map(|info| (info.name.as_str(), info))
357 .collect::<HashMap<_, _>>();
358 let mut prepared = Vec::with_capacity(requests.inserts.len());
359 for request in &mut requests.inserts {
360 let table_info =
361 by_name
362 .get(request.table_name.as_str())
363 .context(TableNotFoundSnafu {
364 table_name: &request.table_name,
365 })?;
366 let Some(rows) = &mut request.rows else {
367 continue;
368 };
369 ImpureDefaultFiller::new((*table_info).clone())?.fill_rows(rows);
370 let batch = rows_to_record_batch(rows, table_info)?;
371 prepared.push(((*table_info).clone(), batch));
372 }
373 let metered = InstantAndNormalInsertRequests {
376 normal_requests: RegionInsertRequests {
377 requests: requests
378 .inserts
379 .into_iter()
380 .map(|request| RegionInsertRequest {
381 rows: request.rows,
382 ..Default::default()
383 })
384 .collect(),
385 },
386 instant_requests: RegionInsertRequests::default(),
387 };
388 let write_cost = write_meter!(
389 ctx.current_catalog(),
390 ctx.current_schema(),
391 metered,
392 ctx.channel() as u8
393 );
394 prepared.retain(|(_, batch)| batch.num_rows() != 0);
395 let results = if prepared.is_empty() {
396 Vec::new()
397 } else {
398 let permit = batcher.acquire().await?;
400 let submissions = prepared.into_iter().map(|(info, batch)| {
401 let mut target_ctx = ctx.fork();
404 target_ctx.set_current_catalog(&info.catalog_name);
405 target_ctx.set_current_schema(&info.schema_name);
406 batcher.submit(info, batch, Arc::new(target_ctx), permit.clone())
407 });
408 future::join_all(submissions).await
410 };
411 let affected_rows = results.into_iter().sum::<Result<usize>>()?;
412 Ok(Output::new(
413 OutputData::AffectedRows(affected_rows),
414 OutputMeta::new_with_cost(write_cost as _),
415 ))
416 }
417
418 pub async fn handle_metric_row_inserts(
420 &self,
421 mut requests: RowInsertRequests,
422 ctx: QueryContextRef,
423 statement_executor: &StatementExecutor,
424 physical_table: String,
425 ) -> Result<Output> {
426 let skip_wal = ctx.skip_wal();
427
428 requests.inserts.retain(|req| {
430 req.rows
431 .as_ref()
432 .map(|r| !r.rows.is_empty())
433 .unwrap_or_default()
434 });
435 validate_column_count_match(&requests)?;
436
437 self.create_physical_table_on_demand(&ctx, physical_table.clone(), statement_executor)
439 .await?;
440
441 let CreateAlterTableResult {
443 instant_table_ids,
444 table_infos,
445 } = self
446 .create_or_alter_tables_on_demand(
447 &mut requests,
448 &ctx,
449 AutoCreateTableType::Logical(physical_table.clone()),
450 statement_executor,
451 true,
452 true,
453 )
454 .await?;
455 let name_to_info = table_infos
456 .values()
457 .map(|info| (info.name.clone(), info.clone()))
458 .collect::<HashMap<_, _>>();
459 let inserts = RowToRegion::new(name_to_info, instant_table_ids, &self.partition_manager)
460 .convert(requests, skip_wal)
461 .await?;
462
463 self.do_request(inserts, &table_infos, &ctx).await
464 }
465
466 fn table_batcher(
467 &self,
468 table_info: &TableInfoRef,
469 ctx: &QueryContextRef,
470 ) -> Option<&Arc<dyn PendingRowsBatcher>> {
471 self.pending_rows_batcher.as_ref().filter(|_| {
472 ctx.batching_enabled()
473 && !table_info.is_ttl_instant_table()
474 && table_info.meta.engine == default_engine()
475 })
476 }
477
478 async fn submit_table_rows(
479 &self,
480 rows: Rows,
481 table_info: TableInfoRef,
482 ctx: QueryContextRef,
483 batcher: &Arc<dyn PendingRowsBatcher>,
484 ) -> Result<Output> {
485 let requests = RowInsertRequests {
486 inserts: vec![RowInsertRequest {
487 table_name: table_info.name.clone(),
488 rows: Some(rows),
489 }],
490 };
491 let table_infos = HashMap::from_iter([(table_info.table_id(), table_info)]);
492 self.submit_pending_rows(requests, table_infos, ctx, batcher)
493 .await
494 }
495
496 pub async fn handle_table_insert(
497 &self,
498 request: TableInsertRequest,
499 ctx: QueryContextRef,
500 ) -> Result<Output> {
501 let catalog = request.catalog_name.as_str();
502 let schema = request.schema_name.as_str();
503 let table_name = request.table_name.as_str();
504 let table = self.get_table(catalog, schema, table_name).await?;
505 let table = table.with_context(|| TableNotFoundSnafu {
506 table_name: common_catalog::format_full_table_name(catalog, schema, table_name),
507 })?;
508 let table_info = table.table_info();
509
510 let converter = TableToRegion::new(&table_info, &self.partition_manager);
511 let skip_wal = request.skip_wal;
512 let rows = converter.prepare(request)?;
513 if let Some(batcher) = self.table_batcher(&table_info, &ctx) {
514 return self.submit_table_rows(rows, table_info, ctx, batcher).await;
515 }
516 let inserts = converter.partition(rows, skip_wal).await?;
517
518 let table_infos = HashMap::from_iter([(table_info.table_id(), table_info.clone())]);
519
520 self.do_request(inserts, &table_infos, &ctx).await
521 }
522
523 pub async fn handle_statement_insert(
524 &self,
525 insert: &Insert,
526 ctx: &QueryContextRef,
527 ) -> Result<Output> {
528 let converter =
529 StatementToRegion::new(self.catalog_manager.as_ref(), &self.partition_manager, ctx);
530 let (rows, table_info) = converter.prepare(insert, ctx).await?;
531 if let Some(batcher) = self.table_batcher(&table_info, ctx) {
532 return self
533 .submit_table_rows(rows, table_info, ctx.clone(), batcher)
534 .await;
535 }
536 let inserts = converter.partition(rows, table_info.clone(), ctx).await?;
537
538 let table_infos = HashMap::from_iter([(table_info.table_id(), table_info.clone())]);
539
540 self.do_request(inserts, &table_infos, ctx).await
541 }
542}
543
544impl Inserter {
545 async fn do_request(
546 &self,
547 requests: InstantAndNormalInsertRequests,
548 table_infos: &HashMap<TableId, Arc<TableInfo>>,
549 ctx: &QueryContextRef,
550 ) -> Result<Output> {
551 let requests = fill_reqs_with_impure_default(table_infos, requests)?;
553
554 let write_cost = write_meter!(
555 ctx.current_catalog(),
556 ctx.current_schema(),
557 requests,
558 ctx.channel() as u8
559 );
560 let request_factory = RegionRequestFactory::new(RegionRequestHeader {
561 tracing_context: TracingContext::from_current_span().to_w3c(),
562 dbname: ctx.get_db_string(),
563 ..Default::default()
564 });
565
566 let InstantAndNormalInsertRequests {
567 normal_requests,
568 instant_requests,
569 } = requests;
570
571 let flow_mirror_task = FlowMirrorTask::new(
573 &self.table_flownode_set_cache,
574 normal_requests
575 .requests
576 .iter()
577 .chain(instant_requests.requests.iter()),
578 )
579 .await?;
580 flow_mirror_task.detach(self.node_manager.clone())?;
581
582 let write_tasks = self
584 .group_requests_by_peer(normal_requests)
585 .await?
586 .into_iter()
587 .map(|(peer, inserts)| {
588 let node_manager = self.node_manager.clone();
589 let request = request_factory.build_insert(inserts);
590 common_runtime::spawn_global(async move {
591 node_manager
592 .datanode(&peer)
593 .await
594 .handle(request)
595 .await
596 .context(RequestInsertsSnafu)
597 })
598 });
599 let results = future::try_join_all(write_tasks)
600 .await
601 .context(JoinTaskSnafu)?;
602 let affected_rows = results
603 .into_iter()
604 .map(|resp| resp.map(|r| r.affected_rows))
605 .sum::<Result<AffectedRows>>()?;
606 crate::metrics::DIST_INGEST_ROW_COUNT
607 .with_label_values(&[ctx.get_db_string().as_str()])
608 .inc_by(affected_rows as u64);
609 Ok(Output::new(
610 OutputData::AffectedRows(affected_rows),
611 OutputMeta::new_with_cost(write_cost as _),
612 ))
613 }
614
615 async fn group_requests_by_peer(
616 &self,
617 requests: RegionInsertRequests,
618 ) -> Result<HashMap<Peer, RegionInsertRequests>> {
619 let mut requests_per_region: HashMap<RegionId, RegionInsertRequests> = HashMap::new();
622 for req in requests.requests {
623 let region_id = RegionId::from_u64(req.region_id);
624 requests_per_region
625 .entry(region_id)
626 .or_default()
627 .requests
628 .push(req);
629 }
630
631 let mut inserts: HashMap<Peer, RegionInsertRequests> = HashMap::new();
632
633 for (region_id, reqs) in requests_per_region {
634 let peer = self
635 .partition_manager
636 .find_region_leader(region_id)
637 .await
638 .context(FindRegionLeaderSnafu)?;
639 inserts
640 .entry(peer)
641 .or_default()
642 .requests
643 .extend(reqs.requests);
644 }
645
646 Ok(inserts)
647 }
648
649 fn auto_create_disabled_reason(&self, ctx: &QueryContextRef) -> Result<Option<&'static str>> {
653 let auto_create_table_hint = ctx
654 .extension(AUTO_CREATE_TABLE_KEY)
655 .map(|v| v.parse::<bool>())
656 .transpose()
657 .map_err(|_| {
658 InvalidInsertRequestSnafu {
659 reason: "`auto_create_table` hint must be a boolean",
660 }
661 .build()
662 })?
663 .unwrap_or(true);
664 Ok(if !self.auto_create_table {
665 Some("auto-create table is disabled by frontend config")
666 } else if !auto_create_table_hint {
667 Some("`auto_create_table` hint is disabled")
668 } else {
669 None
670 })
671 }
672
673 fn is_auto_create_exempt_private_table(schema: &str, table: &str) -> bool {
676 schema == DEFAULT_PRIVATE_SCHEMA_NAME
677 && matches!(
678 table,
679 DEFAULT_EVENTS_TABLE_NAME | SLOW_QUERY_TABLE_NAME | REGION_STATS_HISTORY_TABLE_NAME
680 )
681 }
682
683 pub async fn ensure_trace_table_on_demand(
688 &self,
689 table_name: &str,
690 request_schema: Vec<ColumnSchema>,
691 alter_existing: bool,
692 ctx: &QueryContextRef,
693 statement_executor: &StatementExecutor,
694 ) -> Result<()> {
695 let mut requests = RowInsertRequests {
696 inserts: vec![RowInsertRequest {
697 table_name: table_name.to_string(),
698 rows: Some(api::v1::Rows {
699 schema: request_schema,
700 rows: Vec::new(),
701 }),
702 }],
703 };
704 self.create_or_alter_tables_on_demand(
705 &mut requests,
706 ctx,
707 AutoCreateTableType::Trace { alter_existing },
708 statement_executor,
709 false,
710 false,
711 )
712 .await?;
713 Ok(())
714 }
715
716 async fn create_or_alter_tables_on_demand(
729 &self,
730 requests: &mut RowInsertRequests,
731 ctx: &QueryContextRef,
732 auto_create_table_type: AutoCreateTableType,
733 statement_executor: &StatementExecutor,
734 accommodate_existing_schema: bool,
735 is_single_value: bool,
736 ) -> Result<CreateAlterTableResult> {
737 let _timer = crate::metrics::CREATE_ALTER_ON_DEMAND
738 .with_label_values(&[auto_create_table_type.as_str()])
739 .start_timer();
740 let catalog = ctx.current_catalog();
741 let schema = ctx.current_schema();
742
743 let auto_create_disabled_reason = self.auto_create_disabled_reason(ctx)?;
744 let has_auto_create_exempt_table = auto_create_disabled_reason.is_some()
746 && requests
747 .inserts
748 .iter()
749 .any(|req| Self::is_auto_create_exempt_private_table(&schema, &req.table_name));
750 let mut table_infos = HashMap::new();
751 if let Some(disabled_reason) = auto_create_disabled_reason
753 && !has_auto_create_exempt_table
754 {
755 let mut instant_table_ids = HashSet::new();
756 for req in &requests.inserts {
757 let table = match self.get_table(catalog, &schema, &req.table_name).await? {
758 Some(table) => table,
759 None if is_ddl_reserved_table(&schema, &req.table_name) => {
762 statement_executor
763 .create_declared_relationships_table(catalog, ctx.clone())
764 .await?
765 }
766 None => {
767 return InvalidInsertRequestSnafu {
768 reason: format!(
769 "Table `{}` does not exist, and {}",
770 req.table_name, disabled_reason
771 ),
772 }
773 .fail();
774 }
775 };
776 let table_info = table.table_info();
777 if table_info.is_ttl_instant_table() {
778 instant_table_ids.insert(table_info.table_id());
779 }
780 table_infos.insert(table_info.table_id(), table.table_info());
781 }
782 let ret = CreateAlterTableResult {
783 instant_table_ids,
784 table_infos,
785 };
786 return Ok(ret);
787 }
788
789 let mut create_tables = vec![];
790 let mut alter_tables = vec![];
791 let mut need_refresh_table_infos = HashSet::new();
792 let mut instant_table_ids = HashSet::new();
793 let mut per_table_semantics: Option<Option<PerTableSemanticIndex>> = None;
794
795 for req in &mut requests.inserts {
796 let auto_create_allowed = auto_create_disabled_reason.is_none()
798 || Self::is_auto_create_exempt_private_table(&schema, &req.table_name);
799 match self.get_table(catalog, &schema, &req.table_name).await? {
800 Some(table) => {
801 let table_info = table.table_info();
802 if table_info.is_ttl_instant_table() {
803 instant_table_ids.insert(table_info.table_id());
804 }
805 if auto_create_allowed
806 && let Some(alter_expr) = self.get_alter_table_expr_on_demand(
807 req,
808 &table,
809 ctx,
810 accommodate_existing_schema,
811 is_single_value,
812 auto_create_table_type.alter_existing(),
813 )?
814 {
815 alter_tables.push(alter_expr);
816 need_refresh_table_infos.insert((
817 catalog.to_string(),
818 schema.clone(),
819 req.table_name.clone(),
820 ));
821 } else {
822 table_infos.insert(table_info.table_id(), table.table_info());
823 }
824 }
825 None if is_ddl_reserved_table(&schema, &req.table_name) => {
829 let table = statement_executor
830 .create_declared_relationships_table(catalog, ctx.clone())
831 .await?;
832 let table_info = table.table_info();
833 if table_info.is_ttl_instant_table() {
834 instant_table_ids.insert(table_info.table_id());
835 }
836 table_infos.insert(table_info.table_id(), table_info);
837 }
838 None if !auto_create_allowed
839 && let Some(disabled_reason) = auto_create_disabled_reason =>
840 {
841 return InvalidInsertRequestSnafu {
842 reason: format!(
843 "Table `{}` does not exist, and {}",
844 req.table_name, disabled_reason,
845 ),
846 }
847 .fail();
848 }
849 None => {
850 let semantic_index = per_table_semantics
851 .get_or_insert_with(|| parse_per_table_semantic_index(ctx))
852 .as_ref();
853 let create_expr = self.get_create_table_expr_on_demand(
854 req,
855 &auto_create_table_type,
856 ctx,
857 semantic_index,
858 )?;
859 create_tables.push(create_expr);
860 }
861 }
862 }
863
864 match auto_create_table_type {
865 AutoCreateTableType::Logical(_) => {
866 if !create_tables.is_empty() {
867 let tables = self
869 .create_logical_tables(create_tables, ctx, statement_executor)
870 .await?;
871
872 for table in tables {
873 let table_info = table.table_info();
874 if table_info.is_ttl_instant_table() {
875 instant_table_ids.insert(table_info.table_id());
876 }
877 table_infos.insert(table_info.table_id(), table.table_info());
878 }
879 }
880 if !alter_tables.is_empty() {
881 statement_executor
883 .alter_logical_tables(alter_tables, ctx.clone(), TriggerReason::AutoAlter)
884 .await?;
885 }
886 }
887 AutoCreateTableType::Physical
888 | AutoCreateTableType::Log
889 | AutoCreateTableType::LastNonNull => {
890 for create_table in create_tables {
893 let table = self
894 .create_physical_table(create_table, None, ctx, statement_executor)
895 .await?;
896 let table_info = table.table_info();
897 if table_info.is_ttl_instant_table() {
898 instant_table_ids.insert(table_info.table_id());
899 }
900 table_infos.insert(table_info.table_id(), table.table_info());
901 }
902 for alter_expr in alter_tables.into_iter() {
903 statement_executor
904 .alter_table_inner(alter_expr, ctx.clone(), TriggerReason::AutoAlter)
905 .await?;
906 }
907 }
908
909 AutoCreateTableType::Trace { .. } => {
910 let trace_table_name = ctx
911 .extension(TRACE_TABLE_NAME_SESSION_KEY)
912 .unwrap_or(TRACE_TABLE_NAME);
913
914 let trace_table_partitions = if let Some(trace_table_partitions) =
915 ctx.extension(TRACE_TABLE_PARTITIONS_HINT_KEY)
916 {
917 let p = trace_table_partitions.parse::<u32>().map_err(|_| {
918 InvalidInsertRequestSnafu {
919 reason: format!(
920 "Failed to parse trace_table_partitions: {}",
921 trace_table_partitions
922 ),
923 }
924 .build()
925 })?;
926 Some(p)
927 } else {
928 None
929 };
930
931 for mut create_table in create_tables {
934 if create_table.table_name == trace_services_table_name(trace_table_name)
935 || create_table.table_name == trace_operations_table_name(trace_table_name)
936 {
937 create_table
939 .table_options
940 .insert(APPEND_MODE_KEY.to_string(), "false".to_string());
941 create_table.table_options.remove(TTL_KEY);
943
944 let table = self
945 .create_physical_table(create_table, None, ctx, statement_executor)
946 .await?;
947 let table_info = table.table_info();
948 if table_info.is_ttl_instant_table() {
949 instant_table_ids.insert(table_info.table_id());
950 }
951 table_infos.insert(table_info.table_id(), table.table_info());
952 } else {
953 let partitions = if matches!(trace_table_partitions, Some(0) | Some(1)) {
956 None
958 } else {
959 let p = partition_rule_for_hexstring(
960 TRACE_ID_COLUMN,
961 trace_table_partitions,
962 )
963 .context(CreatePartitionRulesSnafu)?;
964 Some(p)
965 };
966
967 let index_columns =
972 [TRACE_ID_COLUMN, PARENT_SPAN_ID_COLUMN, SERVICE_NAME_COLUMN];
973 for index_column in index_columns {
974 if let Some(col) = create_table
975 .column_defs
976 .iter_mut()
977 .find(|c| c.name == index_column)
978 {
979 col.options =
980 options_from_skipping(&SkippingIndexOptions::default())
981 .context(ColumnOptionsSnafu)?;
982 } else {
983 warn!(
984 "Column {} not found when creating index for trace table: {}.",
985 index_column, create_table.table_name
986 );
987 }
988 }
989
990 create_table.table_options.insert(
992 TABLE_DATA_MODEL.to_string(),
993 TABLE_DATA_MODEL_TRACE_V1.to_string(),
994 );
995
996 let table = self
997 .create_physical_table(
998 create_table,
999 partitions,
1000 ctx,
1001 statement_executor,
1002 )
1003 .await?;
1004 let table_info = table.table_info();
1005 if table_info.is_ttl_instant_table() {
1006 instant_table_ids.insert(table_info.table_id());
1007 }
1008 table_infos.insert(table_info.table_id(), table.table_info());
1009 }
1010 }
1011 for alter_expr in alter_tables.into_iter() {
1012 statement_executor
1013 .alter_table_inner(alter_expr, ctx.clone(), TriggerReason::AutoAlter)
1014 .await?;
1015 }
1016 }
1017 }
1018
1019 for (catalog, schema, table_name) in need_refresh_table_infos {
1021 let table = self
1022 .get_table(&catalog, &schema, &table_name)
1023 .await?
1024 .context(TableNotFoundSnafu {
1025 table_name: common_catalog::format_full_table_name(
1026 &catalog,
1027 &schema,
1028 &table_name,
1029 ),
1030 })?;
1031 let table_info = table.table_info();
1032 table_infos.insert(table_info.table_id(), table.table_info());
1033 }
1034
1035 Ok(CreateAlterTableResult {
1036 instant_table_ids,
1037 table_infos,
1038 })
1039 }
1040
1041 async fn create_physical_table_on_demand(
1042 &self,
1043 ctx: &QueryContextRef,
1044 physical_table: String,
1045 statement_executor: &StatementExecutor,
1046 ) -> Result<()> {
1047 let catalog_name = ctx.current_catalog();
1048 let schema_name = ctx.current_schema();
1049
1050 if self
1052 .get_table(catalog_name, &schema_name, &physical_table)
1053 .await?
1054 .is_some()
1055 {
1056 return Ok(());
1057 }
1058
1059 if let Some(disabled_reason) = self.auto_create_disabled_reason(ctx)? {
1061 return InvalidInsertRequestSnafu {
1062 reason: format!(
1063 "Physical table `{physical_table}` does not exist, and {disabled_reason}"
1064 ),
1065 }
1066 .fail();
1067 }
1068
1069 let table_reference = TableReference::full(catalog_name, &schema_name, &physical_table);
1070 debug!("Ensuring physical metric table `{table_reference}` exists for insert");
1071
1072 let default_schema = vec![
1074 ColumnSchema {
1075 column_name: greptime_timestamp().to_string(),
1076 datatype: ColumnDataType::TimestampMillisecond as _,
1077 semantic_type: SemanticType::Timestamp as _,
1078 datatype_extension: None,
1079 options: None,
1080 },
1081 ColumnSchema {
1082 column_name: greptime_value().to_string(),
1083 datatype: ColumnDataType::Float64 as _,
1084 semantic_type: SemanticType::Field as _,
1085 datatype_extension: None,
1086 options: None,
1087 },
1088 ];
1089 let create_table_expr =
1090 &mut build_create_table_expr(&table_reference, &default_schema, default_engine())?;
1091
1092 create_table_expr.engine = METRIC_ENGINE_NAME.to_string();
1093 create_table_expr
1094 .table_options
1095 .insert(PHYSICAL_TABLE_METADATA_KEY.to_string(), "true".to_string());
1096
1097 let res = statement_executor
1099 .create_table_inner(
1100 create_table_expr,
1101 None,
1102 ctx.clone(),
1103 TriggerReason::AutoCreate,
1104 )
1105 .await;
1106
1107 match res {
1108 Ok(_) => Ok(()),
1109 Err(err) => {
1110 error!(err; "Failed to create table {table_reference}");
1111 Err(err)
1112 }
1113 }
1114 }
1115
1116 async fn get_table(
1117 &self,
1118 catalog: &str,
1119 schema: &str,
1120 table: &str,
1121 ) -> Result<Option<TableRef>> {
1122 self.catalog_manager
1123 .table(catalog, schema, table, None)
1124 .await
1125 .context(CatalogSnafu)
1126 }
1127
1128 fn get_create_table_expr_on_demand(
1129 &self,
1130 req: &RowInsertRequest,
1131 create_type: &AutoCreateTableType,
1132 ctx: &QueryContextRef,
1133 semantic_index: Option<&PerTableSemanticIndex>,
1134 ) -> Result<CreateTableExpr> {
1135 let schema = ctx.current_schema();
1136 let mut table_options = std::collections::HashMap::with_capacity(4);
1137 fill_table_options_for_create(&mut table_options, create_type, ctx);
1138 apply_per_table_semantic_options(
1139 &mut table_options,
1140 semantic_index,
1141 ctx.current_schema().as_str(),
1142 &req.table_name,
1143 );
1144
1145 let engine_name = if let AutoCreateTableType::Logical(_) = create_type {
1146 METRIC_ENGINE_NAME
1148 } else {
1149 default_engine()
1150 };
1151
1152 let table_ref = TableReference::full(ctx.current_catalog(), &schema, &req.table_name);
1153 let request_schema = req.rows.as_ref().unwrap().schema.as_slice();
1155 let mut create_table_expr =
1156 build_create_table_expr(&table_ref, request_schema, engine_name)?;
1157
1158 if ctx.extension(SPLUNK_PK_METADATA_ORDER_KEY).is_some() {
1160 reorder_splunk_primary_keys(&mut create_table_expr.primary_keys);
1161 }
1162
1163 debug!("Ensuring table `{table_ref}` exists for insert");
1164 create_table_expr.table_options.extend(table_options);
1165 Ok(create_table_expr)
1166 }
1167
1168 fn get_alter_table_expr_on_demand(
1176 &self,
1177 req: &mut RowInsertRequest,
1178 table: &TableRef,
1179 ctx: &QueryContextRef,
1180 accommodate_existing_schema: bool,
1181 is_single_value: bool,
1182 alter_existing: bool,
1183 ) -> Result<Option<AlterTableExpr>> {
1184 if !alter_existing {
1185 return Ok(None);
1186 }
1187
1188 let catalog_name = ctx.current_catalog();
1189 let schema_name = ctx.current_schema();
1190 let table_name = table.table_info().name.clone();
1191
1192 if is_ddl_reserved_table(&schema_name, &table_name) {
1195 return Ok(None);
1196 }
1197
1198 let request_schema = req.rows.as_ref().unwrap().schema.as_slice();
1199 let request_field_count = request_schema
1200 .iter()
1201 .filter(|col| col.semantic_type == SemanticType::Field as i32)
1202 .count();
1203 let column_exprs = ColumnExpr::from_column_schemas(request_schema);
1204 let add_columns = expr_helper::extract_add_columns_expr(&table.schema(), column_exprs)?;
1205 let Some(mut add_columns) = add_columns else {
1206 return Ok(None);
1207 };
1208
1209 if is_single_value {
1210 let request_is_native_histogram = request_is_native_histogram(request_schema);
1211 let table_is_native_histogram = table_is_native_histogram(table);
1212 ensure!(
1213 request_is_native_histogram == table_is_native_histogram,
1214 InvalidInsertRequestSnafu {
1215 reason: format!(
1216 "Table `{table_name}` cannot mix native histogram and float sample fields"
1217 ),
1218 }
1219 );
1220 }
1221
1222 if accommodate_existing_schema {
1224 let table_schema = table.schema();
1225 let ts_col_name = table_schema.timestamp_column().map(|c| c.name.clone());
1227 let mut field_col_name = None;
1229 if is_single_value && request_field_count <= 1 {
1230 let mut multiple_field_cols = false;
1231 table.field_columns().for_each(|col| {
1232 if field_col_name.is_none() {
1233 field_col_name = Some(col.name.clone());
1234 } else {
1235 multiple_field_cols = true;
1236 }
1237 });
1238 if multiple_field_cols {
1239 field_col_name = None;
1240 }
1241 }
1242
1243 if let Some(rows) = req.rows.as_mut() {
1245 for col in &mut rows.schema {
1246 match col.semantic_type {
1247 x if x == SemanticType::Timestamp as i32 => {
1248 if let Some(ref ts_name) = ts_col_name
1249 && col.column_name != *ts_name
1250 {
1251 col.column_name = ts_name.clone();
1252 }
1253 }
1254 x if x == SemanticType::Field as i32 => {
1255 if let Some(ref field_name) = field_col_name
1256 && col.column_name != *field_name
1257 {
1258 col.column_name = field_name.clone();
1259 }
1260 }
1261 _ => {}
1262 }
1263 }
1264 }
1265
1266 add_columns.add_columns.retain(|col| {
1268 let def = col.column_def.as_ref().unwrap();
1269 def.semantic_type == SemanticType::Tag as i32
1270 || (def.semantic_type == SemanticType::Field as i32 && field_col_name.is_none())
1271 });
1272
1273 if add_columns.add_columns.is_empty() {
1274 return Ok(None);
1275 }
1276 }
1277
1278 Ok(Some(AlterTableExpr {
1279 catalog_name: catalog_name.to_string(),
1280 schema_name: schema_name.clone(),
1281 table_name: table_name.clone(),
1282 kind: Some(Kind::AddColumns(add_columns)),
1283 }))
1284 }
1285
1286 async fn create_physical_table(
1288 &self,
1289 mut create_table_expr: CreateTableExpr,
1290 partitions: Option<Partitions>,
1291 ctx: &QueryContextRef,
1292 statement_executor: &StatementExecutor,
1293 ) -> Result<TableRef> {
1294 let res = statement_executor
1295 .create_table_inner(
1296 &mut create_table_expr,
1297 partitions,
1298 ctx.clone(),
1299 TriggerReason::AutoCreate,
1300 )
1301 .await;
1302
1303 let table_ref = TableReference::full(
1304 &create_table_expr.catalog_name,
1305 &create_table_expr.schema_name,
1306 &create_table_expr.table_name,
1307 );
1308
1309 match res {
1310 Ok(table) => Ok(table),
1311 Err(err) => {
1312 error!(err; "Failed to create table {}", table_ref);
1313 Err(err)
1314 }
1315 }
1316 }
1317
1318 async fn create_logical_tables(
1319 &self,
1320 create_table_exprs: Vec<CreateTableExpr>,
1321 ctx: &QueryContextRef,
1322 statement_executor: &StatementExecutor,
1323 ) -> Result<Vec<TableRef>> {
1324 let res = statement_executor
1325 .create_logical_tables(&create_table_exprs, ctx.clone(), TriggerReason::AutoCreate)
1326 .await;
1327
1328 match res {
1329 Ok(res) => Ok(res),
1330 Err(err) => {
1331 let failed_tables = create_table_exprs
1332 .into_iter()
1333 .map(|expr| {
1334 format!(
1335 "{}.{}.{}",
1336 expr.catalog_name, expr.schema_name, expr.table_name
1337 )
1338 })
1339 .collect::<Vec<_>>();
1340 error!(
1341 err;
1342 "Failed to create logical tables {:?}",
1343 failed_tables
1344 );
1345 Err(err)
1346 }
1347 }
1348 }
1349
1350 pub fn node_manager(&self) -> &NodeManagerRef {
1351 &self.node_manager
1352 }
1353
1354 pub fn partition_manager(&self) -> &PartitionRuleManagerRef {
1355 &self.partition_manager
1356 }
1357
1358 pub fn table_flownode_set_cache(&self) -> &TableFlownodeSetCacheRef {
1359 &self.table_flownode_set_cache
1360 }
1361}
1362
1363fn request_is_native_histogram(request_schema: &[ColumnSchema]) -> bool {
1364 let mut fields = request_schema
1365 .iter()
1366 .filter(|col| col.semantic_type == SemanticType::Field as i32);
1367 let Some(col) = fields.next() else {
1368 return false;
1369 };
1370
1371 fields.next().is_none()
1372 && api::helper::is_column_type_value_eq(
1373 col.datatype,
1374 col.datatype_extension.clone(),
1375 native_histogram_value_type(),
1376 )
1377}
1378
1379fn table_is_native_histogram(table: &TableRef) -> bool {
1380 let mut fields = table.field_columns();
1381 let Some(col) = fields.next() else {
1382 return false;
1383 };
1384
1385 fields.next().is_none() && is_native_histogram_value_type(&col.data_type)
1386}
1387
1388fn validate_column_count_match(requests: &RowInsertRequests) -> Result<()> {
1389 for request in &requests.inserts {
1390 let rows = request.rows.as_ref().unwrap();
1391 let column_count = rows.schema.len();
1392 rows.rows.iter().try_for_each(|r| {
1393 ensure!(
1394 r.values.len() == column_count,
1395 InvalidInsertRequestSnafu {
1396 reason: format!(
1397 "column count mismatch, columns: {}, values: {}",
1398 column_count,
1399 r.values.len()
1400 )
1401 }
1402 );
1403 Ok(())
1404 })?;
1405 }
1406 Ok(())
1407}
1408
1409pub fn fill_table_options_for_create(
1411 table_options: &mut std::collections::HashMap<String, String>,
1412 create_type: &AutoCreateTableType,
1413 ctx: &QueryContextRef,
1414) {
1415 for key in VALID_TABLE_OPTION_KEYS {
1416 if let Some(value) = ctx.extension(key) {
1417 table_options.insert(key.to_string(), value.to_string());
1418 }
1419 }
1420
1421 for (key, value) in ctx.extensions() {
1423 if is_semantic_option_key(&key) && validate_semantic_option(&key, &value) {
1424 table_options.insert(key, value);
1425 }
1426 }
1427
1428 match create_type {
1429 AutoCreateTableType::Logical(physical_table) => {
1430 table_options.insert(
1431 LOGICAL_TABLE_METADATA_KEY.to_string(),
1432 physical_table.clone(),
1433 );
1434 }
1435 AutoCreateTableType::Physical => {
1436 if let Some(append_mode) = ctx.extension(APPEND_MODE_KEY) {
1437 table_options.insert(APPEND_MODE_KEY.to_string(), append_mode.to_string());
1438 }
1439 if let Some(merge_mode) = ctx.extension(MERGE_MODE_KEY) {
1440 table_options.insert(MERGE_MODE_KEY.to_string(), merge_mode.to_string());
1441 }
1442 if let Some(time_window) = ctx.extension(TWCS_TIME_WINDOW) {
1443 table_options.insert(TWCS_TIME_WINDOW.to_string(), time_window.to_string());
1444 table_options.insert(
1446 COMPACTION_TYPE.to_string(),
1447 COMPACTION_TYPE_TWCS.to_string(),
1448 );
1449 }
1450 }
1451 AutoCreateTableType::Log => {
1454 table_options.insert(APPEND_MODE_KEY.to_string(), "true".to_string());
1455 }
1456 AutoCreateTableType::LastNonNull => {
1457 if ctx
1458 .extension(APPEND_MODE_KEY)
1459 .is_some_and(|value| value.eq_ignore_ascii_case("true"))
1460 {
1461 table_options.insert(APPEND_MODE_KEY.to_string(), "true".to_string());
1462 table_options.insert(MERGE_MODE_KEY.to_string(), "last_row".to_string());
1463 } else if let Some(merge_mode) = ctx.extension(MERGE_MODE_KEY) {
1464 table_options.insert(MERGE_MODE_KEY.to_string(), merge_mode.to_string());
1465 } else {
1466 table_options.insert(MERGE_MODE_KEY.to_string(), "last_non_null".to_string());
1467 }
1468 }
1469 AutoCreateTableType::Trace { .. } => {
1470 table_options.insert(APPEND_MODE_KEY.to_string(), "true".to_string());
1471 }
1472 }
1473}
1474
1475pub type PerTableSemanticIndex = BTreeMap<String, BTreeMap<String, BTreeMap<String, String>>>;
1481
1482pub fn parse_per_table_semantic_index(ctx: &QueryContextRef) -> Option<PerTableSemanticIndex> {
1487 let raw = ctx.extension(SEMANTIC_PER_TABLE_INDEX_KEY)?;
1488 match serde_json::from_str(raw) {
1489 Ok(index) => Some(index),
1490 Err(_) => {
1491 warn!("failed to parse semantic per-table index, skipping per-table options");
1492 None
1493 }
1494 }
1495}
1496
1497pub fn apply_per_table_semantic_options(
1505 table_options: &mut std::collections::HashMap<String, String>,
1506 index: Option<&PerTableSemanticIndex>,
1507 schema: &str,
1508 table_name: &str,
1509) {
1510 let Some(entry) = index
1511 .and_then(|index| index.get(schema))
1512 .and_then(|tables| tables.get(table_name))
1513 else {
1514 return;
1515 };
1516 for (key, value) in entry {
1517 if is_semantic_option_key(key) && validate_semantic_option(key, value) {
1518 table_options.insert(key.clone(), value.clone());
1519 }
1520 }
1521}
1522
1523pub fn build_create_table_expr(
1524 table: &TableReference,
1525 request_schema: &[ColumnSchema],
1526 engine: &str,
1527) -> Result<CreateTableExpr> {
1528 expr_helper::create_table_expr_by_column_schemas(table, request_schema, engine, None)
1529}
1530
1531pub const SPLUNK_PK_METADATA_ORDER_KEY: &str = "splunk_pk_metadata_order";
1535
1536fn reorder_splunk_primary_keys(primary_keys: &mut [String]) {
1539 const LEAD: [&str; 3] = ["host", "source", "sourcetype"];
1540 primary_keys.sort_by_key(|name| {
1543 LEAD.iter()
1544 .position(|&lead| lead == name.as_str())
1545 .unwrap_or(LEAD.len())
1546 });
1547}
1548
1549struct CreateAlterTableResult {
1551 instant_table_ids: HashSet<TableId>,
1553 table_infos: HashMap<TableId, Arc<TableInfo>>,
1555}
1556
1557struct FlowMirrorTask {
1558 requests: HashMap<Peer, RegionInsertRequests>,
1559 num_rows: usize,
1560}
1561
1562impl FlowMirrorTask {
1563 async fn new(
1564 cache: &TableFlownodeSetCacheRef,
1565 requests: impl Iterator<Item = &RegionInsertRequest>,
1566 ) -> Result<Self> {
1567 let mut src_table_reqs: HashMap<TableId, Option<(Vec<Peer>, RegionInsertRequests)>> =
1568 HashMap::new();
1569 let mut num_rows = 0;
1570
1571 for req in requests {
1572 let table_id = RegionId::from_u64(req.region_id).table_id();
1573 match src_table_reqs.get_mut(&table_id) {
1574 Some(Some((_peers, reqs))) => reqs.requests.push(req.clone()),
1575 Some(None) => continue,
1577 _ => {
1578 let peers = cache
1580 .get(table_id)
1581 .await
1582 .context(RequestInsertsSnafu)?
1583 .unwrap_or_default()
1584 .values()
1585 .cloned()
1586 .collect::<HashSet<_>>()
1587 .into_iter()
1588 .collect::<Vec<_>>();
1589
1590 if !peers.is_empty() {
1591 let mut reqs = RegionInsertRequests::default();
1592 reqs.requests.push(req.clone());
1593 num_rows += reqs
1594 .requests
1595 .iter()
1596 .map(|r| r.rows.as_ref().unwrap().rows.len())
1597 .sum::<usize>();
1598 src_table_reqs.insert(table_id, Some((peers, reqs)));
1599 } else {
1600 src_table_reqs.insert(table_id, None);
1602 }
1603 }
1604 }
1605 }
1606
1607 let mut inserts: HashMap<Peer, RegionInsertRequests> = HashMap::new();
1608
1609 for (_table_id, (peers, reqs)) in src_table_reqs
1610 .into_iter()
1611 .filter_map(|(k, v)| v.map(|v| (k, v)))
1612 {
1613 if peers.len() == 1 {
1614 inserts
1616 .entry(peers[0].clone())
1617 .or_default()
1618 .requests
1619 .extend(reqs.requests);
1620 continue;
1621 } else {
1622 for flownode in peers {
1624 inserts
1625 .entry(flownode.clone())
1626 .or_default()
1627 .requests
1628 .extend(reqs.requests.clone());
1629 }
1630 }
1631 }
1632
1633 Ok(Self {
1634 requests: inserts,
1635 num_rows,
1636 })
1637 }
1638
1639 fn detach(self, node_manager: NodeManagerRef) -> Result<()> {
1640 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.add(self.num_rows as i64);
1641 for (peer, inserts) in self.requests {
1642 let node_manager = node_manager.clone();
1643 common_runtime::spawn_global(async move {
1644 let result = node_manager
1645 .flownode(&peer)
1646 .await
1647 .handle_inserts(inserts)
1648 .await
1649 .context(RequestInsertsSnafu);
1650
1651 match result {
1652 Ok(resp) => {
1653 let affected_rows = resp.affected_rows;
1654 crate::metrics::DIST_MIRROR_ROW_COUNT.inc_by(affected_rows);
1655 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.sub(affected_rows as _);
1656 }
1657 Err(err) => {
1658 error!(err; "Failed to insert data into flownode {}", peer);
1659 }
1660 }
1661 });
1662 }
1663
1664 Ok(())
1665 }
1666}
1667
1668#[cfg(test)]
1669mod tests {
1670 use std::sync::Arc;
1671
1672 use api::helper::ColumnDataTypeWrapper;
1673 use api::v1::helper::{field_column_schema, time_index_column_schema};
1674 use api::v1::{RowInsertRequest, Rows, Value};
1675 use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
1676 use common_meta::cache::new_table_flownode_set_cache;
1677 use common_meta::ddl::test_util::datanode_handler::NaiveDatanodeHandler;
1678 use common_meta::test_util::MockDatanodeManager;
1679 use common_query::native_histogram::NATIVE_HISTOGRAM_FIELD;
1680 use common_query::prelude::{greptime_native_histogram, set_default_prefix};
1681 use datatypes::data_type::ConcreteDataType;
1682 use datatypes::schema::ColumnSchema;
1683 use moka::future::Cache;
1684 use session::context::QueryContext;
1685 use table::TableRef;
1686 use table::dist_table::DummyDataSource;
1687 use table::metadata::{TableInfoBuilder, TableMetaBuilder, TableType};
1688
1689 use crate::insert::*;
1690 use crate::test_util::{create_partition_rule_manager, prepare_mocked_backend};
1691
1692 fn make_table_ref_with_schema(
1693 ts_name: &str,
1694 field_name: &str,
1695 field_type: ConcreteDataType,
1696 ) -> TableRef {
1697 let schema = datatypes::schema::SchemaBuilder::try_from_columns(vec![
1698 ColumnSchema::new(
1699 ts_name,
1700 ConcreteDataType::timestamp_millisecond_datatype(),
1701 false,
1702 )
1703 .with_time_index(true),
1704 ColumnSchema::new(field_name, field_type, true),
1705 ])
1706 .unwrap()
1707 .build()
1708 .unwrap();
1709 let meta = TableMetaBuilder::empty()
1710 .schema(Arc::new(schema))
1711 .primary_key_indices(vec![])
1712 .value_indices(vec![1])
1713 .engine("mito")
1714 .next_column_id(0)
1715 .options(Default::default())
1716 .created_on(Default::default())
1717 .build()
1718 .unwrap();
1719 let info = Arc::new(
1720 TableInfoBuilder::default()
1721 .table_id(1)
1722 .table_version(0)
1723 .name("test_table")
1724 .schema_name(DEFAULT_SCHEMA_NAME)
1725 .catalog_name(DEFAULT_CATALOG_NAME)
1726 .desc(None)
1727 .table_type(TableType::Base)
1728 .meta(meta)
1729 .build()
1730 .unwrap(),
1731 );
1732 Arc::new(table::Table::new(
1733 info,
1734 table::metadata::FilterPushDownType::Unsupported,
1735 Arc::new(DummyDataSource),
1736 ))
1737 }
1738
1739 #[tokio::test]
1740 async fn test_accommodate_existing_schema_and_reject_kind_changes() {
1741 let ts_name = "my_ts";
1742 let field_name = "my_field";
1743 let table =
1744 make_table_ref_with_schema(ts_name, field_name, ConcreteDataType::float64_datatype());
1745
1746 let mut req = RowInsertRequest {
1748 table_name: "test_table".to_string(),
1749 rows: Some(Rows {
1750 schema: vec![
1751 time_index_column_schema("ts_wrong", ColumnDataType::TimestampMillisecond),
1752 field_column_schema("field_wrong", ColumnDataType::Float64),
1753 ],
1754 rows: vec![api::v1::Row {
1755 values: vec![Value::default(), Value::default()],
1756 }],
1757 }),
1758 };
1759 let ctx = Arc::new(QueryContext::with(
1760 DEFAULT_CATALOG_NAME,
1761 DEFAULT_SCHEMA_NAME,
1762 ));
1763
1764 let kv_backend = prepare_mocked_backend().await;
1765 let inserter = Inserter::new(
1766 catalog::memory::MemoryCatalogManager::new(),
1767 create_partition_rule_manager(kv_backend.clone()).await,
1768 Arc::new(MockDatanodeManager::new(NaiveDatanodeHandler)),
1769 Arc::new(new_table_flownode_set_cache(
1770 String::new(),
1771 Cache::new(100),
1772 kv_backend.clone(),
1773 )),
1774 true,
1775 );
1776 assert!(
1778 inserter
1779 .get_alter_table_expr_on_demand(&mut req, &table, &ctx, false, false, false)
1780 .unwrap()
1781 .is_none()
1782 );
1783 let alter_expr = inserter
1784 .get_alter_table_expr_on_demand(&mut req, &table, &ctx, true, true, true)
1785 .unwrap();
1786 assert!(alter_expr.is_none());
1787
1788 let req_schema = req.rows.as_ref().unwrap().schema.clone();
1790 assert_eq!(req_schema[0].column_name, ts_name);
1791 assert_eq!(req_schema[1].column_name, field_name);
1792
1793 let (datatype, datatype_extension) =
1794 ColumnDataTypeWrapper::try_from(native_histogram_value_type().clone())
1795 .unwrap()
1796 .into_parts();
1797 let mut histogram_req = RowInsertRequest {
1798 table_name: "test_table".to_string(),
1799 rows: Some(Rows {
1800 schema: vec![
1801 time_index_column_schema("ts", ColumnDataType::TimestampMillisecond),
1802 api::v1::ColumnSchema {
1803 column_name: greptime_native_histogram().to_string(),
1804 datatype: datatype as i32,
1805 semantic_type: SemanticType::Field as i32,
1806 datatype_extension,
1807 options: None,
1808 },
1809 ],
1810 rows: vec![],
1811 }),
1812 };
1813 let error = inserter
1814 .get_alter_table_expr_on_demand(&mut histogram_req, &table, &ctx, false, true, true)
1815 .unwrap_err();
1816 assert!(
1817 error
1818 .to_string()
1819 .contains("cannot mix native histogram and float sample fields")
1820 );
1821
1822 let histogram_table = make_table_ref_with_schema(
1823 "ts",
1824 greptime_native_histogram(),
1825 native_histogram_value_type().clone(),
1826 );
1827 let mut sample_req = RowInsertRequest {
1828 table_name: "test_table".to_string(),
1829 rows: Some(Rows {
1830 schema: vec![
1831 time_index_column_schema("ts", ColumnDataType::TimestampMillisecond),
1832 field_column_schema(greptime_value(), ColumnDataType::Float64),
1833 ],
1834 rows: vec![],
1835 }),
1836 };
1837 let error = inserter
1838 .get_alter_table_expr_on_demand(
1839 &mut sample_req,
1840 &histogram_table,
1841 &ctx,
1842 false,
1843 true,
1844 true,
1845 )
1846 .unwrap_err();
1847 assert!(
1848 error
1849 .to_string()
1850 .contains("cannot mix native histogram and float sample fields")
1851 );
1852 }
1853
1854 #[test]
1855 fn test_native_histogram_detection_survives_prefix_change() {
1856 set_default_prefix(Some("custom")).unwrap();
1857 let table = make_table_ref_with_schema(
1858 "custom_timestamp",
1859 NATIVE_HISTOGRAM_FIELD,
1860 native_histogram_value_type().clone(),
1861 );
1862 let (datatype, datatype_extension) =
1863 ColumnDataTypeWrapper::try_from(native_histogram_value_type().clone())
1864 .unwrap()
1865 .into_parts();
1866 let request_schema = [api::v1::ColumnSchema {
1867 column_name: greptime_native_histogram().to_string(),
1868 datatype: datatype as i32,
1869 semantic_type: SemanticType::Field as i32,
1870 datatype_extension,
1871 options: None,
1872 }];
1873
1874 assert!(request_is_native_histogram(&request_schema));
1875 assert!(table_is_native_histogram(&table));
1876 }
1877
1878 #[test]
1879 fn test_skip_wal_does_not_change_table_options() {
1880 check_skip_wal_does_not_change_table_options(false);
1881 check_skip_wal_does_not_change_table_options(true);
1882 }
1883
1884 fn check_skip_wal_does_not_change_table_options(skip_wal: bool) {
1885 let ctx = Arc::new(QueryContext::with(
1886 DEFAULT_CATALOG_NAME,
1887 DEFAULT_SCHEMA_NAME,
1888 ));
1889 ctx.set_skip_wal(skip_wal);
1890 let mut options = Default::default();
1891 fill_table_options_for_create(&mut options, &AutoCreateTableType::Physical, &ctx);
1892 assert!(!options.contains_key(session::hints::INSERT_SKIP_WAL_HINT));
1893 assert!(!options.contains_key("skip_wal"));
1894 }
1895
1896 #[test]
1897 fn test_last_non_null_create_options_preserve_default_without_append_mode() {
1898 let ctx = Arc::new(QueryContext::with(
1899 DEFAULT_CATALOG_NAME,
1900 DEFAULT_SCHEMA_NAME,
1901 ));
1902 let mut table_options = Default::default();
1903
1904 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
1905
1906 assert_eq!(
1907 Some("last_non_null"),
1908 table_options.get(MERGE_MODE_KEY).map(String::as_str)
1909 );
1910 assert!(!table_options.contains_key(APPEND_MODE_KEY));
1911 }
1912
1913 #[test]
1914 fn test_fill_table_options_copies_semantic_extensions() {
1915 use table::requests::{
1916 SEMANTIC_METRIC_TYPE, SEMANTIC_PER_TABLE_INDEX_KEY, SEMANTIC_SIGNAL_TYPE,
1917 SEMANTIC_SOURCE, SEMANTIC_SOURCE_VERSION, SIGNAL_TYPE_METRIC, SOURCE_OPENTELEMETRY,
1918 };
1919
1920 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
1921 ctx.set_extension(SEMANTIC_SIGNAL_TYPE, SIGNAL_TYPE_METRIC);
1922 ctx.set_extension(SEMANTIC_SOURCE, SOURCE_OPENTELEMETRY);
1923 ctx.set_extension(SEMANTIC_SOURCE_VERSION, "2.0");
1924 ctx.set_extension(SEMANTIC_METRIC_TYPE, "bogus");
1925 ctx.set_extension(SEMANTIC_PER_TABLE_INDEX_KEY, "{}");
1927 let ctx = Arc::new(ctx);
1928 let mut table_options = Default::default();
1929
1930 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::Physical, &ctx);
1931
1932 assert_eq!(
1933 Some(SIGNAL_TYPE_METRIC),
1934 table_options.get(SEMANTIC_SIGNAL_TYPE).map(String::as_str)
1935 );
1936 assert_eq!(
1937 Some(SOURCE_OPENTELEMETRY),
1938 table_options.get(SEMANTIC_SOURCE).map(String::as_str)
1939 );
1940 assert_eq!(
1941 Some("2.0"),
1942 table_options
1943 .get(SEMANTIC_SOURCE_VERSION)
1944 .map(String::as_str)
1945 );
1946 assert!(!table_options.contains_key(SEMANTIC_METRIC_TYPE));
1947 assert!(!table_options.contains_key(SEMANTIC_PER_TABLE_INDEX_KEY));
1948 }
1949
1950 #[test]
1951 fn test_apply_per_table_semantic_options() {
1952 use table::requests::{
1953 SEMANTIC_METRIC_TYPE, SEMANTIC_METRIC_UNIT, SEMANTIC_PER_TABLE_INDEX_KEY,
1954 };
1955
1956 let index = format!(
1957 r#"{{
1958 "{DEFAULT_SCHEMA_NAME}": {{
1959 "http_requests_total": {{
1960 "greptime.semantic.metric.type": "counter",
1961 "greptime.semantic.metric.unit": "By",
1962 "greptime.semantic.metric.type_BOGUS": "x"
1963 }},
1964 "other_table": {{
1965 "greptime.semantic.metric.type": "gauge"
1966 }}
1967 }},
1968 "other_schema": {{
1969 "http_requests_total": {{
1970 "greptime.semantic.metric.type": "gauge"
1971 }}
1972 }}
1973 }}"#
1974 );
1975 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
1976 ctx.set_extension(SEMANTIC_PER_TABLE_INDEX_KEY, index);
1977 let ctx = Arc::new(ctx);
1978
1979 let index = parse_per_table_semantic_index(&ctx);
1980 assert!(index.is_some());
1981 let index = index.as_ref();
1982
1983 let mut table_options = std::collections::HashMap::new();
1984 apply_per_table_semantic_options(
1985 &mut table_options,
1986 index,
1987 DEFAULT_SCHEMA_NAME,
1988 "http_requests_total",
1989 );
1990 assert_eq!(
1992 table_options.get(SEMANTIC_METRIC_TYPE).map(String::as_str),
1993 Some("counter")
1994 );
1995 assert_eq!(
1996 table_options.get(SEMANTIC_METRIC_UNIT).map(String::as_str),
1997 Some("By")
1998 );
1999 assert!(!table_options.contains_key("greptime.semantic.metric.type_BOGUS"));
2002 assert_eq!(table_options.len(), 2);
2003
2004 let mut empty = std::collections::HashMap::new();
2005 apply_per_table_semantic_options(&mut empty, index, DEFAULT_SCHEMA_NAME, "not_in_index");
2006 assert!(empty.is_empty());
2007
2008 let mut opts = std::collections::HashMap::new();
2011 apply_per_table_semantic_options(
2012 &mut opts,
2013 index,
2014 "schema_without_entry",
2015 "http_requests_total",
2016 );
2017 assert!(opts.is_empty());
2018
2019 let bare = Arc::new(QueryContext::with(
2021 DEFAULT_CATALOG_NAME,
2022 DEFAULT_SCHEMA_NAME,
2023 ));
2024 assert!(parse_per_table_semantic_index(&bare).is_none());
2025 let mut opts = std::collections::HashMap::new();
2026 apply_per_table_semantic_options(
2027 &mut opts,
2028 None,
2029 DEFAULT_SCHEMA_NAME,
2030 "http_requests_total",
2031 );
2032 assert!(opts.is_empty());
2033 }
2034
2035 #[test]
2036 fn test_last_non_null_create_options_preserve_default_with_append_mode_false() {
2037 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
2038 ctx.set_extension(APPEND_MODE_KEY, "false");
2039 let ctx = Arc::new(ctx);
2040 let mut table_options = Default::default();
2041
2042 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
2043
2044 assert!(!table_options.contains_key(APPEND_MODE_KEY));
2045 assert_eq!(
2046 Some("last_non_null"),
2047 table_options.get(MERGE_MODE_KEY).map(String::as_str)
2048 );
2049 }
2050
2051 #[test]
2052 fn test_last_non_null_create_options_use_configured_merge_mode() {
2053 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
2054 ctx.set_extension(MERGE_MODE_KEY, "last_row");
2055 let ctx = Arc::new(ctx);
2056 let mut table_options = Default::default();
2057
2058 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
2059
2060 assert_eq!(
2061 Some("last_row"),
2062 table_options.get(MERGE_MODE_KEY).map(String::as_str)
2063 );
2064 assert!(!table_options.contains_key(APPEND_MODE_KEY));
2065 }
2066
2067 #[test]
2068 fn test_last_non_null_create_options_use_last_row_with_append_mode_true() {
2069 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
2070 ctx.set_extension(APPEND_MODE_KEY, "true");
2071 let ctx = Arc::new(ctx);
2072 let mut table_options = Default::default();
2073
2074 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
2075
2076 assert_eq!(
2077 Some("true"),
2078 table_options.get(APPEND_MODE_KEY).map(String::as_str)
2079 );
2080 assert_eq!(
2081 Some("last_row"),
2082 table_options.get(MERGE_MODE_KEY).map(String::as_str)
2083 );
2084 }
2085
2086 struct UnexpectedBatcher;
2087
2088 #[async_trait::async_trait]
2089 impl PendingRowsBatcher for UnexpectedBatcher {
2090 async fn acquire(&self) -> Result<Arc<tokio::sync::OwnedSemaphorePermit>> {
2091 panic!("empty writes must not acquire batch admission")
2092 }
2093
2094 async fn submit(
2095 &self,
2096 _table_info: TableInfoRef,
2097 _batch: arrow::record_batch::RecordBatch,
2098 _ctx: QueryContextRef,
2099 _permit: Arc<tokio::sync::OwnedSemaphorePermit>,
2100 ) -> Result<usize> {
2101 panic!("empty writes must not submit a batch")
2102 }
2103 }
2104
2105 async fn batcher_test_inserter() -> Inserter {
2106 let kv_backend = prepare_mocked_backend().await;
2107 Inserter::new(
2108 catalog::memory::MemoryCatalogManager::new(),
2109 create_partition_rule_manager(kv_backend.clone()).await,
2110 Arc::new(MockDatanodeManager::new(NaiveDatanodeHandler)),
2111 Arc::new(new_table_flownode_set_cache(
2112 String::new(),
2113 Cache::new(100),
2114 kv_backend,
2115 )),
2116 true,
2117 )
2118 }
2119
2120 #[tokio::test]
2121 async fn test_instant_table_bypasses_batcher() {
2122 let batcher: Arc<dyn PendingRowsBatcher> = Arc::new(UnexpectedBatcher);
2123 let inserter = batcher_test_inserter()
2124 .await
2125 .with_pending_rows_batcher(Some(batcher));
2126 let mut ctx = session::context::QueryContextBuilder::default().build();
2127 ctx.set_batching_enabled(true);
2128 let ctx = Arc::new(ctx);
2129 let table = make_table_ref_with_schema("ts", "value", ConcreteDataType::float64_datatype())
2130 .table_info();
2131 assert!(inserter.table_batcher(&table, &ctx).is_some());
2132 let mut instant = (*table).clone();
2133 instant.meta.options.ttl = Some(common_time::ttl::TimeToLive::Instant);
2134 assert!(inserter.table_batcher(&Arc::new(instant), &ctx).is_none());
2135 }
2136
2137 #[tokio::test]
2138 async fn test_empty_prepared_rows_skip_batcher() {
2139 let inserter = batcher_test_inserter().await;
2140 let table = make_table_ref_with_schema("ts", "value", ConcreteDataType::float64_datatype())
2141 .table_info();
2142 let batcher: Arc<dyn PendingRowsBatcher> = Arc::new(UnexpectedBatcher);
2143 let ctx = QueryContext::arc();
2144 let output = inserter
2145 .submit_table_rows(
2146 Rows {
2147 schema: vec![],
2148 rows: vec![],
2149 },
2150 table.clone(),
2151 ctx.clone(),
2152 &batcher,
2153 )
2154 .await
2155 .unwrap();
2156 assert!(matches!(output.data, OutputData::AffectedRows(0)));
2157 let output = inserter
2158 .submit_pending_rows(
2159 RowInsertRequests {
2160 inserts: vec![RowInsertRequest {
2161 table_name: table.name.clone(),
2162 rows: None,
2163 }],
2164 },
2165 HashMap::from_iter([(table.table_id(), table)]),
2166 ctx,
2167 &batcher,
2168 )
2169 .await
2170 .unwrap();
2171 assert!(matches!(output.data, OutputData::AffectedRows(0)));
2172 }
2173}