mirror of
https://github.com/apache/superset.git
synced 2026-09-05 06:51:48 +00:00
Root cause of the "mypass%25123" != "mypass%123" and "p%40ss%21word" != "p@ss!word" round-trip failures in test_database_password_encoding.py: Database.sqlalchemy_uri_decrypted manually percent-encoded the password with urllib.parse.quote() before handing it to URL.render_as_string(hide_password=False). Under SQLAlchemy 1.4, render_as_string() rendered URL.password as a literal value, so the manual pre-encoding was necessary. Under SQLAlchemy 2.0, render_as_string() always percent-encodes the password itself - the pre-encoded value gets encoded a second time (a literal "%" becomes "%25", which decodes back to "%25" instead of "%" on the next parse). Passing the raw password straight through and letting render_as_string() do the (now single) encoding fixes the round-trip. Confirmed the double-encoding mechanism directly against this branch's sqlalchemy==2.0.51: URL.set(password=<pre-encoded>).render_as_string() produces "mypass%2525123"; URL.set(password=<raw>).render_as_string() correctly produces "mypass%25123". Separately, model_tests.py::test_impersonate_user_trino (and the mysqlclient-only test_adjust_engine_params_mysql, exercised on real CI runners but skipped here where mysqlclient isn't importable) asserted on str(url) for URLs containing a password. SQLAlchemy 2.0 also changed URL.__str__() to hide the password by default (a deliberate hardening change - 1.4 rendered it in full); switched those assertions to render_as_string(hide_password=False) to compare against the real, unmasked URL the engine was actually constructed with, rather than relaxing what's being verified. Verified locally (sqlite): test_database_password_encoding.py (5/5) and model_tests.py (21 passed, 5 skipped - the mysqlclient-gated ones) both pass clean.