Passed
Push — master ( f71b04...7dcb0c )
by P.R.
12:30
created

MySqlRoutineLoaderWorker   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 287
Duplicated Lines 0 %

Test Coverage

Coverage 66.67%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 80
dl 0
loc 287
ccs 46
cts 69
cp 0.6667
rs 10
c 3
b 0
f 0
wmc 26

14 Methods

Rating   Name   Duplication   Size   Complexity  
A saveColumnTypesExact() 0 13 3
A createRoutineLoader() 0 7 1
A readConfigFile() 0 7 1
A phpStratumMetadataRevision() 0 3 1
B saveColumnTypesMaxLength() 0 39 8
A createEscaper() 0 3 1
A initRdbmsSpecific() 0 3 1
A fetchRdbmsMetadata() 0 3 1
A createDataTypeHelper() 0 3 1
A dropStoredRoutine() 0 3 1
A maxCharacters() 0 16 3
A connect() 0 13 1
A disconnect() 0 6 2
A fetchColumnTypes() 0 8 1
1
<?php
2
declare(strict_types=1);
3
4
namespace SetBased\Stratum\MySql\Backend;
5
6
use SetBased\Stratum\Backend\RoutineLoaderWorker;
7
use SetBased\Stratum\Common\Backend\CommonRoutineLoaderWorker;
8
use SetBased\Stratum\Common\Helper\CommonDataTypeHelper;
9
use SetBased\Stratum\Common\Loader\CommonRoutineLoader;
10
use SetBased\Stratum\Common\Loader\Helper\EscapeHelper;
11
use SetBased\Stratum\Common\Loader\Helper\LoaderContext;
12
use SetBased\Stratum\Middle\Helper\RowSetHelper;
13
use SetBased\Stratum\MySql\Loader\Helper\MySqlDataTypeHelper;
14
use SetBased\Stratum\MySql\Loader\Helper\MySqlEscapeHelper;
15
use SetBased\Stratum\MySql\Loader\Helper\SqlModeHelper;
16
use SetBased\Stratum\MySql\Loader\MySqlRoutineLoader;
17
use SetBased\Stratum\MySql\MySqlDataLayer;
18
use SetBased\Stratum\MySql\MySqlDefaultConnector;
19
use SetBased\Stratum\MySql\MySqlMetadataLayer;
20
21
/**
22
 * Command for loading stored routines into a MySQL instance from pseudo SQL files.
23
 */
24
class MySqlRoutineLoaderWorker extends CommonRoutineLoaderWorker implements RoutineLoaderWorker
25
{
26
  //--------------------------------------------------------------------------------------------------------------------
27
  /**
28
   * The maximum column size in bytes.
29
   */
30
  const MAX_COLUMN_SIZE = 65532;
31
32
  /**
33
   * Maximum length of a char.
34
   */
35
  const MAX_LENGTH_CHAR = 255;
36
37
  /**
38
   * Maximum length of a varchar.
39
   */
40
  const MAX_LENGTH_VARCHAR = 4096;
41
42
  /**
43
   * Maximum length of a binary.
44
   */
45
  const MAX_LENGTH_BINARY = 255;
46
47
  /**
48
   * Maximum length of a varbinary.
49
   */
50
  const MAX_LENGTH_VARBINARY = 4096;
51
52
  /**
53
   * The metadata layer.
54
   *
55
   * @var MySqlMetadataLayer|null
56
   */
57
  protected ?MySqlMetadataLayer $dl;
58
59
  /**
60
   * The default character set under which the stored routine will be loaded and run.
61
   *
62
   * @var string
63
   */
64
  private string $characterSetClient;
65
66
  /**
67
   * Details of all character sets.
68
   *
69
   * @var array[]|null
70
   */
71
  private ?array $characterSets = null;
72
73
  /**
74
   * The default collate under which the stored routine will be loaded and run.
75
   *
76
   * @var string
77
   */
78
  private string $collationConnection;
79
80
  /**
81
   * The SQL mode under which the stored routine will be loaded and run.
82
   *
83
   * @var string
84
   */
85
  private string $sqlMode;
86
87
  /**
88
   * The helper object for SQL modes.
89
   *
90
   * @var SqlModeHelper
91
   */
92
  private SqlModeHelper $sqlModeHelper;
93
94
  //--------------------------------------------------------------------------------------------------------------------
95
  /**
96
   * Disconnects from MySQL instance.
97
   */
98
  public function disconnect(): void
99
  {
100
    if ($this->dl!==null)
101
    {
102
      $this->dl->disconnect();
103
      $this->dl = null;
104
    }
105
  }
106
107
  //--------------------------------------------------------------------------------------------------------------------
108
  /**
109
   * Connects to a MySQL instance.
110
   */
111
  protected function connect(): void
112
  {
113
    $host     = $this->config->manString('database.host');
114
    $user     = $this->config->manString('database.user');
115
    $password = $this->config->manString('database.password');
116
    $database = $this->config->manString('database.database');
117
    $port     = $this->config->manInt('database.port', 3306);
118
119
    $connector = new MySqlDefaultConnector($host, $user, $password, $database, $port);
120
    $dataLayer = new MySqlDataLayer($connector);
121
    $dataLayer->connect();
122
123
    $this->dl = new MySqlMetadataLayer($dataLayer, $this->io);
124
  }
125
126
  //--------------------------------------------------------------------------------------------------------------------
127
  /**
128
   * @inheritdoc
129
   */
130
  protected function createDataTypeHelper(): CommonDataTypeHelper
131
  {
132
    return new MySqlDataTypeHelper();
133
  }
134 1
135
  //--------------------------------------------------------------------------------------------------------------------
136 1
  /**
137
   * @@inheritdoc
138 1
   */
139 1
  protected function createEscaper(): EscapeHelper
140 1
  {
141 1
    return new MySqlEscapeHelper($this->dl);
0 ignored issues
show
Bug introduced by
It seems like $this->dl can also be of type null; however, parameter $dl of SetBased\Stratum\MySql\L...peHelper::__construct() does only seem to accept SetBased\Stratum\MySql\MySqlMetadataLayer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

141
    return new MySqlEscapeHelper(/** @scrutinizer ignore-type */ $this->dl);
Loading history...
142 1
  }
143 1
144 1
  //--------------------------------------------------------------------------------------------------------------------
145
  /**
146 1
   * @inheritdoc
147
   */
148 1
  protected function createRoutineLoader(LoaderContext $context): CommonRoutineLoader
149 1
  {
150
    return new MySqlRoutineLoader($this->io,
151 1
                                  $this->dl,
0 ignored issues
show
Bug introduced by
It seems like $this->dl can also be of type null; however, parameter $dl of SetBased\Stratum\MySql\L...neLoader::__construct() does only seem to accept SetBased\Stratum\MySql\MySqlMetadataLayer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

151
                                  /** @scrutinizer ignore-type */ $this->dl,
Loading history...
152
                                  $this->sqlModeHelper,
153 1
                                  $this->characterSetClient,
154
                                  $this->collationConnection);
155
  }
156
157
  //--------------------------------------------------------------------------------------------------------------------
158
  /**
159
   * @inheritdoc
160 1
   */
161
  protected function dropStoredRoutine(array $rdbmsMetadata): void
162 1
  {
163
    $this->dl->dropRoutine($rdbmsMetadata['routine_type'], $rdbmsMetadata['routine_name']);
0 ignored issues
show
Bug introduced by
The method dropRoutine() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

163
    $this->dl->/** @scrutinizer ignore-call */ 
164
               dropRoutine($rdbmsMetadata['routine_type'], $rdbmsMetadata['routine_name']);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
164 1
  }
165
166
  //--------------------------------------------------------------------------------------------------------------------
167
  /**
168
   * @inheritdoc
169
   */
170
  protected function fetchColumnTypes(): void
171 1
  {
172
    $columns = $this->dl->allTableColumns();
173
174 1
    $this->saveColumnTypesExact($columns);
175
    $this->saveColumnTypesMaxLength($columns);
176
177 1
    $this->io->text(sprintf('Selected %d column types for substitution', sizeof($columns)));
178
  }
179
180
  //--------------------------------------------------------------------------------------------------------------------
181
  /**
182
   * @inheritdoc
183 1
   */
184
  protected function fetchRdbmsMetadata(): array
185
  {
186
    return $this->dl->allRoutines();
187
  }
188
189
  //--------------------------------------------------------------------------------------------------------------------
190
  /**
191
   * @inheritdoc
192
   */
193
  protected function initRdbmsSpecific(): void
194
  {
195
    $this->sqlModeHelper = new SqlModeHelper($this->dl, $this->sqlMode);
0 ignored issues
show
Bug introduced by
It seems like $this->dl can also be of type null; however, parameter $dl of SetBased\Stratum\MySql\L...deHelper::__construct() does only seem to accept SetBased\Stratum\MySql\MySqlMetadataLayer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

195
    $this->sqlModeHelper = new SqlModeHelper(/** @scrutinizer ignore-type */ $this->dl, $this->sqlMode);
Loading history...
196
  }
197 1
198
  //--------------------------------------------------------------------------------------------------------------------
199 1
  /**
200
   * @inheritdoc
201
   */
202
  protected function phpStratumMetadataRevision(): string
203
  {
204
    return parent::phpStratumMetadataRevision().'.1';
205
  }
206
207
  //--------------------------------------------------------------------------------------------------------------------
208
  /**
209
   * @inheritdoc
210
   */
211
  protected function readConfigFile(): void
212
  {
213 1
    parent::readConfigFile();
214
215
    $this->characterSetClient  = $this->config->manString('database.character_set_client', 'utf8mb4');
216 1
    $this->collationConnection = $this->config->manString('database.collation_connection', 'utf8mb4_general_ci');
217 1
    $this->sqlMode             = $this->config->manString('loader.sql_mode');
218
  }
219 1
220
  //--------------------------------------------------------------------------------------------------------------------
221
  /**
222
   * Returns the maximum number of characters in a VARCHAR or CHAR.
223 1
   *
224
   * @param string $characterSetName The name of the character set of the column.
225
   */
226
  private function maxCharacters(string $characterSetName): ?int
227
  {
228
    if ($this->characterSets===null)
229
    {
230
      $this->characterSets = $this->dl->allCharacterSets();
231
    }
232
233
    $key = RowSetHelper::searchInRowSet($this->characterSets, 'character_set_name', $characterSetName);
0 ignored issues
show
Bug introduced by
It seems like $this->characterSets can also be of type null; however, parameter $rows of SetBased\Stratum\Middle\...elper::searchInRowSet() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

233
    $key = RowSetHelper::searchInRowSet(/** @scrutinizer ignore-type */ $this->characterSets, 'character_set_name', $characterSetName);
Loading history...
234
    if ($key===null)
235
    {
236
      return null;
237
    }
238
239
    $size = $this->characterSets[$key]['maxlen'];
240 1
241
    return (int)floor(self::MAX_COLUMN_SIZE / $size);
242 1
  }
243 1
244
  //--------------------------------------------------------------------------------------------------------------------
245 1
  /**
246
   * Saves the exact column types as type hints.
247 1
   *
248 1
   * @param array[] $columns The details of all table columns.
249
   */
250 1
  private function saveColumnTypesExact(array $columns): void
251
  {
252
    foreach ($columns as $column)
253
    {
254
      $hint  = $column['table_name'].'.'.$column['column_name'];
255
      $value = $column['column_type'];
256
257
      if ($column['character_set_name']!==null)
258
      {
259
        $value .= ' character set '.$column['character_set_name'];
260
      }
261
262
      $this->addTypeHint($hint, $value);
263
    }
264
  }
265
266
  //--------------------------------------------------------------------------------------------------------------------
267
  /**
268
   * Saves the column types with maximum length as type hints.
269
   *
270
   * @param array[] $columns The details of all table columns.
271
   */
272
  private function saveColumnTypesMaxLength(array $columns): void
273
  {
274
    foreach ($columns as $column)
275
    {
276
      $hint = $column['table_name'].'.'.$column['column_name'].'%max';
277
278
      switch ($column['data_type'])
279
      {
280
        case 'char':
281
        case 'varchar':
282
          $max = $this->maxCharacters($column['character_set_name']);
283
          if ($max!==null)
284
          {
285 1
            $value = sprintf('%s(%d) character set %s',
286
                             $column['data_type'],
287
                             $max,
288 1
                             $column['character_set_name']);
289 1
          }
290
          else
291 1
          {
292
            $value = null;
293 1
          }
294
          break;
295 1
296
        case 'binary':
297
          $value = sprintf('%s(%d)', $column['data_type'], self::MAX_LENGTH_BINARY);
298 1
          break;
299
300
        case 'varbinary':
301
          $value = sprintf('%s(%d)', $column['data_type'], self::MAX_LENGTH_VARBINARY);
302
          break;
303 1
304 1
        default:
305 1
          $value = null;
306
      }
307 1
308
      if ($value!==null)
309
      {
310
        $this->addTypeHint($hint, $value);
311
      }
312
    }
313
  }
314 1
315
  //--------------------------------------------------------------------------------------------------------------------
316
}
317
318
//----------------------------------------------------------------------------------------------------------------------
319