XMMS2
src/xmms/collserial.c
Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2011 XMMS2 Team
00003  *
00004  *  PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2.1 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  */
00016 
00017 
00018 /** @file
00019  *  Functions to serialize (save/restore) collections.
00020  */
00021 
00022 #include "xmmspriv/xmms_collserial.h"
00023 #include "xmmspriv/xmms_collection.h"
00024 #include "xmmspriv/xmms_medialib.h"
00025 
00026 
00027 /* Internal helper structures */
00028 
00029 typedef struct {
00030     xmms_medialib_session_t *session;
00031     guint collid;
00032     xmms_collection_namespace_id_t nsid;
00033 } coll_dbwrite_t;
00034 
00035 
00036 static xmmsv_coll_t *xmms_collection_dbread_operator (xmms_medialib_session_t *session, gint id, xmmsv_coll_type_t type);
00037 static guint xmms_collection_dbwrite_operator (xmms_medialib_session_t *session, guint collid, xmmsv_coll_t *coll);
00038 
00039 static void dbwrite_operator (void *key, void *value, void *udata);
00040 static void dbwrite_coll_attributes (const char *key, xmmsv_t *value, void *udata);
00041 static void dbwrite_strip_tmpprops (void *key, void *value, void *udata);
00042 
00043 static gint value_get_dict_int (xmmsv_t *val, const gchar *key);
00044 static const gchar *value_get_dict_string (xmmsv_t *val, const gchar *key);
00045 
00046 
00047 
00048 /** Save the collection DAG in the database.
00049  *
00050  * @param dag  The collection DAG to save.
00051  */
00052 void
00053 xmms_collection_dag_save (xmms_coll_dag_t *dag)
00054 {
00055     gint i;
00056     xmms_medialib_session_t *session;
00057 
00058     session = xmms_medialib_begin_write ();
00059 
00060     /* Empty Collection* tables */
00061     xmms_medialib_select (session, "DELETE FROM CollectionAttributes", NULL);
00062     xmms_medialib_select (session, "DELETE FROM CollectionConnections", NULL);
00063     xmms_medialib_select (session, "DELETE FROM CollectionIdlists", NULL);
00064     xmms_medialib_select (session, "DELETE FROM CollectionLabels", NULL);
00065     xmms_medialib_select (session, "DELETE FROM CollectionOperators", NULL);
00066 
00067     /* Write all collections in all namespaces */
00068     coll_dbwrite_t dbinfos = { session, 1, 0 }; /* ids start at 1 */
00069     for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
00070         dbinfos.nsid = i;
00071         xmms_collection_foreach_in_namespace (dag, i, dbwrite_operator, &dbinfos);
00072     }
00073 
00074     xmms_collection_foreach_in_namespace (dag, XMMS_COLLECTION_NSID_ALL,
00075                                           dbwrite_strip_tmpprops, NULL);
00076 
00077     xmms_medialib_end (session);
00078 }
00079 
00080 /** Restore the collection DAG from the database.
00081  *
00082  * @param dag  The collection DAG to restore to.
00083  */
00084 void
00085 xmms_collection_dag_restore (xmms_coll_dag_t *dag)
00086 {
00087     xmmsv_coll_t *coll = NULL;
00088     xmms_medialib_session_t *session;
00089     xmmsv_t *cmdval;
00090     const gchar *query;
00091     GList *res;
00092     gint previd;
00093 
00094     session = xmms_medialib_begin ();
00095 
00096     /* Fetch all label-coll_operator for all namespaces, register in table */
00097     query = "SELECT op.id AS id, lbl.name AS label, "
00098             "       lbl.namespace AS nsid, op.type AS type "
00099             "FROM CollectionOperators AS op, CollectionLabels as lbl "
00100             "WHERE op.id=lbl.collid "
00101             "ORDER BY id";
00102     res = xmms_medialib_select (session, query, NULL);
00103 
00104     previd = -1;
00105 
00106     while (res) {
00107         gint id, type, nsid;
00108         const gchar *label;
00109 
00110         cmdval = (xmmsv_t*) res->data;
00111         id = value_get_dict_int (cmdval, "id");
00112         type = value_get_dict_int (cmdval, "type");
00113         nsid = value_get_dict_int (cmdval, "nsid");
00114         label = value_get_dict_string (cmdval, "label");
00115 
00116         /* Do not duplicate operator if same id */
00117         if (previd < 0 || id != previd) {
00118             coll = xmms_collection_dbread_operator (session, id, type);
00119             previd = id;
00120         }
00121         else {
00122             xmmsv_coll_ref (coll);  /* New label references the coll */
00123         }
00124 
00125         xmms_collection_dag_replace (dag, nsid, g_strdup (label), coll);
00126 
00127         xmmsv_unref (cmdval);
00128         res = g_list_delete_link (res, res);
00129     }
00130 
00131     xmms_medialib_end (session);
00132 
00133     /* FIXME: validate ? */
00134 
00135     /* Link references in collections to actual operators */
00136     xmms_collection_apply_to_all_collections (dag, bind_all_references, NULL);
00137 }
00138 
00139 /** Given a collection id, query the DB to build the corresponding
00140  *  collection DAG.
00141  *
00142  * @param session  The medialib session connected to the DB.
00143  * @param id  The id of the collection to create.
00144  * @param type  The type of the collection operator.
00145  * @return  The created collection DAG.
00146  */
00147 static xmmsv_coll_t *
00148 xmms_collection_dbread_operator (xmms_medialib_session_t *session,
00149                                  gint id, xmmsv_coll_type_t type)
00150 {
00151     xmmsv_coll_t *coll;
00152     xmmsv_coll_t *op;
00153     GList *res;
00154     GList *n;
00155     xmmsv_t *cmdval;
00156     gchar query[256];
00157 
00158     coll = xmmsv_coll_new (type);
00159 
00160     /* Retrieve the attributes */
00161     g_snprintf (query, sizeof (query),
00162                 "SELECT attr.key AS key, attr.value AS value "
00163                 "FROM CollectionOperators AS op, CollectionAttributes AS attr "
00164                 "WHERE op.id=%d AND attr.collid=op.id", id);
00165 
00166     res = xmms_medialib_select (session, query, NULL);
00167     for (n = res; n; n = n->next) {
00168         const gchar *key, *value;
00169 
00170         cmdval = (xmmsv_t*) n->data;
00171         key = value_get_dict_string (cmdval, "key");
00172         value = value_get_dict_string (cmdval, "value");
00173         xmmsv_coll_attribute_set (coll, key, value);
00174 
00175         xmmsv_unref (n->data);
00176     }
00177     g_list_free (res);
00178 
00179     /* Retrieve the idlist */
00180     g_snprintf (query, sizeof (query),
00181                 "SELECT idl.mid AS mid "
00182                 "FROM CollectionOperators AS op, CollectionIdlists AS idl "
00183                 "WHERE op.id=%d AND idl.collid=op.id "
00184                 "ORDER BY idl.position", id);
00185 
00186     res = xmms_medialib_select (session, query, NULL);
00187     for (n = res; n; n = n->next) {
00188 
00189         cmdval = (xmmsv_t *) n->data;
00190         xmmsv_coll_idlist_append (coll, value_get_dict_int (cmdval, "mid"));
00191 
00192         xmmsv_unref (cmdval);
00193     }
00194     g_list_free (res);
00195 
00196     /* Retrieve the operands */
00197     g_snprintf (query, sizeof (query),
00198                 "SELECT op.id AS id, op.type AS type "
00199                 "FROM CollectionOperators AS op, CollectionConnections AS conn "
00200                 "WHERE conn.to_id=%d AND conn.from_id=op.id", id);
00201 
00202     res = xmms_medialib_select (session, query, NULL);
00203     for (n = res; n; n = n->next) {
00204         gint _id;
00205         gint type;
00206 
00207         cmdval = (xmmsv_t *) n->data;
00208         _id = value_get_dict_int (cmdval, "id");
00209         type = value_get_dict_int (cmdval, "type");
00210 
00211         op = xmms_collection_dbread_operator (session, _id, type);
00212         xmmsv_coll_add_operand (coll, op);
00213 
00214         xmmsv_coll_unref (op);
00215         xmmsv_unref (cmdval);
00216     }
00217     g_list_free (res);
00218 
00219     return coll;
00220 }
00221 
00222 /** Write the given operator to the database under the given id.
00223  *
00224  * @param session  The medialib session connected to the DB.
00225  * @param collid  The id under which to save the collection.
00226  * @param coll  The structure of the collection to save.
00227  * @return  The next free collection id.
00228  */
00229 static guint
00230 xmms_collection_dbwrite_operator (xmms_medialib_session_t *session,
00231                                   guint collid, xmmsv_coll_t *coll)
00232 {
00233     gchar query[128];
00234     xmms_medialib_entry_t entry;
00235     xmmsv_list_iter_t *it;
00236     gint i;
00237     xmmsv_coll_t *op;
00238     xmmsv_t *attrs;
00239     gint newid, nextid;
00240     coll_dbwrite_t dbwrite_infos = { session, collid, 0 };
00241 
00242     /* Write operator */
00243     g_snprintf (query, sizeof (query),
00244                 "INSERT INTO CollectionOperators VALUES(%d, %d)",
00245                 collid, xmmsv_coll_get_type (coll));
00246 
00247     xmms_medialib_select (session, query, NULL);
00248 
00249     /* Write attributes */
00250     attrs = xmmsv_coll_attributes_get (coll);
00251     xmmsv_dict_foreach (attrs, dbwrite_coll_attributes, &dbwrite_infos);
00252     attrs = NULL; /* no unref needed. */
00253 
00254     /* Write idlist */
00255     xmmsv_get_list_iter (xmmsv_coll_idlist_get (coll), &it);
00256     for (xmmsv_list_iter_first (it), i = 0;
00257          xmmsv_list_iter_valid (it);
00258          xmmsv_list_iter_next (it), i++) {
00259 
00260         xmmsv_list_iter_entry_int (it, &entry);
00261         g_snprintf (query, sizeof (query),
00262                     "INSERT INTO CollectionIdlists VALUES(%d, %d, %d)",
00263                     collid, i, entry);
00264 
00265         xmms_medialib_select (session, query, NULL);
00266     }
00267     xmmsv_list_iter_explicit_destroy (it);
00268 
00269     /* Save operands and connections (don't recurse in ref operand) */
00270     newid = collid + 1;
00271     if (xmmsv_coll_get_type (coll) != XMMS_COLLECTION_TYPE_REFERENCE) {
00272         xmmsv_t *tmp;
00273         xmmsv_list_iter_t *iter;
00274 
00275         xmmsv_get_list_iter (xmmsv_coll_operands_get (coll), &iter);
00276 
00277         for (xmmsv_list_iter_first (iter);
00278              xmmsv_list_iter_valid (iter);
00279              xmmsv_list_iter_next (iter)) {
00280 
00281             xmmsv_list_iter_entry (iter, &tmp);
00282             xmmsv_get_coll (tmp, &op);
00283 
00284             nextid = xmms_collection_dbwrite_operator (session, newid, op);
00285             g_snprintf (query, sizeof (query),
00286                         "INSERT INTO CollectionConnections VALUES(%d, %d)",
00287                         newid, collid);
00288             xmms_medialib_select (session, query, NULL);
00289             newid = nextid;
00290         }
00291         xmmsv_list_iter_explicit_destroy (iter);
00292     }
00293 
00294     /* return next available id */
00295     return newid;
00296 }
00297 
00298 /* For all label-operator pairs, write the operator and all its
00299  * operands to the DB recursively. */
00300 static void
00301 dbwrite_operator (void *key, void *value, void *udata)
00302 {
00303     gchar *query;
00304     gchar *label = key;
00305     xmmsv_coll_t *coll = value;
00306     coll_dbwrite_t *dbinfos = udata;
00307     gchar *esc_label;
00308     gint serial_id;
00309 
00310     /* Only serialize each operator once, get previous id if exists */
00311     if (!xmms_collection_get_int_attr (coll, XMMS_COLLSERIAL_ATTR_ID, &serial_id)) {
00312         serial_id = dbinfos->collid;
00313         dbinfos->collid = xmms_collection_dbwrite_operator (dbinfos->session,
00314                                                             dbinfos->collid, coll);
00315         xmms_collection_set_int_attr (coll, XMMS_COLLSERIAL_ATTR_ID, serial_id);
00316     }
00317 
00318     esc_label = sqlite_prepare_string (label);
00319     query = g_strdup_printf ("INSERT INTO CollectionLabels VALUES(%d, %d, %s)",
00320                              serial_id, dbinfos->nsid, esc_label);
00321     xmms_medialib_select (dbinfos->session, query, NULL);
00322 
00323     g_free (query);
00324     g_free (esc_label);
00325 }
00326 
00327 /* Write all attributes of a collection to the DB. */
00328 static void
00329 dbwrite_coll_attributes (const char *key, xmmsv_t *value, void *udata)
00330 {
00331     gchar *query;
00332     coll_dbwrite_t *dbwrite_infos = udata;
00333     gchar *esc_key;
00334     gchar *esc_val;
00335     const gchar *s;
00336     int r;
00337 
00338     r = xmmsv_get_string (value, &s);
00339     g_return_if_fail (r);
00340 
00341     esc_key = sqlite_prepare_string (key);
00342     esc_val = sqlite_prepare_string (s);
00343     query = g_strdup_printf ("INSERT INTO CollectionAttributes VALUES(%d, %s, %s)",
00344                              dbwrite_infos->collid, esc_key, esc_val);
00345     xmms_medialib_select (dbwrite_infos->session, query, NULL);
00346 
00347     g_free (query);
00348     g_free (esc_key);
00349     g_free (esc_val);
00350 }
00351 
00352 /* Remove all temp utility properties used to write collections to the DB. */
00353 static void
00354 dbwrite_strip_tmpprops (void *key, void *value, void *udata)
00355 {
00356     xmmsv_coll_t *coll = value;
00357     xmmsv_coll_attribute_remove (coll, XMMS_COLLSERIAL_ATTR_ID);
00358 }
00359 
00360 
00361 /* Extract the int value out of a xmmsv_t object. */
00362 static gint
00363 value_get_dict_int (xmmsv_t *val, const gchar *key)
00364 {
00365     gint i;
00366     xmmsv_dict_entry_get_int (val, key, &i);
00367     return i;
00368 }
00369 
00370 /* Extract the string value out of a xmmsv_t object. */
00371 static const gchar *
00372 value_get_dict_string (xmmsv_t *val, const gchar *key)
00373 {
00374     const gchar *s;
00375     xmmsv_dict_entry_get_string (val, key, &s);
00376     return s;
00377 }