From 82fcf1460ac80853a4b137a8209b5747ee114fc8 Mon Sep 17 00:00:00 2001 From: Lin Zhiwen Date: Wed, 11 Sep 2024 23:57:17 +0800 Subject: [PATCH] fix: fix update value containing "where" cause exception --- CHANGELOG.md | 9 +++++++ clickhouse_backend/backend/introspection.py | 4 +-- clickhouse_backend/driver/connection.py | 27 ++++++++++++++------- tests/queries/tests.py | 7 ++++++ 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fa743f..bb51381 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +### unreleased + +- fix: #99 fix update value containing "where" cause exception. + +### 1.3.0 + +- fix: #93 last_executed_query() when params is a mapping of values. +- feature: #95 upgrade dependencies. + ### 1.2.0 - feat: #72 support window functions. diff --git a/clickhouse_backend/backend/introspection.py b/clickhouse_backend/backend/introspection.py index 2f8d80c..d38d3fb 100644 --- a/clickhouse_backend/backend/introspection.py +++ b/clickhouse_backend/backend/introspection.py @@ -12,10 +12,10 @@ TableInfo = namedtuple("TableInfo", BaseTableInfo._fields + ("comment",)) constraint_pattern = re.compile( - r"CONSTRAINT (`)?((?(1)(?:[^\\`]|\\.)+|\S+))(?(1)`|) (CHECK .+?),?\n" + r"CONSTRAINT (`)?((?(1)(?:\\.|[^`])+|\S+))(?(1)`|) (CHECK .+?),?\n" ) index_pattern = re.compile( - r"INDEX (`)?((?(1)(?:[^\\`]|\\.)+|\S+))(?(1)`|) (.+? TYPE ([a-zA-Z_][0-9a-zA-Z_]*)\(.+?\) GRANULARITY \d+)" + r"INDEX (`)?((?(1)(?:\\.|[^`])+|\S+))(?(1)`|) (.+? TYPE ([a-zA-Z_][0-9a-zA-Z_]*)\(.+?\) GRANULARITY \d+)" ) diff --git a/clickhouse_backend/driver/connection.py b/clickhouse_backend/driver/connection.py index a825b4f..b7c3ee5 100644 --- a/clickhouse_backend/driver/connection.py +++ b/clickhouse_backend/driver/connection.py @@ -10,10 +10,10 @@ from .escape import escape_params from .pool import ClickhousePool -update_pattern = re.compile( - r"^\s*alter\s+table\s+(\S+)\s+.*?update.+?where\s+(.+?)(?:settings\s+.+)?$", - flags=re.IGNORECASE, -) +name_regex = r'"(?:[^"]|\\.)+"' +value_regex = r"(')?(?(1)(?:[^']|\\.)+|\S+)(?(1)'|)" +name_value_regex = f"{name_regex} = {value_regex}" +update_pattern = re.compile(f"^ALTER TABLE ({name_regex}) UPDATE ") def send_query(self, query, query_id=None, params=None): @@ -100,11 +100,20 @@ def execute(self, operation, parameters=None): query = self._client.substitute_params( operation, parameters, self._client.connection.context ) - table, where = update_pattern.match(query).groups() - super().execute(f"select count(*) from {table} where {where}") - (rowcount,) = self.fetchone() - self._reset_state() - self._rowcount = rowcount + m = update_pattern.match(query) + table = m.group(1) + query_upper = query.upper() + i = query_upper.rfind(" WHERE ") + if i > 0: + j = query_upper.rfind(" SETTINGS ", i + 7) + if j > 0: + where = query[i + 7 : j] + else: + where = query[i + 7 :] + super().execute(f"select count(*) from {table} where {where}") + (rowcount,) = self.fetchone() + self._reset_state() + self._rowcount = rowcount super().execute(operation, parameters) diff --git a/tests/queries/tests.py b/tests/queries/tests.py index de69cf7..aaed34b 100644 --- a/tests/queries/tests.py +++ b/tests/queries/tests.py @@ -20,3 +20,10 @@ def test_update(self): self.a1.save() with self.assertRaises(models.Author.MultipleObjectsReturned): self.a1.refresh_from_db() + + # regression test for https://github.com/jayvynl/django-clickhouse-backend/issues/99 + def test_update_special_string_val(self): + self.a1.name = "where **" + self.a1.save() + self.a1.refresh_from_db() + self.assertEqual(self.a1.name, "where **")