source: branches/feature-module-update/data/module/DB/ibase.php @ 15532

Revision 15532, 32.8 KB checked in by nanasess, 17 years ago (diff)

svn:mime-type 修正

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