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, 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::{error, info, 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;
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::error::{
77 CatalogSnafu, ColumnOptionsSnafu, CreatePartitionRulesSnafu, FindRegionLeaderSnafu,
78 InvalidInsertRequestSnafu, JoinTaskSnafu, RequestInsertsSnafu, Result, TableNotFoundSnafu,
79};
80use crate::expr_helper;
81use crate::region_req_factory::RegionRequestFactory;
82use crate::req_convert::common::preprocess_row_insert_requests;
83use crate::req_convert::insert::{
84 ColumnToRow, RowToRegion, StatementToRegion, TableToRegion, fill_reqs_with_impure_default,
85};
86use crate::statement::StatementExecutor;
87
88pub struct Inserter {
89 catalog_manager: CatalogManagerRef,
90 pub(crate) partition_manager: PartitionRuleManagerRef,
91 pub(crate) node_manager: NodeManagerRef,
92 pub(crate) table_flownode_set_cache: TableFlownodeSetCacheRef,
93 auto_create_table: bool,
97}
98
99pub type InserterRef = Arc<Inserter>;
100
101#[derive(Clone)]
103pub enum AutoCreateTableType {
104 Logical(String),
106 Physical,
108 Log,
110 LastNonNull,
112 Trace { alter_existing: bool },
114}
115
116impl AutoCreateTableType {
117 pub fn as_str(&self) -> &'static str {
118 match self {
119 AutoCreateTableType::Logical(_) => "logical",
120 AutoCreateTableType::Physical => "physical",
121 AutoCreateTableType::Log => "log",
122 AutoCreateTableType::LastNonNull => "last_non_null",
123 AutoCreateTableType::Trace { .. } => "trace",
124 }
125 }
126
127 fn alter_existing(&self) -> bool {
128 !matches!(
129 self,
130 Self::Trace {
131 alter_existing: false
132 }
133 )
134 }
135}
136
137#[derive(Clone)]
144pub struct InstantAndNormalInsertRequests {
145 pub normal_requests: RegionInsertRequests,
147 pub instant_requests: RegionInsertRequests,
150}
151
152impl Inserter {
153 pub fn new(
154 catalog_manager: CatalogManagerRef,
155 partition_manager: PartitionRuleManagerRef,
156 node_manager: NodeManagerRef,
157 table_flownode_set_cache: TableFlownodeSetCacheRef,
158 auto_create_table: bool,
159 ) -> Self {
160 Self {
161 catalog_manager,
162 partition_manager,
163 node_manager,
164 table_flownode_set_cache,
165 auto_create_table,
166 }
167 }
168
169 pub async fn handle_column_inserts(
170 &self,
171 requests: InsertRequests,
172 ctx: QueryContextRef,
173 statement_executor: &StatementExecutor,
174 ) -> Result<Output> {
175 let row_inserts = ColumnToRow::convert(requests)?;
176 self.handle_row_inserts(row_inserts, ctx, statement_executor, false, false)
177 .await
178 }
179
180 pub async fn handle_row_inserts(
182 &self,
183 mut requests: RowInsertRequests,
184 ctx: QueryContextRef,
185 statement_executor: &StatementExecutor,
186 accommodate_existing_schema: bool,
187 is_single_value: bool,
188 ) -> Result<Output> {
189 preprocess_row_insert_requests(&mut requests.inserts)?;
190 self.handle_row_inserts_with_create_type(
191 requests,
192 ctx,
193 statement_executor,
194 AutoCreateTableType::Physical,
195 accommodate_existing_schema,
196 is_single_value,
197 )
198 .await
199 }
200
201 pub async fn handle_log_inserts(
203 &self,
204 requests: RowInsertRequests,
205 ctx: QueryContextRef,
206 statement_executor: &StatementExecutor,
207 ) -> Result<Output> {
208 self.handle_row_inserts_with_create_type(
209 requests,
210 ctx,
211 statement_executor,
212 AutoCreateTableType::Log,
213 false,
214 false,
215 )
216 .await
217 }
218
219 pub async fn handle_trace_inserts(
220 &self,
221 requests: RowInsertRequests,
222 ctx: QueryContextRef,
223 statement_executor: &StatementExecutor,
224 ) -> Result<Output> {
225 self.handle_row_inserts_with_create_type(
226 requests,
227 ctx,
228 statement_executor,
229 AutoCreateTableType::Trace {
230 alter_existing: true,
231 },
232 false,
233 false,
234 )
235 .await
236 }
237
238 pub async fn handle_last_non_null_inserts(
240 &self,
241 requests: RowInsertRequests,
242 ctx: QueryContextRef,
243 statement_executor: &StatementExecutor,
244 accommodate_existing_schema: bool,
245 is_single_value: bool,
246 ) -> Result<Output> {
247 self.handle_row_inserts_with_create_type(
248 requests,
249 ctx,
250 statement_executor,
251 AutoCreateTableType::LastNonNull,
252 accommodate_existing_schema,
253 is_single_value,
254 )
255 .await
256 }
257
258 async fn handle_row_inserts_with_create_type(
260 &self,
261 mut requests: RowInsertRequests,
262 ctx: QueryContextRef,
263 statement_executor: &StatementExecutor,
264 create_type: AutoCreateTableType,
265 accommodate_existing_schema: bool,
266 is_single_value: bool,
267 ) -> Result<Output> {
268 let skip_wal = ctx.skip_wal();
269
270 requests.inserts.retain(|req| {
272 req.rows
273 .as_ref()
274 .map(|r| !r.rows.is_empty())
275 .unwrap_or_default()
276 });
277 validate_column_count_match(&requests)?;
278
279 let CreateAlterTableResult {
280 instant_table_ids,
281 table_infos,
282 } = self
283 .create_or_alter_tables_on_demand(
284 &mut requests,
285 &ctx,
286 create_type,
287 statement_executor,
288 accommodate_existing_schema,
289 is_single_value,
290 )
291 .await?;
292
293 let name_to_info = table_infos
294 .values()
295 .map(|info| (info.name.clone(), info.clone()))
296 .collect::<HashMap<_, _>>();
297 let inserts = RowToRegion::new(
298 name_to_info,
299 instant_table_ids,
300 self.partition_manager.as_ref(),
301 )
302 .convert(requests, skip_wal)
303 .await?;
304
305 self.do_request(inserts, &table_infos, &ctx).await
306 }
307
308 pub async fn handle_metric_row_inserts(
310 &self,
311 mut requests: RowInsertRequests,
312 ctx: QueryContextRef,
313 statement_executor: &StatementExecutor,
314 physical_table: String,
315 ) -> Result<Output> {
316 let skip_wal = ctx.skip_wal();
317
318 requests.inserts.retain(|req| {
320 req.rows
321 .as_ref()
322 .map(|r| !r.rows.is_empty())
323 .unwrap_or_default()
324 });
325 validate_column_count_match(&requests)?;
326
327 self.create_physical_table_on_demand(&ctx, physical_table.clone(), statement_executor)
329 .await?;
330
331 let CreateAlterTableResult {
333 instant_table_ids,
334 table_infos,
335 } = self
336 .create_or_alter_tables_on_demand(
337 &mut requests,
338 &ctx,
339 AutoCreateTableType::Logical(physical_table.clone()),
340 statement_executor,
341 true,
342 true,
343 )
344 .await?;
345 let name_to_info = table_infos
346 .values()
347 .map(|info| (info.name.clone(), info.clone()))
348 .collect::<HashMap<_, _>>();
349 let inserts = RowToRegion::new(name_to_info, instant_table_ids, &self.partition_manager)
350 .convert(requests, skip_wal)
351 .await?;
352
353 self.do_request(inserts, &table_infos, &ctx).await
354 }
355
356 pub async fn handle_table_insert(
357 &self,
358 request: TableInsertRequest,
359 ctx: QueryContextRef,
360 ) -> Result<Output> {
361 let catalog = request.catalog_name.as_str();
362 let schema = request.schema_name.as_str();
363 let table_name = request.table_name.as_str();
364 let table = self.get_table(catalog, schema, table_name).await?;
365 let table = table.with_context(|| TableNotFoundSnafu {
366 table_name: common_catalog::format_full_table_name(catalog, schema, table_name),
367 })?;
368 let table_info = table.table_info();
369
370 let inserts = TableToRegion::new(&table_info, &self.partition_manager)
371 .convert(request)
372 .await?;
373
374 let table_infos = HashMap::from_iter([(table_info.table_id(), table_info.clone())]);
375
376 self.do_request(inserts, &table_infos, &ctx).await
377 }
378
379 pub async fn handle_statement_insert(
380 &self,
381 insert: &Insert,
382 ctx: &QueryContextRef,
383 ) -> Result<Output> {
384 let (inserts, table_info) =
385 StatementToRegion::new(self.catalog_manager.as_ref(), &self.partition_manager, ctx)
386 .convert(insert, ctx)
387 .await?;
388
389 let table_infos = HashMap::from_iter([(table_info.table_id(), table_info.clone())]);
390
391 self.do_request(inserts, &table_infos, ctx).await
392 }
393}
394
395impl Inserter {
396 async fn do_request(
397 &self,
398 requests: InstantAndNormalInsertRequests,
399 table_infos: &HashMap<TableId, Arc<TableInfo>>,
400 ctx: &QueryContextRef,
401 ) -> Result<Output> {
402 let requests = fill_reqs_with_impure_default(table_infos, requests)?;
404
405 let write_cost = write_meter!(
406 ctx.current_catalog(),
407 ctx.current_schema(),
408 requests,
409 ctx.channel() as u8
410 );
411 let request_factory = RegionRequestFactory::new(RegionRequestHeader {
412 tracing_context: TracingContext::from_current_span().to_w3c(),
413 dbname: ctx.get_db_string(),
414 ..Default::default()
415 });
416
417 let InstantAndNormalInsertRequests {
418 normal_requests,
419 instant_requests,
420 } = requests;
421
422 let flow_mirror_task = FlowMirrorTask::new(
424 &self.table_flownode_set_cache,
425 normal_requests
426 .requests
427 .iter()
428 .chain(instant_requests.requests.iter()),
429 )
430 .await?;
431 flow_mirror_task.detach(self.node_manager.clone())?;
432
433 let write_tasks = self
435 .group_requests_by_peer(normal_requests)
436 .await?
437 .into_iter()
438 .map(|(peer, inserts)| {
439 let node_manager = self.node_manager.clone();
440 let request = request_factory.build_insert(inserts);
441 common_runtime::spawn_global(async move {
442 node_manager
443 .datanode(&peer)
444 .await
445 .handle(request)
446 .await
447 .context(RequestInsertsSnafu)
448 })
449 });
450 let results = future::try_join_all(write_tasks)
451 .await
452 .context(JoinTaskSnafu)?;
453 let affected_rows = results
454 .into_iter()
455 .map(|resp| resp.map(|r| r.affected_rows))
456 .sum::<Result<AffectedRows>>()?;
457 crate::metrics::DIST_INGEST_ROW_COUNT
458 .with_label_values(&[ctx.get_db_string().as_str()])
459 .inc_by(affected_rows as u64);
460 Ok(Output::new(
461 OutputData::AffectedRows(affected_rows),
462 OutputMeta::new_with_cost(write_cost as _),
463 ))
464 }
465
466 async fn group_requests_by_peer(
467 &self,
468 requests: RegionInsertRequests,
469 ) -> Result<HashMap<Peer, RegionInsertRequests>> {
470 let mut requests_per_region: HashMap<RegionId, RegionInsertRequests> = HashMap::new();
473 for req in requests.requests {
474 let region_id = RegionId::from_u64(req.region_id);
475 requests_per_region
476 .entry(region_id)
477 .or_default()
478 .requests
479 .push(req);
480 }
481
482 let mut inserts: HashMap<Peer, RegionInsertRequests> = HashMap::new();
483
484 for (region_id, reqs) in requests_per_region {
485 let peer = self
486 .partition_manager
487 .find_region_leader(region_id)
488 .await
489 .context(FindRegionLeaderSnafu)?;
490 inserts
491 .entry(peer)
492 .or_default()
493 .requests
494 .extend(reqs.requests);
495 }
496
497 Ok(inserts)
498 }
499
500 fn auto_create_disabled_reason(&self, ctx: &QueryContextRef) -> Result<Option<&'static str>> {
504 let auto_create_table_hint = ctx
505 .extension(AUTO_CREATE_TABLE_KEY)
506 .map(|v| v.parse::<bool>())
507 .transpose()
508 .map_err(|_| {
509 InvalidInsertRequestSnafu {
510 reason: "`auto_create_table` hint must be a boolean",
511 }
512 .build()
513 })?
514 .unwrap_or(true);
515 Ok(if !self.auto_create_table {
516 Some("auto-create table is disabled by frontend config")
517 } else if !auto_create_table_hint {
518 Some("`auto_create_table` hint is disabled")
519 } else {
520 None
521 })
522 }
523
524 fn is_auto_create_exempt_private_table(schema: &str, table: &str) -> bool {
527 schema == DEFAULT_PRIVATE_SCHEMA_NAME
528 && matches!(
529 table,
530 DEFAULT_EVENTS_TABLE_NAME | SLOW_QUERY_TABLE_NAME | REGION_STATS_HISTORY_TABLE_NAME
531 )
532 }
533
534 pub async fn ensure_trace_table_on_demand(
539 &self,
540 table_name: &str,
541 request_schema: Vec<ColumnSchema>,
542 alter_existing: bool,
543 ctx: &QueryContextRef,
544 statement_executor: &StatementExecutor,
545 ) -> Result<()> {
546 let mut requests = RowInsertRequests {
547 inserts: vec![RowInsertRequest {
548 table_name: table_name.to_string(),
549 rows: Some(api::v1::Rows {
550 schema: request_schema,
551 rows: Vec::new(),
552 }),
553 }],
554 };
555 self.create_or_alter_tables_on_demand(
556 &mut requests,
557 ctx,
558 AutoCreateTableType::Trace { alter_existing },
559 statement_executor,
560 false,
561 false,
562 )
563 .await?;
564 Ok(())
565 }
566
567 async fn create_or_alter_tables_on_demand(
580 &self,
581 requests: &mut RowInsertRequests,
582 ctx: &QueryContextRef,
583 auto_create_table_type: AutoCreateTableType,
584 statement_executor: &StatementExecutor,
585 accommodate_existing_schema: bool,
586 is_single_value: bool,
587 ) -> Result<CreateAlterTableResult> {
588 let _timer = crate::metrics::CREATE_ALTER_ON_DEMAND
589 .with_label_values(&[auto_create_table_type.as_str()])
590 .start_timer();
591 let catalog = ctx.current_catalog();
592 let schema = ctx.current_schema();
593
594 let auto_create_disabled_reason = self.auto_create_disabled_reason(ctx)?;
595 let has_auto_create_exempt_table = auto_create_disabled_reason.is_some()
597 && requests
598 .inserts
599 .iter()
600 .any(|req| Self::is_auto_create_exempt_private_table(&schema, &req.table_name));
601 let mut table_infos = HashMap::new();
602 if let Some(disabled_reason) = auto_create_disabled_reason
604 && !has_auto_create_exempt_table
605 {
606 let mut instant_table_ids = HashSet::new();
607 for req in &requests.inserts {
608 let table = match self.get_table(catalog, &schema, &req.table_name).await? {
609 Some(table) => table,
610 None if is_ddl_reserved_table(&schema, &req.table_name) => {
613 statement_executor
614 .create_declared_relationships_table(catalog, ctx.clone())
615 .await?
616 }
617 None => {
618 return InvalidInsertRequestSnafu {
619 reason: format!(
620 "Table `{}` does not exist, and {}",
621 req.table_name, disabled_reason
622 ),
623 }
624 .fail();
625 }
626 };
627 let table_info = table.table_info();
628 if table_info.is_ttl_instant_table() {
629 instant_table_ids.insert(table_info.table_id());
630 }
631 table_infos.insert(table_info.table_id(), table.table_info());
632 }
633 let ret = CreateAlterTableResult {
634 instant_table_ids,
635 table_infos,
636 };
637 return Ok(ret);
638 }
639
640 let mut create_tables = vec![];
641 let mut alter_tables = vec![];
642 let mut need_refresh_table_infos = HashSet::new();
643 let mut instant_table_ids = HashSet::new();
644 let mut per_table_semantics: Option<Option<PerTableSemanticIndex>> = None;
645
646 for req in &mut requests.inserts {
647 let auto_create_allowed = auto_create_disabled_reason.is_none()
649 || Self::is_auto_create_exempt_private_table(&schema, &req.table_name);
650 match self.get_table(catalog, &schema, &req.table_name).await? {
651 Some(table) => {
652 let table_info = table.table_info();
653 if table_info.is_ttl_instant_table() {
654 instant_table_ids.insert(table_info.table_id());
655 }
656 if auto_create_allowed
657 && let Some(alter_expr) = self.get_alter_table_expr_on_demand(
658 req,
659 &table,
660 ctx,
661 accommodate_existing_schema,
662 is_single_value,
663 auto_create_table_type.alter_existing(),
664 )?
665 {
666 alter_tables.push(alter_expr);
667 need_refresh_table_infos.insert((
668 catalog.to_string(),
669 schema.clone(),
670 req.table_name.clone(),
671 ));
672 } else {
673 table_infos.insert(table_info.table_id(), table.table_info());
674 }
675 }
676 None if is_ddl_reserved_table(&schema, &req.table_name) => {
680 let table = statement_executor
681 .create_declared_relationships_table(catalog, ctx.clone())
682 .await?;
683 let table_info = table.table_info();
684 if table_info.is_ttl_instant_table() {
685 instant_table_ids.insert(table_info.table_id());
686 }
687 table_infos.insert(table_info.table_id(), table_info);
688 }
689 None if !auto_create_allowed
690 && let Some(disabled_reason) = auto_create_disabled_reason =>
691 {
692 return InvalidInsertRequestSnafu {
693 reason: format!(
694 "Table `{}` does not exist, and {}",
695 req.table_name, disabled_reason,
696 ),
697 }
698 .fail();
699 }
700 None => {
701 let semantic_index = per_table_semantics
702 .get_or_insert_with(|| parse_per_table_semantic_index(ctx))
703 .as_ref();
704 let create_expr = self.get_create_table_expr_on_demand(
705 req,
706 &auto_create_table_type,
707 ctx,
708 semantic_index,
709 )?;
710 create_tables.push(create_expr);
711 }
712 }
713 }
714
715 match auto_create_table_type {
716 AutoCreateTableType::Logical(_) => {
717 if !create_tables.is_empty() {
718 let tables = self
720 .create_logical_tables(create_tables, ctx, statement_executor)
721 .await?;
722
723 for table in tables {
724 let table_info = table.table_info();
725 if table_info.is_ttl_instant_table() {
726 instant_table_ids.insert(table_info.table_id());
727 }
728 table_infos.insert(table_info.table_id(), table.table_info());
729 }
730 }
731 if !alter_tables.is_empty() {
732 statement_executor
734 .alter_logical_tables(alter_tables, ctx.clone(), TriggerReason::AutoAlter)
735 .await?;
736 }
737 }
738 AutoCreateTableType::Physical
739 | AutoCreateTableType::Log
740 | AutoCreateTableType::LastNonNull => {
741 for create_table in create_tables {
744 let table = self
745 .create_physical_table(create_table, None, ctx, statement_executor)
746 .await?;
747 let table_info = table.table_info();
748 if table_info.is_ttl_instant_table() {
749 instant_table_ids.insert(table_info.table_id());
750 }
751 table_infos.insert(table_info.table_id(), table.table_info());
752 }
753 for alter_expr in alter_tables.into_iter() {
754 statement_executor
755 .alter_table_inner(alter_expr, ctx.clone(), TriggerReason::AutoAlter)
756 .await?;
757 }
758 }
759
760 AutoCreateTableType::Trace { .. } => {
761 let trace_table_name = ctx
762 .extension(TRACE_TABLE_NAME_SESSION_KEY)
763 .unwrap_or(TRACE_TABLE_NAME);
764
765 let trace_table_partitions = if let Some(trace_table_partitions) =
766 ctx.extension(TRACE_TABLE_PARTITIONS_HINT_KEY)
767 {
768 let p = trace_table_partitions.parse::<u32>().map_err(|_| {
769 InvalidInsertRequestSnafu {
770 reason: format!(
771 "Failed to parse trace_table_partitions: {}",
772 trace_table_partitions
773 ),
774 }
775 .build()
776 })?;
777 Some(p)
778 } else {
779 None
780 };
781
782 for mut create_table in create_tables {
785 if create_table.table_name == trace_services_table_name(trace_table_name)
786 || create_table.table_name == trace_operations_table_name(trace_table_name)
787 {
788 create_table
790 .table_options
791 .insert(APPEND_MODE_KEY.to_string(), "false".to_string());
792 create_table.table_options.remove(TTL_KEY);
794
795 let table = self
796 .create_physical_table(create_table, None, ctx, statement_executor)
797 .await?;
798 let table_info = table.table_info();
799 if table_info.is_ttl_instant_table() {
800 instant_table_ids.insert(table_info.table_id());
801 }
802 table_infos.insert(table_info.table_id(), table.table_info());
803 } else {
804 let partitions = if matches!(trace_table_partitions, Some(0) | Some(1)) {
807 None
809 } else {
810 let p = partition_rule_for_hexstring(
811 TRACE_ID_COLUMN,
812 trace_table_partitions,
813 )
814 .context(CreatePartitionRulesSnafu)?;
815 Some(p)
816 };
817
818 let index_columns =
823 [TRACE_ID_COLUMN, PARENT_SPAN_ID_COLUMN, SERVICE_NAME_COLUMN];
824 for index_column in index_columns {
825 if let Some(col) = create_table
826 .column_defs
827 .iter_mut()
828 .find(|c| c.name == index_column)
829 {
830 col.options =
831 options_from_skipping(&SkippingIndexOptions::default())
832 .context(ColumnOptionsSnafu)?;
833 } else {
834 warn!(
835 "Column {} not found when creating index for trace table: {}.",
836 index_column, create_table.table_name
837 );
838 }
839 }
840
841 create_table.table_options.insert(
843 TABLE_DATA_MODEL.to_string(),
844 TABLE_DATA_MODEL_TRACE_V1.to_string(),
845 );
846
847 let table = self
848 .create_physical_table(
849 create_table,
850 partitions,
851 ctx,
852 statement_executor,
853 )
854 .await?;
855 let table_info = table.table_info();
856 if table_info.is_ttl_instant_table() {
857 instant_table_ids.insert(table_info.table_id());
858 }
859 table_infos.insert(table_info.table_id(), table.table_info());
860 }
861 }
862 for alter_expr in alter_tables.into_iter() {
863 statement_executor
864 .alter_table_inner(alter_expr, ctx.clone(), TriggerReason::AutoAlter)
865 .await?;
866 }
867 }
868 }
869
870 for (catalog, schema, table_name) in need_refresh_table_infos {
872 let table = self
873 .get_table(&catalog, &schema, &table_name)
874 .await?
875 .context(TableNotFoundSnafu {
876 table_name: common_catalog::format_full_table_name(
877 &catalog,
878 &schema,
879 &table_name,
880 ),
881 })?;
882 let table_info = table.table_info();
883 table_infos.insert(table_info.table_id(), table.table_info());
884 }
885
886 Ok(CreateAlterTableResult {
887 instant_table_ids,
888 table_infos,
889 })
890 }
891
892 async fn create_physical_table_on_demand(
893 &self,
894 ctx: &QueryContextRef,
895 physical_table: String,
896 statement_executor: &StatementExecutor,
897 ) -> Result<()> {
898 let catalog_name = ctx.current_catalog();
899 let schema_name = ctx.current_schema();
900
901 if self
903 .get_table(catalog_name, &schema_name, &physical_table)
904 .await?
905 .is_some()
906 {
907 return Ok(());
908 }
909
910 if let Some(disabled_reason) = self.auto_create_disabled_reason(ctx)? {
912 return InvalidInsertRequestSnafu {
913 reason: format!(
914 "Physical table `{physical_table}` does not exist, and {disabled_reason}"
915 ),
916 }
917 .fail();
918 }
919
920 let table_reference = TableReference::full(catalog_name, &schema_name, &physical_table);
921 info!("Physical metric table `{table_reference}` does not exist, try creating table");
922
923 let default_schema = vec![
925 ColumnSchema {
926 column_name: greptime_timestamp().to_string(),
927 datatype: ColumnDataType::TimestampMillisecond as _,
928 semantic_type: SemanticType::Timestamp as _,
929 datatype_extension: None,
930 options: None,
931 },
932 ColumnSchema {
933 column_name: greptime_value().to_string(),
934 datatype: ColumnDataType::Float64 as _,
935 semantic_type: SemanticType::Field as _,
936 datatype_extension: None,
937 options: None,
938 },
939 ];
940 let create_table_expr =
941 &mut build_create_table_expr(&table_reference, &default_schema, default_engine())?;
942
943 create_table_expr.engine = METRIC_ENGINE_NAME.to_string();
944 create_table_expr
945 .table_options
946 .insert(PHYSICAL_TABLE_METADATA_KEY.to_string(), "true".to_string());
947
948 let res = statement_executor
950 .create_table_inner(
951 create_table_expr,
952 None,
953 ctx.clone(),
954 TriggerReason::AutoCreate,
955 )
956 .await;
957
958 match res {
959 Ok(_) => {
960 info!("Successfully created table {table_reference}",);
961 Ok(())
962 }
963 Err(err) => {
964 error!(err; "Failed to create table {table_reference}");
965 Err(err)
966 }
967 }
968 }
969
970 async fn get_table(
971 &self,
972 catalog: &str,
973 schema: &str,
974 table: &str,
975 ) -> Result<Option<TableRef>> {
976 self.catalog_manager
977 .table(catalog, schema, table, None)
978 .await
979 .context(CatalogSnafu)
980 }
981
982 fn get_create_table_expr_on_demand(
983 &self,
984 req: &RowInsertRequest,
985 create_type: &AutoCreateTableType,
986 ctx: &QueryContextRef,
987 semantic_index: Option<&PerTableSemanticIndex>,
988 ) -> Result<CreateTableExpr> {
989 let schema = ctx.current_schema();
990 let mut table_options = std::collections::HashMap::with_capacity(4);
991 fill_table_options_for_create(&mut table_options, create_type, ctx);
992 apply_per_table_semantic_options(
993 &mut table_options,
994 semantic_index,
995 ctx.current_schema().as_str(),
996 &req.table_name,
997 );
998
999 let engine_name = if let AutoCreateTableType::Logical(_) = create_type {
1000 METRIC_ENGINE_NAME
1002 } else {
1003 default_engine()
1004 };
1005
1006 let table_ref = TableReference::full(ctx.current_catalog(), &schema, &req.table_name);
1007 let request_schema = req.rows.as_ref().unwrap().schema.as_slice();
1009 let mut create_table_expr =
1010 build_create_table_expr(&table_ref, request_schema, engine_name)?;
1011
1012 if ctx.extension(SPLUNK_PK_METADATA_ORDER_KEY).is_some() {
1014 reorder_splunk_primary_keys(&mut create_table_expr.primary_keys);
1015 }
1016
1017 info!("Table `{table_ref}` does not exist, try creating table");
1018 create_table_expr.table_options.extend(table_options);
1019 Ok(create_table_expr)
1020 }
1021
1022 fn get_alter_table_expr_on_demand(
1030 &self,
1031 req: &mut RowInsertRequest,
1032 table: &TableRef,
1033 ctx: &QueryContextRef,
1034 accommodate_existing_schema: bool,
1035 is_single_value: bool,
1036 alter_existing: bool,
1037 ) -> Result<Option<AlterTableExpr>> {
1038 if !alter_existing {
1039 return Ok(None);
1040 }
1041
1042 let catalog_name = ctx.current_catalog();
1043 let schema_name = ctx.current_schema();
1044 let table_name = table.table_info().name.clone();
1045
1046 if is_ddl_reserved_table(&schema_name, &table_name) {
1049 return Ok(None);
1050 }
1051
1052 let request_schema = req.rows.as_ref().unwrap().schema.as_slice();
1053 let request_field_count = request_schema
1054 .iter()
1055 .filter(|col| col.semantic_type == SemanticType::Field as i32)
1056 .count();
1057 let column_exprs = ColumnExpr::from_column_schemas(request_schema);
1058 let add_columns = expr_helper::extract_add_columns_expr(&table.schema(), column_exprs)?;
1059 let Some(mut add_columns) = add_columns else {
1060 return Ok(None);
1061 };
1062
1063 if is_single_value {
1064 let request_is_native_histogram = request_is_native_histogram(request_schema);
1065 let table_is_native_histogram = table_is_native_histogram(table);
1066 ensure!(
1067 request_is_native_histogram == table_is_native_histogram,
1068 InvalidInsertRequestSnafu {
1069 reason: format!(
1070 "Table `{table_name}` cannot mix native histogram and float sample fields"
1071 ),
1072 }
1073 );
1074 }
1075
1076 if accommodate_existing_schema {
1078 let table_schema = table.schema();
1079 let ts_col_name = table_schema.timestamp_column().map(|c| c.name.clone());
1081 let mut field_col_name = None;
1083 if is_single_value && request_field_count <= 1 {
1084 let mut multiple_field_cols = false;
1085 table.field_columns().for_each(|col| {
1086 if field_col_name.is_none() {
1087 field_col_name = Some(col.name.clone());
1088 } else {
1089 multiple_field_cols = true;
1090 }
1091 });
1092 if multiple_field_cols {
1093 field_col_name = None;
1094 }
1095 }
1096
1097 if let Some(rows) = req.rows.as_mut() {
1099 for col in &mut rows.schema {
1100 match col.semantic_type {
1101 x if x == SemanticType::Timestamp as i32 => {
1102 if let Some(ref ts_name) = ts_col_name
1103 && col.column_name != *ts_name
1104 {
1105 col.column_name = ts_name.clone();
1106 }
1107 }
1108 x if x == SemanticType::Field as i32 => {
1109 if let Some(ref field_name) = field_col_name
1110 && col.column_name != *field_name
1111 {
1112 col.column_name = field_name.clone();
1113 }
1114 }
1115 _ => {}
1116 }
1117 }
1118 }
1119
1120 add_columns.add_columns.retain(|col| {
1122 let def = col.column_def.as_ref().unwrap();
1123 def.semantic_type == SemanticType::Tag as i32
1124 || (def.semantic_type == SemanticType::Field as i32 && field_col_name.is_none())
1125 });
1126
1127 if add_columns.add_columns.is_empty() {
1128 return Ok(None);
1129 }
1130 }
1131
1132 Ok(Some(AlterTableExpr {
1133 catalog_name: catalog_name.to_string(),
1134 schema_name: schema_name.clone(),
1135 table_name: table_name.clone(),
1136 kind: Some(Kind::AddColumns(add_columns)),
1137 }))
1138 }
1139
1140 async fn create_physical_table(
1142 &self,
1143 mut create_table_expr: CreateTableExpr,
1144 partitions: Option<Partitions>,
1145 ctx: &QueryContextRef,
1146 statement_executor: &StatementExecutor,
1147 ) -> Result<TableRef> {
1148 {
1149 let table_ref = TableReference::full(
1150 &create_table_expr.catalog_name,
1151 &create_table_expr.schema_name,
1152 &create_table_expr.table_name,
1153 );
1154
1155 info!("Table `{table_ref}` does not exist, try creating table");
1156 }
1157 let res = statement_executor
1158 .create_table_inner(
1159 &mut create_table_expr,
1160 partitions,
1161 ctx.clone(),
1162 TriggerReason::AutoCreate,
1163 )
1164 .await;
1165
1166 let table_ref = TableReference::full(
1167 &create_table_expr.catalog_name,
1168 &create_table_expr.schema_name,
1169 &create_table_expr.table_name,
1170 );
1171
1172 match res {
1173 Ok(table) => {
1174 info!(
1175 "Successfully created table {} with options: {:?}",
1176 table_ref, create_table_expr.table_options,
1177 );
1178 Ok(table)
1179 }
1180 Err(err) => {
1181 error!(err; "Failed to create table {}", table_ref);
1182 Err(err)
1183 }
1184 }
1185 }
1186
1187 async fn create_logical_tables(
1188 &self,
1189 create_table_exprs: Vec<CreateTableExpr>,
1190 ctx: &QueryContextRef,
1191 statement_executor: &StatementExecutor,
1192 ) -> Result<Vec<TableRef>> {
1193 let res = statement_executor
1194 .create_logical_tables(&create_table_exprs, ctx.clone(), TriggerReason::AutoCreate)
1195 .await;
1196
1197 match res {
1198 Ok(res) => {
1199 info!("Successfully created logical tables");
1200 Ok(res)
1201 }
1202 Err(err) => {
1203 let failed_tables = create_table_exprs
1204 .into_iter()
1205 .map(|expr| {
1206 format!(
1207 "{}.{}.{}",
1208 expr.catalog_name, expr.schema_name, expr.table_name
1209 )
1210 })
1211 .collect::<Vec<_>>();
1212 error!(
1213 err;
1214 "Failed to create logical tables {:?}",
1215 failed_tables
1216 );
1217 Err(err)
1218 }
1219 }
1220 }
1221
1222 pub fn node_manager(&self) -> &NodeManagerRef {
1223 &self.node_manager
1224 }
1225
1226 pub fn partition_manager(&self) -> &PartitionRuleManagerRef {
1227 &self.partition_manager
1228 }
1229
1230 pub fn table_flownode_set_cache(&self) -> &TableFlownodeSetCacheRef {
1231 &self.table_flownode_set_cache
1232 }
1233}
1234
1235fn request_is_native_histogram(request_schema: &[ColumnSchema]) -> bool {
1236 let mut fields = request_schema
1237 .iter()
1238 .filter(|col| col.semantic_type == SemanticType::Field as i32);
1239 let Some(col) = fields.next() else {
1240 return false;
1241 };
1242
1243 fields.next().is_none()
1244 && api::helper::is_column_type_value_eq(
1245 col.datatype,
1246 col.datatype_extension.clone(),
1247 native_histogram_value_type(),
1248 )
1249}
1250
1251fn table_is_native_histogram(table: &TableRef) -> bool {
1252 let mut fields = table.field_columns();
1253 let Some(col) = fields.next() else {
1254 return false;
1255 };
1256
1257 fields.next().is_none() && is_native_histogram_value_type(&col.data_type)
1258}
1259
1260fn validate_column_count_match(requests: &RowInsertRequests) -> Result<()> {
1261 for request in &requests.inserts {
1262 let rows = request.rows.as_ref().unwrap();
1263 let column_count = rows.schema.len();
1264 rows.rows.iter().try_for_each(|r| {
1265 ensure!(
1266 r.values.len() == column_count,
1267 InvalidInsertRequestSnafu {
1268 reason: format!(
1269 "column count mismatch, columns: {}, values: {}",
1270 column_count,
1271 r.values.len()
1272 )
1273 }
1274 );
1275 Ok(())
1276 })?;
1277 }
1278 Ok(())
1279}
1280
1281pub fn fill_table_options_for_create(
1283 table_options: &mut std::collections::HashMap<String, String>,
1284 create_type: &AutoCreateTableType,
1285 ctx: &QueryContextRef,
1286) {
1287 for key in VALID_TABLE_OPTION_KEYS {
1288 if let Some(value) = ctx.extension(key) {
1289 table_options.insert(key.to_string(), value.to_string());
1290 }
1291 }
1292
1293 for (key, value) in ctx.extensions() {
1295 if is_semantic_option_key(&key) && validate_semantic_option(&key, &value) {
1296 table_options.insert(key, value);
1297 }
1298 }
1299
1300 match create_type {
1301 AutoCreateTableType::Logical(physical_table) => {
1302 table_options.insert(
1303 LOGICAL_TABLE_METADATA_KEY.to_string(),
1304 physical_table.clone(),
1305 );
1306 }
1307 AutoCreateTableType::Physical => {
1308 if let Some(append_mode) = ctx.extension(APPEND_MODE_KEY) {
1309 table_options.insert(APPEND_MODE_KEY.to_string(), append_mode.to_string());
1310 }
1311 if let Some(merge_mode) = ctx.extension(MERGE_MODE_KEY) {
1312 table_options.insert(MERGE_MODE_KEY.to_string(), merge_mode.to_string());
1313 }
1314 if let Some(time_window) = ctx.extension(TWCS_TIME_WINDOW) {
1315 table_options.insert(TWCS_TIME_WINDOW.to_string(), time_window.to_string());
1316 table_options.insert(
1318 COMPACTION_TYPE.to_string(),
1319 COMPACTION_TYPE_TWCS.to_string(),
1320 );
1321 }
1322 }
1323 AutoCreateTableType::Log => {
1326 table_options.insert(APPEND_MODE_KEY.to_string(), "true".to_string());
1327 }
1328 AutoCreateTableType::LastNonNull => {
1329 if ctx
1330 .extension(APPEND_MODE_KEY)
1331 .is_some_and(|value| value.eq_ignore_ascii_case("true"))
1332 {
1333 table_options.insert(APPEND_MODE_KEY.to_string(), "true".to_string());
1334 table_options.insert(MERGE_MODE_KEY.to_string(), "last_row".to_string());
1335 } else if let Some(merge_mode) = ctx.extension(MERGE_MODE_KEY) {
1336 table_options.insert(MERGE_MODE_KEY.to_string(), merge_mode.to_string());
1337 } else {
1338 table_options.insert(MERGE_MODE_KEY.to_string(), "last_non_null".to_string());
1339 }
1340 }
1341 AutoCreateTableType::Trace { .. } => {
1342 table_options.insert(APPEND_MODE_KEY.to_string(), "true".to_string());
1343 }
1344 }
1345}
1346
1347pub type PerTableSemanticIndex = BTreeMap<String, BTreeMap<String, BTreeMap<String, String>>>;
1353
1354pub fn parse_per_table_semantic_index(ctx: &QueryContextRef) -> Option<PerTableSemanticIndex> {
1359 let raw = ctx.extension(SEMANTIC_PER_TABLE_INDEX_KEY)?;
1360 match serde_json::from_str(raw) {
1361 Ok(index) => Some(index),
1362 Err(_) => {
1363 warn!("failed to parse semantic per-table index, skipping per-table options");
1364 None
1365 }
1366 }
1367}
1368
1369pub fn apply_per_table_semantic_options(
1377 table_options: &mut std::collections::HashMap<String, String>,
1378 index: Option<&PerTableSemanticIndex>,
1379 schema: &str,
1380 table_name: &str,
1381) {
1382 let Some(entry) = index
1383 .and_then(|index| index.get(schema))
1384 .and_then(|tables| tables.get(table_name))
1385 else {
1386 return;
1387 };
1388 for (key, value) in entry {
1389 if is_semantic_option_key(key) && validate_semantic_option(key, value) {
1390 table_options.insert(key.clone(), value.clone());
1391 }
1392 }
1393}
1394
1395pub fn build_create_table_expr(
1396 table: &TableReference,
1397 request_schema: &[ColumnSchema],
1398 engine: &str,
1399) -> Result<CreateTableExpr> {
1400 expr_helper::create_table_expr_by_column_schemas(table, request_schema, engine, None)
1401}
1402
1403pub const SPLUNK_PK_METADATA_ORDER_KEY: &str = "splunk_pk_metadata_order";
1407
1408fn reorder_splunk_primary_keys(primary_keys: &mut [String]) {
1411 const LEAD: [&str; 3] = ["host", "source", "sourcetype"];
1412 primary_keys.sort_by_key(|name| {
1415 LEAD.iter()
1416 .position(|&lead| lead == name.as_str())
1417 .unwrap_or(LEAD.len())
1418 });
1419}
1420
1421struct CreateAlterTableResult {
1423 instant_table_ids: HashSet<TableId>,
1425 table_infos: HashMap<TableId, Arc<TableInfo>>,
1427}
1428
1429struct FlowMirrorTask {
1430 requests: HashMap<Peer, RegionInsertRequests>,
1431 num_rows: usize,
1432}
1433
1434impl FlowMirrorTask {
1435 async fn new(
1436 cache: &TableFlownodeSetCacheRef,
1437 requests: impl Iterator<Item = &RegionInsertRequest>,
1438 ) -> Result<Self> {
1439 let mut src_table_reqs: HashMap<TableId, Option<(Vec<Peer>, RegionInsertRequests)>> =
1440 HashMap::new();
1441 let mut num_rows = 0;
1442
1443 for req in requests {
1444 let table_id = RegionId::from_u64(req.region_id).table_id();
1445 match src_table_reqs.get_mut(&table_id) {
1446 Some(Some((_peers, reqs))) => reqs.requests.push(req.clone()),
1447 Some(None) => continue,
1449 _ => {
1450 let peers = cache
1452 .get(table_id)
1453 .await
1454 .context(RequestInsertsSnafu)?
1455 .unwrap_or_default()
1456 .values()
1457 .cloned()
1458 .collect::<HashSet<_>>()
1459 .into_iter()
1460 .collect::<Vec<_>>();
1461
1462 if !peers.is_empty() {
1463 let mut reqs = RegionInsertRequests::default();
1464 reqs.requests.push(req.clone());
1465 num_rows += reqs
1466 .requests
1467 .iter()
1468 .map(|r| r.rows.as_ref().unwrap().rows.len())
1469 .sum::<usize>();
1470 src_table_reqs.insert(table_id, Some((peers, reqs)));
1471 } else {
1472 src_table_reqs.insert(table_id, None);
1474 }
1475 }
1476 }
1477 }
1478
1479 let mut inserts: HashMap<Peer, RegionInsertRequests> = HashMap::new();
1480
1481 for (_table_id, (peers, reqs)) in src_table_reqs
1482 .into_iter()
1483 .filter_map(|(k, v)| v.map(|v| (k, v)))
1484 {
1485 if peers.len() == 1 {
1486 inserts
1488 .entry(peers[0].clone())
1489 .or_default()
1490 .requests
1491 .extend(reqs.requests);
1492 continue;
1493 } else {
1494 for flownode in peers {
1496 inserts
1497 .entry(flownode.clone())
1498 .or_default()
1499 .requests
1500 .extend(reqs.requests.clone());
1501 }
1502 }
1503 }
1504
1505 Ok(Self {
1506 requests: inserts,
1507 num_rows,
1508 })
1509 }
1510
1511 fn detach(self, node_manager: NodeManagerRef) -> Result<()> {
1512 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.add(self.num_rows as i64);
1513 for (peer, inserts) in self.requests {
1514 let node_manager = node_manager.clone();
1515 common_runtime::spawn_global(async move {
1516 let result = node_manager
1517 .flownode(&peer)
1518 .await
1519 .handle_inserts(inserts)
1520 .await
1521 .context(RequestInsertsSnafu);
1522
1523 match result {
1524 Ok(resp) => {
1525 let affected_rows = resp.affected_rows;
1526 crate::metrics::DIST_MIRROR_ROW_COUNT.inc_by(affected_rows);
1527 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.sub(affected_rows as _);
1528 }
1529 Err(err) => {
1530 error!(err; "Failed to insert data into flownode {}", peer);
1531 }
1532 }
1533 });
1534 }
1535
1536 Ok(())
1537 }
1538}
1539
1540#[cfg(test)]
1541mod tests {
1542 use std::sync::Arc;
1543
1544 use api::helper::ColumnDataTypeWrapper;
1545 use api::v1::helper::{field_column_schema, time_index_column_schema};
1546 use api::v1::{RowInsertRequest, Rows, Value};
1547 use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
1548 use common_meta::cache::new_table_flownode_set_cache;
1549 use common_meta::ddl::test_util::datanode_handler::NaiveDatanodeHandler;
1550 use common_meta::test_util::MockDatanodeManager;
1551 use common_query::native_histogram::NATIVE_HISTOGRAM_FIELD;
1552 use common_query::prelude::{greptime_native_histogram, set_default_prefix};
1553 use datatypes::data_type::ConcreteDataType;
1554 use datatypes::schema::ColumnSchema;
1555 use moka::future::Cache;
1556 use session::context::QueryContext;
1557 use table::TableRef;
1558 use table::dist_table::DummyDataSource;
1559 use table::metadata::{TableInfoBuilder, TableMetaBuilder, TableType};
1560
1561 use super::*;
1562 use crate::tests::{create_partition_rule_manager, prepare_mocked_backend};
1563
1564 fn make_table_ref_with_schema(
1565 ts_name: &str,
1566 field_name: &str,
1567 field_type: ConcreteDataType,
1568 ) -> TableRef {
1569 let schema = datatypes::schema::SchemaBuilder::try_from_columns(vec![
1570 ColumnSchema::new(
1571 ts_name,
1572 ConcreteDataType::timestamp_millisecond_datatype(),
1573 false,
1574 )
1575 .with_time_index(true),
1576 ColumnSchema::new(field_name, field_type, true),
1577 ])
1578 .unwrap()
1579 .build()
1580 .unwrap();
1581 let meta = TableMetaBuilder::empty()
1582 .schema(Arc::new(schema))
1583 .primary_key_indices(vec![])
1584 .value_indices(vec![1])
1585 .engine("mito")
1586 .next_column_id(0)
1587 .options(Default::default())
1588 .created_on(Default::default())
1589 .build()
1590 .unwrap();
1591 let info = Arc::new(
1592 TableInfoBuilder::default()
1593 .table_id(1)
1594 .table_version(0)
1595 .name("test_table")
1596 .schema_name(DEFAULT_SCHEMA_NAME)
1597 .catalog_name(DEFAULT_CATALOG_NAME)
1598 .desc(None)
1599 .table_type(TableType::Base)
1600 .meta(meta)
1601 .build()
1602 .unwrap(),
1603 );
1604 Arc::new(table::Table::new(
1605 info,
1606 table::metadata::FilterPushDownType::Unsupported,
1607 Arc::new(DummyDataSource),
1608 ))
1609 }
1610
1611 #[tokio::test]
1612 async fn test_accommodate_existing_schema_and_reject_kind_changes() {
1613 let ts_name = "my_ts";
1614 let field_name = "my_field";
1615 let table =
1616 make_table_ref_with_schema(ts_name, field_name, ConcreteDataType::float64_datatype());
1617
1618 let mut req = RowInsertRequest {
1620 table_name: "test_table".to_string(),
1621 rows: Some(Rows {
1622 schema: vec![
1623 time_index_column_schema("ts_wrong", ColumnDataType::TimestampMillisecond),
1624 field_column_schema("field_wrong", ColumnDataType::Float64),
1625 ],
1626 rows: vec![api::v1::Row {
1627 values: vec![Value::default(), Value::default()],
1628 }],
1629 }),
1630 };
1631 let ctx = Arc::new(QueryContext::with(
1632 DEFAULT_CATALOG_NAME,
1633 DEFAULT_SCHEMA_NAME,
1634 ));
1635
1636 let kv_backend = prepare_mocked_backend().await;
1637 let inserter = Inserter::new(
1638 catalog::memory::MemoryCatalogManager::new(),
1639 create_partition_rule_manager(kv_backend.clone()).await,
1640 Arc::new(MockDatanodeManager::new(NaiveDatanodeHandler)),
1641 Arc::new(new_table_flownode_set_cache(
1642 String::new(),
1643 Cache::new(100),
1644 kv_backend.clone(),
1645 )),
1646 true,
1647 );
1648 assert!(
1650 inserter
1651 .get_alter_table_expr_on_demand(&mut req, &table, &ctx, false, false, false)
1652 .unwrap()
1653 .is_none()
1654 );
1655 let alter_expr = inserter
1656 .get_alter_table_expr_on_demand(&mut req, &table, &ctx, true, true, true)
1657 .unwrap();
1658 assert!(alter_expr.is_none());
1659
1660 let req_schema = req.rows.as_ref().unwrap().schema.clone();
1662 assert_eq!(req_schema[0].column_name, ts_name);
1663 assert_eq!(req_schema[1].column_name, field_name);
1664
1665 let (datatype, datatype_extension) =
1666 ColumnDataTypeWrapper::try_from(native_histogram_value_type().clone())
1667 .unwrap()
1668 .into_parts();
1669 let mut histogram_req = RowInsertRequest {
1670 table_name: "test_table".to_string(),
1671 rows: Some(Rows {
1672 schema: vec![
1673 time_index_column_schema("ts", ColumnDataType::TimestampMillisecond),
1674 api::v1::ColumnSchema {
1675 column_name: greptime_native_histogram().to_string(),
1676 datatype: datatype as i32,
1677 semantic_type: SemanticType::Field as i32,
1678 datatype_extension,
1679 options: None,
1680 },
1681 ],
1682 rows: vec![],
1683 }),
1684 };
1685 let error = inserter
1686 .get_alter_table_expr_on_demand(&mut histogram_req, &table, &ctx, false, true, true)
1687 .unwrap_err();
1688 assert!(
1689 error
1690 .to_string()
1691 .contains("cannot mix native histogram and float sample fields")
1692 );
1693
1694 let histogram_table = make_table_ref_with_schema(
1695 "ts",
1696 greptime_native_histogram(),
1697 native_histogram_value_type().clone(),
1698 );
1699 let mut sample_req = RowInsertRequest {
1700 table_name: "test_table".to_string(),
1701 rows: Some(Rows {
1702 schema: vec![
1703 time_index_column_schema("ts", ColumnDataType::TimestampMillisecond),
1704 field_column_schema(greptime_value(), ColumnDataType::Float64),
1705 ],
1706 rows: vec![],
1707 }),
1708 };
1709 let error = inserter
1710 .get_alter_table_expr_on_demand(
1711 &mut sample_req,
1712 &histogram_table,
1713 &ctx,
1714 false,
1715 true,
1716 true,
1717 )
1718 .unwrap_err();
1719 assert!(
1720 error
1721 .to_string()
1722 .contains("cannot mix native histogram and float sample fields")
1723 );
1724 }
1725
1726 #[test]
1727 fn test_native_histogram_detection_survives_prefix_change() {
1728 set_default_prefix(Some("custom")).unwrap();
1729 let table = make_table_ref_with_schema(
1730 "custom_timestamp",
1731 NATIVE_HISTOGRAM_FIELD,
1732 native_histogram_value_type().clone(),
1733 );
1734 let (datatype, datatype_extension) =
1735 ColumnDataTypeWrapper::try_from(native_histogram_value_type().clone())
1736 .unwrap()
1737 .into_parts();
1738 let request_schema = [api::v1::ColumnSchema {
1739 column_name: greptime_native_histogram().to_string(),
1740 datatype: datatype as i32,
1741 semantic_type: SemanticType::Field as i32,
1742 datatype_extension,
1743 options: None,
1744 }];
1745
1746 assert!(request_is_native_histogram(&request_schema));
1747 assert!(table_is_native_histogram(&table));
1748 }
1749
1750 #[test]
1751 fn test_skip_wal_does_not_change_table_options() {
1752 check_skip_wal_does_not_change_table_options(false);
1753 check_skip_wal_does_not_change_table_options(true);
1754 }
1755
1756 fn check_skip_wal_does_not_change_table_options(skip_wal: bool) {
1757 let ctx = Arc::new(QueryContext::with(
1758 DEFAULT_CATALOG_NAME,
1759 DEFAULT_SCHEMA_NAME,
1760 ));
1761 ctx.set_skip_wal(skip_wal);
1762 let mut options = Default::default();
1763 fill_table_options_for_create(&mut options, &AutoCreateTableType::Physical, &ctx);
1764 assert!(!options.contains_key(session::hints::INSERT_SKIP_WAL_HINT));
1765 assert!(!options.contains_key("skip_wal"));
1766 }
1767
1768 #[test]
1769 fn test_last_non_null_create_options_preserve_default_without_append_mode() {
1770 let ctx = Arc::new(QueryContext::with(
1771 DEFAULT_CATALOG_NAME,
1772 DEFAULT_SCHEMA_NAME,
1773 ));
1774 let mut table_options = Default::default();
1775
1776 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
1777
1778 assert_eq!(
1779 Some("last_non_null"),
1780 table_options.get(MERGE_MODE_KEY).map(String::as_str)
1781 );
1782 assert!(!table_options.contains_key(APPEND_MODE_KEY));
1783 }
1784
1785 #[test]
1786 fn test_fill_table_options_copies_semantic_extensions() {
1787 use table::requests::{
1788 SEMANTIC_METRIC_TYPE, SEMANTIC_PER_TABLE_INDEX_KEY, SEMANTIC_SIGNAL_TYPE,
1789 SEMANTIC_SOURCE, SEMANTIC_SOURCE_VERSION, SIGNAL_TYPE_METRIC, SOURCE_OPENTELEMETRY,
1790 };
1791
1792 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
1793 ctx.set_extension(SEMANTIC_SIGNAL_TYPE, SIGNAL_TYPE_METRIC);
1794 ctx.set_extension(SEMANTIC_SOURCE, SOURCE_OPENTELEMETRY);
1795 ctx.set_extension(SEMANTIC_SOURCE_VERSION, "2.0");
1796 ctx.set_extension(SEMANTIC_METRIC_TYPE, "bogus");
1797 ctx.set_extension(SEMANTIC_PER_TABLE_INDEX_KEY, "{}");
1799 let ctx = Arc::new(ctx);
1800 let mut table_options = Default::default();
1801
1802 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::Physical, &ctx);
1803
1804 assert_eq!(
1805 Some(SIGNAL_TYPE_METRIC),
1806 table_options.get(SEMANTIC_SIGNAL_TYPE).map(String::as_str)
1807 );
1808 assert_eq!(
1809 Some(SOURCE_OPENTELEMETRY),
1810 table_options.get(SEMANTIC_SOURCE).map(String::as_str)
1811 );
1812 assert_eq!(
1813 Some("2.0"),
1814 table_options
1815 .get(SEMANTIC_SOURCE_VERSION)
1816 .map(String::as_str)
1817 );
1818 assert!(!table_options.contains_key(SEMANTIC_METRIC_TYPE));
1819 assert!(!table_options.contains_key(SEMANTIC_PER_TABLE_INDEX_KEY));
1820 }
1821
1822 #[test]
1823 fn test_apply_per_table_semantic_options() {
1824 use table::requests::{
1825 SEMANTIC_METRIC_TYPE, SEMANTIC_METRIC_UNIT, SEMANTIC_PER_TABLE_INDEX_KEY,
1826 };
1827
1828 let index = format!(
1829 r#"{{
1830 "{DEFAULT_SCHEMA_NAME}": {{
1831 "http_requests_total": {{
1832 "greptime.semantic.metric.type": "counter",
1833 "greptime.semantic.metric.unit": "By",
1834 "greptime.semantic.metric.type_BOGUS": "x"
1835 }},
1836 "other_table": {{
1837 "greptime.semantic.metric.type": "gauge"
1838 }}
1839 }},
1840 "other_schema": {{
1841 "http_requests_total": {{
1842 "greptime.semantic.metric.type": "gauge"
1843 }}
1844 }}
1845 }}"#
1846 );
1847 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
1848 ctx.set_extension(SEMANTIC_PER_TABLE_INDEX_KEY, index);
1849 let ctx = Arc::new(ctx);
1850
1851 let index = parse_per_table_semantic_index(&ctx);
1852 assert!(index.is_some());
1853 let index = index.as_ref();
1854
1855 let mut table_options = std::collections::HashMap::new();
1856 apply_per_table_semantic_options(
1857 &mut table_options,
1858 index,
1859 DEFAULT_SCHEMA_NAME,
1860 "http_requests_total",
1861 );
1862 assert_eq!(
1864 table_options.get(SEMANTIC_METRIC_TYPE).map(String::as_str),
1865 Some("counter")
1866 );
1867 assert_eq!(
1868 table_options.get(SEMANTIC_METRIC_UNIT).map(String::as_str),
1869 Some("By")
1870 );
1871 assert!(!table_options.contains_key("greptime.semantic.metric.type_BOGUS"));
1874 assert_eq!(table_options.len(), 2);
1875
1876 let mut empty = std::collections::HashMap::new();
1877 apply_per_table_semantic_options(&mut empty, index, DEFAULT_SCHEMA_NAME, "not_in_index");
1878 assert!(empty.is_empty());
1879
1880 let mut opts = std::collections::HashMap::new();
1883 apply_per_table_semantic_options(
1884 &mut opts,
1885 index,
1886 "schema_without_entry",
1887 "http_requests_total",
1888 );
1889 assert!(opts.is_empty());
1890
1891 let bare = Arc::new(QueryContext::with(
1893 DEFAULT_CATALOG_NAME,
1894 DEFAULT_SCHEMA_NAME,
1895 ));
1896 assert!(parse_per_table_semantic_index(&bare).is_none());
1897 let mut opts = std::collections::HashMap::new();
1898 apply_per_table_semantic_options(
1899 &mut opts,
1900 None,
1901 DEFAULT_SCHEMA_NAME,
1902 "http_requests_total",
1903 );
1904 assert!(opts.is_empty());
1905 }
1906
1907 #[test]
1908 fn test_last_non_null_create_options_preserve_default_with_append_mode_false() {
1909 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
1910 ctx.set_extension(APPEND_MODE_KEY, "false");
1911 let ctx = Arc::new(ctx);
1912 let mut table_options = Default::default();
1913
1914 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
1915
1916 assert!(!table_options.contains_key(APPEND_MODE_KEY));
1917 assert_eq!(
1918 Some("last_non_null"),
1919 table_options.get(MERGE_MODE_KEY).map(String::as_str)
1920 );
1921 }
1922
1923 #[test]
1924 fn test_last_non_null_create_options_use_configured_merge_mode() {
1925 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
1926 ctx.set_extension(MERGE_MODE_KEY, "last_row");
1927 let ctx = Arc::new(ctx);
1928 let mut table_options = Default::default();
1929
1930 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
1931
1932 assert_eq!(
1933 Some("last_row"),
1934 table_options.get(MERGE_MODE_KEY).map(String::as_str)
1935 );
1936 assert!(!table_options.contains_key(APPEND_MODE_KEY));
1937 }
1938
1939 #[test]
1940 fn test_last_non_null_create_options_use_last_row_with_append_mode_true() {
1941 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
1942 ctx.set_extension(APPEND_MODE_KEY, "true");
1943 let ctx = Arc::new(ctx);
1944 let mut table_options = Default::default();
1945
1946 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
1947
1948 assert_eq!(
1949 Some("true"),
1950 table_options.get(APPEND_MODE_KEY).map(String::as_str)
1951 );
1952 assert_eq!(
1953 Some("last_row"),
1954 table_options.get(MERGE_MODE_KEY).map(String::as_str)
1955 );
1956 }
1957}