PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/yii/framework/db/schema/CDbSchema.php

https://github.com/joshuaswarren/weatherhub
PHP | 559 lines | 261 code | 37 blank | 261 comment | 32 complexity | aacb63150629d008b3027af3534dfc43 MD5 | raw file
  1. <?php
  2. /**
  3. * CDbSchema class file.
  4. *
  5. * @author Qiang Xue <qiang.xue@gmail.com>
  6. * @link http://www.yiiframework.com/
  7. * @copyright Copyright &copy; 2008-2011 Yii Software LLC
  8. * @license http://www.yiiframework.com/license/
  9. */
  10. /**
  11. * CDbSchema is the base class for retrieving metadata information.
  12. *
  13. * @author Qiang Xue <qiang.xue@gmail.com>
  14. * @version $Id: CDbSchema.php 3297 2011-06-22 21:46:11Z qiang.xue $
  15. * @package system.db.schema
  16. * @since 1.0
  17. */
  18. abstract class CDbSchema extends CComponent
  19. {
  20. /**
  21. * @var array the abstract column types mapped to physical column types.
  22. * @since 1.1.6
  23. */
  24. public $columnTypes=array();
  25. private $_tableNames=array();
  26. private $_tables=array();
  27. private $_connection;
  28. private $_builder;
  29. private $_cacheExclude=array();
  30. /**
  31. * Loads the metadata for the specified table.
  32. * @param string $name table name
  33. * @return CDbTableSchema driver dependent table metadata, null if the table does not exist.
  34. */
  35. abstract protected function loadTable($name);
  36. /**
  37. * Constructor.
  38. * @param CDbConnection $conn database connection.
  39. */
  40. public function __construct($conn)
  41. {
  42. $this->_connection=$conn;
  43. foreach($conn->schemaCachingExclude as $name)
  44. $this->_cacheExclude[$name]=true;
  45. }
  46. /**
  47. * @return CDbConnection database connection. The connection is active.
  48. */
  49. public function getDbConnection()
  50. {
  51. return $this->_connection;
  52. }
  53. /**
  54. * Obtains the metadata for the named table.
  55. * @param string $name table name
  56. * @return CDbTableSchema table metadata. Null if the named table does not exist.
  57. */
  58. public function getTable($name)
  59. {
  60. if(isset($this->_tables[$name]))
  61. return $this->_tables[$name];
  62. else
  63. {
  64. if($this->_connection->tablePrefix!==null && strpos($name,'{{')!==false)
  65. $realName=preg_replace('/\{\{(.*?)\}\}/',$this->_connection->tablePrefix.'$1',$name);
  66. else
  67. $realName=$name;
  68. // temporarily disable query caching
  69. if($this->_connection->queryCachingDuration>0)
  70. {
  71. $qcDuration=$this->_connection->queryCachingDuration;
  72. $this->_connection->queryCachingDuration=0;
  73. }
  74. if(!isset($this->_cacheExclude[$name]) && ($duration=$this->_connection->schemaCachingDuration)>0 && $this->_connection->schemaCacheID!==false && ($cache=Yii::app()->getComponent($this->_connection->schemaCacheID))!==null)
  75. {
  76. $key='yii:dbschema'.$this->_connection->connectionString.':'.$this->_connection->username.':'.$name;
  77. if(($table=$cache->get($key))===false)
  78. {
  79. $table=$this->loadTable($realName);
  80. if($table!==null)
  81. $cache->set($key,$table,$duration);
  82. }
  83. $this->_tables[$name]=$table;
  84. }
  85. else
  86. $this->_tables[$name]=$table=$this->loadTable($realName);
  87. if(isset($qcDuration)) // re-enable query caching
  88. $this->_connection->queryCachingDuration=$qcDuration;
  89. return $table;
  90. }
  91. }
  92. /**
  93. * Returns the metadata for all tables in the database.
  94. * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
  95. * @return array the metadata for all tables in the database.
  96. * Each array element is an instance of {@link CDbTableSchema} (or its child class).
  97. * The array keys are table names.
  98. * @since 1.0.2
  99. */
  100. public function getTables($schema='')
  101. {
  102. $tables=array();
  103. foreach($this->getTableNames($schema) as $name)
  104. {
  105. if(($table=$this->getTable($name))!==null)
  106. $tables[$name]=$table;
  107. }
  108. return $tables;
  109. }
  110. /**
  111. * Returns all table names in the database.
  112. * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
  113. * If not empty, the returned table names will be prefixed with the schema name.
  114. * @return array all table names in the database.
  115. * @since 1.0.2
  116. */
  117. public function getTableNames($schema='')
  118. {
  119. if(!isset($this->_tableNames[$schema]))
  120. $this->_tableNames[$schema]=$this->findTableNames($schema);
  121. return $this->_tableNames[$schema];
  122. }
  123. /**
  124. * @return CDbCommandBuilder the SQL command builder for this connection.
  125. */
  126. public function getCommandBuilder()
  127. {
  128. if($this->_builder!==null)
  129. return $this->_builder;
  130. else
  131. return $this->_builder=$this->createCommandBuilder();
  132. }
  133. /**
  134. * Refreshes the schema.
  135. * This method resets the loaded table metadata and command builder
  136. * so that they can be recreated to reflect the change of schema.
  137. */
  138. public function refresh()
  139. {
  140. if(($duration=$this->_connection->schemaCachingDuration)>0 && $this->_connection->schemaCacheID!==false && ($cache=Yii::app()->getComponent($this->_connection->schemaCacheID))!==null)
  141. {
  142. foreach(array_keys($this->_tables) as $name)
  143. {
  144. if(!isset($this->_cacheExclude[$name]))
  145. {
  146. $key='yii:dbschema'.$this->_connection->connectionString.':'.$this->_connection->username.':'.$name;
  147. $cache->delete($key);
  148. }
  149. }
  150. }
  151. $this->_tables=array();
  152. $this->_tableNames=array();
  153. $this->_builder=null;
  154. }
  155. /**
  156. * Quotes a table name for use in a query.
  157. * If the table name contains schema prefix, the prefix will also be properly quoted.
  158. * @param string $name table name
  159. * @return string the properly quoted table name
  160. * @see quoteSimpleTableName
  161. */
  162. public function quoteTableName($name)
  163. {
  164. if(strpos($name,'.')===false)
  165. return $this->quoteSimpleTableName($name);
  166. $parts=explode('.',$name);
  167. foreach($parts as $i=>$part)
  168. $parts[$i]=$this->quoteSimpleTableName($part);
  169. return implode('.',$parts);
  170. }
  171. /**
  172. * Quotes a simple table name for use in a query.
  173. * A simple table name does not schema prefix.
  174. * @param string $name table name
  175. * @return string the properly quoted table name
  176. * @since 1.1.6
  177. */
  178. public function quoteSimpleTableName($name)
  179. {
  180. return "'".$name."'";
  181. }
  182. /**
  183. * Quotes a column name for use in a query.
  184. * If the column name contains prefix, the prefix will also be properly quoted.
  185. * @param string $name column name
  186. * @return string the properly quoted column name
  187. * @see quoteSimpleColumnName
  188. */
  189. public function quoteColumnName($name)
  190. {
  191. if(($pos=strrpos($name,'.'))!==false)
  192. {
  193. $prefix=$this->quoteTableName(substr($name,0,$pos)).'.';
  194. $name=substr($name,$pos+1);
  195. }
  196. else
  197. $prefix='';
  198. return $prefix . ($name==='*' ? $name : $this->quoteSimpleColumnName($name));
  199. }
  200. /**
  201. * Quotes a simple column name for use in a query.
  202. * A simple column name does not contain prefix.
  203. * @param string $name column name
  204. * @return string the properly quoted column name
  205. * @since 1.1.6
  206. */
  207. public function quoteSimpleColumnName($name)
  208. {
  209. return '"'.$name.'"';
  210. }
  211. /**
  212. * Compares two table names.
  213. * The table names can be either quoted or unquoted. This method
  214. * will consider both cases.
  215. * @param string $name1 table name 1
  216. * @param string $name2 table name 2
  217. * @return boolean whether the two table names refer to the same table.
  218. */
  219. public function compareTableNames($name1,$name2)
  220. {
  221. $name1=str_replace(array('"','`',"'"),'',$name1);
  222. $name2=str_replace(array('"','`',"'"),'',$name2);
  223. if(($pos=strrpos($name1,'.'))!==false)
  224. $name1=substr($name1,$pos+1);
  225. if(($pos=strrpos($name2,'.'))!==false)
  226. $name2=substr($name2,$pos+1);
  227. if($this->_connection->tablePrefix!==null)
  228. {
  229. if(strpos($name1,'{')!==false)
  230. $name1=$this->_connection->tablePrefix.str_replace(array('{','}'),'',$name1);
  231. if(strpos($name2,'{')!==false)
  232. $name2=$this->_connection->tablePrefix.str_replace(array('{','}'),'',$name2);
  233. }
  234. return $name1===$name2;
  235. }
  236. /**
  237. * Resets the sequence value of a table's primary key.
  238. * The sequence will be reset such that the primary key of the next new row inserted
  239. * will have the specified value or 1.
  240. * @param CDbTableSchema $table the table schema whose primary key sequence will be reset
  241. * @param mixed $value the value for the primary key of the next new row inserted. If this is not set,
  242. * the next new row's primary key will have a value 1.
  243. * @since 1.1
  244. */
  245. public function resetSequence($table,$value=null)
  246. {
  247. }
  248. /**
  249. * Enables or disables integrity check.
  250. * @param boolean $check whether to turn on or off the integrity check.
  251. * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
  252. * @since 1.1
  253. */
  254. public function checkIntegrity($check=true,$schema='')
  255. {
  256. }
  257. /**
  258. * Creates a command builder for the database.
  259. * This method may be overridden by child classes to create a DBMS-specific command builder.
  260. * @return CDbCommandBuilder command builder instance
  261. */
  262. protected function createCommandBuilder()
  263. {
  264. return new CDbCommandBuilder($this);
  265. }
  266. /**
  267. * Returns all table names in the database.
  268. * This method should be overridden by child classes in order to support this feature
  269. * because the default implementation simply throws an exception.
  270. * @param string $schema the schema of the tables. Defaults to empty string, meaning the current or default schema.
  271. * If not empty, the returned table names will be prefixed with the schema name.
  272. * @return array all table names in the database.
  273. * @since 1.0.2
  274. */
  275. protected function findTableNames($schema='')
  276. {
  277. throw new CDbException(Yii::t('yii','{class} does not support fetching all table names.',
  278. array('{class}'=>get_class($this))));
  279. }
  280. /**
  281. * Converts an abstract column type into a physical column type.
  282. * The conversion is done using the type map specified in {@link columnTypes}.
  283. * These abstract column types are supported (using MySQL as example to explain the corresponding
  284. * physical types):
  285. * <ul>
  286. * <li>pk: an auto-incremental primary key type, will be converted into "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY"</li>
  287. * <li>string: string type, will be converted into "varchar(255)"</li>
  288. * <li>text: a long string type, will be converted into "text"</li>
  289. * <li>integer: integer type, will be converted into "int(11)"</li>
  290. * <li>boolean: boolean type, will be converted into "tinyint(1)"</li>
  291. * <li>float: float number type, will be converted into "float"</li>
  292. * <li>decimal: decimal number type, will be converted into "decimal"</li>
  293. * <li>datetime: datetime type, will be converted into "datetime"</li>
  294. * <li>timestamp: timestamp type, will be converted into "timestamp"</li>
  295. * <li>time: time type, will be converted into "time"</li>
  296. * <li>date: date type, will be converted into "date"</li>
  297. * <li>binary: binary data type, will be converted into "blob"</li>
  298. * </ul>
  299. *
  300. * If the abstract type contains two or more parts separated by spaces (e.g. "string NOT NULL"), then only
  301. * the first part will be converted, and the rest of the parts will be appended to the conversion result.
  302. * For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'.
  303. * @param string $type abstract column type
  304. * @return string physical column type.
  305. * @since 1.1.6
  306. */
  307. public function getColumnType($type)
  308. {
  309. if(isset($this->columnTypes[$type]))
  310. return $this->columnTypes[$type];
  311. else if(($pos=strpos($type,' '))!==false)
  312. {
  313. $t=substr($type,0,$pos);
  314. return (isset($this->columnTypes[$t]) ? $this->columnTypes[$t] : $t).substr($type,$pos);
  315. }
  316. else
  317. return $type;
  318. }
  319. /**
  320. * Builds a SQL statement for creating a new DB table.
  321. *
  322. * The columns in the new table should be specified as name-definition pairs (e.g. 'name'=>'string'),
  323. * where name stands for a column name which will be properly quoted by the method, and definition
  324. * stands for the column type which can contain an abstract DB type.
  325. * The {@link getColumnType} method will be invoked to convert any abstract type into a physical one.
  326. *
  327. * If a column is specified with definition only (e.g. 'PRIMARY KEY (name, type)'), it will be directly
  328. * inserted into the generated SQL.
  329. *
  330. * @param string $table the name of the table to be created. The name will be properly quoted by the method.
  331. * @param array $columns the columns (name=>definition) in the new table.
  332. * @param string $options additional SQL fragment that will be appended to the generated SQL.
  333. * @return string the SQL statement for creating a new DB table.
  334. * @since 1.1.6
  335. */
  336. public function createTable($table, $columns, $options=null)
  337. {
  338. $cols=array();
  339. foreach($columns as $name=>$type)
  340. {
  341. if(is_string($name))
  342. $cols[]="\t".$this->quoteColumnName($name).' '.$this->getColumnType($type);
  343. else
  344. $cols[]="\t".$type;
  345. }
  346. $sql="CREATE TABLE ".$this->quoteTableName($table)." (\n".implode(",\n",$cols)."\n)";
  347. return $options===null ? $sql : $sql.' '.$options;
  348. }
  349. /**
  350. * Builds a SQL statement for renaming a DB table.
  351. * @param string $table the table to be renamed. The name will be properly quoted by the method.
  352. * @param string $newName the new table name. The name will be properly quoted by the method.
  353. * @return string the SQL statement for renaming a DB table.
  354. * @since 1.1.6
  355. */
  356. public function renameTable($table, $newName)
  357. {
  358. return 'RENAME TABLE ' . $this->quoteTableName($table) . ' TO ' . $this->quoteTableName($newName);
  359. }
  360. /**
  361. * Builds a SQL statement for dropping a DB table.
  362. * @param string $table the table to be dropped. The name will be properly quoted by the method.
  363. * @return string the SQL statement for dropping a DB table.
  364. * @since 1.1.6
  365. */
  366. public function dropTable($table)
  367. {
  368. return "DROP TABLE ".$this->quoteTableName($table);
  369. }
  370. /**
  371. * Builds a SQL statement for truncating a DB table.
  372. * @param string $table the table to be truncated. The name will be properly quoted by the method.
  373. * @return string the SQL statement for truncating a DB table.
  374. * @since 1.1.6
  375. */
  376. public function truncateTable($table)
  377. {
  378. return "TRUNCATE TABLE ".$this->quoteTableName($table);
  379. }
  380. /**
  381. * Builds a SQL statement for adding a new DB column.
  382. * @param string $table the table that the new column will be added to. The table name will be properly quoted by the method.
  383. * @param string $column the name of the new column. The name will be properly quoted by the method.
  384. * @param string $type the column type. The {@link getColumnType} method will be invoked to convert abstract column type (if any)
  385. * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
  386. * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
  387. * @return string the SQL statement for adding a new column.
  388. * @since 1.1.6
  389. */
  390. public function addColumn($table, $column, $type)
  391. {
  392. $type=$this->getColumnType($type);
  393. $sql='ALTER TABLE ' . $this->quoteTableName($table)
  394. . ' ADD ' . $this->quoteColumnName($column) . ' '
  395. . $this->getColumnType($type);
  396. return $sql;
  397. }
  398. /**
  399. * Builds a SQL statement for dropping a DB column.
  400. * @param string $table the table whose column is to be dropped. The name will be properly quoted by the method.
  401. * @param string $column the name of the column to be dropped. The name will be properly quoted by the method.
  402. * @return string the SQL statement for dropping a DB column.
  403. * @since 1.1.6
  404. */
  405. public function dropColumn($table, $column)
  406. {
  407. return "ALTER TABLE ".$this->quoteTableName($table)
  408. ." DROP COLUMN ".$this->quoteColumnName($column);
  409. }
  410. /**
  411. * Builds a SQL statement for renaming a column.
  412. * @param string $table the table whose column is to be renamed. The name will be properly quoted by the method.
  413. * @param string $name the old name of the column. The name will be properly quoted by the method.
  414. * @param string $newName the new name of the column. The name will be properly quoted by the method.
  415. * @return string the SQL statement for renaming a DB column.
  416. * @since 1.1.6
  417. */
  418. public function renameColumn($table, $name, $newName)
  419. {
  420. return "ALTER TABLE ".$this->quoteTableName($table)
  421. . " RENAME COLUMN ".$this->quoteColumnName($name)
  422. . " TO ".$this->quoteColumnName($newName);
  423. }
  424. /**
  425. * Builds a SQL statement for changing the definition of a column.
  426. * @param string $table the table whose column is to be changed. The table name will be properly quoted by the method.
  427. * @param string $column the name of the column to be changed. The name will be properly quoted by the method.
  428. * @param string $type the new column type. The {@link getColumnType} method will be invoked to convert abstract column type (if any)
  429. * into the physical one. Anything that is not recognized as abstract type will be kept in the generated SQL.
  430. * For example, 'string' will be turned into 'varchar(255)', while 'string not null' will become 'varchar(255) not null'.
  431. * @return string the SQL statement for changing the definition of a column.
  432. * @since 1.1.6
  433. */
  434. public function alterColumn($table, $column, $type)
  435. {
  436. $type=$this->getColumnType($type);
  437. return 'ALTER TABLE ' . $this->quoteTableName($table) . ' CHANGE '
  438. . $this->quoteColumnName($column) . ' '
  439. . $this->quoteColumnName($column) . ' '
  440. . $this->getColumnType($type);
  441. }
  442. /**
  443. * Builds a SQL statement for adding a foreign key constraint to an existing table.
  444. * The method will properly quote the table and column names.
  445. * @param string $name the name of the foreign key constraint.
  446. * @param string $table the table that the foreign key constraint will be added to.
  447. * @param string $columns the name of the column to that the constraint will be added on. If there are multiple columns, separate them with commas.
  448. * @param string $refTable the table that the foreign key references to.
  449. * @param string $refColumns the name of the column that the foreign key references to. If there are multiple columns, separate them with commas.
  450. * @param string $delete the ON DELETE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
  451. * @param string $update the ON UPDATE option. Most DBMS support these options: RESTRICT, CASCADE, NO ACTION, SET DEFAULT, SET NULL
  452. * @return string the SQL statement for adding a foreign key constraint to an existing table.
  453. * @since 1.1.6
  454. */
  455. public function addForeignKey($name, $table, $columns, $refTable, $refColumns, $delete=null, $update=null)
  456. {
  457. $columns=preg_split('/\s*,\s*/',$columns,-1,PREG_SPLIT_NO_EMPTY);
  458. foreach($columns as $i=>$col)
  459. $columns[$i]=$this->quoteColumnName($col);
  460. $refColumns=preg_split('/\s*,\s*/',$refColumns,-1,PREG_SPLIT_NO_EMPTY);
  461. foreach($refColumns as $i=>$col)
  462. $refColumns[$i]=$this->quoteColumnName($col);
  463. $sql='ALTER TABLE '.$this->quoteTableName($table)
  464. .' ADD CONSTRAINT '.$this->quoteColumnName($name)
  465. .' FOREIGN KEY ('.implode(', ', $columns).')'
  466. .' REFERENCES '.$this->quoteTableName($refTable)
  467. .' ('.implode(', ', $refColumns).')';
  468. if($delete!==null)
  469. $sql.=' ON DELETE '.$delete;
  470. if($update!==null)
  471. $sql.=' ON UPDATE '.$update;
  472. return $sql;
  473. }
  474. /**
  475. * Builds a SQL statement for dropping a foreign key constraint.
  476. * @param string $name the name of the foreign key constraint to be dropped. The name will be properly quoted by the method.
  477. * @param string $table the table whose foreign is to be dropped. The name will be properly quoted by the method.
  478. * @return string the SQL statement for dropping a foreign key constraint.
  479. * @since 1.1.6
  480. */
  481. public function dropForeignKey($name, $table)
  482. {
  483. return 'ALTER TABLE '.$this->quoteTableName($table)
  484. .' DROP CONSTRAINT '.$this->quoteColumnName($name);
  485. }
  486. /**
  487. * Builds a SQL statement for creating a new index.
  488. * @param string $name the name of the index. The name will be properly quoted by the method.
  489. * @param string $table the table that the new index will be created for. The table name will be properly quoted by the method.
  490. * @param string $column the column(s) that should be included in the index. If there are multiple columns, please separate them
  491. * by commas. Each column name will be properly quoted by the method, unless a parenthesis is found in the name.
  492. * @param boolean $unique whether to add UNIQUE constraint on the created index.
  493. * @return string the SQL statement for creating a new index.
  494. * @since 1.1.6
  495. */
  496. public function createIndex($name, $table, $column, $unique=false)
  497. {
  498. $cols=array();
  499. $columns=preg_split('/\s*,\s*/',$column,-1,PREG_SPLIT_NO_EMPTY);
  500. foreach($columns as $col)
  501. {
  502. if(strpos($col,'(')!==false)
  503. $cols[]=$col;
  504. else
  505. $cols[]=$this->quoteColumnName($col);
  506. }
  507. return ($unique ? 'CREATE UNIQUE INDEX ' : 'CREATE INDEX ')
  508. . $this->quoteTableName($name).' ON '
  509. . $this->quoteTableName($table).' ('.implode(', ',$cols).')';
  510. }
  511. /**
  512. * Builds a SQL statement for dropping an index.
  513. * @param string $name the name of the index to be dropped. The name will be properly quoted by the method.
  514. * @param string $table the table whose index is to be dropped. The name will be properly quoted by the method.
  515. * @return string the SQL statement for dropping an index.
  516. * @since 1.1.6
  517. */
  518. public function dropIndex($name, $table)
  519. {
  520. return 'DROP INDEX '.$this->quoteTableName($name).' ON '.$this->quoteTableName($table);
  521. }
  522. }