PageRenderTime 40ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/database/classes/kohana/database/mysql.php

https://gitlab.com/ken3/bluSky
PHP | 443 lines | 307 code | 60 blank | 76 comment | 26 complexity | 19e70c8c6b4f95e28447491e9d3858cd MD5 | raw file
  1. <?php defined('SYSPATH') OR die('No direct script access.');
  2. /**
  3. * MySQL database connection.
  4. *
  5. * @package Kohana/Database
  6. * @category Drivers
  7. * @author Kohana Team
  8. * @copyright (c) 2008-2009 Kohana Team
  9. * @license http://kohanaphp.com/license
  10. */
  11. class Kohana_Database_MySQL extends Database {
  12. // Database in use by each connection
  13. protected static $_current_databases = array();
  14. // Use SET NAMES to set the character set
  15. protected static $_set_names;
  16. // Identifier for this connection within the PHP driver
  17. protected $_connection_id;
  18. // MySQL uses a backtick for identifiers
  19. protected $_identifier = '`';
  20. public function connect()
  21. {
  22. if ($this->_connection)
  23. return;
  24. if (Database_MySQL::$_set_names === NULL)
  25. {
  26. // Determine if we can use mysql_set_charset(), which is only
  27. // available on PHP 5.2.3+ when compiled against MySQL 5.0+
  28. Database_MySQL::$_set_names = ! function_exists('mysql_set_charset');
  29. }
  30. // Extract the connection parameters, adding required variabels
  31. extract($this->_config['connection'] + array(
  32. 'database' => '',
  33. 'hostname' => '',
  34. 'username' => '',
  35. 'password' => '',
  36. 'persistent' => FALSE,
  37. ));
  38. // Prevent this information from showing up in traces
  39. unset($this->_config['connection']['username'], $this->_config['connection']['password']);
  40. try
  41. {
  42. if ($persistent)
  43. {
  44. // Create a persistent connection
  45. $this->_connection = mysql_pconnect($hostname, $username, $password);
  46. }
  47. else
  48. {
  49. // Create a connection and force it to be a new link
  50. $this->_connection = mysql_connect($hostname, $username, $password, TRUE);
  51. }
  52. }
  53. catch (Exception $e)
  54. {
  55. // No connection exists
  56. $this->_connection = NULL;
  57. throw new Database_Exception(':error',
  58. array(':error' => $e->getMessage()),
  59. $e->getCode());
  60. }
  61. // \xFF is a better delimiter, but the PHP driver uses underscore
  62. $this->_connection_id = sha1($hostname.'_'.$username.'_'.$password);
  63. $this->_select_db($database);
  64. if ( ! empty($this->_config['charset']))
  65. {
  66. // Set the character set
  67. $this->set_charset($this->_config['charset']);
  68. }
  69. if ( ! empty($this->_config['connection']['variables']))
  70. {
  71. // Set session variables
  72. $variables = array();
  73. foreach ($this->_config['connection']['variables'] as $var => $val)
  74. {
  75. $variables[] = 'SESSION '.$var.' = '.$this->quote($val);
  76. }
  77. mysql_query('SET '.implode(', ', $variables), $this->_connection);
  78. }
  79. }
  80. /**
  81. * Select the database
  82. *
  83. * @param string $database Database
  84. * @return void
  85. */
  86. protected function _select_db($database)
  87. {
  88. if ( ! mysql_select_db($database, $this->_connection))
  89. {
  90. // Unable to select database
  91. throw new Database_Exception(':error',
  92. array(':error' => mysql_error($this->_connection)),
  93. mysql_errno($this->_connection));
  94. }
  95. Database_MySQL::$_current_databases[$this->_connection_id] = $database;
  96. }
  97. public function disconnect()
  98. {
  99. try
  100. {
  101. // Database is assumed disconnected
  102. $status = TRUE;
  103. if (is_resource($this->_connection))
  104. {
  105. if ($status = mysql_close($this->_connection))
  106. {
  107. // Clear the connection
  108. $this->_connection = NULL;
  109. // Clear the instance
  110. parent::disconnect();
  111. }
  112. }
  113. }
  114. catch (Exception $e)
  115. {
  116. // Database is probably not disconnected
  117. $status = ! is_resource($this->_connection);
  118. }
  119. return $status;
  120. }
  121. public function set_charset($charset)
  122. {
  123. // Make sure the database is connected
  124. $this->_connection or $this->connect();
  125. if (Database_MySQL::$_set_names === TRUE)
  126. {
  127. // PHP is compiled against MySQL 4.x
  128. $status = (bool) mysql_query('SET NAMES '.$this->quote($charset), $this->_connection);
  129. }
  130. else
  131. {
  132. // PHP is compiled against MySQL 5.x
  133. $status = mysql_set_charset($charset, $this->_connection);
  134. }
  135. if ($status === FALSE)
  136. {
  137. throw new Database_Exception(':error',
  138. array(':error' => mysql_error($this->_connection)),
  139. mysql_errno($this->_connection));
  140. }
  141. }
  142. public function query($type, $sql, $as_object = FALSE, array $params = NULL)
  143. {
  144. // Make sure the database is connected
  145. $this->_connection or $this->connect();
  146. if ( ! empty($this->_config['profiling']))
  147. {
  148. // Benchmark this query for the current instance
  149. $benchmark = Profiler::start("Database ({$this->_instance})", $sql);
  150. }
  151. if ( ! empty($this->_config['connection']['persistent']) AND $this->_config['connection']['database'] !== Database_MySQL::$_current_databases[$this->_connection_id])
  152. {
  153. // Select database on persistent connections
  154. $this->_select_db($this->_config['connection']['database']);
  155. }
  156. // Execute the query
  157. if (($result = mysql_query($sql, $this->_connection)) === FALSE)
  158. {
  159. if (isset($benchmark))
  160. {
  161. // This benchmark is worthless
  162. Profiler::delete($benchmark);
  163. }
  164. throw new Database_Exception(':error [ :query ]',
  165. array(':error' => mysql_error($this->_connection), ':query' => $sql),
  166. mysql_errno($this->_connection));
  167. }
  168. if (isset($benchmark))
  169. {
  170. Profiler::stop($benchmark);
  171. }
  172. // Set the last query
  173. $this->last_query = $sql;
  174. if ($type === Database::SELECT)
  175. {
  176. // Return an iterator of results
  177. return new Database_MySQL_Result($result, $sql, $as_object, $params);
  178. }
  179. elseif ($type === Database::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. /**
  237. * Start a SQL transaction
  238. *
  239. * @link http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
  240. *
  241. * @param string $mode Isolation level
  242. * @return boolean
  243. */
  244. public function begin($mode = NULL)
  245. {
  246. // Make sure the database is connected
  247. $this->_connection or $this->connect();
  248. if ($mode AND ! mysql_query("SET TRANSACTION ISOLATION LEVEL $mode", $this->_connection))
  249. {
  250. throw new Database_Exception(':error',
  251. array(':error' => mysql_error($this->_connection)),
  252. mysql_errno($this->_connection));
  253. }
  254. return (bool) mysql_query('START TRANSACTION', $this->_connection);
  255. }
  256. /**
  257. * Commit a SQL transaction
  258. *
  259. * @return boolean
  260. */
  261. public function commit()
  262. {
  263. // Make sure the database is connected
  264. $this->_connection or $this->connect();
  265. return (bool) mysql_query('COMMIT', $this->_connection);
  266. }
  267. /**
  268. * Rollback a SQL transaction
  269. *
  270. * @return boolean
  271. */
  272. public function rollback()
  273. {
  274. // Make sure the database is connected
  275. $this->_connection or $this->connect();
  276. return (bool) mysql_query('ROLLBACK', $this->_connection);
  277. }
  278. public function list_tables($like = NULL)
  279. {
  280. if (is_string($like))
  281. {
  282. // Search for table names
  283. $result = $this->query(Database::SELECT, 'SHOW TABLES LIKE '.$this->quote($like), FALSE);
  284. }
  285. else
  286. {
  287. // Find all table names
  288. $result = $this->query(Database::SELECT, 'SHOW TABLES', FALSE);
  289. }
  290. $tables = array();
  291. foreach ($result as $row)
  292. {
  293. $tables[] = reset($row);
  294. }
  295. return $tables;
  296. }
  297. public function list_columns($table, $like = NULL, $add_prefix = TRUE)
  298. {
  299. // Quote the table name
  300. $table = ($add_prefix === TRUE) ? $this->quote_table($table) : $table;
  301. if (is_string($like))
  302. {
  303. // Search for column names
  304. $result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table.' LIKE '.$this->quote($like), FALSE);
  305. }
  306. else
  307. {
  308. // Find all column names
  309. $result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table, FALSE);
  310. }
  311. $count = 0;
  312. $columns = array();
  313. foreach ($result as $row)
  314. {
  315. list($type, $length) = $this->_parse_type($row['Type']);
  316. $column = $this->datatype($type);
  317. $column['column_name'] = $row['Field'];
  318. $column['column_default'] = $row['Default'];
  319. $column['data_type'] = $type;
  320. $column['is_nullable'] = ($row['Null'] == 'YES');
  321. $column['ordinal_position'] = ++$count;
  322. switch ($column['type'])
  323. {
  324. case 'float':
  325. if (isset($length))
  326. {
  327. list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length);
  328. }
  329. break;
  330. case 'int':
  331. if (isset($length))
  332. {
  333. // MySQL attribute
  334. $column['display'] = $length;
  335. }
  336. break;
  337. case 'string':
  338. switch ($column['data_type'])
  339. {
  340. case 'binary':
  341. case 'varbinary':
  342. $column['character_maximum_length'] = $length;
  343. break;
  344. case 'char':
  345. case 'varchar':
  346. $column['character_maximum_length'] = $length;
  347. case 'text':
  348. case 'tinytext':
  349. case 'mediumtext':
  350. case 'longtext':
  351. $column['collation_name'] = $row['Collation'];
  352. break;
  353. case 'enum':
  354. case 'set':
  355. $column['collation_name'] = $row['Collation'];
  356. $column['options'] = explode('\',\'', substr($length, 1, -1));
  357. break;
  358. }
  359. break;
  360. }
  361. // MySQL attributes
  362. $column['comment'] = $row['Comment'];
  363. $column['extra'] = $row['Extra'];
  364. $column['key'] = $row['Key'];
  365. $column['privileges'] = $row['Privileges'];
  366. $columns[$row['Field']] = $column;
  367. }
  368. return $columns;
  369. }
  370. public function escape($value)
  371. {
  372. // Make sure the database is connected
  373. $this->_connection or $this->connect();
  374. if (($value = mysql_real_escape_string( (string) $value, $this->_connection)) === FALSE)
  375. {
  376. throw new Database_Exception(':error',
  377. array(':error' => mysql_error($this->_connection)),
  378. mysql_errno($this->_connection));
  379. }
  380. // SQL standard is to use single-quotes for all values
  381. return "'$value'";
  382. }
  383. } // End Database_MySQL