Guest User

Untitled

a guest
Mar 23rd, 2018
83
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.15 KB | None | 0 0
  1. // Opens a connection to an IndexedDB database.
  2. //
  3. // @param [String] name the IDB database name
  4. // @return [Promise<IDBDatabase>] a connection to the opened database
  5. function OpenDatabase(name) {
  6. return new Promise((resolve, reject) => {
  7. const request = indexedDB.open(name);
  8. request.onsuccess = () => { resolve(request.result); };
  9. request.onerror = () => { reject(request.error); };
  10. });
  11. }
  12.  
  13. // Internal function for reading and/or deleting data from IndexedDB.
  14. //
  15. // Must be called in a readwrite transaction whose scope includes the object
  16. // store(s) owning the given source. Guarantees that the transaction will still
  17. // be alive when the callback is called.
  18. //
  19. // @param [IDBObjectStore | IDBIndex] source the IDB object store or index whose
  20. // values are getting deleted
  21. // @param [Object] options
  22. // @option options [Number] limit stop after reading this many values
  23. // @option options [Boolean] deleteValues delete the read values if true
  24. // @param [function(?Error, ?Number)] callback called when done; in case of
  25. // success, the callback is passed the number of items that were read (and
  26. // potentially deleted)
  27. // @return undefined
  28. function DoIterate(source, options, callback) {
  29. let items_read = 0;
  30. let items_left =
  31. ('limit' in options) ? options.limit : Number.MAX_SAFE_INTEGER;
  32.  
  33. const request =
  34. options.deleteValues ? source.openCursor() : source.openKeyCursor();
  35. request.onsuccess = () => {
  36. const cursor = request.result;
  37. if (!cursor) {
  38. callback(null, items_read);
  39. return;
  40. }
  41.  
  42. const delete_request = options.deleteValues ? cursor.delete() : {};
  43. delete_request.onsuccess = () => {
  44. items_left -= 1; items_read += 1;
  45. if (items_left === 0) {
  46. callback(null, items_read);
  47. return;
  48. }
  49. cursor.continue();
  50. };
  51. delete_request.onerror = () => { callback(request.error); };
  52.  
  53. if (!options.deleteValues)
  54. delete_request.onsuccess();
  55. };
  56. request.onerror = () => { callback(request.error); };
  57. }
  58.  
  59. // Deletes the first N items in an IDB data source and iterates over the rest.
  60. //
  61. // Must be called in a readwrite transaction whose scope includes the object
  62. // store(s) owning the given source. Guarantees that the transaction will still
  63. // be alive when the callback is called.
  64. //
  65. // @param [IDBObjectStore | IDBIndex] source the IDB object store or index whose
  66. // values are getting deleted
  67. // @param [Object] options
  68. // @option options [Number] limit stop after reading this many values
  69. // @option options [Boolean] deleteValues delete the read values if true
  70. // @param [function(?Error, ?Boolean)] callback called when done; in case of
  71. // success, the callback is passed "true" if at least one item was deleted,
  72. // or "false" if no item was deleted
  73. // @return undefined
  74. function IterateAndDelete(source, options, callback) {
  75. DoIterate(source, { limit: options.limit, deleteValues: true },
  76. (error, deleted_count) => {
  77. if (error) {
  78. callback(error);
  79. return;
  80. }
  81. const has_deleted_items = (deleted_count !== 0);
  82. DoIterate(source, { deleteValues: false }, (error, _) => {
  83. if (error) {
  84. callback(error);
  85. return;
  86. }
  87. callback(null, has_deleted_items);
  88. });
  89. });
  90. }
  91.  
  92. // Deletes the first N keys in each index and iterates over all the indexes.
  93. //
  94. // Must be called in a readwrite transaction whose scope includes the object
  95. // store(s) owning the given indexes. Guarantees that the transaction will still
  96. // be alive when the callback is called.
  97. //
  98. // @param [Array<IDBIndex>] indexes the indexes to iterate over; to ensure
  99. // tombstone cleanup, this set must contain all the indexes of a object
  100. // store
  101. // @param [Object] options
  102. // @option options [Number] limit the number of keys to delete from each index
  103. // @param [function(Error, Boolean)] callback called when done; in case of
  104. // success,
  105. function DeleteIndexKeys(indexes, options, callback) {
  106. let current_index = -1;
  107. let has_deleted_items = false;
  108. const Callback = (error, has_deleted_items_in_index) => {
  109. has_deleted_items = has_deleted_items || has_deleted_items_in_index;
  110. current_index += 1;
  111. if (current_index === indexes.length) {
  112. callback(null, has_deleted_items);
  113. return;
  114. }
  115. IterateAndDelete(indexes[current_index], options, Callback);
  116. };
  117. Callback(null, false);
  118. }
  119.  
  120. // Deletes N items from an object store.
  121. //
  122. // This is intended to be called repeatedly when deleting an object store, to
  123. // avoid generating a single IndexedDB transaction that's too large to fit in
  124. // memory.
  125. //
  126. // @param [IDBDatabase] database the database owning the object store
  127. // @param [string] object_store_name the name of the target object store
  128. // @param [Object] options
  129. // @option options [number] limit the maximum number of items to delete
  130. // @option options [Boolean] drain_indexes if true, deletes items via indexes;
  131. // otherwise, deletes item by iterating over the object store
  132. // @return [Promise<Boolean>] resolves with true if at least one item was
  133. // deleted, and false otherwise
  134. function DrainObjectStore(database, object_store_name, options) {
  135. return new Promise((resolve, reject) => {
  136. let has_deleted_items = false;
  137. const transaction = database.transaction([object_store_name], 'readwrite');
  138. transaction.oncomplete = () => { resolve(has_deleted_items); };
  139. transaction.onerror = () => { reject(transaction.error); };
  140.  
  141. const object_store = transaction.objectStore(object_store_name);
  142. const indexes = Array.from(object_store.indexNames).map(
  143. (name) => object_store.index(name));
  144.  
  145. const per_index_limit =
  146. (options.drain_indexes && indexes.length !== 0) ?
  147. options.limit / indexes.length : 0;
  148.  
  149. DeleteIndexKeys(
  150. indexes, { limit: per_index_limit }, (error, call_deleted_items) => {
  151. if (error) {
  152. reject(error);
  153. return;
  154. }
  155. has_deleted_items = call_deleted_items;
  156. if (has_deleted_items) {
  157. // Let the transaction commit. Enough work was done.
  158. return;
  159. }
  160.  
  161. DoIterate(
  162. object_store, { limit: options.limit }, (error, deleted_count) => {
  163. if (error) {
  164. reject(error);
  165. return;
  166. }
  167. has_deleted_items = (deleted_count !== 0);
  168. // Let the transaction commit.
  169. });
  170. });
  171. });
  172. }
  173.  
  174. // Deletes the contents of an IndexedDB object store.
  175. //
  176. // The deletion is done in many small transactions, to avoid generating a single
  177. // IndexedDB transaction that's too large to fit in memory. Does not delete the
  178. // object store itself.
  179. //
  180. // @param [IDBDatabase] database the database owning the object store
  181. // @param [string] object_store_name the name of the target object store
  182. // @param [Object] options
  183. // @option options [number] step_limit maximum number of items to delete in a
  184. // single transaction
  185. // @return [Promise<>] resolves when the data inside the object store is deleted
  186. async function DeleteObjectStore(database, object_store_name, options) {
  187. while (true) {
  188. const seen_items = await DrainObjectStore(
  189. database, object_store_name,
  190. { limit: options.step_limit, drain_indexes: true });
  191. if (!seen_items)
  192. break;
  193. }
  194.  
  195. // Delete any items that may not be listed in indexes.
  196. while (true) {
  197. const seen_items = await DrainObjectStore(
  198. database, object_store_name,
  199. { limit: options.step_limit, drain_indexes: false });
  200. if (!seen_items)
  201. break;
  202. }
  203. }
  204.  
  205. // Deletes the contents of an IndexedDB database.
  206. //
  207. // The deletion is done in many small transactions, to avoid generating a single
  208. // IndexedDB transaction that's too large to fit in memory. Does not delete the
  209. // database's object stores or indexes.
  210. //
  211. // @param [IDBDatabase] database the database to be deleted
  212. // @return [Promise<>] resolves when the data inside the database is deleted
  213. async function DeleteDatabase(database, options) {
  214. for (let object_store_name of database.objectStoreNames)
  215. await DeleteObjectStore(database, object_store_name, options);
  216. }
  217.  
  218. // Driver for testing the code above.
  219. (async () => {
  220. const database = await OpenDatabase('messages0');
  221. await DeleteDatabase(database, { step_limit: 10 });
  222. database.close();
  223. console.log('Database drained');
  224. })();
Add Comment
Please, Sign In to add comment