Completed
Push — master ( a2fc1a...c1afcc )
by Sébastien
02:25
created

PdoMysqlMetadataReader   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 151
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 90.91%

Importance

Changes 0
Metric Value
wmc 15
lcom 1
cbo 12
dl 0
loc 151
ccs 60
cts 66
cp 0.9091
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
C readColumnsMetadata() 0 82 10
A readFields() 0 21 3
1
<?php
2
3
namespace Soluble\Metadata\Reader;
4
5
use Soluble\Metadata\ColumnsMetadata;
6
use Soluble\Metadata\Exception;
7
use Soluble\Datatype\Column;
8
use Soluble\Metadata\Reader\Mapping\PdoMysqlMapping;
9
use PDO;
10
11
class PdoMysqlMetadataReader extends AbstractMetadataReader
12
{
13
14
    /**
15
     * @var PDO
16
     */
17
    protected $pdo;
18
19
    /**
20
     *
21
     * @var boolean
22
     */
23
    protected $cache_active = true;
24
25
    /**
26
     *
27
     * @var Array
28
     */
29
    protected static $metadata_cache = [];
30
31
    /**
32
     *
33
     * @param PDO $pdo
34
     * @throws Exception\UnsupportedFeatureException
35
     * @throws Exception\UnsupportedDriverException
36
     */
37 12
    public function __construct(PDO $pdo)
38
    {
39 12
        $driver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
40 12
        if (strtolower($driver) != 'mysql') {
41 1
            throw new Exception\UnsupportedDriverException(__CLASS__ . " supports only pdo_mysql driver, '$driver' given.");
42
        }
43 12
        $this->pdo = $pdo;
44 12
    }
45
46
    /**
47
     *
48
     * {@inheritdoc}
49
     */
50 6
    protected function readColumnsMetadata($sql)
51
    {
52 6
        $metadata = new ColumnsMetadata();
53 6
        $fields = $this->readFields($sql);
54
55 5
        $type_map = PdoMysqlMapping::getDatatypeMapping();
56
57
58 5
        foreach ($fields as $idx => $field) {
59 5
            $name = $field['name'];
60 5
            $tableName = $field['table'];
61
62 5
            $datatype = strtoupper($field['native_type']);
63
64 5
            if (!$type_map->offsetExists($datatype)) {
65
                $msg = "Cannot get type for field '$name'. Mapping for native type [$datatype] cannot be resolved into a valid type";
66
                throw new Exception\UnsupportedTypeException($msg);
67
            }
68
69 5
            $datatype = $type_map->offsetGet($datatype);
70
71 5
            $column = Column\Type::createColumnDefinition($datatype['type'], $name, $tableName, $schemaName = null);
72 5
            $alias = $field['name'];
73
74
75 5
            $column->setAlias($alias);
76 5
            $column->setTableAlias($field['table']);
77
            //$column->setCatalog($field->catalog);
78 5
            $column->setOrdinalPosition($idx + 1);
79 5
            $column->setDataType($datatype['type']);
80 5
            $column->setIsNullable(!in_array('not_null', $field['flags']));
81 5
            $column->setIsPrimary(in_array('primary_key', $field['flags']));
82
            //$column->setColumnDefault($field->def);
83 5
            $column->setNativeDataType($datatype['native']);
84
85
            /*
86
              if ($column instanceof Column\Definition\NumericColumnInterface) {
87
              $column->setNumericUnsigned(($field->flags & MYSQLI_UNSIGNED_FLAG) > 0);
88
              }
89
90
              if ($column instanceof Column\Definition\IntegerColumn) {
91
              $column->setIsAutoIncrement(($field->flags & MYSQLI_AUTO_INCREMENT_FLAG) > 0);
92
              }
93
             */
94 5
            if ($column instanceof Column\Definition\DecimalColumn) {
95
                // salary DECIMAL(5,2)
96
                // In this example, 5 is the precision and 2 is the scale.
97
                // Standard SQL requires that DECIMAL(5,2) be able to store any value
98
                // with five digits and two decimals, so values that can be stored in
99
                // the salary column range from -999.99 to 999.99.
100
101 3
                $column->setNumericUnsigned(false);
102 3
                $column->setNumericPrecision($field['precision']);
103 3
                $column->setNumericScale($field['len'] - $field['precision'] + 1);
104 3
            }
105
106 5
            if ($column instanceof Column\Definition\StringColumn) {
107 5
                $column->setCharacterMaximumLength($field['len']);
108 5
            }
109
110 5
            if ($column instanceof Column\Definition\BlobColumn) {
111 1
                $column->setCharacterOctetLength($field['len']);
112 1
            }
113
114 5
            if ($metadata->offsetExists($alias)) {
115 1
                $prev_column = $metadata->offsetGet($alias);
116 1
                $prev_def = $prev_column->toArray();
117 1
                $curr_def = $column->toArray();
118 1
                if ($prev_def['dataType'] != $curr_def['dataType'] || $prev_def['nativeDataType'] != $curr_def['nativeDataType']) {
119 1
                    throw new Exception\AmbiguousColumnException("Cannot get column metadata, non unique column found '$alias' in query with different definitions.");
120
                }
121
122
                // If the the previous definition, was a prev_def
123
                if ($prev_def['isPrimary']) {
124
                    $column = $prev_column;
125
                }
126
            }
127 5
            $metadata->offsetSet($alias, $column);
128 5
        }
129
130 4
        return $metadata;
131
    }
132
133
    /**
134
     * Read fields from pdo source
135
     *
136
     * @throws Exception\ConnectionException
137
     * @param string $sql
138
     * @return array
139
     */
140 6
    protected function readFields($sql)
141
    {
142 6
        if (trim($sql) == '') {
143 1
            throw new Exception\EmptyQueryException();
144
        }
145
146 5
        $sql = $this->getEmptyQuery($sql);
147
148 5
        $stmt = $this->pdo->prepare($sql);
149 5
        $stmt->execute();
150 5
        $column_count = $stmt->columnCount();
151 5
        $metaFields = [];
152 5
        for ($i = 0; $i < $column_count; $i++) {
153 5
            $meta = $stmt->getColumnMeta($i);
154 5
            $metaFields[$i] = $meta;
155 5
        }
156
157 5
        $stmt->closeCursor();
158 5
        unset($stmt);
159 5
        return $metaFields;
160
    }
161
}
162