Passed
Push — develop ( 2d5edd...a33225 )
by Felipe
05:40
created

RowTrait::getRowIdentifier()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 50
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 50
rs 8.6315
c 0
b 0
f 0
cc 5
eloc 23
nc 5
nop 1
1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.47
5
 */
6
7
namespace PHPPgAdmin\Traits;
8
9
/**
10
 * Common trait for tables manipulation.
11
 */
12
trait RowTrait
13
{
14
    /**
15
     * Returns a recordset of all columns in a table.
16
     *
17
     * @param string $table The name of a table
18
     * @param array  $key   The associative array holding the key to retrieve
19
     *
20
     * @return \PHPPgAdmin\ADORecordSet A recordset
21
     */
22
    public function browseRow($table, $key)
23
    {
24
        $f_schema = $this->_schema;
25
        $this->fieldClean($f_schema);
26
        $this->fieldClean($table);
27
28
        $sql = "SELECT * FROM \"{$f_schema}\".\"{$table}\"";
29
        if (is_array($key) && sizeof($key) > 0) {
30
            $sql .= ' WHERE true';
31
            foreach ($key as $k => $v) {
32
                $this->fieldClean($k);
33
                $this->clean($v);
34
                $sql .= " AND \"{$k}\"='{$v}'";
35
            }
36
        }
37
38
        return $this->selectSet($sql);
39
    }
40
41
    /**
42
     * Get the fields for uniquely identifying a row in a table.
43
     *
44
     * @param string $table The table for which to retrieve the identifier
45
     *
46
     * @return array|array<integer,string>|int An array mapping attribute number to attribute name, empty for no identifiers
47
     */
48
    public function getRowIdentifier($table)
49
    {
50
        $oldtable = $table;
51
        $c_schema = $this->_schema;
52
        $this->clean($c_schema);
53
        $this->clean($table);
54
55
        $status = $this->beginTransaction();
56
        if ($status != 0) {
57
            return -1;
58
        }
59
60
        // Get the first primary or unique index (sorting primary keys first) that
61
        // is NOT a partial index.
62
        $sql = "
63
            SELECT indrelid, indkey
64
            FROM pg_catalog.pg_index
65
            WHERE indisunique AND indrelid=(
66
                SELECT oid FROM pg_catalog.pg_class
67
                WHERE relname='{$table}' AND relnamespace=(
68
                    SELECT oid FROM pg_catalog.pg_namespace
69
                    WHERE nspname='{$c_schema}'
70
                )
71
            ) AND indpred IS NULL AND indexprs IS NULL
72
            ORDER BY indisprimary DESC LIMIT 1";
73
        $rs = $this->selectSet($sql);
74
75
        // If none, check for an OID column.  Even though OIDs can be duplicated, the edit and delete row
76
        // functions check that they're only modiying a single row.  Otherwise, return empty array.
77
        if ($rs->RecordCount() == 0) {
78
            // Check for OID column
79
            $temp = [];
80
            if ($this->hasObjectID($table)) {
0 ignored issues
show
Bug introduced by
It seems like hasObjectID() 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

80
            if ($this->/** @scrutinizer ignore-call */ hasObjectID($table)) {
Loading history...
81
                $temp = ['oid'];
82
            }
83
            $this->endTransaction();
84
85
            return $temp;
86
        } // Otherwise find the names of the keys
87
88
        $attnames = $this->getAttributeNames($oldtable, explode(' ', $rs->fields['indkey']));
89
        if (!is_array($attnames)) {
90
            $this->rollbackTransaction();
91
92
            return -1;
93
        }
94
95
        $this->endTransaction();
96
97
        return $attnames;
98
    }
99
100
    /**
101
     * Adds a new row to a table.
102
     *
103
     * @param string $table  The table in which to insert
104
     * @param array  $fields Array of given field in values
105
     * @param array  $values Array of new values for the row
106
     * @param array  $nulls  An array mapping column => something if it is to be null
107
     * @param array  $format An array of the data type (VALUE or EXPRESSION)
108
     * @param array  $types  An array of field types
109
     *
110
     * @return int 0 if operation was successful
111
     */
112
    public function insertRow($table, $fields, $values, $nulls, $format, $types)
113
    {
114
        if (!is_array($fields) || !is_array($values) || !is_array($nulls)
3 ignored issues
show
introduced by
The condition is_array($fields) is always true.
Loading history...
introduced by
The condition is_array($nulls) is always true.
Loading history...
introduced by
The condition is_array($values) is always true.
Loading history...
115
            || !is_array($format) || !is_array($types)
2 ignored issues
show
introduced by
The condition is_array($format) is always true.
Loading history...
introduced by
The condition is_array($types) is always true.
Loading history...
116
            || (count($fields) != count($values))
117
        ) {
118
            return -1;
119
        }
120
121
        // Build clause
122
        if (count($values) > 0) {
123
            // Escape all field names
124
            $fields   = array_map(['\PHPPgAdmin\Database\Postgres', 'fieldClean'], $fields);
125
            $f_schema = $this->_schema;
126
            $this->fieldClean($table);
127
            $this->fieldClean($f_schema);
128
129
            $sql = '';
130
            foreach ($values as $i => $value) {
131
                // Handle NULL values
132
                if (isset($nulls[$i])) {
133
                    $sql .= ',NULL';
134
                } else {
135
                    $sql .= ','.$this->formatValue($types[$i], $format[$i], $value);
136
                }
137
            }
138
139
            $sql = "INSERT INTO \"{$f_schema}\".\"{$table}\" (\"".implode('","', $fields).'")
140
                VALUES ('.substr($sql, 1).')';
141
142
            return $this->execute($sql);
143
        }
144
145
        return -1;
146
    }
147
148
    /**
149
     * Formats a value or expression for sql purposes.
150
     *
151
     * @param string $type   The type of the field
152
     * @param mixed  $format VALUE or EXPRESSION
153
     * @param mixed  $value  The actual value entered in the field.  Can be NULL
154
     *
155
     * @return mixed The suitably quoted and escaped value
156
     */
157
    public function formatValue($type, $format, $value)
158
    {
159
        switch ($type) {
160
            case 'bool':
161
            case 'boolean':
162
                if ($value == 't') {
163
                    return 'TRUE';
164
                }
165
166
                if ($value == 'f') {
167
                    return 'FALSE';
168
                }
169
                if ($value == '') {
170
                    return 'NULL';
171
                }
172
173
                return $value;
174
                break;
175
            default:
176
                // Checking variable fields is difficult as there might be a size
177
                // attribute...
178
                if (strpos($type, 'time') === 0) {
179
                    // Assume it's one of the time types...
180
                    if ($value == '') {
181
                        return "''";
182
                    }
183
184
                    if (strcasecmp($value, 'CURRENT_TIMESTAMP') == 0
185
                        || strcasecmp($value, 'CURRENT_TIME') == 0
186
                        || strcasecmp($value, 'CURRENT_DATE') == 0
187
                        || strcasecmp($value, 'LOCALTIME') == 0
188
                        || strcasecmp($value, 'LOCALTIMESTAMP') == 0) {
189
                        return $value;
190
                    }
191
                    if ($format == 'EXPRESSION') {
192
                        return $value;
193
                    }
194
                    $this->clean($value);
195
196
                    return "'{$value}'";
197
                }
198
                if ($format == 'VALUE') {
199
                    $this->clean($value);
200
201
                    return "'{$value}'";
202
                }
203
204
                return $value;
205
        }
206
    }
207
208
    // View functions
209
210
    /**
211
     * Updates a row in a table.
212
     *
213
     * @param string $table  The table in which to update
214
     * @param array  $vars   An array mapping new values for the row
215
     * @param array  $nulls  An array mapping column => something if it is to be null
216
     * @param array  $format An array of the data type (VALUE or EXPRESSION)
217
     * @param array  $types  An array of field types
218
     * @param array  $keyarr An array mapping column => value to update
219
     *
220
     * @return bool|int 0 success
221
     */
222
    public function editRow($table, $vars, $nulls, $format, $types, $keyarr)
223
    {
224
        if (!is_array($vars) || !is_array($nulls) || !is_array($format) || !is_array($types)) {
4 ignored issues
show
introduced by
The condition is_array($types) is always true.
Loading history...
introduced by
The condition is_array($vars) is always true.
Loading history...
introduced by
The condition is_array($format) is always true.
Loading history...
introduced by
The condition is_array($nulls) is always true.
Loading history...
225
            return -1;
226
        }
227
228
        $f_schema = $this->_schema;
229
        $this->fieldClean($f_schema);
230
        $this->fieldClean($table);
231
232
        // Build clause
233
        if (sizeof($vars) > 0) {
234
            foreach ($vars as $key => $value) {
235
                $this->fieldClean($key);
236
237
                // Handle NULL values
238
                if (isset($nulls[$key])) {
239
                    $tmp = 'NULL';
240
                } else {
241
                    $tmp = $this->formatValue($types[$key], $format[$key], $value);
242
                }
243
244
                if (isset($sql)) {
245
                    $sql .= ", \"{$key}\"={$tmp}";
246
                } else {
247
                    $sql = "UPDATE \"{$f_schema}\".\"{$table}\" SET \"{$key}\"={$tmp}";
248
                }
249
            }
250
            $first = true;
251
            foreach ($keyarr as $k => $v) {
252
                $this->fieldClean($k);
253
                $this->clean($v);
254
                if ($first) {
255
                    $sql .= " WHERE \"{$k}\"='{$v}'";
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sql does not seem to be defined for all execution paths leading up to this point.
Loading history...
256
                    $first = false;
257
                } else {
258
                    $sql .= " AND \"{$k}\"='{$v}'";
259
                }
260
            }
261
        }
262
263
        // Begin transaction.  We do this so that we can ensure only one row is
264
        // edited
265
        $status = $this->beginTransaction();
266
        if ($status != 0) {
267
            $this->rollbackTransaction();
268
269
            return -1;
270
        }
271
        $status = $this->execute($sql);
272
        if ($status != 0) {
273
            // update failed
274
            $this->rollbackTransaction();
275
276
            return -1;
277
        }
278
279
        if ($this->conn->Affected_Rows() != 1) {
280
            // more than one row could be updated
281
            $this->rollbackTransaction();
282
283
            return -2;
284
        }
285
286
        // End transaction
287
        return $this->endTransaction();
288
    }
289
290
    /**
291
     * Delete a row from a table.
292
     *
293
     * @param string $table  The table from which to delete
294
     * @param array  $key    An array mapping column => value to delete
295
     * @param string $schema the schema of the table
296
     *
297
     * @return bool|int 0 success
298
     */
299
    public function deleteRow($table, $key, $schema = '')
300
    {
301
        if (!is_array($key)) {
1 ignored issue
show
introduced by
The condition is_array($key) is always true.
Loading history...
302
            return -1;
303
        }
304
305
        // Begin transaction.  We do this so that we can ensure only one row is
306
        // deleted
307
        $status = $this->beginTransaction();
308
        if ($status != 0) {
309
            $this->rollbackTransaction();
310
311
            return -1;
312
        }
313
314
        if ($schema === '') {
315
            $schema = $this->_schema;
316
        }
317
318
        $status = $this->delete($table, $key, $schema);
319
        if ($status != 0 || $this->conn->Affected_Rows() != 1) {
320
            $this->rollbackTransaction();
321
322
            return -2;
323
        }
324
325
        // End transaction
326
        return $this->endTransaction();
327
    }
328
329
    abstract public function fieldClean(&$str);
330
331
    abstract public function beginTransaction();
332
333
    abstract public function rollbackTransaction();
334
335
    abstract public function endTransaction();
336
337
    abstract public function execute($sql);
338
339
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
340
341
    abstract public function selectSet($sql);
342
343
    abstract public function clean(&$str);
344
345
    abstract public function phpBool($parameter);
346
347
    abstract public function hasCreateTableLikeWithConstraints();
348
349
    abstract public function hasCreateTableLikeWithIndexes();
350
351
    abstract public function hasTablespaces();
352
353
    abstract public function delete($table, $conditions, $schema = '');
354
355
    abstract public function fieldArrayClean(&$arr);
356
357
    abstract public function hasCreateFieldWithConstraints();
358
359
    abstract public function getAttributeNames($table, $atts);
360
}
361