#![deny(unsafe_op_in_unsafe_fn)]
#[cfg(not(all(
any(target_os = "linux", target_os = "macos"),
any(target_arch = "x86_64", target_arch = "aarch64")
)))]
mod cee_scape {
#[cfg(not(feature = "cshim"))]
compile_error!("target platform cannot work without feature cshim");
use libc::{c_int, c_void};
use std::marker::PhantomData;
#[repr(C)]
pub struct SigJmpBufFields {
_internal: [u8; 0],
_neither_send_nor_sync: PhantomData<*const u8>,
}
pub fn call_with_sigsetjmp<F>(savemask: bool, mut callback: F) -> c_int
where
F: for<'a> FnOnce(&'a SigJmpBufFields) -> c_int,
{
unsafe extern "C-unwind" {
fn call_closure_with_sigsetjmp(
savemask: c_int,
closure_env_ptr: *mut c_void,
closure_code: unsafe extern "C-unwind" fn(
jbuf: *const SigJmpBufFields,
env_ptr: *mut c_void,
) -> c_int,
) -> c_int;
}
unsafe extern "C-unwind" fn call_from_c_to_rust<F>(
jbuf: *const SigJmpBufFields,
closure_env_ptr: *mut c_void,
) -> c_int
where
F: for<'a> FnOnce(&'a SigJmpBufFields) -> c_int,
{
let closure_env_ptr: *mut F = closure_env_ptr as *mut F;
unsafe { (closure_env_ptr.read())(&*jbuf) }
}
let savemask: libc::c_int = if savemask { 1 } else { 0 };
unsafe {
let closure_env_ptr = core::ptr::addr_of_mut!(callback);
core::mem::forget(callback);
call_closure_with_sigsetjmp(
savemask,
closure_env_ptr as *mut libc::c_void,
call_from_c_to_rust::<F>,
)
}
}
}
use cee_scape::{SigJmpBufFields, call_with_sigsetjmp};
use crate as pg_sys;
use crate::panic::{CaughtError, ErrorReport, ErrorReportLocation, ErrorReportWithLevel};
use core::ffi::CStr;
use std::mem::MaybeUninit;
#[inline(always)]
#[track_caller]
pub unsafe fn pg_guard_ffi_boundary<T, F: FnOnce() -> T>(f: F) -> T {
unsafe { pg_guard_ffi_boundary_impl(f) }
}
#[allow(clippy::missing_transmute_annotations)]
#[inline(always)]
#[track_caller]
unsafe fn pg_guard_ffi_boundary_impl<T, F: FnOnce() -> T>(f: F) -> T {
super::thread_check::check_active_thread();
unsafe {
let caller_memxct = pg_sys::CurrentMemoryContext;
let prev_exception_stack = pg_sys::PG_exception_stack;
let prev_error_context_stack = pg_sys::error_context_stack;
let mut result: std::mem::MaybeUninit<T> = MaybeUninit::uninit();
let jump_value = call_with_sigsetjmp(false, |jump_buffer| {
pg_sys::PG_exception_stack = std::mem::transmute(jump_buffer as *const SigJmpBufFields);
result.write(f());
0
});
if jump_value == 0 {
pg_sys::PG_exception_stack = prev_exception_stack;
pg_sys::error_context_stack = prev_error_context_stack;
result.assume_init()
} else {
pg_sys::CurrentMemoryContext = caller_memxct;
let errdata_ptr = pg_sys::CopyErrorData();
let errdata = errdata_ptr.as_ref().unwrap_unchecked();
let level = errdata.elevel.into();
let sqlerrcode = errdata.sqlerrcode.into();
let message = if errdata.message.is_null() {
String::from("<null error message>")
} else {
CStr::from_ptr(errdata.message).to_string_lossy().to_string()
};
let domain = if errdata.domain.is_null() {
None
} else {
{ Some(CStr::from_ptr(errdata.domain).to_string_lossy().to_string()) }
};
let detail = if errdata.detail.is_null() {
None
} else {
{ Some(CStr::from_ptr(errdata.detail).to_string_lossy().to_string()) }
};
let hint = if errdata.hint.is_null() {
None
} else {
{ Some(CStr::from_ptr(errdata.hint).to_string_lossy().to_string()) }
};
let funcname = if errdata.funcname.is_null() {
None
} else {
{ Some(CStr::from_ptr(errdata.funcname).to_string_lossy().to_string()) }
};
let file = if errdata.filename.is_null() {
String::from("<null filename>")
} else {
CStr::from_ptr(errdata.filename).to_string_lossy().to_string()
};
let line = errdata.lineno as _;
pg_sys::FreeErrorData(errdata_ptr);
pg_sys::FlushErrorState();
pg_sys::PG_exception_stack = prev_exception_stack;
pg_sys::error_context_stack = prev_error_context_stack;
std::panic::panic_any(CaughtError::PostgresError(ErrorReportWithLevel {
level,
inner: ErrorReport {
sqlerrcode,
message: message.into(),
detail,
hint,
domain,
location: ErrorReportLocation { file, funcname, line, col: 0, backtrace: None },
},
}))
}
}
}