1use std::collections::BTreeMap;
16use std::sync::Arc;
17
18use ahash::{HashMap, HashMapExt, HashSet, HashSetExt};
19use api::v1::alter_table_expr::Kind;
20use api::v1::column_def::options_from_skipping;
21use api::v1::region::{
22 InsertRequest as RegionInsertRequest, InsertRequests as RegionInsertRequests,
23 RegionRequestHeader,
24};
25use api::v1::{
26 AlterTableExpr, ColumnDataType, ColumnSchema, CreateTableExpr, InsertRequests,
27 RowInsertRequest, RowInsertRequests, Rows, SemanticType,
28};
29use catalog::CatalogManagerRef;
30use client::{OutputData, OutputMeta};
31use common_catalog::consts::{
32 DEFAULT_PRIVATE_SCHEMA_NAME, PARENT_SPAN_ID_COLUMN, SERVICE_NAME_COLUMN, TRACE_ID_COLUMN,
33 TRACE_TABLE_NAME, TRACE_TABLE_NAME_SESSION_KEY, default_engine, is_ddl_reserved_table,
34 trace_operations_table_name, trace_services_table_name,
35};
36use common_event_recorder::DEFAULT_EVENTS_TABLE_NAME;
37use common_frontend::slow_query_event::SLOW_QUERY_TABLE_NAME;
38use common_grpc_expr::util::ColumnExpr;
39use common_meta::cache::TableFlownodeSetCacheRef;
40use common_meta::datanode::REGION_STATS_HISTORY_TABLE_NAME;
41use common_meta::node_manager::{AffectedRows, NodeManagerRef};
42use common_meta::peer::Peer;
43use common_meta::rpc::ddl::TriggerReason;
44use common_query::Output;
45use common_query::native_histogram::{is_native_histogram_value_type, native_histogram_value_type};
46use common_query::prelude::{greptime_timestamp, greptime_value};
47use common_telemetry::tracing_context::TracingContext;
48use common_telemetry::{debug, error, warn};
49use datatypes::schema::SkippingIndexOptions;
50use futures_util::future;
51use meter_core::data::MeterRecord;
52use meter_macros::write_meter;
53use partition::manager::PartitionRuleManagerRef;
54use session::context::QueryContextRef;
55use snafu::ResultExt;
56use snafu::prelude::*;
57use sql::partition::partition_rule_for_hexstring;
58use sql::statements::create::Partitions;
59use sql::statements::insert::Insert;
60use store_api::metric_engine_consts::{
61 LOGICAL_TABLE_METADATA_KEY, METRIC_ENGINE_NAME, PHYSICAL_TABLE_METADATA_KEY,
62};
63use store_api::mito_engine_options::{
64 APPEND_MODE_KEY, COMPACTION_TYPE, COMPACTION_TYPE_TWCS, MERGE_MODE_KEY, TTL_KEY,
65 TWCS_TIME_WINDOW,
66};
67use store_api::storage::{RegionId, TableId};
68use table::TableRef;
69use table::metadata::{TableInfo, TableInfoRef};
70use table::requests::{
71 AUTO_CREATE_TABLE_KEY, InsertRequest as TableInsertRequest, SEMANTIC_PER_TABLE_INDEX_KEY,
72 SEMANTIC_PIPELINE, TABLE_DATA_MODEL, TABLE_DATA_MODEL_TRACE_V1, TABLE_DATA_MODEL_TRACE_V2,
73 TRACE_TABLE_PARTITIONS_HINT_KEY, VALID_TABLE_OPTION_KEYS, is_semantic_option_key,
74 validate_semantic_option,
75};
76use table::table_reference::TableReference;
77
78use crate::batcher::PendingRowsBatcher;
79use crate::error::{
80 CatalogSnafu, ColumnOptionsSnafu, CreatePartitionRulesSnafu, FindRegionLeaderSnafu,
81 InvalidInsertRequestSnafu, JoinTaskSnafu, RequestInsertsSnafu, Result, TableNotFoundSnafu,
82 WriteRejectedSnafu,
83};
84use crate::expr_helper;
85use crate::region_req_factory::RegionRequestFactory;
86use crate::req_convert::common::preprocess_row_insert_requests;
87use crate::req_convert::insert::{
88 ColumnToRow, ImpureDefaultFiller, RowToRegion, StatementToRegion, TableToRegion,
89 fill_reqs_with_impure_default, rows_to_record_batch,
90};
91use crate::statement::StatementExecutor;
92
93pub struct Inserter {
94 catalog_manager: CatalogManagerRef,
95 pub(crate) partition_manager: PartitionRuleManagerRef,
96 pub(crate) node_manager: NodeManagerRef,
97 pub(crate) table_flownode_set_cache: TableFlownodeSetCacheRef,
98 auto_create_table: bool,
102 pending_rows_batcher: Option<Arc<dyn PendingRowsBatcher>>,
103}
104
105pub type InserterRef = Arc<Inserter>;
106
107#[derive(Clone)]
109pub enum AutoCreateTableType {
110 Logical(String),
112 Physical,
114 Log,
116 LastNonNull,
118 Trace { alter_existing: bool },
120}
121
122impl AutoCreateTableType {
123 pub fn as_str(&self) -> &'static str {
124 match self {
125 AutoCreateTableType::Logical(_) => "logical",
126 AutoCreateTableType::Physical => "physical",
127 AutoCreateTableType::Log => "log",
128 AutoCreateTableType::LastNonNull => "last_non_null",
129 AutoCreateTableType::Trace { .. } => "trace",
130 }
131 }
132
133 fn alter_existing(&self) -> bool {
134 !matches!(
135 self,
136 Self::Trace {
137 alter_existing: false
138 }
139 )
140 }
141}
142
143#[derive(Clone)]
150pub struct InstantAndNormalInsertRequests {
151 pub normal_requests: RegionInsertRequests,
153 pub instant_requests: RegionInsertRequests,
156}
157
158impl Inserter {
159 pub async fn can_batch_metric_rows(
163 &self,
164 requests: &RowInsertRequests,
165 ctx: &QueryContextRef,
166 physical_table: &str,
167 ) -> Result<bool> {
168 if self.auto_create_disabled_reason(ctx)?.is_some() || ctx.extension(TTL_KEY).is_some() {
169 return Ok(false);
170 }
171 for request in &requests.inserts {
172 if request.rows.as_ref().is_some_and(|rows| {
175 rows.schema.iter().any(|column| {
176 column.datatype_extension.is_some()
177 || !matches!(
178 ColumnDataType::try_from(column.datatype),
179 Ok(ColumnDataType::TimestampMillisecond
180 | ColumnDataType::Float64
181 | ColumnDataType::String)
182 )
183 })
184 }) {
185 return Ok(false);
186 }
187
188 let Some(table) = self
189 .get_table(
190 ctx.current_catalog(),
191 &ctx.current_schema(),
192 &request.table_name,
193 )
194 .await?
195 else {
196 continue;
197 };
198 let info = table.table_info();
199 if info.meta.engine != METRIC_ENGINE_NAME
200 || info.is_ttl_instant_table()
201 || info
202 .meta
203 .options
204 .extra_options
205 .get(LOGICAL_TABLE_METADATA_KEY)
206 .map(String::as_str)
207 != Some(physical_table)
208 || info
209 .meta
210 .schema
211 .column_schemas()
212 .iter()
213 .any(|column| column.default_constraint().is_some())
214 {
215 return Ok(false);
216 }
217 if info
220 .meta
221 .primary_key_indices
222 .iter()
223 .any(|&index| !info.meta.schema.column_schemas()[index].is_nullable())
224 {
225 return Ok(false);
226 }
227 match self.table_flownode_set_cache.get(info.table_id()).await {
230 Ok(None) => {}
231 Ok(Some(flows)) if flows.is_empty() => {}
232 _ => return Ok(false),
233 }
234 }
235 Ok(true)
236 }
237
238 pub async fn meter_row_inserts(
241 requests: &mut RowInsertRequests,
242 ctx: &QueryContextRef,
243 ) -> Result<u64> {
244 let metered = InstantAndNormalInsertRequests {
245 normal_requests: RegionInsertRequests {
246 requests: requests
247 .inserts
248 .iter_mut()
249 .map(|request| RegionInsertRequest {
250 rows: request.rows.take(),
251 ..Default::default()
252 })
253 .collect(),
254 },
255 instant_requests: RegionInsertRequests::default(),
256 };
257 let cost = write_meter!(
258 ctx.current_catalog(),
259 ctx.current_schema(),
260 metered,
261 ctx.write_rows_to_admit(
262 ctx.current_catalog(),
263 &ctx.current_schema(),
264 count_insert_rows(&metered)?
265 ),
266 ctx.channel() as u8
267 )
268 .await
269 .context(WriteRejectedSnafu);
270 for (request, region) in requests
271 .inserts
272 .iter_mut()
273 .zip(metered.normal_requests.requests)
274 {
275 request.rows = region.rows;
276 }
277 cost
278 }
279
280 pub fn new(
281 catalog_manager: CatalogManagerRef,
282 partition_manager: PartitionRuleManagerRef,
283 node_manager: NodeManagerRef,
284 table_flownode_set_cache: TableFlownodeSetCacheRef,
285 auto_create_table: bool,
286 ) -> Self {
287 Self {
288 catalog_manager,
289 partition_manager,
290 node_manager,
291 table_flownode_set_cache,
292 auto_create_table,
293 pending_rows_batcher: None,
294 }
295 }
296
297 pub fn with_pending_rows_batcher(
299 mut self,
300 batcher: Option<Arc<dyn PendingRowsBatcher>>,
301 ) -> Self {
302 self.pending_rows_batcher = batcher;
303 self
304 }
305
306 pub async fn handle_column_inserts(
307 &self,
308 requests: InsertRequests,
309 ctx: QueryContextRef,
310 statement_executor: &StatementExecutor,
311 ) -> Result<Output> {
312 let row_inserts = ColumnToRow::convert(requests)?;
313 self.handle_row_inserts(row_inserts, ctx, statement_executor, false, false)
314 .await
315 }
316
317 pub async fn handle_row_inserts(
319 &self,
320 mut requests: RowInsertRequests,
321 ctx: QueryContextRef,
322 statement_executor: &StatementExecutor,
323 accommodate_existing_schema: bool,
324 is_single_value: bool,
325 ) -> Result<Output> {
326 preprocess_row_insert_requests(&mut requests.inserts)?;
327 self.handle_row_inserts_with_create_type(
328 requests,
329 ctx,
330 statement_executor,
331 AutoCreateTableType::Physical,
332 accommodate_existing_schema,
333 is_single_value,
334 )
335 .await
336 }
337
338 pub async fn handle_log_inserts(
340 &self,
341 requests: RowInsertRequests,
342 ctx: QueryContextRef,
343 statement_executor: &StatementExecutor,
344 ) -> Result<Output> {
345 self.handle_row_inserts_with_create_type(
346 requests,
347 ctx,
348 statement_executor,
349 AutoCreateTableType::Log,
350 false,
351 false,
352 )
353 .await
354 }
355
356 pub async fn handle_trace_inserts(
357 &self,
358 requests: RowInsertRequests,
359 ctx: QueryContextRef,
360 statement_executor: &StatementExecutor,
361 ) -> Result<Output> {
362 self.handle_row_inserts_with_create_type(
363 requests,
364 ctx,
365 statement_executor,
366 AutoCreateTableType::Trace {
367 alter_existing: true,
368 },
369 false,
370 false,
371 )
372 .await
373 }
374
375 pub async fn handle_last_non_null_inserts(
377 &self,
378 requests: RowInsertRequests,
379 ctx: QueryContextRef,
380 statement_executor: &StatementExecutor,
381 accommodate_existing_schema: bool,
382 is_single_value: bool,
383 ) -> Result<Output> {
384 self.handle_row_inserts_with_create_type(
385 requests,
386 ctx,
387 statement_executor,
388 AutoCreateTableType::LastNonNull,
389 accommodate_existing_schema,
390 is_single_value,
391 )
392 .await
393 }
394
395 async fn handle_row_inserts_with_create_type(
397 &self,
398 mut requests: RowInsertRequests,
399 ctx: QueryContextRef,
400 statement_executor: &StatementExecutor,
401 create_type: AutoCreateTableType,
402 accommodate_existing_schema: bool,
403 is_single_value: bool,
404 ) -> Result<Output> {
405 let skip_wal = ctx.skip_wal();
406
407 let batcher = self
408 .pending_rows_batcher
409 .as_ref()
410 .filter(|_| ctx.batching_enabled());
411
412 requests.inserts.retain(|req| {
414 req.rows
415 .as_ref()
416 .map(|r| !r.rows.is_empty())
417 .unwrap_or_default()
418 });
419 validate_column_count_match(&requests)?;
420
421 let CreateAlterTableResult {
422 instant_table_ids,
423 table_infos,
424 } = self
425 .create_or_alter_tables_on_demand(
426 &mut requests,
427 &ctx,
428 create_type,
429 statement_executor,
430 accommodate_existing_schema,
431 is_single_value,
432 )
433 .await?;
434
435 if let Some(batcher) = batcher
438 && instant_table_ids.is_empty()
439 && table_infos
440 .values()
441 .all(|info| info.meta.engine == default_engine())
442 {
443 return self
444 .submit_pending_rows(requests, table_infos, ctx, batcher)
445 .await;
446 }
447
448 let name_to_info = table_infos
449 .values()
450 .map(|info| (info.name.clone(), info.clone()))
451 .collect::<HashMap<_, _>>();
452 let inserts = RowToRegion::new(
453 name_to_info,
454 instant_table_ids,
455 self.partition_manager.as_ref(),
456 )
457 .convert(requests, skip_wal)
458 .await?;
459
460 self.do_request(inserts, &table_infos, &ctx).await
461 }
462
463 async fn submit_pending_rows(
464 &self,
465 mut requests: RowInsertRequests,
466 table_infos: HashMap<TableId, Arc<TableInfo>>,
467 ctx: QueryContextRef,
468 batcher: &Arc<dyn PendingRowsBatcher>,
469 ) -> Result<Output> {
470 requests.inserts.retain(|request| {
473 request
474 .rows
475 .as_ref()
476 .is_some_and(|rows| !rows.rows.is_empty())
477 });
478 let by_name = table_infos
479 .values()
480 .map(|info| (info.name.as_str(), info))
481 .collect::<HashMap<_, _>>();
482 let mut prepared = Vec::with_capacity(requests.inserts.len());
483 for request in &mut requests.inserts {
484 let table_info =
485 by_name
486 .get(request.table_name.as_str())
487 .context(TableNotFoundSnafu {
488 table_name: &request.table_name,
489 })?;
490 let Some(rows) = &mut request.rows else {
491 continue;
492 };
493 ImpureDefaultFiller::new((*table_info).clone())?.fill_rows(rows);
494 let batch = rows_to_record_batch(rows, table_info)?;
495 prepared.push(((*table_info).clone(), batch));
496 }
497 let metered = InstantAndNormalInsertRequests {
500 normal_requests: RegionInsertRequests {
501 requests: requests
502 .inserts
503 .into_iter()
504 .map(|request| RegionInsertRequest {
505 rows: request.rows,
506 ..Default::default()
507 })
508 .collect(),
509 },
510 instant_requests: RegionInsertRequests::default(),
511 };
512 let table_info = table_infos.values().next();
513 let catalog = table_info.map_or(ctx.current_catalog(), |info| info.catalog_name.as_str());
514 let schema =
515 table_info.map_or_else(|| ctx.current_schema(), |info| info.schema_name.clone());
516 let write_cost = write_meter!(
517 catalog,
518 &schema,
519 metered,
520 ctx.write_rows_to_admit(catalog, &schema, count_insert_rows(&metered)?),
521 ctx.channel() as u8
522 )
523 .await
524 .context(WriteRejectedSnafu)?;
525 prepared.retain(|(_, batch)| batch.num_rows() != 0);
526 let results = if prepared.is_empty() {
527 Vec::new()
528 } else {
529 let permit = batcher.acquire().await?;
531 let submissions = prepared.into_iter().map(|(info, batch)| {
532 let mut target_ctx = ctx.fork();
534 target_ctx.set_current_catalog(&info.catalog_name);
535 target_ctx.set_current_schema(&info.schema_name);
536 batcher.submit(info, batch, Arc::new(target_ctx), permit.clone())
537 });
538 future::join_all(submissions).await
540 };
541 let affected_rows = results.into_iter().sum::<Result<usize>>()?;
542 Ok(Output::new(
543 OutputData::AffectedRows(affected_rows),
544 OutputMeta::new_with_cost(write_cost as _),
545 ))
546 }
547
548 pub async fn handle_metric_row_inserts(
550 &self,
551 mut requests: RowInsertRequests,
552 ctx: QueryContextRef,
553 statement_executor: &StatementExecutor,
554 physical_table: String,
555 ) -> Result<Output> {
556 let skip_wal = ctx.skip_wal();
557
558 requests.inserts.retain(|req| {
560 req.rows
561 .as_ref()
562 .map(|r| !r.rows.is_empty())
563 .unwrap_or_default()
564 });
565 validate_column_count_match(&requests)?;
566
567 self.create_physical_table_on_demand(&ctx, physical_table.clone(), statement_executor)
569 .await?;
570
571 let CreateAlterTableResult {
573 instant_table_ids,
574 table_infos,
575 } = self
576 .create_or_alter_tables_on_demand(
577 &mut requests,
578 &ctx,
579 AutoCreateTableType::Logical(physical_table.clone()),
580 statement_executor,
581 true,
582 true,
583 )
584 .await?;
585 let name_to_info = table_infos
586 .values()
587 .map(|info| (info.name.clone(), info.clone()))
588 .collect::<HashMap<_, _>>();
589 let inserts = RowToRegion::new(name_to_info, instant_table_ids, &self.partition_manager)
590 .convert(requests, skip_wal)
591 .await?;
592
593 self.do_request(inserts, &table_infos, &ctx).await
594 }
595
596 fn table_batcher(
597 &self,
598 table_info: &TableInfoRef,
599 ctx: &QueryContextRef,
600 ) -> Option<&Arc<dyn PendingRowsBatcher>> {
601 self.pending_rows_batcher.as_ref().filter(|_| {
602 ctx.batching_enabled()
603 && !table_info.is_ttl_instant_table()
604 && table_info.meta.engine == default_engine()
605 })
606 }
607
608 async fn submit_table_rows(
609 &self,
610 rows: Rows,
611 table_info: TableInfoRef,
612 ctx: QueryContextRef,
613 batcher: &Arc<dyn PendingRowsBatcher>,
614 ) -> Result<Output> {
615 let requests = RowInsertRequests {
616 inserts: vec![RowInsertRequest {
617 table_name: table_info.name.clone(),
618 rows: Some(rows),
619 }],
620 };
621 let table_infos = HashMap::from_iter([(table_info.table_id(), table_info)]);
622 self.submit_pending_rows(requests, table_infos, ctx, batcher)
623 .await
624 }
625
626 pub async fn handle_table_insert(
627 &self,
628 request: TableInsertRequest,
629 ctx: QueryContextRef,
630 ) -> Result<Output> {
631 let catalog = request.catalog_name.as_str();
632 let schema = request.schema_name.as_str();
633 let table_name = request.table_name.as_str();
634 let table = self.get_table(catalog, schema, table_name).await?;
635 let table = table.with_context(|| TableNotFoundSnafu {
636 table_name: common_catalog::format_full_table_name(catalog, schema, table_name),
637 })?;
638 let table_info = table.table_info();
639
640 let converter = TableToRegion::new(&table_info, &self.partition_manager);
641 let skip_wal = request.skip_wal;
642 let rows = converter.prepare(request)?;
643 if let Some(batcher) = self.table_batcher(&table_info, &ctx) {
644 return self.submit_table_rows(rows, table_info, ctx, batcher).await;
645 }
646 let inserts = converter.partition(rows, skip_wal).await?;
647
648 let table_infos = HashMap::from_iter([(table_info.table_id(), table_info.clone())]);
649
650 self.do_request(inserts, &table_infos, &ctx).await
651 }
652
653 pub async fn handle_statement_insert(
654 &self,
655 insert: &Insert,
656 ctx: &QueryContextRef,
657 ) -> Result<Output> {
658 let converter =
659 StatementToRegion::new(self.catalog_manager.as_ref(), &self.partition_manager, ctx);
660 let (rows, table_info) = converter.prepare(insert, ctx).await?;
661 if let Some(batcher) = self.table_batcher(&table_info, ctx) {
662 return self
663 .submit_table_rows(rows, table_info, ctx.clone(), batcher)
664 .await;
665 }
666 let inserts = converter.partition(rows, table_info.clone(), ctx).await?;
667
668 let table_infos = HashMap::from_iter([(table_info.table_id(), table_info.clone())]);
669
670 self.do_request(inserts, &table_infos, ctx).await
671 }
672}
673
674pub async fn admit_write(rows: u64, ctx: &QueryContextRef) -> Result<QueryContextRef> {
677 write_meter!(MeterRecord::new(
680 ctx.current_catalog().to_string(),
681 ctx.current_schema(),
682 0,
683 ctx.write_rows_to_admit(ctx.current_catalog(), &ctx.current_schema(), rows),
684 ctx.channel() as u8,
685 ))
686 .await
687 .context(WriteRejectedSnafu)?;
688 Ok(Arc::new(ctx.with_write_admission()))
689}
690
691pub async fn admit_row_insert_batches(
694 batches: &mut [(QueryContextRef, RowInsertRequests)],
695) -> Result<()> {
696 let mut totals = BTreeMap::<_, (QueryContextRef, u64)>::new();
697 for (ctx, requests) in batches.iter() {
698 let catalog = ctx.current_catalog();
699 let schema = ctx.current_schema();
700 if ctx.write_rows_to_admit(catalog, &schema, 1) == 0 {
701 continue;
702 }
703 let (_, total) = totals
704 .entry((catalog.to_string(), schema.clone()))
705 .or_insert_with(|| (ctx.clone(), 0));
706 for rows in requests.inserts.iter().filter_map(|r| r.rows.as_ref()) {
707 *total =
708 total
709 .checked_add(rows.rows.len() as u64)
710 .context(InvalidInsertRequestSnafu {
711 reason: "Insert row count exceeds u64::MAX",
712 })?;
713 }
714 }
715 for (ctx, rows) in totals.values() {
716 admit_write(*rows, ctx).await?;
717 }
718 for (ctx, _) in batches {
719 *ctx = Arc::new(ctx.with_write_admission());
720 }
721 Ok(())
722}
723
724fn count_insert_rows(requests: &InstantAndNormalInsertRequests) -> Result<u64> {
725 requests
726 .normal_requests
727 .requests
728 .iter()
729 .chain(&requests.instant_requests.requests)
730 .filter_map(|request| request.rows.as_ref())
731 .try_fold(0u64, |total, rows| {
732 total
733 .checked_add(rows.rows.len() as u64)
734 .context(InvalidInsertRequestSnafu {
735 reason: "Insert row count exceeds u64::MAX",
736 })
737 })
738}
739
740impl Inserter {
741 async fn do_request(
742 &self,
743 requests: InstantAndNormalInsertRequests,
744 table_infos: &HashMap<TableId, Arc<TableInfo>>,
745 ctx: &QueryContextRef,
746 ) -> Result<Output> {
747 let requests = fill_reqs_with_impure_default(table_infos, requests)?;
749
750 let table_info = table_infos.values().next();
753 let catalog = table_info.map_or(ctx.current_catalog(), |info| info.catalog_name.as_str());
754 let schema =
755 table_info.map_or_else(|| ctx.current_schema(), |info| info.schema_name.clone());
756 let write_cost = write_meter!(
757 catalog,
758 schema.clone(),
759 requests,
760 ctx.write_rows_to_admit(catalog, &schema, count_insert_rows(&requests)?),
761 ctx.channel() as u8
762 )
763 .await
764 .context(WriteRejectedSnafu)?;
765 let request_factory = RegionRequestFactory::new(RegionRequestHeader {
766 tracing_context: TracingContext::from_current_span().to_w3c(),
767 dbname: ctx.get_db_string(),
768 ..Default::default()
769 });
770
771 let InstantAndNormalInsertRequests {
772 normal_requests,
773 instant_requests,
774 } = requests;
775
776 let flow_mirror_task = FlowMirrorTask::new(
778 &self.table_flownode_set_cache,
779 normal_requests
780 .requests
781 .iter()
782 .chain(instant_requests.requests.iter()),
783 )
784 .await?;
785 flow_mirror_task.detach(self.node_manager.clone())?;
786
787 let write_tasks = self
789 .group_requests_by_peer(normal_requests)
790 .await?
791 .into_iter()
792 .map(|(peer, inserts)| {
793 let node_manager = self.node_manager.clone();
794 let request = request_factory.build_insert(inserts);
795 common_runtime::spawn_global(async move {
796 node_manager
797 .datanode(&peer)
798 .await
799 .handle(request)
800 .await
801 .context(RequestInsertsSnafu)
802 })
803 });
804 let results = future::try_join_all(write_tasks)
805 .await
806 .context(JoinTaskSnafu)?;
807 let affected_rows = results
808 .into_iter()
809 .map(|resp| resp.map(|r| r.affected_rows))
810 .sum::<Result<AffectedRows>>()?;
811 crate::metrics::DIST_INGEST_ROW_COUNT
812 .with_label_values(&[ctx.get_db_string().as_str()])
813 .inc_by(affected_rows as u64);
814 Ok(Output::new(
815 OutputData::AffectedRows(affected_rows),
816 OutputMeta::new_with_cost(write_cost as _),
817 ))
818 }
819
820 async fn group_requests_by_peer(
821 &self,
822 requests: RegionInsertRequests,
823 ) -> Result<HashMap<Peer, RegionInsertRequests>> {
824 let mut requests_per_region: HashMap<RegionId, RegionInsertRequests> = HashMap::new();
827 for req in requests.requests {
828 let region_id = RegionId::from_u64(req.region_id);
829 requests_per_region
830 .entry(region_id)
831 .or_default()
832 .requests
833 .push(req);
834 }
835
836 let mut inserts: HashMap<Peer, RegionInsertRequests> = HashMap::new();
837
838 for (region_id, reqs) in requests_per_region {
839 let peer = self
840 .partition_manager
841 .find_region_leader(region_id)
842 .await
843 .context(FindRegionLeaderSnafu)?;
844 inserts
845 .entry(peer)
846 .or_default()
847 .requests
848 .extend(reqs.requests);
849 }
850
851 Ok(inserts)
852 }
853
854 fn auto_create_disabled_reason(&self, ctx: &QueryContextRef) -> Result<Option<&'static str>> {
858 let auto_create_table_hint = ctx
859 .extension(AUTO_CREATE_TABLE_KEY)
860 .map(|v| v.parse::<bool>())
861 .transpose()
862 .map_err(|_| {
863 InvalidInsertRequestSnafu {
864 reason: "`auto_create_table` hint must be a boolean",
865 }
866 .build()
867 })?
868 .unwrap_or(true);
869 Ok(if !self.auto_create_table {
870 Some("auto-create table is disabled by frontend config")
871 } else if !auto_create_table_hint {
872 Some("`auto_create_table` hint is disabled")
873 } else {
874 None
875 })
876 }
877
878 fn is_auto_create_exempt_private_table(schema: &str, table: &str) -> bool {
881 schema == DEFAULT_PRIVATE_SCHEMA_NAME
882 && matches!(
883 table,
884 DEFAULT_EVENTS_TABLE_NAME | SLOW_QUERY_TABLE_NAME | REGION_STATS_HISTORY_TABLE_NAME
885 )
886 }
887
888 pub async fn ensure_trace_table_on_demand(
893 &self,
894 table_name: &str,
895 request_schema: Vec<ColumnSchema>,
896 alter_existing: bool,
897 ctx: &QueryContextRef,
898 statement_executor: &StatementExecutor,
899 ) -> Result<()> {
900 let mut requests = RowInsertRequests {
901 inserts: vec![RowInsertRequest {
902 table_name: table_name.to_string(),
903 rows: Some(api::v1::Rows {
904 schema: request_schema,
905 rows: Vec::new(),
906 }),
907 }],
908 };
909 self.create_or_alter_tables_on_demand(
910 &mut requests,
911 ctx,
912 AutoCreateTableType::Trace { alter_existing },
913 statement_executor,
914 false,
915 false,
916 )
917 .await?;
918 Ok(())
919 }
920
921 async fn create_or_alter_tables_on_demand(
934 &self,
935 requests: &mut RowInsertRequests,
936 ctx: &QueryContextRef,
937 auto_create_table_type: AutoCreateTableType,
938 statement_executor: &StatementExecutor,
939 accommodate_existing_schema: bool,
940 is_single_value: bool,
941 ) -> Result<CreateAlterTableResult> {
942 let _timer = crate::metrics::CREATE_ALTER_ON_DEMAND
943 .with_label_values(&[auto_create_table_type.as_str()])
944 .start_timer();
945 let catalog = ctx.current_catalog();
946 let schema = ctx.current_schema();
947
948 let auto_create_disabled_reason = self.auto_create_disabled_reason(ctx)?;
949 let has_auto_create_exempt_table = auto_create_disabled_reason.is_some()
951 && requests
952 .inserts
953 .iter()
954 .any(|req| Self::is_auto_create_exempt_private_table(&schema, &req.table_name));
955 let mut table_infos = HashMap::new();
956 if let Some(disabled_reason) = auto_create_disabled_reason
958 && !has_auto_create_exempt_table
959 {
960 let mut instant_table_ids = HashSet::new();
961 for req in &requests.inserts {
962 let table = match self.get_table(catalog, &schema, &req.table_name).await? {
963 Some(table) => table,
964 None if is_ddl_reserved_table(&schema, &req.table_name) => {
967 statement_executor
968 .create_declared_relationships_table(catalog, ctx.clone())
969 .await?
970 }
971 None => {
972 return InvalidInsertRequestSnafu {
973 reason: format!(
974 "Table `{}` does not exist, and {}",
975 req.table_name, disabled_reason
976 ),
977 }
978 .fail();
979 }
980 };
981 let table_info = table.table_info();
982 if matches!(auto_create_table_type, AutoCreateTableType::Trace { .. }) {
983 validate_trace_table_model(&table_info, ctx)?;
984 }
985 if table_info.is_ttl_instant_table() {
986 instant_table_ids.insert(table_info.table_id());
987 }
988 table_infos.insert(table_info.table_id(), table.table_info());
989 }
990 let ret = CreateAlterTableResult {
991 instant_table_ids,
992 table_infos,
993 };
994 return Ok(ret);
995 }
996
997 let mut create_tables = vec![];
998 let mut alter_tables = vec![];
999 let mut need_refresh_table_infos = HashSet::new();
1000 let mut instant_table_ids = HashSet::new();
1001 let mut per_table_semantics: Option<Option<PerTableSemanticIndex>> = None;
1002
1003 for req in &mut requests.inserts {
1004 let auto_create_allowed = auto_create_disabled_reason.is_none()
1006 || Self::is_auto_create_exempt_private_table(&schema, &req.table_name);
1007 match self.get_table(catalog, &schema, &req.table_name).await? {
1008 Some(table) => {
1009 let table_info = table.table_info();
1010 if matches!(auto_create_table_type, AutoCreateTableType::Trace { .. }) {
1011 validate_trace_table_model(&table_info, ctx)?;
1012 }
1013 if table_info.is_ttl_instant_table() {
1014 instant_table_ids.insert(table_info.table_id());
1015 }
1016 if auto_create_allowed
1017 && let Some(alter_expr) = self.get_alter_table_expr_on_demand(
1018 req,
1019 &table,
1020 ctx,
1021 accommodate_existing_schema,
1022 is_single_value,
1023 auto_create_table_type.alter_existing(),
1024 )?
1025 {
1026 alter_tables.push(alter_expr);
1027 need_refresh_table_infos.insert((
1028 catalog.to_string(),
1029 schema.clone(),
1030 req.table_name.clone(),
1031 ));
1032 } else {
1033 table_infos.insert(table_info.table_id(), table.table_info());
1034 }
1035 }
1036 None if is_ddl_reserved_table(&schema, &req.table_name) => {
1040 let table = statement_executor
1041 .create_declared_relationships_table(catalog, ctx.clone())
1042 .await?;
1043 let table_info = table.table_info();
1044 if table_info.is_ttl_instant_table() {
1045 instant_table_ids.insert(table_info.table_id());
1046 }
1047 table_infos.insert(table_info.table_id(), table_info);
1048 }
1049 None if !auto_create_allowed
1050 && let Some(disabled_reason) = auto_create_disabled_reason =>
1051 {
1052 return InvalidInsertRequestSnafu {
1053 reason: format!(
1054 "Table `{}` does not exist, and {}",
1055 req.table_name, disabled_reason,
1056 ),
1057 }
1058 .fail();
1059 }
1060 None => {
1061 let semantic_index = per_table_semantics
1062 .get_or_insert_with(|| parse_per_table_semantic_index(ctx))
1063 .as_ref();
1064 let create_expr = self.get_create_table_expr_on_demand(
1065 req,
1066 &auto_create_table_type,
1067 ctx,
1068 semantic_index,
1069 )?;
1070 create_tables.push(create_expr);
1071 }
1072 }
1073 }
1074
1075 match auto_create_table_type {
1076 AutoCreateTableType::Logical(_) => {
1077 if !create_tables.is_empty() {
1078 let tables = self
1080 .create_logical_tables(create_tables, ctx, statement_executor)
1081 .await?;
1082
1083 for table in tables {
1084 let table_info = table.table_info();
1085 if table_info.is_ttl_instant_table() {
1086 instant_table_ids.insert(table_info.table_id());
1087 }
1088 table_infos.insert(table_info.table_id(), table.table_info());
1089 }
1090 }
1091 if !alter_tables.is_empty() {
1092 statement_executor
1094 .alter_logical_tables(alter_tables, ctx.clone(), TriggerReason::AutoAlter)
1095 .await?;
1096 }
1097 }
1098 AutoCreateTableType::Physical
1099 | AutoCreateTableType::Log
1100 | AutoCreateTableType::LastNonNull => {
1101 for create_table in create_tables {
1104 let table = self
1105 .create_physical_table(create_table, None, ctx, statement_executor)
1106 .await?;
1107 let table_info = table.table_info();
1108 if table_info.is_ttl_instant_table() {
1109 instant_table_ids.insert(table_info.table_id());
1110 }
1111 table_infos.insert(table_info.table_id(), table.table_info());
1112 }
1113 for alter_expr in alter_tables.into_iter() {
1114 statement_executor
1115 .alter_table_inner(alter_expr, ctx.clone(), TriggerReason::AutoAlter)
1116 .await?;
1117 }
1118 }
1119
1120 AutoCreateTableType::Trace { .. } => {
1121 let trace_table_name = ctx
1122 .extension(TRACE_TABLE_NAME_SESSION_KEY)
1123 .unwrap_or(TRACE_TABLE_NAME);
1124
1125 let trace_table_partitions = if let Some(trace_table_partitions) =
1126 ctx.extension(TRACE_TABLE_PARTITIONS_HINT_KEY)
1127 {
1128 let p = trace_table_partitions.parse::<u32>().map_err(|_| {
1129 InvalidInsertRequestSnafu {
1130 reason: format!(
1131 "Failed to parse trace_table_partitions: {}",
1132 trace_table_partitions
1133 ),
1134 }
1135 .build()
1136 })?;
1137 Some(p)
1138 } else {
1139 None
1140 };
1141
1142 for mut create_table in create_tables {
1145 if create_table.table_name == trace_services_table_name(trace_table_name)
1146 || create_table.table_name == trace_operations_table_name(trace_table_name)
1147 {
1148 create_table
1150 .table_options
1151 .insert(APPEND_MODE_KEY.to_string(), "false".to_string());
1152 create_table.table_options.remove(TTL_KEY);
1154
1155 let table = self
1156 .create_physical_table(create_table, None, ctx, statement_executor)
1157 .await?;
1158 let table_info = table.table_info();
1159 if table_info.is_ttl_instant_table() {
1160 instant_table_ids.insert(table_info.table_id());
1161 }
1162 table_infos.insert(table_info.table_id(), table.table_info());
1163 } else {
1164 let partitions = if matches!(trace_table_partitions, Some(0) | Some(1)) {
1167 None
1169 } else {
1170 let p = partition_rule_for_hexstring(
1171 TRACE_ID_COLUMN,
1172 trace_table_partitions,
1173 )
1174 .context(CreatePartitionRulesSnafu)?;
1175 Some(p)
1176 };
1177
1178 let index_columns =
1183 [TRACE_ID_COLUMN, PARENT_SPAN_ID_COLUMN, SERVICE_NAME_COLUMN];
1184 for index_column in index_columns {
1185 if let Some(col) = create_table
1186 .column_defs
1187 .iter_mut()
1188 .find(|c| c.name == index_column)
1189 {
1190 col.options =
1191 options_from_skipping(&SkippingIndexOptions::default())
1192 .context(ColumnOptionsSnafu)?;
1193 } else {
1194 warn!(
1195 "Column {} not found when creating index for trace table: {}.",
1196 index_column, create_table.table_name
1197 );
1198 }
1199 }
1200
1201 create_table.table_options.insert(
1203 TABLE_DATA_MODEL.to_string(),
1204 ctx.extension(SEMANTIC_PIPELINE)
1205 .unwrap_or(TABLE_DATA_MODEL_TRACE_V1)
1206 .to_string(),
1207 );
1208
1209 let table = self
1210 .create_physical_table(
1211 create_table,
1212 partitions,
1213 ctx,
1214 statement_executor,
1215 )
1216 .await?;
1217 let table_info = table.table_info();
1218 if table_info.is_ttl_instant_table() {
1219 instant_table_ids.insert(table_info.table_id());
1220 }
1221 table_infos.insert(table_info.table_id(), table.table_info());
1222 }
1223 }
1224 for alter_expr in alter_tables.into_iter() {
1225 statement_executor
1226 .alter_table_inner(alter_expr, ctx.clone(), TriggerReason::AutoAlter)
1227 .await?;
1228 }
1229 }
1230 }
1231
1232 for (catalog, schema, table_name) in need_refresh_table_infos {
1234 let table = self
1235 .get_table(&catalog, &schema, &table_name)
1236 .await?
1237 .context(TableNotFoundSnafu {
1238 table_name: common_catalog::format_full_table_name(
1239 &catalog,
1240 &schema,
1241 &table_name,
1242 ),
1243 })?;
1244 let table_info = table.table_info();
1245 table_infos.insert(table_info.table_id(), table.table_info());
1246 }
1247
1248 Ok(CreateAlterTableResult {
1249 instant_table_ids,
1250 table_infos,
1251 })
1252 }
1253
1254 async fn create_physical_table_on_demand(
1255 &self,
1256 ctx: &QueryContextRef,
1257 physical_table: String,
1258 statement_executor: &StatementExecutor,
1259 ) -> Result<()> {
1260 let catalog_name = ctx.current_catalog();
1261 let schema_name = ctx.current_schema();
1262
1263 if self
1265 .get_table(catalog_name, &schema_name, &physical_table)
1266 .await?
1267 .is_some()
1268 {
1269 return Ok(());
1270 }
1271
1272 if let Some(disabled_reason) = self.auto_create_disabled_reason(ctx)? {
1274 return InvalidInsertRequestSnafu {
1275 reason: format!(
1276 "Physical table `{physical_table}` does not exist, and {disabled_reason}"
1277 ),
1278 }
1279 .fail();
1280 }
1281
1282 let table_reference = TableReference::full(catalog_name, &schema_name, &physical_table);
1283 debug!("Ensuring physical metric table `{table_reference}` exists for insert");
1284
1285 let default_schema = vec![
1287 ColumnSchema {
1288 column_name: greptime_timestamp().to_string(),
1289 datatype: ColumnDataType::TimestampMillisecond as _,
1290 semantic_type: SemanticType::Timestamp as _,
1291 datatype_extension: None,
1292 options: None,
1293 },
1294 ColumnSchema {
1295 column_name: greptime_value().to_string(),
1296 datatype: ColumnDataType::Float64 as _,
1297 semantic_type: SemanticType::Field as _,
1298 datatype_extension: None,
1299 options: None,
1300 },
1301 ];
1302 let create_table_expr =
1303 &mut build_create_table_expr(&table_reference, &default_schema, default_engine())?;
1304
1305 create_table_expr.engine = METRIC_ENGINE_NAME.to_string();
1306 create_table_expr
1307 .table_options
1308 .insert(PHYSICAL_TABLE_METADATA_KEY.to_string(), "true".to_string());
1309
1310 let res = statement_executor
1312 .create_table_inner(
1313 create_table_expr,
1314 None,
1315 ctx.clone(),
1316 TriggerReason::AutoCreate,
1317 )
1318 .await;
1319
1320 match res {
1321 Ok(_) => Ok(()),
1322 Err(err) => {
1323 error!(err; "Failed to create table {table_reference}");
1324 Err(err)
1325 }
1326 }
1327 }
1328
1329 async fn get_table(
1330 &self,
1331 catalog: &str,
1332 schema: &str,
1333 table: &str,
1334 ) -> Result<Option<TableRef>> {
1335 self.catalog_manager
1336 .table(catalog, schema, table, None)
1337 .await
1338 .context(CatalogSnafu)
1339 }
1340
1341 fn get_create_table_expr_on_demand(
1342 &self,
1343 req: &RowInsertRequest,
1344 create_type: &AutoCreateTableType,
1345 ctx: &QueryContextRef,
1346 semantic_index: Option<&PerTableSemanticIndex>,
1347 ) -> Result<CreateTableExpr> {
1348 let schema = ctx.current_schema();
1349 let mut table_options = std::collections::HashMap::with_capacity(4);
1350 fill_table_options_for_create(&mut table_options, create_type, ctx);
1351 apply_per_table_semantic_options(
1352 &mut table_options,
1353 semantic_index,
1354 ctx.current_schema().as_str(),
1355 &req.table_name,
1356 );
1357
1358 let engine_name = if let AutoCreateTableType::Logical(_) = create_type {
1359 METRIC_ENGINE_NAME
1361 } else {
1362 default_engine()
1363 };
1364
1365 let table_ref = TableReference::full(ctx.current_catalog(), &schema, &req.table_name);
1366 let request_schema = req.rows.as_ref().unwrap().schema.as_slice();
1368 let mut create_table_expr =
1369 build_create_table_expr(&table_ref, request_schema, engine_name)?;
1370
1371 if ctx.extension(SPLUNK_PK_METADATA_ORDER_KEY).is_some() {
1373 reorder_splunk_primary_keys(&mut create_table_expr.primary_keys);
1374 }
1375
1376 debug!("Ensuring table `{table_ref}` exists for insert");
1377 create_table_expr.table_options.extend(table_options);
1378 Ok(create_table_expr)
1379 }
1380
1381 fn get_alter_table_expr_on_demand(
1389 &self,
1390 req: &mut RowInsertRequest,
1391 table: &TableRef,
1392 ctx: &QueryContextRef,
1393 accommodate_existing_schema: bool,
1394 is_single_value: bool,
1395 alter_existing: bool,
1396 ) -> Result<Option<AlterTableExpr>> {
1397 if !alter_existing {
1398 return Ok(None);
1399 }
1400
1401 let catalog_name = ctx.current_catalog();
1402 let schema_name = ctx.current_schema();
1403 let table_name = table.table_info().name.clone();
1404
1405 if is_ddl_reserved_table(&schema_name, &table_name) {
1408 return Ok(None);
1409 }
1410
1411 let request_schema = req.rows.as_ref().unwrap().schema.as_slice();
1412 let request_field_count = request_schema
1413 .iter()
1414 .filter(|col| col.semantic_type == SemanticType::Field as i32)
1415 .count();
1416 let column_exprs = ColumnExpr::from_column_schemas(request_schema);
1417 let add_columns = expr_helper::extract_add_columns_expr(&table.schema(), column_exprs)?;
1418 let Some(mut add_columns) = add_columns else {
1419 return Ok(None);
1420 };
1421
1422 if is_single_value {
1423 let request_is_native_histogram = request_is_native_histogram(request_schema);
1424 let table_is_native_histogram = table_is_native_histogram(table);
1425 ensure!(
1426 request_is_native_histogram == table_is_native_histogram,
1427 InvalidInsertRequestSnafu {
1428 reason: format!(
1429 "Table `{table_name}` cannot mix native histogram and float sample fields"
1430 ),
1431 }
1432 );
1433 }
1434
1435 if accommodate_existing_schema {
1437 let table_schema = table.schema();
1438 let ts_col_name = table_schema.timestamp_column().map(|c| c.name.clone());
1440 let mut field_col_name = None;
1442 if is_single_value && request_field_count <= 1 {
1443 let mut multiple_field_cols = false;
1444 table.field_columns().for_each(|col| {
1445 if field_col_name.is_none() {
1446 field_col_name = Some(col.name.clone());
1447 } else {
1448 multiple_field_cols = true;
1449 }
1450 });
1451 if multiple_field_cols {
1452 field_col_name = None;
1453 }
1454 }
1455
1456 if let Some(rows) = req.rows.as_mut() {
1458 for col in &mut rows.schema {
1459 match col.semantic_type {
1460 x if x == SemanticType::Timestamp as i32 => {
1461 if let Some(ref ts_name) = ts_col_name
1462 && col.column_name != *ts_name
1463 {
1464 col.column_name = ts_name.clone();
1465 }
1466 }
1467 x if x == SemanticType::Field as i32 => {
1468 if let Some(ref field_name) = field_col_name
1469 && col.column_name != *field_name
1470 {
1471 col.column_name = field_name.clone();
1472 }
1473 }
1474 _ => {}
1475 }
1476 }
1477 }
1478
1479 add_columns.add_columns.retain(|col| {
1481 let def = col.column_def.as_ref().unwrap();
1482 def.semantic_type == SemanticType::Tag as i32
1483 || (def.semantic_type == SemanticType::Field as i32 && field_col_name.is_none())
1484 });
1485
1486 if add_columns.add_columns.is_empty() {
1487 return Ok(None);
1488 }
1489 }
1490
1491 Ok(Some(AlterTableExpr {
1492 catalog_name: catalog_name.to_string(),
1493 schema_name: schema_name.clone(),
1494 table_name: table_name.clone(),
1495 kind: Some(Kind::AddColumns(add_columns)),
1496 }))
1497 }
1498
1499 async fn create_physical_table(
1501 &self,
1502 mut create_table_expr: CreateTableExpr,
1503 partitions: Option<Partitions>,
1504 ctx: &QueryContextRef,
1505 statement_executor: &StatementExecutor,
1506 ) -> Result<TableRef> {
1507 let res = statement_executor
1508 .create_table_inner(
1509 &mut create_table_expr,
1510 partitions,
1511 ctx.clone(),
1512 TriggerReason::AutoCreate,
1513 )
1514 .await;
1515
1516 let table_ref = TableReference::full(
1517 &create_table_expr.catalog_name,
1518 &create_table_expr.schema_name,
1519 &create_table_expr.table_name,
1520 );
1521
1522 match res {
1523 Ok(table) => {
1524 validate_trace_table_model(&table.table_info(), ctx)?;
1525 Ok(table)
1526 }
1527 Err(err) => {
1528 error!(err; "Failed to create table {}", table_ref);
1529 Err(err)
1530 }
1531 }
1532 }
1533
1534 async fn create_logical_tables(
1535 &self,
1536 create_table_exprs: Vec<CreateTableExpr>,
1537 ctx: &QueryContextRef,
1538 statement_executor: &StatementExecutor,
1539 ) -> Result<Vec<TableRef>> {
1540 let res = statement_executor
1541 .create_logical_tables(&create_table_exprs, ctx.clone(), TriggerReason::AutoCreate)
1542 .await;
1543
1544 match res {
1545 Ok(res) => Ok(res),
1546 Err(err) => {
1547 let failed_tables = create_table_exprs
1548 .into_iter()
1549 .map(|expr| {
1550 format!(
1551 "{}.{}.{}",
1552 expr.catalog_name, expr.schema_name, expr.table_name
1553 )
1554 })
1555 .collect::<Vec<_>>();
1556 error!(
1557 err;
1558 "Failed to create logical tables {:?}",
1559 failed_tables
1560 );
1561 Err(err)
1562 }
1563 }
1564 }
1565
1566 pub fn node_manager(&self) -> &NodeManagerRef {
1567 &self.node_manager
1568 }
1569
1570 pub fn partition_manager(&self) -> &PartitionRuleManagerRef {
1571 &self.partition_manager
1572 }
1573
1574 pub fn table_flownode_set_cache(&self) -> &TableFlownodeSetCacheRef {
1575 &self.table_flownode_set_cache
1576 }
1577}
1578
1579fn request_is_native_histogram(request_schema: &[ColumnSchema]) -> bool {
1580 let mut fields = request_schema
1581 .iter()
1582 .filter(|col| col.semantic_type == SemanticType::Field as i32);
1583 let Some(col) = fields.next() else {
1584 return false;
1585 };
1586
1587 fields.next().is_none()
1588 && api::helper::is_column_type_value_eq(
1589 col.datatype,
1590 col.datatype_extension.clone(),
1591 native_histogram_value_type(),
1592 )
1593}
1594
1595fn table_is_native_histogram(table: &TableRef) -> bool {
1596 let mut fields = table.field_columns();
1597 let Some(col) = fields.next() else {
1598 return false;
1599 };
1600
1601 fields.next().is_none() && is_native_histogram_value_type(&col.data_type)
1602}
1603
1604fn validate_column_count_match(requests: &RowInsertRequests) -> Result<()> {
1605 for request in &requests.inserts {
1606 let rows = request.rows.as_ref().unwrap();
1607 let column_count = rows.schema.len();
1608 rows.rows.iter().try_for_each(|r| {
1609 ensure!(
1610 r.values.len() == column_count,
1611 InvalidInsertRequestSnafu {
1612 reason: format!(
1613 "column count mismatch, columns: {}, values: {}",
1614 column_count,
1615 r.values.len()
1616 )
1617 }
1618 );
1619 Ok(())
1620 })?;
1621 }
1622 Ok(())
1623}
1624
1625pub fn validate_trace_table_model(table_info: &TableInfo, ctx: &QueryContextRef) -> Result<()> {
1628 let Some(expected @ (TABLE_DATA_MODEL_TRACE_V1 | TABLE_DATA_MODEL_TRACE_V2)) =
1629 ctx.extension(SEMANTIC_PIPELINE)
1630 else {
1631 return Ok(());
1632 };
1633 if let Some(actual) = table_info.meta.options.data_model() {
1634 ensure!(
1635 actual == expected,
1636 InvalidInsertRequestSnafu {
1637 reason: format!(
1638 "Trace table `{}` uses {actual}, but the request uses {expected}",
1639 table_info.name,
1640 ),
1641 }
1642 );
1643 }
1644 Ok(())
1645}
1646
1647pub fn fill_table_options_for_create(
1649 table_options: &mut std::collections::HashMap<String, String>,
1650 create_type: &AutoCreateTableType,
1651 ctx: &QueryContextRef,
1652) {
1653 for key in VALID_TABLE_OPTION_KEYS {
1654 if let Some(value) = ctx.extension(key) {
1655 table_options.insert(key.to_string(), value.to_string());
1656 }
1657 }
1658
1659 for (key, value) in ctx.extensions() {
1661 if is_semantic_option_key(&key) && validate_semantic_option(&key, &value) {
1662 table_options.insert(key, value);
1663 }
1664 }
1665
1666 match create_type {
1667 AutoCreateTableType::Logical(physical_table) => {
1668 table_options.insert(
1669 LOGICAL_TABLE_METADATA_KEY.to_string(),
1670 physical_table.clone(),
1671 );
1672 }
1673 AutoCreateTableType::Physical => {
1674 if let Some(append_mode) = ctx.extension(APPEND_MODE_KEY) {
1675 table_options.insert(APPEND_MODE_KEY.to_string(), append_mode.to_string());
1676 }
1677 if let Some(merge_mode) = ctx.extension(MERGE_MODE_KEY) {
1678 table_options.insert(MERGE_MODE_KEY.to_string(), merge_mode.to_string());
1679 }
1680 if let Some(time_window) = ctx.extension(TWCS_TIME_WINDOW) {
1681 table_options.insert(TWCS_TIME_WINDOW.to_string(), time_window.to_string());
1682 table_options.insert(
1684 COMPACTION_TYPE.to_string(),
1685 COMPACTION_TYPE_TWCS.to_string(),
1686 );
1687 }
1688 }
1689 AutoCreateTableType::Log => {
1692 table_options.insert(APPEND_MODE_KEY.to_string(), "true".to_string());
1693 }
1694 AutoCreateTableType::LastNonNull => {
1695 if ctx
1696 .extension(APPEND_MODE_KEY)
1697 .is_some_and(|value| value.eq_ignore_ascii_case("true"))
1698 {
1699 table_options.insert(APPEND_MODE_KEY.to_string(), "true".to_string());
1700 table_options.insert(MERGE_MODE_KEY.to_string(), "last_row".to_string());
1701 } else if let Some(merge_mode) = ctx.extension(MERGE_MODE_KEY) {
1702 table_options.insert(MERGE_MODE_KEY.to_string(), merge_mode.to_string());
1703 } else {
1704 table_options.insert(MERGE_MODE_KEY.to_string(), "last_non_null".to_string());
1705 }
1706 }
1707 AutoCreateTableType::Trace { .. } => {
1708 table_options.insert(APPEND_MODE_KEY.to_string(), "true".to_string());
1709 }
1710 }
1711}
1712
1713pub type PerTableSemanticIndex = BTreeMap<String, BTreeMap<String, BTreeMap<String, String>>>;
1719
1720pub fn parse_per_table_semantic_index(ctx: &QueryContextRef) -> Option<PerTableSemanticIndex> {
1725 let raw = ctx.extension(SEMANTIC_PER_TABLE_INDEX_KEY)?;
1726 match serde_json::from_str(raw) {
1727 Ok(index) => Some(index),
1728 Err(_) => {
1729 warn!("failed to parse semantic per-table index, skipping per-table options");
1730 None
1731 }
1732 }
1733}
1734
1735pub fn apply_per_table_semantic_options(
1743 table_options: &mut std::collections::HashMap<String, String>,
1744 index: Option<&PerTableSemanticIndex>,
1745 schema: &str,
1746 table_name: &str,
1747) {
1748 let Some(entry) = index
1749 .and_then(|index| index.get(schema))
1750 .and_then(|tables| tables.get(table_name))
1751 else {
1752 return;
1753 };
1754 for (key, value) in entry {
1755 if is_semantic_option_key(key) && validate_semantic_option(key, value) {
1756 table_options.insert(key.clone(), value.clone());
1757 }
1758 }
1759}
1760
1761pub fn build_create_table_expr(
1762 table: &TableReference,
1763 request_schema: &[ColumnSchema],
1764 engine: &str,
1765) -> Result<CreateTableExpr> {
1766 expr_helper::create_table_expr_by_column_schemas(table, request_schema, engine, None)
1767}
1768
1769pub const SPLUNK_PK_METADATA_ORDER_KEY: &str = "splunk_pk_metadata_order";
1773
1774fn reorder_splunk_primary_keys(primary_keys: &mut [String]) {
1777 const LEAD: [&str; 3] = ["host", "source", "sourcetype"];
1778 primary_keys.sort_by_key(|name| {
1781 LEAD.iter()
1782 .position(|&lead| lead == name.as_str())
1783 .unwrap_or(LEAD.len())
1784 });
1785}
1786
1787struct CreateAlterTableResult {
1789 instant_table_ids: HashSet<TableId>,
1791 table_infos: HashMap<TableId, Arc<TableInfo>>,
1793}
1794
1795struct FlowMirrorTask {
1796 requests: HashMap<Peer, RegionInsertRequests>,
1797 num_rows: usize,
1798}
1799
1800impl FlowMirrorTask {
1801 async fn new(
1802 cache: &TableFlownodeSetCacheRef,
1803 requests: impl Iterator<Item = &RegionInsertRequest>,
1804 ) -> Result<Self> {
1805 let mut src_table_reqs: HashMap<TableId, Option<(Vec<Peer>, RegionInsertRequests)>> =
1806 HashMap::new();
1807 let mut num_rows = 0;
1808
1809 for req in requests {
1810 let table_id = RegionId::from_u64(req.region_id).table_id();
1811 match src_table_reqs.get_mut(&table_id) {
1812 Some(Some((_peers, reqs))) => reqs.requests.push(req.clone()),
1813 Some(None) => continue,
1815 _ => {
1816 let peers = cache
1818 .get(table_id)
1819 .await
1820 .context(RequestInsertsSnafu)?
1821 .unwrap_or_default()
1822 .values()
1823 .cloned()
1824 .collect::<HashSet<_>>()
1825 .into_iter()
1826 .collect::<Vec<_>>();
1827
1828 if !peers.is_empty() {
1829 let mut reqs = RegionInsertRequests::default();
1830 reqs.requests.push(req.clone());
1831 num_rows += reqs
1832 .requests
1833 .iter()
1834 .map(|r| r.rows.as_ref().unwrap().rows.len())
1835 .sum::<usize>();
1836 src_table_reqs.insert(table_id, Some((peers, reqs)));
1837 } else {
1838 src_table_reqs.insert(table_id, None);
1840 }
1841 }
1842 }
1843 }
1844
1845 let mut inserts: HashMap<Peer, RegionInsertRequests> = HashMap::new();
1846
1847 for (_table_id, (peers, reqs)) in src_table_reqs
1848 .into_iter()
1849 .filter_map(|(k, v)| v.map(|v| (k, v)))
1850 {
1851 if peers.len() == 1 {
1852 inserts
1854 .entry(peers[0].clone())
1855 .or_default()
1856 .requests
1857 .extend(reqs.requests);
1858 continue;
1859 } else {
1860 for flownode in peers {
1862 inserts
1863 .entry(flownode.clone())
1864 .or_default()
1865 .requests
1866 .extend(reqs.requests.clone());
1867 }
1868 }
1869 }
1870
1871 Ok(Self {
1872 requests: inserts,
1873 num_rows,
1874 })
1875 }
1876
1877 fn detach(self, node_manager: NodeManagerRef) -> Result<()> {
1878 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.add(self.num_rows as i64);
1879 for (peer, inserts) in self.requests {
1880 let node_manager = node_manager.clone();
1881 common_runtime::spawn_global(async move {
1882 let result = node_manager
1883 .flownode(&peer)
1884 .await
1885 .handle_inserts(inserts)
1886 .await
1887 .context(RequestInsertsSnafu);
1888
1889 match result {
1890 Ok(resp) => {
1891 let affected_rows = resp.affected_rows;
1892 crate::metrics::DIST_MIRROR_ROW_COUNT.inc_by(affected_rows);
1893 crate::metrics::DIST_MIRROR_PENDING_ROW_COUNT.sub(affected_rows as _);
1894 }
1895 Err(err) => {
1896 error!(err; "Failed to insert data into flownode {}", peer);
1897 }
1898 }
1899 });
1900 }
1901
1902 Ok(())
1903 }
1904}
1905
1906#[cfg(test)]
1907mod tests {
1908 use std::sync::Arc;
1909
1910 use api::helper::ColumnDataTypeWrapper;
1911 use api::v1::helper::{field_column_schema, time_index_column_schema};
1912 use api::v1::{RowInsertRequest, Rows, Value};
1913 use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
1914 use common_meta::cache::new_table_flownode_set_cache;
1915 use common_meta::ddl::test_util::datanode_handler::NaiveDatanodeHandler;
1916 use common_meta::test_util::MockDatanodeManager;
1917 use common_query::native_histogram::NATIVE_HISTOGRAM_FIELD;
1918 use common_query::prelude::{greptime_native_histogram, set_default_prefix};
1919 use datatypes::data_type::ConcreteDataType;
1920 use datatypes::schema::ColumnSchema;
1921 use moka::future::Cache;
1922 use session::context::QueryContext;
1923 use table::TableRef;
1924 use table::dist_table::DummyDataSource;
1925 use table::metadata::{TableInfoBuilder, TableMetaBuilder, TableType};
1926
1927 use crate::insert::*;
1928 use crate::test_util::{
1929 create_partition_rule_manager, new_test_table_info, prepare_mocked_backend,
1930 };
1931
1932 fn make_table_ref_with_schema(
1933 ts_name: &str,
1934 field_name: &str,
1935 field_type: ConcreteDataType,
1936 ) -> TableRef {
1937 let schema = datatypes::schema::SchemaBuilder::try_from_columns(vec![
1938 ColumnSchema::new(
1939 ts_name,
1940 ConcreteDataType::timestamp_millisecond_datatype(),
1941 false,
1942 )
1943 .with_time_index(true),
1944 ColumnSchema::new(field_name, field_type, true),
1945 ])
1946 .unwrap()
1947 .build()
1948 .unwrap();
1949 let meta = TableMetaBuilder::empty()
1950 .schema(Arc::new(schema))
1951 .primary_key_indices(vec![])
1952 .value_indices(vec![1])
1953 .engine("mito")
1954 .next_column_id(0)
1955 .options(Default::default())
1956 .created_on(Default::default())
1957 .build()
1958 .unwrap();
1959 let info = Arc::new(
1960 TableInfoBuilder::default()
1961 .table_id(1)
1962 .table_version(0)
1963 .name("test_table")
1964 .schema_name(DEFAULT_SCHEMA_NAME)
1965 .catalog_name(DEFAULT_CATALOG_NAME)
1966 .desc(None)
1967 .table_type(TableType::Base)
1968 .meta(meta)
1969 .build()
1970 .unwrap(),
1971 );
1972 Arc::new(table::Table::new(
1973 info,
1974 table::metadata::FilterPushDownType::Unsupported,
1975 Arc::new(DummyDataSource),
1976 ))
1977 }
1978
1979 #[tokio::test]
1980 async fn test_accommodate_existing_schema_and_reject_kind_changes() {
1981 let ts_name = "my_ts";
1982 let field_name = "my_field";
1983 let table =
1984 make_table_ref_with_schema(ts_name, field_name, ConcreteDataType::float64_datatype());
1985
1986 let mut req = RowInsertRequest {
1988 table_name: "test_table".to_string(),
1989 rows: Some(Rows {
1990 schema: vec![
1991 time_index_column_schema("ts_wrong", ColumnDataType::TimestampMillisecond),
1992 field_column_schema("field_wrong", ColumnDataType::Float64),
1993 ],
1994 rows: vec![api::v1::Row {
1995 values: vec![Value::default(), Value::default()],
1996 }],
1997 }),
1998 };
1999 let ctx = Arc::new(QueryContext::with(
2000 DEFAULT_CATALOG_NAME,
2001 DEFAULT_SCHEMA_NAME,
2002 ));
2003
2004 let kv_backend = prepare_mocked_backend().await;
2005 let inserter = Inserter::new(
2006 catalog::memory::MemoryCatalogManager::new(),
2007 create_partition_rule_manager(kv_backend.clone()).await,
2008 Arc::new(MockDatanodeManager::new(NaiveDatanodeHandler)),
2009 Arc::new(new_table_flownode_set_cache(
2010 String::new(),
2011 Cache::new(100),
2012 kv_backend.clone(),
2013 )),
2014 true,
2015 );
2016 assert!(
2018 inserter
2019 .get_alter_table_expr_on_demand(&mut req, &table, &ctx, false, false, false)
2020 .unwrap()
2021 .is_none()
2022 );
2023 let alter_expr = inserter
2024 .get_alter_table_expr_on_demand(&mut req, &table, &ctx, true, true, true)
2025 .unwrap();
2026 assert!(alter_expr.is_none());
2027
2028 let req_schema = req.rows.as_ref().unwrap().schema.clone();
2030 assert_eq!(req_schema[0].column_name, ts_name);
2031 assert_eq!(req_schema[1].column_name, field_name);
2032
2033 let (datatype, datatype_extension) =
2034 ColumnDataTypeWrapper::try_from(native_histogram_value_type().clone())
2035 .unwrap()
2036 .into_parts();
2037 let mut histogram_req = RowInsertRequest {
2038 table_name: "test_table".to_string(),
2039 rows: Some(Rows {
2040 schema: vec![
2041 time_index_column_schema("ts", ColumnDataType::TimestampMillisecond),
2042 api::v1::ColumnSchema {
2043 column_name: greptime_native_histogram().to_string(),
2044 datatype: datatype as i32,
2045 semantic_type: SemanticType::Field as i32,
2046 datatype_extension,
2047 options: None,
2048 },
2049 ],
2050 rows: vec![],
2051 }),
2052 };
2053 let error = inserter
2054 .get_alter_table_expr_on_demand(&mut histogram_req, &table, &ctx, false, true, true)
2055 .unwrap_err();
2056 assert!(
2057 error
2058 .to_string()
2059 .contains("cannot mix native histogram and float sample fields")
2060 );
2061
2062 let histogram_table = make_table_ref_with_schema(
2063 "ts",
2064 greptime_native_histogram(),
2065 native_histogram_value_type().clone(),
2066 );
2067 let mut sample_req = RowInsertRequest {
2068 table_name: "test_table".to_string(),
2069 rows: Some(Rows {
2070 schema: vec![
2071 time_index_column_schema("ts", ColumnDataType::TimestampMillisecond),
2072 field_column_schema(greptime_value(), ColumnDataType::Float64),
2073 ],
2074 rows: vec![],
2075 }),
2076 };
2077 let error = inserter
2078 .get_alter_table_expr_on_demand(
2079 &mut sample_req,
2080 &histogram_table,
2081 &ctx,
2082 false,
2083 true,
2084 true,
2085 )
2086 .unwrap_err();
2087 assert!(
2088 error
2089 .to_string()
2090 .contains("cannot mix native histogram and float sample fields")
2091 );
2092 }
2093
2094 #[test]
2095 fn test_native_histogram_detection_survives_prefix_change() {
2096 set_default_prefix(Some("custom")).unwrap();
2097 let table = make_table_ref_with_schema(
2098 "custom_timestamp",
2099 NATIVE_HISTOGRAM_FIELD,
2100 native_histogram_value_type().clone(),
2101 );
2102 let (datatype, datatype_extension) =
2103 ColumnDataTypeWrapper::try_from(native_histogram_value_type().clone())
2104 .unwrap()
2105 .into_parts();
2106 let request_schema = [api::v1::ColumnSchema {
2107 column_name: greptime_native_histogram().to_string(),
2108 datatype: datatype as i32,
2109 semantic_type: SemanticType::Field as i32,
2110 datatype_extension,
2111 options: None,
2112 }];
2113
2114 assert!(request_is_native_histogram(&request_schema));
2115 assert!(table_is_native_histogram(&table));
2116 }
2117
2118 #[tokio::test]
2120 async fn test_write_meter_admission() {
2121 use std::cell::Cell;
2122 use std::sync::Mutex;
2123 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
2124
2125 use api::region::RegionResponse;
2126 use api::v1::region::region_request::Body;
2127 use arrow::array::{Int32Array, TimestampMillisecondArray};
2128 use arrow::record_batch::RecordBatch;
2129 use bytes::Bytes;
2130 use common_error::ext::{ErrorExt, RetryHint};
2131 use common_error::status_code::StatusCode;
2132 use common_grpc::flight::{FlightEncoder, FlightMessage};
2133 use common_meta::ddl::test_util::datanode_handler::DatanodeWatcher;
2134 use futures::future::BoxFuture;
2135 use meter_core::ItemCalculator;
2136 use meter_core::collect::{Collect, WriteRejected};
2137 use meter_core::data::MeterRecord;
2138 use meter_core::global::global_registry;
2139 use session::context::Channel;
2140
2141 const CATALOG: &str = "write_meter_test";
2142
2143 #[derive(Default)]
2144 struct Meter {
2145 reject: AtomicBool,
2146 attempts: Mutex<Vec<MeterRecord>>,
2147 accepted_value: AtomicU64,
2148 }
2149
2150 impl Collect for Meter {
2151 fn on_write(
2152 &self,
2153 record: MeterRecord,
2154 ) -> BoxFuture<'_, std::result::Result<(), WriteRejected>> {
2155 Box::pin(async move {
2156 if record.catalog != CATALOG {
2157 return Ok(());
2158 }
2159 let value = record.value;
2160 self.attempts.lock().unwrap().push(record);
2161 if self.reject.load(Ordering::Relaxed) {
2162 return Err(WriteRejected::new("database row quota exhausted"));
2163 }
2164 self.accepted_value.fetch_add(value, Ordering::Relaxed);
2165 Ok(())
2166 })
2167 }
2168
2169 fn on_read(&self, _: MeterRecord) {}
2170 }
2171
2172 impl ItemCalculator<InstantAndNormalInsertRequests> for Meter {
2173 fn calc(&self, _: &InstantAndNormalInsertRequests) -> u64 {
2174 17
2175 }
2176 }
2177
2178 let kv_backend = prepare_mocked_backend().await;
2179 let partition_manager = create_partition_rule_manager(kv_backend.clone()).await;
2180 let (sender, mut dispatched) = tokio::sync::mpsc::channel(16);
2181 let watcher = DatanodeWatcher::new(sender).with_handler(|_, request| {
2182 let rows = match request.body.unwrap() {
2183 Body::Inserts(requests) => requests
2184 .requests
2185 .iter()
2186 .filter_map(|request| request.rows.as_ref())
2187 .map(|rows| rows.rows.len())
2188 .sum(),
2189 Body::BulkInsert(_) => 2,
2191 body => panic!("unexpected request: {body:?}"),
2192 };
2193 Ok(RegionResponse::new(rows))
2194 });
2195 let flow_cache = Cache::new(100);
2196 let inserter = Inserter::new(
2197 catalog::memory::MemoryCatalogManager::new(),
2198 partition_manager,
2199 Arc::new(MockDatanodeManager::new(watcher)),
2200 Arc::new(new_table_flownode_set_cache(
2201 String::new(),
2202 flow_cache.clone(),
2203 kv_backend,
2204 )),
2205 true,
2206 );
2207 let mut table_info = new_test_table_info(1, "table_1", [1].into_iter());
2208 table_info.catalog_name = CATALOG.to_string();
2209 table_info.schema_name = "target_db".to_string();
2210 let table_info = Arc::new(table_info);
2211 let table_infos = HashMap::from_iter([(1, table_info.clone())]);
2212 let ctx = Arc::new(QueryContext::with_channel(
2213 DEFAULT_CATALOG_NAME,
2214 DEFAULT_SCHEMA_NAME,
2215 Channel::Postgres,
2216 ));
2217 let rows_request = || {
2218 let build = |num_rows| RegionInsertRequests {
2219 requests: vec![RegionInsertRequest {
2220 region_id: RegionId::new(1, 1).as_u64(),
2221 rows: Some(Rows {
2222 schema: vec![],
2223 rows: vec![api::v1::Row { values: vec![] }; num_rows],
2224 }),
2225 ..Default::default()
2226 }],
2227 };
2228 InstantAndNormalInsertRequests {
2229 normal_requests: build(3),
2230 instant_requests: build(2),
2231 }
2232 };
2233
2234 let output = inserter
2236 .do_request(rows_request(), &table_infos, &ctx)
2237 .await
2238 .unwrap();
2239 assert_eq!(output.meta.cost, 0);
2240 assert!(matches!(output.data, OutputData::AffectedRows(3)));
2241 dispatched.try_recv().unwrap();
2242
2243 let meter = Arc::new(Meter::default());
2244 global_registry().set_collector(meter.clone());
2245 global_registry().register_calculator(meter.clone());
2246 let enabled = Cell::new(false);
2248 write_meter!({
2249 enabled.set(true);
2250 MeterRecord::new("probe".into(), "probe".into(), 0, 0, 0)
2251 })
2252 .await
2253 .unwrap();
2254 let enabled = enabled.get();
2255
2256 let output = inserter
2257 .do_request(rows_request(), &table_infos, &ctx)
2258 .await
2259 .unwrap();
2260 assert_eq!(output.meta.cost, if enabled { 17 } else { 0 });
2261 assert!(matches!(output.data, OutputData::AffectedRows(3)));
2262 dispatched.try_recv().unwrap();
2263 assert!(flow_cache.contains_key(&1));
2264 flow_cache.invalidate_all();
2265 meter.reject.store(true, Ordering::Relaxed);
2266 let result = inserter
2267 .do_request(rows_request(), &table_infos, &ctx)
2268 .await;
2269 if enabled {
2270 let error = result.unwrap_err();
2271 assert_eq!(error.status_code(), StatusCode::RateLimited);
2272 assert_eq!(error.retry_hint(), RetryHint::Retryable);
2273 assert!(error.to_string().contains("database row quota exhausted"));
2274 assert!(dispatched.try_recv().is_err());
2275 assert!(
2276 !flow_cache.contains_key(&1),
2277 "rejected write reached flow mirroring"
2278 );
2279 assert_eq!(meter.accepted_value.load(Ordering::Relaxed), 17);
2280 let attempts = meter.attempts.lock().unwrap();
2281 assert_eq!(attempts.len(), 2);
2282 for record in attempts.iter() {
2283 assert_eq!(record.catalog, CATALOG);
2284 assert_eq!(record.schema, "target_db");
2285 assert_eq!(
2286 (record.rows, record.value, record.source),
2287 (5, 17, Channel::Postgres as u8)
2288 );
2289 }
2290 } else {
2291 assert_eq!(result.unwrap().meta.cost, 0);
2292 dispatched.try_recv().unwrap();
2293 assert!(meter.attempts.lock().unwrap().is_empty());
2294 }
2295 meter.attempts.lock().unwrap().clear();
2296
2297 if enabled {
2299 let batcher: Arc<dyn PendingRowsBatcher> = Arc::new(UnexpectedBatcher);
2300 let rows = Rows {
2301 schema: vec![
2302 api::v1::helper::tag_column_schema("a", ColumnDataType::Int32),
2303 time_index_column_schema("ts", ColumnDataType::TimestampMillisecond),
2304 field_column_schema("b", ColumnDataType::Int32),
2305 ],
2306 rows: vec![api::v1::Row {
2307 values: vec![
2308 api::v1::value::ValueData::I32Value(60).into(),
2309 Value {
2310 value_data: Some(api::v1::value::ValueData::TimestampMillisecondValue(
2311 0,
2312 )),
2313 },
2314 api::v1::value::ValueData::I32Value(0).into(),
2315 ],
2316 }],
2317 };
2318 let error = inserter
2319 .submit_table_rows(rows, table_info.clone(), ctx.clone(), &batcher)
2320 .await
2321 .unwrap_err();
2322 assert_eq!(error.status_code(), StatusCode::RateLimited);
2323 let mut attempts = meter.attempts.lock().unwrap();
2324 assert_eq!(attempts.len(), 1);
2325 assert_eq!(attempts[0].catalog, CATALOG);
2326 assert_eq!(attempts[0].schema, "target_db");
2327 assert_eq!(attempts[0].rows, 1);
2328 attempts.clear();
2329 }
2330
2331 let table = Arc::new(table::Table::new(
2332 table_info.clone(),
2333 table::metadata::FilterPushDownType::Unsupported,
2334 Arc::new(DummyDataSource),
2335 ));
2336 let batch = RecordBatch::try_new(
2337 table_info.meta.schema.arrow_schema().clone(),
2338 vec![
2339 Arc::new(Int32Array::from(vec![60, 70])),
2340 Arc::new(TimestampMillisecondArray::from(vec![0, 1])),
2341 Arc::new(Int32Array::from(vec![0, 0])),
2342 ],
2343 )
2344 .unwrap();
2345 let bulk_insert = |batch: RecordBatch| {
2346 let flight_data = FlightEncoder::default()
2347 .encode(FlightMessage::RecordBatch(batch.clone()))
2348 .into_iter()
2349 .next()
2350 .unwrap();
2351 inserter.handle_bulk_insert(
2352 table.clone(),
2353 flight_data,
2354 batch,
2355 Bytes::new(),
2356 false,
2357 Channel::Grpc,
2358 )
2359 };
2360 assert_eq!(bulk_insert(batch.slice(0, 0)).await.unwrap(), 0);
2362 assert!(meter.attempts.lock().unwrap().is_empty());
2363 assert!(dispatched.try_recv().is_err());
2364
2365 for reject in [false, true, false] {
2367 meter.reject.store(reject, Ordering::Relaxed);
2368 let result = bulk_insert(batch.clone()).await;
2369 if enabled && reject {
2370 let error = result.unwrap_err();
2371 assert_eq!(error.status_code(), StatusCode::RateLimited);
2372 assert_eq!(error.retry_hint(), RetryHint::Retryable);
2373 assert!(dispatched.try_recv().is_err());
2374 } else {
2375 assert_eq!(result.unwrap(), 2);
2376 dispatched.try_recv().unwrap();
2377 }
2378 }
2379 {
2380 let attempts = meter.attempts.lock().unwrap();
2381 assert_eq!(attempts.len(), if enabled { 3 } else { 0 });
2382 for record in attempts.iter() {
2383 assert_eq!(record.catalog, CATALOG);
2384 assert_eq!(record.schema, "target_db");
2385 assert_eq!(
2386 (record.rows, record.value, record.source),
2387 (2, 0, Channel::Grpc as u8)
2388 );
2389 }
2390 }
2391 assert_eq!(
2392 meter.accepted_value.load(Ordering::Relaxed),
2393 if enabled { 17 } else { 0 }
2394 );
2395
2396 meter.attempts.lock().unwrap().clear();
2399 let original = Arc::new(QueryContext::with_channel(
2400 CATALOG,
2401 "a",
2402 Channel::Prometheus,
2403 ));
2404 let mut batches = ["a", "b", "a"].map(|schema| {
2405 let ctx = if schema == "a" {
2406 original.clone()
2407 } else {
2408 Arc::new(QueryContext::with_channel(
2409 CATALOG,
2410 schema,
2411 Channel::Prometheus,
2412 ))
2413 };
2414 (
2415 ctx,
2416 RowInsertRequests {
2417 inserts: vec![RowInsertRequest {
2418 table_name: "data".into(),
2419 rows: Some(Rows {
2420 schema: vec![],
2421 rows: vec![api::v1::Row::default(); 2],
2422 }),
2423 }],
2424 },
2425 )
2426 });
2427 admit_row_insert_batches(&mut batches).await.unwrap();
2428 admit_row_insert_batches(&mut batches).await.unwrap();
2429 assert_eq!(original.write_rows_to_admit(CATALOG, "a", 4), 4);
2430 for (ctx, _) in &batches {
2431 assert_eq!(
2432 ctx.write_rows_to_admit(CATALOG, &ctx.current_schema(), 2),
2433 0
2434 );
2435 assert_eq!(ctx.channel(), Channel::Prometheus);
2436 }
2437 {
2438 let attempts = meter.attempts.lock().unwrap();
2439 let totals = attempts
2440 .iter()
2441 .map(|r| (r.schema.as_str(), r.rows, r.value))
2442 .collect::<Vec<_>>();
2443 assert_eq!(
2444 totals,
2445 if enabled {
2446 vec![("a", 4, 0), ("b", 2, 0)]
2447 } else {
2448 vec![]
2449 }
2450 );
2451 }
2452 meter.attempts.lock().unwrap().clear();
2453 meter.reject.store(false, Ordering::Relaxed);
2454 let ctx = Arc::new(QueryContext::with_channel(
2455 CATALOG,
2456 "logical",
2457 Channel::Otlp,
2458 ));
2459 let admitted = admit_write(2, &ctx).await.unwrap();
2460 let mut requests = RowInsertRequests {
2461 inserts: vec![RowInsertRequest {
2462 table_name: "metric".to_string(),
2463 rows: Some(Rows {
2464 schema: vec![],
2465 rows: vec![api::v1::Row::default(); 2],
2466 }),
2467 }],
2468 };
2469 let original = requests.clone();
2470 let cost = Inserter::meter_row_inserts(&mut requests, &admitted)
2471 .await
2472 .unwrap();
2473 assert_eq!(cost, if enabled { 17 } else { 0 });
2474 assert_eq!(requests, original);
2475 let records = meter
2476 .attempts
2477 .lock()
2478 .unwrap()
2479 .iter()
2480 .map(|record| (record.rows, record.value))
2481 .collect::<Vec<_>>();
2482 assert_eq!(
2483 records,
2484 if enabled {
2485 vec![(2, 0), (0, 17)]
2486 } else {
2487 vec![]
2488 }
2489 );
2490 meter.reject.store(true, Ordering::Relaxed);
2491 let result = Inserter::meter_row_inserts(&mut requests, &admitted).await;
2492 if enabled {
2493 assert_eq!(result.unwrap_err().status_code(), StatusCode::RateLimited);
2494 } else {
2495 assert_eq!(result.unwrap(), 0);
2496 }
2497 assert_eq!(requests, original);
2498 }
2499
2500 #[test]
2501 fn test_skip_wal_does_not_change_table_options() {
2502 check_skip_wal_does_not_change_table_options(false);
2503 check_skip_wal_does_not_change_table_options(true);
2504 }
2505
2506 fn check_skip_wal_does_not_change_table_options(skip_wal: bool) {
2507 let ctx = Arc::new(QueryContext::with(
2508 DEFAULT_CATALOG_NAME,
2509 DEFAULT_SCHEMA_NAME,
2510 ));
2511 ctx.set_skip_wal(skip_wal);
2512 let mut options = Default::default();
2513 fill_table_options_for_create(&mut options, &AutoCreateTableType::Physical, &ctx);
2514 assert!(!options.contains_key(session::hints::INSERT_SKIP_WAL_HINT));
2515 assert!(!options.contains_key("skip_wal"));
2516 }
2517
2518 #[test]
2519 fn test_last_non_null_create_options_preserve_default_without_append_mode() {
2520 let ctx = Arc::new(QueryContext::with(
2521 DEFAULT_CATALOG_NAME,
2522 DEFAULT_SCHEMA_NAME,
2523 ));
2524 let mut table_options = Default::default();
2525
2526 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
2527
2528 assert_eq!(
2529 Some("last_non_null"),
2530 table_options.get(MERGE_MODE_KEY).map(String::as_str)
2531 );
2532 assert!(!table_options.contains_key(APPEND_MODE_KEY));
2533 }
2534
2535 #[test]
2536 fn test_fill_table_options_copies_semantic_extensions() {
2537 use table::requests::{
2538 SEMANTIC_METRIC_TYPE, SEMANTIC_PER_TABLE_INDEX_KEY, SEMANTIC_SIGNAL_TYPE,
2539 SEMANTIC_SOURCE, SEMANTIC_SOURCE_VERSION, SIGNAL_TYPE_METRIC, SOURCE_OPENTELEMETRY,
2540 };
2541
2542 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
2543 ctx.set_extension(SEMANTIC_SIGNAL_TYPE, SIGNAL_TYPE_METRIC);
2544 ctx.set_extension(SEMANTIC_SOURCE, SOURCE_OPENTELEMETRY);
2545 ctx.set_extension(SEMANTIC_SOURCE_VERSION, "2.0");
2546 ctx.set_extension(SEMANTIC_METRIC_TYPE, "bogus");
2547 ctx.set_extension(SEMANTIC_PER_TABLE_INDEX_KEY, "{}");
2549 let ctx = Arc::new(ctx);
2550 let mut table_options = Default::default();
2551
2552 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::Physical, &ctx);
2553
2554 assert_eq!(
2555 Some(SIGNAL_TYPE_METRIC),
2556 table_options.get(SEMANTIC_SIGNAL_TYPE).map(String::as_str)
2557 );
2558 assert_eq!(
2559 Some(SOURCE_OPENTELEMETRY),
2560 table_options.get(SEMANTIC_SOURCE).map(String::as_str)
2561 );
2562 assert_eq!(
2563 Some("2.0"),
2564 table_options
2565 .get(SEMANTIC_SOURCE_VERSION)
2566 .map(String::as_str)
2567 );
2568 assert!(!table_options.contains_key(SEMANTIC_METRIC_TYPE));
2569 assert!(!table_options.contains_key(SEMANTIC_PER_TABLE_INDEX_KEY));
2570 }
2571
2572 #[test]
2573 fn test_apply_per_table_semantic_options() {
2574 use table::requests::{
2575 SEMANTIC_METRIC_TYPE, SEMANTIC_METRIC_UNIT, SEMANTIC_PER_TABLE_INDEX_KEY,
2576 };
2577
2578 let index = format!(
2579 r#"{{
2580 "{DEFAULT_SCHEMA_NAME}": {{
2581 "http_requests_total": {{
2582 "greptime.semantic.metric.type": "counter",
2583 "greptime.semantic.metric.unit": "By",
2584 "greptime.semantic.metric.type_BOGUS": "x"
2585 }},
2586 "other_table": {{
2587 "greptime.semantic.metric.type": "gauge"
2588 }}
2589 }},
2590 "other_schema": {{
2591 "http_requests_total": {{
2592 "greptime.semantic.metric.type": "gauge"
2593 }}
2594 }}
2595 }}"#
2596 );
2597 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
2598 ctx.set_extension(SEMANTIC_PER_TABLE_INDEX_KEY, index);
2599 let ctx = Arc::new(ctx);
2600
2601 let index = parse_per_table_semantic_index(&ctx);
2602 assert!(index.is_some());
2603 let index = index.as_ref();
2604
2605 let mut table_options = std::collections::HashMap::new();
2606 apply_per_table_semantic_options(
2607 &mut table_options,
2608 index,
2609 DEFAULT_SCHEMA_NAME,
2610 "http_requests_total",
2611 );
2612 assert_eq!(
2614 table_options.get(SEMANTIC_METRIC_TYPE).map(String::as_str),
2615 Some("counter")
2616 );
2617 assert_eq!(
2618 table_options.get(SEMANTIC_METRIC_UNIT).map(String::as_str),
2619 Some("By")
2620 );
2621 assert!(!table_options.contains_key("greptime.semantic.metric.type_BOGUS"));
2624 assert_eq!(table_options.len(), 2);
2625
2626 let mut empty = std::collections::HashMap::new();
2627 apply_per_table_semantic_options(&mut empty, index, DEFAULT_SCHEMA_NAME, "not_in_index");
2628 assert!(empty.is_empty());
2629
2630 let mut opts = std::collections::HashMap::new();
2633 apply_per_table_semantic_options(
2634 &mut opts,
2635 index,
2636 "schema_without_entry",
2637 "http_requests_total",
2638 );
2639 assert!(opts.is_empty());
2640
2641 let bare = Arc::new(QueryContext::with(
2643 DEFAULT_CATALOG_NAME,
2644 DEFAULT_SCHEMA_NAME,
2645 ));
2646 assert!(parse_per_table_semantic_index(&bare).is_none());
2647 let mut opts = std::collections::HashMap::new();
2648 apply_per_table_semantic_options(
2649 &mut opts,
2650 None,
2651 DEFAULT_SCHEMA_NAME,
2652 "http_requests_total",
2653 );
2654 assert!(opts.is_empty());
2655 }
2656
2657 #[test]
2658 fn test_last_non_null_create_options_preserve_default_with_append_mode_false() {
2659 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
2660 ctx.set_extension(APPEND_MODE_KEY, "false");
2661 let ctx = Arc::new(ctx);
2662 let mut table_options = Default::default();
2663
2664 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
2665
2666 assert!(!table_options.contains_key(APPEND_MODE_KEY));
2667 assert_eq!(
2668 Some("last_non_null"),
2669 table_options.get(MERGE_MODE_KEY).map(String::as_str)
2670 );
2671 }
2672
2673 #[test]
2674 fn test_last_non_null_create_options_use_configured_merge_mode() {
2675 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
2676 ctx.set_extension(MERGE_MODE_KEY, "last_row");
2677 let ctx = Arc::new(ctx);
2678 let mut table_options = Default::default();
2679
2680 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
2681
2682 assert_eq!(
2683 Some("last_row"),
2684 table_options.get(MERGE_MODE_KEY).map(String::as_str)
2685 );
2686 assert!(!table_options.contains_key(APPEND_MODE_KEY));
2687 }
2688
2689 #[test]
2690 fn test_last_non_null_create_options_use_last_row_with_append_mode_true() {
2691 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME);
2692 ctx.set_extension(APPEND_MODE_KEY, "true");
2693 let ctx = Arc::new(ctx);
2694 let mut table_options = Default::default();
2695
2696 fill_table_options_for_create(&mut table_options, &AutoCreateTableType::LastNonNull, &ctx);
2697
2698 assert_eq!(
2699 Some("true"),
2700 table_options.get(APPEND_MODE_KEY).map(String::as_str)
2701 );
2702 assert_eq!(
2703 Some("last_row"),
2704 table_options.get(MERGE_MODE_KEY).map(String::as_str)
2705 );
2706 }
2707
2708 struct UnexpectedBatcher;
2709
2710 #[async_trait::async_trait]
2711 impl PendingRowsBatcher for UnexpectedBatcher {
2712 async fn acquire(&self) -> Result<Arc<tokio::sync::OwnedSemaphorePermit>> {
2713 panic!("empty writes must not acquire batch admission")
2714 }
2715
2716 async fn submit(
2717 &self,
2718 _table_info: TableInfoRef,
2719 _batch: arrow::record_batch::RecordBatch,
2720 _ctx: QueryContextRef,
2721 _permit: Arc<tokio::sync::OwnedSemaphorePermit>,
2722 ) -> Result<usize> {
2723 panic!("empty writes must not submit a batch")
2724 }
2725 }
2726
2727 async fn batcher_test_inserter() -> Inserter {
2728 let kv_backend = prepare_mocked_backend().await;
2729 Inserter::new(
2730 catalog::memory::MemoryCatalogManager::new(),
2731 create_partition_rule_manager(kv_backend.clone()).await,
2732 Arc::new(MockDatanodeManager::new(NaiveDatanodeHandler)),
2733 Arc::new(new_table_flownode_set_cache(
2734 String::new(),
2735 Cache::new(100),
2736 kv_backend,
2737 )),
2738 true,
2739 )
2740 }
2741
2742 #[tokio::test]
2743 async fn test_logical_batcher_eligibility() {
2744 use catalog::RegisterTableRequest;
2745 use catalog::memory::MemoryCatalogManager;
2746 use common_meta::instruction::{CacheIdent, CreateFlow};
2747 use common_meta::kv_backend::KvBackendRef;
2748 use common_meta::kv_backend::memory::MemoryKvBackend;
2749 use datatypes::schema::{ColumnDefaultConstraint, SchemaBuilder};
2750 let requests = RowInsertRequests {
2751 inserts: vec![RowInsertRequest {
2752 table_name: "test_table".to_string(),
2753 rows: None,
2754 }],
2755 };
2756 let original =
2757 make_table_ref_with_schema("ts", "value", ConcreteDataType::float64_datatype())
2758 .table_info();
2759 for case in [
2760 "eligible",
2761 "physical",
2762 "ordinary",
2763 "instant",
2764 "disabled",
2765 "hint",
2766 "flow",
2767 "required_tag",
2768 "default",
2769 ] {
2770 let mut info = (*original).clone();
2771 info.meta.engine = METRIC_ENGINE_NAME.to_string();
2772 info.meta.options.extra_options.insert(
2773 LOGICAL_TABLE_METADATA_KEY.to_string(),
2774 "physical".to_string(),
2775 );
2776 let mut ctx = QueryContext::arc().fork();
2777 match case {
2778 "physical" => {
2779 info.meta
2780 .options
2781 .extra_options
2782 .insert(LOGICAL_TABLE_METADATA_KEY.to_string(), "other".to_string());
2783 }
2784 "ordinary" => info.meta.engine = "mito".to_string(),
2785 "instant" => info.meta.options.ttl = Some(common_time::ttl::TimeToLive::Instant),
2786 "hint" => ctx.set_extension(AUTO_CREATE_TABLE_KEY, "false"),
2787 "required_tag" | "default" => {
2788 let mut columns = info.meta.schema.column_schemas().to_vec();
2789 let mut tag = ColumnSchema::new(
2790 "tag",
2791 ConcreteDataType::string_datatype(),
2792 case != "required_tag",
2793 );
2794 if case == "default" {
2795 tag = tag
2796 .with_default_constraint(Some(ColumnDefaultConstraint::null_value()))
2797 .unwrap();
2798 }
2799 columns.push(tag);
2800 info.meta.schema = Arc::new(
2801 SchemaBuilder::try_from_columns(columns)
2802 .unwrap()
2803 .build()
2804 .unwrap(),
2805 );
2806 info.meta.primary_key_indices = vec![2];
2807 }
2808 _ => {}
2809 }
2810 let catalog = MemoryCatalogManager::with_default_setup();
2811 let table = Arc::new(table::Table::new(
2812 Arc::new(info),
2813 table::metadata::FilterPushDownType::Unsupported,
2814 Arc::new(DummyDataSource),
2815 ));
2816 catalog
2817 .register_table_sync(RegisterTableRequest {
2818 catalog: DEFAULT_CATALOG_NAME.to_string(),
2819 schema: DEFAULT_SCHEMA_NAME.to_string(),
2820 table_name: "test_table".to_string(),
2821 table_id: 1,
2822 table,
2823 })
2824 .unwrap();
2825 let mut inserter = batcher_test_inserter().await;
2826 inserter.catalog_manager = catalog;
2827 inserter.auto_create_table = case != "disabled";
2828 let kv_backend: KvBackendRef = Arc::new(MemoryKvBackend::default());
2829 inserter.table_flownode_set_cache = Arc::new(new_table_flownode_set_cache(
2830 String::new(),
2831 Cache::new(10),
2832 kv_backend,
2833 ));
2834 if case == "flow" {
2835 inserter
2836 .table_flownode_set_cache
2837 .invalidate(&[CacheIdent::CreateFlow(CreateFlow {
2838 flow_id: 1,
2839 source_table_ids: vec![1],
2840 partition_to_peer_mapping: vec![(0, Peer::empty(1))],
2841 })])
2842 .await
2843 .unwrap();
2844 }
2845 assert_eq!(
2846 inserter
2847 .can_batch_metric_rows(&requests, &Arc::new(ctx), "physical")
2848 .await
2849 .unwrap(),
2850 case == "eligible",
2851 "{case}"
2852 );
2853 }
2854 }
2855
2856 #[tokio::test]
2857 async fn test_batcher_meter_preserves_request() {
2858 let mut requests = RowInsertRequests {
2859 inserts: vec![RowInsertRequest {
2860 table_name: "sample".to_string(),
2861 rows: Some(Rows {
2862 schema: vec![],
2863 rows: vec![api::v1::Row {
2864 values: vec![Value {
2865 value_data: Some(api::v1::value::ValueData::F64Value(1.5)),
2866 }],
2867 }],
2868 }),
2869 }],
2870 };
2871 let expected = requests.clone();
2872 Inserter::meter_row_inserts(&mut requests, &QueryContext::arc())
2873 .await
2874 .unwrap();
2875 assert_eq!(requests, expected);
2876 }
2877
2878 #[tokio::test]
2879 async fn test_instant_table_bypasses_batcher() {
2880 let batcher: Arc<dyn PendingRowsBatcher> = Arc::new(UnexpectedBatcher);
2881 let inserter = batcher_test_inserter()
2882 .await
2883 .with_pending_rows_batcher(Some(batcher));
2884 let mut ctx = session::context::QueryContextBuilder::default().build();
2885 ctx.set_batching_enabled(true);
2886 let ctx = Arc::new(ctx);
2887 let table = make_table_ref_with_schema("ts", "value", ConcreteDataType::float64_datatype())
2888 .table_info();
2889 assert!(inserter.table_batcher(&table, &ctx).is_some());
2890 let mut instant = (*table).clone();
2891 instant.meta.options.ttl = Some(common_time::ttl::TimeToLive::Instant);
2892 assert!(inserter.table_batcher(&Arc::new(instant), &ctx).is_none());
2893 }
2894
2895 #[tokio::test]
2896 async fn test_empty_prepared_rows_skip_batcher() {
2897 let inserter = batcher_test_inserter().await;
2898 let table = make_table_ref_with_schema("ts", "value", ConcreteDataType::float64_datatype())
2899 .table_info();
2900 let batcher: Arc<dyn PendingRowsBatcher> = Arc::new(UnexpectedBatcher);
2901 let ctx = QueryContext::arc();
2902 let output = inserter
2903 .submit_table_rows(
2904 Rows {
2905 schema: vec![],
2906 rows: vec![],
2907 },
2908 table.clone(),
2909 ctx.clone(),
2910 &batcher,
2911 )
2912 .await
2913 .unwrap();
2914 assert!(matches!(output.data, OutputData::AffectedRows(0)));
2915 let output = inserter
2916 .submit_pending_rows(
2917 RowInsertRequests {
2918 inserts: vec![RowInsertRequest {
2919 table_name: table.name.clone(),
2920 rows: None,
2921 }],
2922 },
2923 HashMap::from_iter([(table.table_id(), table)]),
2924 ctx,
2925 &batcher,
2926 )
2927 .await
2928 .unwrap();
2929 assert!(matches!(output.data, OutputData::AffectedRows(0)));
2930 }
2931}