Skip to content

Commit 0d96237

Browse files
Backport PR #47474 on branch 1.4.x (BUG: to_sql with method=callable not returning int raising TypeError) (#47590)
Backport PR #47474: BUG: to_sql with method=callable not returning int raising TypeError Co-authored-by: Matthew Roeschke <[email protected]>
1 parent 6e1cf43 commit 0d96237

File tree

4 files changed

+22
-10
lines changed

4 files changed

+22
-10
lines changed

doc/source/whatsnew/v1.4.4.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Fixed regressions
2424
Bug fixes
2525
~~~~~~~~~
2626
- The :class:`errors.FutureWarning` raised when passing arguments (other than ``filepath_or_buffer``) as positional in :func:`read_csv` is now raised at the correct stacklevel (:issue:`47385`)
27-
-
27+
- Bug in :meth:`DataFrame.to_sql` when ``method`` was a ``callable`` that did not return an ``int`` and would raise a ``TypeError`` (:issue:`46891`)
2828

2929
.. ---------------------------------------------------------------------------
3030

pandas/core/generic.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2847,7 +2847,7 @@ def to_sql(
28472847
-------
28482848
None or int
28492849
Number of rows affected by to_sql. None is returned if the callable
2850-
passed into ``method`` does not return the number of rows.
2850+
passed into ``method`` does not return an integer number of rows.
28512851
28522852
The number of returned rows affected is the sum of the ``rowcount``
28532853
attribute of ``sqlite3.Cursor`` or SQLAlchemy connectable which may not

pandas/io/sql.py

+9-6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from pandas.core.dtypes.common import (
3434
is_datetime64tz_dtype,
3535
is_dict_like,
36+
is_integer,
3637
is_list_like,
3738
)
3839
from pandas.core.dtypes.dtypes import DatetimeTZDtype
@@ -670,7 +671,7 @@ def to_sql(
670671
-------
671672
None or int
672673
Number of rows affected by to_sql. None is returned if the callable
673-
passed into ``method`` does not return the number of rows.
674+
passed into ``method`` does not return an integer number of rows.
674675
675676
.. versionadded:: 1.4.0
676677
@@ -938,7 +939,7 @@ def insert(
938939
raise ValueError("chunksize argument should be non-zero")
939940

940941
chunks = (nrows // chunksize) + 1
941-
total_inserted = 0
942+
total_inserted = None
942943
with self.pd_sql.run_transaction() as conn:
943944
for i in range(chunks):
944945
start_i = i * chunksize
@@ -948,10 +949,12 @@ def insert(
948949

949950
chunk_iter = zip(*(arr[start_i:end_i] for arr in data_list))
950951
num_inserted = exec_insert(conn, keys, chunk_iter)
951-
if num_inserted is None:
952-
total_inserted = None
953-
else:
954-
total_inserted += num_inserted
952+
# GH 46891
953+
if is_integer(num_inserted):
954+
if total_inserted is None:
955+
total_inserted = num_inserted
956+
else:
957+
total_inserted += num_inserted
955958
return total_inserted
956959

957960
def _query_iterator(

pandas/tests/io/test_sql.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,8 @@ def test_read_procedure(conn, request):
621621

622622
@pytest.mark.db
623623
@pytest.mark.parametrize("conn", postgresql_connectable)
624-
def test_copy_from_callable_insertion_method(conn, request):
624+
@pytest.mark.parametrize("expected_count", [2, "Success!"])
625+
def test_copy_from_callable_insertion_method(conn, expected_count, request):
625626
# GH 8953
626627
# Example in io.rst found under _io.sql.method
627628
# not available in sqlite, mysql
@@ -642,10 +643,18 @@ def psql_insert_copy(table, conn, keys, data_iter):
642643

643644
sql_query = f"COPY {table_name} ({columns}) FROM STDIN WITH CSV"
644645
cur.copy_expert(sql=sql_query, file=s_buf)
646+
return expected_count
645647

646648
conn = request.getfixturevalue(conn)
647649
expected = DataFrame({"col1": [1, 2], "col2": [0.1, 0.2], "col3": ["a", "n"]})
648-
expected.to_sql("test_frame", conn, index=False, method=psql_insert_copy)
650+
result_count = expected.to_sql(
651+
"test_frame", conn, index=False, method=psql_insert_copy
652+
)
653+
# GH 46891
654+
if not isinstance(expected_count, int):
655+
assert result_count is None
656+
else:
657+
assert result_count == expected_count
649658
result = sql.read_sql_table("test_frame", conn)
650659
tm.assert_frame_equal(result, expected)
651660

0 commit comments

Comments
 (0)