163
static void check_entry (Entry *entry) {
163
static void process_empty_properties (Entry *entry) {
164
// Default entries
164
// Default entries
166
switch(entry->type) {
166
switch(entry->type) {
786
g_warning("Adding %s %i - already owned by another source", ENTRY_PF(entry));
786
g_warning("Adding %s %i - already owned by another source", ENTRY_PF(entry));
789
check_entry (entry);
789
process_empty_properties (entry);
791
// Go through every child entry and add/update this too. (Unless it was the caller)
792
// Go through every child entry and add/update this too. (Unless it was the caller)
958
static gboolean update_shadowing_property(MusicSource *self, Entry *shadower, int
959
static gboolean update_shadowing_property(MusicSource *self, Entry *shadower, int
959
shadowing_property_id, void *old_shadowee_value,
960
shadowing_property_id, void *old_shadowee_value,
960
void *new_shadowee_value, Entry **p_duplicate) {
961
void *new_shadowee_value, Entry **p_duplicate) {
961
//printf ("update_shadowing_property: %s[%i].%s.\n", ENTRY_PF(shadower),
962
//printf ("update_shadowing_property: %s[%i] %x.%s.\n", ENTRY_PF(shadower), shadower,
962
// schema[shadower->type][shadowing_property_id].name); fflush (stdout);
963
// schema[shadower->type][shadowing_property_id].name); fflush (stdout);
963
g_return_val_if_fail (shadowing_property_id >= 0
964
g_return_val_if_fail (shadowing_property_id >= 0
964
&& shadowing_property_id < entry_n_properties[shadower->type], FALSE);
965
&& shadowing_property_id < entry_n_properties[shadower->type], FALSE);
988
* checked out. This is a horrible function in every way. */
989
* checked out. This is a horrible function in every way. */
989
static void update_shadowers (MusicSource *self, Entry *old_entry, Entry *new_entry,
990
static void update_shadowers (MusicSource *self, Entry *old_entry, Entry *new_entry,
990
gboolean types_already_updated[]) {
991
gboolean types_already_updated[]) {
992
editing_trace ("\tupdate_shadowers [%s %i]\n", ENTRY_PF(old_entry));
991
for (GSList *node=entry_type_info[new_entry->type].shadowees; node; node=node->next) {
993
for (GSList *node=entry_type_info[new_entry->type].shadowees; node; node=node->next) {
992
EntryType foreign_type = APID_GET_TYPE(GPOINTER_TO_UINT(node->data));
994
EntryType foreign_type = APID_GET_TYPE(GPOINTER_TO_UINT(node->data));
993
if (types_already_updated[foreign_type]) continue;
995
if (types_already_updated[foreign_type]) continue;
1010
// FIXME: this is really slow, and we do it a lot
1012
// FIXME: this is really slow, and we do it a lot
1012
GSList *relations = music_source_query_relations(self, new_entry->type, new_entry->id,
1014
GSList *relations = music_source_query_relations(self, new_entry->type, new_entry->id,
1013
MAKE_APID(foreign_type, p), "checkin");
1015
MAKE_APID(foreign_type, p), "checkin");
1015
// We assume apids of the same type in the shadowee list are contiguous, so we can do all
1017
// We assume apids of the same type in the shadowee list are contiguous, so we can do all
1016
// the properties for one type in a single pass (quite a significant optimisation :).
1018
// the properties for one type in a single pass (quite a significant optimisation :).
1041
additional_properties = i; continue;
1043
additional_properties = i; continue;
1043
} else if (++i > additional_properties) break;
1045
} else if (++i > additional_properties) break;
1047
// FIXME: this is kind of a hack. We update the source's entry before changes to the
1048
// shadowee entry are committed. So, we pass the new value of the shadowing property
1049
// manually. But we can only pass one at a time, so we have to update the entry
1050
// several times. This code is becoming a bit of a nightmare.
1051
if (MUSIC_SOURCE_GET_CLASS(self)->update_entry_internal != NULL)
1052
MUSIC_SOURCE_GET_CLASS(self)->update_entry_internal (self, shadower, i, new_val);
1044
} while (cur_node);
1053
} while (cur_node);
1046
if (changed) {
1055
if (changed) {
1047
g_warn_if_fail (duplicate!=NULL);
1056
g_warn_if_fail (duplicate!=NULL);
1057
editing_trace ("\tupdated: %s %i.\n", ENTRY_PF(shadower));
1048
//printf ("Queuing %s %i, %s %ip\n", ENTRY_PF(duplicate), ENTRY_PF(shadower)); fflush (stdout);
1058
//printf ("Queuing %s %i, %s %ip\n", ENTRY_PF(duplicate), ENTRY_PF(shadower)); fflush (stdout);
1049
_music_source_queue_notify (self, duplicate, shadower);
1059
_music_source_queue_notify (self, duplicate, shadower);
1242
_unref_checkout_copy_recursive (self, jetsam_entry->type, jetsam_entry->id, TRUE, -1, -1);
1252
_unref_checkout_copy_recursive (self, jetsam_entry->type, jetsam_entry->id, TRUE, -1, -1);
1245
check_entry (new_entry);
1255
process_empty_properties (new_entry);
1256
entry_check (new_entry);
1247
// All the 'jetsam' (entries that we referenced by foreign keys when checked out, but have now
1258
// All the 'jetsam' (entries that we referenced by foreign keys when checked out, but have now
1248
// been replaced with something else) are still checked out - we've remove all their stored
1259
// been replaced with something else) are still checked out - we've remove all their stored
1290
entry_check (new_entry);
1301
entry_check (new_entry);
1291
// Update the reference, in case the child entry was merged with an existing one.
1302
// Update the reference, in case the child entry was merged with an existing one.
1292
entry_dump (child);
1303
//entry_dump (child);
1293
entry_take_property (new_entry, i, child);
1304
entry_take_property (new_entry, i, child);
1313
// Sources such as Library update their stored representation of the entry now. We do this if a
1324
// Sources such as Library update their stored representation of the entry now. We do this if a
1314
// merge happened too in case any mergeable properties were changed in the process.
1325
// merge happened too in case any mergeable properties were changed in the process.
1315
if (MUSIC_SOURCE_GET_CLASS(self)->update_entry_internal != NULL)
1326
if (MUSIC_SOURCE_GET_CLASS(self)->update_entry_internal != NULL)
1316
MUSIC_SOURCE_GET_CLASS(self)->update_entry_internal (self, new_entry);
1327
MUSIC_SOURCE_GET_CLASS(self)->update_entry_internal (self, new_entry, -1, NULL);
1318
g_warn_if_fail (new_entry->id > 0);
1329
g_warn_if_fail (new_entry->id > 0);
1581
* inside a notification function.)
1592
* inside a notification function.)
1583
gboolean _music_source_emit_queued_notifications (MusicSource *self, Entry *caller) {
1594
gboolean _music_source_emit_queued_notifications (MusicSource *self, Entry *caller) {
1584
if (SP->notifying_on_entry != NULL)
1595
if (SP->notifying_on_entry != NULL) {
1596
//printf ("emit_queued_notifications: already notifying.\n"); fflush (stdout);
1585
return FALSE;
1597
return FALSE;
1587
//gboolean caller_emitted = FALSE;
1600
//gboolean caller_emitted = FALSE;
117
static MusicSourceView *create_view(MusicSource *music_source, ViewConfig *config);
117
static MusicSourceView *create_view(MusicSource *music_source, ViewConfig *config);
119
static void add_entry_internal (MusicSource *music_source, Entry *entry);
119
static void add_entry_internal (MusicSource *music_source, Entry *entry);
120
static void update_entry_internal (MusicSource *music_source, Entry *entry);
120
static void update_entry_internal (MusicSource *music_source, Entry *entry, int shadow_override_id,
121
void *shadow_override_value);
121
static void remove_entry_internal (MusicSource *music_source, EntryType type, int id);
122
static void remove_entry_internal (MusicSource *music_source, EntryType type, int id);
122
static void update_foreign_keys (MusicSource *music_source, Entry *from_entry, Entry *to_entry);
123
static void update_foreign_keys (MusicSource *music_source, Entry *from_entry, Entry *to_entry);
393
static void db_action (Library *self, GError **perror, const char *text) {
394
static void db_action (Library *self, GError **perror, const char *text) {
394
g_return_if_fail (self!=NULL);
395
g_return_if_fail (self!=NULL);
395
int result; char *error;
396
int result; char *error;
398
#ifdef LIBRARY_TRACE_QUERIES
399
printf("=> a %s\n", text); fflush(stdout);
396
SQLITE_WRAPPER (sqlite3_exec (SP->db, text, NULL, NULL, &error));
402
SQLITE_WRAPPER (sqlite3_exec (SP->db, text, NULL, NULL, &error));
402
int result; char **data, *error;
408
int result; char **data, *error;
404
#ifdef LIBRARY_TRACE_QUERIES
410
#ifdef LIBRARY_TRACE_QUERIES
405
printf("Query: %s\n", text); fflush(stdout);
411
printf("=> q %s.. ", text); fflush(stdout);
408
SQLITE_WRAPPER (sqlite3_get_table(SP->db, text, &data, rows, columns, &error));
414
SQLITE_WRAPPER (sqlite3_get_table(SP->db, text, &data, rows, columns, &error));
410
#ifdef LIBRARY_TRACE_QUERIES
416
#ifdef LIBRARY_TRACE_QUERIES
411
printf ("Rows %i columns %i\n", *rows, *columns); fflush(stdout);
417
printf ("rows %i columns %i\n", *rows, *columns); fflush(stdout);
414
return data;
420
return data;
418
static gint64 db_expression (Library *self, GError **perror, const char *text) {
424
static gint64 db_expression (Library *self, GError **perror, const char *text) {
419
g_return_val_if_fail (self!=NULL, 0);
425
g_return_val_if_fail (self!=NULL, 0);
420
int result; char **data, *error; int rows, columns;
426
int result; char **data, *error; int rows, columns;
428
#ifdef LIBRARY_TRACE_QUERIES
429
printf("=> e %s ", text); fflush(stdout);
421
SQLITE_WRAPPER (sqlite3_get_table(SP->db, text, &data, &rows, &columns, &error));
432
SQLITE_WRAPPER (sqlite3_get_table(SP->db, text, &data, &rows, &columns, &error));
423
gint64 value = 0;
434
gint64 value = 0;
428
value = atoll(data[1]);
439
value = atoll(data[1]);
430
sqlite3_free_table (data);
441
sqlite3_free_table (data);
443
#ifdef LIBRARY_TRACE_QUERIES
444
printf ("%" G_GINT64_FORMAT "\n", value); fflush (stdout);
431
return value;
446
return value;
860
// FIXME: I wonder if it would be quicker to just remove from cache on checkout and add again on
875
// FIXME: I wonder if it would be quicker to just remove from cache on checkout and add again on
861
// checking back in ...
876
// checking back in ...
862
static void cache_entry_update (Library *self, CacheEntry *cache_entry) {
877
static void cache_entry_update (Library *self, CacheEntry *cache_entry) {
863
/*Entry *entry = cache_entry->entry; entry_ref (entry, "foo");
878
cache_trace ("cache_entry_update: %s %i [%x].\n", ENTRY_PF(cache_entry->entry),
864
cache_remove_cache_entry (self, cache_entry, NULL);
879
cache_entry->entry);
865
cache_store_entry (self, entry); entry_unref (entry, "foo");
868
//#ifdef YOU_LIKE_TO_MAKE_LIFE_HARD_FOR_YOURSELF
869
cache_trace ("cache_entry_update: %s %i.\n", ENTRY_PF(cache_entry->entry));
870
int find_by_property (CacheEntryProperty *property, int property_id) {
880
int find_by_property (CacheEntryProperty *property, int property_id) {
871
if (property->property_id == property_id)
881
if (property->property_id == property_id)
872
return 0;
882
return 0;
911
if (foreign_entry==NULL) continue;
921
if (foreign_entry==NULL) continue;
914
cache_trace ("Property: %s [%x] - old %x, new %x.\n", schema[entry->type][p].name,
924
cache_trace ("Property: %s [%x] - old value %x, new %x.\n", schema[entry->type][p].name,
915
local_cp, local_cp==NULL?0:local_cp->value->entry, foreign_entry);
925
local_cp, local_cp==NULL?0:local_cp->value->entry, foreign_entry);
917
if (local_cp==NULL || (foreign_entry!=local_cp->value->entry)) {
927
if (local_cp==NULL || (foreign_entry!=local_cp->value->entry)) {
953
// Don't do a cache check here. This function gets called once per entry as entries are checked
954
// in, so the cache could be in an inconsistent state because it's in the middle of being
956
//cache_check (self);
970
entry_ref (cache_entry->entry, "library::cache-lookup");
984
entry_ref (cache_entry->entry, "library::cache-lookup");
972
cache_trace ("cache-lookup: returning %s %i from cache.\n", ENTRY_PF(cache_entry->entry));
986
cache_trace ("cache-lookup: returning %s %i [%x] from cache.\n",
987
ENTRY_PF(cache_entry->entry), cache_entry->entry);
973
return cache_entry->entry;
988
return cache_entry->entry;
1080
cache_trace ("cache-store: stored %X %s %i in cache refs %i\n", entry, ENTRY_PF(entry),
1095
cache_trace ("cache-store: stored %X %s %i in cache refs %i\n", entry, ENTRY_PF(entry),
1081
entry->ref_count);
1096
entry->ref_count);
1083
entry_dump (entry);
1098
//entry_dump (entry);
1085
return cache_entry;
1100
return cache_entry;
1227
// FIXME: these functions could do with some tidying and organising
1242
// FIXME: these functions could do with some tidying and organising
1230
static char *make_property_string(Library *self, Entry *entry, int property) {
1245
static char *make_property_string_from_value(Library *self, PropertyType type, void *value) {
1231
switch(schema[entry->type][property].type) {
1246
switch (type) {
1232
case PROPERTY_TYPE_INT:
1247
case PROPERTY_TYPE_INT:
1233
return g_strdup_printf("%i", (int)entry->property[property]);
1248
return g_strdup_printf("%i", (int)value);
1235
case PROPERTY_TYPE_FLOAT: {
1250
case PROPERTY_TYPE_FLOAT: {
1236
//printf("Need to convert to float: %X", entry->property[property]);fflush(stdout);
1251
//printf("Need to convert to float: %X", entry->property[property]);fflush(stdout);
1237
float *value=(float *)(entry->property[property]);
1252
float *float_value=(float *)value;
1253
if (float_value==NULL)
1254
return g_strdup("0.0");
1255
return g_strdup_printf("%g", *float_value);
1258
case PROPERTY_TYPE_STRING: case PROPERTY_TYPE_NAME: {
1238
if (value==NULL)
1259
if (value==NULL)
1239
return g_strdup("0.0");
1240
return g_strdup_printf("%g", *value);
1243
case PROPERTY_TYPE_STRING: case PROPERTY_TYPE_NAME: {
1244
if (entry->property[property]==NULL)
1245
return NULL;
1260
return NULL;
1247
char *sqlite_string=sqlite3_mprintf("%Q", (const char *)entry->property[property]);
1262
char *sqlite_string=sqlite3_mprintf("%Q", (const char *)value);
1248
char *value=g_strdup(sqlite_string);
1263
char *value_string=g_strdup(sqlite_string);
1249
sqlite3_free(sqlite_string);
1264
sqlite3_free(sqlite_string);
1250
return value;
1265
return value_string;
1253
case PROPERTY_TYPE_DATE: {
1268
case PROPERTY_TYPE_DATE: {
1254
if (entry->property[property]==NULL || !g_date_valid(entry->property[property]))
1269
if (value==NULL || !g_date_valid(value))
1255
return NULL;
1270
return NULL;
1256
char *value=g_malloc(16);
1271
char *date_string=g_malloc(16);
1257
g_date_strftime(value, 15, "'%Y-%m-%d'", (GDate *)entry->property[property]);
1272
g_date_strftime(value, 15, "'%Y-%m-%d'", (GDate *)value);
1258
return value;
1273
return date_string;
1261
case PROPERTY_TYPE_DATE_TIME: {
1276
case PROPERTY_TYPE_DATE_TIME: {
1262
time_t *time_value = entry->property[property];
1277
time_t *time_value = value;
1263
if (time_value==NULL) return NULL;
1278
if (time_value==NULL) return NULL;
1264
struct tm brokentime; localtime_r(time_value, &brokentime);
1279
struct tm brokentime; localtime_r(time_value, &brokentime);
1265
char *value = g_malloc(32);
1280
char *date_time_string = g_malloc(32);
1266
strftime (value, 31, "'%Y-%m-%d %H:%M:%S'", &brokentime);
1281
strftime (date_time_string, 31, "'%Y-%m-%d %H:%M:%S'", &brokentime);
1267
return value;
1282
return date_time_string;
1270
default: {
1285
default: {
1271
Entry *child=entry->property[property];
1286
Entry *child = value;
1273
// This value can't be entered in the query at all if it is 0, because if the id
1288
// This value can't be entered in the query at all if it is 0, because if the id
1274
// has a value of 0 instead of NULL all our COALESCE calls no longer work
1289
// has a value of 0 instead of NULL all our COALESCE calls no longer work
1277
int entry_id;
1292
int entry_id;
1278
if (child->id!=0 && child->source==self)
1293
if (child->id!=0 && child->source==self)
1279
entry_id=child->id;
1294
entry_id=child->id;
1280
else entry_id = _music_source_find_entry(MUSIC_SOURCE(self), child, entry);
1295
else entry_id = _music_source_find_entry(MUSIC_SOURCE(self), child, child);
1282
return g_strdup_printf("%i", entry_id);
1297
return g_strdup_printf("%i", entry_id);
1302
static char *make_property_string (Library *self, Entry *entry, int property) {
1303
return make_property_string_from_value (self, schema[entry->type][property].type,
1304
entry->property[property]);
1288
// At the moment this makes the whole query (property=value) whereas
1307
// At the moment this makes the whole query (property=value) whereas
1289
// make_property_string just returns a value string, this is because where
1308
// make_property_string just returns a value string, this is because where
1478
sqlite3_bind_int (query, 1, entry_id);
1497
sqlite3_bind_int (query, 1, entry_id);
1479
int result = sqlite3_step (query);
1498
int result = sqlite3_step (query);
1500
#ifdef LIBRARY_TRACE_QUERIES
1501
printf("=> * %s\n", sqlite3_sql(query)); fflush(stdout);
1481
if (result==SQLITE_DONE) {
1504
if (result==SQLITE_DONE) {
1482
sqlite3_reset (query);
1505
sqlite3_reset (query);
1483
method_track_leave ("done library::fill-entry - entry not found\n");
1506
method_track_leave ("done library::fill-entry - entry not found\n");
1620
int columns) {
1643
int columns) {
1621
GSList *list = NULL;
1644
GSList *list = NULL;
1623
for (int i=0;i<rows;i++) {
1646
for (int i=0; i<rows; i++) {
1624
int pos = (i+1)*columns;
1647
Entry *entry = NULL;
1625
Entry *entry = fill_entry (self, entry_type, atoi(data[pos]), NULL);
1648
const int pos = (i+1) * columns;
1649
const int entry_id = atoi (data[pos]);
1651
entry = cache_lookup_entry(self, entry_type, entry_id);
1653
entry = fill_entry (self, entry_type, entry_id, NULL);
1626
list = g_slist_prepend(list, entry);
1654
list = g_slist_prepend(list, entry);
1898
/* update_entry_internal: re-store properties of 'entry' in the database. */
1926
/* update_entry_internal: re-store properties of 'entry' in the database.
1899
static void update_entry_internal (MusicSource *music_source, Entry *entry) {
1927
*
1928
* shadow_override: used in the offbeat situation where a property in this entry shadows
1929
* another entry which has changed, but the changes haven't yet been committed.
1930
* This actually happens quite often in musicsource.c:update_shadowers().
1932
static void update_entry_internal (MusicSource *music_source, Entry *entry, int shadow_override_id,
1933
void *shadow_override_value) {
1900
Library *self = LIBRARY(music_source);
1934
Library *self = LIBRARY(music_source);
1936
//entry_dump (entry);
1937
//printf ("shadow override: %i. val %x.\n", shadow_override_id, shadow_override_value); fflush (stdout);
1902
GString *assignments = g_string_new(NULL);
1939
GString *assignments = g_string_new(NULL);
1903
for (int i=0; i<entry_n_properties[entry->type]; i++) {
1940
for (int i=0; i<entry_n_properties[entry->type]; i++) {
1904
char *string = NULL;
1941
char *string = NULL;
1943
void *shadowee_value = NULL;
1906
if (schema[entry->type][i].flags & PROPERTY_SHADOW) {
1944
if (schema[entry->type][i].flags & PROPERTY_SHADOW) {
1907
// Any shadow properties which are the same as their shadowee are explicitly set to NULL
1945
// Any shadow properties which are the same as their shadowee are explicitly set to NULL
1908
// here. This means that when a shadow property that was independent starts shadowing
1946
// here. This means that when a shadow property that was independent starts shadowing
1909
// again, it is no longer stored and will mirror the shadowee's value again.
1947
// again, it is no longer stored and will mirror the shadowee's value again.
1910
const guint32 shadow_property_apid = schema[entry->type][i].shadow_property_apid;
1948
1911
void *shadowee_value = entry_get_distant_property (entry, shadow_property_apid);
1949
if (i==shadow_override_id)
1950
shadowee_value = shadow_override_value;
1952
const guint32 shadow_property_apid = schema[entry->type][i].shadow_property_apid;
1953
shadowee_value = entry_get_distant_property (entry, shadow_property_apid);
1912
if (entry_value_match(schema[entry->type][i].type, entry_get_property(entry, i),
1956
if (entry_value_match(schema[entry->type][i].type, entry_get_property(entry, i),
1913
shadowee_value))
1957
shadowee_value))
1914
string = g_strdup("NULL");
1958
string = g_strdup("NULL");
1917
if (string == NULL)
1961
if (string == NULL) {
1918
string = make_property_string(self, entry, i);
1962
/*if (shadowee_value != NULL)
1963
string = make_property_string_from_value (self, schema[entry->type][i].type,
1966
string = make_property_string (self, entry, i);
1920
if (string != NULL) {
1969
if (string != NULL) {
1921
g_string_append (assignments, ", ");
1970
g_string_append (assignments, ", ");
2008
// references those entries contain.
2057
// references those entries contain.
2010
GString *exceptions = g_string_new (NULL);
2059
GString *exceptions = g_string_new (NULL);
2011
for (GSList *q_node=SP->removal_queue; q_node; q_node=q_node->next) {
2060
// FIXME: the removal queue is actually ignored, because the will_be_unreffed flag
2061
// does the job well enough. Why does musicsourceimporting need to do this but library
2063
/* for (GSList *q_node=SP->removal_queue; q_node; q_node=q_node->next) {
2012
_RemovalQueueEntry *r = q_node->data;
2064
_RemovalQueueEntry *r = q_node->data;
2013
if (r->type==APID_GET_TYPE(apid))
2065
if (r->type==APID_GET_TYPE(apid))
2014
g_string_append_printf (exceptions, "AND id!=%i ", r->id);
2066
g_string_append_printf (exceptions, "AND id!=%i ", r->id);
2017
ref_count += db_expression_printf(self, NULL, "SELECT COUNT(id) FROM _%s WHERE %s_id=%i %s "
2069
ref_count += db_expression_printf(self, NULL, "SELECT COUNT(id) FROM _%s WHERE %s_id=%i %s "
2018
"LIMIT %i", entry_type_name[APID_GET_TYPE(apid)],
2070
"LIMIT %i", entry_type_name[APID_GET_TYPE(apid)],
2021
node = node->next;
2073
node = node->next;
2076
//printf ("library: Count_references: %s %i = %i\n", entry_type_name[type], id, ref_count);
2024
return ref_count;
2077
return ref_count;
2031
if (ENTRY_IS_SPECIAL(type, id))
2084
if (ENTRY_IS_SPECIAL(type, id))
2032
return;
2085
return;
2087
//printf ("touch: %s %i - d%i u%i.\n", entry_type_name[type], id,
2088
// delete, will_be_unreffed); fflush (stdout);
2034
if (delete) {
2090
if (delete) {
2035
// Search for entries that point to given entry (unless the link is flagged 'trivial') and
2091
// Search for entries that point to given entry (unless the link is flagged 'trivial') and
2036
// delete those. For example, if removing a recording this deletes all of its files.
2092
// delete those. For example, if removing a recording this deletes all of its files.
2085
(self, NULL, "SELECT %s_id FROM _%s WHERE id=%i",
2141
(self, NULL, "SELECT %s_id FROM _%s WHERE id=%i",
2086
schema[type][p].name, entry_type_name[type], id);
2142
schema[type][p].name, entry_type_name[type], id);
2087
if (foreign_id!=0 && (foreign_type!=caller_type || foreign_id!=caller_id))
2143
if (foreign_id!=0 && (foreign_type!=caller_type || foreign_id!=caller_id))
2088
touch (self, foreign_type, foreign_id, FALSE, delete, p_jetsam, type, id);
2144
touch (self, foreign_type, foreign_id, FALSE, delete /* FALSE */, p_jetsam, type, id);
2118
static void execute_removal_queue (Library *self) {
2174
static void execute_removal_queue (Library *self) {
2119
MusicSource *music_source = MUSIC_SOURCE(self);
2175
MusicSource *music_source = MUSIC_SOURCE(self);
2176
//printf ("Library::execute_removal_queue: %i entries.\n", g_slist_length(SP->removal_queue)); fflush (stdout);
2120
for (GSList *q_node=SP->removal_queue; q_node; q_node=q_node->next) {
2178
for (GSList *q_node=SP->removal_queue; q_node; q_node=q_node->next) {
2121
_RemovalQueueEntry *r = q_node->data;
2179
_RemovalQueueEntry *r = q_node->data;
2122
remove_entry_internal (music_source, r->type, r->id);
2180
remove_entry_internal (music_source, r->type, r->id);
2150
int result_id = result->id;
2208
int result_id = result->id;
2152
_music_source_emit_queued_notifications (music_source, NULL);
2210
if (_music_source_emit_queued_notifications (music_source, NULL)) {
2211
// Entries can be removed during an add, mostly by notify callbacks.
2212
execute_removal_queue (LIBRARY (music_source));
2213
//entry_destroy (entry);
2154
//entry_destroy (result);
2216
//entry_destroy (result);
2155
entry_unref (entry, "library::add_entry");
2217
entry_unref (entry, "library::add_entry");
2232
Entry *entry = query_entry(music_source, root_type, root_id);
2294
Entry *entry = query_entry(music_source, root_type, root_id);
2296
//printf ("library:checkout_entry: 1: removal queue length %i.\n", g_slist_length(LIBRARY(music_source)->priv->removal_queue)); fflush (stdout);
2234
if (entry != NULL)
2298
if (entry != NULL)
2235
_music_source_checkout_entry_recursive (music_source, entry);
2299
_music_source_checkout_entry_recursive (music_source, entry);
2301
//printf ("library:checkout_entry: 2: removal queue length %i.\n", g_slist_length(LIBRARY(music_source)->priv->removal_queue)); fflush (stdout);
2237
// No need to worry about the cache since it points directly to the entry that will be being
2303
// No need to worry about the cache since it points directly to the entry that will be being
2238
// modified, and it can't be queried while checked out anyway.
2304
// modified, and it can't be queried while checked out anyway.
2239
return entry;
2305
return entry;
2243
Library *self = LIBRARY(music_source);
2309
Library *self = LIBRARY(music_source);
2244
GSList *jetsam = NULL;
2310
GSList *jetsam = NULL;
2312
//printf ("library:checkin_entry: 1: removal queue length %i.\n", g_slist_length(SP->removal_queue)); fflush (stdout);
2246
Entry *result = _music_source_checkin_entry_recursive (music_source, entry, &jetsam, entry);
2314
Entry *result = _music_source_checkin_entry_recursive (music_source, entry, &jetsam, entry);
2316
//printf ("library:checkin_entry: 2: removal queue length %i.\n", g_slist_length(SP->removal_queue)); fflush (stdout);
2248
// Original entry is unreffed in music_source_checkin_entry
2318
// Original entry is unreffed in music_source_checkin_entry
2249
if (entry != result)
2319
if (entry != result)
2250
entry_unref (result, "_music_source_checkin_entry_recursive");
2320
entry_unref (result, "_music_source_checkin_entry_recursive");
2256
g_slist_free (jetsam);
2326
g_slist_free (jetsam);
2328
//printf ("end of checkin %s %i.\n", ENTRY_PF(entry)); fflush (stdout);
2258
if (_music_source_emit_queued_notifications (music_source, NULL)) {
2329
if (_music_source_emit_queued_notifications (music_source, NULL)) {
2259
execute_removal_queue (self);
2330
execute_removal_queue (self);
2260
entry_destroy (entry);
2331
//entry_destroy (entry);
152
g_assert (id == 1);
152
g_assert (id == 1);
153
g_assert (test_state == TEST_CHANGING); // Callback should have advanced state
153
g_assert (test_state == TEST_CHANGING); // Callback should have advanced state
155
printf ("\n\n\n\n\n\n\nPart 3.\n\n"); fflush (stdout);
156
artist = music_source_checkout_entry(source, ENTRY_TYPE_ARTIST, 3, "test");
155
artist = music_source_checkout_entry(source, ENTRY_TYPE_ARTIST, 3, "test");
157
entry_set_property (artist, ARTIST_NAME, "Robot Faces");
156
entry_set_property (artist, ARTIST_NAME, "Robot Faces");
158
music_source_checkin_entry (source, artist, "test");
157
music_source_checkin_entry (source, artist, "test");
250
music_source_end_transaction (source);
249
music_source_end_transaction (source);
252
music_source_connect_entry_notify (source, -1, TRUE, notify, NULL);
251
music_source_connect_entry_notify (source, -1, TRUE, notify, NULL);
253
printf("\n\n\n\n\n\n\n\n\nIt starts!\n\n"); fflush (stdout);
254
music_source_remove_entry (source, ENTRY_TYPE_FILE, 1);
252
music_source_remove_entry (source, ENTRY_TYPE_FILE, 1);
255
g_assert_cmpint (notify_received, ==, 1);
253
g_assert_cmpint (notify_received, ==, 1);
394
test_add_song (source, 1, 1, 2, 1, 1);
392
test_add_song (source, 1, 1, 2, 1, 1);
395
music_source_end_transaction (source);
393
music_source_end_transaction (source);
397
// Check out the same entry twice in two seperate checkouts!
395
// Check out the same entry twice in two seperate checkouts! (The two files have the same
396
// recording and composition).
398
Entry *file_1 = music_source_checkout_entry (source, ENTRY_TYPE_FILE, 1, "test");
397
Entry *file_1 = music_source_checkout_entry (source, ENTRY_TYPE_FILE, 1, "test");
399
g_assert (file_1!=NULL);
398
g_assert (file_1!=NULL);
401
Entry *file_2 = music_source_checkout_entry (source, ENTRY_TYPE_FILE, 2, "test");
400
Entry *file_2 = music_source_checkout_entry (source, ENTRY_TYPE_FILE, 2, "test");
402
g_assert (file_2!=NULL);
401
g_assert (file_2!=NULL);
404
// Set a new recording for one of them.
403
// Set a new recording for one of the files.
405
Entry *old_recording = entry_get_property (file_1, FILE_RECORDING),
404
Entry *old_recording = entry_get_property (file_1, FILE_RECORDING),
406
*old_composition = entry_get_property (old_recording, RECORDING_COMPOSITION),
405
*old_composition = entry_get_property (old_recording, RECORDING_COMPOSITION),
407
*new_recording = entry_new (ENTRY_TYPE_RECORDING, 0, NULL, "test");
406
*new_recording = entry_new (ENTRY_TYPE_RECORDING, 0, NULL, "test");
410
entry_take_property (file_1, FILE_RECORDING, new_recording);
409
entry_take_property (file_1, FILE_RECORDING, new_recording);
412
// Here a poor implementation might confuse itself terribly.
411
// Here a poor implementation might confuse itself terribly. file 2 is still checked out and its
412
// recording is still recording 1.
413
music_source_checkin_entry (source, file_1, "test");
413
music_source_checkin_entry (source, file_1, "test");
415
// Check file 1 hasn't been mangled already.
416
file_1 = music_source_query_entry (source, ENTRY_TYPE_FILE, 1, "test");
417
entry_check (file_1);
418
entry_unref (file_1, "test");
415
Entry *composition_2 = entry_get_distant_property(file_2, _RECORDING_COMPOSITION);
420
Entry *composition_2 = entry_get_distant_property(file_2, _RECORDING_COMPOSITION);
417
// Let's do some shadowing to make things even harder.
422
// Let's do some shadowing to make things even harder.
418
entry_set_property (composition_2, COMPOSITION_NAME, "Test Test");
423
entry_set_property (composition_2, COMPOSITION_NAME, "Test Test");
419
music_source_checkin_entry (source, file_2, "test");
425
music_source_checkin_entry (source, file_2, "test");
421
g_object_unref (source);
427
g_object_unref (source);
464
Entry *artist_1, *artist_2;
470
Entry *artist_1, *artist_2;
466
void assert_state(gboolean artist_two_sortname_changed,
472
void assert_state(gboolean artist_two_sortname_changed, gboolean artist_names_changed,
467
gboolean artist_names_changed,
473
gboolean recording_2_artist_changed) {
468
gboolean recording_2_artist_changed) {
469
// Test that shadow properties are filled properly.
474
// Test that shadow properties are filled properly.
470
artist_1 = music_source_query_entry(source, ENTRY_TYPE_ARTIST, 3, "test");
475
artist_1 = music_source_query_entry(source, ENTRY_TYPE_ARTIST, 3, "test");
471
g_assert_cmpstr (entry_get_property(artist_1, ARTIST_NAME), ==,
476
g_assert_cmpstr (entry_get_property(artist_1, ARTIST_NAME), ==,
523
entry_set_property(artist_2, ARTIST_SORTNAME, "Two, Test Artist");
528
entry_set_property(artist_2, ARTIST_SORTNAME, "Two, Test Artist");
524
music_source_checkin_entry(source, artist_2, "test");
529
music_source_checkin_entry(source, artist_2, "test");
531
//printf ("\nArtist 2 sortname changed, no longer shadowing."); fflush (stdout);
526
assert_state(TRUE, FALSE, FALSE);
532
assert_state(TRUE, FALSE, FALSE);
527
music_source_flush (source);
533
music_source_flush (source);
534
//printf ("\nFlushed cache (Artist 2 sortname no longer shadowing)"); fflush (stdout);
528
assert_state(TRUE, FALSE, FALSE);
535
assert_state(TRUE, FALSE, FALSE);
530
// Change the names of both artists. Artist 1's sortname should follow, since it's still
537
// Change the names of both artists. Artist 1's sortname should follow, since it's still
537
entry_set_property(artist_2, ARTIST_NAME, "Test Artist Two");
544
entry_set_property(artist_2, ARTIST_NAME, "Test Artist Two");
538
music_source_checkin_entry(source, artist_2, "test");
545
music_source_checkin_entry(source, artist_2, "test");
547
//printf ("\nBoth artist names changed."); fflush (stdout);
540
assert_state(TRUE, TRUE, FALSE);
548
assert_state(TRUE, TRUE, FALSE);
541
/*music_source_flush (source);
549
music_source_flush (source);
550
//printf ("\nFlushed cache (Both artist names changed)"); fflush (stdout);
542
assert_state(TRUE, TRUE, FALSE);
551
assert_state(TRUE, TRUE, FALSE);
544
// Make song two be recorded by a different artist to its composer.
553
// Make song two be recorded by a different artist to its composer.
551
assert_state(TRUE, TRUE, TRUE);
560
assert_state(TRUE, TRUE, TRUE);
552
music_source_flush (source);
561
music_source_flush (source);
553
assert_state(TRUE, TRUE, TRUE);*/
562
assert_state(TRUE, TRUE, TRUE);
555
g_object_unref (source);
564
g_object_unref (source);
556
_entry_cleanup();
565
_entry_cleanup();
607
// Remove file 1 and see that file 2 is now the default.
616
// Remove file 1 and see that file 2 is now the default.
608
music_source_remove_entry (source, ENTRY_TYPE_FILE, 1);
617
music_source_remove_entry (source, ENTRY_TYPE_FILE, 1);
609
recording = music_source_query_entry(source, ENTRY_TYPE_RECORDING, 1, "test");
618
recording = music_source_query_entry (source, ENTRY_TYPE_RECORDING, 1, "test");
619
g_assert (recording != NULL);
610
default_file = entry_get_property(recording, RECORDING_DEFAULT_FILE);
621
default_file = entry_get_property(recording, RECORDING_DEFAULT_FILE);
611
g_assert (default_file != NULL);
622
g_assert (default_file != NULL);
612
g_assert_cmpint (default_file->id, ==, 2);
623
g_assert_cmpint (default_file->id, ==, 2);
632
g_assert_cmpint (album_artist->id, ==, ARTIST_ID_VARIOUS);
643
g_assert_cmpint (album_artist->id, ==, ARTIST_ID_VARIOUS);
633
entry_unref (album, "test");
644
entry_unref (album, "test");
646
// And, there should be only one album entry.
647
g_assert_cmpint (music_source_get_n_entries (source, ENTRY_TYPE_ALBUM, NULL, NULL), ==, 1);
650
// Give track 2 the same artist name as track 1. Now artists 3 & 4 should merge, and
651
// the album name should change from 'Various Artists' back to 'Test Artist 000002'.
635
Entry *track = music_source_checkout_entry (source, ENTRY_TYPE_TRACK, 2, "test"),
652
Entry *track = music_source_checkout_entry (source, ENTRY_TYPE_TRACK, 2, "test"),
636
*recording = entry_get_property (track, TRACK_RECORDING),
653
*recording = entry_get_property (track, TRACK_RECORDING),
637
*artist = entry_get_property (recording, RECORDING_ARTIST);
654
*artist = entry_get_property (recording, RECORDING_ARTIST);
639
entry_set_property (artist, ARTIST_NAME, "Test Artist 000002");
656
entry_set_property (artist, ARTIST_NAME, "Test Artist 000002");
640
music_source_checkin_entry (source, track, "test");
657
music_source_checkin_entry (source, track, "test");
642
// Check that the merging went okay. Track 2's artist should now be #3 - #4 should have been
659
// Check that the artists merged, now that their names are the same.
643
// merged with #3 on checkin.
644
recording = music_source_query_entry (source, ENTRY_TYPE_RECORDING, 2, "test");
660
recording = music_source_query_entry (source, ENTRY_TYPE_RECORDING, 2, "test");
645
artist = entry_get_property (recording, RECORDING_ARTIST);
661
artist = entry_get_property (recording, RECORDING_ARTIST);
646
g_assert_cmpint (artist->id, ==, 3);
662
g_assert_cmpint (artist->id, ==, 3);
647
entry_unref (recording, "test");
663
entry_unref (recording, "test");
649
// Now they are both the same artist. The album artist should change from 'Various Artists'
665
// Check that the album has changed from VA back to being a single artist.
650
// back to 'Test Artist 000002'.
651
album = music_source_query_entry (source, ENTRY_TYPE_ALBUM, 1, "test");
666
album = music_source_query_entry (source, ENTRY_TYPE_ALBUM, 1, "test");
652
album_artist = entry_get_property (album, ALBUM_ARTIST);
667
album_artist = entry_get_property (album, ALBUM_ARTIST);
653
g_assert_cmpint (album_artist->id, ==, 3);
668
g_assert_cmpint (album_artist->id, ==, 3);
669
g_test_add_func(PATH_PRINTF("/%s/Notify 1", root), test_notify_1);
684
g_test_add_func(PATH_PRINTF("/%s/Notify 1", root), test_notify_1);
670
g_test_add_func(PATH_PRINTF("/%s/Notify 2", root), test_notify_2);
685
g_test_add_func(PATH_PRINTF("/%s/Notify 2", root), test_notify_2);
671
g_test_add_func(PATH_PRINTF("/%s/Notify 3", root), test_notify_3);
686
g_test_add_func(PATH_PRINTF("/%s/Notify 3", root), test_notify_3);
672
//g_test_add_func(PATH_PRINTF("/%s/Notify 4", root), test_notify_4);
687
g_test_add_func(PATH_PRINTF("/%s/Notify 4", root), test_notify_4);
673
g_test_add_func(PATH_PRINTF("/%s/Removal", root), test_removal);
688
g_test_add_func(PATH_PRINTF("/%s/Removal", root), test_removal);
674
g_test_add_func(PATH_PRINTF("/%s/Removal Notify", root), test_removal_notify);
689
g_test_add_func(PATH_PRINTF("/%s/Removal Notify", root), test_removal_notify);
675
g_test_add_func(PATH_PRINTF("/%s/Pruning 1", root), test_pruning_1);
690
g_test_add_func(PATH_PRINTF("/%s/Pruning 1", root), test_pruning_1);
676
//g_test_add_func(PATH_PRINTF("/%s/Pruning 2", root), test_pruning_2);
691
g_test_add_func(PATH_PRINTF("/%s/Pruning 2", root), test_pruning_2);
677
g_test_add_func(PATH_PRINTF("/%s/Pruning 3", root), test_pruning_3);
692
g_test_add_func(PATH_PRINTF("/%s/Pruning 3", root), test_pruning_3);
678
g_test_add_func(PATH_PRINTF("/%s/Shadowing", root), test_shadowing);
693
g_test_add_func(PATH_PRINTF("/%s/Shadowing", root), test_shadowing);
679
g_test_add_func(PATH_PRINTF("/%s/Relations", root), test_relations);
694
g_test_add_func(PATH_PRINTF("/%s/Relations", root), test_relations);
680
//g_test_add_func(PATH_PRINTF("/%s/Recording Watch", root), test_recording_watch);
695
g_test_add_func(PATH_PRINTF("/%s/Recording Watch", root), test_recording_watch);
681
//g_test_add_func(PATH_PRINTF("/%s/Album Watch 1", root), test_album_watch_1);
696
g_test_add_func(PATH_PRINTF("/%s/Album Watch 1", root), test_album_watch_1);