@@ -23,8 +23,24 @@ mod executor;
2323type PreparedStatement = Prepared < StatementConnection < SharedConnection < ' static > > > ;
2424type SharedPreparedStatement = Arc < Mutex < PreparedStatement > > ;
2525
26- fn collect_columns ( prepared : & mut PreparedStatement ) -> Result < Vec < OdbcColumn > , Error > {
27- let count = prepared. num_result_cols ( ) ?;
26+ fn collect_columns (
27+ prepared : & mut PreparedStatement ,
28+ parameter_count : usize ,
29+ allow_deferred_result_columns : bool ,
30+ ) -> Result < Vec < OdbcColumn > , Error > {
31+ let count = match prepared. num_result_cols ( ) {
32+ Ok ( count) => count,
33+ Err ( error) if allow_deferred_result_columns && parameter_count > 0 => {
34+ // Some ODBC drivers only expose result columns for parameterized
35+ // statements after values are bound and the statement is executed.
36+ // Row execution still gets authoritative metadata from the cursor;
37+ // describe() uses the strict path and will surface this error.
38+ log:: debug!( "ODBC prepare did not expose result columns: {error}" ) ;
39+ return Ok ( Vec :: new ( ) ) ;
40+ }
41+ Err ( error) => return Err ( error. into ( ) ) ,
42+ } ;
43+
2844 let mut columns = Vec :: with_capacity ( count as usize ) ;
2945 for i in 1 ..=count {
3046 columns. push ( describe_column ( prepared, i as u16 ) ?) ;
@@ -34,10 +50,13 @@ fn collect_columns(prepared: &mut PreparedStatement) -> Result<Vec<OdbcColumn>,
3450
3551fn collect_statement_metadata (
3652 prepared : & mut PreparedStatement ,
53+ allow_deferred_result_columns : bool ,
3754) -> Result < OdbcStatementMetadata , Error > {
55+ let parameters = usize:: from ( prepared. num_params ( ) ?) ;
56+
3857 Ok ( OdbcStatementMetadata {
39- columns : collect_columns ( prepared) ?,
40- parameters : usize :: from ( prepared . num_params ( ) ? ) ,
58+ columns : collect_columns ( prepared, parameters , allow_deferred_result_columns ) ?,
59+ parameters,
4160 } )
4261}
4362
@@ -216,7 +235,12 @@ impl OdbcConnection {
216235 Ok ( ( ) )
217236 }
218237
219- pub async fn prepare < ' a > ( & mut self , sql : & ' a str ) -> Result < OdbcStatement < ' a > , Error > {
238+ async fn prepare_with_metadata_policy < ' a > (
239+ & mut self ,
240+ sql : & ' a str ,
241+ store_to_cache : bool ,
242+ allow_deferred_result_columns : bool ,
243+ ) -> Result < OdbcStatement < ' a > , Error > {
220244 let cached = self
221245 . stmt_cache
222246 . get_mut ( sql)
@@ -227,7 +251,7 @@ impl OdbcConnection {
227251 let mut prepared = prepared. lock ( ) . map_err ( |_| {
228252 Error :: Protocol ( "ODBC prepare: failed to lock prepared statement" . into ( ) )
229253 } ) ?;
230- collect_statement_metadata ( & mut prepared)
254+ collect_statement_metadata ( & mut prepared, allow_deferred_result_columns )
231255 } )
232256 . await ?;
233257
@@ -242,12 +266,13 @@ impl OdbcConnection {
242266 let sql_clone = sql_owned. clone ( ) ;
243267 let ( prepared, metadata) = spawn_blocking ( move || {
244268 let mut prepared = conn. into_prepared ( & sql_clone) ?;
245- let metadata = collect_statement_metadata ( & mut prepared) ?;
269+ let metadata =
270+ collect_statement_metadata ( & mut prepared, allow_deferred_result_columns) ?;
246271 Ok :: < _ , Error > ( ( prepared, metadata) )
247272 } )
248273 . await ?;
249274
250- if self . stmt_cache . is_enabled ( ) {
275+ if store_to_cache && self . stmt_cache . is_enabled ( ) {
251276 self . stmt_cache
252277 . insert ( & sql_owned, Arc :: new ( Mutex :: new ( prepared) ) ) ;
253278 }
@@ -257,6 +282,17 @@ impl OdbcConnection {
257282 metadata,
258283 } )
259284 }
285+
286+ pub async fn prepare < ' a > ( & mut self , sql : & ' a str ) -> Result < OdbcStatement < ' a > , Error > {
287+ self . prepare_with_metadata_policy ( sql, true , true ) . await
288+ }
289+
290+ pub ( crate ) async fn describe_statement < ' a > (
291+ & mut self ,
292+ sql : & ' a str ,
293+ ) -> Result < OdbcStatement < ' a > , Error > {
294+ self . prepare_with_metadata_policy ( sql, false , false ) . await
295+ }
260296}
261297
262298pub ( crate ) enum MaybePrepared {
0 commit comments