[darwinbuild-changes] [689] branches/PR-7489777/darwinup

source_changes at macosforge.org source_changes at macosforge.org
Thu Feb 4 15:44:09 PST 2010


Revision: 689
          http://trac.macosforge.org/projects/darwinbuild/changeset/689
Author:   wsiegrist at apple.com
Date:     2010-02-04 15:44:08 -0800 (Thu, 04 Feb 2010)
Log Message:
-----------
Implement get_value for single value SELECTs and cache statements with libcache.

Modified Paths:
--------------
    branches/PR-7489777/darwinup/DB.cpp
    branches/PR-7489777/darwinup/DB.h
    branches/PR-7489777/darwinup/Database.cpp
    branches/PR-7489777/darwinup/Database.h
    branches/PR-7489777/darwinup/Depot.cpp
    branches/PR-7489777/darwinup/Depot.h
    branches/PR-7489777/darwinup/Table.cpp
    branches/PR-7489777/darwinup/Table.h
    branches/PR-7489777/darwinup/main.cpp

Modified: branches/PR-7489777/darwinup/DB.cpp
===================================================================
--- branches/PR-7489777/darwinup/DB.cpp	2010-02-03 19:28:01 UTC (rev 688)
+++ branches/PR-7489777/darwinup/DB.cpp	2010-02-04 23:44:08 UTC (rev 689)
@@ -46,6 +46,8 @@
 }
 
 void DarwinupDatabase::init_schema() {
+	// XXX: use macros to make this cleaner
+	
 	this->m_archives_table = new Table("archives");
 	//                                                                           index  pk     unique
 	assert(m_archives_table->add_column(new Column("serial",     TYPE_INTEGER,   false, true,  false)));
@@ -70,6 +72,8 @@
 	assert(this->add_table(this->m_files_table));	
 }
 
+
+
 uint64_t DarwinupDatabase::insert_archive(uuid_t uuid, uint32_t info, const char* name, time_t date_added) {
 
 	bool res = this->insert(this->m_archives_table,
@@ -88,8 +92,42 @@
 	return this->last_insert_id();
 }
 										  
+bool DarwinupDatabase::update_file(Archive* archive, const char* path, uint32_t info, mode_t mode, 
+								   uid_t uid, gid_t gid, Digest* digest) {
+
+	bool res = false;
+	
+	// get the serial for the file where archive and path match
+	uint64_t serial;
+	res = this->get_value("file_serial__archive_path",
+						  (void**)&serial,
+						  this->m_files_table,
+						  this->m_files_table->column(0), // serial
+						  2,                              // number of where conditions
+						  this->m_files_table->column(1), // archive
+						  (uint64_t)archive->serial(),
+						  this->m_files_table->column(8), // path
+						  path);
+									  
+	// update the information
+	res = this->update(this->m_files_table, serial,
+					   (uint64_t)archive->serial(),
+					   (uint64_t)info,
+					   (uint64_t)mode,
+					   (uint64_t)uid,
+					   (uint64_t)gid,
+					   (uint64_t)0, 
+					   (uint8_t*)(digest ? digest->data() : NULL), 
+					   (uint32_t)(digest ? digest->size() : 0), 
+					   path);
+	if (!res) {
+		fprintf(stderr, "Error: unable to update file with serial %llu and path %s: %s \n",
+				serial, path, this->error());
+	}
+	
+	return res;
+}
 										  
-										  
 uint64_t DarwinupDatabase::insert_file(uint32_t info, mode_t mode, uid_t uid, gid_t gid, 
 									   Digest* digest, Archive* archive, const char* path) {
 	

Modified: branches/PR-7489777/darwinup/DB.h
===================================================================
--- branches/PR-7489777/darwinup/DB.h	2010-02-03 19:28:01 UTC (rev 688)
+++ branches/PR-7489777/darwinup/DB.h	2010-02-04 23:44:08 UTC (rev 689)
@@ -59,6 +59,8 @@
 	
 	// inserts into tables, returns serial from primary key
 	uint64_t insert_archive(uuid_t uuid, uint32_t info, const char* name, time_t date);
+	bool update_file(Archive* archive, const char* path, uint32_t info, mode_t mode, 
+					 uid_t uid, gid_t gid, Digest* digest);
 	uint64_t insert_file(uint32_t info, mode_t mode, uid_t uid, gid_t gid, 
 						 Digest* digest, Archive* archive, const char* path);
 	

Modified: branches/PR-7489777/darwinup/Database.cpp
===================================================================
--- branches/PR-7489777/darwinup/Database.cpp	2010-02-03 19:28:01 UTC (rev 688)
+++ branches/PR-7489777/darwinup/Database.cpp	2010-02-04 23:44:08 UTC (rev 689)
@@ -30,18 +30,13 @@
  * @APPLE_BSD_LICENSE_HEADER_END@
  */
 
-#include <assert.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdarg.h>
-#include <stdlib.h>
 #include "Database.h"
 
 /**
  * sqlite3_trace callback for debugging
  */
 void dbtrace(void* context, const char* sql) {
-	fprintf(stderr, "[TRACE] %s \n", sql);
+	IF_DEBUG("[TRACE] %s \n", sql);
 }
 
 Database::Database() {
@@ -49,6 +44,7 @@
 	m_table_max = 1;
 	m_table_count = 0;
 	m_tables = (Table**)malloc(sizeof(Table*) * m_table_max);
+	this->init_cache();
 	m_db = NULL;	
 	m_path = NULL;
 	m_error_size = 1024;
@@ -59,6 +55,7 @@
 	m_table_max = 1;
 	m_table_count = 0;
 	m_tables = (Table**)malloc(sizeof(Table*) * m_table_max);
+	this->init_cache();
 	m_db = NULL;		
 	m_path = strdup(path);
 	if (!m_path) {
@@ -72,15 +69,55 @@
 	for (uint32_t i = 0; i < m_table_count; i++) {
 		delete m_tables[i];
 	}
+	this->destroy_cache();
 	free(m_tables);
 	free(m_path);
 	free(m_error);
 }
 
+
 void Database::init_schema() {
 	// do nothing... children should implement this
 }
 
+
+void Database::init_cache() {
+	cache_attributes_t attrs;
+	attrs.version = CACHE_ATTRIBUTES_VERSION_2;
+	attrs.key_hash_cb = cache_key_hash_cb_cstring;
+	attrs.key_is_equal_cb = cache_key_is_equal_cb_cstring;
+	attrs.key_retain_cb = cache_key_retain;
+	attrs.key_release_cb = cache_key_release;
+	attrs.value_release_cb = cache_value_release;
+	attrs.value_retain_cb = cache_value_retain;
+	cache_create("org.macosforge.darwinbuild.darwinup.statements", &attrs, &m_statement_cache);
+}
+
+void Database::destroy_cache() {
+	cache_destroy(m_statement_cache);
+}
+
+void cache_key_retain(void* key_in, void** key_out, void* user_data) {
+	fprintf(stderr, "CACHE: key_retain %s\n", (char*)key_in);
+	*key_out = strdup((char*)key_in);
+}
+
+void cache_key_release(void* key, void* user_data) {
+	fprintf(stderr, "CACHE: key_release %s\n", (char*)key);
+	free(key);
+}
+
+void cache_value_retain(void* value, void* user_data) {
+	fprintf(stderr, "CACHE: value_retain %p\n", value);
+	// do nothing
+}
+
+void cache_value_release(void* value, void* user_data) {
+	fprintf(stderr, "CACHE: value_release %p\n", value);
+	sqlite3_finalize((sqlite3_stmt*)value);
+}
+
+
 const char* Database::path() {
 	return m_path;
 }
@@ -99,6 +136,7 @@
 		fprintf(stderr, "Error: unable to connect to database at: %s \n", m_path);
 		return false;
 	}	
+	sqlite3_trace(m_db, dbtrace, NULL);
 	if (this->empty()) {
 		assert(this->create_tables());
 	}	
@@ -152,15 +190,6 @@
 	return res==SQLITE_OK;
 }
 
-
-bool Database::update(Table* table, Column* column, const char* value, const char* where, 
-					  uint32_t &count) {
-	// not implemented
-	assert(false);
-	return false;
-}
-
-
 #define __SQL(callback, context, fmt) \
     va_list args; \
     va_start(args, fmt); \
@@ -190,8 +219,157 @@
 #undef __SQL
 
 
+#define __bind_all_columns(_lastarg) \
+    va_list args; \
+    va_start(args, _lastarg); \
+	for (uint32_t i=0; i<table->column_count(); i++) { \
+		Column* col = table->column(i); \
+		if (col->is_pk()) continue; \
+		uint8_t* bdata = NULL; \
+		uint32_t bsize = 0; \
+		switch(col->type()) { \
+			case TYPE_INTEGER: \
+				res = sqlite3_bind_int64(stmt, param++, va_arg(args, uint64_t)); \
+				break; \
+			case TYPE_TEXT: \
+				res = sqlite3_bind_text(stmt, param++, va_arg(args, char*), -1, SQLITE_STATIC); \
+				break; \
+			case TYPE_BLOB: \
+				bdata = va_arg(args, uint8_t*); \
+				bsize = va_arg(args, uint32_t); \
+				res = sqlite3_bind_blob(stmt, param++, \
+										bdata, \
+										bsize, \
+										SQLITE_STATIC); \
+				break; \
+		} \
+		if (res != SQLITE_OK) { \
+			fprintf(stderr, "Error: failed to bind parameter #%d with column #%d of type %d " \
+					"table %s \n", \
+					param, i, col->type(), table->name()); \
+			return false; \
+		} \
+	} \
+    va_end(args);
+
+#define __bind_va_columns(_lastarg) \
+    va_list args; \
+    va_start(args, _lastarg); \
+    for (uint32_t i=0; i<count; i++) { \
+        Column* col = va_arg(args, Column*); \
+        fprintf(stderr, "DEBUG: got a column from va_arg: %p \n", col); \
+        uint8_t* bdata = NULL; \
+        uint32_t bsize = 0; \
+        switch(col->type()) { \
+            case TYPE_INTEGER: \
+                fprintf(stderr, "DEBUG: param %d is integer\n", param); \
+                res = sqlite3_bind_int64(stmt, param++, va_arg(args, uint64_t)); \
+                break; \
+            case TYPE_TEXT: \
+                fprintf(stderr, "DEBUG: param %d is text\n", param); \
+                res = sqlite3_bind_text(stmt, param++, va_arg(args, char*), -1, SQLITE_STATIC); \
+                break; \
+            case TYPE_BLOB: \
+                fprintf(stderr, "DEBUG: param %d is blob\n", param); \
+                bdata = va_arg(args, uint8_t*); \
+                bsize = va_arg(args, uint32_t); \
+                res = sqlite3_bind_blob(stmt, param++, \
+                                        bdata, \
+										bsize, \
+                                        SQLITE_STATIC); \
+                break; \
+		} \
+        if (res != SQLITE_OK) { \
+            fprintf(stderr, "Error: failed to bind parameter #%d with column #%d of type %d " \
+                            "table %s \n", \
+							param, i, col->type(), table->name()); \
+            return false; \
+        } \
+    } \
+    va_end(args);
+
+int Database::execute(sqlite3_stmt* stmt) {
+	int res = sqlite3_step(stmt);
+	if (res == SQLITE_DONE) res = SQLITE_OK;
+	sqlite3_reset(stmt);
+	return res;
+}
+
+
+bool Database::get_value(const char* name, void** output, Table* table, Column* value_column, 
+						 uint32_t count, ...) {
+	sqlite3_stmt* stmt;
+	char* key = strdup(name);
+	cache_get_and_retain(m_statement_cache, key, (void**)&stmt);
+	if (!stmt) {
+		// did not have stmt cached, so we need to generate it
+		fprintf(stderr, "DEBUG: get_value is generating query for %s \n", key);
+		va_list args;
+		va_start(args, count);
+		stmt = table->get_value(m_db, value_column, count, args);
+		va_end(args);
+		cache_set_and_retain(m_statement_cache, key, stmt, sizeof(stmt));
+	} else {
+		fprintf(stderr, "DEBUG: found get_value query for %s in cache: %p \n", key, stmt);
+		fprintf(stderr, "DEBUG: query is: %s \n", sqlite3_sql(stmt));
+	}
+	
+	int res = SQLITE_OK;
+	uint32_t param = 1;
+	__bind_va_columns(count);
+	
+	res = sqlite3_step(stmt);
+	if (res == SQLITE_ROW) {
+		switch(value_column->type()) {
+			case TYPE_INTEGER:
+				*(uint64_t*)output = sqlite3_column_int64(stmt, 0);
+				break;
+			case TYPE_TEXT:
+				*(const unsigned char**)output = sqlite3_column_text(stmt, 0);
+				break;
+			case TYPE_BLOB:
+				*(const void**)output = sqlite3_column_blob(stmt, 0);
+				break;
+		}
+	}
+	sqlite3_reset(stmt);
+	cache_release_value(m_statement_cache, &stmt);
+	free(key);
+	return output != NULL;
+}
+
 /**
  * Given a table and an arg list in the same order as Table::add_column() calls,
+ * binds and executes a sql update. The Table is responsible for preparing the
+ * statement in Table::update()
+ *
+ * All integer args must be cast to uint64_t
+ * All blob columns must provide 2 args in the list. The first arg is a uint8_t* of data
+ * and then the uint32_t value for size of the data. 
+ *
+ */
+bool Database::update(Table* table, uint64_t pkvalue, ...) {
+	int res = SQLITE_OK;
+	
+	// get the prepared statement
+	sqlite3_stmt* stmt = table->update(m_db);
+	if (!stmt) {
+		fprintf(stderr, "Error: %s table gave a NULL statement when trying to update.\n", table->name());
+		return false;
+	}
+	
+	uint32_t param = 1; // counter to track placeholders in sql statement
+	__bind_all_columns(pkvalue);
+	
+	// bind the primary key in the WHERE clause
+	res = sqlite3_bind_int64(stmt, param++, pkvalue);
+	res = this->execute(stmt);
+	return res == SQLITE_OK;
+}
+
+
+/**
+ * Given a table and an arg list in the same order as Table::add_column() calls,
  * binds and executes a sql insertion. The Table is responsible for preparing the
  * statement in Table::insert()
  *
@@ -202,8 +380,6 @@
  */
 bool Database::insert(Table* table, ...) {
 	int res = SQLITE_OK;
-	va_list args;
-	va_start(args, table);
 
 	// get the prepared statement
 	sqlite3_stmt* stmt = table->insert(m_db);
@@ -213,52 +389,13 @@
 	}
 	
 	uint32_t param = 1; // counter to track placeholders in sql statement
-	
-	for (uint32_t i=0; i<table->column_count(); i++) {
-		Column* col = table->column(i);
-		
-		// primary keys do not get inserted
-		if (col->is_pk()) continue;
-
-		// temp variable for blob columns
-		uint8_t* bdata = NULL;
-		uint32_t bsize = 0;
-		
-		switch(col->type()) {
-			case SQLITE_INTEGER:
-				res = sqlite3_bind_int64(stmt, param++, va_arg(args, uint64_t));
-				break;
-			case SQLITE_TEXT:
-				res = sqlite3_bind_text(stmt, param++, va_arg(args, char*), -1, SQLITE_STATIC);
-				break;
-			case SQLITE_BLOB:
-				bdata = va_arg(args, uint8_t*);
-				bsize = va_arg(args, uint32_t);
-				res = sqlite3_bind_blob(stmt, param++, 
-										bdata, 
-										bsize, 
-										SQLITE_STATIC);
-				break;
-				
-		}
-		if (res != SQLITE_OK) {
-			fprintf(stderr, "Error: failed to bind parameter #%d with column #%d of type %d when inserting "
-					        "to table %s \n",
-					param, i, col->type(), table->name());
-			return false;
-		}
-	}
-
-	sqlite3_trace(m_db, dbtrace, NULL);
-	
-	res = sqlite3_step(stmt);
-	if (res == SQLITE_DONE) res = SQLITE_OK;
-	sqlite3_reset(stmt);
-	
-	va_end(args);
+	__bind_all_columns(table);
+	res = this->execute(stmt);
 	return res == SQLITE_OK;
 }
 
+#undef __bind_params
+
 uint64_t Database::last_insert_id() {
 	return (uint64_t)sqlite3_last_insert_rowid(m_db);
 }

Modified: branches/PR-7489777/darwinup/Database.h
===================================================================
--- branches/PR-7489777/darwinup/Database.h	2010-02-03 19:28:01 UTC (rev 688)
+++ branches/PR-7489777/darwinup/Database.h	2010-02-04 23:44:08 UTC (rev 689)
@@ -33,13 +33,27 @@
 #ifndef _DATABASE_H
 #define _DATABASE_H
 
+#include <assert.h>
+#include <cache.h>
+#include <cache_callbacks.h>
+#include <sqlite3.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
 
-#include <stdint.h>
-#include <sqlite3.h>
 #include "Table.h"
 #include "Digest.h"
 #include "Archive.h"
 
+// libcache callbacks
+void cache_key_retain(void* key_in, void** key_out, void* user_data);
+void cache_key_release(void* key, void* user_data);
+void cache_value_retain(void* value, void* user_data);
+void cache_value_release(void* value, void* user_data);
+
+
 /**
  * 
  * Generic sqlite abstraction
@@ -60,25 +74,42 @@
 	bool connect();
 	bool connect(const char* path);
 	
-	uint64_t last_insert_id();
 	
-	const char* get_value(Table* table, Column* column, const char* where);
+	bool begin_transaction();
+	bool rollback_transaction();
+	bool commit_transaction();
+	
+
 	const char* get_row(Table* table, const char* where);
 	const char* get_column(Table* table, Column* column, const char* where);
 	const char* get_all(Table* table, const char* where);
 	
 	uint32_t count(Table* table, const char* where);
 	
-	bool update(Table* table, Column* column, const char* value, const char* where,
-				uint32_t &count);
 	bool del(Table* table, const char* where, uint32_t &count);
-	bool insert(Table* table, ...);
 	
-	bool begin_transaction();
-	bool rollback_transaction();
-	bool commit_transaction();
+	bool get_value(void* value, Table* table, Column* value_column, ...);
 	
+	
+	
+	/**
+	 * SELECT statement caching and execution
+	 *
+	 *  name is a string key that labels the query for caching purposes (63 char max)
+	 *  output is where we will store the value requested
+	 *  table and value_column are what value we'll give back
+	 *
+	 *  everything else are Column*,value pairs for making a WHERE clause
+	 *
+	 */
+	bool get_value(const char* name, void** output, Table* table, Column* value_column, uint32_t count, ...);
+
+	
+	bool update(Table* table, uint64_t pkvalue, ...);
+	bool insert(Table* table, ...);
+
 	bool add_table(Table*);
+	uint64_t last_insert_id();
 	
 
 protected:
@@ -87,6 +118,12 @@
 	bool create_tables();
 	int sql(const char* fmt, ...);
 	
+	int execute(sqlite3_stmt* stmt);
+	
+	// libcache
+	void init_cache();
+	void destroy_cache();
+	
 	char*            m_path;
 	sqlite3*         m_db;
 
@@ -94,11 +131,11 @@
 	uint32_t         m_table_count;
 	uint32_t         m_table_max;
 
+	cache_t*         m_statement_cache;
+	
 	char*            m_error;
 	size_t           m_error_size;
-	
-	sqlite3_stmt**   m_statements;
-	
+
 };
 
 #endif

Modified: branches/PR-7489777/darwinup/Depot.cpp
===================================================================
--- branches/PR-7489777/darwinup/Depot.cpp	2010-02-03 19:28:01 UTC (rev 688)
+++ branches/PR-7489777/darwinup/Depot.cpp	2010-02-04 23:44:08 UTC (rev 689)
@@ -76,6 +76,7 @@
 
 Depot::~Depot() {
 	if (m_lock_fd != -1)	this->unlock();
+	delete m_db2;
 	if (m_db)		sqlite3_close(m_db);
 	if (m_prefix)           free(m_prefix);
 	if (m_depot_path)	free(m_depot_path);
@@ -133,6 +134,8 @@
 		this->SQL("CREATE INDEX files_path ON files (path)");
 	}
 	
+	m_db2 = new DarwinupDatabase(m_database_path);
+	
 	return res;
 }
 
@@ -1218,30 +1221,11 @@
 int Depot::insert(Archive* archive) {
 	// Don't insert an archive that is already in the database
 	assert(archive->serial() == 0);
-	
-	int res = 0;
-	static sqlite3_stmt* stmt = NULL;
-	if (stmt == NULL && m_db) {
-		const char* query = "INSERT INTO archives (uuid, info, name, date_added) VALUES (?, ?, ?, ?)";
-		res = sqlite3_prepare(m_db, query, -1, &stmt, NULL);
-		if (res != 0) fprintf(stderr, "%s:%d: sqlite3_prepare: %s: %s (%d)\n", __FILE__, __LINE__, query, sqlite3_errmsg(m_db), res);
-	}
-	if (stmt && res == 0) {
-		int i = 1;
-		if (res == 0) res = sqlite3_bind_blob(stmt, i++, archive->uuid(), sizeof(uuid_t), SQLITE_STATIC);
-		if (res == 0) res = sqlite3_bind_int(stmt, i++, archive->info());
-		if (res == 0) res = sqlite3_bind_text(stmt, i++, archive->name(), -1, SQLITE_STATIC);
-		if (res == 0) res = sqlite3_bind_int(stmt, i++, archive->date_installed());
-		if (res == 0) res = sqlite3_step(stmt);
-		if (res == SQLITE_DONE) {
-			archive->m_serial = (uint64_t)sqlite3_last_insert_rowid(m_db);
-			res = 0;
-		} else {
-			fprintf(stderr, "%s:%d: Could not add archive to database: %s (%d)\n", __FILE__, __LINE__, sqlite3_errmsg(m_db), res);
-		}
-		sqlite3_reset(stmt);
-	}
-	return res;
+	archive->m_serial = m_db2->insert_archive(archive->uuid(),
+											  archive->info(),
+											  archive->name(),
+											  archive->date_installed());
+	return archive->m_serial == 0;
 }
 
 int Depot::insert(Archive* archive, File* file) {

Modified: branches/PR-7489777/darwinup/Depot.h
===================================================================
--- branches/PR-7489777/darwinup/Depot.h	2010-02-03 19:28:01 UTC (rev 688)
+++ branches/PR-7489777/darwinup/Depot.h	2010-02-04 23:44:08 UTC (rev 689)
@@ -36,6 +36,7 @@
 #include <sys/types.h>
 #include <uuid/uuid.h>
 #include <sqlite3.h>
+#include "DB.h"
 
 struct Archive;
 struct File;
@@ -149,15 +150,17 @@
 
 	virtual int	SQL(const char* fmt, ...);
 
-	sqlite3*	m_db;
+	sqlite3*	      m_db;
+	DarwinupDatabase* m_db2;
+	
 	mode_t		m_depot_mode;
-        char*           m_prefix;
+	char*       m_prefix;
 	char*		m_depot_path;
 	char*		m_database_path;
 	char*		m_archives_path;
 	char*		m_downloads_path;
-	int		m_lock_fd;
-        int             m_is_locked;
+	int		    m_lock_fd;
+	int         m_is_locked;
 };
 
 #endif

Modified: branches/PR-7489777/darwinup/Table.cpp
===================================================================
--- branches/PR-7489777/darwinup/Table.cpp	2010-02-03 19:28:01 UTC (rev 688)
+++ branches/PR-7489777/darwinup/Table.cpp	2010-02-04 23:44:08 UTC (rev 689)
@@ -56,11 +56,16 @@
 	for (uint32_t i = 0; i < m_column_count; i++) {
 		delete m_columns[i];
 	}
+
 	free(m_columns);
 	free(m_name);
+
 	free(m_create_sql);
 	free(m_insert_sql);
+	free(m_update_sql);
+	
 	sqlite3_finalize(m_prepared_insert);
+	sqlite3_finalize(m_prepared_update);
 }
 
 
@@ -142,7 +147,115 @@
 	return m_create_sql;
 }
 
+#define __check_size \
+    if (used >= size-1) { \
+        size *= 4; \
+        query = (char*)realloc(query, size); \
+        if (!query) { \
+			fprintf(stderr, "Error: ran out of memory!\n"); \
+            return NULL; \
+        } \
+    }
 
+sqlite3_stmt* Table::get_value(sqlite3* db, Column* value_column, uint32_t count, va_list args) {
+	size_t size = 256;
+	size_t used = 0;
+	char* query = (char*)malloc(size);
+	sqlite3_stmt* stmt = (sqlite3_stmt*)malloc(sizeof(sqlite3_stmt*));
+	
+	strlcpy(query, "SELECT ", size);
+	used = strlcat(query, value_column->name(), size);
+	__check_size;
+	used = strlcat(query, " FROM ", size);
+	__check_size;
+	used = strlcat(query, m_name, size);
+	__check_size;
+	used = strlcat(query, " WHERE 1", size);
+	__check_size;
+
+	char tmpstr[256];
+	int len;
+	for (uint32_t i=0; i < count; i++) {
+		Column* col = va_arg(args, Column*);
+		va_arg(args, void*); // pop off the value which we do not need this time around
+		len = snprintf(tmpstr, 256, " AND %s=?", col->name()); 
+		if (len >= 255) {
+			fprintf(stderr, "Error: column name is too big (limit: 248): %s\n", col->name());
+			return NULL;
+		}
+		used = strlcat(query, tmpstr, size);
+		__check_size;
+	}
+	strlcat(query, ";", size);
+
+	IF_DEBUG("[TABLE] get_value query: %s \n", query);
+
+	int res = sqlite3_prepare_v2(db, query, size, &stmt, NULL);
+	free(query);
+	if (res != SQLITE_OK) {
+		fprintf(stderr, "Error: unable to prepare statement for get_value.\n");
+		return NULL;
+	}
+
+	return stmt;
+}
+
+/**
+ * Prepare and cache the update statement. 
+ * Assumes table only has 1 primary key
+ */
+sqlite3_stmt* Table::update(sqlite3* db) {
+	// we only need to prepare once, return if we already have it
+	if (m_prepared_update) return m_prepared_update;
+	
+	uint32_t i = 0;
+	bool comma = false;  // flag we set to start adding commas
+	
+	// calculate the length of the sql statement
+	size_t size = 27 + 5*m_column_count;
+	for (i=0; i<m_column_count; i++) {
+		size += strlen(m_columns[i]->name());
+	}
+	
+	// generate the sql query
+	m_update_sql = (char*)malloc(size);
+	strlcpy(m_update_sql, "UPDATE ", size);
+	strlcat(m_update_sql, m_name, size);
+	strlcat(m_update_sql, " SET ", size);
+	for (i=0; i<m_column_count; i++) {
+		// comma separate after 0th column
+		if (comma) strlcat(m_update_sql, ", ", size);
+		// primary keys do not get inserted
+		if (!m_columns[i]->is_pk()) {
+			strlcat(m_update_sql, m_columns[i]->name(), size);
+			strlcat(m_update_sql, "=?", size);
+			comma = true;
+		}
+	}
+	
+	// WHERE statement using primary keys
+	strlcat(m_update_sql, " WHERE ", size);
+	for (i=0; i<m_column_count; i++) {
+		if (m_columns[i]->is_pk()) {
+			strlcat(m_update_sql, m_columns[i]->name(), size);
+			strlcat(m_update_sql, "=?", size);
+			break;
+		}
+	}
+	strlcat(m_update_sql, ";", size);
+	
+	IF_DEBUG("[TABLE] prepared update: %s \n", m_update_sql);
+	
+	// prepare
+	int res = sqlite3_prepare_v2(db, m_update_sql, strlen(m_update_sql), &m_prepared_update, NULL);
+	if (res != SQLITE_OK) {
+		fprintf(stderr, "Error: unable to prepare update statement for table: %s \n", m_name);
+		return NULL;
+	}
+	return m_prepared_update;
+}
+
+
 sqlite3_stmt* Table::insert(sqlite3* db) {
 	// we only need to prepare once, return if we already have it
 	if (m_prepared_insert) return m_prepared_insert;

Modified: branches/PR-7489777/darwinup/Table.h
===================================================================
--- branches/PR-7489777/darwinup/Table.h	2010-02-03 19:28:01 UTC (rev 688)
+++ branches/PR-7489777/darwinup/Table.h	2010-02-04 23:44:08 UTC (rev 689)
@@ -55,10 +55,11 @@
 	char*    count(const char* where);
 	char*    select(const char* where);
 	char*    select_column(const char* column, const char* where);		
-	char*    update(const char* set, const char* where, uint32_t &count);
 	char*    del(const char* where, uint32_t &count);
 
+	sqlite3_stmt*    get_value(sqlite3* db, Column* value_column, uint32_t count, va_list args);
 	sqlite3_stmt*    insert(sqlite3* db);
+	sqlite3_stmt*    update(sqlite3* db);
 	
 	
 protected:
@@ -67,13 +68,14 @@
 	
 	char*          m_create_sql;
 	char*          m_insert_sql;
+	char*          m_update_sql;
 	
 	Column**       m_columns;
 	uint32_t       m_column_count;
 	uint32_t       m_column_max;
 	
 	sqlite3_stmt*  m_prepared_insert;
-	
+	sqlite3_stmt*  m_prepared_update;
 };
 
 #endif

Modified: branches/PR-7489777/darwinup/main.cpp
===================================================================
--- branches/PR-7489777/darwinup/main.cpp	2010-02-03 19:28:01 UTC (rev 688)
+++ branches/PR-7489777/darwinup/main.cpp	2010-02-04 23:44:08 UTC (rev 689)
@@ -147,11 +147,16 @@
 	DarwinupDatabase* testdb = new DarwinupDatabase("/.DarwinDepot/Database-V200");
 
 	Archive* a = new Archive("/.DarwinDepot/Archives/56E93DEE-E6BB-44B2-80A4-32E961751DD8.tar.bz2");
-	uint64_t s = testdb->insert_archive(a->uuid(), a->info(), a->name(), a->date_installed());
+	//uint64_t s = testdb->insert_archive(a->uuid(), a->info(), a->name(), a->date_installed());
 	
 	const char* mypath = "/etc/services";
 	File* f = FileFactory(mypath);	
 	testdb->insert_file(1, 2, 3, 4, f->digest(), a, mypath);
+	testdb->update_file(a, mypath, 5, 6, 7, 8, f->digest());
+	testdb->update_file(a, mypath, 6, 7, 8, 9, f->digest());
+	testdb->update_file(a, mypath, 7, 8, 9, 0, f->digest());
+	testdb->update_file(a, mypath, 8, 9, 0, 1, f->digest());
+	testdb->update_file(a, mypath, 9, 0, 1, 2, f->digest());
 	exit(0);
 	// XXX
 	
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/darwinbuild-changes/attachments/20100204/50b93dcc/attachment-0001.html>


More information about the darwinbuild-changes mailing list