[darwinbuild-changes] [749] branches/PR-7593824/darwinup

source_changes at macosforge.org source_changes at macosforge.org
Mon Mar 8 16:20:01 PST 2010


Revision: 749
          http://trac.macosforge.org/projects/darwinbuild/changeset/749
Author:   wsiegrist at apple.com
Date:     2010-03-08 16:20:00 -0800 (Mon, 08 Mar 2010)
Log Message:
-----------
Support per-table and per-column versioning so we can do most upgrades automatically

Modified Paths:
--------------
    branches/PR-7593824/darwinup/Column.cpp
    branches/PR-7593824/darwinup/Column.h
    branches/PR-7593824/darwinup/DB.cpp
    branches/PR-7593824/darwinup/DB.h
    branches/PR-7593824/darwinup/Database.cpp
    branches/PR-7593824/darwinup/Database.h
    branches/PR-7593824/darwinup/Table.cpp
    branches/PR-7593824/darwinup/Table.h

Modified: branches/PR-7593824/darwinup/Column.cpp
===================================================================
--- branches/PR-7593824/darwinup/Column.cpp	2010-03-09 00:14:45 UTC (rev 748)
+++ branches/PR-7593824/darwinup/Column.cpp	2010-03-09 00:20:00 UTC (rev 749)
@@ -39,25 +39,30 @@
 Column::Column(const char* name, uint32_t type) {
 	m_name       = strdup(name);
 	m_create_sql = NULL;
+	m_alter_sql = NULL;
 	m_type       = type;
 	m_is_index   = false;
 	m_is_pk      = false;
 	m_is_unique  = false;
+	m_version    = 0;
 }
 
 Column::Column(const char* name, uint32_t type, 
 			   bool is_index, bool is_pk, bool is_unique) {
 	m_name       = strdup(name);
 	m_create_sql = NULL;
+	m_alter_sql = NULL;
 	m_type       = type;
 	m_is_index   = is_index;
 	m_is_pk      = is_pk;
 	m_is_unique  = is_unique;
+	m_version    = 0;
 }
 
 Column::~Column() {
 	free(m_name);
 	free(m_create_sql);
+	free(m_alter_sql);
 }
 
 const char* Column::name() {
@@ -80,6 +85,9 @@
 	return m_is_unique;
 }
 
+uint32_t Column::version() {
+	return m_version;
+}
 
 const char* Column::typestr() {
 	switch(m_type) {
@@ -117,3 +125,12 @@
 	}
 	return (const char*)m_create_sql;
 }
+
+const char* Column::alter(const char* table_name) {
+	if (!m_alter_sql) {
+		asprintf(&m_alter_sql, "ALTER TABLE %s ADD COLUMN %s;",
+				table_name, this->create());
+	}
+	return (const char*)m_alter_sql;
+}
+

Modified: branches/PR-7593824/darwinup/Column.h
===================================================================
--- branches/PR-7593824/darwinup/Column.h	2010-03-09 00:14:45 UTC (rev 748)
+++ branches/PR-7593824/darwinup/Column.h	2010-03-09 00:20:00 UTC (rev 749)
@@ -55,6 +55,8 @@
 	const bool     is_pk();
 	const bool     is_unique();
 
+	uint32_t       version();
+	
 	// return size of this column when packed into a result record
 	uint32_t       size();
 
@@ -68,9 +70,13 @@
 	
 	// generate the sql needed to create this column
 	const char*    create();
+	// generate alter table sql for this column for table named table_name
+	const char*    alter(const char* table_name);
 
 	char*          m_name;
-	char*          m_create_sql;
+	uint32_t       m_version; // schema version this was added
+	char*          m_create_sql; // sql fragment for use in CREATE TABLE
+	char*          m_alter_sql; // entire ALTER TABLE ADD COLUMN sql
 	uint32_t       m_type; // SQLITE_* type definition
 	bool           m_is_index;
 	bool           m_is_pk;

Modified: branches/PR-7593824/darwinup/DB.cpp
===================================================================
--- branches/PR-7593824/darwinup/DB.cpp	2010-03-09 00:14:45 UTC (rev 748)
+++ branches/PR-7593824/darwinup/DB.cpp	2010-03-09 00:20:00 UTC (rev 749)
@@ -34,7 +34,6 @@
 
 
 DarwinupDatabase::DarwinupDatabase(const char* path) : Database(path) {
-	m_schema_version = 1;
 	this->connect();
 }
 
@@ -42,18 +41,23 @@
 	// parent automatically deallocates schema objects
 }
 
-int DarwinupDatabase::init_schema() {	
+int DarwinupDatabase::init_schema() {
+	
+
+	SCHEMA_VERSION(0);
+	
 	this->m_archives_table = new Table("archives");
+	ADD_TABLE(this->m_archives_table);
 	ADD_PK(m_archives_table, "serial");
 	ADD_INDEX(m_archives_table, "uuid", TYPE_BLOB, true); 
 	ADD_TEXT(m_archives_table, "name");
 	ADD_INTEGER(m_archives_table, "date_added");
 	ADD_INTEGER(m_archives_table, "active");
 	ADD_INTEGER(m_archives_table, "info");	
-	ADD_TEXT(m_archives_table, "osbuild");
-	assert(this->add_table(this->m_archives_table)==0);
+
 	
 	this->m_files_table = new Table("files");
+	ADD_TABLE(this->m_files_table);
 	ADD_PK(m_files_table, "serial");
 	ADD_INDEX(m_files_table, "archive", TYPE_INTEGER, false);
 	ADD_INTEGER(m_files_table, "info");
@@ -63,19 +67,15 @@
 	ADD_INTEGER(m_files_table, "size");
 	ADD_BLOB(m_files_table, "digest");
 	ADD_INDEX(m_files_table, "path", TYPE_TEXT, false);
+	
 	// custom index to protect from duplicate files
 	assert(this->m_files_table->set_custom_create("CREATE UNIQUE INDEX files_archive_path " 
 												  "ON files (archive, path);") == 0);
-	assert(this->add_table(this->m_files_table)==0);
-	
-	return 0;
-}
 
-int DarwinupDatabase::upgrade_schema(uint32_t fromversion) {
 
-	if (fromversion < 1) {
-		this->sql_once("ALTER TABLE archives ADD COLUMN osbuild TEXT");
-	}
+	SCHEMA_VERSION(1);
+
+	ADD_TEXT(m_archives_table, "osbuild");
 	
 	return 0;
 }

Modified: branches/PR-7593824/darwinup/DB.h
===================================================================
--- branches/PR-7593824/darwinup/DB.h	2010-03-09 00:14:45 UTC (rev 748)
+++ branches/PR-7593824/darwinup/DB.h	2010-03-09 00:20:00 UTC (rev 749)
@@ -56,7 +56,7 @@
 struct DarwinupDatabase : Database {
 	DarwinupDatabase(const char* path);
 	virtual ~DarwinupDatabase();
-	void init_schema();
+	int init_schema();
 	
 	uint64_t count_files(Archive* archive, const char* path);
 	uint64_t count_archives(bool include_rollbacks);

Modified: branches/PR-7593824/darwinup/Database.cpp
===================================================================
--- branches/PR-7593824/darwinup/Database.cpp	2010-03-09 00:14:45 UTC (rev 748)
+++ branches/PR-7593824/darwinup/Database.cpp	2010-03-09 00:20:00 UTC (rev 749)
@@ -82,13 +82,17 @@
 	free(m_error);
 }
 
+uint32_t Database::schema_version() {
+	return this->m_schema_version;
+}
 
-int Database::init_schema() {
-	// do nothing... children should implement this
+void Database::schema_version(uint32_t v) {
+	this->m_schema_version = v;
 }
 
-int Database::upgrade_schema(uint32_t fromversion) {
+int Database::init_schema() {
 	// do nothing... children should implement this
+	return DB_OK;
 }
 
 const char* Database::path() {
@@ -113,6 +117,7 @@
 		return res;
 	}
 	
+	int exists = is_regular_file(m_path);
 	res = sqlite3_open(m_path, &m_db);
 	if (res) {
 		sqlite3_close(m_db);
@@ -128,6 +133,22 @@
 		return res;
 	}
 	
+	if (!exists) {
+		// create schema since it is empty
+		assert(this->create_tables() == 0);
+	} else {
+		// not empty, but upgrade schema if needed
+		uint32_t version = this->get_schema_version();
+		if (version < this->m_schema_version) {
+			assert(this->upgrade_schema(version) == 0);
+			this->set_schema_version(this->m_schema_version);
+		}
+		if (version > this->m_schema_version) {
+			fprintf(stderr, "Error: this client is too old!\n");
+			return DB_ERROR;
+		}
+	}	
+	
 	return res;	
 }
 
@@ -139,7 +160,7 @@
 }
 
 int Database::post_connect() {
-	int res = DB_OK
+	int res = DB_OK;
 	
 	// prepare transaction statements
 	if (res == DB_OK) 
@@ -157,19 +178,7 @@
 	if (verbosity & VERBOSE_SQL) {
 		sqlite3_trace(m_db, dbtrace, NULL);
 	}
-	
-	if (this->is_empty()) {
-		// create schema since it is empty
-		assert(this->create_tables() == 0);
-	} else {
-		// not empty, but upgrade schema if needed
-		version = this->get_schema_version();
-		if (version < this->m_schema_version) {
-			assert(this->upgrade_schema(version) == 0);
-			this->set_schema_version(this->m_schema_version);
-		}
-	}
-	
+		
 	return res;
 }
 
@@ -596,6 +605,7 @@
 		m_table_max *= REALLOC_FACTOR;
 	}
 	m_tables[m_table_count++] = t;
+	t->m_version = this->m_schema_version;
 	
 	return 0;
 }
@@ -617,6 +627,16 @@
 	return res != SQLITE_OK;
 }
 
+int Database::create_table(Table* table) {
+	int res = this->sql_once(table->create());
+	if (res != DB_OK) {
+		fprintf(stderr, "Error: sql error trying to create"
+				" table: %s: %s\n",
+				table->name(), m_error);
+	}
+	return res;
+}
+
 int Database::create_tables() {
 	int res = SQLITE_OK;
 	for (uint32_t i=0; i<m_table_count; i++) {
@@ -630,24 +650,92 @@
 	return res;
 }
 
+int Database::upgrade_schema(uint32_t version) {
+	int res = DB_OK;
+	this->begin_transaction();
+	
+	res = this->upgrade_internal_schema(version);
+	if (res != DB_OK) {
+		fprintf(stderr, "Error: unable to upgrade internal schema.\n");
+		this->rollback_transaction();
+		return res;
+	}			
+	
+	for (uint32_t ti = 0; res == DB_OK && ti < m_table_count; ti++) {
+		if (m_tables[ti]->version() > version) {
+			// entire table is new
+			res = this->create_table(m_tables[ti]);
+		} else {
+			// table is same version, so check for new columns
+			for (uint32_t ci = 0; res == DB_OK && ci < m_tables[ti]->column_count(); ci++) {
+				if (m_tables[ti]->column(ci)->version() < version) {
+					// this should never happen
+					fprintf(stderr, "Error: internal error with schema versioning."
+									" Column %s is older than its table %s. \n",
+							m_tables[ti]->column(ci)->name(), m_tables[ti]->name());
+				}
+				if (m_tables[ti]->column(ci)->version() > version) {
+					// column is new
+					res = this->sql_once(m_tables[ti]->alter_add_column(ci));
+					if (res != DB_OK) {
+						fprintf(stderr, "Error: sql error trying to upgrade (alter)"
+						        " table: %s column: %s : %s\n",
+								m_tables[ti]->name(), m_tables[ti]->column(ci)->name(), 
+								m_error);
+					}		
+				}
+			}
+		}
+	}
+	
+	if (res == DB_OK) {
+		this->commit_transaction();
+	} else {
+		this->rollback_transaction();
+	}
+
+	return res;
+}
+
+int Database::upgrade_internal_schema(uint32_t version) {
+	int res = DB_OK;
+	
+	if (version == 0) {
+		res = this->sql_once(this->m_information_table->create());
+	}
+	
+	return res;
+}
+
 int Database::init_internal_schema() {
 	this->m_information_table = new Table("database_information");
+	ADD_TABLE(this->m_information_table);
 	ADD_PK(m_information_table, "id");
 	ADD_INDEX(m_information_table, "variable", TYPE_TEXT, true);
 	ADD_TEXT(m_information_table, "value");
-	assert(this->add_table(this->m_information_table)==0);
+	return DB_OK;
 }
 
-int Database::get_information_value(char* variable, char** value) {
+int Database::get_information_value(const char* variable, char** value) {
 	return this->get_value("get_information_value",
-						   value,
+						   (void**)value,
 						   this->m_information_table,
-						   this->m_information_table->columns(1), // value
+						   this->m_information_table->column(1), // value
 						   1,
-						   this->m_information_table->columns(0), // variable
+						   this->m_information_table->column(0), // variable
 						   '=', variable);
 }
 
+int Database::update_information_value(const char* variable, const char* value) {
+	return this->update_value("update_information_value",
+							  this->m_information_table,
+							  this->m_information_table->column(1), // value
+							  (void**)value, 
+							  1,
+							  this->m_information_table->column(0), // variable
+							  '=', variable);
+}
+
 uint32_t Database::get_schema_version() {
 	char* vertxt;
 	int res = DB_OK;
@@ -663,23 +751,16 @@
 	}
 }
 
-int Database::update_information_value(char* variable, char* value) {
-	return this->update_value("update_information_value",
-							  this->m_information_table,
-							  this->m_information_table->columns(1), // value
-							  value, 
-							  1,
-							  this->m_information_table->columns(0), // variable
-							  '=', variable);
-}
-
 int Database::set_schema_version(uint32_t version) {
 	int res = DB_OK;
-	res = this->update_information_value("schema_version", &version);
+	char* vertxt;
+	asprintf(&vertxt, "%u", version);
+	if (!vertxt) return DB_ERROR;
+	res = this->update_information_value("schema_version", vertxt);
+	free(vertxt);
 	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);

Modified: branches/PR-7593824/darwinup/Database.h
===================================================================
--- branches/PR-7593824/darwinup/Database.h	2010-03-09 00:14:45 UTC (rev 748)
+++ branches/PR-7593824/darwinup/Database.h	2010-03-09 00:20:00 UTC (rev 749)
@@ -67,19 +67,21 @@
 #define FOUND(x)  ((x & DB_FOUND) && !(x & DB_ERROR))
 
 // Schema creation macros
+#define SCHEMA_VERSION(v) this->schema_version(v);
+#define ADD_TABLE(t) assert(this->add_table(t)==0);
 #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), this->schema_version())==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), this->schema_version())==0);
 #define ADD_PK(table, name) \
 	assert(table->add_column(new Column(name, TYPE_INTEGER, \
-                                        false, true, false))==0);
+                                        false, true, false), this->schema_version())==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), this->schema_version())==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), this->schema_version())==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), this->schema_version())==0);
 
 
 /**
@@ -92,18 +94,17 @@
 	Database(const char* path);
 	virtual ~Database();
 
+	// public setter/getter of class attr
+	uint32_t     schema_version();
+	void         schema_version(uint32_t v);
+	
 	/**
 	 * 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 int init_schema();
-	/**
-	 * upgrade_schema should execute sql statements needed to
-	 * upgrade from fromversion to the current version
-	 */
-	virtual int upgrade_schema(uint32_t fromversion);
+	virtual int  init_schema();
 	
 	const char*  path();
 	const char*  error();
@@ -177,6 +178,18 @@
 	int   pre_connect();
 	int   post_connect();
 	
+	int   upgrade_schema(uint32_t version);
+	int   upgrade_internal_schema(uint32_t version);
+	
+	int   init_internal_schema();
+	
+	int   get_information_value(const char* variable, char** value);
+	int   update_information_value(const char* variable, const char* value);
+	
+	// get and set version info in actual database
+	int       set_schema_version(uint32_t version);
+	uint32_t  get_schema_version(); 
+	
 	// 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
@@ -188,6 +201,7 @@
 	// test if database has had its tables created
 	bool  is_empty();
 	// create tables for the client
+	int   create_table(Table* table);
 	int   create_tables();
 	// create tables for ourselves
 	int   create_internal_tables();
@@ -216,7 +230,7 @@
 	
 	uint32_t         m_schema_version;
 	Table*           m_information_table;
-	sqlite3_stmt     m_get_information_value;
+	sqlite3_stmt*    m_get_information_value;
 	
 	Table**          m_tables;
 	uint32_t         m_table_count;

Modified: branches/PR-7593824/darwinup/Table.cpp
===================================================================
--- branches/PR-7593824/darwinup/Table.cpp	2010-03-09 00:14:45 UTC (rev 748)
+++ branches/PR-7593824/darwinup/Table.cpp	2010-03-09 00:20:00 UTC (rev 749)
@@ -54,7 +54,8 @@
 	m_delete_sql    = NULL;
 	m_prepared_insert = NULL;
 	m_prepared_update = NULL;
-	m_prepared_delete = NULL;	
+	m_prepared_delete = NULL;
+	m_version       = 0;
 }
 
 Table::~Table() {
@@ -89,12 +90,16 @@
 	return m_name;
 }
 
+uint32_t Table::version() {
+	return m_version;
+}
+
 int Table::set_custom_create(const char* sql) {
 	this->m_custom_create_sql = strdup(sql);
 	return this->m_custom_create_sql == 0;
 }
 
-int Table::add_column(Column* c) {
+int Table::add_column(Column* c, uint32_t schema_version) {
 	// accumulate offsets for columns in m_columns_size
 	c->m_offset = this->m_columns_size;
 	this->m_columns_size += c->size();
@@ -109,7 +114,7 @@
 		m_column_max *= REALLOC_FACTOR;
 	}
 	m_columns[m_column_count++] = c;
-	
+	c->m_version = schema_version;
 	return 0;
 }
 
@@ -471,6 +476,11 @@
 	return (const char*)m_create_sql;
 }
 
+const char* Table::alter_add_column(uint32_t index) {
+	if (m_columns[index]) return m_columns[index]->alter(m_name);
+	return NULL;
+}
+
 int Table::where_va_columns(uint32_t count, char* query, size_t size, 
 							size_t* used, va_list args) {
 	char tmpstr[256];

Modified: branches/PR-7593824/darwinup/Table.h
===================================================================
--- branches/PR-7593824/darwinup/Table.h	2010-03-09 00:14:45 UTC (rev 748)
+++ branches/PR-7593824/darwinup/Table.h	2010-03-09 00:20:00 UTC (rev 749)
@@ -44,12 +44,13 @@
 	virtual ~Table();
 	
 	const char*    name();
+	uint32_t       version();
 
 	// Add custom SQL to table initialization
 	int            set_custom_create(const char* sql);
 	
 	// Column handling
-	int            add_column(Column*);
+	int            add_column(Column*, uint32_t schema_version);
 	Column*        column(uint32_t index);
 	// get the result record offset for column at index
 	int            offset(uint32_t index);
@@ -95,6 +96,7 @@
 protected:
 
 	const char*    create();  
+	const char*    alter_add_column(uint32_t index);
 	
 	int            where_va_columns(uint32_t count, char* query, size_t size, 
 									size_t* used, va_list args);
@@ -106,6 +108,7 @@
 	void           dump_results(FILE* f);	
 	
 	char*          m_name;
+	uint32_t       m_version; // schema version this was added
 
 	char*          m_create_sql;
 	char*          m_custom_create_sql;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/darwinbuild-changes/attachments/20100308/969426a3/attachment-0001.html>


More information about the darwinbuild-changes mailing list