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