1use serde::{Deserialize, Serialize};
2
3use crate::types::LockMode;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
6#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
7pub enum DmlKind {
8 #[default]
9 Select,
10 SelectForUpdate,
11 Insert,
12 Update,
13 Delete,
14}
15
16impl DmlKind {
17 pub fn lock_mode(self) -> LockMode {
18 match self {
19 Self::Select => LockMode::AccessShare,
20 Self::SelectForUpdate => LockMode::RowShare,
21 Self::Insert | Self::Update | Self::Delete => LockMode::RowExclusive,
22 }
23 }
24}
25
26impl std::fmt::Display for DmlKind {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 match self {
29 Self::Select => write!(f, "SELECT"),
30 Self::SelectForUpdate => write!(f, "SELECT FOR UPDATE"),
31 Self::Insert => write!(f, "INSERT"),
32 Self::Update => write!(f, "UPDATE"),
33 Self::Delete => write!(f, "DELETE"),
34 }
35 }
36}
37
38const CONFLICT: [[bool; 8]; 8] = [
39 [false, false, false, false, false, false, false, true ], [false, false, false, false, false, false, true, true ], [false, false, false, false, true, true, true, true ], [false, false, false, true, true, true, true, true ], [false, false, true, true, false, true, true, true ], [false, false, true, true, true, true, true, true ], [false, true, true, true, true, true, true, true ], [true, true, true, true, true, true, true, true ], ];
49
50fn lock_index(mode: LockMode) -> usize {
51 match mode {
52 LockMode::AccessShare => 0,
53 LockMode::RowShare => 1,
54 LockMode::RowExclusive => 2,
55 LockMode::ShareUpdateExclusive => 3,
56 LockMode::Share => 4,
57 LockMode::ShareRowExclusive => 5,
58 LockMode::Exclusive => 6,
59 LockMode::AccessExclusive => 7,
60 }
61}
62
63pub fn conflicts(requested: LockMode, held: LockMode) -> bool {
64 CONFLICT[lock_index(requested)][lock_index(held)]
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn access_share_only_conflicts_with_access_exclusive() {
73 assert!(!conflicts(LockMode::AccessShare, LockMode::AccessShare));
74 assert!(!conflicts(LockMode::AccessShare, LockMode::RowShare));
75 assert!(!conflicts(LockMode::AccessShare, LockMode::RowExclusive));
76 assert!(!conflicts(LockMode::AccessShare, LockMode::ShareUpdateExclusive));
77 assert!(!conflicts(LockMode::AccessShare, LockMode::Share));
78 assert!(!conflicts(LockMode::AccessShare, LockMode::ShareRowExclusive));
79 assert!(!conflicts(LockMode::AccessShare, LockMode::Exclusive));
80 assert!(conflicts(LockMode::AccessShare, LockMode::AccessExclusive));
81 }
82
83 #[test]
84 fn access_exclusive_conflicts_with_everything() {
85 assert!(conflicts(LockMode::AccessExclusive, LockMode::AccessShare));
86 assert!(conflicts(LockMode::AccessExclusive, LockMode::RowShare));
87 assert!(conflicts(LockMode::AccessExclusive, LockMode::RowExclusive));
88 assert!(conflicts(LockMode::AccessExclusive, LockMode::ShareUpdateExclusive));
89 assert!(conflicts(LockMode::AccessExclusive, LockMode::Share));
90 assert!(conflicts(LockMode::AccessExclusive, LockMode::ShareRowExclusive));
91 assert!(conflicts(LockMode::AccessExclusive, LockMode::Exclusive));
92 assert!(conflicts(LockMode::AccessExclusive, LockMode::AccessExclusive));
93 }
94
95 #[test]
96 fn matrix_is_symmetric() {
97 let modes = [
98 LockMode::AccessShare,
99 LockMode::RowShare,
100 LockMode::RowExclusive,
101 LockMode::ShareUpdateExclusive,
102 LockMode::Share,
103 LockMode::ShareRowExclusive,
104 LockMode::Exclusive,
105 LockMode::AccessExclusive,
106 ];
107 for &a in &modes {
108 for &b in &modes {
109 assert_eq!(
110 conflicts(a, b),
111 conflicts(b, a),
112 "conflict matrix is not symmetric for {a} vs {b}"
113 );
114 }
115 }
116 }
117
118 #[test]
119 fn row_exclusive_conflicts_with_share() {
120 assert!(conflicts(LockMode::RowExclusive, LockMode::Share));
121 assert!(conflicts(LockMode::Share, LockMode::RowExclusive));
122 }
123
124 #[test]
125 fn share_does_not_conflict_with_share() {
126 assert!(!conflicts(LockMode::Share, LockMode::Share));
127 }
128
129 #[test]
130 fn dml_lock_modes() {
131 assert_eq!(DmlKind::Select.lock_mode(), LockMode::AccessShare);
132 assert_eq!(DmlKind::SelectForUpdate.lock_mode(), LockMode::RowShare);
133 assert_eq!(DmlKind::Insert.lock_mode(), LockMode::RowExclusive);
134 assert_eq!(DmlKind::Update.lock_mode(), LockMode::RowExclusive);
135 assert_eq!(DmlKind::Delete.lock_mode(), LockMode::RowExclusive);
136 }
137}