]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/DB/mysqli.php
Merge branch 'master' of git.gnu.io:gnu/gnu-social into mmn_fixes
[quix0rs-gnu-social.git] / extlib / DB / mysqli.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6  * The PEAR DB driver for PHP's mysqli extension
7  * for interacting with MySQL databases
8  *
9  * PHP version 5
10  *
11  * LICENSE: This source file is subject to version 3.0 of the PHP license
12  * that is available through the world-wide-web at the following URI:
13  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
14  * the PHP License and are unable to obtain it through the web, please
15  * send a note to license@php.net so we can mail you a copy immediately.
16  *
17  * @category   Database
18  * @package    DB
19  * @author     Daniel Convissor <danielc@php.net>
20  * @copyright  1997-2007 The PHP Group
21  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
22  * @version    CVS: $Id$
23  * @link       http://pear.php.net/package/DB
24  */
25
26 /**
27  * Obtain the DB_common class so it can be extended from
28  */
29 require_once 'DB/common.php';
30
31 /**
32  * The methods PEAR DB uses to interact with PHP's mysqli extension
33  * for interacting with MySQL databases
34  *
35  * This is for MySQL versions 4.1 and above.  Requires PHP 5.
36  *
37  * Note that persistent connections no longer exist.
38  *
39  * These methods overload the ones declared in DB_common.
40  *
41  * @category   Database
42  * @package    DB
43  * @author     Daniel Convissor <danielc@php.net>
44  * @copyright  1997-2007 The PHP Group
45  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
46  * @version    Release: 1.9.2
47  * @link       http://pear.php.net/package/DB
48  * @since      Class functional since Release 1.6.3
49  */
50 class DB_mysqli extends DB_common
51 {
52     // {{{ properties
53
54     /**
55      * The DB driver type (mysql, oci8, odbc, etc.)
56      * @var string
57      */
58     var $phptype = 'mysqli';
59
60     /**
61      * The database syntax variant to be used (db2, access, etc.), if any
62      * @var string
63      */
64     var $dbsyntax = 'mysqli';
65
66     /**
67      * The capabilities of this DB implementation
68      *
69      * The 'new_link' element contains the PHP version that first provided
70      * new_link support for this DBMS.  Contains false if it's unsupported.
71      *
72      * Meaning of the 'limit' element:
73      *   + 'emulate' = emulate with fetch row by number
74      *   + 'alter'   = alter the query
75      *   + false     = skip rows
76      *
77      * @var array
78      */
79     var $features = array(
80         'limit'         => 'alter',
81         'new_link'      => false,
82         'numrows'       => true,
83         'pconnect'      => false,
84         'prepare'       => false,
85         'ssl'           => true,
86         'transactions'  => true,
87     );
88
89     /**
90      * A mapping of native error codes to DB error codes
91      * @var array
92      */
93     var $errorcode_map = array(
94         1004 => DB_ERROR_CANNOT_CREATE,
95         1005 => DB_ERROR_CANNOT_CREATE,
96         1006 => DB_ERROR_CANNOT_CREATE,
97         1007 => DB_ERROR_ALREADY_EXISTS,
98         1008 => DB_ERROR_CANNOT_DROP,
99         1022 => DB_ERROR_ALREADY_EXISTS,
100         1044 => DB_ERROR_ACCESS_VIOLATION,
101         1046 => DB_ERROR_NODBSELECTED,
102         1048 => DB_ERROR_CONSTRAINT,
103         1049 => DB_ERROR_NOSUCHDB,
104         1050 => DB_ERROR_ALREADY_EXISTS,
105         1051 => DB_ERROR_NOSUCHTABLE,
106         1054 => DB_ERROR_NOSUCHFIELD,
107         1061 => DB_ERROR_ALREADY_EXISTS,
108         1062 => DB_ERROR_ALREADY_EXISTS,
109         1064 => DB_ERROR_SYNTAX,
110         1091 => DB_ERROR_NOT_FOUND,
111         1100 => DB_ERROR_NOT_LOCKED,
112         1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
113         1142 => DB_ERROR_ACCESS_VIOLATION,
114         1146 => DB_ERROR_NOSUCHTABLE,
115         1216 => DB_ERROR_CONSTRAINT,
116         1217 => DB_ERROR_CONSTRAINT,
117         1356 => DB_ERROR_DIVZERO,
118         1451 => DB_ERROR_CONSTRAINT,
119         1452 => DB_ERROR_CONSTRAINT,
120     );
121
122     /**
123      * The raw database connection created by PHP
124      * @var resource
125      */
126     var $connection;
127
128     /**
129      * The DSN information for connecting to a database
130      * @var array
131      */
132     var $dsn = array();
133
134
135     /**
136      * Should data manipulation queries be committed automatically?
137      * @var bool
138      * @access private
139      */
140     var $autocommit = true;
141
142     /**
143      * The quantity of transactions begun
144      *
145      * {@internal  While this is private, it can't actually be designated
146      * private in PHP 5 because it is directly accessed in the test suite.}}
147      *
148      * @var integer
149      * @access private
150      */
151     var $transaction_opcount = 0;
152
153     /**
154      * The database specified in the DSN
155      *
156      * It's a fix to allow calls to different databases in the same script.
157      *
158      * @var string
159      * @access private
160      */
161     var $_db = '';
162
163     /**
164      * Array for converting MYSQLI_*_FLAG constants to text values
165      * @var    array
166      * @access public
167      * @since  Property available since Release 1.6.5
168      */
169     var $mysqli_flags = array(
170         MYSQLI_NOT_NULL_FLAG        => 'not_null',
171         MYSQLI_PRI_KEY_FLAG         => 'primary_key',
172         MYSQLI_UNIQUE_KEY_FLAG      => 'unique_key',
173         MYSQLI_MULTIPLE_KEY_FLAG    => 'multiple_key',
174         MYSQLI_BLOB_FLAG            => 'blob',
175         MYSQLI_UNSIGNED_FLAG        => 'unsigned',
176         MYSQLI_ZEROFILL_FLAG        => 'zerofill',
177         MYSQLI_AUTO_INCREMENT_FLAG  => 'auto_increment',
178         MYSQLI_TIMESTAMP_FLAG       => 'timestamp',
179         MYSQLI_SET_FLAG             => 'set',
180         // MYSQLI_NUM_FLAG             => 'numeric',  // unnecessary
181         // MYSQLI_PART_KEY_FLAG        => 'multiple_key',  // duplicatvie
182         MYSQLI_GROUP_FLAG           => 'group_by'
183     );
184
185     /**
186      * Array for converting MYSQLI_TYPE_* constants to text values
187      * @var    array
188      * @access public
189      * @since  Property available since Release 1.6.5
190      */
191     var $mysqli_types = array(
192         MYSQLI_TYPE_DECIMAL     => 'decimal',
193         MYSQLI_TYPE_TINY        => 'tinyint',
194         MYSQLI_TYPE_SHORT       => 'int',
195         MYSQLI_TYPE_LONG        => 'int',
196         MYSQLI_TYPE_FLOAT       => 'float',
197         MYSQLI_TYPE_DOUBLE      => 'double',
198         // MYSQLI_TYPE_NULL        => 'DEFAULT NULL',  // let flags handle it
199         MYSQLI_TYPE_TIMESTAMP   => 'timestamp',
200         MYSQLI_TYPE_LONGLONG    => 'bigint',
201         MYSQLI_TYPE_INT24       => 'mediumint',
202         MYSQLI_TYPE_DATE        => 'date',
203         MYSQLI_TYPE_TIME        => 'time',
204         MYSQLI_TYPE_DATETIME    => 'datetime',
205         MYSQLI_TYPE_YEAR        => 'year',
206         MYSQLI_TYPE_NEWDATE     => 'date',
207         MYSQLI_TYPE_ENUM        => 'enum',
208         MYSQLI_TYPE_SET         => 'set',
209         MYSQLI_TYPE_TINY_BLOB   => 'tinyblob',
210         MYSQLI_TYPE_MEDIUM_BLOB => 'mediumblob',
211         MYSQLI_TYPE_LONG_BLOB   => 'longblob',
212         MYSQLI_TYPE_BLOB        => 'blob',
213         MYSQLI_TYPE_VAR_STRING  => 'varchar',
214         MYSQLI_TYPE_STRING      => 'char',
215         MYSQLI_TYPE_GEOMETRY    => 'geometry',
216         /* These constants are conditionally compiled in ext/mysqli, so we'll
217          * define them by number rather than constant. */
218         16                      => 'bit',
219         246                     => 'decimal',
220     );
221
222
223     // }}}
224     // {{{ constructor
225
226     /**
227      * This constructor calls <kbd>parent::__construct()</kbd>
228      *
229      * @return void
230      */
231     function __construct()
232     {
233         parent::__construct();
234     }
235
236     // }}}
237     // {{{ connect()
238
239     /**
240      * Connect to the database server, log in and open the database
241      *
242      * Don't call this method directly.  Use DB::connect() instead.
243      *
244      * PEAR DB's mysqli driver supports the following extra DSN options:
245      *   + When the 'ssl' $option passed to DB::connect() is true:
246      *     + key      The path to the key file.
247      *     + cert     The path to the certificate file.
248      *     + ca       The path to the certificate authority file.
249      *     + capath   The path to a directory that contains trusted SSL
250      *                 CA certificates in pem format.
251      *     + cipher   The list of allowable ciphers for SSL encryption.
252      *
253      * Example of how to connect using SSL:
254      * <code>
255      * require_once 'DB.php';
256      * 
257      * $dsn = array(
258      *     'phptype'  => 'mysqli',
259      *     'username' => 'someuser',
260      *     'password' => 'apasswd',
261      *     'hostspec' => 'localhost',
262      *     'database' => 'thedb',
263      *     'key'      => 'client-key.pem',
264      *     'cert'     => 'client-cert.pem',
265      *     'ca'       => 'cacert.pem',
266      *     'capath'   => '/path/to/ca/dir',
267      *     'cipher'   => 'AES',
268      * );
269      * 
270      * $options = array(
271      *     'ssl' => true,
272      * );
273      * 
274      * $db = DB::connect($dsn, $options);
275      * if (PEAR::isError($db)) {
276      *     die($db->getMessage());
277      * }
278      * </code>
279      *
280      * @param array $dsn         the data source name
281      * @param bool  $persistent  should the connection be persistent?
282      *
283      * @return int  DB_OK on success. A DB_Error object on failure.
284      */
285     function connect($dsn, $persistent = false)
286     {
287         if (!PEAR::loadExtension('mysqli')) {
288             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
289         }
290
291         $this->dsn = $dsn;
292         if ($dsn['dbsyntax']) {
293             $this->dbsyntax = $dsn['dbsyntax'];
294         }
295
296         $ini = ini_get('track_errors');
297         @ini_set('track_errors', 1);
298         $php_errormsg = '';
299
300         if (((int) $this->getOption('ssl')) === 1) {
301             $init = mysqli_init();
302             mysqli_ssl_set(
303                 $init,
304                 empty($dsn['key'])    ? null : $dsn['key'],
305                 empty($dsn['cert'])   ? null : $dsn['cert'],
306                 empty($dsn['ca'])     ? null : $dsn['ca'],
307                 empty($dsn['capath']) ? null : $dsn['capath'],
308                 empty($dsn['cipher']) ? null : $dsn['cipher']
309             );
310             if ($this->connection = @mysqli_real_connect(
311                     $init,
312                     $dsn['hostspec'],
313                     $dsn['username'],
314                     $dsn['password'],
315                     $dsn['database'],
316                     $dsn['port'],
317                     $dsn['socket']))
318             {
319                 $this->connection = $init;
320             }
321         } else {
322             $this->connection = @mysqli_connect(
323                 $dsn['hostspec'],
324                 $dsn['username'],
325                 $dsn['password'],
326                 $dsn['database'],
327                 $dsn['port'],
328                 $dsn['socket']
329             );
330         }
331
332         @ini_set('track_errors', $ini);
333
334         if (!$this->connection) {
335             if (($err = @mysqli_connect_error()) != '') {
336                 return $this->raiseError(DB_ERROR_CONNECT_FAILED,
337                                          null, null, null,
338                                          $err);
339             } else {
340                 return $this->raiseError(DB_ERROR_CONNECT_FAILED,
341                                          null, null, null,
342                                          $php_errormsg);
343             }
344         }
345
346         if ($dsn['database']) {
347             $this->_db = $dsn['database'];
348         }
349
350         return DB_OK;
351     }
352
353     // }}}
354     // {{{ disconnect()
355
356     /**
357      * Disconnects from the database server
358      *
359      * @return bool  TRUE on success, FALSE on failure
360      */
361     function disconnect()
362     {
363         $ret = @mysqli_close($this->connection);
364         $this->connection = null;
365         return $ret;
366     }
367
368     // }}}
369     // {{{ simpleQuery()
370
371     /**
372      * Sends a query to the database server
373      *
374      * @param string  the SQL query string
375      *
376      * @return mixed  + a PHP result resrouce for successful SELECT queries
377      *                + the DB_OK constant for other successful queries
378      *                + a DB_Error object on failure
379      */
380     function simpleQuery($query)
381     {
382         $ismanip = $this->_checkManip($query);
383         $this->last_query = $query;
384         $query = $this->modifyQuery($query);
385         if ($this->_db) {
386             if (!@mysqli_select_db($this->connection, $this->_db)) {
387                 return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
388             }
389         }
390         if (!$this->autocommit && $ismanip) {
391             if ($this->transaction_opcount == 0) {
392                 $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=0');
393                 $result = @mysqli_query($this->connection, 'BEGIN');
394                 if (!$result) {
395                     return $this->mysqliRaiseError();
396                 }
397             }
398             $this->transaction_opcount++;
399         }
400         $result = @mysqli_query($this->connection, $query);
401         if (!$result) {
402             return $this->mysqliRaiseError();
403         }
404         if (is_object($result)) {
405             return $result;
406         }
407         return DB_OK;
408     }
409
410     // }}}
411     // {{{ nextResult()
412
413     /**
414      * Move the internal mysql result pointer to the next available result.
415      *
416      * This method has not been implemented yet.
417      *
418      * @param resource $result a valid sql result resource
419      * @return false
420      * @access public
421      */
422     function nextResult($result)
423     {
424         return false;
425     }
426
427     // }}}
428     // {{{ fetchInto()
429
430     /**
431      * Places a row from the result set into the given array
432      *
433      * Formating of the array and the data therein are configurable.
434      * See DB_result::fetchInto() for more information.
435      *
436      * This method is not meant to be called directly.  Use
437      * DB_result::fetchInto() instead.  It can't be declared "protected"
438      * because DB_result is a separate object.
439      *
440      * @param resource $result    the query result resource
441      * @param array    $arr       the referenced array to put the data in
442      * @param int      $fetchmode how the resulting array should be indexed
443      * @param int      $rownum    the row number to fetch (0 = first row)
444      *
445      * @return mixed  DB_OK on success, NULL when the end of a result set is
446      *                 reached or on failure
447      *
448      * @see DB_result::fetchInto()
449      */
450     function fetchInto($result, &$arr, $fetchmode, $rownum = null)
451     {
452         if ($rownum !== null) {
453             if (!@mysqli_data_seek($result, $rownum)) {
454                 return null;
455             }
456         }
457         if ($fetchmode & DB_FETCHMODE_ASSOC) {
458             $arr = @mysqli_fetch_array($result, MYSQLI_ASSOC);
459             if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
460                 $arr = array_change_key_case($arr, CASE_LOWER);
461             }
462         } else {
463             $arr = @mysqli_fetch_row($result);
464         }
465         if (!$arr) {
466             return null;
467         }
468         if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
469             /*
470              * Even though this DBMS already trims output, we do this because
471              * a field might have intentional whitespace at the end that
472              * gets removed by DB_PORTABILITY_RTRIM under another driver.
473              */
474             $this->_rtrimArrayValues($arr);
475         }
476         if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
477             $this->_convertNullArrayValuesToEmpty($arr);
478         }
479         return DB_OK;
480     }
481
482     // }}}
483     // {{{ freeResult()
484
485     /**
486      * Deletes the result set and frees the memory occupied by the result set
487      *
488      * This method is not meant to be called directly.  Use
489      * DB_result::free() instead.  It can't be declared "protected"
490      * because DB_result is a separate object.
491      *
492      * @param resource $result  PHP's query result resource
493      *
494      * @return bool  TRUE on success, FALSE if $result is invalid
495      *
496      * @see DB_result::free()
497      */
498     function freeResult($result)
499     {
500         if (! $result instanceof mysqli_result) {
501             return false;
502         }
503         mysqli_free_result($result);
504         return true;
505     }
506
507     // }}}
508     // {{{ numCols()
509
510     /**
511      * Gets the number of columns in a result set
512      *
513      * This method is not meant to be called directly.  Use
514      * DB_result::numCols() instead.  It can't be declared "protected"
515      * because DB_result is a separate object.
516      *
517      * @param resource $result  PHP's query result resource
518      *
519      * @return int  the number of columns.  A DB_Error object on failure.
520      *
521      * @see DB_result::numCols()
522      */
523     function numCols($result)
524     {
525         $cols = @mysqli_num_fields($result);
526         if (!$cols) {
527             return $this->mysqliRaiseError();
528         }
529         return $cols;
530     }
531
532     // }}}
533     // {{{ numRows()
534
535     /**
536      * Gets the number of rows in a result set
537      *
538      * This method is not meant to be called directly.  Use
539      * DB_result::numRows() instead.  It can't be declared "protected"
540      * because DB_result is a separate object.
541      *
542      * @param resource $result  PHP's query result resource
543      *
544      * @return int  the number of rows.  A DB_Error object on failure.
545      *
546      * @see DB_result::numRows()
547      */
548     function numRows($result)
549     {
550         $rows = @mysqli_num_rows($result);
551         if ($rows === null) {
552             return $this->mysqliRaiseError();
553         }
554         return $rows;
555     }
556
557     // }}}
558     // {{{ autoCommit()
559
560     /**
561      * Enables or disables automatic commits
562      *
563      * @param bool $onoff  true turns it on, false turns it off
564      *
565      * @return int  DB_OK on success.  A DB_Error object if the driver
566      *               doesn't support auto-committing transactions.
567      */
568     function autoCommit($onoff = false)
569     {
570         // XXX if $this->transaction_opcount > 0, we should probably
571         // issue a warning here.
572         $this->autocommit = $onoff ? true : false;
573         return DB_OK;
574     }
575
576     // }}}
577     // {{{ commit()
578
579     /**
580      * Commits the current transaction
581      *
582      * @return int  DB_OK on success.  A DB_Error object on failure.
583      */
584     function commit()
585     {
586         if ($this->transaction_opcount > 0) {
587             if ($this->_db) {
588                 if (!@mysqli_select_db($this->connection, $this->_db)) {
589                     return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
590                 }
591             }
592             $result = @mysqli_query($this->connection, 'COMMIT');
593             $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
594             $this->transaction_opcount = 0;
595             if (!$result) {
596                 return $this->mysqliRaiseError();
597             }
598         }
599         return DB_OK;
600     }
601
602     // }}}
603     // {{{ rollback()
604
605     /**
606      * Reverts the current transaction
607      *
608      * @return int  DB_OK on success.  A DB_Error object on failure.
609      */
610     function rollback()
611     {
612         if ($this->transaction_opcount > 0) {
613             if ($this->_db) {
614                 if (!@mysqli_select_db($this->connection, $this->_db)) {
615                     return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
616                 }
617             }
618             $result = @mysqli_query($this->connection, 'ROLLBACK');
619             $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
620             $this->transaction_opcount = 0;
621             if (!$result) {
622                 return $this->mysqliRaiseError();
623             }
624         }
625         return DB_OK;
626     }
627
628     // }}}
629     // {{{ affectedRows()
630
631     /**
632      * Determines the number of rows affected by a data maniuplation query
633      *
634      * 0 is returned for queries that don't manipulate data.
635      *
636      * @return int  the number of rows.  A DB_Error object on failure.
637      */
638     function affectedRows()
639     {
640         if ($this->_last_query_manip) {
641             return @mysqli_affected_rows($this->connection);
642         } else {
643             return 0;
644         }
645      }
646
647     // }}}
648     // {{{ nextId()
649
650     /**
651      * Returns the next free id in a sequence
652      *
653      * @param string  $seq_name  name of the sequence
654      * @param boolean $ondemand  when true, the seqence is automatically
655      *                            created if it does not exist
656      *
657      * @return int  the next id number in the sequence.
658      *               A DB_Error object on failure.
659      *
660      * @see DB_common::nextID(), DB_common::getSequenceName(),
661      *      DB_mysqli::createSequence(), DB_mysqli::dropSequence()
662      */
663     function nextId($seq_name, $ondemand = true)
664     {
665         $seqname = $this->getSequenceName($seq_name);
666         do {
667             $repeat = 0;
668             $this->pushErrorHandling(PEAR_ERROR_RETURN);
669             $result = $this->query('UPDATE ' . $seqname
670                                    . ' SET id = LAST_INSERT_ID(id + 1)');
671             $this->popErrorHandling();
672             if ($result === DB_OK) {
673                 // COMMON CASE
674                 $id = @mysqli_insert_id($this->connection);
675                 if ($id != 0) {
676                     return $id;
677                 }
678
679                 // EMPTY SEQ TABLE
680                 // Sequence table must be empty for some reason,
681                 // so fill it and return 1
682                 // Obtain a user-level lock
683                 $result = $this->getOne('SELECT GET_LOCK('
684                                         . "'${seqname}_lock', 10)");
685                 if (DB::isError($result)) {
686                     return $this->raiseError($result);
687                 }
688                 if ($result == 0) {
689                     return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
690                 }
691
692                 // add the default value
693                 $result = $this->query('REPLACE INTO ' . $seqname
694                                        . ' (id) VALUES (0)');
695                 if (DB::isError($result)) {
696                     return $this->raiseError($result);
697                 }
698
699                 // Release the lock
700                 $result = $this->getOne('SELECT RELEASE_LOCK('
701                                         . "'${seqname}_lock')");
702                 if (DB::isError($result)) {
703                     return $this->raiseError($result);
704                 }
705                 // We know what the result will be, so no need to try again
706                 return 1;
707
708             } elseif ($ondemand && DB::isError($result) &&
709                 $result->getCode() == DB_ERROR_NOSUCHTABLE)
710             {
711                 // ONDEMAND TABLE CREATION
712                 $result = $this->createSequence($seq_name);
713
714                 // Since createSequence initializes the ID to be 1,
715                 // we do not need to retrieve the ID again (or we will get 2)
716                 if (DB::isError($result)) {
717                     return $this->raiseError($result);
718                 } else {
719                     // First ID of a newly created sequence is 1
720                     return 1;
721                 }
722
723             } elseif (DB::isError($result) &&
724                       $result->getCode() == DB_ERROR_ALREADY_EXISTS)
725             {
726                 // BACKWARDS COMPAT
727                 // see _BCsequence() comment
728                 $result = $this->_BCsequence($seqname);
729                 if (DB::isError($result)) {
730                     return $this->raiseError($result);
731                 }
732                 $repeat = 1;
733             }
734         } while ($repeat);
735
736         return $this->raiseError($result);
737     }
738
739     /**
740      * Creates a new sequence
741      *
742      * @param string $seq_name  name of the new sequence
743      *
744      * @return int  DB_OK on success.  A DB_Error object on failure.
745      *
746      * @see DB_common::createSequence(), DB_common::getSequenceName(),
747      *      DB_mysqli::nextID(), DB_mysqli::dropSequence()
748      */
749     function createSequence($seq_name)
750     {
751         $seqname = $this->getSequenceName($seq_name);
752         $res = $this->query('CREATE TABLE ' . $seqname
753                             . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
754                             . ' PRIMARY KEY(id))');
755         if (DB::isError($res)) {
756             return $res;
757         }
758         // insert yields value 1, nextId call will generate ID 2
759         return $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
760     }
761
762     // }}}
763     // {{{ dropSequence()
764
765     /**
766      * Deletes a sequence
767      *
768      * @param string $seq_name  name of the sequence to be deleted
769      *
770      * @return int  DB_OK on success.  A DB_Error object on failure.
771      *
772      * @see DB_common::dropSequence(), DB_common::getSequenceName(),
773      *      DB_mysql::nextID(), DB_mysql::createSequence()
774      */
775     function dropSequence($seq_name)
776     {
777         return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
778     }
779
780     // }}}
781     // {{{ _BCsequence()
782
783     /**
784      * Backwards compatibility with old sequence emulation implementation
785      * (clean up the dupes)
786      *
787      * @param string $seqname  the sequence name to clean up
788      *
789      * @return bool  true on success.  A DB_Error object on failure.
790      *
791      * @access private
792      */
793     function _BCsequence($seqname)
794     {
795         // Obtain a user-level lock... this will release any previous
796         // application locks, but unlike LOCK TABLES, it does not abort
797         // the current transaction and is much less frequently used.
798         $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
799         if (DB::isError($result)) {
800             return $result;
801         }
802         if ($result == 0) {
803             // Failed to get the lock, can't do the conversion, bail
804             // with a DB_ERROR_NOT_LOCKED error
805             return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
806         }
807
808         $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
809         if (DB::isError($highest_id)) {
810             return $highest_id;
811         }
812
813         // This should kill all rows except the highest
814         // We should probably do something if $highest_id isn't
815         // numeric, but I'm at a loss as how to handle that...
816         $result = $this->query('DELETE FROM ' . $seqname
817                                . " WHERE id <> $highest_id");
818         if (DB::isError($result)) {
819             return $result;
820         }
821
822         // If another thread has been waiting for this lock,
823         // it will go thru the above procedure, but will have no
824         // real effect
825         $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
826         if (DB::isError($result)) {
827             return $result;
828         }
829         return true;
830     }
831
832     // }}}
833     // {{{ quoteIdentifier()
834
835     /**
836      * Quotes a string so it can be safely used as a table or column name
837      * (WARNING: using names that require this is a REALLY BAD IDEA)
838      *
839      * WARNING:  Older versions of MySQL can't handle the backtick
840      * character (<kbd>`</kbd>) in table or column names.
841      *
842      * @param string $str  identifier name to be quoted
843      *
844      * @return string  quoted identifier string
845      *
846      * @see DB_common::quoteIdentifier()
847      * @since Method available since Release 1.6.0
848      */
849     function quoteIdentifier($str)
850     {
851         return '`' . str_replace('`', '``', $str) . '`';
852     }
853
854     // }}}
855     // {{{ escapeSimple()
856
857     /**
858      * Escapes a string according to the current DBMS's standards
859      *
860      * @param string $str  the string to be escaped
861      *
862      * @return string  the escaped string
863      *
864      * @see DB_common::quoteSmart()
865      * @since Method available since Release 1.6.0
866      */
867     function escapeSimple($str)
868     {
869         return @mysqli_real_escape_string($this->connection, $str);
870     }
871
872     // }}}
873     // {{{ modifyLimitQuery()
874
875     /**
876      * Adds LIMIT clauses to a query string according to current DBMS standards
877      *
878      * @param string $query   the query to modify
879      * @param int    $from    the row to start to fetching (0 = the first row)
880      * @param int    $count   the numbers of rows to fetch
881      * @param mixed  $params  array, string or numeric data to be used in
882      *                         execution of the statement.  Quantity of items
883      *                         passed must match quantity of placeholders in
884      *                         query:  meaning 1 placeholder for non-array
885      *                         parameters or 1 placeholder per array element.
886      *
887      * @return string  the query string with LIMIT clauses added
888      *
889      * @access protected
890      */
891     function modifyLimitQuery($query, $from, $count, $params = array())
892     {
893         if (DB::isManip($query) || $this->_next_query_manip) {
894             return $query . " LIMIT $count";
895         } else {
896             return $query . " LIMIT $from, $count";
897         }
898     }
899
900     // }}}
901     // {{{ mysqliRaiseError()
902
903     /**
904      * Produces a DB_Error object regarding the current problem
905      *
906      * @param int $errno  if the error is being manually raised pass a
907      *                     DB_ERROR* constant here.  If this isn't passed
908      *                     the error information gathered from the DBMS.
909      *
910      * @return object  the DB_Error object
911      *
912      * @see DB_common::raiseError(),
913      *      DB_mysqli::errorNative(), DB_common::errorCode()
914      */
915     function mysqliRaiseError($errno = null)
916     {
917         if ($errno === null) {
918             if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
919                 $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
920                 $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
921                 $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
922             } else {
923                 // Doing this in case mode changes during runtime.
924                 $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
925                 $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
926                 $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
927             }
928             $errno = $this->errorCode(mysqli_errno($this->connection));
929         }
930         return $this->raiseError($errno, null, null, null,
931                                  @mysqli_errno($this->connection) . ' ** ' .
932                                  @mysqli_error($this->connection));
933     }
934
935     // }}}
936     // {{{ errorNative()
937
938     /**
939      * Gets the DBMS' native error code produced by the last query
940      *
941      * @return int  the DBMS' error code
942      */
943     function errorNative()
944     {
945         return @mysqli_errno($this->connection);
946     }
947
948     // }}}
949     // {{{ tableInfo()
950
951     /**
952      * Returns information about a table or a result set
953      *
954      * @param object|string  $result  DB_result object from a query or a
955      *                                 string containing the name of a table.
956      *                                 While this also accepts a query result
957      *                                 resource identifier, this behavior is
958      *                                 deprecated.
959      * @param int            $mode    a valid tableInfo mode
960      *
961      * @return array  an associative array with the information requested.
962      *                 A DB_Error object on failure.
963      *
964      * @see DB_common::setOption()
965      */
966     function tableInfo($result, $mode = null)
967     {
968         if (is_string($result)) {
969             // Fix for bug #11580.
970             if ($this->_db) {
971                 if (!@mysqli_select_db($this->connection, $this->_db)) {
972                     return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
973                 }
974             }
975
976             /*
977              * Probably received a table name.
978              * Create a result resource identifier.
979              */
980             $id = @mysqli_query($this->connection,
981                                 "SELECT * FROM $result LIMIT 0");
982             $got_string = true;
983         } elseif (isset($result->result)) {
984             /*
985              * Probably received a result object.
986              * Extract the result resource identifier.
987              */
988             $id = $result->result;
989             $got_string = false;
990         } else {
991             /*
992              * Probably received a result resource identifier.
993              * Copy it.
994              * Deprecated.  Here for compatibility only.
995              */
996             $id = $result;
997             $got_string = false;
998         }
999
1000         if (!is_object($id) || !is_a($id, 'mysqli_result')) {
1001             return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA);
1002         }
1003
1004         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
1005             $case_func = 'strtolower';
1006         } else {
1007             $case_func = 'strval';
1008         }
1009
1010         $count = @mysqli_num_fields($id);
1011         $res   = array();
1012
1013         if ($mode) {
1014             $res['num_fields'] = $count;
1015         }
1016
1017         for ($i = 0; $i < $count; $i++) {
1018             $tmp = @mysqli_fetch_field($id);
1019
1020             $flags = '';
1021             foreach ($this->mysqli_flags as $const => $means) {
1022                 if ($tmp->flags & $const) {
1023                     $flags .= $means . ' ';
1024                 }
1025             }
1026             if ($tmp->def) {
1027                 $flags .= 'default_' . rawurlencode($tmp->def);
1028             }
1029             $flags = trim($flags);
1030
1031             $res[$i] = array(
1032                 'table' => $case_func($tmp->table),
1033                 'name'  => $case_func($tmp->name),
1034                 'type'  => isset($this->mysqli_types[$tmp->type])
1035                                     ? $this->mysqli_types[$tmp->type]
1036                                     : 'unknown',
1037                 // http://bugs.php.net/?id=36579
1038                 //  Doc Bug #36579: mysqli_fetch_field length handling
1039                 // https://bugs.php.net/bug.php?id=62426
1040                 //  Bug #62426: mysqli_fetch_field_direct returns incorrect
1041                 //  length on UTF8 fields
1042                 'len'   => $tmp->length,
1043                 'flags' => $flags,
1044             );
1045
1046             if ($mode & DB_TABLEINFO_ORDER) {
1047                 $res['order'][$res[$i]['name']] = $i;
1048             }
1049             if ($mode & DB_TABLEINFO_ORDERTABLE) {
1050                 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
1051             }
1052         }
1053
1054         // free the result only if we were called on a table
1055         if ($got_string) {
1056             @mysqli_free_result($id);
1057         }
1058         return $res;
1059     }
1060
1061     // }}}
1062     // {{{ getSpecialQuery()
1063
1064     /**
1065      * Obtains the query string needed for listing a given type of objects
1066      *
1067      * @param string $type  the kind of objects you want to retrieve
1068      *
1069      * @return string  the SQL query string or null if the driver doesn't
1070      *                  support the object type requested
1071      *
1072      * @access protected
1073      * @see DB_common::getListOf()
1074      */
1075     function getSpecialQuery($type)
1076     {
1077         switch ($type) {
1078             case 'tables':
1079                 return 'SHOW TABLES';
1080             case 'users':
1081                 return 'SELECT DISTINCT User FROM mysql.user';
1082             case 'databases':
1083                 return 'SHOW DATABASES';
1084             default:
1085                 return null;
1086         }
1087     }
1088
1089     // }}}
1090
1091 }
1092
1093 /*
1094  * Local variables:
1095  * tab-width: 4
1096  * c-basic-offset: 4
1097  * End:
1098  */
1099
1100 ?>