PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/phpBB/includes/db/mssql.php

http://github.com/phpbb/phpbb3
PHP | 440 lines | 292 code | 65 blank | 83 comment | 51 complexity | 318b11d92a86245732386fc41abb7a0d MD5 | raw file
Possible License(s): AGPL-1.0
  1. <?php
  2. /**
  3. *
  4. * @package dbal
  5. * @copyright (c) 2005 phpBB Group
  6. * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2
  7. *
  8. */
  9. /**
  10. * @ignore
  11. */
  12. if (!defined('IN_PHPBB'))
  13. {
  14. exit;
  15. }
  16. include_once($phpbb_root_path . 'includes/db/dbal.' . $phpEx);
  17. /**
  18. * MSSQL Database Abstraction Layer
  19. * Minimum Requirement is MSSQL 2000+
  20. * @package dbal
  21. */
  22. class dbal_mssql extends dbal
  23. {
  24. /**
  25. * Connect to server
  26. */
  27. function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false)
  28. {
  29. $this->persistency = $persistency;
  30. $this->user = $sqluser;
  31. $this->dbname = $database;
  32. $port_delimiter = (defined('PHP_OS') && substr(PHP_OS, 0, 3) === 'WIN') ? ',' : ':';
  33. $this->server = $sqlserver . (($port) ? $port_delimiter . $port : '');
  34. @ini_set('mssql.charset', 'UTF-8');
  35. @ini_set('mssql.textlimit', 2147483647);
  36. @ini_set('mssql.textsize', 2147483647);
  37. $this->db_connect_id = ($this->persistency) ? @mssql_pconnect($this->server, $this->user, $sqlpassword, $new_link) : @mssql_connect($this->server, $this->user, $sqlpassword, $new_link);
  38. if ($this->db_connect_id && $this->dbname != '')
  39. {
  40. if (!@mssql_select_db($this->dbname, $this->db_connect_id))
  41. {
  42. @mssql_close($this->db_connect_id);
  43. return false;
  44. }
  45. }
  46. return ($this->db_connect_id) ? $this->db_connect_id : $this->sql_error('');
  47. }
  48. /**
  49. * Version information about used database
  50. * @param bool $raw if true, only return the fetched sql_server_version
  51. * @param bool $use_cache If true, it is safe to retrieve the value from the cache
  52. * @return string sql server version
  53. */
  54. function sql_server_info($raw = false, $use_cache = true)
  55. {
  56. global $cache;
  57. if (!$use_cache || empty($cache) || ($this->sql_server_version = $cache->get('mssql_version')) === false)
  58. {
  59. $result_id = @mssql_query("SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY('productlevel'), SERVERPROPERTY('edition')", $this->db_connect_id);
  60. $row = false;
  61. if ($result_id)
  62. {
  63. $row = @mssql_fetch_assoc($result_id);
  64. @mssql_free_result($result_id);
  65. }
  66. $this->sql_server_version = ($row) ? trim(implode(' ', $row)) : 0;
  67. if (!empty($cache) && $use_cache)
  68. {
  69. $cache->put('mssql_version', $this->sql_server_version);
  70. }
  71. }
  72. if ($raw)
  73. {
  74. return $this->sql_server_version;
  75. }
  76. return ($this->sql_server_version) ? 'MSSQL<br />' . $this->sql_server_version : 'MSSQL';
  77. }
  78. /**
  79. * SQL Transaction
  80. * @access private
  81. */
  82. function _sql_transaction($status = 'begin')
  83. {
  84. switch ($status)
  85. {
  86. case 'begin':
  87. return @mssql_query('BEGIN TRANSACTION', $this->db_connect_id);
  88. break;
  89. case 'commit':
  90. return @mssql_query('COMMIT TRANSACTION', $this->db_connect_id);
  91. break;
  92. case 'rollback':
  93. return @mssql_query('ROLLBACK TRANSACTION', $this->db_connect_id);
  94. break;
  95. }
  96. return true;
  97. }
  98. /**
  99. * Base query method
  100. *
  101. * @param string $query Contains the SQL query which shall be executed
  102. * @param int $cache_ttl Either 0 to avoid caching or the time in seconds which the result shall be kept in cache
  103. * @return mixed When casted to bool the returned value returns true on success and false on failure
  104. *
  105. * @access public
  106. */
  107. function sql_query($query = '', $cache_ttl = 0)
  108. {
  109. if ($query != '')
  110. {
  111. global $cache;
  112. // EXPLAIN only in extra debug mode
  113. if (defined('DEBUG_EXTRA'))
  114. {
  115. $this->sql_report('start', $query);
  116. }
  117. $this->query_result = ($cache_ttl && method_exists($cache, 'sql_load')) ? $cache->sql_load($query) : false;
  118. $this->sql_add_num_queries($this->query_result);
  119. if ($this->query_result === false)
  120. {
  121. if (($this->query_result = @mssql_query($query, $this->db_connect_id)) === false)
  122. {
  123. $this->sql_error($query);
  124. }
  125. if (defined('DEBUG_EXTRA'))
  126. {
  127. $this->sql_report('stop', $query);
  128. }
  129. if ($cache_ttl && method_exists($cache, 'sql_save'))
  130. {
  131. $this->open_queries[(int) $this->query_result] = $this->query_result;
  132. $cache->sql_save($query, $this->query_result, $cache_ttl);
  133. }
  134. else if (strpos($query, 'SELECT') === 0 && $this->query_result)
  135. {
  136. $this->open_queries[(int) $this->query_result] = $this->query_result;
  137. }
  138. }
  139. else if (defined('DEBUG_EXTRA'))
  140. {
  141. $this->sql_report('fromcache', $query);
  142. }
  143. }
  144. else
  145. {
  146. return false;
  147. }
  148. return $this->query_result;
  149. }
  150. /**
  151. * Build LIMIT query
  152. */
  153. function _sql_query_limit($query, $total, $offset = 0, $cache_ttl = 0)
  154. {
  155. $this->query_result = false;
  156. // Since TOP is only returning a set number of rows we won't need it if total is set to 0 (return all rows)
  157. if ($total)
  158. {
  159. // We need to grab the total number of rows + the offset number of rows to get the correct result
  160. if (strpos($query, 'SELECT DISTINCT') === 0)
  161. {
  162. $query = 'SELECT DISTINCT TOP ' . ($total + $offset) . ' ' . substr($query, 15);
  163. }
  164. else
  165. {
  166. $query = 'SELECT TOP ' . ($total + $offset) . ' ' . substr($query, 6);
  167. }
  168. }
  169. $result = $this->sql_query($query, $cache_ttl);
  170. // Seek by $offset rows
  171. if ($offset)
  172. {
  173. $this->sql_rowseek($offset, $result);
  174. }
  175. return $result;
  176. }
  177. /**
  178. * Return number of affected rows
  179. */
  180. function sql_affectedrows()
  181. {
  182. return ($this->db_connect_id) ? @mssql_rows_affected($this->db_connect_id) : false;
  183. }
  184. /**
  185. * Fetch current row
  186. */
  187. function sql_fetchrow($query_id = false)
  188. {
  189. global $cache;
  190. if ($query_id === false)
  191. {
  192. $query_id = $this->query_result;
  193. }
  194. if (isset($cache->sql_rowset[$query_id]))
  195. {
  196. return $cache->sql_fetchrow($query_id);
  197. }
  198. if ($query_id === false)
  199. {
  200. return false;
  201. }
  202. $row = @mssql_fetch_assoc($query_id);
  203. // I hope i am able to remove this later... hopefully only a PHP or MSSQL bug
  204. if ($row)
  205. {
  206. foreach ($row as $key => $value)
  207. {
  208. $row[$key] = ($value === ' ' || $value === NULL) ? '' : $value;
  209. }
  210. }
  211. return $row;
  212. }
  213. /**
  214. * Seek to given row number
  215. * rownum is zero-based
  216. */
  217. function sql_rowseek($rownum, &$query_id)
  218. {
  219. global $cache;
  220. if ($query_id === false)
  221. {
  222. $query_id = $this->query_result;
  223. }
  224. if (isset($cache->sql_rowset[$query_id]))
  225. {
  226. return $cache->sql_rowseek($rownum, $query_id);
  227. }
  228. return ($query_id !== false) ? @mssql_data_seek($query_id, $rownum) : false;
  229. }
  230. /**
  231. * Get last inserted id after insert statement
  232. */
  233. function sql_nextid()
  234. {
  235. $result_id = @mssql_query('SELECT SCOPE_IDENTITY()', $this->db_connect_id);
  236. if ($result_id)
  237. {
  238. if ($row = @mssql_fetch_assoc($result_id))
  239. {
  240. @mssql_free_result($result_id);
  241. return $row['computed'];
  242. }
  243. @mssql_free_result($result_id);
  244. }
  245. return false;
  246. }
  247. /**
  248. * Free sql result
  249. */
  250. function sql_freeresult($query_id = false)
  251. {
  252. global $cache;
  253. if ($query_id === false)
  254. {
  255. $query_id = $this->query_result;
  256. }
  257. if (isset($cache->sql_rowset[$query_id]))
  258. {
  259. return $cache->sql_freeresult($query_id);
  260. }
  261. if (isset($this->open_queries[$query_id]))
  262. {
  263. unset($this->open_queries[$query_id]);
  264. return @mssql_free_result($query_id);
  265. }
  266. return false;
  267. }
  268. /**
  269. * Escape string used in sql query
  270. */
  271. function sql_escape($msg)
  272. {
  273. return str_replace(array("'", "\0"), array("''", ''), $msg);
  274. }
  275. /**
  276. * Build LIKE expression
  277. * @access private
  278. */
  279. function _sql_like_expression($expression)
  280. {
  281. return $expression . " ESCAPE '\\'";
  282. }
  283. /**
  284. * return sql error array
  285. * @access private
  286. */
  287. function _sql_error()
  288. {
  289. $error = array(
  290. 'message' => @mssql_get_last_message(),
  291. 'code' => ''
  292. );
  293. // Get error code number
  294. $result_id = @mssql_query('SELECT @@ERROR as code', $this->db_connect_id);
  295. if ($result_id)
  296. {
  297. $row = @mssql_fetch_assoc($result_id);
  298. $error['code'] = $row['code'];
  299. @mssql_free_result($result_id);
  300. }
  301. // Get full error message if possible
  302. $sql = 'SELECT CAST(description as varchar(255)) as message
  303. FROM master.dbo.sysmessages
  304. WHERE error = ' . $error['code'];
  305. $result_id = @mssql_query($sql);
  306. if ($result_id)
  307. {
  308. $row = @mssql_fetch_assoc($result_id);
  309. if (!empty($row['message']))
  310. {
  311. $error['message'] .= '<br />' . $row['message'];
  312. }
  313. @mssql_free_result($result_id);
  314. }
  315. return $error;
  316. }
  317. /**
  318. * Build db-specific query data
  319. * @access private
  320. */
  321. function _sql_custom_build($stage, $data)
  322. {
  323. return $data;
  324. }
  325. /**
  326. * Close sql connection
  327. * @access private
  328. */
  329. function _sql_close()
  330. {
  331. return @mssql_close($this->db_connect_id);
  332. }
  333. /**
  334. * Build db-specific report
  335. * @access private
  336. */
  337. function _sql_report($mode, $query = '')
  338. {
  339. switch ($mode)
  340. {
  341. case 'start':
  342. $html_table = false;
  343. @mssql_query('SET SHOWPLAN_TEXT ON;', $this->db_connect_id);
  344. if ($result = @mssql_query($query, $this->db_connect_id))
  345. {
  346. @mssql_next_result($result);
  347. while ($row = @mssql_fetch_row($result))
  348. {
  349. $html_table = $this->sql_report('add_select_row', $query, $html_table, $row);
  350. }
  351. }
  352. @mssql_query('SET SHOWPLAN_TEXT OFF;', $this->db_connect_id);
  353. @mssql_free_result($result);
  354. if ($html_table)
  355. {
  356. $this->html_hold .= '</table>';
  357. }
  358. break;
  359. case 'fromcache':
  360. $endtime = explode(' ', microtime());
  361. $endtime = $endtime[0] + $endtime[1];
  362. $result = @mssql_query($query, $this->db_connect_id);
  363. while ($void = @mssql_fetch_assoc($result))
  364. {
  365. // Take the time spent on parsing rows into account
  366. }
  367. @mssql_free_result($result);
  368. $splittime = explode(' ', microtime());
  369. $splittime = $splittime[0] + $splittime[1];
  370. $this->sql_report('record_fromcache', $query, $endtime, $splittime);
  371. break;
  372. }
  373. }
  374. }