From 0fc2eb8987cbe0999b77d9f766a80a8b7b458cd9 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Mon, 7 Jul 2025 12:55:04 -0400 Subject: [PATCH 1/8] wip --- Cargo.lock | 1 - Cargo.toml | 3 ++ src/bson_compat.rs | 77 ++++++++++------------------- src/bson_util.rs | 24 ++++----- src/client/auth.rs | 14 ++---- src/client/auth/oidc.rs | 6 +-- src/client/auth/sasl.rs | 4 +- src/client/auth/scram.rs | 4 +- src/client/auth/x509.rs | 4 +- src/client/options/bulk_write.rs | 4 +- src/cmap/establish/handshake.rs | 84 ++++++++++++++++---------------- src/hello.rs | 19 +++----- src/operation/update.rs | 2 +- 13 files changed, 107 insertions(+), 139 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c7824f756..3b9fdb80c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,7 +237,6 @@ dependencies = [ [[package]] name = "bson" version = "3.0.0" -source = "git+https://github.com/mongodb/bson-rust?branch=main#431d4483856b18d1b8885d0b46a60be7f2eb2dee" dependencies = [ "ahash", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index c8e8d8733..853bb33b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -136,6 +136,9 @@ version = "3.0.0" optional = true features = ["serde"] +[patch."https://github.com/mongodb/bson-rust"] +bson3 = { path = "../../../bson-rust/RUST-1992/cstr-driver", package = "bson" } + [dependencies.mongocrypt] git = "https://github.com/mongodb/libmongocrypt-rust.git" branch = "main" diff --git a/src/bson_compat.rs b/src/bson_compat.rs index cb756d35c..c02c39737 100644 --- a/src/bson_compat.rs +++ b/src/bson_compat.rs @@ -1,13 +1,27 @@ -use crate::bson::RawBson; +#[cfg(feature = "bson-3")] +pub(crate) type CStr = crate::bson::raw::CStr; +#[cfg(feature = "bson-3")] +pub(crate) type CString = crate::bson::raw::CString; +#[cfg(feature = "bson-3")] +pub(crate) use crate::bson::raw::cstr; -pub(crate) trait RawDocumentBufExt: Sized { - fn append_err(&mut self, key: impl AsRef, value: impl Into) -> RawResult<()>; +#[cfg(not(feature = "bson-3"))] +pub(crate) type CStr = str; +#[cfg(not(feature = "bson-3"))] +pub(crate) type CString = String; +#[cfg(not(feature = "bson-3"))] +macro_rules! cstr { + ($text:literal) => { + $text + }; +} - fn append_ref_err<'a>( +pub(crate) trait RawDocumentBufExt: Sized { + fn append_ref_compat<'a>( &mut self, - key: impl AsRef, - value: impl Into>, - ) -> RawResult<()>; + key: impl AsRef, + value: impl Into> + 'a, + ); #[cfg(not(feature = "bson-3"))] fn decode_from_bytes(data: Vec) -> RawResult; @@ -15,26 +29,17 @@ pub(crate) trait RawDocumentBufExt: Sized { #[cfg(feature = "bson-3")] impl RawDocumentBufExt for crate::bson::RawDocumentBuf { - fn append_err(&mut self, key: impl AsRef, value: impl Into) -> RawResult<()> { - self.append(key, value.into()) - } - - fn append_ref_err<'a>( + fn append_ref_compat<'a>( &mut self, - key: impl AsRef, - value: impl Into>, - ) -> RawResult<()> { - self.append(key, value) + key: impl AsRef, + value: impl Into> + 'a, + ) { + self.append(key, value); } } #[cfg(not(feature = "bson-3"))] impl RawDocumentBufExt for crate::bson::RawDocumentBuf { - fn append_err(&mut self, key: impl AsRef, value: impl Into) -> RawResult<()> { - self.append(key, value); - Ok(()) - } - fn append_ref_err<'a>( &mut self, key: impl AsRef, @@ -49,36 +54,6 @@ impl RawDocumentBufExt for crate::bson::RawDocumentBuf { } } -pub(crate) trait RawArrayBufExt: Sized { - #[allow(dead_code)] - fn from_iter_err, I: IntoIterator>(iter: I) -> RawResult; - - fn push_err(&mut self, value: impl Into) -> RawResult<()>; -} - -#[cfg(feature = "bson-3")] -impl RawArrayBufExt for crate::bson::RawArrayBuf { - fn from_iter_err, I: IntoIterator>(iter: I) -> RawResult { - Self::from_iter(iter.into_iter().map(|v| v.into())) - } - - fn push_err(&mut self, value: impl Into) -> RawResult<()> { - self.push(value.into()) - } -} - -#[cfg(not(feature = "bson-3"))] -impl RawArrayBufExt for crate::bson::RawArrayBuf { - fn from_iter_err, I: IntoIterator>(iter: I) -> RawResult { - Ok(Self::from_iter(iter)) - } - - fn push_err(&mut self, value: impl Into) -> RawResult<()> { - self.push(value); - Ok(()) - } -} - #[cfg(not(feature = "bson-3"))] pub(crate) trait RawDocumentExt { fn decode_from_bytes + ?Sized>(data: &D) -> RawResult<&Self>; diff --git a/src/bson_util.rs b/src/bson_util.rs index 48ab584b9..942fac561 100644 --- a/src/bson_util.rs +++ b/src/bson_util.rs @@ -17,7 +17,7 @@ use crate::{ RawBsonRef, RawDocumentBuf, }, - bson_compat::{RawArrayBufExt, RawDocumentBufExt as _}, + bson_compat::RawDocumentBufExt as _, checked::Checked, error::{Error, ErrorKind, Result}, runtime::SyncLittleEndianRead, @@ -78,14 +78,14 @@ pub(crate) fn to_bson_array(docs: &[Document]) -> Bson { pub(crate) fn to_raw_bson_array(docs: &[Document]) -> Result { let mut array = RawArrayBuf::new(); for doc in docs { - array.push_err(RawDocumentBuf::from_document(doc)?)?; + array.push(RawDocumentBuf::from_document(doc)?); } Ok(RawBson::Array(array)) } pub(crate) fn to_raw_bson_array_ser(values: &[T]) -> Result { let mut array = RawArrayBuf::new(); for value in values { - array.push_err(crate::bson_compat::serialize_to_raw_document_buf(value)?)?; + array.push(crate::bson_compat::serialize_to_raw_document_buf(value)?); } Ok(RawBson::Array(array)) } @@ -127,7 +127,7 @@ pub(crate) fn replacement_document_check(replacement: &Document) -> Result<()> { pub(crate) fn replacement_raw_document_check(replacement: &RawDocumentBuf) -> Result<()> { if let Some((key, _)) = replacement.iter().next().transpose()? { - if key.starts_with('$') { + if key.as_str().starts_with('$') { return Err(ErrorKind::InvalidArgument { message: "replacement document must not contain update modifiers".to_string(), } @@ -147,12 +147,12 @@ pub(crate) fn array_entry_size_bytes(index: usize, doc_len: usize) -> Result) -> Result { +pub(crate) fn vec_to_raw_array_buf(docs: Vec) -> RawArrayBuf { let mut array = RawArrayBuf::new(); for doc in docs { - array.push_err(doc)?; + array.push(doc); } - Ok(array) + array } /// The number of digits in `n` in base 10. @@ -188,7 +188,7 @@ pub(crate) fn extend_raw_document_buf( this: &mut RawDocumentBuf, other: RawDocumentBuf, ) -> Result<()> { - let mut keys: HashSet = HashSet::new(); + let mut keys: HashSet = HashSet::new(); for elem in this.iter_elements() { keys.insert(elem?.key().to_owned()); } @@ -200,14 +200,14 @@ pub(crate) fn extend_raw_document_buf( k ))); } - this.append_err(k, v.to_raw_bson())?; + this.append(k, v.to_raw_bson()); } Ok(()) } pub(crate) fn append_ser( this: &mut RawDocumentBuf, - key: impl AsRef, + key: impl AsRef, value: impl Serialize, ) -> Result<()> { #[derive(Serialize)] @@ -215,12 +215,12 @@ pub(crate) fn append_ser( value: T, } let raw_doc = crate::bson_compat::serialize_to_raw_document_buf(&Helper { value })?; - this.append_ref_err( + this.append_ref_compat( key, raw_doc .get("value")? .ok_or_else(|| Error::internal("no value"))?, - )?; + ); Ok(()) } diff --git a/src/client/auth.rs b/src/client/auth.rs index 3c53c1a29..eef3f2b81 100644 --- a/src/client/auth.rs +++ b/src/client/auth.rs @@ -14,7 +14,7 @@ mod x509; use std::{borrow::Cow, fmt::Debug, str::FromStr}; -use crate::{bson::RawDocumentBuf, bson_compat::RawDocumentBufExt as _}; +use crate::{bson::RawDocumentBuf, bson_compat::cstr}; use derive_where::derive_where; use hmac::{digest::KeyInit, Mac}; use rand::Rng; @@ -447,17 +447,13 @@ impl Credential { /// If the mechanism is missing, append the appropriate mechanism negotiation key-value-pair to /// the provided hello or legacy hello command document. - pub(crate) fn append_needed_mechanism_negotiation( - &self, - command: &mut RawDocumentBuf, - ) -> Result<()> { + pub(crate) fn append_needed_mechanism_negotiation(&self, command: &mut RawDocumentBuf) { if let (Some(username), None) = (self.username.as_ref(), self.mechanism.as_ref()) { - command.append_err( - "saslSupportedMechs", + command.append( + cstr!("saslSupportedMechs"), format!("{}.{}", self.resolved_source(), username), - )?; + ); } - Ok(()) } /// Attempts to authenticate a stream according to this credential, returning an error diff --git a/src/client/auth/oidc.rs b/src/client/auth/oidc.rs index f3e91eec4..ceb36bc2c 100644 --- a/src/client/auth/oidc.rs +++ b/src/client/auth/oidc.rs @@ -9,7 +9,7 @@ use typed_builder::TypedBuilder; use crate::{ bson::{doc, rawdoc, spec::BinarySubtype, Binary, Document}, - bson_compat::RawDocumentBufExt as _, + bson_compat::cstr, client::options::{ServerAddress, ServerApi}, cmap::{Command, Connection}, error::{Error, Result}, @@ -620,9 +620,9 @@ async fn send_sasl_start_command( ) -> Result { let mut start_doc = rawdoc! {}; if let Some(access_token) = access_token { - start_doc.append_err("jwt", access_token)?; + start_doc.append(cstr!("jwt"), access_token); } else if let Some(username) = credential.username.as_deref() { - start_doc.append_err("n", username)?; + start_doc.append(cstr!("n"), username); } let sasl_start = SaslStart::new( source.to_string(), diff --git a/src/client/auth/sasl.rs b/src/client/auth/sasl.rs index 89d340fb9..49112742f 100644 --- a/src/client/auth/sasl.rs +++ b/src/client/auth/sasl.rs @@ -2,7 +2,7 @@ use crate::bson::{rawdoc, RawBson}; use crate::{ bson::{spec::BinarySubtype, Binary, Bson, Document}, - bson_compat::RawDocumentBufExt as _, + bson_compat::cstr, bson_util, client::{auth::AuthMechanism, options::ServerApi}, cmap::Command, @@ -42,7 +42,7 @@ impl SaslStart { if self.mechanism == AuthMechanism::ScramSha1 || self.mechanism == AuthMechanism::ScramSha256 { - body.append_err("options", rawdoc! { "skipEmptyExchange": true })?; + body.append(cstr!("options"), rawdoc! { "skipEmptyExchange": true }); } let mut command = Command::new("saslStart", self.source, body); diff --git a/src/client/auth/scram.rs b/src/client/auth/scram.rs index dfb70eca7..38af1f365 100644 --- a/src/client/auth/scram.rs +++ b/src/client/auth/scram.rs @@ -19,7 +19,7 @@ use tokio::sync::RwLock; use crate::{ bson::{Bson, Document}, - bson_compat::RawDocumentBufExt as _, + bson_compat::cstr, client::{ auth::{ self, @@ -461,7 +461,7 @@ impl ClientFirst { let mut cmd = sasl_start.into_command()?; if self.include_db { - cmd.body.append_err("db", self.source.clone())?; + cmd.body.append(cstr!("db"), self.source.clone()); } Ok(cmd) diff --git a/src/client/auth/x509.rs b/src/client/auth/x509.rs index 9c8297b0a..238e6f081 100644 --- a/src/client/auth/x509.rs +++ b/src/client/auth/x509.rs @@ -2,7 +2,7 @@ use crate::bson::rawdoc; use crate::{ bson::Document, - bson_compat::RawDocumentBufExt as _, + bson_compat::cstr, client::options::ServerApi, cmap::{Command, Connection, RawCommandResponse}, error::{Error, Result}, @@ -25,7 +25,7 @@ pub(crate) fn build_client_first( }; if let Some(ref username) = credential.username { - auth_command_doc.append_err("username", username.as_str())?; + auth_command_doc.append(cstr!("username"), username.as_str()); } let mut command = Command::new("authenticate", "$external", auth_command_doc); diff --git a/src/client/options/bulk_write.rs b/src/client/options/bulk_write.rs index 2f436438d..5e8df9c99 100644 --- a/src/client/options/bulk_write.rs +++ b/src/client/options/bulk_write.rs @@ -7,7 +7,7 @@ use typed_builder::TypedBuilder; use crate::{ bson::{rawdoc, Array, Bson, Document, RawDocumentBuf}, - bson_compat::RawDocumentBufExt as _, + bson_compat::cstr, bson_util::{get_or_prepend_id_field, replacement_document_check, update_document_check}, error::Result, options::{UpdateModifications, WriteConcern}, @@ -396,7 +396,7 @@ impl WriteModel { }; if let Some(multi) = self.multi() { - model_document.append_err("multi", multi)?; + model_document.append(cstr!("multi"), multi); } Ok((model_document, inserted_id)) diff --git a/src/cmap/establish/handshake.rs b/src/cmap/establish/handshake.rs index b3aaaff75..5474b31a5 100644 --- a/src/cmap/establish/handshake.rs +++ b/src/cmap/establish/handshake.rs @@ -5,7 +5,7 @@ use std::env; use crate::{ bson::{rawdoc, RawBson, RawDocumentBuf}, - bson_compat::RawDocumentBufExt as _, + bson_compat::cstr, }; use once_cell::sync::Lazy; use tokio::sync::broadcast; @@ -77,63 +77,60 @@ pub(crate) enum FaasEnvironmentName { Vercel, } -impl TryFrom<&ClientMetadata> for RawDocumentBuf { - type Error = crate::error::Error; - fn try_from(metadata: &ClientMetadata) -> Result { +impl From<&ClientMetadata> for RawDocumentBuf { + fn from(metadata: &ClientMetadata) -> Self { let mut metadata_doc = RawDocumentBuf::new(); if let Some(application) = &metadata.application { - metadata_doc - .append_err("application", rawdoc! { "name": application.name.as_str() })?; + metadata_doc.append( + cstr!("application"), + rawdoc! { "name": application.name.as_str() }, + ); } - metadata_doc.append_err( - "driver", + metadata_doc.append( + cstr!("driver"), rawdoc! { "name": metadata.driver.name.as_str(), "version": metadata.driver.version.as_str(), }, - )?; + ); - let raw_os: RawBson = (&metadata.os).try_into()?; - metadata_doc.append_err("os", raw_os)?; - metadata_doc.append_err("platform", metadata.platform.as_str())?; + let raw_os: RawBson = (&metadata.os).into(); + metadata_doc.append(cstr!("os"), raw_os); + metadata_doc.append(cstr!("platform"), metadata.platform.as_str()); if let Some(env) = &metadata.env { - let raw_env: RawBson = env.try_into()?; - metadata_doc.append_err("env", raw_env)?; + let raw_env: RawBson = env.into(); + metadata_doc.append(cstr!("env"), raw_env); } - Ok(metadata_doc) + metadata_doc } } -impl TryFrom<&OsMetadata> for RawBson { - type Error = crate::error::Error; - - fn try_from(metadata: &OsMetadata) -> Result { +impl From<&OsMetadata> for RawBson { + fn from(metadata: &OsMetadata) -> Self { let mut doc = rawdoc! { "type": metadata.os_type.as_str() }; if let Some(name) = &metadata.name { - doc.append_err("name", name.as_str())?; + doc.append(cstr!("name"), name.as_str()); } if let Some(arch) = &metadata.architecture { - doc.append_err("architecture", arch.as_str())?; + doc.append(cstr!("architecture"), arch.as_str()); } if let Some(version) = &metadata.version { - doc.append_err("version", version.as_str())?; + doc.append(cstr!("version"), version.as_str()); } - Ok(RawBson::Document(doc)) + RawBson::Document(doc) } } -impl TryFrom<&RuntimeEnvironment> for RawBson { - type Error = crate::error::Error; - - fn try_from(env: &RuntimeEnvironment) -> Result { +impl From<&RuntimeEnvironment> for RawBson { + fn from(env: &RuntimeEnvironment) -> Self { let RuntimeEnvironment { name, runtime, @@ -145,27 +142,27 @@ impl TryFrom<&RuntimeEnvironment> for RawBson { } = env; let mut out = rawdoc! {}; if let Some(name) = name { - out.append_err("name", name.name())?; + out.append(cstr!("name"), name.name()); } if let Some(rt) = runtime { - out.append_err("runtime", rt.as_str())?; + out.append(cstr!("runtime"), rt.as_str()); } if let Some(t) = timeout_sec { - out.append_err("timeout_sec", *t)?; + out.append(cstr!("timeout_sec"), *t); } if let Some(m) = memory_mb { - out.append_err("memory_mb", *m)?; + out.append(cstr!("memory_mb"), *m); } if let Some(r) = region { - out.append_err("region", r.as_str())?; + out.append(cstr!("region"), r.as_str()); } if let Some(u) = url { - out.append_err("url", u.as_str())?; + out.append(cstr!("url"), u.as_str()); } if let Some(c) = container { - out.append_err("container", c.clone())?; + out.append(cstr!("container"), c.clone()); } - Ok(RawBson::Document(out)) + RawBson::Document(out) } } @@ -211,12 +208,10 @@ impl RuntimeEnvironment { } let mut container = rawdoc! {}; if std::path::Path::new("/.dockerenv").exists() { - // Unwrap safety: key and value are static known-valid strings. - container.append_err("runtime", "docker").unwrap(); + container.append(cstr!("runtime"), "docker"); } if var_set("KUBERNETES_SERVICE_HOST") { - // Unwrap safety: key and value are static known-valid strings. - container.append_err("orchestrator", "kubernetes").unwrap(); + container.append(cstr!("orchestrator"), "kubernetes"); } if !container.is_empty() { out.container = Some(container); @@ -387,7 +382,7 @@ impl Handshaker { metadata.env = RuntimeEnvironment::new(); if options.load_balanced { - command.body.append_err("loadBalanced", true)?; + command.body.append(cstr!("loadBalanced"), true); } #[cfg(any( @@ -428,7 +423,7 @@ impl Handshaker { let mut command = self.command.clone(); if let Some(cred) = credential { - cred.append_needed_mechanism_negotiation(&mut command.body)?; + cred.append_needed_mechanism_negotiation(&mut command.body); command.target_db = cred.resolved_source().to_string(); } @@ -449,7 +444,7 @@ impl Handshaker { #[cfg(test)] #[allow(clippy::incompatible_msrv)] let _ = TEST_METADATA.set(metadata); - body.append_err("client", meta_doc)?; + body.append(cstr!("client"), meta_doc); Ok((command, client_first)) } @@ -567,7 +562,10 @@ async fn set_speculative_auth_info( None => return Ok(None), }; - command.append_err("speculativeAuthenticate", client_first.to_document()?)?; + command.append( + cstr!("speculativeAuthenticate"), + client_first.to_document()?, + ); Ok(Some(client_first)) } diff --git a/src/hello.rs b/src/hello.rs index f521ee3e2..b070b8db1 100644 --- a/src/hello.rs +++ b/src/hello.rs @@ -2,7 +2,7 @@ use std::time::Duration; use crate::{ bson::{rawdoc, RawDocumentBuf}, - bson_compat::RawDocumentBufExt, + bson_compat::cstr, }; use serde::{Deserialize, Serialize}; use tokio::sync::broadcast; @@ -23,6 +23,7 @@ use crate::{ /// To limit usages of the legacy name in the codebase, this constant should be used /// wherever possible. pub(crate) const LEGACY_HELLO_COMMAND_NAME: &str = "isMaster"; +pub(crate) const LEGACY_HELLO_COMMAND_NAME_CSTR: &crate::bson_compat::CStr = cstr!("isMaster"); pub(crate) const LEGACY_HELLO_COMMAND_NAME_LOWERCASE: &str = "ismaster"; #[derive(Debug, Clone, Copy)] @@ -49,26 +50,22 @@ pub(crate) fn hello_command( { (rawdoc! { "hello": 1 }, "hello") } else { - let mut body = rawdoc! { LEGACY_HELLO_COMMAND_NAME: 1 }; + let mut body = rawdoc! { LEGACY_HELLO_COMMAND_NAME_CSTR: 1 }; if hello_ok.is_none() { - // Unwrap safety: key and value are static known-good values. - body.append_err("helloOk", true).unwrap(); + body.append(cstr!("helloOk"), true); } (body, LEGACY_HELLO_COMMAND_NAME) }; if let Some(opts) = awaitable_options { - // Unwrap safety: keys are static and values are types without cstrings. - body.append_err("topologyVersion", opts.topology_version) - .unwrap(); - body.append_err( - "maxAwaitTimeMS", + body.append(cstr!("topologyVersion"), opts.topology_version); + body.append( + cstr!("maxAwaitTimeMS"), opts.max_await_time .as_millis() .try_into() .unwrap_or(i64::MAX), - ) - .unwrap(); + ); } let mut command = Command::new(command_name, "admin", body); diff --git a/src/operation/update.rs b/src/operation/update.rs index 13cc358f9..12df4ce8a 100644 --- a/src/operation/update.rs +++ b/src/operation/update.rs @@ -35,7 +35,7 @@ impl UpdateOrReplace { }, Self::Replacement(replacement_doc) => { bson_util::replacement_raw_document_check(replacement_doc)?; - doc.append_ref_err(key, replacement_doc)?; + doc.append_ref_compat(key, replacement_doc)?; } } From 596c02813e785fc3076c0280e09f6f8bdd614f3a Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Mon, 7 Jul 2025 15:52:33 -0400 Subject: [PATCH 2/8] operations wip --- src/client.rs | 9 +++++++-- src/client/executor.rs | 11 ++++++++--- src/client/options/bulk_write.rs | 9 +++++---- src/operation.rs | 17 ++++++++++------- src/operation/abort_transaction.rs | 9 +++++++-- src/operation/aggregate.rs | 5 +++-- src/operation/aggregate/change_stream.rs | 2 +- src/operation/bulk_write.rs | 6 +++--- src/operation/commit_transaction.rs | 3 ++- src/operation/count.rs | 3 ++- src/operation/count_documents.rs | 2 +- src/operation/create.rs | 3 ++- src/operation/create_indexes.rs | 3 ++- src/operation/delete.rs | 5 +++-- src/operation/distinct.rs | 3 ++- src/operation/drop_collection.rs | 3 ++- src/operation/drop_database.rs | 3 ++- 17 files changed, 62 insertions(+), 34 deletions(-) diff --git a/src/client.rs b/src/client.rs index 7f5defc63..9741f1fe6 100644 --- a/src/client.rs +++ b/src/client.rs @@ -447,7 +447,12 @@ impl Client { criteria: Option<&SelectionCriteria>, ) -> Result { let (server, _) = self - .select_server(criteria, "Test select server", None, |_, _| None) + .select_server( + criteria, + crate::bson_compat::cstr!("Test select server"), + None, + |_, _| None, + ) .await?; Ok(server.address.clone()) } @@ -458,7 +463,7 @@ impl Client { &self, criteria: Option<&SelectionCriteria>, #[allow(unused_variables)] // we only use the operation_name for tracing. - operation_name: &str, + operation_name: &crate::bson_compat::CStr, deprioritized: Option<&ServerAddress>, override_criteria: OverrideCriteriaFn, ) -> Result<(SelectedServer, SelectionCriteria)> { diff --git a/src/client/executor.rs b/src/client/executor.rs index 50a0bec51..e4f1db650 100644 --- a/src/client/executor.rs +++ b/src/client/executor.rs @@ -879,7 +879,10 @@ impl Client { } } - async fn select_data_bearing_server(&self, operation_name: &str) -> Result<()> { + async fn select_data_bearing_server( + &self, + operation_name: &crate::bson_compat::CStr, + ) -> Result<()> { let topology_type = self.inner.topology.topology_type(); let criteria = SelectionCriteria::Predicate(Arc::new(move |server_info| { let server_type = server_info.server_type(); @@ -902,8 +905,10 @@ impl Client { // sessions are supported or not. match initial_status { TransactionSupportStatus::Undetermined => { - self.select_data_bearing_server("Check transactions support status") - .await?; + self.select_data_bearing_server(crate::bson_compat::cstr!( + "Check transactions support status" + )) + .await?; Ok(self.inner.topology.transaction_support_status()) } _ => Ok(initial_status), diff --git a/src/client/options/bulk_write.rs b/src/client/options/bulk_write.rs index 5e8df9c99..7aaff23a2 100644 --- a/src/client/options/bulk_write.rs +++ b/src/client/options/bulk_write.rs @@ -362,11 +362,12 @@ impl WriteModel { } } - pub(crate) fn operation_name(&self) -> &'static str { + pub(crate) fn operation_name(&self) -> &'static crate::bson_compat::CStr { + use crate::bson_compat::cstr; match self.operation_type() { - OperationType::Insert => "insert", - OperationType::Update => "update", - OperationType::Delete => "delete", + OperationType::Insert => cstr!("insert"), + OperationType::Update => cstr!("update"), + OperationType::Delete => cstr!("delete"), } } diff --git a/src/operation.rs b/src/operation.rs index cf23f86f0..4252a71fb 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -33,6 +33,7 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use crate::{ bson::{self, Bson, Document}, + bson_compat::CStr, bson_util::{self, extend_raw_document_buf}, client::{ClusterTime, HELLO_COMMAND_NAMES, REDACTED_COMMANDS}, cmap::{ @@ -108,7 +109,7 @@ pub(crate) trait Operation { type O; /// The name of the server side command associated with this operation. - const NAME: &'static str; + const NAME: &'static CStr; /// Returns the command that should be sent to the server as part of this operation. /// The operation may store some additional state that is required for handling the response. @@ -156,7 +157,8 @@ pub(crate) trait Operation { fn pinned_connection(&self) -> Option<&PinnedConnectionHandle>; - fn name(&self) -> &str; + /// The name of the server side command associated with this operation. + fn name(&self) -> &CStr; } pub(crate) type OverrideCriteriaFn = @@ -169,7 +171,7 @@ pub(crate) trait OperationWithDefaults: Send + Sync { type O; /// The name of the server side command associated with this operation. - const NAME: &'static str; + const NAME: &'static CStr; /// Returns the command that should be sent to the server as part of this operation. /// The operation may store some additional state that is required for handling the response. @@ -254,7 +256,8 @@ pub(crate) trait OperationWithDefaults: Send + Sync { None } - fn name(&self) -> &str { + /// The name of the server side command associated with this operation. + fn name(&self) -> &CStr { Self::NAME } } @@ -264,7 +267,7 @@ where T: Send + Sync, { type O = T::O; - const NAME: &'static str = T::NAME; + const NAME: &'static CStr = T::NAME; fn build(&mut self, description: &StreamDescription) -> Result { self.build(description) } @@ -308,14 +311,14 @@ where fn pinned_connection(&self) -> Option<&PinnedConnectionHandle> { self.pinned_connection() } - fn name(&self) -> &str { + fn name(&self) -> &CStr { self.name() } } fn should_redact_body(body: &RawDocumentBuf) -> bool { if let Some(Ok((command_name, _))) = body.into_iter().next() { - HELLO_COMMAND_NAMES.contains(command_name.to_lowercase().as_str()) + HELLO_COMMAND_NAMES.contains(command_name.as_str().to_lowercase().as_str()) && body.get("speculativeAuthenticate").ok().flatten().is_some() } else { false diff --git a/src/operation/abort_transaction.rs b/src/operation/abort_transaction.rs index a09c8a5a6..8e0613128 100644 --- a/src/operation/abort_transaction.rs +++ b/src/operation/abort_transaction.rs @@ -1,6 +1,7 @@ use crate::bson::rawdoc; use crate::{ + bson_compat::{cstr, CStr}, bson_util::append_ser, client::session::TransactionPin, cmap::{conn::PinnedConnectionHandle, Command, RawCommandResponse, StreamDescription}, @@ -29,7 +30,7 @@ impl AbortTransaction { impl OperationWithDefaults for AbortTransaction { type O = (); - const NAME: &'static str = "abortTransaction"; + const NAME: &'static CStr = cstr!("abortTransaction"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { @@ -37,7 +38,11 @@ impl OperationWithDefaults for AbortTransaction { }; if let Some(ref write_concern) = self.write_concern() { if !write_concern.is_empty() { - append_ser(&mut body, "writeConcern", write_concern)?; + append_ser( + &mut body, + crate::bson_compat::cstr!("writeConcern"), + write_concern, + )?; } } diff --git a/src/operation/aggregate.rs b/src/operation/aggregate.rs index ef1c5dbb0..7ae67dcdd 100644 --- a/src/operation/aggregate.rs +++ b/src/operation/aggregate.rs @@ -2,6 +2,7 @@ pub(crate) mod change_stream; use crate::{ bson::{doc, Bson, Document}, + bson_compat::{cstr, CStr}, bson_util, cmap::{Command, RawCommandResponse, StreamDescription}, cursor::CursorSpecification, @@ -46,11 +47,11 @@ impl Aggregate { impl OperationWithDefaults for Aggregate { type O = CursorSpecification; - const NAME: &'static str = "aggregate"; + const NAME: &'static CStr = cstr!("aggregate"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = doc! { - Self::NAME: self.target.to_bson(), + Self::NAME.as_str(): self.target.to_bson(), "pipeline": bson_util::to_bson_array(&self.pipeline), "cursor": {} }; diff --git a/src/operation/aggregate/change_stream.rs b/src/operation/aggregate/change_stream.rs index 0b1cb16ef..d405ec5ae 100644 --- a/src/operation/aggregate/change_stream.rs +++ b/src/operation/aggregate/change_stream.rs @@ -42,7 +42,7 @@ impl ChangeStreamAggregate { impl OperationWithDefaults for ChangeStreamAggregate { type O = (CursorSpecification, ChangeStreamData); - const NAME: &'static str = "aggregate"; + const NAME: &'static crate::bson_compat::CStr = Aggregate::NAME; fn build(&mut self, description: &StreamDescription) -> Result { if let Some(data) = &mut self.resume_data { diff --git a/src/operation/bulk_write.rs b/src/operation/bulk_write.rs index 7d3f40d44..c5cb933dd 100644 --- a/src/operation/bulk_write.rs +++ b/src/operation/bulk_write.rs @@ -7,7 +7,7 @@ use futures_util::{FutureExt, TryStreamExt}; use crate::{ bson::{rawdoc, Bson, RawDocumentBuf}, - bson_compat::RawDocumentBufExt as _, + bson_compat::{cstr, CStr}, bson_util::{self, extend_raw_document_buf}, checked::Checked, cmap::{Command, RawCommandResponse, StreamDescription}, @@ -263,7 +263,7 @@ where { type O = R; - const NAME: &'static str = "bulkWrite"; + const NAME: &'static CStr = cstr!("bulkWrite"); fn build(&mut self, description: &StreamDescription) -> Result { if description.max_wire_version.unwrap_or(0) < SERVER_8_0_0_WIRE_VERSION { @@ -282,7 +282,7 @@ where Some(options) => crate::bson_compat::serialize_to_raw_document_buf(options), None => crate::bson_compat::serialize_to_raw_document_buf(&BulkWriteOptions::default()), }?; - options.append_err("errorsOnly", R::errors_only())?; + options.append(cstr!("errorsOnly"), R::errors_only()); bson_util::extend_raw_document_buf(&mut command_body, options)?; let max_document_sequences_size: usize = (Checked::new(max_message_size) diff --git a/src/operation/commit_transaction.rs b/src/operation/commit_transaction.rs index 69fb1f80f..58a7f77a9 100644 --- a/src/operation/commit_transaction.rs +++ b/src/operation/commit_transaction.rs @@ -3,6 +3,7 @@ use std::time::Duration; use crate::bson::rawdoc; use crate::{ + bson_compat::{cstr, CStr}, cmap::{Command, RawCommandResponse, StreamDescription}, error::Result, operation::{append_options_to_raw_document, OperationWithDefaults, Retryability}, @@ -24,7 +25,7 @@ impl CommitTransaction { impl OperationWithDefaults for CommitTransaction { type O = (); - const NAME: &'static str = "commitTransaction"; + const NAME: &'static CStr = cstr!("commitTransaction"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { diff --git a/src/operation/count.rs b/src/operation/count.rs index 594785eef..d9bb3487e 100644 --- a/src/operation/count.rs +++ b/src/operation/count.rs @@ -3,6 +3,7 @@ use serde::Deserialize; use crate::{ bson::doc, + bson_compat::{cstr, CStr}, cmap::{Command, RawCommandResponse, StreamDescription}, coll::{options::EstimatedDocumentCountOptions, Namespace}, error::{Error, Result}, @@ -26,7 +27,7 @@ impl Count { impl OperationWithDefaults for Count { type O = u64; - const NAME: &'static str = "count"; + const NAME: &'static CStr = cstr!("count"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { diff --git a/src/operation/count_documents.rs b/src/operation/count_documents.rs index 087608b58..9ba6bfe8c 100644 --- a/src/operation/count_documents.rs +++ b/src/operation/count_documents.rs @@ -77,7 +77,7 @@ impl CountDocuments { impl OperationWithDefaults for CountDocuments { type O = u64; - const NAME: &'static str = Aggregate::NAME; + const NAME: &'static crate::bson_compat::CStr = Aggregate::NAME; fn build(&mut self, description: &StreamDescription) -> Result { self.aggregate.build(description) diff --git a/src/operation/create.rs b/src/operation/create.rs index 019659c3f..70f1a5cc3 100644 --- a/src/operation/create.rs +++ b/src/operation/create.rs @@ -1,6 +1,7 @@ use crate::bson::rawdoc; use crate::{ + bson_compat::{cstr, CStr}, cmap::{Command, RawCommandResponse, StreamDescription}, error::Result, operation::{append_options_to_raw_document, OperationWithDefaults, WriteConcernOnlyBody}, @@ -25,7 +26,7 @@ impl Create { impl OperationWithDefaults for Create { type O = (); - const NAME: &'static str = "create"; + const NAME: &'static CStr = cstr!("create"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { diff --git a/src/operation/create_indexes.rs b/src/operation/create_indexes.rs index 008096582..f6e1d64e4 100644 --- a/src/operation/create_indexes.rs +++ b/src/operation/create_indexes.rs @@ -1,6 +1,7 @@ use crate::bson::rawdoc; use crate::{ + bson_compat::{cstr, CStr}, bson_util::to_raw_bson_array_ser, cmap::{Command, RawCommandResponse, StreamDescription}, error::{ErrorKind, Result}, @@ -36,7 +37,7 @@ impl CreateIndexes { impl OperationWithDefaults for CreateIndexes { type O = CreateIndexesResult; - const NAME: &'static str = "createIndexes"; + const NAME: &'static CStr = cstr!("createIndexes"); fn build(&mut self, description: &StreamDescription) -> Result { // commit quorum is not supported on < 4.4 diff --git a/src/operation/delete.rs b/src/operation/delete.rs index b999fee8f..e0e2ab6b3 100644 --- a/src/operation/delete.rs +++ b/src/operation/delete.rs @@ -1,5 +1,6 @@ use crate::{ bson::{doc, Document}, + bson_compat::{cstr, CStr}, cmap::{Command, RawCommandResponse, StreamDescription}, coll::Namespace, collation::Collation, @@ -42,7 +43,7 @@ impl Delete { impl OperationWithDefaults for Delete { type O = DeleteResult; - const NAME: &'static str = "delete"; + const NAME: &'static CStr = cstr!("delete"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut delete = doc! { @@ -62,7 +63,7 @@ impl OperationWithDefaults for Delete { } let mut body = doc! { - Self::NAME: self.ns.coll.clone(), + Self::NAME.as_str(): self.ns.coll.clone(), "deletes": [delete], "ordered": true, // command monitoring tests expect this (SPEC-1130) }; diff --git a/src/operation/distinct.rs b/src/operation/distinct.rs index 3f732c3da..518142502 100644 --- a/src/operation/distinct.rs +++ b/src/operation/distinct.rs @@ -2,6 +2,7 @@ use serde::Deserialize; use crate::{ bson::{doc, rawdoc, Bson, Document, RawBsonRef, RawDocumentBuf}, + bson_compat::{cstr, CStr}, cmap::{Command, RawCommandResponse, StreamDescription}, coll::{options::DistinctOptions, Namespace}, error::Result, @@ -37,7 +38,7 @@ impl Distinct { impl OperationWithDefaults for Distinct { type O = Vec; - const NAME: &'static str = "distinct"; + const NAME: &'static CStr = cstr!("distinct"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { diff --git a/src/operation/drop_collection.rs b/src/operation/drop_collection.rs index 276a42e23..245693156 100644 --- a/src/operation/drop_collection.rs +++ b/src/operation/drop_collection.rs @@ -1,6 +1,7 @@ use crate::bson::rawdoc; use crate::{ + bson_compat::{cstr, CStr}, cmap::{Command, RawCommandResponse, StreamDescription}, error::{Error, Result}, operation::{append_options_to_raw_document, OperationWithDefaults, WriteConcernOnlyBody}, @@ -25,7 +26,7 @@ impl DropCollection { impl OperationWithDefaults for DropCollection { type O = (); - const NAME: &'static str = "drop"; + const NAME: &'static CStr = cstr!("drop"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { diff --git a/src/operation/drop_database.rs b/src/operation/drop_database.rs index 314b5f916..0a9200540 100644 --- a/src/operation/drop_database.rs +++ b/src/operation/drop_database.rs @@ -1,6 +1,7 @@ use crate::bson::rawdoc; use crate::{ + bson_compat::{cstr, CStr}, cmap::{Command, RawCommandResponse, StreamDescription}, db::options::DropDatabaseOptions, error::Result, @@ -25,7 +26,7 @@ impl DropDatabase { impl OperationWithDefaults for DropDatabase { type O = (); - const NAME: &'static str = "dropDatabase"; + const NAME: &'static CStr = cstr!("dropDatabase"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { From 88392347118e0aec6bfdb93180422acc7fd48afe Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Tue, 8 Jul 2025 10:28:02 -0400 Subject: [PATCH 3/8] wip: operations --- src/cmap/conn/command.rs | 8 ++-- src/operation.rs | 2 +- src/operation/abort_transaction.rs | 12 +----- src/operation/aggregate.rs | 4 +- src/operation/commit_transaction.rs | 6 +-- src/operation/count.rs | 4 +- src/operation/create.rs | 6 +-- src/operation/create_indexes.rs | 6 +-- src/operation/delete.rs | 6 +-- src/operation/distinct.rs | 4 +- src/operation/drop_collection.rs | 6 +-- src/operation/drop_database.rs | 6 +-- src/operation/drop_indexes.rs | 9 ++--- src/operation/find.rs | 18 ++++----- src/operation/find_and_modify.rs | 14 +++---- src/operation/get_more.rs | 20 ++++----- src/operation/insert.rs | 6 +-- src/operation/list_collections.rs | 8 ++-- src/operation/list_databases.rs | 9 ++--- src/operation/list_indexes.rs | 14 +++---- src/operation/run_command.rs | 11 ++--- src/operation/run_cursor_command.rs | 5 ++- src/operation/search_index.rs | 15 +++---- src/operation/update.rs | 63 ++++++++++++++--------------- 24 files changed, 105 insertions(+), 157 deletions(-) diff --git a/src/cmap/conn/command.rs b/src/cmap/conn/command.rs index 8904d172d..efa5e5859 100644 --- a/src/cmap/conn/command.rs +++ b/src/cmap/conn/command.rs @@ -76,14 +76,14 @@ impl Command { } pub(crate) fn new_read( - name: String, - target_db: String, + name: impl ToString, + target_db: impl ToString, read_concern: Option, body: RawDocumentBuf, ) -> Self { Self { - name, - target_db, + name: name.to_string(), + target_db: target_db.to_string(), exhaust_allowed: false, body, document_sequences: Vec::new(), diff --git a/src/operation.rs b/src/operation.rs index 4252a71fb..2cab842d3 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -318,7 +318,7 @@ where fn should_redact_body(body: &RawDocumentBuf) -> bool { if let Some(Ok((command_name, _))) = body.into_iter().next() { - HELLO_COMMAND_NAMES.contains(command_name.as_str().to_lowercase().as_str()) + HELLO_COMMAND_NAMES.contains(command_name.to_lowercase().as_str()) && body.get("speculativeAuthenticate").ok().flatten().is_some() } else { false diff --git a/src/operation/abort_transaction.rs b/src/operation/abort_transaction.rs index 8e0613128..802633e4e 100644 --- a/src/operation/abort_transaction.rs +++ b/src/operation/abort_transaction.rs @@ -38,19 +38,11 @@ impl OperationWithDefaults for AbortTransaction { }; if let Some(ref write_concern) = self.write_concern() { if !write_concern.is_empty() { - append_ser( - &mut body, - crate::bson_compat::cstr!("writeConcern"), - write_concern, - )?; + append_ser(&mut body, cstr!("writeConcern"), write_concern)?; } } - Ok(Command::new( - Self::NAME.to_string(), - "admin".to_string(), - body, - )) + Ok(Command::new(Self::NAME, "admin", body)) } fn handle_response<'a>( diff --git a/src/operation/aggregate.rs b/src/operation/aggregate.rs index 7ae67dcdd..65c04e896 100644 --- a/src/operation/aggregate.rs +++ b/src/operation/aggregate.rs @@ -65,8 +65,8 @@ impl OperationWithDefaults for Aggregate { } Ok(Command::new_read( - Self::NAME.to_string(), - self.target.db_name().to_string(), + Self::NAME, + self.target.db_name(), self.options.as_ref().and_then(|o| o.read_concern.clone()), (&body).try_into()?, )) diff --git a/src/operation/commit_transaction.rs b/src/operation/commit_transaction.rs index 58a7f77a9..ccb417cfc 100644 --- a/src/operation/commit_transaction.rs +++ b/src/operation/commit_transaction.rs @@ -34,11 +34,7 @@ impl OperationWithDefaults for CommitTransaction { append_options_to_raw_document(&mut body, self.options.as_ref())?; - Ok(Command::new( - Self::NAME.to_string(), - "admin".to_string(), - body, - )) + Ok(Command::new(Self::NAME, "admin", body)) } fn handle_response<'a>( diff --git a/src/operation/count.rs b/src/operation/count.rs index d9bb3487e..bf3757611 100644 --- a/src/operation/count.rs +++ b/src/operation/count.rs @@ -37,8 +37,8 @@ impl OperationWithDefaults for Count { append_options_to_raw_document(&mut body, self.options.as_ref())?; Ok(Command::new_read( - Self::NAME.to_string(), - self.ns.db.clone(), + Self::NAME, + &self.ns.db, self.options.as_ref().and_then(|o| o.read_concern.clone()), body, )) diff --git a/src/operation/create.rs b/src/operation/create.rs index 70f1a5cc3..56b849ec0 100644 --- a/src/operation/create.rs +++ b/src/operation/create.rs @@ -35,11 +35,7 @@ impl OperationWithDefaults for Create { append_options_to_raw_document(&mut body, self.options.as_ref())?; - Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), - body, - )) + Ok(Command::new(Self::NAME, &self.ns.db, body)) } fn handle_response<'a>( diff --git a/src/operation/create_indexes.rs b/src/operation/create_indexes.rs index f6e1d64e4..621b0d332 100644 --- a/src/operation/create_indexes.rs +++ b/src/operation/create_indexes.rs @@ -64,11 +64,7 @@ impl OperationWithDefaults for CreateIndexes { append_options_to_raw_document(&mut body, self.options.as_ref())?; - Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), - body, - )) + Ok(Command::new(Self::NAME, &self.ns.db, body)) } fn handle_response<'a>( diff --git a/src/operation/delete.rs b/src/operation/delete.rs index e0e2ab6b3..4d5d56c66 100644 --- a/src/operation/delete.rs +++ b/src/operation/delete.rs @@ -70,11 +70,7 @@ impl OperationWithDefaults for Delete { append_options(&mut body, self.options.as_ref())?; - Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), - (&body).try_into()?, - )) + Ok(Command::new(Self::NAME, &self.ns.db, (&body).try_into()?)) } fn handle_response<'a>( diff --git a/src/operation/distinct.rs b/src/operation/distinct.rs index 518142502..cbd20762e 100644 --- a/src/operation/distinct.rs +++ b/src/operation/distinct.rs @@ -50,8 +50,8 @@ impl OperationWithDefaults for Distinct { append_options_to_raw_document(&mut body, self.options.as_ref())?; Ok(Command::new_read( - Self::NAME.to_string(), - self.ns.db.clone(), + Self::NAME, + &self.ns.db, self.options.as_ref().and_then(|o| o.read_concern.clone()), body, )) diff --git a/src/operation/drop_collection.rs b/src/operation/drop_collection.rs index 245693156..48fc24edc 100644 --- a/src/operation/drop_collection.rs +++ b/src/operation/drop_collection.rs @@ -35,11 +35,7 @@ impl OperationWithDefaults for DropCollection { append_options_to_raw_document(&mut body, self.options.as_ref())?; - Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), - body, - )) + Ok(Command::new(Self::NAME, &self.ns.db, body)) } fn handle_response<'a>( diff --git a/src/operation/drop_database.rs b/src/operation/drop_database.rs index 0a9200540..f0009f3c6 100644 --- a/src/operation/drop_database.rs +++ b/src/operation/drop_database.rs @@ -35,11 +35,7 @@ impl OperationWithDefaults for DropDatabase { append_options_to_raw_document(&mut body, self.options.as_ref())?; - Ok(Command::new( - Self::NAME.to_string(), - self.target_db.clone(), - body, - )) + Ok(Command::new(Self::NAME, &self.target_db, body)) } fn handle_response<'a>( diff --git a/src/operation/drop_indexes.rs b/src/operation/drop_indexes.rs index 6201951a2..a9f7c0b4a 100644 --- a/src/operation/drop_indexes.rs +++ b/src/operation/drop_indexes.rs @@ -1,6 +1,7 @@ use crate::bson::rawdoc; use crate::{ + bson_compat::{cstr, CStr}, cmap::{Command, RawCommandResponse, StreamDescription}, error::Result, operation::{append_options_to_raw_document, OperationWithDefaults}, @@ -24,7 +25,7 @@ impl DropIndexes { impl OperationWithDefaults for DropIndexes { type O = (); - const NAME: &'static str = "dropIndexes"; + const NAME: &'static CStr = cstr!("dropIndexes"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { @@ -34,11 +35,7 @@ impl OperationWithDefaults for DropIndexes { append_options_to_raw_document(&mut body, self.options.as_ref())?; - Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), - body, - )) + Ok(Command::new(Self::NAME, &self.ns.db, body)) } fn handle_response<'a>( diff --git a/src/operation/find.rs b/src/operation/find.rs index e3da43dfb..fafffd1be 100644 --- a/src/operation/find.rs +++ b/src/operation/find.rs @@ -2,7 +2,7 @@ use crate::bson::RawDocumentBuf; use crate::{ bson::{rawdoc, Document}, - bson_compat::RawDocumentBufExt as _, + bson_compat::{cstr, CStr}, cmap::{Command, RawCommandResponse, StreamDescription}, cursor::CursorSpecification, error::{Error, Result}, @@ -32,7 +32,7 @@ impl Find { impl OperationWithDefaults for Find { type O = CursorSpecification; - const NAME: &'static str = "find"; + const NAME: &'static CStr = cstr!("find"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { @@ -42,7 +42,7 @@ impl OperationWithDefaults for Find { if let Some(ref mut options) = self.options { // negative limits should be interpreted as request for single batch as per crud spec. if options.limit.map(|limit| limit < 0) == Some(true) { - body.append_err("singleBatch", true)?; + body.append(cstr!("singleBatch"), true); } if let Some(ref mut batch_size) = options.batch_size { @@ -60,11 +60,11 @@ impl OperationWithDefaults for Find { match options.cursor_type { Some(CursorType::Tailable) => { - body.append_err("tailable", true)?; + body.append(cstr!("tailable"), true); } Some(CursorType::TailableAwait) => { - body.append_err("tailable", true)?; - body.append_err("awaitData", true)?; + body.append(cstr!("tailable"), true); + body.append(cstr!("awaitData"), true); } _ => {} }; @@ -73,11 +73,11 @@ impl OperationWithDefaults for Find { append_options_to_raw_document(&mut body, self.options.as_ref())?; let raw_filter: RawDocumentBuf = (&self.filter).try_into()?; - body.append_err("filter", raw_filter)?; + body.append(cstr!("filter"), raw_filter); Ok(Command::new_read( - Self::NAME.to_string(), - self.ns.db.clone(), + Self::NAME, + &self.ns.db, self.options.as_ref().and_then(|o| o.read_concern.clone()), body, )) diff --git a/src/operation/find_and_modify.rs b/src/operation/find_and_modify.rs index 7c67e26cf..2e7d26147 100644 --- a/src/operation/find_and_modify.rs +++ b/src/operation/find_and_modify.rs @@ -7,7 +7,7 @@ use serde::{de::DeserializeOwned, Deserialize}; use self::options::FindAndModifyOptions; use crate::{ bson::{doc, rawdoc, Document, RawBson, RawDocumentBuf}, - bson_compat::{deserialize_from_slice, RawDocumentBufExt as _}, + bson_compat::{cstr, deserialize_from_slice, CStr}, bson_util, cmap::{Command, RawCommandResponse, StreamDescription}, coll::{options::UpdateModifications, Namespace}, @@ -56,7 +56,7 @@ impl FindAndModify { impl OperationWithDefaults for FindAndModify { type O = Option; - const NAME: &'static str = "findAndModify"; + const NAME: &'static CStr = cstr!("findAndModify"); fn build(&mut self, description: &StreamDescription) -> Result { if let Some(ref options) = self.options { @@ -76,19 +76,15 @@ impl OperationWithDefaults for FindAndModify { }; match &self.modification { - Modification::Delete => body.append_err("remove", true)?, + Modification::Delete => body.append(cstr!("remove"), true), Modification::Update(update_or_replace) => { - update_or_replace.append_to_rawdoc(&mut body, "update")? + update_or_replace.append_to_rawdoc(&mut body, cstr!("update"))? } } append_options_to_raw_document(&mut body, self.options.as_ref())?; - Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), - body, - )) + Ok(Command::new(Self::NAME, &self.ns.db, body)) } fn handle_response<'a>( diff --git a/src/operation/get_more.rs b/src/operation/get_more.rs index 88cf72a6a..046cccbde 100644 --- a/src/operation/get_more.rs +++ b/src/operation/get_more.rs @@ -2,7 +2,7 @@ use std::{collections::VecDeque, time::Duration}; use crate::{ bson::{rawdoc, RawBson}, - bson_compat::RawDocumentBufExt as _, + bson_compat::{cstr, CStr}, }; use serde::Deserialize; @@ -52,7 +52,7 @@ impl<'conn> GetMore<'conn> { impl OperationWithDefaults for GetMore<'_> { type O = GetMoreResult; - const NAME: &'static str = "getMore"; + const NAME: &'static CStr = cstr!("getMore"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { @@ -63,27 +63,23 @@ impl OperationWithDefaults for GetMore<'_> { if let Some(batch_size) = self.batch_size { let batch_size = Checked::from(batch_size).try_into::()?; if batch_size != 0 { - body.append_err("batchSize", batch_size)?; + body.append(cstr!("batchSize"), batch_size); } } if let Some(ref max_time) = self.max_time { - body.append_err( - "maxTimeMS", + body.append( + cstr!("maxTimeMS"), max_time.as_millis().try_into().unwrap_or(i32::MAX), - )?; + ); } if let Some(comment) = &self.comment { let raw_comment: RawBson = comment.clone().try_into()?; - body.append_err("comment", raw_comment)?; + body.append(cstr!("comment"), raw_comment); } - Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), - body, - )) + Ok(Command::new(Self::NAME, &self.ns.db, body)) } fn handle_response<'a>( diff --git a/src/operation/insert.rs b/src/operation/insert.rs index 6f0e48969..d42ef5b47 100644 --- a/src/operation/insert.rs +++ b/src/operation/insert.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use crate::{ bson::{rawdoc, Bson, RawDocument}, - bson_compat::RawDocumentBufExt as _, + bson_compat::{cstr, CStr}, bson_util::{ array_entry_size_bytes, extend_raw_document_buf, @@ -54,7 +54,7 @@ impl<'a> Insert<'a> { impl OperationWithDefaults for Insert<'_> { type O = InsertManyResult; - const NAME: &'static str = "insert"; + const NAME: &'static CStr = cstr!("insert"); fn build(&mut self, description: &StreamDescription) -> Result { self.inserted_ids.clear(); @@ -120,7 +120,7 @@ impl OperationWithDefaults for Insert<'_> { if self.encrypted { // Auto-encryption does not support document sequences - body.append_err("documents", vec_to_raw_array_buf(docs)?)?; + body.append(cstr!("documents"), vec_to_raw_array_buf(docs)); Ok(Command::new(Self::NAME, &self.ns.db, body)) } else { let mut command = Command::new(Self::NAME, &self.ns.db, body); diff --git a/src/operation/list_collections.rs b/src/operation/list_collections.rs index fba2e3848..eaad8a0a9 100644 --- a/src/operation/list_collections.rs +++ b/src/operation/list_collections.rs @@ -1,7 +1,7 @@ use crate::bson::rawdoc; use crate::{ - bson_compat::RawDocumentBufExt as _, + bson_compat::{cstr, CStr}, cmap::{Command, RawCommandResponse, StreamDescription}, cursor::CursorSpecification, error::Result, @@ -35,7 +35,7 @@ impl ListCollections { impl OperationWithDefaults for ListCollections { type O = CursorSpecification; - const NAME: &'static str = "listCollections"; + const NAME: &'static CStr = cstr!("listCollections"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { @@ -48,11 +48,11 @@ impl OperationWithDefaults for ListCollections { name_only = false; } } - body.append_err("nameOnly", name_only)?; + body.append(cstr!("nameOnly"), name_only); append_options_to_raw_document(&mut body, self.options.as_ref())?; - Ok(Command::new(Self::NAME.to_string(), self.db.clone(), body)) + Ok(Command::new(Self::NAME, &self.db, body)) } fn handle_response<'a>( diff --git a/src/operation/list_databases.rs b/src/operation/list_databases.rs index 8510b42ab..cf5b67d02 100644 --- a/src/operation/list_databases.rs +++ b/src/operation/list_databases.rs @@ -3,6 +3,7 @@ use serde::Deserialize; use crate::{ bson::{doc, RawDocumentBuf}, + bson_compat::{cstr, CStr}, cmap::{Command, RawCommandResponse, StreamDescription}, db::options::ListDatabasesOptions, error::Result, @@ -27,7 +28,7 @@ impl ListDatabases { impl OperationWithDefaults for ListDatabases { type O = Vec; - const NAME: &'static str = "listDatabases"; + const NAME: &'static CStr = cstr!("listDatabases"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { @@ -37,11 +38,7 @@ impl OperationWithDefaults for ListDatabases { append_options_to_raw_document(&mut body, self.options.as_ref())?; - Ok(Command::new( - Self::NAME.to_string(), - "admin".to_string(), - body, - )) + Ok(Command::new(Self::NAME, "admin", body)) } fn handle_response<'a>( diff --git a/src/operation/list_indexes.rs b/src/operation/list_indexes.rs index 7de76ea21..591c32d18 100644 --- a/src/operation/list_indexes.rs +++ b/src/operation/list_indexes.rs @@ -1,7 +1,7 @@ use crate::bson::rawdoc; use crate::{ - bson_compat::RawDocumentBufExt as _, + bson_compat::{cstr, CStr}, checked::Checked, cmap::{Command, RawCommandResponse, StreamDescription}, cursor::CursorSpecification, @@ -28,23 +28,19 @@ impl ListIndexes { impl OperationWithDefaults for ListIndexes { type O = CursorSpecification; - const NAME: &'static str = "listIndexes"; + const NAME: &'static CStr = cstr!("listIndexes"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { - "listIndexes": self.ns.coll.clone(), + Self::NAME: self.ns.coll.clone(), }; if let Some(size) = self.options.as_ref().and_then(|o| o.batch_size) { let size = Checked::from(size).try_into::()?; - body.append_err("cursor", rawdoc! { "batchSize": size })?; + body.append(cstr!("cursor"), rawdoc! { "batchSize": size }); } append_options_to_raw_document(&mut body, self.options.as_ref())?; - Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), - body, - )) + Ok(Command::new(Self::NAME, &self.ns.db, body)) } fn handle_response<'a>( diff --git a/src/operation/run_command.rs b/src/operation/run_command.rs index 4ad7c050a..fe5a5a869 100644 --- a/src/operation/run_command.rs +++ b/src/operation/run_command.rs @@ -2,6 +2,7 @@ use std::convert::TryInto; use crate::{ bson::{Document, RawBsonRef, RawDocumentBuf}, + bson_compat::{cstr, CStr}, client::SESSIONS_UNSUPPORTED_COMMANDS, cmap::{conn::PinnedConnectionHandle, Command, RawCommandResponse, StreamDescription}, error::{ErrorKind, Result}, @@ -33,7 +34,7 @@ impl<'conn> RunCommand<'conn> { } } - fn command_name(&self) -> Option<&str> { + fn command_name(&self) -> Option<&CStr> { self.command .into_iter() .next() @@ -47,7 +48,7 @@ impl OperationWithDefaults for RunCommand<'_> { // Since we can't actually specify a string statically here, we just put a descriptive string // that should fail loudly if accidentally passed to the server. - const NAME: &'static str = "$genericRunCommand"; + const NAME: &'static CStr = cstr!("$genericRunCommand"); fn build(&mut self, _description: &StreamDescription) -> Result { let command_name = self @@ -56,11 +57,7 @@ impl OperationWithDefaults for RunCommand<'_> { message: "an empty document cannot be passed to a run_command operation".into(), })?; - Ok(Command::new( - command_name.to_string(), - self.db.clone(), - self.command.clone(), - )) + Ok(Command::new(command_name, &self.db, self.command.clone())) } fn extract_at_cluster_time( diff --git a/src/operation/run_cursor_command.rs b/src/operation/run_cursor_command.rs index d535c04b0..ad96eca79 100644 --- a/src/operation/run_cursor_command.rs +++ b/src/operation/run_cursor_command.rs @@ -1,6 +1,7 @@ use futures_util::FutureExt; use crate::{ + bson_compat::{cstr, CStr}, cmap::{conn::PinnedConnectionHandle, Command, RawCommandResponse, StreamDescription}, concern::WriteConcern, cursor::CursorSpecification, @@ -34,7 +35,7 @@ impl<'conn> RunCursorCommand<'conn> { impl Operation for RunCursorCommand<'_> { type O = CursorSpecification; - const NAME: &'static str = "run_cursor_command"; + const NAME: &'static CStr = cstr!("run_cursor_command"); fn build(&mut self, description: &StreamDescription) -> Result { self.run_command.build(description) @@ -87,7 +88,7 @@ impl Operation for RunCursorCommand<'_> { self.run_command.pinned_connection() } - fn name(&self) -> &str { + fn name(&self) -> &CStr { self.run_command.name() } diff --git a/src/operation/search_index.rs b/src/operation/search_index.rs index 784460339..2248cbf95 100644 --- a/src/operation/search_index.rs +++ b/src/operation/search_index.rs @@ -3,6 +3,7 @@ use serde::Deserialize; use crate::{ bson::{doc, Document}, + bson_compat::{cstr, CStr}, bson_util::to_raw_bson_array_ser, cmap::{Command, RawCommandResponse}, error::Result, @@ -26,7 +27,7 @@ impl CreateSearchIndexes { impl OperationWithDefaults for CreateSearchIndexes { type O = Vec; - const NAME: &'static str = "createSearchIndexes"; + const NAME: &'static CStr = cstr!("createSearchIndexes"); fn build(&mut self, _description: &crate::cmap::StreamDescription) -> Result { Ok(Command::new( @@ -93,7 +94,7 @@ impl UpdateSearchIndex { impl OperationWithDefaults for UpdateSearchIndex { type O = (); - const NAME: &'static str = "updateSearchIndex"; + const NAME: &'static CStr = cstr!("updateSearchIndex"); fn build( &mut self, @@ -101,8 +102,8 @@ impl OperationWithDefaults for UpdateSearchIndex { ) -> crate::error::Result { let raw_def: RawDocumentBuf = (&self.definition).try_into()?; Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), + Self::NAME, + &self.ns.db, rawdoc! { Self::NAME: self.ns.coll.as_str(), "name": self.name.as_str(), @@ -142,12 +143,12 @@ impl DropSearchIndex { impl OperationWithDefaults for DropSearchIndex { type O = (); - const NAME: &'static str = "dropSearchIndex"; + const NAME: &'static CStr = cstr!("dropSearchIndex"); fn build(&mut self, _description: &crate::cmap::StreamDescription) -> Result { Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), + Self::NAME, + &self.ns.db, rawdoc! { Self::NAME: self.ns.coll.as_str(), "name": self.name.as_str(), diff --git a/src/operation/update.rs b/src/operation/update.rs index 12df4ce8a..c4c8e6a7d 100644 --- a/src/operation/update.rs +++ b/src/operation/update.rs @@ -2,7 +2,7 @@ use serde::Deserialize; use crate::{ bson::{doc, rawdoc, Document, RawArrayBuf, RawBson, RawDocumentBuf}, - bson_compat::{RawArrayBufExt as _, RawDocumentBufExt as _}, + bson_compat::{cstr, CStr, RawDocumentBufExt as _}, bson_util, cmap::{Command, RawCommandResponse, StreamDescription}, error::{convert_insert_many_error, Result}, @@ -21,21 +21,21 @@ pub(crate) enum UpdateOrReplace { } impl UpdateOrReplace { - pub(crate) fn append_to_rawdoc(&self, doc: &mut RawDocumentBuf, key: &str) -> Result<()> { + pub(crate) fn append_to_rawdoc(&self, doc: &mut RawDocumentBuf, key: &CStr) -> Result<()> { match self { Self::UpdateModifications(update_modifications) => match update_modifications { UpdateModifications::Document(document) => { let raw = RawDocumentBuf::from_document(document)?; - doc.append_err(key, raw)?; + doc.append(key, raw); } UpdateModifications::Pipeline(pipeline) => { let raw = bson_util::to_raw_bson_array(pipeline)?; - doc.append_err(key, raw)?; + doc.append(key, raw); } }, Self::Replacement(replacement_doc) => { bson_util::replacement_raw_document_check(replacement_doc)?; - doc.append_ref_compat(key, replacement_doc)?; + doc.append_ref_compat(key, replacement_doc); } } @@ -95,7 +95,7 @@ impl Update { impl OperationWithDefaults for Update { type O = UpdateResult; - const NAME: &'static str = "update"; + const NAME: &'static CStr = cstr!("update"); fn build(&mut self, _description: &StreamDescription) -> Result { let mut body = rawdoc! { @@ -105,71 +105,70 @@ impl OperationWithDefaults for Update { let mut update = rawdoc! { "q": RawDocumentBuf::from_document(&self.filter)?, }; - self.update.append_to_rawdoc(&mut update, "u")?; + self.update.append_to_rawdoc(&mut update, cstr!("u"))?; if let Some(ref options) = self.options { if let Some(upsert) = options.upsert { - update.append_err("upsert", upsert)?; + update.append(cstr!("upsert"), upsert); } if let Some(ref array_filters) = options.array_filters { - update.append_err("arrayFilters", bson_util::to_raw_bson_array(array_filters)?)?; + update.append( + cstr!("arrayFilters"), + bson_util::to_raw_bson_array(array_filters)?, + ); } if let Some(ref hint) = options.hint { - update.append_err("hint", hint.to_raw_bson()?)?; + update.append(cstr!("hint"), hint.to_raw_bson()?); } if let Some(ref collation) = options.collation { - update.append_err( - "collation", + update.append( + cstr!("collation"), crate::bson_compat::serialize_to_raw_document_buf(&collation)?, - )?; + ); } if let Some(bypass_doc_validation) = options.bypass_document_validation { - body.append_err("bypassDocumentValidation", bypass_doc_validation)?; + body.append(cstr!("bypassDocumentValidation"), bypass_doc_validation); } if let Some(ref write_concern) = options.write_concern { if !write_concern.is_empty() { - body.append_err( - "writeConcern", + body.append( + cstr!("writeConcern"), crate::bson_compat::serialize_to_raw_document_buf(write_concern)?, - )?; + ); } } if let Some(ref let_vars) = options.let_vars { - body.append_err( - "let", + body.append( + cstr!("let"), crate::bson_compat::serialize_to_raw_document_buf(&let_vars)?, - )?; + ); } if let Some(ref comment) = options.comment { - body.append_err("comment", RawBson::try_from(comment.clone())?)?; + body.append(cstr!("comment"), RawBson::try_from(comment.clone())?); } if let Some(ref sort) = options.sort { - update.append_err("sort", RawDocumentBuf::from_document(sort)?)?; + update.append(cstr!("sort"), RawDocumentBuf::from_document(sort)?); } }; if let Some(multi) = self.multi { - update.append_err("multi", multi)?; + update.append(cstr!("multi"), multi); } let mut updates = RawArrayBuf::new(); - updates.push_err(update)?; - body.append_err("updates", updates)?; - body.append_err("ordered", true)?; // command monitoring tests expect this (SPEC-1130) - - Ok(Command::new( - Self::NAME.to_string(), - self.ns.db.clone(), - body, - )) + updates.push(update); + body.append(cstr!("updates"), updates); + body.append(cstr!("ordered"), true); // command monitoring tests expect this (SPEC-1130) + + Ok(Command::new(Self::NAME, &self.ns.db, body)) } fn handle_response<'a>( From f1aa1edc069ac8f4673a4d91e7b28d5a4201effd Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Tue, 8 Jul 2025 10:52:28 -0400 Subject: [PATCH 4/8] 2.x fixes --- src/bson_compat.rs | 11 ++++++----- src/bson_util.rs | 3 ++- src/operation/aggregate.rs | 3 ++- src/operation/delete.rs | 3 ++- src/sdam/description/server.rs | 8 +------- src/test/db.rs | 8 ++++---- 6 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/bson_compat.rs b/src/bson_compat.rs index c02c39737..1d1e216dc 100644 --- a/src/bson_compat.rs +++ b/src/bson_compat.rs @@ -15,6 +15,8 @@ macro_rules! cstr { $text }; } +#[cfg(not(feature = "bson-3"))] +pub(crate) use cstr; pub(crate) trait RawDocumentBufExt: Sized { fn append_ref_compat<'a>( @@ -40,13 +42,12 @@ impl RawDocumentBufExt for crate::bson::RawDocumentBuf { #[cfg(not(feature = "bson-3"))] impl RawDocumentBufExt for crate::bson::RawDocumentBuf { - fn append_ref_err<'a>( + fn append_ref_compat<'a>( &mut self, - key: impl AsRef, + key: impl AsRef, value: impl Into>, - ) -> RawResult<()> { - self.append_ref(key, value); - Ok(()) + ) { + self.append_ref(key, value) } fn decode_from_bytes(data: Vec) -> RawResult { diff --git a/src/bson_util.rs b/src/bson_util.rs index 942fac561..bfa8e26f4 100644 --- a/src/bson_util.rs +++ b/src/bson_util.rs @@ -127,7 +127,8 @@ pub(crate) fn replacement_document_check(replacement: &Document) -> Result<()> { pub(crate) fn replacement_raw_document_check(replacement: &RawDocumentBuf) -> Result<()> { if let Some((key, _)) = replacement.iter().next().transpose()? { - if key.as_str().starts_with('$') { + let key: &str = key.into(); + if key.starts_with('$') { return Err(ErrorKind::InvalidArgument { message: "replacement document must not contain update modifiers".to_string(), } diff --git a/src/operation/aggregate.rs b/src/operation/aggregate.rs index 65c04e896..7c2cbf473 100644 --- a/src/operation/aggregate.rs +++ b/src/operation/aggregate.rs @@ -50,8 +50,9 @@ impl OperationWithDefaults for Aggregate { const NAME: &'static CStr = cstr!("aggregate"); fn build(&mut self, _description: &StreamDescription) -> Result { + let name: &str = Self::NAME.into(); let mut body = doc! { - Self::NAME.as_str(): self.target.to_bson(), + name: self.target.to_bson(), "pipeline": bson_util::to_bson_array(&self.pipeline), "cursor": {} }; diff --git a/src/operation/delete.rs b/src/operation/delete.rs index 4d5d56c66..43a404681 100644 --- a/src/operation/delete.rs +++ b/src/operation/delete.rs @@ -62,8 +62,9 @@ impl OperationWithDefaults for Delete { delete.insert("hint", crate::bson_compat::serialize_to_bson(&hint)?); } + let name: &str = Self::NAME.into(); let mut body = doc! { - Self::NAME.as_str(): self.ns.coll.clone(), + name: self.ns.coll.clone(), "deletes": [delete], "ordered": true, // command monitoring tests expect this (SPEC-1130) }; diff --git a/src/sdam/description/server.rs b/src/sdam/description/server.rs index 63be3eb88..cc9fa00b8 100644 --- a/src/sdam/description/server.rs +++ b/src/sdam/description/server.rs @@ -113,13 +113,7 @@ impl From for RawBson { #[cfg(feature = "bson-3")] impl crate::bson::raw::BindRawBsonRef for TopologyVersion { - fn bind(self, f: F) -> R - where - F: for<'a> FnOnce(bson3::RawBsonRef<'a>) -> R, - { - let raw: RawBson = self.into(); - raw.bind(f) - } + type Target = crate::bson::raw::BindValue; } /// A description of the most up-to-date information known about a server. diff --git a/src/test/db.rs b/src/test/db.rs index 73babc0a4..b13df1abc 100644 --- a/src/test/db.rs +++ b/src/test/db.rs @@ -1,6 +1,6 @@ use std::cmp::Ord; -use crate::{bson::RawDocumentBuf, bson_compat::RawDocumentBufExt as _}; +use crate::{bson::RawDocumentBuf, bson_compat::cstr}; use futures::{stream::TryStreamExt, StreamExt}; use serde::Deserialize; @@ -431,7 +431,7 @@ async fn test_run_command() { // Test run_raw_command { let mut cmd = RawDocumentBuf::new(); - cmd.append_err("ping", 1).unwrap(); + cmd.append(cstr!("ping"), 1); let got = database.run_raw_command(cmd).await.unwrap(); assert_eq!(crate::bson_util::get_int(got.get("ok").unwrap()), Some(1)); } @@ -459,8 +459,8 @@ async fn test_run_command() { // Test run_raw_cursor_command { let mut cmd = RawDocumentBuf::new(); - cmd.append_err("find", "coll").unwrap(); - cmd.append_err("filter", RawDocumentBuf::new()).unwrap(); + cmd.append(cstr!("find"), "coll"); + cmd.append(cstr!("filter"), RawDocumentBuf::new()); let cursor = database.run_raw_cursor_command(cmd).await.unwrap(); let v: Vec> = cursor.collect().await; From a2ed6a757a482687ef0f8ecb1715e79b901a0427 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Tue, 8 Jul 2025 11:09:39 -0400 Subject: [PATCH 5/8] 3.0 full feature fixes --- src/client.rs | 9 ++------- src/client/csfle/state_machine.rs | 18 ++++++++---------- src/client/executor.rs | 14 +++++--------- src/cmap/establish/handshake.rs | 14 ++++++-------- src/operation/raw_output.rs | 5 +++-- src/test/csfle/prose.rs | 15 ++++++++------- 6 files changed, 32 insertions(+), 43 deletions(-) diff --git a/src/client.rs b/src/client.rs index 9741f1fe6..7f5defc63 100644 --- a/src/client.rs +++ b/src/client.rs @@ -447,12 +447,7 @@ impl Client { criteria: Option<&SelectionCriteria>, ) -> Result { let (server, _) = self - .select_server( - criteria, - crate::bson_compat::cstr!("Test select server"), - None, - |_, _| None, - ) + .select_server(criteria, "Test select server", None, |_, _| None) .await?; Ok(server.address.clone()) } @@ -463,7 +458,7 @@ impl Client { &self, criteria: Option<&SelectionCriteria>, #[allow(unused_variables)] // we only use the operation_name for tracing. - operation_name: &crate::bson_compat::CStr, + operation_name: &str, deprioritized: Option<&ServerAddress>, override_criteria: OverrideCriteriaFn, ) -> Result<(SelectedServer, SelectionCriteria)> { diff --git a/src/client/csfle/state_machine.rs b/src/client/csfle/state_machine.rs index a2fbe27c9..5d7363126 100644 --- a/src/client/csfle/state_machine.rs +++ b/src/client/csfle/state_machine.rs @@ -7,7 +7,7 @@ use std::{ use crate::{ bson::{rawdoc, Document, RawDocument, RawDocumentBuf}, - bson_compat::RawDocumentBufExt as _, + bson_compat::{cstr, CString}, }; use futures_util::{stream, TryStreamExt}; use mongocrypt::ctx::{Ctx, KmsCtx, KmsProviderType, State}; @@ -245,6 +245,7 @@ impl CryptExecutor { continue; } + let prov_name: CString = provider.as_string().try_into()?; match provider.provider_type() { KmsProviderType::Aws => { #[cfg(feature = "aws-auth")] @@ -264,9 +265,9 @@ impl CryptExecutor { "secretAccessKey": aws_creds.secret_key(), }; if let Some(token) = aws_creds.session_token() { - creds.append_err("sessionToken", token)?; + creds.append(cstr!("sessionToken"), token); } - kms_providers.append_err(provider.as_string(), creds)?; + kms_providers.append(prov_name, creds); } #[cfg(not(feature = "aws-auth"))] { @@ -279,10 +280,7 @@ impl CryptExecutor { KmsProviderType::Azure => { #[cfg(feature = "azure-kms")] { - kms_providers.append_err( - provider.as_string(), - self.azure.get_token().await?, - )?; + kms_providers.append(prov_name, self.azure.get_token().await?); } #[cfg(not(feature = "azure-kms"))] { @@ -330,10 +328,10 @@ impl CryptExecutor { .send() .await .map_err(|e| kms_error(e.to_string()))?; - kms_providers.append_err( - "gcp", + kms_providers.append( + cstr!("gcp"), rawdoc! { "accessToken": response.access_token }, - )?; + ); } #[cfg(not(feature = "gcp-kms"))] { diff --git a/src/client/executor.rs b/src/client/executor.rs index e4f1db650..822e1a449 100644 --- a/src/client/executor.rs +++ b/src/client/executor.rs @@ -320,10 +320,11 @@ impl Client { .and_then(|s| s.transaction.pinned_mongos()) .or_else(|| op.selection_criteria()); + let op_name: &str = op.name().into(); let (server, effective_criteria) = match self .select_server( selection_criteria, - op.name(), + op_name, retry.as_ref().map(|r| &r.first_server), op.override_criteria(), ) @@ -879,10 +880,7 @@ impl Client { } } - async fn select_data_bearing_server( - &self, - operation_name: &crate::bson_compat::CStr, - ) -> Result<()> { + async fn select_data_bearing_server(&self, operation_name: &str) -> Result<()> { let topology_type = self.inner.topology.topology_type(); let criteria = SelectionCriteria::Predicate(Arc::new(move |server_info| { let server_type = server_info.server_type(); @@ -905,10 +903,8 @@ impl Client { // sessions are supported or not. match initial_status { TransactionSupportStatus::Undetermined => { - self.select_data_bearing_server(crate::bson_compat::cstr!( - "Check transactions support status" - )) - .await?; + self.select_data_bearing_server("Check transactions support status") + .await?; Ok(self.inner.topology.transaction_support_status()) } _ => Ok(initial_status), diff --git a/src/cmap/establish/handshake.rs b/src/cmap/establish/handshake.rs index 5474b31a5..781cca05a 100644 --- a/src/cmap/establish/handshake.rs +++ b/src/cmap/establish/handshake.rs @@ -391,14 +391,12 @@ impl Handshaker { feature = "snappy-compression" ))] if let Some(ref compressors) = options.compressors { - use crate::bson::RawArrayBuf; - - use crate::bson_compat::RawArrayBufExt as _; - - command.body.append_err( - "compression", - RawArrayBuf::from_iter_err(compressors.iter().map(|compressor| compressor.name()))?, - )?; + command.body.append( + crate::bson_compat::cstr!("compression"), + crate::bson::RawArrayBuf::from_iter( + compressors.iter().map(|compressor| compressor.name()), + ), + ); } Ok(Self { diff --git a/src/operation/raw_output.rs b/src/operation/raw_output.rs index 8d90f3170..e7dec57d6 100644 --- a/src/operation/raw_output.rs +++ b/src/operation/raw_output.rs @@ -1,6 +1,7 @@ use futures_util::FutureExt; use crate::{ + bson_compat::CStr, cmap::{Command, RawCommandResponse, StreamDescription}, error::Result, BoxFuture, @@ -15,7 +16,7 @@ pub(crate) struct RawOutput(pub(crate) Op); impl Operation for RawOutput { type O = RawCommandResponse; - const NAME: &'static str = Op::NAME; + const NAME: &'static CStr = Op::NAME; fn build(&mut self, description: &StreamDescription) -> Result { self.0.build(description) @@ -76,7 +77,7 @@ impl Operation for RawOutput { self.0.pinned_connection() } - fn name(&self) -> &str { + fn name(&self) -> &CStr { self.0.name() } } diff --git a/src/test/csfle/prose.rs b/src/test/csfle/prose.rs index b8adebd34..937769dce 100644 --- a/src/test/csfle/prose.rs +++ b/src/test/csfle/prose.rs @@ -1757,10 +1757,11 @@ mod range_explicit_encryption { }; // Case 2: Find encrypted range and return the maximum + let ckey: &crate::bson_compat::CStr = key.as_str().try_into()?; let query = rawdoc! { "$and": [ - { &key: { "$gte": bson_numbers[&6].clone() } }, - { &key: { "$lte": bson_numbers[&200].clone() } }, + { ckey: { "$gte": bson_numbers[&6].clone() } }, + { ckey: { "$lte": bson_numbers[&200].clone() } }, ] }; let find_payload = client_encryption @@ -1780,8 +1781,8 @@ mod range_explicit_encryption { // Case 3: Find encrypted range and return the minimum let query = rawdoc! { "$and": [ - { &key: { "$gte": bson_numbers[&0].clone() } }, - { &key: { "$lte": bson_numbers[&6].clone() } }, + { ckey: { "$gte": bson_numbers[&0].clone() } }, + { ckey: { "$lte": bson_numbers[&6].clone() } }, ] }; let find_payload = client_encryption @@ -1803,7 +1804,7 @@ mod range_explicit_encryption { // Case 4: Find encrypted range with an open range query let query = rawdoc! { "$and": [ - { &key: { "$gt": bson_numbers[&30].clone() } }, + { ckey: { "$gt": bson_numbers[&30].clone() } }, ] }; let find_payload = client_encryption @@ -1855,9 +1856,9 @@ mod range_explicit_encryption { // Case 7: Encrypting a document of a different type errors if bson_type != "DoubleNoPrecision" && bson_type != "DecimalNoPrecision" { let value = if bson_type == "Int" { - rawdoc! { &key: { "$numberDouble": "6" } } + rawdoc! { ckey: { "$numberDouble": "6" } } } else { - rawdoc! { &key: { "$numberInt": "6" } } + rawdoc! { ckey: { "$numberInt": "6" } } }; let error = client_encryption .encrypt(value, key1_id.clone(), Algorithm::Range) From 87a4087e8b853056a22bf812aeab98709d058dc8 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Tue, 8 Jul 2025 11:19:02 -0400 Subject: [PATCH 6/8] point patch at git --- Cargo.lock | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 3b9fdb80c..f78164248 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,6 +237,7 @@ dependencies = [ [[package]] name = "bson" version = "3.0.0" +source = "git+https://github.com/abr-egn/bson-rust?branch=RUST-1992%2Fcstr-driver#5d3355d9501edded6d1c031f75eb9a2daee9ddb5" dependencies = [ "ahash", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index 853bb33b1..df4a83408 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -137,7 +137,7 @@ optional = true features = ["serde"] [patch."https://github.com/mongodb/bson-rust"] -bson3 = { path = "../../../bson-rust/RUST-1992/cstr-driver", package = "bson" } +bson3 = { package = "bson", git = "https://github.com/abr-egn/bson-rust", branch = "RUST-1992/cstr-driver" } [dependencies.mongocrypt] git = "https://github.com/mongodb/libmongocrypt-rust.git" From 198d28555d2982b0a9f5582236097755b99d8b3d Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Tue, 8 Jul 2025 12:35:28 -0400 Subject: [PATCH 7/8] clippy fixes --- src/bson_compat.rs | 11 +++++++++++ src/bson_util.rs | 3 +-- src/client/executor.rs | 3 +-- src/cmap/establish/handshake.rs | 4 ++-- src/operation/aggregate.rs | 3 +-- src/operation/delete.rs | 3 +-- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/bson_compat.rs b/src/bson_compat.rs index 1d1e216dc..01a175cc2 100644 --- a/src/bson_compat.rs +++ b/src/bson_compat.rs @@ -18,6 +18,17 @@ macro_rules! cstr { #[cfg(not(feature = "bson-3"))] pub(crate) use cstr; +pub(crate) fn cstr_to_str(cs: &CStr) -> &str { + #[cfg(feature = "bson-3")] + { + cs.as_str() + } + #[cfg(not(feature = "bson-3"))] + { + cs + } +} + pub(crate) trait RawDocumentBufExt: Sized { fn append_ref_compat<'a>( &mut self, diff --git a/src/bson_util.rs b/src/bson_util.rs index bfa8e26f4..c5c7f9cfb 100644 --- a/src/bson_util.rs +++ b/src/bson_util.rs @@ -127,8 +127,7 @@ pub(crate) fn replacement_document_check(replacement: &Document) -> Result<()> { pub(crate) fn replacement_raw_document_check(replacement: &RawDocumentBuf) -> Result<()> { if let Some((key, _)) = replacement.iter().next().transpose()? { - let key: &str = key.into(); - if key.starts_with('$') { + if crate::bson_compat::cstr_to_str(key).starts_with('$') { return Err(ErrorKind::InvalidArgument { message: "replacement document must not contain update modifiers".to_string(), } diff --git a/src/client/executor.rs b/src/client/executor.rs index 822e1a449..7bed2272b 100644 --- a/src/client/executor.rs +++ b/src/client/executor.rs @@ -320,11 +320,10 @@ impl Client { .and_then(|s| s.transaction.pinned_mongos()) .or_else(|| op.selection_criteria()); - let op_name: &str = op.name().into(); let (server, effective_criteria) = match self .select_server( selection_criteria, - op_name, + crate::bson_compat::cstr_to_str(op.name()), retry.as_ref().map(|r| &r.first_server), op.override_criteria(), ) diff --git a/src/cmap/establish/handshake.rs b/src/cmap/establish/handshake.rs index 781cca05a..605f2f6b2 100644 --- a/src/cmap/establish/handshake.rs +++ b/src/cmap/establish/handshake.rs @@ -430,14 +430,14 @@ impl Handshaker { let body = &mut command.body; let body_size = body.as_bytes().len(); let mut metadata = self.metadata.clone(); - let mut meta_doc: RawDocumentBuf = (&metadata).try_into()?; + let mut meta_doc: RawDocumentBuf = (&metadata).into(); const OVERHEAD: usize = 1 /* tag */ + 6 /* name */ + 1 /* null */; for trunc_fn in METADATA_TRUNCATIONS { if body_size + OVERHEAD + meta_doc.as_bytes().len() <= MAX_HELLO_SIZE { break; } trunc_fn(&mut metadata); - meta_doc = (&metadata).try_into()?; + meta_doc = (&metadata).into(); } #[cfg(test)] #[allow(clippy::incompatible_msrv)] diff --git a/src/operation/aggregate.rs b/src/operation/aggregate.rs index 7c2cbf473..c6d36dfc1 100644 --- a/src/operation/aggregate.rs +++ b/src/operation/aggregate.rs @@ -50,9 +50,8 @@ impl OperationWithDefaults for Aggregate { const NAME: &'static CStr = cstr!("aggregate"); fn build(&mut self, _description: &StreamDescription) -> Result { - let name: &str = Self::NAME.into(); let mut body = doc! { - name: self.target.to_bson(), + crate::bson_compat::cstr_to_str(Self::NAME): self.target.to_bson(), "pipeline": bson_util::to_bson_array(&self.pipeline), "cursor": {} }; diff --git a/src/operation/delete.rs b/src/operation/delete.rs index 43a404681..5a388c1d2 100644 --- a/src/operation/delete.rs +++ b/src/operation/delete.rs @@ -62,9 +62,8 @@ impl OperationWithDefaults for Delete { delete.insert("hint", crate::bson_compat::serialize_to_bson(&hint)?); } - let name: &str = Self::NAME.into(); let mut body = doc! { - name: self.ns.coll.clone(), + crate::bson_compat::cstr_to_str(Self::NAME): self.ns.coll.clone(), "deletes": [delete], "ordered": true, // command monitoring tests expect this (SPEC-1130) }; From 2376e5c6ddb681bd694760390ca541038d61f828 Mon Sep 17 00:00:00 2001 From: Abraham Egnor Date: Tue, 8 Jul 2025 21:03:55 -0400 Subject: [PATCH 8/8] remove patch --- Cargo.lock | 2 +- Cargo.toml | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f78164248..9a3156437 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -237,7 +237,7 @@ dependencies = [ [[package]] name = "bson" version = "3.0.0" -source = "git+https://github.com/abr-egn/bson-rust?branch=RUST-1992%2Fcstr-driver#5d3355d9501edded6d1c031f75eb9a2daee9ddb5" +source = "git+https://github.com/mongodb/bson-rust?branch=main#194177a1593835bf897dd2408db31ce949e32e77" dependencies = [ "ahash", "base64 0.22.1", diff --git a/Cargo.toml b/Cargo.toml index df4a83408..c8e8d8733 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -136,9 +136,6 @@ version = "3.0.0" optional = true features = ["serde"] -[patch."https://github.com/mongodb/bson-rust"] -bson3 = { package = "bson", git = "https://github.com/abr-egn/bson-rust", branch = "RUST-1992/cstr-driver" } - [dependencies.mongocrypt] git = "https://github.com/mongodb/libmongocrypt-rust.git" branch = "main"