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
title
Description
Body
title
Description
Body
title
Description
Body
title
Body