nodedb_sql/parser/database_stmt/
parse.rs1use crate::ddl_ast::statement::NodedbStatement;
11use crate::error::SqlError;
12
13pub fn try_parse_database_statement(sql: &str) -> Result<Option<NodedbStatement>, SqlError> {
20 let trimmed = sql.trim();
21 if trimmed.is_empty() {
22 return Ok(None);
23 }
24 let upper = trimmed.to_uppercase();
25 let parts: Vec<&str> = trimmed.split_whitespace().collect();
26 if parts.is_empty() {
27 return Ok(None);
28 }
29 crate::ddl_ast::parse::database::try_parse(&upper, &parts, trimmed).transpose()
30}
31
32#[cfg(test)]
33mod tests {
34 use super::*;
35 use crate::ddl_ast::statement::{AlterDatabaseOperation, DatabaseStmt, NodedbStatement};
36
37 fn ok(sql: &str) -> NodedbStatement {
38 try_parse_database_statement(sql)
39 .expect("expected Ok")
40 .expect("expected Some")
41 }
42
43 #[test]
44 fn parse_create_database() {
45 match ok("CREATE DATABASE mydb") {
46 NodedbStatement::Database(DatabaseStmt::CreateDatabase {
47 name,
48 if_not_exists,
49 ..
50 }) => {
51 assert_eq!(name, "mydb");
52 assert!(!if_not_exists);
53 }
54 other => panic!("unexpected: {other:?}"),
55 }
56 }
57
58 #[test]
59 fn parse_create_database_if_not_exists() {
60 match ok("CREATE DATABASE IF NOT EXISTS mydb") {
61 NodedbStatement::Database(DatabaseStmt::CreateDatabase {
62 name,
63 if_not_exists,
64 ..
65 }) => {
66 assert_eq!(name, "mydb");
67 assert!(if_not_exists);
68 }
69 other => panic!("unexpected: {other:?}"),
70 }
71 }
72
73 #[test]
74 fn parse_drop_database() {
75 match ok("DROP DATABASE mydb CASCADE") {
76 NodedbStatement::Database(DatabaseStmt::DropDatabase { name, cascade, .. }) => {
77 assert_eq!(name, "mydb");
78 assert!(cascade);
79 }
80 other => panic!("unexpected: {other:?}"),
81 }
82 }
83
84 #[test]
85 fn parse_drop_database_if_exists() {
86 match ok("DROP DATABASE IF EXISTS mydb") {
87 NodedbStatement::Database(DatabaseStmt::DropDatabase {
88 name, if_exists, ..
89 }) => {
90 assert_eq!(name, "mydb");
91 assert!(if_exists);
92 }
93 other => panic!("unexpected: {other:?}"),
94 }
95 }
96
97 #[test]
98 fn parse_alter_database_rename() {
99 match ok("ALTER DATABASE mydb RENAME TO newdb") {
100 NodedbStatement::Database(DatabaseStmt::AlterDatabase { name, operation }) => {
101 assert_eq!(name, "mydb");
102 assert_eq!(
103 operation,
104 AlterDatabaseOperation::Rename {
105 new_name: "newdb".into()
106 }
107 );
108 }
109 other => panic!("unexpected: {other:?}"),
110 }
111 }
112
113 #[test]
114 fn parse_alter_database_set_quota() {
115 match ok("ALTER DATABASE mydb SET QUOTA (max_qps = 500)") {
116 NodedbStatement::Database(DatabaseStmt::AlterDatabase { name, operation }) => {
117 assert_eq!(name, "mydb");
118 match operation {
119 AlterDatabaseOperation::SetQuota(spec) => {
120 assert_eq!(spec.max_qps, Some(500));
121 }
122 other => panic!("expected SetQuota, got {other:?}"),
123 }
124 }
125 other => panic!("unexpected: {other:?}"),
126 }
127 }
128
129 #[test]
130 fn parse_alter_database_materialize() {
131 match ok("ALTER DATABASE mydb MATERIALIZE") {
132 NodedbStatement::Database(DatabaseStmt::AlterDatabase { operation, .. }) => {
133 assert_eq!(operation, AlterDatabaseOperation::Materialize);
134 }
135 other => panic!("unexpected: {other:?}"),
136 }
137 }
138
139 #[test]
140 fn parse_alter_database_promote() {
141 match ok("ALTER DATABASE mydb PROMOTE") {
142 NodedbStatement::Database(DatabaseStmt::AlterDatabase { operation, .. }) => {
143 assert_eq!(operation, AlterDatabaseOperation::Promote);
144 }
145 other => panic!("unexpected: {other:?}"),
146 }
147 }
148
149 #[test]
150 fn parse_show_databases() {
151 assert_eq!(
152 try_parse_database_statement("SHOW DATABASES")
153 .unwrap()
154 .unwrap(),
155 NodedbStatement::Database(DatabaseStmt::ShowDatabases)
156 );
157 }
158
159 #[test]
160 fn parse_use_database() {
161 match ok("USE DATABASE mydb") {
162 NodedbStatement::Database(DatabaseStmt::UseDatabase { name }) => {
163 assert_eq!(name, "mydb")
164 }
165 other => panic!("unexpected: {other:?}"),
166 }
167 }
168
169 #[test]
170 fn passthrough_non_database_sql() {
171 assert!(
172 try_parse_database_statement("SELECT * FROM t")
173 .unwrap()
174 .is_none()
175 );
176 assert!(
177 try_parse_database_statement("CREATE COLLECTION users")
178 .unwrap()
179 .is_none()
180 );
181 assert!(
182 try_parse_database_statement("INSERT INTO foo VALUES (1)")
183 .unwrap()
184 .is_none()
185 );
186 }
187
188 #[test]
189 fn parse_clone_database() {
190 use crate::ddl_ast::CloneAsOf;
191 match ok("CLONE DATABASE new_db FROM source_db") {
192 NodedbStatement::Database(DatabaseStmt::CloneDatabase {
193 new_name,
194 source_name,
195 as_of,
196 }) => {
197 assert_eq!(new_name, "new_db");
198 assert_eq!(source_name, "source_db");
199 assert_eq!(as_of, CloneAsOf::Latest);
200 }
201 other => panic!("unexpected: {other:?}"),
202 }
203 }
204
205 #[test]
206 fn parse_mirror_database() {
207 use crate::ddl_ast::MirrorMode;
208 match ok("MIRROR DATABASE replica FROM prod-us.source") {
209 NodedbStatement::Database(DatabaseStmt::MirrorDatabase {
210 local_name,
211 source_cluster,
212 source_database,
213 mode,
214 }) => {
215 assert_eq!(local_name, "replica");
216 assert_eq!(source_cluster, "prod-us");
217 assert_eq!(source_database, "source");
218 assert_eq!(mode, MirrorMode::Async);
219 }
220 other => panic!("unexpected: {other:?}"),
221 }
222 }
223
224 #[test]
225 fn parse_backup_database() {
226 match ok("BACKUP DATABASE mydb TO 's3://bucket/path'") {
227 NodedbStatement::Database(DatabaseStmt::BackupDatabase { name, uri }) => {
228 assert_eq!(name, "mydb");
229 assert!(!uri.is_empty());
230 }
231 other => panic!("unexpected: {other:?}"),
232 }
233 }
234
235 #[test]
236 fn parse_restore_database() {
237 match ok("RESTORE DATABASE mydb FROM 's3://bucket/path'") {
238 NodedbStatement::Database(DatabaseStmt::RestoreDatabase { name, uri }) => {
239 assert_eq!(name, "mydb");
240 assert!(!uri.is_empty());
241 }
242 other => panic!("unexpected: {other:?}"),
243 }
244 }
245
246 #[test]
247 fn parse_move_tenant() {
248 match ok("MOVE TENANT t1 FROM db_a TO db_b") {
249 NodedbStatement::Database(DatabaseStmt::MoveTenant {
250 tenant_name,
251 from_db,
252 to_db,
253 }) => {
254 assert_eq!(tenant_name, "t1");
255 assert_eq!(from_db, "db_a");
256 assert_eq!(to_db, "db_b");
257 }
258 other => panic!("unexpected: {other:?}"),
259 }
260 }
261
262 #[test]
263 fn drop_missing_name_returns_error() {
264 let result = try_parse_database_statement("DROP DATABASE");
265 assert!(result.is_err() || result.unwrap().is_some());
266 }
267}