2 * arch-tag: Implementation of RhythmDB libgda/SQLite database
4 * Copyright (C) 2004 Benjamin Otte <otte@gnome.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 #include "rhythmdb-gda.h"
24 #include "rhythmdb-query-model.h"
30 static void rhythmdb_gda_class_init (RhythmDBGdaClass
* klass
);
31 static void rhythmdb_gda_init (RhythmDBGda
* shell_player
);
32 static void rhythmdb_gda_finalize (GObject
* object
);
34 static void rhythmdb_gda_load (RhythmDB
* rdb
, gboolean
* die
);
35 static void rhythmdb_gda_save (RhythmDB
* rdb
);
36 static RhythmDBEntry
*rhythmdb_gda_entry_new (RhythmDB
* db
,
37 RhythmDBEntryType type
, const char *uri
);
38 static void rhythmdb_gda_entry_set (RhythmDB
* db
, RhythmDBEntry
* entry
,
39 guint propid
, const GValue
* value
);
40 static void rhythmdb_gda_entry_get (RhythmDB
* db
, RhythmDBEntry
* entry
,
41 guint propid
, GValue
* value
);
42 static void rhythmdb_gda_entry_delete (RhythmDB
* db
, RhythmDBEntry
* entry
);
43 static void rhythmdb_gda_entry_delete_by_type (RhythmDB
* adb
,
44 RhythmDBEntryType type
);
45 static RhythmDBEntry
*rhythmdb_gda_entry_lookup_by_location (RhythmDB
* db
,
47 static void rhythmdb_gda_do_full_query (RhythmDB
* db
, GPtrArray
* query
,
48 GtkTreeModel
* main_model
, gboolean
* cancel
);
49 static gboolean
rhythmdb_gda_evaluate_query (RhythmDB
* adb
, GPtrArray
* query
,
50 RhythmDBEntry
* aentry
);
52 G_DEFINE_TYPE (RhythmDBGda
, rhythmdb_gda
, RHYTHMDB_TYPE
)
56 rhythmdb_gda_class_init (RhythmDBGdaClass
* klass
)
58 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
59 RhythmDBClass
*rhythmdb_class
= RHYTHMDB_CLASS (klass
);
62 object_class
->finalize
= rhythmdb_gda_finalize
;
64 rhythmdb_class
->impl_load
= rhythmdb_gda_load
;
65 rhythmdb_class
->impl_save
= rhythmdb_gda_save
;
66 rhythmdb_class
->impl_entry_new
= rhythmdb_gda_entry_new
;
67 rhythmdb_class
->impl_entry_set
= rhythmdb_gda_entry_set
;
68 rhythmdb_class
->impl_entry_get
= rhythmdb_gda_entry_get
;
69 rhythmdb_class
->impl_entry_delete
= rhythmdb_gda_entry_delete
;
70 rhythmdb_class
->impl_entry_delete_by_type
= rhythmdb_gda_entry_delete_by_type
;
71 rhythmdb_class
->impl_lookup_by_location
=
72 rhythmdb_gda_entry_lookup_by_location
;
73 rhythmdb_class
->impl_evaluate_query
= rhythmdb_gda_evaluate_query
;
74 rhythmdb_class
->impl_do_full_query
= rhythmdb_gda_do_full_query
;
78 rhythmdb_gda_init (RhythmDBGda
* db
)
81 /* FIXME: This is a hack to replace '-' with '_' because of SQL column syntax */
82 for (i
= 0; i
< RHYTHMDB_NUM_PROPERTIES
; i
++) {
83 gchar
*mod
= (gchar
*) rhythmdb_nice_elt_name_from_propid (RHYTHMDB (db
), i
);
85 if (*mod
== '-') *mod
= '_';
90 /* we'll set up the Db in the _new function when we actually know the filename */
94 escape_string (const gchar
*orig
)
98 /* this is the shortest possible version. it's definitely slow */
99 strv
= g_strsplit (orig
, "\"", 0);
100 tmp
= g_strjoinv ("\"\"", strv
);
102 ret
= g_strdup_printf ("\"%s\"", tmp
);
109 dump_model (GdaDataModel
*model
)
113 for (i
= 0; i
< gda_data_model_get_n_rows (model
); i
++) {
114 for (j
= 0; j
< gda_data_model_get_n_columns (model
); j
++) {
115 const GdaValue
*value
= gda_data_model_get_value_at (model
, j
, i
);
118 gchar
*str
= gda_value_stringify (value
);
119 g_print ("(%4u, %4u) - %s (%d)\n", i
, j
, str
, gda_value_get_type (value
));
122 g_print ("(%4u, %4u) - (NULL)\n", i
, j
);
128 GStaticMutex my_mutex
= G_STATIC_MUTEX_INIT
;
130 static GdaDataModel
*
131 execute_query (RhythmDBGda
*db
, const gchar
*query
)
136 g_print ("Executing Query: %s\n", query
);
137 command
= gda_command_new (query
, GDA_COMMAND_TYPE_SQL
, GDA_COMMAND_OPTION_STOP_ON_ERRORS
);
138 g_static_mutex_lock (&my_mutex
);
139 model
= gda_connection_execute_single_command (db
->conn
, command
, NULL
);
140 g_static_mutex_unlock (&my_mutex
);
141 gda_command_free (command
);
145 g_warning ("query '%s' failed", query
);
152 execute_nonquery (RhythmDBGda
*db
, const gchar
*query
)
157 g_print ("Executing NonQuery: %s\n", query
);
158 command
= gda_command_new (query
, GDA_COMMAND_TYPE_SQL
, GDA_COMMAND_OPTION_STOP_ON_ERRORS
);
159 g_static_mutex_lock (&my_mutex
);
160 ret
= gda_connection_execute_non_query (db
->conn
, command
, NULL
) != -1;
161 g_static_mutex_unlock (&my_mutex
);
162 gda_command_free (command
);
164 g_warning ("query '%s' failed", query
);
169 #define TABLE "tracks"
171 ensure_table_exists (RhythmDBGda
*db
)
178 model
= gda_connection_get_schema (db
->conn
, GDA_CONNECTION_SCHEMA_TABLES
, NULL
);
182 for (i
= 0; i
< gda_data_model_get_n_rows (model
); i
++) {
183 const GdaValue
*value
= gda_data_model_get_value_at (model
, i
, 0);
184 if (g_str_equal (gda_value_get_string (value
), TABLE
)) {
185 g_print ("Table %s already exists. Great!\n", TABLE
);
189 /* create the table */
190 s
= g_string_new ("create table " TABLE
" (refcount INTEGER, ");
191 for (i
= 0; i
< RHYTHMDB_NUM_PROPERTIES
; i
++) {
192 GType type
= rhythmdb_get_property_type (RHYTHMDB (db
), i
);
194 g_string_append (s
, ", ");
195 g_string_append (s
, rhythmdb_nice_elt_name_from_propid (RHYTHMDB (db
), i
));
198 g_string_append_printf (s
, " VARCHAR (200)");
201 g_string_append_printf (s
, " BOOLEAN");
204 case G_TYPE_LONG
: /* FIXME */
205 case G_TYPE_UINT64
: /* FIXME */
206 g_string_append_printf (s
, " INTEGER");
210 g_string_append_printf (s
, " FLOAT");
213 g_warning ("unknown type %u", (guint
) type
);
214 g_assert_not_reached ();
219 if (i
== RHYTHMDB_PROP_LOCATION
) {
220 /* location is unique */
221 g_string_append (s
, " UNIQUE");
223 g_string_append (s
, ")");
224 ret
= execute_nonquery (db
, s
->str
);
225 g_string_free (s
, TRUE
);
227 /* refcounting with autodelete (woohoo!) */
228 ret
= execute_nonquery (db
, "create trigger delete_track after update of refcount on "
229 TABLE
" when new.refcount = 0 begin delete from " TABLE
230 " where _rowid_ = new._rowid_; end");
236 collect_value_for_sql (const GValue
*val
)
240 switch (G_VALUE_TYPE (val
)) {
242 value
= escape_string (g_value_get_string (val
));
245 value
= g_strdup (g_value_get_boolean (val
) ? "\"TRUE\"" : "\"FALSE\"");
248 value
= g_strdup_printf ("%d", g_value_get_int (val
));
251 value
= g_strdup_printf ("%ld", g_value_get_long (val
));
254 value
= g_strdup_printf ("%"G_GUINT64_FORMAT
, g_value_get_uint64 (val
));
257 value
= g_strdup_printf ("%f", g_value_get_float (val
));
260 value
= g_strdup_printf ("%g", g_value_get_double (val
));
263 g_assert_not_reached ();
270 collect_value_from_sql (GValue
*dest
, const GdaValue
*src
)
274 if (gda_value_isa (src
, GDA_VALUE_TYPE_NULL
))
276 g_assert (gda_value_isa (src
, GDA_VALUE_TYPE_STRING
));
277 str
= gda_value_get_string (src
);
279 /* keep in sync with create table */
280 switch (G_VALUE_TYPE (dest
)) {
282 g_value_set_string (dest
, str
);
285 g_value_set_boolean (dest
, g_str_equal (str
, "TRUE"));
288 g_value_set_int (dest
, strtol (str
, NULL
, 10));
291 g_value_set_long (dest
, strtol (str
, NULL
, 10));
294 g_value_set_uint64 (dest
, strtoul (str
, NULL
, 10));
297 g_value_set_float (dest
, atof (str
));
300 g_value_set_double (dest
, atof (str
));
303 g_assert_not_reached ();
309 _initialize (RhythmDBGda
*db
)
311 /* check songs table */
312 if (!ensure_table_exists (db
))
315 return execute_nonquery (db
, "update " TABLE
" set refcount=1");
319 rhythmdb_gda_new (const char *name
)
321 RhythmDBGda
*db
= g_object_new (RHYTHMDB_TYPE_GDA
, "name", name
, NULL
);
322 gchar
*conn_str
= g_strdup_printf ("URI=%s", name
);
324 g_print ("opening Db with conn string: %s\n", conn_str
);
325 db
->client
= gda_client_new ();
326 g_return_val_if_fail (db
->client
, NULL
);
327 db
->conn
= gda_client_open_connection_from_string (db
->client
, "SQLite",
331 g_warning ("GDA: error opening the library");
332 g_object_unref (db
->client
);
336 if (!_initialize (db
)) {
337 g_warning ("GDA: error initializing the library");
338 g_object_unref (db
->conn
);
339 g_object_unref (db
->client
);
344 return RHYTHMDB (db
);
348 rhythmdb_gda_finalize (GObject
* object
)
350 RhythmDBGda
*db
= RHYTHMDB_GDA (object
);
352 g_object_unref (db
->conn
);
353 g_object_unref (db
->client
);
355 G_OJECT_CLASS (rhythmdb_gda_parent_class
)->finalize (object
);
359 rhythmdb_gda_load (RhythmDB
* rdb
, gboolean
* die
)
362 static guint types
[] = { RHYTHMDB_PROP_TITLE
, RHYTHMDB_PROP_ARTIST
,
363 RHYTHMDB_PROP_ALBUM
, RHYTHMDB_PROP_LAST_PLAYED
};
364 RhythmDBGda
*db
= RHYTHMDB_GDA (rdb
);
365 GdaDataModel
*model
= execute_query (db
, "select _rowid_, title, artist, album, last_played from " TABLE
);
366 g_return_if_fail (model
);
368 for (i
= 0; i
< gda_data_model_get_n_rows (model
); i
++) {
369 gpointer entry
= GINT_TO_POINTER ((gint
) strtol (gda_value_get_string (
370 gda_data_model_get_value_at (model
, 0, i
)), NULL
, 10));
371 for (j
= 0; j
< G_N_ELEMENTS (types
); j
++) {
373 g_value_init (&val
, rhythmdb_get_property_type (rdb
, types
[j
]));
374 collect_value_from_sql (&val
, gda_data_model_get_value_at (model
, j
+ 1, i
));
375 rhythmdb_entry_sync_mirrored (rdb
, entry
, types
[j
], &val
);
376 g_value_unset (&val
);
378 rhythmdb_emit_entry_restored (rdb
, entry
);
383 rhythmdb_gda_save (RhythmDB
* rdb
)
385 /* nothing to do here */
388 static RhythmDBEntry
*
389 rhythmdb_gda_entry_new (RhythmDB
* rdb
, RhythmDBEntryType type
, const char *uri
)
391 RhythmDBGda
*db
= RHYTHMDB_GDA (rdb
);
392 gchar
*query
= g_strdup_printf ("insert into " TABLE
393 " (type, refcount, location) values (%d, 1, \"%s\")", (gint
) type
, uri
);
395 if (!execute_nonquery (db
, query
)) {
400 return rhythmdb_gda_entry_lookup_by_location (rdb
, uri
);
404 rhythmdb_gda_entry_set (RhythmDB
* rdb
, RhythmDBEntry
* entry
,
405 guint propid
, const GValue
* value
)
407 RhythmDBGda
*db
= RHYTHMDB_GDA (rdb
);
408 gchar
*collect
= collect_value_for_sql (value
);
409 gchar
*query
= g_strdup_printf ("update " TABLE
" set %s = %s where _rowid_ = %d",
410 rhythmdb_nice_elt_name_from_propid (rdb
, propid
), collect
,
411 GPOINTER_TO_INT (entry
));
413 execute_nonquery (db
, query
);
418 rhythmdb_gda_entry_get (RhythmDB
* rdb
, RhythmDBEntry
* entry
,
419 guint propid
, GValue
* value
)
421 RhythmDBGda
*db
= RHYTHMDB_GDA (rdb
);
422 gchar
*query
= g_strdup_printf ("select %s from " TABLE
423 " where _ROWID_ = %d", rhythmdb_nice_elt_name_from_propid (rdb
, propid
),
424 GPOINTER_TO_INT (entry
));
425 GdaDataModel
*model
= execute_query (db
, query
);
430 if (gda_data_model_get_n_rows (model
) > 0) {
431 g_assert (gda_data_model_get_n_rows (model
) == 1);
432 collect_value_from_sql (value
, gda_data_model_get_value_at (model
, 0, 0));
434 g_object_unref (G_OBJECT (model
));
438 rhythmdb_gda_ref (RhythmDBGda
*db
, gint id
, gint count
)
440 gchar
*query
= g_strdup_printf ("select refcount from " TABLE
441 " where _ROWID_ = %d", id
);
442 GdaDataModel
*model
= execute_query (db
, query
);
447 g_assert (gda_data_model_get_n_rows (model
) == 1);
448 count
+= strtol (gda_value_get_string (
449 gda_data_model_get_value_at (model
, 0, 0)), NULL
, 10);
450 g_object_unref (model
);
452 query
= g_strdup_printf ("update " TABLE
" set refcount = %d where _ROWID_ = %d",
454 execute_nonquery (db
, query
);
459 rhythmdb_gda_entry_delete (RhythmDB
* rdb
, RhythmDBEntry
* entry
)
461 rhythmdb_gda_ref (RHYTHMDB_GDA (rdb
), GPOINTER_TO_INT (entry
), -1);
465 rhythmdb_gda_entry_delete_by_type (RhythmDB
* rdb
, RhythmDBEntryType type
)
467 g_assert_not_reached ();
470 static RhythmDBEntry
*
471 rhythmdb_gda_entry_lookup_by_location (RhythmDB
* rdb
, const char *uri
)
474 RhythmDBGda
*db
= RHYTHMDB_GDA (rdb
);
475 gchar
*escaped_uri
= escape_string (uri
);
476 gchar
*query
= g_strdup_printf ("select _ROWID_ from " TABLE
477 " where location = %s", escaped_uri
);
478 GdaDataModel
*model
= execute_query (db
, query
);
480 g_free (escaped_uri
);
482 if (!model
) return NULL
;
484 if (gda_data_model_get_n_rows (model
) > 0) {
485 g_assert (gda_data_model_get_n_rows (model
) == 1);
486 ret
= GINT_TO_POINTER (strtol (gda_value_get_string (
487 gda_data_model_get_value_at (model
, 0, 0)), NULL
, 10));
491 g_object_unref (G_OBJECT (model
));
492 g_print ("FOUND ENTRY %p\n", ret
);
497 translate_query (RhythmDBGda
*db
, RhythmDBQueryData
*data
)
499 gchar
*operation
= NULL
, *value
, *ret
;
501 switch (data
->type
) {
502 case RHYTHMDB_QUERY_DISJUNCTION
:
503 case RHYTHMDB_QUERY_SUBQUERY
:
504 g_assert_not_reached (); /* FIXME */
506 case RHYTHMDB_QUERY_PROP_EQUALS
:
507 operation
= "%s = %s";
509 case RHYTHMDB_QUERY_PROP_LIKE
:
510 case RHYTHMDB_QUERY_PROP_NOT_LIKE
:
511 g_assert_not_reached (); /* FIXME */
513 case RHYTHMDB_QUERY_PROP_GREATER
:
514 operation
= "%s > %s";
516 case RHYTHMDB_QUERY_PROP_LESS
:
517 operation
= "%s < %s";
519 case RHYTHMDB_QUERY_END
:
521 g_assert_not_reached ();
525 value
= collect_value_for_sql (data
->val
);
526 ret
= g_strdup_printf (operation
, rhythmdb_nice_elt_name_from_propid (RHYTHMDB (db
), data
->propid
),
533 /* set rowid to 0 for all rows */
534 static GdaDataModel
*
535 do_query (RhythmDBGda
*db
, GPtrArray
* query
, gint rowid
)
538 RhythmDBQueryData
*data
;
539 GString
*s
= g_string_new (NULL
);
543 g_assert (query
->len
== 1);
544 g_string_append (s
, "select _ROWID_ from " TABLE
" where ");
546 g_string_append_printf (s
, "rowid == %d AND (", rowid
);
547 for (i
= 0; i
< query
->len
; i
++) {
548 data
= (RhythmDBQueryData
*) g_ptr_array_index (query
, i
);
549 tmp
= translate_query (db
, data
);
551 g_string_append (s
, " and ");
552 g_string_append (s
, tmp
);
556 g_string_append (s
, ")");
558 model
= execute_query (db
, s
->str
);
559 g_string_free (s
, TRUE
);
564 rhythmdb_gda_do_full_query (RhythmDB
* rdb
, GPtrArray
* query
,
565 GtkTreeModel
* main_model
, gboolean
* cancel
)
567 RhythmDBGda
*db
= RHYTHMDB_GDA (rdb
);
568 GdaDataModel
*model
= do_query (db
, query
, 0);
569 g_return_if_fail (model
);
576 queue
= g_ptr_array_sized_new (gda_data_model_get_n_rows (model
));
577 for (j
= 0; j
< gda_data_model_get_n_rows (model
); j
++) {
578 g_ptr_array_add (queue
, GINT_TO_POINTER (strtol (gda_value_get_string (
579 gda_data_model_get_value_at (model
, 0, j
)), NULL
, 10)));
581 rhythmdb_query_model_add_entries (RHYTHMDB_QUERY_MODEL (main_model
), queue
);
586 rhythmdb_gda_evaluate_query (RhythmDB
* rdb
, GPtrArray
* query
,
587 RhythmDBEntry
* aentry
)
590 RhythmDBGda
*db
= RHYTHMDB_GDA (rdb
);
591 GdaDataModel
*model
= do_query (db
, query
, GPOINTER_TO_INT (aentry
));
593 if (!model
) return FALSE
;
594 ret
= gda_data_model_get_n_rows (model
) > 0;
595 g_object_unref (model
);