1. 引言
用户代理需要在本地存储大量对象,以满足 Web 应用的离线数据需求。[WEBSTORAGE] 对于存储键值对很有用。但是,它不提供键的顺序检索、值的高效搜索,也不支持为同一个键存储多个重复值。
本规范提供了一个具体的 API 来执行高级键值数据管理,这是大多数复杂查询处理器的核心。它通过使用事务性数据库来存储键及其对应的值(每个键可以有一个或多个),并提供了一种以确定性顺序遍历键的方法。这通常通过使用持久化的 B 树数据结构来实现,该结构在插入、删除以及对大量数据记录进行顺序遍历方面被认为是高效的。
2. 构造
名称是一个等同于 DOMString 的字符串;即任意长度的 16 位代码单元的任意序列,包括空字符串。名称总是作为不透明的 16 位代码单元序列进行比较。
排序名称列表是一个包含按 16 位代码单元升序排序的名称的列表。
详细信息
这与对数组 字符串执行的 Array.prototype.sort 相匹配。此排序顺序比较每个字符串中的 16 位代码单元,产生一种高效、一致且确定的排序顺序。生成的列表将不会匹配任何特定的字母表或字典顺序,特别是对于由代理对表示的代码点。
2.1. 数据库
每个源都有一个关联的数据库集合。数据库拥有零个或多个保存数据库中存储数据的对象存储。
2.1.1. 数据库连接
脚本不会直接与数据库交互。相反,脚本通过连接进行间接访问。连接对象可用于操作该数据库的对象。这也是获取该数据库的事务的唯一方式。
打开数据库的操作会创建一个连接。在任何给定时间,可能存在对给定数据库的多个连接。
连接有一个版本,在连接创建时设置。除非升级被终止,否则它在连接的生命周期内保持不变,在这种情况下它会被设置为数据库的前一个版本。一旦连接关闭,版本就不会改变。
每个连接都有一个关闭挂起标志,初始为未设置。
当连接最初创建时,它处于已打开状态。连接可以通过多种方式关闭。如果创建连接的执行上下文被销毁(例如由于用户导航离开该页面),则连接关闭。连接也可以使用关闭数据库连接的步骤显式关闭。当连接关闭时,如果关闭挂起标志尚未设置,则始终会将其设置为已设置。
连接可能会在特殊情况下被用户代理关闭,例如由于无法访问文件系统、权限更改或清除源的存储。如果发生这种情况,用户代理必须运行带有连接并设置了 强制标志 的关闭数据库连接步骤。
连接有一个对象存储集,在连接创建时,它被初始化为关联数据库中的对象存储集合。除非运行升级事务,否则该集合的内容保持不变。
2.2. 对象存储
对象存储是用于在数据库中存储数据的主要存储机制。
每个数据库都有一组对象存储。对象存储集可以更改,但只能使用升级事务,即响应 upgradeneeded 事件时。当创建一个新数据库时,它不包含任何对象存储。
对象存储有一个记录列表,用于保存存储在对象存储中的数据。每条记录由一个键和一个值组成。列表根据键按升序进行排序。在给定的对象存储中,永远不能有多个具有相同键的记录。
对象存储有一个名称,是一个名称。在任何时候,该名称在其所属的数据库中都是唯一的。
对象存储可选地具有一个键路径。如果对象存储具有键路径,则称其使用内联键。否则,称其使用离线键。
2.2.1. 对象存储句柄
脚本不会直接与对象存储交互。相反,在事务内,脚本通过对象存储句柄进行间接访问。
对象存储句柄有一个关联的对象存储和一个关联的事务。多个句柄可以与不同事务中的同一个对象存储相关联,但在一个事务内,必须只有一个与特定对象存储相关联的对象存储句柄。
对象存储句柄有一个索引集,在对象存储句柄创建时,它被初始化为引用关联对象存储的索引集合。除非运行升级事务,否则该集合的内容保持不变。
2.3. 值
每条记录都与一个值相关联。用户代理必须支持任何可序列化对象。这包括简单类型(如字符串基本值和Date对象)以及Object和Array实例、File对象、Blob对象、ImageData对象等。记录值按值存储和检索,而不是按引用;后续对值的更改对存储在数据库中的记录没有影响。
记录值是 StructuredSerializeForStorage 操作输出的记录。
2.4. 键
为了高效检索存储在索引数据库中的记录,每条记录都根据其键进行组织。
键有一个关联的类型,为以下之一:数字、日期、字符串、二进制 或 数组。
键还有一个关联的值,它将是:如果类型是 数字 或 日期,则为 unrestricted double;如果类型是 字符串,则为 DOMString;如果类型是 二进制,则为 octet 列表;如果类型是 数组,则为其他键的列表。
ECMAScript [ECMA-262] 值可以通过遵循将值转换为键的步骤转换为键。
数组键是一个类型为 数组 的键。数组键的子键是数组键的值列表的成员。
要比较两个键 a 和 b,请运行以下步骤:
-
令 ta 为 a 的类型。
-
令 tb 为 b 的类型。
-
如果 ta 为 数组 且 tb 为 二进制、字符串、日期 或 数字,则返回 1。
-
如果 tb 为 数组 且 ta 为 二进制、字符串、日期 或 数字,则返回 -1。
-
如果 ta 为 二进制 且 tb 为 字符串、日期 或 数字,则返回 1。
-
如果 tb 为 二进制 且 ta 为 字符串、日期 或 数字,则返回 -1。
-
如果 ta 为 字符串 且 tb 为 日期 或 数字,则返回 1。
-
如果 tb 为 字符串 且 ta 为 日期 或 数字,则返回 -1。
-
如果 ta 为 日期 且 tb 为 数字,则返回 1。
-
如果 tb 为 日期 且 ta 为 数字,则返回 -1。
-
断言:ta 和 tb 相等。
-
令 va 为 a 的值。
-
令 vb 为 b 的值。
-
切换 ta:
- number
- 日期
-
-
如果 va 大于 vb,则返回 1。
-
如果 va 小于 vb,则返回 -1。
-
返回 0。
-
- string
-
-
令 length 为 va 的长度和 vb 的长度中的较小者。
-
令 i 为 0。
-
当 i 小于 length 时:
-
令 u 为 va 在索引 i 处的代码单元。
-
令 v 为 vb 在索引 i 处的代码单元。
-
如果 u 大于 v,则返回 1。
-
如果 u 小于 v,则返回 -1。
-
将 i 增加 1。
-
-
如果 va 的长度大于 vb 的长度,则返回 1。
-
如果 va 的长度小于 vb 的长度,则返回 -1。
-
返回 0。
-
- 二进制:
- array
如果运行比较两个键步骤的结果为 1,则键 a 大于 键 b。
如果运行比较两个键步骤的结果为 -1,则键 a 小于 键 b。
如果运行比较两个键步骤的结果为 0,则键 a 等于 键 b。
2.5. 键路径
键路径是一个字符串或字符串列表,定义了如何从值中提取键。有效键路径是以下之一:
-
一个空字符串。
-
一个标识符,即匹配 ECMAScript 语言规范 [ECMA-262] 中 IdentifierName 产生式的字符串。
-
由两个或多个以句点(U+002E FULL STOP)分隔的标识符组成的字符串。
-
仅包含符合上述要求的字符串的非空列表。
键路径值只能从由 StructuredSerializeForStorage 显式复制的属性以及以下特定于类型的属性访问:
| 类型 | 属性 |
|---|---|
Blob
| size, type |
File
| name, lastModified, lastModifiedDate |
| Array | length
|
| 字符串 (String) | length
|
2.6. 索引
有时通过键以外的其他方式检索对象存储中的记录很有用。索引允许使用对象存储记录中值的属性来查找对象存储中的记录。
索引是一种专门的持久化键值存储,并具有引用的对象存储。索引有一个记录列表,用于保存存储在索引中的数据。当引用的对象存储中的记录被插入、更新或删除时,索引中的记录会自动填充。可以有多个引用同一个对象存储的索引,其中对对象存储的更改会导致所有此类索引得到更新。
索引记录中的值总是索引的引用的对象存储中键的值。键使用键路径从引用的对象存储的值中推导出来。如果索引引用的对象存储中具有键 X 的给定记录具有值 A,并且在 A 上评估索引的键路径得出的结果为 Y,则索引将包含一条键为 Y、值为 X 的记录。
索引中的记录被称为具有引用值。这是索引引用的对象存储中键等于索引记录值的记录的值。因此在上面的示例中,索引中键为 Y、值为 X 的记录具有值为 A 的引用值。
索引中的记录总是根据记录的键进行排序。然而,与对象存储不同,给定的索引可以包含多条具有相同键的记录。此类记录还会根据索引记录的值(即引用对象存储中记录的键)进行额外排序。
索引有一个名称,是一个名称。在任何时候,该名称在索引引用的对象存储内都是唯一的。
索引有一个唯一标志。设置此标志后,索引会强制要求索引中没有两条记录具有相同的键。如果尝试插入或修改索引引用的对象存储中的一条记录,使得在记录的新值上评估索引的键路径得到的结果已在索引中存在,则对对象存储的修改尝试将失败。
索引有一个多条目标志。当评估索引的键路径的结果产生一个数组键时,此标志会影响索引的行为。如果多条目标志未设置,则将单个记录(其键是一个数组键)添加到索引中。如果多条目标志为 true,则为每个子键添加一条记录到索引中。
2.6.1. 索引句柄
脚本不会直接与索引交互。相反,在事务内,脚本通过索引句柄进行间接访问。
2.7. 事务
事务用于与数据库中的数据交互。每当对数据库进行读写操作时,都是通过事务完成的。
事务提供了一些防止应用和系统故障的保护措施。事务可用于存储多条数据记录或有条件地修改某些数据记录。事务代表了一组原子且持久的数据访问和数据变更操作。
所有事务都通过连接创建,这是事务的连接。
事务有一个作用域,它决定了事务可以与之交互的对象存储。事务的作用域在事务的整个生命周期内保持不变。
事务有一个模式,决定了可以在该事务上执行哪些类型的交互。模式在事务创建时设置,并在事务的整个生命周期内保持不变。事务的模式是以下之一:
"readonly"- 事务只允许读取数据。此类事务不能进行修改。这有一个好处:即使作用域重叠(即使用相同的对象存储),也可以同时运行多个只读事务。这种类型的事务可以在数据库打开后的任何时候创建。
"readwrite"- 事务允许读取、修改和删除现有对象存储中的数据。但是,不能添加或删除对象存储和索引。如果作用域重叠,则多个
"readwrite"事务不能同时运行,因为这意味着它们可以在事务进行过程中修改彼此的数据。这种类型的事务可以在数据库打开后的任何时候创建。 "versionchange"- 事务允许读取、修改和删除现有对象存储中的数据,还可以创建和删除对象存储和索引。这是唯一可以执行此类操作的事务类型。这种类型的事务不能手动创建,而是当
upgradeneeded事件触发时自动创建。
事务有一个活动标志,决定是否可以对该事务发出新的请求。如果事务的活动标志已设置,则称该事务为活动。
只读事务是模式为 "readonly" 的事务。
读/写事务是模式为 "readwrite" 的事务。
2.7.1. 事务生命周期
事务应尽可能简短。下述的自动提交功能对此进行了鼓励。
事务的生命周期如下:
-
实现必须允许只要活动标志被设置,就可以对该事务发出请求。即使事务尚未启动,情况也是如此。在事务启动之前,实现不得执行这些请求;但是,实现必须跟踪请求及其顺序。请求只能在事务活动时才能对该事务发出。如果尝试在事务不活动时对该事务发出请求,则实现必须抛出 “
TransactionInactiveError”DOMException来拒绝该尝试。 -
一旦事务已启动,实现即可开始执行对该事务发出的请求。除非另有定义,否则请求必须按照对事务发出的顺序执行。同样,其结果必须按照请求对特定事务发出的顺序返回。对于在不同事务中发出的请求结果返回的顺序,没有保证。同样,事务模式确保对不同事务发出的两个请求可以以任何顺序执行,而不会影响存储在数据库中的最终数据。
-
事务可以在其完成之前的任何时间终止,即使该事务当前未处于活动状态或尚未启动。当事务终止时,实现必须撤销(回滚)在事务期间对数据库所做的任何更改。这包括对对象存储内容的更改以及对象存储和索引的添加与删除。
-
事务可能因与特定请求无关的原因而失败。例如,由于提交事务时的 IO 错误,或由于遇到无法将超出配额部分归因于特定请求的配额限制。在这种情况下,实现必须使用该事务作为 transaction,并使用适当的错误类型作为 error,运行终止事务的步骤。例如,如果超出配额,则应使用 “
QuotaExceededError”DOMException作为 error,如果发生 IO 错误,则应使用 “UnknownError”DOMException作为 error。 -
当事务已启动且无法再变为 活动状态 (active) 时,实现必须尝试提交 (commit)该事务,前提是该事务尚未被中止 (aborted)。这通常发生在所有针对该事务的请求已执行完毕、其返回结果已处理,且没有新请求被提交给该事务之后。当事务被提交时,实现必须原子地将针对该事务的请求所作的任何数据库 (database) 变更写入磁盘。也就是说,要么必须写入所有变更,或者如果发生错误(例如磁盘写入错误),实现不得向数据库写入任何变更。如果发生此类错误,实现必须通过遵循中止事务 (abort a transaction) 的步骤来中止事务;否则,它必须通过遵循提交事务 (commit a transaction) 的步骤来提交事务。
-
当事务被提交 (committed) 或中止 (aborted) 时,称该事务已结束 (finished)。如果事务无法结束(例如由于实现崩溃或用户执行了显式取消操作),实现必须中止该事务。
以下约束定义了何时可以启动 (started) 一个事务 (transaction)。
-
允许多个只读事务 (read-only transactions) 并发运行,即使这些事务的作用域 (scope) 重叠并包含相同的对象存储 (object stores)。只要只读事务在运行,实现通过该事务所创建的请求 (requests) 所返回的数据必须保持不变。即,对同一数据片段的两个读取请求必须产生相同的结果,无论是数据已找到并返回该数据的情况,还是未找到数据并指示缺少数据的情况。
-
同样,实现必须确保读写事务仅受通过该事务本身对对象存储所做变更的影响。例如,实现必须确保其他事务不会修改读写事务作用域内的对象存储的内容。实现还必须确保如果读写事务成功完成,使用该事务写入对象存储的变更可以被提交到数据库而不会发生合并冲突。实现不得因合并冲突而中止事务。
-
如果有多个读写事务试图访问同一个对象存储(即它们具有重叠的作用域),则先创建的事务必须首先获得对该对象存储的访问权限。根据前一段的要求,这也意味着在事务结束之前,它是唯一拥有该对象存储访问权限的事务。
-
任何在读写事务之后创建的事务都必须能看到该读写事务所写入的变更。因此,如果创建了一个读写事务 A,稍后创建了另一个事务 B,并且这两个事务具有重叠的作用域,那么 B 必须能看到对属于该重叠作用域内的任何对象存储所做的任何变更。根据前一段的要求,这也意味着在事务 A 结束之前,事务 B 无法访问该重叠作用域内的任何对象存储。
-
用户代理必须确保事务之间具有合理的公平性,以防止资源匮乏(starvation)。例如,如果连续启动多个只读事务,实现不得无限期地阻止待处理的读写事务启动。
要清理 Indexed Database 事务,请对每个事务,其 清理事件循环 (cleanup event loop) 与当前事件循环 (event loop) 相匹配的,执行以下步骤。
-
取消设置事务的活动标志 (active flag)。
2.7.2. 升级事务 (Upgrade Transactions)
升级事务 (upgrade transaction) 是模式 (mode) 为 "versionchange" 的事务。
当连接 (connection) 到数据库并指定了大于当前版本 (version) 的版本号时,在执行运行升级事务的步骤后,会自动创建升级事务。此事务将在 upgradeneeded 事件处理程序内处于活动状态。
2.8. 请求 (Requests)
对数据库的每次异步操作都是使用请求 (request)完成的。每个请求代表一个操作。
请求有一个完成标志 (done flag),初始为未设置状态。
请求有一个源 (source)对象。
请求有一个结果 (result)和一个错误 (error),在设置完成标志之前,两者都不可访问。
请求有一个事务 (transaction),初始为 null。当使用异步执行请求的步骤将请求放置 (placed)到事务中时,此属性将被设置。
发出请求时,会返回一个新的请求,其完成标志为未设置状态。如果请求成功完成,则设置完成标志,将结果设置为请求的结果,并向请求触发类型为 success 的事件。
如果在执行操作期间发生错误,则设置完成标志,将错误设置为该错误,并向请求触发类型为 error 的事件。
请求的 获取父级 (get the parent) 算法返回该请求的事务。
2.8.1. 打开请求 (Open Requests)
打开请求 (open request) 是一种特殊类型的请求,用于打开连接或删除数据库。除了 success 和 error 事件外,blocked 和 upgradeneeded 也可能在打开请求上触发,以指示进度。
打开请求的事务为 null,除非已触发 upgradeneeded 事件。
打开请求在连接队列 (connection queue)中处理。该队列包含所有与特定源 (origin) 和名称 (name) 关联的打开请求。添加到连接队列的请求按顺序处理,每个请求必须运行到完成,然后才能处理下一个请求。一个打开请求可能会阻塞在其他连接上,需要这些连接关闭,请求才能完成并允许处理后续请求。
2.9. 键范围 (Key Range)
可以使用键 (keys) 或键范围 (key ranges) 从对象存储和索引中检索记录。键范围是某种用于键的数据类型上的连续区间。
键范围有一个关联的下界 (lower bound)(null 或一个键)。
键范围有一个关联的上界 (upper bound)(null 或一个键)。
键范围有一个关联的下界开放标志 (lower open flag)。除非另有说明,否则它处于未设置状态。
键范围有一个关联的上界开放标志 (upper open flag)。除非另有说明,否则它处于未设置状态。
键范围的下界可以等于 (equal to) 其上界。键范围的下界不得大于 (greater than) 其上界。
仅包含 (containing only) key 的键范围的下界和上界都等于 key。
如果满足以下两个条件,则key 处于键范围中 (in a key range)
-
下界为 null,或者它小于 (less than) key,或者它既等于 (equal to) key 且下界开放标志未设置。
-
上界为 null,或者它大于 (greater than) key,或者它既等于 (equal to) key 且上界开放标志未设置。
无界键范围 (unbounded key range) 是一个键范围,其下界和上界均等于 null。所有键都处于无界键范围中。
将 value 和可选的 null disallowed flag (禁止 null 标志) 转换为键范围 的步骤如下:
-
如果 value 是一个键范围,则返回 value。
-
如果 value 为 undefined 或 null,则如果设置了 null disallowed flag,则抛出 (throw) "
DataError"DOMException,否则返回一个无界键范围。 -
令 key 为运行将 value 转换为键 的步骤的结果。重新抛出任何异常。
-
如果 key 无效,则抛出 "
DataError"DOMException。 -
返回一个仅包含 key 的键范围。
2.10. 游标 (Cursor)
游标 (cursor) 用于以特定方向迭代索引或对象存储中的一系列记录。
游标有一个源 (source),指示哪个索引或对象存储与游标正在迭代的记录相关联。
游标有一个方向 (direction),决定了迭代时是按单调递增还是单调递减的记录键移动,以及在迭代索引时是否跳过重复值。游标的方向也决定了游标的初始位置是在其源的开头还是结尾。游标的方向是以下之一:
"next"- 此方向使游标从源的开头打开。迭代时,游标应以键的单调递增顺序产生所有记录,包括重复项。
"nextunique"- 此方向使游标从源的开头打开。迭代时,游标不应产生具有相同键的记录,但在其他方面应以键的单调递增顺序产生所有记录。对于每个具有重复值的键,只产生第一条记录。当源是对象存储或已设置唯一性标志 (unique flag) 的索引时,此方向的行为与
"next"完全相同。 "prev"- 此方向使游标从源的结尾打开。迭代时,游标应以键的单调递减顺序产生所有记录,包括重复项。
"prevunique"- 此方向使游标从源的结尾打开。迭代时,游标不应产生具有相同键的记录,但在其他方面应以键的单调递减顺序产生所有记录。对于每个具有重复值的键,只产生第一条记录。当源是对象存储或已设置唯一性标志的索引时,此方向的行为与
"prev"完全相同。
游标在其范围内有一个位置 (position)。游标正在迭代的记录列表可能会在游标的全部范围被迭代之前发生变化。为了处理这个问题,游标维持其位置时,不是以索引形式,而是以上一次返回记录的键形式。对于前向迭代的游标,下次请求迭代到下一条记录时,它返回大于上一次返回的键的最小键的记录。对于后向迭代的游标,情况正好相反,它返回小于上一次返回的键的最大键的记录。
对于迭代索引的游标,情况要复杂一些,因为多条记录可以具有相同的键,因此它们也按值 (value) 排序。当迭代索引时,游标还有一个对象存储位置 (object store position),它指示索引中先前找到的记录的值。在寻找下一条合适记录时,位置和对象存储位置都会被使用。
游标有一个获得值标志 (got value flag)。当此标志未设置时,游标要么正在加载下一个值,要么已到达其范围的末尾。当它已设置时,表示游标当前持有一个值,并准备好迭代到下一个值。
如果游标的源是对象存储,则游标的有效对象存储 (effective object store)就是该对象存储,而游标的有效键 (effective key)就是该游标的位置。如果游标的源是索引,则游标的有效对象存储就是该索引所引用的对象存储,而有效键是该游标的对象存储位置。
游标还有一个仅键标志 (key only flag),指示游标的值是否通过 API 公开。除非另有说明,否则它处于未设置状态。
2.11. 键生成器 (Key Generators)
创建对象存储时,可以指定使用键生成器 (key generator)。如果未另行指定,键生成器用于为插入到对象存储中的记录生成键。
键生成器有一个当前编号 (current number)。当前编号始终是一个小于或等于 253 (9007199254740992) + 1 的正整数。键生成器当前编号的初始值为 1,在关联的对象存储创建时设置。当前编号随着键的生成而递增,并且可以通过使用显式键更新为特定值。
修改键生成器的当前编号被视为数据库操作的一部分。这意味着如果操作失败且操作被还原,当前编号将还原为操作开始前的值。这既适用于由于使用键生成器导致当前编号增加 1 而发生的修改,也适用于由于使用在存储记录的调用中指定的键值来存储记录而发生的修改。
同样,如果事务被中止,事务作用域内每个对象存储的键生成器的当前编号将还原为事务开始前的值。
键生成器的当前编号从不减小,除非是因为数据库操作被还原。从对象存储中删除记录绝不会影响对象存储的键生成器。即使清除对象存储中的所有记录(例如使用 clear() 方法),也不会影响该对象存储键生成器的当前编号。
要为对象存储 store 生成键,请执行以下步骤:
当存储记录且在调用存储记录时指定了键时,关联的键生成器可能会被更新。
要为对象存储 store 可能更新键生成器(使用 key),请执行以下步骤:
当键生成器的当前编号超过值 253 (9007199254740992) 时,任何后续尝试使用该键生成器生成新键的行为都将导致 "ConstraintError" DOMException。仍然可以通过指定显式键将记录插入到对象存储中,但是对于此类记录,再次使用键生成器的唯一方法是删除对象存储并创建一个新的。
实际的结果是,为对象存储生成的第一个键始终是 1(除非首先插入了更高的数字键),并且为对象存储生成的键始终是大于存储中最高数字键的正整数。同一个键永远不会为同一个对象存储生成两次,除非事务被回滚。
3. 异常 (Exceptions)
本文档中使用的每个异常都是具有特定类型的 DOMException。异常类型和属性(例如旧版代码值)在 [WEBIDL] 中定义。
下表列出了本文档中使用的 DOMException,以及对异常类型用法的描述。
| 类型 | 描述 |
|---|---|
AbortError
| 请求已中止。 |
ConstraintError
| 事务中的变异操作失败,因为约束未满足。 |
DataCloneError
| 存储的数据无法通过内部结构化克隆算法进行克隆。 |
DataError
| 提供给操作的数据不符合要求。 |
InvalidAccessError
| 对对象执行了无效操作。 |
InvalidStateError
| 在不允许调用的对象上调用了某个操作,或者在不允许的时间调用了该操作,或者请求是在已被删除或移除的源对象上发出的。 |
NotFoundError
| 操作失败,因为找不到请求的数据库对象。 |
QuotaExceededError
| 操作失败,因为没有足够的剩余存储空间,或者已达到存储配额,且用户拒绝为该数据库提供更多空间。 |
SyntaxError
| keyPath 参数包含无效的键路径。 |
ReadOnlyError
| 尝试在只读事务中进行变异操作。 |
TransactionInactiveError
| 针对当前不活跃或已结束的事务发出了请求。 |
UnknownError
| 操作失败的原因与数据库本身无关,且未被任何其他错误覆盖。 |
VersionError
| 尝试使用低于现有版本的版本打开数据库。 |
4. API
API 方法在不阻塞调用线程的情况下返回。所有异步操作会立即返回一个 IDBRequest 实例。该对象最初不包含任何关于操作结果的信息。一旦信息可用,就会在请求上触发一个事件,并且该信息可以通过 IDBRequest 实例的属性获得。
这些任务的 任务源 (task source) 是 数据库访问任务源 (database access task source)。
4.1. IDBRequest 接口
IDBRequest 接口提供了使用 事件处理程序 IDL 属性 [HTML52] 访问针对数据库和数据库对象的异步请求结果的方法。
每个用于发出异步请求的方法都会返回一个 IDBRequest 对象,该对象通过事件与请求应用程序通信。这种设计意味着在任何数据库上都可以同时激活任意数量的请求。
[Exposed=(Window,Worker)] interfaceIDBRequest: EventTarget { readonly attribute any result; readonly attribute DOMException? error; readonly attribute (IDBObjectStore or IDBIndex or IDBCursor)? source; readonly attribute IDBTransaction? transaction; readonly attribute IDBRequestReadyState readyState; // Event handlers: attribute EventHandler onsuccess; attribute EventHandler onerror; }; enumIDBRequestReadyState{"pending","done"};
- request .
result - 当请求完成时,返回结果,如果请求失败则返回
undefined。如果请求仍处于挂起状态,则抛出 "InvalidStateError"DOMException。 - request .
error - 当请求完成时,返回错误(一个
DOMException),如果请求成功则返回 null。如果请求仍处于挂起状态,则抛出 "InvalidStateError"DOMException。 - request .
source - 返回发出请求的
IDBObjectStore、IDBIndex或IDBCursor;如果是打开请求,则返回 null。 - request .
transaction - 返回发出请求所在的
IDBTransaction。如果这是一个打开请求,则在运行期间返回升级事务,否则返回 null。 - request .
readyState - 在请求完成之前返回
"pending",完成后返回"done"。
result 属性的 getter 如果完成标志未设置,则必须抛出 "InvalidStateError" DOMException。否则,属性的 getter 必须返回请求的结果,或者如果请求导致错误则返回 undefined。
error 属性的 getter 如果完成标志未设置,则必须抛出 "InvalidStateError" DOMException。否则,属性的 getter 必须返回请求的错误,或者如果未发生错误则返回 null。
source 属性的 getter 必须返回请求的源,如果未设置源则返回 null。
transaction 属性的 getter 必须返回请求的事务。对于某些请求(例如从 open() 返回的请求),此属性可以为 null。
readyState 属性的 getter 如果完成标志未设置,则必须返回 "pending",否则返回 "done"。
onsuccess 属性是 success 事件的事件处理程序。
onerror 属性是 error 事件的事件处理程序。
IDBDatabase 上返回打开请求的方法使用扩展接口,以允许监听 blocked 事件和 upgradeneeded 事件。
[Exposed=(Window,Worker)]
interface IDBOpenDBRequest : IDBRequest {
// Event handlers:
attribute EventHandler onblocked;
attribute EventHandler onupgradeneeded;
};
onblocked 属性是 blocked 事件的事件处理程序。
onupgradeneeded 属性是 upgradeneeded 事件的事件处理程序。
4.2. 事件接口 (Event interfaces)
本规范使用以下自定义接口触发事件:
[Exposed=(Window,Worker),Constructor(DOMStringtype, optional IDBVersionChangeEventIniteventInitDict)] interfaceIDBVersionChangeEvent: Event { readonly attribute unsigned long long oldVersion; readonly attribute unsigned long long? newVersion; }; dictionaryIDBVersionChangeEventInit: EventInit { unsigned long longoldVersion= 0; unsigned long long?newVersion= null; };
oldVersion 属性 getter 返回数据库的前一个版本。
newVersion 属性 getter 返回数据库的新版本;如果数据库正在被删除,则返回 null。请参阅运行升级事务的步骤。
事件根据 [DOM41] 中的 构建事件 (Constructing events) 进行构建。
要在给定 oldVersion 和 newVersion 的情况下,向 target 触发名为 e 的 版本变更事件 (version change event),请执行以下步骤:
-
令 event 为使用
IDBVersionChangeEvent创建事件的结果。 -
将 event 的
type属性设置为 e。 -
将 event 的
bubbles和cancelable属性设置为 false。 -
将 event 的
oldVersion属性设置为 oldVersion。 -
将 event 的
newVersion属性设置为 newVersion。 -
令 legacyOutputDidListenersThrowFlag 为未设置状态。
-
在 target 上分发 (Dispatch) event,并带有 legacyOutputDidListenersThrowFlag。
-
返回 legacyOutputDidListenersThrowFlag。
4.3. IDBFactory 接口
数据库对象通过 IDBFactory 接口上的方法进行访问。在支持 Indexed DB 操作的环境的全局作用域中,存在一个实现此接口的单一对象。
partial interface WindowOrWorkerGlobalScope { [SameObject] readonly attribute IDBFactory indexedDB; };
indexedDB 属性为应用程序提供了访问索引数据库功能的机制。
[Exposed=(Window,Worker)] interfaceIDBFactory{ [NewObject] IDBOpenDBRequest open(DOMStringname, optional [EnforceRange] unsigned long longversion); [NewObject] IDBOpenDBRequest deleteDatabase(DOMStringname); short cmp(anyfirst, anysecond); };
- request = indexedDB .
open(name) - 尝试使用当前版本打开与已命名数据库的连接,如果它尚不存在,则版本号为 1。如果请求成功,request 的
result将是该连接。 - request = indexedDB .
open(name, version) - 尝试以指定的版本打开指定名称的数据库连接。如果数据库已存在且版本较低,且存在未响应
versionchange事件而关闭的打开连接,则该请求将被阻塞,直到它们全部关闭,随后将执行升级。如果数据库已存在且版本较高,则请求将失败。如果请求成功,则 request 的result将是该连接。 - request = indexedDB .
deleteDatabase(name) - 尝试删除指定名称的数据库。如果数据库已存在,且存在未响应
versionchange事件而关闭的打开连接,则该请求将被阻塞,直到它们全部关闭。如果请求成功,则 request 的result将为 null。
open(name, version) 方法在被调用时,必须执行以下步骤
-
令 origin 为用于访问此
IDBFactory的全局作用域的源(origin)。 -
如果 origin 是一个不透明源(opaque origin),则抛出一个 "
SecurityError"DOMException并中止这些步骤。 -
令 request 为一个新的打开请求。
-
执行以下步骤并行(in parallel)处理
-
返回一个用于 request 的新
IDBOpenDBRequest对象。
deleteDatabase(name) 方法在被调用时,必须执行以下步骤
-
令 origin 为用于访问此
IDBFactory的全局作用域的源(origin)。 -
如果 origin 是一个不透明源,则抛出一个 "
SecurityError"DOMException并中止这些步骤。 -
令 request 为一个新的打开请求。
-
执行以下步骤并行处理
-
令 result 为执行删除数据库步骤的结果,传入 origin、name 和 request。
-
排队一个任务以运行以下步骤
-
如果 result 是一个错误,将 request 的 error 设置为 result,设置 request 的 完成标志,并向 request 触发一个事件,命名为
error,其bubbles和cancelable属性初始化为 true。 -
否则,将 request 的 result 设置为 undefined,设置 request 的 完成标志,并向 request 触发一个版本变更事件,命名为
success,传入 result 和 null。为什么不使用触发成功事件或触发错误事件的步骤?
因为此时请求没有关联事务,所以那些在分发前激活关联事务并在分发后停用事务的步骤不适用。此外,此处的
success事件是一个IDBVersionChangeEvent,其中包含oldVersion和newVersion详情。
-
-
-
返回一个用于 request 的新
IDBOpenDBRequest对象。
- result = indexedDB .
cmp(key1, key2) - 将两个值作为键进行比较。如果 key1 排在 key2 之前返回 -1,如果 key2 排在 key1 之前返回 1,如果键相等则返回 0。
如果任一输入不是有效的键,则抛出 "
DataError"DOMException。
cmp(first, second) 方法在被调用时,必须执行以下步骤
-
令 a 为执行将值转换为键步骤对 first 进行处理的结果。重新抛出任何异常。
-
如果 a 无效,则抛出一个 "
DataError"DOMException。 -
令 b 为执行将值转换为键步骤对 second 进行处理的结果。重新抛出任何异常。
-
如果 b 无效,则抛出一个 "
DataError"DOMException。 -
返回执行比较两个键步骤对 a 和 b 进行处理的结果。
4.4. IDBDatabase 接口
IDBDatabase 接口代表对数据库的连接。
如果 IDBDatabase 对象关联连接的关闭挂起标志未设置,且其注册了一个或多个类型为 abort、error 或 versionchange 的事件监听器,则该对象不得被垃圾回收。如果一个 IDBDatabase 对象被垃圾回收,则关联的连接必须被关闭。
[Exposed=(Window,Worker)] interfaceIDBDatabase: EventTarget { readonly attribute DOMString name; readonly attribute unsigned long long version; readonly attribute DOMStringList objectStoreNames; [NewObject] IDBTransaction transaction((DOMString or sequence<DOMString>)storeNames, optional IDBTransactionModemode= "readonly"); void close(); [NewObject] IDBObjectStore createObjectStore(DOMStringname, optional IDBObjectStoreParametersoptions); void deleteObjectStore(DOMStringname); // Event handlers: attribute EventHandler onabort; attribute EventHandler onclose; attribute EventHandler onerror; attribute EventHandler onversionchange; }; dictionaryIDBObjectStoreParameters{ (DOMString or sequence<DOMString>)?keyPath= null; booleanautoIncrement= false; };
name 属性的 getter 必须返回已连接数据库的名称。即使在连接上设置了关闭挂起标志,该属性也必须返回此名称。换句话说,此属性的值在 IDBDatabase 实例的生命周期内保持不变。
version 属性的 getter 必须返回此连接的版本。
这与数据库的版本相同吗?
只要连接处于打开状态,它就与已连接数据库的版本相同。但一旦连接已关闭,此属性将不会反映稍后通过升级事务所做的更改。- connection .
objectStoreNames - 返回数据库中对象存储的名称列表。
- store = connection .
createObjectStore(name [, options]) - 创建一个具有给定 name 和 options 的新对象存储,并返回一个新
IDBObjectStore。如果不是在升级事务内调用,则抛出 "
InvalidStateError"DOMException。 - connection .
deleteObjectStore(name) - 删除具有给定 name 的对象存储。
如果不是在升级事务内调用,则抛出 "
InvalidStateError"DOMException。
objectStoreNames 属性的 getter 必须返回一个 DOMStringList,该列表关联了此连接的对象存储集合中对象存储名称的排序名称列表。
这与数据库的对象存储名称相同吗?
只要连接处于打开状态,它就与已连接数据库的对象存储名称相同。但一旦连接已关闭,此属性将不会反映稍后通过升级事务所做的更改。createObjectStore(name, options) 方法在被调用时,必须执行以下步骤
-
如果 database 的升级事务不为 null,则令 transaction 为该事务,否则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 keyPath 为 options 的
keyPath成员(如果它不为 undefined 或 null),否则为 null。 -
如果 keyPath 不为 null 且不是有效键路径,则抛出一个 "
SyntaxError"DOMException。 -
如果 database 中已存在名为 name 的对象存储,则抛出一个 "
ConstraintError"DOMException。 -
如果 options 的
autoIncrement成员为 true,则设置 autoIncrement,否则取消设置。 -
如果设置了 autoIncrement 且 keyPath 为空字符串或任何序列(空或非空),则抛出一个 "
InvalidAccessError"DOMException。 -
令 store 为 database 中的一个新对象存储。将已创建对象存储的名称设置为 name。如果设置了 autoIncrement,则已创建对象存储使用键生成器。如果 keyPath 不为 null,则将已创建对象存储的键路径设置为 keyPath。
-
返回一个关联 store 和 transaction 的新对象存储句柄。
此方法在已连接数据库中创建一个具有给定名称的新对象存储并返回。注意,此方法只能在升级事务内调用。
此方法同步修改被调用时所在的 IDBDatabase 实例上的 objectStoreNames 属性。
在某些实现中,createObjectStore() 方法返回后,在排队创建一个对象存储的任务后,可能会遇到问题。例如,在某些实现中,关于新创建对象存储的元数据是异步插入数据库的,或者实现可能因配额原因需要征求用户许可。此类实现仍必须创建并返回一个 IDBObjectStore 对象;一旦实现确定创建对象存储已失败,必须使用适当的错误中止事务。例如,如果由于配额原因导致创建对象存储失败,则必须将 "QuotaExceededError" DOMException 用作错误。
deleteObjectStore(name) 方法在被调用时,必须执行以下步骤
-
如果 database 的升级事务不为 null,则令 transaction 为该事务,否则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 store 为 database 中名为 name 的对象存储,如果不存在,则抛出一个 "
NotFoundError"DOMException。 -
销毁 store。
此方法销毁已连接数据库中具有给定名称的对象存储。注意,此方法只能在升级事务内调用。
此方法同步修改被调用时所在的 IDBDatabase 实例上的 objectStoreNames 属性。
- transaction = connection .
transaction(scope [, mode = "readonly"]) - 返回一个具有给定 mode(
"readonly"或"readwrite")和 scope 的新事务,scope 可以是单个对象存储名称或名称数组。 - connection .
close() - 一旦所有正在运行的事务都已完成,则关闭该连接。
transaction(storeNames, mode) 方法在被调用时,必须执行以下步骤
-
如果正在运行的升级事务与该连接相关联,则抛出一个 "
InvalidStateError"DOMException。 -
如果连接的关闭挂起标志已设置,则抛出一个 "
InvalidStateError"DOMException。 -
令 scope 为 storeNames 中的唯一字符串集合(如果是序列),否则为包含一个等于 storeNames 的字符串的集合。
-
如果 scope 中的任何字符串不是已连接数据库中对象存储的名称,则抛出一个 "
NotFoundError"DOMException。 -
如果 scope 为空,则抛出一个 "
InvalidAccessError"DOMException。 -
如果 mode 不是
"readonly"或"readwrite",则抛出一个 TypeError。 -
令 transaction 为一个新创建的事务,其关联 connection、mode 以及在 scope 中命名的对象存储集合。
-
返回一个代表 transaction 的
IDBTransaction对象。
onabort 属性是 abort 事件的事件处理程序。
onclose 属性是 close 事件的事件处理程序。
onerror 属性是 error 事件的事件处理程序。
onversionchange 属性是 versionchange 事件的事件处理程序。
4.5. IDBObjectStore 接口
IDBObjectStore 接口代表一个对象存储句柄。
[Exposed=(Window,Worker)] interfaceIDBObjectStore{ attribute DOMString name; readonly attribute any keyPath; readonly attribute DOMStringList indexNames; [SameObject] readonly attribute IDBTransaction transaction; readonly attribute boolean autoIncrement; [NewObject] IDBRequest put(anyvalue, optional anykey); [NewObject] IDBRequest add(anyvalue, optional anykey); [NewObject] IDBRequest delete(anyquery); [NewObject] IDBRequest clear(); [NewObject] IDBRequest get(anyquery); [NewObject] IDBRequest getKey(anyquery); [NewObject] IDBRequest getAll(optional anyquery, optional [EnforceRange] unsigned longcount); [NewObject] IDBRequest getAllKeys(optional anyquery, optional [EnforceRange] unsigned longcount); [NewObject] IDBRequest count(optional anyquery); [NewObject] IDBRequest openCursor(optional anyquery, optional IDBCursorDirectiondirection= "next"); [NewObject] IDBRequest openKeyCursor(optional anyquery, optional IDBCursorDirectiondirection= "next"); IDBIndex index(DOMStringname); [NewObject] IDBIndex createIndex(DOMStringname, (DOMString or sequence<DOMString>)keyPath, optional IDBIndexParametersoptions); void deleteIndex(DOMStringname); }; dictionaryIDBIndexParameters{ booleanunique= false; booleanmultiEntry= false; };
- store .
name - 返回存储的名称。
- store .
name= newName - 将存储的名称更新为 newName。
如果不是在升级事务内调用,则抛出 "
InvalidStateError"DOMException。 - store .
keyPath - 返回存储的键路径,如果没有则返回 null。
- list .
indexNames - 返回存储中索引的名称列表。
- store .
transaction - 返回关联的事务。
- store .
autoIncrement - 如果存储具有键生成器,则返回 true,否则返回 false。
name 属性的 getter 必须返回此对象存储句柄的名称。
这与对象存储的名称相同吗?
只要事务尚未完成,它就与关联的对象存储的名称相同。但一旦事务已完成,此属性将不会反映稍后通过升级事务所做的更改。name 属性的 setter 必须执行以下步骤
-
令 name 为给定值。
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 不是升级事务,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
如果 store 的名称等于 name,则终止这些步骤。
-
如果 store 的数据库中已存在名为 name 的对象存储,则抛出一个 "
ConstraintError"DOMException。 -
设置 store 的名称为 name。
keyPath 属性的 getter 必须返回此对象存储句柄的对象存储的键路径,如果没有则返回 null。根据 [WEBIDL],键路径被转换为 DOMString(如果是一个字符串)或 sequence<DOMString>(如果是一个字符串列表)。
返回值并非对象存储创建时所使用的同一个实例。然而,如果该属性返回一个对象(特别是 Array),它每次被检查时都会返回相同的对象实例。更改该对象的属性不会对对象存储产生影响。
indexNames 属性的 getter 必须返回一个 DOMStringList,该列表关联了此对象存储句柄的索引集合中索引名称的排序名称列表。
这与对象存储的索引名称列表相同吗?
只要事务尚未完成,它就与关联的对象存储的索引名称列表相同。但一旦事务已完成,此属性将不会反映稍后通过升级事务所做的更改。transaction 属性的 getter 必须返回此对象存储句柄的事务。
autoIncrement 属性的 getter 必须返回 true(如果此对象存储句柄的对象存储具有键生成器),否则返回 false。
ReadOnlyError" DOMException 或 "TransactionInactiveError" DOMException。- request = store .
put(value [, key]) - request = store .
add(value [, key]) - 在 store 中添加或更新一个具有给定 value 和 key 的记录。
如果存储使用行内键(in-line keys)且指定了 key,则会抛出 "
DataError"DOMException。如果使用
put(),任何已存在的具有该键的记录都将被替换。如果使用add(),且已存在具有该键的记录,则 request 将失败,并将 request 的error设置为 "ConstraintError"DOMException。 - request = store .
delete(query) - 删除 store 中与 query 中的键匹配或处于给定键范围内的记录。
如果成功,request 的
result将为undefined。 - request = store .
clear() - 删除 store 中的所有记录。
如果成功,request 的
result将为undefined。
put(value, key) 方法在被调用时,必须执行以下步骤
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
如果 transaction 是只读事务,则抛出一个 "
ReadOnlyError"DOMException。 -
如果 store 使用行内键且给定了 key,则抛出一个 "
DataError"DOMException。 -
如果 store 使用离线键(out-of-line keys)且没有键生成器,且未给定 key,则抛出一个 "
DataError"DOMException。 -
如果给定了 key,则
-
令 r 为执行将值转换为键步骤对 key 进行处理的结果。重新抛出任何异常。
-
如果 r 无效,则抛出一个 "
DataError"DOMException。 -
令 key 为 r。
-
-
令 targetRealm 为用户代理定义的领域(Realm)。
-
令 clone 为 value 在 targetRealm 中的克隆。重新抛出任何异常。
为什么要创建值的副本?
值在存储时会被序列化。在此处将其视为副本,允许本规范中的其他算法将其视为 ECMAScript 值,但如果行为上的差异不可观测,实现可以优化此步骤。 -
如果 store 使用行内键,则
-
令 kpk 为执行使用键路径从值中提取键步骤对 clone 和 store 的键路径进行处理的结果。重新抛出任何异常。
-
如果 kpk 无效,则抛出一个 "
DataError"DOMException。 -
如果 kpk 不是失败(failure),则令 key 为 kpk。
-
否则(kpk 为失败)
-
如果 store 没有键生成器,则抛出一个 "
DataError"DOMException。 -
否则,如果执行检查键是否可以注入值步骤对 clone 和 store 的键路径进行处理返回 false,则抛出一个 "
DataError"DOMException。
-
-
-
执行异步执行请求的步骤,并返回这些步骤创建的
IDBRequest。这些步骤以当前对象存储句柄作为 source,并以将记录存储到对象存储中的步骤作为 operation,使用 store、clone 作为 value、key,且未设置 no-overwrite flag。
add(value, key) 方法在被调用时,必须执行以下步骤
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
如果 transaction 是只读事务,则抛出一个 "
ReadOnlyError"DOMException。 -
如果 store 使用行内键且给定了 key,则抛出一个 "
DataError"DOMException。 -
如果 store 使用离线键且没有键生成器,且未给定 key,则抛出一个 "
DataError"DOMException。 -
如果给定了 key,则
-
令 r 为执行将值转换为键步骤对 key 进行处理的结果。重新抛出任何异常。
-
如果 r 无效,则抛出一个 "
DataError"DOMException。 -
令 key 为 r。
-
-
令 targetRealm 为用户代理定义的领域(Realm)。
-
令 clone 为 value 在 targetRealm 中的克隆。重新抛出任何异常。
为什么要创建值的副本?
值在存储时会被序列化。在此处将其视为副本,允许本规范中的其他算法将其视为 ECMAScript 值,但如果行为上的差异不可观测,实现可以优化此步骤。 -
如果 store 使用行内键,则
-
令 kpk 为执行使用键路径从值中提取键步骤对 clone 和 store 的键路径进行处理的结果。重新抛出任何异常。
-
如果 kpk 无效,则抛出一个 "
DataError"DOMException。 -
如果 kpk 不是失败(failure),则令 key 为 kpk。
-
否则(kpk 为失败)
-
如果 store 没有键生成器,则抛出一个 "
DataError"DOMException。 -
否则,如果执行检查键是否可以注入值步骤对 clone 和 store 的键路径进行处理返回 false,则抛出一个 "
DataError"DOMException。
-
-
-
执行异步执行请求的步骤,并返回这些步骤创建的
IDBRequest。这些步骤以当前对象存储句柄作为 source,并以将记录存储到对象存储中的步骤作为 operation,使用 store、clone 作为 value、key,且设置了 no-overwrite flag。
delete(query) 方法在被调用时,必须执行以下步骤
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
如果 transaction 是只读事务,则抛出一个 "
ReadOnlyError"DOMException。 -
令 range 为执行将值转换为键范围步骤对 query 进行处理(且设置了 null disallowed flag)的结果。重新抛出任何异常。
-
执行异步执行请求的步骤,并返回这些步骤创建的
IDBRequest。这些步骤以当前对象存储句柄作为 source,并以从对象存储中删除记录的步骤作为 operation,使用 store 和 range。
query 参数可以是标识要删除的记录键的键或 IDBKeyRange。
clear() 方法在被调用时,必须执行以下步骤
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
如果 transaction 是只读事务,则抛出一个 "
ReadOnlyError"DOMException。 -
执行异步执行请求的步骤,并返回这些步骤创建的
IDBRequest。这些步骤以当前对象存储句柄作为 source,并以清除对象存储的步骤作为 operation,使用 store。
TransactionInactiveError" DOMException。- request = store .
get**(query) - 检索与 query 中的给定键或键范围匹配的第一条记录的值。
- request = store .
getKey(query) - 检索与 query 中的给定键或键范围匹配的第一条记录的键。
- request = store .
getAll(query [, count]) - 检索 query 中给定的键或键范围所匹配的记录的值(若给定了 count,则最多检索该数量)。
- request = store .
getAllKeys(query [, count]) - 检索 query 中给定的键或键范围所匹配的记录的键(若给定了 count,则最多检索该数量)。
- request = store .
count(query) - 检索 query 中给定的键或键范围所匹配的记录数量。
如果成功,request 的
result将为计数值。
get(query) 方法在被调用时,必须执行以下步骤
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为执行将值转换为键范围的步骤并将 query 和 null 不允许标志(null disallowed flag)设置为 true 的结果。重新抛出任何异常。
-
执行异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前对象存储句柄作为 source,以从对象存储中检索值的步骤作为 operation 来运行,并使用当前域(Realm)作为 targetRealm,以及使用 store 和 range。
query 参数可以是一个键或一个标识要检索的记录的 IDBKeyRange。如果指定了范围,则该方法检索该范围内第一个存在的记录值。
getKey(query) 方法在被调用时,必须执行以下步骤
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为执行将值转换为键范围的步骤并将 query 和 null 不允许标志设置为 true 的结果。重新抛出任何异常。
-
执行异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前对象存储句柄作为 source,以从对象存储中检索键的步骤作为 operation 来运行,并使用 store 和 range。
query 参数可以是一个键或一个标识要检索的记录键的 IDBKeyRange。如果指定了范围,则该方法检索该范围内第一个存在的键。
getAll(query, count) 方法在被调用时,必须执行以下步骤
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为执行将值转换为键范围的步骤并传入 query 的结果。重新抛出任何异常。
-
执行异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前对象存储句柄作为 source,以从对象存储中检索多个值的步骤作为 operation 来运行,使用当前域(Realm)作为 targetRealm,并使用 store、range 以及(如果给定了)count。
query 参数可以是一个键或一个标识要检索的记录的 IDBKeyRange。如果为 null 或未给出,则使用无界键范围。如果指定了 count 且范围内的记录数超过 count,则仅检索前 count 条记录。
getAllKeys(query, count) 方法在被调用时,必须执行以下步骤
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为执行将值转换为键范围的步骤并传入 query 的结果。重新抛出任何异常。
-
执行异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前对象存储句柄作为 source,以从对象存储中检索多个键的步骤作为 operation 来运行,并使用 store、range 以及(如果给定了)count。
query 参数可以是一个键或一个标识要检索的记录键的 IDBKeyRange。如果为 null 或未给出,则使用无界键范围。如果指定了 count 且范围内的键数超过 count,则仅检索前 count 个键。
count(query) 方法在被调用时,必须执行以下步骤
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为执行将值转换为键范围的步骤并传入 query 的结果。重新抛出任何异常。
-
执行异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前对象存储句柄作为 source,以统计范围内的记录的步骤作为 operation 来运行,并使用 source 和 range。
query 参数可以是一个键或一个标识要统计的记录键的 IDBKeyRange。如果为 null 或未给出,则使用无界键范围。
TransactionInactiveError" DOMException。- request = store .
openCursor([query [, direction = "next"]]) - 打开一个覆盖匹配 query 的记录的游标,并按 direction 排序。如果 query 为 null,则匹配 store 中的所有记录。
如果成功,request 的
result将是一个指向第一个匹配记录的IDBCursorWithValue,如果没有匹配的记录,则返回 null。 - request = store .
openKeyCursor([query [, direction = "next"]]) - 打开一个设置了仅键标志(key only flag)的游标,覆盖匹配 query 的记录,并按 direction 排序。如果 query 为 null,则匹配 store 中的所有记录。
如果成功,request 的
result将是一个指向第一个匹配记录的IDBCursor,如果没有匹配的记录,则返回 null。
openCursor(query, direction) 方法在被调用时,必须执行以下步骤
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为执行将值转换为键范围的步骤并传入 query 的结果。重新抛出任何异常。
-
令 cursor 为一个新的游标,其中 事务设置为 transaction,位置为 undefined,方向设置为 direction,获得值标志(got value flag)未设置,键和值为 undefined。cursor 的来源是 store。cursor 的范围是 range。
-
执行异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前对象存储句柄作为 source,以遍历游标的步骤作为 operation 来运行,使用当前域(Realm)作为 targetRealm,并使用 cursor。
query 参数可以是一个键或一个用作游标的范围的 IDBKeyRange。如果为 null 或未给出,则使用无界键范围。
openKeyCursor(query, direction) 方法在被调用时,必须执行以下步骤
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为执行将值转换为键范围的步骤并传入 query 的结果。重新抛出任何异常。
-
令 cursor 为一个新的游标,其中 事务设置为 transaction,位置为 undefined,方向设置为 direction,获得值标志未设置,键和值为 undefined。cursor 的来源是 store。cursor 的范围是 range。设置 cursor 的仅键标志。
-
执行异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前对象存储句柄作为 source,以遍历游标的步骤作为 operation 来运行,使用当前域(Realm)作为 targetRealm,并使用 cursor。
query 参数可以是一个键或一个用作游标的范围的 IDBKeyRange。如果为 null 或未给出,则使用无界键范围。
- index = store . index(name)
- 返回 store 中名为 name 的索引的
IDBIndex。 - index = store .
createIndex(name, keyPath [, options]) - 在 store 中创建具有给定 name、keyPath 和 options 的新索引,并返回一个新的
IDBIndex。如果 keyPath 和 options 定义的约束无法通过 store 中现有的数据满足,则升级事务将以 "ConstraintError"DOMException中止。如果未在升级事务中调用,则抛出 "
InvalidStateError"DOMException。 - store .
deleteIndex(name) - 删除 store 中具有给定 name 的索引。
如果未在升级事务中调用,则抛出 "
InvalidStateError"DOMException。
createIndex(name, keyPath, options) 方法在被调用时,必须执行以下步骤
-
如果 transaction 不是升级事务,则抛出一个 "
InvalidStateError"DOMException。 -
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
如果 store 中已存在名为 name 的索引,则抛出一个 "
ConstraintError"DOMException。 -
如果 keyPath 不是有效的键路径,则抛出一个 "
SyntaxError"DOMException。 -
如果 options 的
unique成员为 true,则设置 unique,否则不设置。 -
如果 options 的
multiEntry成员为 true,则设置 multiEntry,否则不设置。 -
如果 keyPath 是一个序列且已设置 multiEntry,则抛出一个 "
InvalidAccessError"DOMException。 -
令 index 为 store 中的一个新索引。将 index 的名称设置为 name,键路径设置为 keyPath。如果已设置 unique,则设置 index 的唯一标志。如果已设置 multiEntry,则设置 index 的多条目标志。
此方法在对象存储中创建并返回一个具有给定名称的新索引。注意,此方法只能在升级事务内调用。
请求创建的索引可以包含对索引所引用的对象存储中允许的数据的约束,例如要求索引的 keyPath 所引用的值具有唯一性。如果所引用的对象存储已经包含违反这些约束的数据,这绝不能导致 createIndex() 的实现抛出异常或影响其返回值。实现仍必须创建并返回一个 IDBIndex 对象,并且实现必须将一个任务加入队列,以中止用于 createIndex() 调用的升级事务。
此方法同步修改调用它的 IDBObjectStore 实例上的 indexNames 属性。虽然此方法不返回 IDBRequest 对象,但索引创建本身是在升级事务内作为异步请求处理的。
在某些实现中,createIndex 方法返回后,实现可能会异步遇到创建索引的问题。例如,在有关新创建索引的元数据被排队以异步插入数据库的实现中,或者实现因配额原因需要询问用户许可的情况。此类实现仍必须创建并返回一个 IDBIndex 对象,一旦实现确定创建索引失败,它必须使用中止事务的步骤并使用适当的错误作为 error 来中止事务。例如,如果创建索引因配额原因失败,则必须将 "QuotaExceededError" DOMException 用作错误;如果因唯一标志约束无法创建索引,则必须将 "ConstraintError" DOMException 用作错误。
index(name) 方法在被调用时,必须执行以下步骤
-
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 已完成,则抛出一个 "
InvalidStateError"DOMException。 -
令 index 为此对象存储句柄的索引集中名为 name 的索引(如果存在),否则抛出一个 "
NotFoundError"DOMException。
deleteIndex(name) 方法在被调用时,必须执行以下步骤
-
如果 transaction 不是升级事务,则抛出一个 "
InvalidStateError"DOMException。 -
如果 store 已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 index 为 store 中名为 name 的索引(如果存在),否则抛出一个 "
NotFoundError"DOMException。 -
销毁 index。
此方法销毁对象存储中具有给定名称的索引。注意,此方法只能在升级事务内调用。
此方法同步修改调用它的 IDBObjectStore 实例上的 indexNames 属性。虽然此方法不返回 IDBRequest 对象,但索引销毁本身是在升级事务内作为异步请求处理的。
4.6. IDBIndex 接口
[Exposed=(Window,Worker)] interfaceIDBIndex{ attribute DOMString name; [SameObject] readonly attribute IDBObjectStore objectStore; readonly attribute any keyPath; readonly attribute boolean multiEntry; readonly attribute boolean unique; [NewObject] IDBRequest get(anyquery); [NewObject] IDBRequest getKey(anyquery); [NewObject] IDBRequest getAll(optional anyquery, optional [EnforceRange] unsigned longcount); [NewObject] IDBRequest getAllKeys(optional anyquery, optional [EnforceRange] unsigned longcount); [NewObject] IDBRequest count(optional anyquery); [NewObject] IDBRequest openCursor(optional anyquery, optional IDBCursorDirectiondirection= "next"); [NewObject] IDBRequest openKeyCursor(optional anyquery, optional IDBCursorDirectiondirection= "next"); };
- index .
name - 返回索引的名称。
- index .
name= newName - 将存储的名称更新为 newName。
如果未在升级事务内调用,则抛出 "
InvalidStateError"DOMException。 - index .
objectStore - 返回索引所属的
IDBObjectStore。 - index . keyPath
- 返回索引的键路径。
- index . multiEntry
- 如果设置了索引的多条目标志,则返回 true。
- index . unique
- 如果设置了索引的唯一标志,则返回 true。
这与索引的名称相同吗?
只要事务没有完成,这与关联的索引的名称相同。但一旦事务完成,此属性将不会反映稍后在升级事务中所做的更改。name 属性的 setter 必须执行以下步骤
-
令 name 为给定值。
-
如果 transaction 不是升级事务,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
如果 index 或 index 的对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 index 的名称等于 name,则终止这些步骤。
-
如果 index 的对象存储中已存在名为 name 的索引,则抛出一个 "
ConstraintError"DOMException。 -
将 index 的名称设置为 name。
objectStore 属性的 getter 必须返回此索引句柄的对象存储句柄。
keyPath 属性的 getter 必须返回此索引句柄的索引的键路径。根据 [WEBIDL],键路径被转换为 DOMString(如果是字符串)或 sequence<DOMString>(如果是字符串列表)。
返回的值并非创建索引时所使用的相同实例。但是,如果此属性返回一个对象(特别是 Array),它每次被检查时都会返回相同的对象实例。更改对象的属性对索引没有任何影响。
multiEntry 属性的 getter 必须在设置了此索引句柄的索引的多条目标志时返回 true,否则返回 false。
unique 属性的 getter 必须在设置了此索引句柄的索引的唯一标志时返回 true,否则返回 false。
TransactionInactiveError" DOMException。- request = store .
get(query) - 检索在 query 中给定的键或键范围所匹配的第一条记录的值。
- request = store .
getKey(query) - 检索在 query 中给定的键或键范围所匹配的第一条记录的键。
- request = store .
getAll(query [, count]) - 检索在 query 中给定的键或键范围所匹配的记录的值(若给定了 count,则最多检索该数量)。
- request = store .
getAllKeys(query [, count]) - 检索在 query 中给定的键或键范围所匹配的记录的键(若给定了 count,则最多检索该数量)。
- request = store .
count(query) - 检索在 query 中给定的键或键范围所匹配的记录数量。
如果成功,request 的
result将为该计数值。
get(query) 方法在被调用时,必须执行以下步骤
-
如果 index 或 index 的对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为执行将值转换为键范围的步骤并将 query 和 null 不允许标志设置为 true 的结果。重新抛出任何异常。
-
执行异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前索引句柄作为 source,以从索引中检索所引用的值的步骤作为 operation 来运行,使用当前域(Realm)作为 targetRealm,并使用 index 和 range。
query 参数可以是一个键或一个标识要检索的记录的 IDBKeyRange。如果指定了范围,则该方法检索该范围内第一个存在的记录。
getKey(query) 方法在被调用时,必须执行以下步骤
-
如果 index 或 index 的对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为执行将值转换为键范围的步骤并将 query 和 null 不允许标志设置为 true 的结果。重新抛出任何异常。
-
执行异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前索引句柄作为 source,以从索引中检索值的步骤作为 operation 来运行,并使用 index 和 range。
query 参数可以是一个键或一个标识要检索的记录键的 IDBKeyRange。如果指定了范围,则该方法检索该范围内第一个存在的键。
getAll(query, count) 方法在被调用时,必须执行以下步骤
-
如果 index 或 index 的对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为执行将值转换为键范围的步骤并传入 query 的结果。重新抛出任何异常。
-
执行异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前索引句柄作为 source,以从索引中检索多个所引用的值的步骤作为 operation 来运行,使用当前域(Realm)作为 targetRealm,并使用 index、range 以及(如果给定了)count。
query 参数可以是一个键或一个标识要检索的记录的 IDBKeyRange。如果为 null 或未给出,则使用无界键范围。如果指定了 count 且范围内的记录数超过 count,则仅检索前 count 条记录。
getAllKeys(query, count) 方法在被调用时,必须执行以下步骤
-
如果 index 或 index 的对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 未处于激活状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为执行将值转换为键范围的步骤并传入 query 的结果。重新抛出任何异常。
-
执行用于异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前索引句柄作为 source,以从索引检索多个值的步骤作为 operation 来运行,并根据需要使用给定的 index、range 和 count。
query 参数可以是键或用于标识待检索记录键的 IDBKeyRange。如果为 null 或未提供,则使用无界键范围。如果指定了 count 且范围内的键多于 count 个,则仅检索前 count 个。
count(query) 方法在被调用时,必须执行这些步骤
-
如果 index 或 index 的对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为使用 query 执行将值转换为键范围的步骤的结果。重新抛出任何异常。
-
执行用于异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前索引句柄作为 source,以计算范围内的记录数量的步骤作为 operation 来运行,并以索引作为 source 和 range。
query 参数可以是键或用于标识待计数记录键的 IDBKeyRange。如果为 null 或未提供,则使用无界键范围。
TransactionInactiveError" DOMException。- request = store .
openCursor([query [, direction = "next"]]) - 在匹配 query 的记录上打开一个游标,并按 direction 排序。如果 query 为 null,则匹配 index 中的所有记录。
如果成功,request 的
result将是一个IDBCursorWithValue,如果没有匹配的记录,则为 null。 - request = store .
openKeyCursor([query [, direction = "next"]]) - 在匹配 query 的记录上打开一个设置了仅键标志的游标,并按 direction 排序。如果 query 为 null,则匹配 index 中的所有记录。
openCursor(query, direction) 方法在被调用时,必须执行这些步骤
-
如果 index 或 index 的对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为使用 query 执行将值转换为键范围的步骤的结果。重新抛出任何异常。
-
令 cursor 为一个新的游标,其事务设置为 transaction,位置未定义,方向设置为 direction,获取值标志未设置,键和值未定义。该 cursor 的来源是 index。该 cursor 的范围是 range。
-
执行用于异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前索引句柄作为 source,以迭代游标的步骤作为 operation 来运行,并使用当前 Realm 作为 targetRealm,以及使用 cursor。
query 参数可以是键或用作游标范围的 IDBKeyRange。如果为 null 或未提供,则使用无界键范围。
openKeyCursor(query, direction) 方法在被调用时,必须执行这些步骤
-
如果 index 或 index 的对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
令 range 为使用 query 执行将值转换为键范围的步骤的结果。重新抛出任何异常。
-
令 cursor 为一个新的游标,其事务设置为 transaction,位置未定义,方向设置为 direction,获取值标志未设置,键和值未定义。该 cursor 的来源是 index。该 cursor 的范围是 range。该 cursor 的仅键标志被设置。
-
执行用于异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前索引句柄作为 source,以迭代游标的步骤作为 operation 来运行,并使用当前 Realm 作为 targetRealm,以及使用 cursor。
query 参数可以是键或用作游标范围的 IDBKeyRange。如果为 null 或未提供,则使用无界键范围。
4.7. IDBKeyRange 接口
IDBKeyRange 接口表示一个键范围。
[Exposed=(Window,Worker)] interfaceIDBKeyRange{ readonly attribute any lower; readonly attribute any upper; readonly attribute boolean lowerOpen; readonly attribute boolean upperOpen; // Static construction methods: [NewObject] static IDBKeyRange only(anyvalue); [NewObject] static IDBKeyRangelowerBound(anylower, optional booleanopen= false); [NewObject] static IDBKeyRangeupperBound(anyupper, optional booleanopen= false); [NewObject] static IDBKeyRange bound(anylower, anyupper, optional booleanlowerOpen= false, optional booleanupperOpen= false); boolean_includes(anykey); };
_includes 标识符映射到 ECMAScript 时,应移除前导的 U+005F LOW LINE ("_") 字符。使用前导的 "_" 是为了转义该标识符,使其看起来不像是保留字(此处为 includes 关键字)。lower 属性的获取器必须返回:如果下界不为 null,则返回执行将键转换为值步骤的结果,否则返回 undefined。
upper 属性的获取器必须返回:如果上界不为 null,则返回执行将键转换为值步骤的结果,否则返回 undefined。
lowerOpen 属性的获取器必须在设置了下界开放标志时返回 true,否则返回 false。
upperOpen 属性的获取器必须在设置了上界开放标志时返回 true,否则返回 false。
- range =
IDBKeyRange.only(key) - 返回一个新的
IDBKeyRange,仅跨越 key。 - range =
IDBKeyRange.lowerBound(key [, open = false]) - 返回一个新的
IDBKeyRange,起始于 key 且无上界。如果 open 为 true,则 key 不包含在范围内。 - range =
IDBKeyRange.upperBound(key [, open = false]) - 返回一个新的
IDBKeyRange,无下界且终止于 key。如果 open 为 true,则 key 不包含在范围内。 - range =
IDBKeyRange.bound(lower, upper [, lowerOpen = false [, upperOpen = false]]) - 返回一个新的
IDBKeyRange,跨越从 lower 到 upper。如果 lowerOpen 为 true,则 lower 不包含在范围内。如果 upperOpen 为 true,则 upper 不包含在范围内。
only(value) 方法在被调用时,必须执行这些步骤
-
令 key 为使用 value 执行将值转换为键步骤的结果。重新抛出任何异常。
-
如果 key 无效,则抛出一个 "
DataError"DOMException。 -
创建并返回一个仅包含 key 的新键范围。
bound(lower, upper, lowerOpen, upperOpen) 方法在被调用时,必须执行这些步骤
-
令 lowerKey 为使用 lower 执行将值转换为键步骤的结果。重新抛出任何异常。
-
如果 lowerKey 无效,则抛出一个 "
DataError"DOMException。 -
令 upperKey 为使用 upper 执行将值转换为键步骤的结果。重新抛出任何异常。
-
如果 upperKey 无效,则抛出一个 "
DataError"DOMException。 -
如果 lowerKey 大于 upperKey,则抛出一个 "
DataError"DOMException。 -
创建并返回一个新键范围,其下界设置为 lowerKey,若 lowerOpen 为 true 则设置下界开放标志,上界设置为 upperKey 且若 upperOpen 为 true 则设置上界开放标志。
- range .
includes(key) - 如果范围中包含 key 则返回 true,否则返回 false。
includes(key) 方法在被调用时,必须执行这些步骤
-
令 k 为使用 key 执行将值转换为键步骤的结果。重新抛出任何异常。
-
如果 k 无效,则抛出一个 "
DataError"DOMException。 -
如果 k 位于此范围,则返回 true,否则返回 false。
4.8. IDBCursor 接口
游标对象实现了 IDBCursor 接口。表示给定游标的 IDBCursor 实例始终只有一个。同时可以使用的游标数量没有限制。
[Exposed=(Window,Worker)] interfaceIDBCursor{ readonly attribute (IDBObjectStore or IDBIndex) source; readonly attribute IDBCursorDirection direction; readonly attribute any key; readonly attribute any primaryKey; void advance([EnforceRange] unsigned longcount); void continue(optional anykey); void continuePrimaryKey(anykey, anyprimaryKey); [NewObject] IDBRequest update(anyvalue); [NewObject] IDBRequest delete(); }; enumIDBCursorDirection{"next","nextunique","prev","prevunique"};
- cursor .
source - 返回打开该游标的
IDBObjectStore或IDBIndex。 - range .
direction - 返回游标的方向(
"next"、"nextunique"、"prev"或"prevunique")。 - cursor .
key - 返回游标的键。如果游标正在前进或已完成,则抛出 "
InvalidStateError"DOMException。 - cursor .
primaryKey - 返回游标的有效键。如果游标正在前进或已完成,则抛出 "
InvalidStateError"DOMException。
source 属性的获取器必须返回此游标的来源。此属性永远不会返回 null 或抛出异常,即使游标当前正在迭代、已迭代过末尾或其事务不处于活动状态。
key 属性的获取器必须返回使用游标当前的键执行将键转换为值步骤的结果。注意,如果该属性返回对象(例如 Date 或 Array),则每次检查它时都会返回相同的对象实例,直到游标的键发生变化。这意味着如果对象被修改,任何检查游标值的人都会看到这些修改。然而,修改此类对象并不会修改数据库的内容。
primaryKey 属性的获取器必须返回使用游标当前的有效键执行将键转换为值步骤的结果。注意,如果该属性返回对象(例如 Date 或 Array),则每次检查它时都会返回相同的对象实例,直到游标的有效键发生变化。这意味着如果对象被修改,任何检查游标值的人都会看到这些修改。然而,修改此类对象并不会修改数据库的内容。
IDBRequest 上触发 success 事件。如果范围内有记录,则 result 将是相同的游标,否则为 undefined。如果在游标已经前进时调用此方法,将抛出 "InvalidStateError" DOMException。
如果调用以下方法时事务不处于活动状态,则会抛出 "TransactionInactiveError" DOMException。
- cursor .
advance(count) - 将游标前进经过范围内接下来的 count 个记录。
- cursor .
continue() - 将游标前进到范围内接下来的记录。
- cursor .
continue(key) - 将游标前进到范围内匹配或之后 key 的下一条记录。
- cursor .
continuePrimaryKey(key, primaryKey) - 将游标前进到范围内匹配或之后 key 和 primaryKey 的下一条记录。如果来源不是索引,则抛出 "
InvalidAccessError"DOMException。
advance(count) 方法在被调用时,必须执行这些步骤
-
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
如果游标的来源或有效对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果此游标的获取值标志未设置(表示游标正在迭代或已迭代过末尾),则抛出一个 "
InvalidStateError"DOMException。 -
取消设置该游标上的获取值标志。
-
取消设置 request 上的完成标志。
-
执行用于异步执行请求的步骤,以该游标的来源作为 source,以迭代游标的步骤作为 operation 和 request,使用当前 Realm 作为 targetRealm,以及此游标和 count。
continue(key) 方法在被调用时,必须执行这些步骤
-
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
如果游标的来源或有效对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果此游标的获取值标志未设置(表示游标正在迭代或已迭代过末尾),则抛出一个 "
InvalidStateError"DOMException。 -
如果给出了 key,则
-
令 r 为使用 key 执行将值转换为键步骤的结果。重新抛出任何异常。
-
如果 r 无效,则抛出一个 "
DataError"DOMException。 -
令 key 为 r。
-
如果 key 小于或等于此游标的位置,且此游标的方向为
"next"或"nextunique",则抛出一个 "DataError"DOMException。 -
如果 key 大于或等于此游标的位置,且此游标的方向为
"prev"或"prevunique",则抛出一个 "DataError"DOMException。
-
-
取消设置该游标上的获取值标志。
-
取消设置 request 上的完成标志。
-
执行用于异步执行请求的步骤,以该游标的来源作为 source,以迭代游标的步骤作为 operation 和 request,使用当前 Realm 作为 targetRealm,以及此游标和 key(如果给出)。
continuePrimaryKey(key, primaryKey) 方法在被调用时,必须执行这些步骤
-
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
如果游标的来源或有效对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果此游标的来源不是索引,则抛出一个 "
InvalidAccessError"DOMException。 -
如果此游标的方向不是
"next"或"prev",则抛出一个 "InvalidAccessError"DOMException。 -
如果此游标的获取值标志未设置(表示游标正在迭代或已迭代过末尾),则抛出一个 "
InvalidStateError"DOMException。 -
令 r 为使用 key 执行将值转换为键步骤的结果。重新抛出任何异常。
-
如果 r 无效,则抛出一个 "
DataError"DOMException。 -
令 key 为 r。
-
令 r 为使用 primaryKey 执行将值转换为键步骤的结果。重新抛出任何异常。
-
如果 r 无效,则抛出一个 "
DataError"DOMException。 -
令 primaryKey 为 r。
-
如果 key 小于此游标的位置,且此游标的方向为
"next",则抛出一个 "DataError"DOMException。 -
如果 key 大于此游标的位置,且此游标的方向为
"prev",则抛出一个 "DataError"DOMException。 -
如果 key 等于此游标的位置,且 primaryKey 小于或等于此游标的对象存储位置,且此游标的方向为
"next",则抛出一个 "DataError"DOMException。 -
如果 key 等于此游标的位置,且 primaryKey 大于或等于此游标的对象存储位置,且此游标的方向为
"prev",则抛出一个 "DataError"DOMException。 -
取消设置该游标上的获取值标志。
-
取消设置 request 上的完成标志。
-
执行用于异步执行请求的步骤,以该游标的来源作为 source,以迭代游标的步骤作为 operation 和 request,使用当前 Realm 作为 targetRealm,以及此游标、key 和 primaryKey。
ReadOnlyError" DOMException;如果调用时事务不处于活动状态,则抛出 "TransactionInactiveError" DOMException。
update(value) 方法在被调用时,必须执行这些步骤
-
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
如果 transaction 是一个只读事务,则抛出一个 "
ReadOnlyError"DOMException。 -
如果游标的来源或有效对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果此游标的获取值标志未设置(表示游标正在迭代或已迭代过末尾),则抛出一个 "
InvalidStateError"DOMException。 -
如果此游标的仅键标志被设置,则抛出一个 "
InvalidStateError"DOMException。 -
令 targetRealm 为用户代理定义的领域 (Realm)。
-
令 clone 为 targetRealm 中 value 的克隆。重新抛出任何异常。
为什么要创建值的副本?
值在存储时会被序列化。在此处将其视为副本,允许本规范中的其他算法将其视为 ECMAScript 值,但如果行为上的差异不可观测,实现可以优化此步骤。 -
-
令 kpk 为使用 clone 和有效对象存储的键路径,执行使用键路径从值中提取键步骤的结果。重新抛出任何异常。
-
如果 kpk 为失败、无效,或与游标的有效键不相等,则抛出一个 "
DataError"DOMException。
-
-
执行用于异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前游标作为 source,以将记录存储到对象存储的步骤作为 operation 来运行,并使用此游标的有效对象存储作为 store,clone 作为 value,此游标的有效键作为 key,并取消设置 no-overwrite 标志。
delete() 方法在被调用时,必须执行这些步骤
-
如果 transaction 不处于活动状态,则抛出一个 "
TransactionInactiveError"DOMException。 -
如果 transaction 是一个只读事务,则抛出一个 "
ReadOnlyError"DOMException。 -
如果游标的来源或有效对象存储已被删除,则抛出一个 "
InvalidStateError"DOMException。 -
如果此游标的获取值标志未设置(表示游标正在迭代或已迭代过末尾),则抛出一个 "
InvalidStateError"DOMException。 -
如果此游标的仅键标志被设置,则抛出一个 "
InvalidStateError"DOMException。 -
执行用于异步执行请求的步骤,并返回由这些步骤创建的
IDBRequest。这些步骤以当前游标作为 source,以从对象存储删除记录的步骤作为 operation 来运行,并分别使用此游标的有效对象存储和有效键作为 store 和 key。
未设置仅键标志的游标也实现了 IDBCursorWithValue 接口。
[Exposed=(Window,Worker)]
interface IDBCursorWithValue : IDBCursor {
readonly attribute any value;
};
value 属性的获取器必须返回游标当前的值。注意,如果此属性返回一个对象,则每次检查它时都会返回相同的对象实例,直到游标的值发生变化。这意味着如果对象被修改,任何检查游标值的人都会看到这些修改。然而,修改此类对象并不会修改数据库的内容。
4.9. IDBTransaction 接口
事务对象实现了以下接口
[Exposed=(Window,Worker)] interfaceIDBTransaction: EventTarget { readonly attribute DOMStringList objectStoreNames; readonly attribute IDBTransactionMode mode; [SameObject] readonly attribute IDBDatabase db; readonly attribute DOMException error; IDBObjectStore objectStore(DOMStringname); void abort(); // Event handlers: attribute EventHandler onabort; attribute EventHandler oncomplete; attribute EventHandler onerror; }; enumIDBTransactionMode{"readonly","readwrite","versionchange"};
- transaction .
objectStoreNames - 返回事务作用域内对象存储的名称列表。对于升级事务,这是数据库中的所有对象存储。
- transaction .
mode - 返回创建该事务时使用的模式(
"readonly"或"readwrite"),对于升级事务则返回"versionchange"。 - transaction .
db - 返回该事务的连接。
- transaction .
error - 如果事务已被中止,则返回提供原因的错误(一个
DOMException)。
objectStoreNames 属性的获取器必须执行这些步骤
error 属性的获取器必须返回此事务的错误,如果没有则返回 null。
- transaction .
objectStore(name) - 返回事务作用域内的一个
IDBObjectStore。 - transaction .
abort() - 中止事务。所有待处理的请求都将以 "
AbortError"DOMException失败,并且对数据库所做的所有更改都将被还原。
objectStore(name) 方法在被调用时,必须执行这些步骤
-
如果 transaction 已结束,则抛出一个 "
InvalidStateError"DOMException。 -
令 store 为此事务作用域内名为 name 的对象存储,如果不存在,则抛出一个 "
NotFoundError"DOMException。
当调用 abort() 方法时,必须执行以下步骤
-
如果此 事务 已 完成,则 抛出 "
InvalidStateError"DOMException。
onabort 属性是 abort 事件的事件处理器。
oncomplete 属性是 complete 事件的事件处理器。
onerror 属性是 error 事件的事件处理器。
5. 算法
5.1. 打开数据库
打开数据库 的步骤如下。这些步骤中的算法接受四个参数:请求打开 数据库 的 源 (origin)、数据库 名称、数据库 版本 以及 请求。
-
令 队列 为对应 源 和 名称 的 连接队列。
-
将 请求 添加到 队列 中。
-
等待,直到 队列 中所有先前的请求都被处理完毕。
-
如果 版本 未定义,则在 db 为 null 时令 版本 为 1,否则令其为 db 的 版本。
-
如果 db 为 null,令 db 为一个新的 数据库,其 名称 为 名称,版本 为 0,且不包含 对象存储空间。如果因任何原因失败,则返回适当的错误(例如 "
QuotaExceededError" 或 "UnknownError"DOMException)。 -
如果 db 的 版本 大于 版本,则返回一个新 创建的 "
VersionError"DOMException并终止这些步骤。 -
令 连接 为通往 db 的新 连接。
-
将 连接 的 版本 设置为 版本。
-
如果 db 的 版本 小于 版本,则
-
令 打开的连接 (openConnections) 为与 db 相关联的所有 连接 的集合,但不包括 连接。
-
对于 打开的连接 中的每个未设置 关闭挂起标志 的 条目,排队一个任务,向 条目 触发一个版本更改事件,该事件名为
versionchange,并携带 db 的 版本 和 版本 参数。 -
等待所有事件触发完毕。
-
如果 打开的连接 中的任何 连接 仍未关闭,排队一个任务,向 请求 触发一个版本更改事件,该事件名为
blocked,并携带 db 的 版本 和 版本 参数。 -
使用 连接、版本 和 请求 执行 运行升级事务 的步骤。
-
如果 连接 已 关闭,则返回一个新 创建的 "
AbortError"DOMException并终止这些步骤。 -
如果 升级事务 已被终止,则执行 关闭数据库连接 的步骤,并使用 连接、返回一个新 创建的 "
AbortError"DOMException并终止这些步骤。
-
-
返回 connection。
5.2. 关闭数据库
关闭数据库连接 的步骤如下。这些步骤接受两个参数:一个 连接 对象和一个可选的 强制标志。
-
设置 连接 的 关闭挂起标志。
-
如果设置了 强制标志,则对于每个使用 连接 创建的 事务,执行 终止事务 的步骤,并传入 事务 和一个新 创建的 "
AbortError"DOMException。 -
如果设置了 强制标志,则向 连接 触发一个事件,该事件名为
close。
5.3. 删除数据库
删除数据库 的步骤如下。这些步骤中的算法接受三个参数:请求删除 数据库 的 源、数据库 名称 以及 请求。
-
令 队列 为对应 源 和 名称 的 连接队列。
-
将 请求 添加到 队列 中。
-
等待,直到 队列 中所有先前的请求都被处理完毕。
-
令 打开的连接 为与 db 相关联的所有 连接 的集合。
-
对于 打开的连接 中的每个未设置 关闭挂起标志 的 条目,排队一个任务,向 条目 触发一个版本更改事件,该事件名为
versionchange,并携带 db 的 版本 和 null 参数。 -
等待所有事件触发完毕。
-
如果 打开的连接 中的任何 连接 仍未关闭,排队一个任务,向 请求 触发一个版本更改事件,该事件名为
blocked,并携带 db 的 版本 和 null 参数。 -
令 版本 为 db 的 版本。
-
删除 db。如果因任何原因失败,则返回适当的错误(例如 "
QuotaExceededError" 或 "UnknownError"DOMException)。 -
返回 版本。
5.4. 提交事务
提交事务 的步骤如下。此算法接受一个参数:要提交的 事务。
-
如果将更改写入 数据库 时发生错误,则遵循 终止事务 的步骤终止该事务,并传入 事务 和一个针对该错误的适当错误信息,例如 "
QuotaExceededError" 或 "UnknownError"DOMException。 -
排队一个任务以运行以下步骤
5.5. 终止事务
终止事务 的步骤如下。此算法接受两个参数:要终止的 事务 和 错误。
-
撤销 事务 对 数据库 所做的所有更改。对于 升级事务,这包括对 对象存储空间 和 索引 集合的更改,以及对 版本 的更改。在事务期间创建的任何 对象存储空间 和 索引 在其他算法中现在被视为已删除。
-
如果 错误 不为 null,将 事务 的 错误 设置为 错误。
-
对于 事务 的 请求列表 中每个未设置 完成标志 的 请求,终止 异步执行请求 的步骤,并 排队一个任务 以运行以下步骤
-
设置 请求 上的 完成标志。
-
将 请求 的 结果 设置为 undefined。
-
将 请求 的 错误 设置为一个新 创建的 "
AbortError"DOMException。 -
触发一个事件,该事件名为
error,以 请求 为目标,并将其bubbles和cancelable属性初始化为 true。
-
-
排队一个任务以运行以下步骤
5.6. 异步执行 请求
异步执行请求 的步骤如下。该算法接受一个 源 对象、要在数据库上执行的 操作 以及一个可选的 请求。
5.7. 运行升级事务
运行升级事务 的步骤如下。此算法接受三个参数:一个用于更新 数据库 的 连接 对象、一个要为 数据库 设置的新 版本,以及一个 请求。
-
令 db 为 连接 的 数据库。
-
取消设置 事务 的 活动标志。
-
启动 事务。
-
令 旧版本 为 db 的 版本。
-
排队一个任务以运行以下步骤
-
将 请求 的 结果 设置为 连接。
-
将 请求 的 事务 设置为 事务。
-
设置 事务 的 活动标志。
-
令 didThrow 为运行 触发一个版本更改事件 的步骤的结果,该事件名为
upgradeneeded,以 请求 为目标,并携带 旧版本 和 版本 参数。 -
取消设置 事务 的 活动标志。
-
如果设置了 didThrow,则使用将 错误 属性设置为新 创建的 "
AbortError"DOMException的方式,执行 终止事务 的步骤。
-
-
等待 事务 完成。
5.8. 终止升级事务
终止升级事务 的步骤如下,传入 事务。
-
令 连接 为 事务 的 连接。
-
令 数据库 为 连接 的 数据库。
-
如果 数据库 先前存在,将 连接 的 对象存储空间集合 设置为 数据库 中的 对象存储空间 集合;如果 数据库 是新创建的,则设置为空集。
-
对于每个与 事务 相关联的 对象存储句柄 句柄,包括在 事务 期间创建或删除的 对象存储空间 的句柄
这如何能够被观察到?
尽管脚本在 事务 被终止后,无法通过IDBTransaction实例上的objectStore()方法访问 对象存储空间,但它仍然可以持有IDBObjectStore实例的引用,从而查询name和indexNames属性。
5.9. 触发成功事件
若要向 请求 触发一个成功事件,实现必须运行以下步骤
-
将 事件 的
type属性设置为 "success"。 -
将 事件 的
bubbles和cancelable属性设置为 false。 -
令 事务 为 请求 的 事务。
-
令 legacyOutputDidListenersThrowFlag 最初处于未设置状态。
-
设置 事务 的 活动标志。
-
以 请求 为目标,并携带 legacyOutputDidListenersThrowFlag,分发 事件。
-
取消设置 事务 的 活动标志。
-
如果设置了 legacyOutputDidListenersThrowFlag,则运行 终止事务 的步骤,并传入 事务 和一个新 创建的 "
AbortError"DOMException。
5.10. 触发错误事件
若要向 请求 触发一个错误事件,实现必须运行以下步骤
-
将 事件 的
type属性设置为 "error"。 -
将 事件 的
bubbles和cancelable属性设置为 true。 -
令 事务 为 请求 的 事务。
-
令 legacyOutputDidListenersThrowFlag 最初处于未设置状态。
-
设置 事务 的 活动标志。
-
以 请求 为目标,并携带 legacyOutputDidListenersThrowFlag,分发 事件。
-
取消设置 事务 的 活动标志。
-
如果设置了 legacyOutputDidListenersThrowFlag,则运行 终止事务 的步骤,并传入 事务 和一个新 创建的 "
AbortError"DOMException,并终止这些步骤。即使事件的 取消标志 未设置,也会执行此操作。
5.11. 克隆值
若要在 目标域 中对 值 进行 克隆,实现必须运行以下步骤
-
令 序列化 为 ? StructuredSerializeForStorage(值)。
-
令 克隆 为 ? StructuredDeserialize(序列化, 目标域)。
-
返回 clone。
6. 数据库操作
本节描述了对 数据库 中 对象存储空间 和 索引 数据所做的各种操作。这些操作由 异步执行请求 的步骤运行。
6.1. 对象存储空间存储操作
将记录存储到对象存储空间 的步骤如下,参数包括 存储空间、值、可选的 键 以及 无覆盖标志。
-
如果 存储空间 使用 键生成器,则
-
如果 键 未定义,则
-
令 键 为运行 生成键 的步骤的结果,以 存储空间 为目标。
-
如果 键 生成失败,则此操作因 "
ConstraintError"DOMException而失败。无需采取进一步行动,终止此算法。 -
如果 存储空间 同时使用 内联键,则使用 值、键 和 存储空间 的 键路径,运行 使用键路径将键注入值 的步骤。
-
-
否则,使用 键 为 存储空间 运行 可能更新键生成器 的步骤。
-
-
如果向这些步骤传递了 无覆盖标志 且已设置,并且 存储空间 中已经存在键 等于 键 的 记录,则此操作因 "
ConstraintError"DOMException而失败。无需采取进一步行动,终止此算法。 -
如果 存储空间 中已经存在键 等于 键 的 记录,则使用 从对象存储空间删除记录 的步骤从 存储空间 中移除该 记录。
-
在 存储空间 中存储一条记录,包含 键 作为其键,! StructuredSerializeForStorage(值) 作为其值。记录存储在对象存储空间的 记录列表 中,使得列表根据记录的键以 升序 排列。
-
对于每个 引用 存储空间 的 索引
-
令 索引键 为运行 使用键路径从值提取键 的步骤的结果,使用 值、索引 的 键路径 以及 索引 的 multiEntry 标志。
-
如果 索引键 是异常、无效或失败,则对 索引 不采取进一步行动,并为下一个索引继续执行这些步骤。
-
如果 索引 的 multiEntry 标志 未设置,或者 索引键 不是 数组键,且如果 索引 中已经包含键 等于 索引键 的 记录,并且 索引 设置了 唯一标志,则此操作因 "
ConstraintError"DOMException而失败。无需采取进一步行动,终止此算法。 -
如果 索引 的 multiEntry 标志 已设置且 索引键 是 数组键,且如果 索引 中已经包含键 等于 索引键 的任何 子键 的 记录,并且 索引 设置了 唯一标志,则此操作因 "
ConstraintError"DOMException而失败。无需采取进一步行动,终止此算法。 -
如果 索引 的 multiEntry 标志 未设置,或者 索引键 不是 数组键,则在 索引 中存储一条记录,包含 索引键 作为其键,键 作为其值。记录存储在 索引 的 记录列表 中,使得列表主要按记录的键、次要按记录的值以 升序 排列。
-
如果 索引 的 multiEntry 标志 已设置且 索引键 是 数组键,则对于 索引键 的 子键 中的每个 子键,在 索引 中存储一条记录,包含 子键 作为其键,键 作为其值。记录存储在 索引 的 记录列表 中,使得列表主要按记录的键、次要按记录的值以 升序 排列。
-
-
返回 key。
6.2. 对象存储空间检索操作
从对象存储空间检索值 的步骤如下,参数包括 目标域、存储空间 和 范围
从对象存储空间检索多个值 的步骤如下,参数包括 目标域、存储空间、范围 和可选的 计数
-
如果未提供 计数 或为 0,令 计数 为无穷大。
-
令 列表 为空列表。
-
对于 记录集合 中的每个 记录
-
令 序列化 为 记录 的 值。
-
令 条目 为 ! StructuredDeserialize(序列化, 目标域)。
-
将 条目 追加到 列表。
-
-
返回 列表 转换为 sequence<any> 的结果。
从对象存储空间检索键 的步骤如下,参数包括 存储空间 和 范围
从对象存储空间检索多个键 的步骤如下,参数包括 存储空间、范围 和可选的 计数
-
如果未提供 计数 或为 0,令 计数 为无穷大。
-
令 列表 为空列表。
-
对于 记录集合 中的每个 记录
-
令 条目 为运行 将键转换为值 的步骤的结果,使用 记录 的键。
-
将 条目 追加到 列表。
-
-
返回 列表 转换为 sequence<any> 的结果。
6.3. 索引检索操作
从索引检索引用值 的步骤如下,参数包括 目标域、索引 和 范围。
从索引检索多个引用值 的步骤如下,参数包括 目标域、索引、范围 和可选的 计数
-
如果未提供 计数 或为 0,令 计数 为无穷大。
-
令 列表 为空列表。
-
对于 记录集合 中的每个 记录
-
令 序列化 为 记录 的 引用值。
-
令 条目 为 ! StructuredDeserialize(序列化, 目标域)。
-
将 条目 追加到 列表。
-
-
返回 列表 转换为 sequence<any> 的结果。
从索引检索值 的步骤如下,参数包括 索引 和 范围。
从索引检索多个值 的步骤如下,参数包括 索引、范围 和可选的 计数
-
如果未提供 计数 或为 0,令 计数 为无穷大。
-
令 列表 为空列表。
-
对于 记录集合 中的每个 记录
-
令 条目 为运行 将键转换为值 的步骤的结果,使用 记录 的值。
-
将 条目 追加到 列表。
-
-
返回 列表 转换为 sequence<any> 的结果。
6.4. 对象存储空间删除操作
从对象存储空间删除记录 的步骤如下,参数包括 存储空间 和 范围。
6.5. 记录计数操作
计算范围内的记录数 的步骤如下,参数包括 源 和 范围
-
令 计数 为 源 的记录列表中键 位于 范围 内的记录数(如果存在)。
-
返回 count。
6.6. 对象存储空间清空操作
6.7. 游标迭代操作
迭代游标 的步骤如下,参数包括 目标域、游标、一个可选的要迭代到的 键 和 主键,以及可选的 计数。
-
令 源 为 游标 的 源。
-
令 方向 为 游标 的 方向。
-
令 记录集合 为 源 中的 记录 列表。
-
令 范围 为 游标 的 范围。
-
令 位置 为 游标 的 位置。
-
令 对象存储空间位置 为 游标 的 对象存储空间位置。
-
如果未提供 计数,令 计数 为 1。
-
当 计数 大于 0 时
-
切换 方向
"next"- 令 找到的记录 为 记录集合 中满足以下所有要求的第一条记录
"nextunique"- 令 找到的记录 为 记录集合 中满足以下所有要求的第一条记录
"prev"- 令 found record 为 records 中满足以下所有条件的最后一条记录
"prevunique"- 令 temp record 为 records 中满足以下所有条件的最后一条记录
如果定义了 temp record,令 found record 为 records 中第一条键 等于 temp record 键的记录。
-
如果未定义 found record,则
-
令 position 为 found record 的键。
-
如果 source 是一个 索引,令 object store position 为 found record 的值。
-
将 count 减 1。
-
-
将 cursor 的 位置 设置为 position。
-
如果 source 是一个 索引,将 cursor 的 对象存储位置 设置为 object store position。
-
将 cursor 的 键 设置为 found record 的键。
-
如果 cursor 的 仅键标志 未设置,则
-
令 serialized 为 found record 的 引用值。
-
将 cursor 的 值 设置为 ! StructuredDeserialize(serialized, targetRealm)
-
-
设置 cursor 的 已获取值标志。
-
返回 cursor。
7. ECMAScript 绑定
本节定义了本规范中定义的 键 值如何与 ECMAScript 值相互转换,以及如何使用 键路径 从 ECMAScript 值中提取或注入这些键。本节引用了 ECMAScript 语言规范中的类型和算法,并使用了一些算法约定。[ECMA-262] 此处未详细说明的转换定义在 [WEBIDL] 中。
7.1. 从值中提取键
使用 value、keyPath 和可选的 multiEntry 标志 从值中提取键 的步骤如下。这些步骤的结果是一个 键、无效值、失败,或者步骤可能抛出异常。
-
令 r 为运行使用 value 和 keyPath 评估值上的键路径 的步骤的结果。重新抛出任何异常。
-
如果 r 是失败,则返回失败。
-
令 key 为运行以下步骤的结果:如果 multiEntry 标志 未设置,则使用 r 将值转换为键;否则使用 r 将值转换为 multiEntry 键。重新抛出任何异常。
-
如果 key 无效,则返回无效。
-
返回 key。
使用 value 和 keyPath 评估值上的键路径 的步骤如下。这些步骤的结果是一个 ECMAScript 值或失败,或者步骤可能抛出异常。
-
如果 keyPath 是字符串列表,则
-
如果 keyPath 是空字符串,则返回 value 并跳过剩余步骤。
-
令 identifiers 为 严格拆分 keyPath(使用 U+002E FULL STOP 字符 (.))的结果。
-
对于 identifiers 中的每个 identifier,跳至下方的相应步骤
- 如果 Type(value) 是 String,且 identifier 是 "
length" - 令 value 为等于 value 中元素数量的 Number。
- 如果 value 是 Array 且 identifier 是 "
length" - 令 value 为 ! ToLength(! Get(value, "
length"))。 - 如果 value 是
Blob且 identifier 是 "size" - 令 value 为等于 value 的
size的 Number。 - 如果 value 是
Blob且 identifier 是 "type" - 令 value 为等于 value 的
type的 String。 - 如果 value 是
File且 identifier 是 "name" - 令 value 为等于 value 的
name的 String。 - 如果 value 是
File且 identifier 是 "lastModified" - 令 value 为等于 value 的
lastModified的 Number。 - 如果 value 是
File且 identifier 是 "lastModifiedDate" - 令 value 为一个新的 Date 对象,其 [[DateValue]] 内部槽等于 value 的
lastModified。 - 否则
-
-
如果 Type(value) 不是 Object,则返回失败。
-
令 hop 为 ! HasOwnProperty(value, identifier)。
-
如果 hop 为 false,则返回失败。
-
如果 value 为 undefined,则返回失败。
-
- 如果 Type(value) 是 String,且 identifier 是 "
-
断言:value 不是 突然完成。
-
返回 value。
7.2. 向值中注入键
检查键是否可以注入到值中 的步骤如下。该算法接收 value 和 keyPath,并输出 true 或 false。
-
令 identifiers 为 严格拆分 keyPath(使用 U+002E FULL STOP 字符 (.))的结果。
-
断言:identifiers 不为空。
-
移除 identifiers 的最后一个成员。
-
对于 identifiers 中的每个剩余的 identifier(如果有)
使用键路径向值中注入键 的步骤如下。该算法接收 value、key 和 keyPath。
-
令 identifiers 为 严格拆分 keyPath(使用 U+002E FULL STOP 字符 (.))的结果。
-
断言:identifiers 不为空。
-
令 last 为 identifiers 的最后一个成员并将其从列表中删除。
-
对于 identifiers 中的每个剩余 identifier
-
令 hop 为 ! HasOwnProperty(value, identifier)。
-
如果 hop 为 false,则
-
令 o 为通过表达式
({})创建的新 Object。 -
令 status 为 CreateDataProperty(value, identifier, o)。
-
断言:status 为 true。
-
-
令 keyValue 为运行 将键转换为值 的步骤的结果(使用 key)。
-
令 status 为 CreateDataProperty(value, last, keyValue)。
-
断言:status 为 true。
7.3. 将键转换为值
将键转换为值 的步骤如下。这些步骤接收一个参数 key,并返回一个 ECMAScript 值。
-
令 type 为 key 的 类型。
-
令 value 为 key 的 值。
-
切换至 type
- number
- 返回一个等于 value 的 ECMAScript Number 值
- string
- 返回一个等于 value 的 ECMAScript String 值
- 日期
-
-
令 date 为执行以 value 为单个参数的 ECMAScript Date 构造函数的结果。
-
断言:date 不是 突然完成。
-
返回 date。
-
- 二进制:
-
-
令 len 为 value 的长度。
-
令 buffer 为执行以 len 为参数的 ECMAScript ArrayBuffer 构造函数的结果。
-
断言:buffer 不是 突然完成。
-
将 buffer 的 [[ArrayBufferData]] 内部槽中的条目设置为 value 中的条目。
-
返回 buffer。
-
- array
-
-
令 array 为执行不带参数的 ECMAScript Array 构造函数的结果。
-
断言:array 不是 突然完成。
-
令 len 为 value 的长度。
-
令 index 为 0。
-
当 index 小于 len 时
-
令 entry 为运行 将键转换为值 的步骤的结果,输入为 value 的第 index 个条目。
-
令 status 为 CreateDataProperty(array, index, entry)。
-
断言:status 为 true。
-
将 index 增加 1。
-
-
返回 array。
-
7.4. 将值转换为键
将值转换为键 的步骤如下。这些步骤接收两个参数:ECMAScript 值 input 和可选的集合 seen。结果是一个 键 或无效值,或者步骤可能抛出异常。
-
如果未提供 seen,令 seen 为一个新的空集合。
-
如果 input 在 seen 中,则返回无效。
-
跳转到下方的相应步骤
- 如果 Type(input) 是 Number
- 如果 input 是 Date(拥有 [[DateValue]] 内部槽)
- 如果 Type(input) 是 String
- 如果 input 是 缓冲区源类型
-
-
令 octets 为运行 获取缓冲区源持有的字节副本 的步骤的结果(参数为 input)。重新抛出任何异常。
-
- 如果 IsArray(input)
-
-
将 input 添加到 seen。
-
令 keys 为一个新的空列表。
-
令 index 为 0。
-
当 index 小于 len 时
-
令 hop 为 ? HasOwnProperty(input, index)。
-
如果 hop 为 false,则返回无效。
-
令 key 为运行 将值转换为键 的步骤的结果(参数为 entry 和 seen)。
-
ReturnIfAbrupt(key)。
-
如果 key 为无效,则中止这些步骤并返回无效。
-
将 key 追加到 keys。
-
将 index 增加 1。
-
- 否则
- 返回无效。
将值转换为 multiEntry 键 的步骤如下。这些步骤接收一个参数:ECMAScript 值 input。结果是一个 键 或无效值,或者步骤可能抛出异常。
8. 隐私考量
本节是非规范性的。
8.1. 用户跟踪
第三方主机(或任何能够将内容分发到多个站点的对象)可以使用存储在其客户端数据库中的唯一标识符来跨多个会话跟踪用户,从而构建用户活动档案。如果结合一个能够识别用户真实身份对象的站点(例如需要身份认证凭据的电子商务网站),这可能使压迫性组织能够比在纯匿名 Web 使用环境中更精确地定位个人。
有许多技术可以用来降低用户跟踪的风险:
- 拦截第三方存储
- 用户代理可以限制脚本对数据库对象的访问,仅允许源自顶层文档的浏览上下文域名的脚本进行访问,例如禁止运行在
iframe中的其他域名的页面访问该 API。 - 存储数据的过期
-
用户代理可能会在一段时间后自动删除存储的数据。
这可以限制站点跟踪用户的能力,因为站点将只能在用户向站点本身进行身份认证(例如通过购物或登录服务)时,才能够跨多个会话跟踪用户。
然而,这也使用户的数据面临风险。
- 将持久化存储视为 Cookie
-
用户代理应以一种将数据库功能与 HTTP 会话 Cookie 强相关联的方式向用户展示。 [COOKIES]
这可能会鼓励用户以合理的怀疑态度来看待此类存储。
- 站点特定的数据库访问安全名单
-
用户代理可能要求用户在站点使用该功能之前,先授权其访问数据库。
- 存储数据的源(Origin)跟踪
-
用户代理可能会记录那些包含导致数据存储的第三方源内容的站点的 源(origins)。
如果此信息随后被用于展示当前持久化存储中的数据概览,它将允许用户就是否要清理持久化存储的哪些部分做出明智的决定。结合黑名单(“删除此数据并防止此域再次存储任何数据”),用户可以将持久化存储的使用限制在她信任的站点上。
- 共享黑名单
-
用户代理可能允许用户共享他们的持久化存储域黑名单。
This would allow communities to act together to protect their privacy.
虽然这些建议防止了该 API 被用于琐碎的用户跟踪,但它们并没有完全阻止这种情况。在单一域名内,站点可以继续在会话期间跟踪用户,然后可以将所有这些信息与站点获取的任何识别信息(姓名、信用卡号、地址)一起传递给第三方。如果第三方与多个站点合作以获取此类信息,仍然可以创建用户档案。
However, user tracking is to some extent possible even with no cooperation from the user agent whatsoever, for instance by using session identifiers in URLs, a technique already commonly used for innocuous purposes but easily repurposed for user tracking (even retroactively). This information can then be shared with other sites, using visitors' IP addresses and other user-specific data (e.g. user-agent headers and configuration settings) to combine separate sessions into coherent user profiles.
8.2. Cookie 复活
如果持久化存储的用户界面将本规范中所述的持久化存储功能中的数据与 HTTP 会话 Cookie 中的数据分开展示,那么用户很可能会删除其中之一而保留另一个。这将允许站点将这两个功能作为彼此的冗余备份,从而挫败用户保护其隐私的尝试。
8.3. 数据敏感性
用户代理应将持久存储的数据视为潜在敏感信息;电子邮件、日历预约、健康记录或其他机密文档完全可能存储在此机制中。
To this end, user agents should ensure that when deleting data, it is promptly deleted from the underlying storage.
9. 安全考量
9.1. DNS 欺骗攻击
由于 DNS 欺骗攻击的潜力,无法保证声称在特定域名的主机确实来自该域名。为了缓解这种情况,页面可以使用 TLS。使用 TLS 的页面可以确信,只有同样使用 TLS 且拥有标识其为来自相同域名证书的页面才能访问其数据库。
9.2. 跨目录攻击
共享同一主机名的不同作者,例如在 geocities.com 上托管内容的用户,都共享同一套数据库。
没有功能可以按路径名限制访问。因此建议共享主机上的作者避免使用这些功能,因为其他作者可以轻易地读取数据并覆盖它。
9.3. 实现风险
实现这些持久化存储功能时,两个主要的风险是:允许恶意站点从其他域名读取信息,以及允许恶意站点写入稍后可被其他域名读取的信息。
让第三方站点读取本不应从其域读取的数据会导致 信息泄露。例如,一个域上的用户购物愿望清单可能被另一个域用于定向广告;或者一个文字处理站点存储的用户正在进行中的机密文档可能被竞争公司的站点检查。
让第三方站点将数据写入其他域的持久化存储可能导致 信息欺骗,这同样危险。例如,敌意站点可以向用户的愿望清单添加记录;或者敌意站点可以将用户的会话标识符设置为一个已知的 ID,敌意站点随后可以使用该 ID 跟踪用户在受害站点上的操作。
因此,严格遵循本规范中描述的 源(origin) 模型对于用户安全非常重要。
如果使用源或数据库名称来构建持久化到文件系统的路径,则必须对它们进行适当的转义,以防止对手使用诸如 "../" 之类的相对路径访问其他源的信息。
9.4. 持久化风险
实际实现会将数据持久化到非易失性存储介质。数据在存储时将被序列化,在检索时将被反序列化,尽管序列化格式的细节将取决于用户代理。用户代理很可能会随着时间的推移更改其序列化格式。例如,格式可能会更新以处理新的数据类型,或以提高性能。为了满足本规范的操作要求,实现必须以某种方式处理较旧的序列化格式。对旧数据处理不当可能会导致安全问题。除了基本的序列化问题外,序列化数据还可能编码在较新版本的用户代理中无效的假设。
一个实际例子是 RegExp 类型。StructuredSerializeForStorage 操作允许序列化 RegExp 对象。典型的用户代理会将正则表达式编译为本机机器指令,并对输入数据的传递方式和结果返回方式做出假设。如果此内部状态作为存储到数据库的数据的一部分被序列化,当稍后反序列化内部表示时,可能会出现各种问题。例如,传递数据到代码的方式可能已经改变。编译器输出中的安全漏洞可能在用户代理的更新中被识别并修复,但仍保留在序列化的内部状态中。
用户代理必须适当识别和处理旧数据。一种方法是在序列化格式中包含版本标识符,并在遇到旧数据时从脚本可见状态重建任何内部状态。
10. 修订历史
以下是自本规范上次发布以来的变更信息性摘要。完整的修订历史可以在 此处 找到。关于第一版的修订历史,请参阅 该文档的修订历史。
-
解决空数组的比较问题。 (bug #27712)
-
在
IDBObjectStore上添加了openKeyCursor()。 (bug #19955) -
更正了
IDBIndex上get()、getKey()和openKeyCursor()所使用的 source。 -
添加了关于
IDBDatabase对象垃圾回收的详细信息。 (bug #25223) -
在接口上添加了
[Exposed=(Window,Worker)]注解。 -
向 关闭数据库连接 的步骤中添加了 forced 标志,并描述了 “
close” 事件的触发以及onclose。 (bug #22540) -
将规范转换为更算法化的风格,并更严格地定义了诸如 键 之类的抽象类型。 (bug #17681)
-
在
IDBObjectStore上添加了getAll()和getAllKeys(),并在IDBIndex上添加了getAll()和getAllKeys()。 (bug #16595) -
将
DOMError替换为DOMException。 (bug #16) -
在
IDBTransaction上添加了objectStoreNames。 (bug #18) -
允许通过
IDBObjectStore的name和IDBIndex的name属性设置器重命名存储和索引。 (bug #22) -
在
IDBCursor上添加了continuePrimaryKey()。 (bug #14) -
在
IDBKeyRange上添加了includes()。 (bug #41) -
在
IDBObjectStore上添加了getKey()。 (bug #26) -
澄清了事务何时可以尝试提交。 (bug #77)
-
确保在排队任务的上下文中触发事件。 (bug #83)
-
定义了在应用多个错误条件时异常的优先级。 (bug #11)
-
移除
IDBEnvironment;改为使用partial interface暴露全局对象。 (bug #94) -
澄清了
deleteDatabase()何时可能失败。 (bug #74) -
为每个方法添加非规范性文档。 (bug #110)
-
如果从不透明源调用
open()或deleteDatabase(),则抛出SecurityError。 (bug #148) -
与 [DOM41] 中的 legacyOutputDidListenersThrowFlag 钩子集成,取代 monkey patching。 (bug #140)
-
为 [HTML52] 定义 清理 Indexed Database 事务 钩子,取代 monkey patching。 (bug #87)
-
修复键生成算法中边缘情况的处理。 (bug #147)
-
使用 [HTML52] 的 StructuredSerialize 和 StructuredDeserialize 钩子。 (bug #170)
-
在 IDL 中适当的地方使用 [
SameObject]/[NewObject]。 (issue #193, issue #194) -
事务是否 活动 的测试可以在 异步执行请求 的步骤中作为断言。 (issue #192)
-
使用 [HTML52] 的 StructuredSerializeForStorage 钩子。 (issue #197, issue #152)
-
定义 数据库 的关联 升级事务,以使
createObjectStore()和deleteObjectStore()抛出的异常与测试和实现保持一致。 (issue #192) -
为来自
open()和deleteDatabase()的请求适当地设置 [=request/result]/已完成标志。 (issue #161)
11. 致谢
特别感谢第一版的原始作者 Nikunj Mehta,以及第一版的其他编辑 Jonas Sicking、Eliot Graff、Andrei Popescu 和 Jeremy Orlow。
Garret Swart 在本规范的设计中发挥了极大的影响。
感谢 Tab Atkins, Jr. 创建并维护了用于创建本文档的规范编写工具 Bikeshed,以及他提供的通用编写建议。
特别感谢 Chris Anderson、Pablo Castro、Victor Costan、Kristof Degrave、Jake Drew、Ben Dilts、João Eiras、Alec Flett、Dana Florescu、David Grogan、Israel Hilerio、Jerome Hode、Kyle Huey、Philip Jägenstedt、Laxminarayan G Kamath A、Anne van Kesteren、Adam Klein、Tobie Langel、Kang-Hao Lu、Andrea Marchesini、Glenn Maynard、Ms2ger、Odin Omdal、Danillo Paiva、Olli Pettay、Addison Phillips、Simon Pieters、Anthony Ramine、Yonathan Randolph、Arun Ranganathan、Margo Seltzer、Maciej Stachowiak、Bevis Tseng、Ben Turner、Kyaw Tun、Hans Wennborg、Shawn Wilsher、Brett Zamir、Boris Zbarsky、Zhiqiang Zhang 和 Kris Zyp,他们所有的反馈和建议都促成了本规范的改进。