Update called vala api.
[stuffkeeper.git] / src / stuffkeeper-data-backend.gob
blob89637e0a398aeea32bc5fccdad27a8847accc2b4
1 %h{
2     #include "stuffkeeper-data-schema.h"
3     #include "stuffkeeper-data-item.h"
4     #include "stuffkeeper-data-tag.h"
5     #include "stuffkeeper-data-item-search.h"
6 %}
7 %ph{
8 #include <sqlite3.h>
9 #include <glib/gstdio.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <sys/time.h>
13 #include <time.h>
14 #include "misc.h"
16 #define DB_VERSION 0.06
20 class Stuffkeeper:Data:Backend from G:Object 
22     /**
23      * Private values 
24      */
25     /* A hash list used to store the items, the item id is the hash key */
26     private GHashTable *schemas = {g_hash_table_new_full(g_int_hash, g_int_equal,g_free, g_object_unref)} ;
28     /* A hash list used to store the tags, the tags id is the hash key */
29     private GHashTable *tags = {g_hash_table_new_full(g_int_hash, g_int_equal,g_free, g_object_unref)} ;
31     /* A hash list used to store the items, the item id is the hash key */
32     private GHashTable *items = {g_hash_table_new_full(g_int_hash, g_int_equal,g_free, g_object_unref)} ;
34     /* A hash list used to store the searches, the item id is the hash key */
35     private GHashTable *searches = {g_hash_table_new_full(g_int_hash, g_int_equal,g_free, g_object_unref)} ;
37     /* The path this "db" resides */
38     private gchar *path = {NULL} destroywith g_free;
40     /* SQLite handler */
41     private sqlite3 *sqlHandle = {NULL};
42     private gint transaction = {0};
44     /* locked */
45     private gboolean locked = {FALSE};
46     property BOOLEAN locked  
47             (nick = "Locked",
48             blurb = "Lock the database, all clients need to check this.",
49             default_value = FALSE,
50             export,
51             link);
53     
54     /**
55      * Signal 
56      */
57     /* searches
58      */
59     private
60     signal last NONE (STRING,POINTER)
61     void
62     search_changed(self,const SearchField *field, StuffkeeperDataItemSearch *search)
63     {
64     }
67     private
68     signal last NONE (POINTER)
69     void
70     search_added(self, StuffkeeperDataItemSearch *search)
71     {
72         g_signal_connect_swapped(G_OBJECT(search), "search-changed", G_CALLBACK(self_search_changed), self);
73     }
75     private
76     signal last NONE (INT)
77     void
78     search_removed(self, gint id)
79     {
80     }
85     /* item */
86     private
87     signal last NONE (STRING,POINTER)
88     void
89     item_changed(self, const gchar *field,StuffkeeperDataItem *item)
90     {
91     }
93     private
94     signal last NONE (POINTER)
95     void
96     item_added(self, StuffkeeperDataItem *item)
97     {
98         g_signal_connect_swapped(G_OBJECT(item), "item-changed", G_CALLBACK(self_item_changed), self);
100     }
102     private
103     signal last NONE (INT)
104     void
105     item_removed(self, gint id)
106     {
107     }
109     /* schema */
110     private
111     signal last NONE (POINTER)
112     void
113     schema_changed(self, StuffkeeperDataSchema *schema)
114     {
115     }
117     private
118     signal last NONE (POINTER)
119     void
120     schema_added(self, StuffkeeperDataSchema *schema)
121     {
122         g_signal_connect_swapped(G_OBJECT(schema), "schema-changed", G_CALLBACK(self_schema_changed), self);
123     }
124     private
125     signal last NONE (INT)
126         void
127     schema_removed(self, gint id)
128     {
129     }
130     /**
131      * searches
132      */
133     public
134     StuffkeeperDataItemSearch *
135     new_search(self)
136     {
137         gint *ide;
138         StuffkeeperDataItemSearch *search = stuffkeeper_data_item_search_new(G_OBJECT(self));
139         /* We have a valid id now, so add it */
140         /* Add to the list */
141         ide = g_malloc0(sizeof(gint));
142         *ide = stuffkeeper_data_item_search_get_id(search);
143         g_hash_table_insert(self->_priv->searches,ide, search);
144         self_search_added(self, search);
145         return search; 
146     }
147     public
148     void
149     remove_search(self, gint id)
150     {
151         StuffkeeperDataItemSearch *search = self_get_search(self, id);
152         if(!search) return;
154         self_search_removed(self,id);
155         if(search)
156         {
157             stuffkeeper_data_item_search_delete_yourself(search); 
158         }
160         if(g_hash_table_remove(self->_priv->searches, &id) == FALSE)
161         {
162             return;
163         }
165     }
167     public
168         GList *
169         get_searches(self)
170         {
171             return g_hash_table_get_values(self->_priv->searches);
172         }
174     public
175     guint
176     get_num_searches(self)
177     {
178         return g_hash_table_size(self->_priv->searches);
179     }
182     public 
183     StuffkeeperDataItemSearch *
184     get_search(self, gint id)
185     {
186         StuffkeeperDataItemSearch *item;
187         item = g_hash_table_lookup(self->_priv->searches, &id);
188         return item;
189     }
191     /**
192      * Public functions 
193      */
194     public
195         GList *
196         get_items(self)
197         {
198             return g_hash_table_get_values(self->_priv->items);
199         }
201     public
202     guint
203     get_num_items(self)
204     {
205         return g_hash_table_size(self->_priv->items);
206     }
209     public 
210     StuffkeeperDataItem *
211     get_item(self, gint id)
212     {
213         StuffkeeperDataItem *item;
214         item = g_hash_table_lookup(self->_priv->items, &id);
215         return item;
216     }
218     public 
219     StuffkeeperDataItem *
220     new_item(self,StuffkeeperDataSchema *schema)
221     {
222         gint *id;
223         StuffkeeperDataItem *item;
224         gchar *path = g_build_path(G_DIR_SEPARATOR_S, self->_priv->path, "items", NULL);
225         /* create a new item */
226         item = stuffkeeper_data_item_new(self,schema);
227         g_free(path);
228         /* want to copy the key, even if it is an integer */
229         id = g_malloc0(sizeof(gint));
230         *id = stuffkeeper_data_item_get_id(item);
231         /* insert it into my hash-list */
232         g_hash_table_insert(self->_priv->items,id, item);
233         self_item_added(self, item);
234         return item;
235     }
237     public StuffkeeperDataItem *clone_item(self, 
238             Stuffkeeper:Data:Item *orig_item (check null type))
239     {
240         gint *id;
241         StuffkeeperDataItem *item;
242         /* create a new item */
243         /* TODO: Could be a bug here: if we have multipile backends at some 
244          * point, the item we clone might not even belong to this backend, 
245          * the right way to fix this would be to have the new items signal the
246          * backend when they are created */
247         item = stuffkeeper_data_item_new_clone(orig_item);
248         /* want to copy the key, even if it is an integer */
249         id = g_malloc0(sizeof(gint));
250         *id = stuffkeeper_data_item_get_id(item);
251         /* insert it into my hash-list */
252         g_hash_table_insert(self->_priv->items,id, item);
253         self_item_added(self, item);
254         return item;
255     }
257     public 
258     void
259     remove_item(self, gint id)
260     {
261         StuffkeeperDataItem *item = g_hash_table_lookup(self->_priv->items, &id);
262         self_item_removed(self, id);
263         if(item)
264         {
265             stuffkeeper_data_item_delete_yourself(item); 
266         }
267         if(g_hash_table_remove(self->_priv->items, &id) == FALSE)
268         {
269             return;
270         }
272     }
273     public
274     void
275     close_yourself(self)
276     {
277         /* Clear items */
278         if(self->_priv->items)
279         {
280             g_hash_table_foreach(self->_priv->items, (GHFunc)self_iterate_remove_item, self);
281             g_hash_table_remove_all(self->_priv->items);
282         }
283         if(self->_priv->tags)
284         {
285             g_hash_table_foreach(self->_priv->tags, (GHFunc)self_iterate_remove_tag, self);
286             g_hash_table_remove_all(self->_priv->tags);
287         }
288         if(self->_priv->schemas)
289         {
290             g_hash_table_foreach(self->_priv->schemas, (GHFunc)self_iterate_remove_schema, self);
291             g_hash_table_remove_all(self->_priv->schemas);
292         }
293         if(self->_priv->searches)
294         {
295             g_hash_table_foreach(self->_priv->searches, (GHFunc)self_iterate_remove_search, self);
296             g_hash_table_remove_all(self->_priv->searches);
297         }
303         /* close the open db */
304         if(self->_priv->sqlHandle)
305         {
306             int retv = sqlite3_close(self->_priv->sqlHandle);
307             if(retv != SQLITE_OK)
308             {
309                 printf("The following error occured while trying to close the db: %s\n",
310                         sqlite3_errmsg(self->_priv->sqlHandle));
311             }
312         }
313         self->_priv->sqlHandle = NULL;
314         g_free(self->_priv->path);
315         self->_priv->path = NULL;
317     }
319     /* Load the data */
320     public
321     void
322     load(self, const gchar *db_path)
323     {
324         int retv;
325         /* item */
326         gchar *path;
327         char *query;
328         sqlite3_stmt *stmt;
329         const char *tail;
330         int r;
332         INIT_TIC_TAC();
334         /* check if there is allready data active */
335         if(self->_priv->path)
336         {
337             self_close_yourself(self);
338         }
340         /**
341          * Store the path in the backend.
342          */
343         self->_priv->path = g_strdup(db_path);
345         /**
346          * The database
347          */
348         path = g_build_path(G_DIR_SEPARATOR_S, self->_priv->path,"database.sqlite3",NULL);
349         /* open the db */
350         retv = sqlite3_open(path, &(self->_priv->sqlHandle));
351         if(retv != SQLITE_OK)
352         {
353             g_error("Failed to open the db: %s because of '%s'", path, sqlite3_errmsg(self->_priv->sqlHandle));
354         }
356         /* Set up the db */
357         /**
358          * Do integrety check, if failed, bail out
359          */
360         r = sqlite3_prepare_v2(self->_priv->sqlHandle, "PRAGMA integrety_check;", -1,  &stmt,  &tail);
361         if (r != SQLITE_OK)
362         {
363             g_error("Failed: %s\n", sqlite3_errmsg(self->_priv->sqlHandle));
364         }
365         else
366         {
367             r = sqlite3_step(stmt);
368             while(r == SQLITE_ROW || r == SQLITE_BUSY)
369             {
370                 if(r == SQLITE_ROW) {
371                     const gchar *value = (const gchar *)sqlite3_column_text(stmt, 0);
372                     if(strcmp(value, "ok") != 0){
373                         g_error("%s: Sqlite database integrety check failed: %s\n",
374                                 __FUNCTION__,
375                                 value);
376                     }
377                 }
378             }
379         }
380         sqlite3_finalize(stmt);
381         g_debug("Database integrety_check: ok");
382         /* Set sync */
384         r = sqlite3_prepare_v2(self->_priv->sqlHandle, "PRAGMA synchronous = 0;", -1,  &stmt,  &tail);
385         do{
386             r = sqlite3_step(stmt);
387         }while(r == SQLITE_BUSY);
388         if(r != SQLITE_DONE) {
389             g_error("%s: sqlite3_step() failed: %s",__FUNCTION__,
390                                 sqlite3_errmsg(self->_priv->sqlHandle));
391         }
392         sqlite3_finalize(stmt);
393         g_debug("Database synchronious transactions: ok");
394         /* Check the db */
395         self_check_sqlite_db(self);
396         g_free(path);
398         /** image dir */
399         path = g_build_path(G_DIR_SEPARATOR_S, self->_priv->path,"images",NULL);
400         if(!g_file_test(path, G_FILE_TEST_IS_DIR))
401         {
402             g_mkdir(path, 0755);
403         }
404         g_free(path);
410         TOC("time elapsed open & check db");
415         /**
416          * Read Schemas 
417          */
418         query = sqlite3_mprintf("SELECT SchemaId FROM Schemas WHERE type=%i",DB_SCHEMAS_TYPE_ID);
419         r = sqlite3_prepare_v2(self->_priv->sqlHandle, query, -1,  &stmt,  &tail);
420         sqlite3_free(query);
421         if (r != SQLITE_OK)
422         {
423             printf("Failed: %s\n", sqlite3_errmsg(self->_priv->sqlHandle));
424         }
425         else
426         {
427             while((r = sqlite3_step(stmt)) == SQLITE_ROW)
428             {
429                 gint oid = sqlite3_column_int(stmt, 0);
430                 StuffkeeperDataSchema *schema;
431                 gint *id;
432                 /* create a new item */
433                 schema = stuffkeeper_data_schema_open_from_id(self, oid);
434                 /* want to copy the key, even if it is an integer */
435                 id = g_malloc0(sizeof(gint));
436                 *id = oid; 
437                 /* insert it into my hash-list */
438                 g_hash_table_insert(self->_priv->schemas,id,schema);
439                 self_schema_added(self,schema);
440             }
441         }
442         sqlite3_finalize(stmt);
443         TOC("time elapsed open schemas");
445         /**
446          * Read Searches 
447          */
448         query = "SELECT SearchID FROM SearchFields GROUP by SearchID";
449         r = sqlite3_prepare_v2(self->_priv->sqlHandle, query, -1,  &stmt,  &tail);
450         if (r != SQLITE_OK)
451         {
452             printf("Failed: %s\n", sqlite3_errmsg(self->_priv->sqlHandle));
453         }
454         else
455         {
456             while((r = sqlite3_step(stmt)) == SQLITE_ROW)
457             {
458                 gint oid = sqlite3_column_int(stmt, 0);
459                 StuffkeeperDataItemSearch *search;
460                 gint *id;
461                 /* create a new item */
462                 search = (StuffkeeperDataItemSearch *) stuffkeeper_data_item_search_open_from_id(G_OBJECT(self), oid);
463                 /* want to copy the key, even if it is an integer */
464                 id = g_malloc0(sizeof(gint));
465                 *id = oid; 
466                 /* insert it into my hash-list */
467                 g_hash_table_insert(self->_priv->searches,id, search);
468                 self_search_added(self, search);
469             }
470         }
471         sqlite3_finalize(stmt);
473         TOC("time elapsed open searches");
475         /**
476          * Read tags 
477          */
478         query = "SELECT id FROM Tags";
479         r = sqlite3_prepare_v2(self->_priv->sqlHandle, query, -1,  &stmt,  &tail);
480         if (r != SQLITE_OK)
481         {
482             printf("Failed: %s\n", sqlite3_errmsg(self->_priv->sqlHandle));
483         }
484         else
485         {
486             while((r = sqlite3_step(stmt)) == SQLITE_ROW)
487             {
488                 gint oid = sqlite3_column_int(stmt, 0);
489                 StuffkeeperDataTag *tag;
490                 gint *id;
491                 /* create a new item */
492                 tag = stuffkeeper_data_tag_open_from_id(self, oid);
493                 /* want to copy the key, even if it is an integer */
494                 id = g_malloc0(sizeof(gint));
495                 *id = oid; 
496                 /* insert it into my hash-list */
497                 g_hash_table_insert(self->_priv->tags,id, tag);
498                 self_tag_added(self, tag);
499             }
500         }
501         sqlite3_finalize(stmt);
503         TOC("time elapsed open tags");
505         /** 
506          * Items
507          */
508         /* also get schemaid, saves an extra N extra queries */
509         query = sqlite3_mprintf("SELECT ItemId,value FROM Items WHERE type=%i",DB_ITEMS_TYPE_SCHEMA);
510         r = sqlite3_prepare_v2(self->_priv->sqlHandle, query, -1,  &stmt,  &tail);
511         sqlite3_free(query);
512         if (r != SQLITE_OK)
513         {
514             printf("Failed: %s\n", sqlite3_errmsg(self->_priv->sqlHandle));
515         }
516         else
517         {
518             while((r = sqlite3_step(stmt)) == SQLITE_ROW)
519             {
520                 gint oid = sqlite3_column_int(stmt, 0);
521                 int schemaid = sqlite3_column_int(stmt, 1);
522                 StuffkeeperDataItem *item;
523                 gint *id;
524                 /* create a new item */
525                 item = stuffkeeper_data_item_open_from_id(self,oid,schemaid);
526                 /* want to copy the key, even if it is an integer */
527                 id = g_malloc0(sizeof(gint));
528                 *id = oid; 
529                 /* insert it into my hash-list */
530                 g_hash_table_insert(self->_priv->items,id,item);
531                 self_item_added(self,item);
532             }
533         }
534         sqlite3_finalize(stmt);
536         TOC("time elapsed open items");
537     }
538     
539     /**
540      * Handle destroying of the object
541      */
542     private
543     void
544     iterate_remove_schema(gint *key, StuffkeeperDataSchema *schema, Self *self) 
545     {
546         self_schema_removed(self, *key);
547     }
548     private
549     void
550     iterate_remove_item(gint *key, StuffkeeperDataItem *item, Self *self )
551     {
552         self_item_removed(self, *key);
553     }
554     private
555     void
556     iterate_remove_tag(gint *key, StuffkeeperDataTag *tag, Self *self)
557     {
558         self_tag_removed(self, *key);
559     }
560     private
561     void
562     iterate_remove_search(gint *key, StuffkeeperDataItemSearch *search, Self *self)
563     {
564         self_search_removed(self, *key);
565     }
569     override (G:Object)
570     void
571     finalize (G:Object *obj)
572     {
573         Self *self = SELF(obj);
575         if(self->_priv->items)
576         {
577             g_hash_table_destroy(self->_priv->items);
578             self->_priv->items = NULL;
579         }
580         if(self->_priv->tags)
581         {
582             g_hash_table_destroy(self->_priv->tags);
583             self->_priv->tags = NULL;
584         }
585         if(self->_priv->schemas)
586         {
587             g_hash_table_destroy(self->_priv->schemas);
588             self->_priv->schemas = NULL;
589         }
590         if(self->_priv->searches)
591         {
592             g_hash_table_destroy(self->_priv->searches);
593             self->_priv->searches = NULL;
594         }
600         /**
601          * Clear SQLite table
602          */
603         if(self->_priv->sqlHandle)
604         {
605             int retv = 0;
606             printf("%i Rows modified.\n", sqlite3_total_changes(self->_priv->sqlHandle));
607             retv = sqlite3_close(self->_priv->sqlHandle);
608             if(retv != SQLITE_OK)
609             {
610                 g_debug("The following error occured while trying to close the db: %s\n",
611                         sqlite3_errmsg(self->_priv->sqlHandle));
612             }
613             /* set pointer to NULL */
614             self->_priv->sqlHandle = NULL;
615         }
616         PARENT_HANDLER(obj);
617     }
620     /**
621      * Create new StuffkeeperDataBackend 
622      * destroy with g_object_unref();
623      */
624     public 
625     StuffkeeperDataBackend *
626     new (void)
627     {
628         Self *self = GET_NEW;
629         return self;
630     }
633     /**
634      * Tags
635      */
636     /* signals  */
637     private
638     signal last NONE (POINTER)
639     void
640     tag_changed(self, StuffkeeperDataTag *tag)
641     {
642     }
644     private
645     signal last NONE (POINTER)
646         void
647         tag_added(self, StuffkeeperDataTag *tag)
648         {
650             g_signal_connect_swapped(G_OBJECT(tag), "tag-changed", G_CALLBACK(self_tag_changed), self);
651         }
653     private
654     signal last NONE (INT)
655     void
656     tag_removed(self,const int id)
657     {
658     }
659     
660     /**
661      * Schema's
662      */
663     public
664     StuffkeeperDataSchema*
665     get_schema(self, const int id)
666     {
667         gint *ide;
668         StuffkeeperDataSchema *schema;
669         /* Look for existing tag */
670         schema = g_hash_table_lookup(self->_priv->schemas, &id);
671         if(schema)
672         {
673             return schema;
674         }
675         /* No tag, lets make one. */
676         schema = stuffkeeper_data_schema_new_with_id(self,id);
678         /* Add to the list */
679         ide = g_malloc0(sizeof(gint));
680         *ide = id;
681         g_hash_table_insert(self->_priv->schemas,ide, schema);
683         self_schema_added(self, schema);
684         return schema;
685     }
686     public
687         GList *
688         get_schemas(self)
689         {
690             return g_hash_table_get_values(self->_priv->schemas);
691         }
692     public
693     StuffkeeperDataSchema *
694     load_from_xml(self, const char *path)
695     {
696         gint *ide;
697         gint id; 
699         StuffkeeperDataSchema *schema = stuffkeeper_data_schema_load_from_xml(self, path);
700         if(schema == NULL)
701         {
702             return NULL;
703         }
704         id = stuffkeeper_data_schema_get_id(schema);
705         /* Add to the list */
706         ide = g_malloc0(sizeof(gint));
707         *ide = id;
708         g_hash_table_insert(self->_priv->schemas,ide, schema);
710         self_schema_added(self, schema);
711         return schema;
712     }
713     public
714         StuffkeeperDataSchema *
715         new_schema(self)
716     {
717         gint *ide;
718         gint id; 
719         StuffkeeperDataSchema *schema;
722         /* No schema, lets make one. */
723         schema = stuffkeeper_data_schema_new(self);
724         id = stuffkeeper_data_schema_get_id(schema);
725         /* Add to the list */
726         ide = g_malloc0(sizeof(gint));
727         *ide = id;
728         g_hash_table_insert(self->_priv->schemas,ide, schema);
730         self_schema_added(self, schema);
731         return schema;
732     }
733     public
734         void
735         remove_schema(self, gint id)
736         {
737             StuffkeeperDataSchema *schema = self_get_schema(self, id);
738             if(!schema) return;
739             if(stuffkeeper_data_schema_num_items(schema) >0)
740             {
741                 return;
742             }
743             self_schema_removed(self,id);
744             if(schema)
745             {
746                 stuffkeeper_data_schema_delete_yourself(schema); 
747             }
749             if(g_hash_table_remove(self->_priv->schemas, &id) == FALSE)
750             {
751                 return;
752             }
753         }
755     /**
756      * TAGS
757      */
759    /**
760      * Add tag
761      * Call this from the item when adding a tag, it will return the tag.
762      * Existing tag if it allready exists
763      */
764     public
765     StuffkeeperDataTag *
766     add_tag(self, const int id)
767     {
768         gint *ide;
769         StuffkeeperDataTag *tag;
770         /* Look for existing tag */
771         tag = g_hash_table_lookup(self->_priv->tags, &id);
772         if(tag)
773         {
774             return tag;
775         }
776         g_debug("Tag not found, this aint good\n");
777         /* No tag, lets make one, with this id. */
778         tag = stuffkeeper_data_tag_new_with_id(self,id);
780         /* Add to the list */
781         ide = g_malloc0(sizeof(gint));
782         *ide = stuffkeeper_data_tag_get_id(tag);
783         g_hash_table_insert(self->_priv->tags,ide, tag);
785         self_tag_added(self, tag);
786         return tag;
787     }
790     public
791     StuffkeeperDataTag *
792     find_tag(self, const char *tag_name)
793     {
794         GHashTableIter iter;
795         gpointer key, value;
796         g_hash_table_iter_init (&iter, self->_priv->tags);
797         while (g_hash_table_iter_next (&iter, &key, &value)) 
798         {
799             /* do something with key and value */
800             StuffkeeperDataTag *tag = (StuffkeeperDataTag *)value;
801             gchar *title = stuffkeeper_data_tag_get_title(tag);
802             if(title && g_utf8_collate(title, tag_name) == 0) {
803                 g_free(title);
804                 return tag;
805             }
806             if(title)g_free(title);
807         }
808         return NULL;
809     }
811     public
812         StuffkeeperDataTag *
813     get_tag(self, const int id)
814     {
815         StuffkeeperDataTag *tag;
816         /* Look for existing tag */
817         tag = g_hash_table_lookup(self->_priv->tags, &id);
818         return tag;
819     }
820     public
821     StuffkeeperDataTag *
822     new_tag(self)
823     {
824         gint *ide;
825         StuffkeeperDataTag *tag = stuffkeeper_data_tag_new(self);
826         /* We have a valid id now, so add it */
827         /* Add to the list */
828         ide = g_malloc0(sizeof(gint));
829         *ide = stuffkeeper_data_tag_get_id(tag);
830         g_hash_table_insert(self->_priv->tags,ide, tag);
831         self_tag_added(self, tag);
832         return tag; 
833     }
835     public
836     GList *
837     get_tags(self)
838     {
839         return g_hash_table_get_values(self->_priv->tags);
840     }
842     public
843     void
844     remove_tag(self, gint id)
845     {
846         StuffkeeperDataTag *tag = self_get_tag(self, id);
847         if(!tag) return;
848         if(stuffkeeper_data_tag_num_items(tag) >0)
849         {
850             return;
851         }
852         /* Add extra reference so it does not get destroyed when removed from hash table */
853         g_object_ref(tag);
854         g_hash_table_remove(self->_priv->tags, &id);
855         self_tag_removed(self,id);
857         if(tag)
858         {
859             stuffkeeper_data_tag_delete_yourself(tag); 
860             /* This should be the last ref and it should be destroyed */
861             g_object_unref(tag);
862         }
863     }
865     public
866     gchar *
867     get_path(self)
868     {
869         return self->_priv->path;
870     }
872     /**
873      * SQLITE 
874      */
875     /* function checks if all the tables exist */
876     private
877     void
878     check_sqlite_db(self)
879     {
880         char *query;
881         const char *tail;
882         int r;
883         sqlite3_stmt *stmt;
885         /* available tables */
886         gboolean has_general    = FALSE;
887         gboolean has_tags       = FALSE;
888         gboolean has_schemas    = FALSE;
889         gboolean has_schemas_field = FALSE;
890         gboolean has_items      = FALSE;
891         gboolean has_items_field = FALSE;
892         gboolean has_search     = FALSE;
894         int i;
895         int result = 0;
896         char *error = NULL;
897         char **resultp = NULL;
898         int rows, columns;
899         double version = 0.0;
901         /* Check what tables exists */
902         query = sqlite3_mprintf("select tbl_name from sqlite_master where type='table'");
903         /* execute */
904         result = sqlite3_get_table(self->_priv->sqlHandle, query, &resultp, &rows, &columns,&error);
905         for (i = 1; i < (rows + 1); i++) {
906             /** Check for availible tables */
907             if (!strcmp(resultp[i], "General")) {
908                 has_general = TRUE;
909             }
910             else if (!strcmp(resultp[i], "Tags")) {
911                 has_tags = TRUE;
912             }
913             else if (!strcmp(resultp[i], "Schemas")) {
914                 has_schemas = TRUE;
915             }
916             else if (!strcmp(resultp[i], "SchemasFields")) {
917                 has_schemas_field = TRUE;
918             }
919             else if (!strcmp(resultp[i], "Items")) {
920                 has_items = TRUE;
921             }
922             else if (!strcmp(resultp[i], "ItemsFields")) {
923                 has_items_field = TRUE;
924             }
925             else if (!strcmp(resultp[i], "SearchFields")) {
926                 has_search = TRUE;
927             }
928         }
929         if(resultp)
930             sqlite3_free_table(resultp);
932         sqlite3_free(query);
933         /* if not exists, create the general table */
934         if(G_UNLIKELY(!has_general)) {
935             g_debug("Create general table\n");
937             query = sqlite3_mprintf("CREATE TABLE 'General' ("
938                     "'id'               INTEGER PRIMARY KEY AUTOINCREMENT," /* not useful, but you want a row id anyway. */
939                     "'key'          TEXT,"  /* the key stored in value */
940                     "'value'    TEXT" /* the value */
941                     ")");
943             result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
944             if (result != SQLITE_OK) {
945                 g_debug("failed: %s\n", error);
946             }
947             sqlite3_free(query);
948             /* store db version */
949             query = sqlite3_mprintf("INSERT INTO 'General' ('key', 'value') VALUES('%q','%f')","Version", DB_VERSION);
950             result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
951             if (result != SQLITE_OK) {
952                 g_debug("failed: %s\n", error);
953             }
954             sqlite3_free(query);
955         }
961         if(G_UNLIKELY(!has_tags)) {
962             g_debug("Create Tags table\n");
964             query = sqlite3_mprintf("CREATE TABLE 'Tags' ("
965                     "'id'               INTEGER PRIMARY KEY AUTOINCREMENT," /* not useful, but you want a row id anyway. */
966                     "'mtime'    INTEGER,"  /* the key stored in value */
967                     "'name'         TEXT" /* the value */
968                     ")");
970             result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
971             if (result != SQLITE_OK) {
972                 g_debug("failed: %s\n", error);
973             }
974             sqlite3_free(query);
975         }
977         if(G_UNLIKELY(!has_schemas)) {
978             g_debug("Create Schemas table\n");
980             query = sqlite3_mprintf("CREATE TABLE 'Schemas' ("
981                     "'id'               INTEGER PRIMARY KEY AUTOINCREMENT," /* not useful, but you want a row id anyway. */
982                     "'SchemaId'  INTEGER," /* id of the schema, taken from type=ID */
983                     "'type'         INTEGER,"  /* the key stored in value */
984                     "'value'    TEXT" /* the value */
985                     ")");
987             result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
988             if (result != SQLITE_OK) {
989                 g_debug("failed: %s\n", error);
990             }
991             sqlite3_free(query);
993             sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX SchemaId ON Schemas(SchemaId);", NULL, NULL, NULL);
994         }
995         if(G_UNLIKELY(!has_schemas_field)) {
996             g_debug("Create SchemasFields table\n");
998             query = sqlite3_mprintf("CREATE TABLE 'SchemasFields' ("
999                     "'id'               INTEGER PRIMARY KEY AUTOINCREMENT," /* not useful, but you want a row id anyway. */
1000                     "'FieldId'  INTEGER," /* id of the field, this is taken from the id of type=ID */
1001                     "'SchemaId' INTEGER," /* Id of the schema this field belongs too */
1002                     "'type'         INTEGER,"  /* the key stored in value */
1003                     "'value'    TEXT" /* the value */
1004                     ")");
1006             result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
1007             if (result != SQLITE_OK) {
1008                 g_debug("failed: %s\n", error);
1009             }
1010             sqlite3_free(query);
1011             result = sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX SchemaId2 ON SchemasFields(SchemaId);", NULL, NULL, NULL);
1012             if (result != SQLITE_OK) {
1013                 g_debug("failed: %s\n", error);
1014             }
1017         }
1018         if(G_UNLIKELY(!has_items)) {
1019             g_debug("Create Items table\n");
1021             query = sqlite3_mprintf("CREATE TABLE 'Items' ("
1022                     "'id'               INTEGER PRIMARY KEY AUTOINCREMENT," /* not useful, but you want a row id anyway. */
1023                     "'ItemId'  INTEGER," /* id of the item, taken from type=ID */
1024                     "'type'         INTEGER,"  /* the key stored in value */
1025                     "'value'    TEXT" /* the value */
1026                     ")");
1028             result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
1029             if (result != SQLITE_OK) {
1030                 g_debug("failed: %s\n", error);
1031             }
1032             sqlite3_free(query);
1034             sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX ItemId ON Items(ItemId);", NULL, NULL, NULL);
1035         }
1036         if(G_UNLIKELY(!has_items_field)) {
1037             g_debug("Create ItemsFields table\n");
1039             query = sqlite3_mprintf("CREATE TABLE 'ItemsFields' ("
1040                     "'id'               INTEGER PRIMARY KEY AUTOINCREMENT," /* not useful, but you want a row id anyway. */
1041                     "'ItemId'   INTEGER," /* id of the field, this is taken from the id of type=ID */
1042                     "'FieldId'  INTEGER," /* Id of the schema this field belongs too */
1043                     "'value'    TEXT" /* the value */
1044                     ")");
1046             result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
1047             if (result != SQLITE_OK) {
1048                 g_debug("failed: %s\n", error);
1049             }
1050             sqlite3_free(query);
1051             result = sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX ItemId2 ON ItemsFields(ItemId);", NULL, NULL, NULL);
1052             if (result != SQLITE_OK) {
1053                 g_debug("failed: %s\n", error);
1054             }
1057         }
1058         if(G_UNLIKELY(!has_search)) {
1059             g_debug("Create SearchFields table\n");
1061             query = sqlite3_mprintf("CREATE TABLE 'SearchFields' ("
1062                     "'id'               INTEGER PRIMARY KEY AUTOINCREMENT," /* not useful, but you want a row id anyway. */
1063                     "'SearchId'   INTEGER," /* id of the field, this is taken from the id of type=ID */
1064                     "'SearchType'   INTEGER," /* id of the field, this is taken from the id of type=ID */
1065                     "'FieldType'  INTEGER," /* Id of the schema this field belongs too */
1066                     "'value'    TEXT" /* the value */
1067                     ")");
1069             result = sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &error);
1070             if (result != SQLITE_OK) {
1071                 g_debug("failed: %s\n", error);
1072             }
1073             sqlite3_free(query);
1074             result = sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX SearchId ON SearchFields(SearchId);", NULL, NULL, NULL);
1075             if (result != SQLITE_OK) {
1076                 g_debug("failed: %s\n", error);
1077             }
1078         }
1081         /**
1082          * Version
1083          */
1084         query = sqlite3_mprintf("SELECT value FROM 'General' WHERE key='Version'");
1085         r = sqlite3_prepare_v2(self->_priv->sqlHandle, query, -1,  &stmt,  &tail);
1086         sqlite3_free(query);
1087         if (r != SQLITE_OK)
1088         {
1089             g_debug("Failed: %s\n", sqlite3_errmsg(self->_priv->sqlHandle));
1090         }
1091         else
1092         {
1093             g_debug("stepping through rows\n");
1094             if((r = sqlite3_step(stmt)) == SQLITE_ROW)
1095             {
1096                 version = sqlite3_column_double(stmt, 0); 
1097             }
1098         }
1099         sqlite3_finalize(stmt);
1100         if(version >= 0.0)
1101         {
1102             printf("Current db version is: %lf\n",version);
1103             if(version != DB_VERSION )
1104             {
1105                 printf("New DB version: %f->%f\n", version, DB_VERSION);
1106                 if(version <= 0.01)
1107                 {
1108                     printf("Adding indexes\n");
1109                     sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX SchemaId ON SchemasFields(SchemaId);", NULL, NULL, NULL);
1110                     sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX ItemId ON ItemsFields(ItemId);", NULL, NULL, NULL);
1111                     sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX SearchId ON SearchFields(SearchId);", NULL, NULL, NULL);
1112                 }
1113                 if(version <= 0.02)
1114                 {
1115                     sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX ItemId ON Items(ItemId);", NULL, NULL, NULL);
1116                     sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX SchemaId ON Schemas(SchemaId);", NULL, NULL, NULL);
1118                 }
1119                 else if (version <= 0.04)
1120                 {
1121                     /* Add the indexes again, sqlite seems to overwrite the values */
1122                     sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX ItemId ON Items(ItemId);", NULL, NULL, NULL);
1123                     sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX ItemId2 ON ItemsFields(ItemId);", NULL, NULL, NULL);
1124                     sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX SchemaId ON Schemas(SchemaId);", NULL, NULL, NULL);
1125                     sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX SchemaId2 ON SchemasFields(SchemaId);", NULL, NULL, NULL);
1126                     sqlite3_exec(self->_priv->sqlHandle, "CREATE INDEX SearchId ON SearchFields(SearchId);", NULL, NULL, NULL);
1127                 }
1128                 else if (version <= 0.05)
1129                 {
1130                     /* Need to update all rich text entries */
1131                     self_update_005_db(self); 
1132                 }
1134                 query = sqlite3_mprintf( "UPDATE 'General' SET value=%f WHERE key='Version'",DB_VERSION); 
1135                 sqlite3_exec(self->_priv->sqlHandle,query, NULL, NULL, NULL);
1136                 sqlite3_free(query);
1137             }
1139         }
1141     }
1143     public
1144         void
1145         begin_transaction(self)
1146         {
1147             if(self->_priv->transaction)
1148             {
1149                 self->_priv->transaction++;
1150                 return;
1151             }
1152             g_debug("begin transaction\n");
1153             sqlite3_exec(self->_priv->sqlHandle, "BEGIN TRANSACTION", NULL, NULL, NULL);
1154             self->_priv->transaction++;
1155         }
1157     public
1158         void
1159         end_transaction(self)
1160         {
1161             self->_priv->transaction--;
1162             if(self->_priv->transaction)
1163                 return;
1164             g_debug("end transaction\n");
1165             sqlite3_exec(self->_priv->sqlHandle, "END TRANSACTION", NULL, NULL, NULL);
1166         }
1168     /* Return the sqlite3 handle */
1169     public
1170     void *
1171     get_handle(self)
1172     {
1173         return self->_priv->sqlHandle;
1174     }
1177     private void update_005_db_field(self, int rowid)
1178     {
1179         GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
1180         GdkAtom atom = gtk_text_buffer_register_deserialize_tagset (buffer, NULL);
1181         gtk_text_buffer_deserialize_set_can_create_tags (buffer, atom, FALSE);
1182         int r;
1183         sqlite3_stmt *stmt;
1184         const char *tail;
1185         char *query = sqlite3_mprintf(
1186                 "SELECT value FROM ItemsFields " 
1187                 "WHERE id=%i", 
1188                 rowid 
1189                 );
1190         r = sqlite3_prepare_v2(self->_priv->sqlHandle, query, -1,  &stmt,  &tail);
1191         sqlite3_free(query);
1192         if((r = sqlite3_step(stmt)) == SQLITE_ROW)
1193         {
1194             const gchar *value = (const gchar *)sqlite3_column_text(stmt, 0);
1195             gsize out;
1196             if(value != NULL && value[0] != '\0')
1197             {
1198                 guchar *base64 = g_base64_decode(value, &out);
1199                 if(base64 && out > 0)
1200                 {
1202                     GtkTextIter end,start;
1203                     gtk_text_buffer_get_start_iter(buffer, &start);
1204                     end = start;
1205                     gboolean result = gtk_text_buffer_deserialize(buffer, buffer, atom,&start,
1206                             base64, out,NULL);
1207                     if(result)
1208                     {
1209                         char *tail;
1210                         gtk_text_buffer_get_start_iter(buffer, &start);
1211                         gtk_text_buffer_get_end_iter(buffer, &end);
1212                         gchar *re = gtk_text_buffer_get_text(buffer,&start,&end, FALSE);
1213                         /* store it */
1215                         query = sqlite3_mprintf(
1216                                 "UPDATE ItemsFields " 
1217                                 "SET value=%Q WHERE id=%i", 
1218                                 re,
1219                                 rowid 
1220                                 );
1221                         sqlite3_exec(self->_priv->sqlHandle, query, NULL, NULL, &tail);
1222                         printf("%i: Converted from rich to normal\n",rowid);
1224                         sqlite3_free(query);
1225                         g_free(re);
1226                     }
1228                 }
1229                 if(base64)g_free(base64);
1230             }
1231         }
1232         g_object_unref(buffer);
1233         sqlite3_finalize(stmt);
1234     }
1235     private void
1236     update_005_db(self)
1237     {
1238         char *query = NULL;
1239         int r;
1240         sqlite3_stmt *stmt;
1241         const char *tail;
1242         printf("Start 005 db update\n");
1243         //select itemsfields from itemsfields JOIN schemasfields on Schemasfields.FieldId == Itemsfields.fieldid where schemasfields.type=3 and schemasfields.value=5;         
1244         self_begin_transaction(self); 
1245         query = sqlite3_mprintf(
1246                 "SELECT ItemsFields.ItemId,ItemsFields.id FROM ItemsFields JOIN SchemasFields on SchemasFields.FieldId == ItemsFields.FieldId "
1247                 "WHERE SchemasFields.type=%i and SchemasFields.value=%i", 
1248                 DB_ITEMS_FIELD_TYPE_TYPE, FIELD_TYPE_TEXT 
1249                 );
1251         r = sqlite3_prepare_v2(self->_priv->sqlHandle, query, -1,  &stmt,  &tail);
1252         sqlite3_free(query);
1253         while((r = sqlite3_step(stmt)) == SQLITE_ROW){
1254             int rowid = sqlite3_column_int(stmt, 1); 
1255             self_update_005_db_field(self, rowid);
1256         }
1257         sqlite3_finalize(stmt);
1258         self_end_transaction(self); 
1259     }