1use std::collections::HashSet;
16use std::sync::Arc;
17use std::time::SystemTime;
18
19use auth::PermissionTableTarget;
20use catalog::information_schema::TABLES;
21use client::OutputData;
22use common_catalog::consts::INFORMATION_SCHEMA_NAME;
23use common_catalog::format_full_table_name;
24use common_recordbatch::{RecordBatch, util};
25use common_telemetry::tracing;
26use datafusion_expr::LogicalPlan;
27use datatypes::arrow::array::{Array, UInt32Array};
28use futures::StreamExt;
29use promql_parser::label::{Matcher, Matchers};
30use query::promql;
31use query::promql::planner::PromPlanner;
32use servers::prometheus;
33use session::context::QueryContextRef;
34use snafu::{OptionExt, ResultExt};
35use store_api::metric_engine_consts::DATA_SCHEMA_TABLE_ID_COLUMN_NAME;
36use store_api::storage::TableId;
37use table::TableRef;
38
39use crate::error::{
40 CatalogSnafu, CollectRecordbatchSnafu, ExecLogicalPlanSnafu,
41 PrometheusLabelValuesQueryPlanSnafu, PrometheusMetricNamesQueryPlanSnafu, ReadTableSnafu,
42 Result, TableNotFoundSnafu, TableSnafu, UnexpectedColumnTypeSnafu,
43};
44use crate::instance::Instance;
45
46pub(super) fn remove_output_sort(plan: LogicalPlan) -> LogicalPlan {
51 match plan {
52 LogicalPlan::Sort(sort) if sort.fetch.is_none() => Arc::unwrap_or_clone(sort.input),
53 LogicalPlan::Projection(mut projection) => {
54 projection.input = Arc::new(remove_output_sort(Arc::unwrap_or_clone(projection.input)));
55 LogicalPlan::Projection(projection)
56 }
57 plan => plan,
58 }
59}
60
61impl Instance {
62 #[tracing::instrument(skip_all)]
64 pub(crate) async fn handle_query_metric_names(
65 &self,
66 matchers: Vec<Matcher>,
67 schema: &str,
68 ctx: &QueryContextRef,
69 ) -> Result<Vec<String>> {
70 let _timer = crate::metrics::PROMQL_QUERY_METRICS_ELAPSED
71 .with_label_values(&[ctx.get_db_string().as_str()])
72 .start_timer();
73
74 let table = self
75 .catalog_manager
76 .table(
77 ctx.current_catalog(),
78 INFORMATION_SCHEMA_NAME,
79 TABLES,
80 Some(ctx),
81 )
82 .await
83 .context(CatalogSnafu)?
84 .with_context(|| TableNotFoundSnafu {
85 table_name: "greptime.information_schema.tables",
86 })?;
87
88 let dataframe = self
89 .query_engine
90 .read_table(table)
91 .with_context(|_| ReadTableSnafu {
92 table_name: "greptime.information_schema.tables",
93 })?;
94
95 let logical_plan =
96 prometheus::metric_name_matchers_to_plan(dataframe, matchers, schema, ctx)
97 .context(PrometheusMetricNamesQueryPlanSnafu)?;
98
99 let results = self
100 .query_engine
101 .execute(logical_plan, ctx.clone())
102 .await
103 .context(ExecLogicalPlanSnafu)?;
104
105 let batches = match results.data {
106 OutputData::Stream(stream) => util::collect(stream)
107 .await
108 .context(CollectRecordbatchSnafu)?,
109 OutputData::RecordBatches(rbs) => rbs.take(),
110 _ => unreachable!("should not happen"),
111 };
112
113 let mut results = Vec::with_capacity(batches.iter().map(|b| b.num_rows()).sum());
114
115 for batch in batches {
116 batch
118 .iter_column_as_string(0)
119 .flatten()
120 .for_each(|x| results.push(x))
121 }
122
123 Ok(results)
124 }
125
126 #[tracing::instrument(skip_all)]
139 pub(crate) async fn handle_query_metric_names_by_labels(
140 &self,
141 matchers: Vec<Matcher>,
142 schema: &str,
143 start: SystemTime,
144 end: SystemTime,
145 ctx: &QueryContextRef,
146 ) -> Result<Vec<String>> {
147 let _timer = crate::metrics::PROMQL_QUERY_METRICS_ELAPSED
148 .with_label_values(&[ctx.get_db_string().as_str()])
149 .start_timer();
150
151 let catalog = ctx.current_catalog();
152 let mut table_ids = HashSet::new();
153 for physical in self.physical_metric_tables(catalog, schema, ctx).await? {
154 table_ids.extend(
155 self.scan_matching_table_ids(physical, &matchers, start, end, ctx)
156 .await?,
157 );
158 }
159
160 let table_ids = table_ids.into_iter().collect::<Vec<_>>();
163 let mut names = self
164 .catalog_manager
165 .tables_by_ids(catalog, schema, &table_ids)
166 .await
167 .context(CatalogSnafu)?
168 .into_iter()
169 .map(|table| table.table_info().name.clone())
170 .collect::<Vec<_>>();
171 names.sort_unstable();
172 Ok(names)
173 }
174
175 async fn physical_metric_tables(
180 &self,
181 catalog: &str,
182 schema: &str,
183 ctx: &QueryContextRef,
184 ) -> Result<Vec<TableRef>> {
185 let mut tables = self.catalog_manager.tables(catalog, schema, Some(ctx));
186 let mut physical_tables = Vec::new();
187
188 while let Some(table) = tables.next().await {
189 let table = table.context(CatalogSnafu)?;
190 if table.table_info().is_physical_table() {
191 physical_tables.push(table);
192 }
193 }
194
195 Ok(physical_tables)
196 }
197
198 async fn scan_distinct_column(
204 &self,
205 table: TableRef,
206 matchers: Vec<Matcher>,
207 column: String,
208 start: SystemTime,
209 end: SystemTime,
210 ctx: &QueryContextRef,
211 ) -> Result<Vec<RecordBatch>> {
212 let dataframe = self
213 .query_engine
214 .read_table(table.clone())
215 .with_context(|_| ReadTableSnafu {
216 table_name: table.table_info().full_table_name(),
217 })?;
218
219 let scan_plan = dataframe.into_unoptimized_plan();
220 let conditions = PromPlanner::matchers_to_expr(Matchers::new(matchers), scan_plan.schema())
221 .context(PrometheusLabelValuesQueryPlanSnafu)?;
222 let logical_plan = promql::label_values::rewrite_label_values_query(
223 table, scan_plan, conditions, column, start, end,
224 )
225 .context(PrometheusLabelValuesQueryPlanSnafu)?;
226
227 let results = self
228 .query_engine
229 .execute(logical_plan, ctx.clone())
230 .await
231 .context(ExecLogicalPlanSnafu)?;
232
233 match results.data {
234 OutputData::Stream(stream) => {
235 util::collect(stream).await.context(CollectRecordbatchSnafu)
236 }
237 OutputData::RecordBatches(rbs) => Ok(rbs.take()),
238 _ => unreachable!("should not happen"),
239 }
240 }
241
242 async fn scan_matching_table_ids(
245 &self,
246 physical: TableRef,
247 matchers: &[Matcher],
248 start: SystemTime,
249 end: SystemTime,
250 ctx: &QueryContextRef,
251 ) -> Result<Vec<TableId>> {
252 if physical
256 .schema()
257 .column_schema_by_name(DATA_SCHEMA_TABLE_ID_COLUMN_NAME)
258 .is_none()
259 {
260 return Ok(Vec::new());
261 }
262
263 let table_name = physical.table_info().full_table_name();
264 let batches = self
265 .scan_distinct_column(
266 physical,
267 matchers.to_vec(),
268 DATA_SCHEMA_TABLE_ID_COLUMN_NAME.to_string(),
269 start,
270 end,
271 ctx,
272 )
273 .await?;
274
275 let mut table_ids = Vec::new();
276 for batch in batches {
277 let column = batch.column(0);
279 let ids = column
280 .as_any()
281 .downcast_ref::<UInt32Array>()
282 .with_context(|| UnexpectedColumnTypeSnafu {
283 table_name: table_name.clone(),
284 column: DATA_SCHEMA_TABLE_ID_COLUMN_NAME,
285 data_type: column.data_type().to_string(),
286 })?;
287 table_ids.extend(ids.iter().flatten());
288 }
289
290 Ok(table_ids)
291 }
292
293 #[tracing::instrument(skip_all)]
295 pub(crate) async fn handle_query_label_values(
296 &self,
297 target: PermissionTableTarget,
298 label_name: String,
299 matchers: Vec<Matcher>,
300 start: SystemTime,
301 end: SystemTime,
302 ctx: &QueryContextRef,
303 ) -> Result<Vec<String>> {
304 let full_table_name =
305 format_full_table_name(&target.catalog, &target.schema, &target.table);
306 let table = self
307 .catalog_manager
308 .table(&target.catalog, &target.schema, &target.table, Some(ctx))
309 .await
310 .context(CatalogSnafu)?
311 .with_context(|| TableNotFoundSnafu {
312 table_name: full_table_name.clone(),
313 })?;
314
315 if table.schema().column_schema_by_name(&label_name).is_none() {
318 return table::error::ColumnNotExistsSnafu {
319 column_name: label_name,
320 table_name: full_table_name,
321 }
322 .fail()
323 .context(TableSnafu);
324 }
325
326 let batches = self
327 .scan_distinct_column(table, matchers, label_name, start, end, ctx)
328 .await?;
329
330 let mut results = Vec::with_capacity(batches.iter().map(|b| b.num_rows()).sum());
331 for batch in batches {
332 batch
334 .iter_column_as_string(0)
335 .flatten()
336 .for_each(|x| results.push(x))
337 }
338
339 Ok(results)
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use datafusion::arrow::array::Int64Array;
346 use datafusion::prelude::{SessionConfig, SessionContext};
347 use datafusion_expr::{LogicalPlanBuilder, Sort, col, lit};
348
349 use super::*;
350
351 #[tokio::test]
352 async fn remove_output_sort_keeps_schema_and_row_selection() {
353 let input =
354 LogicalPlanBuilder::values(vec![vec![lit(3_i64)], vec![lit(1_i64)], vec![lit(2_i64)]])
355 .unwrap()
356 .build()
357 .unwrap();
358 let sort = Sort {
359 expr: vec![col("column1").sort(true, false)],
360 input: Arc::new(input),
361 fetch: None,
362 };
363 let projected = LogicalPlanBuilder::from(LogicalPlan::Sort(sort.clone()))
364 .project(vec![col("column1").alias("sample")])
365 .unwrap()
366 .project(vec![col("sample")])
367 .unwrap()
368 .build()
369 .unwrap();
370 let limited = LogicalPlanBuilder::from(LogicalPlan::Sort(sort.clone()))
372 .limit(0, Some(2))
373 .unwrap()
374 .sort(vec![col("column1").sort(false, false)])
375 .unwrap()
376 .build()
377 .unwrap();
378 let fetched = LogicalPlan::Sort(Sort {
379 fetch: Some(2),
380 ..sort.clone()
381 });
382
383 let context =
384 SessionContext::new_with_config(SessionConfig::new().with_target_partitions(1));
385 for (name, plan, expected) in [
386 ("root", LogicalPlan::Sort(sort), vec![3, 1, 2]),
387 ("projection", projected, vec![3, 1, 2]),
388 ("sort below limit", limited, vec![1, 2]),
389 ("fetch", fetched, vec![1, 2]),
390 ] {
391 let schema = plan.schema().clone();
392 let plan = remove_output_sort(plan);
393 assert_eq!(plan.schema(), &schema, "{name}");
394 let output = context
395 .execute_logical_plan(plan)
396 .await
397 .unwrap()
398 .collect()
399 .await
400 .unwrap();
401 let values = output
402 .iter()
403 .flat_map(|batch| {
404 batch
405 .column(0)
406 .as_any()
407 .downcast_ref::<Int64Array>()
408 .unwrap()
409 .values()
410 .iter()
411 .copied()
412 })
413 .collect::<Vec<_>>();
414 assert_eq!(values, expected, "{name}");
415 }
416 }
417}