1pub mod builder;
16mod dashboard;
17mod grpc;
18mod influxdb;
19mod jaeger;
20mod log_handler;
21mod logs;
22mod opentsdb;
23mod otlp;
24pub mod prom_store;
25mod promql;
26mod region_query;
27pub mod standalone;
28
29use std::collections::HashSet;
30use std::pin::Pin;
31use std::sync::atomic::AtomicBool;
32use std::sync::{Arc, atomic};
33use std::time::{Duration, SystemTime};
34
35use async_stream::stream;
36use async_trait::async_trait;
37use auth::{
38 PermissionChecker, PermissionCheckerRef, PermissionReq, PermissionTableTarget,
39 PermissionTableTargets,
40};
41use catalog::CatalogManagerRef;
42use catalog::process_manager::{
43 ProcessManagerRef, QueryStatement as CatalogQueryStatement, SlowQueryRecorder, SlowQueryTimer,
44};
45use client::OutputData;
46use common_base::Plugins;
47use common_base::cancellation::CancellableFuture;
48use common_error::ext::{BoxedError, ErrorExt};
49use common_event_recorder::EventRecorderRef;
50use common_meta::cache::TableFlownodeSetCacheRef;
51use common_meta::cache_invalidator::CacheInvalidatorRef;
52use common_meta::key::TableMetadataManagerRef;
53use common_meta::key::table_name::TableNameKey;
54use common_meta::node_manager::NodeManagerRef;
55use common_meta::procedure_executor::ProcedureExecutorRef;
56use common_query::Output;
57use common_recordbatch::RecordBatchStreamWrapper;
58use common_recordbatch::error::StreamTimeoutSnafu;
59use common_telemetry::logging::SlowQueryOptions;
60use common_telemetry::{debug, error, tracing};
61use dashmap::DashMap;
62use datafusion::physical_plan::ExecutionPlan;
63use datafusion_expr::LogicalPlan;
64use futures::{Stream, StreamExt, future};
65use lazy_static::lazy_static;
66use operator::delete::DeleterRef;
67use operator::insert::InserterRef;
68use operator::statement::{StatementExecutor, StatementExecutorRef};
69use partition::manager::PartitionRuleManagerRef;
70use pipeline::pipeline_operator::PipelineOperator;
71use prometheus::HistogramTimer;
72use promql_parser::label::Matcher;
73use query::QueryEngineRef;
74use query::metrics::OnDone;
75use query::parser::{PromQuery, QueryStatement};
76use query::query_engine::DescribeResult;
77use query::query_engine::options::{QueryOptions, validate_catalog_and_schema};
78use servers::error::{
79 self as server_error, AuthSnafu, CommonMetaSnafu, ExecuteQuerySnafu,
80 OtlpMetricModeIncompatibleSnafu, UnexpectedResultSnafu,
81};
82use servers::interceptor::{
83 PromQueryInterceptor, PromQueryInterceptorRef, SqlQueryInterceptor, SqlQueryInterceptorRef,
84};
85use servers::otlp::metrics::legacy_normalize_otlp_name;
86use servers::prometheus_handler::{
87 ParsedPromQuery, PrometheusHandler, resolve_schema_from_matchers,
88};
89use servers::query_handler::sql::SqlQueryHandler;
90use session::context::{Channel, QueryContextRef};
91use session::table_name::table_idents_to_full_name;
92use snafu::prelude::*;
93use sql::ast::ObjectNamePartExt;
94use sql::dialect::Dialect;
95use sql::parser::{ParseOptions, ParserContext};
96use sql::statements::comment::CommentObject;
97use sql::statements::copy::{CopyDatabase, CopyTable};
98use sql::statements::statement::Statement;
99use sql::statements::tql::Tql;
100use sql::util::{extract_tables_from_prom_expr_checked, extract_tables_from_statement_checked};
101use sqlparser::ast::{AnalyzeFormat, ObjectName};
102pub use standalone::StandaloneDatanodeManager;
103use table::requests::{OTLP_METRIC_COMPAT_KEY, OTLP_METRIC_COMPAT_PROM};
104use tracing::Span;
105
106use crate::error::{
107 self, CollectRecordbatchSnafu, Error, ExecLogicalPlanSnafu, ExecutePromqlSnafu, ExternalSnafu,
108 InvalidSqlSnafu, ParseSqlSnafu, PermissionSnafu, PlanStatementSnafu, Result,
109 SqlExecInterceptedSnafu, StatementTimeoutSnafu, TableOperationSnafu,
110};
111use crate::service_config::InfluxdbMergeMode;
112use crate::stream_wrapper::CancellableStreamWrapper;
113
114lazy_static! {
115 static ref OTLP_LEGACY_DEFAULT_VALUE: String = "legacy".to_string();
116}
117
118#[derive(Clone)]
122pub struct Instance {
123 frontend_peer_addr: String,
124 catalog_manager: CatalogManagerRef,
125 pipeline_operator: Arc<PipelineOperator>,
126 statement_executor: Arc<StatementExecutor>,
127 query_engine: QueryEngineRef,
128 plugins: Plugins,
129 inserter: InserterRef,
130 deleter: DeleterRef,
131 table_metadata_manager: TableMetadataManagerRef,
132 event_recorder: EventRecorderRef,
133 slow_query_recorder: EventRecorderRef,
134 process_manager: ProcessManagerRef,
135 slow_query_options: SlowQueryOptions,
136 influxdb_default_merge_mode: InfluxdbMergeMode,
137 trace_ingest_chunk_size: usize,
138 suspend: Arc<AtomicBool>,
139
140 otlp_metrics_table_legacy_cache: DashMap<String, DashMap<String, bool>>,
145}
146
147impl Instance {
148 pub fn frontend_peer_addr(&self) -> &str {
149 &self.frontend_peer_addr
150 }
151
152 pub fn catalog_manager(&self) -> &CatalogManagerRef {
153 &self.catalog_manager
154 }
155
156 pub fn query_engine(&self) -> &QueryEngineRef {
157 &self.query_engine
158 }
159
160 pub fn plugins(&self) -> &Plugins {
161 &self.plugins
162 }
163
164 pub fn statement_executor(&self) -> &StatementExecutorRef {
165 &self.statement_executor
166 }
167
168 pub fn table_metadata_manager(&self) -> &TableMetadataManagerRef {
169 &self.table_metadata_manager
170 }
171
172 pub fn inserter(&self) -> &InserterRef {
173 &self.inserter
174 }
175
176 pub fn process_manager(&self) -> &ProcessManagerRef {
177 &self.process_manager
178 }
179
180 pub fn event_recorder(&self) -> EventRecorderRef {
182 self.event_recorder.clone()
183 }
184
185 pub fn node_manager(&self) -> &NodeManagerRef {
186 self.inserter.node_manager()
187 }
188
189 pub fn partition_manager(&self) -> &PartitionRuleManagerRef {
190 self.inserter.partition_manager()
191 }
192
193 pub fn table_flownode_set_cache(&self) -> &TableFlownodeSetCacheRef {
194 self.inserter.table_flownode_set_cache()
195 }
196
197 pub fn cache_invalidator(&self) -> &CacheInvalidatorRef {
198 self.statement_executor.cache_invalidator()
199 }
200
201 pub fn procedure_executor(&self) -> &ProcedureExecutorRef {
202 self.statement_executor.procedure_executor()
203 }
204
205 pub fn suspend_state(&self) -> Arc<AtomicBool> {
206 self.suspend.clone()
207 }
208
209 pub(crate) fn is_suspended(&self) -> bool {
210 self.suspend.load(atomic::Ordering::Relaxed)
211 }
212}
213
214fn parse_stmt(sql: &str, dialect: &(dyn Dialect + Send + Sync)) -> Result<Vec<Statement>> {
215 ParserContext::create_with_dialect(sql, dialect, ParseOptions::default()).context(ParseSqlSnafu)
216}
217
218fn is_explain_analyze_verbose(stmt: &Statement) -> bool {
219 matches!(stmt, Statement::Explain(explain) if explain.analyze && explain.verbose)
220}
221
222fn validate_analyze_stream_statement(stmt: &mut Statement) -> Result<()> {
223 let Statement::Explain(explain) = stmt else {
224 return InvalidSqlSnafu {
225 err_msg: "only EXPLAIN ANALYZE VERBOSE statement is supported",
226 }
227 .fail();
228 };
229 ensure!(
230 explain.analyze && explain.verbose,
231 InvalidSqlSnafu {
232 err_msg: "statement must be EXPLAIN ANALYZE VERBOSE"
233 }
234 );
235 match explain.format {
236 None | Some(AnalyzeFormat::JSON) => {
237 explain.format = None;
241 Ok(())
242 }
243 Some(_) => InvalidSqlSnafu {
244 err_msg: "only FORMAT JSON is supported for analyze stream",
245 }
246 .fail(),
247 }
248}
249
250impl Instance {
251 fn statement_slow_query_timer(
252 &self,
253 stmt: &Statement,
254 schema_name: String,
255 ) -> Option<SlowQueryTimer> {
256 if !stmt.is_readonly() || !self.slow_query_options.enable {
257 return None;
258 }
259
260 Some(SlowQueryTimer::new(
261 CatalogQueryStatement::Sql(stmt.clone()),
262 schema_name,
263 self.slow_query_options.threshold,
264 self.slow_query_options.sample_ratio,
265 self.slow_query_options.record_type,
266 self.slow_query_recorder.clone(),
267 ))
268 }
269
270 async fn query_statement(&self, stmt: Statement, query_ctx: QueryContextRef) -> Result<Output> {
271 check_permission(self.plugins.clone(), &stmt, &query_ctx)?;
272
273 let query_interceptor = self.plugins.get::<SqlQueryInterceptorRef<Error>>();
274 let query_interceptor = query_interceptor.as_ref();
275
276 if should_track_statement_process(&stmt) {
277 let catalog_name = query_ctx.current_catalog().to_string();
278 let schema_name = query_ctx.current_schema();
279 let slow_query_timer = self.statement_slow_query_timer(&stmt, schema_name.clone());
280 let timeout_recorder = is_explain_analyze_verbose(&stmt)
281 .then(|| slow_query_timer.as_ref().map(SlowQueryTimer::recorder))
282 .flatten();
283
284 let ticket = self.process_manager.register_query(
285 catalog_name,
286 vec![schema_name],
287 stmt.to_string(),
288 query_ctx.conn_info().to_string(),
289 Some(query_ctx.process_id()),
290 slow_query_timer,
291 );
292
293 let query_fut = self.exec_statement_with_timeout(
294 stmt,
295 query_ctx,
296 query_interceptor,
297 timeout_recorder,
298 );
299
300 CancellableFuture::new(query_fut, ticket.cancellation_handle.clone())
301 .await
302 .map_err(|_| error::CancelledSnafu.build())?
303 .map(|output| {
304 let Output { meta, data } = output;
305
306 let data = match data {
307 OutputData::Stream(stream) => OutputData::Stream(Box::pin(
308 CancellableStreamWrapper::new(stream, ticket),
309 )),
310 other => other,
311 };
312 Output { data, meta }
313 })
314 } else {
315 self.exec_statement_with_timeout(stmt, query_ctx, query_interceptor, None)
316 .await
317 }
318 }
319
320 async fn exec_statement_with_timeout(
321 &self,
322 stmt: Statement,
323 query_ctx: QueryContextRef,
324 query_interceptor: Option<&SqlQueryInterceptorRef<Error>>,
325 timeout_recorder: Option<SlowQueryRecorder>,
326 ) -> Result<Output> {
327 let timeout = derive_timeout(&stmt, &query_ctx);
328 match timeout {
329 Some(timeout) => {
330 let start = tokio::time::Instant::now();
331 let output = tokio::time::timeout(
332 timeout,
333 self.exec_statement(stmt, query_ctx, query_interceptor),
334 )
335 .await
336 .map_err(|_| StatementTimeoutSnafu.build())??;
337 let output = map_query_output(output)?;
338 let remaining_timeout = timeout.checked_sub(start.elapsed()).unwrap_or_default();
340 attach_timeout(output, remaining_timeout, timeout_recorder)
341 }
342 None => self
343 .exec_statement(stmt, query_ctx, query_interceptor)
344 .await
345 .and_then(map_query_output),
346 }
347 }
348
349 async fn exec_statement(
350 &self,
351 stmt: Statement,
352 query_ctx: QueryContextRef,
353 query_interceptor: Option<&SqlQueryInterceptorRef<Error>>,
354 ) -> Result<Output> {
355 match stmt {
356 Statement::Query(_) | Statement::Explain(_) | Statement::Delete(_) => {
357 if let Statement::Explain(explain) = &stmt
359 && let Some(format) = explain.format()
360 {
361 query_ctx.set_explain_format(format.to_string());
362 }
363
364 self.plan_and_exec_sql(stmt, &query_ctx, query_interceptor)
365 .await
366 }
367 Statement::Tql(tql) => {
368 self.plan_and_exec_tql(&query_ctx, query_interceptor, tql)
369 .await
370 }
371 _ => {
372 query_interceptor.pre_execute(Some(&stmt), None, query_ctx.clone())?;
373 self.statement_executor
374 .execute_sql(stmt, query_ctx)
375 .await
376 .context(TableOperationSnafu)
377 }
378 }
379 }
380
381 async fn plan_and_exec_sql(
382 &self,
383 stmt: Statement,
384 query_ctx: &QueryContextRef,
385 query_interceptor: Option<&SqlQueryInterceptorRef<Error>>,
386 ) -> Result<Output> {
387 let stmt = QueryStatement::Sql(stmt);
388 let plan = self
389 .statement_executor
390 .plan(&stmt, query_ctx.clone())
391 .await?;
392 let QueryStatement::Sql(stmt) = stmt else {
393 unreachable!()
394 };
395 query_interceptor.pre_execute(Some(&stmt), Some(&plan), query_ctx.clone())?;
396
397 self.statement_executor
398 .exec_plan(plan, query_ctx.clone())
399 .await
400 .context(TableOperationSnafu)
401 }
402
403 async fn plan_and_exec_tql(
404 &self,
405 query_ctx: &QueryContextRef,
406 query_interceptor: Option<&SqlQueryInterceptorRef<Error>>,
407 tql: Tql,
408 ) -> Result<Output> {
409 let plan = self
410 .statement_executor
411 .plan_tql(tql.clone(), query_ctx)
412 .await?;
413 query_interceptor.pre_execute(
414 Some(&Statement::Tql(tql)),
415 Some(&plan),
416 query_ctx.clone(),
417 )?;
418 self.statement_executor
419 .exec_plan(plan, query_ctx.clone())
420 .await
421 .context(TableOperationSnafu)
422 }
423
424 async fn check_otlp_legacy(
425 &self,
426 names: &[String],
427 ctx: &QueryContextRef,
428 ) -> server_error::Result<bool> {
429 let db_string = ctx.get_db_string();
430 let cache = self
432 .otlp_metrics_table_legacy_cache
433 .entry(db_string.clone())
434 .or_default();
435 if let Some(flag) = fast_legacy_check(&cache, names)? {
436 return Ok(flag);
437 }
438 drop(cache);
440
441 let catalog = ctx.current_catalog();
442 let schema = ctx.current_schema();
443
444 let normalized_names = names
446 .iter()
447 .map(|n| legacy_normalize_otlp_name(n))
448 .collect::<Vec<_>>();
449 let table_names = normalized_names
450 .iter()
451 .map(|n| TableNameKey::new(catalog, &schema, n))
452 .collect::<Vec<_>>();
453 let table_values = self
454 .table_metadata_manager()
455 .table_name_manager()
456 .batch_get(table_names)
457 .await
458 .context(CommonMetaSnafu)?;
459 let table_ids = table_values
460 .into_iter()
461 .filter_map(|v| v.map(|vi| vi.table_id()))
462 .collect::<Vec<_>>();
463
464 if table_ids.is_empty() {
466 return Ok(false);
467 }
468
469 let table_infos = self
471 .table_metadata_manager()
472 .table_info_manager()
473 .batch_get(&table_ids)
474 .await
475 .context(CommonMetaSnafu)?;
476 let options = table_infos
477 .values()
478 .map(|info| {
479 info.table_info
480 .meta
481 .options
482 .extra_options
483 .get(OTLP_METRIC_COMPAT_KEY)
484 .unwrap_or(&OTLP_LEGACY_DEFAULT_VALUE)
485 })
486 .collect::<Vec<_>>();
487 if !options.is_empty() {
488 let has_prom = options.iter().any(|opt| *opt == OTLP_METRIC_COMPAT_PROM);
490 let has_legacy = options
491 .iter()
492 .any(|opt| *opt == OTLP_LEGACY_DEFAULT_VALUE.as_str());
493 ensure!(!(has_prom && has_legacy), OtlpMetricModeIncompatibleSnafu);
494 Ok(has_legacy)
495 } else {
496 Ok(false)
498 }
499 }
500
501 fn cache_otlp_legacy(
502 &self,
503 names: &[String],
504 ctx: &QueryContextRef,
505 is_legacy: bool,
506 ) -> server_error::Result<()> {
507 let cache = self
508 .otlp_metrics_table_legacy_cache
509 .entry(ctx.get_db_string())
510 .or_default();
511 cache_legacy_mode(&cache, names, is_legacy)
512 }
513}
514
515fn fast_legacy_check(
516 cache: &DashMap<String, bool>,
517 names: &[String],
518) -> server_error::Result<Option<bool>> {
519 let hit_cache = names
520 .iter()
521 .filter_map(|name| cache.get(name))
522 .collect::<Vec<_>>();
523 if !hit_cache.is_empty() {
524 let hit_legacy = hit_cache.iter().any(|en| *en.value());
525 let hit_prom = hit_cache.iter().any(|en| !*en.value());
526
527 ensure!(!(hit_legacy && hit_prom), OtlpMetricModeIncompatibleSnafu);
531
532 Ok(Some(hit_legacy))
533 } else {
534 Ok(None)
535 }
536}
537
538fn cache_legacy_mode(
539 cache: &DashMap<String, bool>,
540 names: &[String],
541 is_legacy: bool,
542) -> server_error::Result<()> {
543 for name in names {
544 let cached = cache.entry(name.clone()).or_insert(is_legacy);
545 ensure!(*cached == is_legacy, OtlpMetricModeIncompatibleSnafu);
546 }
547 Ok(())
548}
549
550fn derive_timeout(stmt: &Statement, query_ctx: &QueryContextRef) -> Option<Duration> {
553 let query_timeout = query_ctx.query_timeout()?;
554 if query_timeout.is_zero() {
555 return None;
556 }
557 match query_ctx.channel() {
558 Channel::Mysql if stmt.is_readonly() => Some(query_timeout),
559 Channel::Postgres => Some(query_timeout),
560 _ => None,
561 }
562}
563
564fn derive_timeout_for_plan(plan: &LogicalPlan, query_ctx: &QueryContextRef) -> Option<Duration> {
566 let query_timeout = query_ctx.query_timeout()?;
567 if query_timeout.is_zero() {
568 return None;
569 }
570 match query_ctx.channel() {
571 Channel::Mysql if is_readonly_plan(plan) => Some(query_timeout),
572 Channel::Postgres => Some(query_timeout),
573 _ => None,
574 }
575}
576
577fn record_explain_analyze_timeout(
578 recorder: Option<&SlowQueryRecorder>,
579 plan: Option<&Arc<dyn ExecutionPlan>>,
580) {
581 let Some(recorder) = recorder else {
582 return;
583 };
584 let metrics = plan
585 .and_then(|plan| query::analyze_plan_metrics_to_json_value(plan, true).ok())
586 .unwrap_or_else(|| serde_json::json!([]));
587 recorder.force_record_with_payload(serde_json::json!({
588 "timed_out": true,
589 "metrics": metrics,
590 }));
591}
592
593fn attach_timeout(
594 output: Output,
595 mut timeout: Duration,
596 timeout_recorder: Option<SlowQueryRecorder>,
597) -> Result<Output> {
598 if timeout.is_zero() {
599 return StatementTimeoutSnafu.fail();
600 }
601
602 let plan = timeout_recorder
603 .as_ref()
604 .and_then(|_| output.meta.plan.clone());
605 let output = match output.data {
606 OutputData::AffectedRows(_) | OutputData::RecordBatches(_) => output,
607 OutputData::Stream(mut stream) => {
608 let schema = stream.schema();
609 let s = Box::pin(stream! {
610 let mut start = tokio::time::Instant::now();
611 while let Some(item) = tokio::time::timeout(timeout, stream.next()).await.map_err(|_| {
612 record_explain_analyze_timeout(timeout_recorder.as_ref(), plan.as_ref());
613 StreamTimeoutSnafu.build()
614 })? {
615 yield item;
616
617 let now = tokio::time::Instant::now();
618 timeout = timeout.checked_sub(now - start).unwrap_or(Duration::ZERO);
619 start = now;
620 if timeout.is_zero() {
622 record_explain_analyze_timeout(timeout_recorder.as_ref(), plan.as_ref());
623 StreamTimeoutSnafu.fail()?;
624 }
625 }
626 }) as Pin<Box<dyn Stream<Item = _> + Send>>;
627 let stream = RecordBatchStreamWrapper {
628 schema,
629 stream: s,
630 output_ordering: None,
631 metrics: Default::default(),
632 span: Span::current(),
633 };
634 Output::new(OutputData::Stream(Box::pin(stream)), output.meta)
635 }
636 };
637
638 Ok(output)
639}
640
641impl Instance {
642 async fn check_sql_permission(
643 &self,
644 stmt: &Statement,
645 query_ctx: &QueryContextRef,
646 ) -> Result<()> {
647 self.plugins
648 .get::<PermissionCheckerRef>()
649 .as_ref()
650 .check_permission_with_context(
651 query_ctx.current_user(),
652 PermissionReq::SqlStatement(stmt),
653 Some(&query_ctx.current_schema()),
654 )
655 .context(PermissionSnafu)?;
656
657 let targets = match extract_tables_from_statement_checked(stmt) {
658 Some(tables) => PermissionTableTargets::resolved(
659 tables
660 .map(|name| {
661 table_idents_to_full_name(&name, query_ctx).map(
662 |(catalog, schema, table)| {
663 PermissionTableTarget::new(catalog, schema, table)
664 },
665 )
666 })
667 .collect::<std::result::Result<Vec<_>, _>>()
668 .map_err(BoxedError::new)
669 .context(ExternalSnafu)?,
670 ),
671 None => PermissionTableTargets::Unresolved,
672 };
673 let targets = self
674 .resolve_query_permission_targets(targets, query_ctx)
675 .await
676 .map_err(BoxedError::new)
677 .context(ExternalSnafu)?;
678 self.check_table_permission(query_ctx, PermissionReq::SqlStatement(stmt), targets)
679 .context(PermissionSnafu)?;
680 Ok(())
681 }
682
683 #[tracing::instrument(skip_all, name = "SqlQueryHandler::do_analyze_stream_query")]
684 async fn do_analyze_stream_query_inner(
685 &self,
686 query: &str,
687 query_ctx: QueryContextRef,
688 ) -> Result<Output> {
689 ensure!(!self.is_suspended(), error::SuspendedSnafu);
690
691 let query_interceptor_opt = self.plugins.get::<SqlQueryInterceptorRef<Error>>();
692 let query_interceptor = query_interceptor_opt.as_ref();
693 let query = query_interceptor.pre_parsing(query, query_ctx.clone())?;
694 let mut stmts = parse_stmt(query.as_ref(), query_ctx.sql_dialect())
695 .and_then(|stmts| query_interceptor.post_parsing(stmts, query_ctx.clone()))?;
696
697 ensure!(
698 stmts.len() == 1,
699 InvalidSqlSnafu {
700 err_msg: "only single EXPLAIN ANALYZE VERBOSE statement is supported"
701 }
702 );
703 let mut stmt = stmts.remove(0);
704 validate_analyze_stream_statement(&mut stmt)?;
705 query_ctx.set_explain_format(AnalyzeFormat::JSON.to_string());
706
707 self.check_sql_permission(&stmt, &query_ctx).await?;
708 check_permission(self.plugins.clone(), &stmt, &query_ctx)?;
709 let catalog_name = query_ctx.current_catalog().to_string();
710 let schema_name = query_ctx.current_schema();
711 let slow_query_timer = self.statement_slow_query_timer(&stmt, schema_name.clone());
712 let ticket = self.process_manager.register_query(
713 catalog_name,
714 vec![schema_name],
715 stmt.to_string(),
716 query_ctx.conn_info().to_string(),
717 Some(query_ctx.process_id()),
718 slow_query_timer,
719 );
720 let query_fut =
721 self.exec_statement_with_timeout(stmt, query_ctx.clone(), query_interceptor, None);
722 let output = CancellableFuture::new(query_fut, ticket.cancellation_handle.clone())
723 .await
724 .map_err(|_| error::CancelledSnafu.build())??;
725 let Output { meta, data } = output;
726 let data = match data {
727 OutputData::Stream(stream) => OutputData::Stream(Box::pin(
728 CancellableStreamWrapper::new_cancel_on_drop(stream, ticket),
729 )),
730 other => other,
731 };
732 query_interceptor.post_execute(Output { data, meta }, query_ctx)
733 }
734
735 #[tracing::instrument(skip_all, name = "SqlQueryHandler::do_query")]
736 async fn do_query_inner(&self, query: &str, query_ctx: QueryContextRef) -> Vec<Result<Output>> {
737 if self.is_suspended() {
738 return vec![error::SuspendedSnafu {}.fail()];
739 }
740
741 let query_interceptor_opt = self.plugins.get::<SqlQueryInterceptorRef<Error>>();
742 let query_interceptor = query_interceptor_opt.as_ref();
743 let query = match query_interceptor.pre_parsing(query, query_ctx.clone()) {
744 Ok(q) => q,
745 Err(e) => return vec![Err(e)],
746 };
747
748 match parse_stmt(query.as_ref(), query_ctx.sql_dialect())
749 .and_then(|stmts| query_interceptor.post_parsing(stmts, query_ctx.clone()))
750 {
751 Ok(stmts) => {
752 if stmts.is_empty() {
753 return vec![
754 InvalidSqlSnafu {
755 err_msg: "empty statements",
756 }
757 .fail(),
758 ];
759 }
760
761 let mut results = Vec::with_capacity(stmts.len());
762 for stmt in stmts {
763 if let Err(e) = self.check_sql_permission(&stmt, &query_ctx).await {
764 results.push(Err(e));
765 break;
766 }
767
768 match self.query_statement(stmt.clone(), query_ctx.clone()).await {
769 Ok(output) => {
770 let output_result =
771 query_interceptor.post_execute(output, query_ctx.clone());
772 results.push(output_result);
773 }
774 Err(e) => {
775 if e.status_code().should_log_error() {
776 error!(e; "Failed to execute query: {stmt}");
777 } else {
778 debug!("Failed to execute query: {stmt}, {e}");
779 }
780 results.push(Err(e));
781 break;
782 }
783 }
784 }
785 results
786 }
787 Err(e) => {
788 vec![Err(e)]
789 }
790 }
791 }
792
793 async fn exec_plan(&self, plan: LogicalPlan, query_ctx: QueryContextRef) -> Result<Output> {
794 self.query_engine
795 .execute(plan, query_ctx)
796 .await
797 .context(ExecLogicalPlanSnafu)
798 }
799
800 async fn exec_plan_with_timeout(
801 &self,
802 plan: LogicalPlan,
803 query_ctx: QueryContextRef,
804 timeout_recorder: Option<SlowQueryRecorder>,
805 ) -> Result<Output> {
806 let timeout = derive_timeout_for_plan(&plan, &query_ctx);
807 match timeout {
808 Some(timeout) => {
809 let start = tokio::time::Instant::now();
810 let output = tokio::time::timeout(timeout, self.exec_plan(plan, query_ctx))
811 .await
812 .map_err(|_| StatementTimeoutSnafu.build())??;
813 let output = map_query_output(output)?;
814 let remaining_timeout = timeout.checked_sub(start.elapsed()).unwrap_or_default();
815 attach_timeout(output, remaining_timeout, timeout_recorder)
816 }
817 None => self
818 .exec_plan(plan, query_ctx)
819 .await
820 .and_then(map_query_output),
821 }
822 }
823
824 async fn do_exec_plan_inner(
825 &self,
826 plan: LogicalPlan,
827 stmt: Option<Statement>,
828 query_ctx: QueryContextRef,
829 ) -> Result<Output> {
830 ensure!(!self.is_suspended(), error::SuspendedSnafu);
831
832 let query_interceptor_opt = self.plugins.get::<SqlQueryInterceptorRef<Error>>();
833 let query_interceptor = query_interceptor_opt.as_ref();
834
835 query_interceptor.pre_execute(stmt.as_ref(), Some(&plan), query_ctx.clone())?;
836
837 let query = stmt
838 .as_ref()
839 .map(|s| s.to_string())
840 .unwrap_or_else(|| plan.display_indent().to_string());
841
842 let plan_is_readonly = is_readonly_plan(&plan);
843 let result = if should_track_plan_process(stmt.as_ref(), &plan) {
844 let catalog_name = query_ctx.current_catalog().to_string();
845 let schema_name = query_ctx.current_schema();
846 let slow_query_timer = if plan_is_readonly {
847 self.slow_query_options.enable.then(|| {
848 SlowQueryTimer::new(
849 CatalogQueryStatement::Plan(query.clone()),
850 schema_name.clone(),
851 self.slow_query_options.threshold,
852 self.slow_query_options.sample_ratio,
853 self.slow_query_options.record_type,
854 self.slow_query_recorder.clone(),
855 )
856 })
857 } else {
858 None
859 };
860
861 let timeout_recorder = stmt
862 .as_ref()
863 .is_some_and(is_explain_analyze_verbose)
864 .then(|| slow_query_timer.as_ref().map(SlowQueryTimer::recorder))
865 .flatten();
866 let ticket = self.process_manager.register_query(
867 catalog_name,
868 vec![schema_name],
869 query,
870 query_ctx.conn_info().to_string(),
871 Some(query_ctx.process_id()),
872 slow_query_timer,
873 );
874
875 let query_fut = self.exec_plan_with_timeout(plan, query_ctx.clone(), timeout_recorder);
876
877 CancellableFuture::new(query_fut, ticket.cancellation_handle.clone())
878 .await
879 .map_err(|_| error::CancelledSnafu.build())?
880 .map(|output| {
881 let Output { meta, data } = output;
882
883 let data = match data {
884 OutputData::Stream(stream) => OutputData::Stream(Box::pin(
885 CancellableStreamWrapper::new(stream, ticket),
886 )),
887 other => other,
888 };
889 Output { data, meta }
890 })
891 } else {
892 self.exec_plan_with_timeout(plan, query_ctx.clone(), None)
893 .await
894 };
895
896 result.and_then(|output| query_interceptor.post_execute(output, query_ctx))
897 }
898
899 #[tracing::instrument(skip_all, name = "SqlQueryHandler::do_promql_query")]
900 async fn do_promql_query_inner(
901 &self,
902 query: &PromQuery,
903 query_ctx: QueryContextRef,
904 ) -> Vec<Result<Output>> {
905 if self.is_suspended() {
906 return vec![error::SuspendedSnafu {}.fail()];
907 }
908
909 let result = PrometheusHandler::do_query(self, query, query_ctx)
911 .await
912 .with_context(|_| ExecutePromqlSnafu {
913 query: format!("{query:?}"),
914 });
915 vec![result]
916 }
917
918 async fn do_describe_inner(
919 &self,
920 stmt: Statement,
921 query_ctx: QueryContextRef,
922 ) -> Result<Option<DescribeResult>> {
923 ensure!(!self.is_suspended(), error::SuspendedSnafu);
924
925 let is_inner_plannable = |s: &Statement| {
929 matches!(
930 s,
931 Statement::Insert(_) | Statement::Query(_) | Statement::Delete(_)
932 )
933 };
934 let plannable = is_inner_plannable(&stmt)
935 || matches!(&stmt, Statement::Explain(explain) if is_inner_plannable(explain.statement.as_ref()));
936
937 if plannable {
938 self.check_sql_permission(&stmt, &query_ctx).await?;
939
940 let plan = self
941 .query_engine
942 .planner()
943 .plan(&QueryStatement::Sql(stmt), query_ctx.clone())
944 .await
945 .context(PlanStatementSnafu)?;
946 self.query_engine
947 .describe(plan, query_ctx)
948 .await
949 .map(Some)
950 .context(error::DescribeStatementSnafu)
951 } else {
952 Ok(None)
953 }
954 }
955
956 async fn is_valid_schema_inner(&self, catalog: &str, schema: &str) -> Result<bool> {
957 self.catalog_manager
958 .schema_exists(catalog, schema, None)
959 .await
960 .context(error::CatalogSnafu)
961 }
962}
963
964#[async_trait]
965impl SqlQueryHandler for Instance {
966 async fn do_query(
967 &self,
968 query: &str,
969 query_ctx: QueryContextRef,
970 ) -> Vec<server_error::Result<Output>> {
971 self.do_query_inner(query, query_ctx)
972 .await
973 .into_iter()
974 .map(|result| result.map_err(BoxedError::new).context(ExecuteQuerySnafu))
975 .collect()
976 }
977
978 async fn do_analyze_stream_query(
979 &self,
980 query: &str,
981 query_ctx: QueryContextRef,
982 ) -> server_error::Result<Output> {
983 self.do_analyze_stream_query_inner(query, query_ctx)
984 .await
985 .map_err(BoxedError::new)
986 .context(ExecuteQuerySnafu)
987 }
988
989 async fn do_exec_plan(
990 &self,
991 plan: LogicalPlan,
992 stmt: Option<Statement>,
993 query_ctx: QueryContextRef,
994 ) -> server_error::Result<Output> {
995 self.do_exec_plan_inner(plan, stmt, query_ctx)
996 .await
997 .map_err(BoxedError::new)
998 .context(server_error::ExecutePlanSnafu)
999 }
1000
1001 async fn do_promql_query(
1002 &self,
1003 query: &PromQuery,
1004 query_ctx: QueryContextRef,
1005 ) -> Vec<server_error::Result<Output>> {
1006 self.do_promql_query_inner(query, query_ctx)
1007 .await
1008 .into_iter()
1009 .map(|result| result.map_err(BoxedError::new).context(ExecuteQuerySnafu))
1010 .collect()
1011 }
1012
1013 async fn do_describe(
1014 &self,
1015 stmt: Statement,
1016 query_ctx: QueryContextRef,
1017 ) -> server_error::Result<Option<DescribeResult>> {
1018 self.do_describe_inner(stmt, query_ctx)
1019 .await
1020 .map_err(BoxedError::new)
1021 .context(server_error::DescribeStatementSnafu)
1022 }
1023
1024 async fn is_valid_schema(&self, catalog: &str, schema: &str) -> server_error::Result<bool> {
1025 self.is_valid_schema_inner(catalog, schema)
1026 .await
1027 .map_err(BoxedError::new)
1028 .context(server_error::CheckDatabaseValiditySnafu)
1029 }
1030}
1031
1032pub(crate) fn map_query_output(output: Output) -> Result<Output> {
1034 output
1035 .map_dictionary_to_values()
1036 .context(CollectRecordbatchSnafu)
1037}
1038
1039pub fn attach_timer(output: Output, timer: HistogramTimer) -> Output {
1041 match output.data {
1042 OutputData::AffectedRows(_) | OutputData::RecordBatches(_) => output,
1043 OutputData::Stream(stream) => {
1044 let stream = OnDone::new(stream, move || {
1045 timer.observe_duration();
1046 });
1047 Output::new(OutputData::Stream(Box::pin(stream)), output.meta)
1048 }
1049 }
1050}
1051
1052impl Instance {
1053 fn check_prom_query_privilege(&self, query_ctx: &QueryContextRef) -> server_error::Result<()> {
1054 self.plugins
1055 .get::<PermissionCheckerRef>()
1056 .as_ref()
1057 .check_permission(query_ctx.current_user(), PermissionReq::PromQuery)
1058 .context(AuthSnafu)?;
1059 Ok(())
1060 }
1061
1062 fn prom_expr_permission_targets(
1063 &self,
1064 expr: &promql_parser::parser::Expr,
1065 query_ctx: &QueryContextRef,
1066 ) -> server_error::Result<Option<Vec<PermissionTableTarget>>> {
1067 extract_tables_from_prom_expr_checked(expr)
1068 .map(|tables| {
1069 tables
1070 .map(|name| {
1071 table_idents_to_full_name(&name, query_ctx).map(
1072 |(catalog, schema, table)| {
1073 PermissionTableTarget::new(catalog, schema, table)
1074 },
1075 )
1076 })
1077 .collect::<std::result::Result<Vec<_>, _>>()
1078 .map_err(BoxedError::new)
1079 .context(ExecuteQuerySnafu)
1080 })
1081 .transpose()
1082 }
1083
1084 async fn is_physical_query_permission_target(
1085 &self,
1086 target: &PermissionTableTarget,
1087 query_ctx: &QueryContextRef,
1088 ) -> server_error::Result<bool> {
1089 self.catalog_manager
1090 .table(
1091 &target.catalog,
1092 &target.schema,
1093 &target.table,
1094 Some(query_ctx),
1095 )
1096 .await
1097 .map(|table| table.is_some_and(|table| table.table_info().is_physical_table()))
1098 .map_err(BoxedError::new)
1099 .context(ExecuteQuerySnafu)
1100 }
1101
1102 async fn resolve_query_permission_targets(
1103 &self,
1104 targets: PermissionTableTargets,
1105 query_ctx: &QueryContextRef,
1106 ) -> server_error::Result<PermissionTableTargets> {
1107 const CONCURRENCY: usize = 8;
1108
1109 let checker = self.plugins.get::<PermissionCheckerRef>();
1110 if !checker.as_ref().uses_table_targets() {
1111 return Ok(targets);
1112 }
1113
1114 let PermissionTableTargets::Resolved(mut targets) = targets else {
1115 return Ok(PermissionTableTargets::Unresolved);
1116 };
1117 if targets.len() > 1 {
1118 let mut seen = HashSet::with_capacity(targets.len());
1119 targets.retain(|target| seen.insert(target.clone()));
1120 }
1121 if let [target] = targets.as_slice() {
1122 return if self
1123 .is_physical_query_permission_target(target, query_ctx)
1124 .await?
1125 {
1126 Ok(PermissionTableTargets::Unresolved)
1127 } else {
1128 Ok(PermissionTableTargets::resolved(targets))
1129 };
1130 }
1131
1132 for chunk in targets.chunks(CONCURRENCY) {
1134 let results = future::join_all(
1135 chunk
1136 .iter()
1137 .map(|target| self.is_physical_query_permission_target(target, query_ctx)),
1138 )
1139 .await;
1140 for result in results {
1141 if result? {
1142 return Ok(PermissionTableTargets::Unresolved);
1143 }
1144 }
1145 }
1146
1147 Ok(PermissionTableTargets::resolved(targets))
1148 }
1149
1150 fn prom_queries_permission_targets(
1151 &self,
1152 queries: &[ParsedPromQuery],
1153 query_ctx: &QueryContextRef,
1154 ) -> server_error::Result<PermissionTableTargets> {
1155 let mut targets = Vec::new();
1156 let mut resolved = true;
1157
1158 for query in queries {
1159 let QueryStatement::Promql(eval_stmt, _) = query.statement() else {
1160 unreachable!("query is parsed from promql");
1161 };
1162
1163 if let Some(query_targets) =
1164 self.prom_expr_permission_targets(&eval_stmt.expr, query_ctx)?
1165 {
1166 targets.extend(query_targets);
1167 } else {
1168 resolved = false;
1169 }
1170 }
1171
1172 Ok(if resolved {
1173 PermissionTableTargets::resolved(targets)
1174 } else {
1175 PermissionTableTargets::Unresolved
1176 })
1177 }
1178}
1179
1180#[async_trait]
1181impl PrometheusHandler for Instance {
1182 #[tracing::instrument(skip_all)]
1183 async fn do_query(
1184 &self,
1185 query: &PromQuery,
1186 query_ctx: QueryContextRef,
1187 ) -> server_error::Result<Output> {
1188 let query = ParsedPromQuery::parse(query.clone(), &query_ctx)?;
1189 self.do_query_parsed(query, query_ctx).await
1190 }
1191
1192 #[tracing::instrument(skip_all)]
1193 async fn do_query_parsed(
1194 &self,
1195 query: ParsedPromQuery,
1196 query_ctx: QueryContextRef,
1197 ) -> server_error::Result<Output> {
1198 let interceptor = self
1199 .plugins
1200 .get::<PromQueryInterceptorRef<server_error::Error>>();
1201
1202 self.check_prom_query_privilege(&query_ctx)?;
1203
1204 let targets =
1205 self.prom_queries_permission_targets(std::slice::from_ref(&query), &query_ctx)?;
1206 self.check_query_target_permission(targets, &query_ctx)
1207 .await?;
1208
1209 let (query, stmt) = query.into_parts();
1210
1211 let QueryStatement::Promql(eval_stmt, _) = &stmt else {
1212 unreachable!("query is parsed from promql");
1213 };
1214
1215 let plan = self
1216 .statement_executor
1217 .plan(&stmt, query_ctx.clone())
1218 .await
1219 .map_err(BoxedError::new)
1220 .context(ExecuteQuerySnafu)?;
1221
1222 interceptor.pre_execute(&query, &eval_stmt.expr, Some(&plan), query_ctx.clone())?;
1223
1224 let query_statement = if let QueryStatement::Promql(eval_stmt, alias) = stmt {
1226 CatalogQueryStatement::Promql(eval_stmt, alias)
1227 } else {
1228 return UnexpectedResultSnafu {
1230 reason: "The query should always be promql.".to_string(),
1231 }
1232 .fail();
1233 };
1234 let raw_query = query_statement.to_string();
1235
1236 let slow_query_timer = self.slow_query_options.enable.then(|| {
1237 SlowQueryTimer::new(
1238 query_statement,
1239 query_ctx.current_schema(),
1240 self.slow_query_options.threshold,
1241 self.slow_query_options.sample_ratio,
1242 self.slow_query_options.record_type,
1243 self.slow_query_recorder.clone(),
1244 )
1245 });
1246
1247 let ticket = self.process_manager.register_query(
1248 query_ctx.current_catalog().to_string(),
1249 vec![query_ctx.current_schema()],
1250 raw_query,
1251 query_ctx.conn_info().to_string(),
1252 Some(query_ctx.process_id()),
1253 slow_query_timer,
1254 );
1255
1256 let query_fut = self.statement_executor.exec_plan(plan, query_ctx.clone());
1257
1258 let output = CancellableFuture::new(query_fut, ticket.cancellation_handle.clone())
1259 .await
1260 .map_err(|_| servers::error::CancelledSnafu.build())?
1261 .map_err(BoxedError::new)
1262 .context(ExecuteQuerySnafu)?;
1263 let output = map_query_output(output)
1264 .map_err(BoxedError::new)
1265 .context(ExecuteQuerySnafu)?;
1266 let Output { meta, data } = output;
1267 let data = match data {
1268 OutputData::Stream(stream) => {
1269 OutputData::Stream(Box::pin(CancellableStreamWrapper::new(stream, ticket)))
1270 }
1271 other => other,
1272 };
1273 let output = Output { data, meta };
1274 Ok(interceptor.post_execute(output, query_ctx)?)
1275 }
1276
1277 async fn check_query_permission(
1278 &self,
1279 queries: &[PromQuery],
1280 query_ctx: &QueryContextRef,
1281 ) -> server_error::Result<()> {
1282 let queries = queries
1283 .iter()
1284 .cloned()
1285 .map(|query| ParsedPromQuery::parse(query, query_ctx))
1286 .collect::<server_error::Result<Vec<_>>>()?;
1287 self.check_query_permission_parsed(&queries, query_ctx)
1288 .await
1289 }
1290
1291 async fn check_query_permission_parsed(
1292 &self,
1293 queries: &[ParsedPromQuery],
1294 query_ctx: &QueryContextRef,
1295 ) -> server_error::Result<()> {
1296 self.check_prom_query_privilege(query_ctx)?;
1297 let targets = self.prom_queries_permission_targets(queries, query_ctx)?;
1298 self.check_query_target_permission(targets, query_ctx).await
1299 }
1300
1301 async fn check_query_target_permission(
1302 &self,
1303 targets: PermissionTableTargets,
1304 query_ctx: &QueryContextRef,
1305 ) -> server_error::Result<()> {
1306 let targets = self
1307 .resolve_query_permission_targets(targets, query_ctx)
1308 .await?;
1309 self.check_table_permission(query_ctx, PermissionReq::PromQuery, targets)
1310 .context(AuthSnafu)?;
1311 Ok(())
1312 }
1313
1314 async fn filter_metadata_metric_names(
1315 &self,
1316 metric_names: Vec<String>,
1317 schema: &str,
1318 query_ctx: &QueryContextRef,
1319 ) -> server_error::Result<Vec<String>> {
1320 let checker = self.plugins.get::<PermissionCheckerRef>();
1321 if !checker.as_ref().uses_table_targets() {
1322 let Some(metric) = metric_names.first() else {
1323 return Ok(metric_names);
1324 };
1325 let target =
1326 PermissionTableTarget::new(query_ctx.current_catalog(), schema, metric.as_str());
1327 let result = checker
1328 .as_ref()
1329 .check_permission_with_table_targets(
1330 query_ctx.current_user(),
1331 PermissionReq::PromQuery,
1332 PermissionTableTargets::resolved(vec![target]),
1333 )
1334 .context(AuthSnafu);
1335 return match result {
1336 Ok(_) => Ok(metric_names),
1337 Err(error)
1338 if error.status_code()
1339 == common_error::status_code::StatusCode::PermissionDenied =>
1340 {
1341 Ok(Vec::new())
1342 }
1343 Err(error) => Err(error),
1344 };
1345 }
1346
1347 let mut allowed = Vec::with_capacity(metric_names.len());
1348 for metric in metric_names {
1349 let target =
1350 PermissionTableTarget::new(query_ctx.current_catalog(), schema, metric.as_str());
1351 match checker
1352 .as_ref()
1353 .check_permission_with_table_targets(
1354 query_ctx.current_user(),
1355 PermissionReq::PromQuery,
1356 PermissionTableTargets::resolved(vec![target]),
1357 )
1358 .context(AuthSnafu)
1359 {
1360 Ok(_) => allowed.push(metric),
1361 Err(error)
1362 if error.status_code()
1363 == common_error::status_code::StatusCode::PermissionDenied => {}
1364 Err(error) => return Err(error),
1365 }
1366 }
1367 Ok(allowed)
1368 }
1369
1370 async fn query_metric_names(
1371 &self,
1372 matchers: Vec<Matcher>,
1373 schema: &str,
1374 ctx: &QueryContextRef,
1375 ) -> server_error::Result<Vec<String>> {
1376 self.handle_query_metric_names(matchers, schema, ctx)
1377 .await
1378 .map_err(BoxedError::new)
1379 .context(ExecuteQuerySnafu)
1380 }
1381
1382 async fn query_label_values(
1383 &self,
1384 metric: String,
1385 label_name: String,
1386 matchers: Vec<Matcher>,
1387 start: SystemTime,
1388 end: SystemTime,
1389 ctx: &QueryContextRef,
1390 ) -> server_error::Result<Vec<String>> {
1391 let schema =
1392 resolve_schema_from_matchers(&matchers)?.unwrap_or_else(|| ctx.current_schema());
1393 let target = PermissionTableTarget::new(ctx.current_catalog(), schema.as_str(), &metric);
1394 self.check_query_target_permission(
1395 PermissionTableTargets::resolved(vec![target.clone()]),
1396 ctx,
1397 )
1398 .await?;
1399
1400 self.handle_query_label_values(target, label_name, matchers, start, end, ctx)
1401 .await
1402 .map_err(BoxedError::new)
1403 .context(ExecuteQuerySnafu)
1404 }
1405
1406 fn catalog_manager(&self) -> CatalogManagerRef {
1407 self.catalog_manager.clone()
1408 }
1409}
1410
1411macro_rules! validate_db_permission {
1413 ($stmt: expr, $query_ctx: expr) => {
1414 if let Some(database) = &$stmt.database {
1415 validate_catalog_and_schema($query_ctx.current_catalog(), database, $query_ctx)
1416 .map_err(BoxedError::new)
1417 .context(SqlExecInterceptedSnafu)?;
1418 }
1419 };
1420}
1421
1422pub fn check_permission(
1423 plugins: Plugins,
1424 stmt: &Statement,
1425 query_ctx: &QueryContextRef,
1426) -> Result<()> {
1427 let need_validate = plugins
1428 .get::<QueryOptions>()
1429 .map(|opts| opts.disallow_cross_catalog_query)
1430 .unwrap_or_default();
1431
1432 if !need_validate {
1433 return Ok(());
1434 }
1435
1436 match stmt {
1437 Statement::Admin(_) => {}
1440 Statement::Query(_)
1442 | Statement::Explain(_)
1443 | Statement::Tql(_)
1444 | Statement::Delete(_)
1445 | Statement::DeclareCursor(_)
1446 | Statement::Copy(sql::statements::copy::Copy::CopyQueryTo(_)) => {}
1447 Statement::CreateDatabase(_)
1449 | Statement::ShowDatabases(_)
1450 | Statement::DropDatabase(_)
1451 | Statement::AlterDatabase(_)
1452 | Statement::DropFlow(_)
1453 | Statement::Use(_) => {}
1454 #[cfg(feature = "enterprise")]
1455 Statement::DropTrigger(_) => {}
1456 Statement::ShowCreateDatabase(stmt) => {
1457 validate_database(&stmt.database_name, query_ctx)?;
1458 }
1459 Statement::ShowCreateTable(stmt) => {
1460 validate_param(&stmt.table_name, query_ctx)?;
1461 }
1462 Statement::ShowCreateFlow(stmt) => {
1463 validate_flow(&stmt.flow_name, query_ctx)?;
1464 }
1465 #[cfg(feature = "enterprise")]
1466 Statement::ShowCreateTrigger(stmt) => {
1467 validate_param(&stmt.trigger_name, query_ctx)?;
1468 }
1469 Statement::ShowCreateView(stmt) => {
1470 validate_param(&stmt.view_name, query_ctx)?;
1471 }
1472 Statement::CreateExternalTable(stmt) => {
1473 validate_param(&stmt.name, query_ctx)?;
1474 }
1475 Statement::CreateFlow(stmt) => {
1476 validate_param(&stmt.sink_table_name, query_ctx)?;
1478 }
1479 #[cfg(feature = "enterprise")]
1480 Statement::CreateTrigger(stmt) => {
1481 validate_param(&stmt.trigger_name, query_ctx)?;
1482 }
1483 Statement::CreateView(stmt) => {
1484 validate_param(&stmt.name, query_ctx)?;
1485 }
1486 Statement::AlterTable(stmt) => {
1487 validate_param(stmt.table_name(), query_ctx)?;
1488 }
1489 #[cfg(feature = "enterprise")]
1490 Statement::AlterTrigger(_) => {}
1491 Statement::SetVariables(_) | Statement::ShowVariables(_) => {}
1493 Statement::ShowCharset(_) | Statement::ShowCollation(_) => {}
1495
1496 Statement::Comment(comment) => match &comment.object {
1497 CommentObject::Table(table) => validate_param(table, query_ctx)?,
1498 CommentObject::Column { table, .. } => validate_param(table, query_ctx)?,
1499 CommentObject::Flow(flow) => validate_flow(flow, query_ctx)?,
1500 },
1501
1502 Statement::Insert(insert) => {
1503 let name = insert.table_name().context(ParseSqlSnafu)?;
1504 validate_param(name, query_ctx)?;
1505 }
1506 Statement::CreateTable(stmt) => {
1507 validate_param(&stmt.name, query_ctx)?;
1508 }
1509 Statement::CreateTableLike(stmt) => {
1510 validate_param(&stmt.table_name, query_ctx)?;
1511 validate_param(&stmt.source_name, query_ctx)?;
1512 }
1513 Statement::DropTable(drop_stmt) => {
1514 for table_name in drop_stmt.table_names() {
1515 validate_param(table_name, query_ctx)?;
1516 }
1517 }
1518 Statement::UndropTable(stmt) => {
1519 validate_param(stmt.table_name(), query_ctx)?;
1520 }
1521 Statement::DropView(stmt) => {
1522 validate_param(&stmt.view_name, query_ctx)?;
1523 }
1524 Statement::ShowTables(stmt) => {
1525 validate_db_permission!(stmt, query_ctx);
1526 }
1527 Statement::ShowTableStatus(stmt) => {
1528 validate_db_permission!(stmt, query_ctx);
1529 }
1530 Statement::ShowColumns(stmt) => {
1531 validate_db_permission!(stmt, query_ctx);
1532 }
1533 Statement::ShowIndex(stmt) => {
1534 validate_db_permission!(stmt, query_ctx);
1535 }
1536 Statement::ShowRegion(stmt) => {
1537 validate_db_permission!(stmt, query_ctx);
1538 }
1539 Statement::ShowViews(stmt) => {
1540 validate_db_permission!(stmt, query_ctx);
1541 }
1542 Statement::ShowFlows(stmt) => {
1543 validate_db_permission!(stmt, query_ctx);
1544 }
1545 #[cfg(feature = "enterprise")]
1546 Statement::ShowTriggers(_stmt) => {
1547 }
1550 Statement::ShowStatus(_stmt) => {}
1551 Statement::ShowSearchPath(_stmt) => {}
1552 Statement::DescribeTable(stmt) => {
1553 validate_param(stmt.name(), query_ctx)?;
1554 }
1555 Statement::Copy(sql::statements::copy::Copy::CopyTable(stmt)) => match stmt {
1556 CopyTable::To(copy_table_to) => validate_param(©_table_to.table_name, query_ctx)?,
1557 CopyTable::From(copy_table_from) => {
1558 validate_param(©_table_from.table_name, query_ctx)?
1559 }
1560 },
1561 Statement::Copy(sql::statements::copy::Copy::CopyDatabase(copy_database)) => {
1562 match copy_database {
1563 CopyDatabase::To(stmt) => validate_database(&stmt.database_name, query_ctx)?,
1564 CopyDatabase::From(stmt) => validate_database(&stmt.database_name, query_ctx)?,
1565 }
1566 }
1567 Statement::TruncateTable(stmt) => {
1568 validate_param(stmt.table_name(), query_ctx)?;
1569 }
1570 Statement::FetchCursor(_) | Statement::CloseCursor(_) => {}
1572 Statement::Kill(_) => {}
1574 Statement::ShowProcesslist(_) => {}
1576 }
1577 Ok(())
1578}
1579
1580fn validate_param(name: &ObjectName, query_ctx: &QueryContextRef) -> Result<()> {
1581 let (catalog, schema, _) = table_idents_to_full_name(name, query_ctx)
1582 .map_err(BoxedError::new)
1583 .context(ExternalSnafu)?;
1584
1585 validate_catalog_and_schema(&catalog, &schema, query_ctx)
1586 .map_err(BoxedError::new)
1587 .context(SqlExecInterceptedSnafu)
1588}
1589
1590fn validate_flow(name: &ObjectName, query_ctx: &QueryContextRef) -> Result<()> {
1591 let catalog = match &name.0[..] {
1592 [_flow] => query_ctx.current_catalog().to_string(),
1593 [catalog, _flow] => catalog.to_string_unquoted(),
1594 _ => {
1595 return InvalidSqlSnafu {
1596 err_msg: format!(
1597 "expect flow name to be <catalog>.<flow_name> or <flow_name>, actual: {name}",
1598 ),
1599 }
1600 .fail();
1601 }
1602 };
1603
1604 let schema = query_ctx.current_schema();
1605
1606 validate_catalog_and_schema(&catalog, &schema, query_ctx)
1607 .map_err(BoxedError::new)
1608 .context(SqlExecInterceptedSnafu)
1609}
1610
1611fn validate_database(name: &ObjectName, query_ctx: &QueryContextRef) -> Result<()> {
1612 let (catalog, schema) = match &name.0[..] {
1613 [schema] => (
1614 query_ctx.current_catalog().to_string(),
1615 schema.to_string_unquoted(),
1616 ),
1617 [catalog, schema] => (catalog.to_string_unquoted(), schema.to_string_unquoted()),
1618 _ => InvalidSqlSnafu {
1619 err_msg: format!(
1620 "expect database name to be <catalog>.<schema> or <schema>, actual: {name}",
1621 ),
1622 }
1623 .fail()?,
1624 };
1625
1626 validate_catalog_and_schema(&catalog, &schema, query_ctx)
1627 .map_err(BoxedError::new)
1628 .context(SqlExecInterceptedSnafu)
1629}
1630
1631fn is_readonly_plan(plan: &LogicalPlan) -> bool {
1632 !matches!(plan, LogicalPlan::Dml(_) | LogicalPlan::Ddl(_))
1633}
1634
1635fn should_track_statement_process(stmt: &Statement) -> bool {
1636 stmt.is_readonly()
1637 || matches!(stmt, Statement::Insert(insert) if insert.has_non_values_query_source())
1638}
1639
1640fn should_track_plan_process(stmt: Option<&Statement>, plan: &LogicalPlan) -> bool {
1641 is_readonly_plan(plan)
1642 || matches!(stmt, Some(Statement::Insert(insert)) if insert.has_non_values_query_source())
1643}
1644
1645#[cfg(test)]
1646mod tests {
1647 use std::collections::HashMap;
1648 use std::future::Future;
1649 use std::pin::Pin;
1650 use std::sync::Arc;
1651 use std::task::{Context, Poll};
1652 use std::time::Duration;
1653
1654 use api::prom_store::remote::label_matcher::Type as PromMatcherType;
1655 use api::prom_store::remote::{LabelMatcher, Query as RemoteQuery, ReadRequest};
1656 use api::v1::meta::{ProcedureDetailResponse, ReconcileRequest, ReconcileResponse};
1657 use auth::{PermissionResp, UserInfoRef};
1658 use catalog::process_manager::{ProcessManager, QueryStatement, SlowQueryTimer};
1659 use common_base::Plugins;
1660 use common_error::ext::{BoxedError, PlainError};
1661 use common_error::status_code::StatusCode;
1662 use common_event_recorder::{Event, EventRecorder, EventTypeFilter, EventTypeFilterRef};
1663 use common_frontend::slow_query_event::SlowQueryEvent;
1664 use common_meta::cache::LayeredCacheRegistryBuilder;
1665 use common_meta::kv_backend::memory::MemoryKvBackend;
1666 use common_meta::procedure_executor::{ExecutorContext, ProcedureExecutor};
1667 use common_meta::rpc::ddl::{SubmitDdlTaskRequest, SubmitDdlTaskResponse};
1668 use common_meta::rpc::procedure::{
1669 MigrateRegionRequest, MigrateRegionResponse, ProcedureStateResponse,
1670 };
1671 use common_query::{Output, OutputMeta};
1672 use common_recordbatch::{
1673 OrderOption, RecordBatch, RecordBatchStream, SendableRecordBatchStream,
1674 };
1675 use common_telemetry::logging::SlowQueriesRecordType;
1676 use datafusion::arrow::datatypes::{DataType, Field, Schema, SchemaRef};
1677 use datafusion::physical_plan::empty::EmptyExec;
1678 use datafusion_expr::dml::InsertOp;
1679 use datafusion_expr::{LogicalPlanBuilder, LogicalTableSource};
1680 use datatypes::prelude::ConcreteDataType;
1681 use datatypes::schema::{ColumnSchema, Schema as GtSchema, SchemaRef as GtSchemaRef};
1682 use datatypes::vectors::{StringVector, VectorRef};
1683 use log_query::LogQuery;
1684 use query::query_engine::options::QueryOptions;
1685 use servers::query_handler::{LogQueryHandler, PromStoreProtocolHandler};
1686 use session::context::{Channel, ConnInfo, QueryContext, QueryContextBuilder};
1687 use snafu::{Location, Snafu};
1688 use sql::dialect::GreptimeDbDialect;
1689 use store_api::data_source::DataSource;
1690 use store_api::metric_engine_consts::{
1691 LOGICAL_TABLE_METADATA_KEY, METRIC_ENGINE_NAME, PHYSICAL_TABLE_METADATA_KEY,
1692 };
1693 use store_api::storage::ScanRequest;
1694 use strfmt::Format;
1695 use table::metadata::{FilterPushDownType, TableInfo, TableInfoBuilder, TableMetaBuilder};
1696 use table::table_name::TableName;
1697 use table::test_util::{EmptyTable, MemTable};
1698 use table::{Table, TableRef};
1699 use tokio::sync::{mpsc, oneshot};
1700
1701 use super::*;
1702 use crate::frontend::FrontendOptions;
1703 use crate::instance::builder::FrontendBuilder;
1704
1705 fn parse_test_sql(sql: &str) -> Vec<Statement> {
1706 parse_stmt(sql, &GreptimeDbDialect {}).unwrap()
1707 }
1708
1709 #[derive(Debug, Default)]
1710 struct RecordingSlowQueryEventRecorder {
1711 payloads: std::sync::Mutex<Vec<serde_json::Value>>,
1712 }
1713
1714 impl EventRecorder for RecordingSlowQueryEventRecorder {
1715 fn record(&self, event: Box<dyn Event>) {
1716 let event = event
1717 .as_any()
1718 .downcast_ref::<SlowQueryEvent>()
1719 .expect("expected a slow query event");
1720 self.payloads.lock().unwrap().push(event.payload.clone());
1721 }
1722
1723 fn event_type_filter(&self) -> EventTypeFilterRef {
1724 Arc::new(EventTypeFilter::All)
1725 }
1726
1727 fn close(&self) {}
1728 }
1729
1730 #[test]
1731 fn test_validate_analyze_stream_statement_strictness() {
1732 for sql in [
1733 "select 1",
1734 "explain analyze select 1",
1735 "explain analyze verbose format text select 1",
1736 "explain analyze verbose format graphviz select 1",
1737 ] {
1738 let mut stmts = parse_test_sql(sql);
1739 assert!(
1740 validate_analyze_stream_statement(&mut stmts[0]).is_err(),
1741 "{sql}"
1742 );
1743 }
1744
1745 for sql in [
1746 "explain analyze verbose select 1",
1747 "explain analyze verbose format json select 1",
1748 ] {
1749 let mut stmts = parse_test_sql(sql);
1750 assert!(
1751 validate_analyze_stream_statement(&mut stmts[0]).is_ok(),
1752 "{sql}"
1753 );
1754 let Statement::Explain(explain) = &stmts[0] else {
1755 unreachable!();
1756 };
1757 assert!(explain.format.is_none());
1758 }
1759
1760 assert_eq!(
1761 parse_test_sql("explain analyze verbose select 1; select 2").len(),
1762 2
1763 );
1764
1765 assert!(is_explain_analyze_verbose(
1766 &parse_test_sql("explain analyze verbose select 1")[0]
1767 ));
1768 for sql in [
1769 "select 1",
1770 "explain select 1",
1771 "explain analyze select 1",
1772 "explain verbose select 1",
1773 ] {
1774 assert!(
1775 !is_explain_analyze_verbose(&parse_test_sql(sql)[0]),
1776 "{sql}"
1777 );
1778 }
1779 }
1780
1781 #[derive(Debug, Snafu)]
1782 enum TestError {
1783 #[snafu(display("Failed to build test cache registry"))]
1784 BuildCacheRegistry {
1785 source: cache::error::Error,
1786 #[snafu(implicit)]
1787 location: Location,
1788 },
1789
1790 #[snafu(display("Failed to build test table meta for table: {table_name}"))]
1791 BuildTableMeta {
1792 table_name: String,
1793 source: table::metadata::TableMetaBuilderError,
1794 #[snafu(implicit)]
1795 location: Location,
1796 },
1797
1798 #[snafu(display("Failed to build test table info for table: {table_name}"))]
1799 BuildTableInfo {
1800 table_name: String,
1801 source: table::metadata::TableInfoBuilderError,
1802 #[snafu(implicit)]
1803 location: Location,
1804 },
1805
1806 #[snafu(display("Failed to register test table: {table_name}"))]
1807 RegisterTable {
1808 table_name: String,
1809 source: catalog::error::Error,
1810 #[snafu(implicit)]
1811 location: Location,
1812 },
1813
1814 #[snafu(display("Failed to build test frontend instance"))]
1815 BuildFrontend {
1816 source: crate::error::Error,
1817 #[snafu(implicit)]
1818 location: Location,
1819 },
1820
1821 #[snafu(display("Expected exactly one output for SQL `{sql}`, got {actual}"))]
1822 UnexpectedOutputCount {
1823 sql: String,
1824 actual: usize,
1825 #[snafu(implicit)]
1826 location: Location,
1827 },
1828
1829 #[snafu(display("Failed to execute SQL `{sql}`"))]
1830 ExecuteSql {
1831 sql: String,
1832 source: crate::error::Error,
1833 #[snafu(implicit)]
1834 location: Location,
1835 },
1836
1837 #[snafu(display("Timed out waiting for insert-select start notification"))]
1838 InsertStartTimeout {
1839 source: tokio::time::error::Elapsed,
1840 #[snafu(implicit)]
1841 location: Location,
1842 },
1843
1844 #[snafu(display("Insert-select start notification channel closed"))]
1845 InsertStartChannelClosed {
1846 #[snafu(implicit)]
1847 location: Location,
1848 },
1849
1850 #[snafu(display("Failed to release blocking insert-select interceptor"))]
1851 ReleaseBlockedInsert {
1852 #[snafu(implicit)]
1853 location: Location,
1854 },
1855
1856 #[snafu(display("Timed out waiting for insert-select source to be polled"))]
1857 SourcePollTimeout {
1858 source: tokio::time::error::Elapsed,
1859 #[snafu(implicit)]
1860 location: Location,
1861 },
1862
1863 #[snafu(display("Insert-select source poll notification channel closed"))]
1864 SourcePollChannelClosed {
1865 source: oneshot::error::RecvError,
1866 #[snafu(implicit)]
1867 location: Location,
1868 },
1869
1870 #[snafu(display("Timed out waiting for insert task to finish"))]
1871 InsertTaskTimeout {
1872 source: tokio::time::error::Elapsed,
1873 #[snafu(implicit)]
1874 location: Location,
1875 },
1876
1877 #[snafu(display("Insert task panicked"))]
1878 InsertTaskPanic {
1879 source: tokio::task::JoinError,
1880 #[snafu(implicit)]
1881 location: Location,
1882 },
1883
1884 #[snafu(display("Expected insert-select to be cancelled"))]
1885 InsertSelectNotCancelled {
1886 #[snafu(implicit)]
1887 location: Location,
1888 },
1889 }
1890
1891 type TestResult<T> = std::result::Result<T, TestError>;
1892
1893 fn parse_one_sql(sql: &str) -> Statement {
1894 parse_stmt(sql, &GreptimeDbDialect {}).unwrap().remove(0)
1895 }
1896
1897 fn test_query_ctx(process_id: u32) -> QueryContextRef {
1898 Arc::new(
1899 QueryContextBuilder::default()
1900 .channel(Channel::Mysql)
1901 .conn_info(ConnInfo::new(None, Channel::Mysql))
1902 .process_id(process_id)
1903 .build(),
1904 )
1905 }
1906
1907 struct RejectUnresolvedPermissionChecker;
1908
1909 impl PermissionChecker for RejectUnresolvedPermissionChecker {
1910 fn check_permission(
1911 &self,
1912 _user_info: UserInfoRef,
1913 _req: PermissionReq,
1914 ) -> auth::error::Result<PermissionResp> {
1915 Ok(PermissionResp::Allow)
1916 }
1917
1918 fn check_permission_with_table_targets(
1919 &self,
1920 _user_info: UserInfoRef,
1921 _req: PermissionReq,
1922 targets: PermissionTableTargets,
1923 ) -> auth::error::Result<PermissionResp> {
1924 let reject = match targets {
1925 PermissionTableTargets::Unresolved => true,
1926 PermissionTableTargets::Resolved(targets) => {
1927 targets.iter().any(|target| target.table == "denied")
1928 }
1929 };
1930 Ok(if reject {
1931 PermissionResp::Reject
1932 } else {
1933 PermissionResp::Allow
1934 })
1935 }
1936 }
1937
1938 #[derive(Default)]
1939 struct TargetIndependentPermissionChecker {
1940 checks: atomic::AtomicUsize,
1941 }
1942
1943 impl PermissionChecker for TargetIndependentPermissionChecker {
1944 fn check_permission(
1945 &self,
1946 _user_info: UserInfoRef,
1947 _req: PermissionReq,
1948 ) -> auth::error::Result<PermissionResp> {
1949 self.checks.fetch_add(1, atomic::Ordering::Relaxed);
1950 Ok(PermissionResp::Allow)
1951 }
1952
1953 fn uses_table_targets(&self) -> bool {
1954 false
1955 }
1956
1957 fn check_permission_with_table_targets(
1958 &self,
1959 user_info: UserInfoRef,
1960 req: PermissionReq,
1961 _targets: PermissionTableTargets,
1962 ) -> auth::error::Result<PermissionResp> {
1963 self.check_permission(user_info, req)
1964 }
1965 }
1966
1967 struct BlockingInsertSelectInterceptor {
1968 started_tx: mpsc::UnboundedSender<()>,
1969 finish_rx: std::sync::Mutex<Option<oneshot::Receiver<()>>>,
1970 }
1971
1972 impl BlockingInsertSelectInterceptor {
1973 fn new(started_tx: mpsc::UnboundedSender<()>, finish_rx: oneshot::Receiver<()>) -> Self {
1974 Self {
1975 started_tx,
1976 finish_rx: std::sync::Mutex::new(Some(finish_rx)),
1977 }
1978 }
1979 }
1980
1981 impl SqlQueryInterceptor for BlockingInsertSelectInterceptor {
1982 type Error = Error;
1983
1984 fn pre_execute(
1985 &self,
1986 statement: Option<&Statement>,
1987 _plan: Option<&LogicalPlan>,
1988 _query_ctx: QueryContextRef,
1989 ) -> Result<()> {
1990 let Some(Statement::Insert(insert)) = statement else {
1991 return Ok(());
1992 };
1993 if !insert.has_non_values_query_source() {
1994 return Ok(());
1995 }
1996
1997 let finish_rx = self.finish_rx.lock().unwrap().take().unwrap();
1998 let _ = self.started_tx.send(());
1999 tokio::task::block_in_place(|| {
2000 tokio::runtime::Handle::current()
2001 .block_on(finish_rx)
2002 .unwrap();
2003 });
2004 Ok(())
2005 }
2006 }
2007
2008 struct PendingRecordBatchStream {
2009 schema: GtSchemaRef,
2010 polled_tx: Option<oneshot::Sender<()>>,
2011 _finish_tx: oneshot::Sender<()>,
2012 finish_rx: Pin<Box<oneshot::Receiver<()>>>,
2013 }
2014
2015 impl RecordBatchStream for PendingRecordBatchStream {
2016 fn schema(&self) -> GtSchemaRef {
2017 self.schema.clone()
2018 }
2019
2020 fn output_ordering(&self) -> Option<&[OrderOption]> {
2021 None
2022 }
2023
2024 fn metrics(&self) -> Option<common_recordbatch::adapter::RecordBatchMetrics> {
2025 None
2026 }
2027 }
2028
2029 impl Stream for PendingRecordBatchStream {
2030 type Item = common_recordbatch::error::Result<RecordBatch>;
2031
2032 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
2033 if let Some(polled_tx) = self.polled_tx.take() {
2034 let _ = polled_tx.send(());
2035 }
2036
2037 match self.finish_rx.as_mut().poll(cx) {
2038 Poll::Ready(_) => Poll::Ready(None),
2039 Poll::Pending => Poll::Pending,
2040 }
2041 }
2042 }
2043
2044 impl Unpin for PendingRecordBatchStream {}
2045
2046 #[test]
2047 fn test_record_explain_analyze_timeout_uses_empty_metrics_without_plan() {
2048 let event_recorder = Arc::new(RecordingSlowQueryEventRecorder::default());
2049 let timer = SlowQueryTimer::new(
2050 QueryStatement::Plan("EXPLAIN ANALYZE VERBOSE SELECT 1".to_string()),
2051 "public".to_string(),
2052 Duration::from_secs(3600),
2053 0.0,
2054 SlowQueriesRecordType::SystemTable,
2055 event_recorder.clone(),
2056 );
2057 let timeout_recorder = timer.recorder();
2058
2059 record_explain_analyze_timeout(Some(&timeout_recorder), None);
2060 drop(timer);
2061
2062 let payloads = event_recorder.payloads.lock().unwrap();
2063 assert_eq!(payloads.len(), 1);
2064 assert_eq!(payloads[0]["timed_out"], true);
2065 assert_eq!(payloads[0]["metrics"], serde_json::json!([]));
2066 }
2067
2068 #[tokio::test]
2069 async fn test_attach_timeout_records_explain_analyze_metrics() {
2070 let event_recorder = Arc::new(RecordingSlowQueryEventRecorder::default());
2071 let timer = SlowQueryTimer::new(
2072 QueryStatement::Plan("EXPLAIN ANALYZE VERBOSE SELECT 1".to_string()),
2073 "public".to_string(),
2074 Duration::from_secs(3600),
2075 0.0,
2076 SlowQueriesRecordType::SystemTable,
2077 event_recorder.clone(),
2078 );
2079 let timeout_recorder = timer.recorder();
2080 let plan: Arc<dyn ExecutionPlan> = Arc::new(EmptyExec::new(Arc::new(Schema::empty())));
2081 let (finish_tx, finish_rx) = oneshot::channel();
2082 let stream = PendingRecordBatchStream {
2083 schema: Arc::new(GtSchema::new(vec![])),
2084 polled_tx: None,
2085 _finish_tx: finish_tx,
2086 finish_rx: Box::pin(finish_rx),
2087 };
2088 let output = Output::new(
2089 OutputData::Stream(Box::pin(stream)),
2090 OutputMeta::new_with_plan(plan),
2091 );
2092 let output =
2093 attach_timeout(output, Duration::from_millis(10), Some(timeout_recorder)).unwrap();
2094 let OutputData::Stream(mut stream) = output.data else {
2095 unreachable!();
2096 };
2097
2098 let err = stream.next().await.unwrap().unwrap_err();
2099 assert_eq!(err.to_string(), "Stream timeout");
2100 drop(stream);
2101 drop(timer);
2102
2103 let payloads = event_recorder.payloads.lock().unwrap();
2104 assert_eq!(payloads.len(), 1);
2105 assert_eq!(payloads[0]["timed_out"], true);
2106 assert!(
2107 payloads[0]["metrics"]
2108 .as_array()
2109 .is_some_and(|metrics| !metrics.is_empty())
2110 );
2111 }
2112
2113 struct PendingDataSource {
2114 schema: GtSchemaRef,
2115 polled_tx: std::sync::Mutex<Option<oneshot::Sender<()>>>,
2116 }
2117
2118 impl DataSource for PendingDataSource {
2119 fn get_stream(
2120 &self,
2121 _request: ScanRequest,
2122 ) -> std::result::Result<SendableRecordBatchStream, BoxedError> {
2123 let (finish_tx, finish_rx) = oneshot::channel();
2124 let mut polled_tx = self.polled_tx.lock().map_err(|_| {
2125 BoxedError::new(PlainError::new(
2126 "pending data source lock poisoned".to_string(),
2127 StatusCode::Unexpected,
2128 ))
2129 })?;
2130 Ok(Box::pin(PendingRecordBatchStream {
2131 schema: self.schema.clone(),
2132 polled_tx: polled_tx.take(),
2133 _finish_tx: finish_tx,
2134 finish_rx: Box::pin(finish_rx),
2135 }))
2136 }
2137 }
2138
2139 struct NoopProcedureExecutor;
2140
2141 #[async_trait::async_trait]
2142 impl ProcedureExecutor for NoopProcedureExecutor {
2143 async fn submit_ddl_task(
2144 &self,
2145 _ctx: &ExecutorContext,
2146 _request: SubmitDdlTaskRequest,
2147 ) -> common_meta::error::Result<SubmitDdlTaskResponse> {
2148 common_meta::error::UnsupportedSnafu {
2149 operation: "submit_ddl_task",
2150 }
2151 .fail()
2152 }
2153
2154 async fn migrate_region(
2155 &self,
2156 _ctx: &ExecutorContext,
2157 _request: MigrateRegionRequest,
2158 ) -> common_meta::error::Result<MigrateRegionResponse> {
2159 common_meta::error::UnsupportedSnafu {
2160 operation: "migrate_region",
2161 }
2162 .fail()
2163 }
2164
2165 async fn reconcile(
2166 &self,
2167 _ctx: &ExecutorContext,
2168 _request: ReconcileRequest,
2169 ) -> common_meta::error::Result<ReconcileResponse> {
2170 common_meta::error::UnsupportedSnafu {
2171 operation: "reconcile",
2172 }
2173 .fail()
2174 }
2175
2176 async fn query_procedure_state(
2177 &self,
2178 _ctx: &ExecutorContext,
2179 _pid: &str,
2180 ) -> common_meta::error::Result<ProcedureStateResponse> {
2181 common_meta::error::UnsupportedSnafu {
2182 operation: "query_procedure_state",
2183 }
2184 .fail()
2185 }
2186
2187 async fn list_procedures(
2188 &self,
2189 _ctx: &ExecutorContext,
2190 ) -> common_meta::error::Result<ProcedureDetailResponse> {
2191 common_meta::error::UnsupportedSnafu {
2192 operation: "list_procedures",
2193 }
2194 .fail()
2195 }
2196 }
2197
2198 fn test_cache_registry(
2199 kv_backend: common_meta::kv_backend::KvBackendRef,
2200 ) -> TestResult<common_meta::cache::LayeredCacheRegistryRef> {
2201 Ok(Arc::new(
2202 cache::with_default_composite_cache_registry(
2203 LayeredCacheRegistryBuilder::default()
2204 .add_cache_registry(cache::build_fundamental_cache_registry(kv_backend)),
2205 )
2206 .context(BuildCacheRegistrySnafu)?
2207 .build(),
2208 ))
2209 }
2210
2211 fn test_table_info(table_id: u32, table_name: &str) -> TestResult<TableInfo> {
2212 let schema = Arc::new(GtSchema::new(vec![
2213 ColumnSchema::new("id", ConcreteDataType::int32_datatype(), false),
2214 ColumnSchema::new(
2215 "ts",
2216 ConcreteDataType::timestamp_millisecond_datatype(),
2217 false,
2218 )
2219 .with_time_index(true),
2220 ]));
2221 let table_meta = TableMetaBuilder::empty()
2222 .schema(schema)
2223 .primary_key_indices(vec![0])
2224 .value_indices(vec![1])
2225 .next_column_id(1024)
2226 .build()
2227 .with_context(|_| BuildTableMetaSnafu {
2228 table_name: table_name.to_string(),
2229 })?;
2230
2231 TableInfoBuilder::new(table_name, table_meta)
2232 .table_id(table_id)
2233 .build()
2234 .with_context(|_| BuildTableInfoSnafu {
2235 table_name: table_name.to_string(),
2236 })
2237 }
2238
2239 fn test_table(table_id: u32, table_name: &str) -> TestResult<table::TableRef> {
2240 let table_info = test_table_info(table_id, table_name)?;
2241 Ok(EmptyTable::from_table_info(&table_info))
2242 }
2243
2244 fn test_physical_table(table_id: u32, table_name: &str) -> TestResult<table::TableRef> {
2245 let mut table_info = test_table_info(table_id, table_name)?;
2246 table_info
2247 .meta
2248 .options
2249 .extra_options
2250 .insert(PHYSICAL_TABLE_METADATA_KEY.to_string(), String::new());
2251 Ok(EmptyTable::from_table_info(&table_info))
2252 }
2253
2254 fn test_logical_table(table_id: u32, table_name: &str) -> TestResult<table::TableRef> {
2255 let mut table_info = test_table_info(table_id, table_name)?;
2256 table_info.meta.engine = METRIC_ENGINE_NAME.to_string();
2257 table_info.meta.options.extra_options.insert(
2258 LOGICAL_TABLE_METADATA_KEY.to_string(),
2259 "physical_metric".to_string(),
2260 );
2261 Ok(EmptyTable::from_table_info(&table_info))
2262 }
2263
2264 fn test_metric_names_table() -> TableRef {
2265 let schema = Arc::new(GtSchema::new(vec![
2266 ColumnSchema::new("table_catalog", ConcreteDataType::string_datatype(), false),
2267 ColumnSchema::new("table_schema", ConcreteDataType::string_datatype(), false),
2268 ColumnSchema::new("table_name", ConcreteDataType::string_datatype(), false),
2269 ColumnSchema::new("engine", ConcreteDataType::string_datatype(), false),
2270 ColumnSchema::new("create_options", ConcreteDataType::string_datatype(), false),
2271 ]));
2272 let columns: Vec<VectorRef> = vec![
2273 Arc::new(StringVector::from(vec!["greptime", "greptime"])),
2274 Arc::new(StringVector::from(vec!["public", "public"])),
2275 Arc::new(StringVector::from(vec!["denied", "target"])),
2276 Arc::new(StringVector::from(vec!["metric", "metric"])),
2277 Arc::new(StringVector::from(vec![
2278 "on_physical_table=physical_metric",
2279 "on_physical_table=physical_metric",
2280 ])),
2281 ];
2282 let record_batch = RecordBatch::new(schema, columns).unwrap();
2283 MemTable::new_with_catalog(
2284 "tables",
2285 record_batch,
2286 2048,
2287 "greptime".to_string(),
2288 "information_schema".to_string(),
2289 )
2290 }
2291
2292 fn pending_table(
2293 table_id: u32,
2294 table_name: &str,
2295 polled_tx: oneshot::Sender<()>,
2296 ) -> TestResult<table::TableRef> {
2297 let table_info = test_table_info(table_id, table_name)?;
2298 let data_source = Arc::new(PendingDataSource {
2299 schema: table_info.meta.schema.clone(),
2300 polled_tx: std::sync::Mutex::new(Some(polled_tx)),
2301 });
2302
2303 Ok(Arc::new(Table::new(
2304 Arc::new(table_info),
2305 FilterPushDownType::Unsupported,
2306 data_source,
2307 )))
2308 }
2309
2310 async fn test_instance_with_tables(
2311 source_table: TableRef,
2312 target_table: TableRef,
2313 ) -> TestResult<Instance> {
2314 test_instance_with_plugins(source_table, target_table, Plugins::new()).await
2315 }
2316
2317 async fn test_instance_with_insert_select_interceptor(
2318 interceptor: SqlQueryInterceptorRef<Error>,
2319 ) -> TestResult<Instance> {
2320 let plugins = Plugins::new();
2321 plugins.insert::<SqlQueryInterceptorRef<Error>>(interceptor);
2322
2323 test_instance_with_plugins(
2324 test_table(1024, "source")?,
2325 test_table(1025, "target")?,
2326 plugins,
2327 )
2328 .await
2329 }
2330
2331 async fn test_instance_with_plugins(
2332 source_table: TableRef,
2333 target_table: TableRef,
2334 plugins: Plugins,
2335 ) -> TestResult<Instance> {
2336 test_instance_with_plugins_and_metric_names(source_table, target_table, plugins, None).await
2337 }
2338
2339 async fn test_instance_with_plugins_and_metric_names(
2340 source_table: TableRef,
2341 target_table: TableRef,
2342 plugins: Plugins,
2343 metric_names_table: Option<TableRef>,
2344 ) -> TestResult<Instance> {
2345 let kv_backend = Arc::new(MemoryKvBackend::new());
2346 let process_manager = Arc::new(ProcessManager::new("test-frontend".to_string(), None));
2347 let catalog_manager = catalog::memory::MemoryCatalogManager::new_with_table(source_table);
2348 let target_table_name = "target";
2349 catalog_manager
2350 .register_table_sync(catalog::RegisterTableRequest {
2351 catalog: "greptime".to_string(),
2352 schema: "public".to_string(),
2353 table_name: target_table_name.to_string(),
2354 table_id: 1025,
2355 table: target_table,
2356 })
2357 .with_context(|_| RegisterTableSnafu {
2358 table_name: target_table_name.to_string(),
2359 })?;
2360 if let Some(table) = metric_names_table {
2361 catalog_manager
2362 .deregister_table_sync(catalog::DeregisterTableRequest {
2363 catalog: "greptime".to_string(),
2364 schema: "information_schema".to_string(),
2365 table_name: "tables".to_string(),
2366 })
2367 .unwrap();
2368 catalog_manager
2369 .register_table_sync(catalog::RegisterTableRequest {
2370 catalog: "greptime".to_string(),
2371 schema: "information_schema".to_string(),
2372 table_name: "tables".to_string(),
2373 table_id: 2048,
2374 table,
2375 })
2376 .unwrap();
2377 }
2378 catalog_manager.register_process_list_table(process_manager.clone());
2379
2380 let cache_registry = test_cache_registry(kv_backend.clone())?;
2381
2382 FrontendBuilder::new(
2383 FrontendOptions::default(),
2384 kv_backend,
2385 cache_registry,
2386 catalog_manager,
2387 Arc::new(client::client_manager::NodeClients::default()),
2388 Arc::new(NoopProcedureExecutor),
2389 process_manager,
2390 )
2391 .with_plugin(plugins)
2392 .try_build()
2393 .await
2394 .context(BuildFrontendSnafu)
2395 }
2396
2397 async fn execute_one_sql(
2398 instance: &Instance,
2399 sql: &str,
2400 query_ctx: QueryContextRef,
2401 ) -> TestResult<Output> {
2402 let mut results = instance.do_query_inner(sql, query_ctx).await;
2403 ensure!(
2404 results.len() == 1,
2405 UnexpectedOutputCountSnafu {
2406 sql: sql.to_string(),
2407 actual: results.len(),
2408 }
2409 );
2410 results.remove(0).with_context(|_| ExecuteSqlSnafu {
2411 sql: sql.to_string(),
2412 })
2413 }
2414
2415 #[tokio::test]
2416 async fn test_event_recorder_is_exposed() -> TestResult<()> {
2417 let instance =
2418 test_instance_with_tables(test_table(1024, "source")?, test_table(1025, "target")?)
2419 .await?;
2420
2421 let _event_recorder = instance.event_recorder();
2422
2423 Ok(())
2424 }
2425
2426 #[tokio::test]
2427 async fn test_target_independent_checker_skips_target_resolution() -> TestResult<()> {
2428 let physical_table = "physical_metric";
2429 let checker = Arc::new(TargetIndependentPermissionChecker::default());
2430 let plugins = Plugins::new();
2431 plugins.insert::<PermissionCheckerRef>(checker.clone());
2432 let instance = test_instance_with_plugins(
2433 test_physical_table(1024, physical_table)?,
2434 test_table(1025, "target")?,
2435 plugins,
2436 )
2437 .await?;
2438
2439 let ctx = test_query_ctx(1);
2440 let physical_target = PermissionTableTarget::new("greptime", "public", physical_table);
2441 assert_eq!(
2442 PermissionTableTargets::Resolved(vec![physical_target.clone()]),
2443 instance
2444 .resolve_query_permission_targets(
2445 PermissionTableTargets::resolved(vec![physical_target]),
2446 &ctx,
2447 )
2448 .await
2449 .unwrap()
2450 );
2451 assert_eq!(
2452 vec![physical_table.to_string(), "target".to_string()],
2453 PrometheusHandler::filter_metadata_metric_names(
2454 &instance,
2455 vec![physical_table.to_string(), "target".to_string()],
2456 "public",
2457 &ctx,
2458 )
2459 .await
2460 .unwrap()
2461 );
2462 assert_eq!(1, checker.checks.load(atomic::Ordering::Relaxed));
2463
2464 Ok(())
2465 }
2466
2467 #[tokio::test]
2468 async fn test_query_permission_targets_are_deduplicated() -> TestResult<()> {
2469 let plugins = Plugins::new();
2470 plugins.insert::<PermissionCheckerRef>(Arc::new(RejectUnresolvedPermissionChecker));
2471 let instance = test_instance_with_plugins(
2472 test_table(1024, "source")?,
2473 test_table(1025, "target")?,
2474 plugins,
2475 )
2476 .await?;
2477 let ctx = test_query_ctx(1);
2478 let target = PermissionTableTarget::new("greptime", "public", "target");
2479
2480 assert_eq!(
2481 PermissionTableTargets::Resolved(vec![target.clone()]),
2482 instance
2483 .resolve_query_permission_targets(
2484 PermissionTableTargets::resolved(vec![target.clone(), target]),
2485 &ctx,
2486 )
2487 .await
2488 .unwrap()
2489 );
2490
2491 Ok(())
2492 }
2493
2494 #[tokio::test]
2495 async fn test_physical_query_targets_fail_closed() -> TestResult<()> {
2496 let physical_table = "physical_metric";
2497 let plugins = Plugins::new();
2498 plugins.insert::<PermissionCheckerRef>(Arc::new(RejectUnresolvedPermissionChecker));
2499 let instance = test_instance_with_plugins(
2500 test_physical_table(1024, physical_table)?,
2501 test_table(1025, "target")?,
2502 plugins,
2503 )
2504 .await?;
2505
2506 let ctx = test_query_ctx(1);
2507 let logical_target = PermissionTableTarget::new("greptime", "public", "target");
2508 assert_eq!(
2509 PermissionTableTargets::Resolved(vec![logical_target.clone()]),
2510 instance
2511 .resolve_query_permission_targets(
2512 PermissionTableTargets::resolved(vec![logical_target.clone()]),
2513 &ctx,
2514 )
2515 .await
2516 .unwrap()
2517 );
2518 let physical_target = PermissionTableTarget::new("greptime", "public", physical_table);
2519 assert_eq!(
2520 PermissionTableTargets::Unresolved,
2521 instance
2522 .resolve_query_permission_targets(
2523 PermissionTableTargets::resolved(
2524 vec![logical_target, physical_target.clone(),]
2525 ),
2526 &ctx,
2527 )
2528 .await
2529 .unwrap()
2530 );
2531 assert_eq!(
2532 vec!["target".to_string()],
2533 PrometheusHandler::filter_metadata_metric_names(
2534 &instance,
2535 vec!["target".to_string(), "denied".to_string()],
2536 "public",
2537 &ctx,
2538 )
2539 .await
2540 .unwrap()
2541 );
2542
2543 let query = PromQuery {
2544 query: physical_table.to_string(),
2545 ..Default::default()
2546 };
2547 let err = PrometheusHandler::check_query_target_permission(
2548 &instance,
2549 PermissionTableTargets::resolved(vec![physical_target]),
2550 &ctx,
2551 )
2552 .await
2553 .unwrap_err();
2554 assert_eq!(StatusCode::PermissionDenied, err.status_code());
2555 let err = PrometheusHandler::check_query_permission(
2556 &instance,
2557 std::slice::from_ref(&query),
2558 &ctx,
2559 )
2560 .await
2561 .unwrap_err();
2562 assert_eq!(StatusCode::PermissionDenied, err.status_code());
2563 let err = PrometheusHandler::do_query(&instance, &query, ctx.clone())
2564 .await
2565 .unwrap_err();
2566 assert_eq!(StatusCode::PermissionDenied, err.status_code());
2567
2568 for sql in [
2569 "SELECT * FROM physical_metric",
2570 "TQL EVAL (0, 10, '5s') physical_metric",
2571 "INSERT INTO target SELECT * FROM physical_metric",
2572 ] {
2573 let mut results = instance.do_query_inner(sql, ctx.clone()).await;
2574 assert_eq!(1, results.len(), "{sql}");
2575 let err = results.remove(0).unwrap_err();
2576 assert_eq!(StatusCode::PermissionDenied, err.status_code(), "{sql}");
2577 }
2578 let err = LogQueryHandler::query(
2579 &instance,
2580 LogQuery {
2581 table: TableName::new("greptime", "public", physical_table),
2582 ..Default::default()
2583 },
2584 ctx.clone(),
2585 )
2586 .await
2587 .unwrap_err();
2588 assert_eq!(StatusCode::PermissionDenied, err.status_code());
2589 let err = instance
2590 .do_describe_inner(parse_one_sql("SELECT * FROM physical_metric"), ctx.clone())
2591 .await
2592 .unwrap_err();
2593 assert_eq!(StatusCode::PermissionDenied, err.status_code());
2594
2595 let request = ReadRequest {
2596 queries: vec![RemoteQuery {
2597 matchers: vec![LabelMatcher {
2598 r#type: PromMatcherType::Eq as i32,
2599 name: servers::prom_store::METRIC_NAME_LABEL.to_string(),
2600 value: physical_table.to_string(),
2601 }],
2602 ..Default::default()
2603 }],
2604 ..Default::default()
2605 };
2606 let Err(err) = PromStoreProtocolHandler::read(&instance, request, ctx.clone()).await else {
2607 panic!("physical remote-read target must be rejected");
2608 };
2609 assert_eq!(StatusCode::PermissionDenied, err.status_code());
2610
2611 let err = PrometheusHandler::query_label_values(
2612 &instance,
2613 physical_table.to_string(),
2614 "host".to_string(),
2615 vec![],
2616 SystemTime::UNIX_EPOCH,
2617 SystemTime::UNIX_EPOCH,
2618 &ctx,
2619 )
2620 .await
2621 .unwrap_err();
2622 assert_eq!(StatusCode::PermissionDenied, err.status_code());
2623
2624 Ok(())
2625 }
2626
2627 #[tokio::test]
2628 async fn test_non_exact_query_discovery_keeps_denied_targets_for_batch_check() -> TestResult<()>
2629 {
2630 let plugins = Plugins::new();
2631 plugins.insert::<PermissionCheckerRef>(Arc::new(RejectUnresolvedPermissionChecker));
2632 let instance = test_instance_with_plugins_and_metric_names(
2633 test_logical_table(1024, "denied")?,
2634 test_logical_table(1025, "target")?,
2635 plugins,
2636 Some(test_metric_names_table()),
2637 )
2638 .await?;
2639 let ctx = test_query_ctx(1);
2640
2641 let mut metric_names = PrometheusHandler::query_metric_names(
2642 &instance,
2643 vec![Matcher::new(
2644 promql_parser::label::MatchOp::NotEqual,
2645 "__name__",
2646 "",
2647 )],
2648 "public",
2649 &ctx,
2650 )
2651 .await
2652 .unwrap();
2653 metric_names.sort_unstable();
2654 assert_eq!(
2655 vec!["denied".to_string(), "target".to_string()],
2656 metric_names
2657 );
2658
2659 let queries = metric_names
2660 .into_iter()
2661 .map(|query| PromQuery {
2662 query,
2663 ..Default::default()
2664 })
2665 .collect::<Vec<_>>();
2666 let err = PrometheusHandler::check_query_permission(&instance, &queries, &ctx)
2667 .await
2668 .unwrap_err();
2669 assert_eq!(StatusCode::PermissionDenied, err.status_code());
2670
2671 Ok(())
2672 }
2673
2674 #[test]
2675 fn test_fast_legacy_check_is_read_only() {
2676 let cache = DashMap::new();
2677 cache.insert("metric1".to_string(), true);
2678
2679 let names = vec!["metric1".to_string(), "metric2".to_string()];
2680 assert_eq!(Some(true), fast_legacy_check(&cache, &names).unwrap());
2681 assert!(!cache.contains_key("metric2"));
2682
2683 cache_legacy_mode(&cache, &names, true).unwrap();
2684 assert!(*cache.get("metric2").unwrap().value());
2685 assert!(cache_legacy_mode(&cache, &names, false).is_err());
2686 assert!(*cache.get("metric2").unwrap().value());
2687
2688 let cache_incompatible = DashMap::new();
2689 cache_incompatible.insert("metric1".to_string(), true);
2690 cache_incompatible.insert("metric2".to_string(), false);
2691 assert!(fast_legacy_check(&cache_incompatible, &names).is_err());
2692 }
2693
2694 #[test]
2695 fn test_should_track_statement_process() {
2696 assert!(should_track_statement_process(&parse_one_sql(
2697 "SELECT * FROM demo"
2698 )));
2699 assert!(should_track_statement_process(&parse_one_sql(
2700 "INSERT INTO demo SELECT * FROM source"
2701 )));
2702 assert!(!should_track_statement_process(&parse_one_sql(
2703 "INSERT INTO demo VALUES (1)"
2704 )));
2705 assert!(!should_track_statement_process(&parse_one_sql(
2706 "INSERT INTO demo VALUES (now())"
2707 )));
2708 }
2709
2710 #[test]
2711 fn test_should_track_plan_process() {
2712 let select_stmt = parse_one_sql("SELECT * FROM demo");
2713 let insert_select_stmt = parse_one_sql("INSERT INTO demo SELECT * FROM source");
2714 let insert_values_stmt = parse_one_sql("INSERT INTO demo VALUES (now())");
2715
2716 let empty_plan = LogicalPlanBuilder::empty(false).build().unwrap();
2717 assert!(should_track_plan_process(Some(&select_stmt), &empty_plan));
2718 assert!(should_track_plan_process(
2719 Some(&insert_select_stmt),
2720 &insert_dml_plan()
2721 ));
2722 assert!(!should_track_plan_process(
2723 Some(&insert_values_stmt),
2724 &insert_dml_plan()
2725 ));
2726 assert!(!should_track_plan_process(None, &insert_dml_plan()));
2727 }
2728
2729 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
2730 async fn test_insert_select_is_visible_in_show_processlist() -> TestResult<()> {
2731 let insert_sql = "INSERT INTO target SELECT * FROM source";
2732 let (started_tx, mut started_rx) = mpsc::unbounded_channel();
2733 let (finish_tx, finish_rx) = oneshot::channel();
2734 let interceptor = Arc::new(BlockingInsertSelectInterceptor::new(started_tx, finish_rx));
2735 let instance = Arc::new(test_instance_with_insert_select_interceptor(interceptor).await?);
2736
2737 let insert_task = tokio::spawn({
2738 let instance = instance.clone();
2739 async move { execute_one_sql(&instance, insert_sql, test_query_ctx(4242)).await }
2740 });
2741
2742 tokio::time::timeout(Duration::from_secs(5), started_rx.recv())
2743 .await
2744 .context(InsertStartTimeoutSnafu)?
2745 .context(InsertStartChannelClosedSnafu)?;
2746
2747 let output = execute_one_sql(&instance, "SHOW PROCESSLIST", test_query_ctx(43)).await?;
2748 let process_list = output.data.pretty_print().await;
2749 assert!(
2750 process_list.contains(insert_sql),
2751 "process list did not contain running insert:\n{process_list}"
2752 );
2753
2754 finish_tx
2755 .send(())
2756 .map_err(|_| ReleaseBlockedInsertSnafu.build())?;
2757 insert_task.await.context(InsertTaskPanicSnafu)??;
2758
2759 Ok(())
2760 }
2761
2762 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
2763 async fn test_kill_query_cancels_insert_select() -> TestResult<()> {
2764 assert_kill_cancels_insert_select("KILL QUERY 4242").await
2765 }
2766
2767 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
2768 async fn test_kill_process_id_cancels_insert_select() -> TestResult<()> {
2769 assert_kill_cancels_insert_select("KILL 'test-frontend/4242'").await
2770 }
2771
2772 async fn assert_kill_cancels_insert_select(kill_sql: &str) -> TestResult<()> {
2773 let insert_sql = "INSERT INTO target SELECT * FROM source";
2774 let (source_polled_tx, source_polled_rx) = oneshot::channel();
2775 let instance = Arc::new(
2776 test_instance_with_tables(
2777 pending_table(1024, "source", source_polled_tx)?,
2778 test_table(1025, "target")?,
2779 )
2780 .await?,
2781 );
2782
2783 let insert_task = tokio::spawn({
2784 let instance = instance.clone();
2785 async move { execute_one_sql(&instance, insert_sql, test_query_ctx(4242)).await }
2786 });
2787
2788 tokio::time::timeout(Duration::from_secs(5), source_polled_rx)
2789 .await
2790 .context(SourcePollTimeoutSnafu)?
2791 .context(SourcePollChannelClosedSnafu)?;
2792
2793 let output = execute_one_sql(&instance, kill_sql, test_query_ctx(43)).await?;
2794 assert!(matches!(output.data, OutputData::AffectedRows(1)));
2795
2796 let insert_result = tokio::time::timeout(Duration::from_secs(5), insert_task)
2797 .await
2798 .context(InsertTaskTimeoutSnafu)?
2799 .context(InsertTaskPanicSnafu)?;
2800 let err = match insert_result {
2801 Ok(_) => return InsertSelectNotCancelledSnafu.fail(),
2802 Err(TestError::ExecuteSql { source, .. }) => source,
2803 Err(err) => return Err(err),
2804 };
2805 assert_eq!(StatusCode::Cancelled, err.status_code());
2806
2807 let output = execute_one_sql(&instance, "SHOW PROCESSLIST", test_query_ctx(43)).await?;
2808 let process_list = output.data.pretty_print().await;
2809 assert!(
2810 !process_list.contains(insert_sql),
2811 "process list still contains killed insert:\n{process_list}"
2812 );
2813
2814 Ok(())
2815 }
2816
2817 fn insert_dml_plan() -> LogicalPlan {
2818 let schema = SchemaRef::new(Schema::new(vec![Field::new(
2819 "value",
2820 DataType::Int64,
2821 true,
2822 )]));
2823 let target = Arc::new(LogicalTableSource::new(schema));
2824 let input = LogicalPlanBuilder::empty(false).build().unwrap();
2825
2826 LogicalPlanBuilder::insert_into(input, "demo", target, InsertOp::Append)
2827 .unwrap()
2828 .build()
2829 .unwrap()
2830 }
2831
2832 #[test]
2833 fn test_exec_validation() {
2834 let query_ctx = QueryContext::arc();
2835 let plugins: Plugins = Plugins::new();
2836 plugins.insert(QueryOptions {
2837 disallow_cross_catalog_query: true,
2838 });
2839
2840 let sql = r#"
2841 SELECT * FROM demo;
2842 EXPLAIN SELECT * FROM demo;
2843 CREATE DATABASE test_database;
2844 SHOW DATABASES;
2845 "#;
2846 let stmts = parse_stmt(sql, &GreptimeDbDialect {}).unwrap();
2847 assert_eq!(stmts.len(), 4);
2848 for stmt in stmts {
2849 let re = check_permission(plugins.clone(), &stmt, &query_ctx);
2850 re.unwrap();
2851 }
2852
2853 let sql = r#"
2854 SHOW CREATE TABLE demo;
2855 ALTER TABLE demo ADD COLUMN new_col INT;
2856 "#;
2857 let stmts = parse_stmt(sql, &GreptimeDbDialect {}).unwrap();
2858 assert_eq!(stmts.len(), 2);
2859 for stmt in stmts {
2860 let re = check_permission(plugins.clone(), &stmt, &query_ctx);
2861 re.unwrap();
2862 }
2863
2864 fn replace_test(template_sql: &str, plugins: Plugins, query_ctx: &QueryContextRef) {
2865 let right = vec![("", ""), ("", "public."), ("greptime.", "public.")];
2867 for (catalog, schema) in right {
2868 let sql = do_fmt(template_sql, catalog, schema);
2869 do_test(&sql, plugins.clone(), query_ctx, true);
2870 }
2871
2872 let wrong = vec![
2873 ("wrongcatalog.", "public."),
2874 ("wrongcatalog.", "wrongschema."),
2875 ];
2876 for (catalog, schema) in wrong {
2877 let sql = do_fmt(template_sql, catalog, schema);
2878 do_test(&sql, plugins.clone(), query_ctx, false);
2879 }
2880 }
2881
2882 fn do_fmt(template: &str, catalog: &str, schema: &str) -> String {
2883 let vars = HashMap::from([
2884 ("catalog".to_string(), catalog),
2885 ("schema".to_string(), schema),
2886 ]);
2887 template.format(&vars).unwrap()
2888 }
2889
2890 fn do_test(sql: &str, plugins: Plugins, query_ctx: &QueryContextRef, is_ok: bool) {
2891 let stmt = &parse_stmt(sql, &GreptimeDbDialect {}).unwrap()[0];
2892 let re = check_permission(plugins, stmt, query_ctx);
2893 if is_ok {
2894 re.unwrap();
2895 } else {
2896 assert!(re.is_err());
2897 }
2898 }
2899
2900 let sql = "INSERT INTO {catalog}{schema}monitor(host) VALUES ('host1');";
2902 replace_test(sql, plugins.clone(), &query_ctx);
2903
2904 let sql = r#"CREATE TABLE {catalog}{schema}demo(
2906 host STRING,
2907 ts TIMESTAMP,
2908 TIME INDEX (ts),
2909 PRIMARY KEY(host)
2910 ) engine=mito;"#;
2911 replace_test(sql, plugins.clone(), &query_ctx);
2912
2913 let sql = "DROP TABLE {catalog}{schema}demo;";
2915 replace_test(sql, plugins.clone(), &query_ctx);
2916
2917 let sql = "UNDROP TABLE {catalog}{schema}demo;";
2919 replace_test(sql, plugins.clone(), &query_ctx);
2920
2921 let sql = "SHOW TABLES FROM public";
2923 let stmt = parse_stmt(sql, &GreptimeDbDialect {}).unwrap();
2924 check_permission(plugins.clone(), &stmt[0], &query_ctx).unwrap();
2925
2926 let sql = "SHOW TABLES FROM private";
2927 let stmt = parse_stmt(sql, &GreptimeDbDialect {}).unwrap();
2928 let re = check_permission(plugins.clone(), &stmt[0], &query_ctx);
2929 assert!(re.is_ok());
2930
2931 let sql = "DESC TABLE {catalog}{schema}demo;";
2933 replace_test(sql, plugins.clone(), &query_ctx);
2934
2935 let comment_flow_cases = [
2936 ("COMMENT ON FLOW my_flow IS 'comment';", true),
2937 ("COMMENT ON FLOW greptime.my_flow IS 'comment';", true),
2938 ("COMMENT ON FLOW wrongcatalog.my_flow IS 'comment';", false),
2939 ];
2940 for (sql, is_ok) in comment_flow_cases {
2941 let stmt = &parse_stmt(sql, &GreptimeDbDialect {}).unwrap()[0];
2942 let result = check_permission(plugins.clone(), stmt, &query_ctx);
2943 assert_eq!(result.is_ok(), is_ok);
2944 }
2945
2946 let show_flow_cases = [
2947 ("SHOW CREATE FLOW my_flow;", true),
2948 ("SHOW CREATE FLOW greptime.my_flow;", true),
2949 ("SHOW CREATE FLOW wrongcatalog.my_flow;", false),
2950 ];
2951 for (sql, is_ok) in show_flow_cases {
2952 let stmt = &parse_stmt(sql, &GreptimeDbDialect {}).unwrap()[0];
2953 let result = check_permission(plugins.clone(), stmt, &query_ctx);
2954 assert_eq!(result.is_ok(), is_ok);
2955 }
2956 }
2957}