1use super::{
2 answer::Answer,
3 deny_queries_table_function::DenyQueriesTableFunction,
4 get_payload_function::GetPayload,
5 lakehouse_context::LakehouseContext,
6 list_audience_grants_table_function::{GrantVisibility, ListAudienceGrantsTableFunction},
7 list_partitions_table_function::ListPartitionsTableFunction,
8 list_query_denials_table_function::ListQueryDenialsTableFunction,
9 list_view_set_definitions_table_function::ListViewSetDefinitionsTableFunction,
10 list_view_sets_table_function::ListViewSetsTableFunction,
11 materialize_partitions_table_function::MaterializePartitionsTableFunction,
12 parse_block_table_function::ParseBlockTableFunction,
13 partition::Partition,
14 partition_cache::QueryPartitionProvider,
15 partitioned_table_provider::PartitionedTableProvider,
16 perfetto_trace_table_function::PerfettoTraceTableFunction,
17 process_spans_table_function::ProcessSpansTableFunction,
18 reader_factory::ReaderFactory,
19 regenerate_partitions_table_function::RegeneratePartitionsTableFunction,
20 remove_query_denial_udf::make_remove_query_denial_udf,
21 retire_partition_by_file_udf::make_retire_partition_by_file_udf,
22 retire_partition_by_metadata_udf::make_retire_partition_by_metadata_udf,
23 retire_partitions_table_function::RetirePartitionsTableFunction,
24 session_configurator::SessionConfigurator,
25 view::View,
26 view_factory::ViewFactory,
27};
28use crate::{
29 lakehouse::{
30 audience_guard::AudienceGuard,
31 materialized_view::MaterializedView,
32 ownership_rewrite::OwnershipRewrite,
33 read_scope::{CallerContext, ReadScope},
34 table_scan_rewrite::TableScanRewrite,
35 view_instance_table_function::ViewInstanceTableFunction,
36 },
37 properties::{
38 properties_to_dict_udf::PropertiesToDict, properties_to_jsonb_udf::PropertiesToJsonb,
39 },
40 time::TimeRange,
41};
42use anyhow::{Context, Result};
43use datafusion::{
44 arrow::{array::RecordBatch, datatypes::SchemaRef},
45 common::TableReference,
46 datasource::DefaultTableSource,
47 execution::{context::SessionContext, object_store::ObjectStoreUrl, runtime_env::RuntimeEnv},
48 logical_expr::{ScalarUDF, TableSource, async_udf::AsyncScalarUDF},
49 prelude::*,
50};
51use micromegas_tracing::prelude::*;
52use std::sync::Arc;
53
54#[span_fn]
55async fn register_table(
56 lakehouse: Arc<LakehouseContext>,
57 reader_factory: Arc<ReaderFactory>,
58 part_provider: Arc<dyn QueryPartitionProvider>,
59 query_range: Option<TimeRange>,
60 ctx: &SessionContext,
61 view: Arc<dyn View>,
62) -> Result<()> {
63 let table = MaterializedView::new(
64 lakehouse,
65 reader_factory,
66 view.clone(),
67 part_provider,
68 query_range,
69 None,
70 );
71 view.register_table(ctx, table).await
72}
73
74#[span_fn]
76pub async fn query_partitions_context(
77 runtime: Arc<RuntimeEnv>,
78 reader_factory: Arc<ReaderFactory>,
79 object_store: Arc<dyn object_store::ObjectStore>,
80 schema: SchemaRef,
81 partitions: Arc<Vec<Partition>>,
82) -> Result<SessionContext> {
83 let table = PartitionedTableProvider::new(schema, reader_factory, partitions);
84 let object_store_url = ObjectStoreUrl::parse("obj://lakehouse/").unwrap();
85 let ctx = SessionContext::new_with_config_rt(SessionConfig::default(), runtime);
86 ctx.register_object_store(object_store_url.as_ref(), object_store);
87 ctx.register_table(
88 TableReference::Bare {
89 table: "source".into(),
90 },
91 Arc::new(table),
92 )?;
93 register_extension_functions(&ctx);
94 Ok(ctx)
95}
96
97#[span_fn]
99pub async fn query_partitions(
100 runtime: Arc<RuntimeEnv>,
101 reader_factory: Arc<ReaderFactory>,
102 object_store: Arc<dyn object_store::ObjectStore>,
103 schema: SchemaRef,
104 partitions: Arc<Vec<Partition>>,
105 sql: &str,
106) -> Result<DataFrame> {
107 let ctx =
108 query_partitions_context(runtime, reader_factory, object_store, schema, partitions).await?;
109 Ok(ctx.sql(sql).await?)
110}
111
112#[span_fn]
114pub fn register_lakehouse_functions(
115 ctx: &SessionContext,
116 lakehouse: Arc<LakehouseContext>,
117 part_provider: Arc<dyn QueryPartitionProvider>,
118 query_range: Option<TimeRange>,
119 view_factory: Arc<ViewFactory>,
120 caller: &CallerContext,
121) {
122 let lakehouse_admin = caller.is_admin;
128 let audience_guard = Arc::new(AudienceGuard::new(
129 caller.read_scope.clone(),
130 lakehouse_admin,
131 caller.isolation_config.public_view_sets.clone(),
132 lakehouse.audience_index().clone(),
133 ));
134 ctx.register_udtf(
135 "view_instance",
136 Arc::new(ViewInstanceTableFunction::new(
137 lakehouse.clone(),
138 view_factory.clone(),
139 part_provider.clone(),
140 query_range,
141 audience_guard.clone(),
142 )),
143 );
144 ctx.register_udtf(
145 "list_partitions",
146 Arc::new(ListPartitionsTableFunction::new(
147 lakehouse.lake().clone(),
148 audience_guard.clone(),
149 )),
150 );
151 ctx.register_udtf(
152 "list_view_sets",
153 Arc::new(ListViewSetsTableFunction::new(view_factory.clone())),
154 );
155 ctx.register_udtf(
159 "list_audience_grants",
160 Arc::new(ListAudienceGrantsTableFunction::new(
161 lakehouse.lake().db_pool.clone(),
162 if caller.is_admin {
163 GrantVisibility::All
164 } else {
165 GrantVisibility::Held(caller.grant_selectors.clone())
166 },
167 )),
168 );
169 ctx.register_udtf(
170 "perfetto_trace_chunks",
171 Arc::new(PerfettoTraceTableFunction::new(
172 lakehouse.clone(),
173 view_factory.clone(),
174 part_provider.clone(),
175 audience_guard.clone(),
176 )),
177 );
178 ctx.register_udtf(
179 "parse_block",
180 Arc::new(ParseBlockTableFunction::new(
181 lakehouse.clone(),
182 view_factory.clone(),
183 part_provider.clone(),
184 query_range,
185 audience_guard.clone(),
186 )),
187 );
188 ctx.register_udtf(
189 "process_spans",
190 Arc::new(ProcessSpansTableFunction::new(
191 lakehouse.clone(),
192 view_factory.clone(),
193 part_provider.clone(),
194 query_range,
195 audience_guard.clone(),
196 )),
197 );
198 ctx.register_udf(
199 AsyncScalarUDF::new(Arc::new(GetPayload::new(
200 lakehouse.lake().clone(),
201 audience_guard.clone(),
202 )))
203 .into_scalar_udf(),
204 );
205 if lakehouse_admin {
206 ctx.register_udtf(
207 "retire_partitions",
208 Arc::new(RetirePartitionsTableFunction::new(lakehouse.lake().clone())),
209 );
210 ctx.register_udtf(
211 "materialize_partitions",
212 Arc::new(MaterializePartitionsTableFunction::new(
213 lakehouse.clone(),
214 view_factory.clone(),
215 )),
216 );
217 ctx.register_udtf(
218 "regenerate_partitions",
219 Arc::new(RegeneratePartitionsTableFunction::new(
220 lakehouse.clone(),
221 view_factory.clone(),
222 )),
223 );
224 ctx.register_udf(
225 make_retire_partition_by_file_udf(lakehouse.lake().clone()).into_scalar_udf(),
226 );
227 ctx.register_udf(
228 make_retire_partition_by_metadata_udf(lakehouse.lake().clone()).into_scalar_udf(),
229 );
230 ctx.register_udtf(
232 "list_query_denials",
233 Arc::new(ListQueryDenialsTableFunction::new(
234 lakehouse.query_denials().clone(),
235 )),
236 );
237 ctx.register_udtf(
238 "deny_queries",
239 Arc::new(DenyQueriesTableFunction::new(
240 lakehouse.query_denials().clone(),
241 caller.identity.clone(),
242 )),
243 );
244 ctx.register_udf(
245 make_remove_query_denial_udf(lakehouse.query_denials().clone()).into_scalar_udf(),
246 );
247 ctx.register_udtf(
251 "list_view_set_definitions",
252 Arc::new(ListViewSetDefinitionsTableFunction::new(
253 lakehouse.lake().clone(),
254 )),
255 );
256 }
257}
258
259#[span_fn]
261pub fn register_extension_functions(ctx: &SessionContext) {
262 ctx.register_udf(ScalarUDF::from(PropertiesToDict::new()));
263 ctx.register_udf(ScalarUDF::from(PropertiesToJsonb::new()));
264 micromegas_datafusion_extensions::register_extension_udfs(ctx);
265}
266
267#[span_fn]
268pub fn register_functions(
269 ctx: &SessionContext,
270 lakehouse: Arc<LakehouseContext>,
271 part_provider: Arc<dyn QueryPartitionProvider>,
272 query_range: Option<TimeRange>,
273 view_factory: Arc<ViewFactory>,
274 caller: &CallerContext,
275) {
276 register_lakehouse_functions(
277 ctx,
278 lakehouse,
279 part_provider,
280 query_range,
281 view_factory,
282 caller,
283 );
284 register_extension_functions(ctx);
285}
286
287#[span_fn]
288pub async fn make_session_context(
289 lakehouse: Arc<LakehouseContext>,
290 part_provider: Arc<dyn QueryPartitionProvider>,
291 query_range: Option<TimeRange>,
292 view_factory: Arc<ViewFactory>,
293 configurator: Arc<dyn SessionConfigurator>,
294 caller: CallerContext,
295) -> Result<SessionContext> {
296 let config = SessionConfig::default()
300 .set_bool("datafusion.execution.parquet.enable_page_index", false)
301 .set_bool("datafusion.sql_parser.collect_spans", true)
306 .with_information_schema(true);
307 let ctx = SessionContext::new_with_config_rt(config, lakehouse.runtime().clone());
308 if let Some(range) = &query_range {
309 ctx.add_analyzer_rule(Arc::new(TableScanRewrite::new(*range)));
310 }
311 let object_store_url = ObjectStoreUrl::parse("obj://lakehouse/").unwrap();
312 let object_store = lakehouse.lake().blob_storage.inner();
313 ctx.register_object_store(object_store_url.as_ref(), object_store);
314 let reader_factory = lakehouse.reader_factory().clone();
315 register_functions(
316 &ctx,
317 lakehouse.clone(),
318 part_provider.clone(),
319 query_range,
320 view_factory.clone(),
321 &caller,
322 );
323 for view in view_factory.get_global_views() {
324 register_table(
325 lakehouse.clone(),
326 reader_factory.clone(),
327 part_provider.clone(),
328 query_range,
329 &ctx,
330 view.clone(),
331 )
332 .await?;
333 }
334 if caller.read_scope != ReadScope::All {
335 let processes_view = view_factory.get_global_view("processes").with_context(
345 || "OwnershipRewrite requires the `processes` global view to be registered",
346 )?;
347 let streams_view = view_factory.get_global_view("streams").with_context(
348 || "OwnershipRewrite requires the `streams` global view to be registered",
349 )?;
350 let processes_source: Arc<dyn TableSource> =
351 Arc::new(DefaultTableSource::new(Arc::new(MaterializedView::new(
352 lakehouse.clone(),
353 reader_factory.clone(),
354 processes_view,
355 part_provider.clone(),
356 None,
357 None,
358 ))));
359 let streams_source: Arc<dyn TableSource> =
360 Arc::new(DefaultTableSource::new(Arc::new(MaterializedView::new(
361 lakehouse.clone(),
362 reader_factory.clone(),
363 streams_view,
364 part_provider.clone(),
365 None,
366 None,
367 ))));
368 ctx.add_analyzer_rule(Arc::new(OwnershipRewrite::new(
369 caller.read_scope.clone(),
370 caller.isolation_config.public_view_sets.clone(),
371 processes_source,
372 streams_source,
373 )));
374 }
375 configurator.configure(&ctx).await?;
377 Ok(ctx)
378}
379
380#[span_fn]
381pub async fn query(
382 lakehouse: Arc<LakehouseContext>,
383 part_provider: Arc<dyn QueryPartitionProvider>,
384 query_range: Option<TimeRange>,
385 sql: &str,
386 view_factory: Arc<ViewFactory>,
387 configurator: Arc<dyn SessionConfigurator>,
388 caller: CallerContext,
389) -> Result<Answer> {
390 info!("query sql={sql}");
391 let ctx = make_session_context(
392 lakehouse,
393 part_provider,
394 query_range,
395 view_factory,
396 configurator,
397 caller,
398 )
399 .await
400 .with_context(|| "make_session_context")?;
401 let df = ctx.sql(sql).await?;
402 let schema = df.schema().inner().clone();
403 let batches: Vec<RecordBatch> = df.collect().await?;
404 Ok(Answer::new(schema, batches))
405}