-
-
Notifications
You must be signed in to change notification settings - Fork 18k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Implement option 'delete_rows' of argument 'if_exists' in 'DataFrame.to_sql' API. #60376
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1068,7 +1068,9 @@ def test_to_sql(conn, method, test_frame1, request): | |
|
||
|
||
@pytest.mark.parametrize("conn", all_connectable) | ||
@pytest.mark.parametrize("mode, num_row_coef", [("replace", 1), ("append", 2)]) | ||
@pytest.mark.parametrize( | ||
"mode, num_row_coef", [("replace", 1), ("append", 2), ("delete_rows", 1)] | ||
) | ||
def test_to_sql_exist(conn, mode, num_row_coef, test_frame1, request): | ||
conn = request.getfixturevalue(conn) | ||
with pandasSQL_builder(conn, need_transaction=True) as pandasSQL: | ||
|
@@ -2698,6 +2700,64 @@ def test_drop_table(conn, request): | |
assert not insp.has_table("temp_frame") | ||
|
||
|
||
@pytest.mark.parametrize("conn", all_connectable) | ||
def test_delete_rows_success(conn, test_frame1, request): | ||
table_name = "temp_frame" | ||
conn = request.getfixturevalue(conn) | ||
pandasSQL = pandasSQL_builder(conn) | ||
|
||
with pandasSQL.run_transaction(): | ||
assert pandasSQL.to_sql(test_frame1, table_name) == test_frame1.shape[0] | ||
|
||
with pandasSQL.run_transaction(): | ||
assert pandasSQL.delete_rows(table_name) is None | ||
|
||
assert count_rows(conn, table_name) == 0 | ||
assert pandasSQL.has_table("temp_frame") | ||
|
||
|
||
@pytest.mark.parametrize("conn", all_connectable) | ||
def test_delete_rows_is_atomic(conn, request): | ||
import adbc_driver_manager | ||
import sqlalchemy | ||
|
||
if "sqlite" in conn: | ||
reason = "This test relies on strict column types, SQLite has a dynamic one" | ||
request.applymarker( | ||
pytest.mark.xfail( | ||
reason=reason, | ||
strict=True, | ||
) | ||
) | ||
|
||
table_name = "temp_frame" | ||
original_df = DataFrame({"a": [1, 2, 3]}) | ||
replacing_df = DataFrame({"a": ["a", "b", "c", "d"]}) | ||
|
||
conn = request.getfixturevalue(conn) | ||
pandasSQL = pandasSQL_builder(conn) | ||
|
||
if isinstance(conn, adbc_driver_manager.dbapi.Connection): | ||
expected_exception = adbc_driver_manager.ProgrammingError | ||
else: | ||
expected_exception = sqlalchemy.exc.DataError | ||
|
||
class Foo(Exception): ... | ||
|
||
with pandasSQL.run_transaction(): | ||
pandasSQL.to_sql(original_df, table_name, if_exists="fail", index=False) | ||
|
||
with pytest.raises(expected_exception): | ||
with pandasSQL.run_transaction(): | ||
pandasSQL.to_sql( | ||
replacing_df, table_name, if_exists="delete_rows", index=False | ||
) | ||
|
||
with pandasSQL.run_transaction(): | ||
result_df = pandasSQL.read_query(f"SELECT * FROM {table_name}") | ||
tm.assert_frame_equal(result_df, original_df) | ||
|
||
|
||
@pytest.mark.parametrize("conn", all_connectable) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a test that ensures that this is atomic? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, the location of this comment got me confused. You mean testing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea; I think can add a new test (unless you see a good one that can be parametrized) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll need some help here. I failed to get the transaction in which There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think you need to inspect the transaction; that's probably a few layers removed from where we are operating. I think you can just write some data, change some integral columns to string, and try to rewrite the same data. I think this should fail, and afterwards you can inspect the table to make sure the data that was successfully written remains There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I was surprised by this behavior but learned it is a feature. It is possible to use the
|
||
def test_roundtrip(conn, request, test_frame1): | ||
if conn == "sqlite_str": | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add an assertion that the table does not actually get dropped?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For sure, good catch !