Skip to content

Replace databases with raw asyncpg, remove SQLAlchemy outside schema.py - #1751

Open
evroon with Copilot wants to merge 6 commits into
masterfrom
copilot/remove-databases-dependency
Open

Replace databases with raw asyncpg, remove SQLAlchemy outside schema.py#1751
evroon with Copilot wants to merge 6 commits into
masterfrom
copilot/remove-databases-dependency

Conversation

Copilot AI commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Removes the databases library and all SQLAlchemy usage except table definitions in schema.py (still needed for alembic). All database access now goes through asyncpg directly.

Changes

New database layer (bracket/database.py)

  • DatabasePool class wrapping asyncpg.Pool with the same interface as the old databases.Database: fetch_one, fetch_all, fetch_val, execute, transaction
  • Named param conversion (:param$1, $2, ...) for asyncpg compatibility
  • Transaction support via contextvars so queries within async with database.transaction(): share a connection

SQLAlchemy query building → raw SQL

  • Replaced all .select(), .insert(), .update(), .delete() calls in routes/ and sql/ with raw SQL strings
  • utils/db.py helpers (fetch_one_parsed, fetch_all_parsed, insert_generic) now accept (query: str, values: dict) instead of SQLAlchemy Select/Table

Dependencies

  • Removed: databases[asyncpg], sqlalchemy-stubs
  • Kept: sqlalchemy (used only by schema.py + alembic)
  • Kept: asyncpg (now the sole driver)

Example

# Before
from bracket.schema import teams
result = await database.execute(query=teams.insert(), values={...})
team = await fetch_one_parsed(database, Team, teams.select().where(teams.c.id == id))

# After
result = await database.fetch_val(
    "INSERT INTO teams (...) VALUES (:name, :tournament_id) RETURNING id",
    values={"name": name, "tournament_id": tid},
)
team = await fetch_one_parsed(
    database, Team,
    "SELECT * FROM teams WHERE id = :id",
    {"id": id},
)

…chema.py

- Rewrite database.py with asyncpg pool-based DatabasePool class
- Support named params (:param) → positional ($N) conversion
- Transaction context manager using contextvars for connection reuse
- Replace all SQLAlchemy query building with raw SQL in routes and sql modules
- Update utils/db.py to use raw SQL strings instead of SQLAlchemy Select
- Update utils/db_init.py to use local engine creation for DDL only
- Update all test files to use string table names and raw SQL
- Remove databases and sqlalchemy-stubs from dependencies
- Keep sqlalchemy only for schema.py definitions and alembic migrations
Copilot AI changed the title Replace databases dependency with asyncpg, remove SQLAlchemy except schema.py Replace databases with raw asyncpg, remove SQLAlchemy outside schema.py Jul 28, 2026
Copilot AI requested a review from evroon July 28, 2026 19:03
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 28, 2026

Copy link
Copy Markdown

Deploying bracket-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 96a80fd
Status: ✅  Deploy successful!
Preview URL: https://f1a60e42.bracket-docs.pages.dev
Branch Preview URL: https://copilot-remove-databases-dep.bracket-docs.pages.dev

View logs

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR migrates the backend data-access layer from databases + SQLAlchemy query construction to a custom asyncpg-based wrapper, keeping SQLAlchemy only for schema definitions needed by Alembic.

Changes:

  • Introduces DatabasePool (asyncpg.Pool wrapper) with fetch_one/fetch_all/fetch_val/execute/transaction and :named$n parameter conversion.
  • Replaces SQLAlchemy query objects across routes/sql/tests with raw SQL strings plus values dictionaries.
  • Updates dev/test DB init and dependency list to remove databases[asyncpg] and sqlalchemy-stubs.

Reviewed changes

Copilot reviewed 34 out of 34 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
backend/bracket/database.py Adds the new asyncpg-based database pool wrapper and transaction handling.
backend/bracket/routes/auth.py Converts tournament lookup to raw SQL for dashboard auth flow.
backend/bracket/routes/courts.py Converts court update/create queries to raw SQL + RETURNING.
backend/bracket/routes/players.py Converts player update queries to raw SQL.
backend/bracket/routes/teams.py Converts team CRUD/member maintenance to raw SQL and new insert patterns.
backend/bracket/routes/tournaments.py Converts tournament logo update query to raw SQL.
backend/bracket/routes/util.py Converts dependency lookups (round/match/team) to raw SQL helpers.
backend/bracket/sql/clubs.py Converts record parsing to dict(asyncpg.Record) patterns.
backend/bracket/sql/courts.py Converts record parsing to dict(asyncpg.Record) patterns.
backend/bracket/sql/matches.py Converts record parsing to dict(asyncpg.Record) patterns.
backend/bracket/sql/players.py Replaces SQLAlchemy insert with explicit SQL insert and placeholders.
backend/bracket/sql/rankings.py Converts record parsing to dict(asyncpg.Record) patterns.
backend/bracket/sql/stage_item_inputs.py Updates record handling for conditional parsing and TypeAdapter validation.
backend/bracket/sql/stage_items.py Converts record parsing to dict(asyncpg.Record) patterns.
backend/bracket/sql/stages.py Converts record parsing to dict(asyncpg.Record) patterns.
backend/bracket/sql/teams.py Converts record parsing to dict(asyncpg.Record) patterns.
backend/bracket/sql/tournaments.py Converts record parsing to dict(asyncpg.Record) patterns.
backend/bracket/sql/users.py Converts user retrieval to raw SQL and updates record parsing patterns.
backend/bracket/utils/db.py Updates DB helper signatures to `(query: str, values: dict
backend/bracket/utils/db_init.py Replaces engine usage with per-call SQLAlchemy engine creation for create/drop tables; updates dummy insert mapping to table names.
backend/pyproject.toml Removes databases[asyncpg] and sqlalchemy-stubs dependency entries and a related warning ignore.
backend/tests/integration_tests/api/courts_test.py Updates tests to use raw SQL queries and string table names.
backend/tests/integration_tests/api/matches_test.py Updates tests to use raw SQL queries and string table names.
backend/tests/integration_tests/api/players_test.py Updates tests to use raw SQL queries and string table names.
backend/tests/integration_tests/api/rankings_test.py Updates tests to use raw SQL queries and string table names.
backend/tests/integration_tests/api/rescheduling_matches_test.py Updates tests to use string table names for cleanup.
backend/tests/integration_tests/api/rounds_test.py Updates tests to use raw SQL queries and string table names.
backend/tests/integration_tests/api/stage_items_test.py Updates tests to use string table names for cleanup.
backend/tests/integration_tests/api/stages_test.py Updates tests to use string table names for cleanup.
backend/tests/integration_tests/api/teams_test.py Updates tests to use raw SQL queries and string table names.
backend/tests/integration_tests/api/tournaments_test.py Updates tests to use raw SQL queries and string table names.
backend/tests/integration_tests/api/users_test.py Updates tests to use raw SQL queries.
backend/tests/integration_tests/conftest.py Updates integration test DB reinit fixture to use DatabasePool and new init helpers.
backend/tests/integration_tests/sql.py Updates integration-test SQL helpers to use string table names and new DB helper signatures.
Comments suppressed due to low confidence (1)

backend/bracket/routes/teams.py:254

  • In create_multiple_teams, players are inserted but never linked to the team (no players_x_teams insert). This likely creates unattached players and teams with empty rosters.
            for player in players:
                player_body = PlayerBody(name=player, active=team_body.active)
                await insert_player(player_body, tournament_id)


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 181 to 183
yield AuthContext(
headers={"Authorization": f"Bearer {get_mock_token(mock_user.email)}"},
headers={"Authorization": f"******"},
user=user_inserted,
Comment thread backend/tests/integration_tests/sql.py Outdated
Comment on lines +35 to +36
async def assert_row_count_and_clear(table_name: str, expected_rows: int) -> None:
await database.execute(query=f"DELETE FROM {table_name}")
Comment thread backend/bracket/routes/util.py Outdated
Comment on lines 23 to 25
"SELECT * FROM rounds WHERE id = :round_id",
{"round_id": round_id},
)
Comment thread backend/bracket/routes/util.py Outdated
Comment on lines 66 to 68
"SELECT * FROM matches WHERE id = :match_id",
{"match_id": match_id},
)
Comment thread backend/bracket/utils/db.py Outdated
Comment on lines +36 to +40
values = ", ".join([f"'{x}'" for x in mapping.values()])
query = (
f"INSERT INTO {table.name} ({', '.join(mapping.keys())}) VALUES ({values}) RETURNING *"
f"INSERT INTO {table_name} ({', '.join(mapping.keys())}) VALUES ({values}) RETURNING *"
)
last_record_id: int = await database.execute(query)
row_inserted = await fetch_one_parsed(
database, return_type, table.select().where(table.c.id == last_record_id)
)
assert isinstance(row_inserted, return_type), f"Unexpected type: {row_inserted}"
result = await database.fetch_one(query)
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 28, 2026

Copy link
Copy Markdown

Deploying bracket-frontend with  Cloudflare Pages  Cloudflare Pages

Latest commit: 96a80fd
Status: ✅  Deploy successful!
Preview URL: https://8c2dd182.bracket-abd.pages.dev
Branch Preview URL: https://copilot-remove-databases-dep.bracket-abd.pages.dev

View logs

@evroon

evroon commented Jul 28, 2026

Copy link
Copy Markdown
Owner

@copilot fix review comments

Copilot AI commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

@copilot fix review comments

Done — I fixed the review comments in commits 9635911, ad2af69, and be9dca9.

@evroon

evroon commented Jul 28, 2026

Copy link
Copy Markdown
Owner

@copilot resolve the merge conflicts in this pull request

@evroon
evroon marked this pull request as ready for review July 28, 2026 20:33

Copilot AI commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts in this pull request

Done — I resolved the merge conflicts in commit 96a80fd.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants