From 490a124cbf929ef980ac61b18db6ad5dc0b35c9e Mon Sep 17 00:00:00 2001 From: Alexandre Colucci Date: Tue, 14 May 2019 08:59:41 +0200 Subject: [PATCH 1/8] #530: Wrong number of statement arguments with "like '%?%'" Add "? as a parameter" in the "Good to Know" section to explain how to build like pattern like '%?%'. --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 57efc30f07..eee77fd768 100644 --- a/README.md +++ b/README.md @@ -289,6 +289,7 @@ Documentation #### Good to Know - [Avoiding SQL Injection](#avoiding-sql-injection) +- [? as a parameter](#-as-a-parameter) - [Error Handling](#error-handling) - [Unicode](#unicode) - [Memory Management](#memory-management) @@ -7470,6 +7471,26 @@ try dbQueue.write { db in ``` +## ? as a parameter + +SQLite only understands `?` as a parameter when it is a placeholder for a whole value (int, double, string, blob, null). +On the other side, the `?` in `'%?%'` is just a character in the `'%?%'` string value: it is not a query parameter, and is not processed in any way. +You need to take this into consideration for queries like: + +```sql +SELECT * FROM students where name like '%Robert%'; +``` + +To provide the parameter, you can let SQLite build the like pattern using the string concatenation operator `||`: + +```swift +let name = textField.text +try dbQueue.read { db in + try db.execute(sql: "SELECT * FROM students where name like '%' || ? || '%'", arguments: [name]) +} +``` + + ## Error Handling GRDB can throw [DatabaseError](#databaseerror), [PersistenceError](#persistenceerror), or crash your program with a [fatal error](#fatal-errors). From ca9ecd7f66283704eef3beb0605394fd141345a2 Mon Sep 17 00:00:00 2001 From: Alexandre Colucci Date: Mon, 27 May 2019 21:22:55 +0200 Subject: [PATCH 2/8] #530: Wrong number of statement arguments with "like '%?%'" - Rewrite the paragraph as an FAQ - Explicitly mention the error "Wrong number of statement arguments" and give an example causing such issue. - Improve the wording and add a link to the SQLite documentation --- README.md | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index eee77fd768..e909ef492a 100644 --- a/README.md +++ b/README.md @@ -7471,26 +7471,6 @@ try dbQueue.write { db in ``` -## ? as a parameter - -SQLite only understands `?` as a parameter when it is a placeholder for a whole value (int, double, string, blob, null). -On the other side, the `?` in `'%?%'` is just a character in the `'%?%'` string value: it is not a query parameter, and is not processed in any way. -You need to take this into consideration for queries like: - -```sql -SELECT * FROM students where name like '%Robert%'; -``` - -To provide the parameter, you can let SQLite build the like pattern using the string concatenation operator `||`: - -```swift -let name = textField.text -try dbQueue.read { db in - try db.execute(sql: "SELECT * FROM students where name like '%' || ? || '%'", arguments: [name]) -} -``` - - ## Error Handling GRDB can throw [DatabaseError](#databaseerror), [PersistenceError](#persistenceerror), or crash your program with a [fatal error](#fatal-errors). @@ -8527,6 +8507,7 @@ FAQ - [Generic parameter 'T' could not be inferred](#generic-parameter-t-could-not-be-inferred) - [SQLite error 10 "disk I/O error", SQLite error 23 "not authorized"](#sqlite-error-10-disk-io-error-sqlite-error-23-not-authorized) - [What Are Experimental Features?](#what-are-experimental-features) +- [SQLite error 21 "wrong number of statement arguments" with LIKE queries](#sqlite-error-21-wrong-number-of-statement-arguments-with-like-queries) ### How do I create a database in my application? @@ -8666,6 +8647,29 @@ There is an exception, though: *experimental features*, marked with the "**:fire Those experimental features are not protected by semantic versioning, and may break between two minor releases of the library. To help them becoming stable, [your feedback](https://github.com/groue/GRDB.swift/issues) is greatly appreciated. +### SQLite error 21 "wrong number of statement arguments" with LIKE queries + +You may get the error "wrong number of statement arguments" when executing a LIKE query similar to: + +```swift +let name = textField.text +try dbQueue.read { db in + try db.execute(sql: "SELECT * FROM students where title like '%?%'", arguments: [name]) +} +``` + +What is important to understand here is that SQLite only interprets `?` as a parameter when it is a placeholder for a whole value (int, double, string, blob, null). In this incorrect query, `?` is just a character in the `'%?%'` string value: it is not a query parameter, and is not processed in any way. See [https://www.sqlite.org/lang_expr.html#varparam](https://www.sqlite.org/lang_expr.html#varparam) for more information about SQLite parameters. + +To provide the parameter, one option is to let SQLite build the like pattern using the string concatenation operator `||`: + +```swift +let name = textField.text +try dbQueue.read { db in + try db.execute(sql: "SELECT * FROM students where name like '%' || ? || '%'", arguments: [name]) +} +``` + + Sample Code =========== From bba9d42433be3eed8c9dbc2c1aad5bdb78568010 Mon Sep 17 00:00:00 2001 From: Alexandre Colucci Date: Tue, 28 May 2019 22:41:28 +0200 Subject: [PATCH 3/8] Remove the line "[? as a parameter]" in the "Good to Know" section --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index e909ef492a..cdfe8f9f1f 100644 --- a/README.md +++ b/README.md @@ -289,7 +289,6 @@ Documentation #### Good to Know - [Avoiding SQL Injection](#avoiding-sql-injection) -- [? as a parameter](#-as-a-parameter) - [Error Handling](#error-handling) - [Unicode](#unicode) - [Memory Management](#memory-management) From 7ed4b799b7b949c29dcdc8e4361ec7b518a2f88c Mon Sep 17 00:00:00 2001 From: Alexandre Colucci Date: Tue, 28 May 2019 22:51:29 +0200 Subject: [PATCH 4/8] Use an SQLite query with the correct GRDB style --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cdfe8f9f1f..9bd79905ae 100644 --- a/README.md +++ b/README.md @@ -8652,8 +8652,8 @@ You may get the error "wrong number of statement arguments" when executing a LIK ```swift let name = textField.text -try dbQueue.read { db in - try db.execute(sql: "SELECT * FROM students where title like '%?%'", arguments: [name]) +let players = try dbQueue.read { db in + try Player.fetchAll(db, sql: "SELECT * FROM player WHERE name LIKE '%?%'", arguments: [name]) } ``` From 3669fadecbeed8668aa2675db5f2dbe89e6d1631 Mon Sep 17 00:00:00 2001 From: Alexandre Colucci Date: Tue, 28 May 2019 22:53:33 +0200 Subject: [PATCH 5/8] Provide one example that lets Swift build the pattern and an example with the query interface. Then provide the second approach using the || operator. --- README.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9bd79905ae..3dd1418ca9 100644 --- a/README.md +++ b/README.md @@ -8659,12 +8659,32 @@ let players = try dbQueue.read { db in What is important to understand here is that SQLite only interprets `?` as a parameter when it is a placeholder for a whole value (int, double, string, blob, null). In this incorrect query, `?` is just a character in the `'%?%'` string value: it is not a query parameter, and is not processed in any way. See [https://www.sqlite.org/lang_expr.html#varparam](https://www.sqlite.org/lang_expr.html#varparam) for more information about SQLite parameters. -To provide the parameter, one option is to let SQLite build the like pattern using the string concatenation operator `||`: +To provide the parameter, one option is to let Swift build the pattern: ```swift let name = textField.text -try dbQueue.read { db in - try db.execute(sql: "SELECT * FROM students where name like '%' || ? || '%'", arguments: [name]) +let players: [Player] = try dbQueue.read { db in + let pattern = "%\(name)%" + return try Player.fetchAll(db, sql: "SELECT * FROM player WHERE name LIKE ?", arguments: [pattern]) +} +``` + +This solution could also be used with the query interface: + +```swift +let name = textField.text +let players: [Player] = try dbQueue.read { db in + let pattern = "%\(name)%" + return try Player.filter(Column("name").like(pattern)).fetchAll(db) +} +``` + +Another option is to let SQLite build the like pattern using the string concatenation operator `||`: + +```swift +let name = textField.text +let players = try dbQueue.read { db in + try Player.fetchAll(db, sql: "SELECT * FROM player WHERE name LIKE '%' || ? || '%'", arguments: [name]) } ``` From 0232c8e792fd663996bd709e765578cf04bb0bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gwendal=20Roue=CC=81?= Date: Wed, 29 May 2019 19:48:33 +0200 Subject: [PATCH 6/8] Slight rewriting: focus on what's wrong, and provide a single fix because choices are complicated --- README.md | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 3dd1418ca9..d364aa2078 100644 --- a/README.md +++ b/README.md @@ -8657,34 +8657,17 @@ let players = try dbQueue.read { db in } ``` -What is important to understand here is that SQLite only interprets `?` as a parameter when it is a placeholder for a whole value (int, double, string, blob, null). In this incorrect query, `?` is just a character in the `'%?%'` string value: it is not a query parameter, and is not processed in any way. See [https://www.sqlite.org/lang_expr.html#varparam](https://www.sqlite.org/lang_expr.html#varparam) for more information about SQLite parameters. +The problem lies in the `'%?%'` pattern. -To provide the parameter, one option is to let Swift build the pattern: +SQLite only interprets `?` as a parameter when it is a placeholder for a whole value (int, double, string, blob, null). In this incorrect query, `?` is just a character in the `'%?%'` string: it is not a query parameter, and is not processed in any way. See [https://www.sqlite.org/lang_expr.html#varparam](https://www.sqlite.org/lang_expr.html#varparam) for more information about SQLite parameters. -```swift -let name = textField.text -let players: [Player] = try dbQueue.read { db in - let pattern = "%\(name)%" - return try Player.fetchAll(db, sql: "SELECT * FROM player WHERE name LIKE ?", arguments: [pattern]) -} -``` - -This solution could also be used with the query interface: +To fix the error, you can feed the request with the pattern itself, instead of the name: ```swift let name = textField.text let players: [Player] = try dbQueue.read { db in let pattern = "%\(name)%" - return try Player.filter(Column("name").like(pattern)).fetchAll(db) -} -``` - -Another option is to let SQLite build the like pattern using the string concatenation operator `||`: - -```swift -let name = textField.text -let players = try dbQueue.read { db in - try Player.fetchAll(db, sql: "SELECT * FROM player WHERE name LIKE '%' || ? || '%'", arguments: [name]) + return try Player.fetchAll(db, sql: "SELECT * FROM player WHERE name LIKE ?", arguments: [pattern]) } ``` From aefaa86302cdd08d575cbc3eb0169d5c1a36ed26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gwendal=20Roue=CC=81?= Date: Wed, 29 May 2019 19:54:40 +0200 Subject: [PATCH 7/8] CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40f2e9d10d..a3f6c443aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one expection: ## Next Release - [#537](https://github.com/groue/GRDB.swift/pull/537): Remove useless parenthesis from generated SQL +- [#538](https://github.com/groue/GRDB.swift/pull/538) by [Timac](https://github.com/Timac): Add FAQ to clarify "Wrong number of statement arguments" error with "like '%?%'" ## 4.0.1 From 0680e5abd797e6255e21061d7c562cea3e3f86a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gwendal=20Roue=CC=81?= Date: Wed, 29 May 2019 19:55:32 +0200 Subject: [PATCH 8/8] Thank you notice --- CHANGELOG.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3f6c443aa..387b0a8044 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,7 +52,7 @@ GRDB adheres to [Semantic Versioning](https://semver.org/), with one expection: ## Next Release - [#537](https://github.com/groue/GRDB.swift/pull/537): Remove useless parenthesis from generated SQL -- [#538](https://github.com/groue/GRDB.swift/pull/538) by [Timac](https://github.com/Timac): Add FAQ to clarify "Wrong number of statement arguments" error with "like '%?%'" +- [#538](https://github.com/groue/GRDB.swift/pull/538) by [@Timac](https://github.com/Timac): Add FAQ to clarify "Wrong number of statement arguments" error with "like '%?%'" ## 4.0.1 diff --git a/README.md b/README.md index 3a8b4e9d2d..d714bbf3aa 100644 --- a/README.md +++ b/README.md @@ -8717,7 +8717,7 @@ Sample Code **Thanks** - [Pierlis](http://pierlis.com), where we write great software. -- [@alextrob](https://github.com/alextrob), [@bellebethcooper](https://github.com/bellebethcooper), [@bfad](https://github.com/bfad), [@cfilipov](https://github.com/cfilipov), [@charlesmchen-signal](https://github.com/charlesmchen-signal), [@Chiliec](https://github.com/Chiliec), [@darrenclark](https://github.com/darrenclark), [@davidkraus](https://github.com/davidkraus), [@fpillet](http://github.com/fpillet), [@gusrota](https://github.com/gusrota), [@hartbit](https://github.com/hartbit), [@kdubb](https://github.com/kdubb), [@kluufger](https://github.com/kluufger), [@KyleLeneau](https://github.com/KyleLeneau), [@Marus](https://github.com/Marus), [@michaelkirk-signal](https://github.com/michaelkirk-signal), [@pakko972](https://github.com/pakko972), [@peter-ss](https://github.com/peter-ss), [@pierlo](https://github.com/pierlo), [@pocketpixels](https://github.com/pocketpixels), [@schveiguy](https://github.com/schveiguy), [@SD10](https://github.com/SD10), [@sobri909](https://github.com/sobri909), [@sroddy](https://github.com/sroddy), [@swiftlyfalling](https://github.com/swiftlyfalling), [@valexa](https://github.com/valexa), and [@zmeyc](https://github.com/zmeyc) for their contributions, help, and feedback on GRDB. +- [@alextrob](https://github.com/alextrob), [@bellebethcooper](https://github.com/bellebethcooper), [@bfad](https://github.com/bfad), [@cfilipov](https://github.com/cfilipov), [@charlesmchen-signal](https://github.com/charlesmchen-signal), [@Chiliec](https://github.com/Chiliec), [@darrenclark](https://github.com/darrenclark), [@davidkraus](https://github.com/davidkraus), [@fpillet](http://github.com/fpillet), [@gusrota](https://github.com/gusrota), [@hartbit](https://github.com/hartbit), [@kdubb](https://github.com/kdubb), [@kluufger](https://github.com/kluufger), [@KyleLeneau](https://github.com/KyleLeneau), [@Marus](https://github.com/Marus), [@michaelkirk-signal](https://github.com/michaelkirk-signal), [@pakko972](https://github.com/pakko972), [@peter-ss](https://github.com/peter-ss), [@pierlo](https://github.com/pierlo), [@pocketpixels](https://github.com/pocketpixels), [@schveiguy](https://github.com/schveiguy), [@SD10](https://github.com/SD10), [@sobri909](https://github.com/sobri909), [@sroddy](https://github.com/sroddy), [@swiftlyfalling](https://github.com/swiftlyfalling), [@Timac](https://github.com/Timac), [@valexa](https://github.com/valexa), and [@zmeyc](https://github.com/zmeyc) for their contributions, help, and feedback on GRDB. - [@aymerick](https://github.com/aymerick) and [@kali](https://github.com/kali) because SQL. - [ccgus/fmdb](https://github.com/ccgus/fmdb) for its excellency.