source: moodle/trunk/fuentes/lib/adodb/drivers/adodb-mysqli.inc.php @ 1331

Last change on this file since 1331 was 1331, checked in by jrpelegrina, 3 years ago

Updated to moodle 3.0.3

File size: 32.1 KB
Line 
1<?php
2/*
3@version   v5.20.1  06-Dec-2015
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
1821 October 2003: MySQLi extension implementation by Arjen de Rijke (a.de.rijke@xs4all.nl)
19Based on adodb 3.40
20*/
21
22// security - hide paths
23if (!defined('ADODB_DIR')) die();
24
25if (! 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
35class 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
882class 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/*
9201      = MYSQLI_NOT_NULL_FLAG
9212      = MYSQLI_PRI_KEY_FLAG
9224      = MYSQLI_UNIQUE_KEY_FLAG
9238      = MYSQLI_MULTIPLE_KEY_FLAG
92416     = MYSQLI_BLOB_FLAG
92532     = MYSQLI_UNSIGNED_FLAG
92664     = MYSQLI_ZEROFILL_FLAG
927128    = MYSQLI_BINARY_FLAG
928256    = MYSQLI_ENUM_FLAG
929512    = MYSQLI_AUTO_INCREMENT_FLAG
9301024   = MYSQLI_TIMESTAMP_FLAG
9312048   = MYSQLI_SET_FLAG
93232768  = MYSQLI_NUM_FLAG
93316384  = MYSQLI_PART_KEY_FLAG
93432768  = MYSQLI_GROUP_FLAG
93565536  = MYSQLI_UNIQUE_FLAG
936131072 = 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)) return true;
1037                $this->EOF = true;
1038                return false;
1039        }
1040
1041        function _fetch()
1042        {
1043                $this->fields = mysqli_fetch_array($this->_queryID,$this->fetchMode);
1044                $this->_updatefields();
1045                return is_array($this->fields);
1046        }
1047
1048        function _close()
1049        {
1050                //if results are attached to this pointer from Stored Proceedure calls, the next standard query will die 2014
1051                //only a problem with persistant connections
1052
1053                while (@mysqli_more_results($this->connection->_connectionID)) {
1054                        @mysqli_next_result($this->connection->_connectionID);
1055                }
1056
1057                @mysqli_free_result($this->_queryID);
1058                $this->_queryID = false;
1059        }
1060
1061/*
1062
10630 = MYSQLI_TYPE_DECIMAL
10641 = MYSQLI_TYPE_CHAR
10651 = MYSQLI_TYPE_TINY
10662 = MYSQLI_TYPE_SHORT
10673 = MYSQLI_TYPE_LONG
10684 = MYSQLI_TYPE_FLOAT
10695 = MYSQLI_TYPE_DOUBLE
10706 = MYSQLI_TYPE_NULL
10717 = MYSQLI_TYPE_TIMESTAMP
10728 = MYSQLI_TYPE_LONGLONG
10739 = MYSQLI_TYPE_INT24
107410 = MYSQLI_TYPE_DATE
107511 = MYSQLI_TYPE_TIME
107612 = MYSQLI_TYPE_DATETIME
107713 = MYSQLI_TYPE_YEAR
107814 = MYSQLI_TYPE_NEWDATE
1079247 = MYSQLI_TYPE_ENUM
1080248 = MYSQLI_TYPE_SET
1081249 = MYSQLI_TYPE_TINY_BLOB
1082250 = MYSQLI_TYPE_MEDIUM_BLOB
1083251 = MYSQLI_TYPE_LONG_BLOB
1084252 = MYSQLI_TYPE_BLOB
1085253 = MYSQLI_TYPE_VAR_STRING
1086254 = MYSQLI_TYPE_STRING
1087255 = MYSQLI_TYPE_GEOMETRY
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                // php_mysql extension always returns 'blob' even if 'text'
1123                // so we have to check whether binary...
1124                case 'IMAGE':
1125                case 'LONGBLOB':
1126                case 'BLOB':
1127                case 'MEDIUMBLOB':
1128
1129                case MYSQLI_TYPE_BLOB :
1130                case MYSQLI_TYPE_LONG_BLOB :
1131                case MYSQLI_TYPE_MEDIUM_BLOB :
1132                        return !empty($fieldobj->binary) ? 'B' : 'X';
1133
1134                case 'YEAR':
1135                case 'DATE':
1136                case MYSQLI_TYPE_DATE :
1137                case MYSQLI_TYPE_YEAR :
1138                        return 'D';
1139
1140                case 'TIME':
1141                case 'DATETIME':
1142                case 'TIMESTAMP':
1143
1144                case MYSQLI_TYPE_DATETIME :
1145                case MYSQLI_TYPE_NEWDATE :
1146                case MYSQLI_TYPE_TIME :
1147                case MYSQLI_TYPE_TIMESTAMP :
1148                        return 'T';
1149
1150                case 'INT':
1151                case 'INTEGER':
1152                case 'BIGINT':
1153                case 'TINYINT':
1154                case 'MEDIUMINT':
1155                case 'SMALLINT':
1156
1157                case MYSQLI_TYPE_INT24 :
1158                case MYSQLI_TYPE_LONG :
1159                case MYSQLI_TYPE_LONGLONG :
1160                case MYSQLI_TYPE_SHORT :
1161                case MYSQLI_TYPE_TINY :
1162                        if (!empty($fieldobj->primary_key)) return 'R';
1163                        return 'I';
1164
1165                // Added floating-point types
1166                // Maybe not necessery.
1167                case 'FLOAT':
1168                case 'DOUBLE':
1169//              case 'DOUBLE PRECISION':
1170                case 'DECIMAL':
1171                case 'DEC':
1172                case 'FIXED':
1173                default:
1174                        //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>";
1175                        return 'N';
1176                }
1177        } // function
1178
1179
1180} // rs class
1181
1182}
1183
1184class ADORecordSet_array_mysqli extends ADORecordSet_array {
1185
1186        function __construct($id=-1,$mode=false)
1187        {
1188                parent::__construct($id,$mode);
1189        }
1190
1191        function MetaType($t, $len = -1, $fieldobj = false)
1192        {
1193                if (is_object($t)) {
1194                        $fieldobj = $t;
1195                        $t = $fieldobj->type;
1196                        $len = $fieldobj->max_length;
1197                }
1198
1199
1200                $len = -1; // mysql max_length is not accurate
1201                switch (strtoupper($t)) {
1202                case 'STRING':
1203                case 'CHAR':
1204                case 'VARCHAR':
1205                case 'TINYBLOB':
1206                case 'TINYTEXT':
1207                case 'ENUM':
1208                case 'SET':
1209
1210                case MYSQLI_TYPE_TINY_BLOB :
1211                #case MYSQLI_TYPE_CHAR :
1212                case MYSQLI_TYPE_STRING :
1213                case MYSQLI_TYPE_ENUM :
1214                case MYSQLI_TYPE_SET :
1215                case 253 :
1216                        if ($len <= $this->blobSize) return 'C';
1217
1218                case 'TEXT':
1219                case 'LONGTEXT':
1220                case 'MEDIUMTEXT':
1221                        return 'X';
1222
1223                // php_mysql extension always returns 'blob' even if 'text'
1224                // so we have to check whether binary...
1225                case 'IMAGE':
1226                case 'LONGBLOB':
1227                case 'BLOB':
1228                case 'MEDIUMBLOB':
1229
1230                case MYSQLI_TYPE_BLOB :
1231                case MYSQLI_TYPE_LONG_BLOB :
1232                case MYSQLI_TYPE_MEDIUM_BLOB :
1233
1234                        return !empty($fieldobj->binary) ? 'B' : 'X';
1235                case 'YEAR':
1236                case 'DATE':
1237                case MYSQLI_TYPE_DATE :
1238                case MYSQLI_TYPE_YEAR :
1239
1240                        return 'D';
1241
1242                case 'TIME':
1243                case 'DATETIME':
1244                case 'TIMESTAMP':
1245
1246                case MYSQLI_TYPE_DATETIME :
1247                case MYSQLI_TYPE_NEWDATE :
1248                case MYSQLI_TYPE_TIME :
1249                case MYSQLI_TYPE_TIMESTAMP :
1250
1251                        return 'T';
1252
1253                case 'INT':
1254                case 'INTEGER':
1255                case 'BIGINT':
1256                case 'TINYINT':
1257                case 'MEDIUMINT':
1258                case 'SMALLINT':
1259
1260                case MYSQLI_TYPE_INT24 :
1261                case MYSQLI_TYPE_LONG :
1262                case MYSQLI_TYPE_LONGLONG :
1263                case MYSQLI_TYPE_SHORT :
1264                case MYSQLI_TYPE_TINY :
1265
1266                        if (!empty($fieldobj->primary_key)) return 'R';
1267
1268                        return 'I';
1269
1270
1271                // Added floating-point types
1272                // Maybe not necessery.
1273                case 'FLOAT':
1274                case 'DOUBLE':
1275//              case 'DOUBLE PRECISION':
1276                case 'DECIMAL':
1277                case 'DEC':
1278                case 'FIXED':
1279                default:
1280                        //if (!is_numeric($t)) echo "<p>--- Error in type matching $t -----</p>";
1281                        return 'N';
1282                }
1283        } // function
1284
1285}
Note: See TracBrowser for help on using the repository browser.