source: pro-violet-viettel/sourcecode/application/libraries/Doctrine/DBAL/Schema/PostgreSqlSchemaManager.php @ 345

Last change on this file since 345 was 345, checked in by quyenla, 11 years ago

collaborator page

File size: 11.9 KB
Line 
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
21namespace Doctrine\DBAL\Schema;
22
23/**
24 * PostgreSQL Schema Manager
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 * @since       2.0
31 */
32class PostgreSqlSchemaManager extends AbstractSchemaManager
33{
34    /**
35     * @var array
36     */
37    private $existingSchemaPaths;
38
39    /**
40     * Get all the existing schema names.
41     *
42     * @return array
43     */
44    public function getSchemaNames()
45    {
46        $rows = $this->_conn->fetchAll('SELECT schema_name FROM information_schema.schemata');
47        return array_map(function($v) { return $v['schema_name']; }, $rows);
48    }
49
50    /**
51     * Return an array of schema search paths
52     *
53     * This is a PostgreSQL only function.
54     *
55     * @return array
56     */
57    public function getSchemaSearchPaths()
58    {
59        $params = $this->_conn->getParams();
60        $schema = explode(",", $this->_conn->fetchColumn('SHOW search_path'));
61        if (isset($params['user'])) {
62            $schema = str_replace('"$user"', $params['user'], $schema);
63        }
64        return $schema;
65    }
66
67    /**
68     * Get names of all existing schemas in the current users search path.
69     *
70     * This is a PostgreSQL only function.
71     *
72     * @return array
73     */
74    public function getExistingSchemaSearchPaths()
75    {
76        if ($this->existingSchemaPaths === null) {
77            $this->determineExistingSchemaSearchPaths();
78        }
79        return $this->existingSchemaPaths;
80    }
81
82    /**
83     * Use this to set or reset the order of the existing schemas in the current search path of the user
84     *
85     * This is a PostgreSQL only function.
86     *
87     * @return type
88     */
89    public function determineExistingSchemaSearchPaths()
90    {
91        $names = $this->getSchemaNames();
92        $paths = $this->getSchemaSearchPaths();
93
94        $this->existingSchemaPaths = array_filter($paths, function ($v) use ($names) {
95            return in_array($v, $names);
96        });
97    }
98
99    protected function _getPortableTableForeignKeyDefinition($tableForeignKey)
100    {
101        $onUpdate = null;
102        $onDelete = null;
103
104        if (preg_match('(ON UPDATE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) {
105            $onUpdate = $match[1];
106        }
107        if (preg_match('(ON DELETE ([a-zA-Z0-9]+( (NULL|ACTION|DEFAULT))?))', $tableForeignKey['condef'], $match)) {
108            $onDelete = $match[1];
109        }
110
111        if (preg_match('/FOREIGN KEY \((.+)\) REFERENCES (.+)\((.+)\)/', $tableForeignKey['condef'], $values)) {
112            // PostgreSQL returns identifiers that are keywords with quotes, we need them later, don't get
113            // the idea to trim them here.
114            $localColumns = array_map('trim', explode(",", $values[1]));
115            $foreignColumns = array_map('trim', explode(",", $values[3]));
116            $foreignTable = $values[2];
117        }
118
119        return new ForeignKeyConstraint(
120                $localColumns, $foreignTable, $foreignColumns, $tableForeignKey['conname'],
121                array('onUpdate' => $onUpdate, 'onDelete' => $onDelete)
122        );
123    }
124
125    public function dropDatabase($database)
126    {
127        $params = $this->_conn->getParams();
128        $params["dbname"] = "postgres";
129        $tmpPlatform = $this->_platform;
130        $tmpConn = $this->_conn;
131
132        $this->_conn = \Doctrine\DBAL\DriverManager::getConnection($params);
133        $this->_platform = $this->_conn->getDatabasePlatform();
134
135        parent::dropDatabase($database);
136
137        $this->_platform = $tmpPlatform;
138        $this->_conn = $tmpConn;
139    }
140
141    public function createDatabase($database)
142    {
143        $params = $this->_conn->getParams();
144        $params["dbname"] = "postgres";
145        $tmpPlatform = $this->_platform;
146        $tmpConn = $this->_conn;
147
148        $this->_conn = \Doctrine\DBAL\DriverManager::getConnection($params);
149        $this->_platform = $this->_conn->getDatabasePlatform();
150
151        parent::createDatabase($database);
152
153        $this->_platform = $tmpPlatform;
154        $this->_conn = $tmpConn;
155    }
156
157    protected function _getPortableTriggerDefinition($trigger)
158    {
159        return $trigger['trigger_name'];
160    }
161
162    protected function _getPortableViewDefinition($view)
163    {
164        return new View($view['viewname'], $view['definition']);
165    }
166
167    protected function _getPortableUserDefinition($user)
168    {
169        return array(
170            'user' => $user['usename'],
171            'password' => $user['passwd']
172        );
173    }
174
175    protected function _getPortableTableDefinition($table)
176    {
177        $schemas = $this->getExistingSchemaSearchPaths();
178        $firstSchema = array_shift($schemas);
179
180        if ($table['schema_name'] == $firstSchema) {
181            return $table['table_name'];
182        } else {
183            return $table['schema_name'] . "." . $table['table_name'];
184        }
185    }
186
187    /**
188     * @license New BSD License
189     * @link http://ezcomponents.org/docs/api/trunk/DatabaseSchema/ezcDbSchemaPgsqlReader.html
190     * @param  array $tableIndexes
191     * @param  string $tableName
192     * @return array
193     */
194    protected function _getPortableTableIndexesList($tableIndexes, $tableName=null)
195    {
196        $buffer = array();
197        foreach ($tableIndexes AS $row) {
198            $colNumbers = explode(' ', $row['indkey']);
199            $colNumbersSql = 'IN (' . join(' ,', $colNumbers) . ' )';
200            $columnNameSql = "SELECT attnum, attname FROM pg_attribute
201                WHERE attrelid={$row['indrelid']} AND attnum $colNumbersSql ORDER BY attnum ASC;";
202
203            $stmt = $this->_conn->executeQuery($columnNameSql);
204            $indexColumns = $stmt->fetchAll();
205
206            // required for getting the order of the columns right.
207            foreach ($colNumbers AS $colNum) {
208                foreach ($indexColumns as $colRow) {
209                    if ($colNum == $colRow['attnum']) {
210                        $buffer[] = array(
211                            'key_name' => $row['relname'],
212                            'column_name' => trim($colRow['attname']),
213                            'non_unique' => !$row['indisunique'],
214                            'primary' => $row['indisprimary']
215                        );
216                    }
217                }
218            }
219        }
220        return parent::_getPortableTableIndexesList($buffer, $tableName);
221    }
222
223    protected function _getPortableDatabaseDefinition($database)
224    {
225        return $database['datname'];
226    }
227
228    protected function _getPortableSequenceDefinition($sequence)
229    {
230        if ($sequence['schemaname'] != 'public') {
231            $sequenceName = $sequence['schemaname'] . "." . $sequence['relname'];
232        } else {
233            $sequenceName = $sequence['relname'];
234        }
235
236        $data = $this->_conn->fetchAll('SELECT min_value, increment_by FROM ' . $sequenceName);
237        return new Sequence($sequenceName, $data[0]['increment_by'], $data[0]['min_value']);
238    }
239
240    protected function _getPortableTableColumnDefinition($tableColumn)
241    {
242        $tableColumn = array_change_key_case($tableColumn, CASE_LOWER);
243
244        if (strtolower($tableColumn['type']) === 'varchar') {
245            // get length from varchar definition
246            $length = preg_replace('~.*\(([0-9]*)\).*~', '$1', $tableColumn['complete_type']);
247            $tableColumn['length'] = $length;
248        }
249
250        $matches = array();
251
252        $autoincrement = false;
253        if (preg_match("/^nextval\('(.*)'(::.*)?\)$/", $tableColumn['default'], $matches)) {
254            $tableColumn['sequence'] = $matches[1];
255            $tableColumn['default'] = null;
256            $autoincrement = true;
257        }
258
259        if (stripos($tableColumn['default'], 'NULL') === 0) {
260            $tableColumn['default'] = null;
261        }
262
263        $length = (isset($tableColumn['length'])) ? $tableColumn['length'] : null;
264        if ($length == '-1' && isset($tableColumn['atttypmod'])) {
265            $length = $tableColumn['atttypmod'] - 4;
266        }
267        if ((int) $length <= 0) {
268            $length = null;
269        }
270        $fixed = null;
271
272        if (!isset($tableColumn['name'])) {
273            $tableColumn['name'] = '';
274        }
275
276        $precision = null;
277        $scale = null;
278
279        $dbType = strtolower($tableColumn['type']);
280        if (strlen($tableColumn['domain_type']) && !$this->_platform->hasDoctrineTypeMappingFor($tableColumn['type'])) {
281            $dbType = strtolower($tableColumn['domain_type']);
282            $tableColumn['complete_type'] = $tableColumn['domain_complete_type'];
283        }
284
285        $type = $this->_platform->getDoctrineTypeMapping($dbType);
286        $type = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);
287        $tableColumn['comment'] = $this->removeDoctrineTypeFromComment($tableColumn['comment'], $type);
288
289        switch ($dbType) {
290            case 'smallint':
291            case 'int2':
292                $length = null;
293                break;
294            case 'int':
295            case 'int4':
296            case 'integer':
297                $length = null;
298                break;
299            case 'bigint':
300            case 'int8':
301                $length = null;
302                break;
303            case 'bool':
304            case 'boolean':
305                $length = null;
306                break;
307            case 'text':
308                $fixed = false;
309                break;
310            case 'varchar':
311            case 'interval':
312            case '_varchar':
313                $fixed = false;
314                break;
315            case 'char':
316            case 'bpchar':
317                $fixed = true;
318                break;
319            case 'float':
320            case 'float4':
321            case 'float8':
322            case 'double':
323            case 'double precision':
324            case 'real':
325            case 'decimal':
326            case 'money':
327            case 'numeric':
328                if (preg_match('([A-Za-z]+\(([0-9]+)\,([0-9]+)\))', $tableColumn['complete_type'], $match)) {
329                    $precision = $match[1];
330                    $scale = $match[2];
331                    $length = null;
332                }
333                break;
334            case 'year':
335                $length = null;
336                break;
337        }
338
339        if ($tableColumn['default'] && preg_match("('([^']+)'::)", $tableColumn['default'], $match)) {
340            $tableColumn['default'] = $match[1];
341        }
342
343        $options = array(
344            'length'        => $length,
345            'notnull'       => (bool) $tableColumn['isnotnull'],
346            'default'       => $tableColumn['default'],
347            'primary'       => (bool) ($tableColumn['pri'] == 't'),
348            'precision'     => $precision,
349            'scale'         => $scale,
350            'fixed'         => $fixed,
351            'unsigned'      => false,
352            'autoincrement' => $autoincrement,
353            'comment'       => $tableColumn['comment'],
354        );
355
356        return new Column($tableColumn['field'], \Doctrine\DBAL\Types\Type::getType($type), $options);
357    }
358
359}
Note: See TracBrowser for help on using the repository browser.