MySQLColumnMeta::getLength()   D
last analyzed

Complexity

Conditions 22
Paths 22

Size

Total Lines 27
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 22

Importance

Changes 0
Metric Value
cc 22
eloc 25
nc 22
nop 0
dl 0
loc 27
ccs 25
cts 25
cp 1
crap 22
rs 4.1666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * Database Access Layer.
6
 * @author Doug Wright
7
 */
8
9
namespace DVDoug\DB;
10
11
use Exception;
12
use function sprintf;
13
use function strpos;
14
use function strtoupper;
15
16
  /**
17
   * Metadata about a database column.
18
   * @author Doug Wright
19
   */
20
  class MySQLColumnMeta implements ColumnMetaInterface
21
  {
22
      use DDLGeneration;
23
24
      /**
25
       * Database connection.
26
       */
27
      protected DatabaseInterface $connection;
28
29
      /**
30
       * Database name.
31
       */
32
      protected string $database;
33
34
      /**
35
       * Table name.
36
       */
37
      protected string $table;
38
39
      /**
40
       * Column name.
41
       */
42
      protected string $name;
43
44
      /**
45
       * Column type.
46
       */
47
      protected string $type;
48
49
      /**
50
       * Column length.
51
       */
52
      protected int $length;
53
54
      /**
55
       * Column precision.
56
       */
57
      protected ?int $precision;
58
59
      /**
60
       * Column scale.
61
       */
62
      protected ?int $scale;
63
64
      /**
65
       * Column nullable?
66
       */
67
      protected bool $isNullable;
68
69
      /**
70
       * Column max value.
71
       */
72
      protected ?string $maxValue;
73
74
      /**
75
       * Column min value.
76
       */
77
      protected ?string $minValue;
78
79
      /**
80
       * Number of distinct values.
81
       */
82
      protected int $distinctValues;
83
84
      /**
85
       * Constructor.
86
       * @param DatabaseInterface $aConnection connection to database
87
       * @param string            $aDatabase   database/schema name
88
       * @param string            $aTable      table name
89
       * @param string            $aColumnName column name
90
       */
91 16
      public function __construct(DatabaseInterface $aConnection, $aDatabase, $aTable, $aColumnName)
92
      {
93 16
          $this->connection = $aConnection;
94 16
          $this->database = $aDatabase;
95 16
          $this->table = $aTable;
96 16
          $this->name = $aColumnName;
97
98
          /*
99
           * Basic metadata from the schema
100
           */
101 16
          $statement = $this->connection->prepare('SELECT TABLE_SCHEMA,
102
                                                      TABLE_NAME,
103
                                                      COLUMN_NAME,
104
                                                      DATA_TYPE,
105
                                                      CHARACTER_MAXIMUM_LENGTH,
106
                                                      NUMERIC_PRECISION,
107
                                                      COALESCE(/*!56000 DATETIME_PRECISION, */NUMERIC_SCALE) AS SCALE,
108
                                                      IS_NULLABLE,
109
                                                      COLUMN_TYPE
110
                                               FROM INFORMATION_SCHEMA.COLUMNS
111
                                               WHERE TABLE_SCHEMA = :database
112
                                                     AND TABLE_NAME = :table_name
113
                                                     AND COLUMN_NAME = :column_name');
114 16
          $statement->bindParamToValue(':database', $this->database);
115 16
          $statement->bindParamToValue(':table_name', $this->table);
116 16
          $statement->bindParamToValue(':column_name', $this->name);
117 16
          $statement->execute();
0 ignored issues
show
Bug introduced by
The call to DVDoug\DB\StatementInterface::execute() has too few arguments starting with boundInputParams. ( Ignorable by Annotation )

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

117
          $statement->/** @scrutinizer ignore-call */ 
118
                      execute();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
118
119 16
          $meta = $statement->fetchAssoc(false);
120
121 16
          $this->type = strtoupper($meta['DATA_TYPE']);
122 16
          $this->length = $meta['CHARACTER_MAXIMUM_LENGTH'] ?: $meta['NUMERIC_PRECISION'];
123 16
          $this->precision = $meta['NUMERIC_PRECISION'];
124 16
          $this->scale = $meta['SCALE'];
125 16
          $this->isNullable = ($meta['IS_NULLABLE'] == 'YES');
126
127 16
          if (strpos($meta['COLUMN_TYPE'], 'unsigned') !== false) {
128 8
              $this->type .= ' UNSIGNED';
129
          }
130
131
          /*
132
           * Metadata from the data stored
133
           */
134 16
          $query = sprintf('SELECT COUNT(*) AS COUNT FROM (SELECT %s FROM %s.%s GROUP BY %s) distinctvalues',
135 16
                       $this->connection->quoteIdentifier($this->name),
136 16
                       $this->connection->quoteIdentifier($this->database),
137 16
                       $this->connection->quoteIdentifier($this->table),
138 16
                       $this->connection->quoteIdentifier($this->name));
139 16
          $this->distinctValues = $this->connection->query($query)->fetchAssoc(false)['COUNT'];
140
141 16
          $query = sprintf('SELECT MIN(%s) AS ROWMIN, MAX(%s) AS ROWMAX FROM %s.%s WHERE %s IS NOT NULL',
142 16
                       $this->connection->quoteIdentifier($this->name),
143 16
                       $this->connection->quoteIdentifier($this->name),
144 16
                       $this->connection->quoteIdentifier($this->database),
145 16
                       $this->connection->quoteIdentifier($this->table),
146 16
                       $this->connection->quoteIdentifier($this->name));
147 16
          $data = $this->connection->query($query)->fetchAssoc(false);
148 16
          $this->maxValue = $data['ROWMAX'];
149 16
          $this->minValue = $data['ROWMIN'];
150 16
      }
151
152
      /**
153
       * Get column name.
154
       */
155 16
      public function getName(): string
156
      {
157 16
          return $this->name;
158
      }
159
160
      /**
161
       * Get column type as suitable for MySQL.
162
       */
163 8
      public function getMySQLType(): string
164
      {
165 8
          return $this->type;
166
      }
167
168
      /**
169
       * Get column type as suitable for Oracle.
170
       *
171
       * @throws Exception
172
       */
173 8
      public function getOracleType(): string
174
      {
175 8
          switch ($this->type) {
176 8
        case 'BIT':
177 8
        case 'TINYINT':
178 8
        case 'TINYINT UNSIGNED':
179 8
        case 'SMALLINT':
180 8
        case 'SMALLINT UNSIGNED':
181 8
        case 'MEDIUMINT':
182 8
        case 'MEDIUMINT UNSIGNED':
183 8
        case 'INT':
184 8
        case 'INT UNSIGNED':
185 8
        case 'BIGINT':
186 8
        case 'BIGINT UNSIGNED':
187 4
        case 'DECIMAL':
188 4
        case 'DECIMAL UNSIGNED':
189 4
          return 'NUMBER';
190
191 4
        case 'FLOAT':
192 4
        case 'FLOAT UNSIGNED':
193
          return 'BINARY_FLOAT';
194
195 4
        case 'DOUBLE':
196 4
        case 'DOUBLE UNSIGNED':
197
          return 'BINARY_DOUBLE';
198
199 4
        case 'DATE':
200 4
        case 'DATETIME':
201
          if ($this->precision) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->precision of type integer|null is loosely compared to true; this is ambiguous if the integer can be 0. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
202
              return 'TIMESTAMP';
203
          } else {
204
              return 'DATE';
205
          }
206
207
          // no break
208 4
        case 'TIMESTAMP':
209
          return 'TIMESTAMP';
210
211 4
        case 'CHAR':
212 4
        case 'TIME':
213 4
        case 'YEAR':
214 4
          return 'CHAR';
215
216 4
        case 'ENUM':
217 4
        case 'SET':
218 4
        case 'VARCHAR':
219 4
          return 'NVARCHAR';
220
221
        case 'TINYBLOB':
222
        case 'SMALLBLOB':
223
        case 'BLOB':
224
        case 'MEDIUMBLOB':
225
        case 'LONGBLOB':
226
        case 'BINARY':
227
        case 'VARBINARY':
228
          return 'BLOB';
229
230
        case 'TINYTEXT':
231
        case 'SMALLTEXT':
232
        case 'TEXT':
233
        case 'MEDIUMTEXT':
234
        case 'LONGTEXT':
235
          return 'NCLOB';
236
237
        default:
238
          throw new Exception("Unknown conversion for column type {$this->type}");
239
      }
240
      }
241
242
      /**
243
       * Get length of column.
244
       */
245 12
      public function getLength(): int
246
      {
247 12
          switch ($this->getOriginalType()) {
248 12
        case 'BIT':
249 12
        case 'TINYINT':
250 12
        case 'TINYINT UNSIGNED':
251 12
        case 'SMALLINT':
252 12
        case 'SMALLINT UNSIGNED':
253 12
        case 'MEDIUMINT':
254 12
        case 'MEDIUMINT UNSIGNED':
255 12
        case 'INT':
256 12
        case 'INT UNSIGNED':
257 12
        case 'BIGINT':
258 12
        case 'BIGINT UNSIGNED':
259 8
        case 'DECIMAL':
260 8
        case 'DECIMAL UNSIGNED':
261 8
        case 'FLOAT':
262 8
        case 'FLOAT UNSIGNED':
263 8
        case 'DOUBLE':
264 8
        case 'DOUBLE UNSIGNED':
265 8
        case 'CHAR':
266 8
        case 'TIME':
267 8
        case 'YEAR':
268 8
        case 'VARCHAR':
269 12
          return $this->length;
270
        default:
271 4
          return 0;
272
      }
273
      }
274
275
      /**
276
       * Get column type as used by originating database.
277
       */
278 12
      public function getOriginalType(): string
279
      {
280 12
          return $this->type;
281
      }
282
283
      /**
284
       * Get column precision (number of digits).
285
       * @return int|null int for numeric columns, null for non-numeric
286
       */
287 16
      public function getPrecision(): ?int
288
      {
289 16
          return $this->precision;
290
      }
291
292
      /**
293
       * Get column scale (number of digits after decimal place).
294
       * @return int|null int for numeric columns, null for non-numeric
295
       */
296 16
      public function getScale(): ?int
297
      {
298 16
          return $this->scale;
299
      }
300
301
      /**
302
       * Get whether column is nullable.
303
       */
304 16
      public function isNullable(): bool
305
      {
306 16
          return $this->isNullable;
307
      }
308
309
      /**
310
       * Get max value.
311
       */
312
      public function getMaxValue(): ?string
313
      {
314
          return $this->maxValue;
315
      }
316
317
      /**
318
       * Get min value.
319
       */
320
      public function getMinValue(): ?string
321
      {
322
          return $this->minValue;
323
      }
324
325
      /**
326
       * The number of distinct values in this column.
327
       */
328 4
      public function getDistinctValueCount(): int
329
      {
330 4
          return $this->distinctValues;
331
      }
332
  }
333