From 0ab58488455322765b06c075f49ed0d1f0b14d30 Mon Sep 17 00:00:00 2001 From: Cathal Mullan Date: Tue, 28 Oct 2025 15:30:19 +0000 Subject: [PATCH] breaking: make `offline` optional to allow building without `serde` --- Cargo.lock | 1 - Cargo.toml | 4 ++-- sqlx-core/src/any/connection/backend.rs | 6 +++++- sqlx-core/src/any/connection/executor.rs | 2 ++ sqlx-core/src/executor.rs | 2 ++ sqlx-core/src/pool/executor.rs | 3 +++ sqlx-core/src/transaction.rs | 1 + sqlx-mysql/Cargo.toml | 4 ++-- sqlx-mysql/src/any.rs | 6 +++++- sqlx-mysql/src/column.rs | 1 + sqlx-mysql/src/connection/executor.rs | 6 +++++- sqlx-postgres/Cargo.toml | 12 +++++------- sqlx-postgres/src/any.rs | 6 +++++- sqlx-postgres/src/connection/describe.rs | 13 +++++++++++++ sqlx-postgres/src/connection/executor.rs | 2 ++ sqlx-postgres/src/io/mod.rs | 2 ++ sqlx-postgres/src/listener.rs | 2 ++ sqlx-postgres/src/types/hstore.rs | 4 ++-- sqlx-postgres/src/types/mod.rs | 8 +++++--- sqlx-postgres/src/types/oid.rs | 11 ++++++----- sqlx-sqlite/Cargo.toml | 1 - sqlx-sqlite/src/any.rs | 6 +++++- sqlx-sqlite/src/connection/establish.rs | 14 +++++++++++++- sqlx-sqlite/src/connection/executor.rs | 2 ++ sqlx-sqlite/src/connection/worker.rs | 9 ++++++++- 25 files changed, 98 insertions(+), 30 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 78e40f0c12..c6972ece0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3995,7 +3995,6 @@ dependencies = [ "percent-encoding", "regex", "serde", - "serde_urlencoded", "sqlx", "sqlx-core", "thiserror 2.0.17", diff --git a/Cargo.toml b/Cargo.toml index 00d5d656c1..23f07a9122 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,7 +63,7 @@ rustdoc-args = ["--cfg", "docsrs"] default = ["any", "macros", "migrate", "json"] derive = ["sqlx-macros/derive"] -macros = ["derive", "sqlx-macros/macros"] +macros = ["derive", "sqlx-macros/macros", "sqlx-core/offline", "sqlx-mysql?/offline", "sqlx-postgres?/offline", "sqlx-sqlite?/offline"] migrate = ["sqlx-core/migrate", "sqlx-macros?/migrate", "sqlx-mysql?/migrate", "sqlx-postgres?/migrate", "sqlx-sqlite?/migrate"] # Enable parsing of `sqlx.toml` for configuring macros and migrations. @@ -211,7 +211,7 @@ features = ["time", "net", "sync", "fs", "io-util", "rt"] default-features = false [dependencies] -sqlx-core = { workspace = true, features = ["offline", "migrate"] } +sqlx-core = { workspace = true, features = ["migrate"] } sqlx-macros = { workspace = true, optional = true } sqlx-mysql = { workspace = true, optional = true } diff --git a/sqlx-core/src/any/connection/backend.rs b/sqlx-core/src/any/connection/backend.rs index 5b3ea7bf2d..dd71be4c19 100644 --- a/sqlx-core/src/any/connection/backend.rs +++ b/sqlx-core/src/any/connection/backend.rs @@ -1,4 +1,7 @@ -use crate::any::{Any, AnyArguments, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo}; +#[cfg(feature = "offline")] +use crate::any::Any; +use crate::any::{AnyArguments, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo}; +#[cfg(feature = "offline")] use crate::describe::Describe; use crate::sql_str::SqlStr; use either::Either; @@ -114,5 +117,6 @@ pub trait AnyConnectionBackend: std::any::Any + Debug + Send + 'static { parameters: &[AnyTypeInfo], ) -> BoxFuture<'c, crate::Result>; + #[cfg(feature = "offline")] fn describe(&mut self, sql: SqlStr) -> BoxFuture<'_, crate::Result>>; } diff --git a/sqlx-core/src/any/connection/executor.rs b/sqlx-core/src/any/connection/executor.rs index a7c2cd33a0..8ed5cf4337 100644 --- a/sqlx-core/src/any/connection/executor.rs +++ b/sqlx-core/src/any/connection/executor.rs @@ -1,4 +1,5 @@ use crate::any::{Any, AnyConnection, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo}; +#[cfg(feature = "offline")] use crate::describe::Describe; use crate::error::Error; use crate::executor::{Execute, Executor}; @@ -56,6 +57,7 @@ impl<'c> Executor<'c> for &'c mut AnyConnection { self.backend.prepare_with(sql, parameters) } + #[cfg(feature = "offline")] fn describe<'e>(self, sql: SqlStr) -> BoxFuture<'e, Result, Error>> where 'c: 'e, diff --git a/sqlx-core/src/executor.rs b/sqlx-core/src/executor.rs index e1c42fc706..5d73c7ac3a 100644 --- a/sqlx-core/src/executor.rs +++ b/sqlx-core/src/executor.rs @@ -1,4 +1,5 @@ use crate::database::Database; +#[cfg(feature = "offline")] use crate::describe::Describe; use crate::error::{BoxDynError, Error}; use crate::sql_str::{SqlSafeStr, SqlStr}; @@ -178,6 +179,7 @@ pub trait Executor<'c>: Send + Debug + Sized { /// This is used by compile-time verification in the query macros to /// power their type inference. #[doc(hidden)] + #[cfg(feature = "offline")] fn describe<'e>(self, sql: SqlStr) -> BoxFuture<'e, Result, Error>> where 'c: 'e; diff --git a/sqlx-core/src/pool/executor.rs b/sqlx-core/src/pool/executor.rs index c168a70f51..8774b53a5f 100644 --- a/sqlx-core/src/pool/executor.rs +++ b/sqlx-core/src/pool/executor.rs @@ -4,6 +4,7 @@ use futures_core::stream::BoxStream; use futures_util::TryStreamExt; use crate::database::Database; +#[cfg(feature = "offline")] use crate::describe::Describe; use crate::error::Error; use crate::executor::{Execute, Executor}; @@ -63,6 +64,7 @@ where } #[doc(hidden)] + #[cfg(feature = "offline")] fn describe<'e>(self, sql: SqlStr) -> BoxFuture<'e, Result, Error>> { let pool = self.clone(); @@ -127,6 +129,7 @@ where // } // // #[doc(hidden)] +// #[cfg(feature = "offline")] // #[inline] // fn describe<'e, 'q: 'e>( // self, diff --git a/sqlx-core/src/transaction.rs b/sqlx-core/src/transaction.rs index 09bb68ed02..46f89d25b2 100644 --- a/sqlx-core/src/transaction.rs +++ b/sqlx-core/src/transaction.rs @@ -189,6 +189,7 @@ where // } // // #[doc(hidden)] +// #[cfg(feature = "offline")] // fn describe<'e, 'q: 'e>( // self, // query: &'q str, diff --git a/sqlx-mysql/Cargo.toml b/sqlx-mysql/Cargo.toml index ee9512b61e..80cc54d80b 100644 --- a/sqlx-mysql/Cargo.toml +++ b/sqlx-mysql/Cargo.toml @@ -12,7 +12,7 @@ rust-version.workspace = true [features] json = ["sqlx-core/json", "serde"] any = ["sqlx-core/any"] -offline = ["sqlx-core/offline", "serde/derive"] +offline = ["sqlx-core/offline", "serde/derive", "bitflags/serde"] migrate = ["sqlx-core/migrate"] # Type Integration features @@ -52,7 +52,7 @@ uuid = { workspace = true, optional = true } # Misc atoi = "2.0" base64 = { version = "0.22.0", default-features = false, features = ["std"] } -bitflags = { version = "2", default-features = false, features = ["serde"] } +bitflags = { version = "2", default-features = false } byteorder = { version = "1.4.3", default-features = false, features = ["std"] } bytes = "1.1.0" either = "1.6.1" diff --git a/sqlx-mysql/src/any.rs b/sqlx-mysql/src/any.rs index 241900560e..fd2d819b6d 100644 --- a/sqlx-mysql/src/any.rs +++ b/sqlx-mysql/src/any.rs @@ -7,12 +7,15 @@ use either::Either; use futures_core::future::BoxFuture; use futures_core::stream::BoxStream; use futures_util::{stream, FutureExt, StreamExt, TryFutureExt, TryStreamExt}; +#[cfg(feature = "offline")] +use sqlx_core::any::Any; use sqlx_core::any::{ - Any, AnyArguments, AnyColumn, AnyConnectOptions, AnyConnectionBackend, AnyQueryResult, AnyRow, + AnyArguments, AnyColumn, AnyConnectOptions, AnyConnectionBackend, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo, AnyTypeInfoKind, }; use sqlx_core::connection::Connection; use sqlx_core::database::Database; +#[cfg(feature = "offline")] use sqlx_core::describe::Describe; use sqlx_core::executor::Executor; use sqlx_core::sql_str::SqlStr; @@ -141,6 +144,7 @@ impl AnyConnectionBackend for MySqlConnection { }) } + #[cfg(feature = "offline")] fn describe(&mut self, sql: SqlStr) -> BoxFuture<'_, sqlx_core::Result>> { Box::pin(async move { let describe = Executor::describe(self, sql).await?; diff --git a/sqlx-mysql/src/column.rs b/sqlx-mysql/src/column.rs index 457cf991d3..26cdccc3e3 100644 --- a/sqlx-mysql/src/column.rs +++ b/sqlx-mysql/src/column.rs @@ -13,6 +13,7 @@ pub struct MySqlColumn { #[cfg_attr(feature = "offline", serde(default))] pub(crate) origin: ColumnOrigin, + #[allow(dead_code)] #[cfg_attr(feature = "offline", serde(skip))] pub(crate) flags: Option, } diff --git a/sqlx-mysql/src/connection/executor.rs b/sqlx-mysql/src/connection/executor.rs index 2b660b94b3..0a55ab46ae 100644 --- a/sqlx-mysql/src/connection/executor.rs +++ b/sqlx-mysql/src/connection/executor.rs @@ -1,5 +1,6 @@ use super::MySqlStream; use crate::connection::stream::Waiting; +#[cfg(feature = "offline")] use crate::describe::Describe; use crate::error::Error; use crate::executor::{Execute, Executor}; @@ -10,7 +11,9 @@ use crate::protocol::response::Status; use crate::protocol::statement::{ BinaryRow, Execute as StatementExecute, Prepare, PrepareOk, StmtClose, }; -use crate::protocol::text::{ColumnDefinition, ColumnFlags, Query, TextRow}; +#[cfg(feature = "offline")] +use crate::protocol::text::ColumnFlags; +use crate::protocol::text::{ColumnDefinition, Query, TextRow}; use crate::statement::{MySqlStatement, MySqlStatementMetadata}; use crate::HashMap; use crate::{ @@ -359,6 +362,7 @@ impl<'c> Executor<'c> for &'c mut MySqlConnection { } #[doc(hidden)] + #[cfg(feature = "offline")] fn describe<'e>(self, sql: SqlStr) -> BoxFuture<'e, Result, Error>> where 'c: 'e, diff --git a/sqlx-postgres/Cargo.toml b/sqlx-postgres/Cargo.toml index ceec9eb648..2525a65f4b 100644 --- a/sqlx-postgres/Cargo.toml +++ b/sqlx-postgres/Cargo.toml @@ -11,9 +11,9 @@ rust-version.workspace = true [features] any = ["sqlx-core/any"] -json = ["sqlx-core/json"] +json = ["dep:serde", "dep:serde_json", "sqlx-core/json"] migrate = ["sqlx-core/migrate"] -offline = ["sqlx-core/offline"] +offline = ["json", "sqlx-core/offline", "smallvec/serde"] # Type Integration features bigdecimal = ["dep:bigdecimal", "dep:num-bigint", "sqlx-core/bigdecimal"] @@ -62,7 +62,7 @@ itoa = "1.0.1" log = "0.4.18" memchr = { version = "2.4.1", default-features = false } num-bigint = { version = "0.4.3", optional = true } -smallvec = { version = "1.7.0", features = ["serde"] } +smallvec = { version = "1.7.0" } stringprep = "0.1.2" tracing = { version = "0.1.37", features = ["log"] } whoami = { version = "1.2.1", default-features = false } @@ -70,13 +70,11 @@ whoami = { version = "1.2.1", default-features = false } dotenvy.workspace = true thiserror.workspace = true -serde = { version = "1.0.144", features = ["derive"] } -serde_json = { version = "1.0.85", features = ["raw_value"] } +serde = { version = "1.0.144", optional = true, features = ["derive"] } +serde_json = { version = "1.0.85", optional = true, features = ["raw_value"] } [dependencies.sqlx-core] workspace = true -# We use JSON in the driver implementation itself so there's no reason not to enable it here. -features = ["json"] [dev-dependencies.sqlx] # FIXME: https://github.com/rust-lang/cargo/issues/15622 diff --git a/sqlx-postgres/src/any.rs b/sqlx-postgres/src/any.rs index 1f248505ae..6340aa505d 100644 --- a/sqlx-postgres/src/any.rs +++ b/sqlx-postgres/src/any.rs @@ -8,14 +8,17 @@ use futures_util::{stream, FutureExt, StreamExt, TryFutureExt, TryStreamExt}; use sqlx_core::sql_str::SqlStr; use std::{future, pin::pin}; +#[cfg(feature = "offline")] +use sqlx_core::any::Any; use sqlx_core::any::{ - Any, AnyArguments, AnyColumn, AnyConnectOptions, AnyConnectionBackend, AnyQueryResult, AnyRow, + AnyArguments, AnyColumn, AnyConnectOptions, AnyConnectionBackend, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo, AnyTypeInfoKind, }; use crate::type_info::PgType; use sqlx_core::connection::Connection; use sqlx_core::database::Database; +#[cfg(feature = "offline")] use sqlx_core::describe::Describe; use sqlx_core::executor::Executor; use sqlx_core::ext::ustr::UStr; @@ -141,6 +144,7 @@ impl AnyConnectionBackend for PgConnection { }) } + #[cfg(feature = "offline")] fn describe<'c>(&mut self, sql: SqlStr) -> BoxFuture<'_, sqlx_core::Result>> { Box::pin(async move { let describe = Executor::describe(self, sql).await?; diff --git a/sqlx-postgres/src/connection/describe.rs b/sqlx-postgres/src/connection/describe.rs index dfe5286458..05aa261770 100644 --- a/sqlx-postgres/src/connection/describe.rs +++ b/sqlx-postgres/src/connection/describe.rs @@ -1,19 +1,25 @@ use crate::connection::TableColumns; use crate::error::Error; use crate::ext::ustr::UStr; +#[cfg(feature = "offline")] use crate::io::StatementId; use crate::message::{ParameterDescription, RowDescription}; use crate::query_as::query_as; use crate::query_scalar::query_scalar; +#[cfg(feature = "offline")] use crate::statement::PgStatementMetadata; use crate::type_info::{PgArrayOf, PgCustomType, PgType, PgTypeKind}; +#[cfg(feature = "offline")] use crate::types::Json; use crate::types::Oid; use crate::HashMap; use crate::{PgColumn, PgConnection, PgTypeInfo}; +#[cfg(feature = "offline")] use smallvec::SmallVec; use sqlx_core::column::{ColumnOrigin, TableColumn}; +#[cfg(feature = "offline")] use sqlx_core::query_builder::QueryBuilder; +#[cfg(feature = "offline")] use sqlx_core::sql_str::AssertSqlSafe; use std::sync::Arc; @@ -490,6 +496,7 @@ WHERE rngtypid = $1 } /// Check whether EXPLAIN statements are supported by the current connection + #[cfg(feature = "offline")] fn is_explain_available(&self) -> bool { let parameter_statuses = &self.inner.stream.parameter_statuses; let is_cockroachdb = parameter_statuses.contains_key("crdb_version"); @@ -498,6 +505,7 @@ WHERE rngtypid = $1 !is_cockroachdb && !is_materialize && !is_questdb } + #[cfg(feature = "offline")] pub(crate) async fn get_nullable_for_columns( &mut self, stmt_id: StatementId, @@ -591,6 +599,7 @@ WHERE rngtypid = $1 /// /// This currently only marks columns that are on the inner half of an outer join /// and returns `None` for all others. + #[cfg(feature = "offline")] async fn nullables_from_explain( &mut self, stmt_id: StatementId, @@ -640,6 +649,7 @@ WHERE rngtypid = $1 } } +#[cfg(feature = "offline")] fn visit_plan(plan: &Plan, outputs: &[String], nullables: &mut Vec>) { if let Some(plan_outputs) = &plan.output { // all outputs of a Full Join must be marked nullable @@ -665,6 +675,7 @@ fn visit_plan(plan: &Plan, outputs: &[String], nullables: &mut Vec> } } +#[cfg(feature = "offline")] #[derive(serde::Deserialize, Debug)] #[serde(untagged)] enum Explain { @@ -688,6 +699,7 @@ enum Explain { Other(serde::de::IgnoredAny), } +#[cfg(feature = "offline")] #[derive(serde::Deserialize, Debug)] struct Plan { #[serde(rename = "Join Type")] @@ -701,6 +713,7 @@ struct Plan { } #[test] +#[cfg(feature = "offline")] fn explain_parsing() { let normal_plan = r#"[ { diff --git a/sqlx-postgres/src/connection/executor.rs b/sqlx-postgres/src/connection/executor.rs index ba4cffa647..1e82cff4b1 100644 --- a/sqlx-postgres/src/connection/executor.rs +++ b/sqlx-postgres/src/connection/executor.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "offline")] use crate::describe::Describe; use crate::error::Error; use crate::executor::{Execute, Executor}; @@ -475,6 +476,7 @@ impl<'c> Executor<'c> for &'c mut PgConnection { }) } + #[cfg(feature = "offline")] fn describe<'e>(self, sql: SqlStr) -> BoxFuture<'e, Result, Error>> where 'c: 'e, diff --git a/sqlx-postgres/src/io/mod.rs b/sqlx-postgres/src/io/mod.rs index 72f2a978c8..766f0f7c6f 100644 --- a/sqlx-postgres/src/io/mod.rs +++ b/sqlx-postgres/src/io/mod.rs @@ -16,6 +16,7 @@ pub(crate) struct PortalId(IdInner); #[derive(Debug, Copy, Clone, PartialEq, Eq)] struct IdInner(Option); +#[allow(dead_code)] pub(crate) struct DisplayId { prefix: &'static str, id: NonZeroU32, @@ -43,6 +44,7 @@ impl StatementId { /// Get a type to format this statement ID with [`Display`]. /// /// Returns `None` if this is the unnamed statement. + #[allow(dead_code)] #[inline(always)] pub fn display(&self) -> Option { self.0.display(Self::NAME_PREFIX) diff --git a/sqlx-postgres/src/listener.rs b/sqlx-postgres/src/listener.rs index 639ec95441..aa38d90a2e 100644 --- a/sqlx-postgres/src/listener.rs +++ b/sqlx-postgres/src/listener.rs @@ -12,6 +12,7 @@ use sqlx_core::transaction::Transaction; use sqlx_core::Either; use tracing::Instrument; +#[cfg(feature = "offline")] use crate::describe::Describe; use crate::error::Error; use crate::executor::{Execute, Executor}; @@ -439,6 +440,7 @@ impl<'c> Executor<'c> for &'c mut PgListener { } #[doc(hidden)] + #[cfg(feature = "offline")] fn describe<'e>(self, query: SqlStr) -> BoxFuture<'e, Result, Error>> where 'c: 'e, diff --git a/sqlx-postgres/src/types/hstore.rs b/sqlx-postgres/src/types/hstore.rs index a03970fb30..2d504904da 100644 --- a/sqlx-postgres/src/types/hstore.rs +++ b/sqlx-postgres/src/types/hstore.rs @@ -12,7 +12,6 @@ use crate::{ types::Type, PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueRef, Postgres, }; -use serde::{Deserialize, Serialize}; use sqlx_core::bytes::Buf; /// Key-value support (`hstore`) for Postgres. @@ -88,7 +87,8 @@ use sqlx_core::bytes::Buf; /// } /// ``` /// -#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] +#[cfg_attr(feature = "offline", derive(serde::Serialize, serde::Deserialize))] pub struct PgHstore(pub BTreeMap>); impl Deref for PgHstore { diff --git a/sqlx-postgres/src/types/mod.rs b/sqlx-postgres/src/types/mod.rs index 0faefbb482..f4ff7ef0e4 100644 --- a/sqlx-postgres/src/types/mod.rs +++ b/sqlx-postgres/src/types/mod.rs @@ -211,7 +211,9 @@ use crate::type_info::PgTypeKind; use crate::{PgTypeInfo, Postgres}; -pub(crate) use sqlx_core::types::{Json, Type}; +#[cfg(feature = "json")] +pub(crate) use sqlx_core::types::Json; +pub(crate) use sqlx_core::types::Type; mod array; mod bool; @@ -221,10 +223,10 @@ mod float; mod hstore; mod int; mod interval; +#[cfg(feature = "json")] +mod json; mod lquery; mod ltree; -// Not behind a Cargo feature because we require JSON in the driver implementation. -mod json; mod money; mod oid; mod range; diff --git a/sqlx-postgres/src/types/oid.rs b/sqlx-postgres/src/types/oid.rs index 04c5ef837a..9ef5dc970b 100644 --- a/sqlx-postgres/src/types/oid.rs +++ b/sqlx-postgres/src/types/oid.rs @@ -1,5 +1,4 @@ use byteorder::{BigEndian, ByteOrder}; -use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; use crate::decode::Decode; use crate::encode::{Encode, IsNull}; @@ -46,19 +45,21 @@ impl Decode<'_, Postgres> for Oid { } } -impl Serialize for Oid { +#[cfg(feature = "offline")] +impl serde::Serialize for Oid { fn serialize(&self, serializer: S) -> Result where - S: Serializer, + S: serde::Serializer, { self.0.serialize(serializer) } } -impl<'de> Deserialize<'de> for Oid { +#[cfg(feature = "offline")] +impl<'de> serde::Deserialize<'de> for Oid { fn deserialize(deserializer: D) -> Result where - D: Deserializer<'de>, + D: serde::Deserializer<'de>, { u32::deserialize(deserializer).map(Self) } diff --git a/sqlx-sqlite/Cargo.toml b/sqlx-sqlite/Cargo.toml index 4f56c3592f..401f9f2e4f 100644 --- a/sqlx-sqlite/Cargo.toml +++ b/sqlx-sqlite/Cargo.toml @@ -77,7 +77,6 @@ uuid = { workspace = true, optional = true } url = { version = "2.2.2" } percent-encoding = "2.1.0" -serde_urlencoded = "0.7" flume = { version = "0.11.0", default-features = false, features = ["async"] } diff --git a/sqlx-sqlite/src/any.rs b/sqlx-sqlite/src/any.rs index 83b141decd..c88e8755dc 100644 --- a/sqlx-sqlite/src/any.rs +++ b/sqlx-sqlite/src/any.rs @@ -6,8 +6,10 @@ use futures_core::future::BoxFuture; use futures_core::stream::BoxStream; use futures_util::{FutureExt, StreamExt, TryFutureExt, TryStreamExt}; +#[cfg(feature = "offline")] +use sqlx_core::any::Any; use sqlx_core::any::{ - Any, AnyArguments, AnyColumn, AnyConnectOptions, AnyConnectionBackend, AnyQueryResult, AnyRow, + AnyArguments, AnyColumn, AnyConnectOptions, AnyConnectionBackend, AnyQueryResult, AnyRow, AnyStatement, AnyTypeInfo, AnyTypeInfoKind, AnyValueKind, }; use sqlx_core::sql_str::SqlStr; @@ -16,6 +18,7 @@ use crate::arguments::SqliteArgumentsBuffer; use crate::type_info::DataType; use sqlx_core::connection::{ConnectOptions, Connection}; use sqlx_core::database::Database; +#[cfg(feature = "offline")] use sqlx_core::describe::Describe; use sqlx_core::executor::Executor; use sqlx_core::transaction::TransactionManager; @@ -140,6 +143,7 @@ impl AnyConnectionBackend for SqliteConnection { }) } + #[cfg(feature = "offline")] fn describe(&mut self, sql: SqlStr) -> BoxFuture<'_, sqlx_core::Result>> { Box::pin(async move { Executor::describe(self, sql).await?.try_into_any() }) } diff --git a/sqlx-sqlite/src/connection/establish.rs b/sqlx-sqlite/src/connection/establish.rs index d811275409..26209bd152 100644 --- a/sqlx-sqlite/src/connection/establish.rs +++ b/sqlx-sqlite/src/connection/establish.rs @@ -92,10 +92,22 @@ impl EstablishParams { } if !query_params.is_empty() { + let query_string = query_params + .iter() + .map(|(key, value)| { + format!( + "{}={}", + percent_encoding::percent_encode(key.as_bytes(), NON_ALPHANUMERIC), + percent_encoding::percent_encode(value.as_bytes(), NON_ALPHANUMERIC) + ) + }) + .collect::>() + .join("&"); + filename = format!( "file:{}?{}", percent_encoding::percent_encode(filename.as_bytes(), NON_ALPHANUMERIC), - serde_urlencoded::to_string(&query_params).unwrap() + query_string ); } diff --git a/sqlx-sqlite/src/connection/executor.rs b/sqlx-sqlite/src/connection/executor.rs index 0bc88cf14e..32c0eccbcb 100644 --- a/sqlx-sqlite/src/connection/executor.rs +++ b/sqlx-sqlite/src/connection/executor.rs @@ -4,6 +4,7 @@ use crate::{ use futures_core::future::BoxFuture; use futures_core::stream::BoxStream; use futures_util::{stream, FutureExt, StreamExt, TryFutureExt, TryStreamExt}; +#[cfg(feature = "offline")] use sqlx_core::describe::Describe; use sqlx_core::error::Error; use sqlx_core::executor::{Execute, Executor}; @@ -89,6 +90,7 @@ impl<'c> Executor<'c> for &'c mut SqliteConnection { } #[doc(hidden)] + #[cfg(feature = "offline")] fn describe<'e>(self, sql: SqlStr) -> BoxFuture<'e, Result, Error>> where 'c: 'e, diff --git a/sqlx-sqlite/src/connection/worker.rs b/sqlx-sqlite/src/connection/worker.rs index 11c8778cc9..5e49cb0261 100644 --- a/sqlx-sqlite/src/connection/worker.rs +++ b/sqlx-sqlite/src/connection/worker.rs @@ -8,6 +8,7 @@ use futures_intrusive::sync::{Mutex, MutexGuard}; use sqlx_core::sql_str::SqlStr; use tracing::span::Span; +#[cfg(feature = "offline")] use sqlx_core::describe::Describe; use sqlx_core::error::Error; use sqlx_core::transaction::{ @@ -15,11 +16,14 @@ use sqlx_core::transaction::{ }; use sqlx_core::Either; +#[cfg(feature = "offline")] use crate::connection::describe::describe; use crate::connection::establish::EstablishParams; use crate::connection::execute; use crate::connection::ConnectionState; -use crate::{Sqlite, SqliteArguments, SqliteQueryResult, SqliteRow, SqliteStatement}; +#[cfg(feature = "offline")] +use crate::Sqlite; +use crate::{SqliteArguments, SqliteQueryResult, SqliteRow, SqliteStatement}; #[cfg(feature = "deserialize")] use crate::connection::deserialize::{deserialize, serialize, SchemaName, SqliteOwnedBuf}; @@ -57,6 +61,7 @@ enum Command { query: SqlStr, tx: oneshot::Sender>, }, + #[cfg(feature = "offline")] Describe { query: SqlStr, tx: oneshot::Sender, Error>>, @@ -157,6 +162,7 @@ impl ConnectionWorker { &shared.cached_statements_size, ); } + #[cfg(feature = "offline")] Command::Describe { query, tx } => { tx.send(describe(&mut conn, query)).ok(); } @@ -352,6 +358,7 @@ impl ConnectionWorker { .await? } + #[cfg(feature = "offline")] pub(crate) async fn describe(&mut self, query: SqlStr) -> Result, Error> { self.oneshot_cmd(|tx| Command::Describe { query, tx }) .await?