1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
use std::io::{self};
use crate::utils::read;
#[cfg(feature = "backup")]
/// Streams a backup of the database as uncompressed data.
///
/// This function retrieves all pages of the database and accumulates them into a
/// `Vec<u8>`. It returns the accumulated data wrapped in an `io::Cursor` for streaming.
///
/// Be aware that this function will try to load the entire database into memory
/// all at once, which may not be feasible for very large databases and could lead
/// to high memory usage or out-of-memory errors. It is particularly useful for
/// cases where you need to stream the database backup without compressing it.
///
/// # Returns
///
/// Returns an `io::Result<impl Read>` where the `impl Read` is an `io::Cursor` over a
/// `Vec<u8>` containing the uncompressed database data. This allows for streaming the
/// data as needed.
///
/// # Errors
///
/// This function returns an `io::Error` if:
/// - The database connection is not properly initialized or locked.
/// - The transaction cannot be started or committed.
/// - Page data cannot be read from the virtual file system or written to the buffer.
///
/// # Panics
///
/// This function may panic if:
/// - The transaction cannot be started or committed.
/// - Page data cannot be read or written to the buffer.
///
/// # Example
///
/// ```
/// use std::io::Read;
/// use ic_sqlite_features::stream_db_backup;
///
/// let mut backup_stream = stream_db_backup().expect("Failed to create backup stream");
/// let mut buffer = Vec::new();
/// backup_stream.read_to_end(&mut buffer).expect("Failed to read backup data");
/// // Use `buffer` as needed
/// ```
pub fn stream_db_backup() -> Result<impl Read, io::Error> {
let mut conn = CONN.lock().unwrap();
// Begin a transaction to ensure consistency
let tx = conn.transaction().unwrap();
// Create a buffer to store uncompressed data
let mut buffer = Vec::new();
let page_count: i64 = tx
.query_row("PRAGMA page_count;", [], |row| row.get(0))
.unwrap();
for page_number in 1..=page_count {
let page_data = read_page_from_vfs(page_number, 4096)?;
buffer.write_all(&page_data)?;
}
// Commit the transaction
tx.commit().unwrap();
// Return the uncompressed data as a cursor for streaming
Ok(io::Cursor::new(buffer))
}
#[cfg(feature = "backup")]
/// Performs a backup of the database by loading it entirely into memory.
///
/// This function retrieves all pages of the database and accumulates them into a
/// `Vec<u8>`. Be aware that this will try to bring the entire database into memory
/// all at once, which may not be feasible for very large databases and could lead
/// to high memory usage or out-of-memory errors.
///
/// # Returns
///
/// Returns a `Vec<u8>` containing the entire database's raw data. The data is
/// concatenated from each page of the database.
///
/// # Panics
///
/// This function may panic if:
/// - The database connection is not properly initialized or locked.
/// - The transaction cannot be started or committed.
/// - Page data cannot be read from the virtual file system.
///
/// # Example
///
/// ```
/// use ic_sqlite_features::backup::db_backup_on_memory;
/// let backup_data = db_backup_on_memory();
/// // Use `backup_data` as needed.
/// ```
pub fn db_backup_on_memory() -> Vec<u8> {
use crate::CONN;
let mut conn = CONN.lock().unwrap();
let mut output = Vec::new();
let tx = conn.transaction().unwrap();
let page_count: i64 = tx
.query_row("PRAGMA page_count;", [], |row| row.get(0))
.unwrap();
for page_number in 1..=page_count {
let page_data = read_page_from_vfs(page_number, 4096).unwrap();
output.extend_from_slice(&page_data);
}
tx.commit().unwrap();
output
}
pub fn read_page_from_vfs(page_number: i64, page_size: usize) -> Result<Vec<u8>, io::Error> {
let offset = (page_number - 1) * page_size as i64;
let mut buffer = vec![0u8; page_size];
let _ = read(&mut buffer, offset as u64)?;
Result::Ok(buffer)
}