Skip to content

Commit c1ba98e

Browse files
fix(rust): pass manifest through FFI metadata functions
The metadata FFI functions (get_catalogs, get_schemas, get_tables, etc.) were discarding the manifest from ExecuteResult. Now they use export_execute_result() which passes the manifest to ResultReaderAdapter, ensuring databricks.* field metadata is attached to the Arrow schema for all query paths, not just Statement::execute(). Co-authored-by: Isaac
1 parent 3785a5c commit c1ba98e

File tree

2 files changed

+59
-23
lines changed

2 files changed

+59
-23
lines changed

rust/examples/metadata_propagation_test.rs

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,27 @@ fn main() {
105105
CAST(0 AS DECIMAL(38,0)) as dec_38_0
106106
"#,
107107
&[
108-
("dec_10_2", "DECIMAL", "DECIMAL(10,2)", Some("10"), Some("2")),
109-
("dec_18_5", "DECIMAL", "DECIMAL(18,5)", Some("18"), Some("5")),
110-
("dec_38_0", "DECIMAL", "DECIMAL(38,0)", Some("38"), Some("0")),
108+
(
109+
"dec_10_2",
110+
"DECIMAL",
111+
"DECIMAL(10,2)",
112+
Some("10"),
113+
Some("2"),
114+
),
115+
(
116+
"dec_18_5",
117+
"DECIMAL",
118+
"DECIMAL(18,5)",
119+
Some("18"),
120+
Some("5"),
121+
),
122+
(
123+
"dec_38_0",
124+
"DECIMAL",
125+
"DECIMAL(38,0)",
126+
Some("38"),
127+
Some("0"),
128+
),
111129
],
112130
);
113131

@@ -137,7 +155,13 @@ fn main() {
137155
&[
138156
("array_col", "ARRAY", "ARRAY<INT>", None, None),
139157
("map_col", "MAP", "MAP<STRING, INT>", None, None),
140-
("struct_col", "STRUCT", "STRUCT<x: INT NOT NULL, y: STRING NOT NULL>", None, None),
158+
(
159+
"struct_col",
160+
"STRUCT",
161+
"STRUCT<x: INT NOT NULL, y: STRING NOT NULL>",
162+
None,
163+
None,
164+
),
141165
],
142166
);
143167

@@ -158,13 +182,14 @@ fn main() {
158182
}
159183
}
160184

185+
/// Expected column metadata: (field_name, type_name, type_text, precision, scale)
186+
type ExpectedColumn<'a> = (&'a str, &'a str, &'a str, Option<&'a str>, Option<&'a str>);
187+
161188
/// Run a query and verify that the Arrow field metadata matches expectations.
162-
///
163-
/// Each expected entry is: (field_name, type_name, type_text, precision, scale)
164189
fn run_test(
165190
conn: &mut impl ConnectionTrait,
166191
sql: &str,
167-
expected: &[(&str, &str, &str, Option<&str>, Option<&str>)],
192+
expected: &[ExpectedColumn<'_>],
168193
) -> bool {
169194
let mut stmt = conn.new_statement().expect("Failed to create statement");
170195
stmt.set_sql_query(sql).expect("Failed to set query");
@@ -203,10 +228,16 @@ fn run_test(
203228
if !ok {
204229
all_ok = false;
205230
if !name_ok {
206-
println!(" expected type_name={exp_type_name}, got {:?}", got_name);
231+
println!(
232+
" expected type_name={exp_type_name}, got {:?}",
233+
got_name
234+
);
207235
}
208236
if !text_ok {
209-
println!(" expected type_text={exp_type_text}, got {:?}", got_text);
237+
println!(
238+
" expected type_text={exp_type_text}, got {:?}",
239+
got_text
240+
);
210241
}
211242
if !prec_ok {
212243
println!(

rust/src/ffi/metadata.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
use crate::ffi::error::{clear_last_error, set_error_from_result, set_last_error, FfiStatus};
2727
use crate::metadata::sql::SqlCommandBuilder;
2828
use crate::reader::{EmptyReader, ResultReader, ResultReaderAdapter};
29-
use crate::types::sea::ExecuteParams;
29+
use crate::types::sea::{ExecuteParams, ResultManifest};
3030
use crate::Connection;
3131
use arrow::ffi_stream::FFI_ArrowArrayStream;
3232
use arrow_array::RecordBatchReader;
@@ -85,17 +85,22 @@ unsafe fn c_str_to_str<'a>(ptr: *const c_char) -> std::result::Result<&'a str, (
8585

8686
/// Export a `ResultReader` as an `FFI_ArrowArrayStream` via `ResultReaderAdapter`.
8787
///
88+
/// When `manifest` is provided, `databricks.*` field-level metadata from the SEA
89+
/// manifest is attached to the Arrow schema. Pass `None` for paths that construct
90+
/// a reader directly (e.g. `EmptyReader`).
91+
///
8892
/// The caller is responsible for releasing the stream.
8993
fn export_reader(
9094
reader: Box<dyn ResultReader + Send>,
95+
manifest: Option<&ResultManifest>,
9196
out: *mut FFI_ArrowArrayStream,
9297
) -> FfiStatus {
9398
if out.is_null() {
9499
set_last_error("Output stream pointer is null", "HY009", -1);
95100
return FfiStatus::InvalidHandle;
96101
}
97102

98-
let adapter = match ResultReaderAdapter::new(reader, None) {
103+
let adapter = match ResultReaderAdapter::new(reader, manifest) {
99104
Ok(a) => a,
100105
Err(e) => return set_error_from_result(&e),
101106
};
@@ -153,7 +158,7 @@ pub unsafe extern "C" fn metadata_get_catalogs(
153158
.runtime_handle()
154159
.block_on(conn.client().list_catalogs(conn.session_id()))
155160
{
156-
Ok(result) => export_reader(result.reader, out),
161+
Ok(result) => export_reader(result.reader, result.manifest.as_ref(), out),
157162
Err(e) => set_error_from_result(&e),
158163
}
159164
}))
@@ -189,7 +194,7 @@ pub unsafe extern "C" fn metadata_get_schemas(
189194
catalog,
190195
schema_pattern,
191196
)) {
192-
Ok(result) => export_reader(result.reader, out),
197+
Ok(result) => export_reader(result.reader, result.manifest.as_ref(), out),
193198
Err(e) => set_error_from_result(&e),
194199
}
195200
}))
@@ -240,7 +245,7 @@ pub unsafe extern "C" fn metadata_get_tables(
240245
table_pattern,
241246
types_vec.as_deref(),
242247
)) {
243-
Ok(result) => export_reader(result.reader, out),
248+
Ok(result) => export_reader(result.reader, result.manifest.as_ref(), out),
244249
Err(e) => set_error_from_result(&e),
245250
}
246251
}))
@@ -288,7 +293,7 @@ pub unsafe extern "C" fn metadata_get_columns(
288293
None => {
289294
let reader: Box<dyn ResultReader + Send> =
290295
Box::new(EmptyReader::new(Arc::new(Schema::empty())));
291-
return export_reader(reader, out);
296+
return export_reader(reader, None, out);
292297
}
293298
};
294299

@@ -299,7 +304,7 @@ pub unsafe extern "C" fn metadata_get_columns(
299304
table_pattern,
300305
column_pattern,
301306
)) {
302-
Ok(result) => export_reader(result.reader, out),
307+
Ok(result) => export_reader(result.reader, result.manifest.as_ref(), out),
303308
Err(e) => set_error_from_result(&e),
304309
}
305310
}))
@@ -342,7 +347,7 @@ pub unsafe extern "C" fn metadata_get_primary_keys(
342347
&sql,
343348
&ExecuteParams::default(),
344349
)) {
345-
Ok(result) => export_reader(result.reader, out),
350+
Ok(result) => export_reader(result.reader, result.manifest.as_ref(), out),
346351
Err(e) => set_error_from_result(&e),
347352
}
348353
}))
@@ -385,7 +390,7 @@ pub unsafe extern "C" fn metadata_get_foreign_keys(
385390
&sql,
386391
&ExecuteParams::default(),
387392
)) {
388-
Ok(result) => export_reader(result.reader, out),
393+
Ok(result) => export_reader(result.reader, result.manifest.as_ref(), out),
389394
Err(e) => set_error_from_result(&e),
390395
}
391396
}))
@@ -407,7 +412,7 @@ mod tests {
407412
RecordBatch::try_new(schema, vec![Arc::new(StringArray::from(vec!["hello"]))]).unwrap();
408413

409414
let reader: Box<dyn ResultReader + Send> = Box::new(MockReader::new(vec![batch]));
410-
let status = export_reader(reader, std::ptr::null_mut());
415+
let status = export_reader(reader, None, std::ptr::null_mut());
411416
assert_eq!(status, FfiStatus::InvalidHandle);
412417
}
413418

@@ -419,15 +424,15 @@ mod tests {
419424

420425
let reader: Box<dyn ResultReader + Send> = Box::new(MockReader::new(vec![batch]));
421426
let mut stream = FFI_ArrowArrayStream::empty();
422-
let status = export_reader(reader, &mut stream);
427+
let status = export_reader(reader, None, &mut stream);
423428
assert_eq!(status, FfiStatus::Success);
424429
}
425430

426431
#[test]
427432
fn test_export_reader_empty() {
428433
let reader: Box<dyn ResultReader + Send> = Box::new(MockReader::new(vec![]));
429434
let mut stream = FFI_ArrowArrayStream::empty();
430-
let status = export_reader(reader, &mut stream);
435+
let status = export_reader(reader, None, &mut stream);
431436
assert_eq!(status, FfiStatus::Success);
432437
}
433438

@@ -443,15 +448,15 @@ mod tests {
443448

444449
let reader: Box<dyn ResultReader + Send> = Box::new(MockReader::new(vec![batch1, batch2]));
445450
let mut stream = FFI_ArrowArrayStream::empty();
446-
let status = export_reader(reader, &mut stream);
451+
let status = export_reader(reader, None, &mut stream);
447452
assert_eq!(status, FfiStatus::Success);
448453
}
449454

450455
#[test]
451456
fn test_export_reader_schema_error() {
452457
let reader: Box<dyn ResultReader + Send> = Box::new(SchemaErrorReader);
453458
let mut stream = FFI_ArrowArrayStream::empty();
454-
let status = export_reader(reader, &mut stream);
459+
let status = export_reader(reader, None, &mut stream);
455460
assert_eq!(status, FfiStatus::Error);
456461
}
457462

0 commit comments

Comments
 (0)