docs: function(tablename, context, definer) {
  	var me = {};
  	var db = common.config.database;
  
  	me.act = new NobleMachine(); // Query subactions are added as states to this.
  
  	var needsTableCreation = (context == 'create');
  
  	function nextQuery(query) {
  		me.act.next(db.query(query));
  	}
  
  	var definitions = {
  		add_column: function(name, type, options) {
  			var col = { name: name, type: type };
  			_.extend(col, options);
  			
  			// If we haven't yet made the table, use the first added column as part of the create statement.
  			// All following columns are added via ALTER TABLE. This is because a table cannot be created
  			// with zero columns.
  			if (needsTableCreation) {
  				nextQuery("CREATE TABLE `" + tablename + "` ( " + columnToSQL(col) + " ) ENGINE=INNODB;");
  				needsTableCreation = false;
  			} else {
  				nextQuery("ALTER TABLE `" + tablename + "` ADD " + columnToSQL(col) + ";");
  			}
  		},
  
  		timestamps: function(options) {
  			this.column('createdAt', 'datetime', options);
  			this.column('updatedAt', 'datetime', options);
  		}
  	};
  
  	definitions.column = definitions.add_column;
  	
  	if (context == 'alter') {
  		_.extend(definitions, {
  			change_column: function(name, type, options) {
  				var col = { name: name, type: type };
  				_.extend(col, options);
  
  				nextQuery("ALTER TABLE `" + tablename + "` MODIFY " + columnToSQL(col) + ";");
  			},
  
  			remove_column: function(name) {
  				nextQuery("ALTER TABLE `" + tablename + "` DROP COLUMN `" + name + "`;");
  			},
  
  			// There doesn't seem to be a proper RENAME COLUMN statement in MySQL.
  			// As such, it is necessary to use CHANGE after extracting the current column definition.
  			rename_column: function(name, newname) {
  				var act = new NobleMachine(function() {
  					act.toNext(db.query("SHOW COLUMNS FROM `" + tablename + "`;"));
  				});
  
  				act.next(function(result) {
  					var sql = "ALTER TABLE `" + tablename + "` CHANGE `" + name + "` `" + newname + "`";
  
  					result.forEach(function(coldatum) {
  						if (coldatum['Field'] == name) {
  							sql += " " + coldatum['Type'];
  							
  							if (coldatum['Null'] == 'NO') {
  								sql += " NOT NULL";
  							}
  
  							if (coldatum['Key'] == 'PRI') {
  								sql += " PRIMARY KEY";
  							}
  
  							sql += coldatum['Extra'];
  
  							if (coldatum['Default'] != 'NULL') {
  								sql += " DEFAULT " + coldatum['Default'];
  							}
  						}
  					});
  					sql += ";";
  
  					act.toNext(db.query(sql));
  				});
  
  				me.act.next(act);
  			},
  		});
  	}
  
  	var types = ['primary_key', 'string', 'text', 'integer', 'boolean', 'datetime', 'timestamp']
  	types.forEach(function(type) {
  		definitions[type] = function() {
  			var options = {}; defs = this;
  
  			var args = Array.prototype.slice.call(arguments);
  
  			args.forEach(function(arg) {
  				if (typeof arg == "object") {
  					_.extend(options, arg);
  				} 
  			});
  
  			args.forEach(function(arg) {
  				if (typeof arg == "string") {
  					defs.column(arg, type, options);
  				}
  			});
  		}
  	});
  
  	definer(definitions);
  
  	return me;
  }