source: branches/feature-module-update/data/module/DB/msql.php @ 15080

Revision 15080, 23.3 KB checked in by nanasess, 17 years ago (diff)

svn properties 設定

  • svn:mime-type - application/x-httpd-php; charset=UTF-8
  • svn:keywords - Id
  • Property svn:keywords set to Id
  • Property svn:mime-type set to application/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-2005 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-2005 The PHP Group
49 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
50 * @version    Release: @package_version@
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 (DB::isManip($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 @msql_free_result($result);
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    // {{{ escapeSimple()
535
536    /**
537     * Escapes a string according to the current DBMS's standards
538     *
539     * @param string $str  the string to be escaped
540     *
541     * @return string  the escaped string
542     *
543     * @see DB_common::quoteSmart()
544     * @since Method available since Release 1.7.0
545     */
546    function escapeSimple($str)
547    {
548        return addslashes($str);
549    }
550
551    // }}}
552    // {{{ msqlRaiseError()
553
554    /**
555     * Produces a DB_Error object regarding the current problem
556     *
557     * @param int $errno  if the error is being manually raised pass a
558     *                     DB_ERROR* constant here.  If this isn't passed
559     *                     the error information gathered from the DBMS.
560     *
561     * @return object  the DB_Error object
562     *
563     * @see DB_common::raiseError(),
564     *      DB_msql::errorNative(), DB_msql::errorCode()
565     */
566    function msqlRaiseError($errno = null)
567    {
568        $native = $this->errorNative();
569        if ($errno === null) {
570            $errno = $this->errorCode($native);
571        }
572        return $this->raiseError($errno, null, null, null, $native);
573    }
574
575    // }}}
576    // {{{ errorNative()
577
578    /**
579     * Gets the DBMS' native error message produced by the last query
580     *
581     * @return string  the DBMS' error message
582     */
583    function errorNative()
584    {
585        return @msql_error();
586    }
587
588    // }}}
589    // {{{ errorCode()
590
591    /**
592     * Determines PEAR::DB error code from the database's text error message
593     *
594     * @param string $errormsg  the error message returned from the database
595     *
596     * @return integer  the error number from a DB_ERROR* constant
597     */
598    function errorCode($errormsg)
599    {
600        static $error_regexps;
601        if (!isset($error_regexps)) {
602            $error_regexps = array(
603                '/^Access to database denied/i'
604                    => DB_ERROR_ACCESS_VIOLATION,
605                '/^Bad index name/i'
606                    => DB_ERROR_ALREADY_EXISTS,
607                '/^Bad order field/i'
608                    => DB_ERROR_SYNTAX,
609                '/^Bad type for comparison/i'
610                    => DB_ERROR_SYNTAX,
611                '/^Can\'t perform LIKE on/i'
612                    => DB_ERROR_SYNTAX,
613                '/^Can\'t use TEXT fields in LIKE comparison/i'
614                    => DB_ERROR_SYNTAX,
615                '/^Couldn\'t create temporary table/i'
616                    => DB_ERROR_CANNOT_CREATE,
617                '/^Error creating table file/i'
618                    => DB_ERROR_CANNOT_CREATE,
619                '/^Field .* cannot be null$/i'
620                    => DB_ERROR_CONSTRAINT_NOT_NULL,
621                '/^Index (field|condition) .* cannot be null$/i'
622                    => DB_ERROR_SYNTAX,
623                '/^Invalid date format/i'
624                    => DB_ERROR_INVALID_DATE,
625                '/^Invalid time format/i'
626                    => DB_ERROR_INVALID,
627                '/^Literal value for .* is wrong type$/i'
628                    => DB_ERROR_INVALID_NUMBER,
629                '/^No Database Selected/i'
630                    => DB_ERROR_NODBSELECTED,
631                '/^No value specified for field/i'
632                    => DB_ERROR_VALUE_COUNT_ON_ROW,
633                '/^Non unique value for unique index/i'
634                    => DB_ERROR_CONSTRAINT,
635                '/^Out of memory for temporary table/i'
636                    => DB_ERROR_CANNOT_CREATE,
637                '/^Permission denied/i'
638                    => DB_ERROR_ACCESS_VIOLATION,
639                '/^Reference to un-selected table/i'
640                    => DB_ERROR_SYNTAX,
641                '/^syntax error/i'
642                    => DB_ERROR_SYNTAX,
643                '/^Table .* exists$/i'
644                    => DB_ERROR_ALREADY_EXISTS,
645                '/^Unknown database/i'
646                    => DB_ERROR_NOSUCHDB,
647                '/^Unknown field/i'
648                    => DB_ERROR_NOSUCHFIELD,
649                '/^Unknown (index|system variable)/i'
650                    => DB_ERROR_NOT_FOUND,
651                '/^Unknown table/i'
652                    => DB_ERROR_NOSUCHTABLE,
653                '/^Unqualified field/i'
654                    => DB_ERROR_SYNTAX,
655            );
656        }
657
658        foreach ($error_regexps as $regexp => $code) {
659            if (preg_match($regexp, $errormsg)) {
660                return $code;
661            }
662        }
663        return DB_ERROR;
664    }
665
666    // }}}
667    // {{{ tableInfo()
668
669    /**
670     * Returns information about a table or a result set
671     *
672     * @param object|string  $result  DB_result object from a query or a
673     *                                 string containing the name of a table.
674     *                                 While this also accepts a query result
675     *                                 resource identifier, this behavior is
676     *                                 deprecated.
677     * @param int            $mode    a valid tableInfo mode
678     *
679     * @return array  an associative array with the information requested.
680     *                 A DB_Error object on failure.
681     *
682     * @see DB_common::setOption()
683     */
684    function tableInfo($result, $mode = null)
685    {
686        if (is_string($result)) {
687            /*
688             * Probably received a table name.
689             * Create a result resource identifier.
690             */
691            $id = @msql_query("SELECT * FROM $result",
692                              $this->connection);
693            $got_string = true;
694        } elseif (isset($result->result)) {
695            /*
696             * Probably received a result object.
697             * Extract the result resource identifier.
698             */
699            $id = $result->result;
700            $got_string = false;
701        } else {
702            /*
703             * Probably received a result resource identifier.
704             * Copy it.
705             * Deprecated.  Here for compatibility only.
706             */
707            $id = $result;
708            $got_string = false;
709        }
710
711        if (!is_resource($id)) {
712            return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
713        }
714
715        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
716            $case_func = 'strtolower';
717        } else {
718            $case_func = 'strval';
719        }
720
721        $count = @msql_num_fields($id);
722        $res   = array();
723
724        if ($mode) {
725            $res['num_fields'] = $count;
726        }
727
728        for ($i = 0; $i < $count; $i++) {
729            $tmp = @msql_fetch_field($id);
730
731            $flags = '';
732            if ($tmp->not_null) {
733                $flags .= 'not_null ';
734            }
735            if ($tmp->unique) {
736                $flags .= 'unique_key ';
737            }
738            $flags = trim($flags);
739
740            $res[$i] = array(
741                'table' => $case_func($tmp->table),
742                'name'  => $case_func($tmp->name),
743                'type'  => $tmp->type,
744                'len'   => msql_field_len($id, $i),
745                'flags' => $flags,
746            );
747
748            if ($mode & DB_TABLEINFO_ORDER) {
749                $res['order'][$res[$i]['name']] = $i;
750            }
751            if ($mode & DB_TABLEINFO_ORDERTABLE) {
752                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
753            }
754        }
755
756        // free the result only if we were called on a table
757        if ($got_string) {
758            @msql_free_result($id);
759        }
760        return $res;
761    }
762
763    // }}}
764    // {{{ getSpecialQuery()
765
766    /**
767     * Obtain a list of a given type of objects
768     *
769     * @param string $type  the kind of objects you want to retrieve
770     *
771     * @return array  the array containing the list of objects requested
772     *
773     * @access protected
774     * @see DB_common::getListOf()
775     */
776    function getSpecialQuery($type)
777    {
778        switch ($type) {
779            case 'databases':
780                $id = @msql_list_dbs($this->connection);
781                break;
782            case 'tables':
783                $id = @msql_list_tables($this->dsn['database'],
784                                        $this->connection);
785                break;
786            default:
787                return null;
788        }
789        if (!$id) {
790            return $this->msqlRaiseError();
791        }
792        $out = array();
793        while ($row = @msql_fetch_row($id)) {
794            $out[] = $row[0];
795        }
796        return $out;
797    }
798
799    // }}}
800
801}
802
803/*
804 * Local variables:
805 * tab-width: 4
806 * c-basic-offset: 4
807 * End:
808 */
809
810?>
Note: See TracBrowser for help on using the repository browser.