diff --git a/pandas/io/sql.py b/pandas/io/sql.py index fa89cf488125a..ac90555526a5e 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -31,7 +31,10 @@ def _convert_params(sql, params): """convert sql and params args to DBAPI2.0 compliant format""" args = [sql] if params is not None: - args += list(params) + if hasattr(params, 'keys'): # test if params is a mapping + args += [params] + else: + args += [list(params)] return args @@ -200,7 +203,7 @@ def read_sql(sql, con, index_col=None, flavor='sqlite', coerce_float=True, Attempt to convert values to non-string, non-numeric objects (like decimal.Decimal) to floating point, useful for SQL result sets cur : depreciated, cursor is obtained from connection - params : list or tuple, optional + params : list, tuple or dict, optional List of parameters to pass to execute method. parse_dates : list or dict - List of column names to parse as dates diff --git a/pandas/io/tests/test_sql.py b/pandas/io/tests/test_sql.py index 2f9323e50c9e2..80da7ae6bf391 100644 --- a/pandas/io/tests/test_sql.py +++ b/pandas/io/tests/test_sql.py @@ -95,6 +95,24 @@ INSERT INTO types_test_data VALUES(%s, %s, %s, %s, %s, %s, %s, %s) """ + }, + 'read_parameters': { + 'sqlite': "SELECT * FROM iris WHERE Name=? AND SepalLength=?", + 'mysql': 'SELECT * FROM iris WHERE `Name`="%s" AND `SepalLength`=%s', + 'postgresql': 'SELECT * FROM iris WHERE "Name"=%s AND "SepalLength"=%s' + }, + 'read_named_parameters': { + 'sqlite': """ + SELECT * FROM iris WHERE Name=:name AND SepalLength=:length + """, + 'mysql': """ + SELECT * FROM iris WHERE + `Name`="%(name)s" AND `SepalLength`=%(length)s + """, + 'postgresql': """ + SELECT * FROM iris WHERE + "Name"=%(name)s AND "SepalLength"=%(length)s + """ } } @@ -168,6 +186,18 @@ def _read_sql_iris(self): iris_frame = self.pandasSQL.read_sql("SELECT * FROM iris") self._check_iris_loaded_frame(iris_frame) + def _read_sql_iris_parameter(self): + query = SQL_STRINGS['read_parameters'][self.flavor] + params = ['Iris-setosa', 5.1] + iris_frame = self.pandasSQL.read_sql(query, params=params) + self._check_iris_loaded_frame(iris_frame) + + def _read_sql_iris_named_parameter(self): + query = SQL_STRINGS['read_named_parameters'][self.flavor] + params = {'name': 'Iris-setosa', 'length': 5.1} + iris_frame = self.pandasSQL.read_sql(query, params=params) + self._check_iris_loaded_frame(iris_frame) + def _to_sql(self): self.drop_table('test_frame1') @@ -491,6 +521,12 @@ class _TestSQLAlchemy(PandasSQLTest): def test_read_sql(self): self._read_sql_iris() + def test_read_sql_parameter(self): + self._read_sql_iris_parameter() + + def test_read_sql_named_parameter(self): + self._read_sql_iris_named_parameter() + def test_to_sql(self): self._to_sql() @@ -703,6 +739,12 @@ def test_invalid_flavor(self): def test_read_sql(self): self._read_sql_iris() + def test_read_sql_parameter(self): + self._read_sql_iris_parameter() + + def test_read_sql_named_parameter(self): + self._read_sql_iris_named_parameter() + def test_to_sql(self): self._to_sql()