src/midgard_collector.c

00001 /* 
00002  * Copyright (C) 2006 Piotr Pokora <piotrek.pokora@gmail.com>
00003  *
00004  * This program is free software; you can redistribute it and/or modify it
00005  * under the terms of the GNU Lesser General Public License as published
00006  * by the Free Software Foundation; either version 2 of the License, or
00007  * (at your option) any later version.
00008  * 
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  * 
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 #include "midgard/midgard_collector.h"
00020 #include "midgard/types.h"
00021 #include "midgard/query_builder.h"
00022 #include "midgard/midgard_object.h"
00023 #include "midgard_core_query_builder.h"
00024 #include "midgard_mysql.h"
00025 
00026 struct _MidgardCollectorPrivate{
00027         const gchar *typename;
00028         GType type;
00029         const gchar *domain;
00030         GValue *domain_value;
00031         const gchar *keyname;
00032         GValue *keyname_value;
00033         MidgardQueryBuilder *builder;
00034         MidgardObjectClass *klass;
00035         GList *values;
00036         GHashTable *keyshash;
00037 };
00038 
00039 /* FIXME */
00040 /* This function return duplicated string due to lack of guid's nick.
00041  * It should be resolved on mgdschema level by column name assigned to property
00042  * and available on core's level API */
00043 static const gchar *_collector_find_class_property(
00044                 MidgardCollector *self, const gchar *propname)
00045 {
00046         if(g_str_equal(propname, "guid"))
00047                 return g_strdup_printf(
00048                                 "%s.guid",
00049                                 midgard_object_class_get_table(
00050                                         self->private->klass));
00051 
00052         GParamSpec *pspec = g_object_class_find_property(
00053                         G_OBJECT_CLASS(self->private->klass), propname);
00054         if(!pspec){
00055                 g_warning("Invalid property name '%s' for '%s' class",
00056                                 propname,
00057                                 self->private->typename);
00058                 return NULL;
00059         }
00060         return g_strdup(g_param_spec_get_nick(pspec));
00061 }
00062 
00063 static void _unset_subkey_value(gpointer value)
00064 {
00065         g_value_unset((GValue *)value);
00066         g_free(value);
00067 }
00068 
00069 /* Creates new Midgard Collector object instance */
00070 MidgardCollector *midgard_collector_new(
00071                 MidgardConnection *mgd, const gchar *typename, 
00072                 const gchar *domain, GValue *value)
00073 {
00074         g_assert(mgd);
00075         g_assert(typename);
00076         g_assert(domain);
00077         g_assert(value);
00078 
00079         MidgardCollector *self = 
00080                 (MidgardCollector *)g_object_new(MIDGARD_TYPE_COLLECTOR, NULL);
00081         
00082         self->private->klass =
00083                 MIDGARD_OBJECT_GET_CLASS_BY_NAME(typename);
00084                 
00085         const gchar *nick = 
00086                 _collector_find_class_property(self, domain);
00087         if(!nick) {
00088                 if(value){
00089                         g_value_unset(value);
00090                         g_free(value);
00091                 }
00092                 return NULL;    
00093         }
00094         g_free((gchar *)nick);
00095 
00096         self->private->typename = (const gchar *) g_strdup(typename);
00097         self->private->type = g_type_from_name(typename);
00098         self->private->domain = (const gchar*) g_strdup(domain);
00099         self->private->domain_value = value;
00100         self->private->keyname = NULL;
00101         self->private->keyname_value = NULL;
00102         
00103         /* Initialize private QB instance and set domain as constraint */
00104         self->private->builder = midgard_query_builder_new(
00105                         mgd->mgd, typename);
00106         midgard_query_builder_add_constraint(self->private->builder,
00107                         domain,
00108                         "=", value);
00109         g_value_unset(value);
00110         g_free(value);
00111         
00112         self->private->values = NULL;
00113         self->private->keyshash = g_hash_table_new(NULL, NULL); 
00114                         
00115         return self;
00116 }
00117 
00118 /* Sets key property for the given MidgardCollector object. */
00119 gboolean midgard_collector_set_key_property(
00120                 MidgardCollector *self, const gchar *key, GValue *value)
00121 {
00122         g_assert(self != NULL);
00123 
00124         const gchar *nick = 
00125                 _collector_find_class_property(self, key);
00126         if(!nick)
00127                 return FALSE;
00128 
00129         gchar *sql_field = g_strconcat(nick, " AS midgard_collector_key", NULL);
00130         g_free((gchar *)nick);
00131         self->private->values = g_list_prepend(self->private->values,
00132                         sql_field);
00133 
00134         self->private->keyname = (const gchar*) g_strdup(key);
00135 
00136         /* add constraint if the value is set*/
00137         if(value){
00138                 midgard_query_builder_add_constraint(
00139                                 self->private->builder,
00140                                 key,
00141                                 "=", value);
00142                 self->private->keyname_value = value;
00143         }
00144 
00145         return TRUE;
00146 }
00147 
00148 /* Adds value property for the given MidgardCollector object.*/
00149 gboolean midgard_collector_add_value_property(
00150                 MidgardCollector *self, const gchar *value)
00151 {
00152         g_assert(self);
00153 
00154         if(!self->private->keyname){
00155                 g_warning("Collector's key is not set. Call set_key_property method");
00156                 return FALSE;
00157         }
00158 
00159         const gchar *nick = 
00160                 _collector_find_class_property(self, value);
00161         if(!nick)
00162                 return FALSE;
00163         
00164         gchar *sql_field = g_strconcat(nick, " AS ", value, NULL); 
00165         g_free((gchar *)nick);
00166         self->private->values = g_list_prepend(self->private->values,
00167                         sql_field);
00168         
00169         return TRUE;
00170 }
00171 
00172 /* Sets ( or adds  new key ) and new key's value for the given MidgardCollector. */
00173 gboolean midgard_collector_set(
00174                 MidgardCollector *collector, 
00175                 const gchar *key, const gchar *subkey, GValue *value)
00176 {
00177         g_assert(collector);
00178         GQuark keyquark = g_quark_from_string(key);
00179 
00180         if(subkey == NULL){
00181                 g_hash_table_insert(collector->private->keyshash,
00182                                 (gpointer)keyquark,
00183                                 NULL);
00184                 return TRUE;
00185         }
00186 
00187         const gchar *nick = 
00188                 _collector_find_class_property(collector, subkey);
00189         if(!nick)
00190                 return FALSE;
00191         g_free((gchar *)nick);
00192         
00193         GQuark subkeyquark = g_quark_from_string(subkey);
00194         GHashTable *valueshash = 
00195                 g_hash_table_lookup(collector->private->keyshash, (gpointer)keyquark);
00196 
00197         if(valueshash == NULL){
00198                 valueshash = g_hash_table_new_full(NULL, NULL,
00199                                 NULL, _unset_subkey_value);
00200         }
00201 
00202         g_hash_table_insert(valueshash, (gpointer)subkeyquark, (gpointer)value);
00203         g_hash_table_insert(collector->private->keyshash,
00204                         (gpointer)keyquark,
00205                         valueshash);
00206         return TRUE;
00207 }
00208 
00209 /* Get key's value for the given MidgardCollector object. */
00210 GHashTable *midgard_collector_get(
00211                 MidgardCollector *self, const gchar *key)
00212 {
00213         g_assert(self);
00214 
00215         if(!self->private->keyname){
00216                 g_warning("Collector's key is not set. Call set_key_property method");
00217                 return FALSE;
00218         }
00219 
00220         return g_hash_table_lookup(self->private->keyshash, 
00221                         (gpointer)g_quark_from_string(key));
00222 }
00223 
00224 /* Get subkey's value */
00225 GValue *midgard_collector_get_subkey(
00226                 MidgardCollector *self, const gchar *key, const gchar *subkey)
00227 {
00228         g_assert(self);
00229 
00230         const gchar *nick;
00231         nick = _collector_find_class_property(self, subkey);
00232         if(!nick)
00233                 return NULL;
00234 
00235         g_free((gchar *)nick);
00236 
00237         if(self->private->keyshash == NULL){
00238                 g_warning("Collector's key set with NULL value");
00239                 return NULL;
00240         }
00241 
00242         GHashTable *valueshash =
00243                 g_hash_table_lookup(self->private->keyshash, 
00244                                 (gpointer)g_quark_from_string(key));
00245 
00246         if(!valueshash) {
00247                 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, 
00248                                 "midgard_collector: No subkeys associated with the given '%s' key",
00249                                 key);
00250                 return NULL;
00251         }
00252 
00253         GValue *value = g_hash_table_lookup(valueshash, 
00254                         (gpointer)g_quark_from_string(subkey));
00255 
00256         return value;
00257 }
00258 
00259 void _get_collection_keys(gpointer key, gpointer value, 
00260                 gpointer userdata)
00261 {
00262         GList **list = userdata;
00263         *list = g_list_prepend(*list, 
00264                         (gchar *)g_quark_to_string((GQuark) key));      
00265 }
00266 
00267 static void _merge_hashvalues(
00268                 gpointer key, gpointer val, gpointer userdata)
00269 {
00270         GValue *value = (GValue *) val;
00271         GHashTable *hashvalues = (GHashTable *) userdata;
00272         GValue *new_value = g_new0(GValue, 1);
00273         g_value_init(new_value, G_VALUE_TYPE(value));
00274         g_value_copy(value, new_value);
00275 
00276         g_hash_table_insert(hashvalues, key, (gpointer)new_value);
00277 }
00278 
00279 /* Merges collection's keys and its values. */
00280 gboolean midgard_collector_merge(
00281                 MidgardCollector *self, MidgardCollector *mc, 
00282                 gboolean overwrite)
00283 {
00284         g_assert(self);
00285         g_assert(mc);
00286 
00287         if(!MIDGARD_COLLECTOR(mc)){
00288                 g_warning("Second argument is not an instance of midgard_collector class!");
00289                 return FALSE;
00290         }
00291 
00292         guint i = 0;
00293         const gchar **keys = 
00294                 midgard_collector_list_keys(mc);
00295         GHashTable *self_hashvalues, *mc_hashvalues;
00296 
00297         if(!keys) return FALSE;
00298 
00299         while(keys[i] != NULL) {
00300                 
00301                 self_hashvalues = 
00302                         midgard_collector_get(self, keys[i]);
00303                 if((self_hashvalues && overwrite) || 
00304                                 (self_hashvalues == NULL)){
00305                         
00306                         mc_hashvalues = 
00307                                 midgard_collector_get(mc, keys[i]);
00308 
00309                         if(!self_hashvalues)
00310                                 self_hashvalues = 
00311                                         g_hash_table_new_full(NULL, NULL,
00312                                                         NULL, _unset_subkey_value);
00313                         g_hash_table_foreach(mc_hashvalues,
00314                                         _merge_hashvalues, self_hashvalues);
00315                         g_hash_table_insert(self->private->keyshash,
00316                                         (gpointer)g_quark_from_string(keys[i]),
00317                                         self_hashvalues);
00318                 }       
00319                 i++;
00320         }
00321         g_free((gchar *)keys);
00322         return TRUE;
00323 }
00324 
00325 /* List all keys in collection */
00326 const gchar **midgard_collector_list_keys(
00327                 MidgardCollector *self)
00328 {       
00329         g_assert(self);
00330 
00331         guint i = 0, hash_size = g_hash_table_size(self->private->keyshash);
00332         const gchar **keys = g_new(const gchar *, hash_size+1);
00333         GList *list = NULL;     
00334 
00335         g_hash_table_foreach(self->private->keyshash,
00336                         _get_collection_keys, &list);
00337 
00338         if(list) {
00339                 
00340                 list = g_list_reverse(list);
00341                 for(; list; list = list->next){
00342                         keys[i] = list->data;
00343                         i++;
00344                 }
00345                 keys[i] = NULL;
00346                 g_list_free(list);
00347                 return keys;
00348         }
00349         
00350         g_free(keys);
00351         return NULL;
00352 }
00353 
00354 /* Removes key and associated value for the given MidgardCollector object.*/
00355 gboolean midgard_collector_remove_key(
00356                 MidgardCollector *self, const gchar *key)
00357 {
00358         g_assert(self);
00359         g_assert(self);
00360 
00361         GQuark keyquark = g_quark_from_string(key);
00362 
00363         GHashTable *valueshash = 
00364                 g_hash_table_lookup(self->private->keyshash,
00365                                 (gpointer)keyquark);
00366         if(!valueshash)
00367                 return FALSE;
00368 
00369         g_hash_table_destroy(valueshash);
00370 
00371         return g_hash_table_remove(self->private->keyshash, 
00372                         (gpointer)keyquark);
00373 }
00374 
00375 /* Destroys given MidgardCollector object. */
00376 void midgard_collector_destroy(
00377                 MidgardCollector *self)
00378 {
00379         g_assert(self);
00380         g_object_unref(self);
00381 }
00382 
00383 
00384 /* Parent methods re-implementation */
00385 
00386 gboolean midgard_collector_add_constraint(
00387                 MidgardCollector *self, const gchar *name, 
00388                 const gchar *op, const GValue *value)
00389 {
00390         g_assert(self);
00391         return MIDGARD_QUERY_BUILDER_GET_CLASS(
00392                         self->private->builder)->add_constraint(
00393                                 self->private->builder, name,
00394                                 op, value);
00395 }
00396 
00397 gboolean midgard_collector_begin_group(
00398                 MidgardCollector *self, const gchar *type)
00399 {
00400         g_assert(self);
00401         return MIDGARD_QUERY_BUILDER_GET_CLASS(
00402                         self->private->builder)->begin_group(
00403                                 self->private->builder, type);
00404 }
00405 
00406 gboolean midgard_collector_end_group(
00407                 MidgardCollector *self)
00408 {
00409         g_assert(self);
00410         return MIDGARD_QUERY_BUILDER_GET_CLASS(
00411                         self->private->builder)->end_group(
00412                                 self->private->builder);
00413 }
00414 
00415 gboolean midgard_collector_add_order(
00416                 MidgardCollector *self,
00417                 const gchar *name, const gchar *dir)
00418 {
00419         g_assert(self);
00420         return MIDGARD_QUERY_BUILDER_GET_CLASS(
00421                         self->private->builder)->add_order(
00422                                 self->private->builder, name, dir);
00423 }
00424 
00425 void midgard_collector_set_offset(
00426                 MidgardCollector *self, guint offset)
00427 {
00428         g_assert(self);
00429         MIDGARD_QUERY_BUILDER_GET_CLASS(
00430                         self->private->builder)->set_offset(
00431                                 self->private->builder, offset);
00432 }
00433 
00434 void midgard_collector_set_limit(
00435                 MidgardCollector *self, guint limit)
00436 {
00437         g_assert(self);
00438         MIDGARD_QUERY_BUILDER_GET_CLASS(
00439                         self->private->builder)->set_limit(
00440                                 self->private->builder, limit);
00441 }
00442 
00443 void midgard_collector_unset_languages(
00444                 MidgardCollector *self)
00445 {
00446         g_assert(self);
00447         MIDGARD_QUERY_BUILDER_GET_CLASS(
00448                         self->private->builder)->unset_languages(
00449                                 self->private->builder);
00450 }
00451 
00452 void midgard_collector_count(
00453                 MidgardCollector *self)
00454 {
00455         g_assert(self);
00456         return;
00457 }
00458 
00459 gboolean midgard_collector_execute(
00460                 MidgardCollector *self)
00461 {
00462         g_assert(self);
00463 
00464         if(!self->private->keyname){
00465                 g_warning("Collector's key is not set. Call set_key_property method");
00466                 return FALSE;
00467         }
00468 
00469         if(!self->private->values)
00470                 return FALSE;
00471 
00472         GList *list =
00473                 g_list_reverse(self->private->values);
00474         GString *sgs = g_string_new("");
00475         guint i = 0;
00476         for( ; list; list = list->next){
00477                 if(i > 0)
00478                         g_string_append(sgs, ", ");
00479                 g_string_append(sgs, list->data);
00480                 i++;
00481         } 
00482 
00483         gchar *select = g_string_free(sgs, FALSE);
00484         gchar *sql = _midgard_core_qb_get_sql( 
00485                         self->private->builder, MQB_SELECT_FIELD, 
00486                         select);
00487         
00488         g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "query=%s", sql);
00489         if(!sql)
00490                 return FALSE;
00491         
00492         gint sq = mysql_query(self->private->builder->mgd->msql->mysql, sql);
00493 
00494         if (sq != 0) {
00495                 g_warning("\n\nQUERY: \n %s \n\n FAILED: \n %s",
00496                                 sql,
00497                                 mysql_error(self->private->builder->mgd->msql->mysql));
00498                 g_free(sql);
00499                 return FALSE;
00500         }
00501         g_free(sql);
00502 
00503         i = 0;
00504         guint ret_rows, ret_fields, j;
00505         MYSQL_ROW row;
00506         MYSQL_FIELD *field;
00507         MYSQL_RES *results = mysql_store_result(self->private->builder->mgd->msql->mysql);
00508         GParamSpec *pspec;
00509         //GValue pval = {0, };
00510 
00511         if (!results)
00512                 return FALSE;
00513         
00514         if ((ret_rows = mysql_num_rows(results)) == 0) {
00515                 mysql_free_result(results);
00516                 return FALSE;
00517         }
00518         
00519         for(i = 0; i < ret_rows; i++){
00520                 row = mysql_fetch_row(results);
00521                 ret_fields =  mysql_num_fields(results);
00522         
00523                 for (j = 0; j < ret_fields; j++){
00524                         field = mysql_fetch_field_direct(results, j);
00525                         
00526                         if ((pspec = g_object_class_find_property(
00527                                                         (GObjectClass *)self->private->klass, 
00528                                                         field->name)) != NULL) {
00529                         
00530                                 GValue *pval = g_new0(GValue, 1);
00531                                 g_value_init(pval, pspec->value_type);
00532                                 switch(pspec->value_type){
00533                                         
00534                                         case G_TYPE_STRING:
00535                                                 g_value_set_string(pval, (gchar *)row[j]);
00536                                                 break;
00537                                                 
00538                                         case G_TYPE_UINT:
00539                                                 g_value_set_uint(pval, atoi(row[j]));
00540                                                 break;
00541                                                 
00542                                         case G_TYPE_INT:
00543                                                 g_value_set_int(pval, atoi(row[j]));
00544                                                 break;
00545                                                 
00546                                         case G_TYPE_FLOAT:
00547                                                 g_value_set_float(pval, g_ascii_strtod(row[j], NULL));
00548                                                 break;
00549                                                 
00550                                         case G_TYPE_BOOLEAN:
00551                                                 g_value_set_boolean(pval, atoi(row[j]));
00552                                                 break;                                          
00553                                 }       
00554                                 midgard_collector_set(self,
00555                                         (gchar *) row[0],
00556                                         field->name,
00557                                         pval);
00558 
00559                         } else if (ret_fields == 1){
00560 
00561                                 midgard_collector_set(self,
00562                                                 (gchar *) row[0],
00563                                                 NULL,
00564                                                 NULL);
00565                         }
00566                 }
00567         }
00568 
00569         mysql_free_result(results);
00570         return TRUE;
00571 }
00572 
00573 /* GOBJECT ROUTINES */
00574 
00575 static void _midgard_collector_instance_init(
00576                 GTypeInstance *instance, gpointer g_class)
00577 {
00578         MidgardCollector *self = (MidgardCollector *) instance;
00579         self->private = g_new(MidgardCollectorPrivate, 1);
00580 
00581         self->private->domain = NULL;
00582         self->private->domain_value = NULL;
00583         self->private->typename = NULL;
00584         self->private->type = 0;
00585         self->private->klass = NULL;
00586 }
00587 
00588 static gboolean _destroy_collector_hash(
00589                 gpointer key, gpointer value, gpointer user_data)
00590 {
00591         GHashTable *valueshash = (GHashTable *) value;
00592         g_hash_table_destroy(valueshash); 
00593         
00594         return TRUE;
00595 }
00596 
00597 static void _midgard_collector_finalize(GObject *object)
00598 {
00599         g_assert(object != NULL);
00600 
00601         MidgardCollector *self = (MidgardCollector *) object;
00602 
00603         if(self->private->keyshash)
00604                 g_hash_table_foreach_remove(self->private->keyshash,
00605                                 _destroy_collector_hash,
00606                                 NULL);
00607 
00608         
00609         g_free((gchar *)self->private->typename);
00610         g_free((gchar *)self->private->domain);
00611         g_free((gchar *)self->private->keyname);
00612         
00613         if(self->private->keyname_value) {
00614                 g_value_unset(self->private->keyname_value);
00615                 g_free(self->private->keyname_value);
00616         }
00617         
00618         g_object_unref(self->private->builder);
00619 
00620         GList *list = self->private->values;
00621         for( ; list; list = list->next){
00622                 g_free(list->data);
00623         }
00624         g_list_free(list);
00625 
00626         g_free(self->private);
00627 }
00628 
00629 static void _midgard_collector_class_init(
00630                 gpointer g_class, gpointer g_class_data)
00631 {
00632         GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
00633         MidgardCollectorClass *klass = MIDGARD_COLLECTOR_CLASS (g_class);
00634         
00635         gobject_class->finalize = _midgard_collector_finalize;
00636         klass->set_key_property = midgard_collector_set_key_property;
00637         klass->add_value_property = midgard_collector_add_value_property;
00638         klass->set = midgard_collector_set;
00639         klass->get = midgard_collector_get;
00640         klass->get_subkey = midgard_collector_get_subkey;
00641         klass->merge = midgard_collector_merge;
00642         klass->list_keys = midgard_collector_list_keys;
00643         klass->remove_key = midgard_collector_remove_key;
00644         klass->destroy = midgard_collector_destroy;
00645         klass->add_constraint = midgard_collector_add_constraint;
00646         klass->begin_group = midgard_collector_begin_group;
00647         klass->end_group = midgard_collector_end_group;
00648         klass->add_order = midgard_collector_add_order;
00649         klass->set_offset = midgard_collector_set_offset;
00650         klass->set_limit = midgard_collector_set_limit;
00651         klass->unset_languages = midgard_collector_unset_languages;
00652         klass->count = midgard_collector_count;
00653         klass->execute = midgard_collector_execute;
00654 }
00655 
00656 GType midgard_collector_get_type(void)
00657 {
00658         static GType type = 0;
00659         if (type == 0) {
00660                 static const GTypeInfo info = {
00661                         sizeof (MidgardCollectorClass),
00662                         NULL,           /* base_init */
00663                         NULL,           /* base_finalize */
00664                         (GClassInitFunc) _midgard_collector_class_init,
00665                         NULL,           /* class_finalize */
00666                         NULL,           /* class_data */
00667                         sizeof (MidgardCollector),
00668                         0,              /* n_preallocs */
00669                         (GInstanceInitFunc) _midgard_collector_instance_init /* instance_init */
00670                 };      
00671                 type = g_type_register_static (MIDGARD_TYPE_QUERY_BUILDER,
00672                                 "midgard_collector",
00673                                 &info, 0);
00674         }
00675         return type;
00676 }

Generated on Thu Feb 22 06:15:17 2007 for midgard-core by  doxygen 1.4.6