diff --git a/diesel/src/row.rs b/diesel/src/row.rs index a6bbcc3d31bb..b084e0852dbf 100644 --- a/diesel/src/row.rs +++ b/diesel/src/row.rs @@ -148,6 +148,18 @@ where } } +/// A row that can be turned into an owned version +#[diesel_derives::__diesel_public_if( + feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes" +)] +pub trait IntoOwnedRow<'a, DB: Backend>: Row<'a, DB> { + /// The owned version of the row + type OwnedRow: Row<'a, DB> + Send + 'static; + + /// Turn the row into its owned version + fn into_owned(self) -> Self::OwnedRow; +} + // These traits are not part of the public API // because: // * we want to control who can implement `Row` (for `RowSealed`) diff --git a/diesel/src/sqlite/connection/mod.rs b/diesel/src/sqlite/connection/mod.rs index dab81bc92fbf..38fd7e6ff6a1 100644 --- a/diesel/src/sqlite/connection/mod.rs +++ b/diesel/src/sqlite/connection/mod.rs @@ -2,6 +2,7 @@ extern crate libsqlite3_sys as ffi; mod bind_collector; mod functions; +mod owned_row; mod raw; mod row; mod serialized_database; diff --git a/diesel/src/sqlite/connection/owned_row.rs b/diesel/src/sqlite/connection/owned_row.rs new file mode 100644 index 000000000000..700c8b341d71 --- /dev/null +++ b/diesel/src/sqlite/connection/owned_row.rs @@ -0,0 +1,90 @@ +use super::sqlite_value::{OwnedSqliteValue, SqliteValue}; +use crate::backend::Backend; +use crate::row::{Field, PartialRow, Row, RowIndex, RowSealed}; +use crate::sqlite::Sqlite; + +#[derive(Debug)] +pub struct OwnedSqliteRow { + pub(super) values: Vec>, + column_names: Box<[Option]>, +} + +impl OwnedSqliteRow { + pub(super) fn new( + values: Vec>, + column_names: Box<[Option]>, + ) -> Self { + OwnedSqliteRow { + values, + column_names, + } + } +} + +impl RowSealed for OwnedSqliteRow {} + +impl<'a> Row<'a, Sqlite> for OwnedSqliteRow { + type Field<'field> = OwnedSqliteField<'field> where 'a: 'field, Self: 'field; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.values.len() + } + + fn get<'field, I>(&'field self, idx: I) -> Option> + where + 'a: 'field, + Self: RowIndex, + { + let idx = self.idx(idx)?; + Some(OwnedSqliteField { + row: self, + col_idx: i32::try_from(idx).ok()?, + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow<'_, Self::InnerPartialRow> { + PartialRow::new(self, range) + } +} + +impl RowIndex for OwnedSqliteRow { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count() { + Some(idx) + } else { + None + } + } +} + +impl<'idx> RowIndex<&'idx str> for OwnedSqliteRow { + fn idx(&self, field_name: &'idx str) -> Option { + self.column_names + .iter() + .position(|n| n.as_ref().map(|s| s as &str) == Some(field_name)) + } +} + +#[allow(missing_debug_implementations)] +pub struct OwnedSqliteField<'row> { + pub(super) row: &'row OwnedSqliteRow, + pub(super) col_idx: i32, +} + +impl<'row> Field<'row, Sqlite> for OwnedSqliteField<'row> { + fn field_name(&self) -> Option<&str> { + self.row + .column_names + .get(self.col_idx as usize) + .and_then(|o| o.as_ref().map(|s| s.as_ref())) + } + + fn is_null(&self) -> bool { + self.value().is_none() + } + + fn value(&self) -> Option<::RawValue<'row>> { + SqliteValue::from_owned_row(self.row, self.col_idx) + } +} diff --git a/diesel/src/sqlite/connection/row.rs b/diesel/src/sqlite/connection/row.rs index 585e0d69096c..6fe8262b431d 100644 --- a/diesel/src/sqlite/connection/row.rs +++ b/diesel/src/sqlite/connection/row.rs @@ -1,10 +1,11 @@ use std::cell::{Ref, RefCell}; use std::rc::Rc; +use super::owned_row::OwnedSqliteRow; use super::sqlite_value::{OwnedSqliteValue, SqliteValue}; use super::stmt::StatementUse; use crate::backend::Backend; -use crate::row::{Field, PartialRow, Row, RowIndex, RowSealed}; +use crate::row::{Field, IntoOwnedRow, PartialRow, Row, RowIndex, RowSealed}; use crate::sqlite::Sqlite; #[allow(missing_debug_implementations)] @@ -21,6 +22,14 @@ pub(super) enum PrivateSqliteRow<'stmt, 'query> { }, } +impl<'stmt> IntoOwnedRow<'stmt, Sqlite> for SqliteRow<'stmt, '_> { + type OwnedRow = OwnedSqliteRow; + + fn into_owned(self) -> Self::OwnedRow { + self.inner.borrow().moveable() + } +} + impl<'stmt, 'query> PrivateSqliteRow<'stmt, 'query> { pub(super) fn duplicate( &mut self, @@ -58,6 +67,37 @@ impl<'stmt, 'query> PrivateSqliteRow<'stmt, 'query> { }, } } + + pub(super) fn moveable(&self) -> OwnedSqliteRow { + match self { + PrivateSqliteRow::Direct(stmt) => { + let column_names: Box<[Option]> = (0..stmt.column_count()) + .map(|idx| stmt.field_name(idx).map(|s| s.to_owned())) + .collect::>() + .into_boxed_slice(); + OwnedSqliteRow::new( + (0..stmt.column_count()) + .map(|idx| stmt.copy_value(idx)) + .collect(), + column_names, + ) + } + PrivateSqliteRow::Duplicated { + values, + column_names, + } => OwnedSqliteRow::new( + values + .iter() + .map(|v| v.as_ref().map(|v| v.duplicate())) + .collect(), + (*column_names) + .iter() + .map(|s| s.to_owned()) + .collect::>() + .into_boxed_slice(), + ), + } + } } impl<'stmt, 'query> RowSealed for SqliteRow<'stmt, 'query> {} diff --git a/diesel/src/sqlite/connection/sqlite_value.rs b/diesel/src/sqlite/connection/sqlite_value.rs index d8e4d5bdfe77..7210d104bb4c 100644 --- a/diesel/src/sqlite/connection/sqlite_value.rs +++ b/diesel/src/sqlite/connection/sqlite_value.rs @@ -7,6 +7,7 @@ use std::{slice, str}; use crate::sqlite::SqliteType; +use super::owned_row::OwnedSqliteRow; use super::row::PrivateSqliteRow; /// Raw sqlite value as received from the database @@ -18,7 +19,7 @@ pub struct SqliteValue<'row, 'stmt, 'query> { // This field exists to ensure that nobody // can modify the underlying row while we are // holding a reference to some row value here - _row: Ref<'row, PrivateSqliteRow<'stmt, 'query>>, + _row: Option>>, // we extract the raw value pointer as part of the constructor // to safe the match statements for each method // According to benchmarks this leads to a ~20-30% speedup @@ -29,6 +30,7 @@ pub struct SqliteValue<'row, 'stmt, 'query> { value: NonNull, } +#[derive(Debug)] #[repr(transparent)] pub(super) struct OwnedSqliteValue { pub(super) value: NonNull, @@ -40,6 +42,10 @@ impl Drop for OwnedSqliteValue { } } +// Unsafe Send impl safe since sqlite3_value is built with sqlite3_value_dup +// see https://www.sqlite.org/c3ref/value.html +unsafe impl Send for OwnedSqliteValue {} + impl<'row, 'stmt, 'query> SqliteValue<'row, 'stmt, 'query> { pub(super) fn new( row: Ref<'row, PrivateSqliteRow<'stmt, 'query>>, @@ -52,7 +58,27 @@ impl<'row, 'stmt, 'query> SqliteValue<'row, 'stmt, 'query> { } }; - let ret = Self { _row: row, value }; + let ret = Self { + _row: Some(row), + value, + }; + if ret.value_type().is_none() { + None + } else { + Some(ret) + } + } + + pub(super) fn from_owned_row( + row: &'row OwnedSqliteRow, + col_idx: i32, + ) -> Option> { + let value = row + .values + .get(col_idx as usize) + .and_then(|v| v.as_ref())? + .value; + let ret = Self { _row: None, value }; if ret.value_type().is_none() { None } else {