summaryrefslogtreecommitdiffstats
path: root/library/Director/Db/Branch/BranchStore.php
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--library/Director/Db/Branch/BranchStore.php240
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;
+ }
+}