diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 418fad56b7674..4be6e265f0f84 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -434,6 +434,12 @@ pub fn transactional(attr: TokenStream, input: TokenStream) -> TokenStream { transactional::transactional(attr, input).unwrap_or_else(|e| e.to_compile_error().into()) } +#[proc_macro_attribute] +pub fn without_transactional(attr: TokenStream, input: TokenStream) -> TokenStream { + transactional::without_transactional(attr, input) + .unwrap_or_else(|e| e.to_compile_error().into()) +} + #[proc_macro_attribute] pub fn require_transactional(attr: TokenStream, input: TokenStream) -> TokenStream { transactional::require_transactional(attr, input) diff --git a/frame/support/procedural/src/transactional.rs b/frame/support/procedural/src/transactional.rs index eb77a320a509f..e16926538b8c4 100644 --- a/frame/support/procedural/src/transactional.rs +++ b/frame/support/procedural/src/transactional.rs @@ -42,6 +42,22 @@ pub fn transactional(_attr: TokenStream, input: TokenStream) -> Result Result { + let ItemFn { attrs, vis, sig, block } = syn::parse(input)?; + + let crate_ = generate_crate_access_2018("frame-support")?; + let output = quote! { + #(#attrs)* + #vis #sig { + use #crate_::storage::set_transactional_mode; + set_transactional_mode(); + #block + } + }; + + Ok(output.into()) +} + pub fn require_transactional(_attr: TokenStream, input: TokenStream) -> Result { let ItemFn { attrs, vis, sig, block } = syn::parse(input)?; diff --git a/frame/support/src/storage/mod.rs b/frame/support/src/storage/mod.rs index d9e50a1e1345e..535ea5e9515de 100644 --- a/frame/support/src/storage/mod.rs +++ b/frame/support/src/storage/mod.rs @@ -31,7 +31,8 @@ use sp_std::{marker::PhantomData, prelude::*}; pub use self::{ transactional::{ - in_storage_layer, with_storage_layer, with_transaction, with_transaction_unchecked, + in_storage_layer, set_transactional_mode, with_storage_layer, with_transaction, + with_transaction_unchecked, }, types::StorageEntryMetadataBuilder, }; diff --git a/frame/support/src/storage/transactional.rs b/frame/support/src/storage/transactional.rs index 909d5909ed8bd..4fbb7c63a27f4 100644 --- a/frame/support/src/storage/transactional.rs +++ b/frame/support/src/storage/transactional.rs @@ -35,6 +35,8 @@ pub type Layer = u32; pub const TRANSACTION_LEVEL_KEY: &[u8] = b":transaction_level:"; /// The maximum number of nested layers. pub const TRANSACTIONAL_LIMIT: Layer = 255; +/// The key that holds if transactional is being used. +pub const TRANSACTIONAL_MODE_KEY: &[u8] = b":transactional_mode:"; /// Returns the current number of nested transactional layers. fn get_transaction_level() -> Layer { @@ -89,7 +91,17 @@ impl Drop for StorageLayerGuard { /// Check if the current call is within a transactional layer. pub fn is_transactional() -> bool { - get_transaction_level() > 0 + get_transaction_level() > 0 && get_transactional_mode() +} + +/// Returns the mode of transactional +pub fn get_transactional_mode() -> bool { + crate::storage::unhashed::get_or::(TRANSACTIONAL_MODE_KEY, true) +} + +/// Set the value of transactional mode +pub fn set_transactional_mode(value: bool) { + crate::storage::unhashed::put::(TRANSACTIONAL_MODE_KEY, &value); } /// Execute the supplied function in a new storage transaction. @@ -298,4 +310,27 @@ mod tests { assert_noop!(res, "epic fail"); }); } + + #[test] + fn transactional_mode_should_be_true_by_default() { + TestExternalities::default().execute_with(|| { + assert_eq!(get_transactional_mode(), true); + }); + } + + #[test] + fn set_transactional_mode_should_work() { + TestExternalities::default().execute_with(|| { + set_transactional_mode(false); + assert_eq!(get_transactional_mode(), false); + }); + } + + #[test] + fn is_transactional_should_be_false_when_mode_false() { + TestExternalities::default().execute_with(|| { + set_transactional_mode(false); + assert_eq!(is_transactional(), false); + }); + } }