]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/pear/DB/fbsql.php
extra_empty_lines
[SourceForge/phpwiki.git] / lib / pear / DB / fbsql.php
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
3 // +----------------------------------------------------------------------+
4 // | PHP Version 4                                                        |
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997-2004 The PHP Group                                |
7 // +----------------------------------------------------------------------+
8 // | This source file is subject to version 2.02 of the PHP license,      |
9 // | that is bundled with this package in the file LICENSE, and is        |
10 // | available at through the world-wide-web at                           |
11 // | http://www.php.net/license/2_02.txt.                                 |
12 // | If you did not receive a copy of the PHP license and are unable to   |
13 // | obtain it through the world-wide-web, please send a note to          |
14 // | license@php.net so we can mail you a copy immediately.               |
15 // +----------------------------------------------------------------------+
16 // | Author: Frank M. Kromann <frank@frontbase.com>                       |
17 // | Maintainer: Daniel Convissor <danielc@php.net>                       |
18 // +----------------------------------------------------------------------+
19 //
20 // $Id$
21
22 // XXX legend:
23 //
24 // XXX ERRORMSG: The error message from the fbsql function should
25 //               be registered here.
26 //
27 // TODO/wishlist:
28 // longReadlen
29 // binmode
30
31 require_once 'DB/common.php';
32
33 /**
34  * Database independent query interface definition for PHP's FrontBase
35  * extension.
36  *
37  * @package  DB
38  * @version  $Id$
39  * @category Database
40  * @author   Frank M. Kromann <frank@frontbase.com>
41  */
42 class DB_fbsql extends DB_common
43 {
44     // {{{ properties
45
46     var $connection;
47     var $phptype, $dbsyntax;
48     var $prepare_tokens = array();
49     var $prepare_types = array();
50     var $num_rows = array();
51     var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */
52
53     // }}}
54     // {{{ constructor
55
56     /**
57      * DB_fbsql constructor.
58      *
59      * @access public
60      */
61     function DB_fbsql()
62     {
63         $this->DB_common();
64         $this->phptype = 'fbsql';
65         $this->dbsyntax = 'fbsql';
66         $this->features = array(
67             'prepare' => false,
68             'pconnect' => true,
69             'transactions' => true,
70             'limit' => 'emulate'
71         );
72         $this->errorcode_map = array(
73             1004 => DB_ERROR_CANNOT_CREATE,
74             1005 => DB_ERROR_CANNOT_CREATE,
75             1006 => DB_ERROR_CANNOT_CREATE,
76             1007 => DB_ERROR_ALREADY_EXISTS,
77             1008 => DB_ERROR_CANNOT_DROP,
78             1046 => DB_ERROR_NODBSELECTED,
79             1050 => DB_ERROR_ALREADY_EXISTS,
80             1051 => DB_ERROR_NOSUCHTABLE,
81             1054 => DB_ERROR_NOSUCHFIELD,
82             1062 => DB_ERROR_ALREADY_EXISTS,
83             1064 => DB_ERROR_SYNTAX,
84             1100 => DB_ERROR_NOT_LOCKED,
85             1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
86             1146 => DB_ERROR_NOSUCHTABLE,
87         );
88     }
89
90     // }}}
91     // {{{ connect()
92
93     /**
94      * Connect to a database and log in as the specified user.
95      *
96      * @param $dsn the data source name (see DB::parseDSN for syntax)
97      * @param $persistent (optional) whether the connection should
98      *        be persistent
99      * @access public
100      * @return int DB_OK on success, a DB error on failure
101      */
102     function connect($dsninfo, $persistent = false)
103     {
104         if (!DB::assertExtension('fbsql')) {
105             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
106         }
107
108         $this->dsn = $dsninfo;
109         $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
110
111         $php_errormsg = '';
112         $connect_function = $persistent ? 'fbsql_pconnect' : 'fbsql_connect';
113
114         if ($dbhost && $dsninfo['username'] && $dsninfo['password']) {
115             $conn = @$connect_function($dbhost, $dsninfo['username'],
116                                        $dsninfo['password']);
117         } elseif ($dbhost && $dsninfo['username']) {
118             $conn = @$connect_function($dbhost, $dsninfo['username']);
119         } elseif ($dbhost) {
120             $conn = @$connect_function($dbhost);
121         } else {
122             $conn = false;
123         }
124         if (!$conn) {
125             if (empty($php_errormsg)) {
126                 return $this->raiseError(DB_ERROR_CONNECT_FAILED);
127             } else {
128                 return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
129                                          null, $php_errormsg);
130             }
131         }
132
133         if ($dsninfo['database']) {
134             if (!fbsql_select_db($dsninfo['database'], $conn)) {
135                 return $this->fbsqlRaiseError();
136             }
137         }
138
139         $this->connection = $conn;
140         return DB_OK;
141     }
142
143     // }}}
144     // {{{ disconnect()
145
146     /**
147      * Log out and disconnect from the database.
148      *
149      * @access public
150      *
151      * @return bool true on success, false if not connected.
152      */
153     function disconnect()
154     {
155         $ret = @fbsql_close($this->connection);
156         $this->connection = null;
157         return $ret;
158     }
159
160     // }}}
161     // {{{ simpleQuery()
162
163     /**
164      * Send a query to fbsql and return the results as a fbsql resource
165      * identifier.
166      *
167      * @param the SQL query
168      *
169      * @access public
170      *
171      * @return mixed returns a valid fbsql result for successful SELECT
172      * queries, DB_OK for other successful queries.  A DB error is
173      * returned on failure.
174      */
175     function simpleQuery($query)
176     {
177         $this->last_query = $query;
178         $query = $this->modifyQuery($query);
179         $result = @fbsql_query("$query;", $this->connection);
180         if (!$result) {
181             return $this->fbsqlRaiseError();
182         }
183         // Determine which queries that should return data, and which
184         // should return an error code only.
185         if (DB::isManip($query)) {
186             return DB_OK;
187         }
188         $numrows = $this->numrows($result);
189         if (is_object($numrows)) {
190             return $numrows;
191         }
192         $this->num_rows[$result] = $numrows;
193         return $result;
194     }
195
196     // }}}
197     // {{{ nextResult()
198
199     /**
200      * Move the internal fbsql result pointer to the next available result
201      *
202      * @param a valid fbsql result resource
203      *
204      * @access public
205      *
206      * @return true if a result is available otherwise return false
207      */
208     function nextResult($result)
209     {
210         return @fbsql_next_result($result);
211     }
212
213     // }}}
214     // {{{ fetchInto()
215
216     /**
217      * Fetch a row and insert the data into an existing array.
218      *
219      * Formating of the array and the data therein are configurable.
220      * See DB_result::fetchInto() for more information.
221      *
222      * @param resource $result query result identifier
223      * @param array    $arr    (reference) array where data from the row
224      *                            should be placed
225      * @param int $fetchmode how the resulting array should be indexed
226      * @param int $rownum    the row number to fetch
227      *
228      * @return mixed DB_OK on success, null when end of result set is
229      *               reached or on failure
230      *
231      * @see DB_result::fetchInto()
232      * @access private
233      */
234     function fetchInto($result, &$arr, $fetchmode, $rownum=null)
235     {
236         if ($rownum !== null) {
237             if (!@fbsql_data_seek($result, $rownum)) {
238                 return null;
239             }
240         }
241         if ($fetchmode & DB_FETCHMODE_ASSOC) {
242             $arr = @fbsql_fetch_array($result, FBSQL_ASSOC);
243             if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
244                 $arr = array_change_key_case($arr, CASE_LOWER);
245             }
246         } else {
247             $arr = @fbsql_fetch_row($result);
248         }
249         if (!$arr) {
250             $errno = @fbsql_errno($this->connection);
251             if (!$errno) {
252                 return null;
253             }
254             return $this->fbsqlRaiseError($errno);
255         }
256         if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
257             $this->_rtrimArrayValues($arr);
258         }
259         if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
260             $this->_convertNullArrayValuesToEmpty($arr);
261         }
262         return DB_OK;
263     }
264
265     // }}}
266     // {{{ freeResult()
267
268     /**
269      * Free the internal resources associated with $result.
270      *
271      * @param $result fbsql result identifier
272      *
273      * @access public
274      *
275      * @return bool true on success, false if $result is invalid
276      */
277     function freeResult($result)
278     {
279         return @fbsql_free_result($result);
280     }
281
282     // }}}
283     // {{{ autoCommit()
284
285     function autoCommit($onoff=false)
286     {
287         if ($onoff) {
288             $this->query("SET COMMIT TRUE");
289         } else {
290             $this->query("SET COMMIT FALSE");
291         }
292     }
293
294     // }}}
295     // {{{ commit()
296
297     function commit()
298     {
299         @fbsql_commit();
300     }
301
302     // }}}
303     // {{{ rollback()
304
305     function rollback()
306     {
307         @fbsql_rollback();
308     }
309
310     // }}}
311     // {{{ numCols()
312
313     /**
314      * Get the number of columns in a result set.
315      *
316      * @param $result fbsql result identifier
317      *
318      * @access public
319      *
320      * @return int the number of columns per row in $result
321      */
322     function numCols($result)
323     {
324         $cols = @fbsql_num_fields($result);
325
326         if (!$cols) {
327             return $this->fbsqlRaiseError();
328         }
329
330         return $cols;
331     }
332
333     // }}}
334     // {{{ numRows()
335
336     /**
337      * Get the number of rows in a result set.
338      *
339      * @param $result fbsql result identifier
340      *
341      * @access public
342      *
343      * @return int the number of rows in $result
344      */
345     function numRows($result)
346     {
347         $rows = @fbsql_num_rows($result);
348         if ($rows === null) {
349             return $this->fbsqlRaiseError();
350         }
351         return $rows;
352     }
353
354     // }}}
355     // {{{ affectedRows()
356
357     /**
358      * Gets the number of rows affected by the data manipulation
359      * query.  For other queries, this function returns 0.
360      *
361      * @return number of rows affected by the last query
362      */
363     function affectedRows()
364     {
365         if (DB::isManip($this->last_query)) {
366             $result = @fbsql_affected_rows($this->connection);
367         } else {
368             $result = 0;
369         }
370         return $result;
371      }
372
373     // }}}
374     // {{{ errorNative()
375
376     /**
377      * Get the native error code of the last error (if any) that
378      * occured on the current connection.
379      *
380      * @access public
381      *
382      * @return int native fbsql error code
383      */
384     function errorNative()
385     {
386         return @fbsql_errno($this->connection);
387     }
388
389     // }}}
390     // {{{ nextId()
391
392     /**
393      * Returns the next free id in a sequence
394      *
395      * @param string  $seq_name name of the sequence
396      * @param boolean $ondemand when true, the seqence is automatically
397      *                           created if it does not exist
398      *
399      * @return int the next id number in the sequence.  DB_Error if problem.
400      *
401      * @internal
402      * @see DB_common::nextID()
403      * @access public
404      */
405     function nextId($seq_name, $ondemand = true)
406     {
407         $seqname = $this->getSequenceName($seq_name);
408         $repeat = 0;
409         do {
410             $result = $this->query("INSERT INTO ${seqname} VALUES(NULL)");
411             if ($ondemand && DB::isError($result) &&
412                 $result->getCode() == DB_ERROR_NOSUCHTABLE) {
413                 $repeat = 1;
414                 $result = $this->createSequence($seq_name);
415                 if (DB::isError($result)) {
416                     return $result;
417                 }
418             } else {
419                 $repeat = 0;
420             }
421         } while ($repeat);
422         if (DB::isError($result)) {
423             return $result;
424         }
425         return @fbsql_insert_id($this->connection);
426     }
427
428     /**
429      * Creates a new sequence
430      *
431      * @param string $seq_name name of the new sequence
432      *
433      * @return int DB_OK on success.  A DB_Error object is returned if
434      *              problems arise.
435      *
436      * @internal
437      * @see DB_common::createSequence()
438      * @access public
439      */
440     function createSequence($seq_name)
441     {
442         $seqname = $this->getSequenceName($seq_name);
443         return $this->query("CREATE TABLE ${seqname} ".
444                             '(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'.
445                             ' PRIMARY KEY(id))');
446     }
447
448     // }}}
449     // {{{ dropSequence()
450
451     /**
452      * Deletes a sequence
453      *
454      * @param string $seq_name name of the sequence to be deleted
455      *
456      * @return int DB_OK on success.  DB_Error if problems.
457      *
458      * @internal
459      * @see DB_common::dropSequence()
460      * @access public
461      */
462     function dropSequence($seq_name)
463     {
464         $seqname = $this->getSequenceName($seq_name);
465         return $this->query("DROP TABLE ${seqname} RESTRICT");
466     }
467
468     // }}}
469     // {{{ modifyQuery()
470
471     function modifyQuery($query)
472     {
473         if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
474             // "DELETE FROM table" gives 0 affected rows in fbsql.
475             // This little hack lets you know how many rows were deleted.
476             if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
477                 $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
478                                       'DELETE FROM \1 WHERE 1=1', $query);
479             }
480         }
481         return $query;
482     }
483
484     // }}}
485     // {{{ quoteSmart()
486
487     /**
488      * Format input so it can be safely used in a query
489      *
490      * @param mixed $in data to be quoted
491      *
492      * @return mixed Submitted variable's type = returned value:
493      *               + null = the string <samp>NULL</samp>
494      *               + boolean = string <samp>TRUE</samp> or <samp>FALSE</samp>
495      *               + integer or double = the unquoted number
496      *               + other (including strings and numeric strings) =
497      *                 the data escaped according to MySQL's settings
498      *                 then encapsulated between single quotes
499      *
500      * @internal
501      */
502     function quoteSmart($in)
503     {
504         if (is_int($in) || is_double($in)) {
505             return $in;
506         } elseif (is_bool($in)) {
507             return $in ? 'TRUE' : 'FALSE';
508         } elseif (is_null($in)) {
509             return 'NULL';
510         } else {
511             return "'" . $this->escapeSimple($in) . "'";
512         }
513     }
514
515     // }}}
516     // {{{ fbsqlRaiseError()
517
518     /**
519      * Gather information about an error, then use that info to create a
520      * DB error object and finally return that object.
521      *
522      * @param integer $errno PEAR error number (usually a DB constant) if
523      *                          manually raising an error
524      * @return object DB error object
525      * @see DB_common::errorCode()
526      * @see DB_common::raiseError()
527      */
528     function fbsqlRaiseError($errno = null)
529     {
530         if ($errno === null) {
531             $errno = $this->errorCode(fbsql_errno($this->connection));
532         }
533         return $this->raiseError($errno, null, null, null,
534                         @fbsql_error($this->connection));
535     }
536
537     // }}}
538     // {{{ tableInfo()
539
540     /**
541      * Returns information about a table or a result set.
542      *
543      * @param object|string $result DB_result object from a query or a
544      *                                string containing the name of a table
545      * @param  int   $mode a valid tableInfo mode
546      * @return array an associative array with the information requested
547      *                or an error object if something is wrong
548      * @access public
549      * @internal
550      * @see DB_common::tableInfo()
551      */
552     function tableInfo($result, $mode = null) {
553         if (isset($result->result)) {
554             /*
555              * Probably received a result object.
556              * Extract the result resource identifier.
557              */
558             $id = $result->result;
559             $got_string = false;
560         } elseif (is_string($result)) {
561             /*
562              * Probably received a table name.
563              * Create a result resource identifier.
564              */
565             $id = @fbsql_list_fields($this->dsn['database'],
566                                      $result, $this->connection);
567             $got_string = true;
568         } else {
569             /*
570              * Probably received a result resource identifier.
571              * Copy it.
572              * Depricated.  Here for compatibility only.
573              */
574             $id = $result;
575             $got_string = false;
576         }
577
578         if (!is_resource($id)) {
579             return $this->fbsqlRaiseError(DB_ERROR_NEED_MORE_DATA);
580         }
581
582         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
583             $case_func = 'strtolower';
584         } else {
585             $case_func = 'strval';
586         }
587
588         $count = @fbsql_num_fields($id);
589
590         // made this IF due to performance (one if is faster than $count if's)
591         if (!$mode) {
592             for ($i=0; $i<$count; $i++) {
593                 $res[$i]['table'] = $case_func(@fbsql_field_table($id, $i));
594                 $res[$i]['name']  = $case_func(@fbsql_field_name($id, $i));
595                 $res[$i]['type']  = @fbsql_field_type($id, $i);
596                 $res[$i]['len']   = @fbsql_field_len($id, $i);
597                 $res[$i]['flags'] = @fbsql_field_flags($id, $i);
598             }
599         } else { // full
600             $res["num_fields"]= $count;
601
602             for ($i=0; $i<$count; $i++) {
603                 $res[$i]['table'] = $case_func(@fbsql_field_table($id, $i));
604                 $res[$i]['name']  = $case_func(@fbsql_field_name($id, $i));
605                 $res[$i]['type']  = @fbsql_field_type($id, $i);
606                 $res[$i]['len']   = @fbsql_field_len($id, $i);
607                 $res[$i]['flags'] = @fbsql_field_flags($id, $i);
608
609                 if ($mode & DB_TABLEINFO_ORDER) {
610                     $res['order'][$res[$i]['name']] = $i;
611                 }
612                 if ($mode & DB_TABLEINFO_ORDERTABLE) {
613                     $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
614                 }
615             }
616         }
617
618         // free the result only if we were called on a table
619         if ($got_string) {
620             @fbsql_free_result($id);
621         }
622         return $res;
623     }
624
625     // }}}
626     // {{{ getSpecialQuery()
627
628     /**
629      * Returns the query needed to get some backend info
630      * @param  string $type What kind of info you want to retrieve
631      * @return string The SQL query string
632      */
633     function getSpecialQuery($type)
634     {
635         switch ($type) {
636             case 'tables':
637                 return 'select "table_name" from information_schema.tables';
638             default:
639                 return null;
640         }
641     }
642
643     // }}}
644 }
645
646 /*
647  * Local variables:
648  * tab-width: 4
649  * c-basic-offset: 4
650  * End:
651  */
652
653 ?>