Skip to main content

reinhardt_query/types/
table_ref.rs

1//! Table reference types for SQL queries.
2//!
3//! This module provides types for referencing tables:
4//!
5//! - [`TableRef`]: Reference to a table (simple, qualified, aliased, subquery)
6//! - [`IntoTableRef`]: Conversion trait for table references
7
8use super::iden::{DynIden, IntoIden};
9
10/// Reference to a table in a SQL query.
11///
12/// This enum represents different ways to reference a table,
13/// from simple table names to subqueries.
14#[derive(Debug, Clone)]
15pub enum TableRef {
16	/// Simple table reference (e.g., `users`)
17	Table(DynIden),
18	/// Schema-qualified table reference (e.g., `public.users`)
19	SchemaTable(DynIden, DynIden),
20	/// Database, schema, and table reference (e.g., `mydb.public.users`)
21	DatabaseSchemaTable(DynIden, DynIden, DynIden),
22	/// Table with alias (e.g., `users AS u`)
23	TableAlias(DynIden, DynIden),
24	/// Schema-qualified table with alias (e.g., `public.users AS u`)
25	SchemaTableAlias(DynIden, DynIden, DynIden),
26	/// Subquery with alias (e.g., `(SELECT ...) AS alias`)
27	SubQuery(Box<crate::query::SelectStatement>, DynIden),
28	/// LATERAL subquery with alias (e.g., `LATERAL (SELECT ...) AS alias`)
29	LateralSubQuery(Box<crate::query::SelectStatement>, DynIden),
30}
31
32impl TableRef {
33	/// Create a simple table reference.
34	///
35	/// # Example
36	///
37	/// ```rust
38	/// use reinhardt_query::TableRef;
39	///
40	/// let table = TableRef::table("users");
41	/// ```
42	pub fn table<I: IntoIden>(table: I) -> Self {
43		Self::Table(table.into_iden())
44	}
45
46	/// Create a schema-qualified table reference.
47	///
48	/// # Example
49	///
50	/// ```rust
51	/// use reinhardt_query::TableRef;
52	///
53	/// let table = TableRef::schema_table("public", "users");
54	/// ```
55	pub fn schema_table<S: IntoIden, T: IntoIden>(schema: S, table: T) -> Self {
56		Self::SchemaTable(schema.into_iden(), table.into_iden())
57	}
58
59	/// Create a database, schema, and table reference.
60	///
61	/// # Example
62	///
63	/// ```rust
64	/// use reinhardt_query::TableRef;
65	///
66	/// let table = TableRef::database_schema_table("mydb", "public", "users");
67	/// ```
68	pub fn database_schema_table<D: IntoIden, S: IntoIden, T: IntoIden>(
69		database: D,
70		schema: S,
71		table: T,
72	) -> Self {
73		Self::DatabaseSchemaTable(database.into_iden(), schema.into_iden(), table.into_iden())
74	}
75
76	/// Create a table reference with an alias.
77	///
78	/// # Example
79	///
80	/// ```rust
81	/// use reinhardt_query::TableRef;
82	///
83	/// let table = TableRef::table_alias("users", "u");
84	/// ```
85	pub fn table_alias<T: IntoIden, A: IntoIden>(table: T, alias: A) -> Self {
86		Self::TableAlias(table.into_iden(), alias.into_iden())
87	}
88
89	/// Create a schema-qualified table reference with an alias.
90	///
91	/// # Example
92	///
93	/// ```rust
94	/// use reinhardt_query::TableRef;
95	///
96	/// let table = TableRef::schema_table_alias("public", "users", "u");
97	/// ```
98	pub fn schema_table_alias<S: IntoIden, T: IntoIden, A: IntoIden>(
99		schema: S,
100		table: T,
101		alias: A,
102	) -> Self {
103		Self::SchemaTableAlias(schema.into_iden(), table.into_iden(), alias.into_iden())
104	}
105
106	/// Create a LATERAL subquery with an alias.
107	pub fn lateral_subquery(query: crate::query::SelectStatement, alias: impl IntoIden) -> Self {
108		Self::LateralSubQuery(Box::new(query), alias.into_iden())
109	}
110}
111
112/// Conversion trait for table references.
113///
114/// This trait allows various types to be converted into `TableRef`.
115pub trait IntoTableRef {
116	/// Convert this type into a `TableRef`.
117	fn into_table_ref(self) -> TableRef;
118}
119
120// Implementation for TableRef itself
121impl IntoTableRef for TableRef {
122	fn into_table_ref(self) -> TableRef {
123		self
124	}
125}
126
127// Implementation for DynIden (simple table reference)
128impl IntoTableRef for DynIden {
129	fn into_table_ref(self) -> TableRef {
130		TableRef::Table(self)
131	}
132}
133
134// Implementation for &'static str (simple table reference)
135impl IntoTableRef for &'static str {
136	fn into_table_ref(self) -> TableRef {
137		TableRef::Table(self.into_iden())
138	}
139}
140
141// Implementation for String (simple table reference)
142impl IntoTableRef for String {
143	fn into_table_ref(self) -> TableRef {
144		TableRef::Table(self.into_iden())
145	}
146}
147
148// Implementation for tuple (schema, table)
149impl<S, T> IntoTableRef for (S, T)
150where
151	S: IntoIden,
152	T: IntoIden,
153{
154	fn into_table_ref(self) -> TableRef {
155		TableRef::SchemaTable(self.0.into_iden(), self.1.into_iden())
156	}
157}
158
159// Implementation for triple (database, schema, table)
160impl<D, S, T> IntoTableRef for (D, S, T)
161where
162	D: IntoIden,
163	S: IntoIden,
164	T: IntoIden,
165{
166	fn into_table_ref(self) -> TableRef {
167		TableRef::DatabaseSchemaTable(self.0.into_iden(), self.1.into_iden(), self.2.into_iden())
168	}
169}
170
171// Implementation for Alias
172impl IntoTableRef for super::alias::Alias {
173	fn into_table_ref(self) -> TableRef {
174		TableRef::Table(self.into_iden())
175	}
176}
177
178#[cfg(test)]
179mod tests {
180	use super::*;
181	use crate::types::Alias;
182	use rstest::rstest;
183
184	#[rstest]
185	fn test_table_ref_simple() {
186		let table = TableRef::table("users");
187		if let TableRef::Table(iden) = table {
188			assert_eq!(iden.to_string(), "users");
189		} else {
190			panic!("Expected Table variant");
191		}
192	}
193
194	#[rstest]
195	fn test_table_ref_schema_qualified() {
196		let table = TableRef::schema_table("public", "users");
197		if let TableRef::SchemaTable(schema, tbl) = table {
198			assert_eq!(schema.to_string(), "public");
199			assert_eq!(tbl.to_string(), "users");
200		} else {
201			panic!("Expected SchemaTable variant");
202		}
203	}
204
205	#[rstest]
206	fn test_table_ref_database_schema_qualified() {
207		let table = TableRef::database_schema_table("mydb", "public", "users");
208		if let TableRef::DatabaseSchemaTable(db, schema, tbl) = table {
209			assert_eq!(db.to_string(), "mydb");
210			assert_eq!(schema.to_string(), "public");
211			assert_eq!(tbl.to_string(), "users");
212		} else {
213			panic!("Expected DatabaseSchemaTable variant");
214		}
215	}
216
217	#[rstest]
218	fn test_table_ref_with_alias() {
219		let table = TableRef::table_alias("users", "u");
220		if let TableRef::TableAlias(tbl, alias) = table {
221			assert_eq!(tbl.to_string(), "users");
222			assert_eq!(alias.to_string(), "u");
223		} else {
224			panic!("Expected TableAlias variant");
225		}
226	}
227
228	#[rstest]
229	fn test_table_ref_schema_with_alias() {
230		let table = TableRef::schema_table_alias("public", "users", "u");
231		if let TableRef::SchemaTableAlias(schema, tbl, alias) = table {
232			assert_eq!(schema.to_string(), "public");
233			assert_eq!(tbl.to_string(), "users");
234			assert_eq!(alias.to_string(), "u");
235		} else {
236			panic!("Expected SchemaTableAlias variant");
237		}
238	}
239
240	#[rstest]
241	fn test_into_table_ref_from_str() {
242		let table: TableRef = "users".into_table_ref();
243		if let TableRef::Table(iden) = table {
244			assert_eq!(iden.to_string(), "users");
245		} else {
246			panic!("Expected Table variant");
247		}
248	}
249
250	#[rstest]
251	fn test_into_table_ref_from_alias() {
252		let alias = Alias::new("my_table");
253		let table: TableRef = alias.into_table_ref();
254		if let TableRef::Table(iden) = table {
255			assert_eq!(iden.to_string(), "my_table");
256		} else {
257			panic!("Expected Table variant");
258		}
259	}
260
261	#[rstest]
262	fn test_into_table_ref_from_database_schema_table_tuple() {
263		let table: TableRef = ("app", "public", "users").into_table_ref();
264		if let TableRef::DatabaseSchemaTable(database, schema, table) = table {
265			assert_eq!(database.to_string(), "app");
266			assert_eq!(schema.to_string(), "public");
267			assert_eq!(table.to_string(), "users");
268		} else {
269			panic!("Expected DatabaseSchemaTable variant");
270		}
271	}
272}