PageRenderTime 38ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/baser/models/datasources/dbo/dbo_bc_postgres.php

https://github.com/hashing/basercms
PHP | 402 lines | 244 code | 26 blank | 132 comment | 70 complexity | 25204b3b6f22a175afe084c7e6d58e43 MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. /* SVN FILE: $Id$ */
  3. /**
  4. * PostgreSQL DBO拡張
  5. *
  6. * PHP versions 5
  7. *
  8. * baserCMS : Based Website Development Project <http://basercms.net>
  9. * Copyright 2008 - 2012, baserCMS Users Community <http://sites.google.com/site/baserusers/>
  10. *
  11. * @copyright Copyright 2008 - 2012, baserCMS Users Community
  12. * @link http://basercms.net baserCMS Project
  13. * @package baser.models.datasources.dbo
  14. * @since baserCMS v 0.1.0
  15. * @version $Revision$
  16. * @modifiedby $LastChangedBy$
  17. * @lastmodified $Date$
  18. * @license http://basercms.net/license/index.html
  19. */
  20. App::import('Core','DboPostgres');
  21. class DboBcPostgres extends DboPostgres {
  22. /**
  23. * Returns an array of the fields in given table name.
  24. *
  25. * @param string $tableName Name of database table to inspect
  26. * @return array Fields in table. Keys are name and type
  27. */
  28. function &describe(&$model) {
  29. // >>> CUSTOMIZE MODIFY 2012/04/23 ryuring
  30. //$fields = parent::describe($model);
  31. // ---
  32. $fields = $this->__describe($model);
  33. // <<<
  34. $table = $this->fullTableName($model, false);
  35. $this->_sequenceMap[$table] = array();
  36. if ($fields === null) {
  37. // >>> CUSTOMIZE MODIFY 2012/04/23 ryuring
  38. /*$cols = $this->fetchAll(
  39. "SELECT DISTINCT column_name AS name, data_type AS type, is_nullable AS null,
  40. column_default AS default, ordinal_position AS position, character_maximum_length AS char_length,
  41. character_octet_length AS oct_length FROM information_schema.columns
  42. WHERE table_name = " . $this->value($table) . " AND table_schema = " .
  43. $this->value($this->config['schema'])." ORDER BY position",
  44. false
  45. );*/
  46. // ---
  47. $cols = $this->fetchAll(
  48. "SELECT DISTINCT column_name AS name, data_type AS type, udt_name AS udt, is_nullable AS null,
  49. column_default AS default, ordinal_position AS position, character_maximum_length AS char_length,
  50. character_octet_length AS oct_length FROM information_schema.columns
  51. WHERE table_name = " . $this->value($table) . " AND table_schema = " .
  52. $this->value($this->config['schema'])." ORDER BY position",
  53. false
  54. );
  55. // <<<
  56. foreach ($cols as $column) {
  57. $colKey = array_keys($column);
  58. if (isset($column[$colKey[0]]) && !isset($column[0])) {
  59. $column[0] = $column[$colKey[0]];
  60. }
  61. if (isset($column[0])) {
  62. $c = $column[0];
  63. if (!empty($c['char_length'])) {
  64. $length = intval($c['char_length']);
  65. } elseif (!empty($c['oct_length'])) {
  66. if ($c['type'] == 'character varying') {
  67. $length = null;
  68. $c['type'] = 'text';
  69. // >>> CUSTOMIZE ADD 2011/08/22 ryuring
  70. } elseif($c['type'] == 'text') {
  71. $length = null;
  72. // <<<
  73. } else {
  74. $length = intval($c['oct_length']);
  75. }
  76. } else {
  77. // >>> CUSTOMIZE MODIFY 2012/04/23 ryuring
  78. //$length = $this->length($c['type']);
  79. // ---
  80. $length = $this->length($c['udt']);
  81. // <<<
  82. }
  83. $fields[$c['name']] = array(
  84. 'type' => $this->column($c['type']),
  85. 'null' => ($c['null'] == 'NO' ? false : true),
  86. 'default' => preg_replace(
  87. "/^'(.*)'$/",
  88. "$1",
  89. preg_replace('/::.*/', '', $c['default'])
  90. ),
  91. 'length' => $length
  92. );
  93. // >>> CUSTOMIZE ADD 2011/08/22 ryuring
  94. if (!$fields[$c['name']]['length'] && $fields[$c['name']]['type'] == 'integer') {
  95. $fields[$c['name']]['length'] = 8;
  96. }
  97. // <<<
  98. if ($c['name'] == $model->primaryKey) {
  99. $fields[$c['name']]['key'] = 'primary';
  100. if ($fields[$c['name']]['type'] !== 'string') {
  101. // >>> CUSTOMIZE MODIFY 2011/08/22 ryuring
  102. //$fields[$c['name']]['length'] = 11;
  103. // ---
  104. $fields[$c['name']]['length'] = 8;
  105. // <<<
  106. }
  107. }
  108. if (
  109. $fields[$c['name']]['default'] == 'NULL' ||
  110. preg_match('/nextval\([\'"]?([\w.]+)/', $c['default'], $seq)
  111. ) {
  112. $fields[$c['name']]['default'] = null;
  113. if (!empty($seq) && isset($seq[1])) {
  114. $this->_sequenceMap[$table][$c['name']] = $seq[1];
  115. }
  116. }
  117. // >>> CUSTOMIZE ADD 2011/08/22 ryuring
  118. if($fields[$c['name']]['default'] === 'true' && $fields[$c['name']]['type'] == 'boolean') {
  119. $fields[$c['name']]['default'] = 1;
  120. } elseif($fields[$c['name']]['default'] === 'false' && $fields[$c['name']]['type'] == 'boolean') {
  121. $fields[$c['name']]['default'] = 0;
  122. }
  123. // <<<
  124. }
  125. }
  126. $this->__cacheDescription($table, $fields);
  127. }
  128. if (isset($model->sequence)) {
  129. $this->_sequenceMap[$table][$model->primaryKey] = $model->sequence;
  130. }
  131. return $fields;
  132. }
  133. /**
  134. * テーブル名のリネームステートメントを生成
  135. *
  136. * @param string $sourceName
  137. * @param string $targetName
  138. * @return string
  139. * @access public
  140. */
  141. function buildRenameTable($sourceName, $targetName) {
  142. return "ALTER TABLE ".$sourceName." RENAME TO ".$targetName;
  143. }
  144. /**
  145. * カラム名を変更する
  146. *
  147. * @param array $options [ table / new / old ]
  148. * @return boolean
  149. * @access public
  150. */
  151. function renameColumn($options) {
  152. extract($options);
  153. if(!isset($table) || !isset($new) || !isset($old)) {
  154. return false;
  155. }
  156. $table = $this->config['prefix'] . $table;
  157. $sql = 'ALTER TABLE "'.$table.'" RENAME "'.$old.'" TO "'.$new.'"';
  158. return $this->execute($sql);
  159. }
  160. /**
  161. * Returns a quoted and escaped string of $data for use in an SQL statement.
  162. *
  163. * @param string $data String to be prepared for use in an SQL statement
  164. * @param string $column The column into which this data will be inserted
  165. * @param boolean $read Value to be used in READ or WRITE context
  166. * @return string Quoted and escaped
  167. * @todo Add logic that formats/escapes data based on column type
  168. * @access public
  169. */
  170. function value($data, $column = null, $read = true) {
  171. // >>> CUSTOMIZE MODIFY 2011/03/23 ryuring
  172. //$parent = parent::value($data, $column);
  173. // ---
  174. $parent = $this->__value($data, $column);
  175. // <<<
  176. if ($parent != null) {
  177. return $parent;
  178. }
  179. if ($data === null) {
  180. return 'NULL';
  181. }
  182. if (empty($column)) {
  183. $column = $this->introspectType($data);
  184. }
  185. switch($column) {
  186. case 'binary':
  187. $data = pg_escape_bytea($data);
  188. break;
  189. case 'boolean':
  190. if ($data === true || $data === 't' || $data === 'true') {
  191. return 'TRUE';
  192. } elseif ($data === false || $data === 'f' || $data === 'false') {
  193. return 'FALSE';
  194. }
  195. return (!empty($data) ? 'TRUE' : 'FALSE');
  196. break;
  197. case 'float':
  198. if (is_float($data)) {
  199. $data = sprintf('%F', $data);
  200. }
  201. case 'inet':
  202. case 'integer':
  203. case 'date':
  204. case 'datetime':
  205. case 'timestamp':
  206. case 'time':
  207. // >>> CUSTOMIZE ADD 2010/03/23 ryuring
  208. // postgresql の場合、0000-00-00 00:00:00 を指定すると範囲外エラーとなる為
  209. if ($data === '0000-00-00 00:00:00') {
  210. return "'".date('Y-m-d H:i:s', 0)."'";
  211. }
  212. // <<<
  213. if ($data === '') {
  214. return $read ? 'NULL' : 'DEFAULT';
  215. }
  216. default:
  217. $data = pg_escape_string($data);
  218. break;
  219. }
  220. return "'" . $data . "'";
  221. }
  222. /**
  223. * Prepares a value, or an array of values for database queries by quoting and escaping them.
  224. *
  225. * @param mixed $data A value or an array of values to prepare.
  226. * @param string $column The column into which this data will be inserted
  227. * @param boolean $read Value to be used in READ or WRITE context
  228. * @return mixed Prepared value or array of values.
  229. * @access private
  230. */
  231. function __value($data, $column = null, $read = true) {
  232. if (is_array($data) && !empty($data)) {
  233. return array_map(
  234. array(&$this, 'value'),
  235. $data, array_fill(0, count($data), $column), array_fill(0, count($data), $read)
  236. );
  237. } elseif (is_object($data) && isset($data->type)) {
  238. if ($data->type == 'identifier') {
  239. return $this->name($data->value);
  240. } elseif ($data->type == 'expression') {
  241. return $data->value;
  242. }
  243. } elseif (in_array($data, array('{$__cakeID__$}', '{$__cakeForeignKey__$}'), true)) {
  244. return $data;
  245. } else {
  246. return null;
  247. }
  248. }
  249. /**
  250. * Alter the Schema of a table.
  251. *
  252. * @param array $compare Results of CakeSchema::compare()
  253. * @param string $table name of the table
  254. * @access public
  255. * @return array
  256. */
  257. function alterSchema($compare, $table = null) {
  258. if (!is_array($compare)) {
  259. return false;
  260. }
  261. $out = '';
  262. $colList = array();
  263. foreach ($compare as $curTable => $types) {
  264. $indexes = array();
  265. if (!$table || $table == $curTable) {
  266. $out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
  267. foreach ($types as $type => $column) {
  268. if (isset($column['indexes'])) {
  269. $indexes[$type] = $column['indexes'];
  270. unset($column['indexes']);
  271. }
  272. switch ($type) {
  273. case 'add':
  274. foreach ($column as $field => $col) {
  275. $col['name'] = $field;
  276. $alter = 'ADD COLUMN '.$this->buildColumn($col);
  277. if (isset($col['after'])) {
  278. $alter .= ' AFTER '. $this->name($col['after']);
  279. }
  280. $colList[] = $alter;
  281. }
  282. break;
  283. case 'drop':
  284. foreach ($column as $field => $col) {
  285. $col['name'] = $field;
  286. $colList[] = 'DROP COLUMN '.$this->name($field);
  287. }
  288. break;
  289. case 'change':
  290. // CUSTOMIZE DEL 2010/05/16 ryuring
  291. //==================================================
  292. // PostgreSQLの場合、schemaでDB側の数値型の長さが取得できない為、
  293. // 変更されてない場合でも変更されてしまうので、chageは無視する
  294. // 仕様に変更(暫定措置)
  295. //==================================================
  296. /*foreach ($column as $field => $col) {
  297. if (!isset($col['name'])) {
  298. $col['name'] = $field;
  299. }
  300. $fieldName = $this->name($field);
  301. $colList[] = 'ALTER COLUMN '. $fieldName .' TYPE ' . str_replace($fieldName, '', $this->buildColumn($col));
  302. }*/
  303. break;
  304. }
  305. }
  306. if (isset($indexes['drop']['PRIMARY'])) {
  307. $colList[] = 'DROP CONSTRAINT ' . $curTable . '_pkey';
  308. }
  309. if (isset($indexes['add']['PRIMARY'])) {
  310. $cols = $indexes['add']['PRIMARY']['column'];
  311. if (is_array($cols)) {
  312. $cols = implode(', ', $cols);
  313. }
  314. $colList[] = 'ADD PRIMARY KEY (' . $cols . ')';
  315. }
  316. if (!empty($colList)) {
  317. $out .= "\t" . implode(",\n\t", $colList) . ";\n\n";
  318. } else {
  319. $out = '';
  320. }
  321. $out .= implode(";\n\t", $this->_alterIndexes($curTable, $indexes)) . ";";
  322. }
  323. }
  324. return $out;
  325. }
  326. /**
  327. * Gets the length of a database-native column description, or null if no length
  328. *
  329. * @param string $real Real database-layer column type (i.e. "varchar(255)")
  330. * @return int An integer representing the length of the column
  331. */
  332. function length($real) {
  333. // >>> CUSTOMIZE ADD 2012/04/23 ryuring
  334. if(preg_match('/^int([0-9]+)$/', $real, $maches)) {
  335. return intval($maches[1]);
  336. }
  337. // <<<
  338. $col = str_replace(array(')', 'unsigned'), '', $real);
  339. $limit = null;
  340. if (strpos($col, '(') !== false) {
  341. list($col, $limit) = explode('(', $col);
  342. }
  343. if ($col == 'uuid') {
  344. return 36;
  345. }
  346. if ($limit != null) {
  347. return intval($limit);
  348. }
  349. return null;
  350. }
  351. /**
  352. * Returns a Model description (metadata) or null if none found.
  353. * DboPostgresのdescribeメソッドを呼び出さずにキャッシュを読み込む為に利用
  354. * Datasource::describe と同じ
  355. *
  356. * @param Model $model
  357. * @return mixed
  358. * @access private
  359. */
  360. function __describe($model) {
  361. if ($this->cacheSources === false) {
  362. return null;
  363. }
  364. $table = $this->fullTableName($model, false);
  365. if (isset($this->__descriptions[$table])) {
  366. return $this->__descriptions[$table];
  367. }
  368. $cache = $this->__cacheDescription($table);
  369. if ($cache !== null) {
  370. $this->__descriptions[$table] =& $cache;
  371. return $cache;
  372. }
  373. return null;
  374. }
  375. }
  376. ?>