Subversion Repositories Applications.papyrus

Rev

Rev 320 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
320 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
 *
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
443 ddelon 22
 * @version    CVS: $Id: mysqli.php,v 1.2 2005-09-20 17:01:22 ddelon Exp $
320 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>
44
 * @copyright  1997-2005 The PHP Group
45
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
443 ddelon 46
 * @version    Release: @package_version@
320 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,
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
?>