/*
* Copyright (C) 2012 - Juan Ferrer Toribio
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "sql-render.h"
#include
/**
* SECTION: sql-render
* @Short_description: renders an #SqlObject to a string
* @Title: SqlRender
*
* #SqlRender takes care of rendering the different types of #SqlObject to a
* valid SQL string, ready to pass to a Database. In most cases the user won't
* need to use any method besides the creation and destruction ones and
* sql_render_get_string().
**/
G_DEFINE_TYPE (SqlRender, sql_render, G_TYPE_OBJECT);
/**
* sql_render_new:
* @delimiter: the delimiter character
*
* Creates an #SqlRender object
*
* Return value: the new #SqlRender
**/
SqlRender * sql_render_new (gchar delimiter)
{
return g_object_new (SQL_TYPE_RENDER, "delimiter", delimiter, NULL);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Public
/**
* sql_render_get_string:
* @obj: a #SqlRender
* @object: the #SqlObject
* @data: pointer to custom data
* @err: (out) (allow-none): the return location for a #GError or %NULL
*
* Transforms the #SqlObject into a SQL string.
*
* Return value: a string with the rendered statement of %NULL if error.
**/
gchar * sql_render_get_string (SqlRender * obj, gpointer object, SqlBatch * batch, gpointer data, GError ** err)
{
gchar * sql;
g_return_val_if_fail (SQL_IS_RENDER (obj), NULL);
g_return_val_if_fail (G_IS_OBJECT (object), NULL);
g_return_val_if_fail (SQL_IS_BATCH (batch) || !batch, NULL);
obj->data = data;
obj->object = g_object_ref (object);
obj->batch = batch ? g_object_ref (batch) : NULL;
obj->buffer = g_string_sized_new (SQL_BUFFER_SIZE);
obj->ancestors = NULL;
sql_render_add_object (obj, object);
if (obj->error)
{
if (!err)
g_warning ("%s",obj->error->message);
else
g_propagate_error (err, obj->error);
g_clear_error (&obj->error);
sql = g_string_free (obj->buffer, TRUE);
}
else
sql = g_string_free (obj->buffer, FALSE);
g_clear_object (&obj->object);
g_clear_object (&obj->batch);
g_slist_free (obj->ancestors);
obj->ancestors = NULL;
obj->buffer = NULL;
obj->data = NULL;
return sql;
}
/**
* sql_render_register_function:
* @obj: a #SqlRender
* @type: #GType for which the function will be used
* @function: (scope async): render function to use with @type
*
* Registers @function as the function to be used to render the objects of type
* @type. Users most likely won't need to use it.
**/
void sql_render_register_function (SqlRender * obj, GType type, SqlRenderFunc function)
{
g_return_if_fail (SQL_IS_RENDER (obj));
g_hash_table_insert (obj->custom_renderers, GUINT_TO_POINTER (type), function);
}
/**
* sql_render_get_ancestors:
* @obj: the #SqlRender
*
* Obtains a list of parents of the currently rendered object, including it.
*
* Return value: (transfer none) (element-type GObject): the #GSList with the
* parents, the list should not be edited or freed.
**/
GSList * sql_render_get_ancestors (SqlRender * obj)
{
g_return_val_if_fail (SQL_IS_RENDER (obj), NULL);
return obj->ancestors;
}
/**
* sql_render_add_object:
* @obj: a #SqlRender
* @object: a #gpointer to an object
*
* Adds an object to a render.
**/
void sql_render_add_object (SqlRender * obj, gpointer object)
{
SqlRenderFunc function;
g_return_if_fail (SQL_IS_RENDER (obj));
g_return_if_fail (G_IS_OBJECT (object) || !object);
if (object)
{
function = g_hash_table_lookup (obj->custom_renderers,
GUINT_TO_POINTER (G_OBJECT_TYPE (object)));
obj->ancestors = g_slist_prepend (obj->ancestors, object);
if (function)
function (object, obj, obj->batch);
else
sql_object_render (object, obj, obj->batch);
obj->ancestors = g_slist_delete_link (obj->ancestors, obj->ancestors);
}
}
/**
* sql_render_add_espace:
* @obj: a #SqlRender
*
* Adds an space character to a render.
**/
void sql_render_add_espace (SqlRender * obj)
{
g_return_if_fail (SQL_IS_RENDER (obj));
gsize len = obj->buffer->len;
if (len > 0)
switch (obj->buffer->str[len-1])
{
case ' ':
case '(':
case '.':
return;
default:
g_string_append_c (obj->buffer, ' ');
}
}
/**
* sql_render_printf:
* @obj: a #SqlRender
* @string: a format string.
* @...: a %NULL terminated list of variables.
*
* Creates an string from a format string.
**/
void sql_render_printf (SqlRender * obj, const gchar * string, ...)
{
va_list vl;
g_return_if_fail (SQL_IS_RENDER (obj));
g_return_if_fail (string);
va_start (vl, string);
sql_render_add_espace (obj);
g_string_append_vprintf (obj->buffer, string, vl);
va_end (vl);
}
/**
* sql_render_append:
* @obj: a #SqlRender
* @string: a character string.
*
* Appends @string to the current contents of @obj.
**/
void sql_render_append (SqlRender * obj, const gchar * string)
{
g_return_if_fail (SQL_IS_RENDER (obj));
g_return_if_fail (string);
g_string_append (obj->buffer, string);
}
/**
* sql_render_append_with_delimiter:
* @obj: a #SqlRender
* @string: a character string.
* @delimiter: the delimiter character to use.
*
* Appends @string to the current contents of @obj. @delimiter is used to add
* spaces into the string depending where required.
**/
void sql_render_append_with_delimiter (SqlRender * obj, const gchar * string, gchar delimiter)
{
g_return_if_fail (SQL_IS_RENDER (obj));
if (string)
{
sql_render_add_espace (obj);
g_string_append_c (obj->buffer, delimiter);
g_string_append (obj->buffer, string);
g_string_append_c (obj->buffer, delimiter);
}
}
/**
* sql_render_add_token:
* @obj: a #SqlRender
* @token: an SQL reserved keyword
*
* Adds @token to @obj. Note that @token must be a reserved SQL keyword.
**/
void sql_render_add_token (SqlRender * obj, const gchar * token)
{
g_return_if_fail (SQL_IS_RENDER (obj));
if (token)
{
sql_render_add_espace (obj);
g_string_append (obj->buffer, token);
}
}
/**
* sql_render_add_identifier:
* @obj: a #SqlRender
* @identifier: a character string.
*
* Adds @identifier to @obj. @identifier is taken as the name of an identifier
* for an expression in an SQL string.
**/
void sql_render_add_identifier (SqlRender * obj, const gchar * identifier)
{
g_return_if_fail (SQL_IS_RENDER (obj));
sql_render_append_with_delimiter (obj, identifier,
obj->delimiter);
}
/**
* sql_render_add_item:
* @obj: a #SqlRender
* @required:
* @token:
* @item:
*
*
**/
void sql_render_add_item (SqlRender * obj, gboolean required, const gchar * token, gpointer item)
{
g_return_if_fail (SQL_IS_RENDER (obj));
g_return_if_fail (G_IS_OBJECT (item) || !item);
if (item)
{
sql_render_add_token (obj, token);
sql_render_add_object (obj, item);
}
else if (required)
sql_render_set_error (obj);
}
/**
* sql_render_add_list:
* @obj: a #SqlRender
* @required:
* @token:
* @list: a list of objects to add
* @separator:
*
*
**/
void sql_render_add_list (SqlRender * obj, gboolean required, const gchar * token,
SqlList * list, const gchar * separator)
{
g_return_if_fail (SQL_IS_RENDER (obj));
g_return_if_fail (SQL_IS_LIST (list) || !list);
sql_render_add_list_with_func (obj, required, token, list, separator, NULL);
}
/**
* sql_render_add_list_with_func:
* @obj: a #SqlRender
* @required:
* @token:
* @list:
* @separator:
* @function: (scope call):
*
*
**/
void sql_render_add_list_with_func (SqlRender * obj, gboolean required, const gchar * token,
SqlList * list, const gchar * separator, SqlRenderFunc function)
{
GList * i;
g_return_if_fail (SQL_IS_RENDER (obj));
g_return_if_fail (SQL_IS_LIST (list) || !list);
if (list && (i = sql_list_get_items (list)))
{
sql_render_add_token (obj, token);
for (; i; i = i->next)
{
if (function)
function (i->data, obj, obj->batch);
else
sql_render_add_object (obj, i->data);
if (i->next)
g_string_append_printf (obj->buffer, " %s", separator);
}
}
else if (required)
sql_render_set_error (obj);
}
/**
* sql_render_set_error:
* @obj: a #SqlRender
**/
void sql_render_set_error (SqlRender * obj)
{
g_return_if_fail (SQL_IS_RENDER (obj));
obj->error = g_error_new (
SQL_RENDER_LOG_DOMAIN
,SQL_RENDER_ERROR
,"An error ocurred during query render: [ %s ] <-- Error is here"
,obj->buffer->str
);
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties
enum
{
PROP_DELIMITER = 1
};
static void sql_render_set_property (SqlRender * obj, guint id,
const GValue * value, GParamSpec * pspec)
{
switch (id)
{
case PROP_DELIMITER:
obj->delimiter = g_value_get_schar (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec);
}
}
static void sql_render_get_property (SqlRender * obj, guint id,
GValue * value, GParamSpec * pspec)
{
switch (id)
{
case PROP_DELIMITER:
g_value_set_schar (value, obj->delimiter);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, id, pspec);
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++ Class
static void sql_render_init (SqlRender * obj)
{
obj->buffer = NULL;
obj->error = NULL;
obj->custom_renderers = g_hash_table_new (
g_direct_hash
,g_direct_equal
);
}
static void sql_render_finalize (SqlRender * obj)
{
g_hash_table_unref (obj->custom_renderers);
G_OBJECT_CLASS (sql_render_parent_class)->finalize (G_OBJECT (obj));
}
static void sql_render_class_init (SqlRenderClass * klass)
{
GObjectClass * k = G_OBJECT_CLASS (klass);
k->finalize = (GObjectFinalizeFunc) sql_render_finalize;
k->set_property = (GObjectSetPropertyFunc) sql_render_set_property;
k->get_property = (GObjectGetPropertyFunc) sql_render_get_property;
g_object_class_install_property (k, PROP_DELIMITER,
g_param_spec_char ("delimiter"
,_("Delimiter")
,_("The character used for delimite the name of fields, tables...")
,G_MININT8 ,G_MAXINT8 ,'`'
,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE
));
}