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 mysql 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     Stig Bakken <ssb@php.net>
20
 * @author     Daniel Convissor <danielc@php.net>
187 mathias 21
 * @copyright  1997-2007 The PHP Group
94 jpm 22
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
187 mathias 23
 * @version    CVS: $Id$
94 jpm 24
 * @link       http://pear.php.net/package/DB
25
 */
26
 
27
/**
28
 * Obtain the DB_common class so it can be extended from
29
 */
30
require_once 'DB/common.php';
31
 
32
/**
33
 * The methods PEAR DB uses to interact with PHP's mysql extension
34
 * for interacting with MySQL databases
35
 *
36
 * These methods overload the ones declared in DB_common.
37
 *
38
 * @category   Database
39
 * @package    DB
40
 * @author     Stig Bakken <ssb@php.net>
41
 * @author     Daniel Convissor <danielc@php.net>
187 mathias 42
 * @copyright  1997-2007 The PHP Group
94 jpm 43
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
187 mathias 44
 * @version    Release: 1.9.2
94 jpm 45
 * @link       http://pear.php.net/package/DB
46
 */
47
class DB_mysql extends DB_common
48
{
49
    // {{{ properties
50
 
51
    /**
52
     * The DB driver type (mysql, oci8, odbc, etc.)
53
     * @var string
54
     */
55
    var $phptype = 'mysql';
56
 
57
    /**
58
     * The database syntax variant to be used (db2, access, etc.), if any
59
     * @var string
60
     */
61
    var $dbsyntax = 'mysql';
62
 
63
    /**
64
     * The capabilities of this DB implementation
65
     *
66
     * The 'new_link' element contains the PHP version that first provided
67
     * new_link support for this DBMS.  Contains false if it's unsupported.
68
     *
69
     * Meaning of the 'limit' element:
70
     *   + 'emulate' = emulate with fetch row by number
71
     *   + 'alter'   = alter the query
72
     *   + false     = skip rows
73
     *
74
     * @var array
75
     */
76
    var $features = array(
77
        'limit'         => 'alter',
78
        'new_link'      => '4.2.0',
79
        'numrows'       => true,
80
        'pconnect'      => true,
81
        'prepare'       => false,
82
        'ssl'           => false,
83
        'transactions'  => true,
84
    );
85
 
86
    /**
87
     * A mapping of native error codes to DB error codes
88
     * @var array
89
     */
90
    var $errorcode_map = array(
91
        1004 => DB_ERROR_CANNOT_CREATE,
92
        1005 => DB_ERROR_CANNOT_CREATE,
93
        1006 => DB_ERROR_CANNOT_CREATE,
94
        1007 => DB_ERROR_ALREADY_EXISTS,
95
        1008 => DB_ERROR_CANNOT_DROP,
96
        1022 => DB_ERROR_ALREADY_EXISTS,
97
        1044 => DB_ERROR_ACCESS_VIOLATION,
98
        1046 => DB_ERROR_NODBSELECTED,
99
        1048 => DB_ERROR_CONSTRAINT,
100
        1049 => DB_ERROR_NOSUCHDB,
101
        1050 => DB_ERROR_ALREADY_EXISTS,
102
        1051 => DB_ERROR_NOSUCHTABLE,
103
        1054 => DB_ERROR_NOSUCHFIELD,
104
        1061 => DB_ERROR_ALREADY_EXISTS,
105
        1062 => DB_ERROR_ALREADY_EXISTS,
106
        1064 => DB_ERROR_SYNTAX,
107
        1091 => DB_ERROR_NOT_FOUND,
108
        1100 => DB_ERROR_NOT_LOCKED,
109
        1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
110
        1142 => DB_ERROR_ACCESS_VIOLATION,
111
        1146 => DB_ERROR_NOSUCHTABLE,
112
        1216 => DB_ERROR_CONSTRAINT,
113
        1217 => DB_ERROR_CONSTRAINT,
187 mathias 114
        1356 => DB_ERROR_DIVZERO,
115
        1451 => DB_ERROR_CONSTRAINT,
116
        1452 => DB_ERROR_CONSTRAINT,
94 jpm 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
    // }}}
162
    // {{{ constructor
163
 
164
    /**
187 mathias 165
     * This constructor calls <kbd>parent::__construct()</kbd>
94 jpm 166
     *
167
     * @return void
168
     */
187 mathias 169
    function __construct()
94 jpm 170
    {
187 mathias 171
        parent::__construct();
94 jpm 172
    }
173
 
174
    // }}}
175
    // {{{ connect()
176
 
177
    /**
178
     * Connect to the database server, log in and open the database
179
     *
180
     * Don't call this method directly.  Use DB::connect() instead.
181
     *
182
     * PEAR DB's mysql driver supports the following extra DSN options:
183
     *   + new_link      If set to true, causes subsequent calls to connect()
184
     *                    to return a new connection link instead of the
185
     *                    existing one.  WARNING: this is not portable to
186
     *                    other DBMS's. Available since PEAR DB 1.7.0.
187
     *   + client_flags  Any combination of MYSQL_CLIENT_* constants.
188
     *                    Only used if PHP is at version 4.3.0 or greater.
189
     *                    Available since PEAR DB 1.7.0.
190
     *
191
     * @param array $dsn         the data source name
192
     * @param bool  $persistent  should the connection be persistent?
193
     *
194
     * @return int  DB_OK on success. A DB_Error object on failure.
195
     */
196
    function connect($dsn, $persistent = false)
197
    {
198
        if (!PEAR::loadExtension('mysql')) {
199
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
200
        }
201
 
202
        $this->dsn = $dsn;
203
        if ($dsn['dbsyntax']) {
204
            $this->dbsyntax = $dsn['dbsyntax'];
205
        }
206
 
207
        $params = array();
208
        if ($dsn['protocol'] && $dsn['protocol'] == 'unix') {
209
            $params[0] = ':' . $dsn['socket'];
210
        } else {
211
            $params[0] = $dsn['hostspec'] ? $dsn['hostspec']
212
                         : 'localhost';
213
            if ($dsn['port']) {
214
                $params[0] .= ':' . $dsn['port'];
215
            }
216
        }
217
        $params[] = $dsn['username'] ? $dsn['username'] : null;
218
        $params[] = $dsn['password'] ? $dsn['password'] : null;
219
 
220
        if (!$persistent) {
221
            if (isset($dsn['new_link'])
222
                && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
223
            {
224
                $params[] = true;
225
            } else {
226
                $params[] = false;
227
            }
228
        }
229
        if (version_compare(phpversion(), '4.3.0', '>=')) {
230
            $params[] = isset($dsn['client_flags'])
231
                        ? $dsn['client_flags'] : null;
232
        }
233
 
234
        $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
235
 
236
        $ini = ini_get('track_errors');
237
        $php_errormsg = '';
238
        if ($ini) {
239
            $this->connection = @call_user_func_array($connect_function,
240
                                                      $params);
241
        } else {
187 mathias 242
            @ini_set('track_errors', 1);
94 jpm 243
            $this->connection = @call_user_func_array($connect_function,
244
                                                      $params);
187 mathias 245
            @ini_set('track_errors', $ini);
94 jpm 246
        }
247
 
248
        if (!$this->connection) {
249
            if (($err = @mysql_error()) != '') {
250
                return $this->raiseError(DB_ERROR_CONNECT_FAILED,
251
                                         null, null, null,
252
                                         $err);
253
            } else {
254
                return $this->raiseError(DB_ERROR_CONNECT_FAILED,
255
                                         null, null, null,
256
                                         $php_errormsg);
257
            }
258
        }
259
 
260
        if ($dsn['database']) {
261
            if (!@mysql_select_db($dsn['database'], $this->connection)) {
262
                return $this->mysqlRaiseError();
263
            }
264
            $this->_db = $dsn['database'];
265
        }
266
 
267
        return DB_OK;
268
    }
269
 
270
    // }}}
271
    // {{{ disconnect()
272
 
273
    /**
274
     * Disconnects from the database server
275
     *
276
     * @return bool  TRUE on success, FALSE on failure
277
     */
278
    function disconnect()
279
    {
280
        $ret = @mysql_close($this->connection);
281
        $this->connection = null;
282
        return $ret;
283
    }
284
 
285
    // }}}
286
    // {{{ simpleQuery()
287
 
288
    /**
289
     * Sends a query to the database server
290
     *
291
     * Generally uses mysql_query().  If you want to use
292
     * mysql_unbuffered_query() set the "result_buffering" option to 0 using
293
     * setOptions().  This option was added in Release 1.7.0.
294
     *
295
     * @param string  the SQL query string
296
     *
297
     * @return mixed  + a PHP result resrouce for successful SELECT queries
298
     *                + the DB_OK constant for other successful queries
299
     *                + a DB_Error object on failure
300
     */
301
    function simpleQuery($query)
302
    {
187 mathias 303
        $ismanip = $this->_checkManip($query);
94 jpm 304
        $this->last_query = $query;
305
        $query = $this->modifyQuery($query);
306
        if ($this->_db) {
307
            if (!@mysql_select_db($this->_db, $this->connection)) {
308
                return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
309
            }
310
        }
311
        if (!$this->autocommit && $ismanip) {
312
            if ($this->transaction_opcount == 0) {
313
                $result = @mysql_query('SET AUTOCOMMIT=0', $this->connection);
314
                $result = @mysql_query('BEGIN', $this->connection);
315
                if (!$result) {
316
                    return $this->mysqlRaiseError();
317
                }
318
            }
319
            $this->transaction_opcount++;
320
        }
321
        if (!$this->options['result_buffering']) {
322
            $result = @mysql_unbuffered_query($query, $this->connection);
323
        } else {
324
            $result = @mysql_query($query, $this->connection);
325
        }
326
        if (!$result) {
327
            return $this->mysqlRaiseError();
328
        }
329
        if (is_resource($result)) {
330
            return $result;
331
        }
332
        return DB_OK;
333
    }
334
 
335
    // }}}
336
    // {{{ nextResult()
337
 
338
    /**
339
     * Move the internal mysql result pointer to the next available result
340
     *
341
     * This method has not been implemented yet.
342
     *
343
     * @param a valid sql result resource
344
     *
345
     * @return false
346
     */
347
    function nextResult($result)
348
    {
349
        return false;
350
    }
351
 
352
    // }}}
353
    // {{{ fetchInto()
354
 
355
    /**
356
     * Places a row from the result set into the given array
357
     *
358
     * Formating of the array and the data therein are configurable.
359
     * See DB_result::fetchInto() for more information.
360
     *
361
     * This method is not meant to be called directly.  Use
362
     * DB_result::fetchInto() instead.  It can't be declared "protected"
363
     * because DB_result is a separate object.
364
     *
365
     * @param resource $result    the query result resource
366
     * @param array    $arr       the referenced array to put the data in
367
     * @param int      $fetchmode how the resulting array should be indexed
368
     * @param int      $rownum    the row number to fetch (0 = first row)
369
     *
370
     * @return mixed  DB_OK on success, NULL when the end of a result set is
371
     *                 reached or on failure
372
     *
373
     * @see DB_result::fetchInto()
374
     */
375
    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
376
    {
377
        if ($rownum !== null) {
378
            if (!@mysql_data_seek($result, $rownum)) {
379
                return null;
380
            }
381
        }
382
        if ($fetchmode & DB_FETCHMODE_ASSOC) {
383
            $arr = @mysql_fetch_array($result, MYSQL_ASSOC);
384
            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
385
                $arr = array_change_key_case($arr, CASE_LOWER);
386
            }
387
        } else {
388
            $arr = @mysql_fetch_row($result);
389
        }
390
        if (!$arr) {
391
            return null;
392
        }
393
        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
394
            /*
395
             * Even though this DBMS already trims output, we do this because
396
             * a field might have intentional whitespace at the end that
397
             * gets removed by DB_PORTABILITY_RTRIM under another driver.
398
             */
399
            $this->_rtrimArrayValues($arr);
400
        }
401
        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
402
            $this->_convertNullArrayValuesToEmpty($arr);
403
        }
404
        return DB_OK;
405
    }
406
 
407
    // }}}
408
    // {{{ freeResult()
409
 
410
    /**
411
     * Deletes the result set and frees the memory occupied by the result set
412
     *
413
     * This method is not meant to be called directly.  Use
414
     * DB_result::free() instead.  It can't be declared "protected"
415
     * because DB_result is a separate object.
416
     *
417
     * @param resource $result  PHP's query result resource
418
     *
419
     * @return bool  TRUE on success, FALSE if $result is invalid
420
     *
421
     * @see DB_result::free()
422
     */
423
    function freeResult($result)
424
    {
187 mathias 425
        return is_resource($result) ? mysql_free_result($result) : false;
94 jpm 426
    }
427
 
428
    // }}}
429
    // {{{ numCols()
430
 
431
    /**
432
     * Gets the number of columns in a result set
433
     *
434
     * This method is not meant to be called directly.  Use
435
     * DB_result::numCols() instead.  It can't be declared "protected"
436
     * because DB_result is a separate object.
437
     *
438
     * @param resource $result  PHP's query result resource
439
     *
440
     * @return int  the number of columns.  A DB_Error object on failure.
441
     *
442
     * @see DB_result::numCols()
443
     */
444
    function numCols($result)
445
    {
446
        $cols = @mysql_num_fields($result);
447
        if (!$cols) {
448
            return $this->mysqlRaiseError();
449
        }
450
        return $cols;
451
    }
452
 
453
    // }}}
454
    // {{{ numRows()
455
 
456
    /**
457
     * Gets the number of rows in a result set
458
     *
459
     * This method is not meant to be called directly.  Use
460
     * DB_result::numRows() instead.  It can't be declared "protected"
461
     * because DB_result is a separate object.
462
     *
463
     * @param resource $result  PHP's query result resource
464
     *
465
     * @return int  the number of rows.  A DB_Error object on failure.
466
     *
467
     * @see DB_result::numRows()
468
     */
469
    function numRows($result)
470
    {
471
        $rows = @mysql_num_rows($result);
472
        if ($rows === null) {
473
            return $this->mysqlRaiseError();
474
        }
475
        return $rows;
476
    }
477
 
478
    // }}}
479
    // {{{ autoCommit()
480
 
481
    /**
482
     * Enables or disables automatic commits
483
     *
484
     * @param bool $onoff  true turns it on, false turns it off
485
     *
486
     * @return int  DB_OK on success.  A DB_Error object if the driver
487
     *               doesn't support auto-committing transactions.
488
     */
489
    function autoCommit($onoff = false)
490
    {
491
        // XXX if $this->transaction_opcount > 0, we should probably
492
        // issue a warning here.
493
        $this->autocommit = $onoff ? true : false;
494
        return DB_OK;
495
    }
496
 
497
    // }}}
498
    // {{{ commit()
499
 
500
    /**
501
     * Commits the current transaction
502
     *
503
     * @return int  DB_OK on success.  A DB_Error object on failure.
504
     */
505
    function commit()
506
    {
507
        if ($this->transaction_opcount > 0) {
508
            if ($this->_db) {
509
                if (!@mysql_select_db($this->_db, $this->connection)) {
510
                    return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
511
                }
512
            }
513
            $result = @mysql_query('COMMIT', $this->connection);
514
            $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
515
            $this->transaction_opcount = 0;
516
            if (!$result) {
517
                return $this->mysqlRaiseError();
518
            }
519
        }
520
        return DB_OK;
521
    }
522
 
523
    // }}}
524
    // {{{ rollback()
525
 
526
    /**
527
     * Reverts the current transaction
528
     *
529
     * @return int  DB_OK on success.  A DB_Error object on failure.
530
     */
531
    function rollback()
532
    {
533
        if ($this->transaction_opcount > 0) {
534
            if ($this->_db) {
535
                if (!@mysql_select_db($this->_db, $this->connection)) {
536
                    return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
537
                }
538
            }
539
            $result = @mysql_query('ROLLBACK', $this->connection);
540
            $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
541
            $this->transaction_opcount = 0;
542
            if (!$result) {
543
                return $this->mysqlRaiseError();
544
            }
545
        }
546
        return DB_OK;
547
    }
548
 
549
    // }}}
550
    // {{{ affectedRows()
551
 
552
    /**
553
     * Determines the number of rows affected by a data maniuplation query
554
     *
555
     * 0 is returned for queries that don't manipulate data.
556
     *
557
     * @return int  the number of rows.  A DB_Error object on failure.
558
     */
559
    function affectedRows()
560
    {
187 mathias 561
        if ($this->_last_query_manip) {
94 jpm 562
            return @mysql_affected_rows($this->connection);
563
        } else {
564
            return 0;
565
        }
566
     }
567
 
568
    // }}}
569
    // {{{ nextId()
570
 
571
    /**
572
     * Returns the next free id in a sequence
573
     *
574
     * @param string  $seq_name  name of the sequence
575
     * @param boolean $ondemand  when true, the seqence is automatically
576
     *                            created if it does not exist
577
     *
578
     * @return int  the next id number in the sequence.
579
     *               A DB_Error object on failure.
580
     *
581
     * @see DB_common::nextID(), DB_common::getSequenceName(),
582
     *      DB_mysql::createSequence(), DB_mysql::dropSequence()
583
     */
584
    function nextId($seq_name, $ondemand = true)
585
    {
586
        $seqname = $this->getSequenceName($seq_name);
587
        do {
588
            $repeat = 0;
589
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
590
            $result = $this->query("UPDATE ${seqname} ".
591
                                   'SET id=LAST_INSERT_ID(id+1)');
592
            $this->popErrorHandling();
593
            if ($result === DB_OK) {
594
                // COMMON CASE
595
                $id = @mysql_insert_id($this->connection);
596
                if ($id != 0) {
597
                    return $id;
598
                }
599
                // EMPTY SEQ TABLE
600
                // Sequence table must be empty for some reason, so fill
601
                // it and return 1 and obtain a user-level lock
602
                $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
603
                if (DB::isError($result)) {
604
                    return $this->raiseError($result);
605
                }
606
                if ($result == 0) {
607
                    // Failed to get the lock
608
                    return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
609
                }
610
 
611
                // add the default value
612
                $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)");
613
                if (DB::isError($result)) {
614
                    return $this->raiseError($result);
615
                }
616
 
617
                // Release the lock
618
                $result = $this->getOne('SELECT RELEASE_LOCK('
619
                                        . "'${seqname}_lock')");
620
                if (DB::isError($result)) {
621
                    return $this->raiseError($result);
622
                }
623
                // We know what the result will be, so no need to try again
624
                return 1;
625
 
626
            } elseif ($ondemand && DB::isError($result) &&
627
                $result->getCode() == DB_ERROR_NOSUCHTABLE)
628
            {
629
                // ONDEMAND TABLE CREATION
630
                $result = $this->createSequence($seq_name);
631
                if (DB::isError($result)) {
632
                    return $this->raiseError($result);
633
                } else {
634
                    $repeat = 1;
635
                }
636
 
637
            } elseif (DB::isError($result) &&
638
                      $result->getCode() == DB_ERROR_ALREADY_EXISTS)
639
            {
640
                // BACKWARDS COMPAT
641
                // see _BCsequence() comment
642
                $result = $this->_BCsequence($seqname);
643
                if (DB::isError($result)) {
644
                    return $this->raiseError($result);
645
                }
646
                $repeat = 1;
647
            }
648
        } while ($repeat);
649
 
650
        return $this->raiseError($result);
651
    }
652
 
653
    // }}}
654
    // {{{ createSequence()
655
 
656
    /**
657
     * Creates a new sequence
658
     *
659
     * @param string $seq_name  name of the new sequence
660
     *
661
     * @return int  DB_OK on success.  A DB_Error object on failure.
662
     *
663
     * @see DB_common::createSequence(), DB_common::getSequenceName(),
664
     *      DB_mysql::nextID(), DB_mysql::dropSequence()
665
     */
666
    function createSequence($seq_name)
667
    {
668
        $seqname = $this->getSequenceName($seq_name);
669
        $res = $this->query('CREATE TABLE ' . $seqname
670
                            . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
671
                            . ' PRIMARY KEY(id))');
672
        if (DB::isError($res)) {
673
            return $res;
674
        }
675
        // insert yields value 1, nextId call will generate ID 2
676
        $res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
677
        if (DB::isError($res)) {
678
            return $res;
679
        }
680
        // so reset to zero
681
        return $this->query("UPDATE ${seqname} SET id = 0");
682
    }
683
 
684
    // }}}
685
    // {{{ dropSequence()
686
 
687
    /**
688
     * Deletes a sequence
689
     *
690
     * @param string $seq_name  name of the sequence to be deleted
691
     *
692
     * @return int  DB_OK on success.  A DB_Error object on failure.
693
     *
694
     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
695
     *      DB_mysql::nextID(), DB_mysql::createSequence()
696
     */
697
    function dropSequence($seq_name)
698
    {
699
        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
700
    }
701
 
702
    // }}}
703
    // {{{ _BCsequence()
704
 
705
    /**
706
     * Backwards compatibility with old sequence emulation implementation
707
     * (clean up the dupes)
708
     *
709
     * @param string $seqname  the sequence name to clean up
710
     *
711
     * @return bool  true on success.  A DB_Error object on failure.
712
     *
713
     * @access private
714
     */
715
    function _BCsequence($seqname)
716
    {
717
        // Obtain a user-level lock... this will release any previous
718
        // application locks, but unlike LOCK TABLES, it does not abort
719
        // the current transaction and is much less frequently used.
720
        $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
721
        if (DB::isError($result)) {
722
            return $result;
723
        }
724
        if ($result == 0) {
725
            // Failed to get the lock, can't do the conversion, bail
726
            // with a DB_ERROR_NOT_LOCKED error
727
            return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
728
        }
729
 
730
        $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
731
        if (DB::isError($highest_id)) {
732
            return $highest_id;
733
        }
734
        // This should kill all rows except the highest
735
        // We should probably do something if $highest_id isn't
736
        // numeric, but I'm at a loss as how to handle that...
737
        $result = $this->query('DELETE FROM ' . $seqname
738
                               . " WHERE id <> $highest_id");
739
        if (DB::isError($result)) {
740
            return $result;
741
        }
742
 
743
        // If another thread has been waiting for this lock,
744
        // it will go thru the above procedure, but will have no
745
        // real effect
746
        $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
747
        if (DB::isError($result)) {
748
            return $result;
749
        }
750
        return true;
751
    }
752
 
753
    // }}}
754
    // {{{ quoteIdentifier()
755
 
756
    /**
757
     * Quotes a string so it can be safely used as a table or column name
187 mathias 758
     * (WARNING: using names that require this is a REALLY BAD IDEA)
94 jpm 759
     *
187 mathias 760
     * WARNING:  Older versions of MySQL can't handle the backtick
761
     * character (<kbd>`</kbd>) in table or column names.
94 jpm 762
     *
763
     * @param string $str  identifier name to be quoted
764
     *
765
     * @return string  quoted identifier string
766
     *
767
     * @see DB_common::quoteIdentifier()
768
     * @since Method available since Release 1.6.0
769
     */
770
    function quoteIdentifier($str)
771
    {
187 mathias 772
        return '`' . str_replace('`', '``', $str) . '`';
94 jpm 773
    }
774
 
775
    // }}}
776
    // {{{ escapeSimple()
777
 
778
    /**
779
     * Escapes a string according to the current DBMS's standards
780
     *
781
     * @param string $str  the string to be escaped
782
     *
783
     * @return string  the escaped string
784
     *
785
     * @see DB_common::quoteSmart()
786
     * @since Method available since Release 1.6.0
787
     */
788
    function escapeSimple($str)
789
    {
790
        if (function_exists('mysql_real_escape_string')) {
791
            return @mysql_real_escape_string($str, $this->connection);
792
        } else {
793
            return @mysql_escape_string($str);
794
        }
795
    }
796
 
797
    // }}}
798
    // {{{ modifyQuery()
799
 
800
    /**
801
     * Changes a query string for various DBMS specific reasons
802
     *
803
     * This little hack lets you know how many rows were deleted
804
     * when running a "DELETE FROM table" query.  Only implemented
805
     * if the DB_PORTABILITY_DELETE_COUNT portability option is on.
806
     *
807
     * @param string $query  the query string to modify
808
     *
809
     * @return string  the modified query string
810
     *
811
     * @access protected
812
     * @see DB_common::setOption()
813
     */
814
    function modifyQuery($query)
815
    {
816
        if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
817
            // "DELETE FROM table" gives 0 affected rows in MySQL.
818
            // This little hack lets you know how many rows were deleted.
819
            if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
820
                $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
821
                                      'DELETE FROM \1 WHERE 1=1', $query);
822
            }
823
        }
824
        return $query;
825
    }
826
 
827
    // }}}
828
    // {{{ modifyLimitQuery()
829
 
830
    /**
831
     * Adds LIMIT clauses to a query string according to current DBMS standards
832
     *
833
     * @param string $query   the query to modify
834
     * @param int    $from    the row to start to fetching (0 = the first row)
835
     * @param int    $count   the numbers of rows to fetch
836
     * @param mixed  $params  array, string or numeric data to be used in
837
     *                         execution of the statement.  Quantity of items
838
     *                         passed must match quantity of placeholders in
839
     *                         query:  meaning 1 placeholder for non-array
840
     *                         parameters or 1 placeholder per array element.
841
     *
842
     * @return string  the query string with LIMIT clauses added
843
     *
844
     * @access protected
845
     */
846
    function modifyLimitQuery($query, $from, $count, $params = array())
847
    {
187 mathias 848
        if (DB::isManip($query) || $this->_next_query_manip) {
94 jpm 849
            return $query . " LIMIT $count";
850
        } else {
851
            return $query . " LIMIT $from, $count";
852
        }
853
    }
854
 
855
    // }}}
856
    // {{{ mysqlRaiseError()
857
 
858
    /**
859
     * Produces a DB_Error object regarding the current problem
860
     *
861
     * @param int $errno  if the error is being manually raised pass a
862
     *                     DB_ERROR* constant here.  If this isn't passed
863
     *                     the error information gathered from the DBMS.
864
     *
865
     * @return object  the DB_Error object
866
     *
867
     * @see DB_common::raiseError(),
868
     *      DB_mysql::errorNative(), DB_common::errorCode()
869
     */
870
    function mysqlRaiseError($errno = null)
871
    {
872
        if ($errno === null) {
873
            if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
874
                $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
875
                $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
876
                $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
877
            } else {
878
                // Doing this in case mode changes during runtime.
879
                $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
880
                $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
881
                $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
882
            }
883
            $errno = $this->errorCode(mysql_errno($this->connection));
884
        }
885
        return $this->raiseError($errno, null, null, null,
886
                                 @mysql_errno($this->connection) . ' ** ' .
887
                                 @mysql_error($this->connection));
888
    }
889
 
890
    // }}}
891
    // {{{ errorNative()
892
 
893
    /**
894
     * Gets the DBMS' native error code produced by the last query
895
     *
896
     * @return int  the DBMS' error code
897
     */
898
    function errorNative()
899
    {
900
        return @mysql_errno($this->connection);
901
    }
902
 
903
    // }}}
904
    // {{{ tableInfo()
905
 
906
    /**
907
     * Returns information about a table or a result set
908
     *
909
     * @param object|string  $result  DB_result object from a query or a
910
     *                                 string containing the name of a table.
911
     *                                 While this also accepts a query result
912
     *                                 resource identifier, this behavior is
913
     *                                 deprecated.
914
     * @param int            $mode    a valid tableInfo mode
915
     *
916
     * @return array  an associative array with the information requested.
917
     *                 A DB_Error object on failure.
918
     *
919
     * @see DB_common::tableInfo()
920
     */
921
    function tableInfo($result, $mode = null)
922
    {
923
        if (is_string($result)) {
187 mathias 924
            // Fix for bug #11580.
925
            if ($this->_db) {
926
                if (!@mysql_select_db($this->_db, $this->connection)) {
927
                    return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
928
                }
929
            }
930
 
94 jpm 931
            /*
932
             * Probably received a table name.
933
             * Create a result resource identifier.
934
             */
187 mathias 935
            $id = @mysql_query("SELECT * FROM $result LIMIT 0",
936
                               $this->connection);
94 jpm 937
            $got_string = true;
938
        } elseif (isset($result->result)) {
939
            /*
940
             * Probably received a result object.
941
             * Extract the result resource identifier.
942
             */
943
            $id = $result->result;
944
            $got_string = false;
945
        } else {
946
            /*
947
             * Probably received a result resource identifier.
948
             * Copy it.
949
             * Deprecated.  Here for compatibility only.
950
             */
951
            $id = $result;
952
            $got_string = false;
953
        }
954
 
955
        if (!is_resource($id)) {
956
            return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA);
957
        }
958
 
959
        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
960
            $case_func = 'strtolower';
961
        } else {
962
            $case_func = 'strval';
963
        }
964
 
965
        $count = @mysql_num_fields($id);
966
        $res   = array();
967
 
968
        if ($mode) {
969
            $res['num_fields'] = $count;
970
        }
971
 
972
        for ($i = 0; $i < $count; $i++) {
973
            $res[$i] = array(
974
                'table' => $case_func(@mysql_field_table($id, $i)),
975
                'name'  => $case_func(@mysql_field_name($id, $i)),
976
                'type'  => @mysql_field_type($id, $i),
977
                'len'   => @mysql_field_len($id, $i),
978
                'flags' => @mysql_field_flags($id, $i),
979
            );
980
            if ($mode & DB_TABLEINFO_ORDER) {
981
                $res['order'][$res[$i]['name']] = $i;
982
            }
983
            if ($mode & DB_TABLEINFO_ORDERTABLE) {
984
                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
985
            }
986
        }
987
 
988
        // free the result only if we were called on a table
989
        if ($got_string) {
990
            @mysql_free_result($id);
991
        }
992
        return $res;
993
    }
994
 
995
    // }}}
996
    // {{{ getSpecialQuery()
997
 
998
    /**
999
     * Obtains the query string needed for listing a given type of objects
1000
     *
1001
     * @param string $type  the kind of objects you want to retrieve
1002
     *
1003
     * @return string  the SQL query string or null if the driver doesn't
1004
     *                  support the object type requested
1005
     *
1006
     * @access protected
1007
     * @see DB_common::getListOf()
1008
     */
1009
    function getSpecialQuery($type)
1010
    {
1011
        switch ($type) {
1012
            case 'tables':
1013
                return 'SHOW TABLES';
1014
            case 'users':
1015
                return 'SELECT DISTINCT User FROM mysql.user';
1016
            case 'databases':
1017
                return 'SHOW DATABASES';
1018
            default:
1019
                return null;
1020
        }
1021
    }
1022
 
1023
    // }}}
1024
 
1025
}
1026
 
1027
/*
1028
 * Local variables:
1029
 * tab-width: 4
1030
 * c-basic-offset: 4
1031
 * End:
1032
 */
1033
 
1034
?>