【问题标题】:SQLite SQLSTATE[HY000]: General error: 8 attempt to write a readonly databaseSQLite SQLSTATE[HY000]:一般错误:8 次尝试写入只读数据库
【发布时间】:2020-11-21 04:05:46
【问题描述】:

我正在使用 SQLite 连接和原则迁移来使用 PHPUnit 进行功能测试。 我正在使用setUp 方法从头开始迁移数据库:

public function setUp()
{
    parent::setUp();
    
    @unlink(__DIR__ . '/../../../../../../../var/sqlite.db');

    exec('./vendor/bin/doctrine-migrations migrations:migrate --db-configuration=migrations-db-test.php --configuration=migrations_test.yml --no-interaction');
}

然后我可以从数据库中写入/读取。例如:

public function test_add_event_should_add_event()
{
    $service = $this->getAdEventComparativesUpdateService();
    $request = AdEventComparativesUpdateServiceRequest::make(self::AD_ID, self::USER_IP);
    $response = $service->execute($request);

    $this->assertEquals(1, $response->getTotal());
}

它有效。即使我使用相同的参数调用两次服务,它也确实有效。在这种情况下,它只需要写第一次:

public function test_add_two_same_events_should_add_one_event()
{
    $service = $this->getAdEventComparativesUpdateService();
    $request = AdEventComparativesUpdateServiceRequest::make(self::AD_ID, self::USER_IP);
    // Call twice
    $service->execute($request);
    $response = $service->execute($request);

    $this->assertEquals(1, $response->getTotal());
}

当我必须测试两个必须同时编写的调用时,问题就来了:

public function test_add_two_different_events_should_add_two_events()
{
    $service = $this->getAdEventComparativesUpdateService();
    $request = AdEventComparativesUpdateServiceRequest::make(self::AD_ID, self::USER_IP);
    $response = $service->execute($request);

    $service = $this->getAdEventComparativesUpdateService();
    $request = AdEventComparativesUpdateServiceRequest::make(self::AD_ID, self::OTHER_USER_IP);
    $response = $service->execute($request); // **** It fails here

    $this->assertEquals(2, $response->getTotal());
}

错误来了:

  1. XXX::test_add_two_different_events_should_add_two_event Doctrine\DBAL\Exception\ReadOnlyException: 使用参数 [xxx, "xxx", "xxx", 执行 'INSERT INTO xxx (xxx, xxx, xxx, xxx) VALUES (?, ?, ?, ?)' 时发生异常, “xxx”]:

SQLSTATE[HY000]:一般错误:8 次尝试写入只读数据库

xxx/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/AbstractSQLiteDriver.php:78 xxx/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php:128 xxx/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php:178 xxx/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php:281 xxx/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:1014 xxx/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php:378 xxx/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:356 xxx/vendor/doctrine/orm/lib/Doctrine/ORM/EntityManager.php:235 xxx/DoctrineSession.php:32 xxx/TransactionalApplicationService.php:39 xxx/xxx/test.php:100

我尝试在调用之间更改数据库文件权限,但没有任何变化:

public function test_add_two_different_events_should_add_two_event()
{
    $service = $this->getAdEventComparativesUpdateService();
    $request = AdEventComparativesUpdateServiceRequest::make(self::AD_ID, self::USER_IP);
    $response = $service->execute($request);

    chmod(__DIR__ . '/../../../../../../../var', 0777);
    chmod(__DIR__ . '/../../../../../../../var/sqlite.db', 0777);
    chown(__DIR__ . '/../../../../../../../var', 'www-data');
    chgrp(__DIR__ . '/../../../../../../../var', 'www-data');
    chown(__DIR__ . '/../../../../../../../var/sqlite.db', 'www-data');
    chgrp(__DIR__ . '/../../../../../../../var/sqlite.db', 'www-data');
   //die;
   // Here I checked the /var sqlite.db permissions. They are 0777

    $service = $this->getAdEventComparativesUpdateService();
    $request = AdEventComparativesUpdateServiceRequest::make(self::AD_ID, self::OTHER_USER_IP);
    $response = $service->execute($request);

    $this->assertEquals(2, $response->getTotal());
}

知道错误可能来自哪里吗?在这种情况下,每个服务调用都会调用persist + flush

【问题讨论】:

  • @d3javu999 这就是我所做的
  • 这是在黑暗中刺伤,因为我不知道教义...我可以设想“真正的”错误可能不是数据库是只读的,但它以某种方式被锁定。 也许(而且我处于危险之中)事情是异步发生的,getTotal() 调用会同步事情吗?如果是这样(大“如果”),则可能是两个更新都试图同时运行。如果您在第一个 execute() 之后添加 $this->assertEquals(1, $response->getTotal()); 有什么变化吗?
  • @Manolo 如果开启extended result codes,原始的sqlite状态码可以比'readonly'更具体。
  • @JánosRoden 我试过$this->setAttribute(PDO::SQLITE_ATTR_EXTENDED_RESULT_CODES, true); 但我得到这个错误:致命错误:未定义的类常量'SQLITE_ATTR_EXTENDED_RESULT_CODES'

标签: php sqlite doctrine phpunit doctrine-migrations


【解决方案1】:

很难回答这个问题,因为我们没有真正的 [mre]。最常见的是这是一个权限问题,但看起来您已经探索了这条路线。我想我预感到这里发生了什么:

看起来单元测试在每次测试时都会重新创建数据库,即取消链接数据库文件,然后当打开连接时,如果数据库文件不存在,则会自动创建它。因此,在两次写入尝试的情况下,连接首先打开并保持打开状态。然后数据库文件 unlink 被相同或另一个测试调用,随后的写入命令失败。

【讨论】:

  • 对不起,我不明白你的意思。您是对的,因为每个测试方法都会删除并重新创建数据库。 setUp 方法在每个测试方法之前被调用,所以这正是它正在做的事情。但是,删除 DB 文件的 unlink 和 DB 重新创建命令仅在方法之前调用一次,而不是在同一方法的两次调用之间。事实上,如果第二个调用只是读取数据库,它就可以工作。那么你的建议是什么?
  • @Manolo 我想在测试运行时重新创建数据库。但是,如果后续读取有效,为什么写入会失败?您是否尝试过捕获生成的 SQL 查询并以相同的顺序手动输入它们?也许有些违反约束..?
  • 不,但我会试试看。
  • 如果我手动执行查询就可以了,所以这似乎不是问题
猜你喜欢
  • 2018-05-29
  • 1970-01-01
  • 2015-10-27
  • 2021-09-11
  • 2017-07-17
  • 2012-10-24
  • 2021-05-01
  • 2011-03-20
相关资源
最近更新 更多