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/// # // This test registers Rust callbacks that sqlite invokes through the
205/// # // FFI boundary, which is not supported by miri `-Zmiri-native-lib`.
206/// # #[cfg(not(miri))]
207/// # fn main() {
208/// use diesel::prelude::*;
209/// use diesel::sqlite::{SqliteConnection, SqliteChangeOps, SqliteUpdateRouter};
210/// use std::sync::{Arc, Mutex};
211///
212/// diesel::table! { users (id) { id -> Integer, name -> Text, } }
213///
214/// # let conn = &mut SqliteConnection::establish(":memory:").unwrap();
215/// # use diesel::connection::SimpleConnection;
216/// # conn.batch_execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)").unwrap();
217/// let inserted = Arc::new(Mutex::new(Vec::new()));
218/// let captured = inserted.clone();
219///
220/// conn.on_update(
221///     SqliteUpdateRouter::new()
222///         .on(users::table, SqliteChangeOps::INSERT, move |change| {
223///             captured.lock().unwrap().push(change.rowid);
224///         }),
225/// );
226///
227/// diesel::insert_into(users::table)
228///     .values(users::name.eq("Alice"))
229///     .execute(conn)
230///     .unwrap();
231///
232/// assert_eq!(*inserted.lock().unwrap(), vec![1]);
233/// # }
234/// # #[cfg(miri)]
235/// # fn main() {}
236/// ```
237///
238/// Every matching route fires for a given event, so overlapping routes (for
239/// example an [`on_any`](Self::on_any) audit log alongside table-specific
240/// handlers) all run. There are no per-route handles: to change the routes,
241/// install a different router or call
242/// [`remove_update_hook`](super::SqliteConnection::remove_update_hook).
243#[allow(missing_debug_implementations)]
244pub struct SqliteUpdateRouter {
245    routes: Vec<Route>,
246}
247
248impl SqliteUpdateRouter {
249    /// Creates an empty router that matches nothing until routes are added.
250    pub fn new() -> Self {
251        SqliteUpdateRouter { routes: Vec::new() }
252    }
253
254    /// Routes changes on `table` matching `ops` to `callback`.
255    ///
256    /// `table` is a table type, either generated by [`table!`](macro@crate::table)
257    /// or a diesel-dynamic-schema table. The schema name is treated as database name
258    /// An unqualified table matches by table name in
259    /// any database, so a same-named table in an `ATTACH`-ed database also
260    /// matches. A schema-qualified `table!` type additionally matches the
261    /// database name, so it fires only for that attached database.
262    pub fn on<T, F>(mut self, table: T, ops: SqliteChangeOps, callback: F) -> Self
263    where
264        T: NamedTable + Send + 'static,
265        F: FnMut(SqliteChangeEvent<'_>) + Send + 'static,
266    {
267        // The table value is taken only for ergonomic call syntax. Its name and
268        // optional schema come from the type's static component.
269        let _ = table;
270        self.routes.push(Route {
271            table: Some(Box::new(table)),
272            ops,
273            callback: Box::new(callback),
274        });
275        self
276    }
277
278    /// Routes changes on any table matching `ops` to `callback`.
279    pub fn on_any<F>(mut self, ops: SqliteChangeOps, callback: F) -> Self
280    where
281        F: FnMut(SqliteChangeEvent<'_>) + Send + 'static,
282    {
283        self.routes.push(Route {
284            table: None,
285            ops,
286            callback: Box::new(callback),
287        });
288        self
289    }
290
291    /// Dispatches an event to every matching route, in build order.
292    fn dispatch(&mut self, event: SqliteChangeEvent<'_>) {
293        for route in &mut self.routes {
294            if route.matches(&event) {
295                (route.callback)(event);
296            }
297        }
298    }
299
300    /// Turns the router into the callback installed by
301    /// [`on_update`](super::SqliteConnection::on_update).
302    pub(crate) fn into_hook(mut self) -> impl FnMut(SqliteChangeEvent<'_>) + Send {
303        move |event| self.dispatch(event)
304    }
305}
306
307impl Default for SqliteUpdateRouter {
308    fn default() -> Self {
309        SqliteUpdateRouter::new()
310    }
311}
312
313impl SqliteConnection {
314    /// Installs a [`SqliteUpdateRouter`](crate::sqlite::SqliteUpdateRouter) as
315    /// the update hook, invoked for every row change (insert, update, or delete)
316    /// on a [rowid table](https://www.sqlite.org/rowidtable.html). Replaces any
317    /// previously registered update hook, since SQLite allows only one per
318    /// connection.
319    ///
320    /// Build the router with [`SqliteUpdateRouter::on`] for typed per-table
321    /// routes and [`SqliteUpdateRouter::on_any`] for a table-agnostic route. A
322    /// single catch-all is `on_any(SqliteChangeOps::ALL, ...)`. Inside a
323    /// callback, [`is_from`](crate::sqlite::SqliteChangeEvent::is_from) and
324    /// [`rowid_in`](crate::sqlite::SqliteChangeEvent::rowid_in) match a `table!`
325    /// marker without a string.
326    ///
327    /// Callbacks run synchronously as part of the `sqlite3_step()` call that
328    /// performs the change, on the thread performing it, so they are never
329    /// invoked concurrently. Per SQLite, a callback must not use the connection
330    /// that triggered it (running any SQL, including a `SELECT`, counts as use)
331    /// and is not reentrant. A panic in a callback aborts the process. To act on
332    /// the changed row, capture its `rowid` and run the query after the
333    /// statement completes.
334    ///
335    /// # Limitations
336    ///
337    /// These come from the underlying
338    /// [`sqlite3_update_hook`](https://www.sqlite.org/c3ref/update_hook.html):
339    ///
340    /// - Only fires for [rowid tables](https://www.sqlite.org/rowidtable.html),
341    ///   not `WITHOUT ROWID` tables.
342    /// - Does not fire for changes to internal system tables, for `ON CONFLICT
343    ///   REPLACE` deletions, or for the truncate optimization (`DELETE` with no
344    ///   `WHERE` on a trigger-free table).
345    ///
346    /// See: [`sqlite3_update_hook`](https://www.sqlite.org/c3ref/update_hook.html)
347    ///
348    /// # Example
349    ///
350    /// ```rust
351    /// # // This test registers Rust callbacks that sqlite invokes through the
352    /// # // FFI boundary, which is not supported by miri `-Zmiri-native-lib`.
353    /// # #[cfg(not(miri))]
354    /// # fn main() {
355    /// use diesel::prelude::*;
356    /// use diesel::sqlite::{SqliteChangeOps, SqliteConnection, SqliteUpdateRouter};
357    /// use std::sync::{Arc, Mutex};
358    ///
359    /// diesel::table! { users (id) { id -> Integer, name -> Text, } }
360    ///
361    /// # use diesel::connection::SimpleConnection;
362    /// # let conn = &mut SqliteConnection::establish(":memory:").unwrap();
363    /// # conn.batch_execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL)").unwrap();
364    /// let changes = Arc::new(Mutex::new(Vec::new()));
365    /// let captured = changes.clone();
366    ///
367    /// conn.on_update(
368    ///     SqliteUpdateRouter::new().on(
369    ///         users::table,
370    ///         SqliteChangeOps::INSERT,
371    ///         move |change| captured.lock().unwrap().push(change.rowid),
372    ///     ),
373    /// );
374    ///
375    /// diesel::insert_into(users::table)
376    ///     .values(users::name.eq("Alice"))
377    ///     .execute(conn)
378    ///     .unwrap();
379    ///
380    /// assert_eq!(*changes.lock().unwrap(), vec![1]);
381    /// # }
382    /// # #[cfg(miri)]
383    /// # fn main() {}
384    /// ```
385    pub fn on_update(&mut self, router: SqliteUpdateRouter) {
386        self.raw_connection.set_update_hook(router.into_hook());
387    }
388
389    /// Removes the update hook. Subsequent row changes will not invoke any
390    /// callback.
391    ///
392    /// See [`on_update`](Self::on_update) for usage.
393    pub fn remove_update_hook(&mut self) {
394        self.raw_connection.remove_update_hook();
395    }
396}
397
398// miri doesn't support ffi callbacks
399#[cfg(all(test, not(miri)))]
400mod tests {
401    use super::*;
402    use crate::connection::Connection;
403    use crate::prelude::*;
404    use crate::query_dsl::RunQueryDsl;
405
406    /// Test-only helper: check mask vs raw FFI op code.
407    impl SqliteChangeOps {
408        fn matches(self, op_code: i32) -> bool {
409            self.matches_op(SqliteChangeOp::from_ffi(op_code))
410        }
411    }
412
413    #[test]
414    fn insert_or_delete_matches_both_but_not_update() {
415        let mask = SqliteChangeOps::INSERT | SqliteChangeOps::DELETE;
416        assert!(mask.matches(ffi::SQLITE_INSERT));
417        assert!(mask.matches(ffi::SQLITE_DELETE));
418        assert!(!mask.matches(ffi::SQLITE_UPDATE));
419    }
420
421    #[test]
422    fn all_matches_all_three() {
423        assert!(SqliteChangeOps::ALL.matches(ffi::SQLITE_INSERT));
424        assert!(SqliteChangeOps::ALL.matches(ffi::SQLITE_UPDATE));
425        assert!(SqliteChangeOps::ALL.matches(ffi::SQLITE_DELETE));
426    }
427
428    #[test]
429    fn combining_identical_masks_is_idempotent() {
430        assert_eq!(
431            SqliteChangeOps::INSERT | SqliteChangeOps::INSERT,
432            SqliteChangeOps::INSERT,
433        );
434    }
435
436    #[test]
437    fn contains_single() {
438        assert!(SqliteChangeOps::INSERT.contains(SqliteChangeOps::INSERT));
439    }
440
441    #[test]
442    fn all_contains_insert_or_delete() {
443        assert!(SqliteChangeOps::ALL.contains(SqliteChangeOps::INSERT | SqliteChangeOps::DELETE));
444    }
445
446    #[test]
447    fn insert_does_not_contain_all() {
448        assert!(!SqliteChangeOps::INSERT.contains(SqliteChangeOps::ALL));
449    }
450
451    #[test]
452    fn from_ffi_insert() {
453        assert_eq!(
454            SqliteChangeOp::from_ffi(ffi::SQLITE_INSERT),
455            SqliteChangeOp::Insert
456        );
457    }
458
459    #[test]
460    fn from_ffi_update() {
461        assert_eq!(
462            SqliteChangeOp::from_ffi(ffi::SQLITE_UPDATE),
463            SqliteChangeOp::Update
464        );
465    }
466
467    #[test]
468    fn from_ffi_delete() {
469        assert_eq!(
470            SqliteChangeOp::from_ffi(ffi::SQLITE_DELETE),
471            SqliteChangeOp::Delete
472        );
473    }
474
475    #[test]
476    fn to_ops_roundtrip() {
477        assert_eq!(SqliteChangeOp::Insert.to_ops(), SqliteChangeOps::INSERT);
478        assert_eq!(SqliteChangeOp::Update.to_ops(), SqliteChangeOps::UPDATE);
479        assert_eq!(SqliteChangeOp::Delete.to_ops(), SqliteChangeOps::DELETE);
480        assert_eq!(
481            SqliteChangeOp::Unknown(999).to_ops(),
482            SqliteChangeOps::UNKNOWN
483        );
484    }
485
486    #[test]
487    fn from_ffi_unknown_code() {
488        assert_eq!(SqliteChangeOp::from_ffi(999), SqliteChangeOp::Unknown(999));
489    }
490
491    #[test]
492    fn sqlite_change_event_is_copy() {
493        let event = SqliteChangeEvent {
494            op: SqliteChangeOp::Delete,
495            db_name: "main",
496            table_name: "posts",
497            rowid: 7,
498        };
499        // Verify Copy by assigning to two bindings without a move error.
500        let a = event;
501        let b = event;
502        assert_eq!(a.rowid, b.rowid);
503    }
504
505    #[test]
506    fn debug_formatting() {
507        // The `bitflags!`-derived Debug names single flags, the exact
508        // composite rendering is bitflags' concern, so only check the names
509        // appear and that empty and composite masks format without panicking.
510        assert!(format!("{:?}", SqliteChangeOps::INSERT).contains("INSERT"));
511        assert!(format!("{:?}", SqliteChangeOps::DELETE).contains("DELETE"));
512        let _ = format!("{:?}", SqliteChangeOps::empty());
513        let _ = format!("{:?}", SqliteChangeOps::ALL);
514    }
515
516    #[test]
517    fn bitand_works() {
518        let mask = SqliteChangeOps::ALL & SqliteChangeOps::INSERT;
519        assert_eq!(mask, SqliteChangeOps::INSERT);
520    }
521
522    // -----------------------------------------------------------------------
523    // Router unit tests (typed `on(table, ...)` routing is covered in hooks.rs,
524    // which has real `table!` markers and a live connection)
525    // -----------------------------------------------------------------------
526
527    fn make_event(
528        op: SqliteChangeOp,
529        table: &'static str,
530        rowid: i64,
531    ) -> SqliteChangeEvent<'static> {
532        SqliteChangeEvent {
533            op,
534            db_name: "main",
535            table_name: table,
536            rowid,
537        }
538    }
539
540    #[test]
541    fn empty_router_dispatches_nothing() {
542        let mut router = SqliteUpdateRouter::new();
543        // Must not panic when there are no routes.
544        router.dispatch(make_event(SqliteChangeOp::Insert, "users", 1));
545    }
546
547    #[test]
548    fn on_any_dispatches_for_every_table() {
549        let fired = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
550        let f2 = fired.clone();
551        let mut router = SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |e| {
552            f2.lock().unwrap().push((e.op, e.rowid));
553        });
554
555        router.dispatch(make_event(SqliteChangeOp::Insert, "users", 1));
556        router.dispatch(make_event(SqliteChangeOp::Delete, "posts", 2));
557
558        assert_eq!(
559            *fired.lock().unwrap(),
560            vec![(SqliteChangeOp::Insert, 1), (SqliteChangeOp::Delete, 2)],
561        );
562    }
563
564    #[test]
565    fn router_filters_by_op_mask() {
566        let fired = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
567        let f2 = fired.clone();
568        let mut router = SqliteUpdateRouter::new().on_any(SqliteChangeOps::INSERT, move |e| {
569            f2.lock().unwrap().push(e.rowid);
570        });
571
572        router.dispatch(make_event(SqliteChangeOp::Insert, "users", 1));
573        router.dispatch(make_event(SqliteChangeOp::Update, "users", 2)); // filtered out
574        router.dispatch(make_event(SqliteChangeOp::Delete, "users", 3)); // filtered out
575
576        assert_eq!(*fired.lock().unwrap(), vec![1]);
577    }
578
579    #[test]
580    fn every_matching_route_fires() {
581        let count = std::sync::Arc::new(std::sync::Mutex::new(0u32));
582        let c1 = count.clone();
583        let c2 = count.clone();
584        let mut router = SqliteUpdateRouter::new()
585            .on_any(SqliteChangeOps::ALL, move |_| {
586                *c1.lock().unwrap() += 1;
587            })
588            .on_any(SqliteChangeOps::INSERT, move |_| {
589                *c2.lock().unwrap() += 1;
590            });
591
592        // Insert matches both routes, delete matches only the first.
593        router.dispatch(make_event(SqliteChangeOp::Insert, "users", 1));
594        router.dispatch(make_event(SqliteChangeOp::Delete, "users", 2));
595
596        assert_eq!(*count.lock().unwrap(), 3);
597    }
598
599    fn connection() -> SqliteConnection {
600        SqliteConnection::establish(":memory:").unwrap()
601    }
602
603    // ===================================================================
604    // Change-hook integration tests
605    // ===================================================================
606
607    table! {
608        hook_users {
609            id -> Integer,
610            name -> Text,
611        }
612    }
613
614    table! {
615        hook_posts {
616            id -> Integer,
617            title -> Text,
618        }
619    }
620
621    fn setup_hook_tables(conn: &mut SqliteConnection) {
622        crate::sql_query(
623            "CREATE TABLE hook_users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
624        )
625        .execute(conn)
626        .unwrap();
627        crate::sql_query(
628            "CREATE TABLE hook_posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL)",
629        )
630        .execute(conn)
631        .unwrap();
632    }
633
634    // A schema-qualified `table!` marker routes only to its attached database,
635    // even when a same-named table exists in `main`.
636    #[diesel_test_helper::test]
637    fn router_on_matches_schema_qualified_table() {
638        use std::sync::{Arc, Mutex};
639
640        table! {
641            attached.shared_items (id) {
642                id -> Integer,
643            }
644        }
645
646        let conn = &mut connection();
647        crate::sql_query("ATTACH DATABASE ':memory:' AS attached")
648            .execute(conn)
649            .unwrap();
650        crate::sql_query("CREATE TABLE shared_items (id INTEGER PRIMARY KEY)")
651            .execute(conn)
652            .unwrap();
653        crate::sql_query("CREATE TABLE attached.shared_items (id INTEGER PRIMARY KEY)")
654            .execute(conn)
655            .unwrap();
656
657        let fired = Arc::new(Mutex::new(Vec::new()));
658        let f2 = fired.clone();
659        conn.on_update(SqliteUpdateRouter::new().on(
660            shared_items::table,
661            SqliteChangeOps::ALL,
662            move |ev| {
663                f2.lock().unwrap().push((ev.db_name.to_owned(), ev.rowid));
664            },
665        ));
666
667        // A change in `main.shared_items` must not fire the attached-only route.
668        crate::sql_query("INSERT INTO main.shared_items (id) VALUES (1)")
669            .execute(conn)
670            .unwrap();
671        // A change in `attached.shared_items` must fire it.
672        crate::sql_query("INSERT INTO attached.shared_items (id) VALUES (2)")
673            .execute(conn)
674            .unwrap();
675
676        assert_eq!(
677            *fired.lock().unwrap(),
678            vec![("attached".to_owned(), 2)],
679            "a schema-qualified route matches only its attached database"
680        );
681    }
682
683    #[diesel_test_helper::test]
684    fn router_on_dispatches_to_typed_table() {
685        use std::sync::{Arc, Mutex};
686        let conn = &mut connection();
687        setup_hook_tables(conn);
688
689        let fired = Arc::new(Mutex::new(Vec::new()));
690        let fired2 = fired.clone();
691
692        conn.on_update(SqliteUpdateRouter::new().on(
693            hook_users::table,
694            SqliteChangeOps::INSERT,
695            move |change| {
696                fired2.lock().unwrap().push((change.op, change.rowid));
697            },
698        ));
699
700        // INSERT a row: the route fires immediately during sqlite3_step().
701        crate::sql_query("INSERT INTO hook_users (name) VALUES ('Alice')")
702            .execute(conn)
703            .unwrap();
704
705        let events = fired.lock().unwrap().clone();
706        assert_eq!(events.len(), 1);
707        assert_eq!(events[0].0, SqliteChangeOp::Insert);
708        assert_eq!(events[0].1, 1); // rowid
709    }
710
711    #[diesel_test_helper::test]
712    fn on_delete_fires_only_for_delete() {
713        use std::sync::{Arc, Mutex};
714        let conn = &mut connection();
715        setup_hook_tables(conn);
716
717        let fired = Arc::new(Mutex::new(Vec::new()));
718        let fired2 = fired.clone();
719
720        conn.on_update(SqliteUpdateRouter::new().on(
721            hook_users::table,
722            SqliteChangeOps::DELETE,
723            move |change| {
724                fired2.lock().unwrap().push(change.op);
725            },
726        ));
727
728        // INSERT + UPDATE + DELETE
729        crate::sql_query("INSERT INTO hook_users (name) VALUES ('Alice')")
730            .execute(conn)
731            .unwrap();
732        crate::sql_query("UPDATE hook_users SET name = 'Bob' WHERE id = 1")
733            .execute(conn)
734            .unwrap();
735        crate::sql_query("DELETE FROM hook_users WHERE id = 1")
736            .execute(conn)
737            .unwrap();
738
739        let events = fired.lock().unwrap().clone();
740        // Only the DELETE should have matched.
741        assert_eq!(events.len(), 1);
742        assert_eq!(events[0], SqliteChangeOp::Delete);
743    }
744
745    #[diesel_test_helper::test]
746    fn every_matching_route_fires_in_order() {
747        use std::sync::{Arc, Mutex};
748        let conn = &mut connection();
749        setup_hook_tables(conn);
750
751        let order = Arc::new(Mutex::new(Vec::new()));
752        let o1 = order.clone();
753        let o2 = order.clone();
754
755        conn.on_update(
756            SqliteUpdateRouter::new()
757                .on(hook_users::table, SqliteChangeOps::INSERT, move |_| {
758                    o1.lock().unwrap().push(1);
759                })
760                .on(hook_users::table, SqliteChangeOps::INSERT, move |_| {
761                    o2.lock().unwrap().push(2);
762                }),
763        );
764
765        crate::sql_query("INSERT INTO hook_users (name) VALUES ('X')")
766            .execute(conn)
767            .unwrap();
768
769        assert_eq!(*order.lock().unwrap(), vec![1, 2]);
770    }
771
772    #[diesel_test_helper::test]
773    fn remove_update_stops_dispatch() {
774        use std::sync::{Arc, Mutex};
775        let conn = &mut connection();
776        setup_hook_tables(conn);
777
778        let fired = Arc::new(Mutex::new(0u32));
779        let f2 = fired.clone();
780
781        conn.on_update(
782            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |_| {
783                *f2.lock().unwrap() += 1;
784            }),
785        );
786
787        crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
788            .execute(conn)
789            .unwrap();
790        assert_eq!(*fired.lock().unwrap(), 1);
791
792        // Remove the hook.
793        conn.remove_update_hook();
794
795        crate::sql_query("INSERT INTO hook_users (name) VALUES ('B')")
796            .execute(conn)
797            .unwrap();
798        // Should still be 1, hook was removed.
799        assert_eq!(*fired.lock().unwrap(), 1);
800    }
801
802    #[diesel_test_helper::test]
803    fn events_fire_immediately_during_statement() {
804        use std::sync::{Arc, Mutex};
805        let conn = &mut connection();
806        setup_hook_tables(conn);
807
808        // Insert a row without a hook first.
809        crate::sql_query("INSERT INTO hook_users (name) VALUES ('Z')")
810            .execute(conn)
811            .unwrap();
812
813        let fired = Arc::new(Mutex::new(Vec::new()));
814        let f2 = fired.clone();
815
816        conn.on_update(SqliteUpdateRouter::new().on(
817            hook_users::table,
818            SqliteChangeOps::UPDATE,
819            move |event| {
820                f2.lock().unwrap().push(event.rowid);
821            },
822        ));
823
824        // UPDATE triggers the C hook immediately during sqlite3_step().
825        crate::sql_query("UPDATE hook_users SET name = 'W' WHERE id = 1")
826            .execute(conn)
827            .unwrap();
828
829        assert_eq!(*fired.lock().unwrap(), vec![1i64]);
830    }
831
832    #[diesel_test_helper::test]
833    fn on_update_fires_for_update_only() {
834        use std::sync::{Arc, Mutex};
835        let conn = &mut connection();
836        setup_hook_tables(conn);
837
838        let count = Arc::new(Mutex::new(0u32));
839        let c2 = count.clone();
840
841        conn.on_update(SqliteUpdateRouter::new().on(
842            hook_users::table,
843            SqliteChangeOps::UPDATE,
844            move |event| {
845                assert_eq!(event.op, SqliteChangeOp::Update);
846                *c2.lock().unwrap() += 1;
847            },
848        ));
849
850        crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
851            .execute(conn)
852            .unwrap();
853        crate::sql_query("UPDATE hook_users SET name = 'B' WHERE id = 1")
854            .execute(conn)
855            .unwrap();
856        crate::sql_query("DELETE FROM hook_users WHERE id = 1")
857            .execute(conn)
858            .unwrap();
859
860        assert_eq!(*count.lock().unwrap(), 1);
861    }
862
863    #[diesel_test_helper::test]
864    fn on_update_receives_every_change() {
865        use std::sync::{Arc, Mutex};
866        let conn = &mut connection();
867        setup_hook_tables(conn);
868
869        let events = Arc::new(Mutex::new(Vec::new()));
870        let e2 = events.clone();
871
872        conn.on_update(
873            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
874                e2.lock().unwrap().push((ev.op, ev.table_name.to_owned()));
875            }),
876        );
877
878        crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
879            .execute(conn)
880            .unwrap();
881        crate::sql_query("INSERT INTO hook_posts (title) VALUES ('P')")
882            .execute(conn)
883            .unwrap();
884        crate::sql_query("UPDATE hook_users SET name = 'B' WHERE id = 1")
885            .execute(conn)
886            .unwrap();
887        crate::sql_query("DELETE FROM hook_posts WHERE id = 1")
888            .execute(conn)
889            .unwrap();
890
891        let evts = events.lock().unwrap().clone();
892        assert_eq!(evts.len(), 4);
893        assert_eq!(evts[0], (SqliteChangeOp::Insert, "hook_users".to_owned()));
894        assert_eq!(evts[1], (SqliteChangeOp::Insert, "hook_posts".to_owned()));
895        assert_eq!(evts[2], (SqliteChangeOp::Update, "hook_users".to_owned()));
896        assert_eq!(evts[3], (SqliteChangeOp::Delete, "hook_posts".to_owned()));
897    }
898
899    #[diesel_test_helper::test]
900    fn on_update_filters_by_op_mask() {
901        use std::sync::{Arc, Mutex};
902        let conn = &mut connection();
903        setup_hook_tables(conn);
904
905        let count = Arc::new(Mutex::new(0u32));
906        let c2 = count.clone();
907
908        conn.on_update(SqliteUpdateRouter::new().on_any(
909            SqliteChangeOps::INSERT | SqliteChangeOps::DELETE,
910            move |_| {
911                *c2.lock().unwrap() += 1;
912            },
913        ));
914
915        crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
916            .execute(conn)
917            .unwrap();
918        crate::sql_query("UPDATE hook_users SET name = 'B' WHERE id = 1")
919            .execute(conn)
920            .unwrap();
921        crate::sql_query("DELETE FROM hook_users WHERE id = 1")
922            .execute(conn)
923            .unwrap();
924
925        // INSERT + DELETE = 2, not UPDATE
926        assert_eq!(*count.lock().unwrap(), 2);
927    }
928
929    #[diesel_test_helper::test]
930    fn router_dispatches_to_multiple_tables() {
931        use std::sync::{Arc, Mutex};
932        let conn = &mut connection();
933        setup_hook_tables(conn);
934
935        let user_count = Arc::new(Mutex::new(0u32));
936        let post_count = Arc::new(Mutex::new(0u32));
937        let uc = user_count.clone();
938        let pc = post_count.clone();
939
940        conn.on_update(
941            SqliteUpdateRouter::new()
942                .on(hook_users::table, SqliteChangeOps::ALL, move |_| {
943                    *uc.lock().unwrap() += 1;
944                })
945                .on(hook_posts::table, SqliteChangeOps::ALL, move |_| {
946                    *pc.lock().unwrap() += 1;
947                }),
948        );
949
950        crate::sql_query("INSERT INTO hook_users (name) VALUES ('X')")
951            .execute(conn)
952            .unwrap();
953        crate::sql_query("INSERT INTO hook_posts (title) VALUES ('Y')")
954            .execute(conn)
955            .unwrap();
956
957        assert_eq!(*user_count.lock().unwrap(), 1);
958        assert_eq!(*post_count.lock().unwrap(), 1);
959    }
960
961    #[diesel_test_helper::test]
962    fn on_any_audit_plus_specific_route() {
963        use std::sync::{Arc, Mutex};
964        let conn = &mut connection();
965        setup_hook_tables(conn);
966
967        let audit_count = Arc::new(Mutex::new(0u32));
968        let user_insert_count = Arc::new(Mutex::new(0u32));
969        let ac = audit_count.clone();
970        let uic = user_insert_count.clone();
971
972        conn.on_update(
973            SqliteUpdateRouter::new()
974                .on_any(SqliteChangeOps::ALL, move |_| {
975                    *ac.lock().unwrap() += 1;
976                })
977                .on(hook_users::table, SqliteChangeOps::INSERT, move |_| {
978                    *uic.lock().unwrap() += 1;
979                }),
980        );
981
982        // Hits both the audit route and the user-insert route.
983        crate::sql_query("INSERT INTO hook_users (name) VALUES ('X')")
984            .execute(conn)
985            .unwrap();
986        // Hits only the audit route.
987        crate::sql_query("INSERT INTO hook_posts (title) VALUES ('Y')")
988            .execute(conn)
989            .unwrap();
990
991        assert_eq!(*audit_count.lock().unwrap(), 2);
992        assert_eq!(*user_insert_count.lock().unwrap(), 1);
993    }
994
995    #[diesel_test_helper::test]
996    fn rowid_in_filters_by_table() {
997        use std::sync::{Arc, Mutex};
998        let conn = &mut connection();
999        setup_hook_tables(conn);
1000
1001        let captured = Arc::new(Mutex::new(Vec::new()));
1002        let c2 = captured.clone();
1003
1004        conn.on_update(
1005            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |change| {
1006                if let Some(rowid) = change.rowid_in(hook_users::table) {
1007                    c2.lock().unwrap().push(rowid);
1008                }
1009            }),
1010        );
1011
1012        crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
1013            .execute(conn)
1014            .unwrap();
1015        crate::sql_query("INSERT INTO hook_posts (title) VALUES ('P')")
1016            .execute(conn)
1017            .unwrap();
1018
1019        // Only the hook_users rowid was captured.
1020        assert_eq!(*captured.lock().unwrap(), vec![1i64]);
1021    }
1022
1023    #[diesel_test_helper::test]
1024    fn is_from_matches_table_marker() {
1025        use std::sync::{Arc, Mutex};
1026        let conn = &mut connection();
1027        setup_hook_tables(conn);
1028
1029        let captured = Arc::new(Mutex::new(Vec::new()));
1030        let c2 = captured.clone();
1031
1032        conn.on_update(
1033            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |change| {
1034                c2.lock().unwrap().push((
1035                    change.is_from(hook_users::table),
1036                    change.is_from(hook_posts::table),
1037                ));
1038            }),
1039        );
1040
1041        crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
1042            .execute(conn)
1043            .unwrap();
1044
1045        assert_eq!(*captured.lock().unwrap(), vec![(true, false)]);
1046    }
1047
1048    #[diesel_test_helper::test]
1049    fn hooks_fire_across_transactions() {
1050        use std::sync::{Arc, Mutex};
1051        let conn = &mut connection();
1052        setup_hook_tables(conn);
1053
1054        let fired = Arc::new(Mutex::new(Vec::new()));
1055        let f2 = fired.clone();
1056
1057        // Register hook BEFORE the transaction.
1058        conn.on_update(SqliteUpdateRouter::new().on(
1059            hook_users::table,
1060            SqliteChangeOps::INSERT,
1061            move |event| {
1062                f2.lock().unwrap().push(event.rowid);
1063            },
1064        ));
1065
1066        conn.immediate_transaction(|conn| {
1067            crate::sql_query("INSERT INTO hook_users (name) VALUES ('TxUser')")
1068                .execute(conn)
1069                .unwrap();
1070            Ok::<_, crate::result::Error>(())
1071        })
1072        .unwrap();
1073
1074        assert_eq!(fired.lock().unwrap().len(), 1);
1075    }
1076
1077    // ===================================================================
1078    // Negative tests: cases where the update hook must NOT fire
1079    //
1080    // These are documented SQLite limitations of sqlite3_update_hook():
1081    // https://www.sqlite.org/c3ref/update_hook.html
1082    // ===================================================================
1083
1084    /// The update hook is not invoked for WITHOUT ROWID tables.
1085    /// See: https://www.sqlite.org/c3ref/update_hook.html
1086    #[diesel_test_helper::test]
1087    fn update_hook_silent_for_without_rowid_tables() {
1088        use std::sync::{Arc, Mutex};
1089        let conn = &mut connection();
1090
1091        crate::sql_query("CREATE TABLE kv (key TEXT PRIMARY KEY, val TEXT NOT NULL) WITHOUT ROWID")
1092            .execute(conn)
1093            .unwrap();
1094
1095        let events: Arc<Mutex<Vec<SqliteChangeOp>>> = Arc::new(Mutex::new(Vec::new()));
1096        let e2 = events.clone();
1097
1098        conn.on_update(
1099            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1100                if ev.table_name == "kv" {
1101                    e2.lock().unwrap().push(ev.op);
1102                }
1103            }),
1104        );
1105
1106        crate::sql_query("INSERT INTO kv (key, val) VALUES ('a', '1')")
1107            .execute(conn)
1108            .unwrap();
1109        crate::sql_query("UPDATE kv SET val = '2' WHERE key = 'a'")
1110            .execute(conn)
1111            .unwrap();
1112        crate::sql_query("DELETE FROM kv WHERE key = 'a'")
1113            .execute(conn)
1114            .unwrap();
1115
1116        assert!(
1117            events.lock().unwrap().is_empty(),
1118            "update hook must not fire for WITHOUT ROWID tables"
1119        );
1120    }
1121
1122    /// When a UNIQUE constraint conflict is resolved via ON CONFLICT REPLACE,
1123    /// the implicit deletion of the conflicting row does NOT fire the update
1124    /// hook. Only the INSERT for the new row fires.
1125    /// See: https://www.sqlite.org/c3ref/update_hook.html
1126    #[diesel_test_helper::test]
1127    fn update_hook_silent_for_on_conflict_replace_deletion() {
1128        use std::sync::{Arc, Mutex};
1129        let conn = &mut connection();
1130
1131        crate::sql_query("CREATE TABLE uq (id INTEGER PRIMARY KEY, val TEXT NOT NULL UNIQUE)")
1132            .execute(conn)
1133            .unwrap();
1134
1135        crate::sql_query("INSERT INTO uq (id, val) VALUES (1, 'original')")
1136            .execute(conn)
1137            .unwrap();
1138
1139        let events: Arc<Mutex<Vec<(SqliteChangeOp, i64)>>> = Arc::new(Mutex::new(Vec::new()));
1140        let e2 = events.clone();
1141
1142        conn.on_update(
1143            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1144                if ev.table_name == "uq" {
1145                    e2.lock().unwrap().push((ev.op, ev.rowid));
1146                }
1147            }),
1148        );
1149
1150        // INSERT OR REPLACE with a conflicting val: the old row (id=1) is
1151        // silently deleted by SQLite and the new row (id=2) is inserted.
1152        // The hook fires only for the INSERT of the new row.
1153        crate::sql_query("INSERT OR REPLACE INTO uq (id, val) VALUES (2, 'original')")
1154            .execute(conn)
1155            .unwrap();
1156
1157        let recorded = events.lock().unwrap();
1158        assert_eq!(
1159            recorded.len(),
1160            1,
1161            "expected only 1 event (INSERT), got: {:?}",
1162            *recorded
1163        );
1164        assert_eq!(recorded[0].0, SqliteChangeOp::Insert);
1165        assert_eq!(recorded[0].1, 2, "new row should have rowid 2");
1166    }
1167
1168    /// DELETE FROM without a WHERE clause triggers the truncate optimization,
1169    /// which bypasses the update hook entirely: no per-row DELETE events fire.
1170    /// See: https://www.sqlite.org/lang_delete.html#truncateopt
1171    #[diesel_test_helper::test]
1172    fn update_hook_silent_for_truncate_optimization() {
1173        use std::sync::{Arc, Mutex};
1174        let conn = &mut connection();
1175
1176        // The truncate optimization applies when:
1177        //   1. No WHERE clause
1178        //   2. No RETURNING clause
1179        //   3. No triggers on the table
1180        crate::sql_query("CREATE TABLE bulk (id INTEGER PRIMARY KEY, data TEXT NOT NULL)")
1181            .execute(conn)
1182            .unwrap();
1183
1184        crate::sql_query("INSERT INTO bulk (data) VALUES ('a'), ('b'), ('c')")
1185            .execute(conn)
1186            .unwrap();
1187
1188        let events: Arc<Mutex<Vec<SqliteChangeOp>>> = Arc::new(Mutex::new(Vec::new()));
1189        let e2 = events.clone();
1190
1191        conn.on_update(
1192            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1193                if ev.table_name == "bulk" {
1194                    e2.lock().unwrap().push(ev.op);
1195                }
1196            }),
1197        );
1198
1199        // DELETE without WHERE: truncate optimization kicks in.
1200        crate::sql_query("DELETE FROM bulk").execute(conn).unwrap();
1201
1202        assert!(
1203            events.lock().unwrap().is_empty(),
1204            "truncate optimization should bypass the update hook"
1205        );
1206    }
1207
1208    /// When a table has triggers, the truncate optimization is disabled, so
1209    /// DELETE without WHERE fires per-row DELETE events as normal.
1210    #[diesel_test_helper::test]
1211    fn update_hook_fires_for_delete_all_when_triggers_disable_truncate() {
1212        use std::sync::{Arc, Mutex};
1213        let conn = &mut connection();
1214
1215        crate::sql_query("CREATE TABLE triggered (id INTEGER PRIMARY KEY, data TEXT NOT NULL)")
1216            .execute(conn)
1217            .unwrap();
1218        // A no-op trigger is enough to disable the truncate optimization.
1219        crate::sql_query(
1220            "CREATE TRIGGER trg_triggered BEFORE DELETE ON triggered \
1221             BEGIN SELECT 1; END",
1222        )
1223        .execute(conn)
1224        .unwrap();
1225
1226        crate::sql_query("INSERT INTO triggered (data) VALUES ('x'), ('y'), ('z')")
1227            .execute(conn)
1228            .unwrap();
1229
1230        let deletes: Arc<Mutex<Vec<i64>>> = Arc::new(Mutex::new(Vec::new()));
1231        let d2 = deletes.clone();
1232
1233        conn.on_update(
1234            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1235                if ev.table_name == "triggered" && ev.op == SqliteChangeOp::Delete {
1236                    d2.lock().unwrap().push(ev.rowid);
1237                }
1238            }),
1239        );
1240
1241        // DELETE without WHERE, but triggers exist ⇒ no truncate optimization.
1242        crate::sql_query("DELETE FROM triggered")
1243            .execute(conn)
1244            .unwrap();
1245
1246        assert_eq!(
1247            deletes.lock().unwrap().len(),
1248            3,
1249            "with triggers present, DELETE without WHERE fires per-row hooks"
1250        );
1251    }
1252
1253    /// Modifications to internal system tables like sqlite_sequence
1254    /// (used by AUTOINCREMENT) do not trigger the update hook.
1255    /// See: https://www.sqlite.org/c3ref/update_hook.html
1256    #[diesel_test_helper::test]
1257    fn update_hook_silent_for_internal_sqlite_sequence() {
1258        use std::sync::{Arc, Mutex};
1259        let conn = &mut connection();
1260
1261        // AUTOINCREMENT causes SQLite to maintain sqlite_sequence.
1262        crate::sql_query(
1263            "CREATE TABLE seq_test (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
1264        )
1265        .execute(conn)
1266        .unwrap();
1267
1268        let tables: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(Vec::new()));
1269        let t2 = tables.clone();
1270
1271        conn.on_update(
1272            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1273                t2.lock().unwrap().push(ev.table_name.to_owned());
1274            }),
1275        );
1276
1277        crate::sql_query("INSERT INTO seq_test (name) VALUES ('row1')")
1278            .execute(conn)
1279            .unwrap();
1280
1281        let recorded = tables.lock().unwrap();
1282        // Only the user table should appear, sqlite_sequence must be absent.
1283        assert!(
1284            recorded.iter().all(|t| t == "seq_test"),
1285            "expected only 'seq_test' events, got: {:?}",
1286            *recorded
1287        );
1288        assert!(
1289            !recorded.iter().any(|t| t == "sqlite_sequence"),
1290            "sqlite_sequence modifications must not trigger the update hook"
1291        );
1292    }
1293
1294    /// INSERT OR REPLACE on the primary key itself: when a row with the same
1295    /// PK already exists, the old row is silently deleted and the new row is
1296    /// inserted. The hook reports only the INSERT, not the implicit DELETE.
1297    #[diesel_test_helper::test]
1298    fn update_hook_silent_for_replace_into_on_pk_conflict() {
1299        use std::sync::{Arc, Mutex};
1300        let conn = &mut connection();
1301
1302        crate::sql_query("CREATE TABLE rep (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
1303            .execute(conn)
1304            .unwrap();
1305
1306        crate::sql_query("INSERT INTO rep (id, val) VALUES (1, 'old')")
1307            .execute(conn)
1308            .unwrap();
1309
1310        let events: Arc<Mutex<Vec<(SqliteChangeOp, i64)>>> = Arc::new(Mutex::new(Vec::new()));
1311        let e2 = events.clone();
1312
1313        conn.on_update(
1314            SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1315                if ev.table_name == "rep" {
1316                    e2.lock().unwrap().push((ev.op, ev.rowid));
1317                }
1318            }),
1319        );
1320
1321        // REPLACE INTO with a conflicting PK.
1322        crate::sql_query("REPLACE INTO rep (id, val) VALUES (1, 'new')")
1323            .execute(conn)
1324            .unwrap();
1325
1326        let recorded = events.lock().unwrap();
1327        // Only one INSERT event, no DELETE for the old row.
1328        assert_eq!(
1329            recorded.len(),
1330            1,
1331            "expected 1 event for REPLACE INTO, got: {:?}",
1332            *recorded
1333        );
1334        assert_eq!(recorded[0].0, SqliteChangeOp::Insert);
1335        assert_eq!(recorded[0].1, 1);
1336    }
1337
1338    /// Regression test for the dangling-pointer soundness bug: after the
1339    /// connection is moved, a write still fires the registered callback.
1340    ///
1341    /// `sqlite3_update_hook` is handed the address of the connection's update
1342    /// hook state at registration time. Because that state is boxed, moving the
1343    /// (freely movable, `Sized`) `SqliteConnection` does not relocate it, so the
1344    /// pointer SQLite holds stays valid. On a buggy inline implementation the
1345    /// move would relocate the state and the trampoline would later dereference
1346    /// freed memory. This documents that the feature works through the move that
1347    /// real code performs (returning a connection, storing it in a struct,
1348    /// handing it to a pool, etc.).
1349    #[diesel_test_helper::test]
1350    fn change_hook_fires_after_connection_move() {
1351        use std::sync::{Arc, Mutex};
1352
1353        let count = Arc::new(Mutex::new(0u32));
1354        let count2 = count.clone();
1355
1356        let mut conn = connection();
1357        setup_hook_tables(&mut conn);
1358        conn.on_update(SqliteUpdateRouter::new().on(
1359            hook_users::table,
1360            SqliteChangeOps::INSERT,
1361            move |_| {
1362                *count2.lock().unwrap() += 1;
1363            },
1364        ));
1365
1366        // Move the connection onto the heap after the hook was registered.
1367        let mut boxed = Box::new(conn);
1368
1369        crate::sql_query("INSERT INTO hook_users (name) VALUES ('Alice')")
1370            .execute(&mut *boxed)
1371            .unwrap();
1372
1373        assert_eq!(
1374            *count.lock().unwrap(),
1375            1,
1376            "change hook did not fire after the connection was moved"
1377        );
1378    }
1379
1380    #[diesel_test_helper::test]
1381    fn router_filters_table_and_op() {
1382        use std::sync::{Arc, Mutex};
1383        let conn = &mut connection();
1384        setup_hook_tables(conn);
1385
1386        let fired = Arc::new(Mutex::new(Vec::new()));
1387        let fired2 = fired.clone();
1388
1389        // Fire for INSERT and UPDATE on hook_users only, not DELETE.
1390        conn.on_update(SqliteUpdateRouter::new().on(
1391            hook_users::table,
1392            SqliteChangeOps::INSERT | SqliteChangeOps::UPDATE,
1393            move |event| fired2.lock().unwrap().push(event.op),
1394        ));
1395
1396        crate::sql_query("INSERT INTO hook_users (name) VALUES ('Alice')")
1397            .execute(conn)
1398            .unwrap();
1399        crate::sql_query("UPDATE hook_users SET name = 'Bob' WHERE id = 1")
1400            .execute(conn)
1401            .unwrap();
1402        crate::sql_query("DELETE FROM hook_users WHERE id = 1")
1403            .execute(conn)
1404            .unwrap();
1405        // A change on a different table must not fire this hook.
1406        crate::sql_query("INSERT INTO hook_posts (title) VALUES ('Hello')")
1407            .execute(conn)
1408            .unwrap();
1409
1410        let events = fired.lock().unwrap().clone();
1411        assert_eq!(events, vec![SqliteChangeOp::Insert, SqliteChangeOp::Update]);
1412    }
1413}