Separate migrations from util (#6690)

* separate migration from util and make its dependencies into libs:

* snappy
* kvdb
* error
* common

* renamed common -> macros

* util error does not depend on snappy module

* ethsync does not depend on util nor ethcore_error

* nibbleslice and nibblevec merged with patricia_trie crate

* removed unused dependencies from util

* util journaldb traits does not need to be public

* util_error

* fixed ethcore compile error

* ignore .swo files

* Update chain.rs
This commit is contained in:
Marek Kotewicz
2017-10-10 20:01:27 +02:00
committed by GitHub
parent 4e8853c9f7
commit 6279ff32f5
86 changed files with 322 additions and 170 deletions

View File

@@ -1,104 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Utils common types and macros global reexport.
use std::io;
#[macro_export]
macro_rules! vec_into {
( $( $x:expr ),* ) => {
vec![ $( $x.into() ),* ]
}
}
#[macro_export]
macro_rules! slice_into {
( $( $x:expr ),* ) => {
&[ $( $x.into() ),* ]
}
}
#[macro_export]
macro_rules! hash_map {
() => { HashMap::new() };
( $( $x:expr => $y:expr ),* ) => {{
let mut x = HashMap::new();
$(
x.insert($x, $y);
)*
x
}}
}
#[macro_export]
macro_rules! hash_map_into {
() => { HashMap::new() };
( $( $x:expr => $y:expr ),* ) => {{
let mut x = HashMap::new();
$(
x.insert($x.into(), $y.into());
)*
x
}}
}
#[macro_export]
macro_rules! map {
() => { BTreeMap::new() };
( $( $x:expr => $y:expr ),* ) => {{
let mut x = BTreeMap::new();
$(
x.insert($x, $y);
)*
x
}}
}
#[macro_export]
macro_rules! map_into {
() => { BTreeMap::new() };
( $( $x:expr => $y:expr ),* ) => {{
let mut x = BTreeMap::new();
$(
x.insert($x.into(), $y.into());
)*
x
}}
}
#[macro_export]
macro_rules! flush {
($arg:expr) => ($crate::common::flush($arg.into()));
($($arg:tt)*) => ($crate::common::flush(format!("{}", format_args!($($arg)*))));
}
#[macro_export]
macro_rules! flushln {
($fmt:expr) => (flush!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (flush!(concat!($fmt, "\n"), $($arg)*));
}
#[doc(hidden)]
pub fn flush(s: String) {
let _ = io::Write::write(&mut io::stdout(), s.as_bytes());
let _ = io::Write::flush(&mut io::stdout());
}
#[test]
fn test_flush() {
flushln!("hello_world {:?}", 1);
}

View File

@@ -1,66 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! General error types for use in ethcore.
#![allow(missing_docs)]
#![allow(unknown_lints)]
use std::{self, fmt};
use rustc_hex::FromHexError;
use rlp::DecoderError;
use bigint::hash::H256;
#[derive(Debug)]
/// Error in database subsystem.
pub enum BaseDataError {
/// An entry was removed more times than inserted.
NegativelyReferencedHash(H256),
/// A committed value was inserted more than once.
AlreadyExists(H256),
}
impl fmt::Display for BaseDataError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
BaseDataError::NegativelyReferencedHash(hash) =>
write!(f, "Entry {} removed from database more times than it was added.", hash),
BaseDataError::AlreadyExists(hash) =>
write!(f, "Committed key already exists in database: {}", hash),
}
}
}
impl std::error::Error for BaseDataError {
fn description(&self) -> &str {
"Error in database subsystem"
}
}
error_chain! {
types {
UtilError, ErrorKind, ResultExt, Result;
}
foreign_links {
Io(::std::io::Error);
FromHex(FromHexError);
Decoder(DecoderError);
Snappy(::snappy::InvalidInput);
BaseData(BaseDataError);
}
}

View File

@@ -20,7 +20,7 @@ use std::{fmt, str};
use std::sync::Arc;
/// Export the journaldb module.
pub mod traits;
mod traits;
mod archivedb;
mod earlymergedb;
mod overlayrecentdb;

View File

@@ -27,7 +27,7 @@ use super::{DB_PREFIX_LEN, LATEST_ERA_KEY};
use super::traits::JournalDB;
use kvdb::{KeyValueDB, DBTransaction};
use bigint::hash::H256;
use UtilError;
use error::UtilError;
use bytes::Bytes;
/// Implementation of the `HashDB` trait for a disk-backed database with a memory overlay

View File

@@ -20,7 +20,7 @@ use std::sync::Arc;
use hashdb::*;
use kvdb::{self, DBTransaction};
use bigint::hash::H256;
use UtilError;
use error::UtilError;
use bytes::Bytes;
/// A `HashDB` which can manage a short-term journal potentially containing many forks of mutually

File diff suppressed because it is too large Load Diff

View File

@@ -100,7 +100,6 @@ extern crate ethcore_bytes as bytes;
extern crate parking_lot;
extern crate tiny_keccak;
extern crate rlp;
extern crate regex;
extern crate lru_cache;
extern crate heapsize;
extern crate ethcore_logger;
@@ -108,22 +107,15 @@ extern crate hash as keccak;
extern crate hashdb;
extern crate memorydb;
extern crate patricia_trie as trie;
#[macro_use]
extern crate error_chain;
extern crate kvdb;
extern crate util_error as error;
#[macro_use]
extern crate log as rlog;
#[macro_use]
pub mod common;
pub mod error;
pub mod misc;
pub mod migration;
pub mod overlaydb;
pub mod journaldb;
pub mod kvdb;
pub mod snappy;
pub mod cache;
pub use misc::*;
@@ -131,8 +123,6 @@ pub use hashdb::*;
pub use memorydb::MemoryDB;
pub use overlaydb::*;
pub use journaldb::JournalDB;
pub use kvdb::*;
pub use error::UtilError;
/// 160-bit integer representing account address
pub type Address = bigint::hash::H160;

View File

@@ -1,367 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! DB Migration module.
#[cfg(test)]
mod tests;
use std::collections::BTreeMap;
use std::fs;
use std::fmt;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use ::kvdb::{CompactionProfile, Database, DatabaseConfig, DBTransaction};
/// Migration config.
#[derive(Clone)]
pub struct Config {
/// Defines how many elements should be migrated at once.
pub batch_size: usize,
/// Database compaction profile.
pub compaction_profile: CompactionProfile,
}
impl Default for Config {
fn default() -> Self {
Config {
batch_size: 1024,
compaction_profile: Default::default(),
}
}
}
/// A batch of key-value pairs to be written into the database.
pub struct Batch {
inner: BTreeMap<Vec<u8>, Vec<u8>>,
batch_size: usize,
column: Option<u32>,
}
impl Batch {
/// Make a new batch with the given config.
pub fn new(config: &Config, col: Option<u32>) -> Self {
Batch {
inner: BTreeMap::new(),
batch_size: config.batch_size,
column: col,
}
}
/// Insert a value into the batch, committing if necessary.
pub fn insert(&mut self, key: Vec<u8>, value: Vec<u8>, dest: &mut Database) -> Result<(), Error> {
self.inner.insert(key, value);
if self.inner.len() == self.batch_size {
self.commit(dest)?;
}
Ok(())
}
/// Commit all the items in the batch to the given database.
pub fn commit(&mut self, dest: &mut Database) -> Result<(), Error> {
if self.inner.is_empty() { return Ok(()) }
let mut transaction = DBTransaction::new();
for keypair in &self.inner {
transaction.put(self.column, &keypair.0, &keypair.1);
}
self.inner.clear();
dest.write(transaction).map_err(Error::Custom)
}
}
/// Migration error.
#[derive(Debug)]
pub enum Error {
/// Error returned when it is impossible to add new migration rules.
CannotAddMigration,
/// Error returned when migration from specific version can not be performed.
MigrationImpossible,
/// Io Error.
Io(::std::io::Error),
/// Custom error.
Custom(String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match *self {
Error::CannotAddMigration => write!(f, "Cannot add migration"),
Error::MigrationImpossible => write!(f, "Migration impossible"),
Error::Io(ref err) => write!(f, "{}", err),
Error::Custom(ref err) => write!(f, "{}", err),
}
}
}
impl From<::std::io::Error> for Error {
fn from(e: ::std::io::Error) -> Self {
Error::Io(e)
}
}
impl From<String> for Error {
fn from(e: String) -> Self {
Error::Custom(e)
}
}
/// A generalized migration from the given db to a destination db.
pub trait Migration: 'static {
/// Number of columns in the database before the migration.
fn pre_columns(&self) -> Option<u32> { self.columns() }
/// Number of columns in database after the migration.
fn columns(&self) -> Option<u32>;
/// Whether this migration alters any existing columns.
/// if not, then column families will simply be added and `migrate` will never be called.
fn alters_existing(&self) -> bool { true }
/// Version of the database after the migration.
fn version(&self) -> u32;
/// Migrate a source to a destination.
fn migrate(&mut self, source: Arc<Database>, config: &Config, destination: &mut Database, col: Option<u32>) -> Result<(), Error>;
}
/// A simple migration over key-value pairs.
pub trait SimpleMigration: 'static {
/// Number of columns in database after the migration.
fn columns(&self) -> Option<u32>;
/// Version of database after the migration.
fn version(&self) -> u32;
/// Should migrate existing object to new database.
/// Returns `None` if the object does not exist in new version of database.
fn simple_migrate(&mut self, key: Vec<u8>, value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)>;
}
impl<T: SimpleMigration> Migration for T {
fn columns(&self) -> Option<u32> { SimpleMigration::columns(self) }
fn version(&self) -> u32 { SimpleMigration::version(self) }
fn alters_existing(&self) -> bool { true }
fn migrate(&mut self, source: Arc<Database>, config: &Config, dest: &mut Database, col: Option<u32>) -> Result<(), Error> {
let mut batch = Batch::new(config, col);
let iter = match source.iter(col) {
Some(iter) => iter,
None => return Ok(()),
};
for (key, value) in iter {
if let Some((key, value)) = self.simple_migrate(key.into_vec(), value.into_vec()) {
batch.insert(key, value, dest)?;
}
}
batch.commit(dest)
}
}
/// An even simpler migration which just changes the number of columns.
pub struct ChangeColumns {
/// The amount of columns before this migration.
pub pre_columns: Option<u32>,
/// The amount of columns after this migration.
pub post_columns: Option<u32>,
/// The version after this migration.
pub version: u32,
}
impl Migration for ChangeColumns {
fn pre_columns(&self) -> Option<u32> { self.pre_columns }
fn columns(&self) -> Option<u32> { self.post_columns }
fn version(&self) -> u32 { self.version }
fn alters_existing(&self) -> bool { false }
fn migrate(&mut self, _: Arc<Database>, _: &Config, _: &mut Database, _: Option<u32>) -> Result<(), Error> {
Ok(())
}
}
/// Get the path where all databases reside.
fn database_path(path: &Path) -> PathBuf {
let mut temp_path = path.to_owned();
temp_path.pop();
temp_path
}
enum TempIndex {
One,
Two,
}
impl TempIndex {
fn swap(&mut self) {
match *self {
TempIndex::One => *self = TempIndex::Two,
TempIndex::Two => *self = TempIndex::One,
}
}
// given the path to the old database, get the path of this one.
fn path(&self, db_root: &Path) -> PathBuf {
let mut buf = db_root.to_owned();
match *self {
TempIndex::One => buf.push("temp_migration_1"),
TempIndex::Two => buf.push("temp_migration_2"),
};
buf
}
}
/// Manages database migration.
pub struct Manager {
config: Config,
migrations: Vec<Box<Migration>>,
}
impl Manager {
/// Creates new migration manager with given configuration.
pub fn new(config: Config) -> Self {
Manager {
config: config,
migrations: vec![],
}
}
/// Adds new migration rules.
pub fn add_migration<T>(&mut self, migration: T) -> Result<(), Error> where T: Migration {
let is_new = match self.migrations.last() {
Some(last) => migration.version() > last.version(),
None => true,
};
match is_new {
true => Ok(self.migrations.push(Box::new(migration))),
false => Err(Error::CannotAddMigration),
}
}
/// Performs migration in order, starting with a source path, migrating between two temporary databases,
/// and producing a path where the final migration lives.
pub fn execute(&mut self, old_path: &Path, version: u32) -> Result<PathBuf, Error> {
let config = self.config.clone();
let migrations = self.migrations_from(version);
trace!(target: "migration", "Total migrations to execute for version {}: {}", version, migrations.len());
if migrations.is_empty() { return Err(Error::MigrationImpossible) };
let columns = migrations.get(0).and_then(|m| m.pre_columns());
trace!(target: "migration", "Expecting database to contain {:?} columns", columns);
let mut db_config = DatabaseConfig {
max_open_files: 64,
cache_sizes: Default::default(),
compaction: config.compaction_profile,
columns: columns,
wal: true,
};
let db_root = database_path(old_path);
let mut temp_idx = TempIndex::One;
let mut temp_path = old_path.to_path_buf();
// start with the old db.
let old_path_str = old_path.to_str().ok_or(Error::MigrationImpossible)?;
let mut cur_db = Arc::new(Database::open(&db_config, old_path_str).map_err(Error::Custom)?);
for migration in migrations {
trace!(target: "migration", "starting migration to version {}", migration.version());
// Change number of columns in new db
let current_columns = db_config.columns;
db_config.columns = migration.columns();
// slow migrations: alter existing data.
if migration.alters_existing() {
temp_path = temp_idx.path(&db_root);
// open the target temporary database.
let temp_path_str = temp_path.to_str().ok_or(Error::MigrationImpossible)?;
let mut new_db = Database::open(&db_config, temp_path_str).map_err(Error::Custom)?;
match current_columns {
// migrate only default column
None => migration.migrate(cur_db.clone(), &config, &mut new_db, None)?,
Some(v) => {
// Migrate all columns in previous DB
for col in 0..v {
migration.migrate(cur_db.clone(), &config, &mut new_db, Some(col))?
}
}
}
// next iteration, we will migrate from this db into the other temp.
cur_db = Arc::new(new_db);
temp_idx.swap();
// remove the other temporary migration database.
let _ = fs::remove_dir_all(temp_idx.path(&db_root));
} else {
// migrations which simply add or remove column families.
// we can do this in-place.
let goal_columns = migration.columns().unwrap_or(0);
while cur_db.num_columns() < goal_columns {
cur_db.add_column().map_err(Error::Custom)?;
}
while cur_db.num_columns() > goal_columns {
cur_db.drop_column().map_err(Error::Custom)?;
}
}
}
Ok(temp_path)
}
/// Returns true if migration is needed.
pub fn is_needed(&self, version: u32) -> bool {
match self.migrations.last() {
Some(last) => version < last.version(),
None => false,
}
}
/// Find all needed migrations.
fn migrations_from(&mut self, version: u32) -> Vec<&mut Box<Migration>> {
self.migrations.iter_mut().filter(|m| m.version() > version).collect()
}
}
/// Prints a dot every `max` ticks
pub struct Progress {
current: usize,
max: usize,
}
impl Default for Progress {
fn default() -> Self {
Progress {
current: 0,
max: 100_000,
}
}
}
impl Progress {
/// Tick progress meter.
pub fn tick(&mut self) {
self.current += 1;
if self.current == self.max {
self.current = 0;
flush!(".");
}
}
}

View File

@@ -1,251 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Tests for migrations.
//! A random temp directory is created. A database is created within it, and migrations
//! are performed in temp sub-directories.
use std::collections::BTreeMap;
use std::sync::Arc;
use std::path::{Path, PathBuf};
use migration::{Batch, Config, Error, SimpleMigration, Migration, Manager};
use kvdb::Database;
use devtools::RandomTempPath;
fn db_path(path: &Path) -> PathBuf {
let mut p = path.to_owned();
p.push("db");
p
}
// initialize a database at the given directory with the given values.
fn make_db(path: &Path, pairs: BTreeMap<Vec<u8>, Vec<u8>>) {
let db = Database::open_default(path.to_str().unwrap()).expect("failed to open temp database");
{
let mut transaction = db.transaction();
for (k, v) in pairs {
transaction.put(None, &k, &v);
}
db.write(transaction).expect("failed to write db transaction");
}
}
// helper for verifying a migrated database.
fn verify_migration(path: &Path, pairs: BTreeMap<Vec<u8>, Vec<u8>>) {
let db = Database::open_default(path.to_str().unwrap()).unwrap();
for (k, v) in pairs {
let x = db.get(None, &k).unwrap().unwrap();
assert_eq!(&x[..], &v[..]);
}
}
struct Migration0;
impl SimpleMigration for Migration0 {
fn columns(&self) -> Option<u32> { None }
fn version(&self) -> u32 { 1 }
fn simple_migrate(&mut self, mut key: Vec<u8>, mut value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
key.push(0x11);
value.push(0x22);
Some((key, value))
}
}
struct Migration1;
impl SimpleMigration for Migration1 {
fn columns(&self) -> Option<u32> { None }
fn version(&self) -> u32 { 2 }
fn simple_migrate(&mut self, key: Vec<u8>, _value: Vec<u8>) -> Option<(Vec<u8>, Vec<u8>)> {
Some((key, vec![]))
}
}
struct AddsColumn;
impl Migration for AddsColumn {
fn pre_columns(&self) -> Option<u32> { None }
fn columns(&self) -> Option<u32> { Some(1) }
fn version(&self) -> u32 { 1 }
fn migrate(&mut self, source: Arc<Database>, config: &Config, dest: &mut Database, col: Option<u32>) -> Result<(), Error> {
let mut batch = Batch::new(config, col);
for (key, value) in source.iter(col).into_iter().flat_map(|inner| inner) {
batch.insert(key.into_vec(), value.into_vec(), dest)?;
}
if col == Some(1) {
batch.insert(vec![1, 2, 3], vec![4, 5, 6], dest)?;
}
batch.commit(dest)
}
}
#[test]
fn one_simple_migration() {
let dir = RandomTempPath::create_dir();
let db_path = db_path(dir.as_path());
let mut manager = Manager::new(Config::default());
make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]);
let expected = map![vec![0x11] => vec![0x22], vec![1, 0x11] => vec![1, 0x22]];
manager.add_migration(Migration0).unwrap();
let end_path = manager.execute(&db_path, 0).unwrap();
verify_migration(&end_path, expected);
}
#[test]
#[should_panic]
fn no_migration_needed() {
let dir = RandomTempPath::create_dir();
let db_path = db_path(dir.as_path());
let mut manager = Manager::new(Config::default());
make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]);
manager.add_migration(Migration0).unwrap();
manager.execute(&db_path, 1).unwrap();
}
#[test]
#[should_panic]
fn wrong_adding_order() {
let dir = RandomTempPath::create_dir();
let db_path = db_path(dir.as_path());
let mut manager = Manager::new(Config::default());
make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]);
manager.add_migration(Migration1).unwrap();
manager.add_migration(Migration0).unwrap();
}
#[test]
fn multiple_migrations() {
let dir = RandomTempPath::create_dir();
let db_path = db_path(dir.as_path());
let mut manager = Manager::new(Config::default());
make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]);
let expected = map![vec![0x11] => vec![], vec![1, 0x11] => vec![]];
manager.add_migration(Migration0).unwrap();
manager.add_migration(Migration1).unwrap();
let end_path = manager.execute(&db_path, 0).unwrap();
verify_migration(&end_path, expected);
}
#[test]
fn second_migration() {
let dir = RandomTempPath::create_dir();
let db_path = db_path(dir.as_path());
let mut manager = Manager::new(Config::default());
make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]);
let expected = map![vec![] => vec![], vec![1] => vec![]];
manager.add_migration(Migration0).unwrap();
manager.add_migration(Migration1).unwrap();
let end_path = manager.execute(&db_path, 1).unwrap();
verify_migration(&end_path, expected);
}
#[test]
fn first_and_noop_migration() {
let dir = RandomTempPath::create_dir();
let db_path = db_path(dir.as_path());
let mut manager = Manager::new(Config::default());
make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]);
let expected = map![vec![0x11] => vec![0x22], vec![1, 0x11] => vec![1, 0x22]];
manager.add_migration(Migration0).unwrap();
let end_path = manager.execute(&db_path, 0).unwrap();
verify_migration(&end_path, expected);
}
#[test]
fn noop_and_second_migration() {
let dir = RandomTempPath::create_dir();
let db_path = db_path(dir.as_path());
let mut manager = Manager::new(Config::default());
make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]);
let expected = map![vec![] => vec![], vec![1] => vec![]];
manager.add_migration(Migration1).unwrap();
let end_path = manager.execute(&db_path, 0).unwrap();
verify_migration(&end_path, expected);
}
#[test]
fn is_migration_needed() {
let mut manager = Manager::new(Config::default());
manager.add_migration(Migration0).unwrap();
manager.add_migration(Migration1).unwrap();
assert!(manager.is_needed(0));
assert!(manager.is_needed(1));
assert!(!manager.is_needed(2));
}
#[test]
fn pre_columns() {
let mut manager = Manager::new(Config::default());
manager.add_migration(AddsColumn).unwrap();
let dir = RandomTempPath::create_dir();
let db_path = db_path(dir.as_path());
// this shouldn't fail to open the database even though it's one column
// short of the one before it.
manager.execute(&db_path, 0).unwrap();
}
#[test]
fn change_columns() {
use kvdb::DatabaseConfig;
let mut manager = Manager::new(Config::default());
manager.add_migration(::migration::ChangeColumns {
pre_columns: None,
post_columns: Some(4),
version: 1,
}).unwrap();
let dir = RandomTempPath::create_dir();
let db_path = db_path(dir.as_path());
let new_path = manager.execute(&db_path, 0).unwrap();
assert_eq!(db_path, new_path, "Changing columns is an in-place migration.");
let config = DatabaseConfig::with_columns(Some(4));
let db = Database::open(&config, new_path.to_str().unwrap()).unwrap();
assert_eq!(db.num_columns(), 4);
}

View File

@@ -1,162 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
//! Snappy compression bindings.
use std::{self, fmt};
use libc::{c_char, c_int, size_t};
const SNAPPY_OK: c_int = 0;
const SNAPPY_INVALID_INPUT: c_int = 1;
const SNAPPY_BUFFER_TOO_SMALL: c_int = 2;
extern {
fn snappy_compress(
input: *const c_char,
input_len: size_t,
compressed: *mut c_char,
compressed_len: *mut size_t
) -> c_int;
fn snappy_max_compressed_length(source_len: size_t) -> size_t;
fn snappy_uncompress(
compressed: *const c_char,
compressed_len: size_t,
uncompressed: *mut c_char,
uncompressed_len: *mut size_t,
) -> c_int;
fn snappy_uncompressed_length(
compressed: *const c_char,
compressed_len: size_t,
result: *mut size_t,
) -> c_int;
fn snappy_validate_compressed_buffer(
compressed: *const c_char,
compressed_len: size_t,
) -> c_int;
}
/// Attempted to decompress an uncompressed buffer.
#[derive(Debug)]
pub struct InvalidInput;
impl std::error::Error for InvalidInput {
fn description(&self) -> &str {
"Attempted snappy decompression with invalid input"
}
}
impl fmt::Display for InvalidInput {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Attempted snappy decompression with invalid input")
}
}
/// The maximum compressed length given a size.
pub fn max_compressed_len(len: usize) -> usize {
unsafe { snappy_max_compressed_length(len as size_t) as usize }
}
/// How large the given data will be when decompressed.
pub fn decompressed_len(compressed: &[u8]) -> Result<usize, InvalidInput> {
let mut size: size_t = 0;
let len = compressed.len() as size_t;
let status = unsafe { snappy_uncompressed_length(compressed.as_ptr() as *const c_char, len, &mut size) };
if status == SNAPPY_INVALID_INPUT {
Err(InvalidInput)
} else {
Ok(size)
}
}
/// Compress a buffer using snappy.
pub fn compress(input: &[u8]) -> Vec<u8> {
let mut buf = Vec::new();
compress_into(input, &mut buf);
buf
}
/// Compress a buffer using snappy, writing the result into
/// the given output buffer, growing it if necessary.
/// Otherwise, returns the length of the compressed data.
pub fn compress_into(input: &[u8], output: &mut Vec<u8>) -> usize {
let mut len = max_compressed_len(input.len());
if output.len() < len {
output.resize(len, 0);
}
let status = unsafe {
snappy_compress(
input.as_ptr() as *const c_char,
input.len() as size_t,
output.as_mut_ptr() as *mut c_char,
&mut len as &mut size_t,
)
};
match status {
SNAPPY_OK => len,
SNAPPY_INVALID_INPUT => panic!("snappy compression has no concept of invalid input"),
SNAPPY_BUFFER_TOO_SMALL => panic!("buffer cannot be too small, the capacity was just ensured."),
_ => panic!("snappy returned unspecified status"),
}
}
/// Decompress a buffer using snappy. Will return an error if the buffer is not snappy-compressed.
pub fn decompress(input: &[u8]) -> Result<Vec<u8>, InvalidInput> {
let mut v = Vec::new();
decompress_into(input, &mut v).map(|_| v)
}
/// Decompress a buffer using snappy, writing the result into
/// the given output buffer, growing it if necessary.
/// Will error if the input buffer is not snappy-compressed.
/// Otherwise, returns the length of the decompressed data.
pub fn decompress_into(input: &[u8], output: &mut Vec<u8>) -> Result<usize, InvalidInput> {
let mut len = decompressed_len(input)?;
if output.len() < len {
output.resize(len, 0);
}
let status = unsafe {
snappy_uncompress(
input.as_ptr() as *const c_char,
input.len() as size_t,
output.as_mut_ptr() as *mut c_char,
&mut len as &mut size_t,
)
};
match status {
SNAPPY_OK => Ok(len as usize),
SNAPPY_INVALID_INPUT => Err(InvalidInput),
SNAPPY_BUFFER_TOO_SMALL => panic!("buffer cannot be too small, size was just set to large enough."),
_ => panic!("snappy returned unspecified status"),
}
}
/// Validate a compressed buffer. True if valid, false if not.
pub fn validate_compressed_buffer(input: &[u8]) -> bool {
let status = unsafe { snappy_validate_compressed_buffer(input.as_ptr() as *const c_char, input.len() as size_t )};
status == SNAPPY_OK
}