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