src/tree_core.c

00001 /* $Id: tree_core.c,v 1.28 2006/09/13 11:36:51 piotras Exp $
00002  *
00003  * midgard-lib: database access for Midgard clients
00004  *
00005  * Copyright (C) 1999 Jukka Zitting <jukka.zitting@iki.fi>
00006  * Copyright (C) 2000 The Midgard Project ry
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Library General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Library General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this library; see the file COPYING.  If not, write to
00020  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00021  * Boston, MA 02111-1307, USA.
00022  */
00023 
00030 #include <config.h>
00031 //#include "midgard/types.h"
00032 #include "midgard/midgard_legacy.h"
00033 #include "defaults.h"
00034 #if HAVE_CRYPT_H
00035 #include <crypt.h>
00036 #else
00037 #define _XOPEN_SOURCE
00038 #ifndef WIN32
00039 #include <unistd.h>
00040 #endif
00041 #endif
00042 #include <errno.h>
00043 #ifndef WIN32
00044 #include <unistd.h>
00045 #endif
00046 
00047 #include "midgard/midgard_object.h"
00048 #include "schema.h"
00049 
00051 const tree_node zero_node = {
00052         0,
00053         0,
00054         0,
00055         0,
00056         NULL,
00057         NULL
00058 }; 
00059 
00061 #define ALLOC_TNODE(pool, node) {\
00062         (node) = (tree_node*)mgd_alloc(pool, sizeof(tree_node));\
00063         *(node) = zero_node;\
00064 }
00065 
00082 static int walktree(midgard * mgd, tree_node * tree, int order, int level,
00083                     int maxlevel, void *xparam, midgard_userfunc func)
00084 {
00085         int branch_ok = 1;
00086         tree_node *tmp;
00087         if(maxlevel && level > maxlevel) return 1;
00088         if (order && func)
00089                 if(!(branch_ok = func(mgd, tree->id, level, xparam)))
00090                         return 0;
00091         if(branch_ok > 0) {
00092                 tmp = tree->child;
00093                 while(tmp) {
00094                         if(!walktree(mgd, tmp, order, level + 1, maxlevel, xparam, func))
00095                                 return 0;
00096                         tmp = tmp->next;
00097                 }
00098         }
00099         if ((!order) && func)
00100                 if(!func(mgd, tree->id, level, xparam))
00101                         return 0;
00102         return 1;
00103 }
00104 
00123 int mgd_tree_get_level(midgard *mgd, midgard_pool * pool, const char *table,
00124                                                 const char * upfield, const char * sort,
00125                                                 int ** ids, tree_node ** nodes)
00126 {
00127         midgard_res *res;
00128         int i,n;
00129 
00130         if(!ids) return 0;
00131         if(!upfield || !(*upfield)) upfield = "up";
00132         if((*ids)[0] && (*ids)[1]) {
00133                 res = mgd_query(mgd,
00134                         "SELECT id,$s FROM $s WHERE $s IN $D AND (sitegroup in (0, $d) OR $d<>0) ORDER BY $s,$s",
00135                         upfield, table,
00136                         upfield, *ids, mgd_sitegroup(mgd), mgd_isroot(mgd),
00137                         upfield, sort ? sort : "id");
00138         } else { /* root query (single id) */
00139                 res = mgd_query(mgd,
00140                         "SELECT id,$s FROM $s WHERE $s=$d AND (sitegroup in (0, $d) OR $d<>0) ORDER BY $s,$s",
00141                         upfield, table,
00142                         upfield, (*ids)[0], mgd_sitegroup(mgd), mgd_isroot(mgd),
00143                         upfield, sort ? sort : "id");
00144         }
00145         if(!res || !(n = mgd_rows(res))) {
00146                 nodes = NULL;
00147                 ids = NULL;
00148                 return 0;
00149         }
00150         *nodes = (tree_node *) mgd_alloc(pool, n * sizeof (tree_node));
00151         *ids = (int *) mgd_alloc(pool, (n + 1) * sizeof(int *));
00152         for(i = 0; mgd_fetch(res); i++) {
00153                 (*ids)[i] = mgd_sql2int(res, 0);
00154                 (*nodes)[i].id = (*ids)[i];
00155                 (*nodes)[i].up = mgd_sql2int(res, 1);
00156                 (*nodes)[i].level = 0;
00157                 (*nodes)[i].size = 0;
00158                 (*nodes)[i].child = NULL;
00159                 (*nodes)[i].next = NULL;
00160                 // if the current node and the previous node are from the same parent,
00161                 // they are siblings so we update the next of the previous node
00162                 if(i && (*nodes)[i].up == (*nodes)[i-1].up) {
00163                         (*nodes)[i-1].next = &((*nodes)[i]);
00164                 }
00165         }
00166         (*ids)[i] = 0;
00167         mgd_release(res);
00168         return n;
00169 }
00170 
00183 void mgd_tree_get_ids(tree_node * tree, int *ids, int *index) {
00184         tree_node * tmp;
00185 
00186         if(!tree) return;
00187         ids[*index] = tree->id;
00188         (*index)++;
00189         tmp = tree->child;
00190         while(tmp) {
00191                 mgd_tree_get_ids(tmp, ids, index);
00192                 tmp = tmp->next;
00193         }
00194 }
00195 
00203 int mgd_tree_init(tree_node * tree, int level) {
00204         tree_node * tmp;
00205 
00206         if(!tree) return 0;
00207         tree->size = 1;
00208         tree->level = level;
00209         tmp = tree->child;
00210         while(tmp) {
00211                 tree->size += mgd_tree_init(tmp, level + 1);
00212                 tmp = tmp->next;
00213         }
00214         
00215         return tree->size; /* store the size of the subtree + the current node */
00216 }
00217 
00230 tree_node * mgd_tree_build(midgard * mgd, midgard_pool * pool,
00231                                 const char * table, const char * upfield, int root,
00232                                 int maxlevel, const char * sort)
00233 {
00234         int root_ids[2] = {root, 0};
00235         int *child_ids = root_ids;
00236         tree_node *root_node, *parent_nodes, *child_nodes;
00237         int parent_num, child_num, i, j, k;
00238 
00239         if(sort && (!strncmp(sort, "id", 2) || !strncmp(sort, "up", 2)
00240                                                 || !strncmp(sort, "sitegroup", 9))) // who knows ?? ;)
00241                 sort = NULL;
00242         ALLOC_TNODE(pool, root_node);
00243         root_node->id = root;
00244         parent_num = 1;
00245         parent_nodes = root_node;
00246         for(i = 0; !maxlevel || (i < maxlevel); i++) {
00247                 child_num = mgd_tree_get_level(mgd, pool, table, upfield, sort,
00248                                                                         &child_ids, &child_nodes);
00249                 if(!child_num) break;
00250                 // if more parents than children, loop thru parents once
00251                 if(parent_num > child_num) {
00252                         for(j = 0; j < parent_num; j++) {
00253                                 for(k = 0; k < child_num; k++) {
00254                                         // zap child until we find the next one with parent
00255                                         if(parent_nodes[j].id != child_nodes[k].up) continue;
00256                                         parent_nodes[j].child = child_nodes + k;
00257                                         break;
00258                                 }
00259                         }
00260                 } else { // more children than parents, loop thru children once
00261                         for(k = 0; k < child_num; k++) {
00262                                 for(j = 0; j < parent_num; j++) {
00263                                         // zap parent until we find the next one with children
00264                                         if(parent_nodes[j].id != child_nodes[k].up) continue;
00265                                         parent_nodes[j].child = child_nodes + k;
00266                                         // found it, zap siblings
00267                                         while(child_nodes[k].next) k++;
00268                                 }
00269                         }
00270                 }
00271                 parent_nodes = child_nodes;
00272                 parent_num = child_num;
00273         }
00274         mgd_tree_init(root_node, 0);
00275         return root_node;
00276 }
00277 
00295 MGD_API int mgd_walk_table_tree(midgard * mgd, const char *table, const char *upfield,
00296                         int root, int maxlevel, int order, void *xparam,
00297                          midgard_userfunc func, const char * sort)
00298 {
00299         midgard_pool *pool;
00300         tree_node *tree;
00301         int ret;
00302 
00303         pool = mgd_alloc_pool();
00304 
00305         tree = mgd_tree_build(mgd, pool, table, upfield, root, maxlevel, sort);
00306 
00307         if (!tree) {
00308                 return 0;
00309         }
00310         /* Be aware of top level trees */
00311         ret = walktree(mgd, tree, order, 0, maxlevel, xparam, func);
00312         mgd_free_pool(pool);
00313         return ret;
00314 }
00315 
00328 MGD_API int *mgd_tree(midgard * mgd, const char *table, const char *upfield,
00329                                 int id, int maxlevel, const char *sort)
00330 {
00331         int *ids, i = 0;
00332         tree_node * tree;
00333         midgard_pool * pool;
00334 
00335         pool = mgd_alloc_pool();
00336         tree = mgd_tree_build(mgd, pool, table, upfield, id, maxlevel, sort);
00337         if(!tree) return NULL;
00338 
00339         ids = (int *)malloc((tree->size + 1) * sizeof(int));
00340         
00341         mgd_tree_get_ids(tree, ids, &i);
00342         ids[i] = 0;
00343 
00344         mgd_free_pool(pool);
00345         return ids;
00346 }
00347 
00358 MGD_API int mgd_is_in_tree(midgard * mgd, const char * table,
00359                                 const char * upfield, int root, int id)
00360 {
00361         midgard_res * res;
00362         midgard_pool * pool;
00363         int level=0;
00364 
00365         pool = mgd_alloc_pool();
00366 
00367         if(!table) return 0;
00368         if(!upfield || !(*upfield)) upfield = "up";
00369         while(root != id) {
00370                 res = mgd_query(mgd,
00371                         "SELECT $s FROM $s WHERE id=$d AND (sitegroup in (0, $d) OR $d<>0)",
00372                         upfield, table, id, mgd_sitegroup(mgd), mgd_isroot(mgd));
00373                 if(!res || !mgd_rows(res)) {
00374                         mgd_free_pool(pool);
00375                         return 0;
00376                 }
00377                 mgd_fetch(res);
00378                 id = mgd_sql2int(res, 0);
00379                 level++;
00380                 mgd_release(res);
00381         }
00382         mgd_free_pool(pool);
00383         if(root == id) return level+1;
00384         return 0;
00385 }
00386 
00387 
00388 MGD_API gchar *midgard_object_build_path(MgdObject *mobj)
00389 {
00390   g_assert(mobj != NULL);       
00391   midgard_res * res;
00392   midgard_pool * pool;
00393   gchar  *path = NULL, *pename;
00394   gint  id;
00395   const gchar *parent_table, *upfield;
00396     
00397   pool = mgd_alloc_pool();
00398   if (!pool)
00399     return NULL;
00400 
00401   MgdObject *parent = midgard_object_new(mobj->mgd, 
00402                   midgard_object_parent(mobj), NULL);
00403 
00404   MidgardObjectClass *parent_class = MIDGARD_OBJECT_GET_CLASS(parent);
00405   MidgardObjectClass *klass = MIDGARD_OBJECT_GET_CLASS(mobj);
00406     
00407   parent_table = midgard_object_class_get_table(parent_class);
00408   upfield = parent_class->data->query->upfield;
00409 
00410   g_object_unref(parent);
00411 
00412   if ((parent_table == NULL) || (upfield == NULL))
00413     return NULL;
00414 
00415   g_log("midgard-lib", G_LOG_LEVEL_DEBUG, "Walk the tree using table '%s' and  upfield: '%s'", parent_table, upfield);  
00416   
00417   g_object_get((GObject *) mobj, klass->data->query->upfield, &id, NULL);
00418   
00419   do {
00420     res = mgd_query(mobj->mgd,
00421         "SELECT $s,name FROM $s WHERE id=$d AND (sitegroup in (0, $d) OR $d<>0)",
00422         upfield, parent_table, id, mgd_sitegroup(mobj->mgd), mgd_isroot(mobj->mgd));
00423     
00424     if(!res || !mgd_rows(res)) {
00425       mgd_free_pool(pool);
00426       return NULL;
00427     } 
00428 
00429     mgd_fetch(res);
00430     id = mgd_sql2int(res, 0);
00431     pename = (gchar *)mgd_colvalue(res, 1);
00432     pename = g_strjoin("/", "", pename, NULL);
00433     path = g_strjoin("", pename, path, NULL);
00434     mgd_release(res);
00435   } while (id != 0);
00436 
00437   mgd_free_pool(pool);
00438   g_log("midgard-lib", G_LOG_LEVEL_DEBUG, "PATH %s", path);
00439   return path;
00440 }
00441 
00442 /* Walk parent's type tree to check if our type is in that tree */
00443 
00444 gboolean midgard_object_is_in_parent_tree(MgdObject *mobj, guint rootid, guint id)
00445 {
00446   g_assert(mobj != NULL);       
00447   
00448   gint ri;
00449   const gchar *parent_table, *parent_up;
00450   
00451   MgdObject *parent = midgard_object_new(mobj->mgd,
00452                   midgard_object_parent(mobj), NULL);
00453   
00454   MidgardObjectClass *parent_class = MIDGARD_OBJECT_GET_CLASS(parent);
00455   
00456   parent_table = midgard_object_class_get_table(parent_class);
00457   parent_up = parent_class->data->query->upfield;
00458   
00459   g_object_unref(parent);
00460         
00461   if (!parent_table)
00462     return FALSE;
00463 
00464   if (!parent_up)
00465     return FALSE;
00466 
00467   /* TODO , replace with repligard's table check */
00468   if(!mgd_exists_id(mobj->mgd,  parent_table, "id=$d", id))
00469     return FALSE;
00470     
00471   if((ri = mgd_is_in_tree(mobj->mgd, parent_table,
00472           parent_up,
00473           rootid, id)))
00474     return TRUE;
00475 
00476   return FALSE;
00477 }
00478 
00479 
00480 /* Walk tree of the same type to check if type is in this tree */
00481   
00482 gboolean midgard_object_is_in_tree(MgdObject *mobj, guint rootid, guint id)
00483 {
00484   g_assert(mobj != NULL);
00485   
00486   gint ri;
00487   const gchar *table, *upfield;  
00488   MidgardObjectClass *klass = MIDGARD_OBJECT_GET_CLASS(mobj);
00489   
00490   table = midgard_object_class_get_table(klass);
00491   upfield = klass->data->query->upfield;
00492   
00493   if (!table)
00494     return FALSE;
00495 
00496   if (!upfield)
00497     return FALSE;
00498 
00499   /* TODO , replace with repligard's table check */
00500   if(!mgd_exists_id(mobj->mgd,  table, "id=$d", id))
00501     return FALSE;
00502     
00503   if((ri = mgd_is_in_tree(mobj->mgd, table,
00504           upfield,
00505           rootid, id)))
00506     return TRUE;
00507 
00508   return FALSE;
00509 }
00510 
00511 
00512 /* Walk object's tree ( down ) and collect primary fields of all objects of the same type*/
00513 gchar *midgard_object_get_tree(MgdObject *object, GSList *tnodes)
00514 {
00515     GValue pval = {0,};
00516     guint i;
00517     midgard_res *res = NULL;
00518     gchar *snodes = "";
00519     GParamSpec *prop;
00520 
00521     MidgardObjectClass *klass = MIDGARD_OBJECT_GET_CLASS(object);
00522     const gchar *table = midgard_object_class_get_table(klass);
00523     prop = g_object_class_find_property((GObjectClass *) object->klass, 
00524             (gchar *)object->data->query->primary);
00525     
00526     g_value_init(&pval,prop->value_type);
00527 
00528     g_object_get_property(G_OBJECT(object), g_strdup((gchar *)klass->data->primary), &pval);
00529     
00530     switch(prop->value_type){
00531 
00532         case G_TYPE_STRING:
00533             
00534             res = mgd_query(object->mgd,
00535                     "SELECT $s FROM $s WHERE $s='$s' AND (sitegroup in (0, $d) OR $d<>0)",
00536                     klass->data->primary, table, 
00537                     klass->data->query->upfield, g_value_get_string(&pval),  
00538                     mgd_sitegroup(object->mgd), mgd_isroot(object->mgd));
00539             break;
00540 
00541         case G_TYPE_UINT:
00542 
00543             res = mgd_query(object->mgd,
00544                     "SELECT $s FROM $s WHERE $s=$d AND (sitegroup in (0, $d) OR $d<>0)",
00545                     klass->data->primary, table,
00546                     klass->data->query->upfield, g_value_get_uint(&pval),
00547                     mgd_sitegroup(object->mgd), mgd_isroot(object->mgd));
00548             break;                             
00549     }
00550     
00551     g_value_unset(&pval);
00552 
00553     if(!res || !mgd_rows(res)) {
00554         if(res) mgd_release(res);
00555         return NULL;
00556     }
00557 
00558     mgd_fetch(res);
00559 
00560     for (i = 0; i < mgd_cols(res); i++) {
00561 
00562         switch(prop->value_type) {
00563 
00564             case G_TYPE_STRING:
00565                 
00566                 tnodes = g_slist_append(tnodes, g_strdup(mgd_colvalue(res, i)));
00567                 g_object_set(G_OBJECT(object), 
00568                         g_strdup((gchar *)klass->data->primary), (gchar *)mgd_colvalue(res, i), NULL);
00569                 break;
00570                 
00571             case G_TYPE_UINT:
00572 
00573                 tnodes = g_slist_append(tnodes, GINT_TO_POINTER (mgd_colvalue(res, i)));
00574                 g_object_set(G_OBJECT(object), 
00575                         g_strdup((gchar *)klass->data->primary), atoi(mgd_colvalue(res, i)), NULL);
00576                 break;
00577 
00578         }
00579         
00580         midgard_object_get_tree(object, tnodes);
00581     }
00582 
00583     return snodes;
00584 }
00585 

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