Skip to main content

cufflink_types/
sql_parse.rs

1//! The `sql_parse` host function's answer: PostgreSQL's parse tree of a
2//! statement, and the identity of the parser that made it.
3
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6
7/// A statement's parse tree, as `pg_query`'s protobuf serialised by serde.
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9pub struct Parsed {
10    pub tree: Value,
11    /// The `pg_query` release that parsed it.
12    pub parser: String,
13    /// The SHA-256 of the release's probe corpus as this parser reads it.
14    pub fingerprint: String,
15}
16
17/// Why a statement has no tree. No message quotes the statement.
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19#[serde(tag = "kind", content = "message", rename_all = "snake_case")]
20pub enum ParseError {
21    Syntax(String),
22    TooLarge(String),
23    Unavailable(String),
24}
25
26impl std::fmt::Display for ParseError {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        match self {
29            Self::Syntax(message) => write!(f, "syntax: {message}"),
30            Self::TooLarge(message) => write!(f, "too large: {message}"),
31            Self::Unavailable(message) => write!(f, "unavailable: {message}"),
32        }
33    }
34}
35
36impl std::error::Error for ParseError {}
37
38impl ParseError {
39    pub fn syntax() -> Self {
40        Self::Syntax("the statement is not valid SQL".into())
41    }
42
43    pub fn unavailable() -> Self {
44        Self::Unavailable("the SQL parser is unavailable".into())
45    }
46}
47
48/// What the host function answers: `{"ok": {…}}` or `{"error": {…}}`.
49#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
50#[serde(rename_all = "snake_case")]
51pub enum ParseResponse {
52    Ok(Parsed),
53    Error(ParseError),
54}
55
56impl From<Result<Parsed, ParseError>> for ParseResponse {
57    fn from(result: Result<Parsed, ParseError>) -> Self {
58        result.map_or_else(Self::Error, Self::Ok)
59    }
60}
61
62impl From<ParseResponse> for Result<Parsed, ParseError> {
63    fn from(response: ParseResponse) -> Self {
64        match response {
65            ParseResponse::Ok(parsed) => Ok(parsed),
66            ParseResponse::Error(error) => Err(error),
67        }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use serde_json::json;
75
76    #[test]
77    fn answers_match_the_wire_shapes() {
78        let parsed = Parsed {
79            tree: json!({"version": 170007, "stmts": []}),
80            parser: "6.2.1".into(),
81            fingerprint: "ab".into(),
82        };
83        for (result, wire) in [
84            (
85                Ok(parsed.clone()),
86                json!({"ok": {"tree": {"version": 170007, "stmts": []}, "parser": "6.2.1", "fingerprint": "ab"}}),
87            ),
88            (
89                Err(ParseError::syntax()),
90                json!({"error": {"kind": "syntax", "message": "the statement is not valid SQL"}}),
91            ),
92            (
93                Err(ParseError::TooLarge("over".into())),
94                json!({"error": {"kind": "too_large", "message": "over"}}),
95            ),
96            (
97                Err(ParseError::unavailable()),
98                json!({"error": {"kind": "unavailable", "message": "the SQL parser is unavailable"}}),
99            ),
100        ] {
101            let response = ParseResponse::from(result.clone());
102            assert_eq!(serde_json::to_value(&response).expect("encode"), wire);
103            let back: ParseResponse = serde_json::from_value(wire).expect("decode");
104            assert_eq!(Result::<Parsed, ParseError>::from(back), result);
105        }
106        assert!(serde_json::from_value::<ParseResponse>(
107            json!({"error": {"kind": "fault", "message": "x"}})
108        )
109        .is_err());
110    }
111}