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