Skip to main content

diesel/sqlite/connection/
update_hook.rs

1//! Types for the SQLite data change notification hook.
2//!
3//! See [`SqliteConnection::on_update`](super::SqliteConnection::on_update) for usage.
4
5#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
6extern crate libsqlite3_sys as ffi;
7
8#[cfg(all(target_family = "wasm", target_os = "unknown"))]
9use sqlite_wasm_rs as ffi;
10
11use super::SqliteConnection;
12
13use crate::query_source::NamedTable;
14use alloc::boxed::Box;
15use alloc::vec::Vec;
16
17#[doc =
r" A bitmask of SQLite change operations used for filtering which events"]
#[doc = r" a hook should receive."]
#[doc = r""]
#[doc = r" Combine masks with `|` (bitwise OR):"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" # use diesel::sqlite::SqliteChangeOps;"]
#[doc =
r" let insert_or_delete = SqliteChangeOps::INSERT | SqliteChangeOps::DELETE;"]
#[doc = r" assert!(insert_or_delete.contains(SqliteChangeOps::INSERT));"]
#[doc = r" assert!(!insert_or_delete.contains(SqliteChangeOps::UPDATE));"]
#[doc = r" ```"]
pub struct SqliteChangeOps(<SqliteChangeOps as
    ::bitflags::__private::PublicFlags>::Internal);
#[automatically_derived]
impl ::core::fmt::Debug for SqliteChangeOps {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f,
            "SqliteChangeOps", &&self.0)
    }
}
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for SqliteChangeOps { }
#[automatically_derived]
impl ::core::clone::Clone for SqliteChangeOps {
    #[inline]
    fn clone(&self) -> Self {
        let _:
                ::core::clone::AssertParamIsClone<<SqliteChangeOps as
                ::bitflags::__private::PublicFlags>::Internal>;
        *self
    }
}
#[automatically_derived]
impl ::core::marker::Copy for SqliteChangeOps { }
#[automatically_derived]
impl ::core::marker::StructuralPartialEq for SqliteChangeOps { }
#[automatically_derived]
impl ::core::cmp::PartialEq for SqliteChangeOps {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
#[automatically_derived]
impl ::core::cmp::Eq for SqliteChangeOps {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _:
                ::core::cmp::AssertParamIsEq<<SqliteChangeOps as
                ::bitflags::__private::PublicFlags>::Internal>;
    }
}
#[automatically_derived]
impl ::core::hash::Hash for SqliteChangeOps {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        ::core::hash::Hash::hash(&self.0, state)
    }
}
#[allow(dead_code, deprecated, unused_doc_comments, unused_attributes,
unused_mut, unused_imports, non_upper_case_globals, clippy :: min_ident_chars,
clippy :: assign_op_pattern, clippy :: indexing_slicing, clippy ::
same_name_method, clippy :: iter_without_into_iter,)]
impl SqliteChangeOps {
    #[doc = r" Match INSERT operations."]
    pub const INSERT: Self = Self::from_bits_retain(1);
    #[doc = r" Match UPDATE operations."]
    pub const UPDATE: Self = Self::from_bits_retain(2);
    #[doc = r" Match DELETE operations."]
    pub const DELETE: Self = Self::from_bits_retain(4);
    #[doc = r" Match unknown or future operation codes."]
    pub const UNKNOWN: Self = Self::from_bits_retain(8);
    #[doc =
    r" Match all row-change operations (INSERT, UPDATE, DELETE, and UNKNOWN)."]
    #[doc = r""]
    #[doc =
    r" `UNKNOWN` is included deliberately: if a future SQLite version emits"]
    #[doc =
    r" an operation code diesel does not recognize, a hook registered with"]
    #[doc =
    r" `ALL` still fires (with [`SqliteChangeOp::Unknown`] carrying the raw"]
    #[doc = r" code) rather than silently dropping the change."]
    pub const ALL: Self =
        Self::from_bits_retain(Self::INSERT.bits() | Self::UPDATE.bits() |
                    Self::DELETE.bits() | Self::UNKNOWN.bits());
}
#[allow(dead_code, deprecated, unused_doc_comments, unused_attributes,
unused_mut, unused_imports, non_upper_case_globals, clippy :: min_ident_chars,
clippy :: assign_op_pattern, clippy :: indexing_slicing, clippy ::
same_name_method, clippy :: iter_without_into_iter,)]
impl ::bitflags::Flags for SqliteChangeOps {
    const FLAGS: &'static [::bitflags::Flag<SqliteChangeOps>] =
        {
            mod __bitflags_flag_names {
                #[allow(unused_imports)]
                use super::*;
                pub(super) const INSERT: &'static str = "INSERT";
                pub(super) const UPDATE: &'static str = "UPDATE";
                pub(super) const DELETE: &'static str = "DELETE";
                pub(super) const UNKNOWN: &'static str = "UNKNOWN";
                pub(super) const ALL: &'static str = "ALL";
            }
            &[{
                            ::bitflags::Flag::new(__bitflags_flag_names::INSERT,
                                SqliteChangeOps::INSERT)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::UPDATE,
                                SqliteChangeOps::UPDATE)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::DELETE,
                                SqliteChangeOps::DELETE)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::UNKNOWN,
                                SqliteChangeOps::UNKNOWN)
                        },
                        {
                            ::bitflags::Flag::new(__bitflags_flag_names::ALL,
                                SqliteChangeOps::ALL)
                        }]
        };
    type Bits = u8;
    fn bits(&self) -> u8 { SqliteChangeOps::bits(self) }
    fn from_bits_retain(bits: u8) -> SqliteChangeOps {
        SqliteChangeOps::from_bits_retain(bits)
    }
    fn all_named() -> SqliteChangeOps {
        const ALL_NAMED: u8 =
            {
                let mut truncated = <u8 as ::bitflags::Bits>::EMPTY;
                let mut i = 0;
                {
                    {
                        let flag =
                            &<SqliteChangeOps as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SqliteChangeOps as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SqliteChangeOps as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SqliteChangeOps as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                {
                    {
                        let flag =
                            &<SqliteChangeOps as ::bitflags::Flags>::FLAGS[i];
                        if flag.is_named() {
                            truncated = truncated | flag.value().bits();
                        }
                        i += 1;
                    }
                };
                let _ = i;
                truncated
            };
        SqliteChangeOps::from_bits_retain(ALL_NAMED)
    }
}
#[allow(dead_code, deprecated, unused_doc_comments, unused_attributes,
unused_mut, unused_imports, non_upper_case_globals, clippy :: min_ident_chars,
clippy :: assign_op_pattern, clippy :: indexing_slicing, clippy ::
same_name_method, clippy :: iter_without_into_iter,)]
const _: () =
    {
        #[repr(transparent)]
        pub struct InternalBitFlags(u8);
        #[automatically_derived]
        #[doc(hidden)]
        unsafe impl ::core::clone::TrivialClone for InternalBitFlags { }
        #[automatically_derived]
        impl ::core::clone::Clone for InternalBitFlags {
            #[inline]
            fn clone(&self) -> Self {
                let _: ::core::clone::AssertParamIsClone<u8>;
                *self
            }
        }
        #[automatically_derived]
        impl ::core::marker::Copy for InternalBitFlags { }
        #[automatically_derived]
        impl ::core::marker::StructuralPartialEq for InternalBitFlags { }
        #[automatically_derived]
        impl ::core::cmp::PartialEq for InternalBitFlags {
            #[inline]
            fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
        }
        #[automatically_derived]
        impl ::core::cmp::Eq for InternalBitFlags {
            #[inline]
            #[doc(hidden)]
            #[coverage(off)]
            fn assert_fields_are_eq(&self) {
                let _: ::core::cmp::AssertParamIsEq<u8>;
            }
        }
        #[automatically_derived]
        impl ::core::cmp::PartialOrd for InternalBitFlags {
            #[inline]
            fn partial_cmp(&self, other: &Self)
                -> ::core::option::Option<::core::cmp::Ordering> {
                ::core::option::Option::Some(::core::cmp::Ord::cmp(self,
                        other))
            }
        }
        #[automatically_derived]
        impl ::core::cmp::Ord for InternalBitFlags {
            #[inline]
            fn cmp(&self, other: &Self) -> ::core::cmp::Ordering {
                ::core::cmp::Ord::cmp(&self.0, &other.0)
            }
        }
        #[automatically_derived]
        impl ::core::hash::Hash for InternalBitFlags {
            #[inline]
            fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
                ::core::hash::Hash::hash(&self.0, state)
            }
        }
        impl ::bitflags::__private::PublicFlags for SqliteChangeOps {
            type Primitive = u8;
            type Internal = InternalBitFlags;
        }
        impl ::bitflags::__private::core::default::Default for
            InternalBitFlags {
            #[inline]
            fn default() -> Self { InternalBitFlags::empty() }
        }
        impl ::bitflags::__private::core::fmt::Debug for InternalBitFlags {
            fn fmt(&self,
                f: &mut ::bitflags::__private::core::fmt::Formatter<'_>)
                -> ::bitflags::__private::core::fmt::Result {
                if self.is_empty() {
                    f.write_fmt(format_args!("{0:#x}",
                            <u8 as ::bitflags::Bits>::EMPTY))
                } else {
                    ::bitflags::__private::core::fmt::Display::fmt(self, f)
                }
            }
        }
        impl ::bitflags::__private::core::fmt::Display for InternalBitFlags {
            fn fmt(&self,
                f: &mut ::bitflags::__private::core::fmt::Formatter<'_>)
                -> ::bitflags::__private::core::fmt::Result {
                ::bitflags::parser::to_writer(&SqliteChangeOps(*self), f)
            }
        }
        impl ::bitflags::__private::core::str::FromStr for InternalBitFlags {
            type Err = ::bitflags::parser::ParseError;
            fn from_str(s: &str)
                ->
                    ::bitflags::__private::core::result::Result<Self,
                    Self::Err> {
                ::bitflags::parser::from_str::<SqliteChangeOps>(s).map(|flags|
                        flags.0)
            }
        }
        impl ::bitflags::__private::core::convert::AsRef<u8> for
            InternalBitFlags {
            fn as_ref(&self) -> &u8 { &self.0 }
        }
        impl ::bitflags::__private::core::convert::From<u8> for
            InternalBitFlags {
            fn from(bits: u8) -> Self { Self::from_bits_retain(bits) }
        }
        impl InternalBitFlags {
            /// Get a flags value with all bits unset.
            #[inline]
            pub const fn empty() -> Self {
                Self(<u8 as ::bitflags::Bits>::EMPTY)
            }
            /// Get a flags value with all known bits set.
            #[inline]
            pub const fn all() -> Self {
                const ALL: InternalBitFlags =
                    {
                        let mut truncated = <u8 as ::bitflags::Bits>::EMPTY;
                        let mut _i = 0;
                        {
                            {
                                truncated |=
                                    <SqliteChangeOps as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SqliteChangeOps as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SqliteChangeOps as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SqliteChangeOps as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        {
                            {
                                truncated |=
                                    <SqliteChangeOps as
                                                    ::bitflags::Flags>::FLAGS[_i].value().bits();
                                _i += 1;
                            }
                        };
                        InternalBitFlags(truncated)
                    };
                ALL
            }
            /// Get the underlying bits value.
            ///
            /// The returned value is exactly the bits set in this flags value.
            #[inline]
            pub const fn bits(&self) -> u8 { self.0 }
            /// Convert from a bits value.
            ///
            /// This method will return `None` if any unknown bits are set.
            #[inline]
            pub const fn from_bits(bits: u8)
                -> ::bitflags::__private::core::option::Option<Self> {
                let truncated = Self::from_bits_truncate(bits).0;
                if truncated == bits {
                    ::bitflags::__private::core::option::Option::Some(Self(bits))
                } else { ::bitflags::__private::core::option::Option::None }
            }
            /// Convert from a bits value, unsetting any unknown bits.
            #[inline]
            pub const fn from_bits_truncate(bits: u8) -> Self {
                Self(bits & Self::all().0)
            }
            /// Convert from a bits value exactly.
            #[inline]
            pub const fn from_bits_retain(bits: u8) -> Self { Self(bits) }
            /// Get a flags value with the bits of a flag with the given name set.
            ///
            /// This method will return `None` if `name` is empty or doesn't
            /// correspond to any named flag.
            #[inline]
            pub fn from_name(name: &str)
                -> ::bitflags::__private::core::option::Option<Self> {
                mod __bitflags_flag_names {
                    #[allow(unused_imports)]
                    use super::*;
                    pub(super) const INSERT: &'static str = "INSERT";
                    pub(super) const UPDATE: &'static str = "UPDATE";
                    pub(super) const DELETE: &'static str = "DELETE";
                    pub(super) const UNKNOWN: &'static str = "UNKNOWN";
                    pub(super) const ALL: &'static str = "ALL";
                }
                {
                    {
                        if name == __bitflags_flag_names::INSERT {
                            return ::bitflags::__private::core::option::Option::Some(Self(SqliteChangeOps::INSERT.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::UPDATE {
                            return ::bitflags::__private::core::option::Option::Some(Self(SqliteChangeOps::UPDATE.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::DELETE {
                            return ::bitflags::__private::core::option::Option::Some(Self(SqliteChangeOps::DELETE.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::UNKNOWN {
                            return ::bitflags::__private::core::option::Option::Some(Self(SqliteChangeOps::UNKNOWN.bits()));
                        }
                    };
                };
                {
                    {
                        if name == __bitflags_flag_names::ALL {
                            return ::bitflags::__private::core::option::Option::Some(Self(SqliteChangeOps::ALL.bits()));
                        }
                    };
                };
                let _ = name;
                ::bitflags::__private::core::option::Option::None
            }
            /// Whether all bits in `self` are unset.
            #[inline]
            pub const fn is_empty(&self) -> bool {
                self.0 == <u8 as ::bitflags::Bits>::EMPTY
            }
            /// Whether all known bits in this flags value are set.
            #[inline]
            pub const fn is_all(&self) -> bool {
                Self::all().0 | self.0 == self.0
            }
            /// Whether any set bits in `other` are also set in `self`.
            #[inline]
            pub const fn intersects(&self, other: Self) -> bool {
                self.0 & other.0 != <u8 as ::bitflags::Bits>::EMPTY
            }
            /// Whether all set bits in `other` are also set in `self`.
            #[inline]
            pub const fn contains(&self, other: Self) -> bool {
                self.0 & other.0 == other.0
            }
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            pub fn insert(&mut self, other: Self) {
                *self = Self(self.0).union(other);
            }
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `remove` won't truncate `other`, but the `!` operator will.
            #[inline]
            pub fn remove(&mut self, other: Self) {
                *self = Self(self.0).difference(other);
            }
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            pub fn toggle(&mut self, other: Self) {
                *self = Self(self.0).symmetric_difference(other);
            }
            /// Call `insert` when `value` is `true` or `remove` when `value` is `false`.
            #[inline]
            pub fn set(&mut self, other: Self, value: bool) {
                if value { self.insert(other); } else { self.remove(other); }
            }
            /// The bitwise and (`&`) of the bits in `self` and `other`.
            #[inline]
            #[must_use]
            pub const fn intersection(self, other: Self) -> Self {
                Self(self.0 & other.0)
            }
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            #[must_use]
            pub const fn union(self, other: Self) -> Self {
                Self(self.0 | other.0)
            }
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `difference` won't truncate `other`, but the `!` operator will.
            #[inline]
            #[must_use]
            pub const fn difference(self, other: Self) -> Self {
                Self(self.0 & !other.0)
            }
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            #[must_use]
            pub const fn symmetric_difference(self, other: Self) -> Self {
                Self(self.0 ^ other.0)
            }
            /// The bitwise negation (`!`) of the bits in `self`, truncating the result.
            #[inline]
            #[must_use]
            pub const fn complement(self) -> Self {
                Self::from_bits_truncate(!self.0)
            }
        }
        impl ::bitflags::__private::core::fmt::Binary for InternalBitFlags {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::Binary::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::fmt::Octal for InternalBitFlags {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::Octal::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::fmt::LowerHex for InternalBitFlags {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::LowerHex::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::fmt::UpperHex for InternalBitFlags {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::UpperHex::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::ops::BitOr for InternalBitFlags {
            type Output = Self;
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            fn bitor(self, other: InternalBitFlags) -> Self {
                self.union(other)
            }
        }
        impl ::bitflags::__private::core::ops::BitOrAssign for
            InternalBitFlags {
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            fn bitor_assign(&mut self, other: Self) { self.insert(other); }
        }
        impl ::bitflags::__private::core::ops::BitXor for InternalBitFlags {
            type Output = Self;
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            fn bitxor(self, other: Self) -> Self {
                self.symmetric_difference(other)
            }
        }
        impl ::bitflags::__private::core::ops::BitXorAssign for
            InternalBitFlags {
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            fn bitxor_assign(&mut self, other: Self) { self.toggle(other); }
        }
        impl ::bitflags::__private::core::ops::BitAnd for InternalBitFlags {
            type Output = Self;
            /// The bitwise and (`&`) of the bits in `self` and `other`.
            #[inline]
            fn bitand(self, other: Self) -> Self { self.intersection(other) }
        }
        impl ::bitflags::__private::core::ops::BitAndAssign for
            InternalBitFlags {
            /// The bitwise and (`&`) of the bits in `self` and `other`.
            #[inline]
            fn bitand_assign(&mut self, other: Self) {
                *self =
                    Self::from_bits_retain(self.bits()).intersection(other);
            }
        }
        impl ::bitflags::__private::core::ops::Sub for InternalBitFlags {
            type Output = Self;
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `difference` won't truncate `other`, but the `!` operator will.
            #[inline]
            fn sub(self, other: Self) -> Self { self.difference(other) }
        }
        impl ::bitflags::__private::core::ops::SubAssign for InternalBitFlags
            {
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `difference` won't truncate `other`, but the `!` operator will.
            #[inline]
            fn sub_assign(&mut self, other: Self) { self.remove(other); }
        }
        impl ::bitflags::__private::core::ops::Not for InternalBitFlags {
            type Output = Self;
            /// The bitwise negation (`!`) of the bits in `self`, truncating the result.
            #[inline]
            fn not(self) -> Self { self.complement() }
        }
        impl ::bitflags::__private::core::iter::Extend<InternalBitFlags> for
            InternalBitFlags {
            /// The bitwise or (`|`) of the bits in each flags value.
            fn extend<T: ::bitflags::__private::core::iter::IntoIterator<Item
                = Self>>(&mut self, iterator: T) {
                for item in iterator { self.insert(item) }
            }
        }
        impl ::bitflags::__private::core::iter::FromIterator<InternalBitFlags>
            for InternalBitFlags {
            /// The bitwise or (`|`) of the bits in each flags value.
            fn from_iter<T: ::bitflags::__private::core::iter::IntoIterator<Item
                = Self>>(iterator: T) -> Self {
                use ::bitflags::__private::core::iter::Extend;
                let mut result = Self::empty();
                result.extend(iterator);
                result
            }
        }
        impl InternalBitFlags {
            /// Yield a set of contained flags values.
            ///
            /// Each yielded flags value will correspond to a defined named flag. Any unknown bits
            /// will be yielded together as a final flags value.
            #[inline]
            pub const fn iter(&self)
                -> ::bitflags::iter::Iter<SqliteChangeOps> {
                ::bitflags::iter::Iter::__private_const_new(<SqliteChangeOps
                        as ::bitflags::Flags>::FLAGS,
                    SqliteChangeOps::from_bits_retain(self.bits()),
                    SqliteChangeOps::from_bits_retain(self.bits()))
            }
            /// Yield a set of contained named flags values.
            ///
            /// This method is like [`iter`](#method.iter), except only yields bits in contained named flags.
            /// Any unknown bits, or bits not corresponding to a contained flag will not be yielded.
            #[inline]
            pub const fn iter_names(&self)
                -> ::bitflags::iter::IterNames<SqliteChangeOps> {
                ::bitflags::iter::IterNames::__private_const_new(<SqliteChangeOps
                        as ::bitflags::Flags>::FLAGS,
                    SqliteChangeOps::from_bits_retain(self.bits()),
                    SqliteChangeOps::from_bits_retain(self.bits()))
            }
        }
        impl ::bitflags::__private::core::iter::IntoIterator for
            InternalBitFlags {
            type Item = SqliteChangeOps;
            type IntoIter = ::bitflags::iter::Iter<SqliteChangeOps>;
            fn into_iter(self) -> Self::IntoIter { self.iter() }
        }
        impl InternalBitFlags {
            /// Returns a mutable reference to the raw value of the flags currently stored.
            #[inline]
            pub fn bits_mut(&mut self) -> &mut u8 { &mut self.0 }
        }
        impl SqliteChangeOps {
            /// Get a flags value with all bits unset.
            #[inline]
            pub const fn empty() -> Self { Self(InternalBitFlags::empty()) }
            /// Get a flags value with all known bits set.
            #[inline]
            pub const fn all() -> Self { Self(InternalBitFlags::all()) }
            /// Get the underlying bits value.
            ///
            /// The returned value is exactly the bits set in this flags value.
            #[inline]
            pub const fn bits(&self) -> u8 { self.0.bits() }
            /// Convert from a bits value.
            ///
            /// This method will return `None` if any unknown bits are set.
            #[inline]
            pub const fn from_bits(bits: u8)
                -> ::bitflags::__private::core::option::Option<Self> {
                match InternalBitFlags::from_bits(bits) {
                    ::bitflags::__private::core::option::Option::Some(bits) =>
                        ::bitflags::__private::core::option::Option::Some(Self(bits)),
                    ::bitflags::__private::core::option::Option::None =>
                        ::bitflags::__private::core::option::Option::None,
                }
            }
            /// Convert from a bits value, unsetting any unknown bits.
            #[inline]
            pub const fn from_bits_truncate(bits: u8) -> Self {
                Self(InternalBitFlags::from_bits_truncate(bits))
            }
            /// Convert from a bits value exactly.
            #[inline]
            pub const fn from_bits_retain(bits: u8) -> Self {
                Self(InternalBitFlags::from_bits_retain(bits))
            }
            /// Get a flags value with the bits of a flag with the given name set.
            ///
            /// This method will return `None` if `name` is empty or doesn't
            /// correspond to any named flag.
            #[inline]
            pub fn from_name(name: &str)
                -> ::bitflags::__private::core::option::Option<Self> {
                match InternalBitFlags::from_name(name) {
                    ::bitflags::__private::core::option::Option::Some(bits) =>
                        ::bitflags::__private::core::option::Option::Some(Self(bits)),
                    ::bitflags::__private::core::option::Option::None =>
                        ::bitflags::__private::core::option::Option::None,
                }
            }
            /// Whether all bits in `self` are unset.
            #[inline]
            pub const fn is_empty(&self) -> bool { self.0.is_empty() }
            /// Whether all known bits in this flags value are set.
            #[inline]
            pub const fn is_all(&self) -> bool { self.0.is_all() }
            /// Whether any set bits in `other` are also set in `self`.
            #[inline]
            pub const fn intersects(&self, other: Self) -> bool {
                self.0.intersects(other.0)
            }
            /// Whether all set bits in `other` are also set in `self`.
            #[inline]
            pub const fn contains(&self, other: Self) -> bool {
                self.0.contains(other.0)
            }
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            pub fn insert(&mut self, other: Self) { self.0.insert(other.0) }
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `remove` won't truncate `other`, but the `!` operator will.
            #[inline]
            pub fn remove(&mut self, other: Self) { self.0.remove(other.0) }
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            pub fn toggle(&mut self, other: Self) { self.0.toggle(other.0) }
            /// Call `insert` when `value` is `true` or `remove` when `value` is `false`.
            #[inline]
            pub fn set(&mut self, other: Self, value: bool) {
                self.0.set(other.0, value)
            }
            /// The bitwise and (`&`) of the bits in `self` and `other`.
            #[inline]
            #[must_use]
            pub const fn intersection(self, other: Self) -> Self {
                Self(self.0.intersection(other.0))
            }
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            #[must_use]
            pub const fn union(self, other: Self) -> Self {
                Self(self.0.union(other.0))
            }
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `difference` won't truncate `other`, but the `!` operator will.
            #[inline]
            #[must_use]
            pub const fn difference(self, other: Self) -> Self {
                Self(self.0.difference(other.0))
            }
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            #[must_use]
            pub const fn symmetric_difference(self, other: Self) -> Self {
                Self(self.0.symmetric_difference(other.0))
            }
            /// The bitwise negation (`!`) of the bits in `self`, truncating the result.
            #[inline]
            #[must_use]
            pub const fn complement(self) -> Self {
                Self(self.0.complement())
            }
        }
        impl ::bitflags::__private::core::fmt::Binary for SqliteChangeOps {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::Binary::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::fmt::Octal for SqliteChangeOps {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::Octal::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::fmt::LowerHex for SqliteChangeOps {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::LowerHex::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::fmt::UpperHex for SqliteChangeOps {
            fn fmt(&self, f: &mut ::bitflags::__private::core::fmt::Formatter)
                -> ::bitflags::__private::core::fmt::Result {
                let inner = self.0;
                ::bitflags::__private::core::fmt::UpperHex::fmt(&inner, f)
            }
        }
        impl ::bitflags::__private::core::ops::BitOr for SqliteChangeOps {
            type Output = Self;
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            fn bitor(self, other: SqliteChangeOps) -> Self {
                self.union(other)
            }
        }
        impl ::bitflags::__private::core::ops::BitOrAssign for SqliteChangeOps
            {
            /// The bitwise or (`|`) of the bits in `self` and `other`.
            #[inline]
            fn bitor_assign(&mut self, other: Self) { self.insert(other); }
        }
        impl ::bitflags::__private::core::ops::BitXor for SqliteChangeOps {
            type Output = Self;
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            fn bitxor(self, other: Self) -> Self {
                self.symmetric_difference(other)
            }
        }
        impl ::bitflags::__private::core::ops::BitXorAssign for
            SqliteChangeOps {
            /// The bitwise exclusive-or (`^`) of the bits in `self` and `other`.
            #[inline]
            fn bitxor_assign(&mut self, other: Self) { self.toggle(other); }
        }
        impl ::bitflags::__private::core::ops::BitAnd for SqliteChangeOps {
            type Output = Self;
            /// The bitwise and (`&`) of the bits in `self` and `other`.
            #[inline]
            fn bitand(self, other: Self) -> Self { self.intersection(other) }
        }
        impl ::bitflags::__private::core::ops::BitAndAssign for
            SqliteChangeOps {
            /// The bitwise and (`&`) of the bits in `self` and `other`.
            #[inline]
            fn bitand_assign(&mut self, other: Self) {
                *self =
                    Self::from_bits_retain(self.bits()).intersection(other);
            }
        }
        impl ::bitflags::__private::core::ops::Sub for SqliteChangeOps {
            type Output = Self;
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `difference` won't truncate `other`, but the `!` operator will.
            #[inline]
            fn sub(self, other: Self) -> Self { self.difference(other) }
        }
        impl ::bitflags::__private::core::ops::SubAssign for SqliteChangeOps {
            /// The intersection of `self` with the complement of `other` (`&!`).
            ///
            /// This method is not equivalent to `self & !other` when `other` has unknown bits set.
            /// `difference` won't truncate `other`, but the `!` operator will.
            #[inline]
            fn sub_assign(&mut self, other: Self) { self.remove(other); }
        }
        impl ::bitflags::__private::core::ops::Not for SqliteChangeOps {
            type Output = Self;
            /// The bitwise negation (`!`) of the bits in `self`, truncating the result.
            #[inline]
            fn not(self) -> Self { self.complement() }
        }
        impl ::bitflags::__private::core::iter::Extend<SqliteChangeOps> for
            SqliteChangeOps {
            /// The bitwise or (`|`) of the bits in each flags value.
            fn extend<T: ::bitflags::__private::core::iter::IntoIterator<Item
                = Self>>(&mut self, iterator: T) {
                for item in iterator { self.insert(item) }
            }
        }
        impl ::bitflags::__private::core::iter::FromIterator<SqliteChangeOps>
            for SqliteChangeOps {
            /// The bitwise or (`|`) of the bits in each flags value.
            fn from_iter<T: ::bitflags::__private::core::iter::IntoIterator<Item
                = Self>>(iterator: T) -> Self {
                use ::bitflags::__private::core::iter::Extend;
                let mut result = Self::empty();
                result.extend(iterator);
                result
            }
        }
        impl SqliteChangeOps {
            /// Yield a set of contained flags values.
            ///
            /// Each yielded flags value will correspond to a defined named flag. Any unknown bits
            /// will be yielded together as a final flags value.
            #[inline]
            pub const fn iter(&self)
                -> ::bitflags::iter::Iter<SqliteChangeOps> {
                ::bitflags::iter::Iter::__private_const_new(<SqliteChangeOps
                        as ::bitflags::Flags>::FLAGS,
                    SqliteChangeOps::from_bits_retain(self.bits()),
                    SqliteChangeOps::from_bits_retain(self.bits()))
            }
            /// Yield a set of contained named flags values.
            ///
            /// This method is like [`iter`](#method.iter), except only yields bits in contained named flags.
            /// Any unknown bits, or bits not corresponding to a contained flag will not be yielded.
            #[inline]
            pub const fn iter_names(&self)
                -> ::bitflags::iter::IterNames<SqliteChangeOps> {
                ::bitflags::iter::IterNames::__private_const_new(<SqliteChangeOps
                        as ::bitflags::Flags>::FLAGS,
                    SqliteChangeOps::from_bits_retain(self.bits()),
                    SqliteChangeOps::from_bits_retain(self.bits()))
            }
        }
        impl ::bitflags::__private::core::iter::IntoIterator for
            SqliteChangeOps {
            type Item = SqliteChangeOps;
            type IntoIter = ::bitflags::iter::Iter<SqliteChangeOps>;
            fn into_iter(self) -> Self::IntoIter { self.iter() }
        }
    };bitflags::bitflags! {
18    /// A bitmask of SQLite change operations used for filtering which events
19    /// a hook should receive.
20    ///
21    /// Combine masks with `|` (bitwise OR):
22    ///
23    /// ```rust
24    /// # use diesel::sqlite::SqliteChangeOps;
25    /// let insert_or_delete = SqliteChangeOps::INSERT | SqliteChangeOps::DELETE;
26    /// assert!(insert_or_delete.contains(SqliteChangeOps::INSERT));
27    /// assert!(!insert_or_delete.contains(SqliteChangeOps::UPDATE));
28    /// ```
29    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30    pub struct SqliteChangeOps: u8 {
31        /// Match INSERT operations.
32        const INSERT = 1;
33        /// Match UPDATE operations.
34        const UPDATE = 2;
35        /// Match DELETE operations.
36        const DELETE = 4;
37        /// Match unknown or future operation codes.
38        const UNKNOWN = 8;
39        /// Match all row-change operations (INSERT, UPDATE, DELETE, and UNKNOWN).
40        ///
41        /// `UNKNOWN` is included deliberately: if a future SQLite version emits
42        /// an operation code diesel does not recognize, a hook registered with
43        /// `ALL` still fires (with [`SqliteChangeOp::Unknown`] carrying the raw
44        /// code) rather than silently dropping the change.
45        const ALL =
46            Self::INSERT.bits() | Self::UPDATE.bits() | Self::DELETE.bits() | Self::UNKNOWN.bits();
47    }
48}
49
50impl SqliteChangeOps {
51    /// Checks whether this mask includes the given [`SqliteChangeOp`].
52    pub(crate) fn matches_op(self, op: SqliteChangeOp) -> bool {
53        self.contains(op.to_ops())
54    }
55}
56
57/// Identifies which kind of row change occurred.
58///
59/// Returned as part of [`SqliteChangeEvent`] to the callback registered via
60/// [`on_update`](super::SqliteConnection::on_update).
61#[derive(#[automatically_derived]
impl ::core::fmt::Debug for SqliteChangeOp {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::Insert => ::core::fmt::Formatter::write_str(f, "Insert"),
            Self::Update => ::core::fmt::Formatter::write_str(f, "Update"),
            Self::Delete => ::core::fmt::Formatter::write_str(f, "Delete"),
            Self::Unknown(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Unknown", &__self_0),
        }
    }
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for SqliteChangeOp { }
#[automatically_derived]
impl ::core::clone::Clone for SqliteChangeOp {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<i32>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for SqliteChangeOp { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for SqliteChangeOp { }
#[automatically_derived]
impl ::core::cmp::PartialEq for SqliteChangeOp {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::Unknown(__self_0), Self::Unknown(__arg1_0)) =>
                    __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for SqliteChangeOp {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<i32>;
    }
}Eq)]
62#[non_exhaustive]
63pub enum SqliteChangeOp {
64    /// A row was inserted.
65    Insert,
66    /// A row was updated.
67    Update,
68    /// A row was deleted.
69    Delete,
70    /// An operation code this version of diesel does not recognize, for example
71    /// one added by a future SQLite version. The inner value is the raw FFI code.
72    Unknown(i32),
73}
74
75impl SqliteChangeOp {
76    /// Converts a raw FFI operation code to the corresponding enum variant.
77    pub(crate) fn from_ffi(code: i32) -> Self {
78        #[allow(non_upper_case_globals)]
79        match code {
80            ffi::SQLITE_INSERT => SqliteChangeOp::Insert,
81            ffi::SQLITE_UPDATE => SqliteChangeOp::Update,
82            ffi::SQLITE_DELETE => SqliteChangeOp::Delete,
83            other => SqliteChangeOp::Unknown(other),
84        }
85    }
86
87    /// Converts this single operation to the corresponding [`SqliteChangeOps`]
88    /// bitmask.
89    pub(crate) fn to_ops(self) -> SqliteChangeOps {
90        match self {
91            SqliteChangeOp::Insert => SqliteChangeOps::INSERT,
92            SqliteChangeOp::Update => SqliteChangeOps::UPDATE,
93            SqliteChangeOp::Delete => SqliteChangeOps::DELETE,
94            SqliteChangeOp::Unknown(_) => SqliteChangeOps::UNKNOWN,
95        }
96    }
97}
98
99/// Describes a single row change event from SQLite.
100///
101/// See: <https://www.sqlite.org/c3ref/update_hook.html>
102#[derive(#[automatically_derived]
impl<'a> ::core::fmt::Debug for SqliteChangeEvent<'a> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field4_finish(f,
            "SqliteChangeEvent", "op", &self.op, "db_name", &self.db_name,
            "table_name", &self.table_name, "rowid", &&self.rowid)
    }
}Debug, #[automatically_derived]
#[doc(hidden)]
unsafe impl<'a> ::core::clone::TrivialClone for SqliteChangeEvent<'a> { }
#[automatically_derived]
impl<'a> ::core::clone::Clone for SqliteChangeEvent<'a> {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<SqliteChangeOp>;
        let _: ::core::clone::AssertParamIsClone<&'a str>;
        let _: ::core::clone::AssertParamIsClone<&'a str>;
        let _: ::core::clone::AssertParamIsClone<i64>;
        *self
    }
}Clone, #[automatically_derived]
impl<'a> ::core::marker::Copy for SqliteChangeEvent<'a> { }Copy)]
103#[non_exhaustive]
104pub struct SqliteChangeEvent<'a> {
105    /// The operation that triggered this event.
106    pub op: SqliteChangeOp,
107    /// The name of the database the change occurred in: `"main"` for the
108    /// primary database, `"temp"` for temporary tables, or the alias from an
109    /// `ATTACH DATABASE` statement.
110    pub db_name: &'a str,
111    /// The name of the table that was modified.
112    pub table_name: &'a str,
113    /// SQLite's internal 64-bit [rowid](https://www.sqlite.org/rowidtable.html)
114    /// of the affected row. For an `INTEGER PRIMARY KEY` table this equals the
115    /// primary key, otherwise it is a separate hidden value.
116    pub rowid: i64,
117}
118
119impl SqliteChangeEvent<'_> {
120    /// Returns `true` if this change is on the [`table!`](macro@crate::table)
121    /// table `T`, comparing [`table_name`](Self::table_name) against `T`'s
122    /// name. Lets a callback match a typed table marker instead of a string
123    /// literal:
124    ///
125    /// ```rust
126    /// # diesel::table! { users (id) { id -> Integer, name -> Text, } }
127    /// # fn f(change: &diesel::sqlite::SqliteChangeEvent<'_>) {
128    /// if change.is_from(users::table) { /* ... */ }
129    /// # }
130    /// ```
131    ///
132    /// Only the table name is compared, not the database, so a same-named
133    /// table in an `ATTACH`-ed database also matches. Inspect
134    /// [`db_name`](Self::db_name) if you need to tell them apart.
135    pub fn is_from(&self, table: impl NamedTable) -> bool {
136        self.table_name == table.table() && table.schema().is_none_or(|db| self.db_name == db)
137    }
138
139    /// Returns `Some(rowid)` if this change is on table `T`, otherwise `None`.
140    /// Shorthand for [`is_from`](Self::is_from) followed by reading
141    /// [`rowid`](Self::rowid):
142    ///
143    /// ```rust
144    /// # diesel::table! { users (id) { id -> Integer, name -> Text, } }
145    /// # fn f(change: &diesel::sqlite::SqliteChangeEvent<'_>) {
146    /// if let Some(rowid) = change.rowid_in(users::table) { let _ = rowid; }
147    /// # }
148    /// ```
149    pub fn rowid_in(&self, table: impl NamedTable) -> Option<i64> {
150        if self.is_from(table) {
151            Some(self.rowid)
152        } else {
153            None
154        }
155    }
156}
157
158// A helper trait to use `NamedTable` with trait objects
159// without requiring all the super type restrictions
160trait DynNamedTable {
161    fn schema(&self) -> Option<&str>;
162    fn table(&self) -> &str;
163}
164
165impl<T> DynNamedTable for T
166where
167    T: NamedTable,
168{
169    fn schema(&self) -> Option<&str> {
170        NamedTable::schema(self)
171    }
172
173    fn table(&self) -> &str {
174        NamedTable::table(self)
175    }
176}
177
178struct Route {
179    table: Option<Box<dyn DynNamedTable + Send>>,
180    ops: SqliteChangeOps,
181    callback: Box<dyn FnMut(SqliteChangeEvent<'_>) + Send>,
182}
183
184impl Route {
185    fn matches(&self, event: &SqliteChangeEvent<'_>) -> bool {
186        self.ops.matches_op(event.op)
187            && self.table.as_deref().is_none_or(|name| {
188                name.table() == event.table_name
189                    && name.schema().is_none_or(|db| db == event.db_name)
190            })
191    }
192}
193
194/// Routes SQLite row-change events to per-table callbacks, selected by typed
195/// [`table!`](macro@crate::table) markers.
196///
197/// SQLite allows only one update hook per connection. This router is a single
198/// value the caller composes and installs into that one slot with
199/// [`on_update`](super::SqliteConnection::on_update), so the dispatch table is
200/// explicit rather than hidden connection state. Build it with [`on`](Self::on)
201/// and [`on_any`](Self::on_any), then install it:
202///
203/// ```rust
204/// use diesel::prelude::*;
205/// use diesel::sqlite::{SqliteConnection, SqliteChangeOps, SqliteUpdateRouter};
206/// use std::sync::{Arc, Mutex};
207///
208/// diesel::table! { users (id) { id -> Integer, name -> Text, } }
209///
210/// # let conn = &mut SqliteConnection::establish(":memory:").unwrap();
211/// # use diesel::connection::SimpleConnection;
212/// # conn.batch_execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)").unwrap();
213/// let inserted = Arc::new(Mutex::new(Vec::new()));
214/// let captured = inserted.clone();
215///
216/// conn.on_update(
217///     SqliteUpdateRouter::new()
218///         .on(users::table, SqliteChangeOps::INSERT, move |change| {
219///             captured.lock().unwrap().push(change.rowid);
220///         }),
221/// );
222///
223/// diesel::insert_into(users::table)
224///     .values(users::name.eq("Alice"))
225///     .execute(conn)
226///     .unwrap();
227///
228/// assert_eq!(*inserted.lock().unwrap(), vec![1]);
229/// ```
230///
231/// Every matching route fires for a given event, so overlapping routes (for
232/// example an [`on_any`](Self::on_any) audit log alongside table-specific
233/// handlers) all run. There are no per-route handles: to change the routes,
234/// install a different router or call
235/// [`remove_update_hook`](super::SqliteConnection::remove_update_hook).
236#[allow(missing_debug_implementations)]
237pub struct SqliteUpdateRouter {
238    routes: Vec<Route>,
239}
240
241impl SqliteUpdateRouter {
242    /// Creates an empty router that matches nothing until routes are added.
243    pub fn new() -> Self {
244        SqliteUpdateRouter { routes: Vec::new() }
245    }
246
247    /// Routes changes on `table` matching `ops` to `callback`.
248    ///
249    /// `table` is a table type, either generated by [`table!`](macro@crate::table)
250    /// or a diesel-dynamic-schema table. The schema name is treated as database name
251    /// An unqualified table matches by table name in
252    /// any database, so a same-named table in an `ATTACH`-ed database also
253    /// matches. A schema-qualified `table!` type additionally matches the
254    /// database name, so it fires only for that attached database.
255    pub fn on<T, F>(mut self, table: T, ops: SqliteChangeOps, callback: F) -> Self
256    where
257        T: NamedTable + Send + 'static,
258        F: FnMut(SqliteChangeEvent<'_>) + Send + 'static,
259    {
260        // The table value is taken only for ergonomic call syntax. Its name and
261        // optional schema come from the type's static component.
262        let _ = table;
263        self.routes.push(Route {
264            table: Some(Box::new(table)),
265            ops,
266            callback: Box::new(callback),
267        });
268        self
269    }
270
271    /// Routes changes on any table matching `ops` to `callback`.
272    pub fn on_any<F>(mut self, ops: SqliteChangeOps, callback: F) -> Self
273    where
274        F: FnMut(SqliteChangeEvent<'_>) + Send + 'static,
275    {
276        self.routes.push(Route {
277            table: None,
278            ops,
279            callback: Box::new(callback),
280        });
281        self
282    }
283
284    /// Dispatches an event to every matching route, in build order.
285    fn dispatch(&mut self, event: SqliteChangeEvent<'_>) {
286        for route in &mut self.routes {
287            if route.matches(&event) {
288                (route.callback)(event);
289            }
290        }
291    }
292
293    /// Turns the router into the callback installed by
294    /// [`on_update`](super::SqliteConnection::on_update).
295    pub(crate) fn into_hook(mut self) -> impl FnMut(SqliteChangeEvent<'_>) + Send {
296        move |event| self.dispatch(event)
297    }
298}
299
300impl Default for SqliteUpdateRouter {
301    fn default() -> Self {
302        SqliteUpdateRouter::new()
303    }
304}
305
306impl SqliteConnection {
307    /// Installs a [`SqliteUpdateRouter`](crate::sqlite::SqliteUpdateRouter) as
308    /// the update hook, invoked for every row change (insert, update, or delete)
309    /// on a [rowid table](https://www.sqlite.org/rowidtable.html). Replaces any
310    /// previously registered update hook, since SQLite allows only one per
311    /// connection.
312    ///
313    /// Build the router with [`SqliteUpdateRouter::on`] for typed per-table
314    /// routes and [`SqliteUpdateRouter::on_any`] for a table-agnostic route. A
315    /// single catch-all is `on_any(SqliteChangeOps::ALL, ...)`. Inside a
316    /// callback, [`is_from`](crate::sqlite::SqliteChangeEvent::is_from) and
317    /// [`rowid_in`](crate::sqlite::SqliteChangeEvent::rowid_in) match a `table!`
318    /// marker without a string.
319    ///
320    /// Callbacks run synchronously as part of the `sqlite3_step()` call that
321    /// performs the change, on the thread performing it, so they are never
322    /// invoked concurrently. Per SQLite, a callback must not use the connection
323    /// that triggered it (running any SQL, including a `SELECT`, counts as use)
324    /// and is not reentrant. A panic in a callback aborts the process. To act on
325    /// the changed row, capture its `rowid` and run the query after the
326    /// statement completes.
327    ///
328    /// # Limitations
329    ///
330    /// These come from the underlying
331    /// [`sqlite3_update_hook`](https://www.sqlite.org/c3ref/update_hook.html):
332    ///
333    /// - Only fires for [rowid tables](https://www.sqlite.org/rowidtable.html),
334    ///   not `WITHOUT ROWID` tables.
335    /// - Does not fire for changes to internal system tables, for `ON CONFLICT
336    ///   REPLACE` deletions, or for the truncate optimization (`DELETE` with no
337    ///   `WHERE` on a trigger-free table).
338    ///
339    /// See: [`sqlite3_update_hook`](https://www.sqlite.org/c3ref/update_hook.html)
340    ///
341    /// # Example
342    ///
343    /// ```rust
344    /// use diesel::prelude::*;
345    /// use diesel::sqlite::{SqliteChangeOps, SqliteConnection, SqliteUpdateRouter};
346    /// use std::sync::{Arc, Mutex};
347    ///
348    /// diesel::table! { users (id) { id -> Integer, name -> Text, } }
349    ///
350    /// # use diesel::connection::SimpleConnection;
351    /// # let conn = &mut SqliteConnection::establish(":memory:").unwrap();
352    /// # conn.batch_execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)").unwrap();
353    /// let changes = Arc::new(Mutex::new(Vec::new()));
354    /// let captured = changes.clone();
355    ///
356    /// conn.on_update(
357    ///     SqliteUpdateRouter::new().on(
358    ///         users::table,
359    ///         SqliteChangeOps::INSERT,
360    ///         move |change| captured.lock().unwrap().push(change.rowid),
361    ///     ),
362    /// );
363    ///
364    /// diesel::insert_into(users::table)
365    ///     .values(users::name.eq("Alice"))
366    ///     .execute(conn)
367    ///     .unwrap();
368    ///
369    /// assert_eq!(*changes.lock().unwrap(), vec![1]);
370    /// ```
371    pub fn on_update(&mut self, router: SqliteUpdateRouter) {
372        self.raw_connection.set_update_hook(router.into_hook());
373    }
374
375    /// Removes the update hook. Subsequent row changes will not invoke any
376    /// callback.
377    ///
378    /// See [`on_update`](Self::on_update) for usage.
379    pub fn remove_update_hook(&mut self) {
380        self.raw_connection.remove_update_hook();
381    }
382}
383
384// miri doesn't support ffi callbacks
385#[cfg(all(test, not(miri)))]
386mod tests {
387    use super::*;
388    use crate::connection::Connection;
389    use crate::prelude::*;
390    use crate::query_dsl::RunQueryDsl;
391
392    /// Test-only helper: check mask vs raw FFI op code.
393    impl SqliteChangeOps {
394        fn matches(self, op_code: i32) -> bool {
395            self.matches_op(SqliteChangeOp::from_ffi(op_code))
396        }
397    }
398
399    #[test]
400    fn insert_or_delete_matches_both_but_not_update() {
401        let mask = SqliteChangeOps::INSERT | SqliteChangeOps::DELETE;
402        assert!(mask.matches(ffi::SQLITE_INSERT));
403        assert!(mask.matches(ffi::SQLITE_DELETE));
404        assert!(!mask.matches(ffi::SQLITE_UPDATE));
405    }
406
407    #[test]
408    fn all_matches_all_three() {
409        assert!(SqliteChangeOps::ALL.matches(ffi::SQLITE_INSERT));
410        assert!(SqliteChangeOps::ALL.matches(ffi::SQLITE_UPDATE));
411        assert!(SqliteChangeOps::ALL.matches(ffi::SQLITE_DELETE));
412    }
413
414    #[test]
415    fn combining_identical_masks_is_idempotent() {
416        assert_eq!(
417            SqliteChangeOps::INSERT | SqliteChangeOps::INSERT,
418            SqliteChangeOps::INSERT,
419        );
420    }
421
422    #[test]
423    fn contains_single() {
424        assert!(SqliteChangeOps::INSERT.contains(SqliteChangeOps::INSERT));
425    }
426
427    #[test]
428    fn all_contains_insert_or_delete() {
429        assert!(SqliteChangeOps::ALL.contains(SqliteChangeOps::INSERT | SqliteChangeOps::DELETE));
430    }
431
432    #[test]
433    fn insert_does_not_contain_all() {
434        assert!(!SqliteChangeOps::INSERT.contains(SqliteChangeOps::ALL));
435    }
436
437    #[test]
438    fn from_ffi_insert() {
439        assert_eq!(
440            SqliteChangeOp::from_ffi(ffi::SQLITE_INSERT),
441            SqliteChangeOp::Insert
442        );
443    }
444
445    #[test]
446    fn from_ffi_update() {
447        assert_eq!(
448            SqliteChangeOp::from_ffi(ffi::SQLITE_UPDATE),
449            SqliteChangeOp::Update
450        );
451    }
452
453    #[test]
454    fn from_ffi_delete() {
455        assert_eq!(
456            SqliteChangeOp::from_ffi(ffi::SQLITE_DELETE),
457            SqliteChangeOp::Delete
458        );
459    }
460
461    #[test]
462    fn to_ops_roundtrip() {
463        assert_eq!(SqliteChangeOp::Insert.to_ops(), SqliteChangeOps::INSERT);
464        assert_eq!(SqliteChangeOp::Update.to_ops(), SqliteChangeOps::UPDATE);
465        assert_eq!(SqliteChangeOp::Delete.to_ops(), SqliteChangeOps::DELETE);
466        assert_eq!(
467            SqliteChangeOp::Unknown(999).to_ops(),
468            SqliteChangeOps::UNKNOWN
469        );
470    }
471
472    #[test]
473    fn from_ffi_unknown_code() {
474        assert_eq!(SqliteChangeOp::from_ffi(999), SqliteChangeOp::Unknown(999));
475    }
476
477    #[test]
478    fn sqlite_change_event_is_copy() {
479        let event = SqliteChangeEvent {
480            op: SqliteChangeOp::Delete,
481            db_name: "main",
482            table_name: "posts",
483            rowid: 7,
484        };
485        // Verify Copy by assigning to two bindings without a move error.
486        let a = event;
487        let b = event;
488        assert_eq!(a.rowid, b.rowid);
489    }
490
491    #[test]
492    fn debug_formatting() {
493        // The `bitflags!`-derived Debug names single flags, the exact
494        // composite rendering is bitflags' concern, so only check the names
495        // appear and that empty and composite masks format without panicking.
496        assert!(format!("{:?}", SqliteChangeOps::INSERT).contains("INSERT"));
497        assert!(format!("{:?}", SqliteChangeOps::DELETE).contains("DELETE"));
498        let _ = format!("{:?}", SqliteChangeOps::empty());
499        let _ = format!("{:?}", SqliteChangeOps::ALL);
500    }
501
502    #[test]
503    fn bitand_works() {
504        let mask = SqliteChangeOps::ALL & SqliteChangeOps::INSERT;
505        assert_eq!(mask, SqliteChangeOps::INSERT);
506    }
507
508    // -----------------------------------------------------------------------
509    // Router unit tests (typed `on(table, ...)` routing is covered in hooks.rs,
510    // which has real `table!` markers and a live connection)
511    // -----------------------------------------------------------------------
512
513    fn make_event(
514        op: SqliteChangeOp,
515        table: &'static str,
516        rowid: i64,
517    ) -> SqliteChangeEvent<'static> {
518        SqliteChangeEvent {
519            op,
520            db_name: "main",
521            table_name: table,
522            rowid,
523        }
524    }
525
526    #[test]
527    fn empty_router_dispatches_nothing() {
528        let mut router = SqliteUpdateRouter::new();
529        // Must not panic when there are no routes.
530        router.dispatch(make_event(SqliteChangeOp::Insert, "users", 1));
531    }
532
533    #[test]
534    fn on_any_dispatches_for_every_table() {
535        let fired = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
536        let f2 = fired.clone();
537        let mut router = SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |e| {
538            f2.lock().unwrap().push((e.op, e.rowid));
539        });
540
541        router.dispatch(make_event(SqliteChangeOp::Insert, "users", 1));
542        router.dispatch(make_event(SqliteChangeOp::Delete, "posts", 2));
543
544        assert_eq!(
545            *fired.lock().unwrap(),
546            vec![(SqliteChangeOp::Insert, 1), (SqliteChangeOp::Delete, 2)],
547        );
548    }
549
550    #[test]
551    fn router_filters_by_op_mask() {
552        let fired = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
553        let f2 = fired.clone();
554        let mut router = SqliteUpdateRouter::new().on_any(SqliteChangeOps::INSERT, move |e| {
555            f2.lock().unwrap().push(e.rowid);
556        });
557
558        router.dispatch(make_event(SqliteChangeOp::Insert, "users", 1));
559        router.dispatch(make_event(SqliteChangeOp::Update, "users", 2)); // filtered out
560        router.dispatch(make_event(SqliteChangeOp::Delete, "users", 3)); // filtered out
561
562        assert_eq!(*fired.lock().unwrap(), vec![1]);
563    }
564
565    #[test]
566    fn every_matching_route_fires() {
567        let count = std::sync::Arc::new(std::sync::Mutex::new(0u32));
568        let c1 = count.clone();
569        let c2 = count.clone();
570        let mut router = SqliteUpdateRouter::new()
571            .on_any(SqliteChangeOps::ALL, move |_| {
572                *c1.lock().unwrap() += 1;
573            })
574            .on_any(SqliteChangeOps::INSERT, move |_| {
575                *c2.lock().unwrap() += 1;
576            });
577
578        // Insert matches both routes, delete matches only the first.
579        router.dispatch(make_event(SqliteChangeOp::Insert, "users", 1));
580        router.dispatch(make_event(SqliteChangeOp::Delete, "users", 2));
581
582        assert_eq!(*count.lock().unwrap(), 3);
583    }
584
585    fn connection() -> SqliteConnection {
586        SqliteConnection::establish(":memory:").unwrap()
587    }
588
589    // ===================================================================
590    // Change-hook integration tests
591    // ===================================================================
592
593    table! {
594        hook_users {
595            id -> Integer,
596            name -> Text,
597        }
598    }
599
600    table! {
601        hook_posts {
602            id -> Integer,
603            title -> Text,
604        }
605    }
606
607    fn setup_hook_tables(conn: &mut SqliteConnection) {
608        crate::sql_query(
609            "CREATE TABLE hook_users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
610        )
611        .execute(conn)
612        .unwrap();
613        crate::sql_query(
614            "CREATE TABLE hook_posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL)",
615        )
616        .execute(conn)
617        .unwrap();
618    }
619
620    // A schema-qualified `table!` marker routes only to its attached database,
621    // even when a same-named table exists in `main`.
622    #[diesel_test_helper::test]
623    fn router_on_matches_schema_qualified_table() {
624        use std::sync::{Arc, Mutex};
625
626        table! {
627            attached.shared_items (id) {
628                id -> Integer,
629            }
630        }
631
632        let conn = &mut connection();
633        crate::sql_query("ATTACH DATABASE ':memory:' AS attached")
634            .execute(conn)
635            .unwrap();
636        crate::sql_query("CREATE TABLE shared_items (id INTEGER PRIMARY KEY)")
637            .execute(conn)
638            .unwrap();
639        crate::sql_query("CREATE TABLE attached.shared_items (id INTEGER PRIMARY KEY)")
640            .execute(conn)
641            .unwrap();
642
643        let fired = Arc::new(Mutex::new(Vec::new()));
644        let f2 = fired.clone();
645        conn.on_update(SqliteUpdateRouter::new().on(
646            shared_items::table,
647            SqliteChangeOps::ALL,
648            move |ev| {
649                f2.lock().unwrap().push((ev.db_name.to_owned(), ev.rowid));
650            },
651        ));
652
653        // A change in `main.shared_items` must not fire the attached-only route.
654        crate::sql_query("INSERT INTO main.shared_items (id) VALUES (1)")
655            .execute(conn)
656            .unwrap();
657        // A change in `attached.shared_items` must fire it.
658        crate::sql_query("INSERT INTO attached.shared_items (id) VALUES (2)")
659            .execute(conn)
660            .unwrap();
661
662        assert_eq!(
663            *fired.lock().unwrap(),
664            vec![("attached".to_owned(), 2)],
665            "a schema-qualified route matches only its attached database"
666        );
667    }
668
669    #[diesel_test_helper::test]
670    fn router_on_dispatches_to_typed_table() {
671        use std::sync::{Arc, Mutex};
672        let conn = &mut connection();
673        setup_hook_tables(conn);
674
675        let fired = Arc::new(Mutex::new(Vec::new()));
676        let fired2 = fired.clone();
677
678        conn.on_update(SqliteUpdateRouter::new().on(
679            hook_users::table,
680            SqliteChangeOps::INSERT,
681            move |change| {
682                fired2.lock().unwrap().push((change.op, change.rowid));
683            },
684        ));
685
686        // INSERT a row: the route fires immediately during sqlite3_step().
687        crate::sql_query("INSERT INTO hook_users (name) VALUES ('Alice')")
688            .execute(conn)
689            .unwrap();
690
691        let events = fired.lock().unwrap().clone();
692        assert_eq!(events.len(), 1);
693        assert_eq!(events[0].0, SqliteChangeOp::Insert);
694        assert_eq!(events[0].1, 1); // rowid
695    }
696
697    #[diesel_test_helper::test]
698    fn on_delete_fires_only_for_delete() {
699        use std::sync::{Arc, Mutex};
700        let conn = &mut connection();
701        setup_hook_tables(conn);
702
703        let fired = Arc::new(Mutex::new(Vec::new()));
704        let fired2 = fired.clone();
705
706        conn.on_update(SqliteUpdateRouter::new().on(
707            hook_users::table,
708            SqliteChangeOps::DELETE,
709            move |change| {
710                fired2.lock().unwrap().push(change.op);
711            },
712        ));
713
714        // INSERT + UPDATE + DELETE
715        crate::sql_query("INSERT INTO hook_users (name) VALUES ('Alice')")
716            .execute(conn)
717            .unwrap();
718        crate::sql_query("UPDATE hook_users SET name = 'Bob' WHERE id = 1")
719            .execute(conn)
720            .unwrap();
721        crate::sql_query("DELETE FROM hook_users WHERE id = 1")
722            .execute(conn)
723            .unwrap();
724
725        let events = fired.lock().unwrap().clone();
726        // Only the DELETE should have matched.
727        assert_eq!(events.len(), 1);
728        assert_eq!(events[0], SqliteChangeOp::Delete);
729    }
730
731    #[diesel_test_helper::test]
732    fn every_matching_route_fires_in_order() {
733        use std::sync::{Arc, Mutex};
734        let conn = &mut connection();
735        setup_hook_tables(conn);
736
737        let order = Arc::new(Mutex::new(Vec::new()));
738        let o1 = order.clone();
739        let o2 = order.clone();
740
741        conn.on_update(
742            SqliteUpdateRouter::new()
743                .on(hook_users::table, SqliteChangeOps::INSERT, move |_| {
744                    o1.lock().unwrap().push(1);
745                })
746                .on(hook_users::table, SqliteChangeOps::INSERT, move |_| {
747                    o2.lock().unwrap().push(2);
748                }),
749        );
750
751        crate::sql_query("INSERT INTO hook_users (name) VALUES ('X')")
752            .execute(conn)
753            .unwrap();
754
755        assert_eq!(*order.lock().unwrap(), vec![1, 2]);
756    }
757
758    #[diesel_test_helper::test]
759    fn remove_update_stops_dispatch() {
760        use std::sync::{Arc, Mutex};
761        let conn = &mut connection();
762        setup_hook_tables(conn);
763
764        let fired = Arc::new(Mutex::new(0u32));
765        let f2 = fired.clone();
766
767        conn.on_update(
768            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |_| {
769                *f2.lock().unwrap() += 1;
770            }),
771        );
772
773        crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
774            .execute(conn)
775            .unwrap();
776        assert_eq!(*fired.lock().unwrap(), 1);
777
778        // Remove the hook.
779        conn.remove_update_hook();
780
781        crate::sql_query("INSERT INTO hook_users (name) VALUES ('B')")
782            .execute(conn)
783            .unwrap();
784        // Should still be 1, hook was removed.
785        assert_eq!(*fired.lock().unwrap(), 1);
786    }
787
788    #[diesel_test_helper::test]
789    fn events_fire_immediately_during_statement() {
790        use std::sync::{Arc, Mutex};
791        let conn = &mut connection();
792        setup_hook_tables(conn);
793
794        // Insert a row without a hook first.
795        crate::sql_query("INSERT INTO hook_users (name) VALUES ('Z')")
796            .execute(conn)
797            .unwrap();
798
799        let fired = Arc::new(Mutex::new(Vec::new()));
800        let f2 = fired.clone();
801
802        conn.on_update(SqliteUpdateRouter::new().on(
803            hook_users::table,
804            SqliteChangeOps::UPDATE,
805            move |event| {
806                f2.lock().unwrap().push(event.rowid);
807            },
808        ));
809
810        // UPDATE triggers the C hook immediately during sqlite3_step().
811        crate::sql_query("UPDATE hook_users SET name = 'W' WHERE id = 1")
812            .execute(conn)
813            .unwrap();
814
815        assert_eq!(*fired.lock().unwrap(), vec![1i64]);
816    }
817
818    #[diesel_test_helper::test]
819    fn on_update_fires_for_update_only() {
820        use std::sync::{Arc, Mutex};
821        let conn = &mut connection();
822        setup_hook_tables(conn);
823
824        let count = Arc::new(Mutex::new(0u32));
825        let c2 = count.clone();
826
827        conn.on_update(SqliteUpdateRouter::new().on(
828            hook_users::table,
829            SqliteChangeOps::UPDATE,
830            move |event| {
831                assert_eq!(event.op, SqliteChangeOp::Update);
832                *c2.lock().unwrap() += 1;
833            },
834        ));
835
836        crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
837            .execute(conn)
838            .unwrap();
839        crate::sql_query("UPDATE hook_users SET name = 'B' WHERE id = 1")
840            .execute(conn)
841            .unwrap();
842        crate::sql_query("DELETE FROM hook_users WHERE id = 1")
843            .execute(conn)
844            .unwrap();
845
846        assert_eq!(*count.lock().unwrap(), 1);
847    }
848
849    #[diesel_test_helper::test]
850    fn on_update_receives_every_change() {
851        use std::sync::{Arc, Mutex};
852        let conn = &mut connection();
853        setup_hook_tables(conn);
854
855        let events = Arc::new(Mutex::new(Vec::new()));
856        let e2 = events.clone();
857
858        conn.on_update(
859            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
860                e2.lock().unwrap().push((ev.op, ev.table_name.to_owned()));
861            }),
862        );
863
864        crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
865            .execute(conn)
866            .unwrap();
867        crate::sql_query("INSERT INTO hook_posts (title) VALUES ('P')")
868            .execute(conn)
869            .unwrap();
870        crate::sql_query("UPDATE hook_users SET name = 'B' WHERE id = 1")
871            .execute(conn)
872            .unwrap();
873        crate::sql_query("DELETE FROM hook_posts WHERE id = 1")
874            .execute(conn)
875            .unwrap();
876
877        let evts = events.lock().unwrap().clone();
878        assert_eq!(evts.len(), 4);
879        assert_eq!(evts[0], (SqliteChangeOp::Insert, "hook_users".to_owned()));
880        assert_eq!(evts[1], (SqliteChangeOp::Insert, "hook_posts".to_owned()));
881        assert_eq!(evts[2], (SqliteChangeOp::Update, "hook_users".to_owned()));
882        assert_eq!(evts[3], (SqliteChangeOp::Delete, "hook_posts".to_owned()));
883    }
884
885    #[diesel_test_helper::test]
886    fn on_update_filters_by_op_mask() {
887        use std::sync::{Arc, Mutex};
888        let conn = &mut connection();
889        setup_hook_tables(conn);
890
891        let count = Arc::new(Mutex::new(0u32));
892        let c2 = count.clone();
893
894        conn.on_update(SqliteUpdateRouter::new().on_any(
895            SqliteChangeOps::INSERT | SqliteChangeOps::DELETE,
896            move |_| {
897                *c2.lock().unwrap() += 1;
898            },
899        ));
900
901        crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
902            .execute(conn)
903            .unwrap();
904        crate::sql_query("UPDATE hook_users SET name = 'B' WHERE id = 1")
905            .execute(conn)
906            .unwrap();
907        crate::sql_query("DELETE FROM hook_users WHERE id = 1")
908            .execute(conn)
909            .unwrap();
910
911        // INSERT + DELETE = 2, not UPDATE
912        assert_eq!(*count.lock().unwrap(), 2);
913    }
914
915    #[diesel_test_helper::test]
916    fn router_dispatches_to_multiple_tables() {
917        use std::sync::{Arc, Mutex};
918        let conn = &mut connection();
919        setup_hook_tables(conn);
920
921        let user_count = Arc::new(Mutex::new(0u32));
922        let post_count = Arc::new(Mutex::new(0u32));
923        let uc = user_count.clone();
924        let pc = post_count.clone();
925
926        conn.on_update(
927            SqliteUpdateRouter::new()
928                .on(hook_users::table, SqliteChangeOps::ALL, move |_| {
929                    *uc.lock().unwrap() += 1;
930                })
931                .on(hook_posts::table, SqliteChangeOps::ALL, move |_| {
932                    *pc.lock().unwrap() += 1;
933                }),
934        );
935
936        crate::sql_query("INSERT INTO hook_users (name) VALUES ('X')")
937            .execute(conn)
938            .unwrap();
939        crate::sql_query("INSERT INTO hook_posts (title) VALUES ('Y')")
940            .execute(conn)
941            .unwrap();
942
943        assert_eq!(*user_count.lock().unwrap(), 1);
944        assert_eq!(*post_count.lock().unwrap(), 1);
945    }
946
947    #[diesel_test_helper::test]
948    fn on_any_audit_plus_specific_route() {
949        use std::sync::{Arc, Mutex};
950        let conn = &mut connection();
951        setup_hook_tables(conn);
952
953        let audit_count = Arc::new(Mutex::new(0u32));
954        let user_insert_count = Arc::new(Mutex::new(0u32));
955        let ac = audit_count.clone();
956        let uic = user_insert_count.clone();
957
958        conn.on_update(
959            SqliteUpdateRouter::new()
960                .on_any(SqliteChangeOps::ALL, move |_| {
961                    *ac.lock().unwrap() += 1;
962                })
963                .on(hook_users::table, SqliteChangeOps::INSERT, move |_| {
964                    *uic.lock().unwrap() += 1;
965                }),
966        );
967
968        // Hits both the audit route and the user-insert route.
969        crate::sql_query("INSERT INTO hook_users (name) VALUES ('X')")
970            .execute(conn)
971            .unwrap();
972        // Hits only the audit route.
973        crate::sql_query("INSERT INTO hook_posts (title) VALUES ('Y')")
974            .execute(conn)
975            .unwrap();
976
977        assert_eq!(*audit_count.lock().unwrap(), 2);
978        assert_eq!(*user_insert_count.lock().unwrap(), 1);
979    }
980
981    #[diesel_test_helper::test]
982    fn rowid_in_filters_by_table() {
983        use std::sync::{Arc, Mutex};
984        let conn = &mut connection();
985        setup_hook_tables(conn);
986
987        let captured = Arc::new(Mutex::new(Vec::new()));
988        let c2 = captured.clone();
989
990        conn.on_update(
991            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |change| {
992                if let Some(rowid) = change.rowid_in(hook_users::table) {
993                    c2.lock().unwrap().push(rowid);
994                }
995            }),
996        );
997
998        crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
999            .execute(conn)
1000            .unwrap();
1001        crate::sql_query("INSERT INTO hook_posts (title) VALUES ('P')")
1002            .execute(conn)
1003            .unwrap();
1004
1005        // Only the hook_users rowid was captured.
1006        assert_eq!(*captured.lock().unwrap(), vec![1i64]);
1007    }
1008
1009    #[diesel_test_helper::test]
1010    fn is_from_matches_table_marker() {
1011        use std::sync::{Arc, Mutex};
1012        let conn = &mut connection();
1013        setup_hook_tables(conn);
1014
1015        let captured = Arc::new(Mutex::new(Vec::new()));
1016        let c2 = captured.clone();
1017
1018        conn.on_update(
1019            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |change| {
1020                c2.lock().unwrap().push((
1021                    change.is_from(hook_users::table),
1022                    change.is_from(hook_posts::table),
1023                ));
1024            }),
1025        );
1026
1027        crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
1028            .execute(conn)
1029            .unwrap();
1030
1031        assert_eq!(*captured.lock().unwrap(), vec![(true, false)]);
1032    }
1033
1034    #[diesel_test_helper::test]
1035    fn hooks_fire_across_transactions() {
1036        use std::sync::{Arc, Mutex};
1037        let conn = &mut connection();
1038        setup_hook_tables(conn);
1039
1040        let fired = Arc::new(Mutex::new(Vec::new()));
1041        let f2 = fired.clone();
1042
1043        // Register hook BEFORE the transaction.
1044        conn.on_update(SqliteUpdateRouter::new().on(
1045            hook_users::table,
1046            SqliteChangeOps::INSERT,
1047            move |event| {
1048                f2.lock().unwrap().push(event.rowid);
1049            },
1050        ));
1051
1052        conn.immediate_transaction(|conn| {
1053            crate::sql_query("INSERT INTO hook_users (name) VALUES ('TxUser')")
1054                .execute(conn)
1055                .unwrap();
1056            Ok::<_, crate::result::Error>(())
1057        })
1058        .unwrap();
1059
1060        assert_eq!(fired.lock().unwrap().len(), 1);
1061    }
1062
1063    // ===================================================================
1064    // Negative tests: cases where the update hook must NOT fire
1065    //
1066    // These are documented SQLite limitations of sqlite3_update_hook():
1067    // https://www.sqlite.org/c3ref/update_hook.html
1068    // ===================================================================
1069
1070    /// The update hook is not invoked for WITHOUT ROWID tables.
1071    /// See: https://www.sqlite.org/c3ref/update_hook.html
1072    #[diesel_test_helper::test]
1073    fn update_hook_silent_for_without_rowid_tables() {
1074        use std::sync::{Arc, Mutex};
1075        let conn = &mut connection();
1076
1077        crate::sql_query("CREATE TABLE kv (key TEXT PRIMARY KEY, val TEXT NOT NULL) WITHOUT ROWID")
1078            .execute(conn)
1079            .unwrap();
1080
1081        let events: Arc<Mutex<Vec<SqliteChangeOp>>> = Arc::new(Mutex::new(Vec::new()));
1082        let e2 = events.clone();
1083
1084        conn.on_update(
1085            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1086                if ev.table_name == "kv" {
1087                    e2.lock().unwrap().push(ev.op);
1088                }
1089            }),
1090        );
1091
1092        crate::sql_query("INSERT INTO kv (key, val) VALUES ('a', '1')")
1093            .execute(conn)
1094            .unwrap();
1095        crate::sql_query("UPDATE kv SET val = '2' WHERE key = 'a'")
1096            .execute(conn)
1097            .unwrap();
1098        crate::sql_query("DELETE FROM kv WHERE key = 'a'")
1099            .execute(conn)
1100            .unwrap();
1101
1102        assert!(
1103            events.lock().unwrap().is_empty(),
1104            "update hook must not fire for WITHOUT ROWID tables"
1105        );
1106    }
1107
1108    /// When a UNIQUE constraint conflict is resolved via ON CONFLICT REPLACE,
1109    /// the implicit deletion of the conflicting row does NOT fire the update
1110    /// hook. Only the INSERT for the new row fires.
1111    /// See: https://www.sqlite.org/c3ref/update_hook.html
1112    #[diesel_test_helper::test]
1113    fn update_hook_silent_for_on_conflict_replace_deletion() {
1114        use std::sync::{Arc, Mutex};
1115        let conn = &mut connection();
1116
1117        crate::sql_query("CREATE TABLE uq (id INTEGER PRIMARY KEY, val TEXT NOT NULL UNIQUE)")
1118            .execute(conn)
1119            .unwrap();
1120
1121        crate::sql_query("INSERT INTO uq (id, val) VALUES (1, 'original')")
1122            .execute(conn)
1123            .unwrap();
1124
1125        let events: Arc<Mutex<Vec<(SqliteChangeOp, i64)>>> = Arc::new(Mutex::new(Vec::new()));
1126        let e2 = events.clone();
1127
1128        conn.on_update(
1129            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1130                if ev.table_name == "uq" {
1131                    e2.lock().unwrap().push((ev.op, ev.rowid));
1132                }
1133            }),
1134        );
1135
1136        // INSERT OR REPLACE with a conflicting val: the old row (id=1) is
1137        // silently deleted by SQLite and the new row (id=2) is inserted.
1138        // The hook fires only for the INSERT of the new row.
1139        crate::sql_query("INSERT OR REPLACE INTO uq (id, val) VALUES (2, 'original')")
1140            .execute(conn)
1141            .unwrap();
1142
1143        let recorded = events.lock().unwrap();
1144        assert_eq!(
1145            recorded.len(),
1146            1,
1147            "expected only 1 event (INSERT), got: {:?}",
1148            *recorded
1149        );
1150        assert_eq!(recorded[0].0, SqliteChangeOp::Insert);
1151        assert_eq!(recorded[0].1, 2, "new row should have rowid 2");
1152    }
1153
1154    /// DELETE FROM without a WHERE clause triggers the truncate optimization,
1155    /// which bypasses the update hook entirely: no per-row DELETE events fire.
1156    /// See: https://www.sqlite.org/lang_delete.html#truncateopt
1157    #[diesel_test_helper::test]
1158    fn update_hook_silent_for_truncate_optimization() {
1159        use std::sync::{Arc, Mutex};
1160        let conn = &mut connection();
1161
1162        // The truncate optimization applies when:
1163        //   1. No WHERE clause
1164        //   2. No RETURNING clause
1165        //   3. No triggers on the table
1166        crate::sql_query("CREATE TABLE bulk (id INTEGER PRIMARY KEY, data TEXT NOT NULL)")
1167            .execute(conn)
1168            .unwrap();
1169
1170        crate::sql_query("INSERT INTO bulk (data) VALUES ('a'), ('b'), ('c')")
1171            .execute(conn)
1172            .unwrap();
1173
1174        let events: Arc<Mutex<Vec<SqliteChangeOp>>> = Arc::new(Mutex::new(Vec::new()));
1175        let e2 = events.clone();
1176
1177        conn.on_update(
1178            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1179                if ev.table_name == "bulk" {
1180                    e2.lock().unwrap().push(ev.op);
1181                }
1182            }),
1183        );
1184
1185        // DELETE without WHERE: truncate optimization kicks in.
1186        crate::sql_query("DELETE FROM bulk").execute(conn).unwrap();
1187
1188        assert!(
1189            events.lock().unwrap().is_empty(),
1190            "truncate optimization should bypass the update hook"
1191        );
1192    }
1193
1194    /// When a table has triggers, the truncate optimization is disabled, so
1195    /// DELETE without WHERE fires per-row DELETE events as normal.
1196    #[diesel_test_helper::test]
1197    fn update_hook_fires_for_delete_all_when_triggers_disable_truncate() {
1198        use std::sync::{Arc, Mutex};
1199        let conn = &mut connection();
1200
1201        crate::sql_query("CREATE TABLE triggered (id INTEGER PRIMARY KEY, data TEXT NOT NULL)")
1202            .execute(conn)
1203            .unwrap();
1204        // A no-op trigger is enough to disable the truncate optimization.
1205        crate::sql_query(
1206            "CREATE TRIGGER trg_triggered BEFORE DELETE ON triggered \
1207             BEGIN SELECT 1; END",
1208        )
1209        .execute(conn)
1210        .unwrap();
1211
1212        crate::sql_query("INSERT INTO triggered (data) VALUES ('x'), ('y'), ('z')")
1213            .execute(conn)
1214            .unwrap();
1215
1216        let deletes: Arc<Mutex<Vec<i64>>> = Arc::new(Mutex::new(Vec::new()));
1217        let d2 = deletes.clone();
1218
1219        conn.on_update(
1220            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1221                if ev.table_name == "triggered" && ev.op == SqliteChangeOp::Delete {
1222                    d2.lock().unwrap().push(ev.rowid);
1223                }
1224            }),
1225        );
1226
1227        // DELETE without WHERE, but triggers exist ⇒ no truncate optimization.
1228        crate::sql_query("DELETE FROM triggered")
1229            .execute(conn)
1230            .unwrap();
1231
1232        assert_eq!(
1233            deletes.lock().unwrap().len(),
1234            3,
1235            "with triggers present, DELETE without WHERE fires per-row hooks"
1236        );
1237    }
1238
1239    /// Modifications to internal system tables like sqlite_sequence
1240    /// (used by AUTOINCREMENT) do not trigger the update hook.
1241    /// See: https://www.sqlite.org/c3ref/update_hook.html
1242    #[diesel_test_helper::test]
1243    fn update_hook_silent_for_internal_sqlite_sequence() {
1244        use std::sync::{Arc, Mutex};
1245        let conn = &mut connection();
1246
1247        // AUTOINCREMENT causes SQLite to maintain sqlite_sequence.
1248        crate::sql_query(
1249            "CREATE TABLE seq_test (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
1250        )
1251        .execute(conn)
1252        .unwrap();
1253
1254        let tables: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(Vec::new()));
1255        let t2 = tables.clone();
1256
1257        conn.on_update(
1258            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1259                t2.lock().unwrap().push(ev.table_name.to_owned());
1260            }),
1261        );
1262
1263        crate::sql_query("INSERT INTO seq_test (name) VALUES ('row1')")
1264            .execute(conn)
1265            .unwrap();
1266
1267        let recorded = tables.lock().unwrap();
1268        // Only the user table should appear, sqlite_sequence must be absent.
1269        assert!(
1270            recorded.iter().all(|t| t == "seq_test"),
1271            "expected only 'seq_test' events, got: {:?}",
1272            *recorded
1273        );
1274        assert!(
1275            !recorded.iter().any(|t| t == "sqlite_sequence"),
1276            "sqlite_sequence modifications must not trigger the update hook"
1277        );
1278    }
1279
1280    /// INSERT OR REPLACE on the primary key itself: when a row with the same
1281    /// PK already exists, the old row is silently deleted and the new row is
1282    /// inserted. The hook reports only the INSERT, not the implicit DELETE.
1283    #[diesel_test_helper::test]
1284    fn update_hook_silent_for_replace_into_on_pk_conflict() {
1285        use std::sync::{Arc, Mutex};
1286        let conn = &mut connection();
1287
1288        crate::sql_query("CREATE TABLE rep (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
1289            .execute(conn)
1290            .unwrap();
1291
1292        crate::sql_query("INSERT INTO rep (id, val) VALUES (1, 'old')")
1293            .execute(conn)
1294            .unwrap();
1295
1296        let events: Arc<Mutex<Vec<(SqliteChangeOp, i64)>>> = Arc::new(Mutex::new(Vec::new()));
1297        let e2 = events.clone();
1298
1299        conn.on_update(
1300            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1301                if ev.table_name == "rep" {
1302                    e2.lock().unwrap().push((ev.op, ev.rowid));
1303                }
1304            }),
1305        );
1306
1307        // REPLACE INTO with a conflicting PK.
1308        crate::sql_query("REPLACE INTO rep (id, val) VALUES (1, 'new')")
1309            .execute(conn)
1310            .unwrap();
1311
1312        let recorded = events.lock().unwrap();
1313        // Only one INSERT event, no DELETE for the old row.
1314        assert_eq!(
1315            recorded.len(),
1316            1,
1317            "expected 1 event for REPLACE INTO, got: {:?}",
1318            *recorded
1319        );
1320        assert_eq!(recorded[0].0, SqliteChangeOp::Insert);
1321        assert_eq!(recorded[0].1, 1);
1322    }
1323
1324    /// Regression test for the dangling-pointer soundness bug: after the
1325    /// connection is moved, a write still fires the registered callback.
1326    ///
1327    /// `sqlite3_update_hook` is handed the address of the connection's update
1328    /// hook state at registration time. Because that state is boxed, moving the
1329    /// (freely movable, `Sized`) `SqliteConnection` does not relocate it, so the
1330    /// pointer SQLite holds stays valid. On a buggy inline implementation the
1331    /// move would relocate the state and the trampoline would later dereference
1332    /// freed memory. This documents that the feature works through the move that
1333    /// real code performs (returning a connection, storing it in a struct,
1334    /// handing it to a pool, etc.).
1335    #[diesel_test_helper::test]
1336    fn change_hook_fires_after_connection_move() {
1337        use std::sync::{Arc, Mutex};
1338
1339        let count = Arc::new(Mutex::new(0u32));
1340        let count2 = count.clone();
1341
1342        let mut conn = connection();
1343        setup_hook_tables(&mut conn);
1344        conn.on_update(SqliteUpdateRouter::new().on(
1345            hook_users::table,
1346            SqliteChangeOps::INSERT,
1347            move |_| {
1348                *count2.lock().unwrap() += 1;
1349            },
1350        ));
1351
1352        // Move the connection onto the heap after the hook was registered.
1353        let mut boxed = Box::new(conn);
1354
1355        crate::sql_query("INSERT INTO hook_users (name) VALUES ('Alice')")
1356            .execute(&mut *boxed)
1357            .unwrap();
1358
1359        assert_eq!(
1360            *count.lock().unwrap(),
1361            1,
1362            "change hook did not fire after the connection was moved"
1363        );
1364    }
1365
1366    #[diesel_test_helper::test]
1367    fn router_filters_table_and_op() {
1368        use std::sync::{Arc, Mutex};
1369        let conn = &mut connection();
1370        setup_hook_tables(conn);
1371
1372        let fired = Arc::new(Mutex::new(Vec::new()));
1373        let fired2 = fired.clone();
1374
1375        // Fire for INSERT and UPDATE on hook_users only, not DELETE.
1376        conn.on_update(SqliteUpdateRouter::new().on(
1377            hook_users::table,
1378            SqliteChangeOps::INSERT | SqliteChangeOps::UPDATE,
1379            move |event| fired2.lock().unwrap().push(event.op),
1380        ));
1381
1382        crate::sql_query("INSERT INTO hook_users (name) VALUES ('Alice')")
1383            .execute(conn)
1384            .unwrap();
1385        crate::sql_query("UPDATE hook_users SET name = 'Bob' WHERE id = 1")
1386            .execute(conn)
1387            .unwrap();
1388        crate::sql_query("DELETE FROM hook_users WHERE id = 1")
1389            .execute(conn)
1390            .unwrap();
1391        // A change on a different table must not fire this hook.
1392        crate::sql_query("INSERT INTO hook_posts (title) VALUES ('Hello')")
1393            .execute(conn)
1394            .unwrap();
1395
1396        let events = fired.lock().unwrap().clone();
1397        assert_eq!(events, vec![SqliteChangeOp::Insert, SqliteChangeOp::Update]);
1398    }
1399}