Updated adodb syysext to upstream version 5.0.8a.
[Packages/TYPO3.CMS.git] / typo3 / sysext / adodb / adodb / drivers / adodb-mysqli.inc.php
1 <?php
2 /*
3 V5.08 6 Apr 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
12 21 October 2003: MySQLi extension implementation by Arjen de Rijke (a.de.rijke@xs4all.nl)
13 Based on adodb 3.40
14 */
15
16 // security - hide paths
17 if (!defined('ADODB_DIR')) die();
18
19 if (! defined("_ADODB_MYSQLI_LAYER")) {
20 define("_ADODB_MYSQLI_LAYER", 1 );
21
22 // PHP5 compat...
23 if (! defined("MYSQLI_BINARY_FLAG")) define("MYSQLI_BINARY_FLAG", 128);
24 if (!defined('MYSQLI_READ_DEFAULT_GROUP')) define('MYSQLI_READ_DEFAULT_GROUP',1);
25
26 // disable adodb extension - currently incompatible.
27 global $ADODB_EXTENSION; $ADODB_EXTENSION = false;
28
29 class ADODB_mysqli extends ADOConnection {
30 var $databaseType = 'mysqli';
31 var $dataProvider = '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) {
495 $create_sql = isset($a_create_table["Create Table"]) ? $a_create_table["Create Table"] : $a_create_table["Create View"];
496 } else $create_sql = $a_create_table[1];
497
498 $matches = array();
499
500 if (!preg_match_all("/FOREIGN KEY \(`(.*?)`\) REFERENCES `(.*?)` \(`(.*?)`\)/", $create_sql, $matches)) return false;
501 $foreign_keys = array();
502 $num_keys = count($matches[0]);
503 for ( $i = 0; $i < $num_keys; $i ++ ) {
504 $my_field = explode('`, `', $matches[1][$i]);
505 $ref_table = $matches[2][$i];
506 $ref_field = explode('`, `', $matches[3][$i]);
507
508 if ( $upper ) {
509 $ref_table = strtoupper($ref_table);
510 }
511
512 // see https://sourceforge.net/tracker/index.php?func=detail&aid=2287278&group_id=42718&atid=433976
513 if (!isset($foreign_keys[$ref_table])) {
514 $foreign_keys[$ref_table] = array();
515 }
516 $num_fields = count($my_field);
517 for ( $j = 0; $j < $num_fields; $j ++ ) {
518 if ( $associative ) {
519 $foreign_keys[$ref_table][$ref_field[$j]] = $my_field[$j];
520 } else {
521 $foreign_keys[$ref_table][] = "{$my_field[$j]}={$ref_field[$j]}";
522 }
523 }
524 }
525
526 return $foreign_keys;
527 }
528
529 function MetaColumns($table)
530 {
531 $false = false;
532 if (!$this->metaColumnsSQL)
533 return $false;
534
535 global $ADODB_FETCH_MODE;
536 $save = $ADODB_FETCH_MODE;
537 $ADODB_FETCH_MODE = ADODB_FETCH_NUM;
538 if ($this->fetchMode !== false)
539 $savem = $this->SetFetchMode(false);
540 $rs = $this->Execute(sprintf($this->metaColumnsSQL,$table));
541 if (isset($savem)) $this->SetFetchMode($savem);
542 $ADODB_FETCH_MODE = $save;
543 if (!is_object($rs))
544 return $false;
545
546 $retarr = array();
547 while (!$rs->EOF) {
548 $fld = new ADOFieldObject();
549 $fld->name = $rs->fields[0];
550 $type = $rs->fields[1];
551
552 // split type into type(length):
553 $fld->scale = null;
554 if (preg_match("/^(.+)\((\d+),(\d+)/", $type, $query_array)) {
555 $fld->type = $query_array[1];
556 $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
557 $fld->scale = is_numeric($query_array[3]) ? $query_array[3] : -1;
558 } elseif (preg_match("/^(.+)\((\d+)/", $type, $query_array)) {
559 $fld->type = $query_array[1];
560 $fld->max_length = is_numeric($query_array[2]) ? $query_array[2] : -1;
561 } elseif (preg_match("/^(enum)\((.*)\)$/i", $type, $query_array)) {
562 $fld->type = $query_array[1];
563 $arr = explode(",",$query_array[2]);
564 $fld->enums = $arr;
565 $zlen = max(array_map("strlen",$arr)) - 2; // PHP >= 4.0.6
566 $fld->max_length = ($zlen > 0) ? $zlen : 1;
567 } else {
568 $fld->type = $type;
569 $fld->max_length = -1;
570 }
571 $fld->not_null = ($rs->fields[2] != 'YES');
572 $fld->primary_key = ($rs->fields[3] == 'PRI');
573 $fld->auto_increment = (strpos($rs->fields[5], 'auto_increment') !== false);
574 $fld->binary = (strpos($type,'blob') !== false);
575 $fld->unsigned = (strpos($type,'unsigned') !== false);
576 $fld->zerofill = (strpos($type,'zerofill') !== false);
577
578 if (!$fld->binary) {
579 $d = $rs->fields[4];
580 if ($d != '' && $d != 'NULL') {
581 $fld->has_default = true;
582 $fld->default_value = $d;
583 } else {
584 $fld->has_default = false;
585 }
586 }
587
588 if ($save == ADODB_FETCH_NUM) {
589 $retarr[] = $fld;
590 } else {
591 $retarr[strtoupper($fld->name)] = $fld;
592 }
593 $rs->MoveNext();
594 }
595
596 $rs->Close();
597 return $retarr;
598 }
599
600 // returns true or false
601 function SelectDB($dbName)
602 {
603 // $this->_connectionID = $this->mysqli_resolve_link($this->_connectionID);
604 $this->database = $dbName;
605 $this->databaseName = $dbName; # obsolete, retained for compat with older adodb versions
606
607 if ($this->_connectionID) {
608 $result = @mysqli_select_db($this->_connectionID, $dbName);
609 if (!$result) {
610 ADOConnection::outp("Select of database " . $dbName . " failed. " . $this->ErrorMsg());
611 }
612 return $result;
613 }
614 return false;
615 }
616
617 // parameters use PostgreSQL convention, not MySQL
618 function SelectLimit($sql,
619 $nrows = -1,
620 $offset = -1,
621 $inputarr = false,
622 $secs = 0)
623 {
624 $offsetStr = ($offset >= 0) ? "$offset," : '';
625 if ($nrows < 0) $nrows = '18446744073709551615';
626
627 if ($secs)
628 $rs = $this->CacheExecute($secs, $sql . " LIMIT $offsetStr$nrows" , $inputarr );
629 else
630 $rs = $this->Execute($sql . " LIMIT $offsetStr$nrows" , $inputarr );
631
632 return $rs;
633 }
634
635
636 function Prepare($sql)
637 {
638 return $sql;
639 $stmt = $this->_connectionID->prepare($sql);
640 if (!$stmt) {
641 echo $this->ErrorMsg();
642 return $sql;
643 }
644 return array($sql,$stmt);
645 }
646
647
648 // returns queryID or false
649 function _query($sql, $inputarr)
650 {
651 global $ADODB_COUNTRECS;
652 // Move to the next recordset, or return false if there is none. In a stored proc
653 // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result
654 // returns false. I think this is because the last "recordset" is actually just the
655 // return value of the stored proc (ie the number of rows affected).
656 // Commented out for reasons of performance. You should retrieve every recordset yourself.
657 // if (!mysqli_next_result($this->connection->_connectionID)) return false;
658
659 if (is_array($sql)) {
660
661 // Prepare() not supported because mysqli_stmt_execute does not return a recordset, but
662 // returns as bound variables.
663
664 $stmt = $sql[1];
665 $a = '';
666 foreach($inputarr as $k => $v) {
667 if (is_string($v)) $a .= 's';
668 else if (is_integer($v)) $a .= 'i';
669 else $a .= 'd';
670 }
671
672 $fnarr = array_merge( array($stmt,$a) , $inputarr);
673 $ret = call_user_func_array('mysqli_stmt_bind_param',$fnarr);
674 $ret = mysqli_stmt_execute($stmt);
675 return $ret;
676 }
677
678 /*
679 if (!$mysql_res = mysqli_query($this->_connectionID, $sql, ($ADODB_COUNTRECS) ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)) {
680 if ($this->debug) ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg());
681 return false;
682 }
683
684 return $mysql_res;
685 */
686
687 if( $rs = mysqli_multi_query($this->_connectionID, $sql.';') )//Contributed by "Geisel Sierote" <geisel#4up.com.br>
688 {
689 $rs = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->_connectionID ) : @mysqli_use_result( $this->_connectionID );
690 return $rs ? $rs : true; // mysqli_more_results( $this->_connectionID )
691 } else {
692 if($this->debug)
693 ADOConnection::outp("Query: " . $sql . " failed. " . $this->ErrorMsg());
694 return false;
695 }
696 }
697
698 /* Returns: the last error message from previous database operation */
699 function ErrorMsg()
700 {
701 if (empty($this->_connectionID))
702 $this->_errorMsg = @mysqli_connect_error();
703 else
704 $this->_errorMsg = @mysqli_error($this->_connectionID);
705 return $this->_errorMsg;
706 }
707
708 /* Returns: the last error number from previous database operation */
709 function ErrorNo()
710 {
711 if (empty($this->_connectionID))
712 return @mysqli_connect_errno();
713 else
714 return @mysqli_errno($this->_connectionID);
715 }
716
717 // returns true or false
718 function _close()
719 {
720 @mysqli_close($this->_connectionID);
721 $this->_connectionID = false;
722 }
723
724 /*
725 * Maximum size of C field
726 */
727 function CharMax()
728 {
729 return 255;
730 }
731
732 /*
733 * Maximum size of X field
734 */
735 function TextMax()
736 {
737 return 4294967295;
738 }
739
740
741
742 // this is a set of functions for managing client encoding - very important if the encodings
743 // of your database and your output target (i.e. HTML) don't match
744 // for instance, you may have UTF8 database and server it on-site as latin1 etc.
745 // GetCharSet - get the name of the character set the client is using now
746 // Under Windows, the functions should work with MySQL 4.1.11 and above, the set of charsets supported
747 // depends on compile flags of mysql distribution
748
749 function GetCharSet()
750 {
751 //we will use ADO's builtin property charSet
752 if (!method_exists($this->_connectionID,'character_set_name'))
753 return false;
754
755 $this->charSet = @$this->_connectionID->character_set_name();
756 if (!$this->charSet) {
757 return false;
758 } else {
759 return $this->charSet;
760 }
761 }
762
763 // SetCharSet - switch the client encoding
764 function SetCharSet($charset_name)
765 {
766 if (!method_exists($this->_connectionID,'set_charset'))
767 return false;
768
769 if ($this->charSet !== $charset_name) {
770 $if = @$this->_connectionID->set_charset($charset_name);
771 if ($if == "0" & $this->GetCharSet() == $charset_name) {
772 return true;
773 } else return false;
774 } else return true;
775 }
776
777
778
779
780 }
781
782 /*--------------------------------------------------------------------------------------
783 Class Name: Recordset
784 --------------------------------------------------------------------------------------*/
785
786 class ADORecordSet_mysqli extends ADORecordSet{
787
788 var $databaseType = "mysqli";
789 var $canSeek = true;
790
791 function ADORecordSet_mysqli($queryID, $mode = false)
792 {
793 if ($mode === false)
794 {
795 global $ADODB_FETCH_MODE;
796 $mode = $ADODB_FETCH_MODE;
797 }
798
799 switch ($mode)
800 {
801 case ADODB_FETCH_NUM:
802 $this->fetchMode = MYSQLI_NUM;
803 break;
804 case ADODB_FETCH_ASSOC:
805 $this->fetchMode = MYSQLI_ASSOC;
806 break;
807 case ADODB_FETCH_DEFAULT:
808 case ADODB_FETCH_BOTH:
809 default:
810 $this->fetchMode = MYSQLI_BOTH;
811 break;
812 }
813 $this->adodbFetchMode = $mode;
814 $this->ADORecordSet($queryID);
815 }
816
817 function _initrs()
818 {
819 global $ADODB_COUNTRECS;
820
821 $this->_numOfRows = $ADODB_COUNTRECS ? @mysqli_num_rows($this->_queryID) : -1;
822 $this->_numOfFields = @mysqli_num_fields($this->_queryID);
823 }
824
825 /*
826 1 = MYSQLI_NOT_NULL_FLAG
827 2 = MYSQLI_PRI_KEY_FLAG
828 4 = MYSQLI_UNIQUE_KEY_FLAG
829 8 = MYSQLI_MULTIPLE_KEY_FLAG
830 16 = MYSQLI_BLOB_FLAG
831 32 = MYSQLI_UNSIGNED_FLAG
832 64 = MYSQLI_ZEROFILL_FLAG
833 128 = MYSQLI_BINARY_FLAG
834 256 = MYSQLI_ENUM_FLAG
835 512 = MYSQLI_AUTO_INCREMENT_FLAG
836 1024 = MYSQLI_TIMESTAMP_FLAG
837 2048 = MYSQLI_SET_FLAG
838 32768 = MYSQLI_NUM_FLAG
839 16384 = MYSQLI_PART_KEY_FLAG
840 32768 = MYSQLI_GROUP_FLAG
841 65536 = MYSQLI_UNIQUE_FLAG
842 131072 = MYSQLI_BINCMP_FLAG
843 */
844
845 function FetchField($fieldOffset = -1)
846 {
847 $fieldnr = $fieldOffset;
848 if ($fieldOffset != -1) {
849 $fieldOffset = @mysqli_field_seek($this->_queryID, $fieldnr);
850 }
851 $o = @mysqli_fetch_field($this->_queryID);
852 if (!$o) return false;
853 /* Properties of an ADOFieldObject as set by MetaColumns */
854 $o->primary_key = $o->flags & MYSQLI_PRI_KEY_FLAG;
855 $o->not_null = $o->flags & MYSQLI_NOT_NULL_FLAG;
856 $o->auto_increment = $o->flags & MYSQLI_AUTO_INCREMENT_FLAG;
857 $o->binary = $o->flags & MYSQLI_BINARY_FLAG;
858 // $o->blob = $o->flags & MYSQLI_BLOB_FLAG; /* not returned by MetaColumns */
859 $o->unsigned = $o->flags & MYSQLI_UNSIGNED_FLAG;
860
861 return $o;
862 }
863
864 function GetRowAssoc($upper = true)
865 {
866 if ($this->fetchMode == MYSQLI_ASSOC && !$upper)
867 return $this->fields;
868 $row = ADORecordSet::GetRowAssoc($upper);
869 return $row;
870 }
871
872 /* Use associative array to get fields array */
873 function Fields($colname)
874 {
875 if ($this->fetchMode != MYSQLI_NUM)
876 return @$this->fields[$colname];
877
878 if (!$this->bind) {
879 $this->bind = array();
880 for ($i = 0; $i < $this->_numOfFields; $i++) {
881 $o = $this->FetchField($i);
882 $this->bind[strtoupper($o->name)] = $i;
883 }
884 }
885 return $this->fields[$this->bind[strtoupper($colname)]];
886 }
887
888 function _seek($row)
889 {
890 if ($this->_numOfRows == 0)
891 return false;
892
893 if ($row < 0)
894 return false;
895
896 mysqli_data_seek($this->_queryID, $row);
897 $this->EOF = false;
898 return true;
899 }
900
901
902 function NextRecordSet()
903 {
904 global $ADODB_COUNTRECS;
905
906 mysqli_free_result($this->_queryID);
907 $this->_queryID = -1;
908 // Move to the next recordset, or return false if there is none. In a stored proc
909 // call, mysqli_next_result returns true for the last "recordset", but mysqli_store_result
910 // returns false. I think this is because the last "recordset" is actually just the
911 // return value of the stored proc (ie the number of rows affected).
912 if(!mysqli_next_result($this->connection->_connectionID)) {
913 return false;
914 }
915 // CD: There is no $this->_connectionID variable, at least in the ADO version I'm using
916 $this->_queryID = ($ADODB_COUNTRECS) ? @mysqli_store_result( $this->connection->_connectionID )
917 : @mysqli_use_result( $this->connection->_connectionID );
918 if(!$this->_queryID) {
919 return false;
920 }
921 $this->_inited = false;
922 $this->bind = false;
923 $this->_currentRow = -1;
924 $this->Init();
925 return true;
926 }
927
928 // 10% speedup to move MoveNext to child class
929 // This is the only implementation that works now (23-10-2003).
930 // Other functions return no or the wrong results.
931 function MoveNext()
932 {
933 if ($this->EOF) return false;
934 $this->_currentRow++;
935 $this->fields = @mysqli_fetch_array($this->_queryID,$this->fetchMode);
936
937 if (is_array($this->fields)) return true;
938 $this->EOF = true;
939 return false;
940 }
941
942 function _fetch()
943 {
944 $this->fields = mysqli_fetch_array($this->_queryID,$this->fetchMode);
945 return is_array($this->fields);
946 }
947
948 function _close()
949 {
950 mysqli_free_result($this->_queryID);
951 $this->_queryID = false;
952 }
953
954 /*
955
956 0 = MYSQLI_TYPE_DECIMAL
957 1 = MYSQLI_TYPE_CHAR
958 1 = MYSQLI_TYPE_TINY
959 2 = MYSQLI_TYPE_SHORT
960 3 = MYSQLI_TYPE_LONG
961 4 = MYSQLI_TYPE_FLOAT
962 5 = MYSQLI_TYPE_DOUBLE
963 6 = MYSQLI_TYPE_NULL
964 7 = MYSQLI_TYPE_TIMESTAMP
965 8 = MYSQLI_TYPE_LONGLONG
966 9 = MYSQLI_TYPE_INT24
967 10 = MYSQLI_TYPE_DATE
968 11 = MYSQLI_TYPE_TIME
969 12 = MYSQLI_TYPE_DATETIME
970 13 = MYSQLI_TYPE_YEAR
971 14 = MYSQLI_TYPE_NEWDATE
972 247 = MYSQLI_TYPE_ENUM
973 248 = MYSQLI_TYPE_SET
974 249 = MYSQLI_TYPE_TINY_BLOB
975 250 = MYSQLI_TYPE_MEDIUM_BLOB
976 251 = MYSQLI_TYPE_LONG_BLOB
977 252 = MYSQLI_TYPE_BLOB
978 253 = MYSQLI_TYPE_VAR_STRING
979 254 = MYSQLI_TYPE_STRING
980 255 = MYSQLI_TYPE_GEOMETRY
981 */
982
983 function MetaType($t, $len = -1, $fieldobj = false)
984 {
985 if (is_object($t)) {
986 $fieldobj = $t;
987 $t = $fieldobj->type;
988 $len = $fieldobj->max_length;
989 }
990
991
992 $len = -1; // mysql max_length is not accurate
993 switch (strtoupper($t)) {
994 case 'STRING':
995 case 'CHAR':
996 case 'VARCHAR':
997 case 'TINYBLOB':
998 case 'TINYTEXT':
999 case 'ENUM':
1000 case 'SET':
1001
1002 case MYSQLI_TYPE_TINY_BLOB :
1003 #case MYSQLI_TYPE_CHAR :
1004 case MYSQLI_TYPE_STRING :
1005 case MYSQLI_TYPE_ENUM :
1006 case MYSQLI_TYPE_SET :
1007 case 253 :
1008 if ($len <= $this->blobSize) return 'C';
1009
1010 case 'TEXT':
1011 case 'LONGTEXT':
1012 case 'MEDIUMTEXT':
1013 return 'X';
1014
1015
1016 // php_mysql extension always returns 'blob' even if 'text'
1017 // so we have to check whether binary...
1018 case 'IMAGE':
1019 case 'LONGBLOB':
1020 case 'BLOB':
1021 case 'MEDIUMBLOB':
1022
1023 case MYSQLI_TYPE_BLOB :
1024 case MYSQLI_TYPE_LONG_BLOB :
1025 case MYSQLI_TYPE_MEDIUM_BLOB :
1026
1027 return !empty($fieldobj->binary) ? 'B' : 'X';
1028 case 'YEAR':
1029 case 'DATE':
1030 case MYSQLI_TYPE_DATE :
1031 case MYSQLI_TYPE_YEAR :
1032
1033 return 'D';
1034
1035 case 'TIME':
1036 case 'DATETIME':
1037 case 'TIMESTAMP':
1038
1039 case MYSQLI_TYPE_DATETIME :
1040 case MYSQLI_TYPE_NEWDATE :
1041 case MYSQLI_TYPE_TIME :
1042 case MYSQLI_TYPE_TIMESTAMP :
1043
1044 return 'T';
1045
1046 case 'INT':
1047 case 'INTEGER':
1048 case 'BIGINT':
1049 case 'TINYINT':
1050 case 'MEDIUMINT':
1051 case 'SMALLINT':
1052
1053 case MYSQLI_TYPE_INT24 :
1054 case MYSQLI_TYPE_LONG :
1055 case MYSQLI_TYPE_LONGLONG :
1056 case MYSQLI_TYPE_SHORT :
1057 case MYSQLI_TYPE_TINY :
1058
1059 if (!empty($fieldobj->primary_key)) return 'R';
1060
1061 return 'I';
1062
1063
1064 // Added floating-point types
1065 // Maybe not necessery.
1066 case 'FLOAT':
1067 case 'DOUBLE':
1068 // case 'DOUBLE PRECISION':
1069 case 'DECIMAL':
1070 case 'DEC':
1071 case 'FIXED':
1072 default:
1073 //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>";
1074 return 'N';
1075 }
1076 } // function
1077
1078
1079 } // rs class
1080
1081 }
1082
1083 class ADORecordSet_array_mysqli extends ADORecordSet_array {
1084
1085 function ADORecordSet_array_mysqli($id=-1,$mode=false)
1086 {
1087 $this->ADORecordSet_array($id,$mode);
1088 }
1089
1090 function MetaType($t, $len = -1, $fieldobj = false)
1091 {
1092 if (is_object($t)) {
1093 $fieldobj = $t;
1094 $t = $fieldobj->type;
1095 $len = $fieldobj->max_length;
1096 }
1097
1098
1099 $len = -1; // mysql max_length is not accurate
1100 switch (strtoupper($t)) {
1101 case 'STRING':
1102 case 'CHAR':
1103 case 'VARCHAR':
1104 case 'TINYBLOB':
1105 case 'TINYTEXT':
1106 case 'ENUM':
1107 case 'SET':
1108
1109 case MYSQLI_TYPE_TINY_BLOB :
1110 #case MYSQLI_TYPE_CHAR :
1111 case MYSQLI_TYPE_STRING :
1112 case MYSQLI_TYPE_ENUM :
1113 case MYSQLI_TYPE_SET :
1114 case 253 :
1115 if ($len <= $this->blobSize) return 'C';
1116
1117 case 'TEXT':
1118 case 'LONGTEXT':
1119 case 'MEDIUMTEXT':
1120 return 'X';
1121
1122
1123 // php_mysql extension always returns 'blob' even if 'text'
1124 // so we have to check whether binary...
1125 case 'IMAGE':
1126 case 'LONGBLOB':
1127 case 'BLOB':
1128 case 'MEDIUMBLOB':
1129
1130 case MYSQLI_TYPE_BLOB :
1131 case MYSQLI_TYPE_LONG_BLOB :
1132 case MYSQLI_TYPE_MEDIUM_BLOB :
1133
1134 return !empty($fieldobj->binary) ? 'B' : 'X';
1135 case 'YEAR':
1136 case 'DATE':
1137 case MYSQLI_TYPE_DATE :
1138 case MYSQLI_TYPE_YEAR :
1139
1140 return 'D';
1141
1142 case 'TIME':
1143 case 'DATETIME':
1144 case 'TIMESTAMP':
1145
1146 case MYSQLI_TYPE_DATETIME :
1147 case MYSQLI_TYPE_NEWDATE :
1148 case MYSQLI_TYPE_TIME :
1149 case MYSQLI_TYPE_TIMESTAMP :
1150
1151 return 'T';
1152
1153 case 'INT':
1154 case 'INTEGER':
1155 case 'BIGINT':
1156 case 'TINYINT':
1157 case 'MEDIUMINT':
1158 case 'SMALLINT':
1159
1160 case MYSQLI_TYPE_INT24 :
1161 case MYSQLI_TYPE_LONG :
1162 case MYSQLI_TYPE_LONGLONG :
1163 case MYSQLI_TYPE_SHORT :
1164 case MYSQLI_TYPE_TINY :
1165
1166 if (!empty($fieldobj->primary_key)) return 'R';
1167
1168 return 'I';
1169
1170
1171 // Added floating-point types
1172 // Maybe not necessery.
1173 case 'FLOAT':
1174 case 'DOUBLE':
1175 // case 'DOUBLE PRECISION':
1176 case 'DECIMAL':
1177 case 'DEC':
1178 case 'FIXED':
1179 default:
1180 //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>";
1181 return 'N';
1182 }
1183 } // function
1184
1185 }
1186
1187 ?>