SQLite的限制性因素探讨

发表时间: 2019-09-13 16:58

本文中的“限制”是指不能超过的大小或数量。我们关心的是BLOB中的最大字节数或表中的最大列数。

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

不幸的是,无限制政策已被证明会产生问题。因为上限没有很好地定义,所以它们没有经过测试,并且在将SQLite推向极端时经常会发现错误(包括可能的安全漏洞)。出于这个原因,较新版本的SQLite具有明确定义的限制,并且这些限制作为测试套件的一部分进行测试。

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

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

字符串或BLOB的最大长度

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

-DSQLITE_MAX_LENGTH = 123456789

  1. 当前实现仅支持字符串或BLOB长度最大为2 31 -1或2147483647.并且一些内置函数(如hex())可能在此之前失败。在安全敏感的应用程序中,最好不要尝试增加最大字符串和blob长度。事实上,如果可能的话,你最好将最大字符串和blob长度降低到几百万的范围内。
  2. 在SQLite的INSERT和SELECT处理过程中,数据库中每行的完整内容被编码为单个BLOB。因此,SQLITE_MAX_LENGTH参数还确定一行中的最大字节数。
  3. 可以使用sqlite3_limit(db,SQLITE_LIMIT_LENGTH,size)接口在运行时降低最大字符串或BLOB长度。

最大列数

  1. SQLITE_MAX_COLUMN编译时参数用于设置上限:
  • 表中的列数
  • 索引中的列数
  • 视图中的列数
  • UPDATE语句的SET子句中的术语数
  • SELECT语句的结果集中的列数
  • GROUP BY或ORDER BY子句中的术语数
  • INSERT语句中的值的数量
  1. SQLITE_MAX_COLUMN的默认设置是2000.您可以在编译时将其更改为最大值32767.另一方面,许多有经验的数据库设计人员会认为,规范化的数据库永远不会在表中需要超过100列。
  2. 在大多数应用程序中,列数很少 - 几十个。SQLite代码生成器中有些地方使用的算法是O(N²),其中N是列数。因此,如果将SQLITE_MAX_COLUMN重新定义为非常大的数字并生成使用大量列的SQL,您可能会发现sqlite3_prepare_v2() 运行缓慢。
  3. 使用sqlite3_limit(db,SQLITE_LIMIT_COLUMN,size)接口可以在运行时降低最大列数。


SQL语句的最大长度

  1. SQL语句文本中的最大字节数限制为SQLITE_MAX_SQL_LENGTH,默认为1000000.您可以将此限制重新定义为SQLITE_MAX_LENGTH和1073741824中的较小值。
  2. 如果SQL语句的长度限制为一百万字节,那么显然您将无法通过在INSERT语句中嵌入它们作为文字来插入数百万字节字符串。但无论如何你不应该这样做。 为您的数据使用主机参数。准备这样的短SQL语句:

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

  1. 然后使用sqlite3_bind_XXXX()函数将大字符串值绑定到SQL语句。绑定的使用避免了在字符串中转义引号字符的需要,从而降低了SQL注入攻击的风险。它也运行得更快,因为不需要解析或复制大字符串。
  2. 可以使用sqlite3_limit(db,SQLITE_LIMIT_SQL_LENGTH,size)接口在运行时降低SQL语句的最大长度。

加入中的最大表数

  1. SQLite不支持包含超过64个表的连接。这个限制源于SQLite代码生成器在查询优化器中使用每个连接表一位的位图这一事实。
  2. SQLite使用高效的查询计划器算法 ,因此即使是大型连接也可以快速准备。因此,没有机制来提高或降低连接中表的数量限制。

表达式树的最大深度

  1. SQLite将表达式解析为树以进行处理。在代码生成期间,SQLite以递归方式遍历此树。因此,表达树的深度受到限制,以避免使用过多的堆栈空间。
  2. SQLITE_MAX_EXPR_DEPTH参数确定最大表达式树深度。如果值为0,则不强制执行限制。当前实现的默认值为1000。
  3. 如果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)是无操作。

函数的最大参数数

  1. SQLITE_MAX_FUNCTION_ARG参数确定可以传递给SQL函数的最大参数数。此限制的默认值为100. SQLite应该使用具有数千个参数的函数。但是,我们怀疑任何试图调用具有多个参数的函数的人都在尝试在使用SQLite的系统中找到安全漏洞,而不是做有用的工作,因此我们将此参数设置得相对较低。
  2. 函数的参数数量有时存储在带符号的字符中。因此,SQLITE_MAX_FUNCTION_ARG为127时存在硬上限。
  3. 可以使用sqlite3_limit(db,SQLITE_LIMIT_FUNCTION_ARG,size)接口在运行时降低函数中的最大参数数。

复合SELECT语句中的最大术语数

  1. 复合SELECT语句是由运算符UNION,UNION ALL,EXCEPT或INTERSECT连接的两个或多个SELECT语句。我们将复合SELECT中的每个SELECT语句称为“术语”。
  2. SQLite中的代码生成器使用递归算法处理复合SELECT语句。因此,为了限制堆栈的大小,我们限制了复合SELECT中的术语数量。最大术语数是SQLITE_MAX_COMPOUND_SELECT,默认为500.我们认为这是一个慷慨的分配,因为在实践中我们几乎从未看到复合选择中的术语数超过一位数。
  3. 使用sqlite3_limit(db,SQLITE_LIMIT_COMPOUND_SELECT,size)接口可以在运行时降低复合SELECT术语的最大数量。

LIKE或GLOB模式的最大长度

  1. 默认LIKE和GLOB中使用的模式匹配算法 对于某些病态情况,SQLite的实现可以表现出O(N²)性能(其中N是模式中的字符数)。为了避免能够指定自己的LIKE或GLOB模式的恶意攻击者的拒绝服务攻击,LIKE或GLOB模式的长度限制为SQLITE_MAX_LIKE_PATTERN_LENGTH个字节。此限制的默认值为50000.现代工作站甚至可以相对快速地评估50000字节的病态LIKE或GLOB模式。拒绝服务问题仅在模式长度达到数百万字节时才起作用。然而,由于大多数有用的LIKE或GLOB模式的长度最多为几十个字节,
  2. 可以使用sqlite3_limit(db,SQLITE_LIMIT_LIKE_PATTERN_LENGTH,size)接口在运行时降低LIKE或GLOB模式的最大长度。


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

  1. host 参数是SQL语句中的占位符,使用sqlite3_bind_XXXX()接口之一填充 。许多SQL程序员都熟悉使用问号(“?”)作为主机参数。SQLite还支持以“:”,“$”或“@”开头的命名主机参数和“?123”形式的编号主机参数。
  2. SQLite语句中的每个主机参数都分配了一个数字。数字通常以1开头,每个新参数增加1。但是,当使用“?123”形式时,主机参数号是问号后面的数字。
  3. SQLite分配空间以容纳1和所使用的最大主机参数号之间的所有主机参数。因此,包含主机参数(如?1000000000)的SQL语句将需要千兆字节的存储空间。这很容易淹没主机的资源。为防止过多的内存分配,主机参数号的最大值为SQLITE_MAX_VARIABLE_NUMBER,默认为999。
  4. 可以使用sqlite3_limit(db,SQLITE_LIMIT_VARIABLE_NUMBER,size)接口在运行时降低最大主机参数号。

最大触发递归深度

  1. SQLite限制了触发器的递归深度,以防止涉及递归触发器的语句使用无限量的内存。
  2. 在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才有意义。
  3. 默认的最大触发器递归深度为1000。

最大附加数据库数

  1. 该ATTACH语句是一个SQLite扩展,它允许两个或更多的数据库要关联到同一个数据库连接,并进行操作,就好像它们是一个单一的数据库。同时附加的数据库的数量限制为SQLITE_MAX_ATTACHED,默认情况下设置为10。附加数据库的最大数量不能超过125。
  2. 使用sqlite3_limit(db,SQLITE_LIMIT_ATTACHED,size)接口可以在运行时降低最大附加数据库数。

数据库文件中的最大页数

  1. SQLite能够限制数据库文件的大小,以防止数据库文件变得过大并占用过多的磁盘空间。SQLITE_MAX_PAGE_COUNT参数(通常设置为1073741823)是单个数据库文件中允许的最大页数。尝试插入会导致数据库文件大于此值的新数据将返回SQLITE_FULL。
  2. SQLITE_MAX_PAGE_COUNT的最大可能设置是2147483646.当使用最大页面大小65536时,这会使SQLite数据库的最大大小约为140 TB。
  3. 该 max_page_count PRAGMA可以用来提高或降低在运行时此限制。

表中的最大行数

  1. 表中的理论最大行数是2 64(18446744073709551616或大约1.8e + 19)。此限制无法访问,因为将首先达到最大数据库大小为140太字节。一个140 TB的数据库可以容纳不超过大约1e + 13行,然后只有没有索引且每行包含非常少的数据。

最大数据库大小

  1. 每个数据库都包含一个或多个“页面”。在单个数据库中,每个页面的大小相同,但不同的数据库的页面大小可以是512到65536之间的两个页面大小。数据库文件的最大大小为2147483646页。在最大页面大小为65536字节时,这意味着最大数据库大小约为1.4e + 14字节(140太字节,或128 tebibytes,或140,000千兆字节或128,000千字节)。
  2. 由于开发人员无法访问能够达到此限制的硬件,因此未经测试此特定上限。但是,当数据库达到基础文件系统的最大文件大小(通常远小于最大理论数据库大小)并且数据库由于磁盘空间耗尽而无法增长时,测试会验证SQLite是否正确且正确地运行。

模式中的最大表数

  1. 每个表和索引都需要数据库文件中至少有一个页面。前一句中的“索引”表示使用CREATE INDEX语句显式创建的索引或由UNIQUE和PRIMARY KEY约束创建的隐式索引。由于数据库文件中的最大页数为2147483646(略多于20亿),因此这也是模式中表和索引数的上限。
  2. 无论何时打开数据库,都会扫描并解析整个模式,并在内存中保存模式的解析树。这意味着数据库连接启动时间和初始内存使用量与架构的大小成正比。