mirror of
https://github.com/trailbaseio/trailbase.git
synced 2026-02-20 09:19:38 -06:00
Fix parsing of elided aliases in SQLite statements. Handle failure to parse views more gracefully.
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -6936,8 +6936,6 @@ dependencies = [
|
||||
"maxminddb",
|
||||
"mini-moka",
|
||||
"parking_lot",
|
||||
"rand 0.9.1",
|
||||
"rand_core 0.9.3",
|
||||
"regex",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
|
||||
@@ -275,11 +275,15 @@ pub async fn lookup_and_parse_all_view_schemas(
|
||||
|
||||
for row in rows.iter() {
|
||||
let sql: String = row.get(0)?;
|
||||
views.push({
|
||||
let mut view: View = sqlite3_parse_view(&sql, tables)?;
|
||||
view.name.database_schema = Some(db.name.clone());
|
||||
view
|
||||
});
|
||||
match sqlite3_parse_view(&sql, tables) {
|
||||
Ok(mut view) => {
|
||||
view.name.database_schema = Some(db.name.clone());
|
||||
views.push(view);
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Failed to parse VIEW definition '{sql}': {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,15 +13,13 @@ crate-type=["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
arc-swap = "1.7.1"
|
||||
argon2 = { version = "^0.5.3", default-features = false, features = ["alloc", "password-hash", "rand"] }
|
||||
argon2 = { version = "^0.5.3", default-features = false, features = ["alloc", "password-hash", "rand", "std"] }
|
||||
base64 = { version = "0.22.1", default-features = false }
|
||||
jsonschema = { version = "0.30.0", default-features = false }
|
||||
log = "0.4.27"
|
||||
maxminddb = "0.26.0"
|
||||
mini-moka = "0.10.3"
|
||||
parking_lot = { version = "0.12.3", default-features = false }
|
||||
rand = "^0.9.0"
|
||||
rand_core = "^0.9"
|
||||
regex = "1.11.0"
|
||||
rusqlite = { workspace = true }
|
||||
serde = { version = "^1.0.203", features = ["derive"] }
|
||||
|
||||
@@ -974,17 +974,15 @@ fn to_entry(
|
||||
qn: AstQualifiedName,
|
||||
alias: Option<sqlite3_parser::ast::As>,
|
||||
) -> (String, QualifiedName) {
|
||||
return (
|
||||
alias
|
||||
.and_then(|alias| {
|
||||
if let sqlite3_parser::ast::As::As(name) = alias {
|
||||
return Some(unquote_name(name));
|
||||
}
|
||||
None
|
||||
})
|
||||
.unwrap_or_else(|| qn.to_string()),
|
||||
qn.into(),
|
||||
);
|
||||
let key = match alias {
|
||||
// "FROM table_name AS alias"
|
||||
Some(sqlite3_parser::ast::As::As(name)) => unquote_name(name),
|
||||
// "FROM table_name alias"
|
||||
Some(sqlite3_parser::ast::As::Elided(name)) => unquote_name(name),
|
||||
_ => qn.to_string(),
|
||||
};
|
||||
|
||||
return (key, qn.into());
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -1151,11 +1149,11 @@ fn try_extract_column_mapping(
|
||||
}
|
||||
Expr::Qualified(qualifier, name) => {
|
||||
let qualifier = unquote_name(qualifier);
|
||||
let col_name = unquote_name(name);
|
||||
let col_name = unquote_name(name.clone());
|
||||
|
||||
let Some(table_name) = table_names.get(&qualifier) else {
|
||||
return Err(SchemaError::Precondition(
|
||||
format!("Missing table with qualifier: {qualifier}").into(),
|
||||
format!("Missing table: Qualified({qualifier}, {name})").into(),
|
||||
));
|
||||
};
|
||||
|
||||
@@ -1535,6 +1533,66 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_view_column_extraction() {
|
||||
let tables = vec![Table {
|
||||
name: QualifiedName {
|
||||
name: "table_name".to_string(),
|
||||
database_schema: None,
|
||||
},
|
||||
strict: true,
|
||||
columns: vec![Column {
|
||||
name: "column".to_string(),
|
||||
data_type: ColumnDataType::Text,
|
||||
options: vec![],
|
||||
}],
|
||||
foreign_keys: vec![],
|
||||
unique: vec![],
|
||||
checks: vec![],
|
||||
virtual_table: false,
|
||||
temporary: false,
|
||||
}];
|
||||
|
||||
{
|
||||
// No alias
|
||||
let sql = "SELECT column FROM table_name";
|
||||
let sqlite3_parser::ast::Stmt::Select(select) =
|
||||
sqlite3_parse_into_statement(sql).unwrap().unwrap()
|
||||
else {
|
||||
panic!("Not a select");
|
||||
};
|
||||
let _mapping = try_extract_column_mapping(*select, &tables)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
// With alias
|
||||
let sql = "SELECT alias.column FROM table_name AS alias";
|
||||
let sqlite3_parser::ast::Stmt::Select(select) =
|
||||
sqlite3_parse_into_statement(sql).unwrap().unwrap()
|
||||
else {
|
||||
panic!("Not a select");
|
||||
};
|
||||
let _mapping = try_extract_column_mapping(*select, &tables)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
// With "elided" alias
|
||||
let sql = "SELECT alias.column FROM table_name alias";
|
||||
let sqlite3_parser::ast::Stmt::Select(select) =
|
||||
sqlite3_parse_into_statement(sql).unwrap().unwrap()
|
||||
else {
|
||||
panic!("Not a select");
|
||||
};
|
||||
let _mapping = try_extract_column_mapping(*select, &tables)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_view_column_extraction_join() {
|
||||
let sql = "SELECT user, *, a.*, p.user AS foo FROM foo.articles AS a LEFT JOIN bar.profiles AS p ON p.user = a.author";
|
||||
let sqlite3_parser::ast::Stmt::Select(select) =
|
||||
sqlite3_parse_into_statement(sql).unwrap().unwrap()
|
||||
|
||||
Reference in New Issue
Block a user