Line data Source code
1 : /**
2 : * @file precizer.h
3 : * @brief Database operations for directory prefix paths
4 : */
5 :
6 : #include "precizer.h"
7 :
8 : /**
9 : * @brief Saves directory prefix paths into the database
10 : *
11 : * @details This function handles the storage of directory prefix paths in the database
12 : * according to several operational modes:
13 : *
14 : * Operational Modes:
15 : * - Compare Mode: Function returns immediately with SUCCESS
16 : * - Force Mode: Previous records are deleted before insertion
17 : * - Dry Run Mode: Data insertion occurs in memory only
18 : *
19 : * Data Insertion Rules:
20 : * - Data is inserted only if:
21 : * - Database file is not physical, OR
22 : * - Dry Run mode is not activated
23 : * - In Dry Run mode with physical database:
24 : * - Writing occurs to in-memory database only
25 : *
26 : * Processing Steps:
27 : * 1. Checks operational mode
28 : * 2. Handles record deletion if in Force mode
29 : * 3. Processes path insertions according to insertion rules
30 : * 4. Removes trailing slashes from paths before insertion
31 : *
32 : * Error Handling:
33 : * - SQLite preparation errors
34 : * - SQLite binding errors
35 : * - SQLite execution errors
36 : * All errors are logged using slog() function
37 : *
38 : * @pre config structure must be properly initialized with:
39 : * - compare flag
40 : * - force flag
41 : * - dry_run flag
42 : * - db connection
43 : * - paths array
44 : *
45 : * @return Return enum value:
46 : * - SUCCESS (0): Paths saved successfully
47 : * - FAILURE (1): Operation failed due to database errors
48 : */
49 184 : Return db_save_prefixes(void)
50 : {
51 : /// The status that will be passed to return() before exiting.
52 : /// By default, the function worked without errors.
53 184 : Return status = SUCCESS;
54 :
55 : /* Interrupt the function smoothly */
56 : /* Interrupt when Ctrl+C */
57 184 : if(global_interrupt_flag == true)
58 : {
59 0 : provide(status);
60 : }
61 :
62 : /* Skip in comparison mode */
63 184 : if(config->compare == true)
64 : {
65 32 : return(status);
66 : }
67 :
68 152 : if(config->force == true && config->dry_run == false)
69 : {
70 : /* Delete previous records in the table */
71 2 : sqlite3_stmt *delete_stmt = NULL;
72 :
73 2 : const char *delete_sql = "DELETE FROM paths WHERE ID IN (SELECT path_id FROM runtime_paths_id.the_path_id_does_not_exists);";
74 :
75 2 : int rc = sqlite3_prepare_v2(config->db,delete_sql,-1,&delete_stmt,NULL);
76 :
77 2 : if(SQLITE_OK != rc)
78 : {
79 0 : log_sqlite_error(config->db,rc,NULL,"Can't prepare delete statement");
80 0 : status = FAILURE;
81 : }
82 :
83 2 : if(SUCCESS == status)
84 : {
85 : /* Execute SQL statement */
86 2 : if(sqlite3_step(delete_stmt) != SQLITE_DONE)
87 : {
88 0 : log_sqlite_error(config->db,rc,NULL,"Delete statement didn't return DONE");
89 0 : status = FAILURE;
90 : }
91 : }
92 :
93 2 : if(SUCCESS == status)
94 : {
95 : /* Reflect changes in global */
96 2 : config->db_primary_file_modified = true;
97 : }
98 :
99 2 : sqlite3_finalize(delete_stmt);
100 : }
101 :
102 : /**
103 : * @brief Data insertion handling rules
104 : * @details Data insertion occurs only if the database file is not a physical file
105 : * and Dry Run mode is not activated. With Dry Run mode activated,
106 : * if a physical file is not opened, writing occurs to an in-memory database.
107 : */
108 152 : if(!(config->dry_run == true && config->db_primary_file_exists == true))
109 : {
110 288 : for(int i = 0; config->paths[i]; i++)
111 : {
112 : // Remove unnecessary trailing slash at the end of the directory prefix
113 144 : remove_trailing_slash(config->paths[i]);
114 :
115 144 : const char *select_sql = "SELECT COUNT(*) FROM paths WHERE prefix = ?1;";
116 144 : sqlite3_stmt *select_stmt = NULL;
117 :
118 : /* First check if prefix exists */
119 144 : int rc = sqlite3_prepare_v2(config->db,select_sql,-1,&select_stmt,NULL);
120 :
121 144 : if(SQLITE_OK != rc)
122 : {
123 0 : log_sqlite_error(config->db,rc,NULL,"Can't prepare select statement %s",select_sql);
124 0 : status = FAILURE;
125 : }
126 :
127 144 : if(SUCCESS == status)
128 : {
129 144 : rc = sqlite3_bind_text(select_stmt,1,config->paths[i],(int)strlen(config->paths[i]),NULL);
130 :
131 144 : if(SQLITE_OK != rc)
132 : {
133 0 : log_sqlite_error(config->db,rc,NULL,"Error binding value in select");
134 0 : status = FAILURE;
135 : }
136 : }
137 :
138 144 : int count = 0;
139 :
140 144 : if(SUCCESS == status)
141 : {
142 144 : if(sqlite3_step(select_stmt) == SQLITE_ROW)
143 : {
144 144 : count = sqlite3_column_int(select_stmt,0);
145 : }
146 : }
147 :
148 144 : sqlite3_finalize(select_stmt);
149 :
150 : /* Only proceed with insert if prefix doesn't exist */
151 144 : if(count == 0)
152 : {
153 80 : const char *insert_sql = "INSERT OR IGNORE INTO paths(prefix) VALUES(?1);";
154 80 : sqlite3_stmt *insert_stmt = NULL;
155 :
156 : /* Create SQL statement. Prepare to write */
157 80 : rc = sqlite3_prepare_v2(config->db,insert_sql,-1,&insert_stmt,NULL);
158 :
159 80 : if(SQLITE_OK != rc)
160 : {
161 0 : log_sqlite_error(config->db,rc,NULL,"Can't prepare insert statement %s",insert_sql);
162 0 : status = FAILURE;
163 : }
164 :
165 80 : if(SUCCESS == status)
166 : {
167 80 : rc = sqlite3_bind_text(insert_stmt,1,config->paths[i],(int)strlen(config->paths[i]),NULL);
168 :
169 80 : if(SQLITE_OK != rc)
170 : {
171 0 : log_sqlite_error(config->db,rc,NULL,"Error binding value in insert");
172 0 : status = FAILURE;
173 : }
174 : }
175 :
176 : /* Execute SQL statement */
177 80 : if(SUCCESS == status)
178 : {
179 80 : if(sqlite3_step(insert_stmt) != SQLITE_DONE)
180 : {
181 0 : log_sqlite_error(config->db,rc,NULL,"Insert statement didn't return DONE");
182 0 : status = FAILURE;
183 : }
184 : }
185 :
186 80 : if(SUCCESS == status && config->dry_run == false)
187 : {
188 : /* Reflect changes in global */
189 78 : config->db_primary_file_modified = true;
190 : }
191 :
192 80 : sqlite3_finalize(insert_stmt);
193 : }
194 :
195 144 : if(SUCCESS != status)
196 : {
197 0 : break;
198 : }
199 : }
200 : }
201 :
202 152 : return(status);
203 : }
|