vortex_tui/
datafusion_helper.rs1use std::sync::Arc;
7
8use arrow_array::Array as ArrowArray;
9use arrow_array::RecordBatch;
10use datafusion::datasource::listing::ListingOptions;
11use datafusion::datasource::listing::ListingTable;
12use datafusion::datasource::listing::ListingTableConfig;
13use datafusion::datasource::listing::ListingTableUrl;
14use datafusion::prelude::SessionContext;
15use vortex::session::VortexSession;
16use vortex_datafusion::VortexFormat;
17
18pub async fn execute_vortex_query(
27 session: &VortexSession,
28 file_path: &str,
29 sql: &str,
30) -> Result<Vec<RecordBatch>, String> {
31 let ctx = create_vortex_context(session, file_path).await?;
32
33 let df = ctx.sql(sql).await.map_err(|e| format!("SQL error: {e}"))?;
34
35 df.collect()
36 .await
37 .map_err(|e| format!("Query execution error: {e}"))
38}
39
40pub async fn create_vortex_context(
46 session: &VortexSession,
47 file_path: &str,
48) -> Result<SessionContext, String> {
49 let ctx = SessionContext::new();
50 let format = Arc::new(VortexFormat::new(session.clone()));
51
52 let canonical = std::fs::canonicalize(file_path)
59 .map_err(|e| format!("Failed to resolve file path '{file_path}': {e}"))?;
60 let canonical = canonical
61 .to_str()
62 .ok_or_else(|| "Resolved path is not valid UTF-8".to_string())?;
63
64 let table_url =
65 ListingTableUrl::parse(canonical).map_err(|e| format!("Failed to parse file path: {e}"))?;
66
67 let listing_options = ListingOptions::new(format)
73 .with_file_extension("")
74 .with_session_config_options(ctx.state().config());
75
76 let config = ListingTableConfig::new(table_url)
77 .with_listing_options(listing_options)
78 .infer_schema(&ctx.state())
79 .await
80 .map_err(|e| format!("Failed to infer schema: {e}"))?;
81
82 let listing_table = Arc::new(
83 ListingTable::try_new(config).map_err(|e| format!("Failed to create table: {e}"))?,
84 );
85
86 ctx.register_table("data", listing_table)
87 .map_err(|e| format!("Failed to register table: {e}"))?;
88
89 Ok(ctx)
90}
91
92#[expect(clippy::unwrap_used)]
99pub fn arrow_value_to_json(array: &dyn ArrowArray, idx: usize) -> serde_json::Value {
100 use arrow_array::*;
101 use arrow_schema::DataType;
102
103 if array.is_null(idx) {
104 return serde_json::Value::Null;
105 }
106
107 match array.data_type() {
108 DataType::Null => serde_json::Value::Null,
109 DataType::Boolean => {
110 let arr = array.as_any().downcast_ref::<BooleanArray>().unwrap();
111 serde_json::Value::Bool(arr.value(idx))
112 }
113 DataType::Int8 => {
114 let arr = array.as_any().downcast_ref::<Int8Array>().unwrap();
115 serde_json::json!(arr.value(idx))
116 }
117 DataType::Int16 => {
118 let arr = array.as_any().downcast_ref::<Int16Array>().unwrap();
119 serde_json::json!(arr.value(idx))
120 }
121 DataType::Int32 => {
122 let arr = array.as_any().downcast_ref::<Int32Array>().unwrap();
123 serde_json::json!(arr.value(idx))
124 }
125 DataType::Int64 => {
126 let arr = array.as_any().downcast_ref::<Int64Array>().unwrap();
127 serde_json::json!(arr.value(idx))
128 }
129 DataType::UInt8 => {
130 let arr = array.as_any().downcast_ref::<UInt8Array>().unwrap();
131 serde_json::json!(arr.value(idx))
132 }
133 DataType::UInt16 => {
134 let arr = array.as_any().downcast_ref::<UInt16Array>().unwrap();
135 serde_json::json!(arr.value(idx))
136 }
137 DataType::UInt32 => {
138 let arr = array.as_any().downcast_ref::<UInt32Array>().unwrap();
139 serde_json::json!(arr.value(idx))
140 }
141 DataType::UInt64 => {
142 let arr = array.as_any().downcast_ref::<UInt64Array>().unwrap();
143 serde_json::json!(arr.value(idx))
144 }
145 DataType::Float16 => {
146 let arr = array.as_any().downcast_ref::<Float16Array>().unwrap();
147 serde_json::json!(arr.value(idx).to_f32())
148 }
149 DataType::Float32 => {
150 let arr = array.as_any().downcast_ref::<Float32Array>().unwrap();
151 serde_json::json!(arr.value(idx))
152 }
153 DataType::Float64 => {
154 let arr = array.as_any().downcast_ref::<Float64Array>().unwrap();
155 serde_json::json!(arr.value(idx))
156 }
157 DataType::Utf8 => {
158 let arr = array.as_any().downcast_ref::<StringArray>().unwrap();
159 serde_json::Value::String(arr.value(idx).to_string())
160 }
161 DataType::LargeUtf8 => {
162 let arr = array.as_any().downcast_ref::<LargeStringArray>().unwrap();
163 serde_json::Value::String(arr.value(idx).to_string())
164 }
165 DataType::Utf8View => {
166 let arr = array.as_any().downcast_ref::<StringViewArray>().unwrap();
167 serde_json::Value::String(arr.value(idx).to_string())
168 }
169 DataType::Binary => {
170 let arr = array.as_any().downcast_ref::<BinaryArray>().unwrap();
171 let hex: String = arr.value(idx).iter().map(|b| format!("{b:02x}")).collect();
172 serde_json::Value::String(hex)
173 }
174 DataType::LargeBinary => {
175 let arr = array.as_any().downcast_ref::<LargeBinaryArray>().unwrap();
176 let hex: String = arr.value(idx).iter().map(|b| format!("{b:02x}")).collect();
177 serde_json::Value::String(hex)
178 }
179 DataType::BinaryView => {
180 let arr = array.as_any().downcast_ref::<BinaryViewArray>().unwrap();
181 let hex: String = arr.value(idx).iter().map(|b| format!("{b:02x}")).collect();
182 serde_json::Value::String(hex)
183 }
184 DataType::Date32 => {
185 let arr = array.as_any().downcast_ref::<Date32Array>().unwrap();
186 serde_json::json!(arr.value(idx))
187 }
188 DataType::Date64 => {
189 let arr = array.as_any().downcast_ref::<Date64Array>().unwrap();
190 serde_json::json!(arr.value(idx))
191 }
192 DataType::Timestamp(..) => {
193 if let Some(arr) = array.as_any().downcast_ref::<TimestampMicrosecondArray>() {
194 serde_json::json!(arr.value(idx))
195 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampMillisecondArray>() {
196 serde_json::json!(arr.value(idx))
197 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampSecondArray>() {
198 serde_json::json!(arr.value(idx))
199 } else if let Some(arr) = array.as_any().downcast_ref::<TimestampNanosecondArray>() {
200 serde_json::json!(arr.value(idx))
201 } else {
202 serde_json::Value::String("<timestamp>".to_string())
203 }
204 }
205 DataType::Decimal128(..) => {
206 let arr = array.as_any().downcast_ref::<Decimal128Array>().unwrap();
207 serde_json::Value::String(arr.value_as_string(idx))
208 }
209 DataType::Decimal256(..) => {
210 let arr = array.as_any().downcast_ref::<Decimal256Array>().unwrap();
211 serde_json::Value::String(arr.value_as_string(idx))
212 }
213 DataType::List(_) => {
214 let arr = array.as_any().downcast_ref::<ListArray>().unwrap();
215 let value_arr = arr.value(idx);
216 let elements: Vec<serde_json::Value> = (0..value_arr.len())
217 .map(|i| arrow_value_to_json(value_arr.as_ref(), i))
218 .collect();
219 serde_json::Value::Array(elements)
220 }
221 DataType::LargeList(_) => {
222 let arr = array.as_any().downcast_ref::<LargeListArray>().unwrap();
223 let value_arr = arr.value(idx);
224 let elements: Vec<serde_json::Value> = (0..value_arr.len())
225 .map(|i| arrow_value_to_json(value_arr.as_ref(), i))
226 .collect();
227 serde_json::Value::Array(elements)
228 }
229 DataType::Struct(_) => {
230 let arr = array.as_any().downcast_ref::<StructArray>().unwrap();
231 let mut obj = serde_json::Map::new();
232 for (i, field) in arr.fields().iter().enumerate() {
233 let col = arr.column(i);
234 obj.insert(field.name().clone(), arrow_value_to_json(col.as_ref(), idx));
235 }
236 serde_json::Value::Object(obj)
237 }
238 _ => {
239 serde_json::Value::String(format!("<{}>", array.data_type()))
241 }
242 }
243}
244
245pub fn json_value_to_display(value: serde_json::Value) -> String {
251 match value {
252 serde_json::Value::Null => "NULL".to_string(),
253 serde_json::Value::String(s) => s,
254 other => other.to_string(),
255 }
256}