Subversion Repositories Applications.papyrus

Rev

Go to most recent revision | Details | 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 mysql 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     Stig Bakken <ssb@php.net>
20
 * @author     Daniel Convissor <danielc@php.net>
21
 * @copyright  1997-2005 The PHP Group
22
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
443 ddelon 23
 * @version    CVS: $Id: mysql.php,v 1.2 2005-09-20 17:01:22 ddelon Exp $
320 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>
42
 * @copyright  1997-2005 The PHP Group
43
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
443 ddelon 44
 * @version    Release: @package_version@
320 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,
114
    );
115
 
116
    /**
117
     * The raw database connection created by PHP
118
     * @var resource
119
     */
120
    var $connection;
121
 
122
    /**
123
     * The DSN information for connecting to a database
124
     * @var array
125
     */
126
    var $dsn = array();
127
 
128
 
129
    /**
130
     * Should data manipulation queries be committed automatically?
131
     * @var bool
132
     * @access private
133
     */
134
    var $autocommit = true;
135
 
136
    /**
137
     * The quantity of transactions begun
138
     *
139
     * {@internal  While this is private, it can't actually be designated
140
     * private in PHP 5 because it is directly accessed in the test suite.}}
141
     *
142
     * @var integer
143
     * @access private
144
     */
145
    var $transaction_opcount = 0;
146
 
147
    /**
148
     * The database specified in the DSN
149
     *
150
     * It's a fix to allow calls to different databases in the same script.
151
     *
152
     * @var string
153
     * @access private
154
     */
155
    var $_db = '';
156
 
157
 
158
    // }}}
159
    // {{{ constructor
160
 
161
    /**
162
     * This constructor calls <kbd>$this->DB_common()</kbd>
163
     *
164
     * @return void
165
     */
166
    function DB_mysql()
167
    {
168
        $this->DB_common();
169
    }
170
 
171
    // }}}
172
    // {{{ connect()
173
 
174
    /**
175
     * Connect to the database server, log in and open the database
176
     *
177
     * Don't call this method directly.  Use DB::connect() instead.
178
     *
179
     * PEAR DB's mysql driver supports the following extra DSN options:
180
     *   + new_link      If set to true, causes subsequent calls to connect()
181
     *                    to return a new connection link instead of the
182
     *                    existing one.  WARNING: this is not portable to
183
     *                    other DBMS's. Available since PEAR DB 1.7.0.
184
     *   + client_flags  Any combination of MYSQL_CLIENT_* constants.
185
     *                    Only used if PHP is at version 4.3.0 or greater.
186
     *                    Available since PEAR DB 1.7.0.
187
     *
188
     * @param array $dsn         the data source name
189
     * @param bool  $persistent  should the connection be persistent?
190
     *
191
     * @return int  DB_OK on success. A DB_Error object on failure.
192
     */
193
    function connect($dsn, $persistent = false)
194
    {
195
        if (!PEAR::loadExtension('mysql')) {
196
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
197
        }
198
 
199
        $this->dsn = $dsn;
200
        if ($dsn['dbsyntax']) {
201
            $this->dbsyntax = $dsn['dbsyntax'];
202
        }
203
 
204
        $params = array();
205
        if ($dsn['protocol'] && $dsn['protocol'] == 'unix') {
206
            $params[0] = ':' . $dsn['socket'];
207
        } else {
208
            $params[0] = $dsn['hostspec'] ? $dsn['hostspec']
209
                         : 'localhost';
210
            if ($dsn['port']) {
211
                $params[0] .= ':' . $dsn['port'];
212
            }
213
        }
214
        $params[] = $dsn['username'] ? $dsn['username'] : null;
215
        $params[] = $dsn['password'] ? $dsn['password'] : null;
216
 
217
        if (!$persistent) {
218
            if (isset($dsn['new_link'])
219
                && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true))
220
            {
221
                $params[] = true;
222
            } else {
223
                $params[] = false;
224
            }
225
        }
226
        if (version_compare(phpversion(), '4.3.0', '>=')) {
227
            $params[] = isset($dsn['client_flags'])
228
                        ? $dsn['client_flags'] : null;
229
        }
230
 
231
        $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
232
 
233
        $ini = ini_get('track_errors');
234
        $php_errormsg = '';
235
        if ($ini) {
236
            $this->connection = @call_user_func_array($connect_function,
237
                                                      $params);
238
        } else {
239
            ini_set('track_errors', 1);
240
            $this->connection = @call_user_func_array($connect_function,
241
                                                      $params);
242
            ini_set('track_errors', $ini);
243
        }
244
 
245
        if (!$this->connection) {
246
            if (($err = @mysql_error()) != '') {
247
                return $this->raiseError(DB_ERROR_CONNECT_FAILED,
248
                                         null, null, null,
249
                                         $err);
250
            } else {
251
                return $this->raiseError(DB_ERROR_CONNECT_FAILED,
252
                                         null, null, null,
253
                                         $php_errormsg);
254
            }
255
        }
256
 
257
        if ($dsn['database']) {
258
            if (!@mysql_select_db($dsn['database'], $this->connection)) {
259
                return $this->mysqlRaiseError();
260
            }
261
            $this->_db = $dsn['database'];
262
        }
263
 
264
        return DB_OK;
265
    }
266
 
267
    // }}}
268
    // {{{ disconnect()
269
 
270
    /**
271
     * Disconnects from the database server
272
     *
273
     * @return bool  TRUE on success, FALSE on failure
274
     */
275
    function disconnect()
276
    {
277
        $ret = @mysql_close($this->connection);
278
        $this->connection = null;
279
        return $ret;
280
    }
281
 
282
    // }}}
283
    // {{{ simpleQuery()
284
 
285
    /**
286
     * Sends a query to the database server
287
     *
288
     * Generally uses mysql_query().  If you want to use
289
     * mysql_unbuffered_query() set the "result_buffering" option to 0 using
290
     * setOptions().  This option was added in Release 1.7.0.
291
     *
292
     * @param string  the SQL query string
293
     *
294
     * @return mixed  + a PHP result resrouce for successful SELECT queries
295
     *                + the DB_OK constant for other successful queries
296
     *                + a DB_Error object on failure
297
     */
298
    function simpleQuery($query)
299
    {
300
        $ismanip = DB::isManip($query);
301
        $this->last_query = $query;
302
        $query = $this->modifyQuery($query);
303
        if ($this->_db) {
304
            if (!@mysql_select_db($this->_db, $this->connection)) {
305
                return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
306
            }
307
        }
308
        if (!$this->autocommit && $ismanip) {
309
            if ($this->transaction_opcount == 0) {
310
                $result = @mysql_query('SET AUTOCOMMIT=0', $this->connection);
311
                $result = @mysql_query('BEGIN', $this->connection);
312
                if (!$result) {
313
                    return $this->mysqlRaiseError();
314
                }
315
            }
316
            $this->transaction_opcount++;
317
        }
318
        if (!$this->options['result_buffering']) {
319
            $result = @mysql_unbuffered_query($query, $this->connection);
320
        } else {
321
            $result = @mysql_query($query, $this->connection);
322
        }
323
        if (!$result) {
324
            return $this->mysqlRaiseError();
325
        }
326
        if (is_resource($result)) {
327
            return $result;
328
        }
329
        return DB_OK;
330
    }
331
 
332
    // }}}
333
    // {{{ nextResult()
334
 
335
    /**
336
     * Move the internal mysql result pointer to the next available result
337
     *
338
     * This method has not been implemented yet.
339
     *
340
     * @param a valid sql result resource
341
     *
342
     * @return false
343
     */
344
    function nextResult($result)
345
    {
346
        return false;
347
    }
348
 
349
    // }}}
350
    // {{{ fetchInto()
351
 
352
    /**
353
     * Places a row from the result set into the given array
354
     *
355
     * Formating of the array and the data therein are configurable.
356
     * See DB_result::fetchInto() for more information.
357
     *
358
     * This method is not meant to be called directly.  Use
359
     * DB_result::fetchInto() instead.  It can't be declared "protected"
360
     * because DB_result is a separate object.
361
     *
362
     * @param resource $result    the query result resource
363
     * @param array    $arr       the referenced array to put the data in
364
     * @param int      $fetchmode how the resulting array should be indexed
365
     * @param int      $rownum    the row number to fetch (0 = first row)
366
     *
367
     * @return mixed  DB_OK on success, NULL when the end of a result set is
368
     *                 reached or on failure
369
     *
370
     * @see DB_result::fetchInto()
371
     */
372
    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
373
    {
374
        if ($rownum !== null) {
375
            if (!@mysql_data_seek($result, $rownum)) {
376
                return null;
377
            }
378
        }
379
        if ($fetchmode & DB_FETCHMODE_ASSOC) {
380
            $arr = @mysql_fetch_array($result, MYSQL_ASSOC);
381
            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
382
                $arr = array_change_key_case($arr, CASE_LOWER);
383
            }
384
        } else {
385
            $arr = @mysql_fetch_row($result);
386
        }
387
        if (!$arr) {
388
            return null;
389
        }
390
        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
391
            /*
392
             * Even though this DBMS already trims output, we do this because
393
             * a field might have intentional whitespace at the end that
394
             * gets removed by DB_PORTABILITY_RTRIM under another driver.
395
             */
396
            $this->_rtrimArrayValues($arr);
397
        }
398
        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
399
            $this->_convertNullArrayValuesToEmpty($arr);
400
        }
401
        return DB_OK;
402
    }
403
 
404
    // }}}
405
    // {{{ freeResult()
406
 
407
    /**
408
     * Deletes the result set and frees the memory occupied by the result set
409
     *
410
     * This method is not meant to be called directly.  Use
411
     * DB_result::free() instead.  It can't be declared "protected"
412
     * because DB_result is a separate object.
413
     *
414
     * @param resource $result  PHP's query result resource
415
     *
416
     * @return bool  TRUE on success, FALSE if $result is invalid
417
     *
418
     * @see DB_result::free()
419
     */
420
    function freeResult($result)
421
    {
422
        return @mysql_free_result($result);
423
    }
424
 
425
    // }}}
426
    // {{{ numCols()
427
 
428
    /**
429
     * Gets the number of columns in a result set
430
     *
431
     * This method is not meant to be called directly.  Use
432
     * DB_result::numCols() instead.  It can't be declared "protected"
433
     * because DB_result is a separate object.
434
     *
435
     * @param resource $result  PHP's query result resource
436
     *
437
     * @return int  the number of columns.  A DB_Error object on failure.
438
     *
439
     * @see DB_result::numCols()
440
     */
441
    function numCols($result)
442
    {
443
        $cols = @mysql_num_fields($result);
444
        if (!$cols) {
445
            return $this->mysqlRaiseError();
446
        }
447
        return $cols;
448
    }
449
 
450
    // }}}
451
    // {{{ numRows()
452
 
453
    /**
454
     * Gets the number of rows in a result set
455
     *
456
     * This method is not meant to be called directly.  Use
457
     * DB_result::numRows() instead.  It can't be declared "protected"
458
     * because DB_result is a separate object.
459
     *
460
     * @param resource $result  PHP's query result resource
461
     *
462
     * @return int  the number of rows.  A DB_Error object on failure.
463
     *
464
     * @see DB_result::numRows()
465
     */
466
    function numRows($result)
467
    {
468
        $rows = @mysql_num_rows($result);
469
        if ($rows === null) {
470
            return $this->mysqlRaiseError();
471
        }
472
        return $rows;
473
    }
474
 
475
    // }}}
476
    // {{{ autoCommit()
477
 
478
    /**
479
     * Enables or disables automatic commits
480
     *
481
     * @param bool $onoff  true turns it on, false turns it off
482
     *
483
     * @return int  DB_OK on success.  A DB_Error object if the driver
484
     *               doesn't support auto-committing transactions.
485
     */
486
    function autoCommit($onoff = false)
487
    {
488
        // XXX if $this->transaction_opcount > 0, we should probably
489
        // issue a warning here.
490
        $this->autocommit = $onoff ? true : false;
491
        return DB_OK;
492
    }
493
 
494
    // }}}
495
    // {{{ commit()
496
 
497
    /**
498
     * Commits the current transaction
499
     *
500
     * @return int  DB_OK on success.  A DB_Error object on failure.
501
     */
502
    function commit()
503
    {
504
        if ($this->transaction_opcount > 0) {
505
            if ($this->_db) {
506
                if (!@mysql_select_db($this->_db, $this->connection)) {
507
                    return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
508
                }
509
            }
510
            $result = @mysql_query('COMMIT', $this->connection);
511
            $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
512
            $this->transaction_opcount = 0;
513
            if (!$result) {
514
                return $this->mysqlRaiseError();
515
            }
516
        }
517
        return DB_OK;
518
    }
519
 
520
    // }}}
521
    // {{{ rollback()
522
 
523
    /**
524
     * Reverts the current transaction
525
     *
526
     * @return int  DB_OK on success.  A DB_Error object on failure.
527
     */
528
    function rollback()
529
    {
530
        if ($this->transaction_opcount > 0) {
531
            if ($this->_db) {
532
                if (!@mysql_select_db($this->_db, $this->connection)) {
533
                    return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
534
                }
535
            }
536
            $result = @mysql_query('ROLLBACK', $this->connection);
537
            $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
538
            $this->transaction_opcount = 0;
539
            if (!$result) {
540
                return $this->mysqlRaiseError();
541
            }
542
        }
543
        return DB_OK;
544
    }
545
 
546
    // }}}
547
    // {{{ affectedRows()
548
 
549
    /**
550
     * Determines the number of rows affected by a data maniuplation query
551
     *
552
     * 0 is returned for queries that don't manipulate data.
553
     *
554
     * @return int  the number of rows.  A DB_Error object on failure.
555
     */
556
    function affectedRows()
557
    {
558
        if (DB::isManip($this->last_query)) {
559
            return @mysql_affected_rows($this->connection);
560
        } else {
561
            return 0;
562
        }
563
     }
564
 
565
    // }}}
566
    // {{{ nextId()
567
 
568
    /**
569
     * Returns the next free id in a sequence
570
     *
571
     * @param string  $seq_name  name of the sequence
572
     * @param boolean $ondemand  when true, the seqence is automatically
573
     *                            created if it does not exist
574
     *
575
     * @return int  the next id number in the sequence.
576
     *               A DB_Error object on failure.
577
     *
578
     * @see DB_common::nextID(), DB_common::getSequenceName(),
579
     *      DB_mysql::createSequence(), DB_mysql::dropSequence()
580
     */
581
    function nextId($seq_name, $ondemand = true)
582
    {
583
        $seqname = $this->getSequenceName($seq_name);
584
        do {
585
            $repeat = 0;
586
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
587
            $result = $this->query("UPDATE ${seqname} ".
588
                                   'SET id=LAST_INSERT_ID(id+1)');
589
            $this->popErrorHandling();
590
            if ($result === DB_OK) {
591
                // COMMON CASE
592
                $id = @mysql_insert_id($this->connection);
593
                if ($id != 0) {
594
                    return $id;
595
                }
596
                // EMPTY SEQ TABLE
597
                // Sequence table must be empty for some reason, so fill
598
                // it and return 1 and obtain a user-level lock
599
                $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
600
                if (DB::isError($result)) {
601
                    return $this->raiseError($result);
602
                }
603
                if ($result == 0) {
604
                    // Failed to get the lock
605
                    return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
606
                }
607
 
608
                // add the default value
609
                $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)");
610
                if (DB::isError($result)) {
611
                    return $this->raiseError($result);
612
                }
613
 
614
                // Release the lock
615
                $result = $this->getOne('SELECT RELEASE_LOCK('
616
                                        . "'${seqname}_lock')");
617
                if (DB::isError($result)) {
618
                    return $this->raiseError($result);
619
                }
620
                // We know what the result will be, so no need to try again
621
                return 1;
622
 
623
            } elseif ($ondemand && DB::isError($result) &&
624
                $result->getCode() == DB_ERROR_NOSUCHTABLE)
625
            {
626
                // ONDEMAND TABLE CREATION
627
                $result = $this->createSequence($seq_name);
628
                if (DB::isError($result)) {
629
                    return $this->raiseError($result);
630
                } else {
631
                    $repeat = 1;
632
                }
633
 
634
            } elseif (DB::isError($result) &&
635
                      $result->getCode() == DB_ERROR_ALREADY_EXISTS)
636
            {
637
                // BACKWARDS COMPAT
638
                // see _BCsequence() comment
639
                $result = $this->_BCsequence($seqname);
640
                if (DB::isError($result)) {
641
                    return $this->raiseError($result);
642
                }
643
                $repeat = 1;
644
            }
645
        } while ($repeat);
646
 
647
        return $this->raiseError($result);
648
    }
649
 
650
    // }}}
651
    // {{{ createSequence()
652
 
653
    /**
654
     * Creates a new sequence
655
     *
656
     * @param string $seq_name  name of the new sequence
657
     *
658
     * @return int  DB_OK on success.  A DB_Error object on failure.
659
     *
660
     * @see DB_common::createSequence(), DB_common::getSequenceName(),
661
     *      DB_mysql::nextID(), DB_mysql::dropSequence()
662
     */
663
    function createSequence($seq_name)
664
    {
665
        $seqname = $this->getSequenceName($seq_name);
666
        $res = $this->query('CREATE TABLE ' . $seqname
667
                            . ' (id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'
668
                            . ' PRIMARY KEY(id))');
669
        if (DB::isError($res)) {
670
            return $res;
671
        }
672
        // insert yields value 1, nextId call will generate ID 2
673
        $res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
674
        if (DB::isError($res)) {
675
            return $res;
676
        }
677
        // so reset to zero
678
        return $this->query("UPDATE ${seqname} SET id = 0");
679
    }
680
 
681
    // }}}
682
    // {{{ dropSequence()
683
 
684
    /**
685
     * Deletes a sequence
686
     *
687
     * @param string $seq_name  name of the sequence to be deleted
688
     *
689
     * @return int  DB_OK on success.  A DB_Error object on failure.
690
     *
691
     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
692
     *      DB_mysql::nextID(), DB_mysql::createSequence()
693
     */
694
    function dropSequence($seq_name)
695
    {
696
        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
697
    }
698
 
699
    // }}}
700
    // {{{ _BCsequence()
701
 
702
    /**
703
     * Backwards compatibility with old sequence emulation implementation
704
     * (clean up the dupes)
705
     *
706
     * @param string $seqname  the sequence name to clean up
707
     *
708
     * @return bool  true on success.  A DB_Error object on failure.
709
     *
710
     * @access private
711
     */
712
    function _BCsequence($seqname)
713
    {
714
        // Obtain a user-level lock... this will release any previous
715
        // application locks, but unlike LOCK TABLES, it does not abort
716
        // the current transaction and is much less frequently used.
717
        $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
718
        if (DB::isError($result)) {
719
            return $result;
720
        }
721
        if ($result == 0) {
722
            // Failed to get the lock, can't do the conversion, bail
723
            // with a DB_ERROR_NOT_LOCKED error
724
            return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
725
        }
726
 
727
        $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
728
        if (DB::isError($highest_id)) {
729
            return $highest_id;
730
        }
731
        // This should kill all rows except the highest
732
        // We should probably do something if $highest_id isn't
733
        // numeric, but I'm at a loss as how to handle that...
734
        $result = $this->query('DELETE FROM ' . $seqname
735
                               . " WHERE id <> $highest_id");
736
        if (DB::isError($result)) {
737
            return $result;
738
        }
739
 
740
        // If another thread has been waiting for this lock,
741
        // it will go thru the above procedure, but will have no
742
        // real effect
743
        $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
744
        if (DB::isError($result)) {
745
            return $result;
746
        }
747
        return true;
748
    }
749
 
750
    // }}}
751
    // {{{ quoteIdentifier()
752
 
753
    /**
754
     * Quotes a string so it can be safely used as a table or column name
755
     *
756
     * MySQL can't handle the backtick character (<kbd>`</kbd>) in
757
     * table or column names.
758
     *
759
     * @param string $str  identifier name to be quoted
760
     *
761
     * @return string  quoted identifier string
762
     *
763
     * @see DB_common::quoteIdentifier()
764
     * @since Method available since Release 1.6.0
765
     */
766
    function quoteIdentifier($str)
767
    {
768
        return '`' . $str . '`';
769
    }
770
 
771
    // }}}
772
    // {{{ quote()
773
 
774
    /**
775
     * @deprecated  Deprecated in release 1.6.0
776
     */
777
    function quote($str)
778
    {
779
        return $this->quoteSmart($str);
780
    }
781
 
782
    // }}}
783
    // {{{ escapeSimple()
784
 
785
    /**
786
     * Escapes a string according to the current DBMS's standards
787
     *
788
     * @param string $str  the string to be escaped
789
     *
790
     * @return string  the escaped string
791
     *
792
     * @see DB_common::quoteSmart()
793
     * @since Method available since Release 1.6.0
794
     */
795
    function escapeSimple($str)
796
    {
797
        if (function_exists('mysql_real_escape_string')) {
798
            return @mysql_real_escape_string($str, $this->connection);
799
        } else {
800
            return @mysql_escape_string($str);
801
        }
802
    }
803
 
804
    // }}}
805
    // {{{ modifyQuery()
806
 
807
    /**
808
     * Changes a query string for various DBMS specific reasons
809
     *
810
     * This little hack lets you know how many rows were deleted
811
     * when running a "DELETE FROM table" query.  Only implemented
812
     * if the DB_PORTABILITY_DELETE_COUNT portability option is on.
813
     *
814
     * @param string $query  the query string to modify
815
     *
816
     * @return string  the modified query string
817
     *
818
     * @access protected
819
     * @see DB_common::setOption()
820
     */
821
    function modifyQuery($query)
822
    {
823
        if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
824
            // "DELETE FROM table" gives 0 affected rows in MySQL.
825
            // This little hack lets you know how many rows were deleted.
826
            if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
827
                $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
828
                                      'DELETE FROM \1 WHERE 1=1', $query);
829
            }
830
        }
831
        return $query;
832
    }
833
 
834
    // }}}
835
    // {{{ modifyLimitQuery()
836
 
837
    /**
838
     * Adds LIMIT clauses to a query string according to current DBMS standards
839
     *
840
     * @param string $query   the query to modify
841
     * @param int    $from    the row to start to fetching (0 = the first row)
842
     * @param int    $count   the numbers of rows to fetch
843
     * @param mixed  $params  array, string or numeric data to be used in
844
     *                         execution of the statement.  Quantity of items
845
     *                         passed must match quantity of placeholders in
846
     *                         query:  meaning 1 placeholder for non-array
847
     *                         parameters or 1 placeholder per array element.
848
     *
849
     * @return string  the query string with LIMIT clauses added
850
     *
851
     * @access protected
852
     */
853
    function modifyLimitQuery($query, $from, $count, $params = array())
854
    {
855
        if (DB::isManip($query)) {
856
            return $query . " LIMIT $count";
857
        } else {
858
            return $query . " LIMIT $from, $count";
859
        }
860
    }
861
 
862
    // }}}
863
    // {{{ mysqlRaiseError()
864
 
865
    /**
866
     * Produces a DB_Error object regarding the current problem
867
     *
868
     * @param int $errno  if the error is being manually raised pass a
869
     *                     DB_ERROR* constant here.  If this isn't passed
870
     *                     the error information gathered from the DBMS.
871
     *
872
     * @return object  the DB_Error object
873
     *
874
     * @see DB_common::raiseError(),
875
     *      DB_mysql::errorNative(), DB_common::errorCode()
876
     */
877
    function mysqlRaiseError($errno = null)
878
    {
879
        if ($errno === null) {
880
            if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
881
                $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
882
                $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
883
                $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
884
            } else {
885
                // Doing this in case mode changes during runtime.
886
                $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
887
                $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
888
                $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
889
            }
890
            $errno = $this->errorCode(mysql_errno($this->connection));
891
        }
892
        return $this->raiseError($errno, null, null, null,
893
                                 @mysql_errno($this->connection) . ' ** ' .
894
                                 @mysql_error($this->connection));
895
    }
896
 
897
    // }}}
898
    // {{{ errorNative()
899
 
900
    /**
901
     * Gets the DBMS' native error code produced by the last query
902
     *
903
     * @return int  the DBMS' error code
904
     */
905
    function errorNative()
906
    {
907
        return @mysql_errno($this->connection);
908
    }
909
 
910
    // }}}
911
    // {{{ tableInfo()
912
 
913
    /**
914
     * Returns information about a table or a result set
915
     *
916
     * @param object|string  $result  DB_result object from a query or a
917
     *                                 string containing the name of a table.
918
     *                                 While this also accepts a query result
919
     *                                 resource identifier, this behavior is
920
     *                                 deprecated.
921
     * @param int            $mode    a valid tableInfo mode
922
     *
923
     * @return array  an associative array with the information requested.
924
     *                 A DB_Error object on failure.
925
     *
926
     * @see DB_common::tableInfo()
927
     */
928
    function tableInfo($result, $mode = null)
929
    {
930
        if (is_string($result)) {
931
            /*
932
             * Probably received a table name.
933
             * Create a result resource identifier.
934
             */
935
            $id = @mysql_list_fields($this->dsn['database'],
936
                                     $result, $this->connection);
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
?>