From fc6647adbf1bb67cbae8e29e01095d550ed04d2e Mon Sep 17 00:00:00 2001 From: "David J. Malan" <malan@harvard.edu> Date: Sun, 17 Dec 2023 19:16:30 -0500 Subject: [PATCH 01/13] updated style --- setup.py | 2 +- src/cs50/sql.py | 13 ++++++++++++- tests/sql.py | 6 ++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 8f4b1be..7976109 100644 --- a/setup.py +++ b/setup.py @@ -18,5 +18,5 @@ package_dir={"": "src"}, packages=["cs50"], url="https://github.com/cs50/python-cs50", - version="9.3.2" + version="9.3.3" ) diff --git a/src/cs50/sql.py b/src/cs50/sql.py index 3be30fe..356ed62 100644 --- a/src/cs50/sql.py +++ b/src/cs50/sql.py @@ -71,9 +71,13 @@ def __init__(self, url, **kwargs): # without isolation_level, PostgreSQL warns with "there is already a transaction in progress" for our own BEGIN and # "there is no transaction in progress" for our own COMMIT self._engine = sqlalchemy.create_engine(url, **kwargs).execution_options( - autocommit=False, isolation_level="AUTOCOMMIT" + autocommit=False, isolation_level="AUTOCOMMIT", no_parameters=True ) + # Avoid doubly escaping percent signs, since no_parameters=True anyway + # https://github.com/cs50/python-cs50/issues/171 + self._engine.dialect.identifier_preparer._double_percents = False + # Get logger self._logger = logging.getLogger("cs50") @@ -559,12 +563,19 @@ def __escape(value): # str elif isinstance(value, str): +<<<<<<< HEAD return sqlparse.sql.Token( sqlparse.tokens.String, sqlalchemy.types.String().literal_processor(self._engine.dialect)( value ), ) +======= + literal = sqlalchemy.types.String().literal_processor(self._engine.dialect)(value) + #if self._engine.dialect.identifier_preparer._double_percents: + # literal = literal.replace("%%", "%") + return sqlparse.sql.Token(sqlparse.tokens.String, literal) +>>>>>>> 3863555 (fixes #171) # None elif value is None: diff --git a/tests/sql.py b/tests/sql.py index 968f98b..b5d5406 100644 --- a/tests/sql.py +++ b/tests/sql.py @@ -138,6 +138,12 @@ def test_lastrowid(self): self.assertEqual(self.db.execute("INSERT INTO foo (firstname, lastname) VALUES('firstname', 'lastname')"), 1) self.assertRaises(ValueError, self.db.execute, "INSERT INTO foo (id, firstname, lastname) VALUES(1, 'firstname', 'lastname')") + def test_url(self): + url = "https://www.amazon.es/Desesperaci%C3%B3n-BEST-SELLER-Stephen-King/dp/8497595890" + self.db.execute("CREATE TABLE foo(id SERIAL PRIMARY KEY, url TEXT)") + self.db.execute("INSERT INTO foo (url) VALUES(?)", url) + self.assertEqual(self.db.execute("SELECT url FROM foo")[0]["url"], url) + def tearDown(self): self.db.execute("DROP TABLE cs50") self.db.execute("DROP TABLE IF EXISTS foo") From 128f498e8e34f2db10c06633b2f7d2b742ecc2c9 Mon Sep 17 00:00:00 2001 From: "David J. Malan" <malan@harvard.edu> Date: Sun, 17 Dec 2023 19:15:37 -0500 Subject: [PATCH 02/13] fixed YAML --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index f795750..91f5a7d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: - postgres environment: MYSQL_HOST: mysql - POSTGRESQL_HOST: postgresql + POSTGRESQL_HOST: postgres links: - mysql - postgres From 3b9036933da29b0265f7ddae0d709ed2784cfb1d Mon Sep 17 00:00:00 2001 From: "David J. Malan" <malan@harvard.edu> Date: Sun, 17 Dec 2023 19:18:14 -0500 Subject: [PATCH 03/13] fixed merge --- src/cs50/sql.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/cs50/sql.py b/src/cs50/sql.py index 356ed62..cbbd3d2 100644 --- a/src/cs50/sql.py +++ b/src/cs50/sql.py @@ -563,19 +563,12 @@ def __escape(value): # str elif isinstance(value, str): -<<<<<<< HEAD return sqlparse.sql.Token( sqlparse.tokens.String, sqlalchemy.types.String().literal_processor(self._engine.dialect)( value ), ) -======= - literal = sqlalchemy.types.String().literal_processor(self._engine.dialect)(value) - #if self._engine.dialect.identifier_preparer._double_percents: - # literal = literal.replace("%%", "%") - return sqlparse.sql.Token(sqlparse.tokens.String, literal) ->>>>>>> 3863555 (fixes #171) # None elif value is None: From e47cc3563cb015159ab897dd8053af2f887f7907 Mon Sep 17 00:00:00 2001 From: "David J. Malan" <malan@harvard.edu> Date: Sun, 17 Dec 2023 19:39:15 -0500 Subject: [PATCH 04/13] updated for MySQL tests --- docker-compose.yml | 2 +- tests/sql.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 91f5a7d..8608080 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,7 +20,7 @@ services: MYSQL_ALLOW_EMPTY_PASSWORD: yes healthcheck: test: ["CMD", "mysqladmin", "-uroot", "ping"] - image: cs50/mysql:8 + image: cs50/mysql ports: - 3306:3306 postgres: diff --git a/tests/sql.py b/tests/sql.py index b5d5406..bb37fd9 100644 --- a/tests/sql.py +++ b/tests/sql.py @@ -336,7 +336,7 @@ def test_cte(self): if __name__ == "__main__": suite = unittest.TestSuite([ unittest.TestLoader().loadTestsFromTestCase(SQLiteTests), - #unittest.TestLoader().loadTestsFromTestCase(MySQLTests), + unittest.TestLoader().loadTestsFromTestCase(MySQLTests), unittest.TestLoader().loadTestsFromTestCase(PostgresTests) ]) From dc6a43f5f93cd22457873ee858a60cbac46237f6 Mon Sep 17 00:00:00 2001 From: "David J. Malan" <malan@harvard.edu> Date: Sun, 17 Dec 2023 23:04:50 -0500 Subject: [PATCH 05/13] updated SQLAlchemy versioning --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 7976109..6fd6cfa 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ "Topic :: Software Development :: Libraries :: Python Modules" ], description="CS50 library for Python", - install_requires=["Flask>=1.0", "packaging", "SQLAlchemy<3", "sqlparse", "termcolor", "wheel"], + install_requires=["Flask>=1.0", "packaging", "SQLAlchemy>=2,<3", "sqlparse", "termcolor", "wheel"], keywords="cs50", license="GPLv3", long_description_content_type="text/markdown", @@ -18,5 +18,5 @@ package_dir={"": "src"}, packages=["cs50"], url="https://github.com/cs50/python-cs50", - version="9.3.3" + version="9.3.4" ) From 688af684d61a01e3bec5008acf2293a6d505196f Mon Sep 17 00:00:00 2001 From: Rongxin Liu <rongxinliu.dev@gmail.com> Date: Tue, 5 Mar 2024 00:23:11 +0700 Subject: [PATCH 06/13] updated workflow actions --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b8165f7..0f14704 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,8 +20,8 @@ jobs: ports: - 5432:5432 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: '3.7' check-latest: true From d67a26b3891a8d093aaa3a53e0f9a7bfeb0a599e Mon Sep 17 00:00:00 2001 From: "yulai.linda@gmail.com" <rongxinliu.dev@gmail.com> Date: Thu, 2 May 2024 12:22:33 -0400 Subject: [PATCH 07/13] updated actions/github-script to version v7 --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0f14704..0b0ee1a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,7 +56,7 @@ jobs: - name: Create Release if: ${{ github.ref == 'refs/heads/main' }} - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: github-token: ${{ github.token }} script: | From 2d5fd94132accb2733833eb273d755803a99794c Mon Sep 17 00:00:00 2001 From: "David J. Malan" <malan@harvard.edu> Date: Mon, 14 Oct 2024 20:39:22 -0400 Subject: [PATCH 08/13] adds support for VACUUM --- setup.py | 2 +- src/cs50/sql.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 6fd6cfa..1817b95 100644 --- a/setup.py +++ b/setup.py @@ -18,5 +18,5 @@ package_dir={"": "src"}, packages=["cs50"], url="https://github.com/cs50/python-cs50", - version="9.3.4" + version="9.4.0" ) diff --git a/src/cs50/sql.py b/src/cs50/sql.py index cbbd3d2..a133293 100644 --- a/src/cs50/sql.py +++ b/src/cs50/sql.py @@ -177,6 +177,7 @@ def execute(self, sql, *args, **kwargs): "SELECT", "START", "UPDATE", + "VACUUM", } # Check if the full_statement starts with any command @@ -378,7 +379,7 @@ def teardown_appcontext(exception): ) # Check for start of transaction - if command in ["BEGIN", "START"]: + if command in ["BEGIN", "START", "VACUUM"]: # cannot VACUUM from within a transaction self._autocommit = False # Execute statement @@ -389,7 +390,7 @@ def teardown_appcontext(exception): connection.execute(sqlalchemy.text("COMMIT")) # Check for end of transaction - if command in ["COMMIT", "ROLLBACK"]: + if command in ["COMMIT", "ROLLBACK", "VACUUM"]: # cannot VACUUM from within a transaction self._autocommit = True # Return value From f81a0a3920d90296537843d45683ec0b5d70171b Mon Sep 17 00:00:00 2001 From: "David J. Malan" <malan@harvard.edu> Date: Mon, 14 Oct 2024 20:40:31 -0400 Subject: [PATCH 09/13] fixes raw strings --- src/cs50/sql.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cs50/sql.py b/src/cs50/sql.py index a133293..81905bc 100644 --- a/src/cs50/sql.py +++ b/src/cs50/sql.py @@ -329,12 +329,12 @@ def execute(self, sql, *args, **kwargs): sqlparse.tokens.Literal.String, sqlparse.tokens.Literal.String.Single, ]: - token.value = re.sub("(^'|\s+):", r"\1\:", token.value) + token.value = re.sub(r"(^'|\s+):", r"\1\:", token.value) # In identifier # https://www.sqlite.org/lang_keywords.html elif token.ttype == sqlparse.tokens.Literal.String.Symbol: - token.value = re.sub('(^"|\s+):', r"\1\:", token.value) + token.value = re.sub(r'(^"|\s+):', r"\1\:", token.value) # Join tokens into statement statement = "".join([str(token) for token in tokens]) From b629b6dcdf3041982117801a35c01be2b72cb3d3 Mon Sep 17 00:00:00 2001 From: "David J. Malan" <malan@harvard.edu> Date: Sat, 5 Apr 2025 11:36:15 -0400 Subject: [PATCH 10/13] Update README.md --- README.md | 47 ----------------------------------------------- 1 file changed, 47 deletions(-) diff --git a/README.md b/README.md index c94bd1f..a9033c6 100644 --- a/README.md +++ b/README.md @@ -39,50 +39,3 @@ s = cs50.get_string(); ``` python tests/sql.py ``` - -### Sample Tests - -``` -import cs50 -db = cs50.SQL("sqlite:///foo.db") -db.execute("CREATE TABLE IF NOT EXISTS cs50 (id INTEGER PRIMARY KEY, val TEXT, bin BLOB)") -db.execute("INSERT INTO cs50 (val) VALUES('a')") -db.execute("INSERT INTO cs50 (val) VALUES('b')") -db.execute("BEGIN") -db.execute("INSERT INTO cs50 (val) VALUES('c')") -db.execute("INSERT INTO cs50 (val) VALUES('x')") -db.execute("INSERT INTO cs50 (val) VALUES('y')") -db.execute("ROLLBACK") -db.execute("INSERT INTO cs50 (val) VALUES('z')") -db.execute("COMMIT") - ---- - -import cs50 -db = cs50.SQL("mysql://root@localhost/test") -db.execute("CREATE TABLE IF NOT EXISTS cs50 (id INTEGER PRIMARY KEY, val TEXT, bin BLOB)") -db.execute("INSERT INTO cs50 (val) VALUES('a')") -db.execute("INSERT INTO cs50 (val) VALUES('b')") -db.execute("BEGIN") -db.execute("INSERT INTO cs50 (val) VALUES('c')") -db.execute("INSERT INTO cs50 (val) VALUES('x')") -db.execute("INSERT INTO cs50 (val) VALUES('y')") -db.execute("ROLLBACK") -db.execute("INSERT INTO cs50 (val) VALUES('z')") -db.execute("COMMIT") - ---- - -import cs50 -db = cs50.SQL("postgresql://postgres@localhost/test") -db.execute("CREATE TABLE IF NOT EXISTS cs50 (id SERIAL PRIMARY KEY, val VARCHAR(16), bin BYTEA)") -db.execute("INSERT INTO cs50 (val) VALUES('a')") -db.execute("INSERT INTO cs50 (val) VALUES('b')") -db.execute("BEGIN") -db.execute("INSERT INTO cs50 (val) VALUES('c')") -db.execute("INSERT INTO cs50 (val) VALUES('x')") -db.execute("INSERT INTO cs50 (val) VALUES('y')") -db.execute("ROLLBACK") -db.execute("INSERT INTO cs50 (val) VALUES('z')") -db.execute("COMMIT") -``` From 68f4380757d8adb92d027c42c2099e798bff9488 Mon Sep 17 00:00:00 2001 From: "David J. Malan" <malan@harvard.edu> Date: Sat, 5 Apr 2025 11:38:49 -0400 Subject: [PATCH 11/13] updated Python for Action --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0b0ee1a..7fcb507 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.7' + python-version: '3.12' check-latest: true - name: Setup databases run: | From b37a00f2f9b16fea9750b3ee6ca8f9d3ed5c1aa1 Mon Sep 17 00:00:00 2001 From: "David J. Malan" <malan@harvard.edu> Date: Sat, 12 Apr 2025 13:32:57 -0400 Subject: [PATCH 12/13] added pkg-config --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ccc4552..dddd430 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM cs50/cli -RUN sudo apt update && sudo apt install --yes libmysqlclient-dev pgloader postgresql +RUN sudo apt update && sudo apt install --yes libmysqlclient-dev pgloader pkg-config postgresql RUN sudo pip3 install mysqlclient psycopg2-binary WORKDIR /mnt From 99cc280cf83eaea5c08319492ad4d9d1ebc0bd15 Mon Sep 17 00:00:00 2001 From: "David J. Malan" <malan@harvard.edu> Date: Sat, 12 Apr 2025 13:33:53 -0400 Subject: [PATCH 13/13] removed version --- docker-compose.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 8608080..bcca424 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -31,4 +31,3 @@ services: POSTGRES_DB: test ports: - 5432:5432 -version: "3.6"