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