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