docs: function do_update(self, ObjType, obj, orig_data) {
  	return nr_fcall("nopg:do_update", function() {
  		var query, params, data, where = {};
  
  		if(obj.$id) {
  			where.$id = obj.$id;
  		} else if(obj.$name) {
  			where.$name = obj.$name;
  		} else {
  			throw new TypeError("Cannot know what to update!");
  		}
  
  		if(orig_data === undefined) {
  			// FIXME: Check that `obj` is an ORM object
  			data = obj.valueOf();
  		} else {
  			data = (new ObjType(obj)).update(orig_data).valueOf();
  		}
  
  		//debug.log('data = ', data);
  
  		// Select only keys that start with $
  		var keys = ARRAY(ObjType.meta.keys)
  			// Remove leading '$' character from keys
  			.filter(first_letter_is_dollar)
  			.map( parse_keyword_name )
  			// Ignore keys that aren't going to be changed
  			.filter(function(key) {
  				return data.hasOwnProperty(key);
  			// Ignore keys that were not changed
  			}).filter(function(key) {
  				return json_cmp(data[key], obj['$'+key]) ? false : true;
  			});
  
  		//debug.log('keys = ', keys.valueOf());
  
  		// Return with the current object if there is no keys to update
  		if(keys.valueOf().length === 0) {
  			return do_select(self, ObjType, where);
  		}
  
  		// FIXME: Implement binary content support
  		query = "UPDATE " + (ObjType.meta.table) + " SET "+ keys.map(function(k, i) { return k + ' = $' + (i+1); }).join(', ') +" WHERE ";
  
  		if(where.$id) {
  			query += "id = $"+ (keys.valueOf().length+1);
  		} else if(where.$name) {
  			query += "name = $"+ (keys.valueOf().length+1);
  		} else {
  			throw new TypeError("Cannot know what to update!");
  		}
  
  		query += " RETURNING *";
  
  		params = keys.map(function(key) {
  			return data[key];
  		}).valueOf();
  
  		if(where.$id) {
  			params.push(where.$id);
  		} else if(where.$name){
  			params.push(where.$name);
  		}
  
  		return do_query(self, query, params);
  	});
  }