1<?php
2/*
3V4.80 8 Mar 2006  (c) 2000-2006 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
17//if (!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
54	function ADODB_mysqli()
55	{
56	 // if(!extension_loaded("mysqli"))
57	      ;//trigger_error("You must have the mysqli extension installed.", E_USER_ERROR);
58
59	}
60
61
62	// returns true or false
63	// To add: parameter int $port,
64	//         parameter string $socket
65	function _connect($argHostname = NULL,
66			  $argUsername = NULL,
67			  $argPassword = NULL,
68			  $argDatabasename = NULL, $persist=false)
69	  {
70	  	 if(!extension_loaded("mysqli")) {
71			return null;
72		 }
73	    $this->_connectionID = @mysqli_init();
74
75	    if (is_null($this->_connectionID)) {
76	      // mysqli_init only fails if insufficient memory
77	      if ($this->debug)
78				ADOConnection::outp("mysqli_init() failed : "  . $this->ErrorMsg());
79	      return false;
80	    }
81		/*
82		I suggest a simple fix which would enable adodb and mysqli driver to
83		read connection options from the standard mysql configuration file
84		/etc/my.cnf - "Bastien Duclaux" <bduclaux#yahoo.com>
85		*/
86		foreach($this->optionFlags as $arr) {
87			mysqli_options($this->_connectionID,$arr[0],$arr[1]);
88		}
89
90		#if (!empty($this->port)) $argHostname .= ":".$this->port;
91		$ok = mysqli_real_connect($this->_connectionID,
92 				    $argHostname,
93 				    $argUsername,
94 				    $argPassword,
95 				    $argDatabasename,
96					$this->port,
97					$this->socket,
98					$this->clientFlags);
99
100		if ($ok) {
101	 		if ($argDatabasename)  return $this->SelectDB($argDatabasename);
102 			return true;
103 	   } else {
104			if ($this->debug)
105		  		ADOConnection::outp("Could't connect : "  . $this->ErrorMsg());
106			return false;
107	   }
108	}
109
110	// returns true or false
111	// How to force a persistent connection
112	function _pconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
113	{
114		return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename, true);
115
116	}
117
118	// When is this used? Close old connection first?
119	// In _connect(), check $this->forceNewConnect?
120	function _nconnect($argHostname, $argUsername, $argPassword, $argDatabasename)
121	  {
122	    $this->forceNewConnect = true;
123	    return $this->_connect($argHostname, $argUsername, $argPassword, $argDatabasename);
124	  }
125
126	function IfNull( $field, $ifNull )
127	{
128		return " IFNULL($field, $ifNull) "; // if MySQL
129	}
130
131	function ServerInfo()
132	{
133		$arr['description'] = $this->GetOne("select version()");
134		$arr['version'] = ADOConnection::_findvers($arr['description']);
135		return $arr;
136	}
137
138
139	function BeginTrans()
140	{
141		if ($this->transOff) return true;
142		$this->transCnt += 1;
143		$this->Execute('SET AUTOCOMMIT=0');
144		$this->Execute('BEGIN');
145		return true;
146	}
147
148	function CommitTrans($ok=true)
149	{
150		if ($this->transOff) return true;
151		if (!$ok) return $this->RollbackTrans();
152
153		if ($this->transCnt) $this->transCnt -= 1;
154		$this->Execute('COMMIT');
155		$this->Execute('SET AUTOCOMMIT=1');
156		return true;
157	}
158
159	function RollbackTrans()
160	{
161		if ($this->transOff) return true;
162		if ($this->transCnt) $this->transCnt -= 1;
163		$this->Execute('ROLLBACK');
164		$this->Execute('SET AUTOCOMMIT=1');
165		return true;
166	}
167
168	function RowLock($tables,$where='',$flds='1 as adodb_ignore')
169	{
170		if ($this->transCnt==0) $this->BeginTrans();
171		if ($where) $where = ' where '.$where;
172		$rs =& $this->Execute("select $flds from $tables $where for update");
173		return !empty($rs);
174	}
175
176	// if magic quotes disabled, use mysql_real_escape_string()
177	// From readme.htm:
178	// Quotes a string to be sent to the database. The $magic_quotes_enabled
179	// parameter may look funny, but the idea is if you are quoting a
180	// string extracted from a POST/GET variable, then
181	// pass get_magic_quotes_gpc() as the second parameter. This will
182	// ensure that the variable is not quoted twice, once by qstr and once
183	// by the magic_quotes_gpc.
184	//
185	//Eg. $s = $db->qstr(_GET['name'],get_magic_quotes_gpc());
186	function qstr($s, $magic_quotes = false)
187	{
188		if (!$magic_quotes) {
189	    	if (PHP_VERSION >= 5)
190	      		return "'" . mysqli_real_escape_string($this->_connectionID, $s) . "'";
191
192		if ($this->replaceQuote[0] == '\\')
193			$s = adodb_str_replace(array('\\',"\0"),array('\\\\',"\\\0"),$s);
194	    return  "'".str_replace("'",$this->replaceQuote,$s)."'";
195	  }
196	  // undo magic quotes for "
197	  $s = str_replace('\\"','"',$s);
198	  return "'$s'";
199	}
200
201	function _insertid()
202	{
203	  $result = @mysqli_insert_id($this->_connectionID);
204	  if ($result == -1){
205	      if ($this->debug) ADOConnection::outp("mysqli_insert_id() failed : "  . $this->ErrorMsg());
206	  }
207	  return $result;
208	}
209
210	// Only works for INSERT, UPDATE and DELETE query's
211	function _affectedrows()
212	{
213	  $result =  @mysqli_affected_rows($this->_connectionID);
214	  if ($result == -1) {
215	      if ($this->debug) ADOConnection::outp("mysqli_affected_rows() failed : "  . $this->ErrorMsg());
216	  }
217	  return $result;
218	}
219
220 	// See http://www.mysql.com/doc/M/i/Miscellaneous_functions.html
221	// Reference on Last_Insert_ID on the recommended way to simulate sequences
222 	var $_genIDSQL = "update %s set id=LAST_INSERT_ID(id+1);";
223	var $_genSeqSQL = "create table %s (id int not null)";
224	var $_genSeq2SQL = "insert into %s values (%s)";
225	var $_dropSeqSQL = "drop table %s";
226
227	function CreateSequence($seqname='adodbseq',$startID=1)
228	{
229		if (empty($this->_genSeqSQL)) return false;
230		$u = strtoupper($seqname);
231
232		$ok = $this->Execute(sprintf($this->_genSeqSQL,$seqname));
233		if (!$ok) return false;
234		return $this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
235	}
236
237	function GenID($seqname='adodbseq',$startID=1)
238	{
239		// post-nuke sets hasGenID to false
240		if (!$this->hasGenID) return false;
241
242		$getnext = sprintf($this->_genIDSQL,$seqname);
243		$holdtransOK = $this->_transOK; // save the current status
244		$rs = @$this->Execute($getnext);
245		if (!$rs) {
246			if ($holdtransOK) $this->_transOK = true; //if the status was ok before reset
247			$u = strtoupper($seqname);
248			$this->Execute(sprintf($this->_genSeqSQL,$seqname));
249			$this->Execute(sprintf($this->_genSeq2SQL,$seqname,$startID-1));
250			$rs = $this->Execute($getnext);
251		}
252		$this->genID = mysqli_insert_id($this->_connectionID);
253
254		if ($rs) $rs->Close();
255
256		return $this->genID;
257	}
258
259  	function &MetaDatabases()
260	{
261		$query = "SHOW DATABASES";
262		$ret =& $this->Execute($query);
263		if ($ret && is_object($ret)){
264		   $arr = array();
265			while (!$ret->EOF){
266				$db = $ret->Fields('Database');
267				if ($db != 'mysql') $arr[] = $db;
268				$ret->MoveNext();
269			}
270   		   return $arr;
271		}
272        return $ret;
273	}
274
275
276	function &MetaIndexes ($table, $primary = FALSE)
277	{
278		// save old fetch mode
279		global $ADODB_FETCH_MODE;
280
281		$false = false;
282		$save = $ADODB_FETCH_MODE;
283		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
284		if ($this->fetchMode !== FALSE) {
285		       $savem = $this->SetFetchMode(FALSE);
286		}
287
288		// get index details
289		$rs = $this->Execute(sprintf('SHOW INDEXES FROM %s',$table));
290
291		// restore fetchmode
292		if (isset($savem)) {
293		        $this->SetFetchMode($savem);
294		}
295		$ADODB_FETCH_MODE = $save;
296
297		if (!is_object($rs)) {
298		        return $false;
299		}
300
301		$indexes = array ();
302
303		// parse index data into array
304		while ($row = $rs->FetchRow()) {
305		        if ($primary == FALSE AND $row[2] == 'PRIMARY') {
306		                continue;
307		        }
308
309		        if (!isset($indexes[$row[2]])) {
310		                $indexes[$row[2]] = array(
311		                        'unique' => ($row[1] == 0),
312		                        'columns' => array()
313		                );
314		        }
315
316		        $indexes[$row[2]]['columns'][$row[3] - 1] = $row[4];
317		}
318
319		// sort columns by order in the index
320		foreach ( array_keys ($indexes) as $index )
321		{
322		        ksort ($indexes[$index]['columns']);
323		}
324
325		return $indexes;
326	}
327
328
329	// Format date column in sql string given an input format that understands Y M D
330	function SQLDate($fmt, $col=false)
331	{
332		if (!$col) $col = $this->sysTimeStamp;
333		$s = 'DATE_FORMAT('.$col.",'";
334		$concat = false;
335		$len = strlen($fmt);
336		for ($i=0; $i < $len; $i++) {
337			$ch = $fmt[$i];
338			switch($ch) {
339			case 'Y':
340			case 'y':
341				$s .= '%Y';
342				break;
343			case 'Q':
344			case 'q':
345				$s .= "'),Quarter($col)";
346
347				if ($len > $i+1) $s .= ",DATE_FORMAT($col,'";
348				else $s .= ",('";
349				$concat = true;
350				break;
351			case 'M':
352				$s .= '%b';
353				break;
354
355			case 'm':
356				$s .= '%m';
357				break;
358			case 'D':
359			case 'd':
360				$s .= '%d';
361				break;
362
363			case 'H':
364				$s .= '%H';
365				break;
366
367			case 'h':
368				$s .= '%I';
369				break;
370
371			case 'i':
372				$s .= '%i';
373				break;
374
375			case 's':
376				$s .= '%s';
377				break;
378
379			case 'a':
380			case 'A':
381				$s .= '%p';
382				break;
383
384			case 'w':
385				$s .= '%w';
386				break;
387
388			case 'l':
389				$s .= '%W';
390				break;
391
392			default:
393
394				if ($ch == '\\') {
395					$i++;
396					$ch = substr($fmt,$i,1);
397				}
398				$s .= $ch;
399				break;
400			}
401		}
402		$s.="')";
403		if ($concat) $s = "CONCAT($s)";
404		return $s;
405	}
406
407	// returns concatenated string
408	// much easier to run "mysqld --ansi" or "mysqld --sql-mode=PIPES_AS_CONCAT" and use || operator
409	function Concat()
410	{
411		$s = "";
412		$arr = func_get_args();
413
414		// suggestion by andrew005@mnogo.ru
415		$s = implode(',',$arr);
416		if (strlen($s) > 0) return "CONCAT($s)";
417		else return '';
418	}
419
420	// dayFraction is a day in floating point
421	function OffsetDate($dayFraction,$date=false)
422	{
423		if (!$date)
424		  $date = $this->sysDate;
425		return "from_unixtime(unix_timestamp($date)+($dayFraction)*24*3600)";
426	}
427
428	function &MetaTables($ttype=false,$showSchema=false,$mask=false)
429	{
430		$save = $this->metaTablesSQL;
431		if ($showSchema && is_string($showSchema)) {
432			$this->metaTablesSQL .= " from $showSchema";
433		}
434
435		if ($mask) {
436			$mask = $this->qstr($mask);
437			$this->metaTablesSQL .= " like $mask";
438		}
439		$ret =& ADOConnection::MetaTables($ttype,$showSchema);
440
441		$this->metaTablesSQL = $save;
442		return $ret;
443	}
444
445	// "Innox - Juan Carlos Gonzalez" <jgonzalez#innox.com.mx>
446	function MetaForeignKeys( $table, $owner = FALSE, $upper = FALSE, $associative = FALSE )
447	{
448	    if ( !empty($owner) ) {
449	       $table = "$owner.$table";
450	    }
451	    $a_create_table = $this->getRow(sprintf('SHOW CREATE TABLE %s', $table));
452		if ($associative) $create_sql = $a_create_table["Create Table"];
453	    else $create_sql  = $a_create_table[1];
454
455	    $matches = array();
456
457	    if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false;
458	 	$foreign_keys = array();
459	    $num_keys = count($matches[0]);
460	    for ( $i = 0;  $i < $num_keys;  $i ++ ) {
461	        $my_field  = explode('`, `', $matches[1][$i]);
462	        $ref_table = $matches[2][$i];
463	        $ref_field = explode('`, `', $matches[3][$i]);
464
465	        if ( $upper ) {
466	            $ref_table = strtoupper($ref_table);
467	        }
468
469	        $foreign_keys[$ref_table] = array();
470	        $num_fields               = count($my_field);
471	        for ( $j = 0;  $j < $num_fields;  $j ++ ) {
472	            if ( $associative ) {
473	                $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j];
474	            } else {
475	                $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}";
476	            }
477	        }
478	    }
479
480	    return  $foreign_keys;
481	}
482
483 	function &MetaColumns($table)
484	{
485		$false = false;
486		if (!$this->metaColumnsSQL)
487			return $false;
488
489		global $ADODB_FETCH_MODE;
490		$save = $ADODB_FETCH_MODE;
491		$ADODB_FETCH_MODE = ADODB_FETCH_NUM;
492		if ($this->fetchMode !== false)
493			$savem = $this->SetFetchMode(false);
494		$rs = $this->Execute(sprintf($this->metaColumnsSQL,$table));
495		if (isset($savem)) $this->SetFetchMode($savem);
496		$ADODB_FETCH_MODE = $save;
497		if (!is_object($rs))
498			return $false;
499
500		$retarr = array();
501		while (!$rs->EOF) {
502			$fld = new ADOFieldObject();
503			$fld->name = $rs->fields[0];
504			$type = $rs->fields[1];
505
506			// split type into type(length):
507			$fld->scale = null;
508			if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) {
509				$fld->type = $query_array[1];
510				$fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
511				$fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1;
512			} elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) {
513				$fld->type = $query_array[1];
514				$fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
515			} elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) {
516				$fld->type = $query_array[1];
517				$fld->max_length = max(array_map("strlen",explode(",",$query_array[2]))) - 2; // PHP >= 4.0.6
518				$fld->max_length = ($fld->max_length == 0 ? 1 : $fld->max_length);
519			} else {
520				$fld->type = $type;
521				$fld->max_length = -1;
522			}
523			$fld->not_null = ($rs->fields[2] != 'YES');
524			$fld->primary_key = ($rs->fields[3] == 'PRI');
525			$fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false);
526			$fld->binary = (strpos($type,'blob') !== false);
527			$fld->unsigned = (strpos($type,'unsigned') !== false);
528
529			if (!$fld->binary) {
530				$d = $rs->fields[4];
531				if ($d != '' && $d != 'NULL') {
532					$fld->has_default = true;
533					$fld->default_value = $d;
534				} else {
535					$fld->has_default = false;
536				}
537			}
538
539			if ($save == ADODB_FETCH_NUM) {
540				$retarr[] = $fld;
541			} else {
542				$retarr[strtoupper($fld->name)] = $fld;
543			}
544			$rs->MoveNext();
545		}
546
547		$rs->Close();
548		return $retarr;
549	}
550
551	// returns true or false
552	function SelectDB($dbName)
553	{
554//	    $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID);
555	    $this->database = $dbName;
556		$this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
557
558	    if ($this->_connectionID) {
559        	$result = @mysqli_select_db($this->_connectionID, $dbName);
560			if (!$result) {
561		    	ADOConnection::outp("Select of database " . $dbName . " failed. " . $this->ErrorMsg());
562			}
563			return $result;
564		}
565	    return false;
566	}
567
568	// parameters use PostgreSQL convention, not MySQL
569	function &SelectLimit($sql,
570			      $nrows = -1,
571			      $offset = -1,
572			      $inputarr = false,
573			      $arg3 = false,
574			      $secs = 0)
575	{
576		$offsetStr = ($offset >= 0) ? "$offset," : '';
577		if ($nrows < 0) $nrows = '18446744073709551615';
578
579		if ($secs)
580			$rs =& $this->CacheExecute($secs, $sql . " LIMIT $offsetStr$nrows" , $inputarr , $arg3);
581		else
582			$rs =& $this->Execute($sql . " LIMIT $offsetStr$nrows" , $inputarr , $arg3);
583
584		return $rs;
585	}
586
587
588	function Prepare($sql)
589	{
590		return $sql;
591
592		$stmt = $this->_connectionID->prepare($sql);
593		if (!$stmt) {
594			echo $this->ErrorMsg();
595			return $sql;
596		}
597		return array($sql,$stmt);
598	}
599
600
601	// returns queryID or false
602	function _query($sql, $inputarr)
603	{
604	global $ADODB_COUNTRECS;
605
606		if (is_array($sql)) {
607			$stmt = $sql[1];
608			$a = '';
609			foreach($inputarr as $k => $v) {
610				if (is_string($v)) $a .= 's';
611				else if (is_integer($v)) $a .= 'i';
612				else $a .= 'd';
613			}
614
615			$fnarr = array_merge( array($stmt,$a) , $inputarr);
616			$ret = call_user_func_array('mysqli_stmt_bind_param',$fnarr);
617
618			$ret = mysqli_stmt_execute($stmt);
619			return $ret;
620		}
621		if (!$mysql_res =  mysqli_query($this->_connectionID, $sql, ($ADODB_COUNTRECS) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)) {
622		    if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg());
623		    return false;
624		}
625
626		return $mysql_res;
627	}
628
629	/*	Returns: the last error message from previous database operation	*/
630	function ErrorMsg()
631	  {
632	    if (empty($this->_connectionID))
633	      $this->_errorMsg = @mysqli_connect_error();
634	    else
635	      $this->_errorMsg = @mysqli_error($this->_connectionID);
636	    return $this->_errorMsg;
637	  }
638
639	/*	Returns: the last error number from previous database operation	*/
640	function ErrorNo()
641	  {
642	    if (empty($this->_connectionID))
643	      return @mysqli_connect_errno();
644	    else
645	      return @mysqli_errno($this->_connectionID);
646	  }
647
648	// returns true or false
649	function _close()
650	  {
651	    @mysqli_close($this->_connectionID);
652	    $this->_connectionID = false;
653	  }
654
655	/*
656	* Maximum size of C field
657	*/
658	function CharMax()
659	{
660		return 255;
661	}
662
663	/*
664	* Maximum size of X field
665	*/
666	function TextMax()
667	{
668	  return 4294967295;
669	}
670
671
672
673	// this is a set of functions for managing client encoding - very important if the encodings
674	// of your database and your output target (i.e. HTML) don't match
675	// for instance, you may have UTF8 database and server it on-site as latin1 etc.
676	// GetCharSet - get the name of the character set the client is using now
677	// Under Windows, the functions should work with MySQL 4.1.11 and above, the set of charsets supported
678	// depends on compile flags of mysql distribution
679
680  function GetCharSet()
681  {
682    //we will use ADO's builtin property charSet
683    if (!is_callable($this->_connectionID,'character_set_name'))
684    	return false;
685
686    $this->charSet = @$this->_connectionID->character_set_name();
687    if (!$this->charSet) {
688      return false;
689    } else {
690      return $this->charSet;
691    }
692  }
693
694  // SetCharSet - switch the client encoding
695  function SetCharSet($charset_name)
696  {
697    if (!is_callable($this->_connectionID,'set_charset'))
698    	return false;
699
700    if ($this->charSet !== $charset_name) {
701      $if = @$this->_connectionID->set_charset($charset_name);
702      if ($if == "0" & $this->GetCharSet() == $charset_name) {
703        return true;
704      } else return false;
705    } else return true;
706  }
707
708
709
710
711}
712
713/*--------------------------------------------------------------------------------------
714	 Class Name: Recordset
715--------------------------------------------------------------------------------------*/
716
717class ADORecordSet_mysqli extends ADORecordSet{
718
719	var $databaseType = "mysqli";
720	var $canSeek = true;
721
722	function ADORecordSet_mysqli($queryID, $mode = false)
723	{
724	  if ($mode === false)
725	   {
726	      global $ADODB_FETCH_MODE;
727	      $mode = $ADODB_FETCH_MODE;
728	   }
729
730	  switch ($mode)
731	    {
732	    case ADODB_FETCH_NUM:
733	      $this->fetchMode = MYSQLI_NUM;
734	      break;
735	    case ADODB_FETCH_ASSOC:
736	      $this->fetchMode = MYSQLI_ASSOC;
737	      break;
738	    case ADODB_FETCH_DEFAULT:
739	    case ADODB_FETCH_BOTH:
740	    default:
741	      $this->fetchMode = MYSQLI_BOTH;
742	      break;
743	    }
744	  $this->adodbFetchMode = $mode;
745	  $this->ADORecordSet($queryID);
746	}
747
748	function _initrs()
749	{
750	global $ADODB_COUNTRECS;
751
752		$this->_numOfRows = $ADODB_COUNTRECS ? @mysqli_num_rows($this->_queryID) : -1;
753		$this->_numOfFields = @mysqli_num_fields($this->_queryID);
754	}
755
756/*
7571      = MYSQLI_NOT_NULL_FLAG
7582      = MYSQLI_PRI_KEY_FLAG
7594      = MYSQLI_UNIQUE_KEY_FLAG
7608      = MYSQLI_MULTIPLE_KEY_FLAG
76116     = MYSQLI_BLOB_FLAG
76232     = MYSQLI_UNSIGNED_FLAG
76364     = MYSQLI_ZEROFILL_FLAG
764128    = MYSQLI_BINARY_FLAG
765256    = MYSQLI_ENUM_FLAG
766512    = MYSQLI_AUTO_INCREMENT_FLAG
7671024   = MYSQLI_TIMESTAMP_FLAG
7682048   = MYSQLI_SET_FLAG
76932768  = MYSQLI_NUM_FLAG
77016384  = MYSQLI_PART_KEY_FLAG
77132768  = MYSQLI_GROUP_FLAG
77265536  = MYSQLI_UNIQUE_FLAG
773131072 = MYSQLI_BINCMP_FLAG
774*/
775
776	function &FetchField($fieldOffset = -1)
777	{
778		$fieldnr = $fieldOffset;
779		if ($fieldOffset != -1) {
780		  $fieldOffset = mysqli_field_seek($this->_queryID, $fieldnr);
781		}
782		$o = mysqli_fetch_field($this->_queryID);
783		/* Properties of an ADOFieldObject as set by MetaColumns */
784		$o->primary_key = $o->flags & MYSQLI_PRI_KEY_FLAG;
785		$o->not_null = $o->flags & MYSQLI_NOT_NULL_FLAG;
786		$o->auto_increment = $o->flags & MYSQLI_AUTO_INCREMENT_FLAG;
787		$o->binary = $o->flags & MYSQLI_BINARY_FLAG;
788		// $o->blob = $o->flags & MYSQLI_BLOB_FLAG; /* not returned by MetaColumns */
789		$o->unsigned = $o->flags & MYSQLI_UNSIGNED_FLAG;
790
791		return $o;
792	}
793
794	function &GetRowAssoc($upper = true)
795	{
796		if ($this->fetchMode == MYSQLI_ASSOC && !$upper)
797		  return $this->fields;
798		$row =& ADORecordSet::GetRowAssoc($upper);
799		return $row;
800	}
801
802	/* Use associative array to get fields array */
803	function Fields($colname)
804	{
805	  if ($this->fetchMode != MYSQLI_NUM)
806	    return @$this->fields[$colname];
807
808	  if (!$this->bind) {
809	    $this->bind = array();
810	    for ($i = 0; $i < $this->_numOfFields; $i++) {
811	      $o = $this->FetchField($i);
812	      $this->bind[strtoupper($o->name)] = $i;
813	    }
814	  }
815	  return $this->fields[$this->bind[strtoupper($colname)]];
816	}
817
818	function _seek($row)
819	{
820	  if ($this->_numOfRows == 0)
821	    return false;
822
823	  if ($row < 0)
824	    return false;
825
826	  mysqli_data_seek($this->_queryID, $row);
827	  $this->EOF = false;
828	  return true;
829	}
830
831	// 10% speedup to move MoveNext to child class
832	// This is the only implementation that works now (23-10-2003).
833	// Other functions return no or the wrong results.
834	function MoveNext()
835	{
836		if ($this->EOF) return false;
837		$this->_currentRow++;
838		$this->fields = @mysqli_fetch_array($this->_queryID,$this->fetchMode);
839
840		if (is_array($this->fields)) return true;
841		$this->EOF = true;
842		return false;
843	}
844
845	function _fetch()
846	{
847		$this->fields = mysqli_fetch_array($this->_queryID,$this->fetchMode);
848	  	return is_array($this->fields);
849	}
850
851	function _close()
852	{
853		mysqli_free_result($this->_queryID);
854	  	$this->_queryID = false;
855	}
856
857/*
858
8590 = MYSQLI_TYPE_DECIMAL
8601 = MYSQLI_TYPE_CHAR
8611 = MYSQLI_TYPE_TINY
8622 = MYSQLI_TYPE_SHORT
8633 = MYSQLI_TYPE_LONG
8644 = MYSQLI_TYPE_FLOAT
8655 = MYSQLI_TYPE_DOUBLE
8666 = MYSQLI_TYPE_NULL
8677 = MYSQLI_TYPE_TIMESTAMP
8688 = MYSQLI_TYPE_LONGLONG
8699 = MYSQLI_TYPE_INT24
87010 = MYSQLI_TYPE_DATE
87111 = MYSQLI_TYPE_TIME
87212 = MYSQLI_TYPE_DATETIME
87313 = MYSQLI_TYPE_YEAR
87414 = MYSQLI_TYPE_NEWDATE
875247 = MYSQLI_TYPE_ENUM
876248 = MYSQLI_TYPE_SET
877249 = MYSQLI_TYPE_TINY_BLOB
878250 = MYSQLI_TYPE_MEDIUM_BLOB
879251 = MYSQLI_TYPE_LONG_BLOB
880252 = MYSQLI_TYPE_BLOB
881253 = MYSQLI_TYPE_VAR_STRING
882254 = MYSQLI_TYPE_STRING
883255 = MYSQLI_TYPE_GEOMETRY
884*/
885
886	function MetaType($t, $len = -1, $fieldobj = false)
887	{
888		if (is_object($t)) {
889		    $fieldobj = $t;
890		    $t = $fieldobj->type;
891		    $len = $fieldobj->max_length;
892		}
893
894
895		 $len = -1; // mysql max_length is not accurate
896		 switch (strtoupper($t)) {
897		 case 'STRING':
898		 case 'CHAR':
899		 case 'VARCHAR':
900		 case 'TINYBLOB':
901		 case 'TINYTEXT':
902		 case 'ENUM':
903		 case 'SET':
904
905		case MYSQLI_TYPE_TINY_BLOB :
906		case MYSQLI_TYPE_CHAR :
907		case MYSQLI_TYPE_STRING :
908		case MYSQLI_TYPE_ENUM :
909		case MYSQLI_TYPE_SET :
910		case 253 :
911		   if ($len <= $this->blobSize) return 'C';
912
913		case 'TEXT':
914		case 'LONGTEXT':
915		case 'MEDIUMTEXT':
916		   return 'X';
917
918
919		   // php_mysql extension always returns 'blob' even if 'text'
920		   // so we have to check whether binary...
921		case 'IMAGE':
922		case 'LONGBLOB':
923		case 'BLOB':
924		case 'MEDIUMBLOB':
925
926		case MYSQLI_TYPE_BLOB :
927		case MYSQLI_TYPE_LONG_BLOB :
928		case MYSQLI_TYPE_MEDIUM_BLOB :
929
930		   return !empty($fieldobj->binary) ? 'B' : 'X';
931		case 'YEAR':
932		case 'DATE':
933		case MYSQLI_TYPE_DATE :
934		case MYSQLI_TYPE_YEAR :
935
936		   return 'D';
937
938		case 'TIME':
939		case 'DATETIME':
940		case 'TIMESTAMP':
941
942		case MYSQLI_TYPE_DATETIME :
943		case MYSQLI_TYPE_NEWDATE :
944		case MYSQLI_TYPE_TIME :
945		case MYSQLI_TYPE_TIMESTAMP :
946
947			return 'T';
948
949		case 'INT':
950		case 'INTEGER':
951		case 'BIGINT':
952		case 'TINYINT':
953		case 'MEDIUMINT':
954		case 'SMALLINT':
955
956		case MYSQLI_TYPE_INT24 :
957		case MYSQLI_TYPE_LONG :
958		case MYSQLI_TYPE_LONGLONG :
959		case MYSQLI_TYPE_SHORT :
960		case MYSQLI_TYPE_TINY :
961
962		   if (!empty($fieldobj->primary_key)) return 'R';
963
964		   return 'I';
965
966
967		   // Added floating-point types
968		   // Maybe not necessery.
969		 case 'FLOAT':
970		 case 'DOUBLE':
971		   //		case 'DOUBLE PRECISION':
972		 case 'DECIMAL':
973		 case 'DEC':
974		 case 'FIXED':
975		 default:
976		 	//if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>";
977		 	return 'N';
978		}
979	} // function
980
981
982} // rs class
983
984}
985
986?>