|
1 <?php |
|
2 |
|
3 /* |
|
4 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
5 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
6 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
7 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
8 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
9 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
10 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
11 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
12 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
13 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
14 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
15 * |
|
16 * This software consists of voluntary contributions made by many individuals |
|
17 * and is licensed under the LGPL. For more information, see |
|
18 * <http://www.doctrine-project.org>. |
|
19 */ |
|
20 |
|
21 namespace Doctrine\DBAL\Schema; |
|
22 |
|
23 /** |
|
24 * xxx |
|
25 * |
|
26 * @license http://www.opensource.org/licenses/lgpl-license.php LGPL |
|
27 * @author Konsta Vesterinen <kvesteri@cc.hut.fi> |
|
28 * @author Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library) |
|
29 * @author Benjamin Eberlei <kontakt@beberlei.de> |
|
30 * @version $Revision$ |
|
31 * @since 2.0 |
|
32 */ |
|
33 class PostgreSqlSchemaManager extends AbstractSchemaManager |
|
34 { |
|
35 |
|
36 protected function _getPortableTableForeignKeyDefinition($tableForeignKey) |
|
37 { |
|
38 $onUpdate = null; |
|
39 $onDelete = null; |
|
40 |
|
41 if (preg_match('(ON UPDATE ([a-zA-Z0-9]+))', $tableForeignKey['condef'], $match)) { |
|
42 $onUpdate = $match[1]; |
|
43 } |
|
44 if (preg_match('(ON DELETE ([a-zA-Z0-9]+))', $tableForeignKey['condef'], $match)) { |
|
45 $onDelete = $match[1]; |
|
46 } |
|
47 |
|
48 if (preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values)) { |
|
49 // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get |
|
50 // the idea to trim them here. |
|
51 $localColumns = array_map('trim', explode(",", $values[1])); |
|
52 $foreignColumns = array_map('trim', explode(",", $values[3])); |
|
53 $foreignTable = $values[2]; |
|
54 } |
|
55 |
|
56 return new ForeignKeyConstraint( |
|
57 $localColumns, $foreignTable, $foreignColumns, $tableForeignKey['conname'], |
|
58 array('onUpdate' => $onUpdate, 'onDelete' => $onDelete) |
|
59 ); |
|
60 } |
|
61 |
|
62 public function dropDatabase($database) |
|
63 { |
|
64 $params = $this->_conn->getParams(); |
|
65 $params["dbname"] = "postgres"; |
|
66 $tmpPlatform = $this->_platform; |
|
67 $tmpConn = $this->_conn; |
|
68 |
|
69 $this->_conn = \Doctrine\DBAL\DriverManager::getConnection($params); |
|
70 $this->_platform = $this->_conn->getDatabasePlatform(); |
|
71 |
|
72 parent::dropDatabase($database); |
|
73 |
|
74 $this->_platform = $tmpPlatform; |
|
75 $this->_conn = $tmpConn; |
|
76 } |
|
77 |
|
78 public function createDatabase($database) |
|
79 { |
|
80 $params = $this->_conn->getParams(); |
|
81 $params["dbname"] = "postgres"; |
|
82 $tmpPlatform = $this->_platform; |
|
83 $tmpConn = $this->_conn; |
|
84 |
|
85 $this->_conn = \Doctrine\DBAL\DriverManager::getConnection($params); |
|
86 $this->_platform = $this->_conn->getDatabasePlatform(); |
|
87 |
|
88 parent::createDatabase($database); |
|
89 |
|
90 $this->_platform = $tmpPlatform; |
|
91 $this->_conn = $tmpConn; |
|
92 } |
|
93 |
|
94 protected function _getPortableTriggerDefinition($trigger) |
|
95 { |
|
96 return $trigger['trigger_name']; |
|
97 } |
|
98 |
|
99 protected function _getPortableViewDefinition($view) |
|
100 { |
|
101 return new View($view['viewname'], $view['definition']); |
|
102 } |
|
103 |
|
104 protected function _getPortableUserDefinition($user) |
|
105 { |
|
106 return array( |
|
107 'user' => $user['usename'], |
|
108 'password' => $user['passwd'] |
|
109 ); |
|
110 } |
|
111 |
|
112 protected function _getPortableTableDefinition($table) |
|
113 { |
|
114 if ($table['schema_name'] == 'public') { |
|
115 return $table['table_name']; |
|
116 } else { |
|
117 return $table['schema_name'] . "." . $table['table_name']; |
|
118 } |
|
119 } |
|
120 |
|
121 /** |
|
122 * @license New BSD License |
|
123 * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html |
|
124 * @param array $tableIndexes |
|
125 * @param string $tableName |
|
126 * @return array |
|
127 */ |
|
128 protected function _getPortableTableIndexesList($tableIndexes, $tableName=null) |
|
129 { |
|
130 $buffer = array(); |
|
131 foreach ($tableIndexes AS $row) { |
|
132 $colNumbers = explode(' ', $row['indkey']); |
|
133 $colNumbersSql = 'IN (' . join(' ,', $colNumbers) . ' )'; |
|
134 $columnNameSql = "SELECT attnum, attname FROM pg_attribute |
|
135 WHERE attrelid={$row['indrelid']} AND attnum $colNumbersSql ORDER BY attnum ASC;"; |
|
136 |
|
137 $stmt = $this->_conn->executeQuery($columnNameSql); |
|
138 $indexColumns = $stmt->fetchAll(); |
|
139 |
|
140 // required for getting the order of the columns right. |
|
141 foreach ($colNumbers AS $colNum) { |
|
142 foreach ($indexColumns as $colRow) { |
|
143 if ($colNum == $colRow['attnum']) { |
|
144 $buffer[] = array( |
|
145 'key_name' => $row['relname'], |
|
146 'column_name' => trim($colRow['attname']), |
|
147 'non_unique' => !$row['indisunique'], |
|
148 'primary' => $row['indisprimary'] |
|
149 ); |
|
150 } |
|
151 } |
|
152 } |
|
153 } |
|
154 return parent::_getPortableTableIndexesList($buffer); |
|
155 } |
|
156 |
|
157 protected function _getPortableDatabaseDefinition($database) |
|
158 { |
|
159 return $database['datname']; |
|
160 } |
|
161 |
|
162 protected function _getPortableSequenceDefinition($sequence) |
|
163 { |
|
164 if ($sequence['schemaname'] != 'public') { |
|
165 $sequenceName = $sequence['schemaname'] . "." . $sequence['relname']; |
|
166 } else { |
|
167 $sequenceName = $sequence['relname']; |
|
168 } |
|
169 |
|
170 $data = $this->_conn->fetchAll('SELECT min_value, increment_by FROM ' . $sequenceName); |
|
171 return new Sequence($sequenceName, $data[0]['increment_by'], $data[0]['min_value']); |
|
172 } |
|
173 |
|
174 protected function _getPortableTableColumnDefinition($tableColumn) |
|
175 { |
|
176 $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); |
|
177 |
|
178 if (strtolower($tableColumn['type']) === 'varchar') { |
|
179 // get length from varchar definition |
|
180 $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $tableColumn['complete_type']); |
|
181 $tableColumn['length'] = $length; |
|
182 } |
|
183 |
|
184 $matches = array(); |
|
185 |
|
186 $autoincrement = false; |
|
187 if (preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches)) { |
|
188 $tableColumn['sequence'] = $matches[1]; |
|
189 $tableColumn['default'] = null; |
|
190 $autoincrement = true; |
|
191 } |
|
192 |
|
193 if (stripos($tableColumn['default'], 'NULL') === 0) { |
|
194 $tableColumn['default'] = null; |
|
195 } |
|
196 |
|
197 $length = (isset($tableColumn['length'])) ? $tableColumn['length'] : null; |
|
198 if ($length == '-1' && isset($tableColumn['atttypmod'])) { |
|
199 $length = $tableColumn['atttypmod'] - 4; |
|
200 } |
|
201 if ((int) $length <= 0) { |
|
202 $length = null; |
|
203 } |
|
204 $fixed = null; |
|
205 |
|
206 if (!isset($tableColumn['name'])) { |
|
207 $tableColumn['name'] = ''; |
|
208 } |
|
209 |
|
210 $precision = null; |
|
211 $scale = null; |
|
212 |
|
213 if ($this->_platform->hasDoctrineTypeMappingFor($tableColumn['type'])) { |
|
214 $dbType = strtolower($tableColumn['type']); |
|
215 } else { |
|
216 $dbType = strtolower($tableColumn['domain_type']); |
|
217 $tableColumn['complete_type'] = $tableColumn['domain_complete_type']; |
|
218 } |
|
219 |
|
220 $type = $this->_platform->getDoctrineTypeMapping($dbType); |
|
221 $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type); |
|
222 $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type); |
|
223 |
|
224 switch ($dbType) { |
|
225 case 'smallint': |
|
226 case 'int2': |
|
227 $length = null; |
|
228 break; |
|
229 case 'int': |
|
230 case 'int4': |
|
231 case 'integer': |
|
232 $length = null; |
|
233 break; |
|
234 case 'bigint': |
|
235 case 'int8': |
|
236 $length = null; |
|
237 break; |
|
238 case 'bool': |
|
239 case 'boolean': |
|
240 $length = null; |
|
241 break; |
|
242 case 'text': |
|
243 $fixed = false; |
|
244 break; |
|
245 case 'varchar': |
|
246 case 'interval': |
|
247 case '_varchar': |
|
248 $fixed = false; |
|
249 break; |
|
250 case 'char': |
|
251 case 'bpchar': |
|
252 $fixed = true; |
|
253 break; |
|
254 case 'float': |
|
255 case 'float4': |
|
256 case 'float8': |
|
257 case 'double': |
|
258 case 'double precision': |
|
259 case 'real': |
|
260 case 'decimal': |
|
261 case 'money': |
|
262 case 'numeric': |
|
263 if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['complete_type'], $match)) { |
|
264 $precision = $match[1]; |
|
265 $scale = $match[2]; |
|
266 $length = null; |
|
267 } |
|
268 break; |
|
269 case 'year': |
|
270 $length = null; |
|
271 break; |
|
272 } |
|
273 |
|
274 $options = array( |
|
275 'length' => $length, |
|
276 'notnull' => (bool) $tableColumn['isnotnull'], |
|
277 'default' => $tableColumn['default'], |
|
278 'primary' => (bool) ($tableColumn['pri'] == 't'), |
|
279 'precision' => $precision, |
|
280 'scale' => $scale, |
|
281 'fixed' => $fixed, |
|
282 'unsigned' => false, |
|
283 'autoincrement' => $autoincrement, |
|
284 'comment' => $tableColumn['comment'], |
|
285 ); |
|
286 |
|
287 return new Column($tableColumn['field'], \Doctrine\DBAL\Types\Type::getType($type), $options); |
|
288 } |
|
289 |
|
290 } |