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)]
237pub struct SqliteUpdateRouter {
238 routes: Vec<Route>,
239}
240
241impl SqliteUpdateRouter {
242 pub fn new() -> Self {
244 SqliteUpdateRouter { routes: Vec::new() }
245 }
246
247 pub fn on<T, F>(mut self, table: T, ops: SqliteChangeOps, callback: F) -> Self
256 where
257 T: NamedTable + Send + 'static,
258 F: FnMut(SqliteChangeEvent<'_>) + Send + 'static,
259 {
260 let _ = table;
263 self.routes.push(Route {
264 table: Some(Box::new(table)),
265 ops,
266 callback: Box::new(callback),
267 });
268 self
269 }
270
271 pub fn on_any<F>(mut self, ops: SqliteChangeOps, callback: F) -> Self
273 where
274 F: FnMut(SqliteChangeEvent<'_>) + Send + 'static,
275 {
276 self.routes.push(Route {
277 table: None,
278 ops,
279 callback: Box::new(callback),
280 });
281 self
282 }
283
284 fn dispatch(&mut self, event: SqliteChangeEvent<'_>) {
286 for route in &mut self.routes {
287 if route.matches(&event) {
288 (route.callback)(event);
289 }
290 }
291 }
292
293 pub(crate) fn into_hook(mut self) -> impl FnMut(SqliteChangeEvent<'_>) + Send {
296 move |event| self.dispatch(event)
297 }
298}
299
300impl Default for SqliteUpdateRouter {
301 fn default() -> Self {
302 SqliteUpdateRouter::new()
303 }
304}
305
306impl SqliteConnection {
307 pub fn on_update(&mut self, router: SqliteUpdateRouter) {
372 self.raw_connection.set_update_hook(router.into_hook());
373 }
374
375 pub fn remove_update_hook(&mut self) {
380 self.raw_connection.remove_update_hook();
381 }
382}
383
384#[cfg(all(test, not(miri)))]
386mod tests {
387 use super::*;
388 use crate::connection::Connection;
389 use crate::prelude::*;
390 use crate::query_dsl::RunQueryDsl;
391
392 impl SqliteChangeOps {
394 fn matches(self, op_code: i32) -> bool {
395 self.matches_op(SqliteChangeOp::from_ffi(op_code))
396 }
397 }
398
399 #[test]
400 fn insert_or_delete_matches_both_but_not_update() {
401 let mask = SqliteChangeOps::INSERT | SqliteChangeOps::DELETE;
402 assert!(mask.matches(ffi::SQLITE_INSERT));
403 assert!(mask.matches(ffi::SQLITE_DELETE));
404 assert!(!mask.matches(ffi::SQLITE_UPDATE));
405 }
406
407 #[test]
408 fn all_matches_all_three() {
409 assert!(SqliteChangeOps::ALL.matches(ffi::SQLITE_INSERT));
410 assert!(SqliteChangeOps::ALL.matches(ffi::SQLITE_UPDATE));
411 assert!(SqliteChangeOps::ALL.matches(ffi::SQLITE_DELETE));
412 }
413
414 #[test]
415 fn combining_identical_masks_is_idempotent() {
416 assert_eq!(
417 SqliteChangeOps::INSERT | SqliteChangeOps::INSERT,
418 SqliteChangeOps::INSERT,
419 );
420 }
421
422 #[test]
423 fn contains_single() {
424 assert!(SqliteChangeOps::INSERT.contains(SqliteChangeOps::INSERT));
425 }
426
427 #[test]
428 fn all_contains_insert_or_delete() {
429 assert!(SqliteChangeOps::ALL.contains(SqliteChangeOps::INSERT | SqliteChangeOps::DELETE));
430 }
431
432 #[test]
433 fn insert_does_not_contain_all() {
434 assert!(!SqliteChangeOps::INSERT.contains(SqliteChangeOps::ALL));
435 }
436
437 #[test]
438 fn from_ffi_insert() {
439 assert_eq!(
440 SqliteChangeOp::from_ffi(ffi::SQLITE_INSERT),
441 SqliteChangeOp::Insert
442 );
443 }
444
445 #[test]
446 fn from_ffi_update() {
447 assert_eq!(
448 SqliteChangeOp::from_ffi(ffi::SQLITE_UPDATE),
449 SqliteChangeOp::Update
450 );
451 }
452
453 #[test]
454 fn from_ffi_delete() {
455 assert_eq!(
456 SqliteChangeOp::from_ffi(ffi::SQLITE_DELETE),
457 SqliteChangeOp::Delete
458 );
459 }
460
461 #[test]
462 fn to_ops_roundtrip() {
463 assert_eq!(SqliteChangeOp::Insert.to_ops(), SqliteChangeOps::INSERT);
464 assert_eq!(SqliteChangeOp::Update.to_ops(), SqliteChangeOps::UPDATE);
465 assert_eq!(SqliteChangeOp::Delete.to_ops(), SqliteChangeOps::DELETE);
466 assert_eq!(
467 SqliteChangeOp::Unknown(999).to_ops(),
468 SqliteChangeOps::UNKNOWN
469 );
470 }
471
472 #[test]
473 fn from_ffi_unknown_code() {
474 assert_eq!(SqliteChangeOp::from_ffi(999), SqliteChangeOp::Unknown(999));
475 }
476
477 #[test]
478 fn sqlite_change_event_is_copy() {
479 let event = SqliteChangeEvent {
480 op: SqliteChangeOp::Delete,
481 db_name: "main",
482 table_name: "posts",
483 rowid: 7,
484 };
485 let a = event;
487 let b = event;
488 assert_eq!(a.rowid, b.rowid);
489 }
490
491 #[test]
492 fn debug_formatting() {
493 assert!(format!("{:?}", SqliteChangeOps::INSERT).contains("INSERT"));
497 assert!(format!("{:?}", SqliteChangeOps::DELETE).contains("DELETE"));
498 let _ = format!("{:?}", SqliteChangeOps::empty());
499 let _ = format!("{:?}", SqliteChangeOps::ALL);
500 }
501
502 #[test]
503 fn bitand_works() {
504 let mask = SqliteChangeOps::ALL & SqliteChangeOps::INSERT;
505 assert_eq!(mask, SqliteChangeOps::INSERT);
506 }
507
508 fn make_event(
514 op: SqliteChangeOp,
515 table: &'static str,
516 rowid: i64,
517 ) -> SqliteChangeEvent<'static> {
518 SqliteChangeEvent {
519 op,
520 db_name: "main",
521 table_name: table,
522 rowid,
523 }
524 }
525
526 #[test]
527 fn empty_router_dispatches_nothing() {
528 let mut router = SqliteUpdateRouter::new();
529 router.dispatch(make_event(SqliteChangeOp::Insert, "users", 1));
531 }
532
533 #[test]
534 fn on_any_dispatches_for_every_table() {
535 let fired = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
536 let f2 = fired.clone();
537 let mut router = SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |e| {
538 f2.lock().unwrap().push((e.op, e.rowid));
539 });
540
541 router.dispatch(make_event(SqliteChangeOp::Insert, "users", 1));
542 router.dispatch(make_event(SqliteChangeOp::Delete, "posts", 2));
543
544 assert_eq!(
545 *fired.lock().unwrap(),
546 vec![(SqliteChangeOp::Insert, 1), (SqliteChangeOp::Delete, 2)],
547 );
548 }
549
550 #[test]
551 fn router_filters_by_op_mask() {
552 let fired = std::sync::Arc::new(std::sync::Mutex::new(Vec::new()));
553 let f2 = fired.clone();
554 let mut router = SqliteUpdateRouter::new().on_any(SqliteChangeOps::INSERT, move |e| {
555 f2.lock().unwrap().push(e.rowid);
556 });
557
558 router.dispatch(make_event(SqliteChangeOp::Insert, "users", 1));
559 router.dispatch(make_event(SqliteChangeOp::Update, "users", 2)); router.dispatch(make_event(SqliteChangeOp::Delete, "users", 3)); assert_eq!(*fired.lock().unwrap(), vec![1]);
563 }
564
565 #[test]
566 fn every_matching_route_fires() {
567 let count = std::sync::Arc::new(std::sync::Mutex::new(0u32));
568 let c1 = count.clone();
569 let c2 = count.clone();
570 let mut router = SqliteUpdateRouter::new()
571 .on_any(SqliteChangeOps::ALL, move |_| {
572 *c1.lock().unwrap() += 1;
573 })
574 .on_any(SqliteChangeOps::INSERT, move |_| {
575 *c2.lock().unwrap() += 1;
576 });
577
578 router.dispatch(make_event(SqliteChangeOp::Insert, "users", 1));
580 router.dispatch(make_event(SqliteChangeOp::Delete, "users", 2));
581
582 assert_eq!(*count.lock().unwrap(), 3);
583 }
584
585 fn connection() -> SqliteConnection {
586 SqliteConnection::establish(":memory:").unwrap()
587 }
588
589 table! {
594 hook_users {
595 id -> Integer,
596 name -> Text,
597 }
598 }
599
600 table! {
601 hook_posts {
602 id -> Integer,
603 title -> Text,
604 }
605 }
606
607 fn setup_hook_tables(conn: &mut SqliteConnection) {
608 crate::sql_query(
609 "CREATE TABLE hook_users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
610 )
611 .execute(conn)
612 .unwrap();
613 crate::sql_query(
614 "CREATE TABLE hook_posts (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL)",
615 )
616 .execute(conn)
617 .unwrap();
618 }
619
620 #[diesel_test_helper::test]
623 fn router_on_matches_schema_qualified_table() {
624 use std::sync::{Arc, Mutex};
625
626 table! {
627 attached.shared_items (id) {
628 id -> Integer,
629 }
630 }
631
632 let conn = &mut connection();
633 crate::sql_query("ATTACH DATABASE ':memory:' AS attached")
634 .execute(conn)
635 .unwrap();
636 crate::sql_query("CREATE TABLE shared_items (id INTEGER PRIMARY KEY)")
637 .execute(conn)
638 .unwrap();
639 crate::sql_query("CREATE TABLE attached.shared_items (id INTEGER PRIMARY KEY)")
640 .execute(conn)
641 .unwrap();
642
643 let fired = Arc::new(Mutex::new(Vec::new()));
644 let f2 = fired.clone();
645 conn.on_update(SqliteUpdateRouter::new().on(
646 shared_items::table,
647 SqliteChangeOps::ALL,
648 move |ev| {
649 f2.lock().unwrap().push((ev.db_name.to_owned(), ev.rowid));
650 },
651 ));
652
653 crate::sql_query("INSERT INTO main.shared_items (id) VALUES (1)")
655 .execute(conn)
656 .unwrap();
657 crate::sql_query("INSERT INTO attached.shared_items (id) VALUES (2)")
659 .execute(conn)
660 .unwrap();
661
662 assert_eq!(
663 *fired.lock().unwrap(),
664 vec![("attached".to_owned(), 2)],
665 "a schema-qualified route matches only its attached database"
666 );
667 }
668
669 #[diesel_test_helper::test]
670 fn router_on_dispatches_to_typed_table() {
671 use std::sync::{Arc, Mutex};
672 let conn = &mut connection();
673 setup_hook_tables(conn);
674
675 let fired = Arc::new(Mutex::new(Vec::new()));
676 let fired2 = fired.clone();
677
678 conn.on_update(SqliteUpdateRouter::new().on(
679 hook_users::table,
680 SqliteChangeOps::INSERT,
681 move |change| {
682 fired2.lock().unwrap().push((change.op, change.rowid));
683 },
684 ));
685
686 crate::sql_query("INSERT INTO hook_users (name) VALUES ('Alice')")
688 .execute(conn)
689 .unwrap();
690
691 let events = fired.lock().unwrap().clone();
692 assert_eq!(events.len(), 1);
693 assert_eq!(events[0].0, SqliteChangeOp::Insert);
694 assert_eq!(events[0].1, 1); }
696
697 #[diesel_test_helper::test]
698 fn on_delete_fires_only_for_delete() {
699 use std::sync::{Arc, Mutex};
700 let conn = &mut connection();
701 setup_hook_tables(conn);
702
703 let fired = Arc::new(Mutex::new(Vec::new()));
704 let fired2 = fired.clone();
705
706 conn.on_update(SqliteUpdateRouter::new().on(
707 hook_users::table,
708 SqliteChangeOps::DELETE,
709 move |change| {
710 fired2.lock().unwrap().push(change.op);
711 },
712 ));
713
714 crate::sql_query("INSERT INTO hook_users (name) VALUES ('Alice')")
716 .execute(conn)
717 .unwrap();
718 crate::sql_query("UPDATE hook_users SET name = 'Bob' WHERE id = 1")
719 .execute(conn)
720 .unwrap();
721 crate::sql_query("DELETE FROM hook_users WHERE id = 1")
722 .execute(conn)
723 .unwrap();
724
725 let events = fired.lock().unwrap().clone();
726 assert_eq!(events.len(), 1);
728 assert_eq!(events[0], SqliteChangeOp::Delete);
729 }
730
731 #[diesel_test_helper::test]
732 fn every_matching_route_fires_in_order() {
733 use std::sync::{Arc, Mutex};
734 let conn = &mut connection();
735 setup_hook_tables(conn);
736
737 let order = Arc::new(Mutex::new(Vec::new()));
738 let o1 = order.clone();
739 let o2 = order.clone();
740
741 conn.on_update(
742 SqliteUpdateRouter::new()
743 .on(hook_users::table, SqliteChangeOps::INSERT, move |_| {
744 o1.lock().unwrap().push(1);
745 })
746 .on(hook_users::table, SqliteChangeOps::INSERT, move |_| {
747 o2.lock().unwrap().push(2);
748 }),
749 );
750
751 crate::sql_query("INSERT INTO hook_users (name) VALUES ('X')")
752 .execute(conn)
753 .unwrap();
754
755 assert_eq!(*order.lock().unwrap(), vec![1, 2]);
756 }
757
758 #[diesel_test_helper::test]
759 fn remove_update_stops_dispatch() {
760 use std::sync::{Arc, Mutex};
761 let conn = &mut connection();
762 setup_hook_tables(conn);
763
764 let fired = Arc::new(Mutex::new(0u32));
765 let f2 = fired.clone();
766
767 conn.on_update(
768 SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |_| {
769 *f2.lock().unwrap() += 1;
770 }),
771 );
772
773 crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
774 .execute(conn)
775 .unwrap();
776 assert_eq!(*fired.lock().unwrap(), 1);
777
778 conn.remove_update_hook();
780
781 crate::sql_query("INSERT INTO hook_users (name) VALUES ('B')")
782 .execute(conn)
783 .unwrap();
784 assert_eq!(*fired.lock().unwrap(), 1);
786 }
787
788 #[diesel_test_helper::test]
789 fn events_fire_immediately_during_statement() {
790 use std::sync::{Arc, Mutex};
791 let conn = &mut connection();
792 setup_hook_tables(conn);
793
794 crate::sql_query("INSERT INTO hook_users (name) VALUES ('Z')")
796 .execute(conn)
797 .unwrap();
798
799 let fired = Arc::new(Mutex::new(Vec::new()));
800 let f2 = fired.clone();
801
802 conn.on_update(SqliteUpdateRouter::new().on(
803 hook_users::table,
804 SqliteChangeOps::UPDATE,
805 move |event| {
806 f2.lock().unwrap().push(event.rowid);
807 },
808 ));
809
810 crate::sql_query("UPDATE hook_users SET name = 'W' WHERE id = 1")
812 .execute(conn)
813 .unwrap();
814
815 assert_eq!(*fired.lock().unwrap(), vec![1i64]);
816 }
817
818 #[diesel_test_helper::test]
819 fn on_update_fires_for_update_only() {
820 use std::sync::{Arc, Mutex};
821 let conn = &mut connection();
822 setup_hook_tables(conn);
823
824 let count = Arc::new(Mutex::new(0u32));
825 let c2 = count.clone();
826
827 conn.on_update(SqliteUpdateRouter::new().on(
828 hook_users::table,
829 SqliteChangeOps::UPDATE,
830 move |event| {
831 assert_eq!(event.op, SqliteChangeOp::Update);
832 *c2.lock().unwrap() += 1;
833 },
834 ));
835
836 crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
837 .execute(conn)
838 .unwrap();
839 crate::sql_query("UPDATE hook_users SET name = 'B' WHERE id = 1")
840 .execute(conn)
841 .unwrap();
842 crate::sql_query("DELETE FROM hook_users WHERE id = 1")
843 .execute(conn)
844 .unwrap();
845
846 assert_eq!(*count.lock().unwrap(), 1);
847 }
848
849 #[diesel_test_helper::test]
850 fn on_update_receives_every_change() {
851 use std::sync::{Arc, Mutex};
852 let conn = &mut connection();
853 setup_hook_tables(conn);
854
855 let events = Arc::new(Mutex::new(Vec::new()));
856 let e2 = events.clone();
857
858 conn.on_update(
859 SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
860 e2.lock().unwrap().push((ev.op, ev.table_name.to_owned()));
861 }),
862 );
863
864 crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
865 .execute(conn)
866 .unwrap();
867 crate::sql_query("INSERT INTO hook_posts (title) VALUES ('P')")
868 .execute(conn)
869 .unwrap();
870 crate::sql_query("UPDATE hook_users SET name = 'B' WHERE id = 1")
871 .execute(conn)
872 .unwrap();
873 crate::sql_query("DELETE FROM hook_posts WHERE id = 1")
874 .execute(conn)
875 .unwrap();
876
877 let evts = events.lock().unwrap().clone();
878 assert_eq!(evts.len(), 4);
879 assert_eq!(evts[0], (SqliteChangeOp::Insert, "hook_users".to_owned()));
880 assert_eq!(evts[1], (SqliteChangeOp::Insert, "hook_posts".to_owned()));
881 assert_eq!(evts[2], (SqliteChangeOp::Update, "hook_users".to_owned()));
882 assert_eq!(evts[3], (SqliteChangeOp::Delete, "hook_posts".to_owned()));
883 }
884
885 #[diesel_test_helper::test]
886 fn on_update_filters_by_op_mask() {
887 use std::sync::{Arc, Mutex};
888 let conn = &mut connection();
889 setup_hook_tables(conn);
890
891 let count = Arc::new(Mutex::new(0u32));
892 let c2 = count.clone();
893
894 conn.on_update(SqliteUpdateRouter::new().on_any(
895 SqliteChangeOps::INSERT | SqliteChangeOps::DELETE,
896 move |_| {
897 *c2.lock().unwrap() += 1;
898 },
899 ));
900
901 crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
902 .execute(conn)
903 .unwrap();
904 crate::sql_query("UPDATE hook_users SET name = 'B' WHERE id = 1")
905 .execute(conn)
906 .unwrap();
907 crate::sql_query("DELETE FROM hook_users WHERE id = 1")
908 .execute(conn)
909 .unwrap();
910
911 assert_eq!(*count.lock().unwrap(), 2);
913 }
914
915 #[diesel_test_helper::test]
916 fn router_dispatches_to_multiple_tables() {
917 use std::sync::{Arc, Mutex};
918 let conn = &mut connection();
919 setup_hook_tables(conn);
920
921 let user_count = Arc::new(Mutex::new(0u32));
922 let post_count = Arc::new(Mutex::new(0u32));
923 let uc = user_count.clone();
924 let pc = post_count.clone();
925
926 conn.on_update(
927 SqliteUpdateRouter::new()
928 .on(hook_users::table, SqliteChangeOps::ALL, move |_| {
929 *uc.lock().unwrap() += 1;
930 })
931 .on(hook_posts::table, SqliteChangeOps::ALL, move |_| {
932 *pc.lock().unwrap() += 1;
933 }),
934 );
935
936 crate::sql_query("INSERT INTO hook_users (name) VALUES ('X')")
937 .execute(conn)
938 .unwrap();
939 crate::sql_query("INSERT INTO hook_posts (title) VALUES ('Y')")
940 .execute(conn)
941 .unwrap();
942
943 assert_eq!(*user_count.lock().unwrap(), 1);
944 assert_eq!(*post_count.lock().unwrap(), 1);
945 }
946
947 #[diesel_test_helper::test]
948 fn on_any_audit_plus_specific_route() {
949 use std::sync::{Arc, Mutex};
950 let conn = &mut connection();
951 setup_hook_tables(conn);
952
953 let audit_count = Arc::new(Mutex::new(0u32));
954 let user_insert_count = Arc::new(Mutex::new(0u32));
955 let ac = audit_count.clone();
956 let uic = user_insert_count.clone();
957
958 conn.on_update(
959 SqliteUpdateRouter::new()
960 .on_any(SqliteChangeOps::ALL, move |_| {
961 *ac.lock().unwrap() += 1;
962 })
963 .on(hook_users::table, SqliteChangeOps::INSERT, move |_| {
964 *uic.lock().unwrap() += 1;
965 }),
966 );
967
968 crate::sql_query("INSERT INTO hook_users (name) VALUES ('X')")
970 .execute(conn)
971 .unwrap();
972 crate::sql_query("INSERT INTO hook_posts (title) VALUES ('Y')")
974 .execute(conn)
975 .unwrap();
976
977 assert_eq!(*audit_count.lock().unwrap(), 2);
978 assert_eq!(*user_insert_count.lock().unwrap(), 1);
979 }
980
981 #[diesel_test_helper::test]
982 fn rowid_in_filters_by_table() {
983 use std::sync::{Arc, Mutex};
984 let conn = &mut connection();
985 setup_hook_tables(conn);
986
987 let captured = Arc::new(Mutex::new(Vec::new()));
988 let c2 = captured.clone();
989
990 conn.on_update(
991 SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |change| {
992 if let Some(rowid) = change.rowid_in(hook_users::table) {
993 c2.lock().unwrap().push(rowid);
994 }
995 }),
996 );
997
998 crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
999 .execute(conn)
1000 .unwrap();
1001 crate::sql_query("INSERT INTO hook_posts (title) VALUES ('P')")
1002 .execute(conn)
1003 .unwrap();
1004
1005 assert_eq!(*captured.lock().unwrap(), vec![1i64]);
1007 }
1008
1009 #[diesel_test_helper::test]
1010 fn is_from_matches_table_marker() {
1011 use std::sync::{Arc, Mutex};
1012 let conn = &mut connection();
1013 setup_hook_tables(conn);
1014
1015 let captured = Arc::new(Mutex::new(Vec::new()));
1016 let c2 = captured.clone();
1017
1018 conn.on_update(
1019 SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |change| {
1020 c2.lock().unwrap().push((
1021 change.is_from(hook_users::table),
1022 change.is_from(hook_posts::table),
1023 ));
1024 }),
1025 );
1026
1027 crate::sql_query("INSERT INTO hook_users (name) VALUES ('A')")
1028 .execute(conn)
1029 .unwrap();
1030
1031 assert_eq!(*captured.lock().unwrap(), vec![(true, false)]);
1032 }
1033
1034 #[diesel_test_helper::test]
1035 fn hooks_fire_across_transactions() {
1036 use std::sync::{Arc, Mutex};
1037 let conn = &mut connection();
1038 setup_hook_tables(conn);
1039
1040 let fired = Arc::new(Mutex::new(Vec::new()));
1041 let f2 = fired.clone();
1042
1043 conn.on_update(SqliteUpdateRouter::new().on(
1045 hook_users::table,
1046 SqliteChangeOps::INSERT,
1047 move |event| {
1048 f2.lock().unwrap().push(event.rowid);
1049 },
1050 ));
1051
1052 conn.immediate_transaction(|conn| {
1053 crate::sql_query("INSERT INTO hook_users (name) VALUES ('TxUser')")
1054 .execute(conn)
1055 .unwrap();
1056 Ok::<_, crate::result::Error>(())
1057 })
1058 .unwrap();
1059
1060 assert_eq!(fired.lock().unwrap().len(), 1);
1061 }
1062
1063 #[diesel_test_helper::test]
1073 fn update_hook_silent_for_without_rowid_tables() {
1074 use std::sync::{Arc, Mutex};
1075 let conn = &mut connection();
1076
1077 crate::sql_query("CREATE TABLE kv (key TEXT PRIMARY KEY, val TEXT NOT NULL) WITHOUT ROWID")
1078 .execute(conn)
1079 .unwrap();
1080
1081 let events: Arc<Mutex<Vec<SqliteChangeOp>>> = Arc::new(Mutex::new(Vec::new()));
1082 let e2 = events.clone();
1083
1084 conn.on_update(
1085 SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1086 if ev.table_name == "kv" {
1087 e2.lock().unwrap().push(ev.op);
1088 }
1089 }),
1090 );
1091
1092 crate::sql_query("INSERT INTO kv (key, val) VALUES ('a', '1')")
1093 .execute(conn)
1094 .unwrap();
1095 crate::sql_query("UPDATE kv SET val = '2' WHERE key = 'a'")
1096 .execute(conn)
1097 .unwrap();
1098 crate::sql_query("DELETE FROM kv WHERE key = 'a'")
1099 .execute(conn)
1100 .unwrap();
1101
1102 assert!(
1103 events.lock().unwrap().is_empty(),
1104 "update hook must not fire for WITHOUT ROWID tables"
1105 );
1106 }
1107
1108 #[diesel_test_helper::test]
1113 fn update_hook_silent_for_on_conflict_replace_deletion() {
1114 use std::sync::{Arc, Mutex};
1115 let conn = &mut connection();
1116
1117 crate::sql_query("CREATE TABLE uq (id INTEGER PRIMARY KEY, val TEXT NOT NULL UNIQUE)")
1118 .execute(conn)
1119 .unwrap();
1120
1121 crate::sql_query("INSERT INTO uq (id, val) VALUES (1, 'original')")
1122 .execute(conn)
1123 .unwrap();
1124
1125 let events: Arc<Mutex<Vec<(SqliteChangeOp, i64)>>> = Arc::new(Mutex::new(Vec::new()));
1126 let e2 = events.clone();
1127
1128 conn.on_update(
1129 SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1130 if ev.table_name == "uq" {
1131 e2.lock().unwrap().push((ev.op, ev.rowid));
1132 }
1133 }),
1134 );
1135
1136 crate::sql_query("INSERT OR REPLACE INTO uq (id, val) VALUES (2, 'original')")
1140 .execute(conn)
1141 .unwrap();
1142
1143 let recorded = events.lock().unwrap();
1144 assert_eq!(
1145 recorded.len(),
1146 1,
1147 "expected only 1 event (INSERT), got: {:?}",
1148 *recorded
1149 );
1150 assert_eq!(recorded[0].0, SqliteChangeOp::Insert);
1151 assert_eq!(recorded[0].1, 2, "new row should have rowid 2");
1152 }
1153
1154 #[diesel_test_helper::test]
1158 fn update_hook_silent_for_truncate_optimization() {
1159 use std::sync::{Arc, Mutex};
1160 let conn = &mut connection();
1161
1162 crate::sql_query("CREATE TABLE bulk (id INTEGER PRIMARY KEY, data TEXT NOT NULL)")
1167 .execute(conn)
1168 .unwrap();
1169
1170 crate::sql_query("INSERT INTO bulk (data) VALUES ('a'), ('b'), ('c')")
1171 .execute(conn)
1172 .unwrap();
1173
1174 let events: Arc<Mutex<Vec<SqliteChangeOp>>> = Arc::new(Mutex::new(Vec::new()));
1175 let e2 = events.clone();
1176
1177 conn.on_update(
1178 SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1179 if ev.table_name == "bulk" {
1180 e2.lock().unwrap().push(ev.op);
1181 }
1182 }),
1183 );
1184
1185 crate::sql_query("DELETE FROM bulk").execute(conn).unwrap();
1187
1188 assert!(
1189 events.lock().unwrap().is_empty(),
1190 "truncate optimization should bypass the update hook"
1191 );
1192 }
1193
1194 #[diesel_test_helper::test]
1197 fn update_hook_fires_for_delete_all_when_triggers_disable_truncate() {
1198 use std::sync::{Arc, Mutex};
1199 let conn = &mut connection();
1200
1201 crate::sql_query("CREATE TABLE triggered (id INTEGER PRIMARY KEY, data TEXT NOT NULL)")
1202 .execute(conn)
1203 .unwrap();
1204 crate::sql_query(
1206 "CREATE TRIGGER trg_triggered BEFORE DELETE ON triggered \
1207 BEGIN SELECT 1; END",
1208 )
1209 .execute(conn)
1210 .unwrap();
1211
1212 crate::sql_query("INSERT INTO triggered (data) VALUES ('x'), ('y'), ('z')")
1213 .execute(conn)
1214 .unwrap();
1215
1216 let deletes: Arc<Mutex<Vec<i64>>> = Arc::new(Mutex::new(Vec::new()));
1217 let d2 = deletes.clone();
1218
1219 conn.on_update(
1220 SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1221 if ev.table_name == "triggered" && ev.op == SqliteChangeOp::Delete {
1222 d2.lock().unwrap().push(ev.rowid);
1223 }
1224 }),
1225 );
1226
1227 crate::sql_query("DELETE FROM triggered")
1229 .execute(conn)
1230 .unwrap();
1231
1232 assert_eq!(
1233 deletes.lock().unwrap().len(),
1234 3,
1235 "with triggers present, DELETE without WHERE fires per-row hooks"
1236 );
1237 }
1238
1239 #[diesel_test_helper::test]
1243 fn update_hook_silent_for_internal_sqlite_sequence() {
1244 use std::sync::{Arc, Mutex};
1245 let conn = &mut connection();
1246
1247 crate::sql_query(
1249 "CREATE TABLE seq_test (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)",
1250 )
1251 .execute(conn)
1252 .unwrap();
1253
1254 let tables: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(Vec::new()));
1255 let t2 = tables.clone();
1256
1257 conn.on_update(
1258 SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1259 t2.lock().unwrap().push(ev.table_name.to_owned());
1260 }),
1261 );
1262
1263 crate::sql_query("INSERT INTO seq_test (name) VALUES ('row1')")
1264 .execute(conn)
1265 .unwrap();
1266
1267 let recorded = tables.lock().unwrap();
1268 assert!(
1270 recorded.iter().all(|t| t == "seq_test"),
1271 "expected only 'seq_test' events, got: {:?}",
1272 *recorded
1273 );
1274 assert!(
1275 !recorded.iter().any(|t| t == "sqlite_sequence"),
1276 "sqlite_sequence modifications must not trigger the update hook"
1277 );
1278 }
1279
1280 #[diesel_test_helper::test]
1284 fn update_hook_silent_for_replace_into_on_pk_conflict() {
1285 use std::sync::{Arc, Mutex};
1286 let conn = &mut connection();
1287
1288 crate::sql_query("CREATE TABLE rep (id INTEGER PRIMARY KEY, val TEXT NOT NULL)")
1289 .execute(conn)
1290 .unwrap();
1291
1292 crate::sql_query("INSERT INTO rep (id, val) VALUES (1, 'old')")
1293 .execute(conn)
1294 .unwrap();
1295
1296 let events: Arc<Mutex<Vec<(SqliteChangeOp, i64)>>> = Arc::new(Mutex::new(Vec::new()));
1297 let e2 = events.clone();
1298
1299 conn.on_update(
1300 SqliteUpdateRouter::new().on_any(SqliteChangeOps::ALL, move |ev| {
1301 if ev.table_name == "rep" {
1302 e2.lock().unwrap().push((ev.op, ev.rowid));
1303 }
1304 }),
1305 );
1306
1307 crate::sql_query("REPLACE INTO rep (id, val) VALUES (1, 'new')")
1309 .execute(conn)
1310 .unwrap();
1311
1312 let recorded = events.lock().unwrap();
1313 assert_eq!(
1315 recorded.len(),
1316 1,
1317 "expected 1 event for REPLACE INTO, got: {:?}",
1318 *recorded
1319 );
1320 assert_eq!(recorded[0].0, SqliteChangeOp::Insert);
1321 assert_eq!(recorded[0].1, 1);
1322 }
1323
1324 #[diesel_test_helper::test]
1336 fn change_hook_fires_after_connection_move() {
1337 use std::sync::{Arc, Mutex};
1338
1339 let count = Arc::new(Mutex::new(0u32));
1340 let count2 = count.clone();
1341
1342 let mut conn = connection();
1343 setup_hook_tables(&mut conn);
1344 conn.on_update(SqliteUpdateRouter::new().on(
1345 hook_users::table,
1346 SqliteChangeOps::INSERT,
1347 move |_| {
1348 *count2.lock().unwrap() += 1;
1349 },
1350 ));
1351
1352 let mut boxed = Box::new(conn);
1354
1355 crate::sql_query("INSERT INTO hook_users (name) VALUES ('Alice')")
1356 .execute(&mut *boxed)
1357 .unwrap();
1358
1359 assert_eq!(
1360 *count.lock().unwrap(),
1361 1,
1362 "change hook did not fire after the connection was moved"
1363 );
1364 }
1365
1366 #[diesel_test_helper::test]
1367 fn router_filters_table_and_op() {
1368 use std::sync::{Arc, Mutex};
1369 let conn = &mut connection();
1370 setup_hook_tables(conn);
1371
1372 let fired = Arc::new(Mutex::new(Vec::new()));
1373 let fired2 = fired.clone();
1374
1375 conn.on_update(SqliteUpdateRouter::new().on(
1377 hook_users::table,
1378 SqliteChangeOps::INSERT | SqliteChangeOps::UPDATE,
1379 move |event| fired2.lock().unwrap().push(event.op),
1380 ));
1381
1382 crate::sql_query("INSERT INTO hook_users (name) VALUES ('Alice')")
1383 .execute(conn)
1384 .unwrap();
1385 crate::sql_query("UPDATE hook_users SET name = 'Bob' WHERE id = 1")
1386 .execute(conn)
1387 .unwrap();
1388 crate::sql_query("DELETE FROM hook_users WHERE id = 1")
1389 .execute(conn)
1390 .unwrap();
1391 crate::sql_query("INSERT INTO hook_posts (title) VALUES ('Hello')")
1393 .execute(conn)
1394 .unwrap();
1395
1396 let events = fired.lock().unwrap().clone();
1397 assert_eq!(events, vec![SqliteChangeOp::Insert, SqliteChangeOp::Update]);
1398 }
1399}