summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/build/changelog/unreleased_12/4140.rst7
-rw-r--r--lib/sqlalchemy/dialects/oracle/cx_oracle.py5
-rw-r--r--lib/sqlalchemy/engine/default.py2
-rw-r--r--test/dialect/oracle/test_compiler.py10
-rw-r--r--test/requirements.py7
-rw-r--r--test/sql/test_query.py60
6 files changed, 90 insertions, 1 deletions
diff --git a/doc/build/changelog/unreleased_12/4140.rst b/doc/build/changelog/unreleased_12/4140.rst
new file mode 100644
index 000000000..2b29b2523
--- /dev/null
+++ b/doc/build/changelog/unreleased_12/4140.rst
@@ -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
diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
index 56a0425c8..68ecce519 100644
--- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py
+++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
@@ -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)
diff --git a/lib/sqlalchemy/engine/default.py b/lib/sqlalchemy/engine/default.py
index 4b9aa9493..36344fc38 100644
--- a/lib/sqlalchemy/engine/default.py
+++ b/lib/sqlalchemy/engine/default.py
@@ -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
)
diff --git a/test/dialect/oracle/test_compiler.py b/test/dialect/oracle/test_compiler.py
index fc310f8f2..3e1ffebb3 100644
--- a/test/dialect/oracle/test_compiler.py
+++ b/test/dialect/oracle/test_compiler.py
@@ -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',
diff --git a/test/requirements.py b/test/requirements.py
index dac949400..39a78dfa5 100644
--- a/test/requirements.py
+++ b/test/requirements.py
@@ -145,6 +145,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.
diff --git a/test/sql/test_query.py b/test/sql/test_query.py
index afb113748..74efbf9a4 100644
--- a/test/sql/test_query.py
+++ b/test/sql/test_query.py
@@ -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(