Skip to main content

reinhardt_query/backend/
cockroachdb.rs

1//! CockroachDB query builder backend
2//!
3//! This module implements the SQL generation backend for CockroachDB.
4//! Since CockroachDB is PostgreSQL-compatible, most operations delegate to PostgreSQL.
5
6use super::{PostgresQueryBuilder, QueryBuilder};
7use crate::{
8	dcl::{
9		AlterRoleStatement, AlterUserStatement, CreateRoleStatement, CreateUserStatement,
10		DropRoleStatement, DropUserStatement, GrantRoleStatement, GrantStatement,
11		RenameUserStatement, ResetRoleStatement, RevokeRoleStatement, RevokeStatement,
12		SetDefaultRoleStatement, SetRoleStatement,
13	},
14	query::{
15		AlterDatabaseStatement, AlterFunctionStatement, AlterIndexStatement,
16		AlterMaterializedViewStatement, AlterProcedureStatement, AlterSchemaStatement,
17		AlterSequenceStatement, AlterTableStatement, AlterTypeStatement, AnalyzeStatement,
18		CheckTableStatement, CommentStatement, CreateDatabaseStatement, CreateFunctionStatement,
19		CreateIndexStatement, CreateMaterializedViewStatement, CreateProcedureStatement,
20		CreateSchemaStatement, CreateSequenceStatement, CreateTableStatement,
21		CreateTriggerStatement, CreateTypeStatement, CreateViewStatement, DeleteStatement,
22		DropDatabaseStatement, DropFunctionStatement, DropIndexStatement,
23		DropMaterializedViewStatement, DropProcedureStatement, DropSchemaStatement,
24		DropSequenceStatement, DropTableStatement, DropTriggerStatement, DropTypeStatement,
25		DropViewStatement, InsertStatement, OptimizeTableStatement,
26		RefreshMaterializedViewStatement, ReindexStatement, RepairTableStatement, SelectStatement,
27		TruncateTableStatement, UpdateStatement, VacuumStatement,
28	},
29	value::Values,
30};
31
32/// CockroachDB query builder
33///
34/// This struct implements SQL generation for CockroachDB. Since CockroachDB is PostgreSQL-compatible,
35/// most operations delegate to [`PostgresQueryBuilder`].
36///
37/// # Examples
38///
39/// ```rust,ignore
40/// use reinhardt_query::backend::{CockroachDBQueryBuilder, QueryBuilder};
41/// use reinhardt_query::prelude::*;
42///
43/// let builder = CockroachDBQueryBuilder::new();
44/// let stmt = Query::select()
45///     .column("id")
46///     .from("users");
47///
48/// let (sql, values) = builder.build_select(&stmt);
49/// // sql: SELECT "id" FROM "users"
50/// ```
51#[derive(Debug, Clone, Default)]
52pub struct CockroachDBQueryBuilder {
53	postgres: PostgresQueryBuilder,
54}
55
56impl CockroachDBQueryBuilder {
57	/// Create a new CockroachDB query builder
58	pub fn new() -> Self {
59		Self {
60			postgres: PostgresQueryBuilder::new(),
61		}
62	}
63}
64
65impl QueryBuilder for CockroachDBQueryBuilder {
66	fn escape_identifier(&self, ident: &str) -> String {
67		self.postgres.escape_identifier(ident)
68	}
69
70	fn format_placeholder(&self, index: usize) -> String {
71		self.postgres.format_placeholder(index)
72	}
73
74	fn build_select(&self, stmt: &SelectStatement) -> (String, Values) {
75		self.postgres.build_select(stmt)
76	}
77
78	fn build_insert(&self, stmt: &InsertStatement) -> (String, Values) {
79		self.postgres.build_insert(stmt)
80	}
81
82	fn build_update(&self, stmt: &UpdateStatement) -> (String, Values) {
83		self.postgres.build_update(stmt)
84	}
85
86	fn build_delete(&self, stmt: &DeleteStatement) -> (String, Values) {
87		self.postgres.build_delete(stmt)
88	}
89
90	fn build_create_table(&self, stmt: &CreateTableStatement) -> (String, Values) {
91		self.postgres.build_create_table(stmt)
92	}
93
94	fn build_alter_table(&self, stmt: &AlterTableStatement) -> (String, Values) {
95		self.postgres.build_alter_table(stmt)
96	}
97
98	fn build_drop_table(&self, stmt: &DropTableStatement) -> (String, Values) {
99		self.postgres.build_drop_table(stmt)
100	}
101
102	fn build_create_index(&self, stmt: &CreateIndexStatement) -> (String, Values) {
103		self.postgres.build_create_index(stmt)
104	}
105
106	fn build_drop_index(&self, stmt: &DropIndexStatement) -> (String, Values) {
107		self.postgres.build_drop_index(stmt)
108	}
109
110	fn build_create_view(&self, stmt: &CreateViewStatement) -> (String, Values) {
111		self.postgres.build_create_view(stmt)
112	}
113
114	fn build_drop_view(&self, stmt: &DropViewStatement) -> (String, Values) {
115		self.postgres.build_drop_view(stmt)
116	}
117
118	fn build_truncate_table(&self, stmt: &TruncateTableStatement) -> (String, Values) {
119		self.postgres.build_truncate_table(stmt)
120	}
121
122	fn build_create_trigger(&self, stmt: &CreateTriggerStatement) -> (String, Values) {
123		self.postgres.build_create_trigger(stmt)
124	}
125
126	fn build_drop_trigger(&self, stmt: &DropTriggerStatement) -> (String, Values) {
127		self.postgres.build_drop_trigger(stmt)
128	}
129
130	fn build_alter_index(&self, stmt: &AlterIndexStatement) -> (String, Values) {
131		self.postgres.build_alter_index(stmt)
132	}
133
134	fn build_reindex(&self, stmt: &ReindexStatement) -> (String, Values) {
135		self.postgres.build_reindex(stmt)
136	}
137
138	fn build_create_schema(&self, stmt: &CreateSchemaStatement) -> (String, Values) {
139		// CockroachDB supports CREATE SCHEMA similar to PostgreSQL
140		self.postgres.build_create_schema(stmt)
141	}
142
143	fn build_alter_schema(&self, stmt: &AlterSchemaStatement) -> (String, Values) {
144		// CockroachDB supports ALTER SCHEMA similar to PostgreSQL
145		self.postgres.build_alter_schema(stmt)
146	}
147
148	fn build_drop_schema(&self, stmt: &DropSchemaStatement) -> (String, Values) {
149		// CockroachDB supports DROP SCHEMA similar to PostgreSQL
150		self.postgres.build_drop_schema(stmt)
151	}
152
153	fn build_create_sequence(&self, stmt: &CreateSequenceStatement) -> (String, Values) {
154		// CockroachDB supports CREATE SEQUENCE similar to PostgreSQL
155		self.postgres.build_create_sequence(stmt)
156	}
157
158	fn build_alter_sequence(&self, stmt: &AlterSequenceStatement) -> (String, Values) {
159		// CockroachDB supports ALTER SEQUENCE similar to PostgreSQL
160		self.postgres.build_alter_sequence(stmt)
161	}
162
163	fn build_drop_sequence(&self, stmt: &DropSequenceStatement) -> (String, Values) {
164		// CockroachDB supports DROP SEQUENCE similar to PostgreSQL
165		self.postgres.build_drop_sequence(stmt)
166	}
167
168	fn build_comment(&self, stmt: &CommentStatement) -> (String, Values) {
169		// CockroachDB supports COMMENT ON similar to PostgreSQL
170		self.postgres.build_comment(stmt)
171	}
172
173	fn build_create_database(&self, stmt: &CreateDatabaseStatement) -> (String, Values) {
174		// CockroachDB supports CREATE DATABASE with multi-region extensions
175		// Delegate to PostgreSQL implementation which includes CockroachDB compatibility
176		self.postgres.build_create_database(stmt)
177	}
178
179	fn build_alter_database(&self, stmt: &AlterDatabaseStatement) -> (String, Values) {
180		// CockroachDB supports ALTER DATABASE operations with multi-region support
181		// Delegate to PostgreSQL implementation which includes CockroachDB compatibility
182		self.postgres.build_alter_database(stmt)
183	}
184
185	fn build_drop_database(&self, stmt: &DropDatabaseStatement) -> (String, Values) {
186		// CockroachDB supports DROP DATABASE similar to PostgreSQL
187		// Delegate to PostgreSQL implementation
188		self.postgres.build_drop_database(stmt)
189	}
190
191	fn build_optimize_table(&self, _stmt: &OptimizeTableStatement) -> (String, Values) {
192		panic!(
193			"OPTIMIZE TABLE is MySQL-specific. CockroachDB automatically optimizes tables in the background."
194		);
195	}
196
197	fn build_repair_table(&self, _stmt: &RepairTableStatement) -> (String, Values) {
198		panic!(
199			"REPAIR TABLE is not supported in CockroachDB. CockroachDB automatically repairs data through replication and consistency checks."
200		);
201	}
202
203	fn build_check_table(&self, _stmt: &CheckTableStatement) -> (String, Values) {
204		panic!(
205			"CHECK TABLE is not supported in CockroachDB. Use SHOW EXPERIMENTAL_RANGES or other system tables to monitor table health."
206		);
207	}
208
209	fn build_create_function(&self, stmt: &CreateFunctionStatement) -> (String, Values) {
210		// CockroachDB delegates to PostgreSQL for functions
211		self.postgres.build_create_function(stmt)
212	}
213
214	fn build_alter_function(&self, stmt: &AlterFunctionStatement) -> (String, Values) {
215		// CockroachDB delegates to PostgreSQL for functions
216		self.postgres.build_alter_function(stmt)
217	}
218
219	fn build_drop_function(&self, stmt: &DropFunctionStatement) -> (String, Values) {
220		// CockroachDB delegates to PostgreSQL for functions
221		self.postgres.build_drop_function(stmt)
222	}
223
224	fn build_create_procedure(&self, stmt: &CreateProcedureStatement) -> (String, Values) {
225		// CockroachDB delegates to PostgreSQL for procedures
226		self.postgres.build_create_procedure(stmt)
227	}
228
229	fn build_alter_procedure(&self, stmt: &AlterProcedureStatement) -> (String, Values) {
230		// CockroachDB delegates to PostgreSQL for procedures
231		self.postgres.build_alter_procedure(stmt)
232	}
233
234	fn build_drop_procedure(&self, stmt: &DropProcedureStatement) -> (String, Values) {
235		// CockroachDB delegates to PostgreSQL for procedures
236		self.postgres.build_drop_procedure(stmt)
237	}
238
239	fn build_create_type(&self, stmt: &CreateTypeStatement) -> (String, Values) {
240		// CockroachDB supports custom types similar to PostgreSQL
241		self.postgres.build_create_type(stmt)
242	}
243
244	fn build_alter_type(&self, stmt: &AlterTypeStatement) -> (String, Values) {
245		// CockroachDB supports custom types similar to PostgreSQL
246		self.postgres.build_alter_type(stmt)
247	}
248
249	fn build_drop_type(&self, stmt: &DropTypeStatement) -> (String, Values) {
250		// CockroachDB supports custom types similar to PostgreSQL
251		self.postgres.build_drop_type(stmt)
252	}
253
254	// DCL (Data Control Language) - CockroachDB delegates to PostgreSQL
255
256	fn build_grant(&self, stmt: &GrantStatement) -> (String, Values) {
257		self.postgres.build_grant(stmt)
258	}
259
260	fn build_revoke(&self, stmt: &RevokeStatement) -> (String, Values) {
261		self.postgres.build_revoke(stmt)
262	}
263
264	fn build_grant_role(&self, stmt: &GrantRoleStatement) -> (String, Values) {
265		self.postgres.build_grant_role(stmt)
266	}
267
268	fn build_revoke_role(&self, stmt: &RevokeRoleStatement) -> (String, Values) {
269		self.postgres.build_revoke_role(stmt)
270	}
271
272	fn build_create_role(&self, stmt: &CreateRoleStatement) -> (String, Values) {
273		self.postgres.build_create_role(stmt)
274	}
275
276	fn build_drop_role(&self, stmt: &DropRoleStatement) -> (String, Values) {
277		self.postgres.build_drop_role(stmt)
278	}
279
280	fn build_alter_role(&self, stmt: &AlterRoleStatement) -> (String, Values) {
281		self.postgres.build_alter_role(stmt)
282	}
283
284	fn build_create_user(&self, stmt: &CreateUserStatement) -> (String, Values) {
285		self.postgres.build_create_user(stmt)
286	}
287
288	fn build_drop_user(&self, stmt: &DropUserStatement) -> (String, Values) {
289		self.postgres.build_drop_user(stmt)
290	}
291
292	fn build_alter_user(&self, stmt: &AlterUserStatement) -> (String, Values) {
293		self.postgres.build_alter_user(stmt)
294	}
295
296	fn build_rename_user(&self, stmt: &RenameUserStatement) -> (String, Values) {
297		self.postgres.build_rename_user(stmt)
298	}
299
300	fn build_set_role(&self, stmt: &SetRoleStatement) -> (String, Values) {
301		self.postgres.build_set_role(stmt)
302	}
303
304	fn build_reset_role(&self, stmt: &ResetRoleStatement) -> (String, Values) {
305		self.postgres.build_reset_role(stmt)
306	}
307
308	fn build_set_default_role(&self, stmt: &SetDefaultRoleStatement) -> (String, Values) {
309		self.postgres.build_set_default_role(stmt)
310	}
311
312	// Maintenance statements - CockroachDB delegates to PostgreSQL
313
314	fn build_vacuum(&self, stmt: &VacuumStatement) -> (String, Values) {
315		self.postgres.build_vacuum(stmt)
316	}
317
318	fn build_analyze(&self, stmt: &AnalyzeStatement) -> (String, Values) {
319		self.postgres.build_analyze(stmt)
320	}
321
322	// Materialized view statements - CockroachDB delegates to PostgreSQL
323
324	fn build_create_materialized_view(
325		&self,
326		stmt: &CreateMaterializedViewStatement,
327	) -> (String, Values) {
328		self.postgres.build_create_materialized_view(stmt)
329	}
330
331	fn build_alter_materialized_view(
332		&self,
333		stmt: &AlterMaterializedViewStatement,
334	) -> (String, Values) {
335		self.postgres.build_alter_materialized_view(stmt)
336	}
337
338	fn build_drop_materialized_view(
339		&self,
340		stmt: &DropMaterializedViewStatement,
341	) -> (String, Values) {
342		self.postgres.build_drop_materialized_view(stmt)
343	}
344
345	fn build_refresh_materialized_view(
346		&self,
347		stmt: &RefreshMaterializedViewStatement,
348	) -> (String, Values) {
349		self.postgres.build_refresh_materialized_view(stmt)
350	}
351}
352
353#[cfg(test)]
354mod tests {
355	use super::*;
356	use crate::query::Query;
357
358	// FUNCTION tests - verify CockroachDB delegates to PostgreSQL
359	#[test]
360	fn test_create_function_delegates_to_postgres() {
361		use crate::types::function::FunctionLanguage;
362
363		let builder = CockroachDBQueryBuilder::new();
364		let mut stmt = Query::create_function();
365		stmt.name("my_func")
366			.returns("integer")
367			.language(FunctionLanguage::Sql)
368			.body("SELECT 1");
369
370		let (sql, values) = builder.build_create_function(&stmt);
371		// Should generate PostgreSQL-style SQL with double quotes and $$
372		assert_eq!(
373			sql,
374			r#"CREATE FUNCTION "my_func"() RETURNS integer LANGUAGE SQL AS $$SELECT 1$$"#
375		);
376		assert_eq!(values.len(), 0);
377	}
378
379	#[test]
380	fn test_alter_function_delegates_to_postgres() {
381		let builder = CockroachDBQueryBuilder::new();
382		let mut stmt = Query::alter_function();
383		stmt.name("my_func").rename_to("new_func");
384
385		let (sql, values) = builder.build_alter_function(&stmt);
386		// Should generate PostgreSQL-style SQL with double quotes
387		assert_eq!(sql, r#"ALTER FUNCTION "my_func" RENAME TO "new_func""#);
388		assert_eq!(values.len(), 0);
389	}
390
391	#[test]
392	fn test_drop_function_delegates_to_postgres() {
393		let builder = CockroachDBQueryBuilder::new();
394		let mut stmt = Query::drop_function();
395		stmt.name("my_func").if_exists().cascade();
396
397		let (sql, values) = builder.build_drop_function(&stmt);
398		// Should generate PostgreSQL-style SQL with double quotes and CASCADE
399		assert_eq!(sql, r#"DROP FUNCTION IF EXISTS "my_func" CASCADE"#);
400		assert_eq!(values.len(), 0);
401	}
402
403	// TYPE tests - verify CockroachDB delegates to PostgreSQL
404	#[test]
405	fn test_create_type_enum_delegates_to_postgres() {
406		let builder = CockroachDBQueryBuilder::new();
407		let mut stmt = Query::create_type();
408		stmt.name("mood")
409			.as_enum(vec!["happy".to_string(), "sad".to_string()]);
410
411		let (sql, values) = builder.build_create_type(&stmt);
412		// Should generate PostgreSQL-style SQL with double quotes
413		assert_eq!(sql, r#"CREATE TYPE "mood" AS ENUM ('happy', 'sad')"#);
414		assert_eq!(values.len(), 0);
415	}
416
417	#[test]
418	fn test_alter_type_delegates_to_postgres() {
419		let builder = CockroachDBQueryBuilder::new();
420		let mut stmt = Query::alter_type();
421		stmt.name("mood").rename_to("feeling");
422
423		let (sql, values) = builder.build_alter_type(&stmt);
424		// Should generate PostgreSQL-style SQL with double quotes
425		assert_eq!(sql, r#"ALTER TYPE "mood" RENAME TO "feeling""#);
426		assert_eq!(values.len(), 0);
427	}
428
429	#[test]
430	fn test_drop_type_delegates_to_postgres() {
431		let builder = CockroachDBQueryBuilder::new();
432		let mut stmt = Query::drop_type();
433		stmt.name("mood").if_exists().cascade();
434
435		let (sql, values) = builder.build_drop_type(&stmt);
436		// Should generate PostgreSQL-style SQL with double quotes and CASCADE
437		assert_eq!(sql, r#"DROP TYPE IF EXISTS "mood" CASCADE"#);
438		assert_eq!(values.len(), 0);
439	}
440
441	// MySQL-specific maintenance command panic tests
442	#[test]
443	#[should_panic(expected = "CockroachDB automatically optimizes tables")]
444	fn test_optimize_table_panics() {
445		let builder = CockroachDBQueryBuilder::new();
446		let mut stmt = Query::optimize_table();
447		stmt.table("users");
448
449		let _ = builder.build_optimize_table(&stmt);
450	}
451
452	#[test]
453	#[should_panic(expected = "not supported in CockroachDB")]
454	fn test_repair_table_panics() {
455		let builder = CockroachDBQueryBuilder::new();
456		let mut stmt = Query::repair_table();
457		stmt.table("users");
458
459		let _ = builder.build_repair_table(&stmt);
460	}
461
462	#[test]
463	#[should_panic(expected = "not supported in CockroachDB")]
464	fn test_check_table_panics() {
465		let builder = CockroachDBQueryBuilder::new();
466		let mut stmt = Query::check_table();
467		stmt.table("users");
468
469		let _ = builder.build_check_table(&stmt);
470	}
471}