Branch data Line data Source code
1 : : #include "precizer.h"
2 : : #include <errno.h>
3 : :
4 : : /**
5 : : * @brief Rebuild a database and report the space saved and elapsed time
6 : : *
7 : : * The VACUUM command rebuilds the database file, repacking it into a minimal
8 : : * amount of disk space. File sizes are measured immediately before and after
9 : : * the SQL maintenance sequence, including its optimization steps. The reported
10 : : * percentage is relative to the initial file size and is negative if the file
11 : : * grows. An initially empty file has no defined percentage, reported as n/a
12 : : *
13 : : * @param[in] db_file_path Path to the database file to vacuum
14 : : * @return SUCCESS on completion or in dry-run mode, otherwise FAILURE
15 : : */
16 : 331 : Return db_vacuum(const char *db_file_path)
17 : : {
18 : : /* Status returned by this function through provide()
19 : : Default value assumes successful completion */
20 : 331 : Return status = SUCCESS;
21 : :
22 : 331 : sqlite3 *db = NULL;
23 : 331 : char *err_msg = NULL;
24 : 331 : bool db_is_primary = false;
25 : 331 : bool db_file_modified = false;
26 : 331 : struct stat db_stat_before = {0};
27 : 331 : struct stat db_stat_after = {0};
28 : :
29 : : /* Validate input parameters */
30 [ - + ]: 331 : if(db_file_path == NULL)
31 : : {
32 : 0 : slog(ERROR,"Invalid input parameters: db_file_path\n");
33 : 0 : provide(FAILURE);
34 : : }
35 : :
36 [ - + ]: 331 : if(config->dry_run == true)
37 : : {
38 : 0 : slog(TRACE,"Dry Run mode is enabled. The database doesn't require vacuuming\n");
39 : 0 : provide(status);
40 : : }
41 : :
42 [ + + ]: 331 : if(strcmp(db_file_path,confstr(db_primary_file_path)) == 0)
43 : : {
44 : 316 : db_is_primary = true;
45 : : }
46 : :
47 : : /* Open database in safe mode */
48 : 331 : int rc = sqlite3_open_v2(db_file_path,&db,SQLITE_OPEN_READWRITE,NULL);
49 : :
50 [ - + ]: 331 : if(SQLITE_OK != rc)
51 : : {
52 : 0 : log_sqlite_error(db,rc,NULL,"Failed to open database");
53 : 0 : status = FAILURE;
54 : : }
55 : :
56 [ + - ]: 331 : if(SUCCESS == status)
57 : : {
58 [ - + ]: 331 : if(stat(db_file_path,&db_stat_before) != 0)
59 : : {
60 : 0 : slog(ERROR,"Cannot stat database %s before vacuuming: %s\n",db_file_path,strerror(errno));
61 : 0 : status = FAILURE;
62 : : }
63 : : }
64 : :
65 [ + - ]: 331 : if(SUCCESS == status)
66 : : {
67 : : /* Create SQL statement */
68 : 331 : const char *sql =
69 : : "PRAGMA analyze;"
70 : : "PRAGMA optimize;"
71 : : "VACUUM;"
72 : : "PRAGMA analyze;"
73 : : "PRAGMA optimize;";
74 : :
75 [ + + ]: 331 : if(db_is_primary == true)
76 : : {
77 : 316 : slog(EVERY,"Start vacuuming the primary database…\n");
78 : : } else {
79 : 15 : slog(EVERY,"Start vacuuming…\n");
80 : : }
81 : :
82 : : /* Execute SQL statement */
83 : 331 : const long long int vacuum_start_ns = cur_time_monotonic_ns();
84 : 331 : rc = sqlite3_exec(db,sql,NULL,NULL,&err_msg);
85 : 331 : long long int vacuum_elapsed_ns = cur_time_monotonic_ns() - vacuum_start_ns;
86 : :
87 [ - + ]: 331 : if(SQLITE_OK != rc)
88 : : {
89 : :
90 : 0 : log_sqlite_error(db,rc,err_msg,"Can't execute vacuum");
91 : 0 : status = FAILURE;
92 : :
93 : : } else {
94 : :
95 : 331 : db_file_modified = true;
96 : :
97 [ - + ]: 331 : if(stat(db_file_path,&db_stat_after) != 0)
98 : : {
99 : 0 : slog(ERROR,"Cannot stat database %s after vacuuming: %s\n",db_file_path,strerror(errno));
100 : 0 : status = FAILURE;
101 : : } else {
102 [ - + ]: 331 : if(vacuum_elapsed_ns < 0LL)
103 : : {
104 : 0 : vacuum_elapsed_ns = 0LL;
105 : : }
106 : :
107 : 331 : char elapsed_string[50] = {0};
108 : 331 : (void)form_date_r(vacuum_elapsed_ns,MAJOR_VIEW,elapsed_string,sizeof(elapsed_string));
109 : :
110 : 331 : const char *db_label = "DB";
111 : :
112 [ + + ]: 331 : if(db_is_primary == true)
113 : : {
114 : 316 : db_label = "Primary DB";
115 : : }
116 : :
117 [ + - ]: 331 : if(db_stat_before.st_size > 0)
118 : : {
119 : 331 : const long double saved_percent =
120 : 331 : ((long double)db_stat_before.st_size - (long double)db_stat_after.st_size)
121 : 331 : * 100.0L / (long double)db_stat_before.st_size;
122 : :
123 : 331 : slog(EVERY,"%s vacuumed: saved %.2Lf%%, elapsed %s\n",db_label,saved_percent,elapsed_string);
124 : : } else {
125 : 0 : slog(EVERY,"%s vacuumed: saved n/a, elapsed %s\n",db_label,elapsed_string);
126 : : }
127 : : }
128 : : }
129 : : }
130 : :
131 [ + - ]: 331 : if(db_file_modified == true)
132 : : {
133 : : /**
134 : : *
135 : : * If the database being updated is the primary one, adjust the global
136 : : * flag indicating that the main database file has been
137 : : * modified (this will consequently update the file's ctime, mtime, and size)
138 : : */
139 [ + + ]: 331 : if(db_is_primary == true)
140 : : {
141 : : /* Changes have been made to the database. Update
142 : : this in the global variable value. */
143 : 316 : config->db_primary_file_modified = true;
144 : : }
145 : :
146 : : }
147 : :
148 : : /* Cleanup */
149 : 331 : call(db_close(db,&db_file_modified));
150 : :
151 : 331 : provide(status);
152 : : }
|