laminar_connectors/lakehouse/
delta_table_provider.rs1#[cfg(feature = "delta-lake")]
21use std::collections::HashMap;
22
23#[cfg(feature = "delta-lake")]
24use std::sync::Arc;
25
26#[cfg(feature = "delta-lake")]
27use datafusion::prelude::SessionContext;
28
29#[cfg(feature = "delta-lake")]
30use tracing::info;
31
32#[cfg(feature = "delta-lake")]
33use crate::error::ConnectorError;
34
35#[cfg(feature = "delta-lake")]
50#[allow(clippy::implicit_hasher)]
51pub async fn register_delta_table(
52 ctx: &SessionContext,
53 name: &str,
54 table_uri: &str,
55 storage_options: HashMap<String, String>,
56) -> Result<(), ConnectorError> {
57 use super::delta_io;
58
59 info!(
60 name,
61 table_uri, "registering Delta Lake table as TableProvider"
62 );
63
64 let table = delta_io::open_or_create_table(table_uri, storage_options, None).await?;
66
67 table
72 .update_datafusion_session(&ctx.state())
73 .map_err(|e| ConnectorError::Internal(format!("register Delta object store: {e}")))?;
74
75 let provider =
77 table.table_provider().build().await.map_err(|e| {
78 ConnectorError::Internal(format!("failed to build table provider: {e}"))
79 })?;
80
81 ctx.register_table(
82 datafusion::common::TableReference::bare(name),
83 Arc::new(provider),
84 )
85 .map_err(|e| {
86 ConnectorError::Internal(format!("failed to register Delta table '{name}': {e}"))
87 })?;
88
89 info!(name, table_uri, "Delta Lake table registered successfully");
90
91 Ok(())
92}
93
94#[cfg(all(test, feature = "delta-lake"))]
95mod tests {
96 use super::*;
97 use arrow_array::{Float64Array, Int64Array, StringArray};
98 use arrow_schema::{DataType, Field, Schema, SchemaRef};
99 use tempfile::TempDir;
100
101 fn test_schema() -> SchemaRef {
102 Arc::new(Schema::new(vec![
103 Field::new("id", DataType::Int64, false),
104 Field::new("name", DataType::Utf8, true),
105 Field::new("value", DataType::Float64, true),
106 ]))
107 }
108
109 #[allow(clippy::cast_precision_loss)]
110 fn test_batch(n: usize) -> arrow_array::RecordBatch {
111 let ids: Vec<i64> = (0..n as i64).collect();
112 let names: Vec<&str> = (0..n).map(|_| "test").collect();
113 let values: Vec<f64> = (0..n).map(|i| i as f64 * 1.5).collect();
114
115 arrow_array::RecordBatch::try_new(
116 test_schema(),
117 vec![
118 Arc::new(Int64Array::from(ids)),
119 Arc::new(StringArray::from(names)),
120 Arc::new(Float64Array::from(values)),
121 ],
122 )
123 .unwrap()
124 }
125
126 #[tokio::test]
127 async fn test_register_and_query_delta_table() {
128 use super::super::delta_io;
129 use deltalake::protocol::SaveMode;
130
131 let temp_dir = TempDir::new().unwrap();
132 let table_path = temp_dir.path().to_str().unwrap();
133
134 let schema = test_schema();
136 let table = delta_io::open_or_create_table(table_path, HashMap::new(), Some(&schema))
137 .await
138 .unwrap();
139
140 let batch = test_batch(10);
141 let (_table, version) = delta_io::write_batches(
142 table,
143 vec![batch],
144 SaveMode::Append,
145 None,
146 false,
147 None,
148 None,
149 )
150 .await
151 .unwrap();
152 assert_eq!(version, 1);
153
154 let ctx = SessionContext::new();
156 register_delta_table(&ctx, "MixedDelta", table_path, HashMap::new())
157 .await
158 .unwrap();
159
160 let df = ctx
161 .sql("SELECT COUNT(*) AS cnt FROM \"MixedDelta\"")
162 .await
163 .unwrap();
164 let results = df.collect().await.unwrap();
165
166 assert_eq!(results.len(), 1);
167 let count = results[0]
168 .column(0)
169 .as_any()
170 .downcast_ref::<Int64Array>()
171 .unwrap()
172 .value(0);
173 assert_eq!(count, 10);
174 }
175}