~ubuntu-branches/ubuntu/breezy/moodle/breezy

« back to all changes in this revision

Viewing changes to lib/adodb/datadict/datadict-postgres.inc.php

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Mitchell
  • Date: 2005-10-13 02:00:59 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051013020059-y2qcyo41t7nqppcg
Tags: 1.5.2-1ubuntu1
* Resync with debian (security update)
* changed dependencys to php5
* changed apache dependency to apache2 
* References
  CAN-2005-2247

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
<?php
2
2
 
3
3
/**
4
 
  V4.51 29 July 2004  (c) 2000-2004 John Lim (jlim@natsoft.com.my). All rights reserved.
 
4
  V4.60 24 Jan 2005  (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.
5
5
  Released under both BSD license and Lesser GPL library license. 
6
6
  Whenever there is any discrepancy between the two licenses, 
7
7
  the BSD license will take precedence.
20
20
        var $seqPrefix = 'SEQ_';
21
21
        var $addCol = ' ADD COLUMN';
22
22
        var $quote = '"';
 
23
        var $renameTable = 'ALTER TABLE %s RENAME TO %s'; // at least since 7.1
23
24
        
24
25
        function MetaType($t,$len=-1,$fieldobj=false)
25
26
        {
28
29
                        $t = $fieldobj->type;
29
30
                        $len = $fieldobj->max_length;
30
31
                }
 
32
                $is_serial = is_object($fieldobj) && $fieldobj->primary_key && $fieldobj->unique && 
 
33
                        $fieldobj->has_default && substr($fieldobj->default_value,0,8) == 'nextval(';
 
34
                
31
35
                switch (strtoupper($t)) {
32
36
                        case 'INTERVAL':
33
37
                        case 'CHAR':
60
64
                        case 'TIMESTAMPTZ':
61
65
                                return 'T';
62
66
                        
63
 
                        case 'INTEGER': return (empty($fieldobj->primary_key) && empty($fieldobj->unique))? 'I' : 'R';
 
67
                        case 'INTEGER': return !$is_serial ? 'I' : 'R';
64
68
                        case 'SMALLINT': 
65
 
                        case 'INT2': return (empty($fieldobj->primary_key) && empty($fieldobj->unique))? 'I2' : 'R';
66
 
                        case 'INT4': return (empty($fieldobj->primary_key) && empty($fieldobj->unique))? 'I4' : 'R';
 
69
                        case 'INT2': return !$is_serial ? 'I2' : 'R';
 
70
                        case 'INT4': return !$is_serial ? 'I4' : 'R';
67
71
                        case 'BIGINT': 
68
 
                        case 'INT8': return (empty($fieldobj->primary_key) && empty($fieldobj->unique))? 'I8' : 'R';
 
72
                        case 'INT8': return !$is_serial ? 'I8' : 'R';
69
73
                                
70
74
                        case 'OID':
71
75
                        case 'SERIAL':
111
115
                }
112
116
        }
113
117
        
114
 
        /* The following does not work in Pg 6.0 - does anyone want to contribute code? 
115
 
        
116
 
        //"ALTER TABLE table ALTER COLUMN column SET DEFAULT mydef" and
117
 
        //"ALTER TABLE table ALTER COLUMN column DROP DEFAULT mydef"
118
 
        //"ALTER TABLE table ALTER COLUMN column SET NOT NULL" and
119
 
        //"ALTER TABLE table ALTER COLUMN column DROP NOT NULL"*/
120
 
        function AlterColumnSQL($tabname, $flds)
121
 
        {
122
 
                if ($this->debug) ADOConnection::outp("AlterColumnSQL not supported for PostgreSQL");
 
118
        /**
 
119
         * Adding a new Column 
 
120
         *
 
121
         * reimplementation of the default function as postgres does NOT allow to set the default in the same statement
 
122
         *
 
123
         * @param string $tabname table-name
 
124
         * @param string $flds column-names and types for the changed columns
 
125
         * @return array with SQL strings
 
126
         */
 
127
        function AddColumnSQL($tabname, $flds)
 
128
        {
 
129
                $tabname = $this->TableName ($tabname);
 
130
                $sql = array();
 
131
                list($lines,$pkey) = $this->_GenFields($flds);
 
132
                $alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' ';
 
133
                foreach($lines as $v) {
 
134
                        if (($not_null = preg_match('/NOT NULL/i',$v))) {
 
135
                                $v = preg_replace('/NOT NULL/i','',$v);
 
136
                        }
 
137
                        if (preg_match('/^([^ ]+) .*(DEFAULT [^ ]+)/',$v,$matches)) {
 
138
                                list(,$colname,$default) = $matches;
 
139
                                $sql[] = $alter . str_replace($default,'',$v);
 
140
                                $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET ' . $default;
 
141
                        } else {                                
 
142
                                $sql[] = $alter . $v;
 
143
                        }
 
144
                        if ($not_null) {
 
145
                                list($colname) = explode(' ',$v);
 
146
                                $sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET NOT NULL';
 
147
                        }
 
148
                }
 
149
                return $sql;
 
150
        }
 
151
        
 
152
        /**
 
153
         * Change the definition of one column
 
154
         *
 
155
         * Postgres can't do that on it's own, you need to supply the complete defintion of the new table,
 
156
         * to allow, recreating the table and copying the content over to the new table
 
157
         * @param string $tabname table-name
 
158
         * @param string $flds column-name and type for the changed column
 
159
         * @param string $tableflds complete defintion of the new table, eg. for postgres, default ''
 
160
         * @param array/ $tableoptions options for the new table see CreateTableSQL, default ''
 
161
         * @return array with SQL strings
 
162
         */
 
163
        function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
 
164
        {
 
165
                if (!$tableflds) {
 
166
                        if ($this->debug) ADOConnection::outp("AlterColumnSQL needs a complete table-definiton for PostgreSQL");
 
167
                        return array();
 
168
                }
 
169
                return $this->_recreate_copy_table($tabname,False,$tableflds,$tableoptions);
 
170
        }
 
171
        
 
172
        /**
 
173
         * Drop one column
 
174
         *
 
175
         * Postgres < 7.3 can't do that on it's own, you need to supply the complete defintion of the new table,
 
176
         * to allow, recreating the table and copying the content over to the new table
 
177
         * @param string $tabname table-name
 
178
         * @param string $flds column-name and type for the changed column
 
179
         * @param string $tableflds complete defintion of the new table, eg. for postgres, default ''
 
180
         * @param array/ $tableoptions options for the new table see CreateTableSQL, default ''
 
181
         * @return array with SQL strings
 
182
         */
 
183
        function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
 
184
        {
 
185
                $has_drop_column = 7.3 <= (float) @$this->serverInfo['version'];
 
186
                if (!$has_drop_column && !$tableflds) {
 
187
                        if ($this->debug) ADOConnection::outp("DropColumnSQL needs complete table-definiton for PostgreSQL < 7.3");
123
188
                return array();
124
189
        }
125
 
        
126
 
        
127
 
        function DropColumnSQL($tabname, $flds)
128
 
        {
129
 
                if ($this->debug) ADOConnection::outp("DropColumnSQL only works with PostgreSQL 7.3+");
130
 
                return ADODB_DataDict::DropColumnSQL($tabname, $flds)."/* only works for PostgreSQL 7.3+ */";
 
190
                if ($has_drop_column) {
 
191
                        return ADODB_DataDict::DropColumnSQL($tabname, $flds);
 
192
                }
 
193
                return $this->_recreate_copy_table($tabname,$flds,$tableflds,$tableoptions);
 
194
        }
 
195
        
 
196
        /**
 
197
         * Save the content into a temp. table, drop and recreate the original table and copy the content back in
 
198
         *
 
199
         * We also take care to set the values of the sequenz and recreate the indexes.
 
200
         * All this is done in a transaction, to not loose the content of the table, if something went wrong!
 
201
         * @internal
 
202
         * @param string $tabname table-name
 
203
         * @param string $dropflds column-names to drop
 
204
         * @param string $tableflds complete defintion of the new table, eg. for postgres
 
205
         * @param array/string $tableoptions options for the new table see CreateTableSQL, default ''
 
206
         * @return array with SQL strings
 
207
         */
 
208
        function _recreate_copy_table($tabname,$dropflds,$tableflds,$tableoptions='')
 
209
        {
 
210
                if ($dropflds && !is_array($dropflds)) $dropflds = explode(',',$dropflds);
 
211
                $copyflds = array();
 
212
                foreach($this->MetaColumns($tabname) as $fld) {
 
213
                        if (!$dropflds || !in_array($fld->name,$dropflds)) {
 
214
                                // we need to explicit convert varchar to a number to be able to do an AlterColumn of a char column to a nummeric one
 
215
                                if (preg_match('/'.$fld->name.' (I|I2|I4|I8|N|F)/i',$tableflds,$matches) && 
 
216
                                        in_array($fld->type,array('varchar','char','text','bytea'))) {
 
217
                                        $copyflds[] = "to_number($fld->name,'S99D99')";
 
218
                                } else {
 
219
                                        $copyflds[] = $fld->name;
 
220
                                }
 
221
                                // identify the sequence name and the fld its on
 
222
                                if ($fld->primary_key && $fld->has_default && 
 
223
                                        preg_match("/nextval\('([^']+)'::text\)/",$fld->default_value,$matches)) {
 
224
                                        $seq_name = $matches[1];
 
225
                                        $seq_fld = $fld->name;
 
226
                                }
 
227
                        }
 
228
                }
 
229
                $copyflds = implode(', ',$copyflds);
 
230
                
 
231
                $tempname = $tabname.'_tmp';
 
232
                $aSql[] = 'BEGIN';              // we use a transaction, to make sure not to loose the content of the table
 
233
                $aSql[] = "SELECT * INTO TEMPORARY TABLE $tempname FROM $tabname";
 
234
                $aSql = array_merge($aSql,$this->DropTableSQL($tabname));
 
235
                $aSql = array_merge($aSql,$this->CreateTableSQL($tabname,$tableflds,$tableoptions));
 
236
                $aSql[] = "INSERT INTO $tabname SELECT $copyflds FROM $tempname";
 
237
                if ($seq_name && $seq_fld) {    // if we have a sequence we need to set it again
 
238
                        $seq_name = $tabname.'_'.$seq_fld.'_seq';       // has to be the name of the new implicit sequence
 
239
                        $aSql[] = "SELECT setval('$seq_name',MAX($seq_fld)) FROM $tabname";
 
240
                }
 
241
                $aSql[] = "DROP TABLE $tempname";
 
242
                // recreate the indexes, if they not contain one of the droped columns
 
243
                foreach($this->MetaIndexes($tabname) as $idx_name => $idx_data)
 
244
                {
 
245
                        if (substr($idx_name,-5) != '_pkey' && (!$dropflds || !count(array_intersect($dropflds,$idx_data['columns'])))) {
 
246
                                $aSql = array_merge($aSql,$this->CreateIndexSQL($idx_name,$tabname,$idx_data['columns'],
 
247
                                        $idx_data['unique'] ? array('UNIQUE') : False));
 
248
                        }
 
249
                }
 
250
                $aSql[] = 'COMMIT';
 
251
                return $aSql;
 
252
        }
 
253
        
 
254
        function DropTableSQL($tabname)
 
255
        {
 
256
                $sql = ADODB_DataDict::DropTableSQL($tabname);
 
257
                
 
258
                $drop_seq = $this->_DropAutoIncrement($tabname);
 
259
                if ($drop_seq) $sql[] = $drop_seq;
 
260
                
 
261
                return $sql;
131
262
        }
132
263
 
133
264
        // return string must begin with space
144
275
                return $suffix;
145
276
        }
146
277
        
147
 
        function _DropAutoIncrement($t)
 
278
        // search for a sequece for the given table (asumes the seqence-name contains the table-name!)
 
279
        // if yes return sql to drop it
 
280
        // this is still necessary if postgres < 7.3 or the SERIAL was created on an earlier version!!!
 
281
        function _DropAutoIncrement($tabname)
148
282
        {
149
 
                return "drop sequence ".$t."_m_id_seq";
 
283
                $tabname = $this->connection->quote('%'.$tabname.'%');
 
284
 
 
285
                $seq = $this->connection->GetOne("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relname LIKE $tabname AND relkind='S'");
 
286
 
 
287
                // check if a tables depends on the sequenz and it therefor cant and dont need to be droped separatly
 
288
                if (!$seq || $this->connection->GetOne("SELECT relname FROM pg_class JOIN pg_depend ON pg_class.relfilenode=pg_depend.objid WHERE relname='$seq' AND relkind='S' AND deptype='i'")) {
 
289
                        return False;
 
290
                }
 
291
                return "DROP SEQUENCE ".$seq;
150
292
        }
151
293
        
152
294
        /*