]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/DB/mysqli.php
DB updated to 1.8.2
[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.8.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>$this->DB_common()</kbd>
228      *
229      * @return void
230      */
231     function DB_mysqli()
232     {
233         $this->DB_common();
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         return is_resource($result) ? mysqli_free_result($result) : false;
501     }
502
503     // }}}
504     // {{{ numCols()
505
506     /**
507      * Gets the number of columns in a result set
508      *
509      * This method is not meant to be called directly.  Use
510      * DB_result::numCols() instead.  It can't be declared "protected"
511      * because DB_result is a separate object.
512      *
513      * @param resource $result  PHP's query result resource
514      *
515      * @return int  the number of columns.  A DB_Error object on failure.
516      *
517      * @see DB_result::numCols()
518      */
519     function numCols($result)
520     {
521         $cols = @mysqli_num_fields($result);
522         if (!$cols) {
523             return $this->mysqliRaiseError();
524         }
525         return $cols;
526     }
527
528     // }}}
529     // {{{ numRows()
530
531     /**
532      * Gets the number of rows in a result set
533      *
534      * This method is not meant to be called directly.  Use
535      * DB_result::numRows() instead.  It can't be declared "protected"
536      * because DB_result is a separate object.
537      *
538      * @param resource $result  PHP's query result resource
539      *
540      * @return int  the number of rows.  A DB_Error object on failure.
541      *
542      * @see DB_result::numRows()
543      */
544     function numRows($result)
545     {
546         $rows = @mysqli_num_rows($result);
547         if ($rows === null) {
548             return $this->mysqliRaiseError();
549         }
550         return $rows;
551     }
552
553     // }}}
554     // {{{ autoCommit()
555
556     /**
557      * Enables or disables automatic commits
558      *
559      * @param bool $onoff  true turns it on, false turns it off
560      *
561      * @return int  DB_OK on success.  A DB_Error object if the driver
562      *               doesn't support auto-committing transactions.
563      */
564     function autoCommit($onoff = false)
565     {
566         // XXX if $this->transaction_opcount > 0, we should probably
567         // issue a warning here.
568         $this->autocommit = $onoff ? true : false;
569         return DB_OK;
570     }
571
572     // }}}
573     // {{{ commit()
574
575     /**
576      * Commits the current transaction
577      *
578      * @return int  DB_OK on success.  A DB_Error object on failure.
579      */
580     function commit()
581     {
582         if ($this->transaction_opcount > 0) {
583             if ($this->_db) {
584                 if (!@mysqli_select_db($this->connection, $this->_db)) {
585                     return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
586                 }
587             }
588             $result = @mysqli_query($this->connection, 'COMMIT');
589             $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
590             $this->transaction_opcount = 0;
591             if (!$result) {
592                 return $this->mysqliRaiseError();
593             }
594         }
595         return DB_OK;
596     }
597
598     // }}}
599     // {{{ rollback()
600
601     /**
602      * Reverts the current transaction
603      *
604      * @return int  DB_OK on success.  A DB_Error object on failure.
605      */
606     function rollback()
607     {
608         if ($this->transaction_opcount > 0) {
609             if ($this->_db) {
610                 if (!@mysqli_select_db($this->connection, $this->_db)) {
611                     return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
612                 }
613             }
614             $result = @mysqli_query($this->connection, 'ROLLBACK');
615             $result = @mysqli_query($this->connection, 'SET AUTOCOMMIT=1');
616             $this->transaction_opcount = 0;
617             if (!$result) {
618                 return $this->mysqliRaiseError();
619             }
620         }
621         return DB_OK;
622     }
623
624     // }}}
625     // {{{ affectedRows()
626
627     /**
628      * Determines the number of rows affected by a data maniuplation query
629      *
630      * 0 is returned for queries that don't manipulate data.
631      *
632      * @return int  the number of rows.  A DB_Error object on failure.
633      */
634     function affectedRows()
635     {
636         if ($this->_last_query_manip) {
637             return @mysqli_affected_rows($this->connection);
638         } else {
639             return 0;
640         }
641      }
642
643     // }}}
644     // {{{ nextId()
645
646     /**
647      * Returns the next free id in a sequence
648      *
649      * @param string  $seq_name  name of the sequence
650      * @param boolean $ondemand  when true, the seqence is automatically
651      *                            created if it does not exist
652      *
653      * @return int  the next id number in the sequence.
654      *               A DB_Error object on failure.
655      *
656      * @see DB_common::nextID(), DB_common::getSequenceName(),
657      *      DB_mysqli::createSequence(), DB_mysqli::dropSequence()
658      */
659     function nextId($seq_name, $ondemand = true)
660     {
661         $seqname = $this->getSequenceName($seq_name);
662         do {
663             $repeat = 0;
664             $this->pushErrorHandling(PEAR_ERROR_RETURN);
665             $result = $this->query('UPDATE ' . $seqname
666                                    . ' SET id = LAST_INSERT_ID(id + 1)');
667             $this->popErrorHandling();
668             if ($result === DB_OK) {
669                 // COMMON CASE
670                 $id = @mysqli_insert_id($this->connection);
671                 if ($id != 0) {
672                     return $id;
673                 }
674
675                 // EMPTY SEQ TABLE
676                 // Sequence table must be empty for some reason,
677                 // so fill it and return 1
678                 // Obtain a user-level lock
679                 $result = $this->getOne('SELECT GET_LOCK('
680                                         . "'${seqname}_lock', 10)");
681                 if (DB::isError($result)) {
682                     return $this->raiseError($result);
683                 }
684                 if ($result == 0) {
685                     return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
686                 }
687
688                 // add the default value
689                 $result = $this->query('REPLACE INTO ' . $seqname
690                                        . ' (id) VALUES (0)');
691                 if (DB::isError($result)) {
692                     return $this->raiseError($result);
693                 }
694
695                 // Release the lock
696                 $result = $this->getOne('SELECT RELEASE_LOCK('
697                                         . "'${seqname}_lock')");
698                 if (DB::isError($result)) {
699                     return $this->raiseError($result);
700                 }
701                 // We know what the result will be, so no need to try again
702                 return 1;
703
704             } elseif ($ondemand && DB::isError($result) &&
705                 $result->getCode() == DB_ERROR_NOSUCHTABLE)
706             {
707                 // ONDEMAND TABLE CREATION
708                 $result = $this->createSequence($seq_name);
709
710                 // Since createSequence initializes the ID to be 1,
711                 // we do not need to retrieve the ID again (or we will get 2)
712                 if (DB::isError($result)) {
713                     return $this->raiseError($result);
714                 } else {
715                     // First ID of a newly created sequence is 1
716                     return 1;
717                 }
718
719             } elseif (DB::isError($result) &&
720                       $result->getCode() == DB_ERROR_ALREADY_EXISTS)
721             {
722                 // BACKWARDS COMPAT
723                 // see _BCsequence() comment
724                 $result = $this->_BCsequence($seqname);
725                 if (DB::isError($result)) {
726                     return $this->raiseError($result);
727                 }
728                 $repeat = 1;
729             }
730         } while ($repeat);
731
732         return $this->raiseError($result);
733     }
734
735     /**
736      * Creates a new sequence
737      *
738      * @param string $seq_name  name of the new sequence
739      *
740      * @return int  DB_OK on success.  A DB_Error object on failure.
741      *
742      * @see DB_common::createSequence(), DB_common::getSequenceName(),
743      *      DB_mysqli::nextID(), DB_mysqli::dropSequence()
744      */
745     function createSequence($seq_name)
746     {
747         $seqname = $this->getSequenceName($seq_name);
748         $res = $this->query('CREATE TABLE ' . $seqname
749                             . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
750                             . ' PRIMARY KEY(id))');
751         if (DB::isError($res)) {
752             return $res;
753         }
754         // insert yields value 1, nextId call will generate ID 2
755         return $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
756     }
757
758     // }}}
759     // {{{ dropSequence()
760
761     /**
762      * Deletes a sequence
763      *
764      * @param string $seq_name  name of the sequence to be deleted
765      *
766      * @return int  DB_OK on success.  A DB_Error object on failure.
767      *
768      * @see DB_common::dropSequence(), DB_common::getSequenceName(),
769      *      DB_mysql::nextID(), DB_mysql::createSequence()
770      */
771     function dropSequence($seq_name)
772     {
773         return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
774     }
775
776     // }}}
777     // {{{ _BCsequence()
778
779     /**
780      * Backwards compatibility with old sequence emulation implementation
781      * (clean up the dupes)
782      *
783      * @param string $seqname  the sequence name to clean up
784      *
785      * @return bool  true on success.  A DB_Error object on failure.
786      *
787      * @access private
788      */
789     function _BCsequence($seqname)
790     {
791         // Obtain a user-level lock... this will release any previous
792         // application locks, but unlike LOCK TABLES, it does not abort
793         // the current transaction and is much less frequently used.
794         $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
795         if (DB::isError($result)) {
796             return $result;
797         }
798         if ($result == 0) {
799             // Failed to get the lock, can't do the conversion, bail
800             // with a DB_ERROR_NOT_LOCKED error
801             return $this->mysqliRaiseError(DB_ERROR_NOT_LOCKED);
802         }
803
804         $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
805         if (DB::isError($highest_id)) {
806             return $highest_id;
807         }
808
809         // This should kill all rows except the highest
810         // We should probably do something if $highest_id isn't
811         // numeric, but I'm at a loss as how to handle that...
812         $result = $this->query('DELETE FROM ' . $seqname
813                                . " WHERE id <> $highest_id");
814         if (DB::isError($result)) {
815             return $result;
816         }
817
818         // If another thread has been waiting for this lock,
819         // it will go thru the above procedure, but will have no
820         // real effect
821         $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
822         if (DB::isError($result)) {
823             return $result;
824         }
825         return true;
826     }
827
828     // }}}
829     // {{{ quoteIdentifier()
830
831     /**
832      * Quotes a string so it can be safely used as a table or column name
833      * (WARNING: using names that require this is a REALLY BAD IDEA)
834      *
835      * WARNING:  Older versions of MySQL can't handle the backtick
836      * character (<kbd>`</kbd>) in table or column names.
837      *
838      * @param string $str  identifier name to be quoted
839      *
840      * @return string  quoted identifier string
841      *
842      * @see DB_common::quoteIdentifier()
843      * @since Method available since Release 1.6.0
844      */
845     function quoteIdentifier($str)
846     {
847         return '`' . str_replace('`', '``', $str) . '`';
848     }
849
850     // }}}
851     // {{{ escapeSimple()
852
853     /**
854      * Escapes a string according to the current DBMS's standards
855      *
856      * @param string $str  the string to be escaped
857      *
858      * @return string  the escaped string
859      *
860      * @see DB_common::quoteSmart()
861      * @since Method available since Release 1.6.0
862      */
863     function escapeSimple($str)
864     {
865         return @mysqli_real_escape_string($this->connection, $str);
866     }
867
868     // }}}
869     // {{{ modifyLimitQuery()
870
871     /**
872      * Adds LIMIT clauses to a query string according to current DBMS standards
873      *
874      * @param string $query   the query to modify
875      * @param int    $from    the row to start to fetching (0 = the first row)
876      * @param int    $count   the numbers of rows to fetch
877      * @param mixed  $params  array, string or numeric data to be used in
878      *                         execution of the statement.  Quantity of items
879      *                         passed must match quantity of placeholders in
880      *                         query:  meaning 1 placeholder for non-array
881      *                         parameters or 1 placeholder per array element.
882      *
883      * @return string  the query string with LIMIT clauses added
884      *
885      * @access protected
886      */
887     function modifyLimitQuery($query, $from, $count, $params = array())
888     {
889         if (DB::isManip($query) || $this->_next_query_manip) {
890             return $query . " LIMIT $count";
891         } else {
892             return $query . " LIMIT $from, $count";
893         }
894     }
895
896     // }}}
897     // {{{ mysqliRaiseError()
898
899     /**
900      * Produces a DB_Error object regarding the current problem
901      *
902      * @param int $errno  if the error is being manually raised pass a
903      *                     DB_ERROR* constant here.  If this isn't passed
904      *                     the error information gathered from the DBMS.
905      *
906      * @return object  the DB_Error object
907      *
908      * @see DB_common::raiseError(),
909      *      DB_mysqli::errorNative(), DB_common::errorCode()
910      */
911     function mysqliRaiseError($errno = null)
912     {
913         if ($errno === null) {
914             if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
915                 $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
916                 $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
917                 $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
918             } else {
919                 // Doing this in case mode changes during runtime.
920                 $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
921                 $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
922                 $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
923             }
924             $errno = $this->errorCode(mysqli_errno($this->connection));
925         }
926         return $this->raiseError($errno, null, null, null,
927                                  @mysqli_errno($this->connection) . ' ** ' .
928                                  @mysqli_error($this->connection));
929     }
930
931     // }}}
932     // {{{ errorNative()
933
934     /**
935      * Gets the DBMS' native error code produced by the last query
936      *
937      * @return int  the DBMS' error code
938      */
939     function errorNative()
940     {
941         return @mysqli_errno($this->connection);
942     }
943
944     // }}}
945     // {{{ tableInfo()
946
947     /**
948      * Returns information about a table or a result set
949      *
950      * @param object|string  $result  DB_result object from a query or a
951      *                                 string containing the name of a table.
952      *                                 While this also accepts a query result
953      *                                 resource identifier, this behavior is
954      *                                 deprecated.
955      * @param int            $mode    a valid tableInfo mode
956      *
957      * @return array  an associative array with the information requested.
958      *                 A DB_Error object on failure.
959      *
960      * @see DB_common::setOption()
961      */
962     function tableInfo($result, $mode = null)
963     {
964         if (is_string($result)) {
965             // Fix for bug #11580.
966             if ($this->_db) {
967                 if (!@mysqli_select_db($this->connection, $this->_db)) {
968                     return $this->mysqliRaiseError(DB_ERROR_NODBSELECTED);
969                 }
970             }
971
972             /*
973              * Probably received a table name.
974              * Create a result resource identifier.
975              */
976             $id = @mysqli_query($this->connection,
977                                 "SELECT * FROM $result LIMIT 0");
978             $got_string = true;
979         } elseif (isset($result->result)) {
980             /*
981              * Probably received a result object.
982              * Extract the result resource identifier.
983              */
984             $id = $result->result;
985             $got_string = false;
986         } else {
987             /*
988              * Probably received a result resource identifier.
989              * Copy it.
990              * Deprecated.  Here for compatibility only.
991              */
992             $id = $result;
993             $got_string = false;
994         }
995
996         if (!is_object($id) || !is_a($id, 'mysqli_result')) {
997             return $this->mysqliRaiseError(DB_ERROR_NEED_MORE_DATA);
998         }
999
1000         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
1001             $case_func = 'strtolower';
1002         } else {
1003             $case_func = 'strval';
1004         }
1005
1006         $count = @mysqli_num_fields($id);
1007         $res   = array();
1008
1009         if ($mode) {
1010             $res['num_fields'] = $count;
1011         }
1012
1013         for ($i = 0; $i < $count; $i++) {
1014             $tmp = @mysqli_fetch_field($id);
1015
1016             $flags = '';
1017             foreach ($this->mysqli_flags as $const => $means) {
1018                 if ($tmp->flags & $const) {
1019                     $flags .= $means . ' ';
1020                 }
1021             }
1022             if ($tmp->def) {
1023                 $flags .= 'default_' . rawurlencode($tmp->def);
1024             }
1025             $flags = trim($flags);
1026
1027             $res[$i] = array(
1028                 'table' => $case_func($tmp->table),
1029                 'name'  => $case_func($tmp->name),
1030                 'type'  => isset($this->mysqli_types[$tmp->type])
1031                                     ? $this->mysqli_types[$tmp->type]
1032                                     : 'unknown',
1033                 // http://bugs.php.net/?id=36579
1034                 'len'   => $tmp->length,
1035                 'flags' => $flags,
1036             );
1037
1038             if ($mode & DB_TABLEINFO_ORDER) {
1039                 $res['order'][$res[$i]['name']] = $i;
1040             }
1041             if ($mode & DB_TABLEINFO_ORDERTABLE) {
1042                 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
1043             }
1044         }
1045
1046         // free the result only if we were called on a table
1047         if ($got_string) {
1048             @mysqli_free_result($id);
1049         }
1050         return $res;
1051     }
1052
1053     // }}}
1054     // {{{ getSpecialQuery()
1055
1056     /**
1057      * Obtains the query string needed for listing a given type of objects
1058      *
1059      * @param string $type  the kind of objects you want to retrieve
1060      *
1061      * @return string  the SQL query string or null if the driver doesn't
1062      *                  support the object type requested
1063      *
1064      * @access protected
1065      * @see DB_common::getListOf()
1066      */
1067     function getSpecialQuery($type)
1068     {
1069         switch ($type) {
1070             case 'tables':
1071                 return 'SHOW TABLES';
1072             case 'users':
1073                 return 'SELECT DISTINCT User FROM mysql.user';
1074             case 'databases':
1075                 return 'SHOW DATABASES';
1076             default:
1077                 return null;
1078         }
1079     }
1080
1081     // }}}
1082
1083 }
1084
1085 /*
1086  * Local variables:
1087  * tab-width: 4
1088  * c-basic-offset: 4
1089  * End:
1090  */
1091
1092 ?>