diff options
Diffstat (limited to 'library/Director/Db/Branch/BranchStore.php')
-rw-r--r-- | library/Director/Db/Branch/BranchStore.php | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/library/Director/Db/Branch/BranchStore.php b/library/Director/Db/Branch/BranchStore.php new file mode 100644 index 0000000..196d079 --- /dev/null +++ b/library/Director/Db/Branch/BranchStore.php @@ -0,0 +1,240 @@ +<?php + +namespace Icinga\Module\Director\Db\Branch; + +use Icinga\Module\Director\Db; +use Icinga\Module\Director\Db\DbUtil; +use Ramsey\Uuid\Uuid; +use Ramsey\Uuid\UuidInterface; + +class BranchStore +{ + const TABLE = 'director_branch'; + const TABLE_ACTIVITY = 'director_branch_activity'; + + protected $connection; + + protected $db; + + public function __construct(Db $connection) + { + $this->connection = $connection; + $this->db = $connection->getDbAdapter(); + } + + /** + * @param UuidInterface $uuid + * @return ?Branch + */ + public function fetchBranchByUuid(UuidInterface $uuid) + { + return $this->newFromDbResult( + $this->select()->where('b.uuid = ?', $this->connection->quoteBinary($uuid->getBytes())) + ); + } + + /** + * @param string $name + * @return ?Branch + */ + public function fetchBranchByName($name) + { + return $this->newFromDbResult($this->select()->where('b.branch_name = ?', $name)); + } + + public function cloneBranchForSync(Branch $branch, $newName, $owner) + { + $this->runTransaction(function ($db) use ($branch, $newName, $owner) { + $tables = BranchSupport::BRANCHED_TABLES; + $tables[] = self::TABLE_ACTIVITY; + $newBranch = $this->createBranchByName($newName, $owner); + $oldQuotedUuid = DbUtil::quoteBinaryCompat($branch->getUuid()->getBytes(), $db); + $quotedUuid = DbUtil::quoteBinaryCompat($newBranch->getUuid()->getBytes(), $db); + // $timestampNs = (int)floor(microtime(true) * 1000000); + // Hint: would love to do SELECT *, $quotedUuid AS branch_uuid FROM $table INTO $table + foreach ($tables as $table) { + $rows = $db->fetchAll($db->select()->from($table)->where('branch_uuid = ?', $oldQuotedUuid)); + foreach ($rows as $row) { + $modified = (array)$row; + $modified['branch_uuid'] = $quotedUuid; + if ($table === self::TABLE_ACTIVITY) { + $modified['timestamp_ns'] = round($modified['timestamp_ns'] / 1000000); + } + $db->insert($table, $modified); + } + } + }); + + return $this->fetchBranchByName($newName); + } + + protected function runTransaction($callback) + { + $db = $this->db; + $db->beginTransaction(); + try { + $callback($db); + $db->commit(); + } catch (\Exception $e) { + try { + $db->rollBack(); + } catch (\Exception $ignored) { + // + } + throw $e; + } + } + + public function wipeBranch(Branch $branch, $after = null) + { + $this->runTransaction(function ($db) use ($branch, $after) { + $tables = BranchSupport::BRANCHED_TABLES; + $tables[] = self::TABLE_ACTIVITY; + $quotedUuid = DbUtil::quoteBinaryCompat($branch->getUuid()->getBytes(), $db); + $where = $db->quoteInto('branch_uuid = ?', $quotedUuid); + foreach ($tables as $table) { + if ($after && $table === self::TABLE_ACTIVITY) { + $db->delete($table, $where . ' AND timestamp_ns > ' . (int) $after); + } else { + $db->delete($table, $where); + } + } + }); + + } + + protected function newFromDbResult($query) + { + if ($row = $this->db->fetchRow($query)) { + if (is_resource($row->uuid)) { + $row->uuid = stream_get_contents($row->uuid); + } + return Branch::fromDbRow($row); + } + + return null; + } + + public function setReadyForMerge(Branch $branch) + { + $update = [ + 'ts_merge_request' => (int) floor(microtime(true) * 1000000), + 'description' => $branch->getDescription(), + ]; + + $name = $branch->getName(); + if (preg_match('#^/enforced/(.+)$#', $name, $match)) { + $update['branch_name'] = '/merge/' . substr(sha1($branch->getUuid()->getBytes()), 0, 7) . '/' . $match[1]; + } + $this->db->update('director_branch', $update, $this->db->quoteInto( + 'uuid = ?', + $this->connection->quoteBinary($branch->getUuid()->getBytes()) + )); + } + + protected function select() + { + return $this->db->select()->from(['b' => 'director_branch'], [ + 'uuid' => 'b.uuid', + 'owner' => 'b.owner', + 'branch_name' => 'b.branch_name', + 'description' => 'b.description', + 'ts_merge_request' => 'b.ts_merge_request', + 'cnt_activities' => 'COUNT(ba.timestamp_ns)', + ])->joinLeft( + ['ba' => self::TABLE_ACTIVITY], + 'b.uuid = ba.branch_uuid', + [] + )->group('b.uuid'); + } + + /** + * @param string $name + * @return Branch + * @throws \Zend_Db_Adapter_Exception + */ + public function fetchOrCreateByName($name, $owner) + { + if ($branch = $this->fetchBranchByName($name)) { + return $branch; + } + + return $this->createBranchByName($name, $owner); + } + + /** + * @param string $branchName + * @param string $owner + * @return Branch + * @throws \Zend_Db_Adapter_Exception + */ + public function createBranchByName($branchName, $owner) + { + $uuid = Uuid::uuid4(); + $properties = [ + 'uuid' => $this->connection->quoteBinary($uuid->getBytes()), + 'branch_name' => $branchName, + 'owner' => $owner, + 'description' => null, + 'ts_merge_request' => null, + ]; + $this->db->insert(self::TABLE, $properties); + + if ($branch = static::fetchBranchByUuid($uuid)) { + return $branch; + } + + throw new \RuntimeException(sprintf( + 'Branch with UUID=%s has been created, but could not be fetched from DB', + $uuid->toString() + )); + } + + public function deleteByUuid(UuidInterface $uuid) + { + return $this->db->delete(self::TABLE, $this->db->quoteInto( + 'uuid = ?', + $this->connection->quoteBinary($uuid->getBytes()) + )); + } + + /** + * @param string $name + * @return int + */ + public function deleteByName($name) + { + return $this->db->delete(self::TABLE, $this->db->quoteInto( + 'branch_name = ?', + $name + )); + } + + public function delete(Branch $branch) + { + return $this->deleteByUuid($branch->getUuid()); + } + + /** + * @param Branch $branch + * @param ?int $after + * @return float|null + */ + public function getLastActivityTime(Branch $branch, $after = null) + { + $db = $this->db; + $query = $db->select() + ->from(self::TABLE_ACTIVITY, 'MAX(timestamp_ns)') + ->where('branch_uuid = ?', DbUtil::quoteBinaryCompat($branch->getUuid()->getBytes(), $db)); + if ($after) { + $query->where('timestamp_ns > ?', (int) $after); + } + + $last = $db->fetchOne($query); + if ($last) { + return $last / 1000000; + } + + return null; + } +} |