public function update($object, $meta=null)
    {
        $query = new Query\Update();
        if (!is_object($object)) {
            throw new \BadMethodCallException("Please use updateTable()");
        } 

        if ($meta) {
            $meta = !$meta instanceof Meta ? $this->mapper->getMeta($meta) : $meta;
        } else {
            $meta = $this->mapper->getMeta(get_class($object));
        }

        if (!$meta->primary) {
            throw new Exception("Cannot update: meta {$meta->id} has no primary key");
        }
        if (!$meta->canUpdate) {
            throw new Exception("Meta {$meta->id} prohibits update");
        }

        event_before: {
            if (isset($meta->on['beforeUpdate'])) {
                foreach ($meta->on['beforeUpdate'] as $cb) { $cb = [$object, $cb]; $cb(); }
            }
            if (isset($this->on['beforeUpdate'])) {
                foreach ($this->on['beforeUpdate'] as $cb) { $cb($object, $meta); }
            }
        }

        query: {
            $query->set = $this->mapper->mapObjectToRow($object, $meta, 'update');
            $query->where = $meta->getIndexValue($object);

            list ($sql, $params, $props) = $query->buildQuery($meta);
            // don't need to do formatParams - it's already covered by the mapPropertiesToRow call in
            // table update mode

            $return = $this->getConnector()->exec($sql, $params);
        }

        event_after: {
            if (isset($meta->on['afterUpdate'])) {
                foreach ($meta->on['afterUpdate'] as $cb) { $cb = [$object, $cb]; $cb(); }
            }
            if (isset($this->on['afterUpdate'])) {
                foreach ($this->on['afterUpdate'] as $cb) { $cb($object, $meta); }
            }
        }
        return $return;
    }