source: branches/comu-ver2/data/module/DB/mysqli.php @ 17877

Revision 17877, 33.8 KB checked in by Seasoft, 15 years ago (diff)

・PEAR::DB を Ver.1.7.14RC1(2007-11-27)ベースに更改。
・不適切な文字列比較を修正。

 http://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=1808&forum=9

  • Property svn:keywords set to Id
  • Property svn:mime-type set to text/x-httpd-php; charset=UTF-8
Line 
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 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-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 */
29require_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.7.14RC1
47 * @link       http://pear.php.net/package/DB
48 * @since      Class functional since Release 1.6.3
49 */
50class 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_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?>
Note: See TracBrowser for help on using the repository browser.