1<?php
2/*
3V4.98 13 Feb 2008  (c) 2000-2008 John Lim (jlim#natsoft.com.my). 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 $_genSeqCountSQL = "select count(*) from %s";
255	var $_genSeq2SQL = "insert into %s values (%s)";
256	var $_dropSeqSQL = "drop table %s";
257
258	function CreateSequence($seqname='adodbseq',$startID=1)
259	{
260		if (empty($this->_genSeqSQL)) return false;
261		$u = strtoupper($seqname);
262
263		$ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname));
264		if (!$ok) return false;
265		return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
266	}
267
268	function GenID($seqname='adodbseq',$startID=1)
269	{
270		// post-nuke sets hasGenID to false
271		if (!$this->hasGenID) return false;
272
273		$getnext = sprintf($this->_genIDSQL,$seqname);
274		$holdtransOK = $this->_transOK; // save the current status
275		$rs = @$this->Execute($getnext);
276		if (!$rs) {
277			if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset
278			$u = strtoupper($seqname);
279			$this->Execute(sprintf($this->_genSeqSQL,$seqname));
280			$cnt = $this->GetOne(sprintf($this->_genSeqCountSQL,$seqname));
281			if (!$cnt) $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
282			$rs = $this->Execute($getnext);
283		}
284
285		if ($rs) {
286			$this->genID = mysqli_insert_id($this->_connectionID);
287			$rs->Close();
288		} else
289			$this->genID = 0;
290
291		return $this->genID;
292	}
293
294  	function &MetaDatabases()
295	{
296		$query = "SHOW DATABASES";
297		$ret =& $this->Execute($query);
298		if ($ret && is_object($ret)){
299		   $arr = array();
300			while (!$ret->EOF){
301				$db = $ret->Fields('Database');
302				if ($db != 'mysql') $arr[] = $db;
303				$ret->MoveNext();
304			}
305   		   return $arr;
306		}
307        return $ret;
308	}
309
310
311	function &MetaIndexes ($table, $primary = FALSE)
312	{
313		// save old fetch mode
314		global $ADODB_FETCH_MODE;
315
316		$false = false;
317		$save = $ADODB_FETCH_MODE;
318		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
319		if ($this->fetchMode !== FALSE) {
320		       $savem = $this->SetFetchMode(FALSE);
321		}
322
323		// get index details
324		$rs = $this->Execute(sprintf('SHOW INDEXES FROM %s',$table));
325
326		// restore fetchmode
327		if (isset($savem)) {
328		        $this->SetFetchMode($savem);
329		}
330		$ADODB_FETCH_MODE = $save;
331
332		if (!is_object($rs)) {
333		        return $false;
334		}
335
336		$indexes = array ();
337
338		// parse index data into array
339		while ($row = $rs->FetchRow()) {
340		        if ($primary == FALSE AND $row[2] == 'PRIMARY') {
341		                continue;
342		        }
343
344		        if (!isset($indexes[$row[2]])) {
345		                $indexes[$row[2]] = array(
346		                        'unique' => ($row[1] == 0),
347		                        'columns' => array()
348		                );
349		        }
350
351		        $indexes[$row[2]]['columns'][$row[3] - 1] = $row[4];
352		}
353
354		// sort columns by order in the index
355		foreach ( array_keys ($indexes) as $index )
356		{
357		        ksort ($indexes[$index]['columns']);
358		}
359
360		return $indexes;
361	}
362
363
364	// Format date column in sql string given an input format that understands Y M D
365	function SQLDate($fmt, $col=false)
366	{
367		if (!$col) $col = $this->sysTimeStamp;
368		$s = 'DATE_FORMAT('.$col.",'";
369		$concat = false;
370		$len = strlen($fmt);
371		for ($i=0; $i < $len; $i++) {
372			$ch = $fmt[$i];
373			switch($ch) {
374			case 'Y':
375			case 'y':
376				$s .= '%Y';
377				break;
378			case 'Q':
379			case 'q':
380				$s .= "'),Quarter($col)";
381
382				if ($len > $i+1) $s .= ",DATE_FORMAT($col,'";
383				else $s .= ",('";
384				$concat = true;
385				break;
386			case 'M':
387				$s .= '%b';
388				break;
389
390			case 'm':
391				$s .= '%m';
392				break;
393			case 'D':
394			case 'd':
395				$s .= '%d';
396				break;
397
398			case 'H':
399				$s .= '%H';
400				break;
401
402			case 'h':
403				$s .= '%I';
404				break;
405
406			case 'i':
407				$s .= '%i';
408				break;
409
410			case 's':
411				$s .= '%s';
412				break;
413
414			case 'a':
415			case 'A':
416				$s .= '%p';
417				break;
418
419			case 'w':
420				$s .= '%w';
421				break;
422
423			case 'l':
424				$s .= '%W';
425				break;
426
427			default:
428
429				if ($ch == '\\') {
430					$i++;
431					$ch = substr($fmt,$i,1);
432				}
433				$s .= $ch;
434				break;
435			}
436		}
437		$s.="')";
438		if ($concat) $s = "CONCAT($s)";
439		return $s;
440	}
441
442	// returns concatenated string
443	// much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator
444	function Concat()
445	{
446		$s = "";
447		$arr = func_get_args();
448
449		// suggestion by andrew005@mnogo.ru
450		$s = implode(',',$arr);
451		if (strlen($s) > 0) return "CONCAT($s)";
452		else return '';
453	}
454
455	// dayFraction is a day in floating point
456	function OffsetDate($dayFraction,$date=false)
457	{
458		if (!$date) $date = $this->sysDate;
459
460		$fraction = $dayFraction * 24 * 3600;
461		return $date . ' + INTERVAL ' .	 $fraction.' SECOND';
462
463//		return "from_unixtime(unix_timestamp($date)+$fraction)";
464	}
465
466	function &MetaTables($ttype=false,$showSchema=false,$mask=false)
467	{
468		$save = $this->metaTablesSQL;
469		if ($showSchema && is_string($showSchema)) {
470			$this->metaTablesSQL .= " from $showSchema";
471		}
472
473		if ($mask) {
474			$mask = $this->qstr($mask);
475			$this->metaTablesSQL .= " like $mask";
476		}
477		$ret =& ADOConnection::MetaTables($ttype,$showSchema);
478
479		$this->metaTablesSQL = $save;
480		return $ret;
481	}
482
483	// "Innox - Juan Carlos Gonzalez" <jgonzalez#innox.com.mx>
484	function MetaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE )
485	{
486	 global $ADODB_FETCH_MODE;
487
488		if ($ADODB_FETCH_MODE == ADODB_FETCH_ASSOC || $this->fetchMode == ADODB_FETCH_ASSOC) $associative = true;
489
490	    if ( !empty($owner) ) {
491	       $table = "$owner.$table";
492	    }
493	    $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table));
494		if ($associative) $create_sql = $a_create_table["Create Table"];
495	    else $create_sql  = $a_create_table[1];
496
497	    $matches = array();
498
499	    if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false;
500	 	$foreign_keys = array();
501	    $num_keys = count($matches[0]);
502	    for ( $i = 0;  $i < $num_keys;  $i ++ ) {
503	        $my_field  = explode('`, `', $matches[1][$i]);
504	        $ref_table = $matches[2][$i];
505	        $ref_field = explode('`, `', $matches[3][$i]);
506
507	        if ( $upper ) {
508	            $ref_table = strtoupper($ref_table);
509	        }
510
511	        $foreign_keys[$ref_table] = array();
512	        $num_fields               = count($my_field);
513	        for ( $j = 0;  $j < $num_fields;  $j ++ ) {
514	            if ( $associative ) {
515	                $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j];
516	            } else {
517	                $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}";
518	            }
519	        }
520	    }
521
522	    return  $foreign_keys;
523	}
524
525 	function &MetaColumns($table)
526	{
527		$false = false;
528		if (!$this->metaColumnsSQL)
529			return $false;
530
531		global $ADODB_FETCH_MODE;
532		$save = $ADODB_FETCH_MODE;
533		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
534		if ($this->fetchMode !== false)
535			$savem = $this->SetFetchMode(false);
536		$rs = $this->Execute(sprintf($this->metaColumnsSQL,$table));
537		if (isset($savem)) $this->SetFetchMode($savem);
538		$ADODB_FETCH_MODE = $save;
539		if (!is_object($rs))
540			return $false;
541
542		$retarr = array();
543		while (!$rs->EOF) {
544			$fld = new ADOFieldObject();
545			$fld->name = $rs->fields[0];
546			$type = $rs->fields[1];
547
548			// split type into type(length):
549			$fld->scale = null;
550			if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) {
551				$fld->type = $query_array[1];
552				$fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
553				$fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1;
554			} elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) {
555				$fld->type = $query_array[1];
556				$fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
557			} elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) {
558				$fld->type = $query_array[1];
559				$fld->max_length = max(array_map("strlen",explode(",",$query_array[2]))) - 2; // PHP >= 4.0.6
560				$fld->max_length = ($fld->max_length == 0 ? 1 : $fld->max_length);
561			} else {
562				$fld->type = $type;
563				$fld->max_length = -1;
564			}
565			$fld->not_null = ($rs->fields[2] != 'YES');
566			$fld->primary_key = ($rs->fields[3] == 'PRI');
567			$fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false);
568			$fld->binary = (strpos($type,'blob') !== false);
569			$fld->unsigned = (strpos($type,'unsigned') !== false);
570			$fld->zerofill = (strpos($type,'zerofill') !== false);
571
572			if (!$fld->binary) {
573				$d = $rs->fields[4];
574				if ($d != '' && $d != 'NULL') {
575					$fld->has_default = true;
576					$fld->default_value = $d;
577				} else {
578					$fld->has_default = false;
579				}
580			}
581
582			if ($save == ADODB_FETCH_NUM) {
583				$retarr[] = $fld;
584			} else {
585				$retarr[strtoupper($fld->name)] = $fld;
586			}
587			$rs->MoveNext();
588		}
589
590		$rs->Close();
591		return $retarr;
592	}
593
594	// returns true or false
595	function SelectDB($dbName)
596	{
597//	    $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID);
598	    $this->database = $dbName;
599		$this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
600
601	    if ($this->_connectionID) {
602        	$result = @mysqli_select_db($this->_connectionID, $dbName);
603			if (!$result) {
604		    	ADOConnection::outp("Select of database " . $dbName . " failed. " . $this->ErrorMsg());
605			}
606			return $result;
607		}
608	    return false;
609	}
610
611	// parameters use PostgreSQL convention, not MySQL
612	function &SelectLimit($sql,
613			      $nrows = -1,
614			      $offset = -1,
615			      $inputarr = false,
616			      $secs = 0)
617	{
618		$offsetStr = ($offset >= 0) ? "$offset," : '';
619		if ($nrows < 0) $nrows = '18446744073709551615';
620
621		if ($secs)
622			$rs =& $this->CacheExecute($secs, $sql . " LIMIT $offsetStr$nrows" , $inputarr);
623		else
624			$rs =& $this->Execute($sql . " LIMIT $offsetStr$nrows" , $inputarr);
625
626		return $rs;
627	}
628
629
630	function Prepare($sql)
631	{
632		return $sql;
633
634		$stmt = $this->_connectionID->prepare($sql);
635		if (!$stmt) {
636			echo $this->ErrorMsg();
637			return $sql;
638		}
639		return array($sql,$stmt);
640	}
641
642
643	// returns queryID or false
644	function _query($sql, $inputarr)
645	{
646	global $ADODB_COUNTRECS;
647
648		if (is_array($sql)) {
649			$stmt = $sql[1];
650			$a = '';
651			foreach($inputarr as $k => $v) {
652				if (is_string($v)) $a .= 's';
653				else if (is_integer($v)) $a .= 'i';
654				else $a .= 'd';
655			}
656
657			$fnarr = array_merge( array($stmt,$a) , $inputarr);
658			$ret = call_user_func_array('mysqli_stmt_bind_param',$fnarr);
659
660			$ret = mysqli_stmt_execute($stmt);
661			return $ret;
662		}
663		if (!$mysql_res =  mysqli_query($this->_connectionID, $sql, ($ADODB_COUNTRECS) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)) {
664		    if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg());
665		    return false;
666		}
667
668		return $mysql_res;
669	}
670
671	/*	Returns: the last error message from previous database operation	*/
672	function ErrorMsg()
673	  {
674	    if (empty($this->_connectionID))
675	      $this->_errorMsg = @mysqli_connect_error();
676	    else
677	      $this->_errorMsg = @mysqli_error($this->_connectionID);
678	    return $this->_errorMsg;
679	  }
680
681	/*	Returns: the last error number from previous database operation	*/
682	function ErrorNo()
683	  {
684	    if(extension_loaded("mysqli")) {
685		if (empty($this->_connectionID))
686		    return @mysqli_connect_errno();
687		else
688		    return @mysqli_errno($this->_connectionID);
689	    } else {
690		return parent::ErrorNo();
691	    }
692	  }
693
694	// returns true or false
695	function _close()
696	  {
697	    @mysqli_close($this->_connectionID);
698	    $this->_connectionID = false;
699	  }
700
701	/*
702	* Maximum size of C field
703	*/
704	function CharMax()
705	{
706		return 255;
707	}
708
709	/*
710	* Maximum size of X field
711	*/
712	function TextMax()
713	{
714	  return 4294967295;
715	}
716
717
718
719	// this is a set of functions for managing client encoding - very important if the encodings
720	// of your database and your output target (i.e. HTML) don't match
721	// for instance, you may have UTF8 database and server it on-site as latin1 etc.
722	// GetCharSet - get the name of the character set the client is using now
723	// Under Windows, the functions should work with MySQL 4.1.11 and above, the set of charsets supported
724	// depends on compile flags of mysql distribution
725
726  function GetCharSet()
727  {
728    //we will use ADO's builtin property charSet
729    if (!method_exists($this->_connectionID,'character_set_name'))
730    	return false;
731
732    $this->charSet = @$this->_connectionID->character_set_name();
733    if (!$this->charSet) {
734      return false;
735    } else {
736      return $this->charSet;
737    }
738  }
739
740  // SetCharSet - switch the client encoding
741  function SetCharSet($charset_name)
742  {
743    if (!method_exists($this->_connectionID,'set_charset'))
744    	return false;
745
746    if ($this->charSet !== $charset_name) {
747      $if = @$this->_connectionID->set_charset($charset_name);
748      if ($if == "0" & $this->GetCharSet() == $charset_name) {
749        return true;
750      } else return false;
751    } else return true;
752  }
753
754
755
756
757}
758
759/*--------------------------------------------------------------------------------------
760	 Class Name: Recordset
761--------------------------------------------------------------------------------------*/
762
763class ADORecordSet_mysqli extends ADORecordSet{
764
765	var $databaseType = "mysqli";
766	var $canSeek = true;
767
768	function ADORecordSet_mysqli($queryID, $mode = false)
769	{
770	  if ($mode === false)
771	   {
772	      global $ADODB_FETCH_MODE;
773	      $mode = $ADODB_FETCH_MODE;
774	   }
775
776	  switch ($mode)
777	    {
778	    case ADODB_FETCH_NUM:
779	      $this->fetchMode = MYSQLI_NUM;
780	      break;
781	    case ADODB_FETCH_ASSOC:
782	      $this->fetchMode = MYSQLI_ASSOC;
783	      break;
784	    case ADODB_FETCH_DEFAULT:
785	    case ADODB_FETCH_BOTH:
786	    default:
787	      $this->fetchMode = MYSQLI_BOTH;
788	      break;
789	    }
790	  $this->adodbFetchMode = $mode;
791	  $this->ADORecordSet($queryID);
792	}
793
794	function _initrs()
795	{
796	global $ADODB_COUNTRECS;
797
798		$this->_numOfRows = $ADODB_COUNTRECS ? @mysqli_num_rows($this->_queryID) : -1;
799		$this->_numOfFields = @mysqli_num_fields($this->_queryID);
800	}
801
802/*
8031      = MYSQLI_NOT_NULL_FLAG
8042      = MYSQLI_PRI_KEY_FLAG
8054      = MYSQLI_UNIQUE_KEY_FLAG
8068      = MYSQLI_MULTIPLE_KEY_FLAG
80716     = MYSQLI_BLOB_FLAG
80832     = MYSQLI_UNSIGNED_FLAG
80964     = MYSQLI_ZEROFILL_FLAG
810128    = MYSQLI_BINARY_FLAG
811256    = MYSQLI_ENUM_FLAG
812512    = MYSQLI_AUTO_INCREMENT_FLAG
8131024   = MYSQLI_TIMESTAMP_FLAG
8142048   = MYSQLI_SET_FLAG
81532768  = MYSQLI_NUM_FLAG
81616384  = MYSQLI_PART_KEY_FLAG
81732768  = MYSQLI_GROUP_FLAG
81865536  = MYSQLI_UNIQUE_FLAG
819131072 = MYSQLI_BINCMP_FLAG
820*/
821
822	function &FetchField($fieldOffset = -1)
823	{
824		$fieldnr = $fieldOffset;
825		if ($fieldOffset != -1) {
826		  $fieldOffset = mysqli_field_seek($this->_queryID, $fieldnr);
827		}
828		$o = mysqli_fetch_field($this->_queryID);
829		/* Properties of an ADOFieldObject as set by MetaColumns */
830		$o->primary_key = $o->flags & MYSQLI_PRI_KEY_FLAG;
831		$o->not_null = $o->flags & MYSQLI_NOT_NULL_FLAG;
832		$o->auto_increment = $o->flags & MYSQLI_AUTO_INCREMENT_FLAG;
833		$o->binary = $o->flags & MYSQLI_BINARY_FLAG;
834		// $o->blob = $o->flags & MYSQLI_BLOB_FLAG; /* not returned by MetaColumns */
835		$o->unsigned = $o->flags & MYSQLI_UNSIGNED_FLAG;
836
837		return $o;
838	}
839
840	function &GetRowAssoc($upper = true)
841	{
842		if ($this->fetchMode == MYSQLI_ASSOC && !$upper)
843		  return $this->fields;
844		$row =& ADORecordSet::GetRowAssoc($upper);
845		return $row;
846	}
847
848	/* Use associative array to get fields array */
849	function Fields($colname)
850	{
851	  if ($this->fetchMode != MYSQLI_NUM)
852	    return @$this->fields[$colname];
853
854	  if (!$this->bind) {
855	    $this->bind = array();
856	    for ($i = 0; $i < $this->_numOfFields; $i++) {
857	      $o = $this->FetchField($i);
858	      $this->bind[strtoupper($o->name)] = $i;
859	    }
860	  }
861	  return $this->fields[$this->bind[strtoupper($colname)]];
862	}
863
864	function _seek($row)
865	{
866	  if ($this->_numOfRows == 0)
867	    return false;
868
869	  if ($row < 0)
870	    return false;
871
872	  mysqli_data_seek($this->_queryID, $row);
873	  $this->EOF = false;
874	  return true;
875	}
876
877	// 10% speedup to move MoveNext to child class
878	// This is the only implementation that works now (23-10-2003).
879	// Other functions return no or the wrong results.
880	function MoveNext()
881	{
882		if ($this->EOF) return false;
883		$this->_currentRow++;
884		$this->fields = @mysqli_fetch_array($this->_queryID,$this->fetchMode);
885
886		if (is_array($this->fields)) return true;
887		$this->EOF = true;
888		return false;
889	}
890
891	function _fetch()
892	{
893		$this->fields = mysqli_fetch_array($this->_queryID,$this->fetchMode);
894	  	return is_array($this->fields);
895	}
896
897	function _close()
898	{
899		mysqli_free_result($this->_queryID);
900	  	$this->_queryID = false;
901	}
902
903/*
904
9050 = MYSQLI_TYPE_DECIMAL
9061 = MYSQLI_TYPE_CHAR
9071 = MYSQLI_TYPE_TINY
9082 = MYSQLI_TYPE_SHORT
9093 = MYSQLI_TYPE_LONG
9104 = MYSQLI_TYPE_FLOAT
9115 = MYSQLI_TYPE_DOUBLE
9126 = MYSQLI_TYPE_NULL
9137 = MYSQLI_TYPE_TIMESTAMP
9148 = MYSQLI_TYPE_LONGLONG
9159 = MYSQLI_TYPE_INT24
91610 = MYSQLI_TYPE_DATE
91711 = MYSQLI_TYPE_TIME
91812 = MYSQLI_TYPE_DATETIME
91913 = MYSQLI_TYPE_YEAR
92014 = MYSQLI_TYPE_NEWDATE
921247 = MYSQLI_TYPE_ENUM
922248 = MYSQLI_TYPE_SET
923249 = MYSQLI_TYPE_TINY_BLOB
924250 = MYSQLI_TYPE_MEDIUM_BLOB
925251 = MYSQLI_TYPE_LONG_BLOB
926252 = MYSQLI_TYPE_BLOB
927253 = MYSQLI_TYPE_VAR_STRING
928254 = MYSQLI_TYPE_STRING
929255 = MYSQLI_TYPE_GEOMETRY
930*/
931
932	function MetaType($t, $len = -1, $fieldobj = false)
933	{
934		if (is_object($t)) {
935		    $fieldobj = $t;
936		    $t = $fieldobj->type;
937		    $len = $fieldobj->max_length;
938		}
939
940
941		 $len = -1; // mysql max_length is not accurate
942		 switch (strtoupper($t)) {
943		 case 'STRING':
944		 case 'CHAR':
945		 case 'VARCHAR':
946		 case 'TINYBLOB':
947		 case 'TINYTEXT':
948		 case 'ENUM':
949		 case 'SET':
950
951		case MYSQLI_TYPE_TINY_BLOB :
952		#case MYSQLI_TYPE_CHAR :
953		case MYSQLI_TYPE_STRING :
954		case MYSQLI_TYPE_ENUM :
955		case MYSQLI_TYPE_SET :
956		case 253 :
957		   if ($len <= $this->blobSize) return 'C';
958
959		case 'TEXT':
960		case 'LONGTEXT':
961		case 'MEDIUMTEXT':
962		   return 'X';
963
964
965		   // php_mysql extension always returns 'blob' even if 'text'
966		   // so we have to check whether binary...
967		case 'IMAGE':
968		case 'LONGBLOB':
969		case 'BLOB':
970		case 'MEDIUMBLOB':
971
972		case MYSQLI_TYPE_BLOB :
973		case MYSQLI_TYPE_LONG_BLOB :
974		case MYSQLI_TYPE_MEDIUM_BLOB :
975
976		   return !empty($fieldobj->binary) ? 'B' : 'X';
977		case 'YEAR':
978		case 'DATE':
979		case MYSQLI_TYPE_DATE :
980		case MYSQLI_TYPE_YEAR :
981
982		   return 'D';
983
984		case 'TIME':
985		case 'DATETIME':
986		case 'TIMESTAMP':
987
988		case MYSQLI_TYPE_DATETIME :
989		case MYSQLI_TYPE_NEWDATE :
990		case MYSQLI_TYPE_TIME :
991		case MYSQLI_TYPE_TIMESTAMP :
992
993			return 'T';
994
995		case 'INT':
996		case 'INTEGER':
997		case 'BIGINT':
998		case 'TINYINT':
999		case 'MEDIUMINT':
1000		case 'SMALLINT':
1001
1002		case MYSQLI_TYPE_INT24 :
1003		case MYSQLI_TYPE_LONG :
1004		case MYSQLI_TYPE_LONGLONG :
1005		case MYSQLI_TYPE_SHORT :
1006		case MYSQLI_TYPE_TINY :
1007
1008		   if (!empty($fieldobj->primary_key)) return 'R';
1009
1010		   return 'I';
1011
1012
1013		   // Added floating-point types
1014		   // Maybe not necessery.
1015		 case 'FLOAT':
1016		 case 'DOUBLE':
1017		   //		case 'DOUBLE PRECISION':
1018		 case 'DECIMAL':
1019		 case 'DEC':
1020		 case 'FIXED':
1021		 default:
1022		 	//if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>";
1023		 	return 'N';
1024		}
1025	} // function
1026
1027
1028} // rs class
1029
1030}
1031
1032class ADORecordSet_array_mysqli extends ADORecordSet_array {
1033  function ADORecordSet_array_mysqli($id=-1,$mode=false)
1034  {
1035    $this->ADORecordSet_array($id,$mode);
1036  }
1037
1038
1039	function MetaType($t, $len = -1, $fieldobj = false)
1040	{
1041		if (is_object($t)) {
1042		    $fieldobj = $t;
1043		    $t = $fieldobj->type;
1044		    $len = $fieldobj->max_length;
1045		}
1046
1047
1048		 $len = -1; // mysql max_length is not accurate
1049		 switch (strtoupper($t)) {
1050		 case 'STRING':
1051		 case 'CHAR':
1052		 case 'VARCHAR':
1053		 case 'TINYBLOB':
1054		 case 'TINYTEXT':
1055		 case 'ENUM':
1056		 case 'SET':
1057
1058		case MYSQLI_TYPE_TINY_BLOB :
1059		#case MYSQLI_TYPE_CHAR :
1060		case MYSQLI_TYPE_STRING :
1061		case MYSQLI_TYPE_ENUM :
1062		case MYSQLI_TYPE_SET :
1063		case 253 :
1064		   if ($len <= $this->blobSize) return 'C';
1065
1066		case 'TEXT':
1067		case 'LONGTEXT':
1068		case 'MEDIUMTEXT':
1069		   return 'X';
1070
1071
1072		   // php_mysql extension always returns 'blob' even if 'text'
1073		   // so we have to check whether binary...
1074		case 'IMAGE':
1075		case 'LONGBLOB':
1076		case 'BLOB':
1077		case 'MEDIUMBLOB':
1078
1079		case MYSQLI_TYPE_BLOB :
1080		case MYSQLI_TYPE_LONG_BLOB :
1081		case MYSQLI_TYPE_MEDIUM_BLOB :
1082
1083		   return !empty($fieldobj->binary) ? 'B' : 'X';
1084		case 'YEAR':
1085		case 'DATE':
1086		case MYSQLI_TYPE_DATE :
1087		case MYSQLI_TYPE_YEAR :
1088
1089		   return 'D';
1090
1091		case 'TIME':
1092		case 'DATETIME':
1093		case 'TIMESTAMP':
1094
1095		case MYSQLI_TYPE_DATETIME :
1096		case MYSQLI_TYPE_NEWDATE :
1097		case MYSQLI_TYPE_TIME :
1098		case MYSQLI_TYPE_TIMESTAMP :
1099
1100			return 'T';
1101
1102		case 'INT':
1103		case 'INTEGER':
1104		case 'BIGINT':
1105		case 'TINYINT':
1106		case 'MEDIUMINT':
1107		case 'SMALLINT':
1108
1109		case MYSQLI_TYPE_INT24 :
1110		case MYSQLI_TYPE_LONG :
1111		case MYSQLI_TYPE_LONGLONG :
1112		case MYSQLI_TYPE_SHORT :
1113		case MYSQLI_TYPE_TINY :
1114
1115		   if (!empty($fieldobj->primary_key)) return 'R';
1116
1117		   return 'I';
1118
1119
1120		   // Added floating-point types
1121		   // Maybe not necessery.
1122		 case 'FLOAT':
1123		 case 'DOUBLE':
1124		   //		case 'DOUBLE PRECISION':
1125		 case 'DECIMAL':
1126		 case 'DEC':
1127		 case 'FIXED':
1128		 default:
1129		 	//if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>";
1130		 	return 'N';
1131		}
1132	} // function
1133
1134}
1135
1136?>
1137