[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