Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
320 jpm 1
<?php
2
 
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
 
5
/**
6
 * The PEAR DB driver for PHP's sybase extension
7
 * for interacting with Sybase databases
8
 *
9
 * PHP versions 4 and 5
10
 *
11
 * LICENSE: This source file is subject to version 3.0 of the PHP license
12
 * that is available through the world-wide-web at the following URI:
13
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
14
 * the PHP License and are unable to obtain it through the web, please
15
 * send a note to license@php.net so we can mail you a copy immediately.
16
 *
17
 * @category   Database
18
 * @package    DB
19
 * @author     Sterling Hughes <sterling@php.net>
20
 * @author     Antônio Carlos Venâncio Júnior <floripa@php.net>
21
 * @author     Daniel Convissor <danielc@php.net>
22
 * @copyright  1997-2005 The PHP Group
23
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
1173 jp_milcent 24
 * @version    CVS: $Id: sybase.php,v 1.3 2006-12-14 15:04:28 jp_milcent Exp $
320 jpm 25
 * @link       http://pear.php.net/package/DB
26
 */
27
 
28
/**
29
 * Obtain the DB_common class so it can be extended from
30
 */
31
require_once 'DB/common.php';
32
 
33
/**
34
 * The methods PEAR DB uses to interact with PHP's sybase extension
35
 * for interacting with Sybase databases
36
 *
37
 * These methods overload the ones declared in DB_common.
38
 *
39
 * WARNING:  This driver may fail with multiple connections under the
40
 * same user/pass/host and different databases.
41
 *
42
 * @category   Database
43
 * @package    DB
44
 * @author     Sterling Hughes <sterling@php.net>
45
 * @author     Antônio Carlos Venâncio Júnior <floripa@php.net>
46
 * @author     Daniel Convissor <danielc@php.net>
47
 * @copyright  1997-2005 The PHP Group
48
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
1173 jp_milcent 49
 * @version    Release: 1.7.6
320 jpm 50
 * @link       http://pear.php.net/package/DB
51
 */
52
class DB_sybase extends DB_common
53
{
54
    // {{{ properties
55
 
56
    /**
57
     * The DB driver type (mysql, oci8, odbc, etc.)
58
     * @var string
59
     */
60
    var $phptype = 'sybase';
61
 
62
    /**
63
     * The database syntax variant to be used (db2, access, etc.), if any
64
     * @var string
65
     */
66
    var $dbsyntax = 'sybase';
67
 
68
    /**
69
     * The capabilities of this DB implementation
70
     *
71
     * The 'new_link' element contains the PHP version that first provided
72
     * new_link support for this DBMS.  Contains false if it's unsupported.
73
     *
74
     * Meaning of the 'limit' element:
75
     *   + 'emulate' = emulate with fetch row by number
76
     *   + 'alter'   = alter the query
77
     *   + false     = skip rows
78
     *
79
     * @var array
80
     */
81
    var $features = array(
82
        'limit'         => 'emulate',
83
        'new_link'      => false,
84
        'numrows'       => true,
85
        'pconnect'      => true,
86
        'prepare'       => false,
87
        'ssl'           => false,
88
        'transactions'  => true,
89
    );
90
 
91
    /**
92
     * A mapping of native error codes to DB error codes
93
     * @var array
94
     */
95
    var $errorcode_map = array(
96
    );
97
 
98
    /**
99
     * The raw database connection created by PHP
100
     * @var resource
101
     */
102
    var $connection;
103
 
104
    /**
105
     * The DSN information for connecting to a database
106
     * @var array
107
     */
108
    var $dsn = array();
109
 
110
 
111
    /**
112
     * Should data manipulation queries be committed automatically?
113
     * @var bool
114
     * @access private
115
     */
116
    var $autocommit = true;
117
 
118
    /**
119
     * The quantity of transactions begun
120
     *
121
     * {@internal  While this is private, it can't actually be designated
122
     * private in PHP 5 because it is directly accessed in the test suite.}}
123
     *
124
     * @var integer
125
     * @access private
126
     */
127
    var $transaction_opcount = 0;
128
 
129
    /**
130
     * The database specified in the DSN
131
     *
132
     * It's a fix to allow calls to different databases in the same script.
133
     *
134
     * @var string
135
     * @access private
136
     */
137
    var $_db = '';
138
 
139
 
140
    // }}}
141
    // {{{ constructor
142
 
143
    /**
144
     * This constructor calls <kbd>$this->DB_common()</kbd>
145
     *
146
     * @return void
147
     */
148
    function DB_sybase()
149
    {
150
        $this->DB_common();
151
    }
152
 
153
    // }}}
154
    // {{{ connect()
155
 
156
    /**
157
     * Connect to the database server, log in and open the database
158
     *
159
     * Don't call this method directly.  Use DB::connect() instead.
160
     *
161
     * PEAR DB's sybase driver supports the following extra DSN options:
162
     *   + appname       The application name to use on this connection.
163
     *                   Available since PEAR DB 1.7.0.
164
     *   + charset       The character set to use on this connection.
165
     *                   Available since PEAR DB 1.7.0.
166
     *
167
     * @param array $dsn         the data source name
168
     * @param bool  $persistent  should the connection be persistent?
169
     *
170
     * @return int  DB_OK on success. A DB_Error object on failure.
171
     */
172
    function connect($dsn, $persistent = false)
173
    {
174
        if (!PEAR::loadExtension('sybase') &&
175
            !PEAR::loadExtension('sybase_ct'))
176
        {
177
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
178
        }
179
 
180
        $this->dsn = $dsn;
181
        if ($dsn['dbsyntax']) {
182
            $this->dbsyntax = $dsn['dbsyntax'];
183
        }
184
 
185
        $dsn['hostspec'] = $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost';
186
        $dsn['password'] = !empty($dsn['password']) ? $dsn['password'] : false;
187
        $dsn['charset'] = isset($dsn['charset']) ? $dsn['charset'] : false;
188
        $dsn['appname'] = isset($dsn['appname']) ? $dsn['appname'] : false;
189
 
190
        $connect_function = $persistent ? 'sybase_pconnect' : 'sybase_connect';
191
 
192
        if ($dsn['username']) {
193
            $this->connection = @$connect_function($dsn['hostspec'],
194
                                                   $dsn['username'],
195
                                                   $dsn['password'],
196
                                                   $dsn['charset'],
197
                                                   $dsn['appname']);
198
        } else {
199
            return $this->raiseError(DB_ERROR_CONNECT_FAILED,
200
                                     null, null, null,
201
                                     'The DSN did not contain a username.');
202
        }
203
 
204
        if (!$this->connection) {
205
            return $this->raiseError(DB_ERROR_CONNECT_FAILED,
206
                                     null, null, null,
207
                                     @sybase_get_last_message());
208
        }
209
 
210
        if ($dsn['database']) {
211
            if (!@sybase_select_db($dsn['database'], $this->connection)) {
212
                return $this->raiseError(DB_ERROR_NODBSELECTED,
213
                                         null, null, null,
214
                                         @sybase_get_last_message());
215
            }
216
            $this->_db = $dsn['database'];
217
        }
218
 
219
        return DB_OK;
220
    }
221
 
222
    // }}}
223
    // {{{ disconnect()
224
 
225
    /**
226
     * Disconnects from the database server
227
     *
228
     * @return bool  TRUE on success, FALSE on failure
229
     */
230
    function disconnect()
231
    {
232
        $ret = @sybase_close($this->connection);
233
        $this->connection = null;
234
        return $ret;
235
    }
236
 
237
    // }}}
238
    // {{{ simpleQuery()
239
 
240
    /**
241
     * Sends a query to the database server
242
     *
243
     * @param string  the SQL query string
244
     *
245
     * @return mixed  + a PHP result resrouce for successful SELECT queries
246
     *                + the DB_OK constant for other successful queries
247
     *                + a DB_Error object on failure
248
     */
249
    function simpleQuery($query)
250
    {
251
        $ismanip = DB::isManip($query);
252
        $this->last_query = $query;
253
        if (!@sybase_select_db($this->_db, $this->connection)) {
254
            return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
255
        }
256
        $query = $this->modifyQuery($query);
257
        if (!$this->autocommit && $ismanip) {
258
            if ($this->transaction_opcount == 0) {
259
                $result = @sybase_query('BEGIN TRANSACTION', $this->connection);
260
                if (!$result) {
261
                    return $this->sybaseRaiseError();
262
                }
263
            }
264
            $this->transaction_opcount++;
265
        }
266
        $result = @sybase_query($query, $this->connection);
267
        if (!$result) {
268
            return $this->sybaseRaiseError();
269
        }
270
        if (is_resource($result)) {
271
            return $result;
272
        }
273
        // Determine which queries that should return data, and which
274
        // should return an error code only.
275
        return $ismanip ? DB_OK : $result;
276
    }
277
 
278
    // }}}
279
    // {{{ nextResult()
280
 
281
    /**
282
     * Move the internal sybase result pointer to the next available result
283
     *
284
     * @param a valid sybase result resource
285
     *
286
     * @access public
287
     *
288
     * @return true if a result is available otherwise return false
289
     */
290
    function nextResult($result)
291
    {
292
        return false;
293
    }
294
 
295
    // }}}
296
    // {{{ fetchInto()
297
 
298
    /**
299
     * Places a row from the result set into the given array
300
     *
301
     * Formating of the array and the data therein are configurable.
302
     * See DB_result::fetchInto() for more information.
303
     *
304
     * This method is not meant to be called directly.  Use
305
     * DB_result::fetchInto() instead.  It can't be declared "protected"
306
     * because DB_result is a separate object.
307
     *
308
     * @param resource $result    the query result resource
309
     * @param array    $arr       the referenced array to put the data in
310
     * @param int      $fetchmode how the resulting array should be indexed
311
     * @param int      $rownum    the row number to fetch (0 = first row)
312
     *
313
     * @return mixed  DB_OK on success, NULL when the end of a result set is
314
     *                 reached or on failure
315
     *
316
     * @see DB_result::fetchInto()
317
     */
318
    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
319
    {
320
        if ($rownum !== null) {
321
            if (!@sybase_data_seek($result, $rownum)) {
322
                return null;
323
            }
324
        }
325
        if ($fetchmode & DB_FETCHMODE_ASSOC) {
326
            if (function_exists('sybase_fetch_assoc')) {
327
                $arr = @sybase_fetch_assoc($result);
328
            } else {
329
                if ($arr = @sybase_fetch_array($result)) {
330
                    foreach ($arr as $key => $value) {
331
                        if (is_int($key)) {
332
                            unset($arr[$key]);
333
                        }
334
                    }
335
                }
336
            }
337
            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
338
                $arr = array_change_key_case($arr, CASE_LOWER);
339
            }
340
        } else {
341
            $arr = @sybase_fetch_row($result);
342
        }
343
        if (!$arr) {
344
            return null;
345
        }
346
        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
347
            $this->_rtrimArrayValues($arr);
348
        }
349
        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
350
            $this->_convertNullArrayValuesToEmpty($arr);
351
        }
352
        return DB_OK;
353
    }
354
 
355
    // }}}
356
    // {{{ freeResult()
357
 
358
    /**
359
     * Deletes the result set and frees the memory occupied by the result set
360
     *
361
     * This method is not meant to be called directly.  Use
362
     * DB_result::free() instead.  It can't be declared "protected"
363
     * because DB_result is a separate object.
364
     *
365
     * @param resource $result  PHP's query result resource
366
     *
367
     * @return bool  TRUE on success, FALSE if $result is invalid
368
     *
369
     * @see DB_result::free()
370
     */
371
    function freeResult($result)
372
    {
373
        return @sybase_free_result($result);
374
    }
375
 
376
    // }}}
377
    // {{{ numCols()
378
 
379
    /**
380
     * Gets the number of columns in a result set
381
     *
382
     * This method is not meant to be called directly.  Use
383
     * DB_result::numCols() instead.  It can't be declared "protected"
384
     * because DB_result is a separate object.
385
     *
386
     * @param resource $result  PHP's query result resource
387
     *
388
     * @return int  the number of columns.  A DB_Error object on failure.
389
     *
390
     * @see DB_result::numCols()
391
     */
392
    function numCols($result)
393
    {
394
        $cols = @sybase_num_fields($result);
395
        if (!$cols) {
396
            return $this->sybaseRaiseError();
397
        }
398
        return $cols;
399
    }
400
 
401
    // }}}
402
    // {{{ numRows()
403
 
404
    /**
405
     * Gets the number of rows in a result set
406
     *
407
     * This method is not meant to be called directly.  Use
408
     * DB_result::numRows() instead.  It can't be declared "protected"
409
     * because DB_result is a separate object.
410
     *
411
     * @param resource $result  PHP's query result resource
412
     *
413
     * @return int  the number of rows.  A DB_Error object on failure.
414
     *
415
     * @see DB_result::numRows()
416
     */
417
    function numRows($result)
418
    {
419
        $rows = @sybase_num_rows($result);
420
        if ($rows === false) {
421
            return $this->sybaseRaiseError();
422
        }
423
        return $rows;
424
    }
425
 
426
    // }}}
427
    // {{{ affectedRows()
428
 
429
    /**
430
     * Determines the number of rows affected by a data maniuplation query
431
     *
432
     * 0 is returned for queries that don't manipulate data.
433
     *
434
     * @return int  the number of rows.  A DB_Error object on failure.
435
     */
436
    function affectedRows()
437
    {
438
        if (DB::isManip($this->last_query)) {
439
            $result = @sybase_affected_rows($this->connection);
440
        } else {
441
            $result = 0;
442
        }
443
        return $result;
444
     }
445
 
446
    // }}}
447
    // {{{ nextId()
448
 
449
    /**
450
     * Returns the next free id in a sequence
451
     *
452
     * @param string  $seq_name  name of the sequence
453
     * @param boolean $ondemand  when true, the seqence is automatically
454
     *                            created if it does not exist
455
     *
456
     * @return int  the next id number in the sequence.
457
     *               A DB_Error object on failure.
458
     *
459
     * @see DB_common::nextID(), DB_common::getSequenceName(),
460
     *      DB_sybase::createSequence(), DB_sybase::dropSequence()
461
     */
462
    function nextId($seq_name, $ondemand = true)
463
    {
464
        $seqname = $this->getSequenceName($seq_name);
465
        if (!@sybase_select_db($this->_db, $this->connection)) {
466
            return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
467
        }
468
        $repeat = 0;
469
        do {
470
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
471
            $result = $this->query("INSERT INTO $seqname (vapor) VALUES (0)");
472
            $this->popErrorHandling();
473
            if ($ondemand && DB::isError($result) &&
474
                ($result->getCode() == DB_ERROR || $result->getCode() == DB_ERROR_NOSUCHTABLE))
475
            {
476
                $repeat = 1;
477
                $result = $this->createSequence($seq_name);
478
                if (DB::isError($result)) {
479
                    return $this->raiseError($result);
480
                }
481
            } elseif (!DB::isError($result)) {
482
                $result =& $this->query("SELECT @@IDENTITY FROM $seqname");
483
                $repeat = 0;
484
            } else {
485
                $repeat = false;
486
            }
487
        } while ($repeat);
488
        if (DB::isError($result)) {
489
            return $this->raiseError($result);
490
        }
491
        $result = $result->fetchRow(DB_FETCHMODE_ORDERED);
492
        return $result[0];
493
    }
494
 
495
    /**
496
     * Creates a new sequence
497
     *
498
     * @param string $seq_name  name of the new sequence
499
     *
500
     * @return int  DB_OK on success.  A DB_Error object on failure.
501
     *
502
     * @see DB_common::createSequence(), DB_common::getSequenceName(),
503
     *      DB_sybase::nextID(), DB_sybase::dropSequence()
504
     */
505
    function createSequence($seq_name)
506
    {
507
        return $this->query('CREATE TABLE '
508
                            . $this->getSequenceName($seq_name)
509
                            . ' (id numeric(10, 0) IDENTITY NOT NULL,'
510
                            . ' vapor int NULL)');
511
    }
512
 
513
    // }}}
514
    // {{{ dropSequence()
515
 
516
    /**
517
     * Deletes a sequence
518
     *
519
     * @param string $seq_name  name of the sequence to be deleted
520
     *
521
     * @return int  DB_OK on success.  A DB_Error object on failure.
522
     *
523
     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
524
     *      DB_sybase::nextID(), DB_sybase::createSequence()
525
     */
526
    function dropSequence($seq_name)
527
    {
528
        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
529
    }
530
 
531
    // }}}
532
    // {{{ autoCommit()
533
 
534
    /**
535
     * Enables or disables automatic commits
536
     *
537
     * @param bool $onoff  true turns it on, false turns it off
538
     *
539
     * @return int  DB_OK on success.  A DB_Error object if the driver
540
     *               doesn't support auto-committing transactions.
541
     */
542
    function autoCommit($onoff = false)
543
    {
544
        // XXX if $this->transaction_opcount > 0, we should probably
545
        // issue a warning here.
546
        $this->autocommit = $onoff ? true : false;
547
        return DB_OK;
548
    }
549
 
550
    // }}}
551
    // {{{ commit()
552
 
553
    /**
554
     * Commits the current transaction
555
     *
556
     * @return int  DB_OK on success.  A DB_Error object on failure.
557
     */
558
    function commit()
559
    {
560
        if ($this->transaction_opcount > 0) {
561
            if (!@sybase_select_db($this->_db, $this->connection)) {
562
                return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
563
            }
564
            $result = @sybase_query('COMMIT', $this->connection);
565
            $this->transaction_opcount = 0;
566
            if (!$result) {
567
                return $this->sybaseRaiseError();
568
            }
569
        }
570
        return DB_OK;
571
    }
572
 
573
    // }}}
574
    // {{{ rollback()
575
 
576
    /**
577
     * Reverts the current transaction
578
     *
579
     * @return int  DB_OK on success.  A DB_Error object on failure.
580
     */
581
    function rollback()
582
    {
583
        if ($this->transaction_opcount > 0) {
584
            if (!@sybase_select_db($this->_db, $this->connection)) {
585
                return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
586
            }
587
            $result = @sybase_query('ROLLBACK', $this->connection);
588
            $this->transaction_opcount = 0;
589
            if (!$result) {
590
                return $this->sybaseRaiseError();
591
            }
592
        }
593
        return DB_OK;
594
    }
595
 
596
    // }}}
597
    // {{{ sybaseRaiseError()
598
 
599
    /**
600
     * Produces a DB_Error object regarding the current problem
601
     *
602
     * @param int $errno  if the error is being manually raised pass a
603
     *                     DB_ERROR* constant here.  If this isn't passed
604
     *                     the error information gathered from the DBMS.
605
     *
606
     * @return object  the DB_Error object
607
     *
608
     * @see DB_common::raiseError(),
609
     *      DB_sybase::errorNative(), DB_sybase::errorCode()
610
     */
611
    function sybaseRaiseError($errno = null)
612
    {
613
        $native = $this->errorNative();
614
        if ($errno === null) {
615
            $errno = $this->errorCode($native);
616
        }
617
        return $this->raiseError($errno, null, null, null, $native);
618
    }
619
 
620
    // }}}
621
    // {{{ errorNative()
622
 
623
    /**
624
     * Gets the DBMS' native error message produced by the last query
625
     *
626
     * @return string  the DBMS' error message
627
     */
628
    function errorNative()
629
    {
630
        return @sybase_get_last_message();
631
    }
632
 
633
    // }}}
634
    // {{{ errorCode()
635
 
636
    /**
637
     * Determines PEAR::DB error code from the database's text error message.
638
     *
639
     * @param  string  $errormsg  error message returned from the database
640
     * @return integer  an error number from a DB error constant
641
     */
642
    function errorCode($errormsg)
643
    {
644
        static $error_regexps;
645
        if (!isset($error_regexps)) {
646
            $error_regexps = array(
647
                '/Incorrect syntax near/'
648
                    => DB_ERROR_SYNTAX,
649
                '/^Unclosed quote before the character string [\"\'].*[\"\']\./'
650
                    => DB_ERROR_SYNTAX,
651
                '/Implicit conversion (from datatype|of NUMERIC value)/i'
652
                    => DB_ERROR_INVALID_NUMBER,
653
                '/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./'
654
                    => DB_ERROR_NOSUCHTABLE,
655
                '/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./'
656
                    => DB_ERROR_ACCESS_VIOLATION,
657
                '/^.+ permission denied on object .+, database .+, owner .+/'
658
                    => DB_ERROR_ACCESS_VIOLATION,
659
                '/^.* permission denied, database .+, owner .+/'
660
                    => DB_ERROR_ACCESS_VIOLATION,
661
                '/[^.*] not found\./'
662
                    => DB_ERROR_NOSUCHTABLE,
663
                '/There is already an object named/'
664
                    => DB_ERROR_ALREADY_EXISTS,
665
                '/Invalid column name/'
666
                    => DB_ERROR_NOSUCHFIELD,
667
                '/does not allow null values/'
668
                    => DB_ERROR_CONSTRAINT_NOT_NULL,
669
                '/Command has been aborted/'
670
                    => DB_ERROR_CONSTRAINT,
671
                '/^Cannot drop the index .* because it doesn\'t exist/i'
672
                    => DB_ERROR_NOT_FOUND,
673
                '/^There is already an index/i'
674
                    => DB_ERROR_ALREADY_EXISTS,
675
                '/^There are fewer columns in the INSERT statement than values specified/i'
676
                    => DB_ERROR_VALUE_COUNT_ON_ROW,
677
            );
678
        }
679
 
680
        foreach ($error_regexps as $regexp => $code) {
681
            if (preg_match($regexp, $errormsg)) {
682
                return $code;
683
            }
684
        }
685
        return DB_ERROR;
686
    }
687
 
688
    // }}}
689
    // {{{ tableInfo()
690
 
691
    /**
692
     * Returns information about a table or a result set
693
     *
694
     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
695
     * is a table name.
696
     *
697
     * @param object|string  $result  DB_result object from a query or a
698
     *                                 string containing the name of a table.
699
     *                                 While this also accepts a query result
700
     *                                 resource identifier, this behavior is
701
     *                                 deprecated.
702
     * @param int            $mode    a valid tableInfo mode
703
     *
704
     * @return array  an associative array with the information requested.
705
     *                 A DB_Error object on failure.
706
     *
707
     * @see DB_common::tableInfo()
708
     * @since Method available since Release 1.6.0
709
     */
710
    function tableInfo($result, $mode = null)
711
    {
712
        if (is_string($result)) {
713
            /*
714
             * Probably received a table name.
715
             * Create a result resource identifier.
716
             */
717
            if (!@sybase_select_db($this->_db, $this->connection)) {
718
                return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
719
            }
720
            $id = @sybase_query("SELECT * FROM $result WHERE 1=0",
721
                                $this->connection);
722
            $got_string = true;
723
        } elseif (isset($result->result)) {
724
            /*
725
             * Probably received a result object.
726
             * Extract the result resource identifier.
727
             */
728
            $id = $result->result;
729
            $got_string = false;
730
        } else {
731
            /*
732
             * Probably received a result resource identifier.
733
             * Copy it.
734
             * Deprecated.  Here for compatibility only.
735
             */
736
            $id = $result;
737
            $got_string = false;
738
        }
739
 
740
        if (!is_resource($id)) {
741
            return $this->sybaseRaiseError(DB_ERROR_NEED_MORE_DATA);
742
        }
743
 
744
        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
745
            $case_func = 'strtolower';
746
        } else {
747
            $case_func = 'strval';
748
        }
749
 
750
        $count = @sybase_num_fields($id);
751
        $res   = array();
752
 
753
        if ($mode) {
754
            $res['num_fields'] = $count;
755
        }
756
 
757
        for ($i = 0; $i < $count; $i++) {
758
            $f = @sybase_fetch_field($id, $i);
759
            // column_source is often blank
760
            $res[$i] = array(
761
                'table' => $got_string
762
                           ? $case_func($result)
763
                           : $case_func($f->column_source),
764
                'name'  => $case_func($f->name),
765
                'type'  => $f->type,
766
                'len'   => $f->max_length,
767
                'flags' => '',
768
            );
769
            if ($res[$i]['table']) {
770
                $res[$i]['flags'] = $this->_sybase_field_flags(
771
                        $res[$i]['table'], $res[$i]['name']);
772
            }
773
            if ($mode & DB_TABLEINFO_ORDER) {
774
                $res['order'][$res[$i]['name']] = $i;
775
            }
776
            if ($mode & DB_TABLEINFO_ORDERTABLE) {
777
                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
778
            }
779
        }
780
 
781
        // free the result only if we were called on a table
782
        if ($got_string) {
783
            @sybase_free_result($id);
784
        }
785
        return $res;
786
    }
787
 
788
    // }}}
789
    // {{{ _sybase_field_flags()
790
 
791
    /**
792
     * Get the flags for a field
793
     *
794
     * Currently supports:
795
     *  + <samp>unique_key</samp>    (unique index, unique check or primary_key)
796
     *  + <samp>multiple_key</samp>  (multi-key index)
797
     *
798
     * @param string  $table   the table name
799
     * @param string  $column  the field name
800
     *
801
     * @return string  space delimited string of flags.  Empty string if none.
802
     *
803
     * @access private
804
     */
805
    function _sybase_field_flags($table, $column)
806
    {
807
        static $tableName = null;
808
        static $flags = array();
809
 
810
        if ($table != $tableName) {
811
            $flags = array();
812
            $tableName = $table;
813
 
814
            // get unique/primary keys
815
            $res = $this->getAll("sp_helpindex $table", DB_FETCHMODE_ASSOC);
816
 
817
            if (!isset($res[0]['index_description'])) {
818
                return '';
819
            }
820
 
821
            foreach ($res as $val) {
822
                $keys = explode(', ', trim($val['index_keys']));
823
 
824
                if (sizeof($keys) > 1) {
825
                    foreach ($keys as $key) {
826
                        $this->_add_flag($flags[$key], 'multiple_key');
827
                    }
828
                }
829
 
830
                if (strpos($val['index_description'], 'unique')) {
831
                    foreach ($keys as $key) {
832
                        $this->_add_flag($flags[$key], 'unique_key');
833
                    }
834
                }
835
            }
836
 
837
        }
838
 
839
        if (array_key_exists($column, $flags)) {
840
            return(implode(' ', $flags[$column]));
841
        }
842
 
843
        return '';
844
    }
845
 
846
    // }}}
847
    // {{{ _add_flag()
848
 
849
    /**
850
     * Adds a string to the flags array if the flag is not yet in there
851
     * - if there is no flag present the array is created
852
     *
853
     * @param array  $array  reference of flags array to add a value to
854
     * @param mixed  $value  value to add to the flag array
855
     *
856
     * @return void
857
     *
858
     * @access private
859
     */
860
    function _add_flag(&$array, $value)
861
    {
862
        if (!is_array($array)) {
863
            $array = array($value);
864
        } elseif (!in_array($value, $array)) {
865
            array_push($array, $value);
866
        }
867
    }
868
 
869
    // }}}
870
    // {{{ getSpecialQuery()
871
 
872
    /**
873
     * Obtains the query string needed for listing a given type of objects
874
     *
875
     * @param string $type  the kind of objects you want to retrieve
876
     *
877
     * @return string  the SQL query string or null if the driver doesn't
878
     *                  support the object type requested
879
     *
880
     * @access protected
881
     * @see DB_common::getListOf()
882
     */
883
    function getSpecialQuery($type)
884
    {
885
        switch ($type) {
886
            case 'tables':
887
                return "SELECT name FROM sysobjects WHERE type = 'U'"
888
                       . ' ORDER BY name';
889
            case 'views':
890
                return "SELECT name FROM sysobjects WHERE type = 'V'";
891
            default:
892
                return null;
893
        }
894
    }
895
 
896
    // }}}
897
 
898
}
899
 
900
/*
901
 * Local variables:
902
 * tab-width: 4
903
 * c-basic-offset: 4
904
 * End:
905
 */
906
 
907
?>