DB
[ class tree: DB ] [ index: DB ] [ all elements ]

Source for file mysql.php

Documentation is available at mysql.php

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**
  6.  * The PEAR DB driver for PHP's mysql extension
  7.  * for interacting with MySQL databases
  8.  *
  9.  * PHP versions 4 and 5
  10.  *
  11.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  12.  * that is available through the world-wide-web at the following URI:
  13.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  14.  * the PHP License and are unable to obtain it through the web, please
  15.  * send a note to license@php.net so we can mail you a copy immediately.
  16.  *
  17.  * @category   Database
  18.  * @package    DB
  19.  * @author     Stig Bakken <ssb@php.net>
  20.  * @author     Daniel Convissor <danielc@php.net>
  21.  * @copyright  1997-2007 The PHP Group
  22.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  23.  * @version    CVS: $Id: mysql.php,v 1.126 2007/09/21 13:32:52 aharvey Exp $
  24.  * @link       http://pear.php.net/package/DB
  25.  */
  26.  
  27. /**
  28.  * Obtain the DB_common class so it can be extended from
  29.  */
  30. require_once 'DB/common.php';
  31.  
  32. /**
  33.  * The methods PEAR DB uses to interact with PHP's mysql extension
  34.  * for interacting with MySQL databases
  35.  *
  36.  * These methods overload the ones declared in DB_common.
  37.  *
  38.  * @category   Database
  39.  * @package    DB
  40.  * @author     Stig Bakken <ssb@php.net>
  41.  * @author     Daniel Convissor <danielc@php.net>
  42.  * @copyright  1997-2007 The PHP Group
  43.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  44.  * @version    Release: 1.7.14RC1
  45.  * @link       http://pear.php.net/package/DB
  46.  */
  47. class DB_mysql extends DB_common
  48. {
  49.     // {{{ properties
  50.  
  51.     /**
  52.      * The DB driver type (mysql, oci8, odbc, etc.)
  53.      * @var string 
  54.      */
  55.     var $phptype = 'mysql';
  56.  
  57.     /**
  58.      * The database syntax variant to be used (db2, access, etc.), if any
  59.      * @var string 
  60.      */
  61.     var $dbsyntax = 'mysql';
  62.  
  63.     /**
  64.      * The capabilities of this DB implementation
  65.      *
  66.      * The 'new_link' element contains the PHP version that first provided
  67.      * new_link support for this DBMS.  Contains false if it's unsupported.
  68.      *
  69.      * Meaning of the 'limit' element:
  70.      *   + 'emulate' = emulate with fetch row by number
  71.      *   + 'alter'   = alter the query
  72.      *   + false     = skip rows
  73.      *
  74.      * @var array 
  75.      */
  76.     var $features = array(
  77.         'limit'         => 'alter',
  78.         'new_link'      => '4.2.0',
  79.         'numrows'       => true,
  80.         'pconnect'      => true,
  81.         'prepare'       => false,
  82.         'ssl'           => false,
  83.         'transactions'  => true,
  84.     );
  85.  
  86.     /**
  87.      * A mapping of native error codes to DB error codes
  88.      * @var array 
  89.      */
  90.     var $errorcode_map = array(
  91.         1004 => DB_ERROR_CANNOT_CREATE,
  92.         1005 => DB_ERROR_CANNOT_CREATE,
  93.         1006 => DB_ERROR_CANNOT_CREATE,
  94.         1007 => DB_ERROR_ALREADY_EXISTS,
  95.         1008 => DB_ERROR_CANNOT_DROP,
  96.         1022 => DB_ERROR_ALREADY_EXISTS,
  97.         1044 => DB_ERROR_ACCESS_VIOLATION,
  98.         1046 => DB_ERROR_NODBSELECTED,
  99.         1048 => DB_ERROR_CONSTRAINT,
  100.         1049 => DB_ERROR_NOSUCHDB,
  101.         1050 => DB_ERROR_ALREADY_EXISTS,
  102.         1051 => DB_ERROR_NOSUCHTABLE,
  103.         1054 => DB_ERROR_NOSUCHFIELD,
  104.         1061 => DB_ERROR_ALREADY_EXISTS,
  105.         1062 => DB_ERROR_ALREADY_EXISTS,
  106.         1064 => DB_ERROR_SYNTAX,
  107.         1091 => DB_ERROR_NOT_FOUND,
  108.         1100 => DB_ERROR_NOT_LOCKED,
  109.         1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
  110.         1142 => DB_ERROR_ACCESS_VIOLATION,
  111.         1146 => DB_ERROR_NOSUCHTABLE,
  112.         1216 => DB_ERROR_CONSTRAINT,
  113.         1217 => DB_ERROR_CONSTRAINT,
  114.         1356 => DB_ERROR_DIVZERO,
  115.         1451 => DB_ERROR_CONSTRAINT,
  116.         1452 => DB_ERROR_CONSTRAINT,
  117.     );
  118.  
  119.     /**
  120.      * The raw database connection created by PHP
  121.      * @var resource 
  122.      */
  123.     var $connection;
  124.  
  125.     /**
  126.      * The DSN information for connecting to a database
  127.      * @var array 
  128.      */
  129.     var $dsn = array();
  130.  
  131.  
  132.     /**
  133.      * Should data manipulation queries be committed automatically?
  134.      * @var bool 
  135.      * @access private
  136.      */
  137.     var $autocommit = true;
  138.  
  139.     /**
  140.      * The quantity of transactions begun
  141.      *
  142.      * {@internal  While this is private, it can't actually be designated
  143.      * private in PHP 5 because it is directly accessed in the test suite.}}}
  144.      *
  145.      * @var integer 
  146.      * @access private
  147.      */
  148.     var $transaction_opcount = 0;
  149.  
  150.     /**
  151.      * The database specified in the DSN
  152.      *
  153.      * It's a fix to allow calls to different databases in the same script.
  154.      *
  155.      * @var string 
  156.      * @access private
  157.      */
  158.     var $_db '';
  159.  
  160.  
  161.     // }}}
  162.     // {{{ constructor
  163.  
  164.     /**
  165.      * This constructor calls <kbd>$this->DB_common()</kbd>
  166.      *
  167.      * @return void 
  168.      */
  169.     function DB_mysql()
  170.     {
  171.         $this->DB_common();
  172.     }
  173.  
  174.     // }}}
  175.     // {{{ connect()
  176.  
  177.     /**
  178.      * Connect to the database server, log in and open the database
  179.      *
  180.      * Don't call this method directly.  Use DB::connect() instead.
  181.      *
  182.      * PEAR DB's mysql driver supports the following extra DSN options:
  183.      *   + new_link      If set to true, causes subsequent calls to connect()
  184.      *                    to return a new connection link instead of the
  185.      *                    existing one.  WARNING: this is not portable to
  186.      *                    other DBMS's. Available since PEAR DB 1.7.0.
  187.      *   + client_flags  Any combination of MYSQL_CLIENT_* constants.
  188.      *                    Only used if PHP is at version 4.3.0 or greater.
  189.      *                    Available since PEAR DB 1.7.0.
  190.      *
  191.      * @param array $dsn         the data source name
  192.      * @param bool  $persistent  should the connection be persistent?
  193.      *
  194.      * @return int  DB_OK on success. A DB_Error object on failure.
  195.      */
  196.     function connect($dsn$persistent = false)
  197.     {
  198.         if (!PEAR::loadExtension('mysql')) {
  199.             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
  200.         }
  201.  
  202.         $this->dsn = $dsn;
  203.         if ($dsn['dbsyntax']{
  204.             $this->dbsyntax = $dsn['dbsyntax'];
  205.         }
  206.  
  207.         $params = array();
  208.         if ($dsn['protocol'&& $dsn['protocol'== 'unix'{
  209.             $params[0':' $dsn['socket'];
  210.         else {
  211.             $params[0$dsn['hostspec'$dsn['hostspec']
  212.                          : 'localhost';
  213.             if ($dsn['port']{
  214.                 $params[0.= ':' $dsn['port'];
  215.             }
  216.         }
  217.         $params[$dsn['username'$dsn['username': null;
  218.         $params[$dsn['password'$dsn['password': null;
  219.  
  220.         if (!$persistent{
  221.             if (isset($dsn['new_link'])
  222.                 && ($dsn['new_link'== 'true' || $dsn['new_link'=== true))
  223.             {
  224.                 $params[= true;
  225.             else {
  226.                 $params[= false;
  227.             }
  228.         }
  229.         if (version_compare(phpversion()'4.3.0''>=')) {
  230.             $params[= isset($dsn['client_flags'])
  231.                         ? $dsn['client_flags': null;
  232.         }
  233.  
  234.         $connect_function $persistent 'mysql_pconnect' 'mysql_connect';
  235.  
  236.         $ini ini_get('track_errors');
  237.         $php_errormsg '';
  238.         if ($ini{
  239.             $this->connection = @call_user_func_array($connect_function,
  240.                                                       $params);
  241.         else {
  242.             @ini_set('track_errors'1);
  243.             $this->connection = @call_user_func_array($connect_function,
  244.                                                       $params);
  245.             @ini_set('track_errors'$ini);
  246.         }
  247.  
  248.         if (!$this->connection{
  249.             if (($err @mysql_error()) != ''{
  250.                 return $this->raiseError(DB_ERROR_CONNECT_FAILED,
  251.                                          nullnullnull
  252.                                          $err);
  253.             else {
  254.                 return $this->raiseError(DB_ERROR_CONNECT_FAILED,
  255.                                          nullnullnull,
  256.                                          $php_errormsg);
  257.             }
  258.         }
  259.  
  260.         if ($dsn['database']{
  261.             if (!@mysql_select_db($dsn['database']$this->connection)) {
  262.                 return $this->mysqlRaiseError();
  263.             }
  264.             $this->_db $dsn['database'];
  265.         }
  266.  
  267.         return DB_OK;
  268.     }
  269.  
  270.     // }}}
  271.     // {{{ disconnect()
  272.  
  273.     /**
  274.      * Disconnects from the database server
  275.      *
  276.      * @return bool  TRUE on success, FALSE on failure
  277.      */
  278.     function disconnect()
  279.     {
  280.         $ret @mysql_close($this->connection);
  281.         $this->connection = null;
  282.         return $ret;
  283.     }
  284.  
  285.     // }}}
  286.     // {{{ simpleQuery()
  287.  
  288.     /**
  289.      * Sends a query to the database server
  290.      *
  291.      * Generally uses mysql_query().  If you want to use
  292.      * mysql_unbuffered_query() set the "result_buffering" option to 0 using
  293.      * setOptions().  This option was added in Release 1.7.0.
  294.      *
  295.      * @param string  the SQL query string
  296.      *
  297.      * @return mixed  + a PHP result resrouce for successful SELECT queries
  298.      *                 + the DB_OK constant for other successful queries
  299.      *                 + a DB_Error object on failure
  300.      */
  301.     function simpleQuery($query)
  302.     {
  303.         $ismanip $this->_checkManip($query);
  304.         $this->last_query = $query;
  305.         $query $this->modifyQuery($query);
  306.         if ($this->_db{
  307.             if (!@mysql_select_db($this->_db$this->connection)) {
  308.                 return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
  309.             }
  310.         }
  311.         if (!$this->autocommit && $ismanip{
  312.             if ($this->transaction_opcount == 0{
  313.                 $result @mysql_query('SET AUTOCOMMIT=0'$this->connection);
  314.                 $result @mysql_query('BEGIN'$this->connection);
  315.                 if (!$result{
  316.                     return $this->mysqlRaiseError();
  317.                 }
  318.             }
  319.             $this->transaction_opcount++;
  320.         }
  321.         if (!$this->options['result_buffering']{
  322.             $result @mysql_unbuffered_query($query$this->connection);
  323.         else {
  324.             $result @mysql_query($query$this->connection);
  325.         }
  326.         if (!$result{
  327.             return $this->mysqlRaiseError();
  328.         }
  329.         if (is_resource($result)) {
  330.             return $result;
  331.         }
  332.         return DB_OK;
  333.     }
  334.  
  335.     // }}}
  336.     // {{{ nextResult()
  337.  
  338.     /**
  339.      * Move the internal mysql result pointer to the next available result
  340.      *
  341.      * This method has not been implemented yet.
  342.      *
  343.      * @param valid sql result resource
  344.      *
  345.      * @return false 
  346.      */
  347.     function nextResult($result)
  348.     {
  349.         return false;
  350.     }
  351.  
  352.     // }}}
  353.     // {{{ fetchInto()
  354.  
  355.     /**
  356.      * Places a row from the result set into the given array
  357.      *
  358.      * Formating of the array and the data therein are configurable.
  359.      * See DB_result::fetchInto() for more information.
  360.      *
  361.      * This method is not meant to be called directly.  Use
  362.      * DB_result::fetchInto() instead.  It can't be declared "protected"
  363.      * because DB_result is a separate object.
  364.      *
  365.      * @param resource $result    the query result resource
  366.      * @param array    $arr       the referenced array to put the data in
  367.      * @param int      $fetchmode how the resulting array should be indexed
  368.      * @param int      $rownum    the row number to fetch (0 = first row)
  369.      *
  370.      * @return mixed  DB_OK on success, NULL when the end of a result set is
  371.      *                  reached or on failure
  372.      *
  373.      * @see DB_result::fetchInto()
  374.      */
  375.     function fetchInto($result&$arr$fetchmode$rownum = null)
  376.     {
  377.         if ($rownum !== null{
  378.             if (!@mysql_data_seek($result$rownum)) {
  379.                 return null;
  380.             }
  381.         }
  382.         if ($fetchmode DB_FETCHMODE_ASSOC{
  383.             $arr @mysql_fetch_array($resultMYSQL_ASSOC);
  384.             if ($this->options['portability'DB_PORTABILITY_LOWERCASE && $arr{
  385.                 $arr array_change_key_case($arrCASE_LOWER);
  386.             }
  387.         else {
  388.             $arr @mysql_fetch_row($result);
  389.         }
  390.         if (!$arr{
  391.             return null;
  392.         }
  393.         if ($this->options['portability'DB_PORTABILITY_RTRIM{
  394.             /*
  395.              * Even though this DBMS already trims output, we do this because
  396.              * a field might have intentional whitespace at the end that
  397.              * gets removed by DB_PORTABILITY_RTRIM under another driver.
  398.              */
  399.             $this->_rtrimArrayValues($arr);
  400.         }
  401.         if ($this->options['portability'DB_PORTABILITY_NULL_TO_EMPTY{
  402.             $this->_convertNullArrayValuesToEmpty($arr);
  403.         }
  404.         return DB_OK;
  405.     }
  406.  
  407.     // }}}
  408.     // {{{ freeResult()
  409.  
  410.     /**
  411.      * Deletes the result set and frees the memory occupied by the result set
  412.      *
  413.      * This method is not meant to be called directly.  Use
  414.      * DB_result::free() instead.  It can't be declared "protected"
  415.      * because DB_result is a separate object.
  416.      *
  417.      * @param resource $result  PHP's query result resource
  418.      *
  419.      * @return bool  TRUE on success, FALSE if $result is invalid
  420.      *
  421.      * @see DB_result::free()
  422.      */
  423.     function freeResult($result)
  424.     {
  425.         return is_resource($resultmysql_free_result($result: false;
  426.     }
  427.  
  428.     // }}}
  429.     // {{{ numCols()
  430.  
  431.     /**
  432.      * Gets the number of columns in a result set
  433.      *
  434.      * This method is not meant to be called directly.  Use
  435.      * DB_result::numCols() instead.  It can't be declared "protected"
  436.      * because DB_result is a separate object.
  437.      *
  438.      * @param resource $result  PHP's query result resource
  439.      *
  440.      * @return int  the number of columns.  A DB_Error object on failure.
  441.      *
  442.      * @see DB_result::numCols()
  443.      */
  444.     function numCols($result)
  445.     {
  446.         $cols @mysql_num_fields($result);
  447.         if (!$cols{
  448.             return $this->mysqlRaiseError();
  449.         }
  450.         return $cols;
  451.     }
  452.  
  453.     // }}}
  454.     // {{{ numRows()
  455.  
  456.     /**
  457.      * Gets the number of rows in a result set
  458.      *
  459.      * This method is not meant to be called directly.  Use
  460.      * DB_result::numRows() instead.  It can't be declared "protected"
  461.      * because DB_result is a separate object.
  462.      *
  463.      * @param resource $result  PHP's query result resource
  464.      *
  465.      * @return int  the number of rows.  A DB_Error object on failure.
  466.      *
  467.      * @see DB_result::numRows()
  468.      */
  469.     function numRows($result)
  470.     {
  471.         $rows @mysql_num_rows($result);
  472.         if ($rows === null{
  473.             return $this->mysqlRaiseError();
  474.         }
  475.         return $rows;
  476.     }
  477.  
  478.     // }}}
  479.     // {{{ autoCommit()
  480.  
  481.     /**
  482.      * Enables or disables automatic commits
  483.      *
  484.      * @param bool $onoff  true turns it on, false turns it off
  485.      *
  486.      * @return int  DB_OK on success.  A DB_Error object if the driver
  487.      *                doesn't support auto-committing transactions.
  488.      */
  489.     function autoCommit($onoff = false)
  490.     {
  491.         // XXX if $this->transaction_opcount > 0, we should probably
  492.         // issue a warning here.
  493.         $this->autocommit $onoff ? true : false;
  494.         return DB_OK;
  495.     }
  496.  
  497.     // }}}
  498.     // {{{ commit()
  499.  
  500.     /**
  501.      * Commits the current transaction
  502.      *
  503.      * @return int  DB_OK on success.  A DB_Error object on failure.
  504.      */
  505.     function commit()
  506.     {
  507.         if ($this->transaction_opcount > 0{
  508.             if ($this->_db{
  509.                 if (!@mysql_select_db($this->_db$this->connection)) {
  510.                     return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
  511.                 }
  512.             }
  513.             $result @mysql_query('COMMIT'$this->connection);
  514.             $result @mysql_query('SET AUTOCOMMIT=1'$this->connection);
  515.             $this->transaction_opcount = 0;
  516.             if (!$result{
  517.                 return $this->mysqlRaiseError();
  518.             }
  519.         }
  520.         return DB_OK;
  521.     }
  522.  
  523.     // }}}
  524.     // {{{ rollback()
  525.  
  526.     /**
  527.      * Reverts the current transaction
  528.      *
  529.      * @return int  DB_OK on success.  A DB_Error object on failure.
  530.      */
  531.     function rollback()
  532.     {
  533.         if ($this->transaction_opcount > 0{
  534.             if ($this->_db{
  535.                 if (!@mysql_select_db($this->_db$this->connection)) {
  536.                     return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
  537.                 }
  538.             }
  539.             $result @mysql_query('ROLLBACK'$this->connection);
  540.             $result @mysql_query('SET AUTOCOMMIT=1'$this->connection);
  541.             $this->transaction_opcount = 0;
  542.             if (!$result{
  543.                 return $this->mysqlRaiseError();
  544.             }
  545.         }
  546.         return DB_OK;
  547.     }
  548.  
  549.     // }}}
  550.     // {{{ affectedRows()
  551.  
  552.     /**
  553.      * Determines the number of rows affected by a data maniuplation query
  554.      *
  555.      * 0 is returned for queries that don't manipulate data.
  556.      *
  557.      * @return int  the number of rows.  A DB_Error object on failure.
  558.      */
  559.     function affectedRows()
  560.     {
  561.         if ($this->_last_query_manip{
  562.             return @mysql_affected_rows($this->connection);
  563.         else {
  564.             return 0;
  565.         }
  566.      }
  567.  
  568.     // }}}
  569.     // {{{ nextId()
  570.  
  571.     /**
  572.      * Returns the next free id in a sequence
  573.      *
  574.      * @param string  $seq_name  name of the sequence
  575.      * @param boolean $ondemand  when true, the seqence is automatically
  576.      *                             created if it does not exist
  577.      *
  578.      * @return int  the next id number in the sequence.
  579.      *                A DB_Error object on failure.
  580.      *
  581.      * @see DB_common::nextID(), DB_common::getSequenceName(),
  582.      *       DB_mysql::createSequence(), DB_mysql::dropSequence()
  583.      */
  584.     function nextId($seq_name$ondemand = true)
  585.     {
  586.         $seqname $this->getSequenceName($seq_name);
  587.         do {
  588.             $repeat = 0;
  589.             $this->pushErrorHandling(PEAR_ERROR_RETURN);
  590.             $result $this->query("UPDATE ${seqname} ".
  591.                                    'SET id=LAST_INSERT_ID(id+1)');
  592.             $this->popErrorHandling();
  593.             if ($result === DB_OK{
  594.                 // COMMON CASE
  595.                 $id @mysql_insert_id($this->connection);
  596.                 if ($id != 0{
  597.                     return $id;
  598.                 }
  599.                 // EMPTY SEQ TABLE
  600.                 // Sequence table must be empty for some reason, so fill
  601.                 // it and return 1 and obtain a user-level lock
  602.                 $result $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
  603.                 if (DB::isError($result)) {
  604.                     return $this->raiseError($result);
  605.                 }
  606.                 if ($result == 0{
  607.                     // Failed to get the lock
  608.                     return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
  609.                 }
  610.  
  611.                 // add the default value
  612.                 $result $this->query("REPLACE INTO ${seqname} (id) VALUES (0)");
  613.                 if (DB::isError($result)) {
  614.                     return $this->raiseError($result);
  615.                 }
  616.  
  617.                 // Release the lock
  618.                 $result $this->getOne('SELECT RELEASE_LOCK('
  619.                                         . "'${seqname}_lock')");
  620.                 if (DB::isError($result)) {
  621.                     return $this->raiseError($result);
  622.                 }
  623.                 // We know what the result will be, so no need to try again
  624.                 return 1;
  625.  
  626.             elseif ($ondemand && DB::isError($result&&
  627.                 $result->getCode(== DB_ERROR_NOSUCHTABLE)
  628.             {
  629.                 // ONDEMAND TABLE CREATION
  630.                 $result $this->createSequence($seq_name);
  631.                 if (DB::isError($result)) {
  632.                     return $this->raiseError($result);
  633.                 else {
  634.                     $repeat = 1;
  635.                 }
  636.  
  637.             elseif (DB::