use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use prost::Message;
use crate::bindings::*;
use crate::error::*;
use crate::parse_result::ParseResult;
use crate::protobuf;
pub struct Fingerprint {
pub value: u64,
pub hex: String,
}
pub fn parse(statement: &str) -> Result<ParseResult> {
let input = CString::new(statement)?;
let result = unsafe { pg_query_parse_protobuf(input.as_ptr()) };
let parse_result = if !result.error.is_null() {
let message = unsafe { CStr::from_ptr((*result.error).message) }.to_string_lossy().to_string();
Err(Error::Parse(message))
} else {
let data = unsafe { std::slice::from_raw_parts(result.parse_tree.data as *const u8, result.parse_tree.len as usize) };
let stderr = unsafe { CStr::from_ptr(result.stderr_buffer) }.to_string_lossy().to_string();
protobuf::ParseResult::decode(data).map_err(Error::Decode).map(|result| ParseResult::new(result, stderr))
};
unsafe { pg_query_free_protobuf_parse_result(result) };
parse_result
}
pub fn deparse(protobuf: &protobuf::ParseResult) -> Result<String> {
let buffer = protobuf.encode_to_vec();
let len = buffer.len();
let data = buffer.as_ptr() as *const c_char as *mut c_char;
let protobuf = PgQueryProtobuf { data, len };
let result = unsafe { pg_query_deparse_protobuf(protobuf) };
let deparse_result = if !result.error.is_null() {
let message = unsafe { CStr::from_ptr((*result.error).message) }.to_string_lossy().to_string();
Err(Error::Parse(message))
} else {
let query = unsafe { CStr::from_ptr(result.query) }.to_string_lossy().to_string();
Ok(query)
};
unsafe { pg_query_free_deparse_result(result) };
deparse_result
}
pub fn normalize(statement: &str) -> Result<String> {
let input = CString::new(statement).unwrap();
let result = unsafe { pg_query_normalize(input.as_ptr()) };
let normalized_query = if !result.error.is_null() {
let message = unsafe { CStr::from_ptr((*result.error).message) }.to_string_lossy().to_string();
Err(Error::Parse(message))
} else {
let n = unsafe { CStr::from_ptr(result.normalized_query) };
Ok(n.to_string_lossy().to_string())
};
unsafe { pg_query_free_normalize_result(result) };
normalized_query
}
pub fn fingerprint(statement: &str) -> Result<Fingerprint> {
let input = CString::new(statement)?;
let result = unsafe { pg_query_fingerprint(input.as_ptr()) };
let fingerprint = if !result.error.is_null() {
let message = unsafe { CStr::from_ptr((*result.error).message) }.to_string_lossy().to_string();
Err(Error::Parse(message))
} else {
let hex = unsafe { CStr::from_ptr(result.fingerprint_str) };
Ok(Fingerprint { value: result.fingerprint, hex: hex.to_string_lossy().to_string() })
};
unsafe { pg_query_free_fingerprint_result(result) };
fingerprint
}
pub fn parse_plpgsql(stmt: &str) -> Result<serde_json::Value> {
let input = CString::new(stmt)?;
let result = unsafe { pg_query_parse_plpgsql(input.as_ptr()) };
let structure = if !result.error.is_null() {
let message = unsafe { CStr::from_ptr((*result.error).message) }.to_string_lossy().to_string();
Err(Error::Parse(message))
} else {
let raw = unsafe { CStr::from_ptr(result.plpgsql_funcs) };
serde_json::from_str(&raw.to_string_lossy()).map_err(|e| Error::InvalidJson(e.to_string()))
};
unsafe { pg_query_free_plpgsql_parse_result(result) };
structure
}
pub fn split_with_parser(query: &str) -> Result<Vec<&str>> {
let input = CString::new(query)?;
let result = unsafe { pg_query_split_with_parser(input.as_ptr()) };
let split_result = if !result.error.is_null() {
let message = unsafe { CStr::from_ptr((*result.error).message) }.to_string_lossy().to_string();
Err(Error::Split(message))
} else {
let n_stmts = result.n_stmts as usize;
let mut statements = Vec::with_capacity(n_stmts);
for offset in 0..n_stmts {
let split_stmt = unsafe { *result.stmts.add(offset).read() };
let start = split_stmt.stmt_location as usize;
let end = start + split_stmt.stmt_len as usize;
statements.push(&query[start..end]);
}
Ok(statements)
};
unsafe { pg_query_free_split_result(result) };
split_result
}
pub fn scan(sql: &str) -> Result<protobuf::ScanResult> {
let input = CString::new(sql)?;
let result = unsafe { pg_query_scan(input.as_ptr()) };
let scan_result = if !result.error.is_null() {
let message = unsafe { CStr::from_ptr((*result.error).message) }.to_string_lossy().to_string();
Err(Error::Scan(message))
} else {
let data = unsafe { std::slice::from_raw_parts(result.pbuf.data as *const u8, result.pbuf.len as usize) };
protobuf::ScanResult::decode(data).map_err(Error::Decode)
};
unsafe { pg_query_free_scan_result(result) };
scan_result
}
pub fn split_with_scanner(query: &str) -> Result<Vec<&str>> {
let input = CString::new(query)?;
let result = unsafe { pg_query_split_with_scanner(input.as_ptr()) };
let split_result = if !result.error.is_null() {
let message = unsafe { CStr::from_ptr((*result.error).message) }.to_string_lossy().to_string();
Err(Error::Split(message))
} else {
let n_stmts = result.n_stmts as usize;
let mut start: usize;
let mut end: usize;
let mut statements = Vec::with_capacity(n_stmts);
for offset in 0..n_stmts {
let split_stmt = unsafe { *result.stmts.add(offset).read() };
start = split_stmt.stmt_location as usize;
end = start + split_stmt.stmt_len as usize;
statements.push(&query[start..end]);
}
Ok(statements)
};
unsafe { pg_query_free_split_result(result) };
split_result
}