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