Subversion Repositories Applications.gtt

Rev

Rev 94 | Details | Compare with Previous | Last modification | View Log | RSS feed

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