6 * @copyright (c) 2005 phpBB Group
7 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
13 if (!defined('IN_PHPBB'))
21 if (!defined('SQL_LAYER'))
24 define('SQL_LAYER', 'mssql');
25 include($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
28 * MSSQL Database Abstraction Layer
29 * Minimum Requirement is MSSQL 2000+
32 class dbal_mssql
extends dbal
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
);
55 return ($this->db_connect_id
) ?
$this->db_connect_id
: $this->sql_error('');
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
);
68 $row = @mssql_fetch_assoc
($result_id);
69 @mssql_free_result
($result_id);
74 return 'MSSQL<br />' . implode(' ', $row);
84 function _sql_transaction($status = 'begin')
89 return @mssql_query
('BEGIN TRANSACTION', $this->db_connect_id
);
93 return @mssql_query
('commit', $this->db_connect_id
);
97 return @mssql_query
('ROLLBACK', $this->db_connect_id
);
107 function sql_query($query = '', $cache_ttl = 0)
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);
154 return ($this->query_result
) ?
$this->query_result
: false;
160 function sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
164 $this->query_result
= false;
166 // if $total is set to 0 we do not want to limit the number of rows
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);
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
189 $this->sql_rowseek($result, $row_offset);
201 * Return number of rows
202 * Not used within core code
204 function sql_numrows($query_id = false)
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;
225 function sql_fetchrow($query_id = false)
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
244 foreach ($row as $key => $value)
246 $row[$key] = ($value === ' ') ?
'' : $value;
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)
261 $query_id = $this->query_result
;
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;
279 * Seek to given row number
280 * rownum is zero-based
282 function sql_rowseek($rownum, $query_id = false)
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
);
300 if ($row = @mssql_fetch_assoc
($result_id))
302 @mssql_free_result
($result_id);
303 return $row['computed'];
305 @mssql_free_result
($result_id);
314 function sql_freeresult($query_id = false)
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);
331 * Escape string used in sql query
333 function sql_escape($msg)
335 return str_replace("'", "''", $msg);
339 * return sql error array
342 function _sql_error()
345 'message' => @mssql_get_last_message
($this->db_connect_id
),
349 // Get error code number
350 $result_id = @mssql_query
('SELECT @@ERROR as code', $this->db_connect_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);
366 $row = @mssql_fetch_assoc
($result_id);
367 if (!empty($row['message']))
369 $error['message'] .= '<br />' . $row['message'];
371 @mssql_free_result
($result_id);
378 * Build db-specific query data
381 function _sql_custom_build($stage, $data)
387 * Close sql connection
390 function _sql_close()
392 return @mssql_close
($this->db_connect_id
);
396 * Build db-specific report
399 function _sql_report($mode, $query = '')
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))
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);
431 $this->html_hold
.= '</table>';
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);