]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/pear/DB/ibase.php
Activated Id substitution for Subversion
[SourceForge/phpwiki.git] / lib / pear / DB / ibase.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: Sterling Hughes <sterling@php.net>                           |
17 // | Maintainer: Daniel Convissor <danielc@php.net>                       |
18 // +----------------------------------------------------------------------+
19 //
20 // $Id$
21
22
23 // Bugs:
24 //  - If dbsyntax is not firebird, the limitQuery may fail
25
26
27 require_once 'DB/common.php';
28
29 /**
30  * Database independent query interface definition for PHP's Interbase
31  * extension.
32  *
33  * @package  DB
34  * @version  $Id$
35  * @category Database
36  * @author   Sterling Hughes <sterling@php.net>
37  */
38 class DB_ibase extends DB_common
39 {
40
41     // {{{ properties
42
43     var $connection;
44     var $phptype, $dbsyntax;
45     var $autocommit = 1;
46     var $manip_query = array();
47
48     // }}}
49     // {{{ constructor
50
51     function DB_ibase()
52     {
53         $this->DB_common();
54         $this->phptype = 'ibase';
55         $this->dbsyntax = 'ibase';
56         $this->features = array(
57             'prepare' => true,
58             'pconnect' => true,
59             'transactions' => true,
60             'limit' => false
61         );
62         // just a few of the tons of Interbase error codes listed in the
63         // Language Reference section of the Interbase manual
64         $this->errorcode_map = array(
65             -104 => DB_ERROR_SYNTAX,
66             -150 => DB_ERROR_ACCESS_VIOLATION,
67             -151 => DB_ERROR_ACCESS_VIOLATION,
68             -155 => DB_ERROR_NOSUCHTABLE,
69             88   => DB_ERROR_NOSUCHTABLE,
70             -157 => DB_ERROR_NOSUCHFIELD,
71             -158 => DB_ERROR_VALUE_COUNT_ON_ROW,
72             -170 => DB_ERROR_MISMATCH,
73             -171 => DB_ERROR_MISMATCH,
74             -172 => DB_ERROR_INVALID,
75             -204 => DB_ERROR_INVALID,
76             -205 => DB_ERROR_NOSUCHFIELD,
77             -206 => DB_ERROR_NOSUCHFIELD,
78             -208 => DB_ERROR_INVALID,
79             -219 => DB_ERROR_NOSUCHTABLE,
80             -297 => DB_ERROR_CONSTRAINT,
81             -530 => DB_ERROR_CONSTRAINT,
82             -607 => DB_ERROR_NOSUCHTABLE,
83             -803 => DB_ERROR_CONSTRAINT,
84             -551 => DB_ERROR_ACCESS_VIOLATION,
85             -552 => DB_ERROR_ACCESS_VIOLATION,
86             -922 => DB_ERROR_NOSUCHDB,
87             -923 => DB_ERROR_CONNECT_FAILED,
88             -924 => DB_ERROR_CONNECT_FAILED
89         );
90     }
91
92     // }}}
93     // {{{ connect()
94
95     function connect($dsninfo, $persistent = false)
96     {
97         if (!DB::assertExtension('interbase')) {
98             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
99         }
100         $this->dsn = $dsninfo;
101         $dbhost = $dsninfo['hostspec'] ?
102                   ($dsninfo['hostspec'] . ':' . $dsninfo['database']) :
103                   $dsninfo['database'];
104
105         $connect_function = $persistent ? 'ibase_pconnect' : 'ibase_connect';
106
107         $params = array();
108         $params[] = $dbhost;
109         $params[] = $dsninfo['username'] ? $dsninfo['username'] : null;
110         $params[] = $dsninfo['password'] ? $dsninfo['password'] : null;
111         $params[] = isset($dsninfo['charset']) ? $dsninfo['charset'] : null;
112         $params[] = isset($dsninfo['buffers']) ? $dsninfo['buffers'] : null;
113         $params[] = isset($dsninfo['dialect']) ? $dsninfo['dialect'] : null;
114         $params[] = isset($dsninfo['role'])    ? $dsninfo['role'] : null;
115
116         $conn = @call_user_func_array($connect_function, $params);
117         if (!$conn) {
118             return $this->ibaseRaiseError(DB_ERROR_CONNECT_FAILED);
119         }
120         $this->connection = $conn;
121         if ($this->dsn['dbsyntax'] == 'firebird') {
122             $this->features['limit'] = 'alter';
123         }
124         return DB_OK;
125     }
126
127     // }}}
128     // {{{ disconnect()
129
130     function disconnect()
131     {
132         $ret = @ibase_close($this->connection);
133         $this->connection = null;
134         return $ret;
135     }
136
137     // }}}
138     // {{{ simpleQuery()
139
140     function simpleQuery($query)
141     {
142         $ismanip = DB::isManip($query);
143         $this->last_query = $query;
144         $query = $this->modifyQuery($query);
145         $result = @ibase_query($this->connection, $query);
146         if (!$result) {
147             return $this->ibaseRaiseError();
148         }
149         if ($this->autocommit && $ismanip) {
150             @ibase_commit($this->connection);
151         }
152         // Determine which queries that should return data, and which
153         // should return an error code only.
154         return $ismanip ? DB_OK : $result;
155     }
156
157     // }}}
158     // {{{ modifyLimitQuery()
159
160     /**
161      * This method is used by backends to alter limited queries
162      * Uses the new FIRST n SKIP n Firebird 1.0 syntax, so it is
163      * only compatible with Firebird 1.x
164      *
165      * @param string  $query query to modify
166      * @param integer $from  the row to start to fetching
167      * @param integer $count the numbers of rows to fetch
168      *
169      * @return the new (modified) query
170      * @author Ludovico Magnocavallo <ludo@sumatrasolutions.com>
171      * @access private
172      */
173     function modifyLimitQuery($query, $from, $count)
174     {
175         if ($this->dsn['dbsyntax'] == 'firebird') {
176             //$from++; // SKIP starts from 1, ie SKIP 1 starts from the first record
177                            // (cox) Seems that SKIP starts in 0
178             $query = preg_replace('/^\s*select\s(.*)$/is',
179                                   "SELECT FIRST $count SKIP $from $1", $query);
180         }
181         return $query;
182     }
183
184     // }}}
185     // {{{ nextResult()
186
187     /**
188      * Move the internal ibase result pointer to the next available result
189      *
190      * @param a valid fbsql result resource
191      *
192      * @access public
193      *
194      * @return true if a result is available otherwise return false
195      */
196     function nextResult($result)
197     {
198         return false;
199     }
200
201     // }}}
202     // {{{ fetchInto()
203
204     /**
205      * Fetch a row and insert the data into an existing array.
206      *
207      * Formating of the array and the data therein are configurable.
208      * See DB_result::fetchInto() for more information.
209      *
210      * @param resource $result    query result identifier
211      * @param array    $arr       (reference) array where data from the row
212      *                            should be placed
213      * @param int      $fetchmode how the resulting array should be indexed
214      * @param int      $rownum    the row number to fetch
215      *
216      * @return mixed DB_OK on success, null when end of result set is
217      *               reached or on failure
218      *
219      * @see DB_result::fetchInto()
220      * @access private
221      */
222     function fetchInto($result, &$arr, $fetchmode, $rownum=null)
223     {
224         if ($rownum !== null) {
225             return $this->ibaseRaiseError(DB_ERROR_NOT_CAPABLE);
226         }
227         if ($fetchmode & DB_FETCHMODE_ASSOC) {
228             if (function_exists('ibase_fetch_assoc')) {
229                 $arr = @ibase_fetch_assoc($result);
230             } else {
231                 $arr = get_object_vars(ibase_fetch_object($result));
232             }
233             if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
234                 $arr = array_change_key_case($arr, CASE_LOWER);
235             }
236         } else {
237             $arr = @ibase_fetch_row($result);
238         }
239         if (!$arr) {
240             if ($errmsg = @ibase_errmsg()) {
241                 return $this->ibaseRaiseError(null, $errmsg);
242             } else {
243                 return null;
244             }
245         }
246         if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
247             $this->_rtrimArrayValues($arr);
248         }
249         if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
250             $this->_convertNullArrayValuesToEmpty($arr);
251         }
252         return DB_OK;
253     }
254
255     // }}}
256     // {{{ freeResult()
257
258     function freeResult($result)
259     {
260         return @ibase_free_result($result);
261     }
262
263     // }}}
264     // {{{ freeQuery()
265
266     function freeQuery($query)
267     {
268         @ibase_free_query($query);
269         return true;
270     }
271
272     // }}}
273     // {{{ numCols()
274
275     function numCols($result)
276     {
277         $cols = @ibase_num_fields($result);
278         if (!$cols) {
279             return $this->ibaseRaiseError();
280         }
281         return $cols;
282     }
283
284     // }}}
285     // {{{ prepare()
286
287     /**
288      * Prepares a query for multiple execution with execute().
289      *
290      * prepare() requires a generic query as string like <code>
291      *    INSERT INTO numbers VALUES (?, ?, ?)
292      * </code>.  The <kbd>?</kbd> characters are placeholders.
293      *
294      * Three types of placeholders can be used:
295      *   + <kbd>?</kbd>  a quoted scalar value, i.e. strings, integers
296      *   + <kbd>!</kbd>  value is inserted 'as is'
297      *   + <kbd>&</kbd>  requires a file name.  The file's contents get
298      *                     inserted into the query (i.e. saving binary
299      *                     data in a db)
300      *
301      * Use backslashes to escape placeholder characters if you don't want
302      * them to be interpreted as placeholders.  Example: <code>
303      *    "UPDATE foo SET col=? WHERE col='over \& under'"
304      * </code>
305      *
306      * @param string $query query to be prepared
307      * @return mixed DB statement resource on success. DB_Error on failure.
308      */
309     function prepare($query)
310     {
311         $tokens   = preg_split('/((?<!\\\)[&?!])/', $query, -1,
312                                PREG_SPLIT_DELIM_CAPTURE);
313         $token    = 0;
314         $types    = array();
315         $newquery = '';
316
317         foreach ($tokens as $key => $val) {
318             switch ($val) {
319                 case '?':
320                     $types[$token++] = DB_PARAM_SCALAR;
321                     break;
322                 case '&':
323                     $types[$token++] = DB_PARAM_OPAQUE;
324                     break;
325                 case '!':
326                     $types[$token++] = DB_PARAM_MISC;
327                     break;
328                 default:
329                     $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
330                     $newquery .= $tokens[$key] . '?';
331             }
332         }
333
334         $newquery = substr($newquery, 0, -1);
335         $this->last_query = $query;
336         $newquery = $this->modifyQuery($newquery);
337         $stmt = @ibase_prepare($this->connection, $newquery);
338         $this->prepare_types[(int)$stmt] = $types;
339         $this->manip_query[(int)$stmt]   = DB::isManip($query);
340         return $stmt;
341     }
342
343     // }}}
344     // {{{ execute()
345
346     /**
347      * Executes a DB statement prepared with prepare().
348      *
349      * @param resource  $stmt  a DB statement resource returned from prepare()
350      * @param mixed  $data  array, string or numeric data to be used in
351      *                      execution of the statement.  Quantity of items
352      *                      passed must match quantity of placeholders in
353      *                      query:  meaning 1 for non-array items or the
354      *                      quantity of elements in the array.
355      * @return object  a new DB_Result or a DB_Error when fail
356      * @see DB_ibase::prepare()
357      * @access public
358      */
359     function &execute($stmt, $data = array())
360     {
361         if (!is_array($data)) {
362             $data = array($data);
363         }
364
365         $types =& $this->prepare_types[$stmt];
366         if (count($types) != count($data)) {
367             $tmp =& $this->raiseError(DB_ERROR_MISMATCH);
368             return $tmp;
369         }
370
371         $i = 0;
372         foreach ($data as $key => $value) {
373             if ($types[$i] == DB_PARAM_MISC) {
374                 /*
375                  * ibase doesn't seem to have the ability to pass a
376                  * parameter along unchanged, so strip off quotes from start
377                  * and end, plus turn two single quotes to one single quote,
378                  * in order to avoid the quotes getting escaped by
379                  * ibase and ending up in the database.
380                  */
381                 $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
382                 $data[$key] = str_replace("''", "'", $data[$key]);
383             } elseif ($types[$i] == DB_PARAM_OPAQUE) {
384                 $fp = @fopen($data[$key], 'rb');
385                 if (!$fp) {
386                     $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
387                     return $tmp;
388                 }
389                 $data[$key] = fread($fp, filesize($data[$key]));
390                 fclose($fp);
391             }
392             $i++;
393         }
394
395         array_unshift($data, $stmt);
396
397         $res = call_user_func_array('ibase_execute', $data);
398         if (!$res) {
399             $tmp =& $this->ibaseRaiseError();
400             return $tmp;
401         }
402         /* XXX need this?
403         if ($this->autocommit && $this->manip_query[(int)$stmt]) {
404             @ibase_commit($this->connection);
405         }*/
406         if ($this->manip_query[(int)$stmt]) {
407             $tmp = DB_OK;
408         } else {
409             $tmp =& new DB_result($this, $res);
410         }
411         return $tmp;
412     }
413
414     /**
415      * Free the internal resources associated with a prepared query.
416      *
417      * @param $stmt The interbase_query resource type
418      *
419      * @return bool true on success, false if $result is invalid
420      */
421     function freePrepared($stmt)
422     {
423         if (!is_resource($stmt)) {
424             return false;
425         }
426         @ibase_free_query($stmt);
427         unset($this->prepare_tokens[(int)$stmt]);
428         unset($this->prepare_types[(int)$stmt]);
429         unset($this->manip_query[(int)$stmt]);
430         return true;
431     }
432
433     // }}}
434     // {{{ autoCommit()
435
436     function autoCommit($onoff = false)
437     {
438         $this->autocommit = $onoff ? 1 : 0;
439         return DB_OK;
440     }
441
442     // }}}
443     // {{{ commit()
444
445     function commit()
446     {
447         return @ibase_commit($this->connection);
448     }
449
450     // }}}
451     // {{{ rollback()
452
453     function rollback()
454     {
455         return @ibase_rollback($this->connection);
456     }
457
458     // }}}
459     // {{{ transactionInit()
460
461     function transactionInit($trans_args = 0)
462     {
463         return $trans_args ? @ibase_trans($trans_args, $this->connection) : @ibase_trans();
464     }
465
466     // }}}
467     // {{{ nextId()
468
469     /**
470      * Returns the next free id in a sequence
471      *
472      * @param string  $seq_name  name of the sequence
473      * @param boolean $ondemand  when true, the seqence is automatically
474      *                           created if it does not exist
475      *
476      * @return int  the next id number in the sequence.  DB_Error if problem.
477      *
478      * @internal
479      * @see DB_common::nextID()
480      * @access public
481      */
482     function nextId($seq_name, $ondemand = true)
483     {
484         $sqn = strtoupper($this->getSequenceName($seq_name));
485         $repeat = 0;
486         do {
487             $this->pushErrorHandling(PEAR_ERROR_RETURN);
488             $result =& $this->query("SELECT GEN_ID(${sqn}, 1) "
489                                    . 'FROM RDB$GENERATORS '
490                                    . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
491             $this->popErrorHandling();
492             if ($ondemand && DB::isError($result)) {
493                 $repeat = 1;
494                 $result = $this->createSequence($seq_name);
495                 if (DB::isError($result)) {
496                     return $result;
497                 }
498             } else {
499                 $repeat = 0;
500             }
501         } while ($repeat);
502         if (DB::isError($result)) {
503             return $this->raiseError($result);
504         }
505         $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
506         $result->free();
507         return $arr[0];
508     }
509
510     // }}}
511     // {{{ createSequence()
512
513     /**
514      * Create the sequence
515      *
516      * @param string $seq_name the name of the sequence
517      * @return mixed DB_OK on success or DB error on error
518      * @access public
519      */
520     function createSequence($seq_name)
521     {
522         $sqn = strtoupper($this->getSequenceName($seq_name));
523         $this->pushErrorHandling(PEAR_ERROR_RETURN);
524         $result = $this->query("CREATE GENERATOR ${sqn}");
525         $this->popErrorHandling();
526
527         return $result;
528     }
529
530     // }}}
531     // {{{ dropSequence()
532
533     /**
534      * Drop a sequence
535      *
536      * @param string $seq_name the name of the sequence
537      * @return mixed DB_OK on success or DB error on error
538      * @access public
539      */
540     function dropSequence($seq_name)
541     {
542         $sqn = strtoupper($this->getSequenceName($seq_name));
543         return $this->query('DELETE FROM RDB$GENERATORS '
544                             . "WHERE RDB\$GENERATOR_NAME='${sqn}'");
545     }
546
547     // }}}
548     // {{{ _ibaseFieldFlags()
549
550     /**
551      * get the Flags of a Field
552      *
553      * @param string $field_name the name of the field
554      * @param string $table_name the name of the table
555      *
556      * @return string The flags of the field ("primary_key", "unique_key", "not_null"
557      *                "default", "computed" and "blob" are supported)
558      * @access private
559      */
560     function _ibaseFieldFlags($field_name, $table_name)
561     {
562         $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE'
563                .' FROM RDB$INDEX_SEGMENTS I'
564                .'  JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME'
565                .' WHERE I.RDB$FIELD_NAME=\'' . $field_name . '\''
566                .'  AND UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\'';
567
568         $result = @ibase_query($this->connection, $sql);
569         if (!$result) {
570             return $this->ibaseRaiseError();
571         }
572
573         $flags = '';
574         if ($obj = @ibase_fetch_object($result)) {
575             @ibase_free_result($result);
576             if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'PRIMARY KEY') {
577                 $flags .= 'primary_key ';
578             }
579             if (isset($obj->CTYPE)  && trim($obj->CTYPE) == 'UNIQUE') {
580                 $flags .= 'unique_key ';
581             }
582         }
583
584         $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,'
585                .'  R.RDB$DEFAULT_SOURCE AS DSOURCE,'
586                .'  F.RDB$FIELD_TYPE AS FTYPE,'
587                .'  F.RDB$COMPUTED_SOURCE AS CSOURCE'
588                .' FROM RDB$RELATION_FIELDS R '
589                .'  JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME'
590                .' WHERE UPPER(R.RDB$RELATION_NAME)=\'' . strtoupper($table_name) . '\''
591                .'  AND R.RDB$FIELD_NAME=\'' . $field_name . '\'';
592
593         $result = @ibase_query($this->connection, $sql);
594         if (!$result) {
595             return $this->ibaseRaiseError();
596         }
597         if ($obj = @ibase_fetch_object($result)) {
598             @ibase_free_result($result);
599             if (isset($obj->NFLAG)) {
600                 $flags .= 'not_null ';
601             }
602             if (isset($obj->DSOURCE)) {
603                 $flags .= 'default ';
604             }
605             if (isset($obj->CSOURCE)) {
606                 $flags .= 'computed ';
607             }
608             if (isset($obj->FTYPE)  && $obj->FTYPE == 261) {
609                 $flags .= 'blob ';
610             }
611         }
612
613         return trim($flags);
614     }
615
616     // }}}
617     // {{{ tableInfo()
618
619     /**
620      * Returns information about a table or a result set.
621      *
622      * NOTE: only supports 'table' and 'flags' if <var>$result</var>
623      * is a table name.
624      *
625      * @param object|string  $result  DB_result object from a query or a
626      *                                string containing the name of a table
627      * @param int            $mode    a valid tableInfo mode
628      * @return array  an associative array with the information requested
629      *                or an error object if something is wrong
630      * @access public
631      * @internal
632      * @see DB_common::tableInfo()
633      */
634     function tableInfo($result, $mode = null)
635     {
636         if (isset($result->result)) {
637             /*
638              * Probably received a result object.
639              * Extract the result resource identifier.
640              */
641             $id = $result->result;
642             $got_string = false;
643         } elseif (is_string($result)) {
644             /*
645              * Probably received a table name.
646              * Create a result resource identifier.
647              */
648              $id = @ibase_query($this->connection,
649                                 "SELECT * FROM $result WHERE 1=0");
650             $got_string = true;
651         } else {
652             /*
653              * Probably received a result resource identifier.
654              * Copy it.
655              * Depricated.  Here for compatibility only.
656              */
657              $id = $result;
658             $got_string = false;
659         }
660
661         if (!is_resource($id)) {
662             return $this->ibaseRaiseError(DB_ERROR_NEED_MORE_DATA);
663         }
664
665         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
666             $case_func = 'strtolower';
667         } else {
668             $case_func = 'strval';
669         }
670
671         $count = @ibase_num_fields($id);
672
673         // made this IF due to performance (one if is faster than $count if's)
674         if (!$mode) {
675             for ($i=0; $i<$count; $i++) {
676                 $info = @ibase_field_info($id, $i);
677                 $res[$i]['table'] = $got_string ? $case_func($result) : '';
678                 $res[$i]['name']  = $case_func($info['name']);
679                 $res[$i]['type']  = $info['type'];
680                 $res[$i]['len']   = $info['length'];
681                 $res[$i]['flags'] = ($got_string) ? $this->_ibaseFieldFlags($info['name'], $result) : '';
682             }
683         } else { // full
684             $res['num_fields']= $count;
685
686             for ($i=0; $i<$count; $i++) {
687                 $info = @ibase_field_info($id, $i);
688                 $res[$i]['table'] = $got_string ? $case_func($result) : '';
689                 $res[$i]['name']  = $case_func($info['name']);
690                 $res[$i]['type']  = $info['type'];
691                 $res[$i]['len']   = $info['length'];
692                 $res[$i]['flags'] = ($got_string) ? $this->_ibaseFieldFlags($info['name'], $result) : '';
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
703         // free the result only if we were called on a table
704         if ($got_string) {
705             @ibase_free_result($id);
706         }
707         return $res;
708     }
709
710     // }}}
711     // {{{ ibaseRaiseError()
712
713     /**
714      * Gather information about an error, then use that info to create a
715      * DB error object and finally return that object.
716      *
717      * @param  integer  $db_errno  PEAR error number (usually a DB constant) if
718      *                             manually raising an error
719      * @param  string  $native_errmsg  text of error message if known
720      * @return object  DB error object
721      * @see DB_common::errorCode()
722      * @see DB_common::raiseError()
723      */
724     function &ibaseRaiseError($db_errno = null, $native_errmsg = null)
725     {
726         if ($native_errmsg === null) {
727             $native_errmsg = @ibase_errmsg();
728         }
729         // memo for the interbase php module hackers: we need something similar
730         // to mysql_errno() to retrieve error codes instead of this ugly hack
731         if (preg_match('/^([^0-9\-]+)([0-9\-]+)\s+(.*)$/', $native_errmsg, $m)) {
732             $native_errno = (int)$m[2];
733         } else {
734             $native_errno = null;
735         }
736         // try to map the native error to the DB one
737         if ($db_errno === null) {
738             if ($native_errno) {
739                 // try to interpret Interbase error code (that's why we need ibase_errno()
740                 // in the interbase module to return the real error code)
741                 switch ($native_errno) {
742                     case -204:
743                         if (is_int(strpos($m[3], 'Table unknown'))) {
744                             $db_errno = DB_ERROR_NOSUCHTABLE;
745                         }
746                         break;
747                     default:
748                         $db_errno = $this->errorCode($native_errno);
749                 }
750             } else {
751                 $error_regexps = array(
752                     '/[tT]able not found/' => DB_ERROR_NOSUCHTABLE,
753                     '/[tT]able .* already exists/' => DB_ERROR_ALREADY_EXISTS,
754                     '/validation error for column .* value "\*\*\* null/' => DB_ERROR_CONSTRAINT_NOT_NULL,
755                     '/violation of [\w ]+ constraint/' => DB_ERROR_CONSTRAINT,
756                     '/conversion error from string/' => DB_ERROR_INVALID_NUMBER,
757                     '/no permission for/' => DB_ERROR_ACCESS_VIOLATION,
758                     '/arithmetic exception, numeric overflow, or string truncation/' => DB_ERROR_DIVZERO
759                 );
760                 foreach ($error_regexps as $regexp => $code) {
761                     if (preg_match($regexp, $native_errmsg)) {
762                         $db_errno = $code;
763                         $native_errno = null;
764                         break;
765                     }
766                 }
767             }
768         }
769         $tmp =& $this->raiseError($db_errno, null, null, null, $native_errmsg);
770         return $tmp;
771     }
772
773     // }}}
774
775 }
776
777 /*
778  * Local variables:
779  * tab-width: 4
780  * c-basic-offset: 4
781  * End:
782  */
783
784 ?>