Skip to content

Conversation

@rrigby
Copy link
Contributor

@rrigby rrigby commented Nov 14, 2025

This will require a closer look, but I've been using these changes for a while with success.

Reasoning:
In most situations a problem with a query resulted in picodb's SQLException propagating out making for easy handling.
Here's the source of the exception in picodb for reference:

   // StatementHandler.php   

    public function execute()
    {
        try {
            $this->beforeExecute();

            $pdoStatement = $this->db->getConnection()->prepare($this->sql);
            $this->bindParams($pdoStatement);
            $pdoStatement->execute();

            $this->afterExecute();
            return $pdoStatement;
        } catch (PDOException $e) {
            return $this->handleSqlError($e);
        }
    }

    public function handleSqlError(PDOException $e)
    {
        $this->cleanup();
        $this->db->cancelTransaction();
        $this->db->setLogMessage($e->getMessage());

        throw new SQLException('SQL Error: '.$e->getMessage());
    }

However in some situations, this exception is caught in picomapper and instead false is returned. This makes handling errors more difficult. The most common situation I encountered this, was when I had a bug and was attempting to update an aggregate with new duplicate child records. It would attempt to insert a duplicate child and that resulting exception would be caught and false would be returned. I was confused why my request was succeeding but my record wasn't updating. After diving in I figured out why, and thus this PR 😅
before:

    } catch (\Exception $e) {
        if ($useTransaction) {
            $this->db->cancelTransaction();
        }

        return false;
    }

after:

    } catch (\Exception $exception) {
        if ($useTransaction) {
            $this->db->cancelTransaction();
        }

        throw $exception;
    }

I've also opted to simply remove the returning false. Such as this example:

    if (!parent::insert($base)) {
            // Transaction already cancelled by the statement handler
            return false;
    }

In the above example false is never returned, as the SQLException from picodb is thrown already. So I've simply replaced it with:

parent::insert($base);

I've also escalated to exception any errors in the picomapper library that previously returned false, for example:
before:

    if (!$original = $this->findOne()) {
        return false;
    }

after:

    if (!$original = $this->findOne()) {
        throw new \Exception('Failed to update record. Original not found.');
    }

Maybe these should use a specific exception class from picomapper, I'm happy to add that if you have thoughts. I'd just kept it simple for now.

This fixes the previous behaviour where most issues resulted in an SQLError exception, however a handful of things were handled and simply returned false, which wasn't clear when using the library and lead to bugs.
$this->getMapping()->update($customer);
}

public function testUpdateDoesNotErrorWhenUpdatingDuplicateChildrenRecords()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test simply confirms existing behaviour, that it's fine to include duplicate items in the update array, as just the last entry for a given id will be applied to the item in the db.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant