Repository: keredson/peewee

Issue:
Using << with an empty list creates an invalid SQL query
from @Prillan in https://github.com/coleifer/peewee/issues/860:

```
psycopg2.ProgrammingError: syntax error at or near ")"
```

I think either of the following solutions are acceptable:

- Throw an error in peewee indicating that the list (iterator) is empty.
- Transform column << [] into False before compiling to SQL.

Not handling this before sending it to the database causes all queries after it to fail (at least on our flask server) with error:

```
peewee.InternalError: current transaction is aborted, commands ignored until end of transaction block
```


Discussion:

Patch:
diff --git a/README.rst b/README.rst
index 719c92c..e0501ae 100644
--- a/README.rst
+++ b/README.rst
@@ -399,6 +399,28 @@ This removes the need for the extra variable in the global namespace and the coo
 Our patch for this has been incorporated upstream, so this is forwards-compatible too, following Peewee's next release.
 
 
+Passing an Empty List/Set/Tuple into IN Doesn't Gen Invalid SQL
+---------------------------------------------------------------
+
+If you try to do a IN operation on an empty list:
+
+.. code-block:: python
+
+  User.select().where(User.id << [])
+
+Peewee will generate the following SQL:
+
+.. code-block:: sql
+
+  SELECT "t1"."id", "t1"."username" FROM "user" AS t1 WHERE ("t1"."id" IN ())
+
+Which the database will reject as invalid, throwing an exception.  We instead generate a "false" statement:
+
+.. code-block:: sql
+
+  SELECT u1.id, u1.username FROM "user" AS u1 WHERE (0 = 1)
+
+So you don't have to manually test for empty lists every time you use a SQL IN.
 
 
 
diff --git a/peewee.py b/peewee.py
index d609c21..de9fb2e 100644
--- a/peewee.py
+++ b/peewee.py
@@ -1598,6 +1598,8 @@ class QueryCompiler(object):
         lhs, lparams = self.parse_node(node.lhs, alias_map, conv)
         rhs, rparams = self.parse_node(node.rhs, alias_map, conv)
         template = '%s %s %s' if node.flat else '(%s %s %s)'
+        if node.op == OP.IN and node.rhs.__class__ in (list,set,tuple) and len(node.rhs)==0:
+          return template % ('0', self.get_op(OP.EQ), '1'), lparams
         sql = template % (lhs, self.get_op(node.op), rhs)
         return sql, lparams + rparams
 


Test patch:
diff --git a/playhouse/tests/test_models.py b/playhouse/tests/test_models.py
index e23e99d..2d9a691 100644
--- a/playhouse/tests/test_models.py
+++ b/playhouse/tests/test_models.py
@@ -131,6 +131,11 @@ class TestQueryingModels(ModelTestCase):
         users = User.select(fn.Count(fn.Distinct(User.username))).scalar()
         self.assertEqual(users, 6)
 
+    def test_empty_in(self):
+        for empty_collection in [list(), set(), tuple()]:
+            users = User.select().where(User.username << empty_collection)
+            self.assertEqual(users.count(), 0)
+
     def test_update(self):
         User.create_users(5)
         uq = User.update(username='u-edited').where(User.username << ['u1', 'u2', 'u3'])