Changeset 3290

Show
Ignore:
Timestamp:
04/27/08 14:48:53 (5 months ago)
Author:
dgollub
Message:

Introduce new osync_hashltable API

* The hashtable is now restricted to one object type/table.
* Instead of performing single SQL queries an internal

hashtable data structure is used for temporary store.

* The persistent store of the hashtable is done by the

function osync_hashtable_save(), which
is using SQL transaction. Call this function in your
syncdone() plugin sink function.

* Hashtable needs to be loaded with function osync_hashtable_load()
* Added reference counting for hashtable object
* osync_hashtable_num_entries() return value changed from int to unsigned int
* osync_hashtable_nth_entry() got replaced by osync_hashtable_foreach(),

which is more performant to iterate through the hashtable

* osync_hashtable_get_deleted() return value changed from char* Array to OSyncList
* osync_hashtable_update_hash() got by replaced osync_hashtable_update_change()

which allows to use the OSyncChange object, since this contains changetype
and UID. Depending on the changetype more specific SQL query get prepared.

* osync_hashtable_write()/osync_hashtable_delete() got dropped. No need - right?
* osync_hashtable_reset() and osync_hashtable_report() are gone - everything is done

with osync_hashtable_update_change()

* It's FAST now (seriously)!

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/opensync/helper/opensync_hashtable.c

    r3264 r3290  
    3939 *  
    4040 * @param table The hashtable 
    41  * @param objtype the object type of the hashtable 
     41 * @param ame The name of the hastable inside the database  
    4242 * @param error An error struct 
    4343 * @returns TRUE on success, or FALSE if an error occurred. 
     
    4545 */ 
    4646 
    47 static osync_bool osync_hashtable_create(OSyncHashTable *table, const char *objtype, OSyncError **error) 
    48 
    49         osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, table, objtype, error); 
    50  
    51         char *query = g_strdup_printf("CREATE TABLE tbl_hash_%s (id INTEGER PRIMARY KEY, uid VARCHAR UNIQUE, hash VARCHAR)", objtype); 
     47static osync_bool osync_hashtable_create(OSyncHashTable *table, OSyncError **error) 
     48
     49        osync_assert(table); 
     50        osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, table, error); 
     51 
     52        char *query = g_strdup_printf("CREATE TABLE %s (id INTEGER PRIMARY KEY, uid VARCHAR UNIQUE, hash VARCHAR)", table->name); 
    5253        if (!osync_db_query(table->dbhandle, query, error)) { 
    5354                osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 
     
    6162} 
    6263 
    63 /*@}*/ 
    64  
    65 /** 
    66  * @defgroup OSyncHashtableAPI OpenSync Hashtables 
    67  * @ingroup OSyncPublic 
    68  * @brief A Hashtable can be used to detect changes 
    69  *  
    70  * Hashtables can be used to detect changes since the last invocation. They do this 
    71  * by keeping track of all reported uids and the hashes of the objects. 
    72  *  
    73  * A hash is a string that changes when an object is updated or when the content of 
    74  * the object changes. So hashes can either be a real hash like an MD5, or something  
    75  * like a timestamp. The only important thing is that the hash changes when the item 
    76  * gets updated. 
    77  *  
    78  * The hashtable is created or loaded from a .db file using the osync_hashtable_new() 
    79  * function. 
    80  *  
    81  * Now you can query and alter the table. You can ask if a item has changed by doing: 
    82  * - osync_hashtable_get_changetype() to get the changetype of a certain uid and hash 
    83  * - or the convience function osync_hashtable_detect_change() which calls  
    84  * osync_hashtable_get_changetype() and sets this changetype on the change object and then 
    85  * automatically calls osync_hashtable_report() 
    86  * After you reported all objects you can query the table for the deleted objects using 
    87  * osync_hashtable_get_deleted() or osync_hashtable_report_deleted() 
    88  *  
    89  * After you are finished using the hashtable, call: 
    90  * - osync_hashtable_free() 
    91  *  
    92  * The hashtable works like this: 
    93  *  
    94  * First the items are reported with a certain uid or hash. If the uid does not yet 
    95  * exist in the database it is reported as ADDED. If the uid exists and the hash is different 
    96  * it is reported as MODIFIED. If the uid exists but the hash is the same it means that the 
    97  * object is UNMODIFIED. 
    98  *  
    99  * To be able to report deleted objects the hashtables keeps track of the uids you reported. 
    100  * After you are done with asking the hashtable for changes you can ask it for deleted objects. 
    101  * All items that are in the hashtable but where not reported by you have to be DELETED. 
    102  *  
    103  */ 
    104 /*@{*/ 
    105  
    106 /*! @brief Loads or creates a hashtable 
    107  *  
    108  * Hashtables can be used to detect what has been changed since 
    109  * the last sync 
    110  *  
    111  * @param path the full path and file name of the hashtable .db file to load from or create 
    112  * @param objtype the object type of the hashtable 
    113  * @param error An error struct 
    114  * @returns A new hashtable, or NULL if an error occurred. 
    115  *  
    116  */ 
    117 OSyncHashTable *osync_hashtable_new(const char *path, const char *objtype, OSyncError **error) 
    118 { 
    119         osync_trace(TRACE_ENTRY, "%s(%s, %s, %p)", __func__, path, objtype, error); 
    120          
    121         OSyncHashTable *table = osync_try_malloc0(sizeof(OSyncHashTable), error); 
    122         if (!table) { 
    123                 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 
    124                 return NULL; 
    125         } 
    126          
    127         table->used_entries = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); 
    128  
    129         table->dbhandle = osync_db_new(error); 
    130         if (!table->dbhandle) 
    131                 goto error; 
    132  
    133         if (!osync_db_open(table->dbhandle, path, error)) 
    134                 goto error_and_free; 
    135  
    136         table->tablename = g_strdup_printf("tbl_hash_%s", objtype); 
    137  
    138         int ret = osync_db_exists(table->dbhandle, table->tablename, error); 
    139         if (ret > 0) { 
    140                 goto end; 
    141         } else if (ret < 0) { 
    142                 goto error_and_free; 
    143         } 
    144         /* if ret == 0 then table does not exist yet. contiune and create one. */ 
    145  
    146         if (!osync_hashtable_create(table, objtype, error)) 
    147                 goto error_and_free; 
    148  
    149 end: 
    150         osync_trace(TRACE_EXIT, "%s: %p", __func__, table); 
    151         return table; 
    152  
    153 error_and_free:  
    154         g_free(table->dbhandle); 
    155         g_free(table); 
    156 error:   
    157         osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 
    158         return FALSE; 
    159  
    160 } 
    161  
    162 /*! @brief Frees a hashtable 
    163  *  
    164  * @param table The hashtable to free 
    165  *  
    166  */ 
    167 void osync_hashtable_free(OSyncHashTable *table) 
    168 { 
    169         osync_trace(TRACE_ENTRY, "%s(%p)", __func__, table); 
    170         osync_assert(table); 
    171          
    172         if (!osync_db_close(table->dbhandle, NULL)) 
    173                 osync_trace(TRACE_INTERNAL, "Can't close database"); 
    174  
    175                  
    176         g_hash_table_destroy(table->used_entries); 
    177  
    178         g_free(table->tablename); 
    179         g_free(table->dbhandle); 
    180         g_free(table); 
    181          
    182         osync_trace(TRACE_EXIT, "%s", __func__); 
    183 } 
    184  
    185 /*! @brief Prepares the hashtable for a slowsync and flush the entire hashtable 
    186  *  
    187  * This function should be called to prepare the hashtable for a slowsync. 
    188  * The entire database, which stores the values of the hashtable beyond the 
    189  * synchronization, gets flushed. 
    190  *  
    191  * @param table The hashtable 
    192  * @param error An error struct 
    193  * @returns TRUE on success, or FALSE if an error occurred. 
    194  *  
    195  */ 
    196 osync_bool osync_hashtable_slowsync(OSyncHashTable *table, OSyncError **error) 
    197 { 
    198         osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, table, error); 
    199         osync_assert(table); 
    200         osync_assert(table->dbhandle); 
    201  
    202         if (!osync_db_reset(table->dbhandle, table->tablename, error)) { 
    203                 osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 
    204                 return FALSE; 
    205         } 
    206          
    207         osync_trace(TRACE_EXIT, "%s", __func__); 
    208         return TRUE; 
    209 } 
    210  
    21164#if !GLIB_CHECK_VERSION(2,12,0) 
    21265/*! \brief g_hash_table_foreach_remove foreach function 
     
    21871#endif 
    21972 
    220 /*! @brief Makes a hashtable forget 
     73/*! @brief Makes a hashtable forget reported entries 
    22174 *  
    22275 * You can ask the hashtable to detect the changes. In the end you can 
     
    22982 *  
    23083 */ 
    231 void osync_hashtable_reset_reports(OSyncHashTable *table) 
     84static void osync_hashtable_reset_reports(OSyncHashTable *table) 
    23285{ 
    23386        osync_trace(TRACE_ENTRY, "%s(%p)", __func__, table); 
     
    23891           Don't flush the real database. */ 
    23992#if GLIB_CHECK_VERSION(2,12,0) 
    240         g_hash_table_remove_all(table->used_entries); 
     93        g_hash_table_remove_all(table->reported_entries); 
    24194#else 
    242         g_hash_table_foreach_remove(table->used_entries, remove_entry, NULL); 
     95        g_hash_table_foreach_remove(table->reported_entries, remove_entry, NULL); 
    24396#endif 
    24497         
     
    24699} 
    247100 
    248 /*! @brief Returns the number of entries in this hashtable 
    249  *  
    250  * @param table The hashtable 
    251  * @returns The number of entries, or -1 if an error occurred 
    252  *  
    253  */ 
    254 int osync_hashtable_num_entries(OSyncHashTable *table) 
     101/*! @brief Makes hashtable in memory forget, not the persistent one 
     102 *  
     103 * @param table The hashtable 
     104 *  
     105 */ 
     106static void osync_hashtable_reset(OSyncHashTable *table) 
    255107{ 
    256108        osync_trace(TRACE_ENTRY, "%s(%p)", __func__, table); 
    257109        osync_assert(table); 
    258110        osync_assert(table->dbhandle); 
    259          
    260         char *query = g_strdup_printf("SELECT * FROM %s", table->tablename); 
    261         int ret = osync_db_count(table->dbhandle, query, NULL); 
    262         g_free(query); 
    263          
    264         if (ret < 0) { 
    265                 osync_trace(TRACE_EXIT_ERROR, "%s: Cannot count number of hashtable entries!", __func__); 
    266                 return -1; 
    267         } 
    268  
    269         osync_trace(TRACE_EXIT, "%s: %i", __func__, ret); 
    270         return ret; 
    271 
    272  
    273 /*! @brief Gets the nth entry from the table 
    274  *  
    275  * This is mainly useful for debugging or special purposes 
    276  *  
    277  * @param table The hashtable 
    278  * @param nth The number of the entry to return 
    279  * @param uid A pointer to a char * that will hold the uid. The caller is responsible for freeing this. 
    280  * @param hash A pointer to a char * that will hold the hash. The caller is responsible for freeing this. 
    281  * @returns TRUE if successful, FALSE otherwise 
    282  *  
    283  */ 
    284 osync_bool osync_hashtable_nth_entry(OSyncHashTable *table, int nth, char **uid, char **hash) 
    285 
    286         osync_assert(table); 
    287         osync_assert(table->dbhandle); 
    288          
    289         GList *list = NULL; 
    290         OSyncError *error = NULL; 
    291          
    292  
    293         char *query = g_strdup_printf("SELECT uid, hash FROM %s LIMIT 1 OFFSET %i", table->tablename, nth); 
    294         list = osync_db_query_table(table->dbhandle, query, &error); 
    295         g_free(query); 
    296  
    297         if (osync_error_is_set(&error)) { 
    298                 osync_trace(TRACE_EXIT_ERROR, "%s: Cannot get #%i entry from hashtable: %s", __func__, nth, osync_error_print(&error)); 
    299                 osync_error_unref(&error); 
    300                 return FALSE; 
    301         } 
    302                  
    303         GList *column = list->data;  
    304  
    305         *uid = g_strdup((char*)g_list_nth_data(column, 0)); 
    306         *hash = g_strdup((char*)g_list_nth_data(column, 1)); 
    307          
    308         osync_db_free_list(list); 
    309  
    310         return TRUE; 
    311 
    312  
    313 /*! @brief Gets the hash value for given uid 
    314  * 
    315  * @param table The hashtable 
    316  * @param uid The uid to lookup 
    317  * @returns The hash. Has to be freed by the caller. 
    318  */ 
    319 char *osync_hashtable_get_hash(OSyncHashTable *table, const char *uid) 
    320 
    321        osync_assert(uid); 
    322        osync_assert(table); 
    323        osync_assert(table->dbhandle); 
    324  
    325        char *hash = NULL; 
    326        GList *list = NULL; 
    327        OSyncError *error = NULL; 
    328        char *escaped_uid = osync_db_sql_escape(uid); 
    329  
    330        char *query = g_strdup_printf("SELECT hash FROM %s WHERE uid= '%s' LIMIT 1", 
    331                                      table->tablename, escaped_uid); 
    332        list = osync_db_query_table(table->dbhandle, query, &error); 
    333        g_free(query); 
    334        g_free(escaped_uid); 
    335  
    336        if (osync_error_is_set(&error)) { 
    337                osync_trace(TRACE_EXIT_ERROR, "%s: Cannot get hash for '%s': %s", 
    338                            __func__, uid, osync_error_print(&error)); 
    339                osync_error_unref(&error); 
    340                return NULL; 
    341        } 
    342  
    343        if (list && list->data) { 
    344                GList *column = list->data; 
    345                hash = g_strdup((char *)g_list_nth_data(column, 0)); 
    346        } 
    347  
    348        osync_db_free_list(list); 
    349  
    350        return hash; 
    351 
    352  
    353 /*! @brief Write the hash value for given uid 
    354  * 
    355  * @param table The hashtable 
    356  * @param uid The uid 
    357  * @param hash The hash 
    358  */ 
    359 void osync_hashtable_write(OSyncHashTable *table, const char *uid, const char *hash) 
    360 
    361         osync_trace(TRACE_ENTRY, "%s(%p, %s, %s)", __func__, table, uid, hash); 
    362         osync_assert(table); 
    363         osync_assert(table->dbhandle); 
    364  
    365         char *escaped_uid = osync_db_sql_escape(uid); 
    366         char *escaped_hash = osync_db_sql_escape(hash); 
    367         char *query = g_strdup_printf("REPLACE INTO %s ('uid', 'hash') VALUES('%s', '%s')", table->tablename, escaped_uid, escaped_hash); 
    368         g_free(escaped_uid); 
    369         g_free(escaped_hash); 
    370  
    371         if (!osync_db_query(table->dbhandle, query, NULL)) { 
    372                 g_free(query); 
    373                 osync_trace(TRACE_EXIT, "%s: Cannot write hashtable entry.", __func__); 
    374                 return; 
    375         } 
    376         g_free(query); 
    377          
    378         osync_trace(TRACE_EXIT, "%s", __func__); 
    379 
    380  
    381 /*! @brief Delete hashtable entries for given uid 
    382  * 
    383  * @param table The hashtable 
    384  * @param uid The uid of the entry to delete 
    385  */ 
    386 void osync_hashtable_delete(OSyncHashTable *table, const char *uid) 
    387 
    388         osync_trace(TRACE_ENTRY, "%s(%p, %s)", __func__, table, uid); 
    389         osync_assert(table); 
    390         osync_assert(table->dbhandle); 
    391  
    392         char *escaped_uid = osync_db_sql_escape(uid); 
    393         char *query = g_strdup_printf("DELETE FROM %s WHERE uid='%s'", table->tablename, escaped_uid); 
    394         g_free(escaped_uid); 
    395  
    396         if (!osync_db_query(table->dbhandle, query, NULL)) { 
    397                 g_free(query); 
    398                 osync_trace(TRACE_EXIT_ERROR, "%s: Cannot delete hashtable entry.", __func__); 
    399                 return; 
    400                 /* TODO: Error handling */ 
    401         } 
    402         g_free(query); 
    403  
    404         osync_trace(TRACE_EXIT, "%s", __func__); 
    405 
    406  
    407 /*! @brief Update the hash for a entry 
    408  *  
    409  * Updates the hash for a entry in the hashtable. Do this after you see that a hash 
    410  * has changed, for example after reading it during get_changes or after you 
    411  * wrote it 
    412  *  
    413  * @param table The hashtable 
    414  * @param type The type of change (added, modified, etc.) 
    415  * @param uid the uid of the changed entry 
    416  * @param hash the new hash of the changed entry 
    417  */ 
    418 void osync_hashtable_update_hash(OSyncHashTable *table, OSyncChangeType type, const char *uid, const char *hash) 
    419 
    420         osync_trace(TRACE_ENTRY, "%s(%p, %i, %s, %s)", __func__, table, type, uid, hash); 
    421         osync_assert(table); 
    422         osync_assert(table->dbhandle); 
    423  
    424         switch (type) { 
    425                 case OSYNC_CHANGE_TYPE_DELETED: 
    426                         osync_hashtable_delete(table, uid); 
    427                         break; 
    428                 case OSYNC_CHANGE_TYPE_UNMODIFIED: 
    429                 case OSYNC_CHANGE_TYPE_UNKNOWN: 
    430                 case OSYNC_CHANGE_TYPE_MODIFIED: 
    431                 case OSYNC_CHANGE_TYPE_ADDED: 
    432                         osync_hashtable_write(table, uid, hash); 
    433                         break; 
    434         } 
    435          
    436         osync_trace(TRACE_EXIT, "%s", __func__); 
    437 
     111 
     112        /* Only free the internal hashtable of reported entries. 
     113           Don't flush the real database. */ 
     114#if GLIB_CHECK_VERSION(2,12,0) 
     115        g_hash_table_remove_all(table->db_entries); 
     116#else 
     117        g_hash_table_foreach_remove(table->db_entries, remove_entry, NULL); 
     118#endif 
     119         
     120        osync_trace(TRACE_EXIT, "%s", __func__); 
     121
     122 
    438123 
    439124/*! @brief Report a item 
     
    444129 *  
    445130 * @param table The hashtable 
    446  * @param uid The uid to report 
    447  *  
    448  */ 
    449 void osync_hashtable_report(OSyncHashTable *table, const char *uid) 
    450 
    451         osync_trace(TRACE_ENTRY, "%s(%p, %s)", __func__, table, uid); 
    452         osync_assert(table); 
    453         osync_assert(table->dbhandle); 
    454          
    455         g_hash_table_insert(table->used_entries, g_strdup(uid), GINT_TO_POINTER(1)); 
    456          
    457         osync_trace(TRACE_EXIT, "%s", __func__); 
    458 
    459  
    460 /*! @brief Get the uid of all deleted items 
    461  *  
    462  * @param table The hashtable 
    463  * @returns A null-terminated array of uids. The uids and this array have to be freed by the caller. 
    464  *  
    465  */ 
    466 char **osync_hashtable_get_deleted(OSyncHashTable *table) 
    467 
     131 * @param change The change to report 
     132 *  
     133 */ 
     134static void osync_hashtable_report(OSyncHashTable *table, OSyncChange *change) 
     135
     136        osync_assert(table); 
     137        osync_assert(table->dbhandle); 
     138        osync_assert(change); 
     139 
     140        osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, table, change); 
     141         
     142        /* uid get freed when hashtable get's destroyed */ 
     143        char *uid = g_strdup(osync_change_get_uid(change)); 
     144 
     145        g_hash_table_insert(table->reported_entries, uid, GINT_TO_POINTER(1)); 
     146         
     147        osync_trace(TRACE_EXIT, "%s", __func__); 
     148
     149 
     150/* TODO */ 
     151static void _osync_hashtable_prepare_insert_query(const char *uid, const char *hash, void *user_data) 
     152
     153        OSyncHashTable *table = user_data; 
     154 
     155        char *escaped_uid = osync_db_sql_escape(uid); 
     156        char *escaped_hash = osync_db_sql_escape(hash); 
     157 
     158        g_string_append_printf(table->query,  
     159                        "REPLACE INTO %s ('uid', 'hash') VALUES('%s', '%s');",  
     160                        table->name, escaped_uid, escaped_hash); 
     161 
     162        g_free(escaped_uid); 
     163        g_free(escaped_hash); 
     164
     165 
     166/*@}*/ 
     167 
     168/** 
     169 * @defgroup OSyncHashtableAPI OpenSync Hashtables 
     170 * @ingroup OSyncPublic 
     171 * @brief A Hashtable can be used to detect changes 
     172 *  
     173 * Hashtables can be used to detect changes since the last invocation. They do this 
     174 * by keeping track of all reported uids and the hashes of the objects. 
     175 *  
     176 * A hash is a string that changes when an object is updated or when the content of 
     177 * the object changes. So hashes can either be a real hash like an MD5, or something  
     178 * like a timestamp. The only important thing is that the hash changes when the item 
     179 * gets updated. 
     180 *  
     181 * The hashtable is created from a .db file using the osync_hashtable_new() function. 
     182 * 
     183 * With osync_hashtable_load() the persinent database gets read and loads all hashtable 
     184 * entries into memory. 
     185 *  
     186 * Now you can query and alter the hashtable in memory. You can ask if a item has changed  
     187 * by doing: 
     188 * 
     189 * - osync_hashtable_get_changetype()  
     190 *   To get the changetype of a certain OSyncChange object. Don't forget to update the hash for  
     191 *   the change in advance. Update your OSyncChange with this detect changetype with 
     192 *   osync_change_set_changetype() 
     193 * 
     194 * - osync_hashtable_update_change() 
     195 *   When the changetype got updated for the OSyncChange object, update the hash entry with 
     196 *   calling osync_hashtable_update_change(). Call this function even if the entry has changetype 
     197 *   unmodified. Otherwise the hashtable will report this entry later as deleted. 
     198 *   
     199 * - osync_hashtable_get_deleted() 
     200 *   Once all available changes got reported call osync_hashtable_get_deleted() to get an OSyncList 
     201 *   of changes which got deleted since last sync. Entries get determined as deleted if they 
     202 *   got not reported as osync_hashtable_update_change(), independent of the changetype. 
     203 * 
     204 * - osync_hashtable_save() 
     205 *   For performance reason the hashtable in memory got only stored persistence with calling 
     206 *   osync_hashtable_save(). Call this function everytime when the synchronization finished. 
     207 *   This is usually inside the sync_done() function. 
     208 *  
     209 * After you are finished using the hashtable, call: 
     210 * - osync_hashtable_unref() 
     211 *  
     212 * The hashtable works like this: 
     213 *  
     214 * First the items are reported with a certain uid or hash. If the uid does not yet 
     215 * exist in the database it is reported as ADDED. If the uid exists and the hash is different 
     216 * it is reported as MODIFIED. If the uid exists but the hash is the same it means that the 
     217 * object is UNMODIFIED. 
     218 *  
     219 * To be able to report deleted objects the hashtables keeps track of the uids you reported. 
     220 * After you are done with asking the hashtable for changes you can ask it for deleted objects. 
     221 * All items that are in the hashtable but where not reported by you have to be DELETED. 
     222 *  
     223 */ 
     224/*@{*/ 
     225 
     226/*! @brief Loads or creates a hashtable 
     227 *  
     228 * Hashtables can be used to detect what has been changed since 
     229 * the last sync 
     230 *  
     231 * @param path the full path and file name of the hashtable .db file to load from or create 
     232 * @param objtype the object type of the hashtable 
     233 * @param error An error struct 
     234 * @returns A new hashtable, or NULL if an error occurred. 
     235 *  
     236 */ 
     237OSyncHashTable *osync_hashtable_new(const char *path, const char *objtype, OSyncError **error) 
     238
     239        osync_trace(TRACE_ENTRY, "%s(%s, %p)", __func__, path, error); 
     240         
     241        OSyncHashTable *table = osync_try_malloc0(sizeof(OSyncHashTable), error); 
     242        if (!table) 
     243                goto error; 
     244 
     245        table->ref_count = 1; 
     246         
     247 
     248        table->reported_entries = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); 
     249        table->db_entries = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); 
     250 
     251        table->dbhandle = osync_db_new(error); 
     252        if (!table->dbhandle) 
     253                goto error_and_free_db; 
     254 
     255        if (!osync_db_open(table->dbhandle, path, error)) 
     256                goto error_and_free; 
     257 
     258        table->name = g_strdup_printf(OSYNC_HASHTABLE_DB_PREFIX"%s", objtype); 
     259 
     260        int ret = osync_db_table_exists(table->dbhandle, table->name, error); 
     261        /* greater then 0 means evrything is O.k. */ 
     262 
     263        if (ret < 0) 
     264                goto error; 
     265        else if (ret == 0) 
     266                /* if ret == 0 then table does not exist yet. contiune and create one. */ 
     267                if (!osync_hashtable_create(table, error)) 
     268                        goto error; 
     269 
     270 
     271        osync_trace(TRACE_EXIT, "%s: %p", __func__, table); 
     272        return table; 
     273 
     274error_and_free_db:       
     275        g_free(table->dbhandle); 
     276error_and_free: 
     277        g_free(table); 
     278error:   
     279        osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 
     280        return FALSE; 
     281 
     282
     283 
     284/*! @brief Increase the refernece count of the hashtable. 
     285 * 
     286 * @param table The hashtable to increase the reference count 
     287 * @returns Pointer to increased hashtable object 
     288 */ 
     289OSyncHashTable *osync_hashtable_ref(OSyncHashTable *table) 
     290
     291        osync_assert(table); 
     292         
     293        g_atomic_int_inc(&(table->ref_count)); 
     294 
     295        return table; 
     296
     297 
     298/*! @brief Decrease the reference count of the hastable. Hashtable 
     299 *         gets freed if the reference count get less then one.  
     300 *  
     301 * @param table The hashtable to decrease the reference count  
     302 *  
     303 */ 
     304void osync_hashtable_unref(OSyncHashTable *table) 
     305
     306        osync_assert(table); 
     307 
     308        if (g_atomic_int_dec_and_test(&(table->ref_count))) { 
     309                osync_trace(TRACE_ENTRY, "%s(%p)", __func__, table); 
     310 
     311                OSyncError *error = NULL; 
     312                 
     313                if (!osync_db_close(table->dbhandle, &error)) { 
     314                        osync_trace(TRACE_ERROR, "Couldn't close database: %s", osync_error_print(&error)); 
     315                        osync_error_unref(&error); 
     316                } 
     317                         
     318                g_hash_table_destroy(table->reported_entries); 
     319                g_hash_table_destroy(table->db_entries); 
     320 
     321                g_free(table->name); 
     322                g_free(table->dbhandle); 
     323                g_free(table); 
     324 
     325                osync_trace(TRACE_EXIT, "%s", __func__); 
     326        } 
     327         
     328
     329 
     330osync_bool osync_hashtable_load(OSyncHashTable *table, OSyncError **error) 
     331
     332        osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, table, error); 
     333 
     334        char *query; 
     335        OSyncList *row, *result; 
     336 
     337        query = g_strdup_printf("SELECT uid, hash FROM %s", table->name); 
     338        result = osync_db_query_table(table->dbhandle, query, error);  
     339        g_free(query); 
     340 
     341        /* If result is NULL, this means no entries - just check for error. */ 
     342        if (osync_error_is_set(error)) 
     343                goto error; 
     344 
     345        for (row = result; row; row = row->next) { 
     346                OSyncList *column = row->data; 
     347 
     348                char *uid =  g_strdup(osync_list_nth_data(column, 0)); 
     349                char *hash = g_strdup(osync_list_nth_data(column, 1));  
     350 
     351                g_hash_table_insert(table->db_entries, uid, hash); 
     352        } 
     353 
     354        osync_trace(TRACE_EXIT, "%s", __func__); 
     355        return TRUE; 
     356 
     357error: 
     358        osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 
     359        return FALSE; 
     360
     361 
     362osync_bool osync_hashtable_save(OSyncHashTable *table, OSyncError **error) 
     363
     364        osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, table, error); 
     365 
     366        osync_bool ret; 
     367 
     368        /* Should only be used by this function */ 
     369        osync_assert(!table->query); 
     370 
     371        table->query = g_string_new("BEGIN TRANSACTION;"); 
     372        g_string_append_printf(table->query, "DELETE FROM %s;", table->name); 
     373 
     374        osync_hashtable_foreach(table, _osync_hashtable_prepare_insert_query, table);  
     375 
     376        table->query = g_string_append(table->query, "COMMIT TRANSACTION;"); 
     377 
     378        char *query = g_string_free(table->query, FALSE); 
     379        ret = osync_db_query(table->dbhandle, query, error); 
     380        g_free(query); 
     381 
     382        table->query = NULL; 
     383 
     384        if (!ret) 
     385                goto error; 
     386 
     387        osync_hashtable_reset_reports(table); 
     388 
     389        osync_trace(TRACE_EXIT, "%s", __func__); 
     390        return TRUE; 
     391 
     392error: 
     393        osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 
     394        return FALSE; 
     395
     396 
     397 
     398/*! @brief Prepares the hashtable for a slowsync and flush the entire hashtable 
     399 *  
     400 * This function should be called to prepare the hashtable for a slowsync. 
     401 * The entire database, which stores the values of the hashtable beyond the 
     402 * synchronization, gets flushed. 
     403 *  
     404 * @param table The hashtable 
     405 * @param error An error struct 
     406 * @returns TRUE on success, or FALSE if an error occurred. 
     407 *  
     408 */ 
     409osync_bool osync_hashtable_slowsync(OSyncHashTable *table, OSyncError **error) 
     410
     411        osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, table, error); 
     412        osync_assert(table); 
     413        osync_assert(table->dbhandle); 
     414 
     415        osync_bool ret = FALSE; 
     416 
     417        /* Reset persistent hashtable in database */ 
     418        if (!osync_db_reset_table(table->dbhandle, table->name, error)) 
     419                goto error; 
     420 
     421        /* Reset hashtable in memory */ 
     422        osync_hashtable_reset(table); 
     423         
     424        osync_trace(TRACE_EXIT, "%s", __func__); 
     425        return TRUE; 
     426 
     427error: 
     428        osync_trace(TRACE_EXIT_ERROR, "%s: %s", __func__, osync_error_print(error)); 
     429        return FALSE; 
     430 
     431
     432 
     433/*! @brief Update the an entry 
     434 *  
     435 * Updates the entry in the hashtable. Use this even if the change entry 
     436 * is unmodified! Usually this function get called in get_changes(). In some 
     437 * rare cases this get even called inside of the commit() plugin functions, 
     438 * to update the UID inside the hashtable of a changed entry. 
     439 *  
     440 * @param table The hashtable 
     441 * @param type The type of change (added, modified, etc.) 
     442 */ 
     443void osync_hashtable_update_change(OSyncHashTable *table, OSyncChange *change) 
     444
     445        osync_assert(table); 
     446        osync_assert(table->dbhandle); 
     447        osync_assert(change); 
     448 
     449        osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, table, change); 
     450         
     451        const char *uid = osync_change_get_uid(change); 
     452        const char *hash = osync_change_get_hash(change); 
     453 
     454        switch (osync_change_get_changetype(change)) { 
     455                case OSYNC_CHANGE_TYPE_DELETED: 
     456                        g_hash_table_remove(table->db_entries, uid); 
     457                        break; 
     458                case OSYNC_CHANGE_TYPE_UNMODIFIED: 
     459                        /* Nothing to do. Just ignore. */ 
     460                        break; 
     461                case OSYNC_CHANGE_TYPE_UNKNOWN: 
     462                        /* Someone violets against the rules of the hashtable API! 
     463                          
     464                           Changetype needs to get set before calling this function! 
     465                           Even if the change entry got not modified, then the change type 
     466                           should get set at least to OSYNC_CHANGE_TYPE_UNMODIFIED. Otherwise: 
     467                         
     468                           BOOOOOOOOOOOOOOOOOOOOOM! 
     469                          
     470                         */ 
     471                        osync_assert_msg(FALSE, "Got called with unknown changetype. This looks like a plugin makes wrong use of a hashtable. Please, contact the plugin author!"); 
     472                        break; 
     473                case OSYNC_CHANGE_TYPE_MODIFIED: 
     474                        /* This works even if the UID/key is new to the hashtable */ 
     475                        g_hash_table_replace(table->db_entries, g_strdup(uid), g_strdup(hash)); 
     476                        break; 
     477                case OSYNC_CHANGE_TYPE_ADDED: 
     478                        g_hash_table_insert(table->db_entries, g_strdup(uid), g_strdup(hash)); 
     479                        break; 
     480        } 
     481 
     482        osync_hashtable_report(table, change); 
     483         
     484        osync_trace(TRACE_EXIT, "%s", __func__); 
     485
     486 
     487/*! @brief Get a list of uids which deleted  
     488 *  
     489 * @param table The hashtable 
     490 * @returns OSyncList containing UIDs of deleted entries. Caller is responsible for freeing the ist, 
     491 *          not the content, with osync_list_free() . 
     492 *  
     493 */ 
     494OSyncList *osync_hashtable_get_deleted(OSyncHashTable *table) 
     495
     496        osync_assert(table); 
     497        osync_assert(table->dbhandle); 
     498 
    468499        osync_trace(TRACE_ENTRY, "%s(%p)", __func__, table); 
    469500 
    470         osync_assert(table); 
    471         osync_assert(table->dbhandle); 
    472  
    473         GList *row = NULL, *result = NULL; 
    474  
    475         char *query = g_strdup_printf("SELECT uid FROM %s", table->tablename); 
    476         result = osync_db_query_table(table->dbhandle, query, NULL);  
    477         g_free(query); 
    478  
    479         int numrows = g_list_length(result); 
    480         char **ret = g_malloc0((numrows + 1) * sizeof(char *)); 
    481          
    482         int num = 0; 
    483         for (row = result; row; row = row->next) { 
    484                 GList *column = row->data; 
    485  
    486                 const char *uid = (const char *) g_list_nth_data(column, 0); 
    487  
    488                 if (!g_hash_table_lookup(table->used_entries, uid)) 
    489                         ret[num++] = g_strdup(uid); 
     501        GList *e, *db_entries; 
     502        OSyncList *deleted_entries = NULL; 
     503 
     504        db_entries = g_hash_table_get_keys(table->db_entries); 
     505 
     506        for (e = db_entries; e; e = e->next) { 
     507                const char *uid = e->data; 
     508 
     509                if (!g_hash_table_lookup(table->reported_entries, uid)) 
     510                        deleted_entries = osync_list_prepend(deleted_entries, (char *) uid); 
    490511        } 
    491512 
    492         osync_db_free_list(result); 
    493  
    494         osync_trace(TRACE_EXIT, "%s: %p(%i)", __func__, ret, num); 
    495         return ret; 
    496 
    497  
    498 /*! @brief Gets the changetype for a given uid and hash 
     513        g_list_free(db_entries); 
     514 
     515        osync_trace(TRACE_EXIT, "%s: %p", __func__, deleted_entries); 
     516        return deleted_entries; 
     517
     518 
     519/*! @brief Gets the changetype for the given OSyncChange object, by comparing the hashs 
     520 *         of the hashtable and OSyncChange object. 
    499521 *  
    500522 * This function does not report the object so if you only use this function then 
     
    504526 * @param table The hashtable 
    505527 * @param uid The uid to lookup 
    506  * @param hash The hash to compare 
     528 * @param newhash The new hash to compare with the stored hash 
    507529 * @returns The changetype 
    508530 *  
    509531 */ 
    510 OSyncChangeType osync_hashtable_get_changetype(OSyncHashTable *table, const char *uid, const char *hash) 
    511 
    512         osync_trace(TRACE_ENTRY, "%s(%p, %s, %s)", __func__, table, uid, hash); 
    513         osync_assert(table); 
    514         osync_assert(table->dbhandle); 
    515  
    516         char *orighash = NULL; 
    517          
    518         OSyncChangeType retval = OSYNC_CHANGE_TYPE_UNMODIFIED; 
    519  
    520         char *escaped_uid = osync_db_sql_escape(uid); 
    521         char *query = g_strdup_printf("SELECT hash FROM %s WHERE uid='%s'", table->tablename, escaped_uid); 
    522         orighash = osync_db_query_single_string(table->dbhandle, query, NULL);  
    523         g_free(query); 
    524         g_free(escaped_uid); 
    525          
    526         osync_trace(TRACE_INTERNAL, "Comparing %s with %s", hash, orighash); 
     532OSyncChangeType osync_hashtable_get_changetype(OSyncHashTable *table, OSyncChange *change) 
     533
     534        osync_assert(table); 
     535        osync_assert(table->dbhandle); 
     536        osync_assert(change); 
     537 
     538        osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, table, change); 
     539 
     540        OSyncChangeType retval = OSYNC_CHANGE_TYPE_UNKNOWN; 
     541 
     542        const char *uid = osync_change_get_uid(change); 
     543        const char *newhash = osync_change_get_hash(change); 
     544 
     545        const char *orighash = osync_hashtable_get_hash(table, uid); 
    527546         
    528547        if (orighash) { 
    529                 if (!strcmp(hash, orighash)) 
     548                if (!strcmp(newhash, orighash)) 
    530549                        retval = OSYNC_CHANGE_TYPE_UNMODIFIED; 
    531550                else 
     
    534553                retval = OSYNC_CHANGE_TYPE_ADDED; 
    535554 
    536         g_free(orighash); 
    537555         
    538556        osync_trace(TRACE_EXIT, "%s: %i", __func__, retval); 
     
    540558} 
    541559 
     560unsigned int osync_hashtable_num_entries(OSyncHashTable *table) 
     561{ 
     562        osync_assert(table); 
     563        return g_hash_table_size(table->db_entries); 
     564} 
     565 
     566void osync_hashtable_foreach(OSyncHashTable *table, OSyncHashtableForEach func, void *user_data) 
     567{ 
     568        osync_assert(table); 
     569        g_hash_table_foreach(table->db_entries, (GHFunc) func, user_data); 
     570} 
     571 
     572const char *osync_hashtable_get_hash(OSyncHashTable *table, const char *uid) 
     573{ 
     574        osync_assert(table); 
     575        osync_assert(uid); 
     576 
     577        return (const char *)  g_hash_table_lookup(table->db_entries, uid); 
     578} 
     579 
    542580/*@}*/ 
  • trunk/opensync/helper/opensync_hashtable.h

    r3151 r3290  
    22 * libopensync - A synchronization framework 
    33 * Copyright (C) 2004-2005  Armin Bauer <armin.bauer@opensync.org> 
     4 * Copyright (C) 2008  Daniel Gollub <dgollub@suse.de> 
    45 *  
    56 * This library is free software; you can redistribute it and/or 
     
    2223#define OPENSYNC_HASHTABLE_H_ 
    2324 
     25typedef void (*OSyncHashtableForEach) (const char *uid, const char *hash, void *user_data); 
     26 
    2427OSYNC_EXPORT OSyncHashTable *osync_hashtable_new(const char *path, const char *objtype, OSyncError **error); 
    25 OSYNC_EXPORT void osync_hashtable_free(OSyncHashTable *table); 
     28 
     29OSYNC_EXPORT OSyncHashTable *osync_hashtable_ref(OSyncHashTable *table); 
     30OSYNC_EXPORT void osync_hashtable_unref(OSyncHashTable *table); 
     31 
     32OSYNC_EXPORT osync_bool osync_hashtable_load(OSyncHashTable *table, OSyncError **error); 
     33OSYNC_EXPORT osync_bool osync_hashtable_save(OSyncHashTable *table, OSyncError **error); 
    2634 
    2735OSYNC_EXPORT osync_bool osync_hashtable_slowsync(OSyncHashTable *table, OSyncError **error); 
    2836 
    29 OSYNC_EXPORT int osync_hashtable_num_entries(OSyncHashTable *table); 
    30 OSYNC_EXPORT osync_bool osync_hashtable_nth_entry(OSyncHashTable *table, int i, char **uid, char **hash); 
    31 OSYNC_EXPORT void osync_hashtable_write(OSyncHashTable *table, const char *uid, const char *hash); 
    32 OSYNC_EXPORT void osync_hashtable_delete(OSyncHashTable *table, const char *uid); 
    33 OSYNC_EXPORT void osync_hashtable_update_hash(OSyncHashTable *table, OSyncChangeType type, const char *uid, const char *hash); 
     37OSYNC_EXPORT unsigned int osync_hashtable_num_entries(OSyncHashTable *table); 
     38OSYNC_EXPORT void osync_hashtable_foreach(OSyncHashTable *table, OSyncHashtableForEach func, void *user_data); 
    3439 
    35 OSYNC_EXPORT void osync_hashtable_report(OSyncHashTable *table, const char *uid); 
    36 OSYNC_EXPORT void osync_hashtable_reset_reports(OSyncHashTable *table); 
     40OSYNC_EXPORT void osync_hashtable_update_change(OSyncHashTable *table, OSyncChange *change); 
    3741 
    38 OSYNC_EXPORT char **osync_hashtable_get_deleted(OSyncHashTable *table); 
    39 OSYNC_EXPORT OSyncChangeType osync_hashtable_get_changetype(OSyncHashTable *table, const char *uid, const char *hash); 
    40 OSYNC_EXPORT char *osync_hashtable_get_hash(OSyncHashTable *table, const char *uid); 
     42//OSYNC_EXPORT void osync_hashtable_report(OSyncHashTable *table, OSyncChange *change); 
     43//OSYNC_EXPORT void osync_hashtable_reset_reports(OSyncHashTable *table); 
     44 
     45OSYNC_EXPORT OSyncList *osync_hashtable_get_deleted(OSyncHashTable *table); 
     46OSYNC_EXPORT OSyncChangeType osync_hashtable_get_changetype(OSyncHashTable *table, OSyncChange *change); 
     47OSYNC_EXPORT const char *osync_hashtable_get_hash(OSyncHashTable *table, const char *uid); 
    4148 
    4249#endif /* OPENSYNC_HASHTABLE_H_ */ 
  • trunk/opensync/helper/opensync_hashtable_internals.h

    r3172 r3290  
    2323#define _OPENSYNC_HASHTABLE_INTERNALS_H_ 
    2424 
     25#define OSYNC_HASHTABLE_DB_PREFIX "tbl_hash_" 
     26 
    2527/*! @brief Represent a hashtable which can be used to check if changes have been modifed or deleted */ 
    2628struct OSyncHashTable { 
     29        int ref_count; 
    2730        OSyncDB *dbhandle; 
    28         GHashTable *used_entries; 
    29         char *tablename; 
     31 
     32        GHashTable *reported_entries; 
     33 
     34        GHashTable *db_entries; 
     35 
     36        char *name; 
     37 
     38        /* Only to build transaction queries */ 
     39        GString *query; 
    3040}; 
    3141