function install(Connection $db, Logger $log) {
    // bail if we've already applied
    if ($this->isApplied($db)) {
      $log->info("Skipping " . $this->getName() . ": already applied");
      return;
    }

    // simply make sure all parent migrations are applied
    foreach ($this->getParents() as $migration) {
      $migration->install($db, $log);
    }

    // check we have a valid name
    if (strlen($this->getName()) == 0 || strlen($this->getName()) > 255) {
      throw new DbException("Invalid migration name '" . $this->getName() . "'");
    }

    // and then install our own
    try {
      $log->info("Applying " . $this->getName());
      if ($this->apply($db)) {
        $log->info("Applied migration " . $this->getName());
      } else {
        $log->error("Could not apply migration '" . $this->getName() . "': " . $db->lastError());
        throw new DbException("Could not apply migration " . $this->getName());
      }
    } catch (DbException $e) {
      $log->error("Could not apply migration '" . $this->getName() . "': " . $e->getMessage());
      throw new DbException("Could not apply migration " . $this->getName(), $e);
    }

    // save migration status
    $q = $db->prepare("INSERT INTO migrations SET name=?");
    $q->execute(array($this->getName()));
  }