• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

src/xmms/sqlite.c

Go to the documentation of this file.
00001 /*  XMMS2 - X Music Multiplexer System
00002  *  Copyright (C) 2003-2009 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 /** @file
00018  * Sqlite Backend.
00019  */
00020 
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 
00024 #include "xmms/xmms_config.h"
00025 #include "xmms/xmms_log.h"
00026 #include "xmmspriv/xmms_sqlite.h"
00027 #include "xmmspriv/xmms_statfs.h"
00028 #include "xmmspriv/xmms_utils.h"
00029 #include "xmmspriv/xmms_collection.h"
00030 #include "xmmsc/xmmsc_idnumbers.h"
00031 
00032 #include <sqlite3.h>
00033 #include <string.h>
00034 #include <glib.h>
00035 
00036 /* increment this whenever there are incompatible db structure changes */
00037 #define DB_VERSION 35
00038 
00039 const char set_version_stm[] = "PRAGMA user_version=" XMMS_STRINGIFY (DB_VERSION);
00040 const char create_Media_stm[] = "create table Media (id integer, key, value, source integer)";
00041 const char create_Sources_stm[] = "create table Sources (id integer primary key AUTOINCREMENT, source)";
00042 const char create_Playlist_stm[] = "create table Playlist (id primary key, name, pos integer)";
00043 const char create_PlaylistEntries_stm[] = "create table PlaylistEntries (playlist_id int, entry, pos integer primary key AUTOINCREMENT)";
00044 const char create_CollectionAttributes_stm[] = "create table CollectionAttributes (collid integer, key text, value text)";
00045 const char create_CollectionConnections_stm[] = "create table CollectionConnections (from_id integer, to_id integer)";
00046 const char create_CollectionIdlists_stm[] = "create table CollectionIdlists (collid integer, position integer, mid integer)";
00047 const char create_CollectionLabels_stm[] = "create table CollectionLabels (collid integer, namespace integer, name text)";
00048 const char create_CollectionOperators_stm[] = "create table CollectionOperators (id integer primary key AUTOINCREMENT, type integer)";
00049 
00050 /**
00051  * This magic numbers are taken from ANALYZE on a big database, if we change the db
00052  * layout drasticly we need to redo them!
00053  */
00054 const char fill_stats[] = "INSERT INTO sqlite_stat1 VALUES('Media', 'key_idx', '199568 14 1 1');"
00055                           "INSERT INTO sqlite_stat1 VALUES('Media', 'prop_idx', '199568 6653 3');"
00056                           "INSERT INTO sqlite_stat1 VALUES('PlaylistEntries', 'playlistentries_idx', '12784 12784 1');"
00057                           "INSERT INTO sqlite_stat1 VALUES('Playlist', 'playlist_idx', '2 1');"
00058                           "INSERT INTO sqlite_stat1 VALUES('Playlist', 'sqlite_autoindex_Playlist_1', '2 1');"
00059                           "INSERT INTO sqlite_stat1 VALUES('CollectionLabels', 'collectionlabels_idx', '2 2');"
00060                           "INSERT INTO sqlite_stat1 VALUES('CollectionIdlists', 'collectionidlists_idx', '15 15 1');"
00061                           "INSERT INTO sqlite_stat1 VALUES('CollectionAttributes', 'collectionattributes_idx', '2 2 1');";
00062 
00063 const char fill_init_playlist_stm[] = "INSERT INTO CollectionOperators VALUES(1, %d);"
00064                                       "INSERT INTO CollectionLabels VALUES(1, %d, 'Default');"
00065                                       "INSERT INTO CollectionLabels VALUES(1, %d, '" XMMS_ACTIVE_PLAYLIST "');"
00066                                       "INSERT INTO CollectionIdlists VALUES(1, 1, 1);";
00067 
00068 const char create_idx_stm[] = "create unique index key_idx on Media (id,key,source);"
00069                               "CREATE INDEX id_key_value_1x ON Media (id, key, value COLLATE BINARY);"
00070                               "CREATE INDEX id_key_value_2x ON Media (id, key, value COLLATE NOCASE);"
00071                               "CREATE INDEX key_value_1x ON Media (key, value COLLATE BINARY);"
00072                               "CREATE INDEX key_value_2x ON Media (key, value COLLATE NOCASE);"
00073                               "create index playlistentries_idx on PlaylistEntries (playlist_id, entry);"
00074                               "create index playlist_idx on Playlist (name);";
00075 
00076 const char create_collidx_stm[] = "create unique index collectionconnections_idx on CollectionConnections (from_id, to_id);"
00077                                   "create unique index collectionattributes_idx on CollectionAttributes (collid, key);"
00078                                   "create unique index collectionidlists_idx on CollectionIdlists (collid, position);"
00079                                   "create index collectionlabels_idx on CollectionLabels (collid);";
00080 
00081 /**
00082  * @defgroup SQLite SQLite
00083  * @ingroup XMMSServer
00084  * @brief The SQLite backend of medialib
00085  * @{
00086  */
00087 
00088 static int
00089 xmms_sqlite_version_cb (void *pArg, int argc, char **argv, char **columnName)
00090 {
00091     guint *id = pArg;
00092 
00093     if (argv[0]) {
00094         *id = atoi (argv[0]);
00095     } else {
00096         *id = 0;
00097     }
00098 
00099     return 0;
00100 }
00101 
00102 static int
00103 xmms_sqlite_integer_coll (void *udata, int len1, const void *str1, int len2, const void *str2)
00104 {
00105     guint32 a, b;
00106     a = strtol (str1, NULL, 10);
00107     b = strtol (str2, NULL, 10);
00108     if (a < b) return -1;
00109     if (a == b) return 0;
00110     return 1;
00111 }
00112 
00113 static void
00114 upgrade_v26_to_v27 (sqlite3 *sql)
00115 {
00116     XMMS_DBG ("Upgrade v26->v27");
00117     sqlite3_exec (sql,
00118                   "drop view albums;"
00119                   "drop view artists;"
00120                   "drop view compilations;"
00121                   "drop view songs;",
00122                   NULL, NULL, NULL);
00123 
00124     XMMS_DBG ("done");
00125 }
00126 
00127 static void
00128 upgrade_v27_to_v28 (sqlite3 *sql)
00129 {
00130     XMMS_DBG ("Upgrade v27->v28");
00131 
00132     sqlite3_exec (sql,
00133                   "drop table Log;",
00134                   NULL, NULL, NULL);
00135 
00136     XMMS_DBG ("done");
00137 }
00138 
00139 static void
00140 upgrade_v28_to_v29 (sqlite3 *sql)
00141 {
00142     XMMS_DBG ("Upgrade v28->v29");
00143 
00144     sqlite3_exec (sql, "delete from Media where source in"
00145                   "(select id from Sources where source like 'plugin%')",
00146                   NULL, NULL, NULL);
00147     sqlite3_exec (sql, "delete from Sources where source like 'plugin%'",
00148                   NULL, NULL, NULL);
00149     sqlite3_exec (sql, "update Media set value=0 where key='resolved'",
00150                   NULL, NULL, NULL);
00151 
00152     XMMS_DBG ("done");
00153 }
00154 
00155 static void
00156 upgrade_v29_to_v30 (sqlite3 *sql)
00157 {
00158     XMMS_DBG ("Upgrade v29->v30");
00159     sqlite3_exec (sql, "insert into Media (id, key, value, source) select distinct id, 'available', 1, (select id from Sources where source='server') from Media", NULL, NULL, NULL);
00160     XMMS_DBG ("done");
00161 }
00162 
00163 static void
00164 upgrade_v30_to_v31 (sqlite3 *sql)
00165 {
00166     XMMS_DBG ("Upgrade v30->v31");
00167 
00168     sqlite3_exec (sql, create_CollectionAttributes_stm, NULL, NULL, NULL);
00169     sqlite3_exec (sql, create_CollectionConnections_stm, NULL, NULL, NULL);
00170     sqlite3_exec (sql, create_CollectionIdlists_stm, NULL, NULL, NULL);
00171     sqlite3_exec (sql, create_CollectionLabels_stm, NULL, NULL, NULL);
00172     sqlite3_exec (sql, create_CollectionOperators_stm, NULL, NULL, NULL);
00173     sqlite3_exec (sql, create_collidx_stm, NULL, NULL, NULL);
00174 
00175     /* Create a default playlist */
00176     xmms_sqlite_exec (sql, fill_init_playlist_stm, XMMS_COLLECTION_TYPE_IDLIST,
00177                                                    XMMS_COLLECTION_NSID_PLAYLISTS,
00178                                                    XMMS_COLLECTION_NSID_PLAYLISTS);
00179 
00180     XMMS_DBG ("done");
00181 }
00182 
00183 static void
00184 upgrade_v31_to_v32 (sqlite3 *sql)
00185 {
00186     XMMS_DBG ("upgrade v31->v32");
00187     sqlite3_exec (sql, "delete from Media where id = (select id from Media where key='available' and value=0)", NULL, NULL, NULL);
00188     sqlite3_exec (sql, "delete from Media where key='available' and source = 1", NULL, NULL, NULL);
00189     sqlite3_exec (sql, "update media set key='status' where key='resolved' and source = 1", NULL, NULL, NULL);
00190     XMMS_DBG ("done");
00191 }
00192 
00193 static void
00194 upgrade_v32_to_v33 (sqlite3 *sql)
00195 {
00196     /* Decrement collection type id, as we removed ERROR from the enum. */
00197     XMMS_DBG ("upgrade v32->v33");
00198     sqlite3_exec (sql, "update CollectionOperators set type=type - 1", NULL, NULL, NULL);
00199     XMMS_DBG ("done");
00200 }
00201 
00202 static void
00203 upgrade_v33_to_v34 (sqlite3 *sql)
00204 {
00205     XMMS_DBG ("upgrade v33->v34");
00206     sqlite3_exec (sql, "update CollectionAttributes set value=replace(replace(value, '%', '*'), '_', '?') WHERE collid IN (SELECT id FROM CollectionOperators WHERE type='6')", NULL, NULL, NULL);
00207     XMMS_DBG ("done");
00208 }
00209 
00210 
00211 static void
00212 upgrade_v34_to_v35 (sqlite3 *sql)
00213 {
00214     XMMS_DBG ("upgrade v34->v35");
00215     sqlite3_exec (sql, "DROP INDEX prop_idx;"
00216                        "CREATE INDEX id_key_value_1x ON Media (id, key, value COLLATE BINARY);"
00217                        "CREATE INDEX id_key_value_2x ON Media (id, key, value COLLATE NOCASE);"
00218                        "CREATE INDEX key_value_1x ON Media (key, value COLLATE BINARY);"
00219                        "CREATE INDEX key_value_2x ON Media (key, value COLLATE NOCASE);"
00220                        "UPDATE CollectionAttributes SET value=replace(replace(value, '%', '*'), '_', '?') WHERE collid IN (SELECT id FROM CollectionOperators WHERE type='6');", NULL, NULL, NULL);
00221     XMMS_DBG ("done");
00222 }
00223 
00224 static gboolean
00225 try_upgrade (sqlite3 *sql, gint version)
00226 {
00227     gboolean can_upgrade = TRUE;
00228 
00229     switch (version) {
00230         case 26:
00231             upgrade_v26_to_v27 (sql);
00232         case 27:
00233             upgrade_v27_to_v28 (sql);
00234         case 28:
00235             upgrade_v28_to_v29 (sql);
00236         case 29:
00237             upgrade_v29_to_v30 (sql);
00238         case 30:
00239             upgrade_v30_to_v31 (sql);
00240         case 31:
00241             upgrade_v31_to_v32 (sql);
00242         case 32:
00243             upgrade_v32_to_v33 (sql);
00244         case 33:
00245             upgrade_v33_to_v34 (sql);
00246         case 34:
00247             upgrade_v34_to_v35 (sql);
00248             break; /* remember to (re)move this! We want fallthrough */
00249         default:
00250             can_upgrade = FALSE;
00251             break;
00252     }
00253 
00254     if (can_upgrade) {
00255         /* store the new version in the database */
00256         sqlite3_exec (sql, set_version_stm, NULL, NULL, NULL);
00257     }
00258 
00259     return can_upgrade;
00260 }
00261 
00262 static void
00263 xmms_sqlite_set_common_properties (sqlite3 *sql)
00264 {
00265     sqlite3_exec (sql, "PRAGMA synchronous = OFF", NULL, NULL, NULL);
00266     sqlite3_exec (sql, "PRAGMA auto_vacuum = 1", NULL, NULL, NULL);
00267     sqlite3_exec (sql, "PRAGMA cache_size = 8000", NULL, NULL, NULL);
00268     sqlite3_exec (sql, "PRAGMA temp_store = MEMORY", NULL, NULL, NULL);
00269 
00270     /* One minute */
00271     sqlite3_busy_timeout (sql, 60000);
00272 
00273     sqlite3_create_collation (sql, "INTCOLL", SQLITE_UTF8, NULL, xmms_sqlite_integer_coll);
00274 }
00275 
00276 gboolean
00277 xmms_sqlite_create (gboolean *create)
00278 {
00279     xmms_config_property_t *cv;
00280     gchar *tmp;
00281     gboolean analyze = FALSE;
00282     const gchar *dbpath;
00283     gint version = 0;
00284     sqlite3 *sql;
00285 
00286     *create = FALSE;
00287 
00288     cv = xmms_config_lookup ("medialib.path");
00289     dbpath = xmms_config_property_get_string (cv);
00290 
00291     if (!g_file_test (dbpath, G_FILE_TEST_EXISTS)) {
00292         *create = TRUE;
00293     }
00294 
00295     if (sqlite3_open (dbpath, &sql)) {
00296         xmms_log_fatal ("Error opening sqlite db: %s", sqlite3_errmsg (sql));
00297         return FALSE;
00298     }
00299 
00300     xmms_sqlite_set_common_properties (sql);
00301 
00302     if (!*create) {
00303         sqlite3_exec (sql, "PRAGMA user_version",
00304                       xmms_sqlite_version_cb, &version, NULL);
00305 
00306         if (version != DB_VERSION && !try_upgrade (sql, version)) {
00307             gchar *old;
00308 
00309             sqlite3_close (sql);
00310 
00311             old = XMMS_BUILD_PATH ("medialib.db.old");
00312             rename (dbpath, old);
00313             if (sqlite3_open (dbpath, &sql)) {
00314                 xmms_log_fatal ("Error creating sqlite db: %s",
00315                                 sqlite3_errmsg (sql));
00316                 g_free (old);
00317                 return FALSE;
00318             }
00319             g_free (old);
00320 
00321             sqlite3_exec (sql, "PRAGMA synchronous = OFF", NULL, NULL, NULL);
00322             *create = TRUE;
00323         }
00324 
00325         cv = xmms_config_lookup ("medialib.analyze_on_startup");
00326         analyze = xmms_config_property_get_int (cv);
00327         if (analyze) {
00328             xmms_log_info ("Analyzing db, please wait a few seconds");
00329             sqlite3_exec (sql, "ANALYZE", NULL, NULL, NULL);
00330             xmms_log_info ("Done with analyze");
00331         }
00332     }
00333 
00334     if (*create) {
00335         /* Check if we are about to put the medialib on a
00336          * remote filesystem. They are known to work less
00337          * well with sqlite and therefore we should refuse
00338          * to do so. The user has to know that he is doing
00339          * something stupid
00340          */
00341 
00342         tmp = g_path_get_dirname (dbpath);
00343         if (xmms_statfs_is_remote (tmp)) {
00344             cv = xmms_config_lookup ("medialib.allow_remote_fs");
00345             if (xmms_config_property_get_int (cv) == 1) {
00346                 xmms_log_info ("Allowing database on remote system against best judgement.");
00347             } else {
00348                 xmms_log_fatal ("Remote filesystem detected!\n"
00349                                 "* It looks like you are putting your database: %s\n"
00350                                 "* on a remote filesystem, this is a bad idea since there are many known bugs\n"
00351                                 "* with SQLite on some remote filesystems. We recomend that you put the db\n"
00352                                 "* somewhere else. You can do this by editing the xmms2.conf and find the\n"
00353                                 "* property for medialib.path. If you however still want to try to run the\n"
00354                                 "* db on a remote filesystem please set medialib.allow_remote_fs=1 in your\n"
00355                                 "* config and restart xmms2d.", dbpath);
00356             }
00357         }
00358 
00359         g_free (tmp);
00360 
00361         XMMS_DBG ("Creating the database...");
00362         /**
00363          * This will create the sqlite_stats1 table which we
00364          * fill out with good information about our indexes.
00365          * Thanks to drh for these pointers!
00366          */
00367         sqlite3_exec (sql, "ANALYZE", NULL, NULL, NULL);
00368         /**
00369          * Fill out sqlite_stats1
00370          */
00371         sqlite3_exec (sql, fill_stats, NULL, NULL, NULL);
00372         /**
00373          * Create the rest of our tables
00374          */
00375         sqlite3_exec (sql, create_Media_stm, NULL, NULL, NULL);
00376         sqlite3_exec (sql, create_Sources_stm, NULL, NULL, NULL);
00377         sqlite3_exec (sql, "insert into Sources (source) values ('server')", NULL, NULL, NULL);
00378         sqlite3_exec (sql, create_PlaylistEntries_stm, NULL, NULL, NULL);
00379         sqlite3_exec (sql, create_Playlist_stm, NULL, NULL, NULL);
00380         sqlite3_exec (sql, create_CollectionAttributes_stm, NULL, NULL, NULL);
00381         sqlite3_exec (sql, create_CollectionConnections_stm, NULL, NULL, NULL);
00382         sqlite3_exec (sql, create_CollectionIdlists_stm, NULL, NULL, NULL);
00383         sqlite3_exec (sql, create_CollectionLabels_stm, NULL, NULL, NULL);
00384         sqlite3_exec (sql, create_CollectionOperators_stm, NULL, NULL, NULL);
00385         sqlite3_exec (sql, create_idx_stm, NULL, NULL, NULL);
00386         sqlite3_exec (sql, create_collidx_stm, NULL, NULL, NULL);
00387         sqlite3_exec (sql, set_version_stm, NULL, NULL, NULL);
00388         /**
00389          * Create a default playlist
00390          */
00391         xmms_sqlite_exec (sql, fill_init_playlist_stm, XMMS_COLLECTION_TYPE_IDLIST,
00392                                                        XMMS_COLLECTION_NSID_PLAYLISTS,
00393                                                        XMMS_COLLECTION_NSID_PLAYLISTS);
00394     }
00395 
00396     sqlite3_close (sql);
00397 
00398     XMMS_DBG ("xmms_sqlite_create done!");
00399     return TRUE;
00400 }
00401 
00402 /**
00403  * Open a database or create a new one
00404  */
00405 sqlite3 *
00406 xmms_sqlite_open ()
00407 {
00408     sqlite3 *sql;
00409     const gchar *dbpath;
00410     xmms_config_property_t *cv;
00411 
00412     cv = xmms_config_lookup ("medialib.path");
00413     dbpath = xmms_config_property_get_string (cv);
00414 
00415     if (sqlite3_open (dbpath, &sql)) {
00416         xmms_log_fatal ("Error opening sqlite db: %s", sqlite3_errmsg (sql));
00417         return NULL;
00418     }
00419 
00420     g_return_val_if_fail (sql, NULL);
00421 
00422     xmms_sqlite_set_common_properties (sql);
00423 
00424     return sql;
00425 }
00426 
00427 static xmmsv_t *
00428 xmms_sqlite_column_to_val (sqlite3_stmt *stm, gint column)
00429 {
00430     xmmsv_t *val = NULL;
00431 
00432     switch (sqlite3_column_type (stm, column)) {
00433         case SQLITE_INTEGER:
00434         case SQLITE_FLOAT:
00435             val = xmmsv_new_int (sqlite3_column_int (stm, column));
00436             break;
00437         case SQLITE_TEXT:
00438         case SQLITE_BLOB:
00439             val = xmmsv_new_string ((gchar *)sqlite3_column_text (stm, column));
00440             break;
00441         case SQLITE_NULL:
00442             val = xmmsv_new_none ();
00443             break;
00444         default:
00445             XMMS_DBG ("Unhandled SQLite type!");
00446             break;
00447     }
00448 
00449     return val;
00450 
00451 }
00452 
00453 /**
00454  * A query that can't retrieve results
00455  */
00456 gboolean
00457 xmms_sqlite_exec (sqlite3 *sql, const char *query, ...)
00458 {
00459     gchar *q, *err;
00460     va_list ap;
00461     gint ret;
00462 
00463     g_return_val_if_fail (query, FALSE);
00464     g_return_val_if_fail (sql, FALSE);
00465 
00466     va_start (ap, query);
00467 
00468     q = sqlite3_vmprintf (query, ap);
00469 
00470     ret = sqlite3_exec (sql, q, NULL, NULL, &err);
00471     if (ret == SQLITE_BUSY) {
00472         xmms_log_fatal ("BUSY EVENT!");
00473         g_assert_not_reached ();
00474     }
00475     if (ret != SQLITE_OK) {
00476         xmms_log_error ("Error in query! \"%s\" (%d) - %s", q, ret, err);
00477         sqlite3_free (q);
00478         va_end (ap);
00479         return FALSE;
00480     }
00481 
00482     sqlite3_free (q);
00483     va_end (ap);
00484 
00485     return TRUE;
00486 }
00487 
00488 /**
00489  * Execute a query to the database.
00490  */
00491 gboolean
00492 xmms_sqlite_query_table (sqlite3 *sql, xmms_medialib_row_table_method_t method, gpointer udata, xmms_error_t *error, const gchar *query, ...)
00493 {
00494     gchar *q;
00495     va_list ap;
00496     gint ret;
00497     sqlite3_stmt *stm;
00498 
00499     g_return_val_if_fail (query, FALSE);
00500     g_return_val_if_fail (sql, FALSE);
00501 
00502     va_start (ap, query);
00503     q = sqlite3_vmprintf (query, ap);
00504     va_end (ap);
00505 
00506     ret = sqlite3_prepare (sql, q, -1, &stm, NULL);
00507 
00508     if (ret == SQLITE_BUSY) {
00509         xmms_log_fatal ("BUSY EVENT!");
00510         g_assert_not_reached ();
00511     }
00512 
00513     if (ret != SQLITE_OK) {
00514         gchar err[256];
00515         g_snprintf (err, sizeof (err),
00516                     "Error in query: %s", sqlite3_errmsg (sql));
00517         xmms_error_set (error, XMMS_ERROR_GENERIC, err);
00518         xmms_log_error ("Error %d (%s) in query '%s'", ret, sqlite3_errmsg (sql), q);
00519         sqlite3_free (q);
00520         return FALSE;
00521     }
00522 
00523     while ((ret = sqlite3_step (stm)) == SQLITE_ROW) {
00524         gint num, i;
00525         xmmsv_t *dict;
00526 
00527         dict = xmmsv_new_dict ();
00528         num = sqlite3_data_count (stm);
00529 
00530         for (i = 0; i < num; i++) {
00531             const char *key;
00532             xmmsv_t *val;
00533 
00534             /* We don't need to strdup the key because xmmsv_dict_set
00535              * will create its own copy.
00536              */
00537             key = sqlite3_column_name (stm, i);
00538             val = xmms_sqlite_column_to_val (stm, i);
00539 
00540             xmmsv_dict_set (dict, key, val);
00541 
00542             /* The dictionary owns the value. */
00543             xmmsv_unref (val);
00544         }
00545 
00546         if (!method (dict, udata)) {
00547             break;
00548         }
00549 
00550     }
00551 
00552     if (ret == SQLITE_ERROR) {
00553         xmms_log_error ("SQLite Error code %d (%s) on query '%s'", ret, sqlite3_errmsg (sql), q);
00554     } else if (ret == SQLITE_MISUSE) {
00555         xmms_log_error ("SQLite api misuse on query '%s'", q);
00556     } else if (ret == SQLITE_BUSY) {
00557         xmms_log_error ("SQLite busy on query '%s'", q);
00558         g_assert_not_reached ();
00559     }
00560 
00561     sqlite3_free (q);
00562     sqlite3_finalize (stm);
00563 
00564     return (ret == SQLITE_DONE);
00565 }
00566 
00567 /**
00568  * Execute a query to the database.
00569  */
00570 static gboolean
00571 xmms_sqlite_query_array_va (sqlite3 *sql, xmms_medialib_row_array_method_t method, gpointer udata, const gchar *query, va_list ap)
00572 {
00573     gchar *q;
00574     gint ret, num_cols;
00575     xmmsv_t **row;
00576     sqlite3_stmt *stm = NULL;
00577 
00578     g_return_val_if_fail (query, FALSE);
00579     g_return_val_if_fail (sql, FALSE);
00580 
00581     q = sqlite3_vmprintf (query, ap);
00582 
00583     ret = sqlite3_prepare (sql, q, -1, &stm, NULL);
00584 
00585     if (ret == SQLITE_BUSY) {
00586         xmms_log_fatal ("BUSY EVENT!");
00587         g_assert_not_reached ();
00588     }
00589 
00590     if (ret != SQLITE_OK) {
00591         xmms_log_error ("Error %d (%s) in query '%s'", ret, sqlite3_errmsg (sql), q);
00592         sqlite3_free (q);
00593         return FALSE;
00594     }
00595 
00596     num_cols = sqlite3_column_count (stm);
00597 
00598     row = g_new (xmmsv_t *, num_cols + 1);
00599     row[num_cols] = NULL;
00600 
00601     while ((ret = sqlite3_step (stm)) == SQLITE_ROW) {
00602         gint i;
00603         gboolean b;
00604 
00605         /* I'm a bit paranoid */
00606         g_assert (num_cols == sqlite3_data_count (stm));
00607 
00608         for (i = 0; i < num_cols; i++) {
00609             row[i] = xmms_sqlite_column_to_val (stm, i);
00610         }
00611 
00612         b = method (row, udata);
00613 
00614         for (i = 0; i < num_cols; i++) {
00615             xmmsv_unref (row[i]);
00616         }
00617 
00618         if (!b) {
00619             break;
00620         }
00621     }
00622 
00623     g_free (row);
00624 
00625     if (ret == SQLITE_ERROR) {
00626         xmms_log_error ("SQLite Error code %d (%s) on query '%s'", ret, sqlite3_errmsg (sql), q);
00627     } else if (ret == SQLITE_MISUSE) {
00628         xmms_log_error ("SQLite api misuse on query '%s'", q);
00629     } else if (ret == SQLITE_BUSY) {
00630         xmms_log_error ("SQLite busy on query '%s'", q);
00631     }
00632 
00633     sqlite3_free (q);
00634     sqlite3_finalize (stm);
00635 
00636     return (ret == SQLITE_DONE);
00637 }
00638 
00639 gboolean
00640 xmms_sqlite_query_array (sqlite3 *sql, xmms_medialib_row_array_method_t method, gpointer udata, const gchar *query, ...)
00641 {
00642     va_list ap;
00643     gboolean r;
00644 
00645     va_start (ap, query);
00646     r = xmms_sqlite_query_array_va (sql, method, udata, query, ap);
00647     va_end (ap);
00648 
00649     return r;
00650 }
00651 
00652 static gboolean
00653 xmms_sqlite_int_cb (xmmsv_t **row, gpointer udata)
00654 {
00655     gint *i = udata;
00656 
00657     if (row && row[0] && xmmsv_get_type (row[0]) == XMMSV_TYPE_INT32)
00658         xmmsv_get_int (row[0], i);
00659     else
00660         XMMS_DBG ("Expected int32 but got something else!");
00661 
00662     return TRUE;
00663 }
00664 
00665 gboolean
00666 xmms_sqlite_query_int (sqlite3 *sql, gint32 *out, const gchar *query, ...)
00667 {
00668     va_list ap;
00669     gboolean r;
00670 
00671     g_return_val_if_fail (query, FALSE);
00672     g_return_val_if_fail (sql, FALSE);
00673 
00674     va_start (ap, query);
00675     r = xmms_sqlite_query_array_va (sql, xmms_sqlite_int_cb, out, query, ap);
00676     va_end (ap);
00677 
00678     return r;
00679 }
00680 
00681 
00682 /**
00683  * Close database and free all resources used.
00684  */
00685 void
00686 xmms_sqlite_close (sqlite3 *sql)
00687 {
00688     g_return_if_fail (sql);
00689     sqlite3_close (sql);
00690 }
00691 
00692 void
00693 xmms_sqlite_print_version (void)
00694 {
00695     printf (" Using sqlite version %d (compiled against "
00696             XMMS_STRINGIFY (SQLITE_VERSION_NUMBER) ")\n",
00697             sqlite3_libversion_number ());
00698 }
00699 
00700 /* Return an escaped string */
00701 gchar *
00702 sqlite_prepare_string (const gchar *input) {
00703     gchar *q, *str;
00704     q = sqlite3_mprintf ("%Q", input);
00705     str = g_strdup (q);
00706     sqlite3_free (q);
00707     return str;
00708 }
00709 
00710 /** @} */

Generated on Wed Feb 9 2011 for XMMS2 by  doxygen 1.7.1