source: branches/version-2_13-dev/data/module/MDB2/Driver/Reverse/pgsql.php @ 23022

Revision 23022, 24.0 KB checked in by Seasoft, 11 years ago (diff)

#2322 (セッションのGC処理がエラーとなる)

  • Property svn:eol-style set to LF
  • Property svn:mime-type set to text/x-httpd-php; charset=UTF-8
Line 
1<?php
2// +----------------------------------------------------------------------+
3// | PHP versions 4 and 5                                                 |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
6// | Stig. S. Bakken, Lukas Smith                                         |
7// | All rights reserved.                                                 |
8// +----------------------------------------------------------------------+
9// | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
10// | API as well as database abstraction for PHP applications.            |
11// | This LICENSE is in the BSD license style.                            |
12// |                                                                      |
13// | Redistribution and use in source and binary forms, with or without   |
14// | modification, are permitted provided that the following conditions   |
15// | are met:                                                             |
16// |                                                                      |
17// | Redistributions of source code must retain the above copyright       |
18// | notice, this list of conditions and the following disclaimer.        |
19// |                                                                      |
20// | Redistributions in binary form must reproduce the above copyright    |
21// | notice, this list of conditions and the following disclaimer in the  |
22// | documentation and/or other materials provided with the distribution. |
23// |                                                                      |
24// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
25// | Lukas Smith nor the names of his contributors may be used to endorse |
26// | or promote products derived from this software without specific prior|
27// | written permission.                                                  |
28// |                                                                      |
29// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
30// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
31// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
32// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
33// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
34// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
35// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
36// |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
37// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
38// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
39// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
40// | POSSIBILITY OF SUCH DAMAGE.                                          |
41// +----------------------------------------------------------------------+
42// | Authors: Paul Cooper <pgc@ucecom.com>                                |
43// |          Lorenzo Alberton <l.alberton@quipo.it>                      |
44// +----------------------------------------------------------------------+
45//
46// $Id: pgsql.php 327310 2012-08-27 15:16:18Z danielc $
47
48require_once 'MDB2/Driver/Reverse/Common.php';
49
50/**
51 * MDB2 PostGreSQL driver for the schema reverse engineering module
52 *
53 * @package  MDB2
54 * @category Database
55 * @author   Paul Cooper <pgc@ucecom.com>
56 * @author   Lorenzo Alberton <l.alberton@quipo.it>
57 */
58class MDB2_Driver_Reverse_pgsql extends MDB2_Driver_Reverse_Common
59{
60    // {{{ getTableFieldDefinition()
61
62    /**
63     * Get the structure of a field into an array
64     *
65     * @param string $table_name name of table that should be used in method
66     * @param string $field_name name of field that should be used in method
67     * @return mixed data array on success, a MDB2 error on failure
68     * @access public
69     */
70    function getTableFieldDefinition($table_name, $field_name)
71    {
72        $db = $this->getDBInstance();
73        if (MDB2::isError($db)) {
74            return $db;
75        }
76
77        $result = $db->loadModule('Datatype', null, true);
78        if (MDB2::isError($result)) {
79            return $result;
80        }
81
82        list($schema, $table) = $this->splitTableSchema($table_name);
83
84        $query = "SELECT a.attname AS name,
85                         t.typname AS type,
86                         CASE a.attlen
87                           WHEN -1 THEN
88                             CASE t.typname
89                               WHEN 'numeric' THEN (a.atttypmod / 65536)
90                               WHEN 'decimal' THEN (a.atttypmod / 65536)
91                               WHEN 'money'   THEN (a.atttypmod / 65536)
92                               ELSE CASE a.atttypmod
93                                 WHEN -1 THEN NULL
94                                 ELSE a.atttypmod - 4
95                               END
96                             END
97                           ELSE a.attlen
98                         END AS length,
99                         CASE t.typname
100                           WHEN 'numeric' THEN (a.atttypmod % 65536) - 4
101                           WHEN 'decimal' THEN (a.atttypmod % 65536) - 4
102                           WHEN 'money'   THEN (a.atttypmod % 65536) - 4
103                           ELSE 0
104                         END AS scale,
105                         a.attnotnull,
106                         a.atttypmod,
107                         a.atthasdef,
108                         (SELECT substring(pg_get_expr(d.adbin, d.adrelid) for 128)
109                            FROM pg_attrdef d
110                           WHERE d.adrelid = a.attrelid
111                             AND d.adnum = a.attnum
112                             AND a.atthasdef
113                         ) as default
114                    FROM pg_attribute a,
115                         pg_class c,
116                         pg_type t
117                   WHERE c.relname = ".$db->quote($table, 'text')."
118                     AND a.atttypid = t.oid
119                     AND c.oid = a.attrelid
120                     AND NOT a.attisdropped
121                     AND a.attnum > 0
122                     AND a.attname = ".$db->quote($field_name, 'text')."
123                ORDER BY a.attnum";
124        $column = $db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);
125        if (MDB2::isError($column)) {
126            return $column;
127        }
128
129        if (empty($column)) {
130            return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
131                'it was not specified an existing table column', __FUNCTION__);
132        }
133
134        $column = array_change_key_case($column, CASE_LOWER);
135        $mapped_datatype = $db->datatype->mapNativeDatatype($column);
136        if (MDB2::isError($mapped_datatype)) {
137            return $mapped_datatype;
138        }
139        list($types, $length, $unsigned, $fixed) = $mapped_datatype;
140        $notnull = false;
141        if (!empty($column['attnotnull']) && $column['attnotnull'] == 't') {
142            $notnull = true;
143        }
144        $default = null;
145        if ($column['atthasdef'] === 't'
146            && strpos($column['default'], 'NULL') !== 0
147            && !preg_match("/nextval\('([^']+)'/", $column['default'])
148        ) {
149            $pattern = '/^\'(.*)\'::[\w ]+$/i';
150            $default = $column['default'];#substr($column['adsrc'], 1, -1);
151            if ((null === $default) && $notnull) {
152                $default = '';
153            } elseif (!empty($default) && preg_match($pattern, $default)) {
154                //remove data type cast
155                $default = preg_replace ($pattern, '\\1', $default);
156            }
157        }
158        $autoincrement = false;
159        if (preg_match("/nextval\('([^']+)'/", $column['default'], $nextvals)) {
160            $autoincrement = true;
161        }
162        $definition[0] = array('notnull' => $notnull, 'nativetype' => $column['type']);
163        if (null !== $length) {
164            $definition[0]['length'] = $length;
165        }
166        if (null !== $unsigned) {
167            $definition[0]['unsigned'] = $unsigned;
168        }
169        if (null !== $fixed) {
170            $definition[0]['fixed'] = $fixed;
171        }
172        if ($default !== false) {
173            $definition[0]['default'] = $default;
174        }
175        if ($autoincrement !== false) {
176            $definition[0]['autoincrement'] = $autoincrement;
177        }
178        foreach ($types as $key => $type) {
179            $definition[$key] = $definition[0];
180            if ($type == 'clob' || $type == 'blob') {
181                unset($definition[$key]['default']);
182            }
183            $definition[$key]['type'] = $type;
184            $definition[$key]['mdb2type'] = $type;
185        }
186        return $definition;
187    }
188
189    // }}}
190    // {{{ getTableIndexDefinition()
191
192    /**
193     * Get the structure of an index into an array
194     *
195     * @param string $table_name name of table that should be used in method
196     * @param string $index_name name of index that should be used in method
197     * @return mixed data array on success, a MDB2 error on failure
198     * @access public
199     */
200    function getTableIndexDefinition($table_name, $index_name)
201    {
202        $db = $this->getDBInstance();
203        if (MDB2::isError($db)) {
204            return $db;
205        }
206       
207        list($schema, $table) = $this->splitTableSchema($table_name);
208
209        $query = 'SELECT relname, indkey FROM pg_index, pg_class';
210        $query.= ' WHERE pg_class.oid = pg_index.indexrelid';
211        $query.= " AND indisunique != 't' AND indisprimary != 't'";
212        $query.= ' AND pg_class.relname = %s';
213        $index_name_mdb2 = $db->getIndexName($index_name);
214        $row = $db->queryRow(sprintf($query, $db->quote($index_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
215        if (MDB2::isError($row) || empty($row)) {
216            // fallback to the given $index_name, without transformation
217            $row = $db->queryRow(sprintf($query, $db->quote($index_name, 'text')), null, MDB2_FETCHMODE_ASSOC);
218        }
219        if (MDB2::isError($row)) {
220            return $row;
221        }
222
223        if (empty($row)) {
224            return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
225                'it was not specified an existing table index', __FUNCTION__);
226        }
227
228        $row = array_change_key_case($row, CASE_LOWER);
229
230        $db->loadModule('Manager', null, true);
231        $columns = $db->manager->listTableFields($table_name);
232
233        $definition = array();
234
235        $index_column_numbers = explode(' ', $row['indkey']);
236
237        $colpos = 1;
238        foreach ($index_column_numbers as $number) {
239            $definition['fields'][$columns[($number - 1)]] = array(
240                'position' => $colpos++,
241                'sorting' => 'ascending',
242            );
243        }
244        return $definition;
245    }
246
247    // }}}
248    // {{{ getTableConstraintDefinition()
249
250    /**
251     * Get the structure of a constraint into an array
252     *
253     * @param string $table_name      name of table that should be used in method
254     * @param string $constraint_name name of constraint that should be used in method
255     * @return mixed data array on success, a MDB2 error on failure
256     * @access public
257     */
258    function getTableConstraintDefinition($table_name, $constraint_name)
259    {
260        $db = $this->getDBInstance();
261        if (MDB2::isError($db)) {
262            return $db;
263        }
264       
265        list($schema, $table) = $this->splitTableSchema($table_name);
266
267        $query = "SELECT c.oid,
268                         c.conname AS constraint_name,
269                         CASE WHEN c.contype = 'c' THEN 1 ELSE 0 END AS \"check\",
270                         CASE WHEN c.contype = 'f' THEN 1 ELSE 0 END AS \"foreign\",
271                         CASE WHEN c.contype = 'p' THEN 1 ELSE 0 END AS \"primary\",
272                         CASE WHEN c.contype = 'u' THEN 1 ELSE 0 END AS \"unique\",
273                         CASE WHEN c.condeferrable = 'f' THEN 0 ELSE 1 END AS deferrable,
274                         CASE WHEN c.condeferred = 'f' THEN 0 ELSE 1 END AS initiallydeferred,
275                         --array_to_string(c.conkey, ' ') AS constraint_key,
276                         t.relname AS table_name,
277                         t2.relname AS references_table,
278                         CASE confupdtype
279                           WHEN 'a' THEN 'NO ACTION'
280                           WHEN 'r' THEN 'RESTRICT'
281                           WHEN 'c' THEN 'CASCADE'
282                           WHEN 'n' THEN 'SET NULL'
283                           WHEN 'd' THEN 'SET DEFAULT'
284                         END AS onupdate,
285                         CASE confdeltype
286                           WHEN 'a' THEN 'NO ACTION'
287                           WHEN 'r' THEN 'RESTRICT'
288                           WHEN 'c' THEN 'CASCADE'
289                           WHEN 'n' THEN 'SET NULL'
290                           WHEN 'd' THEN 'SET DEFAULT'
291                         END AS ondelete,
292                         CASE confmatchtype
293                           WHEN 'u' THEN 'UNSPECIFIED'
294                           WHEN 'f' THEN 'FULL'
295                           WHEN 'p' THEN 'PARTIAL'
296                         END AS match,
297                         --array_to_string(c.confkey, ' ') AS fk_constraint_key,
298                         consrc
299                    FROM pg_constraint c
300               LEFT JOIN pg_class t  ON c.conrelid  = t.oid
301               LEFT JOIN pg_class t2 ON c.confrelid = t2.oid
302                   WHERE c.conname = %s
303                     AND t.relname = " . $db->quote($table, 'text');
304        $constraint_name_mdb2 = $db->getIndexName($constraint_name);
305        $row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
306        if (MDB2::isError($row) || empty($row)) {
307            // fallback to the given $index_name, without transformation
308            $constraint_name_mdb2 = $constraint_name;
309            $row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
310        }
311        if (MDB2::isError($row)) {
312            return $row;
313        }
314        $uniqueIndex = false;
315        if (empty($row)) {
316            // We might be looking for a UNIQUE index that was not created
317            // as a constraint but should be treated as such.
318            $query = 'SELECT relname AS constraint_name,
319                             indkey,
320                             0 AS "check",
321                             0 AS "foreign",
322                             0 AS "primary",
323                             1 AS "unique",
324                             0 AS deferrable,
325                             0 AS initiallydeferred,
326                             NULL AS references_table,
327                             NULL AS onupdate,
328                             NULL AS ondelete,
329                             NULL AS match
330                        FROM pg_index, pg_class
331                       WHERE pg_class.oid = pg_index.indexrelid
332                         AND indisunique = \'t\'
333                         AND pg_class.relname = %s';
334            $constraint_name_mdb2 = $db->getIndexName($constraint_name);
335            $row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
336            if (MDB2::isError($row) || empty($row)) {
337                // fallback to the given $index_name, without transformation
338                $constraint_name_mdb2 = $constraint_name;
339                $row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
340            }
341            if (MDB2::isError($row)) {
342                return $row;
343            }
344            if (empty($row)) {
345                return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
346                    $constraint_name . ' is not an existing table constraint', __FUNCTION__);
347            }
348            $uniqueIndex = true;
349        }
350
351        $row = array_change_key_case($row, CASE_LOWER);
352
353        $definition = array(
354            'primary' => (boolean)$row['primary'],
355            'unique'  => (boolean)$row['unique'],
356            'foreign' => (boolean)$row['foreign'],
357            'check'   => (boolean)$row['check'],
358            'fields'  => array(),
359            'references' => array(
360                'table'  => $row['references_table'],
361                'fields' => array(),
362            ),
363            'deferrable' => (boolean)$row['deferrable'],
364            'initiallydeferred' => (boolean)$row['initiallydeferred'],
365            'onupdate' => $row['onupdate'],
366            'ondelete' => $row['ondelete'],
367            'match'    => $row['match'],
368        );
369
370        if ($uniqueIndex) {
371            $db->loadModule('Manager', null, true);
372            $columns = $db->manager->listTableFields($table_name);
373            $index_column_numbers = explode(' ', $row['indkey']);
374            $colpos = 1;
375            foreach ($index_column_numbers as $number) {
376                $definition['fields'][$columns[($number - 1)]] = array(
377                    'position' => $colpos++,
378                    'sorting'  => 'ascending',
379                );
380            }
381            return $definition;
382        }
383
384        $query = 'SELECT a.attname
385                    FROM pg_constraint c
386               LEFT JOIN pg_class t  ON c.conrelid  = t.oid
387               LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(c.conkey)
388                   WHERE c.conname = %s
389                     AND t.relname = ' . $db->quote($table, 'text');
390        $fields = $db->queryCol(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null);
391        if (MDB2::isError($fields)) {
392            return $fields;
393        }
394        $colpos = 1;
395        foreach ($fields as $field) {
396            $definition['fields'][$field] = array(
397                'position' => $colpos++,
398                'sorting' => 'ascending',
399            );
400        }
401       
402        if ($definition['foreign']) {
403            $query = 'SELECT a.attname
404                        FROM pg_constraint c
405                   LEFT JOIN pg_class t  ON c.confrelid  = t.oid
406                   LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(c.confkey)
407                       WHERE c.conname = %s
408                         AND t.relname = ' . $db->quote($definition['references']['table'], 'text');
409            $foreign_fields = $db->queryCol(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null);
410            if (MDB2::isError($foreign_fields)) {
411                return $foreign_fields;
412            }
413            $colpos = 1;
414            foreach ($foreign_fields as $foreign_field) {
415                $definition['references']['fields'][$foreign_field] = array(
416                    'position' => $colpos++,
417                );
418            }
419        }
420       
421        if ($definition['check']) {
422            $check_def = $db->queryOne("SELECT pg_get_constraintdef(" . $row['oid'] . ", 't')");
423            // ...
424        }
425        return $definition;
426    }
427
428    // }}}
429    // {{{ getTriggerDefinition()
430
431    /**
432     * Get the structure of a trigger into an array
433     *
434     * EXPERIMENTAL
435     *
436     * WARNING: this function is experimental and may change the returned value
437     * at any time until labelled as non-experimental
438     *
439     * @param string $trigger name of trigger that should be used in method
440     * @return mixed data array on success, a MDB2 error on failure
441     * @access public
442     *
443     * @TODO: add support for plsql functions and functions with args
444     */
445    function getTriggerDefinition($trigger)
446    {
447        $db = $this->getDBInstance();
448        if (MDB2::isError($db)) {
449            return $db;
450        }
451
452        $query = "SELECT trg.tgname AS trigger_name,
453                         tbl.relname AS table_name,
454                         CASE
455                            WHEN p.proname IS NOT NULL THEN 'EXECUTE PROCEDURE ' || p.proname || '();'
456                            ELSE ''
457                         END AS trigger_body,
458                         CASE trg.tgtype & cast(2 as int2)
459                            WHEN 0 THEN 'AFTER'
460                            ELSE 'BEFORE'
461                         END AS trigger_type,
462                         CASE trg.tgtype & cast(28 as int2)
463                            WHEN 16 THEN 'UPDATE'
464                            WHEN 8 THEN 'DELETE'
465                            WHEN 4 THEN 'INSERT'
466                            WHEN 20 THEN 'INSERT, UPDATE'
467                            WHEN 28 THEN 'INSERT, UPDATE, DELETE'
468                            WHEN 24 THEN 'UPDATE, DELETE'
469                            WHEN 12 THEN 'INSERT, DELETE'
470                         END AS trigger_event,
471                         CASE trg.tgenabled
472                            WHEN 'O' THEN 't'
473                            ELSE trg.tgenabled
474                         END AS trigger_enabled,
475                         obj_description(trg.oid, 'pg_trigger') AS trigger_comment
476                    FROM pg_trigger trg,
477                         pg_class tbl,
478                         pg_proc p
479                   WHERE trg.tgrelid = tbl.oid
480                     AND trg.tgfoid = p.oid
481                     AND trg.tgname = ". $db->quote($trigger, 'text');
482        $types = array(
483            'trigger_name'    => 'text',
484            'table_name'      => 'text',
485            'trigger_body'    => 'text',
486            'trigger_type'    => 'text',
487            'trigger_event'   => 'text',
488            'trigger_comment' => 'text',
489            'trigger_enabled' => 'boolean',
490        );
491        return $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
492    }
493   
494    // }}}
495    // {{{ tableInfo()
496
497    /**
498     * Returns information about a table or a result set
499     *
500     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
501     * is a table name.
502     *
503     * @param object|string  $result  MDB2_result object from a query or a
504     *                                 string containing the name of a table.
505     *                                 While this also accepts a query result
506     *                                 resource identifier, this behavior is
507     *                                 deprecated.
508     * @param int            $mode    a valid tableInfo mode
509     *
510     * @return array  an associative array with the information requested.
511     *                 A MDB2_Error object on failure.
512     *
513     * @see MDB2_Driver_Common::tableInfo()
514     */
515    function tableInfo($result, $mode = null)
516    {
517        if (is_string($result)) {
518           return parent::tableInfo($result, $mode);
519        }
520
521        $db = $this->getDBInstance();
522        if (MDB2::isError($db)) {
523            return $db;
524        }
525
526        $resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
527        if (!is_resource($resource)) {
528            return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
529                'Could not generate result resource', __FUNCTION__);
530        }
531
532        if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
533            if ($db->options['field_case'] == CASE_LOWER) {
534                $case_func = 'strtolower';
535            } else {
536                $case_func = 'strtoupper';
537            }
538        } else {
539            $case_func = 'strval';
540        }
541
542        $count = @pg_num_fields($resource);
543        $res   = array();
544
545        if ($mode) {
546            $res['num_fields'] = $count;
547        }
548
549        $db->loadModule('Datatype', null, true);
550        for ($i = 0; $i < $count; $i++) {
551            $res[$i] = array(
552                'table' => function_exists('pg_field_table') ? @pg_field_table($resource, $i) : '',
553                'name'  => $case_func(@pg_field_name($resource, $i)),
554                'type'  => @pg_field_type($resource, $i),
555                'length' => @pg_field_size($resource, $i),
556                'flags' => '',
557            );
558            $mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
559            if (MDB2::isError($mdb2type_info)) {
560               return $mdb2type_info;
561            }
562            $res[$i]['mdb2type'] = $mdb2type_info[0][0];
563            if ($mode & MDB2_TABLEINFO_ORDER) {
564                $res['order'][$res[$i]['name']] = $i;
565            }
566            if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
567                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
568            }
569        }
570
571        return $res;
572    }
573}
574?>
Note: See TracBrowser for help on using the repository browser.