]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Fix regexp for expanding IN
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 1 Dec 2017 18:22:23 +0000 (13:22 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 1 Dec 2017 21:32:27 +0000 (16:32 -0500)
Fixed bug in new "expanding bind parameter" feature whereby if multiple
params were used in one statement, the regular expression would not
match the parameter name correctly.

Change-Id: Ifaf7d627aac4ead2a13c8dddccb5c515253d88e6
Fixes: #4140
doc/build/changelog/unreleased_12/4140.rst [new file with mode: 0644]
lib/sqlalchemy/dialects/oracle/cx_oracle.py
lib/sqlalchemy/engine/default.py
test/dialect/oracle/test_compiler.py
test/requirements.py
test/sql/test_query.py

diff --git a/doc/build/changelog/unreleased_12/4140.rst b/doc/build/changelog/unreleased_12/4140.rst
new file mode 100644 (file)
index 0000000..2b29b25
--- /dev/null
@@ -0,0 +1,7 @@
+.. change::
+    :tags: bug, sql
+    :tickets: 4140
+
+    Fixed bug in new "expanding bind parameter" feature whereby if multiple
+    params were used in one statement, the regular expression would not
+    match the parameter name correctly.
\ No newline at end of file
index 56a0425c83b342ca90c4c49cf8f928ec79530ef0..68ecce5194a1207b66431fad59937c15b5ed8600 100644 (file)
@@ -376,6 +376,11 @@ class OracleCompiler_cx_oracle(OracleCompiler):
         quote = getattr(name, 'quote', None)
         if quote is True or quote is not False and \
                 self.preparer._bindparam_requires_quotes(name):
+            if kw.get('expanding', False):
+                raise exc.CompileError(
+                    "Can't use expanding feature with parameter name "
+                    "%r on Oracle; it requires quoting which is not supported "
+                    "in this context." % name)
             quoted_name = '"%s"' % name
             self._quoted_bind_names[name] = quoted_name
             return OracleCompiler.bindparam_string(self, quoted_name, **kw)
index 4b9aa9493e6a47676ea700200668b16f11e5c09f..36344fc38b8c421758aae77c68dc5dbcfe0e7d01 100644 (file)
@@ -779,7 +779,7 @@ class DefaultExecutionContext(interfaces.ExecutionContext):
             return replacement_expressions.pop(m.group(1))
 
         self.statement = re.sub(
-            r"\[EXPANDING_(.+)\]",
+            r"\[EXPANDING_(\S+)\]",
             process_expanding,
             self.statement
         )
index fc310f8f253ef37a90e9f33ee46b9f367d172b65..3e1ffebb3cb9a795cbd655ecb3b907a1067f98dc 100644 (file)
@@ -85,6 +85,16 @@ class CompileTest(fixtures.TestBase, AssertsCompiledSQL):
             t.update().values(plain=5), 'UPDATE s SET "plain"=:"plain"'
         )
 
+    def test_bindparam_quote_raise_on_expanding(self):
+        assert_raises_message(
+            exc.CompileError,
+            "Can't use expanding feature with parameter name 'uid' on "
+            "Oracle; it requires quoting which is not supported in this "
+            "context",
+            bindparam("uid", expanding=True).compile,
+            dialect=cx_oracle.dialect()
+        )
+
     def test_cte(self):
         part = table(
             'part',
index dac9494004646f873c63c461a6bd74c1ddec9bfe..39a78dfa55712521090f022a14619452025e91ab 100644 (file)
@@ -144,6 +144,13 @@ class DefaultRequirements(SuiteRequirements):
         """
         return skip_if(["firebird", "mssql+mxodbc"], "not supported by driver")
 
+    @property
+    def no_quoting_special_bind_names(self):
+        """Target database will quote bound paramter names, doesn't support
+        EXPANDING"""
+
+        return skip_if(["oracle"])
+
     @property
     def identity(self):
         """Target database must support GENERATED AS IDENTITY or a facsimile.
index afb1137488688c8228b72ae4853ba16fd6e92f2b..74efbf9a47ebc97bcb54cfdceacf78a77a0f6246 100644 (file)
@@ -471,6 +471,66 @@ class QueryTest(fixtures.TestBase):
                 ), [{"uname": ['fred']}, {"uname": ['ed']}]
             )
 
+    @testing.requires.no_quoting_special_bind_names
+    def test_expanding_in_special_chars(self):
+        testing.db.execute(
+            users.insert(),
+            [
+                dict(user_id=7, user_name='jack'),
+                dict(user_id=8, user_name='fred'),
+            ]
+        )
+
+        with testing.db.connect() as conn:
+            stmt = select([users]).where(
+                users.c.user_name.in_(bindparam('u35', expanding=True))
+            ).where(
+                users.c.user_id == bindparam("u46")
+            ).order_by(users.c.user_id)
+
+            eq_(
+                conn.execute(
+                    stmt, {"u35": ['jack', 'fred'], "u46": 7}).fetchall(),
+                [(7, 'jack')]
+            )
+
+            stmt = select([users]).where(
+                users.c.user_name.in_(bindparam('u.35', expanding=True))
+            ).where(
+                users.c.user_id == bindparam("u.46")
+            ).order_by(users.c.user_id)
+
+            eq_(
+                conn.execute(
+                    stmt, {"u.35": ['jack', 'fred'], "u.46": 7}).fetchall(),
+                [(7, 'jack')]
+            )
+
+    def test_expanding_in_multiple(self):
+        testing.db.execute(
+            users.insert(),
+            [
+                dict(user_id=7, user_name='jack'),
+                dict(user_id=8, user_name='fred'),
+                dict(user_id=9, user_name='ed')
+            ]
+        )
+
+        with testing.db.connect() as conn:
+            stmt = select([users]).where(
+                users.c.user_name.in_(bindparam('uname', expanding=True))
+            ).where(
+                users.c.user_id.in_(bindparam('userid', expanding=True))
+            ).order_by(users.c.user_id)
+
+            eq_(
+                conn.execute(
+                    stmt,
+                    {"uname": ['jack', 'fred', 'ed'], "userid": [8, 9]}
+                ).fetchall(),
+                [(8, 'fred'), (9, 'ed')]
+            )
+
     @testing.requires.tuple_in
     def test_expanding_in_composite(self):
         testing.db.execute(