Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

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