Skip to content

Commit 2b4cd9f

Browse files
SQL: revert deprecated tquery/uquery to 0.13 version
As tquery and uquery are deprecated, it is not needed to adapt them to the new sqlalchemy interface + the adapted version changed the signature slightly so it was not fully backwards compatible. Therefore: put back the old code and removed new implementatation in the classes. Consequence: the functions are not available for the new sqlalchemy engines, but as they are deprecated this is not needed.
1 parent a1bd004 commit 2b4cd9f

File tree

2 files changed

+91
-77
lines changed

2 files changed

+91
-77
lines changed

pandas/io/sql.py

Lines changed: 80 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from datetime import datetime, date, timedelta
77

88
import warnings
9+
import traceback
910
import itertools
1011
import re
1112
import numpy as np
@@ -102,75 +103,122 @@ def execute(sql, con, cur=None, params=None, flavor='sqlite'):
102103
return pandas_sql.execute(*args)
103104

104105

105-
def tquery(sql, con, cur=None, params=None, flavor='sqlite'):
106+
#------------------------------------------------------------------------------
107+
#--- Deprecated tquery and uquery
108+
109+
def _safe_fetch(cur):
110+
try:
111+
result = cur.fetchall()
112+
if not isinstance(result, list):
113+
result = list(result)
114+
return result
115+
except Exception as e: # pragma: no cover
116+
excName = e.__class__.__name__
117+
if excName == 'OperationalError':
118+
return []
119+
120+
def tquery(sql, con=None, cur=None, retry=True):
106121
"""
107-
Returns list of tuples corresponding to each row in given sql
122+
DEPRECATED. Returns list of tuples corresponding to each row in given sql
108123
query.
109124
110125
If only one column selected, then plain list is returned.
111126
127+
To obtain the same result in the future, you can use the following:
128+
129+
>>> execute(sql, con, params).fetchall()
130+
112131
Parameters
113132
----------
114133
sql: string
115134
SQL query to be executed
116-
con: SQLAlchemy engine or DBAPI2 connection (legacy mode)
117-
Using SQLAlchemy makes it possible to use any DB supported by that
118-
library.
119-
If a DBAPI2 object is given, a supported SQL flavor must also be provided
135+
con: DBAPI2 connection
120136
cur: depreciated, cursor is obtained from connection
121-
params: list or tuple, optional
122-
List of parameters to pass to execute method.
123-
flavor : string "sqlite", "mysql"
124-
Specifies the flavor of SQL to use.
125-
Ignored when using SQLAlchemy engine. Required when using DBAPI2
126-
connection.
137+
127138
Returns
128139
-------
129140
Results Iterable
141+
130142
"""
131143
warnings.warn(
132-
"tquery is depreciated, and will be removed in future versions",
144+
"tquery is depreciated, and will be removed in future versions. "
145+
"You can use ``execute(...).fetchall()`` instead.",
133146
FutureWarning)
147+
148+
cur = execute(sql, con, cur=cur)
149+
result = _safe_fetch(cur)
134150

135-
pandas_sql = pandasSQL_builder(con, flavor=flavor)
136-
args = _convert_params(sql, params)
137-
return pandas_sql.tquery(*args)
151+
if con is not None:
152+
try:
153+
cur.close()
154+
con.commit()
155+
except Exception as e:
156+
excName = e.__class__.__name__
157+
if excName == 'OperationalError': # pragma: no cover
158+
print('Failed to commit, may need to restart interpreter')
159+
else:
160+
raise
138161

162+
traceback.print_exc()
163+
if retry:
164+
return tquery(sql, con=con, retry=False)
139165

140-
def uquery(sql, con, cur=None, params=None, engine=None, flavor='sqlite'):
166+
if result and len(result[0]) == 1:
167+
# python 3 compat
168+
result = list(lzip(*result)[0])
169+
elif result is None: # pragma: no cover
170+
result = []
171+
172+
return result
173+
174+
175+
def uquery(sql, con=None, cur=None, retry=True, params=None):
141176
"""
142-
Does the same thing as tquery, but instead of returning results, it
177+
DEPRECATED. Does the same thing as tquery, but instead of returning results, it
143178
returns the number of rows affected. Good for update queries.
144179
180+
To obtain the same result in the future, you can use the following:
181+
182+
>>> execute(sql, con).rowcount
183+
145184
Parameters
146185
----------
147186
sql: string
148187
SQL query to be executed
149-
con: SQLAlchemy engine or DBAPI2 connection (legacy mode)
150-
Using SQLAlchemy makes it possible to use any DB supported by that
151-
library.
152-
If a DBAPI2 object is given, a supported SQL flavor must also be provided
188+
con: DBAPI2 connection
153189
cur: depreciated, cursor is obtained from connection
154190
params: list or tuple, optional
155191
List of parameters to pass to execute method.
156-
flavor : string "sqlite", "mysql"
157-
Specifies the flavor of SQL to use.
158-
Ignored when using SQLAlchemy engine. Required when using DBAPI2
159-
connection.
192+
160193
Returns
161194
-------
162195
Number of affected rows
196+
163197
"""
164198
warnings.warn(
165-
"uquery is depreciated, and will be removed in future versions",
199+
"uquery is depreciated, and will be removed in future versions. "
200+
"You can use ``execute(...).rowcount`` instead.",
166201
FutureWarning)
167-
pandas_sql = pandasSQL_builder(con, flavor=flavor)
168-
args = _convert_params(sql, params)
169-
return pandas_sql.uquery(*args)
202+
203+
cur = execute(sql, con, cur=cur, params=params)
204+
205+
result = cur.rowcount
206+
try:
207+
con.commit()
208+
except Exception as e:
209+
excName = e.__class__.__name__
210+
if excName != 'OperationalError':
211+
raise
212+
213+
traceback.print_exc()
214+
if retry:
215+
print('Looks like your connection failed, reconnecting...')
216+
return uquery(sql, con, retry=False)
217+
return result
170218

171219

172220
#------------------------------------------------------------------------------
173-
# Read and write to DataFrames
221+
#--- Read and write to DataFrames
174222

175223
def read_sql_table(table_name, con, meta=None, index_col=None,
176224
coerce_float=True, parse_dates=None, columns=None):
@@ -722,14 +770,6 @@ def execute(self, *args, **kwargs):
722770
"""Simple passthrough to SQLAlchemy engine"""
723771
return self.engine.execute(*args, **kwargs)
724772

725-
def tquery(self, *args, **kwargs):
726-
result = self.execute(*args, **kwargs)
727-
return result.fetchall()
728-
729-
def uquery(self, *args, **kwargs):
730-
result = self.execute(*args, **kwargs)
731-
return result.rowcount
732-
733773
def read_table(self, table_name, index_col=None, coerce_float=True,
734774
parse_dates=None, columns=None):
735775

@@ -953,22 +993,6 @@ def execute(self, *args, **kwargs):
953993
ex = DatabaseError("Execution failed on sql: %s" % args[0])
954994
raise_with_traceback(ex)
955995

956-
def tquery(self, *args):
957-
cur = self.execute(*args)
958-
result = self._fetchall_as_list(cur)
959-
960-
# This makes into tuples
961-
if result and len(result[0]) == 1:
962-
# python 3 compat
963-
result = list(lzip(*result)[0])
964-
elif result is None: # pragma: no cover
965-
result = []
966-
return result
967-
968-
def uquery(self, *args):
969-
cur = self.execute(*args)
970-
return cur.rowcount
971-
972996
def read_sql(self, sql, index_col=None, coerce_float=True, params=None,
973997
parse_dates=None):
974998
args = _convert_params(sql, params)
@@ -1020,7 +1044,7 @@ def has_table(self, name):
10201044
'mysql': "SHOW TABLES LIKE '%s'" % name}
10211045
query = flavor_map.get(self.flavor)
10221046

1023-
return len(self.tquery(query)) > 0
1047+
return len(self.execute(query).fetchall()) > 0
10241048

10251049
def get_table(self, table_name):
10261050
return None # not supported in Legacy mode

pandas/io/tests/test_sql.py

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -296,11 +296,6 @@ def _execute_sql(self):
296296
row = iris_results.fetchone()
297297
tm.equalContents(row, [5.1, 3.5, 1.4, 0.2, 'Iris-setosa'])
298298

299-
def _tquery(self):
300-
iris_results = self.pandasSQL.tquery("SELECT * FROM iris")
301-
row = iris_results[0]
302-
tm.equalContents(row, [5.1, 3.5, 1.4, 0.2, 'Iris-setosa'])
303-
304299

305300
#------------------------------------------------------------------------------
306301
#--- Testing the public API
@@ -433,19 +428,6 @@ def test_execute_sql(self):
433428
row = iris_results.fetchone()
434429
tm.equalContents(row, [5.1, 3.5, 1.4, 0.2, 'Iris-setosa'])
435430

436-
def test_tquery(self):
437-
with tm.assert_produces_warning(FutureWarning):
438-
iris_results = sql.tquery(
439-
"SELECT * FROM iris", con=self.conn, flavor='sqlite')
440-
row = iris_results[0]
441-
tm.equalContents(row, [5.1, 3.5, 1.4, 0.2, 'Iris-setosa'])
442-
443-
def test_uquery(self):
444-
with tm.assert_produces_warning(FutureWarning):
445-
rows = sql.uquery(
446-
"SELECT * FROM iris LIMIT 1", con=self.conn, flavor='sqlite')
447-
self.assertEqual(rows, -1)
448-
449431
def test_date_parsing(self):
450432
# Test date parsing in read_sq
451433
# No Parsing
@@ -694,6 +676,17 @@ def test_get_schema2(self):
694676
create_sql = sql.get_schema(self.test_frame1, 'test', 'sqlite')
695677
self.assert_('CREATE' in create_sql)
696678

679+
def test_tquery(self):
680+
with tm.assert_produces_warning(FutureWarning):
681+
iris_results = sql.tquery("SELECT * FROM iris", con=self.conn)
682+
row = iris_results[0]
683+
tm.equalContents(row, [5.1, 3.5, 1.4, 0.2, 'Iris-setosa'])
684+
685+
def test_uquery(self):
686+
with tm.assert_produces_warning(FutureWarning):
687+
rows = sql.uquery("SELECT * FROM iris LIMIT 1", con=self.conn)
688+
self.assertEqual(rows, -1)
689+
697690

698691
#------------------------------------------------------------------------------
699692
#--- Database flavor specific tests
@@ -1063,9 +1056,6 @@ def test_roundtrip(self):
10631056
def test_execute_sql(self):
10641057
self._execute_sql()
10651058

1066-
def test_tquery(self):
1067-
self._tquery()
1068-
10691059

10701060
class TestMySQLLegacy(TestSQLiteLegacy):
10711061
"""

0 commit comments

Comments
 (0)