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?>