src/simple_constraint.c

00001 /* 
00002  * Copyright (C) 2005 Jukka Zitting <jz@yukatan.fi>
00003  * Copyright (C) 2005 Piotr Pokora <piotr.pokora@nemein.net>
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 "simple_constraint.h"
00022 #include "midgard/midgard_object.h"
00023 #include "midgard/midgard_metadata.h"
00024 #include "midgard_mysql.h"
00025 #include <string.h>
00026 #include <stdlib.h>
00027 
00028 static const char *operator[] = {
00029         "=", ">", "<", "<>", "<=", ">=", "LIKE", "NOT LIKE", "IN", "NOT IN", "INTREE",
00030         NULL
00031 };
00032 
00033 static void add_sql_value(
00034                 MidgardSimpleConstraint *constraint, GValue *value, GString *sql) {
00035         g_assert(constraint != NULL);
00036         g_assert(value != NULL);
00037         g_assert(sql != NULL);
00038 
00039         GValue *target = g_new0(GValue, 1);
00040         g_value_init(target, G_PARAM_SPEC_VALUE_TYPE(constraint->spec));
00041 
00042         /* Convert the value to the correct type. */
00043         if (!g_param_value_convert((GParamSpec *)constraint->spec, value, target, FALSE)) {
00044                 /* The default conversions failed, try custom conversions */
00045                 if (G_VALUE_HOLDS_STRING(value) && G_VALUE_HOLDS_UINT(target)) {
00046                         guint uvalue = strtoul(g_value_get_string(value), NULL, 0);
00047                         g_value_set_uint(target, uvalue);
00048                 } else {
00049                         g_warning(
00050                                         "Invalid value conversion from %s to %s",
00051                                         G_VALUE_TYPE_NAME(value),
00052                                         G_VALUE_TYPE_NAME(target));
00053                 }
00054         }
00055 
00056         /* Format the value for the SQL statement */
00057         if (G_VALUE_HOLDS_STRING(target)) {
00058                 const gchar *strval = g_value_get_string(target);
00059                 if(strval == NULL)
00060                         strval = "";
00061                 guint length = strlen(strval);
00062                 gchar *escaped = g_new(gchar, 2 * length + 1);
00063                 /* TODO: Use libgda value escaping */
00064                 mysql_real_escape_string(
00065                         constraint->mgd->msql->mysql, escaped, strval, length);
00066                 g_string_append_printf(sql, "'%s'", escaped);
00067                 g_free(escaped);
00068         } else if (G_VALUE_HOLDS_BOOLEAN(target)) {
00069                 if (g_value_get_boolean(target)) {
00070                         g_string_append(sql, "TRUE");
00071                 } else {
00072                         g_string_append(sql, "FALSE");
00073                 }
00074         } else if (G_VALUE_HOLDS_UINT(target)) {
00075                 g_string_append_printf(sql, "%u", g_value_get_uint(target));
00076         } else if (G_VALUE_HOLDS_INT(target)) {
00077                 g_string_append_printf(sql, "%d", g_value_get_int(target));
00078         } else if (G_VALUE_HOLDS_FLOAT(target)) {
00079                 g_string_append_printf(sql, "%g", g_value_get_float(target));
00080         } else {
00081                 g_warning(
00082                         "Unexpected constraint value type %s",
00083                         G_VALUE_TYPE_NAME(target));
00084                 g_string_append(sql, "NULL");
00085         }
00086 
00087         g_value_unset(target);
00088         g_free(target);
00089 }
00090 
00091 static void add_sql(MidgardQueryConstraint *constraint, GString *sql) {
00092         g_assert(constraint);
00093         g_assert(sql);
00094 
00095         MidgardSimpleConstraint *simple = MIDGARD_SIMPLE_CONSTRAINT(constraint);
00096         guint i;
00097 
00098         const gchar *table = midgard_object_class_get_table(
00099                         (MidgardObjectClass *)simple->initial_klass);
00100 
00101         /* Dereference a parametrized value pointer */
00102         GValue *value = simple->value;
00103         if (G_VALUE_HOLDS_POINTER(value)
00104                         && G_IS_VALUE(g_value_get_pointer(value))) {
00105                 value = (GValue *) g_value_get_pointer(value);
00106         }       
00107         
00108         /* FIXME, MGD_TYPE_SITEGROUP and MGD_TYPE_GUID should be implemented */
00109         /* Get table.sitegroup */
00110         if(g_ascii_strcasecmp(simple->spec->name, "sitegroup") == 0){
00111                 gchar *sgfield = g_strconcat(
00112                                 midgard_object_class_get_table((MidgardObjectClass *)simple->klass), 
00113                                 ".sitegroup",
00114                                 NULL);
00115                 g_string_append(sql, sgfield);
00116                 g_free(sgfield);
00117         /* Get table.guid */    
00118         } else if (g_ascii_strcasecmp(simple->spec->name, "guid") == 0){
00119                 gchar *sgfield = g_strconcat(
00120                                 midgard_object_class_get_table((MidgardObjectClass *)simple->klass),
00121                                 ".guid",
00122                                 NULL);
00123                 g_string_append(sql, sgfield);
00124                 g_free(sgfield);  
00125         } else if (simple->ext_type == MIDGARD_TYPE_METADATA) { 
00126                 MidgardObjectClass *pklass = 
00127                         (MidgardObjectClass*) g_type_class_peek(simple->parent_type);
00128                 if(pklass)
00129                         table = midgard_object_class_get_table(pklass);
00130                 g_string_append_printf(sql, 
00131                                 "%s.%s",
00132                                 table,
00133                                 g_param_spec_get_nick((GParamSpec *) simple->spec));                            
00134         } else {
00135                 g_string_append(sql, g_param_spec_get_nick((GParamSpec *) simple->spec));
00136         }
00137 
00138         g_string_append_c(sql, ' ');
00139         if(g_str_equal(simple->op, "INTREE")){
00140                 if(!G_VALUE_HOLDS(value, G_TYPE_UINT)){
00141                         g_warning(" Only integers values supported by INTREE operator");
00142                         return;
00143                 }
00144                 /* This comparison is done to avoid ugly endless loops for 
00145                  * properties which are not parent or up ones 
00146                  * This should be changed when midgard_tree is implemented */
00147                 const gchar *parent_field, *up_field = NULL;
00148                 guint j = 0;
00149                 parent_field = 
00150                         midgard_object_class_get_parent_property(
00151                                         (MidgardObjectClass *)simple->klass);
00152                 if(parent_field == NULL)
00153                         parent_field = "";
00154                 up_field =
00155                                 midgard_object_class_get_up_property(
00156                                                 (MidgardObjectClass *)simple->klass);   
00157                 if(up_field == NULL)
00158                         up_field = "";
00159                 if(!g_str_equal(parent_field, simple->spec->name))
00160                         j++;
00161                 if(!g_str_equal(up_field, simple->spec->name)) 
00162                         j++;
00163                 if(j > 1){
00164                         g_warning("Constraint's property is neither parent nor up property");
00165                         return;
00166                 }               
00167                 g_string_append(sql, "IN"); 
00168         } else {
00169                 g_string_append(sql, simple->op);
00170         }
00171         g_string_append_c(sql, ' ');
00172 
00173         if (g_str_equal(simple->op, "IN") || g_str_equal(simple->op, "NOT IN")) {
00174                 g_string_append_c(sql, '(');
00175                 if (G_VALUE_HOLDS(value, G_TYPE_VALUE_ARRAY)) {
00176                         GValueArray *array = (GValueArray *) g_value_get_boxed(value);
00177                         for (i = 0; i < array->n_values; i++) {
00178                                 if (i > 0) {
00179                                         g_string_append_c(sql, ',');
00180                                 }
00181                                 add_sql_value(simple, g_value_array_get_nth(array, i), sql);
00182                         }
00183                 } else {
00184                         add_sql_value(simple, value, sql);
00185                 }
00186                 g_string_append_c(sql, ')');
00187                 
00188                 /* FIXME , rewrite it when midgard_tree class or something similiar 
00189                  * is implemented */
00190         } else if(g_str_equal(simple->op, "INTREE")) {
00191                 table = midgard_object_class_get_table(
00192                                 (MidgardObjectClass *)simple->klass);
00193                 gint *prnts =
00194                         mgd_tree((midgard *)simple->mgd, table, 
00195                                         simple->spec->name, 
00196                                         g_value_get_uint(value), 
00197                                         0, NULL);
00198                 if(prnts) {
00199                         g_string_append_c(sql, '(');
00200                         i = 0;
00201                         do {
00202                                 if(i > 0)
00203                                         g_string_append(sql,",");
00204                                                         
00205                                 g_string_append_printf(sql,
00206                                                 "%d", prnts[i]);
00207                                 i++;
00208                                 
00209                         } while (prnts[i]);
00210                         g_string_append_c(sql, ')');
00211                 }
00212                                         
00213         } else {
00214                 add_sql_value(simple, value, sql);
00215         }
00216 }
00217 
00218 G_DEFINE_TYPE(MidgardSimpleConstraint, midgard_simple_constraint, MIDGARD_TYPE_QUERY_CONSTRAINT)
00219 
00220 static void midgard_simple_constraint_init(MidgardSimpleConstraint *self) {
00221         g_assert(self);
00222 }
00223 
00224 static void midgard_simple_constraint_dispose(MidgardSimpleConstraint *simple) {
00225         g_assert(simple);
00226 
00227         g_value_unset(simple->value);
00228         g_free(simple->value);
00229 
00230         GObjectClass *parent =
00231                 G_OBJECT_CLASS(midgard_simple_constraint_parent_class);
00232         parent->dispose(G_OBJECT(simple));
00233 }
00234 
00235 static void midgard_simple_constraint_class_init(
00236                 MidgardSimpleConstraintClass *klass) {
00237         g_assert(klass);
00238         MIDGARD_QUERY_CONSTRAINT_CLASS(klass)->add_sql = add_sql;
00239         G_OBJECT_CLASS(klass)->dispose =
00240                 (void (*)(GObject *)) midgard_simple_constraint_dispose;
00241 }
00242 
00252 MidgardSimpleConstraint *midgard_simple_constraint_new(
00253                 midgard *mgd, GObjectClass *klass,
00254                 const gchar *name, const gchar *op, const GValue *value) {
00255         /* Find the matching object property */
00256         const GParamSpec *spec = g_object_class_find_property(klass, name);
00257               
00258         if (spec == NULL) {
00259                 g_warning("Invalid constraint property %s\n", name);
00260                 return NULL;
00261         }
00262 
00263         /* Find the matching operator */
00264         guint i;
00265         for (i = 0; operator[i] != NULL; i++) {
00266                 if (g_ascii_strcasecmp(op, operator[i]) == 0) {
00267                         op = operator[i];
00268                         break;
00269                 }
00270         }
00271         if (operator[i] == NULL) {
00272                 g_warning("Invalid comparison operator %s\n", op);
00273                 return NULL;     
00274         }
00275 
00276         /* Copy the value */
00277         GValue *new_value = g_new0(GValue, 1);
00278         g_value_init(new_value, G_VALUE_TYPE(value));
00279         g_value_copy(value, new_value);
00280         
00281         MidgardSimpleConstraint *simple = MIDGARD_SIMPLE_CONSTRAINT(
00282                 g_object_new(MIDGARD_TYPE_SIMPLE_CONSTRAINT, NULL));
00283         simple->mgd = mgd;
00284         simple->spec = spec;
00285         simple->op = op;
00286         simple->value = new_value;
00287         simple->klass = klass;
00288         return simple;
00289 }

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