This repository has been archived on 2024-07-15. You can view files and clone it, but cannot push or open issues or pull requests.
hedera/db/db-request.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
));
}