Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

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