Skip to content

Commit 9016676

Browse files
authored
Improve error handling & return meaningful error messages (#246)
* meaningful error * improved error handling * fmt * clippy fix * throw error on the impoissble cases * remove unused error * simplify TableNameInferenceFailedInWhere * fix * fix * clippy fail on warning * remove unncessary type cast
1 parent 6a10c69 commit 9016676

File tree

7 files changed

+107
-41
lines changed

7 files changed

+107
-41
lines changed

.github/workflows/rust.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ jobs:
9797
cache-provider: "github"
9898

9999
- name: Run clippy
100-
run: cargo clippy
100+
run: cargo clippy --lib --all-features -- -D warnings
101101

102102
- name: Run rustfmt
103103
run: cargo fmt -- --check

src/ts_generator/errors.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,22 @@ pub enum TsGeneratorError {
3232
TableFactorWhileProcessingTableWithJoins(String),
3333
#[error("[E014] Failed to find a table name from a FROM statement: statement: `{0}`")]
3434
UnknownErrorWhileProcessingTableWithJoins(String),
35+
#[error("[E015] Table expressions are not supported in INSERT statements - query: `{0}`")]
36+
TableExpressionInInsertStatement(String),
37+
#[error("[E016] Column '{column}' not found in table '{table}'. Available columns: {available_columns}")]
38+
ColumnNotFoundInTable {
39+
column: String,
40+
table: String,
41+
available_columns: String,
42+
},
43+
#[error("[E017] Failed to process INSERT statement: {reason}. Query: `{query}`")]
44+
InsertStatementProcessingFailed { reason: String, query: String },
45+
#[error("[E018] Failed to process UPDATE statement: {reason}. Query: `{query}`")]
46+
UpdateStatementProcessingFailed { reason: String, query: String },
47+
#[error("[E019] Table '{table}' not found in database schema. Check that the table exists and is accessible.")]
48+
TableNotFoundInSchema { table: String },
49+
#[error("[E020] Failed to infer table name while processing WHERE clause. Query: `{query}`")]
50+
TableNameInferenceFailedInWhere { query: String },
3551
#[error("Unknown error: `{0}`")]
3652
Unknown(String),
3753
}

src/ts_generator/generator.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ pub fn write_single_ts_file(sqls_to_write: String) -> Result<()> {
7777
))?;
7878

7979
let parent_output_path: Option<&Path> = output.parent();
80-
if parent_output_path.is_some() {
81-
fs::create_dir_all(parent_output_path.unwrap())?;
80+
if let Some(parent_output_path) = parent_output_path {
81+
fs::create_dir_all(parent_output_path)?;
8282
}
8383

8484
let mut file_to_write = OpenOptions::new()

src/ts_generator/sql_parser/expressions/translate_expr.rs

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -124,15 +124,17 @@ pub async fn get_sql_query_param(
124124
single_table_name: &Option<&str>,
125125
table_with_joins: &Option<Vec<TableWithJoins>>,
126126
db_conn: &DBConn,
127-
) -> Option<(TsFieldType, bool, Option<String>)> {
127+
) -> Result<Option<(TsFieldType, bool, Option<String>)>, TsGeneratorError> {
128128
let table_name: Option<String>;
129129

130130
if table_with_joins.is_some() {
131131
table_name = translate_table_from_expr(table_with_joins, &left.clone()).ok();
132132
} else if single_table_name.is_some() {
133133
table_name = single_table_name.map(|x| x.to_string());
134134
} else {
135-
panic!("failed to find an appropriate table name while processing WHERE statement")
135+
return Err(TsGeneratorError::TableNameInferenceFailedInWhere {
136+
query: left.to_string(),
137+
});
136138
}
137139

138140
let column_name = translate_column_name_expr(left);
@@ -149,15 +151,27 @@ pub async fn get_sql_query_param(
149151
.await
150152
.fetch_table(&table_names, db_conn)
151153
.await
152-
.unwrap_or_else(|| panic!("Failed to fetch columns for table {table_name}"));
154+
.ok_or_else(|| TsGeneratorError::TableNotFoundInSchema {
155+
table: table_name.clone(),
156+
})?;
153157

154158
// get column and return TsFieldType
155-
let column = columns
156-
.get(column_name.as_str())
157-
.unwrap_or_else(|| panic!("Failed to find the column from the table schema of {table_name}"));
158-
Some((column.field_type.to_owned(), column.is_nullable, Some(expr_placeholder)))
159+
let column = columns.get(column_name.as_str()).ok_or_else(|| {
160+
let available_columns = columns.keys().map(|k| k.as_str()).collect::<Vec<_>>().join(", ");
161+
TsGeneratorError::ColumnNotFoundInTable {
162+
column: column_name.clone(),
163+
table: table_name.clone(),
164+
available_columns,
165+
}
166+
})?;
167+
168+
Ok(Some((
169+
column.field_type.to_owned(),
170+
column.is_nullable,
171+
Some(expr_placeholder),
172+
)))
159173
}
160-
_ => None,
174+
_ => Ok(None),
161175
}
162176
}
163177

@@ -298,7 +312,7 @@ pub async fn translate_expr(
298312
// OPERATORS START //
299313
/////////////////////
300314
Expr::BinaryOp { left, op: _, right } => {
301-
let param = get_sql_query_param(left, right, single_table_name, table_with_joins, db_conn).await;
315+
let param = get_sql_query_param(left, right, single_table_name, table_with_joins, db_conn).await?;
302316
if let Some((value, is_nullable, index)) = param {
303317
let _ = ts_query.insert_param(&value, &is_nullable, &index);
304318
Ok(())
@@ -341,7 +355,7 @@ pub async fn translate_expr(
341355
table_with_joins,
342356
db_conn,
343357
)
344-
.await;
358+
.await?;
345359

346360
if let Some((value, is_nullable, index)) = result {
347361
let array_item = TsFieldType::Array(Box::new(value));
@@ -369,8 +383,8 @@ pub async fn translate_expr(
369383
low,
370384
high,
371385
} => {
372-
let low = get_sql_query_param(expr, low, single_table_name, table_with_joins, db_conn).await;
373-
let high = get_sql_query_param(expr, high, single_table_name, table_with_joins, db_conn).await;
386+
let low = get_sql_query_param(expr, low, single_table_name, table_with_joins, db_conn).await?;
387+
let high = get_sql_query_param(expr, high, single_table_name, table_with_joins, db_conn).await?;
374388
if let Some((value, is_nullable, placeholder)) = low {
375389
ts_query.insert_param(&value, &is_nullable, &placeholder)?;
376390
}
@@ -731,16 +745,35 @@ pub async fn translate_assignment(
731745
let value = get_expr_placeholder(&assignment.value);
732746

733747
if value.is_some() {
734-
let table_details = &DB_SCHEMA
748+
let table_details = DB_SCHEMA
735749
.lock()
736750
.await
737751
.fetch_table(&vec![table_name], db_conn)
738752
.await
739-
.unwrap();
740-
let column_name = translate_column_name_assignment(assignment).unwrap();
741-
let field = table_details
742-
.get(&column_name)
743-
.unwrap_or_else(|| panic!("Failed to find the column detail for {column_name}"));
753+
.ok_or_else(|| TsGeneratorError::TableNotFoundInSchema {
754+
table: table_name.to_string(),
755+
})?;
756+
757+
let column_name = translate_column_name_assignment(assignment).ok_or_else(|| {
758+
TsGeneratorError::UpdateStatementProcessingFailed {
759+
reason: "Failed to extract column name from assignment".to_string(),
760+
query: format!("UPDATE {} SET {} = ...", table_name, assignment),
761+
}
762+
})?;
763+
764+
let field = table_details.get(&column_name).ok_or_else(|| {
765+
let available_columns = table_details
766+
.keys()
767+
.map(|k| k.as_str())
768+
.collect::<Vec<_>>()
769+
.join(", ");
770+
TsGeneratorError::ColumnNotFoundInTable {
771+
column: column_name.clone(),
772+
table: table_name.to_string(),
773+
available_columns,
774+
}
775+
})?;
776+
744777
let _ = ts_query.insert_param(&field.field_type, &field.is_nullable, &value);
745778
}
746779
Ok(())

src/ts_generator/sql_parser/translate_insert.rs

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,32 @@ pub async fn translate_insert(
4141
if placeholder.is_some() {
4242
let match_col = &columns
4343
.get(column)
44-
.unwrap_or_else(|| {
45-
panic!(
46-
r#"
47-
Failed to process values of insert statement as column names are not provided or incorrectly specified
48-
49-
Try specifying column names
50-
```
51-
INSERT INTO table_name (column1, column2, column3, ...)
52-
VALUES (value1, value2, value3, ...);
53-
```
54-
"#
55-
)
56-
})
44+
.ok_or_else(|| {
45+
TsGeneratorError::InsertStatementProcessingFailed {
46+
reason: format!(
47+
"Column at position {} is not provided in the column list. Expected {} columns but found {} values.",
48+
column, columns.len(), values.len()
49+
),
50+
query: format!(
51+
"INSERT INTO {} VALUES (...). Try specifying column names explicitly: INSERT INTO {} (column1, column2, ...) VALUES (...)",
52+
table_name, table_name
53+
),
54+
}
55+
})?
5756
.value;
5857

59-
let field = table_details
60-
.get(match_col.as_str())
61-
.unwrap_or_else(|| panic!("Column {match_col} is not found while processing insert params"));
58+
let field = table_details.get(match_col.as_str()).ok_or_else(|| {
59+
let available_columns = table_details
60+
.keys()
61+
.map(|k| k.as_str())
62+
.collect::<Vec<_>>()
63+
.join(", ");
64+
TsGeneratorError::ColumnNotFoundInTable {
65+
column: match_col.clone(),
66+
table: table_name.to_string(),
67+
available_columns,
68+
}
69+
})?;
6270

6371
if value.to_string() == "?" {
6472
// If the placeholder is `'?'`, we can process it using insert_value_params and generate nested params type
@@ -104,7 +112,12 @@ VALUES (value1, value2, value3, ...);
104112
SetExpr::Update(update) => translate_stmt(ts_query, &update, None, conn).await?,
105113
SetExpr::Delete(delete) => translate_stmt(ts_query, &delete, None, conn).await?,
106114
SetExpr::Merge(merge) => translate_stmt(ts_query, &merge, None, conn).await?,
107-
SetExpr::Table(_) => unimplemented!("Table expressions are not supported in INSERT statements"),
115+
SetExpr::Table(table) => {
116+
return Err(TsGeneratorError::TableExpressionInInsertStatement(format!(
117+
"INSERT INTO {} ... FROM {}",
118+
table_name, table
119+
)));
120+
}
108121
}
109122

110123
Ok(())

src/ts_generator/sql_parser/translate_query.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,12 @@ pub async fn translate_select(
123123
let mut table_name: Option<&str> = None;
124124
if full_table_with_joins.is_some() && !full_table_with_joins.as_ref().unwrap().is_empty() {
125125
table_name_owned = Some(
126-
translate_table_with_joins(full_table_with_joins, &select_item)
127-
.unwrap_or_else(|_| panic!("{}", format!("Default FROM table is not found from the query {select}"))),
126+
translate_table_with_joins(full_table_with_joins, &select_item).map_err(|_| {
127+
TsGeneratorError::UnknownErrorWhileProcessingTableWithJoins(format!(
128+
"Default FROM table is not found from the query: {}. Ensure your query has a valid FROM clause.",
129+
select
130+
))
131+
})?,
128132
);
129133
table_name = table_name_owned.as_deref();
130134
}

src/ts_generator/types/ts_query.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ impl TsQuery {
363363
.and_then(|m| m.as_str().parse::<i32>().ok())
364364
.ok_or(TsGeneratorError::UnknownPlaceholder(format!(
365365
"{placeholder} is not a valid placeholder parameter in PostgreSQL"
366-
)))? as i32
366+
)))?
367367
} else {
368368
// No pattern matches the provided placeholder, simply exit out of the function
369369
return Ok(());

0 commit comments

Comments
 (0)