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