- tackle some usability issues
[phpbb.git] / phpBB / includes / db / mssql.php
blobefe0b4333d6b98cc6fd9e0a0d780fee49c8eaee9
1 <?php
2 /**
4 * @package dbal
5 * @version $Id$
6 * @copyright (c) 2005 phpBB Group
7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
9 */
11 /**
13 if (!defined('IN_PHPBB'))
15 exit;
18 /**
19 * @ignore
21 if (!defined('SQL_LAYER'))
24 define('SQL_LAYER', 'mssql');
25 include($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
27 /**
28 * MSSQL Database Abstraction Layer
29 * Minimum Requirement is MSSQL 2000+
30 * @package dbal
32 class dbal_mssql extends dbal
34 /**
35 * Connect to server
37 function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false)
39 $this->persistency = $persistency;
40 $this->user = $sqluser;
41 $this->server = $sqlserver . (($port) ? ':' . $port : '');
42 $this->dbname = $database;
44 $this->db_connect_id = ($this->persistency) ? @mssql_pconnect($this->server, $this->user, $sqlpassword) : @mssql_connect($this->server, $this->user, $sqlpassword);
46 if ($this->db_connect_id && $this->dbname != '')
48 if (!@mssql_select_db($this->dbname, $this->db_connect_id))
50 @mssql_close($this->db_connect_id);
51 return false;
55 return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
58 /**
59 * Version information about used database
61 function sql_server_info()
63 $result_id = @mssql_query("SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY('productlevel'), SERVERPROPERTY('edition')", $this->db_connect_id);
65 $row = false;
66 if ($result_id)
68 $row = @mssql_fetch_assoc($result_id);
69 @mssql_free_result($result_id);
72 if ($row)
74 return 'MSSQL<br />' . implode(' ', $row);
77 return 'MSSQL';
80 /**
81 * SQL Transaction
82 * @access: private
84 function _sql_transaction($status = 'begin')
86 switch ($status)
88 case 'begin':
89 return @mssql_query('BEGIN TRANSACTION', $this->db_connect_id);
90 break;
92 case 'commit':
93 return @mssql_query('commit', $this->db_connect_id);
94 break;
96 case 'rollback':
97 return @mssql_query('ROLLBACK', $this->db_connect_id);
98 break;
101 return true;
105 * Base query method
107 function sql_query($query = '', $cache_ttl = 0)
109 if ($query != '')
111 global $cache;
113 // EXPLAIN only in extra debug mode
114 if (defined('DEBUG_EXTRA'))
116 $this->sql_report('start', $query);
119 $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
120 $this->sql_add_num_queries($this->query_result);
122 if (!$this->query_result)
124 if (($this->query_result = @mssql_query($query, $this->db_connect_id)) === false)
126 $this->sql_error($query);
129 if (defined('DEBUG_EXTRA'))
131 $this->sql_report('stop', $query);
134 if ($cache_ttl && method_exists($cache, 'sql_save'))
136 $this->open_queries[(int) $this->query_result] = $this->query_result;
137 $cache->sql_save($query, $this->query_result, $cache_ttl);
139 else if (strpos($query, 'SELECT') === 0 && $this->query_result)
141 $this->open_queries[(int) $this->query_result] = $this->query_result;
144 else if (defined('DEBUG_EXTRA'))
146 $this->sql_report('fromcache', $query);
149 else
151 return false;
154 return ($this->query_result) ? $this->query_result : false;
158 * Build LIMIT query
160 function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
162 if ($query != '')
164 $this->query_result = false;
166 // if $total is set to 0 we do not want to limit the number of rows
167 if ($total == 0)
169 $total = -1;
172 $row_offset = ($total) ? $offset : '';
173 $num_rows = ($total) ? $total : $offset;
175 if (strpos($query, 'SELECT DISTINCT') === 0)
177 $query = 'SELECT DISTINCT TOP ' . ($row_offset + $num_rows) . ' ' . substr($query, 15);
179 else
181 $query = 'SELECT TOP ' . ($row_offset + $num_rows) . ' ' . substr($query, 6);
184 $result = $this->sql_query($query, $cache_ttl);
186 // Seek by $row_offset rows
187 if ($row_offset)
189 $this->sql_rowseek($result, $row_offset);
192 return $result;
194 else
196 return false;
201 * Return number of rows
202 * Not used within core code
204 function sql_numrows($query_id = false)
206 if (!$query_id)
208 $query_id = $this->query_result;
211 return ($query_id) ? @mssql_num_rows($query_id) : false;
215 * Return number of affected rows
217 function sql_affectedrows()
219 return ($this->db_connect_id) ? @mssql_rows_affected($this->db_connect_id) : false;
223 * Fetch current row
225 function sql_fetchrow($query_id = false)
227 global $cache;
229 if (!$query_id)
231 $query_id = $this->query_result;
234 if (isset($cache->sql_rowset[$query_id]))
236 return $cache->sql_fetchrow($query_id);
239 $row = @mssql_fetch_assoc($query_id);
241 // I hope i am able to remove this later... hopefully only a PHP or MSSQL bug
242 if ($row)
244 foreach ($row as $key => $value)
246 $row[$key] = ($value === ' ') ? '' : $value;
250 return $row;
254 * Fetch field
255 * if rownum is false, the current row is used, else it is pointing to the row (zero-based)
257 function sql_fetchfield($field, $rownum = false, $query_id = false)
259 if (!$query_id)
261 $query_id = $this->query_result;
264 if ($query_id)
266 if ($rownum !== false)
268 $this->sql_rowseek($rownum, $query_id);
271 $row = $this->sql_fetchrow($query_id);
272 return isset($row[$field]) ? $row[$field] : false;
275 return false;
279 * Seek to given row number
280 * rownum is zero-based
282 function sql_rowseek($rownum, $query_id = false)
284 if (!$query_id)
286 $query_id = $this->query_result;
289 return ($query_id) ? @mssql_data_seek($query_id, $rownum) : false;
293 * Get last inserted id after insert statement
295 function sql_nextid()
297 $result_id = @mssql_query('SELECT @@IDENTITY', $this->db_connect_id);
298 if ($result_id)
300 if ($row = @mssql_fetch_assoc($result_id))
302 @mssql_free_result($result_id);
303 return $row['computed'];
305 @mssql_free_result($result_id);
308 return false;
312 * Free sql result
314 function sql_freeresult($query_id = false)
316 if (!$query_id)
318 $query_id = $this->query_result;
321 if (isset($this->open_queries[$query_id]))
323 unset($this->open_queries[$query_id]);
324 return @mssql_free_result($query_id);
327 return false;
331 * Escape string used in sql query
333 function sql_escape($msg)
335 return str_replace("'", "''", $msg);
339 * return sql error array
340 * @access: private
342 function _sql_error()
344 $error = array(
345 'message' => @mssql_get_last_message($this->db_connect_id),
346 'code' => ''
349 // Get error code number
350 $result_id = @mssql_query('SELECT @@ERROR as code', $this->db_connect_id);
351 if ($result_id)
353 $row = @mssql_fetch_assoc($result_id);
354 $error['code'] = $row['code'];
355 @mssql_free_result($result_id);
358 // Get full error message if possible
359 $sql = 'SELECT CAST(description as varchar(255)) as message
360 FROM master.dbo.sysmessages
361 WHERE error = ' . $error['code'];
362 $result_id = @mssql_query($sql);
364 if ($result_id)
366 $row = @mssql_fetch_assoc($result_id);
367 if (!empty($row['message']))
369 $error['message'] .= '<br />' . $row['message'];
371 @mssql_free_result($result_id);
374 return $error;
378 * Build db-specific query data
379 * @access: private
381 function _sql_custom_build($stage, $data)
383 return $data;
387 * Close sql connection
388 * @access: private
390 function _sql_close()
392 return @mssql_close($this->db_connect_id);
396 * Build db-specific report
397 * @access: private
399 function _sql_report($mode, $query = '')
401 switch ($mode)
403 case 'start':
404 $explain_query = $query;
405 if (preg_match('/UPDATE ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
407 $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
409 else if (preg_match('/DELETE FROM ([a-z0-9_]+).*?WHERE(.*)/s', $query, $m))
411 $explain_query = 'SELECT * FROM ' . $m[1] . ' WHERE ' . $m[2];
414 if (preg_match('/^SELECT/', $explain_query))
416 $html_table = false;
417 @mssql_query("SET SHOWPLAN_TEXT ON;", $this->db_connect_id);
418 if ($result = @mssql_query($explain_query, $this->db_connect_id))
420 @mssql_next_result($result);
421 while ($row = @mssql_fetch_row($result))
423 $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
426 @mssql_query("SET SHOWPLAN_TEXT OFF;", $this->db_connect_id);
427 @mssql_free_result($result);
429 if ($html_table)
431 $this->html_hold .= '</table>';
434 break;
436 case 'fromcache':
437 $endtime = explode(' ', microtime());
438 $endtime = $endtime[0] + $endtime[1];
440 $result = @mssql_query($query, $this->db_connect_id);
441 while ($void = @mssql_fetch_assoc($result))
443 // Take the time spent on parsing rows into account
445 @mssql_free_result($result);
447 $splittime = explode(' ', microtime());
448 $splittime = $splittime[0] + $splittime[1];
450 $this->sql_report('record_fromcache', $query, $endtime, $splittime);
452 break;
458 } // if ... define