nette / database

💾 A database layer with a familiar PDO-like API but much more powerful. Building queries, advanced joins, drivers for MySQL, PostgreSQL, SQLite, MS SQL Server and Oracle.
https://doc.nette.org/database
Other
512 stars 108 forks source link

Typed property Nette\Database\ResultSet::$pdoStatement must not be accessed before initialization #310

Closed ondrej-tuhacek closed 3 months ago

ondrej-tuhacek commented 3 months ago

Version: 3.2.3

Bug Description

Since version 3.2.3 there is this error because the $pdoStatement in ResultSet is now 'readonly' without initial null assignment. Problem appears when you try to begin a transaction, which runs query "::beginTransaction", which in constructor of ResultSet means that the $pdoStatement is never initialized.

Steps To Reproduce

It happens in debug mode (because Tracy ConnectionPanel is calling getRowCount() for beginTransaction())

    $dbExplorer->beginTransaction();
    $dbExplorer->table('table')->insert([...]);
    ...
    $dbExplorer->commit();

Expected Behavior

Should begin a transaction, insert some data and commit a transaction

Possible Solution

public function __construct(
    private readonly Connection $connection,
    private readonly string $queryString,
    private readonly array $params,
    ?callable $normalizer = null,
) {
    $time = microtime(true);
    $this->normalizer = $normalizer;
    $types = ['boolean' => PDO::PARAM_BOOL, 'integer' => PDO::PARAM_INT, 'resource' => PDO::PARAM_LOB, 'NULL' => PDO::PARAM_NULL];

    try {
        if (str_starts_with($queryString, '::')) {
            $connection->getPdo()->{substr($queryString, 2)}();
            $this->pdoStatement = null; // THIS LINE SHOULD IMHO BE ADDED TO INITIALIZE $pdoStatement WITH NULL
        } else {
            $this->pdoStatement = $connection->getPdo()->prepare($queryString);
            foreach ($params as $key => $value) {
                $type = gettype($value);
                $this->pdoStatement->bindValue(is_int($key) ? $key + 1 : $key, $value, $types[$type] ?? PDO::PARAM_STR);
            }

            $this->pdoStatement->setFetchMode(PDO::FETCH_ASSOC);
            $this->pdoStatement->execute();
        }
    } catch (\PDOException $e) {
        $e = $connection->getDriver()->convertException($e);
        $e->queryString = $queryString;
        $e->params = $params;
        throw $e;
    }

    $this->time = microtime(true) - $time;
}
KminekMatej commented 3 months ago

Same here: https://forum.nette.org/cs/36253-prosim-o-otestovani-nette-database-3-2#p227469

Checking the code you can find out that in the master branch its all refactored and possibly fixed (havent tested yet, but soon).

So probable solution would be to wait for a 4.0 release