PageRenderTime 103ms CodeModel.GetById 38ms RepoModel.GetById 7ms app.codeStats 0ms

/classes/database/mysql/connection.php

https://github.com/smokin/core
PHP | 424 lines | 303 code | 63 blank | 58 comment | 28 complexity | bcbddf446b5168685043d0c1ba7c5bf0 MD5 | raw file
  1. <?php
  2. /**
  3. * MySQL database connection.
  4. *
  5. * @package Fuel/Database
  6. * @category Drivers
  7. * @author Kohana Team
  8. * @copyright (c) 2008-2009 Kohana Team
  9. * @license http://kohanaphp.com/license
  10. */
  11. namespace Fuel\Core;
  12. class Database_MySQL_Connection extends \Database_Connection {
  13. // Database in use by each connection
  14. protected static $_current_databases = array();
  15. // Use SET NAMES to set the character set
  16. protected static $_set_names;
  17. // Identifier for this connection within the PHP driver
  18. protected $_connection_id;
  19. // MySQL uses a backtick for identifiers
  20. protected $_identifier = '`';
  21. // Allows transactions
  22. protected $_trans_enabled = FALSE;
  23. // transaction errors
  24. public $trans_errors = FALSE;
  25. // Know which kind of DB is used
  26. public $_db_type = 'mysql';
  27. public function connect()
  28. {
  29. if ($this->_connection)
  30. return;
  31. if (static::$_set_names === NULL)
  32. {
  33. // Determine if we can use mysql_set_charset(), which is only
  34. // available on PHP 5.2.3+ when compiled against MySQL 5.0+
  35. static::$_set_names = ! function_exists('mysql_set_charset');
  36. }
  37. // Extract the connection parameters, adding required variabels
  38. extract($this->_config['connection'] + array(
  39. 'database' => '',
  40. 'hostname' => '',
  41. 'username' => '',
  42. 'password' => '',
  43. 'persistent' => FALSE,
  44. ));
  45. // Prevent this information from showing up in traces
  46. unset($this->_config['connection']['username'], $this->_config['connection']['password']);
  47. try
  48. {
  49. if ($persistent)
  50. {
  51. // Create a persistent connection
  52. $this->_connection = mysql_pconnect($hostname, $username, $password);
  53. }
  54. else
  55. {
  56. // Create a connection and force it to be a new link
  57. $this->_connection = mysql_connect($hostname, $username, $password, TRUE);
  58. }
  59. }
  60. catch (ErrorException $e)
  61. {
  62. // No connection exists
  63. $this->_connection = NULL;
  64. throw new \Database_Exception(mysql_error(), mysql_errno());
  65. }
  66. // \xFF is a better delimiter, but the PHP driver uses underscore
  67. $this->_connection_id = sha1($hostname.'_'.$username.'_'.$password);
  68. $this->_select_db($database);
  69. if ( ! empty($this->_config['charset']))
  70. {
  71. // Set the character set
  72. $this->set_charset($this->_config['charset']);
  73. }
  74. }
  75. /**
  76. * Select the database
  77. *
  78. * @param string Database
  79. * @return void
  80. */
  81. protected function _select_db($database)
  82. {
  83. if ( ! mysql_select_db($database, $this->_connection))
  84. {
  85. // Unable to select database
  86. throw new \Database_Exception(mysql_error($this->_connection), mysql_errno($this->_connection));
  87. }
  88. static::$_current_databases[$this->_connection_id] = $database;
  89. }
  90. public function disconnect()
  91. {
  92. try
  93. {
  94. // Database is assumed disconnected
  95. $status = TRUE;
  96. if (is_resource($this->_connection))
  97. {
  98. if ($status = mysql_close($this->_connection))
  99. {
  100. // Clear the connection
  101. $this->_connection = NULL;
  102. }
  103. }
  104. }
  105. catch (Exception $e)
  106. {
  107. // Database is probably not disconnected
  108. $status = ! is_resource($this->_connection);
  109. }
  110. return $status;
  111. }
  112. public function set_charset($charset)
  113. {
  114. // Make sure the database is connected
  115. $this->_connection or $this->connect();
  116. if (static::$_set_names === TRUE)
  117. {
  118. // PHP is compiled against MySQL 4.x
  119. $status = (bool) mysql_query('SET NAMES '.$this->quote($charset), $this->_connection);
  120. }
  121. else
  122. {
  123. // PHP is compiled against MySQL 5.x
  124. $status = mysql_set_charset($charset, $this->_connection);
  125. }
  126. if ($status === FALSE)
  127. {
  128. throw new \Database_Exception(mysql_error($this->_connection), mysql_errno($this->_connection));
  129. }
  130. }
  131. public function query($type, $sql, $as_object)
  132. {
  133. // Make sure the database is connected
  134. $this->_connection or $this->connect();
  135. if ( ! empty($this->_config['profiling']))
  136. {
  137. // Benchmark this query for the current instance
  138. $benchmark = Profiler::start("Database ({$this->_instance})", $sql);
  139. }
  140. if ( ! empty($this->_config['connection']['persistent']) AND $this->_config['connection']['database'] !== static::$_current_databases[$this->_connection_id])
  141. {
  142. // Select database on persistent connections
  143. $this->_select_db($this->_config['connection']['database']);
  144. }
  145. // Execute the query
  146. if (($result = mysql_query($sql, $this->_connection)) === FALSE)
  147. {
  148. if (isset($benchmark))
  149. {
  150. // This benchmark is worthless
  151. Profiler::delete($benchmark);
  152. }
  153. if ($type !== \DB::SELECT && $this->_trans_enabled)
  154. {
  155. // If we are using transactions, throwing an exception would defeat the purpose
  156. // We need to log the failures for transaction status
  157. if ( ! is_array($this->trans_errors))
  158. {
  159. $this->trans_errors = array();
  160. }
  161. $this->trans_errors[] = mysql_errno($this->_connection).': '.mysql_error($this->_connection).' [ '.$sql.' ]';
  162. }
  163. else
  164. {
  165. throw new \Database_Exception(mysql_error($this->_connection).' [ '.$sql.' ]', mysql_errno($this->_connection));
  166. }
  167. }
  168. if (isset($benchmark))
  169. {
  170. Profiler::stop($benchmark);
  171. }
  172. // Set the last query
  173. $this->last_query = $sql;
  174. if ($type === \DB::SELECT)
  175. {
  176. // Return an iterator of results
  177. return new \Database_MySQL_Result($result, $sql, $as_object);
  178. }
  179. elseif ($type === \DB::INSERT)
  180. {
  181. // Return a list of insert id and rows created
  182. return array(
  183. mysql_insert_id($this->_connection),
  184. mysql_affected_rows($this->_connection),
  185. );
  186. }
  187. else
  188. {
  189. // Return the number of rows affected
  190. return mysql_affected_rows($this->_connection);
  191. }
  192. }
  193. public function datatype($type)
  194. {
  195. static $types = array
  196. (
  197. 'blob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '65535'),
  198. 'bool' => array('type' => 'bool'),
  199. 'bigint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '18446744073709551615'),
  200. 'datetime' => array('type' => 'string'),
  201. 'decimal unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
  202. 'double' => array('type' => 'float'),
  203. 'double precision unsigned' => array('type' => 'float', 'min' => '0'),
  204. 'double unsigned' => array('type' => 'float', 'min' => '0'),
  205. 'enum' => array('type' => 'string'),
  206. 'fixed' => array('type' => 'float', 'exact' => TRUE),
  207. 'fixed unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
  208. 'float unsigned' => array('type' => 'float', 'min' => '0'),
  209. 'int unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'),
  210. 'integer unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'),
  211. 'longblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '4294967295'),
  212. 'longtext' => array('type' => 'string', 'character_maximum_length' => '4294967295'),
  213. 'mediumblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '16777215'),
  214. 'mediumint' => array('type' => 'int', 'min' => '-8388608', 'max' => '8388607'),
  215. 'mediumint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '16777215'),
  216. 'mediumtext' => array('type' => 'string', 'character_maximum_length' => '16777215'),
  217. 'national varchar' => array('type' => 'string'),
  218. 'numeric unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
  219. 'nvarchar' => array('type' => 'string'),
  220. 'point' => array('type' => 'string', 'binary' => TRUE),
  221. 'real unsigned' => array('type' => 'float', 'min' => '0'),
  222. 'set' => array('type' => 'string'),
  223. 'smallint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '65535'),
  224. 'text' => array('type' => 'string', 'character_maximum_length' => '65535'),
  225. 'tinyblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '255'),
  226. 'tinyint' => array('type' => 'int', 'min' => '-128', 'max' => '127'),
  227. 'tinyint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '255'),
  228. 'tinytext' => array('type' => 'string', 'character_maximum_length' => '255'),
  229. 'year' => array('type' => 'string'),
  230. );
  231. $type = str_replace(' zerofill', '', $type);
  232. if (isset($types[$type]))
  233. return $types[$type];
  234. return parent::datatype($type);
  235. }
  236. public function list_tables($like = NULL)
  237. {
  238. if (is_string($like))
  239. {
  240. // Search for table names
  241. $result = $this->query(\DB::SELECT, 'SHOW TABLES LIKE '.$this->quote($like), FALSE);
  242. }
  243. else
  244. {
  245. // Find all table names
  246. $result = $this->query(\DB::SELECT, 'SHOW TABLES', FALSE);
  247. }
  248. $tables = array();
  249. foreach ($result as $row)
  250. {
  251. $tables[] = reset($row);
  252. }
  253. return $tables;
  254. }
  255. public function list_columns($table, $like = NULL)
  256. {
  257. // Quote the table name
  258. $table = $this->quote_table($table);
  259. if (is_string($like))
  260. {
  261. // Search for column names
  262. $result = $this->query(\DB::SELECT, 'SHOW FULL COLUMNS FROM '.$table.' LIKE '.$this->quote($like), FALSE);
  263. }
  264. else
  265. {
  266. // Find all column names
  267. $result = $this->query(\DB::SELECT, 'SHOW FULL COLUMNS FROM '.$table, FALSE);
  268. }
  269. $count = 0;
  270. $columns = array();
  271. foreach ($result as $row)
  272. {
  273. list($type, $length) = $this->_parse_type($row['Type']);
  274. $column = $this->datatype($type);
  275. $column['name'] = $row['Field'];
  276. $column['default'] = $row['Default'];
  277. $column['data_type'] = $type;
  278. $column['null'] = ($row['Null'] == 'YES');
  279. $column['ordinal_position'] = ++$count;
  280. switch ($column['type'])
  281. {
  282. case 'float':
  283. if (isset($length))
  284. {
  285. list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length);
  286. }
  287. break;
  288. case 'int':
  289. if (isset($length))
  290. {
  291. // MySQL attribute
  292. $column['display'] = $length;
  293. }
  294. break;
  295. case 'string':
  296. switch ($column['data_type'])
  297. {
  298. case 'binary':
  299. case 'varbinary':
  300. $column['character_maximum_length'] = $length;
  301. break;
  302. case 'char':
  303. case 'varchar':
  304. $column['character_maximum_length'] = $length;
  305. case 'text':
  306. case 'tinytext':
  307. case 'mediumtext':
  308. case 'longtext':
  309. $column['collation_name'] = $row['Collation'];
  310. break;
  311. case 'enum':
  312. case 'set':
  313. $column['collation_name'] = $row['Collation'];
  314. $column['options'] = explode('\',\'', substr($length, 1, -1));
  315. break;
  316. }
  317. break;
  318. }
  319. // MySQL attributes
  320. $column['comment'] = $row['Comment'];
  321. $column['extra'] = $row['Extra'];
  322. $column['key'] = $row['Key'];
  323. $column['privileges'] = $row['Privileges'];
  324. $columns[$row['Field']] = $column;
  325. }
  326. return $columns;
  327. }
  328. public function escape($value)
  329. {
  330. // Make sure the database is connected
  331. $this->_connection or $this->connect();
  332. if (($value = mysql_real_escape_string((string) $value, $this->_connection)) === FALSE)
  333. {
  334. throw new \Database_Exception(mysql_error($this->_connection), mysql_errno($this->_connection));
  335. }
  336. // SQL standard is to use single-quotes for all values
  337. return "'$value'";
  338. }
  339. public function transactional($use_trans = TRUE)
  340. {
  341. if (is_bool($use_trans)) {
  342. $this->_trans_enabled = $use_trans;
  343. }
  344. }
  345. public function start_transaction()
  346. {
  347. $this->transactional();
  348. $this->query(0, 'SET AUTOCOMMIT=0', false);
  349. $this->query(0, 'START TRANSACTION', false);
  350. }
  351. public function commit_transaction()
  352. {
  353. $this->query(0, 'COMMIT', false);
  354. $this->query(0, 'SET AUTOCOMMIT=1', false);
  355. }
  356. public function rollback_transaction()
  357. {
  358. $this->query(0, 'ROLLBACK', false);
  359. $this->query(0, 'SET AUTOCOMMIT=1', false);
  360. }
  361. } // End Database_MySQL