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-2005 The PHP Group
25 * @license http://www.php.net/license/3_0.txt PHP License 3.0
26 * @version CVS: $Id: msql.php 12 2005-10-02 11:36:35Z sparc $
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-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
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 (DB::isManip($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 @msql_free_result($result);
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);
534 // {{{ escapeSimple()
537 * Escapes a string according to the current DBMS's standards
539 * @param string $str the string to be escaped
541 * @return string the escaped string
543 * @see DB_common::quoteSmart()
544 * @since Method available since Release 1.7.0
546 function escapeSimple($str)
548 return addslashes($str);
552 // {{{ msqlRaiseError()
555 * Produces a DB_Error object regarding the current problem
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.
561 * @return object the DB_Error object
563 * @see DB_common::raiseError(),
564 * DB_msql::errorNative(), DB_msql::errorCode()
566 function msqlRaiseError($errno = null)
568 $native = $this->errorNative();
569 if ($errno === null) {
570 $errno = $this->errorCode($native);
572 return $this->raiseError($errno, null, null, null, $native);
579 * Gets the DBMS' native error message produced by the last query
581 * @return string the DBMS' error message
583 function errorNative()
585 return @msql_error();
592 * Determines PEAR::DB error code from the database's text error message
594 * @param string $errormsg the error message returned from the database
596 * @return integer the error number from a DB_ERROR* constant
598 function errorCode($errormsg)
600 static $error_regexps;
601 if (!isset($error_regexps)) {
602 $error_regexps = array(
603 '/^Access to database denied/i'
604 => DB_ERROR_ACCESS_VIOLATION,
606 => DB_ERROR_ALREADY_EXISTS,
607 '/^Bad order field/i'
609 '/^Bad type for comparison/i'
611 '/^Can\'t perform LIKE on/i'
613 '/^Can\'t use TEXT fields in LIKE comparison/i'
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'
623 '/^Invalid date format/i'
624 => DB_ERROR_INVALID_DATE,
625 '/^Invalid time format/i'
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'
643 '/^Table .* exists$/i'
644 => DB_ERROR_ALREADY_EXISTS,
645 '/^Unknown database/i'
646 => DB_ERROR_NOSUCHDB,
648 => DB_ERROR_NOSUCHFIELD,
649 '/^Unknown (index|system variable)/i'
650 => DB_ERROR_NOT_FOUND,
652 => DB_ERROR_NOSUCHTABLE,
653 '/^Unqualified field/i'
658 foreach ($error_regexps as $regexp => $code) {
659 if (preg_match($regexp, $errormsg)) {
670 * Returns information about a table or a result set
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
677 * @param int $mode a valid tableInfo mode
679 * @return array an associative array with the information requested.
680 * A DB_Error object on failure.
682 * @see DB_common::setOption()
684 function tableInfo($result, $mode = null)
686 if (is_string($result)) {
688 * Probably received a table name.
689 * Create a result resource identifier.
691 $id = @msql_query("SELECT * FROM $result",
694 } elseif (isset($result->result)) {
696 * Probably received a result object.
697 * Extract the result resource identifier.
699 $id = $result->result;
703 * Probably received a result resource identifier.
705 * Deprecated. Here for compatibility only.
711 if (!is_resource($id)) {
712 return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
715 if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
716 $case_func = 'strtolower';
718 $case_func = 'strval';
721 $count = @msql_num_fields($id);
725 $res['num_fields'] = $count;
728 for ($i = 0; $i < $count; $i++) {
729 $tmp = @msql_fetch_field($id);
732 if ($tmp->not_null) {
733 $flags .= 'not_null ';
736 $flags .= 'unique_key ';
738 $flags = trim($flags);
741 'table' => $case_func($tmp->table),
742 'name' => $case_func($tmp->name),
743 'type' => $tmp->type,
744 'len' => msql_field_len($id, $i),
748 if ($mode & DB_TABLEINFO_ORDER) {
749 $res['order'][$res[$i]['name']] = $i;
751 if ($mode & DB_TABLEINFO_ORDERTABLE) {
752 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
756 // free the result only if we were called on a table
758 @msql_free_result($id);
764 // {{{ getSpecialQuery()
767 * Obtain a list of a given type of objects
769 * @param string $type the kind of objects you want to retrieve
771 * @return array the array containing the list of objects requested
774 * @see DB_common::getListOf()
776 function getSpecialQuery($type)
780 $id = @msql_list_dbs($this->connection);
783 $id = @msql_list_tables($this->dsn['database'],
790 return $this->msqlRaiseError();
793 while ($row = @msql_fetch_row($id)) {