Completed
Push — master ( 1f7a6d...d6c443 )
by P.R.
03:14
created

AuditDataLayer::showColumns()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 5
ccs 0
cts 3
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace SetBased\Audit\MySql;
5
6
use SetBased\Audit\Metadata\TableColumnsMetadata;
7
use SetBased\Audit\MySql\Sql\AlterAuditTableAddColumns;
8
use SetBased\Audit\MySql\Sql\CreateAuditTable;
9
use SetBased\Audit\MySql\Sql\CreateAuditTrigger;
10
use SetBased\Audit\Style\AuditStyle;
11
use SetBased\Helper\CodeStore\MySqlCompoundSyntaxCodeStore;
12
use SetBased\Stratum\BulkHandler;
13
use SetBased\Stratum\Helper\RowSetHelper;
14
use SetBased\Stratum\MySql\StaticDataLayer;
15
16
/**
17
 * Class for executing SQL statements and retrieving metadata from MySQL.
18
 */
19
class AuditDataLayer extends StaticDataLayer
20
{
21
  //--------------------------------------------------------------------------------------------------------------------
22
  /**
23
   * The Output decorator.
24
   *
25
   * @var AuditStyle
26
   */
27
  private static $io;
28
29
  //--------------------------------------------------------------------------------------------------------------------
30
  /**
31
   * Adds new columns to an audit table.
32
   *
33
   * @param string               $auditSchemaName The name of audit schema.
34
   * @param string               $tableName       The name of the table.
35
   * @param TableColumnsMetadata $columns         The metadata of the new columns.
36
   */
37 2
  public static function addNewColumns(string $auditSchemaName, string $tableName, TableColumnsMetadata $columns): void
38
  {
39 2
    $helper = new AlterAuditTableAddColumns($auditSchemaName, $tableName, $columns);
40 2
    $sql    = $helper->buildStatement();
41
42 2
    static::executeNone($sql);
43 2
  }
44
45
  //--------------------------------------------------------------------------------------------------------------------
46
  /**
47
   * Creates an audit table.
48
   *
49
   * @param string               $dataSchemaName  The name of the data schema.
50
   * @param string               $auditSchemaName The name of the audit schema.
51
   * @param string               $tableName       The name of the table.
52
   * @param TableColumnsMetadata $columns         The metadata of the columns of the audit table (i.e. the audit
53
   *                                              columns and columns of the data table).
54
   */
55 25
  public static function createAuditTable(string $dataSchemaName,
56
                                          string $auditSchemaName,
57
                                          string $tableName,
58
                                          TableColumnsMetadata $columns): void
59
  {
60 25
    $helper = new CreateAuditTable($dataSchemaName, $auditSchemaName, $tableName, $columns);
61 25
    $sql    = $helper->buildStatement();
62
63 25
    static::executeNone($sql);
64 25
  }
65
66
  //--------------------------------------------------------------------------------------------------------------------
67
  /**
68
   * Creates a trigger on a table.
69
   *
70
   * @param string               $dataSchemaName         The name of the data schema.
71
   * @param string               $auditSchemaName        The name of the audit schema.
72
   * @param string               $tableName              The name of the table.
73
   * @param string               $triggerAction          The trigger action (i.e. INSERT, UPDATE, or DELETE).
74
   * @param string               $triggerName            The name of the trigger.
75
   * @param TableColumnsMetadata $additionalAuditColumns The metadata of the additional audit columns.
76
   * @param TableColumnsMetadata $tableColumns           The metadata of the data table columns.
77
   * @param string|null          $skipVariable           The skip variable.
78
   * @param string[]             $additionSql            Additional SQL statements.
79
   */
80 28
  public static function createAuditTrigger(string $dataSchemaName,
81
                                            string $auditSchemaName,
82
                                            string $tableName,
83
                                            string $triggerName,
84
                                            string $triggerAction,
85
                                            TableColumnsMetadata $additionalAuditColumns,
86
                                            TableColumnsMetadata $tableColumns,
87
                                            ?string $skipVariable,
88
                                            array $additionSql): void
89
  {
90 28
    $helper = new CreateAuditTrigger($dataSchemaName,
91 28
                                     $auditSchemaName,
92 28
                                     $tableName,
93 28
                                     $triggerName,
94 28
                                     $triggerAction,
95 28
                                     $additionalAuditColumns,
96 28
                                     $tableColumns,
97 28
                                     $skipVariable,
98 28
                                     $additionSql);
99 28
    $sql    = $helper->buildStatement();
100
101 28
    static::executeNone($sql);
102 28
  }
103
104
  //--------------------------------------------------------------------------------------------------------------------
105
  /**
106
   * Creates a temporary table for getting column type information for audit columns.
107
   *
108
   * @param string  $schemaName   The name of the table schema.
109
   * @param string  $tableName    The table name.
110
   * @param array[] $auditColumns Audit columns from config file.
111
   */
112 19
  public static function createTemporaryTable(string $schemaName, string $tableName, array $auditColumns): void
113
  {
114 19
    $sql = new MySqlCompoundSyntaxCodeStore();
115 19
    $sql->append(sprintf('create table `%s`.`%s` (', $schemaName, $tableName));
116 19
    foreach ($auditColumns as $column)
117
    {
118 19
      $sql->append(sprintf('%s %s', $column['column_name'], $column['column_type']));
119 19
      if (end($auditColumns)!==$column)
120
      {
121 19
        $sql->appendToLastLine(',');
122
      }
123
    }
124 19
    $sql->append(')');
125
126 19
    static::executeNone($sql->getCode());
127 19
  }
128
129
  //--------------------------------------------------------------------------------------------------------------------
130
  /**
131
   * Drops a temporary table.
132
   *
133
   * @param string $schemaName The name of the table schema.
134
   * @param string $tableName  The name of the table.
135
   */
136 19
  public static function dropTemporaryTable(string $schemaName, string $tableName): void
137
  {
138 19
    $sql = sprintf('drop table `%s`.`%s`', $schemaName, $tableName);
139
140 19
    static::executeNone($sql);
141 19
  }
142
143
  //--------------------------------------------------------------------------------------------------------------------
144
  /**
145
   * Drops a trigger.
146
   *
147
   * @param string $triggerSchema The name of the trigger schema.
148
   * @param string $triggerName   The mame of trigger.
149
   */
150 10
  public static function dropTrigger(string $triggerSchema, string $triggerName): void
151
  {
152 10
    $sql = sprintf('drop trigger `%s`.`%s`', $triggerSchema, $triggerName);
153
154 10
    static::executeNone($sql);
155 10
  }
156
157
  //--------------------------------------------------------------------------------------------------------------------
158
  /**
159
   * @inheritdoc
160
   */
161
  public static function executeBulk(BulkHandler $bulkHandler, string $query): void
162
  {
163
    static::logQuery($query);
164
165
    parent::executeBulk($bulkHandler, $query);
166
  }
167
168
  //--------------------------------------------------------------------------------------------------------------------
169
  /**
170
   * @inheritdoc
171
   */
172
  public static function executeMulti(string $queries): array
173
  {
174
    static::logQuery($queries);
175
176
    return parent::executeMulti($queries);
177
  }
178
179
  //--------------------------------------------------------------------------------------------------------------------
180
  /**
181
   * @inheritdoc
182
   */
183 28
  public static function executeNone(string $query): int
184
  {
185 28
    static::logQuery($query);
186
187 28
    return parent::executeNone($query);
188
  }
189
190
  //--------------------------------------------------------------------------------------------------------------------
191
  /**
192
   * @inheritdoc
193
   */
194
  public static function executeRow0(string $query): ?array
195
  {
196
    static::logQuery($query);
197
198
    return parent::executeRow0($query);
199
  }
200
201
  //--------------------------------------------------------------------------------------------------------------------
202
  /**
203
   * @inheritdoc
204
   */
205 25
  public static function executeRow1(string $query): array
206
  {
207 25
    static::logQuery($query);
208
209 25
    return parent::executeRow1($query);
210
  }
211
212
  //--------------------------------------------------------------------------------------------------------------------
213
  /**
214
   * @inheritdoc
215
   */
216 29
  public static function executeRows(string $query): array
217
  {
218 29
    static::logQuery($query);
219
220 29
    return parent::executeRows($query);
221
  }
222
223
  //--------------------------------------------------------------------------------------------------------------------
224
  /**
225
   * @inheritdoc
226
   */
227
  public static function executeSingleton0(string $query)
228
  {
229
    static::logQuery($query);
230
231
    return parent::executeSingleton0($query);
232
  }
233
234
  //--------------------------------------------------------------------------------------------------------------------
235
  /**
236
   * @inheritdoc
237
   */
238
  public static function executeSingleton1(string $query)
239
  {
240
    static::logQuery($query);
241
242
    return parent::executeSingleton1($query);
243
  }
244
245
  //--------------------------------------------------------------------------------------------------------------------
246
  /**
247
   * @inheritdoc
248
   */
249
  public static function executeTable(string $query): int
250
  {
251
    static::logQuery($query);
252
253
    return parent::executeTable($query);
254
  }
255
256
  //--------------------------------------------------------------------------------------------------------------------
257
  /**
258
   * Selects metadata of all columns of table.
259
   *
260
   * @param string $schemaName The name of the table schema.
261
   * @param string $tableName  The name of the table.
262
   *
263
   * @return array[]
264
   */
265 28
  public static function getTableColumns(string $schemaName, string $tableName): array
266
  {
267
    // When a column has no default prior to MariaDB 10.2.7 column_default is null from MariaDB 10.2.7
268
    // column_default = 'NULL' (string(4)).
269 28
    $sql = sprintf("
270
select COLUMN_NAME                    as column_name
271
,      COLUMN_TYPE                    as column_type
272
,      ifnull(COLUMN_DEFAULT, 'NULL') as column_default 
273
,      IS_NULLABLE                    as is_nullable
274
,      CHARACTER_SET_NAME             as character_set_name
275
,      COLLATION_NAME                 as collation_name
276
from   information_schema.COLUMNS
277
where  TABLE_SCHEMA = %s
278
and    TABLE_NAME   = %s
279
order by ORDINAL_POSITION",
280 28
                   static::quoteString($schemaName),
281 28
                   static::quoteString($tableName));
282
283 28
    return static::executeRows($sql);
284
  }
285
286
  //--------------------------------------------------------------------------------------------------------------------
287
  /**
288
   * Selects table engine, character_set_name and table_collation.
289
   *
290
   * @param string $schemaName The name of the table schema.
291
   * @param string $tableName  The name of the table.
292
   *
293
   * @return array
294
   */
295 25
  public static function getTableOptions(string $schemaName, string $tableName): array
296
  {
297 25
    $sql = sprintf('
298
select t1.TABLE_SCHEMA       as table_schema
299
,      t1.TABLE_NAME         as table_name
300
,      t1.TABLE_COLLATION    as table_collation
301
,      t1.ENGINE             as engine
302
,      t2.CHARACTER_SET_NAME as character_set_name
303
from       information_schema.TABLES                                t1
304
inner join information_schema.COLLATION_CHARACTER_SET_APPLICABILITY t2  on  t2.COLLATION_NAME = t1.TABLE_COLLATION
305
where t1.TABLE_SCHEMA = %s
306
and   t1.TABLE_NAME   = %s',
307 25
                   static::quoteString($schemaName),
308 25
                   static::quoteString($tableName));
309
310 25
    return static::executeRow1($sql);
311
  }
312
313
  //--------------------------------------------------------------------------------------------------------------------
314
  /**
315
   * Selects all triggers on a table.
316
   *
317
   * @param string $schemaName The name of the table schema.
318
   * @param string $tableName  The name of the table.
319
   *
320
   * @return array[]
321
   */
322 28
  public static function getTableTriggers(string $schemaName, string $tableName): array
323
  {
324 28
    $sql = sprintf('
325
select TRIGGER_NAME as trigger_name
326
from   information_schema.TRIGGERS
327
where  TRIGGER_SCHEMA     = %s
328
and    EVENT_OBJECT_TABLE = %s
329
order by Trigger_Name',
330 28
                   static::quoteString($schemaName),
331 28
                   static::quoteString($tableName));
332
333 28
    return static::executeRows($sql);
334
  }
335
336
  //--------------------------------------------------------------------------------------------------------------------
337
  /**
338
   * Selects all table names in a schema.
339
   *
340
   * @param string $schemaName The name of the schema.
341
   *
342
   * @return array[]
343
   */
344 29
  public static function getTablesNames(string $schemaName): array
345
  {
346 29
    $sql = sprintf("
347
select TABLE_NAME as table_name
348
from   information_schema.TABLES
349
where  TABLE_SCHEMA = %s
350
and    TABLE_TYPE   = 'BASE TABLE'
351 29
order by TABLE_NAME", static::quoteString($schemaName));
352
353 29
    return static::executeRows($sql);
354
  }
355
356
  //--------------------------------------------------------------------------------------------------------------------
357
  /**
358
   * Selects all triggers in a schema
359
   *
360
   * @param string $schemaName The name of the table schema.
361
   *
362
   * @return array[]
363
   */
364
  public static function getTriggers(string $schemaName): array
365
  {
366
    $sql = sprintf('
367
select EVENT_OBJECT_TABLE as table_name
368
,      TRIGGER_NAME       as trigger_name
369
from   information_schema.TRIGGERS
370
where  TRIGGER_SCHEMA     = %s
371
order by EVENT_OBJECT_TABLE
372
,        TRIGGER_NAME',
373
                   static::quoteString($schemaName));
374
375
    return static::executeRows($sql);
376
  }
377
378
  //--------------------------------------------------------------------------------------------------------------------
379
  /**
380
   * Acquires a write lock on a table.
381
   *
382
   * @param string $schemaName The schema of the table.
383
   * @param string $tableName  The table name.
384
   */
385 28
  public static function lockTable(string $schemaName, string $tableName): void
386
  {
387 28
    $sql = sprintf('lock tables `%s`.`%s` write', $schemaName, $tableName);
388
389 28
    static::executeNone($sql);
390 28
  }
391
392
  //--------------------------------------------------------------------------------------------------------------------
393
  /**
394
   * Resolves the canonical column types of the additional audit columns.
395
   *
396
   * @param string  $auditSchema            The name of the audit schema.
397
   * @param array[] $additionalAuditColumns The metadata of the additional audit columns.
398
   *
399
   * @return TableColumnsMetadata
400
   */
401 29
  public static function resolveCanonicalAdditionalAuditColumns(string $auditSchema,
402
                                                                array $additionalAuditColumns): TableColumnsMetadata
403
  {
404 29
    if (empty($additionalAuditColumns))
405
    {
406 10
      return new TableColumnsMetadata([], 'AuditColumnMetadata');
407
    }
408
409 19
    $tableName = '_TMP_'.uniqid();
410 19
    static::createTemporaryTable($auditSchema, $tableName, $additionalAuditColumns);
411 19
    $columns = AuditDataLayer::getTableColumns($auditSchema, $tableName);
412 19
    static::dropTemporaryTable($auditSchema, $tableName);
413
414 19
    foreach ($additionalAuditColumns as $column)
415
    {
416 19
      $key = RowSetHelper::findInRowSet($columns, 'column_name', $column['column_name']);
417
418 19
      if (isset($column['value_type']))
419
      {
420 19
        $columns[$key]['value_type'] = $column['value_type'];
421
      }
422 19
      if (isset($column['expression']))
423
      {
424 19
        $columns[$key]['expression'] = $column['expression'];
425
      }
426
    }
427
428 19
    return new TableColumnsMetadata($columns, 'AuditColumnMetadata');
429
  }
430
431
  //--------------------------------------------------------------------------------------------------------------------
432
  /**
433
   * Sets the Output decorator.
434
   *
435
   * @param AuditStyle $io The Output decorator.
436
   */
437 29
  public static function setIo(AuditStyle $io)
438
  {
439 29
    self::$io = $io;
440 29
  }
441
442
  //--------------------------------------------------------------------------------------------------------------------
443
  /**
444
   * Releases all table locks.
445
   */
446 28
  public static function unlockTables(): void
447
  {
448 28
    $sql = 'unlock tables';
449
450 28
    static::executeNone($sql);
451 28
  }
452
453
  //--------------------------------------------------------------------------------------------------------------------
454
  /**
455
   * Logs the query on the console.
456
   *
457
   * @param string $query The query.
458
   */
459 29
  private static function logQuery(string $query): void
460
  {
461 29
    $query = trim($query);
462
463 29
    if (strpos($query, "\n")!==false)
464
    {
465
      // Query is a multi line query.
466 29
      self::$io->logVeryVerbose('Executing query:');
467 29
      self::$io->logVeryVerbose('<sql>%s</sql>', $query);
468
    }
469
    else
470
    {
471
      // Query is a single line query.
472 28
      self::$io->logVeryVerbose('Executing query: <sql>%s</sql>', $query);
473
    }
474 29
  }
475
476
  //--------------------------------------------------------------------------------------------------------------------
477
}
478
479
//----------------------------------------------------------------------------------------------------------------------
480