MetadataFactory   A
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 135
Duplicated Lines 0 %

Coupling/Cohesion

Dependencies 7

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 8
cbo 7
dl 0
loc 135
ccs 0
cts 106
cp 0
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getServerMetadata() 0 18 3
A getDatabaseMetadata() 0 24 2
B getTableMetadata() 0 79 3
1
<?php declare(strict_types=1);
2
3
/**
4
 * @copyright   (c) 2017-present brian ridley
5
 * @author      brian ridley <[email protected]>
6
 * @license     http://opensource.org/licenses/MIT MIT
7
 */
8
9
namespace ptlis\GrepDb\Metadata\MySQL;
10
11
use Doctrine\DBAL\Connection;
12
13
/**
14
 * Factory that builds server, database & table metadata.
15
 */
16
final class MetadataFactory
17
{
18
    /**
19
     * Query the server and build server metadata DTO.
20
     */
21
    public function getServerMetadata(
22
        Connection $connection
23
    ) {
24
        // Attempt to list databases, ignoring internal databases
25
        try {
26
            $statement = $connection->query('SHOW DATABASES WHERE `Database` NOT IN ("information_schema", "performance_schema", "sys", "mysql");');
27
        } catch (\Throwable $e) {
28
            throw new \RuntimeException('Failed to list databases: ' . $e->getMessage());
29
        }
30
31
        $databaseMetadataList = [];
32
        /** @var string $databaseName */
33
        while ($databaseName = $statement->fetchColumn(0)) {
34
            $databaseMetadataList[] = $this->getDatabaseMetadata($connection, $databaseName);
35
        }
36
37
        return new ServerMetadata($connection->getHost(), $databaseMetadataList);
38
    }
39
40
    /**
41
     * Query the server and build database metadata DTO.
42
     */
43
    public function getDatabaseMetadata(
44
        Connection $connection,
45
        string $databaseName
46
    ): DatabaseMetadata {
47
        // Get a list of table names
48
        $tableNameStatement = $connection
49
            ->createQueryBuilder()
50
            ->select([
51
                'tables.TABLE_NAME AS name'
52
            ])
53
            ->from('information_schema.TABLES', 'tables')
54
            ->where('TABLE_SCHEMA = :schema')
55
            ->andWhere('TABLE_TYPE = "BASE TABLE"')
56
            ->setParameter('schema', $databaseName)
57
            ->execute();
58
59
        // Build table metadata
60
        $tableMetadataList = [];
61
        while ($tableName = $tableNameStatement->fetchColumn(0)) {
62
            $tableMetadataList[] = $this->getTableMetadata($connection, $databaseName, $tableName);
63
        }
64
65
        return new DatabaseMetadata($databaseName, $tableMetadataList);
66
    }
67
68
    /**
69
     * Query the server and build table metadata DTO.
70
     */
71
    public function getTableMetadata(
72
        Connection $connection,
73
        string $databaseName,
74
        string $tableName
75
    ): TableMetadata {
76
        // Get top-level table information
77
        $tableStatement = $connection
78
            ->createQueryBuilder()
79
            ->select([
80
                'tables.TABLE_NAME AS name',
81
                'tables.ENGINE AS engine',
82
                'tables.TABLE_COLLATION AS collation',
83
                'tables.TABLE_ROWS AS row_count',
84
                'charset.CHARACTER_SET_NAME AS charset'
85
            ])
86
            ->from('information_schema.TABLES', 'tables')
87
            ->leftJoin(
88
                'tables',
89
                'information_schema.COLLATION_CHARACTER_SET_APPLICABILITY',
90
                'charset',
91
                'tables.TABLE_COLLATION = charset.COLLATION_NAME'
92
            )
93
            ->where('TABLE_NAME = :table_name')
94
            ->setParameter('table_name', $tableName)
95
            ->execute();
96
97
        $tableRow = $tableStatement->fetch(\PDO::FETCH_ASSOC);
98
99
        // Get column information
100
        $columnsStatement = $connection
101
            ->createQueryBuilder()
102
            ->select([
103
                'columns.COLUMN_NAME AS name',
104
                'columns.COLUMN_TYPE AS type',
105
                'columns.CHARACTER_MAXIMUM_LENGTH AS max_length',
106
                '"PRI" = columns.COLUMN_KEY AS is_primary_key',
107
                '"YES" = columns.IS_NULLABLE AS is_nullable',
108
                '(
109
                    SELECT COUNT(*) 
110
                    FROM   INFORMATION_SCHEMA.STATISTICS 
111
                    WHERE  TABLE_SCHEMA = :schema 
112
                    AND    TABLE_NAME = :table_name 
113
                    AND    COLUMN_NAME = columns.COLUMN_NAME
114
                ) AS is_indexed'
115
            ])
116
            ->from('information_schema.COLUMNS', 'columns')
117
            ->where('TABLE_SCHEMA = :schema')
118
            ->andWhere('TABLE_NAME = :table_name')
119
            ->setParameters([
120
                'schema' => $databaseName,
121
                'table_name' => $tableName
122
            ])
123
            ->execute();
124
125
        // Build column metadata
126
        $columnMetadataList = [];
127
        foreach ($columnsStatement->fetchAll(\PDO::FETCH_ASSOC) as $columnsRow) {
128
            $columnMetadataList[] = new ColumnMetadata(
129
                $databaseName,
130
                $tableName,
131
                $columnsRow['name'],
132
                $columnsRow['type'],
133
                is_null($columnsRow['max_length']) ? null : intval($columnsRow['max_length']),
134
                boolval($columnsRow['is_primary_key']),
135
                boolval($columnsRow['is_nullable']),
136
                boolval($columnsRow['is_indexed'])
137
            );
138
        }
139
140
        return new TableMetadata(
141
            $databaseName,
142
            $tableName,
143
            $tableRow['engine'],
144
            $tableRow['collation'],
145
            $tableRow['charset'],
146
            intval($tableRow['row_count']),
147
            $columnMetadataList
148
        );
149
    }
150
}