1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file has all the main functions in it that relate to the Postgre database. |
5
|
|
|
* |
6
|
|
|
* @name ElkArte Forum |
7
|
|
|
* @copyright ElkArte Forum contributors |
8
|
|
|
* @license BSD http://opensource.org/licenses/BSD-3-Clause |
9
|
|
|
* |
10
|
|
|
* This file contains code covered by: |
11
|
|
|
* copyright: 2011 Simple Machines (http://www.simplemachines.org) |
12
|
|
|
* license: BSD, See included LICENSE.TXT for terms and conditions. |
13
|
|
|
* |
14
|
|
|
* @version 2.0 dev |
15
|
|
|
* |
16
|
|
|
*/ |
17
|
|
|
|
18
|
|
|
// Let's define the name of the class so that we will be able to use it in the instantiations |
19
|
|
|
if (!defined('DB_TYPE')) |
20
|
|
|
define('DB_TYPE', 'PostgreSQL'); |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* PostgreSQL database class, implements database class to control mysql functions |
24
|
|
|
*/ |
25
|
|
|
class Database_PostgreSQL extends Database_Abstract |
26
|
|
|
{ |
27
|
|
|
/** |
28
|
|
|
* Holds current instance of the class |
29
|
|
|
* @var Database_PostgreSQL |
30
|
|
|
*/ |
31
|
|
|
private static $_db = null; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* Holds last query result |
35
|
|
|
* @var string |
36
|
|
|
*/ |
37
|
|
|
private $_db_last_result = null; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* Since PostgreSQL doesn't support INSERT REPLACE we are using this to remember |
41
|
|
|
* the rows affected by the delete |
42
|
|
|
* @var int |
43
|
|
|
*/ |
44
|
|
|
private $_db_replace_result = null; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* A variable to remember if a transaction was started already or not |
48
|
|
|
* @var boolean |
49
|
|
|
*/ |
50
|
|
|
private $_in_transaction = false; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Initializes a database connection. |
54
|
|
|
* It returns the connection, if successful. |
55
|
|
|
* |
56
|
|
|
* @param string $db_server |
57
|
|
|
* @param string $db_name |
58
|
|
|
* @param string $db_user |
59
|
|
|
* @param string $db_passwd |
60
|
|
|
* @param string $db_prefix |
61
|
|
|
* @param mixed[] $db_options |
62
|
|
|
* |
63
|
|
|
* @return resource |
64
|
|
|
* @throws Elk_Exception |
65
|
|
|
*/ |
66
|
|
|
public static function initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $db_options = array()) |
|
|
|
|
67
|
|
|
{ |
68
|
|
|
// initialize the instance... if not done already! |
69
|
|
|
if (self::$_db === null) |
70
|
|
|
self::$_db = new self(); |
71
|
|
|
|
72
|
|
View Code Duplication |
if (!empty($db_options['port'])) |
73
|
|
|
$db_port = ' port=' . (int) $db_options['port']; |
74
|
|
|
else |
75
|
|
|
$db_port = ''; |
76
|
|
|
|
77
|
|
|
if (!empty($db_options['persist'])) |
78
|
|
|
$connection = @pg_pconnect('host=' . $db_server . $db_port . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\''); |
79
|
|
|
else |
80
|
|
|
$connection = @pg_connect('host=' . $db_server . $db_port . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\''); |
81
|
|
|
|
82
|
|
|
// Something's wrong, show an error if its fatal (which we assume it is) |
83
|
|
View Code Duplication |
if (!$connection) |
84
|
|
|
{ |
85
|
|
|
if (!empty($db_options['non_fatal'])) |
86
|
|
|
return null; |
87
|
|
|
else |
88
|
|
|
Errors::instance()->display_db_error(); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
self::$_db->_connection = $connection; |
92
|
|
|
|
93
|
|
|
return $connection; |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
/** |
97
|
|
|
* Fix the database prefix if necessary. |
98
|
|
|
* Do nothing on postgreSQL |
99
|
|
|
* |
100
|
|
|
* @param string $db_prefix |
101
|
|
|
* @param string $db_name |
102
|
|
|
* |
103
|
|
|
* @return string |
104
|
|
|
*/ |
105
|
|
|
public function fix_prefix($db_prefix, $db_name) |
106
|
|
|
{ |
107
|
|
|
return $db_prefix; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Do a query. Takes care of errors too. |
112
|
|
|
* Special queries may need additional replacements to be appropriate |
113
|
|
|
* for PostgreSQL. |
114
|
|
|
* |
115
|
|
|
* @param string $identifier |
116
|
|
|
* @param string $db_string |
117
|
|
|
* @param mixed[] $db_values |
118
|
|
|
* @param resource|null $connection |
119
|
|
|
* |
120
|
|
|
* @return bool|resource|string |
121
|
|
|
* @throws Elk_Exception |
122
|
|
|
*/ |
123
|
|
|
public function query($identifier, $db_string, $db_values = array(), $connection = null) |
124
|
|
|
{ |
125
|
|
|
global $db_show_debug, $time_start, $modSettings; |
126
|
|
|
|
127
|
|
|
// Decide which connection to use. |
128
|
|
|
$connection = $connection === null ? $this->_connection : $connection; |
129
|
|
|
|
130
|
|
|
// Special queries that need processing. |
131
|
|
|
$replacements = array( |
132
|
|
|
'ban_suggest_error_ips' => array( |
133
|
|
|
'~RLIKE~' => '~', |
134
|
|
|
'~\\.~' => '\.', |
135
|
|
|
), |
136
|
|
|
'ban_suggest_message_ips' => array( |
137
|
|
|
'~RLIKE~' => '~', |
138
|
|
|
'~\\.~' => '\.', |
139
|
|
|
), |
140
|
|
|
'consolidate_spider_stats' => array( |
141
|
|
|
'~MONTH\(log_time\), DAYOFMONTH\(log_time\)~' => 'MONTH(CAST(CAST(log_time AS abstime) AS timestamp)), DAYOFMONTH(CAST(CAST(log_time AS abstime) AS timestamp))', |
142
|
|
|
), |
143
|
|
|
'attach_download_increase' => array( |
144
|
|
|
'~LOW_PRIORITY~' => '', |
145
|
|
|
), |
146
|
|
|
'insert_log_search_topics' => array( |
147
|
|
|
'~NOT RLIKE~' => '!~', |
148
|
|
|
), |
149
|
|
|
'insert_log_search_results_no_index' => array( |
150
|
|
|
'~NOT RLIKE~' => '!~', |
151
|
|
|
), |
152
|
|
|
'insert_log_search_results_subject' => array( |
153
|
|
|
'~NOT RLIKE~' => '!~', |
154
|
|
|
), |
155
|
|
|
'pm_conversation_list' => array( |
156
|
|
|
'~ORDER\\s+BY\\s+\\{raw:sort\\}~' => 'ORDER BY ' . (isset($db_values['sort']) ? ($db_values['sort'] === 'pm.id_pm' ? 'MAX(pm.id_pm)' : $db_values['sort']) : ''), |
157
|
|
|
), |
158
|
|
|
'profile_board_stats' => array( |
159
|
|
|
'~COUNT\(\*\) \/ MAX\(b.num_posts\)~' => 'CAST(COUNT(*) AS DECIMAL) / CAST(b.num_posts AS DECIMAL)', |
160
|
|
|
), |
161
|
|
|
); |
162
|
|
|
|
163
|
|
|
if (isset($replacements[$identifier])) |
164
|
|
|
$db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string); |
165
|
|
|
|
166
|
|
|
// Limits need to be a little different. |
167
|
|
|
$db_string = preg_replace('~\sLIMIT\s(\d+|{int:.+}),\s*(\d+|{int:.+})\s*$~i', 'LIMIT $2 OFFSET $1', $db_string); |
168
|
|
|
|
169
|
|
|
if (trim($db_string) == '') |
170
|
|
|
return false; |
171
|
|
|
|
172
|
|
|
// Comments that are allowed in a query are preg_removed. |
173
|
|
|
static $allowed_comments_from = array( |
174
|
|
|
'~\s+~s', |
175
|
|
|
'~/\*!40001 SQL_NO_CACHE \*/~', |
176
|
|
|
'~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~', |
177
|
|
|
'~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~', |
178
|
|
|
); |
179
|
|
|
static $allowed_comments_to = array( |
180
|
|
|
' ', |
181
|
|
|
'', |
182
|
|
|
'', |
183
|
|
|
'', |
184
|
|
|
); |
185
|
|
|
|
186
|
|
|
// One more query.... |
187
|
|
|
$this->_query_count++; |
188
|
|
|
$this->_db_replace_result = null; |
189
|
|
|
|
190
|
|
View Code Duplication |
if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override'])) |
191
|
|
|
$this->error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__); |
192
|
|
|
|
193
|
|
View Code Duplication |
if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false)) |
194
|
|
|
{ |
195
|
|
|
// Store these values for use in the callback function. |
196
|
|
|
$this->_db_callback_values = $db_values; |
197
|
|
|
$this->_db_callback_connection = $connection; |
198
|
|
|
|
199
|
|
|
// Inject the values passed to this function. |
200
|
|
|
$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', array($this, 'replacement__callback'), $db_string); |
201
|
|
|
|
202
|
|
|
// No need for them any longer. |
203
|
|
|
$this->_db_callback_values = array(); |
204
|
|
|
$this->_db_callback_connection = null; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
// Debugging. |
208
|
|
View Code Duplication |
if ($db_show_debug === true) |
209
|
|
|
{ |
210
|
|
|
$debug = Debug::instance(); |
211
|
|
|
|
212
|
|
|
// Get the file and line number this function was called. |
213
|
|
|
list ($file, $line) = $this->error_backtrace('', '', 'return', __FILE__, __LINE__); |
214
|
|
|
|
215
|
|
|
if (!empty($_SESSION['debug_redirect'])) |
216
|
|
|
{ |
217
|
|
|
$debug->merge_db($_SESSION['debug_redirect']); |
218
|
|
|
// @todo this may be off by 1 |
219
|
|
|
$this->_query_count += count($_SESSION['debug_redirect']); |
220
|
|
|
$_SESSION['debug_redirect'] = array(); |
221
|
|
|
} |
222
|
|
|
|
223
|
|
|
// Don't overload it. |
224
|
|
|
$st = microtime(true); |
225
|
|
|
$db_cache = array(); |
226
|
|
|
$db_cache['q'] = $this->_query_count < 50 ? $db_string : '...'; |
227
|
|
|
$db_cache['f'] = $file; |
228
|
|
|
$db_cache['l'] = $line; |
229
|
|
|
$db_cache['s'] = $st - $time_start; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over. |
233
|
|
|
if (empty($modSettings['disableQueryCheck'])) |
234
|
|
|
{ |
235
|
|
|
$clean = ''; |
236
|
|
|
$old_pos = 0; |
237
|
|
|
$pos = -1; |
238
|
|
View Code Duplication |
while (true) |
239
|
|
|
{ |
240
|
|
|
$pos = strpos($db_string, '\'', $pos + 1); |
241
|
|
|
if ($pos === false) |
242
|
|
|
break; |
243
|
|
|
$clean .= substr($db_string, $old_pos, $pos - $old_pos); |
244
|
|
|
|
245
|
|
|
while (true) |
246
|
|
|
{ |
247
|
|
|
$pos1 = strpos($db_string, '\'', $pos + 1); |
248
|
|
|
$pos2 = strpos($db_string, '\'\'', $pos + 1); |
249
|
|
|
|
250
|
|
|
if ($pos1 === false) |
251
|
|
|
break; |
252
|
|
|
elseif ($pos2 === false || $pos2 > $pos1) |
253
|
|
|
{ |
254
|
|
|
$pos = $pos1; |
255
|
|
|
break; |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
$pos = $pos2 + 1; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
$clean .= ' %s '; |
262
|
|
|
$old_pos = $pos + 1; |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
$clean .= substr($db_string, $old_pos); |
266
|
|
|
$clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean))); |
267
|
|
|
|
268
|
|
|
// Comments? We don't use comments in our queries, we leave 'em outside! |
269
|
|
View Code Duplication |
if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false) |
270
|
|
|
$fail = true; |
271
|
|
|
// Trying to change passwords, slow us down, or something? |
272
|
|
|
elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0) |
273
|
|
|
$fail = true; |
274
|
|
|
elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0) |
275
|
|
|
$fail = true; |
276
|
|
|
|
277
|
|
|
if (!empty($fail) && class_exists('Errors')) |
278
|
|
|
$this->error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__); |
279
|
|
|
|
280
|
|
|
// If we are updating something, better start a transaction so that indexes may be kept consistent |
281
|
|
|
if (!$this->_in_transaction && strpos($clean, 'update') !== false) |
282
|
|
|
$this->db_transaction('begin', $connection); |
|
|
|
|
283
|
|
|
} |
284
|
|
|
|
285
|
|
|
$this->_db_last_result = @pg_query($connection, $db_string); |
|
|
|
|
286
|
|
|
|
287
|
|
|
if ($this->_db_last_result === false && !$this->_skip_error) |
288
|
|
|
{ |
289
|
|
|
$this->error($db_string, $connection); |
|
|
|
|
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
// Revert not to skip errors |
293
|
|
|
if ($this->_skip_error === true) |
294
|
|
|
{ |
295
|
|
|
$this->_skip_error = false; |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
if ($this->_in_transaction) |
299
|
|
|
$this->db_transaction('commit', $connection); |
|
|
|
|
300
|
|
|
|
301
|
|
|
// Debugging. |
302
|
|
View Code Duplication |
if ($db_show_debug === true) |
303
|
|
|
{ |
304
|
|
|
$db_cache['t'] = microtime(true) - $st; |
|
|
|
|
305
|
|
|
$debug->db_query($db_cache); |
|
|
|
|
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
return $this->_db_last_result; |
309
|
|
|
} |
310
|
|
|
|
311
|
|
|
/** |
312
|
|
|
* Affected rows from previous operation. |
313
|
|
|
* |
314
|
|
|
* @param resource|null $result |
315
|
|
|
*/ |
316
|
|
|
public function affected_rows($result = null) |
317
|
|
|
{ |
318
|
|
|
if ($this->_db_replace_result !== null) |
319
|
|
|
return $this->_db_replace_result; |
320
|
|
|
elseif ($result === null && !$this->_db_last_result) |
321
|
|
|
return 0; |
322
|
|
|
|
323
|
|
|
return pg_affected_rows($result === null ? $this->_db_last_result : $result); |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* Last inserted id. |
328
|
|
|
* |
329
|
|
|
* @param string $table |
330
|
|
|
* @param string|null $field = null |
331
|
|
|
* @param resource|null $connection = null |
332
|
|
|
* @throws Elk_Exception |
333
|
|
|
*/ |
334
|
|
|
public function insert_id($table, $field = null, $connection = null) |
335
|
|
|
{ |
336
|
|
|
global $db_prefix; |
337
|
|
|
|
338
|
|
|
$table = str_replace('{db_prefix}', $db_prefix, $table); |
339
|
|
|
|
340
|
|
|
$connection = $connection === null ? $this->_connection : $connection; |
341
|
|
|
|
342
|
|
|
// Try get the last ID for the auto increment field. |
343
|
|
|
$request = $this->query('', 'SELECT CURRVAL(\'' . $table . '_seq\') AS insertID', |
344
|
|
|
array( |
345
|
|
|
), |
346
|
|
|
$connection |
|
|
|
|
347
|
|
|
); |
348
|
|
|
|
349
|
|
|
if (!$request) |
350
|
|
|
return false; |
351
|
|
|
|
352
|
|
|
list ($lastID) = $this->fetch_row($request); |
|
|
|
|
353
|
|
|
$this->free_result($request); |
|
|
|
|
354
|
|
|
|
355
|
|
|
return $lastID; |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* Tracking the current row. |
360
|
|
|
* Fetch a row from the resultset given as parameter. |
361
|
|
|
* |
362
|
|
|
* @param resource $request |
363
|
|
|
* @param integer|bool $counter = false |
364
|
|
|
*/ |
365
|
|
View Code Duplication |
public function fetch_row($request, $counter = false) |
366
|
|
|
{ |
367
|
|
|
global $db_row_count; |
368
|
|
|
|
369
|
|
|
if ($counter !== false) |
370
|
|
|
return pg_fetch_row($request, $counter); |
371
|
|
|
|
372
|
|
|
// Reset the row counter... |
373
|
|
|
if (!isset($db_row_count[(int) $request])) |
374
|
|
|
$db_row_count[(int) $request] = 0; |
375
|
|
|
|
376
|
|
|
// Return the right row. |
377
|
|
|
return @pg_fetch_row($request, $db_row_count[(int) $request]++); |
378
|
|
|
} |
379
|
|
|
|
380
|
|
|
/** |
381
|
|
|
* Free the resultset. |
382
|
|
|
* |
383
|
|
|
* @param resource $result |
384
|
|
|
*/ |
385
|
|
|
public function free_result($result) |
386
|
|
|
{ |
387
|
|
|
// Just delegate to the native function |
388
|
|
|
pg_free_result($result); |
389
|
|
|
} |
390
|
|
|
|
391
|
|
|
/** |
392
|
|
|
* Get the number of rows in the result. |
393
|
|
|
* |
394
|
|
|
* @param resource $result |
395
|
|
|
*/ |
396
|
|
|
public function num_rows($result) |
397
|
|
|
{ |
398
|
|
|
// simply delegate to the native function |
399
|
|
|
return pg_num_rows($result); |
400
|
|
|
} |
401
|
|
|
|
402
|
|
|
/** |
403
|
|
|
* Get the number of fields in the resultset. |
404
|
|
|
* |
405
|
|
|
* @param resource $request |
406
|
|
|
*/ |
407
|
|
|
public function num_fields($request) |
408
|
|
|
{ |
409
|
|
|
return pg_num_fields($request); |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* Reset the internal result pointer. |
414
|
|
|
* |
415
|
|
|
* @param boolean $request |
416
|
|
|
* @param integer $counter |
417
|
|
|
*/ |
418
|
|
|
public function data_seek($request, $counter) |
419
|
|
|
{ |
420
|
|
|
global $db_row_count; |
421
|
|
|
|
422
|
|
|
$db_row_count[(int) $request] = $counter; |
423
|
|
|
|
424
|
|
|
return true; |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
/** |
428
|
|
|
* Do a transaction. |
429
|
|
|
* |
430
|
|
|
* @param string $type - the step to perform (i.e. 'begin', 'commit', 'rollback') |
431
|
|
|
* @param resource|null $connection = null |
432
|
|
|
*/ |
433
|
|
|
public function db_transaction($type = 'commit', $connection = null) |
434
|
|
|
{ |
435
|
|
|
// Decide which connection to use |
436
|
|
|
$connection = $connection === null ? $this->_connection : $connection; |
437
|
|
|
|
438
|
|
|
if ($type == 'begin') |
439
|
|
|
{ |
440
|
|
|
$this->_in_transaction = true; |
441
|
|
|
return @pg_query($connection, 'BEGIN'); |
442
|
|
|
} |
443
|
|
|
elseif ($type == 'rollback') |
444
|
|
|
return @pg_query($connection, 'ROLLBACK'); |
445
|
|
|
elseif ($type == 'commit') |
446
|
|
|
{ |
447
|
|
|
$this->_in_transaction = false; |
448
|
|
|
return @pg_query($connection, 'COMMIT'); |
449
|
|
|
} |
450
|
|
|
|
451
|
|
|
return false; |
452
|
|
|
} |
453
|
|
|
|
454
|
|
|
/** |
455
|
|
|
* Return last error string from the database server |
456
|
|
|
* |
457
|
|
|
* @param resource|null $connection = null |
458
|
|
|
*/ |
459
|
|
|
public function last_error($connection = null) |
460
|
|
|
{ |
461
|
|
|
// Decide which connection to use |
462
|
|
|
$connection = $connection === null ? $this->_connection : $connection; |
463
|
|
|
|
464
|
|
|
if (is_resource($connection)) |
465
|
|
|
return pg_last_error($connection); |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
/** |
469
|
|
|
* Database error. |
470
|
|
|
* Backtrace, log, try to fix. |
471
|
|
|
* |
472
|
|
|
* @param string $db_string |
473
|
|
|
* @param resource|null $connection = null |
474
|
|
|
* |
475
|
|
|
* @throws Elk_Exception |
476
|
|
|
*/ |
477
|
|
|
public function error($db_string, $connection = null) |
478
|
|
|
{ |
479
|
|
|
global $txt, $context, $modSettings, $db_show_debug; |
480
|
|
|
|
481
|
|
|
// We'll try recovering the file and line number the original db query was called from. |
482
|
|
|
list ($file, $line) = $this->error_backtrace('', '', 'return', __FILE__, __LINE__); |
483
|
|
|
|
484
|
|
|
// Decide which connection to use |
485
|
|
|
$connection = $connection === null ? $this->_connection : $connection; |
486
|
|
|
|
487
|
|
|
// This is the error message... |
488
|
|
|
$query_error = @pg_last_error($connection); |
489
|
|
|
|
490
|
|
|
// Log the error. |
491
|
|
View Code Duplication |
if (class_exists('Errors')) |
492
|
|
|
{ |
493
|
|
|
Errors::instance()->log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n" . $db_string : ''), 'database', $file, $line); |
494
|
|
|
} |
495
|
|
|
|
496
|
|
|
// Nothing's defined yet... just die with it. |
497
|
|
|
if (empty($context) || empty($txt)) |
498
|
|
|
die($query_error); |
|
|
|
|
499
|
|
|
|
500
|
|
|
// Show an error message, if possible. |
501
|
|
|
$context['error_title'] = $txt['database_error']; |
502
|
|
View Code Duplication |
if (allowedTo('admin_forum')) |
503
|
|
|
$context['error_message'] = nl2br($query_error) . '<br />' . $txt['file'] . ': ' . $file . '<br />' . $txt['line'] . ': ' . $line; |
504
|
|
|
else |
505
|
|
|
$context['error_message'] = $txt['try_again']; |
506
|
|
|
|
507
|
|
|
// Add database version that we know of, for the admin to know. (and ask for support) |
508
|
|
View Code Duplication |
if (allowedTo('admin_forum')) |
509
|
|
|
$context['error_message'] .= '<br /><br />' . sprintf($txt['database_error_versions'], $modSettings['elkVersion']); |
510
|
|
|
|
511
|
|
View Code Duplication |
if (allowedTo('admin_forum') && $db_show_debug === true) |
512
|
|
|
$context['error_message'] .= '<br /><br />' . nl2br($db_string); |
513
|
|
|
|
514
|
|
|
// It's already been logged... don't log it again. |
515
|
|
|
throw new Elk_Exception($context['error_message'], false); |
516
|
|
|
} |
517
|
|
|
|
518
|
|
|
/** |
519
|
|
|
* Insert data. |
520
|
|
|
* |
521
|
|
|
* @param string $method - options 'replace', 'ignore', 'insert' |
522
|
|
|
* @param string $table |
523
|
|
|
* @param mixed[] $columns |
524
|
|
|
* @param mixed[] $data |
525
|
|
|
* @param mixed[] $keys |
526
|
|
|
* @param bool $disable_trans = false |
527
|
|
|
* @param resource|null $connection = null |
528
|
|
|
* @throws Elk_Exception |
529
|
|
|
*/ |
530
|
|
|
public function insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null) |
531
|
|
|
{ |
532
|
|
|
global $db_prefix; |
533
|
|
|
|
534
|
|
|
$connection = $connection === null ? $this->_connection : $connection; |
535
|
|
|
|
536
|
|
|
// With nothing to insert, simply return. |
537
|
|
|
if (empty($data)) |
538
|
|
|
return; |
539
|
|
|
|
540
|
|
|
// Inserting data as a single row can be done as a single array. |
541
|
|
|
if (!is_array($data[array_rand($data)])) |
542
|
|
|
$data = array($data); |
543
|
|
|
|
544
|
|
|
// Replace the prefix holder with the actual prefix. |
545
|
|
|
$table = str_replace('{db_prefix}', $db_prefix, $table); |
546
|
|
|
|
547
|
|
|
$priv_trans = false; |
548
|
|
|
if ((count($data) > 1 || $method == 'replace') && !$this->_in_transaction && !$disable_trans) |
549
|
|
|
{ |
550
|
|
|
$this->db_transaction('begin', $connection); |
|
|
|
|
551
|
|
|
$priv_trans = true; |
552
|
|
|
} |
553
|
|
|
|
554
|
|
|
// PostgreSQL doesn't support replace: we implement a MySQL-compatible behavior instead |
555
|
|
|
if ($method == 'replace') |
556
|
|
|
{ |
557
|
|
|
$count = 0; |
558
|
|
|
$where = ''; |
559
|
|
|
$db_replace_result = 0; |
560
|
|
|
foreach ($columns as $columnName => $type) |
561
|
|
|
{ |
562
|
|
|
// Are we restricting the length? |
563
|
|
|
if (strpos($type, 'string-') !== false) |
564
|
|
|
$actualType = sprintf($columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $count); |
565
|
|
|
else |
566
|
|
|
$actualType = sprintf($columnName . ' = {%1$s:%2$s}, ', $type, $count); |
567
|
|
|
|
568
|
|
|
// A key? That's what we were looking for. |
569
|
|
|
if (in_array($columnName, $keys)) |
570
|
|
|
$where .= (empty($where) ? '' : ' AND ') . substr($actualType, 0, -2); |
571
|
|
|
$count++; |
572
|
|
|
} |
573
|
|
|
|
574
|
|
|
// Make it so. |
575
|
|
|
if (!empty($where) && !empty($data)) |
576
|
|
|
{ |
577
|
|
|
foreach ($data as $k => $entry) |
578
|
|
|
{ |
579
|
|
|
$this->query('', ' |
580
|
|
|
DELETE FROM ' . $table . |
581
|
|
|
' WHERE ' . $where, |
582
|
|
|
$entry, $connection |
|
|
|
|
583
|
|
|
); |
584
|
|
|
$db_replace_result += (!$this->_db_last_result ? 0 : pg_affected_rows($this->_db_last_result)); |
585
|
|
|
} |
586
|
|
|
} |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
if (!empty($data)) |
590
|
|
|
{ |
591
|
|
|
// Create the mold for a single row insert. |
592
|
|
|
$insertData = '('; |
593
|
|
View Code Duplication |
foreach ($columns as $columnName => $type) |
594
|
|
|
{ |
595
|
|
|
// Are we restricting the length? |
596
|
|
|
if (strpos($type, 'string-') !== false) |
597
|
|
|
$insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName); |
598
|
|
|
else |
599
|
|
|
$insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName); |
600
|
|
|
} |
601
|
|
|
$insertData = substr($insertData, 0, -2) . ')'; |
602
|
|
|
|
603
|
|
|
// Create an array consisting of only the columns. |
604
|
|
|
$indexed_columns = array_keys($columns); |
605
|
|
|
|
606
|
|
|
// Here's where the variables are injected to the query. |
607
|
|
|
$insertRows = array(); |
608
|
|
|
foreach ($data as $dataRow) |
609
|
|
|
$insertRows[] = $this->quote($insertData, $this->_array_combine($indexed_columns, $dataRow), $connection); |
|
|
|
|
610
|
|
|
|
611
|
|
|
$inserted_results = 0; |
612
|
|
|
$skip_error = $method == 'ignore' || $table === $db_prefix . 'log_errors'; |
613
|
|
|
$this->_skip_error = $skip_error; |
614
|
|
|
|
615
|
|
|
// Do the insert. |
616
|
|
|
$this->query('', ' |
617
|
|
|
INSERT INTO ' . $table . '("' . implode('", "', $indexed_columns) . '") |
618
|
|
|
VALUES |
619
|
|
|
' . implode(', |
620
|
|
|
', $insertRows), |
621
|
|
|
array( |
622
|
|
|
'security_override' => true, |
623
|
|
|
), |
624
|
|
|
$connection |
|
|
|
|
625
|
|
|
); |
626
|
|
|
$inserted_results += (!$this->_db_last_result ? 0 : pg_affected_rows($this->_db_last_result)); |
627
|
|
|
|
628
|
|
|
if (isset($db_replace_result)) |
629
|
|
|
$this->_db_replace_result = $db_replace_result + $inserted_results; |
630
|
|
|
} |
631
|
|
|
|
632
|
|
|
if ($priv_trans) |
633
|
|
|
$this->db_transaction('commit', $connection); |
|
|
|
|
634
|
|
|
} |
635
|
|
|
|
636
|
|
|
/** |
637
|
|
|
* Unescape an escaped string! |
638
|
|
|
* |
639
|
|
|
* @param string $string |
640
|
|
|
*/ |
641
|
|
|
public function unescape_string($string) |
642
|
|
|
{ |
643
|
|
|
return strtr($string, array('\'\'' => '\'')); |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
/** |
647
|
|
|
* Returns whether the database system supports ignore. |
648
|
|
|
* |
649
|
|
|
* @return false |
650
|
|
|
*/ |
651
|
|
|
public function support_ignore() |
652
|
|
|
{ |
653
|
|
|
return false; |
654
|
|
|
} |
655
|
|
|
|
656
|
|
|
/** |
657
|
|
|
* Gets all the necessary INSERTs for the table named table_name. |
658
|
|
|
* It goes in 250 row segments. |
659
|
|
|
* |
660
|
|
|
* @param string $tableName - the table to create the inserts for. |
661
|
|
|
* @param bool $new_table |
662
|
|
|
* |
663
|
|
|
* @return string the query to insert the data back in, or an empty string if the table was empty. |
664
|
|
|
* @throws Elk_Exception |
665
|
|
|
*/ |
666
|
|
|
public function insert_sql($tableName, $new_table = false) |
667
|
|
|
{ |
668
|
|
|
global $db_prefix; |
669
|
|
|
|
670
|
|
|
static $start = 0, $num_rows, $fields, $limit; |
671
|
|
|
|
672
|
|
View Code Duplication |
if ($new_table) |
673
|
|
|
{ |
674
|
|
|
$limit = strstr($tableName, 'log_') !== false ? 500 : 250; |
675
|
|
|
$start = 0; |
676
|
|
|
} |
677
|
|
|
|
678
|
|
|
$data = ''; |
679
|
|
|
$tableName = str_replace('{db_prefix}', $db_prefix, $tableName); |
680
|
|
|
|
681
|
|
|
// This will be handy... |
682
|
|
|
$crlf = "\r\n"; |
683
|
|
|
|
684
|
|
|
$result = $this->query('', ' |
685
|
|
|
SELECT * |
686
|
|
|
FROM ' . $tableName . ' |
687
|
|
|
LIMIT ' . $start . ', ' . $limit, |
688
|
|
|
array( |
689
|
|
|
'security_override' => true, |
690
|
|
|
) |
691
|
|
|
); |
692
|
|
|
|
693
|
|
|
// The number of rows, just for record keeping and breaking INSERTs up. |
694
|
|
|
$num_rows = $this->num_rows($result); |
|
|
|
|
695
|
|
|
|
696
|
|
|
if ($num_rows == 0) |
697
|
|
|
return ''; |
698
|
|
|
|
699
|
|
|
if ($new_table) |
700
|
|
|
{ |
701
|
|
|
$fields = array_keys($this->fetch_assoc($result)); |
|
|
|
|
702
|
|
|
$this->data_seek($result, 0); |
|
|
|
|
703
|
|
|
} |
704
|
|
|
|
705
|
|
|
// Start it off with the basic INSERT INTO. |
706
|
|
|
$insert_msg = 'INSERT INTO ' . $tableName . $crlf . "\t" . '(' . implode(', ', $fields) . ')' . $crlf . 'VALUES ' . $crlf . "\t"; |
707
|
|
|
|
708
|
|
|
// Loop through each row. |
709
|
|
View Code Duplication |
while ($row = $this->fetch_assoc($result)) |
|
|
|
|
710
|
|
|
{ |
711
|
|
|
// Get the fields in this row... |
712
|
|
|
$field_list = array(); |
713
|
|
|
|
714
|
|
|
foreach ($row as $key => $item) |
715
|
|
|
{ |
716
|
|
|
// Try to figure out the type of each field. (NULL, number, or 'string'.) |
717
|
|
|
if (!isset($item)) |
718
|
|
|
$field_list[] = 'NULL'; |
719
|
|
|
elseif (is_numeric($item) && (int) $item == $item) |
720
|
|
|
$field_list[] = $item; |
721
|
|
|
else |
722
|
|
|
$field_list[] = '\'' . $this->escape_string($item) . '\''; |
723
|
|
|
} |
724
|
|
|
|
725
|
|
|
// 'Insert' the data. |
726
|
|
|
$data .= $insert_msg . '(' . implode(', ', $field_list) . ');' . $crlf; |
727
|
|
|
} |
728
|
|
|
$this->free_result($result); |
|
|
|
|
729
|
|
|
|
730
|
|
|
$data .= $crlf; |
731
|
|
|
|
732
|
|
|
$start += $limit; |
733
|
|
|
|
734
|
|
|
return $data; |
735
|
|
|
} |
736
|
|
|
|
737
|
|
|
/** |
738
|
|
|
* Dumps the schema (CREATE) for a table. |
739
|
|
|
* |
740
|
|
|
* @param string $tableName - the table |
741
|
|
|
* |
742
|
|
|
* @return string - the CREATE statement as string |
743
|
|
|
* @throws Elk_Exception |
744
|
|
|
*/ |
745
|
|
|
public function db_table_sql($tableName) |
746
|
|
|
{ |
747
|
|
|
global $db_prefix; |
748
|
|
|
|
749
|
|
|
$tableName = str_replace('{db_prefix}', $db_prefix, $tableName); |
750
|
|
|
|
751
|
|
|
// This will be needed... |
752
|
|
|
$crlf = "\r\n"; |
753
|
|
|
|
754
|
|
|
// Start the create table... |
755
|
|
|
$schema_create = 'CREATE TABLE ' . $tableName . ' (' . $crlf; |
756
|
|
|
$index_create = ''; |
757
|
|
|
$seq_create = ''; |
758
|
|
|
|
759
|
|
|
// Find all the fields. |
760
|
|
|
$result = $this->query('', ' |
761
|
|
|
SELECT column_name, column_default, is_nullable, data_type, character_maximum_length |
762
|
|
|
FROM information_schema.columns |
763
|
|
|
WHERE table_name = {string:table} |
764
|
|
|
ORDER BY ordinal_position', |
765
|
|
|
array( |
766
|
|
|
'table' => $tableName, |
767
|
|
|
) |
768
|
|
|
); |
769
|
|
|
while ($row = $this->fetch_assoc($result)) |
|
|
|
|
770
|
|
|
{ |
771
|
|
|
if ($row['data_type'] == 'character varying') |
772
|
|
|
$row['data_type'] = 'varchar'; |
773
|
|
|
elseif ($row['data_type'] == 'character') |
774
|
|
|
$row['data_type'] = 'char'; |
775
|
|
|
|
776
|
|
|
if ($row['character_maximum_length']) |
777
|
|
|
$row['data_type'] .= '(' . $row['character_maximum_length'] . ')'; |
778
|
|
|
|
779
|
|
|
// Make the CREATE for this column. |
780
|
|
|
$schema_create .= ' "' . $row['column_name'] . '" ' . $row['data_type'] . ($row['is_nullable'] != 'YES' ? ' NOT NULL' : ''); |
781
|
|
|
|
782
|
|
|
// Add a default...? |
783
|
|
|
if (trim($row['column_default']) != '') |
784
|
|
|
{ |
785
|
|
|
$schema_create .= ' default ' . $row['column_default'] . ''; |
786
|
|
|
|
787
|
|
|
// Auto increment? |
788
|
|
|
if (preg_match('~nextval\(\'(.+?)\'(.+?)*\)~i', $row['column_default'], $matches) != 0) |
789
|
|
|
{ |
790
|
|
|
// Get to find the next variable first! |
791
|
|
|
$count_req = $this->query('', ' |
792
|
|
|
SELECT MAX("{raw:column}") |
793
|
|
|
FROM {raw:table}', |
794
|
|
|
array( |
795
|
|
|
'column' => $row['column_name'], |
796
|
|
|
'table' => $tableName, |
797
|
|
|
) |
798
|
|
|
); |
799
|
|
|
list ($max_ind) = $this->fetch_row($count_req); |
|
|
|
|
800
|
|
|
$this->free_result($count_req); |
|
|
|
|
801
|
|
|
|
802
|
|
|
// Get the right bloody start! |
803
|
|
|
$seq_create .= 'CREATE SEQUENCE ' . $matches[1] . ' START WITH ' . ($max_ind + 1) . ';' . $crlf . $crlf; |
804
|
|
|
} |
805
|
|
|
} |
806
|
|
|
|
807
|
|
|
$schema_create .= ',' . $crlf; |
808
|
|
|
} |
809
|
|
|
$this->free_result($result); |
|
|
|
|
810
|
|
|
|
811
|
|
|
// Take off the last comma. |
812
|
|
|
$schema_create = substr($schema_create, 0, -strlen($crlf) - 1); |
813
|
|
|
|
814
|
|
|
$result = $this->query('', ' |
815
|
|
|
SELECT CASE WHEN i.indisprimary THEN 1 ELSE 0 END AS is_primary, pg_get_indexdef(i.indexrelid) AS inddef |
816
|
|
|
FROM pg_class AS c |
817
|
|
|
INNER JOIN pg_index AS i ON (i.indrelid = c.oid) |
818
|
|
|
INNER JOIN pg_class AS c2 ON (c2.oid = i.indexrelid) |
819
|
|
|
WHERE c.relname = {string:table}', |
820
|
|
|
array( |
821
|
|
|
'table' => $tableName, |
822
|
|
|
) |
823
|
|
|
); |
824
|
|
|
|
825
|
|
|
while ($row = $this->fetch_assoc($result)) |
|
|
|
|
826
|
|
|
{ |
827
|
|
|
if ($row['is_primary']) |
828
|
|
|
{ |
829
|
|
|
if (preg_match('~\(([^\)]+?)\)~i', $row['inddef'], $matches) == 0) |
830
|
|
|
continue; |
831
|
|
|
|
832
|
|
|
$index_create .= $crlf . 'ALTER TABLE ' . $tableName . ' ADD PRIMARY KEY ("' . $matches[1] . '");'; |
833
|
|
|
} |
834
|
|
|
else |
835
|
|
|
$index_create .= $crlf . $row['inddef'] . ';'; |
836
|
|
|
} |
837
|
|
|
$this->free_result($result); |
|
|
|
|
838
|
|
|
|
839
|
|
|
// Finish it off! |
840
|
|
|
$schema_create .= $crlf . ');'; |
841
|
|
|
|
842
|
|
|
return $seq_create . $schema_create . $index_create; |
843
|
|
|
} |
844
|
|
|
|
845
|
|
|
/** |
846
|
|
|
* {@inheritdoc} |
847
|
|
|
*/ |
848
|
|
|
public function db_list_tables($db_name_str = false, $filter = false) |
849
|
|
|
{ |
850
|
|
|
$request = $this->query('', ' |
851
|
|
|
SELECT tablename |
852
|
|
|
FROM pg_tables |
853
|
|
|
WHERE schemaname = {string:schema_public}' . ($filter === false ? '' : ' |
854
|
|
|
AND tablename LIKE {string:filter}') . ' |
855
|
|
|
ORDER BY tablename', |
856
|
|
|
array( |
857
|
|
|
'schema_public' => 'public', |
858
|
|
|
'filter' => $filter, |
859
|
|
|
) |
860
|
|
|
); |
861
|
|
|
$tables = array(); |
862
|
|
|
while ($row = $this->fetch_row($request)) |
|
|
|
|
863
|
|
|
$tables[] = $row[0]; |
864
|
|
|
$this->free_result($request); |
|
|
|
|
865
|
|
|
|
866
|
|
|
return $tables; |
867
|
|
|
} |
868
|
|
|
|
869
|
|
|
/** |
870
|
|
|
* Backup $table to $backup_table. |
871
|
|
|
* |
872
|
|
|
* @param string $table |
873
|
|
|
* @param string $backup_table |
874
|
|
|
* @throws Elk_Exception |
875
|
|
|
*/ |
876
|
|
|
public function db_backup_table($table, $backup_table) |
877
|
|
|
{ |
878
|
|
|
global $db_prefix; |
879
|
|
|
|
880
|
|
|
$table = str_replace('{db_prefix}', $db_prefix, $table); |
881
|
|
|
|
882
|
|
|
// Do we need to drop it first? |
883
|
|
|
$db_table = db_table(); |
884
|
|
|
$db_table->db_drop_table($backup_table); |
885
|
|
|
|
886
|
|
|
// @todo Should we create backups of sequences as well? |
887
|
|
|
$this->query('', ' |
888
|
|
|
CREATE TABLE {raw:backup_table} |
889
|
|
|
( |
890
|
|
|
LIKE {raw:table} |
891
|
|
|
INCLUDING DEFAULTS |
892
|
|
|
)', |
893
|
|
|
array( |
894
|
|
|
'backup_table' => $backup_table, |
895
|
|
|
'table' => $table, |
896
|
|
|
) |
897
|
|
|
); |
898
|
|
|
|
899
|
|
|
$this->query('', ' |
900
|
|
|
INSERT INTO {raw:backup_table} |
901
|
|
|
SELECT * FROM {raw:table}', |
902
|
|
|
array( |
903
|
|
|
'backup_table' => $backup_table, |
904
|
|
|
'table' => $table, |
905
|
|
|
) |
906
|
|
|
); |
907
|
|
|
} |
908
|
|
|
|
909
|
|
|
/** |
910
|
|
|
* Get the server version number. |
911
|
|
|
* |
912
|
|
|
* @return string - the version |
913
|
|
|
*/ |
914
|
|
|
public function db_server_version() |
915
|
|
|
{ |
916
|
|
|
$version = pg_version(); |
917
|
|
|
|
918
|
|
|
return $version['server']; |
919
|
|
|
} |
920
|
|
|
|
921
|
|
|
/** |
922
|
|
|
* Get the name (title) of the database system. |
923
|
|
|
* |
924
|
|
|
* @return string |
925
|
|
|
*/ |
926
|
|
|
public function db_title() |
927
|
|
|
{ |
928
|
|
|
return 'PostgreSQL'; |
929
|
|
|
} |
930
|
|
|
|
931
|
|
|
/** |
932
|
|
|
* Whether the database system is case sensitive. |
933
|
|
|
* |
934
|
|
|
* @return boolean |
935
|
|
|
*/ |
936
|
|
|
public function db_case_sensitive() |
937
|
|
|
{ |
938
|
|
|
return true; |
939
|
|
|
} |
940
|
|
|
|
941
|
|
|
/** |
942
|
|
|
* Quotes identifiers for replacement__callback. |
943
|
|
|
* |
944
|
|
|
* @param mixed $replacement |
945
|
|
|
* @return string |
946
|
|
|
* @throws Elk_Exception |
947
|
|
|
*/ |
948
|
|
View Code Duplication |
protected function _replaceIdentifier($replacement) |
949
|
|
|
{ |
950
|
|
|
if (preg_match('~[a-z_][0-9,a-z,A-Z$_]{0,60}~', $replacement) !== 1) |
951
|
|
|
{ |
952
|
|
|
$this->error_backtrace('Wrong value type sent to the database. Invalid identifier used. (' . $replacement . ')', '', E_USER_ERROR, __FILE__, __LINE__); |
953
|
|
|
} |
954
|
|
|
|
955
|
|
|
return '"' . $replacement . '"'; |
956
|
|
|
} |
957
|
|
|
|
958
|
|
|
/** |
959
|
|
|
* Escape string for the database input |
960
|
|
|
* |
961
|
|
|
* @param string $string |
962
|
|
|
*/ |
963
|
|
|
public function escape_string($string) |
964
|
|
|
{ |
965
|
|
|
return pg_escape_string($string); |
966
|
|
|
} |
967
|
|
|
|
968
|
|
|
/** |
969
|
|
|
* Fetch next result as association. |
970
|
|
|
* |
971
|
|
|
* @param resource $request |
972
|
|
|
* @param int|bool $counter = false |
973
|
|
|
*/ |
974
|
|
View Code Duplication |
public function fetch_assoc($request, $counter = false) |
975
|
|
|
{ |
976
|
|
|
global $db_row_count; |
977
|
|
|
|
978
|
|
|
if ($counter !== false) |
979
|
|
|
return pg_fetch_assoc($request, $counter); |
980
|
|
|
|
981
|
|
|
// Reset the row counter... |
982
|
|
|
if (!isset($db_row_count[(int) $request])) |
983
|
|
|
$db_row_count[(int) $request] = 0; |
984
|
|
|
|
985
|
|
|
// Return the right row. |
986
|
|
|
return @pg_fetch_assoc($request, $db_row_count[(int) $request]++); |
987
|
|
|
} |
988
|
|
|
|
989
|
|
|
/** |
990
|
|
|
* Return server info. |
991
|
|
|
* |
992
|
|
|
* @return string |
993
|
|
|
*/ |
994
|
|
|
public function db_server_info() |
995
|
|
|
{ |
996
|
|
|
// give info on client! we use it in install and upgrade and such things. |
997
|
|
|
$version = pg_version(); |
998
|
|
|
|
999
|
|
|
return $version['client']; |
1000
|
|
|
} |
1001
|
|
|
|
1002
|
|
|
/** |
1003
|
|
|
* Return client version. |
1004
|
|
|
* |
1005
|
|
|
* @return string - the version |
1006
|
|
|
*/ |
1007
|
|
|
public function db_client_version() |
1008
|
|
|
{ |
1009
|
|
|
$version = pg_version(); |
1010
|
|
|
|
1011
|
|
|
return $version['client']; |
1012
|
|
|
} |
1013
|
|
|
|
1014
|
|
|
/** |
1015
|
|
|
* Dummy function really. Doesn't do anything on PostgreSQL. |
1016
|
|
|
* |
1017
|
|
|
* @param string|null $db_name = null |
1018
|
|
|
* @param resource|null $connection = null |
1019
|
|
|
* |
1020
|
|
|
* @return boolean |
1021
|
|
|
*/ |
1022
|
|
|
public function select_db($db_name = null, $connection = null) |
1023
|
|
|
{ |
1024
|
|
|
return true; |
1025
|
|
|
} |
1026
|
|
|
|
1027
|
|
|
/** |
1028
|
|
|
* Returns a reference to the existing instance |
1029
|
|
|
*/ |
1030
|
|
|
public static function db() |
1031
|
|
|
{ |
1032
|
|
|
return self::$_db; |
1033
|
|
|
} |
1034
|
|
|
|
1035
|
|
|
/** |
1036
|
|
|
* Finds out if the connection is still valid. |
1037
|
|
|
* |
1038
|
|
|
* @param postgre|null $connection = null |
1039
|
|
|
*/ |
1040
|
|
|
public function validConnection($connection = null) |
1041
|
|
|
{ |
1042
|
|
|
return is_resource($connection); |
1043
|
|
|
} |
1044
|
|
|
} |
1045
|
|
|
|
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.