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 : 313 : Return db_init(void)
11 : : {
12 : : /* Status returned by this function through provide()
13 : : Default value assumes successful completion */
14 : 313 : Return status = SUCCESS;
15 : :
16 : : /* Interrupt the function smoothly */
17 : : /* Interrupt when Ctrl+C */
18 [ - + ]: 313 : 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 : 313 : const char *db_file_path = confstr(db_primary_file_path);
29 : :
30 [ + + ]: 313 : 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 [ + + ]: 313 : if(SQLITE_OK == (rc = sqlite3_open_v2(db_file_path,&config->db,config->sqlite_open_flag,NULL)))
39 : : {
40 : 311 : 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 [ + + ]: 313 : if(config->db_initialize_tables == true)
78 : : {
79 [ + + ]: 127 : 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 : 125 : 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 : 125 : rc = sqlite3_exec(config->db,sql,NULL,NULL,NULL);
134 : :
135 [ + - ]: 125 : if(rc == SQLITE_OK)
136 : : {
137 : 125 : 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 : 186 : slog(TRACE,"The primary database and tables do not require initialization\n");
145 : : }
146 : :
147 [ + + ]: 313 : if(SUCCESS == status)
148 : : {
149 : : // Tune the DB performance
150 : 311 : const char *pragma_sql = NULL;
151 : :
152 [ + + ]: 311 : if(config->sqlite_open_flag == SQLITE_OPEN_READONLY)
153 : : {
154 : : // Read-only mode
155 : 86 : 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 : 225 : 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 : : "PRAGMA fsync=OFF;"; // Disables fsync() for faster writes but risks data loss on crash
172 : : }
173 : :
174 : : // Set SQLite pragmas
175 : 311 : rc = sqlite3_exec(config->db,pragma_sql,NULL,NULL,NULL);
176 : :
177 [ + - ]: 311 : if(rc == SQLITE_OK)
178 : : {
179 : 311 : slog(TRACE,"The primary database named %s is ready for operations\n",confstr(db_file_name));
180 : : } else {
181 : 0 : log_sqlite_error(config->db,rc,NULL,"Can't execute pragma setup");
182 : 0 : status = FAILURE;
183 : : }
184 : : }
185 : :
186 [ + + ]: 313 : if(SUCCESS == status)
187 : : {
188 : 311 : const char *remember_history_sql =
189 : : "CREATE TEMP TABLE IF NOT EXISTS remember_history ("
190 : : "id INTEGER PRIMARY KEY AUTOINCREMENT,"
191 : : "message TEXT NOT NULL"
192 : : ");";
193 : :
194 : 311 : rc = sqlite3_exec(config->db,remember_history_sql,NULL,NULL,NULL);
195 : :
196 [ - + ]: 311 : if(rc != SQLITE_OK)
197 : : {
198 : 0 : log_sqlite_error(config->db,rc,NULL,"Can't create TEMP remember_history table");
199 : : }
200 : :
201 [ + + ]: 311 : if(config->compare != true)
202 : : {
203 : 235 : const char *db_runtime_paths =
204 : : "CREATE TEMP TABLE IF NOT EXISTS the_path_id_does_not_exists"
205 : : "(path_id INTEGER UNIQUE NOT NULL);";
206 : :
207 : 235 : rc = sqlite3_exec(config->db,db_runtime_paths,NULL,NULL,NULL);
208 : :
209 [ + - ]: 235 : if(rc == SQLITE_OK)
210 : : {
211 : 235 : slog(TRACE,"The TEMP table the_path_id_does_not_exists is ready for runtime checks\n");
212 : : } else {
213 : 0 : log_sqlite_error(config->db,rc,NULL,"Can't create runtime TEMP table");
214 : 0 : status = FAILURE;
215 : : }
216 : : }
217 : : }
218 : :
219 : 313 : slog(TRACE,"Database initialization process completed\n");
220 : :
221 : 313 : provide(status);
222 : : }
|