3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
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
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.
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: msql.php,v 1.64 2007/09/21 13:40:41 aharvey Exp $
27 * @link http://pear.php.net/package/DB
31 * Obtain the DB_common class so it can be extended from
33 require_once 'DB/common.php';
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
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
54 class DB_msql extends DB_common
59 * The DB driver type (mysql, oci8, odbc, etc.)
62 var $phptype = 'msql';
65 * The database syntax variant to be used (db2, access, etc.), if any
68 var $dbsyntax = 'msql';
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
83 var $features = array(
90 'transactions' => false,
94 * A mapping of native error codes to DB error codes
97 var $errorcode_map = array(
101 * The raw database connection created by PHP
107 * The DSN information for connecting to a database
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.
129 * This constructor calls <kbd>$this->DB_common()</kbd>
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:
148 * require_once 'DB.php';
150 * // $dsn = 'msql://hostname/dbname'; // use a TCP connection
151 * $dsn = 'msql:///dbname'; // use a socket
153 * 'portability' => DB_PORTABILITY_ALL,
156 * $db = DB::connect($dsn, $options);
157 * if (PEAR::isError($db)) {
158 * die($db->getMessage());
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);
174 if ($dsn['dbsyntax']) {
175 $this->dbsyntax = $dsn['dbsyntax'];
179 if ($dsn['hostspec']) {
180 $params[] = $dsn['port']
181 ? $dsn['hostspec'] . ',' . $dsn['port']
185 $connect_function = $persistent ? 'msql_pconnect' : 'msql_connect';
187 $ini = ini_get('track_errors');
190 $this->connection = @call_user_func_array($connect_function,
193 @ini_set('track_errors', 1);
194 $this->connection = @call_user_func_array($connect_function,
196 @ini_set('track_errors', $ini);
199 if (!$this->connection) {
200 if (($err = @msql_error()) != '') {
201 return $this->raiseError(DB_ERROR_CONNECT_FAILED,
205 return $this->raiseError(DB_ERROR_CONNECT_FAILED,
211 if (!@msql_select_db($dsn['database'], $this->connection)) {
212 return $this->msqlRaiseError();
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;
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);
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;
258 $this->_result = false;
268 * Move the internal msql result pointer to the next available result
270 * @param a valid fbsql result resource
274 * @return true if a result is available otherwise return false
276 function nextResult($result)
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
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)) {
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);
321 $arr = @msql_fetch_row($result);
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);
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;
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);
376 return $this->msqlRaiseError();
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();
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) {
421 return msql_affected_rows($this->_result);
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);
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) {
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);
461 if (DB::isError($result)) {
462 return $this->raiseError($result);
464 $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
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)) {
493 $res = $this->query("CREATE SEQUENCE ON ${seqname}");
498 // {{{ dropSequence()
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));
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);
537 * Formats a float value for use within a query in a locale-independent
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))));
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);
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);
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();
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,
627 => DB_ERROR_ALREADY_EXISTS,
628 '/^Bad order field/i'
630 '/^Bad type for comparison/i'
632 '/^Can\'t perform LIKE on/i'
634 '/^Can\'t use TEXT fields in LIKE comparison/i'
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'
644 '/^Invalid date format/i'
645 => DB_ERROR_INVALID_DATE,
646 '/^Invalid time format/i'
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'
664 '/^Table .* exists$/i'
665 => DB_ERROR_ALREADY_EXISTS,
666 '/^Unknown database/i'
667 => DB_ERROR_NOSUCHDB,
669 => DB_ERROR_NOSUCHFIELD,
670 '/^Unknown (index|system variable)/i'
671 => DB_ERROR_NOT_FOUND,
673 => DB_ERROR_NOSUCHTABLE,
674 '/^Unqualified field/i'
679 foreach ($error_regexps as $regexp => $code) {
680 if (preg_match($regexp, $errormsg)) {
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
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",
715 } elseif (isset($result->result)) {
717 * Probably received a result object.
718 * Extract the result resource identifier.
720 $id = $result->result;
724 * Probably received a result resource identifier.
726 * Deprecated. Here for compatibility only.
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';
739 $case_func = 'strval';
742 $count = @msql_num_fields($id);
746 $res['num_fields'] = $count;
749 for ($i = 0; $i < $count; $i++) {
750 $tmp = @msql_fetch_field($id);
753 if ($tmp->not_null) {
754 $flags .= 'not_null ';
757 $flags .= 'unique_key ';
759 $flags = trim($flags);
762 'table' => $case_func($tmp->table),
763 'name' => $case_func($tmp->name),
764 'type' => $tmp->type,
765 'len' => msql_field_len($id, $i),
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
779 @msql_free_result($id);
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
795 * @see DB_common::getListOf()
797 function getSpecialQuery($type)
801 $id = @msql_list_dbs($this->connection);
804 $id = @msql_list_tables($this->dsn['database'],
811 return $this->msqlRaiseError();
814 while ($row = @msql_fetch_row($id)) {