Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: UnitOfWork refresh attempts to load the wrong object #11331

Open
Novynn opened this issue Mar 2, 2024 · 0 comments
Open

Bug: UnitOfWork refresh attempts to load the wrong object #11331

Novynn opened this issue Mar 2, 2024 · 0 comments

Comments

@Novynn
Copy link

Novynn commented Mar 2, 2024

Bug Report

Q A
BC Break no
Version 2.18.0

Summary

When calling refresh on an entity, under certain circumstances the id used to fetch the entity from the database can be wrong.

Current behavior

Confusing the UnitOfWork by creating a new entity with the same id as one that it is already being managed can cause OID collisions when that entity is garbage collected due to not having a reference in the UnitOfWork's identityMap. This leads to a refresh attempting to use the wrong id in the database.

How to reproduce

#[Entity]
#[Table("my_entity")]
class MyEntity
{
    #[Id, Column(name: "id", type: "integer"), GeneratedValue(strategy: 'SEQUENCE')]
    public ?int $id = null;
}

#[Entity]
#[Table("my_token")]
class MyToken
{
    public function __construct(
        #[Id, Column(name: "hash", type: "string")]
        public string $hash,
    ) {}
}

// NOTE: Seed the database with a single my_token
$key = md5(random_bytes(4));

$em->getConnection()->executeStatement(<<<SQL
    DELETE FROM my_token;
    INSERT INTO my_token VALUES ('{$key}');
SQL);

function refreshMyToken(EntityManager $em): void
{
    global $key;

    $token = $em->find(MyToken::class, $key);
    echo "MyToken (original) has oid of ".spl_object_id($token)."\n";

    // NOTE: This causes all the problems, as the UnitOfWork still thinks the token is active and managed
    $em->createQuery('DELETE FROM MyToken s')->execute();

    $token = new MyToken($key);
    // NOTE: This causes the deprecation warning in addToIdentityMap, but continues
    $em->persist($token);
    $em->flush();

    echo "MyToken (new) has oid of ".spl_object_id($token)."\n";

    gc_collect_cycles();
}

refreshMyToken($em);

$entity = new MyEntity();
$em->persist($entity);
$em->flush();
echo "Entity has oid of ".spl_object_id($entity)."\n";

\Closure::fromCallable(
    /** @psalm-suppress InaccessibleProperty */
    function() {
        /** @var \Doctrine\ORM\UnitOfWork $this */
        echo "UnitOfWork identifier map is: ".var_export($this->entityIdentifiers, true)."\n";
    },
)->call($em->getUnitOfWork());

$em->refresh($entity);

Output:

MyToken (original) has oid of 769
MyToken (new) has oid of 765
Entity has oid of 765
UnitOfWork identifier map is: array (
  769 =>
  array (
    'hash' => 'b8ff315c79068f22ccf4543cd93f9f46',
  ),
  765 =>
  array (
    'hash' => 'b8ff315c79068f22ccf4543cd93f9f46',
  ),
)

Fatal error: Uncaught PDOException: SQLSTATE[22P02]: Invalid text representation: 7 ERROR:  invalid input syntax for type integer: "b8ff315c79068f22ccf4543cd93f9f46"

Expected behavior

In the future the exception in #10785 will be thrown, but currently the code continues as normal in a broken state until the mismatched column types stop execution.

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

No branches or pull requests

1 participant