use crate::convert::ConvertNode;
use crate::PgParserError;
use lazy_static::lazy_static;
use std::sync::Mutex;
lazy_static! {
static ref PARSER_LOCK: Mutex<()> = Mutex::new(());
static ref ONETIME_SETUP: () = {
unsafe {
crate::sys::MemoryContextInit();
}
};
}
pub fn parse_query(statements: &str) -> std::result::Result<Vec<crate::Node>, PgParserError> {
#[cfg(target_os = "linux")]
extern "C" {
#[link_name = "__sigsetjmp"]
pub fn sigsetjmp(
env: *mut crate::sys::sigjmp_buf,
savemask: std::os::raw::c_int,
) -> std::os::raw::c_int;
}
#[cfg(target_os = "macos")]
extern "C" {
pub fn sigsetjmp(
env: *mut crate::sys::sigjmp_buf,
savemask: std::os::raw::c_int,
) -> std::os::raw::c_int;
}
unsafe fn raw_parser_wrapper(
str: *const std::os::raw::c_char,
) -> Result<*mut crate::sys::List, PgParserError> {
let prev_exception_stack = crate::sys::PG_exception_stack;
let prev_error_context_stack = crate::sys::error_context_stack;
let mut jmp_buff = std::mem::MaybeUninit::uninit();
let jump_value = sigsetjmp(jmp_buff.as_mut_ptr(), 0);
if jump_value == 0 {
crate::sys::PG_exception_stack = jmp_buff.as_mut_ptr();
Ok(crate::sys::raw_parser(str))
} else {
crate::sys::PG_exception_stack = prev_exception_stack;
crate::sys::error_context_stack = prev_error_context_stack;
let error_data_ptr = crate::sys::CopyErrorData();
let error_data = error_data_ptr
.as_ref()
.expect("CopyErrorData returned null");
let result = if error_data.message.is_null() {
PgParserError::UnknownParseError
} else {
let message = std::ffi::CStr::from_ptr(error_data.message);
let cursor_pos = error_data.cursorpos;
PgParserError::ParseError {
message: message
.to_str()
.expect("failed to convert parse error message into a &str")
.to_string(),
cursor_pos,
}
};
crate::sys::FreeErrorData(error_data_ptr);
crate::sys::FlushErrorState();
Err(result)
}
}
let _mutex = PARSER_LOCK.lock();
let _ = *ONETIME_SETUP;
let (memory_context, old_context) = unsafe {
assert_eq!(
crate::sys::CurrentMemoryContext,
crate::sys::TopMemoryContext
);
let our_context = crate::sys::AllocSetContextCreateInternal(
crate::sys::TopMemoryContext,
std::ffi::CStr::from_bytes_with_nul(b"parser context\0")
.unwrap()
.as_ptr(),
crate::sys::ALLOCSET_DEFAULT_MINSIZE as crate::sys::Size,
crate::sys::ALLOCSET_DEFAULT_INITSIZE as crate::sys::Size,
crate::sys::ALLOCSET_DEFAULT_MAXSIZE as crate::sys::Size,
);
let old_context = crate::sys::CurrentMemoryContext;
crate::sys::CurrentMemoryContext = our_context;
(our_context, old_context)
};
let result = match std::ffi::CString::new(statements) {
Ok(c_str) => match unsafe { raw_parser_wrapper(c_str.as_ptr()) } {
Ok(parse_list) => {
if parse_list.is_null() {
Ok(Vec::new())
} else {
match unsafe { parse_list.as_ref().unwrap().convert() } {
crate::nodes::Node::List(vec) => {
let mut raw_statements = Vec::new();
let mut err = false;
for node in vec {
match node {
crate::Node::RawStmt(mut rawstmt) => {
raw_statements.push(*rawstmt.stmt.take().unwrap())
}
_ => err = true,
}
}
if err {
Err(PgParserError::NotARawStmt)
} else {
Ok(raw_statements)
}
}
_ => Err(PgParserError::NotAList),
}
}
}
Err(e) => Err(e),
},
Err(_) => Err(PgParserError::InternalNull),
};
unsafe {
crate::sys::MemoryContextReset(memory_context);
crate::sys::CurrentMemoryContext = old_context;
}
result
}