Subversion Repositories Applications.bazar

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
468 mathias 1
<?php
2
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
3
// +----------------------------------------------------------------------+
4
// | PHP Version 4                                                        |
5
// +----------------------------------------------------------------------+
6
// | Copyright (c) 1997-2004 The PHP Group                                |
7
// +----------------------------------------------------------------------+
8
// | This source file is subject to version 2.02 of the PHP license,      |
9
// | that is bundled with this package in the file LICENSE, and is        |
10
// | available at through the world-wide-web at                           |
11
// | http://www.php.net/license/2_02.txt.                                 |
12
// | If you did not receive a copy of the PHP license and are unable to   |
13
// | obtain it through the world-wide-web, please send a note to          |
14
// | license@php.net so we can mail you a copy immediately.               |
15
// +----------------------------------------------------------------------+
16
// | Author: Stig Bakken <ssb@php.net>                                    |
17
// | Maintainer: Daniel Convissor <danielc@php.net>                       |
18
// +----------------------------------------------------------------------+
19
//
20
// $Id$
21
 
22
 
23
// XXX legend:
24
//
25
// XXX ERRORMSG: The error message from the mysql function should
26
//               be registered here.
27
//
28
// TODO/wishlist:
29
// longReadlen
30
// binmode
31
 
32
 
33
require_once 'DB/common.php';
34
 
35
/**
36
 * Database independent query interface definition for PHP's MySQL
37
 * extension.
38
 *
39
 * This is for MySQL versions 4.0 and below.
40
 *
41
 * @package  DB
42
 * @version  $Id$
43
 * @category Database
44
 * @author   Stig Bakken <ssb@php.net>
45
 */
46
class DB_mysql extends DB_common
47
{
48
    // {{{ properties
49
 
50
    var $connection;
51
    var $phptype, $dbsyntax;
52
    var $prepare_tokens = array();
53
    var $prepare_types = array();
54
    var $num_rows = array();
55
    var $transaction_opcount = 0;
56
    var $autocommit = true;
57
    var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */
58
    var $_db = false;
59
 
60
    // }}}
61
    // {{{ constructor
62
 
63
    /**
64
     * DB_mysql constructor.
65
     *
66
     * @access public
67
     */
68
    function DB_mysql()
69
    {
70
        $this->DB_common();
71
        $this->phptype = 'mysql';
72
        $this->dbsyntax = 'mysql';
73
        $this->features = array(
74
            'prepare' => false,
75
            'pconnect' => true,
76
            'transactions' => true,
77
            'limit' => 'alter'
78
        );
79
        $this->errorcode_map = array(
80
            1004 => DB_ERROR_CANNOT_CREATE,
81
            1005 => DB_ERROR_CANNOT_CREATE,
82
            1006 => DB_ERROR_CANNOT_CREATE,
83
            1007 => DB_ERROR_ALREADY_EXISTS,
84
            1008 => DB_ERROR_CANNOT_DROP,
85
            1022 => DB_ERROR_ALREADY_EXISTS,
86
            1046 => DB_ERROR_NODBSELECTED,
87
            1048 => DB_ERROR_CONSTRAINT,
88
            1050 => DB_ERROR_ALREADY_EXISTS,
89
            1051 => DB_ERROR_NOSUCHTABLE,
90
            1054 => DB_ERROR_NOSUCHFIELD,
91
            1062 => DB_ERROR_ALREADY_EXISTS,
92
            1064 => DB_ERROR_SYNTAX,
93
            1100 => DB_ERROR_NOT_LOCKED,
94
            1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
95
            1146 => DB_ERROR_NOSUCHTABLE,
96
            1216 => DB_ERROR_CONSTRAINT,
97
            1217 => DB_ERROR_CONSTRAINT,
98
        );
99
    }
100
 
101
    // }}}
102
    // {{{ connect()
103
 
104
    /**
105
     * Connect to a database and log in as the specified user.
106
     *
107
     * @param $dsn the data source name (see DB::parseDSN for syntax)
108
     * @param $persistent (optional) whether the connection should
109
     *        be persistent
110
     * @access public
111
     * @return int DB_OK on success, a DB error on failure
112
     */
113
    function connect($dsninfo, $persistent = false)
114
    {
115
        if (!DB::assertExtension('mysql')) {
116
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
117
        }
118
        $this->dsn = $dsninfo;
119
        if ($dsninfo['protocol'] && $dsninfo['protocol'] == 'unix') {
120
            $dbhost = ':' . $dsninfo['socket'];
121
        } else {
122
            $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
123
            if ($dsninfo['port']) {
124
                $dbhost .= ':' . $dsninfo['port'];
125
            }
126
        }
127
 
128
        $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
129
 
130
        if ($dbhost && $dsninfo['username'] && isset($dsninfo['password'])) {
131
            $conn = @$connect_function($dbhost, $dsninfo['username'],
132
                                       $dsninfo['password']);
133
        } elseif ($dbhost && $dsninfo['username']) {
134
            $conn = @$connect_function($dbhost, $dsninfo['username']);
135
        } elseif ($dbhost) {
136
            $conn = @$connect_function($dbhost);
137
        } else {
138
            $conn = false;
139
        }
140
        if (!$conn) {
141
            if (($err = @mysql_error()) != '') {
142
                return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
143
                                         null, $err);
144
            } elseif (empty($php_errormsg)) {
145
                return $this->raiseError(DB_ERROR_CONNECT_FAILED);
146
            } else {
147
                return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
148
                                         null, $php_errormsg);
149
            }
150
        }
151
 
152
        if ($dsninfo['database']) {
153
            if (!@mysql_select_db($dsninfo['database'], $conn)) {
154
               switch(mysql_errno($conn)) {
155
                        case 1049:
156
                            return $this->raiseError(DB_ERROR_NOSUCHDB, null, null,
157
                                                     null, @mysql_error($conn));
158
                        case 1044:
159
                             return $this->raiseError(DB_ERROR_ACCESS_VIOLATION, null, null,
160
                                                      null, @mysql_error($conn));
161
                        default:
162
                            return $this->raiseError(DB_ERROR, null, null,
163
                                                     null, @mysql_error($conn));
164
                    }
165
            }
166
            // fix to allow calls to different databases in the same script
167
            $this->_db = $dsninfo['database'];
168
        }
169
 
170
        $this->connection = $conn;
171
        return DB_OK;
172
    }
173
 
174
    // }}}
175
    // {{{ disconnect()
176
 
177
    /**
178
     * Log out and disconnect from the database.
179
     *
180
     * @access public
181
     *
182
     * @return bool true on success, false if not connected.
183
     */
184
    function disconnect()
185
    {
186
        $ret = @mysql_close($this->connection);
187
        $this->connection = null;
188
        return $ret;
189
    }
190
 
191
    // }}}
192
    // {{{ simpleQuery()
193
 
194
    /**
195
     * Send a query to MySQL and return the results as a MySQL resource
196
     * identifier.
197
     *
198
     * @param the SQL query
199
     *
200
     * @access public
201
     *
202
     * @return mixed returns a valid MySQL result for successful SELECT
203
     * queries, DB_OK for other successful queries.  A DB error is
204
     * returned on failure.
205
     */
206
    function simpleQuery($query)
207
    {
208
        $ismanip = DB::isManip($query);
209
        $this->last_query = $query;
210
        $query = $this->modifyQuery($query);
211
        if ($this->_db) {
212
            if (!@mysql_select_db($this->_db, $this->connection)) {
213
                return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
214
            }
215
        }
216
        if (!$this->autocommit && $ismanip) {
217
            if ($this->transaction_opcount == 0) {
218
                $result = @mysql_query('SET AUTOCOMMIT=0', $this->connection);
219
                $result = @mysql_query('BEGIN', $this->connection);
220
                if (!$result) {
221
                    return $this->mysqlRaiseError();
222
                }
223
            }
224
            $this->transaction_opcount++;
225
        }
226
        $result = @mysql_query($query, $this->connection);
227
        if (!$result) {
228
            return $this->mysqlRaiseError();
229
        }
230
        if (is_resource($result)) {
231
            $numrows = $this->numrows($result);
232
            if (is_object($numrows)) {
233
                return $numrows;
234
            }
235
            $this->num_rows[(int)$result] = $numrows;
236
            return $result;
237
        }
238
        return DB_OK;
239
    }
240
 
241
    // }}}
242
    // {{{ nextResult()
243
 
244
    /**
245
     * Move the internal mysql result pointer to the next available result
246
     *
247
     * This method has not been implemented yet.
248
     *
249
     * @param a valid sql result resource
250
     *
251
     * @access public
252
     *
253
     * @return false
254
     */
255
    function nextResult($result)
256
    {
257
        return false;
258
    }
259
 
260
    // }}}
261
    // {{{ fetchInto()
262
 
263
    /**
264
     * Fetch a row and insert the data into an existing array.
265
     *
266
     * Formating of the array and the data therein are configurable.
267
     * See DB_result::fetchInto() for more information.
268
     *
269
     * @param resource $result    query result identifier
270
     * @param array    $arr       (reference) array where data from the row
271
     *                            should be placed
272
     * @param int      $fetchmode how the resulting array should be indexed
273
     * @param int      $rownum    the row number to fetch
274
     *
275
     * @return mixed DB_OK on success, null when end of result set is
276
     *               reached or on failure
277
     *
278
     * @see DB_result::fetchInto()
279
     * @access private
280
     */
281
    function fetchInto($result, &$arr, $fetchmode, $rownum=null)
282
    {
283
        if ($rownum !== null) {
284
            if (!@mysql_data_seek($result, $rownum)) {
285
                return null;
286
            }
287
        }
288
        if ($fetchmode & DB_FETCHMODE_ASSOC) {
289
            $arr = @mysql_fetch_array($result, MYSQL_ASSOC);
290
            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
291
                $arr = array_change_key_case($arr, CASE_LOWER);
292
            }
293
        } else {
294
            $arr = @mysql_fetch_row($result);
295
        }
296
        if (!$arr) {
297
            // See: http://bugs.php.net/bug.php?id=22328
298
            // for why we can't check errors on fetching
299
            return null;
300
            /*
301
            $errno = @mysql_errno($this->connection);
302
            if (!$errno) {
303
                return null;
304
            }
305
            return $this->mysqlRaiseError($errno);
306
            */
307
        }
308
        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
309
            /*
310
             * Even though this DBMS already trims output, we do this because
311
             * a field might have intentional whitespace at the end that
312
             * gets removed by DB_PORTABILITY_RTRIM under another driver.
313
             */
314
            $this->_rtrimArrayValues($arr);
315
        }
316
        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
317
            $this->_convertNullArrayValuesToEmpty($arr);
318
        }
319
        return DB_OK;
320
    }
321
 
322
    // }}}
323
    // {{{ freeResult()
324
 
325
    /**
326
     * Free the internal resources associated with $result.
327
     *
328
     * @param $result MySQL result identifier
329
     *
330
     * @access public
331
     *
332
     * @return bool true on success, false if $result is invalid
333
     */
334
    function freeResult($result)
335
    {
336
        unset($this->num_rows[(int)$result]);
337
        return @mysql_free_result($result);
338
    }
339
 
340
    // }}}
341
    // {{{ numCols()
342
 
343
    /**
344
     * Get the number of columns in a result set.
345
     *
346
     * @param $result MySQL result identifier
347
     *
348
     * @access public
349
     *
350
     * @return int the number of columns per row in $result
351
     */
352
    function numCols($result)
353
    {
354
        $cols = @mysql_num_fields($result);
355
 
356
        if (!$cols) {
357
            return $this->mysqlRaiseError();
358
        }
359
 
360
        return $cols;
361
    }
362
 
363
    // }}}
364
    // {{{ numRows()
365
 
366
    /**
367
     * Get the number of rows in a result set.
368
     *
369
     * @param $result MySQL result identifier
370
     *
371
     * @access public
372
     *
373
     * @return int the number of rows in $result
374
     */
375
    function numRows($result)
376
    {
377
        $rows = @mysql_num_rows($result);
378
        if ($rows === null) {
379
            return $this->mysqlRaiseError();
380
        }
381
        return $rows;
382
    }
383
 
384
    // }}}
385
    // {{{ autoCommit()
386
 
387
    /**
388
     * Enable/disable automatic commits
389
     */
390
    function autoCommit($onoff = false)
391
    {
392
        // XXX if $this->transaction_opcount > 0, we should probably
393
        // issue a warning here.
394
        $this->autocommit = $onoff ? true : false;
395
        return DB_OK;
396
    }
397
 
398
    // }}}
399
    // {{{ commit()
400
 
401
    /**
402
     * Commit the current transaction.
403
     */
404
    function commit()
405
    {
406
        if ($this->transaction_opcount > 0) {
407
            if ($this->_db) {
408
                if (!@mysql_select_db($this->_db, $this->connection)) {
409
                    return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
410
                }
411
            }
412
            $result = @mysql_query('COMMIT', $this->connection);
413
            $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
414
            $this->transaction_opcount = 0;
415
            if (!$result) {
416
                return $this->mysqlRaiseError();
417
            }
418
        }
419
        return DB_OK;
420
    }
421
 
422
    // }}}
423
    // {{{ rollback()
424
 
425
    /**
426
     * Roll back (undo) the current transaction.
427
     */
428
    function rollback()
429
    {
430
        if ($this->transaction_opcount > 0) {
431
            if ($this->_db) {
432
                if (!@mysql_select_db($this->_db, $this->connection)) {
433
                    return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
434
                }
435
            }
436
            $result = @mysql_query('ROLLBACK', $this->connection);
437
            $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
438
            $this->transaction_opcount = 0;
439
            if (!$result) {
440
                return $this->mysqlRaiseError();
441
            }
442
        }
443
        return DB_OK;
444
    }
445
 
446
    // }}}
447
    // {{{ affectedRows()
448
 
449
    /**
450
     * Gets the number of rows affected by the data manipulation
451
     * query.  For other queries, this function returns 0.
452
     *
453
     * @return number of rows affected by the last query
454
     */
455
    function affectedRows()
456
    {
457
        if (DB::isManip($this->last_query)) {
458
            return @mysql_affected_rows($this->connection);
459
        } else {
460
            return 0;
461
        }
462
     }
463
 
464
    // }}}
465
    // {{{ errorNative()
466
 
467
    /**
468
     * Get the native error code of the last error (if any) that
469
     * occured on the current connection.
470
     *
471
     * @access public
472
     *
473
     * @return int native MySQL error code
474
     */
475
    function errorNative()
476
    {
477
        return @mysql_errno($this->connection);
478
    }
479
 
480
    // }}}
481
    // {{{ nextId()
482
 
483
    /**
484
     * Returns the next free id in a sequence
485
     *
486
     * @param string  $seq_name  name of the sequence
487
     * @param boolean $ondemand  when true, the seqence is automatically
488
     *                           created if it does not exist
489
     *
490
     * @return int  the next id number in the sequence.  DB_Error if problem.
491
     *
492
     * @internal
493
     * @see DB_common::nextID()
494
     * @access public
495
     */
496
    function nextId($seq_name, $ondemand = true)
497
    {
498
        $seqname = $this->getSequenceName($seq_name);
499
        do {
500
            $repeat = 0;
501
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
502
            $result = $this->query("UPDATE ${seqname} ".
503
                                   'SET id=LAST_INSERT_ID(id+1)');
504
            $this->popErrorHandling();
505
            if ($result === DB_OK) {
506
                /** COMMON CASE **/
507
                $id = @mysql_insert_id($this->connection);
508
                if ($id != 0) {
509
                    return $id;
510
                }
511
                /** EMPTY SEQ TABLE **/
512
                // Sequence table must be empty for some reason, so fill it and return 1
513
                // Obtain a user-level lock
514
                $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
515
                if (DB::isError($result)) {
516
                    return $this->raiseError($result);
517
                }
518
                if ($result == 0) {
519
                    // Failed to get the lock, bail with a DB_ERROR_NOT_LOCKED error
520
                    return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
521
                }
522
 
523
                // add the default value
524
                $result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)");
525
                if (DB::isError($result)) {
526
                    return $this->raiseError($result);
527
                }
528
 
529
                // Release the lock
530
                $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
531
                if (DB::isError($result)) {
532
                    return $this->raiseError($result);
533
                }
534
                // We know what the result will be, so no need to try again
535
                return 1;
536
 
537
            /** ONDEMAND TABLE CREATION **/
538
            } elseif ($ondemand && DB::isError($result) &&
539
                $result->getCode() == DB_ERROR_NOSUCHTABLE)
540
            {
541
                $result = $this->createSequence($seq_name);
542
                if (DB::isError($result)) {
543
                    return $this->raiseError($result);
544
                } else {
545
                    $repeat = 1;
546
                }
547
 
548
            /** BACKWARDS COMPAT **/
549
            } elseif (DB::isError($result) &&
550
                      $result->getCode() == DB_ERROR_ALREADY_EXISTS)
551
            {
552
                // see _BCsequence() comment
553
                $result = $this->_BCsequence($seqname);
554
                if (DB::isError($result)) {
555
                    return $this->raiseError($result);
556
                }
557
                $repeat = 1;
558
            }
559
        } while ($repeat);
560
 
561
        return $this->raiseError($result);
562
    }
563
 
564
    // }}}
565
    // {{{ createSequence()
566
 
567
    /**
568
     * Creates a new sequence
569
     *
570
     * @param string $seq_name  name of the new sequence
571
     *
572
     * @return int  DB_OK on success.  A DB_Error object is returned if
573
     *              problems arise.
574
     *
575
     * @internal
576
     * @see DB_common::createSequence()
577
     * @access public
578
     */
579
    function createSequence($seq_name)
580
    {
581
        $seqname = $this->getSequenceName($seq_name);
582
        $res = $this->query("CREATE TABLE ${seqname} ".
583
                            '(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'.
584
                            ' PRIMARY KEY(id))');
585
        if (DB::isError($res)) {
586
            return $res;
587
        }
588
        // insert yields value 1, nextId call will generate ID 2
589
        $res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
590
        if (DB::isError($res)) {
591
            return $res;
592
        }
593
        // so reset to zero
594
        return $this->query("UPDATE ${seqname} SET id = 0;");
595
    }
596
 
597
    // }}}
598
    // {{{ dropSequence()
599
 
600
    /**
601
     * Deletes a sequence
602
     *
603
     * @param string $seq_name  name of the sequence to be deleted
604
     *
605
     * @return int  DB_OK on success.  DB_Error if problems.
606
     *
607
     * @internal
608
     * @see DB_common::dropSequence()
609
     * @access public
610
     */
611
    function dropSequence($seq_name)
612
    {
613
        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
614
    }
615
 
616
    // }}}
617
    // {{{ _BCsequence()
618
 
619
    /**
620
     * Backwards compatibility with old sequence emulation implementation
621
     * (clean up the dupes)
622
     *
623
     * @param string $seqname The sequence name to clean up
624
     * @return mixed DB_Error or true
625
     */
626
    function _BCsequence($seqname)
627
    {
628
        // Obtain a user-level lock... this will release any previous
629
        // application locks, but unlike LOCK TABLES, it does not abort
630
        // the current transaction and is much less frequently used.
631
        $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
632
        if (DB::isError($result)) {
633
            return $result;
634
        }
635
        if ($result == 0) {
636
            // Failed to get the lock, can't do the conversion, bail
637
            // with a DB_ERROR_NOT_LOCKED error
638
            return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
639
        }
640
 
641
        $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
642
        if (DB::isError($highest_id)) {
643
            return $highest_id;
644
        }
645
        // This should kill all rows except the highest
646
        // We should probably do something if $highest_id isn't
647
        // numeric, but I'm at a loss as how to handle that...
648
        $result = $this->query("DELETE FROM ${seqname} WHERE id <> $highest_id");
649
        if (DB::isError($result)) {
650
            return $result;
651
        }
652
 
653
        // If another thread has been waiting for this lock,
654
        // it will go thru the above procedure, but will have no
655
        // real effect
656
        $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
657
        if (DB::isError($result)) {
658
            return $result;
659
        }
660
        return true;
661
    }
662
 
663
    // }}}
664
    // {{{ quoteIdentifier()
665
 
666
    /**
667
     * Quote a string so it can be safely used as a table or column name
668
     *
669
     * Quoting style depends on which database driver is being used.
670
     *
671
     * MySQL can't handle the backtick character (<kbd>`</kbd>) in
672
     * table or column names.
673
     *
674
     * @param string $str  identifier name to be quoted
675
     *
676
     * @return string  quoted identifier string
677
     *
678
     * @since 1.6.0
679
     * @access public
680
     * @internal
681
     */
682
    function quoteIdentifier($str)
683
    {
684
        return '`' . $str . '`';
685
    }
686
 
687
    // }}}
688
    // {{{ quote()
689
 
690
    /**
691
     * @deprecated  Deprecated in release 1.6.0
692
     * @internal
693
     */
694
    function quote($str) {
695
        return $this->quoteSmart($str);
696
    }
697
 
698
    // }}}
699
    // {{{ escapeSimple()
700
 
701
    /**
702
     * Escape a string according to the current DBMS's standards
703
     *
704
     * @param string $str  the string to be escaped
705
     *
706
     * @return string  the escaped string
707
     *
708
     * @internal
709
     */
710
    function escapeSimple($str) {
711
        if (function_exists('mysql_real_escape_string')) {
712
            return @mysql_real_escape_string($str, $this->connection);
713
        } else {
714
            return @mysql_escape_string($str);
715
        }
716
    }
717
 
718
    // }}}
719
    // {{{ modifyQuery()
720
 
721
    function modifyQuery($query)
722
    {
723
        if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
724
            // "DELETE FROM table" gives 0 affected rows in MySQL.
725
            // This little hack lets you know how many rows were deleted.
726
            if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
727
                $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
728
                                      'DELETE FROM \1 WHERE 1=1', $query);
729
            }
730
        }
731
        return $query;
732
    }
733
 
734
    // }}}
735
    // {{{ modifyLimitQuery()
736
 
737
    function modifyLimitQuery($query, $from, $count, $params = array())
738
    {
739
        if (DB::isManip($query)) {
740
            return $query . " LIMIT $count";
741
        } else {
742
            return $query . " LIMIT $from, $count";
743
        }
744
    }
745
 
746
    // }}}
747
    // {{{ mysqlRaiseError()
748
 
749
    /**
750
     * Gather information about an error, then use that info to create a
751
     * DB error object and finally return that object.
752
     *
753
     * @param  integer  $errno  PEAR error number (usually a DB constant) if
754
     *                          manually raising an error
755
     * @return object  DB error object
756
     * @see DB_common::errorCode()
757
     * @see DB_common::raiseError()
758
     */
759
    function mysqlRaiseError($errno = null)
760
    {
761
        if ($errno === null) {
762
            if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
763
                $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
764
                $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
765
                $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
766
            } else {
767
                // Doing this in case mode changes during runtime.
768
                $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
769
                $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
770
                $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
771
            }
772
            $errno = $this->errorCode(mysql_errno($this->connection));
773
        }
774
        return $this->raiseError($errno, null, null, null,
775
                                 @mysql_errno($this->connection) . ' ** ' .
776
                                 @mysql_error($this->connection));
777
    }
778
 
779
    // }}}
780
    // {{{ tableInfo()
781
 
782
    /**
783
     * Returns information about a table or a result set.
784
     *
785
     * @param object|string  $result  DB_result object from a query or a
786
     *                                string containing the name of a table
787
     * @param int            $mode    a valid tableInfo mode
788
     * @return array  an associative array with the information requested
789
     *                or an error object if something is wrong
790
     * @access public
791
     * @internal
792
     * @see DB_common::tableInfo()
793
     */
794
    function tableInfo($result, $mode = null) {
795
        if (isset($result->result)) {
796
            /*
797
             * Probably received a result object.
798
             * Extract the result resource identifier.
799
             */
800
            $id = $result->result;
801
            $got_string = false;
802
        } elseif (is_string($result)) {
803
            /*
804
             * Probably received a table name.
805
             * Create a result resource identifier.
806
             */
807
            $id = @mysql_list_fields($this->dsn['database'],
808
                                     $result, $this->connection);
809
            $got_string = true;
810
        } else {
811
            /*
812
             * Probably received a result resource identifier.
813
             * Copy it.
814
             * Deprecated.  Here for compatibility only.
815
             */
816
            $id = $result;
817
            $got_string = false;
818
        }
819
 
820
        if (!is_resource($id)) {
821
            return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA);
822
        }
823
 
824
        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
825
            $case_func = 'strtolower';
826
        } else {
827
            $case_func = 'strval';
828
        }
829
 
830
        $count = @mysql_num_fields($id);
831
 
832
        // made this IF due to performance (one if is faster than $count if's)
833
        if (!$mode) {
834
            for ($i=0; $i<$count; $i++) {
835
                $res[$i]['table'] = $case_func(@mysql_field_table($id, $i));
836
                $res[$i]['name']  = $case_func(@mysql_field_name($id, $i));
837
                $res[$i]['type']  = @mysql_field_type($id, $i);
838
                $res[$i]['len']   = @mysql_field_len($id, $i);
839
                $res[$i]['flags'] = @mysql_field_flags($id, $i);
840
            }
841
        } else { // full
842
            $res['num_fields']= $count;
843
 
844
            for ($i=0; $i<$count; $i++) {
845
                $res[$i]['table'] = $case_func(@mysql_field_table($id, $i));
846
                $res[$i]['name']  = $case_func(@mysql_field_name($id, $i));
847
                $res[$i]['type']  = @mysql_field_type($id, $i);
848
                $res[$i]['len']   = @mysql_field_len($id, $i);
849
                $res[$i]['flags'] = @mysql_field_flags($id, $i);
850
 
851
                if ($mode & DB_TABLEINFO_ORDER) {
852
                    $res['order'][$res[$i]['name']] = $i;
853
                }
854
                if ($mode & DB_TABLEINFO_ORDERTABLE) {
855
                    $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
856
                }
857
            }
858
        }
859
 
860
        // free the result only if we were called on a table
861
        if ($got_string) {
862
            @mysql_free_result($id);
863
        }
864
        return $res;
865
    }
866
 
867
    // }}}
868
    // {{{ getSpecialQuery()
869
 
870
    /**
871
     * Returns the query needed to get some backend info
872
     * @param string $type What kind of info you want to retrieve
873
     * @return string The SQL query string
874
     */
875
    function getSpecialQuery($type)
876
    {
877
        switch ($type) {
878
            case 'tables':
879
                return 'SHOW TABLES';
880
            case 'views':
881
                return DB_ERROR_NOT_CAPABLE;
882
            case 'users':
883
                $sql = 'select distinct User from user';
884
                if ($this->dsn['database'] != 'mysql') {
885
                    $dsn = $this->dsn;
886
                    $dsn['database'] = 'mysql';
887
                    if (DB::isError($db = DB::connect($dsn))) {
888
                        return $db;
889
                    }
890
                    $sql = $db->getCol($sql);
891
                    $db->disconnect();
892
                    // XXX Fixme the mysql driver should take care of this
893
                    if (!@mysql_select_db($this->dsn['database'], $this->connection)) {
894
                        return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
895
                    }
896
                }
897
                return $sql;
898
            case 'databases':
899
                return 'SHOW DATABASES';
900
            default:
901
                return null;
902
        }
903
    }
904
 
905
    // }}}
906
 
907
}
908
 
909
/*
910
 * Local variables:
911
 * tab-width: 4
912
 * c-basic-offset: 4
913
 * End:
914
 */
915
 
916
?>