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