lezeh_db/psql/
table_metadata.rs1use std::cell::RefCell;
2use std::rc::Rc;
3
4use anyhow::anyhow;
5use postgres::Row;
6use thiserror::Error;
7
8use crate::psql::connection::PsqlConnection;
9use crate::psql::dto::*;
10use lezeh_common::types::ResultAnyError;
11
12pub struct Query {
13 connection: Rc<RefCell<PsqlConnection>>,
14}
15
16#[derive(Error, Debug)]
17pub enum QueryError {
18 #[error("Row with column {column} = {identifier:?} is not found in table {table_id}")]
19 RowNotFound {
20 table_id: String,
21 column: String,
22 identifier: String,
23 },
24
25 #[error("Too many rows returned({row_count}), expecting only {expected_row_count}")]
26 TooManyRows {
27 row_count: usize,
28 expected_row_count: usize,
29 },
30}
31
32pub struct FetchRowInput<'a> {
33 pub table_id: &'a PsqlTableIdentity,
34 pub column_name: &'a str,
35 pub column_value: &'a PsqlParamValue,
36}
37
38impl<'b> FetchRowInput<'b> {
39 pub fn psql_param_value<'a>(
40 column_value: String,
41 column: PsqlTableColumn,
42 ) -> ResultAnyError<PsqlParamValue> {
43 let data_type: String = column.data_type.to_string();
44 let mut value: PsqlParamValue = Box::new(column_value.clone());
45
46 if data_type == "integer" {
47 let convert_column_value = column_value.clone().parse::<i32>().map_err(|err| {
48 return anyhow!(
49 "Cannot cast column '{}' of value {} to integer. Error: {}",
50 column.name,
51 column_value,
52 err
53 );
54 })?;
55
56 value = Box::new(convert_column_value);
57 } else if data_type == "uuid" {
58 let uuid = Uuid::from_str(&column_value)?;
59
60 value = Box::new(uuid);
61 }
62
63 return Ok(value);
64 }
65}
66
67impl Query {
68 fn find_rows(&mut self, input: &FetchRowInput) -> ResultAnyError<Vec<Row>> {
69 let query_str = format!(
70 "SELECT * FROM {} where {} = $1",
71 input.table_id, input.column_name
72 );
73
74 let mut connection = self.connection.borrow_mut();
75 let connection = connection.get();
76 let statement = connection.prepare(&query_str)?;
77
78 return connection
79 .query(&statement, &[input.column_value.as_ref()])
80 .map_err(anyhow::Error::from);
81 }
82
83 fn find_one_row(&mut self, input: &FetchRowInput) -> ResultAnyError<Option<Row>> {
84 let rows_result = self.find_rows(input);
85
86 return match rows_result {
87 Err(any) => Err(any),
88 Ok(mut rows) => {
89 if rows.len() > 1 {
90 return Err(anyhow!(QueryError::TooManyRows {
91 row_count: rows.len(),
92 expected_row_count: 1,
93 }));
94 }
95
96 if rows.len() == 0 {
97 return Ok(None);
98 }
99
100 return Ok(Some(rows.remove(0)));
101 }
102 };
103 }
104
105 pub fn get_column_metadata<'a>(
106 &mut self,
107 table_id: &PsqlTableIdentity,
108 column_name: &str,
109 ) -> ResultAnyError<Row> {
110 let query_str =
111 "SELECT * FROM information_schema.columns where table_schema = $1 and table_name = $2 and column_name = $3";
112
113 let mut connection = self.connection.borrow_mut();
114 let connection = connection.get();
115 let statement = connection.prepare(&query_str)?;
116
117 return connection
118 .query_one(
119 &statement,
120 &[
121 &table_id.schema.to_string(),
122 &table_id.name.to_string(),
123 &column_name.to_string(),
124 ],
125 )
126 .map_err(anyhow::Error::from);
127 }
128}
129
130#[cfg_attr(test, mockall::automock)]
131pub trait TableMetadata {
132 fn get_column(
133 &self,
134 table_id: &PsqlTableIdentity,
135 column_name: &str,
136 ) -> ResultAnyError<PsqlTableColumn>;
137
138 fn get_rows<'a>(
139 &self,
140 table: PsqlTable,
141 column_name: &str,
142 id: &PsqlParamValue,
143 ) -> ResultAnyError<Vec<PsqlTableRow>>;
144
145 fn get_one_row(
146 &self,
147 table: &PsqlTable,
148 column_name: &str,
149 id: &str,
150 ) -> ResultAnyError<PsqlTableRow>;
151}
152
153pub struct TableMetadataImpl {
154 query: RefCell<Query>,
157}
158
159impl TableMetadataImpl {
160 pub fn new(psql_connection: Rc<RefCell<PsqlConnection>>) -> TableMetadataImpl {
161 return TableMetadataImpl {
162 query: RefCell::new(Query {
163 connection: psql_connection,
164 }),
165 };
166 }
167}
168
169impl TableMetadata for TableMetadataImpl {
170 fn get_column(
171 &self,
172 table_id: &PsqlTableIdentity,
173 column_name: &str,
174 ) -> ResultAnyError<PsqlTableColumn> {
175 let row = self
176 .query
177 .borrow_mut()
178 .get_column_metadata(table_id, column_name)?;
179
180 let column = PsqlTableColumn::new(column_name.to_string(), row.get("data_type"));
181
182 return Ok(column);
183 }
184
185 fn get_rows(
186 &self,
187 table: PsqlTable,
188 column_name: &str,
189 id: &PsqlParamValue,
190 ) -> ResultAnyError<Vec<PsqlTableRow>> {
191 return self
192 .query
193 .borrow_mut()
194 .find_rows(&FetchRowInput {
195 table_id: &table.id,
196 column_name,
197 column_value: id,
198 })
199 .and_then(|rows| {
200 return rows
201 .into_iter()
202 .map(|inner_row| {
203 return PsqlTableRow::new(table.clone(), Rc::new(inner_row));
204 })
205 .collect::<ResultAnyError<Vec<PsqlTableRow>>>();
206 });
207 }
208
209 fn get_one_row<'a>(
210 &self,
211 table: &PsqlTable,
212 column_name: &str,
213 id: &str,
214 ) -> ResultAnyError<PsqlTableRow> {
215 let column = self.get_column(&table.id, column_name)?;
216 let id: PsqlParamValue = FetchRowInput::psql_param_value(id.to_string(), column)?;
217
218 let row = self.query.borrow_mut().find_one_row(&FetchRowInput {
219 table_id: &table.id,
220 column_name,
221 column_value: &id,
222 })?;
223
224 return row
225 .ok_or_else(|| {
226 anyhow!(QueryError::RowNotFound {
227 table_id: format!("{:#?}", table.id),
228 column: column_name.into(),
229 identifier: format!("{:#?}", id),
230 })
231 })
232 .and_then(|inner_row| PsqlTableRow::new(table.clone(), Rc::new(inner_row)));
233 }
234}