Skip to main content

reinhardt_query/query/database/
alter_database.rs

1//! ALTER DATABASE statement builder
2//!
3//! This module provides the `AlterDatabaseStatement` type for building SQL ALTER DATABASE queries.
4
5use crate::{
6	backend::QueryBuilder,
7	types::{DatabaseOperation, DynIden, IntoIden, ZoneConfig},
8};
9
10use crate::query::traits::{QueryBuilderTrait, QueryStatementBuilder, QueryStatementWriter};
11
12/// ALTER DATABASE statement builder
13///
14/// This struct provides a fluent API for constructing ALTER DATABASE queries.
15/// It supports standard PostgreSQL operations and CockroachDB-specific multi-region configuration.
16///
17/// # Examples
18///
19/// ```rust
20/// use reinhardt_query::prelude::*;
21///
22/// // ALTER DATABASE mydb RENAME TO newdb
23/// let query = Query::alter_database()
24///     .name("mydb")
25///     .rename_to("newdb");
26///
27/// // ALTER DATABASE mydb ADD REGION "us-east-1" (CockroachDB)
28/// let query = Query::alter_database()
29///     .name("mydb")
30///     .add_region("us-east-1");
31///
32/// // ALTER DATABASE mydb PRIMARY REGION "us-east-1" (CockroachDB)
33/// let query = Query::alter_database()
34///     .name("mydb")
35///     .set_primary_region("us-east-1");
36/// ```
37#[derive(Debug, Clone)]
38pub struct AlterDatabaseStatement {
39	pub(crate) database_name: Option<DynIden>,
40	pub(crate) operations: Vec<DatabaseOperation>,
41}
42
43impl AlterDatabaseStatement {
44	/// Create a new ALTER DATABASE statement
45	///
46	/// # Examples
47	///
48	/// ```rust
49	/// use reinhardt_query::prelude::*;
50	///
51	/// let query = Query::alter_database();
52	/// ```
53	pub fn new() -> Self {
54		Self {
55			database_name: None,
56			operations: Vec::new(),
57		}
58	}
59
60	/// Take the ownership of data in the current [`AlterDatabaseStatement`]
61	pub fn take(&mut self) -> Self {
62		Self {
63			database_name: self.database_name.take(),
64			operations: std::mem::take(&mut self.operations),
65		}
66	}
67
68	/// Set the database name
69	///
70	/// # Examples
71	///
72	/// ```rust
73	/// use reinhardt_query::prelude::*;
74	///
75	/// let query = Query::alter_database()
76	///     .name("mydb");
77	/// ```
78	pub fn name<N>(&mut self, name: N) -> &mut Self
79	where
80		N: IntoIden,
81	{
82		self.database_name = Some(name.into_iden());
83		self
84	}
85
86	/// Rename the database to a new name
87	///
88	/// # Examples
89	///
90	/// ```rust
91	/// use reinhardt_query::prelude::*;
92	///
93	/// let query = Query::alter_database()
94	///     .name("old_db")
95	///     .rename_to("new_db");
96	/// ```
97	pub fn rename_to<N>(&mut self, new_name: N) -> &mut Self
98	where
99		N: IntoIden,
100	{
101		self.operations
102			.push(DatabaseOperation::RenameDatabase(new_name.into_iden()));
103		self
104	}
105
106	/// Change the owner of the database
107	///
108	/// # Examples
109	///
110	/// ```rust
111	/// use reinhardt_query::prelude::*;
112	///
113	/// let query = Query::alter_database()
114	///     .name("mydb")
115	///     .owner_to("new_owner");
116	/// ```
117	pub fn owner_to<O>(&mut self, new_owner: O) -> &mut Self
118	where
119		O: IntoIden,
120	{
121		self.operations
122			.push(DatabaseOperation::OwnerTo(new_owner.into_iden()));
123		self
124	}
125
126	/// Add a region to the database (CockroachDB-specific)
127	///
128	/// # Examples
129	///
130	/// ```rust
131	/// use reinhardt_query::prelude::*;
132	///
133	/// let query = Query::alter_database()
134	///     .name("mydb")
135	///     .add_region("us-east-1");
136	/// ```
137	pub fn add_region<S>(&mut self, region: S) -> &mut Self
138	where
139		S: Into<String>,
140	{
141		self.operations
142			.push(DatabaseOperation::AddRegion(region.into()));
143		self
144	}
145
146	/// Drop a region from the database (CockroachDB-specific)
147	///
148	/// # Examples
149	///
150	/// ```rust
151	/// use reinhardt_query::prelude::*;
152	///
153	/// let query = Query::alter_database()
154	///     .name("mydb")
155	///     .drop_region("us-west-1");
156	/// ```
157	pub fn drop_region<S>(&mut self, region: S) -> &mut Self
158	where
159		S: Into<String>,
160	{
161		self.operations
162			.push(DatabaseOperation::DropRegion(region.into()));
163		self
164	}
165
166	/// Set the primary region for the database (CockroachDB-specific)
167	///
168	/// # Examples
169	///
170	/// ```rust
171	/// use reinhardt_query::prelude::*;
172	///
173	/// let query = Query::alter_database()
174	///     .name("mydb")
175	///     .set_primary_region("us-east-1");
176	/// ```
177	pub fn set_primary_region<S>(&mut self, region: S) -> &mut Self
178	where
179		S: Into<String>,
180	{
181		self.operations
182			.push(DatabaseOperation::SetPrimaryRegion(region.into()));
183		self
184	}
185
186	/// Configure zone settings for the database (CockroachDB-specific)
187	///
188	/// # Examples
189	///
190	/// ```rust
191	/// use reinhardt_query::prelude::*;
192	/// use reinhardt_query::types::ZoneConfig;
193	///
194	/// let zone = ZoneConfig::new()
195	///     .num_replicas(3)
196	///     .add_constraint("+region=us-east-1");
197	///
198	/// let query = Query::alter_database()
199	///     .name("mydb")
200	///     .configure_zone(zone);
201	/// ```
202	pub fn configure_zone(&mut self, zone: ZoneConfig) -> &mut Self {
203		self.operations.push(DatabaseOperation::ConfigureZone(zone));
204		self
205	}
206}
207
208impl Default for AlterDatabaseStatement {
209	fn default() -> Self {
210		Self::new()
211	}
212}
213
214impl QueryStatementBuilder for AlterDatabaseStatement {
215	fn build_any(&self, query_builder: &dyn QueryBuilderTrait) -> (String, crate::value::Values) {
216		// Downcast to concrete QueryBuilder type
217		use std::any::Any;
218		if let Some(builder) =
219			(query_builder as &dyn Any).downcast_ref::<crate::backend::PostgresQueryBuilder>()
220		{
221			return builder.build_alter_database(self);
222		}
223		if let Some(builder) =
224			(query_builder as &dyn Any).downcast_ref::<crate::backend::MySqlQueryBuilder>()
225		{
226			return builder.build_alter_database(self);
227		}
228		if let Some(builder) =
229			(query_builder as &dyn Any).downcast_ref::<crate::backend::SqliteQueryBuilder>()
230		{
231			return builder.build_alter_database(self);
232		}
233		if let Some(builder) =
234			(query_builder as &dyn Any).downcast_ref::<crate::backend::CockroachDBQueryBuilder>()
235		{
236			return builder.build_alter_database(self);
237		}
238		panic!("Unsupported query builder type");
239	}
240}
241
242impl QueryStatementWriter for AlterDatabaseStatement {}
243
244#[cfg(test)]
245mod tests {
246	use super::*;
247	use rstest::*;
248
249	#[rstest]
250	fn test_alter_database_new() {
251		let stmt = AlterDatabaseStatement::new();
252		assert!(stmt.database_name.is_none());
253		assert!(stmt.operations.is_empty());
254	}
255
256	#[rstest]
257	fn test_alter_database_with_name() {
258		let mut stmt = AlterDatabaseStatement::new();
259		stmt.name("mydb");
260		assert_eq!(stmt.database_name.as_ref().unwrap().to_string(), "mydb");
261	}
262
263	#[rstest]
264	fn test_alter_database_rename_to() {
265		let mut stmt = AlterDatabaseStatement::new();
266		stmt.name("old_db").rename_to("new_db");
267		assert_eq!(stmt.database_name.as_ref().unwrap().to_string(), "old_db");
268		assert_eq!(stmt.operations.len(), 1);
269		match &stmt.operations[0] {
270			DatabaseOperation::RenameDatabase(name) => {
271				assert_eq!(name.to_string(), "new_db");
272			}
273			_ => panic!("Expected RenameDatabase operation"),
274		}
275	}
276
277	#[rstest]
278	fn test_alter_database_owner_to() {
279		let mut stmt = AlterDatabaseStatement::new();
280		stmt.name("mydb").owner_to("new_owner");
281		assert_eq!(stmt.database_name.as_ref().unwrap().to_string(), "mydb");
282		assert_eq!(stmt.operations.len(), 1);
283		match &stmt.operations[0] {
284			DatabaseOperation::OwnerTo(owner) => {
285				assert_eq!(owner.to_string(), "new_owner");
286			}
287			_ => panic!("Expected OwnerTo operation"),
288		}
289	}
290
291	#[rstest]
292	fn test_alter_database_add_region() {
293		let mut stmt = AlterDatabaseStatement::new();
294		stmt.name("mydb").add_region("us-east-1");
295		assert_eq!(stmt.database_name.as_ref().unwrap().to_string(), "mydb");
296		assert_eq!(stmt.operations.len(), 1);
297		match &stmt.operations[0] {
298			DatabaseOperation::AddRegion(region) => {
299				assert_eq!(region, "us-east-1");
300			}
301			_ => panic!("Expected AddRegion operation"),
302		}
303	}
304
305	#[rstest]
306	fn test_alter_database_drop_region() {
307		let mut stmt = AlterDatabaseStatement::new();
308		stmt.name("mydb").drop_region("us-west-1");
309		assert_eq!(stmt.database_name.as_ref().unwrap().to_string(), "mydb");
310		assert_eq!(stmt.operations.len(), 1);
311		match &stmt.operations[0] {
312			DatabaseOperation::DropRegion(region) => {
313				assert_eq!(region, "us-west-1");
314			}
315			_ => panic!("Expected DropRegion operation"),
316		}
317	}
318
319	#[rstest]
320	fn test_alter_database_set_primary_region() {
321		let mut stmt = AlterDatabaseStatement::new();
322		stmt.name("mydb").set_primary_region("us-east-1");
323		assert_eq!(stmt.database_name.as_ref().unwrap().to_string(), "mydb");
324		assert_eq!(stmt.operations.len(), 1);
325		match &stmt.operations[0] {
326			DatabaseOperation::SetPrimaryRegion(region) => {
327				assert_eq!(region, "us-east-1");
328			}
329			_ => panic!("Expected SetPrimaryRegion operation"),
330		}
331	}
332
333	#[rstest]
334	fn test_alter_database_multiple_operations() {
335		let mut stmt = AlterDatabaseStatement::new();
336		stmt.name("mydb")
337			.add_region("us-east-1")
338			.add_region("us-west-1")
339			.set_primary_region("us-east-1");
340		assert_eq!(stmt.operations.len(), 3);
341	}
342
343	#[rstest]
344	fn test_alter_database_take() {
345		let mut stmt = AlterDatabaseStatement::new();
346		stmt.name("mydb").add_region("us-east-1");
347		let taken = stmt.take();
348		assert!(stmt.database_name.is_none());
349		assert!(stmt.operations.is_empty());
350		assert_eq!(taken.database_name.as_ref().unwrap().to_string(), "mydb");
351		assert_eq!(taken.operations.len(), 1);
352	}
353
354	#[rstest]
355	fn test_alter_database_configure_zone() {
356		let mut stmt = AlterDatabaseStatement::new();
357		let zone = ZoneConfig::new()
358			.num_replicas(3)
359			.add_constraint("+region=us-east-1");
360		stmt.name("mydb").configure_zone(zone);
361		assert_eq!(stmt.database_name.as_ref().unwrap().to_string(), "mydb");
362		assert_eq!(stmt.operations.len(), 1);
363		match &stmt.operations[0] {
364			DatabaseOperation::ConfigureZone(config) => {
365				assert_eq!(config.num_replicas, Some(3));
366				assert_eq!(config.constraints.len(), 1);
367			}
368			_ => panic!("Expected ConfigureZone operation"),
369		}
370	}
371
372	#[rstest]
373	fn test_alter_database_configure_zone_multiple_options() {
374		let mut stmt = AlterDatabaseStatement::new();
375		let zone = ZoneConfig::new()
376			.num_replicas(5)
377			.add_constraint("+region=us-east-1")
378			.add_constraint("+zone=a")
379			.add_lease_preference("+region=us-east-1");
380		stmt.name("mydb").configure_zone(zone);
381		assert_eq!(stmt.operations.len(), 1);
382		match &stmt.operations[0] {
383			DatabaseOperation::ConfigureZone(config) => {
384				assert_eq!(config.num_replicas, Some(5));
385				assert_eq!(config.constraints.len(), 2);
386				assert_eq!(config.lease_preferences.len(), 1);
387			}
388			_ => panic!("Expected ConfigureZone operation"),
389		}
390	}
391}