fix(postgres): max number of binds is 65535, not 32767 (regression)

This commit is contained in:
Austin Bonander
2024-08-26 15:54:59 -07:00
parent 371cf4a0cc
commit 20ba796b0d
5 changed files with 80 additions and 6 deletions

View File

@@ -204,7 +204,12 @@ impl PgConnection {
let format = if let Some(mut arguments) = arguments {
// Check this before we write anything to the stream.
let num_params = i16::try_from(arguments.len()).map_err(|_| {
//
// Note: Postgres actually interprets this value as unsigned,
// making the max number of parameters 65535, not 32767
// https://github.com/launchbadge/sqlx/issues/3464
// https://www.postgresql.org/docs/current/limits.html
let num_params = u16::try_from(arguments.len()).map_err(|_| {
err_protocol!(
"PgConnection::run(): too many arguments for query: {}",
arguments.len()

View File

@@ -3,6 +3,12 @@ use crate::message::{FrontendMessage, FrontendMessageFormat};
use crate::PgValueFormat;
use std::num::Saturating;
/// <https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-BIND>
///
/// ## Note:
///
/// The integer values for number of bind parameters, number of parameter format codes,
/// and number of result format codes all are interpreted as *unsigned*!
#[derive(Debug)]
pub struct Bind<'a> {
/// The ID of the destination portal (`PortalId::UNNAMED` selects the unnamed portal).
@@ -18,10 +24,11 @@ pub struct Bind<'a> {
/// parameters; or it can equal the actual number of parameters.
pub formats: &'a [PgValueFormat],
// Note: interpreted as unsigned, as is `formats.len()` and `result_formats.len()`
/// The number of parameters.
///
/// May be different from `formats.len()`
pub num_params: i16,
pub num_params: u16,
/// The value of each parameter, in the indicated format.
pub params: &'a [u8],
@@ -64,7 +71,11 @@ impl FrontendMessage for Bind<'_> {
buf.put_statement_name(self.statement);
let formats_len = i16::try_from(self.formats.len()).map_err(|_| {
// NOTE: the integer values for the number of parameters and format codes in this message
// are all interpreted as *unsigned*!
//
// https://github.com/launchbadge/sqlx/issues/3464
let formats_len = u16::try_from(self.formats.len()).map_err(|_| {
err_protocol!("too many parameter format codes ({})", self.formats.len())
})?;
@@ -78,7 +89,7 @@ impl FrontendMessage for Bind<'_> {
buf.extend(self.params);
let result_formats_len = i16::try_from(self.formats.len())
let result_formats_len = u16::try_from(self.formats.len())
.map_err(|_| err_protocol!("too many result format codes ({})", self.formats.len()))?;
buf.extend(result_formats_len.to_be_bytes());

View File

@@ -14,6 +14,8 @@ impl BackendMessage for ParameterDescription {
const FORMAT: BackendMessageFormat = BackendMessageFormat::ParameterDescription;
fn decode_body(mut buf: Bytes) -> Result<Self, Error> {
// Note: this is correct, max parameters is 65535, not 32767
// https://github.com/launchbadge/sqlx/issues/3464
let cnt = buf.get_u16();
let mut types = SmallVec::with_capacity(cnt as usize);

View File

@@ -43,7 +43,9 @@ impl FrontendMessage for Parse<'_> {
buf.put_str_nul(self.query);
let param_types_len = i16::try_from(self.param_types.len()).map_err(|_| {
// Note: actually interpreted as unsigned
// https://github.com/launchbadge/sqlx/issues/3464
let param_types_len = u16::try_from(self.param_types.len()).map_err(|_| {
err_protocol!(
"param_types.len() too large for binary protocol: {}",
self.param_types.len()