Skip to content

Commit

Permalink
Merge branch 'prepare/2.2.8' into 2.2.x
Browse files Browse the repository at this point in the history
  • Loading branch information
Ten0 committed Mar 3, 2025
2 parents 55d90e0 + c740fa4 commit 9dd25ba
Show file tree
Hide file tree
Showing 37 changed files with 644 additions and 91 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ Increasing the minimal supported Rust version will always be coupled at least wi

## Unreleased

## [2.2.8] 2025-03-03

### Fixed

* Allow `#[diesel(check_for_backend(_))]` to check fields with `#[diesel(embed)]` annotations
* Improve custom compile error message around `CompatibleType`
* Fix a bug that restricted the number of allowed columns in `COPY FROM` statements to 12
* Expose some SqliteValue helper functions
* Use consistent whitespace in `ASC`/`DESC`, `DISTINCT ON`, and `DELETE FROM` clauses

## [2.2.7] 2025-01-31

### Fixed
Expand Down Expand Up @@ -2179,3 +2189,4 @@ queries or set `PIPES_AS_CONCAT` manually.
[2.2.5]: https://github.com/diesel-rs/diesel/compare/v2.2.4...v2.2.5
[2.2.6]: https://github.com/diesel-rs/diesel/compare/v2.2.5...v2.2.6
[2.2.7]: https://github.com/diesel-rs/diesel/compare/v2.2.6...v2.2.7
[2.2.8]: https://github.com/diesel-rs/diesel/compare/v2.2.7...v2.2.8
2 changes: 1 addition & 1 deletion diesel/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "diesel"
version = "2.2.7"
version = "2.2.8"
license = "MIT OR Apache-2.0"
description = "A safe, extensible ORM and Query Builder for PostgreSQL, SQLite, and MySQL"
readme = "README.md"
Expand Down
4 changes: 2 additions & 2 deletions diesel/src/expression/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,12 +540,12 @@ postfix_operator!(IsNull, " IS NULL");
postfix_operator!(IsNotNull, " IS NOT NULL");
postfix_operator!(
Asc,
" ASC ",
" ASC",
crate::expression::expression_types::NotSelectable
);
postfix_operator!(
Desc,
" DESC ",
" DESC",
crate::expression::expression_types::NotSelectable
);

Expand Down
6 changes: 3 additions & 3 deletions diesel/src/pg/query_builder/copy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,11 @@ macro_rules! copy_target_for_columns {
$(
impl<T, $($ST,)*> CopyTarget for ($($ST,)*)
where
$($ST: Column<Table = T>,)*
$($ST: Column<Table = T> + Default,)*
($(<$ST as Expression>::SqlType,)*): SqlType,
T: Table + StaticQueryFragment,
T::Component: QueryFragment<Pg>,
Self: ColumnList + Default,
Self: ColumnList,
{
type Table = T;
type SqlType = crate::dsl::SqlTypeOf<Self>;
Expand All @@ -162,7 +162,7 @@ macro_rules! copy_target_for_columns {
) -> crate::QueryResult<()> {
T::STATIC_COMPONENT.walk_ast(pass.reborrow())?;
pass.push_sql("(");
<Self as ColumnList>::walk_ast(&Self::default(), pass.reborrow())?;
<Self as ColumnList>::walk_ast(&($($ST::default(),)*), pass.reborrow())?;
pass.push_sql(")");
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion diesel/src/pg/query_builder/distinct_on.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ where
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
out.push_sql("DISTINCT ON (");
self.0.walk_ast(out.reborrow())?;
out.push_sql(")");
out.push_sql(") ");
Ok(())
}
}
Expand Down
2 changes: 1 addition & 1 deletion diesel/src/query_builder/delete_statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ where
Ret: QueryFragment<DB>,
{
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
out.push_sql("DELETE ");
out.push_sql("DELETE");
self.from_clause.walk_ast(out.reborrow())?;
self.where_clause.walk_ast(out.reborrow())?;
self.returning.walk_ast(out.reborrow())?;
Expand Down
2 changes: 1 addition & 1 deletion diesel/src/query_dsl/load_dsl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ mod private {
#[diagnostic::on_unimplemented(
note = "this is a mismatch between what your query returns and what your type expects the query to return",
note = "the fields in your struct need to match the fields returned by your query in count, order and type",
note = "consider using `#[derive(Selectable)]` or #[derive(QueryableByName)] + `#[diesel(check_for_backend({DB}))]` \n\
note = "consider using `#[diesel(check_for_backend({DB}))]` on either `#[derive(Selectable)]` or `#[derive(QueryableByName)]` \n\
on your struct `{U}` and in your query `.select({U}::as_select())` to get a better error message"
)]
pub trait CompatibleType<U, DB> {
Expand Down
143 changes: 135 additions & 8 deletions diesel/src/sqlite/connection/sqlite_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ use super::row::PrivateSqliteRow;

/// Raw sqlite value as received from the database
///
/// Use existing `FromSql` implementations to convert this into
/// rust values
/// Use the `read_*` functions to access the actual
/// value or use existing `FromSql` implementations
/// to convert this into rust values
#[allow(missing_debug_implementations, missing_copy_implementations)]
pub struct SqliteValue<'row, 'stmt, 'query> {
// This field exists to ensure that nobody
Expand Down Expand Up @@ -86,7 +87,7 @@ impl<'row, 'stmt, 'query> SqliteValue<'row, 'stmt, 'query> {
}
}

pub(crate) fn parse_string<'value, R>(&'value self, f: impl FnOnce(&'value str) -> R) -> R {
pub(crate) fn parse_string<'value, R>(&'value mut self, f: impl FnOnce(&'value str) -> R) -> R {
let s = unsafe {
let ptr = ffi::sqlite3_value_text(self.value.as_ptr());
let len = ffi::sqlite3_value_bytes(self.value.as_ptr());
Expand All @@ -102,11 +103,29 @@ impl<'row, 'stmt, 'query> SqliteValue<'row, 'stmt, 'query> {
f(s)
}

pub(crate) fn read_text(&self) -> &str {
/// Read the underlying value as string
///
/// If the underlying value is not a string sqlite will convert it
/// into a string and return that value instead.
///
/// Use the [`value_type()`](Self::value_type()) function to determine the actual
/// type of the value.
///
/// See <https://www.sqlite.org/c3ref/value_blob.html> for details
pub fn read_text(&mut self) -> &str {
self.parse_string(|s| s)
}

pub(crate) fn read_blob(&self) -> &[u8] {
/// Read the underlying value as blob
///
/// If the underlying value is not a blob sqlite will convert it
/// into a blob and return that value instead.
///
/// Use the [`value_type()`](Self::value_type()) function to determine the actual
/// type of the value.
///
/// See <https://www.sqlite.org/c3ref/value_blob.html> for details
pub fn read_blob(&mut self) -> &[u8] {
unsafe {
let ptr = ffi::sqlite3_value_blob(self.value.as_ptr());
let len = ffi::sqlite3_value_bytes(self.value.as_ptr());
Expand All @@ -124,15 +143,42 @@ impl<'row, 'stmt, 'query> SqliteValue<'row, 'stmt, 'query> {
}
}

pub(crate) fn read_integer(&self) -> i32 {
/// Read the underlying value as 32 bit integer
///
/// If the underlying value is not an integer sqlite will convert it
/// into an integer and return that value instead.
///
/// Use the [`value_type()`](Self::value_type()) function to determine the actual
/// type of the value.
///
/// See <https://www.sqlite.org/c3ref/value_blob.html> for details
pub fn read_integer(&mut self) -> i32 {
unsafe { ffi::sqlite3_value_int(self.value.as_ptr()) }
}

pub(crate) fn read_long(&self) -> i64 {
/// Read the underlying value as 64 bit integer
///
/// If the underlying value is not a string sqlite will convert it
/// into a string and return that value instead.
///
/// Use the [`value_type()`](Self::value_type()) function to determine the actual
/// type of the value.
///
/// See <https://www.sqlite.org/c3ref/value_blob.html> for details
pub fn read_long(&mut self) -> i64 {
unsafe { ffi::sqlite3_value_int64(self.value.as_ptr()) }
}

pub(crate) fn read_double(&self) -> f64 {
/// Read the underlying value as 64 bit float
///
/// If the underlying value is not a string sqlite will convert it
/// into a string and return that value instead.
///
/// Use the [`value_type()`](Self::value_type()) function to determine the actual
/// type of the value.
///
/// See <https://www.sqlite.org/c3ref/value_blob.html> for details
pub fn read_double(&mut self) -> f64 {
unsafe { ffi::sqlite3_value_double(self.value.as_ptr()) }
}

Expand Down Expand Up @@ -178,3 +224,84 @@ impl OwnedSqliteValue {
OwnedSqliteValue { value }
}
}

#[cfg(test)]
mod tests {
use crate::connection::{LoadConnection, SimpleConnection};
use crate::row::Field;
use crate::row::Row;
use crate::sql_types::{Blob, Double, Int4, Text};
use crate::*;

#[expect(clippy::approx_constant)] // we really want to use 3.14
#[test]
fn can_convert_all_values() {
let mut conn = SqliteConnection::establish(":memory:").unwrap();

conn.batch_execute("CREATE TABLE tests(int INTEGER, text TEXT, blob BLOB, float FLOAT)")
.unwrap();

diesel::sql_query("INSERT INTO tests(int, text, blob, float) VALUES(?, ?, ?, ?)")
.bind::<Int4, _>(42)
.bind::<Text, _>("foo")
.bind::<Blob, _>(b"foo")
.bind::<Double, _>(3.14)
.execute(&mut conn)
.unwrap();

let mut res = conn
.load(diesel::sql_query(
"SELECT int, text, blob, float FROM tests",
))
.unwrap();
let row = res.next().unwrap().unwrap();
let int_field = row.get(0).unwrap();
let text_field = row.get(1).unwrap();
let blob_field = row.get(2).unwrap();
let float_field = row.get(3).unwrap();

let mut int_value = int_field.value().unwrap();
assert_eq!(int_value.read_integer(), 42);
let mut int_value = int_field.value().unwrap();
assert_eq!(int_value.read_long(), 42);
let mut int_value = int_field.value().unwrap();
assert_eq!(int_value.read_double(), 42.0);
let mut int_value = int_field.value().unwrap();
assert_eq!(int_value.read_text(), "42");
let mut int_value = int_field.value().unwrap();
assert_eq!(int_value.read_blob(), b"42");

let mut text_value = text_field.value().unwrap();
assert_eq!(text_value.read_integer(), 0);
let mut text_value = text_field.value().unwrap();
assert_eq!(text_value.read_long(), 0);
let mut text_value = text_field.value().unwrap();
assert_eq!(text_value.read_double(), 0.0);
let mut text_value = text_field.value().unwrap();
assert_eq!(text_value.read_text(), "foo");
let mut text_value = text_field.value().unwrap();
assert_eq!(text_value.read_blob(), b"foo");

let mut blob_value = blob_field.value().unwrap();
assert_eq!(blob_value.read_integer(), 0);
let mut blob_value = blob_field.value().unwrap();
assert_eq!(blob_value.read_long(), 0);
let mut blob_value = blob_field.value().unwrap();
assert_eq!(blob_value.read_double(), 0.0);
let mut blob_value = blob_field.value().unwrap();
assert_eq!(blob_value.read_text(), "foo");
let mut blob_value = blob_field.value().unwrap();
assert_eq!(blob_value.read_blob(), b"foo");

let mut float_value = float_field.value().unwrap();
assert_eq!(float_value.read_integer(), 3);
let mut float_value = float_field.value().unwrap();
assert_eq!(float_value.read_long(), 3);
let mut float_value = float_field.value().unwrap();
assert_eq!(float_value.read_double(), 3.14);
let mut float_value = float_field.value().unwrap();
assert_eq!(float_value.read_text(), "3.14");
let mut float_value = float_field.value().unwrap();
assert_eq!(float_value.read_blob(), b"3.14");
}
}
12 changes: 6 additions & 6 deletions diesel/src/sqlite/types/date_and_time/chrono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ fn parse_julian(julian_days: f64) -> Option<NaiveDateTime> {

#[cfg(all(feature = "sqlite", feature = "chrono"))]
impl FromSql<Date, Sqlite> for NaiveDate {
fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
value
.parse_string(|s| Self::parse_from_str(s, DATE_FORMAT))
.map_err(Into::into)
Expand All @@ -101,7 +101,7 @@ impl ToSql<Date, Sqlite> for NaiveDate {

#[cfg(all(feature = "sqlite", feature = "chrono"))]
impl FromSql<Time, Sqlite> for NaiveTime {
fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
value.parse_string(|text| {
for format in TIME_FORMATS {
if let Ok(time) = Self::parse_from_str(text, format) {
Expand All @@ -124,7 +124,7 @@ impl ToSql<Time, Sqlite> for NaiveTime {

#[cfg(all(feature = "sqlite", feature = "chrono"))]
impl FromSql<Timestamp, Sqlite> for NaiveDateTime {
fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
value.parse_string(|text| {
for format in NAIVE_DATETIME_FORMATS {
if let Ok(dt) = Self::parse_from_str(text, format) {
Expand Down Expand Up @@ -153,7 +153,7 @@ impl ToSql<Timestamp, Sqlite> for NaiveDateTime {

#[cfg(all(feature = "sqlite", feature = "chrono"))]
impl FromSql<TimestamptzSqlite, Sqlite> for NaiveDateTime {
fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
value.parse_string(|text| {
for format in NAIVE_DATETIME_FORMATS {
if let Ok(dt) = Self::parse_from_str(text, format) {
Expand Down Expand Up @@ -182,7 +182,7 @@ impl ToSql<TimestamptzSqlite, Sqlite> for NaiveDateTime {

#[cfg(all(feature = "sqlite", feature = "chrono"))]
impl FromSql<TimestamptzSqlite, Sqlite> for DateTime<Utc> {
fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
// First try to parse the timezone
if let Ok(dt) = value.parse_string(|text| {
for format in DATETIME_FORMATS {
Expand All @@ -205,7 +205,7 @@ impl FromSql<TimestamptzSqlite, Sqlite> for DateTime<Utc> {

#[cfg(all(feature = "sqlite", feature = "chrono"))]
impl FromSql<TimestamptzSqlite, Sqlite> for DateTime<Local> {
fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
// First try to parse the timezone
if let Ok(dt) = value.parse_string(|text| {
for format in DATETIME_FORMATS {
Expand Down
10 changes: 5 additions & 5 deletions diesel/src/sqlite/types/date_and_time/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ fn parse_julian(julian_days: f64) -> Result<PrimitiveDateTime, ComponentRange> {

#[cfg(all(feature = "sqlite", feature = "time"))]
impl FromSql<Date, Sqlite> for NaiveDate {
fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
value
.parse_string(|s| Self::parse(s, DATE_FORMAT))
.map_err(Into::into)
Expand All @@ -154,7 +154,7 @@ impl ToSql<Date, Sqlite> for NaiveDate {

#[cfg(all(feature = "sqlite", feature = "time"))]
impl FromSql<Time, Sqlite> for NaiveTime {
fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
value.parse_string(|text| {
for format in TIME_FORMATS {
if let Ok(time) = Self::parse(text, format) {
Expand Down Expand Up @@ -182,7 +182,7 @@ impl ToSql<Time, Sqlite> for NaiveTime {

#[cfg(all(feature = "sqlite", feature = "time"))]
impl FromSql<Timestamp, Sqlite> for PrimitiveDateTime {
fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
value.parse_string(|text| {
for format in PRIMITIVE_DATETIME_FORMATS {
if let Ok(dt) = Self::parse(text, format) {
Expand Down Expand Up @@ -216,7 +216,7 @@ impl ToSql<Timestamp, Sqlite> for PrimitiveDateTime {

#[cfg(all(feature = "sqlite", feature = "time"))]
impl FromSql<TimestamptzSqlite, Sqlite> for PrimitiveDateTime {
fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
value.parse_string(|text| {
for format in PRIMITIVE_DATETIME_FORMATS {
if let Ok(dt) = Self::parse(text, format) {
Expand Down Expand Up @@ -250,7 +250,7 @@ impl ToSql<TimestamptzSqlite, Sqlite> for PrimitiveDateTime {

#[cfg(all(feature = "sqlite", feature = "time"))]
impl FromSql<TimestamptzSqlite, Sqlite> for OffsetDateTime {
fn from_sql(value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
fn from_sql(mut value: <Sqlite as Backend>::RawValue<'_>) -> deserialize::Result<Self> {
// First try to parse the timezone
if let Ok(dt) = value.parse_string(|text| {
for format in DATETIME_FORMATS {
Expand Down
Loading

0 comments on commit 9dd25ba

Please sign in to comment.