Skip to content

create_database() fails with SQLAlchemy 2.x when passed engine.url (URL object) #801

@n-twena

Description

@n-twena

Description

sqlalchemy_utils.functions.create_database() does not correctly handle SQLAlchemy 2.x URL objects when passed as engine.url.

The documentation and examples explicitly show this usage pattern:

engine = create_engine('postgresql://postgres@localhost/name')

database_exists(engine.url)  # => False
create_database(engine.url)
database_exists(engine.url)  # => True

Source:

create_database(engine.url)

However, with SQLAlchemy 2.x, passing engine.url (a structured URL object) causes create_database() to fail for certain backends (e.g. MySQL + PyMySQL), typically resulting in authentication or connection errors.

The same call works when a string URL is passed instead.

Actual Behavior

Calling create_database(engine.url) fails under SQLAlchemy 2.x with backend-specific errors (e.g. access denied, database creation failure).

sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (1045, "Access denied for user 'user'@'localhost' (using password: YES)")
(Background on this error at: https://sqlalche.me/e/20/e3q8)

Steps to reproduce

from sqlalchemy import create_engine
from sqlalchemy_utils import create_database

engine = create_engine("mysql+pymysql://user:xxxx@localhost:3306/dbname")

# Fails under SQLAlchemy 2.x
create_database(engine.url)

# Works
create_database("mysql+pymysql://user:xxxx@localhost:3306/dbname")

# Also works
create_database(engine.url.render_as_string(hide_password=False))

Root Cause

SQLAlchemy 2.0 intentionally changed the semantics of URL objects:

  • engine.url is now a structured, immutable object
  • Sensitive fields (e.g. passwords) are not preserved unless explicitly rendered

create_database() internally rebuilds an engine based on the provided URL and assumes round-trip equivalence, which is no longer valid in SQLAlchemy 2.x. When a URL object is passed directly, credentials or parameters may be dropped or altered.

Environment

sqlalchemy-utils: 0.42.1
SQLAlchemy: 2.0.44
PyMySQL: 1.1.2
Python: 3.12
Backend example: MySQL + PyMySQL

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions