1use std::collections::HashMap;
6use std::ops::{Deref, DerefMut};
7use std::sync::{Condvar, Mutex, MutexGuard};
8use std::time::{Duration, Instant};
9
10use postgres::{Client, Statement};
11
12use crate::error::{Error, Result};
13use crate::sql::Q;
14
15pub(crate) type Connector = Box<dyn Fn() -> core::result::Result<Client, postgres::Error> + Send + Sync>;
17
18#[derive(Clone, Debug, PartialEq, Eq)]
20pub struct PoolOptions {
21 max_size: usize,
22 checkout_timeout: Duration,
23}
24
25impl Default for PoolOptions {
26 fn default() -> PoolOptions {
27 PoolOptions::new()
28 }
29}
30
31impl PoolOptions {
32 pub fn new() -> PoolOptions {
34 PoolOptions { max_size: 8, checkout_timeout: Duration::from_secs(30) }
35 }
36
37 pub fn max_size(mut self, n: usize) -> PoolOptions {
39 self.max_size = n.max(1);
40 self
41 }
42
43 pub fn checkout_timeout(mut self, d: Duration) -> PoolOptions {
46 self.checkout_timeout = d;
47 self
48 }
49}
50
51pub(crate) struct Conn {
53 pub(crate) client: Client,
54 statements: HashMap<Q, Statement>,
55}
56
57impl Conn {
58 pub(crate) fn stmt(&mut self, q: Q) -> core::result::Result<Statement, postgres::Error> {
60 if let Some(s) = self.statements.get(&q) {
61 return Ok(s.clone());
62 }
63 let s = self.client.prepare_typed(q.sql(), q.types())?;
64 self.statements.insert(q, s.clone());
65 Ok(s)
66 }
67}
68
69struct State {
70 idle: Vec<Conn>,
71 open: usize,
72}
73
74pub(crate) struct Pool {
75 connect: Connector,
76 state: Mutex<State>,
77 freed: Condvar,
78 options: PoolOptions,
79}
80
81const SESSION: &str = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED; \
83 SET TIME ZONE 'UTC'; SET DateStyle = 'ISO, YMD'";
84
85impl Pool {
86 pub(crate) fn new(connect: Connector, options: PoolOptions) -> Pool {
87 Pool { connect, state: Mutex::new(State { idle: Vec::new(), open: 0 }), freed: Condvar::new(), options }
88 }
89
90 fn lock(&self) -> MutexGuard<'_, State> {
91 self.state.lock().unwrap_or_else(|e| e.into_inner())
94 }
95
96 pub(crate) fn get(&self) -> Result<Pooled<'_>> {
99 let deadline = Instant::now() + self.options.checkout_timeout;
100 let mut state = self.lock();
101 loop {
102 while let Some(conn) = state.idle.pop() {
103 if conn.client.is_closed() {
104 state.open -= 1;
105 continue;
106 }
107 return Ok(Pooled { pool: self, conn: Some(conn) });
108 }
109 if state.open < self.options.max_size {
110 state.open += 1;
111 drop(state);
112 return match self.open() {
113 Ok(conn) => Ok(Pooled { pool: self, conn: Some(conn) }),
114 Err(e) => {
115 self.lock().open -= 1;
116 self.freed.notify_one();
117 Err(e)
118 }
119 };
120 }
121 let now = Instant::now();
122 if now >= deadline {
123 return Err(Error::PoolTimeout);
124 }
125 state = self.freed.wait_timeout(state, deadline - now).unwrap_or_else(|e| e.into_inner()).0;
126 }
127 }
128
129 fn open(&self) -> Result<Conn> {
130 let mut client = (self.connect)()?;
131 client.batch_execute(SESSION)?;
132 Ok(Conn { client, statements: HashMap::new() })
133 }
134
135 fn put_back(&self, conn: Conn) {
136 let mut state = self.lock();
137 if conn.client.is_closed() {
138 state.open -= 1;
139 } else {
140 state.idle.push(conn);
141 }
142 drop(state);
143 self.freed.notify_one();
144 }
145}
146
147pub(crate) struct Pooled<'a> {
150 pool: &'a Pool,
151 conn: Option<Conn>,
152}
153
154impl Deref for Pooled<'_> {
155 type Target = Conn;
156 fn deref(&self) -> &Conn {
157 self.conn.as_ref().expect("connection present until drop")
159 }
160}
161
162impl DerefMut for Pooled<'_> {
163 fn deref_mut(&mut self) -> &mut Conn {
164 self.conn.as_mut().expect("connection present until drop")
165 }
166}
167
168impl Drop for Pooled<'_> {
169 fn drop(&mut self) {
170 if let Some(conn) = self.conn.take() {
171 self.pool.put_back(conn);
172 }
173 }
174}