Passed
Push — develop ( c2019a...2cab77 )
by Felipe
04:50
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.48
5
 */
6
7
namespace PHPPgAdmin\Database\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)) {
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)
115
            || !is_array($format) || !is_array($types)
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
175
                break;
176
            default:
177
                // Checking variable fields is difficult as there might be a size
178
                // attribute...
179
                if (strpos($type, 'time') === 0) {
180
                    // Assume it's one of the time types...
181
                    if ($value == '') {
182
                        return "''";
183
                    }
184
185
                    if (strcasecmp($value, 'CURRENT_TIMESTAMP') == 0
186
                        || strcasecmp($value, 'CURRENT_TIME') == 0
187
                        || strcasecmp($value, 'CURRENT_DATE') == 0
188
                        || strcasecmp($value, 'LOCALTIME') == 0
189
                        || strcasecmp($value, 'LOCALTIMESTAMP') == 0) {
190
                        return $value;
191
                    }
192
                    if ($format == 'EXPRESSION') {
193
                        return $value;
194
                    }
195
                    $this->clean($value);
196
197
                    return "'{$value}'";
198
                }
199
                if ($format == 'VALUE') {
200
                    $this->clean($value);
201
202
                    return "'{$value}'";
203
                }
204
205
                return $value;
206
        }
207
    }
208
209
    // View functions
210
211
    /**
212
     * Updates a row in a table.
213
     *
214
     * @param string $table  The table in which to update
215
     * @param array  $vars   An array mapping new values for the row
216
     * @param array  $nulls  An array mapping column => something if it is to be null
217
     * @param array  $format An array of the data type (VALUE or EXPRESSION)
218
     * @param array  $types  An array of field types
219
     * @param array  $keyarr An array mapping column => value to update
220
     *
221
     * @return bool|int 0 success
222
     */
223
    public function editRow($table, $vars, $nulls, $format, $types, $keyarr)
224
    {
225
        if (!is_array($vars) || !is_array($nulls) || !is_array($format) || !is_array($types)) {
226
            return -1;
227
        }
228
229
        $f_schema = $this->_schema;
230
        $this->fieldClean($f_schema);
231
        $this->fieldClean($table);
232
        $sql = '';
233
        // Build clause
234
        if (sizeof($vars) > 0) {
235
            foreach ($vars as $key => $value) {
236
                $this->fieldClean($key);
237
238
                // Handle NULL values
239
                if (isset($nulls[$key])) {
240
                    $tmp = 'NULL';
241
                } else {
242
                    $tmp = $this->formatValue($types[$key], $format[$key], $value);
243
                }
244
245
                if (strlen($sql) > 0) {
246
                    $sql .= ", \"{$key}\"={$tmp}";
247
                } else {
248
                    $sql = "UPDATE \"{$f_schema}\".\"{$table}\" SET \"{$key}\"={$tmp}";
249
                }
250
            }
251
            $first = true;
252
            foreach ($keyarr as $k => $v) {
253
                $this->fieldClean($k);
254
                $this->clean($v);
255
                if ($first) {
256
                    $sql .= " WHERE \"{$k}\"='{$v}'";
257
                    $first = false;
258
                } else {
259
                    $sql .= " AND \"{$k}\"='{$v}'";
260
                }
261
            }
262
        }
263
264
        // Begin transaction.  We do this so that we can ensure only one row is
265
        // edited
266
        $status = $this->beginTransaction();
267
        if ($status != 0) {
268
            $this->rollbackTransaction();
269
270
            return -1;
271
        }
272
        $status = $this->execute($sql);
273
        if ($status != 0) {
274
            // update failed
275
            $this->rollbackTransaction();
276
277
            return -1;
278
        }
279
280
        if ($this->conn->Affected_Rows() != 1) {
281
            // more than one row could be updated
282
            $this->rollbackTransaction();
283
284
            return -2;
285
        }
286
287
        // End transaction
288
        return $this->endTransaction();
289
    }
290
291
    /**
292
     * Delete a row from a table.
293
     *
294
     * @param string $table  The table from which to delete
295
     * @param array  $key    An array mapping column => value to delete
296
     * @param string $schema the schema of the table
297
     *
298
     * @return bool|int 0 success
299
     */
300
    public function deleteRow($table, $key, $schema = '')
301
    {
302
        if (!is_array($key)) {
303
            return -1;
304
        }
305
306
        // Begin transaction.  We do this so that we can ensure only one row is
307
        // deleted
308
        $status = $this->beginTransaction();
309
        if ($status != 0) {
310
            $this->rollbackTransaction();
311
312
            return -1;
313
        }
314
315
        if ($schema === '') {
316
            $schema = $this->_schema;
317
        }
318
319
        $status = $this->delete($table, $key, $schema);
320
        if ($status != 0 || $this->conn->Affected_Rows() != 1) {
321
            $this->rollbackTransaction();
322
323
            return -2;
324
        }
325
326
        // End transaction
327
        return $this->endTransaction();
328
    }
329
330
    abstract public function fieldClean(&$str);
331
332
    abstract public function beginTransaction();
333
334
    abstract public function rollbackTransaction();
335
336
    abstract public function endTransaction();
337
338
    abstract public function execute($sql);
339
340
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
341
342
    abstract public function selectSet($sql);
343
344
    abstract public function clean(&$str);
345
346
    abstract public function phpBool($parameter);
347
348
    abstract public function hasCreateTableLikeWithConstraints();
349
350
    abstract public function hasCreateTableLikeWithIndexes();
351
352
    abstract public function hasTablespaces();
353
354
    abstract public function delete($table, $conditions, $schema = '');
355
356
    abstract public function fieldArrayClean(&$arr);
357
358
    abstract public function hasCreateFieldWithConstraints();
359
360
    abstract public function getAttributeNames($table, $atts);
361
362
    abstract public function hasObjectID($table);
363
}
364