Skip to content

Commit 291d718

Browse files
authored
feat(duckdb): add build_query (#832)
This gives us a function to call to inspect the SQL that we're creating (rather than relying on logs).
1 parent 3ebf2c6 commit 291d718

File tree

2 files changed

+37
-14
lines changed

2 files changed

+37
-14
lines changed

crates/duckdb/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- `build_query` ([#832](https://github.com/stac-utils/rustac/pull/832))
12+
913
## [0.2.1] - 2025-09-23
1014

1115
Update dependencies.

crates/duckdb/src/client.rs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,38 @@ impl Client {
202202
/// ```
203203
pub fn search_to_arrow(&self, href: &str, search: Search) -> Result<Vec<RecordBatch>> {
204204
// TODO can we return an iterator?
205+
if let Some((sql, params)) = self.build_query(href, search)? {
206+
log::debug!("duckdb sql: {sql}");
207+
let mut statement = self.prepare(&sql)?;
208+
statement
209+
.query_arrow(duckdb::params_from_iter(params))?
210+
.map(|record_batch| {
211+
let record_batch = if self.convert_wkb {
212+
stac::geoarrow::with_native_geometry(record_batch, "geometry")?
213+
} else {
214+
stac::geoarrow::add_wkb_metadata(record_batch, "geometry")?
215+
};
216+
Ok(record_batch)
217+
})
218+
.collect::<Result<_>>()
219+
} else {
220+
Ok(Vec::new())
221+
}
222+
}
205223

224+
/// Returns the SQL query string and parameters for this href and search object.
225+
///
226+
/// Returns `None` if we can _know_ that the query will return nothing.
227+
///
228+
/// # Examples
229+
///
230+
/// ```
231+
/// use stac_duckdb::Client;
232+
///
233+
/// let client = Client::new().unwrap();
234+
/// let (sql, params) = client.build_query("data/100-sentinel-2-items.parquet", Default::default()).unwrap().unwrap();
235+
/// ```
236+
pub fn build_query(&self, href: &str, search: Search) -> Result<Option<(String, Vec<Value>)>> {
206237
// Note that we pull out some fields early so we can avoid closing some search strings below.
207238

208239
if search.items.query.is_some() {
@@ -328,7 +359,7 @@ impl Client {
328359
let sql = expr.to_ducksql().map_err(Box::new)?;
329360
wheres.push(sql);
330361
} else {
331-
return Ok(Vec::new());
362+
return Ok(None);
332363
}
333364
}
334365

@@ -352,19 +383,7 @@ impl Client {
352383
self.format_parquet_href(href),
353384
suffix,
354385
);
355-
log::debug!("duckdb sql: {sql}");
356-
let mut statement = self.prepare(&sql)?;
357-
statement
358-
.query_arrow(duckdb::params_from_iter(params))?
359-
.map(|record_batch| {
360-
let record_batch = if self.convert_wkb {
361-
stac::geoarrow::with_native_geometry(record_batch, "geometry")?
362-
} else {
363-
stac::geoarrow::add_wkb_metadata(record_batch, "geometry")?
364-
};
365-
Ok(record_batch)
366-
})
367-
.collect::<Result<_>>()
386+
Ok(Some((sql, params)))
368387
}
369388

370389
fn format_parquet_href(&self, href: &str) -> String {

0 commit comments

Comments
 (0)