]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/DB/fbsql.php
DB updated to 1.8.2
[quix0rs-gnu-social.git] / extlib / DB / fbsql.php
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 version 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-2007 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  */
30 require_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-2007 The PHP Group
43  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
44  * @version    Release: 1.8.2
45  * @link       http://pear.php.net/package/DB
46  * @since      Class functional since Release 1.7.0
47  */
48 class 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 ($this->_checkManip($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 is_resource($result) ? fbsql_free_result($result) : false;
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($this->connection);
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($this->connection);
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 ($this->_last_query_manip) {
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) || $this->_next_query_manip) {
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     // {{{ quoteBoolean()
557
558     /**
559      * Formats a boolean value for use within a query in a locale-independent
560      * manner.
561      *
562      * @param boolean the boolean value to be quoted.
563      * @return string the quoted string.
564      * @see DB_common::quoteSmart()
565      * @since Method available since release 1.7.8.
566      */
567     function quoteBoolean($boolean) {
568         return $boolean ? 'TRUE' : 'FALSE';
569     }
570      
571     // }}}
572     // {{{ quoteFloat()
573
574     /**
575      * Formats a float value for use within a query in a locale-independent
576      * manner.
577      *
578      * @param float the float value to be quoted.
579      * @return string the quoted string.
580      * @see DB_common::quoteSmart()
581      * @since Method available since release 1.7.8.
582      */
583     function quoteFloat($float) {
584         return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
585     }
586      
587     // }}}
588     // {{{ fbsqlRaiseError()
589
590     /**
591      * Produces a DB_Error object regarding the current problem
592      *
593      * @param int $errno  if the error is being manually raised pass a
594      *                     DB_ERROR* constant here.  If this isn't passed
595      *                     the error information gathered from the DBMS.
596      *
597      * @return object  the DB_Error object
598      *
599      * @see DB_common::raiseError(),
600      *      DB_fbsql::errorNative(), DB_common::errorCode()
601      */
602     function fbsqlRaiseError($errno = null)
603     {
604         if ($errno === null) {
605             $errno = $this->errorCode(fbsql_errno($this->connection));
606         }
607         return $this->raiseError($errno, null, null, null,
608                                  @fbsql_error($this->connection));
609     }
610
611     // }}}
612     // {{{ errorNative()
613
614     /**
615      * Gets the DBMS' native error code produced by the last query
616      *
617      * @return int  the DBMS' error code
618      */
619     function errorNative()
620     {
621         return @fbsql_errno($this->connection);
622     }
623
624     // }}}
625     // {{{ tableInfo()
626
627     /**
628      * Returns information about a table or a result set
629      *
630      * @param object|string  $result  DB_result object from a query or a
631      *                                 string containing the name of a table.
632      *                                 While this also accepts a query result
633      *                                 resource identifier, this behavior is
634      *                                 deprecated.
635      * @param int            $mode    a valid tableInfo mode
636      *
637      * @return array  an associative array with the information requested.
638      *                 A DB_Error object on failure.
639      *
640      * @see DB_common::tableInfo()
641      */
642     function tableInfo($result, $mode = null)
643     {
644         if (is_string($result)) {
645             /*
646              * Probably received a table name.
647              * Create a result resource identifier.
648              */
649             $id = @fbsql_list_fields($this->dsn['database'],
650                                      $result, $this->connection);
651             $got_string = true;
652         } elseif (isset($result->result)) {
653             /*
654              * Probably received a result object.
655              * Extract the result resource identifier.
656              */
657             $id = $result->result;
658             $got_string = false;
659         } else {
660             /*
661              * Probably received a result resource identifier.
662              * Copy it.
663              * Deprecated.  Here for compatibility only.
664              */
665             $id = $result;
666             $got_string = false;
667         }
668
669         if (!is_resource($id)) {
670             return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
671         }
672
673         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
674             $case_func = 'strtolower';
675         } else {
676             $case_func = 'strval';
677         }
678
679         $count = @fbsql_num_fields($id);
680         $res   = array();
681
682         if ($mode) {
683             $res['num_fields'] = $count;
684         }
685
686         for ($i = 0; $i < $count; $i++) {
687             $res[$i] = array(
688                 'table' => $case_func(@fbsql_field_table($id, $i)),
689                 'name'  => $case_func(@fbsql_field_name($id, $i)),
690                 'type'  => @fbsql_field_type($id, $i),
691                 'len'   => @fbsql_field_len($id, $i),
692                 'flags' => @fbsql_field_flags($id, $i),
693             );
694             if ($mode & DB_TABLEINFO_ORDER) {
695                 $res['order'][$res[$i]['name']] = $i;
696             }
697             if ($mode & DB_TABLEINFO_ORDERTABLE) {
698                 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
699             }
700         }
701
702         // free the result only if we were called on a table
703         if ($got_string) {
704             @fbsql_free_result($id);
705         }
706         return $res;
707     }
708
709     // }}}
710     // {{{ getSpecialQuery()
711
712     /**
713      * Obtains the query string needed for listing a given type of objects
714      *
715      * @param string $type  the kind of objects you want to retrieve
716      *
717      * @return string  the SQL query string or null if the driver doesn't
718      *                  support the object type requested
719      *
720      * @access protected
721      * @see DB_common::getListOf()
722      */
723     function getSpecialQuery($type)
724     {
725         switch ($type) {
726             case 'tables':
727                 return 'SELECT "table_name" FROM information_schema.tables'
728                        . ' t0, information_schema.schemata t1'
729                        . ' WHERE t0.schema_pk=t1.schema_pk AND'
730                        . ' "table_type" = \'BASE TABLE\''
731                        . ' AND "schema_name" = current_schema';
732             case 'views':
733                 return 'SELECT "table_name" FROM information_schema.tables'
734                        . ' t0, information_schema.schemata t1'
735                        . ' WHERE t0.schema_pk=t1.schema_pk AND'
736                        . ' "table_type" = \'VIEW\''
737                        . ' AND "schema_name" = current_schema';
738             case 'users':
739                 return 'SELECT "user_name" from information_schema.users'; 
740             case 'functions':
741                 return 'SELECT "routine_name" FROM'
742                        . ' information_schema.psm_routines'
743                        . ' t0, information_schema.schemata t1'
744                        . ' WHERE t0.schema_pk=t1.schema_pk'
745                        . ' AND "routine_kind"=\'FUNCTION\''
746                        . ' AND "schema_name" = current_schema';
747             case 'procedures':
748                 return 'SELECT "routine_name" FROM'
749                        . ' information_schema.psm_routines'
750                        . ' t0, information_schema.schemata t1'
751                        . ' WHERE t0.schema_pk=t1.schema_pk'
752                        . ' AND "routine_kind"=\'PROCEDURE\''
753                        . ' AND "schema_name" = current_schema';
754             default:
755                 return null;
756         }
757     }
758
759     // }}}
760 }
761
762 /*
763  * Local variables:
764  * tab-width: 4
765  * c-basic-offset: 4
766  * End:
767  */
768
769 ?>