1use std::any::Any;
21use std::collections::HashMap;
22use std::fmt::Debug;
23use std::sync::Arc;
24use std::sync::RwLock;
25
26use async_trait::async_trait;
27use datafusion::catalog::{CatalogProvider, MemorySchemaProvider, SchemaProvider};
28use datafusion::common::plan_datafusion_err;
29use datafusion::datasource::TableProvider;
30use datafusion::error::Result as DFResult;
31use paimon::catalog::{Catalog, Identifier};
32
33use crate::error::to_datafusion_error;
34use crate::runtime::{await_with_runtime, block_on_with_runtime};
35use crate::system_tables;
36use crate::table::PaimonTableProvider;
37use crate::DynamicOptions;
38
39pub struct PaimonCatalogProvider {
45 catalog: Arc<dyn Catalog>,
47 dynamic_options: DynamicOptions,
49 temp_tables: Arc<RwLock<HashMap<String, Arc<MemorySchemaProvider>>>>,
57}
58
59impl Debug for PaimonCatalogProvider {
60 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 f.debug_struct("PaimonCatalogProvider").finish()
62 }
63}
64
65impl PaimonCatalogProvider {
66 pub fn new(catalog: Arc<dyn Catalog>) -> Self {
72 PaimonCatalogProvider {
73 catalog,
74 dynamic_options: Default::default(),
75 temp_tables: Arc::new(RwLock::new(HashMap::new())),
76 }
77 }
78
79 pub(crate) fn with_dynamic_options(
80 catalog: Arc<dyn Catalog>,
81 dynamic_options: DynamicOptions,
82 ) -> Self {
83 PaimonCatalogProvider {
84 catalog,
85 dynamic_options,
86 temp_tables: Arc::new(RwLock::new(HashMap::new())),
87 }
88 }
89}
90
91impl CatalogProvider for PaimonCatalogProvider {
92 fn as_any(&self) -> &dyn Any {
93 self
94 }
95
96 fn schema_names(&self) -> Vec<String> {
97 let catalog = Arc::clone(&self.catalog);
98 block_on_with_runtime(
99 async move {
100 catalog.list_databases().await.unwrap_or_else(|e| {
101 log::error!("failed to list databases: {e}");
102 vec![]
103 })
104 },
105 "paimon catalog access thread panicked",
106 )
107 }
108
109 fn schema(&self, name: &str) -> Option<Arc<dyn SchemaProvider>> {
110 let catalog = Arc::clone(&self.catalog);
111 let dynamic_options = Arc::clone(&self.dynamic_options);
112 let name = name.to_string();
113
114 let temp_provider = {
115 let databases = self.temp_tables.read().unwrap_or_else(|e| e.into_inner());
116 databases.get(&name).cloned()
117 };
118
119 block_on_with_runtime(
120 async move {
121 match catalog.get_database(&name).await {
122 Ok(_) => Some(Arc::new(PaimonSchemaProvider::new(
123 Arc::clone(&catalog),
124 name,
125 dynamic_options,
126 temp_provider,
127 )) as Arc<dyn SchemaProvider>),
128 Err(paimon::Error::DatabaseNotExist { .. }) => {
129 if temp_provider.is_some() {
130 Some(Arc::new(PaimonSchemaProvider::new(
131 Arc::clone(&catalog),
132 name,
133 dynamic_options,
134 temp_provider,
135 )) as Arc<dyn SchemaProvider>)
136 } else {
137 None
138 }
139 }
140 Err(e) => {
141 log::error!("failed to get database '{}': {e}", name);
142 None
143 }
144 }
145 },
146 "paimon catalog access thread panicked",
147 )
148 }
149
150 fn register_schema(
151 &self,
152 name: &str,
153 _schema: Arc<dyn SchemaProvider>,
154 ) -> DFResult<Option<Arc<dyn SchemaProvider>>> {
155 let catalog = Arc::clone(&self.catalog);
156 let dynamic_options = Arc::clone(&self.dynamic_options);
157 let name = name.to_string();
158 block_on_with_runtime(
159 async move {
160 catalog
161 .create_database(&name, false, HashMap::new())
162 .await
163 .map_err(to_datafusion_error)?;
164 Ok(Some(Arc::new(PaimonSchemaProvider::new(
165 Arc::clone(&catalog),
166 name,
167 dynamic_options,
168 None,
169 )) as Arc<dyn SchemaProvider>))
170 },
171 "paimon catalog access thread panicked",
172 )
173 }
174
175 fn deregister_schema(
176 &self,
177 name: &str,
178 cascade: bool,
179 ) -> DFResult<Option<Arc<dyn SchemaProvider>>> {
180 let catalog = Arc::clone(&self.catalog);
181 let dynamic_options = Arc::clone(&self.dynamic_options);
182 let name = name.to_string();
183 block_on_with_runtime(
184 async move {
185 catalog
186 .drop_database(&name, false, cascade)
187 .await
188 .map_err(to_datafusion_error)?;
189 Ok(Some(Arc::new(PaimonSchemaProvider::new(
190 Arc::clone(&catalog),
191 name,
192 dynamic_options,
193 None,
194 )) as Arc<dyn SchemaProvider>))
195 },
196 "paimon catalog access thread panicked",
197 )
198 }
199}
200
201impl PaimonCatalogProvider {
202 pub fn register_temp_table(
208 &self,
209 database: &str,
210 table_name: &str,
211 table: Arc<dyn TableProvider>,
212 ) -> DFResult<()> {
213 let catalog = Arc::clone(&self.catalog);
215 let db = database.to_string();
216 let tbl = table_name.to_string();
217 let identifier = Identifier::new(db, tbl);
218 if let Ok(true) = block_on_with_runtime(
219 async move {
220 match catalog.get_table(&identifier).await {
221 Ok(_) => Ok::<bool, paimon::Error>(true),
222 Err(paimon::Error::TableNotExist { .. }) => Ok(false),
223 Err(_) => Ok(false),
224 }
225 },
226 "paimon catalog access thread panicked",
227 ) {
228 log::warn!(
229 "Temporary table '{database}.{table_name}' shadows an existing Paimon table"
230 );
231 }
232
233 let mut databases = self.temp_tables.write().unwrap_or_else(|e| e.into_inner());
235 let mem_database = databases
236 .entry(database.to_string())
237 .or_insert_with(|| Arc::new(MemorySchemaProvider::new()));
238
239 let old = mem_database.register_table(table_name.to_string(), table)?;
241 if old.is_some() {
242 return Err(plan_datafusion_err!(
243 "Temporary table '{database}.{table_name}' already exists"
244 ));
245 }
246 Ok(())
247 }
248
249 pub fn deregister_temp_table(
251 &self,
252 database: &str,
253 table_name: &str,
254 ) -> DFResult<Option<Arc<dyn TableProvider>>> {
255 let databases = self.temp_tables.read().unwrap_or_else(|e| e.into_inner());
256 let mem_database = databases
257 .get(database)
258 .ok_or_else(|| plan_datafusion_err!("Unknown temp database '{database}'"))?;
259 mem_database.deregister_table(table_name)
260 }
261
262 pub fn has_temp_table_database(&self, name: &str) -> bool {
264 self.temp_tables
265 .read()
266 .unwrap_or_else(|e| e.into_inner())
267 .contains_key(name)
268 }
269
270 pub fn temp_table_exist(&self, database: &str, table_name: &str) -> bool {
272 let databases = self.temp_tables.read().unwrap_or_else(|e| e.into_inner());
273 databases
274 .get(database)
275 .is_some_and(|db| db.table_exist(table_name))
276 }
277}
278
279pub struct PaimonSchemaProvider {
284 catalog: Arc<dyn Catalog>,
286 database: String,
288 dynamic_options: DynamicOptions,
290 temp_provider: Option<Arc<MemorySchemaProvider>>,
292}
293
294impl Debug for PaimonSchemaProvider {
295 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
296 f.debug_struct("PaimonSchemaProvider")
297 .field("database", &self.database)
298 .field("has_temp_provider", &self.temp_provider.is_some())
299 .finish()
300 }
301}
302
303impl PaimonSchemaProvider {
304 pub fn new(
306 catalog: Arc<dyn Catalog>,
307 database: String,
308 dynamic_options: DynamicOptions,
309 temp_provider: Option<Arc<MemorySchemaProvider>>,
310 ) -> Self {
311 PaimonSchemaProvider {
312 catalog,
313 database,
314 dynamic_options,
315 temp_provider,
316 }
317 }
318}
319
320#[async_trait]
321impl SchemaProvider for PaimonSchemaProvider {
322 fn as_any(&self) -> &dyn Any {
323 self
324 }
325
326 fn table_names(&self) -> Vec<String> {
327 let catalog = Arc::clone(&self.catalog);
328 let database = self.database.clone();
329 let mut names = block_on_with_runtime(
330 {
331 let db = database.clone();
332 async move {
333 match catalog.list_tables(&db).await {
334 Ok(names) => names,
335 Err(e) => {
336 log::error!("failed to list tables in '{}': {e}", db);
337 vec![]
338 }
339 }
340 }
341 },
342 "paimon catalog access thread panicked",
343 );
344
345 if let Some(temp) = &self.temp_provider {
346 names.extend(temp.table_names());
347 }
348
349 let mut seen = std::collections::HashSet::new();
350 names.retain(|name| seen.insert(name.clone()));
351
352 names
353 }
354
355 async fn table(&self, name: &str) -> DFResult<Option<Arc<dyn TableProvider>>> {
356 if let Some(temp) = &self.temp_provider {
357 if let Some(table) = temp.table(name).await? {
358 return Ok(Some(table));
359 }
360 }
361
362 let (base, system_name) = system_tables::split_object_name(name);
363 if let Some(system_name) = system_name {
364 return await_with_runtime(system_tables::load(
365 Arc::clone(&self.catalog),
366 self.database.clone(),
367 base.to_string(),
368 system_name.to_string(),
369 ))
370 .await;
371 }
372
373 let catalog = Arc::clone(&self.catalog);
374 let dynamic_options = Arc::clone(&self.dynamic_options);
375 let identifier = Identifier::new(self.database.clone(), base);
376 await_with_runtime(async move {
377 match catalog.get_table(&identifier).await {
378 Ok(table) => {
379 let opts = dynamic_options.read().unwrap().clone();
380 let table = if opts.is_empty() {
381 table
382 } else {
383 table.copy_with_options(opts)
384 };
385 let provider = PaimonTableProvider::try_new(table)?;
386 Ok(Some(Arc::new(provider) as Arc<dyn TableProvider>))
387 }
388 Err(paimon::Error::TableNotExist { .. }) => Ok(None),
389 Err(e) => Err(to_datafusion_error(e)),
390 }
391 })
392 .await
393 }
394
395 fn table_exist(&self, name: &str) -> bool {
396 if let Some(temp) = &self.temp_provider {
397 if temp.table_exist(name) {
398 return true;
399 }
400 }
401
402 let (base, system_name) = system_tables::split_object_name(name);
403 if let Some(system_name) = system_name {
404 if !system_tables::is_registered(system_name) {
405 return false;
406 }
407 }
408
409 let catalog = Arc::clone(&self.catalog);
410 let identifier = Identifier::new(self.database.clone(), base.to_string());
411 block_on_with_runtime(
412 async move {
413 match catalog.get_table(&identifier).await {
414 Ok(_) => true,
415 Err(paimon::Error::TableNotExist { .. }) => false,
416 Err(e) => {
417 log::error!("failed to check table '{}': {e}", identifier);
418 false
419 }
420 }
421 },
422 "paimon catalog access thread panicked",
423 )
424 }
425
426 fn register_table(
427 &self,
428 _name: String,
429 table: Arc<dyn TableProvider>,
430 ) -> DFResult<Option<Arc<dyn TableProvider>>> {
431 Ok(Some(table))
434 }
435
436 fn deregister_table(&self, name: &str) -> DFResult<Option<Arc<dyn TableProvider>>> {
437 let catalog = Arc::clone(&self.catalog);
438 let identifier = Identifier::new(self.database.clone(), name);
439 block_on_with_runtime(
440 async move {
441 let table = match catalog.get_table(&identifier).await {
443 Ok(t) => t,
444 Err(paimon::Error::TableNotExist { .. }) => return Ok(None),
445 Err(e) => return Err(to_datafusion_error(e)),
446 };
447 let provider = PaimonTableProvider::try_new(table)?;
448 catalog
449 .drop_table(&identifier, false)
450 .await
451 .map_err(to_datafusion_error)?;
452 Ok(Some(Arc::new(provider) as Arc<dyn TableProvider>))
453 },
454 "paimon catalog access thread panicked",
455 )
456 }
457}