/* * 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 "db-request.h" /** * SECTION: db-request * @Short_description: a request to the database sent by the model * @Title: DbRequest * @See_also: #DbConn, #DbModel * * This class is used to send an SQL query to the database through a #DbConn, * and to retrieve any data or metadata obtained by it in the model. **/ G_DEFINE_TYPE (DbRequest, db_request, G_TYPE_OBJECT); //+++++++++++++++++++++++++++++++++++++++++++++++++++ Private static gboolean db_request_idle (DbRequest * obj) { if (obj->callback) obj->callback (obj, obj->user_data); return FALSE; } static void db_request_idle_notify (DbRequest * obj) { if (obj->notify) obj->notify (obj->user_data); g_object_unref (obj); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Methods /** * db_request_new: * @conn: a #DbConn * @sql: SQL statement to execute * * Creates a new request to execute the %sql query. * * Return value: a new #DbRequest **/ DbRequest * db_request_new (DbConn * conn, const gchar * sql) { g_return_val_if_fail (DB_IS_CONN (conn), NULL); return g_object_new (DB_TYPE_REQUEST, "conn", conn, "sql", sql, NULL); } /** * db_request_new_with_stmt: * @conn: a #DbConn * @stmt: the #SqlStmt statement to execute. * * Creates a new request to execute the statement. * * Return value: a new #DbRequest **/ DbRequest * db_request_new_with_stmt (DbConn * conn, SqlStmt * stmt) { g_return_val_if_fail (DB_IS_CONN (conn), NULL); g_return_val_if_fail (SQL_IS_STMT (stmt), NULL); return g_object_new (DB_TYPE_REQUEST, "conn", conn, "stmt", stmt, NULL); } /** * db_request_fetch_result: * @obj: a #DbRequest * @err: (out) (allow-none): return location for a #GError or %NULL * * Obtains the first result of the query. The request must have been made to * call this method. * * Return value: (transfer full) (allow-none): the #DbResult **/ DbResult * db_request_fetch_result (DbRequest * obj, GError ** err) { DbResult * result = NULL; g_return_val_if_fail (DB_IS_REQUEST (obj), NULL); g_mutex_lock (&obj->mutex); if (obj->result_set) { result = db_result_set_take_next (obj->result_set); } else if (err) { g_propagate_error (err, obj->error); obj->error = NULL; } g_mutex_unlock (&obj->mutex); return result; } /** * db_request_fetch_non_select: * @obj: a #DbRequest * @err: (out) (allow-none): return location for a #GError or %NULL * * Fetchs the result of a non select statement. * * Return value: returns 0 or the number of affected rows on success, -1 on failure **/ gint db_request_fetch_non_select (DbRequest * obj, GError ** err) { DbResult * result; g_return_val_if_fail (DB_IS_REQUEST (obj), -1); result = db_request_fetch_result (obj, err); if (result) { gint affected_rows = result->nrows; db_result_free (result); return affected_rows; } return -1; } /** * db_request_fetch_value: * @obj: a #DbRequest * @value: (out): return location for a #GValue * @err: (out) (allow-none): return location for a #GError or %NULL * * Obtains the first result of the query. The request must have been made to * call this method. * * Return value: %TRUE on success, %FALSE on failure **/ gboolean db_request_fetch_value (DbRequest * obj, GValue * value, GError ** err) { DbResult * result; g_return_val_if_fail (DB_IS_REQUEST (obj), FALSE); result = db_request_fetch_result (obj, err); if (result) { DbRow * row = db_result_get_row (result, 0); if (row) { const GValue * row_value = db_row_get_value (row, 0); if (row_value) { g_value_init (value, G_VALUE_TYPE (row_value)); g_value_copy (row_value, value); return TRUE; } } db_result_free (result); } return FALSE; } /** * db_request_exec: * @obj: a #DbRequest * @err: (out) (allow-none): return location for a #GError or %NULL * * Sends the request to the database. **/ gboolean db_request_exec (DbRequest * obj, GError ** err) { gboolean ret; g_return_val_if_fail (DB_IS_REQUEST (obj), FALSE); ret = TRUE; g_mutex_lock (&obj->mutex); switch ((gint) obj->status) { case DB_REQUEST_CLEAN: case DB_REQUEST_ERROR: { DbResultSet * set; GError * query_error = NULL; obj->status = DB_REQUEST_LOADING; g_mutex_unlock (&obj->mutex); set = db_conn_exec (obj->conn, obj->sql, &query_error); g_mutex_lock (&obj->mutex); if (obj->status != DB_REQUEST_CANCELED) { if (set) { obj->result_set = set; obj->status = DB_REQUEST_DONE; } else if (query_error) { g_clear_error (&obj->error); obj->error = g_error_copy (query_error); g_propagate_error (err, query_error); obj->status = DB_REQUEST_ERROR; ret = FALSE; } } else { if (set) db_result_set_free (set); else g_clear_error (&query_error); } } } g_mutex_unlock (&obj->mutex); return ret; } /** * db_request_set_callback: * @obj: a #DbRequest * @callback: * @user_data: * @notify: * * Sends the request to the database. **/ void db_request_set_callback (DbRequest * obj, DbRequestDoneCallback callback, gpointer user_data, GDestroyNotify notify) { g_return_if_fail (DB_IS_REQUEST (obj)); g_mutex_lock (&obj->mutex); obj->callback = callback; obj->user_data = user_data; obj->notify = notify; g_mutex_unlock (&obj->mutex); } /** * db_request_complete: * @obj: a #DbRequest * * Emits the ready or error signal of the object. **/ void db_request_complete (DbRequest * obj) { g_return_if_fail (DB_IS_REQUEST (obj)); g_idle_add_full (G_PRIORITY_HIGH_IDLE, (GSourceFunc) db_request_idle, g_object_ref (obj), (GDestroyNotify) db_request_idle_notify); } /** * db_request_cancel: * @obj: a #DbRequest * * Attempts to cancel the request. * * Return value: %TRUE if request was canceled %FALSE otherwise. **/ void db_request_cancel (DbRequest * obj) { g_return_if_fail (DB_IS_REQUEST (obj)); g_mutex_lock (&obj->mutex); if (obj->status != DB_REQUEST_CANCELED) { if (obj->result_set) { db_result_set_free (obj->result_set); obj->result_set = NULL; } else g_clear_error (&obj->error); obj->status = DB_REQUEST_CANCELED; obj->error = g_error_new ( DB_REQUEST_LOG_DOMAIN ,DB_REQUEST_ERROR_CANCELED ,_("The request was canceled") ); } g_mutex_unlock (&obj->mutex); } /** * db_request_get_conn: * @obj: a #DbRequest * * Gets the connection used by request. * * Return value: (transfer full): the #DbConn **/ DbConn * db_request_get_conn (DbRequest * obj) { g_return_val_if_fail (DB_IS_REQUEST (obj), NULL); return obj->conn; } /** * db_request_get_sql: * @obj: a #DbRequest * * Gets the SQL statement used by #DbRequest. * * Return value: the sql string, must be freed with g_free() **/ gchar * db_request_get_sql (DbRequest * obj) { g_return_val_if_fail (DB_IS_REQUEST (obj), NULL); return g_strdup (obj->sql); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Properties typedef enum { PROP_CONN = 1 ,PROP_SQL ,PROP_STMT ,PROP_RESULT_SET ,PROP_ERROR } DbRequestProp; static void db_request_set_property (DbRequest * obj, guint property_id, const GValue * value, GParamSpec * pspec) { g_mutex_lock (&obj->mutex); switch (property_id) { case PROP_CONN: obj->conn = g_value_dup_object (value); break; case PROP_SQL: { gchar * sql = g_value_dup_string (value); if (sql) obj->sql = sql; break; } case PROP_STMT: { SqlStmt * stmt = g_value_get_object (value); if (obj->conn && stmt) obj->sql = db_conn_render (obj->conn, stmt, NULL); else if (!obj->conn) g_warning ("DbRequest: Can't render stmt, conn property not set"); break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); } g_mutex_unlock (&obj->mutex); } static void db_request_get_property (DbRequest * obj, guint property_id, GValue * value, GParamSpec * pspec) { switch (property_id) { case PROP_CONN: g_value_set_object (value, obj->conn); break; case PROP_SQL: g_value_set_string (value, obj->sql); break; case PROP_RESULT_SET: g_value_set_boxed (value, obj->result_set); break; case PROP_ERROR: g_value_set_boxed (value, obj->error); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec); } } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Class static void db_request_init (DbRequest * obj) { obj->sql = NULL; obj->error = NULL; obj->result_set = NULL; obj->conn = NULL; obj->callback = NULL; obj->notify = NULL; obj->status = DB_REQUEST_CLEAN; g_mutex_init (&obj->mutex); } static void db_request_finalize (DbRequest * obj) { if (obj->result_set) db_result_set_free (obj->result_set); g_clear_error (&obj->error); g_clear_object (&obj->conn); g_free (obj->sql); g_mutex_clear (&obj->mutex); G_OBJECT_CLASS (db_request_parent_class)->finalize (G_OBJECT (obj)); } static void db_request_class_init (DbRequestClass * k) { GObjectClass * klass = G_OBJECT_CLASS (k); klass->finalize = (GObjectFinalizeFunc) db_request_finalize; klass->set_property = (GObjectSetPropertyFunc) db_request_set_property; klass->get_property = (GObjectGetPropertyFunc) db_request_get_property; g_object_class_install_property (klass, PROP_CONN, g_param_spec_object ("conn" ,_("Connection") ,_("The connection used to render and execute the query") ,DB_TYPE_CONN ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE )); g_object_class_install_property (klass, PROP_SQL, g_param_spec_string ("sql" ,_("SQL") ,_("The SQL query to execute") ,NULL ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE )); g_object_class_install_property (klass, PROP_STMT, g_param_spec_object ("stmt" ,_("Statement") ,_("The statement to execute") ,SQL_TYPE_STMT ,G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE )); g_object_class_install_property (klass, PROP_RESULT_SET, g_param_spec_boxed ("result-set" ,_("Result") ,_("The result data of the query") ,DB_TYPE_RESULT_SET ,G_PARAM_READABLE )); g_object_class_install_property (klass, PROP_ERROR, g_param_spec_boxed ("error" ,_("Error") ,_("The GError, if an error ocurred") ,G_TYPE_ERROR ,G_PARAM_READABLE )); }