nodedb_sql/parser/array_stmt/
parse.rs1#[path = "parse_impl.rs"]
11mod parse_impl;
12
13use super::ast::ArrayStatement;
14use super::lexer::tokenize;
15use crate::error::Result;
16use parse_impl::Parser;
17
18pub fn try_parse_array_statement(sql: &str) -> Result<Option<ArrayStatement>> {
21 let trimmed = sql.trim_start();
22 let upper: String = trimmed
23 .chars()
24 .take(40)
25 .collect::<String>()
26 .to_ascii_uppercase();
27
28 if upper.starts_with("CREATE ARRAY ") || upper == "CREATE ARRAY" {
29 let toks = tokenize(trimmed)?;
30 let mut p = Parser::new(&toks);
31 p.expect_kw("CREATE")?;
32 p.expect_kw("ARRAY")?;
33 return Ok(Some(ArrayStatement::Create(p.parse_create()?)));
34 }
35 if upper.starts_with("DROP ARRAY ") || upper == "DROP ARRAY" {
36 let toks = tokenize(trimmed)?;
37 let mut p = Parser::new(&toks);
38 p.expect_kw("DROP")?;
39 p.expect_kw("ARRAY")?;
40 return Ok(Some(ArrayStatement::Drop(p.parse_drop()?)));
41 }
42 if upper.starts_with("INSERT INTO ARRAY ") {
43 let toks = tokenize(trimmed)?;
44 let mut p = Parser::new(&toks);
45 p.expect_kw("INSERT")?;
46 p.expect_kw("INTO")?;
47 p.expect_kw("ARRAY")?;
48 return Ok(Some(ArrayStatement::Insert(p.parse_insert()?)));
49 }
50 if upper.starts_with("DELETE FROM ARRAY ") {
51 let toks = tokenize(trimmed)?;
52 let mut p = Parser::new(&toks);
53 p.expect_kw("DELETE")?;
54 p.expect_kw("FROM")?;
55 p.expect_kw("ARRAY")?;
56 return Ok(Some(ArrayStatement::Delete(p.parse_delete()?)));
57 }
58 if upper.starts_with("ALTER ARRAY ") || upper == "ALTER ARRAY" {
59 let toks = tokenize(trimmed)?;
60 let mut p = Parser::new(&toks);
61 p.expect_kw("ALTER")?;
62 p.expect_kw("ARRAY")?;
63 return Ok(Some(ArrayStatement::Alter(p.parse_alter()?)));
64 }
65 Ok(None)
66}
67
68#[cfg(test)]
69mod tests {
70 use super::super::ast::ArrayStatement;
71 use super::*;
72 use crate::types_array::ArrayCellOrderAst;
73
74 #[test]
75 fn passthrough_non_array_sql() {
76 assert!(
77 try_parse_array_statement("SELECT * FROM t")
78 .unwrap()
79 .is_none()
80 );
81 assert!(
82 try_parse_array_statement("CREATE TABLE t (x INT)")
83 .unwrap()
84 .is_none()
85 );
86 assert!(
87 try_parse_array_statement("INSERT INTO foo VALUES (1)")
88 .unwrap()
89 .is_none()
90 );
91 }
92
93 #[test]
94 fn parse_create_array_full() {
95 let sql = "CREATE ARRAY genome \
96 DIMS (chrom INT64 [1..23], pos INT64 [0..300000000]) \
97 ATTRS (variant STRING, qual FLOAT64) \
98 TILE_EXTENTS (1, 1000000) \
99 CELL_ORDER HILBERT";
100 let stmt = try_parse_array_statement(sql).unwrap().unwrap();
101 match stmt {
102 ArrayStatement::Create(c) => {
103 assert_eq!(c.name, "genome");
104 assert_eq!(c.dims.len(), 2);
105 assert_eq!(c.attrs.len(), 2);
106 assert_eq!(c.tile_extents, vec![1, 1_000_000]);
107 assert_eq!(c.cell_order, ArrayCellOrderAst::Hilbert);
108 }
109 _ => panic!("wrong variant"),
110 }
111 }
112
113 #[test]
114 fn parse_drop_array_if_exists() {
115 let stmt = try_parse_array_statement("DROP ARRAY IF EXISTS g")
116 .unwrap()
117 .unwrap();
118 match stmt {
119 ArrayStatement::Drop(d) => {
120 assert!(d.if_exists);
121 assert_eq!(d.name, "g");
122 }
123 _ => panic!("wrong variant"),
124 }
125 }
126
127 #[test]
128 fn parse_insert_multi_row() {
129 let sql = "INSERT INTO ARRAY g \
130 COORDS (1, 100) VALUES ('SNP', 99.5), \
131 COORDS (1, 200) VALUES ('INS', 88.0)";
132 let stmt = try_parse_array_statement(sql).unwrap().unwrap();
133 match stmt {
134 ArrayStatement::Insert(i) => {
135 assert_eq!(i.name, "g");
136 assert_eq!(i.rows.len(), 2);
137 assert_eq!(i.rows[0].coords.len(), 2);
138 assert_eq!(i.rows[0].attrs.len(), 2);
139 }
140 _ => panic!("wrong variant"),
141 }
142 }
143
144 #[test]
145 fn parse_delete_coords_in() {
146 let sql = "DELETE FROM ARRAY g WHERE COORDS IN ((1, 100), (1, 200))";
147 let stmt = try_parse_array_statement(sql).unwrap().unwrap();
148 match stmt {
149 ArrayStatement::Delete(d) => {
150 assert_eq!(d.name, "g");
151 assert_eq!(d.coords.len(), 2);
152 }
153 _ => panic!("wrong variant"),
154 }
155 }
156
157 #[test]
158 fn create_rejects_unknown_dim_type() {
159 let sql = "CREATE ARRAY g DIMS (x BOGUS [0..10]) ATTRS (v INT64) TILE_EXTENTS (1)";
160 assert!(try_parse_array_statement(sql).is_err());
161 }
162
163 #[test]
164 fn parse_create_array_with_audit_retain() {
165 let sql = "CREATE ARRAY g \
166 DIMS (x INT64 [0..100]) \
167 ATTRS (v INT64) \
168 TILE_EXTENTS (10) \
169 WITH (audit_retain_ms = 86400000)";
170 let stmt = try_parse_array_statement(sql).unwrap().unwrap();
171 match stmt {
172 ArrayStatement::Create(c) => {
173 assert_eq!(c.audit_retain_ms, Some(86_400_000));
174 assert_eq!(c.minimum_audit_retain_ms, None);
175 assert_eq!(c.prefix_bits, 8);
176 }
177 _ => panic!("wrong variant"),
178 }
179 }
180
181 #[test]
182 fn parse_create_array_with_all_retention_keys() {
183 let sql = "CREATE ARRAY genomes \
184 DIMS (variant_id INT64 [0..1000000000], sample_id INT64 [0..100000]) \
185 ATTRS (gt INT64, dp INT64) \
186 TILE_EXTENTS (1024, 256) \
187 WITH (prefix_bits = 8, audit_retain_ms = 86400000, minimum_audit_retain_ms = 3600000)";
188 let stmt = try_parse_array_statement(sql).unwrap().unwrap();
189 match stmt {
190 ArrayStatement::Create(c) => {
191 assert_eq!(c.prefix_bits, 8);
192 assert_eq!(c.audit_retain_ms, Some(86_400_000));
193 assert_eq!(c.minimum_audit_retain_ms, Some(3_600_000));
194 }
195 _ => panic!("wrong variant"),
196 }
197 }
198
199 #[test]
200 fn parse_create_array_unknown_with_key_rejected() {
201 let sql = "CREATE ARRAY g \
202 DIMS (x INT64 [0..10]) \
203 ATTRS (v INT64) \
204 TILE_EXTENTS (1) \
205 WITH (bogus_key = 42)";
206 assert!(try_parse_array_statement(sql).is_err());
207 }
208
209 #[test]
210 fn parse_create_array_negative_retain_rejected() {
211 let sql = "CREATE ARRAY g \
212 DIMS (x INT64 [0..10]) \
213 ATTRS (v INT64) \
214 TILE_EXTENTS (1) \
215 WITH (audit_retain_ms = -1)";
216 assert!(try_parse_array_statement(sql).is_err());
217 }
218
219 #[test]
220 fn parse_alter_array_single_key() {
221 let sql = "ALTER ARRAY my_array SET (audit_retain_ms = 86400000)";
222 let stmt = try_parse_array_statement(sql).unwrap().unwrap();
223 match stmt {
224 ArrayStatement::Alter(a) => {
225 assert_eq!(a.name, "my_array");
226 assert_eq!(a.set.len(), 1);
227 assert_eq!(a.set[0].0, "audit_retain_ms");
228 assert_eq!(a.set[0].1, Some(86_400_000));
229 }
230 _ => panic!("expected Alter variant"),
231 }
232 }
233
234 #[test]
235 fn parse_alter_array_null_value() {
236 let sql = "ALTER ARRAY my_array SET (audit_retain_ms = NULL)";
237 let stmt = try_parse_array_statement(sql).unwrap().unwrap();
238 match stmt {
239 ArrayStatement::Alter(a) => {
240 assert_eq!(a.set[0].0, "audit_retain_ms");
241 assert_eq!(a.set[0].1, None);
242 }
243 _ => panic!("expected Alter variant"),
244 }
245 }
246
247 #[test]
248 fn parse_alter_array_multi_key() {
249 let sql = "ALTER ARRAY arr SET (audit_retain_ms = 5000, minimum_audit_retain_ms = 1000)";
250 let stmt = try_parse_array_statement(sql).unwrap().unwrap();
251 match stmt {
252 ArrayStatement::Alter(a) => {
253 assert_eq!(a.set.len(), 2);
254 assert!(
255 a.set
256 .iter()
257 .any(|(k, v)| k == "audit_retain_ms" && *v == Some(5000))
258 );
259 assert!(
260 a.set
261 .iter()
262 .any(|(k, v)| k == "minimum_audit_retain_ms" && *v == Some(1000))
263 );
264 }
265 _ => panic!("expected Alter variant"),
266 }
267 }
268
269 #[test]
270 fn parse_alter_array_unknown_key_rejected() {
271 let sql = "ALTER ARRAY arr SET (bogus_key = 42)";
272 assert!(try_parse_array_statement(sql).is_err());
273 }
274
275 #[test]
276 fn parse_alter_array_minimum_null_rejected() {
277 let sql = "ALTER ARRAY arr SET (minimum_audit_retain_ms = NULL)";
278 assert!(try_parse_array_statement(sql).is_err());
279 }
280
281 #[test]
282 fn parse_alter_not_matched_for_other_sql() {
283 assert!(
284 try_parse_array_statement("ALTER TABLE foo ADD COLUMN x INT")
285 .unwrap()
286 .is_none()
287 );
288 }
289}