Мне часто приходится получать большие объемы данных с серверов Microsoft SQL для обработки с помощью Polars в Rust, и в соответствии с политикой безопасности предприятия я более или менее вынужден использовать ODBC для этих подключений. Требование ODBC не позволяет мне использовать зрелые и функциональные библиотеки, такие как ConnectorX. Я могу подключаться и эффективно считывать результаты запроса в объекты RecordBatch из Arrow, используя стрелку_odbc, но мне не удалось преобразовать эти объекты RecordBatch в кадры данных Polars.
Поскольку фактические компоненты данных RecordBatch и Series имеют одинаковое базовое представление, я подумал, что можно создать DataFrame из нулевой копии RecordBatch.
Однако в columns.push(Series::from_arrow(&schema.fields().get(i).unwrap().name(), *column)?); я получаю ошибку:
mismatched types
expected struct `std::boxed::Box<(dyn polars::export::polars_arrow::array::Array + 'static)>`
found struct `Arc<dyn arrow::array::Array>`
У меня сложилось впечатление, что Arc<dyn Array> — это ArrayRef, возможно, реальная проблема в том, что у меня есть Arc<dyn arrow::array::Array>, а Series::from_arrow() ожидает Polars Arc<Array>? Если да, то как мне это решить?
Мой полный код приведен ниже для справки.
use arrow_odbc::{odbc_api::{Environment, ConnectionOptions}, OdbcReaderBuilder};
use arrow::record_batch::RecordBatch;
use polars::prelude::*;
use anyhow::Result;
const CONNECTION_STRING: &str = "...";
pub fn test() -> Result<()> {
let odbc_environment = Environment::new()?;
let connection = odbc_environment.connect_with_connection_string(
CONNECTION_STRING,
ConnectionOptions::default()
)?;
let cursor = connection.execute("SELECT * FROM Backcast_Power_Plant_Map", ())?.unwrap();
let arrow_record_batches = OdbcReaderBuilder::new().build(cursor)?;
fn record_batch_to_dataframe(batch: &RecordBatch) -> Result<DataFrame, PolarsError> {
let schema = batch.schema();
let mut columns = Vec::with_capacity(batch.num_columns());
for (i, column) in batch.columns().iter().enumerate() {
columns.push(Series::from_arrow(&schema.fields().get(i).unwrap().name(), *column)?);
}
Ok(DataFrame::from_iter(columns))
}
for batch in arrow_record_batches {
dbg!(record_batch_to_dataframe(&batch?));
}
Ok(())
}

Оказывается, polars и arrow-odbc используют разные ящики со стрелами: polars использует полярную стрелу , а arrow-odbc использует стрелку . Тип массива первого — Box<dyn Polars_arrow::array::Array> , а второго — тип ArrayRef , который является псевдонимом для Arc<dyn стрелка::array::Array> .
К счастью для нас, в ящике polars-arrow есть уровень совместимости. Вы можете конвертировать между двумя типами (и другими) с помощью From impls:
use anyhow::Result;
use arrow::record_batch::RecordBatch;
use arrow_odbc::{
odbc_api::{ConnectionOptions, Environment},
OdbcReaderBuilder,
};
use polars::prelude::*;
const CONNECTION_STRING: &str = "...";
pub fn test() -> Result<()> {
let odbc_environment = Environment::new()?;
let connection = odbc_environment
.connect_with_connection_string(CONNECTION_STRING, ConnectionOptions::default())?;
let cursor = connection
.execute("SELECT * FROM Backcast_Power_Plant_Map", ())?
.unwrap();
let arrow_record_batches = OdbcReaderBuilder::new().build(cursor)?;
fn record_batch_to_dataframe(batch: &RecordBatch) -> Result<DataFrame, PolarsError> {
let schema = batch.schema();
let mut columns = Vec::with_capacity(batch.num_columns());
for (i, column) in batch.columns().iter().enumerate() {
let arrow = Box::<dyn polars_arrow::array::Array>::from(&**column);
columns.push(Series::from_arrow(
&schema.fields().get(i).unwrap().name(),
arrow,
)?);
}
Ok(DataFrame::from_iter(columns))
}
for batch in arrow_record_batches {
dbg!(record_batch_to_dataframe(&batch?));
}
Ok(())
}
Обратите внимание, что для этого требуется polars-arrow с функцией arrow_rs в качестве зависимости.
Насколько я могу судить, это не копирует фактические данные.
@PatBlinds Просмотр функций и свойств docs.rs... По крайней мере, я так сделал.
После согласования зависимой версии и автономной версии Polars-Arrow, которая работала отлично, и профиль памяти определенно выглядит так, как будто копирования не происходит, спасибо. Как мне было бы лучше всего доказать, что Поляры на самом деле не полагаются на ящик со стрелами для получения моих собственных знаний? Просто сверлишь имплиты?