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

/code/ryzom/tools/server/www/webtt/cake/console/libs/schema.php

https://bitbucket.org/mattraykowski/ryzomcore_demoshard
PHP | 512 lines | 339 code | 66 blank | 107 comment | 66 complexity | 52813687eb4adf428225ceeae36bf75f MD5 | raw file
Possible License(s): AGPL-3.0, GPL-3.0, LGPL-2.1
  1. <?php
  2. /**
  3. * Command-line database management utility to automate programmer chores.
  4. *
  5. * Schema is CakePHP's database management utility. This helps you maintain versions of
  6. * of your database.
  7. *
  8. * PHP versions 4 and 5
  9. *
  10. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  11. * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  12. *
  13. * Licensed under The MIT License
  14. * Redistributions of files must retain the above copyright notice.
  15. *
  16. * @copyright Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
  17. * @link http://cakephp.org CakePHP(tm) Project
  18. * @package cake
  19. * @subpackage cake.cake.console.libs
  20. * @since CakePHP(tm) v 1.2.0.5550
  21. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  22. */
  23. App::import('Core', 'File', false);
  24. App::import('Model', 'CakeSchema', false);
  25. /**
  26. * Schema is a command-line database management utility for automating programmer chores.
  27. *
  28. * @package cake
  29. * @subpackage cake.cake.console.libs
  30. * @link http://book.cakephp.org/view/1523/Schema-management-and-migrations
  31. */
  32. class SchemaShell extends Shell {
  33. /**
  34. * is this a dry run?
  35. *
  36. * @var boolean
  37. * @access private
  38. */
  39. var $__dry = null;
  40. /**
  41. * Override initialize
  42. *
  43. * @access public
  44. */
  45. function initialize() {
  46. $this->_welcome();
  47. $this->out('Cake Schema Shell');
  48. $this->hr();
  49. }
  50. /**
  51. * Override startup
  52. *
  53. * @access public
  54. */
  55. function startup() {
  56. $name = $file = $path = $connection = $plugin = null;
  57. if (!empty($this->params['name'])) {
  58. $name = $this->params['name'];
  59. } elseif (!empty($this->args[0])) {
  60. $name = $this->params['name'] = $this->args[0];
  61. }
  62. if (strpos($name, '.')) {
  63. list($this->params['plugin'], $splitName) = pluginSplit($name);
  64. $name = $this->params['name'] = $splitName;
  65. }
  66. if ($name) {
  67. $this->params['file'] = Inflector::underscore($name);
  68. }
  69. if (empty($this->params['file'])) {
  70. $this->params['file'] = 'schema.php';
  71. }
  72. if (strpos($this->params['file'], '.php') === false) {
  73. $this->params['file'] .= '.php';
  74. }
  75. $file = $this->params['file'];
  76. if (!empty($this->params['path'])) {
  77. $path = $this->params['path'];
  78. }
  79. if (!empty($this->params['connection'])) {
  80. $connection = $this->params['connection'];
  81. }
  82. if (!empty($this->params['plugin'])) {
  83. $plugin = $this->params['plugin'];
  84. if (empty($name)) {
  85. $name = $plugin;
  86. }
  87. }
  88. $this->Schema =& new CakeSchema(compact('name', 'path', 'file', 'connection', 'plugin'));
  89. }
  90. /**
  91. * Override main
  92. *
  93. * @access public
  94. */
  95. function main() {
  96. $this->help();
  97. }
  98. /**
  99. * Read and output contents of schema object
  100. * path to read as second arg
  101. *
  102. * @access public
  103. */
  104. function view() {
  105. $File = new File($this->Schema->path . DS . $this->params['file']);
  106. if ($File->exists()) {
  107. $this->out($File->read());
  108. $this->_stop();
  109. } else {
  110. $file = $this->Schema->path . DS . $this->params['file'];
  111. $this->err(sprintf(__('Schema file (%s) could not be found.', true), $file));
  112. $this->_stop();
  113. }
  114. }
  115. /**
  116. * Read database and Write schema object
  117. * accepts a connection as first arg or path to save as second arg
  118. *
  119. * @access public
  120. */
  121. function generate() {
  122. $this->out(__('Generating Schema...', true));
  123. $options = array();
  124. if (isset($this->params['f'])) {
  125. $options = array('models' => false);
  126. }
  127. $snapshot = false;
  128. if (isset($this->args[0]) && $this->args[0] === 'snapshot') {
  129. $snapshot = true;
  130. }
  131. if (!$snapshot && file_exists($this->Schema->path . DS . $this->params['file'])) {
  132. $snapshot = true;
  133. $result = strtolower($this->in("Schema file exists.\n [O]verwrite\n [S]napshot\n [Q]uit\nWould you like to do?", array('o', 's', 'q'), 's'));
  134. if ($result === 'q') {
  135. return $this->_stop();
  136. }
  137. if ($result === 'o') {
  138. $snapshot = false;
  139. }
  140. }
  141. $cacheDisable = Configure::read('Cache.disable');
  142. Configure::write('Cache.disable', true);
  143. $content = $this->Schema->read($options);
  144. $content['file'] = $this->params['file'];
  145. Configure::write('Cache.disable', $cacheDisable);
  146. if ($snapshot === true) {
  147. $Folder =& new Folder($this->Schema->path);
  148. $result = $Folder->read();
  149. $numToUse = false;
  150. if (isset($this->params['s'])) {
  151. $numToUse = $this->params['s'];
  152. }
  153. $count = 1;
  154. if (!empty($result[1])) {
  155. foreach ($result[1] as $file) {
  156. if (preg_match('/schema(?:[_\d]*)?\.php$/', $file)) {
  157. $count++;
  158. }
  159. }
  160. }
  161. if ($numToUse !== false) {
  162. if ($numToUse > $count) {
  163. $count = $numToUse;
  164. }
  165. }
  166. $fileName = rtrim($this->params['file'], '.php');
  167. $content['file'] = $fileName . '_' . $count . '.php';
  168. }
  169. if ($this->Schema->write($content)) {
  170. $this->out(sprintf(__('Schema file: %s generated', true), $content['file']));
  171. $this->_stop();
  172. } else {
  173. $this->err(__('Schema file: %s generated', true));
  174. $this->_stop();
  175. }
  176. }
  177. /**
  178. * Dump Schema object to sql file
  179. * Use the `write` param to enable and control SQL file output location.
  180. * Simply using -write will write the sql file to the same dir as the schema file.
  181. * If -write contains a full path name the file will be saved there. If -write only
  182. * contains no DS, that will be used as the file name, in the same dir as the schema file.
  183. *
  184. * @access public
  185. */
  186. function dump() {
  187. $write = false;
  188. $Schema = $this->Schema->load();
  189. if (!$Schema) {
  190. $this->err(__('Schema could not be loaded', true));
  191. $this->_stop();
  192. }
  193. if (isset($this->params['write'])) {
  194. if ($this->params['write'] == 1) {
  195. $write = Inflector::underscore($this->Schema->name);
  196. } else {
  197. $write = $this->params['write'];
  198. }
  199. }
  200. $db =& ConnectionManager::getDataSource($this->Schema->connection);
  201. $contents = "#" . $Schema->name . " sql generated on: " . date('Y-m-d H:i:s') . " : " . time() . "\n\n";
  202. $contents .= $db->dropSchema($Schema) . "\n\n". $db->createSchema($Schema);
  203. if ($write) {
  204. if (strpos($write, '.sql') === false) {
  205. $write .= '.sql';
  206. }
  207. if (strpos($write, DS) !== false) {
  208. $File =& new File($write, true);
  209. } else {
  210. $File =& new File($this->Schema->path . DS . $write, true);
  211. }
  212. if ($File->write($contents)) {
  213. $this->out(sprintf(__('SQL dump file created in %s', true), $File->pwd()));
  214. $this->_stop();
  215. } else {
  216. $this->err(__('SQL dump could not be created', true));
  217. $this->_stop();
  218. }
  219. }
  220. $this->out($contents);
  221. return $contents;
  222. }
  223. /**
  224. * Run database create commands. Alias for run create.
  225. *
  226. * @return void
  227. */
  228. function create() {
  229. list($Schema, $table) = $this->_loadSchema();
  230. $this->__create($Schema, $table);
  231. }
  232. /**
  233. * Run database create commands. Alias for run create.
  234. *
  235. * @return void
  236. */
  237. function update() {
  238. list($Schema, $table) = $this->_loadSchema();
  239. $this->__update($Schema, $table);
  240. }
  241. /**
  242. * Prepares the Schema objects for database operations.
  243. *
  244. * @return void
  245. */
  246. function _loadSchema() {
  247. $name = $plugin = null;
  248. if (isset($this->params['name'])) {
  249. $name = $this->params['name'];
  250. }
  251. if (isset($this->params['plugin'])) {
  252. $plugin = $this->params['plugin'];
  253. }
  254. if (isset($this->params['dry'])) {
  255. $this->__dry = true;
  256. $this->out(__('Performing a dry run.', true));
  257. }
  258. $options = array('name' => $name, 'plugin' => $plugin);
  259. if (isset($this->params['s'])) {
  260. $fileName = rtrim($this->Schema->file, '.php');
  261. $options['file'] = $fileName . '_' . $this->params['s'] . '.php';
  262. }
  263. $Schema =& $this->Schema->load($options);
  264. if (!$Schema) {
  265. $this->err(sprintf(__('%s could not be loaded', true), $this->Schema->path . DS . $this->Schema->file));
  266. $this->_stop();
  267. }
  268. $table = null;
  269. if (isset($this->args[1])) {
  270. $table = $this->args[1];
  271. }
  272. return array(&$Schema, $table);
  273. }
  274. /**
  275. * Create database from Schema object
  276. * Should be called via the run method
  277. *
  278. * @access private
  279. */
  280. function __create(&$Schema, $table = null) {
  281. $db =& ConnectionManager::getDataSource($this->Schema->connection);
  282. $drop = $create = array();
  283. if (!$table) {
  284. foreach ($Schema->tables as $table => $fields) {
  285. $drop[$table] = $db->dropSchema($Schema, $table);
  286. $create[$table] = $db->createSchema($Schema, $table);
  287. }
  288. } elseif (isset($Schema->tables[$table])) {
  289. $drop[$table] = $db->dropSchema($Schema, $table);
  290. $create[$table] = $db->createSchema($Schema, $table);
  291. }
  292. if (empty($drop) || empty($create)) {
  293. $this->out(__('Schema is up to date.', true));
  294. $this->_stop();
  295. }
  296. $this->out("\n" . __('The following table(s) will be dropped.', true));
  297. $this->out(array_keys($drop));
  298. if ('y' == $this->in(__('Are you sure you want to drop the table(s)?', true), array('y', 'n'), 'n')) {
  299. $this->out(__('Dropping table(s).', true));
  300. $this->__run($drop, 'drop', $Schema);
  301. }
  302. $this->out("\n" . __('The following table(s) will be created.', true));
  303. $this->out(array_keys($create));
  304. if ('y' == $this->in(__('Are you sure you want to create the table(s)?', true), array('y', 'n'), 'y')) {
  305. $this->out(__('Creating table(s).', true));
  306. $this->__run($create, 'create', $Schema);
  307. }
  308. $this->out(__('End create.', true));
  309. }
  310. /**
  311. * Update database with Schema object
  312. * Should be called via the run method
  313. *
  314. * @access private
  315. */
  316. function __update(&$Schema, $table = null) {
  317. $db =& ConnectionManager::getDataSource($this->Schema->connection);
  318. $this->out(__('Comparing Database to Schema...', true));
  319. $options = array();
  320. if (isset($this->params['f'])) {
  321. $options['models'] = false;
  322. }
  323. $Old = $this->Schema->read($options);
  324. $compare = $this->Schema->compare($Old, $Schema);
  325. $contents = array();
  326. if (empty($table)) {
  327. foreach ($compare as $table => $changes) {
  328. $contents[$table] = $db->alterSchema(array($table => $changes), $table);
  329. }
  330. } elseif (isset($compare[$table])) {
  331. $contents[$table] = $db->alterSchema(array($table => $compare[$table]), $table);
  332. }
  333. if (empty($contents)) {
  334. $this->out(__('Schema is up to date.', true));
  335. $this->_stop();
  336. }
  337. $this->out("\n" . __('The following statements will run.', true));
  338. $this->out(array_map('trim', $contents));
  339. if ('y' == $this->in(__('Are you sure you want to alter the tables?', true), array('y', 'n'), 'n')) {
  340. $this->out();
  341. $this->out(__('Updating Database...', true));
  342. $this->__run($contents, 'update', $Schema);
  343. }
  344. $this->out(__('End update.', true));
  345. }
  346. /**
  347. * Runs sql from __create() or __update()
  348. *
  349. * @access private
  350. */
  351. function __run($contents, $event, &$Schema) {
  352. if (empty($contents)) {
  353. $this->err(__('Sql could not be run', true));
  354. return;
  355. }
  356. Configure::write('debug', 2);
  357. $db =& ConnectionManager::getDataSource($this->Schema->connection);
  358. foreach ($contents as $table => $sql) {
  359. if (empty($sql)) {
  360. $this->out(sprintf(__('%s is up to date.', true), $table));
  361. } else {
  362. if ($this->__dry === true) {
  363. $this->out(sprintf(__('Dry run for %s :', true), $table));
  364. $this->out($sql);
  365. } else {
  366. if (!$Schema->before(array($event => $table))) {
  367. return false;
  368. }
  369. $error = null;
  370. if (!$db->execute($sql)) {
  371. $error = $table . ': ' . $db->lastError();
  372. }
  373. $Schema->after(array($event => $table, 'errors' => $error));
  374. if (!empty($error)) {
  375. $this->out($error);
  376. } else {
  377. $this->out(sprintf(__('%s updated.', true), $table));
  378. }
  379. }
  380. }
  381. }
  382. }
  383. /**
  384. * Displays help contents
  385. *
  386. * @access public
  387. */
  388. function help() {
  389. $help = <<<TEXT
  390. The Schema Shell generates a schema object from
  391. the database and updates the database from the schema.
  392. ---------------------------------------------------------------
  393. Usage: cake schema <command> <arg1> <arg2>...
  394. ---------------------------------------------------------------
  395. Params:
  396. -connection <config>
  397. set db config <config>. uses 'default' if none is specified
  398. -path <dir>
  399. path <dir> to read and write schema.php.
  400. default path: {$this->Schema->path}
  401. -name <name>
  402. Classname to use. If <name> is Plugin.className, it will
  403. set the plugin and name params.
  404. -file <name>
  405. file <name> to read and write.
  406. default file: {$this->Schema->file}
  407. -s <number>
  408. snapshot <number> to use for run.
  409. -dry
  410. Perform a dry run on create + update commands.
  411. Queries will be output to window instead of executed.
  412. -f
  413. force 'generate' to create a new schema.
  414. -plugin
  415. Indicate the plugin to use.
  416. Commands:
  417. schema help
  418. shows this help message.
  419. schema view <name>
  420. read and output contents of schema file.
  421. schema generate
  422. reads from 'connection' writes to 'path'
  423. To force generation of all tables into the schema, use the -f param.
  424. Use 'schema generate snapshot <number>' to generate snapshots
  425. which you can use with the -s parameter in the other operations.
  426. schema dump <name>
  427. Dump database sql based on schema file to stdout.
  428. If you use the `-write` param is used a .sql will be generated.
  429. If `-write` is a filename, then that file name will be generate.
  430. If `-write` is a full path, the schema will be written there.
  431. schema create <name> <table>
  432. Drop and create tables based on schema file
  433. optional <table> argument can be used to create only a single
  434. table in the schema. Pass the -s param with a number to use a snapshot.
  435. Use the `-dry` param to preview the changes.
  436. schema update <name> <table>
  437. Alter the tables based on schema file. Optional <table>
  438. parameter will only update one table.
  439. To use a snapshot pass the `-s` param with the snapshot number.
  440. To preview the changes that will be done use `-dry`.
  441. To force update of all tables into the schema, use the -f param.
  442. TEXT;
  443. $this->out($help);
  444. $this->_stop();
  445. }
  446. }