插入数据时出现“尝试写入只读数据库”的SQLite错误?

156

我有一个SQLite数据库,用于网站。问题是当我尝试对其进行INSERT INTO操作时,会出现PDOException异常。

SQLSTATE[HY000]: General error: 8 attempt to write a readonly database

我通过SSH登录到服务器并检查了权限,数据库拥有这些权限

-rw-rw-r--

我对*nix权限不是很熟悉,但我相当确定这意味着:

  • 不是目录
  • 所有者拥有读/写权限(根据ls -l,这是我)
  • 组拥有读/写权限
  • 其他人只有读权限

我也在使用sqlite3程序时搜索了我知道的所有地方,但没有找到相关内容。

因为我不知道PDO尝试以哪种权限打开数据库,所以我做了...

chmod o+w supplies.db

现在,我又收到了一个 PDOException

SQLSTATE[HY000]: General error: 14 unable to open database file

但只有在数据库打开之后尝试执行INSERT查询时才会出现此问题。

您对发生了什么有任何想法吗?


基本上,httpd(Apache > PHP > PDO)不是您,因此它不拥有该文件,因此它没有写入权限......有趣... - SparK
sudo chgrp www-data test.db 并添加权限对我有用。 - Zippp
如果您尝试访问已删除的数据库,也会显示此错误。 - ignacio
13个回答

381
问题在于,PDO SQLite驱动要求如果要执行写操作(INSERTUPDATEDELETEDROP等),则数据库所在的文件夹必须具有写入权限,实际的数据库文件也必须具有写入权限。我在PDO SQLite驱动手册页面底部的注释中找到了这些信息。

10
另外,SELinux(如果已安装)不能强制执行。我花了一天半的时间才想出来。 - Steve V.
7
据我所知,包含文件夹必须可写,因为在写入时会创建一个日志文件以及数据库本身。要使用户与Web服务器相同,请尝试将文件内容复制到另一个特别创建的位置。 - lcapra
4
此外,在 Linux 系统上,db 文件和其所在的目录必须归属于“www-data”。 - anisbet
4
目前sqlite3可能有三个文件,.db.db-shm.db-wal文件,当然还有这三个文件的父目录,该目录必须对运行程序的用户具有可写权限。 - Marcos Dione
1
敬启者,这同样适用于Python的sqlite3模块。 - ZeroKnight
显示剩余2条评论

20

当SQLite文件的所有者与运行脚本的用户不同时,就会发生这种情况。如果整个目录路径(即沿途每个目录)无法写入,也可能会出现类似的错误。

谁拥有SQLite文件?是您吗?

脚本是以什么身份运行的?Apache还是Nobody?


1
我拥有SQLite文件,但不知道脚本以谁的身份运行。我该怎么办?(请注意,这是在共享主机上,我权限有限) - Austin Hyde
2
啊,这让事情更有趣了。如果你在共享主机上,很有可能脚本运行为“nobody”或“apache”。让你的脚本创建一个文件(file_put_contents('./foo.txt', 'Hello, world');),这将告诉你它正在以谁的身份运行。很有可能你需要让脚本创建SQLite数据库。如果你已经有数据在当前文件中,这可能是一个有趣的练习... - Charles
好主意,但不可行。无论 PHP 正在以谁的身份运行,都没有写入权限,因此它无法创建文件。PHP 是否有任何方式可以检索当前正在运行的用户? - Austin Hyde
似乎唯一的方法是通过 POSIX 扩展,该扩展在 POSIX 系统上默认启用。但是,您的托管提供商可能已经阅读了 TFM 并禁用了它。 - Charles
好的,他们已经阅读了TFM。 posix_getuid() 也无法工作。 - Austin Hyde
显示剩余2条评论

8

对我来说问题在于SELinux执行强制访问控制而非权限。当我遵循Steve V.在已接受的答案中评论的建议,禁用执行强制访问控制后,“只读数据库”错误就消失了。

echo 0 >/selinux/enforce

在运行此命令后,一切都按预期工作(CentOS 6.3)。
我遇到的具体问题是在设置Graphite时遇到的。我已经三次检查了apache用户拥有并可以写入我的graphite.db及其父目录。但在我“修复”SELinux之前,我得到的只是一个堆栈跟踪,其效果为: DatabaseError:尝试写入只读数据库

11
SELinux 是一项安全措施,所以不应该没有非常充分的理由而被禁用。与其禁用它,最好找出 SELinux 阻止操作的原因,然后正确地进行配置。 - Jens Wegar

6

这可能是由SELinux引起的。如果您不想完全禁用SELinux,则需要将db目录的fcontext设置为httpd_sys_rw_content_t。

semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/railsapp/db(/.*)?"
restorecon -v /var/www/railsapp/db

5

总之,我通过将数据库文件(* .db)放置在子文件夹中解决了问题。

  • 子文件夹和其中的数据库文件必须是www-data组的成员。
  • 在www-data组中,您必须有权写入子文件夹和数据库文件。

####### 相似问题的附加说明 #####

我将写权限授予了我的sqlite数据库文件给其他用户和组,但仍然无法工作。

文件位于我的.NET Core WebApi的Web根目录中。

它看起来像这样:

-rw-rw-rw-  1 root root  24576 Jan 28 16:03 librestore.db

即使我以root身份运行服务,仍然会出现以下错误:

Error: SQLite Error 8: 'attempt to write a readonly database'。

我也将librestore.db文件的所有权更改为www-data用户,但仍然收到相同的错误。
最终,我将目录移动到WebApi的根目录上方,并授予其他用户对该目录的写许可,然后问题得以解决。

Web root has write access

我不确定为什么需要给目录写入权限,如果特定文件已经有写入权限,但这是唯一有效的方法。

但是,一旦我进行了更改,www-data用户就可以访问.db文件并成功插入。


4
当我尝试在Android系统上写入数据库时,出现了这个错误。
显然,sqlite3不仅需要对数据库文件和包含目录(如@austin-hyde在他的答案中所说)具有写权限,还需要环境变量TMPDIR指向一个(可能可写的)目录。
在我的Android系统上,我将其设置为TMPDIR =“/data/local/tmp”,现在我的脚本按预期运行 :)
编辑:
如果您无法设置环境变量,则可以使用此处列出的其他方法之一:https://www.sqlite.org/tempfiles.html#temporary_file_storage_locations,例如PRAGMA temp_store_directory ='directory-name';

2

我在Windows 7下使用IIS时也遇到了同样的错误。为了修复这个错误,我不得不为SQLite数据库文件添加IUSR帐户的完全控制权限。如果您使用WebMatrix而不是IIS使用SQLite,则无需更改权限。


2

我使用以下代码来查找运行脚本的用户(即用户名):

echo exec('whoami');

然后,我给这个用户整个应用程序目录的权限,如下:

sudo chown -R :用户名 /var/www/html/myapp


0
(对于寻找类似问题答案的读者) 我正在构建一个C# .Net Core 6.0 WPF应用程序。我将Sqlite.db3放在C:\驱动器上以方便开发。要写入数据库,我必须以管理员身份打开Visual Studio 2019。

0

@Charles在评论中指出了解决方案(或者至少是一个拙劣的解决方案)。这只是我更清楚地阐述它。在您的应用程序的根目录(或任何您想要创建数据库的位置)中,将file_put_contents('./nameofyourdb.sqlite', null);(或.db,无论您喜欢哪个)放入.php文件中,然后加载呈现php代码的页面。现在,您有一个由运行您的php代码的任何用户创建的sqlite db,这意味着您的php代码可以写入它。只是不要忘记在控制台中与此数据库交互时使用sudo。

一个好的干净的解决方案是允许您的主用户帐户的文件被(在我的情况下)http用户写入,但这对我有效且简单。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接