Completed
Branch feature/pre-split (ca29cf)
by Anton
03:23
created

Driver::cachedQuery()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 26
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 15
nc 5
nop 5
dl 0
loc 26
rs 8.5806
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A Driver::queryCompiler() 0 7 1
A Driver::tableSchema() 0 7 1
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
9
namespace Spiral\Database\Entities;
10
11
use Psr\Log\LoggerInterface;
12
use Spiral\Cache\StoreInterface;
13
use Spiral\Core\FactoryInterface;
14
use Spiral\Database\Builders\DeleteQuery;
15
use Spiral\Database\Builders\InsertQuery;
16
use Spiral\Database\Builders\SelectQuery;
17
use Spiral\Database\Builders\UpdateQuery;
18
use Spiral\Database\Entities\Prototypes\PDODriver;
19
use Spiral\Database\Schemas\Prototypes\AbstractTable;
20
21
/**
22
 * Driver abstraction is responsible for DBMS specific set of functions and used by Databases to
23
 * hide implementation specific functionality. Extends PDODriver and adds ability to create driver
24
 * specific query builders and schemas (basically operates like a factory).
25
 */
26
abstract class Driver extends PDODriver
27
{
28
    /**
29
     * Schema table class.
30
     */
31
    const TABLE_SCHEMA_CLASS = '';
32
33
    /**
34
     * Commander used to execute commands. :).
35
     */
36
    const COMMANDER = '';
37
38
    /**
39
     * Query compiler class.
40
     */
41
    const QUERY_COMPILER = '';
42
43
    /**
44
     * Transaction level (count of nested transactions). Not all drives can support nested
45
     * transactions.
46
     *
47
     * @var int
48
     */
49
    private $transactionLevel = 0;
50
51
52
    /**
53
     * @var FactoryInterface
54
     */
55
    protected $factory = null;
56
57
    /**
58
     * @param string           $name
59
     * @param array            $options
60
     * @param FactoryInterface $factory Required to build instances of query builders and compilers.
61
     */
62
    public function __construct(string $name, array $options, FactoryInterface $factory)
63
    {
64
        parent::__construct($name, $options);
65
66
        $this->factory = $factory;
67
    }
68
69
    /**
70
     * Set cache store to be used by driver.
71
     *
72
     * @param StoreInterface $store
73
     *
74
     * @return self|$this
75
     */
76
    public function setStore(StoreInterface $store): Driver
77
    {
78
        $this->cacheStore = $store;
0 ignored issues
show
Bug introduced by
The property cacheStore does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
79
80
        return $this;
81
    }
82
83
    /**
84
     * Check if table exists.
85
     *
86
     * @param string $name
87
     *
88
     * @return bool
89
     */
90
    abstract public function hasTable(string $name): bool;
91
92
    /**
93
     * Clean (truncate) specified driver table.
94
     *
95
     * @param string $table Table name with prefix included.
96
     */
97
    abstract public function truncateData(string $table);
98
99
    /**
100
     * Get every available table name as array.
101
     *
102
     * @return array
103
     */
104
    abstract public function tableNames(): array;
105
106
    /**
107
     * Get Driver specific AbstractTable implementation.
108
     *
109
     * @param string $table  Table name without prefix included.
110
     * @param string $prefix Database specific table prefix, this parameter is not required,
111
     *                       but if provided all
112
     *                       foreign keys will be created using it.
113
     *
114
     * @return AbstractTable
115
     */
116
    public function tableSchema(string $table, string $prefix = ''): AbstractTable
117
    {
118
        return $this->factory->make(
119
            static::TABLE_SCHEMA_CLASS,
120
            ['driver' => $this, 'name' => $table, 'prefix' => $prefix]
121
        );
122
    }
123
124
    /**
125
     * Get instance of Driver specific QueryCompiler.
126
     *
127
     * @param string $prefix Database specific table prefix, used to quote table names and build
128
     *                       aliases.
129
     *
130
     * @return QueryCompiler
131
     */
132
    public function queryCompiler(string $prefix = ''): QueryCompiler
133
    {
134
        return $this->factory->make(
135
            static::QUERY_COMPILER,
136
            ['driver' => $this, 'quoter' => new Quoter($this, $prefix)]
137
        );
138
    }
139
140
    /**
141
     * Get InsertQuery builder with driver specific query compiler.
142
     *
143
     * @param string $prefix     Database specific table prefix, used to quote table names and build
144
     *                           aliases.
145
     * @param array  $parameters Initial builder parameters.
146
     *
147
     * @return InsertQuery
148
     */
149 View Code Duplication
    public function insertBuilder(string $prefix, array $parameters = []): InsertQuery
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
150
    {
151
        return $this->factory->make(
152
            InsertQuery::class,
153
            ['driver' => $this, 'compiler' => $this->queryCompiler($prefix)] + $parameters
154
        );
155
    }
156
157
    /**
158
     * Get SelectQuery builder with driver specific query compiler.
159
     *
160
     * @param string $prefix     Database specific table prefix, used to quote table names and build
161
     *                           aliases.
162
     * @param array  $parameters Initial builder parameters.
163
     *
164
     * @return SelectQuery
165
     */
166 View Code Duplication
    public function selectBuilder(string $prefix, array $parameters = []): SelectQuery
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
167
    {
168
        return $this->factory->make(
169
            SelectQuery::class,
170
            ['driver' => $this, 'compiler' => $this->queryCompiler($prefix)] + $parameters
171
        );
172
    }
173
174
    /**
175
     * Get DeleteQuery builder with driver specific query compiler.
176
     *
177
     * @param string $prefix     Database specific table prefix, used to quote table names and build
178
     *                           aliases.
179
     * @param array  $parameters Initial builder parameters.
180
     *
181
     * @return DeleteQuery
182
     */
183 View Code Duplication
    public function deleteBuilder(string $prefix, array $parameters = []): DeleteQuery
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
184
    {
185
        return $this->factory->make(
186
            DeleteQuery::class,
187
            ['driver' => $this, 'compiler' => $this->queryCompiler($prefix)] + $parameters
188
        );
189
    }
190
191
    /**
192
     * Get UpdateQuery builder with driver specific query compiler.
193
     *
194
     * @param string $prefix     Database specific table prefix, used to quote table names and build
195
     *                           aliases.
196
     * @param array  $parameters Initial builder parameters.
197
     *
198
     * @return UpdateQuery
199
     */
200 View Code Duplication
    public function updateBuilder(string $prefix, array $parameters = []): UpdateQuery
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
201
    {
202
        return $this->factory->make(
203
            UpdateQuery::class,
204
            ['driver' => $this, 'compiler' => $this->queryCompiler($prefix)] + $parameters
205
        );
206
    }
207
208
    /**
209
     * Handler responsible for schema related operations. Handlers responsible for sync flow of
210
     * tables and columns, provide logger to aggregate all logger operations.
211
     *
212
     * @param LoggerInterface $logger
213
     *
214
     * @return AbstractHandler
215
     */
216
    abstract public function getHandler(LoggerInterface $logger = null): AbstractHandler;
217
218
    /**
219
     * Start SQL transaction with specified isolation level (not all DBMS support it). Nested
220
     * transactions are processed using savepoints.
221
     *
222
     * @link   http://en.wikipedia.org/wiki/Database_transaction
223
     * @link   http://en.wikipedia.org/wiki/Isolation_(database_systems)
224
     *
225
     * @param string $isolationLevel
226
     *
227
     * @return bool
228
     */
229
    public function beginTransaction(string $isolationLevel = null): bool
230
    {
231
        ++$this->transactionLevel;
232
233
        if ($this->transactionLevel == 1) {
234
            if (!empty($isolationLevel)) {
235
                $this->isolationLevel($isolationLevel);
236
            }
237
238
            if ($this->isProfiling()) {
239
                $this->logger()->info('Begin transaction');
240
            }
241
242
            return $this->getPDO()->beginTransaction();
243
        }
244
245
        $this->savepointCreate($this->transactionLevel);
246
247
        return true;
248
    }
249
250
    /**
251
     * Commit the active database transaction.
252
     *
253
     * @return bool
254
     */
255 View Code Duplication
    public function commitTransaction(): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
256
    {
257
        --$this->transactionLevel;
258
259
        if ($this->transactionLevel == 0) {
260
            if ($this->isProfiling()) {
261
                $this->logger()->info('Commit transaction');
262
            }
263
264
            return $this->getPDO()->commit();
265
        }
266
267
        $this->savepointRelease($this->transactionLevel + 1);
268
269
        return true;
270
    }
271
272
    /**
273
     * Rollback the active database transaction.
274
     *
275
     * @return bool
276
     */
277 View Code Duplication
    public function rollbackTransaction(): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
278
    {
279
        --$this->transactionLevel;
280
281
        if ($this->transactionLevel == 0) {
282
            if ($this->isProfiling()) {
283
                $this->logger()->info('Rollback transaction');
284
            }
285
286
            return $this->getPDO()->rollBack();
287
        }
288
289
        $this->savepointRollback($this->transactionLevel + 1);
290
291
        return true;
292
    }
293
294
    /**
295
     * Set transaction isolation level, this feature may not be supported by specific database
296
     * driver.
297
     *
298
     * @param string $level
299
     */
300
    protected function isolationLevel(string $level)
301
    {
302
        if ($this->isProfiling()) {
303
            $this->logger()->info("Set transaction isolation level to '{$level}'");
304
        }
305
306
        if (!empty($level)) {
307
            $this->statement("SET TRANSACTION ISOLATION LEVEL {$level}");
308
        }
309
    }
310
311
    /**
312
     * Create nested transaction save point.
313
     *
314
     * @link http://en.wikipedia.org/wiki/Savepoint
315
     *
316
     * @param string $name Savepoint name/id, must not contain spaces and be valid database
317
     *                     identifier.
318
     */
319 View Code Duplication
    protected function savepointCreate(string $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
320
    {
321
        if ($this->isProfiling()) {
322
            $this->logger()->info("Creating savepoint '{$name}'");
323
        }
324
325
        $this->statement('SAVEPOINT ' . $this->identifier("SVP{$name}"));
326
    }
327
328
    /**
329
     * Commit/release savepoint.
330
     *
331
     * @link http://en.wikipedia.org/wiki/Savepoint
332
     *
333
     * @param string $name Savepoint name/id, must not contain spaces and be valid database
334
     *                     identifier.
335
     */
336 View Code Duplication
    protected function savepointRelease(string $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
337
    {
338
        if ($this->isProfiling()) {
339
            $this->logger()->info("Releasing savepoint '{$name}'");
340
        }
341
342
        $this->statement('RELEASE SAVEPOINT ' . $this->identifier("SVP{$name}"));
343
    }
344
345
    /**
346
     * Rollback savepoint.
347
     *
348
     * @link http://en.wikipedia.org/wiki/Savepoint
349
     *
350
     * @param string $name Savepoint name/id, must not contain spaces and be valid database
351
     *                     identifier.
352
     */
353 View Code Duplication
    protected function savepointRollback(string $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
354
    {
355
        if ($this->isProfiling()) {
356
            $this->logger()->info("Rolling back savepoint '{$name}'");
357
        }
358
        $this->statement('ROLLBACK TO SAVEPOINT ' . $this->identifier("SVP{$name}"));
359
    }
360
}
361