Skip to content

Commit a8554fd

Browse files
committed
Add no_std support to diesel
This commit adds support for using diesel with the SQLite backend in a no-std (core + alloc) environment. This mainly means fixing a lot of imports to use the relevant items from core or alloc instead. Notable exceptions are: * There is no hashmap in alloc, so we need to pull in hashbrown for this * There is no panicing/abort support in core so we need our custom shims for these. * Global instrumentation setting is not supported yet (cfged away) as it requires lock which are not supported on embedded targets. This commit also introduces a new `std` default feature for enabling `std` lib support. If disabled the users need to manually enable the `hashbrown` feature to pull in hashbrown for hashmap support. Cargo doesn't support pulling in dependencies for disabled features. Finally this commit also adds a `example/sqlite/embedded` example project that demonstrates how to use this on a esp32-c6.
1 parent 1bd9fa1 commit a8554fd

File tree

160 files changed

+1049
-690
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

160 files changed

+1049
-690
lines changed

.github/workflows/ci.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,3 +555,24 @@ jobs:
555555
- name: Run cargo semver-checks
556556
run: |
557557
cargo xtask semver-checks
558+
Sqlite-no-std:
559+
name: Sqlite (no-std)
560+
runs-on: ubuntu-latest
561+
needs: [rustfmt_and_clippy]
562+
steps:
563+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
564+
with:
565+
persist-credentials: false
566+
- uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master
567+
with:
568+
toolchain: stable
569+
targets: riscv32imac-unknown-none-elf
570+
- name: Install cross compiler
571+
run: |
572+
curl -L https://github.com/nycu-arclab/riscv32-toolchain/releases/download/v14.2.0/riscv32-unknown-elf-gcc-14.2.0-linux.tar.gz -o riscv32-toolchain-linux.tar.gz
573+
tar -xzvf riscv32-toolchain-linux.tar.gz -C /opt
574+
- name: Check if diesel builds for no-std targets
575+
run: |
576+
cd examples/sqlite/embedded
577+
rustup target add riscv32imac-unknown-none-elf
578+
LIBSQLITE3_FLAGS="-DSQLITE_SMALL_STACK=1 -DSQLITE_THREADSAFE=0 -DSQLITE_OS_OTHER=1 -DSQLITE_OMIT_LOCALTIME=1 -USQLITE_ENABLE_FTS3 -UQLITE_ENABLE_DBSTAT_VTAB -UDSQLITE_ENABLE_JSON1 -USQLITE_ENABLE_FTS5 -USQLITE_ENABLE_STAT4 -DSQLITE_ENABLE_MEMSYS3=1" TARGET_CC=/opt/riscv/bin/riscv32-unknown-elf-gcc cargo build --target riscv32imac-unknown-none-elf

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ Increasing the minimal supported Rust version will always be coupled at least wi
1515
### Changed
1616

1717
* The minimal supported Rust version is now 1.88.0
18+
* Add support for libsqlite3-sys 0.36
19+
* Add support for no-std environments using the SQLite backend
1820

1921
## [2.3.6] 2026-01-23
2022

diesel/Cargo.toml

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ mysqlclient-src = { version = ">=0.1.0, <0.3", optional = true }
2929
pq-sys = { version = ">=0.4.0, <0.8.0", optional = true }
3030
pq-src = { version = "0.3", optional = true }
3131
quickcheck = { version = "1.0.3", optional = true }
32-
serde_json = { version = ">=0.8.0, <2.0", optional = true }
32+
serde_json = { version = ">=0.8.0, <2.0", optional = true, default-features = false, features = ["alloc"] }
3333
url = { version = "2.1.0", optional = true }
3434
percent-encoding = { version = "2.1.0", optional = true }
3535
uuid = { version = ">=0.7.0, <2.0.0", optional = true }
@@ -43,7 +43,8 @@ bitflags = { version = "2.0.0", optional = true }
4343
r2d2 = { version = ">= 0.8.2, < 0.9.0", optional = true }
4444
itoa = { version = "1.0.0", optional = true }
4545
time = { version = "0.3.9", optional = true, features = ["macros"] }
46-
downcast-rs = "2.0.1"
46+
downcast-rs = { version = "2.0.1", default-features = false }
47+
hashbrown = { version = "0.16", optional = true }
4748

4849
[dependencies.diesel_derives]
4950
version = "~2.3.0"
@@ -69,7 +70,7 @@ tempfile = "3.10.1"
6970
diesel_test_helper = { path = "../diesel_test_helper" }
7071

7172
[features]
72-
default = ["with-deprecated", "32-column-tables"]
73+
default = ["with-deprecated", "32-column-tables", "std"]
7374
extras = ["chrono", "time", "serde_json", "uuid", "network-address", "numeric", "r2d2"]
7475
unstable = ["diesel_derives/nightly"]
7576
large-tables = ["32-column-tables"]
@@ -78,22 +79,24 @@ huge-tables = ["64-column-tables"]
7879
64-column-tables = ["32-column-tables", "diesel_derives/64-column-tables"]
7980
128-column-tables = ["64-column-tables", "diesel_derives/128-column-tables"]
8081
postgres = ["dep:pq-sys", "postgres_backend"]
81-
sqlite = ["dep:libsqlite3-sys", "dep:sqlite-wasm-rs", "diesel_derives/sqlite", "time?/formatting", "time?/parsing"]
82+
sqlite-no-std = ["__sqlite-shared", "hashbrown"]
83+
sqlite = ["__sqlite-shared", "std"]
8284
mysql = ["dep:mysqlclient-sys", "dep:url", "dep:percent-encoding", "dep:bitflags", "mysql_backend"]
8385
without-deprecated = ["diesel_derives/without-deprecated"]
8486
with-deprecated = ["diesel_derives/with-deprecated"]
8587
network-address = ["dep:ipnetwork", "dep:libc"]
8688
ipnet-address = ["dep:ipnet", "dep:libc"]
8789
numeric = ["dep:num-bigint", "dep:bigdecimal", "dep:num-traits", "dep:num-integer", "diesel_derives/numeric"]
88-
postgres_backend = ["diesel_derives/postgres", "dep:bitflags", "dep:byteorder", "dep:itoa"]
89-
mysql_backend = ["diesel_derives/mysql", "dep:byteorder"]
90+
postgres_backend = ["diesel_derives/postgres", "dep:bitflags", "dep:byteorder", "dep:itoa", "std"]
91+
mysql_backend = ["diesel_derives/mysql", "dep:byteorder", "std"]
9092
returning_clauses_for_sqlite_3_35 = ["sqlite"]
9193
i-implement-a-third-party-backend-and-opt-into-breaking-changes = []
9294
r2d2 = ["diesel_derives/r2d2", "dep:r2d2"]
9395
chrono = ["diesel_derives/chrono", "dep:chrono"]
9496
time = ["diesel_derives/time", "dep:time"]
9597
uuid = ["dep:uuid"]
9698
serde_json = ["dep:serde_json"]
99+
__sqlite-shared = ["dep:libsqlite3-sys", "dep:sqlite-wasm-rs", "diesel_derives/sqlite", "time?/formatting", "time?/parsing"]
97100
__with_asan_tests = [
98101
"libsqlite3-sys?/bundled",
99102
"libsqlite3-sys?/with-asan",
@@ -102,6 +105,8 @@ __with_asan_tests = [
102105
"mysqlclient-sys?/bundled",
103106
"mysqlclient-src?/with-asan",
104107
]
108+
std = ["serde_json?/std"]
109+
hashbrown = ["dep:hashbrown"]
105110

106111
[package.metadata.docs.rs]
107112
features = ["postgres", "mysql", "sqlite", "extras"]

diesel/src/associations/belongs_to.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@ use crate::expression::array_comparison::AsInExpression;
55
use crate::prelude::*;
66
use crate::query_dsl::methods::FilterDsl;
77
use crate::sql_types::SqlType;
8+
use alloc::vec::Vec;
89

9-
use std::borrow::Borrow;
10-
use std::hash::Hash;
10+
use core::borrow::Borrow;
11+
use core::hash::Hash;
1112

1213
/// Indicates that a type belongs to `Parent`
1314
///
@@ -18,7 +19,7 @@ use std::hash::Hash;
1819
/// This trait is not capable of supporting composite foreign keys
1920
pub trait BelongsTo<Parent> {
2021
/// The foreign key of this struct
21-
type ForeignKey: Hash + ::std::cmp::Eq;
22+
type ForeignKey: Hash + ::core::cmp::Eq;
2223
/// The database column representing the foreign key
2324
/// of the table this struct represents
2425
type ForeignKeyColumn: Column;
@@ -296,8 +297,8 @@ where
296297
Id<&'a Parent>: Borrow<Child::ForeignKey>,
297298
{
298299
fn grouped_by(self, parents: &'a [Parent]) -> Vec<Vec<Child>> {
299-
use std::collections::HashMap;
300-
use std::iter;
300+
use crate::util::std_compat::HashMap;
301+
use core::iter;
301302

302303
let mut grouped: Vec<_> = iter::repeat_with(Vec::new).take(parents.len()).collect();
303304

@@ -323,8 +324,8 @@ where
323324
self,
324325
parents: &'a [Parent],
325326
) -> Result<Vec<Vec<Child>>, TryGroupedByError<Child>> {
326-
use std::collections::HashMap;
327-
use std::iter;
327+
use crate::util::std_compat::HashMap;
328+
use core::iter;
328329

329330
let mut grouped: Vec<_> = iter::repeat_with(Vec::new).take(parents.len()).collect();
330331
let mut ungrouped: Vec<_> = Vec::new();

diesel/src/associations/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@
403403
//! be used to construct the complex behavior applications need.
404404
mod belongs_to;
405405

406-
use std::hash::Hash;
406+
use core::hash::Hash;
407407

408408
use crate::query_source::Table;
409409

@@ -440,23 +440,23 @@ impl<T: HasTable> HasTable for Option<T> {
440440
}
441441
}
442442

443-
impl<T: HasTable> HasTable for Box<T> {
443+
impl<T: HasTable> HasTable for alloc::boxed::Box<T> {
444444
type Table = T::Table;
445445

446446
fn table() -> Self::Table {
447447
T::table()
448448
}
449449
}
450450

451-
impl<T: HasTable> HasTable for std::rc::Rc<T> {
451+
impl<T: HasTable> HasTable for alloc::rc::Rc<T> {
452452
type Table = T::Table;
453453

454454
fn table() -> Self::Table {
455455
T::table()
456456
}
457457
}
458458

459-
impl<T: HasTable> HasTable for std::sync::Arc<T> {
459+
impl<T: HasTable> HasTable for alloc::sync::Arc<T> {
460460
type Table = T::Table;
461461

462462
fn table() -> Self::Table {

diesel/src/backend.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::sql_types::{self, HasSqlType, TypeMetadata};
77
not(any(
88
feature = "postgres_backend",
99
feature = "mysql_backend",
10-
feature = "sqlite",
10+
feature = "__sqlite-shared",
1111
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
1212
)),
1313
allow(unused_imports)

diesel/src/collation/mod.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ impl QueryId for Custom {
3636
/// This collation is binary, case-sensitive, and locale-free.
3737
/// It is supported by SQLite.
3838
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, QueryId)]
39-
#[cfg(feature = "sqlite")]
39+
#[cfg(feature = "__sqlite-shared")]
4040
pub struct Binary;
4141

42-
#[cfg(feature = "sqlite")]
42+
#[cfg(feature = "__sqlite-shared")]
4343
impl Collation for Binary {}
4444

45-
#[cfg(feature = "sqlite")]
45+
#[cfg(feature = "__sqlite-shared")]
4646
impl QueryFragment<crate::sqlite::Sqlite> for Binary {
4747
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, crate::sqlite::Sqlite>) -> QueryResult<()> {
4848
out.push_sql("BINARY");
@@ -74,13 +74,13 @@ impl QueryFragment<crate::pg::Pg> for C {
7474
/// This collation is ASCII-only case-insensitive.
7575
/// It is supported by SQLite.
7676
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, QueryId)]
77-
#[cfg(feature = "sqlite")]
77+
#[cfg(feature = "__sqlite-shared")]
7878
pub struct NoCase;
7979

80-
#[cfg(feature = "sqlite")]
80+
#[cfg(feature = "__sqlite-shared")]
8181
impl Collation for NoCase {}
8282

83-
#[cfg(feature = "sqlite")]
83+
#[cfg(feature = "__sqlite-shared")]
8484
impl QueryFragment<crate::sqlite::Sqlite> for NoCase {
8585
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, crate::sqlite::Sqlite>) -> QueryResult<()> {
8686
out.push_sql("NOCASE");
@@ -112,13 +112,13 @@ impl QueryFragment<crate::pg::Pg> for Posix {
112112
/// This collation ignores trailing spaces.
113113
/// It is supported by SQLite.
114114
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, QueryId)]
115-
#[cfg(feature = "sqlite")]
115+
#[cfg(feature = "__sqlite-shared")]
116116
pub struct RTrim;
117117

118-
#[cfg(feature = "sqlite")]
118+
#[cfg(feature = "__sqlite-shared")]
119119
impl Collation for RTrim {}
120120

121-
#[cfg(feature = "sqlite")]
121+
#[cfg(feature = "__sqlite-shared")]
122122
impl QueryFragment<crate::sqlite::Sqlite> for RTrim {
123123
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, crate::sqlite::Sqlite>) -> QueryResult<()> {
124124
out.push_sql("RTRIM");

diesel/src/connection/instrumentation.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use alloc::boxed::Box;
2+
use core::fmt::{Debug, Display};
3+
use core::num::NonZeroU32;
4+
use core::ops::{Deref, DerefMut};
15
use downcast_rs::Downcast;
2-
use std::fmt::{Debug, Display};
3-
use std::num::NonZeroU32;
4-
use std::ops::{Deref, DerefMut};
56

7+
#[cfg(feature = "std")]
68
static GLOBAL_INSTRUMENTATION: std::sync::RwLock<fn() -> Option<Box<dyn Instrumentation>>> =
79
std::sync::RwLock::new(|| None);
810

@@ -34,7 +36,7 @@ impl<'query> StrQueryHelper<'query> {
3436
)]
3537
#[cfg(any(
3638
feature = "postgres",
37-
feature = "sqlite",
39+
feature = "__sqlite-shared",
3840
feature = "mysql",
3941
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
4042
))]
@@ -44,13 +46,13 @@ impl<'query> StrQueryHelper<'query> {
4446
}
4547

4648
impl Debug for StrQueryHelper<'_> {
47-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
4850
Debug::fmt(self.s, f)
4951
}
5052
}
5153

5254
impl Display for StrQueryHelper<'_> {
53-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
5456
Display::fmt(&self.s, f)
5557
}
5658
}
@@ -254,10 +256,13 @@ downcast_rs::impl_downcast!(Instrumentation);
254256
/// This function is mostly useful for crates implementing
255257
/// their own connection types
256258
pub fn get_default_instrumentation() -> Option<Box<dyn Instrumentation>> {
259+
#[cfg(feature = "std")]
257260
match GLOBAL_INSTRUMENTATION.read() {
258261
Ok(f) => (*f)(),
259262
Err(_) => None,
260263
}
264+
#[cfg(not(feature = "std"))]
265+
None
261266
}
262267

263268
/// Set a custom constructor for the default [`Instrumentation`]
@@ -277,6 +282,7 @@ pub fn get_default_instrumentation() -> Option<Box<dyn Instrumentation>> {
277282
///
278283
/// set_default_instrumentation(simple_logger);
279284
/// ```
285+
#[cfg(feature = "std")]
280286
pub fn set_default_instrumentation(
281287
default: fn() -> Option<Box<dyn Instrumentation>>,
282288
) -> crate::QueryResult<()> {
@@ -358,7 +364,7 @@ impl DynInstrumentation {
358364
)]
359365
#[cfg(any(
360366
feature = "postgres",
361-
feature = "sqlite",
367+
feature = "__sqlite-shared",
362368
feature = "mysql",
363369
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
364370
))]
@@ -375,7 +381,7 @@ impl DynInstrumentation {
375381
)]
376382
#[cfg(any(
377383
feature = "postgres",
378-
feature = "sqlite",
384+
feature = "__sqlite-shared",
379385
feature = "mysql",
380386
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
381387
))]
@@ -392,7 +398,7 @@ impl DynInstrumentation {
392398
)]
393399
#[cfg(any(
394400
feature = "postgres",
395-
feature = "sqlite",
401+
feature = "__sqlite-shared",
396402
feature = "mysql",
397403
feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
398404
))]

0 commit comments

Comments
 (0)