- 
                Notifications
    You must be signed in to change notification settings 
- Fork 30
draft; add bytea support #128
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
base: master
Are you sure you want to change the base?
Changes from all commits
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 | 
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| use std::ops::Deref; | ||
| use std::ptr::NonNull; | ||
|  | ||
| use crate::native::VarLenA; | ||
| use crate::pg_alloc::{PgAllocated, PgAllocator}; | ||
| use crate::pg_sys; | ||
|  | ||
| /// A zero-overhead view of `bytea` data from Postgres | ||
| pub struct ByteA<'mc>(PgAllocated<'mc, NonNull<pg_sys::bytea>>); | ||
|  | ||
| // :consider would be good to make a derive(FromVarLenA) macro. | ||
| impl<'mc> ByteA<'mc> { | ||
| /// Create from the raw pointer to the Postgres data | ||
| #[allow(clippy::missing_safety_doc)] | ||
| pub unsafe fn from_raw(alloc: &'mc PgAllocator, raw_ptr: *mut pg_sys::bytea) -> Self { | ||
| ByteA(PgAllocated::from_raw(alloc, raw_ptr)) | ||
| } | ||
|  | ||
| /// Convert into the underlying pointer | ||
| #[allow(clippy::missing_safety_doc)] | ||
| pub unsafe fn into_ptr(mut self) -> *mut pg_sys::bytea { | ||
| self.0.take_ptr() | ||
| } | ||
|  | ||
| /// Return true if this is empty | ||
| pub fn is_empty(&self) -> bool { | ||
| self.len() == 0 | ||
| } | ||
|  | ||
| /// Return the length of the bytea data | ||
| pub fn len(&self) -> usize { | ||
| let varlena = unsafe { VarLenA::from_varlena(self.0.as_ref()) }; | ||
| varlena.len() | ||
| } | ||
| } | ||
|  | ||
| impl<'mc> Deref for ByteA<'mc> { | ||
| type Target = [u8]; | ||
|  | ||
| fn deref(&self) -> &[u8] { | ||
| unsafe { | ||
| let varlena = VarLenA::from_varlena(self.0.as_ref()); | ||
| &*(varlena.as_slice() as *const [i8] as *const [u8]) | ||
| } | ||
| } | ||
| } | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -11,8 +11,9 @@ use std::ffi::{CStr, CString}; | |
| use std::marker::PhantomData; | ||
| use std::os::raw::c_char; | ||
| use std::ptr::NonNull; | ||
| use std::convert::TryInto; | ||
|  | ||
| use crate::native::Text; | ||
| use crate::native::{ByteA, Text, VarLenA}; | ||
| use crate::pg_alloc::{PgAllocated, PgAllocator}; | ||
| use crate::pg_bool; | ||
| use crate::pg_sys::{self, Datum}; | ||
|  | @@ -211,6 +212,111 @@ impl<'s> TryFromPgDatum<'s> for String { | |
| } | ||
| } | ||
|  | ||
| // FIXME: this impl is copy-pasta'd from that of Text. Could dedup with macro. | ||
| impl<'s> From<ByteA<'s>> for PgDatum<'s> { | ||
| fn from(value: ByteA<'s>) -> Self { | ||
| let ptr = unsafe { value.into_ptr() }; | ||
| PgDatum(Some(ptr as Datum), PhantomData) | ||
| } | ||
| } | ||
|  | ||
| // FIXME: this impl is copy-pasta'd from that of Text. Could dedup with macro. | ||
| impl<'s> TryFromPgDatum<'s> for ByteA<'s> { | ||
| fn try_from<'mc>( | ||
| memory_context: &'mc PgAllocator, | ||
| datum: PgDatum<'mc>, | ||
| ) -> Result<Self, &'static str> | ||
| where | ||
| Self: 's, | ||
| 'mc: 's, | ||
| { | ||
| if let Some(datum) = datum.0 { | ||
| let text_ptr = datum as *const pg_sys::bytea; | ||
|  | ||
| unsafe { Ok(ByteA::from_raw(memory_context, text_ptr as *mut _)) } | ||
| } else { | ||
| Err("datum was NULL") | ||
| } | ||
| } | ||
| } | ||
|  | ||
| impl<'s> TryFromPgDatum<'s> for &[u8] { | ||
| fn try_from<'mc>( | ||
| memory_context: &'mc PgAllocator, | ||
| datum: PgDatum<'mc>, | ||
| ) -> Result<Self, &'static str> | ||
| where | ||
| Self: 's, | ||
| 'mc: 's, | ||
| { | ||
| let ba = <ByteA<'mc> as TryFromPgDatum>::try_from(memory_context, datum)?; | ||
| let v: &[u8] = &ba; | ||
| // FIXME: transmutes to extend the lifetime to ~ 'mc. | ||
| // there is probably a better way to do this? | ||
| let mv: &[u8] = unsafe{ std::mem::transmute(v) }; | ||
| Ok(mv) | ||
| } | ||
| } | ||
|  | ||
| // :note could just use T: AsRef<[u8]> if specialization existed. | ||
| // as it is, too many types implement AsRef<[u8]>, so we | ||
| // manually dispatch to &[u8] impl. | ||
| impl From<Vec<u8>> for PgDatum<'_> { | ||
| fn from(value: Vec<u8>) -> Self { | ||
| let v: &[u8] = &value; | ||
| v.into() | ||
| } | ||
| } | ||
|  | ||
| // maybe macro is overkill. | ||
| macro_rules! _sizeof_varattrib_4b_header { () => { 4 }; } | ||
|  | ||
| impl From<&[u8]> for PgDatum<'_> { | ||
| fn from(value: &[u8]) -> Self { | ||
| let ptr = unsafe{ | ||
| // :consider should palloc be guard_pg'd? | ||
| 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. yes, all calls (or blocks of calls) into PG functions should be guarded. | ||
| pg_sys::palloc0(value.len() + _sizeof_varattrib_4b_header!()) as *mut u8 | ||
| }; | ||
|  | ||
| if ptr.is_null() { | ||
| // :fixme no alloc; better freak out properly! | ||
| unimplemented!() | ||
| } | ||
|  | ||
| // :note vis. alignment, palloc docs says always 4-word aligned. | ||
| unsafe { | ||
| std::ptr::copy_nonoverlapping( | ||
| value.as_ptr(), | ||
| ptr.offset(_sizeof_varattrib_4b_header!()), | ||
| value.len() | ||
| ); | ||
| } | ||
|  | ||
| // assign header length | ||
| // :note assumes little-endian | ||
| let mut l: u32 = ( | ||
| value.len() + _sizeof_varattrib_4b_header!() | ||
| ).try_into().unwrap(); | ||
| l &= 0x3fffffff; | ||
| l <<= 2; | ||
|  | ||
| unsafe { | ||
| 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. This all feels like it could be captured into a generic allocate and align function, maybe? | ||
| let ptr_len = ptr as *mut u32; | ||
| *ptr_len = l; | ||
| } | ||
|  | ||
| debug_assert!( | ||
| unsafe { | ||
| let varl = VarLenA::from_varlena(std::mem::transmute(ptr)); | ||
| value.len() == varl.len() | ||
| }, | ||
| "length mismatch on varlena encoded byte slice." | ||
| ); | ||
|  | ||
| PgDatum(Some(ptr as Datum), PhantomData) | ||
| } | ||
| } | ||
|  | ||
| // FIXME: this lifetime is wrong | ||
| impl From<String> for PgDatum<'_> { | ||
| fn from(value: String) -> Self { | ||
|  | ||
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.
I think we want
&'s [u8]here, right? and then we should make sure that'sis bound by'mc, right? in other words, this byte array should be bounded by the lifetime of the memory context that encapsulates it.