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