From 8d449c50bd8a43c5472d6e0556b41402fac52ba9 Mon Sep 17 00:00:00 2001 From: Nicolas Bonnotte Date: Wed, 24 Feb 2016 08:31:00 +0100 Subject: [PATCH] BUG: to_sql with datetime.time values with sqlite, #8341 closes #8341 --- doc/source/whatsnew/v0.18.0.txt | 2 +- pandas/io/sql.py | 11 ++++++++++- pandas/io/tests/test_sql.py | 26 ++++++++++++++++++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/doc/source/whatsnew/v0.18.0.txt b/doc/source/whatsnew/v0.18.0.txt index b3bbc5cf5ef8c..f71421a2f43a6 100644 --- a/doc/source/whatsnew/v0.18.0.txt +++ b/doc/source/whatsnew/v0.18.0.txt @@ -1094,7 +1094,7 @@ Bug Fixes - Bug in ``.plot`` potentially modifying the ``colors`` input when the number of columns didn't match the number of series provided (:issue:`12039`). - Bug in ``Series.plot`` failing when index has a ``CustomBusinessDay`` frequency (:issue:`7222`). - +- Bug in ``.to_sql`` for ``datetime.time`` values with sqlite fallback (:issue:`8341`) - Bug in ``read_excel`` failing to read data with one column when ``squeeze=True`` (:issue:`12157`) - Bug in ``.groupby`` where a ``KeyError`` was not raised for a wrong column if there was only one row in the dataframe (:issue:`11741`) - Bug in ``.read_csv`` with dtype specified on empty data producing an error (:issue:`12048`) diff --git a/pandas/io/sql.py b/pandas/io/sql.py index c29286016a34f..addc88bebebe1 100644 --- a/pandas/io/sql.py +++ b/pandas/io/sql.py @@ -5,7 +5,7 @@ """ from __future__ import print_function, division -from datetime import datetime, date +from datetime import datetime, date, time import warnings import traceback @@ -1403,6 +1403,15 @@ class SQLiteTable(SQLTable): Instead of a table variable just use the Create Table statement. """ + def __init__(self, *args, **kwargs): + # GH 8341 + # register an adapter callable for datetime.time object + import sqlite3 + # this will transform time(12,34,56,789) into '12:34:56.000789' + # (this is what sqlalchemy does) + sqlite3.register_adapter(time, lambda _: _.strftime("%H:%M:%S.%f")) + super(SQLiteTable, self).__init__(*args, **kwargs) + def sql_schema(self): return str(";\n".join(self.table)) diff --git a/pandas/io/tests/test_sql.py b/pandas/io/tests/test_sql.py index c2d6f5af48388..b72258cbf588d 100644 --- a/pandas/io/tests/test_sql.py +++ b/pandas/io/tests/test_sql.py @@ -1429,6 +1429,22 @@ def test_datetime_time(self): res = read_sql_table('test_time', self.conn) tm.assert_frame_equal(res, df) + # GH8341 + # first, use the fallback to have the sqlite adapter put in place + sqlite_conn = TestSQLiteFallback.connect() + sql.to_sql(df, "test_time2", sqlite_conn, index=False) + res = sql.read_sql_query("SELECT * FROM test_time2", sqlite_conn) + ref = df.applymap(lambda _: _.strftime("%H:%M:%S.%f")) + tm.assert_frame_equal(ref, res) # check if adapter is in place + # then test if sqlalchemy is unaffected by the sqlite adapter + sql.to_sql(df, "test_time3", self.conn, index=False) + if self.flavor == 'sqlite': + res = sql.read_sql_query("SELECT * FROM test_time3", self.conn) + ref = df.applymap(lambda _: _.strftime("%H:%M:%S.%f")) + tm.assert_frame_equal(ref, res) + res = sql.read_sql_table("test_time3", self.conn) + tm.assert_frame_equal(df, res) + def test_mixed_dtype_insert(self): # see GH6509 s1 = Series(2**25 + 1, dtype=np.int32) @@ -1957,12 +1973,14 @@ def test_datetime_date(self): tm.assert_frame_equal(res, df) def test_datetime_time(self): - # test support for datetime.time + # test support for datetime.time, GH #8341 df = DataFrame([time(9, 0, 0), time(9, 1, 30)], columns=["a"]) - # test it raises an error and not fails silently (GH8341) + df.to_sql('test_time', self.conn, index=False, flavor=self.flavor) + res = read_sql_query('SELECT * FROM test_time', self.conn) if self.flavor == 'sqlite': - self.assertRaises(sqlite3.InterfaceError, sql.to_sql, df, - 'test_time', self.conn) + # comes back as strings + expected = df.applymap(lambda _: _.strftime("%H:%M:%S.%f")) + tm.assert_frame_equal(res, expected) def _get_index_columns(self, tbl_name): ixs = sql.read_sql_query(