RSS

(root)/calliope : /src/core/library.c (revision 452)

Line Revision Contents
1 17 /*  Calliope Music Player
2 185  *  Copyright 2005-09 Sam Thursfield <ssssam gmail.com>
3 17  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15 269  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 17  */
17 269
18 449 /* library: a music_source which stores metadata in an SQL database.
19  *
20  *   * Entries are cached because querying one can mean querying a whole bunch of entries and it
21  *     ends up working very slowly. One possible is to filter which properties we return, but at
22  *     the moment its easiest to just implement a cache for entries so that methods like
23  *     music_source::track-added, which is called every time a track is added and immediately
24  *     queries everything that was just added, don't run slow to the point of death.
25  */
26 17
27 350 /* Strange way of getting strptime */
28 #define _GNU_SOURCE
29 #include <time.h>
30
31 17 #include <string.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <glib.h>
35 #include <glib/gstdio.h>
36 #include <gtk/gtk.h>
37 #include <sqlite3.h>
38 #include "format.h"
39 #include "main.h"
40 #include "schema.h"
41 #include "conftool.h"
42 #include "library.h"
43 451 #include "musicsource-private.h"
44 #include "library-private.h"
45 24 #include "libraryview.h"
46 17
47 356 #ifndef LIBRARY_DISABLE
48
49 201 #define DB_MAJOR_VERSION 1
50
51 451 typedef struct {
52         EntryType type; int id;
53 } _RemovalQueueEntry;
54
55 17 struct LibraryPrivate {
56 269         sqlite3 *db;
57 17         char *file_name;
58
59 269         // This is here because if the constructor fails we can't return null or anything nice,
60 95         // we have to go right through with init.
61 17         gboolean failed;
62 269
63 95         // Precalculated fill-entry queries for each entry type.
64 164         sqlite3_stmt **fill_entry_query;
65 17
66 451         GSList *removal_queue;
67
68 17         // FIXME: in fact the cache could be a lot more advanced. For example, there's no real need to cache all of the file entries, or probably
69         // the track entries really. It should be a fixed size to save running out of memory on huge libraries. Etc.
70         GHashTable *cache[ENTRY_TYPE_COUNT];
71 };
72
73 449 enum {  PROP_0, PROP_FILE_NAME  };
74 17
75 static GObject *constructor(GType type, guint n_construct_properties, GObjectConstructParam *construct_params);
76 static void finalize(GObject *self);
77 static void set_property(GObject *self, guint prop_id, const GValue *value, GParamSpec *pspec);
78
79 static void db_init(Library *self);
80 static void db_free(Library *self);
81 static void db_action(Library *db, GError **perror, const char *text);
82 50 void db_action_printf(Library *db, GError **perror, const char *format, ...);
83 17 static char **db_query(Library *db, int *rows, int *columns, GError **perror, const char *text);
84 static gint64 db_expression(Library *db, GError **perror, const char *text);
85 144 gint64 db_expression_printf(Library *db, GError **perror, const char *format, ...);
86 17 static void db_update_references(Library *self, int type, int from_id, int to_id);
87 static void db_move_row(Library *self, int table_id, int from_id, int to_id);
88 static void db_create(Library *db);
89 static void db_update(Library *db, int ver);
90 static void db_maintain_ids(Library *db);
91
92 274 static void cache_init (Library *self);
93 static void cache_free (Library *self);
94 283 static void *cache_store_entry (Library *self, Entry *entry);
95 274 static void cache_flush (Library *self);
96 451 static void cache_check (Library *self);
97 17
98 static char *make_property_match_string_except(Library *self, Entry *entry, int property, int ignored_apid);
99 static GSList *find_entries_matching_except(Library *self, Entry *entry, int ignored_apid, Entry *caller);
100 451 static int find_entry (MusicSource *music_source, Entry *entry, Entry *caller);
101 304 static inline Entry *fill_entry (Library *self, int entry_type, int entry_id, Entry *caller);
102 17
103 static gboolean is_empty(MusicSource *source);
104 static gboolean get_loaded(MusicSource *source);
105 static char *get_summary(MusicSource *self);
106 449 static int get_n_entries(MusicSource *music_source, EntryType type, int *highest_id, int *n_dead);
107 17 static gboolean is_valid_id(MusicSource *source, EntryType type, int id);
108 static Entry *query_entry(MusicSource *source, EntryType type, int id);
109 269 static int query_n_relations (MusicSource *music_source, int local_id, EntryType foreign_type,
110 102                               int foreign_property_id, int limit);
111 449 static GSList *query_entry_children(MusicSource *music_source, int parent_id, EntryType child_entry_type, int child_property);
112 static GSList *query_entry_children_ids(MusicSource *music_source, int parent_id, EntryType child_entry_type, int child_property);
113 static GSList *query_relations(MusicSource *music_source, EntryType local_type, int local_id, int relation_apid);
114 17 static GSList *query_ids(MusicSource *source, EntryType entry_type);
115 449 static GSList *query_matching_except(MusicSource *music_source, Entry *entry, int ignored_apid);
116 92
117 449 static MusicSourceView *create_view(MusicSource *music_source, ViewConfig *config);
118 92
119 451 static void add_entry_internal    (MusicSource *music_source, Entry *entry);
120 452 static void update_entry_internal (MusicSource *music_source, Entry *entry, int shadow_override_id,
121                                    void *shadow_override_value);
122 451 static void remove_entry_internal (MusicSource *music_source, EntryType type, int id);
123 static void update_foreign_keys   (MusicSource *music_source, Entry *from_entry, Entry *to_entry);
124
125 17 static void begin_transaction(MusicSource *self);
126 static void end_transaction(MusicSource *self);
127 451 //static void update_entry_property (MusicSource *self, EntryType entry_type, int entry_id,
128 //                                   int property_id, const void *value);
129 449 static int add_entry(MusicSource *music_source, Entry *entry);
130 static void remove_entry(MusicSource *music_source, EntryType type, int id);
131 static Entry *checkout_entry(MusicSource *music_source, EntryType type, int id);
132 static void checkin_entry(MusicSource *music_source, Entry *entry);
133 static void flush(MusicSource *music_source);
134 269
135 449 static void dump (MusicSource *music_source, gboolean detailed);
136 17
137 269 //Entry *add_recursive(Library *self, Entry *entry, Entry *caller);
138 //Entry *checkin_recursive(Library *self, Entry *entry, Entry *caller, gboolean touch_only,
139 //                        /);
140 17
141 static void method_track_enter(const char *text, ...);
142 static void method_track_leave(const char *text, ...);
143 static void method_track_printf(const char *text, ...);
144 274 static void cache_trace (const char *text, ...);
145 17
146 G_DEFINE_TYPE(Library, library, MUSIC_SOURCE_TYPE);
147
148 static void library_class_init(LibraryClass *cl) {
149         library_parent_class=g_type_class_peek_parent(G_OBJECT_CLASS(cl));
150         G_OBJECT_CLASS(cl)->constructor=constructor;
151         G_OBJECT_CLASS(cl)->finalize=finalize;
152         G_OBJECT_CLASS(cl)->set_property=set_property;
153 269
154         MusicSourceClass *music_source_class = MUSIC_SOURCE_CLASS(cl);
155 451         music_source_class->find_entry            = find_entry;
156 269         music_source_class->is_empty=is_empty;
157         music_source_class->get_loaded=get_loaded;
158         music_source_class->get_summary=get_summary;
159         music_source_class->get_n_entries=get_n_entries;
160         music_source_class->is_valid_id=is_valid_id;
161         music_source_class->query_entry=query_entry;
162         music_source_class->query_n_relations = query_n_relations;
163         music_source_class->query_entry_children=query_entry_children;
164         music_source_class->query_entry_children_ids=query_entry_children_ids;
165         music_source_class->query_relations = query_relations;
166         music_source_class->query_ids=query_ids;
167         music_source_class->query_matching_except=query_matching_except;
168         music_source_class->create_view=create_view;
169 451         music_source_class->add_entry_internal    = add_entry_internal;
170         music_source_class->update_entry_internal = update_entry_internal;
171         music_source_class->remove_entry_internal = remove_entry_internal;
172         music_source_class->update_foreign_keys   = update_foreign_keys;
173 269         music_source_class->begin_transaction=begin_transaction;
174         music_source_class->end_transaction=end_transaction;
175 451         //music_source_class->update_entry_property=update_entry_property;
176         music_source_class->add_entry             = add_entry;
177 269         music_source_class->remove_entry=remove_entry;
178         music_source_class->checkout_entry=checkout_entry;
179         music_source_class->checkin_entry=checkin_entry;
180         music_source_class->flush = flush;
181         music_source_class->dump = dump;
182 17
183         g_object_class_install_property(G_OBJECT_CLASS(cl), PROP_FILE_NAME,
184       g_param_spec_string("filename", "File name", "Database filename",
185       NULL, G_PARAM_CONSTRUCT_ONLY|G_PARAM_WRITABLE|STATICSTRS));
186
187         g_type_class_add_private(cl, sizeof(LibraryPrivate));
188 }
189
190 449 static void library_init (Library *self) {
191         SP = G_TYPE_INSTANCE_GET_PRIVATE(self, LIBRARY_TYPE, LibraryPrivate);
192 17
193 449         SP->file_name=NULL;
194 17         SP->failed=FALSE;
195
196 451         SP->removal_queue = NULL;
197
198 269         cache_init(self);
199 17 }
200
201 449 static GObject *constructor (GType type, guint n_construct_properties,
202                              GObjectConstructParam *construct_params) {
203 109         GObject *object = ((GObjectClass *)library_parent_class)->constructor(type, n_construct_properties, construct_params);
204         Library *self = LIBRARY(object);
205
206         // FIXME: would be nice if they didn't all have the same name.
207         _music_source_set_name (MUSIC_SOURCE(self), "Music Library", FALSE);
208 269
209 17         // Must be called after filename property is set
210         db_init(self);
211 269         if (SP->failed)
212                 return object;
213
214 95         // Precalculate fill-entry queries.
215         //
216 164         SP->fill_entry_query = g_new(sqlite3_stmt *, ENTRY_TYPE_COUNT);
217 95         for (int i=0; i<ENTRY_TYPE_COUNT; i++) {
218 164                 char *sql;
219 269
220 451                 if (entry_type_info[i].has_shadowing_properties) {
221 95                         GString *projection = g_string_new(NULL);
222                         GString *joins = g_string_new(NULL);
223                         gboolean join_flags[ENTRY_TYPE_COUNT] = {0};
224 168                         g_string_append_printf (projection, "_%s.id", entry_type_name[i]);
225 97                         for (int p=0; p<entry_n_properties[i]; p++)
226 104                                 _db_append_property_query(i, p, projection, TRUE, joins, join_flags);
227 95
228 168                         sql = g_strdup_printf("SELECT %s FROM _%s%s WHERE _%s.id=$id",
229 269                                           projection->str, entry_type_name[i],
230 449                                           joins->str, entry_type_name[i]);
231 95                         g_string_free (projection, TRUE);
232 451                 } else
233                         sql = g_strdup_printf("SELECT * FROM _%s WHERE id=$id", entry_type_name[i]);
234 269
235                 sqlite3_prepare_v2 (SP->db, sql, -1, &SP->fill_entry_query[i], NULL);
236 164                 g_free (sql);
237 95         };
238 269
239 17         return object;
240 };
241
242 449 static void finalize (GObject *object) {
243         Library *self = LIBRARY(object);
244 269
245 95         for (int i=0; i<ENTRY_TYPE_COUNT; i++)
246 164                 sqlite3_finalize (SP->fill_entry_query[i]);
247 95         g_free (SP->fill_entry_query);
248 269
249 17         cache_free(self);
250 451
251         if (SP->removal_queue != NULL)
252                 g_warning ("library: removal queue not empty at finalize: %i entries.\n",
253                            g_slist_length (SP->removal_queue));
254
255 17         db_free(self);
256 269
257 17         G_OBJECT_CLASS(library_parent_class)->finalize(object);
258 };
259
260 static void set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) {
261         Library *self=LIBRARY(object);
262 449         switch(prop_id) {
263 17             case PROP_FILE_NAME: {
264                 const char *file_name=g_value_get_string(value);
265                         if (g_path_is_absolute(file_name))
266                                 SP->file_name=g_strdup(file_name);
267 269                         else SP->file_name=g_build_filename(g_get_current_dir(), file_name, NULL);
268 17                 break;
269             };
270 269
271 17             default:
272                 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break;
273     };
274 };
275
276 449 MusicSource *library_new (const char *file_name) {
277         Library *self = g_object_new (LIBRARY_TYPE, "filename", file_name, NULL);
278 269
279 17         if (SP->failed) {
280 449                 g_object_ref_sink (self); g_object_unref (self);
281 17                 return NULL;
282         } else {
283 449                 return MUSIC_SOURCE (self);
284 17         };
285 };
286
287 ////////////////////////////////////////////////////////
288 // Generic SQL database methods
289 //
290 // FIXME: remove the gerrors, or make them work again
291
292 //#define TRACE_DB
293
294 164 sqlite3 *_library_get_db (Library *self) {
295         return SP->db;
296 269 };
297 164
298 449 static void db_init (Library *self) {
299 17         int result=sqlite3_open(SP->file_name, &SP->db);
300 269
301 17         if (result!=SQLITE_OK) {
302                 // FIXME: I hate monolog boxes
303                 // FIXME: test in create code if creating file will fail for some obvious
304                 // reason (eg dir read only) and give user a more descriptive message.
305 269                 GtkWidget *dialog=gtk_message_dialog_new_with_markup(NULL, GTK_DIALOG_MODAL,
306                   GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
307 17                   "<b>Unable to access database %s</b>\n\n %s.\n", SP->file_name, sqlite3_errmsg(SP->db));
308 201                 gtk_dialog_run (GTK_DIALOG(dialog)); gtk_widget_destroy (dialog);
309                 SP->failed = TRUE; return;
310 17         };
311 65
312         //db_action(SP->db, NULL, "PRAGMA legacy_file_format = OFF");
313
314 17         // FIXME: versioning doesn't really work
315 201         int major_version = 0, minor_version = 0;
316 17         char **data; int rows, columns;
317         GError *silencer=NULL;
318 269         data = db_query(self, &rows, &columns, &silencer,
319 201                         "SELECT major_version, minor_version FROM meta");
320 17         if (data!=NULL) {
321 269                 if (rows > 1 || columns!=2)  {
322 201                         // FIXME: I hate monolog boxes
323                         GtkWidget *dialog = gtk_message_dialog_new_with_markup
324 269                                               (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
325 201                                            "<b>Unable to load database %s</b>\n\n "
326                                                                    "The version information is invalid.\n", SP->file_name);
327                         gtk_dialog_run (GTK_DIALOG(dialog)); gtk_widget_destroy (dialog);
328 269                         SP->failed = TRUE; return;
329 201                 } else if (rows==1) {
330                         major_version = atoi(data[2]);
331                         minor_version = atoi(data[3]);
332                 };
333 17                 sqlite3_free_table(data);
334         };
335 269
336 17         begin_transaction(MUSIC_SOURCE(self));
337         // FIXME: this code is a little messy
338 201         if (major_version==0 && minor_version==0)
339 156                 db_create (self);
340 201         else if (major_version != DB_MAJOR_VERSION) {
341                 // FIXME: I hate monolog boxes
342                 GtkWidget *dialog = gtk_message_dialog_new_with_markup
343 269                                                           (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
344 201                                                            "<b>Database %s is from an incompatible version of Calliope</b>\n\n "
345                                                            "I can only read version %i, but this library is version %i.",
346                                                            SP->file_name, DB_MAJOR_VERSION, major_version);
347                 gtk_dialog_run (GTK_DIALOG(dialog)); gtk_widget_destroy (dialog);
348 269                 SP->failed = TRUE; return;
349 201         } else if (minor_version < SCHEMA_VERSION)
350                 db_update (self, minor_version);
351 269         else if (minor_version!=SCHEMA_VERSION)
352 154                 g_error("FIXME: broken db");
353 156         end_transaction (MUSIC_SOURCE(self));
354 269
355
356 156         db_maintain_ids (self);
357 17 };
358
359 222 static void db_free (Library *self) {
360         g_free (SP->file_name);
361 269
362 222         // sqlite3_close fails when prepared statements haven't been freed, for example.
363         int sqlite3_close_result = sqlite3_close(SP->db);
364         g_warn_if_fail (sqlite3_close_result==SQLITE_OK);
365 17 };
366
367 269 // Make these sqlite callers a little more concise. Note _F is executed on each loop
368 144 // iteration, which is exactly what we want.
369 #define SQLITE_WRAPPER(_F)                                                 G_STMT_START { \
370                 GTimer *timer = g_timer_new(); g_timer_start (timer);                             \
371                 while (1) {                                                                       \
372                         result = _F;                                                                  \
373                         if (result==SQLITE_BUSY || result==SQLITE_LOCKED) {                           \
374 154                                 gboolean retry = FALSE;                                                   \
375 346                                 _music_source_emit (MUSIC_SOURCE(self), MUSIC_SOURCE_SIGNAL_BUSY, 0,      \
376 144                                                    g_timer_elapsed(timer, NULL), SP->file_name, &retry);  \
377                                 if (retry) continue;                                                      \
378                                 else {                                                                    \
379                                         if (perror==NULL) g_warning ("Library: unhandled busy.");             \
380                                         break;                                                                \
381                                 };                                                                        \
382                         };                                                                            \
383                         if (result!=SQLITE_OK)                                                        \
384                                 if (perror==NULL)                                                         \
385                                         g_warning ("Library: SQL action failed: %s.\nCommand: %s",            \
386                                                error, text);                                              \
387                         break;                                                                        \
388                 };  g_timer_destroy (timer);                                        } G_STMT_END  \
389 269
390 144
391         // You can use this to test busy handling.
392 269         //              if (g_random_int()&2) result=SQLITE_LOCKED;
393 144
394 static void db_action (Library *self, GError **perror, const char *text) {
395 269         g_return_if_fail (self!=NULL);
396 144         int result; char *error;
397 452         
398         #ifdef LIBRARY_TRACE_QUERIES
399         printf("=> a %s\n", text); fflush(stdout);
400         #endif
401
402 144         SQLITE_WRAPPER (sqlite3_exec (SP->db, text, NULL, NULL, &error));
403 269 };
404 17
405 269 static char **db_query (Library *self, int *rows, int *columns, GError **perror,
406 144                         const char *text) {
407 269         g_return_val_if_fail (self!=NULL, NULL);
408 144         int result; char **data, *error;
409 17
410 132         #ifdef LIBRARY_TRACE_QUERIES
411 452         printf("=> q %s.. ", text); fflush(stdout);
412 92         #endif
413 269
414 144         SQLITE_WRAPPER (sqlite3_get_table(SP->db, text, &data, rows, columns, &error));
415 269
416 132         #ifdef LIBRARY_TRACE_QUERIES
417 452         printf ("rows %i columns %i\n", *rows, *columns); fflush(stdout);
418 269         #endif
419
420 17         return data;
421 };
422
423 144 // Note that the 'text' parameter is a printf format! Any non-format %'s in text must be escaped as %% !!
424 static gint64 db_expression (Library *self, GError **perror, const char *text) {
425 269         g_return_val_if_fail (self!=NULL, 0);
426 144         int result; char **data, *error; int rows, columns;
427 452
428         #ifdef LIBRARY_TRACE_QUERIES
429         printf("=> e %s ", text); fflush(stdout);
430         #endif
431
432 144         SQLITE_WRAPPER (sqlite3_get_table(SP->db, text, &data, &rows, &columns, &error));
433 269
434 144         gint64 value = 0;
435         if (result==SQLITE_OK) {
436                 // Sometimes sqlite lies and gives us rows=1 when all we have is the column
437                 // header.
438 269                 if (rows > 0 && columns > 0 && data[1]!=NULL)
439 144                         value = atoll(data[1]);
440         };
441         sqlite3_free_table (data);
442 452
443         #ifdef LIBRARY_TRACE_QUERIES
444         printf ("%" G_GINT64_FORMAT "\n", value); fflush (stdout);
445         #endif
446 144         return value;
447 };
448
449 // Remember any non-format %'s in format need escaping as %%
450 void db_action_printf(Library *self, GError **perror, const char *format, ...) {
451         char *buf;
452         va_list ap; va_start(ap, format); g_vasprintf(&buf, format, ap); va_end(ap);
453         db_action(self, perror, buf);
454         g_free(buf);
455 };
456
457 17 // Remember any non-format %'s in format need escaping as %%
458 24 char **db_query_printf(Library *self, int *rows, int *columns, GError **perror, const char *format, ...) {
459 144         char *buf; va_list ap; va_start(ap, format); g_vasprintf(&buf, format, ap); va_end(ap);
460 17         char **result=db_query(self, rows, columns, perror, buf);
461         g_free(buf);
462         return result;
463 };
464
465 // Remember any non-format %'s in format need escaping as %%
466 144 gint64 db_expression_printf(Library *self, GError **perror, const char *format, ...) {
467 17         char *buf;
468         va_list ap; va_start(ap, format); g_vasprintf(&buf, format, ap); va_end(ap);
469         gint64 result=db_expression(self, perror, buf);
470         g_free(buf);
471         return result;
472 };
473
474
475 // FIXME: use the precalculated reference matrix !
476 static void db_update_references(Library *self, int type, int from_id, int to_id) {
477         // This function updates all relations to an entry whose ID has changed.
478         for (int table=0;table<ENTRY_TYPE_COUNT;table++) {
479                 // FIXME: remove this
480 269                 if (str_match(entry_type_name[table], "FIXME")) continue;
481 17                 // FIXME: I guess this will be a problem if we ever have tables that reference themselves
482                 if (table==type) continue;
483 269
484 17                 // Find and update any columns which could reference the row being moved.
485 269                 //
486 17                 for (int col=0;col<entry_n_properties[table];col++) {
487                         if (schema[table][col].type==type) {
488 168                                 db_action_printf (self, NULL, "UPDATE _%s SET %s_id=%i WHERE %s_id=%i",
489                                                   entry_type_name[table], schema[table][col].name, to_id,
490 269                                                   schema[table][col].name, from_id);
491 //                              printf("%s.%s references %s\n", entry_type_name[table],
492 17 //                                schema[table][col].name, entry_type_name[table_id]);
493                         }
494                 }
495         };
496 };
497
498 static void db_move_row(Library *self, int table_id, int from_id, int to_id) {
499 269         db_action_printf (self, NULL, "UPDATE _%s SET id=%i WHERE id=%i",
500                           entry_type_name[table_id], to_id, from_id);
501 168         db_update_references (self, table_id, from_id, to_id);
502 17 };
503
504 static GString *make_column_def(int entry_type, int property_id) {
505 285         GString *column_def = g_string_new(NULL);
506         const EntryProperty *property = &schema[entry_type][property_id];
507         switch(property->type) {
508 17                 case PROPERTY_TYPE_INT:
509 285                         g_string_append(column_def, property->name);
510 17                         g_string_append(column_def, " INTEGER"); break;
511                 case PROPERTY_TYPE_FLOAT:
512 285                         g_string_append(column_def, property->name);
513 17                         g_string_append(column_def, " REAL"); break;
514                 case PROPERTY_TYPE_STRING:
515 285                         g_string_append(column_def, property->name);
516 17                         g_string_append(column_def, " VARCHAR"); break;
517                 case PROPERTY_TYPE_NAME:
518 285                         g_string_append(column_def, property->name);
519 67                         g_string_append(column_def, " VARCHAR"); break;
520 17                 case PROPERTY_TYPE_DATE:
521 285                         g_string_append(column_def, property->name);
522 17                         g_string_append(column_def, " DATE"); break;
523                 case PROPERTY_TYPE_DATE_TIME:
524 285                         g_string_append(column_def, property->name);
525 269                         g_string_append(column_def, " DATETIME"); break;
526 17                 default:        // Link to another table
527 285                         g_string_append_printf(column_def, "%s_id INTEGER", property->name);
528 17         };
529 286
530 285         if (property->flags & PROPERTY_UNIQUE)   g_string_append (column_def, " UNIQUE");
531         if (property->flags & PROPERTY_NOT_NULL) g_string_append (column_def, " NOT NULL");
532 17         return column_def;
533 };
534
535 static void db_create(Library *self) {
536         for (int i=0;i<ENTRY_TYPE_COUNT;i++) {
537                 // FIXME: remove this
538                 if (str_match(entry_type_name[i], "FIXME")) continue;
539 269
540 17                 GString *code=g_string_new(NULL);
541 269                 g_string_append_printf(code, "CREATE TABLE _%s (id INTEGER PRIMARY KEY ", entry_type_name[i]);
542
543 17                 for (int j=0;j<entry_n_properties[i];j++) {
544                         GString *column_def=make_column_def(i, j);
545                         g_string_append(code, ", ");
546                         g_string_append(code, column_def->str);
547                         g_string_free(column_def, TRUE);
548                 };
549                 g_string_append(code, ")");
550                 db_action(self, NULL, code->str);
551                 g_string_free(code, TRUE);
552 269         }
553 17
554 201         db_action (self, NULL, "CREATE TABLE meta (major_version INTEGER, minor_version INTEGER)");
555         db_action_printf (self, NULL, "INSERT INTO meta (major_version, minor_version) VALUES (%i, %i)",
556                           DB_MAJOR_VERSION, SCHEMA_VERSION);
557 269
558 201         db_action_printf (self, NULL, "INSERT INTO _artist (id, name, sortname) "
559 269                               "VALUES (%i, 'Unknown Artist', 'ZZZZUnknown Artist')", ARTIST_ID_UNKNOWN);
560 201         db_action_printf (self, NULL, "INSERT INTO _artist (id, name, sortname) "
561                               "VALUES (%i, 'Various Artists', 'ZZZZZVarious Artists')", ARTIST_ID_VARIOUS);
562 17
563 269         // FIXME: do these still help now code is rearranged? What others can we make ??
564 17         // FIXME: can we make more of these ??
565 449         // These are dropped when a transaction starts and created when it ends, in the music_source code below.
566 17         //db_action(self, NULL, "CREATE INDEX track_idx ON track(release_id, number);");
567         //db_action(self, NULL, "CREATE INDEX release_idx ON release (album_id, date);");
568 };
569
570 // Updates from current_version to SCHEMA_VERSION
571 156 static void db_update (Library *self, int current_version) {
572 17         printf("Updating database of version %i to version %i\n", current_version, SCHEMA_VERSION);
573 168
574         if (current_version < 3) {
575                 // Change table names from %s to _%s
576                 //
577 188                 for (int i=0; i<ENTRY_TYPE_COUNT; i++) {
578                         if (str_match(entry_type_name[i], "FIXME")) continue;
579 168                         db_expression_printf (self, NULL, "ALTER TABLE %s RENAME TO _%s",
580 188                                               entry_type_name[i], entry_type_name[i]);
581                 };
582 168         };
583 269
584 17         for (int i=0;i<ENTRY_TYPE_COUNT;i++) {
585                 // FIXME: remove this
586                 if (str_match(entry_type_name[i], "FIXME")) continue;
587 269
588 17                 for (int j=0;j<entry_n_properties[i];j++) {
589                         if (schema[i][j].initial_version>current_version) {
590                                 GString *column_def=make_column_def(i, j);
591 168                                 db_action_printf(self, NULL, "ALTER TABLE _%s ADD %s", entry_type_name[i], column_def->str);
592 17                                 g_string_free(column_def, TRUE);
593                         };
594                 };
595 269         }
596 201         db_action_printf(self, NULL, "UPDATE meta SET minor_version = %i", SCHEMA_VERSION);
597 17 };
598
599 static void db_maintain_ids(Library *self) {
600 269         // Ensures all of the rowids are consecutive and start at 1.
601 153         // FIXME: a bit of a slowdown on startup.
602 154         gboolean changed = FALSE;
603 269
604 17         for (int table=0;table<ENTRY_TYPE_COUNT;table++) {
605 168                 const char *table_name = entry_type_name[table];
606 269                 // FIXME: should not need this
607                 if (strcmp(table_name, "FIXME")==0) continue;
608
609 17                 int rows, columns; char **result;
610 168                 result=db_query_printf(self, &rows, &columns, NULL, "SELECT id FROM _%s ORDER BY id", table_name);
611 17                 if (rows==0 || result==NULL) continue;
612 269
613 17                 // Since order is not significant, the plan is to take any duplicates
614                 // etc to the bottom of the table and to fill any gaps encountered from
615                 // the bottom of the table.
616                 //
617 269                 // This bottom is tracked using three vars:
618                 //   highest_id points to the bottom row.
619 17                 //   highest_pos holds the actual row number of this row
620                 // When we are moving duplicates or id's <1 it is simple to append them
621                 // on the table. When we want to fill from the end it is more complex;
622                 // to find the id of original rows we can check result, but rows we have
623                 // added are not listed there. Therefore we also track highest_original_row.
624                 //
625                 int errors=0, gaps=0;
626                 int cur_valid_id=1, highest_original_row=rows, highest_pos=rows;
627                 int highest_id=atoi(result[highest_pos]);
628 269
629                 for (int cur_pos=1;cur_pos<=rows;) {
630 17                         int cur_id=atoi(result[cur_pos]);
631 269                         if (cur_id<cur_valid_id) {
632 17                                 errors++;
633                                 g_warning("Error at %i/%i: moving %i to %i\n", cur_pos, rows, cur_id, highest_id);
634                                 g_warning("\tHighest is %i pos %i\n", highest_id, highest_pos);
635                                 highest_pos++; highest_id++;
636                                 db_move_row(self, table, cur_id, highest_id);
637 269
638 17                                 cur_pos++;
639                         } else if (cur_id>cur_valid_id) {
640 269                                 gaps++;
641 17                                 //printf("Found gap at pos %i/%i: id %i should be %i\n", cur_pos, rows, cur_id, cur_valid_id);
642                                 //printf("\tHighest is %i pos %i, clean rows %i\n", highest_id, highest_pos, clean_rows);
643                                 // Move a row from the end.
644                                 db_move_row(self, table, highest_id, cur_valid_id);
645
646                                 highest_pos--;
647                                 if (highest_pos>highest_original_row) highest_id--;
648                                 else {
649                                         highest_id=atoi(result[highest_pos]);
650                                         highest_original_row=highest_pos;
651                                 };
652 269
653 17                                 cur_valid_id++;
654                         } else {
655                                 cur_valid_id++;
656                                 cur_pos++;
657                         };
658 269
659                         // Stop a gap fill overrunning if we are at the end of the table.
660 17                         if (highest_pos<cur_pos) break;
661 269                 }
662 24
663                 sqlite3_free_table(result);
664 269
665 17                 //printf("Table %s - highest id %i\n", table_name, highest_id);
666 269                 // We no longer lower sqlite_sequence, because id's aren't AUTOINCREMENT anymore.
667 167                 // Without autoincrement the primary keys are based on highest id+1 which is what we want anyway.
668 269                 //db_action_printf(self, NULL, "UPDATE sqlite_sequence SET seq=%i WHERE name=\"%s\"", highest_id, table_name);
669
670 17                 if (errors || gaps) {
671 269                         printf("Fixed %i errors and %i gaps in table _%s\n",
672 17                           errors, gaps, table_name);
673                         fflush(stdout);
674 154                         changed = TRUE;
675 269                 };
676         };
677
678 154         // Vacuum if we needed to do anything. I imagine this matches closely with when the
679         // db needs vacuuming (mostly when things have been removed I believe) so this should
680         // be the only place we need to call VACUUM.
681         if (changed == TRUE)
682                 db_action (self, NULL, "VACUUM");
683 17 };
684
685 104 void _db_append_join (EntryType local_type, int local_property_id, EntryType foreign_type,
686                       GString *joins, gboolean *join_flags) {
687 168         g_string_append_printf (joins, " INNER JOIN _%s ON _%s.%s_id = _%s.id",
688 269                                                         entry_type_name[foreign_type], entry_type_name[local_type],
689 104                                                         schema[local_type][local_property_id].name,
690                                                         entry_type_name[foreign_type]);
691         if (join_flags!=NULL)
692                 join_flags[foreign_type] = TRUE;
693 };
694
695 97 // Used constructing queries.
696 //  projection: expression to return property is appended
697 //       joins: any necessary joins are appended, taking into account -
698 //  join_flags: if TRUE for entry type, the table is considered already joined. join_flags is
699 //              updated when a join is appended by this function.
700 269 void _db_append_property_query (EntryType entry_type, int property_id, GString *projection,
701 104                                 gboolean comma_flag, GString *joins, gboolean *join_flags) {
702 222         g_return_if_fail (entry_type >= 0 && entry_type < ENTRY_TYPE_COUNT);
703         g_return_if_fail (property_id >= 0 && property_id < entry_n_properties[entry_type]);
704 97         EntryProperty column = schema[entry_type][property_id];
705
706         if (comma_flag)
707                 g_string_append_c (projection, ',');
708
709 269         if (column.flags & PROPERTY_SHADOW) {
710                 EntryType shadowee_type = APID_GET_TYPE(column.shadow_property_apid);
711                 g_string_append_printf (projection, " COALESCE(_%s.%s%s, _%s.%s%s)",
712                                         entry_type_name[entry_type],
713 97                                         column.name, IS_FOREIGN_KEY(column.type)?"_id":"",
714                                                             entry_type_name[shadowee_type],
715                                                             APID_GET_PROPERTY(column.shadow_property_apid).name,
716                                                             IS_FOREIGN_KEY(APID_GET_PROPERTY(column.shadow_property_apid).type)?"_id":"");
717 269
718 97                 if (shadowee_type!=entry_type && (join_flags!=NULL && !join_flags[shadowee_type])) {
719 269                         // Join the table with shadowee in to this one for the query.
720 97                         int join_property_id = -1;
721                         for (int j=0; j<entry_n_properties[entry_type]; j++)
722                                 if (schema[entry_type][j].type==shadowee_type) {
723                                         join_property_id = j; break;
724                                 };
725 269                         if (join_property_id==-1)
726 97                                 // FIXME: try harder
727 168                                 g_warning ("Unable to connect _%s to _%s for shadow property %s.\n",
728 97                                                    entry_type_name[entry_type], entry_type_name[shadowee_type],
729                                                    column.name);
730                         else {
731 104                                 //printf("Appending join from %s to %s\n", entry_type_name[entry_type], entry_type_name[shadowee_type]); fflush(stdout);
732                                 _db_append_join (entry_type, join_property_id, shadowee_type, joins, join_flags);
733 97                         };
734                 };
735         } else
736 168                 g_string_append_printf (projection, " _%s.%s%s", entry_type_name[entry_type],
737                                                                 column.name, IS_FOREIGN_KEY(column.type)?"_id":"");
738 97 };
739
740 274
741 /***************************************************************************************************
742  *   CACHE
743  *  -------
744  *
745 344  * The cache is mainly to reduce the cost of querying entries several times in a row - it's easier
746 274  * to implement a cache than to make all the location code, all the file import code etc. avoid
747  * querying entries multiple times.
748  *
749  * FIXME: have a think. Could entries knowing who refers to them be useful in other areas as well?
750  * FIXME: would btree be faster than a hashtable? GTree could do this! I reckon it might be faster!
751  */
752
753 /*
754  * An entry is tracked in along with all of the other entries which have foreign keys pointing to
755  * it. These hold references on the entry, so to make sure it's freed when we have to remove them
756  * from the cache too. This also optimises retrieving from the cache, because when we query an entry
757  * we don't have to worry about whether its foreign keys are still valid.
758 451  *
759  *  FIXME: genericsource uses a much simpler system - it just zeroes foreign key properties of an
760  *  entry before it unrefs it. Couldn't we just do that?
761 274  */
762 typedef struct {
763         Entry *entry;
764
765         /* local_references stores properties in this entry that reference foreign entries. We need to
766          * store these so we can update the foreign entry's list of referencing entries when this entry
767 451          * changes to point to a different entry, or gets removed from the cache.
768          * FIXME: surely it would be just as easy to iterate through the entry's foreign key properties
769          *        each time we needed to know this. The extra time looking would probably balance out
770          *        with the time saved maintaining the linked list. */
771 274         GSList *local_references,
772
773         /* foreign_references is the entries with properties that are foreign keys on this entry. These
774          * need to be unreffed before this entry can be unreffed - or it won't be freed, the other
775          * entries will keep it in memory. */
776                *foreign_references;
777 } CacheEntry;
778
779 typedef struct {
780         int property_id;
781         CacheEntry *value;
782 } CacheEntryProperty;
783
784 451 static gint reference_list_find_cache_entry (gconstpointer a, gconstpointer b) {
785         const CacheEntryProperty *property = a;
786         const CacheEntry         *target   = b;
787         if (property->value == target) return 0;
788         return 1;
789 };
790
791 283 static void cache_remove_cache_entry (Library *self, CacheEntry *cache_entry, CacheEntry *caller);
792 274
793 /* cache_entry_remove_foreign: former_key_holder, which points to cache_entry, is being removed so
794  *                             should be taken out of cache_entry->foreign_references. */
795 static void cache_entry_remove_foreign (CacheEntry *cache_entry, CacheEntry *former_key_holder) {
796         g_return_if_fail (cache_entry!=NULL);
797
798         for (GSList *node=cache_entry->foreign_references; node; node=node->next) {
799                 CacheEntryProperty *cp = node->data;
800                 if (cp->value == former_key_holder) {
801                         g_slice_free (CacheEntryProperty, cp);
802                         cache_entry->foreign_references = g_slist_remove_link (cache_entry->foreign_references,
803                                                                                node);
804 280                         cache_trace ("removed %s %i from %s %i. %s's foreign list now %i [ref count %i].\n",
805 274                                      ENTRY_PF(former_key_holder->entry), ENTRY_PF(cache_entry->entry),
806 278                                      entry_type_name[cache_entry->entry->type],
807 280                                      g_slist_length(cache_entry->foreign_references),
808                                      cache_entry->entry->ref_count);
809 278
810 274                         return;
811                 };
812         };
813
814 278         g_warn_if_reached (); // Should have found former_key_holder in the foreign references, or we
815                               // have a cache inconsistency.
816 274 };
817
818 /* cache_entry_new: Wrap an entry in a CacheEntry * for insertion in the cache. */
819 static CacheEntry *cache_entry_new (Library *self, Entry *entry) {
820         CacheEntry *cache_entry = g_slice_new(CacheEntry);
821         cache_entry->entry = entry;
822         cache_entry->local_references = cache_entry->foreign_references = NULL;
823
824         for (int p=0; p<entry_n_properties[entry->type]; p++) {
825                 if (IS_FOREIGN_KEY(schema[entry->type][p].type)) {
826                         // This entry points to another. Look it up and register us with it. If it's not cached,
827                         // store it too.
828                         Entry *foreign_entry = entry_get_property(entry, p);
829                         if (foreign_entry==NULL) continue;
830
831                         CacheEntry *foreign_cache_entry = g_hash_table_lookup(SP->cache[foreign_entry->type],
832                                                                               &foreign_entry->id);
833 283                         if (foreign_cache_entry==NULL)
834                                 foreign_cache_entry = cache_store_entry (self, foreign_entry);
835 274
836 283                         //cache_trace ("!!! Storing foreign from %s %i to %s %i.\n", ENTRY_PF(entry), ENTRY_PF(foreign_entry));
837 274                         CacheEntryProperty *foreign_property = g_slice_new (CacheEntryProperty);
838                         foreign_property->property_id = p;
839                         foreign_property->value = cache_entry;
840                         foreign_cache_entry->foreign_references =
841                           g_slist_prepend(foreign_cache_entry->foreign_references, foreign_property);
842
843                         CacheEntryProperty *local_property = g_slice_new (CacheEntryProperty);
844                         local_property->property_id = p;
845 278                         local_property->value = foreign_cache_entry;
846 274                         cache_entry->local_references = g_slist_prepend(cache_entry->local_references,
847 278                                                                         local_property);
848                         //printf ("Storing for %s %i: %
849 274                 };
850         };
851
852         return cache_entry;
853 };
854
855 /* cache_entry_free: DestroyNotify for hash table. cache_entry should have all its references
856  *                   removed already (handled by cache_remove_entry). We can't do it here because
857  *                   we need a pointer to the whole cache. */
858 static void cache_entry_free (CacheEntry *cache_entry) {
859 278         // Don't check that ref_count is 1 - on checkins the entry by the caller isn't freed until after
860         // checking is all done. Let's just hope nobody keeps around entries that are owned by a source
861 274
862         // Entries that point to this one should already have been removed
863         g_warn_if_fail (cache_entry->foreign_references==NULL);
864
865         for (GSList *node=cache_entry->local_references; node; node=node->next)
866                 g_slice_free (CacheEntryProperty, node->data);
867         g_slist_free (cache_entry->local_references);
868
869         entry_unref (cache_entry->entry, "library cache");
870         g_slice_free (CacheEntry, cache_entry);
871 };
872
873 278 /* cache_entry_update: called when entry is checked in, in case it points to different entries than
874  *                     it used to .. */
875 // FIXME: I wonder if it would be quicker to just remove from cache on checkout and add again on
876 // checking back in ...
877 static void cache_entry_update (Library *self, CacheEntry *cache_entry) {
878 452         cache_trace ("cache_entry_update: %s %i [%x].\n", ENTRY_PF(cache_entry->entry),
879                      cache_entry->entry);
880 278         int find_by_property (CacheEntryProperty *property, int property_id) {
881                 if (property->property_id == property_id)
882                         return 0;
883                 return 1;
884         };
885
886         Entry *entry = cache_entry->entry;
887         for (int p=0; p<entry_n_properties[entry->type]; p++) {
888                 if (IS_FOREIGN_KEY(schema[entry->type][p].type)) {
889                         Entry *foreign_entry = entry_get_property(entry, p);
890
891                         GSList *node = g_slist_find_custom(cache_entry->local_references, GINT_TO_POINTER(p),
892                                                            (GCompareFunc)find_by_property);
893
894                         CacheEntryProperty *local_cp = NULL;
895                         if (node!=NULL) {
896                                 local_cp = node->data;
897                                 g_return_if_fail (local_cp->property_id==p);
898
899                                 // Remove us from reference list of old entry
900 280                                 if (foreign_entry!=local_cp->value->entry)
901 278                                         cache_entry_remove_foreign (local_cp->value, cache_entry);
902
903 280                                 if (foreign_entry==NULL) {
904                                         // Update local references, if now NULL
905 278                                         cache_entry->local_references =
906                                           g_slist_remove_link(cache_entry->local_references, node);
907                                 };
908 280                         } else {
909 283                                 // Property was NULL - it must be trivial, and if it's still NULL we have nothing to
910                                 // do.
911                                 //
912                                 // FIXME: there is a slightly confusing situation with shadow properties - on add,
913                                 //        this function can get called on an entry which hasn't had its shadow
914                                 //        properties filled yet. So for a property to be a NULL shadow property is
915                                 //        not (always) invalid.
916                                 // FIXME: track.release is allowed to be NULL for some unit tests but you could just
917                                 //        set a release for all of the tracks ....
918                                 g_return_if_fail ((schema[entry->type][p].flags & PROPERTY_TRIVIAL) ||
919                                                   (schema[entry->type][p].flags & PROPERTY_SHADOW) ||
920                                                                   (entry->type==ENTRY_TYPE_TRACK && p==TRACK_RELEASE));
921 280                                 if (foreign_entry==NULL) continue;
922                         };
923
924 452                         cache_trace ("Property: %s [%x] - old value %x, new %x.\n", schema[entry->type][p].name,
925 280                                      local_cp, local_cp==NULL?0:local_cp->value->entry, foreign_entry);
926
927                         if (local_cp==NULL || (foreign_entry!=local_cp->value->entry)) {
928                                 CacheEntry *foreign_cache_entry =
929                                   g_hash_table_lookup(SP->cache[foreign_entry->type], &foreign_entry->id);
930                                 if (foreign_cache_entry==NULL) cache_store_entry (self, foreign_entry);
931
932                                 // Add/update local references
933                                 if (local_cp==NULL) {
934                                         local_cp = g_slice_new(CacheEntryProperty);
935 283                                         cache_entry->local_references = g_slist_prepend(cache_entry->local_references,
936                                                                                         local_cp);
937 280                                         local_cp->property_id = p;
938                                 };
939                                 local_cp->value = foreign_cache_entry;
940
941                                 // Add us to reference list of new entry
942 283                                 //cache_trace ("cache_entry|update: Adding %s %i to foreign refs of %s %i.\n",
943                                 //             ENTRY_PF(cache_entry->entry), ENTRY_PF(foreign_cache_entry->entry));
944 280                                 CacheEntryProperty *foreign_cp = g_slice_new(CacheEntryProperty);
945                                 foreign_cp->property_id = p;
946                                 foreign_cp->value = cache_entry;
947                                 foreign_cache_entry->foreign_references =
948                                   g_slist_prepend(foreign_cache_entry->foreign_references, foreign_cp);
949 278                         };
950                 };
951         };
952 452
953         // Don't do a cache check here. This function gets called once per entry as entries are checked
954         // in, so the cache could be in an inconsistent state because it's in the middle of being
955         // updated.
956         //cache_check (self);
957 278 };
958
959 274
960 static void cache_init (Library *self) {
961         for (int i=0; i<ENTRY_TYPE_COUNT; i++)
962 273                 SP->cache[i] = g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free,
963 274                                                      (GDestroyNotify)cache_entry_free);
964 17 };
965
966 274 static void cache_free (Library *self) {
967         // This empties the cache - we can't just unref all the entries, we need to be cleverer because
968         // some entries point to each other etc.
969         cache_flush (self);
970 273
971 269         for (int i=0;i<ENTRY_TYPE_COUNT;i++)
972 274                 g_hash_table_unref (SP->cache[i]);
973 };
974
975 280 static Entry *cache_lookup_entry (Library *self, int entry_type, int entry_id) {
976 274         CacheEntry *cache_entry = g_hash_table_lookup(SP->cache[entry_type], &entry_id);
977
978         if (cache_entry!=NULL) {
979                 if (cache_entry->entry->id!=entry_id || cache_entry->entry->type!=entry_type)
980                         g_warning ("cache-lookup: cache inconsistency! entry listed %i (%X) type %s is %s %i\n",
981                                    entry_id, (guint)&entry_id, entry_type_name[entry_type],
982                                    ENTRY_PF(cache_entry->entry));
983 280                 else
984                         entry_ref (cache_entry->entry, "library::cache-lookup");
985 274
986 452                 cache_trace ("cache-lookup: returning %s %i [%x] from cache.\n",
987                              ENTRY_PF(cache_entry->entry), cache_entry->entry);
988 274                 return cache_entry->entry;
989         }
990
991         cache_trace ("cache-lookup: CACHE MISS on %s %i\n", entry_type_name[entry_type], entry_id);
992         return NULL;
993 };
994
995 279 static void cache_remove_cache_entry (Library *self, CacheEntry *cache_entry, CacheEntry *caller) {
996 280         g_return_if_fail (cache_entry!=NULL);
997
998 283         cache_trace ("cache_remove: %s %i [ refs %i ] - local %i, foreign %i.\n",
999                      ENTRY_PF(cache_entry->entry), cache_entry->entry->ref_count,
1000                      g_slist_length(cache_entry->local_references),
1001                      g_slist_length(cache_entry->foreign_references));
1002 274         // Remove ourself from foreign_references list of all the entries we point to.
1003 283         while (cache_entry->local_references) {
1004                 CacheEntryProperty *cp = cache_entry->local_references->data;
1005 280
1006 283                 //printf ("%s %i: Freeing ptr on to %s %i (local %i)\n", ENTRY_PF(cache_entry->entry),
1007                 //        ENTRY_PF(cp->value->entry), g_slist_length(cp->value->local_references)); fflush (stdout);
1008 280                 // This releases the reference the current entry holds on the foreign entry. Very important!
1009                 entry_set_property (cache_entry->entry, cp->property_id, NULL);
1010
1011 274                 cache_entry_remove_foreign (cp->value, cache_entry);
1012 283                 cache_entry->local_references = g_slist_remove_link(cache_entry->local_references,
1013                                                                     cache_entry->local_references);
1014 274         };
1015
1016 278         // Now, remove entries that point to us. remove_cache_entry removes the list node so we've no
1017         // need to iterate.
1018 283         //cache_trace ("cache_remove: %s %i has %i foreign references - ref count %i.\n",
1019         //             ENTRY_PF(cache_entry->entry), g_slist_length (cache_entry->foreign_references),
1020         //                       cache_entry->entry->ref_count);
1021 278         while (cache_entry->foreign_references) {
1022                 CacheEntryProperty *cp = cache_entry->foreign_references->data;
1023 280
1024 279                 if (cp->value!=caller)
1025                         cache_remove_cache_entry (self, cp->value, cache_entry);
1026 280                 else {
1027                         g_warn_if_fail (schema[cp->value->entry->type][cp->property_id].flags&PROPERTY_TRIVIAL);
1028                         cache_entry->foreign_references = g_slist_remove(cache_entry->foreign_references, cp);
1029 283                         g_slice_free (CacheEntryProperty, cp);
1030 280                 };
1031 274         };
1032
1033         gboolean found = g_hash_table_remove(SP->cache[cache_entry->entry->type],
1034                                              &cache_entry->entry->id);
1035         g_warn_if_fail (found);
1036 };
1037
1038 static void cache_remove_entry (Library *self, EntryType type, int id) {
1039         CacheEntry *cache_entry = g_hash_table_lookup(SP->cache[type], &id);
1040 280         if (cache_entry!=NULL)
1041                 cache_remove_cache_entry (self, cache_entry, NULL);
1042 274 };
1043
1044 283 static void *cache_store_entry (Library *self, Entry *entry) {
1045 274         cache_trace ("cache-store: storing %X %s %i in cache refs %i\n", entry, ENTRY_PF(entry),
1046                      entry->ref_count);
1047
1048 283         CacheEntry *cache_entry = g_hash_table_lookup(SP->cache[entry->type], &entry->id);
1049         if (cache_entry) {
1050 280                 cache_trace ("cache-store: needless call: already stored.\n");
1051 283                 return cache_entry;
1052 280         };
1053
1054 17         // FIXME: these numbers are pulled out of my ass, should do some statistical analysis on cache reads
1055         // FIXME: also, scale by amount of memory we can use for the cache.
1056         // As a reference, these numbers give a total footprint of about 36MB on my system.
1057 283         // Really it's good to have lots of space for entries that are used a lot, like artists, but
1058         // it's also good to have lots of space for entries we have lots of, like files ..
1059         // FIXME: if you ever make these configurable, below like 100 entries the cache is probably useless,
1060         // and below 10 worse than useless. It shouldn't crash however!
1061         // FIXME: There is no code to check if an entry tree is too big for the cache, which is a serious
1062         // corner case but not impossible!
1063 269         static const int cache_max[ENTRY_TYPE_COUNT]=
1064 17                 {       1024,   // Artist
1065                         512,    // Composition
1066                         512,    // Recording
1067                         0,              // FIXME table (I forget what this is even for)
1068 283                         512,    // File
1069 17                         256,    // Album
1070                         256,    // Release
1071 283                         512,    // Track
1072 17                 };
1073 269
1074         // Make sure we don't cane all of the memory.
1075 17         // FIXME: this could be done so much more cleverly by removing entries that are least accessed.
1076 283         g_warn_if_fail (cache_max[entry->type] > 1);
1077         if (g_hash_table_size(SP->cache[entry->type]) > cache_max[entry->type]-1) {
1078 17                 // FIXME: also, is there a faster way ? I'm doing this because I hope its the oldest entry that's
1079                 // returned first but I dunno really.
1080                 GHashTableIter iter; g_hash_table_iter_init(&iter, SP->cache[entry->type]);
1081 351                 void *pointer; CacheEntry *cache_entry;
1082                 g_hash_table_iter_next(&iter, NULL, &pointer); cache_entry = pointer;
1083 273
1084 351                 cache_trace ("cache-store: removing %X %s %i from cache\n", cache_entry,
1085                              ENTRY_PF(cache_entry->entry));
1086                 cache_remove_cache_entry (self, cache_entry, NULL);
1087 17         };
1088 283
1089         // Our reference should be "invisible" - so it's not the first reference removed by entry_unref
1090         entry_ref_invisible(entry, "library cache");
1091
1092         cache_entry = cache_entry_new(self, entry);
1093         int *key = g_new(int, 1); *key = entry->id;
1094         g_hash_table_insert(SP->cache[entry->type], key, cache_entry);
1095         cache_trace ("cache-store: stored %X %s %i in cache refs %i\n", entry, ENTRY_PF(entry),
1096                      entry->ref_count);
1097
1098 452         //entry_dump (entry);
1099 451
1100 283         return cache_entry;
1101 17 };
1102
1103 274
1104 451 /* cache_update_references: Called when entry merging is performed.
1105  *
1106  *   This can happen when entries are checked out, modified in such a way that they match another
1107  *   entry and checked back in. All foreign keys in cached entries that reference 'from' are changed
1108  *   to reference 'to'. */
1109 274 static void cache_change_references (Library *self, Entry *from, Entry *to) {
1110         g_return_if_fail (from!=NULL || to!=NULL);
1111 269         g_return_if_fail (from->type == to->type);
1112 274
1113 451         const EntryType type = from->type;
1114         cache_trace ("\ncache_change_references: updating all cache entries that point to %s %i to "
1115                      "point to %i\n", ENTRY_PF(from), to->id);
1116 274
1117         // If 'from' isn't cached, there's nothing to do - nothing that points to it should be cached
1118         // either.
1119         CacheEntry *from_cache_entry = g_hash_table_lookup (SP->cache[type], &from->id);
1120 451         if (from_cache_entry==NULL)
1121                 return;
1122 274
1123 451         // Cache 'to', so we have a CacheEntry object to store in reference lists.
1124 274         CacheEntry *to_cache_entry = g_hash_table_lookup (SP->cache[type], &to->id);
1125 451         if (to_cache_entry == NULL)
1126                 to_cache_entry = cache_store_entry (self, to);
1127 274
1128 451         int i = 0;
1129 274         for (GSList *node=from_cache_entry->foreign_references; node; node=node->next) {
1130                 CacheEntryProperty *cp = node->data;
1131                 CacheEntry *foreign_cache_entry = cp->value;
1132                 Entry *foreign_entry = foreign_cache_entry->entry;
1133
1134 451                 cache_trace ("\tupdating %s %i.\n", ENTRY_PF(foreign_entry));
1135 274                 g_warn_if_fail (entry_get_property(foreign_entry, cp->property_id)==from);
1136                 entry_set_property (foreign_entry, cp->property_id, to);
1137 451
1138                 // Update foreign_entry->local_references
1139                 GSList *rl_node = g_slist_find_custom (foreign_cache_entry->local_references,
1140                                                        from_cache_entry, reference_list_find_cache_entry);
1141                 g_return_if_fail (rl_node != NULL);
1142
1143                 CacheEntryProperty *foreign_cp = rl_node->data;
1144                 foreign_cp->value = to_cache_entry;
1145
1146                 // The CacheEntryProperty will be taken off 'from'; we now give it to 'to'. Recycling!
1147                 to_cache_entry->foreign_references = g_slist_prepend (to_cache_entry->foreign_references,
1148                                                                       cp);
1149
1150                 #ifdef LIBRARY_TRACE_CACHE
1151                 i++;
1152                 #endif
1153 269         };
1154 274
1155 451         g_slist_free (from_cache_entry->foreign_references);
1156         from_cache_entry->foreign_references = NULL;
1157  
1158         cache_trace ("...updated %i entries.\n\n", i);
1159         cache_check (self);
1160 269 };
1161
1162 95 static void cache_flush (Library *self) {
1163 132         #ifndef LIBRARY_DISABLE_CACHE
1164 278         cache_trace ("\ncache_flush ()\n");
1165 274         for (int i=0; i<ENTRY_TYPE_COUNT; i++) {
1166 351                 void *pointer; CacheEntry *cache_entry; GHashTableIter iter;
1167 278                 // FIXME: nasty way of doing things! Can't iterate though because arbitrary entries are
1168                 // being removed from the hash table :( .. would this be a problem  with a BTRee?
1169                 do {
1170                         g_hash_table_iter_init (&iter, SP->cache[i]);
1171 351                         if (!g_hash_table_iter_next(&iter, NULL, &pointer))
1172 278                                 break;
1173 351                         cache_entry = pointer;
1174 279                         cache_remove_cache_entry (self, cache_entry, NULL);
1175 278                 } while (1);
1176 274         };
1177 95         #endif
1178 };
1179 269
1180 451 static void cache_check (Library *self) {
1181         #if !defined(LIBRARY_DISABLE_CACHE) && defined(LIBRARY_CHECK_CACHE)
1182         for (int i=0; i<ENTRY_TYPE_COUNT; i++) {
1183                 void *pointer; GHashTableIter iter;
1184
1185                 g_hash_table_iter_init (&iter, SP->cache[i]);
1186                 while (g_hash_table_iter_next(&iter, NULL, &pointer)) {
1187                         CacheEntry *cache_entry = pointer;
1188                         Entry      *entry       = cache_entry->entry;
1189                         entry_check (entry);
1190
1191                         // Check local references. FIXME: the simplicity of this test kind of highlights the
1192                         // pointlessness of the local references list.
1193                         int local_reference_count = 0;
1194                         for (int p=0; p < entry_n_properties[entry->type]; p++) {
1195                                 if (!IS_FOREIGN_KEY(schema[entry->type][p].type))
1196                                         continue;
1197
1198                                 Entry      *foreign_entry       = entry_get_property (entry, p);
1199                                 CacheEntry *foreign_cache_entry = g_hash_table_lookup(SP->cache[foreign_entry->type],
1200                                                                                       &foreign_entry->id);
1201                                 g_assert (foreign_cache_entry != NULL);
1202
1203                                 // Check that we hold a cache entry for it.
1204                                 if (g_slist_find_custom (cache_entry->local_references, foreign_cache_entry,
1205                                                          reference_list_find_cache_entry) == NULL)
1206                                         g_warning ("cache_check: %s %i: local_references is missing %s %i.",
1207                                                    ENTRY_PF(entry), ENTRY_PF(foreign_entry));
1208
1209                                 // Check that it holds a cache entry for us.
1210                                 if (g_slist_find_custom (foreign_cache_entry->foreign_references, cache_entry,
1211                                                          reference_list_find_cache_entry) == NULL)
1212                                         g_warning ("cache_check: %s %i: we point to %s %i, but it doesn't point back.",
1213                                                    ENTRY_PF(entry), ENTRY_PF(foreign_entry));
1214
1215                                 local_reference_count ++;
1216                         }
1217                         g_assert_cmpint (g_slist_length (cache_entry->local_references), ==, local_reference_count);
1218
1219                         // Check foreign references. For each foreign cache entry, make sure it actually points to
1220                         // this one.
1221                         for (GSList *node=cache_entry->foreign_references; node; node=node->next) {
1222                                 CacheEntryProperty *foreign_property = node->data;
1223                                 CacheEntry *foreign_cache_entry      = foreign_property->value;
1224                                 Entry *foreign_entry                 = foreign_cache_entry->entry;
1225
1226                                 if (entry_get_property (foreign_entry, foreign_property->property_id) != entry)
1227                                         g_warning ("cache_check: %s %i: foreign_references stores %s %i, but this does "
1228                                                    "not reference the entry.", ENTRY_PF(entry), ENTRY_PF(foreign_entry));
1229                         }
1230                 }
1231         }
1232         #endif
1233 };
1234
1235 /*************************************************************************************************
1236  * Querying - internal functions.
1237  */
1238 17
1239 // FIXME: these functions are very similar to the ones below. I'm willing to
1240 // have some duplication though to keep things faster. Would be nice to merge
1241 // more though ..
1242 // FIXME: these functions could do with some tidying and organising
1243
1244 304
1245 452 static char *make_property_string_from_value(Library *self, PropertyType type, void *value) {
1246         switch (type) {
1247 17                 case PROPERTY_TYPE_INT:
1248 452                         return g_strdup_printf("%i", (int)value);
1249 17
1250                 case PROPERTY_TYPE_FLOAT: {
1251                         //printf("Need to convert to float: %X", entry->property[property]);fflush(stdout);
1252 452                         float *float_value=(float *)value;
1253                         if (float_value==NULL)
1254                                 return g_strdup("0.0");
1255                         return g_strdup_printf("%g", *float_value);
1256                 };
1257
1258                 case PROPERTY_TYPE_STRING: case PROPERTY_TYPE_NAME: {
1259 53                         if (value==NULL)
1260 17                                 return NULL;
1261 269
1262 452                         char *sqlite_string=sqlite3_mprintf("%Q", (const char *)value);
1263                         char *value_string=g_strdup(sqlite_string);
1264 17                         sqlite3_free(sqlite_string);
1265 452                         return value_string;
1266 17                 };
1267 269
1268 17                 case PROPERTY_TYPE_DATE: {
1269 452                         if (value==NULL || !g_date_valid(value))
1270 17                                 return NULL;
1271 452                         char *date_string=g_malloc(16);
1272                         g_date_strftime(value, 15, "'%Y-%m-%d'", (GDate *)value);
1273                         return date_string;
1274 17                 };
1275 269
1276 17                 case PROPERTY_TYPE_DATE_TIME: {
1277 452                         time_t *time_value = value;
1278 283                         if (time_value==NULL) return NULL;
1279                         struct tm brokentime; localtime_r(time_value, &brokentime);
1280 452                         char *date_time_string = g_malloc(32);
1281                         strftime (date_time_string, 31, "'%Y-%m-%d %H:%M:%S'", &brokentime);
1282                         return date_time_string;
1283 17                 }
1284 269
1285 17                 default: {
1286 452                         Entry *child = value;
1287 304
1288 17                         // This value can't be entered in the query at all if it is 0, because if the id
1289                         // has a value of 0 instead of NULL all our COALESCE calls no longer work
1290                         if (child==NULL) return NULL;
1291 304
1292 17                         int entry_id;
1293                         if (child->id!=0 && child->source==self)
1294                                 entry_id=child->id;
1295 452                         else entry_id = _music_source_find_entry(MUSIC_SOURCE(self), child, child);
1296 304
1297 17                         return g_strdup_printf("%i", entry_id);
1298                 };
1299         };
1300 };
1301
1302 452 static char *make_property_string (Library *self, Entry *entry, int property) {
1303         return make_property_string_from_value (self, schema[entry->type][property].type,
1304                                                 entry->property[property]);
1305 };
1306 17
1307 269 // At the moment this makes the whole query (property=value) whereas
1308 17 // make_property_string just returns a value string, this is because where
1309 // entries are concerned we may need to match more than one ! make_property_string
1310 // could change easily though.
1311 // FIXME: I also don't like the names.
1312 // Note this should not return the given entry!
1313 //
1314 static char *make_property_match_string_except(Library *self, Entry *entry, int property, int ignored_apid) {
1315 269 // Builds a SQL query string to find matching entries, ignoring a certain property. This is used for example
1316 449 // (via music_source_query_matching_except) in music_source::track_added_callback
1317 17 // where we find all releases that match on everything but album artist.
1318 //
1319         if (entry->type==APID_GET_TYPE(ignored_apid) && property==APID_GET_PROPERTY_ID(ignored_apid)) return NULL;
1320         const char *property_name=schema[entry->type][property].name;
1321 269
1322 17         switch(schema[entry->type][property].type) {
1323                 case PROPERTY_TYPE_INT:
1324                         return g_strdup_printf("%s=%i", property_name, (int)entry->property[property]);
1325
1326                 case PROPERTY_TYPE_FLOAT:
1327                         //printf("Need to convert to float: %X", entry->property[property]);fflush(stdout);
1328 52                         return g_strdup_printf("%s=%g", property_name, *(float *)(entry->property[property]));
1329 17
1330                 case PROPERTY_TYPE_STRING: case PROPERTY_TYPE_NAME: {
1331                         if (entry->property[property]==NULL)
1332 269                                 return NULL;
1333                         char *sqlite_string=sqlite3_mprintf("%s=%Q", property_name, (const char *)entry->property[property]);
1334 17                         // FIXME: this is silly, should copy sqlite3_mprintf and rewrite using glib allocators. I'm sure they
1335                         // all boil down to libc anyway but it's best to be safe.
1336 269                         char *value=g_strdup(sqlite_string); sqlite3_free(sqlite_string);
1337 17                         return value;
1338                 };
1339 269
1340 17                 case PROPERTY_TYPE_DATE: {
1341 269                         if (entry->property[property]==NULL || !g_date_valid(entry->property[property]))
1342 17                                 return NULL;
1343                         char value[16];
1344                         g_date_strftime(value, 15, "%Y-%m-%d", (GDate *)entry->property[property]);
1345                         return g_strdup_printf("%s='%s'", property_name, value);
1346 269                 };
1347
1348 17                 case PROPERTY_TYPE_DATE_TIME: {
1349                         if (entry->property[property]==NULL) return NULL;
1350                         struct tm brokentime; localtime_r((time_t*)entry->property[property], &brokentime);
1351                         char value[32];
1352                         strftime(value, 31, "%Y-%m-%d %H:%M:%S", &brokentime);
1353                         return g_strdup_printf("%s='%s'", property_name, value);
1354 269                 };
1355
1356 17                 default: {
1357                         Entry *child=entry->property[property];
1358 269                         if (child==NULL) return NULL;
1359
1360 17                         GSList *matching_entry_list=find_entries_matching_except(self, child, ignored_apid, entry);
1361                         if (matching_entry_list==NULL) return NULL;
1362 269
1363 17                         GString *match_string=g_string_new(NULL);
1364                         for (GSList *node=matching_entry_list;node;node=node->next)
1365                                 g_string_append_printf(match_string, "OR %s_id=%i ", property_name, (int)node->data);
1366                         g_slist_free(matching_entry_list);
1367                         match_string->str[0]='('; match_string->str[1]=' '; match_string->str[match_string->len-1]=')';
1368                         return g_string_free(match_string, FALSE);
1369                 };
1370         };
1371 };
1372
1373 168 // FIXME: can we precalculate these queries?
1374 451 static int find_entry (MusicSource *music_source, Entry *entry, Entry *caller) {
1375         Library *self = LIBRARY(music_source);
1376 269         GString *clauses = g_string_new(NULL),
1377 97                 *joins = g_string_new(NULL);
1378         gboolean join_flags[ENTRY_TYPE_COUNT] = {0};
1379 269
1380 17         // FIXME: at the moment, if we search for a child entry which isn't found, find_entry will
1381         // return 0 for id's and this function will generate a query for an id of 0 which will
1382         // fail. However it would be nice to fail straight away if an id of 0 is returned for
1383         // any related entries.
1384 97         for (int i=0; i<entry_n_properties[entry->type]; i++) {
1385 285                 if (!(schema[entry->type][i].flags & PROPERTY_MERGEABLE) &&
1386                     !(schema[entry->type][i].flags & PROPERTY_TRIVIAL)) {
1387 269                     char *property_value_string = NULL;
1388 17                         if (caller!=NULL && schema[entry->type][i].type==caller->type) {
1389                                 // This is where we have an entry that points back up to the calling type.
1390                                 // If it has an id we can simply use that. If not we have to ignore it
1391                                 // pretty much.
1392 97                                 Entry *child = entry_get_property(entry, i);
1393 269                                 if (child!=NULL && child->id!=0)
1394 97                                         property_value_string = g_strdup_printf("%i", child->id);
1395 17                         } else
1396 269                                 property_value_string = make_property_string(self, entry, i/*, find_entry*/);
1397
1398 97                         if (property_value_string!=NULL) {
1399                                 g_string_append (clauses, " AND");
1400 104                                 _db_append_property_query (entry->type, i, clauses, FALSE, joins, join_flags);
1401 269                                 g_string_append_printf (clauses, "=%s", property_value_string);
1402 97                                 g_free (property_value_string);
1403 17                         };
1404                 };
1405         };
1406 269
1407         if (clauses->len>0) {
1408 97                 // Remove initial ' AND'
1409                 clauses->str[0] = clauses->str[1] = clauses->str[2] = clauses->str[3] = ' ';
1410 17         };
1411 269
1412 97         // FIXME: these query formats could actually be largely pre-generated, which would speed
1413         // up imports a bit ..
1414 269         char *query = g_strdup_printf("SELECT _%s.id FROM _%s %s %s %s",
1415                                       entry_type_name[entry->type], entry_type_name[entry->type],
1416 97                                                                   joins->str, clauses->len>0?"WHERE":"", clauses->str);
1417 269
1418 17         char **data; int rows, columns;
1419 97         data = db_query(self, &rows, &columns, NULL, query);
1420         method_track_printf ("find-entry: %s - %i %i\n", query, rows, columns);
1421 269
1422         if (rows==0 || data==NULL)
1423                 return 0;
1424 17         if (rows>1) g_warning("Multiple rows found for query %s", query);
1425 269
1426 97         int id = atoi(data[columns]);
1427 269
1428 17         sqlite3_free_table(data);
1429         g_free(query);
1430 97         g_string_free(joins, TRUE); g_string_free(clauses, TRUE);
1431 269
1432 17         return id;
1433 };
1434
1435
1436 static GSList *find_entries_matching_except(Library *self, Entry *entry, int ignored_apid, Entry *caller) {
1437 449 // Finds matching entries, ignoring a certain property. This is used for example is music_source::track_added_callback
1438 17 // where we find all releases that match on everything but album artist.
1439 //
1440         //printf("\tfind-entry: %s %i %X %i\n", entry_type_name[entry->type], entry->id, entry, entry->ref_count);fflush(stdout);
1441         GString *clauses=g_string_new(NULL);
1442         // FIXME: at the moment, if we search for a child entry which isn't found, find_entry will
1443         // return 0 for id's and this function will generate a query for an id of 0 which will
1444         // fail. However it would be nice to fail straight away if an id of 0 is returned for
1445         // any related entries.
1446         for (int i=0;i<entry_n_properties[entry->type];i++) {
1447                 if (!(schema[entry->type][i].flags & PROPERTY_MERGEABLE) && !(schema[entry->type][i].flags & PROPERTY_TRIVIAL)) {
1448                     char *string=NULL;
1449                         if (caller!=NULL && schema[entry->type][i].type==caller->type) {
1450                                 // FIXME: we don't do anything with backpointers, should we ?
1451                         } else
1452 269                                 string=make_property_match_string_except(self, entry, i, ignored_apid);
1453 17                         if (string!=NULL) {
1454                                 g_string_append_printf(clauses, " AND %s", string);
1455                                 g_free(string);
1456                         };
1457                 };
1458         };
1459 269
1460 17         if (clauses->len>0) {
1461                 clauses->str[0]=clauses->str[1]=clauses->str[2]=clauses->str[3]=' ';
1462         };
1463 269
1464         char *query=g_strdup_printf("SELECT id FROM _%s WHERE %s", entry_type_name[entry->type], clauses->str);
1465 17         char **data; int rows, columns; data=db_query(self, &rows, &columns, NULL, query);
1466         method_track_printf("find-matching-entries-except: %s - %i\n", query, rows);fflush(stdout);
1467 269
1468 17         GSList *result_list=NULL;
1469         if (data!=NULL) {
1470 269                 for (int i=0;i<rows;i++)
1471 17                         result_list=g_slist_prepend(result_list, (void *)atoi(data[(i+1)*columns]));
1472                 sqlite3_free_table(data);
1473         };
1474         g_free(query); g_string_free(clauses, TRUE);
1475 269
1476 17         return result_list;
1477 };
1478
1479 128 // This function doesn't check the cache, so make sure you have called cache_lookup before
1480 // you call this! In fact, you should really use query_entry.
1481 //
1482 304 // FIXME: is query_sql parameter used at all any more?
1483 static Entry *fill_entry (Library *self, int entry_type, int entry_id, Entry *caller) {
1484         method_track_enter ("library::fill-entry - _%s %i\n", entry_type_name[entry_type], entry_id);
1485 17         Entry *entry;
1486 269
1487 17         char *name;
1488 130         #ifdef ENTRY_TRACK_REFERENCE_OWNERS
1489 128         // FIXME: this is leaked - who should own entry reference strings, in fact?
1490 269         if (caller!=NULL)
1491 304                 name = g_strdup_printf("library::fill-entry [caller %s %i]", ENTRY_PF(caller));
1492 128         else name = "library::fill-entry";
1493         #endif
1494 95         entry = entry_new(entry_type, entry_id, MUSIC_SOURCE(self), name);
1495 17
1496 164         sqlite3_stmt *query = SP->fill_entry_query[entry_type];
1497         sqlite3_bind_int (query, 1, entry_id);
1498 269         int result = sqlite3_step (query);
1499 17
1500 452         #ifdef LIBRARY_TRACE_QUERIES
1501                 printf("=> * %s\n", sqlite3_sql(query)); fflush(stdout);
1502         #endif
1503
1504 164         if (result==SQLITE_DONE) {
1505                 sqlite3_reset (query);
1506 128                 method_track_leave ("done library::fill-entry - entry not found\n");
1507                 entry_unref (entry, name);
1508 17                 return NULL;
1509         };
1510 285         //printf ("%s.\n", sqlite3_sql(query)); fflush (stdout);
1511 269
1512 164         g_return_val_if_fail (result == SQLITE_ROW, NULL);
1513 17         //method_track_printf("here library::fill-entry - %s %i, %s\n", entry_type_name[entry->type], entry->id, query);
1514 269
1515 17         // Sqlite returns column names as the first row so this seeks to the end of that. If
1516         // we requested * (generic query) there will also be the id column to skip.
1517 164         //if (query==NULL) pos+=1;
1518 269
1519 286         int child_id_store[entry_n_properties[entry_type]];
1520
1521 164         if (sqlite3_column_count(query)!=entry_n_properties[entry_type]+1) {
1522                 g_warning ("library: Db query returned wrong number results, cols %i props %i - on %s %i\n"
1523 269                                    "This could well mean your database is out of date and the schema has changed.\n",
1524                                    sqlite3_column_count(query), entry_n_properties[entry_type],
1525 164                                    entry_type_name[entry->type], entry->id);
1526 286         } else {
1527                 g_warn_if_fail (sqlite3_column_int(query, 0) == entry_id);
1528
1529                 for (int p=0; p<entry_n_properties[entry_type]; p++) {
1530                         // if (filter!=NULL) printf("Value for current type: %i - bit %i=%i for prop %i\n",
1531                         //  filter[entry->type], 1<<i, filter[entry->type]&(1<<i), i);
1532                         //printf("Parsing property %i - %s\n", prop_id, schema[entry_type][prop_id].name);fflush(stdout);
1533
1534                         const int column = p+1, column_type = sqlite3_column_type(query, column);
1535                         const EntryProperty *property = &schema[entry_type][p];
1536
1537                         switch(property->type) {
1538                                 case PROPERTY_TYPE_INT:
1539                                         entry->property[p] = (void *)sqlite3_column_int(query, column);
1540                                         break;
1541
1542                                 case PROPERTY_TYPE_FLOAT:
1543                                         if (sqlite3_column_type(query, column)!=SQLITE_NULL) {
1544                                                 float *x = g_slice_new0 (float);
1545                                                 *x = sqlite3_column_double(query, column);
1546                                                 entry->property[p] = x;
1547                                         };
1548                                         break;
1549
1550                                 case PROPERTY_TYPE_STRING: case PROPERTY_TYPE_NAME:
1551                                         if (!(property->flags & PROPERTY_NOT_NULL) && column_type==SQLITE_NULL)
1552                                                 break;
1553                                         g_warn_if_fail (column_type==SQLITE_TEXT);
1554
1555 288                                         const guchar *value = sqlite3_column_text(query, column);
1556                                         g_warn_if_fail (value!=NULL);
1557                                         entry->property[p] = (void *)g_strdup((const gchar *)value);
1558 286                                         break;
1559
1560                                 case PROPERTY_TYPE_DATE:
1561                                         if (sqlite3_column_type(query, column)!=SQLITE_NULL) {
1562                                                 //printf ("Query %s\nColumn %i, type %i.\n", query->sql, p
1563                                                 entry->property[p] = g_date_new();
1564 288                                                 const guchar *text = sqlite3_column_text(query, column);
1565 286                                                 g_return_val_if_fail (text!=NULL, NULL);
1566                                                 // FIXME: this isn't brilliant
1567 288                                                 g_date_set_parse ((GDate *)entry->property[p], (const gchar *)text);
1568 286                                         }
1569                                         break;
1570
1571                                 case PROPERTY_TYPE_DATE_TIME: {
1572                                         if (sqlite3_column_type(query, column)!=SQLITE_NULL) {
1573 288                                                 const guchar *datetime = sqlite3_column_text(query, column);
1574 286                                                 // Catch some invalid results we get for some reason.
1575 288                                                 if (datetime!=NULL && strlen((const gchar *)datetime) > 1) {
1576 286                                                         struct tm brokentime;
1577                                                         #ifdef G_OS_WIN32
1578                                                                 // strptime is not available on mingw. This should be faster anyway, although dodgier
1579                                                                 int Y, m, d, H, M, S;
1580                                                                 sscanf (datetime, "%u-%u-%u %u:%u:%u",
1581                                                                                 &Y, &m, &d, &H, &M, &S);
1582                                                                 memset (&brokentime, 0, sizeof(brokentime));
1583                                                                 brokentime.tm_sec = S; brokentime.tm_min = M; brokentime.tm_hour = H;
1584                                                                 brokentime.tm_mday = d; brokentime.tm_mon = m-1; brokentime.tm_year = Y-1900;
1585                                                         #else
1586 350                                                                 // Need to cast for signedness.
1587 351                                                                 strptime ((const gchar *)datetime, "%Y-%m-%d %H:%M:%S",
1588 350                                                                           &brokentime);
1589 286                                                         #endif
1590                                                         time_t *tp = entry->property[p] = g_slice_new(time_t);
1591                                                         *tp = mktime(&brokentime);
1592                                                 }
1593                                         };
1594                                         break;
1595                                 };
1596
1597 288                                 default:
1598 286                                         child_id_store[p] = sqlite3_column_int(query, column);
1599 288                                         break;
1600 286                         };
1601                 };
1602         };
1603
1604         sqlite3_reset (query);
1605 269
1606 128         for (int p=0; p<entry_n_properties[entry_type]; p++) {
1607 286                 if (IS_FOREIGN_KEY(schema[entry_type][p].type)) {
1608                         const int child_id = child_id_store[p];
1609                         if (caller!=NULL && schema[entry_type][p].type==caller->type && child_id==caller->id) {
1610                                 // Avoid back pointers
1611                                 entry_set_property (entry, p, caller); break;
1612                         } else if (child_id!=0) {
1613                                 // Don't use cache_lookup to avoid getting another reference
1614                                 CacheEntry *child_cache_entry = NULL;
1615
1616                                 #ifndef LIBRARY_DISABLE_CACHE
1617                                 child_cache_entry = g_hash_table_lookup
1618                                                                           (SP->cache[schema[entry->type][p].type], &child_id);
1619                                 #endif
1620
1621                                 if (child_cache_entry==NULL) {
1622 304                                         Entry *child_entry = fill_entry(self, schema[entry->type][p].type, child_id,
1623                                                                         entry);
1624 286                                         entry_take_property (entry, p, child_entry);
1625                                 } else if (entry_get_property(entry, p)!=child_cache_entry->entry)
1626                                         entry_set_property (entry, p, child_cache_entry->entry);
1627                         }
1628 17                 };
1629         };
1630 269
1631 17         //method_track_printf("here library::fill-entry - %s %i, %s\n", entry_type_name[entry->type], entry->id, query);
1632
1633 132         #ifndef LIBRARY_DISABLE_CACHE
1634 274         cache_store_entry (self, entry);
1635 17         #endif
1636
1637 128         method_track_leave ("done library::fill-entry - %s %i, %s\n", entry_type_name[entry->type], entry->id, query);
1638 17         return entry;
1639 };
1640
1641 93 // Convert a query result into the entries it represents.
1642 269 static GSList *_query_entries (Library *self, EntryType entry_type, char **data, int rows,
1643 304                                int columns) {
1644 93         GSList *list = NULL;
1645 269
1646 452         for (int i=0; i<rows; i++) {
1647                 Entry     *entry    = NULL;
1648                 const int  pos      = (i+1) * columns;
1649                 const int  entry_id = atoi (data[pos]);
1650                 
1651                 entry = cache_lookup_entry(self, entry_type, entry_id);
1652                 if (entry == NULL)
1653                         entry = fill_entry (self, entry_type, entry_id, NULL);
1654 93                 list = g_slist_prepend(list, entry);
1655 17         }
1656 269
1657         // FIXME: do we need to do this ? Or could sql return the results sorted
1658 17         // backwards if order matters ?? Would be quicker to return an array instead
1659 269         // of a list anyway.
1660 93         list = g_slist_reverse(list);
1661 17         return list;
1662 };
1663
1664
1665 451 /*************************************************************************************************
1666  * Querying - MusicSource interface
1667  */
1668 17
1669 // FIXME: make all of this faster !!
1670 static gboolean is_empty(MusicSource *source) {
1671         return FALSE;
1672         //return !(db_expression(LIBRARY(source), NULL, "SELECT COUNT(id) FROM recording"));
1673 269 };
1674 17
1675 static gboolean get_loaded(MusicSource *source) {
1676         // Library just needs to connect to the db so it's "loaded" straight away.
1677         // FIXME: it never emits MusicSource::loaded which could cause problems in
1678         // the future
1679         // FIXME: the whole 'loaded' thing is actually pretty pointless these days.
1680         return TRUE;
1681 };
1682
1683 449 static char *get_summary(MusicSource *music_source) {
1684         Library *self=LIBRARY(music_source);
1685 175         int files=db_expression(self, NULL, "SELECT COUNT(id) FROM _file");
1686 17         // FIXME: due to the screwy deletion code at the moment it is possible to
1687         // have lots of orphan files, this prevents crash !(when there are files but no recordings I think)
1688 175         int recordings=db_expression(self, NULL, "SELECT COUNT(id) FROM _recording");
1689 269
1690         char *msg;
1691 17         if (files && recordings) {
1692 175                 gint64 size=db_expression(self, NULL, "SELECT SUM(size) FROM _file WHERE size>0");
1693 17                 char *size_str=format_bytes(size);
1694 269
1695 175                 int duration=db_expression(self, NULL, "SELECT SUM(length) FROM _recording WHERE length>0");
1696 17                 char *duration_str=format_seconds_literal(duration);
1697 269
1698 17                 msg=g_strdup_printf("%i files: %s in %s", files, duration_str, size_str);
1699                 g_free(duration_str); g_free(size_str);
1700         } else msg=g_strdup("No files");
1701         return msg;
1702 };
1703
1704 // FIXME: would be faster to track dead & highest ourself. Only a little though,
1705 449 // the main benefit is this code could go in music_source cos it would be the
1706 17 // same as importing's.
1707 168 // FIXME: precalculate these fellows (for a tiny exec time saving :)
1708 449 static int get_n_entries (MusicSource *music_source, EntryType type, int *highest_id, int *n_dead) {
1709         int _highest_id = db_expression_printf(LIBRARY(music_source), NULL,
1710 168                                                "SELECT MAX(id) FROM _%s", entry_type_name[type]);
1711 449         int n_entries = db_expression_printf(LIBRARY(music_source), NULL,
1712 168                                              "SELECT COUNT(id) FROM _%s", entry_type_name[type]);
1713         if (highest_id!=NULL) *highest_id = _highest_id;
1714         if (n_dead!=NULL) *n_dead = _highest_id - n_entries;
1715 17         return n_entries;
1716 269 };
1717 17
1718 168 static gboolean is_valid_id (MusicSource *source, EntryType type, int id) {
1719 17         // FIXME: is there a faster way to do this ??
1720         int rows, columns;
1721 269         char **data = db_query_printf(LIBRARY(source), &rows, &columns, NULL,
1722 168                                       "SELECT id FROM _%s WHERE id=%i", entry_type_name[type], id);
1723         sqlite3_free_table (data);
1724 17         if (rows==0) return FALSE;
1725 269         return TRUE;
1726 17 };
1727
1728 static Entry *query_entry(MusicSource *source, EntryType type, int id) {
1729 280         Entry *entry = cache_lookup_entry(LIBRARY(source), type, id);
1730 128         if (entry==NULL)
1731 304                 entry = fill_entry(LIBRARY(source), type, id, NULL);
1732 280         //entry_dump (entry);
1733 128         return entry;
1734 17 };
1735
1736 269 static int query_n_relations (MusicSource *music_source, int local_id, EntryType foreign_type,
1737 102                               int foreign_property_id, int limit) {
1738         return db_expression_printf(LIBRARY(music_source), NULL,
1739 168                                     "SELECT COUNT(_%s.id) FROM _%s WHERE _%s.%s_id=%i LIMIT %i",
1740 269                                     entry_type_name[foreign_type], entry_type_name[foreign_type],
1741                                     entry_type_name[foreign_type],
1742 168                                     schema[foreign_type][foreign_property_id].name, local_id,
1743                                     limit);
1744 102 };
1745
1746 109 // Return list of entries of child_entry_type where child_property = parent_id
1747 // FIXME: query_relations could do this.
1748 449 static GSList *query_entry_children (MusicSource *music_source, int parent_id,
1749 109                                      EntryType child_entry_type, int child_property) {
1750 17         int rows, columns; char **data;
1751 109         GSList *list = NULL;
1752 449         data = db_query_printf(LIBRARY(music_source), &rows, &columns, NULL,
1753 269                                "SELECT id FROM _%s WHERE %s_id=%i", entry_type_name[child_entry_type],
1754 109                                                    schema[child_entry_type][child_property].name, parent_id);
1755 449         list = _query_entries(LIBRARY(music_source), child_entry_type, data, rows, columns);
1756 109         sqlite3_free_table (data);
1757 17         return list;
1758 };
1759
1760 449 static GSList *query_entry_children_ids(MusicSource *music_source, int parent_id, EntryType child_entry_type, int child_property) {
1761 17         int rows, columns; char **data;
1762         GSList *list=NULL;
1763 449         data = db_query_printf(LIBRARY(music_source), &rows, &columns, NULL,
1764 269                                "SELECT id FROM _%s WHERE %s_id=%i", entry_type_name[child_entry_type],
1765 168                                                    schema[child_entry_type][child_property].name, parent_id);
1766 269
1767 17         if (data==NULL) {
1768                 g_warning("library::query_entry_children_ids: finding all %s for %s %i returned NULL data", entry_type_name[child_entry_type], entry_type_name[schema[child_entry_type][child_property].type], parent_id);
1769                 return NULL;
1770         };
1771 269
1772 17         for (int i=0;i<rows;i++) {
1773                 int pos=(i+1)*columns; list=g_slist_prepend(list, (void *)atoi(data[pos]));
1774         }
1775 269
1776         // FIXME: do we need to do this ? Or could sql return the results sorted
1777 17         // backwards if order matters ?? Would be quicker to return an array instead
1778 269         // of a list anyway.
1779         list=g_slist_reverse(list);
1780 17         sqlite3_free_table(data);
1781         return list;
1782 };
1783
1784 269 static GSList *query_relations (MusicSource *music_source, EntryType local_type,
1785 92                                 int local_id, int relation_apid) {
1786         int rows, columns; char **data; GSList *list = NULL;
1787 269
1788
1789 92         EntryType foreign_type;
1790         if (APID_GET_TYPE(relation_apid)==local_type) {
1791                 // * All entries that local refers to in a property:
1792                 foreign_type = APID_GET_PROPERTY(relation_apid).type;
1793 269                 data = db_query_printf(LIBRARY(music_source), &rows, &columns, NULL,
1794 280                                        "SELECT _%s.id FROM _%s INNER JOIN _%s ON _%s.%s_id=_%s.id "
1795                                        "WHERE _%s.id=%i", entry_type_name[foreign_type],
1796                                        entry_type_name[local_type], entry_type_name[foreign_type],
1797                                        entry_type_name[local_type], APID_GET_PROPERTY(relation_apid).name,
1798                                        entry_type_name[foreign_type], entry_type_name[local_type],
1799                                        local_id);
1800 269
1801 92         } else if (APID_GET_PROPERTY(relation_apid).type==local_type) {
1802                 // * All entries that refer to local in a property
1803 269                 foreign_type = APID_GET_TYPE(relation_apid);
1804                 data = db_query_printf(LIBRARY(music_source), &rows, &columns, NULL,
1805 168                                                            "SELECT _%s.id FROM _%s INNER JOIN _%s ON _%s.%s_id=_%s.id WHERE _%s.id=%i",
1806 92                                                            entry_type_name[APID_GET_TYPE(relation_apid)],
1807                                                            entry_type_name[APID_GET_TYPE(relation_apid)],
1808                                                            entry_type_name[local_type],
1809                                                            entry_type_name[APID_GET_TYPE(relation_apid)],
1810                                                            APID_GET_PROPERTY(relation_apid).name,
1811 93                                                            entry_type_name[local_type],
1812 92                                                            entry_type_name[local_type], local_id);
1813 269
1814 92         } else
1815                 g_return_val_if_reached(NULL);
1816 269
1817 304         list = _query_entries(LIBRARY(music_source), foreign_type, data, rows, columns);
1818 92         sqlite3_free_table (data);
1819         return list;
1820 };
1821
1822 17 // FIXME: there is a much faster way to do this ! It's called iterate through id's and call is_valid_id!!!! Why don't we remove this call
1823 // (maybe make the specific queries into a different function) and delete this !
1824 // Of course, I don't know if is_valid_id would be actually be faster in its present state.
1825 static GSList *query_ids(MusicSource *source, EntryType entry_type) {
1826         int rows, columns; char **data;
1827         GSList *list=NULL;
1828 269
1829         data = db_query_printf(LIBRARY(source), &rows, &columns, NULL,
1830 168                                    "SELECT id FROM _%s", entry_type_name[entry_type]);
1831 269
1832 17         if (data==NULL) {
1833                 g_warning("library::query_ids: NULL data for entry type %s", entry_type_name[entry_type]);
1834                 return NULL;
1835         };
1836 269
1837 17         for (int i=0;i<rows;i++) {
1838                 int pos=(i+1)*columns; list=g_slist_prepend(list, (void *)atoi(data[pos]));
1839         }
1840 269
1841         // FIXME: do we need to do this ? Or could sql return the results sorted
1842 17         // backwards if order matters ?? Would be quicker to return an array instead
1843 269         // of a list anyway.
1844 17         list=g_slist_reverse(list);
1845 269
1846 17         sqlite3_free_table(data);
1847 269
1848 17         return list;
1849 };
1850
1851 449 static GSList *query_matching_except (MusicSource *music_source, Entry *entry, int ignored_apid) {
1852 // Queries entries, ignoring a certain property. This is used for example is music_source::track_added_callback
1853 17 // where we find all releases that match on everything but album artist.
1854 //
1855         // FIXME: this needs to be really fast, because it is called on
1856         // releases for every single track add !!
1857 449         Library *self = LIBRARY(music_source);
1858 17         GSList *result=NULL;
1859         GSList *result_ids=find_entries_matching_except(self, entry, ignored_apid, entry);
1860         for (GSList *id_node=result_ids;id_node;id_node=id_node->next)
1861 449                 if (entry->source==music_source && (int)id_node->data!=entry->id)
1862                         result=g_slist_prepend(result, query_entry(music_source, entry->type, (int)id_node->data));
1863 17         g_slist_free(result_ids);
1864         return result;
1865 };
1866
1867 449 static MusicSourceView *create_view (MusicSource *music_source, ViewConfig *config) {
1868 451         /*MusicSourceView *view = g_object_new(LIBRARY_VIEW_TYPE, "source", music_source,
1869                                              "config", config, NULL);*/
1870         MusicSourceView *view = g_object_new(GENERIC_VIEW_TYPE, "source", music_source,
1871 215                                              "config", config, NULL);
1872 24         //printf("library_create_view done- %X\n", view);fflush(stdout);
1873         return view;
1874 };
1875
1876 451 /*************************************************************************************************
1877  * Editing - internal functions.
1878  */
1879
1880 static void add_entry_internal (MusicSource *music_source, Entry *entry) {
1881         Library *self = LIBRARY(music_source);
1882         GString *columns = g_string_new(NULL), *values = g_string_new(NULL);
1883         for (int i=0; i<entry_n_properties[entry->type]; i++) {
1884                 gboolean ignore_property = FALSE;
1885
1886                 // Don't store shadowing properties in the db - leave the value as null instead.
1887 269                 if (schema[entry->type][i].flags & PROPERTY_SHADOW) {
1888 451                         const guint32 shadow_property_apid = schema[entry->type][i].shadow_property_apid;
1889                         void *shadowee_value = entry_get_distant_property (entry, shadow_property_apid);
1890 269                         if (entry_value_match(schema[entry->type][i].type, entry_get_property(entry, i),
1891 451                                               shadowee_value))
1892                                 ignore_property = TRUE;
1893                 }
1894 269
1895 451                 if (!ignore_property) {
1896                         char *string = make_property_string (self, entry, i);
1897                         if (string != NULL) {
1898                                 g_string_append (columns, ", ");
1899                                 g_string_append (columns, schema[entry->type][i].name);
1900                                 if (IS_FOREIGN_KEY(schema[entry->type][i].type))
1901                                         g_string_append(columns, "_id");
1902                                 g_string_append (values, ", ");
1903                                 g_string_append (values, string);
1904                                 g_free (string);
1905                         }
1906 269                 };
1907         };
1908 451
1909 269         if (columns->len>0) columns->str[0]=' ';
1910         if (values->len>0) values->str[0]=' ';
1911
1912         db_action_printf (self, NULL, "INSERT INTO _%s (%s) VALUES(%s)", entry_type_name[entry->type],
1913                           columns->str, values->str);
1914 451         int entry_id = sqlite3_last_insert_rowid (SP->db);
1915 269
1916         g_string_free(columns, TRUE);
1917         g_string_free(values, TRUE);
1918
1919 451         _entry_set_id (entry, entry_id); entry->source = self;
1920
1921         #ifndef LIBRARY_DISABLE_CACHE
1922                 cache_store_entry (self, entry);
1923         #endif
1924 269 };
1925
1926 452 /* update_entry_internal: re-store properties of 'entry' in the database.
1927  *
1928  *   shadow_override: used in the offbeat situation where a property in this entry shadows
1929  *                    another entry which has changed, but the changes haven't yet been committed.
1930  *                    This actually happens quite often in musicsource.c:update_shadowers().
1931  */
1932 static void update_entry_internal (MusicSource *music_source, Entry *entry, int shadow_override_id,
1933                                    void *shadow_override_value) {
1934 451         Library *self = LIBRARY(music_source);
1935
1936 452         //entry_dump (entry);
1937         //printf ("shadow override: %i. val %x.\n", shadow_override_id, shadow_override_value); fflush (stdout);
1938
1939 269         GString *assignments = g_string_new(NULL);
1940 283         for (int i=0; i<entry_n_properties[entry->type]; i++) {
1941 269                 char *string = NULL;
1942 451
1943 452                 void *shadowee_value = NULL;
1944 269                 if (schema[entry->type][i].flags & PROPERTY_SHADOW) {
1945 283                         // Any shadow properties which are the same as their shadowee are explicitly set to NULL
1946 451                         // here. This means that when a shadow property that was independent starts shadowing
1947                         // again, it is no longer stored and will mirror the shadowee's value again.
1948 452
1949                         if (i==shadow_override_id)
1950                                 shadowee_value = shadow_override_value;
1951                         else {
1952                                 const guint32 shadow_property_apid = schema[entry->type][i].shadow_property_apid;
1953                                 shadowee_value = entry_get_distant_property (entry, shadow_property_apid);
1954                         }
1955
1956 269                         if (entry_value_match(schema[entry->type][i].type, entry_get_property(entry, i),
1957                                               shadowee_value))
1958                                 string = g_strdup("NULL");
1959 451                 }
1960
1961 452                 if (string == NULL) {
1962                         /*if (shadowee_value != NULL)
1963                                 string = make_property_string_from_value (self, schema[entry->type][i].type,
1964                                                                           shadowee_value);
1965                         else*/
1966                         string = make_property_string (self, entry, i);
1967                 }
1968 269
1969 451                 if (string != NULL) {
1970                         g_string_append (assignments, ", ");
1971                         g_string_append (assignments, schema[entry->type][i].name);
1972                         if (IS_FOREIGN_KEY(schema[entry->type][i].type))
1973                                 g_string_append(assignments, "_id");
1974                         g_string_append (assignments, "=");
1975                         g_string_append (assignments, string);
1976                         g_free (string);
1977                 }
1978 269         };
1979 451
1980         if (assignments->len>0)
1981                 assignments->str[0]=' ';
1982 269
1983         db_action_printf (self, NULL, "UPDATE _%s SET %s WHERE id=%i", entry_type_name[entry->type],
1984                           assignments->str, entry->id);
1985
1986         g_string_free(assignments, TRUE);
1987 451
1988         #ifndef LIBRARY_DISABLE_CACHE
1989         CacheEntry *cache_entry = g_hash_table_lookup (SP->cache[entry->type], &entry->id);
1990         if (cache_entry!=NULL)
1991                 cache_entry_update (self, cache_entry);
1992         #endif
1993 269 };
1994
1995 451 static void remove_entry_internal (MusicSource *music_source, EntryType type, int id) {
1996         Library *self = LIBRARY(music_source);
1997 269         db_action_printf (self, NULL, "DELETE FROM _%s WHERE id=%i", entry_type_name[type], id);
1998 451
1999         #ifndef LIBRARY_DISABLE_CACHE
2000         cache_remove_entry (self, type, id);
2001         #endif
2002 };
2003
2004 /* update_foreign_keys: used when entries are merged, to update properties that refer to the
2005  *                      removed one 'from_entry' and should point to the kept one 'to_entry' instead
2006  */
2007 static void update_foreign_keys (MusicSource *music_source, Entry *from_entry, Entry *to_entry) {
2008         Library *self = LIBRARY(music_source);
2009
2010         db_update_references (self, from_entry->type, from_entry->id, to_entry->id);
2011
2012         // Find any checked-out entries which reference from_entry.
2013         // FIXME: this function currently only returns the duplicate version 0. I'm almost certain this
2014         // is the wrong thing to do, consider these cases:
2015         //  - two versions of an entry reference different entries - two different duplicates need
2016         //    unreffing
2017         //  - five versions of an entry reference the same duplicate. Surely it needs to be unreffed
2018         //    five times. Or will there be five copies of it?
2019         // FIXME: we could do this much more quickly using the cache, when it is enabled.
2020         GSList *checkout_entry_list = _music_source_list_checked_out_entries (music_source);
2021         for (GSList *node=checkout_entry_list; node; node=node->next) {
2022                 Entry *co_entry = node->data;
2023                 for (int p=0; p<entry_n_properties[co_entry->type]; p++) {
2024                         if (schema[co_entry->type][p].type != from_entry->type)
2025                                 continue;
2026                         if (entry_get_property (co_entry, p) == from_entry)
2027                                 _music_source_unref_checkout_copies (music_source, from_entry->type, from_entry->id);
2028                 }
2029         }
2030         g_slist_free (checkout_entry_list);
2031
2032         #ifndef LIBRARY_DISABLE_CACHE
2033         cache_change_references (self, from_entry, to_entry);
2034         #endif
2035 };
2036
2037
2038 17
2039 270 /* count_references: internal routine used by 'touch'. Checks if entry is not reffed by anything,
2040 451  *                   taking into account the source's removal queue.
2041  *
2042  *   limit: can be used if the exact value isn't actually needed - rather than execute a long query
2043  *          for no reason, count_references will execute the query until it finds 'limit'
2044  *          references and then give up.
2045  *
2046  * FIXME: sloooow!!
2047  */
2048 static int count_references (Library *self, EntryType type, int id, int limit,
2049                              EntryType caller_type, int caller_id) {
2050 269         int ref_count = 0;
2051 451
2052 304         GSList *node = entry_type_info[type].foreign_key_owners;
2053 451         while(node != NULL) {
2054                 const guint32 apid = GPOINTER_TO_INT(node->data);
2055 269
2056                 // Find anything in the removal queue with the type being counted, and ignore the
2057                 // references those entries contain.
2058                 //
2059                 GString *exceptions = g_string_new (NULL);
2060 452                 // FIXME: the removal queue is actually ignored, because the will_be_unreffed flag
2061                 // does the job well enough. Why does musicsourceimporting need to do this but library
2062                 // does not?
2063                 /* for (GSList *q_node=SP->removal_queue; q_node; q_node=q_node->next) {
2064 451                         _RemovalQueueEntry *r = q_node->data;
2065                         if (r->type==APID_GET_TYPE(apid))
2066                                 g_string_append_printf (exceptions, "AND id!=%i ", r->id);
2067 452                 }; */
2068 269
2069 270                 ref_count += db_expression_printf(self, NULL, "SELECT COUNT(id) FROM _%s WHERE %s_id=%i %s "
2070                                                   "LIMIT %i", entry_type_name[APID_GET_TYPE(apid)],
2071                                                   APID_GET_PROPERTY(apid).name, id, exceptions->str, limit);
2072 269                 g_string_free (exceptions, TRUE);
2073                 node = node->next;
2074         };
2075
2076 452         //printf ("library: Count_references: %s %i = %i\n", entry_type_name[type], id, ref_count);
2077 269         return ref_count;
2078 };
2079
2080 451 /* touch: garbage collection. */
2081 static void touch (Library *self, EntryType type, int id, gboolean delete,
2082                    gboolean will_be_unreffed, GSList **p_jetsam, EntryType caller_type,
2083 344                    int caller_id) {
2084 451         if (ENTRY_IS_SPECIAL(type, id))
2085 269                 return;
2086
2087 452         //printf ("touch: %s %i - d%i u%i.\n", entry_type_name[type], id,
2088         //        delete, will_be_unreffed); fflush (stdout);
2089
2090 269         if (delete) {
2091 451                 // Search for entries that point to given entry (unless the link is flagged 'trivial') and
2092                 // delete those. For example, if removing a recording this deletes all of its files.
2093 269                 //
2094                 for (int i=0; i<ENTRY_TYPE_COUNT; i++) {
2095                         if (i==type) continue;
2096 451
2097 269                         for (int p=0; p<entry_n_properties[i]; p++) {
2098                                 if (schema[i][p].type==type) {
2099                                         char **data; int rows, columns;
2100                                         data = db_query_printf (self, &rows, &columns, NULL,
2101 451                                                                 "SELECT id FROM _%s WHERE %s_id=%i",
2102                                                                 entry_type_name[i], schema[i][p].name, id);
2103 269                                         if (data==NULL) {
2104 451                                                 g_warning ("library::touch: NULL data finding %s entries linked to %s %i\n",
2105                                                            entry_type_name[i], entry_type_name[schema[i][p].type], id);
2106 269                                                 break;
2107                                         };
2108
2109 451                                         const gboolean delete_child = !(schema[i][p].flags & PROPERTY_TRIVIAL);
2110 269                                         for (int k=0; k<rows; k++) {
2111 451                                                 const int child_id = atoi(data[k+1]);
2112                                                 if (i==caller_type && child_id==caller_id)
2113                                                         continue;
2114
2115                                                 touch (self, i, child_id, delete_child, FALSE, p_jetsam, type, id);
2116 269                                         }
2117                                         sqlite3_free_table (data);
2118                                 };
2119                         };
2120                 };
2121         };
2122
2123         if (!delete) {
2124 451                 int ref_count = count_references(self, type, id, 2, caller_type, caller_id);
2125 269
2126 270                 // It is valid for an entry with no references to be touched with will_be_unreffed TRUE -
2127                 // all the entries that point to it could be in the removal queue, even though they can't
2128                 // yet have been removed.
2129                 g_warn_if_fail (ref_count >= 0);
2130 269                 if (will_be_unreffed) ref_count--;
2131
2132 270                 delete = (ref_count <= 0);
2133 269         };
2134
2135 344         // Touch all entries this one links to, to see if they now need removing or 'changed' emitting.
2136 269         for (int p=0; p<entry_n_properties[type]; p++) {
2137                 if (IS_FOREIGN_KEY(schema[type][p].type)) {
2138                         // Parent is eg a recording's composition
2139 451                         const int foreign_type = schema[type][p].type;
2140                         const int foreign_id = db_expression_printf
2141                                                  (self, NULL, "SELECT %s_id FROM _%s WHERE id=%i",
2142                                                   schema[type][p].name, entry_type_name[type], id);
2143 269                         if (foreign_id!=0 && (foreign_type!=caller_type || foreign_id!=caller_id))
2144 452                                 touch (self, foreign_type, foreign_id, FALSE, delete /* FALSE */, p_jetsam, type, id);
2145 269                 };
2146         };
2147
2148         if (delete) {
2149 344                 // Add entry to removal queue. (We can't delete anything until all of the touching is done).
2150 451                 gboolean find (_RemovalQueueEntry *_r) { return !(_r->type==type && _r->id==id); };
2151                 if (!g_slist_find_custom(SP->removal_queue, NULL, (GCompareFunc)find)) {
2152                         _RemovalQueueEntry *removal = g_slice_new0 (_RemovalQueueEntry);
2153                         removal->type = type;
2154                         removal->id = id;
2155                         SP->removal_queue = g_slist_prepend (SP->removal_queue, removal);
2156
2157 346                         Entry *entry = music_source_query_entry (MUSIC_SOURCE(self), type, id, "touch");
2158 451                         _music_source_queue_notify (MUSIC_SOURCE(self), entry, NULL);
2159 346                         entry_unref (entry, "touch");
2160 271                 };
2161 344
2162                 // Also remove from jetsam.
2163                 if (p_jetsam != NULL)
2164                         for (GSList *node=*p_jetsam; node; node=node->next) {
2165                                 Entry *jetsam_entry = node->data;
2166                                 if (jetsam_entry->type==type && jetsam_entry->id==id) {
2167                                         (*p_jetsam) = g_slist_remove_link (*p_jetsam, node);
2168                                         break;
2169                                 };
2170                         };
2171 451         }
2172 269 }
2173
2174 451 static void execute_removal_queue (Library *self) {
2175         MusicSource *music_source = MUSIC_SOURCE(self);
2176 452         //printf ("Library::execute_removal_queue: %i entries.\n", g_slist_length(SP->removal_queue)); fflush (stdout);
2177
2178 451         for (GSList *q_node=SP->removal_queue; q_node; q_node=q_node->next) {
2179                 _RemovalQueueEntry *r = q_node->data;
2180                 remove_entry_internal (music_source, r->type, r->id);
2181                 g_slice_free (_RemovalQueueEntry, r);
2182 269         };
2183 451         g_slist_free (SP->removal_queue);
2184         SP->removal_queue = NULL;
2185 269 };
2186
2187
2188 static void begin_transaction(MusicSource *self) {
2189         // FIXME: before or after?
2190         db_action(LIBRARY(self), NULL, "begin");
2191         //GError *silencer=NULL;
2192         //db_action(LIBRARY(self), &silencer, "DROP INDEX track_idx");
2193         //db_action(LIBRARY(self), &silencer, "DROP INDEX release_idx");
2194 };
2195
2196 static void end_transaction(MusicSource *self) {
2197         //db_action(LIBRARY(self), NULL, "CREATE INDEX track_idx ON track(release_id, number);");
2198         //db_action(LIBRARY(self), NULL, "CREATE INDEX release_idx ON release (album_id, date);");
2199         db_action(LIBRARY(self), NULL, "end");
2200 }
2201
2202 451 // FIXME: these functions could actually go in musicsource now, genericsource does more or less the
2203 // same.
2204 449 static int add_entry (MusicSource *music_source, Entry *entry) {
2205 451         entry_take_last_ref (entry, "library::add_entry");
2206         Entry *result = _music_source_add_entry_recursive (music_source, entry, TRUE, entry);
2207
2208         int result_id = result->id;
2209
2210 452         if (_music_source_emit_queued_notifications (music_source, NULL)) {
2211                 // Entries can be removed during an add, mostly by notify callbacks.
2212                 execute_removal_queue (LIBRARY (music_source));
2213                 //entry_destroy (entry);
2214         }
2215 451
2216         //entry_destroy (result);
2217         entry_unref (entry, "library::add_entry");
2218 269         if (result!=entry)
2219 451                 entry_unref(result, "_music_source_add_entry_recursive");
2220 269
2221 451         return result_id;
2222 17 };
2223
2224 451 // FIXME: reimplement this, we can do it faster than the base class.
2225 /*static void update_entry_property (MusicSource *self, EntryType entry_type, int entry_id,
2226 269                                    int property_id, const void *value) {
2227         if (schema[entry_type][property_id].flags & PROPERTY_MERGEABLE
2228             || schema[entry_type][property_id].flags & PROPERTY_TRIVIAL) {
2229                 // FIXME: can we do it without doing this ?
2230                 Entry *entry = music_source_query_entry(self, entry_type, entry_id,
2231                                                         "library::update-entry-property");
2232                 if (entry==NULL) {
2233                         g_warning ("library::update-entry-property called on invalid entry\n");
2234                         return;
2235                 };
2236
2237                 // Update in cache - since we just queried the entry, all we need to do is this since it
2238                 // will definately be in the cache.
2239                 entry_set_property (entry, property_id, value);
2240
2241                 // Update in DB
2242                 // FIXME: update_entry_property internal ...
2243                 //
2244                 GString *assignment = g_string_new(NULL);
2245                 char *string = make_property_string(LIBRARY(self), entry, property_id);
2246                 if (string!=NULL) {
2247                         g_string_append (assignment, schema[entry->type][property_id].name);
2248                         if (IS_FOREIGN_KEY(schema[entry->type][property_id].type)) g_string_append(assignment, "_id");
2249                         g_string_append(assignment, "="); g_string_append(assignment, string);
2250                         g_free (string);
2251                 };
2252                 db_action_printf (LIBRARY(self), NULL, "UPDATE _%s SET %s WHERE id=%i",
2253                                   entry_type_name[entry_type], assignment->str, entry_id);
2254                 g_string_free (assignment, TRUE);
2255
2256                 // FIXME: would be faster to do this work ourselves, that's the whole point of this method dumbass !
2257                 // WE only need to emit changed on the whole entry tree, not check merges.
2258                 music_source_checkin_entry (self, entry, "library::update-entry-property");
2259         } else {
2260                 Entry *entry = music_source_checkout_entry(self, entry_type, entry_id,
2261                                                            "library::update-entry-property");
2262
2263                 if (entry==NULL) {
2264                         g_warning ("entry %i is null, attemped to set property %i", entry_id, property_id);
2265                         return;
2266                 } else {
2267                         entry_set_property (entry, property_id, value);
2268                         music_source_checkin_entry (self, entry, "library::update-entry-property");
2269                 };
2270         };
2271
2272 449         // FIXME: should just be two neat methods on music_source really to block & unblock signals
2273 351         if (self->signal_blocking_stack==0 && self->signal_queue!=NULL)
2274 346                 _music_source_emit_queued_signals (self);
2275 451 };*/
2276 269
2277 449 static void remove_entry (MusicSource *music_source, EntryType type, int id) {
2278 451         Library *self = LIBRARY(music_source);
2279 126         method_track_enter ("library::remove-entry %s %i\n", entry_type_name[type], id);
2280 17
2281 449         MUSIC_SOURCE_CLASS(library_parent_class)->remove_entry (music_source, type, id);
2282 269
2283 451         g_warn_if_fail (SP->removal_queue == NULL);
2284
2285         touch (self, type, id, TRUE, FALSE, NULL, 0, 0);
2286
2287         _music_source_emit_queued_notifications (music_source, NULL);
2288         execute_removal_queue (self);
2289 17 };
2290
2291 269 static Entry *checkout_entry (MusicSource *music_source, EntryType root_type, int root_id) {
2292         method_track_printf ("library::checkout-entry %s %i\n", entry_type_name[root_type], root_id);
2293 344
2294         Entry *entry = query_entry(music_source, root_type, root_id);
2295
2296 452         //printf ("library:checkout_entry: 1: removal queue length %i.\n", g_slist_length(LIBRARY(music_source)->priv->removal_queue)); fflush (stdout);
2297
2298 344         if (entry != NULL)
2299 451                 _music_source_checkout_entry_recursive (music_source, entry);
2300
2301 452         //printf ("library:checkout_entry: 2: removal queue length %i.\n", g_slist_length(LIBRARY(music_source)->priv->removal_queue)); fflush (stdout);
2302
2303 451         // No need to worry about the cache since it points directly to the entry that will be being
2304         // modified, and it can't be queried while checked out anyway.
2305 17         return entry;
2306 };
2307
2308 269 static void checkin_entry (MusicSource *music_source, Entry *entry) {
2309 345         Library *self = LIBRARY(music_source);
2310 451         GSList *jetsam = NULL;
2311 269
2312 452         //printf ("library:checkin_entry: 1: removal queue length %i.\n", g_slist_length(SP->removal_queue)); fflush (stdout);
2313
2314 451         Entry *result = _music_source_checkin_entry_recursive (music_source, entry, &jetsam, entry);
2315 278
2316 452         //printf ("library:checkin_entry: 2: removal queue length %i.\n", g_slist_length(SP->removal_queue)); fflush (stdout);
2317
2318 17         // Original entry is unreffed in music_source_checkin_entry
2319 451         if (entry != result)
2320                 entry_unref (result, "_music_source_checkin_entry_recursive");
2321 269
2322         for (GSList *node=jetsam; node; node=node->next) {
2323                 Entry *entry = jetsam->data;
2324 451                 touch (self, entry->type, entry->id, FALSE, FALSE, &node->next, 0, 0);
2325 269         };
2326         g_slist_free (jetsam);
2327
2328 452         //printf ("end of checkin %s %i.\n", ENTRY_PF(entry)); fflush (stdout);
2329 451         if (_music_source_emit_queued_notifications (music_source, NULL)) {
2330                 execute_removal_queue (self);
2331 452                 //entry_destroy (entry);
2332 451         }
2333 17 };
2334
2335 269
2336
2337 17 /////////////////////////////////////////////////////////
2338 // Debugging methods
2339 //
2340
2341 95 // Empty caches
2342 static void flush(MusicSource *music_source) {
2343         Library *self = LIBRARY(music_source);
2344         cache_flush(self);
2345 };
2346
2347 17 // For debugging
2348 //
2349 206 static void dump (MusicSource *music_source, gboolean detailed) {
2350 207         if (detailed) {
2351                 #ifndef LIBRARY_DISABLE_CACHE
2352                 printf("\n                              LIBRARY: CONTENTS OF CACHE\n\n");
2353 269                 Library *self=LIBRARY(music_source);
2354 207                 for (int i=0;i<ENTRY_TYPE_COUNT;i++) {
2355                         printf("\n%s\n---------------\n", entry_type_name[i]);
2356                         GHashTableIter iter; g_hash_table_iter_init(&iter, SP->cache[i]);
2357 351                         void *pointer; Entry *entry;
2358                         while (g_hash_table_iter_next(&iter, NULL, &pointer)) {
2359                                 entry = pointer; entry_dump (entry);
2360 207                         };
2361                 };
2362                 fflush(stdout);
2363                 #endif
2364
2365                 printf("\n                              LIBRARY: ACTUAL CONTENTS\n\n");
2366                 for (int i=0;i<ENTRY_TYPE_COUNT;i++) {
2367 269                         if (str_match(entry_type_name[i], "FIXME")) continue;
2368 207                         printf("\n%s\n---------------\n", entry_type_name[i]);
2369                         int highest_id;
2370                         music_source_get_n_entries(music_source, i, &highest_id, NULL);
2371                         for (int j=0;j<highest_id;j++) {
2372                                 Entry *entry=music_source_query_entry(music_source, i, j, "dump");
2373                                 if (entry!=NULL) {
2374                                         entry_dump(entry);
2375                                         entry_unref(entry, "dump");
2376                                 };
2377 56                         };
2378                 };
2379         };
2380 17 };
2381 269
2382 132 #ifdef LIBRARY_TRACE_METHODS
2383 17 static int indent_counter=0;    // FIXME: assumes there is only one instance of library, but this only debugging anyway
2384 #endif
2385
2386 static void method_track_enter(const char *text, ...) {
2387 132         #ifdef LIBRARY_TRACE_METHODS
2388 269         for (int i=0;i<indent_counter*2;i+=2) { printf("  ");   };      indent_counter++;
2389 17         va_list ap; va_start(ap, text); vprintf(text, ap); va_end(ap); fflush(stdout);
2390         #endif
2391 };
2392
2393 static void method_track_leave(const char *text, ...) {
2394 132         #ifdef LIBRARY_TRACE_METHODS
2395 17         indent_counter--; for (int i=0;i<indent_counter*2;i+=2) {       printf("  ");   };
2396         va_list ap; va_start(ap, text); vprintf(text, ap); va_end(ap); fflush(stdout);
2397         #endif
2398 };
2399 static void method_track_printf(const char *text, ...) {
2400 132         #ifdef LIBRARY_TRACE_METHODS
2401 17         for (int i=0;i<indent_counter*2;i+=2) { printf("  ");   };
2402         va_list ap; va_start(ap, text); vprintf(text, ap); va_end(ap); fflush(stdout);
2403         #endif
2404 };
2405
2406 274 static void cache_trace (const char *text, ...) {
2407 132         #ifdef LIBRARY_TRACE_CACHE
2408         #ifdef LIBRARY_TRACE_METHODS
2409 17         for (int i=0;i<indent_counter*2;i+=2) { printf("  ");   };
2410         #endif
2411         va_list ap; va_start(ap, text); vprintf(text, ap); va_end(ap); fflush(stdout);
2412         #endif
2413 };
2414 356
2415 #endif

Loggerhead is a web-based interface for Bazaar branches