Subversion Repositories Sites.obs-saisons.fr

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

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