fraiseql_wire/json/
validate.rs1use crate::protocol::{BackendMessage, FieldDescription};
4use crate::util::oid::is_json_oid;
5use crate::{Result, WireError};
6
7pub fn validate_row_description(msg: &BackendMessage) -> Result<()> {
15 let fields = match msg {
16 BackendMessage::RowDescription(fields) => fields,
17 _ => return Err(WireError::Protocol("expected RowDescription".into())),
18 };
19
20 if fields.len() != 1 {
22 return Err(WireError::InvalidSchema(format!(
23 "expected 1 column, got {}",
24 fields.len()
25 )));
26 }
27
28 let field = &fields[0];
29
30 if field.name != "data" {
32 return Err(WireError::InvalidSchema(format!(
33 "expected column named 'data', got '{}'",
34 field.name
35 )));
36 }
37
38 if !is_json_oid(field.type_oid) {
40 return Err(WireError::InvalidSchema(format!(
41 "expected json/jsonb type, got OID {}",
42 field.type_oid
43 )));
44 }
45
46 Ok(())
47}
48
49pub fn extract_field_description(msg: &BackendMessage) -> Result<FieldDescription> {
55 let fields = match msg {
56 BackendMessage::RowDescription(fields) => fields,
57 _ => return Err(WireError::Protocol("expected RowDescription".into())),
58 };
59
60 Ok(fields[0].clone())
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66 use crate::util::oid::JSON_OID;
67
68 #[test]
69 fn test_valid_row_description() {
70 let field = FieldDescription {
71 name: "data".to_string(),
72 table_oid: 0,
73 column_attr: 0,
74 type_oid: JSON_OID,
75 type_size: -1,
76 type_modifier: -1,
77 format_code: 0,
78 };
79
80 let msg = BackendMessage::RowDescription(vec![field]);
81 validate_row_description(&msg)
82 .unwrap_or_else(|e| panic!("expected Ok for valid RowDescription: {e}"));
83 }
84
85 #[test]
86 fn test_wrong_column_name() {
87 let field = FieldDescription {
88 name: "wrong".to_string(),
89 table_oid: 0,
90 column_attr: 0,
91 type_oid: JSON_OID,
92 type_size: -1,
93 type_modifier: -1,
94 format_code: 0,
95 };
96
97 let msg = BackendMessage::RowDescription(vec![field]);
98 let result = validate_row_description(&msg);
99 assert!(
100 matches!(result, Err(WireError::InvalidSchema(_))),
101 "expected InvalidSchema error for wrong column name, got: {result:?}"
102 );
103 }
104
105 #[test]
106 fn test_wrong_type() {
107 let field = FieldDescription {
108 name: "data".to_string(),
109 table_oid: 0,
110 column_attr: 0,
111 type_oid: 23, type_size: 4,
113 type_modifier: -1,
114 format_code: 0,
115 };
116
117 let msg = BackendMessage::RowDescription(vec![field]);
118 let result = validate_row_description(&msg);
119 assert!(
120 matches!(result, Err(WireError::InvalidSchema(_))),
121 "expected InvalidSchema error for wrong type OID, got: {result:?}"
122 );
123 }
124
125 #[test]
126 fn test_multiple_columns() {
127 let field1 = FieldDescription {
128 name: "data".to_string(),
129 table_oid: 0,
130 column_attr: 0,
131 type_oid: JSON_OID,
132 type_size: -1,
133 type_modifier: -1,
134 format_code: 0,
135 };
136 let field2 = field1.clone();
137
138 let msg = BackendMessage::RowDescription(vec![field1, field2]);
139 let result = validate_row_description(&msg);
140 assert!(
141 matches!(result, Err(WireError::InvalidSchema(_))),
142 "expected InvalidSchema error for multiple columns, got: {result:?}"
143 );
144 }
145}