src/query_builder.c

00001 /*
00002  * Copyright (c) 2005 Jukka Zitting <jz@yukatan.fi>
00003  * Copyright (c) 2005 Piotr Pokora <piotr.pokora@infoglob.pl>
00004  *
00005  * This program is free software; you can redistribute it and/or modify it
00006  * under the terms of the GNU Lesser General Public License as published
00007  * by the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018  */
00019 
00020 #include <config.h>
00021 #include "midgard/midgard_schema.h"
00022 #include "midgard/query_builder.h"
00023 #include "midgard/midgard_legacy.h"
00024 #include "query_constraint.h"
00025 #include "simple_constraint.h"
00026 #include "group_constraint.h"
00027 #include "query_order.h"
00028 #include "midgard/midgard_object_property.h"
00029 #include "midgard/midgard_object.h"
00030 #include "schema.h"
00031 #include "midgard_mysql.h"
00032 #include "midgard_core_query_builder.h"
00033 #include "midgard/midgard_error.h"
00034 #include "midgard_core_object.h"
00035 
00036 /* Internal prototypes , I am not sure if should be included in API */
00037 
00038 gchar *midgard_query_builder_get_object_select(MidgardQueryBuilder *builder, guint select_type);
00039 
00040 GList *_mqb_set_object_from_query(MidgardQueryBuilder *builder, guint select_type) ;
00041 
00042 /* Adds table string for the given builder instance. */
00043 void _midgard_core_qb_add_table(
00044                 MidgardQueryBuilder *builder, const gchar *table)
00045 {
00046         g_assert(builder);
00047         
00048         if(builder->tables){
00049                 guint tlen = strlen(builder->tables); /* tlen doesn't mean oxygen ;) */
00050                 gchar *table_found = g_strstr_len(
00051                                 builder->tables,
00052                                 tlen,
00053                                 table);
00054                 if(!table_found){
00055                         gchar *tmptable  = g_strconcat(builder->tables,
00056                                         ",",
00057                                         table,
00058                                         NULL);
00059                         g_free(builder->tables);
00060                         builder->tables = g_strdup(tmptable);
00061                         g_free(tmptable);
00062                 }
00063         }
00064 }
00065 
00066 /* Appends SQL query part with particular "AND ..." comparison */
00067 void _midgard_core_qb_add_join(
00068                 MidgardQueryBuilder *builder, 
00069                 const gchar *table_a, const gchar *column_a,
00070                 const gchar *table_b, const gchar *column_b)
00071 {
00072         g_assert(table_a != NULL && column_a != NULL 
00073                         && table_b != NULL && column_b != NULL);
00074 
00075         g_string_append(builder->join, " AND ");
00076         g_string_append_printf(builder->join,
00077                         "%s.%s = %s.%s",
00078                         table_a, column_a,
00079                         table_b, column_b);
00080 }
00081 
00082 static void _mqb_type_pop(MidgardQueryBuilder *builder){
00083 
00084         g_assert(builder != NULL);
00085         builder->type = (guint)g_list_nth_data(builder->types, 0);
00086 }
00087 
00088 static gchar **_mqb_parse_constraint_property(
00089                 MidgardQueryBuilder *builder, gchar **name)
00090 {
00091         g_assert(builder != NULL);
00092         
00093         if(!name[2])
00094                 return name;
00095         guint i, n = 0;
00096         MgdObject *ref_object = NULL; 
00097         const gchar *reftype = NULL;
00098         MidgardObjectClass *klass =
00099                 (MidgardObjectClass*) g_type_class_peek(builder->type);
00100         MidgardObjectClass *ref_klass =
00101                 midgard_object_property_get_link_class(
00102                                 klass, name[0]);
00103         if(ref_klass)
00104                 reftype = G_OBJECT_CLASS_NAME(G_OBJECT_CLASS(ref_klass));
00105         
00106         if (reftype != NULL)
00107                 ref_object = midgard_object_new(builder->mgd, reftype, NULL);
00108         if(ref_object == NULL) {
00109                 g_warning("Type %s in not registered in application schema",
00110                                 reftype);
00111                 return NULL;
00112         }
00113         GType new_type = G_TYPE_FROM_INSTANCE(ref_object);
00114         builder->parent_type = new_type;
00115         builder->type = new_type;
00116         builder->types = g_list_append(builder->types, (gpointer)builder->type);
00117         
00118         for(i = 0 ; name[i]; i++){
00119                 n++;
00120         }
00121         gchar **str_array = g_new (gchar *, n);
00122         for(i = 0 ; name[i]; i++){
00123                 str_array[i] = g_strdup(name[i+1]);
00124         }
00125         g_strfreev(name);
00126         return _mqb_parse_constraint_property(builder, str_array);                      
00127 }
00128 
00129 
00130 /* Internal function to set tables member and get referenced object 
00131  * or property of object type.
00132  */
00133 static gchar * _mqb_get_new_property(
00134         MidgardQueryBuilder *builder, const gchar *name){
00135 
00136         g_assert(builder);
00137 
00138         gchar *new_name = (gchar *)name;
00139         guint strl = 0;
00140 
00141         builder->ext_type = 0;
00142         builder->parent_type = 0;
00143         
00144         if((strl = strlen(name)) == 0)
00145                 return new_name;
00146 
00147         gchar **spltd, *tmpstr;
00148         MgdObject *ref_object = NULL;
00149         
00150         /* We're getting referenced object from schema, or reserved object type */
00151         tmpstr = g_strstr_len(name, strl, ".");
00152         if(tmpstr != NULL){                             
00153                 spltd = g_strsplit(name, ".", 0);
00154                 
00155                 if(spltd[2])
00156                         spltd = _mqb_parse_constraint_property(builder, spltd);
00157                 if(!spltd){             
00158                         g_strfreev(spltd);
00159                         return new_name;
00160                 }
00161                         
00162                 if (spltd[1] !=  NULL) {
00163                         new_name = g_strdup(spltd[1]);
00164                 
00165                         /* METADATA */
00166                         /* This is an exception , midgard_metadata is assigned as metadata property */
00167                         if(g_str_equal(spltd[0], "metadata"))  {
00168                                 /* parent type is set from current type or from type set 
00169                                  * while constraint property was parsed. */
00170                                 builder->parent_type = builder->type;
00171                                 builder->type = g_type_from_name("midgard_metadata");
00172                                 builder->ext_type = builder->type;
00173                                 builder->types = g_list_append(builder->types, (gpointer)builder->type);
00174                                 g_strfreev(spltd);
00175                                 return new_name;
00176                         }
00177                         
00178                         /* PARAMETER */
00179                         if(g_str_equal(spltd[0], "parameter")) {
00180                                 builder->parent_type = builder->type;
00181                                 builder->type = g_type_from_name("midgard_parameter");
00182                                 builder->ext_type = builder->type;
00183                                 builder->types = g_list_append(builder->types, (gpointer)builder->type); 
00184                                 g_strfreev(spltd);
00185                                 _midgard_core_qb_add_table(builder, "record_extension");
00186                                 MidgardObjectClass *klass = 
00187                                         (MidgardObjectClass*) g_type_class_peek(builder->parent_type);
00188                                 _midgard_core_qb_add_join(builder,
00189                                                 midgard_object_class_get_table(klass), "guid",
00190                                                 "record_extension", "parent_guid");
00191                                 return new_name;
00192                         }                               
00193                         
00194                         /* ATTACHMENT */
00195                         if(g_str_equal(spltd[0], "attachment")) {
00196                                 builder->parent_type = builder->type;
00197                                 builder->type = g_type_from_name("midgard_attachment");
00198                                 builder->ext_type = builder->type;
00199                                 builder->types = g_list_append(builder->types, (gpointer)builder->type);
00200                                 g_strfreev(spltd);
00201                                 _midgard_core_qb_add_table(builder, "blobs");
00202                                 MidgardObjectClass *klass =
00203                                         (MidgardObjectClass*) g_type_class_peek(builder->parent_type);
00204                                 _midgard_core_qb_add_join(builder,
00205                                                 midgard_object_class_get_table(klass), "guid",
00206                                                 "blobs", "parent_guid");
00207                                 return new_name;
00208                         }
00209                                                 
00210                         /* We're getting link type defined for property in schema */
00211                         const gchar *reftype = NULL;
00212                         MidgardObjectClass *klass = 
00213                                 (MidgardObjectClass*) g_type_class_peek(builder->type);
00214                         MidgardObjectClass *ref_klass = 
00215                                 midgard_object_property_get_link_class(
00216                                                 klass, spltd[0]);
00217                         if(ref_klass)
00218                                 reftype = G_OBJECT_CLASS_NAME(G_OBJECT_CLASS(ref_klass));               
00219 
00220                         /* Referenced type exists so let's make new instance */
00221                         if (reftype != NULL) 
00222                                 ref_object = midgard_object_new(builder->mgd, reftype, NULL);
00223 
00224                         /* This should never happen in good applications , however we have 
00225                          * such "persistant" package like midgard-php used with apache module */
00226                         if(ref_object == NULL) {
00227                                 g_warning("Type %s in not registered in application schema",
00228                                                 reftype);
00229                                 return FALSE;
00230                         }
00231                 
00232                         const gchar *target_name;
00233                         GParamSpec *spec = NULL;
00234 
00235                         /* Find property to get nick and table */
00236                         MidgardReflectionProperty *mrp = 
00237                                 midgard_reflection_property_new(klass);
00238 
00239                         if(mrp) {
00240 
00241                                 target_name = 
00242                                         midgard_reflection_property_get_link_target(
00243                                                         mrp, spltd[0]);                 
00244                                 g_object_unref(mrp);
00245 
00246                                 spec = g_object_class_find_property(
00247                                                 G_OBJECT_CLASS(g_type_class_peek(ref_object->type)),
00248                                                 target_name);
00249                         } else {
00250                                 g_warning("Failed to initialize midgard_reflection_property");
00251                         }
00252 
00253                         if (spec) {
00254 
00255                                 if(g_str_equal(target_name, "guid"))
00256                                         builder->link = 
00257                                                 g_strjoin(".",
00258                                                                 midgard_object_class_get_table(ref_klass),
00259                                                                 "guid", NULL);
00260                                 else
00261                                         builder->link = g_strdup(g_param_spec_get_nick(spec));          
00262 
00263                                 GParamSpec *ospec = g_object_class_find_property(
00264                                                 G_OBJECT_CLASS(
00265                                                         g_type_class_peek(builder->type)),
00266                                                         spltd[0]);
00267                                 if(!ospec) {
00268                                         g_warning("Property '%s' not found for referenced "
00269                                                         "'%s' type",
00270                                                         spltd[0],
00271                                                         g_type_name(builder->type));
00272                                         return FALSE;
00273                                 }
00274 
00275                                 /* Set internal builder->type */
00276                                 builder->type = G_TYPE_FROM_INSTANCE(ref_object);
00277                                 //builder->types = g_list_append(builder->types, (gpointer)builder->type);
00278                                 
00279                                 g_string_append_printf(builder->join,
00280                                                 " AND %s = ",
00281                                                 g_param_spec_get_nick(ospec));
00282                                 
00283                                 g_string_append(builder->join, builder->link);
00284                                                                 
00285                                 /*Avoid adding table names which arleady exists in build->tables string */
00286                                 gchar **duptables = g_strsplit(builder->tables, ",", 0);
00287                                 guint i = 0, j = 0;
00288                                 for(i = 0 ; duptables[i]; i++){
00289                                         if (g_ascii_strcasecmp(duptables[i],
00290                                                                 midgard_object_class_get_table(ref_klass)) == 0 )
00291                                                 j++;
00292                                 }
00293 
00294                                 /* We have new table string so we need to concat all tables */
00295                                 if (j == 0) {
00296                                         gchar *tmptable  = g_strconcat(builder->tables,
00297                                                         ",", 
00298                                                         midgard_object_class_get_table(ref_klass), 
00299                                                         NULL);
00300                                         
00301                                         g_free(builder->tables);
00302                                         builder->tables = g_strdup(tmptable);
00303                                         g_free(tmptable);
00304                                 }
00305                                 g_strfreev(duptables);                                                  
00306                         }                               
00307                         g_object_unref(ref_object);                                                     
00308                 }
00309                 g_strfreev(spltd);
00310                 return new_name;
00311         }       
00312         return new_name;
00313 }
00314 
00315 MidgardQueryBuilder *midgard_query_builder_new(
00316         midgard *mgd, const gchar *classname)
00317 {
00318         g_assert(mgd);
00319         g_assert(classname);
00320 
00321         MidgardQueryBuilder *builder = 
00322                 g_object_new(MIDGARD_TYPE_QUERY_BUILDER, NULL);
00323         
00324         builder->mgd = mgd;
00325         builder->type = g_type_from_name(classname);
00326         builder->parent_type = 0;
00327         builder->ext_type = 0;
00328         builder->types = g_list_append(NULL, (gpointer)builder->type);
00329         builder->lang = -1;
00330         builder->unset_lang = FALSE;
00331         builder->include_deleted = FALSE;
00332         builder->tables = NULL;
00333         builder->link = NULL;
00334         builder->join = g_string_new(" ");
00335         builder->error = 0;
00336 
00337         MidgardObjectClass *klass = MIDGARD_OBJECT_GET_CLASS_BY_NAME(classname);
00338         builder->schema = klass->data;
00339         builder->tables = g_strdup(midgard_object_class_get_tables(klass));
00340         
00341         MidgardGroupConstraint *group =
00342                 midgard_group_constraint_new(MIDGARD_GROUP_CONSTRAINT_TYPE_AND);
00343         builder->stack = g_slist_prepend(NULL, group);
00344         builder->orders =
00345                 g_array_new(FALSE, FALSE, sizeof(MidgardQueryOrder *));
00346         builder->offset = 0;
00347         builder->limit = G_MAXUINT;
00348 
00349         if (builder->type && builder->schema) {
00350                 return builder;
00351         } else {
00352                 g_object_unref(builder);
00353                 return NULL;
00354         }
00355 }
00356 
00357 void midgard_query_builder_free(MidgardQueryBuilder *builder) {
00358         g_assert(builder);
00359 
00360         while (g_slist_next(builder->stack) != NULL) {
00361                 g_warning("Constraint groups not properly nested");
00362                 GSList *item = builder->stack;
00363                 builder->stack = g_slist_remove_link(builder->stack, item);
00364                 g_slist_free_1(item);
00365         }
00366         g_object_unref(G_OBJECT(builder->stack->data));
00367         g_slist_free(builder->stack);
00368 
00369         guint i;
00370         for (i = 0; i < builder->orders->len; i++) {
00371                 midgard_query_order_free(g_array_index(
00372                         builder->orders, MidgardQueryOrder *, i));
00373         }
00374         g_array_free(builder->orders, TRUE);
00375         if(builder->join) {
00376                 gchar *tmpstr = g_string_free(builder->join, FALSE);
00377                 g_free(tmpstr);
00378         }
00379         g_free((gchar *)builder->tables);
00380         g_free((gchar *)builder->link);
00381 }
00382 
00383 gboolean midgard_query_builder_add_constraint(
00384         MidgardQueryBuilder *builder,
00385         const gchar *name, const gchar *op, const GValue *value) {
00386         g_assert(builder);
00387         g_assert(name);
00388         g_assert(op);
00389         g_assert(value);
00390        
00391         GObjectClass *tmpklass = g_type_class_peek(builder->type);
00392 
00393         gchar *new_name = _mqb_get_new_property(builder, name);
00394                 
00395         MidgardSimpleConstraint *simple = midgard_simple_constraint_new(
00396                 builder->mgd, G_OBJECT_CLASS(g_type_class_peek(builder->type)),
00397                 new_name, op, value);
00398 
00399         /* We need to know "initial" klass when builder type is "overwritten"
00400          * with metadata klass or another one */
00401         /* FIXME */
00402         if(simple != NULL){
00403                 simple->ext_type = builder->ext_type;
00404                 simple->parent_type = builder->parent_type;
00405                 simple->initial_klass = tmpklass;
00406                 simple->types = builder->types;
00407         }               
00408         _mqb_type_pop(builder);
00409               
00410         if (simple != NULL) {
00411                 midgard_group_constraint_add_constraint(
00412                         MIDGARD_GROUP_CONSTRAINT(builder->stack->data),
00413                         MIDGARD_QUERY_CONSTRAINT(simple));
00414                 g_object_unref(G_OBJECT(simple));
00415                 return TRUE;
00416         } else {
00417                 g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
00418                         "Invalid query constraint specification");
00419                 /* FIXME, error reporting for QB needed.
00420                  * This is temporary set to avoid selecting all records from tables
00421                  * when comparison operator is invalid */
00422                 builder->error = 1;
00423                 return FALSE;
00424         }
00425 }
00426 
00427 gboolean midgard_query_builder_begin_group(
00428                 MidgardQueryBuilder *builder, const gchar *type) {
00429         g_assert(builder);
00430         g_assert(type);
00431         MidgardGroupConstraint *group = midgard_group_constraint_new(type);
00432         if (group != NULL) {
00433                 midgard_group_constraint_add_constraint(
00434                         MIDGARD_GROUP_CONSTRAINT(builder->stack->data),
00435                         MIDGARD_QUERY_CONSTRAINT(group));
00436                 builder->stack = g_slist_prepend(builder->stack, group);
00437                 return TRUE;
00438         } else {
00439                 g_warning("Invalid query group type: %s", type);
00440                 return FALSE;
00441         }
00442 }
00443 
00444 gboolean midgard_query_builder_end_group(MidgardQueryBuilder *builder) {
00445         g_assert(builder);
00446         if (g_slist_next(builder->stack) != NULL) {
00447                 g_object_unref(G_OBJECT(builder->stack->data));
00448                 builder->stack = g_slist_next(builder->stack);
00449                 return TRUE;
00450         } else {
00451                 g_warning("Invalid number of query group endings");
00452                 return FALSE;
00453         }
00454 }
00455 
00456 gboolean midgard_query_builder_add_order(
00457         MidgardQueryBuilder *builder, const gchar *name, const gchar *dir)
00458 {
00459         g_assert(builder);
00460         g_assert(name);
00461         g_assert(dir);
00462         
00463         gchar *new_name = _mqb_get_new_property(builder, name); 
00464         MidgardQueryOrder *order = midgard_query_order_new(
00465                 builder->mgd, G_OBJECT_CLASS(g_type_class_peek(builder->type)),
00466                 new_name, dir);
00467         _mqb_type_pop(builder);
00468         
00469         if (order) {
00470                 order->ext_type = builder->ext_type;
00471                 order->parent_type = builder->parent_type;
00472                 g_array_append_val(builder->orders, order);
00473                 return TRUE;
00474         } else {
00475                 g_warning("Skipping a ordering constraint specification");
00476                 return FALSE;
00477         }
00478 }
00479 
00480 void midgard_query_builder_set_limit(
00481         MidgardQueryBuilder *builder, guint limit)
00482 {
00483         g_assert(builder);
00484         builder->limit = limit;
00485 }
00486 
00487 void midgard_query_builder_set_offset(
00488         MidgardQueryBuilder *builder, guint offset)
00489 {
00490         g_assert(builder);
00491         builder->offset = offset;
00492 }
00493 
00494 void midgard_query_builder_set_lang(MidgardQueryBuilder *builder, gint lang)
00495 {
00496     g_assert(builder);
00497     builder->lang = lang;
00498 }
00499 
00500 void midgard_query_builder_unset_languages(MidgardQueryBuilder *builder)
00501 {
00502         g_assert(builder);
00503         builder->unset_lang = TRUE;
00504 }
00505 
00506 gint _compare_ml_guid(gconstpointer a, gconstpointer b){
00507 
00508         if(g_ascii_strcasecmp(a,b) == 0) return 0;
00509         return 1;
00510 }
00511 
00512 static GList *midgard_query_builder_execute_or_count(
00513         MidgardQueryBuilder *builder, guint *n_objects, guint select_type)
00514 {
00515         g_assert(builder);
00516         g_assert(n_objects);
00517 
00518         GList *tmp_list = NULL;
00519         
00520         GList *list = _mqb_set_object_from_query(builder, select_type);
00521         if(list == NULL){
00522                 *n_objects = 0;
00523                 return NULL; 
00524         }
00525 
00526         if (builder->schema->use_lang == 1) {
00527                 
00528                 gchar *guid = NULL;
00529                 GList *nlist = NULL;
00530                 
00531                 if(midgard_lang_get(builder->mgd) != 0)
00532                         list = g_list_reverse(list);
00533 
00534                 for(; list ; list = list->next){     
00535                        
00536                         /* Bit of hack, records could be returned with lang DESC */
00537                         
00538                         if(select_type == MQB_SELECT_OBJECT) {
00539                                 g_object_get(G_OBJECT(list->data), "guid", &guid, NULL);
00540                         } else if (select_type == MQB_SELECT_GUID) {
00541                                 guid = g_strdup((gchar *)list->data);
00542                         }
00543                         /* Changed to find_custom due to bug in GLib docs */
00544                         /* g_list_find never returns NULL */
00545                         /* We could use g_hash_table as well */
00546                         if((tmp_list = g_list_find_custom(tmp_list, 
00547                                                 guid, _compare_ml_guid))) {
00548                                 
00549                                 g_free((gchar *)tmp_list->data);  
00550                                 tmp_list->data = g_strdup(""); /* FIXME */
00551                                 
00552                                 if(select_type == MQB_SELECT_OBJECT) {
00553                                         g_object_unref(G_OBJECT(list->data));
00554                                 } else if (select_type == MQB_SELECT_GUID) {
00555                                         /* TODO check leak, none for now */
00556                                         /* g_free(list->data); */
00557                                 }
00558                                 
00559                         } else {
00560                                 nlist = g_list_append(nlist, list->data);                          
00561                         }       
00562                         tmp_list = g_list_prepend(tmp_list, (gpointer)guid);
00563                 }          
00564                 
00565                 /* Free unused guids */
00566                 for(; tmp_list ; tmp_list = tmp_list->next){
00567                         g_free((gchar *)tmp_list->data);
00568                 }
00569                 g_list_free(tmp_list);
00570                 g_list_free(list);
00571                 list = nlist;
00572         }    
00573         
00574         *n_objects = g_list_length(list);               
00575         return list;     
00576 }
00577 
00578 GObject **midgard_query_builder_execute(
00579         MidgardQueryBuilder *builder, guint *n_objects)
00580 {
00581         guint i = 0;
00582         MIDGARD_ERRNO_SET(builder->mgd, MGD_ERR_OK);
00583         GList *list = 
00584                 midgard_query_builder_execute_or_count(builder, n_objects, MQB_SELECT_OBJECT);
00585         if(list == NULL)
00586                 return NULL;
00587 
00588         MgdObject **objects = g_new(MgdObject *, *n_objects);
00589         
00590         for( ; list; list = list->next){
00591                 objects[i] = list->data;
00592                 i++;
00593         }       
00594         g_list_free(list);
00595         return (GObject **)objects;     
00596 }
00597 
00598 guint midgard_query_builder_count(MidgardQueryBuilder *builder)
00599 {
00600         g_assert(builder != NULL);
00601         guint n_objects = 0;
00602         MIDGARD_ERRNO_SET(builder->mgd, MGD_ERR_OK);
00603         GList *list = 
00604                 midgard_query_builder_execute_or_count(builder, &n_objects, MQB_SELECT_GUID);
00605 
00606         if(list != NULL){
00607                 
00608                 for( ; list; list = list->next){                
00609                         g_free((gchar *)list->data);                                     
00610                 }               
00611                 g_list_free(list);
00612         }
00613         return n_objects;
00614 }
00615 
00616 GList *midgard_query_builder_get_guid(
00617                 MidgardQueryBuilder *builder)
00618 {
00619         g_assert(builder != NULL);
00620         guint n_objects = 0;
00621 
00622         GList *list = 
00623                 midgard_query_builder_execute_or_count(builder, &n_objects, MQB_SELECT_GUID);
00624         
00625         return list;    
00626 }
00627 
00628 
00629 gchar *midgard_query_builder_get_object_select(
00630         MidgardQueryBuilder *builder, guint select_type){
00631 
00632         g_assert(builder != NULL);
00633         g_assert(builder->type);
00634 
00635         MidgardObjectClass *klass = (MidgardObjectClass*) g_type_class_peek(builder->type);
00636         const gchar *table = midgard_object_class_get_table(klass);
00637 
00638         if (midgard_object_class_get_table(klass) == NULL){
00639                 g_warning("Object '%s' has no table or storage defined!", 
00640                                 g_type_name(builder->type));
00641                 return NULL;
00642         }
00643                 
00644         GString *select = g_string_new("");
00645 
00646         /* We are getting only guids */
00647         if(select_type == MQB_SELECT_GUID){
00648                 
00649                 g_string_append_printf(select, "%s.guid", table);
00650                 return g_string_free(select, FALSE);
00651         }
00652         
00653         /* guid , sitegroup hardcoded ( inherited from MgdObjectClass )
00654          * metadata properties hardcoded ( defined in midgard_metadata )
00655          */ 
00656         /* TODO , set switch to disable metadata ( for midgard-infectant) */
00657         g_string_append_printf(select, 
00658                         "%s.guid, %s.sitegroup, "
00659                         "%s.metadata_creator, "
00660                         "NULLIF(%s.metadata_created,'0000-00-00 00:00:00') AS metadata_created, "
00661                         "%s.metadata_revisor, "
00662                         "NULLIF(%s.metadata_revised,'0000-00-00 00:00:00') AS metadata_revised, "
00663                         "%s.metadata_revision, "
00664                         "%s.metadata_locker, "
00665                         "NULLIF(%s.metadata_locked,'0000-00-00 00:00:00') AS metadata_locked, "
00666                         "%s.metadata_approver, "
00667                         "NULLIF(%s.metadata_approved,'0000-00-00 00:00:00') AS metadata_approved, "
00668                         "%s.metadata_authors, %s.metadata_owner, "
00669                         "NULLIF(%s.metadata_schedule_start,'0000-00-00 00:00:00') AS metadata_schedule_start, "
00670                         "NULLIF(%s.metadata_schedule_end,'0000-00-00 00:00:00') AS metadata_schedule_end, "
00671                         "%s.metadata_hidden, "
00672                         "%s.metadata_nav_noentry, %s.metadata_size, %s.metadata_published, "
00673                         "NULLIF(%s.metadata_exported,'0000-00-00 00:00:00') AS metadata_exported, "
00674                         "NULLIF(%s.metadata_imported,'0000-00-00 00:00:00') AS metadata_imported,"
00675                         "%s.metadata_deleted, %s.metadata_score, %s",
00676                         table, table, table, table, table, table, table, table, table,
00677                         table, table, table, table, table, table, table, table, table,
00678                         table, table, table, table, table,
00679                         klass->data->query->select_full); /* TODO, Set select which suits better */
00680         
00681         return g_string_free(select, FALSE);        
00682 }
00683 
00684 GList *_mqb_set_object_from_query(MidgardQueryBuilder *builder, guint select_type){
00685 
00686         g_assert(builder != NULL);
00687 
00688         GParamSpec *prop;
00689         MgdObject *object;
00690         GValue pval = {0, };
00691         MidgardObjectClass *klass = (MidgardObjectClass*) g_type_class_peek(builder->type);;
00692         guint ret_rows, ret_fields, i, j;
00693        
00694         gchar *sql = _midgard_core_qb_get_sql(
00695                         builder, select_type, 
00696                         midgard_query_builder_get_object_select(builder, select_type));
00697         
00698         g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "query=%s", sql);
00699 
00700         if(!sql) {
00701                 g_warning("Attempted to execute NULL query");
00702                 return NULL;
00703         }               
00704 
00705         gint sq = mysql_query(builder->mgd->msql->mysql, sql);
00706 
00707         if (sq != 0) {
00708                 g_warning("\n\nQUERY: \n %s \n\n FAILED: \n %s",
00709                                 sql,
00710                                 mysql_error(builder->mgd->msql->mysql));
00711                 midgard_set_error(builder->mgd->_mgd,
00712                                 MGD_GENERIC_ERROR,
00713                                 MGD_ERR_INTERNAL,
00714                                 " SQL query failed. ");
00715                 g_clear_error(&builder->mgd->_mgd->err);        
00716                 g_free(sql); 
00717                 return FALSE;
00718         }
00719         g_free(sql);
00720       
00721         /* We use MySQL API directly, no mgd_query and midgard_res usage */
00722         MYSQL_ROW row;
00723         MYSQL_FIELD *field;
00724         MYSQL_RES *results = mysql_store_result(builder->mgd->msql->mysql);
00725         if (!results)
00726                 return FALSE;
00727         
00728         if ((ret_rows = mysql_num_rows(results)) == 0) {
00729                 mysql_free_result(results);
00730                 return NULL;
00731         }
00732        
00733         /* records found , allocate as many objects as many returned rows */ 
00734         GList *list = NULL;
00735 
00736         /* We get guids only */
00737         if(select_type == MQB_SELECT_GUID) {
00738                 
00739                 for(i = 0; i < ret_rows; i++){
00740                         row = mysql_fetch_row(results);
00741                         ret_fields = mysql_num_fields(results);
00742                         for (j = 0; j < ret_fields; j++){
00743                                 list = g_list_append(list, g_strdup((gchar *) row[j]));
00744                         }                       
00745                 }
00746                 mysql_free_result(results);
00747                 return list;
00748         }
00749               
00750         /* Get every row */
00751         for(i = 0; i < ret_rows; i++){
00752                 
00753                 row = mysql_fetch_row(results);
00754                 object = midgard_object_new(builder->mgd, 
00755                                 g_type_name(builder->type), NULL);
00756                 ret_fields =  mysql_num_fields(results); 
00757                 
00758                 /* We set metadata properties directly , but w get 
00759                  * additional speed. g_object_set looses 10% of performance here */
00760                 g_free(object->metadata->private->creator);
00761                 object->metadata->private->creator = g_strdup((gchar *)row[2]);
00762                 g_free(object->metadata->private->created);
00763                 object->metadata->private->created = g_strdup((gchar *)row[3]);
00764                 g_free(object->metadata->private->revisor);
00765                 object->metadata->private->revisor = g_strdup((gchar *)row[4]);
00766                 g_free(object->metadata->private->revised);
00767                 object->metadata->private->revised = g_strdup((gchar *)row[5]);
00768                 object->metadata->private->revision = atoi(row[6]);
00769                 g_free(object->metadata->private->locker);
00770                 object->metadata->private->locker = g_strdup((gchar *)row[7]);
00771                 g_free(object->metadata->private->locked);
00772                 object->metadata->private->locked = g_strdup((gchar *)row[8]);
00773                 g_free(object->metadata->private->approver);
00774                 object->metadata->private->approver = g_strdup((gchar *)row[9]);
00775                 g_free(object->metadata->private->approved);
00776                 object->metadata->private->approved = g_strdup((gchar *)row[10]);
00777                 g_free(object->metadata->private->authors);
00778                 object->metadata->private->authors = g_strdup((gchar *)row[11]);
00779                 g_free(object->metadata->private->owner);
00780                 object->metadata->private->owner = g_strdup((gchar *)row[12]);
00781                 g_free(object->metadata->private->schedule_start);
00782                 object->metadata->private->schedule_start = g_strdup((gchar *)row[13]);
00783                 g_free(object->metadata->private->schedule_end);
00784                 object->metadata->private->schedule_end = g_strdup((gchar *)row[14]);
00785                 object->metadata->private->hidden = atoi(row[15]);
00786                 object->metadata->private->nav_noentry = atoi(row[16]);
00787                 object->metadata->private->size = atoi(row[17]);
00788                 g_free(object->metadata->private->published);
00789                 object->metadata->private->published = g_strdup((gchar *)row[18]);
00790                 g_free(object->metadata->private->exported);
00791                 object->metadata->private->exported = g_strdup((gchar *)row[19]);
00792                 g_free(object->metadata->private->imported);
00793                 object->metadata->private->imported = g_strdup((gchar *)row[20]);
00794                 object->metadata->private->deleted = atoi(row[21]);
00795                 object->metadata->private->score = atoi(row[22]);
00796                
00797                 /* Set core's object private data */
00798                 object->private->exported = g_strdup((gchar *)row[19]);
00799                 object->private->imported = g_strdup((gchar *)row[20]);
00800 
00801                 /* Get all fields */
00802                 /* We can set guid and sitegroup as row[0] and row[1]
00803                  * and start loop from 16 to increase speed. 
00804                  * All in all metadata won't be changed often */
00805                 for (j = 0; j < ret_fields; j++){
00806                        
00807                         if(j < 22 && j > 2)
00808                                 continue;
00809                         field = mysql_fetch_field_direct(results, j);
00810                                               
00811                         if ((prop = g_object_class_find_property(
00812                                                         (GObjectClass *)klass, field->name)) != NULL) {
00813 
00814                                 g_value_init(&pval,prop->value_type);
00815 
00816                                 switch(prop->value_type){
00817 
00818                                         case G_TYPE_STRING:
00819                                                 g_value_set_string(&pval, (gchar *)row[j]);
00820                                                 break; 
00821 
00822                                         case G_TYPE_UINT:
00823                                                 g_value_set_uint(&pval, atoi(row[j]));
00824                                                 break;
00825 
00826                                         case G_TYPE_INT:
00827                                                 g_value_set_int(&pval, atoi(row[j]));
00828                                                 break;
00829 
00830                                         case G_TYPE_FLOAT:
00831                                                 g_value_set_float(&pval, g_ascii_strtod(row[j], NULL));
00832                                                 break;
00833                                         
00834                                         case G_TYPE_BOOLEAN:
00835                                                 g_value_set_boolean(&pval, atoi(row[j]));
00836                                                 break;                                     
00837                                                 
00838                                 }
00839                                 g_object_set_property(G_OBJECT(object), field->name, &pval);
00840                                 g_value_unset(&pval);                                
00841                         }                                                        
00842                 }
00843                 list = g_list_append(list, G_OBJECT(object));                
00844         }
00845         mysql_free_result(results);
00846         return list;
00847 }
00848 
00849 gboolean midgard_query_builder_join(
00850                 MidgardQueryBuilder *builder, const gchar *prop, 
00851                 const gchar *jobject, const gchar *jprop)
00852 {
00853 
00854         g_assert(prop != NULL);
00855         g_assert(jobject != NULL);
00856         g_assert(jprop != NULL);
00857 
00858         /* Find property prop in builder types' class */
00859         GParamSpec *pspec = g_object_class_find_property(
00860                         G_OBJECT_CLASS(g_type_class_peek(builder->type)), prop);
00861         if(pspec == NULL) {
00862                 g_warning("Invalid property '%s' for '%s'!",
00863                                 prop, g_type_name(builder->type));
00864                 return FALSE;
00865         }
00866 
00867         /* Initialize new instance for joined object typename */
00868         MgdObject *obj = midgard_object_new(builder->mgd, jobject, NULL);
00869         if(obj == NULL) {
00870                 g_warning("Type %s in not registered in application schema",
00871                                 jobject);
00872                 return FALSE;                                           
00873         }
00874         
00875         /* Find property jprop in jobject's class */
00876         GParamSpec *jspec = g_object_class_find_property(
00877                         G_OBJECT_GET_CLASS(G_OBJECT(obj)), jprop);
00878         if(jspec == NULL) {
00879                 g_warning("Invalid property '%s' for joined type '%s'!",
00880                                 jprop, G_OBJECT_TYPE_NAME(G_OBJECT(obj)));
00881                 return FALSE;
00882         }
00883 
00884         /* Create "join" part which should be appended to QB's final query */
00885         builder->link = g_strdup(g_param_spec_get_nick(jspec));
00886         g_string_append_printf(builder->join,
00887                         " AND %s = %s",
00888                         g_param_spec_get_nick(pspec),
00889                         builder->link);
00890         
00891         /* Get MidgardObjectClass pointer */
00892         MidgardObjectClass *klass = MIDGARD_OBJECT_GET_CLASS(obj);
00893         /* Get table name used by class */
00894         const gchar *table = midgard_object_class_get_table(klass);
00895         
00896         /* Do not append table name which is already defined in builder->tables */
00897         gchar **duptables = g_strsplit(builder->tables, ",", 0);
00898         guint i = 0, j = 0;
00899         for(i = 0 ; duptables[i]; i++){
00900                 if (g_ascii_strcasecmp(duptables[i],
00901                                         table) == 0 )
00902                         j++;
00903         }
00904 
00905         /* We have new table string so we need to concat all tables */
00906         if (j == 0) {
00907                 gchar *tmptable  = g_strconcat(builder->tables,
00908                                 ",", table, NULL);
00909                 g_free(builder->tables);
00910                 builder->tables = g_strdup(tmptable);
00911                 g_free(tmptable);
00912         }
00913         g_strfreev(duptables);
00914         g_free(obj);
00915 
00916         return TRUE;                                                                    
00917 }
00918 
00919  
00920 /* Returns type name of the type which is currently used by Query Builder. */
00921 const gchar *midgard_query_builder_get_type_name(
00922                 MidgardQueryBuilder *builder)
00923 {
00924         g_assert(builder != NULL);
00925         return g_type_name(builder->type);
00926 }
00927 
00928 void midgard_query_builder_include_deleted(MidgardQueryBuilder *builder)
00929 {
00930         g_assert(builder);
00931 
00932         builder->include_deleted = TRUE;
00933 }
00934 
00935 /* GOBJECT ROUTINES*/
00936 
00937 static void _midgard_query_builder_finalize(GObject *object)
00938 {
00939         midgard_query_builder_free(
00940                         (MidgardQueryBuilder *)object);
00941 }
00942 
00943 static void _midgard_query_builder_class_init(
00944                 gpointer g_class, gpointer g_class_data)
00945 {
00946         GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
00947         MidgardQueryBuilderClass *klass = MIDGARD_QUERY_BUILDER_CLASS (g_class);
00948         
00949         gobject_class->finalize = _midgard_query_builder_finalize;
00950         klass->set_lang = midgard_query_builder_set_lang;
00951         klass->unset_languages = midgard_query_builder_unset_languages;
00952         klass->add_constraint = midgard_query_builder_add_constraint;
00953         klass->begin_group = midgard_query_builder_begin_group;
00954         klass->end_group = midgard_query_builder_end_group;
00955         klass->add_order = midgard_query_builder_add_order;
00956         klass->set_limit = midgard_query_builder_set_limit;
00957         klass->execute = midgard_query_builder_execute;
00958         klass->count = midgard_query_builder_count;
00959 }
00960 
00961 static void _midgard_query_builder_instance_init(
00962                 GTypeInstance *instance, gpointer g_class)
00963 {
00964 
00965 }
00966 
00967 /* Registers the type as  a fundamental GType unless already registered. */
00968 GType midgard_query_builder_get_type(void)
00969 {
00970         static GType type = 0;
00971         if (type == 0) {
00972                 static const GTypeInfo info = {
00973                         sizeof (MidgardQueryBuilderClass),
00974                         NULL,           /* base_init */
00975                         NULL,           /* base_finalize */
00976                         (GClassInitFunc) _midgard_query_builder_class_init,
00977                         NULL,           /* class_finalize */
00978                         NULL,           /* class_data */
00979                         sizeof (MidgardQueryBuilder),
00980                         0,              /* n_preallocs */
00981                         (GInstanceInitFunc) _midgard_query_builder_instance_init /* instance_init */
00982                 };
00983                 type = g_type_register_static (G_TYPE_OBJECT,
00984                                 "midgard_query_builder",
00985                                 &info, 0);
00986         }
00987         return type;
00988 }
00989 

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