From 5d8006478bae052ba4db013b653992bb09ae50a7 Mon Sep 17 00:00:00 2001
From: "David J. Malan" <malan@harvard.edu>
Date: Sat, 27 May 2017 10:45:22 -0400
Subject: [PATCH 1/5] disabled logging, removed temporary prints

---
 tests/sqltests.py | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/tests/sqltests.py b/tests/sqltests.py
index 8e518b4..5d3e336 100644
--- a/tests/sqltests.py
+++ b/tests/sqltests.py
@@ -1,8 +1,10 @@
-from cs50.sql import SQL
+import logging
 import sys
 import unittest
 import warnings
 
+from cs50.sql import SQL
+
 class SQLTests(unittest.TestCase):
     def multi_inserts_enabled(self):
         return True
@@ -15,11 +17,6 @@ def test_delete_returns_affected_rows(self):
         ]
         for row in rows:
             self.db.execute("INSERT INTO cs50(val) VALUES(:val);", val=row["val"])
-
-        print(self.db.execute("DELETE FROM cs50 WHERE id = :id", id=rows[0]["id"]))
-        print(self.db.execute("SELECT * FROM cs50"))
-        return
-
         self.assertEqual(self.db.execute("DELETE FROM cs50 WHERE id = :id", id=rows[0]["id"]), 1)
         self.assertEqual(self.db.execute("DELETE FROM cs50 WHERE id = :a or id = :b", a=rows[1]["id"], b=rows[2]["id"]), 2)
         self.assertEqual(self.db.execute("DELETE FROM cs50 WHERE id = -50"), 0)
@@ -122,5 +119,5 @@ def multi_inserts_enabled(self):
         unittest.TestLoader().loadTestsFromTestCase(MySQLTests),
         unittest.TestLoader().loadTestsFromTestCase(PostgresTests)
     ])
-
+    logging.getLogger("cs50.sql").disabled = True
     sys.exit(not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful())

From 3faee867b56785c61905e5e634b1ae6cc56af039 Mon Sep 17 00:00:00 2001
From: Kareem Zidane <kzidane@cs50.harvard.edu>
Date: Sat, 27 May 2017 22:46:20 +0200
Subject: [PATCH 2/5] deploying once after tests succeed

---
 .travis.yml | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index a20df42..2a946b4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,18 +15,23 @@ before_script:
 - mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
 - psql -c 'create database test;' -U postgres
 script: python tests/sqltests.py
-deploy:
-- provider: script
-  script: 'curl --fail --data "{ \"tag_name\": \"v$(python setup.py --version)\",
-    \"target_commitish\": \"$TRAVIS_COMMIT\", \"name\": \"v$(python setup.py --version)\"
-    }" --user bot50:$GITHUB_TOKEN https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases'
-  on:
-    branch: master
-- provider: pypi
-  user: "$PYPI_USERNAME"
-  password: "$PYPI_PASSWORD"
-  on:
-    branch: master
+jobs:
+  include:
+  - stage: deploy
+    python: '3.4'
+    script: skip
+    deploy:
+    - provider: script
+      script: 'curl --fail --data "{ \"tag_name\": \"v$(python setup.py --version)\",
+        \"target_commitish\": \"$TRAVIS_COMMIT\", \"name\": \"v$(python setup.py --version)\"
+      }" --user bot50:$GITHUB_TOKEN https://api.github.com/repos/$TRAVIS_REPO_SLUG/releases'
+      on:
+        branch: master
+    - provider: pypi
+      user: "$PYPI_USERNAME"
+      password: "$PYPI_PASSWORD"
+      on:
+        branch: master
 notifications:
   slack:
     secure: lJklhcBVjDT6KzUNa3RFHXdXSeH7ytuuGrkZ5ZcR72CXMoTf2pMJTzPwRLWOp6lCSdDC9Y8MWLrcg/e33dJga4Jlp9alOmWqeqesaFjfee4st8vAsgNbv8/RajPH1gD2bnkt8oIwUzdHItdb5AucKFYjbH2g0d8ndoqYqUeBLrnsT1AP5G/Vi9OHC9OWNpR0FKaZIJE0Wt52vkPMH3sV2mFeIskByPB+56U5y547mualKxn61IVR/dhYBEtZQJuSvnwKHPOn9Pkk7cCa+SSSeTJ4w5LboY8T17otaYNauXo46i1bKIoGiBcCcrJyQHHiPQmcq/YU540MC5Wzt9YXUycmJzRi347oyQeDee27wV3XJlWMXuuhbtJiKCFny7BTQ160VATlj/dbwIzN99Ra6/BtTumv/6LyTdKIuVjdAkcN8dtdDW1nlrQ29zuPNCcXXzJ7zX7kQaOCUV1c2OrsbiH/0fE9nknUORn97txqhlYVi0QMS7764wFo6kg0vpmFQRkkQySsJl+TmgcZ01AlsJc2EMMWVuaj9Af9JU4/4yalqDiXIh1fOYYUZnLfOfWS+MsnI+/oLfqJFyMbrsQQTIjs+kTzbiEdhd2R4EZgusU/xRFWokS2NAvahexrRhRQ6tpAI+LezPrkNOR3aHiykBf+P9BkUa0wPp6V2Ayc6q0=

From fdfbd4bcfb271bb5ac1caf7a168860625d050aec Mon Sep 17 00:00:00 2001
From: Kareem Zidane <kzidane@cs50.harvard.edu>
Date: Sat, 27 May 2017 22:58:26 +0200
Subject: [PATCH 3/5] skipped install and before_script in deploy stage

---
 .travis.yml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/.travis.yml b/.travis.yml
index 2a946b4..b8504fa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,6 +19,8 @@ jobs:
   include:
   - stage: deploy
     python: '3.4'
+    install: skip
+    before_script: skip
     script: skip
     deploy:
     - provider: script

From 24634ad9b4c678042b2424ce9f951689825d7e32 Mon Sep 17 00:00:00 2001
From: "David J. Malan" <malan@harvard.edu>
Date: Thu, 6 Jul 2017 09:11:28 -0400
Subject: [PATCH 4/5] fixed get_backend_name bug

---
 src/cs50/sql.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/cs50/sql.py b/src/cs50/sql.py
index bfa79f0..5d8e345 100644
--- a/src/cs50/sql.py
+++ b/src/cs50/sql.py
@@ -123,7 +123,7 @@ def process(value):
 
             # if INSERT, return primary key value for a newly inserted row
             elif re.search(r"^\s*INSERT\s+", statement, re.I):
-                if self.engine.url.get_backend_name() == "postgresql":
+                if self.engine.url.get_backend_name() in ["postgres", "postgresql"]:
                     result = self.engine.execute(sqlalchemy.text("SELECT LASTVAL()"))
                     return result.first()[0]
                 else:

From 893cb90efe3509cfcf9ad0ab83463c63c89432b9 Mon Sep 17 00:00:00 2001
From: "David J. Malan" <malan@harvard.edu>
Date: Mon, 3 Jul 2017 18:28:54 +0000
Subject: [PATCH 5/5] removed support for multiple statements so all behavior
 is well-defined

---
 setup.py          | 4 ++--
 src/cs50/sql.py   | 5 +++++
 tests/sqltests.py | 7 +++----
 3 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/setup.py b/setup.py
index 8440a86..8e6ee91 100644
--- a/setup.py
+++ b/setup.py
@@ -10,11 +10,11 @@
         "Topic :: Software Development :: Libraries :: Python Modules"
     ],
     description="CS50 library for Python",
-    install_requires=["SQLAlchemy"],
+    install_requires=["SQLAlchemy", "sqlparse"],
     keywords="cs50",
     name="cs50",
     package_dir={"": "src"},
     packages=["cs50"],
     url="https://github.com/cs50/python-cs50",
-    version="2.0.0"
+    version="2.1.0"
 )
diff --git a/src/cs50/sql.py b/src/cs50/sql.py
index 5d8e345..4cfbd78 100644
--- a/src/cs50/sql.py
+++ b/src/cs50/sql.py
@@ -3,6 +3,7 @@
 import logging
 import re
 import sqlalchemy
+import sqlparse
 import sys
 import warnings
 
@@ -86,6 +87,10 @@ def process(value):
                 else:
                     return process(value)
 
+        # allow only one statement at a time
+        if len(sqlparse.split(text)) > 1:
+            raise RuntimeError("too many statements at once")
+
         # raise exceptions for warnings
         warnings.filterwarnings("error")
 
diff --git a/tests/sqltests.py b/tests/sqltests.py
index 5d3e336..c1f3c0e 100644
--- a/tests/sqltests.py
+++ b/tests/sqltests.py
@@ -6,8 +6,9 @@
 from cs50.sql import SQL
 
 class SQLTests(unittest.TestCase):
-    def multi_inserts_enabled(self):
-        return True
+
+    def test_multiple_statements(self):
+        self.assertRaises(RuntimeError, self.db.execute, "INSERT INTO cs50(val) VALUES('baz'); INSERT INTO cs50(val) VALUES('qux')")
 
     def test_delete_returns_affected_rows(self):
         rows = [
@@ -24,8 +25,6 @@ def test_delete_returns_affected_rows(self):
     def test_insert_returns_last_row_id(self):
         self.assertEqual(self.db.execute("INSERT INTO cs50(val) VALUES('foo')"), 1)
         self.assertEqual(self.db.execute("INSERT INTO cs50(val) VALUES('bar')"), 2)
-        if self.multi_inserts_enabled():
-            self.assertEqual(self.db.execute("INSERT INTO cs50(val) VALUES('baz'); INSERT INTO cs50(val) VALUES('qux')"), 4)
 
     def test_select_all(self):
         self.assertEqual(self.db.execute("SELECT * FROM cs50"), [])