Completed
Push — master ( a49ccc...dd28ca )
by Ondřej
03:34
created

TxHandle::rollbackToSavepoint()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
1
<?php
2
namespace Ivory\Connection;
3
4
use Ivory\Exception\InvalidStateException;
5
use Ivory\Result\IQueryResult;
6
use Ivory\Type\Ivory\IdentifierSerializer;
7
use Ivory\Type\Std\StringType;
8
9
/**
10
 * {@inheritdoc}
11
 *
12
 * This implementation checks, upon destruction, whether the transaction has properly been closed. If not, i.e., when
13
 * the transaction handle gets lost and thus no further means of controlling the transaction are available, a warning is
14
 * emitted.
15
 */
16
class TxHandle implements ITxHandle
17
{
18
    private $open = true;
19
    private $stmtExec;
20
    private $txCtl;
21
    private $identSerializer;
22
    private $stringSerializer;
23
24
    public function __construct(IStatementExecution $stmtExec, IObservableTransactionControl $observableTxCtl)
25
    {
26
        $this->stmtExec = $stmtExec;
27
        $this->txCtl = $observableTxCtl;
28
        $this->identSerializer = new IdentifierSerializer();
29
        $this->stringSerializer = new StringType('pg_catalog', 'text');
30
    }
31
32
    public function __destruct()
33
    {
34
        if ($this->open) {
35
            trigger_error(
36
                'An open transaction handle has been released from memory. The transaction has probably stayed open.',
37
                E_USER_WARNING
38
            );
39
        }
40
    }
41
42
    private function assertOpen()
43
    {
44
        if (!$this->open) {
45
            if ($this->txCtl->inTransaction()) {
46
                throw new InvalidStateException(
47
                    'Controlling the transaction using a wrong handle - this has already been closed'
48
                );
49
            } else {
50
                throw new InvalidStateException('The transaction is not open anymore');
51
            }
52
        }
53
    }
54
55
    public function isOpen(): bool
56
    {
57
        return $this->open;
58
    }
59
60
    public function setupTransaction($transactionOptions)
61
    {
62
        $this->assertOpen();
63
        $txConfig = TxConfig::create($transactionOptions);
64
        $this->stmtExec->rawCommand('SET TRANSACTION ' . $txConfig->toSql());
65
    }
66
67
    public function setTransactionSnapshot(string $snapshotId)
68
    {
69
        $this->assertOpen();
70
        $this->stmtExec->rawCommand("SET TRANSACTION SNAPSHOT {$this->stringSerializer->serializeValue($snapshotId)}");
71
        return true;
72
    }
73
74
    public function exportTransactionSnapshot(): string
75
    {
76
        $this->assertOpen();
77
78
        /** @var IQueryResult $r */
79
        $r = $this->stmtExec->rawQuery('SELECT pg_export_snapshot()');
80
        return $r->value();
81
    }
82
83
    public function commit()
84
    {
85
        $this->assertOpen();
86
        $this->stmtExec->rawCommand('COMMIT');
87
        $this->open = false;
88
        $this->txCtl->notifyTransactionCommit();
89
    }
90
91
    public function rollback()
92
    {
93
        $this->assertOpen();
94
        $this->stmtExec->rawCommand('ROLLBACK');
95
        $this->open = false;
96
        $this->txCtl->notifyTransactionRollback();
97
    }
98
99
    public function rollbackIfOpen()
100
    {
101
        if ($this->open) {
102
            $this->stmtExec->rawCommand('ROLLBACK');
103
            $this->open = false;
104
            $this->txCtl->notifyTransactionRollback();
105
        }
106
    }
107
108
    public function savepoint(string $name)
109
    {
110
        $this->assertOpen();
111
        $this->stmtExec->rawCommand(sprintf('SAVEPOINT %s', $this->identSerializer->serializeValue($name)));
112
        $this->txCtl->notifySavepointSaved($name);
113
    }
114
115
    public function rollbackToSavepoint(string $name)
116
    {
117
        $this->assertOpen();
118
        $this->stmtExec->rawCommand(sprintf('ROLLBACK TO SAVEPOINT %s', $this->identSerializer->serializeValue($name)));
119
        $this->txCtl->notifyRollbackToSavepoint($name);
120
    }
121
122
    public function releaseSavepoint(string $name)
123
    {
124
        $this->assertOpen();
125
        $this->stmtExec->rawCommand(sprintf('RELEASE SAVEPOINT %s', $this->identSerializer->serializeValue($name)));
126
        $this->txCtl->notifySavepointReleased($name);
127
    }
128
129
    public function prepareTransaction(string $name)
130
    {
131
        $this->assertOpen();
132
        $this->stmtExec->rawCommand("PREPARE TRANSACTION {$this->stringSerializer->serializeValue($name)}");
133
        $this->open = false;
134
        $this->txCtl->notifyTransactionPrepared($name);
135
    }
136
}
137