PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/baser/vendors/sql_dumper/sql_dumper.php

https://github.com/hashing/basercms
PHP | 506 lines | 221 code | 119 blank | 166 comment | 37 complexity | 0ef56f30f44c86d1252af38bff5ee50b MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. /**
  3. * SQL Dumper for CakePHP
  4. *
  5. * for CakePHP 1.2+
  6. * PHP version 5
  7. *
  8. * Copyright 2010, nojimage (http://php-tips.com/)
  9. *
  10. * Licensed under The MIT License
  11. * Redistributions of files must retain the above copyright notice.
  12. *
  13. * @version 1.0.1
  14. * @author nojimage <nojimage at gmail.com>
  15. * @copyright 2010 nojimage (http://php-tips.com/)
  16. * @license http://www.opensource.org/licenses/mit-license.php The MIT License
  17. * @package sql_dumper
  18. * @subpackage sql_dumper.vendors
  19. * @link  http://php-tips.com/
  20. * @since   File available since Release 1.0.0
  21. *
  22. */
  23. class SqlDumper extends Object {
  24. var $description = '';
  25. var $message = 'generator: SqlDumper for CakePHP ver 1.0.1';
  26. /**
  27. * output filename prefix
  28. *
  29. * allow using strftime format
  30. *
  31. * @var string
  32. */
  33. var $file_prefix = 'sql_dump-';
  34. /**
  35. * output filename suffix
  36. *
  37. * allow using strftime format
  38. *
  39. * @var string
  40. */
  41. var $file_suffix = '-%Y%m%d_%H%M%S.sql';
  42. /**
  43. *
  44. * @var DboSource
  45. */
  46. var $DataSource;
  47. /**
  48. *
  49. * @var CakeSchema
  50. */
  51. var $Schema;
  52. /**
  53. *
  54. * @var File
  55. */
  56. var $File;
  57. /**
  58. *
  59. * @var array
  60. */
  61. var $_tables;
  62. /**
  63. *
  64. */
  65. function __construct() {
  66. App::import('Model', array('CakeSchema', 'AppModel'));
  67. if (!class_exists('CakeSchema')) {
  68. // for CakePHP 1.2
  69. App::import('Model', 'Schema');
  70. }
  71. $this->Schema =& ClassRegistry::init('CakeSchema');
  72. }
  73. /**
  74. * Process Dump Datasouces
  75. *
  76. * @param string $datasource required.
  77. * @param string $tablename optional.
  78. * @param string $save optional.
  79. * @param boolean $with_create optional. default: true
  80. * @param boolean $with_drop optional. default: true
  81. * @param boolean $with_insert optional. default: true
  82. * @param boolean $exclude_missing_tables optional. default: false
  83. * @return string
  84. */
  85. function process($datasource, $tablename = null, $save = null,
  86. $exclude_missing_tables = false,
  87. $with_create = true, $with_drop = true, $with_insert = true) {
  88. if ( !$this->_setupDataSource($datasource) ) {
  89. return false;
  90. }
  91. $this->_setupOutput($datasource, $save);
  92. $processTables = $this->_getProcessTables($tablename, $exclude_missing_tables);
  93. $sql = '';
  94. $sql .= $this->_createSqlDumpHeader($datasource);
  95. foreach ($processTables as $_table => $data) {
  96. if ($with_drop) {
  97. $sql .= $this->getDropSql($datasource, $_table);
  98. }
  99. if ($with_create) {
  100. $sql .= $this->getCreateSql($datasource, $_table);
  101. }
  102. if ($with_insert) {
  103. $this->getInsertSql($datasource, $_table);
  104. }
  105. $sql .= $this->out($this->hr());
  106. }
  107. $sql .= $this->_createSqlDumpFooter($datasource);
  108. $this->_closeOutput();
  109. return $sql;
  110. }
  111. /**
  112. * create all datasouces sql dump file
  113. *
  114. * @param string $save path to save folder.
  115. */
  116. function processAll($save) {
  117. $datasources = ConnectionManager::sourceList();
  118. foreach ($datasources as $datasource) {
  119. $this->process($datasource, null, $save);
  120. }
  121. }
  122. /**
  123. * sql create statement
  124. *
  125. * @param $datasource
  126. * @param $tablename
  127. * @param $exclude_missing_tables
  128. * @return string
  129. */
  130. function getCreateSql($datasource, $tablename = null, $exclude_missing_tables = false) {
  131. if (!$this->_checkCurrentDatasource($datasource)) {
  132. $this->_setupDataSource();
  133. }
  134. $this->Schema->tables = $this->_getProcessTables($tablename, $exclude_missing_tables);
  135. $sql = $this->DataSource->createSchema($this->Schema);
  136. return $this->out($sql);
  137. }
  138. /**
  139. * sql drop statement
  140. *
  141. * @param $datasource
  142. * @param $tablename
  143. * @param $exclude_missing_tables
  144. * @return string
  145. */
  146. function getDropSql($datasource, $tablename = null, $exclude_missing_tables = false) {
  147. if (!$this->_checkCurrentDatasource($datasource)) {
  148. $this->_setupDataSource();
  149. }
  150. $this->Schema->tables = $this->_getProcessTables($tablename, $exclude_missing_tables);
  151. $sql = $this->DataSource->dropSchema($this->Schema);
  152. return $this->out($sql);
  153. }
  154. /**
  155. * sql insert statement
  156. *
  157. * @param $datasource
  158. * @param $tablename
  159. * @param $exclude_missing_tables
  160. * @param $return if want return sql string, set true.
  161. * @return string
  162. */
  163. function getInsertSql($datasource, $tablename, $exclude_missing_tables = false, $return = false) {
  164. if (!$this->_checkCurrentDatasource($datasource)) {
  165. $this->_setupDataSource();
  166. }
  167. if (!$return && (empty($this->File) || !$this->File->writable())) {
  168. return false;
  169. }
  170. $tables = $this->_getProcessTables($tablename, $exclude_missing_tables);
  171. $insert_sql = '';
  172. foreach ($tables as $table => $fields) {
  173. /* @var $model AppModel */
  174. $model = ClassRegistry::init(array('class' => Inflector::classify($table), 'table' => $table));
  175. $field_names = array_keys($this->DataSource->describe($model));
  176. $full_tablename = $this->DataSource->fullTableName($model);
  177. $all_fields = implode(', ', array_map(array($this->DataSource, 'name'), $field_names));
  178. $count_query = array(
  179. 'table' => $full_tablename,
  180. 'fields' => 'count(*) ' . $this->DataSource->alias . 'count',
  181. 'alias' => $this->DataSource->alias . $this->DataSource->name($model->alias),
  182. 'joins' => '',
  183. 'conditions' => 'WHERE 1=1',
  184. 'group' => '',
  185. 'order' => '',
  186. 'limit' => '',
  187. );
  188. $count_sql = $this->DataSource->renderStatement('select', $count_query);
  189. $total = $this->DataSource->fetchRow($count_sql);
  190. if (is_array($total)) {
  191. $total = $total[0]['count'];
  192. }
  193. $query = array(
  194. 'table' => $full_tablename,
  195. 'fields' => implode(', ', $this->DataSource->fields($model)),
  196. 'alias' => $this->DataSource->alias . $this->DataSource->name($model->alias),
  197. 'joins' => '',
  198. 'conditions' => '',
  199. 'group' => '',
  200. 'order' => '',
  201. 'limit' => '',
  202. );
  203. $limit = 100;
  204. $record = array();
  205. for ($offset = 0; $offset < $total; $offset += $limit) {
  206. $query['limit'] = $this->DataSource->limit($limit, $offset);
  207. $select_sql = $this->DataSource->renderStatement('select', $query);
  208. $datas = $this->DataSource->fetchAll($select_sql, false);
  209. foreach ($datas as $record) {
  210. $insert_query = array(
  211. 'table' => $full_tablename,
  212. 'fields' => $all_fields,
  213. 'values' => implode(', ', array_map(array($this->DataSource, 'value'), array_values($record[$model->alias])))
  214. );
  215. $_sql = $this->out($this->DataSource->renderStatement('create', $insert_query) . ';');
  216. if ($return) {
  217. $insert_sql .= $_sql;
  218. }
  219. }
  220. }
  221. // -- sequence update section for postgres
  222. // NOTE: only primary key sequence..
  223. if (method_exists($this->DataSource, 'getSequence')) {
  224. foreach ($fields as $field => $column) {
  225. if ($field == 'indexes' || empty($record)) {
  226. continue;
  227. }
  228. if ($column['type'] == 'integer' && isset($column['key']) && $column['key'] == 'primary') {
  229. // only primary key
  230. $sequence_name = $this->DataSource->getSequence($this->DataSource->fullTableName($model, false), $field);
  231. $_sql = $this->out( sprintf('SELECT setval(%s, %s);', $this->DataSource->value($sequence_name), $record[$model->alias][$field]) );
  232. if ($return) {
  233. $insert_sql .= $_sql;
  234. }
  235. }
  236. }
  237. }
  238. }
  239. return $insert_sql;
  240. }
  241. /**
  242. * Setup DataSource Object and get tables information
  243. *
  244. * @param string $datasouce
  245. * @return bool
  246. */
  247. function _setupDataSource($datasouce) {
  248. // get datasource
  249. $this->DataSource =& ConnectionManager::getDataSource($datasouce);
  250. if (!is_subclass_of($this->DataSource, 'DboSource')) {
  251. // DataSource is not subclass of DboSource
  252. return false;
  253. }
  254. // get datasouces tables
  255. $schema = $this->Schema->read(array('connection' => $this->DataSource->configKeyName, 'models' => false));
  256. $this->_tables = $schema['tables'];
  257. return true;
  258. }
  259. /**
  260. *
  261. *
  262. * @param string $tablename
  263. * @param boolean $exclude_missing_tables
  264. * @return array
  265. */
  266. function _getProcessTables($tablename = null, $exclude_missing_tables = false) {
  267. $tables = $this->_tables;
  268. unset($tables['missing']);
  269. if (!$exclude_missing_tables && !empty($this->_tables['missing'])) {
  270. $tables = am($tables, $this->_tables['missing']);
  271. }
  272. if (!empty($tablename)) {
  273. if (!empty($tables[$tablename])) {
  274. return array($tablename => $tables[$tablename]);
  275. }
  276. return array();
  277. }
  278. return $tables;
  279. }
  280. /**
  281. * Check current DataSouce
  282. *
  283. * @param string $datasource
  284. */
  285. function _checkCurrentDatasource($datasource) {
  286. return $this->DataSource->configKeyName == $datasource;
  287. }
  288. /**
  289. * Setup output source
  290. *
  291. * @param $datasource datasource name
  292. * @param $save path to save folder
  293. */
  294. function _setupOutput($datasource, $save) {
  295. if (empty($save)) {
  296. return false;
  297. }
  298. $now = time();
  299. $path = realpath($save) . DS . strftime($this->file_prefix, $now) . $datasource . strftime($this->file_suffix, $now);
  300. $this->File = new File($path, false, 0666);
  301. return $this->File->open('w');
  302. }
  303. /**
  304. * Close file resource
  305. */
  306. function _closeOutput() {
  307. if (isset($this->File)) {
  308. $this->File->close();
  309. }
  310. }
  311. /**
  312. * get output file path
  313. */
  314. function getOutputPath() {
  315. return $this->File->pwd();
  316. }
  317. /**
  318. * SQL file header
  319. *
  320. * @param $datasource
  321. * @return string
  322. */
  323. function _createSqlDumpHeader($datasource) {
  324. $sql = array();
  325. $sql[] = $this->hr(0);
  326. $sql[] = '-- ' . $this->message;
  327. $sql[] = '-- generated on: ' . date('Y-m-d H:i:s') . ' : ' . time();
  328. $sql[] = $this->hr(0);
  329. $sql[] = '';
  330. if (preg_match('/^mysql/i', $this->DataSource->config['driver'])) {
  331. $sql[] = 'use ' . $this->DataSource->name($this->DataSource->config['database']) . ';';
  332. }
  333. if (!empty($this->DataSource->config['encoding'])) {
  334. $sql[] = 'SET NAMES ' . $this->DataSource->value($this->DataSource->config['encoding']) . ';';
  335. }
  336. return $this->out($sql);
  337. }
  338. /**
  339. * SQL file footer
  340. *
  341. * @param $datasource
  342. * @return string
  343. */
  344. function _createSqlDumpFooter($datasource) {
  345. $sql = array();
  346. $sql[] = $this->hr(0);
  347. $sql[] = '-- END OF SQL DUMP';
  348. $sql[] = $this->hr(0);
  349. return $this->out($sql);
  350. }
  351. /**
  352. * return separator string (SQL comment line)
  353. *
  354. * @return string
  355. */
  356. function hr($newline = 1) {
  357. return '-- ' . str_repeat('-', 70) . $this->nl($newline);
  358. }
  359. /**
  360. * Outputs a single or multiple error messages to stderr. If no parameters
  361. * are passed outputs just a newline.
  362. *
  363. * @param mixed $message A string or a an array of strings to output
  364. * @param integer $newlines Number of newlines to append
  365. * @access public
  366. */
  367. function out($message = null, $newlines = 1) {
  368. if (is_array($message)) {
  369. $message = implode($this->nl(), $message);
  370. }
  371. $output = $message . $this->nl($newlines);
  372. if (isset($this->File) && $this->File->writable()) {
  373. $this->File->append($output);
  374. }
  375. return $output;
  376. }
  377. /**
  378. * Returns a single or multiple linefeeds sequences.
  379. *
  380. * @param integer $multiplier Number of times the linefeed sequence should be repeated
  381. * @access public
  382. * @return string
  383. */
  384. function nl($multiplier = 1) {
  385. return str_repeat("\n", $multiplier);
  386. }
  387. }