Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
/**
3
 * Data Dictionary for PostgreSQL.
4
 *
5
 * This file is part of ADOdb, a Database Abstraction Layer library for PHP.
6
 *
7
 * @package ADOdb
8
 * @link https://adodb.org Project's web site and documentation
9
 * @link https://github.com/ADOdb/ADOdb Source code and issue tracker
10
 *
11
 * The ADOdb Library is dual-licensed, released under both the BSD 3-Clause
12
 * and the GNU Lesser General Public Licence (LGPL) v2.1 or, at your option,
13
 * any later version. This means you can use it in proprietary products.
14
 * See the LICENSE.md file distributed with this source code for details.
15
 * @license BSD-3-Clause
16
 * @license LGPL-2.1-or-later
17
 *
18
 * @copyright 2000-2013 John Lim
19
 * @copyright 2014 Damien Regad, Mark Newnham and the ADOdb community
20
 */
21
 
22
// security - hide paths
23
if (!defined('ADODB_DIR')) die();
24
 
25
class ADODB2_postgres extends ADODB_DataDict
26
{
27
	var $databaseType = 'postgres';
28
	var $seqField = false;
29
	var $seqPrefix = 'SEQ_';
30
	var $addCol = ' ADD COLUMN';
31
	var $quote = '"';
32
	var $renameTable = 'ALTER TABLE %s RENAME TO %s'; // at least since 7.1
33
	var $dropTable = 'DROP TABLE %s CASCADE';
34
 
35
	public $blobAllowsDefaultValue = true;
36
	public $blobAllowsNotNull = true;
37
 
38
	function metaType($t, $len=-1, $fieldobj=false)
39
	{
40
		if (is_object($t)) {
41
			$fieldobj = $t;
42
			$t = $fieldobj->type;
43
			$len = $fieldobj->max_length;
44
		}
45
 
46
		$t = strtoupper($t);
47
 
48
		if (array_key_exists($t,$this->connection->customActualTypes))
49
			return  $this->connection->customActualTypes[$t];
50
 
51
		$is_serial = is_object($fieldobj) && !empty($fieldobj->primary_key) && !empty($fieldobj->unique) &&
52
			!empty($fieldobj->has_default) && substr($fieldobj->default_value,0,8) == 'nextval(';
53
 
54
		switch ($t) {
55
 
56
			case 'INTERVAL':
57
			case 'CHAR':
58
			case 'CHARACTER':
59
			case 'VARCHAR':
60
			case 'NAME':
61
			case 'BPCHAR':
62
				if ($len <= $this->blobSize) return 'C';
63
 
64
			case 'TEXT':
65
				return 'X';
66
 
67
			case 'IMAGE': // user defined type
68
			case 'BLOB': // user defined type
69
			case 'BIT':	// This is a bit string, not a single bit, so don't return 'L'
70
			case 'VARBIT':
71
			case 'BYTEA':
72
				return 'B';
73
 
74
			case 'BOOL':
75
			case 'BOOLEAN':
76
				return 'L';
77
 
78
			case 'DATE':
79
				return 'D';
80
 
81
			case 'TIME':
82
			case 'DATETIME':
83
			case 'TIMESTAMP':
84
			case 'TIMESTAMPTZ':
85
				return 'T';
86
 
87
			case 'INTEGER': return !$is_serial ? 'I' : 'R';
88
			case 'SMALLINT':
89
			case 'INT2': return !$is_serial ? 'I2' : 'R';
90
			case 'INT4': return !$is_serial ? 'I4' : 'R';
91
			case 'BIGINT':
92
			case 'INT8': return !$is_serial ? 'I8' : 'R';
93
 
94
			case 'OID':
95
			case 'SERIAL':
96
				return 'R';
97
 
98
			case 'FLOAT4':
99
			case 'FLOAT8':
100
			case 'DOUBLE PRECISION':
101
			case 'REAL':
102
				return 'F';
103
 
104
			default:
105
				return ADODB_DEFAULT_METATYPE;
106
		}
107
	}
108
 
109
	function actualType($meta)
110
	{
111
		$meta = strtoupper($meta);
112
 
113
		/*
114
		* Add support for custom meta types. We do this
115
		* first, that allows us to override existing types
116
		*/
117
		if (isset($this->connection->customMetaTypes[$meta]))
118
			return $this->connection->customMetaTypes[$meta]['actual'];
119
 
120
		switch ($meta) {
121
		case 'C': return 'VARCHAR';
122
		case 'XL':
123
		case 'X': return 'TEXT';
124
 
125
		case 'C2': return 'VARCHAR';
126
		case 'X2': return 'TEXT';
127
 
128
		case 'B': return 'BYTEA';
129
 
130
		case 'D': return 'DATE';
131
		case 'TS':
132
		case 'T': return 'TIMESTAMP';
133
 
134
		case 'L': return 'BOOLEAN';
135
		case 'I': return 'INTEGER';
136
		case 'I1': return 'SMALLINT';
137
		case 'I2': return 'INT2';
138
		case 'I4': return 'INT4';
139
		case 'I8': return 'INT8';
140
 
141
		case 'F': return 'FLOAT8';
142
		case 'N': return 'NUMERIC';
143
		default:
144
			return $meta;
145
		}
146
	}
147
 
148
	/**
149
	 * Adding a new Column
150
	 *
151
	 * reimplementation of the default function as postgres does NOT allow to set the default in the same statement
152
	 *
153
	 * @param string $tabname table-name
154
	 * @param string $flds column-names and types for the changed columns
155
	 * @return array with SQL strings
156
	 */
157
	function addColumnSQL($tabname, $flds)
158
	{
159
		$tabname = $this->tableName($tabname);
160
		$sql = array();
161
		$not_null = false;
162
		list($lines,$pkey) = $this->_genFields($flds);
163
		$alter = 'ALTER TABLE ' . $tabname . $this->addCol;
164
		$alter .= (float)@$this->serverInfo['version'] < 9.6 ? ' ' : ' IF NOT EXISTS ';
165
		foreach($lines as $v) {
166
			if (($not_null = preg_match('/NOT NULL/i',$v))) {
167
				$v = preg_replace('/NOT NULL/i','',$v);
168
			}
169
			if (preg_match('/^([^ ]+) .*DEFAULT (\'[^\']+\'|\"[^\"]+\"|[^ ]+)/',$v,$matches)) {
170
				list(,$colname,$default) = $matches;
171
				$sql[] = $alter . str_replace('DEFAULT '.$default,'',$v);
172
				$sql[] = 'UPDATE '.$tabname.' SET '.$colname.'='.$default.' WHERE '.$colname.' IS NULL ';
173
				$sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET DEFAULT ' . $default;
174
			} else {
175
				$sql[] = $alter . $v;
176
			}
177
			if ($not_null) {
178
				list($colname) = explode(' ',$v);
179
				$sql[] = 'ALTER TABLE '.$tabname.' ALTER COLUMN '.$colname.' SET NOT NULL';
180
			}
181
		}
182
		return $sql;
183
	}
184
 
185
 
186
	function dropIndexSQL($idxname, $tabname = NULL)
187
	{
188
		return array(sprintf($this->dropIndex, $this->tableName($idxname), $this->tableName($tabname)));
189
	}
190
 
191
	/**
192
	 * Change the definition of one column
193
	 *
194
	 * Postgres can't do that on its own, you need to supply the complete
195
	 * definition of the new table, to allow recreating the table and copying
196
	 * the content over to the new table.
197
	 *
198
	 * @param string $tabname      table-name
199
	 * @param string $flds         column-name and type for the changed column
200
	 * @param string $tableflds    complete definition of the new table, e.g. for postgres, default ''
201
	 * @param array  $tableoptions options for the new table {@see CreateTableSQL()}, default ''
202
	 *
203
	 * @return array with SQL strings
204
	 */
205
	function alterColumnSQL($tabname, $flds, $tableflds='', $tableoptions='')
206
	{
207
		// Check if alter single column datatype available - works with 8.0+
208
		$has_alter_column = 8.0 <= (float) @$this->serverInfo['version'];
209
 
210
		if ($has_alter_column) {
211
			$tabname = $this->tableName($tabname);
212
			$sql = array();
213
			list($lines,$pkey) = $this->_genFields($flds);
214
			$set_null = false;
215
			foreach($lines as $v) {
216
				$alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' ';
217
				if ($not_null = preg_match('/NOT NULL/i',$v)) {
218
					$v = preg_replace('/NOT NULL/i','',$v);
219
				}
220
				// this next block doesn't work - there is no way that I can see to
221
				// explicitly ask a column to be null using $flds
222
				elseif ($set_null = preg_match('/NULL/i',$v)) {
223
					// if they didn't specify not null, see if they explicitly asked for null
224
					// Lookbehind pattern covers the case 'fieldname NULL datatype DEFAULT NULL'
225
					// only the first NULL should be removed, not the one specifying
226
					// the default value
227
					$v = preg_replace('/(?<!DEFAULT)\sNULL/i','',$v);
228
				}
229
 
230
				if (preg_match('/^([^ ]+) .*DEFAULT (\'[^\']+\'|\"[^\"]+\"|[^ ]+)/',$v,$matches)) {
231
					$existing = $this->metaColumns($tabname);
232
					list(,$colname,$default) = $matches;
233
					$alter .= $colname;
234
					if ($this->connection) {
235
						$old_coltype = $this->connection->metaType($existing[strtoupper($colname)]);
236
					} else {
237
						$old_coltype = $t;
238
					}
239
					$v = preg_replace('/^' . preg_quote($colname) . '\s/', '', $v);
240
					$t = trim(str_replace('DEFAULT '.$default,'',$v));
241
 
242
					// Type change from bool to int
243
					if ( $old_coltype == 'L' && $t == 'INTEGER' ) {
244
						$sql[] = $alter . ' DROP DEFAULT';
245
						$sql[] = $alter . " TYPE $t USING ($colname::BOOL)::INT";
246
						$sql[] = $alter . " SET DEFAULT $default";
247
					}
248
					// Type change from int to bool
249
					else if ( $old_coltype == 'I' && $t == 'BOOLEAN' ) {
250
						if( strcasecmp('NULL', trim($default)) != 0 ) {
251
							$default = $this->connection->qstr($default);
252
						}
253
						$sql[] = $alter . ' DROP DEFAULT';
254
						$sql[] = $alter . " TYPE $t USING CASE WHEN $colname = 0 THEN false ELSE true END";
255
						$sql[] = $alter . " SET DEFAULT $default";
256
					}
257
					// Any other column types conversion
258
					else {
259
						$sql[] = $alter . " TYPE $t";
260
						$sql[] = $alter . " SET DEFAULT $default";
261
					}
262
 
263
				}
264
				else {
265
					// drop default?
266
					preg_match ('/^\s*(\S+)\s+(.*)$/',$v,$matches);
267
					list (,$colname,$rest) = $matches;
268
					$alter .= $colname;
269
					$sql[] = $alter . ' TYPE ' . $rest;
270
				}
271
 
272
				#list($colname) = explode(' ',$v);
273
				if ($not_null) {
274
					// this does not error out if the column is already not null
275
					$sql[] = $alter . ' SET NOT NULL';
276
				}
277
				if ($set_null) {
278
					// this does not error out if the column is already null
279
					$sql[] = $alter . ' DROP NOT NULL';
280
				}
281
			}
282
			return $sql;
283
		}
284
 
285
		// does not have alter column
286
		if (!$tableflds) {
287
			if ($this->debug) ADOConnection::outp("AlterColumnSQL needs a complete table-definiton for PostgreSQL");
288
			return array();
289
		}
290
		return $this->_recreate_copy_table($tabname, false, $tableflds,$tableoptions);
291
	}
292
 
293
	/**
294
	 * Drop one column
295
	 *
296
	 * Postgres < 7.3 can't do that on it's own, you need to supply the complete definition of the new table,
297
	 * to allow, recreating the table and copying the content over to the new table
298
	 * @param string $tabname table-name
299
	 * @param string $flds column-name and type for the changed column
300
	 * @param string $tableflds complete definition of the new table, eg. for postgres, default ''
301
	 * @param array  $tableoptions options for the new table {@see CreateTableSQL}, default []
302
	 * @return array with SQL strings
303
	 */
304
	function dropColumnSQL($tabname, $flds, $tableflds='', $tableoptions='')
305
	{
306
		$has_drop_column = 7.3 <= (float) @$this->serverInfo['version'];
307
		if (!$has_drop_column && !$tableflds) {
308
			if ($this->debug) {
309
				ADOConnection::outp("dropColumnSQL needs complete table-definiton for PostgreSQL < 7.3");
310
			}
311
			return array();
312
		}
313
		if ($has_drop_column) {
314
			return ADODB_DataDict::dropColumnSQL($tabname, $flds);
315
		}
316
		return $this->_recreate_copy_table($tabname, $flds, $tableflds, $tableoptions);
317
	}
318
 
319
	/**
320
	 * Save the content into a temp. table, drop and recreate the original table and copy the content back in
321
	 *
322
	 * We also take care to set the values of the sequenz and recreate the indexes.
323
	 * All this is done in a transaction, to not loose the content of the table, if something went wrong!
324
	 * @internal
325
	 * @param string $tabname table-name
326
	 * @param string $dropflds column-names to drop
327
	 * @param string $tableflds complete definition of the new table, eg. for postgres
328
	 * @param array|string $tableoptions options for the new table see CreateTableSQL, default ''
329
	 * @return array with SQL strings
330
	 */
331
	function _recreate_copy_table($tabname, $dropflds, $tableflds, $tableoptions='')
332
	{
333
		if ($dropflds && !is_array($dropflds)) $dropflds = explode(',',$dropflds);
334
		$copyflds = array();
335
		foreach($this->metaColumns($tabname) as $fld) {
336
			if (preg_match('/'.$fld->name.' (\w+)/i', $tableflds, $matches)) {
337
				$new_type = strtoupper($matches[1]);
338
				// AlterColumn of a char column to a nummeric one needs an explicit conversation
339
				if (in_array($new_type, array('I', 'I2', 'I4', 'I8', 'N', 'F')) &&
340
					in_array($fld->type, array('varchar','char','text','bytea'))
341
				) {
342
					$copyflds[] = "to_number($fld->name,'S9999999999999D99')";
343
				} else {
344
					// other column-type changes needs explicit decode, encode for bytea or cast otherwise
345
					$new_actual_type = $this->actualType($new_type);
346
					if (strtoupper($fld->type) != $new_actual_type) {
347
						if ($new_actual_type == 'BYTEA' && $fld->type == 'text') {
348
							$copyflds[] = "DECODE($fld->name, 'escape')";
349
						} elseif ($fld->type == 'bytea' && $new_actual_type == 'TEXT') {
350
							$copyflds[] = "ENCODE($fld->name, 'escape')";
351
						} else {
352
							$copyflds[] = "CAST($fld->name AS $new_actual_type)";
353
						}
354
					}
355
				}
356
			} else {
357
				$copyflds[] = $fld->name;
358
			}
359
			// identify the sequence name and the fld its on
360
			if ($fld->primary_key && $fld->has_default &&
361
				preg_match("/nextval\('([^']+)'::(text|regclass)\)/", $fld->default_value, $matches)) {
362
				$seq_name = $matches[1];
363
				$seq_fld = $fld->name;
364
			}
365
		}
366
		$copyflds = implode(', ', $copyflds);
367
 
368
		$tempname = $tabname.'_tmp';
369
		$aSql[] = 'BEGIN';		// we use a transaction, to make sure not to loose the content of the table
370
		$aSql[] = "SELECT * INTO TEMPORARY TABLE $tempname FROM $tabname";
371
		$aSql = array_merge($aSql,$this->dropTableSQL($tabname));
372
		$aSql = array_merge($aSql,$this->createTableSQL($tabname, $tableflds, $tableoptions));
373
		$aSql[] = "INSERT INTO $tabname SELECT $copyflds FROM $tempname";
374
		if ($seq_name && $seq_fld) {	// if we have a sequence we need to set it again
375
			$seq_name = $tabname.'_'.$seq_fld.'_seq';	// has to be the name of the new implicit sequence
376
			$aSql[] = "SELECT setval('$seq_name', MAX($seq_fld)) FROM $tabname";
377
		}
378
		$aSql[] = "DROP TABLE $tempname";
379
		// recreate the indexes, if they not contain one of the dropped columns
380
		foreach($this->metaIndexes($tabname) as $idx_name => $idx_data) {
381
			if (substr($idx_name,-5) != '_pkey' && (!$dropflds || !count(array_intersect($dropflds,$idx_data['columns'])))) {
382
				$aSql = array_merge($aSql,$this->createIndexSQL($idx_name, $tabname, $idx_data['columns'],
383
					$idx_data['unique'] ? array('UNIQUE') : false));
384
			}
385
		}
386
		$aSql[] = 'COMMIT';
387
		return $aSql;
388
	}
389
 
390
	function dropTableSQL($tabname)
391
	{
392
		$sql = ADODB_DataDict::dropTableSQL($tabname);
393
 
394
		$drop_seq = $this->_dropAutoIncrement($tabname);
395
		if ($drop_seq) {
396
			$sql[] = $drop_seq;
397
		}
398
		return $sql;
399
	}
400
 
401
	// return string must begin with space
402
	function _createSuffix($fname, &$ftype, $fnotnull, $fdefault, $fautoinc, $fconstraint, $funsigned)
403
	{
404
		if ($fautoinc) {
405
			$ftype = 'SERIAL';
406
			return '';
407
		}
408
		$suffix = '';
409
		if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
410
		if ($fnotnull) $suffix .= ' NOT NULL';
411
		if ($fconstraint) $suffix .= ' '.$fconstraint;
412
		return $suffix;
413
	}
414
 
415
	// search for a sequence for the given table (asumes the seqence-name contains the table-name!)
416
	// if yes return sql to drop it
417
	// this is still necessary if postgres < 7.3 or the SERIAL was created on an earlier version!!!
418
	function _dropAutoIncrement($tabname)
419
	{
420
		$tabname = $this->connection->quote('%'.$tabname.'%');
421
 
422
		$seq = $this->connection->getOne("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relname LIKE $tabname AND relkind='S'");
423
 
424
		// check if a tables depends on the sequence and it therefore can't and don't need to be dropped separately
425
		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'")) {
426
			return false;
427
		}
428
		return "DROP SEQUENCE ".$seq;
429
	}
430
 
431
	function renameTableSQL($tabname, $newname)
432
	{
433
		if (!empty($this->schema)) {
434
			$rename_from = $this->tableName($tabname);
435
			$schema_save = $this->schema;
436
			$this->schema = false;
437
			$rename_to = $this->tableName($newname);
438
			$this->schema = $schema_save;
439
			return array (sprintf($this->renameTable, $rename_from, $rename_to));
440
		}
441
 
442
		return array (sprintf($this->renameTable, $this->tableName($tabname), $this->tableName($newname)));
443
	}
444
 
445
	/*
446
	CREATE [ [ LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name (
447
	{ column_name data_type [ DEFAULT default_expr ] [ column_constraint [, ... ] ]
448
	| table_constraint } [, ... ]
449
	)
450
	[ INHERITS ( parent_table [, ... ] ) ]
451
	[ WITH OIDS | WITHOUT OIDS ]
452
	where column_constraint is:
453
	[ CONSTRAINT constraint_name ]
454
	{ NOT NULL | NULL | UNIQUE | PRIMARY KEY |
455
	CHECK (expression) |
456
	REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL ]
457
	[ ON DELETE action ] [ ON UPDATE action ] }
458
	[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
459
	and table_constraint is:
460
	[ CONSTRAINT constraint_name ]
461
	{ UNIQUE ( column_name [, ... ] ) |
462
	PRIMARY KEY ( column_name [, ... ] ) |
463
	CHECK ( expression ) |
464
	FOREIGN KEY ( column_name [, ... ] ) REFERENCES reftable [ ( refcolumn [, ... ] ) ]
465
	[ MATCH FULL | MATCH PARTIAL ] [ ON DELETE action ] [ ON UPDATE action ] }
466
	[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
467
	*/
468
 
469
 
470
	/*
471
	CREATE [ UNIQUE ] INDEX index_name ON table
472
[ USING acc_method ] ( column [ ops_name ] [, ...] )
473
[ WHERE predicate ]
474
CREATE [ UNIQUE ] INDEX index_name ON table
475
[ USING acc_method ] ( func_name( column [, ... ]) [ ops_name ] )
476
[ WHERE predicate ]
477
	*/
478
	function _indexSQL($idxname, $tabname, $flds, $idxoptions)
479
	{
480
		$sql = array();
481
 
482
		if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) {
483
			$sql[] = sprintf ($this->dropIndex, $idxname, $tabname);
484
			if ( isset($idxoptions['DROP']) ) {
485
				return $sql;
486
			}
487
		}
488
 
489
		if (empty($flds)) {
490
			return $sql;
491
		}
492
 
493
		$unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : '';
494
 
495
		$s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' ';
496
 
497
		if (isset($idxoptions['HASH'])) {
498
			$s .= 'USING HASH ';
499
		}
500
 
501
		if (isset($idxoptions[$this->upperName])) {
502
			$s .= $idxoptions[$this->upperName];
503
		}
504
 
505
		if (is_array($flds)) {
506
			$flds = implode(', ', $flds);
507
		}
508
		$s .= '(' . $flds . ')';
509
		$sql[] = $s;
510
 
511
		return $sql;
512
	}
513
 
514
	function _getSize($ftype, $ty, $fsize, $fprec, $options=false)
515
	{
516
		if (strlen($fsize) && $ty != 'X' && $ty != 'B' && $ty  != 'I' && strpos($ftype,'(') === false) {
517
			$ftype .= "(".$fsize;
518
			if (strlen($fprec)) $ftype .= ",".$fprec;
519
			$ftype .= ')';
520
		}
521
 
522
		/*
523
		* Handle additional options
524
		*/
525
		if (is_array($options)) {
526
			foreach($options as $type=>$value) {
527
				switch ($type) {
528
					case 'ENUM':
529
						$ftype .= '(' . $value . ')';
530
						break;
531
					default:
532
				}
533
			}
534
		}
535
		return $ftype;
536
	}
537
 
538
	function changeTableSQL($tablename, $flds, $tableoptions = false, $dropOldFlds=false)
539
	{
540
		global $ADODB_FETCH_MODE;
541
		parent::changeTableSQL($tablename, $flds);
542
		$save = $ADODB_FETCH_MODE;
543
		$ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
544
		if ($this->connection->fetchMode !== false) {
545
			$savem = $this->connection->setFetchMode(false);
546
		}
547
 
548
		// check table exists
549
		$save_handler = $this->connection->raiseErrorFn;
550
		$this->connection->raiseErrorFn = '';
551
		$cols = $this->metaColumns($tablename);
552
		$this->connection->raiseErrorFn = $save_handler;
553
 
554
		if (isset($savem)) {
555
			$this->connection->setFetchMode($savem);
556
		}
557
		$ADODB_FETCH_MODE = $save;
558
 
559
		$sqlResult=array();
560
		if ( empty($cols)) {
561
			$sqlResult=$this->createTableSQL($tablename, $flds, $tableoptions);
562
		} else {
563
			$sqlResultAdd = $this->addColumnSQL($tablename, $flds);
564
			$sqlResultAlter = $this->alterColumnSQL($tablename, $flds, '', $tableoptions);
565
			$sqlResult = array_merge((array)$sqlResultAdd, (array)$sqlResultAlter);
566
 
567
			if ($dropOldFlds) {
568
				// already exists, alter table instead
569
				list($lines,$pkey,$idxs) = $this->_genFields($flds);
570
				// genfields can return FALSE at times
571
				if ($lines == null) {
572
					$lines = array();
573
				}
574
				$alter = 'ALTER TABLE ' . $this->tableName($tablename);
575
				foreach ( $cols as $id => $v ) {
576
					if ( !isset($lines[$id]) ) {
577
						$sqlResult[] = $alter . $this->dropCol . ' ' . $v->name;
578
					}
579
				}
580
			}
581
 
582
		}
583
		return $sqlResult;
584
	}
585
} // end class