Skip to content

Commit

Permalink
Add owned version of Sqlite row
Browse files Browse the repository at this point in the history
  • Loading branch information
momobel committed Apr 12, 2024
1 parent 79c8f19 commit 88ce14f
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 3 deletions.
12 changes: 12 additions & 0 deletions diesel/src/row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`)
Expand Down
1 change: 1 addition & 0 deletions diesel/src/sqlite/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
90 changes: 90 additions & 0 deletions diesel/src/sqlite/connection/owned_row.rs
Original file line number Diff line number Diff line change
@@ -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<Option<OwnedSqliteValue>>,
column_names: Box<[Option<String>]>,
}

impl OwnedSqliteRow {
pub(super) fn new(
values: Vec<Option<OwnedSqliteValue>>,
column_names: Box<[Option<String>]>,
) -> 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<Self::Field<'field>>
where
'a: 'field,
Self: RowIndex<I>,
{
let idx = self.idx(idx)?;
Some(OwnedSqliteField {
row: self,
col_idx: i32::try_from(idx).ok()?,
})
}

fn partial_row(&self, range: std::ops::Range<usize>) -> PartialRow<'_, Self::InnerPartialRow> {
PartialRow::new(self, range)
}
}

impl RowIndex<usize> for OwnedSqliteRow {
fn idx(&self, idx: usize) -> Option<usize> {
if idx < self.field_count() {
Some(idx)
} else {
None
}
}
}

impl<'idx> RowIndex<&'idx str> for OwnedSqliteRow {
fn idx(&self, field_name: &'idx str) -> Option<usize> {
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<<Sqlite as Backend>::RawValue<'row>> {
SqliteValue::from_owned_row(self.row, self.col_idx)
}
}
42 changes: 41 additions & 1 deletion diesel/src/sqlite/connection/row.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -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,
Expand Down Expand Up @@ -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<String>]> = (0..stmt.column_count())
.map(|idx| stmt.field_name(idx).map(|s| s.to_owned()))
.collect::<Vec<_>>()
.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::<Vec<_>>()
.into_boxed_slice(),
),
}
}
}

impl<'stmt, 'query> RowSealed for SqliteRow<'stmt, 'query> {}
Expand Down
30 changes: 28 additions & 2 deletions diesel/src/sqlite/connection/sqlite_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<Ref<'row, PrivateSqliteRow<'stmt, 'query>>>,
// 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
Expand All @@ -29,6 +30,7 @@ pub struct SqliteValue<'row, 'stmt, 'query> {
value: NonNull<ffi::sqlite3_value>,
}

#[derive(Debug)]
#[repr(transparent)]
pub(super) struct OwnedSqliteValue {
pub(super) value: NonNull<ffi::sqlite3_value>,
Expand All @@ -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>>,
Expand All @@ -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<SqliteValue<'row, 'stmt, 'query>> {
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 {
Expand Down

0 comments on commit 88ce14f

Please sign in to comment.