You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

374 lines
9.6 KiB

7 years ago
  1. <?php
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP
  6. *
  7. * This content is released under the MIT License (MIT)
  8. *
  9. * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in
  19. * all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27. * THE SOFTWARE.
  28. *
  29. * @package CodeIgniter
  30. * @author EllisLab Dev Team
  31. * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
  32. * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
  33. * @license http://opensource.org/licenses/MIT MIT License
  34. * @link https://codeigniter.com
  35. * @since Version 3.0.0
  36. * @filesource
  37. */
  38. defined('BASEPATH') OR exit('No direct script access allowed');
  39. /**
  40. * PDO MySQL Database Adapter Class
  41. *
  42. * Note: _DB is an extender class that the app controller
  43. * creates dynamically based on whether the query builder
  44. * class is being used or not.
  45. *
  46. * @package CodeIgniter
  47. * @subpackage Drivers
  48. * @category Database
  49. * @author EllisLab Dev Team
  50. * @link https://codeigniter.com/user_guide/database/
  51. */
  52. class CI_DB_pdo_mysql_driver extends CI_DB_pdo_driver {
  53. /**
  54. * Sub-driver
  55. *
  56. * @var string
  57. */
  58. public $subdriver = 'mysql';
  59. /**
  60. * Compression flag
  61. *
  62. * @var bool
  63. */
  64. public $compress = FALSE;
  65. /**
  66. * Strict ON flag
  67. *
  68. * Whether we're running in strict SQL mode.
  69. *
  70. * @var bool
  71. */
  72. public $stricton;
  73. // --------------------------------------------------------------------
  74. /**
  75. * Identifier escape character
  76. *
  77. * @var string
  78. */
  79. protected $_escape_char = '`';
  80. // --------------------------------------------------------------------
  81. /**
  82. * Class constructor
  83. *
  84. * Builds the DSN if not already set.
  85. *
  86. * @param array $params
  87. * @return void
  88. */
  89. public function __construct($params)
  90. {
  91. parent::__construct($params);
  92. if (empty($this->dsn))
  93. {
  94. $this->dsn = 'mysql:host='.(empty($this->hostname) ? '127.0.0.1' : $this->hostname);
  95. empty($this->port) OR $this->dsn .= ';port='.$this->port;
  96. empty($this->database) OR $this->dsn .= ';dbname='.$this->database;
  97. empty($this->char_set) OR $this->dsn .= ';charset='.$this->char_set;
  98. }
  99. elseif ( ! empty($this->char_set) && strpos($this->dsn, 'charset=', 6) === FALSE)
  100. {
  101. $this->dsn .= ';charset='.$this->char_set;
  102. }
  103. }
  104. // --------------------------------------------------------------------
  105. /**
  106. * Database connection
  107. *
  108. * @param bool $persistent
  109. * @return object
  110. */
  111. public function db_connect($persistent = FALSE)
  112. {
  113. if (isset($this->stricton))
  114. {
  115. if ($this->stricton)
  116. {
  117. $sql = 'CONCAT(@@sql_mode, ",", "STRICT_ALL_TABLES")';
  118. }
  119. else
  120. {
  121. $sql = 'REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
  122. @@sql_mode,
  123. "STRICT_ALL_TABLES,", ""),
  124. ",STRICT_ALL_TABLES", ""),
  125. "STRICT_ALL_TABLES", ""),
  126. "STRICT_TRANS_TABLES,", ""),
  127. ",STRICT_TRANS_TABLES", ""),
  128. "STRICT_TRANS_TABLES", "")';
  129. }
  130. if ( ! empty($sql))
  131. {
  132. if (empty($this->options[PDO::MYSQL_ATTR_INIT_COMMAND]))
  133. {
  134. $this->options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET SESSION sql_mode = '.$sql;
  135. }
  136. else
  137. {
  138. $this->options[PDO::MYSQL_ATTR_INIT_COMMAND] .= ', @@session.sql_mode = '.$sql;
  139. }
  140. }
  141. }
  142. if ($this->compress === TRUE)
  143. {
  144. $this->options[PDO::MYSQL_ATTR_COMPRESS] = TRUE;
  145. }
  146. if (is_array($this->encrypt))
  147. {
  148. $ssl = array();
  149. empty($this->encrypt['ssl_key']) OR $ssl[PDO::MYSQL_ATTR_SSL_KEY] = $this->encrypt['ssl_key'];
  150. empty($this->encrypt['ssl_cert']) OR $ssl[PDO::MYSQL_ATTR_SSL_CERT] = $this->encrypt['ssl_cert'];
  151. empty($this->encrypt['ssl_ca']) OR $ssl[PDO::MYSQL_ATTR_SSL_CA] = $this->encrypt['ssl_ca'];
  152. empty($this->encrypt['ssl_capath']) OR $ssl[PDO::MYSQL_ATTR_SSL_CAPATH] = $this->encrypt['ssl_capath'];
  153. empty($this->encrypt['ssl_cipher']) OR $ssl[PDO::MYSQL_ATTR_SSL_CIPHER] = $this->encrypt['ssl_cipher'];
  154. // DO NOT use array_merge() here!
  155. // It re-indexes numeric keys and the PDO_MYSQL_ATTR_SSL_* constants are integers.
  156. empty($ssl) OR $this->options += $ssl;
  157. }
  158. // Prior to version 5.7.3, MySQL silently downgrades to an unencrypted connection if SSL setup fails
  159. if (
  160. ($pdo = parent::db_connect($persistent)) !== FALSE
  161. && ! empty($ssl)
  162. && version_compare($pdo->getAttribute(PDO::ATTR_CLIENT_VERSION), '5.7.3', '<=')
  163. && empty($pdo->query("SHOW STATUS LIKE 'ssl_cipher'")->fetchObject()->Value)
  164. )
  165. {
  166. $message = 'PDO_MYSQL was configured for an SSL connection, but got an unencrypted connection instead!';
  167. log_message('error', $message);
  168. return ($this->db_debug) ? $this->display_error($message, '', TRUE) : FALSE;
  169. }
  170. return $pdo;
  171. }
  172. // --------------------------------------------------------------------
  173. /**
  174. * Select the database
  175. *
  176. * @param string $database
  177. * @return bool
  178. */
  179. public function db_select($database = '')
  180. {
  181. if ($database === '')
  182. {
  183. $database = $this->database;
  184. }
  185. if (FALSE !== $this->simple_query('USE '.$this->escape_identifiers($database)))
  186. {
  187. $this->database = $database;
  188. $this->data_cache = array();
  189. return TRUE;
  190. }
  191. return FALSE;
  192. }
  193. // --------------------------------------------------------------------
  194. /**
  195. * Begin Transaction
  196. *
  197. * @return bool
  198. */
  199. protected function _trans_begin()
  200. {
  201. $this->conn_id->setAttribute(PDO::ATTR_AUTOCOMMIT, FALSE);
  202. return $this->conn_id->beginTransaction();
  203. }
  204. // --------------------------------------------------------------------
  205. /**
  206. * Commit Transaction
  207. *
  208. * @return bool
  209. */
  210. protected function _trans_commit()
  211. {
  212. if ($this->conn_id->commit())
  213. {
  214. $this->conn_id->setAttribute(PDO::ATTR_AUTOCOMMIT, TRUE);
  215. return TRUE;
  216. }
  217. return FALSE;
  218. }
  219. // --------------------------------------------------------------------
  220. /**
  221. * Rollback Transaction
  222. *
  223. * @return bool
  224. */
  225. protected function _trans_rollback()
  226. {
  227. if ($this->conn_id->rollBack())
  228. {
  229. $this->conn_id->setAttribute(PDO::ATTR_AUTOCOMMIT, TRUE);
  230. return TRUE;
  231. }
  232. return FALSE;
  233. }
  234. // --------------------------------------------------------------------
  235. /**
  236. * Show table query
  237. *
  238. * Generates a platform-specific query string so that the table names can be fetched
  239. *
  240. * @param bool $prefix_limit
  241. * @return string
  242. */
  243. protected function _list_tables($prefix_limit = FALSE)
  244. {
  245. $sql = 'SHOW TABLES';
  246. if ($prefix_limit === TRUE && $this->dbprefix !== '')
  247. {
  248. return $sql." LIKE '".$this->escape_like_str($this->dbprefix)."%'";
  249. }
  250. return $sql;
  251. }
  252. // --------------------------------------------------------------------
  253. /**
  254. * Show column query
  255. *
  256. * Generates a platform-specific query string so that the column names can be fetched
  257. *
  258. * @param string $table
  259. * @return string
  260. */
  261. protected function _list_columns($table = '')
  262. {
  263. return 'SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE);
  264. }
  265. // --------------------------------------------------------------------
  266. /**
  267. * Returns an object with field data
  268. *
  269. * @param string $table
  270. * @return array
  271. */
  272. public function field_data($table)
  273. {
  274. if (($query = $this->query('SHOW COLUMNS FROM '.$this->protect_identifiers($table, TRUE, NULL, FALSE))) === FALSE)
  275. {
  276. return FALSE;
  277. }
  278. $query = $query->result_object();
  279. $retval = array();
  280. for ($i = 0, $c = count($query); $i < $c; $i++)
  281. {
  282. $retval[$i] = new stdClass();
  283. $retval[$i]->name = $query[$i]->Field;
  284. sscanf($query[$i]->Type, '%[a-z](%d)',
  285. $retval[$i]->type,
  286. $retval[$i]->max_length
  287. );
  288. $retval[$i]->default = $query[$i]->Default;
  289. $retval[$i]->primary_key = (int) ($query[$i]->Key === 'PRI');
  290. }
  291. return $retval;
  292. }
  293. // --------------------------------------------------------------------
  294. /**
  295. * Truncate statement
  296. *
  297. * Generates a platform-specific truncate string from the supplied data
  298. *
  299. * If the database does not support the TRUNCATE statement,
  300. * then this method maps to 'DELETE FROM table'
  301. *
  302. * @param string $table
  303. * @return string
  304. */
  305. protected function _truncate($table)
  306. {
  307. return 'TRUNCATE '.$table;
  308. }
  309. // --------------------------------------------------------------------
  310. /**
  311. * FROM tables
  312. *
  313. * Groups tables in FROM clauses if needed, so there is no confusion
  314. * about operator precedence.
  315. *
  316. * @return string
  317. */
  318. protected function _from_tables()
  319. {
  320. if ( ! empty($this->qb_join) && count($this->qb_from) > 1)
  321. {
  322. return '('.implode(', ', $this->qb_from).')';
  323. }
  324. return implode(', ', $this->qb_from);
  325. }
  326. }