-
Notifications
You must be signed in to change notification settings - Fork 42
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
feat: Placeholders in prepared statements #678
Changes from 7 commits
7f7116d
8a3d22a
23d375f
81f2796
d69bec9
100e2d8
795e3d6
f7c67f4
cd9ba52
d57696b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,4 +3,5 @@ pub mod format; | |
pub mod oid; | ||
pub mod types; | ||
|
||
mod reader; | ||
mod writer; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
use crate::error::{PgReprError, Result}; | ||
use std::str::FromStr; | ||
|
||
/// Reader defines the interface for the different kinds of values that can be | ||
/// decoded as a postgres type. | ||
pub(crate) trait Reader { | ||
fn read_bool(buf: &[u8]) -> Result<bool>; | ||
|
||
fn read_int2(buf: &[u8]) -> Result<i16>; | ||
fn read_int4(buf: &[u8]) -> Result<i32>; | ||
fn read_int8(buf: &[u8]) -> Result<i64>; | ||
fn read_float4(buf: &[u8]) -> Result<f32>; | ||
fn read_float8(buf: &[u8]) -> Result<f64>; | ||
|
||
fn read_text(buf: &[u8]) -> Result<String>; | ||
} | ||
|
||
#[derive(Debug)] | ||
pub(crate) struct TextReader; | ||
|
||
impl TextReader { | ||
fn parse<E: std::error::Error + Sync + Send + 'static, F: FromStr<Err = E>>( | ||
buf: &[u8], | ||
) -> Result<F> { | ||
std::str::from_utf8(buf)? | ||
.parse::<F>() | ||
.map_err(|e| PgReprError::ParseError(Box::new(e))) | ||
} | ||
} | ||
|
||
impl Reader for TextReader { | ||
fn read_bool(buf: &[u8]) -> Result<bool> { | ||
TextReader::parse::<_, SqlBool>(buf).map(|b| b.0) | ||
} | ||
|
||
fn read_int2(buf: &[u8]) -> Result<i16> { | ||
TextReader::parse(buf) | ||
} | ||
|
||
fn read_int4(buf: &[u8]) -> Result<i32> { | ||
TextReader::parse(buf) | ||
} | ||
|
||
fn read_int8(buf: &[u8]) -> Result<i64> { | ||
TextReader::parse(buf) | ||
} | ||
|
||
fn read_float4(buf: &[u8]) -> Result<f32> { | ||
TextReader::parse(buf) | ||
} | ||
|
||
fn read_float8(buf: &[u8]) -> Result<f64> { | ||
TextReader::parse(buf) | ||
} | ||
|
||
fn read_text(buf: &[u8]) -> Result<String> { | ||
TextReader::parse(buf) | ||
} | ||
} | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
#[error("String was not 't', 'true', 'f', or 'false'")] | ||
struct ParseSqlBoolError; | ||
|
||
struct SqlBool(bool); | ||
|
||
impl FromStr for SqlBool { | ||
type Err = ParseSqlBoolError; | ||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
match s { | ||
"true" | "t" => Ok(SqlBool(true)), | ||
"false" | "f" => Ok(SqlBool(false)), | ||
_ => Err(ParseSqlBoolError), | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn read_sql_bool() { | ||
let v = TextReader::read_bool("t".as_bytes()).unwrap(); | ||
assert!(v); | ||
|
||
let v = TextReader::read_bool("true".as_bytes()).unwrap(); | ||
assert!(v); | ||
|
||
let v = TextReader::read_bool("f".as_bytes()).unwrap(); | ||
assert!(!v); | ||
|
||
let v = TextReader::read_bool("false".as_bytes()).unwrap(); | ||
assert!(!v); | ||
|
||
let _ = TextReader::read_bool("none".as_bytes()).unwrap_err(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ use tokio_postgres::types::Type as PgType; | |
|
||
use crate::error::{PgReprError, Result}; | ||
use crate::format::Format; | ||
use crate::reader::{Reader, TextReader}; | ||
use crate::writer::{BinaryWriter, TextWriter, Writer}; | ||
|
||
/// Returns a compatible postgres type for the arrow datatype. | ||
|
@@ -64,6 +65,36 @@ pub fn encode_array_value( | |
Ok(()) | ||
} | ||
|
||
/// Decodes a scalar value using the provided format and arrow type. | ||
pub fn decode_scalar_value( | ||
buf: Option<&[u8]>, | ||
format: Format, | ||
arrow_type: &ArrowType, | ||
) -> Result<ScalarValue> { | ||
match buf { | ||
Some(buf) => match format { | ||
Format::Text => decode_not_null_value::<TextReader>(buf, arrow_type), | ||
Format::Binary => Err(PgReprError::BinaryReadUnimplemented), | ||
}, | ||
None => Ok(ScalarValue::Null), | ||
} | ||
} | ||
|
||
fn decode_not_null_value<R: Reader>(buf: &[u8], arrow_type: &ArrowType) -> Result<ScalarValue> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it make sense to do it like For eg: we will need to cast There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might? Postgres docs have this to say:
Currently I'm ignoring the oids being provided during parse and inferring everything. I think we'll want this function to accept something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should have the PG type even when inferred which then should be translated to arrow type There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Making that change right now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Latest change swaps out the arrow types with pg types. |
||
Ok(match arrow_type { | ||
&ArrowType::Boolean => R::read_bool(buf)?.into(), | ||
&ArrowType::Int8 => R::read_int2(buf)?.into(), | ||
&ArrowType::Int16 => R::read_int2(buf)?.into(), | ||
&ArrowType::Int32 => R::read_int4(buf)?.into(), | ||
&ArrowType::Int64 => R::read_int8(buf)?.into(), | ||
&ArrowType::Float16 => R::read_float4(buf)?.into(), | ||
&ArrowType::Float32 => R::read_float4(buf)?.into(), | ||
&ArrowType::Float64 => R::read_float8(buf)?.into(), | ||
&ArrowType::Utf8 => ScalarValue::Utf8(Some(R::read_text(buf)?)), | ||
other => return Err(PgReprError::UnsupportedArrowType(other.clone())), | ||
}) | ||
} | ||
|
||
/// Per writer implementation for encoding non-null array values. | ||
fn encode_array_not_null_value<W: Writer>( | ||
buf: &mut BytesMut, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a personal preference! No need to change for the PR :)