1#[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 {
#[inline]
pub const fn empty() -> Self {
Self(<u8 as ::bitflags::Bits>::EMPTY)
}
#[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
}
#[inline]
pub const fn bits(&self) -> u8 { self.0 }
#[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 }
}
#[inline]
pub const fn from_bits_truncate(bits: u8) -> Self {
Self(bits & Self::all().0)
}
#[inline]
pub const fn from_bits_retain(bits: u8) -> Self { Self(bits) }
#[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
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.0 == <u8 as ::bitflags::Bits>::EMPTY
}
#[inline]
pub const fn is_all(&self) -> bool {
Self::all().0 | self.0 == self.0
}
#[inline]
pub const fn intersects(&self, other: Self) -> bool {
self.0 & other.0 != <u8 as ::bitflags::Bits>::EMPTY
}
#[inline]
pub const fn contains(&self, other: Self) -> bool {
self.0 & other.0 == other.0
}
#[inline]
pub fn insert(&mut self, other: Self) {
*self = Self(self.0).union(other);
}
#[inline]
pub fn remove(&mut self, other: Self) {
*self = Self(self.0).difference(other);
}
#[inline]
pub fn toggle(&mut self, other: Self) {
*self = Self(self.0).symmetric_difference(other);
}
#[inline]
pub fn set(&mut self, other: Self, value: bool) {
if value { self.insert(other); } else { self.remove(other); }
}
#[inline]
#[must_use]
pub const fn intersection(self, other: Self) -> Self {
Self(self.0 & other.0)
}
#[inline]
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[inline]
#[must_use]
pub const fn difference(self, other: Self) -> Self {
Self(self.0 & !other.0)
}
#[inline]
#[must_use]
pub const fn symmetric_difference(self, other: Self) -> Self {
Self(self.0 ^ other.0)
}
#[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;
#[inline]
fn bitor(self, other: InternalBitFlags) -> Self {
self.union(other)
}
}
impl ::bitflags::__private::core::ops::BitOrAssign for
InternalBitFlags {
#[inline]
fn bitor_assign(&mut self, other: Self) { self.insert(other); }
}
impl ::bitflags::__private::core::ops::BitXor for InternalBitFlags {
type Output = Self;
#[inline]
fn bitxor(self, other: Self) -> Self {
self.symmetric_difference(other)
}
}
impl ::bitflags::__private::core::ops::BitXorAssign for
InternalBitFlags {
#[inline]
fn bitxor_assign(&mut self, other: Self) { self.toggle(other); }
}
impl ::bitflags::__private::core::ops::BitAnd for InternalBitFlags {
type Output = Self;
#[inline]
fn bitand(self, other: Self) -> Self { self.intersection(other) }
}
impl ::bitflags::__private::core::ops::BitAndAssign for
InternalBitFlags {
#[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;
#[inline]
fn sub(self, other: Self) -> Self { self.difference(other) }
}
impl ::bitflags::__private::core::ops::SubAssign for InternalBitFlags
{
#[inline]
fn sub_assign(&mut self, other: Self) { self.remove(other); }
}
impl ::bitflags::__private::core::ops::Not for InternalBitFlags {
type Output = Self;
#[inline]
fn not(self) -> Self { self.complement() }
}
impl ::bitflags::__private::core::iter::Extend<InternalBitFlags> for
InternalBitFlags {
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 {
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 {
#[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()))
}
#[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 {
#[inline]
pub fn bits_mut(&mut self) -> &mut u8 { &mut self.0 }
}
impl SqliteChangeOps {
#[inline]
pub const fn empty() -> Self { Self(InternalBitFlags::empty()) }
#[inline]
pub const fn all() -> Self { Self(InternalBitFlags::all()) }
#[inline]
pub const fn bits(&self) -> u8 { self.0.bits() }
#[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,
}
}
#[inline]
pub const fn from_bits_truncate(bits: u8) -> Self {
Self(InternalBitFlags::from_bits_truncate(bits))
}
#[inline]
pub const fn from_bits_retain(bits: u8) -> Self {
Self(InternalBitFlags::from_bits_retain(bits))
}
#[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,
}
}
#[inline]
pub const fn is_empty(&self) -> bool { self.0.is_empty() }
#[inline]
pub const fn is_all(&self) -> bool { self.0.is_all() }
#[inline]
pub const fn intersects(&self, other: Self) -> bool {
self.0.intersects(other.0)
}
#[inline]
pub const fn contains(&self, other: Self) -> bool {
self.0.contains(other.0)
}
#[inline]
pub fn insert(&mut self, other: Self) { self.0.insert(other.0) }
#[inline]
pub fn remove(&mut self, other: Self) { self.0.remove(other.0) }
#[inline]
pub fn toggle(&mut self, other: Self) { self.0.toggle(other.0) }
#[inline]
pub fn set(&mut self, other: Self, value: bool) {
self.0.set(other.0, value)
}
#[inline]
#[must_use]
pub const fn intersection(self, other: Self) -> Self {
Self(self.0.intersection(other.0))
}
#[inline]
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0.union(other.0))
}
#[inline]
#[must_use]
pub const fn difference(self, other: Self) -> Self {
Self(self.0.difference(other.0))
}
#[inline]
#[must_use]
pub const fn symmetric_difference(self, other: Self) -> Self {
Self(self.0.symmetric_difference(other.0))
}
#[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;
#[inline]
fn bitor(self, other: SqliteChangeOps) -> Self {
self.union(other)
}
}
impl ::bitflags::__private::core::ops::BitOrAssign for SqliteChangeOps
{
#[inline]
fn bitor_assign(&mut self, other: Self) { self.insert(other); }
}
impl ::bitflags::__private::core::ops::BitXor for SqliteChangeOps {
type Output = Self;
#[inline]
fn bitxor(self, other: Self) -> Self {
self.symmetric_difference(other)
}
}
impl ::bitflags::__private::core::ops::BitXorAssign for
SqliteChangeOps {
#[inline]
fn bitxor_assign(&mut self, other: Self) { self.toggle(other); }
}
impl ::bitflags::__private::core::ops::BitAnd for SqliteChangeOps {
type Output = Self;
#[inline]
fn bitand(self, other: Self) -> Self { self.intersection(other) }
}
impl ::bitflags::__private::core::ops::BitAndAssign for
SqliteChangeOps {
#[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;
#[inline]
fn sub(self, other: Self) -> Self { self.difference(other) }
}
impl ::bitflags::__private::core::ops::SubAssign for SqliteChangeOps {
#[inline]
fn sub_assign(&mut self, other: Self) { self.remove(other); }
}
impl ::bitflags::__private::core::ops::Not for SqliteChangeOps {
type Output = Self;
#[inline]
fn not(self) -> Self { self.complement() }
}
impl ::bitflags::__private::core::iter::Extend<SqliteChangeOps> for
SqliteChangeOps {
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 {
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 {
#[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()))
}
#[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 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30 pub struct SqliteChangeOps: u8 {
31 const INSERT = 1;
33 const UPDATE = 2;
35 const DELETE = 4;
37 const UNKNOWN = 8;
39 const ALL =
46 Self::INSERT.bits() | Self::UPDATE.bits() | Self::DELETE.bits() | Self::UNKNOWN.bits();
47 }
48}
49
50impl SqliteChangeOps {
51 pub(crate) fn matches_op(self, op: SqliteChangeOp) -> bool {
53 self.contains(op.to_ops())
54 }
55}
56
57#[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 Insert,
66 Update,
68 Delete,
70 Unknown(i32),
73}
74
75impl SqliteChangeOp {
76 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 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#[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 pub op: SqliteChangeOp,
107 pub db_name: &'a str,
111 pub table_name: &'a str,
113 pub rowid: i64,
117}
118
119impl SqliteChangeEvent<'_> {
120 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 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
158trait 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#[allow(missing_debug_implementations)]
244pub struct SqliteUpdateRouter {
245 routes: Vec<Route>,
246}
247
248impl SqliteUpdateRouter {
249 pub fn new() -> Self {
251 SqliteUpdateRouter { routes: Vec::new() }
252 }
253
254 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 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 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 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 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 pub fn on_update(&mut self, router: SqliteUpdateRouter) {
386 self.raw_connection.set_update_hook(router.into_hook());
387 }
388
389 pub fn remove_update_hook(&mut self) {
394 self.raw_connection.remove_update_hook();
395 }
396}
397
398#[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 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 let a = event;
501 let b = event;
502 assert_eq!(a.rowid, b.rowid);
503 }
504
505 #[test]
506 fn debug_formatting() {
507 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 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 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)); router.dispatch(make_event(SqliteChangeOp::Delete, "users", 3)); 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 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 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 #[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 crate::sql_query("INSERT INTO main.shared_items (id) VALUES (1)")
669 .execute(conn)
670 .unwrap();
671 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 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); }
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 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 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 conn.remove_update_hook();
794
795 crate::sql_query("INSERT INTO hook_users (name) VALUES ('B')")
796 .execute(conn)
797 .unwrap();
798 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 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 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 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 crate::sql_query("INSERT INTO hook_users (name) VALUES ('X')")
984 .execute(conn)
985 .unwrap();
986 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 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 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 #[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 #[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 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 #[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 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 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 #[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 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 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 #[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 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 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 #[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 crate::sql_query("REPLACE INTO rep (id, val) VALUES (1, 'new')")
1323 .execute(conn)
1324 .unwrap();
1325
1326 let recorded = events.lock().unwrap();
1327 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 #[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 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 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 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}