Passed
Push — main ( d7d864...d7e834 )
by Thierry
01:48
created

Admin::executeSavedQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 4
rs 10
1
<?php
2
3
namespace Lagdo\DbAdmin\Db;
4
5
use Lagdo\DbAdmin\Driver\DriverInterface;
6
7
use Exception;
8
9
use function trim;
10
use function implode;
11
use function is_numeric;
12
use function preg_match;
13
use function preg_replace;
14
use function strtoupper;
15
use function uniqid;
16
17
class Admin
18
{
19
    use Traits\AdminQueryTrait;
20
    use Traits\UserAdminTrait;
21
22
    /**
23
     * @var Util
24
     */
25
    public $util;
26
27
    /**
28
     * @var DriverInterface
29
     */
30
    public $driver;
31
32
    /**
33
     * @var Translator
34
     */
35
    protected $trans;
36
37
    /**
38
     * The constructor
39
     *
40
     * @param Util $util
41
     * @param DriverInterface $driver
42
     * @param Translator $trans
43
     */
44
    public function __construct(Util $util, DriverInterface $driver, Translator $trans)
45
    {
46
        $this->util = $util;
47
        $this->driver = $driver;
48
        $this->trans = $trans;
49
    }
50
51
    /**
52
     * Create SQL condition from parsed query string
53
     *
54
     * @param array $where Parsed query string
55
     * @param array $fields
56
     *
57
     * @return string
58
     */
59
    public function where(array $where, array $fields = []): string
60
    {
61
        $clauses = [];
62
        $wheres = $where["where"] ?? [];
63
        foreach ((array) $wheres as $key => $value) {
64
            $key = $this->util->bracketEscape($key, 1); // 1 - back
65
            $column = $this->util->escapeKey($key);
66
            $clauses[] = $column .
67
                // LIKE because of floats but slow with ints
68
                ($this->driver->jush() == "sql" && is_numeric($value) && preg_match('~\.~', $value) ? " LIKE " .
69
                $this->driver->quote($value) : ($this->driver->jush() == "mssql" ? " LIKE " .
70
                $this->driver->quote(preg_replace('~[_%[]~', '[\0]', $value)) : " = " . // LIKE because of text
71
                $this->driver->unconvertField($fields[$key], $this->driver->quote($value)))); //! enum and set
72
            if ($this->driver->jush() == "sql" &&
73
                preg_match('~char|text~', $fields[$key]->type) && preg_match("~[^ -@]~", $value)) {
74
                // not just [a-z] to catch non-ASCII characters
75
                $clauses[] = "$column = " . $this->driver->quote($value) . " COLLATE " . $this->driver->charset() . "_bin";
76
            }
77
        }
78
        $nulls = $where["null"] ?? [];
79
        foreach ((array) $nulls as $key) {
80
            $clauses[] = $this->util->escapeKey($key) . " IS NULL";
81
        }
82
        return implode(" AND ", $clauses);
83
    }
84
85
    /**
86
     * Remove current user definer from SQL command
87
     *
88
     * @param string $query
89
     *
90
     * @return string
91
     */
92
    public function removeDefiner(string $query): string
93
    {
94
        return preg_replace('~^([A-Z =]+) DEFINER=`' .
95
            preg_replace('~@(.*)~', '`@`(%|\1)', $this->driver->user()) .
96
            '`~', '\1', $query); //! proper escaping of user
97
    }
98
99
    /**
100
     * Find out foreign keys for each column
101
     *
102
     * @param string $table
103
     *
104
     * @return array
105
     */
106
    public function columnForeignKeys(string $table): array
107
    {
108
        $keys = [];
109
        foreach ($this->driver->foreignKeys($table) as $foreignKey) {
110
            foreach ($foreignKey->source as $val) {
111
                $keys[$val][] = $foreignKey;
112
            }
113
        }
114
        return $keys;
115
    }
116
117
    /**
118
     * Drop old object and redirect
119
     *
120
     * @param string $drop Drop old object query
121
     *
122
     * @return void
123
     * @throws Exception
124
     */
125
    public function drop(string $drop)
126
    {
127
        $this->executeQuery($drop);
128
    }
129
130
    /**
131
     * Create a view
132
     *
133
     * @param array $values The view values
134
     *
135
     * @return bool
136
     * @throws Exception
137
     */
138
    public function createView(array $values)
139
    {
140
        // From view.inc.php
141
        $name = trim($values['name']);
142
        $type = $values['materialized'] ? ' MATERIALIZED VIEW ' : ' VIEW ';
143
144
        $sql = ($this->driver->jush() === 'mssql' ? 'ALTER' : 'CREATE OR REPLACE') .
145
            $type . $this->driver->table($name) . " AS\n" . $values['select'];
146
        return $this->executeQuery($sql);
147
    }
148
149
    /**
150
     * Update a view
151
     *
152
     * @param string $view The view name
153
     * @param array $values The view values
154
     *
155
     * @return string
156
     * @throws Exception
157
     */
158
    public function updateView(string $view, array $values): string
159
    {
160
        // From view.inc.php
161
        $origType = 'VIEW';
162
        if ($this->driver->jush() === 'pgsql') {
163
            $status = $this->driver->tableStatus($view);
164
            $origType = strtoupper($status->engine);
165
        }
166
167
        $name = trim($values['name']);
168
        $type = $values['materialized'] ? 'MATERIALIZED VIEW' : 'VIEW';
169
        $tempName = $name . '_adminer_' . uniqid();
170
171
        return $this->dropAndCreate("DROP $origType " . $this->driver->table($view),
172
            "CREATE $type " . $this->driver->table($name) . " AS\n" . $values['select'],
173
            "DROP $type " . $this->driver->table($name),
174
            "CREATE $type " . $this->driver->table($tempName) . " AS\n" . $values['select'],
175
            "DROP $type " . $this->driver->table($tempName), $view, $name);
176
    }
177
178
    /**
179
     * Drop a view
180
     *
181
     * @param string $view The view name
182
     *
183
     * @return bool
184
     * @throws Exception
185
     */
186
    public function dropView(string $view): bool
187
    {
188
        // From view.inc.php
189
        $origType = 'VIEW';
190
        if ($this->driver->jush() == 'pgsql') {
191
            $status = $this->driver->tableStatus($view);
192
            $origType = strtoupper($status->engine);
193
        }
194
195
        $sql = "DROP $origType " . $this->driver->table($view);
196
        return $this->executeQuery($sql);
197
    }
198
}
199