Skip to content

Commit 2464571

Browse files
committed
Merge branch 'prepare/2.2.8' into 2.2.x (#4490)
2 parents 55d90e0 + c740fa4 commit 2464571

37 files changed

+644
-91
lines changed

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,16 @@ Increasing the minimal supported Rust version will always be coupled at least wi
1010

1111
## Unreleased
1212

13+
## [2.2.8] 2025-03-03
14+
15+
### Fixed
16+
17+
* Allow `#[diesel(check_for_backend(_))]` to check fields with `#[diesel(embed)]` annotations
18+
* Improve custom compile error message around `CompatibleType`
19+
* Fix a bug that restricted the number of allowed columns in `COPY FROM` statements to 12
20+
* Expose some SqliteValue helper functions
21+
* Use consistent whitespace in `ASC`/`DESC`, `DISTINCT ON`, and `DELETE FROM` clauses
22+
1323
## [2.2.7] 2025-01-31
1424

1525
### Fixed
@@ -2179,3 +2189,4 @@ queries or set `PIPES_AS_CONCAT` manually.
21792189
[2.2.5]: https://github.com/diesel-rs/diesel/compare/v2.2.4...v2.2.5
21802190
[2.2.6]: https://github.com/diesel-rs/diesel/compare/v2.2.5...v2.2.6
21812191
[2.2.7]: https://github.com/diesel-rs/diesel/compare/v2.2.6...v2.2.7
2192+
[2.2.8]: https://github.com/diesel-rs/diesel/compare/v2.2.7...v2.2.8

diesel/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "diesel"
3-
version = "2.2.7"
3+
version = "2.2.8"
44
license = "MIT OR Apache-2.0"
55
description = "A safe, extensible ORM and Query Builder for PostgreSQL, SQLite, and MySQL"
66
readme = "README.md"

diesel/src/expression/operators.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -540,12 +540,12 @@ postfix_operator!(IsNull, " IS NULL");
540540
postfix_operator!(IsNotNull, " IS NOT NULL");
541541
postfix_operator!(
542542
Asc,
543-
" ASC ",
543+
" ASC",
544544
crate::expression::expression_types::NotSelectable
545545
);
546546
postfix_operator!(
547547
Desc,
548-
" DESC ",
548+
" DESC",
549549
crate::expression::expression_types::NotSelectable
550550
);
551551

diesel/src/pg/query_builder/copy/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,11 +148,11 @@ macro_rules! copy_target_for_columns {
148148
$(
149149
impl<T, $($ST,)*> CopyTarget for ($($ST,)*)
150150
where
151-
$($ST: Column<Table = T>,)*
151+
$($ST: Column<Table = T> + Default,)*
152152
($(<$ST as Expression>::SqlType,)*): SqlType,
153153
T: Table + StaticQueryFragment,
154154
T::Component: QueryFragment<Pg>,
155-
Self: ColumnList + Default,
155+
Self: ColumnList,
156156
{
157157
type Table = T;
158158
type SqlType = crate::dsl::SqlTypeOf<Self>;
@@ -162,7 +162,7 @@ macro_rules! copy_target_for_columns {
162162
) -> crate::QueryResult<()> {
163163
T::STATIC_COMPONENT.walk_ast(pass.reborrow())?;
164164
pass.push_sql("(");
165-
<Self as ColumnList>::walk_ast(&Self::default(), pass.reborrow())?;
165+
<Self as ColumnList>::walk_ast(&($($ST::default(),)*), pass.reborrow())?;
166166
pass.push_sql(")");
167167
Ok(())
168168
}

diesel/src/pg/query_builder/distinct_on.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ where
271271
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
272272
out.push_sql("DISTINCT ON (");
273273
self.0.walk_ast(out.reborrow())?;
274-
out.push_sql(")");
274+
out.push_sql(") ");
275275
Ok(())
276276
}
277277
}

diesel/src/query_builder/delete_statement/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ where
259259
Ret: QueryFragment<DB>,
260260
{
261261
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> {
262-
out.push_sql("DELETE ");
262+
out.push_sql("DELETE");
263263
self.from_clause.walk_ast(out.reborrow())?;
264264
self.where_clause.walk_ast(out.reborrow())?;
265265
self.returning.walk_ast(out.reborrow())?;

diesel/src/query_dsl/load_dsl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ mod private {
185185
#[diagnostic::on_unimplemented(
186186
note = "this is a mismatch between what your query returns and what your type expects the query to return",
187187
note = "the fields in your struct need to match the fields returned by your query in count, order and type",
188-
note = "consider using `#[derive(Selectable)]` or #[derive(QueryableByName)] + `#[diesel(check_for_backend({DB}))]` \n\
188+
note = "consider using `#[diesel(check_for_backend({DB}))]` on either `#[derive(Selectable)]` or `#[derive(QueryableByName)]` \n\
189189
on your struct `{U}` and in your query `.select({U}::as_select())` to get a better error message"
190190
)]
191191
pub trait CompatibleType<U, DB> {

diesel/src/sqlite/connection/sqlite_value.rs

Lines changed: 135 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ use super::row::PrivateSqliteRow;
1212

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

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

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

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

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

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

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

@@ -178,3 +224,84 @@ impl OwnedSqliteValue {
178224
OwnedSqliteValue { value }
179225
}
180226
}
227+
228+
#[cfg(test)]
229+
mod tests {
230+
use crate::connection::{LoadConnection, SimpleConnection};
231+
use crate::row::Field;
232+
use crate::row::Row;
233+
use crate::sql_types::{Blob, Double, Int4, Text};
234+
use crate::*;
235+
236+
#[expect(clippy::approx_constant)] // we really want to use 3.14
237+
#[test]
238+
fn can_convert_all_values() {
239+
let mut conn = SqliteConnection::establish(":memory:").unwrap();
240+
241+
conn.batch_execute("CREATE TABLE tests(int INTEGER, text TEXT, blob BLOB, float FLOAT)")
242+
.unwrap();
243+
244+
diesel::sql_query("INSERT INTO tests(int, text, blob, float) VALUES(?, ?, ?, ?)")
245+
.bind::<Int4, _>(42)
246+
.bind::<Text, _>("foo")
247+
.bind::<Blob, _>(b"foo")
248+
.bind::<Double, _>(3.14)
249+
.execute(&mut conn)
250+
.unwrap();
251+
252+
let mut res = conn
253+
.load(diesel::sql_query(
254+
"SELECT int, text, blob, float FROM tests",
255+
))
256+
.unwrap();
257+
let row = res.next().unwrap().unwrap();
258+
let int_field = row.get(0).unwrap();
259+
let text_field = row.get(1).unwrap();
260+
let blob_field = row.get(2).unwrap();
261+
let float_field = row.get(3).unwrap();
262+
263+
let mut int_value = int_field.value().unwrap();
264+
assert_eq!(int_value.read_integer(), 42);
265+
let mut int_value = int_field.value().unwrap();
266+
assert_eq!(int_value.read_long(), 42);
267+
let mut int_value = int_field.value().unwrap();
268+
assert_eq!(int_value.read_double(), 42.0);
269+
let mut int_value = int_field.value().unwrap();
270+
assert_eq!(int_value.read_text(), "42");
271+
let mut int_value = int_field.value().unwrap();
272+
assert_eq!(int_value.read_blob(), b"42");
273+
274+
let mut text_value = text_field.value().unwrap();
275+
assert_eq!(text_value.read_integer(), 0);
276+
let mut text_value = text_field.value().unwrap();
277+
assert_eq!(text_value.read_long(), 0);
278+
let mut text_value = text_field.value().unwrap();
279+
assert_eq!(text_value.read_double(), 0.0);
280+
let mut text_value = text_field.value().unwrap();
281+
assert_eq!(text_value.read_text(), "foo");
282+
let mut text_value = text_field.value().unwrap();
283+
assert_eq!(text_value.read_blob(), b"foo");
284+
285+
let mut blob_value = blob_field.value().unwrap();
286+
assert_eq!(blob_value.read_integer(), 0);
287+
let mut blob_value = blob_field.value().unwrap();
288+
assert_eq!(blob_value.read_long(), 0);
289+
let mut blob_value = blob_field.value().unwrap();
290+
assert_eq!(blob_value.read_double(), 0.0);
291+
let mut blob_value = blob_field.value().unwrap();
292+
assert_eq!(blob_value.read_text(), "foo");
293+
let mut blob_value = blob_field.value().unwrap();
294+
assert_eq!(blob_value.read_blob(), b"foo");
295+
296+
let mut float_value = float_field.value().unwrap();
297+
assert_eq!(float_value.read_integer(), 3);
298+
let mut float_value = float_field.value().unwrap();
299+
assert_eq!(float_value.read_long(), 3);
300+
let mut float_value = float_field.value().unwrap();
301+
assert_eq!(float_value.read_double(), 3.14);
302+
let mut float_value = float_field.value().unwrap();
303+
assert_eq!(float_value.read_text(), "3.14");
304+
let mut float_value = float_field.value().unwrap();
305+
assert_eq!(float_value.read_blob(), b"3.14");
306+
}
307+
}

diesel/src/sqlite/types/date_and_time/chrono.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ fn parse_julian(julian_days: f64) -> Option<NaiveDateTime> {
8484

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

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

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

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

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

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

diesel/src/sqlite/types/date_and_time/time.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ fn parse_julian(julian_days: f64) -> Result<PrimitiveDateTime, ComponentRange> {
137137

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

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

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

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

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

0 commit comments

Comments
 (0)