krishiv_sql/
streaming_table_ddl.rs1#![forbid(unsafe_code)]
2#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct StreamingTableDdl {
22 pub name: String,
24 pub query: String,
26 pub or_replace: bool,
28}
29
30pub fn parse_create_streaming_table(sql: &str) -> Option<StreamingTableDdl> {
33 let trimmed = sql.trim().trim_end_matches(';').trim();
34 let upper = trimmed.to_ascii_uppercase();
35 let (prefix, or_replace) = if upper.starts_with("CREATE OR REPLACE STREAMING TABLE ") {
36 ("CREATE OR REPLACE STREAMING TABLE ", true)
37 } else if upper.starts_with("CREATE STREAMING TABLE ") {
38 ("CREATE STREAMING TABLE ", false)
39 } else {
40 return None;
41 };
42 let rest = trimmed.get(prefix.len()..)?;
43 let as_pos = rest.to_ascii_uppercase().find(" AS ")?;
45 let name = rest.get(..as_pos)?.trim().to_string();
46 let query = rest.get(as_pos + 4..)?.trim().to_string();
47 if name.is_empty() || query.is_empty() {
48 return None;
49 }
50 Some(StreamingTableDdl {
51 name,
52 query,
53 or_replace,
54 })
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn parses_create_streaming_table() {
63 let ddl = parse_create_streaming_table(
64 "CREATE STREAMING TABLE clicks_1m AS \
65 SELECT k, COUNT(*) AS c FROM TUMBLE(TABLE clicks, DESCRIPTOR(ts), 60000) \
66 GROUP BY k, window_start, window_end",
67 )
68 .expect("recognised");
69 assert_eq!(ddl.name, "clicks_1m");
70 assert!(!ddl.or_replace);
71 assert!(ddl.query.to_uppercase().starts_with("SELECT"));
72 assert!(ddl.query.contains("TUMBLE"));
73 }
74
75 #[test]
76 fn parses_or_replace() {
77 let ddl = parse_create_streaming_table("CREATE OR REPLACE STREAMING TABLE t AS SELECT 1")
78 .expect("recognised");
79 assert_eq!(ddl.name, "t");
80 assert!(ddl.or_replace);
81 }
82
83 #[test]
84 fn rejects_non_streaming_table_ddl() {
85 assert!(parse_create_streaming_table("CREATE TABLE t AS SELECT 1").is_none());
86 assert!(parse_create_streaming_table("SELECT 1").is_none());
87 assert!(parse_create_streaming_table("CREATE MATERIALIZED VIEW v AS SELECT 1").is_none());
88 }
89
90 #[test]
91 fn requires_name_and_query() {
92 assert!(parse_create_streaming_table("CREATE STREAMING TABLE t").is_none());
93 assert!(parse_create_streaming_table("CREATE STREAMING TABLE AS SELECT 1").is_none());
94 }
95}