Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402] [Versions 402 and 403]

   1  <?php
   2  /**
   3   * ADOdb Data Dictionary base class.
   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  /**
  26   * Test script for parser
  27   */
  28  function lens_ParseTest()
  29  {
  30  $str = "`zcol ACOL` NUMBER(32,2) DEFAULT 'The \"cow\" (and Jim''s dog) jumps over the moon' PRIMARY, INTI INT AUTO DEFAULT 0, zcol2\"afs ds";
  31  print "<p>$str</p>";
  32  $a= lens_ParseArgs($str);
  33  print "<pre>";
  34  print_r($a);
  35  print "</pre>";
  36  }
  37  
  38  
  39  if (!function_exists('ctype_alnum')) {
  40  	function ctype_alnum($text) {
  41  	 	 return preg_match('/^[a-z0-9]*$/i', $text);
  42  	 }
  43  }
  44  
  45  //Lens_ParseTest();
  46  
  47  /**
  48  	 Parse arguments, treat "text" (text) and 'text' as quotation marks.
  49  	 To escape, use "" or '' or ))
  50  
  51  	 Will read in "abc def" sans quotes, as: abc def
  52  	 Same with 'abc def'.
  53  	 However if `abc def`, then will read in as `abc def`
  54  
  55  	 @param endstmtchar    Character that indicates end of statement
  56  	 @param tokenchars     Include the following characters in tokens apart from A-Z and 0-9
  57  	 @returns 2 dimensional array containing parsed tokens.
  58  */
  59  function lens_ParseArgs($args,$endstmtchar=',',$tokenchars='_.-')
  60  {
  61  	 $pos = 0;
  62  	 $intoken = false;
  63  	 $stmtno = 0;
  64  	 $endquote = false;
  65  	 $tokens = array();
  66  	 $tokens[$stmtno] = array();
  67  	 $max = strlen($args);
  68  	 $quoted = false;
  69  	 $tokarr = array();
  70  
  71  	 while ($pos < $max) {
  72  	 	 $ch = substr($args,$pos,1);
  73  	 	 switch($ch) {
  74  	 	 case ' ':
  75  	 	 case "\t":
  76  	 	 case "\n":
  77  	 	 case "\r":
  78  	 	 	 if (!$quoted) {
  79  	 	 	 	 if ($intoken) {
  80  	 	 	 	 	 $intoken = false;
  81  	 	 	 	 	 $tokens[$stmtno][] = implode('',$tokarr);
  82  	 	 	 	 }
  83  	 	 	 	 break;
  84  	 	 	 }
  85  
  86  	 	 	 $tokarr[] = $ch;
  87  	 	 	 break;
  88  
  89  	 	 case '`':
  90  	 	 	 if ($intoken) $tokarr[] = $ch;
  91  	 	 case '(':
  92  	 	 case ')':
  93  	 	 case '"':
  94  	 	 case "'":
  95  
  96  	 	 	 if ($intoken) {
  97  	 	 	 	 if (empty($endquote)) {
  98  	 	 	 	 	 $tokens[$stmtno][] = implode('',$tokarr);
  99  	 	 	 	 	 if ($ch == '(') $endquote = ')';
 100  	 	 	 	 	 else $endquote = $ch;
 101  	 	 	 	 	 $quoted = true;
 102  	 	 	 	 	 $intoken = true;
 103  	 	 	 	 	 $tokarr = array();
 104  	 	 	 	 } else if ($endquote == $ch) {
 105  	 	 	 	 	 $ch2 = substr($args,$pos+1,1);
 106  	 	 	 	 	 if ($ch2 == $endquote) {
 107  	 	 	 	 	 	 $pos += 1;
 108  	 	 	 	 	 	 $tokarr[] = $ch2;
 109  	 	 	 	 	 } else {
 110  	 	 	 	 	 	 $quoted = false;
 111  	 	 	 	 	 	 $intoken = false;
 112  	 	 	 	 	 	 $tokens[$stmtno][] = implode('',$tokarr);
 113  	 	 	 	 	 	 $endquote = '';
 114  	 	 	 	 	 }
 115  	 	 	 	 } else
 116  	 	 	 	 	 $tokarr[] = $ch;
 117  
 118  	 	 	 }else {
 119  
 120  	 	 	 	 if ($ch == '(') $endquote = ')';
 121  	 	 	 	 else $endquote = $ch;
 122  	 	 	 	 $quoted = true;
 123  	 	 	 	 $intoken = true;
 124  	 	 	 	 $tokarr = array();
 125  	 	 	 	 if ($ch == '`') $tokarr[] = '`';
 126  	 	 	 }
 127  	 	 	 break;
 128  
 129  	 	 default:
 130  
 131  	 	 	 if (!$intoken) {
 132  	 	 	 	 if ($ch == $endstmtchar) {
 133  	 	 	 	 	 $stmtno += 1;
 134  	 	 	 	 	 $tokens[$stmtno] = array();
 135  	 	 	 	 	 break;
 136  	 	 	 	 }
 137  
 138  	 	 	 	 $intoken = true;
 139  	 	 	 	 $quoted = false;
 140  	 	 	 	 $endquote = false;
 141  	 	 	 	 $tokarr = array();
 142  
 143  	 	 	 }
 144  
 145  	 	 	 if ($quoted) $tokarr[] = $ch;
 146  	 	 	 else if (ctype_alnum($ch) || strpos($tokenchars,$ch) !== false) $tokarr[] = $ch;
 147  	 	 	 else {
 148  	 	 	 	 if ($ch == $endstmtchar) {
 149  	 	 	 	 	 $tokens[$stmtno][] = implode('',$tokarr);
 150  	 	 	 	 	 $stmtno += 1;
 151  	 	 	 	 	 $tokens[$stmtno] = array();
 152  	 	 	 	 	 $intoken = false;
 153  	 	 	 	 	 $tokarr = array();
 154  	 	 	 	 	 break;
 155  	 	 	 	 }
 156  	 	 	 	 $tokens[$stmtno][] = implode('',$tokarr);
 157  	 	 	 	 $tokens[$stmtno][] = $ch;
 158  	 	 	 	 $intoken = false;
 159  	 	 	 }
 160  	 	 }
 161  	 	 $pos += 1;
 162  	 }
 163  	 if ($intoken) $tokens[$stmtno][] = implode('',$tokarr);
 164  
 165  	 return $tokens;
 166  }
 167  
 168  
 169  class ADODB_DataDict {
 170  	 /** @var ADOConnection */
 171  	 var $connection;
 172  	 var $debug = false;
 173  	 var $dropTable = 'DROP TABLE %s';
 174  	 var $renameTable = 'RENAME TABLE %s TO %s';
 175  	 var $dropIndex = 'DROP INDEX %s';
 176  	 var $addCol = ' ADD';
 177  	 var $alterCol = ' ALTER COLUMN';
 178  	 var $dropCol = ' DROP COLUMN';
 179  	 var $renameColumn = 'ALTER TABLE %s RENAME COLUMN %s TO %s';	 // table, old-column, new-column, column-definitions (not used by default)
 180  	 var $nameRegex = '\w';
 181  	 var $nameRegexBrackets = 'a-zA-Z0-9_\(\)';
 182  	 var $schema = false;
 183  	 var $serverInfo = array();
 184  	 var $autoIncrement = false;
 185  	 var $dataProvider;
 186  	 var $invalidResizeTypes4 = array('CLOB','BLOB','TEXT','DATE','TIME'); // for changeTableSQL
 187  	 var $blobSize = 100; 	 /// any varchar/char field this size or greater is treated as a blob
 188  	 	 	 	 	 	 	 /// in other words, we use a text area for editing.
 189  	 /** @var string Uppercase driver name */
 190  	 var $upperName;
 191  
 192  	 /*
 193  	 * Indicates whether a BLOB/CLOB field will allow a NOT NULL setting
 194  	 * The type is whatever is matched to an X or X2 or B type. We must
 195  	 * explicitly set the value in the driver to switch the behaviour on
 196  	 */
 197  	 public $blobAllowsNotNull;
 198  	 /*
 199  	 * Indicates whether a BLOB/CLOB field will allow a DEFAULT set
 200  	 * The type is whatever is matched to an X or X2 or B type. We must
 201  	 * explicitly set the value in the driver to switch the behaviour on
 202  	 */
 203  	 public $blobAllowsDefaultValue;
 204  
 205  	 /**
 206  	  * @var string String to use to quote identifiers and names
 207  	  */
 208  	 public $quote;
 209  
 210  	function getCommentSQL($table,$col)
 211  	 {
 212  	 	 return false;
 213  	 }
 214  
 215  	function setCommentSQL($table,$col,$cmt)
 216  	 {
 217  	 	 return false;
 218  	 }
 219  
 220  	function metaTables()
 221  	 {
 222  	 	 if (!$this->connection->isConnected()) return array();
 223  	 	 return $this->connection->metaTables();
 224  	 }
 225  
 226  	function metaColumns($tab, $upper=true, $schema=false)
 227  	 {
 228  	 	 if (!$this->connection->isConnected()) return array();
 229  	 	 return $this->connection->metaColumns($this->tableName($tab), $upper, $schema);
 230  	 }
 231  
 232  	function metaPrimaryKeys($tab,$owner=false,$intkey=false)
 233  	 {
 234  	 	 if (!$this->connection->isConnected()) return array();
 235  	 	 return $this->connection->metaPrimaryKeys($this->tableName($tab), $owner, $intkey);
 236  	 }
 237  
 238  	function metaIndexes($table, $primary = false, $owner = false)
 239  	 {
 240  	 	 if (!$this->connection->isConnected()) return array();
 241  	 	 return $this->connection->metaIndexes($this->tableName($table), $primary, $owner);
 242  	 }
 243  
 244  	function metaType($t,$len=-1,$fieldobj=false)
 245  	 {
 246  	 	 static $typeMap = array(
 247  	 	 'VARCHAR' => 'C',
 248  	 	 'VARCHAR2' => 'C',
 249  	 	 'CHAR' => 'C',
 250  	 	 'C' => 'C',
 251  	 	 'STRING' => 'C',
 252  	 	 'NCHAR' => 'C',
 253  	 	 'NVARCHAR' => 'C',
 254  	 	 'VARYING' => 'C',
 255  	 	 'BPCHAR' => 'C',
 256  	 	 'CHARACTER' => 'C',
 257  	 	 'INTERVAL' => 'C',  # Postgres
 258  	 	 'MACADDR' => 'C', # postgres
 259  	 	 'VAR_STRING' => 'C', # mysql
 260  	 	 ##
 261  	 	 'LONGCHAR' => 'X',
 262  	 	 'TEXT' => 'X',
 263  	 	 'NTEXT' => 'X',
 264  	 	 'M' => 'X',
 265  	 	 'X' => 'X',
 266  	 	 'CLOB' => 'X',
 267  	 	 'NCLOB' => 'X',
 268  	 	 'LVARCHAR' => 'X',
 269  	 	 ##
 270  	 	 'BLOB' => 'B',
 271  	 	 'IMAGE' => 'B',
 272  	 	 'BINARY' => 'B',
 273  	 	 'VARBINARY' => 'B',
 274  	 	 'LONGBINARY' => 'B',
 275  	 	 'B' => 'B',
 276  	 	 ##
 277  	 	 'YEAR' => 'D', // mysql
 278  	 	 'DATE' => 'D',
 279  	 	 'D' => 'D',
 280  	 	 ##
 281  	 	 'UNIQUEIDENTIFIER' => 'C', # MS SQL Server
 282  	 	 ##
 283  	 	 'TIME' => 'T',
 284  	 	 'TIMESTAMP' => 'T',
 285  	 	 'DATETIME' => 'T',
 286  	 	 'TIMESTAMPTZ' => 'T',
 287  	 	 'SMALLDATETIME' => 'T',
 288  	 	 'T' => 'T',
 289  	 	 'TIMESTAMP WITHOUT TIME ZONE' => 'T', // postgresql
 290  	 	 ##
 291  	 	 'BOOL' => 'L',
 292  	 	 'BOOLEAN' => 'L',
 293  	 	 'BIT' => 'L',
 294  	 	 'L' => 'L',
 295  	 	 ##
 296  	 	 'COUNTER' => 'R',
 297  	 	 'R' => 'R',
 298  	 	 'SERIAL' => 'R', // ifx
 299  	 	 'INT IDENTITY' => 'R',
 300  	 	 ##
 301  	 	 'INT' => 'I',
 302  	 	 'INT2' => 'I',
 303  	 	 'INT4' => 'I',
 304  	 	 'INT8' => 'I',
 305  	 	 'INTEGER' => 'I',
 306  	 	 'INTEGER UNSIGNED' => 'I',
 307  	 	 'SHORT' => 'I',
 308  	 	 'TINYINT' => 'I',
 309  	 	 'SMALLINT' => 'I',
 310  	 	 'I' => 'I',
 311  	 	 ##
 312  	 	 'LONG' => 'N', // interbase is numeric, oci8 is blob
 313  	 	 'BIGINT' => 'N', // this is bigger than PHP 32-bit integers
 314  	 	 'DECIMAL' => 'N',
 315  	 	 'DEC' => 'N',
 316  	 	 'REAL' => 'N',
 317  	 	 'DOUBLE' => 'N',
 318  	 	 'DOUBLE PRECISION' => 'N',
 319  	 	 'SMALLFLOAT' => 'N',
 320  	 	 'FLOAT' => 'N',
 321  	 	 'NUMBER' => 'N',
 322  	 	 'NUM' => 'N',
 323  	 	 'NUMERIC' => 'N',
 324  	 	 'MONEY' => 'N',
 325  
 326  	 	 ## informix 9.2
 327  	 	 'SQLINT' => 'I',
 328  	 	 'SQLSERIAL' => 'I',
 329  	 	 'SQLSMINT' => 'I',
 330  	 	 'SQLSMFLOAT' => 'N',
 331  	 	 'SQLFLOAT' => 'N',
 332  	 	 'SQLMONEY' => 'N',
 333  	 	 'SQLDECIMAL' => 'N',
 334  	 	 'SQLDATE' => 'D',
 335  	 	 'SQLVCHAR' => 'C',
 336  	 	 'SQLCHAR' => 'C',
 337  	 	 'SQLDTIME' => 'T',
 338  	 	 'SQLINTERVAL' => 'N',
 339  	 	 'SQLBYTES' => 'B',
 340  	 	 'SQLTEXT' => 'X',
 341  	 	  ## informix 10
 342  	 	 "SQLINT8" => 'I8',
 343  	 	 "SQLSERIAL8" => 'I8',
 344  	 	 "SQLNCHAR" => 'C',
 345  	 	 "SQLNVCHAR" => 'C',
 346  	 	 "SQLLVARCHAR" => 'X',
 347  	 	 "SQLBOOL" => 'L'
 348  	 	 );
 349  
 350  	 	 if (!$this->connection->isConnected()) {
 351  	 	 	 $t = strtoupper($t);
 352  	 	 	 if (isset($typeMap[$t])) return $typeMap[$t];
 353  	 	 	 return ADODB_DEFAULT_METATYPE;
 354  	 	 }
 355  	 	 return $this->connection->metaType($t,$len,$fieldobj);
 356  	 }
 357  
 358  	function nameQuote($name = NULL,$allowBrackets=false)
 359  	 {
 360  	 	 if (!is_string($name)) {
 361  	 	 	 return false;
 362  	 	 }
 363  
 364  	 	 $name = trim($name);
 365  
 366  	 	 if ( !is_object($this->connection) ) {
 367  	 	 	 return $name;
 368  	 	 }
 369  
 370  	 	 $quote = $this->connection->nameQuote;
 371  
 372  	 	 // if name is of the form `name`, quote it
 373  	 	 if ( preg_match('/^`(.+)`$/', $name, $matches) ) {
 374  	 	 	 return $quote . $matches[1] . $quote;
 375  	 	 }
 376  
 377  	 	 // if name contains special characters, quote it
 378  	 	 $regex = ($allowBrackets) ? $this->nameRegexBrackets : $this->nameRegex;
 379  
 380  	 	 if ( !preg_match('/^[' . $regex . ']+$/', $name) ) {
 381  	 	 	 return $quote . $name . $quote;
 382  	 	 }
 383  
 384  	 	 return $name;
 385  	 }
 386  
 387  	function tableName($name)
 388  	 {
 389  	 	 if ( $this->schema ) {
 390  	 	 	 return $this->nameQuote($this->schema) .'.'. $this->nameQuote($name);
 391  	 	 }
 392  	 	 return $this->nameQuote($name);
 393  	 }
 394  
 395  	 // Executes the sql array returned by getTableSQL and getIndexSQL
 396  	function executeSQLArray($sql, $continueOnError = true)
 397  	 {
 398  	 	 $rez = 2;
 399  	 	 $conn = $this->connection;
 400  	 	 $saved = $conn->debug;
 401  	 	 foreach($sql as $line) {
 402  
 403  	 	 	 if ($this->debug) $conn->debug = true;
 404  	 	 	 $ok = $conn->execute($line);
 405  	 	 	 $conn->debug = $saved;
 406  	 	 	 if (!$ok) {
 407  	 	 	 	 if ($this->debug) ADOConnection::outp($conn->errorMsg());
 408  	 	 	 	 if (!$continueOnError) return 0;
 409  	 	 	 	 $rez = 1;
 410  	 	 	 }
 411  	 	 }
 412  	 	 return $rez;
 413  	 }
 414  
 415  	 /**
 416  	  	 Returns the actual type given a character code.
 417  
 418  	 	 C:  varchar
 419  	 	 X:  CLOB (character large object) or largest varchar size if CLOB is not supported
 420  	 	 C2: Multibyte varchar
 421  	 	 X2: Multibyte CLOB
 422  
 423  	 	 B:  BLOB (binary large object)
 424  
 425  	 	 D:  Date
 426  	 	 T:  Date-time
 427  	 	 L:  Integer field suitable for storing booleans (0 or 1)
 428  	 	 I:  Integer
 429  	 	 F:  Floating point number
 430  	 	 N:  Numeric or decimal number
 431  	 */
 432  
 433  	function actualType($meta)
 434  	 {
 435  	 	 $meta = strtoupper($meta);
 436  
 437  	 	 /*
 438  	 	 * Add support for custom meta types. We do this
 439  	 	 * first, that allows us to override existing types
 440  	 	 */
 441  	 	 if (isset($this->connection->customMetaTypes[$meta]))
 442  	 	 	 return $this->connection->customMetaTypes[$meta]['actual'];
 443  
 444  	 	 return $meta;
 445  	 }
 446  
 447  	function createDatabase($dbname,$options=false)
 448  	 {
 449  	 	 $options = $this->_options($options);
 450  	 	 $sql = array();
 451  
 452  	 	 $s = 'CREATE DATABASE ' . $this->nameQuote($dbname);
 453  	 	 if (isset($options[$this->upperName]))
 454  	 	 	 $s .= ' '.$options[$this->upperName];
 455  
 456  	 	 $sql[] = $s;
 457  	 	 return $sql;
 458  	 }
 459  
 460  	 /*
 461  	  Generates the SQL to create index. Returns an array of sql strings.
 462  	 */
 463  	function createIndexSQL($idxname, $tabname, $flds, $idxoptions = false)
 464  	 {
 465  	 	 if (!is_array($flds)) {
 466  	 	 	 $flds = explode(',',$flds);
 467  	 	 }
 468  
 469  	 	 foreach($flds as $key => $fld) {
 470  	 	 	 # some indexes can use partial fields, eg. index first 32 chars of "name" with NAME(32)
 471  	 	 	 $flds[$key] = $this->nameQuote($fld,$allowBrackets=true);
 472  	 	 }
 473  
 474  	 	 return $this->_indexSQL($this->nameQuote($idxname), $this->tableName($tabname), $flds, $this->_options($idxoptions));
 475  	 }
 476  
 477  	function dropIndexSQL ($idxname, $tabname = NULL)
 478  	 {
 479  	 	 return array(sprintf($this->dropIndex, $this->nameQuote($idxname), $this->tableName($tabname)));
 480  	 }
 481  
 482  	function setSchema($schema)
 483  	 {
 484  	 	 $this->schema = $schema;
 485  	 }
 486  
 487  	function addColumnSQL($tabname, $flds)
 488  	 {
 489  	 	 $tabname = $this->tableName($tabname);
 490  	 	 $sql = array();
 491  	 	 list($lines,$pkey,$idxs) = $this->_genFields($flds);
 492  	 	 // genfields can return FALSE at times
 493  	 	 if ($lines  == null) $lines = array();
 494  	 	 $alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' ';
 495  	 	 foreach($lines as $v) {
 496  	 	 	 $sql[] = $alter . $v;
 497  	 	 }
 498  	 	 if (is_array($idxs)) {
 499  	 	 	 foreach($idxs as $idx => $idxdef) {
 500  	 	 	 	 $sql_idxs = $this->createIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
 501  	 	 	 	 $sql = array_merge($sql, $sql_idxs);
 502  	 	 	 }
 503  	 	 }
 504  	 	 return $sql;
 505  	 }
 506  
 507  	 /**
 508  	  * Change the definition of one column
 509  	  *
 510  	  * As some DBMs can't do that on their own, you need to supply the complete definition of the new table,
 511  	  * to allow recreating the table and copying the content over to the new table
 512  	  * @param string $tabname table-name
 513  	  * @param string $flds column-name and type for the changed column
 514  	  * @param string $tableflds='' complete definition of the new table, eg. for postgres, default ''
 515  	  * @param array|string $tableoptions='' options for the new table see createTableSQL, default ''
 516  	  * @return array with SQL strings
 517  	  */
 518  	function alterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
 519  	 {
 520  	 	 $tabname = $this->tableName($tabname);
 521  	 	 $sql = array();
 522  	 	 list($lines,$pkey,$idxs) = $this->_genFields($flds);
 523  	 	 // genfields can return FALSE at times
 524  	 	 if ($lines == null) $lines = array();
 525  	 	 $alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' ';
 526  	 	 foreach($lines as $v) {
 527  	 	 	 $sql[] = $alter . $v;
 528  	 	 }
 529  	 	 if (is_array($idxs)) {
 530  	 	 	 foreach($idxs as $idx => $idxdef) {
 531  	 	 	 	 $sql_idxs = $this->createIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
 532  	 	 	 	 $sql = array_merge($sql, $sql_idxs);
 533  	 	 	 }
 534  
 535  	 	 }
 536  	 	 return $sql;
 537  	 }
 538  
 539  	 /**
 540  	  * Rename one column
 541  	  *
 542  	  * Some DBMs can only do this together with changeing the type of the column (even if that stays the same, eg. mysql)
 543  	  * @param string $tabname table-name
 544  	  * @param string $oldcolumn column-name to be renamed
 545  	  * @param string $newcolumn new column-name
 546  	  * @param string $flds='' complete column-definition-string like for addColumnSQL, only used by mysql atm., default=''
 547  	  * @return array with SQL strings
 548  	  */
 549  	function renameColumnSQL($tabname,$oldcolumn,$newcolumn,$flds='')
 550  	 {
 551  	 	 $tabname = $this->tableName($tabname);
 552  	 	 if ($flds) {
 553  	 	 	 list($lines,$pkey,$idxs) = $this->_genFields($flds);
 554  	 	 	 // genfields can return FALSE at times
 555  	 	 	 if ($lines == null) $lines = array();
 556  	 	 	 $first  = current($lines);
 557  	 	 	 list(,$column_def) = preg_split("/[\t ]+/",$first,2);
 558  	 	 }
 559  	 	 return array(sprintf($this->renameColumn,$tabname,$this->nameQuote($oldcolumn),$this->nameQuote($newcolumn),$column_def));
 560  	 }
 561  
 562  	 /**
 563  	  * Drop one column
 564  	  *
 565  	  * Some DBM's can't do that on their own, you need to supply the complete definition of the new table,
 566  	  * to allow, recreating the table and copying the content over to the new table
 567  	  * @param string $tabname table-name
 568  	  * @param string $flds column-name and type for the changed column
 569  	  * @param string $tableflds='' complete definition of the new table, eg. for postgres, default ''
 570  	  * @param array|string $tableoptions='' options for the new table see createTableSQL, default ''
 571  	  * @return array with SQL strings
 572  	  */
 573  	function dropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
 574  	 {
 575  	 	 $tabname = $this->tableName($tabname);
 576  	 	 if (!is_array($flds)) $flds = explode(',',$flds);
 577  	 	 $sql = array();
 578  	 	 $alter = 'ALTER TABLE ' . $tabname . $this->dropCol . ' ';
 579  	 	 foreach($flds as $v) {
 580  	 	 	 $sql[] = $alter . $this->nameQuote($v);
 581  	 	 }
 582  	 	 return $sql;
 583  	 }
 584  
 585  	function dropTableSQL($tabname)
 586  	 {
 587  	 	 return array (sprintf($this->dropTable, $this->tableName($tabname)));
 588  	 }
 589  
 590  	function renameTableSQL($tabname,$newname)
 591  	 {
 592  	 	 return array (sprintf($this->renameTable, $this->tableName($tabname),$this->tableName($newname)));
 593  	 }
 594  
 595  	 /**
 596  	  Generate the SQL to create table. Returns an array of sql strings.
 597  	 */
 598  	function createTableSQL($tabname, $flds, $tableoptions=array())
 599  	 {
 600  	 	 list($lines,$pkey,$idxs) = $this->_genFields($flds, true);
 601  	 	 // genfields can return FALSE at times
 602  	 	 if ($lines == null) $lines = array();
 603  
 604  	 	 $taboptions = $this->_options($tableoptions);
 605  	 	 $tabname = $this->tableName($tabname);
 606  	 	 $sql = $this->_tableSQL($tabname,$lines,$pkey,$taboptions);
 607  
 608  	 	 // ggiunta - 2006/10/12 - KLUDGE:
 609          // if we are on autoincrement, and table options includes REPLACE, the
 610          // autoincrement sequence has already been dropped on table creation sql, so
 611          // we avoid passing REPLACE to trigger creation code. This prevents
 612          // creating sql that double-drops the sequence
 613          if ($this->autoIncrement && isset($taboptions['REPLACE']))
 614          	 unset($taboptions['REPLACE']);
 615  	 	 $tsql = $this->_triggers($tabname,$taboptions);
 616  	 	 foreach($tsql as $s) $sql[] = $s;
 617  
 618  	 	 if (is_array($idxs)) {
 619  	 	 	 foreach($idxs as $idx => $idxdef) {
 620  	 	 	 	 $sql_idxs = $this->createIndexSql($idx, $tabname,  $idxdef['cols'], $idxdef['opts']);
 621  	 	 	 	 $sql = array_merge($sql, $sql_idxs);
 622  	 	 	 }
 623  	 	 }
 624  
 625  	 	 return $sql;
 626  	 }
 627  
 628  
 629  
 630  	function _genFields($flds,$widespacing=false)
 631  	 {
 632  	 	 if (is_string($flds)) {
 633  	 	 	 $padding = '     ';
 634  	 	 	 $txt = $flds.$padding;
 635  	 	 	 $flds = array();
 636  	 	 	 $flds0 = lens_ParseArgs($txt,',');
 637  	 	 	 $hasparam = false;
 638  	 	 	 foreach($flds0 as $f0) {
 639  	 	 	 	 $f1 = array();
 640  	 	 	 	 foreach($f0 as $token) {
 641  	 	 	 	 	 switch (strtoupper($token)) {
 642  	 	 	 	 	 case 'INDEX':
 643  	 	 	 	 	 	 $f1['INDEX'] = '';
 644  	 	 	 	 	 	 // fall through intentionally
 645  	 	 	 	 	 case 'CONSTRAINT':
 646  	 	 	 	 	 case 'DEFAULT':
 647  	 	 	 	 	 	 $hasparam = $token;
 648  	 	 	 	 	 	 break;
 649  	 	 	 	 	 default:
 650  	 	 	 	 	 	 if ($hasparam) $f1[$hasparam] = $token;
 651  	 	 	 	 	 	 else $f1[] = $token;
 652  	 	 	 	 	 	 $hasparam = false;
 653  	 	 	 	 	 	 break;
 654  	 	 	 	 	 }
 655  	 	 	 	 }
 656  	 	 	 	 // 'index' token without a name means single column index: name it after column
 657  	 	 	 	 if (array_key_exists('INDEX', $f1) && $f1['INDEX'] == '') {
 658  	 	 	 	 	 $f1['INDEX'] = isset($f0['NAME']) ? $f0['NAME'] : $f0[0];
 659  	 	 	 	 	 // check if column name used to create an index name was quoted
 660  	 	 	 	 	 if (($f1['INDEX'][0] == '"' || $f1['INDEX'][0] == "'" || $f1['INDEX'][0] == "`") &&
 661  	 	 	 	 	 	 ($f1['INDEX'][0] == substr($f1['INDEX'], -1))) {
 662  	 	 	 	 	 	 $f1['INDEX'] = $f1['INDEX'][0].'idx_'.substr($f1['INDEX'], 1, -1).$f1['INDEX'][0];
 663  	 	 	 	 	 }
 664  	 	 	 	 	 else
 665  	 	 	 	 	 	 $f1['INDEX'] = 'idx_'.$f1['INDEX'];
 666  	 	 	 	 }
 667  	 	 	 	 // reset it, so we don't get next field 1st token as INDEX...
 668  	 	 	 	 $hasparam = false;
 669  
 670  	 	 	 	 $flds[] = $f1;
 671  
 672  	 	 	 }
 673  	 	 }
 674  	 	 $this->autoIncrement = false;
 675  	 	 $lines = array();
 676  	 	 $pkey = array();
 677  	 	 $idxs = array();
 678  	 	 foreach($flds as $fld) {
 679  	 	 	 if (is_array($fld))
 680  	 	 	 	 $fld = array_change_key_case($fld,CASE_UPPER);
 681  	 	 	 $fname = false;
 682  	 	 	 $fdefault = false;
 683  	 	 	 $fautoinc = false;
 684  	 	 	 $ftype = false;
 685  	 	 	 $fsize = false;
 686  	 	 	 $fprec = false;
 687  	 	 	 $fprimary = false;
 688  	 	 	 $fnoquote = false;
 689  	 	 	 $fdefts = false;
 690  	 	 	 $fdefdate = false;
 691  	 	 	 $fconstraint = false;
 692  	 	 	 $fnotnull = false;
 693  	 	 	 $funsigned = false;
 694  	 	 	 $findex = '';
 695  	 	 	 $funiqueindex = false;
 696  	 	 	 $fOptions	   = array();
 697  
 698  	 	 	 //-----------------
 699  	 	 	 // Parse attributes
 700  	 	 	 foreach($fld as $attr => $v) {
 701  	 	 	 	 if ($attr == 2 && is_numeric($v))
 702  	 	 	 	 	 $attr = 'SIZE';
 703  	 	 	 	 elseif ($attr == 2 && strtoupper($ftype) == 'ENUM')
 704  	 	 	 	 	 $attr = 'ENUM';
 705  	 	 	 	 else if (is_numeric($attr) && $attr > 1 && !is_numeric($v))
 706  	 	 	 	 	 $attr = strtoupper($v);
 707  
 708  	 	 	 	 switch($attr) {
 709  	 	 	 	 case '0':
 710  	 	 	 	 case 'NAME': 	 $fname = $v; break;
 711  	 	 	 	 case '1':
 712  	 	 	 	 case 'TYPE':
 713  
 714  	 	 	 	 	 $ty = $v;
 715  	 	 	 	 	 $ftype = $this->actualType(strtoupper($v));
 716  	 	 	 	 	 break;
 717  
 718  	 	 	 	 case 'SIZE':
 719  	 	 	 	 	 $dotat = strpos($v,'.');
 720  	 	 	 	 	 if ($dotat === false)
 721  	 	 	 	 	 	 $dotat = strpos($v,',');
 722  	 	 	 	 	 if ($dotat === false)
 723  	 	 	 	 	 	 $fsize = $v;
 724  	 	 	 	 	 else {
 725  
 726  	 	 	 	 	 	 $fsize = substr($v,0,$dotat);
 727  	 	 	 	 	 	 $fprec = substr($v,$dotat+1);
 728  
 729  	 	 	 	 	 }
 730  	 	 	 	 	 break;
 731  	 	 	 	 case 'UNSIGNED': $funsigned = true; break;
 732  	 	 	 	 case 'AUTOINCREMENT':
 733  	 	 	 	 case 'AUTO':	 $fautoinc = true; $fnotnull = true; break;
 734  	 	 	 	 case 'KEY':
 735                  // a primary key col can be non unique in itself (if key spans many cols...)
 736  	 	 	 	 case 'PRIMARY':	 $fprimary = $v; $fnotnull = true; /*$funiqueindex = true;*/ break;
 737  	 	 	 	 case 'DEF':
 738  	 	 	 	 case 'DEFAULT': $fdefault = $v; break;
 739  	 	 	 	 case 'NOTNULL': $fnotnull = $v; break;
 740  	 	 	 	 case 'NOQUOTE': $fnoquote = $v; break;
 741  	 	 	 	 case 'DEFDATE': $fdefdate = $v; break;
 742  	 	 	 	 case 'DEFTIMESTAMP': $fdefts = $v; break;
 743  	 	 	 	 case 'CONSTRAINT': $fconstraint = $v; break;
 744  	 	 	 	 // let INDEX keyword create a 'very standard' index on column
 745  	 	 	 	 case 'INDEX': $findex = $v; break;
 746  	 	 	 	 case 'UNIQUE': $funiqueindex = true; break;
 747  	 	 	 	 case 'ENUM':
 748  	 	 	 	 	 $fOptions['ENUM'] = $v; break;
 749  	 	 	 	 } //switch
 750  	 	 	 } // foreach $fld
 751  
 752  	 	 	 //--------------------
 753  	 	 	 // VALIDATE FIELD INFO
 754  	 	 	 if (!strlen($fname)) {
 755  	 	 	 	 if ($this->debug) ADOConnection::outp("Undefined NAME");
 756  	 	 	 	 return false;
 757  	 	 	 }
 758  
 759  	 	 	 $fid = strtoupper(preg_replace('/^`(.+)`$/', '$1', $fname));
 760  	 	 	 $fname = $this->nameQuote($fname);
 761  
 762  	 	 	 if (!strlen($ftype)) {
 763  	 	 	 	 if ($this->debug) ADOConnection::outp("Undefined TYPE for field '$fname'");
 764  	 	 	 	 return false;
 765  	 	 	 } else {
 766  	 	 	 	 $ftype = strtoupper($ftype);
 767  	 	 	 }
 768  
 769  	 	 	 $ftype = $this->_getSize($ftype, $ty, $fsize, $fprec, $fOptions);
 770  
 771  	 	 	 if (($ty == 'X' || $ty == 'X2' || $ty == 'XL' || $ty == 'B') && !$this->blobAllowsNotNull)
 772  	 	 	 	 /*
 773  	 	 	 	 * some blob types do not accept nulls, so we override the
 774  	 	 	 	 * previously defined value
 775  	 	 	 	 */
 776  	 	 	 	 $fnotnull = false;
 777  
 778  	 	 	 if ($fprimary)
 779  	 	 	 	 $pkey[] = $fname;
 780  
 781  	 	 	 if (($ty == 'X' || $ty == 'X2' || $ty == 'XL' || $ty == 'B') && !$this->blobAllowsDefaultValue)
 782  	 	 	 	 /*
 783  	 	 	 	 * some databases do not allow blobs to have defaults, so we
 784  	 	 	 	 * override the previously defined value
 785  	 	 	 	 */
 786  	 	 	 	 $fdefault = false;
 787  
 788  	 	 	 // build list of indexes
 789  	 	 	 if ($findex != '') {
 790  	 	 	 	 if (array_key_exists($findex, $idxs)) {
 791  	 	 	 	 	 $idxs[$findex]['cols'][] = ($fname);
 792  	 	 	 	 	 if (in_array('UNIQUE', $idxs[$findex]['opts']) != $funiqueindex) {
 793  	 	 	 	 	 	 if ($this->debug) ADOConnection::outp("Index $findex defined once UNIQUE and once not");
 794  	 	 	 	 	 }
 795  	 	 	 	 	 if ($funiqueindex && !in_array('UNIQUE', $idxs[$findex]['opts']))
 796  	 	 	 	 	 	 $idxs[$findex]['opts'][] = 'UNIQUE';
 797  	 	 	 	 }
 798  	 	 	 	 else
 799  	 	 	 	 {
 800  	 	 	 	 	 $idxs[$findex] = array();
 801  	 	 	 	 	 $idxs[$findex]['cols'] = array($fname);
 802  	 	 	 	 	 if ($funiqueindex)
 803  	 	 	 	 	 	 $idxs[$findex]['opts'] = array('UNIQUE');
 804  	 	 	 	 	 else
 805  	 	 	 	 	 	 $idxs[$findex]['opts'] = array();
 806  	 	 	 	 }
 807  	 	 	 }
 808  
 809  	 	 	 //--------------------
 810  	 	 	 // CONSTRUCT FIELD SQL
 811  	 	 	 if ($fdefts) {
 812  	 	 	 	 if (substr($this->connection->databaseType,0,5) == 'mysql') {
 813  	 	 	 	 	 $ftype = 'TIMESTAMP';
 814  	 	 	 	 } else {
 815  	 	 	 	 	 $fdefault = $this->connection->sysTimeStamp;
 816  	 	 	 	 }
 817  	 	 	 } else if ($fdefdate) {
 818  	 	 	 	 if (substr($this->connection->databaseType,0,5) == 'mysql') {
 819  	 	 	 	 	 $ftype = 'TIMESTAMP';
 820  	 	 	 	 } else {
 821  	 	 	 	 	 $fdefault = $this->connection->sysDate;
 822  	 	 	 	 }
 823  	 	 	 } else if ($fdefault !== false && !$fnoquote) {
 824  	 	 	 	 if ($ty == 'C' or $ty == 'X' or
 825  	 	 	 	 	 ( substr($fdefault,0,1) != "'" && !is_numeric($fdefault))) {
 826  
 827  	 	 	 	 	 if (($ty == 'D' || $ty == 'T') && strtolower($fdefault) != 'null') {
 828  	 	 	 	 	 	 // convert default date into database-aware code
 829  	 	 	 	 	 	 if ($ty == 'T')
 830  	 	 	 	 	 	 {
 831  	 	 	 	 	 	 	 $fdefault = $this->connection->dbTimeStamp($fdefault);
 832  	 	 	 	 	 	 }
 833  	 	 	 	 	 	 else
 834  	 	 	 	 	 	 {
 835  	 	 	 	 	 	 	 $fdefault = $this->connection->dbDate($fdefault);
 836  	 	 	 	 	 	 }
 837  	 	 	 	 	 }
 838  	 	 	 	 	 else
 839  	 	 	 	 	 if (strlen($fdefault) != 1 && substr($fdefault,0,1) == ' ' && substr($fdefault,strlen($fdefault)-1) == ' ')
 840  	 	 	 	 	 	 $fdefault = trim($fdefault);
 841  	 	 	 	 	 else if (strtolower($fdefault) != 'null')
 842  	 	 	 	 	 	 $fdefault = $this->connection->qstr($fdefault);
 843  	 	 	 	 }
 844  	 	 	 }
 845  	 	 	 $suffix = $this->_createSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned);
 846  
 847  	 	 	 // add index creation
 848  	 	 	 if ($widespacing) $fname = str_pad($fname,24);
 849  
 850  	 	 	  // check for field names appearing twice
 851              if (array_key_exists($fid, $lines)) {
 852              	  ADOConnection::outp("Field '$fname' defined twice");
 853              }
 854  
 855  	 	 	 $lines[$fid] = $fname.' '.$ftype.$suffix;
 856  
 857  	 	 	 if ($fautoinc) $this->autoIncrement = true;
 858  	 	 } // foreach $flds
 859  
 860  	 	 return array($lines,$pkey,$idxs);
 861  	 }
 862  
 863  	 /**
 864  	 	  GENERATE THE SIZE PART OF THE DATATYPE
 865  	 	 	 $ftype is the actual type
 866  	 	 	 $ty is the type defined originally in the DDL
 867  	 */
 868  	function _getSize($ftype, $ty, $fsize, $fprec, $options=false)
 869  	 {
 870  	 	 if (strlen($fsize) && $ty != 'X' && $ty != 'B' && strpos($ftype,'(') === false) {
 871  	 	 	 $ftype .= "(".$fsize;
 872  	 	 	 if (strlen($fprec)) $ftype .= ",".$fprec;
 873  	 	 	 $ftype .= ')';
 874  	 	 }
 875  
 876  	 	 /*
 877  	 	 * Handle additional options
 878  	 	 */
 879  	 	 if (is_array($options))
 880  	 	 {
 881  	 	 	 foreach($options as $type=>$value)
 882  	 	 	 {
 883  	 	 	 	 switch ($type)
 884  	 	 	 	 {
 885  	 	 	 	 	 case 'ENUM':
 886  	 	 	 	 	 $ftype .= '(' . $value . ')';
 887  	 	 	 	 	 break;
 888  
 889  	 	 	 	 	 default:
 890  	 	 	 	 }
 891  	 	 	 }
 892  	 	 }
 893  
 894  	 	 return $ftype;
 895  	 }
 896  
 897  
 898  	 // return string must begin with space
 899  	function _createSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
 900  	 {
 901  	 	 $suffix = '';
 902  	 	 if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
 903  	 	 if ($fnotnull) $suffix .= ' NOT NULL';
 904  	 	 if ($fconstraint) $suffix .= ' '.$fconstraint;
 905  	 	 return $suffix;
 906  	 }
 907  
 908  	function _indexSQL($idxname, $tabname, $flds, $idxoptions)
 909  	 {
 910  	 	 $sql = array();
 911  
 912  	 	 if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) {
 913  	 	 	 $sql[] = sprintf ($this->dropIndex, $idxname);
 914  	 	 	 if ( isset($idxoptions['DROP']) )
 915  	 	 	 	 return $sql;
 916  	 	 }
 917  
 918  	 	 if ( empty ($flds) ) {
 919  	 	 	 return $sql;
 920  	 	 }
 921  
 922  	 	 $unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : '';
 923  
 924  	 	 $s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' ';
 925  
 926  	 	 if ( isset($idxoptions[$this->upperName]) )
 927  	 	 	 $s .= $idxoptions[$this->upperName];
 928  
 929  	 	 if ( is_array($flds) )
 930  	 	 	 $flds = implode(', ',$flds);
 931  	 	 $s .= '(' . $flds . ')';
 932  	 	 $sql[] = $s;
 933  
 934  	 	 return $sql;
 935  	 }
 936  
 937  	function _dropAutoIncrement($tabname)
 938  	 {
 939  	 	 return false;
 940  	 }
 941  
 942  	function _tableSQL($tabname,$lines,$pkey,$tableoptions)
 943  	 {
 944  	 	 $sql = array();
 945  
 946  	 	 if (isset($tableoptions['REPLACE']) || isset ($tableoptions['DROP'])) {
 947  	 	 	 $sql[] = sprintf($this->dropTable,$tabname);
 948  	 	 	 if ($this->autoIncrement) {
 949  	 	 	 	 $sInc = $this->_dropAutoIncrement($tabname);
 950  	 	 	 	 if ($sInc) $sql[] = $sInc;
 951  	 	 	 }
 952  	 	 	 if ( isset ($tableoptions['DROP']) ) {
 953  	 	 	 	 return $sql;
 954  	 	 	 }
 955  	 	 }
 956  
 957  	 	 $s = "CREATE TABLE $tabname (\n";
 958  	 	 $s .= implode(",\n", $lines);
 959  	 	 if (sizeof($pkey)>0) {
 960  	 	 	 $s .= ",\n                 PRIMARY KEY (";
 961  	 	 	 $s .= implode(", ",$pkey).")";
 962  	 	 }
 963  	 	 if (isset($tableoptions['CONSTRAINTS']))
 964  	 	 	 $s .= "\n".$tableoptions['CONSTRAINTS'];
 965  
 966  	 	 if (isset($tableoptions[$this->upperName.'_CONSTRAINTS']))
 967  	 	 	 $s .= "\n".$tableoptions[$this->upperName.'_CONSTRAINTS'];
 968  
 969  	 	 $s .= "\n)";
 970  	 	 if (isset($tableoptions[$this->upperName])) $s .= $tableoptions[$this->upperName];
 971  	 	 $sql[] = $s;
 972  
 973  	 	 return $sql;
 974  	 }
 975  
 976  	 /**
 977  	 	 GENERATE TRIGGERS IF NEEDED
 978  	 	 used when table has auto-incrementing field that is emulated using triggers
 979  	 */
 980  	function _triggers($tabname,$taboptions)
 981  	 {
 982  	 	 return array();
 983  	 }
 984  
 985  	 /**
 986  	 	 Sanitize options, so that array elements with no keys are promoted to keys
 987  	 */
 988  	function _options($opts)
 989  	 {
 990  	 	 if (!is_array($opts)) return array();
 991  	 	 $newopts = array();
 992  	 	 foreach($opts as $k => $v) {
 993  	 	 	 if (is_numeric($k)) $newopts[strtoupper($v)] = $v;
 994  	 	 	 else $newopts[strtoupper($k)] = $v;
 995  	 	 }
 996  	 	 return $newopts;
 997  	 }
 998  
 999  
1000  	function _getSizePrec($size)
1001  	 {
1002  	 	 $fsize = false;
1003  	 	 $fprec = false;
1004  	 	 $dotat = strpos($size,'.');
1005  	 	 if ($dotat === false) $dotat = strpos($size,',');
1006  	 	 if ($dotat === false) $fsize = $size;
1007  	 	 else {
1008  	 	 	 $fsize = substr($size,0,$dotat);
1009  	 	 	 $fprec = substr($size,$dotat+1);
1010  	 	 }
1011  	 	 return array($fsize, $fprec);
1012  	 }
1013  
1014  	 /**
1015  	  * This function changes/adds new fields to your table.
1016  	  *
1017  	  * You don't have to know if the col is new or not. It will check on its own.
1018  	  *
1019  	  * @param string   $tablename
1020  	  * @param string   $flds
1021  	  * @param string[] $tableoptions
1022  	  * @param bool     $dropOldFlds
1023  	  *
1024  	  * @return string[] Array of SQL Commands
1025  	  */
1026  	function changeTableSQL($tablename, $flds, $tableoptions = false, $dropOldFlds=false)
1027  	 {
1028  	 global $ADODB_FETCH_MODE;
1029  
1030  	 	 $save = $ADODB_FETCH_MODE;
1031  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
1032  	 	 if ($this->connection->fetchMode !== false) $savem = $this->connection->setFetchMode(false);
1033  
1034  	 	 // check table exists
1035  	 	 $save_handler = $this->connection->raiseErrorFn;
1036  	 	 $this->connection->raiseErrorFn = '';
1037  	 	 $cols = $this->metaColumns($tablename);
1038  	 	 $this->connection->raiseErrorFn = $save_handler;
1039  
1040  	 	 if (isset($savem)) $this->connection->setFetchMode($savem);
1041  	 	 $ADODB_FETCH_MODE = $save;
1042  
1043  	 	 if ( empty($cols)) {
1044  	 	 	 return $this->createTableSQL($tablename, $flds, $tableoptions);
1045  	 	 }
1046  
1047  	 	 if (is_array($flds)) {
1048  	 	 // Cycle through the update fields, comparing
1049  	 	 // existing fields to fields to update.
1050  	 	 // if the Metatype and size is exactly the
1051  	 	 // same, ignore - by Mark Newham
1052  	 	 	 $holdflds = array();
1053  	 	 	 foreach($flds as $k=>$v) {
1054  	 	 	 	 if ( isset($cols[$k]) && is_object($cols[$k]) ) {
1055  	 	 	 	 	 // If already not allowing nulls, then don't change
1056  	 	 	 	 	 $obj = $cols[$k];
1057  	 	 	 	 	 if (isset($obj->not_null) && $obj->not_null)
1058  	 	 	 	 	 	 $v = str_replace('NOT NULL','',$v);
1059  	 	 	 	 	 if (isset($obj->auto_increment) && $obj->auto_increment && empty($v['AUTOINCREMENT']))
1060  	 	 	 	 	     $v = str_replace('AUTOINCREMENT','',$v);
1061  
1062  	 	 	 	 	 $c = $cols[$k];
1063  	 	 	 	 	 $ml = $c->max_length;
1064  	 	 	 	 	 $mt = $this->metaType($c->type,$ml);
1065  
1066  	 	 	 	 	 if (isset($c->scale)) $sc = $c->scale;
1067  	 	 	 	 	 else $sc = 99; // always force change if scale not known.
1068  
1069  	 	 	 	 	 if ($sc == -1) $sc = false;
1070  	 	 	 	 	 list($fsize, $fprec) = $this->_getSizePrec($v['SIZE']);
1071  
1072  	 	 	 	 	 if ($ml == -1) $ml = '';
1073  	 	 	 	 	 if ($mt == 'X') $ml = $v['SIZE'];
1074  	 	 	 	 	 if (($mt != $v['TYPE']) || ($ml != $fsize || $sc != $fprec) || (isset($v['AUTOINCREMENT']) && $v['AUTOINCREMENT'] != $obj->auto_increment)) {
1075  	 	 	 	 	 	 $holdflds[$k] = $v;
1076  	 	 	 	 	 }
1077  	 	 	 	 } else {
1078  	 	 	 	 	 $holdflds[$k] = $v;
1079  	 	 	 	 }
1080  	 	 	 }
1081  	 	 	 $flds = $holdflds;
1082  	 	 }
1083  
1084  	 	 $sql = $this->alterColumnSql($tablename, $flds);
1085  
1086  	 	 if ($dropOldFlds) {
1087  	 	 	 foreach ($cols as $id => $v) {
1088  	 	 	 	 if (!isset($lines[$id])) {
1089  	 	 	 	 	 $sql[] = $this->dropColumnSQL($tablename, $flds);
1090  	 	 	 	 }
1091  	 	 	 }
1092  	 	 }
1093  	 	 return $sql;
1094  	 }
1095  } // class