diff --git a/Cargo.lock b/Cargo.lock index eb79034049..69d967ca1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -355,6 +355,15 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "castaway" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" version = "1.0.73" @@ -448,6 +457,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "compact_str" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e464ece9cd4026f2fb9a2b97af84f7181109a84d877c8324385926206b0bd056" +dependencies = [ + "castaway", + "itoa 1.0.2", + "ryu", +] + [[package]] name = "concurrent-queue" version = "1.2.2" @@ -2156,6 +2176,12 @@ dependencies = [ "base64", ] +[[package]] +name = "rustversion" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" + [[package]] name = "rustyline" version = "9.1.2" @@ -2463,6 +2489,7 @@ dependencies = [ "byteorder", "bytes", "chrono", + "compact_str", "crc", "crossbeam-queue", "digest", diff --git a/Cargo.toml b/Cargo.toml index dd26ec286f..691816decd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -65,6 +65,7 @@ all-types = [ "bit-vec", "bstr", "git2", + "compact_str", ] # previous runtimes, available as features for error messages better than just @@ -130,6 +131,7 @@ time = ["sqlx-core/time", "sqlx-macros/time"] bit-vec = ["sqlx-core/bit-vec", "sqlx-macros/bit-vec"] bstr = ["sqlx-core/bstr"] git2 = ["sqlx-core/git2"] +compact_str = ["sqlx-core/compact_str", "sqlx-macros/compact_str"] [dependencies] sqlx-core = { version = "0.6.0", path = "sqlx-core", default-features = false } diff --git a/sqlx-core/Cargo.toml b/sqlx-core/Cargo.toml index f3689e30b5..2c32d6c0b3 100644 --- a/sqlx-core/Cargo.toml +++ b/sqlx-core/Cargo.toml @@ -59,10 +59,12 @@ all-types = [ "json", "uuid", "bit-vec", + "compact_str" ] bigdecimal = ["bigdecimal_", "num-bigint"] decimal = ["rust_decimal", "num-bigint"] json = ["serde", "serde_json"] +compact_str = ["compact_str_"] # runtimes runtime-actix-native-tls = [ @@ -126,6 +128,7 @@ bitflags = { version = "1.3.2", default-features = false } bytes = "1.1.0" byteorder = { version = "1.4.3", default-features = false, features = ["std"] } chrono = { version = "0.4.19", default-features = false, features = ["clock"], optional = true } +compact_str_ = { version = "0.5.0", optional = true, package = "compact_str" } crc = { version = "3", optional = true } crossbeam-queue = "0.3.2" digest = { version = "0.10.0", default-features = false, optional = true, features = ["std"] } diff --git a/sqlx-core/src/any/mod.rs b/sqlx-core/src/any/mod.rs index c026827bc9..859c5af4c9 100644 --- a/sqlx-core/src/any/mod.rs +++ b/sqlx-core/src/any/mod.rs @@ -28,6 +28,7 @@ pub(crate) mod value; #[cfg(feature = "migrate")] mod migrate; +pub(crate) use arguments::AnyArgumentBufferKind; pub use arguments::{AnyArgumentBuffer, AnyArguments}; pub use column::{AnyColumn, AnyColumnIndex}; pub use connection::AnyConnection; diff --git a/sqlx-core/src/types/compact_str.rs b/sqlx-core/src/types/compact_str.rs new file mode 100644 index 0000000000..2d42ad4d46 --- /dev/null +++ b/sqlx-core/src/types/compact_str.rs @@ -0,0 +1,114 @@ +#[cfg(feature = "sqlite")] +use std::borrow::Cow; + +/// Conversions between `bstr` types and SQL types. +use crate::database::{Database, HasArguments, HasValueRef}; +use crate::decode::Decode; +use crate::encode::{Encode, IsNull}; +use crate::error::BoxDynError; +use crate::types::Type; + +#[cfg(all( + any( + feature = "postgres", + feature = "mysql", + feature = "mssql", + feature = "sqlite" + ), + feature = "any" +))] +use crate::any::{Any, AnyArgumentBuffer, AnyArgumentBufferKind, AnyEncode}; +#[cfg(feature = "mssql")] +use crate::mssql::Mssql; +#[cfg(feature = "mysql")] +use crate::mysql::MySql; +#[cfg(feature = "postgres")] +use crate::postgres::Postgres; +#[cfg(feature = "sqlite")] +use crate::sqlite::{Sqlite, SqliteArgumentValue}; + +#[doc(no_inline)] +pub use compact_str_::CompactString; + +impl Type for CompactString +where + DB: Database, + String: Type, +{ + fn type_info() -> DB::TypeInfo { + >::type_info() + } + + fn compatible(ty: &DB::TypeInfo) -> bool { + >::compatible(ty) + } +} + +impl<'r, DB> Decode<'r, DB> for CompactString +where + DB: Database, + &'r str: Decode<'r, DB>, +{ + fn decode(value: >::ValueRef) -> Result { + <&str as Decode>::decode(value).map(CompactString::new) + } +} + +#[cfg(feature = "postgres")] +impl<'q> Encode<'q, Postgres> for CompactString { + fn encode_by_ref(&self, buf: &mut >::ArgumentBuffer) -> IsNull { + <&str as Encode>::encode(self.as_str(), buf) + } +} + +#[cfg(feature = "mysql")] +impl<'q> Encode<'q, MySql> for CompactString { + fn encode_by_ref(&self, buf: &mut >::ArgumentBuffer) -> IsNull { + <&str as Encode>::encode(self.as_str(), buf) + } +} + +#[cfg(feature = "mssql")] +impl<'q> Encode<'q, Mssql> for CompactString { + fn encode_by_ref(&self, buf: &mut >::ArgumentBuffer) -> IsNull { + <&str as Encode>::encode(self.as_str(), buf) + } +} + +#[cfg(feature = "sqlite")] +impl<'q> Encode<'q, Sqlite> for CompactString { + fn encode_by_ref(&self, buf: &mut >::ArgumentBuffer) -> IsNull { + buf.push(SqliteArgumentValue::Blob(Cow::Owned( + self.as_bytes().to_vec(), + ))); + IsNull::No + } +} + +#[cfg(all( + any( + feature = "postgres", + feature = "mysql", + feature = "mssql", + feature = "sqlite" + ), + feature = "any" +))] +impl<'q> Encode<'q, Any> for CompactString +where + CompactString: AnyEncode<'q>, +{ + fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer<'q>) -> IsNull { + match &mut buf.0 { + #[cfg(feature = "postgres")] + AnyArgumentBufferKind::Postgres(args, _) => args.add(self), + #[cfg(feature = "mysql")] + AnyArgumentBufferKind::MySql(args, _) => args.add(self), + #[cfg(feature = "mssql")] + AnyArgumentBufferKind::Mssql(args, _) => args.add(self), + #[cfg(feature = "sqlite")] + AnyArgumentBufferKind::Sqlite(args) => args.add(self), + } + IsNull::No + } +} diff --git a/sqlx-core/src/types/mod.rs b/sqlx-core/src/types/mod.rs index 0610c67ad6..1988a34f3e 100644 --- a/sqlx-core/src/types/mod.rs +++ b/sqlx-core/src/types/mod.rs @@ -46,6 +46,10 @@ pub mod chrono { }; } +#[cfg(feature = "compact_str")] +#[cfg_attr(docsrs, doc(cfg(feature = "compact_str")))] +pub mod compact_str; + #[cfg(feature = "bit-vec")] #[cfg_attr(docsrs, doc(cfg(feature = "bit-vec")))] #[doc(no_inline)] diff --git a/sqlx-macros/Cargo.toml b/sqlx-macros/Cargo.toml index 4a86dadb29..f92486cf74 100644 --- a/sqlx-macros/Cargo.toml +++ b/sqlx-macros/Cargo.toml @@ -70,6 +70,7 @@ mssql = ["sqlx-core/mssql"] bigdecimal = ["sqlx-core/bigdecimal"] decimal = ["sqlx-core/decimal"] chrono = ["sqlx-core/chrono"] +compact_str = ["sqlx-core/compact_str"] time = ["sqlx-core/time"] ipnetwork = ["sqlx-core/ipnetwork"] mac_address = ["sqlx-core/mac_address"] diff --git a/sqlx-macros/src/database/mysql.rs b/sqlx-macros/src/database/mysql.rs index b5504c32df..e13457d509 100644 --- a/sqlx-macros/src/database/mysql.rs +++ b/sqlx-macros/src/database/mysql.rs @@ -15,6 +15,9 @@ impl_database_ext! { // ordering is important here as otherwise we might infer strings to be binary // CHAR, VAR_CHAR, TEXT + #[cfg(feature = "compact_str")] + sqlx::types::compact_str::CompactString, + String, // BINARY, VAR_BINARY, BLOB diff --git a/sqlx-macros/src/database/postgres.rs b/sqlx-macros/src/database/postgres.rs index 57335430f2..008c2ea2e9 100644 --- a/sqlx-macros/src/database/postgres.rs +++ b/sqlx-macros/src/database/postgres.rs @@ -23,6 +23,9 @@ impl_database_ext! { sqlx::postgres::types::PgLQuery, + #[cfg(feature = "compact_str")] + sqlx::types::compact_str::CompactString, + #[cfg(feature = "uuid")] sqlx::types::Uuid, diff --git a/sqlx-macros/src/database/sqlite.rs b/sqlx-macros/src/database/sqlite.rs index aaa7f2c0a8..b062edff71 100644 --- a/sqlx-macros/src/database/sqlite.rs +++ b/sqlx-macros/src/database/sqlite.rs @@ -12,6 +12,9 @@ impl_database_ext! { String, Vec, + #[cfg(feature = "compact_str")] + sqlx::types::compact_str::CompactString, + #[cfg(feature = "chrono")] sqlx::types::chrono::NaiveDateTime,