500 lines
11 KiB
C
500 lines
11 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 "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, SqlBatch * batch)
|
|
{
|
|
g_return_val_if_fail (DB_IS_CONN (conn), NULL);
|
|
g_return_val_if_fail (SQL_IS_STMT (stmt), NULL);
|
|
g_return_val_if_fail (SQL_IS_BATCH (batch) || !batch, NULL);
|
|
|
|
return g_object_new (DB_TYPE_REQUEST, "conn", conn, "batch", batch, "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_BATCH
|
|
,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, obj->batch, NULL);
|
|
else if (!obj->conn)
|
|
g_warning ("DbRequest: Can't render stmt, conn property not set");
|
|
|
|
break;
|
|
}
|
|
case PROP_BATCH:
|
|
obj->batch = g_value_dup_object (value);
|
|
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_BATCH,
|
|
g_param_spec_object ("batch"
|
|
,_("Batch")
|
|
,_("The batch for render the statement")
|
|
,SQL_TYPE_BATCH
|
|
,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
|
|
));
|
|
}
|