SQLite 中的限制

本文上下文中的“限制”是指不能超过的尺寸或数量。我们关心诸如 BLOB 中的最大字节数或表中的最大列数之类的事情。

SQLite 最初设计时采用了避免任意限制的策略。当然,在内存和磁盘空间有限的机器上运行的每个程序都有某种限制。但在 SQLite 中,这些限制没有明确定义。政策是,如果它适合内存并且您可以用 32 位整数计算它,那么它应该可以工作。

不幸的是,无限制政策已被证明会产生问题。因为上限没有很好地定义,所以没有测试,并且在将 SQLite 推向极端时经常发现错误。出于这个原因,自 3.5.8(2008-04-16)发布以来的 SQLite 版本具有明确定义的限制,并且这些限制作为测试套件的一部分进行测试。

本文定义了 SQLite 的限制以及如何针对特定应用程序对其进行自定义。限制的默认设置通常非常大,足以满足几乎所有应用程序的需求。某些应用程序可能希望在这里或那里增加限制,但我们预计这种需求很少见。更常见的是,应用程序可能希望以低得多的限制重新编译 SQLite,以避免在更高级别的 SQL 语句生成器中出现错误时过度使用资源,或者帮助阻止注入恶意 SQL 语句的攻击者。

使用sqlite3_limit()接口和 为该接口定义的限制类别之一, 可以在运行时基于每个连接更改某些限制。运行时限制是为具有多个数据库的应用程序设计的,其中一些仅供内部使用,而另一些可能会受到潜在的敌对外部代理的影响或控制。例如,Web 浏览器应用程序可能使用内部数据库来跟踪历史页面浏览量,但具有一个或多个单独的数据库,这些数据库由从 Internet 下载的 javascript 应用程序创建和控制。sqlite3_limit( )接口允许受信任代码管理的内部数据库不受约束,同时对不受信任的外部代码创建或控制的数据库施加严格限制,以帮助防止拒绝服务攻击。

  1. 字符串或 BLOB 的最大长度

    SQLite 中字符串或 BLOB 中的最大字节数由预处理器宏 SQLITE_MAX_LENGTH 定义。此宏的默认值为 10 亿(1000 万或 1,000,000,000)。您可以使用如下命令行选项在编译时提高或降低此值:

    -DSQLITE_MAX_LENGTH=123456789

    当前的实现将只支持长度最大为 2 31 -1 或 2147483647 的字符串或 BLOB。并且一些内置函数(例如 hex())可能在此之前就失败了。在对安全敏感的应用程序中,最好不要尝试增加最大字符串和 blob 长度。事实上,如果可能的话,您最好将最大字符串和 blob 长度降低到几百万范围内。

    在 SQLite 的部分 INSERT 和 SELECT 处理过程中,数据库中每一行的完整内容被编码为单个 BLOB。所以 SQLITE_MAX_LENGTH 参数也决定了一行中的最大字节数。

    可以使用sqlite3_limit (db, SQLITE_LIMIT_LENGTH ,size) 接口 在运行时降低最大字符串或 BLOB 长度。

  2. 最大列数

    SQLITE_MAX_COLUMN 编译时参数用于设置上限:

    • 表格中的列数
    • 索引中的列数
    • 视图中的列数
    • UPDATE 语句的 SET 子句中的项数
    • SELECT 语句的结果集中的列数
    • GROUP BY 或 ORDER BY 子句中的项数
    • INSERT 语句中值的数量

    SQLITE_MAX_COLUMN 的默认设置是 2000。您可以在编译时将其更改为最大 32767 的值。另一方面,许多经验丰富的数据库设计人员会争辩说,一个规范化良好的数据库永远不需要表中超过 100 列。

    在大多数应用程序中,列的数量很少——几十个。SQLite 代码生成器中有些地方使用 O(N²) 算法,其中 N 是列数。因此,如果您将 SQLITE_MAX_COLUMN 重新定义为一个非常大的数字并生成使用大量列的 SQL,您可能会发现sqlite3_prepare_v2() 运行缓慢。

    可以使用sqlite3_limit (db, SQLITE_LIMIT_COLUMN ,size) 接口 在运行时降低最大列数。

  3. SQL 语句的最大长度

    SQL 语句文本中的最大字节数限制为 SQLITE_MAX_SQL_LENGTH,默认为 1,000,000,000。

    如果 SQL 语句的长度限制为一百万字节,那么显然您将无法通过将它们作为文字嵌入到 INSERT 语句中来插入数百万字节的字符串。但无论如何你都不应该那样做。为您的数据使用主机参数像这样准备简短的 SQL 语句:

    INSERT INTO tab1 VALUES(?,?,?);

    然后使用sqlite3_bind_XXXX()函数将大字符串值绑定到 SQL 语句。绑定的使用避免了对字符串中引号字符进行转义的需要,从而降低了 SQL 注入攻击的风险。它也运行得更快,因为不需要解析或复制大字符串。

    可以使用sqlite3_limit (db, SQLITE_LIMIT_SQL_LENGTH ,size) 接口 在运行时降低 SQL 语句的最大长度。

  4. 连接中的最大表数

    SQLite 不支持包含超过 64 个表的连接。此限制源于以下事实:SQLite 代码生成器在查询优化器中使用每个连接表一位的位图。

    SQLite 使用高效的查询规划器算法 ,因此即使是大型连接也可以快速准备因此,没有提高或降低连接中表数限制的机制。

  5. 表达式树的最大深度

    SQLite 将表达式解析成一棵树进行处理。在代码生成期间,SQLite 递归地遍历这棵树。因此,表达式树的深度受到限制,以避免使用过多的堆栈空间。

    SQLITE_MAX_EXPR_DEPTH 参数确定最大表达式树深度。如果该值为 0,则不强制执行任何限制。当前实现的默认值为 1000。

    如果 SQLITE_MAX_EXPR_DEPTH 最初为正,则可以使用sqlite3_limit (db, SQLITE_LIMIT_EXPR_DEPTH ,size) 接口 在运行时降低表达式树的最大深度。换句话说,如果表达式深度已经存在编译时限制,则可以在运行时降低最大表达式深度。如果 SQLITE_MAX_EXPR_DEPTH 在编译时设置为 0(如果表达式的深度不受限制),则sqlite3_limit (db, SQLITE_LIMIT_EXPR_DEPTH ,size) 是空操作。

  6. 函数的最大参数数

    SQLITE_MAX_FUNCTION_ARG 参数确定可以传递给 SQL 函数的参数的最大数量。此限制的默认值为 100。SQLite 应与具有数千个参数的函数一起使用。然而,我们怀疑任何试图调用具有多个参数的函数的人实际上是在尝试在使用 SQLite 的系统中寻找安全漏洞,而不是做有用的工作,因此我们将此参数设置得相对较低。

    函数的参数数量有时存储在带符号的字符中。因此 SQLITE_MAX_FUNCTION_ARG 的硬上限为 127。

    可以使用sqlite3_limit (db, SQLITE_LIMIT_FUNCTION_ARG ,size) 接口 在运行时降低函数中参数的最大数量。

  7. 复合 SELECT 语句中的最大项数

    复合SELECT语句是由运算符 UNION、UNION ALL、EXCEPT 或 INTERSECT 连接的两个或多个 SELECT 语句。我们将复合 SELECT 中的每个单独的 SELECT 语句称为“术语”。

    SQLite 中的代码生成器使用递归算法处理复合 SELECT 语句。因此,为了限制堆栈的大小,我们限制了复合 SELECT 中的项数。术语的最大数量是 SQLITE_MAX_COMPOUND_SELECT,默认为 500。我们认为这是一个慷慨的分配,因为在实践中我们几乎从未见过复合选择中的术语数量超过个位数。

    可以使用sqlite3_limit (db, SQLITE_LIMIT_COMPOUND_SELECT ,size) 接口 在运行时降低复合 SELECT 术语的最大数量。

  8. LIKE 或 GLOB 模式的最大长度

    LIKEGLOB 默认使用的模式匹配算法 对于某些病态情况,SQLite 的实现可以表现出 O(N²) 性能(其中 N 是模式中的字符数)。为了避免来自能够指定自己的 LIKE 或 GLOB 模式的不法分子的拒绝服务攻击,LIKE 或 GLOB 模式的长度限制为 SQLITE_MAX_LIKE_PATTERN_LENGTH 字节。此限制的默认值为 50000。现代工作站甚至可以相对快速地评估 50000 字节的病态 LIKE 或 GLOB 模式。只有当模式长度达到数百万字节时,拒绝服务问题才会发挥作用。尽管如此,由于大多数有用的 LIKE 或 GLOB 模式的长度最多只有几十个字节,

    可以使用sqlite3_limit (db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH ,size) 接口 在运行时降低 LIKE 或 GLOB 模式的最大长度。

  9. 单个 SQL 语句中的最大主机参数数

    主机参数是 SQL 语句中的占位符,使用 sqlite3_bind_XXXX()接口之一填充。许多 SQL 程序员都熟悉使用问号(“?”)作为主机参数。SQLite 还支持以“:”、“$”或“@”开头的命名主机参数以及“?123”形式的编号主机参数。

    SQLite 语句中的每个主机参数都分配了一个数字。这些数字通常以 1 开头,并随着每个新参数增加 1。但是,当使用“?123”形式时,主机参数号是问号后面的数字。

    SQLite 分配空间来保存 1 和使用的最大主机参数号之间的所有主机参数。因此,包含主机参数(如 ?1000000000)的 SQL 语句将需要千兆字节的存储空间。这很容易使主机的资源不堪重负。为防止过多的内存分配,主机参数编号的最大值为 SQLITE_MAX_VARIABLE_NUMBER,对于 3.32.0(2020-05-22)之前的 SQLite 版本默认为 999,对于 3.32.0 之后的 SQLite 版本默认为 32766。

    可以使用sqlite3_limit (db, SQLITE_LIMIT_VARIABLE_NUMBER ,size) 接口 在运行时降低最大主机参数数。

  10. 触发递归的最大深度

    SQLite 限制触发器的递归深度,以防止涉及递归触发器的语句使用无限量的内存。

    在 SQLite版本 3.6.18 (2009-09-11) 之前,触发器不是递归的,因此这个限制没有意义。从版本 3.6.18 开始,支持递归触发器,但必须使用 PRAGMA recursive_triggers语句显式启用。版本 3.7.0 (2009-09-11) 开始,递归触发器默认启用,但可以使用PRAGMA recursive_triggers手动禁用。SQLITE_MAX_TRIGGER_DEPTH 仅在启用递归触发器时才有意义。

    默认最大触发递归深度为 1000。

  11. 附加数据库的最大数量

    ATTACH语句是一个 SQLite 扩展 ,它允许两个或多个数据库关联到同一个数据库连接,并像它们是单个数据库一样运行。同时连接的数据库数量限制为 SQLITE_MAX_ATTACHED,默认设置为 10。附加数据库的最大数量不能增加到 125 以上。

    可以使用sqlite3_limit (db, SQLITE_LIMIT_ATTACHED ,size) 接口 在运行时降低附加数据库的最大数量。

  12. 数据库文件中的最大页数

    SQLite 能够限制数据库文件的大小,以防止数据库文件变得太大而占用过多的磁盘空间。SQLITE_MAX_PAGE_COUNT 参数,通常设置为 1073741823,是单个数据库文件中允许的最大页数。尝试插入会导致数据库文件变得比这更大的新数据将返回 SQLITE_FULL。

    SQLITE_MAX_PAGE_COUNT 的最大可能设置为 4294967294。当与最大页面大小 65536 一起使用时,这给出了大约 281 TB 的最大 SQLite 数据库大小。

    max_page_count PRAGMA可用于在运行时提高或降低此限制。

  13. 表中的最大行数

    一个表的理论最大行数是 2 64(18446744073709551616 或大约 1.8e+19)。由于将首先达到 281 TB 的最大数据库大小,因此无法达到此限制。一个 281 TB 的数据库可以容纳不超过大约 2e+13 行,并且只有在没有索引并且每行包含的数据很少的情况下。

  14. 最大数据库大小

    每个数据库都由一个或多个“页面”组成。在单个数据库中,每个页面的大小都相同,但不同数据库的页面大小可以是 512 到 65536(含)之间的 2 的幂。数据库文件的最大大小为 4294967294 页。在 65536 字节的最大页面大小下,这转化为大约 1.4e+14 字节(281 太字节,或 256 太比字节,或 281474 千兆字节或 256,000 千兆字节)的最大数据库大小。

    这个特定的上限未经测试,因为开发人员无法访问能够达到此限制的硬件。然而,测试确实验证了当数据库达到底层文件系统的最大文件大小时(通常远小于最大理论数据库大小)以及当数据库由于磁盘空间耗尽而无法增长时,SQLite 的行为是否正确和理智。

  15. 模式中的最大表数

    每个表和索引在数据库文件中至少需要一页。上一句中的“索引”是指使用CREATE INDEX语句显式创建的索引或由 UNIQUE 和 PRIMARY KEY 约束创建的隐式索引。由于数据库文件中的最大页数为 2147483646(略高于 20 亿),这也是模式中表和索引数量的上限。

    每当打开数据库时,都会扫描和解析整个模式,并将该模式​​的解析树保存在内存中。这意味着数据库连接启动时间和初始内存使用量与模式的大小成正比。