1<?php 2/* 3V5.10 10 Nov 2009 (c) 2000-2009 John Lim (jlim#natsoft.com). All rights reserved. 4 Released under both BSD license and Lesser GPL library license. 5 Whenever there is any discrepancy between the two licenses, 6 the BSD license will take precedence. 7 Set tabs to 8. 8 9 MySQL code that does not support transactions. Use mysqlt if you need transactions. 10 Requires mysql client. Works on Windows and Unix. 11 1221 October 2003: MySQLi extension implementation by Arjen de Rijke (a.de.rijke@xs4all.nl) 13Based on adodb 3.40 14*/ 15 16// security - hide paths 17if (!defined('ADODB_DIR')) die(); 18 19if (! defined("_ADODB_MYSQLI_LAYER")) { 20 define("_ADODB_MYSQLI_LAYER", 1 ); 21 22 // PHP5 compat... 23 if (! defined("MYSQLI_BINARY_FLAG")) define("MYSQLI_BINARY_FLAG", 128); 24 if (!defined('MYSQLI_READ_DEFAULT_GROUP')) define('MYSQLI_READ_DEFAULT_GROUP',1); 25 26 // disable adodb extension - currently incompatible. 27 global $ADODB_EXTENSION; $ADODB_EXTENSION = false; 28 29class ADODB_mysqli extends ADOConnection { 30 var $databaseType = 'mysqli'; 31 var $dataProvider = 'native'; 32 var $hasInsertID = true; 33 var $hasAffectedRows = true; 34 var $metaTablesSQL = "SHOW TABLES"; 35 var $metaColumnsSQL = "SHOW COLUMNS FROM `%s`"; 36 var $fmtTimeStamp = "'Y-m-d H:i:s'"; 37 var $hasLimit = true; 38 var $hasMoveFirst = true; 39 var $hasGenID = true; 40 var $isoDates = true; // accepts dates in ISO format 41 var $sysDate = 'CURDATE()'; 42 var $sysTimeStamp = 'NOW()'; 43 var $hasTransactions = true; 44 var $forceNewConnect = false; 45 var $poorAffectedRows = true; 46 var $clientFlags = 0; 47 var $substr = "substring"; 48 var $port = false; 49 var $socket = false; 50 var $_bindInputArray = false; 51 var $nameQuote = '`'; /// string to use to quote identifiers and names 52 var $optionFlags = array(array(MYSQLI_READ_DEFAULT_GROUP,0)); 53 var $arrayClass = 'ADORecordSet_array_mysqli'; 54 var $multiQuery = false; 55 56 function ADODB_mysqli() 57 { 58 // if(!extension_loaded("mysqli")) 59 ;//trigger_error("You must have the mysqli extension installed.", E_USER_ERROR); 60 61 } 62 63 function SetTransactionMode( $transaction_mode ) 64 { 65 $this->_transmode = $transaction_mode; 66 if (empty($transaction_mode)) { 67 $this->Execute('SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ'); 68 return; 69 } 70 if (!stristr($transaction_mode,'isolation')) $transaction_mode = 'ISOLATION LEVEL '.$transaction_mode; 71 $this->Execute("SET SESSION TRANSACTION ".$transaction_mode); 72 } 73 74 // returns true or false 75 // To add: parameter int $port, 76 // parameter string $socket 77 function _connect($argHostname = NULL, 78 $argUsername = NULL, 79 $argPassword = NULL, 80 $argDatabasename = NULL, $persist=false) 81 { 82 if(!extension_loaded("mysqli")) { 83 return null; 84 } 85 $this->_connectionID = @mysqli_init(); 86 87 if (is_null($this->_connectionID)) { 88 // mysqli_init only fails if insufficient memory 89 if ($this->debug) 90 ADOConnection::outp("mysqli_init() failed : " . $this->ErrorMsg()); 91 return false; 92 } 93 /* 94 I suggest a simple fix which would enable adodb and mysqli driver to 95 read connection options from the standard mysql configuration file 96 /etc/my.cnf - "Bastien Duclaux" <bduclaux#yahoo.com> 97 */ 98 foreach($this->optionFlags as $arr) { 99 mysqli_options($this->_connectionID,$arr[0],$arr[1]); 100 } 101 102 #if (!empty($this->port)) $argHostname .= ":".$this->port; 103 $ok = mysqli_real_connect($this->_connectionID, 104 $argHostname, 105 $argUsername, 106 $argPassword, 107 $argDatabasename, 108 $this->port, 109 $this->socket, 110 $this->clientFlags); 111 112 if ($ok) { 113 if ($argDatabasename) return $this->SelectDB($argDatabasename); 114 return true; 115 } else { 116 if ($this->debug) 117 ADOConnection::outp("Could't connect : " . $this->ErrorMsg()); 118 $this->_connectionID = null; 119 return false; 120 } 121 } 122 123 // returns true or false 124 // How to force a persistent connection 125 function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename) 126 { 127 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true); 128 129 } 130 131 // When is this used? Close old connection first? 132 // In _connect(), check $this->forceNewConnect? 133 function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename) 134 { 135 $this->forceNewConnect = true; 136 return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename); 137 } 138 139 function IfNull( $field, $ifNull ) 140 { 141 return " IFNULL($field, $ifNull) "; // if MySQL 142 } 143 144 // do not use $ADODB_COUNTRECS 145 function GetOne($sql,$inputarr=false) 146 { 147 $ret = false; 148 $rs = $this->Execute($sql,$inputarr); 149 if ($rs) { 150 if (!$rs->EOF) $ret = reset($rs->fields); 151 $rs->Close(); 152 } 153 return $ret; 154 } 155 156 function ServerInfo() 157 { 158 $arr['description'] = $this->GetOne("select version()"); 159 $arr['version'] = ADOConnection::_findvers($arr['description']); 160 return $arr; 161 } 162 163 164 function BeginTrans() 165 { 166 if ($this->transOff) return true; 167 $this->transCnt += 1; 168 169 //$this->Execute('SET AUTOCOMMIT=0'); 170 mysqli_autocommit($this->_connectionID, false); 171 $this->Execute('BEGIN'); 172 return true; 173 } 174 175 function CommitTrans($ok=true) 176 { 177 if ($this->transOff) return true; 178 if (!$ok) return $this->RollbackTrans(); 179 180 if ($this->transCnt) $this->transCnt -= 1; 181 $this->Execute('COMMIT'); 182 183 //$this->Execute('SET AUTOCOMMIT=1'); 184 mysqli_autocommit($this->_connectionID, true); 185 return true; 186 } 187 188 function RollbackTrans() 189 { 190 if ($this->transOff) return true; 191 if ($this->transCnt) $this->transCnt -= 1; 192 $this->Execute('ROLLBACK'); 193 //$this->Execute('SET AUTOCOMMIT=1'); 194 mysqli_autocommit($this->_connectionID, true); 195 return true; 196 } 197 198 function RowLock($tables,$where='',$col='1 as adodb_ignore') 199 { 200 if ($this->transCnt==0) $this->BeginTrans(); 201 if ($where) $where = ' where '.$where; 202 $rs = $this->Execute("select $col from $tables $where for update"); 203 return !empty($rs); 204 } 205 206 // if magic quotes disabled, use mysql_real_escape_string() 207 // From readme.htm: 208 // Quotes a string to be sent to the database. The $magic_quotes_enabled 209 // parameter may look funny, but the idea is if you are quoting a 210 // string extracted from a POST/GET variable, then 211 // pass get_magic_quotes_gpc() as the second parameter. This will 212 // ensure that the variable is not quoted twice, once by qstr and once 213 // by the magic_quotes_gpc. 214 // 215 //Eg. $s = $db->qstr(_GET['name'],get_magic_quotes_gpc()); 216 function qstr($s, $magic_quotes = false) 217 { 218 if (is_null($s)) return 'NULL'; 219 if (!$magic_quotes) { 220 if (PHP_VERSION >= 5) 221 return "'" . mysqli_real_escape_string($this->_connectionID, $s) . "'"; 222 223 if ($this->replaceQuote[0] == '\\') 224 $s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s); 225 return "'".str_replace("'",$this->replaceQuote,$s)."'"; 226 } 227 // undo magic quotes for " 228 $s = str_replace('\\"','"',$s); 229 return "'$s'"; 230 } 231 232 function _insertid() 233 { 234 $result = @mysqli_insert_id($this->_connectionID); 235 if ($result == -1){ 236 if ($this->debug) ADOConnection::outp("mysqli_insert_id() failed : " . $this->ErrorMsg()); 237 } 238 return $result; 239 } 240 241 // Only works for INSERT, UPDATE and DELETE query's 242 function _affectedrows() 243 { 244 $result = @mysqli_affected_rows($this->_connectionID); 245 if ($result == -1) { 246 if ($this->debug) ADOConnection::outp("mysqli_affected_rows() failed : " . $this->ErrorMsg()); 247 } 248 return $result; 249 } 250 251 // See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html 252 // Reference on Last_Insert_ID on the recommended way to simulate sequences 253 var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);"; 254 var $_genSeqSQL = "create table %s (id int not null)"; 255 var $_genSeqCountSQL = "select count(*) from %s"; 256 var $_genSeq2SQL = "insert into %s values (%s)"; 257 var $_dropSeqSQL = "drop table %s"; 258 259 function CreateSequence($seqname='adodbseq',$startID=1) 260 { 261 if (empty($this->_genSeqSQL)) return false; 262 $u = strtoupper($seqname); 263 264 $ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname)); 265 if (!$ok) return false; 266 return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); 267 } 268 269 function GenID($seqname='adodbseq',$startID=1) 270 { 271 // post-nuke sets hasGenID to false 272 if (!$this->hasGenID) return false; 273 274 $getnext = sprintf($this->_genIDSQL,$seqname); 275 $holdtransOK = $this->_transOK; // save the current status 276 $rs = @$this->Execute($getnext); 277 if (!$rs) { 278 if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset 279 $u = strtoupper($seqname); 280 $this->Execute(sprintf($this->_genSeqSQL,$seqname)); 281 $cnt = $this->GetOne(sprintf($this->_genSeqCountSQL,$seqname)); 282 if (!$cnt) $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1)); 283 $rs = $this->Execute($getnext); 284 } 285 286 if ($rs) { 287 $this->genID = mysqli_insert_id($this->_connectionID); 288 $rs->Close(); 289 } else 290 $this->genID = 0; 291 292 return $this->genID; 293 } 294 295 function MetaDatabases() 296 { 297 $query = "SHOW DATABASES"; 298 $ret = $this->Execute($query); 299 if ($ret && is_object($ret)){ 300 $arr = array(); 301 while (!$ret->EOF){ 302 $db = $ret->Fields('Database'); 303 if ($db != 'mysql') $arr[] = $db; 304 $ret->MoveNext(); 305 } 306 return $arr; 307 } 308 return $ret; 309 } 310 311 312 function MetaIndexes ($table, $primary = FALSE) 313 { 314 // save old fetch mode 315 global $ADODB_FETCH_MODE; 316 317 $false = false; 318 $save = $ADODB_FETCH_MODE; 319 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 320 if ($this->fetchMode !== FALSE) { 321 $savem = $this->SetFetchMode(FALSE); 322 } 323 324 // get index details 325 $rs = $this->Execute(sprintf('SHOW INDEXES FROM %s',$table)); 326 327 // restore fetchmode 328 if (isset($savem)) { 329 $this->SetFetchMode($savem); 330 } 331 $ADODB_FETCH_MODE = $save; 332 333 if (!is_object($rs)) { 334 return $false; 335 } 336 337 $indexes = array (); 338 339 // parse index data into array 340 while ($row = $rs->FetchRow()) { 341 if ($primary == FALSE AND $row[2] == 'PRIMARY') { 342 continue; 343 } 344 345 if (!isset($indexes[$row[2]])) { 346 $indexes[$row[2]] = array( 347 'unique' => ($row[1] == 0), 348 'columns' => array() 349 ); 350 } 351 352 $indexes[$row[2]]['columns'][$row[3] - 1] = $row[4]; 353 } 354 355 // sort columns by order in the index 356 foreach ( array_keys ($indexes) as $index ) 357 { 358 ksort ($indexes[$index]['columns']); 359 } 360 361 return $indexes; 362 } 363 364 365 // Format date column in sql string given an input format that understands Y M D 366 function SQLDate($fmt, $col=false) 367 { 368 if (!$col) $col = $this->sysTimeStamp; 369 $s = 'DATE_FORMAT('.$col.",'"; 370 $concat = false; 371 $len = strlen($fmt); 372 for ($i=0; $i < $len; $i++) { 373 $ch = $fmt[$i]; 374 switch($ch) { 375 case 'Y': 376 case 'y': 377 $s .= '%Y'; 378 break; 379 case 'Q': 380 case 'q': 381 $s .= "'),Quarter($col)"; 382 383 if ($len > $i+1) $s .= ",DATE_FORMAT($col,'"; 384 else $s .= ",('"; 385 $concat = true; 386 break; 387 case 'M': 388 $s .= '%b'; 389 break; 390 391 case 'm': 392 $s .= '%m'; 393 break; 394 case 'D': 395 case 'd': 396 $s .= '%d'; 397 break; 398 399 case 'H': 400 $s .= '%H'; 401 break; 402 403 case 'h': 404 $s .= '%I'; 405 break; 406 407 case 'i': 408 $s .= '%i'; 409 break; 410 411 case 's': 412 $s .= '%s'; 413 break; 414 415 case 'a': 416 case 'A': 417 $s .= '%p'; 418 break; 419 420 case 'w': 421 $s .= '%w'; 422 break; 423 424 case 'l': 425 $s .= '%W'; 426 break; 427 428 default: 429 430 if ($ch == '\\') { 431 $i++; 432 $ch = substr($fmt,$i,1); 433 } 434 $s .= $ch; 435 break; 436 } 437 } 438 $s.="')"; 439 if ($concat) $s = "CONCAT($s)"; 440 return $s; 441 } 442 443 // returns concatenated string 444 // much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator 445 function Concat() 446 { 447 $s = ""; 448 $arr = func_get_args(); 449 450 // suggestion by andrew005@mnogo.ru 451 $s = implode(',',$arr); 452 if (strlen($s) > 0) return "CONCAT($s)"; 453 else return ''; 454 } 455 456 // dayFraction is a day in floating point 457 function OffsetDate($dayFraction,$date=false) 458 { 459 if (!$date) $date = $this->sysDate; 460 461 $fraction = $dayFraction * 24 * 3600; 462 return $date . ' + INTERVAL ' . $fraction.' SECOND'; 463 464// return "from_unixtime(unix_timestamp($date)+$fraction)"; 465 } 466 467 function MetaTables($ttype=false,$showSchema=false,$mask=false) 468 { 469 $save = $this->metaTablesSQL; 470 if ($showSchema && is_string($showSchema)) { 471 $this->metaTablesSQL .= " from $showSchema"; 472 } 473 474 if ($mask) { 475 $mask = $this->qstr($mask); 476 $this->metaTablesSQL .= " like $mask"; 477 } 478 $ret = ADOConnection::MetaTables($ttype,$showSchema); 479 480 $this->metaTablesSQL = $save; 481 return $ret; 482 } 483 484 // "Innox - Juan Carlos Gonzalez" <jgonzalez#innox.com.mx> 485 function MetaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE ) 486 { 487 global $ADODB_FETCH_MODE; 488 489 if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC || $this->fetchMode == ADODB_FETCH_ASSOC) $associative = true; 490 491 if ( !empty($owner) ) { 492 $table = "$owner.$table"; 493 } 494 $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table)); 495 if ($associative) { 496 $create_sql = isset($a_create_table["Create Table"]) ? $a_create_table["Create Table"] : $a_create_table["Create View"]; 497 } else $create_sql = $a_create_table[1]; 498 499 $matches = array(); 500 501 if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false; 502 $foreign_keys = array(); 503 $num_keys = count($matches[0]); 504 for ( $i = 0; $i < $num_keys; $i ++ ) { 505 $my_field = explode('`, `', $matches[1][$i]); 506 $ref_table = $matches[2][$i]; 507 $ref_field = explode('`, `', $matches[3][$i]); 508 509 if ( $upper ) { 510 $ref_table = strtoupper($ref_table); 511 } 512 513 // see https://sourceforge.net/tracker/index.php?func=detail&aid=2287278&group_id=42718&atid=433976 514 if (!isset($foreign_keys[$ref_table])) { 515 $foreign_keys[$ref_table] = array(); 516 } 517 $num_fields = count($my_field); 518 for ( $j = 0; $j < $num_fields; $j ++ ) { 519 if ( $associative ) { 520 $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j]; 521 } else { 522 $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}"; 523 } 524 } 525 } 526 527 return $foreign_keys; 528 } 529 530 function MetaColumns($table, $normalize=true) 531 { 532 $false = false; 533 if (!$this->metaColumnsSQL) 534 return $false; 535 536 global $ADODB_FETCH_MODE; 537 $save = $ADODB_FETCH_MODE; 538 $ADODB_FETCH_MODE = ADODB_FETCH_NUM; 539 if ($this->fetchMode !== false) 540 $savem = $this->SetFetchMode(false); 541 $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table)); 542 if (isset($savem)) $this->SetFetchMode($savem); 543 $ADODB_FETCH_MODE = $save; 544 if (!is_object($rs)) 545 return $false; 546 547 $retarr = array(); 548 while (!$rs->EOF) { 549 $fld = new ADOFieldObject(); 550 $fld->name = $rs->fields[0]; 551 $type = $rs->fields[1]; 552 553 // split type into type(length): 554 $fld->scale = null; 555 if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) { 556 $fld->type = $query_array[1]; 557 $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1; 558 $fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1; 559 } elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) { 560 $fld->type = $query_array[1]; 561 $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1; 562 } elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) { 563 $fld->type = $query_array[1]; 564 $arr = explode(",",$query_array[2]); 565 $fld->enums = $arr; 566 $zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6 567 $fld->max_length = ($zlen > 0) ? $zlen : 1; 568 } else { 569 $fld->type = $type; 570 $fld->max_length = -1; 571 } 572 $fld->not_null = ($rs->fields[2] != 'YES'); 573 $fld->primary_key = ($rs->fields[3] == 'PRI'); 574 $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false); 575 $fld->binary = (strpos($type,'blob') !== false); 576 $fld->unsigned = (strpos($type,'unsigned') !== false); 577 $fld->zerofill = (strpos($type,'zerofill') !== false); 578 579 if (!$fld->binary) { 580 $d = $rs->fields[4]; 581 if ($d != '' && $d != 'NULL') { 582 $fld->has_default = true; 583 $fld->default_value = $d; 584 } else { 585 $fld->has_default = false; 586 } 587 } 588 589 if ($save == ADODB_FETCH_NUM) { 590 $retarr[] = $fld; 591 } else { 592 $retarr[strtoupper($fld->name)] = $fld; 593 } 594 $rs->MoveNext(); 595 } 596 597 $rs->Close(); 598 return $retarr; 599 } 600 601 // returns true or false 602 function SelectDB($dbName) 603 { 604// $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID); 605 $this->database = $dbName; 606 $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions 607 608 if ($this->_connectionID) { 609 $result = @mysqli_select_db($this->_connectionID, $dbName); 610 if (!$result) { 611 ADOConnection::outp("Select of database " . $dbName . " failed. " . $this->ErrorMsg()); 612 } 613 return $result; 614 } 615 return false; 616 } 617 618 // parameters use PostgreSQL convention, not MySQL 619 function SelectLimit($sql, 620 $nrows = -1, 621 $offset = -1, 622 $inputarr = false, 623 $secs = 0) 624 { 625 $offsetStr = ($offset >= 0) ? "$offset," : ''; 626 if ($nrows < 0) $nrows = '18446744073709551615'; 627 628 if ($secs) 629 $rs = $this->CacheExecute($secs, $sql . " LIMIT $offsetStr$nrows" , $inputarr ); 630 else 631 $rs = $this->Execute($sql . " LIMIT $offsetStr$nrows" , $inputarr ); 632 633 return $rs; 634 } 635 636 637 function Prepare($sql) 638 { 639 return $sql; 640 $stmt = $this->_connectionID->prepare($sql); 641 if (!$stmt) { 642 echo $this->ErrorMsg(); 643 return $sql; 644 } 645 return array($sql,$stmt); 646 } 647 648 649 // returns queryID or false 650 function _query($sql, $inputarr) 651 { 652 global $ADODB_COUNTRECS; 653 // Move to the next recordset, or return false if there is none. In a stored proc 654 // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result 655 // returns false. I think this is because the last "recordset" is actually just the 656 // return value of the stored proc (ie the number of rows affected). 657 // Commented out for reasons of performance. You should retrieve every recordset yourself. 658 // if (!mysqli_next_result($this->connection->_connectionID)) return false; 659 660 if (is_array($sql)) { 661 662 // Prepare() not supported because mysqli_stmt_execute does not return a recordset, but 663 // returns as bound variables. 664 665 $stmt = $sql[1]; 666 $a = ''; 667 foreach($inputarr as $k => $v) { 668 if (is_string($v)) $a .= 's'; 669 else if (is_integer($v)) $a .= 'i'; 670 else $a .= 'd'; 671 } 672 673 $fnarr = array_merge( array($stmt,$a) , $inputarr); 674 $ret = call_user_func_array('mysqli_stmt_bind_param',$fnarr); 675 $ret = mysqli_stmt_execute($stmt); 676 return $ret; 677 } 678 679 /* 680 if (!$mysql_res = mysqli_query($this->_connectionID, $sql, ($ADODB_COUNTRECS) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)) { 681 if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg()); 682 return false; 683 } 684 685 return $mysql_res; 686 */ 687 688 if ($this->multiQuery) { 689 $rs = mysqli_multi_query($this->_connectionID, $sql.';'); 690 if ($rs) { 691 $rs = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->_connectionID ) : @mysqli_use_result( $this->_connectionID ); 692 return $rs ? $rs : true; // mysqli_more_results( $this->_connectionID ) 693 } 694 } else { 695 $rs = mysqli_query($this->_connectionID, $sql, $ADODB_COUNTRECS ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT); 696 697 if ($rs) return $rs; 698 } 699 700 if($this->debug) 701 ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg()); 702 703 return false; 704 705 } 706 707 /* Returns: the last error message from previous database operation */ 708 function ErrorMsg() 709 { 710 if (empty($this->_connectionID)) 711 $this->_errorMsg = @mysqli_connect_error(); 712 else 713 $this->_errorMsg = @mysqli_error($this->_connectionID); 714 return $this->_errorMsg; 715 } 716 717 /* Returns: the last error number from previous database operation */ 718 function ErrorNo() 719 { 720 if (empty($this->_connectionID)) 721 return @mysqli_connect_errno(); 722 else 723 return @mysqli_errno($this->_connectionID); 724 } 725 726 // returns true or false 727 function _close() 728 { 729 @mysqli_close($this->_connectionID); 730 $this->_connectionID = false; 731 } 732 733 /* 734 * Maximum size of C field 735 */ 736 function CharMax() 737 { 738 return 255; 739 } 740 741 /* 742 * Maximum size of X field 743 */ 744 function TextMax() 745 { 746 return 4294967295; 747 } 748 749 750 751 // this is a set of functions for managing client encoding - very important if the encodings 752 // of your database and your output target (i.e. HTML) don't match 753 // for instance, you may have UTF8 database and server it on-site as latin1 etc. 754 // GetCharSet - get the name of the character set the client is using now 755 // Under Windows, the functions should work with MySQL 4.1.11 and above, the set of charsets supported 756 // depends on compile flags of mysql distribution 757 758 function GetCharSet() 759 { 760 //we will use ADO's builtin property charSet 761 if (!method_exists($this->_connectionID,'character_set_name')) 762 return false; 763 764 $this->charSet = @$this->_connectionID->character_set_name(); 765 if (!$this->charSet) { 766 return false; 767 } else { 768 return $this->charSet; 769 } 770 } 771 772 // SetCharSet - switch the client encoding 773 function SetCharSet($charset_name) 774 { 775 if (!method_exists($this->_connectionID,'set_charset')) 776 return false; 777 778 if ($this->charSet !== $charset_name) { 779 $if = @$this->_connectionID->set_charset($charset_name); 780 if ($if == "0" & $this->GetCharSet() == $charset_name) { 781 return true; 782 } else return false; 783 } else return true; 784 } 785 786 787 788 789} 790 791/*-------------------------------------------------------------------------------------- 792 Class Name: Recordset 793--------------------------------------------------------------------------------------*/ 794 795class ADORecordSet_mysqli extends ADORecordSet{ 796 797 var $databaseType = "mysqli"; 798 var $canSeek = true; 799 800 function ADORecordSet_mysqli($queryID, $mode = false) 801 { 802 if ($mode === false) 803 { 804 global $ADODB_FETCH_MODE; 805 $mode = $ADODB_FETCH_MODE; 806 } 807 808 switch ($mode) 809 { 810 case ADODB_FETCH_NUM: 811 $this->fetchMode = MYSQLI_NUM; 812 break; 813 case ADODB_FETCH_ASSOC: 814 $this->fetchMode = MYSQLI_ASSOC; 815 break; 816 case ADODB_FETCH_DEFAULT: 817 case ADODB_FETCH_BOTH: 818 default: 819 $this->fetchMode = MYSQLI_BOTH; 820 break; 821 } 822 $this->adodbFetchMode = $mode; 823 $this->ADORecordSet($queryID); 824 } 825 826 function _initrs() 827 { 828 global $ADODB_COUNTRECS; 829 830 $this->_numOfRows = $ADODB_COUNTRECS ? @mysqli_num_rows($this->_queryID) : -1; 831 $this->_numOfFields = @mysqli_num_fields($this->_queryID); 832 } 833 834/* 8351 = MYSQLI_NOT_NULL_FLAG 8362 = MYSQLI_PRI_KEY_FLAG 8374 = MYSQLI_UNIQUE_KEY_FLAG 8388 = MYSQLI_MULTIPLE_KEY_FLAG 83916 = MYSQLI_BLOB_FLAG 84032 = MYSQLI_UNSIGNED_FLAG 84164 = MYSQLI_ZEROFILL_FLAG 842128 = MYSQLI_BINARY_FLAG 843256 = MYSQLI_ENUM_FLAG 844512 = MYSQLI_AUTO_INCREMENT_FLAG 8451024 = MYSQLI_TIMESTAMP_FLAG 8462048 = MYSQLI_SET_FLAG 84732768 = MYSQLI_NUM_FLAG 84816384 = MYSQLI_PART_KEY_FLAG 84932768 = MYSQLI_GROUP_FLAG 85065536 = MYSQLI_UNIQUE_FLAG 851131072 = MYSQLI_BINCMP_FLAG 852*/ 853 854 function FetchField($fieldOffset = -1) 855 { 856 $fieldnr = $fieldOffset; 857 if ($fieldOffset != -1) { 858 $fieldOffset = @mysqli_field_seek($this->_queryID, $fieldnr); 859 } 860 $o = @mysqli_fetch_field($this->_queryID); 861 if (!$o) return false; 862 /* Properties of an ADOFieldObject as set by MetaColumns */ 863 $o->primary_key = $o->flags & MYSQLI_PRI_KEY_FLAG; 864 $o->not_null = $o->flags & MYSQLI_NOT_NULL_FLAG; 865 $o->auto_increment = $o->flags & MYSQLI_AUTO_INCREMENT_FLAG; 866 $o->binary = $o->flags & MYSQLI_BINARY_FLAG; 867 // $o->blob = $o->flags & MYSQLI_BLOB_FLAG; /* not returned by MetaColumns */ 868 $o->unsigned = $o->flags & MYSQLI_UNSIGNED_FLAG; 869 870 return $o; 871 } 872 873 function GetRowAssoc($upper = true) 874 { 875 if ($this->fetchMode == MYSQLI_ASSOC && !$upper) 876 return $this->fields; 877 $row = ADORecordSet::GetRowAssoc($upper); 878 return $row; 879 } 880 881 /* Use associative array to get fields array */ 882 function Fields($colname) 883 { 884 if ($this->fetchMode != MYSQLI_NUM) 885 return @$this->fields[$colname]; 886 887 if (!$this->bind) { 888 $this->bind = array(); 889 for ($i = 0; $i < $this->_numOfFields; $i++) { 890 $o = $this->FetchField($i); 891 $this->bind[strtoupper($o->name)] = $i; 892 } 893 } 894 return $this->fields[$this->bind[strtoupper($colname)]]; 895 } 896 897 function _seek($row) 898 { 899 if ($this->_numOfRows == 0) 900 return false; 901 902 if ($row < 0) 903 return false; 904 905 mysqli_data_seek($this->_queryID, $row); 906 $this->EOF = false; 907 return true; 908 } 909 910 911 function NextRecordSet() 912 { 913 global $ADODB_COUNTRECS; 914 915 mysqli_free_result($this->_queryID); 916 $this->_queryID = -1; 917 // Move to the next recordset, or return false if there is none. In a stored proc 918 // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result 919 // returns false. I think this is because the last "recordset" is actually just the 920 // return value of the stored proc (ie the number of rows affected). 921 if(!mysqli_next_result($this->connection->_connectionID)) { 922 return false; 923 } 924 // CD: There is no $this->_connectionID variable, at least in the ADO version I'm using 925 $this->_queryID = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->connection->_connectionID ) 926 : @mysqli_use_result( $this->connection->_connectionID ); 927 if(!$this->_queryID) { 928 return false; 929 } 930 $this->_inited = false; 931 $this->bind = false; 932 $this->_currentRow = -1; 933 $this->Init(); 934 return true; 935 } 936 937 // 10% speedup to move MoveNext to child class 938 // This is the only implementation that works now (23-10-2003). 939 // Other functions return no or the wrong results. 940 function MoveNext() 941 { 942 if ($this->EOF) return false; 943 $this->_currentRow++; 944 $this->fields = @mysqli_fetch_array($this->_queryID,$this->fetchMode); 945 946 if (is_array($this->fields)) return true; 947 $this->EOF = true; 948 return false; 949 } 950 951 function _fetch() 952 { 953 $this->fields = mysqli_fetch_array($this->_queryID,$this->fetchMode); 954 return is_array($this->fields); 955 } 956 957 function _close() 958 { 959 //if results are attached to this pointer from Stored Proceedure calls, the next standard query will die 2014 960 //only a problem with persistant connections 961 962 //mysqli_next_result($this->connection->_connectionID); trashes the DB side attached results. 963 964 while(mysqli_more_results($this->connection->_connectionID)){ 965 @mysqli_next_result($this->connection->_connectionID); 966 } 967 968 //Because you can have one attached result, without tripping mysqli_more_results 969 @mysqli_next_result($this->connection->_connectionID); 970 971 972 mysqli_free_result($this->_queryID); 973 $this->_queryID = false; 974 } 975 976/* 977 9780 = MYSQLI_TYPE_DECIMAL 9791 = MYSQLI_TYPE_CHAR 9801 = MYSQLI_TYPE_TINY 9812 = MYSQLI_TYPE_SHORT 9823 = MYSQLI_TYPE_LONG 9834 = MYSQLI_TYPE_FLOAT 9845 = MYSQLI_TYPE_DOUBLE 9856 = MYSQLI_TYPE_NULL 9867 = MYSQLI_TYPE_TIMESTAMP 9878 = MYSQLI_TYPE_LONGLONG 9889 = MYSQLI_TYPE_INT24 98910 = MYSQLI_TYPE_DATE 99011 = MYSQLI_TYPE_TIME 99112 = MYSQLI_TYPE_DATETIME 99213 = MYSQLI_TYPE_YEAR 99314 = MYSQLI_TYPE_NEWDATE 994247 = MYSQLI_TYPE_ENUM 995248 = MYSQLI_TYPE_SET 996249 = MYSQLI_TYPE_TINY_BLOB 997250 = MYSQLI_TYPE_MEDIUM_BLOB 998251 = MYSQLI_TYPE_LONG_BLOB 999252 = MYSQLI_TYPE_BLOB 1000253 = MYSQLI_TYPE_VAR_STRING 1001254 = MYSQLI_TYPE_STRING 1002255 = MYSQLI_TYPE_GEOMETRY 1003*/ 1004 1005 function MetaType($t, $len = -1, $fieldobj = false) 1006 { 1007 if (is_object($t)) { 1008 $fieldobj = $t; 1009 $t = $fieldobj->type; 1010 $len = $fieldobj->max_length; 1011 } 1012 1013 1014 $len = -1; // mysql max_length is not accurate 1015 switch (strtoupper($t)) { 1016 case 'STRING': 1017 case 'CHAR': 1018 case 'VARCHAR': 1019 case 'TINYBLOB': 1020 case 'TINYTEXT': 1021 case 'ENUM': 1022 case 'SET': 1023 1024 case MYSQLI_TYPE_TINY_BLOB : 1025 #case MYSQLI_TYPE_CHAR : 1026 case MYSQLI_TYPE_STRING : 1027 case MYSQLI_TYPE_ENUM : 1028 case MYSQLI_TYPE_SET : 1029 case 253 : 1030 if ($len <= $this->blobSize) return 'C'; 1031 1032 case 'TEXT': 1033 case 'LONGTEXT': 1034 case 'MEDIUMTEXT': 1035 return 'X'; 1036 1037 1038 // php_mysql extension always returns 'blob' even if 'text' 1039 // so we have to check whether binary... 1040 case 'IMAGE': 1041 case 'LONGBLOB': 1042 case 'BLOB': 1043 case 'MEDIUMBLOB': 1044 1045 case MYSQLI_TYPE_BLOB : 1046 case MYSQLI_TYPE_LONG_BLOB : 1047 case MYSQLI_TYPE_MEDIUM_BLOB : 1048 1049 return !empty($fieldobj->binary) ? 'B' : 'X'; 1050 case 'YEAR': 1051 case 'DATE': 1052 case MYSQLI_TYPE_DATE : 1053 case MYSQLI_TYPE_YEAR : 1054 1055 return 'D'; 1056 1057 case 'TIME': 1058 case 'DATETIME': 1059 case 'TIMESTAMP': 1060 1061 case MYSQLI_TYPE_DATETIME : 1062 case MYSQLI_TYPE_NEWDATE : 1063 case MYSQLI_TYPE_TIME : 1064 case MYSQLI_TYPE_TIMESTAMP : 1065 1066 return 'T'; 1067 1068 case 'INT': 1069 case 'INTEGER': 1070 case 'BIGINT': 1071 case 'TINYINT': 1072 case 'MEDIUMINT': 1073 case 'SMALLINT': 1074 1075 case MYSQLI_TYPE_INT24 : 1076 case MYSQLI_TYPE_LONG : 1077 case MYSQLI_TYPE_LONGLONG : 1078 case MYSQLI_TYPE_SHORT : 1079 case MYSQLI_TYPE_TINY : 1080 1081 if (!empty($fieldobj->primary_key)) return 'R'; 1082 1083 return 'I'; 1084 1085 1086 // Added floating-point types 1087 // Maybe not necessery. 1088 case 'FLOAT': 1089 case 'DOUBLE': 1090 // case 'DOUBLE PRECISION': 1091 case 'DECIMAL': 1092 case 'DEC': 1093 case 'FIXED': 1094 default: 1095 //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>"; 1096 return 'N'; 1097 } 1098 } // function 1099 1100 1101} // rs class 1102 1103} 1104 1105class ADORecordSet_array_mysqli extends ADORecordSet_array { 1106 1107 function ADORecordSet_array_mysqli($id=-1,$mode=false) 1108 { 1109 $this->ADORecordSet_array($id,$mode); 1110 } 1111 1112 function MetaType($t, $len = -1, $fieldobj = false) 1113 { 1114 if (is_object($t)) { 1115 $fieldobj = $t; 1116 $t = $fieldobj->type; 1117 $len = $fieldobj->max_length; 1118 } 1119 1120 1121 $len = -1; // mysql max_length is not accurate 1122 switch (strtoupper($t)) { 1123 case 'STRING': 1124 case 'CHAR': 1125 case 'VARCHAR': 1126 case 'TINYBLOB': 1127 case 'TINYTEXT': 1128 case 'ENUM': 1129 case 'SET': 1130 1131 case MYSQLI_TYPE_TINY_BLOB : 1132 #case MYSQLI_TYPE_CHAR : 1133 case MYSQLI_TYPE_STRING : 1134 case MYSQLI_TYPE_ENUM : 1135 case MYSQLI_TYPE_SET : 1136 case 253 : 1137 if ($len <= $this->blobSize) return 'C'; 1138 1139 case 'TEXT': 1140 case 'LONGTEXT': 1141 case 'MEDIUMTEXT': 1142 return 'X'; 1143 1144 1145 // php_mysql extension always returns 'blob' even if 'text' 1146 // so we have to check whether binary... 1147 case 'IMAGE': 1148 case 'LONGBLOB': 1149 case 'BLOB': 1150 case 'MEDIUMBLOB': 1151 1152 case MYSQLI_TYPE_BLOB : 1153 case MYSQLI_TYPE_LONG_BLOB : 1154 case MYSQLI_TYPE_MEDIUM_BLOB : 1155 1156 return !empty($fieldobj->binary) ? 'B' : 'X'; 1157 case 'YEAR': 1158 case 'DATE': 1159 case MYSQLI_TYPE_DATE : 1160 case MYSQLI_TYPE_YEAR : 1161 1162 return 'D'; 1163 1164 case 'TIME': 1165 case 'DATETIME': 1166 case 'TIMESTAMP': 1167 1168 case MYSQLI_TYPE_DATETIME : 1169 case MYSQLI_TYPE_NEWDATE : 1170 case MYSQLI_TYPE_TIME : 1171 case MYSQLI_TYPE_TIMESTAMP : 1172 1173 return 'T'; 1174 1175 case 'INT': 1176 case 'INTEGER': 1177 case 'BIGINT': 1178 case 'TINYINT': 1179 case 'MEDIUMINT': 1180 case 'SMALLINT': 1181 1182 case MYSQLI_TYPE_INT24 : 1183 case MYSQLI_TYPE_LONG : 1184 case MYSQLI_TYPE_LONGLONG : 1185 case MYSQLI_TYPE_SHORT : 1186 case MYSQLI_TYPE_TINY : 1187 1188 if (!empty($fieldobj->primary_key)) return 'R'; 1189 1190 return 'I'; 1191 1192 1193 // Added floating-point types 1194 // Maybe not necessery. 1195 case 'FLOAT': 1196 case 'DOUBLE': 1197 // case 'DOUBLE PRECISION': 1198 case 'DECIMAL': 1199 case 'DEC': 1200 case 'FIXED': 1201 default: 1202 //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>"; 1203 return 'N'; 1204 } 1205 } // function 1206 1207} 1208 1209?>