Completed
Push — master ( 3bb391...1a7c41 )
by P.R.
03:42
created

MySqlMetaDataLayer::executeNone()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace SetBased\Stratum\MySql;
5
6
use SetBased\Stratum\Backend\StratumStyle;
7
use SetBased\Stratum\Middle\Exception\ResultException;
8
use SetBased\Stratum\MySql\Exception\MySqlQueryErrorException;
9
10
/**
11
 * Data layer for retrieving metadata and loading stored routines.
12
 */
13
class MySqlMetaDataLayer
14
{
15
  //--------------------------------------------------------------------------------------------------------------------
16
  /**
17
   * The connection to the MySQL instance.
18
   *
19
   * @var MySqlDataLayer
20
   */
21
  private $dl;
22
23
  /**
24
   * The Output decorator.
25
   *
26
   * @var StratumStyle
27
   */
28
  private $io;
29
30
  //--------------------------------------------------------------------------------------------------------------------
31
  /**
32
   * MySqlMetaDataLayer constructor.
33
   *
34
   * @param MySqlDataLayer $dl The connection to the MySQL instance.
35
   * @param StratumStyle   $io The Output decorator.
36
   */
37 1
  public function __construct(MySqlDataLayer $dl, StratumStyle $io)
38
  {
39 1
    $this->dl = $dl;
40 1
    $this->io = $io;
41 1
  }
42
43
  //--------------------------------------------------------------------------------------------------------------------
44
  /**
45
   * Selects the details of all character sets.
46
   *
47
   * @return array[]
48
   *
49
   * @throws MySqlQueryErrorException
50
   */
51 1
  public function allCharacterSets(): array
52
  {
53 1
    $sql = "
54
select CHARACTER_SET_NAME  as  character_set_name
55
,      MAXLEN              as  maxlen
56
from   information_schema.CHARACTER_SETS
57
order by CHARACTER_SET_NAME";
58
59 1
    return $this->executeRows($sql);
60
  }
61
62
  //--------------------------------------------------------------------------------------------------------------------
63
  /**
64
   * Selects metadata of tables with a label column.
65
   *
66
   * @return array[]
67
   *
68
   * @throws MySqlQueryErrorException
69
   */
70 1
  public function allLabelTables(): array
71
  {
72 1
    $sql = "
73
select t1.TABLE_NAME   as  table_name
74
,      t1.COLUMN_NAME  as  id
75
,      t2.COLUMN_NAME  as  label
76
from       information_schema.COLUMNS t1
77
inner join information_schema.COLUMNS t2 on t1.table_name = t2.table_name
78
where t1.table_schema = database()
79
and   t1.extra        = 'auto_increment'
80
and   t2.table_schema = database()
81
and   t2.column_name like '%%\\_label'";
82
83 1
    return $this->executeRows($sql);
84
  }
85
86
  //--------------------------------------------------------------------------------------------------------------------
87
  /**
88
   * Selects all routines in the current schema.
89
   *
90
   * @return array[]
91
   *
92
   * @throws MySqlQueryErrorException
93
   */
94 1
  public function allRoutines(): array
95
  {
96 1
    $sql = "
97
select ROUTINE_NAME          as  routine_name                            
98
,      ROUTINE_TYPE          as  routine_type           
99
,      SQL_MODE              as  sql_mode       
100
,      CHARACTER_SET_CLIENT  as  character_set_client                   
101
,      COLLATION_CONNECTION  as  collation_connection                   
102
from  information_schema.ROUTINES
103
where ROUTINE_SCHEMA = database()
104
and   ROUTINE_TYPE in ('PROCEDURE', 'FUNCTION')
105
order by routine_name";
106
107 1
    return $this->executeRows($sql);
108
  }
109
110
  //--------------------------------------------------------------------------------------------------------------------
111
  /**
112
   * Selects metadata of all columns of all tables.
113
   *
114
   * @return array[]
115
   *
116
   * @throws MySqlQueryErrorException
117
   */
118 1
  public function allTableColumns(): array
119
  {
120 1
    $sql = "
121
(
122
  select TABLE_NAME                as  table_name                                                       
123
  ,      COLUMN_NAME               as  column_name                                 
124
  ,      COLUMN_TYPE               as  column_type                                 
125
  ,      DATA_TYPE                 as  data_type                                 
126
  ,      CHARACTER_MAXIMUM_LENGTH  as  character_maximum_length                                 
127
  ,      CHARACTER_SET_NAME        as  character_set_name                                 
128
  ,      COLLATION_NAME            as  collation_name                                 
129
  ,      NUMERIC_PRECISION         as  numeric_precision                                 
130
  ,      NUMERIC_SCALE             as  numeric_scale                                 
131
  from   information_schema.COLUMNS
132
  where  TABLE_SCHEMA = database()
133
  and    TABLE_NAME  rlike '^[a-zA-Z0-9_]*$'
134
  and    COLUMN_NAME rlike '^[a-zA-Z0-9_]*$'
135
  order by TABLE_NAME
136
  ,        ORDINAL_POSITION
137
)
138
139
union all
140
141
(
142
  select concat(TABLE_SCHEMA,'.',TABLE_NAME)  as  table_name                                               
143
  ,      COLUMN_NAME                          as  column_name                     
144
  ,      COLUMN_TYPE                          as  column_type                     
145
  ,      DATA_TYPE                            as  data_type                     
146
  ,      CHARACTER_MAXIMUM_LENGTH             as  character_maximum_length                     
147
  ,      CHARACTER_SET_NAME                   as  character_set_name                     
148
  ,      COLLATION_NAME                       as  collation_name                     
149
  ,      NUMERIC_PRECISION                    as  numeric_precision                     
150
  ,      NUMERIC_SCALE                        as  numeric_scale                     
151
  from   information_schema.COLUMNS
152
  where  TABLE_NAME  rlike '^[a-zA-Z0-9_]*$'
153
  and    COLUMN_NAME rlike '^[a-zA-Z0-9_]*$'
154
  order by TABLE_SCHEMA
155
  ,        TABLE_NAME
156
  ,        ORDINAL_POSITION
157
)
158
";
159
160 1
    return $this->executeRows($sql);
161
  }
162
163
  //--------------------------------------------------------------------------------------------------------------------
164
  /**
165
   * Selects all table names in a schema.
166
   *
167
   * @param string $schemaName The name of the schema.
168
   *
169
   * @return array[]
170
   *
171
   * @throws MySqlQueryErrorException
172
   */
173
  public function allTablesNames(string $schemaName): array
174
  {
175
    $sql = sprintf("
176
select TABLE_NAME  as  table_name
177
from   information_schema.TABLES
178
where  TABLE_SCHEMA = %s
179
and    TABLE_TYPE   = 'BASE TABLE'
180
order by TABLE_NAME", $this->dl->quoteString($schemaName));
181
182
    return $this->executeRows($sql);
183
  }
184
185
  //--------------------------------------------------------------------------------------------------------------------
186
  /**
187
   * Class a stored procedure without arguments.
188
   *
189
   * @param string $procedureName The name of the procedure.
190
   *
191
   * @throws MySqlQueryErrorException
192
   */
193
  public function callProcedure(string $procedureName): void
194
  {
195
    $sql = sprintf('call %s()', $procedureName);
196
197
    $this->executeNone($sql);
198
  }
199
200
  //--------------------------------------------------------------------------------------------------------------------
201
  /**
202
   * Checks if a table exists in the current schema.
203
   *
204
   * @param string $tableName The name of the table.
205
   *
206
   * @return bool
207
   *
208
   * @throws MySqlQueryErrorException
209
   * @throws ResultException
210
   */
211
  public function checkTableExists(string $tableName): bool
212
  {
213
    $sql = sprintf('
214
select 1
215
from   information_schema.TABLES
216
where table_schema = database()
217
and   table_name   = %s', $this->dl->quoteString($tableName));
218
219
    return !empty($this->executeSingleton0($sql));
220
  }
221
222
  //--------------------------------------------------------------------------------------------------------------------
223
  /**
224
   * Describes a table.
225
   *
226
   * @param string $tableName The table name.
227
   *
228
   * @return array[]
229
   *
230
   * @throws MySqlQueryErrorException
231
   */
232
  public function describeTable(string $tableName): array
233
  {
234
    $sql = sprintf('describe `%s`', $tableName);
235
236
    return $this->executeRows($sql);
237
  }
238
239
  //--------------------------------------------------------------------------------------------------------------------
240
  /**
241
   * Closes the connection to the MySQL instance, if connected.
242
   */
243 1
  public function disconnect(): void
244
  {
245 1
    if ($this->dl!==null)
246
    {
247 1
      $this->dl->disconnect();
248 1
      $this->dl = null;
249
    }
250 1
  }
251
252
  //--------------------------------------------------------------------------------------------------------------------
253
  /**
254
   * Drops a routine if it exists.
255
   *
256
   * @param string $routineType The type of the routine (function of procedure).
257
   * @param string $routineName The name of the routine.
258
   *
259
   * @throws MySqlQueryErrorException
260
   */
261
  public function dropRoutine(string $routineType, string $routineName): void
262
  {
263
    $sql = sprintf('drop %s if exists `%s`', $routineType, $routineName);
264
265
    $this->executeNone($sql);
266
  }
267
268
  //--------------------------------------------------------------------------------------------------------------------
269
  /**
270
   * Drops a temporary table.
271
   *
272
   * @param string $tableName the name of the temporary table.
273
   *
274
   * @throws MySqlQueryErrorException
275
   */
276
  public function dropTemporaryTable(string $tableName): void
277
  {
278
    $sql = sprintf('drop temporary table `%s`', $tableName);
279
280
    $this->executeNone($sql);
281
  }
282
283
  //--------------------------------------------------------------------------------------------------------------------
284
  /**
285
   * @param string $sql The SQL statement.
286
   *
287
   * @return int The number of affected rows (if any).
288
   *
289
   * @throws MySqlQueryErrorException
290
   */
291 1
  public function executeNone(string $sql): int
292
  {
293 1
    $this->logQuery($sql);
294
295 1
    return $this->dl->executeNone($sql);
296
  }
297
298
  //--------------------------------------------------------------------------------------------------------------------
299
  /**
300
   * Executes a query that returns 0 or 1 row.
301
   * Throws an exception if the query selects 2 or more rows.
302
   *
303
   * @param string $sql The SQL statement.
304
   *
305
   * @return array|null The selected row.
306
   *
307
   * @throws MySqlQueryErrorException
308
   * @throws ResultException
309
   */
310
  public function executeRow0(string $sql): ?array
311
  {
312
    $this->logQuery($sql);
313
314
    return $this->dl->executeRow0($sql);
315
  }
316
317
  //--------------------------------------------------------------------------------------------------------------------
318
  /**
319
   * Executes a query that returns 1 and only 1 row.
320
   * Throws an exception if the query selects none, 2 or more rows.
321
   *
322
   * @param string $sql The SQL statement.
323
   *
324
   * @return array The selected row.
325
   *
326
   * @throws MySqlQueryErrorException
327
   * @throws ResultException
328
   */
329
  public function executeRow1(string $sql): array
330
  {
331
    $this->logQuery($sql);
332
333
    return $this->dl->executeRow1($sql);
334
  }
335
336
  //--------------------------------------------------------------------------------------------------------------------
337
  /**
338
   * Executes a query that returns 0 or more rows.
339
   *
340
   * @param string $sql The SQL statement.
341
   *
342
   * @return array[]
343
   *
344
   * @throws MySqlQueryErrorException
345
   */
346 1
  public function executeRows(string $sql): array
347
  {
348 1
    $this->logQuery($sql);
349
350 1
    return $this->dl->executeRows($sql);
351
  }
352
353
  //--------------------------------------------------------------------------------------------------------------------
354
  /**
355
   * Executes a query that returns 0 or 1 row.
356
   * Throws an exception if the query selects 2 or more rows.
357
   *
358
   * @param string $sql The SQL statement.
359
   *
360
   * @return mixed The selected row.
361
   *
362
   * @throws MySqlQueryErrorException
363
   * @throws ResultException
364
   */
365
  public function executeSingleton0(string $sql)
366
  {
367
    $this->logQuery($sql);
368
369
    return $this->dl->executeSingleton0($sql);
370
  }
371
  //--------------------------------------------------------------------------------------------------------------------
372
  /**
373
   * Executes a query that returns 1 and only 1 row with 1 column.
374
   * Throws an exception if the query selects none, 2 or more rows.
375
   *
376
   * @param string $sql The SQL statement.
377
   *
378
   * @return mixed The selected row.
379
   *
380
   * @throws MySqlQueryErrorException
381
   * @throws ResultException
382
   */
383 1
  public function executeSingleton1(string $sql)
384
  {
385 1
    $this->logQuery($sql);
386
387 1
    return $this->dl->executeSingleton1($sql);
388
  }
389
390
  //--------------------------------------------------------------------------------------------------------------------
391
  /**
392
   * Selects the SQL mode in the order as preferred by MySQL.
393
   *
394
   * @return string
395
   *
396
   * @throws MySqlQueryErrorException
397
   * @throws ResultException
398
   */
399 1
  public function getCanonicalSqlMode(): string
400
  {
401 1
    $sql = 'select @@sql_mode';
402
403 1
    return (string)$this->executeSingleton1($sql);
404
  }
405
406
  //--------------------------------------------------------------------------------------------------------------------
407
  /**
408
   * Selects all labels from a table with labels.
409
   *
410
   * @param string $tableName       The table name.
411
   * @param string $idColumnName    The name of the auto increment column.
412
   * @param string $labelColumnName The name of the column with labels.
413
   *
414
   * @return array[]
415
   *
416
   * @throws MySqlQueryErrorException
417
   */
418 1
  public function labelsFromTable(string $tableName, string $idColumnName, string $labelColumnName): array
419
  {
420 1
    $sql = "
421
select `%s`  id
422
,      `%s`  label
423
from   `%s`
424
where   nullif(`%s`,'') is not null";
425
426 1
    $sql = sprintf($sql, $idColumnName, $labelColumnName, $tableName, $labelColumnName);
427
428 1
    return $this->executeRows($sql);
429
  }
430
431
  //--------------------------------------------------------------------------------------------------------------------
432
  /**
433
   * Loads a stored routine.
434
   *
435
   * @param string $routineSource The source of the routine.
436
   *
437
   * @throws MySqlQueryErrorException
438
   */
439
  public function loadRoutine(string $routineSource): void
440
  {
441
    $this->executeNone($routineSource);
442
  }
443
444
  //--------------------------------------------------------------------------------------------------------------------
445
  /**
446
   * Escapes special characters in a string such that it can be safely used in SQL statements.
447
   *
448
   * Wrapper around [mysqli::real_escape_string](http://php.net/manual/mysqli.real-escape-string.php).
449
   *
450
   * @param string $string The string.
451
   *
452
   * @return string
453
   */
454
  public function realEscapeString(string $string): string
455
  {
456
    return $this->dl->realEscapeString($string);
457
  }
458
459
  //--------------------------------------------------------------------------------------------------------------------
460
  /**
461
   * Selects the parameters of a stored routine.
462
   *
463
   * @param string $routineName The name of the routine.
464
   *
465
   * @return array[]
466
   *
467
   * @throws MySqlQueryErrorException
468
   */
469
  public function routineParameters(string $routineName): array
470
  {
471
    $sql = sprintf("
472
select t2.PARAMETER_NAME      as  parameter_name                            
473
,      t2.DATA_TYPE           as  data_type             
474
,      t2.NUMERIC_PRECISION   as  numeric_precision                     
475
,      t2.NUMERIC_SCALE       as  numeric_scale                 
476
,      t2.CHARACTER_SET_NAME  as  character_set_name                      
477
,      t2.COLLATION_NAME      as  collation_name                  
478
,      t2.DTD_IDENTIFIER      as  dtd_identifier                  
479
from            information_schema.ROUTINES   t1
480
left outer join information_schema.PARAMETERS t2  on  t2.SPECIFIC_SCHEMA = t1.ROUTINE_SCHEMA and
481
                                                      t2.SPECIFIC_NAME   = t1.ROUTINE_NAME and
482
                                                      t2.PARAMETER_MODE   is not null
483
where t1.ROUTINE_SCHEMA = database()
484
and   t1.ROUTINE_NAME   = '%s'", $routineName);
485
486
    return $this->executeRows($sql);
487
  }
488
489
  //--------------------------------------------------------------------------------------------------------------------
490
  /**
491
   * Sets the default character set and collate.
492
   *
493
   * @param string $characterSet The character set.
494
   * @param string $collate      The collate.
495
   *
496
   * @throws MySqlQueryErrorException
497
   */
498
  public function setCharacterSet(string $characterSet, string $collate): void
499
  {
500
    $sql = sprintf('set names %s collate %s', $this->dl->quoteString($characterSet), $this->dl->quoteString($collate));
501
502
    $this->executeNone($sql);
503
  }
504
505
  //--------------------------------------------------------------------------------------------------------------------
506
  /**
507
   * Sets the SQL mode.
508
   *
509
   * @param string $sqlMode The SQL mode.
510
   *
511
   * @throws MySqlQueryErrorException
512
   */
513 1
  public function setSqlMode(string $sqlMode): void
514
  {
515 1
    $sql = sprintf('set sql_mode = %s', $this->dl->quoteString($sqlMode));
516
517 1
    $this->executeNone($sql);
518 1
  }
519
520
  //--------------------------------------------------------------------------------------------------------------------
521
  /**
522
   * Selects metadata of all columns of table.
523
   *
524
   * @param string $schemaName The name of the table schema.
525
   * @param string $tableName  The name of the table.
526
   *
527
   * @return array[]
528
   *
529
   * @throws MySqlQueryErrorException
530
   */
531
  public function tableColumns(string $schemaName, string $tableName): array
532
  {
533
    $sql = sprintf('
534
select COLUMN_NAME         as  column_name
535
,      COLUMN_TYPE         as  column_type
536
,      IS_NULLABLE         as  is_nullable
537
,      CHARACTER_SET_NAME  as  character_set_name
538
,      COLLATION_NAME      as  collation_name
539
,      EXTRA               as  extra
540
from   information_schema.COLUMNS
541
where  TABLE_SCHEMA = %s
542
and    TABLE_NAME   = %s
543
order by ORDINAL_POSITION',
544
                   $this->dl->quoteString($schemaName),
545
                   $this->dl->quoteString($tableName));
546
547
    return $this->executeRows($sql);
548
  }
549
550
  //--------------------------------------------------------------------------------------------------------------------
551
  /**
552
   * Selects the primary key from a table (if any).
553
   *
554
   * @param string $schemaName The name of the table schema.
555
   * @param string $tableName  The name of the table.
556
   *
557
   * @return array[]
558
   *
559
   * @throws MySqlQueryErrorException
560
   */
561
  public function tablePrimaryKey(string $schemaName, string $tableName): array
562
  {
563
    $sql = sprintf('
564
show index from `%s`.`%s`
565
where Key_name = \'PRIMARY\'',
566
                   $schemaName,
567
                   $tableName);
568
569
    return $this->executeRows($sql);
570
  }
571
572
  //--------------------------------------------------------------------------------------------------------------------
573
  /**
574
   * Selects all unique keys from table.
575
   *
576
   * @param string $schemaName The name of the table schema.
577
   * @param string $tableName  The name of the table.
578
   *
579
   * @return array[]
580
   *
581
   * @throws MySqlQueryErrorException
582
   */
583
  public function tableUniqueIndexes(string $schemaName, string $tableName): array
584
  {
585
    $sql = sprintf('
586
show index from `%s`.`%s`
587
where Non_unique = 0',
588
                   $schemaName,
589
                   $tableName);
590
591
    return $this->executeRows($sql);
592
  }
593
594
  //--------------------------------------------------------------------------------------------------------------------
595
  /**
596
   * Logs the query on the console.
597
   *
598
   * @param string $sql The query.
599
   */
600 1
  private function logQuery(string $sql): void
601
  {
602 1
    $sql = trim($sql);
603
604 1
    if (strpos($sql, "\n")!==false)
605
    {
606
      // Query is a multi line query.
607 1
      $this->io->logVeryVerbose('Executing query:');
608 1
      $this->io->logVeryVerbose('<sql>%s</sql>', $sql);
609
    }
610
    else
611
    {
612
      // Query is a single line query.
613 1
      $this->io->logVeryVerbose('Executing query: <sql>%s</sql>', $sql);
614
    }
615 1
  }
616
617
  //--------------------------------------------------------------------------------------------------------------------
618
}
619
620
//----------------------------------------------------------------------------------------------------------------------
621