/* * 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-sqlite.h" #include /** * SECTION: db-sqlite * @Short_description: manages a connection to a SQLite database. * @Title: DbSqlite * @See_also: #DbConn * * This class manages a connection to a SQLite database internally. This * is accessed through the #DbConn class to internally connect, query and * disconnect the database. **/ G_DEFINE_TYPE (DbSqlite, db_sqlite, DB_TYPE_PLUGIN); //+++++++++++++++++++++++++++++++++++++++++++++++++++ Private static void db_sqlite_close (DbSqlite * self) { if (self->sqlite) { sqlite3_close (self->sqlite); self->sqlite = NULL; } } static gboolean db_sqlite_open (DbSqlite * self, const gchar * host, const gchar * schema, const gchar * user, const gchar * pass, GError ** err) { db_sqlite_close (self); sqlite3_open (schema, &self->sqlite); if (!self->sqlite) { g_set_error (err ,DB_CONN_LOG_DOMAIN ,DB_CONN_ERROR_OPENING ,"%s", sqlite3_errmsg (self->sqlite) ); return FALSE; } return TRUE; } static void db_sqlite_set_ssl (DbSqlite * self, const gchar * ca) { g_warning ("DbSqlite: SSL not supported by this plugin"); } static DbResultSet * db_sqlite_query (DbSqlite * self, const gchar * sql, GError ** err) { gint i, j; gint status; sqlite3_stmt * stmt; GValue def = G_VALUE_INIT; DbResult * result; DbResultSet * set = db_result_set_new (); while (sql) { status = sqlite3_prepare_v2 (self->sqlite, sql, -1, &stmt, &sql); if (status != SQLITE_OK) break; status = sqlite3_step (stmt); if (status != SQLITE_DONE && status != SQLITE_ROW) { sqlite3_finalize (stmt); break; } result = g_new (DbResult, 1); result->ncols = sqlite3_column_count (stmt); set->results = g_slist_append (set->results, result); // INSERT, DELETE or UPDATE if (result->ncols == 0) { result->nrows = sqlite3_changes (self->sqlite); result->column = NULL; result->data = NULL; sqlite3_finalize (stmt); continue; } // SELECT result->nrows = 0; result->column = g_new (DbColumn, result->ncols); result->data = g_ptr_array_new_full (2, (GDestroyNotify) db_row_free); GType gtypes[result->ncols]; for (i = 0; i < result->ncols; i++) { DbColumn * column = &result->column[i]; column->info = 0; column->name = g_strdup (sqlite3_column_origin_name (stmt, i)); column->alias = g_strdup (sqlite3_column_name (stmt, i)); column->table = g_strdup (sqlite3_column_table_name (stmt, i)); column->table_alias = g_strdup (column->table); column->schema = g_strdup (sqlite3_column_database_name (stmt, i)); if (!column->alias) column->alias = g_strdup (column->name); switch (sqlite3_column_type (stmt, i)) { case SQLITE_INTEGER: gtypes[i] = G_TYPE_INT; break; case SQLITE_FLOAT: gtypes[i] = G_TYPE_DOUBLE; break; case SQLITE_BLOB: gtypes[i] = G_TYPE_BYTES; break; case SQLITE_TEXT: case SQLITE_NULL: // TODO default: gtypes[i] = G_TYPE_STRING; break; } if (!g_strcmp0 (column->name, "rowid")) { SqlObject * func = sql_function_new ("last_insert_rowid", NULL); g_value_init (&def, SQL_TYPE_FUNCTION); g_value_take_object (&def, g_object_ref_sink (func)); column->info |= DB_COLUMN_PRI_KEY; } else g_value_init (&def, GVN_TYPE_NULL); column->spec = gvn_param_spec_new_with_attrs (gtypes[i], FALSE, TRUE, &def); g_value_unset (&def); } for (i = 0; status == SQLITE_ROW; i++) { DbRow * row = db_row_new (result->ncols, i); g_ptr_array_add (result->data, row); for (j = 0; j < result->ncols; j++) if (sqlite3_column_type (stmt, j) != SQLITE_NULL) { GValue * value = &row->value[j]; g_value_init (value, gtypes[j]); switch (gtypes[j]) { case G_TYPE_INT: g_value_set_int (value, sqlite3_column_int (stmt, j)); break; case G_TYPE_DOUBLE: g_value_set_double (value, sqlite3_column_double (stmt, j)); break; case G_TYPE_STRING: g_value_set_string (value, (gchar *) sqlite3_column_text (stmt, j)); break; default: if (gtypes[i] == G_TYPE_BYTES) { int len = sqlite3_column_bytes (stmt, j); const void * blob = sqlite3_column_blob (stmt, j); g_value_take_boxed (value, g_bytes_new (blob, len)); } } } else g_value_init (&row->value[j], GVN_TYPE_NULL); status = sqlite3_step (stmt); } result->nrows = i; sqlite3_finalize (stmt); } if (sqlite3_errcode (self->sqlite) != SQLITE_OK) { db_result_set_free (set); set = NULL; g_set_error (err ,DB_CONN_LOG_DOMAIN ,DB_CONN_ERROR_UNKNOW ,"%s", sqlite3_errmsg (self->sqlite) ); } return set; } static void db_sqlite_kill_query (DbSqlite * self) { g_warning ("DbSqlite: Kill not supported by this plugin"); } static void db_sqlite_value_render (SqlValue * self, SqlRender * render, SqlBatch * batch) { if (G_VALUE_TYPE (self->value) == G_TYPE_BYTES) { // TODO: Render binary data g_warning ("DbSqlite: Can't render binary data"); sql_render_set_error (render); } else sql_object_render (SQL_OBJECT (self), render, batch); } //+++++++++++++++++++++++++++++++++++++++++++++++++++ Class static void db_sqlite_init (DbSqlite * self) { SqlRender * render = sql_render_new ('`'); sql_render_register_function (render, SQL_TYPE_VALUE, (SqlRenderFunc) db_sqlite_value_render); DB_PLUGIN (self)->render = render; self->sqlite = NULL; } static void db_sqlite_class_init (DbSqliteClass * k) { DbPluginClass * klass = DB_PLUGIN_CLASS (k); klass->open = (DbPluginOpenFunc) db_sqlite_open; klass->close = (DbPluginCloseFunc) db_sqlite_close; klass->set_ssl = (DbPluginSetSSL) db_sqlite_set_ssl; klass->query = (DbPluginQueryFunc) db_sqlite_query; klass->kill_query = (DbPluginKillQueryFunc) db_sqlite_kill_query; }