Skip to content

support unnamed statements #1067

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed

support unnamed statements #1067

wants to merge 3 commits into from

Conversation

devsnek
Copy link

@devsnek devsnek commented Sep 25, 2023

Refs: #1017
Refs: #1044

This PR implements unnamed statements when querying the database. This is not a performance optimization. The library still uses a "prepared" statement in order to parse the columns and types of queries, so round trips are not reduced (although we don't need to send an additional query to clean up the named prepared statement, which is nice). The reason for this change is to enable queries against pgbouncer instances which are running in transaction or statement pooling modes. A named prepared statement cannot be used in these modes, because the connection may be swapped from under you between preparing the statement and using it.

I found this helpful in understanding what was going on while I was debugging this problem: https://jpcamara.com/2023/04/12/pgbouncer-is-useful.html#prepared-statements

While writing this I also changed the "unexpected message" errors to help me debug, but I can remove that from this PR if you feel it is out of scope.

@sfackler
Copy link
Owner

This will break pipelined queries.

@devsnek
Copy link
Author

devsnek commented Sep 25, 2023

@sfackler Can you elaborate on this? My understanding is the parse/bind/execute is sent as a single unit, and would therefore be unaffected by pipelining?

@sfackler
Copy link
Owner

Oh wait, the same query is prepared both at preparation time and at each use? I guess that works but it feels like an pretty janky setup, and defeats the purpose of statement preparation.

IMO, if you're running pgbouncer in transaction mode you should just make sure you scope your statements within the transaction that uses them. If you're using it in statement mode, either don't, or deal with simple_query.

@devsnek
Copy link
Author

devsnek commented Sep 25, 2023

Yeah the prepare flow is only for this library to get a handle on those types that it expects. It is definitely not ideal BUT it is not worse than the current implementation of doing a named prepare for every query. I don't think I agree that simple_query is an acceptable solution as you have to go back to worrying about sql injection. It also doesn't return something that works with the To/FromSql traits, which is a huge usability problem 😬

@sfackler
Copy link
Owner

It is definitely not ideal BUT it is not worse than the current implementation of doing a named prepare for every query.

It is worse - it means you cannot avoid preparing frequently-executed queries on every execution.

@devsnek
Copy link
Author

devsnek commented Sep 26, 2023

it means you cannot avoid preparing frequently-executed queries on every execution.

To be clear:

let statement = client.prepare("...").await?;
client.query(statement, &[]).await?;
client.query("...", &[]).await?;

The first example is not changed at all by this PR. The second example behaves almost the identically, except that it uses an unnamed statement instead of a named statement. No additional network calls or round trips are introduced in either case by this PR. This matches how other postgres libraries (including libpq) generally behave.

@devsnek
Copy link
Author

devsnek commented Sep 28, 2023

@sfackler any chance of this getting merged? I really don't want to have to maintain a fork of this library... If you're still against this for some reason, maybe it could be behind a cargo feature?

@fbjork
Copy link

fbjork commented Oct 5, 2023

@sfackler We would love to see this one merged too as we're about to launch the Postgres connector at Grafbase.

@janpio
Copy link

janpio commented Oct 6, 2023

We at @prisma would also be very interested in this.

Right now, we do a weird dance of wrapping all queries in transactions, and each time deallocating the named prepared statements when talking to a PgBouncer instance in transaction mode - which adds up to 3 additional roundtrips for 1 query and has an obvious very bad effect on our performance.

With unnamed (but still prepared) statements we could probably avoid this.

@pimeys
Copy link

pimeys commented Oct 12, 2023

It would be extra cool to get this merged... Now, again, needing to use a fork of this crate for a production system.

@devsnek
Copy link
Author

devsnek commented Nov 1, 2023

@sfackler any word here?

@devsnek
Copy link
Author

devsnek commented Nov 14, 2023

@sfackler anything? should i just close this? i really don't want to fork...

@devsnek devsnek closed this Jan 12, 2024
@devsnek devsnek deleted the unnamed-statement branch January 12, 2024 03:10
ramnivas added a commit to exograph/rust-postgres that referenced this pull request Jun 4, 2024
Introduce a new `query_with_param_types` method that allows to specify Postgres type parameters. This obviated the need to use prepared statementsjust to obtain parameter types for a query. It then combines parse, bind, and execute in a single packet.

Related: sfackler#1017, sfackler#1067
rudolphfroger pushed a commit to tandemdrive/rust-postgres that referenced this pull request Mar 4, 2025
* fix code block

* Update lib.rs

* Add table_oid and field_id to columns of prepared statements

* Simplify Debug impl of Column

* Update id types

* feat(types): add default derive to json wrapper

Adds a Default impl for `Json<T> where T: Default` allowing for other
structs to use the wrapper and implement Default.

* Update env_logger requirement from 0.10 to 0.11

Updates the requirements on [env_logger](https://github.com/rust-cli/env_logger) to permit the latest version.
- [Release notes](https://github.com/rust-cli/env_logger/releases)
- [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md)
- [Commits](rust-cli/env_logger@v0.10.0...v0.11.0)

---
updated-dependencies:
- dependency-name: env_logger
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

* Update ci.yml

* Update main.rs

* add #[track_caller] to the Row::get() functions

This small quality-of-life improvement changes these errors:

thread '<unnamed>' panicked at /../.cargo/registry/src/index.crates.io-6f17d22bba15001f/tokio-postgres-0.7.10/src/row.rs:151:25:
error retrieving column 0: error deserializing column 0: a Postgres value was `NULL`

to:

thread '<unnamed>' panicked at my-program.rs:100:25:
error retrieving column 0: error deserializing column 0: a Postgres value was `NULL`

* Bump CI version

* Added ReadOnly session attr

* Added ReadOnly session attr

* Update base64 requirement from 0.21 to 0.22

Updates the requirements on [base64](https://github.com/marshallpierce/rust-base64) to permit the latest version.
- [Changelog](https://github.com/marshallpierce/rust-base64/blob/master/RELEASE-NOTES.md)
- [Commits](marshallpierce/rust-base64@v0.21.0...v0.22.0)

---
updated-dependencies:
- dependency-name: base64
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

* Shrink query_opt/query_one codegen size very slightly

* use `split_once` instead of `split` to parse lsn strings

[`str::split`](https://doc.rust-lang.org/std/primitive.str.html#method.split)
allocates a vector and generates considerably more instructions when compiled
than [`str::split_once`](https://doc.rust-lang.org/std/primitive.str.html#method.split_once).

[`u64::from_str_radix(split_lo, 16)`](https://doc.rust-lang.org/std/primitive.u64.html#method.from_str_radix)
will error if the `lsn_str` contains more than one `/` so this change should
result in the same behavior as the current implementation despite not explicitly
checking this.

* Make license metadata SPDX compliant

* Update heck requirement from 0.4 to 0.5

---
updated-dependencies:
- dependency-name: heck
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

* Avoid extra clone in config if possible

Using `impl Into<String>` instead of `&str` in a fn arg allows both `&str` and `String` as parameters - thus if the caller already has a String object that it doesn't need, it can pass it in without extra cloning.

The same might be done with the password, but may require closer look.

* add simple_query to GenericClient in tokio_postgres

* feat(types): add 'js' feature for wasm

Enables the "js" feature of postgres-protocol.

* Add RowDescription to SimpleQueryMessage

* Formatting updates

* Clippy compliance

* Formatting

* Work with pools that don't support prepared statements

Introduce a new `query_with_param_types` method that allows to specify Postgres type parameters. This obviated the need to use prepared statementsjust to obtain parameter types for a query. It then combines parse, bind, and execute in a single packet.

Related: sfackler#1017, sfackler#1067

* Derive Clone for Row

* impl Debug for Statement

The lack of this common trait bound caused some unpleasantness.
For example, the following didn't compile:

let x = OnceLock::new();
let stmt = db.prepare(...)?;
x.set(stmt).expect(...); // returns Result<(), T=Statement> where T: Debug

* Fix a few nits pointed out by clippy

- ...::max_value() -> ..::MAX
- delete explicit import of signed integer types

* Handle non-UTF8 error fields

* PR Fix: Only use single clone for RowDescription

* Replace the state machine to process messages with a direct match statements

* Remove query_raw_with_param_types as per PR feedback

* PR Fix: Clone first then move

* Address review comment to rename query_with_param_types to query_typed

* Fix a clippy warning

* query_typed tweaks

* Fix cancellation of TransactionBuilder::start

* Release postgres-protocol v0.6.7

* Release postgres-types v0.2.7

* Release tokio-postgres v0.7.11

* Release postgres v0.19.8

* Add jiff support

* For `query_typed`, deal with the no-data case.

If a query returns no data, we receive `Message::NoData`, which signals
the completion of the query. However, we treated it as a no-op, leading
to processing other messages and eventual failure.

This PR fixes the issue and updates the `query_typed` tests to cover
this scenario.

* Support AIX keepalive

* Remove unecessary alias for Timestamp

* Update impl for Timestamp

The impl now directly computes `Timestamp` rather than going through
`DateTime` and `Zoned`.

* Remove impl for `Zoned`

`Timestamp` already has impl and is semantically accurate for mapping to `timestamptz`, unlike `Zoned`.
End users can do their own conversions from `Timestamp` to `Zoned` if desired.

* Rename PG_EPOCH

* Fix ToSql

This sets the smallest unit to microseconds when calculating time deltas.

Previously, the number of microseconds was expressed improperly because
the rounding was not set.

* Add jiff tests and overflow checks

This adds tests in the same fashion as the existing ones for `chrono`
and `time`.

Overflow is now handled using fallible operations.
For example, `Span:microseconds` is replaced with `Span::try_microseconds`.
Postgres infinity values are workiing as expected.

All tests are passing.

* Remove `Zoned` from docs

The implementation was previously removed as per jiff author comment.

* Fix `chrono::DateTime<Utc>` tests

`test_date_time_params` and `test_with_special_date_time_params` could fail when run with a non-UTC system time zone.

In Postgres, if no time zone is stated in the input string, then it is assumed to be in system time.
This means that in these tests, a correctly parsed `DateTime<Utc>` could be compared to an incorrectly offset time.

The offset component is now included in the test strings of these tests. This component is already present in the test strings for `time::OffsetDateTime`.

* Release postgres-derive v0.4.6

* Release postgres-types v0.2.8

* Release tokio-postgres v0.7.12

* Release postgres v0.19.9

* feat: add ssl_negotiation option

* test: updte tests for direct tls

* feat: provide built-in functions for setting ALPN

* refactor: pub use sslnegotiation

* refactor: apply review comments

* chore: update postgres for ci

* Add support for cidr 0.3 under separate feature

* add `load_balance_hosts` to `Debug` impl for `Config`

* Fix time 0.3 infinity panics

Getting `PrimitiveDateTime` panics if the value is infinity.
This commit fixes the panic.

An infinity test is added, mirroring the existing one for chrono.

* Fix clippy needless_lifetimes

* Fix clippy extra_unused_lifetimes

* Fix for Rust 2024 match ergonomics

Fixes an error related to https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html

* Fix another needless_lifetimes

* Fix clippy needless_borrowed_reference

The applied suggestion for 2024 match ergonomics trips another lint.
This fixes the lint.

* Bump CI Rust version to 1.75.0

cargo tests in CI are [failing](https://github.com/sfackler/rust-postgres/actions/runs/12862700447/job/35858038081?pr=1198)
because of a dependency requirement:

```
Run cargo test --all
error: package `geo-types v0.7.15` cannot be built because it requires rustc 1.75 or newer, while the currently active rustc version is 1.74.0
Either upgrade to rustc 1.75 or newer, or use
cargo update [email protected] --precise ver
where `ver` is the latest version of `geo-types` supporting rustc 1.74.0
```

This bumps the Rust version so tests will run.

* Bump actions/checkout

* Empty commit (re-run CI)

* Update rand requirement from 0.8 to 0.9

Updates the requirements on [rand](https://github.com/rust-random/rand) to permit the latest version.
- [Release notes](https://github.com/rust-random/rand/releases)
- [Changelog](https://github.com/rust-random/rand/blob/master/CHANGELOG.md)
- [Commits](rust-random/rand@0.8.5...0.9.0)

---
updated-dependencies:
- dependency-name: rand
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

* refactor: address review comments

* chore: addressed rand 0.9's deprecations

* fix build

* fix clippy

* fix wasm build

* bump getrandom

* Revert time 0.2 change

* Propagate features

* add docs

* fix ci

* Release postgres-native-tls v0.5.1

* Release postgres-openssl v0.5.1

* Release postgres-protocol v0.6.8

* Release postgres-types v0.2.9

* Release tokio-postgres v0.7.13

* Release postgres v0.19.10

* Fix typo in version in postgres’s changelog

* Only call set_tcp_user_timeout when enabled

* feat: support jiff v0.2

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* WIP

* Clearly indicate tademdrive changelog

* Update changelog.

* Fix changelog

* Fix clippy

* Fix clippy

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: James Guthrie <[email protected]>
Co-authored-by: Steven Fackler <[email protected]>
Co-authored-by: Michael P. Jung <[email protected]>
Co-authored-by: Troy Benson <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Charles Samuels <[email protected]>
Co-authored-by: chandr-andr (Kiselev Aleksandr) <[email protected]>
Co-authored-by: novacrazy <[email protected]>
Co-authored-by: laxjesse <[email protected]>
Co-authored-by: Paolo Barbolini <[email protected]>
Co-authored-by: Yuri Astrakhan <[email protected]>
Co-authored-by: vsuryamurthy <[email protected]>
Co-authored-by: Duarte Nunes <[email protected]>
Co-authored-by: Dane Rigby <[email protected]>
Co-authored-by: Ramnivas Laddad <[email protected]>
Co-authored-by: Lev Kokotov <[email protected]>
Co-authored-by: Sidney Cammeresi <[email protected]>
Co-authored-by: Allan Zhang <[email protected]>
Co-authored-by: Qiu Chaofan <[email protected]>
Co-authored-by: Ning Sun <[email protected]>
Co-authored-by: Sidney Cammeresi <[email protected]>
Co-authored-by: Harris Kaufmann <[email protected]>
Co-authored-by: Allan <[email protected]>
Co-authored-by: Kristof Mattei <[email protected]>
Co-authored-by: Charmander <[email protected]>
Co-authored-by: Shawn L. <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants