【问题标题】:Is it possible to use IN clause in plain sql Slick for integers?是否可以在纯 sql Slick 中对整数使用 IN 子句?
【发布时间】:2015-07-01 08:46:23
【问题描述】:

这里有一个类似的问题,但它实际上并没有回答这个问题。

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

请注意,这实际上是更大更复杂查询的一部分,因此我确实需要使用普通 sql 而不是 slick 的提升嵌入。像下面这样的东西会很好:

val ids = List(2,4,9)
sql"SELECT * FROM coffee WHERE id IN ($ids)"

【问题讨论】:

    标签: scala slick


    【解决方案1】:

    sql 前缀解锁 StringContext,您可以在其中设置 SQL 参数。列表没有 SQL 参数,因此如果您不小心,很容易在此处打开自己的 SQL 注入。关于this question 上的 SQLServer 处理此问题有一些好的(也有一些危险的)建议。您有几个选择:

    您最好的选择可能是使用#$ 运算符和mkString 来插入动态SQL:

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

    这不能正确使用参数,因此可能会导致 SQL 注入和其他问题。

    另一种选择是使用常规字符串插值和mkString 来构建语句:

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

    这与使用#$ 的方法基本相同,但在一般情况下可能更灵活。

    如果 SQL 注入漏洞是一个主要问题(例如,如果ids 的元素是用户提供的),您可以为ids 的每个元素构建一个带有参数的查询。然后你需要提供一个自定义的 SetParameter 实例,以便 slick 可以将 List 转换为参数:

    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)
    

    由于您的idsInts,这可能不太需要担心,但如果您更喜欢这种方法,您只需将setStringListParameter 更改为使用Int 而不是String

    【讨论】:

    • 如果ids 的类型为List[Int],即使它们是用户提供的,我也看不到如何进行sql 注入。
    • @Daenyth 这绝对不是问题(尽管有时整数 SQL 注入可能是一个问题,因为会导致除以零或其他异常,然后利用失败状态 – 谷歌“sql 注入整数” )。但我想说,最好的做法是使用参数来避免以后出现问题(例如,如果另一个开发人员将类型更改为 String 以适应包含某些字符的新 ID 类型怎么办)。当这里是String 时,我真的只是在报道这个案子。
    • 感谢本的回答!关于可能存在漏洞的解决方案的信息非常丰富。但是,我同意@Daenyth,您不能使用显式整数类型进行 sql 注入。
    • 答案也可以应用于 List[String] 的情况,这可能是一个问题。
    • 你在这里使用Q.query,是否可以使用slick 3.1.1中的sql插值并定义一个SetParameter?
    【解决方案2】:
      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 是对的。 这是另一个示例代码,在 slick 3.1.0 上进行测试。

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

    【讨论】:

      【解决方案3】:

      虽然这不是普遍的答案,也可能不是作者想要的,但我仍然想向任何看到这个问题的人指出这一点。

      一些 DB 后端支持数组类型,并且 Slick 的扩展允许在插值中设置这些数组类型。

      例如,Postgres 的语法是 where column = any(array),而 slick-pg 可以像这样使用这个语法:

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

      这带来了更简洁的语法,它对语句编译器缓存更友好,也可以避免 SQL 注入以及使用#$var 插值语法创建格式错误的 SQL 的总体危险。

      【讨论】:

        【解决方案4】:

        尝试在 MySQL 的 IN 查询中使用 Seq[Long] 时,在 Slick 3.3.3 中遇到了基本相同的问题。不断收到来自 Slick 的编译错误:

        找不到参数 e 的隐含值:slick.jdbc.SetParameter[Seq[Long]]

        原来的问题应该是这样的:

        找不到参数 e 的隐含值:slick.jdbc.SetParameter[List[Int]]

        Slick 3.3.X+ 可以处理 IN 查询的参数绑定,只要我们提供 Slick 应该如何为我们正在使用的类型执行此操作的隐式定义。这意味着在类级别的某处添加implicit val 定义。所以,比如:

        class MyClass {
          // THIS IS THIS KEY LINE TO ENABLE SLICK TO BIND THE PARAMS
          implicit val setListInt = SetParameter[List[Int]]((inputList, params) => inputList.foreach(params.setInt))
        
          def queryByHardcodedIds() = {
            val ids: List[Int] = List(2,4,9)
        
            sql"SELECT * FROM coffee WHERE id IN ($ids)" // SLICK CAN AUTO-HANDLE BINDING NOW
          }
        }
        

        Seq[Long] 和其他的情况类似。只需确保您的类型/绑定与您需要 Slick 处理的内容保持一致:

        implicit val setSeqLong = SetParameter[Seq[Long]]((inputList, params) => inputList.foreach(params.setLong))
        // ^^Note the `SetParameter[Seq[Long]]` & `.setLong` for type alignment
        
        

        【讨论】:

          猜你喜欢
          • 2013-06-28
          • 1970-01-01
          • 1970-01-01
          • 2016-10-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-05
          • 2019-03-03
          相关资源
          最近更新 更多