mssql_driver.php 15 KB

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