Skip to content

Commit 100e4cf

Browse files
author
Julius de Bruijn
committed
Configuration to disable internal stmt cache
This enables usage with pgBouncer's transaction mode. Typically when using the transaction mode, a client gets a new connection from pgBouncer for every new transaction. It's quite useful and allows one to use prepared statements in this mode. The workflow goes: ```sql -- start a new transaction BEGIN -- deallocate all stored statements from the server to prevent -- collisions DEALLOCATE ALL -- run the queries here -- .. -- .. COMMIT -- or ROLLBACK ``` Now in a case where the query uses custom types such as enums, what tokio-postgres does is it fetches the type info for the given type, stores the info to the cache and also caches the statements for fetching the info to the client. Now when we have two tables with different custom types in both of them, we can imagine the following workflow: ```rust // first query client.simple_query("BEGIN")?; client.simple_query("DEALLOCATE ALL")?; let stmt = client.prepare("SELECT \"public\".\"User\".\"id\", \"public\".\"User\".\"userType\" FROM \"public\".\"User\" WHERE 1=1 OFFSET $1")?; dbg!(client.query(&stmt, &[&0i64])?); client.simple_query("COMMIT")?; // second query client.simple_query("BEGIN")?; client.simple_query("DEALLOCATE ALL")?; let stmt = client.prepare("SELECT \"public\".\"Work\".\"id\", \"public\".\"Work\".\"workType\" FROM \"public\".\"Work\" WHERE 1=1 OFFSET $1")?; dbg!(client.query(&stmt, &[&0i64])?); client.simple_query("COMMIT")?; ``` The `userType` and `workType` are both enums, and the preparing of the second query will give an error `prepared statement "s1" does not exist`, where `s1` is the query to the `pg_catalog` for the type info. The change here gives an extra flag for the client to disable caching of statements.
1 parent c7a8adf commit 100e4cf

File tree

8 files changed

+83
-7
lines changed

8 files changed

+83
-7
lines changed

.direnv/cache-pre278406.d3f7e969b98

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export $'depsTargetTarget'='';export $'phases'=$'nobuildPhase';export NIX_ENFORCE_NO_NATIVE=1;export XDG_DATA_DIRS=$'/nix/store/8nwgidwa623jkz09wn0rjvcs07jjval3-patchelf-0.12/share';export NIX_SSL_CERT_FILE=$'/no-cert-file.crt';export HOST_PATH=$'/nix/store/ra609mrrl92jirph1zbahcb06pxmjhcl-openssl-1.1.1j-bin/bin:/nix/store/7hgm9nfh4c6fs3qxma9472la2hcp5x03-pkg-config-wrapper-0.29.2/bin:/nix/store/yzygh1nkk6sj3hrdrq8lqfg0hff80ww5-libkrb5-1.18-dev/bin:/nix/store/f059zv2fl9ml1q53qj5wbwi8xiim28g1-libkrb5-1.18/bin:/nix/store/9fkdn82nzxgkxyax3m9qdh2knj7apaa5-coreutils-8.32/bin:/nix/store/7nx8fqzpi5nsbi07md1qb1gbf9fscf9i-findutils-4.7.0/bin:/nix/store/7v1xa3528d5frr55d3dhl991zx7by169-diffutils-3.7/bin:/nix/store/ij7k48dhdrda13gcp3lbpi507b2awnmr-gnused-4.8/bin:/nix/store/ia1dn8zyi07phxkx02c0wwqc7v7c54nl-gnugrep-3.6/bin:/nix/store/9sscma6kgp35kb70bi8pms35ppk7j35r-gawk-5.1.0/bin:/nix/store/hxpycgfpdnmnc9iwxakxr037k0bvdq97-gnutar-1.32/bin:/nix/store/89kh3d91b9i9mqh39fj6xdff1sw81m9f-gzip-1.10/bin:/nix/store/vw1lcig1bkncmsmhif27h8hq1gaxldhg-bzip2-1.0.6.0.2-bin/bin:/nix/store/n7vham7g9lgwb7v83sk5nl7akk3zspvq-gnumake-4.3/bin:/nix/store/w65ydq10abi813fi7d5j7afdcrxj3aqq-bash-4.4-p23/bin:/nix/store/lsr2s27gbqxny7layxjab55dlqzrl1v2-patch-2.7.6/bin:/nix/store/sg3h64z6a7mxmap8zgrhhjscpri5b5vn-xz-5.2.5-bin/bin';export $'buildInputs'=$'/nix/store/kic4qky3myhflpf8x6zqq332vqj4wa23-openssl-1.1.1j-dev /nix/store/7hgm9nfh4c6fs3qxma9472la2hcp5x03-pkg-config-wrapper-0.29.2 /nix/store/g2h7j8n4cl8396zvma3qkyf326n0p6d5-stdenv-linux /nix/store/wqc643blcnnw2k9m2r9vc4jd3nk22377-clang-7.1.0-lib /nix/store/yzygh1nkk6sj3hrdrq8lqfg0hff80ww5-libkrb5-1.18-dev';export $'propagatedNativeBuildInputs'='';export LD=$'ld';export SOURCE_DATE_EPOCH=315532800;export $'depsTargetTargetPropagated'='';export OBJCOPY=$'objcopy';export RANLIB=$'ranlib';export AR=$'ar';export NIX_CC=$'/nix/store/pqiwg1jw0s7qzdi4m9xbps2jb98fsxbx-gcc-wrapper-10.2.0';export NIX_BINTOOLS=$'/nix/store/r7v3x5a68a4mxc2kh04cmyzx7xq00w2g-binutils-wrapper-2.35.1';export TEMPDIR=$'/run/user/1000';export LOGNAME=$'pimeys';export CC=$'gcc';export STRIP=$'strip';export PKG_CONFIG_PATH_FOR_TARGET=$'/nix/store/kic4qky3myhflpf8x6zqq332vqj4wa23-openssl-1.1.1j-dev/lib/pkgconfig:/nix/store/yzygh1nkk6sj3hrdrq8lqfg0hff80ww5-libkrb5-1.18-dev/lib/pkgconfig';export HOME=$'/home/pimeys';export $'configureFlags'='';export $'shellHook'='';export NIX_BUILD_TOP=$'/run/user/1000';export __ETC_PROFILE_SOURCED=1;export $'out'=$'/nix/store/z4kmx9zl4dfg558lzlf5ayw111jd75kj-nix-shell';export $'system'=$'x86_64-linux';export TMPDIR=$'/run/user/1000';export $'name'=$'nix-shell';export NM=$'nm';export PKG_CONFIG_FOR_TARGET=$'pkg-config';export _=$'/nix/store/p579xdpm0prfjhhbxibhrrjcyix172ln-direnv-2.28.0/bin/direnv';export $'builder'=$'/nix/store/w65ydq10abi813fi7d5j7afdcrxj3aqq-bash-4.4-p23/bin/bash';export $'strictDeps'='';export NIX_CFLAGS_COMPILE=$' -frandom-seed=z4kmx9zl4d -isystem /nix/store/kic4qky3myhflpf8x6zqq332vqj4wa23-openssl-1.1.1j-dev/include -isystem /nix/store/yzygh1nkk6sj3hrdrq8lqfg0hff80ww5-libkrb5-1.18-dev/include -isystem /nix/store/kic4qky3myhflpf8x6zqq332vqj4wa23-openssl-1.1.1j-dev/include -isystem /nix/store/yzygh1nkk6sj3hrdrq8lqfg0hff80ww5-libkrb5-1.18-dev/include';export OBJDUMP=$'objdump';export STRINGS=$'strings';export $'outputs'=$'out';export $'NIX_BINTOOLS_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu'=1;export PATH=$'/nix/store/mshinwyjb1imfpdjmrpbbh3r7zz5sn6x-bash-interactive-4.4-p23/bin:/nix/store/8nwgidwa623jkz09wn0rjvcs07jjval3-patchelf-0.12/bin:/nix/store/pqiwg1jw0s7qzdi4m9xbps2jb98fsxbx-gcc-wrapper-10.2.0/bin:/nix/store/xrb5qsxpvhpm0irr9ykfqpvj8a6sk4xh-gcc-10.2.0/bin:/nix/store/b6p5cspsidkc2bpljhwz7d0nbmgrx5z0-glibc-2.32-37-bin/bin:/nix/store/9fkdn82nzxgkxyax3m9qdh2knj7apaa5-coreutils-8.32/bin:/nix/store/r7v3x5a68a4mxc2kh04cmyzx7xq00w2g-binutils-wrapper-2.35.1/bin:/nix/store/xdii8qvch5h8chyp0z2is2qzky565w68-binutils-2.35.1/bin:/nix/store/b6p5cspsidkc2bpljhwz7d0nbmgrx5z0-glibc-2.32-37-bin/bin:/nix/store/9fkdn82nzxgkxyax3m9qdh2knj7apaa5-coreutils-8.32/bin:/nix/store/ra609mrrl92jirph1zbahcb06pxmjhcl-openssl-1.1.1j-bin/bin:/nix/store/7hgm9nfh4c6fs3qxma9472la2hcp5x03-pkg-config-wrapper-0.29.2/bin:/nix/store/yzygh1nkk6sj3hrdrq8lqfg0hff80ww5-libkrb5-1.18-dev/bin:/nix/store/f059zv2fl9ml1q53qj5wbwi8xiim28g1-libkrb5-1.18/bin:/nix/store/9fkdn82nzxgkxyax3m9qdh2knj7apaa5-coreutils-8.32/bin:/nix/store/7nx8fqzpi5nsbi07md1qb1gbf9fscf9i-findutils-4.7.0/bin:/nix/store/7v1xa3528d5frr55d3dhl991zx7by169-diffutils-3.7/bin:/nix/store/ij7k48dhdrda13gcp3lbpi507b2awnmr-gnused-4.8/bin:/nix/store/ia1dn8zyi07phxkx02c0wwqc7v7c54nl-gnugrep-3.6/bin:/nix/store/9sscma6kgp35kb70bi8pms35ppk7j35r-gawk-5.1.0/bin:/nix/store/hxpycgfpdnmnc9iwxakxr037k0bvdq97-gnutar-1.32/bin:/nix/store/89kh3d91b9i9mqh39fj6xdff1sw81m9f-gzip-1.10/bin:/nix/store/vw1lcig1bkncmsmhif27h8hq1gaxldhg-bzip2-1.0.6.0.2-bin/bin:/nix/store/n7vham7g9lgwb7v83sk5nl7akk3zspvq-gnumake-4.3/bin:/nix/store/w65ydq10abi813fi7d5j7afdcrxj3aqq-bash-4.4-p23/bin:/nix/store/lsr2s27gbqxny7layxjab55dlqzrl1v2-patch-2.7.6/bin:/nix/store/sg3h64z6a7mxmap8zgrhhjscpri5b5vn-xz-5.2.5-bin/bin';export NIX_STORE=$'/nix/store';export $'doInstallCheck'='';export IN_NIX_SHELL=$'pure';export SHELL=$'/nix/store/mshinwyjb1imfpdjmrpbbh3r7zz5sn6x-bash-interactive-4.4-p23/bin/bash';export LIBCLANG_PATH=$'/nix/store/wqc643blcnnw2k9m2r9vc4jd3nk22377-clang-7.1.0-lib/lib';export READELF=$'readelf';export TMP=$'/run/user/1000';export $'propagatedBuildInputs'='';export SSL_CERT_FILE=$'/no-cert-file.crt';export TERM=$'xterm-256color';export NIX_LDFLAGS=$'-rpath /nix/store/z4kmx9zl4dfg558lzlf5ayw111jd75kj-nix-shell/lib64 -rpath /nix/store/z4kmx9zl4dfg558lzlf5ayw111jd75kj-nix-shell/lib -L/nix/store/1pzq6yh1r415ykizk6zw61z5jlkx9994-openssl-1.1.1j/lib -L/nix/store/wqc643blcnnw2k9m2r9vc4jd3nk22377-clang-7.1.0-lib/lib -L/nix/store/f059zv2fl9ml1q53qj5wbwi8xiim28g1-libkrb5-1.18/lib -L/nix/store/1pzq6yh1r415ykizk6zw61z5jlkx9994-openssl-1.1.1j/lib -L/nix/store/wqc643blcnnw2k9m2r9vc4jd3nk22377-clang-7.1.0-lib/lib -L/nix/store/f059zv2fl9ml1q53qj5wbwi8xiim28g1-libkrb5-1.18/lib';export $'shell'=$'/nix/store/w65ydq10abi813fi7d5j7afdcrxj3aqq-bash-4.4-p23/bin/bash';export NIX_INDENT_MAKE=1;export TEMP=$'/run/user/1000';export DISPLAY=$':1';export USER=$'pimeys';export $'depsBuildBuild'='';export $'nativeBuildInputs'='';export NIX_HARDENING_ENABLE=$'fortify stackprotector pic strictoverflow format relro bindnow';export $'depsBuildTargetPropagated'='';export PAGER=$'less -R';export $'NIX_PKG_CONFIG_WRAPPER_TARGET_TARGET_x86_64_unknown_linux_gnu'=1;export $'depsBuildBuildPropagated'='';export $'depsBuildTarget'='';export $'NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu'=1;export SHLVL=4;export $'depsHostHost'='';export $'patches'='';export $'nobuildPhase'=$'echo\necho "This derivation is not meant to be built, aborting";\necho\nexit 1\n';export AS=$'as';export CXX=$'g++';export $'depsHostHostPropagated'='';export NIX_BUILD_CORES=32;export SIZE=$'size';export $'stdenv'=$'/nix/store/6c47azxacncswc1pllzj28zfzqw40d7c-stdenv-linux';export CONFIG_SHELL=$'/nix/store/w65ydq10abi813fi7d5j7afdcrxj3aqq-bash-4.4-p23/bin/bash';export $'doCheck'='';

.direnv/drv

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/nix/store/y6sw1ry62ik9v6qv4zl59ari6g1swrxd-nix-shell.drv

.envrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use nix

postgres/src/config.rs

+15
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,21 @@ impl Config {
328328
self
329329
}
330330

331+
/// When enabled, the client skips all internal caching for statements,
332+
/// allowing usage with pgBouncer's transaction mode and clearing of
333+
/// statements in the session with `DEALLOCATE ALL`.
334+
///
335+
/// Defaults to `false`.
336+
pub fn pgbouncer_mode(&mut self, enable: bool) -> &mut Config {
337+
self.config.pgbouncer_mode(enable);
338+
self
339+
}
340+
341+
/// Gets the pgBouncer mode status.
342+
pub fn get_pgbouncer_mode(&self) -> bool {
343+
self.config.get_pgbouncer_mode()
344+
}
345+
331346
/// Opens a connection to a PostgreSQL database.
332347
pub fn connect<T>(&self, tls: T) -> Result<Client, Error>
333348
where

shell.nix

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{ pkgs ? import <nixpkgs> {} }:
2+
3+
with pkgs;
4+
5+
mkShell {
6+
LIBCLANG_PATH="${pkgs.llvmPackages.libclang}/lib";
7+
buildInputs = with pkgs; [
8+
openssl
9+
pkg-config
10+
clangStdenv
11+
llvmPackages.libclang
12+
kerberos
13+
];
14+
}

tokio-postgres/src/client.rs

+27-6
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ struct State {
6565
pub struct InnerClient {
6666
sender: mpsc::UnboundedSender<Request>,
6767
state: Mutex<State>,
68+
pgbouncer_mode: bool,
6869
}
6970

7071
impl InnerClient {
@@ -82,27 +83,45 @@ impl InnerClient {
8283
}
8384

8485
pub fn typeinfo(&self) -> Option<Statement> {
85-
self.state.lock().typeinfo.clone()
86+
if self.pgbouncer_mode {
87+
None
88+
} else {
89+
self.state.lock().typeinfo.clone()
90+
}
8691
}
8792

8893
pub fn set_typeinfo(&self, statement: &Statement) {
89-
self.state.lock().typeinfo = Some(statement.clone());
94+
if !self.pgbouncer_mode {
95+
self.state.lock().typeinfo = Some(statement.clone());
96+
}
9097
}
9198

9299
pub fn typeinfo_composite(&self) -> Option<Statement> {
93-
self.state.lock().typeinfo_composite.clone()
100+
if self.pgbouncer_mode {
101+
None
102+
} else {
103+
self.state.lock().typeinfo_composite.clone()
104+
}
94105
}
95106

96107
pub fn set_typeinfo_composite(&self, statement: &Statement) {
97-
self.state.lock().typeinfo_composite = Some(statement.clone());
108+
if !self.pgbouncer_mode {
109+
self.state.lock().typeinfo_composite = Some(statement.clone());
110+
}
98111
}
99112

100113
pub fn typeinfo_enum(&self) -> Option<Statement> {
101-
self.state.lock().typeinfo_enum.clone()
114+
if self.pgbouncer_mode {
115+
None
116+
} else {
117+
self.state.lock().typeinfo_enum.clone()
118+
}
102119
}
103120

104121
pub fn set_typeinfo_enum(&self, statement: &Statement) {
105-
self.state.lock().typeinfo_enum = Some(statement.clone());
122+
if !self.pgbouncer_mode {
123+
self.state.lock().typeinfo_enum = Some(statement.clone());
124+
}
106125
}
107126

108127
pub fn type_(&self, oid: Oid) -> Option<Type> {
@@ -156,6 +175,7 @@ impl Client {
156175
ssl_mode: SslMode,
157176
process_id: i32,
158177
secret_key: i32,
178+
pgbouncer_mode: bool,
159179
) -> Client {
160180
Client {
161181
inner: Arc::new(InnerClient {
@@ -167,6 +187,7 @@ impl Client {
167187
types: HashMap::new(),
168188
buf: BytesMut::new(),
169189
}),
190+
pgbouncer_mode,
170191
}),
171192
#[cfg(feature = "runtime")]
172193
socket_config: None,

tokio-postgres/src/config.rs

+17
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ pub struct Config {
159159
pub(crate) keepalives_idle: Duration,
160160
pub(crate) target_session_attrs: TargetSessionAttrs,
161161
pub(crate) channel_binding: ChannelBinding,
162+
pub(crate) pgbouncer_mode: bool,
162163
}
163164

164165
impl Default for Config {
@@ -184,6 +185,7 @@ impl Config {
184185
keepalives_idle: Duration::from_secs(2 * 60 * 60),
185186
target_session_attrs: TargetSessionAttrs::Any,
186187
channel_binding: ChannelBinding::Prefer,
188+
pgbouncer_mode: false,
187189
}
188190
}
189191

@@ -387,6 +389,21 @@ impl Config {
387389
self.channel_binding
388390
}
389391

392+
/// When enabled, the client skips all internal caching for statements,
393+
/// allowing usage with pgBouncer's transaction mode and clearing of
394+
/// statements in the session with `DEALLOCATE ALL`.
395+
///
396+
/// Defaults to `false`.
397+
pub fn pgbouncer_mode(&mut self, enable: bool) -> &mut Config {
398+
self.pgbouncer_mode = enable;
399+
self
400+
}
401+
402+
/// Gets the pgBouncer mode status.
403+
pub fn get_pgbouncer_mode(&self) -> bool {
404+
self.pgbouncer_mode
405+
}
406+
390407
fn param(&mut self, key: &str, value: &str) -> Result<(), Error> {
391408
match key {
392409
"user" => {

tokio-postgres/src/connect_raw.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,13 @@ where
100100
let (process_id, secret_key, parameters) = read_info(&mut stream).await?;
101101

102102
let (sender, receiver) = mpsc::unbounded();
103-
let client = Client::new(sender, config.ssl_mode, process_id, secret_key);
103+
let client = Client::new(
104+
sender,
105+
config.ssl_mode,
106+
process_id,
107+
secret_key,
108+
config.pgbouncer_mode,
109+
);
104110
let connection = Connection::new(stream.inner, stream.delayed, parameters, receiver);
105111

106112
Ok((client, connection))

0 commit comments

Comments
 (0)