astrognome/src/ag-db.c

1230 lines
32 KiB
C
Raw Permalink Normal View History

2014-09-26 13:01:19 +00:00
/* ag-db.c - Database manipulation functionality for Astrognome
*
* Copyright (C) 2014 Polonkai Gergely
*
* Astrognome 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.
*
* Astrognome 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 software; if not, see
* <http://www.gnu.org/licenses/>.
*/
2014-07-20 21:13:56 +00:00
#include <gio/gio.h>
#include <gobject/gobject.h>
#include <libgda/libgda.h>
#include <sql-parser/gda-sql-parser.h>
#include <gobject/gvaluecollector.h>
#include <glib/gi18n.h>
2014-07-20 21:13:56 +00:00
#include "config.h"
#include "ag-app.h"
2014-07-20 21:13:56 +00:00
#include "ag-db.h"
#define SCHEMA_VERSION 1
static AgDb *singleton = NULL;
typedef struct _AgDbPrivate {
gchar *dsn;
GdaConnection *conn;
} AgDbPrivate;
2014-07-30 22:19:09 +00:00
G_DEFINE_QUARK(ag_db_error_quark, ag_db_error);
2014-07-20 21:13:56 +00:00
G_DEFINE_TYPE_WITH_PRIVATE(AgDb, ag_db, G_TYPE_OBJECT);
2014-09-25 14:13:54 +00:00
G_DEFINE_BOXED_TYPE(
AgDbChartSave,
ag_db_chart_save,
(GBoxedCopyFunc)ag_db_chart_save_ref,
(GBoxedFreeFunc)ag_db_chart_save_unref
);
2014-07-20 21:13:56 +00:00
enum {
COLUMN_CHART_ID,
COLUMN_CHART_NAME,
COLUMN_CHART_COUNTRY,
COLUMN_CHART_CITY,
COLUMN_CHART_LONGITUDE,
COLUMN_CHART_LATITUDE,
COLUMN_CHART_ALTITUDE,
COLUMN_CHART_YEAR,
COLUMN_CHART_MONTH,
COLUMN_CHART_DAY,
COLUMN_CHART_HOUR,
COLUMN_CHART_MINUTE,
COLUMN_CHART_SECOND,
COLUMN_CHART_TIMEZONE,
COLUMN_CHART_NOTE,
/* Leave this as the last element */
COLUMN_CHART_COUNT
};
typedef struct {
int id;
gchar *name;
} TableColumnDef;
static TableColumnDef chart_table_column[] = {
{ COLUMN_CHART_ID, "id" },
{ COLUMN_CHART_NAME, "name" },
{ COLUMN_CHART_COUNTRY, "country_name" },
{ COLUMN_CHART_CITY, "city_name" },
{ COLUMN_CHART_LONGITUDE, "longitude" },
{ COLUMN_CHART_LATITUDE, "latitude" },
{ COLUMN_CHART_ALTITUDE, "altitude" },
{ COLUMN_CHART_YEAR, "year" },
{ COLUMN_CHART_MONTH, "month" },
{ COLUMN_CHART_DAY, "day" },
{ COLUMN_CHART_HOUR, "hour" },
{ COLUMN_CHART_MINUTE, "minute" },
{ COLUMN_CHART_SECOND, "second" },
{ COLUMN_CHART_TIMEZONE, "timezone" },
{ COLUMN_CHART_NOTE, "note" },
};
2014-08-01 08:51:15 +00:00
/**
* ag_db_non_select:
* @db: the AgDb to operate on
* @sql: the SQL query to execute
*
* Executes a non-SELECT query on @db. No result is returned right now (TODO)
*/
2014-07-20 21:13:56 +00:00
static void
ag_db_non_select(AgDb *db, const gchar *sql)
{
GdaStatement *sth;
gint nrows;
const gchar *remain;
GdaSqlParser *parser;
AgDbPrivate *priv = ag_db_get_instance_private(db);
GError *err = NULL;
parser = g_object_get_data(G_OBJECT(priv->conn), "parser");
g_assert(GDA_IS_SQL_PARSER(parser));
if ((sth = gda_sql_parser_parse_string(
parser,
sql,
&remain,
&err
)) == NULL) {
g_error(
"SQL error: %s",
(err && err->message)
? err->message
: "no reason"
);
}
2014-07-20 21:13:56 +00:00
if ((nrows = gda_connection_statement_execute_non_select(
priv->conn,
sth,
NULL,
NULL,
&err
)) == -1) {
g_error(
"SQL error: %s",
(err && err->message)
? err->message
: "no details"
);
}
g_object_unref(sth);
}
2014-08-01 08:51:15 +00:00
/**
* ag_db_table_exists:
* @db: the #AgDb object to operate on
* @table: the nawe of the table to check for
*
* Checks if the specified table exists. It is done by querying the
* sqlite_master system table.
*
* Returns: TRUE if the table exists, FALSE otherwise
*/
2014-07-20 21:13:56 +00:00
static gboolean
ag_db_table_exists(AgDb *db, const gchar *table)
{
GdaSqlParser *parser;
GdaStatement *sth;
GdaDataModel *result;
GdaSet *params;
GdaHolder *holder;
const gchar *remain;
gboolean ret;
GValue table_value = G_VALUE_INIT;
AgDbPrivate *priv = ag_db_get_instance_private(db);
GError *err = NULL;
parser = g_object_get_data(G_OBJECT(priv->conn), "parser");
if ((sth = gda_sql_parser_parse_string(
parser,
"SELECT type \n" \
" FROM sqlite_master \n" \
" WHERE type = 'table' \n" \
" AND name = ##name::string",
&remain,
&err
)) == NULL) {
g_error(
"SQL error: %s",
(err && err->message)
? err->message
: "no reason"
);
}
if (gda_statement_get_parameters(sth, &params, &err) == FALSE) {
g_error(
"Params error: %s",
(err && err->message)
? err->message
: "no reason"
);
}
holder = gda_set_get_holder(params, "name");
g_value_init(&table_value, G_TYPE_STRING);
g_value_set_string(&table_value, table);
gda_holder_set_value(holder, &table_value, &err);
g_value_unset(&table_value);
result = gda_connection_statement_execute_select(
priv->conn,
sth,
params,
&err
);
if (gda_data_model_get_n_rows(result) > 0) {
ret = TRUE;
} else {
ret = FALSE;
}
g_object_unref(result);
g_object_unref(sth);
return ret;
}
/**
* ag_db_select:
* @db: the database object to work on
* @err: a #GError or NULL
* @sql: the query to execute
* @...: a NULL terminated list of key-value pairs of the query parameters
*
* Returns: (transfer full): the #GdaDataModel as the result of the query
*/
static GdaDataModel *
ag_db_select(AgDb *db, GError **err, const gchar *sql, ...)
{
GdaSqlParser *parser;
const gchar *remain;
GdaSet *params;
GdaStatement *sth;
GdaDataModel *ret;
gchar *error = NULL;
GError *local_err = NULL;
AgDbPrivate *priv = ag_db_get_instance_private(db);
2014-07-20 21:13:56 +00:00
parser = g_object_get_data(G_OBJECT(priv->conn), "parser");
if ((sth = gda_sql_parser_parse_string(
parser,
sql,
&remain,
&local_err
2014-07-20 21:13:56 +00:00
)) == NULL) {
g_error(
"SQL error: %s",
(local_err && local_err->message)
? local_err->message
2014-07-20 21:13:56 +00:00
: "no reason"
);
}
if (!gda_statement_get_parameters(sth, &params, &local_err)) {
2014-07-20 21:13:56 +00:00
g_error(
"SQL error: %s",
(local_err && local_err->message)
? local_err->message
2014-07-20 21:13:56 +00:00
: "no reason"
);
}
if (params) {
va_list ap;
va_start(ap, sql);
2014-08-01 22:26:49 +00:00
2014-07-20 21:13:56 +00:00
while (TRUE) {
gchar *key;
GdaHolder *holder;
GType type;
GValue value = G_VALUE_INIT;
if ((key = va_arg(ap, gchar *)) == NULL) {
break;
}
if ((holder = gda_set_get_holder(
params,
(const gchar *)key
)) == NULL) {
g_error("Error: holder %s is not defined in query.", key);
}
type = gda_holder_get_g_type(holder);
g_value_init(&value, type);
G_VALUE_COLLECT_INIT(&value, type, ap, 0, &error);
if (error) {
g_error("SQL GValue error: %s", error);
}
if (!gda_holder_set_value(holder, (const GValue *)&value, err)) {
g_error(
"SQL GdaHolder error: %s",
(*err && (*err)->message)
? (*err)->message
: "no reason"
);
}
}
2014-08-01 22:26:49 +00:00
va_end(ap);
2014-07-20 21:13:56 +00:00
}
ret = gda_connection_statement_execute_select(priv->conn, sth, params, err);
g_object_unref(sth);
return ret;
}
2014-08-01 08:51:15 +00:00
/**
* ag_db_check_version_table:
* @db: the #AgDb object to operate on
*
* Checks if the version table exists, and creates it if necessary. It doesn't
* check if the structure is valid!
*/
2014-07-20 21:13:56 +00:00
static void
ag_db_check_version_table(AgDb *db)
{
if (ag_db_table_exists(db, "version")) {
GdaDataModel *result;
gint ret;
g_debug(
"Version table exists. " \
"Checking if db_version is %d and app_version is %s",
SCHEMA_VERSION,
PACKAGE_VERSION
);
result = ag_db_select(
db,
NULL,
"SELECT db_version, app_version FROM version",
NULL
);
ret = gda_data_model_get_n_rows(result);
if (ret < 0) {
g_error("No number of rows?");
} else if (ret > 1) {
g_error("Error in database. Version table has more than one rows!");
} else if (ret == 0) {
// TODO: Check schema against current one maybe? If its fine, we
// may just add a row here.
g_error("Error in database. Version table has no rows!");
} else {
// Version table has one row
const GValue *value;
GdaDataModelIter *iter = gda_data_model_create_iter(result);
gint version;
gda_data_model_iter_move_next(iter);
value = gda_data_model_iter_get_value_at(iter, 0);
if (!G_VALUE_HOLDS_INT(value)) {
g_error(
"Database is invalid. " \
"version.db_version should be an integer."
);
}
version = g_value_get_int(value);
if (version < SCHEMA_VERSION) {
// TODO
g_error("Update required!");
} else if (version > SCHEMA_VERSION) {
const GValue *app_version_value;
const gchar *app_version;
app_version_value = gda_data_model_iter_get_value_at(
iter,
1
);
app_version = g_value_get_string(
app_version_value
);
g_error(
"The version of your database is from the future. " \
"It seems it was created by Astrognome v%s.",
app_version
);
} else {
g_object_unref(result);
}
}
} else {
GValue db_version = G_VALUE_INIT,
app_version = G_VALUE_INIT;
AgDbPrivate *priv = ag_db_get_instance_private(db);
g_value_init(&db_version, G_TYPE_INT);
g_value_init(&app_version, G_TYPE_STRING);
g_value_set_int(&db_version, SCHEMA_VERSION);
g_value_set_static_string(&app_version, PACKAGE_VERSION);
ag_db_non_select(
db,
"CREATE TABLE version (" \
"id INTEGER PRIMARY KEY, " \
"db_version INTEGER UNIQUE NOT NULL, " \
"app_version TEXT UNIQUE NOT NULL" \
")"
);
gda_connection_insert_row_into_table(
priv->conn,
"version",
NULL,
"db_version", &db_version,
"app_version", &app_version,
NULL
);
}
}
2014-08-01 08:51:15 +00:00
/**
* ag_db_check_chart_table:
* @db: the #AgDb object to operate on
*
* Checks if the chart table exists, and creates it if necessary. It doesn't
* check if the structure is valid!
*/
2014-07-20 21:13:56 +00:00
static void
ag_db_check_chart_table(AgDb *db)
{
ag_db_non_select(
db,
"CREATE TABLE IF NOT EXISTS chart (" \
"id INTEGER PRIMARY KEY, " \
"name TEXT NOT NULL, " \
"country_name TEXT, " \
"city_name TEXT, " \
"longitude DOUBLE NOT NULL, " \
"latitude DOUBLE NOT NULL, " \
"altitude DOUBLE, " \
"year INTEGER NOT NULL, " \
"month UNSIGNED INTEGER NOT NULL, " \
"day UNSIGNED INTEGER NOT NULL, " \
"hour UNSIGNED INTEGER NOT NULL, " \
"minute UNSIGNED INTEGER NOT NULL, " \
"second UNSIGNED INTEGER NOT NULL, " \
"timezone DOUBLE NOT NULL, " \
"note TEXT" \
")"
);
}
2014-08-01 08:51:15 +00:00
/**
* ag_db_verify:
* @db: the #AgDb object to operate on
*
* Checks if the database file is sane.
*
* Returns: the status of the database (TODO: make this an enum!)
*/
2014-07-20 21:13:56 +00:00
static gint
ag_db_verify(AgDb *db)
{
ag_db_check_version_table(db);
ag_db_check_chart_table(db);
return 0;
}
static void
ag_db_init(AgDb *db)
{
GdaSqlParser *parser;
GFile *ag_data_dir = ag_get_user_data_dir();
2014-07-20 21:13:56 +00:00
AgDbPrivate *priv = ag_db_get_instance_private(db);
gchar *path = g_file_get_path(ag_data_dir);
GError *err = NULL;
gda_init();
priv->dsn = g_strdup_printf("SQLite://DB_DIR=%s;DB_NAME=charts", path);
g_free(path);
g_clear_object(&ag_data_dir);
2014-07-20 21:13:56 +00:00
priv->conn = gda_connection_open_from_string(
NULL,
priv->dsn,
NULL,
GDA_CONNECTION_OPTIONS_NONE,
&err
);
if (priv->conn == NULL) {
g_error(
"Unable to initialize database: %s",
(err && err->message)
? err->message
: "no reason"
);
}
if ((parser = gda_connection_create_parser(priv->conn)) == NULL) {
parser = gda_sql_parser_new();
}
g_object_set_data_full(
G_OBJECT(priv->conn),
"parser",
parser,
g_object_unref
);
ag_db_verify(db);
}
static void
ag_db_dispose(GObject *gobject)
{
AgDbPrivate *priv = ag_db_get_instance_private(AG_DB(gobject));
g_object_unref(priv->conn);
g_free(priv->dsn);
G_OBJECT_CLASS(ag_db_parent_class)->dispose(gobject);
}
static void
ag_db_finalize(GObject *gobject)
{
singleton = NULL;
G_OBJECT_CLASS(ag_db_parent_class)->finalize(gobject);
}
static void
ag_db_class_init(AgDbClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
gobject_class->dispose = ag_db_dispose;
gobject_class->finalize = ag_db_finalize;
}
AgDb *
ag_db_get(void)
{
if (!singleton) {
singleton = AG_DB(g_object_new(AG_TYPE_DB, NULL));
} else {
g_object_ref(singleton);
}
g_assert(singleton);
return singleton;
}
2014-08-01 08:51:15 +00:00
/**
* ag_db_save_data_free:
* @save_data: the #AgDbChartSave struct to free
2014-08-01 08:51:15 +00:00
*
* Frees @save_data and all its fields
*/
2014-09-25 14:13:54 +00:00
static void
ag_db_chart_save_free(AgDbChartSave *save_data)
{
if (!save_data) {
return;
}
if (save_data->name) {
g_free(save_data->name);
}
if (save_data->country) {
g_free(save_data->country);
}
if (save_data->city) {
g_free(save_data->city);
}
if (save_data->note) {
g_free(save_data->note);
}
g_free(save_data);
}
2014-09-25 14:13:54 +00:00
AgDbChartSave *
ag_db_chart_save_ref(AgDbChartSave *save_data)
{
if (save_data == NULL) {
return NULL;
}
save_data->refcount++;
return save_data;
}
void
ag_db_chart_save_unref(AgDbChartSave *save_data)
{
if (save_data == NULL) {
return;
}
if (--save_data->refcount == 0) {
ag_db_chart_save_free(save_data);
}
}
2014-08-01 08:51:15 +00:00
/**
* ag_db_chart_save:
2014-08-01 08:51:15 +00:00
* @db: the #AgDb object to operate on
* @save_data: the data to save.
* @err: a #GError for storing errors
*
* Saves @save_data to the database. If its db_id field is -1, a new record is
* created. In this case, @save_data is updated and db_id is set to the actual
* data record ID. Otherwise the row with the given ID will be updated.
2014-08-01 08:51:15 +00:00
*
* Returns: TRUE if the save succeeds, FALSE otherwise
*/
gboolean
ag_db_chart_save(AgDb *db, AgDbChartSave *save_data, GError **err)
{
GError *local_err = NULL;
gboolean save_success = TRUE;
GValue db_id = G_VALUE_INIT,
name = G_VALUE_INIT,
country = G_VALUE_INIT,
city = G_VALUE_INIT,
longitude = G_VALUE_INIT,
latitude = G_VALUE_INIT,
altitude = G_VALUE_INIT,
year = G_VALUE_INIT,
month = G_VALUE_INIT,
day = G_VALUE_INIT,
hour = G_VALUE_INIT,
minute = G_VALUE_INIT,
second = G_VALUE_INIT,
timezone = G_VALUE_INIT,
note = G_VALUE_INIT;
AgDbPrivate *priv = ag_db_get_instance_private(db);
if (save_data == NULL) {
g_error("Trying to save a NULL chart!");
}
if (!save_data->populated) {
g_error("Only populated chart data can be saved!");
}
g_value_init(&name, G_TYPE_STRING);
g_value_set_string(&name, save_data->name);
g_value_init(&country, G_TYPE_STRING);
g_value_set_string(&country, save_data->country);
g_value_init(&city, G_TYPE_STRING);
g_value_set_string(&city, save_data->city);
g_value_init(&longitude, G_TYPE_DOUBLE);
g_value_set_double(&longitude, save_data->longitude);
g_value_init(&latitude, G_TYPE_DOUBLE);
g_value_set_double(&latitude, save_data->latitude);
g_value_init(&altitude, G_TYPE_DOUBLE);
g_value_set_double(&altitude, save_data->altitude);
g_value_init(&year, G_TYPE_INT);
g_value_set_int(&year, save_data->year);
g_value_init(&month, G_TYPE_UINT);
g_value_set_uint(&month, save_data->month);
g_value_init(&day, G_TYPE_UINT);
g_value_set_uint(&day, save_data->day);
g_value_init(&hour, G_TYPE_UINT);
g_value_set_uint(&hour, save_data->hour);
g_value_init(&minute, G_TYPE_UINT);
g_value_set_uint(&minute, save_data->minute);
g_value_init(&second, G_TYPE_UINT);
g_value_set_uint(&second, save_data->second);
g_value_init(&timezone, G_TYPE_DOUBLE);
g_value_set_double(&timezone, save_data->timezone);
g_value_init(&note, G_TYPE_STRING);
g_value_set_string(&note, save_data->note);
/* It is possible to get 0 here, which is as non-existant as -1 */
if (save_data->db_id < 0) {
if (!gda_connection_insert_row_into_table(
priv->conn,
"chart",
&local_err,
"name", &name,
"country_name", &country,
"city_name", &city,
"longitude", &longitude,
"latitude", &latitude,
"altitude", &altitude,
"year", &year,
"month", &month,
"day", &day,
"hour", &hour,
"minute", &minute,
"second", &second,
"timezone", &timezone,
"note", &note,
NULL
)) {
g_set_error(
err,
AG_DB_ERROR,
AG_DB_ERROR_DATABASE_ERROR,
"%s",
(local_err && local_err->message)
? local_err->message
: _("Reason unknown")
);
save_success = FALSE;
} else {
// Get inserted row's id
GdaDataModel *result = ag_db_select(
db,
&local_err,
"SELECT last_insert_rowid()"
);
if (result == NULL) {
if (err) {
*err = g_error_copy(local_err);
}
// TODO: a more reasonable return value should be given here
save_success = FALSE;
} else {
const GValue *value = gda_data_model_get_value_at(
result,
0, 0,
NULL
);
save_data->db_id = g_value_get_int(value);
}
}
} else {
g_value_init(&db_id, G_TYPE_INT);
g_value_set_int(&db_id, save_data->db_id);
if (!gda_connection_update_row_in_table(
priv->conn,
"chart",
"id",
&db_id,
&local_err,
"name", &name,
"country_name", &country,
"city_name", &city,
"longitude", &longitude,
"latitude", &latitude,
"altitude", &altitude,
"year", &year,
"month", &month,
"day", &day,
"hour", &hour,
"minute", &minute,
"second", &second,
"timezone", &timezone,
"note", &note,
NULL
)) {
g_set_error(
err,
AG_DB_ERROR,
AG_DB_ERROR_DATABASE_ERROR,
"%s",
(local_err && local_err->message)
? local_err->message
: _("Reason unknown")
);
save_success = FALSE;
}
g_value_unset(&db_id);
}
g_value_unset(&note);
g_value_unset(&timezone);
g_value_unset(&second);
g_value_unset(&minute);
g_value_unset(&hour);
g_value_unset(&day);
g_value_unset(&month);
g_value_unset(&year);
g_value_unset(&altitude);
g_value_unset(&latitude);
g_value_unset(&longitude);
g_value_unset(&city);
g_value_unset(&country);
g_value_unset(&name);
return save_success;
}
2014-07-22 10:25:00 +00:00
2014-09-25 14:13:54 +00:00
AgDbChartSave *
ag_db_chart_save_new(gboolean populated)
2014-09-25 14:13:54 +00:00
{
AgDbChartSave *save_data;
save_data = g_new0(AgDbChartSave, 1);
save_data->refcount = 1;
save_data->populated = populated;
2014-09-25 14:13:54 +00:00
return save_data;
}
2014-08-01 08:51:15 +00:00
/**
* ag_db_chart_get_list:
2014-08-01 08:51:15 +00:00
* @db: the #AgDb object to operate on
* @err: a #GError
*
* Creates a list of all charts in the database, ordered by name. As the return
* value may be NULL even if there are no charts or if there was an error, you
* may want to check @err if the return value is NULL.
*
* Please be aware that the #AgDbChartSave objects of the returned value are not
2014-08-01 08:51:15 +00:00
* fully realised chart records. To get one, you need to call
* ag_db_chart_get_data_by_id()
2014-08-01 08:51:15 +00:00
*
* Returns: (element-type AgDbChartSave) (transfer full): the list of all
* charts, or NULL if there are none, or if there is an error.
2014-08-01 08:51:15 +00:00
*/
2014-07-22 10:25:00 +00:00
GList *
ag_db_chart_get_list(AgDb *db, GError **err)
2014-07-22 10:25:00 +00:00
{
GdaDataModelIter *iter;
GList *ret = NULL;
GdaDataModel *result = ag_db_select(
db,
err,
"SELECT id, name FROM chart ORDER BY name",
NULL
);
if (result == NULL) {
return NULL;
}
iter = gda_data_model_create_iter(result);
while (gda_data_model_iter_move_next(iter)) {
const GValue *value;
AgDbChartSave *save_data = ag_db_chart_save_new(FALSE);
2014-07-22 10:25:00 +00:00
value = gda_data_model_iter_get_value_at(iter, 0);
save_data->db_id = g_value_get_int(value);
value = gda_data_model_iter_get_value_at(iter, 1);
save_data->name = g_strdup(g_value_get_string(value));
ret = g_list_prepend(ret, save_data);
}
return g_list_reverse(ret);
}
2014-08-01 08:51:15 +00:00
/**
* ag_db_chart_get_data_by_id:
2014-08-01 08:51:15 +00:00
* @db: the #AgDb object to operate on
* @row_id: the ID field of the requested chart
* @err: a #GError
*
* Fetches the specified row from the chart table.
*
* Returns: (transfer full): A fully filled #AgDbChartSave record of the chart
2014-08-01 08:51:15 +00:00
*/
AgDbChartSave *
ag_db_chart_get_data_by_id(AgDb *db, guint row_id, GError **err)
{
AgDbChartSave *save_data;
const GValue *value;
gchar *query,
*columns;
guint i;
GdaDataModel *result;
GError *local_err = NULL;
columns = NULL;
for (i = 1; i < COLUMN_CHART_COUNT; i++) {
gchar *tmp;
if (i == 1) {
columns = g_strjoin(
", ",
chart_table_column[0].name,
chart_table_column[1].name,
NULL
);
} else {
tmp = g_strjoin(", ", columns, chart_table_column[i].name, NULL);
g_free(columns);
columns = tmp;
}
}
query = g_strdup_printf(
"SELECT %s FROM chart WHERE id = ##id::gint",
columns
);
g_free(columns);
result = ag_db_select(db, &local_err, query, "id", row_id, NULL);
g_free(query);
if (local_err && (local_err->message)) {
return NULL;
}
if (gda_data_model_get_n_rows(result) < 1) {
g_set_error(
err,
AG_DB_ERROR, AG_DB_ERROR_NO_CHART,
"Chart does not exist"
);
return NULL;
}
save_data = ag_db_chart_save_new(TRUE);
/* id */
value = gda_data_model_get_value_at(
result,
COLUMN_CHART_ID,
0,
NULL
);
save_data->db_id = g_value_get_int(value);
/* name */
value = gda_data_model_get_value_at(
result,
COLUMN_CHART_NAME,
0,
NULL
);
save_data->name = g_strdup(g_value_get_string(value));
/* country */
value = gda_data_model_get_value_at(result, COLUMN_CHART_COUNTRY, 0, NULL);
if (GDA_VALUE_HOLDS_NULL(value)) {
save_data->country = NULL;
} else {
save_data->country = g_strdup(g_value_get_string(value));
}
value = gda_data_model_get_value_at(result, COLUMN_CHART_CITY, 0, NULL);
if (GDA_VALUE_HOLDS_NULL(value)) {
save_data->city = NULL;
} else {
save_data->city = g_strdup(g_value_get_string(value));
}
value = gda_data_model_get_value_at(
result,
COLUMN_CHART_LONGITUDE,
0,
NULL
);
save_data->longitude = g_value_get_double(value);
value = gda_data_model_get_value_at(
result,
COLUMN_CHART_LATITUDE,
0,
NULL
);
save_data->latitude = g_value_get_double(value);
value = gda_data_model_get_value_at(result, COLUMN_CHART_ALTITUDE, 0, NULL);
if (GDA_VALUE_HOLDS_NULL(value)) {
save_data->altitude = DEFAULT_ALTITUDE;
} else {
save_data->altitude = g_value_get_double(value);
}
value = gda_data_model_get_value_at(
result,
COLUMN_CHART_YEAR,
0,
NULL
);
save_data->year = g_value_get_int(value);
value = gda_data_model_get_value_at(
result,
COLUMN_CHART_MONTH,
0,
NULL
);
save_data->month = g_value_get_uint(value);
value = gda_data_model_get_value_at(result, COLUMN_CHART_DAY, 0, NULL);
save_data->day = g_value_get_uint(value);
value = gda_data_model_get_value_at(result, COLUMN_CHART_HOUR, 0, NULL);
save_data->hour = g_value_get_uint(value);
value = gda_data_model_get_value_at(
result,
COLUMN_CHART_MINUTE,
0,
NULL
);
save_data->minute = g_value_get_uint(value);
value = gda_data_model_get_value_at(
result,
COLUMN_CHART_SECOND,
0,
NULL
);
save_data->second = g_value_get_uint(value);
value = gda_data_model_get_value_at(
result,
COLUMN_CHART_TIMEZONE,
0,
NULL
);
save_data->timezone = g_value_get_double(value);
value = gda_data_model_get_value_at(result, COLUMN_CHART_NOTE, 0, NULL);
if (GDA_VALUE_HOLDS_NULL(value)) {
save_data->note = NULL;
} else {
save_data->note = g_strdup(g_value_get_string(value));
}
g_object_unref(result);
return save_data;
}
2014-08-01 08:51:15 +00:00
/**
* string_collate:
* @str1: the first string
* @str2: the second string
*
* A wrapper function around g_utf8_collate() that can handle NULL values. NULL
* and empty strings ("") are considered equal.
2014-08-01 08:51:15 +00:00
*
* Returns: -1 if str1 is ordered before str2, 1 if str2 comes first, or 0 if
* they are identical
*/
static gint
string_collate(const gchar *str1, const gchar *str2)
{
if (
((str1 == NULL) && str2 && (*str2 == '\0'))
|| (str1 && (*str1 == '\0') && (str2 == NULL))
) {
return 0;
}
if (((str1 == NULL) || (str2 == NULL)) && (str1 != str2)) {
return (str1 == NULL) ? -1 : 1;
}
if (str1 == str2) {
return 0;
}
return g_utf8_collate(str1, str2);
}
2014-08-01 08:51:15 +00:00
/**
* ag_db_chart_save_identical:
* @a: the first #AgDbChartSave structure
* @b: the second #AgDbChartSave structure
2014-08-01 08:51:15 +00:00
*
* Compares two #AgDbChartSave structures and their contents.
2014-08-01 08:51:15 +00:00
*
* Returns: TRUE if the two structs hold equal values (strings are also compared
* with string_collate()), FALSE otherwise
*/
gboolean
ag_db_chart_save_identical(const AgDbChartSave *a,
2014-09-25 14:13:54 +00:00
const AgDbChartSave *b,
gboolean chart_only)
{
if (a == b) {
g_debug("identical: Equal");
return TRUE;
}
if ((a == NULL) || (b == NULL)) {
g_debug("identical: One is NULL");
return FALSE;
}
if (!chart_only && string_collate(a->name, b->name) != 0) {
g_debug("identical: Names differ");
return FALSE;
}
if (!chart_only && string_collate(a->country, b->country) != 0) {
g_debug("identical: Countries differ");
return FALSE;
}
if (!chart_only && string_collate(a->city, b->city) != 0) {
g_debug("identical: Cities differ");
return FALSE;
}
if (a->longitude != b->longitude) {
g_debug("identical: Longitudes differ");
return FALSE;
}
if (a->latitude != b->latitude) {
g_debug("identical: Latitudes differ");
return FALSE;
}
if (a->altitude != b->altitude) {
g_debug("identical: Altitudes differ");
return FALSE;
}
if (a->year != b->year) {
g_debug("identical: Years differ");
return FALSE;
}
if (a->month != b->month) {
g_debug("identical: Months differ");
return FALSE;
}
if (a->day != b->day) {
g_debug("identical: Days differ");
return FALSE;
}
if (a->hour != b->hour) {
g_debug("identical: Hours differ");
return FALSE;
}
if (a->minute != b->minute) {
g_debug("identical: Minutes differ");
return FALSE;
}
if (a->second != b->second) {
g_debug("identical: Seconds differ");
return FALSE;
}
if (a->timezone != b->timezone) {
g_debug("identical: Timezones differ");
return FALSE;
}
if (!chart_only && string_collate(a->note, b->note) != 0) {
g_debug("identical: Notes differ");
return FALSE;
}
return TRUE;
}
2014-08-10 22:58:56 +00:00
gboolean
ag_db_chart_delete(AgDb *db, gint row_id, GError **err)
2014-08-10 22:58:56 +00:00
{
AgDbPrivate *priv = ag_db_get_instance_private(db);
GValue id = G_VALUE_INIT;
g_value_init(&id, G_TYPE_INT);
g_value_set_int(&id, row_id);
return gda_connection_delete_row_from_table(
priv->conn, "chart",
"id", &id,
err
);
}