~ubuntu-branches/ubuntu/trusty/fusionforge/trusty

« back to all changes in this revision

Viewing changes to plugins/wiki/www/lib/pear/DB/ibase.php

  • Committer: Bazaar Package Importer
  • Author(s): Roland Mas
  • Date: 2011-04-15 14:55:34 UTC
  • mfrom: (4.1.10 sid)
  • Revision ID: james.westby@ubuntu.com-20110415145534-mvn1nochufjmw177
Tags: 5.0.3-1
New upstream bugfix release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
 
3
// +----------------------------------------------------------------------+
 
4
// | PHP Version 4                                                        |
 
5
// +----------------------------------------------------------------------+
 
6
// | Copyright (c) 1997-2004 The PHP Group                                |
 
7
// +----------------------------------------------------------------------+
 
8
// | This source file is subject to version 2.02 of the PHP license,      |
 
9
// | that is bundled with this package in the file LICENSE, and is        |
 
10
// | available at through the world-wide-web at                           |
 
11
// | http://www.php.net/license/2_02.txt.                                 |
 
12
// | If you did not receive a copy of the PHP license and are unable to   |
 
13
// | obtain it through the world-wide-web, please send a note to          |
 
14
// | license@php.net so we can mail you a copy immediately.               |
 
15
// +----------------------------------------------------------------------+
 
16
// | Author: Sterling Hughes <sterling@php.net>                           |
 
17
// | Maintainer: Daniel Convissor <danielc@php.net>                       |
 
18
// +----------------------------------------------------------------------+
 
19
//
 
20
// $Id: ibase.php 6184 2008-08-22 10:33:41Z vargenau $
 
21
 
 
22
 
 
23
// Bugs:
 
24
//  - If dbsyntax is not firebird, the limitQuery may fail
 
25
 
 
26
 
 
27
require_once 'DB/common.php';
 
28
 
 
29
/**
 
30
 * Database independent query interface definition for PHP's Interbase
 
31
 * extension.
 
32
 *
 
33
 * @package  DB
 
34
 * @version  $Id: ibase.php 6184 2008-08-22 10:33:41Z vargenau $
 
35
 * @category Database
 
36
 * @author   Sterling Hughes <sterling@php.net>
 
37
 */
 
38
class DB_ibase extends DB_common
 
39
{
 
40
 
 
41
    // {{{ properties
 
42
 
 
43
    var $connection;
 
44
    var $phptype, $dbsyntax;
 
45
    var $autocommit = 1;
 
46
    var $manip_query = array();
 
47
 
 
48
    // }}}
 
49
    // {{{ constructor
 
50
 
 
51
    function DB_ibase()
 
52
    {
 
53
        $this->DB_common();
 
54
        $this->phptype = 'ibase';
 
55
        $this->dbsyntax = 'ibase';
 
56
        $this->features = array(
 
57
            'prepare' => true,
 
58
            'pconnect' => true,
 
59
            'transactions' => true,
 
60
            'limit' => false
 
61
        );
 
62
        // just a few of the tons of Interbase error codes listed in the
 
63
        // Language Reference section of the Interbase manual
 
64
        $this->errorcode_map = array(
 
65
            -104 => DB_ERROR_SYNTAX,
 
66
            -150 => DB_ERROR_ACCESS_VIOLATION,
 
67
            -151 => DB_ERROR_ACCESS_VIOLATION,
 
68
            -155 => DB_ERROR_NOSUCHTABLE,
 
69
            88   => DB_ERROR_NOSUCHTABLE,
 
70
            -157 => DB_ERROR_NOSUCHFIELD,
 
71
            -158 => DB_ERROR_VALUE_COUNT_ON_ROW,
 
72
            -170 => DB_ERROR_MISMATCH,
 
73
            -171 => DB_ERROR_MISMATCH,
 
74
            -172 => DB_ERROR_INVALID,
 
75
            -204 => DB_ERROR_INVALID,
 
76
            -205 => DB_ERROR_NOSUCHFIELD,
 
77
            -206 => DB_ERROR_NOSUCHFIELD,
 
78
            -208 => DB_ERROR_INVALID,
 
79
            -219 => DB_ERROR_NOSUCHTABLE,
 
80
            -297 => DB_ERROR_CONSTRAINT,
 
81
            -530 => DB_ERROR_CONSTRAINT,
 
82
            -607 => DB_ERROR_NOSUCHTABLE,
 
83
            -803 => DB_ERROR_CONSTRAINT,
 
84
            -551 => DB_ERROR_ACCESS_VIOLATION,
 
85
            -552 => DB_ERROR_ACCESS_VIOLATION,
 
86
            -922 => DB_ERROR_NOSUCHDB,
 
87
            -923 => DB_ERROR_CONNECT_FAILED,
 
88
            -924 => DB_ERROR_CONNECT_FAILED
 
89
        );
 
90
    }
 
91
 
 
92
    // }}}
 
93
    // {{{ connect()
 
94
 
 
95
    function connect($dsninfo, $persistent = false)
 
96
    {
 
97
        if (!DB::assertExtension('interbase')) {
 
98
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
 
99
        }
 
100
        $this->dsn = $dsninfo;
 
101
        $dbhost = $dsninfo['hostspec'] ?
 
102
                  ($dsninfo['hostspec'] . ':' . $dsninfo['database']) :
 
103
                  $dsninfo['database'];
 
104
 
 
105
        $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
 
106
 
 
107
        $params = array();
 
108
        $params[] = $dbhost;
 
109
        $params[] = $dsninfo['username'] ? $dsninfo['username'] : null;
 
110
        $params[] = $dsninfo['password'] ? $dsninfo['password'] : null;
 
111
        $params[] = isset($dsninfo['charset']) ? $dsninfo['charset'] : null;
 
112
        $params[] = isset($dsninfo['buffers']) ? $dsninfo['buffers'] : null;
 
113
        $params[] = isset($dsninfo['dialect']) ? $dsninfo['dialect'] : null;
 
114
        $params[] = isset($dsninfo['role'])    ? $dsninfo['role'] : null;
 
115
 
 
116
        $conn = @call_user_func_array($connect_function, $params);
 
117
        if (!$conn) {
 
118
            return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED);
 
119
        }
 
120
        $this->connection = $conn;
 
121
        if ($this->dsn['dbsyntax'] == 'firebird') {
 
122
            $this->features['limit'] = 'alter';
 
123
        }
 
124
        return DB_OK;
 
125
    }
 
126
 
 
127
    // }}}
 
128
    // {{{ disconnect()
 
129
 
 
130
    function disconnect()
 
131
    {
 
132
        $ret = @ibase_close($this->connection);
 
133
        $this->connection = null;
 
134
        return $ret;
 
135
    }
 
136
 
 
137
    // }}}
 
138
    // {{{ simpleQuery()
 
139
 
 
140
    function simpleQuery($query)
 
141
    {
 
142
        $ismanip = DB::isManip($query);
 
143
        $this->last_query = $query;
 
144
        $query = $this->modifyQuery($query);
 
145
        $result = @ibase_query($this->connection, $query);
 
146
        if (!$result) {
 
147
            return $this->ibaseRaiseError();
 
148
        }
 
149
        if ($this->autocommit && $ismanip) {
 
150
            @ibase_commit($this->connection);
 
151
        }
 
152
        // Determine which queries that should return data, and which
 
153
        // should return an error code only.
 
154
        return $ismanip ? DB_OK : $result;
 
155
    }
 
156
 
 
157
    // }}}
 
158
    // {{{ modifyLimitQuery()
 
159
 
 
160
    /**
 
161
     * This method is used by backends to alter limited queries
 
162
     * Uses the new FIRST n SKIP n Firebird 1.0 syntax, so it is
 
163
     * only compatible with Firebird 1.x
 
164
     *
 
165
     * @param string  $query query to modify
 
166
     * @param integer $from  the row to start to fetching
 
167
     * @param integer $count the numbers of rows to fetch
 
168
     *
 
169
     * @return the new (modified) query
 
170
     * @author Ludovico Magnocavallo <ludo@sumatrasolutions.com>
 
171
     * @access private
 
172
     */
 
173
    function modifyLimitQuery($query, $from, $count)
 
174
    {
 
175
        if ($this->dsn['dbsyntax'] == 'firebird') {
 
176
            //$from++; // SKIP starts from 1, ie SKIP 1 starts from the first record
 
177
                           // (cox) Seems that SKIP starts in 0
 
178
            $query = preg_replace('/^\s*select\s(.*)$/is',
 
179
                                  "SELECT FIRST $count SKIP $from $1", $query);
 
180
        }
 
181
        return $query;
 
182
    }
 
183
 
 
184
    // }}}
 
185
    // {{{ nextResult()
 
186
 
 
187
    /**
 
188
     * Move the internal ibase result pointer to the next available result
 
189
     *
 
190
     * @param a valid fbsql result resource
 
191
     *
 
192
     * @access public
 
193
     *
 
194
     * @return true if a result is available otherwise return false
 
195
     */
 
196
    function nextResult($result)
 
197
    {
 
198
        return false;
 
199
    }
 
200
 
 
201
    // }}}
 
202
    // {{{ fetchInto()
 
203
 
 
204
    /**
 
205
     * Fetch a row and insert the data into an existing array.
 
206
     *
 
207
     * Formating of the array and the data therein are configurable.
 
208
     * See DB_result::fetchInto() for more information.
 
209
     *
 
210
     * @param resource $result    query result identifier
 
211
     * @param array    $arr       (reference) array where data from the row
 
212
     *                            should be placed
 
213
     * @param int      $fetchmode how the resulting array should be indexed
 
214
     * @param int      $rownum    the row number to fetch
 
215
     *
 
216
     * @return mixed DB_OK on success, null when end of result set is
 
217
     *               reached or on failure
 
218
     *
 
219
     * @see DB_result::fetchInto()
 
220
     * @access private
 
221
     */
 
222
    function fetchInto($result, &$arr, $fetchmode, $rownum=null)
 
223
    {
 
224
        if ($rownum !== null) {
 
225
            return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
 
226
        }
 
227
        if ($fetchmode & DB_FETCHMODE_ASSOC) {
 
228
            if (function_exists('ibase_fetch_assoc')) {
 
229
                $arr = @ibase_fetch_assoc($result);
 
230
            } else {
 
231
                $arr = get_object_vars(ibase_fetch_object($result));
 
232
            }
 
233
            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
 
234
                $arr = array_change_key_case($arr, CASE_LOWER);
 
235
            }
 
236
        } else {
 
237
            $arr = @ibase_fetch_row($result);
 
238
        }
 
239
        if (!$arr) {
 
240
            if ($errmsg = @ibase_errmsg()) {
 
241
                return $this->ibaseRaiseError(null, $errmsg);
 
242
            } else {
 
243
                return null;
 
244
            }
 
245
        }
 
246
        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
 
247
            $this->_rtrimArrayValues($arr);
 
248
        }
 
249
        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
 
250
            $this->_convertNullArrayValuesToEmpty($arr);
 
251
        }
 
252
        return DB_OK;
 
253
    }
 
254
 
 
255
    // }}}
 
256
    // {{{ freeResult()
 
257
 
 
258
    function freeResult($result)
 
259
    {
 
260
        return @ibase_free_result($result);
 
261
    }
 
262
 
 
263
    // }}}
 
264
    // {{{ freeQuery()
 
265
 
 
266
    function freeQuery($query)
 
267
    {
 
268
        @ibase_free_query($query);
 
269
        return true;
 
270
    }
 
271
 
 
272
    // }}}
 
273
    // {{{ numCols()
 
274
 
 
275
    function numCols($result)
 
276
    {
 
277
        $cols = @ibase_num_fields($result);
 
278
        if (!$cols) {
 
279
            return $this->ibaseRaiseError();
 
280
        }
 
281
        return $cols;
 
282
    }
 
283
 
 
284
    // }}}
 
285
    // {{{ prepare()
 
286
 
 
287
    /**
 
288
     * Prepares a query for multiple execution with execute().
 
289
     *
 
290
     * prepare() requires a generic query as string like <code>
 
291
     *    INSERT INTO numbers VALUES (?, ?, ?)
 
292
     * </code>.  The <kbd>?</kbd> characters are placeholders.
 
293
     *
 
294
     * Three types of placeholders can be used:
 
295
     *   + <kbd>?</kbd>  a quoted scalar value, i.e. strings, integers
 
296
     *   + <kbd>!</kbd>  value is inserted 'as is'
 
297
     *   + <kbd>&</kbd>  requires a file name.  The file's contents get
 
298
     *                     inserted into the query (i.e. saving binary
 
299
     *                     data in a db)
 
300
     *
 
301
     * Use backslashes to escape placeholder characters if you don't want
 
302
     * them to be interpreted as placeholders.  Example: <code>
 
303
     *    "UPDATE foo SET col=? WHERE col='over \& under'"
 
304
     * </code>
 
305
     *
 
306
     * @param string $query query to be prepared
 
307
     * @return mixed DB statement resource on success. DB_Error on failure.
 
308
     */
 
309
    function prepare($query)
 
310
    {
 
311
        $tokens   = preg_split('/((?<!\\\)[&?!])/', $query, -1,
 
312
                               PREG_SPLIT_DELIM_CAPTURE);
 
313
        $token    = 0;
 
314
        $types    = array();
 
315
        $newquery = '';
 
316
 
 
317
        foreach ($tokens as $key => $val) {
 
318
            switch ($val) {
 
319
                case '?':
 
320
                    $types[$token++] = DB_PARAM_SCALAR;
 
321
                    break;
 
322
                case '&':
 
323
                    $types[$token++] = DB_PARAM_OPAQUE;
 
324
                    break;
 
325
                case '!':
 
326
                    $types[$token++] = DB_PARAM_MISC;
 
327
                    break;
 
328
                default:
 
329
                    $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
 
330
                    $newquery .= $tokens[$key] . '?';
 
331
            }
 
332
        }
 
333
 
 
334
        $newquery = substr($newquery, 0, -1);
 
335
        $this->last_query = $query;
 
336
        $newquery = $this->modifyQuery($newquery);
 
337
        $stmt = @ibase_prepare($this->connection, $newquery);
 
338
        $this->prepare_types[(int)$stmt] = $types;
 
339
        $this->manip_query[(int)$stmt]   = DB::isManip($query);
 
340
        return $stmt;
 
341
    }
 
342
 
 
343
    // }}}
 
344
    // {{{ execute()
 
345
 
 
346
    /**
 
347
     * Executes a DB statement prepared with prepare().
 
348
     *
 
349
     * @param resource  $stmt  a DB statement resource returned from prepare()
 
350
     * @param mixed  $data  array, string or numeric data to be used in
 
351
     *                      execution of the statement.  Quantity of items
 
352
     *                      passed must match quantity of placeholders in
 
353
     *                      query:  meaning 1 for non-array items or the
 
354
     *                      quantity of elements in the array.
 
355
     * @return object  a new DB_Result or a DB_Error when fail
 
356
     * @see DB_ibase::prepare()
 
357
     * @access public
 
358
     */
 
359
    function &execute($stmt, $data = array())
 
360
    {
 
361
        if (!is_array($data)) {
 
362
            $data = array($data);
 
363
        }
 
364
 
 
365
        $types =& $this->prepare_types[$stmt];
 
366
        if (count($types) != count($data)) {
 
367
            $tmp =& $this->raiseError(DB_ERROR_MISMATCH);
 
368
            return $tmp;
 
369
        }
 
370
 
 
371
        $i = 0;
 
372
        foreach ($data as $key => $value) {
 
373
            if ($types[$i] == DB_PARAM_MISC) {
 
374
                /*
 
375
                 * ibase doesn't seem to have the ability to pass a
 
376
                 * parameter along unchanged, so strip off quotes from start
 
377
                 * and end, plus turn two single quotes to one single quote,
 
378
                 * in order to avoid the quotes getting escaped by
 
379
                 * ibase and ending up in the database.
 
380
                 */
 
381
                $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
 
382
                $data[$key] = str_replace("''", "'", $data[$key]);
 
383
            } elseif ($types[$i] == DB_PARAM_OPAQUE) {
 
384
                $fp = @fopen($data[$key], 'rb');
 
385
                if (!$fp) {
 
386
                    $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
 
387
                    return $tmp;
 
388
                }
 
389
                $data[$key] = fread($fp, filesize($data[$key]));
 
390
                fclose($fp);
 
391
            }
 
392
            $i++;
 
393
        }
 
394
 
 
395
        array_unshift($data, $stmt);
 
396
 
 
397
        $res = call_user_func_array('ibase_execute', $data);
 
398
        if (!$res) {
 
399
            $tmp =& $this->ibaseRaiseError();
 
400
            return $tmp;
 
401
        }
 
402
        /* XXX need this?
 
403
        if ($this->autocommit && $this->manip_query[(int)$stmt]) {
 
404
            @ibase_commit($this->connection);
 
405
        }*/
 
406
        if ($this->manip_query[(int)$stmt]) {
 
407
            $tmp = DB_OK;
 
408
        } else {
 
409
            $tmp =& new DB_result($this, $res);
 
410
        }
 
411
        return $tmp;
 
412
    }
 
413
 
 
414
    /**
 
415
     * Free the internal resources associated with a prepared query.
 
416
     *
 
417
     * @param $stmt The interbase_query resource type
 
418
     *
 
419
     * @return bool true on success, false if $result is invalid
 
420
     */
 
421
    function freePrepared($stmt)
 
422
    {
 
423
        if (!is_resource($stmt)) {
 
424
            return false;
 
425
        }
 
426
        @ibase_free_query($stmt);
 
427
        unset($this->prepare_tokens[(int)$stmt]);
 
428
        unset($this->prepare_types[(int)$stmt]);
 
429
        unset($this->manip_query[(int)$stmt]);
 
430
        return true;
 
431
    }
 
432
 
 
433
    // }}}
 
434
    // {{{ autoCommit()
 
435
 
 
436
    function autoCommit($onoff = false)
 
437
    {
 
438
        $this->autocommit = $onoff ? 1 : 0;
 
439
        return DB_OK;
 
440
    }
 
441
 
 
442
    // }}}
 
443
    // {{{ commit()
 
444
 
 
445
    function commit()
 
446
    {
 
447
        return @ibase_commit($this->connection);
 
448
    }
 
449
 
 
450
    // }}}
 
451
    // {{{ rollback()
 
452
 
 
453
    function rollback()
 
454
    {
 
455
        return @ibase_rollback($this->connection);
 
456
    }
 
457
 
 
458
    // }}}
 
459
    // {{{ transactionInit()
 
460
 
 
461
    function transactionInit($trans_args = 0)
 
462
    {
 
463
        return $trans_args ? @ibase_trans($trans_args, $this->connection) : @ibase_trans();
 
464
    }
 
465
 
 
466
    // }}}
 
467
    // {{{ nextId()
 
468
 
 
469
    /**
 
470
     * Returns the next free id in a sequence
 
471
     *
 
472
     * @param string  $seq_name  name of the sequence
 
473
     * @param boolean $ondemand  when true, the seqence is automatically
 
474
     *                           created if it does not exist
 
475
     *
 
476
     * @return int  the next id number in the sequence.  DB_Error if problem.
 
477
     *
 
478
     * @internal
 
479
     * @see DB_common::nextID()
 
480
     * @access public
 
481
     */
 
482
    function nextId($seq_name, $ondemand = true)
 
483
    {
 
484
        $sqn = strtoupper($this->getSequenceName($seq_name));
 
485
        $repeat = 0;
 
486
        do {
 
487
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
 
488
            $result =& $this->query("SELECT GEN_ID(${sqn}, 1) "
 
489
                                   . 'FROM RDB$GENERATORS '
 
490
                                   . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
 
491
            $this->popErrorHandling();
 
492
            if ($ondemand && DB::isError($result)) {
 
493
                $repeat = 1;
 
494
                $result = $this->createSequence($seq_name);
 
495
                if (DB::isError($result)) {
 
496
                    return $result;
 
497
                }
 
498
            } else {
 
499
                $repeat = 0;
 
500
            }
 
501
        } while ($repeat);
 
502
        if (DB::isError($result)) {
 
503
            return $this->raiseError($result);
 
504
        }
 
505
        $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
 
506
        $result->free();
 
507
        return $arr[0];
 
508
    }
 
509
 
 
510
    // }}}
 
511
    // {{{ createSequence()
 
512
 
 
513
    /**
 
514
     * Create the sequence
 
515
     *
 
516
     * @param string $seq_name the name of the sequence
 
517
     * @return mixed DB_OK on success or DB error on error
 
518
     * @access public
 
519
     */
 
520
    function createSequence($seq_name)
 
521
    {
 
522
        $sqn = strtoupper($this->getSequenceName($seq_name));
 
523
        $this->pushErrorHandling(PEAR_ERROR_RETURN);
 
524
        $result = $this->query("CREATE GENERATOR ${sqn}");
 
525
        $this->popErrorHandling();
 
526
 
 
527
        return $result;
 
528
    }
 
529
 
 
530
    // }}}
 
531
    // {{{ dropSequence()
 
532
 
 
533
    /**
 
534
     * Drop a sequence
 
535
     *
 
536
     * @param string $seq_name the name of the sequence
 
537
     * @return mixed DB_OK on success or DB error on error
 
538
     * @access public
 
539
     */
 
540
    function dropSequence($seq_name)
 
541
    {
 
542
        $sqn = strtoupper($this->getSequenceName($seq_name));
 
543
        return $this->query('DELETE FROM RDB$GENERATORS '
 
544
                            . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
 
545
    }
 
546
 
 
547
    // }}}
 
548
    // {{{ _ibaseFieldFlags()
 
549
 
 
550
    /**
 
551
     * get the Flags of a Field
 
552
     *
 
553
     * @param string $field_name the name of the field
 
554
     * @param string $table_name the name of the table
 
555
     *
 
556
     * @return string The flags of the field ("primary_key", "unique_key", "not_null"
 
557
     *                "default", "computed" and "blob" are supported)
 
558
     * @access private
 
559
     */
 
560
    function _ibaseFieldFlags($field_name, $table_name)
 
561
    {
 
562
        $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE'
 
563
               .' FROM RDB$INDEX_SEGMENTS I'
 
564
               .'  JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
 
565
               .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\''
 
566
               .'  AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'';
 
567
 
 
568
        $result = @ibase_query($this->connection, $sql);
 
569
        if (!$result) {
 
570
            return $this->ibaseRaiseError();
 
571
        }
 
572
 
 
573
        $flags = '';
 
574
        if ($obj = @ibase_fetch_object($result)) {
 
575
            @ibase_free_result($result);
 
576
            if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'PRIMARY KEY') {
 
577
                $flags .= 'primary_key ';
 
578
            }
 
579
            if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'UNIQUE') {
 
580
                $flags .= 'unique_key ';
 
581
            }
 
582
        }
 
583
 
 
584
        $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,'
 
585
               .'  R.RDB$DEFAULT_SOURCE AS DSOURCE,'
 
586
               .'  F.RDB$FIELD_TYPE AS FTYPE,'
 
587
               .'  F.RDB$COMPUTED_SOURCE AS CSOURCE'
 
588
               .' FROM RDB$RELATION_FIELDS R '
 
589
               .'  JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
 
590
               .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''
 
591
               .'  AND R.RDB$FIELD_NAME=\'' . $field_name . '\'';
 
592
 
 
593
        $result = @ibase_query($this->connection, $sql);
 
594
        if (!$result) {
 
595
            return $this->ibaseRaiseError();
 
596
        }
 
597
        if ($obj = @ibase_fetch_object($result)) {
 
598
            @ibase_free_result($result);
 
599
            if (isset($obj->NFLAG)) {
 
600
                $flags .= 'not_null ';
 
601
            }
 
602
            if (isset($obj->DSOURCE)) {
 
603
                $flags .= 'default ';
 
604
            }
 
605
            if (isset($obj->CSOURCE)) {
 
606
                $flags .= 'computed ';
 
607
            }
 
608
            if (isset($obj->FTYPE)  && $obj->FTYPE == 261) {
 
609
                $flags .= 'blob ';
 
610
            }
 
611
        }
 
612
 
 
613
        return trim($flags);
 
614
    }
 
615
 
 
616
    // }}}
 
617
    // {{{ tableInfo()
 
618
 
 
619
    /**
 
620
     * Returns information about a table or a result set.
 
621
     *
 
622
     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
 
623
     * is a table name.
 
624
     *
 
625
     * @param object|string  $result  DB_result object from a query or a
 
626
     *                                string containing the name of a table
 
627
     * @param int            $mode    a valid tableInfo mode
 
628
     * @return array  an associative array with the information requested
 
629
     *                or an error object if something is wrong
 
630
     * @access public
 
631
     * @internal
 
632
     * @see DB_common::tableInfo()
 
633
     */
 
634
    function tableInfo($result, $mode = null)
 
635
    {
 
636
        if (isset($result->result)) {
 
637
            /*
 
638
             * Probably received a result object.
 
639
             * Extract the result resource identifier.
 
640
             */
 
641
            $id = $result->result;
 
642
            $got_string = false;
 
643
        } elseif (is_string($result)) {
 
644
            /*
 
645
             * Probably received a table name.
 
646
             * Create a result resource identifier.
 
647
             */
 
648
             $id = @ibase_query($this->connection,
 
649
                                "SELECT * FROM $result WHERE 1=0");
 
650
            $got_string = true;
 
651
        } else {
 
652
            /*
 
653
             * Probably received a result resource identifier.
 
654
             * Copy it.
 
655
             * Depricated.  Here for compatibility only.
 
656
             */
 
657
             $id = $result;
 
658
            $got_string = false;
 
659
        }
 
660
 
 
661
        if (!is_resource($id)) {
 
662
            return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA);
 
663
        }
 
664
 
 
665
        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
 
666
            $case_func = 'strtolower';
 
667
        } else {
 
668
            $case_func = 'strval';
 
669
        }
 
670
 
 
671
        $count = @ibase_num_fields($id);
 
672
 
 
673
        // made this IF due to performance (one if is faster than $count if's)
 
674
        if (!$mode) {
 
675
            for ($i=0; $i<$count; $i++) {
 
676
                $info = @ibase_field_info($id, $i);
 
677
                $res[$i]['table'] = $got_string ? $case_func($result) : '';
 
678
                $res[$i]['name']  = $case_func($info['name']);
 
679
                $res[$i]['type']  = $info['type'];
 
680
                $res[$i]['len']   = $info['length'];
 
681
                $res[$i]['flags'] = ($got_string) ? $this->_ibaseFieldFlags($info['name'], $result) : '';
 
682
            }
 
683
        } else { // full
 
684
            $res['num_fields']= $count;
 
685
 
 
686
            for ($i=0; $i<$count; $i++) {
 
687
                $info = @ibase_field_info($id, $i);
 
688
                $res[$i]['table'] = $got_string ? $case_func($result) : '';
 
689
                $res[$i]['name']  = $case_func($info['name']);
 
690
                $res[$i]['type']  = $info['type'];
 
691
                $res[$i]['len']   = $info['length'];
 
692
                $res[$i]['flags'] = ($got_string) ? $this->_ibaseFieldFlags($info['name'], $result) : '';
 
693
 
 
694
                if ($mode & DB_TABLEINFO_ORDER) {
 
695
                    $res['order'][$res[$i]['name']] = $i;
 
696
                }
 
697
                if ($mode & DB_TABLEINFO_ORDERTABLE) {
 
698
                    $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
 
699
                }
 
700
            }
 
701
        }
 
702
 
 
703
        // free the result only if we were called on a table
 
704
        if ($got_string) {
 
705
            @ibase_free_result($id);
 
706
        }
 
707
        return $res;
 
708
    }
 
709
 
 
710
    // }}}
 
711
    // {{{ ibaseRaiseError()
 
712
 
 
713
    /**
 
714
     * Gather information about an error, then use that info to create a
 
715
     * DB error object and finally return that object.
 
716
     *
 
717
     * @param  integer  $db_errno  PEAR error number (usually a DB constant) if
 
718
     *                             manually raising an error
 
719
     * @param  string  $native_errmsg  text of error message if known
 
720
     * @return object  DB error object
 
721
     * @see DB_common::errorCode()
 
722
     * @see DB_common::raiseError()
 
723
     */
 
724
    function &ibaseRaiseError($db_errno = null, $native_errmsg = null)
 
725
    {
 
726
        if ($native_errmsg === null) {
 
727
            $native_errmsg = @ibase_errmsg();
 
728
        }
 
729
        // memo for the interbase php module hackers: we need something similar
 
730
        // to mysql_errno() to retrieve error codes instead of this ugly hack
 
731
        if (preg_match('/^([^0-9\-]+)([0-9\-]+)\s+(.*)$/', $native_errmsg, $m)) {
 
732
            $native_errno = (int)$m[2];
 
733
        } else {
 
734
            $native_errno = null;
 
735
        }
 
736
        // try to map the native error to the DB one
 
737
        if ($db_errno === null) {
 
738
            if ($native_errno) {
 
739
                // try to interpret Interbase error code (that's why we need ibase_errno()
 
740
                // in the interbase module to return the real error code)
 
741
                switch ($native_errno) {
 
742
                    case -204:
 
743
                        if (is_int(strpos($m[3], 'Table unknown'))) {
 
744
                            $db_errno = DB_ERROR_NOSUCHTABLE;
 
745
                        }
 
746
                        break;
 
747
                    default:
 
748
                        $db_errno = $this->errorCode($native_errno);
 
749
                }
 
750
            } else {
 
751
                $error_regexps = array(
 
752
                    '/[tT]able not found/' => DB_ERROR_NOSUCHTABLE,
 
753
                    '/[tT]able .* already exists/' => DB_ERROR_ALREADY_EXISTS,
 
754
                    '/validation error for column .* value "\*\*\* null/' => DB_ERROR_CONSTRAINT_NOT_NULL,
 
755
                    '/violation of [\w ]+ constraint/' => DB_ERROR_CONSTRAINT,
 
756
                    '/conversion error from string/' => DB_ERROR_INVALID_NUMBER,
 
757
                    '/no permission for/' => DB_ERROR_ACCESS_VIOLATION,
 
758
                    '/arithmetic exception, numeric overflow, or string truncation/' => DB_ERROR_DIVZERO
 
759
                );
 
760
                foreach ($error_regexps as $regexp => $code) {
 
761
                    if (preg_match($regexp, $native_errmsg)) {
 
762
                        $db_errno = $code;
 
763
                        $native_errno = null;
 
764
                        break;
 
765
                    }
 
766
                }
 
767
            }
 
768
        }
 
769
        $tmp =& $this->raiseError($db_errno, null, null, null, $native_errmsg);
 
770
        return $tmp;
 
771
    }
 
772
 
 
773
    // }}}
 
774
 
 
775
}
 
776
 
 
777
/*
 
778
 * Local variables:
 
779
 * tab-width: 4
 
780
 * c-basic-offset: 4
 
781
 * End:
 
782
 */
 
783
 
 
784
?>