11use crate :: errors:: ConnectorXPythonError ;
2+ use anyhow:: anyhow;
23use arrow:: record_batch:: RecordBatch ;
34use connectorx:: source_router:: SourceConn ;
45use connectorx:: { prelude:: * , sql:: CXQuery } ;
@@ -12,7 +13,7 @@ use std::sync::Arc;
1213
1314/// Python-exposed RecordBatch wrapper
1415#[ pyclass]
15- pub struct PyRecordBatch ( RecordBatch ) ;
16+ pub struct PyRecordBatch ( Option < RecordBatch > ) ;
1617
1718/// Python-exposed iterator over RecordBatches
1819#[ pyclass( module = "connectorx" ) ]
@@ -21,20 +22,22 @@ pub struct PyRecordBatchIterator(Box<dyn RecordBatchIterator>);
2122#[ pymethods]
2223impl PyRecordBatch {
2324 pub fn num_rows ( & self ) -> usize {
24- self . 0 . num_rows ( )
25+ self . 0 . as_ref ( ) . map_or ( 0 , |rb| rb . num_rows ( ) )
2526 }
2627
2728 pub fn num_columns ( & self ) -> usize {
28- self . 0 . num_columns ( )
29+ self . 0 . as_ref ( ) . map_or ( 0 , |rb| rb . num_columns ( ) )
2930 }
3031
3132 #[ throws( ConnectorXPythonError ) ]
32- pub fn to_ptrs < ' py > ( & self , py : Python < ' py > ) -> Bound < ' py , PyAny > {
33+ pub fn to_ptrs < ' py > ( & mut self , py : Python < ' py > ) -> Bound < ' py , PyAny > {
34+ // Convert the RecordBatch to a vector of pointers, once the RecordBatch is taken, it cannot be reached again.
35+ let rb = self
36+ . 0
37+ . take ( )
38+ . ok_or_else ( || anyhow ! ( "RecordBatch is None, cannot convert to pointers" ) ) ?;
3339 let ptrs = py. allow_threads (
34- || -> Result < ( Vec < String > , Vec < Vec < ( uintptr_t , uintptr_t ) > > ) , ConnectorXPythonError > {
35- let rbs = vec ! [ self . 0 . clone( ) ] ;
36- Ok ( to_ptrs ( rbs) )
37- } ,
40+ || -> Result < Vec < ( uintptr_t , uintptr_t ) > , ConnectorXPythonError > { Ok ( to_ptrs_rb ( rb) ) } ,
3841 ) ?;
3942 let obj: PyObject = ptrs. into_py ( py) ;
4043 obj. into_bound ( py)
@@ -65,7 +68,7 @@ impl PyRecordBatchIterator {
6568 ) -> PyResult < Option < Py < PyRecordBatch > > > {
6669 match slf. 0 . next_batch ( ) {
6770 Some ( rb) => {
68- let wrapped = PyRecordBatch ( rb ) ;
71+ let wrapped = PyRecordBatch ( Some ( rb ) ) ;
6972 let py_obj = Py :: new ( py, wrapped) ?;
7073 Ok ( Some ( py_obj) )
7174 }
@@ -118,6 +121,24 @@ pub fn get_arrow_rb_iter<'py>(
118121 obj. into_bound ( py)
119122}
120123
124+ pub fn to_ptrs_rb ( rb : RecordBatch ) -> Vec < ( uintptr_t , uintptr_t ) > {
125+ let mut cols = vec ! [ ] ;
126+
127+ for array in rb. columns ( ) . into_iter ( ) {
128+ let data = array. to_data ( ) ;
129+ let array_ptr = Arc :: new ( arrow:: ffi:: FFI_ArrowArray :: new ( & data) ) ;
130+ let schema_ptr = Arc :: new (
131+ arrow:: ffi:: FFI_ArrowSchema :: try_from ( data. data_type ( ) ) . expect ( "export schema c" ) ,
132+ ) ;
133+ cols. push ( (
134+ Arc :: into_raw ( array_ptr) as uintptr_t ,
135+ Arc :: into_raw ( schema_ptr) as uintptr_t ,
136+ ) ) ;
137+ }
138+
139+ cols
140+ }
141+
121142pub fn to_ptrs ( rbs : Vec < RecordBatch > ) -> ( Vec < String > , Vec < Vec < ( uintptr_t , uintptr_t ) > > ) {
122143 if rbs. is_empty ( ) {
123144 return ( vec ! [ ] , vec ! [ ] ) ;
@@ -132,21 +153,7 @@ pub fn to_ptrs(rbs: Vec<RecordBatch>) -> (Vec<String>, Vec<Vec<(uintptr_t, uintp
132153 . collect ( ) ;
133154
134155 for rb in rbs. into_iter ( ) {
135- let mut cols = vec ! [ ] ;
136-
137- for array in rb. columns ( ) . into_iter ( ) {
138- let data = array. to_data ( ) ;
139- let array_ptr = Arc :: new ( arrow:: ffi:: FFI_ArrowArray :: new ( & data) ) ;
140- let schema_ptr = Arc :: new (
141- arrow:: ffi:: FFI_ArrowSchema :: try_from ( data. data_type ( ) ) . expect ( "export schema c" ) ,
142- ) ;
143- cols. push ( (
144- Arc :: into_raw ( array_ptr) as uintptr_t ,
145- Arc :: into_raw ( schema_ptr) as uintptr_t ,
146- ) ) ;
147- }
148-
149- result. push ( cols) ;
156+ result. push ( to_ptrs_rb ( rb) ) ;
150157 }
151158 ( names, result)
152159}
0 commit comments