Passed
Push — dev ( 1bbda8...5c17e2 )
by Def
18:33 queued 09:04
created

TransactionPDOOracle::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 0
nc 1
nop 1
dl 0
loc 2
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Oracle\PDO;
6
7
use Psr\Log\LogLevel;
8
use Throwable;
9
use Yiisoft\Db\AwareTrait\LoggerAwareTrait;
10
use Yiisoft\Db\Connection\ConnectionPDOInterface;
11
use Yiisoft\Db\Exception\Exception;
12
use Yiisoft\Db\Exception\InvalidConfigException;
13
use Yiisoft\Db\Exception\NotSupportedException;
14
use Yiisoft\Db\Transaction\TransactionInterface;
15
16
final class TransactionPDOOracle implements TransactionInterface
17
{
18
    use LoggerAwareTrait;
19
20
    /**
21
     * A constant representing the transaction isolation level `READ UNCOMMITTED`.
22
     *
23
     * @link http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels}
24
     */
25
    public const READ_UNCOMMITTED = 'READ UNCOMMITTED';
26
27
    /**
28
     * A constant representing the transaction isolation level `READ COMMITTED`.
29
     *
30
     * @link http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels}
31
     */
32
    public const READ_COMMITTED = 'READ COMMITTED';
33
34
    /**
35
     * A constant representing the transaction isolation level `REPEATABLE READ`.
36
     *
37
     * @link http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels}
38
     */
39
    public const REPEATABLE_READ = 'REPEATABLE READ';
40
41
    /**
42
     * A constant representing the transaction isolation level `SERIALIZABLE`.
43
     *
44
     * {@see http://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Isolation_levels}
45
     */
46
    public const SERIALIZABLE = 'SERIALIZABLE';
47
48
    private int $level = 0;
49
50 8
    public function __construct(private ConnectionPDOInterface $db)
51
    {
52
    }
53
54 8
    public function begin(?string $isolationLevel = null): void
55
    {
56 8
        $this->db->open();
57
58 8
        if ($this->level === 0) {
59 8
            if ($isolationLevel !== null) {
60 2
                $this->db->getSchema()->setTransactionIsolationLevel($isolationLevel);
61
            }
62
63 8
            $this->logger?->log(
64
                LogLevel::DEBUG,
65 8
                'Begin transaction' . ($isolationLevel ? ' with isolation level ' . $isolationLevel : '')
66
                . ' ' . __METHOD__
67
            );
68 8
            $this->db->getPDO()->beginTransaction();
69 8
            $this->level = 1;
70 8
            return;
71
        }
72
73 2
        $schema = $this->db->getSchema();
74
75 2
        if ($schema->supportsSavepoint()) {
76 1
            $this->logger?->log(LogLevel::DEBUG, 'Set savepoint ' . $this->level . ' ' . __METHOD__);
77 1
            $schema->createSavepoint('LEVEL' . $this->level);
78
        } else {
79 1
            $this->logger?->log(
80
                LogLevel::DEBUG,
81 1
                'Transaction not started: nested transaction not supported ' . __METHOD__
82
            );
83 1
            throw new NotSupportedException('Transaction not started: nested transaction not supported.');
84
        }
85
86 1
        $this->level++;
87
    }
88
89
    /**
90
     * Commits a transaction.
91
     *
92
     * @throws Exception|Throwable if the transaction is not active
93
     */
94 5
    public function commit(): void
95
    {
96 5
        if (!$this->isActive()) {
97
            throw new Exception('Failed to commit transaction: transaction was inactive.');
98
        }
99
100 5
        $this->level--;
101
102 5
        if ($this->level === 0) {
103 5
            $this->logger?->log(LogLevel::DEBUG, 'Commit transaction ' . __METHOD__);
104 5
            $this->db->getPDO()->commit();
105 5
            return;
106
        }
107
108
        $schema = $this->db->getSchema();
109
110
        if ($schema->supportsSavepoint()) {
111
            $this->logger?->log(LogLevel::DEBUG, 'Release savepoint ' . $this->level . ' ' . __METHOD__);
112
            $schema->releaseSavepoint('LEVEL' . $this->level);
113
        } else {
114
            $this->logger?->log(
115
                LogLevel::INFO,
116
                'Transaction not committed: nested transaction not supported ' . __METHOD__
117
            );
118
        }
119
    }
120
121 5
    public function getLevel(): int
122
    {
123 5
        return $this->level;
124
    }
125
126 8
    public function isActive(): bool
127
    {
128 8
        return $this->level > 0 && $this->db->isActive();
129
    }
130
131
    /**
132
     * @throws Exception|InvalidConfigException|Throwable
133
     */
134 4
    public function rollBack(): void
135
    {
136 4
        if (!$this->isActive()) {
137
            /**
138
             * do nothing if transaction is not active: this could be the transaction is committed but the event handler
139
             * to "commitTransaction" throw an exception
140
             */
141
            return;
142
        }
143
144 4
        $this->level--;
145 4
        if ($this->level === 0) {
146 3
            $this->logger?->log(LogLevel::INFO, 'Roll back transaction ' . __METHOD__);
147 3
            $this->db->getPDO()->rollBack();
148 3
            return;
149
        }
150
151 1
        $schema = $this->db->getSchema();
152 1
        if ($schema->supportsSavepoint()) {
153 1
            $this->logger?->log(LogLevel::DEBUG, 'Roll back to savepoint ' . $this->level . ' ' . __METHOD__);
154 1
            $schema->rollBackSavepoint('LEVEL' . $this->level);
155
        } else {
156
            $this->logger?->log(
157
                LogLevel::INFO,
158
                'Transaction not rolled back: nested transaction not supported ' . __METHOD__
159
            );
160
        }
161
    }
162
163
    public function setIsolationLevel(string $level): void
164
    {
165
        if (!$this->isActive()) {
166
            throw new Exception('Failed to set isolation level: transaction was inactive.');
167
        }
168
169
        $this->logger?->log(
170
            LogLevel::DEBUG,
171
            'Setting transaction isolation level to ' . $this->level . ' ' . __METHOD__
172
        );
173
        $this->db->getSchema()->setTransactionIsolationLevel($level);
174
    }
175
}
176