source: branches/comu-ver2/data/module/DB/ibase.php @ 17877

Revision 17877, 33.3 KB checked in by Seasoft, 15 years ago (diff)

・PEAR::DB を Ver.1.7.14RC1(2007-11-27)ベースに更改。
・不適切な文字列比較を修正。

 http://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=1808&forum=9

  • 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-2007 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-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 */
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        // -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?>
Note: See TracBrowser for help on using the repository browser.