Lightweight 0.20260921.0
Loading...
Searching...
No Matches
SqlBackup.hpp
1// SPDX-License-Identifier: Apache-2.0
2#pragma once
3
4#include "../Api.hpp"
5#include "../SqlConnectInfo.hpp"
6#include "../SqlQuery/MigrationPlan.hpp"
7#include "../SqlRetryPolicy.hpp"
8#include "../SqlSchema.hpp"
9
10#include <chrono>
11#include <cstdint>
12#include <filesystem>
13#include <map>
14#include <string>
15#include <string_view>
16#include <vector>
17
18/// @defgroup Backup Backup and Restore
19/// @brief Parallel chunked database dump and restore, with archive diffing.
20///
21/// Backups are taken online without a snapshot, so an archive carries no cross-table
22/// consistency guarantee.
23
24namespace Lightweight::SqlBackup
25{
26
27/// Compression methods supported for ZIP entries.
28///
29/// The values correspond to the ZIP compression method IDs used by libzip.
30/// Not all methods may be available at runtime depending on how libzip was compiled.
31/// Use IsCompressionMethodSupported() to check availability.
32// NOLINTNEXTLINE(performance-enum-size) - Values must match libzip ZIP_CM_* constants
33enum class CompressionMethod : std::int32_t
34{
35 Store = 0, ///< No compression (ZIP_CM_STORE)
36 Deflate = 8, ///< Deflate compression (ZIP_CM_DEFLATE) - most compatible
37 Bzip2 = 12, ///< Bzip2 compression (ZIP_CM_BZIP2)
38 Lzma = 14, ///< LZMA compression (ZIP_CM_LZMA)
39 Zstd = 93, ///< Zstandard compression (ZIP_CM_ZSTD)
40 Xz = 95, ///< XZ compression (ZIP_CM_XZ)
41};
42
43/// @ingroup Backup
44/// Configuration for backup operations including compression and chunking.
46{
47 /// The compression method to use.
48 CompressionMethod method = CompressionMethod::Deflate;
49
50 /// The compression level (0-9).
51 /// - For Deflate: 1 = fastest, 9 = best compression, 6 = default
52 /// - For Bzip2: 1-9 (block size in 100k units)
53 /// - For Zstd: maps to zstd levels
54 /// - For Store: ignored
55 std::uint32_t level = 6;
56
57 /// The target size in bytes for each chunk before flushing.
58 /// Chunks are flushed when the buffer exceeds this size.
59 /// Default: 10 MB.
60 std::size_t chunkSizeBytes = 10 * 1024 * 1024;
61
62 /// Target rows per chunk window. Tables with a single numeric primary key are split into
63 /// windows of about this many keys (subject to a per-table window cap) that are backed up
64 /// in parallel by multiple workers.
65 std::size_t rowsPerChunk = 100'000;
66
67 /// Uncompressed bytes each worker accumulates in its private temp archive before sealing it
68 /// (the compression of that archive runs in the worker thread at that point, overlapped with
69 /// the network-bound fetch). Bounds worker memory at about jobs x workerArchiveBytes and
70 /// determines how many temp archives the finalize merge opens. Default: 256 MB.
71 std::size_t workerArchiveBytes = 256ULL * 1024 * 1024;
72
73 /// If true, only export schema metadata without backing up table data.
74 bool schemaOnly = false;
75
76 /// Deprecated no-op. Previously bypassed the MS SQL Server single-worker clamp; that clamp has
77 /// been removed (all databases now back up multi-threaded), so this flag no longer has any
78 /// effect. Retained temporarily to avoid an API/ABI break; pending removal.
80};
81
82/// @ingroup Backup
83/// Configuration for restore operations including memory management.
85{
86 /// Batch size for insert operations (rows per batch).
87 /// Default: 0 (auto-calculated based on available memory).
88 std::size_t batchSize = 0;
89
90 /// Maximum rows before an intermediate commit within a chunk.
91 /// Helps reduce transaction log / WAL memory accumulation.
92 /// Default: 10000. Set to 0 to disable intermediate commits.
93 std::size_t maxRowsPerCommit = 10000;
94
95 /// Database page cache size in KB (used for SQLite PRAGMA cache_size,
96 /// could be extended for other DBMS memory hints).
97 /// Default: 65536 (64MB). Set to 0 to use database default.
98 std::size_t cacheSizeKB = 65536;
99
100 /// Memory limit in bytes (0 = auto-detect from system).
101 std::size_t memoryLimitBytes = 0;
102
103 /// If true, only recreate schema without importing data.
104 bool schemaOnly = false;
105};
106
107/// Returns available system memory in bytes.
108LIGHTWEIGHT_API std::size_t GetAvailableSystemMemory() noexcept;
109
110/// Calculates optimal restore settings based on available memory.
111///
112/// @param availableMemory Available system memory in bytes.
113/// @param concurrency Number of concurrent restore workers.
114/// @return RestoreSettings optimized for the given memory constraints.
115LIGHTWEIGHT_API RestoreSettings CalculateRestoreSettings(std::size_t availableMemory, unsigned concurrency);
116
117/// Checks if a compression method is supported by the current libzip installation.
118///
119/// @param method The compression method to check.
120/// @return true if the method is available for both compression and decompression.
121LIGHTWEIGHT_API bool IsCompressionMethodSupported(CompressionMethod method) noexcept;
122
123/// Returns a list of all compression methods that are supported by the current libzip installation.
124LIGHTWEIGHT_API std::vector<CompressionMethod> GetSupportedCompressionMethods() noexcept;
125
126/// Returns the human-readable name of a compression method.
127LIGHTWEIGHT_API std::string_view CompressionMethodName(CompressionMethod method) noexcept;
128
129/// Configuration for retry behavior on transient errors during backup/restore operations.
130///
131/// An alias for the library-wide @ref SqlRetrySettings: the backup engine was where this policy
132/// was first proven out, and it now shares one implementation with the rest of the library rather
133/// than keeping a parallel copy. The field names, types and defaults are unchanged, so existing
134/// designated-initializer call sites keep compiling verbatim.
135///
136/// @see SqlRetryPolicy
138
139/// Information about a table being backed up.
141{
142 /// The list of columns in the table in SQL format.
143 std::string fields;
144
145 /// The list of columns in the table.
146 std::vector<bool> isBinaryColumn;
147
148 /// The list of columns in the table.
149 std::vector<SqlColumnDeclaration> columns;
150
151 /// The list of foreign key constraints in the table.
152 std::vector<SqlSchema::ForeignKeyConstraint> foreignKeys;
153
154 /// The indexes on the table (excluding primary key index).
155 std::vector<SqlSchema::IndexDefinition> indexes;
156
157 /// The number of rows in the table.
158 size_t rowCount = 0;
159};
160
161/// @ingroup Backup
162/// Progress information for backup/restore operations status updates.
164{
165 /// The state of an individual backup/restore operation.
166 enum class State : std::uint8_t
167 {
168 Started,
169 InProgress,
170 Finished,
171 Error,
172 Warning
173 };
174
175 /// The state of an individual backup/restore operation.
177
178 /// The name of the table being backed up / restored.
179 std::string tableName;
180
181 /// The current number of rows processed.
182 size_t currentRows {};
183
184 /// The total number of rows to be processed, if known.
185 std::optional<size_t> totalRows;
186
187 /// A message associated with the progress update.
188 std::string message;
189};
190
191/// @ingroup Backup
192/// The interface for progress updates.
194{
195 virtual ~ProgressManager() = default;
196 /// Default constructor.
197 ProgressManager() = default;
198 /// Default copy constructor.
200 /// Default copy assignment operator.
202 /// Default move constructor.
204 /// Default move assignment operator.
206
207 /// Gets called when the progress of an individual backup/restore operation changes.
208 virtual void Update(Progress const& p) = 0;
209
210 /// Gets called when all backup/restore operations are finished.
211 virtual void AllDone() = 0;
212
213 /// Sets the maximum length of a table name.
214 /// This is used to align the output of the progress manager.
215 virtual void SetMaxTableNameLength(size_t /*len*/) {}
216
217 /// Returns the number of errors encountered during the operation.
218 [[nodiscard]] virtual size_t ErrorCount() const noexcept
219 {
220 return 0;
221 }
222
223 /// Sets the total number of items to be processed (for ETA calculation).
224 /// @param totalItems Total number of items (rows) to process across all tables.
225 virtual void SetTotalItems(size_t totalItems)
226 {
227 (void) totalItems;
228 }
229
230 /// Adds to the total number of items for progressive ETA calculation.
231 /// This is called as row counts become available during parallel counting.
232 /// @param additionalItems Number of additional items (rows) to add to the total.
233 virtual void AddTotalItems(size_t additionalItems)
234 {
235 (void) additionalItems;
236 }
237
238 /// Called when items are processed (for rate and ETA calculation).
239 /// @param count Number of items (rows) just processed.
240 virtual void OnItemsProcessed(size_t count)
241 {
242 (void) count;
243 }
244
245 /// Announces how many tables the operation will process, once, before any
246 /// per-table `Update()` is emitted.
247 ///
248 /// Backup knows the complete table set after its schema scan and Restore
249 /// after reading the archive manifest, i.e. both know the denominator before
250 /// any data moves. Without this hook a consumer can only count the distinct
251 /// table names it has seen so far, so a progress readout of the form
252 /// "processed / total" has a total that climbs as tables are discovered —
253 /// the GUI's per-table panel showed "12 / 12" then "12 / 47" then "12 / 700"
254 /// on the same run, which reads as the job getting bigger rather than as
255 /// progress being made.
256 ///
257 /// @param totalTables Number of tables that will be processed.
258 virtual void SetTotalTables(size_t totalTables)
259 {
260 (void) totalTables;
261 }
262};
263
264/// @ingroup Backup
265/// Base class for progress managers that tracks errors automatically.
267{
268 public:
269 void Update(Progress const& progress) override
270 {
271 if (progress.state == Progress::State::Error)
272 ++_errorCount;
273 }
274
275 [[nodiscard]] size_t ErrorCount() const noexcept override
276 {
277 return _errorCount;
278 }
279
280 private:
281 size_t _errorCount = 0;
282};
283
284struct NullProgressManager: ErrorTrackingProgressManager
285{
286 void Update(Progress const& progress) override
287 {
289 }
290 void AllDone() override {}
291};
292
293/// Backs up the database to a file.
294///
295/// @param outputFile the output file.
296/// @param connectionString the connection string used to connect to the database.
297/// @param concurrency the number of concurrent jobs.
298/// @param progress the progress manager to use for progress updates.
299/// @param schema the database schema to backup (optional).
300/// @param tableFilter comma-separated table filter patterns (default: "*" for all tables).
301/// Supports glob wildcards (* and ?) and schema.table notation.
302/// Examples: "Users,Products", "*_log", "dbo.Users", "sales.*"
303/// @param retrySettings configuration for retry behavior on transient errors.
304/// @param backupSettings configuration for compression method, level, and chunk size.
305LIGHTWEIGHT_API void Backup(std::filesystem::path const& outputFile,
306 SqlConnectionString const& connectionString,
307 unsigned concurrency,
308 ProgressManager& progress,
309 std::string const& schema = {},
310 std::string const& tableFilter = "*",
311 RetrySettings const& retrySettings = {},
312 BackupSettings const& backupSettings = {});
313
314/// Restores the database from a file.
315///
316/// @param inputFile the input file.
317/// @param connectionString the connection string used to connect to the database.
318/// @param concurrency the number of concurrent jobs.
319/// @param progress the progress manager to use for progress updates.
320/// @param schema the database schema to restore into (optional, overrides backup metadata).
321/// @param tableFilter comma-separated table filter patterns (default: "*" for all tables).
322/// Supports glob wildcards (* and ?) and schema.table notation.
323/// Examples: "Users,Products", "*_log", "dbo.Users", "sales.*"
324/// @param retrySettings configuration for retry behavior on transient errors.
325LIGHTWEIGHT_API void Restore(std::filesystem::path const& inputFile,
326 SqlConnectionString const& connectionString,
327 unsigned concurrency,
328 ProgressManager& progress,
329 std::string const& schema = {},
330 std::string const& tableFilter = "*",
331 RetrySettings const& retrySettings = {});
332
333/// Restores the database from a file with explicit memory management settings.
334///
335/// @param inputFile the input file.
336/// @param connectionString the connection string used to connect to the database.
337/// @param concurrency the number of concurrent jobs.
338/// @param progress the progress manager to use for progress updates.
339/// @param schema the database schema to restore into (optional, overrides backup metadata).
340/// @param tableFilter comma-separated table filter patterns (default: "*" for all tables).
341/// @param retrySettings configuration for retry behavior on transient errors.
342/// @param restoreSettings configuration for memory management during restore.
343LIGHTWEIGHT_API void Restore(std::filesystem::path const& inputFile,
344 SqlConnectionString const& connectionString,
345 unsigned concurrency,
346 ProgressManager& progress,
347 std::string const& schema,
348 std::string const& tableFilter,
349 RetrySettings const& retrySettings,
350 RestoreSettings const& restoreSettings);
351
352/// Returns a copy of `connectionString` with the values of `PWD=` and
353/// `Password=` attributes replaced by `***`.
354///
355/// The string is parsed attribute-wise (`KEY=VALUE` pairs separated by `;`)
356/// following ODBC's quoting rules, so redaction is not fooled by:
357/// - brace-quoted values — `PWD={pa;ss}` masks the whole `{...}` group,
358/// including the embedded `;`, and a `;` inside any other brace-quoted value
359/// (e.g. a driver name) does not start a new attribute;
360/// - whitespace after a separator — `...; PWD=secret` is still matched.
361///
362/// Key matching is case-insensitive and only ever matches a whole attribute
363/// name, never a substring of another key (`MyPWD=`) or of a value
364/// (`Database=PasswordVault`). Applied by CreateMetadata() so a
365/// secretRef-resolved plaintext password never lands in an archive's
366/// metadata.json (mirrors dbtool's `list-profiles` redaction).
367///
368/// @param connectionString Raw ODBC connection string.
369/// @return The connection string with password values masked.
370[[nodiscard]] LIGHTWEIGHT_API std::string RedactConnectionStringSecrets(std::string_view connectionString);
371
372/// Creates the metadata JSON content.
373///
374/// @param connectionString the connection string used to connect to the database.
375/// @param tables the list of tables to backup.
376/// @param schema the database schema used for these tables (optional).
377LIGHTWEIGHT_API std::string CreateMetadata(SqlConnectionString const& connectionString,
378 SqlSchema::TableList const& tables,
379 std::string const& schema = {});
380
381/// Parses the metadata JSON content and returns a map of table info.
382///
383/// @param metadataJson content of the metadata.json file.
384LIGHTWEIGHT_API std::map<std::string, TableInfo> ParseSchema(std::string_view metadataJson,
385 ProgressManager* progress = nullptr);
386
387} // namespace Lightweight::SqlBackup
size_t ErrorCount() const noexcept override
Returns the number of errors encountered during the operation.
void Update(Progress const &progress) override
Gets called when the progress of an individual backup/restore operation changes.
CompressionMethod method
The compression method to use.
Definition SqlBackup.hpp:48
bool schemaOnly
If true, only export schema metadata without backing up table data.
Definition SqlBackup.hpp:74
virtual void SetTotalItems(size_t totalItems)
ProgressManager()=default
Default constructor.
virtual void SetTotalTables(size_t totalTables)
virtual void Update(Progress const &p)=0
Gets called when the progress of an individual backup/restore operation changes.
virtual void SetMaxTableNameLength(size_t)
virtual size_t ErrorCount() const noexcept
Returns the number of errors encountered during the operation.
ProgressManager & operator=(ProgressManager const &)=default
Default copy assignment operator.
virtual void OnItemsProcessed(size_t count)
ProgressManager(ProgressManager &&)=default
Default move constructor.
ProgressManager & operator=(ProgressManager &&)=default
Default move assignment operator.
virtual void AddTotalItems(size_t additionalItems)
virtual void AllDone()=0
Gets called when all backup/restore operations are finished.
ProgressManager(ProgressManager const &)=default
Default copy constructor.
size_t currentRows
The current number of rows processed.
State state
The state of an individual backup/restore operation.
std::string message
A message associated with the progress update.
std::optional< size_t > totalRows
The total number of rows to be processed, if known.
std::string tableName
The name of the table being backed up / restored.
State
The state of an individual backup/restore operation.
std::size_t memoryLimitBytes
Memory limit in bytes (0 = auto-detect from system).
bool schemaOnly
If true, only recreate schema without importing data.
Information about a table being backed up.
std::vector< SqlColumnDeclaration > columns
The list of columns in the table.
std::vector< SqlSchema::IndexDefinition > indexes
The indexes on the table (excluding primary key index).
std::vector< bool > isBinaryColumn
The list of columns in the table.
std::vector< SqlSchema::ForeignKeyConstraint > foreignKeys
The list of foreign key constraints in the table.
std::string fields
The list of columns in the table in SQL format.