Line data Source code
1 : #include "precizer.h"
2 : #define DB_RUNTIME_PATHS_ID "runtime_paths_id"
3 : #define STRINGIFY(x) #x
4 : #define TOSTRING(x) STRINGIFY(x)
5 :
6 : /**
7 : *
8 : * Initialize SQLite database
9 : *
10 : */
11 194 : Return db_init(void)
12 : {
13 : /// The status that will be passed to return() before exiting.
14 : /// By default, the function worked without errors.
15 194 : Return status = SUCCESS;
16 :
17 : /* Interrupt the function smoothly */
18 : /* Interrupt when Ctrl+C */
19 194 : if(global_interrupt_flag == true)
20 : {
21 0 : provide(status);
22 : }
23 :
24 : // SQL request result
25 : int rc;
26 :
27 : /* Open database */
28 :
29 194 : const char *db_file_path = config->db_primary_file_path;
30 :
31 194 : if(config->sqlite_open_flag == SQL_DRY_RUN_MODE)
32 : {
33 2 : db_file_path = ":memory:";
34 2 : config->sqlite_open_flag = SQLITE_OPEN_READWRITE;
35 2 : slog(TRACE,"Dry Run mode was activated. In-memory database will be used to simulate activity.\n");
36 : }
37 :
38 194 : if(SQLITE_OK == (rc = sqlite3_open_v2(db_file_path,&config->db,config->sqlite_open_flag,NULL)))
39 : {
40 192 : slog(TRACE,"Successfully opened database %s\n",config->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 : config->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 194 : if(config->db_initialize_tables == true)
78 : {
79 80 : 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 78 : 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 78 : rc = sqlite3_exec(config->db,sql,NULL,NULL,NULL);
134 :
135 78 : if(rc == SQLITE_OK)
136 : {
137 78 : 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 114 : slog(TRACE,"The primary database and tables do not require initialization\n");
145 : }
146 :
147 194 : if(SUCCESS == status)
148 : {
149 : // Tune the DB performance
150 192 : const char *pragma_sql = NULL;
151 :
152 192 : if(config->compare == true || config->dry_run == true)
153 : {
154 : // Read-only mode
155 46 : 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 page_size=4096;" // Set page size to 4KB (default, but explicit for clarity)
161 : "PRAGMA locking_mode=EXCLUSIVE;" // Hold exclusive locks for the session
162 : "PRAGMA strict=ON;"; // Enforce STRICT table schema validation
163 : } else {
164 : // Read-write mode
165 146 : pragma_sql =
166 : "PRAGMA journal_mode=DELETE; " // Use DELETE journal
167 : "PRAGMA page_size=4096; " // Set page size to 4KB (default, but explicit for clarity)
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 : "PRAGMA fsync=OFF;"; // Disables fsync() for faster writes but risks data loss on crash
174 : }
175 :
176 : // Set SQLite pragmas
177 192 : rc = sqlite3_exec(config->db,pragma_sql,NULL,NULL,NULL);
178 :
179 192 : if(rc == SQLITE_OK)
180 : {
181 192 : slog(TRACE,"The primary database named %s is ready for operations\n",config->db_file_name);
182 : } else {
183 0 : log_sqlite_error(config->db,rc,NULL,"Can't execute pragma setup");
184 0 : status = FAILURE;
185 : }
186 : }
187 :
188 194 : if(SUCCESS == status)
189 : {
190 192 : if(config->compare != true)
191 : {
192 156 : const char *db_runtime_paths = "ATTACH DATABASE ':memory:' AS " DB_RUNTIME_PATHS_ID ";"
193 : "CREATE TABLE if not exists runtime_paths_id.the_path_id_does_not_exists"
194 : "(path_id INTEGER UNIQUE NOT NULL);";
195 :
196 156 : rc = sqlite3_exec(config->db,db_runtime_paths,NULL,NULL,NULL);
197 :
198 156 : if(rc == SQLITE_OK)
199 : {
200 156 : slog(TRACE,"The in-memory %s database successfully attached to the primary database %s\n",DB_RUNTIME_PATHS_ID,config->db_file_name);
201 : } else {
202 0 : log_sqlite_error(config->db,rc,NULL,"Can't execute runtime paths attach");
203 0 : status = FAILURE;
204 : }
205 : }
206 : }
207 :
208 194 : slog(TRACE,"Database initialization process completed\n");
209 :
210 194 : provide(status);
211 : }
|