use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use prost::Message;
use crate::bindings::*;
use crate::error::*;
use crate::options::{DeparseComment, DeparseOptions, FingerprintOptions, ParserOptions};
use crate::parse_result::ParseResult;
use crate::protobuf;
pub struct Fingerprint {
pub value: u64,
pub hex: String,
}
pub fn parse(statement: &str, parser_options: impl Into<ParserOptions>) -> Result<ParseResult> {
let input = CString::new(statement)?;
let result = unsafe { pg_query_parse_protobuf_opts(input.as_ptr(), parser_options.into().bits()) };
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, options: DeparseOptions) -> 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 comment_strings = options.comments.iter().map(|comment| CString::new(comment.text.as_str())).collect::<std::result::Result<Vec<_>, _>>()?;
let mut comment_structs = options
.comments
.iter()
.zip(comment_strings.iter())
.map(|(comment, string)| PostgresDeparseComment {
match_location: comment.match_location,
newlines_before_comment: comment.newlines_before_comment,
newlines_after_comment: comment.newlines_after_comment,
str_: string.as_ptr() as *mut c_char,
})
.collect::<Vec<_>>();
let mut comment_ptrs = comment_structs.iter_mut().map(|comment| comment as *mut _).collect::<Vec<_>>();
let opts = PostgresDeparseOpts {
comments: if comment_ptrs.is_empty() { std::ptr::null_mut() } else { comment_ptrs.as_mut_ptr() },
comment_count: comment_ptrs.len(),
pretty_print: options.pretty_print,
indent_size: options.indent_size,
max_line_length: options.max_line_length,
trailing_newline: options.trailing_newline,
commas_start_of_line: options.commas_start_of_line,
};
let result = unsafe { pg_query_deparse_protobuf_opts(protobuf, opts) };
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 deparse_comments_for_query(query: &str) -> Result<Vec<DeparseComment>> {
let input = CString::new(query)?;
let result = unsafe { pg_query_deparse_comments_for_query(input.as_ptr()) };
let comments = 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 comments = if result.comment_count == 0 {
Vec::new()
} else {
unsafe { std::slice::from_raw_parts(result.comments, result.comment_count) }
.iter()
.map(|comment| {
let comment = unsafe { &**comment };
DeparseComment {
match_location: comment.match_location,
newlines_before_comment: comment.newlines_before_comment,
newlines_after_comment: comment.newlines_after_comment,
text: unsafe { CStr::from_ptr(comment.str_) }.to_string_lossy().to_string(),
}
})
.collect::<Vec<_>>()
};
Ok(comments)
};
unsafe { pg_query_free_deparse_comments_result(result) };
comments
}
pub fn normalize(statement: &str) -> Result<String> {
let input = CString::new(statement)?;
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, parser_options: impl Into<ParserOptions>, fingerprint_options: impl Into<FingerprintOptions>,
) -> Result<Fingerprint> {
let input = CString::new(statement)?;
let result = unsafe { pg_query_fingerprint_opts(input.as_ptr(), parser_options.into().bits(), fingerprint_options.into().bits()) };
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 scan_tokens(sql: &str) -> Result<Vec<protobuf::ScanToken>> {
let input = CString::new(sql)?;
let result = unsafe { pg_query_scan_tokens(input.as_ptr()) };
let tokens = if !result.error.is_null() {
let message = unsafe { CStr::from_ptr((*result.error).message) }.to_string_lossy().to_string();
Err(Error::Scan(message))
} else {
Ok(unsafe { std::slice::from_raw_parts(result.tokens, result.n_tokens as usize) }
.iter()
.map(|token| protobuf::ScanToken {
start: token.start,
end: token.end,
token: token.token as i32,
keyword_kind: token.keyword_kind as i32,
})
.collect::<Vec<_>>())
};
unsafe { pg_query_free_scan_tokens_result(result) };
tokens
}
pub fn is_utility_stmt(query: &str) -> Result<Vec<bool>> {
let input = CString::new(query)?;
let result = unsafe { pg_query_is_utility_stmt(input.as_ptr()) };
let utilities = if !result.error.is_null() {
let message = unsafe { CStr::from_ptr((*result.error).message) }.to_string_lossy().to_string();
Err(Error::IsUtility(message))
} else {
Ok(unsafe { std::slice::from_raw_parts(result.items, result.length as usize) }.to_vec())
};
unsafe { pg_query_free_is_utility_result(result) };
utilities
}
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
}