1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace PHPPgAdmin\Database; |
4
|
|
|
|
5
|
|
|
/* |
6
|
|
|
* Parent class of all ADODB objects. |
7
|
|
|
* |
8
|
|
|
* $Id: ADOdbBase.php,v 1.24 2008/02/20 20:43:10 ioguix Exp $ |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
class ADOdbBase |
12
|
|
|
{ |
13
|
|
|
use \PHPPgAdmin\HelperTrait; |
14
|
|
|
|
15
|
|
|
public $conn; |
16
|
|
|
|
17
|
|
|
// The backend platform. Set to UNKNOWN by default. |
18
|
|
|
public $platform = 'UNKNOWN'; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Base constructor |
22
|
|
|
* |
23
|
|
|
* @param &$conn The connection object |
24
|
|
|
*/ |
25
|
|
|
public function __construct(&$conn) |
26
|
|
|
{ |
27
|
|
|
$this->prtrace('instanced connection class'); |
28
|
|
|
$this->conn = $conn; |
29
|
|
|
} |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Turns on or off query debugging |
33
|
|
|
* |
34
|
|
|
* @param $debug True to turn on debugging, false otherwise |
35
|
|
|
*/ |
36
|
|
|
public function setDebug($debug) |
37
|
|
|
{ |
38
|
|
|
$this->conn->debug = $debug; |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* Cleans (escapes) an array |
43
|
|
|
* |
44
|
|
|
* @param $arr The array to clean, by reference |
45
|
|
|
* @return The cleaned array |
46
|
|
|
*/ |
47
|
|
|
public function arrayClean(&$arr) |
48
|
|
|
{ |
49
|
|
|
reset($arr); |
50
|
|
|
while (list($k, $v) = each($arr)) { |
51
|
|
|
$arr[$k] = addslashes($v); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
return $arr; |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* Executes a query on the underlying connection |
59
|
|
|
* |
60
|
|
|
* @param $sql The SQL query to execute |
61
|
|
|
* @return A recordset |
62
|
|
|
*/ |
63
|
|
|
public function execute($sql) |
64
|
|
|
{ |
65
|
|
|
// Execute the statement |
66
|
|
|
$rs = $this->conn->Execute($sql); |
67
|
|
|
|
68
|
|
|
// If failure, return error value |
69
|
|
|
return $this->conn->ErrorNo(); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Closes the connection the database class |
74
|
|
|
* relies on. |
75
|
|
|
*/ |
76
|
|
|
public function close() |
77
|
|
|
{ |
78
|
|
|
$this->conn->close(); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Retrieves a ResultSet from a query |
83
|
|
|
* |
84
|
|
|
* @param $sql The SQL statement to be executed |
85
|
|
|
* @return A recordset |
86
|
|
|
*/ |
87
|
|
|
public function selectSet($sql) |
88
|
|
|
{ |
89
|
|
|
// Execute the statement |
90
|
|
|
$rs = $this->conn->Execute($sql); |
91
|
|
|
|
92
|
|
|
if (!$rs) { |
93
|
|
|
return $this->conn->ErrorNo(); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
return $rs; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Retrieves a single value from a query |
101
|
|
|
* |
102
|
|
|
* @@ assumes that the query will return only one row - returns field value in the first row |
103
|
|
|
* |
104
|
|
|
* @param $sql The SQL statement to be executed |
105
|
|
|
* @param $field The field name to be returned |
106
|
|
|
* @return A single field value |
107
|
|
|
* @return -1 No rows were found |
108
|
|
|
*/ |
109
|
|
|
public function selectField($sql, $field) |
110
|
|
|
{ |
111
|
|
|
// Execute the statement |
112
|
|
|
$rs = $this->conn->Execute($sql); |
113
|
|
|
|
114
|
|
|
// If failure, or no rows returned, return error value |
115
|
|
|
if (!$rs) { |
116
|
|
|
return $this->conn->ErrorNo(); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
if ($rs->RecordCount() == 0) { |
120
|
|
|
return -1; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
return $rs->fields[$field]; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* Delete from the database |
128
|
|
|
* |
129
|
|
|
* @param $table The name of the table |
130
|
|
|
* @param $conditions (array) A map of field names to conditions |
131
|
|
|
* @param string $schema (optional) The table's schema |
132
|
|
|
* @return int 0 success |
133
|
|
|
*/ |
134
|
|
|
public function delete($table, $conditions, $schema = '') |
135
|
|
|
{ |
136
|
|
|
$this->fieldClean($table); |
137
|
|
|
|
138
|
|
|
reset($conditions); |
139
|
|
|
|
140
|
|
|
if (!empty($schema)) { |
141
|
|
|
$this->fieldClean($schema); |
142
|
|
|
$schema = "\"{$schema}\"."; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
// Build clause |
146
|
|
|
$sql = ''; |
147
|
|
|
while (list($key, $value) = each($conditions)) { |
148
|
|
|
$this->clean($key); |
149
|
|
|
$this->clean($value); |
150
|
|
|
if ($sql) { |
151
|
|
|
$sql .= " AND \"{$key}\"='{$value}'"; |
152
|
|
|
} else { |
153
|
|
|
$sql = "DELETE FROM {$schema}\"{$table}\" WHERE \"{$key}\"='{$value}'"; |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
// Check for failures |
158
|
|
|
if (!$this->conn->Execute($sql)) { |
159
|
|
|
// Check for referential integrity failure |
160
|
|
|
if (stristr($this->conn->ErrorMsg(), 'referential')) { |
161
|
|
|
return -1; |
162
|
|
|
} |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
// Check for no rows modified |
166
|
|
|
if ($this->conn->Affected_Rows() == 0) { |
167
|
|
|
return -2; |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
return $this->conn->ErrorNo(); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
/** |
174
|
|
|
* Cleans (escapes) an object name (eg. table, field) |
175
|
|
|
* |
176
|
|
|
* @param $str The string to clean, by reference |
177
|
|
|
* @return The cleaned string |
178
|
|
|
*/ |
179
|
|
|
public function fieldClean(&$str) |
180
|
|
|
{ |
181
|
|
|
$str = str_replace('"', '""', $str); |
182
|
|
|
|
183
|
|
|
return $str; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Cleans (escapes) a string |
188
|
|
|
* |
189
|
|
|
* @param string $str The string to clean, by reference |
190
|
|
|
* @return string The cleaned string |
191
|
|
|
*/ |
192
|
|
|
public function clean(&$str) |
193
|
|
|
{ |
194
|
|
|
$str = addslashes($str); |
195
|
|
|
|
196
|
|
|
return $str; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Insert a set of values into the database |
201
|
|
|
* |
202
|
|
|
* @param $table The table to insert into |
203
|
|
|
* @param $vars (array) A mapping of the field names to the values to be inserted |
204
|
|
|
* @return int 0 success |
205
|
|
|
*/ |
206
|
|
|
public function insert($table, $vars) |
207
|
|
|
{ |
208
|
|
|
$this->fieldClean($table); |
209
|
|
|
|
210
|
|
|
// Build clause |
211
|
|
|
if (sizeof($vars) > 0) { |
212
|
|
|
$fields = ''; |
213
|
|
|
$values = ''; |
214
|
|
|
foreach ($vars as $key => $value) { |
215
|
|
|
$this->clean($key); |
216
|
|
|
$this->clean($value); |
217
|
|
|
|
218
|
|
|
if ($fields) { |
219
|
|
|
$fields .= ", \"{$key}\""; |
220
|
|
|
} else { |
221
|
|
|
$fields = "INSERT INTO \"{$table}\" (\"{$key}\""; |
222
|
|
|
} |
223
|
|
|
|
224
|
|
|
if ($values) { |
225
|
|
|
$values .= ", '{$value}'"; |
226
|
|
|
} else { |
227
|
|
|
$values = ") VALUES ('{$value}'"; |
228
|
|
|
} |
229
|
|
|
} |
230
|
|
|
$sql = $fields . $values . ')'; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
// Check for failures |
234
|
|
View Code Duplication |
if (!$this->conn->Execute($sql)) { |
|
|
|
|
235
|
|
|
// Check for unique constraint failure |
236
|
|
|
if (stristr($this->conn->ErrorMsg(), 'unique')) { |
237
|
|
|
return -1; |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
if (stristr($this->conn->ErrorMsg(), 'referential')) { |
241
|
|
|
return -2; |
242
|
|
|
} // Check for referential integrity failure |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
return $this->conn->ErrorNo(); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
/** |
249
|
|
|
* Update a row in the database |
250
|
|
|
* |
251
|
|
|
* @param $table The table that is to be updated |
252
|
|
|
* @param $vars (array) A mapping of the field names to the values to be updated |
253
|
|
|
* @param $where (array) A mapping of field names to values for the where clause |
254
|
|
|
* @param array $nulls (array, optional) An array of fields to be set null |
255
|
|
|
* @return int 0 success |
256
|
|
|
*/ |
257
|
|
|
public function update($table, $vars, $where, $nulls = []) |
258
|
|
|
{ |
259
|
|
|
$this->fieldClean($table); |
260
|
|
|
|
261
|
|
|
$setClause = ''; |
262
|
|
|
$whereClause = ''; |
263
|
|
|
|
264
|
|
|
// Populate the syntax arrays |
265
|
|
|
reset($vars); |
266
|
|
View Code Duplication |
while (list($key, $value) = each($vars)) { |
|
|
|
|
267
|
|
|
$this->fieldClean($key); |
268
|
|
|
$this->clean($value); |
269
|
|
|
if ($setClause) { |
270
|
|
|
$setClause .= ", \"{$key}\"='{$value}'"; |
271
|
|
|
} else { |
272
|
|
|
$setClause = "UPDATE \"{$table}\" SET \"{$key}\"='{$value}'"; |
273
|
|
|
} |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
reset($nulls); |
277
|
|
|
while (list(, $value) = each($nulls)) { |
278
|
|
|
$this->fieldClean($value); |
279
|
|
|
if ($setClause) { |
280
|
|
|
$setClause .= ", \"{$value}\"=NULL"; |
281
|
|
|
} else { |
282
|
|
|
$setClause = "UPDATE \"{$table}\" SET \"{$value}\"=NULL"; |
283
|
|
|
} |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
reset($where); |
287
|
|
View Code Duplication |
while (list($key, $value) = each($where)) { |
|
|
|
|
288
|
|
|
$this->fieldClean($key); |
289
|
|
|
$this->clean($value); |
290
|
|
|
if ($whereClause) { |
291
|
|
|
$whereClause .= " AND \"{$key}\"='{$value}'"; |
292
|
|
|
} else { |
293
|
|
|
$whereClause = " WHERE \"{$key}\"='{$value}'"; |
294
|
|
|
} |
295
|
|
|
} |
296
|
|
|
|
297
|
|
|
// Check for failures |
298
|
|
View Code Duplication |
if (!$this->conn->Execute($setClause . $whereClause)) { |
|
|
|
|
299
|
|
|
// Check for unique constraint failure |
300
|
|
|
if (stristr($this->conn->ErrorMsg(), 'unique')) { |
301
|
|
|
return -1; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
if (stristr($this->conn->ErrorMsg(), 'referential')) { |
305
|
|
|
return -2; |
306
|
|
|
} // Check for referential integrity failure |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
// Check for no rows modified |
310
|
|
|
if ($this->conn->Affected_Rows() == 0) { |
311
|
|
|
return -3; |
312
|
|
|
} |
313
|
|
|
|
314
|
|
|
return $this->conn->ErrorNo(); |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Begin a transaction |
319
|
|
|
* |
320
|
|
|
* @return bool 0 success |
321
|
|
|
*/ |
322
|
|
|
public function beginTransaction() |
323
|
|
|
{ |
324
|
|
|
return !$this->conn->BeginTrans(); |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* End a transaction |
329
|
|
|
* |
330
|
|
|
* @return bool 0 success |
331
|
|
|
*/ |
332
|
|
|
public function endTransaction() |
333
|
|
|
{ |
334
|
|
|
return !$this->conn->CommitTrans(); |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
/** |
338
|
|
|
* Roll back a transaction |
339
|
|
|
* |
340
|
|
|
* @return bool 0 success |
341
|
|
|
*/ |
342
|
|
|
public function rollbackTransaction() |
343
|
|
|
{ |
344
|
|
|
return !$this->conn->RollbackTrans(); |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
/** |
348
|
|
|
* Get the backend platform |
349
|
|
|
* |
350
|
|
|
* @return The backend platform |
351
|
|
|
*/ |
352
|
|
|
public function getPlatform() |
353
|
|
|
{ |
354
|
|
|
//return $this->conn->platform; |
355
|
|
|
return 'UNKNOWN'; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
// Type conversion routines |
359
|
|
|
|
360
|
|
|
/** |
361
|
|
|
* Change the value of a parameter to database representation depending on whether it evaluates to true or false |
362
|
|
|
* |
363
|
|
|
* @param $parameter the parameter |
364
|
|
|
* @return \PHPPgAdmin\Database\the |
365
|
|
|
*/ |
366
|
|
|
public function dbBool(&$parameter) |
367
|
|
|
{ |
368
|
|
|
return $parameter; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
/** |
372
|
|
|
* Change a parameter from database representation to a boolean, (others evaluate to false) |
373
|
|
|
* |
374
|
|
|
* @param $parameter the parameter |
375
|
|
|
* @return \PHPPgAdmin\Database\the |
376
|
|
|
*/ |
377
|
|
|
public function phpBool($parameter) |
378
|
|
|
{ |
379
|
|
|
return $parameter; |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
/** |
383
|
|
|
* Change a db array into a PHP array |
384
|
|
|
* |
385
|
|
|
* @param $dbarr |
386
|
|
|
* @return \PHPPgAdmin\Database\A PHP array |
387
|
|
|
* @internal param String $arr representing the DB array |
388
|
|
|
*/ |
389
|
|
|
public function phpArray($dbarr) |
390
|
|
|
{ |
391
|
|
|
// Take off the first and last characters (the braces) |
392
|
|
|
$arr = substr($dbarr, 1, strlen($dbarr) - 2); |
393
|
|
|
|
394
|
|
|
// Pick out array entries by carefully parsing. This is necessary in order |
395
|
|
|
// to cope with double quotes and commas, etc. |
396
|
|
|
$elements = []; |
397
|
|
|
$i = $j = 0; |
398
|
|
|
$in_quotes = false; |
399
|
|
View Code Duplication |
while ($i < strlen($arr)) { |
|
|
|
|
400
|
|
|
// If current char is a double quote and it's not escaped, then |
401
|
|
|
// enter quoted bit |
402
|
|
|
$char = substr($arr, $i, 1); |
403
|
|
|
if ($char == '"' && ($i == 0 || substr($arr, $i - 1, 1) != '\\')) { |
404
|
|
|
$in_quotes = !$in_quotes; |
405
|
|
|
} elseif ($char == ',' && !$in_quotes) { |
406
|
|
|
// Add text so far to the array |
407
|
|
|
$elements[] = substr($arr, $j, $i - $j); |
408
|
|
|
$j = $i + 1; |
409
|
|
|
} |
410
|
|
|
$i++; |
411
|
|
|
} |
412
|
|
|
// Add final text to the array |
413
|
|
|
$elements[] = substr($arr, $j); |
414
|
|
|
|
415
|
|
|
// Do one further loop over the elements array to remote double quoting |
416
|
|
|
// and escaping of double quotes and backslashes |
417
|
|
|
for ($i = 0; $i < sizeof($elements); $i++) { |
|
|
|
|
418
|
|
|
$v = $elements[$i]; |
419
|
|
View Code Duplication |
if (strpos($v, '"') === 0) { |
|
|
|
|
420
|
|
|
$v = substr($v, 1, strlen($v) - 2); |
421
|
|
|
$v = str_replace('\\"', '"', $v); |
422
|
|
|
$v = str_replace('\\\\', '\\', $v); |
423
|
|
|
$elements[$i] = $v; |
424
|
|
|
} |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
return $elements; |
428
|
|
|
} |
429
|
|
|
} |
430
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.