Skip to content

Diesel queries: should we prefer is_not_distinct_from() over separate eq() / is_null() branches? #9834

@jgallagher

Description

@jgallagher

(This is followup from #9570 where we added additional branching queries.)

When we compare a nullable column against a Rust value with the type Option<T>, we often box the query and branch based on its value to either compare with eq or check for is_null; e.g. from main:

let query = match config.vrf.clone() {
Some(v) => query.filter(dsl::vrf.eq(v)),
None => query.filter(dsl::vrf.is_null()),
};
let query = match config.shaper.clone() {
Some(v) => query.filter(dsl::shaper.eq(v)),
None => query.filter(dsl::shaper.is_null()),
};
let query = match config.checker.clone() {
Some(v) => query.filter(dsl::checker.eq(v)),
None => query.filter(dsl::checker.is_null()),
};

This is because eq() faithfully reproduces the SQL behavior of some_column = NULL returning false even if the value of some_column is itself NULL.

However, I believe we could replace these branches by using https://docs.rs/diesel/latest/diesel/expression_methods/trait.PgExpressionMethods.html#method.is_not_distinct_from, which behaves more like we'd expect from Option comparisons in Rust:

This behaves identically to the = operator, except that NULL is treated as a normal value.

The value of this is pretty small on examples like the above (although we potentially get to drop the boxing of the query to type-erase it); it becomes more of a win if the branching contains multiple other filters that have to be manually kept in sync. An example from #9570, where these branches are identical except for addr.eq(addr) vs addr.is_null():

// Query the main peer config table. For unnumbered peers,
// addr is NULL; for numbered peers, addr matches.
let active = if addr.is_some() {
peer_dsl::switch_port_settings_bgp_peer_config
.filter(db_peer::port_settings_id.eq(port_settings_id))
.filter(db_peer::addr.eq(addr))
.select(db_peer::allow_export_list_active)
.limit(1)
.first_async::<bool>(&conn)
.await
} else {
peer_dsl::switch_port_settings_bgp_peer_config
.filter(db_peer::port_settings_id.eq(port_settings_id))
.filter(db_peer::addr.is_null())
.filter(db_peer::interface_name.eq(interface_name.to_owned()))
.select(db_peer::allow_export_list_active)
.limit(1)
.first_async::<bool>(&conn)
.await
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions