fix: abort query builder for empty arrays

Signed-off-by: Greg Heartsfield <scsibug@imap.cc>
This commit is contained in:
Kieran 2023-11-23 00:15:18 +00:00 committed by Greg Heartsfield
parent b4471a6698
commit ab736f5f98

View File

@ -726,7 +726,6 @@ fn query_from_filter(f: &ReqFilter) -> Option<QueryBuilder<Postgres>> {
} }
let mut query = QueryBuilder::new("SELECT e.\"content\", e.created_at FROM \"event\" e WHERE "); let mut query = QueryBuilder::new("SELECT e.\"content\", e.created_at FROM \"event\" e WHERE ");
// This tracks whether we need to push a prefix AND before adding another clause // This tracks whether we need to push a prefix AND before adding another clause
let mut push_and = false; let mut push_and = false;
// Query for "authors", allowing prefix matches // Query for "authors", allowing prefix matches
@ -734,140 +733,142 @@ fn query_from_filter(f: &ReqFilter) -> Option<QueryBuilder<Postgres>> {
// filter out non-hex values // filter out non-hex values
let auth_vec: Vec<&String> = auth_vec.iter().filter(|a| is_hex(a)).collect(); let auth_vec: Vec<&String> = auth_vec.iter().filter(|a| is_hex(a)).collect();
if !auth_vec.is_empty() { if auth_vec.is_empty() {
query.push("("); return None;
}
query.push("(");
// shortcut authors into "IN" query // shortcut authors into "IN" query
let any_is_range = auth_vec.iter().any(|pk| pk.len() != 64); let any_is_range = auth_vec.iter().any(|pk| pk.len() != 64);
if !any_is_range { if !any_is_range {
query.push("e.pub_key in ("); query.push("e.pub_key in (");
let mut pk_sep = query.separated(", "); let mut pk_sep = query.separated(", ");
for pk in auth_vec.iter() { for pk in auth_vec.iter() {
pk_sep.push_bind(hex::decode(pk).ok()); pk_sep.push_bind(hex::decode(pk).ok());
} }
query.push(") OR e.delegated_by in ("); query.push(") OR e.delegated_by in (");
let mut pk_delegated_sep = query.separated(", "); let mut pk_delegated_sep = query.separated(", ");
for pk in auth_vec.iter() { for pk in auth_vec.iter() {
pk_delegated_sep.push_bind(hex::decode(pk).ok()); pk_delegated_sep.push_bind(hex::decode(pk).ok());
}
query.push(")");
push_and = true;
} else {
let mut range_authors = query.separated(" OR ");
for auth in auth_vec {
match hex_range(auth) {
Some(HexSearch::Exact(ex)) => {
range_authors
.push("(e.pub_key = ")
.push_bind_unseparated(ex.clone())
.push_unseparated(" OR e.delegated_by = ")
.push_bind_unseparated(ex)
.push_unseparated(")");
}
Some(HexSearch::Range(lower, upper)) => {
range_authors
.push("((e.pub_key > ")
.push_bind_unseparated(lower.clone())
.push_unseparated(" AND e.pub_key < ")
.push_bind_unseparated(upper.clone())
.push_unseparated(") OR (e.delegated_by > ")
.push_bind_unseparated(lower)
.push_unseparated(" AND e.delegated_by < ")
.push_bind_unseparated(upper)
.push_unseparated("))");
}
Some(HexSearch::LowerOnly(lower)) => {
range_authors
.push("(e.pub_key > ")
.push_bind_unseparated(lower.clone())
.push_unseparated(" OR e.delegated_by > ")
.push_bind_unseparated(lower)
.push_unseparated(")");
}
None => {
info!("Could not parse hex range from author {:?}", auth);
}
}
push_and = true;
}
} }
query.push(")"); query.push(")");
push_and = true;
} else {
let mut range_authors = query.separated(" OR ");
for auth in auth_vec {
match hex_range(auth) {
Some(HexSearch::Exact(ex)) => {
range_authors
.push("(e.pub_key = ")
.push_bind_unseparated(ex.clone())
.push_unseparated(" OR e.delegated_by = ")
.push_bind_unseparated(ex)
.push_unseparated(")");
}
Some(HexSearch::Range(lower, upper)) => {
range_authors
.push("((e.pub_key > ")
.push_bind_unseparated(lower.clone())
.push_unseparated(" AND e.pub_key < ")
.push_bind_unseparated(upper.clone())
.push_unseparated(") OR (e.delegated_by > ")
.push_bind_unseparated(lower)
.push_unseparated(" AND e.delegated_by < ")
.push_bind_unseparated(upper)
.push_unseparated("))");
}
Some(HexSearch::LowerOnly(lower)) => {
range_authors
.push("(e.pub_key > ")
.push_bind_unseparated(lower.clone())
.push_unseparated(" OR e.delegated_by > ")
.push_bind_unseparated(lower)
.push_unseparated(")");
}
None => {
info!("Could not parse hex range from author {:?}", auth);
}
}
push_and = true;
}
} }
query.push(")");
} }
// Query for Kind // Query for Kind
if let Some(ks) = &f.kinds { if let Some(ks) = &f.kinds {
if !ks.is_empty() { if ks.is_empty() {
if push_and { return None;
query.push(" AND ");
}
push_and = true;
query.push("e.kind in (");
let mut list_query = query.separated(", ");
for k in ks.iter() {
list_query.push_bind(*k as i64);
}
query.push(")");
} }
if push_and {
query.push(" AND ");
}
push_and = true;
query.push("e.kind in (");
let mut list_query = query.separated(", ");
for k in ks.iter() {
list_query.push_bind(*k as i64);
}
query.push(")");
} }
// Query for event, allowing prefix matches // Query for event, allowing prefix matches
if let Some(id_vec) = &f.ids { if let Some(id_vec) = &f.ids {
// filter out non-hex values // filter out non-hex values
let id_vec: Vec<&String> = id_vec.iter().filter(|a| is_hex(a)).collect(); let id_vec: Vec<&String> = id_vec.iter().filter(|a| is_hex(a)).collect();
if id_vec.is_empty() {
return None;
}
if push_and {
query.push(" AND (");
} else {
query.push("(");
}
push_and = true;
if !id_vec.is_empty() { // shortcut ids into "IN" query
if push_and { let any_is_range = id_vec.iter().any(|pk| pk.len() != 64);
query.push(" AND ("); if !any_is_range {
} else { query.push("id in (");
query.push("("); let mut sep = query.separated(", ");
for id in id_vec.iter() {
sep.push_bind(hex::decode(id).ok());
} }
push_and = true; query.push(")");
} else {
// shortcut ids into "IN" query // take each author and convert to a hex search
let any_is_range = id_vec.iter().any(|pk| pk.len() != 64); let mut id_query = query.separated(" OR ");
if !any_is_range { for id in id_vec {
query.push("id in ("); match hex_range(id) {
let mut sep = query.separated(", "); Some(HexSearch::Exact(ex)) => {
for id in id_vec.iter() { id_query
sep.push_bind(hex::decode(id).ok()); .push("(id = ")
} .push_bind_unseparated(ex)
query.push(")"); .push_unseparated(")");
} else { }
// take each author and convert to a hex search Some(HexSearch::Range(lower, upper)) => {
let mut id_query = query.separated(" OR "); id_query
for id in id_vec { .push("(id > ")
match hex_range(id) { .push_bind_unseparated(lower)
Some(HexSearch::Exact(ex)) => { .push_unseparated(" AND id < ")
id_query .push_bind_unseparated(upper)
.push("(id = ") .push_unseparated(")");
.push_bind_unseparated(ex) }
.push_unseparated(")"); Some(HexSearch::LowerOnly(lower)) => {
} id_query
Some(HexSearch::Range(lower, upper)) => { .push("(id > ")
id_query .push_bind_unseparated(lower)
.push("(id > ") .push_unseparated(")");
.push_bind_unseparated(lower) }
.push_unseparated(" AND id < ") None => {
.push_bind_unseparated(upper) info!("Could not parse hex range from id {:?}", id);
.push_unseparated(")");
}
Some(HexSearch::LowerOnly(lower)) => {
id_query
.push("(id > ")
.push_bind_unseparated(lower)
.push_unseparated(")");
}
None => {
info!("Could not parse hex range from id {:?}", id);
}
} }
} }
} }
query.push(")");
} }
query.push(")");
} }
// Query for tags // Query for tags
@ -881,6 +882,9 @@ fn query_from_filter(f: &ReqFilter) -> Option<QueryBuilder<Postgres>> {
let mut push_or = false; let mut push_or = false;
query.push("e.id IN (SELECT ee.id FROM \"event\" ee LEFT JOIN tag t on ee.id = t.event_id WHERE ee.hidden != 1::bit(1) and "); query.push("e.id IN (SELECT ee.id FROM \"event\" ee LEFT JOIN tag t on ee.id = t.event_id WHERE ee.hidden != 1::bit(1) and ");
for (key, val) in map.iter() { for (key, val) in map.iter() {
if val.is_empty() {
return None;
}
if push_or { if push_or {
query.push(" OR "); query.push(" OR ");
} }
@ -1065,4 +1069,21 @@ mod tests {
let q = query_from_filter(&filter).unwrap(); let q = query_from_filter(&filter).unwrap();
assert_eq!(q.sql(), "SELECT e.\"content\", e.created_at FROM \"event\" e WHERE e.kind in ($1) AND e.id IN (SELECT ee.id FROM \"event\" ee LEFT JOIN tag t on ee.id = t.event_id WHERE ee.hidden != 1::bit(1) and (t.\"name\" = $2 AND (value in ($3))) OR (t.\"name\" = $4 AND (value in ($5)))) AND e.hidden != 1::bit(1) AND (e.expires_at IS NULL OR e.expires_at > now()) ORDER BY e.created_at ASC LIMIT 1000") assert_eq!(q.sql(), "SELECT e.\"content\", e.created_at FROM \"event\" e WHERE e.kind in ($1) AND e.id IN (SELECT ee.id FROM \"event\" ee LEFT JOIN tag t on ee.id = t.event_id WHERE ee.hidden != 1::bit(1) and (t.\"name\" = $2 AND (value in ($3))) OR (t.\"name\" = $4 AND (value in ($5)))) AND e.hidden != 1::bit(1) AND (e.expires_at IS NULL OR e.expires_at > now()) ORDER BY e.created_at ASC LIMIT 1000")
} }
#[test]
fn test_query_empty_tags() {
let filter = ReqFilter {
ids: None,
kinds: Some(vec![1, 6, 16, 30023, 1063, 6969]),
since: Some(1700697846),
until: None,
authors: None,
limit: None,
tags: Some(HashMap::from([
('a', HashSet::new())
])),
force_no_match: false,
};
assert!(query_from_filter(&filter).is_none());
}
} }