summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/dialects/firebird/base.py36
-rw-r--r--lib/sqlalchemy/dialects/mssql/base.py12
-rw-r--r--lib/sqlalchemy/dialects/mssql/provision.py20
-rw-r--r--lib/sqlalchemy/dialects/mssql/pymssql.py2
-rw-r--r--lib/sqlalchemy/dialects/mssql/pyodbc.py4
-rw-r--r--lib/sqlalchemy/dialects/mysql/base.py26
-rw-r--r--lib/sqlalchemy/dialects/mysql/mysqldb.py7
-rw-r--r--lib/sqlalchemy/dialects/mysql/oursql.py2
-rw-r--r--lib/sqlalchemy/dialects/mysql/provision.py14
-rw-r--r--lib/sqlalchemy/dialects/mysql/pyodbc.py4
-rw-r--r--lib/sqlalchemy/dialects/oracle/base.py6
-rw-r--r--lib/sqlalchemy/dialects/oracle/cx_oracle.py4
-rw-r--r--lib/sqlalchemy/dialects/oracle/provision.py18
-rw-r--r--lib/sqlalchemy/dialects/postgresql/base.py30
-rw-r--r--lib/sqlalchemy/dialects/postgresql/provision.py8
-rw-r--r--lib/sqlalchemy/dialects/sqlite/base.py34
-rw-r--r--lib/sqlalchemy/dialects/sqlite/pysqlcipher.py4
-rw-r--r--lib/sqlalchemy/dialects/sqlite/pysqlite.py4
-rw-r--r--lib/sqlalchemy/dialects/sybase/pysybase.py2
-rw-r--r--lib/sqlalchemy/engine/base.py111
-rw-r--r--lib/sqlalchemy/engine/reflection.py2
-rw-r--r--lib/sqlalchemy/engine/result.py2
-rw-r--r--lib/sqlalchemy/orm/events.py6
-rw-r--r--lib/sqlalchemy/orm/util.py5
-rw-r--r--lib/sqlalchemy/sql/base.py44
-rw-r--r--lib/sqlalchemy/sql/events.py5
-rw-r--r--lib/sqlalchemy/testing/suite/test_results.py8
-rw-r--r--lib/sqlalchemy/testing/suite/test_rowcount.py2
-rw-r--r--lib/sqlalchemy/testing/suite/test_select.py6
-rw-r--r--lib/sqlalchemy/testing/suite/test_types.py2
30 files changed, 268 insertions, 162 deletions
diff --git a/lib/sqlalchemy/dialects/firebird/base.py b/lib/sqlalchemy/dialects/firebird/base.py
index 51bda30a2..a43413780 100644
--- a/lib/sqlalchemy/dialects/firebird/base.py
+++ b/lib/sqlalchemy/dialects/firebird/base.py
@@ -41,7 +41,7 @@ hang until other transactions are released. SQLAlchemy does its best
to release transactions as quickly as possible. The most common cause
of hanging transactions is a non-fully consumed result set, i.e.::
- result = engine.execute("select * from table")
+ result = engine.execute(text("select * from table"))
row = result.fetchone()
return
@@ -679,7 +679,9 @@ class FBDialect(default.DefaultDialect):
FROM rdb$relations
WHERE rdb$relation_name=?)
"""
- c = connection.execute(tblqry, [self.denormalize_name(table_name)])
+ c = connection.exec_driver_sql(
+ tblqry, [self.denormalize_name(table_name)]
+ )
return c.first() is not None
def has_sequence(self, connection, sequence_name, schema=None):
@@ -691,7 +693,9 @@ class FBDialect(default.DefaultDialect):
FROM rdb$generators
WHERE rdb$generator_name=?)
"""
- c = connection.execute(genqry, [self.denormalize_name(sequence_name)])
+ c = connection.exec_driver_sql(
+ genqry, [self.denormalize_name(sequence_name)]
+ )
return c.first() is not None
@reflection.cache
@@ -714,7 +718,10 @@ class FBDialect(default.DefaultDialect):
# FROM rdb$relation_fields
# WHERE rdb$system_flag=0 AND rdb$view_context IS NULL
- return [self.normalize_name(row[0]) for row in connection.execute(s)]
+ return [
+ self.normalize_name(row[0])
+ for row in connection.exec_driver_sql(s)
+ ]
@reflection.cache
def get_view_names(self, connection, schema=None, **kw):
@@ -725,7 +732,10 @@ class FBDialect(default.DefaultDialect):
where rdb$view_blr is not null
and (rdb$system_flag is null or rdb$system_flag = 0);
"""
- return [self.normalize_name(row[0]) for row in connection.execute(s)]
+ return [
+ self.normalize_name(row[0])
+ for row in connection.exec_driver_sql(s)
+ ]
@reflection.cache
def get_view_definition(self, connection, view_name, schema=None, **kw):
@@ -734,7 +744,9 @@ class FBDialect(default.DefaultDialect):
FROM rdb$relations
WHERE rdb$relation_name=?
"""
- rp = connection.execute(qry, [self.denormalize_name(view_name)])
+ rp = connection.exec_driver_sql(
+ qry, [self.denormalize_name(view_name)]
+ )
row = rp.first()
if row:
return row["view_source"]
@@ -752,7 +764,7 @@ class FBDialect(default.DefaultDialect):
"""
tablename = self.denormalize_name(table_name)
# get primary key fields
- c = connection.execute(keyqry, ["PRIMARY KEY", tablename])
+ c = connection.exec_driver_sql(keyqry, ["PRIMARY KEY", tablename])
pkfields = [self.normalize_name(r["fname"]) for r in c.fetchall()]
return {"constrained_columns": pkfields, "name": None}
@@ -780,7 +792,7 @@ class FBDialect(default.DefaultDialect):
FROM rdb$dependencies trigdep2
WHERE trigdep2.rdb$dependent_name = trigdep.rdb$dependent_name) = 2
"""
- genr = connection.execute(genqry, [tablename, colname]).first()
+ genr = connection.exec_driver_sql(genqry, [tablename, colname]).first()
if genr is not None:
return dict(name=self.normalize_name(genr["fgenerator"]))
@@ -814,7 +826,7 @@ class FBDialect(default.DefaultDialect):
tablename = self.denormalize_name(table_name)
# get all of the fields for this table
- c = connection.execute(tblqry, [tablename])
+ c = connection.exec_driver_sql(tblqry, [tablename])
cols = []
while True:
row = c.fetchone()
@@ -905,7 +917,7 @@ class FBDialect(default.DefaultDialect):
"""
tablename = self.denormalize_name(table_name)
- c = connection.execute(fkqry, ["FOREIGN KEY", tablename])
+ c = connection.exec_driver_sql(fkqry, ["FOREIGN KEY", tablename])
fks = util.defaultdict(
lambda: {
"name": None,
@@ -944,7 +956,9 @@ class FBDialect(default.DefaultDialect):
AND rdb$relation_constraints.rdb$constraint_type IS NULL
ORDER BY index_name, ic.rdb$field_position
"""
- c = connection.execute(qry, [self.denormalize_name(table_name)])
+ c = connection.exec_driver_sql(
+ qry, [self.denormalize_name(table_name)]
+ )
indexes = util.defaultdict(dict)
for row in c:
diff --git a/lib/sqlalchemy/dialects/mssql/base.py b/lib/sqlalchemy/dialects/mssql/base.py
index 94db296bf..924c0f457 100644
--- a/lib/sqlalchemy/dialects/mssql/base.py
+++ b/lib/sqlalchemy/dialects/mssql/base.py
@@ -2265,9 +2265,9 @@ def _db_plus_owner(fn):
def _switch_db(dbname, connection, fn, *arg, **kw):
if dbname:
- current_db = connection.scalar("select db_name()")
+ current_db = connection.exec_driver_sql("select db_name()").scalar()
if current_db != dbname:
- connection.execute(
+ connection.exec_driver_sql(
"use %s"
% connection.dialect.identifier_preparer.quote_schema(dbname)
)
@@ -2275,7 +2275,7 @@ def _switch_db(dbname, connection, fn, *arg, **kw):
return fn(*arg, **kw)
finally:
if dbname and current_db != dbname:
- connection.execute(
+ connection.exec_driver_sql(
"use %s"
% connection.dialect.identifier_preparer.quote_schema(
current_db
@@ -2387,7 +2387,7 @@ class MSDialect(default.DefaultDialect):
def do_savepoint(self, connection, name):
# give the DBAPI a push
- connection.execute("IF @@TRANCOUNT = 0 BEGIN TRANSACTION")
+ connection.exec_driver_sql("IF @@TRANCOUNT = 0 BEGIN TRANSACTION")
super(MSDialect, self).do_savepoint(connection, name)
def do_release_savepoint(self, connection, name):
@@ -2751,7 +2751,7 @@ class MSDialect(default.DefaultDialect):
for col in cols:
colmap[col["name"]] = col
# We also run an sp_columns to check for identity columns:
- cursor = connection.execute(
+ cursor = connection.exec_driver_sql(
"sp_columns @table_name = '%s', "
"@table_owner = '%s'" % (tablename, owner)
)
@@ -2773,7 +2773,7 @@ class MSDialect(default.DefaultDialect):
if ic is not None and self.server_version_info >= MS_2005_VERSION:
table_fullname = "%s.%s" % (owner, tablename)
- cursor = connection.execute(
+ cursor = connection.exec_driver_sql(
"select ident_seed('%s'), ident_incr('%s')"
% (table_fullname, table_fullname)
)
diff --git a/lib/sqlalchemy/dialects/mssql/provision.py b/lib/sqlalchemy/dialects/mssql/provision.py
index a10043db1..84b9e4194 100644
--- a/lib/sqlalchemy/dialects/mssql/provision.py
+++ b/lib/sqlalchemy/dialects/mssql/provision.py
@@ -15,16 +15,16 @@ def _mssql_update_db_opts(db_url, db_opts):
@create_db.for_db("mssql")
def _mssql_create_db(cfg, eng, ident):
with eng.connect().execution_options(isolation_level="AUTOCOMMIT") as conn:
- conn.execute("create database %s" % ident)
- conn.execute(
+ conn.exec_driver_sql("create database %s" % ident)
+ conn.exec_driver_sql(
"ALTER DATABASE %s SET ALLOW_SNAPSHOT_ISOLATION ON" % ident
)
- conn.execute(
+ conn.exec_driver_sql(
"ALTER DATABASE %s SET READ_COMMITTED_SNAPSHOT ON" % ident
)
- conn.execute("use %s" % ident)
- conn.execute("create schema test_schema")
- conn.execute("create schema test_schema_2")
+ conn.exec_driver_sql("use %s" % ident)
+ conn.exec_driver_sql("create schema test_schema")
+ conn.exec_driver_sql("create schema test_schema_2")
@drop_db.for_db("mssql")
@@ -37,13 +37,13 @@ def _mssql_drop_ignore(conn, ident):
try:
# typically when this happens, we can't KILL the session anyway,
# so let the cleanup process drop the DBs
- # for row in conn.execute(
+ # for row in conn.exec_driver_sql(
# "select session_id from sys.dm_exec_sessions "
# "where database_id=db_id('%s')" % ident):
# log.info("killing SQL server sesssion %s", row['session_id'])
- # conn.execute("kill %s" % row['session_id'])
+ # conn.exec_driver_sql("kill %s" % row['session_id'])
- conn.execute("drop database %s" % ident)
+ conn.exec_driver_sql("drop database %s" % ident)
log.info("Reaped db: %s", ident)
return True
except exc.DatabaseError as err:
@@ -59,7 +59,7 @@ def _reap_mssql_dbs(url, idents):
log.info("identifiers in file: %s", ", ".join(idents))
- to_reap = conn.execute(
+ to_reap = conn.exec_driver_sql(
"select d.name from sys.databases as d where name "
"like 'TEST_%' and not exists (select session_id "
"from sys.dm_exec_sessions "
diff --git a/lib/sqlalchemy/dialects/mssql/pymssql.py b/lib/sqlalchemy/dialects/mssql/pymssql.py
index b8b3bf67d..2d355d964 100644
--- a/lib/sqlalchemy/dialects/mssql/pymssql.py
+++ b/lib/sqlalchemy/dialects/mssql/pymssql.py
@@ -73,7 +73,7 @@ class MSDialect_pymssql(MSDialect):
return module
def _get_server_version_info(self, connection):
- vers = connection.scalar("select @@version")
+ vers = connection.exec_driver_sql("select @@version").scalar()
m = re.match(r"Microsoft .*? - (\d+).(\d+).(\d+).(\d+)", vers)
if m:
return tuple(int(x) for x in m.group(1, 2, 3, 4))
diff --git a/lib/sqlalchemy/dialects/mssql/pyodbc.py b/lib/sqlalchemy/dialects/mssql/pyodbc.py
index 4ba3a0dfa..ffaf66fdd 100644
--- a/lib/sqlalchemy/dialects/mssql/pyodbc.py
+++ b/lib/sqlalchemy/dialects/mssql/pyodbc.py
@@ -353,9 +353,9 @@ class MSDialect_pyodbc(PyODBCConnector, MSDialect):
try:
# "Version of the instance of SQL Server, in the form
# of 'major.minor.build.revision'"
- raw = connection.scalar(
+ raw = connection.exec_driver_sql(
"SELECT CAST(SERVERPROPERTY('ProductVersion') AS VARCHAR)"
- )
+ ).scalar()
except exc.DBAPIError:
# SQL Server docs indicate this function isn't present prior to
# 2008. Before we had the VARCHAR cast above, pyodbc would also
diff --git a/lib/sqlalchemy/dialects/mysql/base.py b/lib/sqlalchemy/dialects/mysql/base.py
index a97719532..e193c1daa 100644
--- a/lib/sqlalchemy/dialects/mysql/base.py
+++ b/lib/sqlalchemy/dialects/mysql/base.py
@@ -2371,7 +2371,7 @@ class MySQLDialect(default.DefaultDialect):
connection.execute(sql.text("XA COMMIT :xid"), xid=xid)
def do_recover_twophase(self, connection):
- resultset = connection.execute("XA RECOVER")
+ resultset = connection.exec_driver_sql("XA RECOVER")
return [row["data"][0 : row["gtrid_length"]] for row in resultset]
def is_disconnect(self, e, connection, cursor):
@@ -2424,7 +2424,7 @@ class MySQLDialect(default.DefaultDialect):
raise NotImplementedError()
def _get_default_schema_name(self, connection):
- return connection.execute("SELECT DATABASE()").scalar()
+ return connection.exec_driver_sql("SELECT DATABASE()").scalar()
def has_table(self, connection, table_name, schema=None):
# SHOW TABLE STATUS LIKE and SHOW TABLES LIKE do not function properly
@@ -2449,7 +2449,7 @@ class MySQLDialect(default.DefaultDialect):
try:
rs = connection.execution_options(
skip_user_error_events=True
- ).execute(st)
+ ).exec_driver_sql(st)
have = rs.fetchone() is not None
rs.close()
return have
@@ -2552,7 +2552,7 @@ class MySQLDialect(default.DefaultDialect):
@reflection.cache
def get_schema_names(self, connection, **kw):
- rp = connection.execute("SHOW schemas")
+ rp = connection.exec_driver_sql("SHOW schemas")
return [r[0] for r in rp]
@reflection.cache
@@ -2565,7 +2565,7 @@ class MySQLDialect(default.DefaultDialect):
charset = self._connection_charset
if self.server_version_info < (5, 0, 2):
- rp = connection.execute(
+ rp = connection.exec_driver_sql(
"SHOW TABLES FROM %s"
% self.identifier_preparer.quote_identifier(current_schema)
)
@@ -2573,7 +2573,7 @@ class MySQLDialect(default.DefaultDialect):
row[0] for row in self._compat_fetchall(rp, charset=charset)
]
else:
- rp = connection.execute(
+ rp = connection.exec_driver_sql(
"SHOW FULL TABLES FROM %s"
% self.identifier_preparer.quote_identifier(current_schema)
)
@@ -2593,7 +2593,7 @@ class MySQLDialect(default.DefaultDialect):
if self.server_version_info < (5, 0, 2):
return self.get_table_names(connection, schema)
charset = self._connection_charset
- rp = connection.execute(
+ rp = connection.exec_driver_sql(
"SHOW FULL TABLES FROM %s"
% self.identifier_preparer.quote_identifier(schema)
)
@@ -2905,7 +2905,9 @@ class MySQLDialect(default.DefaultDialect):
charset = self._connection_charset
row = self._compat_first(
- connection.execute("SHOW VARIABLES LIKE 'lower_case_table_names'"),
+ connection.execute(
+ sql.text("SHOW VARIABLES LIKE 'lower_case_table_names'")
+ ),
charset=charset,
)
if not row:
@@ -2933,14 +2935,14 @@ class MySQLDialect(default.DefaultDialect):
pass
else:
charset = self._connection_charset
- rs = connection.execute("SHOW COLLATION")
+ rs = connection.exec_driver_sql("SHOW COLLATION")
for row in self._compat_fetchall(rs, charset):
collations[row[0]] = row[1]
return collations
def _detect_sql_mode(self, connection):
row = self._compat_first(
- connection.execute("SHOW VARIABLES LIKE 'sql_mode'"),
+ connection.exec_driver_sql("SHOW VARIABLES LIKE 'sql_mode'"),
charset=self._connection_charset,
)
@@ -2981,7 +2983,7 @@ class MySQLDialect(default.DefaultDialect):
try:
rp = connection.execution_options(
skip_user_error_events=True
- ).execute(st)
+ ).exec_driver_sql(st)
except exc.DBAPIError as e:
if self._extract_error_code(e.orig) == 1146:
util.raise_(exc.NoSuchTableError(full_name), replace_context=e)
@@ -3004,7 +3006,7 @@ class MySQLDialect(default.DefaultDialect):
try:
rp = connection.execution_options(
skip_user_error_events=True
- ).execute(st)
+ ).exec_driver_sql(st)
except exc.DBAPIError as e:
code = self._extract_error_code(e.orig)
if code == 1146:
diff --git a/lib/sqlalchemy/dialects/mysql/mysqldb.py b/lib/sqlalchemy/dialects/mysql/mysqldb.py
index 56aa991db..03c1779c3 100644
--- a/lib/sqlalchemy/dialects/mysql/mysqldb.py
+++ b/lib/sqlalchemy/dialects/mysql/mysqldb.py
@@ -154,15 +154,14 @@ class MySQLDialect_mysqldb(MySQLDialect):
# https://github.com/farcepest/MySQLdb1/commit/cd44524fef63bd3fcb71947392326e9742d520e8
# specific issue w/ the utf8mb4_bin collation and unicode returns
- has_utf8mb4_bin = self.server_version_info > (
- 5,
- ) and connection.scalar(
+ collation = connection.exec_driver_sql(
"show collation where %s = 'utf8mb4' and %s = 'utf8mb4_bin'"
% (
self.identifier_preparer.quote("Charset"),
self.identifier_preparer.quote("Collation"),
)
- )
+ ).scalar()
+ has_utf8mb4_bin = self.server_version_info > (5,) and collation
if has_utf8mb4_bin:
additional_tests = [
sql.collate(
diff --git a/lib/sqlalchemy/dialects/mysql/oursql.py b/lib/sqlalchemy/dialects/mysql/oursql.py
index 32357a24b..d7334711b 100644
--- a/lib/sqlalchemy/dialects/mysql/oursql.py
+++ b/lib/sqlalchemy/dialects/mysql/oursql.py
@@ -91,7 +91,7 @@ class MySQLDialect_oursql(MySQLDialect):
xid.encode(charset)
).decode(charset)
arg = "'%s'" % arg
- connection.execution_options(_oursql_plain_query=True).execute(
+ connection.execution_options(_oursql_plain_query=True).exec_driver_sql(
query % arg
)
diff --git a/lib/sqlalchemy/dialects/mysql/provision.py b/lib/sqlalchemy/dialects/mysql/provision.py
index 843b35caa..bf126464d 100644
--- a/lib/sqlalchemy/dialects/mysql/provision.py
+++ b/lib/sqlalchemy/dialects/mysql/provision.py
@@ -12,11 +12,13 @@ def _mysql_create_db(cfg, eng, ident):
except Exception:
pass
- conn.execute("CREATE DATABASE %s CHARACTER SET utf8mb4" % ident)
- conn.execute(
+ conn.exec_driver_sql(
+ "CREATE DATABASE %s CHARACTER SET utf8mb4" % ident
+ )
+ conn.exec_driver_sql(
"CREATE DATABASE %s_test_schema CHARACTER SET utf8mb4" % ident
)
- conn.execute(
+ conn.exec_driver_sql(
"CREATE DATABASE %s_test_schema_2 CHARACTER SET utf8mb4" % ident
)
@@ -30,9 +32,9 @@ def _mysql_configure_follower(config, ident):
@drop_db.for_db("mysql")
def _mysql_drop_db(cfg, eng, ident):
with eng.connect() as conn:
- conn.execute("DROP DATABASE %s_test_schema" % ident)
- conn.execute("DROP DATABASE %s_test_schema_2" % ident)
- conn.execute("DROP DATABASE %s" % ident)
+ conn.exec_driver_sql("DROP DATABASE %s_test_schema" % ident)
+ conn.exec_driver_sql("DROP DATABASE %s_test_schema_2" % ident)
+ conn.exec_driver_sql("DROP DATABASE %s" % ident)
@temp_table_keyword_args.for_db("mysql")
diff --git a/lib/sqlalchemy/dialects/mysql/pyodbc.py b/lib/sqlalchemy/dialects/mysql/pyodbc.py
index eb62e6425..5a696562e 100644
--- a/lib/sqlalchemy/dialects/mysql/pyodbc.py
+++ b/lib/sqlalchemy/dialects/mysql/pyodbc.py
@@ -87,7 +87,9 @@ class MySQLDialect_pyodbc(PyODBCConnector, MySQLDialect):
#
# If it's decided that issuing that sort of SQL leaves you SOL, then
# this can prefer the driver value.
- rs = connection.execute("SHOW VARIABLES LIKE 'character_set%%'")
+ rs = connection.exec_driver_sql(
+ "SHOW VARIABLES LIKE 'character_set%%'"
+ )
opts = {row[0]: row[1] for row in self._compat_fetchall(rs)}
for key in ("character_set_connection", "character_set"):
if opts.get(key, None):
diff --git a/lib/sqlalchemy/dialects/oracle/base.py b/lib/sqlalchemy/dialects/oracle/base.py
index 356c37562..d12453924 100644
--- a/lib/sqlalchemy/dialects/oracle/base.py
+++ b/lib/sqlalchemy/dialects/oracle/base.py
@@ -1340,7 +1340,7 @@ class OracleDialect(default.DefaultDialect):
if self.server_version_info < (12, 2):
return self.server_version_info
try:
- compat = connection.execute(
+ compat = connection.exec_driver_sql(
"SELECT value FROM v$parameter WHERE name = 'compatible'"
).scalar()
except exc.DBAPIError:
@@ -1424,7 +1424,7 @@ class OracleDialect(default.DefaultDialect):
def _get_default_schema_name(self, connection):
return self.normalize_name(
- connection.execute("SELECT USER FROM DUAL").scalar()
+ connection.exec_driver_sql("SELECT USER FROM DUAL").scalar()
)
def _resolve_synonym(
@@ -1535,7 +1535,7 @@ class OracleDialect(default.DefaultDialect):
@reflection.cache
def get_schema_names(self, connection, **kw):
s = "SELECT username FROM all_users ORDER BY username"
- cursor = connection.execute(s)
+ cursor = connection.exec_driver_sql(s)
return [self.normalize_name(row[0]) for row in cursor]
@reflection.cache
diff --git a/lib/sqlalchemy/dialects/oracle/cx_oracle.py b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
index 69423992f..d5177daa6 100644
--- a/lib/sqlalchemy/dialects/oracle/cx_oracle.py
+++ b/lib/sqlalchemy/dialects/oracle/cx_oracle.py
@@ -870,10 +870,10 @@ class OracleDialect_cx_oracle(OracleDialect):
# NLS_TERRITORY or formatting behavior of the DB, we opt
# to just look at it
- self._decimal_char = connection.scalar(
+ self._decimal_char = connection.exec_driver_sql(
"select value from nls_session_parameters "
"where parameter = 'NLS_NUMERIC_CHARACTERS'"
- )[0]
+ ).scalar()[0]
if self._decimal_char != ".":
_detect_decimal = self._detect_decimal
_to_decimal = self._to_decimal
diff --git a/lib/sqlalchemy/dialects/oracle/provision.py b/lib/sqlalchemy/dialects/oracle/provision.py
index 7901eb4e8..9de14bff0 100644
--- a/lib/sqlalchemy/dialects/oracle/provision.py
+++ b/lib/sqlalchemy/dialects/oracle/provision.py
@@ -17,13 +17,13 @@ def _oracle_create_db(cfg, eng, ident):
# similar, so that the default tablespace is not "system"; reflection will
# fail otherwise
with eng.connect() as conn:
- conn.execute("create user %s identified by xe" % ident)
- conn.execute("create user %s_ts1 identified by xe" % ident)
- conn.execute("create user %s_ts2 identified by xe" % ident)
- conn.execute("grant dba to %s" % (ident,))
- conn.execute("grant unlimited tablespace to %s" % ident)
- conn.execute("grant unlimited tablespace to %s_ts1" % ident)
- conn.execute("grant unlimited tablespace to %s_ts2" % ident)
+ conn.exec_driver_sql("create user %s identified by xe" % ident)
+ conn.exec_driver_sql("create user %s_ts1 identified by xe" % ident)
+ conn.exec_driver_sql("create user %s_ts2 identified by xe" % ident)
+ conn.exec_driver_sql("grant dba to %s" % (ident,))
+ conn.exec_driver_sql("grant unlimited tablespace to %s" % ident)
+ conn.exec_driver_sql("grant unlimited tablespace to %s_ts1" % ident)
+ conn.exec_driver_sql("grant unlimited tablespace to %s_ts2" % ident)
@configure_follower.for_db("oracle")
@@ -34,7 +34,7 @@ def _oracle_configure_follower(config, ident):
def _ora_drop_ignore(conn, dbname):
try:
- conn.execute("drop user %s cascade" % dbname)
+ conn.exec_driver_sql("drop user %s cascade" % dbname)
log.info("Reaped db: %s", dbname)
return True
except exc.DatabaseError as err:
@@ -68,7 +68,7 @@ def _reap_oracle_dbs(url, idents):
log.info("identifiers in file: %s", ", ".join(idents))
- to_reap = conn.execute(
+ to_reap = conn.exec_driver_sql(
"select u.username from all_users u where username "
"like 'TEST_%' and not exists (select username "
"from v$session where username=u.username)"
diff --git a/lib/sqlalchemy/dialects/postgresql/base.py b/lib/sqlalchemy/dialects/postgresql/base.py
index 6089b2b8a..cb41a8f65 100644
--- a/lib/sqlalchemy/dialects/postgresql/base.py
+++ b/lib/sqlalchemy/dialects/postgresql/base.py
@@ -195,10 +195,10 @@ in order to determine the remote schema name. That is, if our ``search_path``
were set to include ``test_schema``, and we invoked a table
reflection process as follows::
- >>> from sqlalchemy import Table, MetaData, create_engine
+ >>> from sqlalchemy import Table, MetaData, create_engine, text
>>> engine = create_engine("postgresql://scott:tiger@localhost/test")
>>> with engine.connect() as conn:
- ... conn.execute("SET search_path TO test_schema, public")
+ ... conn.execute(text("SET search_path TO test_schema, public"))
... meta = MetaData()
... referring = Table('referring', meta,
... autoload=True, autoload_with=conn)
@@ -218,7 +218,7 @@ dialect-specific argument to both :class:`.Table` as well as
:meth:`.MetaData.reflect`::
>>> with engine.connect() as conn:
- ... conn.execute("SET search_path TO test_schema, public")
+ ... conn.execute(text("SET search_path TO test_schema, public"))
... meta = MetaData()
... referring = Table('referring', meta, autoload=True,
... autoload_with=conn,
@@ -2464,9 +2464,11 @@ class PGDialect(default.DefaultDialect):
# http://www.postgresql.org/docs/9.3/static/release-9-2.html#AEN116689
self.supports_smallserial = self.server_version_info >= (9, 2)
+ std_string = connection.exec_driver_sql(
+ "show standard_conforming_strings"
+ ).scalar()
self._backslash_escapes = (
- self.server_version_info < (8, 2)
- or connection.scalar("show standard_conforming_strings") == "off"
+ self.server_version_info < (8, 2) or std_string == "off"
)
self._supports_create_index_concurrently = (
@@ -2523,7 +2525,7 @@ class PGDialect(default.DefaultDialect):
self.do_begin(connection.connection)
def do_prepare_twophase(self, connection, xid):
- connection.execute("PREPARE TRANSACTION '%s'" % xid)
+ connection.exec_driver_sql("PREPARE TRANSACTION '%s'" % xid)
def do_rollback_twophase(
self, connection, xid, is_prepared=True, recover=False
@@ -2534,9 +2536,9 @@ class PGDialect(default.DefaultDialect):
# context when committing recoverable transactions
# Must find out a way how to make the dbapi not
# open a transaction.
- connection.execute("ROLLBACK")
- connection.execute("ROLLBACK PREPARED '%s'" % xid)
- connection.execute("BEGIN")
+ connection.exec_driver_sql("ROLLBACK")
+ connection.exec_driver_sql("ROLLBACK PREPARED '%s'" % xid)
+ connection.exec_driver_sql("BEGIN")
self.do_rollback(connection.connection)
else:
self.do_rollback(connection.connection)
@@ -2546,9 +2548,9 @@ class PGDialect(default.DefaultDialect):
):
if is_prepared:
if recover:
- connection.execute("ROLLBACK")
- connection.execute("COMMIT PREPARED '%s'" % xid)
- connection.execute("BEGIN")
+ connection.exec_driver_sql("ROLLBACK")
+ connection.exec_driver_sql("COMMIT PREPARED '%s'" % xid)
+ connection.exec_driver_sql("BEGIN")
self.do_rollback(connection.connection)
else:
self.do_commit(connection.connection)
@@ -2560,7 +2562,7 @@ class PGDialect(default.DefaultDialect):
return [row[0] for row in resultset]
def _get_default_schema_name(self, connection):
- return connection.scalar("select current_schema()")
+ return connection.exec_driver_sql("select current_schema()").scalar()
def has_schema(self, connection, schema):
query = (
@@ -2689,7 +2691,7 @@ class PGDialect(default.DefaultDialect):
return bool(cursor.scalar())
def _get_server_version_info(self, connection):
- v = connection.execute("select version()").scalar()
+ v = connection.exec_driver_sql("select version()").scalar()
m = re.match(
r".*(?:PostgreSQL|EnterpriseDB) "
r"(\d+)\.?(\d+)?(?:\.(\d+))?(?:\.\d+)?(?:devel|beta)?",
diff --git a/lib/sqlalchemy/dialects/postgresql/provision.py b/lib/sqlalchemy/dialects/postgresql/provision.py
index c8f83d2be..6c6dc4be6 100644
--- a/lib/sqlalchemy/dialects/postgresql/provision.py
+++ b/lib/sqlalchemy/dialects/postgresql/provision.py
@@ -18,12 +18,14 @@ def _pg_create_db(cfg, eng, ident):
except Exception:
pass
if not template_db:
- template_db = conn.scalar("select current_database()")
+ template_db = conn.exec_driver_sql(
+ "select current_database()"
+ ).scalar()
attempt = 0
while True:
try:
- conn.execute(
+ conn.exec_driver_sql(
"CREATE DATABASE %s TEMPLATE %s" % (ident, template_db)
)
except exc.OperationalError as err:
@@ -56,7 +58,7 @@ def _pg_drop_db(cfg, eng, ident):
),
dname=ident,
)
- conn.execute("DROP DATABASE %s" % ident)
+ conn.exec_driver_sql("DROP DATABASE %s" % ident)
@temp_table_keyword_args.for_db("postgresql")
diff --git a/lib/sqlalchemy/dialects/sqlite/base.py b/lib/sqlalchemy/dialects/sqlite/base.py
index b1a83bf92..a63ce0033 100644
--- a/lib/sqlalchemy/dialects/sqlite/base.py
+++ b/lib/sqlalchemy/dialects/sqlite/base.py
@@ -518,14 +518,14 @@ to filter these out::
eng = create_engine("sqlite://")
conn = eng.connect()
- conn.execute("create table x (a integer, b integer)")
- conn.execute("insert into x (a, b) values (1, 1)")
- conn.execute("insert into x (a, b) values (2, 2)")
+ conn.exec_driver_sql("create table x (a integer, b integer)")
+ conn.exec_driver_sql("insert into x (a, b) values (1, 1)")
+ conn.exec_driver_sql("insert into x (a, b) values (2, 2)")
- result = conn.execute("select x.a, x.b from x")
+ result = conn.exec_driver_sql("select x.a, x.b from x")
assert result.keys() == ["a", "b"]
- result = conn.execute('''
+ result = conn.exec_driver_sql('''
select x.a, x.b from x where a=1
union
select x.a, x.b from x where a=2
@@ -553,7 +553,7 @@ contain dots, and the functionality of :meth:`.ResultProxy.keys` and
the ``sqlite_raw_colnames`` execution option may be provided, either on a
per-:class:`.Connection` basis::
- result = conn.execution_options(sqlite_raw_colnames=True).execute('''
+ result = conn.execution_options(sqlite_raw_colnames=True).exec_driver_sql('''
select x.a, x.b from x where a=1
union
select x.a, x.b from x where a=2
@@ -1588,7 +1588,7 @@ class SQLiteDialect(default.DefaultDialect):
@reflection.cache
def get_schema_names(self, connection, **kw):
s = "PRAGMA database_list"
- dl = connection.execute(s)
+ dl = connection.exec_driver_sql(s)
return [db[1] for db in dl if db[1] != "temp"]
@@ -1602,7 +1602,7 @@ class SQLiteDialect(default.DefaultDialect):
s = ("SELECT name FROM %s " "WHERE type='table' ORDER BY name") % (
master,
)
- rs = connection.execute(s)
+ rs = connection.exec_driver_sql(s)
return [row[0] for row in rs]
@reflection.cache
@@ -1611,7 +1611,7 @@ class SQLiteDialect(default.DefaultDialect):
"SELECT name FROM sqlite_temp_master "
"WHERE type='table' ORDER BY name "
)
- rs = connection.execute(s)
+ rs = connection.exec_driver_sql(s)
return [row[0] for row in rs]
@@ -1621,7 +1621,7 @@ class SQLiteDialect(default.DefaultDialect):
"SELECT name FROM sqlite_temp_master "
"WHERE type='view' ORDER BY name "
)
- rs = connection.execute(s)
+ rs = connection.exec_driver_sql(s)
return [row[0] for row in rs]
@@ -1641,7 +1641,7 @@ class SQLiteDialect(default.DefaultDialect):
s = ("SELECT name FROM %s " "WHERE type='view' ORDER BY name") % (
master,
)
- rs = connection.execute(s)
+ rs = connection.exec_driver_sql(s)
return [row[0] for row in rs]
@@ -1654,7 +1654,7 @@ class SQLiteDialect(default.DefaultDialect):
master,
view_name,
)
- rs = connection.execute(s)
+ rs = connection.exec_driver_sql(s)
else:
try:
s = (
@@ -1664,13 +1664,13 @@ class SQLiteDialect(default.DefaultDialect):
"WHERE name = '%s' "
"AND type='view'"
) % view_name
- rs = connection.execute(s)
+ rs = connection.exec_driver_sql(s)
except exc.DBAPIError:
s = (
"SELECT sql FROM sqlite_master WHERE name = '%s' "
"AND type='view'"
) % view_name
- rs = connection.execute(s)
+ rs = connection.exec_driver_sql(s)
result = rs.fetchall()
if result:
@@ -2070,7 +2070,7 @@ class SQLiteDialect(default.DefaultDialect):
"AND type = 'table'"
% {"schema": schema_expr, "table": table_name}
)
- rs = connection.execute(s)
+ rs = connection.exec_driver_sql(s)
except exc.DBAPIError:
s = (
"SELECT sql FROM %(schema)ssqlite_master "
@@ -2078,7 +2078,7 @@ class SQLiteDialect(default.DefaultDialect):
"AND type = 'table'"
% {"schema": schema_expr, "table": table_name}
)
- rs = connection.execute(s)
+ rs = connection.exec_driver_sql(s)
return rs.scalar()
def _get_table_pragma(self, connection, pragma, table_name, schema=None):
@@ -2095,7 +2095,7 @@ class SQLiteDialect(default.DefaultDialect):
qtable = quote(table_name)
for statement in statements:
statement = "%s%s(%s)" % (statement, pragma, qtable)
- cursor = connection.execute(statement)
+ cursor = connection.exec_driver_sql(statement)
if not cursor._soft_closed:
# work around SQLite issue whereby cursor.description
# is blank when PRAGMA returns no rows:
diff --git a/lib/sqlalchemy/dialects/sqlite/pysqlcipher.py b/lib/sqlalchemy/dialects/sqlite/pysqlcipher.py
index f8236dea9..a1243f271 100644
--- a/lib/sqlalchemy/dialects/sqlite/pysqlcipher.py
+++ b/lib/sqlalchemy/dialects/sqlite/pysqlcipher.py
@@ -112,10 +112,10 @@ class SQLiteDialect_pysqlcipher(SQLiteDialect_pysqlite):
conn = super(SQLiteDialect_pysqlcipher, self).connect(
*cargs, **cparams
)
- conn.execute('pragma key="%s"' % passphrase)
+ conn.exec_driver_sql('pragma key="%s"' % passphrase)
for prag, value in pragmas.items():
if value is not None:
- conn.execute('pragma %s="%s"' % (prag, value))
+ conn.exec_driver_sql('pragma %s="%s"' % (prag, value))
return conn
diff --git a/lib/sqlalchemy/dialects/sqlite/pysqlite.py b/lib/sqlalchemy/dialects/sqlite/pysqlite.py
index 4485631ce..b8a42a506 100644
--- a/lib/sqlalchemy/dialects/sqlite/pysqlite.py
+++ b/lib/sqlalchemy/dialects/sqlite/pysqlite.py
@@ -322,7 +322,7 @@ ourselves. This is achieved using two event listeners::
@event.listens_for(engine, "begin")
def do_begin(conn):
# emit our own BEGIN
- conn.execute("BEGIN")
+ conn.exec_driver_sql("BEGIN")
Above, we intercept a new pysqlite connection and disable any transactional
integration. Then, at the point at which SQLAlchemy knows that transaction
@@ -335,7 +335,7 @@ by adding the desired locking mode to our ``"BEGIN"``::
@event.listens_for(engine, "begin")
def do_begin(conn):
- conn.execute("BEGIN EXCLUSIVE")
+ conn.exec_driver_sql("BEGIN EXCLUSIVE")
.. seealso::
diff --git a/lib/sqlalchemy/dialects/sybase/pysybase.py b/lib/sqlalchemy/dialects/sybase/pysybase.py
index ab8e6eac0..a36cd74ca 100644
--- a/lib/sqlalchemy/dialects/sybase/pysybase.py
+++ b/lib/sqlalchemy/dialects/sybase/pysybase.py
@@ -82,7 +82,7 @@ class SybaseDialect_pysybase(SybaseDialect):
cursor.execute(statement, param)
def _get_server_version_info(self, connection):
- vers = connection.scalar("select @@version_number")
+ vers = connection.exec_driver_sql("select @@version_number").scalar()
# i.e. 15500, 15000, 12500 == (15, 5, 0, 0), (15, 0, 0, 0),
# (12, 5, 0, 0)
return (vers / 1000, vers % 1000 / 100, vers % 100 / 10, vers % 10)
diff --git a/lib/sqlalchemy/engine/base.py b/lib/sqlalchemy/engine/base.py
index 598b43e7b..aa21fb13b 100644
--- a/lib/sqlalchemy/engine/base.py
+++ b/lib/sqlalchemy/engine/base.py
@@ -563,7 +563,7 @@ class Connection(Connectable):
with engine.connect() as conn:
conn.detach()
- conn.execute("SET search_path TO schema1, schema2")
+ conn.execute(text("SET search_path TO schema1, schema2"))
# work with connection
@@ -933,7 +933,7 @@ class Connection(Connectable):
:param object: The statement to be executed. May be
one of:
- * a plain string
+ * a plain string (deprecated)
* any :class:`.ClauseElement` construct that is also
a subclass of :class:`.Executable`, such as a
:func:`~.expression.select` construct
@@ -944,6 +944,13 @@ class Connection(Connectable):
* a :class:`.DefaultGenerator` object
* a :class:`.Compiled` object
+ .. deprecated:: 2.0 passing a string to :meth:`.Connection.execute` is
+ deprecated and will be removed in version 2.0. Use the
+ :func:`~.expression.text` construct with
+ :meth:`.Connection.execute`, or the
+ :meth:`.Connection.exec_driver_sql` method to invoke a driver-level
+ SQL string.
+
:param \*multiparams/\**params: represent bound parameter
values to be used in the execution. Typically,
the format is either a collection of one or more
@@ -984,9 +991,22 @@ class Connection(Connectable):
To execute a textual SQL statement which uses bound parameters in a
DBAPI-agnostic way, use the :func:`~.expression.text` construct.
+ .. deprecated:: 2.0 use of tuple or scalar positional parameters
+ is deprecated. All params should be dicts or sequences of dicts.
+ Use :meth:`.exec_driver_sql` to execute a plain string with
+ tuple or scalar positional parameters.
+
"""
if isinstance(object_, util.string_types[0]):
- return self._execute_text(object_, multiparams, params)
+ util.warn_deprecated_20(
+ "Passing a string to Connection.execute() is "
+ "deprecated and will be removed in version 2.0. Use the "
+ "text() construct, "
+ "or the Connection.exec_driver_sql() method to invoke a "
+ "driver-level SQL string."
+ )
+ distilled_params = _distill_params(multiparams, params)
+ return self._exec_driver_sql_distilled(object_, distilled_params)
try:
meth = object_._execute_on_connection
except AttributeError as err:
@@ -1147,17 +1167,15 @@ class Connection(Connectable):
)
return ret
- def _execute_text(self, statement, multiparams, params):
- """Execute a string SQL statement."""
+ def _exec_driver_sql_distilled(self, statement, parameters):
if self._has_events or self.engine._has_events:
for fn in self.dispatch.before_execute:
statement, multiparams, params = fn(
- self, statement, multiparams, params
+ self, statement, parameters, {}
)
dialect = self.dialect
- parameters = _distill_params(multiparams, params)
ret = self._execute_context(
dialect,
dialect.execution_ctx_cls._init_statement,
@@ -1167,11 +1185,60 @@ class Connection(Connectable):
parameters,
)
if self._has_events or self.engine._has_events:
- self.dispatch.after_execute(
- self, statement, multiparams, params, ret
- )
+ self.dispatch.after_execute(self, statement, parameters, {})
return ret
+ def exec_driver_sql(self, statement, parameters=None):
+ r"""Executes a SQL statement construct and returns a
+ :class:`.ResultProxy`.
+
+ :param statement: The statement str to be executed. Bound parameters
+ must use the underlying DBAPI's paramstyle, such as "qmark",
+ "pyformat", "format", etc.
+
+ :param parameters: represent bound parameter values to be used in the
+ execution. The format is one of: a dictionary of named parameters,
+ a tuple of positional parameters, or a list containing either
+ dictionaries or tuples for multiple-execute support.
+
+ E.g. multiple dictionaries::
+
+
+ conn.exec_driver_sql(
+ "INSERT INTO table (id, value) VALUES (%(id)s, %(value)s)",
+ [{"id":1, "value":"v1"}, {"id":2, "value":"v2"}]
+ )
+
+ Single dictionary::
+
+ conn.exec_driver_sql(
+ "INSERT INTO table (id, value) VALUES (%(id)s, %(value)s)",
+ dict(id=1, value="v1")
+ )
+
+ Single tuple::
+
+ conn.exec_driver_sql(
+ "INSERT INTO table (id, value) VALUES (?, ?)",
+ (1, 'v1')
+ )
+
+ .. seealso::
+
+ :pep:`249`
+
+ """
+
+ if isinstance(parameters, list) and parameters:
+ if not isinstance(parameters[0], (dict, tuple)):
+ raise exc.ArgumentError(
+ "List argument must consist only of tuples or dictionaries"
+ )
+ elif isinstance(parameters, (dict, tuple)):
+ parameters = [parameters]
+
+ return self._exec_driver_sql_distilled(statement, parameters or ())
+
def _execute_context(
self, dialect, constructor, statement, parameters, *args
):
@@ -1603,7 +1670,7 @@ class Connection(Connectable):
e.g.::
def do_something(conn, x, y):
- conn.execute("some statement", {'x':x, 'y':y})
+ conn.execute(text("some statement"), {'x':x, 'y':y})
conn.transaction(do_something, 5, 10)
@@ -1620,12 +1687,12 @@ class Connection(Connectable):
be used with :meth:`.Connection.begin`::
with conn.begin():
- conn.execute("some statement", {'x':5, 'y':10})
+ conn.execute(text("some statement"), {'x':5, 'y':10})
As well as with :meth:`.Engine.begin`::
with engine.begin() as conn:
- conn.execute("some statement", {'x':5, 'y':10})
+ conn.execute(text("some statement"), {'x':5, 'y':10})
.. seealso::
@@ -1706,7 +1773,7 @@ class Transaction(object):
engine = create_engine("postgresql://scott:tiger@localhost/test")
connection = engine.connect()
trans = connection.begin()
- connection.execute("insert into x (a, b) values (1, 2)")
+ connection.execute(text("insert into x (a, b) values (1, 2)"))
trans.commit()
The object provides :meth:`.rollback` and :meth:`.commit`
@@ -1716,7 +1783,7 @@ class Transaction(object):
:meth:`.Connection.begin` method::
with connection.begin():
- connection.execute("insert into x (a, b) values (1, 2)")
+ connection.execute(text("insert into x (a, b) values (1, 2)"))
The Transaction object is **not** threadsafe.
@@ -2129,8 +2196,10 @@ class Engine(Connectable, log.Identified):
E.g.::
with engine.begin() as conn:
- conn.execute("insert into table (x, y, z) values (1, 2, 3)")
- conn.execute("my_special_procedure(5)")
+ conn.execute(
+ text("insert into table (x, y, z) values (1, 2, 3)")
+ )
+ conn.execute(text("my_special_procedure(5)"))
Upon successful operation, the :class:`.Transaction`
is committed. If an error is raised, the :class:`.Transaction`
@@ -2177,7 +2246,7 @@ class Engine(Connectable, log.Identified):
e.g.::
def do_something(conn, x, y):
- conn.execute("some statement", {'x':x, 'y':y})
+ conn.execute(text("some statement"), {'x':x, 'y':y})
engine.transaction(do_something, 5, 10)
@@ -2194,7 +2263,7 @@ class Engine(Connectable, log.Identified):
be used with :meth:`.Engine.begin`::
with engine.begin() as conn:
- conn.execute("some statement", {'x':5, 'y':10})
+ conn.execute(text("some statement"), {'x':5, 'y':10})
.. seealso::
@@ -2270,6 +2339,10 @@ class Engine(Connectable, log.Identified):
"used to return a scalar result.",
)
def scalar(self, statement, *multiparams, **params):
+ """Executes and returns the first column of the first row.
+
+ The underlying result/cursor is closed after execution.
+ """
return self.execute(statement, *multiparams, **params).scalar()
def _execute_clauseelement(self, elem, multiparams=None, params=None):
diff --git a/lib/sqlalchemy/engine/reflection.py b/lib/sqlalchemy/engine/reflection.py
index ba60d634e..203369ed8 100644
--- a/lib/sqlalchemy/engine/reflection.py
+++ b/lib/sqlalchemy/engine/reflection.py
@@ -114,7 +114,7 @@ class Inspector(object):
return self
def _init_legacy(self, bind):
- if hasattr(bind, "engine"):
+ if hasattr(bind, "exec_driver_sql"):
self._init_connection(bind)
else:
self._init_engine(bind)
diff --git a/lib/sqlalchemy/engine/result.py b/lib/sqlalchemy/engine/result.py
index 7db9eecae..6755903a0 100644
--- a/lib/sqlalchemy/engine/result.py
+++ b/lib/sqlalchemy/engine/result.py
@@ -918,7 +918,7 @@ class BufferedRowCursorFetchStrategy(DefaultCursorFetchStrategy):
result = conn.execution_options(
stream_results=True, max_row_buffer=50
- ).execute("select * from table")
+ ).execute(text("select * from table"))
.. versionadded:: 1.4 ``max_row_buffer`` may now exceed 1000 rows.
diff --git a/lib/sqlalchemy/orm/events.py b/lib/sqlalchemy/orm/events.py
index 4a50f6464..64f09ea2f 100644
--- a/lib/sqlalchemy/orm/events.py
+++ b/lib/sqlalchemy/orm/events.py
@@ -603,9 +603,9 @@ class MapperEvents(event.Events):
def my_before_insert_listener(mapper, connection, target):
# execute a stored procedure upon INSERT,
# apply the value to the row to be inserted
- target.calculated_value = connection.scalar(
- "select my_special_function(%d)"
- % target.special_number)
+ target.calculated_value = connection.execute(
+ text("select my_special_function(%d)" % target.special_number)
+ ).scalar()
# associate the listener function with SomeClass,
# to execute during the "before_insert" hook
diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py
index ccadeeaac..10cd39e75 100644
--- a/lib/sqlalchemy/orm/util.py
+++ b/lib/sqlalchemy/orm/util.py
@@ -306,8 +306,9 @@ def identity_key(*args, **kwargs):
E.g.::
- >>> row = engine.execute("select * from table where a=1 and b=2").\
-first()
+ >>> row = engine.execute(\
+ text("select * from table where a=1 and b=2")\
+ ).first()
>>> identity_key(MyClass, row=row)
(<class '__main__.MyClass'>, (1, 2), None)
diff --git a/lib/sqlalchemy/sql/base.py b/lib/sqlalchemy/sql/base.py
index 262dc4a0e..77222706a 100644
--- a/lib/sqlalchemy/sql/base.py
+++ b/lib/sqlalchemy/sql/base.py
@@ -470,6 +470,28 @@ class Generative(object):
s.__dict__ = self.__dict__.copy()
return s
+
+class HasCompileState(Generative):
+ """A class that has a :class:`.CompileState` associated with it."""
+
+ _compile_state_factory = CompileState._create
+
+ _compile_state_plugin = None
+
+
+class Executable(Generative):
+ """Mark a ClauseElement as supporting execution.
+
+ :class:`.Executable` is a superclass for all "statement" types
+ of objects, including :func:`select`, :func:`delete`, :func:`update`,
+ :func:`insert`, :func:`text`.
+
+ """
+
+ supports_execution = True
+ _execution_options = util.immutabledict()
+ _bind = None
+
def options(self, *options):
"""Apply options to this statement.
@@ -501,28 +523,6 @@ class Generative(object):
"""
self._options += options
-
-class HasCompileState(Generative):
- """A class that has a :class:`.CompileState` associated with it."""
-
- _compile_state_factory = CompileState._create
-
- _compile_state_plugin = None
-
-
-class Executable(Generative):
- """Mark a ClauseElement as supporting execution.
-
- :class:`.Executable` is a superclass for all "statement" types
- of objects, including :func:`select`, :func:`delete`, :func:`update`,
- :func:`insert`, :func:`text`.
-
- """
-
- supports_execution = True
- _execution_options = util.immutabledict()
- _bind = None
-
@_generative
def execution_options(self, **kw):
""" Set non-SQL options for the statement which take effect during
diff --git a/lib/sqlalchemy/sql/events.py b/lib/sqlalchemy/sql/events.py
index cd48d99fc..cd0ba2640 100644
--- a/lib/sqlalchemy/sql/events.py
+++ b/lib/sqlalchemy/sql/events.py
@@ -35,8 +35,9 @@ class DDLEvents(event.Events):
some_table = Table('some_table', m, Column('data', Integer))
def after_create(target, connection, **kw):
- connection.execute("ALTER TABLE %s SET name=foo_%s" %
- (target.name, target.name))
+ connection.execute(text(
+ "ALTER TABLE %s SET name=foo_%s" % (target.name, target.name)
+ ))
event.listen(some_table, "after_create", after_create)
diff --git a/lib/sqlalchemy/testing/suite/test_results.py b/lib/sqlalchemy/testing/suite/test_results.py
index 5186e189c..7a3e92564 100644
--- a/lib/sqlalchemy/testing/suite/test_results.py
+++ b/lib/sqlalchemy/testing/suite/test_results.py
@@ -15,6 +15,7 @@ from ... import sql
from ... import String
from ... import testing
from ... import text
+from ... import util
class RowFetchTest(fixtures.TablesTest):
@@ -287,7 +288,10 @@ class ServerSideCursorsTest(
):
engine = self._fixture(engine_ss_arg)
with engine.begin() as conn:
- result = conn.execute(statement)
+ if isinstance(statement, util.string_types):
+ result = conn.exec_driver_sql(statement)
+ else:
+ result = conn.execute(statement)
eq_(self._is_server_side(result.cursor), cursor_ss_status)
result.close()
@@ -298,7 +302,7 @@ class ServerSideCursorsTest(
result = (
engine.connect()
.execution_options(stream_results=True)
- .execute("select 1")
+ .exec_driver_sql("select 1")
)
assert self._is_server_side(result.cursor)
diff --git a/lib/sqlalchemy/testing/suite/test_rowcount.py b/lib/sqlalchemy/testing/suite/test_rowcount.py
index 83c2f8da4..ae189bc79 100644
--- a/lib/sqlalchemy/testing/suite/test_rowcount.py
+++ b/lib/sqlalchemy/testing/suite/test_rowcount.py
@@ -95,7 +95,7 @@ class RowCountTest(fixtures.TablesTest):
def test_raw_sql_rowcount(self, connection):
# test issue #3622, make sure eager rowcount is called for text
- result = connection.execute(
+ result = connection.exec_driver_sql(
"update employees set department='Z' where department='C'"
)
eq_(result.rowcount, 3)
diff --git a/lib/sqlalchemy/testing/suite/test_select.py b/lib/sqlalchemy/testing/suite/test_select.py
index 3b64b0f29..5a7fd28e1 100644
--- a/lib/sqlalchemy/testing/suite/test_select.py
+++ b/lib/sqlalchemy/testing/suite/test_select.py
@@ -175,6 +175,10 @@ class LimitOffsetTest(fixtures.TablesTest):
def _assert_result(self, select, result, params=()):
eq_(config.db.execute(select, params).fetchall(), result)
+ def _assert_result_str(self, select, result, params=()):
+ conn = config.db.connect(close_with_result=True)
+ eq_(conn.exec_driver_sql(select, params).fetchall(), result)
+
def test_simple_limit(self):
table = self.tables.some_table
self._assert_result(
@@ -209,7 +213,7 @@ class LimitOffsetTest(fixtures.TablesTest):
)
sql = str(sql)
- self._assert_result(sql, [(2, 2, 3), (3, 3, 4)])
+ self._assert_result_str(sql, [(2, 2, 3), (3, 3, 4)])
@testing.requires.bound_limit_offset
def test_bound_limit(self):
diff --git a/lib/sqlalchemy/testing/suite/test_types.py b/lib/sqlalchemy/testing/suite/test_types.py
index 73cd1deca..9dabdbd65 100644
--- a/lib/sqlalchemy/testing/suite/test_types.py
+++ b/lib/sqlalchemy/testing/suite/test_types.py
@@ -1075,7 +1075,7 @@ class JSONStringCastIndexTest(_LiteralRoundTripFixture, fixtures.TablesTest):
)
)
- eq_(conn.scalar(literal_sql), expected)
+ eq_(conn.exec_driver_sql(literal_sql).scalar(), expected)
def test_string_cast_crit_spaces_in_key(self):
name = self.tables.data_table.c.name