Passed
Push — main ( 37b0c3...6cf3a1 )
by Thierry
07:04
created

TableTrait::makePrimaryIndex()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 15
nc 4
nop 1
dl 0
loc 21
rs 9.7666
c 1
b 0
f 0
1
<?php
2
3
namespace Lagdo\DbAdmin\Driver\Sqlite\Db;
4
5
use Lagdo\DbAdmin\Driver\Entity\IndexEntity;
6
use Lagdo\DbAdmin\Driver\Entity\TableEntity;
7
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity;
8
9
use function preg_match;
10
use function str_replace;
11
use function strtolower;
12
use function preg_match_all;
13
use function preg_quote;
14
use function array_filter;
15
16
trait TableTrait
17
{
18
    /**
19
     * @param string $type
20
     *
21
     * @return string
22
     */
23
    private function rowType(string $type): string
24
    {
25
        if (preg_match('~int~i', $type)) {
26
            return 'integer';
27
        }
28
        if (preg_match('~char|clob|text~i', $type)) {
29
            return 'text';
30
        }
31
        if (preg_match('~blob~i', $type)) {
32
            return 'blob';
33
        }
34
        if (preg_match('~real|floa|doub~i', $type)) {
35
            return 'real';
36
        }
37
        return 'numeric';
38
    }
39
40
    /**
41
     * @param array $row
42
     *
43
     * @return mixed|null
44
     */
45
    private function defaultvalue(array $row)
46
    {
47
        $default = $row["dflt_value"];
48
        if (preg_match("~'(.*)'~", $default, $match)) {
49
            return str_replace("''", "'", $match[1]);
50
        }
51
        if ($default == "NULL") {
52
            return null;
53
        }
54
        return $default;
55
    }
56
57
    /**
58
     * @param array $row
59
     *
60
     * @return TableFieldEntity
61
     */
62
    private function makeFieldEntity(array $row): TableFieldEntity
63
    {
64
        $field = new TableFieldEntity();
65
66
        $type = strtolower($row["type"]);
67
        $field->name = $row["name"];
68
        $field->type = $this->rowType($type);
69
        $field->fullType = $type;
70
        $field->default = $this->defaultvalue($row);
71
        $field->null = !$row["notnull"];
72
        $field->privileges = ["select" => 1, "insert" => 1, "update" => 1];
73
        $field->primary = $row["pk"];
74
        return $field;
75
    }
76
77
    /**
78
     * @param string $table
79
     *
80
     * @return array
81
     */
82
    private function tableFields(string $table): array
83
    {
84
        $fields = [];
85
        $rows = $this->driver->rows('PRAGMA table_info(' . $this->driver->table($table) . ')');
86
        $primary = "";
87
        foreach ($rows as $row) {
88
            $name = $row["name"];
89
            $type = strtolower($row["type"]);
90
            $field = $this->makeFieldEntity($row);
91
            if ($row["pk"]) {
92
                if ($primary != "") {
93
                    $fields[$primary]->autoIncrement = false;
94
                } elseif (preg_match('~^integer$~i', $type)) {
95
                    $field->autoIncrement = true;
96
                }
97
                $primary = $name;
98
            }
99
            $fields[$name] = $field;
100
        }
101
        return $fields;
102
    }
103
104
    /**
105
     * @param string $table
106
     *
107
     * @return array
108
     */
109
    private function queryStatus(string $table = ''): array
110
    {
111
        $query = "SELECT name AS Name, type AS Engine, 'rowid' AS Oid, '' AS Auto_increment " .
112
            "FROM sqlite_master WHERE type IN ('table', 'view') " .
113
            ($table != "" ? "AND name = " . $this->driver->quote($table) : "ORDER BY name");
114
        return $this->driver->rows($query);
115
    }
116
117
    /**
118
     * @param array $row
119
     *
120
     * @return TableEntity
121
     */
122
    private function makeStatus(array $row): TableEntity
123
    {
124
        $status = new TableEntity($row['Name']);
125
        $status->engine = $row['Engine'];
126
        $status->oid = $row['Oid'];
127
        // $status->Auto_increment = $row['Auto_increment'];
128
        $query = 'SELECT COUNT(*) FROM ' . $this->driver->escapeId($row['Name']);
129
        $status->rows = $this->driver->result($query);
130
131
        return $status;
132
    }
133
134
    /**
135
     * @param array $row
136
     * @param array $results
137
     * @param string $table
138
     *
139
     * @return IndexEntity
140
     */
141
    private function makeIndexEntity(array $row, array $results, string $table): IndexEntity
142
    {
143
        $index = new IndexEntity();
144
145
        $index->name = $row["name"];
146
        $index->type = $row["unique"] ? "UNIQUE" : "INDEX";
147
        $index->lengths = [];
148
        $index->descs = [];
149
        $columns = $this->driver->rows("PRAGMA index_info(" . $this->driver->escapeId($index->name) . ")");
150
        foreach ($columns as $column) {
151
            $index->columns[] = $column["name"];
152
            $index->descs[] = null;
153
        }
154
        if (preg_match('~^CREATE( UNIQUE)? INDEX ' . preg_quote($this->driver->escapeId($index->name) . ' ON ' .
155
                $this->driver->escapeId($table), '~') . ' \((.*)\)$~i', $results[$index->name], $regs)) {
156
            preg_match_all('/("[^"]*+")+( DESC)?/', $regs[2], $matches);
157
            foreach ($matches[2] as $key => $val) {
158
                if ($val) {
159
                    $index->descs[$key] = '1';
160
                }
161
            }
162
        }
163
        return $index;
164
    }
165
166
    /**
167
     * @param string $table
168
     *
169
     * @return IndexEntity|null
170
     */
171
    private function queryPrimaryIndex(string $table): ?IndexEntity
172
    {
173
        $primaryIndex = null;
174
        $query = "SELECT sql FROM sqlite_master WHERE type = 'table' AND name = " . $this->driver->quote($table);
175
        $result = $this->driver->result($query);
176
        if (preg_match('~\bPRIMARY\s+KEY\s*\((([^)"]+|"[^"]*"|`[^`]*`)++)~i', $result, $match)) {
177
            $primaryIndex = new IndexEntity();
178
            $primaryIndex->type = "PRIMARY";
179
            preg_match_all('~((("[^"]*+")+|(?:`[^`]*+`)+)|(\S+))(\s+(ASC|DESC))?(,\s*|$)~i',
180
                $match[1], $matches, PREG_SET_ORDER);
181
            foreach ($matches as $match) {
182
                $primaryIndex->columns[] = $this->driver->unescapeId($match[2]) . $match[4];
183
                $primaryIndex->descs[] = (preg_match('~DESC~i', $match[5]) ? '1' : null);
184
            }
185
        }
186
        return $primaryIndex;
187
    }
188
189
    /**
190
     * @param string $table
191
     *
192
     * @return IndexEntity|null
193
     */
194
    private function makePrimaryIndex(string $table): ?IndexEntity
195
    {
196
        $primaryIndex = $this->queryPrimaryIndex($table);
197
        if ($primaryIndex !== null) {
198
            return $primaryIndex;
199
        }
200
        $primaryFields = array_filter($this->fields($table), function($field) {
0 ignored issues
show
Bug introduced by
It seems like fields() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

200
        $primaryFields = array_filter($this->/** @scrutinizer ignore-call */ fields($table), function($field) {
Loading history...
201
            return $field->primary;
202
        });
203
        if (!$primaryFields) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $primaryFields of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
204
            return null;
205
        }
206
        $primaryIndex = new IndexEntity();
207
        $primaryIndex->type = "PRIMARY";
208
        $primaryIndex->lengths = [];
209
        $primaryIndex->descs = [null];
210
        $primaryIndex->columns = [];
211
        foreach ($primaryFields as $name => $field) {
212
            $primaryIndex->columns[] = $name;
213
        }
214
        return $primaryIndex;
215
    }
216
217
    /**
218
     * @param IndexEntity $index
219
     * @param IndexEntity $primaryIndex
220
     *
221
     * @return bool
222
     */
223
    private function indexIsPrimary(IndexEntity $index, IndexEntity $primaryIndex): bool
224
    {
225
        return $index->type === 'UNIQUE' && $index->columns == $primaryIndex->columns &&
226
            $index->descs == $primaryIndex->descs && preg_match("~^sqlite_~", $index->name);
227
    }
228
}
229