go-jet / jet

Type safe SQL builder with code generation and automatic query result data mapping
Apache License 2.0
2.52k stars 118 forks source link

Unable to use `.IN` and `.NOT_IN` with dynamic values #163

Closed philippta closed 2 years ago

philippta commented 2 years ago

Describe the bug

Environment (please complete the following information):

Code snippet

import (
    . "myapp/table"

    "github.com/go-jet/jet/v2/internal/jet" // for jet.IntegerExpression, fails to compile
    . "github.com/go-jet/jet/v2/sqlite"
)

func demo() {
    var userIDs = []int64{1, 2, 3} // could be user provided
    var sqlIDs []jet.IntegerExpression

    for _, userID := range userIDs {
        sqlIDs = append(sqlIDs, Int(userID))
    }

    SELECT(Users.AllColumns).FROM(Users).WHERE(Users.UserID.IN(sqlIDs...))
}

Compilation error

use of internal package github.com/go-jet/jet/v2/internal/jet not allowed

Expected behavior Defining a slice of jet.IntegerExpression should not result in a compilation error.

go-jet commented 2 years ago

Hi @philippta . All the internal types of interest are exported through dialect package(for instance). There is no need to use internal package. In this case you should only import github.com/go-jet/jet/v2/sqlite. IN/NOT_IN method does not accepts list of IntegerExpression, but list of Expression objects. This is a bug, but it can't be fixed before V3, because it is a part of the public API.

import (
    . "myapp/table"

    . "github.com/go-jet/jet/v2/sqlite"  // !!! Always import only the dialect package !!!
)

func demo() {
    var userIDs = []int64{1, 2, 3}         // could be user provided
    var sqlIDs []Expression                // !!! sqlite.Expression list !!! 

    for _, userID := range userIDs {
        sqlIDs = append(sqlIDs, Int(userID))
    }

    SELECT(Users.AllColumns).FROM(Users).WHERE(Users.UserID.IN(sqlIDs...))
}
philippta commented 2 years ago

Thanks for clarification. This definitely helps solving my problem. Maybe it‘s worth adding this example to the wiki.

Issue can be closed then.

go-jet commented 2 years ago

wiki

josephbuchma commented 10 months ago

Some generic helpers:

// Strings converts slice of strings into slice of mysql.Expression,
// useful for IN queries.
func Strings[T ~string](s []T) []mysql.Expression {
    ret := make([]mysql.Expression, 0, len(s))
    for _, v := range s {
        ret = append(ret, mysql.Expression(mysql.String(string(v))))
    }
    return ret
}

// Integers converts slice of integers into slice of mysql.Expression,
// useful for IN queries.
func Integers[T constraints.Signed](s []T) []mysql.Expression {
    ret := make([]mysql.Expression, 0, len(s))
    for _, v := range s {
        ret = append(ret, mysql.Expression(mysql.Int(int64(v))))
    }
    return ret
}

Example usage:

import (
    . "myapp/table"

    . "github.com/go-jet/jet/v2/mysql"  // !!! Always import only the dialect package !!!
)

func demo() {
    var userIDs = []int64{1, 2, 3}         // could be user provided
    SELECT(Users.AllColumns).FROM(Users).WHERE(Users.UserID.IN(Integers(userIDs)...))
}

@go-jet do you think something like that can be a part of jet API?