Subversion Repositories Sites.obs-saisons.fr

Rev

Rev 5 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 aurelien 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-2007 The PHP Group
23
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
24
 * @version    CVS: $Id: sybase.php,v 1.1 2008/07/07 18:00:39 mrflos Exp $
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-2007 The PHP Group
48
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
49
 * @version    Release: 1.7.14RC1
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 = $this->_checkManip($query);
252
        $this->last_query = $query;
253
        if ($this->_db && !@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 is_resource($result) ? sybase_free_result($result) : false;
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 ($this->_last_query_manip) {
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 ($this->_db && !@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
    // {{{ quoteFloat()
533
 
534
    /**
535
     * Formats a float value for use within a query in a locale-independent
536
     * manner.
537
     *
538
     * @param float the float value to be quoted.
539
     * @return string the quoted string.
540
     * @see DB_common::quoteSmart()
541
     * @since Method available since release 1.7.8.
542
     */
543
    function quoteFloat($float) {
544
        return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
545
    }
546
 
547
    // }}}
548
    // {{{ autoCommit()
549
 
550
    /**
551
     * Enables or disables automatic commits
552
     *
553
     * @param bool $onoff  true turns it on, false turns it off
554
     *
555
     * @return int  DB_OK on success.  A DB_Error object if the driver
556
     *               doesn't support auto-committing transactions.
557
     */
558
    function autoCommit($onoff = false)
559
    {
560
        // XXX if $this->transaction_opcount > 0, we should probably
561
        // issue a warning here.
562
        $this->autocommit = $onoff ? true : false;
563
        return DB_OK;
564
    }
565
 
566
    // }}}
567
    // {{{ commit()
568
 
569
    /**
570
     * Commits the current transaction
571
     *
572
     * @return int  DB_OK on success.  A DB_Error object on failure.
573
     */
574
    function commit()
575
    {
576
        if ($this->transaction_opcount > 0) {
577
            if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
578
                return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
579
            }
580
            $result = @sybase_query('COMMIT', $this->connection);
581
            $this->transaction_opcount = 0;
582
            if (!$result) {
583
                return $this->sybaseRaiseError();
584
            }
585
        }
586
        return DB_OK;
587
    }
588
 
589
    // }}}
590
    // {{{ rollback()
591
 
592
    /**
593
     * Reverts the current transaction
594
     *
595
     * @return int  DB_OK on success.  A DB_Error object on failure.
596
     */
597
    function rollback()
598
    {
599
        if ($this->transaction_opcount > 0) {
600
            if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
601
                return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
602
            }
603
            $result = @sybase_query('ROLLBACK', $this->connection);
604
            $this->transaction_opcount = 0;
605
            if (!$result) {
606
                return $this->sybaseRaiseError();
607
            }
608
        }
609
        return DB_OK;
610
    }
611
 
612
    // }}}
613
    // {{{ sybaseRaiseError()
614
 
615
    /**
616
     * Produces a DB_Error object regarding the current problem
617
     *
618
     * @param int $errno  if the error is being manually raised pass a
619
     *                     DB_ERROR* constant here.  If this isn't passed
620
     *                     the error information gathered from the DBMS.
621
     *
622
     * @return object  the DB_Error object
623
     *
624
     * @see DB_common::raiseError(),
625
     *      DB_sybase::errorNative(), DB_sybase::errorCode()
626
     */
627
    function sybaseRaiseError($errno = null)
628
    {
629
        $native = $this->errorNative();
630
        if ($errno === null) {
631
            $errno = $this->errorCode($native);
632
        }
633
        return $this->raiseError($errno, null, null, null, $native);
634
    }
635
 
636
    // }}}
637
    // {{{ errorNative()
638
 
639
    /**
640
     * Gets the DBMS' native error message produced by the last query
641
     *
642
     * @return string  the DBMS' error message
643
     */
644
    function errorNative()
645
    {
646
        return @sybase_get_last_message();
647
    }
648
 
649
    // }}}
650
    // {{{ errorCode()
651
 
652
    /**
653
     * Determines PEAR::DB error code from the database's text error message.
654
     *
655
     * @param  string  $errormsg  error message returned from the database
656
     * @return integer  an error number from a DB error constant
657
     */
658
    function errorCode($errormsg)
659
    {
660
        static $error_regexps;
661
 
662
        // PHP 5.2+ prepends the function name to $php_errormsg, so we need
663
        // this hack to work around it, per bug #9599.
664
        $errormsg = preg_replace('/^sybase[a-z_]+\(\): /', '', $errormsg);
665
 
666
        if (!isset($error_regexps)) {
667
            $error_regexps = array(
668
                '/Incorrect syntax near/'
669
                    => DB_ERROR_SYNTAX,
670
                '/^Unclosed quote before the character string [\"\'].*[\"\']\./'
671
                    => DB_ERROR_SYNTAX,
672
                '/Implicit conversion (from datatype|of NUMERIC value)/i'
673
                    => DB_ERROR_INVALID_NUMBER,
674
                '/Cannot drop the table [\"\'].+[\"\'], because it doesn\'t exist in the system catalogs\./'
675
                    => DB_ERROR_NOSUCHTABLE,
676
                '/Only the owner of object [\"\'].+[\"\'] or a user with System Administrator \(SA\) role can run this command\./'
677
                    => DB_ERROR_ACCESS_VIOLATION,
678
                '/^.+ permission denied on object .+, database .+, owner .+/'
679
                    => DB_ERROR_ACCESS_VIOLATION,
680
                '/^.* permission denied, database .+, owner .+/'
681
                    => DB_ERROR_ACCESS_VIOLATION,
682
                '/[^.*] not found\./'
683
                    => DB_ERROR_NOSUCHTABLE,
684
                '/There is already an object named/'
685
                    => DB_ERROR_ALREADY_EXISTS,
686
                '/Invalid column name/'
687
                    => DB_ERROR_NOSUCHFIELD,
688
                '/does not allow null values/'
689
                    => DB_ERROR_CONSTRAINT_NOT_NULL,
690
                '/Command has been aborted/'
691
                    => DB_ERROR_CONSTRAINT,
692
                '/^Cannot drop the index .* because it doesn\'t exist/i'
693
                    => DB_ERROR_NOT_FOUND,
694
                '/^There is already an index/i'
695
                    => DB_ERROR_ALREADY_EXISTS,
696
                '/^There are fewer columns in the INSERT statement than values specified/i'
697
                    => DB_ERROR_VALUE_COUNT_ON_ROW,
698
                '/Divide by zero/i'
699
                    => DB_ERROR_DIVZERO,
700
            );
701
        }
702
 
703
        foreach ($error_regexps as $regexp => $code) {
704
            if (preg_match($regexp, $errormsg)) {
705
                return $code;
706
            }
707
        }
708
        return DB_ERROR;
709
    }
710
 
711
    // }}}
712
    // {{{ tableInfo()
713
 
714
    /**
715
     * Returns information about a table or a result set
716
     *
717
     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
718
     * is a table name.
719
     *
720
     * @param object|string  $result  DB_result object from a query or a
721
     *                                 string containing the name of a table.
722
     *                                 While this also accepts a query result
723
     *                                 resource identifier, this behavior is
724
     *                                 deprecated.
725
     * @param int            $mode    a valid tableInfo mode
726
     *
727
     * @return array  an associative array with the information requested.
728
     *                 A DB_Error object on failure.
729
     *
730
     * @see DB_common::tableInfo()
731
     * @since Method available since Release 1.6.0
732
     */
733
    function tableInfo($result, $mode = null)
734
    {
735
        if (is_string($result)) {
736
            /*
737
             * Probably received a table name.
738
             * Create a result resource identifier.
739
             */
740
            if ($this->_db && !@sybase_select_db($this->_db, $this->connection)) {
741
                return $this->sybaseRaiseError(DB_ERROR_NODBSELECTED);
742
            }
743
            $id = @sybase_query("SELECT * FROM $result WHERE 1=0",
744
                                $this->connection);
745
            $got_string = true;
746
        } elseif (isset($result->result)) {
747
            /*
748
             * Probably received a result object.
749
             * Extract the result resource identifier.
750
             */
751
            $id = $result->result;
752
            $got_string = false;
753
        } else {
754
            /*
755
             * Probably received a result resource identifier.
756
             * Copy it.
757
             * Deprecated.  Here for compatibility only.
758
             */
759
            $id = $result;
760
            $got_string = false;
761
        }
762
 
763
        if (!is_resource($id)) {
764
            return $this->sybaseRaiseError(DB_ERROR_NEED_MORE_DATA);
765
        }
766
 
767
        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
768
            $case_func = 'strtolower';
769
        } else {
770
            $case_func = 'strval';
771
        }
772
 
773
        $count = @sybase_num_fields($id);
774
        $res   = array();
775
 
776
        if ($mode) {
777
            $res['num_fields'] = $count;
778
        }
779
 
780
        for ($i = 0; $i < $count; $i++) {
781
            $f = @sybase_fetch_field($id, $i);
782
            // column_source is often blank
783
            $res[$i] = array(
784
                'table' => $got_string
785
                           ? $case_func($result)
786
                           : $case_func($f->column_source),
787
                'name'  => $case_func($f->name),
788
                'type'  => $f->type,
789
                'len'   => $f->max_length,
790
                'flags' => '',
791
            );
792
            if ($res[$i]['table']) {
793
                $res[$i]['flags'] = $this->_sybase_field_flags(
794
                        $res[$i]['table'], $res[$i]['name']);
795
            }
796
            if ($mode & DB_TABLEINFO_ORDER) {
797
                $res['order'][$res[$i]['name']] = $i;
798
            }
799
            if ($mode & DB_TABLEINFO_ORDERTABLE) {
800
                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
801
            }
802
        }
803
 
804
        // free the result only if we were called on a table
805
        if ($got_string) {
806
            @sybase_free_result($id);
807
        }
808
        return $res;
809
    }
810
 
811
    // }}}
812
    // {{{ _sybase_field_flags()
813
 
814
    /**
815
     * Get the flags for a field
816
     *
817
     * Currently supports:
818
     *  + <samp>unique_key</samp>    (unique index, unique check or primary_key)
819
     *  + <samp>multiple_key</samp>  (multi-key index)
820
     *
821
     * @param string  $table   the table name
822
     * @param string  $column  the field name
823
     *
824
     * @return string  space delimited string of flags.  Empty string if none.
825
     *
826
     * @access private
827
     */
828
    function _sybase_field_flags($table, $column)
829
    {
830
        static $tableName = null;
831
        static $flags = array();
832
 
833
        if ($table != $tableName) {
834
            $flags = array();
835
            $tableName = $table;
836
 
837
            /* We're running sp_helpindex directly because it doesn't exist in
838
             * older versions of ASE -- unfortunately, we can't just use
839
             * DB::isError() because the user may be using callback error
840
             * handling. */
841
            $res = @sybase_query("sp_helpindex $table", $this->connection);
842
 
843
            if ($res === false || $res === true) {
844
                // Fake a valid response for BC reasons.
845
                return '';
846
            }
847
 
848
            while (($val = sybase_fetch_assoc($res)) !== false) {
849
                if (!isset($val['index_keys'])) {
850
                    /* No useful information returned. Break and be done with
851
                     * it, which preserves the pre-1.7.9 behaviour. */
852
                    break;
853
                }
854
 
855
                $keys = explode(', ', trim($val['index_keys']));
856
 
857
                if (sizeof($keys) > 1) {
858
                    foreach ($keys as $key) {
859
                        $this->_add_flag($flags[$key], 'multiple_key');
860
                    }
861
                }
862
 
863
                if (strpos($val['index_description'], 'unique')) {
864
                    foreach ($keys as $key) {
865
                        $this->_add_flag($flags[$key], 'unique_key');
866
                    }
867
                }
868
            }
869
 
870
            sybase_free_result($res);
871
 
872
        }
873
 
874
        if (array_key_exists($column, $flags)) {
875
            return(implode(' ', $flags[$column]));
876
        }
877
 
878
        return '';
879
    }
880
 
881
    // }}}
882
    // {{{ _add_flag()
883
 
884
    /**
885
     * Adds a string to the flags array if the flag is not yet in there
886
     * - if there is no flag present the array is created
887
     *
888
     * @param array  $array  reference of flags array to add a value to
889
     * @param mixed  $value  value to add to the flag array
890
     *
891
     * @return void
892
     *
893
     * @access private
894
     */
895
    function _add_flag(&$array, $value)
896
    {
897
        if (!is_array($array)) {
898
            $array = array($value);
899
        } elseif (!in_array($value, $array)) {
900
            array_push($array, $value);
901
        }
902
    }
903
 
904
    // }}}
905
    // {{{ getSpecialQuery()
906
 
907
    /**
908
     * Obtains the query string needed for listing a given type of objects
909
     *
910
     * @param string $type  the kind of objects you want to retrieve
911
     *
912
     * @return string  the SQL query string or null if the driver doesn't
913
     *                  support the object type requested
914
     *
915
     * @access protected
916
     * @see DB_common::getListOf()
917
     */
918
    function getSpecialQuery($type)
919
    {
920
        switch ($type) {
921
            case 'tables':
922
                return "SELECT name FROM sysobjects WHERE type = 'U'"
923
                       . ' ORDER BY name';
924
            case 'views':
925
                return "SELECT name FROM sysobjects WHERE type = 'V'";
926
            default:
927
                return null;
928
        }
929
    }
930
 
931
    // }}}
932
 
933
}
934
 
935
/*
936
 * Local variables:
937
 * tab-width: 4
938
 * c-basic-offset: 4
939
 * End:
940
 */
941
 
942
?>