Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 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  	 	 return $meta;
 431  	 }
 432  
 433  	function createDatabase($dbname,$options=false)
 434  	 {
 435  	 	 $options = $this->_options($options);
 436  	 	 $sql = array();
 437  
 438  	 	 $s = 'CREATE DATABASE ' . $this->nameQuote($dbname);
 439  	 	 if (isset($options[$this->upperName]))
 440  	 	 	 $s .= ' '.$options[$this->upperName];
 441  
 442  	 	 $sql[] = $s;
 443  	 	 return $sql;
 444  	 }
 445  
 446  	 /*
 447  	  Generates the SQL to create index. Returns an array of sql strings.
 448  	 */
 449  	function createIndexSQL($idxname, $tabname, $flds, $idxoptions = false)
 450  	 {
 451  	 	 if (!is_array($flds)) {
 452  	 	 	 $flds = explode(',',$flds);
 453  	 	 }
 454  
 455  	 	 foreach($flds as $key => $fld) {
 456  	 	 	 # some indexes can use partial fields, eg. index first 32 chars of "name" with NAME(32)
 457  	 	 	 $flds[$key] = $this->nameQuote($fld,$allowBrackets=true);
 458  	 	 }
 459  
 460  	 	 return $this->_indexSQL($this->nameQuote($idxname), $this->tableName($tabname), $flds, $this->_options($idxoptions));
 461  	 }
 462  
 463  	function dropIndexSQL ($idxname, $tabname = NULL)
 464  	 {
 465  	 	 return array(sprintf($this->dropIndex, $this->nameQuote($idxname), $this->tableName($tabname)));
 466  	 }
 467  
 468  	function setSchema($schema)
 469  	 {
 470  	 	 $this->schema = $schema;
 471  	 }
 472  
 473  	function addColumnSQL($tabname, $flds)
 474  	 {
 475  	 	 $tabname = $this->tableName($tabname);
 476  	 	 $sql = array();
 477  	 	 list($lines,$pkey,$idxs) = $this->_genFields($flds);
 478  	 	 // genfields can return FALSE at times
 479  	 	 if ($lines  == null) $lines = array();
 480  	 	 $alter = 'ALTER TABLE ' . $tabname . $this->addCol . ' ';
 481  	 	 foreach($lines as $v) {
 482  	 	 	 $sql[] = $alter . $v;
 483  	 	 }
 484  	 	 if (is_array($idxs)) {
 485  	 	 	 foreach($idxs as $idx => $idxdef) {
 486  	 	 	 	 $sql_idxs = $this->createIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
 487  	 	 	 	 $sql = array_merge($sql, $sql_idxs);
 488  	 	 	 }
 489  	 	 }
 490  	 	 return $sql;
 491  	 }
 492  
 493  	 /**
 494  	  * Change the definition of one column
 495  	  *
 496  	  * As some DBMs can't do that on their own, you need to supply the complete definition of the new table,
 497  	  * to allow recreating the table and copying the content over to the new table
 498  	  * @param string $tabname table-name
 499  	  * @param string $flds column-name and type for the changed column
 500  	  * @param string $tableflds='' complete definition of the new table, eg. for postgres, default ''
 501  	  * @param array/string $tableoptions='' options for the new table see createTableSQL, default ''
 502  	  * @return array with SQL strings
 503  	  */
 504  	function alterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
 505  	 {
 506  	 	 $tabname = $this->tableName($tabname);
 507  	 	 $sql = array();
 508  	 	 list($lines,$pkey,$idxs) = $this->_genFields($flds);
 509  	 	 // genfields can return FALSE at times
 510  	 	 if ($lines == null) $lines = array();
 511  	 	 $alter = 'ALTER TABLE ' . $tabname . $this->alterCol . ' ';
 512  	 	 foreach($lines as $v) {
 513  	 	 	 $sql[] = $alter . $v;
 514  	 	 }
 515  	 	 if (is_array($idxs)) {
 516  	 	 	 foreach($idxs as $idx => $idxdef) {
 517  	 	 	 	 $sql_idxs = $this->createIndexSql($idx, $tabname, $idxdef['cols'], $idxdef['opts']);
 518  	 	 	 	 $sql = array_merge($sql, $sql_idxs);
 519  	 	 	 }
 520  
 521  	 	 }
 522  	 	 return $sql;
 523  	 }
 524  
 525  	 /**
 526  	  * Rename one column
 527  	  *
 528  	  * Some DBMs can only do this together with changeing the type of the column (even if that stays the same, eg. mysql)
 529  	  * @param string $tabname table-name
 530  	  * @param string $oldcolumn column-name to be renamed
 531  	  * @param string $newcolumn new column-name
 532  	  * @param string $flds='' complete column-definition-string like for addColumnSQL, only used by mysql atm., default=''
 533  	  * @return array with SQL strings
 534  	  */
 535  	function renameColumnSQL($tabname,$oldcolumn,$newcolumn,$flds='')
 536  	 {
 537  	 	 $tabname = $this->tableName($tabname);
 538  	 	 if ($flds) {
 539  	 	 	 list($lines,$pkey,$idxs) = $this->_genFields($flds);
 540  	 	 	 // genfields can return FALSE at times
 541  	 	 	 if ($lines == null) $lines = array();
 542  	 	 	 $first  = current($lines);
 543  	 	 	 list(,$column_def) = preg_split("/[\t ]+/",$first,2);
 544  	 	 }
 545  	 	 return array(sprintf($this->renameColumn,$tabname,$this->nameQuote($oldcolumn),$this->nameQuote($newcolumn),$column_def));
 546  	 }
 547  
 548  	 /**
 549  	  * Drop one column
 550  	  *
 551  	  * Some DBM's can't do that on their own, you need to supply the complete definition of the new table,
 552  	  * to allow, recreating the table and copying the content over to the new table
 553  	  * @param string $tabname table-name
 554  	  * @param string $flds column-name and type for the changed column
 555  	  * @param string $tableflds='' complete definition of the new table, eg. for postgres, default ''
 556  	  * @param array/string $tableoptions='' options for the new table see createTableSQL, default ''
 557  	  * @return array with SQL strings
 558  	  */
 559  	function dropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='')
 560  	 {
 561  	 	 $tabname = $this->tableName($tabname);
 562  	 	 if (!is_array($flds)) $flds = explode(',',$flds);
 563  	 	 $sql = array();
 564  	 	 $alter = 'ALTER TABLE ' . $tabname . $this->dropCol . ' ';
 565  	 	 foreach($flds as $v) {
 566  	 	 	 $sql[] = $alter . $this->nameQuote($v);
 567  	 	 }
 568  	 	 return $sql;
 569  	 }
 570  
 571  	function dropTableSQL($tabname)
 572  	 {
 573  	 	 return array (sprintf($this->dropTable, $this->tableName($tabname)));
 574  	 }
 575  
 576  	function renameTableSQL($tabname,$newname)
 577  	 {
 578  	 	 return array (sprintf($this->renameTable, $this->tableName($tabname),$this->tableName($newname)));
 579  	 }
 580  
 581  	 /**
 582  	  Generate the SQL to create table. Returns an array of sql strings.
 583  	 */
 584  	function createTableSQL($tabname, $flds, $tableoptions=array())
 585  	 {
 586  	 	 list($lines,$pkey,$idxs) = $this->_genFields($flds, true);
 587  	 	 // genfields can return FALSE at times
 588  	 	 if ($lines == null) $lines = array();
 589  
 590  	 	 $taboptions = $this->_options($tableoptions);
 591  	 	 $tabname = $this->tableName($tabname);
 592  	 	 $sql = $this->_tableSQL($tabname,$lines,$pkey,$taboptions);
 593  
 594  	 	 // ggiunta - 2006/10/12 - KLUDGE:
 595          // if we are on autoincrement, and table options includes REPLACE, the
 596          // autoincrement sequence has already been dropped on table creation sql, so
 597          // we avoid passing REPLACE to trigger creation code. This prevents
 598          // creating sql that double-drops the sequence
 599          if ($this->autoIncrement && isset($taboptions['REPLACE']))
 600          	 unset($taboptions['REPLACE']);
 601  	 	 $tsql = $this->_triggers($tabname,$taboptions);
 602  	 	 foreach($tsql as $s) $sql[] = $s;
 603  
 604  	 	 if (is_array($idxs)) {
 605  	 	 	 foreach($idxs as $idx => $idxdef) {
 606  	 	 	 	 $sql_idxs = $this->createIndexSql($idx, $tabname,  $idxdef['cols'], $idxdef['opts']);
 607  	 	 	 	 $sql = array_merge($sql, $sql_idxs);
 608  	 	 	 }
 609  	 	 }
 610  
 611  	 	 return $sql;
 612  	 }
 613  
 614  
 615  
 616  	function _genFields($flds,$widespacing=false)
 617  	 {
 618  	 	 if (is_string($flds)) {
 619  	 	 	 $padding = '     ';
 620  	 	 	 $txt = $flds.$padding;
 621  	 	 	 $flds = array();
 622  	 	 	 $flds0 = lens_ParseArgs($txt,',');
 623  	 	 	 $hasparam = false;
 624  	 	 	 foreach($flds0 as $f0) {
 625  	 	 	 	 $f1 = array();
 626  	 	 	 	 foreach($f0 as $token) {
 627  	 	 	 	 	 switch (strtoupper($token)) {
 628  	 	 	 	 	 case 'INDEX':
 629  	 	 	 	 	 	 $f1['INDEX'] = '';
 630  	 	 	 	 	 	 // fall through intentionally
 631  	 	 	 	 	 case 'CONSTRAINT':
 632  	 	 	 	 	 case 'DEFAULT':
 633  	 	 	 	 	 	 $hasparam = $token;
 634  	 	 	 	 	 	 break;
 635  	 	 	 	 	 default:
 636  	 	 	 	 	 	 if ($hasparam) $f1[$hasparam] = $token;
 637  	 	 	 	 	 	 else $f1[] = $token;
 638  	 	 	 	 	 	 $hasparam = false;
 639  	 	 	 	 	 	 break;
 640  	 	 	 	 	 }
 641  	 	 	 	 }
 642  	 	 	 	 // 'index' token without a name means single column index: name it after column
 643  	 	 	 	 if (array_key_exists('INDEX', $f1) && $f1['INDEX'] == '') {
 644  	 	 	 	 	 $f1['INDEX'] = isset($f0['NAME']) ? $f0['NAME'] : $f0[0];
 645  	 	 	 	 	 // check if column name used to create an index name was quoted
 646  	 	 	 	 	 if (($f1['INDEX'][0] == '"' || $f1['INDEX'][0] == "'" || $f1['INDEX'][0] == "`") &&
 647  	 	 	 	 	 	 ($f1['INDEX'][0] == substr($f1['INDEX'], -1))) {
 648  	 	 	 	 	 	 $f1['INDEX'] = $f1['INDEX'][0].'idx_'.substr($f1['INDEX'], 1, -1).$f1['INDEX'][0];
 649  	 	 	 	 	 }
 650  	 	 	 	 	 else
 651  	 	 	 	 	 	 $f1['INDEX'] = 'idx_'.$f1['INDEX'];
 652  	 	 	 	 }
 653  	 	 	 	 // reset it, so we don't get next field 1st token as INDEX...
 654  	 	 	 	 $hasparam = false;
 655  
 656  	 	 	 	 $flds[] = $f1;
 657  
 658  	 	 	 }
 659  	 	 }
 660  	 	 $this->autoIncrement = false;
 661  	 	 $lines = array();
 662  	 	 $pkey = array();
 663  	 	 $idxs = array();
 664  	 	 foreach($flds as $fld) {
 665  	 	 	 if (is_array($fld))
 666  	 	 	 	 $fld = array_change_key_case($fld,CASE_UPPER);
 667  	 	 	 $fname = false;
 668  	 	 	 $fdefault = false;
 669  	 	 	 $fautoinc = false;
 670  	 	 	 $ftype = false;
 671  	 	 	 $fsize = false;
 672  	 	 	 $fprec = false;
 673  	 	 	 $fprimary = false;
 674  	 	 	 $fnoquote = false;
 675  	 	 	 $fdefts = false;
 676  	 	 	 $fdefdate = false;
 677  	 	 	 $fconstraint = false;
 678  	 	 	 $fnotnull = false;
 679  	 	 	 $funsigned = false;
 680  	 	 	 $findex = '';
 681  	 	 	 $funiqueindex = false;
 682  	 	 	 $fOptions	   = array();
 683  
 684  	 	 	 //-----------------
 685  	 	 	 // Parse attributes
 686  	 	 	 foreach($fld as $attr => $v) {
 687  	 	 	 	 if ($attr == 2 && is_numeric($v))
 688  	 	 	 	 	 $attr = 'SIZE';
 689  	 	 	 	 elseif ($attr == 2 && strtoupper($ftype) == 'ENUM')
 690  	 	 	 	 	 $attr = 'ENUM';
 691  	 	 	 	 else if (is_numeric($attr) && $attr > 1 && !is_numeric($v))
 692  	 	 	 	 	 $attr = strtoupper($v);
 693  
 694  	 	 	 	 switch($attr) {
 695  	 	 	 	 case '0':
 696  	 	 	 	 case 'NAME': 	 $fname = $v; break;
 697  	 	 	 	 case '1':
 698  	 	 	 	 case 'TYPE': 	 $ty = $v; $ftype = $this->actualType(strtoupper($v)); break;
 699  
 700  	 	 	 	 case 'SIZE':
 701  	 	 	 	 	 	 	 	 $dotat = strpos($v,'.'); if ($dotat === false) $dotat = strpos($v,',');
 702  	 	 	 	 	 	 	 	 if ($dotat === false) $fsize = $v;
 703  	 	 	 	 	 	 	 	 else {
 704  	 	 	 	 	 	 	 	 	 $fsize = substr($v,0,$dotat);
 705  	 	 	 	 	 	 	 	 	 $fprec = substr($v,$dotat+1);
 706  	 	 	 	 	 	 	 	 }
 707  	 	 	 	 	 	 	 	 break;
 708  	 	 	 	 case 'UNSIGNED': $funsigned = true; break;
 709  	 	 	 	 case 'AUTOINCREMENT':
 710  	 	 	 	 case 'AUTO':	 $fautoinc = true; $fnotnull = true; break;
 711  	 	 	 	 case 'KEY':
 712                  // a primary key col can be non unique in itself (if key spans many cols...)
 713  	 	 	 	 case 'PRIMARY':	 $fprimary = $v; $fnotnull = true; /*$funiqueindex = true;*/ break;
 714  	 	 	 	 case 'DEF':
 715  	 	 	 	 case 'DEFAULT': $fdefault = $v; break;
 716  	 	 	 	 case 'NOTNULL': $fnotnull = $v; break;
 717  	 	 	 	 case 'NOQUOTE': $fnoquote = $v; break;
 718  	 	 	 	 case 'DEFDATE': $fdefdate = $v; break;
 719  	 	 	 	 case 'DEFTIMESTAMP': $fdefts = $v; break;
 720  	 	 	 	 case 'CONSTRAINT': $fconstraint = $v; break;
 721  	 	 	 	 // let INDEX keyword create a 'very standard' index on column
 722  	 	 	 	 case 'INDEX': $findex = $v; break;
 723  	 	 	 	 case 'UNIQUE': $funiqueindex = true; break;
 724  	 	 	 	 case 'ENUM':
 725  	 	 	 	 	 $fOptions['ENUM'] = $v; break;
 726  	 	 	 	 } //switch
 727  	 	 	 } // foreach $fld
 728  
 729  	 	 	 //--------------------
 730  	 	 	 // VALIDATE FIELD INFO
 731  	 	 	 if (!strlen($fname)) {
 732  	 	 	 	 if ($this->debug) ADOConnection::outp("Undefined NAME");
 733  	 	 	 	 return false;
 734  	 	 	 }
 735  
 736  	 	 	 $fid = strtoupper(preg_replace('/^`(.+)`$/', '$1', $fname));
 737  	 	 	 $fname = $this->nameQuote($fname);
 738  
 739  	 	 	 if (!strlen($ftype)) {
 740  	 	 	 	 if ($this->debug) ADOConnection::outp("Undefined TYPE for field '$fname'");
 741  	 	 	 	 return false;
 742  	 	 	 } else {
 743  	 	 	 	 $ftype = strtoupper($ftype);
 744  	 	 	 }
 745  
 746  	 	 	 $ftype = $this->_getSize($ftype, $ty, $fsize, $fprec, $fOptions);
 747  
 748  	 	 	 if (($ty == 'X' || $ty == 'X2' || $ty == 'XL' || $ty == 'B') && !$this->blobAllowsNotNull)
 749  	 	 	 	 /*
 750  	 	 	 	 * some blob types do not accept nulls, so we override the
 751  	 	 	 	 * previously defined value
 752  	 	 	 	 */
 753  	 	 	 	 $fnotnull = false;
 754  
 755  	 	 	 if ($fprimary)
 756  	 	 	 	 $pkey[] = $fname;
 757  
 758  	 	 	 if (($ty == 'X' || $ty == 'X2' || $ty == 'XL' || $ty == 'B') && !$this->blobAllowsDefaultValue)
 759  	 	 	 	 /*
 760  	 	 	 	 * some databases do not allow blobs to have defaults, so we
 761  	 	 	 	 * override the previously defined value
 762  	 	 	 	 */
 763  	 	 	 	 $fdefault = false;
 764  
 765  	 	 	 // build list of indexes
 766  	 	 	 if ($findex != '') {
 767  	 	 	 	 if (array_key_exists($findex, $idxs)) {
 768  	 	 	 	 	 $idxs[$findex]['cols'][] = ($fname);
 769  	 	 	 	 	 if (in_array('UNIQUE', $idxs[$findex]['opts']) != $funiqueindex) {
 770  	 	 	 	 	 	 if ($this->debug) ADOConnection::outp("Index $findex defined once UNIQUE and once not");
 771  	 	 	 	 	 }
 772  	 	 	 	 	 if ($funiqueindex && !in_array('UNIQUE', $idxs[$findex]['opts']))
 773  	 	 	 	 	 	 $idxs[$findex]['opts'][] = 'UNIQUE';
 774  	 	 	 	 }
 775  	 	 	 	 else
 776  	 	 	 	 {
 777  	 	 	 	 	 $idxs[$findex] = array();
 778  	 	 	 	 	 $idxs[$findex]['cols'] = array($fname);
 779  	 	 	 	 	 if ($funiqueindex)
 780  	 	 	 	 	 	 $idxs[$findex]['opts'] = array('UNIQUE');
 781  	 	 	 	 	 else
 782  	 	 	 	 	 	 $idxs[$findex]['opts'] = array();
 783  	 	 	 	 }
 784  	 	 	 }
 785  
 786  	 	 	 //--------------------
 787  	 	 	 // CONSTRUCT FIELD SQL
 788  	 	 	 if ($fdefts) {
 789  	 	 	 	 if (substr($this->connection->databaseType,0,5) == 'mysql') {
 790  	 	 	 	 	 $ftype = 'TIMESTAMP';
 791  	 	 	 	 } else {
 792  	 	 	 	 	 $fdefault = $this->connection->sysTimeStamp;
 793  	 	 	 	 }
 794  	 	 	 } else if ($fdefdate) {
 795  	 	 	 	 if (substr($this->connection->databaseType,0,5) == 'mysql') {
 796  	 	 	 	 	 $ftype = 'TIMESTAMP';
 797  	 	 	 	 } else {
 798  	 	 	 	 	 $fdefault = $this->connection->sysDate;
 799  	 	 	 	 }
 800  	 	 	 } else if ($fdefault !== false && !$fnoquote) {
 801  	 	 	 	 if ($ty == 'C' or $ty == 'X' or
 802  	 	 	 	 	 ( substr($fdefault,0,1) != "'" && !is_numeric($fdefault))) {
 803  
 804  	 	 	 	 	 if (($ty == 'D' || $ty == 'T') && strtolower($fdefault) != 'null') {
 805  	 	 	 	 	 	 // convert default date into database-aware code
 806  	 	 	 	 	 	 if ($ty == 'T')
 807  	 	 	 	 	 	 {
 808  	 	 	 	 	 	 	 $fdefault = $this->connection->dbTimeStamp($fdefault);
 809  	 	 	 	 	 	 }
 810  	 	 	 	 	 	 else
 811  	 	 	 	 	 	 {
 812  	 	 	 	 	 	 	 $fdefault = $this->connection->dbDate($fdefault);
 813  	 	 	 	 	 	 }
 814  	 	 	 	 	 }
 815  	 	 	 	 	 else
 816  	 	 	 	 	 if (strlen($fdefault) != 1 && substr($fdefault,0,1) == ' ' && substr($fdefault,strlen($fdefault)-1) == ' ')
 817  	 	 	 	 	 	 $fdefault = trim($fdefault);
 818  	 	 	 	 	 else if (strtolower($fdefault) != 'null')
 819  	 	 	 	 	 	 $fdefault = $this->connection->qstr($fdefault);
 820  	 	 	 	 }
 821  	 	 	 }
 822  	 	 	 $suffix = $this->_createSuffix($fname,$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned);
 823  
 824  	 	 	 // add index creation
 825  	 	 	 if ($widespacing) $fname = str_pad($fname,24);
 826  
 827  	 	 	  // check for field names appearing twice
 828              if (array_key_exists($fid, $lines)) {
 829              	  ADOConnection::outp("Field '$fname' defined twice");
 830              }
 831  
 832  	 	 	 $lines[$fid] = $fname.' '.$ftype.$suffix;
 833  
 834  	 	 	 if ($fautoinc) $this->autoIncrement = true;
 835  	 	 } // foreach $flds
 836  
 837  	 	 return array($lines,$pkey,$idxs);
 838  	 }
 839  
 840  	 /**
 841  	 	  GENERATE THE SIZE PART OF THE DATATYPE
 842  	 	 	 $ftype is the actual type
 843  	 	 	 $ty is the type defined originally in the DDL
 844  	 */
 845  	function _getSize($ftype, $ty, $fsize, $fprec, $options=false)
 846  	 {
 847  	 	 if (strlen($fsize) && $ty != 'X' && $ty != 'B' && strpos($ftype,'(') === false) {
 848  	 	 	 $ftype .= "(".$fsize;
 849  	 	 	 if (strlen($fprec)) $ftype .= ",".$fprec;
 850  	 	 	 $ftype .= ')';
 851  	 	 }
 852  
 853  	 	 /*
 854  	 	 * Handle additional options
 855  	 	 */
 856  	 	 if (is_array($options))
 857  	 	 {
 858  	 	 	 foreach($options as $type=>$value)
 859  	 	 	 {
 860  	 	 	 	 switch ($type)
 861  	 	 	 	 {
 862  	 	 	 	 	 case 'ENUM':
 863  	 	 	 	 	 $ftype .= '(' . $value . ')';
 864  	 	 	 	 	 break;
 865  
 866  	 	 	 	 	 default:
 867  	 	 	 	 }
 868  	 	 	 }
 869  	 	 }
 870  
 871  	 	 return $ftype;
 872  	 }
 873  
 874  
 875  	 // return string must begin with space
 876  	function _createSuffix($fname,&$ftype,$fnotnull,$fdefault,$fautoinc,$fconstraint,$funsigned)
 877  	 {
 878  	 	 $suffix = '';
 879  	 	 if (strlen($fdefault)) $suffix .= " DEFAULT $fdefault";
 880  	 	 if ($fnotnull) $suffix .= ' NOT NULL';
 881  	 	 if ($fconstraint) $suffix .= ' '.$fconstraint;
 882  	 	 return $suffix;
 883  	 }
 884  
 885  	function _indexSQL($idxname, $tabname, $flds, $idxoptions)
 886  	 {
 887  	 	 $sql = array();
 888  
 889  	 	 if ( isset($idxoptions['REPLACE']) || isset($idxoptions['DROP']) ) {
 890  	 	 	 $sql[] = sprintf ($this->dropIndex, $idxname);
 891  	 	 	 if ( isset($idxoptions['DROP']) )
 892  	 	 	 	 return $sql;
 893  	 	 }
 894  
 895  	 	 if ( empty ($flds) ) {
 896  	 	 	 return $sql;
 897  	 	 }
 898  
 899  	 	 $unique = isset($idxoptions['UNIQUE']) ? ' UNIQUE' : '';
 900  
 901  	 	 $s = 'CREATE' . $unique . ' INDEX ' . $idxname . ' ON ' . $tabname . ' ';
 902  
 903  	 	 if ( isset($idxoptions[$this->upperName]) )
 904  	 	 	 $s .= $idxoptions[$this->upperName];
 905  
 906  	 	 if ( is_array($flds) )
 907  	 	 	 $flds = implode(', ',$flds);
 908  	 	 $s .= '(' . $flds . ')';
 909  	 	 $sql[] = $s;
 910  
 911  	 	 return $sql;
 912  	 }
 913  
 914  	function _dropAutoIncrement($tabname)
 915  	 {
 916  	 	 return false;
 917  	 }
 918  
 919  	function _tableSQL($tabname,$lines,$pkey,$tableoptions)
 920  	 {
 921  	 	 $sql = array();
 922  
 923  	 	 if (isset($tableoptions['REPLACE']) || isset ($tableoptions['DROP'])) {
 924  	 	 	 $sql[] = sprintf($this->dropTable,$tabname);
 925  	 	 	 if ($this->autoIncrement) {
 926  	 	 	 	 $sInc = $this->_dropAutoIncrement($tabname);
 927  	 	 	 	 if ($sInc) $sql[] = $sInc;
 928  	 	 	 }
 929  	 	 	 if ( isset ($tableoptions['DROP']) ) {
 930  	 	 	 	 return $sql;
 931  	 	 	 }
 932  	 	 }
 933  
 934  	 	 $s = "CREATE TABLE $tabname (\n";
 935  	 	 $s .= implode(",\n", $lines);
 936  	 	 if (sizeof($pkey)>0) {
 937  	 	 	 $s .= ",\n                 PRIMARY KEY (";
 938  	 	 	 $s .= implode(", ",$pkey).")";
 939  	 	 }
 940  	 	 if (isset($tableoptions['CONSTRAINTS']))
 941  	 	 	 $s .= "\n".$tableoptions['CONSTRAINTS'];
 942  
 943  	 	 if (isset($tableoptions[$this->upperName.'_CONSTRAINTS']))
 944  	 	 	 $s .= "\n".$tableoptions[$this->upperName.'_CONSTRAINTS'];
 945  
 946  	 	 $s .= "\n)";
 947  	 	 if (isset($tableoptions[$this->upperName])) $s .= $tableoptions[$this->upperName];
 948  	 	 $sql[] = $s;
 949  
 950  	 	 return $sql;
 951  	 }
 952  
 953  	 /**
 954  	 	 GENERATE TRIGGERS IF NEEDED
 955  	 	 used when table has auto-incrementing field that is emulated using triggers
 956  	 */
 957  	function _triggers($tabname,$taboptions)
 958  	 {
 959  	 	 return array();
 960  	 }
 961  
 962  	 /**
 963  	 	 Sanitize options, so that array elements with no keys are promoted to keys
 964  	 */
 965  	function _options($opts)
 966  	 {
 967  	 	 if (!is_array($opts)) return array();
 968  	 	 $newopts = array();
 969  	 	 foreach($opts as $k => $v) {
 970  	 	 	 if (is_numeric($k)) $newopts[strtoupper($v)] = $v;
 971  	 	 	 else $newopts[strtoupper($k)] = $v;
 972  	 	 }
 973  	 	 return $newopts;
 974  	 }
 975  
 976  
 977  	function _getSizePrec($size)
 978  	 {
 979  	 	 $fsize = false;
 980  	 	 $fprec = false;
 981  	 	 $dotat = strpos($size,'.');
 982  	 	 if ($dotat === false) $dotat = strpos($size,',');
 983  	 	 if ($dotat === false) $fsize = $size;
 984  	 	 else {
 985  	 	 	 $fsize = substr($size,0,$dotat);
 986  	 	 	 $fprec = substr($size,$dotat+1);
 987  	 	 }
 988  	 	 return array($fsize, $fprec);
 989  	 }
 990  
 991  	 /**
 992  	 "Florian Buzin [ easywe ]" <florian.buzin#easywe.de>
 993  
 994  	 This function changes/adds new fields to your table. You don't
 995  	 have to know if the col is new or not. It will check on its own.
 996  	 */
 997  	function changeTableSQL($tablename, $flds, $tableoptions = false, $dropOldFlds=false)
 998  	 {
 999  	 global $ADODB_FETCH_MODE;
1000  
1001  	 	 $save = $ADODB_FETCH_MODE;
1002  	 	 $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
1003  	 	 if ($this->connection->fetchMode !== false) $savem = $this->connection->setFetchMode(false);
1004  
1005  	 	 // check table exists
1006  	 	 $save_handler = $this->connection->raiseErrorFn;
1007  	 	 $this->connection->raiseErrorFn = '';
1008  	 	 $cols = $this->metaColumns($tablename);
1009  	 	 $this->connection->raiseErrorFn = $save_handler;
1010  
1011  	 	 if (isset($savem)) $this->connection->setFetchMode($savem);
1012  	 	 $ADODB_FETCH_MODE = $save;
1013  
1014  	 	 if ( empty($cols)) {
1015  	 	 	 return $this->createTableSQL($tablename, $flds, $tableoptions);
1016  	 	 }
1017  
1018  	 	 if (is_array($flds)) {
1019  	 	 // Cycle through the update fields, comparing
1020  	 	 // existing fields to fields to update.
1021  	 	 // if the Metatype and size is exactly the
1022  	 	 // same, ignore - by Mark Newham
1023  	 	 	 $holdflds = array();
1024  	 	 	 foreach($flds as $k=>$v) {
1025  	 	 	 	 if ( isset($cols[$k]) && is_object($cols[$k]) ) {
1026  	 	 	 	 	 // If already not allowing nulls, then don't change
1027  	 	 	 	 	 $obj = $cols[$k];
1028  	 	 	 	 	 if (isset($obj->not_null) && $obj->not_null)
1029  	 	 	 	 	 	 $v = str_replace('NOT NULL','',$v);
1030  	 	 	 	 	 if (isset($obj->auto_increment) && $obj->auto_increment && empty($v['AUTOINCREMENT']))
1031  	 	 	 	 	     $v = str_replace('AUTOINCREMENT','',$v);
1032  
1033  	 	 	 	 	 $c = $cols[$k];
1034  	 	 	 	 	 $ml = $c->max_length;
1035  	 	 	 	 	 $mt = $this->metaType($c->type,$ml);
1036  
1037  	 	 	 	 	 if (isset($c->scale)) $sc = $c->scale;
1038  	 	 	 	 	 else $sc = 99; // always force change if scale not known.
1039  
1040  	 	 	 	 	 if ($sc == -1) $sc = false;
1041  	 	 	 	 	 list($fsize, $fprec) = $this->_getSizePrec($v['SIZE']);
1042  
1043  	 	 	 	 	 if ($ml == -1) $ml = '';
1044  	 	 	 	 	 if ($mt == 'X') $ml = $v['SIZE'];
1045  	 	 	 	 	 if (($mt != $v['TYPE']) || ($ml != $fsize || $sc != $fprec) || (isset($v['AUTOINCREMENT']) && $v['AUTOINCREMENT'] != $obj->auto_increment)) {
1046  	 	 	 	 	 	 $holdflds[$k] = $v;
1047  	 	 	 	 	 }
1048  	 	 	 	 } else {
1049  	 	 	 	 	 $holdflds[$k] = $v;
1050  	 	 	 	 }
1051  	 	 	 }
1052  	 	 	 $flds = $holdflds;
1053  	 	 }
1054  
1055  
1056  	 	 // already exists, alter table instead
1057  	 	 list($lines,$pkey,$idxs) = $this->_genFields($flds);
1058  	 	 // genfields can return FALSE at times
1059  	 	 if ($lines == null) $lines = array();
1060  	 	 $alter = 'ALTER TABLE ' . $this->tableName($tablename);
1061  	 	 $sql = array();
1062  
1063  	 	 foreach ( $lines as $id => $v ) {
1064  	 	 	 if ( isset($cols[$id]) && is_object($cols[$id]) ) {
1065  
1066  	 	 	 	 $flds = lens_ParseArgs($v,',');
1067  
1068  	 	 	 	 //  We are trying to change the size of the field, if not allowed, simply ignore the request.
1069  	 	 	 	 // $flds[1] holds the type, $flds[2] holds the size -postnuke addition
1070  	 	 	 	 if ($flds && in_array(strtoupper(substr($flds[0][1],0,4)),$this->invalidResizeTypes4)
1071  	 	 	 	  && (isset($flds[0][2]) && is_numeric($flds[0][2]))) {
1072  	 	 	 	 	 if ($this->debug) ADOConnection::outp(sprintf("<h3>%s cannot be changed to %s currently</h3>", $flds[0][0], $flds[0][1]));
1073  	 	 	 	 	 #echo "<h3>$this->alterCol cannot be changed to $flds currently</h3>";
1074  	 	 	 	 	 continue;
1075  	  	 	 	 }
1076  	 	 	 	 $sql[] = $alter . $this->alterCol . ' ' . $v;
1077  	 	 	 } else {
1078  	 	 	 	 $sql[] = $alter . $this->addCol . ' ' . $v;
1079  	 	 	 }
1080  	 	 }
1081  
1082  	 	 if ($dropOldFlds) {
1083  	 	 	 foreach ( $cols as $id => $v )
1084  	 	 	     if ( !isset($lines[$id]) )
1085  	 	 	 	 	 $sql[] = $alter . $this->dropCol . ' ' . $v->name;
1086  	 	 }
1087  	 	 return $sql;
1088  	 }
1089  } // class