RSS

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

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

Loggerhead is a web-based interface for Bazaar branches