2015-07-01 5 views
5

Es gibt eine ähnliche Frage hier, aber es beantwortet nicht wirklich die Frage.Ist es möglich, IN-Klausel in Plain sql Slick für ganze Zahlen zu verwenden?

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

Beachten Sie, dass dies tatsächlich Teil einer größeren und komplexeren Abfrage, so dass ich brauche Ebene SQL verwenden statt Slicks angehoben Einbettung. So etwas wie die folgenden wird gut sein:

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

Antwort

11

Der sql Präfix entriegelt ein StringContext, wo Sie SQL-Parameter einstellen können. Es gibt keinen SQL-Parameter für eine Liste, so dass Sie sich hier leicht öffnen können, wenn Sie nicht vorsichtig sind. Es gibt einige gute (und einige gefährliche) Vorschläge zum Umgang mit diesem Problem mit SQLServer unter this question. Sie haben ein paar Optionen:

Ihre beste Wette ist wahrscheinlich den #$ Operator verwenden zusammen mit mkString dynamischen SQL zu interpolieren:

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

Dies gilt nicht richtig Parameter verwenden, und daher könnte sein, offen für SQL- Injektion und andere Probleme.

Eine weitere Option ist die regelmäßige String-Interpolation zu verwenden und mkString die Aussage zu bauen:

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

Dies ist im Wesentlichen der gleiche Ansatz wie #$ verwenden, aber möglicherweise flexibler im allgemeinen Fall.

Wenn die Sicherheitsanfälligkeit durch SQL-Injection von großer Bedeutung ist (z. B. wenn die Elemente von ids vom Benutzer bereitgestellt werden), können Sie eine Abfrage mit einem Parameter für jedes Element von ids erstellen. Dann müssen Sie eine benutzerdefinierte SetParameter Instanz schaffen, so dass glatte die List in Parameter drehen kann:

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) 

Da Ihr idsInts sind, ist dies wahrscheinlich weniger ein Problem, aber wenn Sie diese Methode bevorzugen, Sie müssten nur die setStringListParameter ändern Int statt String zu verwenden:

+3

Wenn 'ids' Typ 'List [Int]' 'Ich kann nicht sehen, wie SQL-Injektion möglich ist, auch wenn sie Benutzer zur Verfügung gestellt werden. – Daenyth

+1

@Daenyth Es ist definitiv weniger Besorgnis (obwohl manchmal ganzzahlige SQL-Injection ein Problem sein kann, durch eine Division durch Null oder andere Ausnahmen verursachen, und dann den gescheiterten Zustand ausnutzen - Google "sql injection Ganzzahlen"). Aber ich würde sagen, dass es am besten ist, Parameter zu verwenden, um Probleme auf der Straße zu vermeiden (z. B. wenn ein anderer Entwickler den Typ in "String" geändert hat, um die neuen Typen von IDs aufzunehmen, die einige Zeichen enthalten). Ich habe wirklich nur über den Fall gesprochen, als es hier eine "Schnur" ist. –

+0

Danke für die Antwort Ben! Sehr informativ über die Lösungen mit möglichen Schwachstellen. Ich stimme jedoch @ Daenyth zu, dass Sie nicht mit einem expliziten Ganzzahl-Typ injizieren können. –

3
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 richtig ist. Dies ist ein weiterer Beispielcode, Test auf Slick 3.1.0.

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

1

Obwohl dies nicht allgemeingültige Antwort und kann nicht sein, was der Autor wollte, ich will noch dies darauf hinweisen, wer auch immer diese Frage sieht.

Einige DB-Backends unterstützen Array-Typen, und es gibt Erweiterungen zu Slick, mit denen diese Array-Typen in den Interpolationen festgelegt werden können.

Zum Beispiel hat Postgres die Syntax where column = any(array) und mit slick-pg können Sie diese Syntax wie so verwenden:

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

Dies eine viel saubere Syntax bringt, die den Aussage Compiler-Cache ist freundlicher und auch sicher vor SQL-Injektionen und die allgemeine Gefahr der Erstellung eines fehlerhaften SQL mit der Interpolationssyntax #$var.