kvdb_web/
lib.rs

1// Copyright 2020 Parity Technologies
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! A key-value database for use in browsers
10//!
11//! Writes data both into memory and IndexedDB, reads the whole database in memory
12//! from the IndexedDB on `open`.
13
14#![deny(missing_docs)]
15
16mod error;
17mod indexed_db;
18
19use kvdb::{DBTransaction, DBValue};
20use kvdb_memorydb::{self as in_memory, InMemory};
21use send_wrapper::SendWrapper;
22use std::io;
23
24pub use error::Error;
25pub use kvdb::KeyValueDB;
26
27use futures::prelude::*;
28
29use web_sys::IdbDatabase;
30
31/// Database backed by both IndexedDB and in memory implementation.
32pub struct Database {
33	name: String,
34	version: u32,
35	columns: u32,
36	in_memory: InMemory,
37	indexed_db: SendWrapper<IdbDatabase>,
38}
39
40// TODO: implement when web-based implementation need memory stats
41parity_util_mem::malloc_size_of_is_0!(Database);
42
43impl Database {
44	/// Opens the database with the given name,
45	/// and the specified number of columns (not including the default one).
46	pub async fn open(name: String, columns: u32) -> Result<Database, error::Error> {
47		let name_clone = name.clone();
48		// let's try to open the latest version of the db first
49		let db = indexed_db::open(name.as_str(), None, columns).await?;
50
51		// If we need more column than the latest version has,
52		// then bump the version (+ 1 for the default column).
53		// In order to bump the version, we close the database
54		// and reopen it with a higher version than it was opened with previously.
55		// cf. https://github.com/paritytech/parity-common/pull/202#discussion_r321221751
56		let db = if columns + 1 > db.columns {
57			let next_version = db.version + 1;
58			drop(db);
59			indexed_db::open(name.as_str(), Some(next_version), columns).await?
60		} else {
61			db
62		};
63		// populate the in_memory db from the IndexedDB
64		let indexed_db::IndexedDB { version, inner, .. } = db;
65		let in_memory = in_memory::create(columns);
66		// read the columns from the IndexedDB
67		for column in 0..columns {
68			let mut txn = DBTransaction::new();
69			let mut stream = indexed_db::idb_cursor(&*inner, column);
70			while let Some((key, value)) = stream.next().await {
71				txn.put_vec(column, key.as_ref(), value);
72			}
73			// write each column into memory
74			in_memory.write(txn).expect("writing in memory always succeeds; qed");
75		}
76		Ok(Database { name: name_clone, version, columns, in_memory, indexed_db: inner })
77	}
78
79	/// Get the database name.
80	pub fn name(&self) -> &str {
81		self.name.as_str()
82	}
83
84	/// Get the database version.
85	pub fn version(&self) -> u32 {
86		self.version
87	}
88}
89
90impl Drop for Database {
91	fn drop(&mut self) {
92		self.indexed_db.close();
93	}
94}
95
96impl KeyValueDB for Database {
97	fn get(&self, col: u32, key: &[u8]) -> io::Result<Option<DBValue>> {
98		self.in_memory.get(col, key)
99	}
100
101	fn get_by_prefix(&self, col: u32, prefix: &[u8]) -> Option<Box<[u8]>> {
102		self.in_memory.get_by_prefix(col, prefix)
103	}
104
105	fn write(&self, transaction: DBTransaction) -> io::Result<()> {
106		let _ = indexed_db::idb_commit_transaction(&*self.indexed_db, &transaction, self.columns);
107		self.in_memory.write(transaction)
108	}
109
110	// NOTE: clones the whole db
111	fn iter<'a>(&'a self, col: u32) -> Box<dyn Iterator<Item = (Box<[u8]>, Box<[u8]>)> + 'a> {
112		self.in_memory.iter(col)
113	}
114
115	// NOTE: clones the whole db
116	fn iter_with_prefix<'a>(
117		&'a self,
118		col: u32,
119		prefix: &'a [u8],
120	) -> Box<dyn Iterator<Item = (Box<[u8]>, Box<[u8]>)> + 'a> {
121		self.in_memory.iter_with_prefix(col, prefix)
122	}
123
124	// NOTE: not supported
125	fn restore(&self, _new_db: &str) -> std::io::Result<()> {
126		Err(io::Error::new(io::ErrorKind::Other, "Not supported yet"))
127	}
128}