示例#1
0
/**
 * midgard_query_builder_new():
 * @mgd: #MidgardConnection instance
 * @classname: any #MidgardDBObjectClass derived class' name
 *
 * Returns: new #MidgardQueryBuilder instance or %NULL if target class is not registered in GType system 
 * or it's not #MidgardDBObjectClass class derived one.
 */
MidgardQueryBuilder *midgard_query_builder_new(
        MidgardConnection *mgd, const gchar *classname)
{
        g_assert(mgd != NULL);
        g_assert(classname != NULL);

	GType class_type = g_type_from_name(classname);

	if(!__type_is_valid(class_type)) {

		g_warning("Can not initialize Midgard Query Builder for '%s'. It's not registered GType system class", classname);
		MIDGARD_ERRNO_SET(mgd, MGD_ERR_INVALID_OBJECT);
		return NULL;
	}

        MidgardQueryBuilder *builder = 
		g_object_new(MIDGARD_TYPE_QUERY_BUILDER, NULL);
	
	builder->priv = midgard_query_builder_private_new();

        builder->priv->mgd = mgd;
        builder->priv->type = g_type_from_name(classname);
	builder->priv->include_deleted = FALSE;
        builder->priv->error = 0;	
	
	MidgardDBObjectClass *klass = 
		(MidgardDBObjectClass*) g_type_class_peek(class_type);

	if (klass->dbpriv == NULL) {
		
		g_warning("Given %s class has no storage definitions", g_type_name(class_type));
		g_object_unref(builder);
		return NULL;
	}

	builder->priv->schema = klass->dbpriv->storage_data;
       	
	gchar **tables = g_strsplit(midgard_core_class_get_tables(MIDGARD_DBOBJECT_CLASS(klass)), ",", 0);
	guint i = 0;
	while(tables[i] != NULL) {	
		midgard_core_qb_add_table(builder, 
				(const gchar *)g_strchug(tables[i]));
		i++;
	}
	g_strfreev(tables);

        builder->priv->offset = 0;
        builder->priv->limit = G_MAXUINT;

        if (builder->priv->type && builder->priv->schema) {
                return builder;
        } else {
		g_object_unref(builder);
                return NULL;
        }
}
示例#2
0
/**
 * midgard_query_builder_add_constraint_with_property:
 * @builder: #MidgardQueryBuilder instance
 * @property_a: property name
 * @op: comparison operator
 * @property_b: property name
 *
 * Adds named property constraint to the given query builder.
 * Unlike add_constraint method, this one accepts property name
 * instead of scalar value. The difference is that with add_constraint
 * method you can compare property with particular value, while using 
 * add_constraint_with_property method you can compare two different 
 * properties without any need to know their values. 
 * For example, you should use this method if you want to select only 
 * those objects which has been revised after publication time, and particular 
 * date doesn't matter.
 *
 * <example>
 * <programlisting>
 * 
 * midgard_query_builder_add_constraint_with_property(builder, "metadata.revised", ">", "metadata.published");
 *
 * </programlisting>
 * </example>
 *
 * @See: midgard_query_builder_add_constraint()
 *
 * Returns: %TRUE if properties' names are valid, %FALSE otherwise
 */ 
gboolean midgard_query_builder_add_constraint_with_property(
		MidgardQueryBuilder *builder, const gchar *property_a,
		const gchar *op, const gchar *property_b)
{
	g_assert(builder != NULL);
	g_assert(property_a != NULL);
	g_assert(op != NULL);
	g_assert(property_b != NULL);

	MidgardDBObjectClass *klass = MIDGARD_DBOBJECT_CLASS(g_type_class_peek(builder->priv->type));

	MidgardCoreQueryConstraint *constraint = midgard_core_query_constraint_new();
	midgard_core_query_constraint_set_builder(constraint, builder);
	midgard_core_query_constraint_set_class(constraint, klass);

	if(!midgard_core_query_constraint_parse_property(&constraint, klass, property_a))
		return FALSE;
	constraint->priv->current = constraint->priv->prop_right;
	if(!midgard_core_query_constraint_parse_property(&constraint, klass, property_b))
		return FALSE;

	constraint->priv->condition_operator = g_strdup(op);

	midgard_core_query_constraint_build_condition(constraint);

	midgard_core_qb_add_table(builder, constraint->priv->prop_left->table);
	midgard_core_qb_add_table(builder, constraint->priv->prop_right->table);

	if(builder->priv->grouping_ref > 0) {
		
		MidgardGroupConstraint *group = builder->priv->group_constraint;
		midgard_group_constraint_add_constraint(group, constraint);
		return TRUE;
	
	} else {
		
		midgard_core_qb_add_constraint(builder, constraint);
	}
	
	return TRUE;
}
示例#3
0
MidgardQueryOrder *midgard_core_query_order_new(
		MidgardQueryBuilder *builder, const gchar *name, const gchar *dir)
{
	g_assert(builder != NULL);
	g_assert(name != NULL);

	const gchar *user_dir = dir;

	if(user_dir == NULL)
		user_dir = "ASC";

	guint i = 0;
	while(valid_dirs[i] != NULL) {
		
		if (g_str_equal(user_dir, valid_dirs[i])) 
			break;

		i++;
	}

	if(valid_dirs[i] == NULL) {
		
		 g_warning("Invalid order direction: %s", dir);
		 return NULL;
	}

        MidgardQueryOrder *order = g_new(MidgardQueryOrder, 1);

	order->constraint = midgard_core_query_constraint_new();
	order->constraint->priv->order_dir = g_strdup(dir);

	/* Explicitly set builder. It's required for proper constraint's property "parser".
	 * Without builder, implicit joins (for example) are not added */
	midgard_core_query_constraint_set_builder(order->constraint, builder);

	MidgardDBObjectClass *klass = MIDGARD_DBOBJECT_CLASS(g_type_class_peek(builder->priv->type));

	if(!midgard_core_query_constraint_parse_property(&(order->constraint), klass, name)) {

		midgard_core_query_order_free(order);
		return FALSE;
	}

	midgard_core_qb_add_table(builder, order->constraint->priv->prop_left->table);

        return order;
}
示例#4
0
/**
 * midgard_query_builder_add_constraint():
 * @builder: #MidgardQueryBuilder instance
 * @name: property name used for this constraint 
 * @op: comparison operator
 * @value: value used in comparison
 *
 * Adds a constraint to the given query builder. The constraint is
 * expressed as a triple of a field name, a comparison operator, and
 * a comparison value. 
 * 
 * <para>
 * @name referes to a property of the queried Midgard object class. 
 * For example: #MidgardQueryBuilder has been initialized for person 
 * class which has lastname property registered.
 * <example>
 * <programlisting>
 * 
 * GValue value = {0, };
 * g_value_init(&value, G_TYPE_STRING);
 * g_value_set_string(&value, "smith");
 *
 * midgard_query_builder_add_constraint(builder, "lastname", "=", &value);
 * 
 * </programlisting>
 * </example>
 * </para>
 * <para>
 * It also can be name of the linked class property or reserved one. 
 * A dot '.' is used to separate properties for special constraints.
 * If such special constraint property is used, #MidgardQueryBuilder
 * performs right join.
 * <itemizedlist>
 * <listitem><para>
 * First property is the one registered for given class which is a link
 * to property of different class. Second is a property of target class.
 * For example: person object has property 'friend' which holds some identifier
 * (id or guid) to friend class property, and friend class has property 'nick'.
 * In such case we can use constraint and comparison using friend property,
 * even if #MidgardQueryBuilder has been initialized for person class.
 * <example>
 * <programlisting>
 * 
 * GValue value = {0, };
 * g_value_init(&value, G_TYPE_STRING);
 * g_value_set_string(&value, "Lancelot");
 *
 * midgard_query_builder_add_constraint(builder, "friend.nick", "=", &value);
 *
 * </programlisting>
 * </example>
 * </para></listitem>
 * <listitem><para>
 * There are three reserved words which have special meaning for query builder.
 * 'metadata', 'parameter' and 'attachment'. If one of them is used, query builder
 * will make (if necessary) right join and query objects against dependent class table.
 * <example>
 * <programlisting>
 * 
 * GValue value = {0, };
 * g_value_init(&value, G_TYPE_STRING);
 * g_value_set_string(&value, "avatar");
 *
 * midgard_query_builder_add_constraint(builder, "attachment.name", "=", &value);
 *
 * </programlisting>
 * </example>
 * </para></listitem>
 * </itemizedlist>
 * </para>
 *
 * <para>
 * The comparison operator is a string representation of the requested comparison. 
 * Available operators are =, <>, <, >, <=, >=, LIKE, NOT LIKE, IN, INTREE. 
 * </para>
 * 
 * <para>
 * The given @value is copied and converted into the property type before comparison.
 * </para>
 *  
 * Returns: %TRUE if constraint is valid, %FALSE otherwise
 */
gboolean midgard_query_builder_add_constraint(
        MidgardQueryBuilder *builder,
        const gchar *name, const gchar *op, const GValue *value) 
{
	g_assert(builder);
        g_assert(name);
        g_assert(op);
        g_assert(value);
     
	MidgardCoreQueryConstraint *constraint = midgard_core_query_constraint_new();
	midgard_core_query_constraint_set_builder(constraint, builder);
	midgard_core_query_constraint_set_class(constraint, MIDGARD_DBOBJECT_CLASS(g_type_class_peek(builder->priv->type)));
	
	if(!midgard_core_query_constraint_add_operator(constraint, op))
		return FALSE;
		
	if(!midgard_core_query_constraint_parse_property(&constraint, MIDGARD_DBOBJECT_CLASS(g_type_class_peek(builder->priv->type)), name))
		return FALSE;

	if(!midgard_core_query_constraint_add_value(constraint, value))
		return FALSE;
	
	midgard_core_query_constraint_build_condition(constraint);

	/* FIXME, table should be stored per every constraint, order, whatever */
	midgard_core_qb_add_table(builder, constraint->priv->prop_left->table);
	
	if(builder->priv->grouping_ref > 0) {

		MidgardGroupConstraint *group = 
			(MidgardGroupConstraint *)builder->priv->group_constraint;
		midgard_group_constraint_add_constraint(group, constraint);
		return TRUE;
	
	} 

	midgard_core_qb_add_constraint(builder, constraint);	

	return TRUE;
}
gboolean 
midgard_core_query_constraint_parse_property (MidgardCoreQueryConstraint **constraint, MidgardDBObjectClass *klass, const gchar *name)
{
	g_assert(name != NULL);

	if (!klass)
		klass = MIDGARD_DBOBJECT_CLASS((*constraint)->priv->klass);

	(*constraint)->priv->current->table = midgard_core_class_get_table(MIDGARD_DBOBJECT_CLASS(klass)); 

	/* This is optional and might be set to NULL explicitly.
	   We need to parse property for any class which needs this */
	MidgardQueryBuilder *builder = (*constraint)->priv->builder;

	gchar **spltd = g_strsplit(name, ".", 0);
	guint i = 0;

	/* We can support max 3 tokens */
	while(spltd[i] != NULL)
		i++;

	if(i == 1) {
	
		g_strfreev(spltd);

		/* FIXME , it should be done by some more generic function */
		GParamSpec *pspec = 
			g_object_class_find_property(G_OBJECT_CLASS(klass), name);

		if(!pspec) {
			
			g_warning("%s is not a member of %s", 
					name, G_OBJECT_CLASS_NAME(klass));
			return FALSE;
		}

		const gchar *stable = 
			midgard_core_class_get_property_table(
					MIDGARD_DBOBJECT_CLASS(klass), name);
		(*constraint)->priv->pspec = pspec;
		(*constraint)->priv->current->field = 
			midgard_core_class_get_property_colname(
					MIDGARD_DBOBJECT_CLASS(klass), name);
		(*constraint)->priv->current->table = stable;
		
		if (builder)
			midgard_core_qb_add_table(builder, stable);

		(*constraint)->priv->klass = G_OBJECT_CLASS(klass);
		(*constraint)->priv->propname = name;
					
		if(g_str_equal(name, "guid")) 
			(*constraint)->priv->current->field = "guid";

		return TRUE;
	}

	if(i > 3) {
		g_warning("Failed to parse '%s'. At most 3 tokens allowed",
				name);
		g_strfreev(spltd);
		
		return FALSE;
	}

	gboolean parsed = FALSE;
	gboolean do_link = TRUE;
	guint j = 0;
	while(spltd[j] != NULL) {
		
		if(j+1 == i)
			do_link = FALSE;

		parsed = __set_schema_property_attr (builder, &klass, constraint, (const gchar *) spltd[j], do_link);
		
		if(!parsed) {
			g_strfreev(spltd);
			return FALSE;
		}
		j++;
	}

	g_strfreev(spltd);

	return TRUE;
}
static gboolean __set_schema_property_attr(
		MidgardQueryBuilder *builder, MidgardDBObjectClass **klass, 
		MidgardCoreQueryConstraint **constraint, const gchar *name, gboolean do_link)
{
	MgdSchemaPropertyAttr *attr = NULL;
	const gchar *target_property = NULL;
	const gchar *link_property = NULL;

	/* Set klass and property so later we use this info when 
	 * gvalue typecasting should be done */
	(*constraint)->priv->klass = G_OBJECT_CLASS(*klass);
	(*constraint)->priv->propname = name;

	/* Reserved properties */
	/* METADATA */
	if (g_str_equal("metadata", name)) {	
		*klass = g_type_class_peek(MIDGARD_TYPE_METADATA);
		return TRUE;
	}

	/* ATTACHMENT */
	if (g_str_equal(_RESERVED_BLOB_NAME, name)) {
		if (builder && !builder->priv->blob_join_exists) {
			__join_reference(builder, (const gchar *)(*constraint)->priv->current->table, _RESERVED_BLOB_TABLE);
			builder->priv->blob_join_exists = TRUE;
		}

		*klass = 
			g_type_class_peek(g_type_from_name("midgard_attachment"));
		(*constraint)->priv->current->table = _RESERVED_BLOB_TABLE;
		
		return TRUE;
	}

	/* PARAMETER */
	if (g_str_equal(_RESERVED_PARAM_NAME, name)) {
		if (builder && !builder->priv->param_join_exists) {
			__join_reference(builder, (const gchar *)(*constraint)->priv->current->table, _RESERVED_PARAM_TABLE);
			builder->priv->param_join_exists = TRUE;
		}

		*klass = g_type_class_peek(g_type_from_name("midgard_parameter"));
		(*constraint)->priv->current->table = _RESERVED_PARAM_TABLE;

		/* Select DISTINCT.
		 * Constraints might be grouped with "OR" operator and duplicated 
		 * records can be returned explicitly */
		if (builder) {
			builder->priv->select_distinct = TRUE;
		}

		return TRUE;
	}
	
	/* User defined properties */
	GParamSpec *pspec = g_object_class_find_property(G_OBJECT_CLASS(*klass), name);
	if(!pspec) {
		g_warning("%s is not registered property of %s class", name, G_OBJECT_CLASS_NAME(*klass)); 
		return FALSE;
	}

	/* Set klass and property so later we use this info when gvalue typecasting should be done */
	(*constraint)->priv->klass = G_OBJECT_CLASS(*klass);
	(*constraint)->priv->propname = pspec->name;
	(*constraint)->priv->pspec = pspec;

	attr = midgard_core_class_get_property_attr (MIDGARD_DBOBJECT_CLASS(*klass), name);
	target_property = NULL;
	link_property = NULL;

	MidgardReflectionProperty *mrp = midgard_reflection_property_new (MIDGARD_DBOBJECT_CLASS (*klass));
		
	if (midgard_reflection_property_is_link (mrp, name) && do_link) {
		const gchar *target_property = midgard_reflection_property_get_link_target(mrp, name);
		const gchar *link_klass = midgard_reflection_property_get_link_name(mrp, name);
		gboolean is_link = TRUE;
	
		if ((*constraint)->priv->current->link_target == NULL) {
			MidgardCoreQueryConstraintPrivate *mqcp = midgard_core_query_constraint_private_new();
			mqcp->current->is_link = is_link;
			mqcp->current->link_target = target_property;
			mqcp->condition_operator = g_strdup("=");
	
			mqcp->prop_left->table = midgard_core_class_get_property_table (MIDGARD_DBOBJECT_CLASS(*klass), name);
			mqcp->prop_left->field = midgard_core_class_get_property_colname (MIDGARD_DBOBJECT_CLASS(*klass), name); 
			MidgardObjectClass *lclass = g_type_class_peek (g_type_from_name(link_klass));
			mqcp->prop_right->table = midgard_core_class_get_property_table (MIDGARD_DBOBJECT_CLASS(lclass), target_property);
			mqcp->prop_right->field = midgard_core_class_get_property_colname (MIDGARD_DBOBJECT_CLASS(lclass), target_property);

			GString *cond = g_string_new ("");
			g_string_append_printf (cond, 
					"%s.%s %s %s.%s",
					mqcp->prop_left->table,
					mqcp->prop_left->field,
					mqcp->condition_operator,
					mqcp->prop_right->table,
					mqcp->prop_right->field);

			mqcp->condition = g_string_free (cond, FALSE);

			if (builder) {
				midgard_core_qb_add_table(builder, mqcp->prop_left->table);
				midgard_core_qb_add_table(builder, mqcp->prop_right->table);
			}

			if (builder) {
				if(!__add_join(&builder->priv->joins, mqcp)) {
					midgard_core_query_constraint_private_free(mqcp);
				}
			}
		}
			

		*klass = (MidgardDBObjectClass *) midgard_reflection_property_get_link_class(mrp, name);
		(*constraint)->priv->current->table = midgard_core_class_get_table(MIDGARD_DBOBJECT_CLASS(*klass));
		g_object_unref(mrp);
		return TRUE;
	}	
	
	g_object_unref(mrp);
	
	const gchar *table, *field;

	if (g_str_equal (name, "guid")) {	
		table = midgard_core_class_get_table(MIDGARD_DBOBJECT_CLASS(*klass));
		field = "guid";
	} else {
		table = attr->table;
		field = attr->field;
	}

	if(table != NULL)
		(*constraint)->priv->current->table = table;
	
	(*constraint)->priv->current->field = field;		

	return TRUE;
}