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

source_changes at macosforge.org source_changes at macosforge.org
Wed Feb 24 10:22:03 PST 2010


Revision: 720
          http://trac.macosforge.org/projects/darwinbuild/changeset/720
Author:   wsiegrist at apple.com
Date:     2010-02-24 10:22:01 -0800 (Wed, 24 Feb 2010)
Log Message:
-----------
Cleanup, document, and reorganize Database

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

Modified: branches/PR-7489777/darwinup/Database.cpp
===================================================================
--- branches/PR-7489777/darwinup/Database.cpp	2010-02-24 16:58:27 UTC (rev 719)
+++ branches/PR-7489777/darwinup/Database.cpp	2010-02-24 18:22:01 UTC (rev 720)
@@ -58,7 +58,8 @@
 	m_db = NULL;		
 	m_path = strdup(path);
 	if (!m_path) {
-		fprintf(stderr, "Error: ran out of memory when constructing database object.\n");
+		fprintf(stderr, "Error: ran out of memory when constructing "
+				        "database object.\n");
 	}
 	m_error_size = ERROR_BUF_SIZE;
 	m_error = (char*)malloc(m_error_size);
@@ -84,45 +85,6 @@
 	// 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;
-	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);
-}
-
-bool cache_key_is_equal(void* key1, void* key2, void* user) {
-	bool res = (strcmp((char*)key1, (char*)key2) == 0);
-	return res;
-}
-
-void cache_key_retain(void* key_in, void** key_out, void* user_data) {
-	*key_out = strdup((char*)key_in);
-}
-
-void cache_key_release(void* key, void* user_data) {
-	free(key);
-}
-
-void cache_value_retain(void* value, void* user_data) {
-	// do nothing
-}
-
-void cache_value_release(void* value, void* user_data) {
-	sqlite3_finalize((sqlite3_stmt*)value);
-}
-
-
 const char* Database::path() {
 	return m_path;
 }
@@ -132,13 +94,18 @@
 }
 
 int Database::connect() {
+	if (!m_path) {
+		fprintf(stderr, "Error: need to specify a path to Database.\n");
+		return -1;
+	}
 	int res = SQLITE_OK;
 	this->init_schema();
 	res = sqlite3_open(m_path, &m_db);
 	if (res) {
 		sqlite3_close(m_db);
 		m_db = NULL;
-		fprintf(stderr, "Error: unable to connect to database at: %s \n", m_path);
+		fprintf(stderr, "Error: unable to connect to database at: %s \n", 
+				m_path);
 		return res;
 	}	
 	sqlite3_trace(m_db, dbtrace, NULL);
@@ -147,12 +114,15 @@
 	}
 	
 	// prepare transaction statements
-	if (res == SQLITE_OK) res = sqlite3_prepare_v2(m_db, "BEGIN TRANSACTION", 18,
-												   &m_begin_transaction, NULL);
-	if (res == SQLITE_OK) res = sqlite3_prepare_v2(m_db, "ROLLBACK TRANSACTION", 21,
-												   &m_rollback_transaction, NULL);
-	if (res == SQLITE_OK) res = sqlite3_prepare_v2(m_db, "COMMIT TRANSACTION", 19,
-												   &m_commit_transaction, NULL);
+	if (res == SQLITE_OK) 
+		res = sqlite3_prepare_v2(m_db, "BEGIN TRANSACTION", 18,
+								 &m_begin_transaction, NULL);
+	if (res == SQLITE_OK) 
+		res = sqlite3_prepare_v2(m_db, "ROLLBACK TRANSACTION", 21,
+								 &m_rollback_transaction, NULL);
+	if (res == SQLITE_OK) 
+		res = sqlite3_prepare_v2(m_db, "COMMIT TRANSACTION", 19,
+								 &m_commit_transaction, NULL);
 	
 	return res;	
 }
@@ -160,93 +130,26 @@
 int Database::connect(const char* path) {
 	this->m_path = strdup(path);
 	if (!m_path) {
-		fprintf(stderr, "Error: ran out of memory when trying to connect to database.\n");
+		fprintf(stderr, "Error: ran out of memory when trying to connect to "
+				        "database.\n");
 		return 1;
 	}
 	return this->connect();
 }
 
-
-int Database::add_table(Table* t) {
-	if (m_table_count >= m_table_max) {
-		m_tables = (Table**)realloc(m_tables, m_table_max * sizeof(Table*) * REALLOC_FACTOR);
-		if (!m_tables) {
-			fprintf(stderr, "Error: unable to reallocate memory to add a table\n");
-			return 1;
-		}
-		m_table_max *= REALLOC_FACTOR;
-	}
-	m_tables[m_table_count++] = t;
-	
-	return 0;
+int Database::begin_transaction() {
+	return this->execute(m_begin_transaction);
 }
 
-int Database::create_tables() {
-	int res = SQLITE_OK;
-	for (uint32_t i=0; i<m_table_count; i++) {
-		res = this->execute(m_tables[i]->create(this->m_db));
-		if (res!=SQLITE_OK) {
-			fprintf(stderr, "Error: sql error trying to create table: %s: %s\n", 
-					m_tables[i]->name(), m_error);
-			return res;
-		}
-	}
-	return res;
+int Database::rollback_transaction() {
+	return this->execute(m_rollback_transaction);
 }
 
-/**
- * attempt to get a row count of the first table to detect if the schema
- * needs to be initialized
- */
-bool Database::is_empty() {
-	if (!m_tables[0]) {
-		fprintf(stderr, "Error: Database has not had a schema initialized.\n");
-		return false;
-	}
-	int res = SQLITE_OK;
-	char* query;
-	asprintf(&query, "SELECT count(*) FROM %s;", m_tables[0]->name());
-	res = sqlite3_exec(this->m_db, query, NULL, NULL, NULL);
-	free(query);
-	return res != SQLITE_OK;
+int Database::commit_transaction() {
+	return this->execute(m_commit_transaction);
 }
 
-int Database::sql_once(const char* fmt, ...) {
-	int res = 0;
-    va_list args;
-    va_start(args, fmt);
-    char* error;
-    if (this->m_db) {
-        char *query = sqlite3_vmprintf(fmt, args);
-        res = sqlite3_exec(this->m_db, query, NULL, NULL, &error);
-        sqlite3_free(query);
-    } else {
-        fprintf(stderr, "Error: database not open.\n");
-        res = SQLITE_ERROR;
-    }
-    va_end(args);
-	if (error) {
-		strlcpy(m_error, error, m_error_size);
-		fprintf(stderr, "Error: sql(): %s \n", m_error);
-		fprintf(stderr, "Error: fmt: %s \n", fmt);
-		sqlite3_free(error);
-	}
-	return res;
-}
 
-int Database::execute(sqlite3_stmt* stmt) {
-	int res = sqlite3_step(stmt);
-	if (res == SQLITE_DONE) {
-		res = SQLITE_OK;
-	} else {
-		strlcpy(m_error, sqlite3_errmsg(m_db), m_error_size);
-		fprintf(stderr, "Error: execute() error: %s \n", m_error);
-	}
-	res = sqlite3_reset(stmt);
-	return res;
-}
-
-
 #define __bind_all_columns(_lastarg) \
     va_list args; \
     va_start(args, _lastarg); \
@@ -328,96 +231,22 @@
 	} \
 	free(key);
 
-size_t Database::store_column(sqlite3_stmt* stmt, int column, uint8_t* output) {
-	size_t used;
-	int type = sqlite3_column_type(stmt, column);
-	const void* blob;
-	int blobsize;
-	switch(type) {
-		case SQLITE_INTEGER:
-			*(uint64_t*)output = (uint64_t)sqlite3_column_int64(stmt, column);
-			used = sizeof(uint64_t);
-			break;
-		case SQLITE_TEXT:
-			*(const char**)output = strdup((const char*)sqlite3_column_text(stmt, column));
-			used = sizeof(char*);
-			break;
-		case SQLITE_BLOB:
-			blob = sqlite3_column_blob(stmt, column);
-			blobsize = sqlite3_column_bytes(stmt, column);
-			*(void**)output = malloc(blobsize);
-			if (*(void**)output && blobsize) {
-				memcpy(*(void**)output, blob, blobsize);
-			} else {
-				fprintf(stderr, "Error: unable to get blob from database stmt.\n");
-			}
-			used = sizeof(void*);
-			break;
-		case SQLITE_NULL:
-			// result row has a NULL value which is okay
-			*(const char**)output = NULL;
-			used = sizeof(char*);
-			break;
-		default:
-			fprintf(stderr, "Error: unhandled column type in Database::store_column(): %d \n", 
-					type);
-			return 0;
-	}
 
-	return used;
-}
-
-/**
- *
- *
- *   will not realloc memory for output since caller should know how
- *   much to alloc in the first place. Sets used to be how many bytes
- *   were written to output
- */
-int Database::step_once(sqlite3_stmt* stmt, uint8_t* output, uint32_t* used) {
-	int res = sqlite3_step(stmt);
-	uint8_t* current = output;
-	if (used) *used = 0;
-	if (res == SQLITE_ROW) {
-		int count = sqlite3_column_count(stmt);
-		for (int i = 0; i < count; i++) {
-			current += this->store_column(stmt, i, current);
-		}
-		if (used) {
-			*used = current - output;
-		}
-	}
-
+int Database::count(const char* name, void** output, Table* table, 
+					uint32_t count, ...) {
+	__get_stmt(table->count(m_db, count, args));
+	int res = SQLITE_OK;
+	uint32_t param = 1;
+	__bind_va_columns(count);
+	*output = malloc(sizeof(uint64_t));
+	res = this->step_once(stmt, *(uint8_t**)output, NULL);
+	sqlite3_reset(stmt);
+	cache_release_value(m_statement_cache, &stmt);	
 	return res;
 }
 
-int Database::step_all(sqlite3_stmt* stmt, void** output, uint32_t size, uint32_t* count) {
-	uint32_t used = 0;
-	uint32_t total_used = used;
-	uint32_t rowsize = size / INITIAL_ROWS;
-	uint8_t* current = *(uint8_t**)output;
-	*count = 0;
-	int res = SQLITE_ROW;
-	while (res == SQLITE_ROW) {
-		current = *(uint8_t**)output + total_used;
-		res = this->step_once(stmt, current, &used);
-		if (res == SQLITE_ROW) (*count)++;
-		total_used += used;
-		if (total_used >= (size - rowsize)) {
-			size *= REALLOC_FACTOR;
-			*output = realloc(*output, size);
-			if (!*output) {
-				fprintf(stderr, "Error: ran out of memory in Database::step_all \n");
-				return SQLITE_ERROR;
-			}
-		}		
-	}
-    sqlite3_reset(stmt);
-	return res;
-}
-
-int Database::get_value(const char* name, void** output, Table* table, Column* value_column, 
-						uint32_t count, ...) {
+int Database::get_value(const char* name, void** output, Table* table, 
+						Column* value_column, uint32_t count, ...) {
 	__get_stmt(table->get_column(m_db, value_column, count, args));
 	int res = SQLITE_OK;
 	uint32_t param = 1;
@@ -430,7 +259,7 @@
 	return res;
 }
 
-int Database::get_column(const char* name, void** output, uint32_t* result_count, 
+int Database::get_column(const char* name, void** output, uint32_t* result_count,
 						 Table* table, Column* column, uint32_t count, ...) {
 	__get_stmt(table->get_column(m_db, column, count, args));
 	int res = SQLITE_OK;
@@ -444,7 +273,8 @@
 	return res;
 }
 
-int Database::get_row(const char* name, uint8_t** output, Table* table, uint32_t count, ...) {
+int Database::get_row(const char* name, uint8_t** output, Table* table, 
+					  uint32_t count, ...) {
 	__get_stmt(table->get_row(m_db, count, args));
 	int res = SQLITE_OK;
 	uint32_t param = 1;
@@ -456,8 +286,8 @@
 	return res;
 }
 
-int Database::get_row_ordered(const char* name, uint8_t** output, Table* table, Column* order_by,
-							  int order, uint32_t count, ...) {
+int Database::get_row_ordered(const char* name, uint8_t** output, Table* table, 
+							  Column* order_by, int order, uint32_t count, ...) {
 	__get_stmt(table->get_row_ordered(m_db, order_by, order, count, args));
 	int res = SQLITE_OK;
 	uint32_t param = 1;
@@ -469,8 +299,9 @@
 	return res;
 }
 
-int Database::get_all_ordered(const char* name, uint8_t*** output, uint32_t* result_count,
-							  Table* table, Column* order_by, int order, uint32_t count, ...) {
+int Database::get_all_ordered(const char* name, uint8_t*** output, 
+							  uint32_t* result_count, Table* table, 
+							  Column* order_by, int order, uint32_t count, ...) {
 	__get_stmt(table->get_row_ordered(m_db, order_by, order, count, args));
 	int res = SQLITE_OK;
 	uint32_t param = 1;
@@ -506,39 +337,45 @@
 	return res;
 }
 
-
-int Database::sql(const char* name, const char* fmt, ...) {
-	sqlite3_stmt* stmt;
-	char* key = strdup(name);
-	cache_get_and_retain(m_statement_cache, key, (void**)&stmt);
-	if (!stmt) {
-		va_list args;
-		va_start(args, fmt);
-		char* query = sqlite3_vmprintf(fmt, args);
-		int res = sqlite3_prepare_v2(m_db, query, strlen(query), &stmt, NULL);
-		va_end(args);
-		if (res != SQLITE_OK) {
-			fprintf(stderr, "Error: unable to prepare statement for query: %s\nError: %s\n",
-					query, sqlite3_errmsg(m_db));
-			free(key);
-			return res;
-		}
-		cache_set_and_retain(m_statement_cache, key, stmt, sizeof(stmt)); \
-		free(key);
+int Database::update_value(const char* name, Table* table, Column* value_column, 
+						   void** value, uint32_t count, ...) {
+	__get_stmt(table->update_value(m_db, value_column, count, args));
+	int res = SQLITE_OK;
+	uint32_t param = 1;
+	switch(value_column->type()) {
+		case TYPE_INTEGER:
+			res = sqlite3_bind_int64(stmt, param++, (uint64_t)*value);
+			break;
+		case TYPE_TEXT:
+			res = sqlite3_bind_text(stmt, param++, (char*)*value, -1, SQLITE_STATIC);
+			break;
+			// XXX: support blob columns here
+		case TYPE_BLOB:
+			fprintf(stderr, "Error: Database::update_value() not implemented for "
+					        "BLOB columns.\n");
+			assert(false);
 	}
-	return this->execute(stmt);
+	if (res != SQLITE_OK) {
+		fprintf(stderr, "Error: update_value failed to bind value with value_column "
+				        "type %d in table %s. \n",
+				value_column->type(), table->name());
+		return res;
+	}
+	__bind_va_columns(count);
+	res = sqlite3_step(stmt);
+	sqlite3_reset(stmt);
+    cache_release_value(m_statement_cache, &stmt);
+	return (res == SQLITE_DONE ? SQLITE_OK : res);
 }
 
-int Database::count(const char* name, void** output, Table* table, uint32_t count, ...) {
-	__get_stmt(table->count(m_db, count, args));
+int Database::del(const char* name, Table* table, uint32_t count, ...) {
+	__get_stmt(table->del(m_db, count, args));
 	int res = SQLITE_OK;
 	uint32_t param = 1;
 	__bind_va_columns(count);
-	*output = malloc(sizeof(uint64_t));
-	res = this->step_once(stmt, *(uint8_t**)output, NULL);
-	sqlite3_reset(stmt);
-	cache_release_value(m_statement_cache, &stmt);	
+	if (res == SQLITE_OK) res = this->execute(stmt);
 	return res;
+	
 }
 
 /**
@@ -547,8 +384,8 @@
  * 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. 
+ * 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. 
  *
  */
 int Database::update(Table* table, uint64_t pkvalue, ...) {
@@ -557,7 +394,8 @@
 	// 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());
+		fprintf(stderr, "Error: %s table gave a NULL statement when trying to "
+				        "update.\n", table->name());
 		return 1;
 	}
 	
@@ -570,57 +408,13 @@
 	return res;
 }
 
-/**
- * Given a table, value_column, and value to set, plus a va_list of Column,compare,value for WHERE clause
- *  set the value_column to value when WHERE is true
- */
-int Database::update_value(const char* name, Table* table, Column* value_column, void** value, 
-							uint32_t count, ...) {
-	__get_stmt(table->update_value(m_db, value_column, count, args));
-	int res = SQLITE_OK;
-	uint32_t param = 1;
-	switch(value_column->type()) {
-		case TYPE_INTEGER:
-			res = sqlite3_bind_int64(stmt, param++, (uint64_t)*value);
-			break;
-		case TYPE_TEXT:
-			res = sqlite3_bind_text(stmt, param++, (char*)*value, -1, SQLITE_STATIC);
-			break;
-		// XXX: support blob columns here
-		case TYPE_BLOB:
-			fprintf(stderr, "Error: Database::update_value() not implemented for BLOB columns.\n");
-			assert(false);
-	}
-	if (res != SQLITE_OK) {
-		fprintf(stderr, "Error: update_value failed to bind value with value_column type %d in "
-				"table %s. \n",
-				value_column->type(), table->name());
-		return res;
-	}
-	__bind_va_columns(count);
-	res = sqlite3_step(stmt);
-	sqlite3_reset(stmt);
-    cache_release_value(m_statement_cache, &stmt);
-	return (res == SQLITE_DONE ? SQLITE_OK : res);
-}
-
-/**
- * Given a table and an arg list in the same order as Table::add_column() calls,
- * minus any primary key columns, 
- * binds and executes a sql insertion. The Table is responsible for preparing the
- * statement in Table::insert()
- *
- * 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. 
- *
- */
 int Database::insert(Table* table, ...) {
 	int res = SQLITE_OK;
 	// get the prepared statement
 	sqlite3_stmt* stmt = table->insert(m_db);
 	if (!stmt) {
-		fprintf(stderr, "Error: %s table gave a NULL statement when trying to insert.\n", table->name());
+		fprintf(stderr, "Error: %s table gave a NULL statement when trying to "
+				        "insert.\n", table->name());
 		return 1;
 	}
 	uint32_t param = 1; // counter to track placeholders in sql statement
@@ -629,12 +423,15 @@
 	return res;
 }
 
+#undef __bind_all_columns
+#undef __get_stmt
 
 int Database::del(Table* table, uint64_t serial) {
 	int res = SQLITE_OK;
 	sqlite3_stmt* stmt = table->del(m_db);
 	if (!stmt) {
-		fprintf(stderr, "Error: %s table gave a NULL statement when trying to delete.\n", table->name());
+		fprintf(stderr, "Error: %s table gave a NULL statement when trying to "
+				        "delete.\n", table->name());
 		return res;
 	}
 	if (res == SQLITE_OK) res = sqlite3_bind_int64(stmt, 1, serial);
@@ -642,33 +439,245 @@
 	return res;
 }
 
-int Database::del(const char* name, Table* table, uint32_t count, ...) {
-	__get_stmt(table->del(m_db, count, args));
+uint64_t Database::last_insert_id() {
+	return (uint64_t)sqlite3_last_insert_rowid(m_db);
+}
+
+
+
+int Database::sql_once(const char* fmt, ...) {
+	int res = 0;
+    va_list args;
+    va_start(args, fmt);
+    char* error;
+    if (this->m_db) {
+        char *query = sqlite3_vmprintf(fmt, args);
+        res = sqlite3_exec(this->m_db, query, NULL, NULL, &error);
+        sqlite3_free(query);
+    } else {
+        fprintf(stderr, "Error: database not open.\n");
+        res = SQLITE_ERROR;
+    }
+    va_end(args);
+	if (error) {
+		strlcpy(m_error, error, m_error_size);
+		fprintf(stderr, "Error: sql(): %s \n", m_error);
+		fprintf(stderr, "Error: fmt: %s \n", fmt);
+		sqlite3_free(error);
+	}
+	return res;
+}
+
+int Database::sql(const char* name, const char* fmt, ...) {
+	sqlite3_stmt* stmt;
+	char* key = strdup(name);
+	cache_get_and_retain(m_statement_cache, key, (void**)&stmt);
+	if (!stmt) {
+		va_list args;
+		va_start(args, fmt);
+		char* query = sqlite3_vmprintf(fmt, args);
+		int res = sqlite3_prepare_v2(m_db, query, strlen(query), &stmt, NULL);
+		va_end(args);
+		if (res != SQLITE_OK) {
+			fprintf(stderr, "Error: unable to prepare statement for query: %s\n"
+					        "Error: %s\n",
+					query, sqlite3_errmsg(m_db));
+			free(key);
+			return res;
+		}
+		cache_set_and_retain(m_statement_cache, key, stmt, sizeof(stmt)); \
+		free(key);
+	}
+	return this->execute(stmt);
+}
+
+int Database::execute(sqlite3_stmt* stmt) {
+	int res = sqlite3_step(stmt);
+	if (res == SQLITE_DONE) {
+		res = SQLITE_OK;
+	} else {
+		strlcpy(m_error, sqlite3_errmsg(m_db), m_error_size);
+		fprintf(stderr, "Error: execute() error: %s \n", m_error);
+	}
+	res = sqlite3_reset(stmt);
+	return res;
+}
+
+int Database::add_table(Table* t) {
+	if (m_table_count >= m_table_max) {
+		m_tables = (Table**)realloc(m_tables, 
+									m_table_max*sizeof(Table*)*REALLOC_FACTOR);
+		if (!m_tables) {
+			fprintf(stderr, "Error: unable to reallocate memory to add a "
+					"table\n");
+			return 1;
+		}
+		m_table_max *= REALLOC_FACTOR;
+	}
+	m_tables[m_table_count++] = t;
+	
+	return 0;
+}
+
+/**
+ * get a row count of the first table to detect if the schema
+ * needs to be initialized
+ */
+bool Database::is_empty() {
+	if (!m_tables[0]) {
+		fprintf(stderr, "Warning: Database has not had a schema initialized.\n");
+		return false;
+	}
 	int res = SQLITE_OK;
-	uint32_t param = 1;
-	__bind_va_columns(count);
-	if (res == SQLITE_OK) res = this->execute(stmt);
+	char* query;
+	asprintf(&query, "SELECT count(*) FROM %s;", m_tables[0]->name());
+	res = sqlite3_exec(this->m_db, query, NULL, NULL, NULL);
+	free(query);
+	return res != SQLITE_OK;
+}
+
+int Database::create_tables() {
+	int res = SQLITE_OK;
+	for (uint32_t i=0; i<m_table_count; i++) {
+		res = this->execute(m_tables[i]->create(this->m_db));
+		if (res!=SQLITE_OK) {
+			fprintf(stderr, "Error: sql error trying to create table: %s: %s\n",
+					m_tables[i]->name(), m_error);
+			return res;
+		}
+	}
 	return res;
+}
+
+size_t Database::store_column(sqlite3_stmt* stmt, int column, uint8_t* output) {
+	size_t used;
+	int type = sqlite3_column_type(stmt, column);
+	const void* blob;
+	int blobsize;
+	switch(type) {
+		case SQLITE_INTEGER:
+			*(uint64_t*)output = (uint64_t)sqlite3_column_int64(stmt, column);
+			used = sizeof(uint64_t);
+			break;
+		case SQLITE_TEXT:
+			*(const char**)output = strdup((const char*)sqlite3_column_text(stmt, 
+																			column));
+			used = sizeof(char*);
+			break;
+		case SQLITE_BLOB:
+			blob = sqlite3_column_blob(stmt, column);
+			blobsize = sqlite3_column_bytes(stmt, column);
+			*(void**)output = malloc(blobsize);
+			if (*(void**)output && blobsize) {
+				memcpy(*(void**)output, blob, blobsize);
+			} else {
+				fprintf(stderr, "Error: unable to get blob from database stmt.\n");
+			}
+			used = sizeof(void*);
+			break;
+		case SQLITE_NULL:
+			// result row has a NULL value which is okay
+			*(const char**)output = NULL;
+			used = sizeof(char*);
+			break;
+		default:
+			fprintf(stderr, "Error: unhandled column type in "
+							"Database::store_column(): %d \n", 
+					type);
+			return 0;
+	}
 	
+	return used;
 }
 
-#undef __bind_all_columns
-#undef __get_stmt
+/**
+ *   will not realloc memory for output since caller should know how
+ *   much to alloc in the first place. Sets used to be how many bytes
+ *   were written to output
+ */
+int Database::step_once(sqlite3_stmt* stmt, uint8_t* output, uint32_t* used) {
+	int res = sqlite3_step(stmt);
+	uint8_t* current = output;
+	if (used) *used = 0;
+	if (res == SQLITE_ROW) {
+		int count = sqlite3_column_count(stmt);
+		for (int i = 0; i < count; i++) {
+			current += this->store_column(stmt, i, current);
+		}
+		if (used) {
+			*used = current - output;
+		}
+	}
+	
+	return res;
+}
 
-uint64_t Database::last_insert_id() {
-	return (uint64_t)sqlite3_last_insert_rowid(m_db);
+int Database::step_all(sqlite3_stmt* stmt, void** output, uint32_t size, 
+					   uint32_t* count) {
+	uint32_t used = 0;
+	uint32_t total_used = used;
+	uint32_t rowsize = size / INITIAL_ROWS;
+	uint8_t* current = *(uint8_t**)output;
+	*count = 0;
+	int res = SQLITE_ROW;
+	while (res == SQLITE_ROW) {
+		current = *(uint8_t**)output + total_used;
+		res = this->step_once(stmt, current, &used);
+		if (res == SQLITE_ROW) (*count)++;
+		total_used += used;
+		if (total_used >= (size - rowsize)) {
+			size *= REALLOC_FACTOR;
+			*output = realloc(*output, size);
+			if (!*output) {
+				fprintf(stderr, "Error: ran out of memory in Database::step_all \n");
+				return SQLITE_ERROR;
+			}
+		}		
+	}
+    sqlite3_reset(stmt);
+	return res;
 }
 
 
-int Database::begin_transaction() {
-	return this->execute(m_begin_transaction);
+/**
+ *
+ *  libcache
+ *
+ */
+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;
+	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);
 }
 
-int Database::rollback_transaction() {
-	return this->execute(m_rollback_transaction);
+void Database::destroy_cache() {
+	cache_destroy(m_statement_cache);
 }
 
-int Database::commit_transaction() {
-	return this->execute(m_commit_transaction);
+bool cache_key_is_equal(void* key1, void* key2, void* user) {
+	bool res = (strcmp((char*)key1, (char*)key2) == 0);
+	return res;
 }
 
+void cache_key_retain(void* key_in, void** key_out, void* user_data) {
+	*key_out = strdup((char*)key_in);
+}
+
+void cache_key_release(void* key, void* user_data) {
+	free(key);
+}
+
+void cache_value_retain(void* value, void* user_data) {
+	// do nothing
+}
+
+void cache_value_release(void* value, void* user_data) {
+	sqlite3_finalize((sqlite3_stmt*)value);
+}

Modified: branches/PR-7489777/darwinup/Database.h
===================================================================
--- branches/PR-7489777/darwinup/Database.h	2010-02-24 16:58:27 UTC (rev 719)
+++ branches/PR-7489777/darwinup/Database.h	2010-02-24 18:22:01 UTC (rev 720)
@@ -47,6 +47,7 @@
 #include "Digest.h"
 #include "Archive.h"
 
+// flag for generating queries with ORDER BY clauses
 #define ORDER_BY_DESC 0
 #define ORDER_BY_ASC  1
 
@@ -62,30 +63,25 @@
 #define DB_ERROR     0x0001
 #define DB_FOUND     0x0010
 
+// test return code to see if actual results were found
 #define FOUND(x)  ((x & DB_FOUND) && !(x & DB_ERROR))
 
 // Schema creation macros
 #define ADD_COLUMN(table, name, type, index, pk, unique) \
-        assert(table->add_column(new Column(name, type, index, pk, unique))==0);
+    assert(table->add_column(new Column(name, type, index, pk, unique))==0);
 #define ADD_INDEX(table, name, type, unique) \
-        assert(table->add_column(new Column(name, type, true, false, unique))==0);
+	assert(table->add_column(new Column(name, type, true, false, unique))==0);
 #define ADD_PK(table, name) \
-        assert(table->add_column(new Column(name, TYPE_INTEGER, false, true, false))==0);
+	assert(table->add_column(new Column(name, TYPE_INTEGER, \
+                                        false, true, false))==0);
 #define ADD_TEXT(table, name) \
-		assert(table->add_column(new Column(name, TYPE_TEXT))==0);
+	assert(table->add_column(new Column(name, TYPE_TEXT))==0);
 #define ADD_INTEGER(table, name) \
-		assert(table->add_column(new Column(name, TYPE_INTEGER))==0);
+	assert(table->add_column(new Column(name, TYPE_INTEGER))==0);
 #define ADD_BLOB(table, name) \
-        assert(table->add_column(new Column(name, TYPE_BLOB))==0);
+	assert(table->add_column(new Column(name, TYPE_BLOB))==0);
 
-// libcache callbacks
-bool cache_key_is_equal(void* key1, void* key2, void* user);
-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
@@ -96,30 +92,40 @@
 	Database(const char* path);
 	virtual ~Database();
 
-	static const int TYPE_INTEGER = SQLITE_INTEGER;
-	static const int TYPE_TEXT    = SQLITE3_TEXT;
-	static const int TYPE_BLOB    = SQLITE_BLOB;
+	/**
+	 * init_schema is called during db connection.
+	 * Projects implementing a Database derived class
+	 * should use Table::add_column() or the ADD_*
+	 * macros in their init_schema() to define their schema
+	 */
+	virtual void init_schema();
 	
-	virtual void init_schema();
 	const char*  path();
 	const char*  error();
 	int          connect();
 	int          connect(const char* path);
 	
-	
 	int          begin_transaction();
 	int          rollback_transaction();
 	int          commit_transaction();
 	
 	/**
-	 * SELECT statement caching and execution
+	 * 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
+	 * - name is a string key that labels the query for caching purposes 
+	 * - output is where we will store the value requested
+	 * - count is the number of sets of parameters
+	 * - va_list should have sets of 3 (integer and text) or 4 (blob) 
+	 *     parameters for WHERE clause like Column*, char, value(s)
+	 *      - Column* is the column to match against
+	 *      - char is how to compare, one of '=', '!', '>', or '<'
+	 *      - value(s) is the value to match
+	 *          - text columns require a char* arg
+	 *          - integer columns require a uint64_t arg
+	 *          - blob columns require 2 args in the list:
+	 *              - first is a uint8_t* of data
+	 *              - second is a uint32_t value for size of the data
 	 *
-	 *  everything else are Column*,value pairs for making a WHERE clause
-	 *
 	 */
 	int  count(const char* name, void** output, Table* table, uint32_t count, ...);
 	int  get_value(const char* name, void** output, Table* table, Column* value_column, 
@@ -133,26 +139,52 @@
 						 Table* table, Column* order_by, int order, uint32_t count, ...);
 	int  update_value(const char* name, Table* table, Column* value_column, void** value, 
 					  uint32_t count, ...);
+	int  del(const char* name, Table* table, uint32_t count, ...);
+	
+	/**
+	 * update/insert whole rows
+	 *
+	 * Given a table and a va_list in the same order as Table::add_column() 
+	 * calls, minus any primary key columns, bind and executes a sql query 
+	 * for insert or update. 
+	 *
+	 * The Table is responsible for preparing the statement
+	 *
+	 * text columns require char* args
+	 * integer columns require uint64_t args
+	 * blob columns require 2 args in the list:
+	 *    - first is a uint8_t* of data
+	 *    - second is a uint32_t value for size of the data 
+	 *
+	 */	
 	int  update(Table* table, uint64_t pkvalue, ...);
 	int  insert(Table* table, ...);
-
+	
+	// delete row with primary key equal to serial
 	int  del(Table* table, uint64_t serial);
-	int  del(const char* name, Table* table, uint32_t count, ...);
-		
-	int  add_table(Table*);
+	
 	uint64_t last_insert_id();
 	
+	
+protected:
+
+	// execute query with printf-style format, does not cache statement
 	int sql_once(const char* fmt, ...);
+	// cache statement with name, execute query with printf-style format
 	int sql(const char* name, const char* fmt, ...);
+	int execute(sqlite3_stmt* stmt);
 	
-protected:
+	int  add_table(Table*);
 	
+	// test if database has had its tables created
 	bool  is_empty();
 	int   create_tables();
 
-	
-	int execute(sqlite3_stmt* stmt);
-	
+
+
+	/**
+	 * step and store functions
+	 */
 	size_t store_column(sqlite3_stmt* stmt, int column, uint8_t* output);
 	int step_once(sqlite3_stmt* stmt, uint8_t* output, uint32_t* used);
 	int step_all(sqlite3_stmt* stmt, void** output, uint32_t size, uint32_t* count);
@@ -176,7 +208,18 @@
 	
 	char*            m_error;
 	size_t           m_error_size;
+	
+	static const int TYPE_INTEGER = SQLITE_INTEGER;
+	static const int TYPE_TEXT    = SQLITE3_TEXT;
+	static const int TYPE_BLOB    = SQLITE_BLOB;
 
 };
 
+// libcache callbacks
+bool cache_key_is_equal(void* key1, void* key2, void* user);
+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);
+
 #endif

Modified: branches/PR-7489777/darwinup/Table.cpp
===================================================================
--- branches/PR-7489777/darwinup/Table.cpp	2010-02-24 16:58:27 UTC (rev 719)
+++ branches/PR-7489777/darwinup/Table.cpp	2010-02-24 18:22:01 UTC (rev 720)
@@ -189,6 +189,7 @@
             op = tmp_op; \
         } \
         va_arg(args, void*); \
+        if (col->type() == SQLITE_BLOB) va_arg(args, uint32_t); \
 		len = snprintf(tmpstr, 256, " AND %s%c%c?", col->name(), not_op, op); \
 		if (len >= 255) { \
 			fprintf(stderr, "Error: column name is too big (limit: 248): %s\n", col->name()); \

Modified: branches/PR-7489777/darwinup/Table.h
===================================================================
--- branches/PR-7489777/darwinup/Table.h	2010-02-24 16:58:27 UTC (rev 719)
+++ branches/PR-7489777/darwinup/Table.h	2010-02-24 18:22:01 UTC (rev 720)
@@ -62,13 +62,13 @@
 	 *
 	 * - order is either ORDER_BY_ASC or ORDER_BY_DESC
 	 * - count parameters should be the number of items in the va_list
-	 * - args should be a va_list with sets of 3 parameters for WHERE clause:
-	 *        Column*, char, value
-	 *
+	 * - args should have sets of 3 (integer and text) or 4 (blob) 
+	 *     parameters for WHERE clause like Column*, char, value
 	 *      - Column* is the column to match against
 	 *      - char is how to compare, one of '=', '!', '>', or '<'
-	 *      - value is the value to match, which is ignored by these API
-	 *          since they leave placeholders instead
+	 *      - value is the value to match (1 for integer and text, 2 for blobs)
+	 *          which is ignored by these API since they leave placeholders 
+	 *          instead
 	 *
 	 */
 	sqlite3_stmt*    create(sqlite3* db);  
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/darwinbuild-changes/attachments/20100224/de36d116/attachment-0001.html>


More information about the darwinbuild-changes mailing list