436 lines
10 KiB
C
436 lines
10 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "sql-render.h"
|
|
#include <string.h>
|
|
|
|
/**
|
|
* 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
|
|
));
|
|
}
|