source: tmp/version-2_5-test/data/module/DB/ifx.php @ 18609

Revision 18609, 19.5 KB checked in by kajiwara, 14 years ago (diff)

正式版にナイトリービルド版をマージしてみるテスト

  • 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 ifx extension
7 * for interacting with Informix 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     Tomas V.V.Cox <cox@idecnet.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 */
30require_once 'DB/common.php';
31
32/**
33 * The methods PEAR DB uses to interact with PHP's ifx extension
34 * for interacting with Informix databases
35 *
36 * These methods overload the ones declared in DB_common.
37 *
38 * More info on Informix errors can be found at:
39 * http://www.informix.com/answers/english/ierrors.htm
40 *
41 * TODO:
42 *   - set needed env Informix vars on connect
43 *   - implement native prepare/execute
44 *
45 * @category   Database
46 * @package    DB
47 * @author     Tomas V.V.Cox <cox@idecnet.com>
48 * @author     Daniel Convissor <danielc@php.net>
49 * @copyright  1997-2007 The PHP Group
50 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
51 * @version    Release: 1.7.14RC1
52 * @link       http://pear.php.net/package/DB
53 */
54class DB_ifx extends DB_common
55{
56    // {{{ properties
57
58    /**
59     * The DB driver type (mysql, oci8, odbc, etc.)
60     * @var string
61     */
62    var $phptype = 'ifx';
63
64    /**
65     * The database syntax variant to be used (db2, access, etc.), if any
66     * @var string
67     */
68    var $dbsyntax = 'ifx';
69
70    /**
71     * The capabilities of this DB implementation
72     *
73     * The 'new_link' element contains the PHP version that first provided
74     * new_link support for this DBMS.  Contains false if it's unsupported.
75     *
76     * Meaning of the 'limit' element:
77     *   + 'emulate' = emulate with fetch row by number
78     *   + 'alter'   = alter the query
79     *   + false     = skip rows
80     *
81     * @var array
82     */
83    var $features = array(
84        'limit'         => 'emulate',
85        'new_link'      => false,
86        'numrows'       => 'emulate',
87        'pconnect'      => true,
88        'prepare'       => false,
89        'ssl'           => false,
90        'transactions'  => true,
91    );
92
93    /**
94     * A mapping of native error codes to DB error codes
95     * @var array
96     */
97    var $errorcode_map = array(
98        '-201'    => DB_ERROR_SYNTAX,
99        '-206'    => DB_ERROR_NOSUCHTABLE,
100        '-217'    => DB_ERROR_NOSUCHFIELD,
101        '-236'    => DB_ERROR_VALUE_COUNT_ON_ROW,
102        '-239'    => DB_ERROR_CONSTRAINT,
103        '-253'    => DB_ERROR_SYNTAX,
104        '-268'    => DB_ERROR_CONSTRAINT,
105        '-292'    => DB_ERROR_CONSTRAINT_NOT_NULL,
106        '-310'    => DB_ERROR_ALREADY_EXISTS,
107        '-316'    => DB_ERROR_ALREADY_EXISTS,
108        '-319'    => DB_ERROR_NOT_FOUND,
109        '-329'    => DB_ERROR_NODBSELECTED,
110        '-346'    => DB_ERROR_CONSTRAINT,
111        '-386'    => DB_ERROR_CONSTRAINT_NOT_NULL,
112        '-391'    => DB_ERROR_CONSTRAINT_NOT_NULL,
113        '-554'    => DB_ERROR_SYNTAX,
114        '-691'    => DB_ERROR_CONSTRAINT,
115        '-692'    => DB_ERROR_CONSTRAINT,
116        '-703'    => DB_ERROR_CONSTRAINT_NOT_NULL,
117        '-1202'   => DB_ERROR_DIVZERO,
118        '-1204'   => DB_ERROR_INVALID_DATE,
119        '-1205'   => DB_ERROR_INVALID_DATE,
120        '-1206'   => DB_ERROR_INVALID_DATE,
121        '-1209'   => DB_ERROR_INVALID_DATE,
122        '-1210'   => DB_ERROR_INVALID_DATE,
123        '-1212'   => DB_ERROR_INVALID_DATE,
124        '-1213'   => DB_ERROR_INVALID_NUMBER,
125    );
126
127    /**
128     * The raw database connection created by PHP
129     * @var resource
130     */
131    var $connection;
132
133    /**
134     * The DSN information for connecting to a database
135     * @var array
136     */
137    var $dsn = array();
138
139
140    /**
141     * Should data manipulation queries be committed automatically?
142     * @var bool
143     * @access private
144     */
145    var $autocommit = true;
146
147    /**
148     * The quantity of transactions begun
149     *
150     * {@internal  While this is private, it can't actually be designated
151     * private in PHP 5 because it is directly accessed in the test suite.}}
152     *
153     * @var integer
154     * @access private
155     */
156    var $transaction_opcount = 0;
157
158    /**
159     * The number of rows affected by a data manipulation query
160     * @var integer
161     * @access private
162     */
163    var $affected = 0;
164
165
166    // }}}
167    // {{{ constructor
168
169    /**
170     * This constructor calls <kbd>$this->DB_common()</kbd>
171     *
172     * @return void
173     */
174    function DB_ifx()
175    {
176        $this->DB_common();
177    }
178
179    // }}}
180    // {{{ connect()
181
182    /**
183     * Connect to the database server, log in and open the database
184     *
185     * Don't call this method directly.  Use DB::connect() instead.
186     *
187     * @param array $dsn         the data source name
188     * @param bool  $persistent  should the connection be persistent?
189     *
190     * @return int  DB_OK on success. A DB_Error object on failure.
191     */
192    function connect($dsn, $persistent = false)
193    {
194        if (!PEAR::loadExtension('informix') &&
195            !PEAR::loadExtension('Informix'))
196        {
197            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
198        }
199
200        $this->dsn = $dsn;
201        if ($dsn['dbsyntax']) {
202            $this->dbsyntax = $dsn['dbsyntax'];
203        }
204
205        $dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : '';
206        $dbname = $dsn['database'] ? $dsn['database'] . $dbhost : '';
207        $user = $dsn['username'] ? $dsn['username'] : '';
208        $pw = $dsn['password'] ? $dsn['password'] : '';
209
210        $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect';
211
212        $this->connection = @$connect_function($dbname, $user, $pw);
213        if (!is_resource($this->connection)) {
214            return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED);
215        }
216        return DB_OK;
217    }
218
219    // }}}
220    // {{{ disconnect()
221
222    /**
223     * Disconnects from the database server
224     *
225     * @return bool  TRUE on success, FALSE on failure
226     */
227    function disconnect()
228    {
229        $ret = @ifx_close($this->connection);
230        $this->connection = null;
231        return $ret;
232    }
233
234    // }}}
235    // {{{ simpleQuery()
236
237    /**
238     * Sends a query to the database server
239     *
240     * @param string  the SQL query string
241     *
242     * @return mixed  + a PHP result resrouce for successful SELECT queries
243     *                + the DB_OK constant for other successful queries
244     *                + a DB_Error object on failure
245     */
246    function simpleQuery($query)
247    {
248        $ismanip = $this->_checkManip($query);
249        $this->last_query = $query;
250        $this->affected   = null;
251        if (preg_match('/(SELECT|EXECUTE)/i', $query)) {    //TESTME: Use !DB::isManip()?
252            // the scroll is needed for fetching absolute row numbers
253            // in a select query result
254            $result = @ifx_query($query, $this->connection, IFX_SCROLL);
255        } else {
256            if (!$this->autocommit && $ismanip) {
257                if ($this->transaction_opcount == 0) {
258                    $result = @ifx_query('BEGIN WORK', $this->connection);
259                    if (!$result) {
260                        return $this->ifxRaiseError();
261                    }
262                }
263                $this->transaction_opcount++;
264            }
265            $result = @ifx_query($query, $this->connection);
266        }
267        if (!$result) {
268            return $this->ifxRaiseError();
269        }
270        $this->affected = @ifx_affected_rows($result);
271        // Determine which queries should return data, and which
272        // should return an error code only.
273        if (preg_match('/(SELECT|EXECUTE)/i', $query)) {
274            return $result;
275        }
276        // XXX Testme: free results inside a transaction
277        // may cause to stop it and commit the work?
278
279        // Result has to be freed even with a insert or update
280        @ifx_free_result($result);
281
282        return DB_OK;
283    }
284
285    // }}}
286    // {{{ nextResult()
287
288    /**
289     * Move the internal ifx result pointer to the next available result
290     *
291     * @param a valid fbsql result resource
292     *
293     * @access public
294     *
295     * @return true if a result is available otherwise return false
296     */
297    function nextResult($result)
298    {
299        return false;
300    }
301
302    // }}}
303    // {{{ affectedRows()
304
305    /**
306     * Determines the number of rows affected by a data maniuplation query
307     *
308     * 0 is returned for queries that don't manipulate data.
309     *
310     * @return int  the number of rows.  A DB_Error object on failure.
311     */
312    function affectedRows()
313    {
314        if ($this->_last_query_manip) {
315            return $this->affected;
316        } else {
317            return 0;
318        }
319    }
320
321    // }}}
322    // {{{ fetchInto()
323
324    /**
325     * Places a row from the result set into the given array
326     *
327     * Formating of the array and the data therein are configurable.
328     * See DB_result::fetchInto() for more information.
329     *
330     * This method is not meant to be called directly.  Use
331     * DB_result::fetchInto() instead.  It can't be declared "protected"
332     * because DB_result is a separate object.
333     *
334     * @param resource $result    the query result resource
335     * @param array    $arr       the referenced array to put the data in
336     * @param int      $fetchmode how the resulting array should be indexed
337     * @param int      $rownum    the row number to fetch (0 = first row)
338     *
339     * @return mixed  DB_OK on success, NULL when the end of a result set is
340     *                 reached or on failure
341     *
342     * @see DB_result::fetchInto()
343     */
344    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
345    {
346        if (($rownum !== null) && ($rownum < 0)) {
347            return null;
348        }
349        if ($rownum === null) {
350            /*
351             * Even though fetch_row() should return the next row  if
352             * $rownum is null, it doesn't in all cases.  Bug 598.
353             */
354            $rownum = 'NEXT';
355        } else {
356            // Index starts at row 1, unlike most DBMS's starting at 0.
357            $rownum++;
358        }
359        if (!$arr = @ifx_fetch_row($result, $rownum)) {
360            return null;
361        }
362        if ($fetchmode !== DB_FETCHMODE_ASSOC) {
363            $i=0;
364            $order = array();
365            foreach ($arr as $val) {
366                $order[$i++] = $val;
367            }
368            $arr = $order;
369        } elseif ($fetchmode == DB_FETCHMODE_ASSOC &&
370                  $this->options['portability'] & DB_PORTABILITY_LOWERCASE)
371        {
372            $arr = array_change_key_case($arr, CASE_LOWER);
373        }
374        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
375            $this->_rtrimArrayValues($arr);
376        }
377        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
378            $this->_convertNullArrayValuesToEmpty($arr);
379        }
380        return DB_OK;
381    }
382
383    // }}}
384    // {{{ numCols()
385
386    /**
387     * Gets the number of columns in a result set
388     *
389     * This method is not meant to be called directly.  Use
390     * DB_result::numCols() instead.  It can't be declared "protected"
391     * because DB_result is a separate object.
392     *
393     * @param resource $result  PHP's query result resource
394     *
395     * @return int  the number of columns.  A DB_Error object on failure.
396     *
397     * @see DB_result::numCols()
398     */
399    function numCols($result)
400    {
401        if (!$cols = @ifx_num_fields($result)) {
402            return $this->ifxRaiseError();
403        }
404        return $cols;
405    }
406
407    // }}}
408    // {{{ freeResult()
409
410    /**
411     * Deletes the result set and frees the memory occupied by the result set
412     *
413     * This method is not meant to be called directly.  Use
414     * DB_result::free() instead.  It can't be declared "protected"
415     * because DB_result is a separate object.
416     *
417     * @param resource $result  PHP's query result resource
418     *
419     * @return bool  TRUE on success, FALSE if $result is invalid
420     *
421     * @see DB_result::free()
422     */
423    function freeResult($result)
424    {
425        return is_resource($result) ? ifx_free_result($result) : false;
426    }
427
428    // }}}
429    // {{{ autoCommit()
430
431    /**
432     * Enables or disables automatic commits
433     *
434     * @param bool $onoff  true turns it on, false turns it off
435     *
436     * @return int  DB_OK on success.  A DB_Error object if the driver
437     *               doesn't support auto-committing transactions.
438     */
439    function autoCommit($onoff = true)
440    {
441        // XXX if $this->transaction_opcount > 0, we should probably
442        // issue a warning here.
443        $this->autocommit = $onoff ? true : false;
444        return DB_OK;
445    }
446
447    // }}}
448    // {{{ commit()
449
450    /**
451     * Commits the current transaction
452     *
453     * @return int  DB_OK on success.  A DB_Error object on failure.
454     */
455    function commit()
456    {
457        if ($this->transaction_opcount > 0) {
458            $result = @ifx_query('COMMIT WORK', $this->connection);
459            $this->transaction_opcount = 0;
460            if (!$result) {
461                return $this->ifxRaiseError();
462            }
463        }
464        return DB_OK;
465    }
466
467    // }}}
468    // {{{ rollback()
469
470    /**
471     * Reverts the current transaction
472     *
473     * @return int  DB_OK on success.  A DB_Error object on failure.
474     */
475    function rollback()
476    {
477        if ($this->transaction_opcount > 0) {
478            $result = @ifx_query('ROLLBACK WORK', $this->connection);
479            $this->transaction_opcount = 0;
480            if (!$result) {
481                return $this->ifxRaiseError();
482            }
483        }
484        return DB_OK;
485    }
486
487    // }}}
488    // {{{ ifxRaiseError()
489
490    /**
491     * Produces a DB_Error object regarding the current problem
492     *
493     * @param int $errno  if the error is being manually raised pass a
494     *                     DB_ERROR* constant here.  If this isn't passed
495     *                     the error information gathered from the DBMS.
496     *
497     * @return object  the DB_Error object
498     *
499     * @see DB_common::raiseError(),
500     *      DB_ifx::errorNative(), DB_ifx::errorCode()
501     */
502    function ifxRaiseError($errno = null)
503    {
504        if ($errno === null) {
505            $errno = $this->errorCode(ifx_error());
506        }
507        return $this->raiseError($errno, null, null, null,
508                                 $this->errorNative());
509    }
510
511    // }}}
512    // {{{ errorNative()
513
514    /**
515     * Gets the DBMS' native error code and message produced by the last query
516     *
517     * @return string  the DBMS' error code and message
518     */
519    function errorNative()
520    {
521        return @ifx_error() . ' ' . @ifx_errormsg();
522    }
523
524    // }}}
525    // {{{ errorCode()
526
527    /**
528     * Maps native error codes to DB's portable ones.
529     *
530     * Requires that the DB implementation's constructor fills
531     * in the <var>$errorcode_map</var> property.
532     *
533     * @param  string  $nativecode  error code returned by the database
534     * @return int a portable DB error code, or DB_ERROR if this DB
535     * implementation has no mapping for the given error code.
536     */
537    function errorCode($nativecode)
538    {
539        if (ereg('SQLCODE=(.*)]', $nativecode, $match)) {
540            $code = $match[1];
541            if (isset($this->errorcode_map[$code])) {
542                return $this->errorcode_map[$code];
543            }
544        }
545        return DB_ERROR;
546    }
547
548    // }}}
549    // {{{ tableInfo()
550
551    /**
552     * Returns information about a table or a result set
553     *
554     * NOTE: only supports 'table' if <var>$result</var> is a table name.
555     *
556     * If analyzing a query result and the result has duplicate field names,
557     * an error will be raised saying
558     * <samp>can't distinguish duplicate field names</samp>.
559     *
560     * @param object|string  $result  DB_result object from a query or a
561     *                                 string containing the name of a table.
562     *                                 While this also accepts a query result
563     *                                 resource identifier, this behavior is
564     *                                 deprecated.
565     * @param int            $mode    a valid tableInfo mode
566     *
567     * @return array  an associative array with the information requested.
568     *                 A DB_Error object on failure.
569     *
570     * @see DB_common::tableInfo()
571     * @since Method available since Release 1.6.0
572     */
573    function tableInfo($result, $mode = null)
574    {
575        if (is_string($result)) {
576            /*
577             * Probably received a table name.
578             * Create a result resource identifier.
579             */
580            $id = @ifx_query("SELECT * FROM $result WHERE 1=0",
581                             $this->connection);
582            $got_string = true;
583        } elseif (isset($result->result)) {
584            /*
585             * Probably received a result object.
586             * Extract the result resource identifier.
587             */
588            $id = $result->result;
589            $got_string = false;
590        } else {
591            /*
592             * Probably received a result resource identifier.
593             * Copy it.
594             */
595            $id = $result;
596            $got_string = false;
597        }
598
599        if (!is_resource($id)) {
600            return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA);
601        }
602
603        $flds = @ifx_fieldproperties($id);
604        $count = @ifx_num_fields($id);
605
606        if (count($flds) != $count) {
607            return $this->raiseError("can't distinguish duplicate field names");
608        }
609
610        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
611            $case_func = 'strtolower';
612        } else {
613            $case_func = 'strval';
614        }
615
616        $i   = 0;
617        $res = array();
618
619        if ($mode) {
620            $res['num_fields'] = $count;
621        }
622
623        foreach ($flds as $key => $value) {
624            $props = explode(';', $value);
625            $res[$i] = array(
626                'table' => $got_string ? $case_func($result) : '',
627                'name'  => $case_func($key),
628                'type'  => $props[0],
629                'len'   => $props[1],
630                'flags' => $props[4] == 'N' ? 'not_null' : '',
631            );
632            if ($mode & DB_TABLEINFO_ORDER) {
633                $res['order'][$res[$i]['name']] = $i;
634            }
635            if ($mode & DB_TABLEINFO_ORDERTABLE) {
636                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
637            }
638            $i++;
639        }
640
641        // free the result only if we were called on a table
642        if ($got_string) {
643            @ifx_free_result($id);
644        }
645        return $res;
646    }
647
648    // }}}
649    // {{{ getSpecialQuery()
650
651    /**
652     * Obtains the query string needed for listing a given type of objects
653     *
654     * @param string $type  the kind of objects you want to retrieve
655     *
656     * @return string  the SQL query string or null if the driver doesn't
657     *                  support the object type requested
658     *
659     * @access protected
660     * @see DB_common::getListOf()
661     */
662    function getSpecialQuery($type)
663    {
664        switch ($type) {
665            case 'tables':
666                return 'SELECT tabname FROM systables WHERE tabid >= 100';
667            default:
668                return null;
669        }
670    }
671
672    // }}}
673
674}
675
676/*
677 * Local variables:
678 * tab-width: 4
679 * c-basic-offset: 4
680 * End:
681 */
682
683?>
Note: See TracBrowser for help on using the repository browser.