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

Revision 15532, 22.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 fbsql extension
7 * for interacting with FrontBase databases
8 *
9 * PHP versions 4 and 5
10 *
11 * LICENSE: This source file is subject to version 3.0 of the PHP license
12 * that is available through the world-wide-web at the following URI:
13 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
14 * the PHP License and are unable to obtain it through the web, please
15 * send a note to license@php.net so we can mail you a copy immediately.
16 *
17 * @category   Database
18 * @package    DB
19 * @author     Frank M. Kromann <frank@frontbase.com>
20 * @author     Daniel Convissor <danielc@php.net>
21 * @copyright  1997-2005 The PHP Group
22 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
23 * @version    CVS: $Id$
24 * @link       http://pear.php.net/package/DB
25 */
26
27/**
28 * Obtain the DB_common class so it can be extended from
29 */
30require_once 'DB/common.php';
31
32/**
33 * The methods PEAR DB uses to interact with PHP's fbsql extension
34 * for interacting with FrontBase databases
35 *
36 * These methods overload the ones declared in DB_common.
37 *
38 * @category   Database
39 * @package    DB
40 * @author     Frank M. Kromann <frank@frontbase.com>
41 * @author     Daniel Convissor <danielc@php.net>
42 * @copyright  1997-2005 The PHP Group
43 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
44 * @version    Release: @package_version@
45 * @link       http://pear.php.net/package/DB
46 * @since      Class functional since Release 1.7.0
47 */
48class DB_fbsql extends DB_common
49{
50    // {{{ properties
51
52    /**
53     * The DB driver type (mysql, oci8, odbc, etc.)
54     * @var string
55     */
56    var $phptype = 'fbsql';
57
58    /**
59     * The database syntax variant to be used (db2, access, etc.), if any
60     * @var string
61     */
62    var $dbsyntax = 'fbsql';
63
64    /**
65     * The capabilities of this DB implementation
66     *
67     * The 'new_link' element contains the PHP version that first provided
68     * new_link support for this DBMS.  Contains false if it's unsupported.
69     *
70     * Meaning of the 'limit' element:
71     *   + 'emulate' = emulate with fetch row by number
72     *   + 'alter'   = alter the query
73     *   + false     = skip rows
74     *
75     * @var array
76     */
77    var $features = array(
78        'limit'         => 'alter',
79        'new_link'      => false,
80        'numrows'       => true,
81        'pconnect'      => true,
82        'prepare'       => false,
83        'ssl'           => false,
84        'transactions'  => true,
85    );
86
87    /**
88     * A mapping of native error codes to DB error codes
89     * @var array
90     */
91    var $errorcode_map = array(
92         22 => DB_ERROR_SYNTAX,
93         85 => DB_ERROR_ALREADY_EXISTS,
94        108 => DB_ERROR_SYNTAX,
95        116 => DB_ERROR_NOSUCHTABLE,
96        124 => DB_ERROR_VALUE_COUNT_ON_ROW,
97        215 => DB_ERROR_NOSUCHFIELD,
98        217 => DB_ERROR_INVALID_NUMBER,
99        226 => DB_ERROR_NOSUCHFIELD,
100        231 => DB_ERROR_INVALID,
101        239 => DB_ERROR_TRUNCATED,
102        251 => DB_ERROR_SYNTAX,
103        266 => DB_ERROR_NOT_FOUND,
104        357 => DB_ERROR_CONSTRAINT_NOT_NULL,
105        358 => DB_ERROR_CONSTRAINT,
106        360 => DB_ERROR_CONSTRAINT,
107        361 => DB_ERROR_CONSTRAINT,
108    );
109
110    /**
111     * The raw database connection created by PHP
112     * @var resource
113     */
114    var $connection;
115
116    /**
117     * The DSN information for connecting to a database
118     * @var array
119     */
120    var $dsn = array();
121
122
123    // }}}
124    // {{{ constructor
125
126    /**
127     * This constructor calls <kbd>$this->DB_common()</kbd>
128     *
129     * @return void
130     */
131    function DB_fbsql()
132    {
133        $this->DB_common();
134    }
135
136    // }}}
137    // {{{ connect()
138
139    /**
140     * Connect to the database server, log in and open the database
141     *
142     * Don't call this method directly.  Use DB::connect() instead.
143     *
144     * @param array $dsn         the data source name
145     * @param bool  $persistent  should the connection be persistent?
146     *
147     * @return int  DB_OK on success. A DB_Error object on failure.
148     */
149    function connect($dsn, $persistent = false)
150    {
151        if (!PEAR::loadExtension('fbsql')) {
152            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
153        }
154
155        $this->dsn = $dsn;
156        if ($dsn['dbsyntax']) {
157            $this->dbsyntax = $dsn['dbsyntax'];
158        }
159
160        $params = array(
161            $dsn['hostspec'] ? $dsn['hostspec'] : 'localhost',
162            $dsn['username'] ? $dsn['username'] : null,
163            $dsn['password'] ? $dsn['password'] : null,
164        );
165
166        $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect';
167
168        $ini = ini_get('track_errors');
169        $php_errormsg = '';
170        if ($ini) {
171            $this->connection = @call_user_func_array($connect_function,
172                                                      $params);
173        } else {
174            ini_set('track_errors', 1);
175            $this->connection = @call_user_func_array($connect_function,
176                                                      $params);
177            ini_set('track_errors', $ini);
178        }
179
180        if (!$this->connection) {
181            return $this->raiseError(DB_ERROR_CONNECT_FAILED,
182                                     null, null, null,
183                                     $php_errormsg);
184        }
185
186        if ($dsn['database']) {
187            if (!@fbsql_select_db($dsn['database'], $this->connection)) {
188                return $this->fbsqlRaiseError();
189            }
190        }
191
192        return DB_OK;
193    }
194
195    // }}}
196    // {{{ disconnect()
197
198    /**
199     * Disconnects from the database server
200     *
201     * @return bool  TRUE on success, FALSE on failure
202     */
203    function disconnect()
204    {
205        $ret = @fbsql_close($this->connection);
206        $this->connection = null;
207        return $ret;
208    }
209
210    // }}}
211    // {{{ simpleQuery()
212
213    /**
214     * Sends a query to the database server
215     *
216     * @param string  the SQL query string
217     *
218     * @return mixed  + a PHP result resrouce for successful SELECT queries
219     *                + the DB_OK constant for other successful queries
220     *                + a DB_Error object on failure
221     */
222    function simpleQuery($query)
223    {
224        $this->last_query = $query;
225        $query = $this->modifyQuery($query);
226        $result = @fbsql_query("$query;", $this->connection);
227        if (!$result) {
228            return $this->fbsqlRaiseError();
229        }
230        // Determine which queries that should return data, and which
231        // should return an error code only.
232        if (DB::isManip($query)) {
233            return DB_OK;
234        }
235        return $result;
236    }
237
238    // }}}
239    // {{{ nextResult()
240
241    /**
242     * Move the internal fbsql result pointer to the next available result
243     *
244     * @param a valid fbsql result resource
245     *
246     * @access public
247     *
248     * @return true if a result is available otherwise return false
249     */
250    function nextResult($result)
251    {
252        return @fbsql_next_result($result);
253    }
254
255    // }}}
256    // {{{ fetchInto()
257
258    /**
259     * Places a row from the result set into the given array
260     *
261     * Formating of the array and the data therein are configurable.
262     * See DB_result::fetchInto() for more information.
263     *
264     * This method is not meant to be called directly.  Use
265     * DB_result::fetchInto() instead.  It can't be declared "protected"
266     * because DB_result is a separate object.
267     *
268     * @param resource $result    the query result resource
269     * @param array    $arr       the referenced array to put the data in
270     * @param int      $fetchmode how the resulting array should be indexed
271     * @param int      $rownum    the row number to fetch (0 = first row)
272     *
273     * @return mixed  DB_OK on success, NULL when the end of a result set is
274     *                 reached or on failure
275     *
276     * @see DB_result::fetchInto()
277     */
278    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
279    {
280        if ($rownum !== null) {
281            if (!@fbsql_data_seek($result, $rownum)) {
282                return null;
283            }
284        }
285        if ($fetchmode & DB_FETCHMODE_ASSOC) {
286            $arr = @fbsql_fetch_array($result, FBSQL_ASSOC);
287            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
288                $arr = array_change_key_case($arr, CASE_LOWER);
289            }
290        } else {
291            $arr = @fbsql_fetch_row($result);
292        }
293        if (!$arr) {
294            return null;
295        }
296        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
297            $this->_rtrimArrayValues($arr);
298        }
299        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
300            $this->_convertNullArrayValuesToEmpty($arr);
301        }
302        return DB_OK;
303    }
304
305    // }}}
306    // {{{ freeResult()
307
308    /**
309     * Deletes the result set and frees the memory occupied by the result set
310     *
311     * This method is not meant to be called directly.  Use
312     * DB_result::free() instead.  It can't be declared "protected"
313     * because DB_result is a separate object.
314     *
315     * @param resource $result  PHP's query result resource
316     *
317     * @return bool  TRUE on success, FALSE if $result is invalid
318     *
319     * @see DB_result::free()
320     */
321    function freeResult($result)
322    {
323        return @fbsql_free_result($result);
324    }
325
326    // }}}
327    // {{{ autoCommit()
328
329    /**
330     * Enables or disables automatic commits
331     *
332     * @param bool $onoff  true turns it on, false turns it off
333     *
334     * @return int  DB_OK on success.  A DB_Error object if the driver
335     *               doesn't support auto-committing transactions.
336     */
337    function autoCommit($onoff=false)
338    {
339        if ($onoff) {
340            $this->query("SET COMMIT TRUE");
341        } else {
342            $this->query("SET COMMIT FALSE");
343        }
344    }
345
346    // }}}
347    // {{{ commit()
348
349    /**
350     * Commits the current transaction
351     *
352     * @return int  DB_OK on success.  A DB_Error object on failure.
353     */
354    function commit()
355    {
356        @fbsql_commit();
357    }
358
359    // }}}
360    // {{{ rollback()
361
362    /**
363     * Reverts the current transaction
364     *
365     * @return int  DB_OK on success.  A DB_Error object on failure.
366     */
367    function rollback()
368    {
369        @fbsql_rollback();
370    }
371
372    // }}}
373    // {{{ numCols()
374
375    /**
376     * Gets the number of columns in a result set
377     *
378     * This method is not meant to be called directly.  Use
379     * DB_result::numCols() instead.  It can't be declared "protected"
380     * because DB_result is a separate object.
381     *
382     * @param resource $result  PHP's query result resource
383     *
384     * @return int  the number of columns.  A DB_Error object on failure.
385     *
386     * @see DB_result::numCols()
387     */
388    function numCols($result)
389    {
390        $cols = @fbsql_num_fields($result);
391        if (!$cols) {
392            return $this->fbsqlRaiseError();
393        }
394        return $cols;
395    }
396
397    // }}}
398    // {{{ numRows()
399
400    /**
401     * Gets the number of rows in a result set
402     *
403     * This method is not meant to be called directly.  Use
404     * DB_result::numRows() 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 int  the number of rows.  A DB_Error object on failure.
410     *
411     * @see DB_result::numRows()
412     */
413    function numRows($result)
414    {
415        $rows = @fbsql_num_rows($result);
416        if ($rows === null) {
417            return $this->fbsqlRaiseError();
418        }
419        return $rows;
420    }
421
422    // }}}
423    // {{{ affectedRows()
424
425    /**
426     * Determines the number of rows affected by a data maniuplation query
427     *
428     * 0 is returned for queries that don't manipulate data.
429     *
430     * @return int  the number of rows.  A DB_Error object on failure.
431     */
432    function affectedRows()
433    {
434        if (DB::isManip($this->last_query)) {
435            $result = @fbsql_affected_rows($this->connection);
436        } else {
437            $result = 0;
438        }
439        return $result;
440     }
441
442    // }}}
443    // {{{ nextId()
444
445    /**
446     * Returns the next free id in a sequence
447     *
448     * @param string  $seq_name  name of the sequence
449     * @param boolean $ondemand  when true, the seqence is automatically
450     *                            created if it does not exist
451     *
452     * @return int  the next id number in the sequence.
453     *               A DB_Error object on failure.
454     *
455     * @see DB_common::nextID(), DB_common::getSequenceName(),
456     *      DB_fbsql::createSequence(), DB_fbsql::dropSequence()
457     */
458    function nextId($seq_name, $ondemand = true)
459    {
460        $seqname = $this->getSequenceName($seq_name);
461        do {
462            $repeat = 0;
463            $this->pushErrorHandling(PEAR_ERROR_RETURN);
464            $result = $this->query('SELECT UNIQUE FROM ' . $seqname);
465            $this->popErrorHandling();
466            if ($ondemand && DB::isError($result) &&
467                $result->getCode() == DB_ERROR_NOSUCHTABLE) {
468                $repeat = 1;
469                $result = $this->createSequence($seq_name);
470                if (DB::isError($result)) {
471                    return $result;
472                }
473            } else {
474                $repeat = 0;
475            }
476        } while ($repeat);
477        if (DB::isError($result)) {
478            return $this->fbsqlRaiseError();
479        }
480        $result->fetchInto($tmp, DB_FETCHMODE_ORDERED);
481        return $tmp[0];
482    }
483
484    /**
485     * Creates a new sequence
486     *
487     * @param string $seq_name  name of the new sequence
488     *
489     * @return int  DB_OK on success.  A DB_Error object on failure.
490     *
491     * @see DB_common::createSequence(), DB_common::getSequenceName(),
492     *      DB_fbsql::nextID(), DB_fbsql::dropSequence()
493     */
494    function createSequence($seq_name)
495    {
496        $seqname = $this->getSequenceName($seq_name);
497        $res = $this->query('CREATE TABLE ' . $seqname
498                            . ' (id INTEGER NOT NULL,'
499                            . ' PRIMARY KEY(id))');
500        if ($res) {
501            $res = $this->query('SET UNIQUE = 0 FOR ' . $seqname);
502        }
503        return $res;
504    }
505
506    // }}}
507    // {{{ dropSequence()
508
509    /**
510     * Deletes a sequence
511     *
512     * @param string $seq_name  name of the sequence to be deleted
513     *
514     * @return int  DB_OK on success.  A DB_Error object on failure.
515     *
516     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
517     *      DB_fbsql::nextID(), DB_fbsql::createSequence()
518     */
519    function dropSequence($seq_name)
520    {
521        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name)
522                            . ' RESTRICT');
523    }
524
525    // }}}
526    // {{{ modifyLimitQuery()
527
528    /**
529     * Adds LIMIT clauses to a query string according to current DBMS standards
530     *
531     * @param string $query   the query to modify
532     * @param int    $from    the row to start to fetching (0 = the first row)
533     * @param int    $count   the numbers of rows to fetch
534     * @param mixed  $params  array, string or numeric data to be used in
535     *                         execution of the statement.  Quantity of items
536     *                         passed must match quantity of placeholders in
537     *                         query:  meaning 1 placeholder for non-array
538     *                         parameters or 1 placeholder per array element.
539     *
540     * @return string  the query string with LIMIT clauses added
541     *
542     * @access protected
543     */
544    function modifyLimitQuery($query, $from, $count, $params = array())
545    {
546        if (DB::isManip($query)) {
547            return preg_replace('/^([\s(])*SELECT/i',
548                                "\\1SELECT TOP($count)", $query);
549        } else {
550            return preg_replace('/([\s(])*SELECT/i',
551                                "\\1SELECT TOP($from, $count)", $query);
552        }
553    }
554
555    // }}}
556    // {{{ quoteSmart()
557
558    /**
559     * Formats input so it can be safely used in a query
560     *
561     * @param mixed $in  the data to be formatted
562     *
563     * @return mixed  the formatted data.  The format depends on the input's
564     *                 PHP type:
565     *                 + null = the string <samp>NULL</samp>
566     *                 + boolean = string <samp>TRUE</samp> or <samp>FALSE</samp>
567     *                 + integer or double = the unquoted number
568     *                 + other (including strings and numeric strings) =
569     *                   the data escaped according to FrontBase's settings
570     *                   then encapsulated between single quotes
571     *
572     * @see DB_common::quoteSmart()
573     * @since Method available since Release 1.6.0
574     */
575    function quoteSmart($in)
576    {
577        if (is_int($in) || is_double($in)) {
578            return $in;
579        } elseif (is_bool($in)) {
580            return $in ? 'TRUE' : 'FALSE';
581        } elseif (is_null($in)) {
582            return 'NULL';
583        } else {
584            return "'" . $this->escapeSimple($in) . "'";
585        }
586    }
587
588    // }}}
589    // {{{ fbsqlRaiseError()
590
591    /**
592     * Produces a DB_Error object regarding the current problem
593     *
594     * @param int $errno  if the error is being manually raised pass a
595     *                     DB_ERROR* constant here.  If this isn't passed
596     *                     the error information gathered from the DBMS.
597     *
598     * @return object  the DB_Error object
599     *
600     * @see DB_common::raiseError(),
601     *      DB_fbsql::errorNative(), DB_common::errorCode()
602     */
603    function fbsqlRaiseError($errno = null)
604    {
605        if ($errno === null) {
606            $errno = $this->errorCode(fbsql_errno($this->connection));
607        }
608        return $this->raiseError($errno, null, null, null,
609                                 @fbsql_error($this->connection));
610    }
611
612    // }}}
613    // {{{ errorNative()
614
615    /**
616     * Gets the DBMS' native error code produced by the last query
617     *
618     * @return int  the DBMS' error code
619     */
620    function errorNative()
621    {
622        return @fbsql_errno($this->connection);
623    }
624
625    // }}}
626    // {{{ tableInfo()
627
628    /**
629     * Returns information about a table or a result set
630     *
631     * @param object|string  $result  DB_result object from a query or a
632     *                                 string containing the name of a table.
633     *                                 While this also accepts a query result
634     *                                 resource identifier, this behavior is
635     *                                 deprecated.
636     * @param int            $mode    a valid tableInfo mode
637     *
638     * @return array  an associative array with the information requested.
639     *                 A DB_Error object on failure.
640     *
641     * @see DB_common::tableInfo()
642     */
643    function tableInfo($result, $mode = null)
644    {
645        if (is_string($result)) {
646            /*
647             * Probably received a table name.
648             * Create a result resource identifier.
649             */
650            $id = @fbsql_list_fields($this->dsn['database'],
651                                     $result, $this->connection);
652            $got_string = true;
653        } elseif (isset($result->result)) {
654            /*
655             * Probably received a result object.
656             * Extract the result resource identifier.
657             */
658            $id = $result->result;
659            $got_string = false;
660        } else {
661            /*
662             * Probably received a result resource identifier.
663             * Copy it.
664             * Deprecated.  Here for compatibility only.
665             */
666            $id = $result;
667            $got_string = false;
668        }
669
670        if (!is_resource($id)) {
671            return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
672        }
673
674        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
675            $case_func = 'strtolower';
676        } else {
677            $case_func = 'strval';
678        }
679
680        $count = @fbsql_num_fields($id);
681        $res   = array();
682
683        if ($mode) {
684            $res['num_fields'] = $count;
685        }
686
687        for ($i = 0; $i < $count; $i++) {
688            $res[$i] = array(
689                'table' => $case_func(@fbsql_field_table($id, $i)),
690                'name'  => $case_func(@fbsql_field_name($id, $i)),
691                'type'  => @fbsql_field_type($id, $i),
692                'len'   => @fbsql_field_len($id, $i),
693                'flags' => @fbsql_field_flags($id, $i),
694            );
695            if ($mode & DB_TABLEINFO_ORDER) {
696                $res['order'][$res[$i]['name']] = $i;
697            }
698            if ($mode & DB_TABLEINFO_ORDERTABLE) {
699                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
700            }
701        }
702
703        // free the result only if we were called on a table
704        if ($got_string) {
705            @fbsql_free_result($id);
706        }
707        return $res;
708    }
709
710    // }}}
711    // {{{ getSpecialQuery()
712
713    /**
714     * Obtains the query string needed for listing a given type of objects
715     *
716     * @param string $type  the kind of objects you want to retrieve
717     *
718     * @return string  the SQL query string or null if the driver doesn't
719     *                  support the object type requested
720     *
721     * @access protected
722     * @see DB_common::getListOf()
723     */
724    function getSpecialQuery($type)
725    {
726        switch ($type) {
727            case 'tables':
728                return 'SELECT "table_name" FROM information_schema.tables'
729                       . ' t0, information_schema.schemata t1'
730                       . ' WHERE t0.schema_pk=t1.schema_pk AND'
731                       . ' "table_type" = \'BASE TABLE\''
732                       . ' AND "schema_name" = current_schema';
733            case 'views':
734                return 'SELECT "table_name" FROM information_schema.tables'
735                       . ' t0, information_schema.schemata t1'
736                       . ' WHERE t0.schema_pk=t1.schema_pk AND'
737                       . ' "table_type" = \'VIEW\''
738                       . ' AND "schema_name" = current_schema';
739            case 'users':
740                return 'SELECT "user_name" from information_schema.users';
741            case 'functions':
742                return 'SELECT "routine_name" FROM'
743                       . ' information_schema.psm_routines'
744                       . ' t0, information_schema.schemata t1'
745                       . ' WHERE t0.schema_pk=t1.schema_pk'
746                       . ' AND "routine_kind"=\'FUNCTION\''
747                       . ' AND "schema_name" = current_schema';
748            case 'procedures':
749                return 'SELECT "routine_name" FROM'
750                       . ' information_schema.psm_routines'
751                       . ' t0, information_schema.schemata t1'
752                       . ' WHERE t0.schema_pk=t1.schema_pk'
753                       . ' AND "routine_kind"=\'PROCEDURE\''
754                       . ' AND "schema_name" = current_schema';
755            default:
756                return null;
757        }
758    }
759
760    // }}}
761}
762
763/*
764 * Local variables:
765 * tab-width: 4
766 * c-basic-offset: 4
767 * End:
768 */
769
770?>
Note: See TracBrowser for help on using the repository browser.