@@ -4398,3 +4398,185 @@ def test_xsqlite_if_exists(sqlite_buildin):
43984398 (5 , "E" ),
43994399 ]
44004400 drop_table (table_name , sqlite_buildin )
4401+
4402+
4403+ # -----------------------------------------------------------------------------
4404+ # -- Testing SQL Hints
4405+
4406+
4407+ class TestProcessSQLHints :
4408+ """Tests for _process_sql_hints helper function."""
4409+
4410+ def test_process_sql_hints_oracle_list (self ):
4411+ """Test hint processing with Oracle dialect and list input."""
4412+ hints = {"oracle" : ["APPEND" , "PARALLEL" ]}
4413+ result = sql ._process_sql_hints (hints , "oracle" )
4414+ assert result == "/*+ APPEND PARALLEL */"
4415+
4416+ def test_process_sql_hints_oracle_string (self ):
4417+ """Test hint processing with Oracle dialect and string input."""
4418+ hints = {"oracle" : "APPEND PARALLEL" }
4419+ result = sql ._process_sql_hints (hints , "oracle" )
4420+ assert result == "/*+ APPEND PARALLEL */"
4421+
4422+ def test_process_sql_hints_preformatted (self ):
4423+ """Test that pre-formatted hints are returned as-is."""
4424+ hints = {"oracle" : "/*+ APPEND PARALLEL */" }
4425+ result = sql ._process_sql_hints (hints , "oracle" )
4426+ assert result == "/*+ APPEND PARALLEL */"
4427+
4428+ def test_process_sql_hints_case_insensitive (self ):
4429+ """Test that dialect names are case-insensitive."""
4430+ hints = {"ORACLE" : ["APPEND" ]}
4431+ result = sql ._process_sql_hints (hints , "oracle" )
4432+ assert result == "/*+ APPEND */"
4433+
4434+ hints = {"oracle" : ["APPEND" ]}
4435+ result = sql ._process_sql_hints (hints , "ORACLE" )
4436+ assert result == "/*+ APPEND */"
4437+
4438+ def test_process_sql_hints_no_match (self ):
4439+ """Test that None is returned when dialect doesn't match."""
4440+ hints = {"mysql" : "HIGH_PRIORITY" }
4441+ result = sql ._process_sql_hints (hints , "oracle" )
4442+ assert result is None
4443+
4444+ def test_process_sql_hints_none (self ):
4445+ """Test that None input returns None."""
4446+ result = sql ._process_sql_hints (None , "oracle" )
4447+ assert result is None
4448+
4449+ def test_process_sql_hints_empty_dict (self ):
4450+ """Test that empty dict returns None."""
4451+ result = sql ._process_sql_hints ({}, "oracle" )
4452+ assert result is None
4453+
4454+ def test_process_sql_hints_mysql (self ):
4455+ """Test hint processing for MySQL dialect."""
4456+ hints = {"mysql" : "HIGH_PRIORITY" }
4457+ result = sql ._process_sql_hints (hints , "mysql" )
4458+ assert result == "HIGH_PRIORITY"
4459+
4460+ def test_process_sql_hints_mssql (self ):
4461+ """Test hint processing for SQL Server dialect."""
4462+ hints = {"mssql" : "TABLOCK" }
4463+ result = sql ._process_sql_hints (hints , "mssql" )
4464+ assert result == "TABLOCK"
4465+
4466+
4467+ @pytest .mark .parametrize ("conn" , sqlalchemy_connectable )
4468+ def test_to_sql_with_hints_parameter (conn , test_frame1 , request ):
4469+ """Test that to_sql accepts hints parameter without error."""
4470+ conn = request .getfixturevalue (conn )
4471+
4472+ with pandasSQL_builder (conn , need_transaction = True ) as pandasSQL :
4473+ pandasSQL .to_sql (
4474+ test_frame1 , "test_hints" , hints = {"oracle" : ["APPEND" ]}, if_exists = "replace"
4475+ )
4476+ assert pandasSQL .has_table ("test_hints" )
4477+ assert count_rows (conn , "test_hints" ) == len (test_frame1 )
4478+
4479+
4480+ @pytest .mark .parametrize ("conn" , sqlalchemy_connectable )
4481+ def test_to_sql_hints_none_default (conn , test_frame1 , request ):
4482+ """Test that hints defaults to None and doesn't break existing code."""
4483+ conn = request .getfixturevalue (conn )
4484+
4485+ with pandasSQL_builder (conn , need_transaction = True ) as pandasSQL :
4486+ pandasSQL .to_sql (test_frame1 , "test_no_hints" )
4487+ assert pandasSQL .has_table ("test_no_hints" )
4488+ assert count_rows (conn , "test_no_hints" ) == len (test_frame1 )
4489+
4490+
4491+ @pytest .mark .parametrize ("conn" , sqlalchemy_connectable )
4492+ def test_to_sql_hints_with_method (conn , test_frame1 , request ):
4493+ """Test that hints work alongside custom method parameter."""
4494+ conn = request .getfixturevalue (conn )
4495+
4496+ check = []
4497+
4498+ def sample (pd_table , conn , keys , data_iter ):
4499+ check .append (1 )
4500+ data = [dict (zip (keys , row )) for row in data_iter ]
4501+ conn .execute (pd_table .table .insert (), data )
4502+
4503+ with pandasSQL_builder (conn , need_transaction = True ) as pandasSQL :
4504+ pandasSQL .to_sql (
4505+ test_frame1 ,
4506+ "test_hints_method" ,
4507+ method = sample ,
4508+ hints = {"oracle" : ["APPEND" ]},
4509+ )
4510+ assert pandasSQL .has_table ("test_hints_method" )
4511+
4512+ assert check == [1 ]
4513+ assert count_rows (conn , "test_hints_method" ) == len (test_frame1 )
4514+
4515+
4516+ @pytest .mark .parametrize ("conn" , sqlalchemy_connectable )
4517+ @pytest .mark .parametrize ("method" , [None , "multi" ])
4518+ def test_to_sql_hints_with_different_methods (conn , method , test_frame1 , request ):
4519+ """Test hints work with different insertion methods."""
4520+ conn = request .getfixturevalue (conn )
4521+
4522+ with pandasSQL_builder (conn , need_transaction = True ) as pandasSQL :
4523+ pandasSQL .to_sql (
4524+ test_frame1 ,
4525+ "test_hints_methods" ,
4526+ method = method ,
4527+ hints = {"oracle" : ["APPEND" , "PARALLEL" ]},
4528+ if_exists = "replace" ,
4529+ )
4530+ assert pandasSQL .has_table ("test_hints_methods" )
4531+
4532+ assert count_rows (conn , "test_hints_methods" ) == len (test_frame1 )
4533+
4534+
4535+ @pytest .mark .parametrize ("conn" , sqlalchemy_connectable )
4536+ def test_to_sql_hints_multidb_dict (conn , test_frame1 , request ):
4537+ """Test that multi-database hints dict works (only matching dialect used)."""
4538+ conn = request .getfixturevalue (conn )
4539+
4540+ hints = {
4541+ "oracle" : ["APPEND" , "PARALLEL" ],
4542+ "mysql" : "HIGH_PRIORITY" ,
4543+ "postgresql" : "some_pg_hint" ,
4544+ "sqlite" : "ignored" ,
4545+ }
4546+
4547+ with pandasSQL_builder (conn , need_transaction = True ) as pandasSQL :
4548+ pandasSQL .to_sql (
4549+ test_frame1 , "test_multidb_hints" , hints = hints , if_exists = "replace"
4550+ )
4551+ assert pandasSQL .has_table ("test_multidb_hints" )
4552+
4553+ assert count_rows (conn , "test_multidb_hints" ) == len (test_frame1 )
4554+
4555+
4556+ def test_to_sql_hints_adbc_not_supported (sqlite_adbc_conn , test_frame1 ):
4557+ """Test that ADBC connections raise NotImplementedError for hints."""
4558+ pytest .importorskip ("adbc_driver_manager.dbapi" )
4559+
4560+ df = test_frame1 .copy ()
4561+ msg = "'hints' is not implemented for ADBC drivers"
4562+
4563+ with pytest .raises (NotImplementedError , match = msg ):
4564+ df .to_sql ("test" , sqlite_adbc_conn , hints = {"oracle" : ["APPEND" ]})
4565+
4566+
4567+ def test_to_sql_hints_sqlite_builtin (sqlite_buildin , test_frame1 ):
4568+ """Test that sqlite builtin connection handles hints gracefully."""
4569+ df = test_frame1 .copy ()
4570+
4571+ msg = "SQL hints are not supported for SQLite and will be ignored."
4572+ with tm .assert_produces_warning (UserWarning , match = msg ):
4573+ result = df .to_sql (
4574+ "test_sqlite_hints" ,
4575+ sqlite_buildin ,
4576+ if_exists = "replace" ,
4577+ hints = {"sqlite" : "IGNORED" },
4578+ )
4579+
4580+ assert result == len (test_frame1 )
4581+ result_df = pd .read_sql ("SELECT * FROM test_sqlite_hints" , sqlite_buildin )
4582+ assert len (result_df ) == len (test_frame1 )
0 commit comments