PageRenderTime 58ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/fuel/core/classes/database/mysql/connection.php

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