BUG: read_sql wraps plain SQL strings in sqlalchemy.text() to allow % modulo and LIKE patterns (#35484)
The issue is coming from #35484
What does this PR do? Fixes a long-standing issue where pd.read_sql fails to handle the percent character (%) when using SQLAlchemy engines. Since pandas ≥ 1.1, the IO layer sets no_parameters=True for SQL execution, which causes plain string queries containing % to be misinterpreted as parameter placeholders.
As a result: Queries like SELECT 1 % 2 raised UndefinedFunction errors. Queries using LIKE 'Jo%' failed to match rows correctly. This PR introduces a small helper _sa_text_if_string that wraps plain SQL strings in sqlalchemy.text() before execution. This ensures that % is treated correctly, while SQLAlchemy select() objects continue to work as before.
Summary of changes pandas/io/sql.py Added _sa_text_if_string(stmt) helper. Applied it inside SQLDatabase.read_query() before self.execute(). pandas/tests/io/sql/test_percent_patterns.py
Added new tests for: SELECT 5 % 2 (modulo operator) LIKE 'John%' (pattern matching) SQLAlchemy selectable using (literal(7) % literal(3))
Other Restored .gitignore to match upstream so only relevant files are modified.
How was this tested? All pre-commit checks (ruff, codespell, etc.) pass locally. pytest -q pandas/tests/io/sql/test_percent_patterns.py -ra → 3 tests passed. Tested both on in-memory SQLite and PostgreSQL (PANDAS_TEST_POSTGRES_URI).
Notes for reviewers The change only affects plain string queries. SQLAlchemy expressions (select(), text(), etc.) remain unaffected. A short “Bug Fixes” note will be added to doc/source/whatsnew/v3.x.y.rst.
@SergioGarcia00 Can you update the description linking to the relevant issue?
@SergioGarcia00 Can you update the description linking to the relevant issue?
Oh, missed that sry. #35484
I dont really understand the errors I got by the CI, is there something I did wrong?
I really dont get it, What am I doing wrong?
pre-commit.ci autofix
It looks like the failing job is due to an artifact/cache conflict (Failed to CreateArtifact: 409 Conflict), not the code itself. Could someone with permissions re-run the “Without PyArrow” job?