source: branches/comu-ver2/data/module/DB/msql.php @ 17877

Revision 17877, 24.0 KB checked in by Seasoft, 15 years ago (diff)

・PEAR::DB を Ver.1.7.14RC1(2007-11-27)ベースに更改。
・不適切な文字列比較を修正。

 http://xoops.ec-cube.net/modules/newbb/viewtopic.php?topic_id=1808&forum=9

  • 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 msql extension
7 * for interacting with Mini SQL databases
8 *
9 * PHP's mSQL extension did weird things with NULL values prior to PHP
10 * 4.3.11 and 5.0.4.  Make sure your version of PHP meets or exceeds
11 * those versions.
12 *
13 * PHP versions 4 and 5
14 *
15 * LICENSE: This source file is subject to version 3.0 of the PHP license
16 * that is available through the world-wide-web at the following URI:
17 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
18 * the PHP License and are unable to obtain it through the web, please
19 * send a note to license@php.net so we can mail you a copy immediately.
20 *
21 * @category   Database
22 * @package    DB
23 * @author     Daniel Convissor <danielc@php.net>
24 * @copyright  1997-2007 The PHP Group
25 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
26 * @version    CVS: $Id$
27 * @link       http://pear.php.net/package/DB
28 */
29
30/**
31 * Obtain the DB_common class so it can be extended from
32 */
33require_once 'DB/common.php';
34
35/**
36 * The methods PEAR DB uses to interact with PHP's msql extension
37 * for interacting with Mini SQL databases
38 *
39 * These methods overload the ones declared in DB_common.
40 *
41 * PHP's mSQL extension did weird things with NULL values prior to PHP
42 * 4.3.11 and 5.0.4.  Make sure your version of PHP meets or exceeds
43 * those versions.
44 *
45 * @category   Database
46 * @package    DB
47 * @author     Daniel Convissor <danielc@php.net>
48 * @copyright  1997-2007 The PHP Group
49 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
50 * @version    Release: 1.7.14RC1
51 * @link       http://pear.php.net/package/DB
52 * @since      Class not functional until Release 1.7.0
53 */
54class DB_msql extends DB_common
55{
56    // {{{ properties
57
58    /**
59     * The DB driver type (mysql, oci8, odbc, etc.)
60     * @var string
61     */
62    var $phptype = 'msql';
63
64    /**
65     * The database syntax variant to be used (db2, access, etc.), if any
66     * @var string
67     */
68    var $dbsyntax = 'msql';
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'       => true,
87        'pconnect'      => true,
88        'prepare'       => false,
89        'ssl'           => false,
90        'transactions'  => false,
91    );
92
93    /**
94     * A mapping of native error codes to DB error codes
95     * @var array
96     */
97    var $errorcode_map = array(
98    );
99
100    /**
101     * The raw database connection created by PHP
102     * @var resource
103     */
104    var $connection;
105
106    /**
107     * The DSN information for connecting to a database
108     * @var array
109     */
110    var $dsn = array();
111
112
113    /**
114     * The query result resource created by PHP
115     *
116     * Used to make affectedRows() work.  Only contains the result for
117     * data manipulation queries.  Contains false for other queries.
118     *
119     * @var resource
120     * @access private
121     */
122    var $_result;
123
124
125    // }}}
126    // {{{ constructor
127
128    /**
129     * This constructor calls <kbd>$this->DB_common()</kbd>
130     *
131     * @return void
132     */
133    function DB_msql()
134    {
135        $this->DB_common();
136    }
137
138    // }}}
139    // {{{ connect()
140
141    /**
142     * Connect to the database server, log in and open the database
143     *
144     * Don't call this method directly.  Use DB::connect() instead.
145     *
146     * Example of how to connect:
147     * <code>
148     * require_once 'DB.php';
149     *
150     * // $dsn = 'msql://hostname/dbname';  // use a TCP connection
151     * $dsn = 'msql:///dbname';             // use a socket
152     * $options = array(
153     *     'portability' => DB_PORTABILITY_ALL,
154     * );
155     *
156     * $db = DB::connect($dsn, $options);
157     * if (PEAR::isError($db)) {
158     *     die($db->getMessage());
159     * }
160     * </code>
161     *
162     * @param array $dsn         the data source name
163     * @param bool  $persistent  should the connection be persistent?
164     *
165     * @return int  DB_OK on success. A DB_Error object on failure.
166     */
167    function connect($dsn, $persistent = false)
168    {
169        if (!PEAR::loadExtension('msql')) {
170            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
171        }
172
173        $this->dsn = $dsn;
174        if ($dsn['dbsyntax']) {
175            $this->dbsyntax = $dsn['dbsyntax'];
176        }
177
178        $params = array();
179        if ($dsn['hostspec']) {
180            $params[] = $dsn['port']
181                        ? $dsn['hostspec'] . ',' . $dsn['port']
182                        : $dsn['hostspec'];
183        }
184
185        $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect';
186
187        $ini = ini_get('track_errors');
188        $php_errormsg = '';
189        if ($ini) {
190            $this->connection = @call_user_func_array($connect_function,
191                                                      $params);
192        } else {
193            @ini_set('track_errors', 1);
194            $this->connection = @call_user_func_array($connect_function,
195                                                      $params);
196            @ini_set('track_errors', $ini);
197        }
198
199        if (!$this->connection) {
200            if (($err = @msql_error()) != '') {
201                return $this->raiseError(DB_ERROR_CONNECT_FAILED,
202                                         null, null, null,
203                                         $err);
204            } else {
205                return $this->raiseError(DB_ERROR_CONNECT_FAILED,
206                                         null, null, null,
207                                         $php_errormsg);
208            }
209        }
210
211        if (!@msql_select_db($dsn['database'], $this->connection)) {
212            return $this->msqlRaiseError();
213        }
214        return DB_OK;
215    }
216
217    // }}}
218    // {{{ disconnect()
219
220    /**
221     * Disconnects from the database server
222     *
223     * @return bool  TRUE on success, FALSE on failure
224     */
225    function disconnect()
226    {
227        $ret = @msql_close($this->connection);
228        $this->connection = null;
229        return $ret;
230    }
231
232    // }}}
233    // {{{ simpleQuery()
234
235    /**
236     * Sends a query to the database server
237     *
238     * @param string  the SQL query string
239     *
240     * @return mixed  + a PHP result resrouce for successful SELECT queries
241     *                + the DB_OK constant for other successful queries
242     *                + a DB_Error object on failure
243     */
244    function simpleQuery($query)
245    {
246        $this->last_query = $query;
247        $query = $this->modifyQuery($query);
248        $result = @msql_query($query, $this->connection);
249        if (!$result) {
250            return $this->msqlRaiseError();
251        }
252        // Determine which queries that should return data, and which
253        // should return an error code only.
254        if ($this->_checkManip($query)) {
255            $this->_result = $result;
256            return DB_OK;
257        } else {
258            $this->_result = false;
259            return $result;
260        }
261    }
262
263
264    // }}}
265    // {{{ nextResult()
266
267    /**
268     * Move the internal msql result pointer to the next available result
269     *
270     * @param a valid fbsql result resource
271     *
272     * @access public
273     *
274     * @return true if a result is available otherwise return false
275     */
276    function nextResult($result)
277    {
278        return false;
279    }
280
281    // }}}
282    // {{{ fetchInto()
283
284    /**
285     * Places a row from the result set into the given array
286     *
287     * Formating of the array and the data therein are configurable.
288     * See DB_result::fetchInto() for more information.
289     *
290     * This method is not meant to be called directly.  Use
291     * DB_result::fetchInto() instead.  It can't be declared "protected"
292     * because DB_result is a separate object.
293     *
294     * PHP's mSQL extension did weird things with NULL values prior to PHP
295     * 4.3.11 and 5.0.4.  Make sure your version of PHP meets or exceeds
296     * those versions.
297     *
298     * @param resource $result    the query result resource
299     * @param array    $arr       the referenced array to put the data in
300     * @param int      $fetchmode how the resulting array should be indexed
301     * @param int      $rownum    the row number to fetch (0 = first row)
302     *
303     * @return mixed  DB_OK on success, NULL when the end of a result set is
304     *                 reached or on failure
305     *
306     * @see DB_result::fetchInto()
307     */
308    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
309    {
310        if ($rownum !== null) {
311            if (!@msql_data_seek($result, $rownum)) {
312                return null;
313            }
314        }
315        if ($fetchmode & DB_FETCHMODE_ASSOC) {
316            $arr = @msql_fetch_array($result, MSQL_ASSOC);
317            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
318                $arr = array_change_key_case($arr, CASE_LOWER);
319            }
320        } else {
321            $arr = @msql_fetch_row($result);
322        }
323        if (!$arr) {
324            return null;
325        }
326        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
327            $this->_rtrimArrayValues($arr);
328        }
329        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
330            $this->_convertNullArrayValuesToEmpty($arr);
331        }
332        return DB_OK;
333    }
334
335    // }}}
336    // {{{ freeResult()
337
338    /**
339     * Deletes the result set and frees the memory occupied by the result set
340     *
341     * This method is not meant to be called directly.  Use
342     * DB_result::free() instead.  It can't be declared "protected"
343     * because DB_result is a separate object.
344     *
345     * @param resource $result  PHP's query result resource
346     *
347     * @return bool  TRUE on success, FALSE if $result is invalid
348     *
349     * @see DB_result::free()
350     */
351    function freeResult($result)
352    {
353        return is_resource($result) ? msql_free_result($result) : false;
354    }
355
356    // }}}
357    // {{{ numCols()
358
359    /**
360     * Gets the number of columns in a result set
361     *
362     * This method is not meant to be called directly.  Use
363     * DB_result::numCols() instead.  It can't be declared "protected"
364     * because DB_result is a separate object.
365     *
366     * @param resource $result  PHP's query result resource
367     *
368     * @return int  the number of columns.  A DB_Error object on failure.
369     *
370     * @see DB_result::numCols()
371     */
372    function numCols($result)
373    {
374        $cols = @msql_num_fields($result);
375        if (!$cols) {
376            return $this->msqlRaiseError();
377        }
378        return $cols;
379    }
380
381    // }}}
382    // {{{ numRows()
383
384    /**
385     * Gets the number of rows in a result set
386     *
387     * This method is not meant to be called directly.  Use
388     * DB_result::numRows() instead.  It can't be declared "protected"
389     * because DB_result is a separate object.
390     *
391     * @param resource $result  PHP's query result resource
392     *
393     * @return int  the number of rows.  A DB_Error object on failure.
394     *
395     * @see DB_result::numRows()
396     */
397    function numRows($result)
398    {
399        $rows = @msql_num_rows($result);
400        if ($rows === false) {
401            return $this->msqlRaiseError();
402        }
403        return $rows;
404    }
405
406    // }}}
407    // {{{ affected()
408
409    /**
410     * Determines the number of rows affected by a data maniuplation query
411     *
412     * 0 is returned for queries that don't manipulate data.
413     *
414     * @return int  the number of rows.  A DB_Error object on failure.
415     */
416    function affectedRows()
417    {
418        if (!$this->_result) {
419            return 0;
420        }
421        return msql_affected_rows($this->_result);
422    }
423
424    // }}}
425    // {{{ nextId()
426
427    /**
428     * Returns the next free id in a sequence
429     *
430     * @param string  $seq_name  name of the sequence
431     * @param boolean $ondemand  when true, the seqence is automatically
432     *                            created if it does not exist
433     *
434     * @return int  the next id number in the sequence.
435     *               A DB_Error object on failure.
436     *
437     * @see DB_common::nextID(), DB_common::getSequenceName(),
438     *      DB_msql::createSequence(), DB_msql::dropSequence()
439     */
440    function nextId($seq_name, $ondemand = true)
441    {
442        $seqname = $this->getSequenceName($seq_name);
443        $repeat = false;
444        do {
445            $this->pushErrorHandling(PEAR_ERROR_RETURN);
446            $result = $this->query("SELECT _seq FROM ${seqname}");
447            $this->popErrorHandling();
448            if ($ondemand && DB::isError($result) &&
449                $result->getCode() == DB_ERROR_NOSUCHTABLE) {
450                $repeat = true;
451                $this->pushErrorHandling(PEAR_ERROR_RETURN);
452                $result = $this->createSequence($seq_name);
453                $this->popErrorHandling();
454                if (DB::isError($result)) {
455                    return $this->raiseError($result);
456                }
457            } else {
458                $repeat = false;
459            }
460        } while ($repeat);
461        if (DB::isError($result)) {
462            return $this->raiseError($result);
463        }
464        $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
465        $result->free();
466        return $arr[0];
467    }
468
469    // }}}
470    // {{{ createSequence()
471
472    /**
473     * Creates a new sequence
474     *
475     * Also creates a new table to associate the sequence with.  Uses
476     * a separate table to ensure portability with other drivers.
477     *
478     * @param string $seq_name  name of the new sequence
479     *
480     * @return int  DB_OK on success.  A DB_Error object on failure.
481     *
482     * @see DB_common::createSequence(), DB_common::getSequenceName(),
483     *      DB_msql::nextID(), DB_msql::dropSequence()
484     */
485    function createSequence($seq_name)
486    {
487        $seqname = $this->getSequenceName($seq_name);
488        $res = $this->query('CREATE TABLE ' . $seqname
489                            . ' (id INTEGER NOT NULL)');
490        if (DB::isError($res)) {
491            return $res;
492        }
493        $res = $this->query("CREATE SEQUENCE ON ${seqname}");
494        return $res;
495    }
496
497    // }}}
498    // {{{ dropSequence()
499
500    /**
501     * Deletes a sequence
502     *
503     * @param string $seq_name  name of the sequence to be deleted
504     *
505     * @return int  DB_OK on success.  A DB_Error object on failure.
506     *
507     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
508     *      DB_msql::nextID(), DB_msql::createSequence()
509     */
510    function dropSequence($seq_name)
511    {
512        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
513    }
514
515    // }}}
516    // {{{ quoteIdentifier()
517
518    /**
519     * mSQL does not support delimited identifiers
520     *
521     * @param string $str  the identifier name to be quoted
522     *
523     * @return object  a DB_Error object
524     *
525     * @see DB_common::quoteIdentifier()
526     * @since Method available since Release 1.7.0
527     */
528    function quoteIdentifier($str)
529    {
530        return $this->raiseError(DB_ERROR_UNSUPPORTED);
531    }
532
533    // }}}
534    // {{{ quoteFloat()
535
536    /**
537     * Formats a float value for use within a query in a locale-independent
538     * manner.
539     *
540     * @param float the float value to be quoted.
541     * @return string the quoted string.
542     * @see DB_common::quoteSmart()
543     * @since Method available since release 1.7.8.
544     */
545    function quoteFloat($float) {
546        return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
547    }
548     
549    // }}}
550    // {{{ escapeSimple()
551
552    /**
553     * Escapes a string according to the current DBMS's standards
554     *
555     * @param string $str  the string to be escaped
556     *
557     * @return string  the escaped string
558     *
559     * @see DB_common::quoteSmart()
560     * @since Method available since Release 1.7.0
561     */
562    function escapeSimple($str)
563    {
564        return addslashes($str);
565    }
566
567    // }}}
568    // {{{ msqlRaiseError()
569
570    /**
571     * Produces a DB_Error object regarding the current problem
572     *
573     * @param int $errno  if the error is being manually raised pass a
574     *                     DB_ERROR* constant here.  If this isn't passed
575     *                     the error information gathered from the DBMS.
576     *
577     * @return object  the DB_Error object
578     *
579     * @see DB_common::raiseError(),
580     *      DB_msql::errorNative(), DB_msql::errorCode()
581     */
582    function msqlRaiseError($errno = null)
583    {
584        $native = $this->errorNative();
585        if ($errno === null) {
586            $errno = $this->errorCode($native);
587        }
588        return $this->raiseError($errno, null, null, null, $native);
589    }
590
591    // }}}
592    // {{{ errorNative()
593
594    /**
595     * Gets the DBMS' native error message produced by the last query
596     *
597     * @return string  the DBMS' error message
598     */
599    function errorNative()
600    {
601        return @msql_error();
602    }
603
604    // }}}
605    // {{{ errorCode()
606
607    /**
608     * Determines PEAR::DB error code from the database's text error message
609     *
610     * @param string $errormsg  the error message returned from the database
611     *
612     * @return integer  the error number from a DB_ERROR* constant
613     */
614    function errorCode($errormsg)
615    {
616        static $error_regexps;
617       
618        // PHP 5.2+ prepends the function name to $php_errormsg, so we need
619        // this hack to work around it, per bug #9599.
620        $errormsg = preg_replace('/^msql[a-z_]+\(\): /', '', $errormsg);
621
622        if (!isset($error_regexps)) {
623            $error_regexps = array(
624                '/^Access to database denied/i'
625                    => DB_ERROR_ACCESS_VIOLATION,
626                '/^Bad index name/i'
627                    => DB_ERROR_ALREADY_EXISTS,
628                '/^Bad order field/i'
629                    => DB_ERROR_SYNTAX,
630                '/^Bad type for comparison/i'
631                    => DB_ERROR_SYNTAX,
632                '/^Can\'t perform LIKE on/i'
633                    => DB_ERROR_SYNTAX,
634                '/^Can\'t use TEXT fields in LIKE comparison/i'
635                    => DB_ERROR_SYNTAX,
636                '/^Couldn\'t create temporary table/i'
637                    => DB_ERROR_CANNOT_CREATE,
638                '/^Error creating table file/i'
639                    => DB_ERROR_CANNOT_CREATE,
640                '/^Field .* cannot be null$/i'
641                    => DB_ERROR_CONSTRAINT_NOT_NULL,
642                '/^Index (field|condition) .* cannot be null$/i'
643                    => DB_ERROR_SYNTAX,
644                '/^Invalid date format/i'
645                    => DB_ERROR_INVALID_DATE,
646                '/^Invalid time format/i'
647                    => DB_ERROR_INVALID,
648                '/^Literal value for .* is wrong type$/i'
649                    => DB_ERROR_INVALID_NUMBER,
650                '/^No Database Selected/i'
651                    => DB_ERROR_NODBSELECTED,
652                '/^No value specified for field/i'
653                    => DB_ERROR_VALUE_COUNT_ON_ROW,
654                '/^Non unique value for unique index/i'
655                    => DB_ERROR_CONSTRAINT,
656                '/^Out of memory for temporary table/i'
657                    => DB_ERROR_CANNOT_CREATE,
658                '/^Permission denied/i'
659                    => DB_ERROR_ACCESS_VIOLATION,
660                '/^Reference to un-selected table/i'
661                    => DB_ERROR_SYNTAX,
662                '/^syntax error/i'
663                    => DB_ERROR_SYNTAX,
664                '/^Table .* exists$/i'
665                    => DB_ERROR_ALREADY_EXISTS,
666                '/^Unknown database/i'
667                    => DB_ERROR_NOSUCHDB,
668                '/^Unknown field/i'
669                    => DB_ERROR_NOSUCHFIELD,
670                '/^Unknown (index|system variable)/i'
671                    => DB_ERROR_NOT_FOUND,
672                '/^Unknown table/i'
673                    => DB_ERROR_NOSUCHTABLE,
674                '/^Unqualified field/i'
675                    => DB_ERROR_SYNTAX,
676            );
677        }
678
679        foreach ($error_regexps as $regexp => $code) {
680            if (preg_match($regexp, $errormsg)) {
681                return $code;
682            }
683        }
684        return DB_ERROR;
685    }
686
687    // }}}
688    // {{{ tableInfo()
689
690    /**
691     * Returns information about a table or a result set
692     *
693     * @param object|string  $result  DB_result object from a query or a
694     *                                 string containing the name of a table.
695     *                                 While this also accepts a query result
696     *                                 resource identifier, this behavior is
697     *                                 deprecated.
698     * @param int            $mode    a valid tableInfo mode
699     *
700     * @return array  an associative array with the information requested.
701     *                 A DB_Error object on failure.
702     *
703     * @see DB_common::setOption()
704     */
705    function tableInfo($result, $mode = null)
706    {
707        if (is_string($result)) {
708            /*
709             * Probably received a table name.
710             * Create a result resource identifier.
711             */
712            $id = @msql_query("SELECT * FROM $result",
713                              $this->connection);
714            $got_string = true;
715        } elseif (isset($result->result)) {
716            /*
717             * Probably received a result object.
718             * Extract the result resource identifier.
719             */
720            $id = $result->result;
721            $got_string = false;
722        } else {
723            /*
724             * Probably received a result resource identifier.
725             * Copy it.
726             * Deprecated.  Here for compatibility only.
727             */
728            $id = $result;
729            $got_string = false;
730        }
731
732        if (!is_resource($id)) {
733            return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
734        }
735
736        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
737            $case_func = 'strtolower';
738        } else {
739            $case_func = 'strval';
740        }
741
742        $count = @msql_num_fields($id);
743        $res   = array();
744
745        if ($mode) {
746            $res['num_fields'] = $count;
747        }
748
749        for ($i = 0; $i < $count; $i++) {
750            $tmp = @msql_fetch_field($id);
751
752            $flags = '';
753            if ($tmp->not_null) {
754                $flags .= 'not_null ';
755            }
756            if ($tmp->unique) {
757                $flags .= 'unique_key ';
758            }
759            $flags = trim($flags);
760
761            $res[$i] = array(
762                'table' => $case_func($tmp->table),
763                'name'  => $case_func($tmp->name),
764                'type'  => $tmp->type,
765                'len'   => msql_field_len($id, $i),
766                'flags' => $flags,
767            );
768
769            if ($mode & DB_TABLEINFO_ORDER) {
770                $res['order'][$res[$i]['name']] = $i;
771            }
772            if ($mode & DB_TABLEINFO_ORDERTABLE) {
773                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
774            }
775        }
776
777        // free the result only if we were called on a table
778        if ($got_string) {
779            @msql_free_result($id);
780        }
781        return $res;
782    }
783
784    // }}}
785    // {{{ getSpecialQuery()
786
787    /**
788     * Obtain a list of a given type of objects
789     *
790     * @param string $type  the kind of objects you want to retrieve
791     *
792     * @return array  the array containing the list of objects requested
793     *
794     * @access protected
795     * @see DB_common::getListOf()
796     */
797    function getSpecialQuery($type)
798    {
799        switch ($type) {
800            case 'databases':
801                $id = @msql_list_dbs($this->connection);
802                break;
803            case 'tables':
804                $id = @msql_list_tables($this->dsn['database'],
805                                        $this->connection);
806                break;
807            default:
808                return null;
809        }
810        if (!$id) {
811            return $this->msqlRaiseError();
812        }
813        $out = array();
814        while ($row = @msql_fetch_row($id)) {
815            $out[] = $row[0];
816        }
817        return $out;
818    }
819
820    // }}}
821
822}
823
824/*
825 * Local variables:
826 * tab-width: 4
827 * c-basic-offset: 4
828 * End:
829 */
830
831?>
Note: See TracBrowser for help on using the repository browser.