当前位置:首页 > 文章列表 > 文章 > python教程 > Python sqlite3 怎样用 URI 参数打开只读连接

Python sqlite3 怎样用 URI 参数打开只读连接

来源:17golang原创 2026-10-20 01:53:10 0浏览 收藏

如果程序只是读取一份已经准备好的 SQLite 数据库,普通的 sqlite3.connect("data/app.db") 并不能表达“文件必须存在、连接不能写入”这两个要求。SQLite 默认可能创建缺失文件,排查配置错误时就容易把一个空库当成成功连接。

更明确的写法是使用 URI 参数:sqlite3.connect("file:data/app.db?mode=ro", uri=True)。其中 file: 表示 SQLite URI,mode=ro 表示只读打开,uri=True 告诉 Python 把第一个参数按 URI 解释。文件不存在时连接应直接失败,连接建立后执行写操作也会被拒绝。

官方文档:https://docs.python.org/3/library/sqlite3.html

先记住这三个判断
  • uri=True 不是只读开关;没有它,mode=ro 只是普通字符串的一部分。
  • mode=ro 要求目标数据库已经存在,不会因为路径写错而悄悄创建新库。
  • 只读连接仍然可以执行 SELECT;真正的 INSERT、UPDATE 或 DDL 会在写入阶段失败。

先把只读连接拆成三个条件

这个问题常常不是 Python 语法错了,而是三个层次混在了一起。file: 是 SQLite URI 的方案前缀,负责让后面的路径和查询参数组成一个 URI;mode=ro 是 SQLite 的打开模式,负责要求只读;uri=True 是 Python sqlite3.connect() 的参数,负责启用 URI 解析。

因此,下面两种写法的含义不同:

import sqlite3

# 这里明确启用 URI 解析;mode=ro 才会被 SQLite 当作只读参数。
conn = sqlite3.connect("file:data/app.db?mode=ro", uri=True)
conn.close()  # 连接用完后主动释放文件句柄。
import sqlite3

# 没有 uri=True 时,不要把整个字符串误认为已经启用了只读模式。
conn = sqlite3.connect("file:data/app.db?mode=ro")
conn.close()  # 这不是本文要求的 URI 只读连接。
Python sqlite3 URI 只读连接中 connect、uri=True、mode=ro 与 SQLite 文件和异常的静态关系图
图1:把 Python 连接函数、URI 解析、只读参数、已有文件和异常边界放在同一张静态结构说明图中;它不是运行截图。

用 mode=ro 打开已经存在的数据库

最小可用代码可以先固定一个相对路径:

from pathlib import Path
import sqlite3

db_uri = "file:data/app.db?mode=ro"
conn = None
try:
    # uri=True 让 SQLite 读取 mode=ro,而不是把它当成普通文件名。
    conn = sqlite3.connect(db_uri, uri=True)
    rows = conn.execute("SELECT id, name FROM users ORDER BY id").fetchall()
    for row in rows:
        print(row)
except sqlite3.OperationalError as exc:
    # 连接阶段或查询阶段的 SQLite 错误都保留原始信息,便于定位路径和权限问题。
    raise RuntimeError(f"只读数据库访问失败: {exc}") from exc
finally:
    if conn is not None:
        conn.close()  # 只读不等于不占用句柄,连接仍然需要关闭。

示例中的 Path 只是提醒路径应由配置决定;真正传给 connect() 的值仍是带 file: 前缀和查询串的 URI。读取失败时保留原始 OperationalError,比只输出“数据库不可用”更容易区分文件路径、权限和 SQL 问题。

文件不存在时,为什么应该在连接阶段失败

普通路径连接的一个常见误区是:程序启动后看到一个新的 app.db,就以为部署成功。实际上,路径拼写错误、挂载目录没有准备好、工作目录改变,都可能让程序打开了一个刚创建的空文件。

mode=ro 把这个边界前移到连接阶段。目标不存在时,SQLite 不应创建数据库,而是返回打开失败的 OperationalError。这正是只读数据目录适合使用 URI 的原因:缺文件会暴露出来,而不会被“自动初始化”掩盖。

import sqlite3

def open_existing_readonly(path: str) -> sqlite3.Connection:
    # 只读连接要求部署先准备好数据库文件;不会替缺失文件创建空库。
    uri = f"file:{path}?mode=ro"
    return sqlite3.connect(uri, uri=True)

try:
    conn = open_existing_readonly("data/app.db")
except sqlite3.OperationalError as exc:
    # 这里优先检查工作目录、挂载路径和文件权限,而不是先重试查询。
    raise RuntimeError(f"无法打开既有 SQLite 文件: {exc}") from exc
else:
    conn.close()  # 成功建立连接后也要释放资源。

这段代码适合表达“文件必须由部署步骤准备好”的约束。若路径来自用户输入或包含 ?、# 等 URI 保留字符,不能简单拼接字符串,应先按照 SQLite URI 规则对路径部分进行编码,并把查询参数放在编码后的路径之后。

写入被拒绝,不代表读取失败

只读连接的另一个排查现场是:SELECT 正常,但第一次写入时报 attempt to write a readonly database 一类错误。这通常说明 URI 已经按预期生效,问题不在连接建立,而在代码越过了只读边界。

import sqlite3

conn = sqlite3.connect("file:data/app.db?mode=ro", uri=True)
try:
    # 查询属于只读连接允许的操作。
    row = conn.execute("SELECT count(*) FROM users").fetchone()
    print("当前记录数:", row[0])

    # 这个写操作应被 SQLite 拒绝,用来暴露误写路径。
    conn.execute("INSERT INTO users(name) VALUES (?)", ("demo",))
except sqlite3.OperationalError as exc:
    # 生产代码应记录 SQL 场景,但不要吞掉只读约束错误。
    raise RuntimeError(f"只读连接拒绝写入: {exc}") from exc
finally:
    conn.close()  # 无论查询还是写入失败,都关闭连接。

这里不要把只读 URI 改成普通路径来“修复”错误。先确认调用方是否本来就应该写入;如果需要写入,应把写库职责放到拥有写权限的连接或服务中,而不是让一个只读数据源偷偷承担写操作。

相对路径、绝对路径和工作目录要分开判断

URI 只读模式能约束打开方式,但不能替你决定相对路径从哪里开始解析。file:data/app.db?mode=ro 依赖当前工作目录;同一个服务在本地启动、进程管理器启动和容器启动时,工作目录可能不同。

排查时可把路径解析和 URI 拼装分开记录:

from pathlib import Path
from urllib.parse import quote
import sqlite3

db_path = Path("data/app.db").resolve()
# quote 只处理路径部分,保留斜杠,避免特殊字符被误当成 URI 查询语法。
encoded_path = quote(str(db_path), safe="/:\\")
db_uri = f"file:{encoded_path}?mode=ro"

conn = sqlite3.connect(db_uri, uri=True)
try:
    conn.execute("SELECT 1").fetchone()
finally:
    conn.close()  # 只做探测也要及时关闭连接。

这个版本适合配置系统已经给出绝对路径的场景。Windows 路径、网络挂载和包含特殊字符的路径应在目标运行环境中按 SQLite URI 文档单独确认;不要因为本机相对路径可用,就假设服务部署目录也一致。

SQLite URI 只读模式中相对路径、绝对路径、file 方案、mode=ro、已有文件、查询和写入拒绝的静态关系图
图2:路径表达、URI 解析、已有文件、读取和写入拒绝之间的静态边界说明图,不代表某次真实运行结果。

上线前按这张清单复查

检查点应该看到的结果异常时先看哪里
URI 形式以 file: 开头,并带 mode=ro配置拼接和路径编码
Python 参数sqlite3.connect(..., uri=True)是否漏传 uri=True
文件存在性缺失文件在连接阶段失败工作目录、挂载点、部署产物
查询行为SELECT 可以执行文件权限、数据库损坏、SQL
写入行为写操作被明确拒绝调用方是否误用了只读连接
资源释放所有分支最终调用 close()异常处理和连接生命周期

归纳起来,Python sqlite3 的 URI 只读连接写法就是 file:数据库路径?mode=ro 配合 uri=True。先用它保证“文件必须存在”,再用查询成功和写入拒绝分别确认读取路径与只读边界,最后把绝对路径解析和连接关闭纳入部署代码。

常见问题

只传 mode=ro,不传 uri=True 可以吗?

不建议这样做。uri=True 是 Python 端启用 URI 解析的开关;缺少它时,查询串不会按本文需要的 SQLite URI 参数处理。

只读连接能执行事务吗?

可以执行不改变数据的查询,但事务语义不能把写入权限变出来。是否显式开启事务应服从应用的查询模式,写操作仍会被只读数据库拒绝。

文件不存在时想自动创建怎么办?

那是初始化或写入职责,不应和只读连接混在一起。先由部署或初始化流程创建并准备数据库,再用 mode=ro 提供读取连接,能更早暴露环境缺失。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Java jcmd 怎样按线程状态筛选定位阻塞Java jcmd 怎样按线程状态筛选定位阻塞
上一篇
Java jcmd 怎样按线程状态筛选定位阻塞
Python 3.15 pathlib mkdir 怎样设置中间目录权限
下一篇
Python 3.15 pathlib mkdir 怎样设置中间目录权限
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    551次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    603次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    629次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    583次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    409次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码