Renamed internal DB interfaces to allow for more namespace (Coming Soon!)
[jigit.git] / jigdb-sql.c
1 #include <sqlite3.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <errno.h>
6 #include "jigdb.h"
7
8 typedef struct
9 {
10     sqlite3 *db;
11 } db_state_t;
12
13 struct results
14 {
15     struct results *next;
16     struct results *prev;
17     db_file_entry_t entry;
18 };
19
20 struct results *res_head = NULL;
21 struct results *res_current = NULL;
22 struct results *res_tail = NULL;
23
24 char sql_command[2 * PATH_MAX];
25
26 JIGDB *db_open(char *db_name)
27 {
28     db_state_t *dbp = NULL;
29     int error = 0;            /* function return value */
30     char *open_error;
31
32     /* Allocate state structure */
33     dbp = calloc(1, sizeof(*dbp));
34     if (dbp)
35     {
36         error = sqlite3_open(db_name, &dbp->db);
37         if (error)
38         {
39             fprintf(stderr, "Unable to open sqlite file %s: error %d\n", db_name, error);
40             errno = error;
41             return NULL;
42         }
43         
44         /* We have a database pointer open. Do we need to init the
45          * "files" table? Try to grab the first row of the table and
46          * see if we get an error. There has to be a better way than
47          * this! */
48         error = sqlite3_exec(dbp->db, "SELECT COUNT(*) FROM files;", NULL, NULL, NULL);
49         if (SQLITE_ERROR == error)
50         {
51             /* We can't access the table. Delete it and create new */
52             error = sqlite3_exec(dbp->db, "DROP TABLE files;", NULL, NULL, NULL);
53             sprintf(sql_command, "CREATE TABLE files ("
54                     "md5 VARCHAR(32),"
55                     "filetype INTEGER,"
56                     "mtime INTEGER,"
57                     "age INTEGER,"
58                     "size INTEGER,"
59                     "filename VARCHAR(%d),"
60                     "extra VARCHAR(%d));", PATH_MAX, PATH_MAX);
61             error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
62             if (error)
63             {
64                 fprintf(stderr, "db_open: got error %d (%s) from create\n", error, open_error);
65                 if (open_error)
66                     sqlite3_free(open_error);
67                 errno = error;
68                 return NULL;
69             }
70             /* Create indices */
71             sprintf(sql_command, "CREATE INDEX files_md5 ON files (md5);");
72             error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
73             if (error)
74             {
75                 fprintf(stderr, "db_open: got error %d (%s) from create index\n", error, open_error);
76                 if (open_error)
77                     sqlite3_free(open_error);
78                 sqlite3_close(dbp->db);
79                 errno = error;
80                 return NULL;
81             }                                 
82             sprintf(sql_command, "CREATE INDEX files_name ON files (filename);");
83             error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
84             if (error)
85             {
86                 fprintf(stderr, "db_open: got error %d (%s) from create index\n", error, open_error);
87                 if (open_error)
88                     sqlite3_free(open_error);
89                 sqlite3_close(dbp->db);
90                 errno = error;
91                 return NULL;
92             }
93             sprintf(sql_command, "CREATE INDEX files_age ON files (age);");
94             error = sqlite3_exec(dbp->db, sql_command, NULL, NULL, &open_error);
95             if (error)
96             {
97                 fprintf(stderr, "db_open: got error %d (%s) from create index\n", error, open_error);
98                 if (open_error)
99                     sqlite3_free(open_error);
100                 sqlite3_close(dbp->db);
101                 errno = error;
102                 return NULL;
103             }
104         }
105     }
106     
107     return dbp;
108 }
109
110 int db_close(JIGDB *dbp)
111 {
112     db_state_t *state = dbp;
113     /* When we're done with the database, close it. */
114     if (state->db)
115         sqlite3_close(state->db);
116     free(state);
117     return 0;
118 }
119
120 int db_store_file(JIGDB *dbp, db_file_entry_t *entry)
121 {
122     int error = 0;
123     db_state_t *state = dbp;
124     char *open_error;
125     
126     sprintf(sql_command, "INSERT INTO files VALUES('%s', %d , %ld , %ld , %lld , '%s', '%s');",
127             entry->md5, entry->type, entry->mtime, entry->age,
128             entry->file_size, entry->filename, entry->extra);
129     error = sqlite3_exec(state->db, sql_command, NULL, NULL, &open_error);
130     if (error)
131     {
132         fprintf(stderr, "db_store_file: Failed to write entry, error %d (%s)\n", error, open_error);
133         if (open_error)
134             sqlite3_free(open_error);
135         return error;
136     }
137     return error;
138 }
139
140 void free_results(void)
141 {
142     struct results *entry = res_head;
143     struct results *current = res_head;
144     
145     while(entry)
146     {
147         entry = entry->next;
148         free(current);
149         current = entry;
150     }
151     res_head = NULL;
152     res_current = NULL;
153     res_tail = NULL;
154 }
155
156 static int results_callback(void *pArg, int argc, char **argv, char **columnNames)
157 {
158     struct results *entry = calloc(1, sizeof (*entry));
159     
160     if (res_tail)
161         res_tail->next = entry;
162     if (!res_head)
163         res_head = entry;
164
165     entry->prev = res_tail;
166     res_tail = entry;
167     if (argv[0])
168         strncpy(entry->entry.md5, argv[0], sizeof(entry->entry.md5));
169     if (argv[1])
170         entry->entry.type = strtol(argv[1], NULL, 10);
171     if (argv[2])
172         entry->entry.mtime = strtoul(argv[2], NULL, 10);
173     if (argv[3])
174         entry->entry.age = strtoul(argv[3], NULL, 10);
175     if (argv[4])
176         entry->entry.file_size = strtoull(argv[4], NULL, 10);
177     if (argv[5])
178         strncpy(entry->entry.filename, argv[5], sizeof(entry->entry.filename));
179     if (argv[6])
180         strncpy(entry->entry.extra, argv[6], sizeof(entry->entry.extra));
181
182     return 0;
183 }
184
185 /* Look up the most recent record that is older than the specified
186  * age */
187 int db_lookup_file_by_age(JIGDB *dbp, time_t age, db_file_entry_t **out)
188 {
189     int error = 0;
190     db_state_t *state = dbp;
191     char *open_error;
192
193     free_results();
194
195     sprintf(sql_command, "SELECT * FROM files WHERE age > %ld", age);
196     error = sqlite3_exec(state->db, sql_command, results_callback, NULL, &open_error);
197     if (error)
198     {
199         fprintf(stderr, "db_lookup_file_by_age: Failed to lookup, error %d (%s)\n", error, open_error);
200         return error;
201     }
202
203     res_current = res_head;
204     if (res_current)
205     {
206         *out = &res_current->entry;
207         res_current = res_current->next;
208     }
209     else
210         error = ENOENT;
211
212     return error;
213 }
214
215 /* Look up the next oldest record */
216 int db_lookup_file_older(JIGDB *dbp, db_file_entry_t **out)
217 {
218     int error = 0;
219
220     if (!res_head)
221         return EINVAL;
222     
223     if (res_current)
224     {
225         *out = &res_current->entry;
226         res_current = res_current->next;
227     }
228     else
229         error = ENOENT;
230
231     return error;
232 }
233
234 int db_lookup_file_by_md5(JIGDB *dbp, char *md5, db_file_entry_t **out)
235 {
236     int error = 0;
237     db_state_t *state = dbp;
238     char *open_error;
239
240     free_results();
241
242     sprintf(sql_command, "SELECT * FROM files WHERE md5 == '%s' ORDER BY filetype ASC;", md5);
243     error = sqlite3_exec(state->db, sql_command, results_callback, NULL, &open_error);
244     if (error)
245     {
246         fprintf(stderr, "db_lookup_file_by_md5: Failed to lookup, error %d (%s)\n", error, open_error);
247         return error;
248     }
249
250     res_current = res_head;
251     if (res_current)
252     {
253         *out = &res_current->entry;
254         res_current = res_current->next;
255     }
256     else
257         error = ENOENT;
258
259     return error;
260 }
261
262 int db_lookup_file_by_name(JIGDB *dbp, char *filename, db_file_entry_t **out)
263 {
264     int error = 0;
265     db_state_t *state = dbp;
266     char *open_error;
267
268     free_results();
269
270     sprintf(sql_command, "SELECT * FROM files WHERE filename == '%s';", filename);
271     error = sqlite3_exec(state->db, sql_command, results_callback, NULL, &open_error);
272     if (error)
273     {
274         fprintf(stderr, "db_lookup_file_by_name: Failed to lookup, error %d (%s)\n", error, open_error);
275         return error;
276     }
277
278     res_current = res_head;
279     if (res_current)
280     {
281         *out = &res_current->entry;
282         res_current = res_current->next;
283     }
284     else
285         error = ENOENT;
286
287     return error;
288 }
289
290 int db_delete_file(JIGDB *dbp, char *md5, enum filetype type, char *filename)
291 {
292     int error = 0;
293     db_state_t *state = dbp;
294     char *open_error;
295
296     sprintf(sql_command, "DELETE FROM files WHERE md5 == '%s' AND type == '%d' AND filename == '%s';", md5, type, filename);
297     error = sqlite3_exec(state->db, sql_command, NULL, NULL, &open_error);
298     if (error)
299         fprintf(stderr, "db_delete_file: Failed to delete, error %d (%s)\n", error, open_error);
300
301     return error;
302 }
303
304 int db_dump(JIGDB *dbp)
305 {
306     int error = 0;
307 /*    int num_records = 0;
308     db_file_entry_t *entry = NULL;
309     db_state_t *state = dbp; */
310
311     return error;
312 }