[tracker] libtracker-db: Support dynamic statement preparation



commit 67a4c236cbe27c359fe1968d0d10e8a88aba4cd1
Author: Jürg Billeter <j bitron ch>
Date:   Thu Apr 9 18:56:39 2009 +0200

    libtracker-db: Support dynamic statement preparation
---
 src/libtracker-db/tracker-db-interface-sqlite.c |  182 +++++++++++++++++++++++
 src/libtracker-db/tracker-db-interface-sqlite.h |   18 +++
 src/libtracker-db/tracker-db-interface.c        |   92 ++++++++++++
 src/libtracker-db/tracker-db-interface.h        |   46 ++++++
 4 files changed, 338 insertions(+), 0 deletions(-)

diff --git a/src/libtracker-db/tracker-db-interface-sqlite.c b/src/libtracker-db/tracker-db-interface-sqlite.c
index 112d69b..30a6d74 100644
--- a/src/libtracker-db/tracker-db-interface-sqlite.c
+++ b/src/libtracker-db/tracker-db-interface-sqlite.c
@@ -25,7 +25,10 @@
 
 #define TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_DB_INTERFACE_SQLITE, TrackerDBInterfaceSqlitePrivate))
 
+#define TRACKER_DB_STATEMENT_SQLITE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), TRACKER_TYPE_DB_STATEMENT_SQLITE, TrackerDBStatementSqlitePrivate))
+
 typedef struct TrackerDBInterfaceSqlitePrivate TrackerDBInterfaceSqlitePrivate;
+typedef struct TrackerDBStatementSqlitePrivate TrackerDBStatementSqlitePrivate;
 typedef struct SqliteFunctionData SqliteFunctionData;
 typedef struct SqliteAggregateData SqliteAggregateData;
 
@@ -33,6 +36,7 @@ struct TrackerDBInterfaceSqlitePrivate {
 	gchar *filename;
 	sqlite3 *db;
 
+	GHashTable *dynamic_statements;
 	GHashTable *statements;
 	GHashTable *procedures;
 
@@ -43,6 +47,11 @@ struct TrackerDBInterfaceSqlitePrivate {
 	guint ro : 1;
 };
 
+struct TrackerDBStatementSqlitePrivate {
+	TrackerDBInterfaceSqlite *db_interface;
+	sqlite3_stmt *stmt;
+};
+
 struct SqliteFunctionData {
 	TrackerDBInterface *interface;
 	TrackerDBFunc func;
@@ -57,6 +66,11 @@ struct SqliteAggregateData {
 };
 
 static void tracker_db_interface_sqlite_iface_init (TrackerDBInterfaceIface *iface);
+static void tracker_db_statement_sqlite_iface_init (TrackerDBStatementIface *iface);
+
+static TrackerDBStatementSqlite * tracker_db_statement_sqlite_new (TrackerDBInterfaceSqlite	*db_interface,
+								   sqlite3_stmt			*sqlite_stmt);
+static void tracker_db_statement_sqlite_reset (TrackerDBStatementSqlite *stmt);
 
 enum {
 	PROP_0,
@@ -69,6 +83,10 @@ G_DEFINE_TYPE_WITH_CODE (TrackerDBInterfaceSqlite, tracker_db_interface_sqlite,
 			 G_IMPLEMENT_INTERFACE (TRACKER_TYPE_DB_INTERFACE,
 						tracker_db_interface_sqlite_iface_init))
 
+G_DEFINE_TYPE_WITH_CODE (TrackerDBStatementSqlite, tracker_db_statement_sqlite, G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (TRACKER_TYPE_DB_STATEMENT,
+						tracker_db_statement_sqlite_iface_init))
+
 void 
 tracker_db_interface_sqlite_enable_shared_cache (void) 
 {
@@ -166,6 +184,8 @@ tracker_db_interface_sqlite_finalize (GObject *object)
 
 	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (object);
 
+	g_hash_table_destroy (priv->dynamic_statements);
+
 	g_hash_table_destroy (priv->statements);
 
 	if (priv->procedures) {
@@ -228,6 +248,9 @@ tracker_db_interface_sqlite_init (TrackerDBInterfaceSqlite *db_interface)
 	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (db_interface);
 
 	priv->ro = FALSE;
+	priv->dynamic_statements = g_hash_table_new_full (g_str_hash, g_str_equal,
+							  (GDestroyNotify) g_free,
+							  (GDestroyNotify) g_object_unref);
 	priv->statements = g_hash_table_new_full (g_str_hash, g_str_equal,
 						  (GDestroyNotify) g_free,
 						  (GDestroyNotify) sqlite3_finalize);
@@ -362,6 +385,36 @@ internal_sqlite3_function (sqlite3_context *context,
 	g_free (values);
 }
 
+static TrackerDBStatement *
+tracker_db_interface_sqlite_create_statement (TrackerDBInterface *db_interface,
+					      const gchar	 *query)
+{
+	TrackerDBInterfaceSqlitePrivate *priv;
+	TrackerDBStatementSqlite *stmt;
+
+	priv = TRACKER_DB_INTERFACE_SQLITE_GET_PRIVATE (db_interface);
+
+	stmt = g_hash_table_lookup (priv->dynamic_statements, query);
+
+	if (!stmt) {
+		sqlite3_stmt *sqlite_stmt;
+
+	        g_debug ("Preparing query: '%s'", query);
+
+		if (sqlite3_prepare_v2 (priv->db, query, -1, &sqlite_stmt, NULL) != SQLITE_OK) {
+			g_critical ("Unable to prepare query '%s': %s", query, sqlite3_errmsg (priv->db));
+			return NULL;
+		}
+
+		stmt = tracker_db_statement_sqlite_new (TRACKER_DB_INTERFACE_SQLITE (db_interface), sqlite_stmt);
+		g_hash_table_insert (priv->dynamic_statements, g_strdup (query), stmt);
+	} else {
+		tracker_db_statement_sqlite_reset (stmt);
+	}
+
+	return g_object_ref (stmt);
+}
+
 static void
 internal_sqlite3_aggregate_step (sqlite3_context *context,
 				 int		    argc,
@@ -721,6 +774,7 @@ tracker_db_interface_sqlite_iface_init (TrackerDBInterfaceIface *iface)
 	iface->set_procedure_table = tracker_db_interface_sqlite_set_procedure_table;
 	iface->execute_procedure = tracker_db_interface_sqlite_execute_procedure;
 	iface->execute_procedure_len = tracker_db_interface_sqlite_execute_procedure_len;
+	iface->create_statement = tracker_db_interface_sqlite_create_statement;
 	iface->execute_query = tracker_db_interface_sqlite_execute_query;
 }
 
@@ -829,3 +883,131 @@ tracker_db_interface_sqlite_get_last_insert_id (TrackerDBInterfaceSqlite *interf
 
 	return (gint64) sqlite3_last_insert_rowid (priv->db);
 }
+
+static void
+tracker_db_statement_sqlite_finalize (GObject *object)
+{
+	TrackerDBStatementSqlitePrivate *priv;
+
+	priv = TRACKER_DB_STATEMENT_SQLITE_GET_PRIVATE (object);
+
+	sqlite3_finalize (priv->stmt);
+
+	G_OBJECT_CLASS (tracker_db_statement_sqlite_parent_class)->finalize (object);
+}
+
+static void
+tracker_db_statement_sqlite_class_init (TrackerDBStatementSqliteClass *class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+	object_class->finalize = tracker_db_statement_sqlite_finalize;
+
+	g_type_class_add_private (object_class,
+				  sizeof (TrackerDBStatementSqlitePrivate));
+}
+
+static TrackerDBStatementSqlite *
+tracker_db_statement_sqlite_new (TrackerDBInterfaceSqlite	*db_interface,
+				 sqlite3_stmt			*sqlite_stmt)
+{
+	TrackerDBStatementSqlite *stmt;
+	TrackerDBStatementSqlitePrivate *priv;
+
+	stmt = g_object_new (TRACKER_TYPE_DB_STATEMENT_SQLITE, NULL);
+
+	priv = TRACKER_DB_STATEMENT_SQLITE_GET_PRIVATE (stmt);
+
+	priv->db_interface = db_interface;
+	priv->stmt = sqlite_stmt;
+
+	return stmt;
+}
+
+static void
+tracker_db_statement_sqlite_bind_double (TrackerDBStatement	 *stmt,
+					 int			  index,
+					 double			  value)
+{
+	TrackerDBStatementSqlitePrivate *priv;
+
+	priv = TRACKER_DB_STATEMENT_SQLITE_GET_PRIVATE (stmt);
+
+	sqlite3_bind_double (priv->stmt, index + 1, value);
+}
+
+static void
+tracker_db_statement_sqlite_bind_int (TrackerDBStatement	 *stmt,
+				      int			  index,
+				      int			  value)
+{
+	TrackerDBStatementSqlitePrivate *priv;
+
+	priv = TRACKER_DB_STATEMENT_SQLITE_GET_PRIVATE (stmt);
+
+	sqlite3_bind_int (priv->stmt, index + 1, value);
+}
+
+
+
+static void
+tracker_db_statement_sqlite_bind_int64 (TrackerDBStatement	 *stmt,
+				         int			  index,
+				         gint64			  value)
+{
+	TrackerDBStatementSqlitePrivate *priv;
+
+	priv = TRACKER_DB_STATEMENT_SQLITE_GET_PRIVATE (stmt);
+
+	sqlite3_bind_int64 (priv->stmt, index + 1, value);
+}
+
+static void
+tracker_db_statement_sqlite_bind_text (TrackerDBStatement	 *stmt,
+				       int			  index,
+				       const gchar		 *value)
+{
+	TrackerDBStatementSqlitePrivate *priv;
+
+	priv = TRACKER_DB_STATEMENT_SQLITE_GET_PRIVATE (stmt);
+
+	sqlite3_bind_text (priv->stmt, index + 1, value, -1, SQLITE_TRANSIENT);
+}
+
+static TrackerDBResultSet *
+tracker_db_statement_sqlite_execute (TrackerDBStatement		 *stmt,
+				     GError			**error)
+{
+	TrackerDBStatementSqlitePrivate *priv;
+
+	priv = TRACKER_DB_STATEMENT_SQLITE_GET_PRIVATE (stmt);
+
+	return create_result_set_from_stmt (priv->db_interface, priv->stmt, error);
+}
+
+static void
+tracker_db_statement_sqlite_iface_init (TrackerDBStatementIface *iface)
+{
+	iface->bind_double = tracker_db_statement_sqlite_bind_double;
+	iface->bind_int = tracker_db_statement_sqlite_bind_int;
+	iface->bind_int64 = tracker_db_statement_sqlite_bind_int64;
+	iface->bind_text = tracker_db_statement_sqlite_bind_text;
+	iface->execute = tracker_db_statement_sqlite_execute;
+}
+
+static void
+tracker_db_statement_sqlite_init (TrackerDBStatementSqlite *stmt)
+{
+}
+
+static void
+tracker_db_statement_sqlite_reset (TrackerDBStatementSqlite *stmt)
+{
+	TrackerDBStatementSqlitePrivate *priv;
+
+	priv = TRACKER_DB_STATEMENT_SQLITE_GET_PRIVATE (stmt);
+
+	sqlite3_reset (priv->stmt);
+	sqlite3_clear_bindings (priv->stmt);
+}
+
diff --git a/src/libtracker-db/tracker-db-interface-sqlite.h b/src/libtracker-db/tracker-db-interface-sqlite.h
index a1c9cae..8ef3ace 100644
--- a/src/libtracker-db/tracker-db-interface-sqlite.h
+++ b/src/libtracker-db/tracker-db-interface-sqlite.h
@@ -31,8 +31,17 @@ G_BEGIN_DECLS
 #define TRACKER_IS_DB_INTERFACE_SQLITE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((o),    TRACKER_TYPE_DB_INTERFACE_SQLITE))
 #define TRACKER_DB_INTERFACE_SQLITE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),  TRACKER_TYPE_DB_INTERFACE_SQLITE, TrackerDBInterfaceSqliteClass))
 
+#define TRACKER_TYPE_DB_STATEMENT_SQLITE	 (tracker_db_statement_sqlite_get_type ())
+#define TRACKER_DB_STATEMENT_SQLITE(o)		 (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_DB_STATEMENT_SQLITE, TrackerDBStatementSqlite))
+#define TRACKER_DB_STATEMENT_SQLITE_CLASS(c)	 (G_TYPE_CHECK_CLASS_CAST ((c),    TRACKER_TYPE_DB_STATEMENT_SQLITE, TrackerDBStatementSqliteClass))
+#define TRACKER_IS_DB_STATEMENT_SQLITE(o)	 (G_TYPE_CHECK_INSTANCE_TYPE ((o), TRACKER_TYPE_DB_STATEMENT_SQLITE))
+#define TRACKER_IS_DB_STATEMENT_SQLITE_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((o),    TRACKER_TYPE_DB_STATEMENT_SQLITE))
+#define TRACKER_DB_STATEMENT_SQLITE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),  TRACKER_TYPE_DB_STATEMENT_SQLITE, TrackerDBStatementSqliteClass))
+
 typedef struct TrackerDBInterfaceSqlite      TrackerDBInterfaceSqlite;
 typedef struct TrackerDBInterfaceSqliteClass TrackerDBInterfaceSqliteClass;
+typedef struct TrackerDBStatementSqlite      TrackerDBStatementSqlite;
+typedef struct TrackerDBStatementSqliteClass TrackerDBStatementSqliteClass;
 
 typedef gint (* TrackerDBCollationFunc) (gchar *str1,
 					 gint	len1,
@@ -58,7 +67,16 @@ struct TrackerDBInterfaceSqliteClass {
 	GObjectClass parent_class;
 };
 
+struct TrackerDBStatementSqlite {
+	GObject parent_instance;
+};
+
+struct TrackerDBStatementSqliteClass {
+	GObjectClass parent_class;
+};
+
 GType tracker_db_interface_sqlite_get_type (void);
+GType tracker_db_statement_sqlite_get_type (void);
 
 TrackerDBInterface * tracker_db_interface_sqlite_new (const gchar *filename);
 TrackerDBInterface * tracker_db_interface_sqlite_new_ro (const gchar *filename);
diff --git a/src/libtracker-db/tracker-db-interface.c b/src/libtracker-db/tracker-db-interface.c
index 344bf12..26d46d7 100644
--- a/src/libtracker-db/tracker-db-interface.c
+++ b/src/libtracker-db/tracker-db-interface.c
@@ -77,6 +77,24 @@ tracker_db_interface_get_type (void)
 	return type;
 }
 
+GType
+tracker_db_statement_get_type (void)
+{
+	static GType type = 0;
+
+	if (G_UNLIKELY (type == 0)) {
+		type = g_type_register_static_simple (G_TYPE_INTERFACE,
+						      "TrackerDBStatement",
+						      sizeof (TrackerDBStatementIface),
+						      NULL,
+						      0, NULL, 0);
+
+		g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
+	}
+
+	return type;
+}
+
 /* Boxed type for blobs */
 static gpointer
 blob_copy (gpointer boxed)
@@ -233,6 +251,26 @@ ensure_result_set_state (TrackerDBResultSet *result_set)
 	return result_set;
 }
 
+TrackerDBStatement *
+tracker_db_interface_create_statement (TrackerDBInterface  *interface,
+				       const gchar	   *query,
+				       ...)
+{
+	va_list args;
+	gchar *str;
+
+	g_return_val_if_fail (TRACKER_IS_DB_INTERFACE (interface), NULL);
+	g_return_val_if_fail (query != NULL, NULL);
+
+	va_start (args, query);
+	str = g_strdup_vprintf (query, args);
+	va_end (args);
+
+	return TRACKER_DB_INTERFACE_GET_IFACE (interface)->create_statement (interface,
+									     str);
+}
+
+
 TrackerDBResultSet *
 tracker_db_interface_execute_vquery (TrackerDBInterface  *interface,
 				     GError		**error,
@@ -439,6 +477,60 @@ tracker_db_interface_end_transaction (TrackerDBInterface *interface)
 	return TRUE;
 }
 
+void
+tracker_db_statement_bind_double (TrackerDBStatement	*stmt,
+				  int			 index,
+				  double		 value)
+{
+	g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
+
+	TRACKER_DB_STATEMENT_GET_IFACE (stmt)->bind_double (stmt, index, value);
+}
+
+void
+tracker_db_statement_bind_int (TrackerDBStatement	*stmt,
+			       int			 index,
+			       int			 value)
+{
+	g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
+
+	TRACKER_DB_STATEMENT_GET_IFACE (stmt)->bind_int (stmt, index, value);
+}
+
+
+void
+tracker_db_statement_bind_int64 (TrackerDBStatement	*stmt,
+			         int			 index,
+			         gint64			 value)
+{
+	g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
+
+	TRACKER_DB_STATEMENT_GET_IFACE (stmt)->bind_int64 (stmt, index, value);
+}
+
+void
+tracker_db_statement_bind_text (TrackerDBStatement	*stmt,
+				int			 index,
+				const gchar		*value)
+{
+	g_return_if_fail (TRACKER_IS_DB_STATEMENT (stmt));
+
+	TRACKER_DB_STATEMENT_GET_IFACE (stmt)->bind_text (stmt, index, value);
+}
+
+TrackerDBResultSet *
+tracker_db_statement_execute (TrackerDBStatement	 *stmt,
+			      GError			**error)
+{
+	TrackerDBResultSet *result_set;
+
+	g_return_val_if_fail (TRACKER_IS_DB_STATEMENT (stmt), NULL);
+
+	result_set = TRACKER_DB_STATEMENT_GET_IFACE (stmt)->execute (stmt, error);
+
+	return ensure_result_set_state (result_set);
+}
+
 /* TrackerDBResultSet semiprivate API */
 TrackerDBResultSet *
 _tracker_db_result_set_new (guint columns)
diff --git a/src/libtracker-db/tracker-db-interface.h b/src/libtracker-db/tracker-db-interface.h
index 14964ba..230c3ba 100644
--- a/src/libtracker-db/tracker-db-interface.h
+++ b/src/libtracker-db/tracker-db-interface.h
@@ -30,6 +30,11 @@ G_BEGIN_DECLS
 #define TRACKER_IS_DB_INTERFACE(obj)	    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRACKER_TYPE_DB_INTERFACE))
 #define TRACKER_DB_INTERFACE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TRACKER_TYPE_DB_INTERFACE, TrackerDBInterfaceIface))
 
+#define TRACKER_TYPE_DB_STATEMENT	    (tracker_db_statement_get_type ())
+#define TRACKER_DB_STATEMENT(obj)	    (G_TYPE_CHECK_INSTANCE_CAST ((obj), TRACKER_TYPE_DB_STATEMENT, TrackerDBStatement))
+#define TRACKER_IS_DB_STATEMENT(obj)	    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TRACKER_TYPE_DB_STATEMENT))
+#define TRACKER_DB_STATEMENT_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), TRACKER_TYPE_DB_STATEMENT, TrackerDBStatementIface))
+
 #define TRACKER_TYPE_DB_RESULT_SET	    (tracker_db_result_set_get_type ())
 #define TRACKER_DB_RESULT_SET(o)	    (G_TYPE_CHECK_INSTANCE_CAST ((o), TRACKER_TYPE_DB_RESULT_SET, TrackerDbResultSet))
 #define TRACKER_DB_RESULT_SET_CLASS(c)	    (G_TYPE_CHECK_CLASS_CAST ((c),    TRACKER_TYPE_DB_RESULT_SET, TrackerDbResultSetClass))
@@ -48,6 +53,8 @@ typedef enum {
 
 typedef struct TrackerDBInterface TrackerDBInterface;
 typedef struct TrackerDBInterfaceIface TrackerDBInterfaceIface;
+typedef struct TrackerDBStatement TrackerDBStatement;
+typedef struct TrackerDBStatementIface TrackerDBStatementIface;
 typedef struct TrackerDBResultSet TrackerDBResultSet;
 typedef struct TrackerDBResultSetClass TrackerDBResultSetClass;
 
@@ -64,12 +71,33 @@ struct TrackerDBInterfaceIface {
 							GError		   **error,
 							const gchar	    *procedure,
 							va_list		     args);
+	TrackerDBStatement * (* create_statement)      (TrackerDBInterface  *interface,
+							const gchar	    *query);
 	TrackerDBResultSet * (* execute_query)	       (TrackerDBInterface  *interface,
 							GError		   **error,
 							const gchar	    *query);
 
 };
 
+struct TrackerDBStatementIface {
+	GTypeInterface iface;
+
+	void		     (* bind_double)	(TrackerDBStatement	 *stmt,
+						 int			  index,
+						 double			  value);
+	void		     (* bind_int)	(TrackerDBStatement	 *stmt,
+						 int			  index,
+						 int			  value);
+	void		     (* bind_text)	(TrackerDBStatement	 *stmt,
+						 int			  index,
+						 const gchar		 *value);
+	TrackerDBResultSet * (* execute)	(TrackerDBStatement	 *stmt,
+						 GError			**error);
+	void		     (* bind_int64)	(TrackerDBStatement	 *stmt,
+						 int			  index,
+						 gint64			  value);
+};
+
 struct TrackerDBResultSet {
 	GObject parent_class;
 };
@@ -82,11 +110,15 @@ struct TrackerDBResultSetClass {
 GQuark tracker_db_interface_error_quark (void);
 
 GType tracker_db_interface_get_type (void);
+GType tracker_db_statement_get_type (void);
 GType tracker_db_result_set_get_type (void);
 GType tracker_db_blob_get_type (void);
 
 
 /* Functions to create queries/procedures */
+TrackerDBStatement *	tracker_db_interface_create_statement	 (TrackerDBInterface   *interface,
+								  const gchar	       *query,
+								  ...) G_GNUC_PRINTF (2, 3);
 TrackerDBResultSet *	tracker_db_interface_execute_vquery	 (TrackerDBInterface   *interface,
 								  GError	     **error,
 								  const gchar	       *query,
@@ -118,6 +150,20 @@ TrackerDBResultSet *	tracker_db_interface_execute_procedure_len  (TrackerDBInter
 gboolean		tracker_db_interface_start_transaction	    (TrackerDBInterface   *interface);
 gboolean		tracker_db_interface_end_transaction	    (TrackerDBInterface   *interface);
 
+void			tracker_db_statement_bind_double	(TrackerDBStatement	 *stmt,
+								 int			  index,
+								 double			  value);
+void			tracker_db_statement_bind_int		(TrackerDBStatement	 *stmt,
+								 int			  index,
+								 int			  value);
+void			tracker_db_statement_bind_int64		(TrackerDBStatement	 *stmt,
+								 int			  index,
+								 gint64			  value);
+void			tracker_db_statement_bind_text		(TrackerDBStatement	 *stmt,
+								 int			  index,
+								 const gchar		 *value);
+TrackerDBResultSet *	tracker_db_statement_execute		(TrackerDBStatement	 *stmt,
+								 GError			**error);
 
 /* Semi private TrackerDBResultSet functions */
 TrackerDBResultSet *	  _tracker_db_result_set_new	       (guint		    cols);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]