Is it possible to use IN clause in plain sql Slick for integers?

后端 未结 3 1414
迷失自我
迷失自我 2021-01-05 01:03

There is a similar question here but it doesn\'t actually answer the question.

Is it possible to use IN clause in plain sql Slick?

Note that this is actually

相关标签:
3条回答
  • 2021-01-05 01:41

    The sql prefix unlocks a StringContext where you can set SQL parameters. There is no SQL parameter for a list, so you can easily end up opening yourself up to SQL injection here if you're not careful. There are some good (and some dangerous) suggestions about dealing with this problem with SQLServer on this question. You have a few options:

    Your best bet is probably to use the #$ operator together with mkString to interpolate dynamic SQL:

    val sql = sql"""SELECT * FROM coffee WHERE id IN (#${ids.mkString(",")})"""
    

    This doesn't properly use parameters and therefore might be open to SQL-injection and other problems.

    Another option is to use regular string interpolation and mkString to build the statement:

    val query = s"""SELECT * FROM coffee WHERE id IN (${ids.mkString(",")})"""
    StaticQuery.queryNA[Coffee](query)
    

    This is essentially the same approach as using #$, but might be more flexible in the general case.

    If SQL-injection vulnerability is a major concern (e.g. if the elements of ids are user provided), you can build a query with a parameter for each element of ids. Then you'll need to provide a custom SetParameter instance so that slick can turn the List into parameters:

    implicit val setStringListParameter = new SetParameter[List[String]]{
        def apply(v1: List[String], v2: PositionedParameters): Unit = {
            v1.foreach(v2.setString)
        }
    }
    
    val idsInClause = List.fill(ids.length)("?").mkString("(", ",", ")")
    val query = s"""SELECT * FROM coffee WHERE id IN ($idsInClause)"""
    Q.query[List[String], String](query).apply(ids).list(s)
    

    Since your ids are Ints, this is probably less of a concern, but if you prefer this method, you would just need to change the setStringListParameter to use Int instead of String:

    0 讨论(0)
  • 2021-01-05 01:49

    Although this is not universal answer and may not be what the author wanted, I still want to point this out to whoever views this question.

    Some DB backends support array types, and there are extensions to Slick that allow setting these array types in the interpolations.

    For example, Postgres has the syntax where column = any(array), and with slick-pg you can use this syntax like so:

    def query(ids: Seq[Long]) = db.run(sql"select * from table where ids = any($ids)".as[Long])
    

    This brings a much cleaner syntax, which is friendlier to the statement compiler cache and also safe from SQL injections and overall danger of creating a malformed SQL with the #$var interpolation syntax.

    0 讨论(0)
  • 2021-01-05 01:53
      val ids = List(610113193610210035L, 220702198208189710L)
    
      implicit object SetListLong extends SetParameter[List[Long]] {
        def apply(vList: List[Long], pp: PositionedParameters) {
          vList.foreach(pp.setLong)
        }
      }
    
      val select = sql"""
            select idnum from idnum_0
            where idnum in ($ids#${",?" * (ids.size - 1)})
        """.as[Long]
    

    @Ben Reich is right. this is another sample code, test on slick 3.1.0.

    ($ids#${",?" * (ids.size - 1)})

    0 讨论(0)
提交回复
热议问题