Branch data Line data Source code
1 : : #include "precizer.h"
2 : : #define STRINGIFY(x) #x
3 : : #define TOSTRING(x) STRINGIFY(x)
4 : :
5 : : /**
6 : : *
7 : : * Initialize SQLite database
8 : : *
9 : : */
10 : 391 : Return db_init(void)
11 : : {
12 : : /* Status returned by this function through provide()
13 : : Default value assumes successful completion */
14 : 391 : Return status = SUCCESS;
15 : :
16 : : /* Interrupt the function smoothly */
17 : : /* Interrupt when Ctrl+C */
18 [ - + ]: 391 : if(global_interrupt_flag == true)
19 : : {
20 : 0 : provide(status);
21 : : }
22 : :
23 : : // SQL request result
24 : : int rc;
25 : :
26 : : /* Open database */
27 : :
28 : 391 : const char *db_file_path = confstr(db_primary_file_path);
29 : :
30 [ + + ]: 391 : if(config->sqlite_open_flag == SQL_DRY_RUN_MODE)
31 : : {
32 : 6 : db_file_path = ":memory:";
33 : 6 : config->db_primary_path_is_memory = true;
34 : 6 : config->sqlite_open_flag = SQLITE_OPEN_READWRITE;
35 : 6 : slog(TRACE,"Dry Run mode was activated. In-memory database will be used to simulate activity.\n");
36 : : }
37 : :
38 [ + + ]: 391 : if(SQLITE_OK == (rc = sqlite3_open_v2(db_file_path,&config->db,config->sqlite_open_flag,NULL)))
39 : : {
40 : 389 : slog(TRACE,"Successfully opened database %s\n",confstr(db_file_name));
41 [ + - ]: 2 : } else if(config->compare != true){
42 : 2 : log_sqlite_error(config->db,
43 : : rc,
44 : : NULL,
45 : : "Can't open database %s",
46 : 2 : confstr(db_primary_file_path));
47 : 2 : status = FAILURE;
48 : : }
49 : :
50 : : /**
51 : : * Allow or disallow database table initialization.
52 : : * If Dry Run mode is active, this option can be useful
53 : : * to prevent modification of the existing database.
54 : : *
55 : : * Table initialization will be necessary when the database
56 : : * is used in in-memory mode and is effectively recreated
57 : : * during the first connection.
58 : : *
59 : : * Additionally, database table initialization can be used
60 : : * to update the database schema during software upgrades.
61 : : *
62 : : * This describes several database management scenarios:
63 : : *
64 : : * 1. Table Initialization Control
65 : : * - Option to enable/disable table schema creation
66 : : * - Particularly useful with Dry Run mode to prevent schema changes
67 : : * - Acts as a safety mechanism for existing databases
68 : : * 2. In-Memory Database Scenarios
69 : : * - When using in-memory mode, tables need initialization
70 : : * - Tables are recreated on first connection
71 : : * - No persistent storage between sessions
72 : : * 3. Schema Migration Use Case
73 : : * - Can be used during software updates
74 : : * - Allows automatic schema updates
75 : : * - Supports database structure evolution with software versions
76 : : */
77 [ + + ]: 391 : if(config->db_initialize_tables == true)
78 : : {
79 [ + + ]: 161 : if(SUCCESS == status)
80 : : {
81 : : #if 0 // Frozen multiPATH feature
82 : : const char *sql =
83 : : "PRAGMA foreign_keys=OFF;"
84 : : "PRAGMA journal_mode=DELETE;" // Use DELETE journal to avoid WAL artifacts
85 : : "PRAGMA page_size=4096;" // Set page size to 4KB (default, but explicit for clarity)
86 : : "PRAGMA cache_size=-8192;" // Use 8MB of memory for caching (negative value = KB)
87 : : "PRAGMA synchronous=NORMAL;" // Balance speed and safety (NORMAL = fsync only for checkpoints)
88 : : "PRAGMA strict = ON;"
89 : : "BEGIN TRANSACTION;"
90 : : "CREATE TABLE IF NOT EXISTS metadata (db_version INTEGER NOT NULL UNIQUE);"
91 : : "CREATE TABLE IF NOT EXISTS files(" \
92 : : "ID INTEGER PRIMARY KEY NOT NULL,"
93 : : "offset INTEGER DEFAULT NULL,"
94 : : "path_prefix_index INTEGER NOT NULL,"
95 : : "relative_path TEXT NOT NULL,"
96 : : "sha512 BLOB DEFAULT NULL,"
97 : : "stat BLOB DEFAULT NULL,"
98 : : "mdContext BLOB DEFAULT NULL,"
99 : : "CONSTRAINT full_path UNIQUE (path_prefix_index, relative_path) ON CONFLICT FAIL);"
100 : : "CREATE INDEX IF NOT EXISTS full_path_ASC ON files (path_prefix_index, relative_path ASC);"
101 : : "CREATE TABLE IF NOT EXISTS paths ("
102 : : "ID INTEGER PRIMARY KEY UNIQUE NOT NULL,"
103 : : "prefix TEXT NOT NULL UNIQUE);"
104 : : "REPLACE INTO metadata (db_version) VALUES (" TOSTRING(CURRENT_DB_VERSION) ");"
105 : : "COMMIT;";
106 : : #endif
107 : :
108 : : /* Full runtime path is stored in the table 'paths' */
109 : 159 : const char *sql =
110 : : "PRAGMA foreign_keys=OFF;"
111 : : "PRAGMA journal_mode=DELETE;" // Use DELETE journal to avoid WAL artifacts
112 : : "PRAGMA page_size=4096;" // Set page size to 4KB (default, but explicit for clarity)
113 : : "PRAGMA cache_size=-8192;" // Use 8MB of memory for caching (negative value = KB)
114 : : "PRAGMA synchronous=NORMAL;" // Balance speed and safety (NORMAL = fsync only for checkpoints)
115 : : "PRAGMA strict = ON;"
116 : : "BEGIN TRANSACTION;"
117 : : "CREATE TABLE IF NOT EXISTS metadata (db_version INTEGER NOT NULL UNIQUE);"
118 : : "CREATE TABLE IF NOT EXISTS files(" \
119 : : "ID INTEGER PRIMARY KEY NOT NULL,"
120 : : "offset INTEGER DEFAULT NULL,"
121 : : "relative_path TEXT UNIQUE NOT NULL,"
122 : : "sha512 BLOB DEFAULT NULL,"
123 : : "stat BLOB DEFAULT NULL,"
124 : : "mdContext BLOB DEFAULT NULL);"
125 : : "CREATE UNIQUE INDEX IF NOT EXISTS 'TEXT_ASC' ON 'files' ('relative_path' ASC);"
126 : : "CREATE TABLE IF NOT EXISTS paths ("
127 : : "ID INTEGER PRIMARY KEY UNIQUE NOT NULL,"
128 : : "prefix TEXT NOT NULL UNIQUE);"
129 : : "REPLACE INTO metadata (db_version) VALUES (" TOSTRING(CURRENT_DB_VERSION) ");"
130 : : "COMMIT;";
131 : :
132 : : /* Execute SQL statement */
133 : 159 : rc = sqlite3_exec(config->db,sql,NULL,NULL,NULL);
134 : :
135 [ + - ]: 159 : if(rc == SQLITE_OK)
136 : : {
137 : 159 : slog(TRACE,"The primary database and tables have been successfully initialized\n");
138 : : } else {
139 : 0 : log_sqlite_error(config->db,rc,NULL,"Can't execute table initialization");
140 : 0 : status = FAILURE;
141 : : }
142 : : }
143 : : } else {
144 : 230 : slog(TRACE,"The primary database and tables do not require initialization\n");
145 : : }
146 : :
147 [ + + ]: 391 : if(SUCCESS == status)
148 : : {
149 : : // Tune the DB performance
150 : 389 : const char *pragma_sql = NULL;
151 : :
152 [ + + ]: 389 : if(config->sqlite_open_flag == SQLITE_OPEN_READONLY)
153 : : {
154 : : // Read-only mode
155 : 128 : pragma_sql =
156 : : "PRAGMA synchronous=OFF;" // Disable fsync to speed up read-only access
157 : : "PRAGMA cache_size=-8192;" // Increased cache to 8MB
158 : : "PRAGMA temp_store=MEMORY;" // Keep temporary data in RAM
159 : : "PRAGMA mmap_size=30000000000;" // Using memory-mapped I/O
160 : : "PRAGMA locking_mode=EXCLUSIVE;" // Hold exclusive locks for the session
161 : : "PRAGMA strict=ON;"; // Enforce STRICT table schema validation
162 : : } else {
163 : : // Read-write mode
164 : 261 : pragma_sql =
165 : : "PRAGMA journal_mode=DELETE; " // Use DELETE journal
166 : : "PRAGMA cache_size=-8192; " // Use 8MB of memory for caching (negative value = KB)
167 : : "PRAGMA synchronous=NORMAL; " // Balance speed and safety (NORMAL = fsync only for checkpoints)
168 : : "PRAGMA temp_store=MEMORY; " // Store temporary tables in memory (not on disk)
169 : : "PRAGMA strict=ON;" // Enforce STRICT table schema validation
170 : : "PRAGMA locking_mode=EXCLUSIVE;"; // Hold exclusive locks for the session
171 : : }
172 : :
173 : : // Set SQLite pragmas
174 : 389 : rc = sqlite3_exec(config->db,pragma_sql,NULL,NULL,NULL);
175 : :
176 [ + - ]: 389 : if(rc == SQLITE_OK)
177 : : {
178 : 389 : slog(TRACE,"The primary database named %s is ready for operations\n",confstr(db_file_name));
179 : : } else {
180 : 0 : log_sqlite_error(config->db,rc,NULL,"Can't execute pragma setup");
181 : 0 : status = FAILURE;
182 : : }
183 : : }
184 : :
185 [ + + ]: 391 : if(SUCCESS == status)
186 : : {
187 : 389 : const char *remember_history_sql =
188 : : "CREATE TEMP TABLE IF NOT EXISTS remember_history ("
189 : : "id INTEGER PRIMARY KEY AUTOINCREMENT,"
190 : : "message TEXT NOT NULL"
191 : : ");";
192 : :
193 : 389 : rc = sqlite3_exec(config->db,remember_history_sql,NULL,NULL,NULL);
194 : :
195 [ - + ]: 389 : if(rc != SQLITE_OK)
196 : : {
197 : 0 : log_sqlite_error(config->db,rc,NULL,"Can't create TEMP remember_history table");
198 : : }
199 : :
200 [ + + ]: 389 : if(config->compare != true)
201 : : {
202 : 271 : const char *db_runtime_paths =
203 : : "CREATE TEMP TABLE IF NOT EXISTS the_path_id_does_not_exists"
204 : : "(path_id INTEGER UNIQUE NOT NULL);";
205 : :
206 : 271 : rc = sqlite3_exec(config->db,db_runtime_paths,NULL,NULL,NULL);
207 : :
208 [ + - ]: 271 : if(rc == SQLITE_OK)
209 : : {
210 : 271 : slog(TRACE,"The TEMP table the_path_id_does_not_exists is ready for runtime checks\n");
211 : : } else {
212 : 0 : log_sqlite_error(config->db,rc,NULL,"Can't create runtime TEMP table");
213 : 0 : status = FAILURE;
214 : : }
215 : : }
216 : : }
217 : :
218 : 391 : slog(TRACE,"Database initialization process completed\n");
219 : :
220 : 391 : provide(status);
221 : : }
|