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

Table::trigger()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 14
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 11
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 14
rs 9.9
1
<?php
2
3
namespace Lagdo\DbAdmin\Driver\Sqlite\Db;
4
5
use Lagdo\DbAdmin\Driver\Entity\TableEntity;
6
use Lagdo\DbAdmin\Driver\Entity\TriggerEntity;
7
use Lagdo\DbAdmin\Driver\Entity\ForeignKeyEntity;
8
use Lagdo\DbAdmin\Driver\Db\Table as AbstractTable;
9
10
use function str_replace;
11
use function preg_match;
12
use function preg_match_all;
13
use function preg_replace;
14
use function strtoupper;
15
use function implode;
16
17
class Table extends AbstractTable
18
{
19
    use TableTrait;
20
21
    /**
22
     * @inheritDoc
23
     */
24
    public function isView(TableEntity $tableStatus)
25
    {
26
        return $tableStatus->engine == 'view';
27
    }
28
29
    /**
30
     * @inheritDoc
31
     */
32
    public function supportForeignKeys(TableEntity $tableStatus)
33
    {
34
        return !$this->driver->result("SELECT sqlite_compileoption_used('OMIT_FOREIGN_KEY')");
35
    }
36
37
    /**
38
     * @inheritDoc
39
     */
40
    public function fields(string $table)
41
    {
42
        $fields = $this->tableFields($table);
43
        $query = "SELECT sql FROM sqlite_master WHERE type IN ('table', 'view') AND name = " . $this->driver->quote($table);
44
        $result = $this->driver->result($query);
45
        $pattern = '~(("[^"]*+")+|[a-z0-9_]+)\s+text\s+COLLATE\s+(\'[^\']+\'|\S+)~i';
46
        preg_match_all($pattern, $result, $matches, PREG_SET_ORDER);
47
        foreach ($matches as $match) {
48
            $name = str_replace('""', '"', preg_replace('~^"|"$~', '', $match[1]));
49
            if (isset($fields[$name])) {
50
                $fields[$name]->collation = trim($match[3], "'");
51
            }
52
        }
53
        return $fields;
54
    }
55
56
    /**
57
     * @inheritDoc
58
     */
59
    public function indexes(string $table)
60
    {
61
        $primaryIndex = $this->makePrimaryIndex($table);
62
        if ($primaryIndex === null) {
63
            return [];
64
        }
65
66
        $indexes = ['' => $primaryIndex];
67
        $query = "SELECT name, sql FROM sqlite_master WHERE type = 'index' AND tbl_name = " . $this->driver->quote($table);
68
        $results = $this->driver->keyValues($query);
69
        $rows = $this->driver->rows("PRAGMA index_list(" . $this->driver->table($table) . ")");
70
        foreach ($rows as $row) {
71
            $index = $this->makeIndexEntity($row, $results, $table);
72
            if ($this->indexIsPrimary($index, $primaryIndex)) {
73
                $indexes[$index->name] = $index;
74
            }
75
        }
76
77
        return $indexes;
78
    }
79
80
    /**
81
     * @inheritDoc
82
     */
83
    public function foreignKeys(string $table)
84
    {
85
        $foreignKeys = [];
86
        $query = 'PRAGMA foreign_key_list(' . $this->driver->table($table) . ')';
87
        foreach ($this->driver->rows($query) as $row) {
88
            $name = $row["id"];
89
            if (!isset($foreignKeys[$name])) {
90
                $foreignKeys[$name] = new ForeignKeyEntity();
91
            }
92
            //! idf_unescape in SQLite2
93
            $foreignKeys[$name]->source[] = $row["from"];
94
            $foreignKeys[$name]->target[] = $row["to"];
95
        }
96
        return $foreignKeys;
97
    }
98
99
    /**
100
     * @inheritDoc
101
     */
102
    public function tableStatus(string $table, bool $fast = false)
103
    {
104
        $rows = $this->queryStatus($table);
105
        if (!($row = reset($rows))) {
106
            return null;
107
        }
108
        return $this->makeStatus($row);
109
    }
110
111
    /**
112
     * @inheritDoc
113
     */
114
    public function tableStatuses(bool $fast = false)
115
    {
116
        $tables = [];
117
        $rows = $this->queryStatus();
118
        foreach ($rows as $row) {
119
            $tables[$row['Name']] = $this->makeStatus($row);
120
        }
121
        return $tables;
122
    }
123
124
    /**
125
     * @inheritDoc
126
     */
127
    public function tableNames()
128
    {
129
        $tables = [];
130
        $rows = $this->queryStatus();
131
        foreach ($rows as $row) {
132
            $tables[] = $row['Name'];
133
        }
134
        return $tables;
135
    }
136
137
    /**
138
     * @inheritDoc
139
     */
140
    public function triggerOptions()
141
    {
142
        return [
143
            "Timing" => ["BEFORE", "AFTER", "INSTEAD OF"],
144
            "Event" => ["INSERT", "UPDATE", "UPDATE OF", "DELETE"],
145
            "Type" => ["FOR EACH ROW"],
146
        ];
147
    }
148
149
    /**
150
     * @inheritDoc
151
     */
152
    public function trigger(string $name, string $table = '')
153
    {
154
        if ($name == "") {
155
            return new TriggerEntity('', '', "BEGIN\n\t;\nEND");
156
        }
157
        $idf = '(?:[^`"\s]+|`[^`]*`|"[^"]*")+';
158
        $options = $this->triggerOptions();
159
        preg_match("~^CREATE\\s+TRIGGER\\s*$idf\\s*(" . implode("|", $options["Timing"]) .
160
            ")\\s+([a-z]+)(?:\\s+OF\\s+($idf))?\\s+ON\\s*$idf\\s*(?:FOR\\s+EACH\\s+ROW\\s)?(.*)~is",
161
            $this->driver->result("SELECT sql FROM sqlite_master WHERE type = 'trigger' AND name = " .
162
                $this->driver->quote($name)), $match);
163
        $of = $match[3];
164
        return new TriggerEntity(strtoupper($match[1]), strtoupper($match[2]), $match[4],
165
            ($of[0] == '`' || $of[0] == '"' ? $this->driver->unescapeId($of) : $of), $name);
166
    }
167
168
    /**
169
     * @inheritDoc
170
     */
171
    public function triggers(string $table)
172
    {
173
        $triggers = [];
174
        $options = $this->triggerOptions();
175
        $query = "SELECT * FROM sqlite_master WHERE type = 'trigger' AND tbl_name = " . $this->driver->quote($table);
176
        foreach ($this->driver->rows($query) as $row) {
177
            preg_match('~^CREATE\s+TRIGGER\s*(?:[^`"\s]+|`[^`]*`|"[^"]*")+\s*(' .
178
                implode("|", $options["Timing"]) . ')\s*(.*?)\s+ON\b~i', $row["sql"], $match);
179
            $triggers[$row["name"]] = new TriggerEntity($match[1], $match[2], '', '', $row["name"]);
180
        }
181
        return $triggers;
182
    }
183
184
    /**
185
     * @inheritDoc
186
     */
187
    public function tableHelp(string $name)
188
    {
189
        if ($name == "sqlite_sequence") {
190
            return "fileformat2.html#seqtab";
191
        }
192
        if ($name == "sqlite_master") {
193
            return "fileformat2.html#$name";
194
        }
195
        return '';
196
    }
197
}
198