MailZu 0.8RC3
[bMailZu.git] / lib / pear / DB / msql.php
blob04b0af886fe81b3743e7b84414f2345bd835ffb7
1 <?php
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
5 /**
6 * The PEAR DB driver for PHP's msql extension
7 * for interacting with Mini SQL databases
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.
13 * PHP versions 4 and 5
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.
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: msql.php,v 1.62 2007/01/12 03:11:17 aharvey Exp $
27 * @link http://pear.php.net/package/DB
30 /**
31 * Obtain the DB_common class so it can be extended from
33 require_once 'DB/common.php';
35 /**
36 * The methods PEAR DB uses to interact with PHP's msql extension
37 * for interacting with Mini SQL databases
39 * These methods overload the ones declared in DB_common.
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.
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: 1.7.11
51 * @link http://pear.php.net/package/DB
52 * @since Class not functional until Release 1.7.0
54 class DB_msql extends DB_common
56 // {{{ properties
58 /**
59 * The DB driver type (mysql, oci8, odbc, etc.)
60 * @var string
62 var $phptype = 'msql';
64 /**
65 * The database syntax variant to be used (db2, access, etc.), if any
66 * @var string
68 var $dbsyntax = 'msql';
70 /**
71 * The capabilities of this DB implementation
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.
76 * Meaning of the 'limit' element:
77 * + 'emulate' = emulate with fetch row by number
78 * + 'alter' = alter the query
79 * + false = skip rows
81 * @var array
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,
93 /**
94 * A mapping of native error codes to DB error codes
95 * @var array
97 var $errorcode_map = array(
101 * The raw database connection created by PHP
102 * @var resource
104 var $connection;
107 * The DSN information for connecting to a database
108 * @var array
110 var $dsn = array();
114 * The query result resource created by PHP
116 * Used to make affectedRows() work. Only contains the result for
117 * data manipulation queries. Contains false for other queries.
119 * @var resource
120 * @access private
122 var $_result;
125 // }}}
126 // {{{ constructor
129 * This constructor calls <kbd>$this->DB_common()</kbd>
131 * @return void
133 function DB_msql()
135 $this->DB_common();
138 // }}}
139 // {{{ connect()
142 * Connect to the database server, log in and open the database
144 * Don't call this method directly. Use DB::connect() instead.
146 * Example of how to connect:
147 * <code>
148 * require_once 'DB.php';
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 * );
156 * $db =& DB::connect($dsn, $options);
157 * if (PEAR::isError($db)) {
158 * die($db->getMessage());
160 * </code>
162 * @param array $dsn the data source name
163 * @param bool $persistent should the connection be persistent?
165 * @return int DB_OK on success. A DB_Error object on failure.
167 function connect($dsn, $persistent = false)
169 if (!PEAR::loadExtension('msql')) {
170 return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
173 $this->dsn = $dsn;
174 if ($dsn['dbsyntax']) {
175 $this->dbsyntax = $dsn['dbsyntax'];
178 $params = array();
179 if ($dsn['hostspec']) {
180 $params[] = $dsn['port']
181 ? $dsn['hostspec'] . ',' . $dsn['port']
182 : $dsn['hostspec'];
185 $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect';
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);
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);
211 if (!@msql_select_db($dsn['database'], $this->connection)) {
212 return $this->msqlRaiseError();
214 return DB_OK;
217 // }}}
218 // {{{ disconnect()
221 * Disconnects from the database server
223 * @return bool TRUE on success, FALSE on failure
225 function disconnect()
227 $ret = @msql_close($this->connection);
228 $this->connection = null;
229 return $ret;
232 // }}}
233 // {{{ simpleQuery()
236 * Sends a query to the database server
238 * @param string the SQL query string
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
244 function simpleQuery($query)
246 $this->last_query = $query;
247 $query = $this->modifyQuery($query);
248 $result = @msql_query($query, $this->connection);
249 if (!$result) {
250 return $this->msqlRaiseError();
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;
264 // }}}
265 // {{{ nextResult()
268 * Move the internal msql result pointer to the next available result
270 * @param a valid fbsql result resource
272 * @access public
274 * @return true if a result is available otherwise return false
276 function nextResult($result)
278 return false;
281 // }}}
282 // {{{ fetchInto()
285 * Places a row from the result set into the given array
287 * Formating of the array and the data therein are configurable.
288 * See DB_result::fetchInto() for more information.
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.
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.
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)
303 * @return mixed DB_OK on success, NULL when the end of a result set is
304 * reached or on failure
306 * @see DB_result::fetchInto()
308 function fetchInto($result, &$arr, $fetchmode, $rownum = null)
310 if ($rownum !== null) {
311 if (!@msql_data_seek($result, $rownum)) {
312 return null;
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);
320 } else {
321 $arr = @msql_fetch_row($result);
323 if (!$arr) {
324 return null;
326 if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
327 $this->_rtrimArrayValues($arr);
329 if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
330 $this->_convertNullArrayValuesToEmpty($arr);
332 return DB_OK;
335 // }}}
336 // {{{ freeResult()
339 * Deletes the result set and frees the memory occupied by the result set
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.
345 * @param resource $result PHP's query result resource
347 * @return bool TRUE on success, FALSE if $result is invalid
349 * @see DB_result::free()
351 function freeResult($result)
353 return is_resource($result) ? msql_free_result($result) : false;
356 // }}}
357 // {{{ numCols()
360 * Gets the number of columns in a result set
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.
366 * @param resource $result PHP's query result resource
368 * @return int the number of columns. A DB_Error object on failure.
370 * @see DB_result::numCols()
372 function numCols($result)
374 $cols = @msql_num_fields($result);
375 if (!$cols) {
376 return $this->msqlRaiseError();
378 return $cols;
381 // }}}
382 // {{{ numRows()
385 * Gets the number of rows in a result set
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.
391 * @param resource $result PHP's query result resource
393 * @return int the number of rows. A DB_Error object on failure.
395 * @see DB_result::numRows()
397 function numRows($result)
399 $rows = @msql_num_rows($result);
400 if ($rows === false) {
401 return $this->msqlRaiseError();
403 return $rows;
406 // }}}
407 // {{{ affected()
410 * Determines the number of rows affected by a data maniuplation query
412 * 0 is returned for queries that don't manipulate data.
414 * @return int the number of rows. A DB_Error object on failure.
416 function affectedRows()
418 if (!$this->_result) {
419 return 0;
421 return msql_affected_rows($this->_result);
424 // }}}
425 // {{{ nextId()
428 * Returns the next free id in a sequence
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
434 * @return int the next id number in the sequence.
435 * A DB_Error object on failure.
437 * @see DB_common::nextID(), DB_common::getSequenceName(),
438 * DB_msql::createSequence(), DB_msql::dropSequence()
440 function nextId($seq_name, $ondemand = true)
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);
457 } else {
458 $repeat = false;
460 } while ($repeat);
461 if (DB::isError($result)) {
462 return $this->raiseError($result);
464 $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
465 $result->free();
466 return $arr[0];
469 // }}}
470 // {{{ createSequence()
473 * Creates a new sequence
475 * Also creates a new table to associate the sequence with. Uses
476 * a separate table to ensure portability with other drivers.
478 * @param string $seq_name name of the new sequence
480 * @return int DB_OK on success. A DB_Error object on failure.
482 * @see DB_common::createSequence(), DB_common::getSequenceName(),
483 * DB_msql::nextID(), DB_msql::dropSequence()
485 function createSequence($seq_name)
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;
493 $res = $this->query("CREATE SEQUENCE ON ${seqname}");
494 return $res;
497 // }}}
498 // {{{ dropSequence()
501 * Deletes a sequence
503 * @param string $seq_name name of the sequence to be deleted
505 * @return int DB_OK on success. A DB_Error object on failure.
507 * @see DB_common::dropSequence(), DB_common::getSequenceName(),
508 * DB_msql::nextID(), DB_msql::createSequence()
510 function dropSequence($seq_name)
512 return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
515 // }}}
516 // {{{ quoteIdentifier()
519 * mSQL does not support delimited identifiers
521 * @param string $str the identifier name to be quoted
523 * @return object a DB_Error object
525 * @see DB_common::quoteIdentifier()
526 * @since Method available since Release 1.7.0
528 function quoteIdentifier($str)
530 return $this->raiseError(DB_ERROR_UNSUPPORTED);
533 // }}}
534 // {{{ quoteFloat()
537 * Formats a float value for use within a query in a locale-independent
538 * manner.
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.
545 function quoteFloat($float) {
546 return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
549 // }}}
550 // {{{ escapeSimple()
553 * Escapes a string according to the current DBMS's standards
555 * @param string $str the string to be escaped
557 * @return string the escaped string
559 * @see DB_common::quoteSmart()
560 * @since Method available since Release 1.7.0
562 function escapeSimple($str)
564 return addslashes($str);
567 // }}}
568 // {{{ msqlRaiseError()
571 * Produces a DB_Error object regarding the current problem
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.
577 * @return object the DB_Error object
579 * @see DB_common::raiseError(),
580 * DB_msql::errorNative(), DB_msql::errorCode()
582 function msqlRaiseError($errno = null)
584 $native = $this->errorNative();
585 if ($errno === null) {
586 $errno = $this->errorCode($native);
588 return $this->raiseError($errno, null, null, null, $native);
591 // }}}
592 // {{{ errorNative()
595 * Gets the DBMS' native error message produced by the last query
597 * @return string the DBMS' error message
599 function errorNative()
601 return @msql_error();
604 // }}}
605 // {{{ errorCode()
608 * Determines PEAR::DB error code from the database's text error message
610 * @param string $errormsg the error message returned from the database
612 * @return integer the error number from a DB_ERROR* constant
614 function errorCode($errormsg)
616 static $error_regexps;
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);
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,
679 foreach ($error_regexps as $regexp => $code) {
680 if (preg_match($regexp, $errormsg)) {
681 return $code;
684 return DB_ERROR;
687 // }}}
688 // {{{ tableInfo()
691 * Returns information about a table or a result set
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
700 * @return array an associative array with the information requested.
701 * A DB_Error object on failure.
703 * @see DB_common::setOption()
705 function tableInfo($result, $mode = null)
707 if (is_string($result)) {
709 * Probably received a table name.
710 * Create a result resource identifier.
712 $id = @msql_query("SELECT * FROM $result",
713 $this->connection);
714 $got_string = true;
715 } elseif (isset($result->result)) {
717 * Probably received a result object.
718 * Extract the result resource identifier.
720 $id = $result->result;
721 $got_string = false;
722 } else {
724 * Probably received a result resource identifier.
725 * Copy it.
726 * Deprecated. Here for compatibility only.
728 $id = $result;
729 $got_string = false;
732 if (!is_resource($id)) {
733 return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
736 if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
737 $case_func = 'strtolower';
738 } else {
739 $case_func = 'strval';
742 $count = @msql_num_fields($id);
743 $res = array();
745 if ($mode) {
746 $res['num_fields'] = $count;
749 for ($i = 0; $i < $count; $i++) {
750 $tmp = @msql_fetch_field($id);
752 $flags = '';
753 if ($tmp->not_null) {
754 $flags .= 'not_null ';
756 if ($tmp->unique) {
757 $flags .= 'unique_key ';
759 $flags = trim($flags);
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,
769 if ($mode & DB_TABLEINFO_ORDER) {
770 $res['order'][$res[$i]['name']] = $i;
772 if ($mode & DB_TABLEINFO_ORDERTABLE) {
773 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
777 // free the result only if we were called on a table
778 if ($got_string) {
779 @msql_free_result($id);
781 return $res;
784 // }}}
785 // {{{ getSpecialQuery()
788 * Obtain a list of a given type of objects
790 * @param string $type the kind of objects you want to retrieve
792 * @return array the array containing the list of objects requested
794 * @access protected
795 * @see DB_common::getListOf()
797 function getSpecialQuery($type)
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;
810 if (!$id) {
811 return $this->msqlRaiseError();
813 $out = array();
814 while ($row = @msql_fetch_row($id)) {
815 $out[] = $row[0];
817 return $out;
820 // }}}
825 * Local variables:
826 * tab-width: 4
827 * c-basic-offset: 4
828 * End: