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 interbase extension
7
 * for interacting with Interbase and Firebird databases
8
 *
9
 * While this class works with PHP 4, PHP's InterBase extension is
10
 * unstable in PHP 4.  Use PHP 5.
11
 *
12
 * PHP versions 4 and 5
13
 *
14
 * LICENSE: This source file is subject to version 3.0 of the PHP license
15
 * that is available through the world-wide-web at the following URI:
16
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
17
 * the PHP License and are unable to obtain it through the web, please
18
 * send a note to license@php.net so we can mail you a copy immediately.
19
 *
20
 * @category   Database
21
 * @package    DB
22
 * @author     Sterling Hughes <sterling@php.net>
23
 * @author     Daniel Convissor <danielc@php.net>
24
 * @copyright  1997-2007 The PHP Group
25
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
26
 * @version    CVS: $Id: ibase.php,v 1.1 2008/07/07 18:00:39 mrflos Exp $
27
 * @link       http://pear.php.net/package/DB
28
 */
29
 
30
/**
31
 * Obtain the DB_common class so it can be extended from
32
 */
33
require_once 'DB/common.php';
34
 
35
/**
36
 * The methods PEAR DB uses to interact with PHP's interbase extension
37
 * for interacting with Interbase and Firebird databases
38
 *
39
 * These methods overload the ones declared in DB_common.
40
 *
41
 * While this class works with PHP 4, PHP's InterBase extension is
42
 * unstable in PHP 4.  Use PHP 5.
43
 *
44
 * NOTICE:  limitQuery() only works for Firebird.
45
 *
46
 * @category   Database
47
 * @package    DB
48
 * @author     Sterling Hughes <sterling@php.net>
49
 * @author     Daniel Convissor <danielc@php.net>
50
 * @copyright  1997-2007 The PHP Group
51
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
52
 * @version    Release: 1.7.14RC1
53
 * @link       http://pear.php.net/package/DB
54
 * @since      Class became stable in Release 1.7.0
55
 */
56
class DB_ibase extends DB_common
57
{
58
    // {{{ properties
59
 
60
    /**
61
     * The DB driver type (mysql, oci8, odbc, etc.)
62
     * @var string
63
     */
64
    var $phptype = 'ibase';
65
 
66
    /**
67
     * The database syntax variant to be used (db2, access, etc.), if any
68
     * @var string
69
     */
70
    var $dbsyntax = 'ibase';
71
 
72
    /**
73
     * The capabilities of this DB implementation
74
     *
75
     * The 'new_link' element contains the PHP version that first provided
76
     * new_link support for this DBMS.  Contains false if it's unsupported.
77
     *
78
     * Meaning of the 'limit' element:
79
     *   + 'emulate' = emulate with fetch row by number
80
     *   + 'alter'   = alter the query
81
     *   + false     = skip rows
82
     *
83
     * NOTE: only firebird supports limit.
84
     *
85
     * @var array
86
     */
87
    var $features = array(
88
        'limit'         => false,
89
        'new_link'      => false,
90
        'numrows'       => 'emulate',
91
        'pconnect'      => true,
92
        'prepare'       => true,
93
        'ssl'           => false,
94
        'transactions'  => true,
95
    );
96
 
97
    /**
98
     * A mapping of native error codes to DB error codes
99
     * @var array
100
     */
101
    var $errorcode_map = array(
102
        -104 => DB_ERROR_SYNTAX,
103
        -150 => DB_ERROR_ACCESS_VIOLATION,
104
        -151 => DB_ERROR_ACCESS_VIOLATION,
105
        -155 => DB_ERROR_NOSUCHTABLE,
106
        -157 => DB_ERROR_NOSUCHFIELD,
107
        -158 => DB_ERROR_VALUE_COUNT_ON_ROW,
108
        -170 => DB_ERROR_MISMATCH,
109
        -171 => DB_ERROR_MISMATCH,
110
        -172 => DB_ERROR_INVALID,
111
        // -204 =>  // Covers too many errors, need to use regex on msg
112
        -205 => DB_ERROR_NOSUCHFIELD,
113
        -206 => DB_ERROR_NOSUCHFIELD,
114
        -208 => DB_ERROR_INVALID,
115
        -219 => DB_ERROR_NOSUCHTABLE,
116
        -297 => DB_ERROR_CONSTRAINT,
117
        -303 => DB_ERROR_INVALID,
118
        -413 => DB_ERROR_INVALID_NUMBER,
119
        -530 => DB_ERROR_CONSTRAINT,
120
        -551 => DB_ERROR_ACCESS_VIOLATION,
121
        -552 => DB_ERROR_ACCESS_VIOLATION,
122
        // -607 =>  // Covers too many errors, need to use regex on msg
123
        -625 => DB_ERROR_CONSTRAINT_NOT_NULL,
124
        -803 => DB_ERROR_CONSTRAINT,
125
        -804 => DB_ERROR_VALUE_COUNT_ON_ROW,
126
        // -902 =>  // Covers too many errors, need to use regex on msg
127
        -904 => DB_ERROR_CONNECT_FAILED,
128
        -922 => DB_ERROR_NOSUCHDB,
129
        -923 => DB_ERROR_CONNECT_FAILED,
130
        -924 => DB_ERROR_CONNECT_FAILED
131
    );
132
 
133
    /**
134
     * The raw database connection created by PHP
135
     * @var resource
136
     */
137
    var $connection;
138
 
139
    /**
140
     * The DSN information for connecting to a database
141
     * @var array
142
     */
143
    var $dsn = array();
144
 
145
 
146
    /**
147
     * The number of rows affected by a data manipulation query
148
     * @var integer
149
     * @access private
150
     */
151
    var $affected = 0;
152
 
153
    /**
154
     * Should data manipulation queries be committed automatically?
155
     * @var bool
156
     * @access private
157
     */
158
    var $autocommit = true;
159
 
160
    /**
161
     * The prepared statement handle from the most recently executed statement
162
     *
163
     * {@internal  Mainly here because the InterBase/Firebird API is only
164
     * able to retrieve data from result sets if the statemnt handle is
165
     * still in scope.}}
166
     *
167
     * @var resource
168
     */
169
    var $last_stmt;
170
 
171
    /**
172
     * Is the given prepared statement a data manipulation query?
173
     * @var array
174
     * @access private
175
     */
176
    var $manip_query = array();
177
 
178
 
179
    // }}}
180
    // {{{ constructor
181
 
182
    /**
183
     * This constructor calls <kbd>$this->DB_common()</kbd>
184
     *
185
     * @return void
186
     */
187
    function DB_ibase()
188
    {
189
        $this->DB_common();
190
    }
191
 
192
    // }}}
193
    // {{{ connect()
194
 
195
    /**
196
     * Connect to the database server, log in and open the database
197
     *
198
     * Don't call this method directly.  Use DB::connect() instead.
199
     *
200
     * PEAR DB's ibase driver supports the following extra DSN options:
201
     *   + buffers    The number of database buffers to allocate for the
202
     *                 server-side cache.
203
     *   + charset    The default character set for a database.
204
     *   + dialect    The default SQL dialect for any statement
205
     *                 executed within a connection.  Defaults to the
206
     *                 highest one supported by client libraries.
207
     *                 Functional only with InterBase 6 and up.
208
     *   + role       Functional only with InterBase 5 and up.
209
     *
210
     * @param array $dsn         the data source name
211
     * @param bool  $persistent  should the connection be persistent?
212
     *
213
     * @return int  DB_OK on success. A DB_Error object on failure.
214
     */
215
    function connect($dsn, $persistent = false)
216
    {
217
        if (!PEAR::loadExtension('interbase')) {
218
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
219
        }
220
 
221
        $this->dsn = $dsn;
222
        if ($dsn['dbsyntax']) {
223
            $this->dbsyntax = $dsn['dbsyntax'];
224
        }
225
        if ($this->dbsyntax == 'firebird') {
226
            $this->features['limit'] = 'alter';
227
        }
228
 
229
        $params = array(
230
            $dsn['hostspec']
231
                    ? ($dsn['hostspec'] . ':' . $dsn['database'])
232
                    : $dsn['database'],
233
            $dsn['username'] ? $dsn['username'] : null,
234
            $dsn['password'] ? $dsn['password'] : null,
235
            isset($dsn['charset']) ? $dsn['charset'] : null,
236
            isset($dsn['buffers']) ? $dsn['buffers'] : null,
237
            isset($dsn['dialect']) ? $dsn['dialect'] : null,
238
            isset($dsn['role'])    ? $dsn['role'] : null,
239
        );
240
 
241
        $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
242
 
243
        $this->connection = @call_user_func_array($connect_function, $params);
244
        if (!$this->connection) {
245
            return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED);
246
        }
247
        return DB_OK;
248
    }
249
 
250
    // }}}
251
    // {{{ disconnect()
252
 
253
    /**
254
     * Disconnects from the database server
255
     *
256
     * @return bool  TRUE on success, FALSE on failure
257
     */
258
    function disconnect()
259
    {
260
        $ret = @ibase_close($this->connection);
261
        $this->connection = null;
262
        return $ret;
263
    }
264
 
265
    // }}}
266
    // {{{ simpleQuery()
267
 
268
    /**
269
     * Sends a query to the database server
270
     *
271
     * @param string  the SQL query string
272
     *
273
     * @return mixed  + a PHP result resrouce for successful SELECT queries
274
     *                + the DB_OK constant for other successful queries
275
     *                + a DB_Error object on failure
276
     */
277
    function simpleQuery($query)
278
    {
279
        $ismanip = $this->_checkManip($query);
280
        $this->last_query = $query;
281
        $query = $this->modifyQuery($query);
282
        $result = @ibase_query($this->connection, $query);
283
 
284
        if (!$result) {
285
            return $this->ibaseRaiseError();
286
        }
287
        if ($this->autocommit && $ismanip) {
288
            @ibase_commit($this->connection);
289
        }
290
        if ($ismanip) {
291
            $this->affected = $result;
292
            return DB_OK;
293
        } else {
294
            $this->affected = 0;
295
            return $result;
296
        }
297
    }
298
 
299
    // }}}
300
    // {{{ modifyLimitQuery()
301
 
302
    /**
303
     * Adds LIMIT clauses to a query string according to current DBMS standards
304
     *
305
     * Only works with Firebird.
306
     *
307
     * @param string $query   the query to modify
308
     * @param int    $from    the row to start to fetching (0 = the first row)
309
     * @param int    $count   the numbers of rows to fetch
310
     * @param mixed  $params  array, string or numeric data to be used in
311
     *                         execution of the statement.  Quantity of items
312
     *                         passed must match quantity of placeholders in
313
     *                         query:  meaning 1 placeholder for non-array
314
     *                         parameters or 1 placeholder per array element.
315
     *
316
     * @return string  the query string with LIMIT clauses added
317
     *
318
     * @access protected
319
     */
320
    function modifyLimitQuery($query, $from, $count, $params = array())
321
    {
322
        if ($this->dsn['dbsyntax'] == 'firebird') {
323
            $query = preg_replace('/^([\s(])*SELECT/i',
324
                                  "SELECT FIRST $count SKIP $from", $query);
325
        }
326
        return $query;
327
    }
328
 
329
    // }}}
330
    // {{{ nextResult()
331
 
332
    /**
333
     * Move the internal ibase result pointer to the next available result
334
     *
335
     * @param a valid fbsql result resource
336
     *
337
     * @access public
338
     *
339
     * @return true if a result is available otherwise return false
340
     */
341
    function nextResult($result)
342
    {
343
        return false;
344
    }
345
 
346
    // }}}
347
    // {{{ fetchInto()
348
 
349
    /**
350
     * Places a row from the result set into the given array
351
     *
352
     * Formating of the array and the data therein are configurable.
353
     * See DB_result::fetchInto() for more information.
354
     *
355
     * This method is not meant to be called directly.  Use
356
     * DB_result::fetchInto() instead.  It can't be declared "protected"
357
     * because DB_result is a separate object.
358
     *
359
     * @param resource $result    the query result resource
360
     * @param array    $arr       the referenced array to put the data in
361
     * @param int      $fetchmode how the resulting array should be indexed
362
     * @param int      $rownum    the row number to fetch (0 = first row)
363
     *
364
     * @return mixed  DB_OK on success, NULL when the end of a result set is
365
     *                 reached or on failure
366
     *
367
     * @see DB_result::fetchInto()
368
     */
369
    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
370
    {
371
        if ($rownum !== null) {
372
            return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
373
        }
374
        if ($fetchmode & DB_FETCHMODE_ASSOC) {
375
            if (function_exists('ibase_fetch_assoc')) {
376
                $arr = @ibase_fetch_assoc($result);
377
            } else {
378
                $arr = get_object_vars(ibase_fetch_object($result));
379
            }
380
            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
381
                $arr = array_change_key_case($arr, CASE_LOWER);
382
            }
383
        } else {
384
            $arr = @ibase_fetch_row($result);
385
        }
386
        if (!$arr) {
387
            return null;
388
        }
389
        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
390
            $this->_rtrimArrayValues($arr);
391
        }
392
        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
393
            $this->_convertNullArrayValuesToEmpty($arr);
394
        }
395
        return DB_OK;
396
    }
397
 
398
    // }}}
399
    // {{{ freeResult()
400
 
401
    /**
402
     * Deletes the result set and frees the memory occupied by the result set
403
     *
404
     * This method is not meant to be called directly.  Use
405
     * DB_result::free() instead.  It can't be declared "protected"
406
     * because DB_result is a separate object.
407
     *
408
     * @param resource $result  PHP's query result resource
409
     *
410
     * @return bool  TRUE on success, FALSE if $result is invalid
411
     *
412
     * @see DB_result::free()
413
     */
414
    function freeResult($result)
415
    {
416
        return is_resource($result) ? ibase_free_result($result) : false;
417
    }
418
 
419
    // }}}
420
    // {{{ freeQuery()
421
 
422
    function freeQuery($query)
423
    {
424
        return is_resource($query) ? ibase_free_query($query) : false;
425
    }
426
 
427
    // }}}
428
    // {{{ affectedRows()
429
 
430
    /**
431
     * Determines the number of rows affected by a data maniuplation query
432
     *
433
     * 0 is returned for queries that don't manipulate data.
434
     *
435
     * @return int  the number of rows.  A DB_Error object on failure.
436
     */
437
    function affectedRows()
438
    {
439
        if (is_integer($this->affected)) {
440
            return $this->affected;
441
        }
442
        return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
443
    }
444
 
445
    // }}}
446
    // {{{ numCols()
447
 
448
    /**
449
     * Gets the number of columns in a result set
450
     *
451
     * This method is not meant to be called directly.  Use
452
     * DB_result::numCols() instead.  It can't be declared "protected"
453
     * because DB_result is a separate object.
454
     *
455
     * @param resource $result  PHP's query result resource
456
     *
457
     * @return int  the number of columns.  A DB_Error object on failure.
458
     *
459
     * @see DB_result::numCols()
460
     */
461
    function numCols($result)
462
    {
463
        $cols = @ibase_num_fields($result);
464
        if (!$cols) {
465
            return $this->ibaseRaiseError();
466
        }
467
        return $cols;
468
    }
469
 
470
    // }}}
471
    // {{{ prepare()
472
 
473
    /**
474
     * Prepares a query for multiple execution with execute().
475
     *
476
     * prepare() requires a generic query as string like <code>
477
     *    INSERT INTO numbers VALUES (?, ?, ?)
478
     * </code>.  The <kbd>?</kbd> characters are placeholders.
479
     *
480
     * Three types of placeholders can be used:
481
     *   + <kbd>?</kbd>  a quoted scalar value, i.e. strings, integers
482
     *   + <kbd>!</kbd>  value is inserted 'as is'
483
     *   + <kbd>&</kbd>  requires a file name.  The file's contents get
484
     *                     inserted into the query (i.e. saving binary
485
     *                     data in a db)
486
     *
487
     * Use backslashes to escape placeholder characters if you don't want
488
     * them to be interpreted as placeholders.  Example: <code>
489
     *    "UPDATE foo SET col=? WHERE col='over \& under'"
490
     * </code>
491
     *
492
     * @param string $query query to be prepared
493
     * @return mixed DB statement resource on success. DB_Error on failure.
494
     */
495
    function prepare($query)
496
    {
497
        $tokens   = preg_split('/((?<!\\\)[&?!])/', $query, -1,
498
                               PREG_SPLIT_DELIM_CAPTURE);
499
        $token    = 0;
500
        $types    = array();
501
        $newquery = '';
502
 
503
        foreach ($tokens as $key => $val) {
504
            switch ($val) {
505
                case '?':
506
                    $types[$token++] = DB_PARAM_SCALAR;
507
                    break;
508
                case '&':
509
                    $types[$token++] = DB_PARAM_OPAQUE;
510
                    break;
511
                case '!':
512
                    $types[$token++] = DB_PARAM_MISC;
513
                    break;
514
                default:
515
                    $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
516
                    $newquery .= $tokens[$key] . '?';
517
            }
518
        }
519
 
520
        $newquery = substr($newquery, 0, -1);
521
        $this->last_query = $query;
522
        $newquery = $this->modifyQuery($newquery);
523
        $stmt = @ibase_prepare($this->connection, $newquery);
524
 
525
        if ($stmt === false) {
526
            $stmt = $this->ibaseRaiseError();
527
        } else {
528
            $this->prepare_types[(int)$stmt] = $types;
529
            $this->manip_query[(int)$stmt]   = DB::isManip($query);
530
        }
531
 
532
        return $stmt;
533
    }
534
 
535
    // }}}
536
    // {{{ execute()
537
 
538
    /**
539
     * Executes a DB statement prepared with prepare().
540
     *
541
     * @param resource  $stmt  a DB statement resource returned from prepare()
542
     * @param mixed  $data  array, string or numeric data to be used in
543
     *                      execution of the statement.  Quantity of items
544
     *                      passed must match quantity of placeholders in
545
     *                      query:  meaning 1 for non-array items or the
546
     *                      quantity of elements in the array.
547
     * @return object  a new DB_Result or a DB_Error when fail
548
     * @see DB_ibase::prepare()
549
     * @access public
550
     */
551
    function &execute($stmt, $data = array())
552
    {
553
        $data = (array)$data;
554
        $this->last_parameters = $data;
555
 
556
        $types = $this->prepare_types[(int)$stmt];
557
        if (count($types) != count($data)) {
558
            $tmp = $this->raiseError(DB_ERROR_MISMATCH);
559
            return $tmp;
560
        }
561
 
562
        $i = 0;
563
        foreach ($data as $key => $value) {
564
            if ($types[$i] == DB_PARAM_MISC) {
565
                /*
566
                 * ibase doesn't seem to have the ability to pass a
567
                 * parameter along unchanged, so strip off quotes from start
568
                 * and end, plus turn two single quotes to one single quote,
569
                 * in order to avoid the quotes getting escaped by
570
                 * ibase and ending up in the database.
571
                 */
572
                $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
573
                $data[$key] = str_replace("''", "'", $data[$key]);
574
            } elseif ($types[$i] == DB_PARAM_OPAQUE) {
575
                $fp = @fopen($data[$key], 'rb');
576
                if (!$fp) {
577
                    $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
578
                    return $tmp;
579
                }
580
                $data[$key] = fread($fp, filesize($data[$key]));
581
                fclose($fp);
582
            }
583
            $i++;
584
        }
585
 
586
        array_unshift($data, $stmt);
587
 
588
        $res = call_user_func_array('ibase_execute', $data);
589
        if (!$res) {
590
            $tmp = $this->ibaseRaiseError();
591
            return $tmp;
592
        }
593
        /* XXX need this?
594
        if ($this->autocommit && $this->manip_query[(int)$stmt]) {
595
            @ibase_commit($this->connection);
596
        }*/
597
        $this->last_stmt = $stmt;
598
        if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) {
599
            $this->_last_query_manip = true;
600
            $this->_next_query_manip = false;
601
            $tmp = DB_OK;
602
        } else {
603
            $this->_last_query_manip = false;
604
            $tmp = new DB_result($this, $res);
605
        }
606
        return $tmp;
607
    }
608
 
609
    /**
610
     * Frees the internal resources associated with a prepared query
611
     *
612
     * @param resource $stmt           the prepared statement's PHP resource
613
     * @param bool     $free_resource  should the PHP resource be freed too?
614
     *                                  Use false if you need to get data
615
     *                                  from the result set later.
616
     *
617
     * @return bool  TRUE on success, FALSE if $result is invalid
618
     *
619
     * @see DB_ibase::prepare()
620
     */
621
    function freePrepared($stmt, $free_resource = true)
622
    {
623
        if (!is_resource($stmt)) {
624
            return false;
625
        }
626
        if ($free_resource) {
627
            @ibase_free_query($stmt);
628
        }
629
        unset($this->prepare_tokens[(int)$stmt]);
630
        unset($this->prepare_types[(int)$stmt]);
631
        unset($this->manip_query[(int)$stmt]);
632
        return true;
633
    }
634
 
635
    // }}}
636
    // {{{ autoCommit()
637
 
638
    /**
639
     * Enables or disables automatic commits
640
     *
641
     * @param bool $onoff  true turns it on, false turns it off
642
     *
643
     * @return int  DB_OK on success.  A DB_Error object if the driver
644
     *               doesn't support auto-committing transactions.
645
     */
646
    function autoCommit($onoff = false)
647
    {
648
        $this->autocommit = $onoff ? 1 : 0;
649
        return DB_OK;
650
    }
651
 
652
    // }}}
653
    // {{{ commit()
654
 
655
    /**
656
     * Commits the current transaction
657
     *
658
     * @return int  DB_OK on success.  A DB_Error object on failure.
659
     */
660
    function commit()
661
    {
662
        return @ibase_commit($this->connection);
663
    }
664
 
665
    // }}}
666
    // {{{ rollback()
667
 
668
    /**
669
     * Reverts the current transaction
670
     *
671
     * @return int  DB_OK on success.  A DB_Error object on failure.
672
     */
673
    function rollback()
674
    {
675
        return @ibase_rollback($this->connection);
676
    }
677
 
678
    // }}}
679
    // {{{ transactionInit()
680
 
681
    function transactionInit($trans_args = 0)
682
    {
683
        return $trans_args
684
                ? @ibase_trans($trans_args, $this->connection)
685
                : @ibase_trans();
686
    }
687
 
688
    // }}}
689
    // {{{ nextId()
690
 
691
    /**
692
     * Returns the next free id in a sequence
693
     *
694
     * @param string  $seq_name  name of the sequence
695
     * @param boolean $ondemand  when true, the seqence is automatically
696
     *                            created if it does not exist
697
     *
698
     * @return int  the next id number in the sequence.
699
     *               A DB_Error object on failure.
700
     *
701
     * @see DB_common::nextID(), DB_common::getSequenceName(),
702
     *      DB_ibase::createSequence(), DB_ibase::dropSequence()
703
     */
704
    function nextId($seq_name, $ondemand = true)
705
    {
706
        $sqn = strtoupper($this->getSequenceName($seq_name));
707
        $repeat = 0;
708
        do {
709
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
710
            $result = $this->query("SELECT GEN_ID(${sqn}, 1) "
711
                                   . 'FROM RDB$GENERATORS '
712
                                   . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
713
            $this->popErrorHandling();
714
            if ($ondemand && DB::isError($result)) {
715
                $repeat = 1;
716
                $result = $this->createSequence($seq_name);
717
                if (DB::isError($result)) {
718
                    return $result;
719
                }
720
            } else {
721
                $repeat = 0;
722
            }
723
        } while ($repeat);
724
        if (DB::isError($result)) {
725
            return $this->raiseError($result);
726
        }
727
        $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
728
        $result->free();
729
        return $arr[0];
730
    }
731
 
732
    // }}}
733
    // {{{ createSequence()
734
 
735
    /**
736
     * Creates a new sequence
737
     *
738
     * @param string $seq_name  name of the new sequence
739
     *
740
     * @return int  DB_OK on success.  A DB_Error object on failure.
741
     *
742
     * @see DB_common::createSequence(), DB_common::getSequenceName(),
743
     *      DB_ibase::nextID(), DB_ibase::dropSequence()
744
     */
745
    function createSequence($seq_name)
746
    {
747
        $sqn = strtoupper($this->getSequenceName($seq_name));
748
        $this->pushErrorHandling(PEAR_ERROR_RETURN);
749
        $result = $this->query("CREATE GENERATOR ${sqn}");
750
        $this->popErrorHandling();
751
 
752
        return $result;
753
    }
754
 
755
    // }}}
756
    // {{{ dropSequence()
757
 
758
    /**
759
     * Deletes a sequence
760
     *
761
     * @param string $seq_name  name of the sequence to be deleted
762
     *
763
     * @return int  DB_OK on success.  A DB_Error object on failure.
764
     *
765
     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
766
     *      DB_ibase::nextID(), DB_ibase::createSequence()
767
     */
768
    function dropSequence($seq_name)
769
    {
770
        return $this->query('DELETE FROM RDB$GENERATORS '
771
                            . "WHERE RDB\$GENERATOR_NAME='"
772
                            . strtoupper($this->getSequenceName($seq_name))
773
                            . "'");
774
    }
775
 
776
    // }}}
777
    // {{{ _ibaseFieldFlags()
778
 
779
    /**
780
     * Get the column's flags
781
     *
782
     * Supports "primary_key", "unique_key", "not_null", "default",
783
     * "computed" and "blob".
784
     *
785
     * @param string $field_name  the name of the field
786
     * @param string $table_name  the name of the table
787
     *
788
     * @return string  the flags
789
     *
790
     * @access private
791
     */
792
    function _ibaseFieldFlags($field_name, $table_name)
793
    {
794
        $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE'
795
               .' FROM RDB$INDEX_SEGMENTS I'
796
               .'  JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
797
               .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\''
798
               .'  AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'';
799
 
800
        $result = @ibase_query($this->connection, $sql);
801
        if (!$result) {
802
            return $this->ibaseRaiseError();
803
        }
804
 
805
        $flags = '';
806
        if ($obj = @ibase_fetch_object($result)) {
807
            @ibase_free_result($result);
808
            if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'PRIMARY KEY') {
809
                $flags .= 'primary_key ';
810
            }
811
            if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'UNIQUE') {
812
                $flags .= 'unique_key ';
813
            }
814
        }
815
 
816
        $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,'
817
               .'  R.RDB$DEFAULT_SOURCE AS DSOURCE,'
818
               .'  F.RDB$FIELD_TYPE AS FTYPE,'
819
               .'  F.RDB$COMPUTED_SOURCE AS CSOURCE'
820
               .' FROM RDB$RELATION_FIELDS R '
821
               .'  JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
822
               .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''
823
               .'  AND R.RDB$FIELD_NAME=\'' . $field_name . '\'';
824
 
825
        $result = @ibase_query($this->connection, $sql);
826
        if (!$result) {
827
            return $this->ibaseRaiseError();
828
        }
829
        if ($obj = @ibase_fetch_object($result)) {
830
            @ibase_free_result($result);
831
            if (isset($obj->NFLAG)) {
832
                $flags .= 'not_null ';
833
            }
834
            if (isset($obj->DSOURCE)) {
835
                $flags .= 'default ';
836
            }
837
            if (isset($obj->CSOURCE)) {
838
                $flags .= 'computed ';
839
            }
840
            if (isset($obj->FTYPE)  && $obj->FTYPE == 261) {
841
                $flags .= 'blob ';
842
            }
843
        }
844
 
845
        return trim($flags);
846
    }
847
 
848
    // }}}
849
    // {{{ ibaseRaiseError()
850
 
851
    /**
852
     * Produces a DB_Error object regarding the current problem
853
     *
854
     * @param int $errno  if the error is being manually raised pass a
855
     *                     DB_ERROR* constant here.  If this isn't passed
856
     *                     the error information gathered from the DBMS.
857
     *
858
     * @return object  the DB_Error object
859
     *
860
     * @see DB_common::raiseError(),
861
     *      DB_ibase::errorNative(), DB_ibase::errorCode()
862
     */
863
    function &ibaseRaiseError($errno = null)
864
    {
865
        if ($errno === null) {
866
            $errno = $this->errorCode($this->errorNative());
867
        }
868
        $tmp = $this->raiseError($errno, null, null, null, @ibase_errmsg());
869
        return $tmp;
870
    }
871
 
872
    // }}}
873
    // {{{ errorNative()
874
 
875
    /**
876
     * Gets the DBMS' native error code produced by the last query
877
     *
878
     * @return int  the DBMS' error code.  NULL if there is no error code.
879
     *
880
     * @since Method available since Release 1.7.0
881
     */
882
    function errorNative()
883
    {
884
        if (function_exists('ibase_errcode')) {
885
            return @ibase_errcode();
886
        }
887
        if (preg_match('/^Dynamic SQL Error SQL error code = ([0-9-]+)/i',
888
                       @ibase_errmsg(), $m)) {
889
            return (int)$m[1];
890
        }
891
        return null;
892
    }
893
 
894
    // }}}
895
    // {{{ errorCode()
896
 
897
    /**
898
     * Maps native error codes to DB's portable ones
899
     *
900
     * @param int $nativecode  the error code returned by the DBMS
901
     *
902
     * @return int  the portable DB error code.  Return DB_ERROR if the
903
     *               current driver doesn't have a mapping for the
904
     *               $nativecode submitted.
905
     *
906
     * @since Method available since Release 1.7.0
907
     */
908
    function errorCode($nativecode = null)
909
    {
910
        if (isset($this->errorcode_map[$nativecode])) {
911
            return $this->errorcode_map[$nativecode];
912
        }
913
 
914
        static $error_regexps;
915
        if (!isset($error_regexps)) {
916
            $error_regexps = array(
917
                '/generator .* is not defined/'
918
                    => DB_ERROR_SYNTAX,  // for compat. w ibase_errcode()
919
                '/table.*(not exist|not found|unknown)/i'
920
                    => DB_ERROR_NOSUCHTABLE,
921
                '/table .* already exists/i'
922
                    => DB_ERROR_ALREADY_EXISTS,
923
                '/unsuccessful metadata update .* failed attempt to store duplicate value/i'
924
                    => DB_ERROR_ALREADY_EXISTS,
925
                '/unsuccessful metadata update .* not found/i'
926
                    => DB_ERROR_NOT_FOUND,
927
                '/validation error for column .* value "\*\*\* null/i'
928
                    => DB_ERROR_CONSTRAINT_NOT_NULL,
929
                '/violation of [\w ]+ constraint/i'
930
                    => DB_ERROR_CONSTRAINT,
931
                '/conversion error from string/i'
932
                    => DB_ERROR_INVALID_NUMBER,
933
                '/no permission for/i'
934
                    => DB_ERROR_ACCESS_VIOLATION,
935
                '/arithmetic exception, numeric overflow, or string truncation/i'
936
                    => DB_ERROR_INVALID,
937
                '/feature is not supported/i'
938
                    => DB_ERROR_NOT_CAPABLE,
939
            );
940
        }
941
 
942
        $errormsg = @ibase_errmsg();
943
        foreach ($error_regexps as $regexp => $code) {
944
            if (preg_match($regexp, $errormsg)) {
945
                return $code;
946
            }
947
        }
948
        return DB_ERROR;
949
    }
950
 
951
    // }}}
952
    // {{{ tableInfo()
953
 
954
    /**
955
     * Returns information about a table or a result set
956
     *
957
     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
958
     * is a table name.
959
     *
960
     * @param object|string  $result  DB_result object from a query or a
961
     *                                 string containing the name of a table.
962
     *                                 While this also accepts a query result
963
     *                                 resource identifier, this behavior is
964
     *                                 deprecated.
965
     * @param int            $mode    a valid tableInfo mode
966
     *
967
     * @return array  an associative array with the information requested.
968
     *                 A DB_Error object on failure.
969
     *
970
     * @see DB_common::tableInfo()
971
     */
972
    function tableInfo($result, $mode = null)
973
    {
974
        if (is_string($result)) {
975
            /*
976
             * Probably received a table name.
977
             * Create a result resource identifier.
978
             */
979
            $id = @ibase_query($this->connection,
980
                               "SELECT * FROM $result WHERE 1=0");
981
            $got_string = true;
982
        } elseif (isset($result->result)) {
983
            /*
984
             * Probably received a result object.
985
             * Extract the result resource identifier.
986
             */
987
            $id = $result->result;
988
            $got_string = false;
989
        } else {
990
            /*
991
             * Probably received a result resource identifier.
992
             * Copy it.
993
             * Deprecated.  Here for compatibility only.
994
             */
995
            $id = $result;
996
            $got_string = false;
997
        }
998
 
999
        if (!is_resource($id)) {
1000
            return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA);
1001
        }
1002
 
1003
        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
1004
            $case_func = 'strtolower';
1005
        } else {
1006
            $case_func = 'strval';
1007
        }
1008
 
1009
        $count = @ibase_num_fields($id);
1010
        $res   = array();
1011
 
1012
        if ($mode) {
1013
            $res['num_fields'] = $count;
1014
        }
1015
 
1016
        for ($i = 0; $i < $count; $i++) {
1017
            $info = @ibase_field_info($id, $i);
1018
            $res[$i] = array(
1019
                'table' => $got_string ? $case_func($result) : '',
1020
                'name'  => $case_func($info['name']),
1021
                'type'  => $info['type'],
1022
                'len'   => $info['length'],
1023
                'flags' => ($got_string)
1024
                            ? $this->_ibaseFieldFlags($info['name'], $result)
1025
                            : '',
1026
            );
1027
            if ($mode & DB_TABLEINFO_ORDER) {
1028
                $res['order'][$res[$i]['name']] = $i;
1029
            }
1030
            if ($mode & DB_TABLEINFO_ORDERTABLE) {
1031
                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
1032
            }
1033
        }
1034
 
1035
        // free the result only if we were called on a table
1036
        if ($got_string) {
1037
            @ibase_free_result($id);
1038
        }
1039
        return $res;
1040
    }
1041
 
1042
    // }}}
1043
    // {{{ getSpecialQuery()
1044
 
1045
    /**
1046
     * Obtains the query string needed for listing a given type of objects
1047
     *
1048
     * @param string $type  the kind of objects you want to retrieve
1049
     *
1050
     * @return string  the SQL query string or null if the driver doesn't
1051
     *                  support the object type requested
1052
     *
1053
     * @access protected
1054
     * @see DB_common::getListOf()
1055
     */
1056
    function getSpecialQuery($type)
1057
    {
1058
        switch ($type) {
1059
            case 'tables':
1060
                return 'SELECT DISTINCT R.RDB$RELATION_NAME FROM '
1061
                       . 'RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0';
1062
            case 'views':
1063
                return 'SELECT DISTINCT RDB$VIEW_NAME from RDB$VIEW_RELATIONS';
1064
            case 'users':
1065
                return 'SELECT DISTINCT RDB$USER FROM RDB$USER_PRIVILEGES';
1066
            default:
1067
                return null;
1068
        }
1069
    }
1070
 
1071
    // }}}
1072
 
1073
}
1074
 
1075
/*
1076
 * Local variables:
1077
 * tab-width: 4
1078
 * c-basic-offset: 4
1079
 * End:
1080
 */
1081
 
1082
?>