/* * 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): 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)); 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)); 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 )); }