Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
// vim: set et ts=4 sw=4 fdm=marker:
3
// +----------------------------------------------------------------------+
4
// | PHP Version 4                                                        |
5
// +----------------------------------------------------------------------+
6
// | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
7
// | Stig. S. Bakken, Lukas Smith                                         |
8
// | All rights reserved.                                                 |
9
// +----------------------------------------------------------------------+
10
// | MDB is a merge of PEAR DB and Metabases that provides a unified DB   |
11
// | API as well as database abstraction for PHP applications.            |
12
// | This LICENSE is in the BSD license style.                            |
13
// |                                                                      |
14
// | Redistribution and use in source and binary forms, with or without   |
15
// | modification, are permitted provided that the following conditions   |
16
// | are met:                                                             |
17
// |                                                                      |
18
// | Redistributions of source code must retain the above copyright       |
19
// | notice, this list of conditions and the following disclaimer.        |
20
// |                                                                      |
21
// | Redistributions in binary form must reproduce the above copyright    |
22
// | notice, this list of conditions and the following disclaimer in the  |
23
// | documentation and/or other materials provided with the distribution. |
24
// |                                                                      |
25
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
26
// | Lukas Smith nor the names of his contributors may be used to endorse |
27
// | or promote products derived from this software without specific prior|
28
// | written permission.                                                  |
29
// |                                                                      |
30
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
31
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
32
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
33
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
34
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
35
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
36
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
37
// |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
38
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
39
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
40
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
41
// | POSSIBILITY OF SUCH DAMAGE.                                          |
42
// +----------------------------------------------------------------------+
43
// | Author: Lukas Smith <smith@backendmedia.com>                         |
44
// +----------------------------------------------------------------------+
45
//
46
// $Id: mysql.php,v 1.80.4.18 2004/04/22 11:31:56 lsmith Exp $
47
//
48
 
49
require_once('MDB/Common.php');
50
 
51
/**
52
 * MDB MySQL driver
53
 *
54
 * Notes:
55
 * - The decimal type fields are emulated with integer fields.
56
 *
57
 * @package MDB
58
 * @category Database
59
 * @author  Lukas Smith <smith@backendmedia.com>
60
 */
61
class MDB_mysql extends MDB_Common
62
{
63
    // {{{ properties
64
 
65
    var $connection = 0;
66
    var $connected_host;
67
    var $connected_user;
68
    var $connected_password;
69
    var $connected_port;
70
    var $opened_persistent = '';
71
 
72
    var $escape_quotes = "\\";
73
    var $decimal_factor = 1.0;
74
 
75
    var $highest_fetched_row = array();
76
    var $columns = array();
77
 
78
    // MySQL specific class variable
79
    var $default_table_type = '';
80
    var $fixed_float = 0;
81
    var $dummy_primary_key = 'dummy_primary_key';
82
 
83
    // }}}
84
    // {{{ constructor
85
 
86
    /**
87
    * Constructor
88
    */
89
    function MDB_mysql()
90
    {
91
        $this->MDB_Common();
92
        $this->phptype = 'mysql';
93
        $this->dbsyntax = 'mysql';
94
 
95
        $this->supported['Sequences'] = 1;
96
        $this->supported['Indexes'] = 1;
97
        $this->supported['AffectedRows'] = 1;
98
        $this->supported['Summaryfunctions'] = 1;
99
        $this->supported['OrderByText'] = 1;
100
        $this->supported['CurrId'] = 1;
101
        $this->supported['SelectRowRanges'] = 1;
102
        $this->supported['LOBs'] = 1;
103
        $this->supported['Replace'] = 1;
104
        $this->supported['SubSelects'] = 0;
105
        $this->supported['Transactions'] = 0;
106
 
107
        $this->decimal_factor = pow(10.0, $this->decimal_places);
108
 
109
        $this->options['DefaultTableType'] = FALSE;
110
        $this->options['fixed_float'] = FALSE;
111
 
112
        $this->errorcode_map = array(
113
            1004 => MDB_ERROR_CANNOT_CREATE,
114
            1005 => MDB_ERROR_CANNOT_CREATE,
115
            1006 => MDB_ERROR_CANNOT_CREATE,
116
            1007 => MDB_ERROR_ALREADY_EXISTS,
117
            1008 => MDB_ERROR_CANNOT_DROP,
118
            1022 => MDB_ERROR_ALREADY_EXISTS,
119
            1046 => MDB_ERROR_NODBSELECTED,
120
            1050 => MDB_ERROR_ALREADY_EXISTS,
121
            1051 => MDB_ERROR_NOSUCHTABLE,
122
            1054 => MDB_ERROR_NOSUCHFIELD,
123
            1062 => MDB_ERROR_ALREADY_EXISTS,
124
            1064 => MDB_ERROR_SYNTAX,
125
            1100 => MDB_ERROR_NOT_LOCKED,
126
            1136 => MDB_ERROR_VALUE_COUNT_ON_ROW,
127
            1146 => MDB_ERROR_NOSUCHTABLE,
128
            1048 => MDB_ERROR_CONSTRAINT,
129
            1213 => MDB_ERROR_DEADLOCK,
130
            1216 => MDB_ERROR_CONSTRAINT,
131
        );
132
    }
133
 
134
    // }}}
135
    // {{{ errorNative()
136
 
137
    /**
138
     * Get the native error code of the last error (if any) that
139
     * occured on the current connection.
140
     *
141
     * @access public
142
     *
143
     * @return int native MySQL error code
144
     */
145
    function errorNative()
146
    {
147
        return(@mysql_errno($this->connection));
148
    }
149
 
150
    // }}}
151
    // {{{ mysqlRaiseError()
152
 
153
    /**
154
     * This method is used to communicate an error and invoke error
155
     * callbacks etc.  Basically a wrapper for MDB::raiseError
156
     * that checks for native error msgs.
157
     *
158
     * @param integer $errno error code
159
     * @param string  $message userinfo message
160
     * @return object a PEAR error object
161
     * @access public
162
     * @see PEAR_Error
163
     */
164
    function mysqlRaiseError($errno = NULL, $message = NULL)
165
    {
166
        if ($errno == NULL) {
167
            if ($this->connection) {
168
                $errno = @mysql_errno($this->connection);
169
            } else {
170
                $errno = @mysql_errno();
171
            }
172
        }
173
        if ($this->connection) {
174
            $error = @mysql_errno($this->connection);
175
        } else {
176
            $error = @mysql_error();
177
        }
178
        return($this->raiseError($this->errorCode($errno), NULL, NULL,
179
            $message, $error));
180
    }
181
 
182
    // }}}
183
    // {{{ quoteIdentifier()
184
 
185
    /**
186
     * Quote a string so it can be safely used as a table or column name
187
     *
188
     * Quoting style depends on which database driver is being used.
189
     *
190
     * MySQL can't handle the backtick character (<kbd>`</kbd>) in
191
     * table or column names.
192
     *
193
     * @param string $str  identifier name to be quoted
194
     *
195
     * @return string  quoted identifier string
196
     *
197
     * @access public
198
     * @internal
199
     */
200
    function quoteIdentifier($str)
201
    {
202
        return '`' . $str . '`';
203
    }
204
 
205
    // }}}
206
    // {{{ autoCommit()
207
 
208
    /**
209
     * Define whether database changes done on the database be automatically
210
     * committed. This function may also implicitly start or end a transaction.
211
     *
212
     * @param boolean $auto_commit    flag that indicates whether the database
213
     *                                changes should be committed right after
214
     *                                executing every query statement. If this
215
     *                                argument is 0 a transaction implicitly
216
     *                                started. Otherwise, if a transaction is
217
     *                                in progress it is ended by committing any
218
     *                                database changes that were pending.
219
     *
220
     * @access public
221
     *
222
     * @return mixed MDB_OK on success, a MDB error on failure
223
     */
224
    function autoCommit($auto_commit)
225
    {
226
        $this->debug("AutoCommit: ".($auto_commit ? "On" : "Off"));
227
        if (!isset($this->supported['Transactions'])) {
228
            return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
229
                'Auto-commit transactions: transactions are not in use'));
230
        }
231
        if ($this->auto_commit == $auto_commit) {
232
            return(MDB_OK);
233
        }
234
        if ($this->connection) {
235
            if ($auto_commit) {
236
                $result = $this->query('COMMIT');
237
                if (MDB::isError($result)) {
238
                    return($result);
239
                }
240
                $result = $this->query('SET AUTOCOMMIT = 1');
241
                if (MDB::isError($result)) {
242
                    return($result);
243
                }
244
            } else {
245
                $result = $this->query('SET AUTOCOMMIT = 0');
246
                if (MDB::isError($result)) {
247
                    return($result);
248
                }
249
            }
250
        }
251
        $this->auto_commit = $auto_commit;
252
        $this->in_transaction = !$auto_commit;
253
        return(MDB_OK);
254
    }
255
 
256
    // }}}
257
    // {{{ commit()
258
 
259
    /**
260
     * Commit the database changes done during a transaction that is in
261
     * progress. This function may only be called when auto-committing is
262
     * disabled, otherwise it will fail. Therefore, a new transaction is
263
     * implicitly started after committing the pending changes.
264
     *
265
     * @access public
266
     *
267
     * @return mixed MDB_OK on success, a MDB error on failure
268
     */
269
    function commit()
270
    {
271
        $this->debug("Commit Transaction");
272
        if (!isset($this->supported['Transactions'])) {
273
            return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
274
                'Commit transactions: transactions are not in use'));
275
        }
276
        if ($this->auto_commit) {
277
            return($this->raiseError(MDB_ERROR, NULL, NULL,
278
            'Commit transactions: transaction changes are being auto commited'));
279
        }
280
        return($this->query('COMMIT'));
281
    }
282
 
283
    // }}}
284
    // {{{ rollback()
285
 
286
    /**
287
     * Cancel any database changes done during a transaction that is in
288
     * progress. This function may only be called when auto-committing is
289
     * disabled, otherwise it will fail. Therefore, a new transaction is
290
     * implicitly started after canceling the pending changes.
291
     *
292
     * @access public
293
     *
294
     * @return mixed MDB_OK on success, a MDB error on failure
295
     */
296
    function rollback()
297
    {
298
        $this->debug("Rollback Transaction");
299
        if (!isset($this->supported['Transactions'])) {
300
            return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
301
                'Rollback transactions: transactions are not in use'));
302
        }
303
        if ($this->auto_commit) {
304
            return($this->raiseError(MDB_ERROR, NULL, NULL,
305
                'Rollback transactions: transactions can not be rolled back when changes are auto commited'));
306
        }
307
        return($this->query('ROLLBACK'));
308
    }
309
 
310
    // }}}
311
    // {{{ connect()
312
 
313
    /**
314
     * Connect to the database
315
     *
316
     * @return TRUE on success, MDB_Error on failure
317
     **/
318
    function connect()
319
    {
320
        $port = (isset($this->port) ? $this->port : '');
321
        if($this->connection != 0) {
322
            if (!strcmp($this->connected_host, $this->host)
323
                && !strcmp($this->connected_user, $this->user)
324
                && !strcmp($this->connected_password, $this->password)
325
                && !strcmp($this->connected_port, $port)
326
                && $this->opened_persistent == $this->options['persistent'])
327
            {
328
                return(MDB_OK);
329
            }
330
            @mysql_close($this->connection);
331
            $this->connection = 0;
332
            $this->affected_rows = -1;
333
        }
334
 
335
        if(!PEAR::loadExtension($this->phptype)) {
336
            return(PEAR::raiseError(NULL, MDB_ERROR_NOT_FOUND,
337
                NULL, NULL, 'extension '.$this->phptype.' is not compiled into PHP',
338
                'MDB_Error', TRUE));
339
        }
340
        $UseTransactions = $this->getOption('UseTransactions');
341
        if(!MDB::isError($UseTransactions) && $UseTransactions) {
342
            $this->supported['Transactions'] = 1;
343
            $this->default_table_type = 'BDB';
344
        } else {
345
            $this->supported['Transactions'] = 0;
346
            $this->default_table_type = '';
347
        }
348
        $DefaultTableType = $this->getOption('DefaultTableType');
349
        if(!MDB::isError($DefaultTableType) && $DefaultTableType) {
350
            switch($this->default_table_type = strtoupper($DefaultTableType)) {
351
                case 'BERKELEYDB':
352
                    $this->default_table_type = 'BDB';
353
                case 'BDB':
354
                case 'INNODB':
355
                case 'GEMINI':
356
                    break;
357
                case 'HEAP':
358
                case 'ISAM':
359
                case 'MERGE':
360
                case 'MRG_MYISAM':
361
                case 'MYISAM':
362
                    if(isset($this->supported['Transactions'])) {
363
                        $this->warnings[] = $DefaultTableType
364
                            .' is not a transaction-safe default table type';
365
                    }
366
                    break;
367
                default:
368
                    $this->warnings[] = $DefaultTableType
369
                        .' is not a supported default table type';
370
            }
371
        }
372
 
373
        $this->fixed_float = 30;
374
        $function = ($this->options['persistent'] ? 'mysql_pconnect' : 'mysql_connect');
375
        if (!function_exists($function)) {
376
            return($this->raiseError(MDB_ERROR_UNSUPPORTED));
377
        }
378
 
379
        @ini_set('track_errors', TRUE);
380
        $this->connection = @$function(
381
            $this->host.(!strcmp($port,'') ? '' : ':'.$port),
382
            $this->user, $this->password);
383
        @ini_restore('track_errors');
384
        if ($this->connection <= 0) {
385
            return($this->raiseError(MDB_ERROR_CONNECT_FAILED, NULL, NULL,
386
                $php_errormsg));
387
        }
388
 
389
        if (isset($this->options['fixedfloat'])) {
390
            $this->fixed_float = $this->options['fixedfloat'];
391
        } else {
392
            if (($result = @mysql_query('SELECT VERSION()', $this->connection))) {
393
                $version = explode('.', @mysql_result($result,0,0));
394
                $major = intval($version[0]);
395
                $minor = intval($version[1]);
396
                $revision = intval($version[2]);
397
                if ($major > 3 || ($major == 3 && $minor >= 23
398
                    && ($minor > 23 || $revision >= 6)))
399
                {
400
                    $this->fixed_float = 0;
401
                }
402
                @mysql_free_result($result);
403
            }
404
        }
405
        if (isset($this->supported['Transactions']) && !$this->auto_commit) {
406
            if (!@mysql_query('SET AUTOCOMMIT = 0', $this->connection)) {
407
                @mysql_close($this->connection);
408
                $this->connection = 0;
409
                $this->affected_rows = -1;
410
                return($this->raiseError());
411
            }
412
            $this->in_transaction = TRUE;
413
        }
414
        $this->connected_host = $this->host;
415
        $this->connected_user = $this->user;
416
        $this->connected_password = $this->password;
417
        $this->connected_port = $port;
418
        $this->opened_persistent = $this->getoption('persistent');
419
        return(MDB_OK);
420
    }
421
 
422
    // }}}
423
    // {{{ _close()
424
    /**
425
     * all the RDBMS specific things needed close a DB connection
426
     *
427
     * @return boolean
428
     * @access private
429
     **/
430
    function _close()
431
    {
432
        if ($this->connection != 0) {
433
            if (isset($this->supported['Transactions']) && !$this->auto_commit) {
434
                $result = $this->autoCommit(TRUE);
435
            }
436
            @mysql_close($this->connection);
437
            $this->connection = 0;
438
            $this->affected_rows = -1;
439
 
440
            if (isset($result) && MDB::isError($result)) {
441
                return($result);
442
            }
443
            unset($GLOBALS['_MDB_databases'][$this->database]);
444
            return(TRUE);
445
        }
446
        return(FALSE);
447
    }
448
 
449
    // }}}
450
    // {{{ query()
451
 
452
    /**
453
     * Send a query to the database and return any results
454
     *
455
     * @access public
456
     *
457
     * @param string  $query  the SQL query
458
     * @param mixed   $types  array that contains the types of the columns in
459
     *                        the result set
460
     *
461
     * @return mixed a result handle or MDB_OK on success, a MDB error on failure
462
     */
463
    function query($query, $types = NULL)
464
    {
465
        $this->debug("Query: $query");
466
        $ismanip = MDB::isManip($query);
467
        $this->last_query = $query;
468
        $first = $this->first_selected_row;
469
        $limit = $this->selected_row_limit;
470
        $this->first_selected_row = $this->selected_row_limit = 0;
471
 
472
        $result = $this->connect();
473
        if (MDB::isError($result)) {
474
            return($result);
475
        }
476
        if($limit > 0) {
477
            if ($ismanip) {
478
                $query .= " LIMIT $limit";
479
            } else {
480
                $query .= " LIMIT $first,$limit";
481
            }
482
        }
483
        if ($this->database_name) {
484
            if(!@mysql_select_db($this->database_name, $this->connection)) {
485
                return($this->mysqlRaiseError());
486
            }
487
        }
488
        if ($result = @mysql_query($query, $this->connection)) {
489
            if ($ismanip) {
490
                $this->affected_rows = @mysql_affected_rows($this->connection);
491
                return(MDB_OK);
492
            } else {
493
                $result_value = intval($result);
494
                $this->highest_fetched_row[$result_value] = -1;
495
                if ($types != NULL) {
496
                    if (!is_array($types)) {
497
                        $types = array($types);
498
                    }
499
                    if (MDB::isError($err = $this->setResultTypes($result, $types))) {
500
                        $this->freeResult($result);
501
                        return($err);
502
                    }
503
                }
504
                return($result);
505
            }
506
        }
507
        return($this->mysqlRaiseError());
508
    }
509
 
510
    // }}}
511
    // {{{ subSelect()
512
 
513
    /**
514
     * simple subselect emulation for Mysql
515
     *
516
     * @access public
517
     *
518
     * @param string $query the SQL query for the subselect that may only
519
     *                      return a column
520
     * @param string $quote determines if the data needs to be quoted before
521
     *                      being returned
522
     *
523
     * @return string the query
524
     */
525
    function subSelect($query, $quote = FALSE)
526
    {
527
        if($this->supported['SubSelects'] == 1) {
528
            return($query);
529
        }
530
        $col = $this->queryCol($query);
531
        if (MDB::isError($col)) {
532
            return($col);
533
        }
534
        if(!is_array($col) || count($col) == 0) {
535
            return 'NULL';
536
        }
537
        if($quote) {
538
            for($i = 0, $j = count($col); $i < $j; ++$i) {
539
                $col[$i] = $this->getTextValue($col[$i]);
540
            }
541
        }
542
        return(implode(', ', $col));
543
    }
544
 
545
    // }}}
546
    // {{{ replace()
547
 
548
    /**
549
     * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
550
     * query, except that if there is already a row in the table with the same
551
     * key field values, the REPLACE query just updates its values instead of
552
     * inserting a new row.
553
     *
554
     * The REPLACE type of query does not make part of the SQL standards. Since
555
     * practically only MySQL implements it natively, this type of query is
556
     * emulated through this method for other DBMS using standard types of
557
     * queries inside a transaction to assure the atomicity of the operation.
558
     *
559
     * @access public
560
     *
561
     * @param string $table name of the table on which the REPLACE query will
562
     *  be executed.
563
     * @param array $fields associative array that describes the fields and the
564
     *  values that will be inserted or updated in the specified table. The
565
     *  indexes of the array are the names of all the fields of the table. The
566
     *  values of the array are also associative arrays that describe the
567
     *  values and other properties of the table fields.
568
     *
569
     *  Here follows a list of field properties that need to be specified:
570
     *
571
     *    Value:
572
     *          Value to be assigned to the specified field. This value may be
573
     *          of specified in database independent type format as this
574
     *          function can perform the necessary datatype conversions.
575
     *
576
     *    Default:
577
     *          this property is required unless the Null property
578
     *          is set to 1.
579
     *
580
     *    Type
581
     *          Name of the type of the field. Currently, all types Metabase
582
     *          are supported except for clob and blob.
583
     *
584
     *    Default: no type conversion
585
     *
586
     *    Null
587
     *          Boolean property that indicates that the value for this field
588
     *          should be set to NULL.
589
     *
590
     *          The default value for fields missing in INSERT queries may be
591
     *          specified the definition of a table. Often, the default value
592
     *          is already NULL, but since the REPLACE may be emulated using
593
     *          an UPDATE query, make sure that all fields of the table are
594
     *          listed in this function argument array.
595
     *
596
     *    Default: 0
597
     *
598
     *    Key
599
     *          Boolean property that indicates that this field should be
600
     *          handled as a primary key or at least as part of the compound
601
     *          unique index of the table that will determine the row that will
602
     *          updated if it exists or inserted a new row otherwise.
603
     *
604
     *          This function will fail if no key field is specified or if the
605
     *          value of a key field is set to NULL because fields that are
606
     *          part of unique index they may not be NULL.
607
     *
608
     *    Default: 0
609
     *
610
     * @return mixed MDB_OK on success, a MDB error on failure
611
     */
612
    function replace($table, $fields)
613
    {
614
        $count = count($fields);
615
        for($keys = 0, $query = $values = '',reset($fields), $field = 0;
616
            $field<$count;
617
            next($fields), $field++)
618
        {
619
            $name = key($fields);
620
            if ($field > 0) {
621
                $query .= ',';
622
                $values .= ',';
623
            }
624
            $query .= $name;
625
            if (isset($fields[$name]['Null']) && $fields[$name]['Null']) {
626
                $value = 'NULL';
627
            } else {
628
                if(isset($fields[$name]['Type'])) {
629
                    switch ($fields[$name]['Type']) {
630
                        case 'text':
631
                            $value = $this->getTextValue($fields[$name]['Value']);
632
                            break;
633
                        case 'boolean':
634
                            $value = $this->getBooleanValue($fields[$name]['Value']);
635
                            break;
636
                        case 'integer':
637
                            $value = $this->getIntegerValue($fields[$name]['Value']);
638
                            break;
639
                        case 'decimal':
640
                            $value = $this->getDecimalValue($fields[$name]['Value']);
641
                            break;
642
                        case 'float':
643
                            $value = $this->getFloatValue($fields[$name]['Value']);
644
                            break;
645
                        case 'date':
646
                            $value = $this->getDateValue($fields[$name]['Value']);
647
                            break;
648
                        case 'time':
649
                            $value = $this->getTimeValue($fields[$name]['Value']);
650
                            break;
651
                        case 'timestamp':
652
                            $value = $this->getTimestampValue($fields[$name]['Value']);
653
                            break;
654
                        default:
655
                            return($this->raiseError(MDB_ERROR_CANNOT_REPLACE, NULL, NULL,
656
                                'no supported type for field "' . $name . '" specified'));
657
                    }
658
                } else {
659
                    $value = $fields[$name]['Value'];
660
                }
661
            }
662
            $values .= $value;
663
            if (isset($fields[$name]['Key']) && $fields[$name]['Key']) {
664
                if ($value === 'NULL') {
665
                    return($this->raiseError(MDB_ERROR_CANNOT_REPLACE, NULL, NULL,
666
                        $name.': key values may not be NULL'));
667
                }
668
                $keys++;
669
            }
670
        }
671
        if ($keys == 0) {
672
            return($this->raiseError(MDB_ERROR_CANNOT_REPLACE, NULL, NULL,
673
                'not specified which fields are keys'));
674
        }
675
        return($this->query("REPLACE INTO $table ($query) VALUES ($values)"));
676
    }
677
 
678
    // }}}
679
    // {{{ getColumnNames()
680
 
681
    /**
682
     * Retrieve the names of columns returned by the DBMS in a query result.
683
     *
684
     * @param resource   $result    result identifier
685
     * @return mixed                an associative array variable
686
     *                              that will hold the names of columns. The
687
     *                              indexes of the array are the column names
688
     *                              mapped to lower case and the values are the
689
     *                              respective numbers of the columns starting
690
     *                              from 0. Some DBMS may not return any
691
     *                              columns when the result set does not
692
     *                              contain any rows.
693
     *
694
     *                              a MDB error on failure
695
     * @access public
696
     */
697
    function getColumnNames($result)
698
    {
699
        $result_value = intval($result);
700
        if (!isset($this->highest_fetched_row[$result_value])) {
701
            return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
702
                'Get column names: it was specified an inexisting result set'));
703
        }
704
        if (!isset($this->columns[$result_value])) {
705
            $this->columns[$result_value] = array();
706
            $columns = @mysql_num_fields($result);
707
            for($column = 0; $column < $columns; $column++) {
708
                $field_name = @mysql_field_name($result, $column);
709
                if ($this->options['optimize'] == 'portability') {
710
                    $field_name = strtolower($field_name);
711
                }
712
                $this->columns[$result_value][$field_name] = $column;
713
            }
714
        }
715
        return($this->columns[$result_value]);
716
    }
717
 
718
    // }}}
719
    // {{{ numCols()
720
 
721
    /**
722
     * Count the number of columns returned by the DBMS in a query result.
723
     *
724
     * @param resource    $result        result identifier
725
     * @access public
726
     * @return mixed integer value with the number of columns, a MDB error
727
     *                       on failure
728
     */
729
    function numCols($result)
730
    {
731
        $result_value = intval($result);
732
        if (!isset($this->highest_fetched_row[$result_value])) {
733
            return($this->raiseError(MDB_ERROR_INVALID, NULL, NULL,
734
                'numCols: it was specified an inexisting result set'));
735
        }
736
        return(@mysql_num_fields($result));
737
    }
738
 
739
    // }}}
740
    // {{{ endOfResult()
741
 
742
    /**
743
    * check if the end of the result set has been reached
744
    *
745
    * @param resource    $result result identifier
746
    * @return mixed TRUE or FALSE on sucess, a MDB error on failure
747
    * @access public
748
    */
749
    function endOfResult($result)
750
    {
751
        $result_value = intval($result);
752
        if (!isset($this->highest_fetched_row[$result_value])) {
753
            return($this->raiseError(MDB_ERROR, NULL, NULL,
754
                'End of result: attempted to check the end of an unknown result'));
755
        }
756
        return($this->highest_fetched_row[$result_value] >= $this->numRows($result)-1);
757
    }
758
 
759
    // }}}
760
    // {{{ fetch()
761
 
762
    /**
763
    * fetch value from a result set
764
    *
765
    * @param resource    $result result identifier
766
    * @param int    $row    number of the row where the data can be found
767
    * @param int    $field    field number where the data can be found
768
    * @return mixed string on success, a MDB error on failure
769
    * @access public
770
    */
771
    function fetch($result, $row, $field)
772
    {
773
        $result_value = intval($result);
774
        $this->highest_fetched_row[$result_value] =
775
            max($this->highest_fetched_row[$result_value], $row);
776
        $res = @mysql_result($result, $row, $field);
777
        if ($res === FALSE && $res != NULL) {
778
            return($this->mysqlRaiseError());
779
        }
780
        return($res);
781
    }
782
 
783
    // }}}
784
    // {{{ fetchClob()
785
 
786
    /**
787
    * fetch a clob value from a result set
788
    *
789
    * @param resource    $result result identifier
790
    * @param int    $row    number of the row where the data can be found
791
    * @param int    $field    field number where the data can be found
792
    * @return mixed content of the specified data cell, a MDB error on failure,
793
    *               a MDB error on failure
794
    * @access public
795
    */
796
    function fetchClob($result, $row, $field)
797
    {
798
        return($this->fetchLob($result, $row, $field));
799
    }
800
 
801
    // }}}
802
    // {{{ fetchBlob()
803
 
804
    /**
805
    * fetch a blob value from a result set
806
    *
807
    * @param resource    $result result identifier
808
    * @param int    $row    number of the row where the data can be found
809
    * @param int    $field    field number where the data can be found
810
    * @return mixed content of the specified data cell, a MDB error on failure
811
    * @access public
812
    */
813
    function fetchBlob($result, $row, $field)
814
    {
815
        return($this->fetchLob($result, $row, $field));
816
    }
817
 
818
    // }}}
819
    // {{{ convertResult()
820
 
821
    /**
822
    * convert a value to a RDBMS indepdenant MDB type
823
    *
824
    * @param mixed  $value   value to be converted
825
    * @param int    $type    constant that specifies which type to convert to
826
    * @return mixed converted value
827
    * @access public
828
    */
829
    function convertResult($value, $type)
830
    {
831
        switch($type) {
832
            case MDB_TYPE_DECIMAL:
833
                return(sprintf('%.'.$this->decimal_places.'f', doubleval($value)/$this->decimal_factor));
834
            default:
835
                return($this->_baseConvertResult($value, $type));
836
        }
837
    }
838
 
839
    // }}}
840
    // {{{ numRows()
841
 
842
    /**
843
    * returns the number of rows in a result object
844
    *
845
     * @param ressource $result a valid result ressouce pointer
846
    * @return mixed MDB_Error or the number of rows
847
    * @access public
848
    */
849
    function numRows($result)
850
    {
851
        return(@mysql_num_rows($result));
852
    }
853
 
854
    // }}}
855
    // {{{ freeResult()
856
 
857
    /**
858
     * Free the internal resources associated with $result.
859
     *
860
     * @param $result result identifier
861
     * @return boolean TRUE on success, FALSE if $result is invalid
862
     * @access public
863
     */
864
    function freeResult($result)
865
    {
866
        $result_value = intval($result);
867
        if(isset($this->highest_fetched_row[$result_value])) {
868
            unset($this->highest_fetched_row[$result_value]);
869
        }
870
        if(isset($this->columns[$result_value])) {
871
            unset($this->columns[$result_value]);
872
        }
873
        if(isset($this->result_types[$result_value])) {
874
            unset($this->result_types[$result_value]);
875
        }
876
        return(@mysql_free_result($result));
877
    }
878
 
879
    // }}}
880
    // {{{ getIntegerDeclaration()
881
 
882
    /**
883
     * Obtain DBMS specific SQL code portion needed to declare an integer type
884
     * field to be used in statements like CREATE TABLE.
885
     *
886
     * @param string  $name   name the field to be declared.
887
     * @param string  $field  associative array with the name of the properties
888
     *                        of the field being declared as array indexes.
889
     *                        Currently, the types of supported field
890
     *                        properties are as follows:
891
     *
892
     *                       unsigned
893
     *                        Boolean flag that indicates whether the field
894
     *                        should be declared as unsigned integer if
895
     *                        possible.
896
     *
897
     *                       default
898
     *                        Integer value to be used as default for this
899
     *                        field.
900
     *
901
     *                       notnull
902
     *                        Boolean flag that indicates whether this field is
903
     *                        constrained to not be set to NULL.
904
     * @return string  DBMS specific SQL code portion that should be used to
905
     *                 declare the specified field.
906
     * @access public
907
     */
908
    function getIntegerDeclaration($name, $field)
909
    {
910
        return("$name INT".
911
                (isset($field['unsigned']) ? ' UNSIGNED' : '').
912
                (isset($field['default']) ? ' DEFAULT '.$field['default'] : '').
913
                (isset($field['notnull']) ? ' NOT NULL' : '')
914
               );
915
    }
916
 
917
    // }}}
918
    // {{{ getClobDeclaration()
919
 
920
    /**
921
     * Obtain DBMS specific SQL code portion needed to declare an character
922
     * large object type field to be used in statements like CREATE TABLE.
923
     *
924
     * @param string  $name   name the field to be declared.
925
     * @param string  $field  associative array with the name of the
926
     *                        properties of the field being declared as array
927
     *                        indexes. Currently, the types of supported field
928
     *                        properties are as follows:
929
     *
930
     *                       length
931
     *                        Integer value that determines the maximum length
932
     *                        of the large object field. If this argument is
933
     *                        missing the field should be declared to have the
934
     *                        longest length allowed by the DBMS.
935
     *
936
     *                       notnull
937
     *                        Boolean flag that indicates whether this field
938
     *                        is constrained to not be set to NULL.
939
     * @return string  DBMS specific SQL code portion that should be used to
940
     *                 declare the specified field.
941
     * @access public
942
     */
943
    function getClobDeclaration($name, $field)
944
    {
945
        if (isset($field['length'])) {
946
            $length = $field['length'];
947
            if ($length <= 255) {
948
                $type = 'TINYTEXT';
949
            } else {
950
                if ($length <= 65535) {
951
                    $type = 'TEXT';
952
                } else {
953
                    if ($length <= 16777215) {
954
                        $type = 'MEDIUMTEXT';
955
                    } else {
956
                        $type = 'LONGTEXT';
957
                    }
958
                }
959
            }
960
        } else {
961
            $type = 'LONGTEXT';
962
        }
963
        return("$name $type".
964
                 (isset($field['notnull']) ? ' NOT NULL' : ''));
965
    }
966
 
967
    // }}}
968
    // {{{ getBlobDeclaration()
969
 
970
    /**
971
     * Obtain DBMS specific SQL code portion needed to declare an binary large
972
     * object type field to be used in statements like CREATE TABLE.
973
     *
974
     * @param string  $name   name the field to be declared.
975
     * @param string  $field  associative array with the name of the properties
976
     *                        of the field being declared as array indexes.
977
     *                        Currently, the types of supported field
978
     *                        properties are as follows:
979
     *
980
     *                       length
981
     *                        Integer value that determines the maximum length
982
     *                        of the large object field. If this argument is
983
     *                        missing the field should be declared to have the
984
     *                        longest length allowed by the DBMS.
985
     *
986
     *                       notnull
987
     *                        Boolean flag that indicates whether this field is
988
     *                        constrained to not be set to NULL.
989
     * @return string  DBMS specific SQL code portion that should be used to
990
     *                 declare the specified field.
991
     * @access public
992
     */
993
    function getBlobDeclaration($name, $field)
994
    {
995
        if (isset($field['length'])) {
996
            $length = $field['length'];
997
            if ($length <= 255) {
998
                $type = 'TINYBLOB';
999
            } else {
1000
                if ($length <= 65535) {
1001
                    $type = 'BLOB';
1002
                } else {
1003
                    if ($length <= 16777215) {
1004
                        $type = 'MEDIUMBLOB';
1005
                    } else {
1006
                        $type = 'LONGBLOB';
1007
                    }
1008
                }
1009
            }
1010
        }
1011
        else {
1012
            $type = 'LONGBLOB';
1013
        }
1014
        return("$name $type".
1015
                (isset($field['notnull']) ? ' NOT NULL' : ''));
1016
    }
1017
 
1018
    // }}}
1019
    // {{{ getDateDeclaration()
1020
 
1021
    /**
1022
     * Obtain DBMS specific SQL code portion needed to declare an date type
1023
     * field to be used in statements like CREATE TABLE.
1024
     *
1025
     * @param string  $name   name the field to be declared.
1026
     * @param string  $field  associative array with the name of the properties
1027
     *                        of the field being declared as array indexes.
1028
     *                        Currently, the types of supported field properties
1029
     *                        are as follows:
1030
     *
1031
     *                       default
1032
     *                        Date value to be used as default for this field.
1033
     *
1034
     *                       notnull
1035
     *                        Boolean flag that indicates whether this field is
1036
     *                        constrained to not be set to NULL.
1037
     * @return string  DBMS specific SQL code portion that should be used to
1038
     *                 declare the specified field.
1039
     * @access public
1040
     */
1041
    function getDateDeclaration($name, $field)
1042
    {
1043
        return("$name DATE".
1044
                (isset($field['default']) ? " DEFAULT '".$field['default']."'" : '').
1045
                (isset($field['notnull']) ? ' NOT NULL' : '')
1046
               );
1047
    }
1048
 
1049
    // }}}
1050
    // {{{ getTimestampDeclaration()
1051
 
1052
    /**
1053
     * Obtain DBMS specific SQL code portion needed to declare an timestamp
1054
     * type field to be used in statements like CREATE TABLE.
1055
     *
1056
     * @param string  $name   name the field to be declared.
1057
     * @param string  $field  associative array with the name of the properties
1058
     *                        of the field being declared as array indexes.
1059
     *                        Currently, the types of supported field
1060
     *                        properties are as follows:
1061
     *
1062
     *                       default
1063
     *                        Time stamp value to be used as default for this
1064
     *                        field.
1065
     *
1066
     *                       notnull
1067
     *                        Boolean flag that indicates whether this field is
1068
     *                        constrained to not be set to NULL.
1069
     * @return string  DBMS specific SQL code portion that should be used to
1070
     *                 declare the specified field.
1071
     * @access public
1072
     */
1073
    function getTimestampDeclaration($name, $field)
1074
    {
1075
        return("$name DATETIME".
1076
                (isset($field['default']) ? " DEFAULT '".$field['default']."'" : '').
1077
                (isset($field['notnull']) ? ' NOT NULL' : '')
1078
               );
1079
    }
1080
 
1081
    // }}}
1082
    // {{{ getTimeDeclaration()
1083
 
1084
    /**
1085
     * Obtain DBMS specific SQL code portion needed to declare an time type
1086
     * field to be used in statements like CREATE TABLE.
1087
     *
1088
     * @param string  $name   name the field to be declared.
1089
     * @param string  $field  associative array with the name of the properties
1090
     *                        of the field being declared as array indexes.
1091
     *                        Currently, the types of supported field
1092
     *                        properties are as follows:
1093
     *
1094
     *                       default
1095
     *                        Time value to be used as default for this field.
1096
     *
1097
     *                       notnull
1098
     *                        Boolean flag that indicates whether this field is
1099
     *                        constrained to not be set to NULL.
1100
     * @return string  DBMS specific SQL code portion that should be used to
1101
     *                 declare the specified field.
1102
     * @access public
1103
     */
1104
    function getTimeDeclaration($name, $field)
1105
    {
1106
        return("$name TIME".
1107
                (isset($field['default']) ? " DEFAULT '".$field['default']."'" : '').
1108
                (isset($field['notnull']) ? ' NOT NULL' : '')
1109
               );
1110
    }
1111
 
1112
    // }}}
1113
    // {{{ getFloatDeclaration()
1114
 
1115
    /**
1116
     * Obtain DBMS specific SQL code portion needed to declare an float type
1117
     * field to be used in statements like CREATE TABLE.
1118
     *
1119
     * @param string  $name   name the field to be declared.
1120
     * @param string  $field  associative array with the name of the properties
1121
     *                        of the field being declared as array indexes.
1122
     *                        Currently, the types of supported field
1123
     *                        properties are as follows:
1124
     *
1125
     *                       default
1126
     *                        Integer value to be used as default for this
1127
     *                        field.
1128
     *
1129
     *                       notnull
1130
     *                        Boolean flag that indicates whether this field is
1131
     *                        constrained to not be set to NULL.
1132
     * @return string  DBMS specific SQL code portion that should be used to
1133
     *                 declare the specified field.
1134
     * @access public
1135
     */
1136
    function getFloatDeclaration($name, $field)
1137
    {
1138
        if (isset($this->options['fixedfloat'])) {
1139
            $this->fixed_float = $this->options['fixedfloat'];
1140
        } else {
1141
            if ($this->connection == 0) {
1142
                // XXX needs more checking
1143
                $this->connect();
1144
            }
1145
        }
1146
        return("$name DOUBLE".
1147
                ($this->fixed_float ?
1148
                 '('.($this->fixed_float + 2).','.$this->fixed_float.')' : '').
1149
                (isset($field['default']) ?
1150
                 ' DEFAULT '.$this->getFloatValue($field['default']) : '').
1151
                (isset($field['notnull']) ? ' NOT NULL' : '')
1152
               );
1153
    }
1154
 
1155
    // }}}
1156
    // {{{ getDecimalDeclaration()
1157
 
1158
    /**
1159
     * Obtain DBMS specific SQL code portion needed to declare an decimal type
1160
     * field to be used in statements like CREATE TABLE.
1161
     *
1162
     * @param string  $name   name the field to be declared.
1163
     * @param string  $field  associative array with the name of the properties
1164
     *                        of the field being declared as array indexes.
1165
     *                        Currently, the types of supported field
1166
     *                        properties are as follows:
1167
     *
1168
     *                       default
1169
     *                        Integer value to be used as default for this
1170
     *                        field.
1171
     *
1172
     *                       notnull
1173
     *                        Boolean flag that indicates whether this field is
1174
     *                        constrained to not be set to NULL.
1175
     * @return string  DBMS specific SQL code portion that should be used to
1176
     *                 declare the specified field.
1177
     * @access public
1178
     */
1179
    function getDecimalDeclaration($name, $field)
1180
    {
1181
        return("$name BIGINT".
1182
                (isset($field['default']) ?
1183
                 ' DEFAULT '.$this->getDecimalValue($field['default']) : '').
1184
                 (isset($field['notnull']) ? ' NOT NULL' : '')
1185
               );
1186
    }
1187
 
1188
    // }}}
1189
    // {{{ getClobValue()
1190
 
1191
    /**
1192
     * Convert a text value into a DBMS specific format that is suitable to
1193
     * compose query statements.
1194
     *
1195
     * @param resource  $prepared_query query handle from prepare()
1196
     * @param           $parameter
1197
     * @param           $clob
1198
     * @return string  text string that represents the given argument value in
1199
     *                 a DBMS specific format.
1200
     * @access public
1201
     */
1202
    function getClobValue($prepared_query, $parameter, $clob)
1203
    {
1204
        $value = "'";
1205
        while(!$this->endOfLob($clob)) {
1206
            if (MDB::isError($result = $this->readLob($clob, $data, $this->options['lob_buffer_length']))) {
1207
                return($result);
1208
            }
1209
            $value .= $this->_quote($data);
1210
        }
1211
        $value .= "'";
1212
        return($value);
1213
    }
1214
 
1215
    // }}}
1216
    // {{{ freeClobValue()
1217
 
1218
    /**
1219
     * free a character large object
1220
     *
1221
     * @param resource  $prepared_query query handle from prepare()
1222
     * @param string    $clob
1223
     * @return MDB_OK
1224
     * @access public
1225
     */
1226
    function freeClobValue($prepared_query, $clob)
1227
    {
1228
        unset($this->lobs[$clob]);
1229
        return(MDB_OK);
1230
    }
1231
 
1232
    // }}}
1233
    // {{{ getBlobValue()
1234
 
1235
    /**
1236
     * Convert a text value into a DBMS specific format that is suitable to
1237
     * compose query statements.
1238
     *
1239
     * @param resource  $prepared_query query handle from prepare()
1240
     * @param           $parameter
1241
     * @param           $blob
1242
     * @return string  text string that represents the given argument value in
1243
     *                 a DBMS specific format.
1244
     * @access public
1245
     */
1246
    function getBlobValue($prepared_query, $parameter, $blob)
1247
    {
1248
        $value = "'";
1249
        while(!$this->endOfLob($blob)) {
1250
            if (MDB::isError($result = $this->readLob($blob, $data, $this->options['lob_buffer_length']))) {
1251
                return($result);
1252
            }
1253
            $value .= addslashes($data);
1254
        }
1255
        $value .= "'";
1256
        return($value);
1257
    }
1258
 
1259
    // }}}
1260
    // {{{ freeBlobValue()
1261
 
1262
    /**
1263
     * free a binary large object
1264
     *
1265
     * @param resource  $prepared_query query handle from prepare()
1266
     * @param string    $blob
1267
     * @return MDB_OK
1268
     * @access public
1269
     */
1270
    function freeBlobValue($prepared_query, $blob)
1271
    {
1272
        unset($this->lobs[$blob]);
1273
        return(MDB_OK);
1274
    }
1275
 
1276
    // }}}
1277
    // {{{ getFloatValue()
1278
 
1279
    /**
1280
     * Convert a text value into a DBMS specific format that is suitable to
1281
     * compose query statements.
1282
     *
1283
     * @param string  $value text string value that is intended to be converted.
1284
     * @return string  text string that represents the given argument value in
1285
     *                 a DBMS specific format.
1286
     * @access public
1287
     */
1288
    function getFloatValue($value)
1289
    {
1290
        return(($value === NULL) ? 'NULL' : (float)$value);
1291
    }
1292
 
1293
    // }}}
1294
    // {{{ getDecimalValue()
1295
 
1296
    /**
1297
     * Convert a text value into a DBMS specific format that is suitable to
1298
     * compose query statements.
1299
     *
1300
     * @param string  $value text string value that is intended to be converted.
1301
     * @return string  text string that represents the given argument value in
1302
     *                 a DBMS specific format.
1303
     * @access public
1304
     */
1305
    function getDecimalValue($value)
1306
    {
1307
        return(($value === NULL) ? 'NULL' : strval(round(doubleval($value)*$this->decimal_factor)));
1308
    }
1309
 
1310
    // }}}
1311
    // {{{ nextId()
1312
 
1313
    /**
1314
     * returns the next free id of a sequence
1315
     *
1316
     * @param string  $seq_name name of the sequence
1317
     * @param boolean $ondemand when true the seqence is
1318
     *                          automatic created, if it
1319
     *                          not exists
1320
     *
1321
     * @return mixed MDB_Error or id
1322
     * @access public
1323
     */
1324
    function nextId($seq_name, $ondemand = TRUE)
1325
    {
1326
        $sequence_name = $this->getSequenceName($seq_name);
1327
        $this->expectError(MDB_ERROR_NOSUCHTABLE);
1328
        $result = $this->query("INSERT INTO $sequence_name ("
1329
            .$this->options['sequence_col_name'].") VALUES (NULL)");
1330
        $this->popExpect();
1331
        if ($ondemand && MDB::isError($result) &&
1332
            $result->getCode() == MDB_ERROR_NOSUCHTABLE)
1333
        {
1334
            // Since we are create the sequence on demand
1335
            // we know the first id = 1 so initialize the
1336
            // sequence at 2
1337
            $result = $this->createSequence($seq_name, 2);
1338
            if (MDB::isError($result)) {
1339
                return($this->raiseError(MDB_ERROR, NULL, NULL,
1340
                    'Next ID: on demand sequence could not be created'));
1341
            } else {
1342
                // First ID of a newly created sequence is 1
1343
                return(1);
1344
            }
1345
        }
1346
        $value = intval(@mysql_insert_id($this->connection));
1347
        $res = $this->query("DELETE FROM $sequence_name WHERE "
1348
            .$this->options['sequence_col_name']." < $value");
1349
        if (MDB::isError($res)) {
1350
            $this->warnings[] = 'Next ID: could not delete previous sequence table values';
1351
        }
1352
        return($value);
1353
    }
1354
 
1355
 
1356
    // }}}
1357
    // {{{ currId()
1358
 
1359
    /**
1360
     * returns the current id of a sequence
1361
     *
1362
     * @param string  $seq_name name of the sequence
1363
     * @return mixed MDB_Error or id
1364
     * @access public
1365
     */
1366
    function currId($seq_name)
1367
    {
1368
        $sequence_name = $this->getSequenceName($seq_name);
1369
        $result = $this->query("SELECT MAX(".$this->options['sequence_col_name'].") FROM $sequence_name", 'integer');
1370
        if (MDB::isError($result)) {
1371
            return($result);
1372
        }
1373
 
1374
        return($this->fetchOne($result));
1375
    }
1376
 
1377
    // }}}
1378
    // {{{ fetchInto()
1379
 
1380
    /**
1381
     * Fetch a row and insert the data into an existing array.
1382
     *
1383
     * @param resource  $result     result identifier
1384
     * @param int       $fetchmode  how the array data should be indexed
1385
     * @param int       $rownum     the row number to fetch
1386
     * @return int data array on success, a MDB error on failure
1387
     * @access public
1388
     */
1389
    function fetchInto($result, $fetchmode = MDB_FETCHMODE_DEFAULT, $rownum = NULL)
1390
    {
1391
        $result_value = intval($result);
1392
        if ($rownum == NULL) {
1393
            ++$this->highest_fetched_row[$result_value];
1394
        } else {
1395
            if (!@mysql_data_seek($result, $rownum)) {
1396
                return(NULL);
1397
            }
1398
            $this->highest_fetched_row[$result_value] =
1399
                max($this->highest_fetched_row[$result_value], $rownum);
1400
        }
1401
        if ($fetchmode == MDB_FETCHMODE_DEFAULT) {
1402
            $fetchmode = $this->fetchmode;
1403
        }
1404
        if ($fetchmode & MDB_FETCHMODE_ASSOC) {
1405
            $row = @mysql_fetch_assoc($result);
1406
            if (is_array($row) && $this->options['optimize'] == 'portability') {
1407
                $row = array_change_key_case($row, CASE_LOWER);
1408
            }
1409
        } else {
1410
            $row = @mysql_fetch_row($result);
1411
        }
1412
        if (!$row) {
1413
            if($this->options['autofree']) {
1414
                $this->freeResult($result);
1415
            }
1416
            return(NULL);
1417
        }
1418
        if (isset($this->result_types[$result_value])) {
1419
            $row = $this->convertResultRow($result, $row);
1420
        }
1421
        return($row);
1422
    }
1423
 
1424
    // }}}
1425
    // {{{ nextResult()
1426
 
1427
    /**
1428
     * Move the internal mysql result pointer to the next available result
1429
     * Currently not supported
1430
     *
1431
     * @param a valid result resource
1432
     * @return true if a result is available otherwise return false
1433
     * @access public
1434
     */
1435
    function nextResult($result)
1436
    {
1437
        return(FALSE);
1438
    }
1439
 
1440
    // }}}
1441
    // {{{ tableInfo()
1442
 
1443
    /**
1444
    * returns meta data about the result set
1445
    *
1446
    * @param resource    $result    result identifier
1447
    * @param mixed $mode depends on implementation
1448
    * @return array an nested array, or a MDB error
1449
    * @access public
1450
    */
1451
    function tableInfo($result, $mode = NULL) {
1452
        $count = 0;
1453
        $id     = 0;
1454
        $res  = array();
1455
 
1456
        /*
1457
         * depending on $mode, metadata returns the following values:
1458
         *
1459
         * - mode is false (default):
1460
         * $result[]:
1461
         *   [0]['table']  table name
1462
         *   [0]['name']   field name
1463
         *   [0]['type']   field type
1464
         *   [0]['len']    field length
1465
         *   [0]['flags']  field flags
1466
         *
1467
         * - mode is MDB_TABLEINFO_ORDER
1468
         * $result[]:
1469
         *   ['num_fields'] number of metadata records
1470
         *   [0]['table']  table name
1471
         *   [0]['name']   field name
1472
         *   [0]['type']   field type
1473
         *   [0]['len']    field length
1474
         *   [0]['flags']  field flags
1475
         *   ['order'][field name]  index of field named "field name"
1476
         *   The last one is used, if you have a field name, but no index.
1477
         *   Test:  if (isset($result['meta']['myfield'])) { ...
1478
         *
1479
         * - mode is MDB_TABLEINFO_ORDERTABLE
1480
         *    the same as above. but additionally
1481
         *   ['ordertable'][table name][field name] index of field
1482
         *      named 'field name'
1483
         *
1484
         *      this is, because if you have fields from different
1485
         *      tables with the same field name * they override each
1486
         *      other with MDB_TABLEINFO_ORDER
1487
         *
1488
         *      you can combine MDB_TABLEINFO_ORDER and
1489
         *      MDB_TABLEINFO_ORDERTABLE with MDB_TABLEINFO_ORDER |
1490
         *      MDB_TABLEINFO_ORDERTABLE * or with MDB_TABLEINFO_FULL
1491
         */
1492
 
1493
        // if $result is a string, then we want information about a
1494
        // table without a resultset
1495
        if (is_string($result)) {
1496
            if (MDB::isError($connect = $this->connect())) {
1497
                return $connect;
1498
            }
1499
            $id = @mysql_list_fields($this->database_name,
1500
                $result, $this->connection);
1501
            if (empty($id)) {
1502
                return($this->mysqlRaiseError());
1503
            }
1504
        } else { // else we want information about a resultset
1505
            $id = $result;
1506
            if (empty($id)) {
1507
                return($this->mysqlRaiseError());
1508
            }
1509
        }
1510
 
1511
        $count = @mysql_num_fields($id);
1512
 
1513
        // made this IF due to performance (one if is faster than $count if's)
1514
        if (empty($mode)) {
1515
            for ($i = 0, $j = 0; $i<$count; $i++) {
1516
                $name = @mysql_field_name($id, $i);
1517
                if ($name != 'dummy_primary_key') {
1518
                    $res[$j]['table'] = @mysql_field_table($id, $i);
1519
                    $res[$j]['name'] = $name;
1520
                    $res[$j]['type'] = @mysql_field_type($id, $i);
1521
                    $res[$j]['len']  = @mysql_field_len($id, $i);
1522
                    $res[$j]['flags'] = @mysql_field_flags($id, $i);
1523
                    $j++;
1524
                }
1525
            }
1526
        } else { // full
1527
            $res['num_fields'] = $count;
1528
 
1529
            for ($i = 0; $i<$count; $i++) {
1530
                $name = @mysql_field_name($id, $i);
1531
                if ($name != 'dummy_primary_key') {
1532
                    $res[$j]['table'] = @mysql_field_table($id, $i);
1533
                    $res[$j]['name'] = $name;
1534
                    $res[$j]['type'] = @mysql_field_type($id, $i);
1535
                    $res[$j]['len']  = @mysql_field_len($id, $i);
1536
                    $res[$j]['flags'] = @mysql_field_flags($id, $i);
1537
                    if ($mode & MDB_TABLEINFO_ORDER) {
1538
                        // note sure if this should be $i or $j
1539
                        $res['order'][$res[$j]['name']] = $i;
1540
                    }
1541
                    if ($mode & MDB_TABLEINFO_ORDERTABLE) {
1542
                        $res['ordertable'][$res[$j]['table']][$res[$j]['name']] = $j;
1543
                    }
1544
                    $j++;
1545
                }
1546
            }
1547
        }
1548
 
1549
        // free the result only if we were called on a table
1550
        if (is_string($result)) {
1551
            @mysql_free_result($id);
1552
        }
1553
        return($res);
1554
    }
1555
}
1556
 
1557
?>