pdo_driver.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  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 2.1.2
  14. * @filesource
  15. */
  16. // ------------------------------------------------------------------------
  17. /**
  18. * PDO 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_pdo_driver extends CI_DB {
  31. var $dbdriver = 'pdo';
  32. // the character used to excape - not necessary for PDO
  33. var $_escape_char = '';
  34. var $_like_escape_str;
  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;
  43. var $options = array();
  44. function __construct($params)
  45. {
  46. parent::__construct($params);
  47. // clause and character used for LIKE escape sequences
  48. if (strpos($this->hostname, 'mysql') !== FALSE)
  49. {
  50. $this->_like_escape_str = '';
  51. $this->_like_escape_chr = '';
  52. //Prior to this version, the charset can't be set in the dsn
  53. if(is_php('5.3.6'))
  54. {
  55. $this->hostname .= ";charset={$this->char_set}";
  56. }
  57. //Set the charset with the connection options
  58. $this->options['PDO::MYSQL_ATTR_INIT_COMMAND'] = "SET NAMES {$this->char_set}";
  59. }
  60. elseif (strpos($this->hostname, 'odbc') !== FALSE)
  61. {
  62. $this->_like_escape_str = " {escape '%s'} ";
  63. $this->_like_escape_chr = '!';
  64. }
  65. else
  66. {
  67. $this->_like_escape_str = " ESCAPE '%s' ";
  68. $this->_like_escape_chr = '!';
  69. }
  70. empty($this->database) OR $this->hostname .= ';dbname='.$this->database;
  71. $this->trans_enabled = FALSE;
  72. $this->_random_keyword = ' RND('.time().')'; // database specific random keyword
  73. }
  74. /**
  75. * Non-persistent database connection
  76. *
  77. * @access private called by the base class
  78. * @return resource
  79. */
  80. function db_connect()
  81. {
  82. $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT;
  83. return new PDO($this->hostname, $this->username, $this->password, $this->options);
  84. }
  85. // --------------------------------------------------------------------
  86. /**
  87. * Persistent database connection
  88. *
  89. * @access private called by the base class
  90. * @return resource
  91. */
  92. function db_pconnect()
  93. {
  94. $this->options['PDO::ATTR_ERRMODE'] = PDO::ERRMODE_SILENT;
  95. $this->options['PDO::ATTR_PERSISTENT'] = TRUE;
  96. return new PDO($this->hostname, $this->username, $this->password, $this->options);
  97. }
  98. // --------------------------------------------------------------------
  99. /**
  100. * Reconnect
  101. *
  102. * Keep / reestablish the db connection if no queries have been
  103. * sent for a length of time exceeding the server's idle timeout
  104. *
  105. * @access public
  106. * @return void
  107. */
  108. function reconnect()
  109. {
  110. if ($this->db->db_debug)
  111. {
  112. return $this->db->display_error('db_unsuported_feature');
  113. }
  114. return FALSE;
  115. }
  116. // --------------------------------------------------------------------
  117. /**
  118. * Select the database
  119. *
  120. * @access private called by the base class
  121. * @return resource
  122. */
  123. function db_select()
  124. {
  125. // Not needed for PDO
  126. return TRUE;
  127. }
  128. // --------------------------------------------------------------------
  129. /**
  130. * Set client character set
  131. *
  132. * @access public
  133. * @param string
  134. * @param string
  135. * @return resource
  136. */
  137. function db_set_charset($charset, $collation)
  138. {
  139. // @todo - add support if needed
  140. return TRUE;
  141. }
  142. // --------------------------------------------------------------------
  143. /**
  144. * Version number query string
  145. *
  146. * @access public
  147. * @return string
  148. */
  149. function _version()
  150. {
  151. return $this->conn_id->getAttribute(PDO::ATTR_CLIENT_VERSION);
  152. }
  153. // --------------------------------------------------------------------
  154. /**
  155. * Execute the query
  156. *
  157. * @access private called by the base class
  158. * @param string an SQL query
  159. * @return object
  160. */
  161. function _execute($sql)
  162. {
  163. $sql = $this->_prep_query($sql);
  164. $result_id = $this->conn_id->prepare($sql);
  165. if (is_object($result_id) && $result_id->execute())
  166. {
  167. if (is_numeric(stripos($sql, 'SELECT')))
  168. {
  169. $this->affect_rows = count($result_id->fetchAll());
  170. }
  171. else
  172. {
  173. $this->affect_rows = $result_id->rowCount();
  174. }
  175. }
  176. else
  177. {
  178. $this->affect_rows = 0;
  179. return FALSE;
  180. }
  181. return $result_id;
  182. }
  183. // --------------------------------------------------------------------
  184. /**
  185. * Prep the query
  186. *
  187. * If needed, each database adapter can prep the query string
  188. *
  189. * @access private called by execute()
  190. * @param string an SQL query
  191. * @return string
  192. */
  193. function _prep_query($sql)
  194. {
  195. return $sql;
  196. }
  197. // --------------------------------------------------------------------
  198. /**
  199. * Begin Transaction
  200. *
  201. * @access public
  202. * @return bool
  203. */
  204. function trans_begin($test_mode = FALSE)
  205. {
  206. if ( ! $this->trans_enabled)
  207. {
  208. return TRUE;
  209. }
  210. // When transactions are nested we only begin/commit/rollback the outermost ones
  211. if ($this->_trans_depth > 0)
  212. {
  213. return TRUE;
  214. }
  215. // Reset the transaction failure flag.
  216. // If the $test_mode flag is set to TRUE transactions will be rolled back
  217. // even if the queries produce a successful result.
  218. $this->_trans_failure = (bool) ($test_mode === TRUE);
  219. return $this->conn_id->beginTransaction();
  220. }
  221. // --------------------------------------------------------------------
  222. /**
  223. * Commit Transaction
  224. *
  225. * @access public
  226. * @return bool
  227. */
  228. function trans_commit()
  229. {
  230. if ( ! $this->trans_enabled)
  231. {
  232. return TRUE;
  233. }
  234. // When transactions are nested we only begin/commit/rollback the outermost ones
  235. if ($this->_trans_depth > 0)
  236. {
  237. return TRUE;
  238. }
  239. $ret = $this->conn->commit();
  240. return $ret;
  241. }
  242. // --------------------------------------------------------------------
  243. /**
  244. * Rollback Transaction
  245. *
  246. * @access public
  247. * @return bool
  248. */
  249. function trans_rollback()
  250. {
  251. if ( ! $this->trans_enabled)
  252. {
  253. return TRUE;
  254. }
  255. // When transactions are nested we only begin/commit/rollback the outermost ones
  256. if ($this->_trans_depth > 0)
  257. {
  258. return TRUE;
  259. }
  260. $ret = $this->conn_id->rollBack();
  261. return $ret;
  262. }
  263. // --------------------------------------------------------------------
  264. /**
  265. * Escape String
  266. *
  267. * @access public
  268. * @param string
  269. * @param bool whether or not the string will be used in a LIKE condition
  270. * @return string
  271. */
  272. function escape_str($str, $like = FALSE)
  273. {
  274. if (is_array($str))
  275. {
  276. foreach ($str as $key => $val)
  277. {
  278. $str[$key] = $this->escape_str($val, $like);
  279. }
  280. return $str;
  281. }
  282. //Escape the string
  283. $str = $this->conn_id->quote($str);
  284. //If there are duplicated quotes, trim them away
  285. if (strpos($str, "'") === 0)
  286. {
  287. $str = substr($str, 1, -1);
  288. }
  289. // escape LIKE condition wildcards
  290. if ($like === TRUE)
  291. {
  292. $str = str_replace( array('%', '_', $this->_like_escape_chr),
  293. array($this->_like_escape_chr.'%', $this->_like_escape_chr.'_', $this->_like_escape_chr.$this->_like_escape_chr),
  294. $str);
  295. }
  296. return $str;
  297. }
  298. // --------------------------------------------------------------------
  299. /**
  300. * Affected Rows
  301. *
  302. * @access public
  303. * @return integer
  304. */
  305. function affected_rows()
  306. {
  307. return $this->affect_rows;
  308. }
  309. // --------------------------------------------------------------------
  310. /**
  311. * Insert ID
  312. *
  313. * @access public
  314. * @return integer
  315. */
  316. function insert_id($name=NULL)
  317. {
  318. //Convenience method for postgres insertid
  319. if (strpos($this->hostname, 'pgsql') !== FALSE)
  320. {
  321. $v = $this->_version();
  322. $table = func_num_args() > 0 ? func_get_arg(0) : NULL;
  323. if ($table == NULL && $v >= '8.1')
  324. {
  325. $sql='SELECT LASTVAL() as ins_id';
  326. }
  327. $query = $this->query($sql);
  328. $row = $query->row();
  329. return $row->ins_id;
  330. }
  331. else
  332. {
  333. return $this->conn_id->lastInsertId($name);
  334. }
  335. }
  336. // --------------------------------------------------------------------
  337. /**
  338. * "Count All" query
  339. *
  340. * Generates a platform-specific query string that counts all records in
  341. * the specified database
  342. *
  343. * @access public
  344. * @param string
  345. * @return string
  346. */
  347. function count_all($table = '')
  348. {
  349. if ($table == '')
  350. {
  351. return 0;
  352. }
  353. $query = $this->query($this->_count_string . $this->_protect_identifiers('numrows') . " FROM " . $this->_protect_identifiers($table, TRUE, NULL, FALSE));
  354. if ($query->num_rows() == 0)
  355. {
  356. return 0;
  357. }
  358. $row = $query->row();
  359. $this->_reset_select();
  360. return (int) $row->numrows;
  361. }
  362. // --------------------------------------------------------------------
  363. /**
  364. * Show table query
  365. *
  366. * Generates a platform-specific query string so that the table names can be fetched
  367. *
  368. * @access private
  369. * @param boolean
  370. * @return string
  371. */
  372. function _list_tables($prefix_limit = FALSE)
  373. {
  374. $sql = "SHOW TABLES FROM `".$this->database."`";
  375. if ($prefix_limit !== FALSE AND $this->dbprefix != '')
  376. {
  377. //$sql .= " LIKE '".$this->escape_like_str($this->dbprefix)."%' ".sprintf($this->_like_escape_str, $this->_like_escape_chr);
  378. return FALSE; // not currently supported
  379. }
  380. return $sql;
  381. }
  382. // --------------------------------------------------------------------
  383. /**
  384. * Show column query
  385. *
  386. * Generates a platform-specific query string so that the column names can be fetched
  387. *
  388. * @access public
  389. * @param string the table name
  390. * @return string
  391. */
  392. function _list_columns($table = '')
  393. {
  394. return "SHOW COLUMNS FROM ".$table;
  395. }
  396. // --------------------------------------------------------------------
  397. /**
  398. * Field data query
  399. *
  400. * Generates a platform-specific query so that the column data can be retrieved
  401. *
  402. * @access public
  403. * @param string the table name
  404. * @return object
  405. */
  406. function _field_data($table)
  407. {
  408. return "SELECT TOP 1 FROM ".$table;
  409. }
  410. // --------------------------------------------------------------------
  411. /**
  412. * The error message string
  413. *
  414. * @access private
  415. * @return string
  416. */
  417. function _error_message()
  418. {
  419. $error_array = $this->conn_id->errorInfo();
  420. return $error_array[2];
  421. }
  422. // --------------------------------------------------------------------
  423. /**
  424. * The error message number
  425. *
  426. * @access private
  427. * @return integer
  428. */
  429. function _error_number()
  430. {
  431. return $this->conn_id->errorCode();
  432. }
  433. // --------------------------------------------------------------------
  434. /**
  435. * Escape the SQL Identifiers
  436. *
  437. * This function escapes column and table names
  438. *
  439. * @access private
  440. * @param string
  441. * @return string
  442. */
  443. function _escape_identifiers($item)
  444. {
  445. if ($this->_escape_char == '')
  446. {
  447. return $item;
  448. }
  449. foreach ($this->_reserved_identifiers as $id)
  450. {
  451. if (strpos($item, '.'.$id) !== FALSE)
  452. {
  453. $str = $this->_escape_char. str_replace('.', $this->_escape_char.'.', $item);
  454. // remove duplicates if the user already included the escape
  455. return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
  456. }
  457. }
  458. if (strpos($item, '.') !== FALSE)
  459. {
  460. $str = $this->_escape_char.str_replace('.', $this->_escape_char.'.'.$this->_escape_char, $item).$this->_escape_char;
  461. }
  462. else
  463. {
  464. $str = $this->_escape_char.$item.$this->_escape_char;
  465. }
  466. // remove duplicates if the user already included the escape
  467. return preg_replace('/['.$this->_escape_char.']+/', $this->_escape_char, $str);
  468. }
  469. // --------------------------------------------------------------------
  470. /**
  471. * From Tables
  472. *
  473. * This function implicitly groups FROM tables so there is no confusion
  474. * about operator precedence in harmony with SQL standards
  475. *
  476. * @access public
  477. * @param type
  478. * @return type
  479. */
  480. function _from_tables($tables)
  481. {
  482. if ( ! is_array($tables))
  483. {
  484. $tables = array($tables);
  485. }
  486. return (count($tables) == 1) ? $tables[0] : '('.implode(', ', $tables).')';
  487. }
  488. // --------------------------------------------------------------------
  489. /**
  490. * Insert statement
  491. *
  492. * Generates a platform-specific insert string from the supplied data
  493. *
  494. * @access public
  495. * @param string the table name
  496. * @param array the insert keys
  497. * @param array the insert values
  498. * @return string
  499. */
  500. function _insert($table, $keys, $values)
  501. {
  502. return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES (".implode(', ', $values).")";
  503. }
  504. // --------------------------------------------------------------------
  505. /**
  506. * Insert_batch statement
  507. *
  508. * Generates a platform-specific insert string from the supplied data
  509. *
  510. * @access public
  511. * @param string the table name
  512. * @param array the insert keys
  513. * @param array the insert values
  514. * @return string
  515. */
  516. function _insert_batch($table, $keys, $values)
  517. {
  518. return "INSERT INTO ".$table." (".implode(', ', $keys).") VALUES ".implode(', ', $values);
  519. }
  520. // --------------------------------------------------------------------
  521. /**
  522. * Update statement
  523. *
  524. * Generates a platform-specific update string from the supplied data
  525. *
  526. * @access public
  527. * @param string the table name
  528. * @param array the update data
  529. * @param array the where clause
  530. * @param array the orderby clause
  531. * @param array the limit clause
  532. * @return string
  533. */
  534. function _update($table, $values, $where, $orderby = array(), $limit = FALSE)
  535. {
  536. foreach ($values as $key => $val)
  537. {
  538. $valstr[] = $key." = ".$val;
  539. }
  540. $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
  541. $orderby = (count($orderby) >= 1)?' ORDER BY '.implode(", ", $orderby):'';
  542. $sql = "UPDATE ".$table." SET ".implode(', ', $valstr);
  543. $sql .= ($where != '' AND count($where) >=1) ? " WHERE ".implode(" ", $where) : '';
  544. $sql .= $orderby.$limit;
  545. return $sql;
  546. }
  547. // --------------------------------------------------------------------
  548. /**
  549. * Update_Batch statement
  550. *
  551. * Generates a platform-specific batch update string from the supplied data
  552. *
  553. * @access public
  554. * @param string the table name
  555. * @param array the update data
  556. * @param array the where clause
  557. * @return string
  558. */
  559. function _update_batch($table, $values, $index, $where = NULL)
  560. {
  561. $ids = array();
  562. $where = ($where != '' AND count($where) >=1) ? implode(" ", $where).' AND ' : '';
  563. foreach ($values as $key => $val)
  564. {
  565. $ids[] = $val[$index];
  566. foreach (array_keys($val) as $field)
  567. {
  568. if ($field != $index)
  569. {
  570. $final[$field][] = 'WHEN '.$index.' = '.$val[$index].' THEN '.$val[$field];
  571. }
  572. }
  573. }
  574. $sql = "UPDATE ".$table." SET ";
  575. $cases = '';
  576. foreach ($final as $k => $v)
  577. {
  578. $cases .= $k.' = CASE '."\n";
  579. foreach ($v as $row)
  580. {
  581. $cases .= $row."\n";
  582. }
  583. $cases .= 'ELSE '.$k.' END, ';
  584. }
  585. $sql .= substr($cases, 0, -2);
  586. $sql .= ' WHERE '.$where.$index.' IN ('.implode(',', $ids).')';
  587. return $sql;
  588. }
  589. // --------------------------------------------------------------------
  590. /**
  591. * Truncate statement
  592. *
  593. * Generates a platform-specific truncate string from the supplied data
  594. * If the database does not support the truncate() command
  595. * This function maps to "DELETE FROM table"
  596. *
  597. * @access public
  598. * @param string the table name
  599. * @return string
  600. */
  601. function _truncate($table)
  602. {
  603. return $this->_delete($table);
  604. }
  605. // --------------------------------------------------------------------
  606. /**
  607. * Delete statement
  608. *
  609. * Generates a platform-specific delete string from the supplied data
  610. *
  611. * @access public
  612. * @param string the table name
  613. * @param array the where clause
  614. * @param string the limit clause
  615. * @return string
  616. */
  617. function _delete($table, $where = array(), $like = array(), $limit = FALSE)
  618. {
  619. $conditions = '';
  620. if (count($where) > 0 OR count($like) > 0)
  621. {
  622. $conditions = "\nWHERE ";
  623. $conditions .= implode("\n", $this->ar_where);
  624. if (count($where) > 0 && count($like) > 0)
  625. {
  626. $conditions .= " AND ";
  627. }
  628. $conditions .= implode("\n", $like);
  629. }
  630. $limit = ( ! $limit) ? '' : ' LIMIT '.$limit;
  631. return "DELETE FROM ".$table.$conditions.$limit;
  632. }
  633. // --------------------------------------------------------------------
  634. /**
  635. * Limit string
  636. *
  637. * Generates a platform-specific LIMIT clause
  638. *
  639. * @access public
  640. * @param string the sql query string
  641. * @param integer the number of rows to limit the query to
  642. * @param integer the offset value
  643. * @return string
  644. */
  645. function _limit($sql, $limit, $offset)
  646. {
  647. if (strpos($this->hostname, 'cubrid') !== FALSE || strpos($this->hostname, 'sqlite') !== FALSE)
  648. {
  649. if ($offset == 0)
  650. {
  651. $offset = '';
  652. }
  653. else
  654. {
  655. $offset .= ", ";
  656. }
  657. return $sql."LIMIT ".$offset.$limit;
  658. }
  659. else
  660. {
  661. $sql .= "LIMIT ".$limit;
  662. if ($offset > 0)
  663. {
  664. $sql .= " OFFSET ".$offset;
  665. }
  666. return $sql;
  667. }
  668. }
  669. // --------------------------------------------------------------------
  670. /**
  671. * Close DB Connection
  672. *
  673. * @access public
  674. * @param resource
  675. * @return void
  676. */
  677. function _close($conn_id)
  678. {
  679. $this->conn_id = null;
  680. }
  681. }
  682. /* End of file pdo_driver.php */
  683. /* Location: ./system/database/drivers/pdo/pdo_driver.php */