Completed
Branch master (7bb890)
by Sébastien
01:56
created

MysqliMetadataReader::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Soluble\Metadata\Reader;
4
5
use Soluble\Metadata\ColumnsMetadata;
6
use Soluble\Metadata\Exception;
7
use Soluble\Metadata\Reader\Mapping\MysqliMapping;
8
use Soluble\Datatype\Column;
9
10
11
class MysqliMetadataReader extends AbstractMetadataReader
12
{
13
14
    /**
15
     * @var \Mysqli
16
     */
17
    protected $mysqli;
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 \Mysqli $mysqli
34
     */
35 13
    public function __construct(\Mysqli $mysqli)
36
    {
37 13
        $this->mysqli = $mysqli;
38 13
    }
39
40
    /**
41
     *
42
     * {@inheritdoc}
43
     */
44 7
    protected function readColumnsMetadata($sql)
45
    {
46 7
        $metadata = new ColumnsMetadata();
47 7
        $fields = $this->readFields($sql);
48 5
        $type_map = MysqliMapping::getDatatypeMapping();
49
50 5
        foreach ($fields as $idx => $field) {
51 5
            $name = $field->orgname == '' ? $field->name : $field->orgname;
52 5
            $tableName = $field->orgtable;
53 5
            $schemaName = $field->db;
54
55 5
            $type = $field->type;
56
57 5
            if (!$type_map->offsetExists($type)) {
58
                $msg = "Cannot get type for field '$name'. Mapping for native type [$type] cannot be resolved into a valid type for driver: " . __CLASS__;
59
                throw new Exception\UnsupportedTypeException($msg);
60
            }
61
62 5
            $datatype = $type_map->offsetGet($type);
63
64 5
            $column = Column\Type::createColumnDefinition($datatype['type'], $name, $tableName, $schemaName);
65
66 5
            $column->setAlias($field->name);
67 5
            $column->setTableAlias($field->table);
68 5
            $column->setCatalog($field->catalog);
69 5
            $column->setOrdinalPosition($idx + 1);
70 5
            $column->setDataType($datatype['type']);
71 5
            $column->setIsNullable(!($field->flags & MYSQLI_NOT_NULL_FLAG) > 0 && ($field->orgtable != ''));
72 5
            $column->setIsPrimary(($field->flags & MYSQLI_PRI_KEY_FLAG) > 0);
73
74 5
            $column->setColumnDefault($field->def);
75
76 5
            if (($field->flags & MYSQLI_SET_FLAG) > 0) {
77 1
                $column->setNativeDataType('SET');
78 5
            } elseif (($field->flags & MYSQLI_ENUM_FLAG) > 0) {
79 2
                $column->setNativeDataType('ENUM');
80 2
            } else {
81 5
                $column->setNativeDataType($datatype['native']);
82
            }
83
84 5
            if ($field->table == '') {
85 1
                $column->setIsGroup(($field->flags & MYSQLI_GROUP_FLAG) > 0);
86 1
            }
87
88 5
            if ($column instanceof Column\Definition\NumericColumnInterface) {
89 5
                $column->setNumericUnsigned(($field->flags & MYSQLI_UNSIGNED_FLAG) > 0);
90 5
            }
91
92 5
            if ($column instanceof Column\Definition\IntegerColumn) {
93 5
                $column->setIsAutoIncrement(($field->flags & MYSQLI_AUTO_INCREMENT_FLAG) > 0);
94 5
            } elseif ($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 2
                $column->setNumericScale($field->length - $field->decimals + 1);
101 2
                $column->setNumericPrecision($field->decimals);
102 2
            }
103
104 5
            if ($column instanceof Column\Definition\StringColumn) {
105 4
                $column->setCharacterMaximumLength($field->length);
106 4
            }
107
108 5
            if ($column instanceof Column\Definition\BlobColumn) {
109 1
                $column->setCharacterOctetLength($field->length);
110 1
            }
111
112 5
            $alias = $column->getAlias();
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
128
129 5
            $metadata->offsetSet($alias, $column);
130 5
        }
131
132 4
        return $metadata;
133
    }
134
135
    /**
136
     *
137
     * @param string $sql
138
     * @throws Exception\ConnectionException
139
     * @return array
140
     */
141 7
    protected function readFields($sql)
142
    {
143 7
        if (trim($sql) == '') {
144 1
            throw new Exception\EmptyQueryException(__METHOD__ . ": Error cannot handle empty queries");
145
        }
146
147 6
        $sql = $this->getEmptyQuery($sql);
148 6
        $stmt = $this->mysqli->prepare($sql);
149
150 6
        if (!$stmt) {
151 1
            $message = $this->mysqli->error;
152 1
            throw new Exception\InvalidQueryException(__METHOD__ . ": Error sql is not correct : $message");
153
        }
154 5
        $stmt->execute();
155
156 5
        $result = $stmt->result_metadata();
157 5
        $metaFields = $result->fetch_fields();
158 5
        $result->close();
159 5
        $stmt->close();
160 5
        return $metaFields;
161
    }
162
}
163