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