| 1 | <?php
|
|---|
| 2 | /**
|
|---|
| 3 | *
|
|---|
| 4 | * @package dbal
|
|---|
| 5 | * @version $Id$
|
|---|
| 6 | * @copyright (c) 2005 phpBB Group
|
|---|
| 7 | * @license http://opensource.org/licenses/gpl-license.php GNU Public License
|
|---|
| 8 | *
|
|---|
| 9 | */
|
|---|
| 10 |
|
|---|
| 11 | /**
|
|---|
| 12 | * @ignore
|
|---|
| 13 | */
|
|---|
| 14 | if (!defined('IN_PHPBB'))
|
|---|
| 15 | {
|
|---|
| 16 | exit;
|
|---|
| 17 | }
|
|---|
| 18 |
|
|---|
| 19 | include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
|
|---|
| 20 |
|
|---|
| 21 | /**
|
|---|
| 22 | * PostgreSQL Database Abstraction Layer
|
|---|
| 23 | * Minimum Requirement is Version 7.3+
|
|---|
| 24 | * @package dbal
|
|---|
| 25 | */
|
|---|
| 26 | class dbal_postgres extends dbal
|
|---|
| 27 | {
|
|---|
| 28 | var $last_query_text = '';
|
|---|
| 29 |
|
|---|
| 30 | /**
|
|---|
| 31 | * Connect to server
|
|---|
| 32 | */
|
|---|
| 33 | function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
|
|---|
| 34 | {
|
|---|
| 35 | $connect_string = '';
|
|---|
| 36 |
|
|---|
| 37 | if ($sqluser)
|
|---|
| 38 | {
|
|---|
| 39 | $connect_string .= "user=$sqluser ";
|
|---|
| 40 | }
|
|---|
| 41 |
|
|---|
| 42 | if ($sqlpassword)
|
|---|
| 43 | {
|
|---|
| 44 | $connect_string .= "password=$sqlpassword ";
|
|---|
| 45 | }
|
|---|
| 46 |
|
|---|
| 47 | if ($sqlserver)
|
|---|
| 48 | {
|
|---|
| 49 | if (strpos($sqlserver, ':') !== false)
|
|---|
| 50 | {
|
|---|
| 51 | list($sqlserver, $port) = explode(':', $sqlserver);
|
|---|
| 52 | }
|
|---|
| 53 |
|
|---|
| 54 | if ($sqlserver !== 'localhost')
|
|---|
| 55 | {
|
|---|
| 56 | $connect_string .= "host=$sqlserver ";
|
|---|
| 57 | }
|
|---|
| 58 |
|
|---|
| 59 | if ($port)
|
|---|
| 60 | {
|
|---|
| 61 | $connect_string .= "port=$port ";
|
|---|
| 62 | }
|
|---|
| 63 | }
|
|---|
| 64 |
|
|---|
| 65 | $schema = '';
|
|---|
| 66 |
|
|---|
| 67 | if ($database)
|
|---|
| 68 | {
|
|---|
| 69 | $this->dbname = $database;
|
|---|
| 70 | if (strpos($database, '.') !== false)
|
|---|
| 71 | {
|
|---|
| 72 | list($database, $schema) = explode('.', $database);
|
|---|
| 73 | }
|
|---|
| 74 | $connect_string .= "dbname=$database";
|
|---|
| 75 | }
|
|---|
| 76 |
|
|---|
| 77 | $this->persistency = $persistency;
|
|---|
| 78 |
|
|---|
| 79 | $this->db_connect_id = ($this->persistency) ? @pg_pconnect($connect_string, $new_link) : @pg_connect($connect_string, $new_link);
|
|---|
| 80 |
|
|---|
| 81 | if ($this->db_connect_id)
|
|---|
| 82 | {
|
|---|
| 83 | if (version_compare($this->sql_server_info(true), '8.2', '>='))
|
|---|
| 84 | {
|
|---|
| 85 | $this->multi_insert = true;
|
|---|
| 86 | }
|
|---|
| 87 |
|
|---|
| 88 | if ($schema !== '')
|
|---|
| 89 | {
|
|---|
| 90 | @pg_query($this->db_connect_id, 'SET search_path TO ' . $schema);
|
|---|
| 91 | }
|
|---|
| 92 | return $this->db_connect_id;
|
|---|
| 93 | }
|
|---|
| 94 |
|
|---|
| 95 | return $this->sql_error('');
|
|---|
| 96 | }
|
|---|
| 97 |
|
|---|
| 98 | /**
|
|---|
| 99 | * Version information about used database
|
|---|
| 100 | * @param bool $raw if true, only return the fetched sql_server_version
|
|---|
| 101 | * @return string sql server version
|
|---|
| 102 | */
|
|---|
| 103 | function sql_server_info($raw = false)
|
|---|
| 104 | {
|
|---|
| 105 | global $cache;
|
|---|
| 106 |
|
|---|
| 107 | if (empty($cache) || ($this->sql_server_version = $cache->get('pgsql_version')) === false)
|
|---|
| 108 | {
|
|---|
| 109 | $query_id = @pg_query($this->db_connect_id, 'SELECT VERSION() AS version');
|
|---|
| 110 | $row = @pg_fetch_assoc($query_id, null);
|
|---|
| 111 | @pg_free_result($query_id);
|
|---|
| 112 |
|
|---|
| 113 | $this->sql_server_version = (!empty($row['version'])) ? trim(substr($row['version'], 10)) : 0;
|
|---|
| 114 |
|
|---|
| 115 | if (!empty($cache))
|
|---|
| 116 | {
|
|---|
| 117 | $cache->put('pgsql_version', $this->sql_server_version);
|
|---|
| 118 | }
|
|---|
| 119 | }
|
|---|
| 120 |
|
|---|
| 121 | return ($raw) ? $this->sql_server_version : 'PostgreSQL ' . $this->sql_server_version;
|
|---|
| 122 | }
|
|---|
| 123 |
|
|---|
| 124 | /**
|
|---|
| 125 | * SQL Transaction
|
|---|
| 126 | * @access private
|
|---|
| 127 | */
|
|---|
| 128 | function _sql_transaction($status = 'begin')
|
|---|
| 129 | {
|
|---|
| 130 | switch ($status)
|
|---|
| 131 | {
|
|---|
| 132 | case 'begin':
|
|---|
| 133 | return @pg_query($this->db_connect_id, 'BEGIN');
|
|---|
| 134 | break;
|
|---|
| 135 |
|
|---|
| 136 | case 'commit':
|
|---|
| 137 | return @pg_query($this->db_connect_id, 'COMMIT');
|
|---|
| 138 | break;
|
|---|
| 139 |
|
|---|
| 140 | case 'rollback':
|
|---|
| 141 | return @pg_query($this->db_connect_id, 'ROLLBACK');
|
|---|
| 142 | break;
|
|---|
| 143 | }
|
|---|
| 144 |
|
|---|
| 145 | return true;
|
|---|
| 146 | }
|
|---|
| 147 |
|
|---|
| 148 | /**
|
|---|
| 149 | * Base query method
|
|---|
| 150 | *
|
|---|
| 151 | * @param string $query Contains the SQL query which shall be executed
|
|---|
| 152 | * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
|
|---|
| 153 | * @return mixed When casted to bool the returned value returns true on success and false on failure
|
|---|
| 154 | *
|
|---|
| 155 | * @access public
|
|---|
| 156 | */
|
|---|
| 157 | function sql_query($query = '', $cache_ttl = 0)
|
|---|
| 158 | {
|
|---|
| 159 | if ($query != '')
|
|---|
| 160 | {
|
|---|
| 161 | global $cache;
|
|---|
| 162 |
|
|---|
| 163 | // EXPLAIN only in extra debug mode
|
|---|
| 164 | if (defined('DEBUG_EXTRA'))
|
|---|
| 165 | {
|
|---|
| 166 | $this->sql_report('start', $query);
|
|---|
| 167 | }
|
|---|
| 168 |
|
|---|
| 169 | $this->last_query_text = $query;
|
|---|
| 170 | $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
|
|---|
| 171 | $this->sql_add_num_queries($this->query_result);
|
|---|
| 172 |
|
|---|
| 173 | if ($this->query_result === false)
|
|---|
| 174 | {
|
|---|
| 175 | if (($this->query_result = @pg_query($this->db_connect_id, $query)) === false)
|
|---|
| 176 | {
|
|---|
| 177 | $this->sql_error($query);
|
|---|
| 178 | }
|
|---|
| 179 |
|
|---|
| 180 | if (defined('DEBUG_EXTRA'))
|
|---|
| 181 | {
|
|---|
| 182 | $this->sql_report('stop', $query);
|
|---|
| 183 | }
|
|---|
| 184 |
|
|---|
| 185 | if ($cache_ttl && method_exists($cache, 'sql_save'))
|
|---|
| 186 | {
|
|---|
| 187 | $this->open_queries[(int) $this->query_result] = $this->query_result;
|
|---|
| 188 | $cache->sql_save($query, $this->query_result, $cache_ttl);
|
|---|
| 189 | }
|
|---|
| 190 | else if (strpos($query, 'SELECT') === 0 && $this->query_result)
|
|---|
| 191 | {
|
|---|
| 192 | $this->open_queries[(int) $this->query_result] = $this->query_result;
|
|---|
| 193 | }
|
|---|
| 194 | }
|
|---|
| 195 | else if (defined('DEBUG_EXTRA'))
|
|---|
| 196 | {
|
|---|
| 197 | $this->sql_report('fromcache', $query);
|
|---|
| 198 | }
|
|---|
| 199 | }
|
|---|
| 200 | else
|
|---|
| 201 | {
|
|---|
| 202 | return false;
|
|---|
| 203 | }
|
|---|
| 204 |
|
|---|
| 205 | return $this->query_result;
|
|---|
| 206 | }
|
|---|
| 207 |
|
|---|
| 208 | /**
|
|---|
| 209 | * Build db-specific query data
|
|---|
| 210 | * @access private
|
|---|
| 211 | */
|
|---|
| 212 | function _sql_custom_build($stage, $data)
|
|---|
| 213 | {
|
|---|
| 214 | return $data;
|
|---|
| 215 | }
|
|---|
| 216 |
|
|---|
| 217 | /**
|
|---|
| 218 | * Build LIMIT query
|
|---|
| 219 | */
|
|---|
| 220 | function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
|
|---|
| 221 | {
|
|---|
| 222 | $this->query_result = false;
|
|---|
| 223 |
|
|---|
| 224 | // if $total is set to 0 we do not want to limit the number of rows
|
|---|
| 225 | if ($total == 0)
|
|---|
| 226 | {
|
|---|
| 227 | $total = 'ALL';
|
|---|
| 228 | }
|
|---|
| 229 |
|
|---|
| 230 | $query .= "\n LIMIT $total OFFSET $offset";
|
|---|
| 231 |
|
|---|
| 232 | return $this->sql_query($query, $cache_ttl);
|
|---|
| 233 | }
|
|---|
| 234 |
|
|---|
| 235 | /**
|
|---|
| 236 | * Return number of affected rows
|
|---|
| 237 | */
|
|---|
| 238 | function sql_affectedrows()
|
|---|
| 239 | {
|
|---|
| 240 | return ($this->query_result) ? @pg_affected_rows($this->query_result) : false;
|
|---|
| 241 | }
|
|---|
| 242 |
|
|---|
| 243 | /**
|
|---|
| 244 | * Fetch current row
|
|---|
| 245 | */
|
|---|
| 246 | function sql_fetchrow($query_id = false)
|
|---|
| 247 | {
|
|---|
| 248 | global $cache;
|
|---|
| 249 |
|
|---|
| 250 | if ($query_id === false)
|
|---|
| 251 | {
|
|---|
| 252 | $query_id = $this->query_result;
|
|---|
| 253 | }
|
|---|
| 254 |
|
|---|
| 255 | if (isset($cache->sql_rowset[$query_id]))
|
|---|
| 256 | {
|
|---|
| 257 | return $cache->sql_fetchrow($query_id);
|
|---|
| 258 | }
|
|---|
| 259 |
|
|---|
| 260 | return ($query_id !== false) ? @pg_fetch_assoc($query_id, null) : false;
|
|---|
| 261 | }
|
|---|
| 262 |
|
|---|
| 263 | /**
|
|---|
| 264 | * Seek to given row number
|
|---|
| 265 | * rownum is zero-based
|
|---|
| 266 | */
|
|---|
| 267 | function sql_rowseek($rownum, &$query_id)
|
|---|
| 268 | {
|
|---|
| 269 | global $cache;
|
|---|
| 270 |
|
|---|
| 271 | if ($query_id === false)
|
|---|
| 272 | {
|
|---|
| 273 | $query_id = $this->query_result;
|
|---|
| 274 | }
|
|---|
| 275 |
|
|---|
| 276 | if (isset($cache->sql_rowset[$query_id]))
|
|---|
| 277 | {
|
|---|
| 278 | return $cache->sql_rowseek($rownum, $query_id);
|
|---|
| 279 | }
|
|---|
| 280 |
|
|---|
| 281 | return ($query_id !== false) ? @pg_result_seek($query_id, $rownum) : false;
|
|---|
| 282 | }
|
|---|
| 283 |
|
|---|
| 284 | /**
|
|---|
| 285 | * Get last inserted id after insert statement
|
|---|
| 286 | */
|
|---|
| 287 | function sql_nextid()
|
|---|
| 288 | {
|
|---|
| 289 | $query_id = $this->query_result;
|
|---|
| 290 |
|
|---|
| 291 | if ($query_id !== false && $this->last_query_text != '')
|
|---|
| 292 | {
|
|---|
| 293 | if (preg_match("/^INSERT[\t\n ]+INTO[\t\n ]+([a-z0-9\_\-]+)/is", $this->last_query_text, $tablename))
|
|---|
| 294 | {
|
|---|
| 295 | $query = "SELECT currval('" . $tablename[1] . "_seq') AS last_value";
|
|---|
| 296 | $temp_q_id = @pg_query($this->db_connect_id, $query);
|
|---|
| 297 |
|
|---|
| 298 | if (!$temp_q_id)
|
|---|
| 299 | {
|
|---|
| 300 | return false;
|
|---|
| 301 | }
|
|---|
| 302 |
|
|---|
| 303 | $temp_result = @pg_fetch_assoc($temp_q_id, NULL);
|
|---|
| 304 | @pg_free_result($query_id);
|
|---|
| 305 |
|
|---|
| 306 | return ($temp_result) ? $temp_result['last_value'] : false;
|
|---|
| 307 | }
|
|---|
| 308 | }
|
|---|
| 309 |
|
|---|
| 310 | return false;
|
|---|
| 311 | }
|
|---|
| 312 |
|
|---|
| 313 | /**
|
|---|
| 314 | * Free sql result
|
|---|
| 315 | */
|
|---|
| 316 | function sql_freeresult($query_id = false)
|
|---|
| 317 | {
|
|---|
| 318 | global $cache;
|
|---|
| 319 |
|
|---|
| 320 | if ($query_id === false)
|
|---|
| 321 | {
|
|---|
| 322 | $query_id = $this->query_result;
|
|---|
| 323 | }
|
|---|
| 324 |
|
|---|
| 325 | if (isset($cache->sql_rowset[$query_id]))
|
|---|
| 326 | {
|
|---|
| 327 | return $cache->sql_freeresult($query_id);
|
|---|
| 328 | }
|
|---|
| 329 |
|
|---|
| 330 | if (isset($this->open_queries[(int) $query_id]))
|
|---|
| 331 | {
|
|---|
| 332 | unset($this->open_queries[(int) $query_id]);
|
|---|
| 333 | return @pg_free_result($query_id);
|
|---|
| 334 | }
|
|---|
| 335 |
|
|---|
| 336 | return false;
|
|---|
| 337 | }
|
|---|
| 338 |
|
|---|
| 339 | /**
|
|---|
| 340 | * Escape string used in sql query
|
|---|
| 341 | * Note: Do not use for bytea values if we may use them at a later stage
|
|---|
| 342 | */
|
|---|
| 343 | function sql_escape($msg)
|
|---|
| 344 | {
|
|---|
| 345 | return @pg_escape_string($msg);
|
|---|
| 346 | }
|
|---|
| 347 |
|
|---|
| 348 | /**
|
|---|
| 349 | * Build LIKE expression
|
|---|
| 350 | * @access private
|
|---|
| 351 | */
|
|---|
| 352 | function _sql_like_expression($expression)
|
|---|
| 353 | {
|
|---|
| 354 | return $expression;
|
|---|
| 355 | }
|
|---|
| 356 |
|
|---|
| 357 | /**
|
|---|
| 358 | * return sql error array
|
|---|
| 359 | * @access private
|
|---|
| 360 | */
|
|---|
| 361 | function _sql_error()
|
|---|
| 362 | {
|
|---|
| 363 | return array(
|
|---|
| 364 | 'message' => (!$this->db_connect_id) ? @pg_last_error() : @pg_last_error($this->db_connect_id),
|
|---|
| 365 | 'code' => ''
|
|---|
| 366 | );
|
|---|
| 367 | }
|
|---|
| 368 |
|
|---|
| 369 | /**
|
|---|
| 370 | * Close sql connection
|
|---|
| 371 | * @access private
|
|---|
| 372 | */
|
|---|
| 373 | function _sql_close()
|
|---|
| 374 | {
|
|---|
| 375 | return @pg_close($this->db_connect_id);
|
|---|
| 376 | }
|
|---|
| 377 |
|
|---|
| 378 | /**
|
|---|
| 379 | * Build db-specific report
|
|---|
| 380 | * @access private
|
|---|
| 381 | */
|
|---|
| 382 | function _sql_report($mode, $query = '')
|
|---|
| 383 | {
|
|---|
| 384 | switch ($mode)
|
|---|
| 385 | {
|
|---|
| 386 | case 'start':
|
|---|
| 387 |
|
|---|
| 388 | $explain_query = $query;
|
|---|
| 389 | if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
|---|
| 390 | {
|
|---|
| 391 | $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
|---|
| 392 | }
|
|---|
| 393 | else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
|
|---|
| 394 | {
|
|---|
| 395 | $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
|
|---|
| 396 | }
|
|---|
| 397 |
|
|---|
| 398 | if (preg_match('/^SELECT/', $explain_query))
|
|---|
| 399 | {
|
|---|
| 400 | $html_table = false;
|
|---|
| 401 |
|
|---|
| 402 | if ($result = @pg_query($this->db_connect_id, "EXPLAIN $explain_query"))
|
|---|
| 403 | {
|
|---|
| 404 | while ($row = @pg_fetch_assoc($result, NULL))
|
|---|
| 405 | {
|
|---|
| 406 | $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
|
|---|
| 407 | }
|
|---|
| 408 | }
|
|---|
| 409 | @pg_free_result($result);
|
|---|
| 410 |
|
|---|
| 411 | if ($html_table)
|
|---|
| 412 | {
|
|---|
| 413 | $this->html_hold .= '</table>';
|
|---|
| 414 | }
|
|---|
| 415 | }
|
|---|
| 416 |
|
|---|
| 417 | break;
|
|---|
| 418 |
|
|---|
| 419 | case 'fromcache':
|
|---|
| 420 | $endtime = explode(' ', microtime());
|
|---|
| 421 | $endtime = $endtime[0] + $endtime[1];
|
|---|
| 422 |
|
|---|
| 423 | $result = @pg_query($this->db_connect_id, $query);
|
|---|
| 424 | while ($void = @pg_fetch_assoc($result, NULL))
|
|---|
| 425 | {
|
|---|
| 426 | // Take the time spent on parsing rows into account
|
|---|
| 427 | }
|
|---|
| 428 | @pg_free_result($result);
|
|---|
| 429 |
|
|---|
| 430 | $splittime = explode(' ', microtime());
|
|---|
| 431 | $splittime = $splittime[0] + $splittime[1];
|
|---|
| 432 |
|
|---|
| 433 | $this->sql_report('record_fromcache', $query, $endtime, $splittime);
|
|---|
| 434 |
|
|---|
| 435 | break;
|
|---|
| 436 | }
|
|---|
| 437 | }
|
|---|
| 438 | }
|
|---|
| 439 |
|
|---|
| 440 | ?>
|
|---|