thomascube
2006-02-22 745b1466fc76d5ded589e2469328086002430c1c
commit | author | age
c9462d 1 <?php
S 2 // +----------------------------------------------------------------------+
3 // | PHP versions 4 and 5                                                 |
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) 1998-2004 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 // | Author: Paul Cooper <pgc@ucecom.com>                                 |
43 // +----------------------------------------------------------------------+
44 //
45 // $Id$
46
47 require_once 'MDB2/Driver/Reverse/Common.php';
48
49 /**
50  * MDB2 PostGreSQL driver for the schema reverse engineering module
51  *
52  * @package MDB2
53  * @category Database
54  * @author  Paul Cooper <pgc@ucecom.com>
55  */
56 class MDB2_Driver_Reverse_pgsql extends MDB2_Driver_Reverse_Common
57 {
58     // {{{ getTableFieldDefinition()
59
60     /**
61      * get the stucture of a field into an array
62      *
63      * @param string    $table         name of table that should be used in method
64      * @param string    $field_name     name of field that should be used in method
65      * @return mixed data array on success, a MDB2 error on failure
66      * @access public
67      */
68     function getTableFieldDefinition($table, $field_name)
69     {
70         $db =& $this->getDBInstance();
71         if (PEAR::isError($db)) {
72             return $db;
73         }
74
75         $result = $db->loadModule('Datatype');
76         if (PEAR::isError($result)) {
77             return $result;
78         }
79
80         $column = $db->queryRow("SELECT
81                     attnum,attname,typname,attlen,attnotnull,
82                     atttypmod,usename,usesysid,pg_class.oid,relpages,
83                     reltuples,relhaspkey,relhasrules,relacl,adsrc
84                     FROM pg_class,pg_user,pg_type,
85                          pg_attribute left outer join pg_attrdef on
86                          pg_attribute.attrelid=pg_attrdef.adrelid
87                     WHERE (pg_class.relname='$table')
88                         and (pg_class.oid=pg_attribute.attrelid)
89                         and (pg_class.relowner=pg_user.usesysid)
90                         and (pg_attribute.atttypid=pg_type.oid)
91                         and attnum > 0
92                         and attname = '$field_name'
93                         ORDER BY attnum
94                         ", null, MDB2_FETCHMODE_ASSOC);
95         if (PEAR::isError($column)) {
96             return $column;
97         }
98
99         list($types, $length) = $db->datatype->mapNativeDatatype($column);
100         $notnull = false;
101         if (array_key_exists('attnotnull', $column) && $column['attnotnull'] == 't') {
102             $notnull = true;
103         }
104         $default = false;
105         // todo .. check how default look like
106         if (!preg_match("/nextval\('([^']+)'/", $column['adsrc'])
107             && strlen($column['adsrc']) > 2
108         ) {
109             $default = substr($column['adsrc'], 1, -1);
110             if (is_null($default) && $notnull) {
111                 $default = '';
112             }
113         }
114         $autoincrement = false;
115         if (preg_match("/nextval\('([^']+)'/", $column['adsrc'], $nextvals)) {
116             $autoincrement = true;
117         }
118         $definition = array();
119         foreach ($types as $key => $type) {
120             $definition[$key] = array(
121                 'type' => $type,
122                 'notnull' => $notnull,
123             );
124             if ($length > 0) {
125                 $definition[$key]['length'] = $length;
126             }
127             if ($default !== false) {
128                 $definition[$key]['default'] = $default;
129             }
130             if ($autoincrement !== false) {
131                 $definition[$key]['autoincrement'] = $autoincrement;
132             }
133         }
134         return $definition;
135     }
136
137
138     // }}}
139     // {{{ getTableIndexDefinition()
140     /**
141      * get the stucture of an index into an array
142      *
143      * @param string    $table      name of table that should be used in method
144      * @param string    $index_name name of index that should be used in method
145      * @return mixed data array on success, a MDB2 error on failure
146      * @access public
147      */
148     function getTableIndexDefinition($table, $index_name)
149     {
150         $db =& $this->getDBInstance();
151         if (PEAR::isError($db)) {
152             return $db;
153         }
154
155         $query = "SELECT * FROM pg_index, pg_class
156             WHERE (pg_class.relname='$index_name') AND (pg_class.oid=pg_index.indexrelid)";
157         $row = $db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);
158         if (PEAR::isError($row)) {
159             return $row;
160         }
161         if ($row['relname'] != $index_name) {
162             return $db->raiseError(MDB2_ERROR, null, null,
163                 'getTableIndexDefinition: it was not specified an existing table index');
164         }
165
166         $db->loadModule('Manager');
167         $columns = $db->manager->listTableFields($table);
168
169         $definition = array();
170         if ($row['indisunique'] == 't') {
171             $definition['unique'] = true;
172         }
173
174         $index_column_numbers = explode(' ', $row['indkey']);
175
176         foreach ($index_column_numbers as $number) {
177             $definition['fields'][$columns[($number - 1)]] = array('sorting' => 'ascending');
178         }
179         return $definition;
180     }
181
182     // }}}
183     // {{{ tableInfo()
184
185     /**
186      * Returns information about a table or a result set
187      *
188      * NOTE: only supports 'table' and 'flags' if <var>$result</var>
189      * is a table name.
190      *
191      * @param object|string  $result  MDB2_result object from a query or a
192      *                                 string containing the name of a table.
193      *                                 While this also accepts a query result
194      *                                 resource identifier, this behavior is
195      *                                 deprecated.
196      * @param int            $mode    a valid tableInfo mode
197      *
198      * @return array  an associative array with the information requested.
199      *                 A MDB2_Error object on failure.
200      *
201      * @see MDB2_Driver_Common::tableInfo()
202      */
203     function tableInfo($result, $mode = null)
204     {
205         $db =& $this->getDBInstance();
206         if (PEAR::isError($db)) {
207             return $db;
208         }
209
210         if (is_string($result)) {
211             /*
212              * Probably received a table name.
213              * Create a result resource identifier.
214              */
215             $id = $db->_doQuery("SELECT * FROM $result LIMIT 0");
216             if (PEAR::isError($id)) {
217                 return $id;
218             }
219             $got_string = true;
220         } elseif (MDB2::isResultCommon($result)) {
221             /*
222              * Probably received a result object.
223              * Extract the result resource identifier.
224              */
225             $id = $result->getResource();
226             $got_string = false;
227         } else {
228             /*
229              * Probably received a result resource identifier.
230              * Copy it.
231              * Deprecated.  Here for compatibility only.
232              */
233             $id = $result;
234             $got_string = false;
235         }
236
237         if (!is_resource($id)) {
238             return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA);
239         }
240
241         if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
242             if ($db->options['field_case'] == CASE_LOWER) {
243                 $case_func = 'strtolower';
244             } else {
245                 $case_func = 'strtoupper';
246             }
247         } else {
248             $case_func = 'strval';
249         }
250
251         $count = @pg_num_fields($id);
252         $res   = array();
253
254         if ($mode) {
255             $res['num_fields'] = $count;
256         }
257
258         for ($i = 0; $i < $count; $i++) {
259             $res[$i] = array(
260                 'table' => $got_string ? $case_func($result) : '',
261                 'name'  => $case_func(@pg_field_name($id, $i)),
262                 'type'  => @pg_field_type($id, $i),
263                 'len'   => @pg_field_size($id, $i),
264                 'flags' => $got_string
265                            ? $this->_pgFieldFlags($id, $i, $result)
266                            : '',
267             );
268             if ($mode & MDB2_TABLEINFO_ORDER) {
269                 $res['order'][$res[$i]['name']] = $i;
270             }
271             if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
272                 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
273             }
274         }
275
276         // free the result only if we were called on a table
277         if ($got_string) {
278             @pg_free_result($id);
279         }
280         return $res;
281     }
282
283     // }}}
284     // {{{ _pgFieldFlags()
285
286     /**
287      * Get a column's flags
288      *
289      * Supports "not_null", "default_value", "primary_key", "unique_key"
290      * and "multiple_key".  The default value is passed through
291      * rawurlencode() in case there are spaces in it.
292      *
293      * @param int $resource   the PostgreSQL result identifier
294      * @param int $num_field  the field number
295      *
296      * @return string  the flags
297      *
298      * @access protected
299      */
300     function _pgFieldFlags($resource, $num_field, $table_name)
301     {
302         $db =& $this->getDBInstance();
303         if (PEAR::isError($db)) {
304             return $db;
305         }
306
307         $field_name = @pg_field_name($resource, $num_field);
308
309         $result = @pg_query($db->connection, "SELECT f.attnotnull, f.atthasdef
310                                 FROM pg_attribute f, pg_class tab, pg_type typ
311                                 WHERE tab.relname = typ.typname
312                                 AND typ.typrelid = f.attrelid
313                                 AND f.attname = '$field_name'
314                                 AND tab.relname = '$table_name'");
315         if (@pg_num_rows($result) > 0) {
316             $row = @pg_fetch_row($result, 0);
317             $flags  = ($row[0] == 't') ? 'not_null ' : '';
318
319             if ($row[1] == 't') {
320                 $result = @pg_query($db->connection, "SELECT a.adsrc
321                                     FROM pg_attribute f, pg_class tab, pg_type typ, pg_attrdef a
322                                     WHERE tab.relname = typ.typname AND typ.typrelid = f.attrelid
323                                     AND f.attrelid = a.adrelid AND f.attname = '$field_name'
324                                     AND tab.relname = '$table_name' AND f.attnum = a.adnum");
325                 $row = @pg_fetch_row($result, 0);
326                 $num = preg_replace("/'(.*)'::\w+/", "\\1", $row[0]);
327                 $flags.= 'default_' . rawurlencode($num) . ' ';
328             }
329         } else {
330             $flags = '';
331         }
332         $result = @pg_query($db->connection, "SELECT i.indisunique, i.indisprimary, i.indkey
333                                 FROM pg_attribute f, pg_class tab, pg_type typ, pg_index i
334                                 WHERE tab.relname = typ.typname
335                                 AND typ.typrelid = f.attrelid
336                                 AND f.attrelid = i.indrelid
337                                 AND f.attname = '$field_name'
338                                 AND tab.relname = '$table_name'");
339         $count = @pg_num_rows($result);
340
341         for ($i = 0; $i < $count ; $i++) {
342             $row = @pg_fetch_row($result, $i);
343             $keys = explode(' ', $row[2]);
344
345             if (in_array($num_field + 1, $keys)) {
346                 $flags.= ($row[0] == 't' && $row[1] == 'f') ? 'unique_key ' : '';
347                 $flags.= ($row[1] == 't') ? 'primary_key ' : '';
348                 if (count($keys) > 1)
349                     $flags.= 'multiple_key ';
350             }
351         }
352
353         return trim($flags);
354     }
355 }
356 ?>