SeaQL/sea-query

Double paren causes SQL error when using `IN` operator

Closed this issue · 3 comments

Description

Values used by IN operator get double wrapped in parens. Example code that reproduces the error:

use sea_query::{BinOper, Expr, Iden, MysqlQueryBuilder, Query, SimpleExpr};

#[derive(Iden)]
enum Char {
    Table,
    Character,
}

fn main() {
    let condition = Expr::col((Char::Table, Char::Character)).binary(
        BinOper::In,
        SimpleExpr::Values(vec![sea_query::Value::from(1), sea_query::Value::from(2)]),
    );

    let query = Query::select()
        .column(Char::Character)
        .from(Char::Table)
        .and_where(condition)
        .to_string(MysqlQueryBuilder);

    println!("{}", query);
}

When sea-query 0.30.1 is used, I get this output:

SELECT `character` FROM `char` WHERE `char`.`character` IN ((1, 2)) 

We get a runtime error (using mariadb 10.6.14):

4078 (HY000): Illegal parameter data types bigint and row for operation '='

Removing the double paren makes the error go away. I'll try to minimize the expression that causes the error.
EDIT: Using 2 values triggers the error (MRE code updated).

When sea-query 0.30.0 is used, I get this output:

SELECT `character` FROM `char` WHERE `char`.`character` IN (1, 2)

And no runtime error.

Could be a regression related to #675 because this error started to happen in 0.30.1. Version 0.30.0 does not have this error.

Reproduces How Often

Always

Versions

0.30.1

I'd suggest to yank 0.30.1 version,because it's a breaking change that causes queries to fail.

I can confirm this should be a consequence of 675, however I'd suggest you to use SimpleExpr::Tuple instead of Values. Since it was changed quite a long time ago in

Expr::is_in, Expr::is_not_in now accepts Into<SimpleExpr> instead of Into<Value> and convert it to SimpleExpr::Tuple instead of SimpleExpr::Values #476

sea-query/src/expr.rs

Lines 1515 to 1523 in 5922d6f

pub fn is_in<V, I>(mut self, v: I) -> SimpleExpr
where
V: Into<SimpleExpr>,
I: IntoIterator<Item = V>,
{
self.bopr = Some(BinOper::In);
self.right = Some(SimpleExpr::Tuple(v.into_iter().map(|v| v.into()).collect()));
self.into()
}

If you also look at in_tuples, it should explain the semantic of SimpleExpr::Values. SimpleExpr::Values is always wrapped in parens.

SimpleExpr::Values is only being used in in_tuples, but I am not sure whether we can change the current behaviour without breaking some other things.