diff options
author | Xan Lopez <xlopez@igalia.com> | 2011-11-24 17:37:23 +0800 |
---|---|---|
committer | Xan Lopez <xan@igalia.com> | 2012-03-07 04:49:42 +0800 |
commit | 77ee0fdf4c5383f2134608e741b73f51ef30f430 (patch) | |
tree | c46e275c07d732d6e65cc45673f56f9e7fc9660a | |
parent | a915b03c590e60f557d9097893601e1b1bf8c08d (diff) | |
download | gsoc2013-epiphany-77ee0fdf4c5383f2134608e741b73f51ef30f430.tar gsoc2013-epiphany-77ee0fdf4c5383f2134608e741b73f51ef30f430.tar.gz gsoc2013-epiphany-77ee0fdf4c5383f2134608e741b73f51ef30f430.tar.bz2 gsoc2013-epiphany-77ee0fdf4c5383f2134608e741b73f51ef30f430.tar.lz gsoc2013-epiphany-77ee0fdf4c5383f2134608e741b73f51ef30f430.tar.xz gsoc2013-epiphany-77ee0fdf4c5383f2134608e741b73f51ef30f430.tar.zst gsoc2013-epiphany-77ee0fdf4c5383f2134608e741b73f51ef30f430.zip |
Add GObject wrapper classes for SQLite
We'll use them to implement the new history/bookmarks storage backend.
Code by Martin Robinson (mrobinson@igalia.com) and Claudio Saavedra
(csaavedra@igalia.com)
-rw-r--r-- | lib/Makefile.am | 5 | ||||
-rw-r--r-- | lib/ephy-sqlite-connection.c | 200 | ||||
-rw-r--r-- | lib/ephy-sqlite-connection.h | 72 | ||||
-rw-r--r-- | lib/ephy-sqlite-statement.c | 269 | ||||
-rw-r--r-- | lib/ephy-sqlite-statement.h | 74 | ||||
-rw-r--r-- | tests/Makefile.am | 4 | ||||
-rw-r--r-- | tests/ephy-sqlite.c | 213 |
7 files changed, 837 insertions, 0 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am index 0e4869ad8..1d7a26825 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -24,6 +24,9 @@ NOINST_H_FILES = \ ephy-shlib-loader.h \ ephy-signal-accumulator.h \ ephy-smaps.h \ + ephy-sqlite.h \ + ephy-sqlite-connection.h \ + ephy-sqlite-statement.h \ ephy-string.h \ ephy-time-helpers.h \ ephy-zoom.h @@ -64,6 +67,8 @@ libephymisc_la_SOURCES = \ ephy-shlib-loader.c \ ephy-signal-accumulator.c \ ephy-smaps.c \ + ephy-sqlite-connection.c \ + ephy-sqlite-statement.c \ ephy-state.c \ ephy-string.c \ ephy-time-helpers.c \ diff --git a/lib/ephy-sqlite-connection.c b/lib/ephy-sqlite-connection.c new file mode 100644 index 000000000..655d6c28e --- /dev/null +++ b/lib/ephy-sqlite-connection.c @@ -0,0 +1,200 @@ +/* + * Copyright © 2011 Igalia S.L. + * + * 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 2, 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "ephy-sqlite-connection.h" + +#include <sqlite3.h> + +struct _EphySQLiteConnectionPrivate { + sqlite3 *database; +}; + +#define EPHY_SQLITE_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), EPHY_TYPE_SQLITE_CONNECTION, EphySQLiteConnectionPrivate)) + +G_DEFINE_TYPE (EphySQLiteConnection, ephy_sqlite_connection, G_TYPE_OBJECT); + +static void +ephy_sqlite_connection_finalize (GObject *self) +{ + ephy_sqlite_connection_close (EPHY_SQLITE_CONNECTION (self)); + G_OBJECT_CLASS (ephy_sqlite_connection_parent_class)->dispose (self); +} + +static void +ephy_sqlite_connection_class_init (EphySQLiteConnectionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = ephy_sqlite_connection_finalize; + g_type_class_add_private (object_class, sizeof (EphySQLiteConnectionPrivate)); +} + +static void +ephy_sqlite_connection_init (EphySQLiteConnection *self) +{ + self->priv = EPHY_SQLITE_CONNECTION_GET_PRIVATE (self); + self->priv->database = NULL; +} + +static GQuark get_ephy_sqlite_quark () +{ + return g_quark_from_static_string ("ephy-sqlite"); +} + +static void +set_error_from_string (const char* string, GError **error) +{ + if (error) + *error = g_error_new (get_ephy_sqlite_quark (), 0, string, 0); +} + +EphySQLiteConnection * +ephy_sqlite_connection_new () +{ + return EPHY_SQLITE_CONNECTION (g_object_new (EPHY_TYPE_SQLITE_CONNECTION, NULL)); +} + +gboolean +ephy_sqlite_connection_open (EphySQLiteConnection *self, const gchar *filename, GError **error) +{ + EphySQLiteConnectionPrivate *priv = self->priv; + + if (priv->database) { + set_error_from_string ("Connection already open.", error); + return FALSE; + } + + if (sqlite3_open (filename, &priv->database) != SQLITE_OK) { + ephy_sqlite_connection_get_error (self, error); + priv->database = NULL; + return FALSE; + } + + return TRUE; +} + +void +ephy_sqlite_connection_close (EphySQLiteConnection *self) +{ + EphySQLiteConnectionPrivate *priv = self->priv; + if (priv->database) { + sqlite3_close (priv->database); + priv->database = NULL; + } +} + +void +ephy_sqlite_connection_get_error (EphySQLiteConnection *self, GError **error) +{ + if (error) + *error = g_error_new (get_ephy_sqlite_quark (), 0, sqlite3_errmsg (self->priv->database), 0); +} + +gboolean +ephy_sqlite_connection_execute (EphySQLiteConnection *self, const char *sql, GError **error) +{ + EphySQLiteConnectionPrivate *priv = self->priv; + + if (priv->database == NULL) { + set_error_from_string ("Connection not open.", error); + return FALSE; + } + + return sqlite3_exec (priv->database, sql, NULL, NULL, NULL) == SQLITE_OK; +} + +EphySQLiteStatement * +ephy_sqlite_connection_create_statement (EphySQLiteConnection *self, const char *sql, GError **error) +{ + EphySQLiteConnectionPrivate *priv = self->priv; + sqlite3_stmt *prepared_statement; + + if (priv->database == NULL) { + set_error_from_string ("Connection not open.", error); + return NULL; + } + + if (sqlite3_prepare_v2 (priv->database, sql, -1, &prepared_statement, NULL) != SQLITE_OK) { + ephy_sqlite_connection_get_error (self, error); + return NULL; + } + + return EPHY_SQLITE_STATEMENT (g_object_new (EPHY_TYPE_SQLITE_STATEMENT, + "prepared-statement", prepared_statement, + "connection", self, + NULL)); +} + +gint64 +ephy_sqlite_connection_get_last_insert_id (EphySQLiteConnection *self) +{ + return sqlite3_last_insert_rowid (self->priv->database); +} + +gboolean +ephy_sqlite_connection_begin_transaction (EphySQLiteConnection *self, GError **error) +{ + return ephy_sqlite_connection_execute (self, "BEGIN TRANSACTION", error); +} + +gboolean +ephy_sqlite_connection_rollback_transaction (EphySQLiteConnection *self, GError **error) +{ + return ephy_sqlite_connection_execute (self, "ROLLBACK", error); +} + +gboolean +ephy_sqlite_connection_commit_transaction (EphySQLiteConnection *self, GError **error) +{ + return ephy_sqlite_connection_execute (self, "COMMIT", error); +} + +gboolean +ephy_sqlite_connection_table_exists (EphySQLiteConnection *self, const char *table_name) +{ + GError *error = NULL; + gboolean table_exists = FALSE; + + EphySQLiteStatement *statement = ephy_sqlite_connection_create_statement (self, + "SELECT COUNT(type) FROM sqlite_master WHERE type='table' and name=?", &error); + if (error) { + g_error ("Could not detect table existence: %s", error->message); + g_error_free (error); + return FALSE; + } + + ephy_sqlite_statement_bind_string (statement, 0, table_name, &error); + if (error) { + g_object_unref (statement); + g_error ("Could not detect table existence: %s", error->message); + g_error_free (error); + return FALSE; + } + + ephy_sqlite_statement_step (statement, &error); + if (error) { + g_object_unref (statement); + g_error ("Could not detect table existence: %s", error->message); + g_error_free (error); + return FALSE; + } + + table_exists = ephy_sqlite_statement_get_column_as_int (statement, 0); + g_object_unref (statement); + return table_exists; +} diff --git a/lib/ephy-sqlite-connection.h b/lib/ephy-sqlite-connection.h new file mode 100644 index 000000000..74f30bd64 --- /dev/null +++ b/lib/ephy-sqlite-connection.h @@ -0,0 +1,72 @@ +/* + * Copyright © 2011 Igalia S.L. + * + * 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 2, 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef EPHY_SQLITE_CONNECTION_H +#define EPHY_SQLITE_CONNECTION_H + +#include <glib-object.h> +#include "ephy-sqlite-statement.h" + +G_BEGIN_DECLS + +/* convenience macros */ +#define EPHY_TYPE_SQLITE_CONNECTION (ephy_sqlite_connection_get_type()) +#define EPHY_SQLITE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),EPHY_TYPE_SQLITE_CONNECTION,EphySQLiteConnection)) +#define EPHY_SQLITE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),EPHY_TYPE_SQLITE_CONNECTION,EphySQLiteConnectionClass)) +#define EPHY_IS_SQLITE_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),EPHY_TYPE_SQLITE_CONNECTION)) +#define EPHY_IS_SQLITE_CONNECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),EPHY_TYPE_SQLITE_CONNECTION)) +#define EPHY_SQLITE_CONNECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),EPHY_TYPE_SQLITE_CONNECTION,EphySQLiteConnectionClass)) + +typedef struct _EphySQLiteConnection EphySQLiteConnection; +typedef struct _EphySQLiteConnectionClass EphySQLiteConnectionClass; +typedef struct _EphySQLiteConnectionPrivate EphySQLiteConnectionPrivate; + +struct _EphySQLiteConnection { + GObject parent; + + /* private */ + EphySQLiteConnectionPrivate *priv; +}; + +struct _EphySQLiteConnectionClass { + GObjectClass parent_class; +}; + +GType ephy_sqlite_connection_get_type (void); + +EphySQLiteConnection * ephy_sqlite_connection_new (void); + +gboolean ephy_sqlite_connection_open (EphySQLiteConnection *self, const gchar *filename, GError **error); +void ephy_sqlite_connection_close (EphySQLiteConnection *self); + +void ephy_sqlite_connection_get_error (EphySQLiteConnection *self, GError **error); + +gboolean ephy_sqlite_connection_execute (EphySQLiteConnection *self, const char *sql, GError **error); +EphySQLiteStatement * ephy_sqlite_connection_create_statement (EphySQLiteConnection *self, const char *sql, GError **error); +gint64 ephy_sqlite_connection_get_last_insert_id (EphySQLiteConnection *self); + +gboolean ephy_sqlite_connection_begin_transaction (EphySQLiteConnection *self, GError **error); +gboolean ephy_sqlite_connection_rollback_transaction (EphySQLiteConnection *self, GError **error); +gboolean ephy_sqlite_connection_commit_transaction (EphySQLiteConnection *self, GError **error); + +gboolean ephy_sqlite_connection_table_exists (EphySQLiteConnection *self, const char *table_name); + +G_END_DECLS + +#endif /* EPHY_SQLITE_CONNECTION_H */ + diff --git a/lib/ephy-sqlite-statement.c b/lib/ephy-sqlite-statement.c new file mode 100644 index 000000000..f95518a2a --- /dev/null +++ b/lib/ephy-sqlite-statement.c @@ -0,0 +1,269 @@ +/* + * Copyright © 2011 Igalia S.L. + * + * 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 2, 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "ephy-sqlite-statement.h" + +#include "ephy-sqlite-connection.h" +#include <sqlite3.h> + +enum +{ + PROP_0, + PROP_PREPARED_STATEMENT, + PROP_CONNECTION +}; + +struct _EphySQLiteStatementPrivate { + sqlite3_stmt *prepared_statement; + EphySQLiteConnection *connection; +}; + +#define EPHY_SQLITE_STATEMENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), EPHY_TYPE_SQLITE_STATEMENT, EphySQLiteStatementPrivate)) + +G_DEFINE_TYPE (EphySQLiteStatement, ephy_sqlite_statement, G_TYPE_OBJECT); + +static void +ephy_sqlite_statement_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + EphySQLiteStatement *self = EPHY_SQLITE_STATEMENT (object); + + switch (property_id) { + case PROP_PREPARED_STATEMENT: + self->priv->prepared_statement = g_value_get_pointer (value); + break; + case PROP_CONNECTION: + self->priv->connection = EPHY_SQLITE_CONNECTION (g_object_ref (g_value_get_object (value))); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec); + break; + } +} + +static void +ephy_sqlite_statement_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + EphySQLiteStatement *self = EPHY_SQLITE_STATEMENT (object); + + switch (property_id) { + case PROP_PREPARED_STATEMENT: + g_value_set_pointer (value, self->priv->prepared_statement); + break; + case PROP_CONNECTION: + g_value_set_object (value, self->priv->connection); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +ephy_sqlite_statement_finalize (GObject *self) +{ + EphySQLiteStatementPrivate *priv = EPHY_SQLITE_STATEMENT (self)->priv; + + if (priv->prepared_statement) { + sqlite3_finalize (priv->prepared_statement); + priv->prepared_statement = NULL; + } + + if (priv->connection) { + g_object_unref (priv->connection); + priv->connection = NULL; + } + + G_OBJECT_CLASS (ephy_sqlite_statement_parent_class)->dispose (self); +} + +static void +ephy_sqlite_statement_class_init (EphySQLiteStatementClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + gobject_class->finalize = ephy_sqlite_statement_finalize; + gobject_class->get_property = ephy_sqlite_statement_get_property; + gobject_class->set_property = ephy_sqlite_statement_set_property; + g_type_class_add_private (gobject_class, sizeof (EphySQLiteStatementPrivate)); + + g_object_class_install_property (gobject_class, + PROP_PREPARED_STATEMENT, + g_param_spec_pointer ("prepared-statement", + "Prepared statement", + "The statement's backing SQLite prepared statement", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); + + g_object_class_install_property (gobject_class, + PROP_CONNECTION, + g_param_spec_object ("connection", + "Connection", + "The statement's backing SQLite connection", + EPHY_TYPE_SQLITE_CONNECTION, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB)); +} + +static void +ephy_sqlite_statement_init (EphySQLiteStatement *self) +{ + self->priv = EPHY_SQLITE_STATEMENT_GET_PRIVATE (self); + self->priv->prepared_statement = NULL; + self->priv->connection = NULL; +} + +gboolean +ephy_sqlite_statement_bind_null (EphySQLiteStatement *self, int column, GError **error) +{ + if (sqlite3_bind_null (self->priv->prepared_statement, column) != SQLITE_OK) { + ephy_sqlite_connection_get_error (self->priv->connection, error); + return FALSE; + } + + return TRUE; +} + +gboolean +ephy_sqlite_statement_bind_boolean (EphySQLiteStatement *self, int column, gboolean value, GError **error) +{ + if (sqlite3_bind_int (self->priv->prepared_statement, column + 1, value ? 1 : 0) != SQLITE_OK) { + ephy_sqlite_connection_get_error (self->priv->connection, error); + return FALSE; + } + + return TRUE; +} + +gboolean +ephy_sqlite_statement_bind_int (EphySQLiteStatement *self, int column, int value, GError **error) +{ + if (sqlite3_bind_int (self->priv->prepared_statement, column + 1, value) != SQLITE_OK) { + ephy_sqlite_connection_get_error (self->priv->connection, error); + return FALSE; + } + + return TRUE; +} + +gboolean +ephy_sqlite_statement_bind_double (EphySQLiteStatement *self, int column, double value, GError **error) +{ + if (sqlite3_bind_double (self->priv->prepared_statement, column + 1, value) != SQLITE_OK) { + ephy_sqlite_connection_get_error (self->priv->connection, error); + return FALSE; + } + + return TRUE; +} + +gboolean +ephy_sqlite_statement_bind_string (EphySQLiteStatement *self, int column, const char *value, GError **error) +{ + if (sqlite3_bind_text (self->priv->prepared_statement, column + 1, value, -1, SQLITE_TRANSIENT) != SQLITE_OK) { + ephy_sqlite_connection_get_error (self->priv->connection, error); + return FALSE; + } + + return TRUE; +} + +gboolean +ephy_sqlite_statement_bind_blob (EphySQLiteStatement *self, int column, const void *value, int length, GError **error) +{ + if (sqlite3_bind_blob (self->priv->prepared_statement, column + 1, value, length, SQLITE_TRANSIENT) != SQLITE_OK) { + ephy_sqlite_connection_get_error (self->priv->connection, error); + return FALSE; + } + return TRUE; +} + +gboolean +ephy_sqlite_statement_step (EphySQLiteStatement *self, GError **error) +{ + int error_code = sqlite3_step (self->priv->prepared_statement); + if (error_code != SQLITE_OK && error_code != SQLITE_ROW && error_code != SQLITE_DONE) { + ephy_sqlite_connection_get_error (self->priv->connection, error); + } + + return error_code == SQLITE_ROW; +} + +void +ephy_sqlite_statement_reset (EphySQLiteStatement *self) +{ + sqlite3_reset (self->priv->prepared_statement); +} + +int +ephy_sqlite_statement_get_column_count (EphySQLiteStatement *self) +{ + return sqlite3_column_count (self->priv->prepared_statement); +} + +EphySQLiteColumnType +ephy_sqlite_statement_get_column_type (EphySQLiteStatement *self, int column) +{ + int column_type = sqlite3_column_type (self->priv->prepared_statement, column); + switch (column_type) { + case SQLITE_INTEGER: + return EPHY_SQLITE_COLUMN_TYPE_INT; + case SQLITE_FLOAT: + return EPHY_SQLITE_COLUMN_TYPE_FLOAT; + case SQLITE_TEXT: + return EPHY_SQLITE_COLUMN_TYPE_STRING; + case SQLITE_BLOB: + return EPHY_SQLITE_COLUMN_TYPE_BLOB; + case SQLITE_NULL: + default: + return EPHY_SQLITE_COLUMN_TYPE_NULL; + } +} + +int +ephy_sqlite_statement_get_column_size (EphySQLiteStatement *self, int column) +{ + return sqlite3_column_bytes (self->priv->prepared_statement, column); +} + +int +ephy_sqlite_statement_get_column_as_boolean (EphySQLiteStatement *self, int column) +{ + return ephy_sqlite_statement_get_column_as_int (self, column); +} + +int +ephy_sqlite_statement_get_column_as_int (EphySQLiteStatement *self, int column) +{ + return sqlite3_column_int (self->priv->prepared_statement, column); +} + +double +ephy_sqlite_statement_get_column_as_double (EphySQLiteStatement *self, int column) +{ + return sqlite3_column_double (self->priv->prepared_statement, column); +} + +const char* +ephy_sqlite_statement_get_column_as_string (EphySQLiteStatement *self, int column) +{ + return (const char*) sqlite3_column_text (self->priv->prepared_statement, column); +} + +const void* +ephy_sqlite_statement_get_column_as_blob (EphySQLiteStatement *self, int column) +{ + return sqlite3_column_blob (self->priv->prepared_statement, column); +} diff --git a/lib/ephy-sqlite-statement.h b/lib/ephy-sqlite-statement.h new file mode 100644 index 000000000..c88947fab --- /dev/null +++ b/lib/ephy-sqlite-statement.h @@ -0,0 +1,74 @@ +/* + * Copyright © 2011 Igalia S.L. + * + * 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 2, 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef EPHY_SQLITE_STATEMENT_H +#define EPHY_SQLITE_STATEMENT_H + +#include <glib-object.h> +#include "ephy-sqlite.h" + +G_BEGIN_DECLS + +/* convenience macros */ +#define EPHY_TYPE_SQLITE_STATEMENT (ephy_sqlite_statement_get_type()) +#define EPHY_SQLITE_STATEMENT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),EPHY_TYPE_SQLITE_STATEMENT,EphySQLiteStatement)) +#define EPHY_SQLITE_STATEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),EPHY_TYPE_SQLITE_STATEMENT,EphySQLiteStatementClass)) +#define EPHY_IS_SQLITE_STATEMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),EPHY_TYPE_SQLITE_STATEMENT)) +#define EPHY_IS_SQLITE_STATEMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),EPHY_TYPE_SQLITE_STATEMENT)) +#define EPHY_SQLITE_STATEMENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),EPHY_TYPE_SQLITE_STATEMENT,EphySQLiteStatementClass)) + +typedef struct _EphySQLiteStatement EphySQLiteStatement; +typedef struct _EphySQLiteStatementClass EphySQLiteStatementClass; +typedef struct _EphySQLiteStatementPrivate EphySQLiteStatementPrivate; + +struct _EphySQLiteStatement { + GObject parent; + + /* private */ + EphySQLiteStatementPrivate *priv; +}; + +struct _EphySQLiteStatementClass { + GObjectClass parent_class; +}; + +GType ephy_sqlite_statement_get_type (void); + +gboolean ephy_sqlite_statement_bind_null (EphySQLiteStatement *statement, int column, GError **error); +gboolean ephy_sqlite_statement_bind_boolean (EphySQLiteStatement *statement, int column, gboolean value, GError **error); +gboolean ephy_sqlite_statement_bind_int (EphySQLiteStatement *statement, int column, int value, GError **error); +gboolean ephy_sqlite_statement_bind_double (EphySQLiteStatement *statement, int column, double value, GError **error); +gboolean ephy_sqlite_statement_bind_string (EphySQLiteStatement *statement, int column, const char *value, GError **error); +gboolean ephy_sqlite_statement_bind_blob (EphySQLiteStatement *statement, int column, const void *value, int length, GError **error); + +gboolean ephy_sqlite_statement_step (EphySQLiteStatement *statement, GError **error); +void ephy_sqlite_statement_reset (EphySQLiteStatement *statement); + +int ephy_sqlite_statement_get_column_count (EphySQLiteStatement *statement); +EphySQLiteColumnType ephy_sqlite_statement_get_column_type (EphySQLiteStatement *statement, int column); +int ephy_sqlite_statement_get_column_size (EphySQLiteStatement *statement, int column); +int ephy_sqlite_statement_get_column_as_boolean (EphySQLiteStatement *statement, int column); +int ephy_sqlite_statement_get_column_as_int (EphySQLiteStatement *statement, int column); +double ephy_sqlite_statement_get_column_as_double (EphySQLiteStatement *statement, int column); +const char* ephy_sqlite_statement_get_column_as_string (EphySQLiteStatement *statement, int column); +const void* ephy_sqlite_statement_get_column_as_blob (EphySQLiteStatement *statement, int column); + +G_END_DECLS + +#endif /* EPHY_SQLITE_STATEMENT_H */ + diff --git a/tests/Makefile.am b/tests/Makefile.am index 2571843fb..1a0d2cc26 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -4,6 +4,7 @@ noinst_PROGRAMS = \ test-ephy-embed-utils \ test-ephy-location-entry \ test-ephy-search-entry \ + test-ephy-sqlite \ $(NULL) # Mostly copied from Makefile.decl in glib @@ -119,3 +120,6 @@ test_ephy_location_entry_SOURCES = \ test_ephy_search_entry_SOURCES = \ ephy-search-entry-test.c + +test_ephy_sqlite_SOURCES = \ + ephy-sqlite.c diff --git a/tests/ephy-sqlite.c b/tests/ephy-sqlite.c new file mode 100644 index 000000000..309f7cfb5 --- /dev/null +++ b/tests/ephy-sqlite.c @@ -0,0 +1,213 @@ +/* + * ephy-sqlite-statement.c + * This file is part of Epiphany + * + * Copyright © 2011 Igalia S.L. + * + * Epiphany 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 2 of the License, or + * (at your option) any later version. + * + * Epiphany 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 Epiphany; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include "ephy-sqlite-connection.h" +#include "ephy-sqlite-statement.h" +#include <glib.h> +#include <glib/gstdio.h> +#include <gtk/gtk.h> + +static EphySQLiteConnection * +ensure_empty_database (const char* filename) +{ + EphySQLiteConnection *connection = ephy_sqlite_connection_new (); + GError *error = NULL; + + if (g_file_test (filename, G_FILE_TEST_IS_REGULAR)) + g_unlink (filename); + + g_assert (ephy_sqlite_connection_open (connection, filename, &error)); + g_assert (!error); + return connection; +} + +static void +test_create_connection (void) +{ + GError *error = NULL; + gchar *temporary_file = g_build_filename (g_get_tmp_dir (), "epiphany-sqlite-test.db", NULL); + + EphySQLiteConnection *connection = ensure_empty_database (temporary_file); + ephy_sqlite_connection_close (connection); + + g_assert ( g_file_test (temporary_file, G_FILE_TEST_IS_REGULAR)); + g_unlink (temporary_file); + g_assert ( !g_file_test (temporary_file, G_FILE_TEST_IS_REGULAR)); + g_free (temporary_file); + + temporary_file = g_build_filename (g_get_tmp_dir (), "directory-that-does-not-exist", "epiphany_sqlite_test.db", NULL); + g_assert (!ephy_sqlite_connection_open (connection, temporary_file, &error)); + g_assert (error); + g_assert (!g_file_test (temporary_file, G_FILE_TEST_IS_REGULAR)); + g_object_unref (connection); +} + + +static void +test_create_statement (void) +{ + gchar *temporary_file = g_build_filename (g_get_tmp_dir (), "epiphany-sqlite-test.db", NULL); + EphySQLiteConnection* connection = ensure_empty_database (temporary_file); + GError *error = NULL; + EphySQLiteStatement *statement = NULL; + + statement = ephy_sqlite_connection_create_statement (connection, "CREATE TABLE TEST (id INTEGER)", &error); + g_assert (statement); + g_assert (!error); + g_object_unref (statement); + + statement = ephy_sqlite_connection_create_statement (connection, "BLAHBLAHBLAHBA", &error); + g_assert (!statement); + g_assert (error); + + ephy_sqlite_connection_close (connection); + g_unlink (temporary_file); + g_free (temporary_file); + + g_object_unref (connection); +} + +static void +create_table_and_insert_row (EphySQLiteConnection *connection) +{ + GError *error = NULL; + EphySQLiteStatement *statement = ephy_sqlite_connection_create_statement (connection, "CREATE TABLE test (id INTEGER, text LONGVARCHAR)", &error); + g_assert (statement); + g_assert (!error); + ephy_sqlite_statement_step (statement, &error); + g_assert (!error); + g_object_unref (statement); + + statement = ephy_sqlite_connection_create_statement (connection, "SELECT * FROM test", &error); + g_assert (statement); + g_assert (!error); + g_assert (!ephy_sqlite_statement_step (statement, &error)); + g_assert (!error); + g_object_unref (statement); + + statement = ephy_sqlite_connection_create_statement (connection, "INSERT INTO test (id, text) VALUES (3, \"test\")", &error); + g_assert (statement); + g_assert (!error); + ephy_sqlite_statement_step (statement, &error); + g_assert (!error); + g_object_unref (statement); + + statement = ephy_sqlite_connection_create_statement (connection, "SELECT * FROM test", &error); + g_assert (statement); + g_assert (!error); + + g_assert (ephy_sqlite_statement_step (statement, &error)); + g_assert (!error); + + g_assert_cmpint (ephy_sqlite_connection_get_last_insert_id (connection), ==, 1); + g_assert_cmpint (ephy_sqlite_statement_get_column_count (statement), ==, 2); + g_assert_cmpint (ephy_sqlite_statement_get_column_type (statement, 0), ==, EPHY_SQLITE_COLUMN_TYPE_INT); + g_assert_cmpint (ephy_sqlite_statement_get_column_type (statement, 1), ==, EPHY_SQLITE_COLUMN_TYPE_STRING); + + /* Step will return false here since there is only one row. */ + g_assert (!ephy_sqlite_statement_step (statement, &error)); + g_object_unref (statement); +} + +static void +test_create_table_and_insert_row (void) +{ + gchar *temporary_file = g_build_filename (g_get_tmp_dir (), "epiphany-sqlite-test.db", NULL); + EphySQLiteConnection* connection = ensure_empty_database (temporary_file); + + create_table_and_insert_row (connection); + + g_object_unref (connection); + g_unlink (temporary_file); + g_free (temporary_file); +} + +static void +test_bind_data (void) +{ + gchar *temporary_file = g_build_filename (g_get_tmp_dir (), "epiphany-sqlite-test.db", NULL); + EphySQLiteConnection* connection = ensure_empty_database (temporary_file); + GError *error = NULL; + EphySQLiteStatement *statement = NULL; + + ephy_sqlite_connection_execute (connection, "CREATE TABLE test (id INTEGER, text LONGVARCHAR)", &error); + + statement = ephy_sqlite_connection_create_statement (connection, "INSERT INTO test (id, text) VALUES (?, ?)", &error); + g_assert (statement); + g_assert (!error); + + g_assert (ephy_sqlite_statement_bind_int (statement, 0, 3, &error)); + g_assert (!error); + g_assert (ephy_sqlite_statement_bind_string (statement, 1, "foo", &error)); + g_assert (!error); + + /* Will return false since there are no resulting rows. */ + g_assert (!ephy_sqlite_statement_step (statement, &error)); + g_assert (!error); + g_object_unref (statement); + + statement = ephy_sqlite_connection_create_statement (connection, "SELECT * FROM test", &error); + g_assert (statement); + g_assert (!error); + g_assert (ephy_sqlite_statement_step (statement, &error)); + g_assert (!error); + g_assert_cmpint (ephy_sqlite_statement_get_column_count (statement), ==, 2); + g_assert_cmpint (ephy_sqlite_statement_get_column_as_int (statement, 0), ==, 3); + g_assert_cmpstr (ephy_sqlite_statement_get_column_as_string (statement, 1), ==, "foo"); + + g_object_unref (connection); + g_unlink (temporary_file); + g_free (temporary_file); +} + +static void +test_table_exists (void) +{ + gchar *temporary_file = g_build_filename (g_get_tmp_dir (), "epiphany-sqlite-test.db", NULL); + EphySQLiteConnection* connection = ensure_empty_database (temporary_file); + + g_assert (!ephy_sqlite_connection_table_exists (connection, "test")); + g_assert (!ephy_sqlite_connection_table_exists (connection, "something_fakey")); + create_table_and_insert_row (connection); + g_assert (ephy_sqlite_connection_table_exists (connection, "test")); + g_assert (!ephy_sqlite_connection_table_exists (connection, "something_fakey")); + + g_object_unref (connection); + g_unlink (temporary_file); + g_free (temporary_file); +} + +int +main (int argc, char *argv[]) +{ + gtk_test_init (&argc, &argv); + + g_test_add_func ("/lib/sqlite/ephy-sqlite/create_connection", test_create_connection); + g_test_add_func ("/lib/sqlite/ephy-sqlite/create_statement", test_create_statement); + g_test_add_func ("/lib/sqlite/ephy-sqlite/create_table_and_insert_row", test_create_table_and_insert_row); + g_test_add_func ("/lib/sqlite/ephy-sqlite/bind_data", test_bind_data); + g_test_add_func ("/lib/sqlite/ephy-sqlite/table_exists", test_table_exists); + + return g_test_run (); +} |