launchbadge / sqlx

🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, and SQLite.
Apache License 2.0
13.52k stars 1.28k forks source link

fix: URL decode database name when parsing connection url #3593

Open BenoitRanque opened 2 weeks ago

BenoitRanque commented 2 weeks ago

The problem

Connecting to databases with non-standard characters in the db name fails.

    let connection_uri = "postgres://postgres:postgres@localhost:5432/test db";

    let mut connection = PgConnection::connect(connection_uri).await?;

    let results = sqlx::query("SELECT * FROM test")
        .fetch_all(&mut connection)
        .await?;

Fails with database "test%20db" does not exist

Creating a connection configuration, and then setting the db name explicitly, fixes the issue:

    let connection_uri = "postgres://postgres:postgres@localhost:5432/test db";

    let connect_options = PgConnectOptions::from_url(&connection_uri.parse()?)?;
    let connect_options = connect_options.database("test db");

    let mut connection = PgConnection::connect_with(&connect_options).await?;

    let results = sqlx::query("SELECT * FROM test")
        .fetch_all(&mut connection)
        .await?;

Additionally, setting the db name with a query parameter also fixes the problem:

    let connection_uri = "postgres://postgres:postgres@localhost:5432?dbname=test db";

Background

When parsing a connection string, the various parts are url decoded before being stored in the connection configuration struct.

The one exception is the database name.

When we get the name from the dbname query param, it seems the values are decoded.

When building back a URL, it seems we rely on the behavior of the set_path path method which will encode the string if needed, (if we passed the name using the dbname query param), or leave it as is if already percent encoded (if we passed it using the url path)

However, it looks like the establish method does not handle getting a url encoded database name, and sends it as-is. Hence our problem.

Fix

URL decode the db name when extracted from the path of a connection string