mssql_driver.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP 5.1.6 or newer
  6. *
  7. * @package CodeIgniter
  8. * @author ExpressionEngine Dev Team
  9. * @copyright Copyright (c) 2008 - 2011, EllisLab, Inc.
  10. * @license http://codeigniter.com/user_guide/license.html
  11. * @link http://codeigniter.com
  12. * @since Version 1.0
  13. * @filesource
  14. */
  15. // ------------------------------------------------------------------------
  16. /**
  17. * MS SQL Database Adapter Class
  18. *
  19. * Note: _DB is an extender class that the app controller
  20. * creates dynamically based on whether the active record
  21. * class is being used or not.
  22. *
  23. * @package CodeIgniter
  24. * @subpackage Drivers
  25. * @category Database
  26. * @author ExpressionEngine Dev Team
  27. * @link http://codeigniter.com/user_guide/database/
  28. */
  29. class CI_DB_mssql_driver extends CI_DB {
  30. var $dbdriver = 'mssql';
  31. // The character used for escaping
  32. var $_escape_char = '';
  33. // clause and character used for LIKE escape sequences
  34. var $_like_escape_str = " ESCAPE '%s' ";
  35. var $_like_escape_chr = '!';
  36. /**
  37. * The syntax to count rows is slightly different across different
  38. * database engines, so this string appears in each driver and is
  39. * used for the count_all() and count_all_results() functions.
  40. */
  41. var $_count_string = "SELECT COUNT(*) AS ";
  42. var $_random_keyword = ' ASC'; // not currently supported
  43. /**
  44. * Non-persistent database connection
  45. *
  46. * @access private called by the base class
  47. * @return resource
  48. */
  49. function db_connect()
  50. {
  51. if ($this->port != '')
  52. {
  53. $this->hostname .= ','.$this->port;
  54. }
  55. return @mssql_connect($this->hostname, $this->username, $this->password);
  56. }
  57. // --------------------------------------------------------------------
  58. /**
  59. * Persistent database connection
  60. *
  61. * @access private called by the base class
  62. * @return resource
  63. */
  64. function db_pconnect()
  65. {
  66. if ($this->port != '')
  67. {
  68. $this->hostname .= ','.$this->port;
  69. }
  70. return @mssql_pconnect($this->hostname, $this->username, $this->password);
  71. }
  72. // --------------------------------------------------------------------
  73. /**
  74. * Reconnect
  75. *
  76. * Keep / reestablish the db connection if no queries have been
  77. * sent for a length of time exceeding the server's idle timeout
  78. *
  79. * @access public
  80. * @return void
  81. */
  82. function reconnect()
  83. {
  84. // not implemented in MSSQL
  85. }
  86. // --------------------------------------------------------------------
  87. /**
  88. * Select the database
  89. *
  90. * @access private called by the base class
  91. * @return resource
  92. */
  93. function db_select()
  94. {
  95. // Note: The brackets are required in the event that the DB name
  96. // contains reserved characters
  97. return @mssql_select_db('['.$this->database.']', $this->conn_id);
  98. }
  99. // --------------------------------------------------------------------
  100. /**
  101. * Set client character set
  102. *
  103. * @access public
  104. * @param string
  105. * @param string
  106. * @return resource
  107. */
  108. function db_set_charset($charset, $collation)
  109. {
  110. // @todo - add support if needed
  111. return TRUE;
  112. }
  113. // --------------------------------------------------------------------
  114. /**
  115. * Execute the query
  116. *
  117. * @access private called by the base class
  118. * @param string an SQL query
  119. * @return resource
  120. */
  121. function _execute($sql)
  122. {
  123. $sql = $this->_prep_query($sql);
  124. return @mssql_query($sql, $this->conn_id);
  125. }
  126. // --------------------------------------------------------------------
  127. /**
  128. * Prep the query
  129. *
  130. * If needed, each database adapter can prep the query string
  131. *
  132. * @access private called by execute()
  133. * @param string an SQL query
  134. * @return string
  135. */
  136. function _prep_query($sql)
  137. {
  138. return $sql;
  139. }
  140. // --------------------------------------------------------------------
  141. /**
  142. * Begin Transaction
  143. *
  144. * @access public
  145. * @return bool
  146. */
  147. function trans_begin($test_mode = FALSE)
  148. {
  149. if ( ! $this->trans_enabled)
  150. {
  151. return TRUE;
  152. }
  153. // When transactions are nested we only begin/commit/rollback the outermost ones
  154. if ($this->_trans_depth > 0)
  155. {
  156. return TRUE;
  157. }
  158. // Reset the transaction failure flag.
  159. // If the $test_mode flag is set to TRUE transactions will be rolled back
  160. // even if the queries produce a successful result.
  161. $this->_trans_failure = ($test_mode === TRUE) ? TRUE : FALSE;
  162. $this->simple_query('BEGIN TRAN');
  163. return TRUE;
  164. }
  165. // --------------------------------------------------------------------
  166. /**
  167. * Commit Transaction
  168. *
  169. * @access public
  170. * @return bool
  171. */
  172. function trans_commit()
  173. {
  174. if ( ! $this->trans_enabled)
  175. {
  176. return TRUE;
  177. }
  178. // When transactions are nested we only begin/commit/rollback the outermost ones
  179. if ($this->_trans_depth > 0)
  180. {
  181. return TRUE;
  182. }
  183. $this->simple_query('COMMIT TRAN');
  184. return TRUE;
  185. }
  186. // --------------------------------------------------------------------
  187. /**
  188. * Rollback Transaction
  189. *
  190. * @access public
  191. * @return bool
  192. */
  193. function trans_rollback()
  194. {
  195. if ( ! $this->trans_enabled)
  196. {
  197. return TRUE;
  198. }
  199. // When transactions are nested we only begin/commit/rollback the outermost ones
  200. if ($this->_trans_depth > 0)
  201. {
  202. return TRUE;
  203. }
  204. $this->simple_query('ROLLBACK TRAN');
  205. return TRUE;
  206. }
  207. // --------------------------------------------------------------------
  208. /**
  209. * Escape String
  210. *
  211. * @access public
  212. * @param string
  213. * @param bool whether or not the string will be used in a LIKE condition
  214. * @return string
  215. */
  216. function escape_str($str, $like = FALSE)
  217. {
  218. if (is_array($str))
  219. {
  220. foreach ($str as $key => $val)
  221. {
  222. $str[$key] = $this->escape_str($val, $like);
  223. }
  224. return $str;
  225. }
  226. // Escape single quotes
  227. $str = str_replace("'", "''", remove_invisible_characters($str));
  228. // escape LIKE condition wildcards
  229. if ($like === TRUE)
  230. {
  231. $str = str_replace(
  232. array($this->_like_escape_chr, '%', '_'),
  233. array($this->_like_escape_chr.$this->_like_escape_chr, $this->_like_escape_chr.'%', $this->_like_escape_chr.'_'),
  234. $str
  235. );
  236. }
  237. return $str;
  238. }
  239. // --------------------------------------------------------------------
  240. /**
  241. * Affected Rows
  242. *
  243. * @access public
  244. * @return integer
  245. */
  246. function affected_rows()
  247. {
  248. return @mssql_rows_affected($this->conn_id);
  249. }
  250. // --------------------------------------------------------------------
  251. /**
  252. * Insert ID
  253. *
  254. * Returns the last id created in the Identity column.
  255. *
  256. * @access public
  257. * @return integer
  258. */
  259. function insert_id()
  260. {
  261. $ver = self::_parse_major_version($this->version());
  262. $sql = ($ver >= 8 ? "SELECT SCOPE_IDENTITY() AS last_id" : "SELECT @@IDENTITY AS last_id");
  263. $query = $this->query($sql);
  264. $row = $query->row();
  265. return $row->last_id;
  266. }
  267. // --------------------------------------------------------------------
  268. /**
  269. * Parse major version
  270. *
  271. * Grabs the major version number from the
  272. * database server version string passed in.
  273. *
  274. * @access private
  275. * @param string $version
  276. * @return int16 major version number
  277. */
  278. function _parse_major_version($version)
  279. {
  280. preg_match('/([0-9]+)\.([0-9]+)\.([0-9]+)/', $version, $ver_info);
  281. return $ver_info[1]; // return the major version b/c that's all we're interested in.
  282. }
  283. // --------------------------------------------------------------------
  284. /**
  285. * Version number query string
  286. *
  287. * @access public
  288. * @return string
  289. */
  290. function _version()
  291. {
  292. return "SELECT @@VERSION AS ver";
  293. }
  294. // --------------------------------------------------------------------
  295. /**
  296. * "Count All" query
  297. *
  298. * Generates a platform-specific query string that counts all records in
  299. * the specified database
  300. *
  301. * @access public
  302. * @param string
  303. * @return string
  304. */
  305. function count_all($table = '')
  306. {
  307. if ($table == '')
  308. {
  309. return 0;
  310. }
  311. $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
  312. if ($query->num_rows() == 0)
  313. {
  314. return 0;
  315. }
  316. $row = $query->row();
  317. $this->_reset_select();
  318. return (int) $row->numrows;
  319. }
  320. // --------------------------------------------------------------------
  321. /**
  322. * List table query
  323. *
  324. * Generates a platform-specific query string so that the table names can be fetched
  325. *
  326. * @access private
  327. * @param boolean
  328. * @return string
  329. */
  330. function _list_tables($prefix_limit = FALSE)
  331. {
  332. $sql = "SELECT name FROM sysobjects WHERE type = 'U' ORDER BY name";
  333. // for future compatibility
  334. if ($prefix_limit !== FALSE AND $this->dbprefix != '')
  335. {
  336. //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
  337. return FALSE; // not currently supported
  338. }
  339. return $sql;
  340. }
  341. // --------------------------------------------------------------------
  342. /**
  343. * List column query
  344. *
  345. * Generates a platform-specific query string so that the column names can be fetched
  346. *
  347. * @access private
  348. * @param string the table name
  349. * @return string
  350. */
  351. function _list_columns($table = '')
  352. {
  353. return "SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = '".$table."'";
  354. }
  355. // --------------------------------------------------------------------
  356. /**
  357. * Field data query
  358. *
  359. * Generates a platform-specific query so that the column data can be retrieved
  360. *
  361. * @access public
  362. * @param string the table name
  363. * @return object
  364. */
  365. function _field_data($table)
  366. {
  367. return "SELECT TOP 1 * FROM ".$table;
  368. }
  369. // --------------------------------------------------------------------
  370. /**
  371. * The error message string
  372. *
  373. * @access private
  374. * @return string
  375. */
  376. function _error_message()
  377. {
  378. return mssql_get_last_message();
  379. }
  380. // --------------------------------------------------------------------
  381. /**
  382. * The error message number
  383. *
  384. * @access private
  385. * @return integer
  386. */
  387. function _error_number()
  388. {
  389. // Are error numbers supported?
  390. return '';
  391. }
  392. // --------------------------------------------------------------------
  393. /**
  394. * Escape the SQL Identifiers
  395. *
  396. * This function escapes column and table names
  397. *
  398. * @access private
  399. * @param string
  400. * @return string
  401. */
  402. function _escape_identifiers($item)
  403. {
  404. if ($this->_escape_char == '')
  405. {
  406. return $item;
  407. }
  408. foreach ($this->_reserved_identifiers as $id)
  409. {
  410. if (strpos($item, '.'.$id) !== FALSE)
  411. {
  412. $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
  413. // remove duplicates if the user already included the escape
  414. return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
  415. }
  416. }
  417. if (strpos($item, '.') !== FALSE)
  418. {
  419. $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
  420. }
  421. else
  422. {
  423. $str = $this->_escape_char.$item.$this->_escape_char;
  424. }
  425. // remove duplicates if the user already included the escape
  426. return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
  427. }
  428. // --------------------------------------------------------------------
  429. /**
  430. * From Tables
  431. *
  432. * This function implicitly groups FROM tables so there is no confusion
  433. * about operator precedence in harmony with SQL standards
  434. *
  435. * @access public
  436. * @param type
  437. * @return type
  438. */
  439. function _from_tables($tables)
  440. {
  441. if ( ! is_array($tables))
  442. {
  443. $tables = array($tables);
  444. }
  445. return implode(', ', $tables);
  446. }
  447. // --------------------------------------------------------------------
  448. /**
  449. * Insert statement
  450. *
  451. * Generates a platform-specific insert string from the supplied data
  452. *
  453. * @access public
  454. * @param string the table name
  455. * @param array the insert keys
  456. * @param array the insert values
  457. * @return string
  458. */
  459. function _insert($table, $keys, $values)
  460. {
  461. return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
  462. }
  463. // --------------------------------------------------------------------
  464. /**
  465. * Update statement
  466. *
  467. * Generates a platform-specific update string from the supplied data
  468. *
  469. * @access public
  470. * @param string the table name
  471. * @param array the update data
  472. * @param array the where clause
  473. * @param array the orderby clause
  474. * @param array the limit clause
  475. * @return string
  476. */
  477. function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
  478. {
  479. foreach ($values as $key => $val)
  480. {
  481. $valstr[] = $key." = ".$val;
  482. }
  483. $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
  484. $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
  485. $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
  486. $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
  487. $sql .= $orderby.$limit;
  488. return $sql;
  489. }
  490. // --------------------------------------------------------------------
  491. /**
  492. * Truncate statement
  493. *
  494. * Generates a platform-specific truncate string from the supplied data
  495. * If the database does not support the truncate() command
  496. * This function maps to "DELETE FROM table"
  497. *
  498. * @access public
  499. * @param string the table name
  500. * @return string
  501. */
  502. function _truncate($table)
  503. {
  504. return "TRUNCATE ".$table;
  505. }
  506. // --------------------------------------------------------------------
  507. /**
  508. * Delete statement
  509. *
  510. * Generates a platform-specific delete string from the supplied data
  511. *
  512. * @access public
  513. * @param string the table name
  514. * @param array the where clause
  515. * @param string the limit clause
  516. * @return string
  517. */
  518. function _delete($table, $where = array(), $like = array(), $limit = FALSE)
  519. {
  520. $conditions = '';
  521. if (count($where) > 0 OR count($like) > 0)
  522. {
  523. $conditions = "\nWHERE ";
  524. $conditions .= implode("\n", $this->ar_where);
  525. if (count($where) > 0 && count($like) > 0)
  526. {
  527. $conditions .= " AND ";
  528. }
  529. $conditions .= implode("\n", $like);
  530. }
  531. $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
  532. return "DELETE FROM ".$table.$conditions.$limit;
  533. }
  534. // --------------------------------------------------------------------
  535. /**
  536. * Limit string
  537. *
  538. * Generates a platform-specific LIMIT clause
  539. *
  540. * @access public
  541. * @param string the sql query string
  542. * @param integer the number of rows to limit the query to
  543. * @param integer the offset value
  544. * @return string
  545. */
  546. function _limit($sql, $limit, $offset)
  547. {
  548. $i = $limit + $offset;
  549. return preg_replace('/(^\SELECT (DISTINCT)?)/i','\\1 TOP '.$i.' ', $sql);
  550. }
  551. // --------------------------------------------------------------------
  552. /**
  553. * Close DB Connection
  554. *
  555. * @access public
  556. * @param resource
  557. * @return void
  558. */
  559. function _close($conn_id)
  560. {
  561. @mssql_close($conn_id);
  562. }
  563. }
  564. /* End of file mssql_driver.php */
  565. /* Location: ./system/database/drivers/mssql/mssql_driver.php */