Skip to content

Relationships are loaded without data consistency via separate queries instead of JOINs #1175

Description

@leio

What you did

A belongs_to relationship is loaded with a separate SQL SELECT when either eager-loaded or used with .include() if toasty::Deferred wrapped in a BEGIN/COMMIT block on at least PostgreSQL.

So querying a single (child) table row via get_by_id() with the parent relationship included is issuing something like this to the database:

BEGIN;
SELECT
  tbl_0_0."id" AS column1,
  tbl_0_0."parent_id" AS column2,
  tbl_0_0."name" AS column3
FROM "child_table" AS tbl_0_0
WHERE tbl_0_0."id" = $1;
SELECT
  tbl_0_0."id" AS column1,
  tbl_0_0."parent_name" AS column2
FROM "parent_table" AS tbl_0_0
WHERE EXISTS (SELECT $2 AS column1 FROM (VALUES ($1)) AS tbl_1_0 WHERE tbl_1_0.column1 = tbl_0_0."id");
COMMIT;

This doesn't actually guarantee data consistency because the SELECTs happen at a different time. Transactions with the default isolation level does not make the separate SELECTs see the data from the same point of time - if something else updates data (and commits it if using explicit transactions) inbetween the separate SELECT calls issued here database connection roundtrips, then the data will not be consistent. This is very bad in my book.

The correct way to do this is to JOIN (the appropriate type, e.g. LEFT JOIN if the relationship is nullable or that just in case) or some other CTE thing as to do this in a single statement.

What you expected

I expected the ORM to issue a query more like this:

SELECT
  tbl_0_0."id" AS column1,
  tbl_0_0."parent_id" AS column2,
  tbl_0_0."name" AS column3,
  tbl_1_0."id" AS column4,
  tbl_1_0."parent_name" AS column5
FROM "child_table" AS tbl_0_0
LEFT JOIN "parent_table" AS tbl_1_0
ON tbl_1_0."id" = tbl_0_0."parent_id"
WHERE tbl_0_0."id" = $1;

This is safe in terms of data consistency, so we won't at least have the child and parent models in application logic seen from a different point of time.

What actually happened

No response

Minimal reproducer

Database driver

PostgreSQL

Toasty version

0.9.0

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugA defect: the code behaves incorrectly

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions